| @@ -0,0 +1,33 @@ | |||
| package main | |||
| import ( | |||
| svg "github.com/ajstarks/svgo/float" | |||
| "naaipatroon/pkg/patroon" | |||
| "os" | |||
| ) | |||
| func main() { | |||
| broek := patroon.Basispatroonbroek{ | |||
| Heupwijdte: 103, | |||
| Taillewijdte: 85, | |||
| Zithoogte: 31, | |||
| Tussenbeenlengte: 83, | |||
| Pijpbreedte: 25, | |||
| Taillebandbreedte: 4, | |||
| Eenheid: patroon.CentiMeter, | |||
| Eigenaar: "Wouter Horlings", | |||
| } | |||
| points := broek.GeneratePoints() | |||
| f, err := os.OpenFile("broek.svg", os.O_RDWR|os.O_CREATE, 0o755) | |||
| if err != nil { | |||
| panic(err) | |||
| } | |||
| _, max := points.Normalize().Box() | |||
| canvas := svg.New(f) | |||
| canvas.Start(max.X, max.Y) | |||
| broek.Voorbeen(canvas) | |||
| canvas.End() | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| module naaipatroon | |||
| go 1.17 | |||
| require github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b | |||
| @@ -0,0 +1,29 @@ | |||
| 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= | |||
| 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= | |||
| 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/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/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/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/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/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/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= | |||
| @@ -0,0 +1,60 @@ | |||
| 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[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]) | |||
| } | |||
| @@ -0,0 +1,134 @@ | |||
| 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,0 +1,73 @@ | |||
| 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,0 +1,13 @@ | |||
| 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") | |||
| } | |||