From 649c7cfb7cf3a1f99eee22c23a8e9c212290805d Mon Sep 17 00:00:00 2001 From: Chris Bruner Date: Mon, 3 Nov 2025 17:25:40 -0500 Subject: [PATCH] Fix GearConnector circular recompute dependency - Call purgeTouched() on slave gear after Placement modifications - Mark computed properties as Output (flag 8) instead of read-only (flag 1) to prevent triggering recomputes when set during execute() Resolves infinite recompute loop and "still touched after recompute" warning. --- freecad/gears/connector.py | 4 ++++ freecad/gears/internalinvolutegear.py | 12 +++++++----- freecad/gears/involutegear.py | 17 +++++++++-------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/freecad/gears/connector.py b/freecad/gears/connector.py index 1a05d5d..fd20941 100644 --- a/freecad/gears/connector.py +++ b/freecad/gears/connector.py @@ -141,6 +141,7 @@ class GearConnector(object): mat1 = rot * mat0 * rot2 * rot3 * rot4 mat1.move(fp.master_gear.Placement.Base) fp.slave_gear.Placement = mat1 + fp.slave_gear.purgeTouched() if isinstance(fp.master_gear.Proxy, InternalInvoluteGear) and isinstance( fp.slave_gear.Proxy, InvoluteGear @@ -174,6 +175,7 @@ class GearConnector(object): mat1 = rot * mat0 * rot2 * rot3 * rot4 mat1.move(fp.master_gear.Placement.Base) fp.slave_gear.Placement = mat1 + fp.slave_gear.purgeTouched() if ( isinstance(fp.master_gear.Proxy, InvoluteGear) @@ -198,6 +200,7 @@ class GearConnector(object): mat3 = rot * mat2 * mat1 * mat0 mat3.move(fp.master_gear.Placement.Base) fp.slave_gear.Placement = mat3 + fp.slave_gear.purgeTouched() if isinstance(fp.master_gear.Proxy, CycloidGear) and isinstance( fp.slave_gear.Proxy, CycloidGear @@ -221,6 +224,7 @@ class GearConnector(object): mat1 = rot * mat0 * rot2 * rot3 * rot4 mat1.move(fp.master_gear.Placement.Base) fp.slave_gear.Placement = mat1 + fp.slave_gear.purgeTouched() def execute(self, fp): self.onChanged(fp, None) diff --git a/freecad/gears/internalinvolutegear.py b/freecad/gears/internalinvolutegear.py index 8a3255d..4346c9c 100644 --- a/freecad/gears/internalinvolutegear.py +++ b/freecad/gears/internalinvolutegear.py @@ -131,7 +131,7 @@ class InternalInvoluteGear(BaseGear): "pitch_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."), - 1, + 8, ) obj.pitch_diameter = pitch_diameter obj.removeProperty("dw") @@ -146,7 +146,7 @@ class InternalInvoluteGear(BaseGear): "addendum_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The addendum diameter."), - 1, + 8, ) obj.addendum_diameter = addendum_diameter obj.removeProperty("da") @@ -159,7 +159,7 @@ class InternalInvoluteGear(BaseGear): "root_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The root diameter."), - 1, + 8, ) obj.root_diameter = root_diameter obj.removeProperty("df") @@ -200,6 +200,7 @@ class InternalInvoluteGear(BaseGear): "pitch_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."), + 8, ) obj.addProperty( "App::PropertyAngle", @@ -209,6 +210,7 @@ class InternalInvoluteGear(BaseGear): "App::Property", "The angle by which this gear can turn without moving the mating gear.", ), + 8, ) obj.setExpression( "angular_backlash", "backlash / pitch_diameter * 360° / pi" @@ -221,14 +223,14 @@ class InternalInvoluteGear(BaseGear): "transverse_pitch", "computed", QT_TRANSLATE_NOOP("App::Property", "transverse_pitch"), - 1, + 8, ) obj.addProperty( "App::PropertyLength", "outside_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "Outside diameter"), - 1, + 8, ) def add_fillet_properties(self, obj): diff --git a/freecad/gears/involutegear.py b/freecad/gears/involutegear.py index 3c09ea7..80d5fde 100644 --- a/freecad/gears/involutegear.py +++ b/freecad/gears/involutegear.py @@ -95,7 +95,7 @@ class InvoluteGear(BaseGear): "pitch_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."), - 1, + 8, ) obj.pitch_diameter = pitch_diameter obj.removeProperty("dw") @@ -110,7 +110,7 @@ class InvoluteGear(BaseGear): "addendum_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The addendum diameter."), - 1, + 8, ) obj.addendum_diameter = addendum_diameter obj.removeProperty("da") @@ -123,7 +123,7 @@ class InvoluteGear(BaseGear): "root_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The root diameter."), - 1, + 8, ) obj.root_diameter = root_diameter obj.removeProperty("df") @@ -265,14 +265,14 @@ class InvoluteGear(BaseGear): "addendum_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The outside diameter"), - 1, + 8, ) obj.addProperty( "App::PropertyLength", "root_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The root diameter"), - 1, + 8, ) self.add_traverse_module_property(obj) obj.addProperty( @@ -280,7 +280,7 @@ class InvoluteGear(BaseGear): "pitch_diameter", "computed", QT_TRANSLATE_NOOP("App::Property", "The pitch diameter."), - 1, + 8, ) obj.addProperty( "App::PropertyAngle", @@ -290,6 +290,7 @@ class InvoluteGear(BaseGear): "App::Property", "The angle by which this gear can turn without moving the mating gear.", ), + 8, ) obj.setExpression( "angular_backlash", "backlash / pitch_diameter * 360° / pi" @@ -302,7 +303,7 @@ class InvoluteGear(BaseGear): "transverse_pitch", "computed", QT_TRANSLATE_NOOP("App::Property", "transverse_pitch"), - 1, + 8, ) def add_tolerance_properties(self, obj): @@ -356,7 +357,7 @@ class InvoluteGear(BaseGear): "traverse_module", "computed", QT_TRANSLATE_NOOP("App::Property", "traverse module of the generated gear"), - 1, + 8, ) def compute_traverse_properties(self, obj):