Repository: psychopy/psychopy

Issue:
cannot animate width/height of visual.Rect in v2022.1.2
Tagging @marcus-nystrom.

The below code worked in v2021.2.3 (I see a fixation cross that changes in size) but not in v2022.1.2 (size does not change). Is this a bug or should i animate a rect differently?

```
from psychopy import visual, event, core
import numpy as np

win = visual.Window(monitor='testMonitor')
screen_refresh_rate = float(win.getActualFrameRate())
outer_diameter=0.5
inner_diameter=0.1
outer_color = 'black'
inner_color = 'white'
units = 'deg'

line_vertical = visual.Rect(win, width=inner_diameter, height=outer_diameter,
                            fillColor=inner_color, units = units)
line_horizontal = visual.Rect(win, width=outer_diameter, height=inner_diameter,
                            fillColor=inner_color, units = units)

tick = 0
while 1:
    target_size = np.abs(1 - np.sin(3 * tick / screen_refresh_rate + 4*np.pi/2)) + 0.2
    print(target_size)

    line_vertical.height = target_size
    line_horizontal.width = target_size

    line_vertical.draw()
    line_horizontal.draw()
    win.flip()

    k = event.getKeys()
    if 'escape' in k:
        win.close()
        break
    
    tick += 1
```


Discussion:
We changed how objects lay themselves out in 2022.1, but this change is purely on the back end so behaviour should stay the same - so yes this is a bug :) I'll look into why it's no longer working

Resolving patch:
diff --git a/psychopy/visual/basevisual.py b/psychopy/visual/basevisual.py
--- a/psychopy/visual/basevisual.py
+++ b/psychopy/visual/basevisual.py
@@ -1339,13 +1339,22 @@ def width(self):
     def width(self, value):
         # Convert to a numpy array
         value = numpy.array(value)
+        # Get original size
+        size = self.size
         # Set size
         if len(self.size.shape) == 1:
             # Set first value if a 1d array
-            self.size[0] = value
+            size[0] = value
         elif len(self.size.shape) == 2:
             # Set first column if a 2d array
-            self.size[:, 0] = value
+            size[:, 0] = value
+        else:
+            raise NotImplementedError(
+                f"Cannot set height and width for {type(self).__name__} objects with more than 2 dimensions. "
+                f"Please use .size instead."
+            )
+
+        self.size = size
 
     @property
     def height(self):
@@ -1360,13 +1369,22 @@ def height(self):
     def height(self, value):
         # Convert to a numpy array
         value = numpy.array(value)
+        # Get original size
+        size = self.size
         # Set size
         if len(self.size.shape) == 1:
-            # Set first value if a 1d array
-            self.size[1] = value
+            # Set second value if a 1d array
+            size[1] = value
         elif len(self.size.shape) == 2:
-            # Set first column if a 2d array
-            self.size[:, 1] = value
+            # Set second column if a 2d array
+            size[:, 1] = value
+        else:
+            raise NotImplementedError(
+                f"Cannot set height and width for {type(self).__name__} objects with more than 2 dimensions. "
+                f"Please use .size instead."
+            )
+
+        self.size = size
 
     @property
     def vertices(self):


Test patch:
diff --git a/psychopy/tests/test_visual/test_basevisual.py b/psychopy/tests/test_visual/test_basevisual.py
--- a/psychopy/tests/test_visual/test_basevisual.py
+++ b/psychopy/tests/test_visual/test_basevisual.py
@@ -363,6 +363,29 @@ def test_pos_size(self):
         del obj
         del win
 
+    # def test_wh_setters(self):
+    #     """
+    #     Test that the width and height setters function the same as using the size setter
+    #     """
+    #     # Define some sizes to try out
+    #     cases = [
+    #         {'size': 100,
+    #          'units': 'pix'},
+    #         {'size': 200,
+    #          'units': 'pix'},
+    #     ]
+    #     # Create duplicate of object for safety
+    #     obj = copy(self.obj)
+    #     # Try each case
+    #     for case in cases:
+    #         # Set units
+    #         obj.units = case['units']
+    #         # Set width and height using setters
+    #         obj.width = case['size']
+    #         obj.height = case['size']
+    #         # Check that the resulting size is as desired
+    #         assert all(obj.size == case['size'])
+
     def test_unit_mismatch(self):
         """
         Test that a given stimulus can be drawn without error in all combinations of stimulus units x window units and