ruby2d 0.12.0 → 1.0.0

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 (619) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +7 -0
  3. data/README.md +141 -0
  4. data/USAGE.md +2435 -0
  5. data/assets/Rakefile +637 -0
  6. data/assets/build_support/CMakeLists.txt +94 -0
  7. data/assets/build_support/mruby/build_config.rb +34 -0
  8. data/assets/build_support/mruby/build_config_wasm.rb +30 -0
  9. data/assets/build_support/sdl_test.c +174 -0
  10. data/assets/deps.yaml +16 -0
  11. data/assets/platform/include/SDL3/SDL.h +91 -0
  12. data/assets/platform/include/SDL3/SDL_assert.h +705 -0
  13. data/assets/platform/include/SDL3/SDL_asyncio.h +550 -0
  14. data/assets/platform/include/SDL3/SDL_atomic.h +692 -0
  15. data/assets/platform/include/SDL3/SDL_audio.h +2370 -0
  16. data/assets/platform/include/SDL3/SDL_begin_code.h +553 -0
  17. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_bits.h +47 -27
  18. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_blendmode.h +59 -55
  19. data/assets/platform/include/SDL3/SDL_camera.h +535 -0
  20. data/assets/platform/include/SDL3/SDL_clipboard.h +331 -0
  21. data/assets/{include/SDL2/close_code.h → platform/include/SDL3/SDL_close_code.h} +9 -8
  22. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_copying.h +3 -1
  23. data/assets/platform/include/SDL3/SDL_cpuinfo.h +374 -0
  24. data/assets/platform/include/SDL3/SDL_dialog.h +343 -0
  25. data/assets/platform/include/SDL3/SDL_dlopennote.h +234 -0
  26. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_egl.h +11 -8
  27. data/assets/platform/include/SDL3/SDL_endian.h +645 -0
  28. data/assets/platform/include/SDL3/SDL_error.h +226 -0
  29. data/assets/platform/include/SDL3/SDL_events.h +1645 -0
  30. data/assets/platform/include/SDL3/SDL_filesystem.h +537 -0
  31. data/assets/platform/include/SDL3/SDL_gamepad.h +1656 -0
  32. data/assets/platform/include/SDL3/SDL_gpu.h +4608 -0
  33. data/assets/platform/include/SDL3/SDL_guid.h +106 -0
  34. data/assets/platform/include/SDL3/SDL_haptic.h +1461 -0
  35. data/assets/platform/include/SDL3/SDL_hidapi.h +571 -0
  36. data/assets/platform/include/SDL3/SDL_hints.h +5001 -0
  37. data/assets/platform/include/SDL3/SDL_init.h +507 -0
  38. data/assets/platform/include/SDL3/SDL_intrin.h +410 -0
  39. data/assets/platform/include/SDL3/SDL_iostream.h +1379 -0
  40. data/assets/platform/include/SDL3/SDL_joystick.h +1385 -0
  41. data/assets/platform/include/SDL3/SDL_keyboard.h +608 -0
  42. data/assets/platform/include/SDL3/SDL_keycode.h +347 -0
  43. data/assets/platform/include/SDL3/SDL_loadso.h +145 -0
  44. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_locale.h +40 -24
  45. data/assets/platform/include/SDL3/SDL_log.h +541 -0
  46. data/assets/platform/include/SDL3/SDL_main.h +704 -0
  47. data/assets/platform/include/SDL3/SDL_main_impl.h +151 -0
  48. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_messagebox.h +85 -48
  49. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_metal.h +29 -29
  50. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_misc.h +13 -12
  51. data/assets/platform/include/SDL3/SDL_mouse.h +813 -0
  52. data/assets/platform/include/SDL3/SDL_mutex.h +1117 -0
  53. data/assets/platform/include/SDL3/SDL_oldnames.h +1339 -0
  54. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengl.h +1247 -278
  55. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengl_glext.h +5 -1
  56. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles.h +6 -7
  57. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2.h +10 -11
  58. data/assets/platform/include/SDL3/SDL_pen.h +198 -0
  59. data/assets/platform/include/SDL3/SDL_pixels.h +1441 -0
  60. data/assets/{include/SDL2/SDL_test_log.h → platform/include/SDL3/SDL_platform.h} +25 -26
  61. data/assets/platform/include/SDL3/SDL_platform_defines.h +497 -0
  62. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_power.h +40 -20
  63. data/assets/platform/include/SDL3/SDL_process.h +441 -0
  64. data/assets/platform/include/SDL3/SDL_properties.h +572 -0
  65. data/assets/platform/include/SDL3/SDL_rect.h +528 -0
  66. data/assets/platform/include/SDL3/SDL_render.h +3028 -0
  67. data/assets/platform/include/SDL3/SDL_revision.h +59 -0
  68. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_scancode.h +59 -68
  69. data/assets/platform/include/SDL3/SDL_sensor.h +321 -0
  70. data/assets/platform/include/SDL3/SDL_stdinc.h +6186 -0
  71. data/assets/platform/include/SDL3/SDL_storage.h +686 -0
  72. data/assets/platform/include/SDL3/SDL_surface.h +1769 -0
  73. data/assets/platform/include/SDL3/SDL_system.h +840 -0
  74. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test.h +16 -22
  75. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_assert.h +27 -34
  76. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_common.h +109 -52
  77. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_compare.h +22 -14
  78. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_crc32.h +27 -30
  79. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_font.h +50 -49
  80. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_fuzzer.h +64 -79
  81. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_harness.h +47 -30
  82. data/assets/platform/include/SDL3/SDL_test_log.h +83 -0
  83. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_md5.h +21 -28
  84. data/assets/{include/SDL2 → platform/include/SDL3}/SDL_test_memory.h +19 -16
  85. data/assets/platform/include/SDL3/SDL_thread.h +602 -0
  86. data/assets/platform/include/SDL3/SDL_time.h +249 -0
  87. data/assets/platform/include/SDL3/SDL_timer.h +454 -0
  88. data/assets/platform/include/SDL3/SDL_touch.h +184 -0
  89. data/assets/platform/include/SDL3/SDL_tray.h +544 -0
  90. data/assets/platform/include/SDL3/SDL_version.h +198 -0
  91. data/assets/platform/include/SDL3/SDL_video.h +3497 -0
  92. data/assets/platform/include/SDL3/SDL_vulkan.h +287 -0
  93. data/assets/platform/include/SDL3_image/SDL_image.h +3328 -0
  94. data/assets/platform/include/SDL3_mixer/SDL_mixer.h +3369 -0
  95. data/assets/platform/include/SDL3_ttf/SDL_textengine.h +181 -0
  96. data/assets/platform/include/SDL3_ttf/SDL_ttf.h +2833 -0
  97. data/assets/{include → platform/include}/mrbconf.h +42 -31
  98. data/assets/{include → platform/include}/mruby/array.h +3 -4
  99. data/assets/{include → platform/include}/mruby/boxing_nan.h +10 -0
  100. data/assets/platform/include/mruby/boxing_no.h +59 -0
  101. data/assets/{include → platform/include}/mruby/boxing_word.h +72 -26
  102. data/assets/platform/include/mruby/class.h +157 -0
  103. data/assets/{include → platform/include}/mruby/common.h +30 -1
  104. data/assets/{include → platform/include}/mruby/compile.h +52 -74
  105. data/assets/{include → platform/include}/mruby/data.h +2 -2
  106. data/assets/{include → platform/include}/mruby/debug.h +12 -5
  107. data/assets/{include → platform/include}/mruby/dump.h +7 -9
  108. data/assets/{include → platform/include}/mruby/endian.h +1 -1
  109. data/assets/platform/include/mruby/error.h +173 -0
  110. data/assets/platform/include/mruby/gc.h +79 -0
  111. data/assets/{include → platform/include}/mruby/hash.h +1 -7
  112. data/assets/platform/include/mruby/internal.h +282 -0
  113. data/assets/platform/include/mruby/irep.h +143 -0
  114. data/assets/{include → platform/include}/mruby/istruct.h +1 -1
  115. data/assets/platform/include/mruby/khash.h +455 -0
  116. data/assets/platform/include/mruby/mempool.h +19 -0
  117. data/assets/{include → platform/include}/mruby/numeric.h +7 -10
  118. data/assets/platform/include/mruby/object.h +51 -0
  119. data/assets/{include → platform/include}/mruby/opcode.h +5 -5
  120. data/assets/{include → platform/include}/mruby/ops.h +19 -6
  121. data/assets/platform/include/mruby/presym/id.h +1447 -0
  122. data/assets/{include → platform/include}/mruby/presym/scanning.h +10 -7
  123. data/assets/platform/include/mruby/presym/table.h +2893 -0
  124. data/assets/platform/include/mruby/presym.h +65 -0
  125. data/assets/platform/include/mruby/proc.h +184 -0
  126. data/assets/{include → platform/include}/mruby/range.h +0 -2
  127. data/assets/{include → platform/include}/mruby/string.h +33 -53
  128. data/assets/platform/include/mruby/throw.h +81 -0
  129. data/assets/{include → platform/include}/mruby/value.h +64 -29
  130. data/assets/{include → platform/include}/mruby/variable.h +6 -23
  131. data/assets/{include → platform/include}/mruby/version.h +7 -7
  132. data/assets/{include → platform/include}/mruby.h +238 -116
  133. data/assets/platform/macos-arm64/bin/mrbc +0 -0
  134. data/assets/platform/macos-arm64/lib/libSDL3.a +0 -0
  135. data/assets/platform/macos-arm64/lib/libSDL3_image.a +0 -0
  136. data/assets/platform/macos-arm64/lib/libSDL3_mixer.a +0 -0
  137. data/assets/platform/macos-arm64/lib/libSDL3_ttf.a +0 -0
  138. data/assets/platform/macos-arm64/lib/libmruby.a +0 -0
  139. data/assets/platform/wasm/lib/libFLAC.a +0 -0
  140. data/assets/platform/wasm/lib/libSDL3.a +0 -0
  141. data/assets/platform/wasm/lib/libSDL3_image.a +0 -0
  142. data/assets/platform/wasm/lib/libSDL3_mixer.a +0 -0
  143. data/assets/platform/wasm/lib/libSDL3_ttf.a +0 -0
  144. data/assets/platform/wasm/lib/libfreetype.a +0 -0
  145. data/assets/platform/wasm/lib/libharfbuzz.a +0 -0
  146. data/assets/platform/wasm/lib/libmruby.a +0 -0
  147. data/assets/platform/wasm/lib/libogg.a +0 -0
  148. data/assets/platform/wasm/lib/libplutosvg.a +0 -0
  149. data/assets/platform/wasm/lib/libplutovg.a +0 -0
  150. data/assets/platform/wasm/lib/libpng.a +0 -0
  151. data/assets/platform/wasm/lib/libpng16.a +0 -0
  152. data/assets/platform/wasm/lib/libvorbis.a +0 -0
  153. data/assets/platform/wasm/lib/libvorbisfile.a +0 -0
  154. data/assets/platform/wasm/lib/libzlibstatic.a +0 -0
  155. data/assets/platform/windows-arm64-mingw-ucrt/bin/mrbc.exe +0 -0
  156. data/assets/platform/windows-arm64-mingw-ucrt/lib/libSDL3.a +0 -0
  157. data/assets/platform/windows-arm64-mingw-ucrt/lib/libSDL3_image.a +0 -0
  158. data/assets/platform/windows-arm64-mingw-ucrt/lib/libSDL3_mixer.a +0 -0
  159. data/assets/platform/windows-arm64-mingw-ucrt/lib/libSDL3_ttf.a +0 -0
  160. data/assets/platform/windows-arm64-mingw-ucrt/lib/libmruby.a +0 -0
  161. data/assets/platform/windows-x86_64-mingw-ucrt/bin/mrbc.exe +0 -0
  162. data/assets/platform/windows-x86_64-mingw-ucrt/lib/libSDL3.a +0 -0
  163. data/assets/platform/windows-x86_64-mingw-ucrt/lib/libSDL3_image.a +0 -0
  164. data/assets/platform/windows-x86_64-mingw-ucrt/lib/libSDL3_mixer.a +0 -0
  165. data/assets/platform/windows-x86_64-mingw-ucrt/lib/libSDL3_ttf.a +0 -0
  166. data/assets/platform/windows-x86_64-mingw-ucrt/lib/libmruby.a +0 -0
  167. data/assets/resources/fonts/outfit/OFL.txt +93 -0
  168. data/assets/resources/fonts/outfit/outfit.ttf +0 -0
  169. data/assets/resources/fonts/roboto_mono/OFL.txt +91 -0
  170. data/assets/resources/fonts/roboto_mono/roboto_mono.ttf +0 -0
  171. data/assets/resources/icons/icon.icns +0 -0
  172. data/assets/resources/icons/icon.png +0 -0
  173. data/assets/resources/spritesheets/License.txt +28 -0
  174. data/assets/resources/spritesheets/spritesheet-backgrounds.png +0 -0
  175. data/assets/resources/spritesheets/spritesheet-backgrounds.xml +16 -0
  176. data/assets/resources/spritesheets/spritesheet-characters.png +0 -0
  177. data/assets/resources/spritesheets/spritesheet-characters.xml +47 -0
  178. data/assets/resources/spritesheets/spritesheet-enemies.png +0 -0
  179. data/assets/resources/spritesheets/spritesheet-enemies.xml +62 -0
  180. data/assets/resources/spritesheets/spritesheet-tiles.png +0 -0
  181. data/assets/resources/spritesheets/spritesheet-tiles.xml +316 -0
  182. data/assets/{wasm → resources/web}/template.html +1 -1
  183. data/assets/target.rb +131 -0
  184. data/bin/ruby2d +281 -82
  185. data/examples/astar.rb +269 -0
  186. data/examples/asteroids.rb +403 -0
  187. data/examples/bezier_editor.rb +96 -0
  188. data/examples/boids.rb +191 -0
  189. data/examples/bouncing_balls.rb +119 -0
  190. data/examples/breakout.rb +186 -0
  191. data/examples/constellations.rb +100 -0
  192. data/examples/dashboard.rb +133 -0
  193. data/examples/double_pendulum.rb +254 -0
  194. data/examples/falling_sand.rb +291 -0
  195. data/examples/fireworks.rb +110 -0
  196. data/examples/fluid_dynamics.rb +340 -0
  197. data/examples/fourier_epicycles.rb +142 -0
  198. data/examples/fractal_tree.rb +326 -0
  199. data/examples/game_of_life.rb +148 -0
  200. data/examples/gamepads.rb +333 -0
  201. data/examples/hill_driver.rb +613 -0
  202. data/examples/logo_animation.rb +251 -0
  203. data/examples/lorenz_attractor.rb +115 -0
  204. data/examples/mandelbrot.rb +207 -0
  205. data/examples/marching_squares.rb +200 -0
  206. data/examples/maze.rb +323 -0
  207. data/examples/nbody.rb +338 -0
  208. data/examples/outrun.rb +391 -0
  209. data/examples/pixel_paint.rb +313 -0
  210. data/examples/platformer.rb +335 -0
  211. data/examples/ray_casting_maze.rb +175 -0
  212. data/examples/shadow_casting.rb +151 -0
  213. data/examples/shape_recognizer.rb +908 -0
  214. data/examples/snake.rb +144 -0
  215. data/examples/sprite_sheets.rb +246 -0
  216. data/examples/swarm.rb +206 -0
  217. data/examples/tsp_annealing.rb +104 -0
  218. data/examples/ui_controls.rb +163 -0
  219. data/examples/wireframe_3d.rb +139 -0
  220. data/ext/ruby2d/audio.c +324 -0
  221. data/ext/ruby2d/canvas.c +1715 -494
  222. data/ext/ruby2d/ext.c +541 -0
  223. data/ext/ruby2d/extconf.rb +122 -145
  224. data/ext/ruby2d/font.c +566 -20
  225. data/ext/ruby2d/fps.c +92 -0
  226. data/ext/ruby2d/image.c +466 -41
  227. data/ext/ruby2d/ruby2d.c +374 -1890
  228. data/ext/ruby2d/ruby2d.h +840 -460
  229. data/ext/ruby2d/shapes.c +931 -101
  230. data/ext/ruby2d/text.c +586 -21
  231. data/ext/ruby2d/window.c +1693 -283
  232. data/lib/ruby2d/atlas_parser.rb +194 -0
  233. data/lib/ruby2d/audio.rb +73 -0
  234. data/lib/ruby2d/benchmark.rb +259 -0
  235. data/lib/ruby2d/bitmap_text.rb +155 -0
  236. data/lib/ruby2d/button.rb +402 -0
  237. data/lib/ruby2d/canvas.rb +703 -279
  238. data/lib/ruby2d/circle.rb +206 -29
  239. data/lib/ruby2d/cli/browser.rb +314 -0
  240. data/lib/ruby2d/cli/build.rb +471 -257
  241. data/lib/ruby2d/cli/colorize.rb +7 -5
  242. data/lib/ruby2d/cli/examples.rb +127 -0
  243. data/lib/ruby2d/cli/launch.rb +24 -39
  244. data/lib/ruby2d/cli/lib_files.rb +48 -0
  245. data/lib/ruby2d/cli/messages.rb +40 -0
  246. data/lib/ruby2d/cli/setup.rb +273 -0
  247. data/lib/ruby2d/cli/static_server.rb +160 -0
  248. data/lib/ruby2d/cli/usage.rb +255 -0
  249. data/lib/ruby2d/color.rb +191 -69
  250. data/lib/ruby2d/core.rb +37 -19
  251. data/lib/ruby2d/deps_help.rb +89 -0
  252. data/lib/ruby2d/dsl.rb +51 -12
  253. data/lib/ruby2d/ellipse.rb +228 -0
  254. data/lib/ruby2d/exceptions.rb +0 -2
  255. data/lib/ruby2d/font.rb +34 -74
  256. data/lib/ruby2d/gamepad.rb +323 -0
  257. data/lib/ruby2d/gem_paths.rb +19 -0
  258. data/lib/ruby2d/image.rb +155 -52
  259. data/lib/ruby2d/interactive.rb +103 -0
  260. data/lib/ruby2d/json_parser.rb +268 -0
  261. data/lib/ruby2d/line.rb +194 -85
  262. data/lib/ruby2d/mruby_compat.rb +12 -0
  263. data/lib/ruby2d/polygon.rb +297 -0
  264. data/lib/ruby2d/polyline.rb +368 -0
  265. data/lib/ruby2d/quad.rb +358 -94
  266. data/lib/ruby2d/rectangle.rb +77 -19
  267. data/lib/ruby2d/renderable.rb +411 -11
  268. data/lib/ruby2d/sprite.rb +471 -180
  269. data/lib/ruby2d/sprite_sheet.rb +64 -0
  270. data/lib/ruby2d/square.rb +29 -17
  271. data/lib/ruby2d/text.rb +196 -65
  272. data/lib/ruby2d/tileset.rb +139 -81
  273. data/lib/ruby2d/triangle.rb +324 -89
  274. data/lib/ruby2d/version.rb +1 -3
  275. data/lib/ruby2d/vertices.rb +62 -85
  276. data/lib/ruby2d/warnings.rb +29 -0
  277. data/lib/ruby2d/window/class_methods.rb +218 -0
  278. data/lib/ruby2d/window/gamepad_events.rb +141 -0
  279. data/lib/ruby2d/window/key_events.rb +74 -0
  280. data/lib/ruby2d/window/mouse_events.rb +171 -0
  281. data/lib/ruby2d/window/object_events.rb +168 -0
  282. data/lib/ruby2d/window.rb +632 -585
  283. data/lib/ruby2d.rb +14 -19
  284. metadata +275 -428
  285. data/assets/app.icns +0 -0
  286. data/assets/include/GLES2/gl2.h +0 -656
  287. data/assets/include/GLES2/gl2ext.h +0 -3949
  288. data/assets/include/GLES2/gl2ext_angle.h +0 -701
  289. data/assets/include/GLES2/gl2platform.h +0 -27
  290. data/assets/include/GLES3/gl3.h +0 -1192
  291. data/assets/include/GLES3/gl31.h +0 -1507
  292. data/assets/include/GLES3/gl32.h +0 -1808
  293. data/assets/include/GLES3/gl3platform.h +0 -27
  294. data/assets/include/KHR/khrplatform.h +0 -290
  295. data/assets/include/SDL2/SDL.h +0 -233
  296. data/assets/include/SDL2/SDL_assert.h +0 -326
  297. data/assets/include/SDL2/SDL_atomic.h +0 -415
  298. data/assets/include/SDL2/SDL_audio.h +0 -1500
  299. data/assets/include/SDL2/SDL_clipboard.h +0 -141
  300. data/assets/include/SDL2/SDL_config.h +0 -61
  301. data/assets/include/SDL2/SDL_config_android.h +0 -194
  302. data/assets/include/SDL2/SDL_config_emscripten.h +0 -218
  303. data/assets/include/SDL2/SDL_config_iphoneos.h +0 -217
  304. data/assets/include/SDL2/SDL_config_macosx.h +0 -277
  305. data/assets/include/SDL2/SDL_config_minimal.h +0 -95
  306. data/assets/include/SDL2/SDL_config_ngage.h +0 -89
  307. data/assets/include/SDL2/SDL_config_os2.h +0 -207
  308. data/assets/include/SDL2/SDL_config_pandora.h +0 -141
  309. data/assets/include/SDL2/SDL_config_windows.h +0 -331
  310. data/assets/include/SDL2/SDL_config_wingdk.h +0 -253
  311. data/assets/include/SDL2/SDL_config_winrt.h +0 -220
  312. data/assets/include/SDL2/SDL_config_xbox.h +0 -235
  313. data/assets/include/SDL2/SDL_cpuinfo.h +0 -594
  314. data/assets/include/SDL2/SDL_endian.h +0 -348
  315. data/assets/include/SDL2/SDL_error.h +0 -163
  316. data/assets/include/SDL2/SDL_events.h +0 -1166
  317. data/assets/include/SDL2/SDL_filesystem.h +0 -149
  318. data/assets/include/SDL2/SDL_gamecontroller.h +0 -1074
  319. data/assets/include/SDL2/SDL_gesture.h +0 -117
  320. data/assets/include/SDL2/SDL_guid.h +0 -100
  321. data/assets/include/SDL2/SDL_haptic.h +0 -1341
  322. data/assets/include/SDL2/SDL_hidapi.h +0 -451
  323. data/assets/include/SDL2/SDL_hints.h +0 -2569
  324. data/assets/include/SDL2/SDL_image.h +0 -2173
  325. data/assets/include/SDL2/SDL_joystick.h +0 -1066
  326. data/assets/include/SDL2/SDL_keyboard.h +0 -353
  327. data/assets/include/SDL2/SDL_keycode.h +0 -358
  328. data/assets/include/SDL2/SDL_loadso.h +0 -115
  329. data/assets/include/SDL2/SDL_log.h +0 -404
  330. data/assets/include/SDL2/SDL_main.h +0 -275
  331. data/assets/include/SDL2/SDL_mixer.h +0 -2784
  332. data/assets/include/SDL2/SDL_mouse.h +0 -465
  333. data/assets/include/SDL2/SDL_mutex.h +0 -471
  334. data/assets/include/SDL2/SDL_name.h +0 -33
  335. data/assets/include/SDL2/SDL_pixels.h +0 -644
  336. data/assets/include/SDL2/SDL_platform.h +0 -261
  337. data/assets/include/SDL2/SDL_quit.h +0 -58
  338. data/assets/include/SDL2/SDL_rect.h +0 -376
  339. data/assets/include/SDL2/SDL_render.h +0 -1919
  340. data/assets/include/SDL2/SDL_revision.h +0 -6
  341. data/assets/include/SDL2/SDL_rwops.h +0 -841
  342. data/assets/include/SDL2/SDL_sensor.h +0 -322
  343. data/assets/include/SDL2/SDL_shape.h +0 -155
  344. data/assets/include/SDL2/SDL_stdinc.h +0 -830
  345. data/assets/include/SDL2/SDL_surface.h +0 -997
  346. data/assets/include/SDL2/SDL_system.h +0 -623
  347. data/assets/include/SDL2/SDL_syswm.h +0 -386
  348. data/assets/include/SDL2/SDL_test_images.h +0 -78
  349. data/assets/include/SDL2/SDL_test_random.h +0 -115
  350. data/assets/include/SDL2/SDL_thread.h +0 -464
  351. data/assets/include/SDL2/SDL_timer.h +0 -222
  352. data/assets/include/SDL2/SDL_touch.h +0 -150
  353. data/assets/include/SDL2/SDL_ttf.h +0 -2316
  354. data/assets/include/SDL2/SDL_types.h +0 -29
  355. data/assets/include/SDL2/SDL_version.h +0 -204
  356. data/assets/include/SDL2/SDL_video.h +0 -2150
  357. data/assets/include/SDL2/SDL_vulkan.h +0 -215
  358. data/assets/include/SDL2/begin_code.h +0 -187
  359. data/assets/include/mruby/boxing_no.h +0 -58
  360. data/assets/include/mruby/class.h +0 -108
  361. data/assets/include/mruby/error.h +0 -147
  362. data/assets/include/mruby/gc.h +0 -94
  363. data/assets/include/mruby/irep.h +0 -147
  364. data/assets/include/mruby/khash.h +0 -284
  365. data/assets/include/mruby/object.h +0 -43
  366. data/assets/include/mruby/presym/disable.h +0 -70
  367. data/assets/include/mruby/presym/enable.h +0 -37
  368. data/assets/include/mruby/presym.h +0 -40
  369. data/assets/include/mruby/proc.h +0 -211
  370. data/assets/include/mruby/throw.h +0 -66
  371. data/assets/macos/universal/bin/mrbc +0 -0
  372. data/assets/macos/universal/lib/libFLAC.a +0 -0
  373. data/assets/macos/universal/lib/libSDL2.a +0 -0
  374. data/assets/macos/universal/lib/libSDL2_image.a +0 -0
  375. data/assets/macos/universal/lib/libSDL2_mixer.a +0 -0
  376. data/assets/macos/universal/lib/libSDL2_ttf.a +0 -0
  377. data/assets/macos/universal/lib/libavif.a +0 -0
  378. data/assets/macos/universal/lib/libbrotlicommon-static.a +0 -0
  379. data/assets/macos/universal/lib/libbrotlidec-static.a +0 -0
  380. data/assets/macos/universal/lib/libfreetype.a +0 -0
  381. data/assets/macos/universal/lib/libgraphite2.a +0 -0
  382. data/assets/macos/universal/lib/libharfbuzz.a +0 -0
  383. data/assets/macos/universal/lib/libhwy.a +0 -0
  384. data/assets/macos/universal/lib/libjpeg.a +0 -0
  385. data/assets/macos/universal/lib/libjxl.a +0 -0
  386. data/assets/macos/universal/lib/libmodplug.a +0 -0
  387. data/assets/macos/universal/lib/libmpg123.a +0 -0
  388. data/assets/macos/universal/lib/libmruby.a +0 -0
  389. data/assets/macos/universal/lib/libogg.a +0 -0
  390. data/assets/macos/universal/lib/libpng.a +0 -0
  391. data/assets/macos/universal/lib/libtiff.a +0 -0
  392. data/assets/macos/universal/lib/libvorbis.a +0 -0
  393. data/assets/macos/universal/lib/libvorbisfile.a +0 -0
  394. data/assets/macos/universal/lib/libwebp.a +0 -0
  395. data/assets/macos/universal/lib/libzstd.a +0 -0
  396. data/assets/test_media/README.md +0 -3
  397. data/assets/test_media/bitstream_vera/COPYRIGHT.txt +0 -124
  398. data/assets/test_media/bitstream_vera/vera.ttf +0 -0
  399. data/assets/test_media/boom.png +0 -0
  400. data/assets/test_media/coin.png +0 -0
  401. data/assets/test_media/colors.png +0 -0
  402. data/assets/test_media/controller.png +0 -0
  403. data/assets/test_media/dial.wav +0 -0
  404. data/assets/test_media/hero.png +0 -0
  405. data/assets/test_media/image.bmp +0 -0
  406. data/assets/test_media/image.jpg +0 -0
  407. data/assets/test_media/image.png +0 -0
  408. data/assets/test_media/music.flac +0 -0
  409. data/assets/test_media/music.mp3 +0 -0
  410. data/assets/test_media/music.ogg +0 -0
  411. data/assets/test_media/music.wav +0 -0
  412. data/assets/test_media/originals/boom.pxm +0 -0
  413. data/assets/test_media/originals/coin.pxm +0 -0
  414. data/assets/test_media/originals/controller.sketch +0 -0
  415. data/assets/test_media/originals/hero.pxm +0 -0
  416. data/assets/test_media/originals/image.pxm +0 -0
  417. data/assets/test_media/originals/music.caf +0 -0
  418. data/assets/test_media/originals/texture_atlas.pxm +0 -0
  419. data/assets/test_media/rondo_alla_turka.ogg +0 -0
  420. data/assets/test_media/sound.flac +0 -0
  421. data/assets/test_media/sound.mp3 +0 -0
  422. data/assets/test_media/sound.ogg +0 -0
  423. data/assets/test_media/sound.wav +0 -0
  424. data/assets/test_media/sprite_sheet.png +0 -0
  425. data/assets/test_media/texture_atlas.png +0 -0
  426. data/assets/wasm/libmruby.a +0 -0
  427. data/assets/windows/mingw-w64-ucrt-x86_64/bin/mrbc.exe +0 -0
  428. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libFLAC.a +0 -0
  429. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libLerc.a +0 -0
  430. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libSDL2.a +0 -0
  431. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libSDL2_image.a +0 -0
  432. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libSDL2_mixer.a +0 -0
  433. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libSDL2_ttf.a +0 -0
  434. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libbrotlicommon.a +0 -0
  435. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libbrotlidec.a +0 -0
  436. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libbz2.a +0 -0
  437. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libdeflate.a +0 -0
  438. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libfreetype.a +0 -0
  439. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libglew32.a +0 -0
  440. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libgraphite2.a +0 -0
  441. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libharfbuzz.a +0 -0
  442. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libhwy.a +0 -0
  443. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libjbig.a +0 -0
  444. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libjpeg.a +0 -0
  445. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libjxl.a +0 -0
  446. data/assets/windows/mingw-w64-ucrt-x86_64/lib/liblzma.a +0 -0
  447. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libmodplug.a +0 -0
  448. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libmpg123.a +0 -0
  449. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libmruby.a +0 -0
  450. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libogg.a +0 -0
  451. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libopus.a +0 -0
  452. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libopusfile.a +0 -0
  453. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libpng.a +0 -0
  454. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libsndfile.a +0 -0
  455. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libssp.a +0 -1
  456. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libstdc++.a +0 -0
  457. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libtiff.a +0 -0
  458. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libvorbis.a +0 -0
  459. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libvorbisfile.a +0 -0
  460. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libwebp.a +0 -0
  461. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libz.a +0 -0
  462. data/assets/windows/mingw-w64-ucrt-x86_64/lib/libzstd.a +0 -0
  463. data/assets/windows/mingw-w64-x86_64/bin/mrbc.exe +0 -0
  464. data/assets/windows/mingw-w64-x86_64/lib/libFLAC.a +0 -0
  465. data/assets/windows/mingw-w64-x86_64/lib/libLerc.a +0 -0
  466. data/assets/windows/mingw-w64-x86_64/lib/libSDL2.a +0 -0
  467. data/assets/windows/mingw-w64-x86_64/lib/libSDL2_image.a +0 -0
  468. data/assets/windows/mingw-w64-x86_64/lib/libSDL2_mixer.a +0 -0
  469. data/assets/windows/mingw-w64-x86_64/lib/libSDL2_ttf.a +0 -0
  470. data/assets/windows/mingw-w64-x86_64/lib/libbrotlicommon.a +0 -0
  471. data/assets/windows/mingw-w64-x86_64/lib/libbrotlidec.a +0 -0
  472. data/assets/windows/mingw-w64-x86_64/lib/libbz2.a +0 -0
  473. data/assets/windows/mingw-w64-x86_64/lib/libdeflate.a +0 -0
  474. data/assets/windows/mingw-w64-x86_64/lib/libfreetype.a +0 -0
  475. data/assets/windows/mingw-w64-x86_64/lib/libglew32.a +0 -0
  476. data/assets/windows/mingw-w64-x86_64/lib/libgraphite2.a +0 -0
  477. data/assets/windows/mingw-w64-x86_64/lib/libharfbuzz.a +0 -0
  478. data/assets/windows/mingw-w64-x86_64/lib/libhwy.a +0 -0
  479. data/assets/windows/mingw-w64-x86_64/lib/libjbig.a +0 -0
  480. data/assets/windows/mingw-w64-x86_64/lib/libjpeg.a +0 -0
  481. data/assets/windows/mingw-w64-x86_64/lib/libjxl.a +0 -0
  482. data/assets/windows/mingw-w64-x86_64/lib/liblzma.a +0 -0
  483. data/assets/windows/mingw-w64-x86_64/lib/libmodplug.a +0 -0
  484. data/assets/windows/mingw-w64-x86_64/lib/libmpg123.a +0 -0
  485. data/assets/windows/mingw-w64-x86_64/lib/libmruby.a +0 -0
  486. data/assets/windows/mingw-w64-x86_64/lib/libogg.a +0 -0
  487. data/assets/windows/mingw-w64-x86_64/lib/libopus.a +0 -0
  488. data/assets/windows/mingw-w64-x86_64/lib/libopusfile.a +0 -0
  489. data/assets/windows/mingw-w64-x86_64/lib/libpng.a +0 -0
  490. data/assets/windows/mingw-w64-x86_64/lib/libsndfile.a +0 -0
  491. data/assets/windows/mingw-w64-x86_64/lib/libssp.a +0 -1
  492. data/assets/windows/mingw-w64-x86_64/lib/libstdc++.a +0 -0
  493. data/assets/windows/mingw-w64-x86_64/lib/libtiff.a +0 -0
  494. data/assets/windows/mingw-w64-x86_64/lib/libvorbis.a +0 -0
  495. data/assets/windows/mingw-w64-x86_64/lib/libvorbisfile.a +0 -0
  496. data/assets/windows/mingw-w64-x86_64/lib/libwebp.a +0 -0
  497. data/assets/windows/mingw-w64-x86_64/lib/libz.a +0 -0
  498. data/assets/windows/mingw-w64-x86_64/lib/libzstd.a +0 -0
  499. data/assets/xcode/ios/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -98
  500. data/assets/xcode/ios/Assets.xcassets/Contents.json +0 -6
  501. data/assets/xcode/ios/Base.lproj/LaunchScreen.storyboard +0 -27
  502. data/assets/xcode/ios/Info.plist +0 -43
  503. data/assets/xcode/ios/MRuby.framework/Headers/mrbconf.h +0 -143
  504. data/assets/xcode/ios/MRuby.framework/Headers/mruby/array.h +0 -280
  505. data/assets/xcode/ios/MRuby.framework/Headers/mruby/boxing_nan.h +0 -102
  506. data/assets/xcode/ios/MRuby.framework/Headers/mruby/boxing_no.h +0 -56
  507. data/assets/xcode/ios/MRuby.framework/Headers/mruby/boxing_word.h +0 -144
  508. data/assets/xcode/ios/MRuby.framework/Headers/mruby/class.h +0 -97
  509. data/assets/xcode/ios/MRuby.framework/Headers/mruby/common.h +0 -77
  510. data/assets/xcode/ios/MRuby.framework/Headers/mruby/compile.h +0 -195
  511. data/assets/xcode/ios/MRuby.framework/Headers/mruby/data.h +0 -75
  512. data/assets/xcode/ios/MRuby.framework/Headers/mruby/debug.h +0 -67
  513. data/assets/xcode/ios/MRuby.framework/Headers/mruby/dump.h +0 -196
  514. data/assets/xcode/ios/MRuby.framework/Headers/mruby/error.h +0 -75
  515. data/assets/xcode/ios/MRuby.framework/Headers/mruby/gc.h +0 -91
  516. data/assets/xcode/ios/MRuby.framework/Headers/mruby/hash.h +0 -215
  517. data/assets/xcode/ios/MRuby.framework/Headers/mruby/irep.h +0 -75
  518. data/assets/xcode/ios/MRuby.framework/Headers/mruby/istruct.h +0 -47
  519. data/assets/xcode/ios/MRuby.framework/Headers/mruby/khash.h +0 -274
  520. data/assets/xcode/ios/MRuby.framework/Headers/mruby/numeric.h +0 -161
  521. data/assets/xcode/ios/MRuby.framework/Headers/mruby/object.h +0 -44
  522. data/assets/xcode/ios/MRuby.framework/Headers/mruby/opcode.h +0 -69
  523. data/assets/xcode/ios/MRuby.framework/Headers/mruby/ops.h +0 -117
  524. data/assets/xcode/ios/MRuby.framework/Headers/mruby/proc.h +0 -131
  525. data/assets/xcode/ios/MRuby.framework/Headers/mruby/range.h +0 -49
  526. data/assets/xcode/ios/MRuby.framework/Headers/mruby/re.h +0 -16
  527. data/assets/xcode/ios/MRuby.framework/Headers/mruby/string.h +0 -444
  528. data/assets/xcode/ios/MRuby.framework/Headers/mruby/throw.h +0 -55
  529. data/assets/xcode/ios/MRuby.framework/Headers/mruby/value.h +0 -309
  530. data/assets/xcode/ios/MRuby.framework/Headers/mruby/variable.h +0 -136
  531. data/assets/xcode/ios/MRuby.framework/Headers/mruby/version.h +0 -110
  532. data/assets/xcode/ios/MRuby.framework/Headers/mruby.h +0 -1282
  533. data/assets/xcode/ios/MRuby.framework/Info.plist +0 -20
  534. data/assets/xcode/ios/MRuby.framework/MRuby +0 -0
  535. data/assets/xcode/ios/MyApp.xcodeproj/project.pbxproj +0 -368
  536. data/assets/xcode/ios/MyApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  537. data/assets/xcode/ios/MyApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  538. data/assets/xcode/ios/MyApp.xcodeproj/project.xcworkspace/xcshareddata/MyApp.xcscmblueprint +0 -30
  539. data/assets/xcode/ios/main.c +0 -1
  540. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +0 -16
  541. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json +0 -6
  542. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json +0 -17
  543. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +0 -16
  544. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json +0 -6
  545. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +0 -16
  546. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json +0 -6
  547. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +0 -16
  548. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json +0 -6
  549. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json +0 -17
  550. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +0 -16
  551. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json +0 -6
  552. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +0 -16
  553. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json +0 -6
  554. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json +0 -32
  555. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json +0 -16
  556. data/assets/xcode/tvos/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json +0 -16
  557. data/assets/xcode/tvos/Assets.xcassets/Contents.json +0 -6
  558. data/assets/xcode/tvos/Assets.xcassets/LaunchImage.launchimage/Contents.json +0 -22
  559. data/assets/xcode/tvos/Info.plist +0 -30
  560. data/assets/xcode/tvos/MRuby.framework/Headers/mrbconf.h +0 -143
  561. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/array.h +0 -280
  562. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/boxing_nan.h +0 -102
  563. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/boxing_no.h +0 -56
  564. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/boxing_word.h +0 -144
  565. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/class.h +0 -97
  566. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/common.h +0 -77
  567. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/compile.h +0 -195
  568. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/data.h +0 -75
  569. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/debug.h +0 -67
  570. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/dump.h +0 -196
  571. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/error.h +0 -75
  572. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/gc.h +0 -91
  573. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/hash.h +0 -215
  574. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/irep.h +0 -75
  575. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/istruct.h +0 -47
  576. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/khash.h +0 -274
  577. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/numeric.h +0 -161
  578. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/object.h +0 -44
  579. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/opcode.h +0 -69
  580. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/ops.h +0 -117
  581. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/proc.h +0 -131
  582. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/range.h +0 -49
  583. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/re.h +0 -16
  584. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/string.h +0 -444
  585. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/throw.h +0 -55
  586. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/value.h +0 -309
  587. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/variable.h +0 -136
  588. data/assets/xcode/tvos/MRuby.framework/Headers/mruby/version.h +0 -110
  589. data/assets/xcode/tvos/MRuby.framework/Headers/mruby.h +0 -1282
  590. data/assets/xcode/tvos/MRuby.framework/Info.plist +0 -20
  591. data/assets/xcode/tvos/MRuby.framework/MRuby +0 -0
  592. data/assets/xcode/tvos/MyApp.xcodeproj/project.pbxproj +0 -350
  593. data/assets/xcode/tvos/MyApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  594. data/assets/xcode/tvos/MyApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  595. data/assets/xcode/tvos/MyApp.xcodeproj/project.xcworkspace/xcshareddata/MyApp.xcscmblueprint +0 -30
  596. data/assets/xcode/tvos/main.c +0 -1
  597. data/ext/ruby2d/common.c +0 -185
  598. data/ext/ruby2d/controllers.c +0 -110
  599. data/ext/ruby2d/gl.c +0 -398
  600. data/ext/ruby2d/gl2.c +0 -86
  601. data/ext/ruby2d/gl3.c +0 -304
  602. data/ext/ruby2d/gles.c +0 -302
  603. data/ext/ruby2d/input.c +0 -48
  604. data/ext/ruby2d/music.c +0 -127
  605. data/ext/ruby2d/sound.c +0 -132
  606. data/lib/ruby2d/cli/console.rb +0 -65
  607. data/lib/ruby2d/cli/enable_console.rb +0 -7
  608. data/lib/ruby2d/cli/platform.rb +0 -17
  609. data/lib/ruby2d/music.rb +0 -77
  610. data/lib/ruby2d/pixel.rb +0 -18
  611. data/lib/ruby2d/pixmap.rb +0 -39
  612. data/lib/ruby2d/pixmap_atlas.rb +0 -56
  613. data/lib/ruby2d/sound.rb +0 -61
  614. data/lib/ruby2d/texture.rb +0 -38
  615. /data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2_gl2.h +0 -0
  616. /data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2_gl2ext.h +0 -0
  617. /data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2_gl2platform.h +0 -0
  618. /data/assets/{include/SDL2 → platform/include/SDL3}/SDL_opengles2_khrplatform.h +0 -0
  619. /data/assets/{include → platform/include}/mruby/re.h +0 -0
