53 lines
3.5 KiB
Plaintext
53 lines
3.5 KiB
Plaintext
from pathlib import Path
|
|
import os,time,traceback,json
|
|
import FreeCAD as A, FreeCADGui as G, Part, Mesh, Import
|
|
V=A.Vector
|
|
R=Path(r'C:\projects\wheel'); O=R/'models'/'fan120_integer_v2'; O.mkdir(parents=True,exist_ok=True)
|
|
def cy(r,h,x,y,z): return Part.makeCylinder(r,h,V(x,y,z))
|
|
def slot(x,y,z,r,travel,h): return Part.makeBox(travel,2*r,h,V(x-travel/2,y-r,z)).fuse(cy(r,h,x-travel/2,y,z)).fuse(cy(r,h,x+travel/2,y,z))
|
|
def feature(d,name,shape,color):
|
|
o=d.addObject('PartDesign::Feature',name);o.Shape=shape;o.Label=name;o.ViewObject.ShapeColor=color;return o
|
|
try:
|
|
d=A.openDocument(str(R/'models'/'integer_profiles'/'Assembly.FCStd'));d.recompute()
|
|
source=[o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility]
|
|
centers=[(d.getObject('Rail%d'%i).Shape.BoundBox.XMin+d.getObject('Rail%d'%i).Shape.BoundBox.XMax)/2 for i in range(2)]
|
|
zrail=20.; zframe=42.
|
|
frame=Part.makeBox(120,120,4,V(-60,-60,zframe)).cut(Part.makeBox(96,96,4.04,V(-48,-48,zframe-.02)))
|
|
for x in centers:
|
|
for y in (-25,25):frame=frame.fuse(Part.makeBox(24,18,zframe+4-zrail,V(x-12,y-9,zrail)))
|
|
frame=frame.removeSplitter()
|
|
for x in (-52.5,52.5):
|
|
for y in (-52.5,52.5):frame=frame.cut(cy(2.25,4.04,x,y,zframe-.02))
|
|
for x in centers:
|
|
for y in (-25,25):
|
|
frame=frame.cut(slot(x,y,zrail-.02,2.75,10,zframe+4-zrail+.04))
|
|
frame=frame.cut(slot(x,y,zframe-1.5,4.9,10,5.52))
|
|
frame=frame.removeSplitter()
|
|
assert frame.isValid() and len(frame.Solids)==1
|
|
fan=Part.makeBox(120,120,25,V(-60,-60,zframe+4)).cut(cy(56,25.04,0,0,zframe+3.98))
|
|
for x in (-52.5,52.5):
|
|
for y in (-52.5,52.5):fan=fan.cut(cy(2.2,25.04,x,y,zframe+3.98))
|
|
fan=fan.removeSplitter()
|
|
result=[]
|
|
for o in source:
|
|
for name,sh in [('frame',frame),('fan',fan)]:
|
|
if sh.BoundBox.intersect(o.Shape.BoundBox):
|
|
v=sh.common(o.Shape).Volume
|
|
if v>1e-4:result.append([name,o.Name,v])
|
|
(O/'hits.json').write_text(json.dumps(result,indent=2),encoding='utf8')
|
|
if result:raise RuntimeError('Intersecting assemblies '+str(result))
|
|
for i in range(2):
|
|
rail=d.getObject('Rail%d'%i).Shape
|
|
assert frame.distToShape(rail)[0]<1e-6 and frame.common(rail).Volume<1e-5
|
|
mount=feature(d,'FanSupportV2',frame,(0.95,.55,.08)); ff=feature(d,'FanEnvelopeV2',fan,(.2,.2,.2));ff.ViewObject.Transparency=55
|
|
d.recompute();d.saveAs(str(O/'Assembly.FCStd'))
|
|
G.activeDocument().activeView().viewAxonometric();G.updateGui();G.activeDocument().activeView().fitAll();time.sleep(.5);G.updateGui();G.activeDocument().activeView().saveImage(str(O/'assembly.png'),1600,1200,'White')
|
|
p=frame.copy();p.translate(V(0,0,-(zframe+4)));p.rotate(V(),V(1,0,0),180)
|
|
pd=A.newDocument('Fan120Print');po=feature(pd,'FanMount',Part.makeCompound([p]),(.15,.5,.86));pd.recompute();Mesh.export([po],str(O/'Fan120Mount.stl'));Import.export([po],str(O/'Fan120Mount.step'));pd.saveAs(str(O/'Fan120Mount.FCStd'))
|
|
mesh=Mesh.Mesh(str(O/'Fan120Mount.stl'));assert mesh.isSolid() and abs(mesh.BoundBox.ZMin)<1e-3
|
|
data={'rail_centres_x':centers,'leg_y':[ -25,25],'rail_top_z':zrail,'ring_bottom_z':zframe,'fan_bottom_z':zframe+4,'leg_height':zframe+4-zrail,'frame_volume':frame.Volume,'mesh_solid':mesh.isSolid(),'stl_z_min':mesh.BoundBox.ZMin,'static_hits':result,'fan_envelope_mm':[120,120,25],'not_modeled':'slot hardware, fan hub, cable, user physical components'}
|
|
(O/'validation.json').write_text(json.dumps(data,indent=2),encoding='utf8');(O/'DONE.txt').write_text('ok',encoding='utf8')
|
|
A.closeDocument(pd.Name);A.closeDocument(d.Name);os._exit(0)
|
|
except Exception:
|
|
(O/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf8');os._exit(1)
|