@cortex-js/compute-engine 0.55.5 → 0.56.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 (259) hide show
  1. package/dist/compile.esm.js +912 -770
  2. package/dist/compile.min.esm.js +300 -523
  3. package/dist/compile.min.umd.cjs +301 -524
  4. package/dist/compile.umd.cjs +912 -770
  5. package/dist/compute-engine.esm.js +1517 -902
  6. package/dist/compute-engine.min.esm.js +300 -522
  7. package/dist/compute-engine.min.umd.cjs +300 -522
  8. package/dist/compute-engine.umd.cjs +1517 -902
  9. package/dist/core.esm.js +1516 -901
  10. package/dist/core.min.esm.js +299 -521
  11. package/dist/core.min.umd.cjs +299 -521
  12. package/dist/core.umd.cjs +1516 -901
  13. package/dist/interval.esm.js +268 -63
  14. package/dist/interval.min.esm.js +7 -7
  15. package/dist/interval.min.umd.cjs +7 -7
  16. package/dist/interval.umd.cjs +268 -63
  17. package/dist/latex-syntax.esm.js +371 -77
  18. package/dist/latex-syntax.min.esm.js +7 -6
  19. package/dist/latex-syntax.min.umd.cjs +7 -6
  20. package/dist/latex-syntax.umd.cjs +371 -77
  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 +1 -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 +1 -1
  105. package/dist/types/compute-engine/compilation/compile-expression.d.ts +2 -3
  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 -58
  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 -67
  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 +23 -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 +4 -2
  169. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  170. package/dist/types/compute-engine/latex-syntax/types.d.ts +1 -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 +1 -1
  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 +6 -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 +2 -3
  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
  259. package/dist/types/compute-engine/compilation/fractal-orbit.d.ts +0 -19
