"""Concept test only: 50mm mounting with open slots, corners flat on motor.""" from pathlib import Path import time, math, traceback import FreeCAD as A, FreeCADGui as G, Part, Import from pivy import coin R=Path(__file__).resolve().parent.parent O=R/'models'/'profile_mount_check' V=A.Vector def cyl(r,h,x,y,z): return Part.makeCylinder(r,h,V(x,y,z)) def add(doc,name,label,sh,color,trans=0): o=doc.addObject('PartDesign::Feature',name); o.Label=label; o.Shape=sh o.ViewObject.ShapeColor=color; o.ViewObject.LineColor=(.1,.1,.1); o.ViewObject.Transparency=trans return o try: old=A.openDocument(str(O/'Direct.FCStd')) keep=['Motor','Holder','Magnet','PCB','Chip']+['PCBHead%d'%i for i in range(1,4)]+['Corner%d'%i for i in range(4)]+['Rail%d'%i for i in range(2)]+['MountM4_%d'%i for i in range(1,5)] shapes={n:old.getObject(n).Shape.copy() for n in keep} orig=old.getObject('Bracket').Shape.copy() shims=[old.getObject('Shim%d'%i).Shape.copy() for i in range(4)] A.closeDocument(old.Name) doc=A.newDocument('OpenSlot50') colors={'Motor':(.32,.34,.37),'Holder':(.98,.55,.10),'Magnet':(.8,.8,.8),'PCB':(.04,.65,.19),'Chip':(.1,.1,.1)} objects={} for n in keep: color=colors.get(n,(.84,.47,.13) if n.startswith('Corner') else (.55,.56,.60) if n.startswith('Rail') else (.8,.8,.8)) objects[n]=add(doc,n,n,shapes[n],color,70 if n=='Motor' else 20 if n.startswith('Corner') else 0) pts=[] for i in range(4): a=math.radians(45+90*i);pts.append((25*math.cos(a),25*math.sin(a))) # Existing foot top is z=2.5 with 0.5 shim. Increase clamping surface to 3.5; # subtract motor-side angle brackets, keeping them directly on motor. bracket=orig for x,y in pts: bracket=bracket.fuse(cyl(4.5,1,x,y,2.5)) # Add 0.2mm plan clearance to each metal foot and 0.2mm above it. cutters=[] for i in range(4): bb=shapes['Corner%d'%i].BoundBox cutters.append(Part.makeBox(bb.XLength+.4,bb.YLength+.4,3.22,V(bb.XMin-.2,bb.YMin-.2,-.01))) for cutter in cutters: bracket=bracket.cut(cutter) # Four slots open in the same -Y direction; existing Ø4.4 clearance continues. for x,y in pts: slot=Part.makeBox(4.4,6,3.7,V(x-2.2,y-6,-.05)).fuse(cyl(2.2,3.7,x,y,-.05)) bracket=bracket.cut(slot) for j in range(4): shims[j]=shims[j].cut(slot) for j in range(4): for cutter in cutters: shims[j]=shims[j].cut(cutter) bracket=bracket.removeSplitter() if bracket.isNull() or not bracket.isValid() or len(bracket.Solids)!=1:raise RuntimeError('support not a single solid') blue=add(doc,'OpenBracket','PCD50 4 open slots - concept',bracket,(.12,.43,.88)) for i,sh in enumerate(shims): sh=sh.removeSplitter() if sh.isNull() or not sh.isValid():raise RuntimeError('shim invalid %d'%i) add(doc,'SlottedShim%d'%i,'Open 0.5mm shim - concept',sh,(.65,.7,.75)) heads=[];shafts=[] for i,(x,y) in enumerate(pts): head=cyl(3.5,3,x,y,3.5);shaft=cyl(1.95,8.5,x,y,-5) heads.append(add(doc,'EncoderHead%d'%i,'M4 head Ø7 x 3 - assumed',head,(.8,.8,.8))) shafts.append(add(doc,'EncoderShaft%d'%i,'M4 shaft Ø3.9 - assumed',shaft,(.8,.8,.8))) doc.recompute() corners=[objects['Corner%d'%i] for i in range(4)] rails=[objects['Rail%d'%i] for i in range(2)] moving=[blue]+[doc.getObject('SlottedShim%d'%i) for i in range(4)]+heads+shafts checks=[] for o in moving: for c in corners+rails: if not o.Shape.BoundBox.intersect(c.Shape.BoundBox):continue vol=o.Shape.common(c.Shape).Volume if vol>.0001:checks.append('%s vs %s %.6f mm3'%(o.Name,c.Name,vol)) for o in shafts: for c in [blue]+[doc.getObject('SlottedShim%d'%i) for i in range(4)]: if not o.Shape.BoundBox.intersect(c.Shape.BoundBox): continue vol=o.Shape.common(c.Shape).Volume if vol>.0001: checks.append('%s vs %s %.6f mm3'%(o.Name,c.Name,vol)) # Effective supporting material under each head immediately below the top. areas=[] for x,y in pts: # Planar sampling avoids degenerate thin-cylinder OCC intersections. count=0; step=.1 for ix in range(-35,36): for iy in range(-35,36): dx=ix*step;dy=iy*step if dx*dx+dy*dy<=3.5*3.5 and bracket.isInside(V(x+dx,y+dy,3.49),1e-7,True):count+=1 areas.append(count*step*step) # Kinematic slide of support from y=+5 to its assembled position: only a # quick volume test of support vs angles + shafts, not a tool/hand check. slide=[] for n in range(11): dy=5.0-n*.5; moved=bracket.copy();moved.translate(V(0,dy,0));hits=[] for c in corners+rails+shafts: if moved.BoundBox.intersect(c.Shape.BoundBox): v=moved.common(c.Shape).Volume if v>.0001:hits.append('%s %.4f'%(c.Name,v)) slide.append('dy=%.1f: %s'%(dy,', '.join(hits) if hits else 'no sampled volume overlap')) report=['CONCEPT ONLY; corners are flush to motor; no spacer under PCD76 corners.', 'Support feet 0.5..3.5mm; shim 0.5mm; screw head 7mm dia x 3mm assumed; 0.2mm corner clearance.', 'PCD50 screw slots open toward -Y; all four are parallel.', 'Only 0.05mm theoretical slot-end clearance in motor corner as original direct check.', 'Intersections of support/shims/screw envelope with corners and rails:']+(checks or ['none in checked pairs']) report+=['Approx material area under 7mm screw head using 0.1mm sampled grid (mm2): '+', '.join('%.3f'%x for x in areas),'Sampled lateral slide +Y to 0:']+slide (O/'OpenSlot50_check.txt').write_text('\n'.join(report)+'\n',encoding='utf8') view=G.activeDocument().activeView() def render(kind,name): if kind=='rear':view.viewTop() else:view.getCameraNode().orientation.setValue(coin.SbRotation(.880476,.115917,-.279848,-.364705)) G.updateGui();time.sleep(.5);view.fitAll();G.updateGui();time.sleep(.5);view.saveImage(str(O/name),1600,1200,'White') render('rear','OpenSlot50_rear.png');render('axon','OpenSlot50_axon.png') doc.recompute();doc.saveAs(str(O/'OpenSlot50.FCStd')) Import.export([o for o in doc.Objects if hasattr(o,'Shape')],str(O/'OpenSlot50.step')) (O/'OPEN_SLOT_DONE.txt').write_text('ok\n',encoding='utf8') except Exception: (O/'OPEN_SLOT_ERROR.txt').write_text(traceback.format_exc(),encoding='utf8') raise