@cortex-js/compute-engine 0.55.6 → 0.57.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 (258) hide show
  1. package/dist/compile.esm.js +1224 -179
  2. package/dist/compile.min.esm.js +290 -91
  3. package/dist/compile.min.umd.cjs +291 -92
  4. package/dist/compile.umd.cjs +1224 -179
  5. package/dist/compute-engine.esm.js +1973 -306
  6. package/dist/compute-engine.min.esm.js +301 -102
  7. package/dist/compute-engine.min.umd.cjs +301 -102
  8. package/dist/compute-engine.umd.cjs +1973 -306
  9. package/dist/core.esm.js +1972 -305
  10. package/dist/core.min.esm.js +300 -101
  11. package/dist/core.min.umd.cjs +300 -101
  12. package/dist/core.umd.cjs +1972 -305
  13. package/dist/interval.esm.js +360 -19
  14. package/dist/interval.min.esm.js +6 -6
  15. package/dist/interval.min.umd.cjs +6 -6
  16. package/dist/interval.umd.cjs +360 -19
  17. package/dist/latex-syntax.esm.js +427 -25
  18. package/dist/latex-syntax.min.esm.js +7 -7
  19. package/dist/latex-syntax.min.umd.cjs +7 -7
  20. package/dist/latex-syntax.umd.cjs +427 -25
  21. package/dist/math-json.esm.js +2 -2
  22. package/dist/math-json.min.esm.js +2 -2
  23. package/dist/math-json.min.umd.cjs +2 -2
  24. package/dist/math-json.umd.cjs +2 -2
  25. package/dist/numerics.esm.js +4 -2
  26. package/dist/numerics.min.esm.js +3 -3
  27. package/dist/numerics.min.umd.cjs +3 -3
  28. package/dist/numerics.umd.cjs +4 -2
  29. package/dist/types/big-decimal/big-decimal.d.ts +1 -1
  30. package/dist/types/big-decimal/index.d.ts +1 -1
  31. package/dist/types/big-decimal/transcendentals.d.ts +1 -1
  32. package/dist/types/big-decimal/utils.d.ts +1 -1
  33. package/dist/types/common/ansi-codes.d.ts +1 -1
  34. package/dist/types/common/configuration-change.d.ts +1 -1
  35. package/dist/types/common/fuzzy-string-match.d.ts +1 -1
  36. package/dist/types/common/grapheme-splitter.d.ts +1 -1
  37. package/dist/types/common/interruptible.d.ts +1 -1
  38. package/dist/types/common/one-of.d.ts +1 -1
  39. package/dist/types/common/signals.d.ts +1 -1
  40. package/dist/types/common/type/ast-nodes.d.ts +1 -1
  41. package/dist/types/common/type/boxed-type.d.ts +1 -1
  42. package/dist/types/common/type/lexer.d.ts +1 -1
  43. package/dist/types/common/type/parse.d.ts +1 -1
  44. package/dist/types/common/type/parser.d.ts +1 -1
  45. package/dist/types/common/type/primitive.d.ts +1 -1
  46. package/dist/types/common/type/reduce.d.ts +1 -1
  47. package/dist/types/common/type/serialize.d.ts +1 -1
  48. package/dist/types/common/type/subtype.d.ts +1 -1
  49. package/dist/types/common/type/type-builder.d.ts +1 -1
  50. package/dist/types/common/type/types.d.ts +2 -2
  51. package/dist/types/common/type/utils.d.ts +1 -1
  52. package/dist/types/common/utils.d.ts +1 -1
  53. package/dist/types/compile.d.ts +1 -1
  54. package/dist/types/compute-engine/assume.d.ts +1 -1
  55. package/dist/types/compute-engine/boxed-expression/abstract-boxed-expression.d.ts +6 -1
  56. package/dist/types/compute-engine/boxed-expression/apply.d.ts +1 -1
  57. package/dist/types/compute-engine/boxed-expression/arithmetic-add.d.ts +1 -1
  58. package/dist/types/compute-engine/boxed-expression/arithmetic-mul-div.d.ts +1 -1
  59. package/dist/types/compute-engine/boxed-expression/arithmetic-power.d.ts +1 -1
  60. package/dist/types/compute-engine/boxed-expression/ascii-math.d.ts +1 -1
  61. package/dist/types/compute-engine/boxed-expression/box.d.ts +1 -1
  62. package/dist/types/compute-engine/boxed-expression/boxed-dictionary.d.ts +1 -1
  63. package/dist/types/compute-engine/boxed-expression/boxed-function.d.ts +1 -1
  64. package/dist/types/compute-engine/boxed-expression/boxed-number.d.ts +1 -1
  65. package/dist/types/compute-engine/boxed-expression/boxed-operator-definition.d.ts +1 -1
  66. package/dist/types/compute-engine/boxed-expression/boxed-patterns.d.ts +1 -1
  67. package/dist/types/compute-engine/boxed-expression/boxed-string.d.ts +1 -1
  68. package/dist/types/compute-engine/boxed-expression/boxed-symbol.d.ts +1 -1
  69. package/dist/types/compute-engine/boxed-expression/boxed-tensor.d.ts +1 -1
  70. package/dist/types/compute-engine/boxed-expression/boxed-value-definition.d.ts +1 -1
  71. package/dist/types/compute-engine/boxed-expression/cache.d.ts +1 -1
  72. package/dist/types/compute-engine/boxed-expression/canonical-utils.d.ts +1 -1
  73. package/dist/types/compute-engine/boxed-expression/canonical.d.ts +1 -1
  74. package/dist/types/compute-engine/boxed-expression/compare.d.ts +1 -1
  75. package/dist/types/compute-engine/boxed-expression/constants.d.ts +1 -1
  76. package/dist/types/compute-engine/boxed-expression/expand.d.ts +1 -1
  77. package/dist/types/compute-engine/boxed-expression/expression-map.d.ts +1 -1
  78. package/dist/types/compute-engine/boxed-expression/factor.d.ts +1 -1
  79. package/dist/types/compute-engine/boxed-expression/flatten.d.ts +1 -1
  80. package/dist/types/compute-engine/boxed-expression/hold.d.ts +1 -1
  81. package/dist/types/compute-engine/boxed-expression/inequality-bounds.d.ts +1 -1
  82. package/dist/types/compute-engine/boxed-expression/init-lazy-refs.d.ts +1 -1
  83. package/dist/types/compute-engine/boxed-expression/invisible-operator.d.ts +1 -1
  84. package/dist/types/compute-engine/boxed-expression/match.d.ts +1 -1
  85. package/dist/types/compute-engine/boxed-expression/negate.d.ts +1 -1
  86. package/dist/types/compute-engine/boxed-expression/numerics.d.ts +1 -1
  87. package/dist/types/compute-engine/boxed-expression/order.d.ts +1 -1
  88. package/dist/types/compute-engine/boxed-expression/pattern-utils.d.ts +1 -1
  89. package/dist/types/compute-engine/boxed-expression/polynomial-degree.d.ts +1 -1
  90. package/dist/types/compute-engine/boxed-expression/polynomials.d.ts +1 -1
  91. package/dist/types/compute-engine/boxed-expression/predicates.d.ts +1 -1
  92. package/dist/types/compute-engine/boxed-expression/rules.d.ts +1 -1
  93. package/dist/types/compute-engine/boxed-expression/serialize.d.ts +1 -1
  94. package/dist/types/compute-engine/boxed-expression/sgn.d.ts +1 -1
  95. package/dist/types/compute-engine/boxed-expression/simplify.d.ts +1 -1
  96. package/dist/types/compute-engine/boxed-expression/solve-linear-system.d.ts +1 -1
  97. package/dist/types/compute-engine/boxed-expression/solve.d.ts +1 -1
  98. package/dist/types/compute-engine/boxed-expression/stochastic-equal.d.ts +1 -1
  99. package/dist/types/compute-engine/boxed-expression/trigonometry.d.ts +1 -1
  100. package/dist/types/compute-engine/boxed-expression/type-guards.d.ts +1 -1
  101. package/dist/types/compute-engine/boxed-expression/utils.d.ts +1 -1
  102. package/dist/types/compute-engine/boxed-expression/validate.d.ts +1 -1
  103. package/dist/types/compute-engine/collection-utils.d.ts +1 -1
  104. package/dist/types/compute-engine/compilation/base-compiler.d.ts +55 -6
  105. package/dist/types/compute-engine/compilation/compile-expression.d.ts +1 -1
  106. package/dist/types/compute-engine/compilation/constant-folding.d.ts +1 -1
  107. package/dist/types/compute-engine/compilation/glsl-target.d.ts +1 -1
  108. package/dist/types/compute-engine/compilation/gpu-target.d.ts +15 -5
  109. package/dist/types/compute-engine/compilation/interval-javascript-target.d.ts +1 -1
  110. package/dist/types/compute-engine/compilation/javascript-target.d.ts +25 -3
  111. package/dist/types/compute-engine/compilation/python-target.d.ts +1 -1
  112. package/dist/types/compute-engine/compilation/types.d.ts +1 -1
  113. package/dist/types/compute-engine/compilation/wgsl-target.d.ts +1 -1
  114. package/dist/types/compute-engine/cost-function.d.ts +1 -1
  115. package/dist/types/compute-engine/engine-assumptions.d.ts +1 -1
  116. package/dist/types/compute-engine/engine-cache.d.ts +1 -1
  117. package/dist/types/compute-engine/engine-common-symbols.d.ts +1 -1
  118. package/dist/types/compute-engine/engine-compilation-targets.d.ts +1 -1
  119. package/dist/types/compute-engine/engine-configuration-lifecycle.d.ts +1 -1
  120. package/dist/types/compute-engine/engine-declarations.d.ts +1 -1
  121. package/dist/types/compute-engine/engine-expression-entrypoints.d.ts +1 -1
  122. package/dist/types/compute-engine/engine-extension-contracts.d.ts +1 -1
  123. package/dist/types/compute-engine/engine-library-bootstrap.d.ts +1 -1
  124. package/dist/types/compute-engine/engine-numeric-configuration.d.ts +1 -1
  125. package/dist/types/compute-engine/engine-runtime-state.d.ts +1 -1
  126. package/dist/types/compute-engine/engine-scope.d.ts +1 -1
  127. package/dist/types/compute-engine/engine-sequences.d.ts +1 -1
  128. package/dist/types/compute-engine/engine-simplification-rules.d.ts +1 -1
  129. package/dist/types/compute-engine/engine-startup-coordinator.d.ts +1 -1
  130. package/dist/types/compute-engine/engine-type-resolver.d.ts +1 -1
  131. package/dist/types/compute-engine/engine-validation-entrypoints.d.ts +1 -1
  132. package/dist/types/compute-engine/free-functions.d.ts +1 -1
  133. package/dist/types/compute-engine/function-utils.d.ts +1 -1
  134. package/dist/types/compute-engine/global-types.d.ts +1 -1
  135. package/dist/types/compute-engine/index.d.ts +24 -3
  136. package/dist/types/compute-engine/interval/arithmetic.d.ts +1 -1
  137. package/dist/types/compute-engine/interval/comparison.d.ts +1 -1
  138. package/dist/types/compute-engine/interval/elementary.d.ts +1 -1
  139. package/dist/types/compute-engine/interval/index.d.ts +1 -1
  140. package/dist/types/compute-engine/interval/trigonometric.d.ts +1 -1
  141. package/dist/types/compute-engine/interval/types.d.ts +1 -1
  142. package/dist/types/compute-engine/interval/util.d.ts +1 -1
  143. package/dist/types/compute-engine/latex-syntax/dictionary/default-dictionary.d.ts +4 -3
  144. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-algebra.d.ts +1 -1
  145. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-arithmetic.d.ts +1 -1
  146. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-calculus.d.ts +1 -1
  147. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-colors.d.ts +10 -0
  148. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-complex.d.ts +1 -1
  149. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-core.d.ts +1 -1
  150. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-linear-algebra.d.ts +1 -1
  151. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-logic.d.ts +1 -1
  152. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-other.d.ts +1 -1
  153. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-relational-operators.d.ts +1 -1
  154. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-sets.d.ts +1 -1
  155. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-statistics.d.ts +1 -1
  156. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-symbols.d.ts +1 -1
  157. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-trigonometry.d.ts +1 -1
  158. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-units.d.ts +1 -1
  159. package/dist/types/compute-engine/latex-syntax/dictionary/definitions.d.ts +1 -1
  160. package/dist/types/compute-engine/latex-syntax/dictionary/indexed-types.d.ts +9 -1
  161. package/dist/types/compute-engine/latex-syntax/latex-syntax.d.ts +1 -1
  162. package/dist/types/compute-engine/latex-syntax/parse-number.d.ts +1 -1
  163. package/dist/types/compute-engine/latex-syntax/parse-symbol.d.ts +1 -1
  164. package/dist/types/compute-engine/latex-syntax/parse.d.ts +1 -1
  165. package/dist/types/compute-engine/latex-syntax/serialize-dms.d.ts +1 -1
  166. package/dist/types/compute-engine/latex-syntax/serialize-number.d.ts +1 -1
  167. package/dist/types/compute-engine/latex-syntax/serializer-style.d.ts +1 -1
  168. package/dist/types/compute-engine/latex-syntax/serializer.d.ts +1 -1
  169. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  170. package/dist/types/compute-engine/latex-syntax/types.d.ts +40 -1
  171. package/dist/types/compute-engine/latex-syntax/utils.d.ts +1 -1
  172. package/dist/types/compute-engine/library/arithmetic.d.ts +1 -1
  173. package/dist/types/compute-engine/library/calculus.d.ts +1 -1
  174. package/dist/types/compute-engine/library/collections.d.ts +5 -3
  175. package/dist/types/compute-engine/library/colors.d.ts +1 -1
  176. package/dist/types/compute-engine/library/combinatorics.d.ts +1 -1
  177. package/dist/types/compute-engine/library/complex.d.ts +1 -1
  178. package/dist/types/compute-engine/library/control-structures.d.ts +1 -1
  179. package/dist/types/compute-engine/library/core.d.ts +1 -1
  180. package/dist/types/compute-engine/library/fractals.d.ts +1 -1
  181. package/dist/types/compute-engine/library/library.d.ts +1 -1
  182. package/dist/types/compute-engine/library/linear-algebra.d.ts +1 -1
  183. package/dist/types/compute-engine/library/logic-analysis.d.ts +1 -1
  184. package/dist/types/compute-engine/library/logic.d.ts +1 -1
  185. package/dist/types/compute-engine/library/number-theory.d.ts +1 -1
  186. package/dist/types/compute-engine/library/polynomials.d.ts +1 -1
  187. package/dist/types/compute-engine/library/quantity-arithmetic.d.ts +1 -1
  188. package/dist/types/compute-engine/library/random-expression.d.ts +1 -1
  189. package/dist/types/compute-engine/library/relational-operator.d.ts +1 -1
  190. package/dist/types/compute-engine/library/sets.d.ts +1 -1
  191. package/dist/types/compute-engine/library/statistics.d.ts +1 -1
  192. package/dist/types/compute-engine/library/trigonometry.d.ts +1 -1
  193. package/dist/types/compute-engine/library/type-handlers.d.ts +1 -1
  194. package/dist/types/compute-engine/library/unit-data.d.ts +1 -1
  195. package/dist/types/compute-engine/library/units.d.ts +1 -1
  196. package/dist/types/compute-engine/library/utils.d.ts +1 -1
  197. package/dist/types/compute-engine/numeric-value/big-numeric-value.d.ts +1 -1
  198. package/dist/types/compute-engine/numeric-value/exact-numeric-value.d.ts +1 -1
  199. package/dist/types/compute-engine/numeric-value/machine-numeric-value.d.ts +1 -1
  200. package/dist/types/compute-engine/numeric-value/types.d.ts +1 -1
  201. package/dist/types/compute-engine/numerics/bigint.d.ts +1 -1
  202. package/dist/types/compute-engine/numerics/expression.d.ts +1 -1
  203. package/dist/types/compute-engine/numerics/interval.d.ts +1 -1
  204. package/dist/types/compute-engine/numerics/linear-algebra.d.ts +1 -1
  205. package/dist/types/compute-engine/numerics/monte-carlo.d.ts +1 -1
  206. package/dist/types/compute-engine/numerics/numeric-bigint.d.ts +1 -1
  207. package/dist/types/compute-engine/numerics/numeric-bignum.d.ts +1 -1
  208. package/dist/types/compute-engine/numerics/numeric-complex.d.ts +1 -1
  209. package/dist/types/compute-engine/numerics/numeric.d.ts +1 -1
  210. package/dist/types/compute-engine/numerics/primes.d.ts +1 -1
  211. package/dist/types/compute-engine/numerics/rationals.d.ts +1 -1
  212. package/dist/types/compute-engine/numerics/richardson.d.ts +1 -1
  213. package/dist/types/compute-engine/numerics/special-functions.d.ts +1 -1
  214. package/dist/types/compute-engine/numerics/statistics.d.ts +1 -1
  215. package/dist/types/compute-engine/numerics/strings.d.ts +1 -1
  216. package/dist/types/compute-engine/numerics/types.d.ts +1 -1
  217. package/dist/types/compute-engine/numerics/unit-data.d.ts +1 -1
  218. package/dist/types/compute-engine/oeis.d.ts +1 -1
  219. package/dist/types/compute-engine/sequence.d.ts +1 -1
  220. package/dist/types/compute-engine/symbolic/antiderivative.d.ts +1 -1
  221. package/dist/types/compute-engine/symbolic/derivative.d.ts +1 -1
  222. package/dist/types/compute-engine/symbolic/distribute.d.ts +1 -1
  223. package/dist/types/compute-engine/symbolic/fu-cost.d.ts +1 -1
  224. package/dist/types/compute-engine/symbolic/fu-transforms.d.ts +1 -1
  225. package/dist/types/compute-engine/symbolic/fu.d.ts +1 -1
  226. package/dist/types/compute-engine/symbolic/logic-utils.d.ts +1 -1
  227. package/dist/types/compute-engine/symbolic/simplify-abs.d.ts +1 -1
  228. package/dist/types/compute-engine/symbolic/simplify-divide.d.ts +1 -1
  229. package/dist/types/compute-engine/symbolic/simplify-factorial.d.ts +1 -1
  230. package/dist/types/compute-engine/symbolic/simplify-hyperbolic.d.ts +1 -1
  231. package/dist/types/compute-engine/symbolic/simplify-infinity.d.ts +1 -1
  232. package/dist/types/compute-engine/symbolic/simplify-log.d.ts +1 -1
  233. package/dist/types/compute-engine/symbolic/simplify-logic.d.ts +1 -1
  234. package/dist/types/compute-engine/symbolic/simplify-power.d.ts +1 -1
  235. package/dist/types/compute-engine/symbolic/simplify-product.d.ts +1 -1
  236. package/dist/types/compute-engine/symbolic/simplify-rules.d.ts +1 -1
  237. package/dist/types/compute-engine/symbolic/simplify-sum.d.ts +1 -1
  238. package/dist/types/compute-engine/symbolic/simplify-trig.d.ts +1 -1
  239. package/dist/types/compute-engine/tensor/tensor-fields.d.ts +1 -1
  240. package/dist/types/compute-engine/tensor/tensors.d.ts +1 -1
  241. package/dist/types/compute-engine/types-definitions.d.ts +1 -1
  242. package/dist/types/compute-engine/types-engine.d.ts +23 -2
  243. package/dist/types/compute-engine/types-evaluation.d.ts +1 -1
  244. package/dist/types/compute-engine/types-expression.d.ts +1 -1
  245. package/dist/types/compute-engine/types-kernel-evaluation.d.ts +1 -1
  246. package/dist/types/compute-engine/types-kernel-serialization.d.ts +1 -1
  247. package/dist/types/compute-engine/types-serialization.d.ts +1 -1
  248. package/dist/types/compute-engine/types.d.ts +1 -1
  249. package/dist/types/compute-engine.d.ts +1 -2
  250. package/dist/types/core.d.ts +1 -1
  251. package/dist/types/interval.d.ts +1 -1
  252. package/dist/types/latex-syntax.d.ts +2 -2
  253. package/dist/types/math-json/symbols.d.ts +1 -1
  254. package/dist/types/math-json/types.d.ts +1 -1
  255. package/dist/types/math-json/utils.d.ts +1 -1
  256. package/dist/types/math-json.d.ts +2 -2
  257. package/dist/types/numerics.d.ts +1 -1
  258. package/package.json +2 -2
