ggaction 0.0.18 → 0.0.20

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 (164) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +6 -2
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/package.json +1 -1
  7. package/src/actions/canvas/fitting.js +6 -2
  8. package/src/actions/charts/heatmap.js +4 -3
  9. package/src/actions/data/derived.js +11 -1
  10. package/src/actions/data/edit.js +2 -0
  11. package/src/actions/data/index.js +4 -0
  12. package/src/actions/data/regression.js +24 -16
  13. package/src/actions/data/regressionStatistics.js +73 -0
  14. package/src/actions/data/shared.js +5 -2
  15. package/src/actions/distributions/revision.js +24 -18
  16. package/src/actions/encodings/color/continuous.js +7 -1
  17. package/src/actions/encodings/remove.js +11 -6
  18. package/src/actions/encodings/shared.js +26 -24
  19. package/src/actions/facets/actions.js +18 -7
  20. package/src/actions/facets/guides.js +2 -1
  21. package/src/actions/guides/axes/axes.js +30 -11
  22. package/src/actions/guides/axes/axis.js +58 -8
  23. package/src/actions/guides/axes/components.js +13 -13
  24. package/src/actions/guides/axes/edit.js +36 -24
  25. package/src/actions/guides/axes/labels.js +37 -27
  26. package/src/actions/guides/axes/lines.js +22 -20
  27. package/src/actions/guides/axes/remove.js +10 -6
  28. package/src/actions/guides/axes/tickGroups.js +8 -7
  29. package/src/actions/guides/axes/ticks.js +31 -22
  30. package/src/actions/guides/axes/titles.js +53 -60
  31. package/src/actions/guides/facadeAxes.js +33 -24
  32. package/src/actions/guides/facadeLegend.js +3 -3
  33. package/src/actions/guides/grids/grid.js +1 -1
  34. package/src/actions/guides/grids/resolve.js +48 -51
  35. package/src/actions/guides/legends/blocks.js +43 -19
  36. package/src/actions/guides/legends/categorical/actions.js +32 -9
  37. package/src/actions/guides/legends/categorical/components.js +3 -10
  38. package/src/actions/guides/legends/categorical/index.js +2 -0
  39. package/src/actions/guides/legends/categorical/layout.js +9 -13
  40. package/src/actions/guides/legends/categorical/options.js +8 -2
  41. package/src/actions/guides/legends/categorical/recipes.js +7 -3
  42. package/src/actions/guides/legends/categorical/resolve.js +9 -2
  43. package/src/actions/guides/legends/categorical/symbols.js +22 -8
  44. package/src/actions/guides/legends/colorSize.js +110 -0
  45. package/src/actions/guides/legends/combine.js +37 -0
  46. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -1
  47. package/src/actions/guides/legends/continuous/common.js +1 -1
  48. package/src/actions/guides/legends/continuous/gradient.js +27 -13
  49. package/src/actions/guides/legends/continuous/opacity.js +16 -11
  50. package/src/actions/guides/legends/creation.js +2 -0
  51. package/src/actions/guides/legends/edit.js +1 -1
  52. package/src/actions/guides/legends/focused.js +6 -5
  53. package/src/actions/guides/legends/lane.js +1 -1
  54. package/src/actions/guides/legends/lifecycle.js +2 -1
  55. package/src/actions/guides/legends/remove.js +6 -3
  56. package/src/actions/guides/legends/size.js +16 -6
  57. package/src/actions/guides/legends/strokeWidth.js +2 -0
  58. package/src/actions/guides/legends/target.js +2 -79
  59. package/src/actions/guides/legends/transition.js +24 -5
  60. package/src/actions/guides/tickValues.js +8 -5
  61. package/src/actions/marks/area/materialize.js +1 -190
  62. package/src/actions/marks/bar/edit.js +23 -12
  63. package/src/actions/marks/bar/materialize.js +6 -1
  64. package/src/actions/marks/point/materialize.js +5 -1
  65. package/src/actions/marks/remove.js +10 -10
  66. package/src/actions/primitives/createGraphics.js +3 -2
  67. package/src/actions/primitives/editGraphics.js +15 -11
  68. package/src/actions/primitives/graphicProperties.js +1 -6
  69. package/src/actions/primitives/semanticAction.js +27 -1
  70. package/src/actions/primitives/semanticValidation/guide.js +11 -1
  71. package/src/actions/primitives/semanticValidation/layer.js +4 -0
  72. package/src/actions/regression/edit.js +15 -3
  73. package/src/actions/scales/channels.js +2 -2
  74. package/src/actions/scales/consumers/common.js +7 -1
  75. package/src/actions/scales/create.js +3 -3
  76. package/src/actions/scales/definitions.js +1 -1
  77. package/src/actions/scales/edit.js +1 -1
  78. package/src/actions/scales/editPolicy.js +3 -0
  79. package/src/actions/scales/patch.js +1 -1
  80. package/src/actions/scales/quantitativeColor.js +8 -3
  81. package/src/actions/theme/reconcile.js +50 -23
  82. package/src/core/compositionState.js +2 -2
  83. package/src/core/graphicProperties.js +1 -0
  84. package/src/core/programState.js +4 -0
  85. package/src/core/resourceReferences.js +2 -2
  86. package/src/core/validation.js +9 -0
  87. package/src/core/vocabulary.js +3 -0
  88. package/src/grammar/aggregate.js +3 -2
  89. package/src/grammar/areaSeries.js +1 -415
  90. package/src/grammar/axisTextPlacement.js +1 -0
  91. package/src/grammar/colorSizeLegend.js +39 -0
  92. package/src/grammar/computed.js +23 -1
  93. package/src/grammar/datasetSchema.js +10 -2
  94. package/src/grammar/displayLabels.js +8 -0
  95. package/src/grammar/facets/guides.js +54 -31
  96. package/src/grammar/facets/legendIntent.js +65 -0
  97. package/src/grammar/guideDash.js +1 -0
  98. package/src/grammar/interval.js +22 -6
  99. package/src/grammar/legendBlocks.js +1 -0
  100. package/src/grammar/legendBorder.js +9 -0
  101. package/src/grammar/legendClip.js +19 -0
  102. package/src/grammar/legendCombinations.js +45 -0
  103. package/src/grammar/legendGradient.js +9 -0
  104. package/src/grammar/legendGroups.js +1 -1
  105. package/src/grammar/legendOpacity.js +6 -0
  106. package/src/grammar/pathSeries.js +2 -2
  107. package/src/grammar/pointShapes.js +16 -2
  108. package/src/grammar/regression/derive.js +9 -1
  109. package/src/grammar/regression/models.js +60 -0
  110. package/src/grammar/regression/parameters.js +9 -1
  111. package/src/grammar/regression/statistics.js +64 -0
  112. package/src/grammar/scales/color.js +1 -1
  113. package/src/grammar/scales/colorConsumers.js +6 -3
  114. package/src/grammar/scales/definition.js +1 -1
  115. package/src/grammar/scales/types.js +1 -1
  116. package/src/grammar/schemas/concreteGraphic.js +3 -0
  117. package/src/grammar/schemas/graphic.js +3 -0
  118. package/src/grammar/schemas/graphicBounds.js +31 -7
  119. package/src/grammar/schemas/semanticPath.js +17 -2
  120. package/src/grammar/statistics/confidenceInterval.js +1 -101
  121. package/src/grammar/summary.js +1 -320
  122. package/src/grammar/timeUnit.js +6 -4
  123. package/src/grammar/transformTopology.js +1 -0
  124. package/src/grammar/transforms.js +11 -1
  125. package/src/inspectionDescriptors.js +1 -1
  126. package/src/layout/guideCollisions.js +9 -1
  127. package/src/layout/legendGroup.js +1 -1
  128. package/src/materialization/compositionSnapshot.js +2 -7
  129. package/src/materialization/coordinateBounds.js +17 -4
  130. package/src/materialization/facetGuides/edit.js +30 -0
  131. package/src/materialization/facetGuides/layout.js +1 -1
  132. package/src/materialization/facetGuides/owner.js +125 -0
  133. package/src/materialization/facetGuides/ownerLayout.js +71 -0
  134. package/src/materialization/facetGuides/placement.js +52 -6
  135. package/src/materialization/facetGuides/preparation.js +7 -0
  136. package/src/materialization/facets.js +1 -1
  137. package/src/materialization/graphicHierarchy.js +5 -1
  138. package/src/materialization/graphicTranslation.js +73 -0
  139. package/src/materialization/guides/layout.js +1 -1
  140. package/src/materialization/guides/resources.js +1 -1
  141. package/src/materialization/legendBorder.js +28 -0
  142. package/src/materialization/legendClip.js +76 -0
  143. package/src/materialization/legendGroups.js +1 -1
  144. package/src/materialization/legends.js +1 -1
  145. package/src/materialization/marks/grouping.js +1 -1
  146. package/src/materialization/scaleGuideDependencies.js +13 -14
  147. package/src/materialization/scales/map.js +3 -0
  148. package/src/materialization/scales/resolve.js +2 -0
  149. package/src/materialization/typography.js +10 -2
  150. package/src/persistence/validation.js +174 -1
  151. package/src/persistence.js +1 -1
  152. package/src/renderers/canvas/index.js +20 -10
  153. package/src/renderers/canvas/isolated.js +166 -0
  154. package/src/renderers/canvas/limits.js +3 -0
  155. package/src/renderers/canvas/rect.js +1 -1
  156. package/src/renderers/pdf.js +2 -2
  157. package/src/renderers/png.js +2 -2
  158. package/src/renderers/svg.js +10 -3
  159. package/src/selectors/axes.js +1 -0
  160. package/src/selectors/graphicCapacity.js +38 -0
  161. package/src/version.js +1 -1
  162. package/types/basic.d.ts +2 -0
  163. package/types/index.d.ts +7 -0
  164. package/types/program.d.ts +220 -64
