Files
wheel/scripts/build_kfl001_plate_v1.py

89 lines
6.1 KiB
Python

"""Editable KFL001 bearing mounting plate for prototype.
Run with rebuild_kfl001_plate_v1.FCMacro in a hidden FreeCAD instance.
Both assembly plates have the same geometry; flip the second when fitting.
The centre cut provides 0.5 mm radial clearance for a 38 mm boss.
"""
from pathlib import Path
import os, time, traceback, json
import FreeCAD as A, FreeCADGui as G, Part, Import
R=Path(r'C:\projects\wheel')
O=R/'models'/'kfl001_plate_v1'
O.mkdir(parents=True, exist_ok=True)
try:
d=A.newDocument('KFL001_Plate_V1')
d.Label='KFL001 8mm bearing mounting plate V1'
s=d.addObject('Spreadsheet::Sheet','Parameters')
s.Label='Parameters - dimensions in mm'
rows=[('PlateWidth','76 mm','Overall width'),('PlateHeight','80 mm','Overall height'),
('Thickness','8 mm','Plate thickness'),('AxisFromBottom','51 mm','Axle height above lower edge'),
('CenterClearance','39 mm','39 mm clearance for user confirmed 38 mm boss'),
('BearingPitch','48 mm','Bolt centre spacing for KFL001'),
('BearingHole','7 mm','Bearing mount clearance diameter'),
('SlotPitch','47 mm','Centre spacing of lower mounting slots'),
('SlotBottom','9 mm','Mounting slot centre above plate lower edge'),
('SlotLength','9.5 mm','Horizontal slot overall length'),
('SlotWidth','5.5 mm','Horizontal slot width')]
for row,(name,val,note) in enumerate(rows,2):
s.set('A%d'%row,name);s.set('B%d'%row,val);s.set('C%d'%row,note);s.setAlias('B%d'%row,name)
def expr(o,p,v):o.setExpression(p,v)
plate=d.addObject('Part::Box','PlateBlank');plate.Label='76 x 80 x 8 plate blank'
expr(plate,'Length','Parameters.PlateWidth');expr(plate,'Width','Parameters.PlateHeight');expr(plate,'Height','Parameters.Thickness')
expr(plate,'Placement.Base.x','-Parameters.PlateWidth / 2')
def cyl(name,x,y,r):
o=d.addObject('Part::Cylinder',name);expr(o,'Radius',r);expr(o,'Height','Parameters.Thickness + 0.2 mm');expr(o,'Placement.Base.x',x);expr(o,'Placement.Base.y',y);o.Placement.Base.z=-.1;return o
tools=[]
tools.append(cyl('CentreHole','0 mm','Parameters.AxisFromBottom','Parameters.CenterClearance / 2'))
for side in (-1,1):
sign='-' if side<0 else ''
tools.append(cyl('BearingHole_'+('L' if side<0 else 'R'),sign+'Parameters.BearingPitch / 2','Parameters.AxisFromBottom','Parameters.BearingHole / 2'))
cx=sign+'Parameters.SlotPitch / 2';tag='L' if side<0 else 'R'
tools.append(cyl('Slot'+tag+'End1','(%s) - (Parameters.SlotLength - Parameters.SlotWidth) / 2'%cx,'Parameters.SlotBottom','Parameters.SlotWidth / 2'))
tools.append(cyl('Slot'+tag+'End2','(%s) + (Parameters.SlotLength - Parameters.SlotWidth) / 2'%cx,'Parameters.SlotBottom','Parameters.SlotWidth / 2'))
b=d.addObject('Part::Box','Slot'+tag+'Middle')
expr(b,'Length','Parameters.SlotLength - Parameters.SlotWidth');expr(b,'Width','Parameters.SlotWidth');expr(b,'Height','Parameters.Thickness + 0.2 mm')
expr(b,'Placement.Base.x','(%s) - (Parameters.SlotLength - Parameters.SlotWidth) / 2'%cx)
expr(b,'Placement.Base.y','Parameters.SlotBottom - Parameters.SlotWidth / 2');b.Placement.Base.z=-.1
tools.append(b)
d.recompute()
cutters=d.addObject('Part::MultiFuse','Cutters');cutters.Shapes=tools;d.recompute()
final=d.addObject('Part::Cut','Plate_V1');final.Label='KFL001 bearing mounting plate V1';final.Base=plate;final.Tool=cutters
d.recompute()
assert not final.Shape.isNull() and final.Shape.isValid() and len(final.Shape.Solids)==1
for o in d.Objects:
if o.ViewObject:o.ViewObject.Visibility=False
final.ViewObject.Visibility=True;final.ViewObject.ShapeColor=(.71,.74,.77);final.ViewObject.LineColor=(.12,.12,.12)
G.activateWorkbench('PartWorkbench');view=G.activeDocument().activeView();view.viewAxonometric();G.updateGui();view.fitAll();time.sleep(.3);G.updateGui();view.saveImage(str(O/'plate_isometric.png'),1400,1100,'White')
view.viewTop();G.updateGui();view.fitAll();time.sleep(.3);G.updateGui();view.saveImage(str(O/'plate_top.png'),1400,1100,'White')
d.saveAs(str(O/'KFL001_plate_V1.FCStd'));Import.export([final],str(O/'KFL001_plate_V1.step'))
baseline=final.Shape.Volume
s.set('B6','38 mm');d.recompute()
assert final.Shape.isValid() and abs(final.Shape.Volume-baseline)>0.1
changed=final.Shape.Volume
s.set('B6','39 mm');d.recompute()
assert final.Shape.isValid() and abs(final.Shape.Volume-baseline)<1e-5
A.closeDocument(d.Name)
dd=A.openDocument(str(O/'KFL001_plate_V1.FCStd'));dd.recompute();ff=dd.getObject('Plate_V1')
assert ff.Shape.isValid() and len(ff.Shape.Solids)==1 and abs(ff.Shape.Volume-baseline)<1e-5
imported=Part.read(str(O/'KFL001_plate_V1.step'))
assert imported.isValid() and len(imported.Solids)==1
# At the middle Z plane compare the STEP vs native shape by mutual differences.
stepdiff1=ff.Shape.cut(imported).Volume;stepdiff2=imported.cut(ff.Shape).Volume
assert stepdiff1<1e-4 and stepdiff2<1e-4
angles=[(3.5,24,51),(3.5,-24,51),(19.5,0,51)]
cylinders=[]
for f in imported.Faces:
surf=f.Surface
if hasattr(surf,'Radius') and hasattr(surf,'Center'):
cylinders.append({'radius':round(float(surf.Radius),5),'x':round(float(surf.Center.x),5),'y':round(float(surf.Center.y),5),'z_min':round(f.BoundBox.ZMin,5),'z_max':round(f.BoundBox.ZMax,5)})
for r,x,y in angles:
assert any(abs(c['radius']-r)<1e-4 and abs(c['x']-x)<1e-4 and abs(c['y']-y)<1e-4 and c['z_min']<=.001 and c['z_max']>=7.999 for c in cylinders), (r,x,y,cylinders)
info={'fcstd_valid':True,'step_valid':True,'solid_count':len(imported.Solids),'volume':baseline,'diameter_parameter_test_38':changed,'diameter_test_restored':True,'step_old_minus_new':stepdiff1,'step_new_minus_old':stepdiff2,'dimensions_mm':{'width':76,'height':80,'thickness':8,'axis_from_bottom':51,'center_diameter':39,'bearing_pitch':48,'bearing_hole':7,'slot_pitch':47,'slot_bottom':9,'slot_length':9.5,'slot_width':5.5},'cylinder_faces':cylinders,'nominal_m5_slot_range_mm':4.5,'quantity_identical_plates':2,'boss_diameter_user_confirmed_mm':38}
(O/'validation.json').write_text(json.dumps(info,ensure_ascii=False,indent=2),encoding='utf-8')
A.closeDocument(dd.Name)
(O/'DONE.txt').write_text('ok',encoding='utf-8')
os._exit(0)
except Exception:
(O/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf-8');os._exit(1)