77 lines
5.1 KiB
Plaintext
77 lines
5.1 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_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 - dimensional concept with envelope collision'
|
|
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.,-35.,'A'),(42.,-22.,'B'))):
|
|
ax=x+(-.75 if x<0 else .75) # angle offset; M4 ear bolt clearance in 6.5mm slot
|
|
# Long leg on the 2020 side; short foot extends away from the 2020 and stays above the 3030.
|
|
upright=box(ax-9.5,-55,-145,19,50,3).cut(sy(ax,-45.75,-19.25,-145.02,3.25,3.04))
|
|
foot=box(ax-9.5,-55,-176,19,3,31).cut(sz(ax,-55.02,-165.25,-155.75,3.25,3.04))
|
|
a=add(d,'SwitchAngle'+tag,upright.fuse(foot),(.98,.52,.05),'Long 50x31 angle '+tag+'; foot extends outward');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,-45,-148),(.12,.12,.12),'Side 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)
|
|
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([o.Name for o in new+ears]+['SwitchEnvelope'])
|
|
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)
|