1package main
  2
  3import (
  4	"fmt"
  5	"math"
  6
  7	"github.com/fogleman/gg"
  8)
  9
 10type fcoords struct {
 11	X float64
 12	Y float64
 13}
 14
 15func main() {
 16	a4_dimensions := fcoords{210, 297}       //mm
 17	scale := 30.                             // px per mm
 18	card_dimensions := fcoords{85.60, 53.98} //mm
 19	stroke_width := 0.25                     //mm
 20	stitching_width := 5.                    //mm
 21	hole_spacing := 4.                       //mm
 22	hole_radius := 1.                        //mm
 23	initial_hole_spacing_from_center := 4.1  //mm
 24	hole_spacing_from_top := 3.              //mm
 25	leather_thickness := 2.                  //mm
 26	optimal_angle := 90.                     //deg
 27	rounding_radius := 4.                    //mm
 28
 29	layer_offset := 12. //mm
 30	alternate_layer_offset := true
 31
 32	page_margins := 10. //mm
 33
 34	layer_count := 5
 35
 36	hole_counts := []int{44, 32}
 37
 38	font_path := "assets/iosevka-heavy.ttf"
 39	font_size := 8.
 40
 41	ruler_count := []int{50, 4}
 42	ruler_square_size := 2. //mm
 43
 44	// Code
 45	anchor := fcoords{page_margins, page_margins} // mm
 46
 47	for layer := 0; layer < layer_count; layer++ {
 48		dc := gg.NewContext(int(math.Round(a4_dimensions.X*scale)), int(math.Round(a4_dimensions.Y*scale)))
 49		dc.SetRGB(1, 1, 1)
 50		dc.Clear()
 51		dc.SetRGB(0, 0, 0)
 52		dc.LoadFontFace(font_path, font_size*scale)
 53		dc.SetLineWidth(stroke_width * scale)
 54
 55		stencil_dimensions := fcoords{card_dimensions.X*2 + stitching_width*2 + float64(layer_count-layer-1)*math.Pi*leather_thickness*optimal_angle/360*2, card_dimensions.Y + layer_offset + stitching_width} //mm
 56
 57		dc.DrawRoundedRectangle(anchor.X*scale, anchor.Y*scale, stencil_dimensions.X*scale, stencil_dimensions.Y*scale, rounding_radius*scale)
 58		dc.Stroke()
 59		hole_spacing_from_center := initial_hole_spacing_from_center + math.Pi*leather_thickness*float64(layer_count-layer-1)*optimal_angle/360
 60		for i := 0; i < hole_counts[0]; i++ {
 61			dc.DrawCircle((anchor.X+stencil_dimensions.X/2+(hole_spacing_from_center+float64(i/2)*hole_spacing)*float64(i%2*2-1))*scale, (anchor.Y+stencil_dimensions.Y-stitching_width/2)*scale, hole_radius*scale)
 62			dc.Stroke()
 63		}
 64
 65		for i := 0; i < hole_counts[1]; i++ {
 66			if i%2 == 0 {
 67				dc.DrawCircle((anchor.X+stitching_width/2)*scale, (anchor.Y+hole_spacing_from_top+float64(i/2)*hole_spacing)*scale, hole_radius*scale)
 68			} else {
 69				dc.DrawCircle((anchor.X+stencil_dimensions.X-stitching_width/2)*scale, (anchor.Y+hole_spacing_from_top+float64(i/2)*hole_spacing)*scale, hole_radius*scale)
 70			}
 71			dc.Stroke()
 72		}
 73
 74		if layer != 0 {
 75			for i := 0; i < hole_counts[1]; i++ {
 76				dc.DrawCircle((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center*float64(i%2*2-1))*scale, (anchor.Y+hole_spacing_from_top+float64(i/2)*hole_spacing)*scale, hole_radius*scale)
 77				dc.Stroke()
 78			}
 79		}
 80
 81		dc.DrawLine((anchor.X)*scale, (anchor.Y+stencil_dimensions.Y-stitching_width)*scale, (anchor.X+stencil_dimensions.X)*scale, (anchor.Y+stencil_dimensions.Y-stitching_width)*scale)
 82		dc.Stroke()
 83		dc.DrawLine((anchor.X+stitching_width)*scale, (anchor.Y)*scale, (anchor.X+stitching_width)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
 84		dc.Stroke()
 85		dc.DrawLine((anchor.X+stencil_dimensions.X-stitching_width)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X-stitching_width)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
 86		dc.Stroke()
 87
 88		if layer != 0 {
 89			dc.DrawLine((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center-stitching_width/2)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X/2+hole_spacing_from_center-stitching_width/2)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
 90			dc.Stroke()
 91			dc.DrawLine((anchor.X+stencil_dimensions.X/2-hole_spacing_from_center+stitching_width/2)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X/2-hole_spacing_from_center+stitching_width/2)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
 92			dc.Stroke()
 93
 94			dc.DrawLine((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
 95			dc.Stroke()
 96			dc.DrawLine((anchor.X+stencil_dimensions.X/2-hole_spacing_from_center-stitching_width/2)*scale, (anchor.Y)*scale, (anchor.X+stencil_dimensions.X/2-hole_spacing_from_center-stitching_width/2)*scale, (anchor.Y+stencil_dimensions.Y)*scale)
 97			dc.Stroke()
 98		}
 99
100		if layer > 1 {
101			effective_layer_index := layer - 1
102			ay := anchor.Y + layer_offset*float64(effective_layer_index)
103			by := anchor.Y + layer_offset*float64(effective_layer_index+1)
104			if alternate_layer_offset {
105				if layer%2 == 1 {
106					cy := by
107					by = ay
108					ay = cy
109				}
110			}
111			dc.DrawLine((anchor.X+stitching_width)*scale, ay*scale, (anchor.X+stencil_dimensions.X/2-hole_spacing_from_center-stitching_width/2)*scale, by*scale)
112			dc.Stroke()
113			dc.DrawLine((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, by*scale, (anchor.X+stencil_dimensions.X-stitching_width)*scale, ay*scale)
114			dc.Stroke()
115			dc.DrawLine(anchor.X*scale, ay*scale, (anchor.X+stitching_width)*scale, ay*scale)
116			dc.Stroke()
117			dc.DrawLine((anchor.X+stencil_dimensions.X/2-hole_spacing_from_center-stitching_width/2)*scale, by*scale, (anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, by*scale)
118			dc.Stroke()
119			dc.DrawLine((anchor.X+stencil_dimensions.X-stitching_width)*scale, ay*scale, (anchor.X+stencil_dimensions.X)*scale, ay*scale)
120			dc.Stroke()
121			dc.DrawArc((anchor.X+rounding_radius)*scale, (ay+rounding_radius)*scale, rounding_radius*scale, 3*math.Pi/2, math.Pi)
122			dc.Stroke()
123			dc.DrawArc((anchor.X+stencil_dimensions.X-rounding_radius)*scale, (ay+rounding_radius)*scale, rounding_radius*scale, -math.Pi/2, 0)
124			dc.Stroke()
125		}
126
127		dc.DrawStringAnchored(fmt.Sprintf("Layer %d", layer), (anchor.X)*scale, (anchor.Y+stencil_dimensions.Y+page_margins)*scale, 0, 0)
128		for i := 0; i < ruler_count[0]; i++ {
129			for j := 0; j < ruler_count[1]; j++ {
130				dc.DrawRectangle((anchor.X+float64(i)*ruler_square_size)*scale, (anchor.Y+stencil_dimensions.Y+page_margins*2+font_size+float64(j)*ruler_square_size)*scale, ruler_square_size*scale, ruler_square_size*scale)
131				dc.Stroke()
132			}
133		}
134		dc.SavePNG(fmt.Sprintf("out-%04d.png", layer))
135	}
136}