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'/'kfl_3030_motor_corner_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 add(d,n,s,c): o=d.addObject('PartDesign::Feature',n);o.Shape=s.removeSplitter();o.Label=n;o.ViewObject.ShapeColor=c;o.ViewObject.LineColor=(.15,.15,.15);return o ok=False try: d=A.openDocument(str(R/'models'/'kfl_3030_concept'/'Assembly.FCStd'));d.Label='3030 base, motor rail corner joints - concept' d.recompute() corners=[]; extensions=[] for i,x in enumerate((-50.6200577,50.6200577)): rail=d.getObject('NewRail%d'%i);rail.Shape=box(x-15,-85,-215,30,30,255);rail.Label='3030 longitudinal base rail, extended 20 mm' ext=box(x-15,-85,20,30,30,20);extensions.append(ext) # Outer size 20 x 20, width 18, thickness 3. Short slot overall 9.5mm, 4mm from tip. upright=box(x-9,-55,20,18,20,3) slot=box(x-2.75,-45.75,19.98,5.5,4,3.04).fuse(cy(2.75,3.04,x,-45.75,19.98)).fuse(cy(2.75,3.04,x,-41.75,19.98)) upright=upright.cut(slot) foot=box(x-9,-55,20,18,3,20).cut(cy(2.75,3.04,x,-55.02,30,V(0,1,0))) corners.append(add(d,'MotorBaseCorner%d'%i,upright.fuse(foot),(.86,.12,.14))) d.recompute() # Only check new angles and the newly added portions of rails against existing visible geometry. visible=[o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility and o not in corners] lines=[]; bad=[] for i,a in enumerate(corners): for b in visible + corners[i+1:]: v=a.Shape.common(b.Shape).Volume if a.Shape.BoundBox.intersect(b.Shape.BoundBox) else 0. dist=a.Shape.distToShape(b.Shape)[0] lines.append('%s / %s: overlap=%.6f mm3, distance=%.4f mm'%(a.Name,b.Name,v,dist)) if v>0.001:bad.append((a.Name,b.Name,v)) for i,ext in enumerate(extensions): for b in visible: if b.Name=='NewRail%d'%i: continue v=ext.common(b.Shape).Volume if ext.BoundBox.intersect(b.Shape.BoundBox) else 0. if v>0.001:bad.append(('rail extension %d'%i,b.Name,v)) if v>0.001:lines.append('rail extension %d / %s: overlap=%.6f mm3'%(i,b.Name,v)) (O/'interference_report.txt').write_text('\n'.join(lines)+'\n',encoding='utf8') (O/'positive_intersections.txt').write_text('\n'.join('%s / %s = %.6f mm3'%x for x in bad) or 'none',encoding='utf8') d.saveAs(str(O/'Assembly.FCStd'));Import.export([o for o in d.Objects if hasattr(o,'Shape')],str(O/'Assembly.step')) view=G.activeDocument().activeView();G.activateWorkbench('PartWorkbench') def save(name,mode): if mode=='front':view.viewFront() elif mode=='top':view.viewTop() 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') save('assembly.png','axon') vis={o.Name:o.ViewObject.Visibility for o in d.Objects if hasattr(o,'Shape')} # Hide the broad framing/shaft only to make the actual motor connections legible. names={'Motor','OpenPillarBracket','Holder','Magnet','PCB','Chip','Rail0','Rail1','NewRail0','NewRail1','Corner0','Corner1','Corner2','Corner3','MotorBaseCorner0','MotorBaseCorner1'} for o in d.Objects: if hasattr(o,'Shape'):o.ViewObject.Visibility=o.Name in names save('motor_corner_detail.png','axon');save('motor_corner_top.png','top') 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) # Reopen FCStd and separately import STEP to confirm persistence/nonempty shape. d=A.openDocument(str(O/'Assembly.FCStd')) valid=[(n,not d.getObject(n).Shape.isNull(),d.getObject(n).Shape.isValid()) for n in ['MotorBaseCorner0','MotorBaseCorner1','NewRail0','NewRail1']] A.closeDocument(d.Name) stepdoc=A.newDocument('StepVerify');Import.insert(str(O/'Assembly.step'),stepdoc.Name);stepdoc.recompute() stepcount=sum(1 for o in stepdoc.Objects if hasattr(o,'Shape') and not o.Shape.isNull()) A.closeDocument(stepdoc.Name) (O/'validation.txt').write_text('FCStd reopen: '+str(valid)+'\nSTEP nonempty shapes: '+str(stepcount)+'\n',encoding='utf8') (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)