GearConnection and ChainConnection updated.
ChainConnection can connect a gearConnection to a gear.
This commit is contained in:
@@ -49,34 +49,6 @@ class Chain(object):
|
||||
fp.connector.angle1 = fp.angle
|
||||
|
||||
|
||||
class Chain(object):
|
||||
def __init__(self, obj, gear_list):
|
||||
obj.addProperty("App::PropertyLinkList", "gear_list", "GearChain", "list of gears in the chain")
|
||||
obj.addProperty("App::PropertyAngle", "angle", "GearChain", "angle of the first gear")
|
||||
obj.gear_list = gear_list
|
||||
obj.Proxy = self
|
||||
self.obj = obj
|
||||
ViewProviderGearConnector(obj.ViewObject)
|
||||
|
||||
# Create connectors
|
||||
master_gear = gear_list[0]
|
||||
for i, slave_gear in enumerate(gear_list[1:]):
|
||||
if i == 0:
|
||||
connector = app.ActiveDocument.addObject("Part::FeaturePython", "GearConnector")
|
||||
GearConnector(connector, master_gear, slave_gear)
|
||||
connector.angle1 = self.obj.angle
|
||||
self.obj.addProperty("App::PropertyLink", "connector", "GearChain", "main connector")
|
||||
self.obj.connector = connector
|
||||
else:
|
||||
connector = app.ActiveDocument.addObject("Part::FeaturePython", "ChainConnector")
|
||||
ChainConnector(connector, self.obj.connector, slave_gear)
|
||||
master_gear = slave_gear
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
if prop == 'angle':
|
||||
fp.connector.angle1 = fp.angle
|
||||
|
||||
|
||||
class ChainConnector(object):
|
||||
def __init__(self, obj, master_connector, slave_gear):
|
||||
|
||||
@@ -108,100 +80,7 @@ class ChainConnector(object):
|
||||
# We convert radians (from .Angle) to degrees for consistency, though
|
||||
# onChanged will convert it back. Or we can just use the raw Angle.
|
||||
obj.setExpression('input_gear_angle', f'{master_connector.Name}.slave_gear.Placement.Rotation.Angle')
|
||||
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
# Only react when the input angle (G2's rotation) or the final slave gear (G3) link changes
|
||||
if prop not in ('input_gear_angle', 'slave_gear'):
|
||||
return
|
||||
|
||||
# Ensure we have both gears before proceeding
|
||||
if fp.master_gear is None or fp.slave_gear is None:
|
||||
return
|
||||
|
||||
# input_gear_angle is linked to Placement.Rotation.Angle, which is in RADIANS
|
||||
master_angle_rad = fp.input_gear_angle.Value
|
||||
master_angle_deg = np.rad2deg(master_angle_rad) # Convert to degrees
|
||||
|
||||
dw_master = fp.master_gear.pitch_diameter.Value
|
||||
dw_slave = fp.slave_gear.pitch_diameter.Value
|
||||
|
||||
# --- Involute Gear Pair Logic ---
|
||||
if isinstance(fp.master_gear.Proxy, InvoluteGear) and isinstance(fp.slave_gear.Proxy, InvoluteGear):
|
||||
dist = (dw_master + dw_slave) / 2
|
||||
slave_pos = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
|
||||
|
||||
# Kinematics: Calculate slave rotation (opposite direction)
|
||||
angle_slave_deg = -(dw_master / dw_slave) * master_angle_deg
|
||||
|
||||
# Apply rotation and position to G3 (slave_gear)
|
||||
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
|
||||
rot_slave = app.Rotation(app.Vector(0, 0, 1), angle_slave_deg + angle3)
|
||||
|
||||
fp.slave_gear.Placement = app.Placement(slave_pos, rot_slave)
|
||||
fp.slave_gear.purgeTouched()
|
||||
|
||||
# --- Internal Involute Gear Logic ---
|
||||
elif isinstance(fp.master_gear.Proxy, InternalInvoluteGear) and isinstance(fp.slave_gear.Proxy, InvoluteGear):
|
||||
dist = (dw_master - dw_slave) / 2
|
||||
slave_pos = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
|
||||
|
||||
# Kinematics: Calculate slave rotation (same direction)
|
||||
angle_slave_deg = (dw_master / dw_slave) * master_angle_deg
|
||||
|
||||
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
|
||||
rot_slave = app.Rotation(app.Vector(0, 0, 1), angle_slave_deg + angle3)
|
||||
|
||||
fp.slave_gear.Placement = app.Placement(slave_pos, rot_slave)
|
||||
fp.slave_gear.purgeTouched()
|
||||
|
||||
# --- Cycloid Gear Pair Logic ---
|
||||
elif isinstance(fp.master_gear.Proxy, CycloidGear) and isinstance(fp.slave_gear.Proxy, CycloidGear):
|
||||
dist = (dw_master + dw_slave) / 2
|
||||
slave_pos = fp.master_gear.Placement.Base + app.Vector(dist, 0, 0)
|
||||
|
||||
# Kinematics: Calculate slave rotation (opposite direction)
|
||||
angle_slave_deg = -(dw_master / dw_slave) * master_angle_deg
|
||||
|
||||
# Apply rotation and position to G3 (slave_gear)
|
||||
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
|
||||
rot_slave = app.Rotation(app.Vector(0, 0, 1), angle_slave_deg + angle3)
|
||||
|
||||
fp.slave_gear.Placement = app.Placement(slave_pos, rot_slave)
|
||||
fp.slave_gear.purgeTouched()
|
||||
|
||||
# [Add Rack Logic here if chains need to drive racks]
|
||||
|
||||
def execute(self, fp):
|
||||
# When executed, simply trigger the onChanged to use the current expression-linked value
|
||||
self.onChanged(fp, 'input_gear_angle')
|
||||
|
||||
class GearDev(object):
|
||||
def __init__(self, obj, gear_list):
|
||||
obj.addProperty("App::PropertyLinkList", "gear_list", "GearChain", "list of gears in the chain")
|
||||
obj.addProperty("App::PropertyAngle", "angle", "GearChain", "angle of the first gear")
|
||||
obj.gear_list = gear_list
|
||||
obj.Proxy = self
|
||||
self.obj = obj
|
||||
ViewProviderGearConnector(obj.ViewObject)
|
||||
|
||||
# Create connectors
|
||||
master_gear = gear_list[0]
|
||||
for i, slave_gear in enumerate(gear_list[1:]):
|
||||
if i == 0:
|
||||
connector = app.ActiveDocument.addObject("Part::FeaturePython", "GearConnector")
|
||||
GearConnector(connector, master_gear, slave_gear)
|
||||
connector.angle1 = self.obj.angle
|
||||
self.obj.addProperty("App::PropertyLink", "connector", "GearChain", "main connector")
|
||||
self.obj.connector = connector
|
||||
else:
|
||||
connector = app.ActiveDocument.addObject("Part::FeaturePython", "ChainConnector")
|
||||
ChainConnector(connector, self.obj.connector, slave_gear)
|
||||
master_gear = slave_gear
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
if prop == 'angle':
|
||||
fp.connector.angle1 = fp.angle
|
||||
self.onChanged(obj, 'input_gear_angle')
|
||||
|
||||
def __init__(self, obj, master_connector, slave_gear):
|
||||
|
||||
@@ -301,3 +180,4 @@ class GearDev(object):
|
||||
# When executed, simply trigger the onChanged to use the current expression-linked value
|
||||
self.onChanged(fp, 'input_gear_angle')
|
||||
|
||||
|
||||
|
||||
@@ -190,37 +190,34 @@ class GearConnector(object):
|
||||
|
||||
elif master_stationary and not slave_stationary:
|
||||
# Original behavior: slave gear orbits around master
|
||||
mat0 = app.Matrix() # unity matrix
|
||||
trans = app.Vector(dist)
|
||||
mat0.move(trans)
|
||||
rot = app.Rotation(app.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 = app.Rotation(app.Vector(0, 0, 1), angle2).toMatrix()
|
||||
angle3 = abs(fp.slave_gear.num_teeth % 2 - 1) * 180.0 / fp.slave_gear.num_teeth
|
||||
rot3 = app.Rotation(app.Vector(0, 0, 1), angle3).toMatrix()
|
||||
rot4 = app.Rotation(app.Vector(0, 0, 1), -angle4).toMatrix()
|
||||
mat1 = rot * mat0 * rot2 * rot3 * rot4
|
||||
mat1.move(fp.master_gear.Placement.Base)
|
||||
fp.slave_gear.Placement = mat1
|
||||
orbit_angle = fp.angle1.Value
|
||||
slave_rotation_angle = - (dw_master / dw_slave) * orbit_angle
|
||||
|
||||
# Slave gear's local rotation
|
||||
slave_local_rot = app.Rotation(app.Vector(0, 0, 1), slave_rotation_angle)
|
||||
|
||||
# Orbital placement
|
||||
orbit_rot = app.Rotation(app.Vector(0, 0, 1), orbit_angle)
|
||||
orbit_pos = fp.master_gear.Placement.Base + orbit_rot.multVec(app.Vector(dist, 0, 0))
|
||||
|
||||
# Combine orbital placement with local rotation
|
||||
fp.slave_gear.Placement = app.Placement(orbit_pos, orbit_rot * slave_local_rot)
|
||||
fp.slave_gear.purgeTouched()
|
||||
|
||||
elif not master_stationary and slave_stationary:
|
||||
# Master orbits around slave (inverse behavior)
|
||||
mat0 = app.Matrix() # unity matrix
|
||||
trans = app.Vector(dist)
|
||||
mat0.move(trans)
|
||||
rot = app.Rotation(app.Vector(0, 0, 1), -fp.angle1).toMatrix() # negative angle for reverse orbit
|
||||
angle2 = -dw_slave / dw_master * fp.angle1.Value # master's rotation based on orbital motion
|
||||
angle_slave = fp.slave_gear.Placement.Rotation.Angle * sum(
|
||||
fp.slave_gear.Placement.Rotation.Axis
|
||||
)
|
||||
angle4 = -dw_slave / dw_master * np.rad2deg(angle_slave) # additional rotation from slave's current angle
|
||||
rot2 = app.Rotation(app.Vector(0, 0, 1), angle2).toMatrix()
|
||||
rot4 = app.Rotation(app.Vector(0, 0, 1), -angle4).toMatrix()
|
||||
mat1 = rot * mat0 * rot2 * rot4
|
||||
mat1.move(fp.slave_gear.Placement.Base)
|
||||
fp.master_gear.Placement = mat1
|
||||
orbit_angle = -fp.angle1.Value
|
||||
master_rotation_angle = - (dw_slave / dw_master) * orbit_angle
|
||||
|
||||
# Master gear's local rotation
|
||||
master_local_rot = app.Rotation(app.Vector(0, 0, 1), master_rotation_angle)
|
||||
|
||||
# Orbital placement
|
||||
orbit_rot = app.Rotation(app.Vector(0, 0, 1), orbit_angle)
|
||||
orbit_pos = fp.slave_gear.Placement.Base + orbit_rot.multVec(app.Vector(dist, 0, 0))
|
||||
|
||||
# Combine orbital placement with local rotation
|
||||
fp.master_gear.Placement = app.Placement(orbit_pos, orbit_rot * master_local_rot)
|
||||
fp.master_gear.purgeTouched()
|
||||
|
||||
# else: both not stationary - no action needed
|
||||
|
||||
Reference in New Issue
Block a user