package/dist/core.umd.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /** ComputeEngineCore 0.55.5 */
1
+ /** ComputeEngineCore 0.56.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
@@ -7895,9 +7898,13 @@ var ComputeEngineCore = (() => {
7895
7898
  fractionalDigits: "auto"
7896
7899
  });
7897
7900
  const syntax = this.engine._requireLatexSyntax();
7898
- if (!options || Object.keys(options).length === 0)
7899
- return syntax.serialize(json);
7900
- return syntax.serialize(json, options);
7901
+ const latexOpts = this.engine.latexOptions;
7902
+ const haveEngineOpts = Object.keys(latexOpts).length > 0;
7903
+ const haveCallOpts = options && Object.keys(options).length > 0;
7904
+ if (!haveEngineOpts && !haveCallOpts) return syntax.serialize(json);
7905
+ if (!haveEngineOpts) return syntax.serialize(json, options);
7906
+ if (!haveCallOpts) return syntax.serialize(json, { ...latexOpts });
7907
+ return syntax.serialize(json, { ...latexOpts, ...options });
7901
7908
  }
7902
7909
  /** Called by `JSON.stringify()` when serializing to json.
7903
7910
  *
@@ -7941,11 +7948,13 @@ var ComputeEngineCore = (() => {
7941
7948
  "number",
7942
7949
  "dictionary"
7943
7950
  ];
7944
- }
7945
- if (Array.isArray(options.shorthands))
7951
+ } else if (Array.isArray(options.shorthands)) {
7946
7952
  defaultOptions.shorthands = options.shorthands;
7953
+ }
7947
7954
  if (typeof options.metadata === "string" && options.metadata === "all" || options.metadata?.includes("all")) {
7948
7955
  defaultOptions.metadata = ["latex", "wikidata"];
7956
+ } else if (Array.isArray(options.metadata)) {
7957
+ defaultOptions.metadata = options.metadata;
7949
7958
  }
7950
7959
  if (options.fractionalDigits === "auto")
7951
7960
  defaultOptions.fractionalDigits = -this.engine.precision;
@@ -11084,10 +11093,6 @@ var ComputeEngineCore = (() => {
11084
11093
  // Lagrange notation
11085
11094
  {
11086
11095
  name: "Derivative",
11087
- // @todo: Leibniz notation: {% latex " \\frac{d^n}{dx^n} f(x)" %}
11088
- // @todo: Euler modified notation: This notation is used by Mathematica. The Euler notation uses `D` instead of
11089
- // `\partial`: `\partial_{x} f`, `\partial_{x,y} f`
11090
- // Newton notation (\dot{v}, \ddot{v}) is implemented below
11091
11096
  serialize: (serializer, expr2) => {
11092
11097
  const degree = machineValue(operand(expr2, 2)) ?? 1;
11093
11098
  const base = serializer.serialize(operand(expr2, 1));
@@ -12125,14 +12130,18 @@ var ComputeEngineCore = (() => {
12125
12130
  return ["Complement", lhs];
12126
12131
  }
12127
12132
  // precedence: 240,
12128
- // @todo: serialize for the multiple argument case
12129
12133
  },
12130
12134
  {
12131
12135
  name: "Complement",
12132
12136
  latexTrigger: ["^", "<{>", "\\complement", "<}>"],
12133
- kind: "postfix"
12137
+ kind: "postfix",
12134
12138
  // precedence: 240,
12135
- // @todo: serialize for the multiple argument case
12139
+ serialize: (serializer, expr2) => {
12140
+ return joinLatex([
12141
+ serializer.serialize(operand(expr2, 1)),
12142
+ "^\\complement"
12143
+ ]);
12144
+ }
12136
12145
  },
12137
12146
  {
12138
12147
  name: "Intersection",
@@ -12219,7 +12228,6 @@ var ComputeEngineCore = (() => {
12219
12228
  // commands like \rbrack a, b \rbrack which are unambiguous.
12220
12229
  {
12221
12230
  name: "Multiple",
12222
- // @todo: parse
12223
12231
  serialize: serializeSet
12224
12232
  },
12225
12233
  {
@@ -12228,14 +12236,28 @@ var ComputeEngineCore = (() => {
12228
12236
  kind: "infix",
12229
12237
  precedence: 350
12230
12238
  },
12239
+ // \mid as a separator/operator (used in set-builder notation: {x \mid x > 0})
12240
+ // Low precedence so it binds loosely — everything on each side is parsed first
12241
+ {
12242
+ name: "Divides",
12243
+ latexTrigger: ["\\mid"],
12244
+ kind: "infix",
12245
+ precedence: 160
12246
+ },
12231
12247
  {
12232
12248
  name: "Set",
12233
12249
  kind: "matchfix",
12234
12250
  openTrigger: "{",
12235
12251
  closeTrigger: "}",
12236
- // @todo: the set syntax can also include conditions...
12237
12252
  parse: (_parser, body) => {
12238
12253
  if (isEmptySequence(body)) return "EmptySet";
12254
+ const h = operator(body);
12255
+ if (h === "Divides" || h === "Colon") {
12256
+ const expr2 = operand(body, 1);
12257
+ const condition = operand(body, 2);
12258
+ if (expr2 !== null && condition !== null)
12259
+ return ["Set", expr2, ["Condition", condition]];
12260
+ }
12239
12261
  if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12240
12262
  body = operand(body, 1);
12241
12263
  }
@@ -12243,6 +12265,17 @@ var ComputeEngineCore = (() => {
12243
12265
  return ["Set", ...operands(body)];
12244
12266
  },
12245
12267
  serialize: (serializer, expr2) => {
12268
+ if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
12269
+ const condition = operand(expr2, 2);
12270
+ return joinLatex([
12271
+ "\\lbrace",
12272
+ serializer.serialize(operand(expr2, 1)),
12273
+ "\\mid",
12274
+ // Serialize the inner expression of the Condition wrapper
12275
+ serializer.serialize(operand(condition, 1)),
12276
+ "\\rbrace"
12277
+ ]);
12278
+ }
12246
12279
  return joinLatex([
12247
12280
  "\\lbrace",
12248
12281
  operands(expr2).map((x) => serializer.serialize(x)).join(", "),
@@ -12409,23 +12442,6 @@ var ComputeEngineCore = (() => {
12409
12442
  if (expr2 === null) return "";
12410
12443
  const h = operator(expr2);
12411
12444
  if (!h) return "";
12412
- if (h === "Set") {
12413
- if (nops(expr2) === 0) return "\\emptyset";
12414
- if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
12415
- return joinLatex([
12416
- "\\left\\lbrace",
12417
- serializer.serialize(operand(expr2, 1)),
12418
- "\\middle\\mid",
12419
- serializer.serialize(operand(expr2, 2)),
12420
- "\\right\\rbrace"
12421
- ]);
12422
- }
12423
- return joinLatex([
12424
- "\\left\\lbrace",
12425
- ...operands(expr2).map((x) => serializer.serialize(x) + " ,"),
12426
- "\\right\\rbrace"
12427
- ]);
12428
- }
12429
12445
  if (h === "Multiple") {
12430
12446
  }
12431
12447
  if (h === "Range") {
@@ -13573,11 +13589,13 @@ var ComputeEngineCore = (() => {
13573
13589
  if (!parser.match("_")) return null;
13574
13590
  const base = parser.parseGroup();
13575
13591
  if (operator(base) !== "To") return null;
13576
- const expr2 = parser.parseArguments("implicit");
13592
+ const expr2 = parser.parseExpression({
13593
+ minPrec: MULTIPLICATION_PRECEDENCE
13594
+ });
13577
13595
  if (!expr2) return null;
13578
13596
  return [
13579
13597
  "Limit",
13580
- ["Function", expr2[0], operand(base, 1)],
13598
+ ["Function", expr2, operand(base, 1)],
13581
13599
  operand(base, 2)
13582
13600
  ];
13583
13601
  },
@@ -13658,6 +13676,8 @@ var ComputeEngineCore = (() => {
13658
13676
  precedence: DIVISION_PRECEDENCE,
13659
13677
  parse: "Mod"
13660
13678
  },
13679
+ // Function-style alias: `\operatorname{mod}(a, b)`
13680
+ { latexTrigger: "\\operatorname{mod}", parse: "Mod" },
13661
13681
  {
13662
13682
  latexTrigger: "\\pmod",
13663
13683
  kind: "prefix",
@@ -13898,6 +13918,13 @@ var ComputeEngineCore = (() => {
13898
13918
  const rhs = serializer.wrap(operand(expr2, 2), ADDITION_PRECEDENCE + 3);
13899
13919
  return joinLatex([lhs, "-", rhs]);
13900
13920
  }
13921
+ },
13922
+ // Euclidean distance between two points (tuples of numbers).
13923
+ {
13924
+ name: "Distance",
13925
+ latexTrigger: ["\\operatorname{distance}"],
13926
+ kind: "function",
13927
+ serialize: (serializer, expr2) => "\\operatorname{distance}" + serializer.wrapArguments(expr2)
13901
13928
  }
13902
13929
  ];
13903
13930
  function getIndexAssignment(expr2, upper) {
@@ -15322,7 +15349,9 @@ var ComputeEngineCore = (() => {
15322
15349
  if (!expr2 || !symbol(expr2)) return null;
15323
15350
  return ["Mean", expr2];
15324
15351
  }
15325
- }
15352
+ },
15353
+ // Function-style alias: `\operatorname{var}(...)`
15354
+ { latexTrigger: "\\operatorname{var}", parse: "Variance" }
15326
15355
  ];
15327
15356
 
15328
15357
  // src/compute-engine/numerics/unit-data.ts
@@ -15874,12 +15903,52 @@ var ComputeEngineCore = (() => {
15874
15903
  ];
15875
15904
 
15876
15905
  // src/compute-engine/latex-syntax/dictionary/definitions-other.ts
15906
+ var TEX_UNITS = [
15907
+ "pt",
15908
+ "em",
15909
+ "mu",
15910
+ "ex",
15911
+ "mm",
15912
+ "cm",
15913
+ "in",
15914
+ "bp",
15915
+ "sp",
15916
+ "dd",
15917
+ "cc",
15918
+ "pc",
15919
+ "nc",
15920
+ "nd"
15921
+ ];
15922
+ function skipTexDimension(parser) {
15923
+ parser.skipSpace();
15924
+ if (parser.peek === "-" || parser.peek === "+") parser.nextToken();
15925
+ while (/^[\d.]$/.test(parser.peek)) parser.nextToken();
15926
+ for (const unit of TEX_UNITS) {
15927
+ if (parser.matchAll([...unit])) return;
15928
+ }
15929
+ }
15877
15930
  function parseSingleArg(cmd) {
15878
15931
  return (parser) => {
15879
15932
  const arg = parser.parseGroup();
15880
15933
  return arg === null ? [cmd] : [cmd, arg];
15881
15934
  };
15882
15935
  }
15936
+ function parseMathStyleSwitch(mathStyle) {
15937
+ return (parser) => {
15938
+ const body = parser.parseExpression();
15939
+ if (body !== null && !isEmptySequence(body))
15940
+ return ["Annotated", body, { dict: { mathStyle } }];
15941
+ return "Nothing";
15942
+ };
15943
+ }
15944
+ function parseSizeSwitch(size) {
15945
+ return (parser) => {
15946
+ const body = parser.parseExpression();
15947
+ if (body !== null && !isEmptySequence(body))
15948
+ return ["Annotated", body, { dict: { size } }];
15949
+ return "Nothing";
15950
+ };
15951
+ }
15883
15952
  var DEFINITIONS_OTHERS = [
15884
15953
  {
15885
15954
  name: "Overscript",
@@ -16119,80 +16188,71 @@ var ComputeEngineCore = (() => {
16119
16188
  },
16120
16189
  {
16121
16190
  latexTrigger: ["\\displaystyle"],
16122
- parse: () => "Nothing"
16123
- // @todo: parse as ['Annotated'...]
16191
+ parse: parseMathStyleSwitch("normal")
16124
16192
  },
16125
16193
  {
16126
16194
  latexTrigger: ["\\textstyle"],
16127
- parse: () => "Nothing"
16128
- // @todo: parse as ['Annotated'...]
16195
+ parse: parseMathStyleSwitch("compact")
16129
16196
  },
16130
16197
  {
16131
16198
  latexTrigger: ["\\scriptstyle"],
16132
- parse: () => "Nothing"
16133
- // @todo: parse as ['Annotated'...]
16199
+ parse: parseMathStyleSwitch("script")
16134
16200
  },
16135
16201
  {
16136
16202
  latexTrigger: ["\\scriptscriptstyle"],
16137
- parse: () => "Nothing"
16138
- // @todo: parse as ['Annotated'...]
16203
+ parse: parseMathStyleSwitch("scriptscript")
16139
16204
  },
16140
16205
  {
16141
16206
  latexTrigger: ["\\color"],
16142
16207
  parse: (parser) => {
16143
- parser.parseGroup();
16208
+ const color = parser.parseStringGroup();
16209
+ if (color !== null) {
16210
+ const body = parser.parseExpression();
16211
+ if (body !== null && !isEmptySequence(body))
16212
+ return ["Annotated", body, { dict: { color } }];
16213
+ }
16144
16214
  return "Nothing";
16145
16215
  }
16146
16216
  },
16147
16217
  {
16148
16218
  latexTrigger: ["\\tiny"],
16149
- parse: () => "Nothing"
16150
- // @todo: parse as ['Annotated'...]
16219
+ parse: parseSizeSwitch(1)
16151
16220
  },
16152
16221
  {
16153
16222
  latexTrigger: ["\\scriptsize"],
16154
- parse: () => "Nothing"
16155
- // @todo: parse as ['Annotated'...]
16223
+ parse: parseSizeSwitch(2)
16156
16224
  },
16157
16225
  {
16158
16226
  latexTrigger: ["\\footnotesize"],
16159
- parse: () => "Nothing"
16160
- // @todo: parse as ['Annotated'...]
16227
+ parse: parseSizeSwitch(3)
16161
16228
  },
16162
16229
  {
16163
16230
  latexTrigger: ["\\small"],
16164
- parse: () => "Nothing"
16165
- // @todo: parse as ['Annotated'...]
16231
+ parse: parseSizeSwitch(4)
16166
16232
  },
16167
16233
  {
16168
16234
  latexTrigger: ["\\normalsize"],
16169
- parse: () => "Nothing"
16170
- // @todo: parse as ['Annotated'...]
16235
+ parse: parseSizeSwitch(5)
16171
16236
  },
16172
16237
  {
16173
16238
  latexTrigger: ["\\large"],
16174
- parse: () => "Nothing"
16175
- // @todo: parse as ['Annotated'...]
16239
+ parse: parseSizeSwitch(6)
16176
16240
  },
16177
16241
  {
16178
16242
  latexTrigger: ["\\Large"],
16179
- parse: () => "Nothing"
16180
- // @todo: parse as ['Annotated'...]
16243
+ parse: parseSizeSwitch(7)
16181
16244
  },
16182
16245
  {
16183
16246
  latexTrigger: ["\\LARGE"],
16184
- parse: () => "Nothing"
16185
- // @todo: parse as ['Annotated'...]
16247
+ parse: parseSizeSwitch(8)
16186
16248
  },
16187
16249
  {
16188
16250
  latexTrigger: ["\\huge"],
16189
- parse: () => "Nothing"
16190
- // @todo: parse as ['Annotated'...]
16251
+ parse: parseSizeSwitch(9)
16191
16252
  },
16192
16253
  {
16193
16254
  latexTrigger: ["\\Huge"],
16194
- parse: () => "Nothing"
16195
- // @todo: parse as ['Annotated'...]
16255
+ parse: parseSizeSwitch(10)
16196
16256
  },
16197
16257
  {
16198
16258
  name: "Annotated",
@@ -16204,6 +16264,10 @@ var ComputeEngineCore = (() => {
16204
16264
  result = joinLatex(["{\\displaystyle", result, "}"]);
16205
16265
  else if (dict.dict.mathStyle === "compact")
16206
16266
  result = joinLatex(["{\\textstyle", result, "}"]);
16267
+ else if (dict.dict.mathStyle === "script")
16268
+ result = joinLatex(["{\\scriptstyle", result, "}"]);
16269
+ else if (dict.dict.mathStyle === "scriptscript")
16270
+ result = joinLatex(["{\\scriptscriptstyle", result, "}"]);
16207
16271
  const v = dict.dict.size;
16208
16272
  if (v !== null && v >= 1 && v <= 10) {
16209
16273
  result = joinLatex([
@@ -16291,6 +16355,28 @@ var ComputeEngineCore = (() => {
16291
16355
  latexTrigger: ["\\enspace"],
16292
16356
  parse: () => ["HorizontalSpacing", 9]
16293
16357
  },
16358
+ {
16359
+ latexTrigger: ["\\hspace"],
16360
+ parse: (parser) => {
16361
+ if (parser.peek === "*") parser.nextToken();
16362
+ parser.parseStringGroup();
16363
+ return ["HorizontalSpacing", 0];
16364
+ }
16365
+ },
16366
+ {
16367
+ latexTrigger: ["\\hskip"],
16368
+ parse: (parser) => {
16369
+ skipTexDimension(parser);
16370
+ return ["HorizontalSpacing", 0];
16371
+ }
16372
+ },
16373
+ {
16374
+ latexTrigger: ["\\kern"],
16375
+ parse: (parser) => {
16376
+ skipTexDimension(parser);
16377
+ return ["HorizontalSpacing", 0];
16378
+ }
16379
+ },
16294
16380
  {
16295
16381
  latexTrigger: ["\\phantom"],
16296
16382
  parse: (parser) => {
@@ -16341,7 +16427,17 @@ var ComputeEngineCore = (() => {
16341
16427
  // `["HorizontalSpacing", expr, 'op'|'bin'|rel]` -> indicate a spacing around and expression, i.e. `\mathbin{x}`, etc...
16342
16428
  serialize: (serializer, expr2) => {
16343
16429
  if (operand(expr2, 2) !== null) {
16344
- return serializer.serialize(operand(expr2, 1));
16430
+ const cls = stringValue(operand(expr2, 2));
16431
+ const inner = serializer.serialize(operand(expr2, 1));
16432
+ if (cls === "bin") return `\\mathbin{${inner}}`;
16433
+ if (cls === "op") return `\\mathop{${inner}}`;
16434
+ if (cls === "rel") return `\\mathrel{${inner}}`;
16435
+ if (cls === "ord") return `\\mathord{${inner}}`;
16436
+ if (cls === "open") return `\\mathopen{${inner}}`;
16437
+ if (cls === "close") return `\\mathclose{${inner}}`;
16438
+ if (cls === "punct") return `\\mathpunct{${inner}}`;
16439
+ if (cls === "inner") return `\\mathinner{${inner}}`;
16440
+ return inner;
16345
16441
  }
16346
16442
  const v = machineValue(operand(expr2, 1));
16347
16443
  if (v === null) return "";
@@ -16356,7 +16452,7 @@ var ComputeEngineCore = (() => {
16356
16452
  36: "\\qquad"
16357
16453
  }[v] ?? "";
16358
16454
  }
16359
- }
16455
+ },
16360
16456
  // if (
16361
16457
  // [
16362
16458
  // '\\!',
@@ -16380,6 +16476,121 @@ var ComputeEngineCore = (() => {
16380
16476
  // name: '',
16381
16477
  // trigger: '\\check',
16382
16478
  // },
16479
+ // ---------------------------------------------------------------------------
16480
+ // Function-style aliases for collection / random operators that some
16481
+ // notations write in lowercase (e.g. `\operatorname{shuffle}(L)`).
16482
+ // The capitalized library entries already exist; these are pure parse
16483
+ // aliases so the lowercase names don't land in `unsupported-operator`.
16484
+ // ---------------------------------------------------------------------------
16485
+ { latexTrigger: "\\operatorname{random}", parse: "Random" },
16486
+ { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16487
+ { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16488
+ { latexTrigger: "\\operatorname{join}", parse: "Join" },
16489
+ // ---------------------------------------------------------------------------
16490
+ // Geometric primitive heads. Registered as known typed heads so consumers
16491
+ // can branch on the operator name; CE itself doesn't render them. The
16492
+ // library entries (with no evaluator) live in `library/core.ts`.
16493
+ // ---------------------------------------------------------------------------
16494
+ {
16495
+ name: "Triangle",
16496
+ latexTrigger: ["\\operatorname{triangle}"],
16497
+ kind: "function",
16498
+ serialize: (serializer, expr2) => "\\operatorname{triangle}" + serializer.wrapArguments(expr2)
16499
+ },
16500
+ // Desmos's geometric `vector(p1, p2)` — a directed segment between two
16501
+ // points. Routed to a dedicated head (not the existing column-vector
16502
+ // `Vector`, which has a narrower `(number+) -> vector` signature).
16503
+ {
16504
+ name: "GeometricVector",
16505
+ latexTrigger: ["\\operatorname{vector}"],
16506
+ kind: "function",
16507
+ serialize: (serializer, expr2) => "\\operatorname{vector}" + serializer.wrapArguments(expr2)
16508
+ },
16509
+ {
16510
+ name: "Sphere",
16511
+ latexTrigger: ["\\operatorname{sphere}"],
16512
+ kind: "function",
16513
+ serialize: (serializer, expr2) => "\\operatorname{sphere}" + serializer.wrapArguments(expr2)
16514
+ },
16515
+ {
16516
+ name: "Segment",
16517
+ latexTrigger: ["\\operatorname{segment}"],
16518
+ kind: "function",
16519
+ serialize: (serializer, expr2) => "\\operatorname{segment}" + serializer.wrapArguments(expr2)
16520
+ }
16521
+ ];
16522
+
16523
+ // src/compute-engine/latex-syntax/dictionary/definitions-colors.ts
16524
+ var DEFINITIONS_COLORS = [
16525
+ // Color constructors (one per colorspace, preserves space on evaluation)
16526
+ {
16527
+ name: "Rgb",
16528
+ latexTrigger: ["\\operatorname{rgb}"],
16529
+ kind: "function",
16530
+ serialize: (serializer, expr2) => "\\operatorname{rgb}" + serializer.wrapArguments(expr2)
16531
+ },
16532
+ {
16533
+ name: "Hsv",
16534
+ latexTrigger: ["\\operatorname{hsv}"],
16535
+ kind: "function",
16536
+ serialize: (serializer, expr2) => "\\operatorname{hsv}" + serializer.wrapArguments(expr2)
16537
+ },
16538
+ {
16539
+ name: "Hsl",
16540
+ latexTrigger: ["\\operatorname{hsl}"],
16541
+ kind: "function",
16542
+ serialize: (serializer, expr2) => "\\operatorname{hsl}" + serializer.wrapArguments(expr2)
16543
+ },
16544
+ {
16545
+ name: "Oklab",
16546
+ latexTrigger: ["\\operatorname{oklab}"],
16547
+ kind: "function",
16548
+ serialize: (serializer, expr2) => "\\operatorname{oklab}" + serializer.wrapArguments(expr2)
16549
+ },
16550
+ {
16551
+ name: "Oklch",
16552
+ latexTrigger: ["\\operatorname{oklch}"],
16553
+ kind: "function",
16554
+ serialize: (serializer, expr2) => "\\operatorname{oklch}" + serializer.wrapArguments(expr2)
16555
+ },
16556
+ // Conversion functions (color → color in the named space)
16557
+ {
16558
+ name: "AsRgb",
16559
+ latexTrigger: ["\\operatorname{asRgb}"],
16560
+ kind: "function",
16561
+ serialize: (serializer, expr2) => "\\operatorname{asRgb}" + serializer.wrapArguments(expr2)
16562
+ },
16563
+ {
16564
+ name: "AsHsv",
16565
+ latexTrigger: ["\\operatorname{asHsv}"],
16566
+ kind: "function",
16567
+ serialize: (serializer, expr2) => "\\operatorname{asHsv}" + serializer.wrapArguments(expr2)
16568
+ },
16569
+ {
16570
+ name: "AsHsl",
16571
+ latexTrigger: ["\\operatorname{asHsl}"],
16572
+ kind: "function",
16573
+ serialize: (serializer, expr2) => "\\operatorname{asHsl}" + serializer.wrapArguments(expr2)
16574
+ },
16575
+ {
16576
+ name: "AsOklab",
16577
+ latexTrigger: ["\\operatorname{asOklab}"],
16578
+ kind: "function",
16579
+ serialize: (serializer, expr2) => "\\operatorname{asOklab}" + serializer.wrapArguments(expr2)
16580
+ },
16581
+ {
16582
+ name: "AsOklch",
16583
+ latexTrigger: ["\\operatorname{asOklch}"],
16584
+ kind: "function",
16585
+ serialize: (serializer, expr2) => "\\operatorname{asOklch}" + serializer.wrapArguments(expr2)
16586
+ },
16587
+ // Perceptual difference (returns a scalar in [0, ~1])
16588
+ {
16589
+ name: "ColorDelta",
16590
+ latexTrigger: ["\\operatorname{colorDelta}"],
16591
+ kind: "function",
16592
+ serialize: (serializer, expr2) => "\\operatorname{colorDelta}" + serializer.wrapArguments(expr2)
16593
+ }
16383
16594
  ];
16384
16595
 
16385
16596
  // src/compute-engine/latex-syntax/dictionary/default-dictionary.ts
@@ -16410,7 +16621,8 @@ var ComputeEngineCore = (() => {
16410
16621
  ...DEFINITIONS_STATISTICS,
16411
16622
  ...DEFINITIONS_UNITS,
16412
16623
  ...DEFINITIONS_OTHERS,
16413
- ...DEFINITIONS_PHYSICS
16624
+ ...DEFINITIONS_PHYSICS,
16625
+ ...DEFINITIONS_COLORS
16414
16626
  ];
16415
16627
 
16416
16628
  // src/math-json/symbols.ts
@@ -16575,6 +16787,17 @@ var ComputeEngineCore = (() => {
16575
16787
  } else if (Array.isArray(openTrigger) && openTrigger.length > 0) {
16576
16788
  openTokens.push(openTrigger[0]);
16577
16789
  }
16790
+ const closeTrigger = indexedEntry.closeTrigger;
16791
+ const closeTokens = /* @__PURE__ */ new Set();
16792
+ if (typeof closeTrigger === "string") {
16793
+ const variants = DELIMITER_SHORTHAND[closeTrigger];
16794
+ if (variants) for (const v of variants) closeTokens.add(v);
16795
+ else closeTokens.add(closeTrigger);
16796
+ if (closeTrigger === "||") closeTokens.add("|");
16797
+ } else if (Array.isArray(closeTrigger) && closeTrigger.length > 0) {
16798
+ closeTokens.add(closeTrigger[0]);
16799
+ }
16800
+ indexedEntry.closeTokens = closeTokens;
16578
16801
  for (const token of openTokens) {
16579
16802
  const existing = result.matchfixByOpen.get(token);
16580
16803
  if (existing) {
@@ -16732,25 +16955,43 @@ var ComputeEngineCore = (() => {
16732
16955
  result.arguments = entry.arguments;
16733
16956
  return result;
16734
16957
  }
16958
+ function serializeTabularBody(serializer, body) {
16959
+ if (!body) return "";
16960
+ if (operator(body) !== "List") return serializer.serialize(body);
16961
+ const rows = operands(body);
16962
+ if (rows.length === 0) return "";
16963
+ if (!rows.every((row) => operator(row) === "List"))
16964
+ return serializer.serialize(body);
16965
+ return rows.map(
16966
+ (row) => operands(row).map((cell) => serializer.serialize(cell)).join(" & ")
16967
+ ).join(" \\\\\n");
16968
+ }
16735
16969
  function makeSerializeHandler(entry, latexTrigger, idTrigger) {
16736
16970
  if (typeof entry.serialize === "function") return entry.serialize;
16737
16971
  const kind = entry["kind"] ?? "expression";
16738
16972
  if (kind === "environment") {
16739
16973
  const envName = entry["symbolTrigger"] ?? entry.name ?? "unknown";
16740
- return (serializer, expr2) => joinLatex([
16741
- `\\begin{${envName}}`,
16742
- serializer.serialize(operand(expr2, 1)),
16743
- `\\end{${envName}}`
16744
- ]);
16974
+ return (serializer, expr2) => {
16975
+ const body = operand(expr2, 1);
16976
+ return joinLatex([
16977
+ `\\begin{${envName}}`,
16978
+ serializeTabularBody(serializer, body),
16979
+ `\\end{${envName}}`
16980
+ ]);
16981
+ };
16745
16982
  }
16746
16983
  if (isMatchfixEntry(entry)) {
16747
16984
  const openDelim = typeof entry.openTrigger === "string" ? DEFAULT_DELIMITER[entry.openTrigger] : tokensToString(entry.openTrigger);
16748
16985
  const closeDelim = typeof entry.closeTrigger === "string" ? DEFAULT_DELIMITER[entry.closeTrigger] : tokensToString(entry.closeTrigger);
16749
- return (serializer, expr2) => joinLatex([
16750
- openDelim,
16751
- serializer.serialize(operand(expr2, 1)),
16752
- closeDelim
16753
- ]);
16986
+ return (serializer, expr2) => {
16987
+ const style = serializer.groupStyle(expr2, serializer.level + 1);
16988
+ const inner = serializer.serialize(operand(expr2, 1));
16989
+ if (style === "scaled")
16990
+ return joinLatex([`\\left${openDelim}`, inner, `\\right${closeDelim}`]);
16991
+ if (style === "big")
16992
+ return joinLatex([`\\Bigl${openDelim}`, inner, `\\Bigr${closeDelim}`]);
16993
+ return joinLatex([openDelim, inner, closeDelim]);
16994
+ };
16754
16995
  }
16755
16996
  let latex = entry.serialize;
16756
16997
  if (latex === void 0 && latexTrigger) latex = tokensToString(latexTrigger);
@@ -17635,6 +17876,16 @@ var ComputeEngineCore = (() => {
17635
17876
  }
17636
17877
 
17637
17878
  // src/compute-engine/latex-syntax/parse.ts
17879
+ var _symbolToUnicode = null;
17880
+ function getSymbolToUnicode() {
17881
+ if (!_symbolToUnicode) {
17882
+ _symbolToUnicode = /* @__PURE__ */ new Map();
17883
+ for (const [, latex, codepoint] of SYMBOLS2) {
17884
+ _symbolToUnicode.set(latex, String.fromCodePoint(codepoint));
17885
+ }
17886
+ }
17887
+ return _symbolToUnicode;
17888
+ }
17638
17889
  var DELIMITER_SHORTHAND2 = {
17639
17890
  "(": ["\\lparen", "("],
17640
17891
  ")": ["\\rparen", ")"],
@@ -18082,6 +18333,35 @@ var ComputeEngineCore = (() => {
18082
18333
  this.nextToken();
18083
18334
  this.skipVisualSpace();
18084
18335
  }
18336
+ if (this.match("\\hspace")) {
18337
+ this.match("*");
18338
+ this.parseStringGroup();
18339
+ this.skipVisualSpace();
18340
+ }
18341
+ if (this.match("\\hskip") || this.match("\\kern")) {
18342
+ this.skipSpace();
18343
+ if (!this.match("-")) this.match("+");
18344
+ while (/^[\d.]$/.test(this.peek)) this.nextToken();
18345
+ for (const unit of [
18346
+ "pt",
18347
+ "em",
18348
+ "mu",
18349
+ "ex",
18350
+ "mm",
18351
+ "cm",
18352
+ "in",
18353
+ "bp",
18354
+ "sp",
18355
+ "dd",
18356
+ "cc",
18357
+ "pc",
18358
+ "nc",
18359
+ "nd"
18360
+ ]) {
18361
+ if (this.matchAll([...unit])) break;
18362
+ }
18363
+ this.skipVisualSpace();
18364
+ }
18085
18365
  this.skipSpace();
18086
18366
  }
18087
18367
  match(token) {
@@ -18419,7 +18699,8 @@ var ComputeEngineCore = (() => {
18419
18699
  } else if (token === "<space>") {
18420
18700
  result += " ";
18421
18701
  } else if (token[0] === "\\") {
18422
- result += token;
18702
+ const unicode = getSymbolToUnicode().get(token);
18703
+ result += unicode ?? token;
18423
18704
  } else {
18424
18705
  result += token;
18425
18706
  }
@@ -18573,6 +18854,19 @@ var ComputeEngineCore = (() => {
18573
18854
  }
18574
18855
  for (const def of defs) {
18575
18856
  this.index = start;
18857
+ if (def.closeTokens.size > 0) {
18858
+ let found = false;
18859
+ const tokens = this._tokens;
18860
+ for (let i = start; i < tokens.length; i++) {
18861
+ if (def.closeTokens.has(tokens[i])) {
18862
+ found = true;
18863
+ break;
18864
+ }
18865
+ }
18866
+ if (!found) continue;
18867
+ }
18868
+ if (typeof def.openTrigger === "string" && def.openTrigger === "." && !OPEN_DELIMITER_PREFIX[currentToken])
18869
+ continue;
18576
18870
  const matched = this.matchDelimiter(def.openTrigger, def.closeTrigger);
18577
18871
  if (!matched) continue;
18578
18872
  const bodyStart = this.index;
@@ -19779,7 +20073,7 @@ var ComputeEngineCore = (() => {
19779
20073
  sansserif: (s) => `\\mathsf{${s}}`,
19780
20074
  monospace: (s) => `\\mathtt{${s}}`
19781
20075
  };
19782
- var Serializer4 = class {
20076
+ var Serializer5 = class {
19783
20077
  options;
19784
20078
  dictionary;
19785
20079
  level = -1;
@@ -19794,11 +20088,18 @@ var ComputeEngineCore = (() => {
19794
20088
  /**
19795
20089
  * Serialize the expression, and if the expression is an operator
19796
20090
  * of precedence less than or equal to prec, wrap it in some parens.
19797
- * @todo: don't wrap Abs, Floor, Ceil, Delimiter
20091
+ *
20092
+ * Skip wrapping for matchfix operators (Abs, Floor, Ceil, Norm, etc.)
20093
+ * and Delimiter since they already have visible delimiters.
19798
20094
  */
19799
20095
  wrap(expr2, prec) {
19800
20096
  if (expr2 === null || expr2 === void 0) return "";
19801
20097
  if (prec === void 0) {
20098
+ const name2 = operator(expr2);
20099
+ if (name2) {
20100
+ const def = this.dictionary.ids.get(name2);
20101
+ if (def?.kind === "matchfix") return this.serialize(expr2);
20102
+ }
19802
20103
  return this.wrapString(
19803
20104
  this.serialize(expr2),
19804
20105
  this.options.groupStyle(expr2, this.level + 1)
@@ -20092,7 +20393,7 @@ var ComputeEngineCore = (() => {
20092
20393
  return body;
20093
20394
  }
20094
20395
  function serializeLatex(expr2, dict, options) {
20095
- const serializer = new Serializer4(dict, options);
20396
+ const serializer = new Serializer5(dict, options);
20096
20397
  return serializer.serialize(expr2);
20097
20398
  }
20098
20399
 
@@ -32128,6 +32429,29 @@ ${lines.join("\n")}`;
32128
32429
  signature: "(value*) -> number | list",
32129
32430
  evaluate: (xs, { engine }) => evaluateMinMax(engine, xs, "Infimum")
32130
32431
  },
32432
+ Distance: {
32433
+ description: "Euclidean distance between two points (tuples of numbers).",
32434
+ complexity: 6e3,
32435
+ signature: "(tuple, tuple) -> number",
32436
+ evaluate: ([a, b], { engine: ce }) => {
32437
+ if (!isFunction2(a) || !isFunction2(b))
32438
+ return ce.error("incompatible-type");
32439
+ if (a.operator !== "Tuple" || b.operator !== "Tuple")
32440
+ return ce.error("incompatible-type");
32441
+ if (a.ops.length !== b.ops.length || a.ops.length === 0)
32442
+ return ce.error("incompatible-type");
32443
+ let sumSq = 0;
32444
+ for (let i = 0; i < a.ops.length; i++) {
32445
+ const ai = a.ops[i].re;
32446
+ const bi = b.ops[i].re;
32447
+ if (!Number.isFinite(ai) || !Number.isFinite(bi))
32448
+ return ce.error("expected-value");
32449
+ const d = ai - bi;
32450
+ sumSq += d * d;
32451
+ }
32452
+ return ce.number(Math.sqrt(sumSq));
32453
+ }
32454
+ },
32131
32455
  Product: {
32132
32456
  description: "`Product(f, a, b)` computes the product of `f` from `a` to `b`",
32133
32457
  wikidata: "Q901718",
@@ -32660,16 +32984,11 @@ ${lines.join("\n")}`;
32660
32984
  );
32661
32985
  let condFn;
32662
32986
  if (typeof condition === "string") {
32663
- const latex = asLatexString(condition);
32664
- if (latex) {
32665
- const condPattern = ce.parse(latex, {
32666
- form: options?.canonical ? "canonical" : "raw"
32667
- }) ?? ce.expr("Nothing");
32668
- condFn = (x, _ce) => {
32669
- const evaluated = condPattern.subs(x).evaluate();
32670
- return isSymbol2(evaluated, "True");
32671
- };
32672
- }
32987
+ const condPattern = ce.parse(condition) ?? ce.expr("Nothing");
32988
+ condFn = (x, _ce) => {
32989
+ const evaluated = condPattern.subs(x).evaluate();
32990
+ return isSymbol2(evaluated, "True");
32991
+ };
32673
32992
  } else {
32674
32993
  if (condition !== void 0 && typeof condition !== "function")
32675
32994
  throw new Error(
@@ -32771,6 +33090,15 @@ ${e.message}
32771
33090
  function applyRule(rule, expr2, substitution, options) {
32772
33091
  if (!rule) return null;
32773
33092
  let canonical2 = options?.canonical ?? (expr2.isCanonical || expr2.isStructural);
33093
+ let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
33094
+ const because = id ?? "";
33095
+ const ce = expr2.engine;
33096
+ if (canonical2 && match2) {
33097
+ const awc = getWildcards(match2);
33098
+ const canonicalMatch = match2.canonical;
33099
+ const bwc = getWildcards(canonicalMatch);
33100
+ if (!awc.every((x) => bwc.includes(x))) return null;
33101
+ }
32774
33102
  let operandsMatched = false;
32775
33103
  if (isFunction2(expr2) && options?.recursive) {
32776
33104
  const newOps = expr2.ops.map((op) => {
@@ -32782,20 +33110,11 @@ ${e.message}
32782
33110
  if (operandsMatched) {
32783
33111
  if (!canonical2 && options?.canonical === void 0 && newOps.every((x) => x.isCanonical))
32784
33112
  canonical2 = true;
32785
- expr2 = expr2.engine.function(expr2.operator, newOps, {
33113
+ expr2 = ce.function(expr2.operator, newOps, {
32786
33114
  form: canonical2 ? "canonical" : "raw"
32787
33115
  });
32788
33116
  }
32789
33117
  }
32790
- let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
32791
- const because = id ?? "";
32792
- if (canonical2 && match2) {
32793
- const awc = getWildcards(match2);
32794
- const canonicalMatch = match2.canonical;
32795
- const bwc = getWildcards(canonicalMatch);
32796
- if (!awc.every((x) => bwc.includes(x)))
32797
- return operandsMatched ? { value: expr2, because } : null;
32798
- }
32799
33118
  const useVariations = rule.useVariations ?? options?.useVariations ?? false;
32800
33119
  const matchPermutations = options?.matchPermutations ?? true;
32801
33120
  onBeforeMatch?.(rule, expr2);
@@ -32814,7 +33133,7 @@ ${e.message}
32814
33133
  ...sub2
32815
33134
  };
32816
33135
  try {
32817
- if (!condition(conditionSub, expr2.engine))
33136
+ if (!condition(conditionSub, ce))
32818
33137
  return operandsMatched ? { value: expr2, because } : null;
32819
33138
  } catch (e) {
32820
33139
  console.error(
@@ -32829,7 +33148,8 @@ ${e.message}
32829
33148
  if (!canonical2 && options?.canonical === void 0 && replace2 instanceof _BoxedExpression && replace2.isCanonical)
32830
33149
  canonical2 = true;
32831
33150
  const result = typeof replace2 === "function" ? replace2(expr2, sub2) : replace2.subs(sub2, { canonical: canonical2 });
32832
- if (!result) return null;
33151
+ if (!result)
33152
+ return operandsMatched ? { value: canonical2 ? expr2.canonical : expr2, because } : null;
32833
33153
  onMatch?.(rule, expr2, result);
32834
33154
  if (isRuleStep(result))
32835
33155
  return canonical2 ? { ...result, value: result.value.canonical } : result;
@@ -38403,6 +38723,40 @@ ${e.message}
38403
38723
  else h = ((r - g) / d + 4) / 6;
38404
38724
  return { h: h * 360, s, l };
38405
38725
  }
38726
+ function hsvToRgb(h, s, v) {
38727
+ h = (h % 360 + 360) % 360;
38728
+ s = Math.max(0, Math.min(1, s));
38729
+ v = Math.max(0, Math.min(1, v));
38730
+ const c = v * s;
38731
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
38732
+ const m = v - c;
38733
+ let r = 0, g = 0, b = 0;
38734
+ if (h < 60) [r, g, b] = [c, x, 0];
38735
+ else if (h < 120) [r, g, b] = [x, c, 0];
38736
+ else if (h < 180) [r, g, b] = [0, c, x];
38737
+ else if (h < 240) [r, g, b] = [0, x, c];
38738
+ else if (h < 300) [r, g, b] = [x, 0, c];
38739
+ else [r, g, b] = [c, 0, x];
38740
+ return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };
38741
+ }
38742
+ function rgbToHsv(r, g, b) {
38743
+ r /= 255;
38744
+ g /= 255;
38745
+ b /= 255;
38746
+ const max2 = Math.max(r, g, b);
38747
+ const min2 = Math.min(r, g, b);
38748
+ const d = max2 - min2;
38749
+ let h = 0;
38750
+ if (d > 0) {
38751
+ if (max2 === r) h = (g - b) / d % 6;
38752
+ else if (max2 === g) h = (b - r) / d + 2;
38753
+ else h = (r - g) / d + 4;
38754
+ h *= 60;
38755
+ if (h < 0) h += 360;
38756
+ }
38757
+ const s = max2 === 0 ? 0 : d / max2;
38758
+ return { h, s, v: max2 };
38759
+ }
38406
38760
  function parseHexColor(s) {
38407
38761
  const hex = s.startsWith("#") ? s.substring(1) : s;
38408
38762
  let r, g, b;
@@ -38427,6 +38781,12 @@ ${e.message}
38427
38781
  if (alpha !== void 0) result.alpha = alpha;
38428
38782
  return result;
38429
38783
  }
38784
+ function asOklch(color) {
38785
+ if (typeof color === "string") return rgbToOklch(parseHexColor(color));
38786
+ if ("C" in color) return color;
38787
+ if ("a" in color && "b" in color) return oklabToOklch(color);
38788
+ return rgbToOklch(color);
38789
+ }
38430
38790
  function asRgb(color) {
38431
38791
  if (typeof color === "number") {
38432
38792
  return {
@@ -38858,6 +39218,13 @@ ${e.message}
38858
39218
  };
38859
39219
  function parseColor(s, darkMode) {
38860
39220
  const str = s.trim().toLowerCase();
39221
+ const opacityMatch = str.match(/^(.+?)\s*\/\s*(\d+(?:\.\d+)?)%?\s*$/);
39222
+ if (opacityMatch) {
39223
+ const base = parseColor(opacityMatch[1].trim(), darkMode);
39224
+ const opacity = Math.max(0, Math.min(100, parseFloat(opacityMatch[2])));
39225
+ const alpha = Math.round(opacity / 100 * 255);
39226
+ return base & 4294967040 | alpha;
39227
+ }
38861
39228
  if (str.startsWith("#")) {
38862
39229
  const hex = str.substring(1);
38863
39230
  let r, g, b, a = 255;
@@ -38990,14 +39357,6 @@ ${e.message}
38990
39357
  console.warn(`parseColor: unrecognized color "${s}"`);
38991
39358
  return 0;
38992
39359
  }
38993
- function parseColorToRgb01(s, darkMode) {
38994
- const color = parseColor(s, darkMode);
38995
- return [
38996
- (color >>> 24 & 255) / 255,
38997
- (color >>> 16 & 255) / 255,
38998
- (color >>> 8 & 255) / 255
38999
- ];
39000
- }
39001
39360
  function apca(bgColor, fgColor) {
39002
39361
  const bgRgb = asRgb(bgColor);
39003
39362
  const fgRgb = asRgb(fgColor);
@@ -39056,6 +39415,12 @@ ${e.message}
39056
39415
  const contrast2 = Math.abs(apca(fg2, bg));
39057
39416
  return contrast1 >= contrast2 ? asColorNumber(fg1) : asColorNumber(fg2);
39058
39417
  }
39418
+ function oklabDeltaE(a, b) {
39419
+ const dL = a.L - b.L;
39420
+ const da = a.a - b.a;
39421
+ const db = a.b - b.b;
39422
+ return Math.sqrt(dL * dL + da * da + db * db);
39423
+ }
39059
39424
  function lerpOklch(c1, c2, f) {
39060
39425
  const L = c1.L + (c2.L - c1.L) * f;
39061
39426
  const C = c1.C + (c2.C - c1.C) * f;
@@ -41817,14 +42182,30 @@ ${e.message}
41817
42182
  };
41818
42183
 
41819
42184
  // src/compute-engine/library/colors.ts
41820
- function colorNumberToTuple(ce, color) {
41821
- const r = (color >>> 24 & 255) / 255;
41822
- const g = (color >>> 16 & 255) / 255;
41823
- const b = (color >>> 8 & 255) / 255;
41824
- const a = (color & 255) / 255;
41825
- if (Math.abs(a - 1) < 1e-4)
41826
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
41827
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b), ce.number(a));
42185
+ function normalizeAlpha(a) {
42186
+ if (a === void 0) return void 0;
42187
+ if (!Number.isFinite(a)) return void 0;
42188
+ if (Math.abs(a - 1) < 1e-9) return void 0;
42189
+ return a;
42190
+ }
42191
+ function normalizeColorHead(ce, expr2) {
42192
+ if (!isFunction2(expr2) || !expr2.ops || expr2.ops.length < 4) return expr2;
42193
+ const alphaExpr = expr2.ops[3];
42194
+ if (!isNumber(alphaExpr)) return expr2;
42195
+ if (normalizeAlpha(alphaExpr.re) === void 0) {
42196
+ return ce.function(expr2.operator, [expr2.ops[0], expr2.ops[1], expr2.ops[2]]);
42197
+ }
42198
+ return expr2;
42199
+ }
42200
+ function colorNumberToOklch(ce, color) {
42201
+ const r = color >>> 24 & 255;
42202
+ const g = color >>> 16 & 255;
42203
+ const b = color >>> 8 & 255;
42204
+ const a = normalizeAlpha((color & 255) / 255);
42205
+ const c = rgbToOklch({ r, g, b });
42206
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42207
+ if (a !== void 0) args.push(ce.number(a));
42208
+ return ce.function("Oklch", args);
41828
42209
  }
41829
42210
  var ALL_PALETTES = {
41830
42211
  ...SEQUENTIAL_PALETTES,
@@ -41835,45 +42216,139 @@ ${e.message}
41835
42216
  t = Math.max(0, Math.min(1, t));
41836
42217
  const n = palette.length;
41837
42218
  if (n === 0) return ce.error("expected-value");
41838
- if (n === 1) return colorNumberToTuple(ce, parseColor(palette[0]));
42219
+ if (n === 1) return colorNumberToOklch(ce, parseColor(palette[0]));
41839
42220
  const pos = t * (n - 1);
41840
42221
  const i = Math.floor(pos);
41841
42222
  const frac = pos - i;
41842
- if (i >= n - 1) return colorNumberToTuple(ce, parseColor(palette[n - 1]));
41843
- if (frac < 1e-9) return colorNumberToTuple(ce, parseColor(palette[i]));
41844
- const rgb = interpolateOklch(palette[i], palette[i + 1], frac);
41845
- const r = rgb.r / 255;
41846
- const g = rgb.g / 255;
41847
- const b = rgb.b / 255;
41848
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42223
+ if (i >= n - 1) return colorNumberToOklch(ce, parseColor(palette[n - 1]));
42224
+ if (frac < 1e-9) return colorNumberToOklch(ce, parseColor(palette[i]));
42225
+ return oklchToExpr(
42226
+ ce,
42227
+ asOklch(interpolateOklch(palette[i], palette[i + 1], frac))
42228
+ );
42229
+ }
42230
+ var COLOR_OPERATORS = /* @__PURE__ */ new Set(["Rgb", "Hsv", "Hsl", "Oklab", "Oklch"]);
42231
+ function readColorExpr(arg) {
42232
+ if (!isFunction2(arg)) return null;
42233
+ if (!COLOR_OPERATORS.has(arg.operator)) return null;
42234
+ if (!arg.ops || arg.ops.length < 3) return null;
42235
+ const c0 = arg.ops[0].re;
42236
+ const c1 = arg.ops[1].re;
42237
+ const c2 = arg.ops[2].re;
42238
+ if (!Number.isFinite(c0) || !Number.isFinite(c1) || !Number.isFinite(c2))
42239
+ return null;
42240
+ const alpha = arg.ops.length >= 4 ? normalizeAlpha(arg.ops[3].re) : void 0;
42241
+ return { space: arg.operator, c0, c1, c2, alpha };
42242
+ }
42243
+ function colorExprToRgb(arg) {
42244
+ const c = readColorExpr(arg);
42245
+ if (!c) return null;
42246
+ const withAlpha = (rgb) => c.alpha !== void 0 ? { ...rgb, alpha: c.alpha } : rgb;
42247
+ switch (c.space) {
42248
+ case "Rgb":
42249
+ return withAlpha({ r: c.c0 * 255, g: c.c1 * 255, b: c.c2 * 255 });
42250
+ case "Hsv":
42251
+ return withAlpha(hsvToRgb(c.c0, c.c1, c.c2));
42252
+ case "Hsl":
42253
+ return withAlpha(hslToRgb(c.c0, c.c1, c.c2));
42254
+ case "Oklab":
42255
+ return withAlpha(oklabToRgb({ L: c.c0, a: c.c1, b: c.c2 }));
42256
+ case "Oklch":
42257
+ return withAlpha(oklchToRgb({ L: c.c0, C: c.c1, H: c.c2 }));
42258
+ }
42259
+ return null;
42260
+ }
42261
+ function colorExprToOklch(arg) {
42262
+ const c = readColorExpr(arg);
42263
+ if (!c) return null;
42264
+ switch (c.space) {
42265
+ case "Oklch":
42266
+ return asOklch({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42267
+ case "Oklab":
42268
+ return asOklch({ L: c.c0, a: c.c1, b: c.c2, alpha: c.alpha });
42269
+ case "Rgb":
42270
+ return asOklch({
42271
+ r: c.c0 * 255,
42272
+ g: c.c1 * 255,
42273
+ b: c.c2 * 255,
42274
+ alpha: c.alpha
42275
+ });
42276
+ case "Hsv": {
42277
+ const rgb = hsvToRgb(c.c0, c.c1, c.c2);
42278
+ return asOklch({ ...rgb, alpha: c.alpha });
42279
+ }
42280
+ case "Hsl": {
42281
+ const rgb = hslToRgb(c.c0, c.c1, c.c2);
42282
+ return asOklch({ r: rgb.r, g: rgb.g, b: rgb.b, alpha: c.alpha });
42283
+ }
42284
+ }
42285
+ return null;
42286
+ }
42287
+ function toOklch(ce, arg) {
42288
+ const direct = colorExprToOklch(arg);
42289
+ if (direct) return direct;
42290
+ const rgb = extractRgb(ce, arg);
42291
+ return rgb ? asOklch(rgb) : null;
42292
+ }
42293
+ function lerpOklchColor(a, b, t) {
42294
+ const L = a.L + (b.L - a.L) * t;
42295
+ const C = a.C + (b.C - a.C) * t;
42296
+ const aAchromatic = a.C < 1e-6;
42297
+ const bAchromatic = b.C < 1e-6;
42298
+ let H;
42299
+ if (aAchromatic && bAchromatic) H = a.H;
42300
+ else if (aAchromatic) H = b.H;
42301
+ else if (bAchromatic) H = a.H;
42302
+ else {
42303
+ let dH = b.H - a.H;
42304
+ if (dH > 180) dH -= 360;
42305
+ if (dH < -180) dH += 360;
42306
+ H = a.H + dH * t;
42307
+ if (H < 0) H += 360;
42308
+ if (H >= 360) H -= 360;
42309
+ }
42310
+ const alphaA = a.alpha ?? 1;
42311
+ const alphaB = b.alpha ?? 1;
42312
+ return { L, C, H, alpha: normalizeAlpha(alphaA + (alphaB - alphaA) * t) };
42313
+ }
42314
+ function oklchToExpr(ce, c) {
42315
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42316
+ if (c.alpha !== void 0) args.push(ce.number(c.alpha));
42317
+ return ce.function("Oklch", args);
41849
42318
  }
41850
42319
  function extractRgb(ce, arg) {
41851
42320
  if (isString(arg)) {
41852
42321
  const s = arg.string;
41853
42322
  if (!s) return void 0;
41854
42323
  const color = parseColor(s);
41855
- return {
42324
+ const rgb = {
41856
42325
  r: color >>> 24 & 255,
41857
42326
  g: color >>> 16 & 255,
41858
- b: color >>> 8 & 255,
41859
- alpha: (color & 255) / 255
42327
+ b: color >>> 8 & 255
41860
42328
  };
42329
+ const alpha = normalizeAlpha((color & 255) / 255);
42330
+ if (alpha !== void 0) rgb.alpha = alpha;
42331
+ return rgb;
41861
42332
  }
42333
+ const fromTyped = colorExprToRgb(arg);
42334
+ if (fromTyped) return fromTyped;
41862
42335
  if (arg.operator === "Tuple" && arg.ops && arg.ops.length >= 3) {
41863
42336
  const rgb = {
41864
42337
  r: arg.ops[0].re * 255,
41865
42338
  g: arg.ops[1].re * 255,
41866
42339
  b: arg.ops[2].re * 255
41867
42340
  };
41868
- if (arg.ops.length >= 4) rgb.alpha = arg.ops[3].re;
42341
+ if (arg.ops.length >= 4) {
42342
+ const alpha = normalizeAlpha(arg.ops[3].re);
42343
+ if (alpha !== void 0) rgb.alpha = alpha;
42344
+ }
41869
42345
  return rgb;
41870
42346
  }
41871
42347
  return void 0;
41872
42348
  }
41873
42349
  function componentsTuple(ce, components, alpha) {
41874
42350
  const args = components.map((v) => ce.number(v));
41875
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4)
41876
- args.push(ce.number(alpha));
42351
+ if (alpha !== void 0) args.push(ce.number(alpha));
41877
42352
  return ce.tuple(...args);
41878
42353
  }
41879
42354
  function rgbToHex(rgb) {
@@ -41881,7 +42356,7 @@ ${e.message}
41881
42356
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
41882
42357
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
41883
42358
  const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
41884
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
42359
+ if (rgb.alpha !== void 0) {
41885
42360
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
41886
42361
  return hex + a.toString(16).padStart(2, "0");
41887
42362
  }
@@ -41889,22 +42364,29 @@ ${e.message}
41889
42364
  }
41890
42365
  var COLORS_LIBRARY = {
41891
42366
  Color: {
41892
- description: "Convert a color string to a canonical sRGB tuple",
42367
+ description: "Parse a CSS-style color string to an Oklch color",
41893
42368
  complexity: 8e3,
41894
- signature: "(string) -> tuple",
42369
+ signature: "(string) -> color",
41895
42370
  evaluate: (ops, { engine: ce }) => {
41896
42371
  const input = isString(ops[0]) ? ops[0].string : void 0;
41897
42372
  if (!input) return ce.error("incompatible-type");
41898
42373
  const color = parseColor(input);
41899
42374
  if (color === 0 && input.trim().toLowerCase() !== "transparent")
41900
42375
  return ce.error("incompatible-type");
41901
- return colorNumberToTuple(ce, color);
42376
+ const r = color >>> 24 & 255;
42377
+ const g = color >>> 16 & 255;
42378
+ const b = color >>> 8 & 255;
42379
+ const a = normalizeAlpha((color & 255) / 255);
42380
+ const c = rgbToOklch({ r, g, b });
42381
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42382
+ if (a !== void 0) args.push(ce.number(a));
42383
+ return ce.function("Oklch", args);
41902
42384
  }
41903
42385
  },
41904
42386
  ColorToString: {
41905
42387
  description: "Convert a color to a string in the specified format",
41906
42388
  complexity: 8e3,
41907
- signature: "(any, string?) -> string",
42389
+ signature: "(color | string | tuple, string?) -> string",
41908
42390
  evaluate: (ops, { engine: ce }) => {
41909
42391
  const rgb = extractRgb(ce, ops[0]);
41910
42392
  if (!rgb) return ce.error("incompatible-type");
@@ -41916,7 +42398,7 @@ ${e.message}
41916
42398
  const r = Math.round(rgb.r);
41917
42399
  const g = Math.round(rgb.g);
41918
42400
  const b = Math.round(rgb.b);
41919
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42401
+ if (rgb.alpha !== void 0)
41920
42402
  return ce.string(`rgb(${r} ${g} ${b} / ${rgb.alpha})`);
41921
42403
  return ce.string(`rgb(${r} ${g} ${b})`);
41922
42404
  }
@@ -41925,17 +42407,17 @@ ${e.message}
41925
42407
  const h = Math.round(hsl.h * 10) / 10;
41926
42408
  const s = Math.round(hsl.s * 1e3) / 10;
41927
42409
  const l = Math.round(hsl.l * 1e3) / 10;
41928
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42410
+ if (rgb.alpha !== void 0)
41929
42411
  return ce.string(`hsl(${h} ${s}% ${l}% / ${rgb.alpha})`);
41930
42412
  return ce.string(`hsl(${h} ${s}% ${l}%)`);
41931
42413
  }
41932
42414
  case "oklch": {
41933
- const c = rgbToOklch(rgb);
42415
+ const c = colorExprToOklch(ops[0]) ?? asOklch(rgb);
41934
42416
  const L = Math.round(c.L * 1e3) / 1e3;
41935
42417
  const C = Math.round(c.C * 1e3) / 1e3;
41936
42418
  const H = Math.round(c.H * 10) / 10;
41937
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
41938
- return ce.string(`oklch(${L} ${C} ${H} / ${rgb.alpha})`);
42419
+ if (c.alpha !== void 0)
42420
+ return ce.string(`oklch(${L} ${C} ${H} / ${c.alpha})`);
41939
42421
  return ce.string(`oklch(${L} ${C} ${H})`);
41940
42422
  }
41941
42423
  default:
@@ -41946,60 +42428,44 @@ ${e.message}
41946
42428
  ColorMix: {
41947
42429
  description: "Mix two colors in OKLCh space",
41948
42430
  complexity: 8e3,
41949
- signature: "(any, any, number?) -> tuple",
42431
+ signature: "(color | string | tuple, color | string | tuple, number?) -> color",
41950
42432
  evaluate: (ops, { engine: ce }) => {
41951
- const rgb1 = extractRgb(ce, ops[0]);
41952
- const rgb2 = extractRgb(ce, ops[1]);
41953
- if (!rgb1 || !rgb2) return ce.error("incompatible-type");
41954
42433
  let ratio = 0.5;
41955
42434
  if (ops.length >= 3 && ops[2] !== void 0) {
41956
42435
  ratio = ops[2].re;
41957
42436
  if (!Number.isFinite(ratio)) return ce.error("expected-value");
41958
42437
  ratio = Math.max(0, Math.min(1, ratio));
41959
42438
  }
41960
- const c1 = rgbToOklch(rgb1);
41961
- const c2 = rgbToOklch(rgb2);
41962
- const mixed = oklchToRgb(lerpOklch(c1, c2, ratio));
41963
- const r = mixed.r / 255;
41964
- const g = mixed.g / 255;
41965
- const b = mixed.b / 255;
41966
- const a1 = rgb1.alpha ?? 1;
41967
- const a2 = rgb2.alpha ?? 1;
41968
- const alpha = a1 + (a2 - a1) * ratio;
41969
- if (Math.abs(alpha - 1) > 1e-4)
41970
- return ce.tuple(
41971
- ce.number(r),
41972
- ce.number(g),
41973
- ce.number(b),
41974
- ce.number(alpha)
41975
- );
41976
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42439
+ const oklch1 = toOklch(ce, ops[0]);
42440
+ const oklch2 = toOklch(ce, ops[1]);
42441
+ if (!oklch1 || !oklch2) return ce.error("incompatible-type");
42442
+ return oklchToExpr(ce, lerpOklchColor(oklch1, oklch2, ratio));
41977
42443
  }
41978
42444
  },
41979
42445
  Colormap: {
41980
42446
  description: "Sample colors from a named palette",
41981
42447
  complexity: 8e3,
41982
- signature: "(string, number?) -> any",
42448
+ signature: "(string, number?) -> color | list<color>",
41983
42449
  evaluate: (ops, { engine: ce }) => {
41984
42450
  const name = isString(ops[0]) ? ops[0].string : void 0;
41985
42451
  if (!name) return ce.error("incompatible-type");
41986
42452
  const palette = ALL_PALETTES[name];
41987
42453
  if (!palette) return ce.error("expected-value", name);
41988
42454
  if (ops.length < 2 || ops[1] === void 0) {
41989
- const tuples = palette.map(
41990
- (hex) => colorNumberToTuple(ce, parseColor(hex))
42455
+ const colors = palette.map(
42456
+ (hex) => colorNumberToOklch(ce, parseColor(hex))
41991
42457
  );
41992
- return ce.function("List", tuples);
42458
+ return ce.function("List", colors);
41993
42459
  }
41994
42460
  const val = ops[1].re;
41995
42461
  if (!Number.isFinite(val)) return ce.error("expected-value");
41996
42462
  if (Number.isInteger(val) && val >= 2) {
41997
42463
  const n = val;
41998
- const tuples = [];
42464
+ const colors = [];
41999
42465
  for (let i = 0; i < n; i++) {
42000
- tuples.push(samplePalette(ce, palette, i / (n - 1)));
42466
+ colors.push(samplePalette(ce, palette, i / (n - 1)));
42001
42467
  }
42002
- return ce.function("List", tuples);
42468
+ return ce.function("List", colors);
42003
42469
  }
42004
42470
  return samplePalette(ce, palette, val);
42005
42471
  }
@@ -42007,12 +42473,25 @@ ${e.message}
42007
42473
  ColorToColorspace: {
42008
42474
  description: "Convert a color to components in a target color space",
42009
42475
  complexity: 8e3,
42010
- signature: "(any, string) -> tuple",
42476
+ signature: "(color | string | tuple, string) -> tuple",
42011
42477
  evaluate: (ops, { engine: ce }) => {
42012
- const rgb = extractRgb(ce, ops[0]);
42013
- if (!rgb) return ce.error("incompatible-type");
42014
42478
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42015
42479
  if (!space) return ce.error("incompatible-type");
42480
+ if (space === "oklch" || space === "oklab" || space === "lab") {
42481
+ const oklch2 = colorExprToOklch(ops[0]);
42482
+ if (oklch2) {
42483
+ if (space === "oklch")
42484
+ return componentsTuple(
42485
+ ce,
42486
+ [oklch2.L, oklch2.C, oklch2.H],
42487
+ oklch2.alpha
42488
+ );
42489
+ const lab = oklchToOklab(oklch2);
42490
+ return componentsTuple(ce, [lab.L, lab.a, lab.b], lab.alpha);
42491
+ }
42492
+ }
42493
+ const rgb = extractRgb(ce, ops[0]);
42494
+ if (!rgb) return ce.error("incompatible-type");
42016
42495
  const alpha = rgb.alpha;
42017
42496
  switch (space) {
42018
42497
  case "rgb":
@@ -42042,17 +42521,27 @@ ${e.message}
42042
42521
  ColorFromColorspace: {
42043
42522
  description: "Convert color space components to a canonical sRGB tuple",
42044
42523
  complexity: 8e3,
42045
- signature: "(tuple, string) -> tuple",
42524
+ signature: "(color | tuple, string) -> tuple",
42046
42525
  evaluate: (ops, { engine: ce }) => {
42047
- const tuple = ops[0];
42048
- if (!isFunction2(tuple) || tuple.operator !== "Tuple" || tuple.ops.length < 3)
42049
- return ce.error("incompatible-type");
42050
- const c0 = tuple.ops[0].re;
42051
- const c1 = tuple.ops[1].re;
42052
- const c2 = tuple.ops[2].re;
42053
- const alpha = tuple.ops.length >= 4 ? tuple.ops[3].re : void 0;
42054
42526
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42055
42527
  if (!space) return ce.error("incompatible-type");
42528
+ let c0, c1, c2;
42529
+ let alpha;
42530
+ const arg = ops[0];
42531
+ const typed = readColorExpr(arg);
42532
+ if (typed) {
42533
+ c0 = typed.c0;
42534
+ c1 = typed.c1;
42535
+ c2 = typed.c2;
42536
+ alpha = typed.alpha;
42537
+ } else if (isFunction2(arg) && arg.operator === "Tuple" && arg.ops.length >= 3) {
42538
+ c0 = arg.ops[0].re;
42539
+ c1 = arg.ops[1].re;
42540
+ c2 = arg.ops[2].re;
42541
+ alpha = arg.ops.length >= 4 ? arg.ops[3].re : void 0;
42542
+ } else {
42543
+ return ce.error("incompatible-type");
42544
+ }
42056
42545
  let rgb;
42057
42546
  switch (space) {
42058
42547
  case "rgb":
@@ -42088,7 +42577,7 @@ ${e.message}
42088
42577
  ColorContrast: {
42089
42578
  description: "APCA contrast ratio between two colors",
42090
42579
  complexity: 8e3,
42091
- signature: "(any, any) -> number",
42580
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42092
42581
  evaluate: (ops, { engine: ce }) => {
42093
42582
  const bgRgb = extractRgb(ce, ops[0]);
42094
42583
  const fgRgb = extractRgb(ce, ops[1]);
@@ -42099,19 +42588,186 @@ ${e.message}
42099
42588
  ContrastingColor: {
42100
42589
  description: "Choose the foreground color with better APCA contrast against a background",
42101
42590
  complexity: 8e3,
42102
- signature: "(any, any?, any?) -> tuple",
42591
+ signature: "(color | string | tuple, (color | string | tuple)?, (color | string | tuple)?) -> color",
42103
42592
  evaluate: (ops, { engine: ce }) => {
42104
42593
  const bgRgb = extractRgb(ce, ops[0]);
42105
42594
  if (!bgRgb) return ce.error("incompatible-type");
42595
+ let packed;
42106
42596
  if (ops.length >= 3 && ops[1] !== void 0 && ops[2] !== void 0) {
42107
42597
  const fg1 = extractRgb(ce, ops[1]);
42108
42598
  const fg2 = extractRgb(ce, ops[2]);
42109
42599
  if (!fg1 || !fg2) return ce.error("incompatible-type");
42110
- const result2 = contrastingColor({ bg: bgRgb, fg1, fg2 });
42111
- return colorNumberToTuple(ce, result2);
42600
+ packed = contrastingColor({ bg: bgRgb, fg1, fg2 });
42601
+ } else {
42602
+ packed = contrastingColor(bgRgb);
42603
+ }
42604
+ const r = (packed >>> 24 & 255) / 255;
42605
+ const g = (packed >>> 16 & 255) / 255;
42606
+ const b = (packed >>> 8 & 255) / 255;
42607
+ const alpha = normalizeAlpha((packed & 255) / 255);
42608
+ const args = [ce.number(r), ce.number(g), ce.number(b)];
42609
+ if (alpha !== void 0) args.push(ce.number(alpha));
42610
+ return ce.function("Rgb", args);
42611
+ }
42612
+ },
42613
+ // ---------------------------------------------------------------------------
42614
+ // Color constructors. Each preserves its colorspace on evaluation; the
42615
+ // operator name is the discriminator. Components are interpreted per
42616
+ // colorspace conventions (Rgb channels 0-1, Hsv/Hsl hue in degrees with
42617
+ // sat/value 0-1, Oklab/Oklch L 0-1 with standard a/b/C/H ranges). The
42618
+ // optional 4th argument is alpha in [0, 1]. No clamping at evaluation time.
42619
+ // ---------------------------------------------------------------------------
42620
+ Rgb: {
42621
+ description: "sRGB color (channels 0-1, optional alpha 0-1)",
42622
+ complexity: 8e3,
42623
+ signature: "(number, number, number, number?) -> color"
42624
+ },
42625
+ Hsv: {
42626
+ description: "HSV color (hue degrees, saturation/value 0-1, optional alpha)",
42627
+ complexity: 8e3,
42628
+ signature: "(number, number, number, number?) -> color"
42629
+ },
42630
+ Hsl: {
42631
+ description: "HSL color (hue degrees, saturation/lightness 0-1, optional alpha)",
42632
+ complexity: 8e3,
42633
+ signature: "(number, number, number, number?) -> color"
42634
+ },
42635
+ Oklab: {
42636
+ description: "OKLab color (L 0-1, a/b ~ -0.4..0.4, optional alpha)",
42637
+ complexity: 8e3,
42638
+ signature: "(number, number, number, number?) -> color"
42639
+ },
42640
+ Oklch: {
42641
+ description: "OKLCh color (L 0-1, C 0-~0.4, hue degrees, optional alpha)",
42642
+ complexity: 8e3,
42643
+ signature: "(number, number, number, number?) -> color"
42644
+ },
42645
+ // ---------------------------------------------------------------------------
42646
+ // Color-space conversions. Each accepts any of the five color heads and
42647
+ // returns the same color in the named space. If the input is already in
42648
+ // the target space, returns the input unchanged.
42649
+ // ---------------------------------------------------------------------------
42650
+ AsRgb: {
42651
+ description: "Convert any color to sRGB (channels 0-1)",
42652
+ complexity: 8e3,
42653
+ signature: "(color) -> color",
42654
+ evaluate: (ops, { engine: ce }) => {
42655
+ const arg = ops[0];
42656
+ if (isFunction2(arg) && arg.operator === "Rgb")
42657
+ return normalizeColorHead(ce, arg);
42658
+ const rgb = colorExprToRgb(arg);
42659
+ if (!rgb) return ce.error("incompatible-type");
42660
+ const args = [
42661
+ ce.number(rgb.r / 255),
42662
+ ce.number(rgb.g / 255),
42663
+ ce.number(rgb.b / 255)
42664
+ ];
42665
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42666
+ return ce.function("Rgb", args);
42667
+ }
42668
+ },
42669
+ AsHsv: {
42670
+ description: "Convert any color to HSV (hue degrees, s/v 0-1)",
42671
+ complexity: 8e3,
42672
+ signature: "(color) -> color",
42673
+ evaluate: (ops, { engine: ce }) => {
42674
+ const arg = ops[0];
42675
+ if (isFunction2(arg) && arg.operator === "Hsv")
42676
+ return normalizeColorHead(ce, arg);
42677
+ const rgb = colorExprToRgb(arg);
42678
+ if (!rgb) return ce.error("incompatible-type");
42679
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
42680
+ const args = [ce.number(hsv.h), ce.number(hsv.s), ce.number(hsv.v)];
42681
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42682
+ return ce.function("Hsv", args);
42683
+ }
42684
+ },
42685
+ AsHsl: {
42686
+ description: "Convert any color to HSL (hue degrees, s/l 0-1)",
42687
+ complexity: 8e3,
42688
+ signature: "(color) -> color",
42689
+ evaluate: (ops, { engine: ce }) => {
42690
+ const arg = ops[0];
42691
+ if (isFunction2(arg) && arg.operator === "Hsl")
42692
+ return normalizeColorHead(ce, arg);
42693
+ const rgb = colorExprToRgb(arg);
42694
+ if (!rgb) return ce.error("incompatible-type");
42695
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
42696
+ const args = [ce.number(hsl.h), ce.number(hsl.s), ce.number(hsl.l)];
42697
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42698
+ return ce.function("Hsl", args);
42699
+ }
42700
+ },
42701
+ AsOklab: {
42702
+ description: "Convert any color to OKLab",
42703
+ complexity: 8e3,
42704
+ signature: "(color) -> color",
42705
+ evaluate: (ops, { engine: ce }) => {
42706
+ const arg = ops[0];
42707
+ if (isFunction2(arg) && arg.operator === "Oklab")
42708
+ return normalizeColorHead(ce, arg);
42709
+ if (isFunction2(arg) && arg.operator === "Oklch") {
42710
+ const c = readColorExpr(arg);
42711
+ if (!c) return ce.error("incompatible-type");
42712
+ const lab2 = oklchToOklab({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42713
+ const args2 = [ce.number(lab2.L), ce.number(lab2.a), ce.number(lab2.b)];
42714
+ if (lab2.alpha !== void 0) args2.push(ce.number(lab2.alpha));
42715
+ return ce.function("Oklab", args2);
42716
+ }
42717
+ const rgb = colorExprToRgb(arg);
42718
+ if (!rgb) return ce.error("incompatible-type");
42719
+ const lab = rgbToOklab(rgb);
42720
+ const args = [ce.number(lab.L), ce.number(lab.a), ce.number(lab.b)];
42721
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42722
+ return ce.function("Oklab", args);
42723
+ }
42724
+ },
42725
+ AsOklch: {
42726
+ description: "Convert any color to OKLCh",
42727
+ complexity: 8e3,
42728
+ signature: "(color) -> color",
42729
+ evaluate: (ops, { engine: ce }) => {
42730
+ const arg = ops[0];
42731
+ if (isFunction2(arg) && arg.operator === "Oklch")
42732
+ return normalizeColorHead(ce, arg);
42733
+ if (isFunction2(arg) && arg.operator === "Oklab") {
42734
+ const c2 = readColorExpr(arg);
42735
+ if (!c2) return ce.error("incompatible-type");
42736
+ const oklch2 = oklabToOklch({
42737
+ L: c2.c0,
42738
+ a: c2.c1,
42739
+ b: c2.c2,
42740
+ alpha: c2.alpha
42741
+ });
42742
+ const args2 = [
42743
+ ce.number(oklch2.L),
42744
+ ce.number(oklch2.C),
42745
+ ce.number(oklch2.H)
42746
+ ];
42747
+ if (oklch2.alpha !== void 0) args2.push(ce.number(oklch2.alpha));
42748
+ return ce.function("Oklch", args2);
42112
42749
  }
42113
- const result = contrastingColor(bgRgb);
42114
- return colorNumberToTuple(ce, result);
42750
+ const rgb = colorExprToRgb(arg);
42751
+ if (!rgb) return ce.error("incompatible-type");
42752
+ const c = rgbToOklch(rgb);
42753
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42754
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42755
+ return ce.function("Oklch", args);
42756
+ }
42757
+ },
42758
+ // ---------------------------------------------------------------------------
42759
+ // Perceptual difference. Returns ΔE_OK (Euclidean distance in OKLab),
42760
+ // an approximately perceptually uniform scalar.
42761
+ // ---------------------------------------------------------------------------
42762
+ ColorDelta: {
42763
+ description: "Perceptual color difference (\u0394E_OK) between two colors",
42764
+ complexity: 8e3,
42765
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42766
+ evaluate: (ops, { engine: ce }) => {
42767
+ const a = toOklch(ce, ops[0]);
42768
+ const b = toOklch(ce, ops[1]);
42769
+ if (!a || !b) return ce.error("incompatible-type");
42770
+ return ce.number(oklabDeltaE(oklchToOklab(a), oklchToOklab(b)));
42115
42771
  }
42116
42772
  }
42117
42773
  };
@@ -42131,12 +42787,14 @@ ${e.message}
42131
42787
  canonical: canonicalBlock,
42132
42788
  evaluate: evaluateBlock
42133
42789
  },
42134
- // A condition expression tests for one or more conditions of an expression
42135
- // ['Condition', value, "positive"]
42790
+ // A condition expression tests for one or more conditions of an expression.
42791
+ // Two forms:
42792
+ // ['Condition', value, "positive"] — tests value against named condition(s)
42793
+ // ['Condition', predicate] — set-builder predicate (e.g. x > 0)
42136
42794
  Condition: {
42137
42795
  description: "Test whether a value satisfies one or more conditions.",
42138
42796
  lazy: true,
42139
- signature: "(value, symbol) -> boolean",
42797
+ signature: "(expression, symbol?) -> boolean",
42140
42798
  evaluate: ([value, conds], { engine }) => {
42141
42799
  let conditions = [];
42142
42800
  if (isSymbol2(conds)) {
@@ -44589,6 +45247,34 @@ ${e.message}
44589
45247
  signature: "() -> expression",
44590
45248
  evaluate: (_ops, { engine }) => engine.expr(randomExpression())
44591
45249
  }
45250
+ },
45251
+ // ---------------------------------------------------------------------------
45252
+ // Opaque typed heads — registered so the names are in the standard set
45253
+ // (consumers can branch on the operator name); CE itself does not evaluate
45254
+ // them. Geometric primitives `Triangle`/`Sphere`/`Segment` and the action
45255
+ // arrow `To` (`a \to b`).
45256
+ // ---------------------------------------------------------------------------
45257
+ {
45258
+ Triangle: {
45259
+ description: "Triangle primitive \u2014 opaque typed head.",
45260
+ signature: "(any+) -> expression"
45261
+ },
45262
+ GeometricVector: {
45263
+ description: "Geometric vector (directed segment between two points) \u2014 opaque typed head. Distinct from the column-vector `Vector` operator.",
45264
+ signature: "(any, any) -> expression"
45265
+ },
45266
+ Sphere: {
45267
+ description: "Sphere primitive \u2014 opaque typed head.",
45268
+ signature: "(any+) -> expression"
45269
+ },
45270
+ Segment: {
45271
+ description: "Segment primitive \u2014 opaque typed head.",
45272
+ signature: "(any+) -> expression"
45273
+ },
45274
+ To: {
45275
+ description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45276
+ signature: "(any, any) -> nothing"
45277
+ }
44592
45278
  }
44593
45279
  ];
44594
45280
 
@@ -51725,6 +52411,7 @@ Error in definition of "${name}"`,
51725
52411
 
51726
52412
  // src/compute-engine/boxed-expression/cache.ts
51727
52413
  function cachedValue(v, generation, fn) {
52414
+ if (v.generation === generation && v.value !== null) return v.value;
51728
52415
  v.generation = generation;
51729
52416
  v.value = fn();
51730
52417
  return v.value;
@@ -53502,6 +54189,12 @@ Error in definition of "${name}"`,
53502
54189
  function _escapeJsonString(s) {
53503
54190
  return s;
53504
54191
  }
54192
+ function _serializeLatexMetadata(ce, expr2) {
54193
+ const syntax = ce.latexSyntax;
54194
+ const opts = ce.latexOptions;
54195
+ if (Object.keys(opts).length === 0) return syntax.serialize(expr2);
54196
+ return syntax.serialize(expr2, { ...opts });
54197
+ }
53505
54198
  function serializeSubtract(ce, a, b, options, metadata) {
53506
54199
  if (isNumber(a) && a.isNegative) {
53507
54200
  const v = a.numericValue;
@@ -53806,7 +54499,7 @@ Error in definition of "${name}"`,
53806
54499
  ];
53807
54500
  const md = { ...metadata ?? {} };
53808
54501
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53809
- md.latex = _escapeJsonString(md.latex ?? ce.latexSyntax.serialize(fn));
54502
+ md.latex = _escapeJsonString(md.latex ?? _serializeLatexMetadata(ce, fn));
53810
54503
  } else md.latex = "";
53811
54504
  if (!options.metadata.includes("wikidata")) md.wikidata = "";
53812
54505
  if (!md.latex && !md.wikidata && options.shorthands.includes("function"))
@@ -53831,7 +54524,7 @@ Error in definition of "${name}"`,
53831
54524
  }
53832
54525
  metadata = { ...metadata };
53833
54526
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53834
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(sym2);
54527
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, sym2);
53835
54528
  if (metadata.latex !== void 0)
53836
54529
  metadata.latex = _escapeJsonString(metadata.latex);
53837
54530
  } else metadata.latex = void 0;
@@ -53993,7 +54686,7 @@ Error in definition of "${name}"`,
53993
54686
  }
53994
54687
  }
53995
54688
  if (options.metadata.includes("latex") && ce.latexSyntax)
53996
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(result2 ?? { num });
54689
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, result2 ?? { num });
53997
54690
  if (result2) {
53998
54691
  if (metadata.latex !== void 0)
53999
54692
  return { sym: result2, latex: metadata.latex };
@@ -54009,7 +54702,7 @@ Error in definition of "${name}"`,
54009
54702
  if (value.isNaN()) {
54010
54703
  num = "NaN";
54011
54704
  if (options.metadata.includes("latex") && ce.latexSyntax)
54012
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
54705
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54013
54706
  return metadata.latex !== void 0 ? { num, latex: metadata.latex } : { num };
54014
54707
  }
54015
54708
  return serializeJsonFunction(
@@ -54043,7 +54736,7 @@ Error in definition of "${name}"`,
54043
54736
  value = Number(value);
54044
54737
  } else {
54045
54738
  if (options.metadata.includes("latex") && ce.latexSyntax)
54046
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({
54739
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, {
54047
54740
  num: value.toString()
54048
54741
  });
54049
54742
  if (metadata.latex !== void 0)
@@ -54057,7 +54750,7 @@ Error in definition of "${name}"`,
54057
54750
  result = value > 0 ? "PositiveInfinity" : "NegativeInfinity";
54058
54751
  else num = serializeRepeatingDecimals(value.toString(), options);
54059
54752
  if (options.metadata.includes("latex") && ce.latexSyntax)
54060
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
54753
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54061
54754
  if (result) {
54062
54755
  if (metadata.latex !== void 0)
54063
54756
  return { sym: result, latex: metadata.latex };
@@ -54810,8 +55503,7 @@ Error in definition of "${name}"`,
54810
55503
  vars: options?.vars,
54811
55504
  imports: options?.imports,
54812
55505
  preamble: options?.preamble,
54813
- realOnly: options?.realOnly,
54814
- hints: options?.hints
55506
+ realOnly: options?.realOnly
54815
55507
  });
54816
55508
  } catch (e) {
54817
55509
  if (options?.fallback ?? true) {
@@ -54823,8 +55515,7 @@ Error in definition of "${name}"`,
54823
55515
  ce.pushScope();
54824
55516
  try {
54825
55517
  if (vars && typeof vars === "object") {
54826
- for (const [k, v] of Object.entries(vars))
54827
- ce.assign(k, v);
55518
+ for (const [k, v] of Object.entries(vars)) ce.assign(k, v);
54828
55519
  }
54829
55520
  return expr2.evaluate().re;
54830
55521
  } finally {
@@ -60054,8 +60745,7 @@ Error in definition of "${name}"`,
60054
60745
  return { re: null, im: formatFloat(iScale) };
60055
60746
  }
60056
60747
  const compiledFactors = remaining.map((r) => compile3(r));
60057
- if (iScale !== 1)
60058
- compiledFactors.unshift(formatFloat(iScale));
60748
+ if (iScale !== 1) compiledFactors.unshift(formatFloat(iScale));
60059
60749
  const imCode = foldTerms(compiledFactors, "1.0", "*");
60060
60750
  return { re: null, im: imCode };
60061
60751
  }
@@ -60659,39 +61349,130 @@ Error in definition of "${name}"`,
60659
61349
  if (args.length >= 2)
60660
61350
  return `_SYS.colormap(${compile3(args[0])}, ${compile3(args[1])})`;
60661
61351
  return `_SYS.colormap(${compile3(args[0])})`;
61352
+ },
61353
+ // -----------------------------------------------------------------------
61354
+ // Color constructor heads. All compile to OKLCh arrays at runtime — the
61355
+ // canonical color representation in this target. The constructors take
61356
+ // their own colorspace's components and convert internally.
61357
+ // (Mirrors the GPU target's design: color values are vec3 OKLCh.)
61358
+ // -----------------------------------------------------------------------
61359
+ Rgb: (args, compile3) => {
61360
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
61361
+ return `_SYS.rgb(${args.map(compile3).join(", ")})`;
61362
+ },
61363
+ Hsv: (args, compile3) => {
61364
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
61365
+ return `_SYS.hsv(${args.map(compile3).join(", ")})`;
61366
+ },
61367
+ Hsl: (args, compile3) => {
61368
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
61369
+ return `_SYS.hsl(${args.map(compile3).join(", ")})`;
61370
+ },
61371
+ Oklab: (args, compile3) => {
61372
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
61373
+ return `_SYS.oklab(${args.map(compile3).join(", ")})`;
61374
+ },
61375
+ Oklch: (args, compile3) => {
61376
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
61377
+ return `_SYS.oklch(${args.map(compile3).join(", ")})`;
61378
+ },
61379
+ // -----------------------------------------------------------------------
61380
+ // As* converters. Compile-time output convention matches the engine and
61381
+ // the GPU target: each returns components in the named space as a 3- or
61382
+ // 4-element array. `AsRgb` uses 0-1 sRGB channels (consistent across all
61383
+ // layers). `AsOklch` is the identity (canonical form).
61384
+ // -----------------------------------------------------------------------
61385
+ AsRgb: ([c], compile3) => {
61386
+ if (c === null) throw new Error("AsRgb: no argument");
61387
+ return `_SYS.asRgb(${compile3(c)})`;
61388
+ },
61389
+ AsHsv: ([c], compile3) => {
61390
+ if (c === null) throw new Error("AsHsv: no argument");
61391
+ return `_SYS.asHsv(${compile3(c)})`;
61392
+ },
61393
+ AsHsl: ([c], compile3) => {
61394
+ if (c === null) throw new Error("AsHsl: no argument");
61395
+ return `_SYS.asHsl(${compile3(c)})`;
61396
+ },
61397
+ AsOklab: ([c], compile3) => {
61398
+ if (c === null) throw new Error("AsOklab: no argument");
61399
+ return `_SYS.asOklab(${compile3(c)})`;
61400
+ },
61401
+ AsOklch: ([c], compile3) => {
61402
+ if (c === null) throw new Error("AsOklch: no argument");
61403
+ return compile3(c);
61404
+ },
61405
+ // Perceptual color difference (ΔE_OK).
61406
+ ColorDelta: ([a, b], compile3) => {
61407
+ if (a === null || b === null)
61408
+ throw new Error("ColorDelta: need two colors");
61409
+ return `_SYS.colorDelta(${compile3(a)}, ${compile3(b)})`;
61410
+ },
61411
+ // Euclidean distance between two tuples (any positive dimension).
61412
+ // The GPU target maps `Distance` to the GLSL/WGSL `distance()` builtin
61413
+ // (vec-only); this JS handler works on plain arrays of any length.
61414
+ Distance: ([a, b], compile3) => {
61415
+ if (a === null || b === null) throw new Error("Distance: need two points");
61416
+ return `_SYS.distance(${compile3(a)}, ${compile3(b)})`;
60662
61417
  }
60663
61418
  };
60664
61419
  function toRI(c) {
60665
61420
  return { re: c.re, im: c.im };
60666
61421
  }
61422
+ function normalizeAlpha2(a) {
61423
+ if (a === void 0) return void 0;
61424
+ if (!Number.isFinite(a)) return void 0;
61425
+ if (Math.abs(a - 1) < 1e-9) return void 0;
61426
+ return a;
61427
+ }
60667
61428
  function toRgb255(input) {
60668
61429
  if (typeof input === "string") {
60669
61430
  const c = parseColor(input);
60670
- return {
61431
+ const rgb2 = {
60671
61432
  r: c >>> 24 & 255,
60672
61433
  g: c >>> 16 & 255,
60673
- b: c >>> 8 & 255,
60674
- alpha: (c & 255) / 255
61434
+ b: c >>> 8 & 255
60675
61435
  };
61436
+ const alpha = normalizeAlpha2((c & 255) / 255);
61437
+ if (alpha !== void 0) rgb2.alpha = alpha;
61438
+ return rgb2;
61439
+ }
61440
+ const rgb = oklchToRgb({ L: input[0], C: input[1], H: input[2] });
61441
+ if (input.length >= 4) {
61442
+ const alpha = normalizeAlpha2(input[3]);
61443
+ if (alpha !== void 0) rgb.alpha = alpha;
60676
61444
  }
60677
- const rgb = {
60678
- r: input[0] * 255,
60679
- g: input[1] * 255,
60680
- b: input[2] * 255
60681
- };
60682
- if (input.length >= 4) rgb.alpha = input[3];
60683
61445
  return rgb;
60684
61446
  }
60685
- function packedToArray(c) {
60686
- const r = (c >>> 24 & 255) / 255;
60687
- const g = (c >>> 16 & 255) / 255;
60688
- const b = (c >>> 8 & 255) / 255;
60689
- const a = (c & 255) / 255;
60690
- return Math.abs(a - 1) < 1e-4 ? [r, g, b] : [r, g, b, a];
61447
+ function toOklch2(input) {
61448
+ if (typeof input === "string") {
61449
+ const c = parseColor(input);
61450
+ const r = c >>> 24 & 255;
61451
+ const g = c >>> 16 & 255;
61452
+ const b = c >>> 8 & 255;
61453
+ const oklch2 = rgbToOklch({ r, g, b });
61454
+ const alpha = normalizeAlpha2((c & 255) / 255);
61455
+ if (alpha !== void 0) oklch2.alpha = alpha;
61456
+ return oklch2;
61457
+ }
61458
+ return {
61459
+ L: input[0],
61460
+ C: input[1],
61461
+ H: input[2],
61462
+ alpha: input.length >= 4 ? normalizeAlpha2(input[3]) : void 0
61463
+ };
61464
+ }
61465
+ function packedToOklch(c) {
61466
+ const r = c >>> 24 & 255;
61467
+ const g = c >>> 16 & 255;
61468
+ const b = c >>> 8 & 255;
61469
+ const oklch2 = rgbToOklch({ r, g, b });
61470
+ const alpha = normalizeAlpha2((c & 255) / 255);
61471
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
60691
61472
  }
60692
61473
  var colorHelpers = {
60693
61474
  color(input) {
60694
- return packedToArray(parseColor(input));
61475
+ return packedToOklch(parseColor(input));
60695
61476
  },
60696
61477
  colorToString(input, format) {
60697
61478
  const rgb = toRgb255(input);
@@ -60702,7 +61483,7 @@ Error in definition of "${name}"`,
60702
61483
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
60703
61484
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
60704
61485
  let hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
60705
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
61486
+ if (rgb.alpha !== void 0) {
60706
61487
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
60707
61488
  hex += a.toString(16).padStart(2, "0");
60708
61489
  }
@@ -60712,7 +61493,7 @@ Error in definition of "${name}"`,
60712
61493
  const r = Math.round(rgb.r);
60713
61494
  const g = Math.round(rgb.g);
60714
61495
  const b = Math.round(rgb.b);
60715
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61496
+ if (rgb.alpha !== void 0)
60716
61497
  return `rgb(${r} ${g} ${b} / ${rgb.alpha})`;
60717
61498
  return `rgb(${r} ${g} ${b})`;
60718
61499
  }
@@ -60721,7 +61502,7 @@ Error in definition of "${name}"`,
60721
61502
  const h = Math.round(hsl.h * 10) / 10;
60722
61503
  const s = Math.round(hsl.s * 1e3) / 10;
60723
61504
  const l = Math.round(hsl.l * 1e3) / 10;
60724
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61505
+ if (rgb.alpha !== void 0)
60725
61506
  return `hsl(${h} ${s}% ${l}% / ${rgb.alpha})`;
60726
61507
  return `hsl(${h} ${s}% ${l}%)`;
60727
61508
  }
@@ -60730,7 +61511,7 @@ Error in definition of "${name}"`,
60730
61511
  const L = Math.round(c.L * 1e3) / 1e3;
60731
61512
  const C = Math.round(c.C * 1e3) / 1e3;
60732
61513
  const H = Math.round(c.H * 10) / 10;
60733
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61514
+ if (rgb.alpha !== void 0)
60734
61515
  return `oklch(${L} ${C} ${H} / ${rgb.alpha})`;
60735
61516
  return `oklch(${L} ${C} ${H})`;
60736
61517
  }
@@ -60739,29 +61520,29 @@ Error in definition of "${name}"`,
60739
61520
  }
60740
61521
  },
60741
61522
  colorMix(input1, input2, ratio = 0.5) {
60742
- const rgb1 = toRgb255(input1);
60743
- const rgb2 = toRgb255(input2);
61523
+ const c1 = toOklch2(input1);
61524
+ const c2 = toOklch2(input2);
60744
61525
  ratio = Math.max(0, Math.min(1, ratio));
60745
- const c1 = rgbToOklch(rgb1);
60746
- const c2 = rgbToOklch(rgb2);
60747
- let dh = c2.H - c1.H;
60748
- if (dh > 180) dh -= 360;
60749
- if (dh < -180) dh += 360;
60750
- let H = c1.H + dh * ratio;
60751
- if (H < 0) H += 360;
60752
- if (H >= 360) H -= 360;
60753
- const mixed = oklchToRgb({
60754
- L: c1.L + (c2.L - c1.L) * ratio,
60755
- C: c1.C + (c2.C - c1.C) * ratio,
60756
- H
60757
- });
60758
- const r = mixed.r / 255;
60759
- const g = mixed.g / 255;
60760
- const b = mixed.b / 255;
60761
- const a1 = rgb1.alpha ?? 1;
60762
- const a2 = rgb2.alpha ?? 1;
60763
- const alpha = a1 + (a2 - a1) * ratio;
60764
- return Math.abs(alpha - 1) > 1e-4 ? [r, g, b, alpha] : [r, g, b];
61526
+ const c1Achromatic = c1.C < 1e-6;
61527
+ const c2Achromatic = c2.C < 1e-6;
61528
+ let H;
61529
+ if (c1Achromatic && c2Achromatic) H = c1.H;
61530
+ else if (c1Achromatic) H = c2.H;
61531
+ else if (c2Achromatic) H = c1.H;
61532
+ else {
61533
+ let dh = c2.H - c1.H;
61534
+ if (dh > 180) dh -= 360;
61535
+ if (dh < -180) dh += 360;
61536
+ H = c1.H + dh * ratio;
61537
+ if (H < 0) H += 360;
61538
+ if (H >= 360) H -= 360;
61539
+ }
61540
+ const L = c1.L + (c2.L - c1.L) * ratio;
61541
+ const C = c1.C + (c2.C - c1.C) * ratio;
61542
+ const a1 = c1.alpha ?? 1;
61543
+ const a2 = c2.alpha ?? 1;
61544
+ const alpha = normalizeAlpha2(a1 + (a2 - a1) * ratio);
61545
+ return alpha !== void 0 ? [L, C, H, alpha] : [L, C, H];
60765
61546
  },
60766
61547
  colorContrast(bg, fg) {
60767
61548
  return apca(toRgb255(bg), toRgb255(fg));
@@ -60769,11 +61550,11 @@ Error in definition of "${name}"`,
60769
61550
  contrastingColor(bg, fg1, fg2) {
60770
61551
  const bgRgb = toRgb255(bg);
60771
61552
  if (fg1 !== void 0 && fg2 !== void 0) {
60772
- return packedToArray(
61553
+ return packedToOklch(
60773
61554
  contrastingColor({ bg: bgRgb, fg1: toRgb255(fg1), fg2: toRgb255(fg2) })
60774
61555
  );
60775
61556
  }
60776
- return packedToArray(contrastingColor(bgRgb));
61557
+ return packedToOklch(contrastingColor(bgRgb));
60777
61558
  },
60778
61559
  colorToColorspace(input, space) {
60779
61560
  const rgb = toRgb255(input);
@@ -60802,7 +61583,7 @@ Error in definition of "${name}"`,
60802
61583
  default:
60803
61584
  throw new Error(`Unknown color space: ${space}`);
60804
61585
  }
60805
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
61586
+ if (alpha !== void 0) result.push(alpha);
60806
61587
  return result;
60807
61588
  },
60808
61589
  colormap(name, arg) {
@@ -60814,7 +61595,7 @@ Error in definition of "${name}"`,
60814
61595
  const palette = allPalettes[name];
60815
61596
  if (!palette) throw new Error(`Unknown palette: ${name}`);
60816
61597
  const colors = palette.map(
60817
- (hex) => parseColorToRgb01(hex)
61598
+ (hex) => packedToOklch(parseColor(hex))
60818
61599
  );
60819
61600
  if (arg === void 0) return colors;
60820
61601
  if (Number.isInteger(arg) && arg >= 2) {
@@ -60838,62 +61619,128 @@ Error in definition of "${name}"`,
60838
61619
  const frac = pos - i;
60839
61620
  if (frac === 0 || i >= colors.length - 1)
60840
61621
  return [...colors[Math.min(i, colors.length - 1)]];
60841
- const rgb1 = {
60842
- r: colors[i][0] * 255,
60843
- g: colors[i][1] * 255,
60844
- b: colors[i][2] * 255
60845
- };
60846
- const rgb2 = {
60847
- r: colors[i + 1][0] * 255,
60848
- g: colors[i + 1][1] * 255,
60849
- b: colors[i + 1][2] * 255
60850
- };
60851
- const c1 = rgbToOklch(rgb1);
60852
- const c2 = rgbToOklch(rgb2);
60853
- let dh = c2.H - c1.H;
60854
- if (dh > 180) dh -= 360;
60855
- if (dh < -180) dh += 360;
60856
- let H = c1.H + dh * frac;
60857
- if (H < 0) H += 360;
60858
- if (H >= 360) H -= 360;
60859
- const mixed = oklchToRgb({
60860
- L: c1.L + (c2.L - c1.L) * frac,
60861
- C: c1.C + (c2.C - c1.C) * frac,
60862
- H
60863
- });
60864
- return [mixed.r / 255, mixed.g / 255, mixed.b / 255];
61622
+ const [L1, C1, H1] = colors[i];
61623
+ const [L2, C2, H2] = colors[i + 1];
61624
+ const c1Achromatic = C1 < 1e-6;
61625
+ const c2Achromatic = C2 < 1e-6;
61626
+ let H;
61627
+ if (c1Achromatic && c2Achromatic) H = H1;
61628
+ else if (c1Achromatic) H = H2;
61629
+ else if (c2Achromatic) H = H1;
61630
+ else {
61631
+ let dh = H2 - H1;
61632
+ if (dh > 180) dh -= 360;
61633
+ if (dh < -180) dh += 360;
61634
+ H = H1 + dh * frac;
61635
+ if (H < 0) H += 360;
61636
+ if (H >= 360) H -= 360;
61637
+ }
61638
+ return [L1 + (L2 - L1) * frac, C1 + (C2 - C1) * frac, H];
60865
61639
  },
60866
61640
  colorFromColorspace(components, space) {
60867
61641
  const c0 = components[0];
60868
61642
  const c1 = components[1];
60869
61643
  const c2 = components[2];
60870
61644
  const alpha = components.length >= 4 ? components[3] : void 0;
60871
- let result;
61645
+ let oklch2;
60872
61646
  switch (space.toLowerCase()) {
60873
61647
  case "rgb":
60874
- result = [c0, c1, c2];
61648
+ oklch2 = rgbToOklch({ r: c0 * 255, g: c1 * 255, b: c2 * 255 });
60875
61649
  break;
60876
61650
  case "hsl": {
60877
- const r = hslToRgb(c0, c1, c2);
60878
- result = [r.r / 255, r.g / 255, r.b / 255];
61651
+ const rgb = hslToRgb(c0, c1, c2);
61652
+ oklch2 = rgbToOklch(rgb);
60879
61653
  break;
60880
61654
  }
60881
- case "oklch": {
60882
- const r = oklchToRgb({ L: c0, C: c1, H: c2 });
60883
- result = [r.r / 255, r.g / 255, r.b / 255];
61655
+ case "oklch":
61656
+ oklch2 = { L: c0, C: c1, H: c2 };
60884
61657
  break;
60885
- }
60886
61658
  case "oklab":
60887
- case "lab": {
60888
- const r = oklabToRgb({ L: c0, a: c1, b: c2 });
60889
- result = [r.r / 255, r.g / 255, r.b / 255];
61659
+ case "lab":
61660
+ oklch2 = oklabToOklch({ L: c0, a: c1, b: c2 });
60890
61661
  break;
60891
- }
60892
61662
  default:
60893
61663
  throw new Error(`Unknown color space: ${space}`);
60894
61664
  }
60895
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
60896
- return result;
61665
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
61666
+ },
61667
+ // -----------------------------------------------------------------------
61668
+ // Color constructors. Each accepts components in its colorspace's natural
61669
+ // units and returns the canonical OKLCh array `[L, C, H]` (or with alpha).
61670
+ // -----------------------------------------------------------------------
61671
+ rgb(r, g, b, alpha) {
61672
+ const c = rgbToOklch({ r: r * 255, g: g * 255, b: b * 255 });
61673
+ const a = normalizeAlpha2(alpha);
61674
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61675
+ },
61676
+ hsv(h, s, v, alpha) {
61677
+ const rgb = hsvToRgb(h, s, v);
61678
+ const c = rgbToOklch(rgb);
61679
+ const a = normalizeAlpha2(alpha);
61680
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61681
+ },
61682
+ hsl(h, s, l, alpha) {
61683
+ const rgb = hslToRgb(h, s, l);
61684
+ const c = rgbToOklch({ r: rgb.r, g: rgb.g, b: rgb.b });
61685
+ const a = normalizeAlpha2(alpha);
61686
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61687
+ },
61688
+ oklab(L, a, b, alpha) {
61689
+ const c = oklabToOklch({ L, a, b });
61690
+ const al = normalizeAlpha2(alpha);
61691
+ return al !== void 0 ? [c.L, c.C, c.H, al] : [c.L, c.C, c.H];
61692
+ },
61693
+ oklch(L, C, H, alpha) {
61694
+ const a = normalizeAlpha2(alpha);
61695
+ return a !== void 0 ? [L, C, H, a] : [L, C, H];
61696
+ },
61697
+ // -----------------------------------------------------------------------
61698
+ // As* converters. Inputs are anything `toOklch` accepts (string, packed
61699
+ // int, or OKLCh array). Outputs are 3- or 4-element arrays in the named
61700
+ // space. sRGB-based outputs (asRgb/asHsv/asHsl) use 0-1 channels for
61701
+ // consistency with the GPU target's shader convention.
61702
+ // -----------------------------------------------------------------------
61703
+ asRgb(input) {
61704
+ const rgb = toRgb255(input);
61705
+ const r = rgb.r / 255;
61706
+ const g = rgb.g / 255;
61707
+ const b = rgb.b / 255;
61708
+ return rgb.alpha !== void 0 ? [r, g, b, rgb.alpha] : [r, g, b];
61709
+ },
61710
+ asHsv(input) {
61711
+ const rgb = toRgb255(input);
61712
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
61713
+ return rgb.alpha !== void 0 ? [hsv.h, hsv.s, hsv.v, rgb.alpha] : [hsv.h, hsv.s, hsv.v];
61714
+ },
61715
+ asHsl(input) {
61716
+ const rgb = toRgb255(input);
61717
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
61718
+ return rgb.alpha !== void 0 ? [hsl.h, hsl.s, hsl.l, rgb.alpha] : [hsl.h, hsl.s, hsl.l];
61719
+ },
61720
+ asOklab(input) {
61721
+ const c = toOklch2(input);
61722
+ const lab = oklchToOklab({ L: c.L, C: c.C, H: c.H });
61723
+ return c.alpha !== void 0 ? [lab.L, lab.a, lab.b, c.alpha] : [lab.L, lab.a, lab.b];
61724
+ },
61725
+ // asOklch is identity — handled at compile time as a pass-through
61726
+ // Perceptual color difference (ΔE_OK).
61727
+ colorDelta(a, b) {
61728
+ const labA = oklchToOklab(toOklch2(a));
61729
+ const labB = oklchToOklab(toOklch2(b));
61730
+ return oklabDeltaE(labA, labB);
61731
+ },
61732
+ // Euclidean distance between two tuples. Plain numeric — not a color
61733
+ // operation despite living in the same helpers block.
61734
+ distance(a, b) {
61735
+ if (!Array.isArray(a) || !Array.isArray(b))
61736
+ throw new Error("Distance: expected two arrays");
61737
+ if (a.length !== b.length) throw new Error("Distance: dimension mismatch");
61738
+ let sumSq = 0;
61739
+ for (let i = 0; i < a.length; i++) {
61740
+ const d = a[i] - b[i];
61741
+ sumSq += d * d;
61742
+ }
61743
+ return Math.sqrt(sumSq);
60897
61744
  }
60898
61745
  };
60899
61746
  var SYS_HELPERS = {
@@ -61311,43 +62158,6 @@ Error in definition of "${name}"`,
61311
62158
  return b;
61312
62159
  }
61313
62160
 
61314
- // src/compute-engine/compilation/fractal-orbit.ts
61315
- function toBigDecimal(v) {
61316
- if (typeof v === "object" && "hi" in v)
61317
- return new BigDecimal(v.hi).add(new BigDecimal(v.lo));
61318
- return new BigDecimal(v);
61319
- }
61320
- function hpToNumber(v) {
61321
- if (typeof v === "number") return v;
61322
- if (typeof v === "string") return Number(v);
61323
- return v.hi + v.lo;
61324
- }
61325
- function computeReferenceOrbit(center, maxIter, precision) {
61326
- const prevPrecision = BigDecimal.precision;
61327
- BigDecimal.precision = precision;
61328
- try {
61329
- const cr = toBigDecimal(center[0]);
61330
- const ci = toBigDecimal(center[1]);
61331
- let zr = BigDecimal.ZERO;
61332
- let zi = BigDecimal.ZERO;
61333
- const ESCAPE = new BigDecimal(256);
61334
- const points = [];
61335
- for (let i = 0; i < maxIter; i++) {
61336
- points.push(zr.toNumber(), zi.toNumber());
61337
- const zr2 = zr.mul(zr).toPrecision(precision);
61338
- const zi2 = zi.mul(zi).toPrecision(precision);
61339
- const mag2 = zr2.add(zi2);
61340
- if (mag2.cmp(ESCAPE) > 0) break;
61341
- const new_zi = zr.mul(zi).toPrecision(precision).mul(2).add(ci);
61342
- zr = zr2.sub(zi2).add(cr);
61343
- zi = new_zi;
61344
- }
61345
- return new Float32Array(points);
61346
- } finally {
61347
- BigDecimal.precision = prevPrecision;
61348
- }
61349
- }
61350
-
61351
62161
  // src/compute-engine/compilation/gpu-target.ts
61352
62162
  var GPU_OPERATORS = {
61353
62163
  Add: ["+", 11],
@@ -61369,6 +62179,13 @@ Error in definition of "${name}"`,
61369
62179
  function gpuVec2(target) {
61370
62180
  return target?.language === "wgsl" ? "vec2f" : "vec2";
61371
62181
  }
62182
+ function gpuVec3(target) {
62183
+ return target?.language === "wgsl" ? "vec3f" : "vec3";
62184
+ }
62185
+ function readStringLiteral(expr2) {
62186
+ if (!isString(expr2)) return null;
62187
+ return expr2.string?.toLowerCase() ?? null;
62188
+ }
61372
62189
  function compileIntArg(expr2, compile3, target) {
61373
62190
  const c = tryGetConstant(expr2);
61374
62191
  if (c !== void 0 && Number.isInteger(c)) return c.toString();
@@ -61427,17 +62244,10 @@ Error in definition of "${name}"`,
61427
62244
  `for (${indexDecl} = ${lowerStr}; ${index} <= ${upperStr}; ${index}++) {`,
61428
62245
  ` ${acc} ${op}= ${body};`,
61429
62246
  `}`,
61430
- `return ${acc}`
62247
+ `return ${acc};`
61431
62248
  ];
61432
62249
  return lines.join("\n");
61433
62250
  }
61434
- function selectFractalStrategy(target) {
61435
- const radius = target.hints?.viewport?.radius;
61436
- if (radius === void 0) return "single";
61437
- if (radius > 1e-6) return "single";
61438
- if (radius > 1e-14) return "double";
61439
- return "perturbation";
61440
- }
61441
62251
  var GPU_FUNCTIONS = {
61442
62252
  // Variadic arithmetic (for function-call form, e.g., with vectors)
61443
62253
  Add: (args, compile3, target) => {
@@ -61488,8 +62298,7 @@ Error in definition of "${name}"`,
61488
62298
  const iScale = isSymbol2(iFactor, "ImaginaryUnit") ? 1 : iFactor.im;
61489
62299
  const realFactors = args.filter((_, i) => i !== iIndex);
61490
62300
  const v2 = gpuVec2(target);
61491
- if (realFactors.length === 0)
61492
- return `${v2}(0.0, ${formatFloat(iScale)})`;
62301
+ if (realFactors.length === 0) return `${v2}(0.0, ${formatFloat(iScale)})`;
61493
62302
  const factors = realFactors.map((f) => compile3(f));
61494
62303
  if (iScale !== 1) factors.unshift(formatFloat(iScale));
61495
62304
  const imCode = foldTerms(factors, "1.0", "*");
@@ -61542,8 +62351,7 @@ Error in definition of "${name}"`,
61542
62351
  if (isNumber(x) && x.im !== 0) {
61543
62352
  return `${gpuVec2(target)}(${formatFloat(-x.re)}, ${formatFloat(-x.im)})`;
61544
62353
  }
61545
- if (isSymbol2(x, "ImaginaryUnit"))
61546
- return `${gpuVec2(target)}(0.0, -1.0)`;
62354
+ if (isSymbol2(x, "ImaginaryUnit")) return `${gpuVec2(target)}(0.0, -1.0)`;
61547
62355
  return `(-${compile3(x)})`;
61548
62356
  },
61549
62357
  // Standard math functions with complex dispatch
@@ -61916,49 +62724,139 @@ Error in definition of "${name}"`,
61916
62724
  }
61917
62725
  const isWGSL = target?.language === "wgsl";
61918
62726
  const v3 = isWGSL ? "vec3f" : "vec3";
61919
- return `((_gpu_apca(${bg}, ${v3}(0.0)) > 50.0) ? ${v3}(0.0) : ${v3}(1.0))`;
62727
+ const black = `${v3}(0.0)`;
62728
+ const white = `${v3}(1.0, 0.0, 0.0)`;
62729
+ return `((_gpu_apca(${bg}, ${black}) > 50.0) ? ${black} : ${white})`;
61920
62730
  },
61921
62731
  ColorToColorspace: ([color, space], compile3) => {
61922
62732
  if (color === null || space === null)
61923
62733
  throw new Error("ColorToColorspace: need color and space");
61924
- return `_gpu_srgb_to_oklab(${compile3(color)})`;
62734
+ const spaceName = readStringLiteral(space);
62735
+ if (spaceName === null)
62736
+ throw new Error("ColorToColorspace: space must be a string literal");
62737
+ const c = compile3(color);
62738
+ switch (spaceName) {
62739
+ case "oklch":
62740
+ return c;
62741
+ case "oklab":
62742
+ case "lab":
62743
+ return `_gpu_oklch_to_oklab(${c})`;
62744
+ case "rgb":
62745
+ return `_gpu_oklch_to_srgb(${c})`;
62746
+ case "hsl":
62747
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${c}))`;
62748
+ case "hsv":
62749
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${c}))`;
62750
+ default:
62751
+ throw new Error(
62752
+ `ColorToColorspace: unsupported space "${spaceName}" on GPU target`
62753
+ );
62754
+ }
61925
62755
  },
61926
62756
  ColorFromColorspace: ([components, space], compile3) => {
61927
62757
  if (components === null || space === null)
61928
62758
  throw new Error("ColorFromColorspace: need components and space");
61929
- return `_gpu_oklab_to_srgb(${compile3(components)})`;
62759
+ const spaceName = readStringLiteral(space);
62760
+ if (spaceName === null)
62761
+ throw new Error("ColorFromColorspace: space must be a string literal");
62762
+ const c = compile3(components);
62763
+ switch (spaceName) {
62764
+ case "oklch":
62765
+ return c;
62766
+ case "oklab":
62767
+ case "lab":
62768
+ return `_gpu_oklab_to_oklch(${c})`;
62769
+ case "rgb":
62770
+ return `_gpu_srgb_to_oklch(${c})`;
62771
+ case "hsl":
62772
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${c}))`;
62773
+ case "hsv":
62774
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${c}))`;
62775
+ default:
62776
+ throw new Error(
62777
+ `ColorFromColorspace: unsupported space "${spaceName}" on GPU target`
62778
+ );
62779
+ }
62780
+ },
62781
+ // ---------------------------------------------------------------------------
62782
+ // Color literals. Each typed head compiles to a canonical OKLCh vec3.
62783
+ // Alpha (4th argument) is dropped — GPU color values are vec3 only. Pass
62784
+ // alpha as a separate uniform if it's needed at the framebuffer boundary.
62785
+ // ---------------------------------------------------------------------------
62786
+ Color: ([s], _compile2, target) => {
62787
+ if (s === null) throw new Error("Color: no argument");
62788
+ const str = readStringLiteral(s);
62789
+ if (str === null)
62790
+ throw new Error("Color: argument must be a string literal on GPU target");
62791
+ const packed = parseColor(str);
62792
+ if (packed === 0 && str.trim().toLowerCase() !== "transparent")
62793
+ throw new Error(`Color: invalid color string "${str}"`);
62794
+ const r = packed >>> 24 & 255;
62795
+ const g = packed >>> 16 & 255;
62796
+ const b = packed >>> 8 & 255;
62797
+ const oklch2 = rgbToOklch({ r, g, b });
62798
+ return `${gpuVec3(target)}(${formatFloat(oklch2.L)}, ${formatFloat(oklch2.C)}, ${formatFloat(oklch2.H)})`;
62799
+ },
62800
+ Rgb: (args, compile3, target) => {
62801
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
62802
+ const v3 = gpuVec3(target);
62803
+ return `_gpu_srgb_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
62804
+ },
62805
+ Hsv: (args, compile3, target) => {
62806
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
62807
+ const v3 = gpuVec3(target);
62808
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
62809
+ },
62810
+ Hsl: (args, compile3, target) => {
62811
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
62812
+ const v3 = gpuVec3(target);
62813
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
62814
+ },
62815
+ Oklab: (args, compile3, target) => {
62816
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
62817
+ const v3 = gpuVec3(target);
62818
+ return `_gpu_oklab_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
62819
+ },
62820
+ Oklch: (args, compile3, target) => {
62821
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
62822
+ const v3 = gpuVec3(target);
62823
+ return `${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})`;
62824
+ },
62825
+ // ---------------------------------------------------------------------------
62826
+ // As* operators. AsOklch is identity (canonical). The other As* return
62827
+ // components in the named space, equivalent to ColorToColorspace(c, 'x').
62828
+ // ---------------------------------------------------------------------------
62829
+ AsOklch: ([c], compile3) => {
62830
+ if (c === null) throw new Error("AsOklch: no argument");
62831
+ return compile3(c);
62832
+ },
62833
+ AsOklab: ([c], compile3) => {
62834
+ if (c === null) throw new Error("AsOklab: no argument");
62835
+ return `_gpu_oklch_to_oklab(${compile3(c)})`;
62836
+ },
62837
+ AsRgb: ([c], compile3) => {
62838
+ if (c === null) throw new Error("AsRgb: no argument");
62839
+ return `_gpu_oklch_to_srgb(${compile3(c)})`;
62840
+ },
62841
+ AsHsv: ([c], compile3) => {
62842
+ if (c === null) throw new Error("AsHsv: no argument");
62843
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${compile3(c)}))`;
62844
+ },
62845
+ AsHsl: ([c], compile3) => {
62846
+ if (c === null) throw new Error("AsHsl: no argument");
62847
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${compile3(c)}))`;
61930
62848
  },
61931
62849
  // Fractal functions
61932
62850
  Mandelbrot: ([c, maxIter], compile3, target) => {
61933
62851
  if (c === null || maxIter === null)
61934
62852
  throw new Error("Mandelbrot: missing arguments");
61935
62853
  const iterCode = compileIntArg(maxIter, compile3, target);
61936
- const strategy = selectFractalStrategy(target);
61937
- if (strategy === "double") {
61938
- const dpCoord = target?.language === "wgsl" ? "_dp_coord(v_uv)" : "_dp_coord()";
61939
- return `_fractal_mandelbrot_dp(${dpCoord}, ${iterCode})`;
61940
- }
61941
- if (strategy === "perturbation") {
61942
- const ptDelta = target?.language === "wgsl" ? "_pt_delta(v_uv)" : "_pt_delta()";
61943
- return `_fractal_mandelbrot_pt(${ptDelta}, ${iterCode})`;
61944
- }
61945
62854
  return `_fractal_mandelbrot(${compile3(c)}, ${iterCode})`;
61946
62855
  },
61947
62856
  Julia: ([z, c, maxIter], compile3, target) => {
61948
62857
  if (z === null || c === null || maxIter === null)
61949
62858
  throw new Error("Julia: missing arguments");
61950
62859
  const iterCode = compileIntArg(maxIter, compile3, target);
61951
- const strategy = selectFractalStrategy(target);
61952
- if (strategy === "double") {
61953
- const dpCoord = target?.language === "wgsl" ? "_dp_coord(v_uv)" : "_dp_coord()";
61954
- const cCode = compile3(c);
61955
- return `_fractal_julia_dp(${dpCoord}, vec4(${cCode}, vec2(0.0)), ${iterCode})`;
61956
- }
61957
- if (strategy === "perturbation") {
61958
- const ptDelta = target?.language === "wgsl" ? "_pt_delta(v_uv)" : "_pt_delta()";
61959
- const cCode = compile3(c);
61960
- return `_fractal_julia_pt(${ptDelta}, ${cCode}, ${iterCode})`;
61961
- }
61962
62860
  return `_fractal_julia(${compile3(z)}, ${compile3(c)}, ${iterCode})`;
61963
62861
  },
61964
62862
  // Vector/Matrix operations
@@ -62555,232 +63453,6 @@ fn _gpu_besselJ(n_in: i32, x_in: f32) -> f32 {
62555
63453
  for (var k2: i32 = 2; k2 <= M; k2 += 2) { norm += 2.0 * vals[k2]; }
62556
63454
  return sgn * vals[n] / norm;
62557
63455
  }
62558
- `;
62559
- var GPU_DS_ARITHMETIC_PREAMBLE_GLSL = `
62560
- // Split a float into high and low parts for exact multiplication
62561
- vec2 ds_split(float a) {
62562
- const float SPLIT = 4097.0; // 2^12 + 1
62563
- float t = SPLIT * a;
62564
- float hi = t - (t - a);
62565
- float lo = a - hi;
62566
- return vec2(hi, lo);
62567
- }
62568
-
62569
- // Create a double-single from a single float
62570
- vec2 ds_from(float a) {
62571
- return vec2(a, 0.0);
62572
- }
62573
-
62574
- // Error-free addition (Knuth TwoSum)
62575
- vec2 ds_add(vec2 a, vec2 b) {
62576
- float s = a.x + b.x;
62577
- float v = s - a.x;
62578
- float e = (a.x - (s - v)) + (b.x - v);
62579
- float lo = (a.y + b.y) + e;
62580
- float hi = s + lo;
62581
- lo = lo - (hi - s);
62582
- return vec2(hi, lo);
62583
- }
62584
-
62585
- // Double-single subtraction
62586
- vec2 ds_sub(vec2 a, vec2 b) {
62587
- return ds_add(a, vec2(-b.x, -b.y));
62588
- }
62589
-
62590
- // Error-free multiplication (Dekker TwoProduct)
62591
- vec2 ds_mul(vec2 a, vec2 b) {
62592
- float p = a.x * b.x;
62593
- vec2 sa = ds_split(a.x);
62594
- vec2 sb = ds_split(b.x);
62595
- float err = ((sa.x * sb.x - p) + sa.x * sb.y + sa.y * sb.x) + sa.y * sb.y;
62596
- err += a.x * b.y + a.y * b.x;
62597
- float hi = p + err;
62598
- float lo = err - (hi - p);
62599
- return vec2(hi, lo);
62600
- }
62601
-
62602
- // Optimized self-multiply
62603
- vec2 ds_sqr(vec2 a) {
62604
- float p = a.x * a.x;
62605
- vec2 sa = ds_split(a.x);
62606
- float err = ((sa.x * sa.x - p) + 2.0 * sa.x * sa.y) + sa.y * sa.y;
62607
- err += 2.0 * a.x * a.y;
62608
- float hi = p + err;
62609
- float lo = err - (hi - p);
62610
- return vec2(hi, lo);
62611
- }
62612
-
62613
- // Compare magnitude: returns -1, 0, or 1
62614
- float ds_cmp(vec2 a, vec2 b) {
62615
- float d = a.x - b.x;
62616
- if (d != 0.0) return sign(d);
62617
- return sign(a.y - b.y);
62618
- }
62619
- `;
62620
- var GPU_DS_ARITHMETIC_PREAMBLE_WGSL = `
62621
- fn ds_split(a: f32) -> vec2f {
62622
- const SPLIT: f32 = 4097.0;
62623
- let t = SPLIT * a;
62624
- let hi = t - (t - a);
62625
- let lo = a - hi;
62626
- return vec2f(hi, lo);
62627
- }
62628
-
62629
- fn ds_from(a: f32) -> vec2f {
62630
- return vec2f(a, 0.0);
62631
- }
62632
-
62633
- fn ds_add(a: vec2f, b: vec2f) -> vec2f {
62634
- let s = a.x + b.x;
62635
- let v = s - a.x;
62636
- let e = (a.x - (s - v)) + (b.x - v);
62637
- let lo_t = (a.y + b.y) + e;
62638
- let hi = s + lo_t;
62639
- let lo = lo_t - (hi - s);
62640
- return vec2f(hi, lo);
62641
- }
62642
-
62643
- fn ds_sub(a: vec2f, b: vec2f) -> vec2f {
62644
- return ds_add(a, vec2f(-b.x, -b.y));
62645
- }
62646
-
62647
- fn ds_mul(a: vec2f, b: vec2f) -> vec2f {
62648
- let p = a.x * b.x;
62649
- let sa = ds_split(a.x);
62650
- let sb = ds_split(b.x);
62651
- var err = ((sa.x * sb.x - p) + sa.x * sb.y + sa.y * sb.x) + sa.y * sb.y;
62652
- err += a.x * b.y + a.y * b.x;
62653
- let hi = p + err;
62654
- let lo = err - (hi - p);
62655
- return vec2f(hi, lo);
62656
- }
62657
-
62658
- fn ds_sqr(a: vec2f) -> vec2f {
62659
- let p = a.x * a.x;
62660
- let sa = ds_split(a.x);
62661
- var err = ((sa.x * sa.x - p) + 2.0 * sa.x * sa.y) + sa.y * sa.y;
62662
- err += 2.0 * a.x * a.y;
62663
- let hi = p + err;
62664
- let lo = err - (hi - p);
62665
- return vec2f(hi, lo);
62666
- }
62667
-
62668
- fn ds_cmp(a: vec2f, b: vec2f) -> f32 {
62669
- let d = a.x - b.x;
62670
- if (d != 0.0) { return sign(d); }
62671
- return sign(a.y - b.y);
62672
- }
62673
- `;
62674
- var GPU_FRACTAL_DP_PREAMBLE_GLSL = `
62675
- uniform float _dp_cx_hi;
62676
- uniform float _dp_cx_lo;
62677
- uniform float _dp_cy_hi;
62678
- uniform float _dp_cy_lo;
62679
- uniform float _dp_w;
62680
- uniform float _dp_h;
62681
-
62682
- vec4 _dp_coord() {
62683
- // Per-pixel offset from center \u2014 small, so float-precise
62684
- float dx = (v_uv.x - 0.5) * _dp_w;
62685
- float dy = (v_uv.y - 0.5) * _dp_h;
62686
- // Combine center (hi+lo) + delta with emulated double precision
62687
- vec2 cre = ds_add(vec2(_dp_cx_hi, _dp_cx_lo), ds_from(dx));
62688
- vec2 cim = ds_add(vec2(_dp_cy_hi, _dp_cy_lo), ds_from(dy));
62689
- return vec4(cre.x, cim.x, cre.y, cim.y);
62690
- }
62691
-
62692
- float _fractal_mandelbrot_dp(vec4 c, int maxIter) {
62693
- // c = (re_hi, im_hi, re_lo, im_lo)
62694
- vec2 cr = vec2(c.x, c.z); // real part as ds
62695
- vec2 ci = vec2(c.y, c.w); // imag part as ds
62696
- vec2 zr = vec2(0.0, 0.0);
62697
- vec2 zi = vec2(0.0, 0.0);
62698
- for (int i = 0; i < maxIter; i++) {
62699
- vec2 zr2 = ds_sqr(zr);
62700
- vec2 zi2 = ds_sqr(zi);
62701
- // |z|^2 > 4.0 ?
62702
- vec2 mag2 = ds_add(zr2, zi2);
62703
- if (mag2.x > 4.0)
62704
- return clamp((float(i) - log2(log2(mag2.x)) + 4.0) / float(maxIter), 0.0, 1.0);
62705
- // z = z^2 + c
62706
- vec2 new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci); // 2*zr*zi + ci
62707
- zr = ds_add(ds_sub(zr2, zi2), cr); // zr^2 - zi^2 + cr
62708
- zi = new_zi;
62709
- }
62710
- return 1.0;
62711
- }
62712
-
62713
- float _fractal_julia_dp(vec4 z_in, vec4 c, int maxIter) {
62714
- vec2 zr = vec2(z_in.x, z_in.z);
62715
- vec2 zi = vec2(z_in.y, z_in.w);
62716
- vec2 cr = vec2(c.x, c.z);
62717
- vec2 ci = vec2(c.y, c.w);
62718
- for (int i = 0; i < maxIter; i++) {
62719
- vec2 zr2 = ds_sqr(zr);
62720
- vec2 zi2 = ds_sqr(zi);
62721
- vec2 mag2 = ds_add(zr2, zi2);
62722
- if (mag2.x > 4.0)
62723
- return clamp((float(i) - log2(log2(mag2.x)) + 4.0) / float(maxIter), 0.0, 1.0);
62724
- vec2 new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62725
- zr = ds_add(ds_sub(zr2, zi2), cr);
62726
- zi = new_zi;
62727
- }
62728
- return 1.0;
62729
- }
62730
- `;
62731
- var GPU_FRACTAL_DP_PREAMBLE_WGSL = `
62732
- @group(0) @binding(10) var<uniform> _dp_cx_hi: f32;
62733
- @group(0) @binding(11) var<uniform> _dp_cx_lo: f32;
62734
- @group(0) @binding(12) var<uniform> _dp_cy_hi: f32;
62735
- @group(0) @binding(13) var<uniform> _dp_cy_lo: f32;
62736
- @group(0) @binding(14) var<uniform> _dp_w: f32;
62737
- @group(0) @binding(15) var<uniform> _dp_h: f32;
62738
-
62739
- fn _dp_coord(uv: vec2f) -> vec4f {
62740
- let dx = (uv.x - 0.5) * _dp_w;
62741
- let dy = (uv.y - 0.5) * _dp_h;
62742
- let cre = ds_add(vec2f(_dp_cx_hi, _dp_cx_lo), ds_from(dx));
62743
- let cim = ds_add(vec2f(_dp_cy_hi, _dp_cy_lo), ds_from(dy));
62744
- return vec4f(cre.x, cim.x, cre.y, cim.y);
62745
- }
62746
-
62747
- fn _fractal_mandelbrot_dp(c: vec4f, maxIter: i32) -> f32 {
62748
- let cr = vec2f(c.x, c.z);
62749
- let ci = vec2f(c.y, c.w);
62750
- var zr = vec2f(0.0, 0.0);
62751
- var zi = vec2f(0.0, 0.0);
62752
- for (var i: i32 = 0; i < maxIter; i++) {
62753
- let zr2 = ds_sqr(zr);
62754
- let zi2 = ds_sqr(zi);
62755
- let mag2 = ds_add(zr2, zi2);
62756
- if (mag2.x > 4.0) {
62757
- return clamp((f32(i) - log2(log2(mag2.x)) + 4.0) / f32(maxIter), 0.0, 1.0);
62758
- }
62759
- let new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62760
- zr = ds_add(ds_sub(zr2, zi2), cr);
62761
- zi = new_zi;
62762
- }
62763
- return 1.0;
62764
- }
62765
-
62766
- fn _fractal_julia_dp(z_in: vec4f, c: vec4f, maxIter: i32) -> f32 {
62767
- var zr = vec2f(z_in.x, z_in.z);
62768
- var zi = vec2f(z_in.y, z_in.w);
62769
- let cr = vec2f(c.x, c.z);
62770
- let ci = vec2f(c.y, c.w);
62771
- for (var i: i32 = 0; i < maxIter; i++) {
62772
- let zr2 = ds_sqr(zr);
62773
- let zi2 = ds_sqr(zi);
62774
- let mag2 = ds_add(zr2, zi2);
62775
- if (mag2.x > 4.0) {
62776
- return clamp((f32(i) - log2(log2(mag2.x)) + 4.0) / f32(maxIter), 0.0, 1.0);
62777
- }
62778
- let new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62779
- zr = ds_add(ds_sub(zr2, zi2), cr);
62780
- zi = new_zi;
62781
- }
62782
- return 1.0;
62783
- }
62784
63456
  `;
62785
63457
  var GPU_FRACTAL_PREAMBLE_GLSL = `
62786
63458
  float _fractal_mandelbrot(vec2 c, int maxIter) {
@@ -62824,208 +63496,6 @@ fn _fractal_julia(z_in: vec2f, c: vec2f, maxIter: i32) -> f32 {
62824
63496
  }
62825
63497
  return 1.0;
62826
63498
  }
62827
- `;
62828
- var GPU_FRACTAL_PT_PREAMBLE_GLSL = `
62829
- uniform sampler2D _refOrbit;
62830
- uniform int _refOrbitLen;
62831
- uniform int _refOrbitTexWidth;
62832
- uniform float _pt_offset_x;
62833
- uniform float _pt_offset_y;
62834
- uniform float _pt_w;
62835
- uniform float _pt_h;
62836
-
62837
- vec2 _pt_delta() {
62838
- float dx = _pt_offset_x + (v_uv.x - 0.5) * _pt_w;
62839
- float dy = _pt_offset_y + (v_uv.y - 0.5) * _pt_h;
62840
- return vec2(dx, dy);
62841
- }
62842
-
62843
- vec2 _pt_fetch_orbit(int i) {
62844
- int y = i / _refOrbitTexWidth;
62845
- int x = i - y * _refOrbitTexWidth;
62846
- return texelFetch(_refOrbit, ivec2(x, y), 0).rg;
62847
- }
62848
-
62849
- float _fractal_mandelbrot_pt(vec2 delta_c, int maxIter) {
62850
- float dr = 0.0;
62851
- float di = 0.0;
62852
- int orbitLen = min(maxIter, _refOrbitLen);
62853
- for (int i = 0; i < orbitLen; i++) {
62854
- vec2 Zn = _pt_fetch_orbit(i);
62855
- // delta_{n+1} = 2*Z_n*delta_n + delta_n^2 + delta_c
62856
- float new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62857
- float new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62858
- dr = new_dr;
62859
- di = new_di;
62860
- // Full z = Z_{n+1} + delta for escape check
62861
- vec2 Zn1 = (i + 1 < orbitLen) ? _pt_fetch_orbit(i + 1) : vec2(0.0);
62862
- float zr = Zn1.x + dr;
62863
- float zi = Zn1.y + di;
62864
- float mag2 = zr * zr + zi * zi;
62865
- if (mag2 > 4.0)
62866
- return clamp((float(i) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62867
- // Glitch detection: |delta|^2 > |Z|^2
62868
- float dmag2 = dr * dr + di * di;
62869
- float Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62870
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62871
- // Rebase to absolute coordinates and continue with single-float
62872
- float abs_zr = Zn1.x + dr;
62873
- float abs_zi = Zn1.y + di;
62874
- // Reconstruct absolute c from reference + delta
62875
- // (Use ds_from for the concept, but single-float suffices for fallback)
62876
- float cx = abs_zr - dr + delta_c.x;
62877
- float cy = abs_zi - di + delta_c.y;
62878
- for (int j = i + 1; j < maxIter; j++) {
62879
- float new_zr = abs_zr * abs_zr - abs_zi * abs_zi + cx;
62880
- abs_zi = 2.0 * abs_zr * abs_zi + cy;
62881
- abs_zr = new_zr;
62882
- mag2 = abs_zr * abs_zr + abs_zi * abs_zi;
62883
- if (mag2 > 4.0)
62884
- return clamp((float(j) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62885
- }
62886
- return 1.0;
62887
- }
62888
- }
62889
- return 1.0;
62890
- }
62891
-
62892
- float _fractal_julia_pt(vec2 z_delta, vec2 delta_c, int maxIter) {
62893
- float dr = z_delta.x;
62894
- float di = z_delta.y;
62895
- int orbitLen = min(maxIter, _refOrbitLen);
62896
- for (int i = 0; i < orbitLen; i++) {
62897
- vec2 Zn = _pt_fetch_orbit(i);
62898
- float new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62899
- float new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62900
- dr = new_dr;
62901
- di = new_di;
62902
- vec2 Zn1 = (i + 1 < orbitLen) ? _pt_fetch_orbit(i + 1) : vec2(0.0);
62903
- float zr = Zn1.x + dr;
62904
- float zi = Zn1.y + di;
62905
- float mag2 = zr * zr + zi * zi;
62906
- if (mag2 > 4.0)
62907
- return clamp((float(i) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62908
- float dmag2 = dr * dr + di * di;
62909
- float Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62910
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62911
- float abs_zr = Zn1.x + dr;
62912
- float abs_zi = Zn1.y + di;
62913
- float cx = delta_c.x;
62914
- float cy = delta_c.y;
62915
- for (int j = i + 1; j < maxIter; j++) {
62916
- float new_zr = abs_zr * abs_zr - abs_zi * abs_zi + cx;
62917
- abs_zi = 2.0 * abs_zr * abs_zi + cy;
62918
- abs_zr = new_zr;
62919
- mag2 = abs_zr * abs_zr + abs_zi * abs_zi;
62920
- if (mag2 > 4.0)
62921
- return clamp((float(j) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62922
- }
62923
- return 1.0;
62924
- }
62925
- }
62926
- return 1.0;
62927
- }
62928
- `;
62929
- var GPU_FRACTAL_PT_PREAMBLE_WGSL = `
62930
- @group(0) @binding(1) var _refOrbit: texture_2d<f32>;
62931
- var<uniform> _refOrbitLen: i32;
62932
- var<uniform> _refOrbitTexWidth: i32;
62933
- var<uniform> _pt_offset_x: f32;
62934
- var<uniform> _pt_offset_y: f32;
62935
- var<uniform> _pt_w: f32;
62936
- var<uniform> _pt_h: f32;
62937
-
62938
- fn _pt_delta(uv: vec2f) -> vec2f {
62939
- let dx = _pt_offset_x + (uv.x - 0.5) * _pt_w;
62940
- let dy = _pt_offset_y + (uv.y - 0.5) * _pt_h;
62941
- return vec2f(dx, dy);
62942
- }
62943
-
62944
- fn _pt_fetch_orbit(i: i32) -> vec2f {
62945
- let y = i / _refOrbitTexWidth;
62946
- let x = i - y * _refOrbitTexWidth;
62947
- return textureLoad(_refOrbit, vec2i(x, y), 0).rg;
62948
- }
62949
-
62950
- fn _fractal_mandelbrot_pt(delta_c: vec2f, maxIter: i32) -> f32 {
62951
- var dr: f32 = 0.0;
62952
- var di: f32 = 0.0;
62953
- let orbitLen = min(maxIter, _refOrbitLen);
62954
- for (var i: i32 = 0; i < orbitLen; i++) {
62955
- let Zn = _pt_fetch_orbit(i);
62956
- let new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62957
- let new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62958
- dr = new_dr;
62959
- di = new_di;
62960
- var Zn1 = vec2f(0.0);
62961
- if (i + 1 < orbitLen) { Zn1 = _pt_fetch_orbit(i + 1); }
62962
- let zr = Zn1.x + dr;
62963
- let zi = Zn1.y + di;
62964
- var mag2 = zr * zr + zi * zi;
62965
- if (mag2 > 4.0) {
62966
- return clamp((f32(i) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
62967
- }
62968
- let dmag2 = dr * dr + di * di;
62969
- let Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62970
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62971
- var f_zr = Zn1.x + dr;
62972
- var f_zi = Zn1.y + di;
62973
- let cx = delta_c.x;
62974
- let cy = delta_c.y;
62975
- for (var j: i32 = i + 1; j < maxIter; j++) {
62976
- let t_zr = f_zr * f_zr - f_zi * f_zi + cx;
62977
- f_zi = 2.0 * f_zr * f_zi + cy;
62978
- f_zr = t_zr;
62979
- mag2 = f_zr * f_zr + f_zi * f_zi;
62980
- if (mag2 > 4.0) {
62981
- return clamp((f32(j) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
62982
- }
62983
- }
62984
- return 1.0;
62985
- }
62986
- }
62987
- return 1.0;
62988
- }
62989
-
62990
- fn _fractal_julia_pt(z_delta: vec2f, delta_c: vec2f, maxIter: i32) -> f32 {
62991
- var dr = z_delta.x;
62992
- var di = z_delta.y;
62993
- let orbitLen = min(maxIter, _refOrbitLen);
62994
- for (var i: i32 = 0; i < orbitLen; i++) {
62995
- let Zn = _pt_fetch_orbit(i);
62996
- let new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62997
- let new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62998
- dr = new_dr;
62999
- di = new_di;
63000
- var Zn1 = vec2f(0.0);
63001
- if (i + 1 < orbitLen) { Zn1 = _pt_fetch_orbit(i + 1); }
63002
- let zr = Zn1.x + dr;
63003
- let zi = Zn1.y + di;
63004
- var mag2 = zr * zr + zi * zi;
63005
- if (mag2 > 4.0) {
63006
- return clamp((f32(i) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
63007
- }
63008
- let dmag2 = dr * dr + di * di;
63009
- let Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
63010
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
63011
- var f_zr = Zn1.x + dr;
63012
- var f_zi = Zn1.y + di;
63013
- let cx = delta_c.x;
63014
- let cy = delta_c.y;
63015
- for (var j: i32 = i + 1; j < maxIter; j++) {
63016
- let t_zr = f_zr * f_zr - f_zi * f_zi + cx;
63017
- f_zi = 2.0 * f_zr * f_zi + cy;
63018
- f_zr = t_zr;
63019
- mag2 = f_zr * f_zr + f_zi * f_zi;
63020
- if (mag2 > 4.0) {
63021
- return clamp((f32(j) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
63022
- }
63023
- }
63024
- return 1.0;
63025
- }
63026
- }
63027
- return 1.0;
63028
- }
63029
63499
  `;
63030
63500
  var GPU_COLOR_PREAMBLE_GLSL = `
63031
63501
  float _gpu_srgb_to_linear(float c) {
@@ -63067,28 +63537,124 @@ vec3 _gpu_oklab_to_srgb(vec3 lab) {
63067
63537
 
63068
63538
  vec3 _gpu_oklab_to_oklch(vec3 lab) {
63069
63539
  float C = length(lab.yz);
63070
- float H = atan(lab.z, lab.y);
63540
+ float H = atan(lab.z, lab.y) * (180.0 / 3.14159265359);
63541
+ if (H < 0.0) H += 360.0;
63071
63542
  return vec3(lab.x, C, H);
63072
63543
  }
63073
63544
 
63074
63545
  vec3 _gpu_oklch_to_oklab(vec3 lch) {
63075
- return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
63546
+ float h_rad = lch.z * (3.14159265359 / 180.0);
63547
+ return vec3(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
63548
+ }
63549
+
63550
+ vec3 _gpu_srgb_to_oklch(vec3 rgb) {
63551
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
63552
+ }
63553
+
63554
+ vec3 _gpu_oklch_to_srgb(vec3 lch) {
63555
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
63076
63556
  }
63077
63557
 
63078
- vec3 _gpu_color_mix(vec3 rgb1, vec3 rgb2, float t) {
63079
- vec3 lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
63080
- vec3 lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
63558
+ // HSL conversion. Hue in degrees, saturation/lightness in 0-1.
63559
+ vec3 _gpu_hsl_to_rgb(vec3 hsl) {
63560
+ float h = hsl.x;
63561
+ float s = hsl.y;
63562
+ float l = hsl.z;
63563
+ float c = (1.0 - abs(2.0 * l - 1.0)) * s;
63564
+ float h6 = h / 60.0;
63565
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
63566
+ float r = 0.0;
63567
+ float g = 0.0;
63568
+ float b = 0.0;
63569
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63570
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63571
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63572
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63573
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63574
+ else { r = c; g = 0.0; b = x; }
63575
+ float m = l - c / 2.0;
63576
+ return vec3(r + m, g + m, b + m);
63577
+ }
63578
+
63579
+ vec3 _gpu_rgb_to_hsl(vec3 rgb) {
63580
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
63581
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
63582
+ float l = (maxc + minc) / 2.0;
63583
+ float d = maxc - minc;
63584
+ if (d < 1e-6) return vec3(0.0, 0.0, l);
63585
+ float s = d / (1.0 - abs(2.0 * l - 1.0));
63586
+ float h;
63587
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
63588
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
63589
+ else h = (rgb.x - rgb.y) / d + 4.0;
63590
+ h *= 60.0;
63591
+ if (h < 0.0) h += 360.0;
63592
+ return vec3(h, s, l);
63593
+ }
63594
+
63595
+ // HSV conversion. Hue in degrees, saturation/value in 0-1.
63596
+ vec3 _gpu_hsv_to_rgb(vec3 hsv) {
63597
+ float h = hsv.x;
63598
+ float s = hsv.y;
63599
+ float v = hsv.z;
63600
+ float c = v * s;
63601
+ float h6 = h / 60.0;
63602
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
63603
+ float r = 0.0;
63604
+ float g = 0.0;
63605
+ float b = 0.0;
63606
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63607
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63608
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63609
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63610
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63611
+ else { r = c; g = 0.0; b = x; }
63612
+ float m = v - c;
63613
+ return vec3(r + m, g + m, b + m);
63614
+ }
63615
+
63616
+ vec3 _gpu_rgb_to_hsv(vec3 rgb) {
63617
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
63618
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
63619
+ float v = maxc;
63620
+ float d = maxc - minc;
63621
+ if (d < 1e-6) return vec3(0.0, 0.0, v);
63622
+ float s = (maxc < 1e-6) ? 0.0 : d / maxc;
63623
+ float h;
63624
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
63625
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
63626
+ else h = (rgb.x - rgb.y) / d + 4.0;
63627
+ h *= 60.0;
63628
+ if (h < 0.0) h += 360.0;
63629
+ return vec3(h, s, v);
63630
+ }
63631
+
63632
+ vec3 _gpu_color_mix(vec3 lch1, vec3 lch2, float t) {
63081
63633
  float L = mix(lch1.x, lch2.x, t);
63082
63634
  float C = mix(lch1.y, lch2.y, t);
63083
- float dh = lch2.z - lch1.z;
63084
- const float PI = 3.14159265359;
63085
- if (dh > PI) dh -= 2.0 * PI;
63086
- if (dh < -PI) dh += 2.0 * PI;
63087
- float H = lch1.z + dh * t;
63088
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3(L, C, H)));
63635
+ bool a1 = lch1.y < 1e-6;
63636
+ bool a2 = lch2.y < 1e-6;
63637
+ float H;
63638
+ if (a1 && a2) {
63639
+ H = lch1.z;
63640
+ } else if (a1) {
63641
+ H = lch2.z;
63642
+ } else if (a2) {
63643
+ H = lch1.z;
63644
+ } else {
63645
+ float dh = lch2.z - lch1.z;
63646
+ if (dh > 180.0) dh -= 360.0;
63647
+ if (dh < -180.0) dh += 360.0;
63648
+ H = lch1.z + dh * t;
63649
+ if (H < 0.0) H += 360.0;
63650
+ if (H >= 360.0) H -= 360.0;
63651
+ }
63652
+ return vec3(L, C, H);
63089
63653
  }
63090
63654
 
63091
- float _gpu_apca(vec3 bg, vec3 fg) {
63655
+ float _gpu_apca(vec3 lch_bg, vec3 lch_fg) {
63656
+ vec3 bg = _gpu_oklch_to_srgb(lch_bg);
63657
+ vec3 fg = _gpu_oklch_to_srgb(lch_fg);
63092
63658
  float bgR = _gpu_srgb_to_linear(bg.x);
63093
63659
  float bgG = _gpu_srgb_to_linear(bg.y);
63094
63660
  float bgB = _gpu_srgb_to_linear(bg.z);
@@ -63099,9 +63665,7 @@ float _gpu_apca(vec3 bg, vec3 fg) {
63099
63665
  float fgY = 0.2126729 * fgR + 0.7151522 * fgG + 0.0721750 * fgB;
63100
63666
  float bgC = pow(bgY, 0.56);
63101
63667
  float fgC = pow(fgY, 0.57);
63102
- float contrast = (bgC > fgC)
63103
- ? (bgC - fgC) * 1.14
63104
- : (bgC - fgC) * 1.14;
63668
+ float contrast = (bgC - fgC) * 1.14;
63105
63669
  return contrast * 100.0;
63106
63670
  }
63107
63671
  `;
@@ -63145,28 +63709,133 @@ fn _gpu_oklab_to_srgb(lab: vec3f) -> vec3f {
63145
63709
 
63146
63710
  fn _gpu_oklab_to_oklch(lab: vec3f) -> vec3f {
63147
63711
  let C = length(lab.yz);
63148
- let H = atan2(lab.z, lab.y);
63712
+ var H = atan2(lab.z, lab.y) * (180.0 / 3.14159265359);
63713
+ if (H < 0.0) { H = H + 360.0; }
63149
63714
  return vec3f(lab.x, C, H);
63150
63715
  }
63151
63716
 
63152
63717
  fn _gpu_oklch_to_oklab(lch: vec3f) -> vec3f {
63153
- return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
63718
+ let h_rad = lch.z * (3.14159265359 / 180.0);
63719
+ return vec3f(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
63720
+ }
63721
+
63722
+ fn _gpu_srgb_to_oklch(rgb: vec3f) -> vec3f {
63723
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
63724
+ }
63725
+
63726
+ fn _gpu_oklch_to_srgb(lch: vec3f) -> vec3f {
63727
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
63728
+ }
63729
+
63730
+ fn _gpu_hsl_to_rgb(hsl: vec3f) -> vec3f {
63731
+ let h = hsl.x;
63732
+ let s = hsl.y;
63733
+ let l = hsl.z;
63734
+ let c = (1.0 - abs(2.0 * l - 1.0)) * s;
63735
+ let h6 = h / 60.0;
63736
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
63737
+ var r: f32 = 0.0;
63738
+ var g: f32 = 0.0;
63739
+ var b: f32 = 0.0;
63740
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63741
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63742
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63743
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63744
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63745
+ else { r = c; g = 0.0; b = x; }
63746
+ let m = l - c / 2.0;
63747
+ return vec3f(r + m, g + m, b + m);
63154
63748
  }
63155
63749
 
63156
- fn _gpu_color_mix(rgb1: vec3f, rgb2: vec3f, t: f32) -> vec3f {
63157
- let lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
63158
- let lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
63750
+ fn _gpu_rgb_to_hsl(rgb: vec3f) -> vec3f {
63751
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
63752
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
63753
+ let l = (maxc + minc) / 2.0;
63754
+ let d = maxc - minc;
63755
+ if (d < 1e-6) { return vec3f(0.0, 0.0, l); }
63756
+ let s = d / (1.0 - abs(2.0 * l - 1.0));
63757
+ var h: f32;
63758
+ if (maxc == rgb.x) {
63759
+ let v = (rgb.y - rgb.z) / d;
63760
+ h = v - 6.0 * floor(v / 6.0);
63761
+ } else if (maxc == rgb.y) {
63762
+ h = (rgb.z - rgb.x) / d + 2.0;
63763
+ } else {
63764
+ h = (rgb.x - rgb.y) / d + 4.0;
63765
+ }
63766
+ h = h * 60.0;
63767
+ if (h < 0.0) { h = h + 360.0; }
63768
+ return vec3f(h, s, l);
63769
+ }
63770
+
63771
+ fn _gpu_hsv_to_rgb(hsv: vec3f) -> vec3f {
63772
+ let h = hsv.x;
63773
+ let s = hsv.y;
63774
+ let v = hsv.z;
63775
+ let c = v * s;
63776
+ let h6 = h / 60.0;
63777
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
63778
+ var r: f32 = 0.0;
63779
+ var g: f32 = 0.0;
63780
+ var b: f32 = 0.0;
63781
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63782
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63783
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63784
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63785
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63786
+ else { r = c; g = 0.0; b = x; }
63787
+ let m = v - c;
63788
+ return vec3f(r + m, g + m, b + m);
63789
+ }
63790
+
63791
+ fn _gpu_rgb_to_hsv(rgb: vec3f) -> vec3f {
63792
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
63793
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
63794
+ let v = maxc;
63795
+ let d = maxc - minc;
63796
+ if (d < 1e-6) { return vec3f(0.0, 0.0, v); }
63797
+ var s: f32 = 0.0;
63798
+ if (maxc >= 1e-6) { s = d / maxc; }
63799
+ var h: f32;
63800
+ if (maxc == rgb.x) {
63801
+ let q = (rgb.y - rgb.z) / d;
63802
+ h = q - 6.0 * floor(q / 6.0);
63803
+ } else if (maxc == rgb.y) {
63804
+ h = (rgb.z - rgb.x) / d + 2.0;
63805
+ } else {
63806
+ h = (rgb.x - rgb.y) / d + 4.0;
63807
+ }
63808
+ h = h * 60.0;
63809
+ if (h < 0.0) { h = h + 360.0; }
63810
+ return vec3f(h, s, v);
63811
+ }
63812
+
63813
+ fn _gpu_color_mix(lch1: vec3f, lch2: vec3f, t: f32) -> vec3f {
63159
63814
  let L = mix(lch1.x, lch2.x, t);
63160
63815
  let C = mix(lch1.y, lch2.y, t);
63161
- let PI = 3.14159265359;
63162
- var dh = lch2.z - lch1.z;
63163
- if (dh > PI) { dh -= 2.0 * PI; }
63164
- if (dh < -PI) { dh += 2.0 * PI; }
63165
- let H = lch1.z + dh * t;
63166
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3f(L, C, H)));
63816
+ let a1 = lch1.y < 1e-6;
63817
+ let a2 = lch2.y < 1e-6;
63818
+ var H: f32;
63819
+ if (a1 && a2) {
63820
+ H = lch1.z;
63821
+ } else if (a1) {
63822
+ H = lch2.z;
63823
+ } else if (a2) {
63824
+ H = lch1.z;
63825
+ } else {
63826
+ var dh = lch2.z - lch1.z;
63827
+ if (dh > 180.0) { dh = dh - 360.0; }
63828
+ if (dh < -180.0) { dh = dh + 360.0; }
63829
+ H = lch1.z + dh * t;
63830
+ if (H < 0.0) { H = H + 360.0; }
63831
+ if (H >= 360.0) { H = H - 360.0; }
63832
+ }
63833
+ return vec3f(L, C, H);
63167
63834
  }
63168
63835
 
63169
- fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63836
+ fn _gpu_apca(lch_bg: vec3f, lch_fg: vec3f) -> f32 {
63837
+ let bg = _gpu_oklch_to_srgb(lch_bg);
63838
+ let fg = _gpu_oklch_to_srgb(lch_fg);
63170
63839
  let bgR = _gpu_srgb_to_linear(bg.x);
63171
63840
  let bgG = _gpu_srgb_to_linear(bg.y);
63172
63841
  let bgB = _gpu_srgb_to_linear(bg.z);
@@ -63454,7 +64123,7 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63454
64123
  if (stmts.length === 0) return "";
63455
64124
  const last = stmts.length - 1;
63456
64125
  stmts[last] = `return ${stmts[last]}`;
63457
- return stmts.join(";\n");
64126
+ return stmts.join(";\n") + ";";
63458
64127
  },
63459
64128
  ...options
63460
64129
  };
@@ -63465,7 +64134,6 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63465
64134
  const constants = this.getConstants();
63466
64135
  const v2 = this.languageId === "wgsl" ? "vec2f" : "vec2";
63467
64136
  const target = this.createTarget({
63468
- hints: options.hints,
63469
64137
  functions: (id) => {
63470
64138
  if (userFunctions && id in userFunctions) {
63471
64139
  const fn = userFunctions[id];
@@ -63504,89 +64172,12 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63504
64172
  if (code.includes("_gpu_besselJ"))
63505
64173
  preamble += this.languageId === "wgsl" ? GPU_BESSELJ_PREAMBLE_WGSL : GPU_BESSELJ_PREAMBLE_GLSL;
63506
64174
  if (code.includes("_fractal_")) {
63507
- if (code.includes("_fractal_mandelbrot_pt") || code.includes("_fractal_julia_pt")) {
63508
- preamble += this.languageId === "wgsl" ? GPU_DS_ARITHMETIC_PREAMBLE_WGSL : GPU_DS_ARITHMETIC_PREAMBLE_GLSL;
63509
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PT_PREAMBLE_WGSL : GPU_FRACTAL_PT_PREAMBLE_GLSL;
63510
- } else if (code.includes("_fractal_mandelbrot_dp") || code.includes("_fractal_julia_dp")) {
63511
- preamble += this.languageId === "wgsl" ? GPU_DS_ARITHMETIC_PREAMBLE_WGSL : GPU_DS_ARITHMETIC_PREAMBLE_GLSL;
63512
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_DP_PREAMBLE_WGSL : GPU_FRACTAL_DP_PREAMBLE_GLSL;
63513
- } else {
63514
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
63515
- }
64175
+ preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
63516
64176
  }
63517
64177
  if (code.includes("_gpu_srgb_to") || code.includes("_gpu_oklab") || code.includes("_gpu_oklch") || code.includes("_gpu_color_mix") || code.includes("_gpu_apca")) {
63518
64178
  preamble += this.languageId === "wgsl" ? GPU_COLOR_PREAMBLE_WGSL : GPU_COLOR_PREAMBLE_GLSL;
63519
64179
  }
63520
64180
  if (preamble) result.preamble = preamble;
63521
- if (code.includes("_fractal_") && options.hints?.viewport) {
63522
- const strategy = selectFractalStrategy(target);
63523
- const radius = options.hints.viewport.radius;
63524
- switch (strategy) {
63525
- case "single":
63526
- result.staleWhen = { radiusBelow: 1e-6 };
63527
- break;
63528
- case "double":
63529
- result.staleWhen = { radiusBelow: 1e-14, radiusAbove: 1e-5 };
63530
- break;
63531
- case "perturbation":
63532
- result.staleWhen = {
63533
- radiusAbove: 1e-5,
63534
- radiusBelow: radius * 0.01,
63535
- centerDistance: radius * 2
63536
- };
63537
- break;
63538
- }
63539
- }
63540
- if ((code.includes("_fractal_mandelbrot_dp") || code.includes("_fractal_julia_dp")) && options.hints?.viewport) {
63541
- const cx = hpToNumber(options.hints.viewport.center[0]);
63542
- const cy = hpToNumber(options.hints.viewport.center[1]);
63543
- const size = options.hints.viewport.radius * 2;
63544
- const cx_hi = Math.fround(cx);
63545
- const cy_hi = Math.fround(cy);
63546
- result.uniforms = {
63547
- ...result.uniforms,
63548
- _dp_cx_hi: cx_hi,
63549
- _dp_cx_lo: cx - cx_hi,
63550
- _dp_cy_hi: cy_hi,
63551
- _dp_cy_lo: cy - cy_hi,
63552
- _dp_w: size,
63553
- _dp_h: size
63554
- };
63555
- }
63556
- if ((code.includes("_fractal_mandelbrot_pt") || code.includes("_fractal_julia_pt")) && options.hints?.viewport) {
63557
- const viewport = options.hints.viewport;
63558
- const size = viewport.radius * 2;
63559
- result.uniforms = {
63560
- ...result.uniforms,
63561
- _pt_offset_x: 0,
63562
- _pt_offset_y: 0,
63563
- _pt_w: size,
63564
- _pt_h: size
63565
- };
63566
- const digits = Math.max(50, Math.ceil(-Math.log10(viewport.radius)) + 10);
63567
- const maxIter = 1e3;
63568
- const orbit = computeReferenceOrbit(
63569
- viewport.center,
63570
- maxIter,
63571
- digits
63572
- );
63573
- const orbitLen = orbit.length / 2;
63574
- const texWidth = Math.min(orbitLen, 4096);
63575
- const texHeight = Math.ceil(orbitLen / texWidth);
63576
- result.textures = {
63577
- _refOrbit: {
63578
- data: orbit,
63579
- width: texWidth,
63580
- height: texHeight,
63581
- format: "rg32f"
63582
- }
63583
- };
63584
- result.uniforms = {
63585
- ...result.uniforms,
63586
- _refOrbitLen: orbitLen,
63587
- _refOrbitTexWidth: texWidth
63588
- };
63589
- }
63590
64181
  return result;
63591
64182
  }
63592
64183
  compileToSource(expr2, _options = {}) {
@@ -63631,7 +64222,7 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63631
64222
  if (body.includes("\n")) {
63632
64223
  const indented = body.split("\n").map((l) => ` ${l}`).join("\n");
63633
64224
  return `${returnType} ${functionName}(${params}) {
63634
- ${indented};
64225
+ ${indented}
63635
64226
  }`;
63636
64227
  }
63637
64228
  return `${returnType} ${functionName}(${params}) {
@@ -63742,7 +64333,7 @@ ${indented};
63742
64333
  return `fn ${functionName}(${params}) -> ${toWGSLType(
63743
64334
  returnType
63744
64335
  )} {
63745
- ${indented};
64336
+ ${indented}
63746
64337
  }`;
63747
64338
  }
63748
64339
  return `fn ${functionName}(${params}) -> ${toWGSLType(returnType)} {
@@ -67727,6 +68318,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
67727
68318
  this.pushScope(void 0, "global");
67728
68319
  this._compilationTargets.registerDefaults();
67729
68320
  if (options?.latexSyntax) this._latexSyntax = options.latexSyntax;
68321
+ if (options?.latexOptions) this._latexOptions = { ...options.latexOptions };
67730
68322
  hidePrivateProperties(this);
67731
68323
  }
67732
68324
  toJSON() {
@@ -68387,6 +68979,29 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
68387
68979
  );
68388
68980
  return this._latexSyntax;
68389
68981
  }
68982
+ /** @internal Engine-wide LaTeX parse/serialize options (e.g. decimalSeparator).
68983
+ * Merged into every `parse()` and `toLatex()` call between the LatexSyntax
68984
+ * instance defaults and any per-call overrides. */
68985
+ _latexOptions = {};
68986
+ /** Engine-wide LaTeX parse/serialize options.
68987
+ *
68988
+ * These options are merged into every `parse()` and `toLatex()` call.
68989
+ * Precedence (most-specific wins):
68990
+ * 1. LatexSyntax instance defaults (set at its construction)
68991
+ * 2. `ce.latexOptions` (this property)
68992
+ * 3. Per-call options passed to `ce.parse()` / `expr.toLatex()`
68993
+ *
68994
+ * Assigning replaces the whole bag. Use spread to merge:
68995
+ * ```ts
68996
+ * ce.latexOptions = { ...ce.latexOptions, decimalSeparator: '{,}' };
68997
+ * ```
68998
+ */
68999
+ get latexOptions() {
69000
+ return this._latexOptions;
69001
+ }
69002
+ set latexOptions(options) {
69003
+ this._latexOptions = { ...options };
69004
+ }
68390
69005
  parse(latex, options) {
68391
69006
  if (latex === null || latex === void 0) return null;
68392
69007
  if (typeof latex !== "string")
@@ -68394,7 +69009,6 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
68394
69009
  const syntax = this._requireLatexSyntax();
68395
69010
  const { form, ...parseOpts } = options ?? {};
68396
69011
  const result = syntax.parse(latex, {
68397
- decimalSeparator: ".",
68398
69012
  getSymbolType: (id) => {
68399
69013
  const def = this.lookupDefinition(id);
68400
69014
  if (!def) return BoxedType.unknown;
@@ -68406,6 +69020,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
68406
69020
  const def = this.lookupDefinition(id);
68407
69021
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
68408
69022
  },
69023
+ ...this._latexOptions,
68409
69024
  ...parseOpts
68410
69025
  });
68411
69026
  if (result === null) return null;
@@ -68569,7 +69184,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
68569
69184
  _setDefaultEngineFactory(() => new ComputeEngine());
68570
69185
 
68571
69186
  // src/core.ts
68572
- var version = "0.55.5";
69187
+ var version = "0.56.0";
68573
69188
  return __toCommonJS(core_exports);
68574
69189
  })();
68575
69190
  /*! Bundled license information: