89 lines
6.2 KiB
Python
89 lines
6.2 KiB
Python
"""Editable KFL001 bearing mounting plate -- provisional concept only.
|
|
|
|
Run with rebuild_kfl001_plate_draft.FCMacro in a hidden FreeCAD instance.
|
|
Both assembly plates have the same geometry; flip the second when fitting.
|
|
The centre cut is an unverified placeholder and MUST NOT be used for CNC.
|
|
"""
|
|
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_draft'
|
|
O.mkdir(parents=True, exist_ok=True)
|
|
try:
|
|
d=A.newDocument('KFL001_Plate_Draft')
|
|
d.Label='KFL001 8mm plate DRAFT - centre cut unverified'
|
|
s=d.addObject('Spreadsheet::Sheet','Parameters')
|
|
s.Label='Parameters - centre diameter UNVERIFIED'
|
|
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','TEMPORARY ONLY - verify real KFL back geometry'),
|
|
('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('TEMP_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_DRAFT');final.Label='DRAFT bearing plate - centre 39mm NOT verified';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_DRAFT.FCStd'));Import.export([final],str(O/'KFL001_plate_DRAFT.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_DRAFT.FCStd'));dd.recompute();ff=dd.getObject('Plate_DRAFT')
|
|
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_DRAFT.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_provisional':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,'center_UNVERIFIED':True}
|
|
(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)
|