framerudiments 0.4.2__tar.gz → 0.4.4__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 (259) hide show
  1. {framerudiments-0.4.2 → framerudiments-0.4.4}/AGENTS.md +8 -0
  2. {framerudiments-0.4.2/src/framerudiments.egg-info → framerudiments-0.4.4}/PKG-INFO +6 -5
  3. {framerudiments-0.4.2 → framerudiments-0.4.4}/README.md +5 -4
  4. {framerudiments-0.4.2 → framerudiments-0.4.4}/RELEASE_NOTES.md +19 -0
  5. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/natural-materials.md +12 -4
  6. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/spectrum.md +3 -1
  7. {framerudiments-0.4.2 → framerudiments-0.4.4}/pyproject.toml +1 -1
  8. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/__init__.py +1 -1
  9. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_renderer.py +80 -14
  10. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/rudiment_renderers.py +6 -1
  11. {framerudiments-0.4.2 → framerudiments-0.4.4/src/framerudiments.egg-info}/PKG-INFO +6 -5
  12. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_fire_methods.py +20 -0
  13. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_spectrum.py +12 -0
  14. {framerudiments-0.4.2 → framerudiments-0.4.4}/LICENSE +0 -0
  15. {framerudiments-0.4.2 → framerudiments-0.4.4}/MANIFEST.in +0 -0
  16. {framerudiments-0.4.2 → framerudiments-0.4.4}/analysis-releases.json +0 -0
  17. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/ECOSYSTEM.md +0 -0
  18. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/FrameRudiments-PATCH_WORKFLOW.md +0 -0
  19. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/annular-modulation.md +0 -0
  20. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/authoring-workspace.md +0 -0
  21. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/beat-alignment.md +0 -0
  22. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/controlled-audio-response.md +0 -0
  23. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/expanded-effects.md +0 -0
  24. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/extraction.md +0 -0
  25. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/full-preset-analysis.md +0 -0
  26. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/hybrid-preset-analysis.md +0 -0
  27. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/keyframed-mattes.md +0 -0
  28. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/beat-alignment-evidence.json +0 -0
  29. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/beat-alignment.mp4 +0 -0
  30. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/beat-alignment.png +0 -0
  31. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/demo-transcript.md +0 -0
  32. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/effects-pipeline.svg +0 -0
  33. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/full-frame-comparison.png +0 -0
  34. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/full-frame-demo.mp4 +0 -0
  35. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/media-evidence.json +0 -0
  36. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/natural-materials.mp4 +0 -0
  37. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/natural-materials.png +0 -0
  38. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/preset-test-cards.png +0 -0
  39. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/rangoli-transcript.md +0 -0
  40. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/rangoli.mp4 +0 -0
  41. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/rangoli.png +0 -0
  42. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/test-card-inputs/colour-bars.png +0 -0
  43. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/test-card-inputs/grid-rings.png +0 -0
  44. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/media/test-card-inputs/text-card.png +0 -0
  45. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/multi-stem-modulation.md +0 -0
  46. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/music-reactive-3d.md +0 -0
  47. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/particle-smoke.md +0 -0
  48. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/phase3-compositing.md +0 -0
  49. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/preset-behaviour.md +0 -0
  50. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/preset-profiling.md +0 -0
  51. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/project-format.md +0 -0
  52. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/projectm-local-runtime.md +0 -0
  53. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/quality-effects.md +0 -0
  54. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/rangoli.md +0 -0
  55. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/rich-lyric-effects.md +0 -0
  56. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/rudiment-renderers.md +0 -0
  57. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/scoped-post-effects.md +0 -0
  58. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/solid-spatial-renderers.md +0 -0
  59. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/spatial-renderers.md +0 -0
  60. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/spatiotemporal-supersampling.md +0 -0
  61. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/sprite-visualisations.md +0 -0
  62. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/stem-sources-and-masks.md +0 -0
  63. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/variable-bpm-word-subtitles.md +0 -0
  64. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/visualisation-atlas.md +0 -0
  65. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/visualisation-coverage-matrix.md +0 -0
  66. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/visualiser-routing.md +0 -0
  67. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/visualisers.md +0 -0
  68. {framerudiments-0.4.2 → framerudiments-0.4.4}/docs/workflow-media.md +0 -0
  69. {framerudiments-0.4.2 → framerudiments-0.4.4}/integration/test_fire_volume_gpu.py +0 -0
  70. {framerudiments-0.4.2 → framerudiments-0.4.4}/integration/test_music_spatial_gpu.py +0 -0
  71. {framerudiments-0.4.2 → framerudiments-0.4.4}/integration/test_preset_analysis.py +0 -0
  72. {framerudiments-0.4.2 → framerudiments-0.4.4}/integration/test_released_preset_analysis.py +0 -0
  73. {framerudiments-0.4.2 → framerudiments-0.4.4}/integration/test_visualisers_gpu.py +0 -0
  74. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/Run-ControlledPresetAnalysis.ps1 +0 -0
  75. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/Run-FullPresetAnalysis.ps1 +0 -0
  76. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/Run-HybridPresetAnalysis.ps1 +0 -0
  77. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/Run-PresetBehaviourAnalysis.ps1 +0 -0
  78. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/authoring_preview.py +0 -0
  79. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/bake_mantaflow_fire.py +0 -0
  80. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/build_sprite_assets.py +0 -0
  81. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/check_projectm_runtime.py +0 -0
  82. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/check_release_packages.py +0 -0
  83. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/controlled_preset_analysis.py +0 -0
  84. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/ensure_projectm_runtime.py +0 -0
  85. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/full_preset_analysis.py +0 -0
  86. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_particle_gallery.py +0 -0
  87. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_post_effect_gallery.py +0 -0
  88. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_quality_gallery.py +0 -0
  89. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_rich_lyric_gallery.py +0 -0
  90. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_sprite_gallery.py +0 -0
  91. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_supersampling_gallery.py +0 -0
  92. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_timing_gallery.py +0 -0
  93. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_visualisation_gallery.py +0 -0
  94. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/generate_visualiser_routing_gallery.py +0 -0
  95. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/install_analysis_releases.py +0 -0
  96. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/prepare_fire_atlas.py +0 -0
  97. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/prepare_pixelcue_taggers.py +0 -0
  98. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/preset_behaviour_pipeline.py +0 -0
  99. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/profile_visualisation_coverage.py +0 -0
  100. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/profile_visualisers.py +0 -0
  101. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/projectm_dll_diagnostics.py +0 -0
  102. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/render_natural_material_demo.py +0 -0
  103. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/repair_projectm_mingw.py +0 -0
  104. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/select_projectm_generator.py +0 -0
  105. {framerudiments-0.4.2 → framerudiments-0.4.4}/scripts/validate_patch.py +0 -0
  106. {framerudiments-0.4.2 → framerudiments-0.4.4}/setup.cfg +0 -0
  107. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/__main__.py +0 -0
  108. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/advanced_renderers.py +0 -0
  109. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/atlas.py +0 -0
  110. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/audio_probes.py +0 -0
  111. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/beat_alignment.py +0 -0
  112. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/beat_timeline.py +0 -0
  113. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/choreography.py +0 -0
  114. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/circular_renderers.py +0 -0
  115. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/cli.py +0 -0
  116. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/compositor.py +0 -0
  117. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/controlled_profiling.py +0 -0
  118. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/demo.py +0 -0
  119. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/effect_catalogue.py +0 -0
  120. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/effect_renderers.py +0 -0
  121. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/effects.py +0 -0
  122. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/encoding.py +0 -0
  123. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_assets/mantaflow/NOTICE.md +0 -0
  124. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_assets/mantaflow/atlas.json +0 -0
  125. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_assets/mantaflow/flames.png +0 -0
  126. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_assets/marburg/NOTICE.md +0 -0
  127. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_assets/marburg/atlas.json +0 -0
  128. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_assets/marburg/flames.png +0 -0
  129. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_assets.py +0 -0
  130. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_fluid.py +0 -0
  131. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/fire_volume.py +0 -0
  132. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/flight.py +0 -0
  133. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/gpu_spatial_backend.py +0 -0
  134. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/io.py +0 -0
  135. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/keyframes.py +0 -0
  136. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/layer_animation.py +0 -0
  137. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/layer_displacement.py +0 -0
  138. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/layer_masks.py +0 -0
  139. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/lyric_indicator.py +0 -0
  140. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/modulation.py +0 -0
  141. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/motion.py +0 -0
  142. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/music_spatial_renderers.py +0 -0
  143. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/natural_materials.py +0 -0
  144. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/output_quality.py +0 -0
  145. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/particle_engine.py +0 -0
  146. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/post_effects.py +0 -0
  147. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/preset_library.py +0 -0
  148. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/preset_profiling.py +0 -0
  149. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/profile_analysis.py +0 -0
  150. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/project.py +0 -0
  151. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/projectm_backend.py +0 -0
  152. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/quality_effects.py +0 -0
  153. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/rangoli_renderer.py +0 -0
  154. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/render.py +0 -0
  155. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/rich_subtitles.py +0 -0
  156. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/scoped_effects.py +0 -0
  157. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/solid_spatial_renderers.py +0 -0
  158. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/spatial_renderers.py +0 -0
  159. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/spectrum.py +0 -0
  160. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/arcade.png +0 -0
  161. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/crystal.png +0 -0
  162. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/fire.png +0 -0
  163. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/generated-neon.png +0 -0
  164. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/glass.png +0 -0
  165. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/hud.png +0 -0
  166. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/ice.png +0 -0
  167. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/lcd-amber.png +0 -0
  168. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/lcd-green.png +0 -0
  169. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/lcd.png +0 -0
  170. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/led.png +0 -0
  171. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/manifest.json +0 -0
  172. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/metal.png +0 -0
  173. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/mono.png +0 -0
  174. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/neon.png +0 -0
  175. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/pixel.png +0 -0
  176. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/plasma.png +0 -0
  177. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_assets/vfd.png +0 -0
  178. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_catalogue.py +0 -0
  179. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/sprite_renderers.py +0 -0
  180. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/stem_blend.py +0 -0
  181. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/stemlab.py +0 -0
  182. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/subtitles.py +0 -0
  183. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/super_sampling.py +0 -0
  184. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/terrain_field.py +0 -0
  185. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/timeline.py +0 -0
  186. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visual_style.py +0 -0
  187. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualisation_catalogue.py +0 -0
  188. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/butterchurn-page.js +0 -0
  189. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/butterchurn-worker.mjs +0 -0
  190. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/feedback.json +0 -0
  191. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/feedback.milk +0 -0
  192. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/julia.json +0 -0
  193. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/julia.milk +0 -0
  194. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/kaleidoscope.json +0 -0
  195. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/kaleidoscope.milk +0 -0
  196. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/package-lock.json +0 -0
  197. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/package.json +0 -0
  198. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_assets/rangoli-background.milk +0 -0
  199. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_routing.py +0 -0
  200. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualiser_setup.py +0 -0
  201. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/visualisers.py +0 -0
  202. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments/word_timing.py +0 -0
  203. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments.egg-info/SOURCES.txt +0 -0
  204. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments.egg-info/dependency_links.txt +0 -0
  205. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments.egg-info/entry_points.txt +0 -0
  206. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments.egg-info/requires.txt +0 -0
  207. {framerudiments-0.4.2 → framerudiments-0.4.4}/src/framerudiments.egg-info/top_level.txt +0 -0
  208. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/conftest.py +0 -0
  209. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_adapters.py +0 -0
  210. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_advanced_renderers.py +0 -0
  211. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_authoring_preview.py +0 -0
  212. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_beat_alignment.py +0 -0
  213. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_behaviour_pipeline.py +0 -0
  214. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_checkpoint_batches.py +0 -0
  215. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_controlled_audio.py +0 -0
  216. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_coverage_matrix.py +0 -0
  217. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_default_antialias.py +0 -0
  218. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_effect_batch.py +0 -0
  219. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_extracted_helpers.py +0 -0
  220. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_flight.py +0 -0
  221. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_full_preset_analysis.py +0 -0
  222. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_gpu_ci_isolation.py +0 -0
  223. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_hybrid_launcher.py +0 -0
  224. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_keyframes.py +0 -0
  225. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_layer_masks.py +0 -0
  226. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_lyrics.py +0 -0
  227. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_music_spatial_pipeline.py +0 -0
  228. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_music_spatial_renderers.py +0 -0
  229. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_natural_materials.py +0 -0
  230. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_particle_engine.py +0 -0
  231. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_patch_validation.py +0 -0
  232. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_phase3_animation.py +0 -0
  233. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_phase3_gpu_injection.py +0 -0
  234. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_pipeline.py +0 -0
  235. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_preset_profiling.py +0 -0
  236. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_projectm_dll_diagnostics.py +0 -0
  237. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_projectm_generator.py +0 -0
  238. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_projectm_runtime_preflight.py +0 -0
  239. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_pytest_temp_isolation.py +0 -0
  240. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_quality_effects.py +0 -0
  241. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_rangoli_renderer.py +0 -0
  242. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_release_metadata.py +0 -0
  243. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_repair_projectm_mingw.py +0 -0
  244. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_rich_subtitles.py +0 -0
  245. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_rudiment_renderers.py +0 -0
  246. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_scoped_effects.py +0 -0
  247. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_solid_spatial_integration.py +0 -0
  248. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_solid_spatial_renderers.py +0 -0
  249. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_spatial_renderers.py +0 -0
  250. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_sprite_renderers.py +0 -0
  251. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_stem_blend.py +0 -0
  252. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_stem_source_integration.py +0 -0
  253. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_supersampling.py +0 -0
  254. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_timeline.py +0 -0
  255. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_variable_bpm_words.py +0 -0
  256. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_visualisation_modulation.py +0 -0
  257. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_visualiser_routing.py +0 -0
  258. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_visualisers.py +0 -0
  259. {framerudiments-0.4.2 → framerudiments-0.4.4}/tests/test_windows_launcher_splitpath.py +0 -0
