Quellcode durchsuchen

Implement patterns

pull/1/head
Wouter Horlings vor 3 Jahren
Ursprung
Commit
353421e0da
43 geänderte Dateien mit 3792 neuen und 322 gelöschten Zeilen
  1. +6
    -0
      .gitignore
  2. +91
    -0
      .golangci.yml
  3. +31
    -20
      cmd/basispatroon_broek/basispatroon_broek.go
  4. +7
    -0
      cmd/gopatterns/combined_pdf.tex.template
  5. +91
    -0
      cmd/gopatterns/gopatterns.go
  6. +21
    -0
      cmd/oefenbocht/oefenbocht.go
  7. +36
    -3
      go.mod
  8. +114
    -18
      go.sum
  9. +168
    -0
      internal/basispatroonbroek/basispatroon_broek.go
  10. +0
    -61
      pkg/patroon/basispatroon_broek.go
  11. +67
    -0
      pkg/pattern/dimensions.go
  12. +49
    -0
      pkg/pattern/path/path.go
  13. +87
    -0
      pkg/pattern/path/splines.go
  14. +98
    -0
      pkg/pattern/pattern.go
  15. +82
    -0
      pkg/pattern/point/absolute_point.go
  16. +92
    -0
      pkg/pattern/point/between_point.go
  17. +59
    -0
      pkg/pattern/point/point.go
  18. +160
    -0
      pkg/pattern/point/relative_point.go
  19. +119
    -0
      pkg/pattern/template/fixtures/classic_trouser_block.yaml
  20. +9
    -0
      pkg/pattern/template/fixtures/trouser.yaml
  21. +6
    -0
      pkg/pattern/template/information.go
  22. +53
    -0
      pkg/pattern/template/line.go
  23. +18
    -0
      pkg/pattern/template/panel.go
  24. +247
    -0
      pkg/pattern/template/point.go
  25. +28
    -0
      pkg/pattern/template/position.go
  26. +115
    -0
      pkg/pattern/template/renderer.go
  27. +74
    -0
      pkg/pattern/template/storage.go
  28. +56
    -0
      pkg/pattern/template/template.go
  29. +14
    -0
      pkg/pattern/template/template_test.go
  30. +38
    -0
      pkg/pattern/template/value.go
  31. +21
    -0
      pkg/pattern/text/text.go
  32. +39
    -0
      pkg/position/position.go
  33. +100
    -0
      pkg/position/position_test.go
  34. +0
    -134
      pkg/util/point.go
  35. +0
    -73
      pkg/util/point_list.go
  36. +0
    -13
      pkg/util/svg_helper.go
  37. +168
    -0
      pkg/vector/vector.go
  38. +487
    -0
      pkg/vector/vector_test.go
  39. +102
    -0
      spec/template.yaml
  40. +302
    -0
      templates/basic_trouser_block.yaml
  41. +215
    -0
      templates/basis_grondpatroon_heren.yaml
  42. +284
    -0
      templates/classic_trouser_block.yaml
  43. +38
    -0
      templates/dimension_names.yaml

+ 6
- 0
.gitignore Datei anzeigen

@@ -0,0 +1,6 @@
gopatterns
gopatterns.exe
/*.yaml

*.svg
*.pdf

+ 91
- 0
.golangci.yml Datei anzeigen

@@ -0,0 +1,91 @@
---
linters:
enable-all: true
disable:
- nakedret # naked returns are acceptable
- nlreturn # covered by wsl cuddle rules
- nonamedreturns # named returns are accepted
- mnd
- gomnd
# deprecated
- exhaustruct
- execinquery

severity:
default-severity: major

issues:
exclude-use-default: false
exclude-case-sensitive: true
exclude-rules:
- path: _test\.go
linters:
- funlen
- gochecknoglobals
- errchkjson
max-same-issues: 0
max-issues-per-linter: 0

linters-settings:
depguard:
rules:
main:
allow:
- $gostd
- git.wtrh.nl/wouter/gopatterns/pkg/pattern
- git.wtrh.nl/wouter/gopatterns/pkg/pattern/point
- git.wtrh.nl/wouter/gopatterns/pkg/pattern/template
- git.wtrh.nl/wouter/gopatterns/pkg/position
- git.wtrh.nl/wouter/gopatterns/pkg/vector
- github.com/stretchr/testify/assert
- github.com/stretchr/testify/require
- github.com/tdewolff/canvas
- github.com/tdewolff/canvas/renderers
- gitlab.com/Achilleshiel/gosplines

gci:
sections:
- standard
- default

govet:
enable-all: true
disable:
- fieldalignment # misalignment is accepted

revive:
# see https://github.com/mgechev/revive#recommended-configuration
rules:
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
- name: unreachable-code
- name: redefines-builtin-id

stylecheck:
checks: [all]

varnamelen:
min-name-length: 1

wsl:
force-err-cuddling: true

+ 31
- 20
cmd/basispatroon_broek/basispatroon_broek.go Datei anzeigen

@@ -1,35 +1,46 @@
package main

import (
svg "github.com/ajstarks/svgo/float"
"naaipatroon/pkg/patroon"
"os"
"git.wtrh.nl/wouter/gopatterns/internal/basispatroonbroek"
"github.com/tdewolff/canvas"
"github.com/tdewolff/canvas/renderers"
)

func main() {
broek := patroon.Basispatroonbroek{
Heupwijdte: 103,
Taillewijdte: 85,
Zithoogte: 31,
Tussenbeenlengte: 83,
Pijpbreedte: 25,
Taillebandbreedte: 4,
Eenheid: patroon.CentiMeter,
broek := basispatroonbroek.Basispatroonbroek{
Heupwijdte: 1030,
Taillewijdte: 850,
Zithoogte: 310,
Tussenbeenlengte: 830,
Pijpbreedte: 250,
Taillebandbreedte: 40,
ExtraKniebreedte: 15,
Eigenaar: "Wouter Horlings",
}

points := broek.GeneratePoints()
f, err := os.OpenFile("broek.svg", os.O_RDWR|os.O_CREATE, 0o755)
pat := broek.GenereerPatroon()
c := canvas.New(200, 200)
err := pat.ToCanvas(c)
if err != nil {
panic(err)
}
c.Fit(10)

points = points.Normalize()
_, max := points.Box()
canvas := svg.New(f)
err = renderers.Write("broek2.pdf", c)
if err != nil {
panic(err)
}

canvas.Startview(max.X+40, max.Y+40, -20, -20, max.X+40, max.Y+40)
broek.Voorbeen(canvas)
canvas.End()
//_, err := os.OpenFile("broek.svg", os.O_RDWR|os.O_CREATE, 0o755)
//if err != nil {
// panic(err)
//}
//
//points = points.Normalize()
//_, max := points.Box()
//canvas := svg.New(f)
//
//canvas.Startview(max.X+40, max.Y+40, -20, -20, max.X+40, max.Y+40)
//broek.Voorbeen(canvas)
//canvas.End()
}

+ 7
- 0
cmd/gopatterns/combined_pdf.tex.template Datei anzeigen

@@ -0,0 +1,7 @@
\documentclass{standalone}
\usepackage{graphicx}
\begin{document}
{{ range $val := . }}
\includegraphics[angle=270]{ {{- $val -}} }
{{ end }}
\end{document}

+ 91
- 0
cmd/gopatterns/gopatterns.go Datei anzeigen

@@ -0,0 +1,91 @@
package main

import (
_ "embed"
"flag"
"fmt"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/template"
"gitlab.com/slxh/go/env"
"log/slog"
"os"

gotemplate "text/template"
)

//go:embed combined_pdf.tex.template
var texTemplate string

func main() {
var (
templateDir string
outputDir string
help bool
)

flag.StringVar(&templateDir, "templates", "templates", "Directory with template files")
flag.StringVar(&outputDir, "out", ".", "output directory")
flag.BoolVar(&help, "help", false, "show help")

if err := env.ParseWithFlags(); err != nil {
slog.Error("parsing flags failed", "err", err)
return
}

if help {
fmt.Printf(`
Render patterns. Usage
gopatterns [-templates <template-dir>] [-out <output-dir>] input-file
`)
}

args := flag.Args()
if len(args) == 0 {
slog.Error("at least one pattern is required")
}

os.MkdirAll(outputDir, 0o770)

storage, err := template.NewStorage(templateDir)
if err != nil {
slog.Error("failed to open template directory", "err", err, "dir", templateDir)
return
}

files := make([]string, 0)
for _, arg := range args {
pattern, err := template.LoadPattern(arg)
if err != nil {
slog.Error("failed to load pattern", "err", err)
return
}

filenames, err := storage.RenderPatterns(pattern, outputDir)
if err != nil {
slog.Error("error occurred while creating pattern", "pattern", arg, "err", err)
return
}

files = append(files, filenames...)
}

generateLatex(outputDir, files...)
}

func generateLatex(outDir string, filenames ...string) {
open, err := os.OpenFile(outDir+"/combined_pdf.tex", os.O_RDWR+os.O_CREATE+os.O_TRUNC, 0o640)
if err != nil {
panic(err)
}

defer open.Close()

parse, err := gotemplate.New("latex").Parse(texTemplate)
if err != nil {
panic(err)
}

err = parse.Execute(open, filenames)
if err != nil {
panic(err)
}
}

+ 21
- 0
cmd/oefenbocht/oefenbocht.go Datei anzeigen

@@ -0,0 +1,21 @@
package main

func main() {
// pat := pattern.NewPatroon()
// basePoint := point.NewAbsolutePoint(1, 1, 0, 0)
// pat.AddPoint(basePoint)
// pat.AddPoint(point.NewRelativePoint(basePoint, 5, 5, 1))
// pat.AddPoint(point.NewRelativePoint(&basePoint, 10, 0, 2))
// pat.AddPoint(point.NewRelativePoint(&basePoint, 7.5, 2.5, 4))
// pat.AddPoint(point.NewRelativePoint(&basePoint, 5, 0, 3))
// pat.AddLine(path.NewBezierCurve(pat.GetPoint(0), pat.GetPoint(1), pat.GetPoint(2)))
// pat.AddLine(path.NewQBezierCurve(pat.GetPoint(0), pat.GetPoint(1), pat.GetPoint(3), pat.GetPoint(4)))
// secondPoint := point.NewAbsolutePoint(5, 10, 0, 10)
// pat.AddPoint(secondPoint)
// pat.AddPoint(point.NewRelativePointBelow(secondPoint, 2, 11))
// pat.AddPoint(point.NewRelativePointBelow(secondPoint, 4, 12))
// pat.AddPoint(point.NewRelativePointBelow(secondPoint, 6, 13))
// pat.AddPoint(point.NewRelativePointBelow(secondPoint, 8, 14))
// pat.AddLine(path.NewMultiPointCurve(math.Pi/-8, math.Pi/8, pat.GetPoints(10, 11, 12, 13)...))
// pat.ToSVG("oefenbocht.svg", renderer.CentiMeter)
}

+ 36
- 3
go.mod Datei anzeigen

@@ -1,5 +1,38 @@
module naaipatroon
module git.wtrh.nl/wouter/gopatterns

go 1.17
go 1.22.0

require github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b
toolchain go1.22.2

require (
github.com/stretchr/testify v1.9.0
github.com/tdewolff/canvas v0.0.0-20240404204646-eb921826d23b
gitlab.com/Achilleshiel/gosplines v0.0.0-20240601225309-88d1e67b1066
gopkg.in/Knetic/govaluate.v3 v3.0.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/benoitkugler/textlayout v0.3.0 // indirect
github.com/benoitkugler/textprocessing v0.0.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-fonts/latin-modern v0.3.2 // indirect
github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea // indirect
github.com/go-text/typesetting v0.0.0-20231013144250-6cc35dbfae7d // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/tdewolff/font v0.0.0-20240404204409-be214eafe484 // indirect
github.com/tdewolff/minify/v2 v2.20.5 // indirect
github.com/tdewolff/parse/v2 v2.7.3 // indirect
github.com/wcharczuk/go-chart/v2 v2.1.1 // indirect
gitlab.com/slxh/go/env v1.0.0 // indirect
golang.org/x/image v0.15.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
gonum.org/v1/gonum v0.15.0 // indirect
gonum.org/v1/plot v0.14.0 // indirect
star-tex.org/x/tex v0.4.0 // indirect
)

+ 114
- 18
go.sum Datei anzeigen

@@ -1,29 +1,125 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
git.sr.ht/~sbinet/gg v0.5.0 h1:6V43j30HM623V329xA9Ntq+WJrMjDxRjuAB1LFWF5m8=
git.sr.ht/~sbinet/gg v0.5.0/go.mod h1:G2C0eRESqlKhS7ErsNey6HHrqU1PwsnCQlekFi9Q2Oo=
github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f h1:l7moT9o/v/9acCWA64Yz/HDLqjcRTvc0noQACi4MsJw=
github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f/go.mod h1:vIOkSdX3NDCPwgu8FIuTat2zDF0FPXXQ0RYFRy+oQic=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE=
github.com/benoitkugler/textlayout v0.3.0 h1:2ehWXEkgb6RUokTjXh1LzdGwG4dRP6X3dqhYYDYhUVk=
github.com/benoitkugler/textlayout v0.3.0/go.mod h1:o+1hFV+JSHBC9qNLIuwVoLedERU7sBPgEFcuSgfvi/w=
github.com/benoitkugler/textlayout-testdata v0.1.1/go.mod h1:i/qZl09BbUOtd7Bu/W1CAubRwTWrEXWq6JwMkw8wYxo=
github.com/benoitkugler/textprocessing v0.0.3 h1:Q2X+Z6vxuW5Bxn1R9RaNt0qcprBfpc2hEUDeTlz90Ng=
github.com/benoitkugler/textprocessing v0.0.3/go.mod h1:/4bLyCf1QYywunMK3Gf89Nhb50YI/9POewqrLxWhxd4=
github.com/blend/go-sdk v1.20220411.3 h1:GFV4/FQX5UzXLPwWV03gP811pj7B8J2sbuq+GJQofXc=
github.com/blend/go-sdk v1.20220411.3/go.mod h1:7lnH8fTi6U4i1fArEXRyOIY2E1X4MALg09qsQqY1+ak=
github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-fonts/dejavu v0.3.2 h1:3XlHi0JBYX+Cp8n98c6qSoHrxPa4AUKDMKdrh/0sUdk=
github.com/go-fonts/dejavu v0.3.2/go.mod h1:m+TzKY7ZEl09/a17t1593E4VYW8L1VaBXHzFZOIjGEY=
github.com/go-fonts/latin-modern v0.3.2 h1:M+Sq24Dp0ZRPf3TctPnG1MZxRblqyWC/cRUL9WmdaFc=
github.com/go-fonts/latin-modern v0.3.2/go.mod h1:9odJt4NbRrbdj4UAMuLVd4zEukf6aAEKnDaQga0whqQ=
github.com/go-fonts/liberation v0.3.2 h1:XuwG0vGHFBPRRI8Qwbi5tIvR3cku9LUfZGq/Ar16wlQ=
github.com/go-fonts/liberation v0.3.2/go.mod h1:N0QsDLVUQPy3UYg9XAc3Uh3UDMp2Z7M1o4+X98dXkmI=
github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea h1:DfZQkvEbdmOe+JK2TMtBM+0I9GSdzE2y/L1/AmD8xKc=
github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea/go.mod h1:Y7Vld91/HRbTBm7JwoI7HejdDB0u+e9AUBO9MB7yuZk=
github.com/go-pdf/fpdf v0.9.0 h1:PPvSaUuo1iMi9KkaAn90NuKi+P4gwMedWPHhj8YlJQw=
github.com/go-pdf/fpdf v0.9.0/go.mod h1:oO8N111TkmKb9D7VvWGLvLJlaZUQVPM+6V42pp3iV4Y=
github.com/go-text/typesetting v0.0.0-20231013144250-6cc35dbfae7d h1:HrdwTlHVMdi9nOW7ZnYiLmIT1hJHvipIwM0aX3rKn8I=
github.com/go-text/typesetting v0.0.0-20231013144250-6cc35dbfae7d/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tdewolff/canvas v0.0.0-20240404204646-eb921826d23b h1:8wPwQtX2ulzB611fk+tPxLACiqb9VoJ9vwn8occWHD0=
github.com/tdewolff/canvas v0.0.0-20240404204646-eb921826d23b/go.mod h1:dFEWjsGVQGviBNKYPqDbDhjfRg5QQjmEgoR6jo057bU=
github.com/tdewolff/font v0.0.0-20240404204409-be214eafe484 h1:pS1QrGQdj4Qrwc26uU8wDHlGq1oe/64mFjwWhCuGdvo=
github.com/tdewolff/font v0.0.0-20240404204409-be214eafe484/go.mod h1:S1ByajP+rzLFlhudtNTELNuhxoSZ19Coif+JE4kivAo=
github.com/tdewolff/minify/v2 v2.20.5 h1:IbJpmpAFESnuJPdsvFBJWsDcXE5qHsmaVQrRqhOI9sI=
github.com/tdewolff/minify/v2 v2.20.5/go.mod h1:N78HtaitkDYAWXFbqhWX/LzgwylwudK0JvybGDVQ+Mw=
github.com/tdewolff/parse/v2 v2.7.3 h1:SHj/ry85FdqniccvzJTG+Gt/mi/HNa1cJcTzYZnvc5U=
github.com/tdewolff/parse/v2 v2.7.3/go.mod h1:9p2qMIHpjRSTr1qnFxQr+igogyTUTlwvf9awHSm84h8=
github.com/tdewolff/test v1.0.10/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.11-0.20231121141655-2d5236e10ae4 h1:CmTImZFElFD07EUPqgMEraDMnJX1E5oJKeibjg0SC2c=
github.com/tdewolff/test v1.0.11-0.20231121141655-2d5236e10ae4/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
github.com/wcharczuk/go-chart/v2 v2.1.1 h1:2u7na789qiD5WzccZsFz4MJWOJP72G+2kUuJoSNqWnE=
github.com/wcharczuk/go-chart/v2 v2.1.1/go.mod h1:CyCAUt2oqvfhCl6Q5ZvAZwItgpQKZOkCJGb+VGv6l14=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
gitlab.com/Achilleshiel/gosplines v0.0.0-20240601225309-88d1e67b1066 h1:v/CD9lHfrsY6V5JHyPEMiq2Meu+WDVNTXnktDuDQlGE=
gitlab.com/Achilleshiel/gosplines v0.0.0-20240601225309-88d1e67b1066/go.mod h1:arpcB6wX3cMXszN2eA8phrMe7Z3OB1nks3Hata24tTM=
gitlab.com/slxh/go/env v1.0.0 h1:MHjny7PNu5MAvPRHspQWy7tTDjjkZDsfhxAT8Eq3dbU=
gitlab.com/slxh/go/env v1.0.0/go.mod h1:ApyfFy6Azu3faux6ekFESPHdHT8zAN0W8QrEGqD2ejw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=
gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=
gonum.org/v1/plot v0.14.0 h1:+LBDVFYwFe4LHhdP8coW6296MBEY4nQ+Y4vuUpJopcE=
gonum.org/v1/plot v0.14.0/go.mod h1:MLdR9424SJed+5VqC6MsouEpig9pZX2VZ57H9ko2bXU=
gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc=
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
star-tex.org/x/tex v0.4.0 h1:AXUwgpnHLCxZUWW3qrmjv6ezNhH3PjUVBuLLejz2cgU=
star-tex.org/x/tex v0.4.0/go.mod h1:w91ycsU/DkkCr7GWr60GPWqp3gn2U+6VX71T0o8k8qE=

+ 168
- 0
internal/basispatroonbroek/basispatroon_broek.go Datei anzeigen

@@ -0,0 +1,168 @@
package basispatroonbroek

import (
"math"

"git.wtrh.nl/wouter/gopatterns/pkg/pattern"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/path"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/point"
"git.wtrh.nl/wouter/gopatterns/pkg/vector"
)

type Basispatroonbroek struct {
Heupwijdte float64
Taillewijdte float64
Zithoogte float64
Tussenbeenlengte float64
Pijpbreedte float64
Taillebandbreedte float64
ExtraKniebreedte float64
Eigenaar string
}

func (b *Basispatroonbroek) GenereerPatroon() *pattern.Pattern {
p := pattern.NewPattern()
b.generatePoints(p)
b.generateLines(p)
return p
}

func (b *Basispatroonbroek) generatePoints(p *pattern.Pattern) {
//p0 := point.NewAbsolutePoint(0, 0, 0, "0")
//p.AddPoint(p0)
//p1 := p.RelativePoint(p0).WithYOffset(-(b.Zithoogte + 10 - b.Taillebandbreedte)).MarkWith("1").Done()
//// p.AddPoint(point.NewRelativePointBelow(p.GetPoint(0), b.Zithoogte+1-b.Taillebandbreedte, 1))
//p2 := p.RelativePoint(p1).WithYOffset(-b.Tussenbeenlengte).MarkWith("2").Done()
//// p.AddPoint(point.NewRelativePointBelow(p.GetPoint(1), b.Tussenbeenlengte, 2))
//
//p3 := p.RelativePoint(p2).WithYOffset(p1.Vector().Distance(p2.Vector())/2 + 50).
// MarkWith("3").Done()
//
//// p.AddPoint(point.NewRelativePointAbove(p.GetPoint(2), p.GetPoint(1).Vector().Distance(p.GetPoint(2).Vector())/2+5, 3))
//p4 := p.RelativePoint(p1).WithYOffset(b.Zithoogte / 4).MarkWith("4").Done()
//// p.AddPoint(point.NewRelativePointAbove(p.GetPoint(1), b.Zithoogte/4, 4))
//p5 := p.RelativePoint(p1).WithXOffset(-b.Heupwijdte / 12).MarkWith("5").Done()
//// p.AddPoint(point.NewRelativePointLeft(p.GetPoint(1), b.Heupwijdte/12, 5))
//p6 := p.RelativePoint(p4).WithXOffset(-b.Heupwijdte / 12).MarkWith("6").Done()
//// p.AddPoint(point.NewRelativePointLeft(p.GetPoint(4), b.Heupwijdte/12, 6))
//p7 := p.RelativePoint(p0).WithXOffset(-b.Heupwijdte / 12).MarkWith("7").Done()
//// p.AddPoint(point.NewRelativePointLeft(p.GetPoint(0), b.Heupwijdte/12, 7))
//p.RelativePoint(p6).WithXOffset(b.Heupwijdte/4 + 20).MarkWith("8").Done()
//// p.AddPoint(point.NewRelativePointRight(p.GetPoint(6), b.Heupwijdte/4+2, 8))
//p9 := p.RelativePoint(p5).WithXOffset(-(b.Heupwijdte/16 + 5)).MarkWith("9").Done()
//// p.AddPoint(point.NewRelativePointLeft(p.GetPoint(5), b.Heupwijdte/16+0.5, 9))
//p10 := p.RelativePoint(p7).WithXOffset(10).MarkWith("10").Done()
//// p.AddPoint(point.NewRelativePointRight(p.GetPoint(7), 1, 10))
//p.RelativePoint(p10).WithXOffset(b.Taillewijdte/4 + 25).MarkWith("11").Done()
//// p.AddPoint(point.NewRelativePointRight(p.GetPoint(10), b.Taillewijdte/4+2.5, 11))
//p12 := p.RelativePoint(p2).WithXOffset(b.Pijpbreedte / 2).MarkWith("12").Done()
//// p.AddPoint(point.NewRelativePointRight(p.GetPoint(2), b.Pijpbreedte/2, 12))
//p13 := p.RelativePoint(p2).WithXOffset(-b.Pijpbreedte / 2).MarkWith("13").Done()
//// p.AddPoint(point.NewRelativePointLeft(p.GetPoint(2), b.Pijpbreedte/2, 13))
//p14 := p.RelativePoint(p3).WithXOffset(b.Pijpbreedte/2 + b.ExtraKniebreedte).MarkWith("14").Done()
//// p.AddPoint(point.NewRelativePointRight(p.GetPoint(3), b.Pijpbreedte/2+1.5, 14))
//p15 := p.RelativePoint(p3).WithXOffset(-(b.Pijpbreedte/2 + b.ExtraKniebreedte)).MarkWith("15").Done()
//// p.AddPoint(point.NewRelativePointLeft(p.GetPoint(3), b.Pijpbreedte/2+1.5, 15))
//p1To5Distance := p1.Vector().Distance(p5.Vector())
//p16 := p.RelativePoint(p5).WithXOffset(p1To5Distance / 4).MarkWith("16").Done()
//p17 := p.RelativePoint(p6).WithXOffset(p1To5Distance / 4).MarkWith("17").Done()
//p18 := p.RelativePoint(p7).WithXOffset(p1To5Distance / 4).MarkWith("18").Done()
//p.AddPoint(point.NewBetweenPoint(p16, p18, 0.5, "19"))
//p20 := p.RelativePoint(p18).WithXOffset(20).MarkWith("20").Done()
//distance20to21 := 10.0
//p21 := p.RelativePoint(p20).WithYOffset(distance20to21).MarkWith("21").Done()
//p22 := p.RelativePoint(p9).WithXOffset(-(p5.Vector().Distance(p9.Vector())/2 + 5)).MarkWith("22").Done()
//p.RelativePoint(p22).WithYOffset(-5).MarkWith("23").Done()
//distance20to24 := math.Sqrt(math.Pow(b.Taillewijdte/4+45, 2) - math.Pow(distance20to21, 2))
//p24 := p.RelativePoint(p20).WithXOffset(distance20to24).MarkWith("24").Done()
//p.AddPoint(point.NewBetweenPoint(p21, p24, 0.5, "25"))
//p.RelativePoint(p17).WithXOffset(b.Heupwijdte/4 + 30).MarkWith("26").Done()
//p.RelativePoint(p12).WithXOffset(20).MarkWith("27").Done()
//p.RelativePoint(p13).WithXOffset(-20).MarkWith("28").Done()
//p.RelativePoint(p14).WithXOffset(20).MarkWith("29").Done()
//p.RelativePoint(p15).WithXOffset(-20).MarkWith("30").Done()
}

func (b *Basispatroonbroek) generateLines(p *pattern.Pattern) {
p.AddLine(path.NewPath(p.GetPoints("15", "13", "12", "14")...))
p.AddLine(path.NewPath(p.GetPoints("6", "10", "11")...))
p15 := p.GetPoint("15")
p9 := p.GetPoint("9")
p13 := p.GetPoint("13")
pstart15 := point.NewBetweenPoint(p13, p15, 1.5, "109")
p.AddPoint(pstart15)
p.AddLine(path.NewSpline(pstart15, nil, p15, p9))
hp5 := point.NewRelativePointWithVector(p.GetPoint("5"), vector.Vector{X: 30, Y: 0}.Rotate(3*math.Pi/4), "110")
p.AddLine(path.NewSpline(nil, nil, p.GetPoint("6"), hp5, p9))
p25 := p.GetPoint("25")
p21 := p.GetPoint("21")
p24 := p.GetPoint("24")
p19 := p.GetPoint("19")
hp251 := point.NewRelativePoint(p25).WithYOffset(-25 / 2).Done()
hp252 := point.NewRelativePoint(p25).WithYOffset(25 / 2).Done()
hp253 := point.NewRelativePoint(p25).WithXOffset(120).Done()
p.AddLine(path.NewPath(p19, p21, hp251, hp253, hp252, p24))

hp3 := point.NewBetweenPoint(p.GetPoint("14"), p.GetPoint("8"), 0.5, "17")
hp4 := point.NewRelativePointLeft(hp3, 5, "21")

hp7 := point.NewBetweenPoint(p.GetPoint("8"), p.GetPoint("11"), 0.5, "17")
hp8 := point.NewRelativePointRight(hp7, 20, "22")
hp10 := point.NewBetweenPoint(p.GetPoint("12"), p.GetPoint("14"), 1.3, "112")
p.AddLine(path.NewSpline(hp10, hp8, p.GetPoint("14"), hp4, p.GetPoint("8"), p.GetPoint("11")))

hp16 := point.NewRelativePointWithVector(p.GetPoint("16"), vector.Vector{X: 45, Y: 0}.Rotate(3*math.Pi/4), "110")
hp19 := point.NewBetweenPoint(p21, p19, 1.4, "0")
p23 := p.GetPoint("23")
hp23 := point.NewRelativePointWithVector(p23, vector.Vector{X: 70, Y: 0}.Rotate(-math.Pi/10), "111")
p.AddLine(path.NewSpline(hp19, hp23, p19, hp16, p23))
p30 := p.GetPoint("30")
p28 := p.GetPoint("28")
hp30 := point.NewBetweenPoint(p28, p30, 1.5, "0")
p.AddLine(path.NewSpline(nil, hp30, p23, p30))
p.AddLine(path.NewPath(p30, p28))

p29 := p.GetPoint("29")
p27 := p.GetPoint("27")
p.AddLine(path.NewPath(p29, p27))
hp28 := point.NewRelativePointRight(point.NewBetweenPoint(p28, p27, 0.5, "0"), 10, "0")
p.AddLine(path.NewSpline(nil, nil, p27, hp28, p28))

p26 := p.GetPoint("26")
hp13 := point.NewBetweenPoint(p29, p26, 0.5, "17")
hp14 := point.NewRelativePointLeft(hp13, 5, "21")

hp17 := point.NewBetweenPoint(p26, p24, 0.5, "17")
hp18 := point.NewRelativePointRight(hp17, 20, "22")
hp110 := point.NewBetweenPoint(p27, p29, 1.3, "112")
p.AddLine(path.NewSpline(hp110, hp18, p29, hp14, p26, p24))
// p.AddLine(path.NewStraightLine(p.GetPoint(19), p.GetPoint(21)))
// p.AddLine(path.NewStraightLine(hp251, p.GetPoint(21)))
// p.AddLine(path.NewStraightLine(hp251, hp253))
// p.AddLine(path.NewStraightLine(hp252, hp253))
// p.AddLine(path.NewStraightLine(hp252, p.GetPoint(24)))
// p19 := p.GetPoint(19)
// p23 := p.GetPoint(23)
// p.AddLine(path.NewMultiPointCurve(0.0, p.GetPoint(21).Position().Direction(p.GetPoint(19).Position()), p23, p19))
// p.AddLine(path.NewMultiPointCurve(-0.18, -math.Pi+0.18, p.GetPoints(28, 27)...))
// hp9 := point.NewBetweenPoint(p.GetPoint(29), p.GetPoint(26), 0.5, 0)
// hp10 := point.NewRelativePointLeft(hp9, 0.3, 0)
// p.AddLine(path.NewMultiPointCurve(math.Pi/2, -math.Pi/2, p.GetPoint(29), hp10, p.GetPoint(26), p.GetPoint(24)))
// p.AddLine(path.NewMultiPointCurve(p.GetPoint(28).Position().Direction(p.GetPoint(30).Position()), -math.Pi/2, p.GetPoint(30), p.GetPoint(23)))
// p.AddLine(path.NewStraightLine(p.GetPoint(28), p.GetPoint(30)))
// p.AddLine(path.NewStraightLine(p.GetPoint(27), p.GetPoint(29)))
}

//func (b *Basispatroonbroek) Voorbeen(canvas *svg.SVG) {
// p := b.GeneratePoints().Normalize()
// p.Draw(canvas)
// p.Line(canvas, 15, 13, 12, 14)
// p.Line(canvas, 10, 11)
// pa := p[14].Above(p[14].Distance(p[8]) / 3)
// pb := p[8].Below(p[14].Distance(p[8]) / 3)
// util.Bezier(canvas, p[14], pa, pb, p[8])
// p15p9half := p[9].Subtract(p[15]).Divide(2)
// pc := p[15].Add(p15p9half).Add(p15p9half.Unit().Rotate(math.Pi / 2).Multiply(3 * float64(b.Eenheid)))
//
// util.Qbez(canvas, p[9], pc, p[15])
//}

+ 0
- 61
pkg/patroon/basispatroon_broek.go Datei anzeigen

@@ -1,61 +0,0 @@
package patroon

import (
svg "github.com/ajstarks/svgo/float"
"math"
"naaipatroon/pkg/util"
)

const (
MilliMeter SvgScale = 3.7795
CentiMeter SvgScale = 37.795
)

type SvgScale float64

type Basispatroonbroek struct {
Heupwijdte float64
Taillewijdte float64
Zithoogte float64
Tussenbeenlengte float64
Pijpbreedte float64
Taillebandbreedte float64
Eenheid SvgScale
Eigenaar string
}

func (b *Basispatroonbroek) GeneratePoints() util.PointMap {
p := make(util.PointMap, 30)
p[0] = util.Point{}
p[1] = p[0].Below(b.Zithoogte + 1 - b.Taillebandbreedte)
p[2] = p[1].Below(b.Tussenbeenlengte)
p[3] = p[2].Above(p[1].Distance(p[2])/2 + 5)
p[4] = p[1].Above(b.Zithoogte / 4)
p[5] = p[1].Left(b.Heupwijdte / 12)
p[6] = p[4].Left(b.Heupwijdte / 12)
p[7] = p[0].Left(b.Heupwijdte / 12)
p[8] = p[6].Right(b.Heupwijdte/4 + 2)
p[9] = p[5].Left(b.Heupwijdte/16 + 5)
p[10] = p[7].Right(1)
p[11] = p[10].Right(b.Taillewijdte/4 + 2.5)
p[12] = p[2].Right(b.Pijpbreedte / 2)
p[13] = p[2].Left(b.Pijpbreedte / 2)
p[14] = p[3].Right(b.Pijpbreedte / 2)
p[15] = p[3].Left(b.Pijpbreedte / 2)

return p.Scale(float64(b.Eenheid))
}

func (b *Basispatroonbroek) Voorbeen(canvas *svg.SVG) {
p := b.GeneratePoints().Normalize()
p.Draw(canvas)
p.Line(canvas, 15, 13, 12, 14)
p.Line(canvas, 10, 11)
pa := p[14].Above(p[14].Distance(p[8]) / 3)
pb := p[8].Below(p[14].Distance(p[8]) / 3)
util.Bezier(canvas, p[14], pa, pb, p[8])
p15p9half := p[9].Subtract(p[15]).Divide(2)
pc := p[15].Add(p15p9half).Add(p15p9half.Unit().Rotate(math.Pi / 2).Multiply(3 * float64(b.Eenheid)))

util.Qbez(canvas, p[9], pc, p[15])
}

+ 67
- 0
pkg/pattern/dimensions.go Datei anzeigen

@@ -0,0 +1,67 @@
package pattern

import (
"fmt"
"gopkg.in/yaml.v3"
"io/fs"
"math"

"gopkg.in/Knetic/govaluate.v3"
)

// DimensionID describe the ID of the dimension.
type DimensionID string

// Dimensions is a map with dimensions.
type Dimensions map[DimensionID]Dimension

// Parameters returns a govaluate.MapParameters object based on the dimensions.
func (d Dimensions) Parameters() govaluate.MapParameters {
parameters := govaluate.MapParameters{}
parameters["pi"] = math.Pi

for id, dimension := range d {
parameters[string(id)] = dimension.Value
}

return parameters
}

func (d Dimensions) Names(templateDir fs.FS) (map[string]string, error) {
f, err := templateDir.Open("dimension_names.yaml")
if err != nil {
return nil, fmt.Errorf("open \"dimension_names.yaml\": %w", err)
}

namedDimensions := Dimensions{}

err = yaml.NewDecoder(f).Decode(&namedDimensions)
if err != nil {
return nil, fmt.Errorf("decode yaml from \"dimension_names.yaml\": %w", err)
}

out := make(map[string]string)

for id, dimension := range d {
nd, ok := namedDimensions[id]
if !ok {
continue
}

value := math.Round(dimension.Value) / 10
out[nd.Name] = fmt.Sprintf("%.1f mm", value)
}

return out, nil
}

// Dimension is a combination of a name and a value.
type Dimension struct {
Name string
Value float64
}

// AddDimension adds a dimension to a pattern.
func (p *Pattern) AddDimension(id DimensionID, dimension Dimension) {
p.dimensions[id] = dimension
}

+ 49
- 0
pkg/pattern/path/path.go Datei anzeigen

@@ -0,0 +1,49 @@
// Package path provides objects to define lines on a sewing pattern.
package path

import (
"image/color"

"git.wtrh.nl/wouter/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
}

// 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}
}

// Draw the path to the provided [canvas.Canvas].
func (p Path) Draw(c *canvas.Canvas) error {
polyline := canvas.Polyline{}
for _, next := range p.points {
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)

return nil
}

+ 87
- 0
pkg/pattern/path/splines.go Datei anzeigen

@@ -0,0 +1,87 @@
package path

import (
"fmt"

"git.wtrh.nl/wouter/gopatterns/pkg/pattern/point"
"github.com/tdewolff/canvas"
splines "gitlab.com/Achilleshiel/gosplines"
)

const resolution = 40

// Spline defines a smooth curved path through points.
type Spline struct {
Path
start, end point.Point
}

// 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 {
s := Spline{
Path: NewPath(points...),
start: start,
end: end,
}

if start == nil && len(points) > 1 {
s.start = points[0]
}

if end == nil && len(points) > 1 {
s.end = points[len(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 {
return nil
}

x := make([]float64, len(p.points))
y := make([]float64, len(p.points))

for i, point := range p.points {
x[i], y[i] = point.Vector().Values()
}

diffStart := p.start.Vector().Subtract(p.points[0].Vector())
diffEnd := p.points[len(p.points)-1].Vector().Subtract(p.end.Vector())

xCoefficient, err := splines.SolveSplineWithConstraint(x, diffStart.X, diffEnd.X)
if err != nil {
return fmt.Errorf("unable to calculate coefficients for x: %w", err)
}

yCoefficient, err := splines.SolveSplineWithConstraint(y, diffStart.Y, diffEnd.Y)
if err != nil {
return fmt.Errorf("unable to calculate coefficients for y: %w", err)
}

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 t := stepSize; t < 1.0; t += stepSize {
xCalculated := xCoefficient[i].Calculate(t)
yCalculated := yCoefficient[i].Calculate(t)
points = append(points, point.NewAbsolutePoint(xCalculated, yCalculated, 0, "0"))
}
}

points = append(points, p.points[len(p.points)-1])

err = NewPath(points...).Draw(c)
if err != nil {
return fmt.Errorf("draw spline points to canvas: %w", err)
}

return nil
}

+ 98
- 0
pkg/pattern/pattern.go Datei anzeigen

@@ -0,0 +1,98 @@
// Package pattern defines a pattern that can be drawn to a canvas.
package pattern

import (
"fmt"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/point"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/text"
"github.com/tdewolff/canvas"
"golang.org/x/image/font/gofont/goregular"
"gopkg.in/Knetic/govaluate.v3"
)

// 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
dimensions Dimensions
texts []*text.Text
}

type pathDrawer interface {
Draw(c *canvas.Canvas) error
}

// AddPoint adds a point.Point to the pattern.
func (p *Pattern) AddPoint(point point.Point) {
p.points[point.ID()] = point
}

// AddLine adds a drawable line to the pattern.
func (p *Pattern) AddLine(line pathDrawer) {
p.lines = append(p.lines, line)
}

// GetPoints returns a slice with points for the given IDs.
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))
}

return points
}

// GetPoint returns the point for the given ID.
func (p *Pattern) GetPoint(id point.ID) point.Point { //nolint:ireturn
return p.points[id]
}

// NewPattern returns a new Pattern.
func NewPattern() *Pattern {
return &Pattern{
points: make(map[point.ID]point.Point, 32),
lines: make([]pathDrawer, 0, 32),
dimensions: make(Dimensions),
texts: make([]*text.Text, 0),
}
}

// ToCanvas draws the pattern on the provided [canvas.Canvas].
func (p *Pattern) ToCanvas(c *canvas.Canvas) error {
fontDejaVu := canvas.NewFontFamily("latin")

if err := fontDejaVu.LoadFont(goregular.TTF, 0, canvas.FontRegular); err != nil {
panic(err)
}

face := fontDejaVu.Face(12.0, canvas.Black, canvas.FontRegular)
for _, pp := range p.points {
point.Draw(c, pp, face)
}

for _, path := range p.lines {
err := path.Draw(c)
if err != nil {
return fmt.Errorf("draw path to canvas: %w", err)
}
}

for _, t := range p.texts {
t.ToCanvas(c, face)
}

return nil
}

// Parameters return the parameters based on the dimensions.
func (p *Pattern) Parameters() govaluate.MapParameters {
return p.dimensions.Parameters()
}

func (p *Pattern) AddText(t *text.Text) {
p.texts = append(p.texts, t)
}

func (p *Pattern) SetDimensions(dimensions Dimensions) {
p.dimensions = dimensions
}

+ 82
- 0
pkg/pattern/point/absolute_point.go Datei anzeigen

@@ -0,0 +1,82 @@
package point

import (
"git.wtrh.nl/wouter/gopatterns/pkg/position"
"git.wtrh.nl/wouter/gopatterns/pkg/vector"
"github.com/tdewolff/canvas"
)

// AbsolutePoint implements Point and defines an absolute position.
// All other points should eventually be relative to one or more absolute point(s).
type AbsolutePoint struct {
id ID
position position.Position
name string
draw bool
hide bool
}

// Matrix calculates and returns the [canvas.Matrix] of a point.
func (a *AbsolutePoint) Matrix() canvas.Matrix {
return canvas.Identity.Translate(a.position.Vector.Values()).Rotate(a.position.Rotation)
}

// ID returns the point ID.
func (a *AbsolutePoint) ID() ID {
return a.id
}

// Name returns the name of a point.
func (a *AbsolutePoint) Name() string {
return a.name
}

// Position calculates and returns the absolute [position.Position].
func (a *AbsolutePoint) Position() position.Position {
return a.position
}

// Vector calculates and returns the absolute [vector.Vector].
func (a *AbsolutePoint) Vector() vector.Vector {
return a.Position().Vector
}

// NewAbsolutePoint returns a new absolute point.
func NewAbsolutePoint(x, y, r float64, id ID) *AbsolutePoint {
return &AbsolutePoint{
position: position.Position{
Vector: vector.Vector{
X: x,
Y: y,
},
Rotation: r,
},
id: id,
name: string(id),
}
}

// Draw returns if the point should be drawn.
func (a *AbsolutePoint) Draw() bool {
return a.draw
}

// SetDraw indicates that the point should be drawn.
func (a *AbsolutePoint) SetDraw() {
a.draw = true
}

// UnsetDraw indicates that the point should not be drawn.
func (a *AbsolutePoint) UnsetDraw() {
a.draw = true
}

// Hide returns if the point must remain hidden.
func (a *AbsolutePoint) Hide() bool {
return a.hide
}

// SetHide indicates that the must be hidden.
func (a *AbsolutePoint) SetHide() {
a.hide = true
}

+ 92
- 0
pkg/pattern/point/between_point.go Datei anzeigen

@@ -0,0 +1,92 @@
package point

import (
"math"

"git.wtrh.nl/wouter/gopatterns/pkg/position"
"git.wtrh.nl/wouter/gopatterns/pkg/vector"
"github.com/tdewolff/canvas"
)

// BetweenPoint defines a point on the line between two other points.
type BetweenPoint struct {
id ID
p Point
q Point
offset float64
name string
draw bool
hide bool
}

// NewBetweenPoint returns a new BetweenPoint relative to two other points p and q.
// The given offset defines where the new point is.
// With offset = 0 the new point is a p, offset = 0.5 results in a point exactly in the middle.
// Offset can be <0 (extending from p side) or >1 (extending from the q side).
func NewBetweenPoint(p, q Point, offset float64, id ID) *BetweenPoint {
return &BetweenPoint{
id: id,
p: p,
q: q,
offset: offset,
name: string(id),
}
}

// Position calculates and returns the absolute [position.Position].
func (b *BetweenPoint) Position() position.Position {
return position.Position{
Vector: b.p.Vector().Add(b.inBetween()),
Rotation: b.p.Vector().AngleBetween(b.q.Vector()) - math.Pi/2,
}
}

func (b *BetweenPoint) inBetween() vector.Vector {
return b.q.Vector().Subtract(b.p.Vector()).Multiply(b.offset)
}

// Vector calculates and returns the absolute [vector.Vector].
func (b *BetweenPoint) Vector() vector.Vector {
return b.Position().Vector
}

// Matrix calculates and returns the [canvas.Matrix] of a point.
func (b *BetweenPoint) Matrix() canvas.Matrix {
return b.p.Matrix().Translate(b.inBetween().Values()).
Rotate((b.p.Vector().AngleBetween(b.q.Vector()) - math.Pi/2) * 180 / math.Pi)
}

// ID returns the point ID.
func (b *BetweenPoint) ID() ID {
return b.id
}

// Name returns the name of a point.
func (b *BetweenPoint) Name() string {
return b.name
}

// Draw returns if the point should be drawn.
func (b *BetweenPoint) Draw() bool {
return b.draw
}

// SetDraw indicates that the point should be drawn.
func (b *BetweenPoint) SetDraw() {
b.draw = true
}

// UnsetDraw indicates that the point should not be drawn.
func (b *BetweenPoint) UnsetDraw() {
b.draw = true
}

// Hide returns if the point must remain hidden.
func (b *BetweenPoint) Hide() bool {
return b.hide
}

// SetHide indicates that the must be hidden.
func (b *BetweenPoint) SetHide() {
b.hide = true
}

+ 59
- 0
pkg/pattern/point/point.go Datei anzeigen

@@ -0,0 +1,59 @@
// Package point contains different types of points for drawing patterns.
// There are points that define an absolute position or a relative position.
// Relative can be a specific distance one point or between two other points.
package point

import (
"git.wtrh.nl/wouter/gopatterns/pkg/position"
"git.wtrh.nl/wouter/gopatterns/pkg/vector"
"github.com/tdewolff/canvas"
)

// ID defines a point id.
type ID string

// Point defines the interface for different types of points.
type Point interface {
// ID returns the point ID.
ID() ID

// Position calculates and returns the absolute [position.Position].
Position() position.Position

// Vector calculates and returns the absolute [vector.Vector].
Vector() vector.Vector

// Matrix calculates and returns the [canvas.Matrix] of a point.
Matrix() canvas.Matrix

// Name returns the name of a point.
Name() string

// SetDraw indicates that the point should be drawn.
SetDraw()

// Draw returns if the point should be drawn.
Draw() bool

// Hide returns if the point must remain hidden.
Hide() bool

// SetHide indicates that the must be hidden.
SetHide()
}

// Draw draws a specific Point to the provided [canvas.Canvas].
// The point is only drawn when the point states that it should be drawn and not must be hidden.
func Draw(c *canvas.Canvas, point Point, face *canvas.FontFace) {
if !point.Draw() || point.Hide() {
return
}

path := canvas.Circle(1.0)
m := point.Matrix()
style := canvas.Style{Fill: canvas.Paint{Color: canvas.Black}}
c.RenderPath(path, style, m)

text := canvas.NewTextLine(face, point.Name(), canvas.Bottom)
c.RenderText(text, m.Translate(2, -4))
}

+ 160
- 0
pkg/pattern/point/relative_point.go Datei anzeigen

@@ -0,0 +1,160 @@
package point

import (
"math"

"git.wtrh.nl/wouter/gopatterns/pkg/position"
"git.wtrh.nl/wouter/gopatterns/pkg/vector"
"github.com/tdewolff/canvas"
)

// RelativePoint implements Point and defines a position that is relative to a different Point.
type RelativePoint struct {
draw bool
name string
point Point
relativeOffset position.Position
id ID
hide bool
}

// Name returns the name of a point.
func (r *RelativePoint) Name() string {
return r.name
}

// Done returns the relativePoint as interface.
func (r *RelativePoint) Done() Point { //nolint:ireturn
return r
}

// MarkWith sets the ID of the point.
func (r *RelativePoint) MarkWith(n ID) *RelativePoint {
r.id = n

if r.name == "" {
r.name = string(n)
}

return r
}

// Position calculates and returns the absolute [position.Position].
func (r *RelativePoint) Position() position.Position {
p := r.point.Position()
return p.Add(r.relativeOffset)
}

// Matrix calculates and returns the [canvas.Matrix] of a point.
func (r *RelativePoint) Matrix() canvas.Matrix {
return r.point.Matrix().Translate(r.relativeOffset.Vector.Values()).Rotate(r.relativeOffset.Rotation / math.Pi * 180)
}

// Vector calculates and returns the absolute [vector.Vector].
func (r *RelativePoint) Vector() vector.Vector {
return r.Position().Vector
}

// ID returns the point ID.
func (r *RelativePoint) ID() ID {
return r.id
}

// NewRelativePointWithVector returns a new RelativePoint.
func NewRelativePointWithVector(point Point, p vector.Vector, id ID) *RelativePoint {
return &RelativePoint{
point: point,
relativeOffset: position.Position{Vector: p},
id: id,
name: string(id),
}
}

// NewRelativePoint returns a new RelativePoint.
func NewRelativePoint(point Point) *RelativePoint {
return &RelativePoint{
point: point,
}
}

// NewRotationPoint returns a new RelativePoint with a specific rotation.
func NewRotationPoint(point Point, a float64, id ID) *RelativePoint {
p := &RelativePoint{
point: point,
relativeOffset: position.Position{
Vector: vector.Vector{},
Rotation: a,
},

name: string(id),
id: id,
}

return p
}

// WithXOffset sets the x value for the relative offset.
func (r *RelativePoint) WithXOffset(value float64) *RelativePoint {
r.relativeOffset.Vector.X = value

return r
}

// WithYOffset sets the y value for the relative offset.
func (r *RelativePoint) WithYOffset(value float64) *RelativePoint {
r.relativeOffset.Vector.Y = value

return r
}

// WithRotationOffset sets the angle for the relative offset.
func (r *RelativePoint) WithRotationOffset(value float64) *RelativePoint {
r.relativeOffset.Rotation = value

return r
}

// NewRelativePointBelow returns a RelativePoint distance f below another Point.
func NewRelativePointBelow(point Point, f float64, id ID) *RelativePoint {
return NewRelativePointWithVector(point, vector.Vector{X: 0, Y: -f}, id)
}

// NewRelativePointAbove returns a RelativePoint distance f above another Point.
func NewRelativePointAbove(point Point, f float64, id ID) *RelativePoint {
return NewRelativePointWithVector(point, vector.Vector{X: 0, Y: f}, id)
}

// NewRelativePointLeft returns a RelativePoint distance f left of another Point.
func NewRelativePointLeft(point Point, f float64, id ID) *RelativePoint {
return NewRelativePointWithVector(point, vector.Vector{X: -f, Y: 0}, id)
}

// NewRelativePointRight returns a RelativePoint distance f right of another Point.
func NewRelativePointRight(point Point, f float64, id ID) *RelativePoint {
return NewRelativePointWithVector(point, vector.Vector{X: f, Y: 0}, id)
}

// Draw returns if the point should be drawn.
func (r *RelativePoint) Draw() bool {
return r.draw
}

// SetDraw indicates that the point should be drawn.
func (r *RelativePoint) SetDraw() {
r.draw = true
}

// UnsetDraw indicates that the point should not be drawn.
func (r *RelativePoint) UnsetDraw() {
r.draw = true
}

// Hide returns if the point must remain hidden.
func (r *RelativePoint) Hide() bool {
return r.hide
}

// SetHide indicates that the must be hidden.
func (r *RelativePoint) SetHide() {
r.hide = true
}

+ 119
- 0
pkg/pattern/template/fixtures/classic_trouser_block.yaml Datei anzeigen

@@ -0,0 +1,119 @@
---
points:
0:
position:
1:
position:
y: -(body_rise + 10)
relativeTo: 0
2:
position:
y: -inside_leg
relativeTo: 1
3:
position:
y: inside_leg/2+50
relativeTo: 2
4:
position:
y: body_rise/4
relativeTo: 1
5:
position:
x: -(seat/8 - 10)
relativeTo: 1
6:
position:
x: -(seat/8 - 10)
relativeTo: 4
7:
position:
x: -(seat/8 - 10)
relativeTo: 0
8:
position:
x: seat/4 + 20
relativeTo: 6
9:
position:
x: -(seat/16 + 5)
relativeTo: 5
10:
position:
x: 10
relativeTo: 7
11:
position:
x: trouser_waist/4 + 25
relativeTo: 10
12:
position:
x: trouser_bottom_width/2
relativeTo: 2
13:
position:
x: -trouser_bottom_width/2
relativeTo: 2
14:
position:
x: trouser_bottom_width/2 + 15
relativeTo: 3
15:
position:
x: -(trouser_bottom_width/2 + 15)
relativeTo: 3

panels:
front:
points:
extend12-14:
between:
from: 12
to: 14
offset: 1.3
between11-8:
between:
from: 8
to: 11
offset: 0.5
between9-15:
between:
from: 15
to: 9
offset: 0.5
offset_between11-8:
position:
x: 5
relativeTo: between11-8
offset_between9-15:
position:
x: 5
relativeTo: between9-15
extend13-15:
between:
from: 13
to: 15
offset: 1.5
r5:
position:
rotation: 3*pi/4
relativeTo: 5
h5:
position:
x: 30
relativeTo: r5
hide: true
lines:
- through: [0,4,1,3,2]
- through: [14,12,13,15]
- through: [14,8,11]
curve:
start: extend12-14
end: offset_between11-8
- through: [15,9]
curve:
start: extend13-15
- through: [9, h5, 6]
curve: {}
- through: [6, 10, 11]


+ 9
- 0
pkg/pattern/template/fixtures/trouser.yaml Datei anzeigen

@@ -0,0 +1,9 @@
---
owner: Wouter
sizes:
body_rise: 281
inside_leg: 800
seat: 1020
trouser_waist: 900
trouser_bottom_width: 226
template: fixtures/classic_trouser_block.yaml

+ 6
- 0
pkg/pattern/template/information.go Datei anzeigen

@@ -0,0 +1,6 @@
package template

type Information struct {
Point `yaml:",inline"`
Anchor string `yaml:"anchor"`
}

+ 53
- 0
pkg/pattern/template/line.go Datei anzeigen

@@ -0,0 +1,53 @@
package template

import (
"git.wtrh.nl/wouter/gopatterns/pkg/pattern"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/path"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/point"
)

// Lines contains one Line or more in a slice.
type Lines []Line

// Line describes a pattern line.
type Line struct {
Through []point.ID `yaml:"through"`
Curve *Curve `yaml:"curve,omitempty"`
}

// Curve describes if a Line curves and if it has start and end constraints.
type Curve struct {
Start point.ID `yaml:"start,omitempty"`
End point.ID `yaml:"end,omitempty"`
}

// Build adds the line to the provided [pattern.Pattern].
func (l Line) Build(pat *pattern.Pattern) error {
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)
pat.AddLine(path.NewSpline(startPoint, endPoint, points...))
default:
pat.AddLine(path.NewPath(points...))
}

return nil
}

// Build adds all the lines to the provided [pattern.Pattern].
func (l Lines) Build(pat *pattern.Pattern) error {
for _, line := range l {
err := line.Build(pat)
if err != nil {
return err
}
}

return nil
}

+ 18
- 0
pkg/pattern/template/panel.go Datei anzeigen

@@ -0,0 +1,18 @@
package template

// Panels contains a map with named panels.
type Panels map[string]Panel

// Panel contains all the lines and extra points to draw a panel.
type Panel struct {
Points Points `yaml:"points"`
Lines Lines `yaml:"lines"`
Allowances Allowances `yaml:"allowances"`
Name string `yaml:"name"`
Information Information `yaml:"information"`
}

type Allowances struct {
Hem string `yaml:"hem"`
Seam string `yaml:"seam"`
}

+ 247
- 0
pkg/pattern/template/point.go Datei anzeigen

@@ -0,0 +1,247 @@
package template

import (
"errors"
"fmt"
"gopkg.in/Knetic/govaluate.v3"
"math"
"strconv"

"git.wtrh.nl/wouter/gopatterns/pkg/pattern"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/point"
)

const maxRecursionDepth = 100

var (
// ErrPointNotFound is returned when a required point is not defined.
ErrPointNotFound = errors.New("required point not found")

// ErrRelativePointRecursion is returned when a points are relative to itself.
ErrRelativePointRecursion = errors.New("point cannot be relative to itself")
)

// Points contains a map with points.
type Points map[point.ID]Point

// Point contains the template information for a point.
type Point struct {
Position Position `yaml:"position"`
RelativeTo *point.ID `yaml:"relativeTo,omitempty"`
Description string `yaml:"description"`
Between *BetweenPoint `yaml:"between"`
Hide bool `yaml:"hide"`
}

var ErrInvalidPointID = errors.New("type cannot be converted to a PointID")

func (p Points) Functions(pat *pattern.Pattern) map[string]govaluate.ExpressionFunction {
return map[string]govaluate.ExpressionFunction{
"DistanceBetween": func(args ...interface{}) (interface{}, error) {
id0, err := toPointID(args[0])
if err != nil {
return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
}

id1, err := toPointID(args[1])
if err != nil {
return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
}

p0, err := p.getOrCreate(id0, pat, 0)
if err != nil {
return nil, fmt.Errorf("get or create point %q: %w", id0, err)
}

p1, err := p.getOrCreate(id1, pat, 0)
if err != nil {
return nil, fmt.Errorf("get or create point %q: %w", id1, err)
}

return p0.Position().Distance(p1.Position()), nil
},
"AngleBetween": func(args ...interface{}) (interface{}, error) {
id0, err := toPointID(args[0])
if err != nil {
return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
}

id1, err := toPointID(args[1])
if err != nil {
return nil, fmt.Errorf("parsing args[0] to pointID: %w", err)
}

p0, err := p.getOrCreate(id0, pat, 0)
if err != nil {
return nil, fmt.Errorf("get or create point %q: %w", id0, err)
}

p1, err := p.getOrCreate(id1, pat, 0)
if err != nil {
return nil, fmt.Errorf("get or create point %q: %w", id1, err)
}

return p0.Vector().AngleBetween(p1.Vector()), nil
},
}
}

func toPointID(arg interface{}) (point.ID, error) {
v1, ok := arg.(string)
if !ok {
f, ok := arg.(float64)
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
}

// BetweenPoint contains the template information for a point in between two other points.
type BetweenPoint struct {
From point.ID `yaml:"from"`
To point.ID `yaml:"to"`
Offset *Value `yaml:"offset"`
}

func (p Points) evaluationFunctions() map[string]govaluate.ExpressionFunction {
return map[string]govaluate.ExpressionFunction{
"acos": func(args ...interface{}) (interface{}, error) {
return math.Acos(args[0].(float64)), nil
},
"atan2": func(args ...interface{}) (interface{}, error) {
return math.Atan2(args[0].(float64), args[1].(float64)), nil
},
}
}

// AddToPattern will add all points to the provided [pattern.Pattern].
func (p Points) AddToPattern(pat *pattern.Pattern) error {
for id := range p {
if pat.GetPoint(id) == nil {
err := p.addSingleToPattern(id, pat, 0)
if err != nil {
return err
}
}
}

return nil
}

func (p Points) addSingleToPattern(id point.ID, pat *pattern.Pattern, depth int) (err error) {
templatePoint, ok := p[id]
if !ok {
return ErrPointNotFound
}

var newPoint point.Point

switch {
case templatePoint.RelativeTo != nil:
newPoint, err = p.createRelative(id, pat, depth)
if err != nil {
return err
}
case templatePoint.Between != nil:
newPoint, err = p.createBetween(id, pat, depth)
if err != nil {
return err
}
default:
x, y, r, err := templatePoint.Position.evaluate(pat.Parameters(), p.Functions(pat))
if err != nil {
return err
}

newPoint = point.NewAbsolutePoint(x, y, r, id)
}

if templatePoint.Hide {
newPoint.SetHide()
}

pat.AddPoint(newPoint)

return nil
}

func (p Points) createRelative(
id point.ID,
pat *pattern.Pattern,
depth int,
) (*point.RelativePoint, error) {
templatePoint, ok := p[id]
if !ok {
return nil, ErrPointNotFound
}

relativePointID := *templatePoint.RelativeTo
if relativePointID == id || depth > maxRecursionDepth {
return nil, ErrRelativePointRecursion
}

relativePoint, err := p.getOrCreate(relativePointID, pat, depth)
if err != nil {
return nil, err
}

x, y, r, err := templatePoint.Position.evaluate(pat.Parameters(), p.Functions(pat))
if err != nil {
return nil, err
}

return point.NewRelativePoint(relativePoint).
WithXOffset(x).WithYOffset(y).WithRotationOffset(r).
MarkWith(id), nil
}

//nolint:ireturn
func (p Points) createBetween(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
newPoint, ok := p[id]
if !ok {
return nil, ErrPointNotFound
}

if newPoint.Between.To == id || newPoint.Between.From == id || depth > maxRecursionDepth {
return nil, ErrRelativePointRecursion
}

fromPoint, err := p.getOrCreate(newPoint.Between.From, pat, depth)
if err != nil {
return nil, err
}

toPoint, err := p.getOrCreate(newPoint.Between.To, pat, depth)
if err != nil {
return nil, err
}

params := pat.Parameters()

offset, err := newPoint.Between.Offset.Evaluate(params, p.Functions(pat))
if err != nil {
return nil, err
}

return point.NewBetweenPoint(fromPoint, toPoint, offset, id), nil
}

//nolint:ireturn
func (p Points) getOrCreate(id point.ID, pat *pattern.Pattern, depth int) (point.Point, error) {
if pat.GetPoint(id) == nil {
err := p.addSingleToPattern(id, pat, depth+1)
if err != nil {
return nil, err
}
}

createdPoint := pat.GetPoint(id)
if createdPoint == nil {
panic("getPoint cannot be nil")
}

return createdPoint, nil
}

+ 28
- 0
pkg/pattern/template/position.go Datei anzeigen

@@ -0,0 +1,28 @@
package template

import "gopkg.in/Knetic/govaluate.v3"

type Position struct {
X *Value
Y *Value
Rotation *Value
}

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
}

y, err = p.Y.Evaluate(params, funcs)
if err != nil {
return 0, 0, 0, err
}

rotation, err = p.Rotation.Evaluate(params, funcs)
if err != nil {
return 0, 0, 0, err
}

return
}

+ 115
- 0
pkg/pattern/template/renderer.go Datei anzeigen

@@ -0,0 +1,115 @@
package template

import (
"fmt"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern"
"git.wtrh.nl/wouter/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.
func (s Storage) RenderPatterns(request Request, outputDir string) ([]string, error) {
template, err := s.LoadTemplate(request.Template)
if err != nil {
return nil, fmt.Errorf("load pattern %q: %w", request.Template, err)
}

filenames := make([]string, 0, len(template.Panels))
dim, err := s.Dimensions(request.Sizes)

renderer := Renderer{dimensions: dim, owner: request.Owner, pattern: request.Template}

for name, panel := range template.Panels {
pat := pattern.NewPattern()
pat.SetDimensions(dim)

err = template.Points.AddToPattern(pat)
if err != nil {
return nil, fmt.Errorf("add generic points to pattern: %w", err)
}

err = renderer.BuildPanel(panel, pat)
if err != nil {
return nil, fmt.Errorf("constructing %s panel: %w", name, err)
}

c := canvas.New(200, 200)

err = pat.ToCanvas(c)
if err != nil {
return nil, fmt.Errorf("write pattern to canvas: %w", err)
}

c.Fit(10)

filename := filepath.Join(outputDir, strings.Join([]string{request.Template, name,
strcase.SnakeCase(request.Owner)}, "_")+".pdf")

filenames = append(filenames, filename)

err = renderers.Write(filename, c)
if err != nil {
return nil, fmt.Errorf("write canvas to file: %w", err)
}
}
return filenames, nil
}

type Renderer struct {
dimensions pattern.Dimensions
owner string
pattern string
}

// BuildPanel translates the panel to the provided [pattern.Pattern].
func (r Renderer) BuildPanel(panel Panel, pat *pattern.Pattern) error {
err := panel.Points.AddToPattern(pat)
if err != nil {
return err
}

err = panel.Lines.Build(pat)
if err != nil {
return err
}

err = r.GenerateInformation(panel, pat)
if err != nil {
return err
}

return nil
}

func (r Renderer) GenerateInformation(p Panel, pat *pattern.Pattern) error {
err := Points{"_information": p.Information.Point}.AddToPattern(pat)
if err != nil {
return err
}

dimensions := make([]string, 0, len(r.dimensions))
for _, dimension := range r.dimensions {
dimensions = append(dimensions,
fmt.Sprintf(" %s: %.1f cm", dimension.Name, dimension.Value/10))
}

slices.Sort(dimensions)
dimensions = append([]string{
"For: " + r.owner,
"Pattern: " + startCase(r.pattern),
"Panel: " + p.Name,
"Hem allowance: " + p.Allowances.Hem,
"Seam allowance: " + p.Allowances.Seam,
"\nMeasurements:",
}, dimensions...)

point := pat.GetPoint("_information")
point.SetHide()
pat.AddText(text.NewText(point, "", strings.Join(dimensions, "\n")))
return nil
}

+ 74
- 0
pkg/pattern/template/storage.go Datei anzeigen

@@ -0,0 +1,74 @@
package template

import (
"fmt"
"git.wtrh.nl/wouter/gopatterns/pkg/pattern"
"gopkg.in/yaml.v3"
"io/fs"
"os"
)

type Storage struct {
dir fs.FS
}

func NewStorage(dir string) (Storage, error) {
_, err := os.Stat(dir)
if err != nil {
return Storage{}, err
}

return Storage{dir: os.DirFS(dir)}, nil
}

func (s Storage) Dimensions(sizes Sizes) (pattern.Dimensions, error) {
f, err := s.dir.Open("dimension_names.yaml")
if err != nil {
return nil, fmt.Errorf("open \"dimension_names.yaml\": %w", err)
}

namedDimensions := pattern.Dimensions{}

err = yaml.NewDecoder(f).Decode(&namedDimensions)
if err != nil {
return nil, fmt.Errorf("decode yaml from \"dimension_names.yaml\": %w", err)
}

for id, dimension := range namedDimensions {
size, ok := sizes[string(id)]
if !ok {
delete(namedDimensions, id)
continue
}

dimension.Value = size
namedDimensions[id] = dimension
}

return namedDimensions, nil
}

// Request contains the information to draw a pattern for a specific owner, with corresponding
// sizes and the template name.
type Request struct {
Sizes Sizes `yaml:"sizes,omitempty"`
Owner string `yaml:"owner"`
Template string `yaml:"template,omitempty"`
}

// LoadTemplate reads and decodes a [Template] from a yaml file.
func (s Storage) LoadTemplate(name string) (Template, error) {
fh, err := s.dir.Open(name + ".yaml")
if err != nil {
return Template{}, fmt.Errorf("open template file %q: %w", name, err)
}

template := Template{}

err = yaml.NewDecoder(fh).Decode(&template)
if err != nil {
return Template{}, fmt.Errorf("decode content of file %q as yaml: %w", name, err)
}

return template, nil
}

+ 56
- 0
pkg/pattern/template/template.go Datei anzeigen

@@ -0,0 +1,56 @@
// Package template makes it possible to describe templates and patterns in yaml files and
// to render them to SVG.
package template

import (
"fmt"
"os"
"unicode"

"gopkg.in/yaml.v3"
)

// Sizes defines a map with the size name and the size value.
type Sizes map[string]float64

// Template contains the generic information to draw a specific clothing pattern.
type Template struct {
Points `yaml:"points"`
Panels `yaml:"panels"`
}

// LoadPattern reads and decodes a [Request] from a yaml file.
func LoadPattern(name string) (Request, error) {
fh, err := os.Open(name)
if err != nil {
return Request{}, fmt.Errorf("open pattern file %q: %w", name, err)
}

pat := Request{}

err = yaml.NewDecoder(fh).Decode(&pat)
if err != nil {
return Request{}, fmt.Errorf("decode content of file %q as yaml: %w", name, err)
}

return pat, nil
}

func startCase(text string) string {
output := make([]rune, len(text))

for i, val := range text {
switch {
case i == 0:
output[i] = unicode.ToUpper(val)
case val == '_':
output[i] = ' '
case output[i-1] == ' ':
output[i] = unicode.ToUpper(val)
default:
output[i] = val
}
}

return string(output)
}

+ 14
- 0
pkg/pattern/template/template_test.go Datei anzeigen

@@ -0,0 +1,14 @@
package template_test

import (
"testing"

"git.wtrh.nl/wouter/gopatterns/pkg/pattern/template"
"github.com/stretchr/testify/require"
)

func TestRenderPattern(t *testing.T) {
t.Parallel()

require.NoError(t, template.RenderPattern("fixtures/trouser.yaml"))
}

+ 38
- 0
pkg/pattern/template/value.go Datei anzeigen

@@ -0,0 +1,38 @@
package template

import (
"errors"
"fmt"

"gopkg.in/Knetic/govaluate.v3"
)

// ErrNonFloatValue is returned when the result of the expression is not a float64.
var ErrNonFloatValue = errors.New("failed to cast expression result for float64")

// Value describes a measurement or dimension in the templates.
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) {
if v == nil {
return 0, nil
}

expression, err := govaluate.NewEvaluableExpressionWithFunctions(string(*v), funcs)
if err != nil {
return 0, fmt.Errorf("create new evaluable expression for %q: %w", *v, err)
}

result, err := expression.Evaluate(parameters)
if err != nil {
return 0, fmt.Errorf("evaluable expression for %q: %w", *v, err)
}

f, ok := result.(float64)
if !ok {
return 0, fmt.Errorf("cast %v to float: %w", result, ErrNonFloatValue)
}

return f, nil
}

+ 21
- 0
pkg/pattern/text/text.go Datei anzeigen

@@ -0,0 +1,21 @@
package text

import (
"git.wtrh.nl/wouter/gopatterns/pkg/pattern/point"
"github.com/tdewolff/canvas"
)

type Text struct {
point.Point
anchor string
text string
}

func NewText(point point.Point, anchor string, text string) *Text {
return &Text{Point: point, anchor: anchor, text: text}
}

func (t Text) ToCanvas(c *canvas.Canvas, face *canvas.FontFace) {
text := canvas.NewTextLine(face, t.text, canvas.Left)
c.RenderText(text, t.Matrix())
}

+ 39
- 0
pkg/position/position.go Datei anzeigen

@@ -0,0 +1,39 @@
// Package position is a simple 2D position library.
// Positions consist of a vector.Vector and a rotation.
package position

import (
"math"

"git.wtrh.nl/wouter/gopatterns/pkg/vector"
)

// Position contains a 2D vector and a rotation angle.
type Position struct {
Vector vector.Vector
Rotation float64
}

// Add a position.
func (p Position) Add(q Position) Position {
return Position{
Vector: p.Vector.Add(q.Vector.Rotate(p.Rotation)),
Rotation: p.Rotation + q.Rotation,
}
}

// Translate the position to a new reference frame.
func (p Position) Translate(q Position) Position {
q.Vector = q.Vector.Span(q.Rotation)
return p.Add(q)
}

// Distance returns the distance between two positions.
func (p Position) Distance(q Position) float64 {
return p.Vector.Distance(q.Vector)
}

// Direction returns the angle to another position.
func (p Position) Direction(q Position) float64 {
return math.Remainder(p.Vector.AngleBetween(q.Vector)-p.Rotation, 2*math.Pi)
}

+ 100
- 0
pkg/position/position_test.go Datei anzeigen

@@ -0,0 +1,100 @@
package position_test

import (
"math"
"testing"

"git.wtrh.nl/wouter/gopatterns/pkg/position"
"git.wtrh.nl/wouter/gopatterns/pkg/vector"
"github.com/stretchr/testify/require"
)

func TestPosition_Direction(t *testing.T) {
t.Parallel()

type testCase struct {
A, B position.Position
ExpA2B, ExpB2A float64
}

testCases := []testCase{
{
A: position.Position{
Vector: vector.Vector{X: 0, Y: 0},
Rotation: math.Pi / 4,
},
B: position.Position{
Vector: vector.Vector{X: 2, Y: 0},
Rotation: 3 * math.Pi / 4,
},
ExpA2B: -math.Pi / 4,
ExpB2A: math.Pi / 4,
},
{
A: position.Position{
Vector: vector.Vector{X: 0, Y: 1},
Rotation: math.Pi,
},
B: position.Position{
Vector: vector.Vector{X: 1, Y: 0},
Rotation: -math.Pi / 2,
},
ExpA2B: 3 * math.Pi / 4,
ExpB2A: -3 * math.Pi / 4,
},
}

for _, test := range testCases {
actA2B := test.A.Direction(test.B)
require.InDelta(t, test.ExpA2B, actA2B, 1e-12)

actB2A := test.B.Direction(test.A)
require.InDelta(t, test.ExpB2A, actB2A, 1e-12)
}
}

func TestPosition_Add(t *testing.T) {
t.Parallel()

tests := map[string]struct {
p, q position.Position
want position.Position
}{
"straight angle": {
p: position.Position{
Vector: vector.Vector{X: 1, Y: 0},
Rotation: math.Pi / 2,
},
q: position.Position{
Vector: vector.Vector{X: 1, Y: 0},
Rotation: 0,
},
want: position.Position{
Vector: vector.Vector{X: 1, Y: 1},
Rotation: math.Pi / 2,
},
},
"3-4-5 triangle": {
p: position.Position{
Vector: vector.Vector{X: -2, Y: 0},
Rotation: math.Atan2(3, 4),
},
q: position.Position{
Vector: vector.Vector{X: 5, Y: 0},
Rotation: -math.Atan2(3, 4),
},
want: position.Position{
Vector: vector.Vector{X: 2, Y: 3},
Rotation: 0,
},
},
}

for name, tt := range tests {
t.Run(name, func(t *testing.T) {
t.Parallel()

require.Equal(t, tt.want, tt.p.Add(tt.q))
})
}
}

+ 0
- 134
pkg/util/point.go Datei anzeigen

@@ -1,134 +0,0 @@
package util

import (
"fmt"
"math"
)

// Point represents a 2 dimensional Point
type Point struct {
X, Y float64
}

// Abs returns a Point with the absolute value of X and Y.
// Should not be confused with Magnitude due to its notation ||A||.
func (p Point) Abs() Point {
return Point{
math.Abs(p.X),
math.Abs(p.Y),
}
}

// Add returns a Point with the value of Point p plus Point q.
func (p Point) Add(q Point) Point {
return Point{
p.X + q.X,
p.Y + q.Y,
}
}

// Subtract returns a Point with the value of Point p minus Point q.
func (p Point) Subtract(q Point) Point {
return Point{
X: p.X - q.X,
Y: p.Y - q.Y,
}
}

// Multiply returns a Point with the value of Point p multiplied by f.
func (p Point) Multiply(f float64) Point {
return Point{X: p.X * f, Y: p.Y * f}
}

// Divide returns a Point with the value of Point p divided by f.
func (p Point) Divide(f float64) Point {
return Point{X: p.X / f, Y: p.Y / f}
}

// Round returns a Point with the rounded value of Point p.
func (p Point) Round() Point {
return Point{X: math.Round(p.X), Y: math.Round(p.Y)}
}

// Floor returns a Point with the floored value of Point p.
func (p Point) Floor() Point {
return Point{X: math.Floor(p.X), Y: math.Floor(p.Y)}
}

// Ceil returns a Point with the rounded up value of Point p.
func (p Point) Ceil() Point {
return Point{X: math.Ceil(p.X), Y: math.Ceil(p.Y)}
}

// Scale returns a Point with the values of Point p scaled up by the values of Point q.
func (p Point) Scale(q Point) Point {
return Point{
X: p.X * q.X,
Y: p.Y * q.Y,
}
}

// ScaleDown returns a Point with the values of Point p scaled down by the values of Point q.
func (p Point) ScaleDown(q Point) Point {
return Point{
X: p.X / q.X,
Y: p.Y / q.Y,
}
}

// Min returns a Point with the smallest values of X and Y for Point p and q.
func (p Point) Min(q Point) Point {
return Point{
X: math.Min(p.X, q.X),
Y: math.Min(p.Y, q.Y),
}
}

// Max returns a Point with the largest values of X and Y for Point p and q.
func (p Point) Max(q Point) Point {
return Point{
X: math.Max(p.X, q.X),
Y: math.Max(p.Y, q.Y),
}
}

// String returns a string with comma separated values of Point p.
func (p Point) String() string {
return fmt.Sprintf("%v,%v", p.X, p.Y)
}

// Magnitude returns a float64 with the length/magnitude of Point p.
func (p Point) Magnitude() float64 {
return math.Sqrt(math.Pow(p.X, 2) + math.Pow(p.Y, 2))
}

func (p Point) Unit() Point {
return p.Divide(p.Magnitude())
}

func (p Point) Rotate(a float64) Point {
return Point{
X: math.Cos(a)*p.X - math.Sin(a)*p.Y,
Y: math.Sin(a)*p.X + math.Cos(a)*p.Y,
}
}

func (p Point) Distance(q Point) float64 {
return p.Subtract(q).Magnitude()
}

func (p Point) Above(l float64) Point {
return p.Subtract(Point{Y: l})
}

func (p Point) Below(l float64) Point {
return p.Add(Point{Y: l})
}

func (p Point) Right(l float64) Point {
return p.Add(Point{X: l})
}

func (p Point) Left(l float64) Point {
return p.Subtract(Point{X: l})
}

+ 0
- 73
pkg/util/point_list.go Datei anzeigen

@@ -1,73 +0,0 @@
package util

import (
"fmt"
"strconv"

svg "github.com/ajstarks/svgo/float"
)

type PointMap map[int]Point

func (pm PointMap) Scale(s float64) PointMap {
pointMap := make(PointMap, len(pm))

for i, p := range pm {
pointMap[i] = p.Multiply(s)
}

return pointMap

}

func (pm PointMap) Box() (Point, Point) {
min := Point{}
max := Point{}

for _, p := range pm {
min = min.Min(p)
max = max.Max(p)
}

return min, max
}

func (pm PointMap) Offset(point Point) PointMap {
pointMap := make(PointMap, len(pm))

for i, p := range pm {
pointMap[i] = p.Subtract(point)
}

return pointMap
}

func (pm PointMap) Normalize() PointMap {
min, _ := pm.Box()
return pm.Offset(min)
}

func (pm PointMap) Draw(canvas *svg.SVG) {
for i, point := range pm.Normalize() {
canvas.Circle(point.X, point.Y, 2.0, "stroke:black;fill:black")
canvas.Text(point.X+2, point.Y+2, strconv.Itoa(i))
}
}

func (pm PointMap) Line(canvas *svg.SVG, lines ...int) {
for i := 0; i < len(lines)-1; i++ {
start := lines[i]
end := lines[i+1]
canvas.Line(pm[start].X, pm[start].Y, pm[end].X, pm[end].Y, "stroke:black;stroke-width:3")
}
}

func (pm PointMap) Bezier(canvas *svg.SVG, lines ...int) error {
if len(lines) != 4 {
return fmt.Errorf("expected 4 line definitons, got %d instead", len(lines))
}

canvas.Bezier(pm[lines[0]].X, pm[lines[0]].Y, pm[lines[1]].X, pm[lines[1]].Y, pm[lines[2]].X, pm[lines[2]].Y, pm[lines[3]].X, pm[lines[3]].Y, "stroke:black;fill:none")

return nil
}

+ 0
- 13
pkg/util/svg_helper.go Datei anzeigen

@@ -1,13 +0,0 @@
package util

import (
svg "github.com/ajstarks/svgo/float"
)

func Bezier(canvas *svg.SVG, start, c1, c2, end Point) {
canvas.Bezier(start.X, start.Y, c1.X, c1.Y, c2.X, c2.Y, end.X, end.Y, "fill:none;stroke:black;stroke-width:3")
}

func Qbez(canvas *svg.SVG, start, control, end Point) {
canvas.Qbez(start.X, start.Y, control.X, control.Y, end.X, end.Y, "fill:none;stroke:black;stroke-width:3")
}

+ 168
- 0
pkg/vector/vector.go Datei anzeigen

@@ -0,0 +1,168 @@
// Package vector is a simple 2D vector math package.
package vector

import (
"fmt"
"math"
)

// Vector represents a 2 dimensional Vector.
type Vector struct {
X, Y float64
}

// Values returns the x, y values of the Vector.
func (v Vector) Values() (x, y float64) {
return v.X, v.Y
}

// Abs returns a Vector with the absolute value of X and Y.
// Should not be confused with Magnitude due to its notation ||A||.
func (v Vector) Abs() Vector {
return Vector{
math.Abs(v.X),
math.Abs(v.Y),
}
}

// Add returns a Vector with the value of Vector p plus Vector q.
func (v Vector) Add(q Vector) Vector {
return Vector{
v.X + q.X,
v.Y + q.Y,
}
}

// Subtract returns a Vector with the value of Vector p minus Vector q.
func (v Vector) Subtract(q Vector) Vector {
return Vector{
X: v.X - q.X,
Y: v.Y - q.Y,
}
}

// Multiply returns a Vector with the value of Vector p multiplied by f.
func (v Vector) Multiply(f float64) Vector {
return Vector{X: v.X * f, Y: v.Y * f}
}

// Divide returns a Vector with the value of Vector p divided by f.
func (v Vector) Divide(f float64) Vector {
return Vector{X: v.X / f, Y: v.Y / f}
}

// Round returns a Vector with the rounded value of Vector p.
func (v Vector) Round() Vector {
return Vector{X: math.Round(v.X), Y: math.Round(v.Y)}
}

// Floor returns a Vector with the floored value of Vector p.
func (v Vector) Floor() Vector {
return Vector{X: math.Floor(v.X), Y: math.Floor(v.Y)}
}

// Ceil returns a Vector with the rounded up value of Vector p.
func (v Vector) Ceil() Vector {
return Vector{X: math.Ceil(v.X), Y: math.Ceil(v.Y)}
}

// Scale returns a Vector with the values of Vector p scaled up by the values of Vector q.
func (v Vector) Scale(q Vector) Vector {
return Vector{
X: v.X * q.X,
Y: v.Y * q.Y,
}
}

// ScaleDown returns a Vector with the values of Vector p scaled down by the values of Vector q.
func (v Vector) ScaleDown(q Vector) Vector {
return Vector{
X: v.X / q.X,
Y: v.Y / q.Y,
}
}

// Min returns a Vector with the smallest values of X and Y for Vector p and q.
func (v Vector) Min(q Vector) Vector {
return Vector{
X: math.Min(v.X, q.X),
Y: math.Min(v.Y, q.Y),
}
}

// Max returns a Vector with the largest values of X and Y for Vector p and q.
func (v Vector) Max(q Vector) Vector {
return Vector{
X: math.Max(v.X, q.X),
Y: math.Max(v.Y, q.Y),
}
}

// String returns a string with comma separated values of Vector p.
func (v Vector) String() string {
return fmt.Sprintf("%v,%v", v.X, v.Y)
}

// Magnitude returns a float64 with the length/magnitude of Vector p.
func (v Vector) Magnitude() float64 {
return math.Sqrt(math.Pow(v.X, 2) + math.Pow(v.Y, 2))
}

// Unit returns a vector with the same direction but with a length of 1.
func (v Vector) Unit() Vector {
m := v.Magnitude()
if m == 0 {
panic("Cannot create unit vector of a vector with length 0.")
}

return v.Divide(m)
}

// Rotate the vector with an angle in radians.
func (v Vector) Rotate(a float64) Vector {
return Vector{
X: math.Cos(a)*v.X - math.Sin(a)*v.Y,
Y: math.Sin(a)*v.X + math.Cos(a)*v.Y,
}
}

// Span returns the difference between a vector and the rotated version of the vector.
func (v Vector) Span(a float64) Vector {
return v.Subtract(v.Rotate(a))
}

// Distance between two vectors.
func (v Vector) Distance(q Vector) float64 {
return v.Subtract(q).Magnitude()
}

// Angle of the vector.
func (v Vector) Angle() float64 {
return math.Atan2(v.Y, v.X)
}

// AngleBetween returns the angle of the line between to vector endpoints.
func (v Vector) AngleBetween(q Vector) float64 {
diff := q.Subtract(v)
return diff.Angle()
}

// Above returns a vector that is length l above the existing vector.
func (v Vector) Above(l float64) Vector {
return v.Add(Vector{Y: l})
}

// Below returns a vector that is length l below the existing vector.
func (v Vector) Below(l float64) Vector {
return v.Subtract(Vector{Y: l})
}

// Right returns a vector that is length l right of the existing vector.
func (v Vector) Right(l float64) Vector {
return v.Add(Vector{X: l})
}

// Left returns a vector that is length l left of the existing vector.
func (v Vector) Left(l float64) Vector {
return v.Subtract(Vector{X: l})
}

+ 487
- 0
pkg/vector/vector_test.go Datei anzeigen

@@ -0,0 +1,487 @@
package vector_test

import (
"math"
"testing"

"git.wtrh.nl/wouter/gopatterns/pkg/vector"
"github.com/stretchr/testify/assert"
)

func TestVector_Abs(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: -1,
Y: 1,
}.Abs()
exp := vector.Vector{
X: 1,
Y: 1,
}

assert.Equal(t, exp, act)
}

func TestVector_Abs2(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 0,
Y: -2,
}.Abs()
exp := vector.Vector{
X: 0,
Y: 2,
}

assert.Equal(t, exp, act)
}

func TestVector_Subtract(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.5,
Y: 4,
}
v2 := vector.Vector{
X: 7,
Y: -3,
}
act := v1.Subtract(v2)
exp := vector.Vector{
X: 5.5,
Y: 7,
}

assert.Equal(t, exp, act)
}

func TestVector_Add(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.5,
Y: 4,
}
v2 := vector.Vector{
X: 7,
Y: -3,
}
act := v1.Add(v2)
exp := vector.Vector{
X: 19.5,
Y: 1,
}

assert.Equal(t, exp, act)
}

func TestVector_Multiply(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.5,
Y: 4,
}
act := v1.Multiply(7)
exp := vector.Vector{
X: 87.5,
Y: 28,
}

assert.Equal(t, exp, act)
}

func TestVector_Divide(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 15,
Y: 3,
}
act := v1.Divide(3)
exp := vector.Vector{
X: 5,
Y: 1,
}

assert.Equal(t, exp, act)
}

func TestVector_Divide2(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: -15,
Y: 3,
}
act := v1.Divide(0)
exp := vector.Vector{
X: math.Inf(-1),
Y: math.Inf(1),
}

assert.Equal(t, exp, act)
}

func TestVector_Round(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.5,
Y: 4.3,
}
act := v1.Round()
exp := vector.Vector{
X: 13,
Y: 4,
}

assert.Equal(t, exp, act)
}

func TestVector_Floor(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.9,
Y: 4.3,
}
act := v1.Floor()
exp := vector.Vector{
X: 12,
Y: 4,
}

assert.Equal(t, exp, act)
}

func TestVector_Ciel(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.9,
Y: 4.3,
}
act := v1.Ceil()
exp := vector.Vector{
X: 13,
Y: 5,
}

assert.Equal(t, exp, act)
}

func TestVector_Scale(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.5,
Y: 4,
}
v2 := vector.Vector{
X: 7,
Y: -3,
}
act := v1.Scale(v2)
exp := vector.Vector{
X: 87.5,
Y: -12,
}

assert.Equal(t, exp, act)
}

func TestVector_ScaleDown(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.5,
Y: 4,
}
v2 := vector.Vector{
X: 5,
Y: -2,
}
act := v1.ScaleDown(v2)
exp := vector.Vector{
X: 2.5,
Y: -2,
}

assert.Equal(t, exp, act)
}

func TestVector_Max(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.5,
Y: -4,
}
v2 := vector.Vector{
X: 5,
Y: 2,
}
act := v1.Max(v2)
exp := vector.Vector{
X: 12.5,
Y: 2,
}

assert.Equal(t, exp, act)
}

func TestVector_Min(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 12.5,
Y: -4,
}
v2 := vector.Vector{
X: 5,
Y: 2,
}
act := v1.Min(v2)
exp := vector.Vector{
X: 5,
Y: -4,
}

assert.Equal(t, exp, act)
}

func TestVector_String(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 12.5,
Y: 4,
}.String()
exp := "12.5,4"

assert.Equal(t, exp, act)
}

func TestVector_Magnitude(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 3,
Y: 4,
}.Magnitude()
exp := 5.0

assert.InDelta(t, exp, act, 1e-15)
}

func TestVector_Unit_Panic(t *testing.T) {
t.Parallel()

emptyVector := vector.Vector{}

assert.Panics(t, func() { emptyVector.Unit() })
}

func TestVector_Unit(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 3,
Y: 4,
}.Unit()
exp := vector.Vector{
X: 0.6,
Y: 0.8,
}

assert.Equal(t, exp, act)
}

func TestVector_Rotate(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 2,
Y: 0,
}.Rotate(math.Pi / 2)
exp := vector.Vector{
X: 0,
Y: 2,
}

assert.InDelta(t, exp.X, act.X, math.Pow10(-15))
assert.InDelta(t, exp.Y, act.Y, math.Pow10(-15))
}

func TestVector_Rotate2(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 2,
Y: 0,
}.Rotate(math.Pi)
exp := vector.Vector{
X: -2,
Y: 0,
}

assert.InDelta(t, exp.X, act.X, math.Pow10(-15))
assert.InDelta(t, exp.Y, act.Y, math.Pow10(-15))
}

func TestVector_Rotate3(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 0,
Y: 2,
}.Rotate(math.Pi / 2)
exp := vector.Vector{
X: -2,
Y: 0,
}

assert.InDelta(t, exp.X, act.X, math.Pow10(-15))
assert.InDelta(t, exp.Y, act.Y, math.Pow10(-15))
}

func TestVector_Rotate4(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 0,
Y: 2,
}.Rotate(math.Pi)
exp := vector.Vector{
X: 0,
Y: -2,
}

assert.InDelta(t, exp.X, act.X, math.Pow10(-15))
assert.InDelta(t, exp.Y, act.Y, math.Pow10(-15))
}

func TestVector_Chord(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 2,
Y: 0,
}.Span(math.Pi / 2)
exp := vector.Vector{
X: 2,
Y: -2,
}

assert.InDelta(t, exp.X, act.X, math.Pow10(-15))
assert.InDelta(t, exp.Y, act.Y, math.Pow10(-15))
}

func TestVector_Distance(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: 3,
Y: 0,
}
v2 := vector.Vector{
X: 0,
Y: 4,
}

act := v1.Distance(v2)
exp := 5.0
assert.InDelta(t, exp, act, 1e-15)
}

func TestVector_Angle(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: 3,
Y: 3,
}.Angle()
exp := math.Pi / 4

assert.InDelta(t, exp, act, 1e-15)
}

func TestVector_Angle2(t *testing.T) {
t.Parallel()

act := vector.Vector{
X: -3,
Y: 3,
}.Angle()
exp := 3 * math.Pi / 4

assert.InDelta(t, exp, act, 1e-15)
}

func TestVector_AngleBetween(t *testing.T) {
t.Parallel()

v1 := vector.Vector{
X: -1,
Y: 3,
}
v2 := vector.Vector{
X: 2,
Y: 6,
}
act := v1.AngleBetween(v2)
exp := math.Pi / 4
assert.InDelta(t, exp, act, 1e-15)
}

func TestVector_Above(t *testing.T) {
t.Parallel()

act := vector.Vector{}.Above(1)
exp := vector.Vector{
X: 0,
Y: 1,
}

assert.Equal(t, exp, act)
}

func TestVector_Below(t *testing.T) {
t.Parallel()

act := vector.Vector{}.Below(1)
exp := vector.Vector{
X: 0,
Y: -1,
}

assert.Equal(t, exp, act)
}

func TestVector_Right(t *testing.T) {
t.Parallel()

act := vector.Vector{}.Right(1)
exp := vector.Vector{
X: 1,
Y: 0,
}

assert.Equal(t, exp, act)
}

func TestVector_Left(t *testing.T) {
t.Parallel()

act := vector.Vector{}.Left(1)
exp := vector.Vector{
X: -1,
Y: 0,
}

assert.Equal(t, exp, act)
}

+ 102
- 0
spec/template.yaml Datei anzeigen

@@ -0,0 +1,102 @@
---
$schema: "https://json-schema.org/draft-04/schema"
id: "https://stsci.edu/schemas/yaml-schema/draft-01"
title:
YAML Schema

type: object
properties:
points:
$ref: '#/components/schemas/points'
panels:
type: object
additionalProperties:
type: object
properties:
allowances:
type: object
properties:
hem:
type: string
seam:
type: string
points:
$ref: '#/components/schemas/points'
lines:
type: array
items:
$ref: '#/components/schemas/line'
information:
allOf:
- $ref: '#/components/schemas/point'
- type: object
properties:
anchor:
type: string
enum: [top, center, bottom, left, right]

components:
schemas:
point:
type: object
properties:
position:
$ref: '#/components/schemas/position'
relativeTo:
$ref: '#/components/schemas/pointID'
description:
type: string
between:
type: object
properties:
from:
$ref: '#/components/schemas/pointID'
to:
$ref: '#/components/schemas/pointID'
offset:
$ref: '#/components/schemas/expression'
hide:
type: bool

points:
type: object
additionalProperties:
$ref: '#/components/schemas/point'

position:
type: object
properties:
y:
type: string
x:
type: string
rotation:
type: string

line:
type: object
properties:
through:
type: array
items:
$ref: '#/components/schemas/pointID'
curve:
type: object
properties:
start:
$ref: '#/components/schemas/pointID'
end:
type:
$ref: '#/components/schemas/pointID'
pointID:
oneOf:
- type: integer
- type: string
expression:
oneOf:
- type: integer
- type: string





+ 302
- 0
templates/basic_trouser_block.yaml Datei anzeigen

@@ -0,0 +1,302 @@
---
name: Basic Trouser Block
points:
0:
position: {}
1:
position:
y: -(body_rise)
relativeTo: 0
2:
position:
y: -waist_to_hip
relativeTo: 0
3:
position:
y: -waist_to_floor
relativeTo: 0
4:
position:
y: -(((waist_to_floor - body_rise)/2) - 50)
relativeTo: 1
5:
position:
x: -(hips/12 + 20)
relativeTo: 1
6:
position:
x: -(hips/12 + 20)
relativeTo: 2
7:
position:
x: -(hips/12 + 20)
relativeTo: 0
8:
position:
x: hips/4 + 5
relativeTo: 6
9:
position:
x: -(hips/16 + 10)
relativeTo: 5
10:
position:
x: 10
relativeTo: 7
11:
position:
x: waist/4 + 5
relativeTo: 10
12:
position:
x: trouser_bottom_width/2 - 5
relativeTo: 3
13:
position:
x: trouser_bottom_width/2 + 12
relativeTo: 4
14:
position:
x: -(trouser_bottom_width/2 - 5)
relativeTo: 3
15:
position:
x: -(trouser_bottom_width/2 + 12)
relativeTo: 4

16:
position:
x: DistanceBetween("1","5")/4
relativeTo: 5
17:
position:
x: DistanceBetween("16","5")
relativeTo: 6
18:
position:
x: DistanceBetween("16","5")
relativeTo: 7
19:
between:
from: 16
to: 18
offset: 0.5
20:
position:
x: 20
relativeTo: 18
21:
position:
y: 20
relativeTo: 20
22:
position:
x: (((waist/4 + 40)^2) - (DistanceBetween(20,21)^2))^(0.5)
y: -DistanceBetween(20,21)
relativeTo: 21
23:
position:
x: -(DistanceBetween(5,9)/2+8)
relativeTo: 9
24:
position:
y: -5
relativeTo: 23
25:
relativeTo: 17
position:
x: hips/4 + 15
26:
relativeTo: 12
position:
x: 10
27:
relativeTo: 13
position:
x: 10
28:
relativeTo: 14
position:
x: -10
29:
relativeTo: 15
position:
x: -10
30:
between:
from: 21
to: 22
offset: 1/3
31:
between:
from: 21
to: 22
offset: 2/3

panels:
front:
allowances:
hem: none
seam: none
information:
position:
x: 10
y: -10
relativeTo: 1
points:
5r:
position:
rotation: pi/4
relativeTo: 5
5d:
position:
y: 35
relativeTo: 5r
hide: true
6e:
between:
from: 10
to: 6
offset: 1.3
0l:
position:
x: -10
relativeTo: 0
0r:
position:
x: 10
relativeTo: 0
0b:
position:
y: -100
relativeTo: 0
15e:
between:
from: 14
to: 15
offset: 1.8
13e:
between:
from: 12
to: 13
offset: 1.8

lines:
- through: [9,1]
- through: [6,8]
- through: [15,13]
- through: [0,2,1,4,3]
- through: [6,10,0l,0b,0r,11]
- through: [9,5d,6]
curve:
start: 5
end: 6e
- through: [15,14,12,13]
- through: [15,9]
curve:
start: 15e
- through: [13,8,11]
curve:
start: 13e
back:
name: Back
information:
position:
x: 10
y: -10
relativeTo: 1
allowances:
hem: none
seam: none
points:
30l:
relativeTo: 30
position:
y: -10
hide: true
30r:
relativeTo: 30
position:
y: 10
hide: true
30b:
relativeTo: 30
position:
x: 120
hide: true
31l:
relativeTo: 31
position:
y: -10
hide: true
31r:
relativeTo: 31
position:
y: 10
hide: true
31b:
relativeTo: 31
position:
x: 100
hide: true
16r:
position:
rotation: pi/4
relativeTo: 16
16d:
position:
y: 47.5
relativeTo: 16r
hide: true
23a:
position:
x: 160
y: -30
relativeTo: 24
19e:
between:
from: 21
to: 19
offset: 1.8
29extend:
between:
from: 28
to: 29
offset: 1.7
27extend:
between:
from: 26
to: 27
offset: 1.7
3down:
position:
y: -10
relativeTo: 3
hide: true


lines:
- through: [19,21,30l,30b,30r,31l,31b,31r,22]
- through: [24,16d,19]
curve:
start: 23a
end: 19e
- through: [29,24]
curve:
start: 29extend
- through: [27,25,22]
curve:
start: 27extend
- through: [29,28]
- through: [27,26]
- through: [28,3down, 26]
curve: {}
- through: [23,1]
- through: [6,25]
- through: [29,27]
- through: [0,2,1,4,3]






+ 215
- 0
templates/basis_grondpatroon_heren.yaml Datei anzeigen

@@ -0,0 +1,215 @@
---
points:
A:
position: {}
B:
position:
x: bovenwijdte/2 + 40
relativeTo: A
C:
position:
y: -(20 + ruglengte + heupdiepte)
relativeTo: A
D:
position:
x: bovenwijdte/2 + 40
relativeTo: C
E:
between:
from: A
to: B
offset: 0.5
F:
between:
from: C
to: D
offset: 0.5
G:
position:
y: -(armsgatdiepte + 40)
relativeTo: A
H:
position:
y: -(armsgatdiepte + 40)
relativeTo: B
I:
position:
y: heupdiepte
relativeTo: C
J:
position:
y: heupdiepte
relativeTo: D
K:
between:
from: G
to: H
offset: 0.5
L:
between:
from: I
to: J
offset: 0.5
M:
position:
y: -rughals
relativeTo: A
N:
position:
x: rughals + 20
relativeTo: A
O:
position:
x: borstbreedte/2
relativeTo: A
P:
position:
x: borstbreedte/2
relativeTo: G
Q:
position:
y: -40
relativeTo: O
R:
position:
x: -rughals
relativeTo: B
S:
position:
y: -20
relativeTo: B
T:
position:
x: -rugbreedte/2
y: -40
relativeTo: B
U:
position:
x: -rugbreedte/2
relativeTo: H
Nrotated:
position:
rotation: AngleBetween("N","Q")
relativeTo: N
V:
position:
x: DistanceBetween("R","W")-10
relativeTo: Nrotated

Rrotated:
position:
rotation: AngleBetween("R","T")
relativeTo: R
W:
position:
x: DistanceBetween("R","T") + 20
relativeTo: Rrotated

panels:
basis:
name: Basis
information:
position:
x: 30
y: -30
relativeTo: M
points:
Tprime:
position:
y: 20
relativeTo: T
hide: true
Eprime:
position:
x: 20
relativeTo: E
hide: true
Rprime:
position:
y: -20
relativeTo: R
hide: true
Nprime:
position:
y: -rughals
rotation: 3*pi/4
relativeTo: N
hide: true
Noffset:
position:
x: DistanceBetween("A","Nprime")/3.5
relativeTo: Nprime
hide: true
Roffset:
between:
from: S
to: Rprime
offset: 2
Arm1:
between:
from: U
to: T
offset: 0.5
hide: true
Arm2:
between:
from: P
to: Q
offset: 2/5
hide: true
Arm3:
between:
from: K
to: P
offset: 1.2
Arm4:
between:
from: K
to: U
offset: 0.8
Protated:
position:
rotation: pi/4
relativeTo: P
Poffset:
position:
x: 22
relativeTo: Protated
hide: true
Urotated:
position:
rotation: 3*pi/4
relativeTo: U
Uoffset:
position:
x: 30
relativeTo: Urotated
hide: true


lines:
- through: [N,V]
- through: [R,W]
- through: [M,G,I,C,F,L,K]
- through: [S,H,J,D,F]
- through: [M,Noffset,N]
curve:
start: Nprime
- through: [S,R]
curve:
start: Roffset
- through: [V,Arm2,Poffset,K]
curve:
end: Arm3
- through: [W,Arm1,Uoffset,K]
curve:
end: Arm4

# ooit stippellijnen
- through: [G,H,J,I]
- through: [Tprime,U]
- through: [A,O,P]
- through: [S,Eprime,K]
- through: [R,Rprime]
- through: [A,M,Nprime,N]


+ 284
- 0
templates/classic_trouser_block.yaml Datei anzeigen

@@ -0,0 +1,284 @@
---
name: Classic Trouser Block
points:
0:
position: {}
1:
position:
y: -(body_rise + 10)
relativeTo: 0
2:
position:
y: -inside_leg
relativeTo: 1
3:
position:
y: inside_leg/2+50
relativeTo: 2
4:
position:
y: body_rise/4
relativeTo: 1
5:
position:
x: -(seat/8 - 10)
relativeTo: 1
6:
position:
x: -(seat/8 - 10)
relativeTo: 4
7:
position:
x: -(seat/8 - 10)
relativeTo: 0
8:
position:
x: seat/4 + 20
relativeTo: 6
9:
position:
x: -(seat/16 + 5)
relativeTo: 5
10:
position:
x: 10
relativeTo: 7
11:
position:
x: trouser_waist/4 + 25
relativeTo: 10
12:
position:
x: trouser_bottom_width/2
relativeTo: 2
13:
position:
x: -trouser_bottom_width/2
relativeTo: 2
14:
position:
x: trouser_bottom_width/2 + 15
relativeTo: 3
15:
position:
x: -(trouser_bottom_width/2 + 15)
relativeTo: 3
16:
position:
x: (seat/8 - 10) / 4
relativeTo: 5
17:
position:
x: (seat/8 - 10) / 4
relativeTo: 6
18:
position:
x: (seat/8 - 10) / 4
relativeTo: 7
19:
between:
from: 16
to: 18
offset: 0.5
21:
position:
x: 20
y: 10
relativeTo: 18
22:
position:
x: -(seat/16 + 5)/2 + 5
relativeTo: 9
23:
position:
y: -5
relativeTo: 22
24:
position:
x: (((trouser_waist/4 + 45)^2) - (10^2))^(0.5)
y: -10
relativeTo: 21
25:
between:
from: 21
to: 24
offset: 0.5
26:
position:
x: seat/4 + 30
relativeTo: 17
27:
position:
x: 20
relativeTo: 12
28:
position:
x: -20
relativeTo: 13
29:
position:
x: 20
relativeTo: 14
30:
position:
x: -20
relativeTo: 15

panels:
front:
name: Front
information:
position:
x: 10
y: -10
relativeTo: 1
allowances:
hem: none
seam: 1cm
points:
extend12-14:
between:
from: 12
to: 14
offset: 1.3
between11-8:
between:
from: 8
to: 11
offset: 0.5
between9-15:
between:
from: 15
to: 9
offset: 0.5
offset_between11-8:
position:
x: 5
relativeTo: between11-8
offset_between9-15:
position:
x: 5
relativeTo: between9-15
extend13-15:
between:
from: 13
to: 15
offset: 1.5
r5:
position:
rotation: 3*pi/4
relativeTo: 5
h5:
position:
x: 30
relativeTo: r5
hide: true
1extend:
position:
y: -body_rise/4
relativeTo: 8
hide: true
lines:
- through: [0,4,1,3,2]
- through: [6, 8]
- through: [9, 1extend]
- through: [14,12,13,15,14]
- through: [14,8,11]
curve:
start: extend12-14
end: offset_between11-8
- through: [15,9]
curve:
start: extend13-15
- through: [9, h5, 6]
curve: {}
- through: [6, 10, 11]

back:
name: Back
information:
position:
x: 10
y: -10
relativeTo: 1
allowances:
hem: none
seam: 1cm
points:
25a:
position:
y: -12.5
relativeTo: 25
hide: true
25b:
position:
y: 12.5
relativeTo: 25
hide: true
25c:
position:
x: 120
relativeTo: 25
hide: true
2down:
position:
y: -10
relativeTo: 2
hide: true
16rotated:
position:
rotation: pi/4
relativeTo: 16
16offset:
position:
y: 45
relativeTo: 16rotated
hide: true
23a:
position:
x: 160
y: -30
relativeTo: 23
30extend:
between:
from: 28
to: 30
offset: 1.7
29extend:
between:
from: 27
to: 29
offset: 1.7
16extend:
position:
x: seat/4 + 30
relativeTo: 16
hide: true
0extend:
position:
y: 10
relativeTo: 0
hide: true

lines:
- through: [0extend, 0, 4, 1, 3, 2]
- through: [6, 17, 26]
- through: [22, 16, 16extend]
- through: [28, 30, 29, 27]
- through: [21,25a,25c,25b,24]
- through: [28,2down,27]
curve: {}
- through: [23,16offset,19,21]
curve:
start: 23a
- through: [30,23]
curve:
start: 30extend
- through: [29,26,24]
curve:
start: 29extend






+ 38
- 0
templates/dimension_names.yaml Datei anzeigen

@@ -0,0 +1,38 @@
---
seat:
name: Seat
trouser_waist:
name: Trouser waist
body_rise:
name: Body rise
inside_leg:
name: Inside leg
trouser_bottom_width:
name: Trouser bottom width
waist:
name: Waist
hips:
name: Hips
waist_to_hip:
name: Waist to hip
waist_to_floor:
name: Waist to floor

bovenwijdte:
name: Bovenwijdte
rughals:
name: Rughals
rughoogte:
name: Rughoogte
rugbreedte:
name: Rugbreedte
borstbreedte:
name: Borstbreedte
schouderlengte:
name: Schouderlengte
armsgatdiepte:
name: Armsgatdiepte
ruglengte:
name: Ruglengte
heupdiepte:
name: Heupdiepte

Laden…
Abbrechen
Speichern