Repository: psychopy/psychopy

Issue:
Circle-Stim doesn't update radius when redrawing in 2022.1.2
I implemented a pulsating circle a while ago and just noticed, that the pulsing no longer works.

My current workaround is to downgrade to some version from last year (e.g. 2021.1.3), where it still works.

### A minimal implementation:

```python
from psychopy import visual

win = visual.Window(size=(500,500), fullscr=False, units="pix", color="white")
circle = visual.Circle(win, radius=5, units="pix", lineColor="black", fillColor="grey")

for i in range(100):
    circle.setRadius(circle.radius + 2)
    circle.draw()
    win.flip()
```

### The faulty behavior in the latest 2022.1.2:
- the circle.radius property actually changes its value
- but the circle keeps a constant (initial) size

I just noticed, that the latest release is newer than "2022.1.2" - but it's accidentally been called "2021.1.3"?! I didn't check, if this latest version already solves this problem (the fixes don't seem to mention this, though).




Discussion:
Ah, I see the problem... There's a missed call to `_updateVertices` in the backend of Circle. Will put in a PR to fix it, in the meantime you can fix the problem in your code by adding this after `circle.setRadius`:
```
circle._updateVertices()
```
Thank you, that fixes it! 
I obviously overlooked this missing call when having a quick look into the backend.


Resolving patch:
diff --git a/psychopy/visual/basevisual.py b/psychopy/visual/basevisual.py
--- a/psychopy/visual/basevisual.py
+++ b/psychopy/visual/basevisual.py
@@ -1413,6 +1413,7 @@ def vertices(self, value):
             ]
         # Create Vertices object
         self._vertices = Vertices(value, obj=self, flip=self.flip, anchor=self.anchor)
+        self._needVertexUpdate = True
 
     @property
     def flip(self):


Test patch:
diff --git a/psychopy/tests/data/test_circle_radius_.1height.png b/psychopy/tests/data/test_circle_radius_.1height.png
new file mode 100644
Binary files /dev/null and b/psychopy/tests/data/test_circle_radius_.1height.png differ
diff --git a/psychopy/tests/data/test_circle_radius_.2height.png b/psychopy/tests/data/test_circle_radius_.2height.png
new file mode 100644
Binary files /dev/null and b/psychopy/tests/data/test_circle_radius_.2height.png differ
diff --git a/psychopy/tests/data/test_circle_radius_10pix.png b/psychopy/tests/data/test_circle_radius_10pix.png
new file mode 100644
Binary files /dev/null and b/psychopy/tests/data/test_circle_radius_10pix.png differ
diff --git a/psychopy/tests/data/test_circle_radius_20pix.png b/psychopy/tests/data/test_circle_radius_20pix.png
new file mode 100644
Binary files /dev/null and b/psychopy/tests/data/test_circle_radius_20pix.png differ
diff --git a/psychopy/tests/test_visual/test_circle.py b/psychopy/tests/test_visual/test_circle.py
--- a/psychopy/tests/test_visual/test_circle.py
+++ b/psychopy/tests/test_visual/test_circle.py
@@ -1,6 +1,9 @@
+from pathlib import Path
+
 import pytest
 from psychopy import visual
 from .test_basevisual import _TestColorMixin, _TestUnitsMixin
+from .. import utils
 
 
 class TestCircle(_TestColorMixin, _TestUnitsMixin):
@@ -23,16 +26,29 @@ def setup_class(self):
     def test_radius(self):
         # Define some use cases
         cases = [
-            {'size': [1, 1], 'radius': 1, 'minvertices': [-1, -1], 'maxvertices': [1, 1], 'units': 'height'},
-            {'size': [1, 1], 'radius': 2, 'minvertices': [-2, -2], 'maxvertices': [2, 2], 'units': 'height'},
-            {'size': [2, 2], 'radius': 0.5, 'minvertices': [-1, -1], 'maxvertices': [1, 1], 'units': 'height'},
+            {'size': [.1, .1], 'radius': 1, 'units': 'height',
+             'minvertices': [-.1, -.1], 'maxvertices': [.1, .1],
+             'label': ".1height"},
+            {'size': [.1, .1], 'radius': 2, 'units': 'height',
+             'minvertices': [-.2, -.2], 'maxvertices': [.2, .2],
+             'label': ".2height"},
+            {'size': [.2, .2], 'radius': 0.5, 'units': 'height',
+             'minvertices': [-.1, -.1], 'maxvertices': [.1, .1],
+             'label': ".1height"},
 
-            {'size': [100, 100], 'radius': 1, 'minvertices': [-100, -100], 'maxvertices': [100, 100], 'units': 'pix'},
-            {'size': [100, 100], 'radius': 2, 'minvertices': [-200, -200], 'maxvertices': [200, 200], 'units': 'pix'},
-            {'size': [200, 200], 'radius': 0.5, 'minvertices': [-100, -100], 'maxvertices': [100, 100], 'units': 'pix'},
+            {'size': [10, 10], 'radius': 1, 'units': 'pix',
+             'minvertices': [-10, -10], 'maxvertices': [10, 10],
+             'label': "10pix"},
+            {'size': [10, 10], 'radius': 2, 'units': 'pix',
+             'minvertices': [-20, -20], 'maxvertices': [20, 20],
+             'label': "20pix"},
+            {'size': [20, 20], 'radius': 0.5, 'units': 'pix',
+             'minvertices': [-10, -10], 'maxvertices': [10, 10],
+             'label': "10pix"},
         ]
         # Test each case
         for case in cases:
+            self.win.flip()
             # Apply units, size and radius
             self.obj.units = case['units']
             self.obj.radius = case['radius']
@@ -42,3 +58,8 @@ def test_radius(self):
             for i in [0, 1]:
                 assert min(verts[:, i]) == case['minvertices'][i]
                 assert max(verts[:, i]) == case['maxvertices'][i]
+            # Check that vertices are drawn correctly
+            filename = f"test_circle_radius_{case['label']}.png"
+            self.obj.draw()
+            # self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / filename)
+            # utils.compareScreenshot(Path(utils.TESTS_DATA_PATH) / filename, self.win, crit=20)