@@ -22,3 +22,11 @@ and immutable assets. New publication destinations still require user authority.
22
22
  ## Full ecosystem and release preflight
23
23
 
24
24
  Before every new workflow run, check the latest stable official release and observe the actual runtime used by the job. Follow [the shared preflight](https://github.com/kieransimkin/DanceFlow/blob/main/docs/workflows/release-preflight.md) and this project's [people-and-agent guide](docs/ECOSYSTEM.md). Review the complete relevant capability inventory rather than only a familiar command. Consider every DanceFlow component against the actual task; use and compose all applicable supported capabilities, and improve a concrete reusable gap in its canonical repository. Record why existing capabilities suffice when no change is justified. No catalogue checklist expands the task, authorises model downloads/training, or proves integration. Preserve exact source/package identity, clocks, model-local storage, privacy, costs, immutable releases and separate deployment gates.
25
+
26
+ ## Preflight temporary-directory access
27
+
28
+ Before any long, costly, model-backed, media-rendering, build, test, packaging or release command that creates private or atomic temporary directories, run a disposable preflight with the exact runtime, user or sandbox, and intended output parent. The probe must create a unique directory, write and read a file, atomically rename it, then remove the file and directory.
29
+
30
+ Keep temporary data and caches under the owning project on `Z:`; model weights and model-specific caches remain under that project's ignored `models/`. If the probe returns `WinError 5`, access denied, a rename failure or a cleanup failure, stop before the expensive command. Preserve the exact error and use the documented narrowly approved unrestricted recovery from the outset only when the task requires it. Use a fresh output directory and do not repeat the already-failing full job in the restricted shell.
31
+
32
+ After any unrestricted recovery, verify that the final output is readable and writable from the normal managed environment. If it is not, copy the completed bytes into a pre-created project-local directory that passed the probe, verify hashes, and retain the original failure and recovery evidence. Do not edit global ACLs, switch caches to `C:`, terminate unrelated processes or claim the underlying permission problem is fixed. Repositories that own temporary-directory creation should provide and test a reusable preflight helper rather than relying only on this instruction.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: framerudiments
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Deterministic Shorts and lyric visualisers using DanceRudiments and StemLab evidence.
5
5
  Author: Kieran Simkin
6
6
  License-Expression: MIT
@@ -33,9 +33,10 @@ Dynamic: license-file
33
33
 
34
34
  ## Five fire methods and ice
35
35
 
36
- Version 0.4.2 adds filmed flame, animated flame sprites, a bounded 2D fluid
37
- solver, GPU volumetric rays, Blender/Mantaflow bake playback, scorched artwork burn-away,
38
- fractured ice and frost. Use ordinary material layers or the reusable
36
+ Version 0.4.3 adds evidence-recorded three-stop fire palettes, an optional
37
+ vertical tip bias applied after burn-front placement, and less repetitive
38
+ deterministic layouts for filmed and Mantaflow sources. The five fire methods,
39
+ scorched artwork burn-away, fractured ice and frost remain available through ordinary material layers or the reusable
39
40
  `FireRenderer` and `NaturalMaterial` APIs. Source clocks, clean text, transparent edges and
40
41
  seek-safe rendering remain explicit. See [natural materials](docs/natural-materials.md).
41
42
 
@@ -151,7 +152,7 @@ its own tone, sprite, LRC, CUE and four-dimensional recording.
151
152
  stills, a poster, phone preview, input/output hashes, commands and logs.
152
153
 
153
154
  The [spectrum guide](docs/spectrum.md) covers saved StemLab Fourier calibration,
154
- frequency labels, peak controls and per-frame JSONL effect signals. Run
155
+ frequency labels, independent fixed-grid visibility, peak controls and per-frame JSONL effect signals. Run
155
156
  `framerudiments renderers` for the current built-in names. The optional
156
157
  `demo --spectrum` generator uses StemLab 1.6+; rendering its saved recording
157
158
  keeps the core package lightweight.
@@ -2,9 +2,10 @@
2
2
 
3
3
  ## Five fire methods and ice
4
4
 
5
- Version 0.4.2 adds filmed flame, animated flame sprites, a bounded 2D fluid
6
- solver, GPU volumetric rays, Blender/Mantaflow bake playback, scorched artwork burn-away,
7
- fractured ice and frost. Use ordinary material layers or the reusable
5
+ Version 0.4.3 adds evidence-recorded three-stop fire palettes, an optional
6
+ vertical tip bias applied after burn-front placement, and less repetitive
7
+ deterministic layouts for filmed and Mantaflow sources. The five fire methods,
8
+ scorched artwork burn-away, fractured ice and frost remain available through ordinary material layers or the reusable
8
9
  `FireRenderer` and `NaturalMaterial` APIs. Source clocks, clean text, transparent edges and
9
10
  seek-safe rendering remain explicit. See [natural materials](docs/natural-materials.md).
10
11
 
@@ -120,7 +121,7 @@ its own tone, sprite, LRC, CUE and four-dimensional recording.
120
121
  stills, a poster, phone preview, input/output hashes, commands and logs.
121
122
 
122
123
  The [spectrum guide](docs/spectrum.md) covers saved StemLab Fourier calibration,
123
- frequency labels, peak controls and per-frame JSONL effect signals. Run
124
+ frequency labels, independent fixed-grid visibility, peak controls and per-frame JSONL effect signals. Run
124
125
  `framerudiments renderers` for the current built-in names. The optional
125
126
  `demo --spectrum` generator uses StemLab 1.6+; rendering its saved recording
126
127
  keeps the core package lightweight.
@@ -1,3 +1,22 @@
1
+ # FrameRudiments 0.4.4
2
+
3
+ - Spectrum views accept a validated `style.grid` boolean. Set it to `false` to remove the three fixed dB guide lines while retaining calibrated bars and moving peak holds; label visibility remains independent.
4
+ - The existing default remains `true` for backward compatibility. Documentation and focused regression coverage include the no-grid, no-label presentation used by clean campaign visualisers.
5
+
6
+ # FrameRudiments 0.4.3
7
+
8
+ - Atlas fire accepts an evidence-recorded three-stop CSS palette ordered as
9
+ shadow, body and core. An optional bounded `palette_vertical` bias can carry
10
+ the shadow colour into visible flame tongues after burn-front placement while
11
+ preserving the source alpha silhouette.
12
+ - Filmed and Mantaflow layouts vary deterministic jet scale, playback rate,
13
+ phase, mirroring, angle and placement across wide surfaces, reducing obvious
14
+ repeated strips without introducing a new source clock.
15
+ - Invalid palette shapes, colours and bounds fail closed. The focused fire
16
+ suite, configured full suite, package checks and clean-wheel palette smoke
17
+ test cover the reusable contract.
18
+ - [Actual synthetic still/video, API, settings and limitations](docs/natural-materials.md).
19
+
1
20
  # FrameRudiments 0.4.2
2
21
 
3
22
  - Five selectable fire routes: hash-pinned CC0 filmed flame, animated flame
@@ -1,6 +1,6 @@
1
1
  # Five fire methods and ice
2
2
 
3
- FrameRudiments 0.4.2 offers five distinct fire routes through one RGBA and
3
+ FrameRudiments 0.4.3 offers five distinct fire routes through one RGBA and
4
4
  artwork-burn API. Recorded flame preserves photographed detail; sprite
5
5
  particles rearrange that detail; a 2D fluid solver, a GPU volume renderer and
6
6
  a Blender/Mantaflow bake offer different degrees of simulation and control.
@@ -49,7 +49,9 @@ assets fail clearly; methods never silently substitute another backend.
49
49
  from framerudiments import FireRenderer, NaturalMaterial
50
50
 
51
51
  fire = FireRenderer(ticket.size, method="sprites", seed=21,
52
- resolution=0.75, warmth=1.0, glow=0.1)
52
+ resolution=0.75, warmth=1.0, glow=0.1,
53
+ palette=["#531178", "#ef5b16", "#ffd25c"],
54
+ palette_vertical=0.7)
53
55
  ice = NaturalMaterial(ticket.size, "ice", seed=21, resolution=0.75)
54
56
  try:
55
57
  burning = fire.burn(ticket, source_seconds, progress=0.5)
@@ -87,12 +89,18 @@ source-to-asset time mapping in the scene if a bake represents another interval.
87
89
 
88
90
  Shared controls are `seed`, `speed`, `glow` and `opacity`. `resolution` declares
89
91
  the synthetic working tile scale (0.1–1). Atlas detail remains bounded by its
90
- actual cell resolution. `flame_height` controls the tiled footage/bake layout;
92
+ actual cell resolution. The footage route deterministically varies jet scale, playback rate, phase, mirroring, angle and placement so wide surfaces do not expose an evenly repeated source silhouette. `flame_height` controls the overlapping footage/bake layout;
91
93
  sprite shape and simulation shape are owned by their respective backends.
92
94
  The legacy noise `scale`/`turbulence` controls apply to `stylized`, and to the
93
95
  shared scorched burn mask, rather than altering recorded motion. `warmth`
94
96
  (0–1) grades only atlas-based methods, preserves alpha and is a creative colour
95
- grade, never a new source or temperature observation.
97
+ grade, never a new source or temperature observation. It can blend the original
98
+ emission towards the default hot grade or an explicit
99
+ three-stop CSS `palette` ordered shadow, body and core. The selected colours
100
+ are retained in renderer evidence; the palette changes RGB only and preserves
101
+ the source alpha silhouette. `palette_vertical` (0–1) optionally biases the
102
+ shadow colour into higher flame tongues while retaining the energy-led body and
103
+ core grade; it is useful when art direction calls for coloured flame tips.
96
104
 
97
105
  Simulation controls are explicit `backend_settings`: `fluid_2d` accepts
98
106
  `simulation_hz`, `resolution` and `warmup`; `volume` accepts `steps`. Atlas
@@ -26,7 +26,8 @@ the ordinary FrameRudiments dependencies.
26
26
  "min_peak_db": -60,
27
27
  "min_peak_contrast_db": 3,
28
28
  "min_peak_distance": 2,
29
- "labels": true
29
+ "labels": true,
30
+ "grid": true
30
31
  }
31
32
  }
32
33
  ```
@@ -112,6 +113,7 @@ In addition to the common `colours`, `padding` and `background`, spectrum accept
112
113
  | `gap` | 1 pixel, nonnegative |
113
114
  | `peak_colour`, `label_colour` | CSS colours |
114
115
  | `labels`, `font_size` | true; responsive default font size, explicit range 6..128 pixels |
116
+ | `grid` | true; set false to omit the three horizontal dB reference lines while retaining bars and peak holds |
115
117
 
116
118
  Every render/preflight streams active spectrum reports into
117
119
  `rudiment-analysis.jsonl`, binding the sidecar hash in the final manifest.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "framerudiments"
7
- version = "0.4.2"
7
+ version = "0.4.4"
8
8
  description = "Deterministic Shorts and lyric visualisers using DanceRudiments and StemLab evidence."
9
9
  requires-python = ">=3.11"
10
10
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  """Offline frame composition on a traceable musical clock."""
2
2
 
3
- __version__ = "0.4.2"
3
+ __version__ = "0.4.4"
4
4
 
5
5
  from .project import Project
6
6
  from .render import render, preflight
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  import math
5
5
  from types import SimpleNamespace
6
6
 
7
- from PIL import Image, ImageFilter
7
+ from PIL import Image, ImageColor, ImageFilter
8
8
 
9
9
  from .natural_materials import NaturalMaterial
10
10
  from .io import file_ref, number, sha256
@@ -24,7 +24,17 @@ def builtin_asset(name="marburg"):
24
24
 
25
25
  class FireRenderer(NaturalMaterial):
26
26
  def __init__(
27
- self, size, method="footage", *, asset=None, repeat=True, warmth=0, backend_settings=None, **settings
27
+ self,
28
+ size,
29
+ method="footage",
30
+ *,
31
+ asset=None,
32
+ repeat=True,
33
+ warmth=0,
34
+ palette=None,
35
+ palette_vertical=0,
36
+ backend_settings=None,
37
+ **settings,
28
38
  ):
29
39
  if method not in METHODS:
30
40
  raise ValueError(f"Unknown fire method; choose {METHODS}")
@@ -34,6 +44,20 @@ class FireRenderer(NaturalMaterial):
34
44
  self.method = method
35
45
  self.closed = False
36
46
  self.warmth = number(warmth, "fire warmth grade", minimum=0, maximum=1)
47
+ if palette is not None:
48
+ if not isinstance(palette, (list, tuple)) or len(palette) != 3:
49
+ raise ValueError("Fire palette must contain exactly three CSS colours")
50
+ try:
51
+ self.palette = tuple(ImageColor.getcolor(value, "RGB") for value in palette)
52
+ except (TypeError, ValueError) as exc:
53
+ raise ValueError("Fire palette must contain exactly three CSS colours") from exc
54
+ self.palette_source = list(palette)
55
+ else:
56
+ self.palette = None
57
+ self.palette_source = None
58
+ self.palette_vertical = number(
59
+ palette_vertical, "fire vertical palette bias", minimum=0, maximum=1
60
+ )
37
61
  self.repeat = repeat
38
62
  self.atlas = None
39
63
  self.backend = None
@@ -71,6 +95,8 @@ class FireRenderer(NaturalMaterial):
71
95
  self.evidence.update(
72
96
  method=method,
73
97
  warmth_grade=self.warmth,
98
+ palette=self.palette_source,
99
+ palette_vertical=self.palette_vertical,
74
100
  repeat=repeat,
75
101
  asset=self.atlas.evidence if self.atlas else None,
76
102
  backend="opengl"
@@ -168,16 +194,30 @@ class FireRenderer(NaturalMaterial):
168
194
 
169
195
  def _footage(self, seconds):
170
196
  image = Image.new("RGBA", self.size)
171
- h = max(8, round(self.size[1] * self.flame_height))
197
+ base_h = max(8, round(self.size[1] * self.flame_height))
172
198
  cw, ch = self.atlas.cell_size
173
- w = max(4, round(h * cw / ch))
174
- count = math.ceil(self.size[0] / max(1, w * 0.72)) + 1
199
+ base_w = max(4, round(base_h * cw / ch))
200
+ step = max(2, round(base_w * 0.42))
201
+ count = math.ceil(self.size[0] / step) + 3
175
202
  for i in range(count):
176
- t = seconds + ((i * 73 + self.seed * 17) % 233) / 100
203
+ # Vary every jet deterministically so a real source does not become
204
+ # an artificial picket-fence pattern when it spans a wide surface.
205
+ code = (i * 1103515245 + self.seed * 12345) & 0x7FFFFFFF
206
+ scale = 0.72 + ((code >> 4) % 67) / 100
207
+ speed = 0.82 + ((code >> 11) % 39) / 100
208
+ h = max(8, round(base_h * scale))
209
+ w = max(4, round(h * cw / ch))
210
+ t = seconds * speed + ((code >> 17) % 997) / 137
177
211
  tile = self.atlas.frame_at(t, repeat=self.repeat)
178
212
  tile = tile.convert("RGBa").resize((w, h), Image.Resampling.LANCZOS).convert("RGBA")
179
- x = round(i * w * 0.72 - w * 0.2)
180
- y = self.size[1] - h
213
+ if code & 1:
214
+ tile = tile.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
215
+ angle = ((code >> 23) % 7) - 3
216
+ if angle:
217
+ tile = tile.rotate(angle, Image.Resampling.BICUBIC, expand=True)
218
+ jitter_x = ((code >> 7) % max(2, step)) - step / 2
219
+ x = round((i - 1) * step + jitter_x - tile.width * 0.18)
220
+ y = round(self.size[1] - tile.height + (((code >> 14) % 13) - 6) * base_h / 100)
181
221
  image.alpha_composite(tile, (x, y))
182
222
  return image
183
223
 
@@ -211,19 +251,45 @@ class FireRenderer(NaturalMaterial):
211
251
  # photographed/simulated silhouette so blue-screen fringes cannot
212
252
  # leak into a warm fire treatment.
213
253
  energy = np.max(a[:, :, :3], axis=2)
214
- warm = np.dstack(
215
- (
216
- np.ones_like(energy),
217
- np.clip(0.04 + energy * 0.58, 0, 1),
218
- np.clip((energy - 0.84) * 4.5, 0, 1),
254
+ if self.palette:
255
+ low, middle, high = (np.asarray(colour, dtype=np.float32) / 255 for colour in self.palette)
256
+ split = 0.58
257
+ lower_t = np.clip(energy / split, 0, 1)[:, :, None]
258
+ upper_t = np.clip((energy - split) / (1 - split), 0, 1)[:, :, None]
259
+ lower = low + (middle - low) * lower_t
260
+ upper = middle + (high - middle) * upper_t
261
+ warm = np.where((energy < split)[:, :, None], lower, upper)
262
+ # Keep low-energy tongues dark while allowing the hot core to
263
+ # reach the selected highlight. Alpha continues to own shape.
264
+ warm *= (0.28 + 0.72 * energy)[:, :, None]
265
+ else:
266
+ warm = np.dstack(
267
+ (
268
+ np.ones_like(energy),
269
+ np.clip(0.12 + energy * 0.82, 0, 1),
270
+ np.clip((energy - 0.93) * 8, 0, 1),
271
+ )
219
272
  )
220
- )
221
273
  a[:, :, :3] = a[:, :, :3] * (1 - self.warmth) + warm * self.warmth
222
274
  tile = Image.fromarray(np.rint(a * 255).astype(np.uint8))
223
275
  if front != 1:
224
276
  shifted = Image.new("RGBA", self.size)
225
277
  shifted.alpha_composite(tile, (0, round((front - 1) * self.size[1])))
226
278
  tile = shifted
279
+ if self.palette and self.palette_vertical:
280
+ # Apply the tip bias after front placement. Otherwise a moving burn
281
+ # front can translate all cooler graded pixels off-canvas and leave
282
+ # only the body/core colours visible at the consumption edge.
283
+ np = self.np
284
+ a = np.asarray(tile, dtype=np.float32) / 255
285
+ energy = np.max(a[:, :, :3], axis=2)
286
+ low = np.asarray(self.palette[0], dtype=np.float32) / 255
287
+ y = np.linspace(0, 1, a.shape[0], dtype=np.float32)[:, None]
288
+ tip = np.clip((front - y) / max(0.05, self.flame_height), 0, 1)
289
+ tip *= self.palette_vertical
290
+ cool = low[None, None, :] * (0.38 + 0.62 * energy)[:, :, None]
291
+ a[:, :, :3] = a[:, :, :3] * (1 - tip[:, :, None]) + cool * tip[:, :, None]
292
+ tile = Image.fromarray(np.rint(a * 255).astype(np.uint8))
227
293
  factor = amount * self.opacity
228
294
  tile.putalpha(tile.getchannel("A").point(lambda a: round(a * factor)))
229
295
  if self.glow:
@@ -280,6 +280,7 @@ class SpectrumRenderer(_Viewport):
280
280
  "label_colour",
281
281
  "font_size",
282
282
  "labels",
283
+ "grid",
283
284
  },
284
285
  )
285
286
  self.floor = number(style.get("floor_db", -72), "spectrum floor", minimum=-240, maximum=0)
@@ -306,6 +307,9 @@ class SpectrumRenderer(_Viewport):
306
307
  self.labels = style.get("labels", True)
307
308
  if not isinstance(self.labels, bool):
308
309
  raise ValueError("Spectrum labels must be boolean")
310
+ self.grid = style.get("grid", True)
311
+ if not isinstance(self.grid, bool):
312
+ raise ValueError("Spectrum grid must be boolean")
309
313
  self.font_size = integer(
310
314
  style.get("font_size", max(6, min(14, size[0] // 30))), "spectrum font", minimum=6, maximum=128
311
315
  )
@@ -346,7 +350,8 @@ class SpectrumRenderer(_Viewport):
346
350
  return bottom - max(0, min(1, (db - self.floor) / (self.ceiling - self.floor))) * (bottom - top)
347
351
 
348
352
  for db in (self.floor, (self.floor + self.ceiling) / 2, self.ceiling):
349
- draw.line((left, y(db), right, y(db)), fill="#40516a")
353
+ if self.grid:
354
+ draw.line((left, y(db), right, y(db)), fill="#40516a")
350
355
  if self.labels:
351
356
  draw.text(
352
357
  (self.padding, y(db) - self.font_size / 2),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: framerudiments
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Deterministic Shorts and lyric visualisers using DanceRudiments and StemLab evidence.
5
5
  Author: Kieran Simkin
6
6
  License-Expression: MIT
@@ -33,9 +33,10 @@ Dynamic: license-file
33
33
 
34
34
  ## Five fire methods and ice
35
35
 
36
- Version 0.4.2 adds filmed flame, animated flame sprites, a bounded 2D fluid
37
- solver, GPU volumetric rays, Blender/Mantaflow bake playback, scorched artwork burn-away,
38
- fractured ice and frost. Use ordinary material layers or the reusable
36
+ Version 0.4.3 adds evidence-recorded three-stop fire palettes, an optional
37
+ vertical tip bias applied after burn-front placement, and less repetitive
38
+ deterministic layouts for filmed and Mantaflow sources. The five fire methods,
39
+ scorched artwork burn-away, fractured ice and frost remain available through ordinary material layers or the reusable
39
40
  `FireRenderer` and `NaturalMaterial` APIs. Source clocks, clean text, transparent edges and
40
41
  seek-safe rendering remain explicit. See [natural materials](docs/natural-materials.md).
41
42
 
@@ -151,7 +152,7 @@ its own tone, sprite, LRC, CUE and four-dimensional recording.
151
152
  stills, a poster, phone preview, input/output hashes, commands and logs.
152
153
 
153
154
  The [spectrum guide](docs/spectrum.md) covers saved StemLab Fourier calibration,
154
- frequency labels, peak controls and per-frame JSONL effect signals. Run
155
+ frequency labels, independent fixed-grid visibility, peak controls and per-frame JSONL effect signals. Run
155
156
  `framerudiments renderers` for the current built-in names. The optional
156
157
  `demo --spectrum` generator uses StemLab 1.6+; rendering its saved recording
157
158
  keeps the core package lightweight.
@@ -98,6 +98,23 @@ def test_grade_keeps_alpha_at_unshifted_front(atlas):
98
98
  b.close()
99
99
 
100
100
 
101
+ def test_three_stop_palette_grades_fire_without_changing_alpha(atlas):
102
+ palette = ["#531178", "#ef5b16", "#ffd25c"]
103
+ plain = FireRenderer((64, 64), "footage", asset=atlas, warmth=0, glow=0)
104
+ graded = FireRenderer(
105
+ (64, 64), "footage", asset=atlas, warmth=1, palette=palette, palette_vertical=0.7, glow=0
106
+ )
107
+ try:
108
+ a, b = plain.render(0.1, front=1), graded.render(0.1, front=1)
109
+ assert a.getchannel("A").tobytes() == b.getchannel("A").tobytes()
110
+ assert a.tobytes() != b.tobytes()
111
+ assert graded.evidence["palette"] == palette
112
+ assert graded.evidence["palette_vertical"] == 0.7
113
+ finally:
114
+ plain.close()
115
+ graded.close()
116
+
117
+
101
118
  def test_native_sprite_particle_age_selects_frame_and_long_source_is_bounded(atlas):
102
119
  f = FireRenderer((64, 64), "sprites", asset=atlas)
103
120
  try:
@@ -153,6 +170,9 @@ def test_bundled_assets_have_reviewed_provenance_and_no_private_paths():
153
170
  {"backend_settings": []},
154
171
  {"method": "missing"},
155
172
  {"asset": {}, "method": "fluid_2d"},
173
+ {"palette": ["#fff", "#000"]},
174
+ {"palette": ["#fff", "not-a-colour", "#000"]},
175
+ {"palette_vertical": 2},
156
176
  ),
157
177
  )
158
178
  def test_invalid_fire_options_do_not_fallback(settings):
@@ -4,6 +4,7 @@ import os
4
4
 
5
5
  from dancerudiments_authoring.recording import Recording, RecordingClock, write_recording
6
6
  import pytest
7
+ from PIL import ImageColor
7
8
 
8
9
  from framerudiments import Project, render
9
10
  from framerudiments.compositor import Compositor
@@ -93,6 +94,17 @@ def test_peak_hold_decays_deterministically_without_hidden_state(spectrum_projec
93
94
  assert comp.frame(30)[0].tobytes() == first
94
95
 
95
96
 
97
+ def test_spectrum_grid_can_be_omitted_without_removing_bars_or_peak_holds(spectrum_project):
98
+ project, _, _ = spectrum_project
99
+ project.data["layers"][0]["style"] = {"grid": False, "labels": False}
100
+ comp = Compositor(Project(project.data, project.base))
101
+ view = comp.layers[0]
102
+ assert view.renderer.grid is False
103
+ image = view.renderer.render(view.data(comp.context(0)))
104
+ assert ImageColor.getcolor("#40516a", "RGBA") not in set(image.get_flattened_data())
105
+ assert image.getbbox() is not None
106
+
107
+
96
108
  def test_local_peak_plateaus_threshold_contrast_and_minimum_distance():
97
109
  assert peak_indices(
98
110
  [-60, -10, -10, -60, -20, -60], floor=-72, threshold=-50, contrast=3, distance=2, count=3
File without changes