package main import ( "fmt" "math" "github.com/fogleman/gg" ) type fcoords struct { X float64 Y float64 } func main() { a4_dimensions := fcoords{210, 297} //mm scale := 10. // px per mm card_dimensions := fcoords{85.60, 53.98} //mm stitching_width := 5. //mm hole_spacing := 4. //mm hole_radius := 1. //mm initial_hole_spacing_from_center := 2. //mm hole_spacing_from_top := 3. //mm leather_thickness := 2. //mm optimal_angle := 90. //deg rounding_radius := 4. //mm layer_offset := 12. //mm alternate_layer_offset := true page_margins := 10. //mm layer_count := 5 hole_counts := []int{44, 32} font_path := "assets/iosevka-heavy.ttf" font_size := 8. ruler_count := []int{50, 4} ruler_square_size := 2. //mm // Code anchor := fcoords{page_margins, page_margins} // mm for layer := 0; layer < layer_count; layer++ { dc := gg.NewContext(int(math.Round(a4_dimensions.X*scale)), int(math.Round(a4_dimensions.Y*scale))) dc.SetRGB(1, 1, 1) dc.Clear() dc.SetRGB(0, 0, 0) dc.LoadFontFace(font_path, font_size*scale) stencil_dimensions := fcoords{card_dimensions.X*2 + stitching_width*2 + float64(layer)*math.Pi*leather_thickness*optimal_angle/360*2, card_dimensions.Y + layer_offset + stitching_width} //mm dc.DrawRoundedRectangle(anchor.X*scale, anchor.Y*scale, stencil_dimensions.X*scale, stencil_dimensions.Y*scale, rounding_radius*scale) dc.Stroke() hole_spacing_from_center := initial_hole_spacing_from_center + math.Pi*leather_thickness*float64(layer_count-layer-1)*optimal_angle/360 for i := 0; i < hole_counts[0]; i++ { 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) dc.Stroke() } for i := 0; i < hole_counts[1]; i++ { if i%2 == 0 { dc.DrawCircle((anchor.X+stitching_width/2)*scale, (anchor.Y+hole_spacing_from_top+float64(i/2)*hole_spacing)*scale, hole_radius*scale) } else { 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) } dc.Stroke() } for i := 0; i < hole_counts[1]; i++ { 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) dc.Stroke() } 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) dc.Stroke() dc.DrawLine((anchor.X+stitching_width)*scale, (anchor.Y)*scale, (anchor.X+stitching_width)*scale, (anchor.Y+stencil_dimensions.Y)*scale) dc.Stroke() 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) dc.Stroke() if layer != 0 { 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) dc.Stroke() 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) dc.Stroke() 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) dc.Stroke() 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) dc.Stroke() } if layer > 1 { effective_layer_index := layer - 1 ay := anchor.Y + layer_offset*float64(effective_layer_index) by := anchor.Y + layer_offset*float64(effective_layer_index+1) if alternate_layer_offset { if layer%2 == 1 { cy := by by = ay ay = cy } } 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) dc.Stroke() dc.DrawLine((anchor.X+stencil_dimensions.X/2+hole_spacing_from_center+stitching_width/2)*scale, ay*scale, (anchor.X+stencil_dimensions.X-stitching_width)*scale, by*scale) dc.Stroke() dc.DrawLine(anchor.X*scale,ay,(anchor.X+stitching_width)*scale,ay*scale) dc.Stroke() dc.DrawLine((anchor.X+stencil_dimensions.X/2)*scale,ay,(anchor.X+stencil_dimensions.X+hole_spacing_from_center+stitching_width)*scale,ay*scale) dc.Stroke() } dc.DrawStringAnchored(fmt.Sprintf("Layer %d", layer), (anchor.X)*scale, (anchor.Y+stencil_dimensions.Y+page_margins)*scale, 0, 0) for i := 0; i < ruler_count[0]; i++ { for j := 0; j < ruler_count[1]; j++ { 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) dc.Stroke() } } dc.SavePNG(fmt.Sprintf("out-%04d.png", layer)) } }