建立方向盘项目初始版本
This commit is contained in:
103
scripts/build_fan_direct145.FCMacro
Normal file
103
scripts/build_fan_direct145.FCMacro
Normal file
@@ -0,0 +1,103 @@
|
||||
from pathlib import Path
|
||||
import os, math, json, time, traceback
|
||||
import FreeCAD as A, FreeCADGui as G, Part, Import
|
||||
V = A.Vector
|
||||
root = Path(r'C:\projects\wheel')
|
||||
out = root / 'models' / 'fan_direct_145'
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
d = A.openDocument(str(root / 'models' / 'integer_profiles' / 'Assembly.FCStd'))
|
||||
d.recompute()
|
||||
rails = [d.getObject('Rail0'), d.getObject('Rail1')]
|
||||
xs = [(o.Shape.BoundBox.XMin + o.Shape.BoundBox.XMax) / 2 for o in rails]
|
||||
spacing = xs[1] - xs[0]
|
||||
theta = math.degrees(math.acos(spacing / (105.0 * math.sqrt(2.0)))) - 45.0
|
||||
corners_before = [d.getObject('MotorBaseCorner%d' % i).Shape.copy() for i in range(2)]
|
||||
old_rails = [o.Shape.copy() for o in rails]
|
||||
for i, o in enumerate(rails):
|
||||
o.Placement = A.Placement()
|
||||
o.Shape = Part.makeBox(20, 145, 20, V(xs[i] - 10, -55, 0))
|
||||
o.Label = '2020 motor profile - 145 mm'
|
||||
d.recompute()
|
||||
src = Part.makeBox(120, 120, 25, V(-60, -60, 20)).cut(Part.makeCylinder(56, 25.04, V(0,0,19.98)))
|
||||
for x in (-52.5, 52.5):
|
||||
for y in (-52.5, 52.5):
|
||||
src = src.cut(Part.makeCylinder(2.2, 25.04, V(x,y,19.98)))
|
||||
fan = src.removeSplitter()
|
||||
fan.rotate(V(0,0,0), V(0,0,1), theta)
|
||||
fan.translate(V(0,28,0))
|
||||
assert fan.isValid() and len(fan.Solids) == 1
|
||||
original = [o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility]
|
||||
fan_hits = []
|
||||
for o in original:
|
||||
if fan.BoundBox.intersect(o.Shape.BoundBox):
|
||||
volume = fan.common(o.Shape).Volume
|
||||
if volume > 1e-4: fan_hits.append([o.Name, volume])
|
||||
assert not fan_hits, 'fan collisions: ' + str(fan_hits)
|
||||
extra_hits = []
|
||||
for i, rail in enumerate(rails):
|
||||
extra = rail.Shape.cut(old_rails[i])
|
||||
for o in original:
|
||||
if o == rail or o in rails: continue
|
||||
if extra.BoundBox.intersect(o.Shape.BoundBox):
|
||||
volume = extra.common(o.Shape).Volume
|
||||
if volume > 1e-4: extra_hits.append([rail.Name, o.Name, volume])
|
||||
assert old_rails[i].cut(rail.Shape).Volume < 1e-5
|
||||
assert not extra_hits, 'rail extension collisions: ' + str(extra_hits)
|
||||
for i in range(2):
|
||||
assert corners_before[i].cut(d.getObject('MotorBaseCorner%d' % i).Shape).Volume < 1e-7
|
||||
assert d.getObject('MotorBaseCorner%d' % i).Shape.cut(corners_before[i]).Volume < 1e-7
|
||||
o = d.addObject('PartDesign::Feature', 'DirectFan')
|
||||
o.Label = '120 mm fan envelope - rotated, two diagonal fixings; user fan assumed'
|
||||
o.Shape = fan
|
||||
o.ViewObject.ShapeColor = (.22,.24,.27)
|
||||
o.ViewObject.Transparency = 18
|
||||
alpha = math.radians(theta)
|
||||
fasteners=[]
|
||||
holes=[]
|
||||
for sign in (-1,1):
|
||||
px = sign * 52.5 * (math.cos(alpha) - math.sin(alpha))
|
||||
py = 28 + sign * 52.5 * (math.cos(alpha) + math.sin(alpha))
|
||||
idx=0 if sign<0 else 1
|
||||
assert abs(px-xs[idx]) < 1e-6
|
||||
assert -55 < py < 90
|
||||
shaft=Part.makeCylinder(2,25,V(px,py,20))
|
||||
head=Part.makeCylinder(3.5,3,V(px,py,45))
|
||||
screw=shaft.fuse(head).removeSplitter()
|
||||
assert screw.common(fan).Volume < 1e-5
|
||||
f=d.addObject('PartDesign::Feature','FanBolt'+str(idx))
|
||||
f.Shape=screw; f.Label='Illustrative M4 fastener, nut not modeled'; f.ViewObject.ShapeColor=(.65,.66,.68)
|
||||
fasteners.append(f)
|
||||
holes.append({'x':px,'y':py,'rail_center_x':xs[idx], 'distance_to_end_mm': min(py+55,90-py)})
|
||||
d.recompute()
|
||||
for obj in [o]+fasteners:
|
||||
assert not obj.Shape.isNull() and obj.Shape.isValid()
|
||||
d.Label='120 mm fan on 145 mm profiles - 2 diagonal screws, no bracket'
|
||||
d.saveAs(str(out/'Assembly.FCStd'))
|
||||
visible=[a for a in d.Objects if hasattr(a,'Shape') and not a.Shape.isNull() and a.ViewObject.Visibility]
|
||||
Import.export(visible, str(out/'Assembly.step'))
|
||||
view=G.activeDocument().activeView(); view.viewAxonometric(); G.updateGui();view.fitAll();time.sleep(.5);G.updateGui()
|
||||
view.saveImage(str(out/'assembly.png'),1800,1300,'White')
|
||||
vis={a.Name:a.ViewObject.Visibility for a in d.Objects if hasattr(a,'Shape')}
|
||||
names={'Rail0','Rail1','NewRail0','NewRail1','MotorBaseCorner0','MotorBaseCorner1','DirectFan','FanBolt0','FanBolt1','Motor','OpenPillarBracket','Holder','Magnet','PCB','Chip'}
|
||||
for a in d.Objects:
|
||||
if hasattr(a,'Shape'):a.ViewObject.Visibility=a.Name in names
|
||||
view.viewTop();G.updateGui();view.fitAll();time.sleep(.5);G.updateGui();view.saveImage(str(out/'fan_top.png'),1800,1300,'White')
|
||||
view.viewAxonometric();G.updateGui();view.fitAll();time.sleep(.5);G.updateGui();view.saveImage(str(out/'fan_detail.png'),1800,1300,'White')
|
||||
for a in d.Objects:
|
||||
if hasattr(a,'Shape'):a.ViewObject.Visibility=vis.get(a.Name,False)
|
||||
d.recompute();d.saveAs(str(out/'Assembly.FCStd'))
|
||||
expected=[a.Name for a in visible]
|
||||
A.closeDocument(d.Name)
|
||||
d2=A.openDocument(str(out/'Assembly.FCStd'));d2.recompute()
|
||||
assert all(d2.getObject(x) and d2.getObject(x).Shape.isValid() for x in ['Rail0','Rail1','DirectFan','FanBolt0','FanBolt1'])
|
||||
assert all(abs(d2.getObject('Rail%d'%i).Shape.BoundBox.YLength-145)<1e-7 for i in (0,1))
|
||||
st=Part.read(str(out/'Assembly.step'));assert st.isValid() and len(st.Solids)>0
|
||||
data={'motor_2020_length_mm':145,'2020_longitudinal_limits_mm':[-55,90], 'fan_envelope_mm':[120,120,25], 'fan_mount_pitch_mm':105,'fan_center_y_mm':28,'motor_center_y_mm':10,'fan_offset_from_motor_mm':18,'rotation_deg':theta,'fastener_centers':holes,'fan_collisions':fan_hits,'extension_collisions':extra_hits,'original_corners_unchanged':True,'fcstd_reopened':True,'step_valid':True,'assumptions':'Fan solid frame and holes schematic; no blade, hub, wires, real T-slot or nut. Shaft of screw only above extrusion surface.'}
|
||||
(out/'validation.json').write_text(json.dumps(data,ensure_ascii=False,indent=2),encoding='utf8')
|
||||
A.closeDocument(d2.Name)
|
||||
(out/'DONE.txt').write_text('ok',encoding='utf8')
|
||||
os._exit(0)
|
||||
except Exception:
|
||||
(out/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf8')
|
||||
os._exit(1)
|
||||
Reference in New Issue
Block a user