Files
wheel/scripts/build_switch2020_raise10.FCMacro

88 lines
6.0 KiB
Plaintext

from pathlib import Path
import os,time,traceback
import FreeCAD as A, FreeCADGui as G, Part, Import
from pivy import coin
V=A.Vector
R=Path(r'C:\projects\wheel'); O=R/'models'/'switch_2020_raise10_concept';O.mkdir(parents=True,exist_ok=True)
def box(x,y,z,dx,dy,dz): return Part.makeBox(dx,dy,dz,V(x,y,z))
def cy(r,h,x,y,z,d=V(0,0,1)): return Part.makeCylinder(r,h,V(x,y,z),d)
def sy(x,lo,hi,z,r,h): return box(x-r,lo,z,2*r,hi-lo,h).fuse(cy(r,h,x,lo,z)).fuse(cy(r,h,x,hi,z))
def sz(x,y,lo,hi,r,h): return box(x-r,y,lo,2*r,h,hi-lo).fuse(cy(r,h,x,y,lo,V(0,1,0))).fuse(cy(r,h,x,y,hi,V(0,1,0)))
def add(d,n,s,c,label):
o=d.addObject('PartDesign::Feature',n);o.Shape=s.removeSplitter();o.Label=label;o.ViewObject.ShapeColor=c;o.ViewObject.LineColor=(.15,.15,.15);return o
ok=False
try:
d=A.openDocument(str(R/'models'/'kfl_3030_motor_corner_concept'/'Assembly.FCStd'))
d.Label='Switch crossbeam 2020 - common shaft raised by 10 mm'
# Every visible component moves together except the fixed 3030 frame, its joints and the long bearing-plate brackets.
fixed_prefix=('NewRail','Crossbeam','PurchasedAngle','BaseJoint','FootHead')
moved=[]
for o in d.Objects:
if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility and not o.Name.startswith(fixed_prefix):
p=o.Placement;p.Base.y+=10.0;o.Placement=p;moved.append(o.Name)
d.recompute()
(O/'moved_names.txt').write_text('\n'.join(moved)+'\n',encoding='utf8')
sw=d.getObject('SwitchEnvelope');sw.ViewObject.Transparency=78
beam=add(d,'SwitchCross2020',box(-80,-55,-142,160,20,20),(.32,.36,.39),'Added 2020 switch crossbeam; solid envelope')
new=[beam];ang=[];con=[];ears=[];heads=[]
for i,(x,y,tag) in enumerate(((-42.,-25.,'A'),(42.,-12.,'B'))):
ax=x+(-.75 if x<0 else .75) # angle offset; M4 ear bolt clearance in 6.5mm slot
# 50 mm upright attaches switch; 31 mm foot sits on the top of 2020.
upright=box(ax-9.5,-32,-145,19,47,3).cut(sy(ax,-25.75,.75,-145.02,3.25,3.04))
foot=box(ax-9.5,-35,-142,19,3,31).cut(sz(ax,-35.02,-131.25,-121.75,3.25,3.04))
a=add(d,'SwitchAngle'+tag,upright.fuse(foot),(.98,.52,.05),'Long 50x31 angle '+tag+'; top mounted');ang.append(a);new.append(a)
# Separate assumed schematic bolt head and ear on the switch side of the angle.
ear=cy(7,3,x,y,-148).cut(cy(2.2,3.04,x,y,-148.02))
e=add(d,'AssumedEar'+tag,ear,(.21,.67,.24),'ASSUMED ear '+tag+'; illustrative, no actual shell measured');ears.append(e)
h=cy(3.5,2.2,x,y,-150.2)
hd=add(d,'SwitchHead'+tag,h,(.12,.12,.12),'ASSUMED head '+tag+' Ø7');heads.append(hd);new.append(hd)
# Fastener head at the side groove of the 2020 (no shank entering solid extrusion).
railhead=add(d,'SwitchRailHead'+tag,cy(4.25,3,ax,-32,-132,V(0,1,0)),(.12,.12,.12),'Top groove head '+tag+'; bolt and groove not modeled');new.append(railhead)
for i,x in enumerate((-50.6200577,50.6200577)):
# 2020 to 3030 separate L angles on the opposite, plate-facing side.
upright=box(x-9,-55,-122,18,20,3).cut(cy(2.75,3.04,x,-45,-122.02))
foot=box(x-9,-55,-119,18,3,20).cut(cy(2.75,3.04,x,-55.02,-109,V(0,1,0)))
q=add(d,'SwitchCrossJoint%d'%i,upright.fuse(foot),(.91,.23,.18),'2020 to 3030 corner '+str(i+1));con.append(q);new.append(q)
# Shim bridges the old 2020 end and raised motor corner foot, one 5.5mm fastening hole.
shim=box(x-10,-55,0,20,10,40).cut(cy(2.75,10.04,x,-55.02,30,V(0,1,0)))
q=add(d,'MotorShim%d'%i,shim,(.45,.58,.75),'10mm shim supports motor 2020 and connector foot '+str(i+1));new.append(q)
d.recompute()
contextual=[o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o not in new and o not in ears and o.ViewObject.Visibility]
lines=[];bad=[]
for i,a in enumerate(new):
for b in new[i+1:]+contextual:
try:
if not a.Shape.BoundBox.intersect(b.Shape.BoundBox):continue
v=a.Shape.common(b.Shape).Volume
if v>0.001:bad.append((a.Name,b.Name,v))
lines.append('%s / %s : %.6f mm3'%(a.Name,b.Name,v))
except Exception as ex:lines.append('ERROR %s/%s %s'%(a.Name,b.Name,ex))
(O/'interference_report.txt').write_text('\n'.join(lines)+'\n',encoding='utf8')
(O/'positive_intersections.txt').write_text('\n'.join('%s / %s : %.6f mm3'%v for v in bad) or 'none',encoding='utf8')
d.saveAs(str(O/'Assembly.FCStd'));Import.export([o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility],str(O/'Assembly.step'))
view=G.activeDocument().activeView();G.activateWorkbench('PartWorkbench')
def snap(name,mode):
if mode=='front':view.viewBottom()
elif mode=='side':view.viewFront()
else:view.viewAxonometric();G.updateGui();view.getCameraNode().orientation.setValue(coin.SbRotation(.880476,.115917,-.279848,-.364705))
G.updateGui();time.sleep(.3);view.fitAll();G.updateGui();time.sleep(.4);view.saveImage(str(O/name),1800,1250,'White')
snap('assembly.png','axon')
# Show surrounding structure and schematic switch, focus the full assembly so the beam is visible.
snap('end.png','front');snap('side.png','side')
vis={o.Name:o.ViewObject.Visibility for o in d.Objects if hasattr(o,'Shape')}
show=set([o.Name for o in new+ears]+['NewRail0','NewRail1','Crossbeam0','Crossbeam1','CompactPlate0','CompactPlate1','KFLHousing0','KFLHousing1','CamShaft','SwitchEnvelope'])
for o in d.Objects:
if hasattr(o,'Shape'):o.ViewObject.Visibility=o.Name in show
snap('focus.png','axon')
simple=set(['SwitchCross2020','SwitchAngleA','SwitchAngleB','SwitchHeadA','SwitchHeadB','SwitchRailHeadA','SwitchRailHeadB','SwitchCrossJoint0','SwitchCrossJoint1','SwitchEnvelope','AssumedEarA','AssumedEarB'])
for o in d.Objects:
if hasattr(o,'Shape'):o.ViewObject.Visibility=o.Name in simple
view.viewTop();G.updateGui();time.sleep(.3);view.fitAll();G.updateGui();time.sleep(.3);view.saveImage(str(O/'switch_front.png'),1800,1250,'White')
snap('switch_detail.png','axon')
for o in d.Objects:
if hasattr(o,'Shape'):o.ViewObject.Visibility=vis.get(o.Name,True)
d.recompute();d.saveAs(str(O/'Assembly.FCStd'));A.closeDocument(d.Name)
(O/'DONE.txt').write_text('ok',encoding='utf8');ok=True
except Exception:(O/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf8')
finally:os._exit(0 if ok else 1)