package/dist/core.umd.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /** ComputeEngineCore 0.55.6 */
1
+ /** ComputeEngineCore 0.57.0 */
2
2
  (function(global,factory){typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'],factory):(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ComputeEngineCore = {}));})(this, (function (exports) { 'use strict';
3
3
  var ComputeEngineCore = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -2287,6 +2287,7 @@ var ComputeEngineCore = (() => {
2287
2287
  ];
2288
2288
  var VALUE_TYPES = [
2289
2289
  "value",
2290
+ "color",
2290
2291
  ...COLLECTION_TYPES,
2291
2292
  ...SCALAR_TYPES
2292
2293
  ];
@@ -3986,6 +3987,7 @@ var ComputeEngineCore = (() => {
3986
3987
  symbol: [],
3987
3988
  boolean: [],
3988
3989
  string: [],
3990
+ color: [],
3989
3991
  expression: EXPRESSION_TYPES
3990
3992
  };
3991
3993
  function isPrimitiveSubtype(lhs, rhs) {
@@ -7869,9 +7871,10 @@ var ComputeEngineCore = (() => {
7869
7871
  if (!materialized.isLazyCollection) return materialized.latex;
7870
7872
  }
7871
7873
  const syntax = this.engine._requireLatexSyntax();
7872
- return syntax.serialize(
7873
- this.toMathJson({ prettify: true, fractionalDigits: "auto" })
7874
- );
7874
+ const json = this.toMathJson({ prettify: true, fractionalDigits: "auto" });
7875
+ const latexOpts = this.engine.latexOptions;
7876
+ if (Object.keys(latexOpts).length === 0) return syntax.serialize(json);
7877
+ return syntax.serialize(json, { ...latexOpts });
7875
7878
  }
7876
7879
  /**
7877
7880
  * Return a LaTeX representation of this expression with custom
@@ -7882,8 +7885,15 @@ var ComputeEngineCore = (() => {
7882
7885
  *
7883
7886
  * Numeric values are rounded to `ce.precision` significant digits
7884
7887
  * (via `fractionalDigits: 'auto'`).
7888
+ *
7889
+ * If `options.verbatim` is `true` and `verbatimLatex` is set on this
7890
+ * expression (i.e. it was parsed with `preserveLatex: true`), return
7891
+ * the verbatim source instead of re-serializing. Falls through to
7892
+ * re-serialization if no verbatim is available.
7885
7893
  */
7886
7894
  toLatex(options) {
7895
+ if (options?.verbatim === true && this.verbatimLatex !== void 0)
7896
+ return this.verbatimLatex;
7887
7897
  if (this.isLazyCollection) {
7888
7898
  const materialized = this.evaluate({
7889
7899
  materialization: options?.materialization ?? true
@@ -7895,9 +7905,13 @@ var ComputeEngineCore = (() => {
7895
7905
  fractionalDigits: "auto"
7896
7906
  });
7897
7907
  const syntax = this.engine._requireLatexSyntax();
7898
- if (!options || Object.keys(options).length === 0)
7899
- return syntax.serialize(json);
7900
- return syntax.serialize(json, options);
7908
+ const latexOpts = this.engine.latexOptions;
7909
+ const haveEngineOpts = Object.keys(latexOpts).length > 0;
7910
+ const haveCallOpts = options && Object.keys(options).length > 0;
7911
+ if (!haveEngineOpts && !haveCallOpts) return syntax.serialize(json);
7912
+ if (!haveEngineOpts) return syntax.serialize(json, options);
7913
+ if (!haveCallOpts) return syntax.serialize(json, { ...latexOpts });
7914
+ return syntax.serialize(json, { ...latexOpts, ...options });
7901
7915
  }
7902
7916
  /** Called by `JSON.stringify()` when serializing to json.
7903
7917
  *
@@ -7941,11 +7955,13 @@ var ComputeEngineCore = (() => {
7941
7955
  "number",
7942
7956
  "dictionary"
7943
7957
  ];
7944
- }
7945
- if (Array.isArray(options.shorthands))
7958
+ } else if (Array.isArray(options.shorthands)) {
7946
7959
  defaultOptions.shorthands = options.shorthands;
7960
+ }
7947
7961
  if (typeof options.metadata === "string" && options.metadata === "all" || options.metadata?.includes("all")) {
7948
7962
  defaultOptions.metadata = ["latex", "wikidata"];
7963
+ } else if (Array.isArray(options.metadata)) {
7964
+ defaultOptions.metadata = options.metadata;
7949
7965
  }
7950
7966
  if (options.fractionalDigits === "auto")
7951
7967
  defaultOptions.fractionalDigits = -this.engine.precision;
@@ -10020,6 +10036,64 @@ var ComputeEngineCore = (() => {
10020
10036
  }
10021
10037
 
10022
10038
  // src/compute-engine/latex-syntax/dictionary/definitions-core.ts
10039
+ var COMPONENT_ACCESS_HEADS = {
10040
+ x: "First",
10041
+ y: "Second",
10042
+ z: "Third",
10043
+ real: "Real",
10044
+ re: "Real",
10045
+ imag: "Imaginary",
10046
+ im: "Imaginary",
10047
+ count: "Length",
10048
+ total: "Sum",
10049
+ max: "Max",
10050
+ min: "Min"
10051
+ };
10052
+ function memberHead(name) {
10053
+ return COMPONENT_ACCESS_HEADS[name] ?? null;
10054
+ }
10055
+ function parseComponentAccess(parser, lhs) {
10056
+ parser.skipVisualSpace();
10057
+ if (parser.match("\\operatorname")) {
10058
+ const name = parser.parseStringGroup();
10059
+ if (name === null) return null;
10060
+ const head = memberHead(name.trim());
10061
+ if (head === null) return null;
10062
+ return [head, lhs];
10063
+ }
10064
+ const tok = parser.peek;
10065
+ if (typeof tok === "string" && tok.startsWith("\\")) {
10066
+ const bare = tok.slice(1);
10067
+ const head = memberHead(bare);
10068
+ if (head !== null) {
10069
+ parser.nextToken();
10070
+ return [head, lhs];
10071
+ }
10072
+ return null;
10073
+ }
10074
+ if (typeof tok === "string" && /^[a-zA-Z]$/.test(tok)) {
10075
+ const head = memberHead(tok);
10076
+ if (head === null) return null;
10077
+ parser.nextToken();
10078
+ return [head, lhs];
10079
+ }
10080
+ return null;
10081
+ }
10082
+ function parseWhenRestriction(parser, lhs, close) {
10083
+ parser.addBoundary(close);
10084
+ parser.skipVisualSpace();
10085
+ const cond = parser.parseExpression({ minPrec: 0 });
10086
+ if (cond === null) {
10087
+ parser.removeBoundary();
10088
+ return null;
10089
+ }
10090
+ parser.skipVisualSpace();
10091
+ if (!parser.matchBoundary()) {
10092
+ parser.removeBoundary();
10093
+ return null;
10094
+ }
10095
+ return ["When", lhs, cond];
10096
+ }
10023
10097
  function parseSequence(parser, terminator, lhs, prec, sep) {
10024
10098
  if (terminator && terminator.minPrec >= prec) return null;
10025
10099
  const result = lhs ? [lhs] : ["Nothing"];
@@ -10491,6 +10565,15 @@ var ComputeEngineCore = (() => {
10491
10565
  }
10492
10566
  },
10493
10567
  { name: "LatexTokens", serialize: serializeLatexTokens },
10568
+ // Component-access postfix: expr.member (C3)
10569
+ // The '.' trigger is consumed before the parse function is called.
10570
+ // Precedence 850 > 810 (At/indexing) so .x chains tightly.
10571
+ {
10572
+ kind: "postfix",
10573
+ precedence: 850,
10574
+ latexTrigger: ["."],
10575
+ parse: parseComponentAccess
10576
+ },
10494
10577
  {
10495
10578
  name: "At",
10496
10579
  kind: "postfix",
@@ -10511,6 +10594,29 @@ var ComputeEngineCore = (() => {
10511
10594
  latexTrigger: ["\\left", "\\lbrack"],
10512
10595
  parse: parseAt("\\right", "\\rbrack")
10513
10596
  },
10597
+ // When-restriction: `expr\left\{cond\right\}` → `When(expr, cond)` (D3)
10598
+ {
10599
+ name: "When",
10600
+ kind: "postfix",
10601
+ precedence: 800,
10602
+ latexTrigger: ["\\left", "\\{"],
10603
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\right", "\\}"]),
10604
+ serialize: (serializer, expr2) => {
10605
+ const e = operand(expr2, 1);
10606
+ const cond = operand(expr2, 2);
10607
+ if (!e || !cond) return "";
10608
+ const clauses = operator(cond) === "And" ? operands(cond) ?? [] : [cond];
10609
+ const inner = clauses.map((c) => `\\left\\{${serializer.serialize(c)}\\right\\}`).join("");
10610
+ return `${serializer.serialize(e)}${inner}`;
10611
+ }
10612
+ },
10613
+ // When-restriction: bare `expr\{cond\}` → `When(expr, cond)`
10614
+ {
10615
+ kind: "postfix",
10616
+ precedence: 800,
10617
+ latexTrigger: ["\\{"],
10618
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\}"])
10619
+ },
10514
10620
  {
10515
10621
  kind: "postfix",
10516
10622
  latexTrigger: ["_"],
@@ -10593,6 +10699,29 @@ var ComputeEngineCore = (() => {
10593
10699
  return "";
10594
10700
  }
10595
10701
  },
10702
+ // Additional triggers for Range: `...`, `\ldots`, and `\dots` are
10703
+ // equivalent to `..` when used as infix operators (e.g. `[1...9]`).
10704
+ // No `name` field here — names must be unique per the dictionary rules;
10705
+ // the first Range entry owns the name. When there is no LHS the symbol
10706
+ // entries near the top of the file still fire (ContinuationPlaceholder).
10707
+ {
10708
+ latexTrigger: [".", ".", "."],
10709
+ kind: "infix",
10710
+ precedence: 800,
10711
+ parse: parseRange
10712
+ },
10713
+ {
10714
+ latexTrigger: ["\\ldots"],
10715
+ kind: "infix",
10716
+ precedence: 800,
10717
+ parse: parseRange
10718
+ },
10719
+ {
10720
+ latexTrigger: ["\\dots"],
10721
+ kind: "infix",
10722
+ precedence: 800,
10723
+ parse: parseRange
10724
+ },
10596
10725
  {
10597
10726
  latexTrigger: [";"],
10598
10727
  kind: "infix",
@@ -10777,13 +10906,24 @@ var ComputeEngineCore = (() => {
10777
10906
  const args = operands(expr2);
10778
10907
  if (!args || args.length < 2) return "";
10779
10908
  const body = args[0];
10780
- const indexing = args[1];
10781
- if (operator(indexing) === "Element") {
10782
- const index = operand(indexing, 1);
10783
- const range2 = operand(indexing, 2);
10784
- if (operator(range2) === "Range") {
10785
- const lo = operand(range2, 1);
10786
- const hi = operand(range2, 2);
10909
+ const elements = args.slice(1);
10910
+ const allElements = elements.every((e) => operator(e) === "Element");
10911
+ if (!allElements) {
10912
+ return joinLatex([
10913
+ "\\operatorname{Loop}(",
10914
+ serializer.serialize(body),
10915
+ ", ",
10916
+ serializer.serialize(elements[0]),
10917
+ ")"
10918
+ ]);
10919
+ }
10920
+ if (elements.length === 1) {
10921
+ const elem = elements[0];
10922
+ const index = operand(elem, 1);
10923
+ const coll = operand(elem, 2);
10924
+ if (operator(coll) === "Range") {
10925
+ const lo = operand(coll, 1);
10926
+ const hi = operand(coll, 2);
10787
10927
  return joinLatex([
10788
10928
  "\\text{for }",
10789
10929
  serializer.serialize(index),
@@ -10795,13 +10935,27 @@ var ComputeEngineCore = (() => {
10795
10935
  serializer.serialize(body)
10796
10936
  ]);
10797
10937
  }
10938
+ return joinLatex([
10939
+ serializer.serialize(body),
10940
+ " \\operatorname{for} ",
10941
+ serializer.serialize(index),
10942
+ " = ",
10943
+ serializer.serialize(coll)
10944
+ ]);
10798
10945
  }
10946
+ const bindings = elements.map((elem) => {
10947
+ const name = operand(elem, 1);
10948
+ const coll = operand(elem, 2);
10949
+ return joinLatex([
10950
+ serializer.serialize(name),
10951
+ " = ",
10952
+ serializer.serialize(coll)
10953
+ ]);
10954
+ }).join(", ");
10799
10955
  return joinLatex([
10800
- "\\operatorname{Loop}(",
10801
10956
  serializer.serialize(body),
10802
- ", ",
10803
- serializer.serialize(indexing),
10804
- ")"
10957
+ " \\operatorname{for} ",
10958
+ bindings
10805
10959
  ]);
10806
10960
  }
10807
10961
  },
@@ -10834,6 +10988,18 @@ var ComputeEngineCore = (() => {
10834
10988
  precedence: 245,
10835
10989
  parse: (parser, until) => parseForExpression(parser, until)
10836
10990
  },
10991
+ // \operatorname{for} as postfix infix (list comprehension):
10992
+ // `body \operatorname{for} x = L_1, y = L_2`
10993
+ // Precedence 19 — just below comma (20) so the body is allowed to use
10994
+ // any operator (including comma sequencing) up to the keyword, and the
10995
+ // bindings can be comma-separated below us.
10996
+ {
10997
+ symbolTrigger: "for",
10998
+ kind: "infix",
10999
+ associativity: "none",
11000
+ precedence: 19,
11001
+ parse: (parser, lhs, until) => parseForComprehension(parser, lhs, until)
11002
+ },
10837
11003
  // \operatorname{break}
10838
11004
  {
10839
11005
  symbolTrigger: "break",
@@ -11038,7 +11204,10 @@ var ComputeEngineCore = (() => {
11038
11204
  if (!sym2 || !parser.getSymbolType(sym2).matches("function")) return null;
11039
11205
  parser.addBoundary([")"]);
11040
11206
  const expr2 = parser.parseExpression(until);
11041
- if (!parser.matchBoundary()) return null;
11207
+ if (!parser.matchBoundary()) {
11208
+ parser.removeBoundary();
11209
+ return null;
11210
+ }
11042
11211
  if (!parser.match("<}>")) return null;
11043
11212
  return ["Derivative", lhs, expr2];
11044
11213
  }
@@ -11479,7 +11648,12 @@ var ComputeEngineCore = (() => {
11479
11648
  if (isEmptySequence(body)) return ["List"];
11480
11649
  const h = operator(body);
11481
11650
  if (h === "Range" || h === "Linspace") return body;
11482
- if (h === "Sequence") return ["List", ...operands(body)];
11651
+ if (h === "Sequence") {
11652
+ const elems = operands(body);
11653
+ const inferred = tryInferRangeFromElements(elems, parser);
11654
+ if (inferred) return inferred;
11655
+ return ["List", ...elems];
11656
+ }
11483
11657
  if (h === "Delimiter") {
11484
11658
  const delim = stringValue(operand(body, 2)) ?? "...";
11485
11659
  if (delim === ";" || delim === ".;.") {
@@ -11492,12 +11666,37 @@ var ComputeEngineCore = (() => {
11492
11666
  }
11493
11667
  if (delim === "," || delim === ".,.") {
11494
11668
  body = operand(body, 1);
11495
- if (operator(body) === "Sequence") return ["List", ...operands(body)];
11669
+ if (operator(body) === "Sequence") {
11670
+ const elems = operands(body);
11671
+ const inferred = tryInferRangeFromElements(elems, parser);
11672
+ if (inferred) return inferred;
11673
+ return ["List", ...elems];
11674
+ }
11496
11675
  return ["List", body ?? "Nothing"];
11497
11676
  }
11498
11677
  }
11499
11678
  return ["List", body];
11500
11679
  }
11680
+ function tryInferRangeFromElements(elems, parser) {
11681
+ if (elems.length < 4) return null;
11682
+ const penultimate = elems[elems.length - 2];
11683
+ if (symbol(penultimate) !== "ContinuationPlaceholder") return null;
11684
+ const samples = elems.slice(0, -2);
11685
+ const endExpr = elems[elems.length - 1];
11686
+ if (samples.length < 2) return null;
11687
+ const sampleNums = samples.map(machineValue);
11688
+ if (sampleNums.some((n) => n === null)) return null;
11689
+ const nums = sampleNums;
11690
+ const step = nums[nums.length - 1] - nums[nums.length - 2];
11691
+ const tol = parser.options.tolerance;
11692
+ if (Math.abs(step) < tol)
11693
+ return parser.error("degenerate-range-step", parser.index);
11694
+ for (let i = 1; i < nums.length; i++) {
11695
+ if (Math.abs(nums[i] - nums[i - 1] - step) > tol)
11696
+ return parser.error("inconsistent-range-samples", parser.index);
11697
+ }
11698
+ return ["Range", nums[0], endExpr, step];
11699
+ }
11501
11700
  function serializeList(serializer, expr2) {
11502
11701
  if (nops(expr2) > 1 && operands(expr2).every((x) => {
11503
11702
  const op = operator(x);
@@ -11749,6 +11948,35 @@ var ComputeEngineCore = (() => {
11749
11948
  ["Element", index, ["Range", lower, upper]]
11750
11949
  ];
11751
11950
  }
11951
+ function parseForComprehension(parser, lhs, until) {
11952
+ const bindingTerminator = {
11953
+ minPrec: 21,
11954
+ // Above comma (20) and ; (19), so `x = L_1` is captured whole
11955
+ condition: (p) => {
11956
+ if (until?.condition?.(p)) return true;
11957
+ const saved = p.index;
11958
+ p.skipVisualSpace();
11959
+ const isComma = p.peek === ",";
11960
+ p.index = saved;
11961
+ return isComma;
11962
+ }
11963
+ };
11964
+ const elements = [];
11965
+ do {
11966
+ parser.skipVisualSpace();
11967
+ const binding = parser.parseExpression(bindingTerminator);
11968
+ if (binding === null) break;
11969
+ const op = operator(binding);
11970
+ if (op !== "Equal" && op !== "Assign") return null;
11971
+ const name = operand(binding, 1);
11972
+ const list = operand(binding, 2);
11973
+ if (!name || !list) return null;
11974
+ elements.push(["Element", name, list]);
11975
+ parser.skipVisualSpace();
11976
+ } while (parser.match(","));
11977
+ if (elements.length === 0) return null;
11978
+ return ["Loop", lhs, ...elements];
11979
+ }
11752
11980
  function parseWhereExpression(parser, lhs, until) {
11753
11981
  const bindingTerminator = {
11754
11982
  minPrec: 21,
@@ -13667,6 +13895,8 @@ var ComputeEngineCore = (() => {
13667
13895
  precedence: DIVISION_PRECEDENCE,
13668
13896
  parse: "Mod"
13669
13897
  },
13898
+ // Function-style alias: `\operatorname{mod}(a, b)`
13899
+ { latexTrigger: "\\operatorname{mod}", parse: "Mod" },
13670
13900
  {
13671
13901
  latexTrigger: "\\pmod",
13672
13902
  kind: "prefix",
@@ -13907,6 +14137,13 @@ var ComputeEngineCore = (() => {
13907
14137
  const rhs = serializer.wrap(operand(expr2, 2), ADDITION_PRECEDENCE + 3);
13908
14138
  return joinLatex([lhs, "-", rhs]);
13909
14139
  }
14140
+ },
14141
+ // Euclidean distance between two points (tuples of numbers).
14142
+ {
14143
+ name: "Distance",
14144
+ latexTrigger: ["\\operatorname{distance}"],
14145
+ kind: "function",
14146
+ serialize: (serializer, expr2) => "\\operatorname{distance}" + serializer.wrapArguments(expr2)
13910
14147
  }
13911
14148
  ];
13912
14149
  function getIndexAssignment(expr2, upper) {
@@ -15331,7 +15568,9 @@ var ComputeEngineCore = (() => {
15331
15568
  if (!expr2 || !symbol(expr2)) return null;
15332
15569
  return ["Mean", expr2];
15333
15570
  }
15334
- }
15571
+ },
15572
+ // Function-style alias: `\operatorname{var}(...)`
15573
+ { latexTrigger: "\\operatorname{var}", parse: "Variance" }
15335
15574
  ];
15336
15575
 
15337
15576
  // src/compute-engine/numerics/unit-data.ts
@@ -16432,7 +16671,7 @@ var ComputeEngineCore = (() => {
16432
16671
  36: "\\qquad"
16433
16672
  }[v] ?? "";
16434
16673
  }
16435
- }
16674
+ },
16436
16675
  // if (
16437
16676
  // [
16438
16677
  // '\\!',
@@ -16456,6 +16695,121 @@ var ComputeEngineCore = (() => {
16456
16695
  // name: '',
16457
16696
  // trigger: '\\check',
16458
16697
  // },
16698
+ // ---------------------------------------------------------------------------
16699
+ // Function-style aliases for collection / random operators that some
16700
+ // notations write in lowercase (e.g. `\operatorname{shuffle}(L)`).
16701
+ // The capitalized library entries already exist; these are pure parse
16702
+ // aliases so the lowercase names don't land in `unsupported-operator`.
16703
+ // ---------------------------------------------------------------------------
16704
+ { latexTrigger: "\\operatorname{random}", parse: "Random" },
16705
+ { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16706
+ { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16707
+ { latexTrigger: "\\operatorname{join}", parse: "Join" },
16708
+ // ---------------------------------------------------------------------------
16709
+ // Geometric primitive heads. Registered as known typed heads so consumers
16710
+ // can branch on the operator name; CE itself doesn't render them. The
16711
+ // library entries (with no evaluator) live in `library/core.ts`.
16712
+ // ---------------------------------------------------------------------------
16713
+ {
16714
+ name: "Triangle",
16715
+ latexTrigger: ["\\operatorname{triangle}"],
16716
+ kind: "function",
16717
+ serialize: (serializer, expr2) => "\\operatorname{triangle}" + serializer.wrapArguments(expr2)
16718
+ },
16719
+ // Desmos's geometric `vector(p1, p2)` — a directed segment between two
16720
+ // points. Routed to a dedicated head (not the existing column-vector
16721
+ // `Vector`, which has a narrower `(number+) -> vector` signature).
16722
+ {
16723
+ name: "GeometricVector",
16724
+ latexTrigger: ["\\operatorname{vector}"],
16725
+ kind: "function",
16726
+ serialize: (serializer, expr2) => "\\operatorname{vector}" + serializer.wrapArguments(expr2)
16727
+ },
16728
+ {
16729
+ name: "Sphere",
16730
+ latexTrigger: ["\\operatorname{sphere}"],
16731
+ kind: "function",
16732
+ serialize: (serializer, expr2) => "\\operatorname{sphere}" + serializer.wrapArguments(expr2)
16733
+ },
16734
+ {
16735
+ name: "Segment",
16736
+ latexTrigger: ["\\operatorname{segment}"],
16737
+ kind: "function",
16738
+ serialize: (serializer, expr2) => "\\operatorname{segment}" + serializer.wrapArguments(expr2)
16739
+ }
16740
+ ];
16741
+
16742
+ // src/compute-engine/latex-syntax/dictionary/definitions-colors.ts
16743
+ var DEFINITIONS_COLORS = [
16744
+ // Color constructors (one per colorspace, preserves space on evaluation)
16745
+ {
16746
+ name: "Rgb",
16747
+ latexTrigger: ["\\operatorname{rgb}"],
16748
+ kind: "function",
16749
+ serialize: (serializer, expr2) => "\\operatorname{rgb}" + serializer.wrapArguments(expr2)
16750
+ },
16751
+ {
16752
+ name: "Hsv",
16753
+ latexTrigger: ["\\operatorname{hsv}"],
16754
+ kind: "function",
16755
+ serialize: (serializer, expr2) => "\\operatorname{hsv}" + serializer.wrapArguments(expr2)
16756
+ },
16757
+ {
16758
+ name: "Hsl",
16759
+ latexTrigger: ["\\operatorname{hsl}"],
16760
+ kind: "function",
16761
+ serialize: (serializer, expr2) => "\\operatorname{hsl}" + serializer.wrapArguments(expr2)
16762
+ },
16763
+ {
16764
+ name: "Oklab",
16765
+ latexTrigger: ["\\operatorname{oklab}"],
16766
+ kind: "function",
16767
+ serialize: (serializer, expr2) => "\\operatorname{oklab}" + serializer.wrapArguments(expr2)
16768
+ },
16769
+ {
16770
+ name: "Oklch",
16771
+ latexTrigger: ["\\operatorname{oklch}"],
16772
+ kind: "function",
16773
+ serialize: (serializer, expr2) => "\\operatorname{oklch}" + serializer.wrapArguments(expr2)
16774
+ },
16775
+ // Conversion functions (color → color in the named space)
16776
+ {
16777
+ name: "AsRgb",
16778
+ latexTrigger: ["\\operatorname{asRgb}"],
16779
+ kind: "function",
16780
+ serialize: (serializer, expr2) => "\\operatorname{asRgb}" + serializer.wrapArguments(expr2)
16781
+ },
16782
+ {
16783
+ name: "AsHsv",
16784
+ latexTrigger: ["\\operatorname{asHsv}"],
16785
+ kind: "function",
16786
+ serialize: (serializer, expr2) => "\\operatorname{asHsv}" + serializer.wrapArguments(expr2)
16787
+ },
16788
+ {
16789
+ name: "AsHsl",
16790
+ latexTrigger: ["\\operatorname{asHsl}"],
16791
+ kind: "function",
16792
+ serialize: (serializer, expr2) => "\\operatorname{asHsl}" + serializer.wrapArguments(expr2)
16793
+ },
16794
+ {
16795
+ name: "AsOklab",
16796
+ latexTrigger: ["\\operatorname{asOklab}"],
16797
+ kind: "function",
16798
+ serialize: (serializer, expr2) => "\\operatorname{asOklab}" + serializer.wrapArguments(expr2)
16799
+ },
16800
+ {
16801
+ name: "AsOklch",
16802
+ latexTrigger: ["\\operatorname{asOklch}"],
16803
+ kind: "function",
16804
+ serialize: (serializer, expr2) => "\\operatorname{asOklch}" + serializer.wrapArguments(expr2)
16805
+ },
16806
+ // Perceptual difference (returns a scalar in [0, ~1])
16807
+ {
16808
+ name: "ColorDelta",
16809
+ latexTrigger: ["\\operatorname{colorDelta}"],
16810
+ kind: "function",
16811
+ serialize: (serializer, expr2) => "\\operatorname{colorDelta}" + serializer.wrapArguments(expr2)
16812
+ }
16459
16813
  ];
16460
16814
 
16461
16815
  // src/compute-engine/latex-syntax/dictionary/default-dictionary.ts
@@ -16486,7 +16840,8 @@ var ComputeEngineCore = (() => {
16486
16840
  ...DEFINITIONS_STATISTICS,
16487
16841
  ...DEFINITIONS_UNITS,
16488
16842
  ...DEFINITIONS_OTHERS,
16489
- ...DEFINITIONS_PHYSICS
16843
+ ...DEFINITIONS_PHYSICS,
16844
+ ...DEFINITIONS_COLORS
16490
16845
  ];
16491
16846
 
16492
16847
  // src/math-json/symbols.ts
@@ -16651,6 +17006,17 @@ var ComputeEngineCore = (() => {
16651
17006
  } else if (Array.isArray(openTrigger) && openTrigger.length > 0) {
16652
17007
  openTokens.push(openTrigger[0]);
16653
17008
  }
17009
+ const closeTrigger = indexedEntry.closeTrigger;
17010
+ const closeTokens = /* @__PURE__ */ new Set();
17011
+ if (typeof closeTrigger === "string") {
17012
+ const variants = DELIMITER_SHORTHAND[closeTrigger];
17013
+ if (variants) for (const v of variants) closeTokens.add(v);
17014
+ else closeTokens.add(closeTrigger);
17015
+ if (closeTrigger === "||") closeTokens.add("|");
17016
+ } else if (Array.isArray(closeTrigger) && closeTrigger.length > 0) {
17017
+ closeTokens.add(closeTrigger[0]);
17018
+ }
17019
+ indexedEntry.closeTokens = closeTokens;
16654
17020
  for (const token of openTokens) {
16655
17021
  const existing = result.matchfixByOpen.get(token);
16656
17022
  if (existing) {
@@ -16842,11 +17208,7 @@ var ComputeEngineCore = (() => {
16842
17208
  if (style === "scaled")
16843
17209
  return joinLatex([`\\left${openDelim}`, inner, `\\right${closeDelim}`]);
16844
17210
  if (style === "big")
16845
- return joinLatex([
16846
- `\\Bigl${openDelim}`,
16847
- inner,
16848
- `\\Bigr${closeDelim}`
16849
- ]);
17211
+ return joinLatex([`\\Bigl${openDelim}`, inner, `\\Bigr${closeDelim}`]);
16850
17212
  return joinLatex([openDelim, inner, closeDelim]);
16851
17213
  };
16852
17214
  }
@@ -18197,7 +18559,7 @@ var ComputeEngineCore = (() => {
18197
18559
  }
18198
18560
  if (this.match("\\hskip") || this.match("\\kern")) {
18199
18561
  this.skipSpace();
18200
- this.match("-") || this.match("+");
18562
+ if (!this.match("-")) this.match("+");
18201
18563
  while (/^[\d.]$/.test(this.peek)) this.nextToken();
18202
18564
  for (const unit of [
18203
18565
  "pt",
@@ -18711,6 +19073,19 @@ var ComputeEngineCore = (() => {
18711
19073
  }
18712
19074
  for (const def of defs) {
18713
19075
  this.index = start;
19076
+ if (def.closeTokens.size > 0) {
19077
+ let found = false;
19078
+ const tokens = this._tokens;
19079
+ for (let i = start; i < tokens.length; i++) {
19080
+ if (def.closeTokens.has(tokens[i])) {
19081
+ found = true;
19082
+ break;
19083
+ }
19084
+ }
19085
+ if (!found) continue;
19086
+ }
19087
+ if (typeof def.openTrigger === "string" && def.openTrigger === "." && !OPEN_DELIMITER_PREFIX[currentToken])
19088
+ continue;
18714
19089
  const matched = this.matchDelimiter(def.openTrigger, def.closeTrigger);
18715
19090
  if (!matched) continue;
18716
19091
  const bodyStart = this.index;
@@ -19373,6 +19748,19 @@ var ComputeEngineCore = (() => {
19373
19748
  } while (postfix !== null);
19374
19749
  }
19375
19750
  if (result !== null) result = this.parseSupsub(result);
19751
+ if (result !== null) {
19752
+ let postfix = null;
19753
+ let index = this.index;
19754
+ do {
19755
+ postfix = this.parsePostfixOperator(result, until);
19756
+ result = postfix ?? result;
19757
+ if (this.index === index && postfix !== null) {
19758
+ console.assert(this.index !== index, "No token consumed");
19759
+ break;
19760
+ }
19761
+ index = this.index;
19762
+ } while (postfix !== null);
19763
+ }
19376
19764
  if (result === null) {
19377
19765
  result = this.options.parseUnexpectedToken?.(null, this) ?? null;
19378
19766
  if (result === null && this.peek.startsWith("\\")) {
@@ -19881,6 +20269,28 @@ var ComputeEngineCore = (() => {
19881
20269
  }
19882
20270
 
19883
20271
  // src/compute-engine/latex-syntax/serializer.ts
20272
+ var DOT_NOTATION_MAP = {
20273
+ First: ".x",
20274
+ Second: ".y",
20275
+ Third: ".z",
20276
+ Real: ".\\operatorname{real}",
20277
+ Imaginary: ".\\operatorname{imag}",
20278
+ Length: ".\\operatorname{count}",
20279
+ Sum: ".\\operatorname{total}",
20280
+ Max: ".\\max",
20281
+ Min: ".\\min"
20282
+ };
20283
+ function trySerializeDotNotation(serializer, expr2) {
20284
+ if (!serializer.options.dotNotation) return null;
20285
+ const ops = operands(expr2);
20286
+ if (!ops || ops.length !== 1) return null;
20287
+ const head = operator(expr2);
20288
+ if (!head) return null;
20289
+ const suffix = DOT_NOTATION_MAP[head];
20290
+ if (suffix === void 0) return null;
20291
+ const lhs = serializer.wrap(ops[0], 810);
20292
+ return `${lhs}${suffix}`;
20293
+ }
19884
20294
  var ACCENT_MODIFIERS = {
19885
20295
  deg: (s) => `${s}\\degree`,
19886
20296
  prime: (s) => `${s}^{\\prime}`,
@@ -19924,6 +20334,7 @@ var ComputeEngineCore = (() => {
19924
20334
  constructor(dictionary, options) {
19925
20335
  this.dictionary = dictionary;
19926
20336
  this.options = {
20337
+ dotNotation: false,
19927
20338
  dmsFormat: false,
19928
20339
  angleNormalization: "none",
19929
20340
  ...options
@@ -20022,6 +20433,8 @@ var ComputeEngineCore = (() => {
20022
20433
  return def?.serialize?.(this, expr2) ?? serializeSymbol2(symbol(expr2)) ?? "";
20023
20434
  }
20024
20435
  serializeFunction(expr2, def) {
20436
+ const dotResult = trySerializeDotNotation(this, expr2);
20437
+ if (dotResult !== null) return dotResult;
20025
20438
  if (def?.serialize) return def.serialize(this, expr2);
20026
20439
  const h = operator(expr2);
20027
20440
  return serializeSymbol2(h, "auto") + this.wrapArguments(expr2);
@@ -20264,6 +20677,8 @@ var ComputeEngineCore = (() => {
20264
20677
  preserveLatex: opts.preserveLatex ?? false,
20265
20678
  quantifierScope: opts.quantifierScope ?? "tight",
20266
20679
  timeDerivativeVariable: opts.timeDerivativeVariable ?? "t",
20680
+ // Standalone mode has no engine; use the same default as ComputeEngine
20681
+ tolerance: 1e-7,
20267
20682
  // Callbacks -- standalone mode has no engine, so these are stubs
20268
20683
  getSymbolType: (_id) => BoxedType.unknown,
20269
20684
  hasSubscriptEvaluate: (_id) => false,
@@ -20296,6 +20711,7 @@ var ComputeEngineCore = (() => {
20296
20711
  invisiblePlus: "",
20297
20712
  multiply: "\\times",
20298
20713
  missingSymbol: "\\blacksquare",
20714
+ dotNotation: false,
20299
20715
  dmsFormat: false,
20300
20716
  angleNormalization: "none",
20301
20717
  // Style callbacks -- use same defaults as the engine
@@ -27266,7 +27682,11 @@ ${lines.join("\n")}`;
27266
27682
  //
27267
27683
  Range: {
27268
27684
  complexity: 8200,
27269
- signature: "(number, number?, step: number?) -> indexed_collection<integer>",
27685
+ signature: "(number, number?, step: number?) -> indexed_collection<number>",
27686
+ type: (ops) => {
27687
+ const allInt = ops.every((op) => op.isInteger);
27688
+ return allInt ? parseType("indexed_collection<integer>") : parseType("indexed_collection<number>");
27689
+ },
27270
27690
  canonical: (ops, { engine: ce }) => {
27271
27691
  if (ops.length === 0) return null;
27272
27692
  if (ops.length === 1) return ce._fn("Range", [ce.One, ops[0].canonical]);
@@ -27290,19 +27710,26 @@ ${lines.join("\n")}`;
27290
27710
  const [lower, upper, step] = range(expr2);
27291
27711
  if (step === 0) return 0;
27292
27712
  if (!isFinite(lower) || !isFinite(upper)) return Infinity;
27293
- return 1 + Math.max(0, Math.floor((upper - lower) / step));
27713
+ return Math.max(0, Math.floor((upper - lower) / step) + 1);
27294
27714
  },
27295
27715
  contains: (expr2, target) => {
27296
- if (!target.type.matches("integer")) return false;
27297
27716
  const t = target.re;
27717
+ if (!isFinite(t)) return false;
27298
27718
  const [lower, upper, step] = range(expr2);
27299
27719
  if (step === 0) return false;
27300
- if (step > 0) return t >= lower && t <= upper;
27301
- return t <= lower && t >= upper;
27720
+ if (step > 0) {
27721
+ if (t < lower || t > upper) return false;
27722
+ } else {
27723
+ if (t > lower || t < upper) return false;
27724
+ }
27725
+ const k = (t - lower) / step;
27726
+ const tol = expr2.engine.tolerance;
27727
+ const kRounded = Math.round(k);
27728
+ return kRounded >= 0 && Math.abs(k - kRounded) < tol;
27302
27729
  },
27303
27730
  iterator: (expr2) => {
27304
27731
  const [lower, upper, step] = range(expr2);
27305
- const maxCount = step === 0 ? 0 : Math.floor((upper - lower) / step) + 1;
27732
+ const maxCount = step === 0 ? 0 : Math.max(0, Math.floor((upper - lower) / step) + 1);
27306
27733
  let index = 1;
27307
27734
  return {
27308
27735
  next: () => {
@@ -27320,7 +27747,9 @@ ${lines.join("\n")}`;
27320
27747
  at: (expr2, index) => {
27321
27748
  if (typeof index !== "number") return void 0;
27322
27749
  const [lower, upper, step] = range(expr2);
27323
- if (index < 1 || index > 1 + (upper - lower) / step) return void 0;
27750
+ if (step === 0) return void 0;
27751
+ const maxCount = Math.max(0, Math.floor((upper - lower) / step) + 1);
27752
+ if (index < 1 || index > maxCount) return void 0;
27324
27753
  return expr2.engine.number(lower + step * (index - 1));
27325
27754
  },
27326
27755
  indexWhere: void 0,
@@ -27345,7 +27774,13 @@ ${lines.join("\n")}`;
27345
27774
  if (step > 0) return lower <= upper ? "positive" : "negative";
27346
27775
  return lower >= upper ? "positive" : "negative";
27347
27776
  },
27348
- elttype: (_expr) => "finite_integer"
27777
+ elttype: (expr2) => {
27778
+ if (!isFunction2(expr2)) return "finite_integer";
27779
+ for (let i = 1; i <= expr2.nops; i++) {
27780
+ if (!expr2[`op${i}`].isInteger) return "finite_real";
27781
+ }
27782
+ return "finite_integer";
27783
+ }
27349
27784
  }
27350
27785
  },
27351
27786
  Interval: {
@@ -27943,15 +28378,45 @@ ${lines.join("\n")}`;
27943
28378
  },
27944
28379
  First: {
27945
28380
  complexity: 8200,
27946
- signature: "(collection) -> any",
28381
+ signature: "(any) -> any",
27947
28382
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
27948
- evaluate: ([xs], { engine: ce }) => xs.at(1) ?? ce.Nothing
28383
+ evaluate: ([xs], { engine: ce }) => {
28384
+ if (!xs.isCollection)
28385
+ return ce.error([
28386
+ "incompatible-type",
28387
+ `'collection'`,
28388
+ xs.type.toString()
28389
+ ]);
28390
+ return xs.at(1) ?? ce.Nothing;
28391
+ }
27949
28392
  },
27950
28393
  Second: {
27951
28394
  complexity: 8200,
27952
- signature: "(collection) -> any",
28395
+ signature: "(any) -> any",
27953
28396
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
27954
- evaluate: ([xs], { engine: ce }) => xs.at(2) ?? ce.Nothing
28397
+ evaluate: ([xs], { engine: ce }) => {
28398
+ if (!xs.isCollection)
28399
+ return ce.error([
28400
+ "incompatible-type",
28401
+ `'collection'`,
28402
+ xs.type.toString()
28403
+ ]);
28404
+ return xs.at(2) ?? ce.Nothing;
28405
+ }
28406
+ },
28407
+ Third: {
28408
+ complexity: 8200,
28409
+ signature: "(any) -> any",
28410
+ type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28411
+ evaluate: ([xs], { engine: ce }) => {
28412
+ if (!xs.isCollection)
28413
+ return ce.error([
28414
+ "incompatible-type",
28415
+ `'collection'`,
28416
+ xs.type.toString()
28417
+ ]);
28418
+ return xs.at(3) ?? ce.Nothing;
28419
+ }
27955
28420
  },
27956
28421
  Last: {
27957
28422
  complexity: 8200,
@@ -28930,17 +29395,14 @@ ${lines.join("\n")}`;
28930
29395
  if (!isFunction2(expr2)) return [1, 0, 0];
28931
29396
  if (expr2.nops === 0) return [1, 0, 0];
28932
29397
  let op1 = expr2.op1.re;
28933
- if (!isFinite(op1)) op1 = 1;
28934
- else op1 = Math.round(op1);
29398
+ if (!isFinite(op1) && !op1) op1 = 1;
28935
29399
  if (expr2.nops === 1) return [1, op1, 1];
28936
29400
  let op2 = expr2.op2.re;
28937
29401
  if (!isFinite(op2) && !op2) op2 = 1;
28938
- else if (isFinite(op2)) op2 = Math.round(op2);
28939
- if (expr2.nops === 2) return [op1, op2, op2 > op1 ? 1 : -1];
29402
+ if (expr2.nops === 2) return [op1, op2, op2 >= op1 ? 1 : -1];
28940
29403
  let op3 = expr2.op3.re;
28941
- if (!isFinite(op3)) op3 = 1;
28942
- else op3 = Math.abs(Math.round(op3));
28943
- return [op1, op2, op1 < op2 ? op3 : -op3];
29404
+ if (!isFinite(op3) && !op3) op3 = 1;
29405
+ return [op1, op2, op3];
28944
29406
  }
28945
29407
  function rangeLast(r) {
28946
29408
  const [lower, upper, step] = r;
@@ -32273,6 +32735,29 @@ ${lines.join("\n")}`;
32273
32735
  signature: "(value*) -> number | list",
32274
32736
  evaluate: (xs, { engine }) => evaluateMinMax(engine, xs, "Infimum")
32275
32737
  },
32738
+ Distance: {
32739
+ description: "Euclidean distance between two points (tuples of numbers).",
32740
+ complexity: 6e3,
32741
+ signature: "(tuple, tuple) -> number",
32742
+ evaluate: ([a, b], { engine: ce }) => {
32743
+ if (!isFunction2(a) || !isFunction2(b))
32744
+ return ce.error("incompatible-type");
32745
+ if (a.operator !== "Tuple" || b.operator !== "Tuple")
32746
+ return ce.error("incompatible-type");
32747
+ if (a.ops.length !== b.ops.length || a.ops.length === 0)
32748
+ return ce.error("incompatible-type");
32749
+ let sumSq = 0;
32750
+ for (let i = 0; i < a.ops.length; i++) {
32751
+ const ai = a.ops[i].re;
32752
+ const bi = b.ops[i].re;
32753
+ if (!Number.isFinite(ai) || !Number.isFinite(bi))
32754
+ return ce.error("expected-value");
32755
+ const d = ai - bi;
32756
+ sumSq += d * d;
32757
+ }
32758
+ return ce.number(Math.sqrt(sumSq));
32759
+ }
32760
+ },
32276
32761
  Product: {
32277
32762
  description: "`Product(f, a, b)` computes the product of `f` from `a` to `b`",
32278
32763
  wikidata: "Q901718",
@@ -32805,16 +33290,11 @@ ${lines.join("\n")}`;
32805
33290
  );
32806
33291
  let condFn;
32807
33292
  if (typeof condition === "string") {
32808
- const latex = asLatexString(condition);
32809
- if (latex) {
32810
- const condPattern = ce.parse(latex, {
32811
- form: options?.canonical ? "canonical" : "raw"
32812
- }) ?? ce.expr("Nothing");
32813
- condFn = (x, _ce) => {
32814
- const evaluated = condPattern.subs(x).evaluate();
32815
- return isSymbol2(evaluated, "True");
32816
- };
32817
- }
33293
+ const condPattern = ce.parse(condition) ?? ce.expr("Nothing");
33294
+ condFn = (x, _ce) => {
33295
+ const evaluated = condPattern.subs(x).evaluate();
33296
+ return isSymbol2(evaluated, "True");
33297
+ };
32818
33298
  } else {
32819
33299
  if (condition !== void 0 && typeof condition !== "function")
32820
33300
  throw new Error(
@@ -32916,6 +33396,15 @@ ${e.message}
32916
33396
  function applyRule(rule, expr2, substitution, options) {
32917
33397
  if (!rule) return null;
32918
33398
  let canonical2 = options?.canonical ?? (expr2.isCanonical || expr2.isStructural);
33399
+ let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
33400
+ const because = id ?? "";
33401
+ const ce = expr2.engine;
33402
+ if (canonical2 && match2) {
33403
+ const awc = getWildcards(match2);
33404
+ const canonicalMatch = match2.canonical;
33405
+ const bwc = getWildcards(canonicalMatch);
33406
+ if (!awc.every((x) => bwc.includes(x))) return null;
33407
+ }
32919
33408
  let operandsMatched = false;
32920
33409
  if (isFunction2(expr2) && options?.recursive) {
32921
33410
  const newOps = expr2.ops.map((op) => {
@@ -32927,20 +33416,11 @@ ${e.message}
32927
33416
  if (operandsMatched) {
32928
33417
  if (!canonical2 && options?.canonical === void 0 && newOps.every((x) => x.isCanonical))
32929
33418
  canonical2 = true;
32930
- expr2 = expr2.engine.function(expr2.operator, newOps, {
33419
+ expr2 = ce.function(expr2.operator, newOps, {
32931
33420
  form: canonical2 ? "canonical" : "raw"
32932
33421
  });
32933
33422
  }
32934
33423
  }
32935
- let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
32936
- const because = id ?? "";
32937
- if (canonical2 && match2) {
32938
- const awc = getWildcards(match2);
32939
- const canonicalMatch = match2.canonical;
32940
- const bwc = getWildcards(canonicalMatch);
32941
- if (!awc.every((x) => bwc.includes(x)))
32942
- return operandsMatched ? { value: expr2, because } : null;
32943
- }
32944
33424
  const useVariations = rule.useVariations ?? options?.useVariations ?? false;
32945
33425
  const matchPermutations = options?.matchPermutations ?? true;
32946
33426
  onBeforeMatch?.(rule, expr2);
@@ -32959,7 +33439,7 @@ ${e.message}
32959
33439
  ...sub2
32960
33440
  };
32961
33441
  try {
32962
- if (!condition(conditionSub, expr2.engine))
33442
+ if (!condition(conditionSub, ce))
32963
33443
  return operandsMatched ? { value: expr2, because } : null;
32964
33444
  } catch (e) {
32965
33445
  console.error(
@@ -32974,7 +33454,8 @@ ${e.message}
32974
33454
  if (!canonical2 && options?.canonical === void 0 && replace2 instanceof _BoxedExpression && replace2.isCanonical)
32975
33455
  canonical2 = true;
32976
33456
  const result = typeof replace2 === "function" ? replace2(expr2, sub2) : replace2.subs(sub2, { canonical: canonical2 });
32977
- if (!result) return null;
33457
+ if (!result)
33458
+ return operandsMatched ? { value: canonical2 ? expr2.canonical : expr2, because } : null;
32978
33459
  onMatch?.(rule, expr2, result);
32979
33460
  if (isRuleStep(result))
32980
33461
  return canonical2 ? { ...result, value: result.value.canonical } : result;
@@ -38548,6 +39029,40 @@ ${e.message}
38548
39029
  else h = ((r - g) / d + 4) / 6;
38549
39030
  return { h: h * 360, s, l };
38550
39031
  }
39032
+ function hsvToRgb(h, s, v) {
39033
+ h = (h % 360 + 360) % 360;
39034
+ s = Math.max(0, Math.min(1, s));
39035
+ v = Math.max(0, Math.min(1, v));
39036
+ const c = v * s;
39037
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
39038
+ const m = v - c;
39039
+ let r = 0, g = 0, b = 0;
39040
+ if (h < 60) [r, g, b] = [c, x, 0];
39041
+ else if (h < 120) [r, g, b] = [x, c, 0];
39042
+ else if (h < 180) [r, g, b] = [0, c, x];
39043
+ else if (h < 240) [r, g, b] = [0, x, c];
39044
+ else if (h < 300) [r, g, b] = [x, 0, c];
39045
+ else [r, g, b] = [c, 0, x];
39046
+ return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };
39047
+ }
39048
+ function rgbToHsv(r, g, b) {
39049
+ r /= 255;
39050
+ g /= 255;
39051
+ b /= 255;
39052
+ const max2 = Math.max(r, g, b);
39053
+ const min2 = Math.min(r, g, b);
39054
+ const d = max2 - min2;
39055
+ let h = 0;
39056
+ if (d > 0) {
39057
+ if (max2 === r) h = (g - b) / d % 6;
39058
+ else if (max2 === g) h = (b - r) / d + 2;
39059
+ else h = (r - g) / d + 4;
39060
+ h *= 60;
39061
+ if (h < 0) h += 360;
39062
+ }
39063
+ const s = max2 === 0 ? 0 : d / max2;
39064
+ return { h, s, v: max2 };
39065
+ }
38551
39066
  function parseHexColor(s) {
38552
39067
  const hex = s.startsWith("#") ? s.substring(1) : s;
38553
39068
  let r, g, b;
@@ -38572,6 +39087,12 @@ ${e.message}
38572
39087
  if (alpha !== void 0) result.alpha = alpha;
38573
39088
  return result;
38574
39089
  }
39090
+ function asOklch(color) {
39091
+ if (typeof color === "string") return rgbToOklch(parseHexColor(color));
39092
+ if ("C" in color) return color;
39093
+ if ("a" in color && "b" in color) return oklabToOklch(color);
39094
+ return rgbToOklch(color);
39095
+ }
38575
39096
  function asRgb(color) {
38576
39097
  if (typeof color === "number") {
38577
39098
  return {
@@ -39003,6 +39524,13 @@ ${e.message}
39003
39524
  };
39004
39525
  function parseColor(s, darkMode) {
39005
39526
  const str = s.trim().toLowerCase();
39527
+ const opacityMatch = str.match(/^(.+?)\s*\/\s*(\d+(?:\.\d+)?)%?\s*$/);
39528
+ if (opacityMatch) {
39529
+ const base = parseColor(opacityMatch[1].trim(), darkMode);
39530
+ const opacity = Math.max(0, Math.min(100, parseFloat(opacityMatch[2])));
39531
+ const alpha = Math.round(opacity / 100 * 255);
39532
+ return base & 4294967040 | alpha;
39533
+ }
39006
39534
  if (str.startsWith("#")) {
39007
39535
  const hex = str.substring(1);
39008
39536
  let r, g, b, a = 255;
@@ -39135,14 +39663,6 @@ ${e.message}
39135
39663
  console.warn(`parseColor: unrecognized color "${s}"`);
39136
39664
  return 0;
39137
39665
  }
39138
- function parseColorToRgb01(s, darkMode) {
39139
- const color = parseColor(s, darkMode);
39140
- return [
39141
- (color >>> 24 & 255) / 255,
39142
- (color >>> 16 & 255) / 255,
39143
- (color >>> 8 & 255) / 255
39144
- ];
39145
- }
39146
39666
  function apca(bgColor, fgColor) {
39147
39667
  const bgRgb = asRgb(bgColor);
39148
39668
  const fgRgb = asRgb(fgColor);
@@ -39201,6 +39721,12 @@ ${e.message}
39201
39721
  const contrast2 = Math.abs(apca(fg2, bg));
39202
39722
  return contrast1 >= contrast2 ? asColorNumber(fg1) : asColorNumber(fg2);
39203
39723
  }
39724
+ function oklabDeltaE(a, b) {
39725
+ const dL = a.L - b.L;
39726
+ const da = a.a - b.a;
39727
+ const db = a.b - b.b;
39728
+ return Math.sqrt(dL * dL + da * da + db * db);
39729
+ }
39204
39730
  function lerpOklch(c1, c2, f) {
39205
39731
  const L = c1.L + (c2.L - c1.L) * f;
39206
39732
  const C = c1.C + (c2.C - c1.C) * f;
@@ -41962,14 +42488,30 @@ ${e.message}
41962
42488
  };
41963
42489
 
41964
42490
  // src/compute-engine/library/colors.ts
41965
- function colorNumberToTuple(ce, color) {
41966
- const r = (color >>> 24 & 255) / 255;
41967
- const g = (color >>> 16 & 255) / 255;
41968
- const b = (color >>> 8 & 255) / 255;
41969
- const a = (color & 255) / 255;
41970
- if (Math.abs(a - 1) < 1e-4)
41971
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
41972
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b), ce.number(a));
42491
+ function normalizeAlpha(a) {
42492
+ if (a === void 0) return void 0;
42493
+ if (!Number.isFinite(a)) return void 0;
42494
+ if (Math.abs(a - 1) < 1e-9) return void 0;
42495
+ return a;
42496
+ }
42497
+ function normalizeColorHead(ce, expr2) {
42498
+ if (!isFunction2(expr2) || !expr2.ops || expr2.ops.length < 4) return expr2;
42499
+ const alphaExpr = expr2.ops[3];
42500
+ if (!isNumber(alphaExpr)) return expr2;
42501
+ if (normalizeAlpha(alphaExpr.re) === void 0) {
42502
+ return ce.function(expr2.operator, [expr2.ops[0], expr2.ops[1], expr2.ops[2]]);
42503
+ }
42504
+ return expr2;
42505
+ }
42506
+ function colorNumberToOklch(ce, color) {
42507
+ const r = color >>> 24 & 255;
42508
+ const g = color >>> 16 & 255;
42509
+ const b = color >>> 8 & 255;
42510
+ const a = normalizeAlpha((color & 255) / 255);
42511
+ const c = rgbToOklch({ r, g, b });
42512
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42513
+ if (a !== void 0) args.push(ce.number(a));
42514
+ return ce.function("Oklch", args);
41973
42515
  }
41974
42516
  var ALL_PALETTES = {
41975
42517
  ...SEQUENTIAL_PALETTES,
@@ -41980,45 +42522,139 @@ ${e.message}
41980
42522
  t = Math.max(0, Math.min(1, t));
41981
42523
  const n = palette.length;
41982
42524
  if (n === 0) return ce.error("expected-value");
41983
- if (n === 1) return colorNumberToTuple(ce, parseColor(palette[0]));
42525
+ if (n === 1) return colorNumberToOklch(ce, parseColor(palette[0]));
41984
42526
  const pos = t * (n - 1);
41985
42527
  const i = Math.floor(pos);
41986
42528
  const frac = pos - i;
41987
- if (i >= n - 1) return colorNumberToTuple(ce, parseColor(palette[n - 1]));
41988
- if (frac < 1e-9) return colorNumberToTuple(ce, parseColor(palette[i]));
41989
- const rgb = interpolateOklch(palette[i], palette[i + 1], frac);
41990
- const r = rgb.r / 255;
41991
- const g = rgb.g / 255;
41992
- const b = rgb.b / 255;
41993
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42529
+ if (i >= n - 1) return colorNumberToOklch(ce, parseColor(palette[n - 1]));
42530
+ if (frac < 1e-9) return colorNumberToOklch(ce, parseColor(palette[i]));
42531
+ return oklchToExpr(
42532
+ ce,
42533
+ asOklch(interpolateOklch(palette[i], palette[i + 1], frac))
42534
+ );
42535
+ }
42536
+ var COLOR_OPERATORS = /* @__PURE__ */ new Set(["Rgb", "Hsv", "Hsl", "Oklab", "Oklch"]);
42537
+ function readColorExpr(arg) {
42538
+ if (!isFunction2(arg)) return null;
42539
+ if (!COLOR_OPERATORS.has(arg.operator)) return null;
42540
+ if (!arg.ops || arg.ops.length < 3) return null;
42541
+ const c0 = arg.ops[0].re;
42542
+ const c1 = arg.ops[1].re;
42543
+ const c2 = arg.ops[2].re;
42544
+ if (!Number.isFinite(c0) || !Number.isFinite(c1) || !Number.isFinite(c2))
42545
+ return null;
42546
+ const alpha = arg.ops.length >= 4 ? normalizeAlpha(arg.ops[3].re) : void 0;
42547
+ return { space: arg.operator, c0, c1, c2, alpha };
42548
+ }
42549
+ function colorExprToRgb(arg) {
42550
+ const c = readColorExpr(arg);
42551
+ if (!c) return null;
42552
+ const withAlpha = (rgb) => c.alpha !== void 0 ? { ...rgb, alpha: c.alpha } : rgb;
42553
+ switch (c.space) {
42554
+ case "Rgb":
42555
+ return withAlpha({ r: c.c0 * 255, g: c.c1 * 255, b: c.c2 * 255 });
42556
+ case "Hsv":
42557
+ return withAlpha(hsvToRgb(c.c0, c.c1, c.c2));
42558
+ case "Hsl":
42559
+ return withAlpha(hslToRgb(c.c0, c.c1, c.c2));
42560
+ case "Oklab":
42561
+ return withAlpha(oklabToRgb({ L: c.c0, a: c.c1, b: c.c2 }));
42562
+ case "Oklch":
42563
+ return withAlpha(oklchToRgb({ L: c.c0, C: c.c1, H: c.c2 }));
42564
+ }
42565
+ return null;
42566
+ }
42567
+ function colorExprToOklch(arg) {
42568
+ const c = readColorExpr(arg);
42569
+ if (!c) return null;
42570
+ switch (c.space) {
42571
+ case "Oklch":
42572
+ return asOklch({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42573
+ case "Oklab":
42574
+ return asOklch({ L: c.c0, a: c.c1, b: c.c2, alpha: c.alpha });
42575
+ case "Rgb":
42576
+ return asOklch({
42577
+ r: c.c0 * 255,
42578
+ g: c.c1 * 255,
42579
+ b: c.c2 * 255,
42580
+ alpha: c.alpha
42581
+ });
42582
+ case "Hsv": {
42583
+ const rgb = hsvToRgb(c.c0, c.c1, c.c2);
42584
+ return asOklch({ ...rgb, alpha: c.alpha });
42585
+ }
42586
+ case "Hsl": {
42587
+ const rgb = hslToRgb(c.c0, c.c1, c.c2);
42588
+ return asOklch({ r: rgb.r, g: rgb.g, b: rgb.b, alpha: c.alpha });
42589
+ }
42590
+ }
42591
+ return null;
42592
+ }
42593
+ function toOklch(ce, arg) {
42594
+ const direct = colorExprToOklch(arg);
42595
+ if (direct) return direct;
42596
+ const rgb = extractRgb(ce, arg);
42597
+ return rgb ? asOklch(rgb) : null;
42598
+ }
42599
+ function lerpOklchColor(a, b, t) {
42600
+ const L = a.L + (b.L - a.L) * t;
42601
+ const C = a.C + (b.C - a.C) * t;
42602
+ const aAchromatic = a.C < 1e-6;
42603
+ const bAchromatic = b.C < 1e-6;
42604
+ let H;
42605
+ if (aAchromatic && bAchromatic) H = a.H;
42606
+ else if (aAchromatic) H = b.H;
42607
+ else if (bAchromatic) H = a.H;
42608
+ else {
42609
+ let dH = b.H - a.H;
42610
+ if (dH > 180) dH -= 360;
42611
+ if (dH < -180) dH += 360;
42612
+ H = a.H + dH * t;
42613
+ if (H < 0) H += 360;
42614
+ if (H >= 360) H -= 360;
42615
+ }
42616
+ const alphaA = a.alpha ?? 1;
42617
+ const alphaB = b.alpha ?? 1;
42618
+ return { L, C, H, alpha: normalizeAlpha(alphaA + (alphaB - alphaA) * t) };
42619
+ }
42620
+ function oklchToExpr(ce, c) {
42621
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42622
+ if (c.alpha !== void 0) args.push(ce.number(c.alpha));
42623
+ return ce.function("Oklch", args);
41994
42624
  }
41995
42625
  function extractRgb(ce, arg) {
41996
42626
  if (isString(arg)) {
41997
42627
  const s = arg.string;
41998
42628
  if (!s) return void 0;
41999
42629
  const color = parseColor(s);
42000
- return {
42630
+ const rgb = {
42001
42631
  r: color >>> 24 & 255,
42002
42632
  g: color >>> 16 & 255,
42003
- b: color >>> 8 & 255,
42004
- alpha: (color & 255) / 255
42633
+ b: color >>> 8 & 255
42005
42634
  };
42635
+ const alpha = normalizeAlpha((color & 255) / 255);
42636
+ if (alpha !== void 0) rgb.alpha = alpha;
42637
+ return rgb;
42006
42638
  }
42639
+ const fromTyped = colorExprToRgb(arg);
42640
+ if (fromTyped) return fromTyped;
42007
42641
  if (arg.operator === "Tuple" && arg.ops && arg.ops.length >= 3) {
42008
42642
  const rgb = {
42009
42643
  r: arg.ops[0].re * 255,
42010
42644
  g: arg.ops[1].re * 255,
42011
42645
  b: arg.ops[2].re * 255
42012
42646
  };
42013
- if (arg.ops.length >= 4) rgb.alpha = arg.ops[3].re;
42647
+ if (arg.ops.length >= 4) {
42648
+ const alpha = normalizeAlpha(arg.ops[3].re);
42649
+ if (alpha !== void 0) rgb.alpha = alpha;
42650
+ }
42014
42651
  return rgb;
42015
42652
  }
42016
42653
  return void 0;
42017
42654
  }
42018
42655
  function componentsTuple(ce, components, alpha) {
42019
42656
  const args = components.map((v) => ce.number(v));
42020
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4)
42021
- args.push(ce.number(alpha));
42657
+ if (alpha !== void 0) args.push(ce.number(alpha));
42022
42658
  return ce.tuple(...args);
42023
42659
  }
42024
42660
  function rgbToHex(rgb) {
@@ -42026,7 +42662,7 @@ ${e.message}
42026
42662
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
42027
42663
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
42028
42664
  const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
42029
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
42665
+ if (rgb.alpha !== void 0) {
42030
42666
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
42031
42667
  return hex + a.toString(16).padStart(2, "0");
42032
42668
  }
@@ -42034,22 +42670,29 @@ ${e.message}
42034
42670
  }
42035
42671
  var COLORS_LIBRARY = {
42036
42672
  Color: {
42037
- description: "Convert a color string to a canonical sRGB tuple",
42673
+ description: "Parse a CSS-style color string to an Oklch color",
42038
42674
  complexity: 8e3,
42039
- signature: "(string) -> tuple",
42675
+ signature: "(string) -> color",
42040
42676
  evaluate: (ops, { engine: ce }) => {
42041
42677
  const input = isString(ops[0]) ? ops[0].string : void 0;
42042
42678
  if (!input) return ce.error("incompatible-type");
42043
42679
  const color = parseColor(input);
42044
42680
  if (color === 0 && input.trim().toLowerCase() !== "transparent")
42045
42681
  return ce.error("incompatible-type");
42046
- return colorNumberToTuple(ce, color);
42682
+ const r = color >>> 24 & 255;
42683
+ const g = color >>> 16 & 255;
42684
+ const b = color >>> 8 & 255;
42685
+ const a = normalizeAlpha((color & 255) / 255);
42686
+ const c = rgbToOklch({ r, g, b });
42687
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42688
+ if (a !== void 0) args.push(ce.number(a));
42689
+ return ce.function("Oklch", args);
42047
42690
  }
42048
42691
  },
42049
42692
  ColorToString: {
42050
42693
  description: "Convert a color to a string in the specified format",
42051
42694
  complexity: 8e3,
42052
- signature: "(any, string?) -> string",
42695
+ signature: "(color | string | tuple, string?) -> string",
42053
42696
  evaluate: (ops, { engine: ce }) => {
42054
42697
  const rgb = extractRgb(ce, ops[0]);
42055
42698
  if (!rgb) return ce.error("incompatible-type");
@@ -42061,7 +42704,7 @@ ${e.message}
42061
42704
  const r = Math.round(rgb.r);
42062
42705
  const g = Math.round(rgb.g);
42063
42706
  const b = Math.round(rgb.b);
42064
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42707
+ if (rgb.alpha !== void 0)
42065
42708
  return ce.string(`rgb(${r} ${g} ${b} / ${rgb.alpha})`);
42066
42709
  return ce.string(`rgb(${r} ${g} ${b})`);
42067
42710
  }
@@ -42070,17 +42713,17 @@ ${e.message}
42070
42713
  const h = Math.round(hsl.h * 10) / 10;
42071
42714
  const s = Math.round(hsl.s * 1e3) / 10;
42072
42715
  const l = Math.round(hsl.l * 1e3) / 10;
42073
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42716
+ if (rgb.alpha !== void 0)
42074
42717
  return ce.string(`hsl(${h} ${s}% ${l}% / ${rgb.alpha})`);
42075
42718
  return ce.string(`hsl(${h} ${s}% ${l}%)`);
42076
42719
  }
42077
42720
  case "oklch": {
42078
- const c = rgbToOklch(rgb);
42721
+ const c = colorExprToOklch(ops[0]) ?? asOklch(rgb);
42079
42722
  const L = Math.round(c.L * 1e3) / 1e3;
42080
42723
  const C = Math.round(c.C * 1e3) / 1e3;
42081
42724
  const H = Math.round(c.H * 10) / 10;
42082
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42083
- return ce.string(`oklch(${L} ${C} ${H} / ${rgb.alpha})`);
42725
+ if (c.alpha !== void 0)
42726
+ return ce.string(`oklch(${L} ${C} ${H} / ${c.alpha})`);
42084
42727
  return ce.string(`oklch(${L} ${C} ${H})`);
42085
42728
  }
42086
42729
  default:
@@ -42091,60 +42734,44 @@ ${e.message}
42091
42734
  ColorMix: {
42092
42735
  description: "Mix two colors in OKLCh space",
42093
42736
  complexity: 8e3,
42094
- signature: "(any, any, number?) -> tuple",
42737
+ signature: "(color | string | tuple, color | string | tuple, number?) -> color",
42095
42738
  evaluate: (ops, { engine: ce }) => {
42096
- const rgb1 = extractRgb(ce, ops[0]);
42097
- const rgb2 = extractRgb(ce, ops[1]);
42098
- if (!rgb1 || !rgb2) return ce.error("incompatible-type");
42099
42739
  let ratio = 0.5;
42100
42740
  if (ops.length >= 3 && ops[2] !== void 0) {
42101
42741
  ratio = ops[2].re;
42102
42742
  if (!Number.isFinite(ratio)) return ce.error("expected-value");
42103
42743
  ratio = Math.max(0, Math.min(1, ratio));
42104
42744
  }
42105
- const c1 = rgbToOklch(rgb1);
42106
- const c2 = rgbToOklch(rgb2);
42107
- const mixed = oklchToRgb(lerpOklch(c1, c2, ratio));
42108
- const r = mixed.r / 255;
42109
- const g = mixed.g / 255;
42110
- const b = mixed.b / 255;
42111
- const a1 = rgb1.alpha ?? 1;
42112
- const a2 = rgb2.alpha ?? 1;
42113
- const alpha = a1 + (a2 - a1) * ratio;
42114
- if (Math.abs(alpha - 1) > 1e-4)
42115
- return ce.tuple(
42116
- ce.number(r),
42117
- ce.number(g),
42118
- ce.number(b),
42119
- ce.number(alpha)
42120
- );
42121
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42745
+ const oklch1 = toOklch(ce, ops[0]);
42746
+ const oklch2 = toOklch(ce, ops[1]);
42747
+ if (!oklch1 || !oklch2) return ce.error("incompatible-type");
42748
+ return oklchToExpr(ce, lerpOklchColor(oklch1, oklch2, ratio));
42122
42749
  }
42123
42750
  },
42124
42751
  Colormap: {
42125
42752
  description: "Sample colors from a named palette",
42126
42753
  complexity: 8e3,
42127
- signature: "(string, number?) -> any",
42754
+ signature: "(string, number?) -> color | list<color>",
42128
42755
  evaluate: (ops, { engine: ce }) => {
42129
42756
  const name = isString(ops[0]) ? ops[0].string : void 0;
42130
42757
  if (!name) return ce.error("incompatible-type");
42131
42758
  const palette = ALL_PALETTES[name];
42132
42759
  if (!palette) return ce.error("expected-value", name);
42133
42760
  if (ops.length < 2 || ops[1] === void 0) {
42134
- const tuples = palette.map(
42135
- (hex) => colorNumberToTuple(ce, parseColor(hex))
42761
+ const colors = palette.map(
42762
+ (hex) => colorNumberToOklch(ce, parseColor(hex))
42136
42763
  );
42137
- return ce.function("List", tuples);
42764
+ return ce.function("List", colors);
42138
42765
  }
42139
42766
  const val = ops[1].re;
42140
42767
  if (!Number.isFinite(val)) return ce.error("expected-value");
42141
42768
  if (Number.isInteger(val) && val >= 2) {
42142
42769
  const n = val;
42143
- const tuples = [];
42770
+ const colors = [];
42144
42771
  for (let i = 0; i < n; i++) {
42145
- tuples.push(samplePalette(ce, palette, i / (n - 1)));
42772
+ colors.push(samplePalette(ce, palette, i / (n - 1)));
42146
42773
  }
42147
- return ce.function("List", tuples);
42774
+ return ce.function("List", colors);
42148
42775
  }
42149
42776
  return samplePalette(ce, palette, val);
42150
42777
  }
@@ -42152,12 +42779,25 @@ ${e.message}
42152
42779
  ColorToColorspace: {
42153
42780
  description: "Convert a color to components in a target color space",
42154
42781
  complexity: 8e3,
42155
- signature: "(any, string) -> tuple",
42782
+ signature: "(color | string | tuple, string) -> tuple",
42156
42783
  evaluate: (ops, { engine: ce }) => {
42157
- const rgb = extractRgb(ce, ops[0]);
42158
- if (!rgb) return ce.error("incompatible-type");
42159
42784
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42160
42785
  if (!space) return ce.error("incompatible-type");
42786
+ if (space === "oklch" || space === "oklab" || space === "lab") {
42787
+ const oklch2 = colorExprToOklch(ops[0]);
42788
+ if (oklch2) {
42789
+ if (space === "oklch")
42790
+ return componentsTuple(
42791
+ ce,
42792
+ [oklch2.L, oklch2.C, oklch2.H],
42793
+ oklch2.alpha
42794
+ );
42795
+ const lab = oklchToOklab(oklch2);
42796
+ return componentsTuple(ce, [lab.L, lab.a, lab.b], lab.alpha);
42797
+ }
42798
+ }
42799
+ const rgb = extractRgb(ce, ops[0]);
42800
+ if (!rgb) return ce.error("incompatible-type");
42161
42801
  const alpha = rgb.alpha;
42162
42802
  switch (space) {
42163
42803
  case "rgb":
@@ -42187,17 +42827,27 @@ ${e.message}
42187
42827
  ColorFromColorspace: {
42188
42828
  description: "Convert color space components to a canonical sRGB tuple",
42189
42829
  complexity: 8e3,
42190
- signature: "(tuple, string) -> tuple",
42830
+ signature: "(color | tuple, string) -> tuple",
42191
42831
  evaluate: (ops, { engine: ce }) => {
42192
- const tuple = ops[0];
42193
- if (!isFunction2(tuple) || tuple.operator !== "Tuple" || tuple.ops.length < 3)
42194
- return ce.error("incompatible-type");
42195
- const c0 = tuple.ops[0].re;
42196
- const c1 = tuple.ops[1].re;
42197
- const c2 = tuple.ops[2].re;
42198
- const alpha = tuple.ops.length >= 4 ? tuple.ops[3].re : void 0;
42199
42832
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42200
42833
  if (!space) return ce.error("incompatible-type");
42834
+ let c0, c1, c2;
42835
+ let alpha;
42836
+ const arg = ops[0];
42837
+ const typed = readColorExpr(arg);
42838
+ if (typed) {
42839
+ c0 = typed.c0;
42840
+ c1 = typed.c1;
42841
+ c2 = typed.c2;
42842
+ alpha = typed.alpha;
42843
+ } else if (isFunction2(arg) && arg.operator === "Tuple" && arg.ops.length >= 3) {
42844
+ c0 = arg.ops[0].re;
42845
+ c1 = arg.ops[1].re;
42846
+ c2 = arg.ops[2].re;
42847
+ alpha = arg.ops.length >= 4 ? arg.ops[3].re : void 0;
42848
+ } else {
42849
+ return ce.error("incompatible-type");
42850
+ }
42201
42851
  let rgb;
42202
42852
  switch (space) {
42203
42853
  case "rgb":
@@ -42233,7 +42883,7 @@ ${e.message}
42233
42883
  ColorContrast: {
42234
42884
  description: "APCA contrast ratio between two colors",
42235
42885
  complexity: 8e3,
42236
- signature: "(any, any) -> number",
42886
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42237
42887
  evaluate: (ops, { engine: ce }) => {
42238
42888
  const bgRgb = extractRgb(ce, ops[0]);
42239
42889
  const fgRgb = extractRgb(ce, ops[1]);
@@ -42244,19 +42894,186 @@ ${e.message}
42244
42894
  ContrastingColor: {
42245
42895
  description: "Choose the foreground color with better APCA contrast against a background",
42246
42896
  complexity: 8e3,
42247
- signature: "(any, any?, any?) -> tuple",
42897
+ signature: "(color | string | tuple, (color | string | tuple)?, (color | string | tuple)?) -> color",
42248
42898
  evaluate: (ops, { engine: ce }) => {
42249
42899
  const bgRgb = extractRgb(ce, ops[0]);
42250
42900
  if (!bgRgb) return ce.error("incompatible-type");
42901
+ let packed;
42251
42902
  if (ops.length >= 3 && ops[1] !== void 0 && ops[2] !== void 0) {
42252
42903
  const fg1 = extractRgb(ce, ops[1]);
42253
42904
  const fg2 = extractRgb(ce, ops[2]);
42254
42905
  if (!fg1 || !fg2) return ce.error("incompatible-type");
42255
- const result2 = contrastingColor({ bg: bgRgb, fg1, fg2 });
42256
- return colorNumberToTuple(ce, result2);
42906
+ packed = contrastingColor({ bg: bgRgb, fg1, fg2 });
42907
+ } else {
42908
+ packed = contrastingColor(bgRgb);
42909
+ }
42910
+ const r = (packed >>> 24 & 255) / 255;
42911
+ const g = (packed >>> 16 & 255) / 255;
42912
+ const b = (packed >>> 8 & 255) / 255;
42913
+ const alpha = normalizeAlpha((packed & 255) / 255);
42914
+ const args = [ce.number(r), ce.number(g), ce.number(b)];
42915
+ if (alpha !== void 0) args.push(ce.number(alpha));
42916
+ return ce.function("Rgb", args);
42917
+ }
42918
+ },
42919
+ // ---------------------------------------------------------------------------
42920
+ // Color constructors. Each preserves its colorspace on evaluation; the
42921
+ // operator name is the discriminator. Components are interpreted per
42922
+ // colorspace conventions (Rgb channels 0-1, Hsv/Hsl hue in degrees with
42923
+ // sat/value 0-1, Oklab/Oklch L 0-1 with standard a/b/C/H ranges). The
42924
+ // optional 4th argument is alpha in [0, 1]. No clamping at evaluation time.
42925
+ // ---------------------------------------------------------------------------
42926
+ Rgb: {
42927
+ description: "sRGB color (channels 0-1, optional alpha 0-1)",
42928
+ complexity: 8e3,
42929
+ signature: "(number, number, number, number?) -> color"
42930
+ },
42931
+ Hsv: {
42932
+ description: "HSV color (hue degrees, saturation/value 0-1, optional alpha)",
42933
+ complexity: 8e3,
42934
+ signature: "(number, number, number, number?) -> color"
42935
+ },
42936
+ Hsl: {
42937
+ description: "HSL color (hue degrees, saturation/lightness 0-1, optional alpha)",
42938
+ complexity: 8e3,
42939
+ signature: "(number, number, number, number?) -> color"
42940
+ },
42941
+ Oklab: {
42942
+ description: "OKLab color (L 0-1, a/b ~ -0.4..0.4, optional alpha)",
42943
+ complexity: 8e3,
42944
+ signature: "(number, number, number, number?) -> color"
42945
+ },
42946
+ Oklch: {
42947
+ description: "OKLCh color (L 0-1, C 0-~0.4, hue degrees, optional alpha)",
42948
+ complexity: 8e3,
42949
+ signature: "(number, number, number, number?) -> color"
42950
+ },
42951
+ // ---------------------------------------------------------------------------
42952
+ // Color-space conversions. Each accepts any of the five color heads and
42953
+ // returns the same color in the named space. If the input is already in
42954
+ // the target space, returns the input unchanged.
42955
+ // ---------------------------------------------------------------------------
42956
+ AsRgb: {
42957
+ description: "Convert any color to sRGB (channels 0-1)",
42958
+ complexity: 8e3,
42959
+ signature: "(color) -> color",
42960
+ evaluate: (ops, { engine: ce }) => {
42961
+ const arg = ops[0];
42962
+ if (isFunction2(arg) && arg.operator === "Rgb")
42963
+ return normalizeColorHead(ce, arg);
42964
+ const rgb = colorExprToRgb(arg);
42965
+ if (!rgb) return ce.error("incompatible-type");
42966
+ const args = [
42967
+ ce.number(rgb.r / 255),
42968
+ ce.number(rgb.g / 255),
42969
+ ce.number(rgb.b / 255)
42970
+ ];
42971
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42972
+ return ce.function("Rgb", args);
42973
+ }
42974
+ },
42975
+ AsHsv: {
42976
+ description: "Convert any color to HSV (hue degrees, s/v 0-1)",
42977
+ complexity: 8e3,
42978
+ signature: "(color) -> color",
42979
+ evaluate: (ops, { engine: ce }) => {
42980
+ const arg = ops[0];
42981
+ if (isFunction2(arg) && arg.operator === "Hsv")
42982
+ return normalizeColorHead(ce, arg);
42983
+ const rgb = colorExprToRgb(arg);
42984
+ if (!rgb) return ce.error("incompatible-type");
42985
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
42986
+ const args = [ce.number(hsv.h), ce.number(hsv.s), ce.number(hsv.v)];
42987
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42988
+ return ce.function("Hsv", args);
42989
+ }
42990
+ },
42991
+ AsHsl: {
42992
+ description: "Convert any color to HSL (hue degrees, s/l 0-1)",
42993
+ complexity: 8e3,
42994
+ signature: "(color) -> color",
42995
+ evaluate: (ops, { engine: ce }) => {
42996
+ const arg = ops[0];
42997
+ if (isFunction2(arg) && arg.operator === "Hsl")
42998
+ return normalizeColorHead(ce, arg);
42999
+ const rgb = colorExprToRgb(arg);
43000
+ if (!rgb) return ce.error("incompatible-type");
43001
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
43002
+ const args = [ce.number(hsl.h), ce.number(hsl.s), ce.number(hsl.l)];
43003
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43004
+ return ce.function("Hsl", args);
43005
+ }
43006
+ },
43007
+ AsOklab: {
43008
+ description: "Convert any color to OKLab",
43009
+ complexity: 8e3,
43010
+ signature: "(color) -> color",
43011
+ evaluate: (ops, { engine: ce }) => {
43012
+ const arg = ops[0];
43013
+ if (isFunction2(arg) && arg.operator === "Oklab")
43014
+ return normalizeColorHead(ce, arg);
43015
+ if (isFunction2(arg) && arg.operator === "Oklch") {
43016
+ const c = readColorExpr(arg);
43017
+ if (!c) return ce.error("incompatible-type");
43018
+ const lab2 = oklchToOklab({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
43019
+ const args2 = [ce.number(lab2.L), ce.number(lab2.a), ce.number(lab2.b)];
43020
+ if (lab2.alpha !== void 0) args2.push(ce.number(lab2.alpha));
43021
+ return ce.function("Oklab", args2);
43022
+ }
43023
+ const rgb = colorExprToRgb(arg);
43024
+ if (!rgb) return ce.error("incompatible-type");
43025
+ const lab = rgbToOklab(rgb);
43026
+ const args = [ce.number(lab.L), ce.number(lab.a), ce.number(lab.b)];
43027
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43028
+ return ce.function("Oklab", args);
43029
+ }
43030
+ },
43031
+ AsOklch: {
43032
+ description: "Convert any color to OKLCh",
43033
+ complexity: 8e3,
43034
+ signature: "(color) -> color",
43035
+ evaluate: (ops, { engine: ce }) => {
43036
+ const arg = ops[0];
43037
+ if (isFunction2(arg) && arg.operator === "Oklch")
43038
+ return normalizeColorHead(ce, arg);
43039
+ if (isFunction2(arg) && arg.operator === "Oklab") {
43040
+ const c2 = readColorExpr(arg);
43041
+ if (!c2) return ce.error("incompatible-type");
43042
+ const oklch2 = oklabToOklch({
43043
+ L: c2.c0,
43044
+ a: c2.c1,
43045
+ b: c2.c2,
43046
+ alpha: c2.alpha
43047
+ });
43048
+ const args2 = [
43049
+ ce.number(oklch2.L),
43050
+ ce.number(oklch2.C),
43051
+ ce.number(oklch2.H)
43052
+ ];
43053
+ if (oklch2.alpha !== void 0) args2.push(ce.number(oklch2.alpha));
43054
+ return ce.function("Oklch", args2);
42257
43055
  }
42258
- const result = contrastingColor(bgRgb);
42259
- return colorNumberToTuple(ce, result);
43056
+ const rgb = colorExprToRgb(arg);
43057
+ if (!rgb) return ce.error("incompatible-type");
43058
+ const c = rgbToOklch(rgb);
43059
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
43060
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43061
+ return ce.function("Oklch", args);
43062
+ }
43063
+ },
43064
+ // ---------------------------------------------------------------------------
43065
+ // Perceptual difference. Returns ΔE_OK (Euclidean distance in OKLab),
43066
+ // an approximately perceptually uniform scalar.
43067
+ // ---------------------------------------------------------------------------
43068
+ ColorDelta: {
43069
+ description: "Perceptual color difference (\u0394E_OK) between two colors",
43070
+ complexity: 8e3,
43071
+ signature: "(color | string | tuple, color | string | tuple) -> number",
43072
+ evaluate: (ops, { engine: ce }) => {
43073
+ const a = toOklch(ce, ops[0]);
43074
+ const b = toOklch(ce, ops[1]);
43075
+ if (!a || !b) return ce.error("incompatible-type");
43076
+ return ce.number(oklabDeltaE(oklchToOklab(a), oklchToOklab(b)));
42260
43077
  }
42261
43078
  }
42262
43079
  };
@@ -42315,12 +43132,42 @@ ${e.message}
42315
43132
  }
42316
43133
  },
42317
43134
  Loop: {
42318
- description: "Evaluate a body expression over elements of a collection.",
43135
+ description: "Evaluate a body expression in nested iteration over Element clauses. Later clauses see earlier bindings; independent clauses produce a Cartesian product.",
42319
43136
  lazy: true,
42320
- signature: "(body:expression, collection:expression) -> any",
42321
- type: ([body]) => body.type,
42322
- evaluate: ([body, collection], { engine: ce }) => run(runLoop(body, collection, ce), ce._timeRemaining),
42323
- evaluateAsync: async ([body, collection], { engine: ce, signal }) => runAsync(runLoop(body, collection, ce), ce._timeRemaining, signal)
43137
+ signature: "(body:expression, iterators:expression*) -> any",
43138
+ type: ([body]) => {
43139
+ if (!body) return "nothing";
43140
+ return parseType(`indexed_collection<${String(body.type)}>`);
43141
+ },
43142
+ canonical: canonicalLoop,
43143
+ evaluate: (ops, { engine: ce }) => run(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining),
43144
+ evaluateAsync: async (ops, { engine: ce, signal }) => runAsync(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining, signal)
43145
+ },
43146
+ When: {
43147
+ description: "Conditional value: returns expr when cond holds, undefined otherwise.",
43148
+ lazy: true,
43149
+ signature: "(expression, boolean) -> any",
43150
+ type: ([expr2]) => expr2.type,
43151
+ canonical: (args, { engine: ce }) => {
43152
+ if (args.length !== 2) return null;
43153
+ const [expr2, cond] = args;
43154
+ if (isFunction2(expr2, "When")) {
43155
+ const inner = expr2.op1.canonical;
43156
+ const innerCond = expr2.op2.canonical;
43157
+ return ce._fn("When", [
43158
+ inner,
43159
+ ce._fn("And", [innerCond, cond.canonical])
43160
+ ]);
43161
+ }
43162
+ return ce._fn("When", [expr2.canonical, cond.canonical]);
43163
+ },
43164
+ evaluate: ([expr2, cond], { engine: ce }) => {
43165
+ const c = cond.evaluate();
43166
+ const cs = sym(c);
43167
+ if (cs === "True") return expr2.evaluate();
43168
+ if (cs === "False") return ce.symbol("Undefined");
43169
+ return ce._fn("When", [expr2, c]);
43170
+ }
42324
43171
  },
42325
43172
  Which: {
42326
43173
  description: "Return the value for the first condition that is true.",
@@ -42380,9 +43227,141 @@ ${e.message}
42380
43227
  );
42381
43228
  return result;
42382
43229
  }
42383
- function* runLoop(body, collection, ce) {
43230
+ function canonicalLoop(ops, options) {
43231
+ const { engine: ce, scope } = options;
43232
+ if (ops.length === 0) return null;
43233
+ if (ops.length === 1) {
43234
+ return ce._fn("Loop", [ops[0].canonical]);
43235
+ }
43236
+ const body = ops[0];
43237
+ const iterators = ops.slice(1);
43238
+ const allElement = iterators.every((it) => it.operator === "Element");
43239
+ if (!allElement) {
43240
+ return ce._fn(
43241
+ "Loop",
43242
+ ops.map((op) => op.canonical)
43243
+ );
43244
+ }
43245
+ const loopScope = scope ?? {
43246
+ parent: ce.context.lexicalScope,
43247
+ bindings: /* @__PURE__ */ new Map()
43248
+ };
43249
+ loopScope.noAutoDeclare = true;
43250
+ ce.pushScope(loopScope);
43251
+ let canonicalIterators;
43252
+ let canonicalBody;
43253
+ try {
43254
+ canonicalIterators = iterators.map((it) => {
43255
+ if (!isFunction2(it, "Element")) {
43256
+ return ce._fn("Element", [
43257
+ ce.error("missing").canonical,
43258
+ ce.error("missing").canonical
43259
+ ]);
43260
+ }
43261
+ const indexExpr = it.ops[0];
43262
+ const collExpr = it.ops[1];
43263
+ if (!indexExpr || !collExpr) {
43264
+ return ce._fn("Element", [
43265
+ (indexExpr ?? ce.error("missing")).canonical,
43266
+ (collExpr ?? ce.error("missing")).canonical
43267
+ ]);
43268
+ }
43269
+ if (isSymbol2(indexExpr) && indexExpr.symbol !== "Nothing") {
43270
+ if (!ce.context.lexicalScope.bindings.has(indexExpr.symbol))
43271
+ ce.declare(indexExpr.symbol, "unknown");
43272
+ }
43273
+ return ce._fn("Element", [indexExpr.canonical, collExpr.canonical]);
43274
+ });
43275
+ canonicalBody = body.canonical;
43276
+ } finally {
43277
+ ce.popScope();
43278
+ loopScope.noAutoDeclare = false;
43279
+ }
43280
+ return ce._fn("Loop", [canonicalBody, ...canonicalIterators], {
43281
+ scope: loopScope
43282
+ });
43283
+ }
43284
+ function* runLoop(body, elements, ce) {
42384
43285
  body ??= ce.Nothing;
42385
43286
  if (sym(body) === "Nothing") return body;
43287
+ if (elements.length === 0) {
43288
+ const result = body.evaluate();
43289
+ yield result;
43290
+ return result;
43291
+ }
43292
+ if (elements.length === 1 && elements[0].operator !== "Element") {
43293
+ return yield* runLoopLegacy(body, elements[0], ce);
43294
+ }
43295
+ const results = [];
43296
+ const state = { stopped: false, broke: false, count: 0 };
43297
+ const freshScope = {
43298
+ parent: ce.context.lexicalScope,
43299
+ bindings: /* @__PURE__ */ new Map()
43300
+ };
43301
+ ce._pushEvalContext(freshScope);
43302
+ try {
43303
+ for (const elem of elements) {
43304
+ if (!isFunction2(elem, "Element")) continue;
43305
+ const idx = elem.ops[0];
43306
+ if (idx && isSymbol2(idx) && idx.symbol !== "Nothing") {
43307
+ if (!freshScope.bindings.has(idx.symbol))
43308
+ ce.declare(idx.symbol, "unknown");
43309
+ }
43310
+ }
43311
+ yield* runLoopNested(body, elements, 0, ce, results, state);
43312
+ } finally {
43313
+ ce._popEvalContext();
43314
+ }
43315
+ if (state.stopped && state.value !== void 0) {
43316
+ if (!state.broke) return state.value;
43317
+ return state.value;
43318
+ }
43319
+ return ce.function("List", results);
43320
+ }
43321
+ function* runLoopNested(body, elements, index, ce, results, state) {
43322
+ if (state.stopped) return;
43323
+ if (index === elements.length) {
43324
+ const result = body.evaluate();
43325
+ state.count += 1;
43326
+ if (state.count > ce.iterationLimit)
43327
+ throw new CancellationError({ cause: "iteration-limit-exceeded" });
43328
+ if (isFunction2(result, "Break")) {
43329
+ state.stopped = true;
43330
+ state.broke = true;
43331
+ state.value = result.op1;
43332
+ return;
43333
+ }
43334
+ if (result.operator === "Return") {
43335
+ state.stopped = true;
43336
+ state.value = result;
43337
+ return;
43338
+ }
43339
+ results.push(result);
43340
+ yield result;
43341
+ return;
43342
+ }
43343
+ const elem = elements[index];
43344
+ if (!isFunction2(elem, "Element")) {
43345
+ return;
43346
+ }
43347
+ const indexExpr = elem.ops[0];
43348
+ const collExpr = elem.ops[1];
43349
+ if (!indexExpr || !isSymbol2(indexExpr) || !collExpr) {
43350
+ return;
43351
+ }
43352
+ const name = indexExpr.symbol;
43353
+ const collection = collExpr.evaluate();
43354
+ if (!collection?.isCollection) {
43355
+ return;
43356
+ }
43357
+ const skipAssign = name === "Nothing";
43358
+ for (const value of collection.each()) {
43359
+ if (!skipAssign) ce.assign(name, value);
43360
+ yield* runLoopNested(body, elements, index + 1, ce, results, state);
43361
+ if (state.stopped) return;
43362
+ }
43363
+ }
43364
+ function* runLoopLegacy(body, collection, ce) {
42386
43365
  if (collection?.isCollection) {
42387
43366
  let result = void 0;
42388
43367
  const fn = applicable(body);
@@ -44736,6 +45715,34 @@ ${e.message}
44736
45715
  signature: "() -> expression",
44737
45716
  evaluate: (_ops, { engine }) => engine.expr(randomExpression())
44738
45717
  }
45718
+ },
45719
+ // ---------------------------------------------------------------------------
45720
+ // Opaque typed heads — registered so the names are in the standard set
45721
+ // (consumers can branch on the operator name); CE itself does not evaluate
45722
+ // them. Geometric primitives `Triangle`/`Sphere`/`Segment` and the action
45723
+ // arrow `To` (`a \to b`).
45724
+ // ---------------------------------------------------------------------------
45725
+ {
45726
+ Triangle: {
45727
+ description: "Triangle primitive \u2014 opaque typed head.",
45728
+ signature: "(any+) -> expression"
45729
+ },
45730
+ GeometricVector: {
45731
+ description: "Geometric vector (directed segment between two points) \u2014 opaque typed head. Distinct from the column-vector `Vector` operator.",
45732
+ signature: "(any, any) -> expression"
45733
+ },
45734
+ Sphere: {
45735
+ description: "Sphere primitive \u2014 opaque typed head.",
45736
+ signature: "(any+) -> expression"
45737
+ },
45738
+ Segment: {
45739
+ description: "Segment primitive \u2014 opaque typed head.",
45740
+ signature: "(any+) -> expression"
45741
+ },
45742
+ To: {
45743
+ description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45744
+ signature: "(any, any) -> nothing"
45745
+ }
44739
45746
  }
44740
45747
  ];
44741
45748
 
@@ -51872,6 +52879,7 @@ Error in definition of "${name}"`,
51872
52879
 
51873
52880
  // src/compute-engine/boxed-expression/cache.ts
51874
52881
  function cachedValue(v, generation, fn) {
52882
+ if (v.generation === generation && v.value !== null) return v.value;
51875
52883
  v.generation = generation;
51876
52884
  v.value = fn();
51877
52885
  return v.value;
@@ -53649,6 +54657,12 @@ Error in definition of "${name}"`,
53649
54657
  function _escapeJsonString(s) {
53650
54658
  return s;
53651
54659
  }
54660
+ function _serializeLatexMetadata(ce, expr2) {
54661
+ const syntax = ce.latexSyntax;
54662
+ const opts = ce.latexOptions;
54663
+ if (Object.keys(opts).length === 0) return syntax.serialize(expr2);
54664
+ return syntax.serialize(expr2, { ...opts });
54665
+ }
53652
54666
  function serializeSubtract(ce, a, b, options, metadata) {
53653
54667
  if (isNumber(a) && a.isNegative) {
53654
54668
  const v = a.numericValue;
@@ -53953,7 +54967,7 @@ Error in definition of "${name}"`,
53953
54967
  ];
53954
54968
  const md = { ...metadata ?? {} };
53955
54969
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53956
- md.latex = _escapeJsonString(md.latex ?? ce.latexSyntax.serialize(fn));
54970
+ md.latex = _escapeJsonString(md.latex ?? _serializeLatexMetadata(ce, fn));
53957
54971
  } else md.latex = "";
53958
54972
  if (!options.metadata.includes("wikidata")) md.wikidata = "";
53959
54973
  if (!md.latex && !md.wikidata && options.shorthands.includes("function"))
@@ -53978,7 +54992,7 @@ Error in definition of "${name}"`,
53978
54992
  }
53979
54993
  metadata = { ...metadata };
53980
54994
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53981
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(sym2);
54995
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, sym2);
53982
54996
  if (metadata.latex !== void 0)
53983
54997
  metadata.latex = _escapeJsonString(metadata.latex);
53984
54998
  } else metadata.latex = void 0;
@@ -54140,7 +55154,7 @@ Error in definition of "${name}"`,
54140
55154
  }
54141
55155
  }
54142
55156
  if (options.metadata.includes("latex") && ce.latexSyntax)
54143
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(result2 ?? { num });
55157
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, result2 ?? { num });
54144
55158
  if (result2) {
54145
55159
  if (metadata.latex !== void 0)
54146
55160
  return { sym: result2, latex: metadata.latex };
@@ -54156,7 +55170,7 @@ Error in definition of "${name}"`,
54156
55170
  if (value.isNaN()) {
54157
55171
  num = "NaN";
54158
55172
  if (options.metadata.includes("latex") && ce.latexSyntax)
54159
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
55173
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54160
55174
  return metadata.latex !== void 0 ? { num, latex: metadata.latex } : { num };
54161
55175
  }
54162
55176
  return serializeJsonFunction(
@@ -54190,7 +55204,7 @@ Error in definition of "${name}"`,
54190
55204
  value = Number(value);
54191
55205
  } else {
54192
55206
  if (options.metadata.includes("latex") && ce.latexSyntax)
54193
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({
55207
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, {
54194
55208
  num: value.toString()
54195
55209
  });
54196
55210
  if (metadata.latex !== void 0)
@@ -54204,7 +55218,7 @@ Error in definition of "${name}"`,
54204
55218
  result = value > 0 ? "PositiveInfinity" : "NegativeInfinity";
54205
55219
  else num = serializeRepeatingDecimals(value.toString(), options);
54206
55220
  if (options.metadata.includes("latex") && ce.latexSyntax)
54207
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
55221
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54208
55222
  if (result) {
54209
55223
  if (metadata.latex !== void 0)
54210
55224
  return { sym: result, latex: metadata.latex };
@@ -54430,6 +55444,23 @@ Error in definition of "${name}"`,
54430
55444
  };
54431
55445
  return compilePair(0);
54432
55446
  }
55447
+ if (h === "When") {
55448
+ if (args.length !== 2)
55449
+ throw new Error("When: expected exactly 2 arguments (expr, cond)");
55450
+ const fn2 = target.functions?.(h);
55451
+ if (fn2) {
55452
+ if (typeof fn2 === "function") {
55453
+ return fn2(args, (expr2) => _BaseCompiler.compile(expr2, target), target);
55454
+ }
55455
+ return `${fn2}(${args.map((x) => _BaseCompiler.compile(x, target)).join(", ")})`;
55456
+ }
55457
+ if (isSymbol2(args[1], "True"))
55458
+ return `(${_BaseCompiler.compile(args[0], target)})`;
55459
+ if (isSymbol2(args[1], "False")) return "NaN";
55460
+ const val = _BaseCompiler.compile(args[0], target);
55461
+ const cond = _BaseCompiler.compile(args[1], target);
55462
+ return `((${cond}) ? (${val}) : NaN)`;
55463
+ }
54433
55464
  if (h === "Block") {
54434
55465
  return _BaseCompiler.compileBlock(args, target);
54435
55466
  }
@@ -54504,17 +55535,91 @@ Error in definition of "${name}"`,
54504
55535
  )}${target.ws("\n")}})()`;
54505
55536
  }
54506
55537
  /**
54507
- * Compile a Loop expression with Element(index, Range(lo, hi)) indexing.
54508
- * Generates: (() => { for (let i = lo; i <= hi; i++) { body } })()
55538
+ * Compile a Loop expression.
55539
+ *
55540
+ * Two forms are supported:
55541
+ *
55542
+ * 1. **Imperative / single-Element form** (existing behaviour):
55543
+ * `Loop(body, Element(i, Range(lo, hi)))`
55544
+ * Generates a raw `for (let i = lo; i <= hi; i++) { body }` loop wrapped
55545
+ * in an IIFE. The loop counter is always a plain number. For targets
55546
+ * that wrap numeric values (e.g. interval-js uses `_IA.point()`),
55547
+ * references to the loop index inside the body are re-wrapped via
55548
+ * `target.number`. `break` / `continue` / `return` are preserved.
54509
55549
  *
54510
- * The loop counter is always a raw number. For targets that wrap numeric
54511
- * values (e.g. interval-js wraps with `_IA.point()`), references to the
54512
- * loop index inside the body are wrapped via `target.number`.
55550
+ * 2. **Comprehension / variadic-Element form** (new):
55551
+ * `Loop(body, Element(x, coll1), Element(y, coll2), )`
55552
+ * When two or more `Element` clauses are present or when the single
55553
+ * Element's collection is not a `Range` — the loop is compiled as a
55554
+ * comprehension that collects results into an array. Each clause
55555
+ * produces a `for (const name of collection)` loop, nested
55556
+ * outermost-to-innermost, and the innermost body pushes into `result`.
55557
+ *
55558
+ * Example output (JS):
55559
+ * ```js
55560
+ * (() => { const result = [];
55561
+ * for (const x of [1,2]) { for (const y of [3,4]) { result.push(body); } }
55562
+ * return result; })()
55563
+ * ```
55564
+ *
55565
+ * GLSL: multi-Element comprehension is not trivially representable in
55566
+ * GLSL (no dynamic arrays, no push). A compile-time error is thrown.
55567
+ * TODO(E3-GLSL): support GLSL multi-Element via a pre-declared fixed-size
55568
+ * array or by unrolling when bounds are known at compile time.
54513
55569
  */
54514
55570
  static compileForLoop(args, target) {
54515
55571
  if (!args[0]) throw new Error("Loop: no body");
54516
55572
  if (!args[1]) throw new Error("Loop: no indexing set");
54517
- const indexing = args[1];
55573
+ const body = args[0];
55574
+ const elements = args.slice(1);
55575
+ const useComprehension = elements.length > 1 || elements.length === 1 && isFunction2(elements[0], "Element") && !_BaseCompiler.isLegacyCompatibleRange(elements[0].ops[1]);
55576
+ if (useComprehension) {
55577
+ const lang = target.language ?? "";
55578
+ if (lang === "glsl" || lang === "wgsl") {
55579
+ throw new Error(
55580
+ `${lang.toUpperCase()}: multi-Element Loop comprehension is not yet supported. TODO(E3-GLSL): unroll or use a fixed-size array.`
55581
+ );
55582
+ }
55583
+ const narrowedElements = [];
55584
+ for (let i = 0; i < elements.length; i++) {
55585
+ const elem = elements[i];
55586
+ if (!isFunction2(elem, "Element"))
55587
+ throw new Error(
55588
+ `Loop: argument ${i + 1} must be an Element clause, got ${elem.operator ?? "?"}`
55589
+ );
55590
+ if (!isSymbol2(elem.ops[0]))
55591
+ throw new Error(
55592
+ `Loop: Element index (argument ${i + 1}) must be a symbol`
55593
+ );
55594
+ narrowedElements.push(elem);
55595
+ }
55596
+ const loopVarSet = new Set(
55597
+ narrowedElements.map(
55598
+ (e) => e.ops[0].symbol
55599
+ )
55600
+ );
55601
+ const needsWrap2 = target.number(0) !== "0";
55602
+ const bodyTarget2 = needsWrap2 ? {
55603
+ ...target,
55604
+ var: (id) => loopVarSet.has(id) ? target.number(0).replace("0", id) : target.var(id)
55605
+ } : target;
55606
+ const bodyCode = _BaseCompiler.compile(body, bodyTarget2);
55607
+ let inner = `result.push(${bodyCode});`;
55608
+ for (let i = narrowedElements.length - 1; i >= 0; i--) {
55609
+ const elem = narrowedElements[i];
55610
+ const name = elem.ops[0].symbol;
55611
+ const collExpr = elem.ops[1];
55612
+ let collection;
55613
+ if (isFunction2(collExpr, "Range")) {
55614
+ collection = _BaseCompiler.compileRangeIterable(collExpr, bodyTarget2);
55615
+ } else {
55616
+ collection = _BaseCompiler.compile(collExpr, bodyTarget2);
55617
+ }
55618
+ inner = `for (const ${name} of ${collection}) { ${inner} }`;
55619
+ }
55620
+ return `(() => { const result = []; ${inner} return result; })()`;
55621
+ }
55622
+ const indexing = elements[0];
54518
55623
  if (!isFunction2(indexing, "Element"))
54519
55624
  throw new Error("Loop: expected Element(index, Range(lo, hi))");
54520
55625
  const indexExpr = indexing.ops[0];
@@ -54532,13 +55637,72 @@ Error in definition of "${name}"`,
54532
55637
  ...target,
54533
55638
  var: (id) => id === index ? needsWrap ? target.number(0).replace("0", index) : index : target.var(id)
54534
55639
  };
54535
- const bodyStmts = _BaseCompiler.compileLoopBody(args[0], bodyTarget);
55640
+ const bodyStmts = _BaseCompiler.compileLoopBody(body, bodyTarget);
54536
55641
  return `(() => {${target.ws(
54537
55642
  "\n"
54538
55643
  )}for (let ${index} = ${lower}; ${index} <= ${upper}; ${index}++) {${target.ws(
54539
55644
  "\n"
54540
55645
  )}${bodyStmts}${target.ws("\n")}}${target.ws("\n")}})()`;
54541
55646
  }
55647
+ /**
55648
+ * Returns `true` when the given collection expression is a `Range` whose
55649
+ * runtime semantics match the legacy imperative for-loop shape
55650
+ * `for (let i = lo; i <= hi; i++)`.
55651
+ *
55652
+ * Concretely: integer-ascending bounds and step omitted-or-1. When bounds
55653
+ * are not statically numeric we accept the Range (the historical
55654
+ * behaviour) — runtime mismatch in the descending-unknown-bounds case is
55655
+ * left as a known limitation; callers can force the iterable path by
55656
+ * supplying an explicit step.
55657
+ */
55658
+ static isLegacyCompatibleRange(coll) {
55659
+ if (!isFunction2(coll, "Range")) return false;
55660
+ if (coll.ops.length >= 3) {
55661
+ const stepExpr = coll.ops[2];
55662
+ if (!isNumber(stepExpr) || stepExpr.re !== 1) return false;
55663
+ }
55664
+ const lo = coll.ops[0];
55665
+ const hi = coll.ops[1];
55666
+ if (isNumber(lo) && !Number.isInteger(lo.re)) return false;
55667
+ if (isNumber(hi) && !Number.isInteger(hi.re)) return false;
55668
+ if (isNumber(lo) && isNumber(hi) && lo.re > hi.re) return false;
55669
+ return true;
55670
+ }
55671
+ /**
55672
+ * Compile a `Range(lo, hi)` or `Range(lo, hi, step)` expression into a JS
55673
+ * iterable expression. Mirrors the runtime semantics in
55674
+ * `library/collections.ts` Range:
55675
+ * count = step === 0 ? 0 : max(0, floor((hi - lo) / step) + 1)
55676
+ * element = lo + step * k (0-indexed)
55677
+ * Default step is 1 when omitted. Bounds and step may be fractional.
55678
+ *
55679
+ * Only used from the comprehension path in `compileForLoop`.
55680
+ * Caller must have already verified `isFunction(rangeExpr, 'Range')`.
55681
+ */
55682
+ static compileRangeIterable(rangeExpr, target) {
55683
+ const loExpr = rangeExpr.ops[0];
55684
+ const hiExpr = rangeExpr.ops[1];
55685
+ const stepExpr = rangeExpr.ops[2];
55686
+ if (isNumber(loExpr) && isNumber(hiExpr) && (stepExpr === void 0 || isNumber(stepExpr))) {
55687
+ const lo2 = loExpr.re;
55688
+ const hi2 = hiExpr.re;
55689
+ const step2 = stepExpr === void 0 ? hi2 >= lo2 ? 1 : -1 : stepExpr.re;
55690
+ if (step2 === 0) return "[]";
55691
+ const len = Math.max(0, Math.floor((hi2 - lo2) / step2) + 1);
55692
+ if (step2 === 1) {
55693
+ if (lo2 === 0) return `Array.from({length:${len}},(_,k)=>k)`;
55694
+ return `Array.from({length:${len}},(_,k)=>${lo2}+k)`;
55695
+ }
55696
+ return `Array.from({length:${len}},(_,k)=>${lo2}+(${step2})*k)`;
55697
+ }
55698
+ const lo = _BaseCompiler.compile(loExpr, target);
55699
+ const hi = _BaseCompiler.compile(hiExpr, target);
55700
+ if (stepExpr === void 0) {
55701
+ return `((_lo,_hi)=>{const _st=_hi>=_lo?1:-1;return Array.from({length:Math.max(0,Math.floor((_hi-_lo)/_st)+1)},(_,k)=>_lo+_st*k);})(${lo},${hi})`;
55702
+ }
55703
+ const step = _BaseCompiler.compile(stepExpr, target);
55704
+ return `((_lo,_hi,_st)=>_st===0?[]:Array.from({length:Math.max(0,Math.floor((_hi-_lo)/_st)+1)},(_,k)=>_lo+_st*k))(${lo},${hi},${step})`;
55705
+ }
54542
55706
  /**
54543
55707
  * Compile a loop body expression as statements (not wrapped in IIFE).
54544
55708
  * Handles Break, Continue, Return as statements, and If as if-else when
@@ -54969,8 +56133,7 @@ Error in definition of "${name}"`,
54969
56133
  ce.pushScope();
54970
56134
  try {
54971
56135
  if (vars && typeof vars === "object") {
54972
- for (const [k, v] of Object.entries(vars))
54973
- ce.assign(k, v);
56136
+ for (const [k, v] of Object.entries(vars)) ce.assign(k, v);
54974
56137
  }
54975
56138
  return expr2.evaluate().re;
54976
56139
  } finally {
@@ -60200,8 +61363,7 @@ Error in definition of "${name}"`,
60200
61363
  return { re: null, im: formatFloat(iScale) };
60201
61364
  }
60202
61365
  const compiledFactors = remaining.map((r) => compile3(r));
60203
- if (iScale !== 1)
60204
- compiledFactors.unshift(formatFloat(iScale));
61366
+ if (iScale !== 1) compiledFactors.unshift(formatFloat(iScale));
60205
61367
  const imCode = foldTerms(compiledFactors, "1.0", "*");
60206
61368
  return { re: null, im: imCode };
60207
61369
  }
@@ -60805,39 +61967,130 @@ Error in definition of "${name}"`,
60805
61967
  if (args.length >= 2)
60806
61968
  return `_SYS.colormap(${compile3(args[0])}, ${compile3(args[1])})`;
60807
61969
  return `_SYS.colormap(${compile3(args[0])})`;
61970
+ },
61971
+ // -----------------------------------------------------------------------
61972
+ // Color constructor heads. All compile to OKLCh arrays at runtime — the
61973
+ // canonical color representation in this target. The constructors take
61974
+ // their own colorspace's components and convert internally.
61975
+ // (Mirrors the GPU target's design: color values are vec3 OKLCh.)
61976
+ // -----------------------------------------------------------------------
61977
+ Rgb: (args, compile3) => {
61978
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
61979
+ return `_SYS.rgb(${args.map(compile3).join(", ")})`;
61980
+ },
61981
+ Hsv: (args, compile3) => {
61982
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
61983
+ return `_SYS.hsv(${args.map(compile3).join(", ")})`;
61984
+ },
61985
+ Hsl: (args, compile3) => {
61986
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
61987
+ return `_SYS.hsl(${args.map(compile3).join(", ")})`;
61988
+ },
61989
+ Oklab: (args, compile3) => {
61990
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
61991
+ return `_SYS.oklab(${args.map(compile3).join(", ")})`;
61992
+ },
61993
+ Oklch: (args, compile3) => {
61994
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
61995
+ return `_SYS.oklch(${args.map(compile3).join(", ")})`;
61996
+ },
61997
+ // -----------------------------------------------------------------------
61998
+ // As* converters. Compile-time output convention matches the engine and
61999
+ // the GPU target: each returns components in the named space as a 3- or
62000
+ // 4-element array. `AsRgb` uses 0-1 sRGB channels (consistent across all
62001
+ // layers). `AsOklch` is the identity (canonical form).
62002
+ // -----------------------------------------------------------------------
62003
+ AsRgb: ([c], compile3) => {
62004
+ if (c === null) throw new Error("AsRgb: no argument");
62005
+ return `_SYS.asRgb(${compile3(c)})`;
62006
+ },
62007
+ AsHsv: ([c], compile3) => {
62008
+ if (c === null) throw new Error("AsHsv: no argument");
62009
+ return `_SYS.asHsv(${compile3(c)})`;
62010
+ },
62011
+ AsHsl: ([c], compile3) => {
62012
+ if (c === null) throw new Error("AsHsl: no argument");
62013
+ return `_SYS.asHsl(${compile3(c)})`;
62014
+ },
62015
+ AsOklab: ([c], compile3) => {
62016
+ if (c === null) throw new Error("AsOklab: no argument");
62017
+ return `_SYS.asOklab(${compile3(c)})`;
62018
+ },
62019
+ AsOklch: ([c], compile3) => {
62020
+ if (c === null) throw new Error("AsOklch: no argument");
62021
+ return compile3(c);
62022
+ },
62023
+ // Perceptual color difference (ΔE_OK).
62024
+ ColorDelta: ([a, b], compile3) => {
62025
+ if (a === null || b === null)
62026
+ throw new Error("ColorDelta: need two colors");
62027
+ return `_SYS.colorDelta(${compile3(a)}, ${compile3(b)})`;
62028
+ },
62029
+ // Euclidean distance between two tuples (any positive dimension).
62030
+ // The GPU target maps `Distance` to the GLSL/WGSL `distance()` builtin
62031
+ // (vec-only); this JS handler works on plain arrays of any length.
62032
+ Distance: ([a, b], compile3) => {
62033
+ if (a === null || b === null) throw new Error("Distance: need two points");
62034
+ return `_SYS.distance(${compile3(a)}, ${compile3(b)})`;
60808
62035
  }
60809
62036
  };
60810
62037
  function toRI(c) {
60811
62038
  return { re: c.re, im: c.im };
60812
62039
  }
62040
+ function normalizeAlpha2(a) {
62041
+ if (a === void 0) return void 0;
62042
+ if (!Number.isFinite(a)) return void 0;
62043
+ if (Math.abs(a - 1) < 1e-9) return void 0;
62044
+ return a;
62045
+ }
60813
62046
  function toRgb255(input) {
60814
62047
  if (typeof input === "string") {
60815
62048
  const c = parseColor(input);
60816
- return {
62049
+ const rgb2 = {
60817
62050
  r: c >>> 24 & 255,
60818
62051
  g: c >>> 16 & 255,
60819
- b: c >>> 8 & 255,
60820
- alpha: (c & 255) / 255
62052
+ b: c >>> 8 & 255
60821
62053
  };
62054
+ const alpha = normalizeAlpha2((c & 255) / 255);
62055
+ if (alpha !== void 0) rgb2.alpha = alpha;
62056
+ return rgb2;
62057
+ }
62058
+ const rgb = oklchToRgb({ L: input[0], C: input[1], H: input[2] });
62059
+ if (input.length >= 4) {
62060
+ const alpha = normalizeAlpha2(input[3]);
62061
+ if (alpha !== void 0) rgb.alpha = alpha;
60822
62062
  }
60823
- const rgb = {
60824
- r: input[0] * 255,
60825
- g: input[1] * 255,
60826
- b: input[2] * 255
60827
- };
60828
- if (input.length >= 4) rgb.alpha = input[3];
60829
62063
  return rgb;
60830
62064
  }
60831
- function packedToArray(c) {
60832
- const r = (c >>> 24 & 255) / 255;
60833
- const g = (c >>> 16 & 255) / 255;
60834
- const b = (c >>> 8 & 255) / 255;
60835
- const a = (c & 255) / 255;
60836
- return Math.abs(a - 1) < 1e-4 ? [r, g, b] : [r, g, b, a];
62065
+ function toOklch2(input) {
62066
+ if (typeof input === "string") {
62067
+ const c = parseColor(input);
62068
+ const r = c >>> 24 & 255;
62069
+ const g = c >>> 16 & 255;
62070
+ const b = c >>> 8 & 255;
62071
+ const oklch2 = rgbToOklch({ r, g, b });
62072
+ const alpha = normalizeAlpha2((c & 255) / 255);
62073
+ if (alpha !== void 0) oklch2.alpha = alpha;
62074
+ return oklch2;
62075
+ }
62076
+ return {
62077
+ L: input[0],
62078
+ C: input[1],
62079
+ H: input[2],
62080
+ alpha: input.length >= 4 ? normalizeAlpha2(input[3]) : void 0
62081
+ };
62082
+ }
62083
+ function packedToOklch(c) {
62084
+ const r = c >>> 24 & 255;
62085
+ const g = c >>> 16 & 255;
62086
+ const b = c >>> 8 & 255;
62087
+ const oklch2 = rgbToOklch({ r, g, b });
62088
+ const alpha = normalizeAlpha2((c & 255) / 255);
62089
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
60837
62090
  }
60838
62091
  var colorHelpers = {
60839
62092
  color(input) {
60840
- return packedToArray(parseColor(input));
62093
+ return packedToOklch(parseColor(input));
60841
62094
  },
60842
62095
  colorToString(input, format) {
60843
62096
  const rgb = toRgb255(input);
@@ -60848,7 +62101,7 @@ Error in definition of "${name}"`,
60848
62101
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
60849
62102
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
60850
62103
  let hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
60851
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
62104
+ if (rgb.alpha !== void 0) {
60852
62105
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
60853
62106
  hex += a.toString(16).padStart(2, "0");
60854
62107
  }
@@ -60858,7 +62111,7 @@ Error in definition of "${name}"`,
60858
62111
  const r = Math.round(rgb.r);
60859
62112
  const g = Math.round(rgb.g);
60860
62113
  const b = Math.round(rgb.b);
60861
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62114
+ if (rgb.alpha !== void 0)
60862
62115
  return `rgb(${r} ${g} ${b} / ${rgb.alpha})`;
60863
62116
  return `rgb(${r} ${g} ${b})`;
60864
62117
  }
@@ -60867,7 +62120,7 @@ Error in definition of "${name}"`,
60867
62120
  const h = Math.round(hsl.h * 10) / 10;
60868
62121
  const s = Math.round(hsl.s * 1e3) / 10;
60869
62122
  const l = Math.round(hsl.l * 1e3) / 10;
60870
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62123
+ if (rgb.alpha !== void 0)
60871
62124
  return `hsl(${h} ${s}% ${l}% / ${rgb.alpha})`;
60872
62125
  return `hsl(${h} ${s}% ${l}%)`;
60873
62126
  }
@@ -60876,7 +62129,7 @@ Error in definition of "${name}"`,
60876
62129
  const L = Math.round(c.L * 1e3) / 1e3;
60877
62130
  const C = Math.round(c.C * 1e3) / 1e3;
60878
62131
  const H = Math.round(c.H * 10) / 10;
60879
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62132
+ if (rgb.alpha !== void 0)
60880
62133
  return `oklch(${L} ${C} ${H} / ${rgb.alpha})`;
60881
62134
  return `oklch(${L} ${C} ${H})`;
60882
62135
  }
@@ -60885,29 +62138,29 @@ Error in definition of "${name}"`,
60885
62138
  }
60886
62139
  },
60887
62140
  colorMix(input1, input2, ratio = 0.5) {
60888
- const rgb1 = toRgb255(input1);
60889
- const rgb2 = toRgb255(input2);
62141
+ const c1 = toOklch2(input1);
62142
+ const c2 = toOklch2(input2);
60890
62143
  ratio = Math.max(0, Math.min(1, ratio));
60891
- const c1 = rgbToOklch(rgb1);
60892
- const c2 = rgbToOklch(rgb2);
60893
- let dh = c2.H - c1.H;
60894
- if (dh > 180) dh -= 360;
60895
- if (dh < -180) dh += 360;
60896
- let H = c1.H + dh * ratio;
60897
- if (H < 0) H += 360;
60898
- if (H >= 360) H -= 360;
60899
- const mixed = oklchToRgb({
60900
- L: c1.L + (c2.L - c1.L) * ratio,
60901
- C: c1.C + (c2.C - c1.C) * ratio,
60902
- H
60903
- });
60904
- const r = mixed.r / 255;
60905
- const g = mixed.g / 255;
60906
- const b = mixed.b / 255;
60907
- const a1 = rgb1.alpha ?? 1;
60908
- const a2 = rgb2.alpha ?? 1;
60909
- const alpha = a1 + (a2 - a1) * ratio;
60910
- return Math.abs(alpha - 1) > 1e-4 ? [r, g, b, alpha] : [r, g, b];
62144
+ const c1Achromatic = c1.C < 1e-6;
62145
+ const c2Achromatic = c2.C < 1e-6;
62146
+ let H;
62147
+ if (c1Achromatic && c2Achromatic) H = c1.H;
62148
+ else if (c1Achromatic) H = c2.H;
62149
+ else if (c2Achromatic) H = c1.H;
62150
+ else {
62151
+ let dh = c2.H - c1.H;
62152
+ if (dh > 180) dh -= 360;
62153
+ if (dh < -180) dh += 360;
62154
+ H = c1.H + dh * ratio;
62155
+ if (H < 0) H += 360;
62156
+ if (H >= 360) H -= 360;
62157
+ }
62158
+ const L = c1.L + (c2.L - c1.L) * ratio;
62159
+ const C = c1.C + (c2.C - c1.C) * ratio;
62160
+ const a1 = c1.alpha ?? 1;
62161
+ const a2 = c2.alpha ?? 1;
62162
+ const alpha = normalizeAlpha2(a1 + (a2 - a1) * ratio);
62163
+ return alpha !== void 0 ? [L, C, H, alpha] : [L, C, H];
60911
62164
  },
60912
62165
  colorContrast(bg, fg) {
60913
62166
  return apca(toRgb255(bg), toRgb255(fg));
@@ -60915,11 +62168,11 @@ Error in definition of "${name}"`,
60915
62168
  contrastingColor(bg, fg1, fg2) {
60916
62169
  const bgRgb = toRgb255(bg);
60917
62170
  if (fg1 !== void 0 && fg2 !== void 0) {
60918
- return packedToArray(
62171
+ return packedToOklch(
60919
62172
  contrastingColor({ bg: bgRgb, fg1: toRgb255(fg1), fg2: toRgb255(fg2) })
60920
62173
  );
60921
62174
  }
60922
- return packedToArray(contrastingColor(bgRgb));
62175
+ return packedToOklch(contrastingColor(bgRgb));
60923
62176
  },
60924
62177
  colorToColorspace(input, space) {
60925
62178
  const rgb = toRgb255(input);
@@ -60948,7 +62201,7 @@ Error in definition of "${name}"`,
60948
62201
  default:
60949
62202
  throw new Error(`Unknown color space: ${space}`);
60950
62203
  }
60951
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
62204
+ if (alpha !== void 0) result.push(alpha);
60952
62205
  return result;
60953
62206
  },
60954
62207
  colormap(name, arg) {
@@ -60960,7 +62213,7 @@ Error in definition of "${name}"`,
60960
62213
  const palette = allPalettes[name];
60961
62214
  if (!palette) throw new Error(`Unknown palette: ${name}`);
60962
62215
  const colors = palette.map(
60963
- (hex) => parseColorToRgb01(hex)
62216
+ (hex) => packedToOklch(parseColor(hex))
60964
62217
  );
60965
62218
  if (arg === void 0) return colors;
60966
62219
  if (Number.isInteger(arg) && arg >= 2) {
@@ -60984,62 +62237,128 @@ Error in definition of "${name}"`,
60984
62237
  const frac = pos - i;
60985
62238
  if (frac === 0 || i >= colors.length - 1)
60986
62239
  return [...colors[Math.min(i, colors.length - 1)]];
60987
- const rgb1 = {
60988
- r: colors[i][0] * 255,
60989
- g: colors[i][1] * 255,
60990
- b: colors[i][2] * 255
60991
- };
60992
- const rgb2 = {
60993
- r: colors[i + 1][0] * 255,
60994
- g: colors[i + 1][1] * 255,
60995
- b: colors[i + 1][2] * 255
60996
- };
60997
- const c1 = rgbToOklch(rgb1);
60998
- const c2 = rgbToOklch(rgb2);
60999
- let dh = c2.H - c1.H;
61000
- if (dh > 180) dh -= 360;
61001
- if (dh < -180) dh += 360;
61002
- let H = c1.H + dh * frac;
61003
- if (H < 0) H += 360;
61004
- if (H >= 360) H -= 360;
61005
- const mixed = oklchToRgb({
61006
- L: c1.L + (c2.L - c1.L) * frac,
61007
- C: c1.C + (c2.C - c1.C) * frac,
61008
- H
61009
- });
61010
- return [mixed.r / 255, mixed.g / 255, mixed.b / 255];
62240
+ const [L1, C1, H1] = colors[i];
62241
+ const [L2, C2, H2] = colors[i + 1];
62242
+ const c1Achromatic = C1 < 1e-6;
62243
+ const c2Achromatic = C2 < 1e-6;
62244
+ let H;
62245
+ if (c1Achromatic && c2Achromatic) H = H1;
62246
+ else if (c1Achromatic) H = H2;
62247
+ else if (c2Achromatic) H = H1;
62248
+ else {
62249
+ let dh = H2 - H1;
62250
+ if (dh > 180) dh -= 360;
62251
+ if (dh < -180) dh += 360;
62252
+ H = H1 + dh * frac;
62253
+ if (H < 0) H += 360;
62254
+ if (H >= 360) H -= 360;
62255
+ }
62256
+ return [L1 + (L2 - L1) * frac, C1 + (C2 - C1) * frac, H];
61011
62257
  },
61012
62258
  colorFromColorspace(components, space) {
61013
62259
  const c0 = components[0];
61014
62260
  const c1 = components[1];
61015
62261
  const c2 = components[2];
61016
62262
  const alpha = components.length >= 4 ? components[3] : void 0;
61017
- let result;
62263
+ let oklch2;
61018
62264
  switch (space.toLowerCase()) {
61019
62265
  case "rgb":
61020
- result = [c0, c1, c2];
62266
+ oklch2 = rgbToOklch({ r: c0 * 255, g: c1 * 255, b: c2 * 255 });
61021
62267
  break;
61022
62268
  case "hsl": {
61023
- const r = hslToRgb(c0, c1, c2);
61024
- result = [r.r / 255, r.g / 255, r.b / 255];
62269
+ const rgb = hslToRgb(c0, c1, c2);
62270
+ oklch2 = rgbToOklch(rgb);
61025
62271
  break;
61026
62272
  }
61027
- case "oklch": {
61028
- const r = oklchToRgb({ L: c0, C: c1, H: c2 });
61029
- result = [r.r / 255, r.g / 255, r.b / 255];
62273
+ case "oklch":
62274
+ oklch2 = { L: c0, C: c1, H: c2 };
61030
62275
  break;
61031
- }
61032
62276
  case "oklab":
61033
- case "lab": {
61034
- const r = oklabToRgb({ L: c0, a: c1, b: c2 });
61035
- result = [r.r / 255, r.g / 255, r.b / 255];
62277
+ case "lab":
62278
+ oklch2 = oklabToOklch({ L: c0, a: c1, b: c2 });
61036
62279
  break;
61037
- }
61038
62280
  default:
61039
62281
  throw new Error(`Unknown color space: ${space}`);
61040
62282
  }
61041
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
61042
- return result;
62283
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
62284
+ },
62285
+ // -----------------------------------------------------------------------
62286
+ // Color constructors. Each accepts components in its colorspace's natural
62287
+ // units and returns the canonical OKLCh array `[L, C, H]` (or with alpha).
62288
+ // -----------------------------------------------------------------------
62289
+ rgb(r, g, b, alpha) {
62290
+ const c = rgbToOklch({ r: r * 255, g: g * 255, b: b * 255 });
62291
+ const a = normalizeAlpha2(alpha);
62292
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62293
+ },
62294
+ hsv(h, s, v, alpha) {
62295
+ const rgb = hsvToRgb(h, s, v);
62296
+ const c = rgbToOklch(rgb);
62297
+ const a = normalizeAlpha2(alpha);
62298
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62299
+ },
62300
+ hsl(h, s, l, alpha) {
62301
+ const rgb = hslToRgb(h, s, l);
62302
+ const c = rgbToOklch({ r: rgb.r, g: rgb.g, b: rgb.b });
62303
+ const a = normalizeAlpha2(alpha);
62304
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62305
+ },
62306
+ oklab(L, a, b, alpha) {
62307
+ const c = oklabToOklch({ L, a, b });
62308
+ const al = normalizeAlpha2(alpha);
62309
+ return al !== void 0 ? [c.L, c.C, c.H, al] : [c.L, c.C, c.H];
62310
+ },
62311
+ oklch(L, C, H, alpha) {
62312
+ const a = normalizeAlpha2(alpha);
62313
+ return a !== void 0 ? [L, C, H, a] : [L, C, H];
62314
+ },
62315
+ // -----------------------------------------------------------------------
62316
+ // As* converters. Inputs are anything `toOklch` accepts (string, packed
62317
+ // int, or OKLCh array). Outputs are 3- or 4-element arrays in the named
62318
+ // space. sRGB-based outputs (asRgb/asHsv/asHsl) use 0-1 channels for
62319
+ // consistency with the GPU target's shader convention.
62320
+ // -----------------------------------------------------------------------
62321
+ asRgb(input) {
62322
+ const rgb = toRgb255(input);
62323
+ const r = rgb.r / 255;
62324
+ const g = rgb.g / 255;
62325
+ const b = rgb.b / 255;
62326
+ return rgb.alpha !== void 0 ? [r, g, b, rgb.alpha] : [r, g, b];
62327
+ },
62328
+ asHsv(input) {
62329
+ const rgb = toRgb255(input);
62330
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
62331
+ return rgb.alpha !== void 0 ? [hsv.h, hsv.s, hsv.v, rgb.alpha] : [hsv.h, hsv.s, hsv.v];
62332
+ },
62333
+ asHsl(input) {
62334
+ const rgb = toRgb255(input);
62335
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
62336
+ return rgb.alpha !== void 0 ? [hsl.h, hsl.s, hsl.l, rgb.alpha] : [hsl.h, hsl.s, hsl.l];
62337
+ },
62338
+ asOklab(input) {
62339
+ const c = toOklch2(input);
62340
+ const lab = oklchToOklab({ L: c.L, C: c.C, H: c.H });
62341
+ return c.alpha !== void 0 ? [lab.L, lab.a, lab.b, c.alpha] : [lab.L, lab.a, lab.b];
62342
+ },
62343
+ // asOklch is identity — handled at compile time as a pass-through
62344
+ // Perceptual color difference (ΔE_OK).
62345
+ colorDelta(a, b) {
62346
+ const labA = oklchToOklab(toOklch2(a));
62347
+ const labB = oklchToOklab(toOklch2(b));
62348
+ return oklabDeltaE(labA, labB);
62349
+ },
62350
+ // Euclidean distance between two tuples. Plain numeric — not a color
62351
+ // operation despite living in the same helpers block.
62352
+ distance(a, b) {
62353
+ if (!Array.isArray(a) || !Array.isArray(b))
62354
+ throw new Error("Distance: expected two arrays");
62355
+ if (a.length !== b.length) throw new Error("Distance: dimension mismatch");
62356
+ let sumSq = 0;
62357
+ for (let i = 0; i < a.length; i++) {
62358
+ const d = a[i] - b[i];
62359
+ sumSq += d * d;
62360
+ }
62361
+ return Math.sqrt(sumSq);
61043
62362
  }
61044
62363
  };
61045
62364
  var SYS_HELPERS = {
@@ -61478,6 +62797,13 @@ Error in definition of "${name}"`,
61478
62797
  function gpuVec2(target) {
61479
62798
  return target?.language === "wgsl" ? "vec2f" : "vec2";
61480
62799
  }
62800
+ function gpuVec3(target) {
62801
+ return target?.language === "wgsl" ? "vec3f" : "vec3";
62802
+ }
62803
+ function readStringLiteral(expr2) {
62804
+ if (!isString(expr2)) return null;
62805
+ return expr2.string?.toLowerCase() ?? null;
62806
+ }
61481
62807
  function compileIntArg(expr2, compile3, target) {
61482
62808
  const c = tryGetConstant(expr2);
61483
62809
  if (c !== void 0 && Number.isInteger(c)) return c.toString();
@@ -61536,7 +62862,7 @@ Error in definition of "${name}"`,
61536
62862
  `for (${indexDecl} = ${lowerStr}; ${index} <= ${upperStr}; ${index}++) {`,
61537
62863
  ` ${acc} ${op}= ${body};`,
61538
62864
  `}`,
61539
- `return ${acc}`
62865
+ `return ${acc};`
61540
62866
  ];
61541
62867
  return lines.join("\n");
61542
62868
  }
@@ -61590,8 +62916,7 @@ Error in definition of "${name}"`,
61590
62916
  const iScale = isSymbol2(iFactor, "ImaginaryUnit") ? 1 : iFactor.im;
61591
62917
  const realFactors = args.filter((_, i) => i !== iIndex);
61592
62918
  const v2 = gpuVec2(target);
61593
- if (realFactors.length === 0)
61594
- return `${v2}(0.0, ${formatFloat(iScale)})`;
62919
+ if (realFactors.length === 0) return `${v2}(0.0, ${formatFloat(iScale)})`;
61595
62920
  const factors = realFactors.map((f) => compile3(f));
61596
62921
  if (iScale !== 1) factors.unshift(formatFloat(iScale));
61597
62922
  const imCode = foldTerms(factors, "1.0", "*");
@@ -61644,8 +62969,7 @@ Error in definition of "${name}"`,
61644
62969
  if (isNumber(x) && x.im !== 0) {
61645
62970
  return `${gpuVec2(target)}(${formatFloat(-x.re)}, ${formatFloat(-x.im)})`;
61646
62971
  }
61647
- if (isSymbol2(x, "ImaginaryUnit"))
61648
- return `${gpuVec2(target)}(0.0, -1.0)`;
62972
+ if (isSymbol2(x, "ImaginaryUnit")) return `${gpuVec2(target)}(0.0, -1.0)`;
61649
62973
  return `(-${compile3(x)})`;
61650
62974
  },
61651
62975
  // Standard math functions with complex dispatch
@@ -62018,17 +63342,127 @@ Error in definition of "${name}"`,
62018
63342
  }
62019
63343
  const isWGSL = target?.language === "wgsl";
62020
63344
  const v3 = isWGSL ? "vec3f" : "vec3";
62021
- return `((_gpu_apca(${bg}, ${v3}(0.0)) > 50.0) ? ${v3}(0.0) : ${v3}(1.0))`;
63345
+ const black = `${v3}(0.0)`;
63346
+ const white = `${v3}(1.0, 0.0, 0.0)`;
63347
+ return `((_gpu_apca(${bg}, ${black}) > 50.0) ? ${black} : ${white})`;
62022
63348
  },
62023
63349
  ColorToColorspace: ([color, space], compile3) => {
62024
63350
  if (color === null || space === null)
62025
63351
  throw new Error("ColorToColorspace: need color and space");
62026
- return `_gpu_srgb_to_oklab(${compile3(color)})`;
63352
+ const spaceName = readStringLiteral(space);
63353
+ if (spaceName === null)
63354
+ throw new Error("ColorToColorspace: space must be a string literal");
63355
+ const c = compile3(color);
63356
+ switch (spaceName) {
63357
+ case "oklch":
63358
+ return c;
63359
+ case "oklab":
63360
+ case "lab":
63361
+ return `_gpu_oklch_to_oklab(${c})`;
63362
+ case "rgb":
63363
+ return `_gpu_oklch_to_srgb(${c})`;
63364
+ case "hsl":
63365
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${c}))`;
63366
+ case "hsv":
63367
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${c}))`;
63368
+ default:
63369
+ throw new Error(
63370
+ `ColorToColorspace: unsupported space "${spaceName}" on GPU target`
63371
+ );
63372
+ }
62027
63373
  },
62028
63374
  ColorFromColorspace: ([components, space], compile3) => {
62029
63375
  if (components === null || space === null)
62030
63376
  throw new Error("ColorFromColorspace: need components and space");
62031
- return `_gpu_oklab_to_srgb(${compile3(components)})`;
63377
+ const spaceName = readStringLiteral(space);
63378
+ if (spaceName === null)
63379
+ throw new Error("ColorFromColorspace: space must be a string literal");
63380
+ const c = compile3(components);
63381
+ switch (spaceName) {
63382
+ case "oklch":
63383
+ return c;
63384
+ case "oklab":
63385
+ case "lab":
63386
+ return `_gpu_oklab_to_oklch(${c})`;
63387
+ case "rgb":
63388
+ return `_gpu_srgb_to_oklch(${c})`;
63389
+ case "hsl":
63390
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${c}))`;
63391
+ case "hsv":
63392
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${c}))`;
63393
+ default:
63394
+ throw new Error(
63395
+ `ColorFromColorspace: unsupported space "${spaceName}" on GPU target`
63396
+ );
63397
+ }
63398
+ },
63399
+ // ---------------------------------------------------------------------------
63400
+ // Color literals. Each typed head compiles to a canonical OKLCh vec3.
63401
+ // Alpha (4th argument) is dropped — GPU color values are vec3 only. Pass
63402
+ // alpha as a separate uniform if it's needed at the framebuffer boundary.
63403
+ // ---------------------------------------------------------------------------
63404
+ Color: ([s], _compile2, target) => {
63405
+ if (s === null) throw new Error("Color: no argument");
63406
+ const str = readStringLiteral(s);
63407
+ if (str === null)
63408
+ throw new Error("Color: argument must be a string literal on GPU target");
63409
+ const packed = parseColor(str);
63410
+ if (packed === 0 && str.trim().toLowerCase() !== "transparent")
63411
+ throw new Error(`Color: invalid color string "${str}"`);
63412
+ const r = packed >>> 24 & 255;
63413
+ const g = packed >>> 16 & 255;
63414
+ const b = packed >>> 8 & 255;
63415
+ const oklch2 = rgbToOklch({ r, g, b });
63416
+ return `${gpuVec3(target)}(${formatFloat(oklch2.L)}, ${formatFloat(oklch2.C)}, ${formatFloat(oklch2.H)})`;
63417
+ },
63418
+ Rgb: (args, compile3, target) => {
63419
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
63420
+ const v3 = gpuVec3(target);
63421
+ return `_gpu_srgb_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
63422
+ },
63423
+ Hsv: (args, compile3, target) => {
63424
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
63425
+ const v3 = gpuVec3(target);
63426
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
63427
+ },
63428
+ Hsl: (args, compile3, target) => {
63429
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
63430
+ const v3 = gpuVec3(target);
63431
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
63432
+ },
63433
+ Oklab: (args, compile3, target) => {
63434
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
63435
+ const v3 = gpuVec3(target);
63436
+ return `_gpu_oklab_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
63437
+ },
63438
+ Oklch: (args, compile3, target) => {
63439
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
63440
+ const v3 = gpuVec3(target);
63441
+ return `${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})`;
63442
+ },
63443
+ // ---------------------------------------------------------------------------
63444
+ // As* operators. AsOklch is identity (canonical). The other As* return
63445
+ // components in the named space, equivalent to ColorToColorspace(c, 'x').
63446
+ // ---------------------------------------------------------------------------
63447
+ AsOklch: ([c], compile3) => {
63448
+ if (c === null) throw new Error("AsOklch: no argument");
63449
+ return compile3(c);
63450
+ },
63451
+ AsOklab: ([c], compile3) => {
63452
+ if (c === null) throw new Error("AsOklab: no argument");
63453
+ return `_gpu_oklch_to_oklab(${compile3(c)})`;
63454
+ },
63455
+ AsRgb: ([c], compile3) => {
63456
+ if (c === null) throw new Error("AsRgb: no argument");
63457
+ return `_gpu_oklch_to_srgb(${compile3(c)})`;
63458
+ },
63459
+ AsHsv: ([c], compile3) => {
63460
+ if (c === null) throw new Error("AsHsv: no argument");
63461
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${compile3(c)}))`;
63462
+ },
63463
+ AsHsl: ([c], compile3) => {
63464
+ if (c === null) throw new Error("AsHsl: no argument");
63465
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${compile3(c)}))`;
62032
63466
  },
62033
63467
  // Fractal functions
62034
63468
  Mandelbrot: ([c, maxIter], compile3, target) => {
@@ -62721,28 +64155,124 @@ vec3 _gpu_oklab_to_srgb(vec3 lab) {
62721
64155
 
62722
64156
  vec3 _gpu_oklab_to_oklch(vec3 lab) {
62723
64157
  float C = length(lab.yz);
62724
- float H = atan(lab.z, lab.y);
64158
+ float H = atan(lab.z, lab.y) * (180.0 / 3.14159265359);
64159
+ if (H < 0.0) H += 360.0;
62725
64160
  return vec3(lab.x, C, H);
62726
64161
  }
62727
64162
 
62728
64163
  vec3 _gpu_oklch_to_oklab(vec3 lch) {
62729
- return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
64164
+ float h_rad = lch.z * (3.14159265359 / 180.0);
64165
+ return vec3(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
64166
+ }
64167
+
64168
+ vec3 _gpu_srgb_to_oklch(vec3 rgb) {
64169
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
62730
64170
  }
62731
64171
 
62732
- vec3 _gpu_color_mix(vec3 rgb1, vec3 rgb2, float t) {
62733
- vec3 lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
62734
- vec3 lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
64172
+ vec3 _gpu_oklch_to_srgb(vec3 lch) {
64173
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
64174
+ }
64175
+
64176
+ // HSL conversion. Hue in degrees, saturation/lightness in 0-1.
64177
+ vec3 _gpu_hsl_to_rgb(vec3 hsl) {
64178
+ float h = hsl.x;
64179
+ float s = hsl.y;
64180
+ float l = hsl.z;
64181
+ float c = (1.0 - abs(2.0 * l - 1.0)) * s;
64182
+ float h6 = h / 60.0;
64183
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
64184
+ float r = 0.0;
64185
+ float g = 0.0;
64186
+ float b = 0.0;
64187
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64188
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64189
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64190
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64191
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64192
+ else { r = c; g = 0.0; b = x; }
64193
+ float m = l - c / 2.0;
64194
+ return vec3(r + m, g + m, b + m);
64195
+ }
64196
+
64197
+ vec3 _gpu_rgb_to_hsl(vec3 rgb) {
64198
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
64199
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
64200
+ float l = (maxc + minc) / 2.0;
64201
+ float d = maxc - minc;
64202
+ if (d < 1e-6) return vec3(0.0, 0.0, l);
64203
+ float s = d / (1.0 - abs(2.0 * l - 1.0));
64204
+ float h;
64205
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
64206
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
64207
+ else h = (rgb.x - rgb.y) / d + 4.0;
64208
+ h *= 60.0;
64209
+ if (h < 0.0) h += 360.0;
64210
+ return vec3(h, s, l);
64211
+ }
64212
+
64213
+ // HSV conversion. Hue in degrees, saturation/value in 0-1.
64214
+ vec3 _gpu_hsv_to_rgb(vec3 hsv) {
64215
+ float h = hsv.x;
64216
+ float s = hsv.y;
64217
+ float v = hsv.z;
64218
+ float c = v * s;
64219
+ float h6 = h / 60.0;
64220
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
64221
+ float r = 0.0;
64222
+ float g = 0.0;
64223
+ float b = 0.0;
64224
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64225
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64226
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64227
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64228
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64229
+ else { r = c; g = 0.0; b = x; }
64230
+ float m = v - c;
64231
+ return vec3(r + m, g + m, b + m);
64232
+ }
64233
+
64234
+ vec3 _gpu_rgb_to_hsv(vec3 rgb) {
64235
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
64236
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
64237
+ float v = maxc;
64238
+ float d = maxc - minc;
64239
+ if (d < 1e-6) return vec3(0.0, 0.0, v);
64240
+ float s = (maxc < 1e-6) ? 0.0 : d / maxc;
64241
+ float h;
64242
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
64243
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
64244
+ else h = (rgb.x - rgb.y) / d + 4.0;
64245
+ h *= 60.0;
64246
+ if (h < 0.0) h += 360.0;
64247
+ return vec3(h, s, v);
64248
+ }
64249
+
64250
+ vec3 _gpu_color_mix(vec3 lch1, vec3 lch2, float t) {
62735
64251
  float L = mix(lch1.x, lch2.x, t);
62736
64252
  float C = mix(lch1.y, lch2.y, t);
62737
- float dh = lch2.z - lch1.z;
62738
- const float PI = 3.14159265359;
62739
- if (dh > PI) dh -= 2.0 * PI;
62740
- if (dh < -PI) dh += 2.0 * PI;
62741
- float H = lch1.z + dh * t;
62742
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3(L, C, H)));
64253
+ bool a1 = lch1.y < 1e-6;
64254
+ bool a2 = lch2.y < 1e-6;
64255
+ float H;
64256
+ if (a1 && a2) {
64257
+ H = lch1.z;
64258
+ } else if (a1) {
64259
+ H = lch2.z;
64260
+ } else if (a2) {
64261
+ H = lch1.z;
64262
+ } else {
64263
+ float dh = lch2.z - lch1.z;
64264
+ if (dh > 180.0) dh -= 360.0;
64265
+ if (dh < -180.0) dh += 360.0;
64266
+ H = lch1.z + dh * t;
64267
+ if (H < 0.0) H += 360.0;
64268
+ if (H >= 360.0) H -= 360.0;
64269
+ }
64270
+ return vec3(L, C, H);
62743
64271
  }
62744
64272
 
62745
- float _gpu_apca(vec3 bg, vec3 fg) {
64273
+ float _gpu_apca(vec3 lch_bg, vec3 lch_fg) {
64274
+ vec3 bg = _gpu_oklch_to_srgb(lch_bg);
64275
+ vec3 fg = _gpu_oklch_to_srgb(lch_fg);
62746
64276
  float bgR = _gpu_srgb_to_linear(bg.x);
62747
64277
  float bgG = _gpu_srgb_to_linear(bg.y);
62748
64278
  float bgB = _gpu_srgb_to_linear(bg.z);
@@ -62753,9 +64283,7 @@ float _gpu_apca(vec3 bg, vec3 fg) {
62753
64283
  float fgY = 0.2126729 * fgR + 0.7151522 * fgG + 0.0721750 * fgB;
62754
64284
  float bgC = pow(bgY, 0.56);
62755
64285
  float fgC = pow(fgY, 0.57);
62756
- float contrast = (bgC > fgC)
62757
- ? (bgC - fgC) * 1.14
62758
- : (bgC - fgC) * 1.14;
64286
+ float contrast = (bgC - fgC) * 1.14;
62759
64287
  return contrast * 100.0;
62760
64288
  }
62761
64289
  `;
@@ -62799,28 +64327,133 @@ fn _gpu_oklab_to_srgb(lab: vec3f) -> vec3f {
62799
64327
 
62800
64328
  fn _gpu_oklab_to_oklch(lab: vec3f) -> vec3f {
62801
64329
  let C = length(lab.yz);
62802
- let H = atan2(lab.z, lab.y);
64330
+ var H = atan2(lab.z, lab.y) * (180.0 / 3.14159265359);
64331
+ if (H < 0.0) { H = H + 360.0; }
62803
64332
  return vec3f(lab.x, C, H);
62804
64333
  }
62805
64334
 
62806
64335
  fn _gpu_oklch_to_oklab(lch: vec3f) -> vec3f {
62807
- return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
64336
+ let h_rad = lch.z * (3.14159265359 / 180.0);
64337
+ return vec3f(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
64338
+ }
64339
+
64340
+ fn _gpu_srgb_to_oklch(rgb: vec3f) -> vec3f {
64341
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
64342
+ }
64343
+
64344
+ fn _gpu_oklch_to_srgb(lch: vec3f) -> vec3f {
64345
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
64346
+ }
64347
+
64348
+ fn _gpu_hsl_to_rgb(hsl: vec3f) -> vec3f {
64349
+ let h = hsl.x;
64350
+ let s = hsl.y;
64351
+ let l = hsl.z;
64352
+ let c = (1.0 - abs(2.0 * l - 1.0)) * s;
64353
+ let h6 = h / 60.0;
64354
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
64355
+ var r: f32 = 0.0;
64356
+ var g: f32 = 0.0;
64357
+ var b: f32 = 0.0;
64358
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64359
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64360
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64361
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64362
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64363
+ else { r = c; g = 0.0; b = x; }
64364
+ let m = l - c / 2.0;
64365
+ return vec3f(r + m, g + m, b + m);
62808
64366
  }
62809
64367
 
62810
- fn _gpu_color_mix(rgb1: vec3f, rgb2: vec3f, t: f32) -> vec3f {
62811
- let lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
62812
- let lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
64368
+ fn _gpu_rgb_to_hsl(rgb: vec3f) -> vec3f {
64369
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
64370
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
64371
+ let l = (maxc + minc) / 2.0;
64372
+ let d = maxc - minc;
64373
+ if (d < 1e-6) { return vec3f(0.0, 0.0, l); }
64374
+ let s = d / (1.0 - abs(2.0 * l - 1.0));
64375
+ var h: f32;
64376
+ if (maxc == rgb.x) {
64377
+ let v = (rgb.y - rgb.z) / d;
64378
+ h = v - 6.0 * floor(v / 6.0);
64379
+ } else if (maxc == rgb.y) {
64380
+ h = (rgb.z - rgb.x) / d + 2.0;
64381
+ } else {
64382
+ h = (rgb.x - rgb.y) / d + 4.0;
64383
+ }
64384
+ h = h * 60.0;
64385
+ if (h < 0.0) { h = h + 360.0; }
64386
+ return vec3f(h, s, l);
64387
+ }
64388
+
64389
+ fn _gpu_hsv_to_rgb(hsv: vec3f) -> vec3f {
64390
+ let h = hsv.x;
64391
+ let s = hsv.y;
64392
+ let v = hsv.z;
64393
+ let c = v * s;
64394
+ let h6 = h / 60.0;
64395
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
64396
+ var r: f32 = 0.0;
64397
+ var g: f32 = 0.0;
64398
+ var b: f32 = 0.0;
64399
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64400
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64401
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64402
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64403
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64404
+ else { r = c; g = 0.0; b = x; }
64405
+ let m = v - c;
64406
+ return vec3f(r + m, g + m, b + m);
64407
+ }
64408
+
64409
+ fn _gpu_rgb_to_hsv(rgb: vec3f) -> vec3f {
64410
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
64411
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
64412
+ let v = maxc;
64413
+ let d = maxc - minc;
64414
+ if (d < 1e-6) { return vec3f(0.0, 0.0, v); }
64415
+ var s: f32 = 0.0;
64416
+ if (maxc >= 1e-6) { s = d / maxc; }
64417
+ var h: f32;
64418
+ if (maxc == rgb.x) {
64419
+ let q = (rgb.y - rgb.z) / d;
64420
+ h = q - 6.0 * floor(q / 6.0);
64421
+ } else if (maxc == rgb.y) {
64422
+ h = (rgb.z - rgb.x) / d + 2.0;
64423
+ } else {
64424
+ h = (rgb.x - rgb.y) / d + 4.0;
64425
+ }
64426
+ h = h * 60.0;
64427
+ if (h < 0.0) { h = h + 360.0; }
64428
+ return vec3f(h, s, v);
64429
+ }
64430
+
64431
+ fn _gpu_color_mix(lch1: vec3f, lch2: vec3f, t: f32) -> vec3f {
62813
64432
  let L = mix(lch1.x, lch2.x, t);
62814
64433
  let C = mix(lch1.y, lch2.y, t);
62815
- let PI = 3.14159265359;
62816
- var dh = lch2.z - lch1.z;
62817
- if (dh > PI) { dh -= 2.0 * PI; }
62818
- if (dh < -PI) { dh += 2.0 * PI; }
62819
- let H = lch1.z + dh * t;
62820
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3f(L, C, H)));
64434
+ let a1 = lch1.y < 1e-6;
64435
+ let a2 = lch2.y < 1e-6;
64436
+ var H: f32;
64437
+ if (a1 && a2) {
64438
+ H = lch1.z;
64439
+ } else if (a1) {
64440
+ H = lch2.z;
64441
+ } else if (a2) {
64442
+ H = lch1.z;
64443
+ } else {
64444
+ var dh = lch2.z - lch1.z;
64445
+ if (dh > 180.0) { dh = dh - 360.0; }
64446
+ if (dh < -180.0) { dh = dh + 360.0; }
64447
+ H = lch1.z + dh * t;
64448
+ if (H < 0.0) { H = H + 360.0; }
64449
+ if (H >= 360.0) { H = H - 360.0; }
64450
+ }
64451
+ return vec3f(L, C, H);
62821
64452
  }
62822
64453
 
62823
- fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
64454
+ fn _gpu_apca(lch_bg: vec3f, lch_fg: vec3f) -> f32 {
64455
+ let bg = _gpu_oklch_to_srgb(lch_bg);
64456
+ let fg = _gpu_oklch_to_srgb(lch_fg);
62824
64457
  let bgR = _gpu_srgb_to_linear(bg.x);
62825
64458
  let bgG = _gpu_srgb_to_linear(bg.y);
62826
64459
  let bgB = _gpu_srgb_to_linear(bg.z);
@@ -63108,7 +64741,7 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63108
64741
  if (stmts.length === 0) return "";
63109
64742
  const last = stmts.length - 1;
63110
64743
  stmts[last] = `return ${stmts[last]}`;
63111
- return stmts.join(";\n");
64744
+ return stmts.join(";\n") + ";";
63112
64745
  },
63113
64746
  ...options
63114
64747
  };
@@ -63207,7 +64840,7 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63207
64840
  if (body.includes("\n")) {
63208
64841
  const indented = body.split("\n").map((l) => ` ${l}`).join("\n");
63209
64842
  return `${returnType} ${functionName}(${params}) {
63210
- ${indented};
64843
+ ${indented}
63211
64844
  }`;
63212
64845
  }
63213
64846
  return `${returnType} ${functionName}(${params}) {
@@ -63318,7 +64951,7 @@ ${indented};
63318
64951
  return `fn ${functionName}(${params}) -> ${toWGSLType(
63319
64952
  returnType
63320
64953
  )} {
63321
- ${indented};
64954
+ ${indented}
63322
64955
  }`;
63323
64956
  }
63324
64957
  return `fn ${functionName}(${params}) -> ${toWGSLType(returnType)} {
@@ -67303,6 +68936,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
67303
68936
  this.pushScope(void 0, "global");
67304
68937
  this._compilationTargets.registerDefaults();
67305
68938
  if (options?.latexSyntax) this._latexSyntax = options.latexSyntax;
68939
+ if (options?.latexOptions) this._latexOptions = { ...options.latexOptions };
67306
68940
  hidePrivateProperties(this);
67307
68941
  }
67308
68942
  toJSON() {
@@ -67717,6 +69351,15 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
67717
69351
  lookupDefinition(id) {
67718
69352
  return lookupDefinition(this, id);
67719
69353
  }
69354
+ operatorInfo(head) {
69355
+ const def = this.lookupDefinition(head);
69356
+ if (!def || !isOperatorDef(def)) return void 0;
69357
+ const op = def.operator;
69358
+ return {
69359
+ kind: op.evaluate || op.collection ? "function" : "opaque",
69360
+ signature: op.signature
69361
+ };
69362
+ }
67720
69363
  /**
67721
69364
  * Associate a new definition to a symbol in the current context.
67722
69365
  *
@@ -67963,6 +69606,29 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
67963
69606
  );
67964
69607
  return this._latexSyntax;
67965
69608
  }
69609
+ /** @internal Engine-wide LaTeX parse/serialize options (e.g. decimalSeparator).
69610
+ * Merged into every `parse()` and `toLatex()` call between the LatexSyntax
69611
+ * instance defaults and any per-call overrides. */
69612
+ _latexOptions = {};
69613
+ /** Engine-wide LaTeX parse/serialize options.
69614
+ *
69615
+ * These options are merged into every `parse()` and `toLatex()` call.
69616
+ * Precedence (most-specific wins):
69617
+ * 1. LatexSyntax instance defaults (set at its construction)
69618
+ * 2. `ce.latexOptions` (this property)
69619
+ * 3. Per-call options passed to `ce.parse()` / `expr.toLatex()`
69620
+ *
69621
+ * Assigning replaces the whole bag. Use spread to merge:
69622
+ * ```ts
69623
+ * ce.latexOptions = { ...ce.latexOptions, decimalSeparator: '{,}' };
69624
+ * ```
69625
+ */
69626
+ get latexOptions() {
69627
+ return this._latexOptions;
69628
+ }
69629
+ set latexOptions(options) {
69630
+ this._latexOptions = { ...options };
69631
+ }
67966
69632
  parse(latex, options) {
67967
69633
  if (latex === null || latex === void 0) return null;
67968
69634
  if (typeof latex !== "string")
@@ -67970,7 +69636,6 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
67970
69636
  const syntax = this._requireLatexSyntax();
67971
69637
  const { form, ...parseOpts } = options ?? {};
67972
69638
  const result = syntax.parse(latex, {
67973
- decimalSeparator: ".",
67974
69639
  getSymbolType: (id) => {
67975
69640
  const def = this.lookupDefinition(id);
67976
69641
  if (!def) return BoxedType.unknown;
@@ -67982,6 +69647,8 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
67982
69647
  const def = this.lookupDefinition(id);
67983
69648
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
67984
69649
  },
69650
+ tolerance: this.tolerance,
69651
+ ...this._latexOptions,
67985
69652
  ...parseOpts
67986
69653
  });
67987
69654
  if (result === null) return null;
@@ -68145,7 +69812,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
68145
69812
  _setDefaultEngineFactory(() => new ComputeEngine());
68146
69813
 
68147
69814
  // src/core.ts
68148
- var version = "0.55.6";
69815
+ var version = "0.57.0";
68149
69816
  return __toCommonJS(core_exports);
68150
69817
  })();
68151
69818
  /*! Bundled license information: