@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
@@ -1,4 +1,4 @@
1
- /** Compute Engine 0.55.5 */
1
+ /** Compute Engine 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.ComputeEngine = {}));})(this, (function (exports) { 'use strict';
3
3
  var ComputeEngine = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -2316,6 +2316,7 @@ var ComputeEngine = (() => {
2316
2316
  ];
2317
2317
  var VALUE_TYPES = [
2318
2318
  "value",
2319
+ "color",
2319
2320
  ...COLLECTION_TYPES,
2320
2321
  ...SCALAR_TYPES
2321
2322
  ];
@@ -4015,6 +4016,7 @@ var ComputeEngine = (() => {
4015
4016
  symbol: [],
4016
4017
  boolean: [],
4017
4018
  string: [],
4019
+ color: [],
4018
4020
  expression: EXPRESSION_TYPES
4019
4021
  };
4020
4022
  function isPrimitiveSubtype(lhs, rhs) {
@@ -7898,9 +7900,10 @@ var ComputeEngine = (() => {
7898
7900
  if (!materialized.isLazyCollection) return materialized.latex;
7899
7901
  }
7900
7902
  const syntax = this.engine._requireLatexSyntax();
7901
- return syntax.serialize(
7902
- this.toMathJson({ prettify: true, fractionalDigits: "auto" })
7903
- );
7903
+ const json = this.toMathJson({ prettify: true, fractionalDigits: "auto" });
7904
+ const latexOpts = this.engine.latexOptions;
7905
+ if (Object.keys(latexOpts).length === 0) return syntax.serialize(json);
7906
+ return syntax.serialize(json, { ...latexOpts });
7904
7907
  }
7905
7908
  /**
7906
7909
  * Return a LaTeX representation of this expression with custom
@@ -7924,9 +7927,13 @@ var ComputeEngine = (() => {
7924
7927
  fractionalDigits: "auto"
7925
7928
  });
7926
7929
  const syntax = this.engine._requireLatexSyntax();
7927
- if (!options || Object.keys(options).length === 0)
7928
- return syntax.serialize(json);
7929
- return syntax.serialize(json, options);
7930
+ const latexOpts = this.engine.latexOptions;
7931
+ const haveEngineOpts = Object.keys(latexOpts).length > 0;
7932
+ const haveCallOpts = options && Object.keys(options).length > 0;
7933
+ if (!haveEngineOpts && !haveCallOpts) return syntax.serialize(json);
7934
+ if (!haveEngineOpts) return syntax.serialize(json, options);
7935
+ if (!haveCallOpts) return syntax.serialize(json, { ...latexOpts });
7936
+ return syntax.serialize(json, { ...latexOpts, ...options });
7930
7937
  }
7931
7938
  /** Called by `JSON.stringify()` when serializing to json.
7932
7939
  *
@@ -7970,11 +7977,13 @@ var ComputeEngine = (() => {
7970
7977
  "number",
7971
7978
  "dictionary"
7972
7979
  ];
7973
- }
7974
- if (Array.isArray(options.shorthands))
7980
+ } else if (Array.isArray(options.shorthands)) {
7975
7981
  defaultOptions.shorthands = options.shorthands;
7982
+ }
7976
7983
  if (typeof options.metadata === "string" && options.metadata === "all" || options.metadata?.includes("all")) {
7977
7984
  defaultOptions.metadata = ["latex", "wikidata"];
7985
+ } else if (Array.isArray(options.metadata)) {
7986
+ defaultOptions.metadata = options.metadata;
7978
7987
  }
7979
7988
  if (options.fractionalDigits === "auto")
7980
7989
  defaultOptions.fractionalDigits = -this.engine.precision;
@@ -11113,10 +11122,6 @@ var ComputeEngine = (() => {
11113
11122
  // Lagrange notation
11114
11123
  {
11115
11124
  name: "Derivative",
11116
- // @todo: Leibniz notation: {% latex " \\frac{d^n}{dx^n} f(x)" %}
11117
- // @todo: Euler modified notation: This notation is used by Mathematica. The Euler notation uses `D` instead of
11118
- // `\partial`: `\partial_{x} f`, `\partial_{x,y} f`
11119
- // Newton notation (\dot{v}, \ddot{v}) is implemented below
11120
11125
  serialize: (serializer, expr2) => {
11121
11126
  const degree = machineValue(operand(expr2, 2)) ?? 1;
11122
11127
  const base = serializer.serialize(operand(expr2, 1));
@@ -12154,14 +12159,18 @@ var ComputeEngine = (() => {
12154
12159
  return ["Complement", lhs];
12155
12160
  }
12156
12161
  // precedence: 240,
12157
- // @todo: serialize for the multiple argument case
12158
12162
  },
12159
12163
  {
12160
12164
  name: "Complement",
12161
12165
  latexTrigger: ["^", "<{>", "\\complement", "<}>"],
12162
- kind: "postfix"
12166
+ kind: "postfix",
12163
12167
  // precedence: 240,
12164
- // @todo: serialize for the multiple argument case
12168
+ serialize: (serializer, expr2) => {
12169
+ return joinLatex([
12170
+ serializer.serialize(operand(expr2, 1)),
12171
+ "^\\complement"
12172
+ ]);
12173
+ }
12165
12174
  },
12166
12175
  {
12167
12176
  name: "Intersection",
@@ -12248,7 +12257,6 @@ var ComputeEngine = (() => {
12248
12257
  // commands like \rbrack a, b \rbrack which are unambiguous.
12249
12258
  {
12250
12259
  name: "Multiple",
12251
- // @todo: parse
12252
12260
  serialize: serializeSet
12253
12261
  },
12254
12262
  {
@@ -12257,14 +12265,28 @@ var ComputeEngine = (() => {
12257
12265
  kind: "infix",
12258
12266
  precedence: 350
12259
12267
  },
12268
+ // \mid as a separator/operator (used in set-builder notation: {x \mid x > 0})
12269
+ // Low precedence so it binds loosely — everything on each side is parsed first
12270
+ {
12271
+ name: "Divides",
12272
+ latexTrigger: ["\\mid"],
12273
+ kind: "infix",
12274
+ precedence: 160
12275
+ },
12260
12276
  {
12261
12277
  name: "Set",
12262
12278
  kind: "matchfix",
12263
12279
  openTrigger: "{",
12264
12280
  closeTrigger: "}",
12265
- // @todo: the set syntax can also include conditions...
12266
12281
  parse: (_parser, body) => {
12267
12282
  if (isEmptySequence(body)) return "EmptySet";
12283
+ const h = operator(body);
12284
+ if (h === "Divides" || h === "Colon") {
12285
+ const expr2 = operand(body, 1);
12286
+ const condition = operand(body, 2);
12287
+ if (expr2 !== null && condition !== null)
12288
+ return ["Set", expr2, ["Condition", condition]];
12289
+ }
12268
12290
  if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12269
12291
  body = operand(body, 1);
12270
12292
  }
@@ -12272,6 +12294,17 @@ var ComputeEngine = (() => {
12272
12294
  return ["Set", ...operands(body)];
12273
12295
  },
12274
12296
  serialize: (serializer, expr2) => {
12297
+ if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
12298
+ const condition = operand(expr2, 2);
12299
+ return joinLatex([
12300
+ "\\lbrace",
12301
+ serializer.serialize(operand(expr2, 1)),
12302
+ "\\mid",
12303
+ // Serialize the inner expression of the Condition wrapper
12304
+ serializer.serialize(operand(condition, 1)),
12305
+ "\\rbrace"
12306
+ ]);
12307
+ }
12275
12308
  return joinLatex([
12276
12309
  "\\lbrace",
12277
12310
  operands(expr2).map((x) => serializer.serialize(x)).join(", "),
@@ -12438,23 +12471,6 @@ var ComputeEngine = (() => {
12438
12471
  if (expr2 === null) return "";
12439
12472
  const h = operator(expr2);
12440
12473
  if (!h) return "";
12441
- if (h === "Set") {
12442
- if (nops(expr2) === 0) return "\\emptyset";
12443
- if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
12444
- return joinLatex([
12445
- "\\left\\lbrace",
12446
- serializer.serialize(operand(expr2, 1)),
12447
- "\\middle\\mid",
12448
- serializer.serialize(operand(expr2, 2)),
12449
- "\\right\\rbrace"
12450
- ]);
12451
- }
12452
- return joinLatex([
12453
- "\\left\\lbrace",
12454
- ...operands(expr2).map((x) => serializer.serialize(x) + " ,"),
12455
- "\\right\\rbrace"
12456
- ]);
12457
- }
12458
12474
  if (h === "Multiple") {
12459
12475
  }
12460
12476
  if (h === "Range") {
@@ -13602,11 +13618,13 @@ var ComputeEngine = (() => {
13602
13618
  if (!parser.match("_")) return null;
13603
13619
  const base = parser.parseGroup();
13604
13620
  if (operator(base) !== "To") return null;
13605
- const expr2 = parser.parseArguments("implicit");
13621
+ const expr2 = parser.parseExpression({
13622
+ minPrec: MULTIPLICATION_PRECEDENCE
13623
+ });
13606
13624
  if (!expr2) return null;
13607
13625
  return [
13608
13626
  "Limit",
13609
- ["Function", expr2[0], operand(base, 1)],
13627
+ ["Function", expr2, operand(base, 1)],
13610
13628
  operand(base, 2)
13611
13629
  ];
13612
13630
  },
@@ -13687,6 +13705,8 @@ var ComputeEngine = (() => {
13687
13705
  precedence: DIVISION_PRECEDENCE,
13688
13706
  parse: "Mod"
13689
13707
  },
13708
+ // Function-style alias: `\operatorname{mod}(a, b)`
13709
+ { latexTrigger: "\\operatorname{mod}", parse: "Mod" },
13690
13710
  {
13691
13711
  latexTrigger: "\\pmod",
13692
13712
  kind: "prefix",
@@ -13927,6 +13947,13 @@ var ComputeEngine = (() => {
13927
13947
  const rhs = serializer.wrap(operand(expr2, 2), ADDITION_PRECEDENCE + 3);
13928
13948
  return joinLatex([lhs, "-", rhs]);
13929
13949
  }
13950
+ },
13951
+ // Euclidean distance between two points (tuples of numbers).
13952
+ {
13953
+ name: "Distance",
13954
+ latexTrigger: ["\\operatorname{distance}"],
13955
+ kind: "function",
13956
+ serialize: (serializer, expr2) => "\\operatorname{distance}" + serializer.wrapArguments(expr2)
13930
13957
  }
13931
13958
  ];
13932
13959
  function getIndexAssignment(expr2, upper) {
@@ -15351,7 +15378,9 @@ var ComputeEngine = (() => {
15351
15378
  if (!expr2 || !symbol(expr2)) return null;
15352
15379
  return ["Mean", expr2];
15353
15380
  }
15354
- }
15381
+ },
15382
+ // Function-style alias: `\operatorname{var}(...)`
15383
+ { latexTrigger: "\\operatorname{var}", parse: "Variance" }
15355
15384
  ];
15356
15385
 
15357
15386
  // src/compute-engine/numerics/unit-data.ts
@@ -15903,12 +15932,52 @@ var ComputeEngine = (() => {
15903
15932
  ];
15904
15933
 
15905
15934
  // src/compute-engine/latex-syntax/dictionary/definitions-other.ts
15935
+ var TEX_UNITS = [
15936
+ "pt",
15937
+ "em",
15938
+ "mu",
15939
+ "ex",
15940
+ "mm",
15941
+ "cm",
15942
+ "in",
15943
+ "bp",
15944
+ "sp",
15945
+ "dd",
15946
+ "cc",
15947
+ "pc",
15948
+ "nc",
15949
+ "nd"
15950
+ ];
15951
+ function skipTexDimension(parser) {
15952
+ parser.skipSpace();
15953
+ if (parser.peek === "-" || parser.peek === "+") parser.nextToken();
15954
+ while (/^[\d.]$/.test(parser.peek)) parser.nextToken();
15955
+ for (const unit of TEX_UNITS) {
15956
+ if (parser.matchAll([...unit])) return;
15957
+ }
15958
+ }
15906
15959
  function parseSingleArg(cmd) {
15907
15960
  return (parser) => {
15908
15961
  const arg = parser.parseGroup();
15909
15962
  return arg === null ? [cmd] : [cmd, arg];
15910
15963
  };
15911
15964
  }
15965
+ function parseMathStyleSwitch(mathStyle) {
15966
+ return (parser) => {
15967
+ const body = parser.parseExpression();
15968
+ if (body !== null && !isEmptySequence(body))
15969
+ return ["Annotated", body, { dict: { mathStyle } }];
15970
+ return "Nothing";
15971
+ };
15972
+ }
15973
+ function parseSizeSwitch(size) {
15974
+ return (parser) => {
15975
+ const body = parser.parseExpression();
15976
+ if (body !== null && !isEmptySequence(body))
15977
+ return ["Annotated", body, { dict: { size } }];
15978
+ return "Nothing";
15979
+ };
15980
+ }
15912
15981
  var DEFINITIONS_OTHERS = [
15913
15982
  {
15914
15983
  name: "Overscript",
@@ -16148,80 +16217,71 @@ var ComputeEngine = (() => {
16148
16217
  },
16149
16218
  {
16150
16219
  latexTrigger: ["\\displaystyle"],
16151
- parse: () => "Nothing"
16152
- // @todo: parse as ['Annotated'...]
16220
+ parse: parseMathStyleSwitch("normal")
16153
16221
  },
16154
16222
  {
16155
16223
  latexTrigger: ["\\textstyle"],
16156
- parse: () => "Nothing"
16157
- // @todo: parse as ['Annotated'...]
16224
+ parse: parseMathStyleSwitch("compact")
16158
16225
  },
16159
16226
  {
16160
16227
  latexTrigger: ["\\scriptstyle"],
16161
- parse: () => "Nothing"
16162
- // @todo: parse as ['Annotated'...]
16228
+ parse: parseMathStyleSwitch("script")
16163
16229
  },
16164
16230
  {
16165
16231
  latexTrigger: ["\\scriptscriptstyle"],
16166
- parse: () => "Nothing"
16167
- // @todo: parse as ['Annotated'...]
16232
+ parse: parseMathStyleSwitch("scriptscript")
16168
16233
  },
16169
16234
  {
16170
16235
  latexTrigger: ["\\color"],
16171
16236
  parse: (parser) => {
16172
- parser.parseGroup();
16237
+ const color = parser.parseStringGroup();
16238
+ if (color !== null) {
16239
+ const body = parser.parseExpression();
16240
+ if (body !== null && !isEmptySequence(body))
16241
+ return ["Annotated", body, { dict: { color } }];
16242
+ }
16173
16243
  return "Nothing";
16174
16244
  }
16175
16245
  },
16176
16246
  {
16177
16247
  latexTrigger: ["\\tiny"],
16178
- parse: () => "Nothing"
16179
- // @todo: parse as ['Annotated'...]
16248
+ parse: parseSizeSwitch(1)
16180
16249
  },
16181
16250
  {
16182
16251
  latexTrigger: ["\\scriptsize"],
16183
- parse: () => "Nothing"
16184
- // @todo: parse as ['Annotated'...]
16252
+ parse: parseSizeSwitch(2)
16185
16253
  },
16186
16254
  {
16187
16255
  latexTrigger: ["\\footnotesize"],
16188
- parse: () => "Nothing"
16189
- // @todo: parse as ['Annotated'...]
16256
+ parse: parseSizeSwitch(3)
16190
16257
  },
16191
16258
  {
16192
16259
  latexTrigger: ["\\small"],
16193
- parse: () => "Nothing"
16194
- // @todo: parse as ['Annotated'...]
16260
+ parse: parseSizeSwitch(4)
16195
16261
  },
16196
16262
  {
16197
16263
  latexTrigger: ["\\normalsize"],
16198
- parse: () => "Nothing"
16199
- // @todo: parse as ['Annotated'...]
16264
+ parse: parseSizeSwitch(5)
16200
16265
  },
16201
16266
  {
16202
16267
  latexTrigger: ["\\large"],
16203
- parse: () => "Nothing"
16204
- // @todo: parse as ['Annotated'...]
16268
+ parse: parseSizeSwitch(6)
16205
16269
  },
16206
16270
  {
16207
16271
  latexTrigger: ["\\Large"],
16208
- parse: () => "Nothing"
16209
- // @todo: parse as ['Annotated'...]
16272
+ parse: parseSizeSwitch(7)
16210
16273
  },
16211
16274
  {
16212
16275
  latexTrigger: ["\\LARGE"],
16213
- parse: () => "Nothing"
16214
- // @todo: parse as ['Annotated'...]
16276
+ parse: parseSizeSwitch(8)
16215
16277
  },
16216
16278
  {
16217
16279
  latexTrigger: ["\\huge"],
16218
- parse: () => "Nothing"
16219
- // @todo: parse as ['Annotated'...]
16280
+ parse: parseSizeSwitch(9)
16220
16281
  },
16221
16282
  {
16222
16283
  latexTrigger: ["\\Huge"],
16223
- parse: () => "Nothing"
16224
- // @todo: parse as ['Annotated'...]
16284
+ parse: parseSizeSwitch(10)
16225
16285
  },
16226
16286
  {
16227
16287
  name: "Annotated",
@@ -16233,6 +16293,10 @@ var ComputeEngine = (() => {
16233
16293
  result = joinLatex(["{\\displaystyle", result, "}"]);
16234
16294
  else if (dict.dict.mathStyle === "compact")
16235
16295
  result = joinLatex(["{\\textstyle", result, "}"]);
16296
+ else if (dict.dict.mathStyle === "script")
16297
+ result = joinLatex(["{\\scriptstyle", result, "}"]);
16298
+ else if (dict.dict.mathStyle === "scriptscript")
16299
+ result = joinLatex(["{\\scriptscriptstyle", result, "}"]);
16236
16300
  const v = dict.dict.size;
16237
16301
  if (v !== null && v >= 1 && v <= 10) {
16238
16302
  result = joinLatex([
@@ -16320,6 +16384,28 @@ var ComputeEngine = (() => {
16320
16384
  latexTrigger: ["\\enspace"],
16321
16385
  parse: () => ["HorizontalSpacing", 9]
16322
16386
  },
16387
+ {
16388
+ latexTrigger: ["\\hspace"],
16389
+ parse: (parser) => {
16390
+ if (parser.peek === "*") parser.nextToken();
16391
+ parser.parseStringGroup();
16392
+ return ["HorizontalSpacing", 0];
16393
+ }
16394
+ },
16395
+ {
16396
+ latexTrigger: ["\\hskip"],
16397
+ parse: (parser) => {
16398
+ skipTexDimension(parser);
16399
+ return ["HorizontalSpacing", 0];
16400
+ }
16401
+ },
16402
+ {
16403
+ latexTrigger: ["\\kern"],
16404
+ parse: (parser) => {
16405
+ skipTexDimension(parser);
16406
+ return ["HorizontalSpacing", 0];
16407
+ }
16408
+ },
16323
16409
  {
16324
16410
  latexTrigger: ["\\phantom"],
16325
16411
  parse: (parser) => {
@@ -16370,7 +16456,17 @@ var ComputeEngine = (() => {
16370
16456
  // `["HorizontalSpacing", expr, 'op'|'bin'|rel]` -> indicate a spacing around and expression, i.e. `\mathbin{x}`, etc...
16371
16457
  serialize: (serializer, expr2) => {
16372
16458
  if (operand(expr2, 2) !== null) {
16373
- return serializer.serialize(operand(expr2, 1));
16459
+ const cls = stringValue(operand(expr2, 2));
16460
+ const inner = serializer.serialize(operand(expr2, 1));
16461
+ if (cls === "bin") return `\\mathbin{${inner}}`;
16462
+ if (cls === "op") return `\\mathop{${inner}}`;
16463
+ if (cls === "rel") return `\\mathrel{${inner}}`;
16464
+ if (cls === "ord") return `\\mathord{${inner}}`;
16465
+ if (cls === "open") return `\\mathopen{${inner}}`;
16466
+ if (cls === "close") return `\\mathclose{${inner}}`;
16467
+ if (cls === "punct") return `\\mathpunct{${inner}}`;
16468
+ if (cls === "inner") return `\\mathinner{${inner}}`;
16469
+ return inner;
16374
16470
  }
16375
16471
  const v = machineValue(operand(expr2, 1));
16376
16472
  if (v === null) return "";
@@ -16385,7 +16481,7 @@ var ComputeEngine = (() => {
16385
16481
  36: "\\qquad"
16386
16482
  }[v] ?? "";
16387
16483
  }
16388
- }
16484
+ },
16389
16485
  // if (
16390
16486
  // [
16391
16487
  // '\\!',
@@ -16409,6 +16505,121 @@ var ComputeEngine = (() => {
16409
16505
  // name: '',
16410
16506
  // trigger: '\\check',
16411
16507
  // },
16508
+ // ---------------------------------------------------------------------------
16509
+ // Function-style aliases for collection / random operators that some
16510
+ // notations write in lowercase (e.g. `\operatorname{shuffle}(L)`).
16511
+ // The capitalized library entries already exist; these are pure parse
16512
+ // aliases so the lowercase names don't land in `unsupported-operator`.
16513
+ // ---------------------------------------------------------------------------
16514
+ { latexTrigger: "\\operatorname{random}", parse: "Random" },
16515
+ { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16516
+ { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16517
+ { latexTrigger: "\\operatorname{join}", parse: "Join" },
16518
+ // ---------------------------------------------------------------------------
16519
+ // Geometric primitive heads. Registered as known typed heads so consumers
16520
+ // can branch on the operator name; CE itself doesn't render them. The
16521
+ // library entries (with no evaluator) live in `library/core.ts`.
16522
+ // ---------------------------------------------------------------------------
16523
+ {
16524
+ name: "Triangle",
16525
+ latexTrigger: ["\\operatorname{triangle}"],
16526
+ kind: "function",
16527
+ serialize: (serializer, expr2) => "\\operatorname{triangle}" + serializer.wrapArguments(expr2)
16528
+ },
16529
+ // Desmos's geometric `vector(p1, p2)` — a directed segment between two
16530
+ // points. Routed to a dedicated head (not the existing column-vector
16531
+ // `Vector`, which has a narrower `(number+) -> vector` signature).
16532
+ {
16533
+ name: "GeometricVector",
16534
+ latexTrigger: ["\\operatorname{vector}"],
16535
+ kind: "function",
16536
+ serialize: (serializer, expr2) => "\\operatorname{vector}" + serializer.wrapArguments(expr2)
16537
+ },
16538
+ {
16539
+ name: "Sphere",
16540
+ latexTrigger: ["\\operatorname{sphere}"],
16541
+ kind: "function",
16542
+ serialize: (serializer, expr2) => "\\operatorname{sphere}" + serializer.wrapArguments(expr2)
16543
+ },
16544
+ {
16545
+ name: "Segment",
16546
+ latexTrigger: ["\\operatorname{segment}"],
16547
+ kind: "function",
16548
+ serialize: (serializer, expr2) => "\\operatorname{segment}" + serializer.wrapArguments(expr2)
16549
+ }
16550
+ ];
16551
+
16552
+ // src/compute-engine/latex-syntax/dictionary/definitions-colors.ts
16553
+ var DEFINITIONS_COLORS = [
16554
+ // Color constructors (one per colorspace, preserves space on evaluation)
16555
+ {
16556
+ name: "Rgb",
16557
+ latexTrigger: ["\\operatorname{rgb}"],
16558
+ kind: "function",
16559
+ serialize: (serializer, expr2) => "\\operatorname{rgb}" + serializer.wrapArguments(expr2)
16560
+ },
16561
+ {
16562
+ name: "Hsv",
16563
+ latexTrigger: ["\\operatorname{hsv}"],
16564
+ kind: "function",
16565
+ serialize: (serializer, expr2) => "\\operatorname{hsv}" + serializer.wrapArguments(expr2)
16566
+ },
16567
+ {
16568
+ name: "Hsl",
16569
+ latexTrigger: ["\\operatorname{hsl}"],
16570
+ kind: "function",
16571
+ serialize: (serializer, expr2) => "\\operatorname{hsl}" + serializer.wrapArguments(expr2)
16572
+ },
16573
+ {
16574
+ name: "Oklab",
16575
+ latexTrigger: ["\\operatorname{oklab}"],
16576
+ kind: "function",
16577
+ serialize: (serializer, expr2) => "\\operatorname{oklab}" + serializer.wrapArguments(expr2)
16578
+ },
16579
+ {
16580
+ name: "Oklch",
16581
+ latexTrigger: ["\\operatorname{oklch}"],
16582
+ kind: "function",
16583
+ serialize: (serializer, expr2) => "\\operatorname{oklch}" + serializer.wrapArguments(expr2)
16584
+ },
16585
+ // Conversion functions (color → color in the named space)
16586
+ {
16587
+ name: "AsRgb",
16588
+ latexTrigger: ["\\operatorname{asRgb}"],
16589
+ kind: "function",
16590
+ serialize: (serializer, expr2) => "\\operatorname{asRgb}" + serializer.wrapArguments(expr2)
16591
+ },
16592
+ {
16593
+ name: "AsHsv",
16594
+ latexTrigger: ["\\operatorname{asHsv}"],
16595
+ kind: "function",
16596
+ serialize: (serializer, expr2) => "\\operatorname{asHsv}" + serializer.wrapArguments(expr2)
16597
+ },
16598
+ {
16599
+ name: "AsHsl",
16600
+ latexTrigger: ["\\operatorname{asHsl}"],
16601
+ kind: "function",
16602
+ serialize: (serializer, expr2) => "\\operatorname{asHsl}" + serializer.wrapArguments(expr2)
16603
+ },
16604
+ {
16605
+ name: "AsOklab",
16606
+ latexTrigger: ["\\operatorname{asOklab}"],
16607
+ kind: "function",
16608
+ serialize: (serializer, expr2) => "\\operatorname{asOklab}" + serializer.wrapArguments(expr2)
16609
+ },
16610
+ {
16611
+ name: "AsOklch",
16612
+ latexTrigger: ["\\operatorname{asOklch}"],
16613
+ kind: "function",
16614
+ serialize: (serializer, expr2) => "\\operatorname{asOklch}" + serializer.wrapArguments(expr2)
16615
+ },
16616
+ // Perceptual difference (returns a scalar in [0, ~1])
16617
+ {
16618
+ name: "ColorDelta",
16619
+ latexTrigger: ["\\operatorname{colorDelta}"],
16620
+ kind: "function",
16621
+ serialize: (serializer, expr2) => "\\operatorname{colorDelta}" + serializer.wrapArguments(expr2)
16622
+ }
16412
16623
  ];
16413
16624
 
16414
16625
  // src/compute-engine/latex-syntax/dictionary/default-dictionary.ts
@@ -16439,7 +16650,8 @@ var ComputeEngine = (() => {
16439
16650
  ...DEFINITIONS_STATISTICS,
16440
16651
  ...DEFINITIONS_UNITS,
16441
16652
  ...DEFINITIONS_OTHERS,
16442
- ...DEFINITIONS_PHYSICS
16653
+ ...DEFINITIONS_PHYSICS,
16654
+ ...DEFINITIONS_COLORS
16443
16655
  ];
16444
16656
 
16445
16657
  // src/math-json/symbols.ts
@@ -16604,6 +16816,17 @@ var ComputeEngine = (() => {
16604
16816
  } else if (Array.isArray(openTrigger) && openTrigger.length > 0) {
16605
16817
  openTokens.push(openTrigger[0]);
16606
16818
  }
16819
+ const closeTrigger = indexedEntry.closeTrigger;
16820
+ const closeTokens = /* @__PURE__ */ new Set();
16821
+ if (typeof closeTrigger === "string") {
16822
+ const variants = DELIMITER_SHORTHAND[closeTrigger];
16823
+ if (variants) for (const v of variants) closeTokens.add(v);
16824
+ else closeTokens.add(closeTrigger);
16825
+ if (closeTrigger === "||") closeTokens.add("|");
16826
+ } else if (Array.isArray(closeTrigger) && closeTrigger.length > 0) {
16827
+ closeTokens.add(closeTrigger[0]);
16828
+ }
16829
+ indexedEntry.closeTokens = closeTokens;
16607
16830
  for (const token of openTokens) {
16608
16831
  const existing = result.matchfixByOpen.get(token);
16609
16832
  if (existing) {
@@ -16761,25 +16984,43 @@ var ComputeEngine = (() => {
16761
16984
  result.arguments = entry.arguments;
16762
16985
  return result;
16763
16986
  }
16987
+ function serializeTabularBody(serializer, body) {
16988
+ if (!body) return "";
16989
+ if (operator(body) !== "List") return serializer.serialize(body);
16990
+ const rows = operands(body);
16991
+ if (rows.length === 0) return "";
16992
+ if (!rows.every((row) => operator(row) === "List"))
16993
+ return serializer.serialize(body);
16994
+ return rows.map(
16995
+ (row) => operands(row).map((cell) => serializer.serialize(cell)).join(" & ")
16996
+ ).join(" \\\\\n");
16997
+ }
16764
16998
  function makeSerializeHandler(entry, latexTrigger, idTrigger) {
16765
16999
  if (typeof entry.serialize === "function") return entry.serialize;
16766
17000
  const kind = entry["kind"] ?? "expression";
16767
17001
  if (kind === "environment") {
16768
17002
  const envName = entry["symbolTrigger"] ?? entry.name ?? "unknown";
16769
- return (serializer, expr2) => joinLatex([
16770
- `\\begin{${envName}}`,
16771
- serializer.serialize(operand(expr2, 1)),
16772
- `\\end{${envName}}`
16773
- ]);
17003
+ return (serializer, expr2) => {
17004
+ const body = operand(expr2, 1);
17005
+ return joinLatex([
17006
+ `\\begin{${envName}}`,
17007
+ serializeTabularBody(serializer, body),
17008
+ `\\end{${envName}}`
17009
+ ]);
17010
+ };
16774
17011
  }
16775
17012
  if (isMatchfixEntry(entry)) {
16776
17013
  const openDelim = typeof entry.openTrigger === "string" ? DEFAULT_DELIMITER[entry.openTrigger] : tokensToString(entry.openTrigger);
16777
17014
  const closeDelim = typeof entry.closeTrigger === "string" ? DEFAULT_DELIMITER[entry.closeTrigger] : tokensToString(entry.closeTrigger);
16778
- return (serializer, expr2) => joinLatex([
16779
- openDelim,
16780
- serializer.serialize(operand(expr2, 1)),
16781
- closeDelim
16782
- ]);
17015
+ return (serializer, expr2) => {
17016
+ const style = serializer.groupStyle(expr2, serializer.level + 1);
17017
+ const inner = serializer.serialize(operand(expr2, 1));
17018
+ if (style === "scaled")
17019
+ return joinLatex([`\\left${openDelim}`, inner, `\\right${closeDelim}`]);
17020
+ if (style === "big")
17021
+ return joinLatex([`\\Bigl${openDelim}`, inner, `\\Bigr${closeDelim}`]);
17022
+ return joinLatex([openDelim, inner, closeDelim]);
17023
+ };
16783
17024
  }
16784
17025
  let latex = entry.serialize;
16785
17026
  if (latex === void 0 && latexTrigger) latex = tokensToString(latexTrigger);
@@ -17664,6 +17905,16 @@ var ComputeEngine = (() => {
17664
17905
  }
17665
17906
 
17666
17907
  // src/compute-engine/latex-syntax/parse.ts
17908
+ var _symbolToUnicode = null;
17909
+ function getSymbolToUnicode() {
17910
+ if (!_symbolToUnicode) {
17911
+ _symbolToUnicode = /* @__PURE__ */ new Map();
17912
+ for (const [, latex, codepoint] of SYMBOLS2) {
17913
+ _symbolToUnicode.set(latex, String.fromCodePoint(codepoint));
17914
+ }
17915
+ }
17916
+ return _symbolToUnicode;
17917
+ }
17667
17918
  var DELIMITER_SHORTHAND2 = {
17668
17919
  "(": ["\\lparen", "("],
17669
17920
  ")": ["\\rparen", ")"],
@@ -18111,6 +18362,35 @@ var ComputeEngine = (() => {
18111
18362
  this.nextToken();
18112
18363
  this.skipVisualSpace();
18113
18364
  }
18365
+ if (this.match("\\hspace")) {
18366
+ this.match("*");
18367
+ this.parseStringGroup();
18368
+ this.skipVisualSpace();
18369
+ }
18370
+ if (this.match("\\hskip") || this.match("\\kern")) {
18371
+ this.skipSpace();
18372
+ if (!this.match("-")) this.match("+");
18373
+ while (/^[\d.]$/.test(this.peek)) this.nextToken();
18374
+ for (const unit of [
18375
+ "pt",
18376
+ "em",
18377
+ "mu",
18378
+ "ex",
18379
+ "mm",
18380
+ "cm",
18381
+ "in",
18382
+ "bp",
18383
+ "sp",
18384
+ "dd",
18385
+ "cc",
18386
+ "pc",
18387
+ "nc",
18388
+ "nd"
18389
+ ]) {
18390
+ if (this.matchAll([...unit])) break;
18391
+ }
18392
+ this.skipVisualSpace();
18393
+ }
18114
18394
  this.skipSpace();
18115
18395
  }
18116
18396
  match(token) {
@@ -18448,7 +18728,8 @@ var ComputeEngine = (() => {
18448
18728
  } else if (token === "<space>") {
18449
18729
  result += " ";
18450
18730
  } else if (token[0] === "\\") {
18451
- result += token;
18731
+ const unicode = getSymbolToUnicode().get(token);
18732
+ result += unicode ?? token;
18452
18733
  } else {
18453
18734
  result += token;
18454
18735
  }
@@ -18602,6 +18883,19 @@ var ComputeEngine = (() => {
18602
18883
  }
18603
18884
  for (const def of defs) {
18604
18885
  this.index = start;
18886
+ if (def.closeTokens.size > 0) {
18887
+ let found = false;
18888
+ const tokens = this._tokens;
18889
+ for (let i = start; i < tokens.length; i++) {
18890
+ if (def.closeTokens.has(tokens[i])) {
18891
+ found = true;
18892
+ break;
18893
+ }
18894
+ }
18895
+ if (!found) continue;
18896
+ }
18897
+ if (typeof def.openTrigger === "string" && def.openTrigger === "." && !OPEN_DELIMITER_PREFIX[currentToken])
18898
+ continue;
18605
18899
  const matched = this.matchDelimiter(def.openTrigger, def.closeTrigger);
18606
18900
  if (!matched) continue;
18607
18901
  const bodyStart = this.index;
@@ -19808,7 +20102,7 @@ var ComputeEngine = (() => {
19808
20102
  sansserif: (s) => `\\mathsf{${s}}`,
19809
20103
  monospace: (s) => `\\mathtt{${s}}`
19810
20104
  };
19811
- var Serializer4 = class {
20105
+ var Serializer5 = class {
19812
20106
  options;
19813
20107
  dictionary;
19814
20108
  level = -1;
@@ -19823,11 +20117,18 @@ var ComputeEngine = (() => {
19823
20117
  /**
19824
20118
  * Serialize the expression, and if the expression is an operator
19825
20119
  * of precedence less than or equal to prec, wrap it in some parens.
19826
- * @todo: don't wrap Abs, Floor, Ceil, Delimiter
20120
+ *
20121
+ * Skip wrapping for matchfix operators (Abs, Floor, Ceil, Norm, etc.)
20122
+ * and Delimiter since they already have visible delimiters.
19827
20123
  */
19828
20124
  wrap(expr2, prec) {
19829
20125
  if (expr2 === null || expr2 === void 0) return "";
19830
20126
  if (prec === void 0) {
20127
+ const name2 = operator(expr2);
20128
+ if (name2) {
20129
+ const def = this.dictionary.ids.get(name2);
20130
+ if (def?.kind === "matchfix") return this.serialize(expr2);
20131
+ }
19831
20132
  return this.wrapString(
19832
20133
  this.serialize(expr2),
19833
20134
  this.options.groupStyle(expr2, this.level + 1)
@@ -20121,7 +20422,7 @@ var ComputeEngine = (() => {
20121
20422
  return body;
20122
20423
  }
20123
20424
  function serializeLatex(expr2, dict, options) {
20124
- const serializer = new Serializer4(dict, options);
20425
+ const serializer = new Serializer5(dict, options);
20125
20426
  return serializer.serialize(expr2);
20126
20427
  }
20127
20428
 
@@ -32157,6 +32458,29 @@ ${lines.join("\n")}`;
32157
32458
  signature: "(value*) -> number | list",
32158
32459
  evaluate: (xs, { engine }) => evaluateMinMax(engine, xs, "Infimum")
32159
32460
  },
32461
+ Distance: {
32462
+ description: "Euclidean distance between two points (tuples of numbers).",
32463
+ complexity: 6e3,
32464
+ signature: "(tuple, tuple) -> number",
32465
+ evaluate: ([a, b], { engine: ce }) => {
32466
+ if (!isFunction2(a) || !isFunction2(b))
32467
+ return ce.error("incompatible-type");
32468
+ if (a.operator !== "Tuple" || b.operator !== "Tuple")
32469
+ return ce.error("incompatible-type");
32470
+ if (a.ops.length !== b.ops.length || a.ops.length === 0)
32471
+ return ce.error("incompatible-type");
32472
+ let sumSq = 0;
32473
+ for (let i = 0; i < a.ops.length; i++) {
32474
+ const ai = a.ops[i].re;
32475
+ const bi = b.ops[i].re;
32476
+ if (!Number.isFinite(ai) || !Number.isFinite(bi))
32477
+ return ce.error("expected-value");
32478
+ const d = ai - bi;
32479
+ sumSq += d * d;
32480
+ }
32481
+ return ce.number(Math.sqrt(sumSq));
32482
+ }
32483
+ },
32160
32484
  Product: {
32161
32485
  description: "`Product(f, a, b)` computes the product of `f` from `a` to `b`",
32162
32486
  wikidata: "Q901718",
@@ -32689,16 +33013,11 @@ ${lines.join("\n")}`;
32689
33013
  );
32690
33014
  let condFn;
32691
33015
  if (typeof condition === "string") {
32692
- const latex = asLatexString(condition);
32693
- if (latex) {
32694
- const condPattern = ce.parse(latex, {
32695
- form: options?.canonical ? "canonical" : "raw"
32696
- }) ?? ce.expr("Nothing");
32697
- condFn = (x, _ce) => {
32698
- const evaluated = condPattern.subs(x).evaluate();
32699
- return isSymbol2(evaluated, "True");
32700
- };
32701
- }
33016
+ const condPattern = ce.parse(condition) ?? ce.expr("Nothing");
33017
+ condFn = (x, _ce) => {
33018
+ const evaluated = condPattern.subs(x).evaluate();
33019
+ return isSymbol2(evaluated, "True");
33020
+ };
32702
33021
  } else {
32703
33022
  if (condition !== void 0 && typeof condition !== "function")
32704
33023
  throw new Error(
@@ -32800,6 +33119,15 @@ ${e.message}
32800
33119
  function applyRule(rule, expr2, substitution, options) {
32801
33120
  if (!rule) return null;
32802
33121
  let canonical2 = options?.canonical ?? (expr2.isCanonical || expr2.isStructural);
33122
+ let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
33123
+ const because = id ?? "";
33124
+ const ce = expr2.engine;
33125
+ if (canonical2 && match2) {
33126
+ const awc = getWildcards(match2);
33127
+ const canonicalMatch = match2.canonical;
33128
+ const bwc = getWildcards(canonicalMatch);
33129
+ if (!awc.every((x) => bwc.includes(x))) return null;
33130
+ }
32803
33131
  let operandsMatched = false;
32804
33132
  if (isFunction2(expr2) && options?.recursive) {
32805
33133
  const newOps = expr2.ops.map((op) => {
@@ -32811,20 +33139,11 @@ ${e.message}
32811
33139
  if (operandsMatched) {
32812
33140
  if (!canonical2 && options?.canonical === void 0 && newOps.every((x) => x.isCanonical))
32813
33141
  canonical2 = true;
32814
- expr2 = expr2.engine.function(expr2.operator, newOps, {
33142
+ expr2 = ce.function(expr2.operator, newOps, {
32815
33143
  form: canonical2 ? "canonical" : "raw"
32816
33144
  });
32817
33145
  }
32818
33146
  }
32819
- let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
32820
- const because = id ?? "";
32821
- if (canonical2 && match2) {
32822
- const awc = getWildcards(match2);
32823
- const canonicalMatch = match2.canonical;
32824
- const bwc = getWildcards(canonicalMatch);
32825
- if (!awc.every((x) => bwc.includes(x)))
32826
- return operandsMatched ? { value: expr2, because } : null;
32827
- }
32828
33147
  const useVariations = rule.useVariations ?? options?.useVariations ?? false;
32829
33148
  const matchPermutations = options?.matchPermutations ?? true;
32830
33149
  onBeforeMatch?.(rule, expr2);
@@ -32843,7 +33162,7 @@ ${e.message}
32843
33162
  ...sub2
32844
33163
  };
32845
33164
  try {
32846
- if (!condition(conditionSub, expr2.engine))
33165
+ if (!condition(conditionSub, ce))
32847
33166
  return operandsMatched ? { value: expr2, because } : null;
32848
33167
  } catch (e) {
32849
33168
  console.error(
@@ -32858,7 +33177,8 @@ ${e.message}
32858
33177
  if (!canonical2 && options?.canonical === void 0 && replace2 instanceof _BoxedExpression && replace2.isCanonical)
32859
33178
  canonical2 = true;
32860
33179
  const result = typeof replace2 === "function" ? replace2(expr2, sub2) : replace2.subs(sub2, { canonical: canonical2 });
32861
- if (!result) return null;
33180
+ if (!result)
33181
+ return operandsMatched ? { value: canonical2 ? expr2.canonical : expr2, because } : null;
32862
33182
  onMatch?.(rule, expr2, result);
32863
33183
  if (isRuleStep(result))
32864
33184
  return canonical2 ? { ...result, value: result.value.canonical } : result;
@@ -38432,6 +38752,40 @@ ${e.message}
38432
38752
  else h = ((r - g) / d + 4) / 6;
38433
38753
  return { h: h * 360, s, l };
38434
38754
  }
38755
+ function hsvToRgb(h, s, v) {
38756
+ h = (h % 360 + 360) % 360;
38757
+ s = Math.max(0, Math.min(1, s));
38758
+ v = Math.max(0, Math.min(1, v));
38759
+ const c = v * s;
38760
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
38761
+ const m = v - c;
38762
+ let r = 0, g = 0, b = 0;
38763
+ if (h < 60) [r, g, b] = [c, x, 0];
38764
+ else if (h < 120) [r, g, b] = [x, c, 0];
38765
+ else if (h < 180) [r, g, b] = [0, c, x];
38766
+ else if (h < 240) [r, g, b] = [0, x, c];
38767
+ else if (h < 300) [r, g, b] = [x, 0, c];
38768
+ else [r, g, b] = [c, 0, x];
38769
+ return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };
38770
+ }
38771
+ function rgbToHsv(r, g, b) {
38772
+ r /= 255;
38773
+ g /= 255;
38774
+ b /= 255;
38775
+ const max2 = Math.max(r, g, b);
38776
+ const min2 = Math.min(r, g, b);
38777
+ const d = max2 - min2;
38778
+ let h = 0;
38779
+ if (d > 0) {
38780
+ if (max2 === r) h = (g - b) / d % 6;
38781
+ else if (max2 === g) h = (b - r) / d + 2;
38782
+ else h = (r - g) / d + 4;
38783
+ h *= 60;
38784
+ if (h < 0) h += 360;
38785
+ }
38786
+ const s = max2 === 0 ? 0 : d / max2;
38787
+ return { h, s, v: max2 };
38788
+ }
38435
38789
  function parseHexColor(s) {
38436
38790
  const hex = s.startsWith("#") ? s.substring(1) : s;
38437
38791
  let r, g, b;
@@ -38456,6 +38810,12 @@ ${e.message}
38456
38810
  if (alpha !== void 0) result.alpha = alpha;
38457
38811
  return result;
38458
38812
  }
38813
+ function asOklch(color) {
38814
+ if (typeof color === "string") return rgbToOklch(parseHexColor(color));
38815
+ if ("C" in color) return color;
38816
+ if ("a" in color && "b" in color) return oklabToOklch(color);
38817
+ return rgbToOklch(color);
38818
+ }
38459
38819
  function asRgb(color) {
38460
38820
  if (typeof color === "number") {
38461
38821
  return {
@@ -38887,6 +39247,13 @@ ${e.message}
38887
39247
  };
38888
39248
  function parseColor(s, darkMode) {
38889
39249
  const str = s.trim().toLowerCase();
39250
+ const opacityMatch = str.match(/^(.+?)\s*\/\s*(\d+(?:\.\d+)?)%?\s*$/);
39251
+ if (opacityMatch) {
39252
+ const base = parseColor(opacityMatch[1].trim(), darkMode);
39253
+ const opacity = Math.max(0, Math.min(100, parseFloat(opacityMatch[2])));
39254
+ const alpha = Math.round(opacity / 100 * 255);
39255
+ return base & 4294967040 | alpha;
39256
+ }
38890
39257
  if (str.startsWith("#")) {
38891
39258
  const hex = str.substring(1);
38892
39259
  let r, g, b, a = 255;
@@ -39019,14 +39386,6 @@ ${e.message}
39019
39386
  console.warn(`parseColor: unrecognized color "${s}"`);
39020
39387
  return 0;
39021
39388
  }
39022
- function parseColorToRgb01(s, darkMode) {
39023
- const color = parseColor(s, darkMode);
39024
- return [
39025
- (color >>> 24 & 255) / 255,
39026
- (color >>> 16 & 255) / 255,
39027
- (color >>> 8 & 255) / 255
39028
- ];
39029
- }
39030
39389
  function apca(bgColor, fgColor) {
39031
39390
  const bgRgb = asRgb(bgColor);
39032
39391
  const fgRgb = asRgb(fgColor);
@@ -39085,6 +39444,12 @@ ${e.message}
39085
39444
  const contrast2 = Math.abs(apca(fg2, bg));
39086
39445
  return contrast1 >= contrast2 ? asColorNumber(fg1) : asColorNumber(fg2);
39087
39446
  }
39447
+ function oklabDeltaE(a, b) {
39448
+ const dL = a.L - b.L;
39449
+ const da = a.a - b.a;
39450
+ const db = a.b - b.b;
39451
+ return Math.sqrt(dL * dL + da * da + db * db);
39452
+ }
39088
39453
  function lerpOklch(c1, c2, f) {
39089
39454
  const L = c1.L + (c2.L - c1.L) * f;
39090
39455
  const C = c1.C + (c2.C - c1.C) * f;
@@ -41846,14 +42211,30 @@ ${e.message}
41846
42211
  };
41847
42212
 
41848
42213
  // src/compute-engine/library/colors.ts
41849
- function colorNumberToTuple(ce, color) {
41850
- const r = (color >>> 24 & 255) / 255;
41851
- const g = (color >>> 16 & 255) / 255;
41852
- const b = (color >>> 8 & 255) / 255;
41853
- const a = (color & 255) / 255;
41854
- if (Math.abs(a - 1) < 1e-4)
41855
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
41856
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b), ce.number(a));
42214
+ function normalizeAlpha(a) {
42215
+ if (a === void 0) return void 0;
42216
+ if (!Number.isFinite(a)) return void 0;
42217
+ if (Math.abs(a - 1) < 1e-9) return void 0;
42218
+ return a;
42219
+ }
42220
+ function normalizeColorHead(ce, expr2) {
42221
+ if (!isFunction2(expr2) || !expr2.ops || expr2.ops.length < 4) return expr2;
42222
+ const alphaExpr = expr2.ops[3];
42223
+ if (!isNumber(alphaExpr)) return expr2;
42224
+ if (normalizeAlpha(alphaExpr.re) === void 0) {
42225
+ return ce.function(expr2.operator, [expr2.ops[0], expr2.ops[1], expr2.ops[2]]);
42226
+ }
42227
+ return expr2;
42228
+ }
42229
+ function colorNumberToOklch(ce, color) {
42230
+ const r = color >>> 24 & 255;
42231
+ const g = color >>> 16 & 255;
42232
+ const b = color >>> 8 & 255;
42233
+ const a = normalizeAlpha((color & 255) / 255);
42234
+ const c = rgbToOklch({ r, g, b });
42235
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42236
+ if (a !== void 0) args.push(ce.number(a));
42237
+ return ce.function("Oklch", args);
41857
42238
  }
41858
42239
  var ALL_PALETTES = {
41859
42240
  ...SEQUENTIAL_PALETTES,
@@ -41864,45 +42245,139 @@ ${e.message}
41864
42245
  t = Math.max(0, Math.min(1, t));
41865
42246
  const n = palette.length;
41866
42247
  if (n === 0) return ce.error("expected-value");
41867
- if (n === 1) return colorNumberToTuple(ce, parseColor(palette[0]));
42248
+ if (n === 1) return colorNumberToOklch(ce, parseColor(palette[0]));
41868
42249
  const pos = t * (n - 1);
41869
42250
  const i = Math.floor(pos);
41870
42251
  const frac = pos - i;
41871
- if (i >= n - 1) return colorNumberToTuple(ce, parseColor(palette[n - 1]));
41872
- if (frac < 1e-9) return colorNumberToTuple(ce, parseColor(palette[i]));
41873
- const rgb = interpolateOklch(palette[i], palette[i + 1], frac);
41874
- const r = rgb.r / 255;
41875
- const g = rgb.g / 255;
41876
- const b = rgb.b / 255;
41877
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42252
+ if (i >= n - 1) return colorNumberToOklch(ce, parseColor(palette[n - 1]));
42253
+ if (frac < 1e-9) return colorNumberToOklch(ce, parseColor(palette[i]));
42254
+ return oklchToExpr(
42255
+ ce,
42256
+ asOklch(interpolateOklch(palette[i], palette[i + 1], frac))
42257
+ );
42258
+ }
42259
+ var COLOR_OPERATORS = /* @__PURE__ */ new Set(["Rgb", "Hsv", "Hsl", "Oklab", "Oklch"]);
42260
+ function readColorExpr(arg) {
42261
+ if (!isFunction2(arg)) return null;
42262
+ if (!COLOR_OPERATORS.has(arg.operator)) return null;
42263
+ if (!arg.ops || arg.ops.length < 3) return null;
42264
+ const c0 = arg.ops[0].re;
42265
+ const c1 = arg.ops[1].re;
42266
+ const c2 = arg.ops[2].re;
42267
+ if (!Number.isFinite(c0) || !Number.isFinite(c1) || !Number.isFinite(c2))
42268
+ return null;
42269
+ const alpha = arg.ops.length >= 4 ? normalizeAlpha(arg.ops[3].re) : void 0;
42270
+ return { space: arg.operator, c0, c1, c2, alpha };
42271
+ }
42272
+ function colorExprToRgb(arg) {
42273
+ const c = readColorExpr(arg);
42274
+ if (!c) return null;
42275
+ const withAlpha = (rgb) => c.alpha !== void 0 ? { ...rgb, alpha: c.alpha } : rgb;
42276
+ switch (c.space) {
42277
+ case "Rgb":
42278
+ return withAlpha({ r: c.c0 * 255, g: c.c1 * 255, b: c.c2 * 255 });
42279
+ case "Hsv":
42280
+ return withAlpha(hsvToRgb(c.c0, c.c1, c.c2));
42281
+ case "Hsl":
42282
+ return withAlpha(hslToRgb(c.c0, c.c1, c.c2));
42283
+ case "Oklab":
42284
+ return withAlpha(oklabToRgb({ L: c.c0, a: c.c1, b: c.c2 }));
42285
+ case "Oklch":
42286
+ return withAlpha(oklchToRgb({ L: c.c0, C: c.c1, H: c.c2 }));
42287
+ }
42288
+ return null;
42289
+ }
42290
+ function colorExprToOklch(arg) {
42291
+ const c = readColorExpr(arg);
42292
+ if (!c) return null;
42293
+ switch (c.space) {
42294
+ case "Oklch":
42295
+ return asOklch({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42296
+ case "Oklab":
42297
+ return asOklch({ L: c.c0, a: c.c1, b: c.c2, alpha: c.alpha });
42298
+ case "Rgb":
42299
+ return asOklch({
42300
+ r: c.c0 * 255,
42301
+ g: c.c1 * 255,
42302
+ b: c.c2 * 255,
42303
+ alpha: c.alpha
42304
+ });
42305
+ case "Hsv": {
42306
+ const rgb = hsvToRgb(c.c0, c.c1, c.c2);
42307
+ return asOklch({ ...rgb, alpha: c.alpha });
42308
+ }
42309
+ case "Hsl": {
42310
+ const rgb = hslToRgb(c.c0, c.c1, c.c2);
42311
+ return asOklch({ r: rgb.r, g: rgb.g, b: rgb.b, alpha: c.alpha });
42312
+ }
42313
+ }
42314
+ return null;
42315
+ }
42316
+ function toOklch(ce, arg) {
42317
+ const direct = colorExprToOklch(arg);
42318
+ if (direct) return direct;
42319
+ const rgb = extractRgb(ce, arg);
42320
+ return rgb ? asOklch(rgb) : null;
42321
+ }
42322
+ function lerpOklchColor(a, b, t) {
42323
+ const L = a.L + (b.L - a.L) * t;
42324
+ const C = a.C + (b.C - a.C) * t;
42325
+ const aAchromatic = a.C < 1e-6;
42326
+ const bAchromatic = b.C < 1e-6;
42327
+ let H;
42328
+ if (aAchromatic && bAchromatic) H = a.H;
42329
+ else if (aAchromatic) H = b.H;
42330
+ else if (bAchromatic) H = a.H;
42331
+ else {
42332
+ let dH = b.H - a.H;
42333
+ if (dH > 180) dH -= 360;
42334
+ if (dH < -180) dH += 360;
42335
+ H = a.H + dH * t;
42336
+ if (H < 0) H += 360;
42337
+ if (H >= 360) H -= 360;
42338
+ }
42339
+ const alphaA = a.alpha ?? 1;
42340
+ const alphaB = b.alpha ?? 1;
42341
+ return { L, C, H, alpha: normalizeAlpha(alphaA + (alphaB - alphaA) * t) };
42342
+ }
42343
+ function oklchToExpr(ce, c) {
42344
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42345
+ if (c.alpha !== void 0) args.push(ce.number(c.alpha));
42346
+ return ce.function("Oklch", args);
41878
42347
  }
41879
42348
  function extractRgb(ce, arg) {
41880
42349
  if (isString(arg)) {
41881
42350
  const s = arg.string;
41882
42351
  if (!s) return void 0;
41883
42352
  const color = parseColor(s);
41884
- return {
42353
+ const rgb = {
41885
42354
  r: color >>> 24 & 255,
41886
42355
  g: color >>> 16 & 255,
41887
- b: color >>> 8 & 255,
41888
- alpha: (color & 255) / 255
42356
+ b: color >>> 8 & 255
41889
42357
  };
42358
+ const alpha = normalizeAlpha((color & 255) / 255);
42359
+ if (alpha !== void 0) rgb.alpha = alpha;
42360
+ return rgb;
41890
42361
  }
42362
+ const fromTyped = colorExprToRgb(arg);
42363
+ if (fromTyped) return fromTyped;
41891
42364
  if (arg.operator === "Tuple" && arg.ops && arg.ops.length >= 3) {
41892
42365
  const rgb = {
41893
42366
  r: arg.ops[0].re * 255,
41894
42367
  g: arg.ops[1].re * 255,
41895
42368
  b: arg.ops[2].re * 255
41896
42369
  };
41897
- if (arg.ops.length >= 4) rgb.alpha = arg.ops[3].re;
42370
+ if (arg.ops.length >= 4) {
42371
+ const alpha = normalizeAlpha(arg.ops[3].re);
42372
+ if (alpha !== void 0) rgb.alpha = alpha;
42373
+ }
41898
42374
  return rgb;
41899
42375
  }
41900
42376
  return void 0;
41901
42377
  }
41902
42378
  function componentsTuple(ce, components, alpha) {
41903
42379
  const args = components.map((v) => ce.number(v));
41904
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4)
41905
- args.push(ce.number(alpha));
42380
+ if (alpha !== void 0) args.push(ce.number(alpha));
41906
42381
  return ce.tuple(...args);
41907
42382
  }
41908
42383
  function rgbToHex(rgb) {
@@ -41910,7 +42385,7 @@ ${e.message}
41910
42385
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
41911
42386
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
41912
42387
  const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
41913
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
42388
+ if (rgb.alpha !== void 0) {
41914
42389
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
41915
42390
  return hex + a.toString(16).padStart(2, "0");
41916
42391
  }
@@ -41918,22 +42393,29 @@ ${e.message}
41918
42393
  }
41919
42394
  var COLORS_LIBRARY = {
41920
42395
  Color: {
41921
- description: "Convert a color string to a canonical sRGB tuple",
42396
+ description: "Parse a CSS-style color string to an Oklch color",
41922
42397
  complexity: 8e3,
41923
- signature: "(string) -> tuple",
42398
+ signature: "(string) -> color",
41924
42399
  evaluate: (ops, { engine: ce }) => {
41925
42400
  const input = isString(ops[0]) ? ops[0].string : void 0;
41926
42401
  if (!input) return ce.error("incompatible-type");
41927
42402
  const color = parseColor(input);
41928
42403
  if (color === 0 && input.trim().toLowerCase() !== "transparent")
41929
42404
  return ce.error("incompatible-type");
41930
- return colorNumberToTuple(ce, color);
42405
+ const r = color >>> 24 & 255;
42406
+ const g = color >>> 16 & 255;
42407
+ const b = color >>> 8 & 255;
42408
+ const a = normalizeAlpha((color & 255) / 255);
42409
+ const c = rgbToOklch({ r, g, b });
42410
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42411
+ if (a !== void 0) args.push(ce.number(a));
42412
+ return ce.function("Oklch", args);
41931
42413
  }
41932
42414
  },
41933
42415
  ColorToString: {
41934
42416
  description: "Convert a color to a string in the specified format",
41935
42417
  complexity: 8e3,
41936
- signature: "(any, string?) -> string",
42418
+ signature: "(color | string | tuple, string?) -> string",
41937
42419
  evaluate: (ops, { engine: ce }) => {
41938
42420
  const rgb = extractRgb(ce, ops[0]);
41939
42421
  if (!rgb) return ce.error("incompatible-type");
@@ -41945,7 +42427,7 @@ ${e.message}
41945
42427
  const r = Math.round(rgb.r);
41946
42428
  const g = Math.round(rgb.g);
41947
42429
  const b = Math.round(rgb.b);
41948
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42430
+ if (rgb.alpha !== void 0)
41949
42431
  return ce.string(`rgb(${r} ${g} ${b} / ${rgb.alpha})`);
41950
42432
  return ce.string(`rgb(${r} ${g} ${b})`);
41951
42433
  }
@@ -41954,17 +42436,17 @@ ${e.message}
41954
42436
  const h = Math.round(hsl.h * 10) / 10;
41955
42437
  const s = Math.round(hsl.s * 1e3) / 10;
41956
42438
  const l = Math.round(hsl.l * 1e3) / 10;
41957
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42439
+ if (rgb.alpha !== void 0)
41958
42440
  return ce.string(`hsl(${h} ${s}% ${l}% / ${rgb.alpha})`);
41959
42441
  return ce.string(`hsl(${h} ${s}% ${l}%)`);
41960
42442
  }
41961
42443
  case "oklch": {
41962
- const c = rgbToOklch(rgb);
42444
+ const c = colorExprToOklch(ops[0]) ?? asOklch(rgb);
41963
42445
  const L = Math.round(c.L * 1e3) / 1e3;
41964
42446
  const C = Math.round(c.C * 1e3) / 1e3;
41965
42447
  const H = Math.round(c.H * 10) / 10;
41966
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
41967
- return ce.string(`oklch(${L} ${C} ${H} / ${rgb.alpha})`);
42448
+ if (c.alpha !== void 0)
42449
+ return ce.string(`oklch(${L} ${C} ${H} / ${c.alpha})`);
41968
42450
  return ce.string(`oklch(${L} ${C} ${H})`);
41969
42451
  }
41970
42452
  default:
@@ -41975,60 +42457,44 @@ ${e.message}
41975
42457
  ColorMix: {
41976
42458
  description: "Mix two colors in OKLCh space",
41977
42459
  complexity: 8e3,
41978
- signature: "(any, any, number?) -> tuple",
42460
+ signature: "(color | string | tuple, color | string | tuple, number?) -> color",
41979
42461
  evaluate: (ops, { engine: ce }) => {
41980
- const rgb1 = extractRgb(ce, ops[0]);
41981
- const rgb2 = extractRgb(ce, ops[1]);
41982
- if (!rgb1 || !rgb2) return ce.error("incompatible-type");
41983
42462
  let ratio = 0.5;
41984
42463
  if (ops.length >= 3 && ops[2] !== void 0) {
41985
42464
  ratio = ops[2].re;
41986
42465
  if (!Number.isFinite(ratio)) return ce.error("expected-value");
41987
42466
  ratio = Math.max(0, Math.min(1, ratio));
41988
42467
  }
41989
- const c1 = rgbToOklch(rgb1);
41990
- const c2 = rgbToOklch(rgb2);
41991
- const mixed = oklchToRgb(lerpOklch(c1, c2, ratio));
41992
- const r = mixed.r / 255;
41993
- const g = mixed.g / 255;
41994
- const b = mixed.b / 255;
41995
- const a1 = rgb1.alpha ?? 1;
41996
- const a2 = rgb2.alpha ?? 1;
41997
- const alpha = a1 + (a2 - a1) * ratio;
41998
- if (Math.abs(alpha - 1) > 1e-4)
41999
- return ce.tuple(
42000
- ce.number(r),
42001
- ce.number(g),
42002
- ce.number(b),
42003
- ce.number(alpha)
42004
- );
42005
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42468
+ const oklch1 = toOklch(ce, ops[0]);
42469
+ const oklch2 = toOklch(ce, ops[1]);
42470
+ if (!oklch1 || !oklch2) return ce.error("incompatible-type");
42471
+ return oklchToExpr(ce, lerpOklchColor(oklch1, oklch2, ratio));
42006
42472
  }
42007
42473
  },
42008
42474
  Colormap: {
42009
42475
  description: "Sample colors from a named palette",
42010
42476
  complexity: 8e3,
42011
- signature: "(string, number?) -> any",
42477
+ signature: "(string, number?) -> color | list<color>",
42012
42478
  evaluate: (ops, { engine: ce }) => {
42013
42479
  const name = isString(ops[0]) ? ops[0].string : void 0;
42014
42480
  if (!name) return ce.error("incompatible-type");
42015
42481
  const palette = ALL_PALETTES[name];
42016
42482
  if (!palette) return ce.error("expected-value", name);
42017
42483
  if (ops.length < 2 || ops[1] === void 0) {
42018
- const tuples = palette.map(
42019
- (hex) => colorNumberToTuple(ce, parseColor(hex))
42484
+ const colors = palette.map(
42485
+ (hex) => colorNumberToOklch(ce, parseColor(hex))
42020
42486
  );
42021
- return ce.function("List", tuples);
42487
+ return ce.function("List", colors);
42022
42488
  }
42023
42489
  const val = ops[1].re;
42024
42490
  if (!Number.isFinite(val)) return ce.error("expected-value");
42025
42491
  if (Number.isInteger(val) && val >= 2) {
42026
42492
  const n = val;
42027
- const tuples = [];
42493
+ const colors = [];
42028
42494
  for (let i = 0; i < n; i++) {
42029
- tuples.push(samplePalette(ce, palette, i / (n - 1)));
42495
+ colors.push(samplePalette(ce, palette, i / (n - 1)));
42030
42496
  }
42031
- return ce.function("List", tuples);
42497
+ return ce.function("List", colors);
42032
42498
  }
42033
42499
  return samplePalette(ce, palette, val);
42034
42500
  }
@@ -42036,12 +42502,25 @@ ${e.message}
42036
42502
  ColorToColorspace: {
42037
42503
  description: "Convert a color to components in a target color space",
42038
42504
  complexity: 8e3,
42039
- signature: "(any, string) -> tuple",
42505
+ signature: "(color | string | tuple, string) -> tuple",
42040
42506
  evaluate: (ops, { engine: ce }) => {
42041
- const rgb = extractRgb(ce, ops[0]);
42042
- if (!rgb) return ce.error("incompatible-type");
42043
42507
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42044
42508
  if (!space) return ce.error("incompatible-type");
42509
+ if (space === "oklch" || space === "oklab" || space === "lab") {
42510
+ const oklch2 = colorExprToOklch(ops[0]);
42511
+ if (oklch2) {
42512
+ if (space === "oklch")
42513
+ return componentsTuple(
42514
+ ce,
42515
+ [oklch2.L, oklch2.C, oklch2.H],
42516
+ oklch2.alpha
42517
+ );
42518
+ const lab = oklchToOklab(oklch2);
42519
+ return componentsTuple(ce, [lab.L, lab.a, lab.b], lab.alpha);
42520
+ }
42521
+ }
42522
+ const rgb = extractRgb(ce, ops[0]);
42523
+ if (!rgb) return ce.error("incompatible-type");
42045
42524
  const alpha = rgb.alpha;
42046
42525
  switch (space) {
42047
42526
  case "rgb":
@@ -42071,17 +42550,27 @@ ${e.message}
42071
42550
  ColorFromColorspace: {
42072
42551
  description: "Convert color space components to a canonical sRGB tuple",
42073
42552
  complexity: 8e3,
42074
- signature: "(tuple, string) -> tuple",
42553
+ signature: "(color | tuple, string) -> tuple",
42075
42554
  evaluate: (ops, { engine: ce }) => {
42076
- const tuple = ops[0];
42077
- if (!isFunction2(tuple) || tuple.operator !== "Tuple" || tuple.ops.length < 3)
42078
- return ce.error("incompatible-type");
42079
- const c0 = tuple.ops[0].re;
42080
- const c1 = tuple.ops[1].re;
42081
- const c2 = tuple.ops[2].re;
42082
- const alpha = tuple.ops.length >= 4 ? tuple.ops[3].re : void 0;
42083
42555
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42084
42556
  if (!space) return ce.error("incompatible-type");
42557
+ let c0, c1, c2;
42558
+ let alpha;
42559
+ const arg = ops[0];
42560
+ const typed = readColorExpr(arg);
42561
+ if (typed) {
42562
+ c0 = typed.c0;
42563
+ c1 = typed.c1;
42564
+ c2 = typed.c2;
42565
+ alpha = typed.alpha;
42566
+ } else if (isFunction2(arg) && arg.operator === "Tuple" && arg.ops.length >= 3) {
42567
+ c0 = arg.ops[0].re;
42568
+ c1 = arg.ops[1].re;
42569
+ c2 = arg.ops[2].re;
42570
+ alpha = arg.ops.length >= 4 ? arg.ops[3].re : void 0;
42571
+ } else {
42572
+ return ce.error("incompatible-type");
42573
+ }
42085
42574
  let rgb;
42086
42575
  switch (space) {
42087
42576
  case "rgb":
@@ -42117,7 +42606,7 @@ ${e.message}
42117
42606
  ColorContrast: {
42118
42607
  description: "APCA contrast ratio between two colors",
42119
42608
  complexity: 8e3,
42120
- signature: "(any, any) -> number",
42609
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42121
42610
  evaluate: (ops, { engine: ce }) => {
42122
42611
  const bgRgb = extractRgb(ce, ops[0]);
42123
42612
  const fgRgb = extractRgb(ce, ops[1]);
@@ -42128,19 +42617,186 @@ ${e.message}
42128
42617
  ContrastingColor: {
42129
42618
  description: "Choose the foreground color with better APCA contrast against a background",
42130
42619
  complexity: 8e3,
42131
- signature: "(any, any?, any?) -> tuple",
42620
+ signature: "(color | string | tuple, (color | string | tuple)?, (color | string | tuple)?) -> color",
42132
42621
  evaluate: (ops, { engine: ce }) => {
42133
42622
  const bgRgb = extractRgb(ce, ops[0]);
42134
42623
  if (!bgRgb) return ce.error("incompatible-type");
42624
+ let packed;
42135
42625
  if (ops.length >= 3 && ops[1] !== void 0 && ops[2] !== void 0) {
42136
42626
  const fg1 = extractRgb(ce, ops[1]);
42137
42627
  const fg2 = extractRgb(ce, ops[2]);
42138
42628
  if (!fg1 || !fg2) return ce.error("incompatible-type");
42139
- const result2 = contrastingColor({ bg: bgRgb, fg1, fg2 });
42140
- return colorNumberToTuple(ce, result2);
42629
+ packed = contrastingColor({ bg: bgRgb, fg1, fg2 });
42630
+ } else {
42631
+ packed = contrastingColor(bgRgb);
42632
+ }
42633
+ const r = (packed >>> 24 & 255) / 255;
42634
+ const g = (packed >>> 16 & 255) / 255;
42635
+ const b = (packed >>> 8 & 255) / 255;
42636
+ const alpha = normalizeAlpha((packed & 255) / 255);
42637
+ const args = [ce.number(r), ce.number(g), ce.number(b)];
42638
+ if (alpha !== void 0) args.push(ce.number(alpha));
42639
+ return ce.function("Rgb", args);
42640
+ }
42641
+ },
42642
+ // ---------------------------------------------------------------------------
42643
+ // Color constructors. Each preserves its colorspace on evaluation; the
42644
+ // operator name is the discriminator. Components are interpreted per
42645
+ // colorspace conventions (Rgb channels 0-1, Hsv/Hsl hue in degrees with
42646
+ // sat/value 0-1, Oklab/Oklch L 0-1 with standard a/b/C/H ranges). The
42647
+ // optional 4th argument is alpha in [0, 1]. No clamping at evaluation time.
42648
+ // ---------------------------------------------------------------------------
42649
+ Rgb: {
42650
+ description: "sRGB color (channels 0-1, optional alpha 0-1)",
42651
+ complexity: 8e3,
42652
+ signature: "(number, number, number, number?) -> color"
42653
+ },
42654
+ Hsv: {
42655
+ description: "HSV color (hue degrees, saturation/value 0-1, optional alpha)",
42656
+ complexity: 8e3,
42657
+ signature: "(number, number, number, number?) -> color"
42658
+ },
42659
+ Hsl: {
42660
+ description: "HSL color (hue degrees, saturation/lightness 0-1, optional alpha)",
42661
+ complexity: 8e3,
42662
+ signature: "(number, number, number, number?) -> color"
42663
+ },
42664
+ Oklab: {
42665
+ description: "OKLab color (L 0-1, a/b ~ -0.4..0.4, optional alpha)",
42666
+ complexity: 8e3,
42667
+ signature: "(number, number, number, number?) -> color"
42668
+ },
42669
+ Oklch: {
42670
+ description: "OKLCh color (L 0-1, C 0-~0.4, hue degrees, optional alpha)",
42671
+ complexity: 8e3,
42672
+ signature: "(number, number, number, number?) -> color"
42673
+ },
42674
+ // ---------------------------------------------------------------------------
42675
+ // Color-space conversions. Each accepts any of the five color heads and
42676
+ // returns the same color in the named space. If the input is already in
42677
+ // the target space, returns the input unchanged.
42678
+ // ---------------------------------------------------------------------------
42679
+ AsRgb: {
42680
+ description: "Convert any color to sRGB (channels 0-1)",
42681
+ complexity: 8e3,
42682
+ signature: "(color) -> color",
42683
+ evaluate: (ops, { engine: ce }) => {
42684
+ const arg = ops[0];
42685
+ if (isFunction2(arg) && arg.operator === "Rgb")
42686
+ return normalizeColorHead(ce, arg);
42687
+ const rgb = colorExprToRgb(arg);
42688
+ if (!rgb) return ce.error("incompatible-type");
42689
+ const args = [
42690
+ ce.number(rgb.r / 255),
42691
+ ce.number(rgb.g / 255),
42692
+ ce.number(rgb.b / 255)
42693
+ ];
42694
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42695
+ return ce.function("Rgb", args);
42696
+ }
42697
+ },
42698
+ AsHsv: {
42699
+ description: "Convert any color to HSV (hue degrees, s/v 0-1)",
42700
+ complexity: 8e3,
42701
+ signature: "(color) -> color",
42702
+ evaluate: (ops, { engine: ce }) => {
42703
+ const arg = ops[0];
42704
+ if (isFunction2(arg) && arg.operator === "Hsv")
42705
+ return normalizeColorHead(ce, arg);
42706
+ const rgb = colorExprToRgb(arg);
42707
+ if (!rgb) return ce.error("incompatible-type");
42708
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
42709
+ const args = [ce.number(hsv.h), ce.number(hsv.s), ce.number(hsv.v)];
42710
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42711
+ return ce.function("Hsv", args);
42712
+ }
42713
+ },
42714
+ AsHsl: {
42715
+ description: "Convert any color to HSL (hue degrees, s/l 0-1)",
42716
+ complexity: 8e3,
42717
+ signature: "(color) -> color",
42718
+ evaluate: (ops, { engine: ce }) => {
42719
+ const arg = ops[0];
42720
+ if (isFunction2(arg) && arg.operator === "Hsl")
42721
+ return normalizeColorHead(ce, arg);
42722
+ const rgb = colorExprToRgb(arg);
42723
+ if (!rgb) return ce.error("incompatible-type");
42724
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
42725
+ const args = [ce.number(hsl.h), ce.number(hsl.s), ce.number(hsl.l)];
42726
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42727
+ return ce.function("Hsl", args);
42728
+ }
42729
+ },
42730
+ AsOklab: {
42731
+ description: "Convert any color to OKLab",
42732
+ complexity: 8e3,
42733
+ signature: "(color) -> color",
42734
+ evaluate: (ops, { engine: ce }) => {
42735
+ const arg = ops[0];
42736
+ if (isFunction2(arg) && arg.operator === "Oklab")
42737
+ return normalizeColorHead(ce, arg);
42738
+ if (isFunction2(arg) && arg.operator === "Oklch") {
42739
+ const c = readColorExpr(arg);
42740
+ if (!c) return ce.error("incompatible-type");
42741
+ const lab2 = oklchToOklab({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42742
+ const args2 = [ce.number(lab2.L), ce.number(lab2.a), ce.number(lab2.b)];
42743
+ if (lab2.alpha !== void 0) args2.push(ce.number(lab2.alpha));
42744
+ return ce.function("Oklab", args2);
42745
+ }
42746
+ const rgb = colorExprToRgb(arg);
42747
+ if (!rgb) return ce.error("incompatible-type");
42748
+ const lab = rgbToOklab(rgb);
42749
+ const args = [ce.number(lab.L), ce.number(lab.a), ce.number(lab.b)];
42750
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42751
+ return ce.function("Oklab", args);
42752
+ }
42753
+ },
42754
+ AsOklch: {
42755
+ description: "Convert any color to OKLCh",
42756
+ complexity: 8e3,
42757
+ signature: "(color) -> color",
42758
+ evaluate: (ops, { engine: ce }) => {
42759
+ const arg = ops[0];
42760
+ if (isFunction2(arg) && arg.operator === "Oklch")
42761
+ return normalizeColorHead(ce, arg);
42762
+ if (isFunction2(arg) && arg.operator === "Oklab") {
42763
+ const c2 = readColorExpr(arg);
42764
+ if (!c2) return ce.error("incompatible-type");
42765
+ const oklch2 = oklabToOklch({
42766
+ L: c2.c0,
42767
+ a: c2.c1,
42768
+ b: c2.c2,
42769
+ alpha: c2.alpha
42770
+ });
42771
+ const args2 = [
42772
+ ce.number(oklch2.L),
42773
+ ce.number(oklch2.C),
42774
+ ce.number(oklch2.H)
42775
+ ];
42776
+ if (oklch2.alpha !== void 0) args2.push(ce.number(oklch2.alpha));
42777
+ return ce.function("Oklch", args2);
42141
42778
  }
42142
- const result = contrastingColor(bgRgb);
42143
- return colorNumberToTuple(ce, result);
42779
+ const rgb = colorExprToRgb(arg);
42780
+ if (!rgb) return ce.error("incompatible-type");
42781
+ const c = rgbToOklch(rgb);
42782
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42783
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42784
+ return ce.function("Oklch", args);
42785
+ }
42786
+ },
42787
+ // ---------------------------------------------------------------------------
42788
+ // Perceptual difference. Returns ΔE_OK (Euclidean distance in OKLab),
42789
+ // an approximately perceptually uniform scalar.
42790
+ // ---------------------------------------------------------------------------
42791
+ ColorDelta: {
42792
+ description: "Perceptual color difference (\u0394E_OK) between two colors",
42793
+ complexity: 8e3,
42794
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42795
+ evaluate: (ops, { engine: ce }) => {
42796
+ const a = toOklch(ce, ops[0]);
42797
+ const b = toOklch(ce, ops[1]);
42798
+ if (!a || !b) return ce.error("incompatible-type");
42799
+ return ce.number(oklabDeltaE(oklchToOklab(a), oklchToOklab(b)));
42144
42800
  }
42145
42801
  }
42146
42802
  };
@@ -42160,12 +42816,14 @@ ${e.message}
42160
42816
  canonical: canonicalBlock,
42161
42817
  evaluate: evaluateBlock
42162
42818
  },
42163
- // A condition expression tests for one or more conditions of an expression
42164
- // ['Condition', value, "positive"]
42819
+ // A condition expression tests for one or more conditions of an expression.
42820
+ // Two forms:
42821
+ // ['Condition', value, "positive"] — tests value against named condition(s)
42822
+ // ['Condition', predicate] — set-builder predicate (e.g. x > 0)
42165
42823
  Condition: {
42166
42824
  description: "Test whether a value satisfies one or more conditions.",
42167
42825
  lazy: true,
42168
- signature: "(value, symbol) -> boolean",
42826
+ signature: "(expression, symbol?) -> boolean",
42169
42827
  evaluate: ([value, conds], { engine }) => {
42170
42828
  let conditions = [];
42171
42829
  if (isSymbol2(conds)) {
@@ -44618,6 +45276,34 @@ ${e.message}
44618
45276
  signature: "() -> expression",
44619
45277
  evaluate: (_ops, { engine }) => engine.expr(randomExpression())
44620
45278
  }
45279
+ },
45280
+ // ---------------------------------------------------------------------------
45281
+ // Opaque typed heads — registered so the names are in the standard set
45282
+ // (consumers can branch on the operator name); CE itself does not evaluate
45283
+ // them. Geometric primitives `Triangle`/`Sphere`/`Segment` and the action
45284
+ // arrow `To` (`a \to b`).
45285
+ // ---------------------------------------------------------------------------
45286
+ {
45287
+ Triangle: {
45288
+ description: "Triangle primitive \u2014 opaque typed head.",
45289
+ signature: "(any+) -> expression"
45290
+ },
45291
+ GeometricVector: {
45292
+ description: "Geometric vector (directed segment between two points) \u2014 opaque typed head. Distinct from the column-vector `Vector` operator.",
45293
+ signature: "(any, any) -> expression"
45294
+ },
45295
+ Sphere: {
45296
+ description: "Sphere primitive \u2014 opaque typed head.",
45297
+ signature: "(any+) -> expression"
45298
+ },
45299
+ Segment: {
45300
+ description: "Segment primitive \u2014 opaque typed head.",
45301
+ signature: "(any+) -> expression"
45302
+ },
45303
+ To: {
45304
+ description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45305
+ signature: "(any, any) -> nothing"
45306
+ }
44621
45307
  }
44622
45308
  ];
44623
45309
 
@@ -51754,6 +52440,7 @@ Error in definition of "${name}"`,
51754
52440
 
51755
52441
  // src/compute-engine/boxed-expression/cache.ts
51756
52442
  function cachedValue(v, generation, fn) {
52443
+ if (v.generation === generation && v.value !== null) return v.value;
51757
52444
  v.generation = generation;
51758
52445
  v.value = fn();
51759
52446
  return v.value;
@@ -53531,6 +54218,12 @@ Error in definition of "${name}"`,
53531
54218
  function _escapeJsonString(s) {
53532
54219
  return s;
53533
54220
  }
54221
+ function _serializeLatexMetadata(ce, expr2) {
54222
+ const syntax = ce.latexSyntax;
54223
+ const opts = ce.latexOptions;
54224
+ if (Object.keys(opts).length === 0) return syntax.serialize(expr2);
54225
+ return syntax.serialize(expr2, { ...opts });
54226
+ }
53534
54227
  function serializeSubtract(ce, a, b, options, metadata) {
53535
54228
  if (isNumber(a) && a.isNegative) {
53536
54229
  const v = a.numericValue;
@@ -53835,7 +54528,7 @@ Error in definition of "${name}"`,
53835
54528
  ];
53836
54529
  const md = { ...metadata ?? {} };
53837
54530
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53838
- md.latex = _escapeJsonString(md.latex ?? ce.latexSyntax.serialize(fn));
54531
+ md.latex = _escapeJsonString(md.latex ?? _serializeLatexMetadata(ce, fn));
53839
54532
  } else md.latex = "";
53840
54533
  if (!options.metadata.includes("wikidata")) md.wikidata = "";
53841
54534
  if (!md.latex && !md.wikidata && options.shorthands.includes("function"))
@@ -53860,7 +54553,7 @@ Error in definition of "${name}"`,
53860
54553
  }
53861
54554
  metadata = { ...metadata };
53862
54555
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53863
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(sym2);
54556
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, sym2);
53864
54557
  if (metadata.latex !== void 0)
53865
54558
  metadata.latex = _escapeJsonString(metadata.latex);
53866
54559
  } else metadata.latex = void 0;
@@ -54022,7 +54715,7 @@ Error in definition of "${name}"`,
54022
54715
  }
54023
54716
  }
54024
54717
  if (options.metadata.includes("latex") && ce.latexSyntax)
54025
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(result2 ?? { num });
54718
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, result2 ?? { num });
54026
54719
  if (result2) {
54027
54720
  if (metadata.latex !== void 0)
54028
54721
  return { sym: result2, latex: metadata.latex };
@@ -54038,7 +54731,7 @@ Error in definition of "${name}"`,
54038
54731
  if (value.isNaN()) {
54039
54732
  num = "NaN";
54040
54733
  if (options.metadata.includes("latex") && ce.latexSyntax)
54041
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
54734
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54042
54735
  return metadata.latex !== void 0 ? { num, latex: metadata.latex } : { num };
54043
54736
  }
54044
54737
  return serializeJsonFunction(
@@ -54072,7 +54765,7 @@ Error in definition of "${name}"`,
54072
54765
  value = Number(value);
54073
54766
  } else {
54074
54767
  if (options.metadata.includes("latex") && ce.latexSyntax)
54075
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({
54768
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, {
54076
54769
  num: value.toString()
54077
54770
  });
54078
54771
  if (metadata.latex !== void 0)
@@ -54086,7 +54779,7 @@ Error in definition of "${name}"`,
54086
54779
  result = value > 0 ? "PositiveInfinity" : "NegativeInfinity";
54087
54780
  else num = serializeRepeatingDecimals(value.toString(), options);
54088
54781
  if (options.metadata.includes("latex") && ce.latexSyntax)
54089
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
54782
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54090
54783
  if (result) {
54091
54784
  if (metadata.latex !== void 0)
54092
54785
  return { sym: result, latex: metadata.latex };
@@ -54839,8 +55532,7 @@ Error in definition of "${name}"`,
54839
55532
  vars: options?.vars,
54840
55533
  imports: options?.imports,
54841
55534
  preamble: options?.preamble,
54842
- realOnly: options?.realOnly,
54843
- hints: options?.hints
55535
+ realOnly: options?.realOnly
54844
55536
  });
54845
55537
  } catch (e) {
54846
55538
  if (options?.fallback ?? true) {
@@ -54852,8 +55544,7 @@ Error in definition of "${name}"`,
54852
55544
  ce.pushScope();
54853
55545
  try {
54854
55546
  if (vars && typeof vars === "object") {
54855
- for (const [k, v] of Object.entries(vars))
54856
- ce.assign(k, v);
55547
+ for (const [k, v] of Object.entries(vars)) ce.assign(k, v);
54857
55548
  }
54858
55549
  return expr2.evaluate().re;
54859
55550
  } finally {
@@ -60083,8 +60774,7 @@ Error in definition of "${name}"`,
60083
60774
  return { re: null, im: formatFloat(iScale) };
60084
60775
  }
60085
60776
  const compiledFactors = remaining.map((r) => compile3(r));
60086
- if (iScale !== 1)
60087
- compiledFactors.unshift(formatFloat(iScale));
60777
+ if (iScale !== 1) compiledFactors.unshift(formatFloat(iScale));
60088
60778
  const imCode = foldTerms(compiledFactors, "1.0", "*");
60089
60779
  return { re: null, im: imCode };
60090
60780
  }
@@ -60688,39 +61378,130 @@ Error in definition of "${name}"`,
60688
61378
  if (args.length >= 2)
60689
61379
  return `_SYS.colormap(${compile3(args[0])}, ${compile3(args[1])})`;
60690
61380
  return `_SYS.colormap(${compile3(args[0])})`;
61381
+ },
61382
+ // -----------------------------------------------------------------------
61383
+ // Color constructor heads. All compile to OKLCh arrays at runtime — the
61384
+ // canonical color representation in this target. The constructors take
61385
+ // their own colorspace's components and convert internally.
61386
+ // (Mirrors the GPU target's design: color values are vec3 OKLCh.)
61387
+ // -----------------------------------------------------------------------
61388
+ Rgb: (args, compile3) => {
61389
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
61390
+ return `_SYS.rgb(${args.map(compile3).join(", ")})`;
61391
+ },
61392
+ Hsv: (args, compile3) => {
61393
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
61394
+ return `_SYS.hsv(${args.map(compile3).join(", ")})`;
61395
+ },
61396
+ Hsl: (args, compile3) => {
61397
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
61398
+ return `_SYS.hsl(${args.map(compile3).join(", ")})`;
61399
+ },
61400
+ Oklab: (args, compile3) => {
61401
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
61402
+ return `_SYS.oklab(${args.map(compile3).join(", ")})`;
61403
+ },
61404
+ Oklch: (args, compile3) => {
61405
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
61406
+ return `_SYS.oklch(${args.map(compile3).join(", ")})`;
61407
+ },
61408
+ // -----------------------------------------------------------------------
61409
+ // As* converters. Compile-time output convention matches the engine and
61410
+ // the GPU target: each returns components in the named space as a 3- or
61411
+ // 4-element array. `AsRgb` uses 0-1 sRGB channels (consistent across all
61412
+ // layers). `AsOklch` is the identity (canonical form).
61413
+ // -----------------------------------------------------------------------
61414
+ AsRgb: ([c], compile3) => {
61415
+ if (c === null) throw new Error("AsRgb: no argument");
61416
+ return `_SYS.asRgb(${compile3(c)})`;
61417
+ },
61418
+ AsHsv: ([c], compile3) => {
61419
+ if (c === null) throw new Error("AsHsv: no argument");
61420
+ return `_SYS.asHsv(${compile3(c)})`;
61421
+ },
61422
+ AsHsl: ([c], compile3) => {
61423
+ if (c === null) throw new Error("AsHsl: no argument");
61424
+ return `_SYS.asHsl(${compile3(c)})`;
61425
+ },
61426
+ AsOklab: ([c], compile3) => {
61427
+ if (c === null) throw new Error("AsOklab: no argument");
61428
+ return `_SYS.asOklab(${compile3(c)})`;
61429
+ },
61430
+ AsOklch: ([c], compile3) => {
61431
+ if (c === null) throw new Error("AsOklch: no argument");
61432
+ return compile3(c);
61433
+ },
61434
+ // Perceptual color difference (ΔE_OK).
61435
+ ColorDelta: ([a, b], compile3) => {
61436
+ if (a === null || b === null)
61437
+ throw new Error("ColorDelta: need two colors");
61438
+ return `_SYS.colorDelta(${compile3(a)}, ${compile3(b)})`;
61439
+ },
61440
+ // Euclidean distance between two tuples (any positive dimension).
61441
+ // The GPU target maps `Distance` to the GLSL/WGSL `distance()` builtin
61442
+ // (vec-only); this JS handler works on plain arrays of any length.
61443
+ Distance: ([a, b], compile3) => {
61444
+ if (a === null || b === null) throw new Error("Distance: need two points");
61445
+ return `_SYS.distance(${compile3(a)}, ${compile3(b)})`;
60691
61446
  }
60692
61447
  };
60693
61448
  function toRI(c) {
60694
61449
  return { re: c.re, im: c.im };
60695
61450
  }
61451
+ function normalizeAlpha2(a) {
61452
+ if (a === void 0) return void 0;
61453
+ if (!Number.isFinite(a)) return void 0;
61454
+ if (Math.abs(a - 1) < 1e-9) return void 0;
61455
+ return a;
61456
+ }
60696
61457
  function toRgb255(input) {
60697
61458
  if (typeof input === "string") {
60698
61459
  const c = parseColor(input);
60699
- return {
61460
+ const rgb2 = {
60700
61461
  r: c >>> 24 & 255,
60701
61462
  g: c >>> 16 & 255,
60702
- b: c >>> 8 & 255,
60703
- alpha: (c & 255) / 255
61463
+ b: c >>> 8 & 255
60704
61464
  };
61465
+ const alpha = normalizeAlpha2((c & 255) / 255);
61466
+ if (alpha !== void 0) rgb2.alpha = alpha;
61467
+ return rgb2;
61468
+ }
61469
+ const rgb = oklchToRgb({ L: input[0], C: input[1], H: input[2] });
61470
+ if (input.length >= 4) {
61471
+ const alpha = normalizeAlpha2(input[3]);
61472
+ if (alpha !== void 0) rgb.alpha = alpha;
60705
61473
  }
60706
- const rgb = {
60707
- r: input[0] * 255,
60708
- g: input[1] * 255,
60709
- b: input[2] * 255
60710
- };
60711
- if (input.length >= 4) rgb.alpha = input[3];
60712
61474
  return rgb;
60713
61475
  }
60714
- function packedToArray(c) {
60715
- const r = (c >>> 24 & 255) / 255;
60716
- const g = (c >>> 16 & 255) / 255;
60717
- const b = (c >>> 8 & 255) / 255;
60718
- const a = (c & 255) / 255;
60719
- return Math.abs(a - 1) < 1e-4 ? [r, g, b] : [r, g, b, a];
61476
+ function toOklch2(input) {
61477
+ if (typeof input === "string") {
61478
+ const c = parseColor(input);
61479
+ const r = c >>> 24 & 255;
61480
+ const g = c >>> 16 & 255;
61481
+ const b = c >>> 8 & 255;
61482
+ const oklch2 = rgbToOklch({ r, g, b });
61483
+ const alpha = normalizeAlpha2((c & 255) / 255);
61484
+ if (alpha !== void 0) oklch2.alpha = alpha;
61485
+ return oklch2;
61486
+ }
61487
+ return {
61488
+ L: input[0],
61489
+ C: input[1],
61490
+ H: input[2],
61491
+ alpha: input.length >= 4 ? normalizeAlpha2(input[3]) : void 0
61492
+ };
61493
+ }
61494
+ function packedToOklch(c) {
61495
+ const r = c >>> 24 & 255;
61496
+ const g = c >>> 16 & 255;
61497
+ const b = c >>> 8 & 255;
61498
+ const oklch2 = rgbToOklch({ r, g, b });
61499
+ const alpha = normalizeAlpha2((c & 255) / 255);
61500
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
60720
61501
  }
60721
61502
  var colorHelpers = {
60722
61503
  color(input) {
60723
- return packedToArray(parseColor(input));
61504
+ return packedToOklch(parseColor(input));
60724
61505
  },
60725
61506
  colorToString(input, format) {
60726
61507
  const rgb = toRgb255(input);
@@ -60731,7 +61512,7 @@ Error in definition of "${name}"`,
60731
61512
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
60732
61513
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
60733
61514
  let hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
60734
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
61515
+ if (rgb.alpha !== void 0) {
60735
61516
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
60736
61517
  hex += a.toString(16).padStart(2, "0");
60737
61518
  }
@@ -60741,7 +61522,7 @@ Error in definition of "${name}"`,
60741
61522
  const r = Math.round(rgb.r);
60742
61523
  const g = Math.round(rgb.g);
60743
61524
  const b = Math.round(rgb.b);
60744
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61525
+ if (rgb.alpha !== void 0)
60745
61526
  return `rgb(${r} ${g} ${b} / ${rgb.alpha})`;
60746
61527
  return `rgb(${r} ${g} ${b})`;
60747
61528
  }
@@ -60750,7 +61531,7 @@ Error in definition of "${name}"`,
60750
61531
  const h = Math.round(hsl.h * 10) / 10;
60751
61532
  const s = Math.round(hsl.s * 1e3) / 10;
60752
61533
  const l = Math.round(hsl.l * 1e3) / 10;
60753
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61534
+ if (rgb.alpha !== void 0)
60754
61535
  return `hsl(${h} ${s}% ${l}% / ${rgb.alpha})`;
60755
61536
  return `hsl(${h} ${s}% ${l}%)`;
60756
61537
  }
@@ -60759,7 +61540,7 @@ Error in definition of "${name}"`,
60759
61540
  const L = Math.round(c.L * 1e3) / 1e3;
60760
61541
  const C = Math.round(c.C * 1e3) / 1e3;
60761
61542
  const H = Math.round(c.H * 10) / 10;
60762
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61543
+ if (rgb.alpha !== void 0)
60763
61544
  return `oklch(${L} ${C} ${H} / ${rgb.alpha})`;
60764
61545
  return `oklch(${L} ${C} ${H})`;
60765
61546
  }
@@ -60768,29 +61549,29 @@ Error in definition of "${name}"`,
60768
61549
  }
60769
61550
  },
60770
61551
  colorMix(input1, input2, ratio = 0.5) {
60771
- const rgb1 = toRgb255(input1);
60772
- const rgb2 = toRgb255(input2);
61552
+ const c1 = toOklch2(input1);
61553
+ const c2 = toOklch2(input2);
60773
61554
  ratio = Math.max(0, Math.min(1, ratio));
60774
- const c1 = rgbToOklch(rgb1);
60775
- const c2 = rgbToOklch(rgb2);
60776
- let dh = c2.H - c1.H;
60777
- if (dh > 180) dh -= 360;
60778
- if (dh < -180) dh += 360;
60779
- let H = c1.H + dh * ratio;
60780
- if (H < 0) H += 360;
60781
- if (H >= 360) H -= 360;
60782
- const mixed = oklchToRgb({
60783
- L: c1.L + (c2.L - c1.L) * ratio,
60784
- C: c1.C + (c2.C - c1.C) * ratio,
60785
- H
60786
- });
60787
- const r = mixed.r / 255;
60788
- const g = mixed.g / 255;
60789
- const b = mixed.b / 255;
60790
- const a1 = rgb1.alpha ?? 1;
60791
- const a2 = rgb2.alpha ?? 1;
60792
- const alpha = a1 + (a2 - a1) * ratio;
60793
- return Math.abs(alpha - 1) > 1e-4 ? [r, g, b, alpha] : [r, g, b];
61555
+ const c1Achromatic = c1.C < 1e-6;
61556
+ const c2Achromatic = c2.C < 1e-6;
61557
+ let H;
61558
+ if (c1Achromatic && c2Achromatic) H = c1.H;
61559
+ else if (c1Achromatic) H = c2.H;
61560
+ else if (c2Achromatic) H = c1.H;
61561
+ else {
61562
+ let dh = c2.H - c1.H;
61563
+ if (dh > 180) dh -= 360;
61564
+ if (dh < -180) dh += 360;
61565
+ H = c1.H + dh * ratio;
61566
+ if (H < 0) H += 360;
61567
+ if (H >= 360) H -= 360;
61568
+ }
61569
+ const L = c1.L + (c2.L - c1.L) * ratio;
61570
+ const C = c1.C + (c2.C - c1.C) * ratio;
61571
+ const a1 = c1.alpha ?? 1;
61572
+ const a2 = c2.alpha ?? 1;
61573
+ const alpha = normalizeAlpha2(a1 + (a2 - a1) * ratio);
61574
+ return alpha !== void 0 ? [L, C, H, alpha] : [L, C, H];
60794
61575
  },
60795
61576
  colorContrast(bg, fg) {
60796
61577
  return apca(toRgb255(bg), toRgb255(fg));
@@ -60798,11 +61579,11 @@ Error in definition of "${name}"`,
60798
61579
  contrastingColor(bg, fg1, fg2) {
60799
61580
  const bgRgb = toRgb255(bg);
60800
61581
  if (fg1 !== void 0 && fg2 !== void 0) {
60801
- return packedToArray(
61582
+ return packedToOklch(
60802
61583
  contrastingColor({ bg: bgRgb, fg1: toRgb255(fg1), fg2: toRgb255(fg2) })
60803
61584
  );
60804
61585
  }
60805
- return packedToArray(contrastingColor(bgRgb));
61586
+ return packedToOklch(contrastingColor(bgRgb));
60806
61587
  },
60807
61588
  colorToColorspace(input, space) {
60808
61589
  const rgb = toRgb255(input);
@@ -60831,7 +61612,7 @@ Error in definition of "${name}"`,
60831
61612
  default:
60832
61613
  throw new Error(`Unknown color space: ${space}`);
60833
61614
  }
60834
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
61615
+ if (alpha !== void 0) result.push(alpha);
60835
61616
  return result;
60836
61617
  },
60837
61618
  colormap(name, arg) {
@@ -60843,7 +61624,7 @@ Error in definition of "${name}"`,
60843
61624
  const palette = allPalettes[name];
60844
61625
  if (!palette) throw new Error(`Unknown palette: ${name}`);
60845
61626
  const colors = palette.map(
60846
- (hex) => parseColorToRgb01(hex)
61627
+ (hex) => packedToOklch(parseColor(hex))
60847
61628
  );
60848
61629
  if (arg === void 0) return colors;
60849
61630
  if (Number.isInteger(arg) && arg >= 2) {
@@ -60867,62 +61648,128 @@ Error in definition of "${name}"`,
60867
61648
  const frac = pos - i;
60868
61649
  if (frac === 0 || i >= colors.length - 1)
60869
61650
  return [...colors[Math.min(i, colors.length - 1)]];
60870
- const rgb1 = {
60871
- r: colors[i][0] * 255,
60872
- g: colors[i][1] * 255,
60873
- b: colors[i][2] * 255
60874
- };
60875
- const rgb2 = {
60876
- r: colors[i + 1][0] * 255,
60877
- g: colors[i + 1][1] * 255,
60878
- b: colors[i + 1][2] * 255
60879
- };
60880
- const c1 = rgbToOklch(rgb1);
60881
- const c2 = rgbToOklch(rgb2);
60882
- let dh = c2.H - c1.H;
60883
- if (dh > 180) dh -= 360;
60884
- if (dh < -180) dh += 360;
60885
- let H = c1.H + dh * frac;
60886
- if (H < 0) H += 360;
60887
- if (H >= 360) H -= 360;
60888
- const mixed = oklchToRgb({
60889
- L: c1.L + (c2.L - c1.L) * frac,
60890
- C: c1.C + (c2.C - c1.C) * frac,
60891
- H
60892
- });
60893
- return [mixed.r / 255, mixed.g / 255, mixed.b / 255];
61651
+ const [L1, C1, H1] = colors[i];
61652
+ const [L2, C2, H2] = colors[i + 1];
61653
+ const c1Achromatic = C1 < 1e-6;
61654
+ const c2Achromatic = C2 < 1e-6;
61655
+ let H;
61656
+ if (c1Achromatic && c2Achromatic) H = H1;
61657
+ else if (c1Achromatic) H = H2;
61658
+ else if (c2Achromatic) H = H1;
61659
+ else {
61660
+ let dh = H2 - H1;
61661
+ if (dh > 180) dh -= 360;
61662
+ if (dh < -180) dh += 360;
61663
+ H = H1 + dh * frac;
61664
+ if (H < 0) H += 360;
61665
+ if (H >= 360) H -= 360;
61666
+ }
61667
+ return [L1 + (L2 - L1) * frac, C1 + (C2 - C1) * frac, H];
60894
61668
  },
60895
61669
  colorFromColorspace(components, space) {
60896
61670
  const c0 = components[0];
60897
61671
  const c1 = components[1];
60898
61672
  const c2 = components[2];
60899
61673
  const alpha = components.length >= 4 ? components[3] : void 0;
60900
- let result;
61674
+ let oklch2;
60901
61675
  switch (space.toLowerCase()) {
60902
61676
  case "rgb":
60903
- result = [c0, c1, c2];
61677
+ oklch2 = rgbToOklch({ r: c0 * 255, g: c1 * 255, b: c2 * 255 });
60904
61678
  break;
60905
61679
  case "hsl": {
60906
- const r = hslToRgb(c0, c1, c2);
60907
- result = [r.r / 255, r.g / 255, r.b / 255];
61680
+ const rgb = hslToRgb(c0, c1, c2);
61681
+ oklch2 = rgbToOklch(rgb);
60908
61682
  break;
60909
61683
  }
60910
- case "oklch": {
60911
- const r = oklchToRgb({ L: c0, C: c1, H: c2 });
60912
- result = [r.r / 255, r.g / 255, r.b / 255];
61684
+ case "oklch":
61685
+ oklch2 = { L: c0, C: c1, H: c2 };
60913
61686
  break;
60914
- }
60915
61687
  case "oklab":
60916
- case "lab": {
60917
- const r = oklabToRgb({ L: c0, a: c1, b: c2 });
60918
- result = [r.r / 255, r.g / 255, r.b / 255];
61688
+ case "lab":
61689
+ oklch2 = oklabToOklch({ L: c0, a: c1, b: c2 });
60919
61690
  break;
60920
- }
60921
61691
  default:
60922
61692
  throw new Error(`Unknown color space: ${space}`);
60923
61693
  }
60924
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
60925
- return result;
61694
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
61695
+ },
61696
+ // -----------------------------------------------------------------------
61697
+ // Color constructors. Each accepts components in its colorspace's natural
61698
+ // units and returns the canonical OKLCh array `[L, C, H]` (or with alpha).
61699
+ // -----------------------------------------------------------------------
61700
+ rgb(r, g, b, alpha) {
61701
+ const c = rgbToOklch({ r: r * 255, g: g * 255, b: b * 255 });
61702
+ const a = normalizeAlpha2(alpha);
61703
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61704
+ },
61705
+ hsv(h, s, v, alpha) {
61706
+ const rgb = hsvToRgb(h, s, v);
61707
+ const c = rgbToOklch(rgb);
61708
+ const a = normalizeAlpha2(alpha);
61709
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61710
+ },
61711
+ hsl(h, s, l, alpha) {
61712
+ const rgb = hslToRgb(h, s, l);
61713
+ const c = rgbToOklch({ r: rgb.r, g: rgb.g, b: rgb.b });
61714
+ const a = normalizeAlpha2(alpha);
61715
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61716
+ },
61717
+ oklab(L, a, b, alpha) {
61718
+ const c = oklabToOklch({ L, a, b });
61719
+ const al = normalizeAlpha2(alpha);
61720
+ return al !== void 0 ? [c.L, c.C, c.H, al] : [c.L, c.C, c.H];
61721
+ },
61722
+ oklch(L, C, H, alpha) {
61723
+ const a = normalizeAlpha2(alpha);
61724
+ return a !== void 0 ? [L, C, H, a] : [L, C, H];
61725
+ },
61726
+ // -----------------------------------------------------------------------
61727
+ // As* converters. Inputs are anything `toOklch` accepts (string, packed
61728
+ // int, or OKLCh array). Outputs are 3- or 4-element arrays in the named
61729
+ // space. sRGB-based outputs (asRgb/asHsv/asHsl) use 0-1 channels for
61730
+ // consistency with the GPU target's shader convention.
61731
+ // -----------------------------------------------------------------------
61732
+ asRgb(input) {
61733
+ const rgb = toRgb255(input);
61734
+ const r = rgb.r / 255;
61735
+ const g = rgb.g / 255;
61736
+ const b = rgb.b / 255;
61737
+ return rgb.alpha !== void 0 ? [r, g, b, rgb.alpha] : [r, g, b];
61738
+ },
61739
+ asHsv(input) {
61740
+ const rgb = toRgb255(input);
61741
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
61742
+ return rgb.alpha !== void 0 ? [hsv.h, hsv.s, hsv.v, rgb.alpha] : [hsv.h, hsv.s, hsv.v];
61743
+ },
61744
+ asHsl(input) {
61745
+ const rgb = toRgb255(input);
61746
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
61747
+ return rgb.alpha !== void 0 ? [hsl.h, hsl.s, hsl.l, rgb.alpha] : [hsl.h, hsl.s, hsl.l];
61748
+ },
61749
+ asOklab(input) {
61750
+ const c = toOklch2(input);
61751
+ const lab = oklchToOklab({ L: c.L, C: c.C, H: c.H });
61752
+ return c.alpha !== void 0 ? [lab.L, lab.a, lab.b, c.alpha] : [lab.L, lab.a, lab.b];
61753
+ },
61754
+ // asOklch is identity — handled at compile time as a pass-through
61755
+ // Perceptual color difference (ΔE_OK).
61756
+ colorDelta(a, b) {
61757
+ const labA = oklchToOklab(toOklch2(a));
61758
+ const labB = oklchToOklab(toOklch2(b));
61759
+ return oklabDeltaE(labA, labB);
61760
+ },
61761
+ // Euclidean distance between two tuples. Plain numeric — not a color
61762
+ // operation despite living in the same helpers block.
61763
+ distance(a, b) {
61764
+ if (!Array.isArray(a) || !Array.isArray(b))
61765
+ throw new Error("Distance: expected two arrays");
61766
+ if (a.length !== b.length) throw new Error("Distance: dimension mismatch");
61767
+ let sumSq = 0;
61768
+ for (let i = 0; i < a.length; i++) {
61769
+ const d = a[i] - b[i];
61770
+ sumSq += d * d;
61771
+ }
61772
+ return Math.sqrt(sumSq);
60926
61773
  }
60927
61774
  };
60928
61775
  var SYS_HELPERS = {
@@ -61340,43 +62187,6 @@ Error in definition of "${name}"`,
61340
62187
  return b;
61341
62188
  }
61342
62189
 
61343
- // src/compute-engine/compilation/fractal-orbit.ts
61344
- function toBigDecimal(v) {
61345
- if (typeof v === "object" && "hi" in v)
61346
- return new BigDecimal(v.hi).add(new BigDecimal(v.lo));
61347
- return new BigDecimal(v);
61348
- }
61349
- function hpToNumber(v) {
61350
- if (typeof v === "number") return v;
61351
- if (typeof v === "string") return Number(v);
61352
- return v.hi + v.lo;
61353
- }
61354
- function computeReferenceOrbit(center, maxIter, precision) {
61355
- const prevPrecision = BigDecimal.precision;
61356
- BigDecimal.precision = precision;
61357
- try {
61358
- const cr = toBigDecimal(center[0]);
61359
- const ci = toBigDecimal(center[1]);
61360
- let zr = BigDecimal.ZERO;
61361
- let zi = BigDecimal.ZERO;
61362
- const ESCAPE = new BigDecimal(256);
61363
- const points = [];
61364
- for (let i = 0; i < maxIter; i++) {
61365
- points.push(zr.toNumber(), zi.toNumber());
61366
- const zr2 = zr.mul(zr).toPrecision(precision);
61367
- const zi2 = zi.mul(zi).toPrecision(precision);
61368
- const mag2 = zr2.add(zi2);
61369
- if (mag2.cmp(ESCAPE) > 0) break;
61370
- const new_zi = zr.mul(zi).toPrecision(precision).mul(2).add(ci);
61371
- zr = zr2.sub(zi2).add(cr);
61372
- zi = new_zi;
61373
- }
61374
- return new Float32Array(points);
61375
- } finally {
61376
- BigDecimal.precision = prevPrecision;
61377
- }
61378
- }
61379
-
61380
62190
  // src/compute-engine/compilation/gpu-target.ts
61381
62191
  var GPU_OPERATORS = {
61382
62192
  Add: ["+", 11],
@@ -61398,6 +62208,13 @@ Error in definition of "${name}"`,
61398
62208
  function gpuVec2(target) {
61399
62209
  return target?.language === "wgsl" ? "vec2f" : "vec2";
61400
62210
  }
62211
+ function gpuVec3(target) {
62212
+ return target?.language === "wgsl" ? "vec3f" : "vec3";
62213
+ }
62214
+ function readStringLiteral(expr2) {
62215
+ if (!isString(expr2)) return null;
62216
+ return expr2.string?.toLowerCase() ?? null;
62217
+ }
61401
62218
  function compileIntArg(expr2, compile3, target) {
61402
62219
  const c = tryGetConstant(expr2);
61403
62220
  if (c !== void 0 && Number.isInteger(c)) return c.toString();
@@ -61456,17 +62273,10 @@ Error in definition of "${name}"`,
61456
62273
  `for (${indexDecl} = ${lowerStr}; ${index} <= ${upperStr}; ${index}++) {`,
61457
62274
  ` ${acc} ${op}= ${body};`,
61458
62275
  `}`,
61459
- `return ${acc}`
62276
+ `return ${acc};`
61460
62277
  ];
61461
62278
  return lines.join("\n");
61462
62279
  }
61463
- function selectFractalStrategy(target) {
61464
- const radius = target.hints?.viewport?.radius;
61465
- if (radius === void 0) return "single";
61466
- if (radius > 1e-6) return "single";
61467
- if (radius > 1e-14) return "double";
61468
- return "perturbation";
61469
- }
61470
62280
  var GPU_FUNCTIONS = {
61471
62281
  // Variadic arithmetic (for function-call form, e.g., with vectors)
61472
62282
  Add: (args, compile3, target) => {
@@ -61517,8 +62327,7 @@ Error in definition of "${name}"`,
61517
62327
  const iScale = isSymbol2(iFactor, "ImaginaryUnit") ? 1 : iFactor.im;
61518
62328
  const realFactors = args.filter((_, i) => i !== iIndex);
61519
62329
  const v2 = gpuVec2(target);
61520
- if (realFactors.length === 0)
61521
- return `${v2}(0.0, ${formatFloat(iScale)})`;
62330
+ if (realFactors.length === 0) return `${v2}(0.0, ${formatFloat(iScale)})`;
61522
62331
  const factors = realFactors.map((f) => compile3(f));
61523
62332
  if (iScale !== 1) factors.unshift(formatFloat(iScale));
61524
62333
  const imCode = foldTerms(factors, "1.0", "*");
@@ -61571,8 +62380,7 @@ Error in definition of "${name}"`,
61571
62380
  if (isNumber(x) && x.im !== 0) {
61572
62381
  return `${gpuVec2(target)}(${formatFloat(-x.re)}, ${formatFloat(-x.im)})`;
61573
62382
  }
61574
- if (isSymbol2(x, "ImaginaryUnit"))
61575
- return `${gpuVec2(target)}(0.0, -1.0)`;
62383
+ if (isSymbol2(x, "ImaginaryUnit")) return `${gpuVec2(target)}(0.0, -1.0)`;
61576
62384
  return `(-${compile3(x)})`;
61577
62385
  },
61578
62386
  // Standard math functions with complex dispatch
@@ -61945,49 +62753,139 @@ Error in definition of "${name}"`,
61945
62753
  }
61946
62754
  const isWGSL = target?.language === "wgsl";
61947
62755
  const v3 = isWGSL ? "vec3f" : "vec3";
61948
- return `((_gpu_apca(${bg}, ${v3}(0.0)) > 50.0) ? ${v3}(0.0) : ${v3}(1.0))`;
62756
+ const black = `${v3}(0.0)`;
62757
+ const white = `${v3}(1.0, 0.0, 0.0)`;
62758
+ return `((_gpu_apca(${bg}, ${black}) > 50.0) ? ${black} : ${white})`;
61949
62759
  },
61950
62760
  ColorToColorspace: ([color, space], compile3) => {
61951
62761
  if (color === null || space === null)
61952
62762
  throw new Error("ColorToColorspace: need color and space");
61953
- return `_gpu_srgb_to_oklab(${compile3(color)})`;
62763
+ const spaceName = readStringLiteral(space);
62764
+ if (spaceName === null)
62765
+ throw new Error("ColorToColorspace: space must be a string literal");
62766
+ const c = compile3(color);
62767
+ switch (spaceName) {
62768
+ case "oklch":
62769
+ return c;
62770
+ case "oklab":
62771
+ case "lab":
62772
+ return `_gpu_oklch_to_oklab(${c})`;
62773
+ case "rgb":
62774
+ return `_gpu_oklch_to_srgb(${c})`;
62775
+ case "hsl":
62776
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${c}))`;
62777
+ case "hsv":
62778
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${c}))`;
62779
+ default:
62780
+ throw new Error(
62781
+ `ColorToColorspace: unsupported space "${spaceName}" on GPU target`
62782
+ );
62783
+ }
61954
62784
  },
61955
62785
  ColorFromColorspace: ([components, space], compile3) => {
61956
62786
  if (components === null || space === null)
61957
62787
  throw new Error("ColorFromColorspace: need components and space");
61958
- return `_gpu_oklab_to_srgb(${compile3(components)})`;
62788
+ const spaceName = readStringLiteral(space);
62789
+ if (spaceName === null)
62790
+ throw new Error("ColorFromColorspace: space must be a string literal");
62791
+ const c = compile3(components);
62792
+ switch (spaceName) {
62793
+ case "oklch":
62794
+ return c;
62795
+ case "oklab":
62796
+ case "lab":
62797
+ return `_gpu_oklab_to_oklch(${c})`;
62798
+ case "rgb":
62799
+ return `_gpu_srgb_to_oklch(${c})`;
62800
+ case "hsl":
62801
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${c}))`;
62802
+ case "hsv":
62803
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${c}))`;
62804
+ default:
62805
+ throw new Error(
62806
+ `ColorFromColorspace: unsupported space "${spaceName}" on GPU target`
62807
+ );
62808
+ }
62809
+ },
62810
+ // ---------------------------------------------------------------------------
62811
+ // Color literals. Each typed head compiles to a canonical OKLCh vec3.
62812
+ // Alpha (4th argument) is dropped — GPU color values are vec3 only. Pass
62813
+ // alpha as a separate uniform if it's needed at the framebuffer boundary.
62814
+ // ---------------------------------------------------------------------------
62815
+ Color: ([s], _compile2, target) => {
62816
+ if (s === null) throw new Error("Color: no argument");
62817
+ const str = readStringLiteral(s);
62818
+ if (str === null)
62819
+ throw new Error("Color: argument must be a string literal on GPU target");
62820
+ const packed = parseColor(str);
62821
+ if (packed === 0 && str.trim().toLowerCase() !== "transparent")
62822
+ throw new Error(`Color: invalid color string "${str}"`);
62823
+ const r = packed >>> 24 & 255;
62824
+ const g = packed >>> 16 & 255;
62825
+ const b = packed >>> 8 & 255;
62826
+ const oklch2 = rgbToOklch({ r, g, b });
62827
+ return `${gpuVec3(target)}(${formatFloat(oklch2.L)}, ${formatFloat(oklch2.C)}, ${formatFloat(oklch2.H)})`;
62828
+ },
62829
+ Rgb: (args, compile3, target) => {
62830
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
62831
+ const v3 = gpuVec3(target);
62832
+ return `_gpu_srgb_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
62833
+ },
62834
+ Hsv: (args, compile3, target) => {
62835
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
62836
+ const v3 = gpuVec3(target);
62837
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
62838
+ },
62839
+ Hsl: (args, compile3, target) => {
62840
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
62841
+ const v3 = gpuVec3(target);
62842
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
62843
+ },
62844
+ Oklab: (args, compile3, target) => {
62845
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
62846
+ const v3 = gpuVec3(target);
62847
+ return `_gpu_oklab_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
62848
+ },
62849
+ Oklch: (args, compile3, target) => {
62850
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
62851
+ const v3 = gpuVec3(target);
62852
+ return `${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})`;
62853
+ },
62854
+ // ---------------------------------------------------------------------------
62855
+ // As* operators. AsOklch is identity (canonical). The other As* return
62856
+ // components in the named space, equivalent to ColorToColorspace(c, 'x').
62857
+ // ---------------------------------------------------------------------------
62858
+ AsOklch: ([c], compile3) => {
62859
+ if (c === null) throw new Error("AsOklch: no argument");
62860
+ return compile3(c);
62861
+ },
62862
+ AsOklab: ([c], compile3) => {
62863
+ if (c === null) throw new Error("AsOklab: no argument");
62864
+ return `_gpu_oklch_to_oklab(${compile3(c)})`;
62865
+ },
62866
+ AsRgb: ([c], compile3) => {
62867
+ if (c === null) throw new Error("AsRgb: no argument");
62868
+ return `_gpu_oklch_to_srgb(${compile3(c)})`;
62869
+ },
62870
+ AsHsv: ([c], compile3) => {
62871
+ if (c === null) throw new Error("AsHsv: no argument");
62872
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${compile3(c)}))`;
62873
+ },
62874
+ AsHsl: ([c], compile3) => {
62875
+ if (c === null) throw new Error("AsHsl: no argument");
62876
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${compile3(c)}))`;
61959
62877
  },
61960
62878
  // Fractal functions
61961
62879
  Mandelbrot: ([c, maxIter], compile3, target) => {
61962
62880
  if (c === null || maxIter === null)
61963
62881
  throw new Error("Mandelbrot: missing arguments");
61964
62882
  const iterCode = compileIntArg(maxIter, compile3, target);
61965
- const strategy = selectFractalStrategy(target);
61966
- if (strategy === "double") {
61967
- const dpCoord = target?.language === "wgsl" ? "_dp_coord(v_uv)" : "_dp_coord()";
61968
- return `_fractal_mandelbrot_dp(${dpCoord}, ${iterCode})`;
61969
- }
61970
- if (strategy === "perturbation") {
61971
- const ptDelta = target?.language === "wgsl" ? "_pt_delta(v_uv)" : "_pt_delta()";
61972
- return `_fractal_mandelbrot_pt(${ptDelta}, ${iterCode})`;
61973
- }
61974
62883
  return `_fractal_mandelbrot(${compile3(c)}, ${iterCode})`;
61975
62884
  },
61976
62885
  Julia: ([z, c, maxIter], compile3, target) => {
61977
62886
  if (z === null || c === null || maxIter === null)
61978
62887
  throw new Error("Julia: missing arguments");
61979
62888
  const iterCode = compileIntArg(maxIter, compile3, target);
61980
- const strategy = selectFractalStrategy(target);
61981
- if (strategy === "double") {
61982
- const dpCoord = target?.language === "wgsl" ? "_dp_coord(v_uv)" : "_dp_coord()";
61983
- const cCode = compile3(c);
61984
- return `_fractal_julia_dp(${dpCoord}, vec4(${cCode}, vec2(0.0)), ${iterCode})`;
61985
- }
61986
- if (strategy === "perturbation") {
61987
- const ptDelta = target?.language === "wgsl" ? "_pt_delta(v_uv)" : "_pt_delta()";
61988
- const cCode = compile3(c);
61989
- return `_fractal_julia_pt(${ptDelta}, ${cCode}, ${iterCode})`;
61990
- }
61991
62889
  return `_fractal_julia(${compile3(z)}, ${compile3(c)}, ${iterCode})`;
61992
62890
  },
61993
62891
  // Vector/Matrix operations
@@ -62584,232 +63482,6 @@ fn _gpu_besselJ(n_in: i32, x_in: f32) -> f32 {
62584
63482
  for (var k2: i32 = 2; k2 <= M; k2 += 2) { norm += 2.0 * vals[k2]; }
62585
63483
  return sgn * vals[n] / norm;
62586
63484
  }
62587
- `;
62588
- var GPU_DS_ARITHMETIC_PREAMBLE_GLSL = `
62589
- // Split a float into high and low parts for exact multiplication
62590
- vec2 ds_split(float a) {
62591
- const float SPLIT = 4097.0; // 2^12 + 1
62592
- float t = SPLIT * a;
62593
- float hi = t - (t - a);
62594
- float lo = a - hi;
62595
- return vec2(hi, lo);
62596
- }
62597
-
62598
- // Create a double-single from a single float
62599
- vec2 ds_from(float a) {
62600
- return vec2(a, 0.0);
62601
- }
62602
-
62603
- // Error-free addition (Knuth TwoSum)
62604
- vec2 ds_add(vec2 a, vec2 b) {
62605
- float s = a.x + b.x;
62606
- float v = s - a.x;
62607
- float e = (a.x - (s - v)) + (b.x - v);
62608
- float lo = (a.y + b.y) + e;
62609
- float hi = s + lo;
62610
- lo = lo - (hi - s);
62611
- return vec2(hi, lo);
62612
- }
62613
-
62614
- // Double-single subtraction
62615
- vec2 ds_sub(vec2 a, vec2 b) {
62616
- return ds_add(a, vec2(-b.x, -b.y));
62617
- }
62618
-
62619
- // Error-free multiplication (Dekker TwoProduct)
62620
- vec2 ds_mul(vec2 a, vec2 b) {
62621
- float p = a.x * b.x;
62622
- vec2 sa = ds_split(a.x);
62623
- vec2 sb = ds_split(b.x);
62624
- float err = ((sa.x * sb.x - p) + sa.x * sb.y + sa.y * sb.x) + sa.y * sb.y;
62625
- err += a.x * b.y + a.y * b.x;
62626
- float hi = p + err;
62627
- float lo = err - (hi - p);
62628
- return vec2(hi, lo);
62629
- }
62630
-
62631
- // Optimized self-multiply
62632
- vec2 ds_sqr(vec2 a) {
62633
- float p = a.x * a.x;
62634
- vec2 sa = ds_split(a.x);
62635
- float err = ((sa.x * sa.x - p) + 2.0 * sa.x * sa.y) + sa.y * sa.y;
62636
- err += 2.0 * a.x * a.y;
62637
- float hi = p + err;
62638
- float lo = err - (hi - p);
62639
- return vec2(hi, lo);
62640
- }
62641
-
62642
- // Compare magnitude: returns -1, 0, or 1
62643
- float ds_cmp(vec2 a, vec2 b) {
62644
- float d = a.x - b.x;
62645
- if (d != 0.0) return sign(d);
62646
- return sign(a.y - b.y);
62647
- }
62648
- `;
62649
- var GPU_DS_ARITHMETIC_PREAMBLE_WGSL = `
62650
- fn ds_split(a: f32) -> vec2f {
62651
- const SPLIT: f32 = 4097.0;
62652
- let t = SPLIT * a;
62653
- let hi = t - (t - a);
62654
- let lo = a - hi;
62655
- return vec2f(hi, lo);
62656
- }
62657
-
62658
- fn ds_from(a: f32) -> vec2f {
62659
- return vec2f(a, 0.0);
62660
- }
62661
-
62662
- fn ds_add(a: vec2f, b: vec2f) -> vec2f {
62663
- let s = a.x + b.x;
62664
- let v = s - a.x;
62665
- let e = (a.x - (s - v)) + (b.x - v);
62666
- let lo_t = (a.y + b.y) + e;
62667
- let hi = s + lo_t;
62668
- let lo = lo_t - (hi - s);
62669
- return vec2f(hi, lo);
62670
- }
62671
-
62672
- fn ds_sub(a: vec2f, b: vec2f) -> vec2f {
62673
- return ds_add(a, vec2f(-b.x, -b.y));
62674
- }
62675
-
62676
- fn ds_mul(a: vec2f, b: vec2f) -> vec2f {
62677
- let p = a.x * b.x;
62678
- let sa = ds_split(a.x);
62679
- let sb = ds_split(b.x);
62680
- var err = ((sa.x * sb.x - p) + sa.x * sb.y + sa.y * sb.x) + sa.y * sb.y;
62681
- err += a.x * b.y + a.y * b.x;
62682
- let hi = p + err;
62683
- let lo = err - (hi - p);
62684
- return vec2f(hi, lo);
62685
- }
62686
-
62687
- fn ds_sqr(a: vec2f) -> vec2f {
62688
- let p = a.x * a.x;
62689
- let sa = ds_split(a.x);
62690
- var err = ((sa.x * sa.x - p) + 2.0 * sa.x * sa.y) + sa.y * sa.y;
62691
- err += 2.0 * a.x * a.y;
62692
- let hi = p + err;
62693
- let lo = err - (hi - p);
62694
- return vec2f(hi, lo);
62695
- }
62696
-
62697
- fn ds_cmp(a: vec2f, b: vec2f) -> f32 {
62698
- let d = a.x - b.x;
62699
- if (d != 0.0) { return sign(d); }
62700
- return sign(a.y - b.y);
62701
- }
62702
- `;
62703
- var GPU_FRACTAL_DP_PREAMBLE_GLSL = `
62704
- uniform float _dp_cx_hi;
62705
- uniform float _dp_cx_lo;
62706
- uniform float _dp_cy_hi;
62707
- uniform float _dp_cy_lo;
62708
- uniform float _dp_w;
62709
- uniform float _dp_h;
62710
-
62711
- vec4 _dp_coord() {
62712
- // Per-pixel offset from center \u2014 small, so float-precise
62713
- float dx = (v_uv.x - 0.5) * _dp_w;
62714
- float dy = (v_uv.y - 0.5) * _dp_h;
62715
- // Combine center (hi+lo) + delta with emulated double precision
62716
- vec2 cre = ds_add(vec2(_dp_cx_hi, _dp_cx_lo), ds_from(dx));
62717
- vec2 cim = ds_add(vec2(_dp_cy_hi, _dp_cy_lo), ds_from(dy));
62718
- return vec4(cre.x, cim.x, cre.y, cim.y);
62719
- }
62720
-
62721
- float _fractal_mandelbrot_dp(vec4 c, int maxIter) {
62722
- // c = (re_hi, im_hi, re_lo, im_lo)
62723
- vec2 cr = vec2(c.x, c.z); // real part as ds
62724
- vec2 ci = vec2(c.y, c.w); // imag part as ds
62725
- vec2 zr = vec2(0.0, 0.0);
62726
- vec2 zi = vec2(0.0, 0.0);
62727
- for (int i = 0; i < maxIter; i++) {
62728
- vec2 zr2 = ds_sqr(zr);
62729
- vec2 zi2 = ds_sqr(zi);
62730
- // |z|^2 > 4.0 ?
62731
- vec2 mag2 = ds_add(zr2, zi2);
62732
- if (mag2.x > 4.0)
62733
- return clamp((float(i) - log2(log2(mag2.x)) + 4.0) / float(maxIter), 0.0, 1.0);
62734
- // z = z^2 + c
62735
- vec2 new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci); // 2*zr*zi + ci
62736
- zr = ds_add(ds_sub(zr2, zi2), cr); // zr^2 - zi^2 + cr
62737
- zi = new_zi;
62738
- }
62739
- return 1.0;
62740
- }
62741
-
62742
- float _fractal_julia_dp(vec4 z_in, vec4 c, int maxIter) {
62743
- vec2 zr = vec2(z_in.x, z_in.z);
62744
- vec2 zi = vec2(z_in.y, z_in.w);
62745
- vec2 cr = vec2(c.x, c.z);
62746
- vec2 ci = vec2(c.y, c.w);
62747
- for (int i = 0; i < maxIter; i++) {
62748
- vec2 zr2 = ds_sqr(zr);
62749
- vec2 zi2 = ds_sqr(zi);
62750
- vec2 mag2 = ds_add(zr2, zi2);
62751
- if (mag2.x > 4.0)
62752
- return clamp((float(i) - log2(log2(mag2.x)) + 4.0) / float(maxIter), 0.0, 1.0);
62753
- vec2 new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62754
- zr = ds_add(ds_sub(zr2, zi2), cr);
62755
- zi = new_zi;
62756
- }
62757
- return 1.0;
62758
- }
62759
- `;
62760
- var GPU_FRACTAL_DP_PREAMBLE_WGSL = `
62761
- @group(0) @binding(10) var<uniform> _dp_cx_hi: f32;
62762
- @group(0) @binding(11) var<uniform> _dp_cx_lo: f32;
62763
- @group(0) @binding(12) var<uniform> _dp_cy_hi: f32;
62764
- @group(0) @binding(13) var<uniform> _dp_cy_lo: f32;
62765
- @group(0) @binding(14) var<uniform> _dp_w: f32;
62766
- @group(0) @binding(15) var<uniform> _dp_h: f32;
62767
-
62768
- fn _dp_coord(uv: vec2f) -> vec4f {
62769
- let dx = (uv.x - 0.5) * _dp_w;
62770
- let dy = (uv.y - 0.5) * _dp_h;
62771
- let cre = ds_add(vec2f(_dp_cx_hi, _dp_cx_lo), ds_from(dx));
62772
- let cim = ds_add(vec2f(_dp_cy_hi, _dp_cy_lo), ds_from(dy));
62773
- return vec4f(cre.x, cim.x, cre.y, cim.y);
62774
- }
62775
-
62776
- fn _fractal_mandelbrot_dp(c: vec4f, maxIter: i32) -> f32 {
62777
- let cr = vec2f(c.x, c.z);
62778
- let ci = vec2f(c.y, c.w);
62779
- var zr = vec2f(0.0, 0.0);
62780
- var zi = vec2f(0.0, 0.0);
62781
- for (var i: i32 = 0; i < maxIter; i++) {
62782
- let zr2 = ds_sqr(zr);
62783
- let zi2 = ds_sqr(zi);
62784
- let mag2 = ds_add(zr2, zi2);
62785
- if (mag2.x > 4.0) {
62786
- return clamp((f32(i) - log2(log2(mag2.x)) + 4.0) / f32(maxIter), 0.0, 1.0);
62787
- }
62788
- let new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62789
- zr = ds_add(ds_sub(zr2, zi2), cr);
62790
- zi = new_zi;
62791
- }
62792
- return 1.0;
62793
- }
62794
-
62795
- fn _fractal_julia_dp(z_in: vec4f, c: vec4f, maxIter: i32) -> f32 {
62796
- var zr = vec2f(z_in.x, z_in.z);
62797
- var zi = vec2f(z_in.y, z_in.w);
62798
- let cr = vec2f(c.x, c.z);
62799
- let ci = vec2f(c.y, c.w);
62800
- for (var i: i32 = 0; i < maxIter; i++) {
62801
- let zr2 = ds_sqr(zr);
62802
- let zi2 = ds_sqr(zi);
62803
- let mag2 = ds_add(zr2, zi2);
62804
- if (mag2.x > 4.0) {
62805
- return clamp((f32(i) - log2(log2(mag2.x)) + 4.0) / f32(maxIter), 0.0, 1.0);
62806
- }
62807
- let new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62808
- zr = ds_add(ds_sub(zr2, zi2), cr);
62809
- zi = new_zi;
62810
- }
62811
- return 1.0;
62812
- }
62813
63485
  `;
62814
63486
  var GPU_FRACTAL_PREAMBLE_GLSL = `
62815
63487
  float _fractal_mandelbrot(vec2 c, int maxIter) {
@@ -62853,208 +63525,6 @@ fn _fractal_julia(z_in: vec2f, c: vec2f, maxIter: i32) -> f32 {
62853
63525
  }
62854
63526
  return 1.0;
62855
63527
  }
62856
- `;
62857
- var GPU_FRACTAL_PT_PREAMBLE_GLSL = `
62858
- uniform sampler2D _refOrbit;
62859
- uniform int _refOrbitLen;
62860
- uniform int _refOrbitTexWidth;
62861
- uniform float _pt_offset_x;
62862
- uniform float _pt_offset_y;
62863
- uniform float _pt_w;
62864
- uniform float _pt_h;
62865
-
62866
- vec2 _pt_delta() {
62867
- float dx = _pt_offset_x + (v_uv.x - 0.5) * _pt_w;
62868
- float dy = _pt_offset_y + (v_uv.y - 0.5) * _pt_h;
62869
- return vec2(dx, dy);
62870
- }
62871
-
62872
- vec2 _pt_fetch_orbit(int i) {
62873
- int y = i / _refOrbitTexWidth;
62874
- int x = i - y * _refOrbitTexWidth;
62875
- return texelFetch(_refOrbit, ivec2(x, y), 0).rg;
62876
- }
62877
-
62878
- float _fractal_mandelbrot_pt(vec2 delta_c, int maxIter) {
62879
- float dr = 0.0;
62880
- float di = 0.0;
62881
- int orbitLen = min(maxIter, _refOrbitLen);
62882
- for (int i = 0; i < orbitLen; i++) {
62883
- vec2 Zn = _pt_fetch_orbit(i);
62884
- // delta_{n+1} = 2*Z_n*delta_n + delta_n^2 + delta_c
62885
- float new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62886
- float new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62887
- dr = new_dr;
62888
- di = new_di;
62889
- // Full z = Z_{n+1} + delta for escape check
62890
- vec2 Zn1 = (i + 1 < orbitLen) ? _pt_fetch_orbit(i + 1) : vec2(0.0);
62891
- float zr = Zn1.x + dr;
62892
- float zi = Zn1.y + di;
62893
- float mag2 = zr * zr + zi * zi;
62894
- if (mag2 > 4.0)
62895
- return clamp((float(i) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62896
- // Glitch detection: |delta|^2 > |Z|^2
62897
- float dmag2 = dr * dr + di * di;
62898
- float Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62899
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62900
- // Rebase to absolute coordinates and continue with single-float
62901
- float abs_zr = Zn1.x + dr;
62902
- float abs_zi = Zn1.y + di;
62903
- // Reconstruct absolute c from reference + delta
62904
- // (Use ds_from for the concept, but single-float suffices for fallback)
62905
- float cx = abs_zr - dr + delta_c.x;
62906
- float cy = abs_zi - di + delta_c.y;
62907
- for (int j = i + 1; j < maxIter; j++) {
62908
- float new_zr = abs_zr * abs_zr - abs_zi * abs_zi + cx;
62909
- abs_zi = 2.0 * abs_zr * abs_zi + cy;
62910
- abs_zr = new_zr;
62911
- mag2 = abs_zr * abs_zr + abs_zi * abs_zi;
62912
- if (mag2 > 4.0)
62913
- return clamp((float(j) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62914
- }
62915
- return 1.0;
62916
- }
62917
- }
62918
- return 1.0;
62919
- }
62920
-
62921
- float _fractal_julia_pt(vec2 z_delta, vec2 delta_c, int maxIter) {
62922
- float dr = z_delta.x;
62923
- float di = z_delta.y;
62924
- int orbitLen = min(maxIter, _refOrbitLen);
62925
- for (int i = 0; i < orbitLen; i++) {
62926
- vec2 Zn = _pt_fetch_orbit(i);
62927
- float new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62928
- float new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62929
- dr = new_dr;
62930
- di = new_di;
62931
- vec2 Zn1 = (i + 1 < orbitLen) ? _pt_fetch_orbit(i + 1) : vec2(0.0);
62932
- float zr = Zn1.x + dr;
62933
- float zi = Zn1.y + di;
62934
- float mag2 = zr * zr + zi * zi;
62935
- if (mag2 > 4.0)
62936
- return clamp((float(i) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62937
- float dmag2 = dr * dr + di * di;
62938
- float Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62939
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62940
- float abs_zr = Zn1.x + dr;
62941
- float abs_zi = Zn1.y + di;
62942
- float cx = delta_c.x;
62943
- float cy = delta_c.y;
62944
- for (int j = i + 1; j < maxIter; j++) {
62945
- float new_zr = abs_zr * abs_zr - abs_zi * abs_zi + cx;
62946
- abs_zi = 2.0 * abs_zr * abs_zi + cy;
62947
- abs_zr = new_zr;
62948
- mag2 = abs_zr * abs_zr + abs_zi * abs_zi;
62949
- if (mag2 > 4.0)
62950
- return clamp((float(j) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62951
- }
62952
- return 1.0;
62953
- }
62954
- }
62955
- return 1.0;
62956
- }
62957
- `;
62958
- var GPU_FRACTAL_PT_PREAMBLE_WGSL = `
62959
- @group(0) @binding(1) var _refOrbit: texture_2d<f32>;
62960
- var<uniform> _refOrbitLen: i32;
62961
- var<uniform> _refOrbitTexWidth: i32;
62962
- var<uniform> _pt_offset_x: f32;
62963
- var<uniform> _pt_offset_y: f32;
62964
- var<uniform> _pt_w: f32;
62965
- var<uniform> _pt_h: f32;
62966
-
62967
- fn _pt_delta(uv: vec2f) -> vec2f {
62968
- let dx = _pt_offset_x + (uv.x - 0.5) * _pt_w;
62969
- let dy = _pt_offset_y + (uv.y - 0.5) * _pt_h;
62970
- return vec2f(dx, dy);
62971
- }
62972
-
62973
- fn _pt_fetch_orbit(i: i32) -> vec2f {
62974
- let y = i / _refOrbitTexWidth;
62975
- let x = i - y * _refOrbitTexWidth;
62976
- return textureLoad(_refOrbit, vec2i(x, y), 0).rg;
62977
- }
62978
-
62979
- fn _fractal_mandelbrot_pt(delta_c: vec2f, maxIter: i32) -> f32 {
62980
- var dr: f32 = 0.0;
62981
- var di: f32 = 0.0;
62982
- let orbitLen = min(maxIter, _refOrbitLen);
62983
- for (var i: i32 = 0; i < orbitLen; i++) {
62984
- let Zn = _pt_fetch_orbit(i);
62985
- let new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62986
- let new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62987
- dr = new_dr;
62988
- di = new_di;
62989
- var Zn1 = vec2f(0.0);
62990
- if (i + 1 < orbitLen) { Zn1 = _pt_fetch_orbit(i + 1); }
62991
- let zr = Zn1.x + dr;
62992
- let zi = Zn1.y + di;
62993
- var mag2 = zr * zr + zi * zi;
62994
- if (mag2 > 4.0) {
62995
- return clamp((f32(i) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
62996
- }
62997
- let dmag2 = dr * dr + di * di;
62998
- let Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62999
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
63000
- var f_zr = Zn1.x + dr;
63001
- var f_zi = Zn1.y + di;
63002
- let cx = delta_c.x;
63003
- let cy = delta_c.y;
63004
- for (var j: i32 = i + 1; j < maxIter; j++) {
63005
- let t_zr = f_zr * f_zr - f_zi * f_zi + cx;
63006
- f_zi = 2.0 * f_zr * f_zi + cy;
63007
- f_zr = t_zr;
63008
- mag2 = f_zr * f_zr + f_zi * f_zi;
63009
- if (mag2 > 4.0) {
63010
- return clamp((f32(j) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
63011
- }
63012
- }
63013
- return 1.0;
63014
- }
63015
- }
63016
- return 1.0;
63017
- }
63018
-
63019
- fn _fractal_julia_pt(z_delta: vec2f, delta_c: vec2f, maxIter: i32) -> f32 {
63020
- var dr = z_delta.x;
63021
- var di = z_delta.y;
63022
- let orbitLen = min(maxIter, _refOrbitLen);
63023
- for (var i: i32 = 0; i < orbitLen; i++) {
63024
- let Zn = _pt_fetch_orbit(i);
63025
- let new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
63026
- let new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
63027
- dr = new_dr;
63028
- di = new_di;
63029
- var Zn1 = vec2f(0.0);
63030
- if (i + 1 < orbitLen) { Zn1 = _pt_fetch_orbit(i + 1); }
63031
- let zr = Zn1.x + dr;
63032
- let zi = Zn1.y + di;
63033
- var mag2 = zr * zr + zi * zi;
63034
- if (mag2 > 4.0) {
63035
- return clamp((f32(i) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
63036
- }
63037
- let dmag2 = dr * dr + di * di;
63038
- let Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
63039
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
63040
- var f_zr = Zn1.x + dr;
63041
- var f_zi = Zn1.y + di;
63042
- let cx = delta_c.x;
63043
- let cy = delta_c.y;
63044
- for (var j: i32 = i + 1; j < maxIter; j++) {
63045
- let t_zr = f_zr * f_zr - f_zi * f_zi + cx;
63046
- f_zi = 2.0 * f_zr * f_zi + cy;
63047
- f_zr = t_zr;
63048
- mag2 = f_zr * f_zr + f_zi * f_zi;
63049
- if (mag2 > 4.0) {
63050
- return clamp((f32(j) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
63051
- }
63052
- }
63053
- return 1.0;
63054
- }
63055
- }
63056
- return 1.0;
63057
- }
63058
63528
  `;
63059
63529
  var GPU_COLOR_PREAMBLE_GLSL = `
63060
63530
  float _gpu_srgb_to_linear(float c) {
@@ -63096,28 +63566,124 @@ vec3 _gpu_oklab_to_srgb(vec3 lab) {
63096
63566
 
63097
63567
  vec3 _gpu_oklab_to_oklch(vec3 lab) {
63098
63568
  float C = length(lab.yz);
63099
- float H = atan(lab.z, lab.y);
63569
+ float H = atan(lab.z, lab.y) * (180.0 / 3.14159265359);
63570
+ if (H < 0.0) H += 360.0;
63100
63571
  return vec3(lab.x, C, H);
63101
63572
  }
63102
63573
 
63103
63574
  vec3 _gpu_oklch_to_oklab(vec3 lch) {
63104
- return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
63575
+ float h_rad = lch.z * (3.14159265359 / 180.0);
63576
+ return vec3(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
63577
+ }
63578
+
63579
+ vec3 _gpu_srgb_to_oklch(vec3 rgb) {
63580
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
63581
+ }
63582
+
63583
+ vec3 _gpu_oklch_to_srgb(vec3 lch) {
63584
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
63105
63585
  }
63106
63586
 
63107
- vec3 _gpu_color_mix(vec3 rgb1, vec3 rgb2, float t) {
63108
- vec3 lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
63109
- vec3 lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
63587
+ // HSL conversion. Hue in degrees, saturation/lightness in 0-1.
63588
+ vec3 _gpu_hsl_to_rgb(vec3 hsl) {
63589
+ float h = hsl.x;
63590
+ float s = hsl.y;
63591
+ float l = hsl.z;
63592
+ float c = (1.0 - abs(2.0 * l - 1.0)) * s;
63593
+ float h6 = h / 60.0;
63594
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
63595
+ float r = 0.0;
63596
+ float g = 0.0;
63597
+ float b = 0.0;
63598
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63599
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63600
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63601
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63602
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63603
+ else { r = c; g = 0.0; b = x; }
63604
+ float m = l - c / 2.0;
63605
+ return vec3(r + m, g + m, b + m);
63606
+ }
63607
+
63608
+ vec3 _gpu_rgb_to_hsl(vec3 rgb) {
63609
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
63610
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
63611
+ float l = (maxc + minc) / 2.0;
63612
+ float d = maxc - minc;
63613
+ if (d < 1e-6) return vec3(0.0, 0.0, l);
63614
+ float s = d / (1.0 - abs(2.0 * l - 1.0));
63615
+ float h;
63616
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
63617
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
63618
+ else h = (rgb.x - rgb.y) / d + 4.0;
63619
+ h *= 60.0;
63620
+ if (h < 0.0) h += 360.0;
63621
+ return vec3(h, s, l);
63622
+ }
63623
+
63624
+ // HSV conversion. Hue in degrees, saturation/value in 0-1.
63625
+ vec3 _gpu_hsv_to_rgb(vec3 hsv) {
63626
+ float h = hsv.x;
63627
+ float s = hsv.y;
63628
+ float v = hsv.z;
63629
+ float c = v * s;
63630
+ float h6 = h / 60.0;
63631
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
63632
+ float r = 0.0;
63633
+ float g = 0.0;
63634
+ float b = 0.0;
63635
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63636
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63637
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63638
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63639
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63640
+ else { r = c; g = 0.0; b = x; }
63641
+ float m = v - c;
63642
+ return vec3(r + m, g + m, b + m);
63643
+ }
63644
+
63645
+ vec3 _gpu_rgb_to_hsv(vec3 rgb) {
63646
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
63647
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
63648
+ float v = maxc;
63649
+ float d = maxc - minc;
63650
+ if (d < 1e-6) return vec3(0.0, 0.0, v);
63651
+ float s = (maxc < 1e-6) ? 0.0 : d / maxc;
63652
+ float h;
63653
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
63654
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
63655
+ else h = (rgb.x - rgb.y) / d + 4.0;
63656
+ h *= 60.0;
63657
+ if (h < 0.0) h += 360.0;
63658
+ return vec3(h, s, v);
63659
+ }
63660
+
63661
+ vec3 _gpu_color_mix(vec3 lch1, vec3 lch2, float t) {
63110
63662
  float L = mix(lch1.x, lch2.x, t);
63111
63663
  float C = mix(lch1.y, lch2.y, t);
63112
- float dh = lch2.z - lch1.z;
63113
- const float PI = 3.14159265359;
63114
- if (dh > PI) dh -= 2.0 * PI;
63115
- if (dh < -PI) dh += 2.0 * PI;
63116
- float H = lch1.z + dh * t;
63117
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3(L, C, H)));
63664
+ bool a1 = lch1.y < 1e-6;
63665
+ bool a2 = lch2.y < 1e-6;
63666
+ float H;
63667
+ if (a1 && a2) {
63668
+ H = lch1.z;
63669
+ } else if (a1) {
63670
+ H = lch2.z;
63671
+ } else if (a2) {
63672
+ H = lch1.z;
63673
+ } else {
63674
+ float dh = lch2.z - lch1.z;
63675
+ if (dh > 180.0) dh -= 360.0;
63676
+ if (dh < -180.0) dh += 360.0;
63677
+ H = lch1.z + dh * t;
63678
+ if (H < 0.0) H += 360.0;
63679
+ if (H >= 360.0) H -= 360.0;
63680
+ }
63681
+ return vec3(L, C, H);
63118
63682
  }
63119
63683
 
63120
- float _gpu_apca(vec3 bg, vec3 fg) {
63684
+ float _gpu_apca(vec3 lch_bg, vec3 lch_fg) {
63685
+ vec3 bg = _gpu_oklch_to_srgb(lch_bg);
63686
+ vec3 fg = _gpu_oklch_to_srgb(lch_fg);
63121
63687
  float bgR = _gpu_srgb_to_linear(bg.x);
63122
63688
  float bgG = _gpu_srgb_to_linear(bg.y);
63123
63689
  float bgB = _gpu_srgb_to_linear(bg.z);
@@ -63128,9 +63694,7 @@ float _gpu_apca(vec3 bg, vec3 fg) {
63128
63694
  float fgY = 0.2126729 * fgR + 0.7151522 * fgG + 0.0721750 * fgB;
63129
63695
  float bgC = pow(bgY, 0.56);
63130
63696
  float fgC = pow(fgY, 0.57);
63131
- float contrast = (bgC > fgC)
63132
- ? (bgC - fgC) * 1.14
63133
- : (bgC - fgC) * 1.14;
63697
+ float contrast = (bgC - fgC) * 1.14;
63134
63698
  return contrast * 100.0;
63135
63699
  }
63136
63700
  `;
@@ -63174,28 +63738,133 @@ fn _gpu_oklab_to_srgb(lab: vec3f) -> vec3f {
63174
63738
 
63175
63739
  fn _gpu_oklab_to_oklch(lab: vec3f) -> vec3f {
63176
63740
  let C = length(lab.yz);
63177
- let H = atan2(lab.z, lab.y);
63741
+ var H = atan2(lab.z, lab.y) * (180.0 / 3.14159265359);
63742
+ if (H < 0.0) { H = H + 360.0; }
63178
63743
  return vec3f(lab.x, C, H);
63179
63744
  }
63180
63745
 
63181
63746
  fn _gpu_oklch_to_oklab(lch: vec3f) -> vec3f {
63182
- return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
63747
+ let h_rad = lch.z * (3.14159265359 / 180.0);
63748
+ return vec3f(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
63749
+ }
63750
+
63751
+ fn _gpu_srgb_to_oklch(rgb: vec3f) -> vec3f {
63752
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
63753
+ }
63754
+
63755
+ fn _gpu_oklch_to_srgb(lch: vec3f) -> vec3f {
63756
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
63757
+ }
63758
+
63759
+ fn _gpu_hsl_to_rgb(hsl: vec3f) -> vec3f {
63760
+ let h = hsl.x;
63761
+ let s = hsl.y;
63762
+ let l = hsl.z;
63763
+ let c = (1.0 - abs(2.0 * l - 1.0)) * s;
63764
+ let h6 = h / 60.0;
63765
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
63766
+ var r: f32 = 0.0;
63767
+ var g: f32 = 0.0;
63768
+ var b: f32 = 0.0;
63769
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63770
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63771
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63772
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63773
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63774
+ else { r = c; g = 0.0; b = x; }
63775
+ let m = l - c / 2.0;
63776
+ return vec3f(r + m, g + m, b + m);
63183
63777
  }
63184
63778
 
63185
- fn _gpu_color_mix(rgb1: vec3f, rgb2: vec3f, t: f32) -> vec3f {
63186
- let lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
63187
- let lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
63779
+ fn _gpu_rgb_to_hsl(rgb: vec3f) -> vec3f {
63780
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
63781
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
63782
+ let l = (maxc + minc) / 2.0;
63783
+ let d = maxc - minc;
63784
+ if (d < 1e-6) { return vec3f(0.0, 0.0, l); }
63785
+ let s = d / (1.0 - abs(2.0 * l - 1.0));
63786
+ var h: f32;
63787
+ if (maxc == rgb.x) {
63788
+ let v = (rgb.y - rgb.z) / d;
63789
+ h = v - 6.0 * floor(v / 6.0);
63790
+ } else if (maxc == rgb.y) {
63791
+ h = (rgb.z - rgb.x) / d + 2.0;
63792
+ } else {
63793
+ h = (rgb.x - rgb.y) / d + 4.0;
63794
+ }
63795
+ h = h * 60.0;
63796
+ if (h < 0.0) { h = h + 360.0; }
63797
+ return vec3f(h, s, l);
63798
+ }
63799
+
63800
+ fn _gpu_hsv_to_rgb(hsv: vec3f) -> vec3f {
63801
+ let h = hsv.x;
63802
+ let s = hsv.y;
63803
+ let v = hsv.z;
63804
+ let c = v * s;
63805
+ let h6 = h / 60.0;
63806
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
63807
+ var r: f32 = 0.0;
63808
+ var g: f32 = 0.0;
63809
+ var b: f32 = 0.0;
63810
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63811
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63812
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63813
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63814
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63815
+ else { r = c; g = 0.0; b = x; }
63816
+ let m = v - c;
63817
+ return vec3f(r + m, g + m, b + m);
63818
+ }
63819
+
63820
+ fn _gpu_rgb_to_hsv(rgb: vec3f) -> vec3f {
63821
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
63822
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
63823
+ let v = maxc;
63824
+ let d = maxc - minc;
63825
+ if (d < 1e-6) { return vec3f(0.0, 0.0, v); }
63826
+ var s: f32 = 0.0;
63827
+ if (maxc >= 1e-6) { s = d / maxc; }
63828
+ var h: f32;
63829
+ if (maxc == rgb.x) {
63830
+ let q = (rgb.y - rgb.z) / d;
63831
+ h = q - 6.0 * floor(q / 6.0);
63832
+ } else if (maxc == rgb.y) {
63833
+ h = (rgb.z - rgb.x) / d + 2.0;
63834
+ } else {
63835
+ h = (rgb.x - rgb.y) / d + 4.0;
63836
+ }
63837
+ h = h * 60.0;
63838
+ if (h < 0.0) { h = h + 360.0; }
63839
+ return vec3f(h, s, v);
63840
+ }
63841
+
63842
+ fn _gpu_color_mix(lch1: vec3f, lch2: vec3f, t: f32) -> vec3f {
63188
63843
  let L = mix(lch1.x, lch2.x, t);
63189
63844
  let C = mix(lch1.y, lch2.y, t);
63190
- let PI = 3.14159265359;
63191
- var dh = lch2.z - lch1.z;
63192
- if (dh > PI) { dh -= 2.0 * PI; }
63193
- if (dh < -PI) { dh += 2.0 * PI; }
63194
- let H = lch1.z + dh * t;
63195
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3f(L, C, H)));
63845
+ let a1 = lch1.y < 1e-6;
63846
+ let a2 = lch2.y < 1e-6;
63847
+ var H: f32;
63848
+ if (a1 && a2) {
63849
+ H = lch1.z;
63850
+ } else if (a1) {
63851
+ H = lch2.z;
63852
+ } else if (a2) {
63853
+ H = lch1.z;
63854
+ } else {
63855
+ var dh = lch2.z - lch1.z;
63856
+ if (dh > 180.0) { dh = dh - 360.0; }
63857
+ if (dh < -180.0) { dh = dh + 360.0; }
63858
+ H = lch1.z + dh * t;
63859
+ if (H < 0.0) { H = H + 360.0; }
63860
+ if (H >= 360.0) { H = H - 360.0; }
63861
+ }
63862
+ return vec3f(L, C, H);
63196
63863
  }
63197
63864
 
63198
- fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63865
+ fn _gpu_apca(lch_bg: vec3f, lch_fg: vec3f) -> f32 {
63866
+ let bg = _gpu_oklch_to_srgb(lch_bg);
63867
+ let fg = _gpu_oklch_to_srgb(lch_fg);
63199
63868
  let bgR = _gpu_srgb_to_linear(bg.x);
63200
63869
  let bgG = _gpu_srgb_to_linear(bg.y);
63201
63870
  let bgB = _gpu_srgb_to_linear(bg.z);
@@ -63483,7 +64152,7 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63483
64152
  if (stmts.length === 0) return "";
63484
64153
  const last = stmts.length - 1;
63485
64154
  stmts[last] = `return ${stmts[last]}`;
63486
- return stmts.join(";\n");
64155
+ return stmts.join(";\n") + ";";
63487
64156
  },
63488
64157
  ...options
63489
64158
  };
@@ -63494,7 +64163,6 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63494
64163
  const constants = this.getConstants();
63495
64164
  const v2 = this.languageId === "wgsl" ? "vec2f" : "vec2";
63496
64165
  const target = this.createTarget({
63497
- hints: options.hints,
63498
64166
  functions: (id) => {
63499
64167
  if (userFunctions && id in userFunctions) {
63500
64168
  const fn = userFunctions[id];
@@ -63533,89 +64201,12 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63533
64201
  if (code.includes("_gpu_besselJ"))
63534
64202
  preamble += this.languageId === "wgsl" ? GPU_BESSELJ_PREAMBLE_WGSL : GPU_BESSELJ_PREAMBLE_GLSL;
63535
64203
  if (code.includes("_fractal_")) {
63536
- if (code.includes("_fractal_mandelbrot_pt") || code.includes("_fractal_julia_pt")) {
63537
- preamble += this.languageId === "wgsl" ? GPU_DS_ARITHMETIC_PREAMBLE_WGSL : GPU_DS_ARITHMETIC_PREAMBLE_GLSL;
63538
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PT_PREAMBLE_WGSL : GPU_FRACTAL_PT_PREAMBLE_GLSL;
63539
- } else if (code.includes("_fractal_mandelbrot_dp") || code.includes("_fractal_julia_dp")) {
63540
- preamble += this.languageId === "wgsl" ? GPU_DS_ARITHMETIC_PREAMBLE_WGSL : GPU_DS_ARITHMETIC_PREAMBLE_GLSL;
63541
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_DP_PREAMBLE_WGSL : GPU_FRACTAL_DP_PREAMBLE_GLSL;
63542
- } else {
63543
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
63544
- }
64204
+ preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
63545
64205
  }
63546
64206
  if (code.includes("_gpu_srgb_to") || code.includes("_gpu_oklab") || code.includes("_gpu_oklch") || code.includes("_gpu_color_mix") || code.includes("_gpu_apca")) {
63547
64207
  preamble += this.languageId === "wgsl" ? GPU_COLOR_PREAMBLE_WGSL : GPU_COLOR_PREAMBLE_GLSL;
63548
64208
  }
63549
64209
  if (preamble) result.preamble = preamble;
63550
- if (code.includes("_fractal_") && options.hints?.viewport) {
63551
- const strategy = selectFractalStrategy(target);
63552
- const radius = options.hints.viewport.radius;
63553
- switch (strategy) {
63554
- case "single":
63555
- result.staleWhen = { radiusBelow: 1e-6 };
63556
- break;
63557
- case "double":
63558
- result.staleWhen = { radiusBelow: 1e-14, radiusAbove: 1e-5 };
63559
- break;
63560
- case "perturbation":
63561
- result.staleWhen = {
63562
- radiusAbove: 1e-5,
63563
- radiusBelow: radius * 0.01,
63564
- centerDistance: radius * 2
63565
- };
63566
- break;
63567
- }
63568
- }
63569
- if ((code.includes("_fractal_mandelbrot_dp") || code.includes("_fractal_julia_dp")) && options.hints?.viewport) {
63570
- const cx = hpToNumber(options.hints.viewport.center[0]);
63571
- const cy = hpToNumber(options.hints.viewport.center[1]);
63572
- const size = options.hints.viewport.radius * 2;
63573
- const cx_hi = Math.fround(cx);
63574
- const cy_hi = Math.fround(cy);
63575
- result.uniforms = {
63576
- ...result.uniforms,
63577
- _dp_cx_hi: cx_hi,
63578
- _dp_cx_lo: cx - cx_hi,
63579
- _dp_cy_hi: cy_hi,
63580
- _dp_cy_lo: cy - cy_hi,
63581
- _dp_w: size,
63582
- _dp_h: size
63583
- };
63584
- }
63585
- if ((code.includes("_fractal_mandelbrot_pt") || code.includes("_fractal_julia_pt")) && options.hints?.viewport) {
63586
- const viewport = options.hints.viewport;
63587
- const size = viewport.radius * 2;
63588
- result.uniforms = {
63589
- ...result.uniforms,
63590
- _pt_offset_x: 0,
63591
- _pt_offset_y: 0,
63592
- _pt_w: size,
63593
- _pt_h: size
63594
- };
63595
- const digits = Math.max(50, Math.ceil(-Math.log10(viewport.radius)) + 10);
63596
- const maxIter = 1e3;
63597
- const orbit = computeReferenceOrbit(
63598
- viewport.center,
63599
- maxIter,
63600
- digits
63601
- );
63602
- const orbitLen = orbit.length / 2;
63603
- const texWidth = Math.min(orbitLen, 4096);
63604
- const texHeight = Math.ceil(orbitLen / texWidth);
63605
- result.textures = {
63606
- _refOrbit: {
63607
- data: orbit,
63608
- width: texWidth,
63609
- height: texHeight,
63610
- format: "rg32f"
63611
- }
63612
- };
63613
- result.uniforms = {
63614
- ...result.uniforms,
63615
- _refOrbitLen: orbitLen,
63616
- _refOrbitTexWidth: texWidth
63617
- };
63618
- }
63619
64210
  return result;
63620
64211
  }
63621
64212
  compileToSource(expr2, _options = {}) {
@@ -63660,7 +64251,7 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63660
64251
  if (body.includes("\n")) {
63661
64252
  const indented = body.split("\n").map((l) => ` ${l}`).join("\n");
63662
64253
  return `${returnType} ${functionName}(${params}) {
63663
- ${indented};
64254
+ ${indented}
63664
64255
  }`;
63665
64256
  }
63666
64257
  return `${returnType} ${functionName}(${params}) {
@@ -63771,7 +64362,7 @@ ${indented};
63771
64362
  return `fn ${functionName}(${params}) -> ${toWGSLType(
63772
64363
  returnType
63773
64364
  )} {
63774
- ${indented};
64365
+ ${indented}
63775
64366
  }`;
63776
64367
  }
63777
64368
  return `fn ${functionName}(${params}) -> ${toWGSLType(returnType)} {
@@ -68178,6 +68769,7 @@ ${code}`;
68178
68769
  this.pushScope(void 0, "global");
68179
68770
  this._compilationTargets.registerDefaults();
68180
68771
  if (options?.latexSyntax) this._latexSyntax = options.latexSyntax;
68772
+ if (options?.latexOptions) this._latexOptions = { ...options.latexOptions };
68181
68773
  hidePrivateProperties(this);
68182
68774
  }
68183
68775
  toJSON() {
@@ -68838,6 +69430,29 @@ ${code}`;
68838
69430
  );
68839
69431
  return this._latexSyntax;
68840
69432
  }
69433
+ /** @internal Engine-wide LaTeX parse/serialize options (e.g. decimalSeparator).
69434
+ * Merged into every `parse()` and `toLatex()` call between the LatexSyntax
69435
+ * instance defaults and any per-call overrides. */
69436
+ _latexOptions = {};
69437
+ /** Engine-wide LaTeX parse/serialize options.
69438
+ *
69439
+ * These options are merged into every `parse()` and `toLatex()` call.
69440
+ * Precedence (most-specific wins):
69441
+ * 1. LatexSyntax instance defaults (set at its construction)
69442
+ * 2. `ce.latexOptions` (this property)
69443
+ * 3. Per-call options passed to `ce.parse()` / `expr.toLatex()`
69444
+ *
69445
+ * Assigning replaces the whole bag. Use spread to merge:
69446
+ * ```ts
69447
+ * ce.latexOptions = { ...ce.latexOptions, decimalSeparator: '{,}' };
69448
+ * ```
69449
+ */
69450
+ get latexOptions() {
69451
+ return this._latexOptions;
69452
+ }
69453
+ set latexOptions(options) {
69454
+ this._latexOptions = { ...options };
69455
+ }
68841
69456
  parse(latex, options) {
68842
69457
  if (latex === null || latex === void 0) return null;
68843
69458
  if (typeof latex !== "string")
@@ -68845,7 +69460,6 @@ ${code}`;
68845
69460
  const syntax = this._requireLatexSyntax();
68846
69461
  const { form, ...parseOpts } = options ?? {};
68847
69462
  const result = syntax.parse(latex, {
68848
- decimalSeparator: ".",
68849
69463
  getSymbolType: (id) => {
68850
69464
  const def = this.lookupDefinition(id);
68851
69465
  if (!def) return BoxedType.unknown;
@@ -68857,6 +69471,7 @@ ${code}`;
68857
69471
  const def = this.lookupDefinition(id);
68858
69472
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
68859
69473
  },
69474
+ ...this._latexOptions,
68860
69475
  ...parseOpts
68861
69476
  });
68862
69477
  if (result === null) return null;
@@ -69020,14 +69635,14 @@ ${code}`;
69020
69635
  _setDefaultEngineFactory(() => new ComputeEngine());
69021
69636
 
69022
69637
  // src/compute-engine.ts
69023
- var version = "0.55.5";
69638
+ var version = "0.56.0";
69024
69639
  ComputeEngine._latexSyntaxFactory = () => new LatexSyntax();
69025
69640
  _setDefaultEngineFactory(
69026
69641
  () => new ComputeEngine({ latexSyntax: new LatexSyntax() })
69027
69642
  );
69028
69643
  globalThis[/* @__PURE__ */ Symbol.for("io.cortexjs.compute-engine")] = {
69029
69644
  ComputeEngine: ComputeEngine.prototype.constructor,
69030
- version: "0.55.5"
69645
+ version: "0.56.0"
69031
69646
  };
69032
69647
  return __toCommonJS(compute_engine_exports);
69033
69648
  })();