diff --git a/freecad/gears/__init__.py b/freecad/gears/__init__.py index d59c720..979541d 100644 --- a/freecad/gears/__init__.py +++ b/freecad/gears/__init__.py @@ -17,4 +17,5 @@ # *************************************************************************** import pygears + __version__ = pygears.__version__ diff --git a/freecad/gears/commands.py b/freecad/gears/commands.py index 7063204..212d65d 100644 --- a/freecad/gears/commands.py +++ b/freecad/gears/commands.py @@ -19,8 +19,23 @@ import os import FreeCAD import FreeCADGui as Gui -from .features import ViewProviderGear, InvoluteGear, InternalInvoluteGear, InvoluteGearRack, CycloidGearRack -from .features import CycloidGear, BevelGear, CrownGear, WormGear, TimingGear, LanternGear, HypoCycloidGear, BaseGear +from .features import ( + ViewProviderGear, + InvoluteGear, + InternalInvoluteGear, + InvoluteGearRack, + CycloidGearRack, +) +from .features import ( + CycloidGear, + BevelGear, + CrownGear, + WormGear, + TimingGear, + LanternGear, + HypoCycloidGear, + BaseGear, +) from .connector import GearConnector, ViewProviderGearConnector @@ -40,8 +55,9 @@ class BaseCommand(object): def Activated(self): Gui.doCommandGui("import freecad.gears.commands") - Gui.doCommandGui("freecad.gears.commands.{}.create()".format( - self.__class__.__name__)) + Gui.doCommandGui( + "freecad.gears.commands.{}.create()".format(self.__class__.__name__) + ) FreeCAD.ActiveDocument.recompute() Gui.SendMsgToActiveView("ViewFit") @@ -54,7 +70,9 @@ class BaseCommand(object): part = Gui.ActiveDocument.ActiveView.getActiveObject("part") if body: - obj = FreeCAD.ActiveDocument.addObject("PartDesign::FeaturePython", cls.NAME) + obj = FreeCAD.ActiveDocument.addObject( + "PartDesign::FeaturePython", cls.NAME + ) else: obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", cls.NAME) ViewProviderGear(obj.ViewObject, cls.Pixmap) @@ -70,101 +88,107 @@ class BaseCommand(object): return obj def GetResources(self): - return {'Pixmap': self.Pixmap, - 'MenuText': self.MenuText, - 'ToolTip': self.ToolTip} + return { + "Pixmap": self.Pixmap, + "MenuText": self.MenuText, + "ToolTip": self.ToolTip, + } class CreateInvoluteGear(BaseCommand): NAME = "InvoluteGear" GEAR_FUNCTION = InvoluteGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'involutegear.svg') - MenuText = 'Involute Gear' - ToolTip = 'Create an external involute gear' + Pixmap = os.path.join(BaseCommand.ICONDIR, "involutegear.svg") + MenuText = "Involute Gear" + ToolTip = "Create an external involute gear" class CreateInternalInvoluteGear(BaseCommand): NAME = "InternalInvoluteGear" GEAR_FUNCTION = InternalInvoluteGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'internalinvolutegear.svg') - MenuText = 'Internal Involute Gear' - ToolTip = 'Create an internal involute gear' + Pixmap = os.path.join(BaseCommand.ICONDIR, "internalinvolutegear.svg") + MenuText = "Internal Involute Gear" + ToolTip = "Create an internal involute gear" class CreateInvoluteRack(BaseCommand): NAME = "InvoluteRack" GEAR_FUNCTION = InvoluteGearRack - Pixmap = os.path.join(BaseCommand.ICONDIR, 'involuterack.svg') - MenuText = 'Involute Rack' - ToolTip = 'Create an Involute rack' + Pixmap = os.path.join(BaseCommand.ICONDIR, "involuterack.svg") + MenuText = "Involute Rack" + ToolTip = "Create an Involute rack" + class CreateCycloidRack(BaseCommand): NAME = "CycloidRack" GEAR_FUNCTION = CycloidGearRack - Pixmap = os.path.join(BaseCommand.ICONDIR, 'cycloidrack.svg') - MenuText = 'Cycloid Rack' - ToolTip = 'Create an Cycloid rack' + Pixmap = os.path.join(BaseCommand.ICONDIR, "cycloidrack.svg") + MenuText = "Cycloid Rack" + ToolTip = "Create an Cycloid rack" class CreateCrownGear(BaseCommand): NAME = "CrownGear" GEAR_FUNCTION = CrownGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'crowngear.svg') - MenuText = 'Crown Gear' - ToolTip = 'Create a Crown gear' + Pixmap = os.path.join(BaseCommand.ICONDIR, "crowngear.svg") + MenuText = "Crown Gear" + ToolTip = "Create a Crown gear" class CreateCycloidGear(BaseCommand): NAME = "CycloidGear" GEAR_FUNCTION = CycloidGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'cycloidgear.svg') - MenuText = 'Cycloid Gear' - ToolTip = 'Create a Cycloid gear' + Pixmap = os.path.join(BaseCommand.ICONDIR, "cycloidgear.svg") + MenuText = "Cycloid Gear" + ToolTip = "Create a Cycloid gear" class CreateBevelGear(BaseCommand): NAME = "BevelGear" GEAR_FUNCTION = BevelGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'bevelgear.svg') - MenuText = 'Bevel Gear' - ToolTip = 'Create a Bevel gear' + Pixmap = os.path.join(BaseCommand.ICONDIR, "bevelgear.svg") + MenuText = "Bevel Gear" + ToolTip = "Create a Bevel gear" + class CreateHypoCycloidGear(BaseCommand): NAME = "HypocycloidGear" GEAR_FUNCTION = HypoCycloidGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'hypocycloidgear.svg') - MenuText = 'HypoCycloid Gear' - ToolTip = 'Create a HypoCycloid gear with its pins' + Pixmap = os.path.join(BaseCommand.ICONDIR, "hypocycloidgear.svg") + MenuText = "HypoCycloid Gear" + ToolTip = "Create a HypoCycloid gear with its pins" class CreateWormGear(BaseCommand): NAME = "WormGear" GEAR_FUNCTION = WormGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'wormgear.svg') - MenuText = 'Worm Gear' - ToolTip = 'Create a Worm gear' + Pixmap = os.path.join(BaseCommand.ICONDIR, "wormgear.svg") + MenuText = "Worm Gear" + ToolTip = "Create a Worm gear" class CreateTimingGear(BaseCommand): NAME = "TimingGear" GEAR_FUNCTION = TimingGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'timinggear.svg') - MenuText = 'Timing Gear' - ToolTip = 'Create a Timing gear' + Pixmap = os.path.join(BaseCommand.ICONDIR, "timinggear.svg") + MenuText = "Timing Gear" + ToolTip = "Create a Timing gear" + class CreateLanternGear(BaseCommand): NAME = "LanternGear" GEAR_FUNCTION = LanternGear - Pixmap = os.path.join(BaseCommand.ICONDIR, 'lanterngear.svg') - MenuText = 'Lantern Gear' - ToolTip = 'Create a Lantern gear' + Pixmap = os.path.join(BaseCommand.ICONDIR, "lanterngear.svg") + MenuText = "Lantern Gear" + ToolTip = "Create a Lantern gear" + class CreateGearConnector(BaseCommand): NAME = "GearConnector" GEAR_FUNCTION = GearConnector - Pixmap = os.path.join(BaseCommand.ICONDIR, 'gearconnector.svg') - MenuText = 'Combine two gears' - ToolTip = 'Combine two gears' + Pixmap = os.path.join(BaseCommand.ICONDIR, "gearconnector.svg") + MenuText = "Combine two gears" + ToolTip = "Combine two gears" def Activated(self): gear1 = Gui.Selection.getSelection()[0] diff --git a/freecad/gears/connector.py b/freecad/gears/connector.py index 9869bf4..394cb03 100644 --- a/freecad/gears/connector.py +++ b/freecad/gears/connector.py @@ -20,16 +20,23 @@ import os import numpy as np import FreeCAD from pygears import __version__ -from .features import InvoluteGear, CycloidGear, InvoluteGearRack, CycloidGearRack, InternalInvoluteGear +from .features import ( + InvoluteGear, + CycloidGear, + InvoluteGearRack, + CycloidGearRack, + InternalInvoluteGear, +) from pygears.computation import compute_shifted_gears + class ViewProviderGearConnector(object): def __init__(self, vobj, icon_fn=None): # Set this object to the proxy object of the actual view provider vobj.Proxy = self dirname = os.path.dirname(__file__) self.icon_fn = icon_fn or os.path.join(dirname, "icons", "gearconnector.svg") - + def attach(self, vobj): self.vobj = vobj @@ -45,11 +52,25 @@ class ViewProviderGearConnector(object): class GearConnector(object): def __init__(self, obj, master_gear, slave_gear): - obj.addProperty("App::PropertyString", "version", "version", "freecad.gears-version", 1) - obj.addProperty("App::PropertyLink","master_gear","gear","master gear", 1) - obj.addProperty("App::PropertyLink","slave_gear","gear","slave gear", 1) - obj.addProperty("App::PropertyAngle", "angle1", "gear", "angle at which second gear is placed", 0) - obj.addProperty("App::PropertyAngle", "angle2", "gear", "angle at which second gear is placed", 1) + obj.addProperty( + "App::PropertyString", "version", "version", "freecad.gears-version", 1 + ) + obj.addProperty("App::PropertyLink", "master_gear", "gear", "master gear", 1) + obj.addProperty("App::PropertyLink", "slave_gear", "gear", "slave gear", 1) + obj.addProperty( + "App::PropertyAngle", + "angle1", + "gear", + "angle at which second gear is placed", + 0, + ) + obj.addProperty( + "App::PropertyAngle", + "angle2", + "gear", + "angle at which second gear is placed", + 1, + ) obj.version = __version__ obj.master_gear = master_gear obj.slave_gear = slave_gear @@ -59,8 +80,12 @@ class GearConnector(object): def onChanged(self, fp, prop): # fp.angle2 = fp.master_gear.Placement.Rotation.Angle - if isinstance(fp.master_gear.Proxy, InvoluteGear) and isinstance(fp.slave_gear.Proxy, InvoluteGear): - angle_master = fp.master_gear.Placement.Rotation.Angle * sum(fp.master_gear.Placement.Rotation.Axis) + if isinstance(fp.master_gear.Proxy, InvoluteGear) and isinstance( + fp.slave_gear.Proxy, InvoluteGear + ): + angle_master = fp.master_gear.Placement.Rotation.Angle * sum( + fp.master_gear.Placement.Rotation.Axis + ) dw_master = fp.master_gear.dw dw_slave = fp.slave_gear.dw dist = (dw_master + dw_slave) / 2 @@ -71,24 +96,29 @@ class GearConnector(object): fp.master_gear.teeth, fp.slave_gear.teeth, fp.master_gear.shift, - fp.slave_gear.shift) + fp.slave_gear.shift, + ) mat0 = FreeCAD.Matrix() # unity matrix trans = FreeCAD.Vector(dist) mat0.move(trans) - rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), fp.angle1).toMatrix() + rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), fp.angle1).toMatrix() angle2 = dw_master / dw_slave * fp.angle1.Value angle4 = dw_master / dw_slave * np.rad2deg(angle_master) - rot2 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), angle2).toMatrix() - angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180. / fp.slave_gear.teeth - rot3 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), angle3).toMatrix() - rot4 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), -angle4).toMatrix() + rot2 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle2).toMatrix() + angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180.0 / fp.slave_gear.teeth + rot3 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle3).toMatrix() + rot4 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -angle4).toMatrix() mat1 = rot * mat0 * rot2 * rot3 * rot4 mat1.move(fp.master_gear.Placement.Base) fp.slave_gear.Placement = mat1 - if isinstance(fp.master_gear.Proxy, InternalInvoluteGear) and isinstance(fp.slave_gear.Proxy, InvoluteGear): - angle_master = fp.master_gear.Placement.Rotation.Angle * sum(fp.master_gear.Placement.Rotation.Axis) + if isinstance(fp.master_gear.Proxy, InternalInvoluteGear) and isinstance( + fp.slave_gear.Proxy, InvoluteGear + ): + angle_master = fp.master_gear.Placement.Rotation.Angle * sum( + fp.master_gear.Placement.Rotation.Axis + ) dw_master = fp.master_gear.dw dw_slave = fp.slave_gear.dw dist = (dw_master - dw_slave) / 2 @@ -99,25 +129,33 @@ class GearConnector(object): fp.master_gear.teeth, fp.slave_gear.teeth, fp.master_gear.shift, - fp.slave_gear.shift) + fp.slave_gear.shift, + ) mat0 = FreeCAD.Matrix() # unity matrix trans = FreeCAD.Vector(dist) mat0.move(trans) - rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), fp.angle1).toMatrix() + rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), fp.angle1).toMatrix() angle2 = -dw_master / dw_slave * fp.angle1.Value angle4 = -dw_master / dw_slave * np.rad2deg(angle_master) - rot2 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), angle2).toMatrix() - angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180. / fp.slave_gear.teeth - rot3 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), angle3).toMatrix() - rot4 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), -angle4).toMatrix() + rot2 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle2).toMatrix() + angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180.0 / fp.slave_gear.teeth + rot3 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle3).toMatrix() + rot4 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -angle4).toMatrix() mat1 = rot * mat0 * rot2 * rot3 * rot4 mat1.move(fp.master_gear.Placement.Base) fp.slave_gear.Placement = mat1 - if ((isinstance(fp.master_gear.Proxy, InvoluteGear) and isinstance(fp.slave_gear.Proxy, InvoluteGearRack)) - or (isinstance(fp.master_gear.Proxy, CycloidGear) and isinstance(fp.slave_gear.Proxy, CycloidGearRack))): - angle_master = fp.master_gear.Placement.Rotation.Angle * sum(fp.master_gear.Placement.Rotation.Axis) + if ( + isinstance(fp.master_gear.Proxy, InvoluteGear) + and isinstance(fp.slave_gear.Proxy, InvoluteGearRack) + ) or ( + isinstance(fp.master_gear.Proxy, CycloidGear) + and isinstance(fp.slave_gear.Proxy, CycloidGearRack) + ): + angle_master = fp.master_gear.Placement.Rotation.Angle * sum( + fp.master_gear.Placement.Rotation.Axis + ) dw_master = fp.master_gear.dw.Value dw_slave = 0 dist = -(dw_master + dw_slave) / 2 @@ -126,35 +164,36 @@ class GearConnector(object): mat1 = FreeCAD.Matrix() mat1.move(FreeCAD.Vector(0, np.deg2rad(fp.angle1.Value) * dw_master / 2, 0)) mat2 = FreeCAD.Matrix() - mat2.move(FreeCAD.Vector(0, -np.deg2rad(fp.angle2.Value) * dw_master / 2, 0)) - rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), fp.angle1).toMatrix() - mat3 = rot * mat2 *mat1 * mat0 + mat2.move( + FreeCAD.Vector(0, -np.deg2rad(fp.angle2.Value) * dw_master / 2, 0) + ) + rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), fp.angle1).toMatrix() + mat3 = rot * mat2 * mat1 * mat0 mat3.move(fp.master_gear.Placement.Base) fp.slave_gear.Placement = mat3 - if isinstance(fp.master_gear.Proxy, CycloidGear) and isinstance(fp.slave_gear.Proxy, CycloidGear): - angle_master = fp.master_gear.Placement.Rotation.Angle * sum(fp.master_gear.Placement.Rotation.Axis) + if isinstance(fp.master_gear.Proxy, CycloidGear) and isinstance( + fp.slave_gear.Proxy, CycloidGear + ): + angle_master = fp.master_gear.Placement.Rotation.Angle * sum( + fp.master_gear.Placement.Rotation.Axis + ) dw_master = fp.master_gear.dw dw_slave = fp.slave_gear.dw dist = (dw_master + dw_slave) / 2 mat0 = FreeCAD.Matrix() # unity matrix trans = FreeCAD.Vector(dist, 0, 0) mat0.move(trans) - rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), fp.angle1).toMatrix() + rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), fp.angle1).toMatrix() angle2 = dw_master / dw_slave * fp.angle1.Value angle4 = dw_master / dw_slave * np.rad2deg(angle_master) - rot2 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), angle2).toMatrix() - angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180. / fp.slave_gear.teeth - rot3 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), angle3).toMatrix() - rot4 = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), -angle4).toMatrix() + rot2 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle2).toMatrix() + angle3 = abs(fp.slave_gear.teeth % 2 - 1) * 180.0 / fp.slave_gear.teeth + rot3 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), angle3).toMatrix() + rot4 = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -angle4).toMatrix() mat1 = rot * mat0 * rot2 * rot3 * rot4 mat1.move(fp.master_gear.Placement.Base) fp.slave_gear.Placement = mat1 def execute(self, fp): self.onChanged(fp, None) - - - - - diff --git a/freecad/gears/features.py b/freecad/gears/features.py index f97cf44..89acb12 100644 --- a/freecad/gears/features.py +++ b/freecad/gears/features.py @@ -25,31 +25,49 @@ from pygears import __version__ from pygears.involute_tooth import InvoluteTooth, InvoluteRack from pygears.cycloid_tooth import CycloidTooth from pygears.bevel_tooth import BevelTooth -from pygears._functions import rotation3D, rotation, reflection, arc_from_points_and_center +from pygears._functions import ( + rotation3D, + rotation, + reflection, + arc_from_points_and_center, +) import FreeCAD as App import Part -from Part import BSplineCurve, Shape, Wire, Face, makePolygon, \ - makeLoft, BSplineSurface, \ - makePolygon, makeHelix, makeShell, makeSolid, LineSegment +from Part import ( + BSplineCurve, + Shape, + Wire, + Face, + makePolygon, + makeLoft, + BSplineSurface, + makePolygon, + makeHelix, + makeShell, + makeSolid, + LineSegment, +) -__all__ = ["InvoluteGear", - "CycloidGear", - "BevelGear", - "InvoluteGearRack", - "CrownGear", - "WormGear", - "HypoCycloidGear", - "ViewProviderGear"] +__all__ = [ + "InvoluteGear", + "CycloidGear", + "BevelGear", + "InvoluteGearRack", + "CrownGear", + "WormGear", + "HypoCycloidGear", + "ViewProviderGear", +] def fcvec(x): if len(x) == 2: - return(App.Vector(x[0], x[1], 0)) + return App.Vector(x[0], x[1], 0) else: - return(App.Vector(x[0], x[1], x[2])) + return App.Vector(x[0], x[1], x[2]) class ViewProviderGear(object): @@ -59,11 +77,15 @@ class ViewProviderGear(object): self._check_attr() dirname = os.path.dirname(__file__) self.icon_fn = icon_fn or os.path.join(dirname, "icons", "involutegear.svg") - + def _check_attr(self): - ''' Check for missing attributes. ''' + """Check for missing attributes.""" if not hasattr(self, "icon_fn"): - setattr(self, "icon_fn", os.path.join(os.path.dirname(__file__), "icons", "involutegear.svg")) + setattr( + self, + "icon_fn", + os.path.join(os.path.dirname(__file__), "icons", "involutegear.svg"), + ) def attach(self, vobj): self.vobj = vobj @@ -80,9 +102,12 @@ class ViewProviderGear(object): if state and "icon_fn" in state: self.icon_fn = state["icon_fn"] + class BaseGear(object): def __init__(self, obj): - obj.addProperty("App::PropertyString", "version", "version", "freecad.gears-version", 1) + obj.addProperty( + "App::PropertyString", "version", "version", "freecad.gears-version", 1 + ) obj.version = __version__ self.make_attachable(obj) @@ -91,11 +116,11 @@ class BaseGear(object): # aka able to attach parameterically to other objects # cf. https://wiki.freecadweb.org/Scripted_objects_with_attachment if int(App.Version()[1]) >= 19: - obj.addExtension('Part::AttachExtensionPython') + obj.addExtension("Part::AttachExtensionPython") else: - obj.addExtension('Part::AttachExtensionPython', obj) + obj.addExtension("Part::AttachExtensionPython", obj) # unveil the "Placement" property, which seems hidden by default in PartDesign - obj.setEditorMode('Placement', 0) #non-readonly non-hidden + obj.setEditorMode("Placement", 0) # non-readonly non-hidden def execute(self, fp): # checksbackwardcompatibility: @@ -105,9 +130,13 @@ class BaseGear(object): gear_shape = self.generate_gear_shape(fp) if hasattr(fp, "BaseFeature") and fp.BaseFeature != None: # we're inside a PartDesign Body, thus need to fuse with the base feature - gear_shape.Placement = fp.Placement # ensure the gear is placed correctly before fusing + gear_shape.Placement = ( + fp.Placement + ) # ensure the gear is placed correctly before fusing result_shape = fp.BaseFeature.Shape.fuse(gear_shape) - result_shape.transformShape(fp.Placement.inverse().toMatrix(), True) # account for setting fp.Shape below moves the shape to fp.Placement, ignoring its previous placement + result_shape.transformShape( + fp.Placement.inverse().toMatrix(), True + ) # account for setting fp.Shape below moves the shape to fp.Placement, ignoring its previous placement fp.Shape = result_shape else: fp.Shape = gear_shape @@ -133,8 +162,9 @@ class InvoluteGear(BaseGear): super(InvoluteGear, self).__init__(obj) self.involute_tooth = InvoluteTooth() - obj.addProperty("App::PropertyPythonObject", - "gear", "base", "python gear object") + obj.addProperty( + "App::PropertyPythonObject", "gear", "base", "python gear object" + ) self.add_gear_properties(obj) self.add_fillet_properties(obj) @@ -147,16 +177,16 @@ class InvoluteGear(BaseGear): obj.simple = False obj.undercut = False obj.teeth = 15 - obj.module = '1. mm' - obj.shift = 0. - obj.pressure_angle = '20. deg' - obj.beta = '0. deg' - obj.height = '5. mm' + obj.module = "1. mm" + obj.shift = 0.0 + obj.pressure_angle = "20. deg" + obj.beta = "0. deg" + obj.height = "5. mm" obj.clearance = 0.25 - obj.head = 0. + obj.head = 0.0 obj.numpoints = 6 obj.double_helix = False - obj.backlash = '0.00 mm' + obj.backlash = "0.00 mm" obj.reversed_backlash = False obj.properties_from_tool = False obj.head_fillet = 0 @@ -168,62 +198,106 @@ class InvoluteGear(BaseGear): def add_gear_properties(self, obj): obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") obj.addProperty( - "App::PropertyLength", "module", "base", "normal module if properties_from_tool=True, \ - else it's the transverse module.") + "App::PropertyLength", + "module", + "base", + "normal module if properties_from_tool=True, \ + else it's the transverse module.", + ) + obj.addProperty("App::PropertyLength", "height", "base", "height") obj.addProperty( - "App::PropertyLength", "height", "base", "height") - obj.addProperty( - "App::PropertyAngle", "pressure_angle", "involute", "pressure angle") + "App::PropertyAngle", "pressure_angle", "involute", "pressure angle" + ) obj.addProperty("App::PropertyFloat", "shift", "involute", "shift") def add_fillet_properties(self, obj): obj.addProperty("App::PropertyBool", "undercut", "fillets", "undercut") - obj.addProperty("App::PropertyFloat", "head_fillet", "fillets", - "a fillet for the tooth-head, radius = head_fillet x module") - obj.addProperty("App::PropertyFloat", "root_fillet", "fillets", - "a fillet for the tooth-root, radius = root_fillet x module") + obj.addProperty( + "App::PropertyFloat", + "head_fillet", + "fillets", + "a fillet for the tooth-head, radius = head_fillet x module", + ) + obj.addProperty( + "App::PropertyFloat", + "root_fillet", + "fillets", + "a fillet for the tooth-root, radius = root_fillet x module", + ) def add_helical_properties(self, obj): - obj.addProperty("App::PropertyBool", "properties_from_tool", - "helical", "if beta is given and properties_from_tool is enabled, \ - gear parameters are internally recomputed for the rotated gear") + obj.addProperty( + "App::PropertyBool", + "properties_from_tool", + "helical", + "if beta is given and properties_from_tool is enabled, \ + gear parameters are internally recomputed for the rotated gear", + ) obj.addProperty("App::PropertyAngle", "beta", "helical", "beta ") obj.addProperty("App::PropertyBool", "double_helix", "helical", "double helix") def add_computed_properties(self, obj): - obj.addProperty("App::PropertyLength", "da", - "computed", "outside diameter", 1) - obj.addProperty("App::PropertyLength", "df", - "computed", "root diameter", 1) + obj.addProperty("App::PropertyLength", "da", "computed", "outside diameter", 1) + obj.addProperty("App::PropertyLength", "df", "computed", "root diameter", 1) self.add_traverse_module_property(obj) - obj.addProperty("App::PropertyLength", "dw", "computed", "The pitch diameter.", 1) - obj.addProperty("App::PropertyAngle", "angular_backlash", "computed", - "The angle by which this gear can turn without moving the mating gear.") - obj.setExpression('angular_backlash', 'backlash / dw * 360° / pi') # calculate via expression to ease usage for placement - obj.setEditorMode('angular_backlash', 1) # set read-only after setting the expression, else it won't be visible. bug? - obj.addProperty("App::PropertyLength", "transverse_pitch", - "computed", "transverse_pitch", 1) + obj.addProperty( + "App::PropertyLength", "dw", "computed", "The pitch diameter.", 1 + ) + obj.addProperty( + "App::PropertyAngle", + "angular_backlash", + "computed", + "The angle by which this gear can turn without moving the mating gear.", + ) + obj.setExpression( + "angular_backlash", "backlash / dw * 360° / pi" + ) # calculate via expression to ease usage for placement + obj.setEditorMode( + "angular_backlash", 1 + ) # set read-only after setting the expression, else it won't be visible. bug? + obj.addProperty( + "App::PropertyLength", "transverse_pitch", "computed", "transverse_pitch", 1 + ) def add_tolerance_properties(self, obj): - obj.addProperty("App::PropertyLength", "backlash", "tolerance", - "The arc length on the pitch circle by which the tooth thicknes is reduced.") - obj.addProperty("App::PropertyBool", "reversed_backlash", "tolerance", "backlash direction") obj.addProperty( - "App::PropertyFloat", "clearance", "tolerance", "clearance") + "App::PropertyLength", + "backlash", + "tolerance", + "The arc length on the pitch circle by which the tooth thicknes is reduced.", + ) obj.addProperty( - "App::PropertyFloat", "head", "tolerance", "head_value * modul_value = additional length of head") + "App::PropertyBool", "reversed_backlash", "tolerance", "backlash direction" + ) + obj.addProperty("App::PropertyFloat", "clearance", "tolerance", "clearance") + obj.addProperty( + "App::PropertyFloat", + "head", + "tolerance", + "head_value * modul_value = additional length of head", + ) def add_accuracy_properties(self, obj): obj.addProperty("App::PropertyBool", "simple", "accuracy", "simple") - obj.addProperty("App::PropertyInteger", "numpoints", - "accuracy", "number of points for spline") - + obj.addProperty( + "App::PropertyInteger", + "numpoints", + "accuracy", + "number of points for spline", + ) + def add_traverse_module_property(self, obj): - obj.addProperty("App::PropertyLength", "traverse_module", "computed", "traverse module of the generated gear", 1) + obj.addProperty( + "App::PropertyLength", + "traverse_module", + "computed", + "traverse module of the generated gear", + 1, + ) def compute_traverse_properties(self, obj): # traverse_module added recently, if old freecad doc is loaded without it, it will not exist when generate_gear_shape() is called - if not hasattr(obj, 'traverse_module'): + if not hasattr(obj, "traverse_module"): self.add_traverse_module_property(obj) if obj.properties_from_tool: obj.traverse_module = obj.module / np.cos(obj.gear.beta) @@ -241,24 +315,21 @@ class InvoluteGear(BaseGear): obj.gear.z = obj.teeth obj.gear.undercut = obj.undercut obj.gear.shift = obj.shift - obj.gear.pressure_angle = obj.pressure_angle.Value * np.pi / 180. + obj.gear.pressure_angle = obj.pressure_angle.Value * np.pi / 180.0 obj.gear.beta = obj.beta.Value * np.pi / 180 obj.gear.clearance = obj.clearance - obj.gear.backlash = obj.backlash.Value * \ - (-obj.reversed_backlash + 0.5) * 2. + obj.gear.backlash = obj.backlash.Value * (-obj.reversed_backlash + 0.5) * 2.0 obj.gear.head = obj.head obj.gear.properties_from_tool = obj.properties_from_tool obj.gear._update() self.compute_traverse_properties(obj) - if not obj.simple: - pts = obj.gear.points(num=obj.numpoints) rot = rotation(-obj.gear.phipart) rotated_pts = list(map(rot, pts)) - pts.append([pts[-1][-1],rotated_pts[0][0]]) + pts.append([pts[-1][-1], rotated_pts[0][0]]) pts += rotated_pts tooth = points_to_wire(pts) edges = tooth.Edges @@ -266,9 +337,11 @@ class InvoluteGear(BaseGear): # head-fillet: r_head = float(obj.head_fillet * obj.module) r_root = float(obj.root_fillet * obj.module) - if obj.undercut and r_root != 0.: - r_root = 0. - App.Console.PrintWarning("root fillet is not allowed if undercut is computed") + if obj.undercut and r_root != 0.0: + r_root = 0.0 + App.Console.PrintWarning( + "root fillet is not allowed if undercut is computed" + ) if len(tooth.Edges) == 11: pos_head = [1, 3, 9] pos_root = [6, 8] @@ -292,7 +365,7 @@ class InvoluteGear(BaseGear): for pos in pos_root: edges = insert_fillet(edges, pos, r_root) break - edges = edges[edge_range[0]:edge_range[1]] + edges = edges[edge_range[0] : edge_range[1]] edges = [e for e in edges if e is not None] tooth = Wire(edges) @@ -305,7 +378,9 @@ class InvoluteGear(BaseGear): return base.extrude(App.Vector(0, 0, obj.height.Value)) else: twist_angle = obj.height.Value * np.tan(obj.gear.beta) * 2 / obj.gear.d - return helicalextrusion(base, obj.height.Value, twist_angle, obj.double_helix) + return helicalextrusion( + base, obj.height.Value, twist_angle, obj.double_helix + ) else: rw = obj.gear.dw / 2 return Part.makeCylinder(rw, obj.height.Value) @@ -321,16 +396,23 @@ class InternalInvoluteGear(BaseGear): def __init__(self, obj): super(InternalInvoluteGear, self).__init__(obj) self.involute_tooth = InvoluteTooth() - obj.addProperty( - "App::PropertyBool", "simple", "precision", "simple") + obj.addProperty("App::PropertyBool", "simple", "precision", "simple") obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") obj.addProperty( - "App::PropertyLength", "module", "base", "normal module if properties_from_tool=True, \ - else it's the transverse module.") + "App::PropertyLength", + "module", + "base", + "normal module if properties_from_tool=True, \ + else it's the transverse module.", + ) obj.addProperty("App::PropertyLength", "height", "base", "height") obj.addProperty("App::PropertyLength", "thickness", "base", "thickness") - obj.addProperty("App::PropertyInteger", "numpoints", - "accuracy", "number of points for spline") + obj.addProperty( + "App::PropertyInteger", + "numpoints", + "accuracy", + "number of points for spline", + ) obj.addProperty("App::PropertyPythonObject", "gear", "base", "test") self.add_involute_properties(obj) @@ -343,17 +425,17 @@ class InternalInvoluteGear(BaseGear): obj.gear = self.involute_tooth obj.simple = False obj.teeth = 15 - obj.module = '1. mm' - obj.shift = 0. - obj.pressure_angle = '20. deg' - obj.beta = '0. deg' - obj.height = '5. mm' - obj.thickness = '5 mm' + obj.module = "1. mm" + obj.shift = 0.0 + obj.pressure_angle = "20. deg" + obj.beta = "0. deg" + obj.height = "5. mm" + obj.thickness = "5 mm" obj.clearance = 0.25 - obj.head = -0.4 # using head=0 and shift=0.5 may be better, but makes placeing the pinion less intuitive + obj.head = -0.4 # using head=0 and shift=0.5 may be better, but makes placeing the pinion less intuitive obj.numpoints = 6 obj.double_helix = False - obj.backlash = '0.00 mm' + obj.backlash = "0.00 mm" obj.reversed_backlash = False obj.properties_from_tool = False obj.head_fillet = 0 @@ -362,77 +444,114 @@ class InternalInvoluteGear(BaseGear): obj.Proxy = self def add_limiting_diameter_properties(self, obj): - obj.addProperty("App::PropertyLength", "da", - "computed", "inside diameter", 1) - obj.addProperty("App::PropertyLength", "df", - "computed", "root diameter", 1) + obj.addProperty("App::PropertyLength", "da", "computed", "inside diameter", 1) + obj.addProperty("App::PropertyLength", "df", "computed", "root diameter", 1) def add_computed_properties(self, obj): obj.addProperty("App::PropertyLength", "dw", "computed", "The pitch diameter.") - obj.addProperty("App::PropertyAngle", "angular_backlash", "computed", - "The angle by which this gear can turn without moving the mating gear.") - obj.setExpression('angular_backlash', 'backlash / dw * 360° / pi') # calculate via expression to ease usage for placement - obj.setEditorMode('angular_backlash', 1) # set read-only after setting the expression, else it won't be visible. bug? - obj.addProperty("App::PropertyLength", "transverse_pitch", "computed", "transverse_pitch", 1) - obj.addProperty("App::PropertyLength", "outside_diameter", "computed", "Outside diameter", 1) + obj.addProperty( + "App::PropertyAngle", + "angular_backlash", + "computed", + "The angle by which this gear can turn without moving the mating gear.", + ) + obj.setExpression( + "angular_backlash", "backlash / dw * 360° / pi" + ) # calculate via expression to ease usage for placement + obj.setEditorMode( + "angular_backlash", 1 + ) # set read-only after setting the expression, else it won't be visible. bug? + obj.addProperty( + "App::PropertyLength", "transverse_pitch", "computed", "transverse_pitch", 1 + ) + obj.addProperty( + "App::PropertyLength", "outside_diameter", "computed", "Outside diameter", 1 + ) def add_fillet_properties(self, obj): - obj.addProperty("App::PropertyFloat", "head_fillet", "fillets", "a fillet for the tooth-head, radius = head_fillet x module") - obj.addProperty("App::PropertyFloat", "root_fillet", "fillets", "a fillet for the tooth-root, radius = root_fillet x module") + obj.addProperty( + "App::PropertyFloat", + "head_fillet", + "fillets", + "a fillet for the tooth-head, radius = head_fillet x module", + ) + obj.addProperty( + "App::PropertyFloat", + "root_fillet", + "fillets", + "a fillet for the tooth-root, radius = root_fillet x module", + ) def add_tolerance_properties(self, obj): - obj.addProperty("App::PropertyLength", "backlash", "tolerance", - "The arc length on the pitch circle by which the tooth thicknes is reduced.") - obj.addProperty("App::PropertyBool", "reversed_backlash", "tolerance", "backlash direction") - obj.addProperty("App::PropertyFloat", "head", "tolerance", "head_value * modul_value = additional length of head") + obj.addProperty( + "App::PropertyLength", + "backlash", + "tolerance", + "The arc length on the pitch circle by which the tooth thicknes is reduced.", + ) + obj.addProperty( + "App::PropertyBool", "reversed_backlash", "tolerance", "backlash direction" + ) + obj.addProperty( + "App::PropertyFloat", + "head", + "tolerance", + "head_value * modul_value = additional length of head", + ) obj.addProperty("App::PropertyFloat", "clearance", "tolerance", "clearance") def add_involute_properties(self, obj): obj.addProperty("App::PropertyFloat", "shift", "involute", "shift") - obj.addProperty("App::PropertyAngle", "pressure_angle", "involute", "pressure angle") + obj.addProperty( + "App::PropertyAngle", "pressure_angle", "involute", "pressure angle" + ) def add_helical_properties(self, obj): obj.addProperty("App::PropertyAngle", "beta", "helical", "beta ") obj.addProperty("App::PropertyBool", "double_helix", "helical", "double helix") obj.addProperty( - "App::PropertyBool", "properties_from_tool", "helical", "if beta is given and properties_from_tool is enabled, \ - gear parameters are internally recomputed for the rotated gear") + "App::PropertyBool", + "properties_from_tool", + "helical", + "if beta is given and properties_from_tool is enabled, \ + gear parameters are internally recomputed for the rotated gear", + ) def generate_gear_shape(self, fp): fp.gear.double_helix = fp.double_helix fp.gear.m_n = fp.module.Value fp.gear.z = fp.teeth - fp.gear.undercut = False # no undercut for internal gears + fp.gear.undercut = False # no undercut for internal gears fp.gear.shift = fp.shift - fp.gear.pressure_angle = fp.pressure_angle.Value * np.pi / 180. + fp.gear.pressure_angle = fp.pressure_angle.Value * np.pi / 180.0 fp.gear.beta = fp.beta.Value * np.pi / 180 - fp.gear.clearance = fp.head # swap head and clearance to become "internal" - fp.gear.backlash = fp.backlash.Value * \ - (fp.reversed_backlash - 0.5) * 2. # negate "reversed_backslash", for "internal" - fp.gear.head = fp.clearance # swap head and clearance to become "internal" + fp.gear.clearance = fp.head # swap head and clearance to become "internal" + fp.gear.backlash = ( + fp.backlash.Value * (fp.reversed_backlash - 0.5) * 2.0 + ) # negate "reversed_backslash", for "internal" + fp.gear.head = fp.clearance # swap head and clearance to become "internal" fp.gear.properties_from_tool = fp.properties_from_tool fp.gear._update() fp.dw = "{}mm".format(fp.gear.dw) - + # computed properties fp.transverse_pitch = "{}mm".format(fp.gear.pitch) fp.outside_diameter = fp.dw + 2 * fp.thickness # checksbackwardcompatibility: if not "da" in fp.PropertiesList: self.add_limiting_diameter_properties(fp) - fp.da = "{}mm".format(fp.gear.df) # swap addednum and dedendum for "internal" - fp.df = "{}mm".format(fp.gear.da) # swap addednum and dedendum for "internal" + fp.da = "{}mm".format(fp.gear.df) # swap addednum and dedendum for "internal" + fp.df = "{}mm".format(fp.gear.da) # swap addednum and dedendum for "internal" - - outer_circle = Part.Wire(Part.makeCircle(fp.outside_diameter / 2.)) + outer_circle = Part.Wire(Part.makeCircle(fp.outside_diameter / 2.0)) outer_circle.reverse() if not fp.simple: # head-fillet: pts = fp.gear.points(num=fp.numpoints) rot = rotation(-fp.gear.phipart) rotated_pts = list(map(rot, pts)) - pts.append([pts[-1][-1],rotated_pts[0][0]]) + pts.append([pts[-1][-1], rotated_pts[0][0]]) pts += rotated_pts tooth = points_to_wire(pts) r_head = float(fp.root_fillet * fp.module) # reversing head @@ -461,7 +580,7 @@ class InternalInvoluteGear(BaseGear): for pos in pos_root: edges = insert_fillet(edges, pos, r_root) break - edges = edges[edge_range[0]:edge_range[1]] + edges = edges[edge_range[0] : edge_range[1]] edges = [e for e in edges if e is not None] tooth = Wire(edges) @@ -473,9 +592,11 @@ class InternalInvoluteGear(BaseGear): return base.extrude(App.Vector(0, 0, fp.height.Value)) else: twist_angle = fp.height.Value * np.tan(fp.gear.beta) * 2 / fp.gear.d - return helicalextrusion(base, fp.height.Value, twist_angle, fp.double_helix) + return helicalextrusion( + base, fp.height.Value, twist_angle, fp.double_helix + ) else: - inner_circle = Part.Wire(Part.makeCircle(fp.dw / 2.)) + inner_circle = Part.Wire(Part.makeCircle(fp.dw / 2.0)) inner_circle.reverse() base = Face([outer_circle, inner_circle]) return base.extrude(App.Vector(0, 0, fp.height.Value)) @@ -494,17 +615,17 @@ class InvoluteGearRack(BaseGear): def __init__(self, obj): super(InvoluteGearRack, self).__init__(obj) self.involute_rack = InvoluteRack() - obj.addProperty("App::PropertyInteger", - "teeth", "base", "number of teeth") + obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") + obj.addProperty("App::PropertyLength", "height", "base", "height") + obj.addProperty("App::PropertyLength", "module", "base", "module") + obj.addProperty("App::PropertyLength", "thickness", "base", "thickness") obj.addProperty( - "App::PropertyLength", "height", "base", "height") - obj.addProperty( - "App::PropertyLength", "module", "base", "module") - obj.addProperty( - "App::PropertyLength", "thickness", "base", "thickness") - obj.addProperty( - "App::PropertyBool", "simplified", "precision", "if enabled the rack is drawn with a constant number of \ - teeth to avoid topologic renaming.") + "App::PropertyBool", + "simplified", + "precision", + "if enabled the rack is drawn with a constant number of \ + teeth to avoid topologic renaming.", + ) obj.addProperty("App::PropertyPythonObject", "rack", "base", "test") self.add_helical_properties(obj) @@ -514,13 +635,13 @@ class InvoluteGearRack(BaseGear): self.add_fillet_properties(obj) obj.rack = self.involute_rack obj.teeth = 15 - obj.module = '1. mm' - obj.pressure_angle = '20. deg' - obj.height = '5. mm' - obj.thickness = '5 mm' - obj.beta = '0. deg' + obj.module = "1. mm" + obj.pressure_angle = "20. deg" + obj.height = "5. mm" + obj.thickness = "5 mm" + obj.beta = "0. deg" obj.clearance = 0.25 - obj.head = 0. + obj.head = 0.0 obj.properties_from_tool = False obj.add_endings = True obj.simplified = False @@ -529,39 +650,70 @@ class InvoluteGearRack(BaseGear): def add_helical_properties(self, obj): obj.addProperty( - "App::PropertyBool", "properties_from_tool", "helical", "if beta is given and properties_from_tool is enabled, \ - gear parameters are internally recomputed for the rotated gear") - obj.addProperty( - "App::PropertyAngle", "beta", "helical", "beta ") - obj.addProperty( - "App::PropertyBool", "double_helix", "helical", "double helix") + "App::PropertyBool", + "properties_from_tool", + "helical", + "if beta is given and properties_from_tool is enabled, \ + gear parameters are internally recomputed for the rotated gear", + ) + obj.addProperty("App::PropertyAngle", "beta", "helical", "beta ") + obj.addProperty("App::PropertyBool", "double_helix", "helical", "double helix") def add_computed_properties(self, obj): - obj.addProperty("App::PropertyLength", "transverse_pitch", - "computed", "pitch in the transverse plane", 1) - obj.addProperty("App::PropertyBool", "add_endings", "base", "if enabled the total length of the rack is teeth x pitch, \ - otherwise the rack starts with a tooth-flank") + obj.addProperty( + "App::PropertyLength", + "transverse_pitch", + "computed", + "pitch in the transverse plane", + 1, + ) + obj.addProperty( + "App::PropertyBool", + "add_endings", + "base", + "if enabled the total length of the rack is teeth x pitch, \ + otherwise the rack starts with a tooth-flank", + ) def add_tolerance_properties(self, obj): obj.addProperty( - "App::PropertyFloat", "head", "tolerance", "head * module = additional length of head") + "App::PropertyFloat", + "head", + "tolerance", + "head * module = additional length of head", + ) obj.addProperty( - "App::PropertyFloat", "clearance", "tolerance", "clearance * module = additional length of root") + "App::PropertyFloat", + "clearance", + "tolerance", + "clearance * module = additional length of root", + ) def add_involute_properties(self, obj): obj.addProperty( - "App::PropertyAngle", "pressure_angle", "involute", "pressure angle") + "App::PropertyAngle", "pressure_angle", "involute", "pressure angle" + ) def add_fillet_properties(self, obj): - obj.addProperty("App::PropertyFloat", "head_fillet", "fillets", "a fillet for the tooth-head, radius = head_fillet x module") - obj.addProperty("App::PropertyFloat", "root_fillet", "fillets", "a fillet for the tooth-root, radius = root_fillet x module") + obj.addProperty( + "App::PropertyFloat", + "head_fillet", + "fillets", + "a fillet for the tooth-head, radius = head_fillet x module", + ) + obj.addProperty( + "App::PropertyFloat", + "root_fillet", + "fillets", + "a fillet for the tooth-root, radius = root_fillet x module", + ) def generate_gear_shape(self, obj): obj.rack.m = obj.module.Value obj.rack.z = obj.teeth - obj.rack.pressure_angle = obj.pressure_angle.Value * np.pi / 180. + obj.rack.pressure_angle = obj.pressure_angle.Value * np.pi / 180.0 obj.rack.thickness = obj.thickness.Value - obj.rack.beta = obj.beta.Value * np.pi / 180. + obj.rack.beta = obj.beta.Value * np.pi / 180.0 obj.rack.head = obj.head # checksbackwardcompatibility: if "clearance" in obj.PropertiesList: @@ -578,7 +730,7 @@ class InvoluteGearRack(BaseGear): t = obj.thickness.Value c = obj.clearance h = obj.head - alpha = obj.pressure_angle.Value * np.pi / 180. + alpha = obj.pressure_angle.Value * np.pi / 180.0 head_fillet = obj.head_fillet root_fillet = obj.root_fillet x1 = -m * np.pi / 2 @@ -635,8 +787,8 @@ class InvoluteGearRack(BaseGear): p_start = np.array(teeth[0].Edges[0].firstVertex().Point[:-1]) p_end = np.array(teeth[-1].Edges[-1].lastVertex().Point[:-1]) - p_start_1 = p_start - np.array([obj.thickness.Value, 0.]) - p_end_1 = p_end - np.array([obj.thickness.Value, 0.]) + p_start_1 = p_start - np.array([obj.thickness.Value, 0.0]) + p_end_1 = p_end - np.array([obj.thickness.Value, 0.0]) line6 = [p_start, p_start_1] line7 = [p_start_1, p_end_1] @@ -650,20 +802,22 @@ class InvoluteGearRack(BaseGear): return pol elif obj.beta.Value == 0: face = Face(Wire(pol)) - return face.extrude(fcvec([0., 0., obj.height.Value])) + return face.extrude(fcvec([0.0, 0.0, obj.height.Value])) elif obj.double_helix: - beta = obj.beta.Value * np.pi / 180. + beta = obj.beta.Value * np.pi / 180.0 pol2 = Part.Wire(pol) pol2.translate( - fcvec([0., np.tan(beta) * obj.height.Value / 2, obj.height.Value / 2])) + fcvec([0.0, np.tan(beta) * obj.height.Value / 2, obj.height.Value / 2]) + ) pol3 = Part.Wire(pol) - pol3.translate(fcvec([0., 0., obj.height.Value])) + pol3.translate(fcvec([0.0, 0.0, obj.height.Value])) return makeLoft([pol, pol2, pol3], True, True) else: - beta = obj.beta.Value * np.pi / 180. + beta = obj.beta.Value * np.pi / 180.0 pol2 = Part.Wire(pol) pol2.translate( - fcvec([0., np.tan(beta) * obj.height.Value, obj.height.Value])) + fcvec([0.0, np.tan(beta) * obj.height.Value, obj.height.Value]) + ) return makeLoft([pol, pol2], True) @@ -673,18 +827,23 @@ class CycloidGearRack(BaseGear): def __init__(self, obj): super(CycloidGearRack, self).__init__(obj) - obj.addProperty("App::PropertyInteger", - "teeth", "base", "number of teeth") + obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") + obj.addProperty("App::PropertyLength", "height", "base", "height") + obj.addProperty("App::PropertyLength", "thickness", "base", "thickness") + obj.addProperty("App::PropertyLength", "module", "involute", "module") obj.addProperty( - "App::PropertyLength", "height", "base", "height") + "App::PropertyBool", + "simplified", + "precision", + "if enabled the rack is drawn with a constant number of \ + teeth to avoid topologic renaming.", + ) obj.addProperty( - "App::PropertyLength", "thickness", "base", "thickness") - obj.addProperty( - "App::PropertyLength", "module", "involute", "module") - obj.addProperty( - "App::PropertyBool", "simplified", "precision", "if enabled the rack is drawn with a constant number of \ - teeth to avoid topologic renaming.") - obj.addProperty("App::PropertyInteger", "numpoints", "accuracy", "number of points for spline") + "App::PropertyInteger", + "numpoints", + "accuracy", + "number of points for spline", + ) obj.addProperty("App::PropertyPythonObject", "rack", "base", "test") self.add_helical_properties(obj) @@ -693,14 +852,14 @@ class CycloidGearRack(BaseGear): self.add_cycloid_properties(obj) self.add_fillet_properties(obj) obj.teeth = 15 - obj.module = '1. mm' + obj.module = "1. mm" obj.inner_diameter = 7.5 obj.outer_diameter = 7.5 - obj.height = '5. mm' - obj.thickness = '5 mm' - obj.beta = '0. deg' + obj.height = "5. mm" + obj.thickness = "5 mm" + obj.beta = "0. deg" obj.clearance = 0.25 - obj.head = 0. + obj.head = 0.0 obj.add_endings = True obj.simplified = False obj.numpoints = 15 @@ -708,31 +867,67 @@ class CycloidGearRack(BaseGear): obj.Proxy = self def add_helical_properties(self, obj): - obj.addProperty( - "App::PropertyAngle", "beta", "helical", "beta ") - obj.addProperty( - "App::PropertyBool", "double_helix", "helical", "double helix") + obj.addProperty("App::PropertyAngle", "beta", "helical", "beta ") + obj.addProperty("App::PropertyBool", "double_helix", "helical", "double helix") def add_computed_properties(self, obj): - obj.addProperty("App::PropertyLength", "transverse_pitch", - "computed", "pitch in the transverse plane", 1) - obj.addProperty("App::PropertyBool", "add_endings", "base", "if enabled the total length of the rack is teeth x pitch, \ - otherwise the rack starts with a tooth-flank") + obj.addProperty( + "App::PropertyLength", + "transverse_pitch", + "computed", + "pitch in the transverse plane", + 1, + ) + obj.addProperty( + "App::PropertyBool", + "add_endings", + "base", + "if enabled the total length of the rack is teeth x pitch, \ + otherwise the rack starts with a tooth-flank", + ) def add_tolerance_properties(self, obj): obj.addProperty( - "App::PropertyFloat", "head", "tolerance", "head * module = additional length of head") + "App::PropertyFloat", + "head", + "tolerance", + "head * module = additional length of head", + ) obj.addProperty( - "App::PropertyFloat", "clearance", "tolerance", "clearance * module = additional length of root") + "App::PropertyFloat", + "clearance", + "tolerance", + "clearance * module = additional length of root", + ) def add_cycloid_properties(self, obj): - obj.addProperty("App::PropertyFloat", "inner_diameter", "cycloid", "inner_diameter divided by module (hypocycloid)") - obj.addProperty("App::PropertyFloat", "outer_diameter", "cycloid", "outer_diameter divided by module (epicycloid)") + obj.addProperty( + "App::PropertyFloat", + "inner_diameter", + "cycloid", + "inner_diameter divided by module (hypocycloid)", + ) + obj.addProperty( + "App::PropertyFloat", + "outer_diameter", + "cycloid", + "outer_diameter divided by module (epicycloid)", + ) def add_fillet_properties(self, obj): - obj.addProperty("App::PropertyFloat", "head_fillet", "fillets", "a fillet for the tooth-head, radius = head_fillet x module") - obj.addProperty("App::PropertyFloat", "root_fillet", "fillets", "a fillet for the tooth-root, radius = root_fillet x module") - + obj.addProperty( + "App::PropertyFloat", + "head_fillet", + "fillets", + "a fillet for the tooth-head, radius = head_fillet x module", + ) + obj.addProperty( + "App::PropertyFloat", + "root_fillet", + "fillets", + "a fillet for the tooth-root, radius = root_fillet x module", + ) + def generate_gear_shape(self, obj): numpoints = obj.numpoints m = obj.module.Value @@ -757,8 +952,8 @@ class CycloidGearRack(BaseGear): mirror = reflection(0) points_1 = mirror(points)[::-1] line_1 = [points[-1], points_1[0]] - line_2 = [points_1[-1], np.array([-(1 + c) * m , m * np.pi / 2])] - line_0 = [np.array([-(1 + c) * m , -m * np.pi / 2]), points[0]] + line_2 = [points_1[-1], np.array([-(1 + c) * m, m * np.pi / 2])] + line_0 = [np.array([-(1 + c) * m, -m * np.pi / 2]), points[0]] tooth = points_to_wire([line_0, points, line_1, points_1, line_2]) edges = tooth.Edges @@ -790,8 +985,8 @@ class CycloidGearRack(BaseGear): p_start = np.array(teeth[0].Edges[0].firstVertex().Point[:-1]) p_end = np.array(teeth[-1].Edges[-1].lastVertex().Point[:-1]) - p_start_1 = p_start - np.array([obj.thickness.Value, 0.]) - p_end_1 = p_end - np.array([obj.thickness.Value, 0.]) + p_start_1 = p_start - np.array([obj.thickness.Value, 0.0]) + p_end_1 = p_end - np.array([obj.thickness.Value, 0.0]) line6 = [p_start, p_start_1] line7 = [p_start_1, p_end_1] @@ -805,26 +1000,24 @@ class CycloidGearRack(BaseGear): return pol elif obj.beta.Value == 0: face = Face(Wire(pol)) - return face.extrude(fcvec([0., 0., obj.height.Value])) + return face.extrude(fcvec([0.0, 0.0, obj.height.Value])) elif obj.double_helix: - beta = obj.beta.Value * np.pi / 180. + beta = obj.beta.Value * np.pi / 180.0 pol2 = Part.Wire(pol) pol2.translate( - fcvec([0., np.tan(beta) * obj.height.Value / 2, obj.height.Value / 2])) + fcvec([0.0, np.tan(beta) * obj.height.Value / 2, obj.height.Value / 2]) + ) pol3 = Part.Wire(pol) - pol3.translate(fcvec([0., 0., obj.height.Value])) + pol3.translate(fcvec([0.0, 0.0, obj.height.Value])) return makeLoft([pol, pol2, pol3], True, True) else: - beta = obj.beta.Value * np.pi / 180. + beta = obj.beta.Value * np.pi / 180.0 pol2 = Part.Wire(pol) pol2.translate( - fcvec([0., np.tan(beta) * obj.height.Value, obj.height.Value])) + fcvec([0.0, np.tan(beta) * obj.height.Value, obj.height.Value]) + ) return makeLoft([pol, pol2], True) - - - - def __getstate__(self): return None @@ -835,32 +1028,49 @@ class CycloidGearRack(BaseGear): class CrownGear(BaseGear): def __init__(self, obj): super(CrownGear, self).__init__(obj) - obj.addProperty("App::PropertyInteger", - "teeth", "base", "number of teeth") - obj.addProperty("App::PropertyInteger", - "other_teeth", "base", "number of teeth of other gear") + obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") + obj.addProperty( + "App::PropertyInteger", + "other_teeth", + "base", + "number of teeth of other gear", + ) obj.addProperty("App::PropertyLength", "module", "base", "module") obj.addProperty("App::PropertyLength", "height", "base", "height") obj.addProperty("App::PropertyLength", "thickness", "base", "thickness") - obj.addProperty("App::PropertyAngle", "pressure_angle", "involute", "pressure angle") + obj.addProperty( + "App::PropertyAngle", "pressure_angle", "involute", "pressure angle" + ) self.add_accuracy_properties(obj) obj.teeth = 15 obj.other_teeth = 15 - obj.module = '1. mm' - obj.pressure_angle = '20. deg' - obj.height = '2. mm' - obj.thickness = '5 mm' + obj.module = "1. mm" + obj.pressure_angle = "20. deg" + obj.height = "2. mm" + obj.thickness = "5 mm" obj.num_profiles = 4 obj.preview_mode = True self.obj = obj obj.Proxy = self - App.Console.PrintMessage("Gear module: Crown gear created, preview_mode = true for improved performance. "\ - "Set preview_mode property to false when ready to cut teeth.") + App.Console.PrintMessage( + "Gear module: Crown gear created, preview_mode = true for improved performance. " + "Set preview_mode property to false when ready to cut teeth." + ) def add_accuracy_properties(self, obj): - obj.addProperty("App::PropertyInteger", "num_profiles", "accuracy", "number of profiles used for loft") - obj.addProperty("App::PropertyBool", "preview_mode", "accuracy", "if true no boolean operation is done") + obj.addProperty( + "App::PropertyInteger", + "num_profiles", + "accuracy", + "number of profiles used for loft", + ) + obj.addProperty( + "App::PropertyBool", + "preview_mode", + "accuracy", + "if true no boolean operation is done", + ) def profile(self, m, r, r0, t_c, t_i, alpha_w, y0, y1, y2): r_ew = m * t_i / 2 @@ -872,8 +1082,7 @@ class CrownGear(BaseGear): alpha = np.arccos(r0 / r * np.cos(alpha_w)) # 3: winkel phi bei senkrechter stellung eines zahns: - phi = np.pi / t_i / 2 + (alpha - alpha_w) + \ - (np.tan(alpha_w) - np.tan(alpha)) + phi = np.pi / t_i / 2 + (alpha - alpha_w) + (np.tan(alpha_w) - np.tan(alpha)) # 4: Position des Eingriffspunktes: x_c = r_e * np.sin(phi) @@ -890,23 +1099,18 @@ class CrownGear(BaseGear): x2 = x_c - c r *= np.cos(phi) - pts = [ - [-x1, r, y0], - [-x2, r, y0 - y1 - y2], - [x2, r, y0 - y1 - y2], - [x1, r, y0] - ] + pts = [[-x1, r, y0], [-x2, r, y0 - y1 - y2], [x2, r, y0 - y1 - y2], [x1, r, y0]] pts.append(pts[0]) return pts def generate_gear_shape(self, fp): inner_diameter = fp.module.Value * fp.teeth outer_diameter = inner_diameter + fp.height.Value * 2 - inner_circle = Part.Wire(Part.makeCircle(inner_diameter / 2.)) - outer_circle = Part.Wire(Part.makeCircle(outer_diameter / 2.)) + inner_circle = Part.Wire(Part.makeCircle(inner_diameter / 2.0)) + outer_circle = Part.Wire(Part.makeCircle(outer_diameter / 2.0)) inner_circle.reverse() face = Part.Face([outer_circle, inner_circle]) - solid = face.extrude(App.Vector([0., 0., -fp.thickness.Value])) + solid = face.extrude(App.Vector([0.0, 0.0, -fp.thickness.Value])) if fp.preview_mode: return solid @@ -937,23 +1141,25 @@ class CrownGear(BaseGear): return solid.cut(cut_shapes) - class CycloidGear(BaseGear): """FreeCAD gear""" def __init__(self, obj): super(CycloidGear, self).__init__(obj) self.cycloid_tooth = CycloidTooth() - obj.addProperty("App::PropertyInteger", - "teeth", "base", "number of teeth") - obj.addProperty( - "App::PropertyLength", "module", "base", "module") - obj.addProperty( - "App::PropertyLength", "height", "base", "height") + obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") + obj.addProperty("App::PropertyLength", "module", "base", "module") + obj.addProperty("App::PropertyLength", "height", "base", "height") - obj.addProperty("App::PropertyInteger", "numpoints", "accuracy", "number of points for spline") - obj.addProperty("App::PropertyPythonObject", "gear", - "base", "the python object") + obj.addProperty( + "App::PropertyInteger", + "numpoints", + "accuracy", + "number of points for spline", + ) + obj.addProperty( + "App::PropertyPythonObject", "gear", "base", "the python object" + ) self.add_helical_properties(obj) self.add_fillet_properties(obj) @@ -962,14 +1168,16 @@ class CycloidGear(BaseGear): self.add_computed_properties(obj) obj.gear = self.cycloid_tooth obj.teeth = 15 - obj.module = '1. mm' - obj.setExpression('inner_diameter', 'teeth / 2') # teeth/2 makes the hypocycloid a straight line to the center - obj.outer_diameter = 7.5 # we don't know the mating gear, so we just set the default to mesh with our default - obj.beta = '0. deg' - obj.height = '5. mm' + obj.module = "1. mm" + obj.setExpression( + "inner_diameter", "teeth / 2" + ) # teeth/2 makes the hypocycloid a straight line to the center + obj.outer_diameter = 7.5 # we don't know the mating gear, so we just set the default to mesh with our default + obj.beta = "0. deg" + obj.height = "5. mm" obj.clearance = 0.25 obj.numpoints = 15 - obj.backlash = '0.00 mm' + obj.backlash = "0.00 mm" obj.double_helix = False obj.head = 0 obj.head_fillet = 0 @@ -981,27 +1189,68 @@ class CycloidGear(BaseGear): obj.addProperty("App::PropertyAngle", "beta", "helical", "beta") def add_fillet_properties(self, obj): - obj.addProperty("App::PropertyFloat", "head_fillet", "fillets", "a fillet for the tooth-head, radius = head_fillet x module") - obj.addProperty("App::PropertyFloat", "root_fillet", "fillets", "a fillet for the tooth-root, radius = root_fillet x module") + obj.addProperty( + "App::PropertyFloat", + "head_fillet", + "fillets", + "a fillet for the tooth-head, radius = head_fillet x module", + ) + obj.addProperty( + "App::PropertyFloat", + "root_fillet", + "fillets", + "a fillet for the tooth-root, radius = root_fillet x module", + ) def add_tolerance_properties(self, obj): obj.addProperty("App::PropertyFloat", "clearance", "tolerance", "clearance") - obj.addProperty("App::PropertyLength", "backlash", "tolerance", - "The arc length on the pitch circle by which the tooth thicknes is reduced.") - obj.addProperty("App::PropertyFloat", "head", "tolerance", "head_value * modul_value = additional length of head") + obj.addProperty( + "App::PropertyLength", + "backlash", + "tolerance", + "The arc length on the pitch circle by which the tooth thicknes is reduced.", + ) + obj.addProperty( + "App::PropertyFloat", + "head", + "tolerance", + "head_value * modul_value = additional length of head", + ) def add_cycloid_properties(self, obj): - obj.addProperty("App::PropertyFloat", "inner_diameter", "cycloid", "inner_diameter divided by module (hypocycloid)") - obj.addProperty("App::PropertyFloat", "outer_diameter", "cycloid", "outer_diameter divided by module (epicycloid)") + obj.addProperty( + "App::PropertyFloat", + "inner_diameter", + "cycloid", + "inner_diameter divided by module (hypocycloid)", + ) + obj.addProperty( + "App::PropertyFloat", + "outer_diameter", + "cycloid", + "outer_diameter divided by module (epicycloid)", + ) def add_computed_properties(self, obj): obj.addProperty("App::PropertyLength", "dw", "computed", "The pitch diameter.") - obj.setExpression('dw', 'teeth * module') # calculate via expression to ease usage for placement - obj.setEditorMode('dw', 1) # set read-only after setting the expression, else it won't be visible. bug? - obj.addProperty("App::PropertyAngle", "angular_backlash", "computed", - "The angle by which this gear can turn without moving the mating gear.") - obj.setExpression('angular_backlash', 'backlash / dw * 360° / pi') # calculate via expression to ease usage for placement - obj.setEditorMode('angular_backlash', 1) # set read-only after setting the expression, else it won't be visible. bug? + obj.setExpression( + "dw", "teeth * module" + ) # calculate via expression to ease usage for placement + obj.setEditorMode( + "dw", 1 + ) # set read-only after setting the expression, else it won't be visible. bug? + obj.addProperty( + "App::PropertyAngle", + "angular_backlash", + "computed", + "The angle by which this gear can turn without moving the mating gear.", + ) + obj.setExpression( + "angular_backlash", "backlash / dw * 360° / pi" + ) # calculate via expression to ease usage for placement + obj.setEditorMode( + "angular_backlash", 1 + ) # set read-only after setting the expression, else it won't be visible. bug? def generate_gear_shape(self, fp): fp.gear.m = fp.module.Value @@ -1017,7 +1266,7 @@ class CycloidGear(BaseGear): pts = fp.gear.points(num=fp.numpoints) rot = rotation(-fp.gear.phipart) rotated_pts = list(map(rot, pts)) - pts.append([pts[-1][-1],rotated_pts[0][0]]) + pts.append([pts[-1][-1], rotated_pts[0][0]]) pts += rotated_pts tooth = points_to_wire(pts) edges = tooth.Edges @@ -1035,7 +1284,7 @@ class CycloidGear(BaseGear): for pos in pos_root: edges = insert_fillet(edges, pos, r_root) - edges = edges[edge_range[0]:edge_range[1]] + edges = edges[edge_range[0] : edge_range[1]] edges = [e for e in edges if e is not None] tooth = Wire(edges) @@ -1047,16 +1296,17 @@ class CycloidGear(BaseGear): if fp.beta.Value == 0: return base.extrude(App.Vector(0, 0, fp.height.Value)) else: - twist_angle = fp.height.Value * np.tan(fp.beta.Value * np.pi / 180) * 2 / fp.gear.d + twist_angle = ( + fp.height.Value * np.tan(fp.beta.Value * np.pi / 180) * 2 / fp.gear.d + ) return helicalextrusion(base, fp.height.Value, twist_angle, fp.double_helix) - class BevelGear(BaseGear): """parameters: - pressure_angle: pressureangle, 10-30° - pitch_angle: cone angle, 0 < pitch_angle < pi/4 + pressure_angle: pressureangle, 10-30° + pitch_angle: cone angle, 0 < pitch_angle < pi/4 """ def __init__(self, obj): @@ -1065,32 +1315,65 @@ class BevelGear(BaseGear): obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") obj.addProperty("App::PropertyLength", "height", "base", "height") obj.addProperty("App::PropertyAngle", "pitch_angle", "involute", "pitch_angle") - obj.addProperty("App::PropertyAngle", "pressure_angle", "involute_parameter", "pressure_angle") + obj.addProperty( + "App::PropertyAngle", + "pressure_angle", + "involute_parameter", + "pressure_angle", + ) obj.addProperty("App::PropertyLength", "module", "base", "module") obj.addProperty("App::PropertyFloat", "clearance", "tolerance", "clearance") - obj.addProperty("App::PropertyInteger", "numpoints", "precision", "number of points for spline") - obj.addProperty("App::PropertyBool", "reset_origin", "base", "if value is true the gears outer face will match the z=0 plane") - obj.addProperty("App::PropertyLength", "backlash", "tolerance", - "The arc length on the pitch circle by which the tooth thicknes is reduced.") + obj.addProperty( + "App::PropertyInteger", + "numpoints", + "precision", + "number of points for spline", + ) + obj.addProperty( + "App::PropertyBool", + "reset_origin", + "base", + "if value is true the gears outer face will match the z=0 plane", + ) + obj.addProperty( + "App::PropertyLength", + "backlash", + "tolerance", + "The arc length on the pitch circle by which the tooth thicknes is reduced.", + ) obj.addProperty("App::PropertyPythonObject", "gear", "base", "test") - obj.addProperty("App::PropertyAngle", "beta","helical", "angle used for spiral bevel-gears") + obj.addProperty( + "App::PropertyAngle", "beta", "helical", "angle used for spiral bevel-gears" + ) obj.addProperty("App::PropertyLength", "dw", "computed", "The pitch diameter.") - obj.setExpression('dw', 'teeth * module') # calculate via expression to ease usage for placement - obj.setEditorMode('dw', 1) # set read-only after setting the expression, else it won't be visible. bug? - obj.addProperty("App::PropertyAngle", "angular_backlash", "computed", - "The angle by which this gear can turn without moving the mating gear.") - obj.setExpression('angular_backlash', 'backlash / dw * 360° / pi') # calculate via expression to ease usage for placement - obj.setEditorMode('angular_backlash', 1) # set read-only after setting the expression, else it won't be visible. bug? + obj.setExpression( + "dw", "teeth * module" + ) # calculate via expression to ease usage for placement + obj.setEditorMode( + "dw", 1 + ) # set read-only after setting the expression, else it won't be visible. bug? + obj.addProperty( + "App::PropertyAngle", + "angular_backlash", + "computed", + "The angle by which this gear can turn without moving the mating gear.", + ) + obj.setExpression( + "angular_backlash", "backlash / dw * 360° / pi" + ) # calculate via expression to ease usage for placement + obj.setEditorMode( + "angular_backlash", 1 + ) # set read-only after setting the expression, else it won't be visible. bug? obj.gear = self.bevel_tooth - obj.module = '1. mm' + obj.module = "1. mm" obj.teeth = 15 - obj.pressure_angle = '20. deg' - obj.pitch_angle = '45. deg' - obj.height = '5. mm' + obj.pressure_angle = "20. deg" + obj.pitch_angle = "45. deg" + obj.height = "5. mm" obj.numpoints = 6 - obj.backlash = '0.00 mm' + obj.backlash = "0.00 mm" obj.clearance = 0.1 - obj.beta = '0 deg' + obj.beta = "0 deg" obj.reset_origin = True self.obj = obj obj.Proxy = self @@ -1098,14 +1381,17 @@ class BevelGear(BaseGear): def generate_gear_shape(self, fp): fp.gear.z = fp.teeth fp.gear.module = fp.module.Value - fp.gear.pressure_angle = (90 - fp.pressure_angle.Value) * np.pi / 180. + fp.gear.pressure_angle = (90 - fp.pressure_angle.Value) * np.pi / 180.0 fp.gear.pitch_angle = fp.pitch_angle.Value * np.pi / 180 max_height = fp.gear.module * fp.teeth / 2 / np.tan(fp.gear.pitch_angle) if fp.height >= max_height: - App.Console.PrintWarning("height must be smaller than {}".format(max_height)) + App.Console.PrintWarning( + "height must be smaller than {}".format(max_height) + ) fp.gear.backlash = fp.backlash.Value - scale = fp.module.Value * fp.gear.z / 2 / \ - np.tan(fp.pitch_angle.Value * np.pi / 180) + scale = ( + fp.module.Value * fp.gear.z / 2 / np.tan(fp.pitch_angle.Value * np.pi / 180) + ) fp.gear.clearance = fp.clearance / scale fp.gear._update() pts = list(fp.gear.points(num=fp.numpoints)) @@ -1123,7 +1409,7 @@ class BevelGear(BaseGear): if not "version" in fp.PropertiesList: scale_0 = scale - fp.height.Value / 2 scale_1 = scale + fp.height.Value / 2 - else: # starting with version 0.0.2 + else: # starting with version 0.0.2 scale_0 = scale - fp.height.Value scale_1 = scale if fp.beta.Value == 0: @@ -1134,11 +1420,17 @@ class BevelGear(BaseGear): # beta_i = (scale_i - scale_0) * fp.beta.Value * np.pi / 180 # rot = rotation3D(beta_i) # points = [rot(pt) * scale_i for pt in pts] - angle = fp.beta.Value * np.pi / 180. * \ - np.sin(np.pi / 4) / \ - np.sin(fp.pitch_angle.Value * np.pi / 180.) - points = [np.array([self.spherical_rot(p, angle) - for p in scale_i * pt]) for pt in pts] + angle = ( + fp.beta.Value + * np.pi + / 180.0 + * np.sin(np.pi / 4) + / np.sin(fp.pitch_angle.Value * np.pi / 180.0) + ) + points = [ + np.array([self.spherical_rot(p, angle) for p in scale_i * pt]) + for pt in pts + ] wires.append(make_bspline_wire(points)) shape = makeLoft(wires, True) if fp.reset_origin: @@ -1151,16 +1443,28 @@ class BevelGear(BaseGear): def create_tooth(self): w = [] - scal1 = self.obj.m.Value * self.obj.gear.z / 2 / np.tan( - self.obj.pitch_angle.Value * np.pi / 180) - self.obj.height.Value / 2 - scal2 = self.obj.m.Value * self.obj.gear.z / 2 / np.tan( - self.obj.pitch_angle.Value * np.pi / 180) + self.obj.height.Value / 2 + scal1 = ( + self.obj.m.Value + * self.obj.gear.z + / 2 + / np.tan(self.obj.pitch_angle.Value * np.pi / 180) + - self.obj.height.Value / 2 + ) + scal2 = ( + self.obj.m.Value + * self.obj.gear.z + / 2 + / np.tan(self.obj.pitch_angle.Value * np.pi / 180) + + self.obj.height.Value / 2 + ) s = [scal1, scal2] pts = self.obj.gear.points(num=self.obj.numpoints) for j, pos in enumerate(s): w1 = [] - def scale(x): return fcvec(x * pos) + def scale(x): + return fcvec(x * pos) + for i in pts: i_scale = list(map(scale, i)) w1.append(i_scale) @@ -1178,7 +1482,6 @@ class BevelGear(BaseGear): return rotation3D(new_phi)(point) - class WormGear(BaseGear): """FreeCAD gear rack""" @@ -1186,19 +1489,33 @@ class WormGear(BaseGear): def __init__(self, obj): super(WormGear, self).__init__(obj) obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") - obj.addProperty( "App::PropertyLength", "module", "base", "module") + obj.addProperty("App::PropertyLength", "module", "base", "module") obj.addProperty("App::PropertyLength", "height", "base", "height") - obj.addProperty("App::PropertyLength", 'diameter', "base", "diameter") + obj.addProperty("App::PropertyLength", "diameter", "base", "diameter") obj.addProperty("App::PropertyAngle", "beta", "computed", "beta ", 1) - obj.addProperty("App::PropertyAngle", "pressure_angle", "involute", "pressure angle") - obj.addProperty("App::PropertyBool", "reverse_pitch", "base", "reverse rotation of helix") - obj.addProperty("App::PropertyFloat", "head", "tolerance", "head * module = additional length of head") - obj.addProperty("App::PropertyFloat", "clearance", "tolerance", "clearance * module = additional length of root") + obj.addProperty( + "App::PropertyAngle", "pressure_angle", "involute", "pressure angle" + ) + obj.addProperty( + "App::PropertyBool", "reverse_pitch", "base", "reverse rotation of helix" + ) + obj.addProperty( + "App::PropertyFloat", + "head", + "tolerance", + "head * module = additional length of head", + ) + obj.addProperty( + "App::PropertyFloat", + "clearance", + "tolerance", + "clearance * module = additional length of root", + ) obj.teeth = 3 - obj.module = '1. mm' - obj.pressure_angle = '20. deg' - obj.height = '5. mm' - obj.diameter = '5. mm' + obj.module = "1. mm" + obj.pressure_angle = "20. deg" + obj.height = "5. mm" + obj.diameter = "5. mm" obj.clearance = 0.25 obj.head = 0 obj.reverse_pitch = False @@ -1234,13 +1551,18 @@ class WormGear(BaseGear): x = r * np.cos(phi) y = r * np.sin(phi) z = 0 * y - return np.array([x, y, z]). T + return np.array([x, y, z]).T # create a circle from phi=0 to phi_1 with r_1 phi_0 = 2 * z_0 / m / t phi_1 = 2 * z_1 / m / t - c1 = Part.makeCircle(r_1, App.Vector(0, 0, 0), - App.Vector(0, 0, 1), np.rad2deg(phi_0), np.rad2deg(phi_1)) + c1 = Part.makeCircle( + r_1, + App.Vector(0, 0, 0), + App.Vector(0, 0, 1), + np.rad2deg(phi_0), + np.rad2deg(phi_1), + ) # create first bspline z_values = np.linspace(z_1, z_2, 10) @@ -1253,8 +1575,13 @@ class WormGear(BaseGear): # create circle from phi_2 to phi_3 phi_2 = 2 * z_2 / m / t phi_3 = 2 * z_3 / m / t - c2 = Part.makeCircle(r_2, App.Vector(0, 0, 0), App.Vector( - 0, 0, 1), np.rad2deg(phi_2), np.rad2deg(phi_3)) + c2 = Part.makeCircle( + r_2, + App.Vector(0, 0, 0), + App.Vector(0, 0, 1), + np.rad2deg(phi_2), + np.rad2deg(phi_3), + ) # create second bspline z_values = np.linspace(z_3, z_4, 10) @@ -1276,68 +1603,123 @@ class WormGear(BaseGear): if h == 0: return full_wire else: - shape = helicalextrusion(Face(full_wire), - h, - h * np.tan(beta) * 2 / d) + shape = helicalextrusion(Face(full_wire), h, h * np.tan(beta) * 2 / d) return shape - class TimingGear(BaseGear): """FreeCAD gear rack""" + data = { - "gt2": { - 'pitch': 2.0, 'u': 0.254, 'h': 0.75, - 'H': 1.38, 'r0': 0.555, 'r1': 1.0, - 'rs': 0.15, 'offset': 0.40 - }, - "gt3": { - 'pitch': 3.0, 'u': 0.381, 'h': 1.14, - 'H': 2.40, 'r0': 0.85, 'r1': 1.52, - 'rs': 0.25, 'offset': 0.61 - }, - "gt5": { - 'pitch': 5.0, 'u': 0.5715, 'h': 1.93, - 'H': 3.81, 'r0': 1.44, 'r1': 2.57, - 'rs': 0.416, 'offset': 1.03 - }, - "gt8": { - 'pitch': 8.0, 'u': 0.9144, 'h': 3.088, - 'H': 6.096, 'r0': 2.304, 'r1': 4.112, - 'rs': 0.6656, 'offset': 1.648 - }, - "htd3": { - 'pitch': 3.0, 'u': 0.381, 'h': 1.21, - 'H': 2.40, 'r0': 0.89, 'r1': 0.89, - 'rs': 0.26, 'offset': 0.0 - }, - "htd5": { - 'pitch': 5.0, 'u': 0.5715, 'h': 2.06, - 'H': 3.80, 'r0': 1.49, 'r1': 1.49, - 'rs': 0.43, 'offset': 0.0 - }, - "htd8": { - 'pitch': 8.0, 'u': 0.686, 'h': 3.45, - 'H': 6.00, 'r0': 2.46, 'r1': 2.46, - 'rs': 0.70, 'offset': 0.0 - } - } + "gt2": { + "pitch": 2.0, + "u": 0.254, + "h": 0.75, + "H": 1.38, + "r0": 0.555, + "r1": 1.0, + "rs": 0.15, + "offset": 0.40, + }, + "gt3": { + "pitch": 3.0, + "u": 0.381, + "h": 1.14, + "H": 2.40, + "r0": 0.85, + "r1": 1.52, + "rs": 0.25, + "offset": 0.61, + }, + "gt5": { + "pitch": 5.0, + "u": 0.5715, + "h": 1.93, + "H": 3.81, + "r0": 1.44, + "r1": 2.57, + "rs": 0.416, + "offset": 1.03, + }, + "gt8": { + "pitch": 8.0, + "u": 0.9144, + "h": 3.088, + "H": 6.096, + "r0": 2.304, + "r1": 4.112, + "rs": 0.6656, + "offset": 1.648, + }, + "htd3": { + "pitch": 3.0, + "u": 0.381, + "h": 1.21, + "H": 2.40, + "r0": 0.89, + "r1": 0.89, + "rs": 0.26, + "offset": 0.0, + }, + "htd5": { + "pitch": 5.0, + "u": 0.5715, + "h": 2.06, + "H": 3.80, + "r0": 1.49, + "r1": 1.49, + "rs": 0.43, + "offset": 0.0, + }, + "htd8": { + "pitch": 8.0, + "u": 0.686, + "h": 3.45, + "H": 6.00, + "r0": 2.46, + "r1": 2.46, + "rs": 0.70, + "offset": 0.0, + }, + } def __init__(self, obj): super(TimingGear, self).__init__(obj) obj.addProperty("App::PropertyInteger", "teeth", "base", "number of teeth") - obj.addProperty( "App::PropertyEnumeration", "type", "base", "type of timing-gear") - obj.addProperty( "App::PropertyLength", "height", "base", "height") - obj.addProperty( "App::PropertyLength", "pitch", "computed", "pitch of gear", 1) - obj.addProperty( "App::PropertyLength", "h", "computed", "radial height of teeth", 1) - obj.addProperty( "App::PropertyLength", "u", "computed", "radial difference between pitch diameter and head of gear", 1) - obj.addProperty( "App::PropertyLength", "r0", "computed", "radius of first arc", 1) - obj.addProperty( "App::PropertyLength", "r1", "computed", "radius of second arc", 1) - obj.addProperty( "App::PropertyLength", "rs", "computed", "radius of third arc", 1) - obj.addProperty( "App::PropertyLength", "offset", "computed", "x-offset of second arc-midpoint", 1) + obj.addProperty( + "App::PropertyEnumeration", "type", "base", "type of timing-gear" + ) + obj.addProperty("App::PropertyLength", "height", "base", "height") + obj.addProperty("App::PropertyLength", "pitch", "computed", "pitch of gear", 1) + obj.addProperty( + "App::PropertyLength", "h", "computed", "radial height of teeth", 1 + ) + obj.addProperty( + "App::PropertyLength", + "u", + "computed", + "radial difference between pitch diameter and head of gear", + 1, + ) + obj.addProperty( + "App::PropertyLength", "r0", "computed", "radius of first arc", 1 + ) + obj.addProperty( + "App::PropertyLength", "r1", "computed", "radius of second arc", 1 + ) + obj.addProperty( + "App::PropertyLength", "rs", "computed", "radius of third arc", 1 + ) + obj.addProperty( + "App::PropertyLength", + "offset", + "computed", + "x-offset of second arc-midpoint", + 1, + ) obj.teeth = 15 - obj.type = ['gt2', 'gt3', 'gt5', 'gt8', 'htd3', 'htd5', 'htd8'] - obj.height = '5. mm' + obj.type = ["gt2", "gt3", "gt5", "gt8", "htd3", "htd5", "htd8"] + obj.height = "5. mm" self.obj = obj obj.Proxy = self @@ -1358,74 +1740,117 @@ class TimingGear(BaseGear): offset = fp.offset = gt_data["offset"] arcs = [] - if offset == 0.: - phi5 = np.pi / fp.teeth - ref = reflection(-phi5 - np.pi / 2.) - rp = pitch * fp.teeth / np.pi / 2. - u - - m_34 = np.array([-(r_12 + r_34), rp - h + r_12]) - x2 = np.array([-r_12, m_34[1]]) - x4 = np.array([m_34[0], m_34[1] + r_34]) - x6 = ref(x4) + if offset == 0.0: + phi5 = np.pi / fp.teeth + ref = reflection(-phi5 - np.pi / 2.0) + rp = pitch * fp.teeth / np.pi / 2.0 - u - mir = np.array([-1., 1.]) - xn2 = mir * x2 - xn4 = mir * x4 - mn_34 = mir * m_34 + m_34 = np.array([-(r_12 + r_34), rp - h + r_12]) + x2 = np.array([-r_12, m_34[1]]) + x4 = np.array([m_34[0], m_34[1] + r_34]) + x6 = ref(x4) - arcs.append(part_arc_from_points_and_center(xn4, xn2, mn_34).toShape()) - arcs.append(Part.Arc(App.Vector(*xn2, 0.), App.Vector(0, rp - h, 0.), App.Vector(*x2, 0.)).toShape()) - arcs.append(part_arc_from_points_and_center(x2, x4, m_34).toShape()) - arcs.append(part_arc_from_points_and_center(x4, x6, np.array([0. ,0.])).toShape()) + mir = np.array([-1.0, 1.0]) + xn2 = mir * x2 + xn4 = mir * x4 + mn_34 = mir * m_34 + + arcs.append(part_arc_from_points_and_center(xn4, xn2, mn_34).toShape()) + arcs.append( + Part.Arc( + App.Vector(*xn2, 0.0), + App.Vector(0, rp - h, 0.0), + App.Vector(*x2, 0.0), + ).toShape() + ) + arcs.append(part_arc_from_points_and_center(x2, x4, m_34).toShape()) + arcs.append( + part_arc_from_points_and_center(x4, x6, np.array([0.0, 0.0])).toShape() + ) else: - phi_12 = np.arctan(np.sqrt(1. / (((r_12 - r_23) / offset) ** 2 - 1))) - rp = pitch * fp.teeth / np.pi / 2. - r4 = r5 = rp - u + phi_12 = np.arctan(np.sqrt(1.0 / (((r_12 - r_23) / offset) ** 2 - 1))) + rp = pitch * fp.teeth / np.pi / 2.0 + r4 = r5 = rp - u - m_12 = np.array([0., r5 - h + r_12]) - m_23 = np.array([offset, offset / np.tan(phi_12) + m_12[1]]) - m_23y = m_23[1] + m_12 = np.array([0.0, r5 - h + r_12]) + m_23 = np.array([offset, offset / np.tan(phi_12) + m_12[1]]) + m_23y = m_23[1] - # solving for phi4: - # sympy.solve( - # ((r5 - r_34) * sin(phi4) + offset) ** 2 + \ - # ((r5 - r_34) * cos(phi4) - m_23y) ** 2 - \ - # ((r_34 + r_23) ** 2), phi4) + # solving for phi4: + # sympy.solve( + # ((r5 - r_34) * sin(phi4) + offset) ** 2 + \ + # ((r5 - r_34) * cos(phi4) - m_23y) ** 2 - \ + # ((r_34 + r_23) ** 2), phi4) - phi4 = 2*np.arctan((-2*offset*r5 + 2*offset*r_34 + np.sqrt(-m_23y**4 - 2*m_23y**2*offset**2 + \ - 2*m_23y**2*r5**2 - 4*m_23y**2*r5*r_34 + 2*m_23y**2*r_23**2 + \ - 4*m_23y**2*r_23*r_34 + 4*m_23y**2*r_34**2 - offset**4 + 2*offset**2*r5**2 - \ - 4*offset**2*r5*r_34 + 2*offset**2*r_23**2 + 4*offset**2*r_23*r_34 + 4*offset**2*r_34**2 - \ - r5**4 + 4*r5**3*r_34 + 2*r5**2*r_23**2 + 4*r5**2*r_23*r_34 - \ - 4*r5**2*r_34**2 - 4*r5*r_23**2*r_34 - 8*r5*r_23*r_34**2 - r_23**4 - \ - 4*r_23**3*r_34 - 4*r_23**2*r_34**2))/(m_23y**2 + 2*m_23y*r5 - \ - 2*m_23y*r_34 + offset**2 + r5**2 - 2*r5*r_34 - r_23**2 - 2*r_23*r_34)) + phi4 = 2 * np.arctan( + ( + -2 * offset * r5 + + 2 * offset * r_34 + + np.sqrt( + -(m_23y**4) + - 2 * m_23y**2 * offset**2 + + 2 * m_23y**2 * r5**2 + - 4 * m_23y**2 * r5 * r_34 + + 2 * m_23y**2 * r_23**2 + + 4 * m_23y**2 * r_23 * r_34 + + 4 * m_23y**2 * r_34**2 + - offset**4 + + 2 * offset**2 * r5**2 + - 4 * offset**2 * r5 * r_34 + + 2 * offset**2 * r_23**2 + + 4 * offset**2 * r_23 * r_34 + + 4 * offset**2 * r_34**2 + - r5**4 + + 4 * r5**3 * r_34 + + 2 * r5**2 * r_23**2 + + 4 * r5**2 * r_23 * r_34 + - 4 * r5**2 * r_34**2 + - 4 * r5 * r_23**2 * r_34 + - 8 * r5 * r_23 * r_34**2 + - r_23**4 + - 4 * r_23**3 * r_34 + - 4 * r_23**2 * r_34**2 + ) + ) + / ( + m_23y**2 + + 2 * m_23y * r5 + - 2 * m_23y * r_34 + + offset**2 + + r5**2 + - 2 * r5 * r_34 + - r_23**2 + - 2 * r_23 * r_34 + ) + ) - phi5 = np.pi / fp.teeth + phi5 = np.pi / fp.teeth - m_34 = (r5 - r_34) * np.array([-np.sin(phi4), np.cos(phi4)]) + m_34 = (r5 - r_34) * np.array([-np.sin(phi4), np.cos(phi4)]) - x2 = np.array([-r_12 * np.sin(phi_12), m_12[1] - r_12 * np.cos(phi_12)]) - x3 = m_34 + r_34 / (r_34 + r_23) * (m_23 - m_34) - x4 = r4 * np.array([-np.sin(phi4), np.cos(phi4)]) + x2 = np.array([-r_12 * np.sin(phi_12), m_12[1] - r_12 * np.cos(phi_12)]) + x3 = m_34 + r_34 / (r_34 + r_23) * (m_23 - m_34) + x4 = r4 * np.array([-np.sin(phi4), np.cos(phi4)]) - ref = reflection(-phi5 - np.pi / 2) - x6 = ref(x4) - mir = np.array([-1., 1.]) - xn2 = mir * x2 - xn3 = mir * x3 - xn4 = mir * x4 + ref = reflection(-phi5 - np.pi / 2) + x6 = ref(x4) + mir = np.array([-1.0, 1.0]) + xn2 = mir * x2 + xn3 = mir * x3 + xn4 = mir * x4 - mn_34 = mir * m_34 - mn_23 = mir * m_23 + mn_34 = mir * m_34 + mn_23 = mir * m_23 - arcs.append(part_arc_from_points_and_center(xn4, xn3, mn_34).toShape()) - arcs.append(part_arc_from_points_and_center(xn3, xn2, mn_23).toShape()) - arcs.append(part_arc_from_points_and_center(xn2, x2, m_12).toShape()) - arcs.append(part_arc_from_points_and_center(x2, x3, m_23).toShape()) - arcs.append(part_arc_from_points_and_center(x3, x4, m_34).toShape()) - arcs.append(part_arc_from_points_and_center(x4, x6, np.array([0. ,0.])).toShape()) + arcs.append(part_arc_from_points_and_center(xn4, xn3, mn_34).toShape()) + arcs.append(part_arc_from_points_and_center(xn3, xn2, mn_23).toShape()) + arcs.append(part_arc_from_points_and_center(xn2, x2, m_12).toShape()) + arcs.append(part_arc_from_points_and_center(x2, x3, m_23).toShape()) + arcs.append(part_arc_from_points_and_center(x3, x4, m_34).toShape()) + arcs.append( + part_arc_from_points_and_center(x4, x6, np.array([0.0, 0.0])).toShape() + ) wire = Part.Wire(arcs) wires = [wire] @@ -1442,24 +1867,40 @@ class TimingGear(BaseGear): return Part.Face(wi).extrude(App.Vector(0, 0, fp.height)) - class LanternGear(BaseGear): def __init__(self, obj): super(LanternGear, self).__init__(obj) - obj.addProperty("App::PropertyInteger", "teeth", "gear_parameter", "number of teeth") + obj.addProperty( + "App::PropertyInteger", "teeth", "gear_parameter", "number of teeth" + ) obj.addProperty("App::PropertyLength", "module", "base", "module") - obj.addProperty("App::PropertyLength", "bolt_radius", "base", "the bolt radius of the rack/chain") + obj.addProperty( + "App::PropertyLength", + "bolt_radius", + "base", + "the bolt radius of the rack/chain", + ) obj.addProperty("App::PropertyLength", "height", "base", "height") - obj.addProperty("App::PropertyInteger", "num_profiles", "accuracy", "number of profiles used for loft") - obj.addProperty("App::PropertyFloat", "head", "tolerance", "head * module = additional length of head") + obj.addProperty( + "App::PropertyInteger", + "num_profiles", + "accuracy", + "number of profiles used for loft", + ) + obj.addProperty( + "App::PropertyFloat", + "head", + "tolerance", + "head * module = additional length of head", + ) obj.teeth = 15 - obj.module = '1. mm' - obj.bolt_radius = '1 mm' - - obj.height = '5. mm' + obj.module = "1. mm" + obj.bolt_radius = "1 mm" + + obj.height = "5. mm" obj.num_profiles = 10 - + self.obj = obj obj.Proxy = self @@ -1473,13 +1914,25 @@ class LanternGear(BaseGear): phi_max = (r_r + np.sqrt(r_max**2 - r_0**2)) / r_0 def find_phi_min(phi_min): - return r_0*(phi_min**2*r_0 - 2*phi_min*r_0*np.sin(phi_min) - \ - 2*phi_min*r_r - 2*r_0*np.cos(phi_min) + 2*r_0 + 2*r_r*np.sin(phi_min)) + return r_0 * ( + phi_min**2 * r_0 + - 2 * phi_min * r_0 * np.sin(phi_min) + - 2 * phi_min * r_r + - 2 * r_0 * np.cos(phi_min) + + 2 * r_0 + + 2 * r_r * np.sin(phi_min) + ) + try: import scipy.optimize - phi_min = scipy.optimize.root(find_phi_min, (phi_max + r_r / r_0 * 4) / 5).x[0] # , r_r / r_0, phi_max) + + phi_min = scipy.optimize.root( + find_phi_min, (phi_max + r_r / r_0 * 4) / 5 + ).x[0] # , r_r / r_0, phi_max) except ImportError: - App.Console.PrintWarning("scipy not available. Can't compute numerical root. Leads to a wrong bolt-radius") + App.Console.PrintWarning( + "scipy not available. Can't compute numerical root. Leads to a wrong bolt-radius" + ) phi_min = r_r / r_0 # phi_min = 0 # r_r / r_0 @@ -1493,21 +1946,25 @@ class LanternGear(BaseGear): bsp_1.interpolate(list(map(fcvec, xy1))) w_1 = bsp_1.toShape() - xy2 = xy1 * np.array([1., -1.]) + xy2 = xy1 * np.array([1.0, -1.0]) p_2 = xy2[0] p_2_end = xy2[-1] bsp_2 = BSplineCurve() bsp_2.interpolate(list(map(fcvec, xy2))) w_2 = bsp_2.toShape() - p_12 = np.array([r_0 - r_r, 0.]) + p_12 = np.array([r_0 - r_r, 0.0]) - arc = Part.Arc(App.Vector(*p_1, 0.), App.Vector(*p_12, 0.), App.Vector(*p_2, 0.)).toShape() + arc = Part.Arc( + App.Vector(*p_1, 0.0), App.Vector(*p_12, 0.0), App.Vector(*p_2, 0.0) + ).toShape() rot = rotation(-np.pi * 2 / teeth) p_3 = rot(np.array([p_2_end]))[0] # l = Part.LineSegment(fcvec(p_1_end), fcvec(p_3)).toShape() - l = part_arc_from_points_and_center(p_1_end, p_3, np.array([0., 0.])).toShape() + l = part_arc_from_points_and_center( + p_1_end, p_3, np.array([0.0, 0.0]) + ).toShape() w = Part.Wire([w_2, arc, w_1, l]) wires = [w] @@ -1526,86 +1983,141 @@ class LanternGear(BaseGear): class HypoCycloidGear(BaseGear): """parameters: - pressure_angle: pressureangle, 10-30° - pitch_angle: cone angle, 0 < pitch_angle < pi/4 + pressure_angle: pressureangle, 10-30° + pitch_angle: cone angle, 0 < pitch_angle < pi/4 """ def __init__(self, obj): super(HypoCycloidGear, self).__init__(obj) - obj.addProperty("App::PropertyFloat","pin_circle_radius", "gear_parameter","Pin ball circle radius(overrides Tooth Pitch") - obj.addProperty("App::PropertyFloat","roller_diameter", "gear_parameter","Roller Diameter") - obj.addProperty("App::PropertyFloat","eccentricity", "gear_parameter","Eccentricity") - obj.addProperty("App::PropertyAngle","pressure_angle_lim", "gear_parameter","Pressure angle limit") - obj.addProperty("App::PropertyFloat","pressure_angle_offset", "gear_parameter","Offset in pressure angle") - obj.addProperty("App::PropertyInteger","teeth_number", "gear_parameter","Number of teeth in Cam") - obj.addProperty("App::PropertyInteger","segment_count", "gear_parameter","Number of points used for spline interpolation") - obj.addProperty("App::PropertyLength","hole_radius", "gear_parameter","Center hole's radius") + obj.addProperty( + "App::PropertyFloat", + "pin_circle_radius", + "gear_parameter", + "Pin ball circle radius(overrides Tooth Pitch", + ) + obj.addProperty( + "App::PropertyFloat", "roller_diameter", "gear_parameter", "Roller Diameter" + ) + obj.addProperty( + "App::PropertyFloat", "eccentricity", "gear_parameter", "Eccentricity" + ) + obj.addProperty( + "App::PropertyAngle", + "pressure_angle_lim", + "gear_parameter", + "Pressure angle limit", + ) + obj.addProperty( + "App::PropertyFloat", + "pressure_angle_offset", + "gear_parameter", + "Offset in pressure angle", + ) + obj.addProperty( + "App::PropertyInteger", + "teeth_number", + "gear_parameter", + "Number of teeth in Cam", + ) + obj.addProperty( + "App::PropertyInteger", + "segment_count", + "gear_parameter", + "Number of points used for spline interpolation", + ) + obj.addProperty( + "App::PropertyLength", + "hole_radius", + "gear_parameter", + "Center hole's radius", + ) + obj.addProperty( + "App::PropertyBool", "show_pins", "Pins", "Create pins in place" + ) + obj.addProperty("App::PropertyLength", "pin_height", "Pins", "height") + obj.addProperty( + "App::PropertyBool", + "center_pins", + "Pins", + "Center pin Z axis to generated disks", + ) - obj.addProperty("App::PropertyBool", "show_pins", "Pins", "Create pins in place") - obj.addProperty("App::PropertyLength","pin_height", "Pins", "height") - obj.addProperty("App::PropertyBool", "center_pins", "Pins", "Center pin Z axis to generated disks") - - obj.addProperty("App::PropertyBool", "show_disk0", "Disks", "Show main cam disk") - obj.addProperty("App::PropertyBool", "show_disk1", "Disks", "Show another reversed cam disk on top") - obj.addProperty("App::PropertyLength","disk_height", "Disks", "height") + obj.addProperty( + "App::PropertyBool", "show_disk0", "Disks", "Show main cam disk" + ) + obj.addProperty( + "App::PropertyBool", + "show_disk1", + "Disks", + "Show another reversed cam disk on top", + ) + obj.addProperty("App::PropertyLength", "disk_height", "Disks", "height") obj.pin_circle_radius = 66 obj.roller_diameter = 3 obj.eccentricity = 1.5 - obj.pressure_angle_lim = '50.0 deg' + obj.pressure_angle_lim = "50.0 deg" obj.pressure_angle_offset = 0.01 obj.teeth_number = 42 obj.segment_count = 42 - obj.hole_radius = '30. mm' + obj.hole_radius = "30. mm" - obj.show_pins = True - obj.pin_height = '20. mm' - obj.center_pins= True + obj.show_pins = True + obj.pin_height = "20. mm" + obj.center_pins = True obj.show_disk0 = True obj.show_disk1 = True - obj.disk_height= '10. mm' + obj.disk_height = "10. mm" self.obj = obj obj.Proxy = self - def to_polar(self,x, y): - return (x**2 + y**2)**0.5, math.atan2(y, x) + def to_polar(self, x, y): + return (x**2 + y**2) ** 0.5, math.atan2(y, x) - def to_rect(self,r, a): - return r*math.cos(a), r*math.sin(a) + def to_rect(self, r, a): + return r * math.cos(a), r * math.sin(a) - def calcyp(self,p,a,e,n): - return math.atan(math.sin(n*a)/(math.cos(n*a)+(n*p)/(e*(n+1)))) + def calcyp(self, p, a, e, n): + return math.atan(math.sin(n * a) / (math.cos(n * a) + (n * p) / (e * (n + 1)))) - def calc_x(self,p,d,e,n,a): - return (n*p)*math.cos(a)+e*math.cos((n+1)*a)-d/2*math.cos(self.calcyp(p,a,e,n)+a) + def calc_x(self, p, d, e, n, a): + return ( + (n * p) * math.cos(a) + + e * math.cos((n + 1) * a) + - d / 2 * math.cos(self.calcyp(p, a, e, n) + a) + ) - def calc_y(self,p,d,e,n,a): - return (n*p)*math.sin(a)+e*math.sin((n+1)*a)-d/2*math.sin(self.calcyp(p,a,e,n)+a) + def calc_y(self, p, d, e, n, a): + return ( + (n * p) * math.sin(a) + + e * math.sin((n + 1) * a) + - d / 2 * math.sin(self.calcyp(p, a, e, n) + a) + ) - def calc_pressure_angle(self,p,d,n,a): + def calc_pressure_angle(self, p, d, n, a): ex = 2**0.5 - r3 = p*n - rg = r3/ex - pp = rg * (ex**2 + 1 - 2*ex*math.cos(a))**0.5 - d/2 - return math.asin( (r3*math.cos(a)-rg)/(pp+d/2))*180/math.pi + r3 = p * n + rg = r3 / ex + pp = rg * (ex**2 + 1 - 2 * ex * math.cos(a)) ** 0.5 - d / 2 + return math.asin((r3 * math.cos(a) - rg) / (pp + d / 2)) * 180 / math.pi - def calc_pressure_limit(self,p,d,e,n,a): + def calc_pressure_limit(self, p, d, e, n, a): ex = 2**0.5 - r3 = p*n - rg = r3/ex - q = (r3**2 + rg**2 - 2*r3*rg*math.cos(a))**0.5 - x = rg - e + (q-d/2)*(r3*math.cos(a)-rg)/q - y = (q-d/2)*r3*math.sin(a)/q - return (x**2 + y**2)**0.5 + r3 = p * n + rg = r3 / ex + q = (r3**2 + rg**2 - 2 * r3 * rg * math.cos(a)) ** 0.5 + x = rg - e + (q - d / 2) * (r3 * math.cos(a) - rg) / q + y = (q - d / 2) * r3 * math.sin(a) / q + return (x**2 + y**2) ** 0.5 - def check_limit(self,x,y,maxrad,minrad,offset): + def check_limit(self, x, y, maxrad, minrad, offset): r, a = self.to_polar(x, y) if (r > maxrad) or (r < minrad): - r = r - offset - x, y = self.to_rect(r, a) + r = r - offset + x, y = self.to_rect(r, a) return x, y def generate_gear_shape(self, fp): @@ -1613,83 +2125,87 @@ class HypoCycloidGear(BaseGear): d = fp.roller_diameter e = fp.eccentricity n = fp.teeth_number - p = b/n + p = b / n s = fp.segment_count ang = fp.pressure_angle_lim c = fp.pressure_angle_offset - q = 2*math.pi/float(s) + q = 2 * math.pi / float(s) # Find the pressure angle limit circles minAngle = -1.0 maxAngle = -1.0 for i in range(0, 180): - x = self.calc_pressure_angle(p, d, n, i * math.pi / 180.) - if ( x < ang) and (minAngle < 0): + x = self.calc_pressure_angle(p, d, n, i * math.pi / 180.0) + if (x < ang) and (minAngle < 0): minAngle = float(i) if (x < -ang) and (maxAngle < 0): - maxAngle = float(i-1) + maxAngle = float(i - 1) - minRadius = self.calc_pressure_limit(p, d, e, n, minAngle * math.pi / 180.) - maxRadius = self.calc_pressure_limit(p, d, e, n, maxAngle * math.pi / 180.) + minRadius = self.calc_pressure_limit(p, d, e, n, minAngle * math.pi / 180.0) + maxRadius = self.calc_pressure_limit(p, d, e, n, maxAngle * math.pi / 180.0) # unused # Wire(Part.makeCircle(minRadius,App.Vector(-e, 0, 0))) # Wire(Part.makeCircle(maxRadius,App.Vector(-e, 0, 0))) App.Console.PrintMessage("Generating cam disk\r\n") - #generate the cam profile - note: shifted in -x by eccentricicy amount - i=0 - x = self.calc_x(p, d, e, n, q*i / float(n)) - y = self.calc_y(p, d, e, n, q*i / n) - x, y = self.check_limit(x,y,maxRadius,minRadius,c) - points = [App.Vector(x-e, y, 0)] - for i in range(0,s): - x = self.calc_x(p, d, e, n, q*(i+1) / n) - y = self.calc_y(p, d, e, n, q*(i+1) / n) + # generate the cam profile - note: shifted in -x by eccentricicy amount + i = 0 + x = self.calc_x(p, d, e, n, q * i / float(n)) + y = self.calc_y(p, d, e, n, q * i / n) + x, y = self.check_limit(x, y, maxRadius, minRadius, c) + points = [App.Vector(x - e, y, 0)] + for i in range(0, s): + x = self.calc_x(p, d, e, n, q * (i + 1) / n) + y = self.calc_y(p, d, e, n, q * (i + 1) / n) x, y = self.check_limit(x, y, maxRadius, minRadius, c) - points.append([x-e, y, 0]) + points.append([x - e, y, 0]) wi = make_bspline_wire([points]) wires = [] - mat= App.Matrix() - mat.move(App.Vector(e, 0., 0.)) + mat = App.Matrix() + mat.move(App.Vector(e, 0.0, 0.0)) mat.rotateZ(2 * np.pi / n) - mat.move(App.Vector(-e, 0., 0.)) + mat.move(App.Vector(-e, 0.0, 0.0)) for _ in range(n): wi = wi.transformGeometry(mat) wires.append(wi) cam = Face(Wire(wires)) - #add a circle in the center of the cam + # add a circle in the center of the cam if fp.hole_radius.Value: - centerCircle = Face(Wire(Part.makeCircle(fp.hole_radius.Value, App.Vector(-e, 0, 0)))) + centerCircle = Face( + Wire(Part.makeCircle(fp.hole_radius.Value, App.Vector(-e, 0, 0))) + ) cam = cam.cut(centerCircle) to_be_fused = [] - if fp.show_disk0==True: - if fp.disk_height.Value==0: + if fp.show_disk0 == True: + if fp.disk_height.Value == 0: to_be_fused.append(cam) else: to_be_fused.append(cam.extrude(App.Vector(0, 0, fp.disk_height.Value))) - #secondary cam disk - if fp.show_disk1==True: + # secondary cam disk + if fp.show_disk1 == True: App.Console.PrintMessage("Generating secondary cam disk\r\n") second_cam = cam.copy() - mat= App.Matrix() + mat = App.Matrix() mat.rotateZ(np.pi) mat.move(App.Vector(-e, 0, 0)) - if n%2 == 0: - mat.rotateZ(np.pi/n) + if n % 2 == 0: + mat.rotateZ(np.pi / n) mat.move(App.Vector(e, 0, 0)) second_cam = second_cam.transformGeometry(mat) - if fp.disk_height.Value==0: + if fp.disk_height.Value == 0: to_be_fused.append(second_cam) else: - to_be_fused.append(second_cam.extrude(App.Vector(0, 0, -fp.disk_height.Value))) + to_be_fused.append( + second_cam.extrude(App.Vector(0, 0, -fp.disk_height.Value)) + ) - #pins - if fp.show_pins==True: + # pins + if fp.show_pins == True: App.Console.PrintMessage("Generating pins\r\n") pins = [] for i in range(0, n + 1): @@ -1699,20 +2215,19 @@ class HypoCycloidGear(BaseGear): pins = Face(pins) - z_offset = -fp.pin_height.Value / 2; - - if fp.center_pins==True: - if fp.show_disk0==True and fp.show_disk1==False: - z_offset += fp.disk_height.Value / 2; - elif fp.show_disk0==False and fp.show_disk1==True: - z_offset += -fp.disk_height.Value / 2; - #extrude - if z_offset!=0: + z_offset = -fp.pin_height.Value / 2 + if fp.center_pins == True: + if fp.show_disk0 == True and fp.show_disk1 == False: + z_offset += fp.disk_height.Value / 2 + elif fp.show_disk0 == False and fp.show_disk1 == True: + z_offset += -fp.disk_height.Value / 2 + # extrude + if z_offset != 0: pins.translate(App.Vector(0, 0, z_offset)) - if fp.pin_height!=0: + if fp.pin_height != 0: pins = pins.extrude(App.Vector(0, 0, fp.pin_height.Value)) - to_be_fused.append(pins); + to_be_fused.append(pins) if to_be_fused: return Part.makeCompound(to_be_fused) @@ -1720,7 +2235,9 @@ class HypoCycloidGear(BaseGear): def part_arc_from_points_and_center(p_1, p_2, m): p_1, p_12, p_2 = arc_from_points_and_center(p_1, p_2, m) - return Part.Arc(App.Vector(*p_1, 0.), App.Vector(*p_12, 0.), App.Vector(*p_2, 0.)) + return Part.Arc( + App.Vector(*p_1, 0.0), App.Vector(*p_12, 0.0), App.Vector(*p_2, 0.0) + ) def helicalextrusion(face, height, angle, double_helix=False): @@ -1733,24 +2250,30 @@ def helicalextrusion(face, height, angle, double_helix=False): returns a solid """ pitch = height * 2 * np.pi / abs(angle) - radius = 10.0 # as we are only interested in the "twist", we take an arbitrary constant here + radius = 10.0 # as we are only interested in the "twist", we take an arbitrary constant here cone_angle = 0 direction = bool(angle < 0) if double_helix: spine = Part.makeHelix(pitch, height / 2.0, radius, cone_angle, direction) spine.translate(App.Vector(0, 0, height / 2.0)) - face = face.translated(App.Vector(0, 0, height / 2.0)) # don't transform our argument + face = face.translated( + App.Vector(0, 0, height / 2.0) + ) # don't transform our argument else: spine = Part.makeHelix(pitch, height, radius, cone_angle, direction) + def make_pipe(path, profile): """ returns (shell, last_wire) """ mkPS = Part.BRepOffsetAPI.MakePipeShell(path) - mkPS.setFrenetMode(True) # otherwise, the profile's normal would follow the path + mkPS.setFrenetMode( + True + ) # otherwise, the profile's normal would follow the path mkPS.add(profile, False, False) mkPS.build() return (mkPS.shape(), mkPS.lastShape()) + shell_faces = [] top_wires = [] for wire in face.Wires: @@ -1770,12 +2293,13 @@ def helicalextrusion(face, height, angle, double_helix=False): hacky_intermediate_compound = Part.makeCompound(shell_faces) shell_faces = hacky_intermediate_compound.Faces else: - shell_faces.append(face) # the bottom is what we extruded + shell_faces.append(face) # the bottom is what we extruded shell = Part.makeShell(shell_faces) - #shell.sewShape() # fill gaps that may result from accumulated tolerances. Needed? - #shell = shell.removeSplitter() # refine. Needed? + # shell.sewShape() # fill gaps that may result from accumulated tolerances. Needed? + # shell = shell.removeSplitter() # refine. Needed? return Part.makeSolid(shell) + def make_face(edge1, edge2): v1, v2 = edge1.Vertexes v3, v4 = edge2.Vertexes @@ -1784,7 +2308,7 @@ def make_face(edge1, edge2): e3 = edge2 e4 = LineSegment(v4.Point, v2.Point).toShape().Edges[0] w = Wire([e3, e4, e1, e2]) - return(Face(w)) + return Face(w) def make_bspline_wire(pts): @@ -1805,9 +2329,10 @@ def points_to_wire(pts): else: out = BSplineCurve() out.interpolate(list(map(fcvec, i))) - wire.append(out.toShape()) + wire.append(out.toShape()) return Wire(wire) + def rotate_tooth(base_tooth, num_teeth): rot = App.Matrix() rot.rotateZ(2 * np.pi / num_teeth) @@ -1817,15 +2342,15 @@ def rotate_tooth(base_tooth, num_teeth): return Wire(flat_shape) - - def fillet_between_edges(edge_1, edge_2, radius): # assuming edges are in a plane # extracting vertices try: from Part import ChFi2d except ImportError: - App.Console.PrintWarning("Your freecad version has no python bindings for 2d-fillets") + App.Console.PrintWarning( + "Your freecad version has no python bindings for 2d-fillets" + ) return [edge_1, edge_2] api = ChFi2d.FilletAPI() diff --git a/freecad/gears/init_gui.py b/freecad/gears/init_gui.py index 04f7c37..322ad71 100644 --- a/freecad/gears/init_gui.py +++ b/freecad/gears/init_gui.py @@ -19,22 +19,26 @@ import os import FreeCADGui as Gui import FreeCAD as App + __dirname__ = os.path.dirname(__file__) try: from FreeCADGui import Workbench except ImportError as e: App.Console.PrintWarning( - "you are using the GearWorkbench with an old version of FreeCAD (<0.16)") + "you are using the GearWorkbench with an old version of FreeCAD (<0.16)" + ) App.Console.PrintWarning( - "the class Workbench is loaded, although not imported: magic") + "the class Workbench is loaded, although not imported: magic" + ) class GearWorkbench(Workbench): """glider workbench""" + MenuText = "Gear" ToolTip = "Gear Workbench" - Icon = os.path.join(__dirname__, 'icons', 'gearworkbench.svg') + Icon = os.path.join(__dirname__, "icons", "gearworkbench.svg") commands = [ "CreateInvoluteGear", "CreateInternalInvoluteGear", @@ -47,13 +51,18 @@ class GearWorkbench(Workbench): "CreateTimingGear", "CreateLanternGear", "CreateHypoCycloidGear", - "CreateGearConnector"] + "CreateGearConnector", + ] def GetClassName(self): return "Gui::PythonWorkbench" def Initialize(self): - from .commands import CreateCycloidGear, CreateInvoluteGear, CreateInternalInvoluteGear + from .commands import ( + CreateCycloidGear, + CreateInvoluteGear, + CreateInternalInvoluteGear, + ) from .commands import CreateBevelGear, CreateInvoluteRack, CreateCrownGear from .commands import CreateWormGear, CreateTimingGear, CreateLanternGear from .commands import CreateHypoCycloidGear, CreateCycloidRack @@ -62,18 +71,18 @@ class GearWorkbench(Workbench): self.appendToolbar("Gear", self.commands) self.appendMenu("Gear", self.commands) # Gui.addIconPath(App.getHomePath()+"Mod/gear/icons/") - Gui.addCommand('CreateInvoluteGear', CreateInvoluteGear()) - Gui.addCommand('CreateInternalInvoluteGear', CreateInternalInvoluteGear()) - Gui.addCommand('CreateCycloidGear', CreateCycloidGear()) - Gui.addCommand('CreateCycloidRack', CreateCycloidRack()) - Gui.addCommand('CreateBevelGear', CreateBevelGear()) - Gui.addCommand('CreateInvoluteRack', CreateInvoluteRack()) - Gui.addCommand('CreateCrownGear', CreateCrownGear()) - Gui.addCommand('CreateWormGear', CreateWormGear()) - Gui.addCommand('CreateTimingGear', CreateTimingGear()) - Gui.addCommand('CreateLanternGear', CreateLanternGear()) - Gui.addCommand('CreateHypoCycloidGear', CreateHypoCycloidGear()) - Gui.addCommand('CreateGearConnector', CreateGearConnector()) + Gui.addCommand("CreateInvoluteGear", CreateInvoluteGear()) + Gui.addCommand("CreateInternalInvoluteGear", CreateInternalInvoluteGear()) + Gui.addCommand("CreateCycloidGear", CreateCycloidGear()) + Gui.addCommand("CreateCycloidRack", CreateCycloidRack()) + Gui.addCommand("CreateBevelGear", CreateBevelGear()) + Gui.addCommand("CreateInvoluteRack", CreateInvoluteRack()) + Gui.addCommand("CreateCrownGear", CreateCrownGear()) + Gui.addCommand("CreateWormGear", CreateWormGear()) + Gui.addCommand("CreateTimingGear", CreateTimingGear()) + Gui.addCommand("CreateLanternGear", CreateLanternGear()) + Gui.addCommand("CreateHypoCycloidGear", CreateHypoCycloidGear()) + Gui.addCommand("CreateGearConnector", CreateGearConnector()) def Activated(self): pass diff --git a/setup.py b/setup.py index b106108..ab51b7f 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,14 @@ from setuptools import setup from pygears import __version__ -setup(name='freecad.gears', - version=str(__version__), - packages=['freecad', - 'freecad.gears', - 'pygears'], - maintainer="looooo", - maintainer_email="sppedflyer@gmail.com", - url="https://github.com/looooo/FCGear", - description="gears for FreeCAD", - install_requires=['numpy'], - include_package_data=True +setup( + name="freecad.gears", + version=str(__version__), + packages=["freecad", "freecad.gears", "pygears"], + maintainer="looooo", + maintainer_email="sppedflyer@gmail.com", + url="https://github.com/looooo/FCGear", + description="gears for FreeCAD", + install_requires=["numpy"], + include_package_data=True, )