#!/usr/bin/env python3 """ Pure Python DXF R12 Generator for SendCutSend Sheet Metal Panels. Generates compliant ASCII DXF files for laser cutting. """ import os import math class DXFWriter: def __init__(self, filename): self.filename = filename self.entities = [] def add_line(self, x1, y1, x2, y2, layer="0"): self.entities.append( f"0\nLINE\n8\n{layer}\n10\n{x1:.4f}\n20\n{y1:.4f}\n30\n0.0\n11\n{x2:.4f}\n21\n{y2:.4f}\n31\n0.0\n" ) def add_rect(self, x, y, width, height, layer="0"): self.add_line(x, y, x + width, y, layer) self.add_line(x + width, y, x + width, y + height, layer) self.add_line(x + width, y + height, x, y + height, layer) self.add_line(x, y + height, x, y, layer) def add_circle(self, cx, cy, radius, layer="0"): self.entities.append( f"0\nCIRCLE\n8\n{layer}\n10\n{cx:.4f}\n20\n{cy:.4f}\n30\n0.0\n40\n{radius:.4f}\n" ) def add_slot(self, cx, cy, width, height, layer="0"): # Oblong horizontal or vertical slot if width >= height: r = height / 2.0 x1 = cx - width / 2.0 + r x2 = cx + width / 2.0 - r self.add_line(x1, cy - r, x2, cy - r, layer) self.add_line(x1, cy + r, x2, cy + r, layer) # approximate semicircles with line segments steps = 16 for i in range(steps): a1 = math.pi/2 + i * math.pi / steps a2 = math.pi/2 + (i + 1) * math.pi / steps self.add_line(x1 + r * math.cos(a1), cy + r * math.sin(a1), x1 + r * math.cos(a2), cy + r * math.sin(a2), layer) b1 = -math.pi/2 + i * math.pi / steps b2 = -math.pi/2 + (i + 1) * math.pi / steps self.add_line(x2 + r * math.cos(b1), cy + r * math.sin(b1), x2 + r * math.cos(b2), cy + r * math.sin(b2), layer) else: r = width / 2.0 y1 = cy - height / 2.0 + r y2 = cy + height / 2.0 - r self.add_line(cx - r, y1, cx - r, y2, layer) self.add_line(cx + r, y1, cx + r, y2, layer) steps = 16 for i in range(steps): a1 = 0 + i * math.pi / steps a2 = 0 + (i + 1) * math.pi / steps self.add_line(cx + r * math.cos(a1), y2 + r * math.sin(a1), cx + r * math.cos(a2), y2 + r * math.sin(a2), layer) b1 = math.pi + i * math.pi / steps b2 = math.pi + (i + 1) * math.pi / steps self.add_line(cx + r * math.cos(b1), y1 + r * math.sin(b1), cx + r * math.cos(b2), y1 + r * math.sin(b2), layer) def write(self): content = [ "0\nSECTION\n2\nHEADER\n0\nENDSEC\n", "0\nSECTION\n2\nTABLES\n0\nENDSEC\n", "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n", "0\nSECTION\n2\nENTITIES\n", "".join(self.entities), "0\nENDSEC\n", "0\nEOF\n" ] with open(self.filename, "w", encoding="utf-8") as f: f.write("".join(content)) print(f"Generated: {self.filename}") def generate_all_dxf(): out_dir = os.path.join(os.path.dirname(__file__), "dxf") os.makedirs(out_dir, exist_ok=True) # 1. SMP-01 Top Cover (436 x 500) top = DXFWriter(os.path.join(out_dir, "SMP-01_Top_Cover_Plate.dxf")) top.add_rect(0, 0, 436.0, 500.0) # Left & Right edge holes (Ø4.5mm -> r=2.25) for y in [25.0, 150.0, 275.0, 400.0, 475.0]: top.add_circle(10.0, y, 2.25) top.add_circle(426.0, y, 2.25) # Front & Rear edge holes for x in [109.0, 218.0, 327.0]: top.add_circle(x, 10.0, 2.25) top.add_circle(x, 490.0, 2.25) top.write() # 2. SMP-02 Bottom Base Plate (436 x 500) bot = DXFWriter(os.path.join(out_dir, "SMP-02_Bottom_Base_Plate.dxf")) bot.add_rect(0, 0, 436.0, 500.0) # Perimeter for y in [25.0, 150.0, 275.0, 400.0, 475.0]: bot.add_circle(10.0, y, 2.25) bot.add_circle(426.0, y, 2.25) for x in [109.0, 218.0, 327.0]: bot.add_circle(x, 10.0, 2.25) bot.add_circle(x, 490.0, 2.25) # PCB Standoffs (Ø3.5mm -> r=1.75) for (px, py) in [(40.0, 230.0), (200.0, 230.0), (40.0, 330.0), (200.0, 330.0)]: bot.add_circle(px, py, 1.75) # Mezzanine anchors for (mx, my) in [(280.0, 230.0), (380.0, 230.0), (280.0, 430.0), (380.0, 430.0)]: bot.add_circle(mx, my, 1.75) bot.write() # 3. SMP-03 & 04 Side Panels (500 x 260) for side_name in ["SMP-03_Left_Side_Panel.dxf", "SMP-04_Right_Side_Panel.dxf"]: side = DXFWriter(os.path.join(out_dir, side_name)) side.add_rect(0, 0, 500.0, 260.0) for x in [25.0, 150.0, 275.0, 400.0, 475.0]: side.add_circle(x, 10.0, 2.25) side.add_circle(x, 250.0, 2.25) for y in [70.0, 130.0, 190.0]: side.add_circle(10.0, y, 2.25) side.add_circle(490.0, y, 2.25) side.write() # 4. SMP-05 Rear I/O Panel (396 x 220) rear = DXFWriter(os.path.join(out_dir, "SMP-05_Rear_IO_Exhaust_Plate.dxf")) rear.add_rect(0, 0, 396.0, 220.0) # Perimeter for x in [30.0, 130.0, 266.0, 366.0]: rear.add_circle(x, 10.0, 2.25) rear.add_circle(x, 210.0, 2.25) for y in [60.0, 110.0, 160.0]: rear.add_circle(10.0, y, 2.25) rear.add_circle(386.0, y, 2.25) # Exhaust Honeycomb Grid (Array of Ø5.0mm ventilation holes for laser efficiency) for hx in range(35, 220, 10): for hy in range(35, 190, 10): rear.add_circle(hx, hy, 3.5) # Dual PCIe Vertical Slot Cutouts (120mm x 21.6mm) rear.add_rect(250.0, 35.0, 22.0, 120.0) rear.add_rect(280.0, 35.0, 22.0, 120.0) # Host I/O Cutouts rear.add_rect(335.0, 40.0, 32.0, 14.0) # Dual RJ45 rear.add_rect(335.0, 65.0, 16.0, 15.0) # Dual USB 3.0 rear.add_circle(355.0, 110.0, 5.75) # DC Barrel Jack (Ø11.5mm) rear.add_rect(345.0, 135.0, 20.0, 10.5) # 8-pin Molex rear.write() # 5. SMP-06L & SMP-06R Rack Ears (40 x 260, 3mm) for ear_name in ["SMP-06L_Rack_Mount_Ear.dxf", "SMP-06R_Rack_Mount_Ear.dxf"]: ear = DXFWriter(os.path.join(out_dir, ear_name)) ear.add_rect(0, 0, 40.0, 260.0) # Extrusion mounting holes (Ø5.5mm -> r=2.75) for y in [20.0, 70.0, 130.0, 190.0, 240.0]: ear.add_circle(15.0, y, 2.75) # EIA-310-D Oblong Rack Slots (10.0mm x 6.8mm) rack_y = [ 12.7, 28.6, 44.5, # 1U 57.2, 73.0, 88.9, # 2U 101.6, 117.5, 133.4, # 3U 146.0, 161.9, 177.8, # 4U 190.5, 206.4, 222.2, # 5U 235.0, 250.8 # 6U ] for ry in rack_y: ear.add_slot(30.0, ry, 10.0, 6.8) ear.write() if __name__ == "__main__": generate_all_dxf()