CreativePython 1.1.5__tar.gz → 1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. {creativepython-1.1.5/src/CreativePython.egg-info → creativepython-1.2}/PKG-INFO +1 -1
  2. {creativepython-1.1.5 → creativepython-1.2}/pyproject.toml +2 -2
  3. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/GuiRenderer.py +39 -94
  4. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/RealtimeAudioPlayer.py +9 -8
  5. {creativepython-1.1.5 → creativepython-1.2/src/CreativePython.egg-info}/PKG-INFO +1 -1
  6. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython.egg-info/SOURCES.txt +3 -0
  7. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython.egg-info/top_level.txt +1 -0
  8. creativepython-1.2/src/animation.py +554 -0
  9. {creativepython-1.1.5 → creativepython-1.2}/src/gui.py +472 -139
  10. {creativepython-1.1.5 → creativepython-1.2}/src/midi.py +52 -52
  11. {creativepython-1.1.5 → creativepython-1.2}/src/music.py +73 -82
  12. {creativepython-1.1.5 → creativepython-1.2}/src/osc.py +10 -10
  13. creativepython-1.2/tests/test_crescendo.py +82 -0
  14. creativepython-1.2/tests/test_crescendo_import.py +114 -0
  15. {creativepython-1.1.5 → creativepython-1.2}/LICENSE +0 -0
  16. {creativepython-1.1.5 → creativepython-1.2}/LICENSE-PSF +0 -0
  17. {creativepython-1.1.5 → creativepython-1.2}/MANIFEST.in +0 -0
  18. {creativepython-1.1.5 → creativepython-1.2}/README.md +0 -0
  19. {creativepython-1.1.5 → creativepython-1.2}/setup.cfg +0 -0
  20. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/GuiHandler.py +0 -0
  21. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/__init__.py +0 -0
  22. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/Java-Comparison-Tests/advMetricRunner.pythonSurvey.py +0 -0
  23. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/Java-Comparison-Tests/compareMetrics_Java-Vs-Python.py +0 -0
  24. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/RunMetrics.py +0 -0
  25. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/Surveyor.py +0 -0
  26. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/__init__.py +0 -0
  27. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/Confidence.py +0 -0
  28. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/Contig.py +0 -0
  29. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/ExtendedNote.py +0 -0
  30. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/Histogram.py +0 -0
  31. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/Judgement.py +0 -0
  32. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/Measurement.py +0 -0
  33. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/PianoRoll.py +0 -0
  34. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/PianoRollOld.py +0 -0
  35. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/__init__.py +0 -0
  36. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/test_ExtendedNote.py +0 -0
  37. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/test_Histogram.py +0 -0
  38. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/test_Measurement.py +0 -0
  39. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/test_PianoRoll_assertions.py +0 -0
  40. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/test_PianoRoll_integration.py +0 -0
  41. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/test_PianoRoll_quantization.py +0 -0
  42. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/data/test_PianoRoll_unit.py +0 -0
  43. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/Metric.py +0 -0
  44. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/ZipfMetrics.py +0 -0
  45. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/__init__.py +0 -0
  46. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ChordDensityMetric.py +0 -0
  47. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ChordDistanceMetric.py +0 -0
  48. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ChordMetric.py +0 -0
  49. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ChordNormalizedMetric.py +0 -0
  50. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ChromaticToneMetric.py +0 -0
  51. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ContourBasslineDurationMetric.py +0 -0
  52. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ContourBasslineDurationQuantizedMetric.py +0 -0
  53. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ContourBasslinePitchMetric.py +0 -0
  54. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ContourMelodyDurationMetric.py +0 -0
  55. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ContourMelodyDurationQuantizedMetric.py +0 -0
  56. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/ContourMelodyPitchMetric.py +0 -0
  57. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/DurationBigramMetric.py +0 -0
  58. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/DurationDistanceMetric.py +0 -0
  59. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/DurationMetric.py +0 -0
  60. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/DurationQuantizedBigramMetric.py +0 -0
  61. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/DurationQuantizedDistanceMetric.py +0 -0
  62. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/DurationQuantizedMetric.py +0 -0
  63. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/HarmonicBigramMetric.py +0 -0
  64. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/HarmonicConsonanceMetric.py +0 -0
  65. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/HarmonicIntervalMetric.py +0 -0
  66. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/MelodicBigramMetric.py +0 -0
  67. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/MelodicConsonanceMetric.py +0 -0
  68. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/MelodicIntervalMetric.py +0 -0
  69. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/PitchDistanceMetric.py +0 -0
  70. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/PitchDurationMetric.py +0 -0
  71. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/PitchDurationQuantizedMetric.py +0 -0
  72. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/PitchMetric.py +0 -0
  73. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/RestMetric.py +0 -0
  74. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/__init__.py +0 -0
  75. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/test_DurationMetric.py +0 -0
  76. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/test_Metrics_BasicIntervalsAndBigrams.py +0 -0
  77. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/test_Metrics_ChordsAndConsonance.py +0 -0
  78. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/test_Metrics_ContoursAndChromatic.py +0 -0
  79. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/test_Metrics_QuantizedDurationsAndDistances.py +0 -0
  80. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/test_PitchMetric.py +0 -0
  81. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/simple/test_RestMetric.py +0 -0
  82. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/metrics/test_Metric.py +0 -0
  83. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/utilities/CSVWriter.py +0 -0
  84. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/utilities/PowerLawRandom.py +0 -0
  85. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/nevmuse/utilities/__init__.py +0 -0
  86. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython/notationRenderer.py +0 -0
  87. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython.egg-info/dependency_links.txt +0 -0
  88. {creativepython-1.1.5 → creativepython-1.2}/src/CreativePython.egg-info/requires.txt +0 -0
  89. {creativepython-1.1.5 → creativepython-1.2}/src/bin/libportaudio.2.dylib +0 -0
  90. {creativepython-1.1.5 → creativepython-1.2}/src/iannix.py +0 -0
  91. {creativepython-1.1.5 → creativepython-1.2}/src/image.py +0 -0
  92. {creativepython-1.1.5 → creativepython-1.2}/src/markov.py +0 -0
  93. {creativepython-1.1.5 → creativepython-1.2}/src/timer.py +0 -0
  94. {creativepython-1.1.5 → creativepython-1.2}/src/zipf.py +0 -0
  95. {creativepython-1.1.5 → creativepython-1.2}/tests/testAnimate.py +0 -0
  96. {creativepython-1.1.5 → creativepython-1.2}/tests/testCompress.py +0 -0
  97. {creativepython-1.1.5 → creativepython-1.2}/tests/testGameboard.py +0 -0
  98. {creativepython-1.1.5 → creativepython-1.2}/tests/testHitTesting.py +0 -0
  99. {creativepython-1.1.5 → creativepython-1.2}/tests/testPeer.py +0 -0
  100. {creativepython-1.1.5 → creativepython-1.2}/tests/testToolTips.py +0 -0
  101. {creativepython-1.1.5 → creativepython-1.2}/tests/test_keyEvent.py +0 -0
  102. {creativepython-1.1.5 → creativepython-1.2}/tests/test_midi.py +0 -0
  103. {creativepython-1.1.5 → creativepython-1.2}/tests/test_osc.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CreativePython
3
- Version: 1.1.5
3
+ Version: 1.2
4
4
  Summary: A Python-based software environment for developing algorithmic art projects.
5
5
  Author-email: "Dr. Bill Manaris" <manaris@cofc.edu>, Taj Ballinger <ballingertj@g.cofc.edu>, Trevor Ritchie <ritchiets@g.cofc.edu>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "CreativePython"
7
- version = "1.1.5"
7
+ version = "1.2"
8
8
  description = "A Python-based software environment for developing algorithmic art projects."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -40,7 +40,7 @@ dependencies = [
40
40
 
41
41
  [tool.setuptools]
42
42
  package-dir = {"" = "src"}
43
- py-modules = ["gui", "image", "midi", "music", "osc", "timer", "iannix", "markov", "zipf"]
43
+ py-modules = ["gui", "image", "midi", "music", "osc", "timer", "animation", "iannix", "markov", "zipf"]
44
44
 
45
45
  [tool.setuptools.packages.find]
46
46
  where = ["src"]
@@ -624,7 +624,30 @@ class _QPolygonItem(_QtGraphicsItemEventMixin, QtWidgets.QGraphicsPolygonItem):
624
624
  pass
625
625
 
626
626
  class _QPixmapItem(_QtGraphicsItemEventMixin, QtWidgets.QGraphicsPixmapItem):
627
- pass
627
+ _colorFilter = None # (r, g, b, a) set by IconMirror._applyColor; None = no filter
628
+
629
+ def paint(self, painter, option, widget=None):
630
+ if self._colorFilter is None:
631
+ super().paint(painter, option, widget)
632
+ return
633
+
634
+ pixmap = self.pixmap()
635
+ if pixmap.isNull():
636
+ return
637
+
638
+ r, g, b, a = self._colorFilter
639
+ image = pixmap.toImage().convertToFormat(QtGui.QImage.Format.Format_ARGB32)
640
+
641
+ filtered = QtGui.QImage(image.size(), QtGui.QImage.Format.Format_ARGB32)
642
+ filtered.fill(QtCore.Qt.GlobalColor.transparent)
643
+
644
+ p = QtGui.QPainter(filtered)
645
+ p.drawImage(0, 0, image)
646
+ p.setCompositionMode(QtGui.QPainter.CompositionMode.CompositionMode_SourceOver)
647
+ p.fillRect(image.rect(), QtGui.QColor(r, g, b, a))
648
+ p.end()
649
+
650
+ painter.drawImage(self.offset(), filtered)
628
651
 
629
652
  class _QGroupItem(_QtGraphicsItemEventMixin, QtWidgets.QGraphicsItemGroup):
630
653
  pass
@@ -974,8 +997,6 @@ class DisplayMirror:
974
997
  else:
975
998
  item.qObject.setZValue(qZValue)
976
999
  self._scene.addItem(item.qObject) # attach QGraphicsItem to scene
977
- cacheMode = QtWidgets.QGraphicsItem.CacheMode.DeviceCoordinateCache
978
- item.qObject.setCacheMode(cacheMode)
979
1000
 
980
1001
  posArgs = {}
981
1002
  if x is not None:
@@ -1688,15 +1709,12 @@ class _DrawableMirror:
1688
1709
  self._rotation = 0
1689
1710
  self._toolTipText = None
1690
1711
 
1691
- # GuiRenderer only handles commands that alter an object visually,
1692
- # or that rely on hit testing. Everything else is handled in gui.py
1712
+ # GuiRenderer only handles commands that alter an object visually.
1713
+ # Hit testing (contains/intersects/encloses) is handled entirely in gui.py.
1693
1714
  self._commandHandlers = {
1694
1715
  'setPosition': self._setPosition,
1695
1716
  'setSize': self._setSize,
1696
1717
  'setRotation': self._setRotation,
1697
- 'contains': self._contains,
1698
- 'intersects': self._intersects,
1699
- 'encloses': self._encloses,
1700
1718
  'setToolTipText': self._setToolTipText,
1701
1719
  'show': self._show,
1702
1720
  'hide': self._hide,
@@ -1759,43 +1777,6 @@ class _DrawableMirror:
1759
1777
  self.qObject.prepareGeometryChange()
1760
1778
  self.qObject.setRotation(qtDegree)
1761
1779
 
1762
- # ── Hit testing ────────────────────────────────────────────────────────────
1763
-
1764
- def _contains(self, args, responseId):
1765
- """Returns True if the point (x, y) is inside this item."""
1766
- x = args.get('x', 0)
1767
- y = args.get('y', 0)
1768
- globalPoint = QtCore.QPointF(x, y)
1769
- localPoint = self.qObject.mapFromScene(globalPoint)
1770
- result = self.qObject.contains(localPoint)
1771
- self.guiRenderer.sendResponse(responseId, [result])
1772
-
1773
- def _intersects(self, args, responseId):
1774
- """Returns True if this item intersects another item."""
1775
- otherObjectId = args.get('otherObjectId')
1776
- otherMirror = self.guiRenderer._objectRegistry.get(otherObjectId)
1777
- result = False
1778
-
1779
- if otherMirror is not None:
1780
- pathA = self.qObject.mapToScene(self.qObject.shape())
1781
- pathB = otherMirror.qObject.mapToScene(otherMirror.qObject.shape())
1782
- result = pathA.intersects(pathB)
1783
-
1784
- self.guiRenderer.sendResponse(responseId, [result])
1785
-
1786
- def _encloses(self, args, responseId):
1787
- """Returns True if this item fully encloses another item."""
1788
- otherObjectId = args.get('otherObjectId')
1789
- otherMirror = self.guiRenderer._objectRegistry.get(otherObjectId)
1790
- result = False
1791
-
1792
- if otherMirror is not None:
1793
- pathA = self.qObject.mapToScene(self.qObject.shape())
1794
- pathB = otherMirror.qObject.mapToScene(otherMirror.qObject.shape())
1795
- result = pathA.contains(pathB)
1796
-
1797
- self.guiRenderer.sendResponse(responseId, [result])
1798
-
1799
1780
  # ── Visibility ─────────────────────────────────────────────────────────────
1800
1781
 
1801
1782
  def _show(self, args, responseId):
@@ -2405,6 +2386,7 @@ class PolygonMirror(_GraphicsMirror):
2405
2386
  # IconMirror — mirror of gui.py's Icon
2406
2387
  #######################################################################################
2407
2388
 
2389
+
2408
2390
  class IconMirror(_GraphicsMirror):
2409
2391
  """
2410
2392
  Mirror of gui.py's Icon. Backed by a QGraphicsPixmapItem.
@@ -2474,16 +2456,25 @@ class IconMirror(_GraphicsMirror):
2474
2456
  'getPixels': self._getPixels,
2475
2457
  'setPixels': self._setPixels,
2476
2458
  'write': self._write,
2459
+ 'setAlpha': self._setAlpha,
2477
2460
  })
2478
2461
 
2479
2462
  def _applyColor(self):
2480
- """No-op QGraphicsPixmapItem has no pen or brush."""
2481
- pass
2463
+ r, g, b, a = self._color
2464
+ if [r, g, b, a] == [0, 0, 0, 0]:
2465
+ self.qObject._colorFilter = None
2466
+ else:
2467
+ self.qObject._colorFilter = (r, g, b, a)
2468
+ self.qObject.update()
2482
2469
 
2483
2470
  def _applyThickness(self):
2484
2471
  """No-op — QGraphicsPixmapItem has no pen or brush."""
2485
2472
  pass
2486
2473
 
2474
+ def _setAlpha(self, args, responseId):
2475
+ alpha = args.get('alpha', 255)
2476
+ self.qObject.setOpacity(alpha / 255.0)
2477
+
2487
2478
  # ── Size ───────────────────────────────────────────────────────────────────
2488
2479
 
2489
2480
  def _getSize(self, args, responseId):
@@ -2881,9 +2872,6 @@ class GroupMirror(_DrawableMirror):
2881
2872
  item.qObject.setZValue(qZValue)
2882
2873
  self.qObject.addToGroup(item.qObject)
2883
2874
 
2884
- cacheMode = QtWidgets.QGraphicsItem.CacheMode.DeviceCoordinateCache
2885
- item.qObject.setCacheMode(cacheMode)
2886
-
2887
2875
  def _removeChild(self, args, responseId):
2888
2876
  """Removes a child from the group."""
2889
2877
  itemId = args.get('itemId')
@@ -2922,8 +2910,7 @@ class _ControlMirror(_DrawableMirror):
2922
2910
  - dimensions: setRect(), setPath(), etc. -> setFixedSize()
2923
2911
  - color: [r, g, b] -> stylesheets
2924
2912
 
2925
- Controls have no hit-testing (contains/intersects/encloses) because QWidgets
2926
- handle their own input directly.
2913
+ Controls handle their own input directly via Qt's widget event system.
2927
2914
 
2928
2915
  Concrete classes must:
2929
2916
  1. Call super().__init__(objectId, guiRenderer, args) first.
@@ -2964,48 +2951,6 @@ class _ControlMirror(_DrawableMirror):
2964
2951
  def _setRotation(self, args, responseId):
2965
2952
  pass # Controls cannot be rotated; gui.py warns at the call site
2966
2953
 
2967
- # ── Hit testing ────────────────────────────────────────────────────────────
2968
-
2969
- def _contains(self, args, responseId):
2970
- """Returns True if the point (x, y) is inside this widget's bounding box."""
2971
- x = args.get('x', 0)
2972
- y = args.get('y', 0)
2973
- rect = QtCore.QRectF(self.qObject.geometry())
2974
- result = rect.contains(QtCore.QPointF(x, y))
2975
- self.guiRenderer.sendResponse(responseId, [result])
2976
-
2977
- def _intersects(self, args, responseId):
2978
- """Returns True if this widget's bounding box intersects another item."""
2979
- otherObjectId = args.get('otherObjectId')
2980
- otherMirror = self.guiRenderer._objectRegistry.get(otherObjectId)
2981
- result = False
2982
-
2983
- if otherMirror is not None:
2984
- rectA = QtCore.QRectF(self.qObject.geometry())
2985
- if isinstance(otherMirror, _ControlMirror):
2986
- rectB = QtCore.QRectF(otherMirror.qObject.geometry())
2987
- else:
2988
- rectB = otherMirror.qObject.sceneBoundingRect()
2989
- result = rectA.intersects(rectB)
2990
-
2991
- self.guiRenderer.sendResponse(responseId, [result])
2992
-
2993
- def _encloses(self, args, responseId):
2994
- """Returns True if this widget's bounding box fully encloses another item."""
2995
- otherObjectId = args.get('otherObjectId')
2996
- otherMirror = self.guiRenderer._objectRegistry.get(otherObjectId)
2997
- result = False
2998
-
2999
- if otherMirror is not None:
3000
- rectA = QtCore.QRectF(self.qObject.geometry())
3001
- if isinstance(otherMirror, _ControlMirror):
3002
- rectB = QtCore.QRectF(otherMirror.qObject.geometry())
3003
- else:
3004
- rectB = otherMirror.qObject.sceneBoundingRect()
3005
- result = rectA.contains(rectB)
3006
-
3007
- self.guiRenderer.sendResponse(responseId, [result])
3008
-
3009
2954
  # ── Size ───────────────────────────────────────────────────────────────────
3010
2955
 
3011
2956
  def _getSize(self, args, responseId):
@@ -306,9 +306,9 @@ class _RealtimeAudioPlayer:
306
306
  self.loopRegionStartFrame = max(0.0, float(loopRegionStartFrame))
307
307
  self.loopRegionEndFrame = float(loopRegionEndFrame) if loopRegionEndFrame is not None else -1.0
308
308
 
309
- # adjust the loop region end frame so it does not exceed the last valid frame in the audio file
309
+ # adjust the loop region end frame so it does not exceed the end of the audio file
310
310
  if self.loopRegionEndFrame > 0:
311
- self.loopRegionEndFrame = min(self.loopRegionEndFrame, self.numFrames - 1 if self.numFrames > 0 else 0.0)
311
+ self.loopRegionEndFrame = min(self.loopRegionEndFrame, float(self.numFrames))
312
312
 
313
313
  # ensure the loop region is valid: if the start frame is after or equal to the end frame, reset to default (full file)
314
314
  if self.numFrames > 0 and self.loopRegionEndFrame > 0 and self.loopRegionStartFrame >= self.loopRegionEndFrame:
@@ -689,7 +689,8 @@ class _RealtimeAudioPlayer:
689
689
 
690
690
  # determine where playback should end for this segment
691
691
  # NOTE: supports three modes: natural EOF, loop region end, or play(size) duration
692
- effectiveSegmentEndFrame = numAudioFrames - 1
692
+ # effectiveSegmentEndFrame is the exclusive end (first frame NOT to play)
693
+ effectiveSegmentEndFrame = float(numAudioFrames)
693
694
  if looping and self.loopRegionEndFrame > 0:
694
695
  effectiveSegmentEndFrame = self.loopRegionEndFrame
695
696
  elif not looping and self.targetEndSourceFrame > 0:
@@ -699,14 +700,13 @@ class _RealtimeAudioPlayer:
699
700
  # NOTE: batch boundary detection avoids per-sample checks (major optimization)
700
701
  framesToProcess = frames
701
702
  willHitBoundary = False
702
- boundaryFrame = -1
703
703
 
704
704
  if effectiveSegmentEndFrame > 0:
705
705
  framesToEndpoint = (effectiveSegmentEndFrame - self.playbackPosition) / rateFactor
706
- if framesToEndpoint < frames and framesToEndpoint > 0:
706
+ if 0 <= framesToEndpoint < frames:
707
707
  willHitBoundary = True
708
- boundaryFrame = int(np.floor(framesToEndpoint))
709
- framesToProcess = min(frames, boundaryFrame + 1)
708
+ # ceil gives the number of frames that stay strictly within the endpoint
709
+ framesToProcess = min(frames, int(np.ceil(framesToEndpoint)))
710
710
 
711
711
  ###########################################################################
712
712
  # PHASE 2: PITCH SHIFTING VIA TIME-STRETCH INTERPOLATION
@@ -856,7 +856,8 @@ class _RealtimeAudioPlayer:
856
856
  outdata[framesToProcess:, :] = 0.0
857
857
 
858
858
  # safety - fill any unfilled portion with silence
859
- if framesToProcess < frames and self.isPlaying:
859
+ # skip when willHitBoundary is True: boundary handling already filled remainder (via recursion or explicit zero-fill)
860
+ if framesToProcess < frames and self.isPlaying and not willHitBoundary:
860
861
  outdata[framesToProcess:, :] = 0.0
861
862
 
862
863
  # update pan smoothing (prevents clicks from abrupt pan changes)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CreativePython
3
- Version: 1.1.5
3
+ Version: 1.2
4
4
  Summary: A Python-based software environment for developing algorithmic art projects.
5
5
  Author-email: "Dr. Bill Manaris" <manaris@cofc.edu>, Taj Ballinger <ballingertj@g.cofc.edu>, Trevor Ritchie <ritchiets@g.cofc.edu>
6
6
  License: MIT License
@@ -3,6 +3,7 @@ LICENSE-PSF
3
3
  MANIFEST.in
4
4
  README.md
5
5
  pyproject.toml
6
+ src/animation.py
6
7
  src/gui.py
7
8
  src/iannix.py
8
9
  src/image.py
@@ -93,6 +94,8 @@ tests/testGameboard.py
93
94
  tests/testHitTesting.py
94
95
  tests/testPeer.py
95
96
  tests/testToolTips.py
97
+ tests/test_crescendo.py
98
+ tests/test_crescendo_import.py
96
99
  tests/test_keyEvent.py
97
100
  tests/test_midi.py
98
101
  tests/test_osc.py
@@ -1,4 +1,5 @@
1
1
  CreativePython
2
+ animation
2
3
  bin
3
4
  gui
4
5
  iannix