建立方向盘项目初始版本
This commit is contained in:
110
scripts/build_shop_sprues.py
Normal file
110
scripts/build_shop_sprues.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""Create two genuinely connected eight-washer sprues for shop review.
|
||||
|
||||
Run with the FreeCAD bundled Python. The original single-part files are read
|
||||
only; all lengths and placements below are in millimetres.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import shutil
|
||||
import zipfile
|
||||
|
||||
import FreeCAD as App
|
||||
import Part
|
||||
import Mesh
|
||||
import MeshPart
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
SOURCE = ROOT / 'models' / 'pcd50_open_pillar_v1'
|
||||
OUT = ROOT / 'models' / 'print_shop_joined_eight'
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
V = App.Vector
|
||||
|
||||
# STEP washer geometry is centered on (10,10) in these exported files. These
|
||||
# coordinates are explicit because imported STEP and STL placements differ.
|
||||
def centered_shape(name):
|
||||
shape = Part.read(str(SOURCE / name))
|
||||
if shape.isNull() or not shape.isValid() or len(shape.Solids) != 1:
|
||||
raise RuntimeError('Invalid source shape ' + name)
|
||||
shape.translate(V(-10,-10,0))
|
||||
return shape
|
||||
|
||||
def make_sprue(step_name, doc_name, stem, expected_t):
|
||||
source = centered_shape(step_name)
|
||||
if abs(source.BoundBox.ZLength-expected_t) > 1e-5:
|
||||
raise RuntimeError('Unexpected washer thickness')
|
||||
pads=[]
|
||||
# Left column is rotated so both inward edges are solid. Two columns and
|
||||
# four rows give eight washers with seven straight connecting bars.
|
||||
for col in (0, 1):
|
||||
for row in range(4):
|
||||
pad=source.copy()
|
||||
if col == 0:
|
||||
pad.rotate(V(),V(0,0,1),180)
|
||||
pad.translate(V(24*col,16*row,0))
|
||||
pads.append(pad)
|
||||
# Each bar is constant 1.8 x 1.8 mm. Ends overlap the outer edge by
|
||||
# 0.8 mm on the centerline. The root is sacrificial and needs careful
|
||||
# trimming and smoothing after printing.
|
||||
rods=[]
|
||||
for x in (0,24):
|
||||
for row in range(3):
|
||||
rods.append(Part.makeBox(1.8,5.6,1.8,V(x-.9,16*row+5.2,0)))
|
||||
rods.append(Part.makeBox(13.6,1.8,1.8,V(5.2,-.9,0)))
|
||||
# Quantify the overlap of each end with its two washers. The rod body is
|
||||
# never necked down, but its contact height is limited by the washer.
|
||||
overlaps=[]
|
||||
for col in (0,1):
|
||||
for row in range(3):
|
||||
rod=rods[col*3+row]
|
||||
overlaps.append([rod.common(pads[col*4+row]).Volume,
|
||||
rod.common(pads[col*4+row+1]).Volume])
|
||||
overlaps.append([rods[6].common(pads[0]).Volume,
|
||||
rods[6].common(pads[4]).Volume])
|
||||
if any(min(pair) < 0.5 for pair in overlaps):
|
||||
raise RuntimeError('Insufficient rod/pad intersection')
|
||||
combined=pads[0]
|
||||
# All parts touch through each rod, but may not be sequentially connected.
|
||||
# MultiFuse avoids relying on order of the graph during the operation.
|
||||
combined = pads[0].multiFuse(pads[1:]+rods).removeSplitter()
|
||||
if combined.isNull() or not combined.isValid() or len(combined.Solids)!=1:
|
||||
raise RuntimeError('Sprue is not one valid connected solid: '+stem)
|
||||
# Confirm every washer's complete original geometry is retained.
|
||||
for pad in pads:
|
||||
if pad.cut(combined).Volume > 1e-5:
|
||||
raise RuntimeError('Source washer material was removed')
|
||||
bounds=combined.BoundBox
|
||||
doc=App.newDocument(doc_name)
|
||||
o=doc.addObject('Part::Feature','ConnectedSprue');o.Label=stem;o.Shape=combined
|
||||
doc.recompute()
|
||||
doc.saveAs(str(OUT/(stem+'.FCStd')))
|
||||
Part.export([o],str(OUT/(stem+'.step')))
|
||||
mesh=MeshPart.meshFromShape(Shape=combined,LinearDeflection=0.03,
|
||||
AngularDeflection=0.2,Relative=False)
|
||||
mesh.write(str(OUT/(stem+'.stl')))
|
||||
App.closeDocument(doc.Name)
|
||||
check=Mesh.Mesh(str(OUT/(stem+'.stl')))
|
||||
components=check.getSeparateComponents()
|
||||
if len(components)!=1 or not components[0].isSolid():
|
||||
raise RuntimeError('STL is not one closed connected mesh')
|
||||
return {'file':stem+'.stl','washer_quantity':8,'nominal_washer_thickness_mm':expected_t,
|
||||
'rod_count':len(rods),'rod_width_mm':1.8,'rod_height_mm':1.8,
|
||||
'rod_centerline_radial_overlap_mm':0.8,
|
||||
'rod_end_intersection_volumes_mm3': [[round(v,5) for v in pair] for pair in overlaps],
|
||||
'closed_components':len(components),'closed':True,
|
||||
'bounds_mm':[bounds.XMin,bounds.XMax,bounds.YMin,bounds.YMax,bounds.ZMin,bounds.ZMax],
|
||||
'nominal_washer_volume_mm3':round(source.Volume,5)}
|
||||
|
||||
result=[]
|
||||
result.append(make_sprue('Shim05.step','WasherSprue05','垫片0.5mm_8个拼件',.5))
|
||||
result.append(make_sprue('Shim10.step','WasherSprue10','垫片1.0mm_8个拼件',1.0))
|
||||
source=SOURCE/'EncoderBracket.stl'; target=OUT/'编码器支架_单件.stl'
|
||||
shutil.copyfile(source,target)
|
||||
digest=hashlib.sha256(source.read_bytes()).hexdigest()
|
||||
if hashlib.sha256(target.read_bytes()).hexdigest()!=digest: raise RuntimeError('Bracket copy mismatch')
|
||||
m=Mesh.Mesh(str(target))
|
||||
if len(m.getSeparateComponents())!=1 or not m.isSolid(): raise RuntimeError('Invalid bracket mesh')
|
||||
result.insert(0,{'file':target.name,'quantity':1,'closed_components':1,'sha256':digest})
|
||||
(OUT/'几何检查.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print(json.dumps(result,ensure_ascii=False,indent=2))
|
||||
Reference in New Issue
Block a user