| @@ -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") | |||||
| } | |||||