@@ -0,0 +1,64 @@
1
+ import { isPlainObject } from "../../core/immutable.js";
2
+ import { rejectUnknownProperties } from "../../core/validation.js";
3
+
4
+ export const REGRESSION_STATISTIC_FIELDS = Object.freeze(["count", "intercept", "slope", "rSquared"]);
5
+
6
+ export function validateRegressionStatisticsTransform(transform) {
7
+ if (!isPlainObject(transform)) throw new TypeError("Regression statistics transform must be a plain object.");
8
+ rejectUnknownProperties(transform, ["type"], "regression statistics transform");
9
+ if (transform.type !== "regressionStatistics") throw new Error("Unsupported regression statistics transform.");
10
+ return transform;
11
+ }
12
+
13
+ export function requireRegressionStatisticsSource(source) {
14
+ const transform = source?.transform?.[0];
15
+ if (source?.transform?.length !== 1 || transform.type !== "regression") {
16
+ throw new Error("Regression statistics source must be a regression dataset.");
17
+ }
18
+ if (transform.method !== "linear") throw new Error("Regression statistics only supports linear regression.");
19
+ if (REGRESSION_STATISTIC_FIELDS.includes(transform.groupBy)) {
20
+ throw new Error(`Regression statistics group field "${transform.groupBy}" collides with a statistic output.`);
21
+ }
22
+ return transform;
23
+ }
24
+
25
+ export function validateRegressionFitStatistics(cache, dataset, source) {
26
+ if (!isPlainObject(cache)) throw new TypeError("Regression fit statistics must be a plain object.");
27
+ rejectUnknownProperties(cache, ["version", "method", "groups"], "regression fit statistics");
28
+ const transform = dataset?.transform?.[0];
29
+ if (dataset?.transform?.length !== 1 || transform.type !== "regression" || transform.method !== "linear" ||
30
+ !Array.isArray(dataset.values) || cache.version !== 1 || cache.method !== "linear" || !Array.isArray(cache.groups)) {
31
+ throw new Error("Regression fit statistics requires a materialized linear owner and version 1 groups.");
32
+ }
33
+ const expected = new Map();
34
+ for (const row of source?.values ?? []) {
35
+ if (transform.missing === "drop" && (row[transform.x] == null || row[transform.y] == null)) continue;
36
+ if (!Number.isFinite(row[transform.x]) || !Number.isFinite(row[transform.y])) {
37
+ throw new Error("Regression fit statistics source must contain eligible finite pairs.");
38
+ }
39
+ const key = transform.groupBy === undefined ? undefined : row[transform.groupBy];
40
+ expected.set(key, (expected.get(key) ?? 0) + 1);
41
+ }
42
+ const groups = [...expected.entries()];
43
+ if (groups.length !== cache.groups.length) throw new Error("Regression fit statistics group count does not match its source.");
44
+ cache.groups.forEach((row, index) => {
45
+ if (!isPlainObject(row)) throw new TypeError("Regression fit statistic row must be a plain object.");
46
+ const grouped = transform.groupBy !== undefined;
47
+ rejectUnknownProperties(row, [...REGRESSION_STATISTIC_FIELDS, ...(grouped ? ["group"] : [])], "regression fit statistic row");
48
+ if (grouped && !Object.is(row.group, groups[index][0]) ||
49
+ !Number.isSafeInteger(row.count) || row.count < 2 || row.count !== groups[index][1] ||
50
+ !Number.isFinite(row.intercept) || !Number.isFinite(row.slope) ||
51
+ row.rSquared !== null && !Number.isFinite(row.rSquared)) {
52
+ throw new Error("Regression fit statistics contains invalid coefficients, group identity or observation count.");
53
+ }
54
+ });
55
+ return cache;
56
+ }
57
+
58
+ export function regressionStatisticsRows(cache, dataset) {
59
+ const transform = requireRegressionStatisticsSource(dataset);
60
+ return cache.groups.map(({ group, count, intercept, slope, rSquared }) => ({
61
+ ...(transform.groupBy === undefined ? {} : { [transform.groupBy]: group }),
62
+ count, intercept, slope, rSquared
63
+ }));
64
+ }
@@ -1 +1 @@
1
- import{mapTransformedValues as $}from"./transformed.js";import{cloneAndFreeze as p,isPlainObject as k}from"../../core/immutable.js";import{validateGeneratedItemLimit as F}from"../../core/validation.js";import{normalizePalette as P,resolveContinuousPalette as T}from"../palettes.js";import{inverseLerp as w}from"../numeric.js";const B=p(["rgb","hsl","hsl-long","lab","hcl","hcl-long","cubehelix","cubehelix-long"]),D=Object.freeze({black:"#000000",blue:"#0000ff",cyan:"#00ffff",gray:"#808080",green:"#008000",grey:"#808080",magenta:"#ff00ff",navy:"#000080",orange:"#ffa500",purple:"#800080",red:"#ff0000",teal:"#008080",white:"#ffffff",yellow:"#ffff00"});function M(t,r=0,n=1){return Math.max(r,Math.min(n,t))}function m(t){return(t%360+360)%360}function x({h:t,s:r,l:n}){const e=(1-Math.abs(2*n-1))*r,s=m(t)/60,i=e*(1-Math.abs(s%2-1)),[o,c,l]=s<1?[e,i,0]:s<2?[i,e,0]:s<3?[0,e,i]:s<4?[0,i,e]:s<5?[i,0,e]:[e,0,i],a=n-e/2;return{r:o+a,g:c+a,b:l+a}}function y(t){if(typeof t!="string"||t.length===0)throw new TypeError("Continuous color range requires non-empty color strings.");const r=D[t.toLowerCase()]??t.toLowerCase();let n=r.match(/^#([0-9a-f]{3})$/i);if(n)return Object.fromEntries(["r","g","b"].map((e,s)=>[e,Number.parseInt(n[1][s].repeat(2),16)/255]));if(n=r.match(/^#([0-9a-f]{6})$/i),n)return{r:Number.parseInt(n[1].slice(0,2),16)/255,g:Number.parseInt(n[1].slice(2,4),16)/255,b:Number.parseInt(n[1].slice(4,6),16)/255};if(n=r.match(/^rgb\(\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*\)$/),n){const s=r.includes("%")?100:255,i=n.slice(1).map(o=>Number(o)/s);if(i.every(o=>Number.isFinite(o)&&o>=0&&o<=1))return{r:i[0],g:i[1],b:i[2]}}if(n=r.match(/^hsl\(\s*([-\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*\)$/),n){const e=Number(n[1]),s=Number(n[2])/100,i=Number(n[3])/100;if([e,s,i].every(Number.isFinite)&&s>=0&&s<=1&&i>=0&&i<=1)return x({h:e,s,l:i})}throw new Error(`Unsupported continuous color "${t}".`)}function q({r:t,g:r,b:n}){return`#${[t,r,n].map(e=>Math.round(M(e)*255).toString(16).padStart(2,"0")).join("")}`}function N({r:t,g:r,b:n}){return[t,r,n].map(e=>Math.round(e*255))}function A({r:t,g:r,b:n}){const e=Math.max(t,r,n),s=Math.min(t,r,n),i=e-s,o=(e+s)/2;if(i===0)return{h:0,s:0,l:o};const c=i/(1-Math.abs(2*o-1)),l=e===t?(r-n)/i%6:e===r?(n-t)/i+2:(t-r)/i+4;return{h:m(l*60),s:c,l:o}}function C(t){return t<=.04045?t/12.92:((t+.055)/1.055)**2.4}function E(t){return t<=.0031308?12.92*t:1.055*t**(1/2.4)-.055}function d(t){const r=C(t.r),n=C(t.g),e=C(t.b),s=(.4124564*r+.3575761*n+.1804375*e)/.95047,i=.2126729*r+.7151522*n+.072175*e,o=(.0193339*r+.119192*n+.9503041*e)/1.08883,c=u=>u>216/24389?Math.cbrt(u):841/108*u+4/29,l=c(s),a=c(i),f=c(o);return{l:116*a-16,a:500*(l-a),b:200*(a-f)}}function O({l:t,a:r,b:n}){const e=(t+16)/116,s=e+r/500,i=e-n/200,o=f=>f**3>216/24389?f**3:108/841*(f-4/29),c=.95047*o(s),l=o(e),a=1.08883*o(i);return{r:E(3.2404542*c-1.5371385*l-.4985314*a),g:E(-.969266*c+1.8760108*l+.041556*a),b:E(.0556434*c-.2040259*l+1.0572252*a)}}function I({l:t,a:r,b:n}){return{h:m(Math.atan2(n,r)*180/Math.PI),c:Math.hypot(r,n),l:t}}function U({h:t,c:r,l:n}){const e=t*Math.PI/180;return{l:n,a:Math.cos(e)*r,b:Math.sin(e)*r}}function R({r:t,g:r,b:n}){const l=-1.7884503806,a=1.97294*1.78277,f=1.78277*-.29227- -.90649*-.14861,u=(f*n+l*t-a*r)/(f+l-a),b=n-u,h=(1.97294*(r-u)- -.29227*b)/-.90649,g=1.97294*u*(1-u);return{h:m(Math.atan2(h,b)*180/Math.PI-120),s:g===0?0:Math.hypot(h,b)/g,l:u}}function W({h:t,s:r,l:n}){const e=(t+120)*Math.PI/180,s=r*n*(1-n),i=Math.cos(e),o=Math.sin(e);return{r:n+s*(-.14861*i+1.78277*o),g:n+s*(-.29227*i-.90649*o),b:n+s*(1.97294*i)}}function S(t,r,n,e){let s=r-t;return e||(s=(s+540)%360-180),m(t+s*n)}function H(t,r,n,e){if(e.startsWith("hsl")){const o=A(t),c=A(r);return x({h:S(o.h,c.h,n,e.endsWith("-long")),s:o.s+(c.s-o.s)*n,l:o.l+(c.l-o.l)*n})}if(e==="lab"){const o=d(t),c=d(r);return O({l:o.l+(c.l-o.l)*n,a:o.a+(c.a-o.a)*n,b:o.b+(c.b-o.b)*n})}if(e.startsWith("hcl")){const o=I(d(t)),c=I(d(r));return O(U({h:S(o.h,c.h,n,e.endsWith("-long")),c:o.c+(c.c-o.c)*n,l:o.l+(c.l-o.l)*n}))}const s=R(t),i=R(r);return W({h:S(s.h,i.h,n,e.endsWith("-long")),s:s.s+(i.s-s.s)*n,l:s.l+(i.l-s.l)*n})}function j(t){if(!B.includes(t))throw new Error(`Unsupported continuous color interpolation "${t}".`);return t}function _(t){if(t==="auto")return t;if(Array.isArray(t)){if(F(t.length,"Sequential color range length"),t.length<2)throw new TypeError("Sequential color range requires at least two colors.");return t.forEach(y),p(t)}if(!k(t)||Object.keys(t).length!==1||!Object.hasOwn(t,"palette"))throw new TypeError("Sequential color range requires colors or a palette descriptor.");const r=P(t.palette);if(r.count!==void 0&&r.count<2)throw new RangeError("Sequential palette count must be an integer of at least 2.");return p({palette:r})}function Y(t){const r=_(t);return r==="auto"?T("viridis"):Array.isArray(r)?p(r.map(n=>q(y(n)))):T(r.palette)}function z(t,r,n,e){const i=(e?M(r):r)*(t.length-1),o=Math.max(0,Math.min(t.length-2,Math.floor(i))),c=o+1,l=i-o;if(n==="rgb"){const a=N(t[o]),f=N(t[c]);return`#${a.map((u,b)=>Math.round(M(u+(f[b]-u)*l,0,255)).toString(16).padStart(2,"0")).join("")}`}return q(H(t[o],t[c],l,n))}function Z(t,r,n="rgb"){const e=j(n);if(!Array.isArray(t)||t.length<2)throw new TypeError("Color interpolation requires at least two stops.");const s=t.map(y);return z(s,r,e,!0)}function L(t,r,n="auto"){if(!(t===void 0||t==="auto")){if(r!=="sequential")throw new Error(`Scale type "${r}" does not support midpoint.`);if(!Number.isFinite(t))throw new TypeError("Scale midpoint must be finite or auto.");if(n!=="auto"&&(!Array.isArray(n)||n.length!==2||!n.every(Number.isFinite)||!(t>Math.min(...n)&&t<Math.max(...n))))throw new RangeError("Scale midpoint must lie strictly inside its resolved domain.");return t}}function G(t,r,n,e={}){const{interpolation:s="rgb",clamp:i=!1}=e;if(["log","sqrt","symlog"].includes(e.type)){L(e.midpoint,e.type,r);const h=$(t,r,[0,1],{type:e.type,clamp:i,...e.type==="log"?{base:e.base}:{},...e.type==="symlog"?{constant:e.constant}:{},...Object.hasOwn(e,"unknown")?{unknown:NaN}:{}});return G(h,[0,1],n,{...e,type:"sequential"})}const o=Object.hasOwn(e,"unknown");if(!Array.isArray(r)||r.length!==2||!r.every(Number.isFinite))throw new TypeError("Sequential color domain must contain two finite numbers.");if(!Array.isArray(t)||!o&&!t.every(Number.isFinite))throw new TypeError("Sequential color values must be finite numbers.");if(typeof i!="boolean")throw new TypeError("Sequential color clamp must be a boolean.");const c=L(e.midpoint,"sequential",r),[l,a]=r,f=l===a,u=j(s);if(!Array.isArray(n)||n.length<2)throw new TypeError("Color interpolation requires at least two stops.");const b=n.map(y);return p(t.map(h=>{if(!Number.isFinite(h)){if(o)return e.unknown;throw new TypeError("Sequential color values must be finite numbers.")}const g=c===void 0?f?.5:w(h,l,a):(a>l?h<=c:h>=c)?w(h,l,c)/2:.5+w(h,c,a)/2;return z(b,g,u,i)}))}export{B as CONTINUOUS_COLOR_INTERPOLATIONS,Z as interpolateColorStops,G as mapSequentialColors,Y as resolveSequentialColorStops,j as validateContinuousColorInterpolation,_ as validateSequentialColorRange,L as validateSequentialMidpoint};
1
+ import{mapTransformedValues as F}from"./transformed.js";import{cloneAndFreeze as p,isPlainObject as j}from"../../core/immutable.js";import{validateGeneratedItemLimit as $}from"../../core/validation.js";import{normalizePalette as k,resolveContinuousPalette as S}from"../palettes.js";import{inverseLerp as C}from"../numeric.js";const B=p(["rgb","hsl","hsl-long","lab","hcl","hcl-long","cubehelix","cubehelix-long"]),D=Object.freeze({black:"#000000",blue:"#0000ff",cyan:"#00ffff",gray:"#808080",green:"#008000",grey:"#808080",magenta:"#ff00ff",navy:"#000080",orange:"#ffa500",purple:"#800080",red:"#ff0000",teal:"#008080",white:"#ffffff",yellow:"#ffff00"});function E(t,e=0,n=1){return Math.max(e,Math.min(n,t))}function d(t){return(t%360+360)%360}function q({h:t,s:e,l:n}){const r=(1-Math.abs(2*n-1))*e,o=d(t)/60,i=r*(1-Math.abs(o%2-1)),[a,u,l]=o<1?[r,i,0]:o<2?[i,r,0]:o<3?[0,r,i]:o<4?[0,i,r]:o<5?[i,0,r]:[r,0,i],s=n-r/2;return{r:a+s,g:u+s,b:l+s}}function w(t){if(typeof t!="string"||t.length===0)throw new TypeError("Continuous color range requires non-empty color strings.");const e=D[t.toLowerCase()]??t.toLowerCase();let n=e.match(/^#([0-9a-f]{3})$/i);if(n)return Object.fromEntries(["r","g","b"].map((r,o)=>[r,Number.parseInt(n[1][o].repeat(2),16)/255]));if(n=e.match(/^#([0-9a-f]{6})$/i),n)return{r:Number.parseInt(n[1].slice(0,2),16)/255,g:Number.parseInt(n[1].slice(2,4),16)/255,b:Number.parseInt(n[1].slice(4,6),16)/255};if(n=e.match(/^rgb\(\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*\)$/),n){const o=e.includes("%")?100:255,i=n.slice(1).map(a=>Number(a)/o);if(i.every(a=>Number.isFinite(a)&&a>=0&&a<=1))return{r:i[0],g:i[1],b:i[2]}}if(n=e.match(/^hsl\(\s*([-\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*\)$/),n){const r=Number(n[1]),o=Number(n[2])/100,i=Number(n[3])/100;if([r,o,i].every(Number.isFinite)&&o>=0&&o<=1&&i>=0&&i<=1)return q({h:r,s:o,l:i})}throw new Error(`Unsupported continuous color "${t}".`)}function N({r:t,g:e,b:n}){return`#${[t,e,n].map(r=>Math.round(E(r)*255).toString(16).padStart(2,"0")).join("")}`}function T({r:t,g:e,b:n}){return[t,e,n].map(r=>Math.round(r*255))}function A({r:t,g:e,b:n}){const r=Math.max(t,e,n),o=Math.min(t,e,n),i=r-o,a=(r+o)/2;if(i===0)return{h:0,s:0,l:a};const u=i/(1-Math.abs(2*a-1)),l=r===t?(e-n)/i%6:r===e?(n-t)/i+2:(t-e)/i+4;return{h:d(l*60),s:u,l:a}}function x(t){return t<=.04045?t/12.92:((t+.055)/1.055)**2.4}function m(t){return t<=.0031308?12.92*t:1.055*t**(1/2.4)-.055}function y(t,e="d65"){const n=x(t.r),r=x(t.g),o=x(t.b),i=e==="d50"?(.4360747*n+.3850649*r+.1430804*o)/.96422:(.4124564*n+.3575761*r+.1804375*o)/.95047,a=e==="d50"?.2225045*n+.7168786*r+.0606169*o:.2126729*n+.7151522*r+.072175*o,u=e==="d50"?(.0139322*n+.0971045*r+.7141733*o)/.82521:(.0193339*n+.119192*r+.9503041*o)/1.08883,l=b=>b>216/24389?Math.cbrt(b):841/108*b+4/29,s=l(i),c=l(a),f=l(u);return{l:116*c-16,a:500*(s-c),b:200*(c-f)}}function O({l:t,a:e,b:n},r="d65"){const o=(t+16)/116,i=o+e/500,a=o-n/200,u=f=>f**3>216/24389?f**3:108/841*(f-4/29),l=(r==="d50"?.96422:.95047)*u(i),s=u(o),c=(r==="d50"?.82521:1.08883)*u(a);return r==="d50"?{r:m(3.1338561*l-1.6168667*s-.4906146*c),g:m(-.9787684*l+1.9161415*s+.033454*c),b:m(.0719453*l-.2289914*s+1.4052427*c)}:{r:m(3.2404542*l-1.5371385*s-.4985314*c),g:m(-.969266*l+1.8760108*s+.041556*c),b:m(.0556434*l-.2040259*s+1.0572252*c)}}function I({l:t,a:e,b:n}){return{h:d(Math.atan2(n,e)*180/Math.PI),c:Math.hypot(e,n),l:t}}function H({h:t,c:e,l:n}){const r=t*Math.PI/180;return{l:n,a:Math.cos(r)*e,b:Math.sin(r)*e}}function R({r:t,g:e,b:n}){const l=-1.7884503806,s=1.97294*1.78277,c=1.78277*-.29227- -.90649*-.14861,f=(c*n+l*t-s*e)/(c+l-s),b=n-f,h=(1.97294*(e-f)- -.29227*b)/-.90649,g=1.97294*f*(1-f);return{h:d(Math.atan2(h,b)*180/Math.PI-120),s:g===0?0:Math.hypot(h,b)/g,l:f}}function U({h:t,s:e,l:n}){const r=(t+120)*Math.PI/180,o=e*n*(1-n),i=Math.cos(r),a=Math.sin(r);return{r:n+o*(-.14861*i+1.78277*a),g:n+o*(-.29227*i-.90649*a),b:n+o*(1.97294*i)}}function M(t,e,n,r){let o=e-t;return r||(o=(o+540)%360-180),d(t+o*n)}function W(t,e,n,r,o){const i=o.whitePoint??"d65";if(r.startsWith("hsl")){const s=A(t),c=A(e);return q({h:M(s.h,c.h,n,r.endsWith("-long")),s:s.s+(c.s-s.s)*n,l:s.l+(c.l-s.l)*n})}if(r==="lab"){const s=y(t,i),c=y(e,i);return O({l:s.l+(c.l-s.l)*n,a:s.a+(c.a-s.a)*n,b:s.b+(c.b-s.b)*n},i)}if(r.startsWith("hcl")){const s=I(y(t,i)),c=I(y(e,i));return O(H({h:M(s.h,c.h,n,r.endsWith("-long")),c:s.c+(c.c-s.c)*n,l:s.l+(c.l-s.l)*n}),i)}const a=R(t),u=R(e),l=n**(o.gamma??1);if(!Number.isFinite(l))throw new RangeError("Continuous cubehelix gamma produced a nonfinite interpolation.");return U({h:M(a.h,u.h,n,r.endsWith("-long")),s:a.s+(u.s-a.s)*n,l:a.l+(u.l-a.l)*l})}function P(t){if(!B.includes(t))throw new Error(`Unsupported continuous color interpolation "${t}".`);return t}function _(t,e={}){const n={};if(e.whitePoint!==void 0){if(!["lab","hcl","hcl-long"].includes(t))throw new Error("Scale whitePoint requires Lab or HCL interpolation.");if(!["d65","d50"].includes(e.whitePoint))throw new Error("Scale whitePoint must be d65 or d50.");n.whitePoint=e.whitePoint}if(e.gamma!==void 0){if(!["cubehelix","cubehelix-long"].includes(t))throw new Error("Scale gamma requires cubehelix interpolation.");if(!Number.isFinite(e.gamma)||e.gamma<=0)throw new RangeError("Scale gamma must be a positive finite number.");n.gamma=e.gamma}return n}function G(t){if(t==="auto")return t;if(Array.isArray(t)){if($(t.length,"Sequential color range length"),t.length<2)throw new TypeError("Sequential color range requires at least two colors.");return t.forEach(w),p(t)}if(!j(t)||Object.keys(t).length!==1||!Object.hasOwn(t,"palette"))throw new TypeError("Sequential color range requires colors or a palette descriptor.");const e=k(t.palette);if(e.count!==void 0&&e.count<2)throw new RangeError("Sequential palette count must be an integer of at least 2.");return p({palette:e})}function Z(t){const e=G(t);return e==="auto"?S("viridis"):Array.isArray(e)?p(e.map(n=>N(w(n)))):S(e.palette)}function L(t,e,n,r,o={}){const a=(r?E(e):e)*(t.length-1),u=Math.max(0,Math.min(t.length-2,Math.floor(a))),l=u+1,s=a-u;if(n==="rgb"){const c=T(t[u]),f=T(t[l]);return`#${c.map((b,h)=>Math.round(E(b+(f[h]-b)*s,0,255)).toString(16).padStart(2,"0")).join("")}`}return N(W(t[u],t[l],s,n,o))}function v(t,e,n="rgb"){const r=P(n);if(!Array.isArray(t)||t.length<2)throw new TypeError("Color interpolation requires at least two stops.");const o=t.map(w);return L(o,e,r,!0)}function z(t,e,n="auto"){if(!(t===void 0||t==="auto")){if(e!=="sequential")throw new Error(`Scale type "${e}" does not support midpoint.`);if(!Number.isFinite(t))throw new TypeError("Scale midpoint must be finite or auto.");if(n!=="auto"&&(!Array.isArray(n)||n.length!==2||!n.every(Number.isFinite)||!(t>Math.min(...n)&&t<Math.max(...n))))throw new RangeError("Scale midpoint must lie strictly inside its resolved domain.");return t}}function V(t,e,n,r={}){const{interpolation:o="rgb",clamp:i=!1}=r;if(_(o,r),["log","pow","sqrt","symlog"].includes(r.type)){z(r.midpoint,r.type,e);const h=F(t,e,[0,1],{type:r.type,clamp:i,...r.type==="log"?{base:r.base}:{},...r.type==="pow"?{exponent:r.exponent}:{},...r.type==="symlog"?{constant:r.constant}:{},...Object.hasOwn(r,"unknown")?{unknown:NaN}:{}});return V(h,[0,1],n,{...r,type:"sequential"})}const a=Object.hasOwn(r,"unknown");if(!Array.isArray(e)||e.length!==2||!e.every(Number.isFinite))throw new TypeError("Sequential color domain must contain two finite numbers.");if(!Array.isArray(t)||!a&&!t.every(Number.isFinite))throw new TypeError("Sequential color values must be finite numbers.");if(typeof i!="boolean")throw new TypeError("Sequential color clamp must be a boolean.");const u=z(r.midpoint,"sequential",e),[l,s]=e,c=l===s,f=P(o);if(!Array.isArray(n)||n.length<2)throw new TypeError("Color interpolation requires at least two stops.");const b=n.map(w);return p(t.map(h=>{if(!Number.isFinite(h)){if(a)return r.unknown;throw new TypeError("Sequential color values must be finite numbers.")}const g=u===void 0?c?.5:C(h,l,s):(s>l?h<=u:h>=u)?C(h,l,u)/2:.5+C(h,u,s)/2;return L(b,g,f,i,r)}))}export{B as CONTINUOUS_COLOR_INTERPOLATIONS,v as interpolateColorStops,V as mapSequentialColors,_ as normalizeColorInterpolationOptions,Z as resolveSequentialColorStops,P as validateContinuousColorInterpolation,G as validateSequentialColorRange,z as validateSequentialMidpoint};
@@ -7,7 +7,7 @@ export function validateContinuousColorConsumer(layer, encoding, scale, {
7
7
  channel = "color"
8
8
  } = {}) {
9
9
  const kind = layer.mark?.type;
10
- if (["log", "sqrt", "symlog"].includes(scale.type) && encoding.fieldType !== "quantitative") {
10
+ if (["log", "pow", "sqrt", "symlog"].includes(scale.type) && encoding.fieldType !== "quantitative") {
11
11
  throw new Error(`Scale type "${scale.type}" requires quantitative color.`);
12
12
  }
13
13
  if (channel === "stroke") {
@@ -26,8 +26,8 @@ export function validateContinuousColorConsumer(layer, encoding, scale, {
26
26
  }
27
27
  return undefined;
28
28
  }
29
- if (!["point", "bar", "rect", "text"].includes(kind)) {
30
- throw new Error("Continuous color requires a Point, aggregate Bar, Rect, or Text consumer.");
29
+ if (!["point", "bar", "rect", "text", "area"].includes(kind)) {
30
+ throw new Error("Continuous color requires a Point, aggregate Bar, Rect, Text, or quantitative Area consumer.");
31
31
  }
32
32
  if (!["quantitative", "temporal"].includes(encoding.fieldType) ||
33
33
  (isDiscretizedColorScaleType(scale.type) && encoding.fieldType !== "quantitative")) {
@@ -36,6 +36,9 @@ export function validateContinuousColorConsumer(layer, encoding, scale, {
36
36
  if (Object.hasOwn(scale, "unknown") && kind !== "point") {
37
37
  throw new Error("Continuous color scale unknown currently requires a row-owned point mark.");
38
38
  }
39
+ if (kind === "area" && encoding.fieldType !== "quantitative") {
40
+ throw new Error("Continuous area color requires a quantitative field.");
41
+ }
39
42
  if (kind !== "bar") {
40
43
  if (encoding.aggregate !== undefined) throw new Error(`${kind} continuous color does not support aggregate.`);
41
44
  return undefined;
@@ -1 +1 @@
1
- import{validateMeasuredRadiusScale as I}from"./radial.js";import{validateContinuousColorInterpolation as M,validateSequentialMidpoint as x}from"./color.js";import{isTransformedScaleType as T}from"./mapping.js";import{normalizeTransformParameters as D,validateTransformedDomain as F}from"./transformed.js";import{isContinuousColorScaleType as N,validateScalePropertyForType as O}from"./types.js";const k=Object.freeze(["nice","zero","clamp","reverse"]),z=Object.freeze(["base","exponent","constant"]);function m(i,t,n,e,o){return Object.hasOwn(t,e)?t[e]:!o&&Object.hasOwn(i,e)?i[e]:n[e]}function q(i,t,n,e,o){const l=Object.hasOwn(n,"padding");if(l&&(Object.hasOwn(n,"paddingInner")||Object.hasOwn(n,"paddingOuter")))throw new Error("Scale padding cannot be combined with paddingInner or paddingOuter.");if(l&&(!Number.isFinite(n.padding)||n.padding<0||n.padding>=1))throw new RangeError("Scale padding must be from 0 (inclusive) to 1 (exclusive).");const d=l?{...n,paddingInner:n.padding,paddingOuter:n.padding}:n,g=m(t,d,e,"paddingInner",o)??0,w=m(t,d,e,"paddingOuter",o)??0,c=m(t,d,e,"align",o)??.5;if(!Number.isFinite(g)||g<0||g>=1)throw new RangeError("Scale paddingInner must be from 0 (inclusive) to 1 (exclusive).");if(!Number.isFinite(w)||w<0)throw new RangeError("Scale paddingOuter must be non-negative and finite.");if(!Number.isFinite(c)||c<0||c>1)throw new RangeError("Scale align must be between 0 and 1.");Object.assign(i,{paddingInner:g,paddingOuter:w,align:c})}function A(i,t,n,e,o){const l=m(t,n,e,"padding",o)??.5,d=m(t,n,e,"align",o)??.5;if(!Number.isFinite(l)||l<0)throw new RangeError("Scale padding must be non-negative and finite.");if(!Number.isFinite(d)||d<0||d>1)throw new RangeError("Scale align must be between 0 and 1.");Object.assign(i,{padding:l,align:d})}function V({type:i,color:t=!1,previous:n={},patch:e={},defaults:o={},retainCoreOnTypeChange:l=!1,retainCompatibleOnTypeChange:d=!1,allowOrdinalBandParameters:g=!1,validateDomain:w,validateRange:c}){const s=n.type!==void 0&&n.type!==i,R=Object.hasOwn(e,"domain")?e.domain:(!s||l)&&Object.hasOwn(n,"domain")?n.domain:o.domain??"auto",P=Object.hasOwn(e,"range")?e.range:(!s||l)&&Object.hasOwn(n,"range")?n.range:o.range??"auto",r={type:i,domain:w(i,R),range:c(i,P)},u=m(n,e,o,"emptyDomain",s);if(u!==void 0){if(!["preserve","require-explicit"].includes(u))throw new Error('Scale emptyDomain must be "preserve" or "require-explicit".');r.emptyDomain=u}for(const a of k){let f=m(n,e,o,a,s);if(f===void 0&&s&&d&&Object.hasOwn(n,a))try{O(i,a),f=n[a]}catch{f=void 0}if(f!==void 0){if(typeof f!="boolean")throw new TypeError(`Scale ${a} must be a boolean.`);O(i,a),r[a]=f}}const j=Object.fromEntries(z.flatMap(a=>{const f=m(n,e,o,a,s);return f===void 0?[]:[[a,f]]}));if(T(i)){const a=D(i,j);i==="log"&&(r.base=a.base),i==="pow"&&(r.exponent=a.exponent),i==="symlog"&&(r.constant=a.constant),r.domain!=="auto"&&F(i,r.domain,j)}else for(const a of Object.keys(j))O(i,a);let b=m(n,e,o,"interpolate",s);if(i==="sequential"||t&&N(i))b===void 0&&d&&(b=n.interpolate),r.interpolate=M(b??"rgb");else if(b!==void 0)throw new Error(`Scale type "${i}" does not support interpolate.`);const S=x(m(n,e,o,"midpoint",s),i,r.domain);if(S!==void 0&&(r.midpoint=S),i==="band"||i==="ordinal"&&g)q(r,n,e,o,s);else if(i==="point")A(r,n,e,o,s);else for(const a of["paddingInner","paddingOuter","padding","align"])Object.hasOwn(e,a)&&O(i,a);const E=Object.hasOwn(e,"radialMapping")?e.radialMapping:n.radialMapping;return E!==void 0&&(r.radialMapping=E,I({...r,...Object.hasOwn(e,"unknown")||Object.hasOwn(n,"unknown")?{unknown:e.unknown??n.unknown}:{}},{allowAuto:!0})),r}export{V as normalizeScaleDefinition};
1
+ import{validateMeasuredRadiusScale as D}from"./radial.js";import{normalizeColorInterpolationOptions as F,validateContinuousColorInterpolation as N,validateSequentialMidpoint as T}from"./color.js";import{isTransformedScaleType as k}from"./mapping.js";import{normalizeTransformParameters as q,validateTransformedDomain as A}from"./transformed.js";import{isContinuousColorScaleType as R,validateScalePropertyForType as b}from"./types.js";const p=Object.freeze(["nice","zero","clamp","reverse"]),B=Object.freeze(["base","exponent","constant"]);function f(i,l,n,e,o){return Object.hasOwn(l,e)?l[e]:!o&&Object.hasOwn(i,e)?i[e]:n[e]}function _(i,l,n,e,o){const m=Object.hasOwn(n,"padding");if(m&&(Object.hasOwn(n,"paddingInner")||Object.hasOwn(n,"paddingOuter")))throw new Error("Scale padding cannot be combined with paddingInner or paddingOuter.");if(m&&(!Number.isFinite(n.padding)||n.padding<0||n.padding>=1))throw new RangeError("Scale padding must be from 0 (inclusive) to 1 (exclusive).");const d=m?{...n,paddingInner:n.padding,paddingOuter:n.padding}:n,g=f(l,d,e,"paddingInner",o)??0,w=f(l,d,e,"paddingOuter",o)??0,c=f(l,d,e,"align",o)??.5;if(!Number.isFinite(g)||g<0||g>=1)throw new RangeError("Scale paddingInner must be from 0 (inclusive) to 1 (exclusive).");if(!Number.isFinite(w)||w<0)throw new RangeError("Scale paddingOuter must be non-negative and finite.");if(!Number.isFinite(c)||c<0||c>1)throw new RangeError("Scale align must be between 0 and 1.");Object.assign(i,{paddingInner:g,paddingOuter:w,align:c})}function $(i,l,n,e,o){const m=f(l,n,e,"padding",o)??.5,d=f(l,n,e,"align",o)??.5;if(!Number.isFinite(m)||m<0)throw new RangeError("Scale padding must be non-negative and finite.");if(!Number.isFinite(d)||d<0||d>1)throw new RangeError("Scale align must be between 0 and 1.");Object.assign(i,{padding:m,align:d})}function J({type:i,color:l=!1,previous:n={},patch:e={},defaults:o={},retainCoreOnTypeChange:m=!1,retainCompatibleOnTypeChange:d=!1,allowColorReset:g=!1,allowOrdinalBandParameters:w=!1,validateDomain:c,validateRange:I}){const s=n.type!==void 0&&n.type!==i,M=Object.hasOwn(e,"domain")?e.domain:(!s||m)&&Object.hasOwn(n,"domain")?n.domain:o.domain??"auto",x=Object.hasOwn(e,"range")?e.range:(!s||m)&&Object.hasOwn(n,"range")?n.range:o.range??"auto",r={type:i,domain:c(i,M),range:I(i,x)},u=f(n,e,o,"emptyDomain",s);if(u!==void 0){if(!["preserve","require-explicit"].includes(u))throw new Error('Scale emptyDomain must be "preserve" or "require-explicit".');r.emptyDomain=u}for(const a of p){let t=f(n,e,o,a,s);if(t===void 0&&s&&d&&Object.hasOwn(n,a))try{b(i,a),t=n[a]}catch{t=void 0}if(t!==void 0){if(typeof t!="boolean")throw new TypeError(`Scale ${a} must be a boolean.`);b(i,a),r[a]=t}}const j=Object.fromEntries(B.flatMap(a=>{const t=f(n,e,o,a,s);return t===void 0?[]:[[a,t]]}));if(k(i)){const a=q(i,j);i==="log"&&(r.base=a.base),i==="pow"&&(r.exponent=a.exponent),i==="symlog"&&(r.constant=a.constant),r.domain!=="auto"&&A(i,r.domain,j)}else for(const a of Object.keys(j))b(i,a);let O=f(n,e,o,"interpolate",s);if(i==="sequential"||l&&R(i))O===void 0&&d&&(O=n.interpolate),r.interpolate=N(O??"rgb");else if(O!==void 0)throw new Error(`Scale type "${i}" does not support interpolate.`);const z=Object.hasOwn(e,"interpolate")&&e.interpolate!==n.interpolate,S={};for(const a of["whitePoint","gamma"]){const t=Object.hasOwn(e,a)?e[a]:z?void 0:f(n,e,o,a,s&&!(d&&R(i)));t!==void 0&&!(g&&t==="auto")&&(S[a]=t)}Object.assign(r,F(r.interpolate,S));const E=T(f(n,e,o,"midpoint",s),i,r.domain);if(E!==void 0&&(r.midpoint=E),i==="band"||i==="ordinal"&&w)_(r,n,e,o,s);else if(i==="point")$(r,n,e,o,s);else for(const a of["paddingInner","paddingOuter","padding","align"])Object.hasOwn(e,a)&&b(i,a);const P=Object.hasOwn(e,"radialMapping")?e.radialMapping:n.radialMapping;return P!==void 0&&(r.radialMapping=P,D({...r,...Object.hasOwn(e,"unknown")||Object.hasOwn(n,"unknown")?{unknown:e.unknown??n.unknown}:{}},{allowAuto:!0})),r}export{J as normalizeScaleDefinition};
@@ -16,7 +16,7 @@ export const SCALE_TYPES_BY_ROLE = Object.freeze({
16
16
  [SCALE_ROLES.bandPosition]: Object.freeze(["band"]),
17
17
  [SCALE_ROLES.pointPosition]: Object.freeze(["point"]),
18
18
  [SCALE_ROLES.discreteAppearance]: Object.freeze(["ordinal"]),
19
- [SCALE_ROLES.continuousColor]: Object.freeze(["sequential", "log", "sqrt", "symlog"]),
19
+ [SCALE_ROLES.continuousColor]: Object.freeze(["sequential", "log", "pow", "sqrt", "symlog"]),
20
20
  [SCALE_ROLES.discretizedColor]: Object.freeze([
21
21
  "quantize", "quantile", "threshold"
22
22
  ])
@@ -53,6 +53,9 @@ export function validateConcreteGraphicValue(type, property, value) {
53
53
  if (type === "text" && property === "fontSize" && value === 0) {
54
54
  throw new RangeError("text.fontSize must be positive.");
55
55
  }
56
+ if (property === "blend" && !(value === "normal" || value === "difference" && type !== "circle")) {
57
+ throw new Error(`${type}.blend must be ${type === "circle" ? "normal" : "normal or difference"}.`);
58
+ }
56
59
  if (property === "opacity" && (value < 0 || value > 1)) {
57
60
  throw new RangeError(`${type}.opacity must be between 0 and 1.`);
58
61
  }
@@ -52,6 +52,7 @@ const GRAPHIC_PROPERTIES = Object.freeze({
52
52
  "lineJoin",
53
53
  "miterLimit",
54
54
  "opacity",
55
+ "blend",
55
56
  "length"
56
57
  ]),
57
58
  rect: new Set([
@@ -66,6 +67,7 @@ const GRAPHIC_PROPERTIES = Object.freeze({
66
67
  "lineJoin",
67
68
  "miterLimit",
68
69
  "opacity",
70
+ "blend",
69
71
  "length"
70
72
  ]),
71
73
  line: new Set([
@@ -108,6 +110,7 @@ const GRAPHIC_PROPERTIES = Object.freeze({
108
110
  "lineJoin",
109
111
  "miterLimit",
110
112
  "opacity",
113
+ "blend",
111
114
  "length"
112
115
  ])
113
116
  });
@@ -376,7 +376,7 @@ function hasActiveStroke(properties) {
376
376
  (properties.strokeWidth ?? 0) > 0;
377
377
  }
378
378
 
379
- function primitiveBounds(type, properties = {}, profile) {
379
+ export function resolvePrimitiveGraphicBounds(type, properties = {}, profile) {
380
380
  const strokeExtent = hasActiveStroke(properties)
381
381
  ? properties.strokeWidth / 2
382
382
  : 0;
@@ -469,15 +469,34 @@ function ancestorOffset(graphicSpec, target) {
469
469
  }
470
470
  }
471
471
 
472
+ function intersectVisibleBounds(bounds,clip) {
473
+ if(bounds===undefined)return undefined;
474
+ const result={left:Math.max(bounds.left,clip.left),right:Math.min(bounds.right,clip.right),
475
+ top:Math.max(bounds.top,clip.top),bottom:Math.min(bounds.bottom,clip.bottom)};
476
+ return result.right<=result.left||result.bottom<=result.top?undefined:result;
477
+ }
478
+
479
+ function clipAncestorBounds(graphicSpec,id,bounds) {
480
+ let parent=findGraphicParent(graphicSpec,id);
481
+ while(parent!==undefined){
482
+ if(parent.object.type==="canvas"&&findGraphicParent(graphicSpec,parent.id)!==undefined){
483
+ const [x,y]=ancestorOffset(graphicSpec,parent.id),p=parent.object.properties;
484
+ bounds=intersectVisibleBounds(bounds,{left:x+(p.x??0),right:x+(p.x??0)+p.width,top:y+(p.y??0),bottom:y+(p.y??0)+p.height});
485
+ }
486
+ parent=findGraphicParent(graphicSpec,parent.id);
487
+ }
488
+ return bounds;
489
+ }
490
+
472
491
  function objectBounds(graphicSpec, id, object, ancestors, x, y, profile) {
473
492
  if (ancestors.has(id)) {
474
493
  throw new Error(`Graphic attachment cycle includes "${id}".`);
475
494
  }
476
495
  ancestors.add(id);
477
496
  let result = object.items === undefined
478
- ? translateBounds(primitiveBounds(object.type, object.properties, profile), x, y)
497
+ ? translateBounds(resolvePrimitiveGraphicBounds(object.type, object.properties, profile), x, y)
479
498
  : unionBounds(object.items.map(item => translateBounds(
480
- primitiveBounds(item.type ?? object.type, item.properties, profile),
499
+ resolvePrimitiveGraphicBounds(item.type ?? object.type, item.properties, profile),
481
500
  x,
482
501
  y
483
502
  )));
@@ -497,20 +516,25 @@ function objectBounds(graphicSpec, id, object, ancestors, x, y, profile) {
497
516
  );
498
517
  }
499
518
  }
519
+ if(object.type==="canvas"&&findGraphicParent(graphicSpec,id)!==undefined){
520
+ result=intersectVisibleBounds(result,{left:childX,right:childX+object.properties.width,
521
+ top:childY,bottom:childY+object.properties.height});
522
+ }
500
523
  ancestors.delete(id);
501
524
  return result;
502
525
  }
503
526
 
504
527
  export function resolveConcreteGraphicBounds(graphicSpec, target, profile) {
505
528
  const found = requireGraphic(graphicSpec, target);
506
- const [x, y] = ancestorOffset(graphicSpec, target);
507
- return requireFiniteBounds(found.kind === "item"
529
+ const anchor=found.kind==="item"?found.ownerId:found.id;
530
+ const [x, y] = ancestorOffset(graphicSpec, anchor);
531
+ return requireFiniteBounds(clipAncestorBounds(graphicSpec,anchor,found.kind === "item"
508
532
  ? translateBounds(
509
- primitiveBounds(found.object.type ?? found.owner.type, found.object.properties, profile),
533
+ resolvePrimitiveGraphicBounds(found.object.type ?? found.owner.type, found.object.properties, profile),
510
534
  x,
511
535
  y
512
536
  )
513
- : objectBounds(graphicSpec, found.id, found.object, new Set(), x, y, profile));
537
+ : objectBounds(graphicSpec, found.id, found.object, new Set(), x, y, profile)));
514
538
  }
515
539
 
516
540
  export function unionConcreteGraphicBounds(graphicSpec, targets, profile) {
@@ -1,5 +1,6 @@
1
1
  import { USER_ID_SOURCE } from "../../core/identifiers.js";
2
2
  import {
3
+ AXIS_RESOURCE_CHANNELS,
3
4
  CARTESIAN_POSITION_CHANNELS,
4
5
  ENCODING_CHANNELS,
5
6
  SCALED_ENCODING_CHANNELS
@@ -65,6 +66,7 @@ const ENTITY_PATHS = Object.freeze({
65
66
  "mark.type",
66
67
  "mark.missing",
67
68
  "mark.orientation",
69
+ "mark.blend",
68
70
  "derivedBindings.regression",
69
71
  "layout.mode",
70
72
  ...ENCODING_PATHS
@@ -74,7 +76,7 @@ const ENTITY_PATHS = Object.freeze({
74
76
  collection: "scales",
75
77
  properties: new Set([
76
78
  "type", "domain", "range", "emptyDomain", "nice", "zero", "clamp", "reverse",
77
- "unknown", "base", "exponent", "constant", "interpolate", "radialMapping", "midpoint",
79
+ "unknown", "base", "exponent", "constant", "interpolate", "whitePoint", "gamma", "radialMapping", "midpoint",
78
80
  "paddingInner", "paddingOuter", "padding", "align"
79
81
  ])
80
82
  },
@@ -101,6 +103,9 @@ const GUIDE_PATHS = new Set([
101
103
  "axis.parallel.coordinate",
102
104
  "axis.parallel.scales",
103
105
  "axis.parallel.titles",
106
+ "legend.colorSize.colorScale",
107
+ "legend.colorSize.sizeScale",
108
+ "legend.colorSize.title",
104
109
  "legend.color.scale",
105
110
  "legend.color.title",
106
111
  "legend.color.order",
@@ -137,6 +142,7 @@ const GUIDE_REMOVABLE_CONTAINERS = new Set([
137
142
  "grid.vertical",
138
143
  "grid.theta",
139
144
  "grid.radial",
145
+ "legend.colorSize",
140
146
  "legend.color",
141
147
  "legend.stroke",
142
148
  "legend.size",
@@ -147,6 +153,13 @@ const GUIDE_REMOVABLE_CONTAINERS = new Set([
147
153
 
148
154
  const TITLE_PATHS = new Set(["text", "subtitle"]);
149
155
 
156
+ function isNamedAxisPath(path, container = false) {
157
+ const match = path.match(new RegExp(container
158
+ ? `^axis\\.(${USER_ID_SOURCE})$`
159
+ : `^axis\\.(${USER_ID_SOURCE})\\.(scale|coordinate|title|channel|position)$`));
160
+ return match !== null && !AXIS_RESOURCE_CHANNELS.includes(match[1]);
161
+ }
162
+
150
163
  function splitPropertyPath(property) {
151
164
  return property.split(".");
152
165
  }
@@ -201,7 +214,9 @@ export function parseSemanticPath(property, { allowContainer = false } = {}) {
201
214
 
202
215
  if (
203
216
  !GUIDE_PATHS.has(propertyPath) &&
204
- !(allowContainer && GUIDE_REMOVABLE_CONTAINERS.has(propertyPath))
217
+ !isNamedAxisPath(propertyPath) &&
218
+ !(allowContainer && (GUIDE_REMOVABLE_CONTAINERS.has(propertyPath) ||
219
+ isNamedAxisPath(propertyPath, true)))
205
220
  ) {
206
221
  throw new Error(`Unknown semantic property "${property}".`);
207
222
  }
@@ -1,101 +1 @@
1
- import { cloneAndFreeze } from "../../core/immutable.js";
2
- import { studentTCriticalValue } from "./studentT.js";
3
-
4
- export const CONFIDENCE_INTERVAL_METHODS = Object.freeze([
5
- "normal",
6
- "student-t"
7
- ]);
8
-
9
- function validateMethod(method, label) {
10
- if (!CONFIDENCE_INTERVAL_METHODS.includes(method)) {
11
- throw new Error(`Unsupported ${label} method "${method}".`);
12
- }
13
- }
14
-
15
- function validateLevel(level, label) {
16
- if (!Number.isFinite(level) || level <= 0 || level >= 1) {
17
- throw new RangeError(`${label} level must be between 0 and 1.`);
18
- }
19
- }
20
-
21
- // Peter J. Acklam's inverse-normal approximation. The exact 95% compatibility
22
- // value is handled by normalCriticalValue before this approximation is used.
23
- function inverseNormal(probability) {
24
- const a = [
25
- -3.969683028665376e1, 2.209460984245205e2,
26
- -2.759285104469687e2, 1.38357751867269e2,
27
- -3.066479806614716e1, 2.506628277459239
28
- ];
29
- const b = [
30
- -5.447609879822406e1, 1.615858368580409e2,
31
- -1.556989798598866e2, 6.680131188771972e1,
32
- -1.328068155288572e1
33
- ];
34
- const c = [
35
- -7.784894002430293e-3, -3.223964580411365e-1,
36
- -2.400758277161838, -2.549732539343734,
37
- 4.374664141464968, 2.938163982698783
38
- ];
39
- const d = [
40
- 7.784695709041462e-3, 3.224671290700398e-1,
41
- 2.445134137142996, 3.754408661907416
42
- ];
43
- const low = 0.02425;
44
- const high = 1 - low;
45
- if (probability < low) {
46
- const q = Math.sqrt(-2 * Math.log(probability));
47
- return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
48
- ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
49
- }
50
- if (probability > high) {
51
- const q = Math.sqrt(-2 * Math.log(1 - probability));
52
- return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
53
- ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
54
- }
55
- const q = probability - 0.5;
56
- const r = q * q;
57
- return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q /
58
- (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
59
- }
60
-
61
- export function normalizeConfidenceInterval({
62
- method,
63
- level
64
- } = {}, {
65
- defaultMethod,
66
- defaultLevel = 0.95,
67
- label = "Confidence interval"
68
- } = {}) {
69
- validateMethod(defaultMethod, label);
70
- const resolvedMethod = method ?? defaultMethod;
71
- const resolvedLevel = level ?? defaultLevel;
72
- validateMethod(resolvedMethod, label);
73
- validateLevel(resolvedLevel, label);
74
- return cloneAndFreeze({ method: resolvedMethod, level: resolvedLevel });
75
- }
76
-
77
- export function normalCriticalValue(level) {
78
- validateLevel(level, "Normal confidence interval");
79
- if (level === 0.95) return 1.96;
80
- return inverseNormal((1 + level) / 2);
81
- }
82
-
83
- export function confidenceCriticalValue({
84
- method,
85
- level,
86
- degreesOfFreedom
87
- }) {
88
- const normalized = normalizeConfidenceInterval(
89
- { method, level },
90
- { defaultMethod: method, label: "Confidence interval" }
91
- );
92
- if (normalized.method === "normal") {
93
- return normalCriticalValue(normalized.level);
94
- }
95
- if (!Number.isInteger(degreesOfFreedom) || degreesOfFreedom < 1) {
96
- throw new RangeError(
97
- "Student-t confidence interval degreesOfFreedom must be a positive integer."
98
- );
99
- }
100
- return studentTCriticalValue(normalized.level, degreesOfFreedom);
101
- }
1
+ import{quantileSorted as d,requireFiniteResult as c}from"../numeric.js";import{cloneAndFreeze as m,isPlainObject as w}from"../../core/immutable.js";import{studentTCriticalValue as g}from"./studentT.js";const h=Object.freeze(["normal","student-t"]);function l(e,n,r=!1){if(r&&w(e)){const t=Object.keys(e).find(a=>!["type","seed","samples"].includes(a));if(t!==void 0)throw new Error(`Unknown bootstrap method property "${t}".`);if(e.type!=="bootstrap")throw new Error(`Unsupported ${n} method type.`);if(!Number.isInteger(e.seed)||e.seed<1||e.seed>2147483646)throw new RangeError("Bootstrap seed must be an integer between 1 and 2147483646.");const o=e.samples===void 0?1e3:e.samples;if(!Number.isInteger(o)||o<1||o>1e4)throw new RangeError("Bootstrap samples must be an integer between 1 and 10000.");return{type:"bootstrap",seed:e.seed,samples:o}}if(!h.includes(e))throw new Error(`Unsupported ${n} method "${e}".`);return e}function f(e,n){if(!Number.isFinite(e)||e<=0||e>=1)throw new RangeError(`${n} level must be between 0 and 1.`)}function E(e){const n=[-39.69683028665376,220.9460984245205,-275.9285104469687,138.357751867269,-30.66479806614716,2.506628277459239],r=[-54.47609879822406,161.5858368580409,-155.6989798598866,66.80131188771972,-13.28068155288572],t=[-.007784894002430293,-.3223964580411365,-2.400758277161838,-2.549732539343734,4.374664141464968,2.938163982698783],o=[.007784695709041462,.3224671290700398,2.445134137142996,3.754408661907416],a=.02425,u=1-a;if(e<a){const s=Math.sqrt(-2*Math.log(e));return(((((t[0]*s+t[1])*s+t[2])*s+t[3])*s+t[4])*s+t[5])/((((o[0]*s+o[1])*s+o[2])*s+o[3])*s+1)}if(e>u){const s=Math.sqrt(-2*Math.log(1-e));return-(((((t[0]*s+t[1])*s+t[2])*s+t[3])*s+t[4])*s+t[5])/((((o[0]*s+o[1])*s+o[2])*s+o[3])*s+1)}const p=e-.5,i=p*p;return(((((n[0]*i+n[1])*i+n[2])*i+n[3])*i+n[4])*i+n[5])*p/(((((r[0]*i+r[1])*i+r[2])*i+r[3])*i+r[4])*i+1)}function b({method:e,level:n}={},{defaultMethod:r,defaultLevel:t=.95,label:o="Confidence interval",allowBootstrap:a=!1}={}){l(r,o);const u=l(e??r,o,a),p=n??t;return f(p,o),m({method:u,level:p})}function B(e){return f(e,"Normal confidence interval"),e===.95?1.96:E((1+e)/2)}function q({method:e,level:n,degreesOfFreedom:r}){const t=b({method:e,level:n},{defaultMethod:e,label:"Confidence interval"});if(t.method==="normal")return B(t.level);if(!Number.isInteger(r)||r<1)throw new RangeError("Student-t confidence interval degreesOfFreedom must be a positive integer.");return g(t.level,r)}function x(e){if(!Number.isSafeInteger(e)||e<0||e>5e7)throw new RangeError("Bootstrap resampling cannot exceed 50000000 draws per action.")}function M(e){const n=l(e,"Bootstrap mean interval",!0);if(n?.type!=="bootstrap")throw new Error("Bootstrap sampler requires a bootstrap method.");let r=n.seed;return t=>{if(t.length===0)return[];const o=[];for(let a=0;a<n.samples;a+=1){let u=0;for(let p=0;p<t.length;p+=1)r=(1103515245*r+12345)%2147483647,u+=t[Math.floor(r/2147483647*t.length)];o.push(c(u/t.length,"Bootstrap sample mean"))}return o.sort((a,u)=>a-u)}}function I(e,n=.95){if(f(n,"Bootstrap mean interval"),e.length===0)return;const r=(1-n)/2;return{lower:c(d(e,r),"Bootstrap lower endpoint"),upper:c(d(e,1-r),"Bootstrap upper endpoint")}}export{h as CONFIDENCE_INTERVAL_METHODS,I as bootstrapMeanBounds,q as confidenceCriticalValue,M as createBootstrapMeanSampler,B as normalCriticalValue,b as normalizeConfidenceInterval,x as validateBootstrapDrawCount};