@@ -0,0 +1,908 @@
1
+ # Shape recognizer
2
+ # A two-layer neural net learns triangles, circles, and squares on a 16×16 grid.
3
+ #
4
+ # On startup it trains on a stream of procedurally generated shapes
5
+ # (random sizes, rotations, brush widths, and small position jitter);
6
+ # the left grid shows each training shape as the network sees it, and a
7
+ # yellow box marks its true class on the right — so you can watch the
8
+ # receptive fields resolve and the predictions catch up to ground truth
9
+ # in real time. After training, draw with the mouse on the left grid:
10
+ # the middle panel shows the network in action (each hidden unit is
11
+ # rendered as its 16×16 receptive field, lines pulse with activations
12
+ # and weights), and the right panel shows the predicted class with a
13
+ # confidence bar. Press `c` to clear the grid; `r` to retrain from
14
+ # scratch.
15
+ #
16
+ # A note on what makes this work: an MLP can't learn translation
17
+ # invariance from raw pixels (that's CNNs' job), so training shapes are
18
+ # centered with small jitter and the user's drawing is auto-centered (by
19
+ # centroid) before the network sees it. With that, ~99% accuracy on
20
+ # centered shapes; ~85% if you draw way off-center and rely on the
21
+ # centroid shift.
22
+
23
+ require 'ruby2d'
24
+
25
+ # === Tunables ===
26
+
27
+ WIDTH = 1200 # window width in pixels
28
+ HEIGHT = 600 # window height in pixels
29
+
30
+ GRID = 16 # input is GRID × GRID = 256 cells
31
+ INPUT_DIM = GRID * GRID
32
+ GRID_CENTER = (GRID - 1) / 2.0 # 7.5
33
+ HIDDEN = 16 # hidden-layer width
34
+ NUM_CLASSES = 3
35
+ CLASSES = %w[triangle circle square].freeze
36
+ CLASS_COLORS = %w[#fb923c #38bdf8 #a3e635].freeze # orange / cyan / lime
37
+
38
+ # Training (online: a fresh example is generated each step, so the network
39
+ # can't memorize a fixed set — it has to learn shape-ness)
40
+ TRAIN_TOTAL_STEPS = 18_000
41
+ LEARNING_RATE = 0.06
42
+ TRAIN_PER_FRAME = 70
43
+ POSITION_JITTER = 1.5 # training-shape position offset from center, in cells
44
+ TRAIN_BRUSHES = [0.7, 0.8, 0.9, 1.0].freeze # brackets USER_BRUSH so it's mid-distribution, not at the upper edge
45
+ USER_BRUSH = 0.9 # brush radius the user paints with
46
+
47
+ # Pacing for the two training visuals, in updates per second. Training
48
+ # itself runs at TRAIN_PER_FRAME each frame regardless; these only set how
49
+ # fast the visualization reads, and being wall-clock rates they read the
50
+ # same whether the build does 15 fps or 150. The thumbnails are the
51
+ # expensive panel — a repaint touches all 16 receptive fields — so they
52
+ # advance more slowly than the shape slideshow, which costs nothing.
53
+ SHAPE_UPDATES_PER_SEC = 8 # training shapes shown per second
54
+ RF_UPDATES_PER_SEC = 3 # receptive-field repaints per second
55
+
56
+ # Drawing area (left panel)
57
+ DRAW_X = 40
58
+ DRAW_Y = 110
59
+ DRAW_CELL = 18
60
+ DRAW_W = GRID * DRAW_CELL
61
+ DRAW_RIGHT = DRAW_X + DRAW_W
62
+
63
+ # Network panel — hidden units rendered as 16×16 receptive-field thumbnails.
64
+ # HIDDEN_TOP/BOTTOM sit far enough apart that HIDDEN_SPACING (= 34 here)
65
+ # is strictly greater than RF_SIZE (= 32), so adjacent thumbnails clear
66
+ # each other with a 2 px gutter — leaving room for the active-unit stroke.
67
+ RF_X = 470
68
+ RF_CELL = 2
69
+ RF_SIZE = GRID * RF_CELL
70
+ HIDDEN_TOP = 60 # center y of the topmost thumbnail
71
+ HIDDEN_BOTTOM = 570 # center y of the bottommost thumbnail
72
+ HIDDEN_SPACING = (HIDDEN_BOTTOM - HIDDEN_TOP).to_f / (HIDDEN - 1)
73
+
74
+ # Output panel — class icons sized to roughly the same 48 px bbox: a
75
+ # 24-radius circle is the baseline; the square matches at side 48; the
76
+ # triangle gets a larger circumradius (pointy shapes look smaller for
77
+ # the same enclosing radius) and is shifted down by TRI_OFFSET so its
78
+ # bounding box (rather than its centroid) sits on cy — otherwise it
79
+ # reads as floating high relative to the bar centerline.
80
+ OUT_X = 880
81
+ OUT_Y = [150, 300, 450].freeze
82
+ OUT_RADIUS = 24 # circle radius
83
+ TRI_RADIUS = 30 # triangle circumradius
84
+ TRI_OFFSET = TRI_RADIUS / 4.0 # shift to bbox-center the triangle on cy
85
+ SQUARE_SIDE = 48 # square side
86
+ ICON_HALF = 26 # max half-extent across icons (drives line endpoints + highlight)
87
+ BAR_X = 920
88
+ BAR_W = 240
89
+ BAR_H = 18
90
+ TARGET_PAD = 6 # padding around the target-class highlight box
91
+
92
+ # Loss sparkline (under the drawing area, fills left-to-right as training progresses)
93
+ SPARKLINE_X = DRAW_X
94
+ SPARKLINE_Y = DRAW_Y + DRAW_W + 60
95
+ SPARKLINE_W = DRAW_W
96
+ SPARKLINE_H = 70
97
+ SPARKLINE_HISTORY = 200 # number of samples to plot
98
+ SPARKLINE_SAMPLE_EVERY = TRAIN_TOTAL_STEPS / SPARKLINE_HISTORY # one sample every ~90 steps
99
+
100
+ # Smoothing factor for the prediction bars/icons. Each frame the displayed
101
+ # softmax moves SMOOTH_RATE of the way toward the raw network output, so
102
+ # rapid training-shape switching doesn't read as flicker on the right panel.
103
+ SMOOTH_RATE = 0.25
104
+
105
+ IDLE_PULSE_RATE = 1.5 # rad/s — input lines breathe at ~4 s/cycle while idle
106
+
107
+ # When true, post-training overlays the user's ink centroid (yellow dot)
108
+ # and the grid center (faint cross) on the drawing area, making the
109
+ # auto-centering shift visible. Off by default — useful but noisy.
110
+ SHOW_CENTROID_HINT = false
111
+
112
+ # === Window ===
113
+
114
+ set title: 'Ruby 2D ▸ Examples ▸ Shape recognizer', width: WIDTH, height: HEIGHT
115
+ set close_on_esc: true
116
+ set background: '#0b1220'
117
+
118
+ canvas = Canvas.new(x: 0, y: 0, width: WIDTH, height: HEIGHT, fill: [0, 0, 0, 0])
119
+
120
+ # The receptive-field thumbnails get their own canvas, below the main one.
121
+ # They only change as W1 moves, so they repaint at RF_UPDATES_PER_SEC
122
+ # while training and once when it finishes — not every frame. Their own
123
+ # layer is what makes that possible: the main canvas is cleared and
124
+ # repainted each frame for the drawing grid and the active-unit outlines
125
+ # that sit on top of these thumbnails, and a shared canvas would take the
126
+ # thumbnails with it.
127
+ rf_canvas = Canvas.new(x: RF_X, y: 0, width: RF_SIZE, height: HEIGHT,
128
+ fill: [0, 0, 0, 0], z: -1)
129
+
130
+ # === Palettes ===
131
+
132
+ GRAY_PAL = Array.new(32) do |i|
133
+ t = i / 31.0
134
+ [0.05 + t * 0.93, 0.07 + t * 0.88, 0.12 + t * 0.83, 1.0]
135
+ end
136
+
137
+ # Diverging palette: blue (negative weights) → near-black (zero) → orange (positive)
138
+ DIVERGING_PAL = Array.new(32) do |i|
139
+ t = (i - 15.5) / 15.5 # roughly -1..1
140
+ if t.negative?
141
+ a = -t
142
+ [0.05 + a * 0.05, 0.10 + a * 0.30, 0.18 + a * 0.55, 1.0]
143
+ else
144
+ [0.18 + t * 0.65, 0.12 + t * 0.30, 0.06 + t * 0.04, 1.0]
145
+ end
146
+ end
147
+
148
+ # Every canvas cell the panels paint sits at a fixed position — only which
149
+ # color bucket it lands in changes from frame to frame. Building the
150
+ # rectangles once and re-bucketing references costs no allocation, where
151
+ # rebuilding them inline allocated ~4,400 arrays every frame.
152
+ DRAW_RECTS = Array.new(GRID * GRID) do |n|
153
+ [DRAW_X + (n % GRID) * DRAW_CELL, DRAW_Y + (n / GRID) * DRAW_CELL,
154
+ DRAW_CELL, DRAW_CELL]
155
+ end
156
+ RF_RECTS = Array.new(HIDDEN) do |i|
157
+ ty = (HIDDEN_TOP + i * HIDDEN_SPACING - RF_SIZE / 2.0).round
158
+ # x is relative to `rf_canvas`, which starts at RF_X; y is absolute
159
+ # because that canvas spans the full window height.
160
+ Array.new(GRID * GRID) do |n|
161
+ [(n % GRID) * RF_CELL, ty + (n / GRID) * RF_CELL, RF_CELL, RF_CELL]
162
+ end
163
+ end
164
+ draw_buckets = Array.new(GRAY_PAL.size) { [] }
165
+ thumb_buckets = Array.new(DIVERGING_PAL.size) { [] }
166
+
167
+ CLASS_RGB = CLASS_COLORS.map do |hex|
168
+ [hex[1..2].to_i(16) / 255.0, hex[3..4].to_i(16) / 255.0, hex[5..6].to_i(16) / 255.0]
169
+ end
170
+ DIM_RGB = [0.12, 0.16, 0.22].freeze
171
+
172
+ # === Network ===
173
+
174
+ def rand_normal(stddev)
175
+ u = rand
176
+ u = 1e-9 if u.zero?
177
+ Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math::PI * rand) * stddev
178
+ end
179
+
180
+ def fresh_weights
181
+ he1 = Math.sqrt(2.0 / INPUT_DIM)
182
+ he2 = Math.sqrt(2.0 / HIDDEN)
183
+ [
184
+ Array.new(HIDDEN * INPUT_DIM) { rand_normal(he1) },
185
+ Array.new(HIDDEN, 0.0),
186
+ Array.new(NUM_CLASSES * HIDDEN) { rand_normal(he2) },
187
+ Array.new(NUM_CLASSES, 0.0)
188
+ ]
189
+ end
190
+
191
+ # Forward pass with sparse input — `non_zero` is the list of pixel indices
192
+ # above threshold. Drawings are mostly empty cells, so skipping zeros makes
193
+ # both training and per-frame inference much faster.
194
+ def forward(x, non_zero, w1, b1, w2, b2)
195
+ h = Array.new(HIDDEN)
196
+ # The two `non_zero` sweeps — here and in `train_step` — are the hottest
197
+ # loops in the file: HIDDEN passes over every inked pixel, twice per
198
+ # training step. A block per element would cost more than the multiply
199
+ # and add inside it, so these two spell out the loop.
200
+ nz = non_zero.length
201
+ HIDDEN.times do |i|
202
+ s = b1[i]
203
+ base = i * INPUT_DIM
204
+ t = 0
205
+ while t < nz
206
+ j = non_zero[t]
207
+ s += w1[base + j] * x[j]
208
+ t += 1
209
+ end
210
+ h[i] = s.positive? ? s : 0.0
211
+ end
212
+ z = Array.new(NUM_CLASSES)
213
+ NUM_CLASSES.times do |k|
214
+ s = b2[k]
215
+ base = k * HIDDEN
216
+ HIDDEN.times { |j| s += w2[base + j] * h[j] }
217
+ z[k] = s
218
+ end
219
+ m = z.max
220
+ ez = z.map { |v| Math.exp(v - m) }
221
+ sum = ez.sum
222
+ [h, ez.map { |v| v / sum }]
223
+ end
224
+
225
+ # One SGD step with cross-entropy loss on a softmax output. Returns the loss.
226
+ def train_step(x, non_zero, target, w1, b1, w2, b2, lr)
227
+ h, y = forward(x, non_zero, w1, b1, w2, b2)
228
+
229
+ dz2 = y.dup
230
+ dz2[target] -= 1.0
231
+
232
+ dh = Array.new(HIDDEN, 0.0)
233
+ HIDDEN.times do |j|
234
+ next if h[j].zero? # ReLU gate: gradient is zero where the unit was inactive
235
+ s = 0.0
236
+ NUM_CLASSES.times { |k| s += w2[k * HIDDEN + j] * dz2[k] }
237
+ dh[j] = s
238
+ end
239
+
240
+ NUM_CLASSES.times do |k|
241
+ base = k * HIDDEN
242
+ g = lr * dz2[k]
243
+ HIDDEN.times { |j| w2[base + j] -= g * h[j] }
244
+ b2[k] -= g
245
+ end
246
+ nz = non_zero.length
247
+ HIDDEN.times do |i|
248
+ g = dh[i]
249
+ next if g.zero?
250
+ base = i * INPUT_DIM
251
+ lg = lr * g
252
+ t = 0
253
+ while t < nz
254
+ j = non_zero[t]
255
+ w1[base + j] -= lg * x[j]
256
+ t += 1
257
+ end
258
+ b1[i] -= lg
259
+ end
260
+
261
+ -Math.log(y[target] + 1e-9)
262
+ end
263
+
264
+ # === Shape rasterization ===
265
+
266
+ def stamp(grid, fx, fy, br)
267
+ x0 = (fx - br).floor
268
+ x1 = (fx + br).ceil
269
+ y0 = (fy - br).floor
270
+ y1 = (fy + br).ceil
271
+ # `while`, not ranges: a training example stamps ~140 times, and each
272
+ # `(a..b).each` pair allocated a Range per row. Comparing squared
273
+ # distances also keeps the square root off the rejected cells.
274
+ br2 = br * br
275
+ y = y0
276
+ while y <= y1
277
+ if y >= 0 && y < GRID
278
+ dy = y - fy
279
+ dy2 = dy * dy
280
+ row = y * GRID
281
+ x = x0
282
+ while x <= x1
283
+ if x >= 0 && x < GRID
284
+ dx = x - fx
285
+ d2 = dx * dx + dy2
286
+ if d2 <= br2
287
+ v = 1.0 - Math.sqrt(d2) / br
288
+ idx = row + x
289
+ grid[idx] += (1.0 - grid[idx]) * v
290
+ end
291
+ end
292
+ x += 1
293
+ end
294
+ end
295
+ y += 1
296
+ end
297
+ end
298
+
299
+ def stroke(grid, x0, y0, x1, y1, br)
300
+ # Space the stamps at half the brush radius: close enough that the disks
301
+ # always overlap, and no closer. A fixed rate independent of `br` either
302
+ # leaves gaps for a wide brush or (as a flat 4x per cell did here)
303
+ # stamps four times over the same ground for a narrow one.
304
+ steps = (Math.sqrt((x1 - x0)**2 + (y1 - y0)**2) / (br * 0.5)).ceil
305
+ steps = 1 if steps < 1
306
+ (steps + 1).times do |i|
307
+ t = i.to_f / steps
308
+ stamp(grid, x0 + (x1 - x0) * t, y0 + (y1 - y0) * t, br)
309
+ end
310
+ end
311
+
312
+ def jitter
313
+ GRID_CENTER + (rand - 0.5) * POSITION_JITTER * 2
314
+ end
315
+
316
+ # Hand-drawn shapes are rarely closed cleanly — the most common artifact
317
+ # is a small gap where the user starts and ends the stroke (especially on
318
+ # circles). Each generator occasionally drops a piece of its outline so
319
+ # the network sees "almost-closed" shapes during training and doesn't
320
+ # treat a closed boundary as a hard requirement. Triangles and squares
321
+ # get a small gap probability too, otherwise the network would learn
322
+ # "has a gap" ≈ "is a circle" and start mis-classifying open polygons.
323
+
324
+ def gen_triangle(grid, br)
325
+ cx = jitter; cy = jitter
326
+ r = 4.5 + rand * 2.5
327
+ rot = rand * 2 * Math::PI
328
+ pts = 3.times.map do |i|
329
+ a = rot + i * 2 * Math::PI / 3
330
+ [cx + Math.cos(a) * r, cy + Math.sin(a) * r]
331
+ end
332
+ skip_last = rand < 0.15
333
+ 3.times do |i|
334
+ next if skip_last && i == 2
335
+ x0, y0 = pts[i]; x1, y1 = pts[(i + 1) % 3]
336
+ stroke(grid, x0, y0, x1, y1, br)
337
+ end
338
+ end
339
+
340
+ def gen_circle(grid, br)
341
+ cx = jitter; cy = jitter
342
+ r = 3.5 + rand * 3.0
343
+ segs = 32
344
+ gap_start = rand < 0.50 ? rand(segs) : -1
345
+ gap_len = gap_start >= 0 ? 1 + rand(3) : 0
346
+ segs.times do |i|
347
+ next if gap_len > 0 && (((i - gap_start) % segs) < gap_len)
348
+ a0 = i * 2 * Math::PI / segs
349
+ a1 = (i + 1) * 2 * Math::PI / segs
350
+ stroke(grid, cx + Math.cos(a0) * r, cy + Math.sin(a0) * r,
351
+ cx + Math.cos(a1) * r, cy + Math.sin(a1) * r, br)
352
+ end
353
+ end
354
+
355
+ def gen_square(grid, br)
356
+ cx = jitter; cy = jitter
357
+ half = 3.5 + rand * 2.5
358
+ rot = rand * Math::PI / 2 # square has 4-fold rotational symmetry
359
+ c = Math.cos(rot); s = Math.sin(rot)
360
+ pts = [[-half, -half], [half, -half], [half, half], [-half, half]].map do |x, y|
361
+ [cx + x * c - y * s, cy + x * s + y * c]
362
+ end
363
+ skip_last = rand < 0.10
364
+ 4.times do |i|
365
+ next if skip_last && i == 3
366
+ x0, y0 = pts[i]; x1, y1 = pts[(i + 1) % 4]
367
+ stroke(grid, x0, y0, x1, y1, br)
368
+ end
369
+ end
370
+
371
+ GENERATORS = [method(:gen_triangle), method(:gen_circle), method(:gen_square)].freeze
372
+
373
+ def make_example(k)
374
+ grid = Array.new(INPUT_DIM, 0.0)
375
+ GENERATORS[k].call(grid, TRAIN_BRUSHES.sample)
376
+ non_zero = []
377
+ j = 0
378
+ while j < INPUT_DIM
379
+ non_zero << j if grid[j] > 0.001
380
+ j += 1
381
+ end
382
+ [grid, non_zero, k]
383
+ end
384
+
385
+ # Ink-weighted centroid of a 16×16 grid in cell coordinates, or nil if
386
+ # the grid is essentially empty.
387
+ def grid_centroid(grid)
388
+ total = 0.0; cx = 0.0; cy = 0.0
389
+ GRID.times do |r|
390
+ GRID.times do |c|
391
+ v = grid[r * GRID + c]
392
+ next if v < 0.001
393
+ total += v; cx += c * v; cy += r * v
394
+ end
395
+ end
396
+ return nil if total < 1e-6
397
+ [cx / total, cy / total]
398
+ end
399
+
400
+ # Shift a 16×16 grid so its ink centroid lands at the grid center. The MLP
401
+ # can't learn translation invariance from raw pixels — auto-centering at
402
+ # inference time is what lets a user draw anywhere on the grid and still
403
+ # get a good prediction.
404
+ def centered_input(grid)
405
+ c = grid_centroid(grid)
406
+ return grid if c.nil?
407
+ cx, cy = c
408
+ dx = (GRID_CENTER - cx).round
409
+ dy = (GRID_CENTER - cy).round
410
+ return grid if dx.zero? && dy.zero?
411
+
412
+ out = Array.new(INPUT_DIM, 0.0)
413
+ GRID.times do |r|
414
+ nr = r + dy
415
+ next if nr.negative? || nr >= GRID
416
+ GRID.times do |c|
417
+ nc = c + dx
418
+ next if nc.negative? || nc >= GRID
419
+ out[nr * GRID + nc] = grid[r * GRID + c]
420
+ end
421
+ end
422
+ out
423
+ end
424
+
425
+ # === Mutable state ===
426
+
427
+ w1, b1, w2, b2 = fresh_weights
428
+ state = { steps: 0, loss: 0.0, done: false }
429
+
430
+ user_grid = Array.new(INPUT_DIM, 0.0)
431
+ last_mouse = nil
432
+ mouse_held = false
433
+
434
+ current_h = Array.new(HIDDEN, 0.0)
435
+ current_y = Array.new(NUM_CLASSES, 1.0 / NUM_CLASSES)
436
+ display_y = Array.new(NUM_CLASSES, 1.0 / NUM_CLASSES) # smoothed softmax for the bars/icons
437
+
438
+ # Most recently captured training example — what the drawing panel
439
+ # shows and what the prediction bars are computed from while training.
440
+ display_grid = Array.new(INPUT_DIM, 0.0)
441
+ display_nz = []
442
+ display_class = nil
443
+ rf_dirty = true # receptive-field thumbnails need repainting
444
+ shape_accum = 0.0 # fractional progress toward the next shape swap
445
+ rf_accum = 0.0 # fractional progress toward the next thumbnail repaint
446
+
447
+ loss_history = [] # appended every SPARKLINE_SAMPLE_EVERY training steps
448
+ elapsed = 0.0 # seconds since show, drives the idle pulse
449
+
450
+ # === Persistent renderables ===
451
+
452
+ header_text = Text.new('Training…', x: DRAW_X, y: 70, size: 16, color: '#cbd5e1')
453
+ network_label = Text.new('Network', y: 20, size: 16, color: '#cbd5e1')
454
+ network_label.x = RF_X + RF_SIZE / 2 - network_label.width / 2
455
+ prediction_label = Text.new('Prediction', y: 70, size: 16, color: '#cbd5e1')
456
+ prediction_label.x = OUT_X - prediction_label.width / 2
457
+ hint = Text.new('R retrain',
458
+ x: DRAW_X, y: DRAW_Y + DRAW_W + 10, size: 12, color: '#64748b')
459
+
460
+ # Drawing-area frame
461
+ [
462
+ [DRAW_X - 1, DRAW_Y - 1, DRAW_RIGHT + 1, DRAW_Y - 1],
463
+ [DRAW_X - 1, DRAW_Y + DRAW_W + 1, DRAW_RIGHT + 1, DRAW_Y + DRAW_W + 1],
464
+ [DRAW_X - 1, DRAW_Y - 1, DRAW_X - 1, DRAW_Y + DRAW_W + 1],
465
+ [DRAW_RIGHT + 1, DRAW_Y - 1, DRAW_RIGHT + 1, DRAW_Y + DRAW_W + 1]
466
+ ].each do |x1, y1, x2, y2|
467
+ Line.new(x1: x1, y1: y1, x2: x2, y2: y2, stroke_width: 1, color: '#1e293b')
468
+ end
469
+
470
+ # Loss-sparkline frame and label
471
+ Text.new('Loss', x: SPARKLINE_X, y: SPARKLINE_Y - 22, size: 12, color: '#94a3b8')
472
+ [
473
+ [SPARKLINE_X - 1, SPARKLINE_Y - 1,
474
+ SPARKLINE_X + SPARKLINE_W + 1, SPARKLINE_Y - 1],
475
+ [SPARKLINE_X - 1, SPARKLINE_Y + SPARKLINE_H + 1,
476
+ SPARKLINE_X + SPARKLINE_W + 1, SPARKLINE_Y + SPARKLINE_H + 1],
477
+ [SPARKLINE_X - 1, SPARKLINE_Y - 1,
478
+ SPARKLINE_X - 1, SPARKLINE_Y + SPARKLINE_H + 1],
479
+ [SPARKLINE_X + SPARKLINE_W + 1, SPARKLINE_Y - 1,
480
+ SPARKLINE_X + SPARKLINE_W + 1, SPARKLINE_Y + SPARKLINE_H + 1]
481
+ ].each do |x1, y1, x2, y2|
482
+ Line.new(x1: x1, y1: y1, x2: x2, y2: y2, stroke_width: 1, color: '#1e293b')
483
+ end
484
+
485
+ # Input → hidden: a visual abstraction. Each input pixel actually connects
486
+ # to every hidden unit (256 × 16 = 4096 connections), which would be
487
+ # unreadable on screen. Instead we draw one summary line per hidden unit;
488
+ # its thickness/opacity track that unit's post-ReLU activation each frame
489
+ # — i.e. how strongly the unit's receptive-field pattern matches what's
490
+ # on the grid.
491
+ input_lines = HIDDEN.times.map do |i|
492
+ src_y = DRAW_Y + (i + 0.5) * DRAW_W / HIDDEN.to_f
493
+ dst_y = HIDDEN_TOP + i * HIDDEN_SPACING
494
+ Line.new(x1: DRAW_RIGHT + 6, y1: src_y, x2: RF_X - 6, y2: dst_y,
495
+ stroke_width: 1, color: [0.55, 0.7, 0.95, 0.20])
496
+ end
497
+
498
+ # Hidden → output: every connection is drawn (HIDDEN × NUM_CLASSES = 48).
499
+ # Orange = positive weight — when this hidden unit fires, it pushes this
500
+ # class up. Blue = negative weight — firing pushes the class down.
501
+ # Thickness/opacity scale with |w · h|, the connection's actual
502
+ # contribution this frame, so you can see which paths are driving the
503
+ # current prediction rather than just which weights happen to be large.
504
+ output_lines = []
505
+ NUM_CLASSES.times do |k|
506
+ HIDDEN.times do |i|
507
+ src_y = HIDDEN_TOP + i * HIDDEN_SPACING
508
+ output_lines << Line.new(x1: RF_X + RF_SIZE + 6, y1: src_y,
509
+ x2: OUT_X - ICON_HALF - 4, y2: OUT_Y[k],
510
+ stroke_width: 1, color: [0.6, 0.7, 0.85, 0.20])
511
+ end
512
+ end
513
+
514
+ # Output: bar background, colored fill, big class circle, label
515
+ NUM_CLASSES.times do |k|
516
+ Rectangle.new(x: BAR_X, y: OUT_Y[k] - BAR_H / 2,
517
+ width: BAR_W, height: BAR_H, color: '#1e293b')
518
+ end
519
+ out_bars = NUM_CLASSES.times.map do |k|
520
+ Rectangle.new(x: BAR_X, y: OUT_Y[k] - BAR_H / 2,
521
+ width: 0, height: BAR_H, color: CLASS_COLORS[k])
522
+ end
523
+ # One icon per class. Sized for visual balance — the triangle gets a
524
+ # larger circumradius than the circle/square's enclosing radius so it
525
+ # carries similar visual weight, and is shifted down by TRI_OFFSET so
526
+ # its bounding box (not its centroid, which sits 1/4 R above the base)
527
+ # centers on cy and lines up with the rest.
528
+ out_icons = NUM_CLASSES.times.map do |k|
529
+ cx = OUT_X
530
+ cy = OUT_Y[k]
531
+ dim = DIM_RGB + [1.0]
532
+ case k
533
+ when 0 # triangle, point up, equilateral, bbox-centered at (cx, cy)
534
+ s60 = Math.sin(Math::PI / 3)
535
+ Triangle.new(x1: cx, y1: cy - TRI_RADIUS + TRI_OFFSET,
536
+ x2: cx - TRI_RADIUS * s60, y2: cy + TRI_RADIUS / 2.0 + TRI_OFFSET,
537
+ x3: cx + TRI_RADIUS * s60, y3: cy + TRI_RADIUS / 2.0 + TRI_OFFSET,
538
+ color: dim)
539
+ when 1
540
+ Circle.new(x: cx, y: cy, radius: OUT_RADIUS, color: dim, sectors: 32)
541
+ when 2
542
+ Square.new(x: cx - SQUARE_SIDE / 2, y: cy - SQUARE_SIDE / 2,
543
+ size: SQUARE_SIDE, color: dim)
544
+ end
545
+ end
546
+
547
+ result_text = Text.new('—', y: 510, size: 30, color: '#cbd5e1', visible: false)
548
+ status = Text.new('Training…', x: DRAW_X, y: HEIGHT - 28, size: 13, color: '#94a3b8')
549
+
550
+ # Repaint every hidden unit's receptive field: the 16×16 slice of W1
551
+ # weighting how each input pixel feeds that unit, normalized per unit so
552
+ # the strongest weight uses the full palette. Driven by `rf_dirty` rather
553
+ # than run every frame — see `rf_canvas`.
554
+ draw_receptive_fields = lambda do
555
+ rf_canvas.clear
556
+ thumb_buckets.each(&:clear)
557
+ HIDDEN.times do |i|
558
+ base = i * INPUT_DIM
559
+ wmax = 0.0
560
+ INPUT_DIM.times do |j|
561
+ a = w1[base + j].abs
562
+ wmax = a if a > wmax
563
+ end
564
+ wmax = 1e-6 if wmax < 1e-6
565
+ rects = RF_RECTS[i]
566
+ j = 0
567
+ while j < INPUT_DIM
568
+ v = w1[base + j] / wmax # normalized to -1..1
569
+ idx = ((v + 1.0) * 0.5 * 31).round.clamp(0, 31)
570
+ thumb_buckets[idx] << rects[j]
571
+ j += 1
572
+ end
573
+ end
574
+ thumb_buckets.each_with_index do |rects, i|
575
+ next if rects.empty?
576
+
577
+ rf_canvas.fill_rectangles(rectangles: rects, color: DIVERGING_PAL[i])
578
+ end
579
+ end
580
+
581
+ reset = lambda do
582
+ rf_dirty = true
583
+ shape_accum = 0.0
584
+ rf_accum = 0.0
585
+ user_grid.fill(0.0)
586
+ last_mouse = nil
587
+ mouse_held = false
588
+ w1, b1, w2, b2 = fresh_weights
589
+ state[:steps] = 0
590
+ state[:loss] = 0.0
591
+ state[:done] = false
592
+ display_grid = Array.new(INPUT_DIM, 0.0)
593
+ display_nz = []
594
+ display_class = nil
595
+ display_y.fill(1.0 / NUM_CLASSES)
596
+ loss_history.clear
597
+ status.content = 'Training…'
598
+ header_text.content = 'Training…'
599
+ header_text.color = '#cbd5e1'
600
+ hint.content = 'R retrain'
601
+ end
602
+
603
+ # === Input ===
604
+
605
+ def user_grid_coords(event)
606
+ return nil unless event.x.between?(DRAW_X, DRAW_RIGHT - 1) &&
607
+ event.y.between?(DRAW_Y, DRAW_Y + DRAW_W - 1)
608
+ [(event.x - DRAW_X) / DRAW_CELL.to_f, (event.y - DRAW_Y) / DRAW_CELL.to_f]
609
+ end
610
+
611
+ on :mouse_down do |event|
612
+ next unless event.button?(:left)
613
+ next unless state[:done] # the grid is showing training shapes — not a canvas yet
614
+ pos = user_grid_coords(event)
615
+ next unless pos
616
+ mouse_held = true
617
+ stamp(user_grid, *pos, USER_BRUSH)
618
+ last_mouse = pos
619
+ end
620
+
621
+ on :mouse_up do
622
+ mouse_held = false
623
+ last_mouse = nil
624
+ end
625
+
626
+ on :mouse_move do |event|
627
+ next unless mouse_held
628
+ pos = user_grid_coords(event)
629
+ next unless pos
630
+ if last_mouse
631
+ stroke(user_grid, *last_mouse, *pos, USER_BRUSH)
632
+ else
633
+ stamp(user_grid, *pos, USER_BRUSH)
634
+ end
635
+ last_mouse = pos
636
+ end
637
+
638
+ on(key_down: :c) do
639
+ user_grid.fill(0.0)
640
+ last_mouse = nil
641
+ end
642
+
643
+ on(key_down: :r) { reset.call }
644
+
645
+ # === Update ===
646
+
647
+ update do |dt|
648
+ elapsed += dt
649
+
650
+ # --- Train (online: fresh example each step) ---
651
+ last_x = nil; last_nz = nil; last_class = nil
652
+ unless state[:done]
653
+ TRAIN_PER_FRAME.times do
654
+ break if state[:steps] >= TRAIN_TOTAL_STEPS
655
+ x, nz, target = make_example(rand(NUM_CLASSES))
656
+ l = train_step(x, nz, target, w1, b1, w2, b2, LEARNING_RATE)
657
+ state[:loss] = state[:loss].zero? ? l : state[:loss] * 0.99 + l * 0.01
658
+ state[:steps] += 1
659
+ loss_history << state[:loss] if (state[:steps] % SPARKLINE_SAMPLE_EVERY).zero?
660
+ last_x = x; last_nz = nz; last_class = target
661
+ end
662
+ if state[:steps] >= TRAIN_TOTAL_STEPS
663
+ state[:done] = true
664
+ rf_dirty = true # last repaint: the weights stop moving here
665
+ status.content = format('Trained — final loss %.3f. Draw a shape!', state[:loss])
666
+ header_text.content = 'Draw a shape'
667
+ header_text.color = '#cbd5e1'
668
+ hint.content = 'C clear · R retrain'
669
+ else
670
+ pct = (state[:steps] * 100.0 / TRAIN_TOTAL_STEPS).to_i
671
+ status.content = format('Training… %d%% loss %.3f', pct, state[:loss])
672
+ end
673
+
674
+ # Both panels advance on wall-clock accumulators, so the visualization
675
+ # reads at the same speed everywhere. Training runs far faster than the
676
+ # eye and at very different speeds per platform — pacing these by
677
+ # frames or by step count made them strobe on one build and crawl on
678
+ # another. Surplus is dropped rather than carried: at most one shape
679
+ # and one repaint per frame, however long the frame was.
680
+ shape_accum += SHAPE_UPDATES_PER_SEC * dt
681
+ if last_x && (display_class.nil? || shape_accum >= 1.0)
682
+ shape_accum = 0.0
683
+ display_grid = last_x
684
+ display_nz = last_nz
685
+ display_class = last_class
686
+ end
687
+
688
+ rf_accum += RF_UPDATES_PER_SEC * dt
689
+ if rf_accum >= 1.0
690
+ rf_accum = 0.0
691
+ rf_dirty = true
692
+ end
693
+
694
+ if !state[:done] && display_class
695
+ header_text.content = "Training on #{CLASSES[display_class].upcase}"
696
+ header_text.color = CLASS_COLORS[display_class]
697
+ end
698
+ end
699
+
700
+ # --- Forward pass ---
701
+ # While training, classify the displayed training shape (so the user
702
+ # sees the network's current verdict on the very example it just took
703
+ # a gradient step on). After training, classify the user's drawing.
704
+ if state[:done]
705
+ centered = centered_input(user_grid)
706
+ forward_grid = centered
707
+ forward_nz = []
708
+ INPUT_DIM.times { |j| forward_nz << j if centered[j] > 0.001 }
709
+ panel_grid = user_grid
710
+ else
711
+ forward_grid = display_grid
712
+ forward_nz = display_nz
713
+ panel_grid = display_grid
714
+ end
715
+ current_h, current_y = forward(forward_grid, forward_nz, w1, b1, w2, b2)
716
+
717
+ # Predictions are meaningful while a training shape is on screen, or
718
+ # after training when the user has drawn enough ink. Otherwise the
719
+ # bars/lines/icons would reflect the learned biases on an empty grid
720
+ # — visually misleading — so we keep them in a neutral resting state.
721
+ show_prediction = if state[:done]
722
+ forward_nz.size > 4
723
+ else
724
+ !display_class.nil?
725
+ end
726
+
727
+ # Ease the displayed softmax toward the network's raw output. With a
728
+ # training shape changing several times a second, the raw
729
+ # current_y would jump on each switch; this turns the jumps into
730
+ # smooth animation. Snap back to uniform when there's nothing to
731
+ # predict so the bars don't drift on bias alone.
732
+ if show_prediction
733
+ NUM_CLASSES.times { |k| display_y[k] += (current_y[k] - display_y[k]) * SMOOTH_RATE }
734
+ else
735
+ display_y.fill(1.0 / NUM_CLASSES)
736
+ end
737
+
738
+ # --- Render canvas: drawing grid + receptive-field thumbnails ---
739
+ canvas.clear
740
+
741
+ draw_buckets.each(&:clear)
742
+ n = 0
743
+ while n < INPUT_DIM
744
+ idx = (panel_grid[n] * 31).round.clamp(0, 31)
745
+ draw_buckets[idx] << DRAW_RECTS[n]
746
+ n += 1
747
+ end
748
+ draw_buckets.each_with_index do |rects, i|
749
+ next if rects.empty?
750
+ canvas.fill_rectangles(rectangles: rects, color: GRAY_PAL[i])
751
+ end
752
+
753
+ # Each thumbnail visualizes one hidden unit's "receptive field": the
754
+ # 16×16 slice of W1 weighting how every input pixel feeds into that
755
+ # unit. Orange cells are pixels that push the unit's activation up
756
+ # when ink is present; blue cells push it down. Right after random
757
+ # init the thumbnails look like noise; as training progresses you'll
758
+ # see them resolve into shape detectors — patterns the unit has
759
+ # learned to fire on. This is the network's self-discovered
760
+ # vocabulary, the features it built without anyone telling it what
761
+ # to look for.
762
+ if rf_dirty
763
+ draw_receptive_fields.call
764
+ rf_dirty = false
765
+ end
766
+
767
+ # Outline thumbnails whose unit is firing on the current input, so the
768
+ # eye can pick out which features are matching. Stroke is drawn on the
769
+ # thumbnail's own edge (not padded out) so adjacent active units don't
770
+ # touch in the 2 px gutter between thumbnails.
771
+ h_max = current_h.max
772
+ h_max = 1.0 if h_max < 1e-6
773
+ if show_prediction
774
+ HIDDEN.times do |i|
775
+ a = current_h[i] / h_max
776
+ next if a < 0.05
777
+ ty = (HIDDEN_TOP + i * HIDDEN_SPACING - RF_SIZE / 2.0).round
778
+ canvas.stroke_rectangle(x: RF_X, y: ty, width: RF_SIZE, height: RF_SIZE,
779
+ stroke_width: 1, color: [0.95, 0.85, 0.40, 0.2 + a * 0.6])
780
+ end
781
+ end
782
+
783
+ # Box the target class while training so you can see what label the
784
+ # network is being pushed toward this step (vs. its current guess,
785
+ # shown in the bars and icon tints).
786
+ if !state[:done] && display_class
787
+ cy = OUT_Y[display_class]
788
+ canvas.stroke_rectangle(
789
+ x: OUT_X - ICON_HALF - TARGET_PAD,
790
+ y: cy - ICON_HALF - TARGET_PAD,
791
+ width: 2 * (ICON_HALF + TARGET_PAD),
792
+ height: 2 * (ICON_HALF + TARGET_PAD),
793
+ stroke_width: 2,
794
+ color: [0.98, 0.85, 0.30, 0.85]
795
+ )
796
+ end
797
+
798
+ # Centroid hint — once trained, mark grid center with a faint cross
799
+ # and the user's ink centroid with a small dot. The visible offset
800
+ # between them is exactly what centered_input compensates for before
801
+ # the network sees the drawing, which is otherwise invisible to the user.
802
+ if SHOW_CENTROID_HINT && state[:done]
803
+ cen_x = DRAW_X + (GRID_CENTER + 0.5) * DRAW_CELL
804
+ cen_y = DRAW_Y + (GRID_CENTER + 0.5) * DRAW_CELL
805
+ canvas.draw_line(x1: cen_x - 8, y1: cen_y, x2: cen_x + 8, y2: cen_y,
806
+ stroke_width: 1, color: [0.95, 0.85, 0.30, 0.35])
807
+ canvas.draw_line(x1: cen_x, y1: cen_y - 8, x2: cen_x, y2: cen_y + 8,
808
+ stroke_width: 1, color: [0.95, 0.85, 0.30, 0.35])
809
+ if forward_nz.size > 4 && (centroid = grid_centroid(user_grid))
810
+ cgx, cgy = centroid
811
+ canvas.fill_ellipse(x: DRAW_X + (cgx + 0.5) * DRAW_CELL,
812
+ y: DRAW_Y + (cgy + 0.5) * DRAW_CELL,
813
+ xradius: 3.5, yradius: 3.5,
814
+ color: [0.95, 0.85, 0.30, 0.9])
815
+ end
816
+ end
817
+
818
+ # Loss curve — fills the panel left-to-right as samples accumulate.
819
+ # Y is normalized to the running max so early high losses don't
820
+ # squash the late curve into a flat line.
821
+ if loss_history.size >= 2
822
+ max_l = loss_history.max
823
+ max_l = 1e-6 if max_l < 1e-6
824
+ step = SPARKLINE_W.to_f / (SPARKLINE_HISTORY - 1)
825
+ pts = loss_history.each_with_index.map do |l, i|
826
+ [SPARKLINE_X + i * step,
827
+ SPARKLINE_Y + SPARKLINE_H - 4 - (l / max_l) * (SPARKLINE_H - 8)]
828
+ end
829
+ canvas.draw_polyline(points: pts, stroke_width: 1.5, color: '#38bdf8')
830
+ end
831
+
832
+ # --- Update connection lines based on current activations & weights ---
833
+ # When show_prediction is false, fall back to the lines' resting style
834
+ # rather than letting bias-driven activations on an empty grid drive
835
+ # the animation.
836
+ if show_prediction
837
+ HIDDEN.times do |i|
838
+ a = current_h[i] / h_max
839
+ line = input_lines[i]
840
+ line.stroke_width = 0.6 + a * 3.4
841
+ line.color = [0.55, 0.75, 1.0, 0.10 + a * 0.55]
842
+ end
843
+ NUM_CLASSES.times do |k|
844
+ base = k * HIDDEN
845
+ HIDDEN.times do |i|
846
+ w = w2[base + i]
847
+ contribution = (w * current_h[i]).abs
848
+ line = output_lines[k * HIDDEN + i]
849
+ line.stroke_width = 0.5 + (contribution * 1.6).clamp(0, 4.5)
850
+ alpha = 0.10 + (contribution * 0.9).clamp(0, 0.7)
851
+ line.color = if w >= 0
852
+ [0.96, 0.60, 0.32, alpha]
853
+ else
854
+ [0.32, 0.65, 0.96, alpha]
855
+ end
856
+ end
857
+ end
858
+ else
859
+ # Idle pulse — input lines breathe slowly so the network reads as
860
+ # alive while it waits for a drawing. Output lines stay flat so the
861
+ # eye isn't pulled in two directions.
862
+ pulse = 0.5 + 0.5 * Math.sin(elapsed * IDLE_PULSE_RATE)
863
+ input_lines.each do |line|
864
+ line.stroke_width = 0.8 + 0.4 * pulse
865
+ line.color = [0.55, 0.7, 0.95, 0.10 + 0.10 * pulse]
866
+ end
867
+ output_lines.each do |line|
868
+ line.stroke_width = 1
869
+ line.color = [0.6, 0.7, 0.85, 0.20]
870
+ end
871
+ end
872
+
873
+ # --- Update output panel ---
874
+ # Drive bars, icon tints, and the predicted-class pick from display_y
875
+ # rather than current_y, so everything moves at the same smoothed
876
+ # cadence and a momentary tie between two classes doesn't make the
877
+ # label hop back and forth between them.
878
+ predicted = 0
879
+ best = -1.0
880
+ NUM_CLASSES.times do |k|
881
+ p = show_prediction ? display_y[k] : 0.0
882
+ out_bars[k].width = (BAR_W * p).round
883
+ rb, gb, bb = CLASS_RGB[k]
884
+ rd, gd, bdd = DIM_RGB
885
+ out_icons[k].color = [rd + (rb - rd) * p, gd + (gb - gd) * p,
886
+ bdd + (bb - bdd) * p, 1.0]
887
+ if display_y[k] > best
888
+ best = display_y[k]
889
+ predicted = k
890
+ end
891
+ end
892
+
893
+ # The result label sits directly under whichever icon was picked, so
894
+ # the eye associates the word with its shape. Hidden entirely when
895
+ # there's nothing to predict — a stray dash on screen reads as a
896
+ # leftover placeholder, not a "neutral" state.
897
+ if show_prediction
898
+ result_text.content = CLASSES[predicted].upcase
899
+ result_text.color = best > 0.5 ? CLASS_COLORS[predicted] : '#94a3b8'
900
+ result_text.x = OUT_X - result_text.width / 2
901
+ result_text.y = OUT_Y[predicted] + ICON_HALF + 6
902
+ result_text.show
903
+ else
904
+ result_text.hide
905
+ end
906
+ end
907
+
908
+ show