ggaction 0.0.9 → 0.0.11

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 (222) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +5 -4
  3. package/knowledge/action-card.schema.json +1 -0
  4. package/knowledge/action-cards.json +1 -10941
  5. package/knowledge/action-cards.schema.json +1 -0
  6. package/knowledge/extension-authoring.md +248 -0
  7. package/knowledge/intent-taxonomy.json +1 -183
  8. package/knowledge/intent-taxonomy.schema.json +1 -0
  9. package/knowledge/mcp-resources.json +1 -95
  10. package/knowledge/mcp-resources.schema.json +1 -0
  11. package/knowledge/task-packet.schema.json +1 -159
  12. package/knowledge/task-resolver.js +1 -1230
  13. package/package.json +19 -2
  14. package/src/actions/boxPlots/edit.js +45 -298
  15. package/src/actions/boxPlots/options.js +46 -77
  16. package/src/actions/boxPlots/resolve.js +1 -1
  17. package/src/actions/charts/bar.js +3 -0
  18. package/src/actions/charts/heatmap.js +73 -83
  19. package/src/actions/charts/index.js +1 -1
  20. package/src/actions/charts/parallel.js +35 -1
  21. package/src/actions/charts/shared.js +20 -0
  22. package/src/actions/composition/actions.js +46 -49
  23. package/src/actions/data/bin2d.js +105 -2
  24. package/src/actions/data/box.js +17 -17
  25. package/src/actions/data/density.js +42 -78
  26. package/src/actions/data/gradientProfile.js +22 -52
  27. package/src/actions/data/horizon.js +16 -41
  28. package/src/actions/data/index.js +10 -10
  29. package/src/actions/data/interval.js +16 -38
  30. package/src/actions/data/intervalEdit.js +55 -0
  31. package/src/actions/data/regression.js +7 -17
  32. package/src/actions/data/shared.js +47 -0
  33. package/src/actions/data/timeUnit.js +14 -38
  34. package/src/actions/data/window.js +14 -38
  35. package/src/actions/distributions/revision.js +154 -34
  36. package/src/actions/encodings/angle.js +9 -10
  37. package/src/actions/encodings/appearance.js +17 -51
  38. package/src/actions/encodings/density/resolve.js +3 -1
  39. package/src/actions/encodings/density.js +1 -15
  40. package/src/actions/encodings/horizon/resolve.js +13 -3
  41. package/src/actions/encodings/horizon.js +8 -7
  42. package/src/actions/encodings/index.js +9 -9
  43. package/src/actions/encodings/offset.js +8 -24
  44. package/src/actions/encodings/parallel.js +9 -10
  45. package/src/actions/encodings/pathOrder.js +6 -13
  46. package/src/actions/encodings/position/policies/index.js +4 -4
  47. package/src/actions/encodings/position/policies/line.js +47 -17
  48. package/src/actions/encodings/position/resolve.js +24 -5
  49. package/src/actions/encodings/ranged.js +18 -23
  50. package/src/actions/encodings/remove.js +15 -25
  51. package/src/actions/encodings/ruleAppearance.js +9 -24
  52. package/src/actions/encodings/shared.js +37 -0
  53. package/src/actions/encodings/strokeDash.js +8 -23
  54. package/src/actions/encodings/text.js +9 -10
  55. package/src/actions/errorBands/create.js +105 -39
  56. package/src/actions/errorBands/edit.js +89 -93
  57. package/src/actions/errorBands/index.js +1 -2
  58. package/src/actions/errorBars/create.js +78 -27
  59. package/src/actions/errorBars/edit.js +81 -48
  60. package/src/actions/errorBars/index.js +1 -2
  61. package/src/actions/facets/actions.js +45 -69
  62. package/src/actions/facets/derive.js +38 -53
  63. package/src/actions/facets/replay.js +0 -1
  64. package/src/actions/gradientPlots/components.js +59 -48
  65. package/src/actions/gradientPlots/edit.js +35 -216
  66. package/src/actions/gradientPlots/index.js +11 -10
  67. package/src/actions/gradientPlots/materialize.js +21 -35
  68. package/src/actions/gradientPlots/options.js +5 -7
  69. package/src/actions/gradientPlots/paint.js +9 -6
  70. package/src/actions/gradientPlots/resolve.js +1 -1
  71. package/src/actions/guides/axes/index.js +3 -3
  72. package/src/actions/guides/axes/labels.js +68 -26
  73. package/src/actions/guides/axes/lines.js +22 -2
  74. package/src/actions/guides/axes/policy.js +28 -8
  75. package/src/actions/guides/axes/ticks.js +27 -9
  76. package/src/actions/guides/axes/titles.js +113 -65
  77. package/src/actions/guides/grids/resolve.js +14 -0
  78. package/src/actions/guides/guides.js +2 -4
  79. package/src/actions/guides/index.js +2 -2
  80. package/src/actions/guides/legends/categorical/actions.js +44 -68
  81. package/src/actions/guides/legends/categorical/components.js +6 -1
  82. package/src/actions/guides/legends/categorical/index.js +2 -2
  83. package/src/actions/guides/legends/categorical/layout.js +148 -175
  84. package/src/actions/guides/legends/categorical/recipes.js +5 -0
  85. package/src/actions/guides/legends/categorical/resolve.js +8 -1
  86. package/src/actions/guides/legends/continuous/common.js +117 -66
  87. package/src/actions/guides/legends/continuous/gradient.js +38 -37
  88. package/src/actions/guides/legends/continuous/interval.js +97 -76
  89. package/src/actions/guides/legends/continuous/opacity.js +28 -38
  90. package/src/actions/guides/legends/edit.js +14 -1
  91. package/src/actions/guides/legends/index.js +1 -1
  92. package/src/actions/guides/legends/lane.js +2 -1
  93. package/src/actions/guides/legends/remove.js +8 -0
  94. package/src/actions/guides/legends/size.js +36 -43
  95. package/src/actions/guides/legends/strokeWidth.js +20 -26
  96. package/src/actions/guides/polar/axes/labels.js +17 -1
  97. package/src/actions/guides/polar/axes/shared.js +7 -1
  98. package/src/actions/guides/polar/axes/titles.js +19 -2
  99. package/src/actions/guides/polar/grids.js +4 -0
  100. package/src/actions/guides/polar/resolve.js +6 -0
  101. package/src/actions/index.js +13 -13
  102. package/src/actions/intervals/resolve.js +87 -117
  103. package/src/actions/marks/arc/actions.js +31 -36
  104. package/src/actions/marks/area/actions.js +23 -31
  105. package/src/actions/marks/index.js +6 -6
  106. package/src/actions/marks/line/actions.js +33 -52
  107. package/src/actions/marks/line/materialize.js +7 -5
  108. package/src/actions/marks/point/materialize.js +5 -3
  109. package/src/actions/marks/remove.js +21 -0
  110. package/src/actions/marks/rule/actions.js +53 -53
  111. package/src/actions/marks/shared.js +8 -4
  112. package/src/actions/marks/text/actions.js +14 -6
  113. package/src/actions/marks/text/layout.js +21 -45
  114. package/src/actions/marks/tick/actions.js +22 -21
  115. package/src/actions/primitives/createGraphics.js +4 -0
  116. package/src/actions/primitives/editGraphics.js +4 -0
  117. package/src/actions/primitives/index.js +3 -3
  118. package/src/actions/primitives/semantic.js +1 -1
  119. package/src/actions/primitives/semanticValidation/index.js +2 -2
  120. package/src/actions/primitives/semanticValidation/layer.js +1 -1
  121. package/src/actions/regression/edit.js +3 -43
  122. package/src/actions/scales/consumers/families.js +24 -4
  123. package/src/actions/scales/consumers/index.js +1 -1
  124. package/src/actions/scales/consumers/seriesLayout.js +17 -0
  125. package/src/actions/scales/definitions.js +100 -103
  126. package/src/actions/scales/materialize.js +5 -0
  127. package/src/actions/selection/actions.js +79 -121
  128. package/src/actions/titles/actions.js +48 -94
  129. package/src/actions/titles/resolve.js +14 -27
  130. package/src/actions/violinPlots/create.js +37 -44
  131. package/src/core/action.js +20 -5
  132. package/src/core/extensionRegistry.js +104 -0
  133. package/src/core/textMetrics.js +74 -37
  134. package/src/core/validation.js +18 -0
  135. package/src/extension.js +8 -1
  136. package/src/grammar/aggregate.js +48 -28
  137. package/src/grammar/arcs.js +58 -11
  138. package/src/grammar/areaSeries.js +14 -16
  139. package/src/grammar/bin2d.js +37 -23
  140. package/src/grammar/boxPlot.js +14 -13
  141. package/src/grammar/categoricalDensity.js +14 -14
  142. package/src/grammar/categoryOrder.js +8 -14
  143. package/src/grammar/curveCommands.js +87 -20
  144. package/src/grammar/density.js +162 -141
  145. package/src/grammar/facets/index.js +14 -0
  146. package/src/grammar/facets/scales.js +7 -4
  147. package/src/grammar/gradientProfile.js +12 -15
  148. package/src/grammar/histogram.js +145 -83
  149. package/src/grammar/horizon.js +60 -23
  150. package/src/grammar/interval.js +14 -13
  151. package/src/grammar/lineSeries.js +91 -61
  152. package/src/grammar/numeric.js +290 -0
  153. package/src/grammar/paint.js +3 -4
  154. package/src/grammar/palettes.js +21 -32
  155. package/src/grammar/pathCommands.js +6 -0
  156. package/src/grammar/polarPaths.js +5 -5
  157. package/src/grammar/regression/derive.js +48 -9
  158. package/src/grammar/regression/models.js +248 -69
  159. package/src/grammar/regression/parameters.js +8 -0
  160. package/src/grammar/scales/color.js +35 -26
  161. package/src/grammar/scales/continuous.js +71 -24
  162. package/src/grammar/scales/definition.js +1 -1
  163. package/src/grammar/scales/discretized.js +42 -12
  164. package/src/grammar/scales/fields.js +21 -4
  165. package/src/grammar/scales/ordinal.js +117 -61
  166. package/src/grammar/scales/temporal.js +40 -11
  167. package/src/grammar/scales/transformed.js +199 -52
  168. package/src/grammar/schemas/graphicBounds.js +270 -79
  169. package/src/grammar/seriesLayout.js +50 -18
  170. package/src/grammar/ticks.js +123 -53
  171. package/src/grammar/timeUnit.js +18 -8
  172. package/src/grammar/transforms.js +4 -1
  173. package/src/grammar/window.js +70 -57
  174. package/src/index.js +1 -1
  175. package/src/layout/canvas.js +4 -8
  176. package/src/layout/composition.js +39 -40
  177. package/src/layout/facets.js +10 -14
  178. package/src/layout/labels.js +91 -15
  179. package/src/layout/legend.js +22 -20
  180. package/src/layout/legendLane.js +54 -40
  181. package/src/layout/title.js +7 -7
  182. package/src/materialization/bars/aggregate.js +14 -40
  183. package/src/materialization/bars/grouped.js +7 -18
  184. package/src/materialization/bars/histogram.js +2 -19
  185. package/src/materialization/bars/ranged.js +20 -15
  186. package/src/materialization/bars/resolve.js +21 -0
  187. package/src/materialization/facetGuides/legacyCategorical.js +2 -1
  188. package/src/materialization/facetGuides/placement.js +34 -19
  189. package/src/materialization/facetGuides/preparation.js +9 -5
  190. package/src/materialization/facets.js +39 -27
  191. package/src/materialization/guides/resources.js +2 -1
  192. package/src/materialization/marks/capabilities.js +5 -2
  193. package/src/materialization/marks/index.js +24 -4
  194. package/src/materialization/scales/policies/bar.js +61 -59
  195. package/src/materialization/scales/policies/binnedPosition.js +34 -0
  196. package/src/materialization/scales/policies/series.js +13 -7
  197. package/src/materialization/scales/resolve.js +4 -5
  198. package/src/materialization/selection/items/arc.js +16 -2
  199. package/src/materialization/selection/items/bar.js +4 -2
  200. package/src/materialization/selection/items/common.js +21 -1
  201. package/src/materialization/selection/items/path.js +19 -2
  202. package/src/materialization/selection/items/point.js +4 -16
  203. package/src/materialization/selection/items/tick.js +13 -21
  204. package/src/materialization/selection/point.js +11 -4
  205. package/src/materialization/selection/styles.js +19 -27
  206. package/src/materialization/text.js +4 -1
  207. package/src/mcp/adapter.js +8 -3
  208. package/src/renderers/canvas/fill.js +2 -2
  209. package/src/renderers/canvas/index.js +35 -2
  210. package/src/renderers/canvas/native.js +160 -0
  211. package/src/renderers/pdf.js +14 -0
  212. package/src/renderers/svg.js +37 -4
  213. package/types/extension.d.ts +19 -0
  214. package/types/index.d.ts +3 -0
  215. package/types/program.d.ts +509 -131
  216. package/types/svg.d.ts +1 -0
  217. package/src/actions/errorBands/components.js +0 -81
  218. package/src/actions/errorBands/options.js +0 -39
  219. package/src/actions/errorBands/resolve.js +0 -28
  220. package/src/actions/errorBars/components.js +0 -62
  221. package/src/actions/errorBars/options.js +0 -58
  222. package/src/actions/errorBars/resolve.js +0 -25
