ranger-compiler 3.5.1 → 3.5.2

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 (338) hide show
  1. package/CHANGELOG.md +1064 -20
  2. package/LICENSE +3 -1
  3. package/README.md +123 -28
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79923 -56921
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
@@ -0,0 +1,322 @@
1
+ # The view on the GPU — a design
2
+
3
+ A canvas that pans and zooms rebuilds its whole display list for every frame,
4
+ because the pan and the zoom are baked into the coordinates that list carries.
5
+ They do not have to be. The painter already applies a translate in the vertex
6
+ shader — `uShift`, what a scroll layer moves by — and a *view* is that with a
7
+ scale and no layer. Keep the frame, change two uniforms.
8
+
9
+ Status: **S0 through S3 are built** (§11 says what each one is and what gate it
10
+ passed); S4 is not started. Every number in the design was measured on this
11
+ repository as it stood when it was written, on the machine it was written on,
12
+ and the measurement is named beside it.
13
+
14
+ Related: [`gl/README.md`](gl/README.md) (the display list as a GPU input),
15
+ [`README.md`](README.md) (the pipeline and the seam),
16
+ [`PLAN_NATIVE_HOSTS.md`](PLAN_NATIVE_HOSTS.md) (the engine off the UI thread —
17
+ this is orthogonal to it and composes with it),
18
+ [`../../gallery/figma/README.md`](../../gallery/figma/README.md) (the canvas that found this).
19
+
20
+ ---
21
+
22
+ ## 0. The short version
23
+
24
+ One pan frame of a FigJam board of 3,565 nodes — 744 commands, 767,000 points
25
+ — in Chromium, after the flattening and bridge work already on this branch:
26
+
27
+ | per frame | today | with a view |
28
+ | --- | --- | --- |
29
+ | build the display list (Ranger) | 37 ms | — |
30
+ | decode it in the page | 9 ms | — |
31
+ | build the GPU frame: atlas, buffers, upload | 18 ms | — |
32
+ | draw it | 18 ms | 18 ms |
33
+ | **a pan** | **87 ms** (11 fps) | **~18 ms** (50 fps) |
34
+
35
+ The last row is not a projection. `prepareDisplayList` already returns a frame
36
+ whose `draw()` can be called again, and drawing the same frame a second time
37
+ costs 18.4 ms and rebuilds nothing — measured. What is missing is a way to
38
+ draw that same frame *somewhere else*: a scale beside the translate the shader
39
+ already has, and a host that keeps the frame while the view moves.
40
+
41
+ **A zoom is not free the way a pan is.** Two things in a built frame have the
42
+ scale baked into them — the glyph atlas is rasterized at a size, and a curve
43
+ was flattened for a size — so a view may stretch a frame only so far before it
44
+ has to be built again (§4). That band is the whole of the design's honesty:
45
+ pans are exact and free, zoom is free within about ±40% and costs one rebuild
46
+ outside it.
47
+
48
+ ---
49
+
50
+ ## 1. What is already there
51
+
52
+ Nothing in this design is a new mechanism. Every piece exists and is in use:
53
+
54
+ | | where | what it does |
55
+ | --- | --- | --- |
56
+ | a translate applied by the shader | `gl/evg-webgl.js`, `uShift` | a scroll layer that moved since the frame was built draws moved, without rebuilding |
57
+ | a frame that outlives its draw | `prepareDisplayList` → `frame.draw(shifts)` | RealTrainer's fling: 266 scroll frames, none of them laid out again (`npm run rt:scroll`) |
58
+ | a clip carried into a transformed subtree | `EVGDisplayList.cullThroughTransform` | culling against the region a view will actually show |
59
+ | subdivision chosen from the drawn size | `EVGDisplayList.drawScale` | a curve knows how big it will be after the transform |
60
+ | a flattened outline kept on its element | `EVGElement.ringsCache` | the same path is not re-parsed for the next frame |
61
+ | the frame as typed arrays | `EVGDisplayList.toBinary` | no text between the engine and the painter |
62
+
63
+ The design is one step further along the line those five are already on: stop
64
+ baking the camera into the coordinates at all.
65
+
66
+ ---
67
+
68
+ ## 2. The contract
69
+
70
+ Today a display list is in **device space**: a command's `x` is where it lands
71
+ on the canvas, and `transformFrom` has already multiplied every point of it by
72
+ whatever transforms its element sat under. With a view, a list is in **scene
73
+ space** and carries the camera beside it:
74
+
75
+ ```ranger
76
+ list.setView(x y scale) ; the camera, not an element property
77
+ ```
78
+
79
+ * The walk does not bake that transform. Nothing else changes: element
80
+ transforms *inside* the scene (a rotated label, a scaled badge) are baked as
81
+ they are today, because they belong to the picture rather than to the
82
+ camera.
83
+ * `toJson` and `toBinary` carry `view` beside `width`/`height`. The binary
84
+ record is unchanged — the view is three numbers on the envelope, not a field
85
+ per command — and `sceneStride` already derives the record width, so an
86
+ older reader meets a list it can still read (it would draw it unpanned,
87
+ which is why `view` is also reported in `stats`).
88
+ * No view means an identity view, which is exactly the list every consumer
89
+ gets today. **This is opt-in from the first line.** A PDF, a PNG, an SVG
90
+ export and every golden test see byte-identical output unless a host asks
91
+ for a camera.
92
+
93
+ The host stops putting the pan/zoom on a world element. The figma viewer's
94
+ `SceneToEVG.buildPage` would no longer write `transform: translate(…) scale(…)`
95
+ on the world div; it would call `list.setView(panX, panY, zoom)` instead. The
96
+ element tree then no longer changes when the view does — which is the whole
97
+ point: a tree that does not change does not need to be walked.
98
+
99
+ ---
100
+
101
+ ## 3. What multiplies, and where
102
+
103
+ In the vertex shader, `uShift` (vec2) becomes `uView` (vec4: `sx, sy, tx, ty`):
104
+
105
+ ```glsl
106
+ vec2 p = aRect.xy * uView.xy + uView.zw + aCorner * aRect.zw * uView.xy;
107
+ ```
108
+
109
+ and with it, in the same shader: the half-size that feeds the distance field,
110
+ the corner radii, the border thickness, and the rotation origin. A gradient is
111
+ computed in the quad's own 0..1 space and is unaffected; an image's UV crop is
112
+ computed from aspect ratios and is unaffected. The path program
113
+ (`PATH_VERT`) takes the same uniform, applied to `aPos`.
114
+
115
+ Outside the shader, three things are in device pixels and must be mapped by
116
+ the host before they are handed to GL:
117
+
118
+ | | today | with a view |
119
+ | --- | --- | --- |
120
+ | a clip (scissor rectangle) | device pixels | multiply by the view when the scissor is set |
121
+ | the stroke expansion (a quad per segment, CPU) | device pixels | expand in scene units, let the shader scale — or keep a device-width stroke by dividing the width by the scale, which is what a hairline wants |
122
+ | a blur/shadow radius | device pixels | scale it; a shadow that does not grow with the picture reads as a different shadow |
123
+
124
+ Rotation in the view is possible (`uView` becomes a `mat3`) and nothing in the
125
+ repository needs it. Leave it out; say so where the field is declared.
126
+
127
+ ---
128
+
129
+ ## 4. The two things a scale cannot carry
130
+
131
+ A view stretches a *built* frame, and two things in a built frame were built
132
+ for a size.
133
+
134
+ **Glyphs.** The atlas is a 2-D canvas rasterization of each run at its size.
135
+ Scaled up on the GPU it blurs; scaled down it aliases. `atlasFor` already
136
+ keys its slots by run and size, so the fix is a policy and not a mechanism:
137
+ rebuild the atlas — not the list — when the view's scale leaves the band the
138
+ atlas was built for. That is a text-only rebuild, and on the board above it is
139
+ part of the 18 ms build, not all of it. (The permanent fix is SDF glyphs,
140
+ which is its own plan and its own quality argument. Not this one.)
141
+
142
+ **Curves.** A path was flattened into points for a drawn size (`drawScale`),
143
+ so a frame stretched far enough shows facets — and one shrunk far enough
144
+ carries points nobody can see. Same policy, same band.
145
+
146
+ So the rule, and it is the whole risk of the design:
147
+
148
+ > A frame may be drawn at any **pan**. It may be drawn at a scale between
149
+ > `1/√2` and `√2` of the scale it was built at. Outside that, or outside the
150
+ > region it was built for (§5), it is built again.
151
+
152
+ A pinch crosses a √2 band about every three notches, so a zoom gesture costs a
153
+ rebuild every third frame or so and pans between them cost nothing — against
154
+ every frame costing one today. The tolerance is a number, not a law: a viewer
155
+ that cares more about sharpness than about frames picks 1.15 instead.
156
+
157
+ ---
158
+
159
+ ## 5. Culling a list you mean to keep
160
+
161
+ Culling today asks *what is in the viewport*, and a kept frame is drawn at
162
+ views the viewport did not have. So a kept list is built for a **region**:
163
+ the viewport grown by an overscan, in scene units.
164
+
165
+ The scroll layers already do this vertically — `setOverscan(before, after)`,
166
+ defaulting to one clip height each way — and `refreshLayers` already decides
167
+ when a shift has left the range the overscan covered. A view needs the same
168
+ decision in two dimensions:
169
+
170
+ ```
171
+ rebuild when the view's scale left the band (§4)
172
+ or the viewport is no longer inside the built region
173
+ ```
174
+
175
+ Sizing the region is a memory argument, not a correctness one: one viewport of
176
+ overscan in each direction is 9× the commands in the worst case and usually
177
+ far less (a board at fit-zoom is mostly on screen already; a board at 8× has
178
+ almost nothing on it). A cap — "grow the region until it would exceed N
179
+ commands, then stop" — keeps the worst case bounded and degrades to today's
180
+ behaviour, one build per frame, rather than to a stall.
181
+
182
+ ---
183
+
184
+ ## 6. What each painter has to do
185
+
186
+ | painter | work | notes |
187
+ | --- | --- | --- |
188
+ | WebGL (`gl/evg-webgl.js`) | `uShift` → `uView` in two shaders, the scissor multiply, `frame.draw(view, shifts)` | the frame already survives its draw |
189
+ | DOM (`html/evg-html.js`) | one CSS `transform` on the root group | the natural form; arguably already free |
190
+ | Apple (`apple/`) | a CTM before the frame is drawn | `CGContext`/`CALayer` take one |
191
+ | Android (`android/`) | `Canvas.setMatrix` before the frame | same |
192
+ | SDL2 / C++ (RealTrainer native) | the same uniform in the same shader | the shader is shared source |
193
+ | PDF, PNG, SVG exporters | nothing | there is no camera in a printed page: they get an identity view and bake as they do now |
194
+ | the a11y mirror (`gl/evg-a11y.js`) | multiply the rectangles it positions the DOM at | it mirrors *painted* geometry, so it follows the camera |
195
+ | hit testing | nothing, but it becomes honest | every host already maps a click through `(screen − pan) / zoom` by hand; the view is that number in one place |
196
+
197
+ ---
198
+
199
+ ## 7. Who it helps, measured as it is today
200
+
201
+ | | today | what the view removes |
202
+ | --- | --- | --- |
203
+ | **figma viewer** — pan/zoom a board, 3,565 nodes | 87 ms a frame (§0) | the list build, the decode and the GPU build: ~69 of the 87 |
204
+ | **rangerflow** — drag, wheel, pinch a diagram, 2,516 commands | 12.1 ms to build and serialize a frame (`loadSqlFixture`, node) | the same: its `FlowView.build()` walks and culls the whole graph per frame, and a pan does not change the graph |
205
+ | **markdown** — scroll a document, 3,365 commands | 5.1 ms a frame: `offsetBy` over every command, a copy of the list, then JSON | `offsetBy` and the copy disappear — the scroll IS the view |
206
+ | **RealTrainer / ui** — flings and scroll containers | already kept; `refreshLayers` moves the commands on the CPU | a layer shift becomes a view with a translate: the CPU move goes too |
207
+ | **pptx viewer** | a slide is static | little per frame; the win is at fit/zoom and on a zoomed presenter view |
208
+ | **evg inspect, the responsive demo** | rebuild per view change | the same as any canvas |
209
+
210
+ Two of those — rangerflow and markdown — are already fast enough that nobody
211
+ would rewrite them for this alone. They are in the table because the change is
212
+ in the engine and they get it for the price of deleting code: `offsetBy` and
213
+ the per-frame list copy in markdown's `frame()`, the camera arithmetic spread
214
+ through `FlowView`'s painting in rangerflow.
215
+
216
+ ---
217
+
218
+ ## 8. Where it gives nothing
219
+
220
+ * A page that draws once. PDF, PNG, SVG, the pptx export: there is no second
221
+ frame to keep.
222
+ * The first frame of a file. A build is a build; this is about the second one.
223
+ * Content that changes every frame — an animation, a simulation tick, a drag
224
+ that moves a node. The view keeps a frame whose *scene* is unchanged. (A
225
+ drag that moves one node is the fragments mechanism's problem, and §10 says
226
+ why the view is what would finally make fragments work.)
227
+
228
+ ---
229
+
230
+ ## 9. What could go wrong
231
+
232
+ | risk | what it looks like | what holds it |
233
+ | --- | --- | --- |
234
+ | text softens while zooming | a pinch looks blurry until it settles | the band (§4), and a rebuild on gesture end; the check is a pixel diff of a kept frame against a rebuilt one at the band's edge |
235
+ | a golden test moves | a PDF or a conformance oracle shifts by a rounding | identity view is the default and bakes exactly as today; any consumer that does not call `setView` must be byte-identical, and that is the gate on S0 |
236
+ | two mechanisms disagree | a scroll layer inside a view drifts | one composition, written once: `view ∘ layerShift`, the layer's shift in scene units; `rt:scroll` already holds the layer half to a full rebuild, and the same check gains a view |
237
+ | the region policy thrashes | every second frame is a rebuild after all | the policy is one function in `gl/`, with the region and the band as its inputs, so it is testable without a browser (`evg:gestures:check` is the precedent) |
238
+ | the binary record grows | an older reader misreads a frame | it does not grow: the view rides on the envelope. `sceneStride` derives the width and refuses a narrower record with both numbers in the message |
239
+
240
+ ---
241
+
242
+ ## 10. Alternatives, and why this one
243
+
244
+ * **Move the engine to a Worker** (`gl/evg-engine.js`, PLAN_NATIVE_HOSTS S1).
245
+ Takes the cost off the UI thread; does not remove it. A 87 ms frame stays 87
246
+ ms of somebody's CPU, and the pan still lags by one frame. Composes with
247
+ this design rather than competing: a Worker that need not rebuild has
248
+ nothing to send.
249
+ * **Render to a texture and scale that** (a tile cache). Cheap to draw, and
250
+ every zoom is a resample: text goes soft immediately rather than at the band
251
+ edge, and the memory is the whole board at device resolution.
252
+ * **Keep the per-element commands and re-emit them** (the fragments mechanism,
253
+ already in `EVGDisplayList`). It cannot survive a view change today,
254
+ because what it keeps is in device space — the commands it re-emits would be
255
+ transformed a second time. In scene space that objection disappears, which
256
+ makes the view the thing that unlocks fragments for canvases, and fragments
257
+ the answer to "one node moved, nothing else did".
258
+ * **Do nothing and flatten less.** That was the last three commits, and it
259
+ bought 20×. It runs out here: what is left per frame is the frame itself.
260
+
261
+ ---
262
+
263
+ ## 11. Stages
264
+
265
+ | | what | gate | state |
266
+ | --- | --- | --- | --- |
267
+ | **S0** | `setView` on `EVGDisplayList`, carried by `toJson`/`toBinary`; `uView` in the two WebGL shaders; `frame.draw(shifts, view)` | every existing consumer byte-identical (identity view); a kept frame drawn at a new view matches a rebuilt list pixel for pixel (`evg:binary:check` gains the view; a pixel diff on the figma board) | **done** — `evg:view:check`, 31 checks, worst channel difference 0 |
268
+ | **S1** | the keep/rebuild policy as a host helper in `gl/` — region, band, and "rebuild now" — with the figma viewer on it | a headless check of the policy alone (no browser), and the board's pan frame at ~18 ms with the same pixels | **done** — `gl/evg-view.js` and `evg:view:policy` (30 checks); the figma viewer pans on a kept frame |
269
+ | **S2** | rangerflow on it: `FlowView` builds in world space, the camera leaves its painting code | its smoke test, and the minimap (which draws the *same* scene at another view — a second view, free) | **done** — in three frames rather than one, see below |
270
+ | **S3** | markdown's scroll on it: `frame()` stops copying and offsetting the document list | `markdown:web:test`, and the caret still lands where it is clicked | **done** — and the caret is now a list of its own |
271
+ | **S4** | layer shifts expressed as views; the native painters (Apple, Android, SDL) take a CTM | `rt:scroll` and `rt:frame` on both hosts | not started |
272
+
273
+ **S2 came out as THREE frames, not one.** The design assumed a canvas is a
274
+ scene with a camera on it. RangerFlow is three things at three different rates,
275
+ and the split is the interesting part of the stage:
276
+
277
+ | | what | in | rebuilt when |
278
+ | --- | --- | --- | --- |
279
+ | the pattern | the paper and the dot grid | screen pixels | the zoom or the window changes — a pan SLIDES it, because a pattern moved by one of its own periods is itself |
280
+ | the diagram | lanes, edges, nodes, labels | flow units, with the camera beside them | the graph changes, or the view leaves the band or the region |
281
+ | the chrome | rulers, minimap, ports, grips, buttons | screen pixels | every frame; it is a few dozen commands and all of it moves when the view does |
282
+
283
+ Two things had to be said out loud that the baked walk got for free, and they
284
+ are `FlowView.drawn` and `FlowView.hair`: **whether a thing is worth drawing is
285
+ a question about screen size** (a label at 1.2 pixels is mush whatever the model
286
+ says, so every level-of-detail test asks `drawn(len)`), and **a hairline is a
287
+ pixel, not a flow unit** (`hair(px)` divides by the zoom the frame is built at).
288
+ Everything else — a border, a row height, a font size — was already a flow
289
+ length going through `sl`, and those needed no change at all.
290
+
291
+ The grid is the reason for the third frame. Built into the diagram's list it
292
+ would cover the region rather than the window: nine times the dots, rebuilt on
293
+ every click — 1.2 MB of display list for one PlantUML diagram, measured. As its
294
+ own periodic frame it costs what it always did and a pan does not touch it.
295
+
296
+ Not done in S2: **the minimap is still drawn the long way**, as its own dots
297
+ rather than as the diagram's frame at a second view. The frames are there now,
298
+ so it is a small change, but it is a change to the minimap rather than to the
299
+ camera and nothing depends on it.
300
+
301
+ **Two bugs only a pixel could find**, both caught by the page's own
302
+ camera-against-baked comparison and both fixed here:
303
+
304
+ * **A camera written to two decimals.** `EVGDisplayList.toJson` wrote the view
305
+ through `num`, which rounds to a hundredth — right for a coordinate, wrong
306
+ for a SCALE that multiplies every coordinate in the list. A zoom of 0.4053
307
+ came out as 0.41 and drew the diagram 1.2% too large: eight pixels of slip at
308
+ the bottom of the canvas, and invisible at zoom 1, which is where every check
309
+ had been looking. `EVGDisplayList.fine` writes six decimals, and the view is
310
+ the only thing that uses it. (The binary bridge was never affected: it carries
311
+ doubles.)
312
+ * **A hairline floor measured in the wrong space.** The painter widens a stroke
313
+ to three quarters of a PIXEL so a thin line does not fall between samples —
314
+ but it applied that floor to the list's own units, and a list with a camera is
315
+ in scene units. A 1.5-unit edge on a diagram at 0.17 is a quarter of a pixel:
316
+ the floor never applied, and every line came out a third as dark as the same
317
+ diagram drawn without a camera. `strokeTriangles` now takes the scale the
318
+ frame is built at.
319
+
320
+ S0 is the only stage that touches the engine's contract, and it is additive.
321
+ S1 is where the frames come from. Everything after it is a host choosing to
322
+ stop doing arithmetic it no longer has to do.
@@ -0,0 +1,333 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ Import "SVGPathParser.rgr"
4
+ Import "VectorShapes.rgr"
5
+
6
+ ; ==============================================================================
7
+ ; PathBuilder.rgr — build a path from data, the way the parser reads one
8
+ ; ==============================================================================
9
+ ; PLAN_VECTOR_IR.md Stage 3.5.
10
+ ;
11
+ ; SVGPathParser turns `d` into commands. This is the other direction, and it is
12
+ ; the direction a chart, a diagram or any generated drawing actually needs: the
13
+ ; shape comes from data, not from a string somebody typed.
14
+ ;
15
+ ; Without it the only way to draw computed geometry is to concatenate `d` by
16
+ ; hand —
17
+ ;
18
+ ; d = d + "L" + (to_string x) + "," + (to_string y) + " "
19
+ ;
20
+ ; — which puts number formatting, separator rules and command syntax into every
21
+ ; caller, and hands the result straight back to the parser to be taken apart
22
+ ; again. Building commands directly skips the round trip entirely: a renderer
23
+ ; can consume `getCommands()` as-is, and `asPathData()` is there only for the
24
+ ; targets that speak `d`, like an inline <svg>.
25
+ ;
26
+ ; The basic shapes are here too, so a whole scene — axes, gridlines, a series,
27
+ ; its markers — can accumulate into ONE path with one fill and one stroke,
28
+ ; rather than one element per line.
29
+ ; ==============================================================================
30
+
31
+ class PathBuilder {
32
+ def commands:[PathCommand]
33
+
34
+ ; The pen, so the relative moves know where they are starting from.
35
+ def curX:double 0.0
36
+ def curY:double 0.0
37
+ ; Where the current subpath began, which is where `close` returns to.
38
+ def startX:double 0.0
39
+ def startY:double 0.0
40
+ ; Whether anything has been drawn yet. A relative move as the very first
41
+ ; command has no pen position to be relative TO, so it is treated as
42
+ ; absolute rather than silently measured from (0, 0).
43
+ def started:boolean false
44
+
45
+ Constructor () {
46
+ def c:[PathCommand]
47
+ commands = c
48
+ }
49
+
50
+ fn reset:void () {
51
+ def c:[PathCommand]
52
+ commands = c
53
+ curX = 0.0
54
+ curY = 0.0
55
+ startX = 0.0
56
+ startY = 0.0
57
+ started = false
58
+ }
59
+
60
+ fn isEmpty:boolean () {
61
+ return ((array_length commands) == 0)
62
+ }
63
+
64
+ fn commandCount:int () {
65
+ return (array_length commands)
66
+ }
67
+
68
+ fn getCommands:[PathCommand] () {
69
+ return commands
70
+ }
71
+
72
+ ; --------------------------------------------------------------------------
73
+ ; Absolute moves
74
+ ; --------------------------------------------------------------------------
75
+
76
+ fn moveTo:void (x:double y:double) {
77
+ push commands (VectorShapes.moveTo(x y))
78
+ curX = x
79
+ curY = y
80
+ startX = x
81
+ startY = y
82
+ started = true
83
+ }
84
+
85
+ fn lineTo:void (x:double y:double) {
86
+ ; A lineto with no preceding moveto has no start point. Promoting it to
87
+ ; a moveto is what the path grammar would require anyway, and it keeps
88
+ ; a caller that loops over data from producing an unusable path when
89
+ ; the first point arrives through the same code as the rest.
90
+ if (started == false) {
91
+ this.moveTo(x y)
92
+ return
93
+ }
94
+ push commands (VectorShapes.lineTo(x y))
95
+ curX = x
96
+ curY = y
97
+ }
98
+
99
+ fn cubicTo:void (x1:double y1:double x2:double y2:double x:double y:double) {
100
+ if (started == false) {
101
+ this.moveTo(x1 y1)
102
+ }
103
+ push commands (VectorShapes.cubicTo(x1 y1 x2 y2 x y))
104
+ curX = x
105
+ curY = y
106
+ }
107
+
108
+ fn quadTo:void (x1:double y1:double x:double y:double) {
109
+ if (started == false) {
110
+ this.moveTo(x1 y1)
111
+ }
112
+ def c (new PathCommand())
113
+ c.type = "Q"
114
+ c.x1 = x1
115
+ c.y1 = y1
116
+ c.x = x
117
+ c.y = y
118
+ push commands c
119
+ curX = x
120
+ curY = y
121
+ }
122
+
123
+ fn close:void () {
124
+ if (started == false) {
125
+ return
126
+ }
127
+ push commands (VectorShapes.closePath())
128
+ curX = startX
129
+ curY = startY
130
+ }
131
+
132
+ ; --------------------------------------------------------------------------
133
+ ; Relative moves
134
+ ; --------------------------------------------------------------------------
135
+ ; Convenient for stepping along a series, and for the small offsets that
136
+ ; make up a tick or a marker.
137
+
138
+ fn moveBy:void (dx:double dy:double) {
139
+ if (started == false) {
140
+ this.moveTo(dx dy)
141
+ return
142
+ }
143
+ this.moveTo((curX + dx) (curY + dy))
144
+ }
145
+
146
+ fn lineBy:void (dx:double dy:double) {
147
+ if (started == false) {
148
+ this.moveTo(dx dy)
149
+ return
150
+ }
151
+ this.lineTo((curX + dx) (curY + dy))
152
+ }
153
+
154
+ ; --------------------------------------------------------------------------
155
+ ; Whole shapes
156
+ ; --------------------------------------------------------------------------
157
+ ; Each of these is its own subpath, so they accumulate into one path that
158
+ ; is filled and stroked once. That is the difference between a chart with
159
+ ; forty gridlines as forty elements and one with a single gridline path.
160
+
161
+ ; Turn everything built so far about a point. A symbol that carries an
162
+ ; angle — a wind vector, an arrow along a route — is the same shape drawn
163
+ ; the same way and then rotated, and doing it to the GEOMETRY rather than
164
+ ; in a renderer's transform means every target draws it turned: SVG, PDF,
165
+ ; the raster and the GPU all read the commands and none of them read a
166
+ ; transform attribute.
167
+ fn rotateAbout:void (cx:double cy:double degrees:double) {
168
+ if (degrees == 0.0) {
169
+ return
170
+ }
171
+ def rad:double (degrees * 0.017453292519943295)
172
+ def c:double (cos rad)
173
+ def s:double (sin rad)
174
+ def n:int (array_length commands)
175
+ def k:int 0
176
+ while (k < n) {
177
+ def one:PathCommand (itemAt commands k)
178
+ k = (k + 1)
179
+ def px:double (one.x - cx)
180
+ def py:double (one.y - cy)
181
+ one.x = (cx + ((px * c) - (py * s)))
182
+ one.y = (cy + ((px * s) + (py * c)))
183
+ def p1x:double (one.x1 - cx)
184
+ def p1y:double (one.y1 - cy)
185
+ one.x1 = (cx + ((p1x * c) - (p1y * s)))
186
+ one.y1 = (cy + ((p1x * s) + (p1y * c)))
187
+ def p2x:double (one.x2 - cx)
188
+ def p2y:double (one.y2 - cy)
189
+ one.x2 = (cx + ((p2x * c) - (p2y * s)))
190
+ one.y2 = (cy + ((p2x * s) + (p2y * c)))
191
+ ; An elliptical arc keeps its radii and turns its own axis with
192
+ ; the rest of the shape.
193
+ if (one.type == "A") {
194
+ one.rotation = (one.rotation + degrees)
195
+ }
196
+ }
197
+ }
198
+
199
+ fn addCommands:void (cmds:[PathCommand]) {
200
+ def n:int (array_length cmds)
201
+ def k:int 0
202
+ while (k < n) {
203
+ def c:PathCommand (itemAt cmds k)
204
+ push commands c
205
+ ; Keep the pen in step with what was appended, so a relative move
206
+ ; after an appended shape starts from the right place.
207
+ if ((c.type == "M")) {
208
+ startX = c.x
209
+ startY = c.y
210
+ started = true
211
+ }
212
+ if ((c.type == "Z") == false) {
213
+ curX = c.x
214
+ curY = c.y
215
+ } {
216
+ curX = startX
217
+ curY = startY
218
+ }
219
+ k = (k + 1)
220
+ }
221
+ if (n > 0) {
222
+ started = true
223
+ }
224
+ }
225
+
226
+ fn addRect:void (x:double y:double w:double h:double) {
227
+ def negOne:double (0.0 - 1.0)
228
+ def cmds:[PathCommand] (VectorShapes.rect(x y w h negOne negOne))
229
+ this.addCommands(cmds)
230
+ }
231
+
232
+ fn addRoundedRect:void (x:double y:double w:double h:double rx:double ry:double) {
233
+ def cmds:[PathCommand] (VectorShapes.rect(x y w h rx ry))
234
+ this.addCommands(cmds)
235
+ }
236
+
237
+ fn addCircle:void (cx:double cy:double r:double) {
238
+ def cmds:[PathCommand] (VectorShapes.circle(cx cy r))
239
+ this.addCommands(cmds)
240
+ }
241
+
242
+ fn addEllipse:void (cx:double cy:double rx:double ry:double) {
243
+ def cmds:[PathCommand] (VectorShapes.ellipse(cx cy rx ry))
244
+ this.addCommands(cmds)
245
+ }
246
+
247
+ fn addLine:void (x1:double y1:double x2:double y2:double) {
248
+ def cmds:[PathCommand] (VectorShapes.line(x1 y1 x2 y2))
249
+ this.addCommands(cmds)
250
+ }
251
+
252
+ fn addPolyline:void (pts:[double]) {
253
+ def cmds:[PathCommand] (VectorShapes.polyline(pts))
254
+ this.addCommands(cmds)
255
+ }
256
+
257
+ fn addPolygon:void (pts:[double]) {
258
+ def cmds:[PathCommand] (VectorShapes.polygon(pts))
259
+ this.addCommands(cmds)
260
+ }
261
+
262
+ ; Append a path written as `d`. The bridge back from the string form, for
263
+ ; an icon or a marker glyph that already exists as path data. Malformed
264
+ ; data follows the parser's rule: what parses is appended, and the caller
265
+ ; can ask the parser itself for the diagnostic.
266
+ fn addPathData:void (d:string) {
267
+ def p (new SVGPathParser())
268
+ p.parse(d)
269
+ def cmds:[PathCommand] (p.getCommands())
270
+ this.addCommands(cmds)
271
+ }
272
+
273
+ ; --------------------------------------------------------------------------
274
+ ; Output
275
+ ; --------------------------------------------------------------------------
276
+
277
+ fn asPathData:string () {
278
+ return (VectorShapes.asPathData(commands))
279
+ }
280
+
281
+ ; The extent of everything built so far, for callers that need to frame it
282
+ ; — a viewBox, a legend box, a hit region.
283
+ fn bounds:PathBounds () {
284
+ def b (new PathBounds())
285
+ def n:int (array_length commands)
286
+ def seen:boolean false
287
+ def k:int 0
288
+ while (k < n) {
289
+ def c:PathCommand (itemAt commands k)
290
+ if ((c.type == "Z") == false) {
291
+ ; Control points are included: a curve can bulge past its
292
+ ; endpoints, and a box that cropped it would be wrong for
293
+ ; exactly the shapes curves are used for.
294
+ def xs:[double]
295
+ def ys:[double]
296
+ push xs c.x
297
+ push ys c.y
298
+ if (c.type == "C") {
299
+ push xs c.x1
300
+ push ys c.y1
301
+ push xs c.x2
302
+ push ys c.y2
303
+ }
304
+ if (c.type == "Q") {
305
+ push xs c.x1
306
+ push ys c.y1
307
+ }
308
+ def j:int 0
309
+ while (j < (array_length xs)) {
310
+ def vx:double (itemAt xs j)
311
+ def vy:double (itemAt ys j)
312
+ if (seen == false) {
313
+ b.minX = vx
314
+ b.maxX = vx
315
+ b.minY = vy
316
+ b.maxY = vy
317
+ seen = true
318
+ } {
319
+ if (vx < b.minX) { b.minX = vx }
320
+ if (vx > b.maxX) { b.maxX = vx }
321
+ if (vy < b.minY) { b.minY = vy }
322
+ if (vy > b.maxY) { b.maxY = vy }
323
+ }
324
+ j = (j + 1)
325
+ }
326
+ }
327
+ k = (k + 1)
328
+ }
329
+ b.width = (b.maxX - b.minX)
330
+ b.height = (b.maxY - b.minY)
331
+ return b
332
+ }
333
+ }