Files
wheel/scripts/build_conversion_disk.py

110 lines
6.2 KiB
Python

# Rebuild through rebuild_conversion_disk.FCMacro in FreeCAD's GUI to save the
# same visibility and view as the delivered file. Running this .py directly
# with FreeCADCmd also builds the geometry, but has no GUI view to save.
# Rebuilding overwrites models/conversion_disk_parametric.FCStd.
import FreeCAD as App
import Part, Sketcher, math, pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent
source = ROOT / 'models' / '转换盘1.step'
target = ROOT / 'models' / 'conversion_disk_parametric.FCStd'
doc = App.newDocument('ConversionDisk')
params = doc.addObject('Spreadsheet::Sheet', 'Parameters')
params.Label = 'Parameters (mm)'
rows = [
('Thickness','8 mm','Plate thickness'),
('OuterPCD','48 mm','Outer 6-hole pitch circle diameter'),
('OuterHole','4.234 mm','Outer 6-hole diameter'),
('CenterHole','15 mm','Center through hole diameter'),
('InnerPCD','20 mm','Inner 8-hole pitch circle diameter'),
('InnerHole','3 mm','Inner 8-hole diameter'),
('RecessDiameter','28 mm','Bottom recess diameter'),
('RecessDepth','2 mm','Bottom recess depth'),
('OutlineScale','1','Overall outline scale relative to outer PCD'),
('EdgeChamfer','1.5 mm','Outer edges chamfer'),
('OuterAngle','30 deg','Outer 6-hole start angle'),
('InnerAngle','0 deg','Inner 8-hole start angle'),
]
params.set('A1','Parameter'); params.set('B1','Value'); params.set('C1','Description')
for i,(key,val,desc) in enumerate(rows,2):
params.set('A%d'%i,key);params.set('B%d'%i,val);params.set('C%d'%i,desc);params.setAlias('B%d'%i,key)
doc.recompute()
reference=doc.addObject('PartDesign::Feature','OriginalReference')
reference.Label='Original STEP reference'
reference.Shape=Part.read(str(source))
body=doc.addObject('PartDesign::Body','Body')
body.Label='Conversion disk'
def sketch(name, z=None):
s=doc.addObject('Sketcher::SketchObject',name);body.addObject(s)
if z is not None:
s.Placement.Base.z=z
s.setExpression('Placement.Base.z','Parameters.Thickness')
return s
def circle(s,x,y,r,exprx=None,expry=None,exprr=None):
k=s.addGeometry(Part.Circle(App.Vector(x,y,0),App.Vector(0,0,1),r),False)
if abs(x)<1e-8 and abs(y)<1e-8:
s.addConstraint(Sketcher.Constraint('Coincident',k,3,-1,1))
else:
if abs(x)<1e-8: s.addConstraint(Sketcher.Constraint('PointOnObject',k,3,-2))
else:
ci=s.addConstraint(Sketcher.Constraint('DistanceX',k,3,x))
if exprx: s.setExpression('Constraints[%d]'%ci,exprx)
if abs(y)<1e-8: s.addConstraint(Sketcher.Constraint('PointOnObject',k,3,-1))
else:
ci=s.addConstraint(Sketcher.Constraint('DistanceY',k,3,y))
if expry: s.setExpression('Constraints[%d]'%ci,expry)
ci=s.addConstraint(Sketcher.Constraint('Radius',k,r))
if exprr:s.setExpression('Constraints[%d]'%ci,exprr)
def verify(obj):
doc.recompute()
print('CHECK',obj.Name, 'shape', obj.Shape.isNull(), 'valid', False if obj.Shape.isNull() else obj.Shape.isValid(), 'status',obj.State)
if obj.Shape.isNull() or not obj.Shape.isValid() or 'Invalid' in obj.State:raise RuntimeError('Invalid feature '+obj.Name)
# Preserve the true STEP mid-plane perimeter. A native link scales the whole
# profile in XY and rotates it with the outer hole group; the individual arcs
# are fixed source geometry rather than independently editable constraints.
wire=max(reference.Shape.slice(App.Vector(0,0,1),4),key=lambda w:w.Length)
face=Part.Face(wire);face.translate(App.Vector(0,0,-4))
profile=doc.addObject('Part::Feature','OriginalOutline')
profile.Label='Original outline at mid-thickness (reference geometry)';profile.Shape=face
scaled=doc.addObject('App::Link','ScaledOutline');scaled.Label='Original outline - scaled and rotated'
scaled.setLink(profile);scaled.LinkTransform=True
scaled.setExpression('ScaleVector.x','Parameters.OuterPCD / 48 mm * Parameters.OutlineScale')
scaled.setExpression('ScaleVector.y','Parameters.OuterPCD / 48 mm * Parameters.OutlineScale')
scaled.setExpression('Placement.Rotation.Angle','Parameters.OuterAngle - 30 deg')
outline=doc.addObject('Part::Extrusion','Outline');outline.Label='Original profile extruded'
outline.Base=scaled;outline.DirMode='Custom';outline.Dir=App.Vector(0,0,1);outline.LengthFwd=8;outline.Solid=True
outline.setExpression('LengthFwd','Parameters.Thickness');verify(outline)
body.BaseFeature=outline;doc.recompute()
edges=[]
for i,e in enumerate(outline.Shape.Edges,1):
b=e.BoundBox
if abs(b.ZMax-b.ZMin)<1e-7 and (abs(b.ZMin)<1e-7 or abs(b.ZMin-8)<1e-7):edges.append('Edge%d'%i)
print('CHAMFER edges',edges)
ch=body.newObject('PartDesign::Chamfer','PerimeterChamfer');ch.Base=(ch.BaseFeature,edges);ch.Size=1.5;ch.setExpression('Size','Parameters.EdgeChamfer');verify(ch)
recesssk=sketch('RecessProfile');circle(recesssk,0,0,14,exprr='Parameters.RecessDiameter / 2');doc.recompute()
recess=body.newObject('PartDesign::Pocket','BottomRecess');recess.Profile=recesssk;recess.Length=2;recess.Reversed=True;recess.setExpression('Length','Parameters.RecessDepth');verify(recess)
center=sketch('CenterProfile',8);center.Label='Center through hole'
circle(center,0,0,7.5,exprr='Parameters.CenterHole / 2');doc.recompute()
center_cut=body.newObject('PartDesign::Pocket','CenterHole');center_cut.Profile=center;center_cut.Length=8;center_cut.setExpression('Length','Parameters.Thickness');verify(center_cut)
for count,pcd,r,diam,angle in [(6,'OuterPCD',2.117,'OuterHole','OuterAngle'),(8,'InnerPCD',1.5,'InnerHole','InnerAngle')]:
holes=sketch(diam+'Profile',8);holes.Label=diam+' pattern'
holes.setExpression('Placement.Rotation.Angle','Parameters.'+angle)
for i in range(count):
a=math.radians(i*360/count);c=math.cos(a);s=math.sin(a);d=24 if count==6 else 10
circle(holes,d*c,d*s,r,'(%s) * Parameters.%s / 2'%(c,pcd),'(%s) * Parameters.%s / 2'%(s,pcd),'Parameters.%s / 2'%diam)
doc.recompute()
holes_cut=body.newObject('PartDesign::Pocket',diam+'Through');holes_cut.Profile=holes;holes_cut.Length=8;holes_cut.setExpression('Length','Parameters.Thickness');verify(holes_cut)
print('FINAL volume',holes_cut.Shape.Volume,'bounds',holes_cut.Shape.BoundBox)
doc.recompute()
if App.GuiUp:
import FreeCADGui as Gui
for obj in doc.Objects:
if obj.ViewObject:
obj.ViewObject.Visibility = False
body.ViewObject.Visibility = True
holes_cut.ViewObject.Visibility = True
Gui.activeDocument().activeView().viewAxonometric()
Gui.activeDocument().activeView().fitAll()
doc.saveAs(str(target))
print('SAVED',str(target))