@@ -1,4 +1,5 @@
1
1
  import { cloneAndFreeze } from "../../core/immutable.js";
2
+ import { utcTimestamp, validTimestamp } from "../timeUnit.js";
2
3
 
3
4
  const TIME_UNITS = [
4
5
  { span: 2 * 365 * 24 * 60 * 60 * 1000, unit: "year" },
@@ -9,22 +10,47 @@ const TIME_UNITS = [
9
10
  { span: 0, unit: "second" }
10
11
  ];
11
12
 
13
+ function requireTimeDomain(domain) {
14
+ if (
15
+ !Array.isArray(domain) ||
16
+ domain.length !== 2 ||
17
+ !domain.every(validTimestamp)
18
+ ) {
19
+ throw new TypeError(
20
+ "Nice time domain must contain two timestamps representing valid dates."
21
+ );
22
+ }
23
+ return domain;
24
+ }
25
+
12
26
  function floorUtc(timestamp, unit) {
13
27
  const date = new Date(timestamp);
14
- if (unit === "year") return Date.UTC(date.getUTCFullYear(), 0, 1);
15
- if (unit === "month") return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
28
+ if (unit === "year") return utcTimestamp(date.getUTCFullYear());
29
+ if (unit === "month") {
30
+ return utcTimestamp(date.getUTCFullYear(), date.getUTCMonth());
31
+ }
16
32
  if (unit === "day") {
17
- return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
33
+ return utcTimestamp(
34
+ date.getUTCFullYear(),
35
+ date.getUTCMonth(),
36
+ date.getUTCDate()
37
+ );
18
38
  }
19
39
  if (unit === "hour") {
20
- return Date.UTC(
21
- date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours()
40
+ return utcTimestamp(
41
+ date.getUTCFullYear(),
42
+ date.getUTCMonth(),
43
+ date.getUTCDate(),
44
+ date.getUTCHours()
22
45
  );
23
46
  }
24
47
  if (unit === "minute") {
25
- return Date.UTC(
26
- date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
27
- date.getUTCHours(), date.getUTCMinutes()
48
+ return utcTimestamp(
49
+ date.getUTCFullYear(),
50
+ date.getUTCMonth(),
51
+ date.getUTCDate(),
52
+ date.getUTCHours(),
53
+ date.getUTCMinutes()
28
54
  );
29
55
  }
30
56
  return Math.floor(timestamp / 1000) * 1000;
@@ -32,9 +58,9 @@ function floorUtc(timestamp, unit) {
32
58
 
33
59
  function offsetUtc(timestamp, unit) {
34
60
  const date = new Date(timestamp);
35
- if (unit === "year") return Date.UTC(date.getUTCFullYear() + 1, 0, 1);
61
+ if (unit === "year") return utcTimestamp(date.getUTCFullYear() + 1);
36
62
  if (unit === "month") {
37
- return Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1);
63
+ return utcTimestamp(date.getUTCFullYear(), date.getUTCMonth() + 1);
38
64
  }
39
65
  if (unit === "day") return timestamp + 24 * 60 * 60 * 1000;
40
66
  if (unit === "hour") return timestamp + 60 * 60 * 1000;
@@ -43,11 +69,14 @@ function offsetUtc(timestamp, unit) {
43
69
  }
44
70
 
45
71
  export function niceTimeDomain(domain) {
46
- const [minimum, maximum] = domain;
72
+ const [minimum, maximum] = requireTimeDomain(domain);
47
73
  if (minimum === maximum) return domain;
48
74
  const unit = TIME_UNITS.find(item => maximum - minimum >= item.span).unit;
49
75
  const start = floorUtc(minimum, unit);
50
76
  const endFloor = floorUtc(maximum, unit);
51
77
  const end = endFloor === maximum ? maximum : offsetUtc(endFloor, unit);
78
+ if (![start, end].every(validTimestamp)) {
79
+ throw new RangeError("Nice time domain exceeds the supported date range.");
80
+ }
52
81
  return cloneAndFreeze([start, end]);
53
82
  }
@@ -1,4 +1,6 @@
1
1
  import { cloneAndFreeze, isPlainObject } from "../../core/immutable.js";
2
+ import { MAX_GENERATED_ITEMS } from "../../core/validation.js";
3
+ import { interpolateNumber, inverseLerp, numericExtent } from "../numeric.js";
2
4
  import { niceTicks } from "../ticks.js";
3
5
  import { niceLinearDomain } from "./continuous.js";
4
6
  import {
@@ -51,22 +53,89 @@ export function normalizeTransformParameters(type, options = {}) {
51
53
  return cloneAndFreeze({});
52
54
  }
53
55
 
54
- function transformValue(value, type, parameters) {
55
- if (type === "linear") return value;
56
- if (type === "log") {
57
- return Math.sign(value) * Math.log10(Math.abs(value)) /
58
- Math.log10(parameters.base);
56
+ function signedTransformProportion(value, start, end, transform) {
57
+ const transformed = [start, end, value].map(item =>
58
+ Math.sign(item) * transform(Math.abs(item))
59
+ );
60
+ return transformed[0] !== transformed[1] &&
61
+ transformed.every(Number.isFinite)
62
+ ? inverseLerp(transformed[2], transformed[0], transformed[1])
63
+ : undefined;
64
+ }
65
+
66
+ function powerProportion(value, domain, exponent) {
67
+ const [start, end] = domain;
68
+ const direct = signedTransformProportion(
69
+ value, start, end, magnitude => magnitude ** exponent
70
+ );
71
+ if (direct !== undefined) return direct;
72
+ const scale = Math.max(Math.abs(start), Math.abs(end));
73
+ const normalized = signedTransformProportion(
74
+ value,
75
+ start,
76
+ end,
77
+ magnitude => magnitude === 0 ? 0 : Math.exp(exponent * (
78
+ Math.log(magnitude) - Math.log(scale)
79
+ ))
80
+ );
81
+ if (normalized !== undefined) return normalized;
82
+
83
+ if (
84
+ start !== 0 &&
85
+ end !== 0 &&
86
+ value !== 0 &&
87
+ Math.sign(start) === Math.sign(end)
88
+ ) {
89
+ const endLogRatio = Math.log(Math.abs(end)) - Math.log(Math.abs(start));
90
+ const valueLogRatio = Math.log(Math.abs(value)) - Math.log(Math.abs(start));
91
+ if (Math.abs(exponent * endLogRatio) < 1e-8) {
92
+ return valueLogRatio / endLogRatio;
93
+ }
94
+ const proportion = Math.expm1(exponent * valueLogRatio) /
95
+ Math.expm1(exponent * endLogRatio);
96
+ if (Number.isFinite(proportion)) return proportion;
59
97
  }
60
- if (type === "sqrt") {
61
- return Math.sign(value) * Math.sqrt(Math.abs(value));
98
+
99
+ return inverseLerp(value, start, end);
100
+ }
101
+
102
+ function logOnePlusRatio(value, constant) {
103
+ if (value === 0) return 0;
104
+ const ratio = Math.abs(value) / constant;
105
+ if (Number.isFinite(ratio)) return Math.sign(value) * Math.log1p(ratio);
106
+ const logRatio = Math.log(Math.abs(value)) - Math.log(constant);
107
+ return Math.sign(value) * (
108
+ Math.max(0, logRatio) + Math.log1p(Math.exp(-Math.abs(logRatio)))
109
+ );
110
+ }
111
+
112
+ function transformedProportion(value, domain, type, parameters) {
113
+ const [start, end] = domain;
114
+ if (type === "linear") return inverseLerp(value, start, end);
115
+ if (type === "log") {
116
+ const logarithm = Math.log10(parameters.base);
117
+ const direct = signedTransformProportion(
118
+ value, start, end, magnitude => Math.log10(magnitude) / logarithm
119
+ );
120
+ return direct ?? signedTransformProportion(
121
+ value, start, end, Math.log
122
+ ) ?? inverseLerp(value, start, end);
62
123
  }
63
- const exponent = type === "sqrt" ? 0.5 : parameters.exponent;
64
124
  if (type === "pow" || type === "sqrt") {
65
- return Math.sign(value) * Math.abs(value) ** exponent;
125
+ return powerProportion(value, domain, parameters.exponent);
66
126
  }
67
- return Math.sign(value) * Math.log1p(
68
- Math.abs(value) / parameters.constant
127
+
128
+ const transformedStart = logOnePlusRatio(start, parameters.constant);
129
+ const transformedEnd = logOnePlusRatio(end, parameters.constant);
130
+ if (transformedStart === transformedEnd) {
131
+ return inverseLerp(value, start, end);
132
+ }
133
+ const proportion = inverseLerp(
134
+ logOnePlusRatio(value, parameters.constant),
135
+ transformedStart,
136
+ transformedEnd
69
137
  );
138
+ return Number.isFinite(proportion) ? proportion : inverseLerp(value, start, end);
70
139
  }
71
140
 
72
141
  export function validateTransformedDomain(type, domain, options = {}) {
@@ -87,25 +156,81 @@ export function validateTransformedDomain(type, domain, options = {}) {
87
156
  return validated;
88
157
  }
89
158
 
90
- function niceLogDomain(domain, base) {
91
- const reversed = domain[0] > domain[1];
159
+ function radixPower(radix, exponent, logarithm) {
160
+ const direct = Number.isFinite(radix) ? radix ** exponent : Number.NaN;
161
+ if (Number.isFinite(direct) && direct > 0) return direct;
162
+ const transformed = exponent * logarithm;
163
+ if (transformed > Math.log(Number.MAX_VALUE)) return Number.POSITIVE_INFINITY;
164
+ if (transformed < Math.log(Number.MIN_VALUE)) return 0;
165
+ return Math.exp(transformed);
166
+ }
167
+
168
+ function logDomainInfo(domain, base) {
92
169
  const low = Math.min(...domain);
93
170
  const high = Math.max(...domain);
94
- let result;
95
- if (low > 0) {
96
- result = [
97
- base ** Math.floor(Math.log(low) / Math.log(base)),
98
- base ** Math.ceil(Math.log(high) / Math.log(base))
99
- ];
100
- } else {
101
- result = [
102
- -(base ** Math.ceil(Math.log(Math.abs(low)) / Math.log(base))),
103
- -(base ** Math.floor(Math.log(Math.abs(high)) / Math.log(base)))
104
- ];
105
- }
171
+ const negative = high < 0;
172
+ return [
173
+ base < 1 ? -Math.log(base) : Math.log(base),
174
+ base < 1 ? 1 / base : base,
175
+ domain[0] > domain[1],
176
+ negative,
177
+ negative ? Math.abs(high) : low,
178
+ negative ? Math.abs(low) : high
179
+ ];
180
+ }
181
+
182
+ function niceLogDomain(domain, base) {
183
+ const [logarithm, radix, reversed, negative, minimum, maximum] =
184
+ logDomainInfo(domain, base);
185
+ let lower = radixPower(
186
+ radix,
187
+ Math.floor(Math.log(minimum) / logarithm),
188
+ logarithm
189
+ );
190
+ let upper = radixPower(
191
+ radix,
192
+ Math.ceil(Math.log(maximum) / logarithm),
193
+ logarithm
194
+ );
195
+ if (!Number.isFinite(lower) || lower <= 0 || lower > minimum) lower = minimum;
196
+ if (!Number.isFinite(upper) || upper < maximum) upper = maximum;
197
+ const result = negative ? [-upper, -lower] : [lower, upper];
106
198
  return cloneAndFreeze(reversed ? result.reverse() : result);
107
199
  }
108
200
 
201
+ function padAutomaticTransformedDomain(type, value, options) {
202
+ const parameters = normalizeTransformParameters(type, options);
203
+ if (value === 0) {
204
+ if (type === "log") {
205
+ throw new RangeError(
206
+ "Log scale domain must be strictly positive or strictly negative."
207
+ );
208
+ }
209
+ return [-1, 1];
210
+ }
211
+ const magnitude = Math.abs(value);
212
+ let lower = magnitude / 2;
213
+ let upper = magnitude * 2;
214
+ if (type === "pow" || type === "sqrt") {
215
+ upper = magnitude * (2 - 2 ** -parameters.exponent) **
216
+ (1 / parameters.exponent);
217
+ } else if (type === "symlog") {
218
+ const center = Math.abs(logOnePlusRatio(value, parameters.constant));
219
+ if (center === 0) return [-1, 1];
220
+ upper = parameters.constant * Math.expm1(
221
+ 2 * center - logOnePlusRatio(lower, parameters.constant)
222
+ );
223
+ }
224
+ if (!(lower < magnitude && upper > magnitude && Number.isFinite(upper))) {
225
+ lower = magnitude / 2;
226
+ upper = Number.isFinite(magnitude * 2) ? magnitude * 2 : magnitude;
227
+ }
228
+ if (type === "log" && lower === 0) lower = magnitude;
229
+ return value > 0
230
+ ? [lower, upper]
231
+ : [-upper, lower === 0 ? 0 : -lower];
232
+ }
233
+
109
234
  export function resolveTransformedDomain({
110
235
  type,
111
236
  domain = "auto",
@@ -129,11 +254,11 @@ export function resolveTransformedDomain({
129
254
  if (values.length === 0) {
130
255
  throw new Error("Cannot infer an automatic scale domain from no values.");
131
256
  }
132
- let resolved = [Math.min(...values), Math.max(...values)];
257
+ let resolved = numericExtent(values);
258
+ if (zero) resolved = [Math.min(0, resolved[0]), Math.max(0, resolved[1])];
133
259
  if (resolved[0] === resolved[1]) {
134
- throw new RangeError("Automatic transformed domain requires distinct values.");
260
+ resolved = padAutomaticTransformedDomain(type, resolved[0], options);
135
261
  }
136
- if (zero) resolved = [Math.min(0, resolved[0]), Math.max(0, resolved[1])];
137
262
  validateTransformedDomain(type, resolved, options);
138
263
  if (nice) {
139
264
  resolved = type === "log"
@@ -166,30 +291,34 @@ export function mapTransformedValues(
166
291
  throw new TypeError("Scale clamp must be a boolean.");
167
292
  }
168
293
  const hasUnknown = Object.hasOwn(options, "unknown");
169
- const transformOptions = Object.fromEntries(
170
- Object.entries(options).filter(([key]) => key !== "unknown")
171
- );
294
+ const { unknown, ...transformOptions } = options;
172
295
  const parameters = normalizeTransformParameters(type, transformOptions);
173
- const domainOptions = type === "sqrt" ? {} : transformOptions;
174
- const validatedDomain = validateTransformedDomain(type, domain, domainOptions);
296
+ const validatedDomain = validateTransformedDomain(type, domain, transformOptions);
175
297
  const resolvedRange = resolveMappingRange(range, { reverse });
176
- const transformedDomain = validatedDomain.map(value =>
177
- transformValue(value, type, parameters)
178
- );
179
- const span = transformedDomain[1] - transformedDomain[0];
180
-
298
+ const span = resolvedRange[1] - resolvedRange[0];
181
299
  return cloneAndFreeze(values.map(value => {
182
300
  const valid = Number.isFinite(value) && !(type === "log" && (
183
301
  value === 0 || Math.sign(value) !== Math.sign(validatedDomain[0])
184
302
  ));
185
303
  if (!valid) {
186
- if (hasUnknown) return options.unknown;
304
+ if (hasUnknown) return unknown;
187
305
  throw new TypeError(`Scale type "${type}" received an invalid value.`);
188
306
  }
189
- const transformed = transformValue(value, type, parameters);
190
- let proportion = (transformed - transformedDomain[0]) / span;
307
+ let proportion = transformedProportion(
308
+ value,
309
+ validatedDomain,
310
+ type,
311
+ parameters
312
+ );
191
313
  if (clamp) proportion = Math.max(0, Math.min(1, proportion));
192
- return resolvedRange[0] + proportion * (resolvedRange[1] - resolvedRange[0]);
314
+ const direct = resolvedRange[0] + proportion * span;
315
+ return Number.isFinite(span) && Number.isFinite(direct) && (
316
+ proportion !== 1 ||
317
+ Math.abs(direct - resolvedRange[1]) <=
318
+ Number.EPSILON * Math.abs(resolvedRange[1])
319
+ )
320
+ ? direct
321
+ : interpolateNumber(resolvedRange[0], resolvedRange[1], proportion);
193
322
  }));
194
323
  }
195
324
 
@@ -200,21 +329,39 @@ export function transformedTicks(type, domain, count, options = {}) {
200
329
  }
201
330
  if (type !== "log") return niceTicks(validated, count);
202
331
  const { base } = normalizeTransformParameters(type, options);
203
- const reversed = validated[0] > validated[1];
204
- const low = Math.min(...validated);
205
- const high = Math.max(...validated);
206
- const negative = high < 0;
207
- const minimumMagnitude = negative ? Math.abs(high) : low;
208
- const maximumMagnitude = negative ? Math.abs(low) : high;
332
+ const [
333
+ logarithm,
334
+ radix,
335
+ reversed,
336
+ negative,
337
+ minimumMagnitude,
338
+ maximumMagnitude
339
+ ] = logDomainInfo(validated, base);
209
340
  const start = Math.ceil(
210
- Math.log(minimumMagnitude) / Math.log(base) - 1e-12
341
+ Math.log(minimumMagnitude) / logarithm - 1e-12
211
342
  );
212
343
  const end = Math.floor(
213
- Math.log(maximumMagnitude) / Math.log(base) + 1e-12
344
+ Math.log(maximumMagnitude) / logarithm + 1e-12
214
345
  );
346
+ const available = end - start + 1;
347
+ const tickCount = available <= MAX_GENERATED_ITEMS
348
+ ? available
349
+ : Math.min(count, MAX_GENERATED_ITEMS);
215
350
  const values = [];
216
- for (let exponent = start; exponent <= end; exponent += 1) {
217
- values.push((negative ? -1 : 1) * base ** exponent);
351
+ for (let index = 0; index < tickCount; index += 1) {
352
+ const exponent = available <= MAX_GENERATED_ITEMS
353
+ ? start + index
354
+ : Math.round(start + (end - start) * index / Math.max(1, tickCount - 1));
355
+ const magnitude = radixPower(radix, exponent, logarithm);
356
+ const value = (negative ? -1 : 1) * magnitude;
357
+ if (
358
+ Number.isFinite(value) &&
359
+ magnitude >= minimumMagnitude &&
360
+ magnitude <= maximumMagnitude &&
361
+ (values.length === 0 || value !== values.at(-1))
362
+ ) {
363
+ values.push(value);
364
+ }
218
365
  }
219
366
  if (negative) values.reverse();
220
367
  if (reversed) values.reverse();