script improvements
This commit is contained in:
118
pcb_to_stl.py
118
pcb_to_stl.py
@@ -3,7 +3,7 @@ import sys
|
||||
import os
|
||||
|
||||
# --- Parse Arguments ---
|
||||
# structure: [..., 'input.svg', 'output.stl', 'thickness', 'width']
|
||||
# structure: [..., 'input.svg', 'output.stl', 'thickness', 'invert_flag', 'padding']
|
||||
try:
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
@@ -11,7 +11,11 @@ try:
|
||||
input_svg = argv[0]
|
||||
output_stl = argv[1]
|
||||
thickness_mm = float(argv[2])
|
||||
target_width_mm = float(argv[3])
|
||||
# Read the 4th argument as integer (1 or 0), convert to boolean
|
||||
invert_mode = bool(int(argv[3])) if len(argv) > 3 else False
|
||||
# Read Padding (Default 0.0)
|
||||
padding_mm = float(argv[4]) if len(argv) > 4 else 0.0
|
||||
|
||||
except (ValueError, IndexError) as e:
|
||||
print(f"Error parsing arguments: {e}")
|
||||
sys.exit(1)
|
||||
@@ -22,7 +26,6 @@ bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
scene = bpy.context.scene
|
||||
scene.unit_settings.system = 'METRIC'
|
||||
scene.unit_settings.length_unit = 'MILLIMETERS'
|
||||
# This scale allows us to work in mm directly (1.0 = 1mm)
|
||||
scene.unit_settings.scale_length = 0.001
|
||||
|
||||
# --- Step 2: Import SVG ---
|
||||
@@ -36,52 +39,91 @@ bpy.ops.import_curve.svg(filepath=input_svg)
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
|
||||
if bpy.context.selected_objects:
|
||||
# Set active object
|
||||
bpy.context.view_layer.objects.active = bpy.context.selected_objects[0]
|
||||
|
||||
# 1. Join all parts into one
|
||||
# 1. Join all parts
|
||||
bpy.ops.object.join()
|
||||
obj = bpy.context.active_object
|
||||
obj.name = "Pattern"
|
||||
|
||||
# 2. Convert to Mesh (Flat 2D)
|
||||
# 2. Convert to Mesh
|
||||
bpy.ops.object.convert(target='MESH')
|
||||
|
||||
# --- Step 3.5: RESIZE TO TARGET WIDTH (MM) ---
|
||||
if target_width_mm > 0:
|
||||
# Get current width (X dimension)
|
||||
current_width = obj.dimensions.x
|
||||
# 3. Center Geometry
|
||||
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
|
||||
obj.location = (0, 0, 0)
|
||||
|
||||
print(f"Imported Dimensions: {obj.dimensions.x:.2f}mm x {obj.dimensions.y:.2f}mm")
|
||||
|
||||
def add_solidify(target_obj, thick, offset=0):
|
||||
mod = target_obj.modifiers.new(name='Solidify', type='SOLIDIFY')
|
||||
mod.thickness = thick
|
||||
mod.use_even_offset = True
|
||||
mod.use_rim = True
|
||||
mod.offset = offset
|
||||
bpy.context.view_layer.objects.active = target_obj
|
||||
bpy.ops.object.modifier_apply(modifier=mod.name)
|
||||
|
||||
# --- Step 4: Logic Branch ---
|
||||
if invert_mode:
|
||||
print(f"--- INVERSION MODE (Negative) ---")
|
||||
if padding_mm > 0:
|
||||
print(f"Applying Padding: {padding_mm}mm (Total Extra Width: {padding_mm*2}mm)")
|
||||
|
||||
# 1. Cutter (thicker than base)
|
||||
cutter_thickness = thickness_mm * 3.0
|
||||
add_solidify(obj, cutter_thickness, offset=0)
|
||||
|
||||
if current_width > 0.000001:
|
||||
scale_factor = target_width_mm / current_width
|
||||
print(f"Resizing: {current_width:.2f}mm -> {target_width_mm:.2f}mm (Factor: {scale_factor:.4f})")
|
||||
|
||||
# Apply Scale to X and Y (Maintain Aspect Ratio)
|
||||
obj.scale[0] = scale_factor
|
||||
obj.scale[1] = scale_factor
|
||||
|
||||
# Freeze the scale transformation so dimensions are real before adding modifiers
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
else:
|
||||
print("Warning: Object has 0 width, skipping resize.")
|
||||
# 2. Base Plate
|
||||
bpy.ops.mesh.primitive_cube_add(location=(0,0,0))
|
||||
base = bpy.context.active_object
|
||||
base.name = "BasePlate"
|
||||
|
||||
# Calculate Dimensions with Padding
|
||||
# Padding is added to all sides, so width increases by padding*2
|
||||
final_x = obj.dimensions.x + (padding_mm * 2)
|
||||
final_y = obj.dimensions.y + (padding_mm * 2)
|
||||
|
||||
base.dimensions = (final_x, final_y, thickness_mm)
|
||||
|
||||
# Apply transforms so scale is 1,1,1
|
||||
bpy.ops.object.transform_apply(scale=True)
|
||||
base.location = (0,0,0)
|
||||
|
||||
# 3. Boolean Difference
|
||||
print("Applying Boolean Difference...")
|
||||
bool_mod = base.modifiers.new(name='Boolean', type='BOOLEAN')
|
||||
bool_mod.object = obj
|
||||
bool_mod.operation = 'DIFFERENCE'
|
||||
bool_mod.solver = 'EXACT'
|
||||
|
||||
try:
|
||||
bpy.context.view_layer.objects.active = base
|
||||
bpy.ops.object.modifier_apply(modifier=bool_mod.name)
|
||||
except Exception as e:
|
||||
print(f"Warning: Exact Boolean failed. {e}")
|
||||
bool_mod.solver = 'FAST'
|
||||
bpy.ops.object.modifier_apply(modifier=bool_mod.name)
|
||||
|
||||
# 3. Add Solidify Modifier (Replaces manual extrusion)
|
||||
print(f"Applying Solidify Modifier: Thickness {thickness_mm}mm")
|
||||
|
||||
# Add the modifier
|
||||
mod = obj.modifiers.new(name='PCBSolidify', type='SOLIDIFY')
|
||||
|
||||
# Set Modifier Properties
|
||||
mod.thickness = thickness_mm
|
||||
mod.use_even_offset = True # "Even Thickness: yes"
|
||||
mod.use_rim = True # "Fill Rim: ON"
|
||||
mod.offset = 0 # 0 centers the thickness, 1 extends vertically.
|
||||
# Use 0 or 1 depending on where you want the origin.
|
||||
|
||||
# Apply the modifier (Bake it into the mesh)
|
||||
bpy.ops.object.modifier_apply(modifier=mod.name)
|
||||
bpy.data.objects.remove(obj, do_unlink=True)
|
||||
obj = base
|
||||
|
||||
# --- Step 4: Export STL ---
|
||||
else:
|
||||
print(f"--- STANDARD MODE (Positive) ---")
|
||||
add_solidify(obj, thickness_mm, offset=0)
|
||||
|
||||
# --- Step 5: Final Placement (Z=0) ---
|
||||
bpy.context.view_layer.update()
|
||||
min_z = min([c[2] for c in obj.bound_box])
|
||||
obj.location.z -= min_z
|
||||
|
||||
# --- Step 6: Export ---
|
||||
print(f"Exporting to: {output_stl}")
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
|
||||
bpy.ops.wm.stl_export(filepath=output_stl, global_scale=1.0)
|
||||
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user