11import { ascending , InternSet } from "d3" ;
2- import { isOrdinal , labelof , valueof , isOptions , isColor , isObject } from "../options.js" ;
2+ import { marks } from "../mark.js" ;
3+ import { isColor , isObject , isOptions , isOrdinal , labelof , valueof } from "../options.js" ;
4+ import { bin , binX , binY } from "../transforms/bin.js" ;
5+ import { group , groupX , groupY } from "../transforms/group.js" ;
36import { areaX , areaY } from "./area.js" ;
4- import { dot } from "./dot.js" ;
5- import { line , lineX , lineY } from "./line.js" ;
6- import { ruleX , ruleY } from "./rule.js" ;
77import { barX , barY } from "./bar.js" ;
8- import { rect , rectX , rectY } from "./rect.js" ;
98import { cell } from "./cell.js" ;
9+ import { dot } from "./dot.js" ;
1010import { frame } from "./frame.js" ;
11- import { bin , binX , binY } from "../transforms/bin .js" ;
12- import { group , groupX , groupY } from "../transforms/group .js" ;
13- import { marks } from "../mark .js" ;
11+ import { line , lineX , lineY } from "./line .js" ;
12+ import { rectX , rectY } from "./rect .js" ;
13+ import { ruleX , ruleY } from "./rule .js" ;
1414
1515export function autoSpec ( data , options ) {
16+ const { x, y, fx, fy, color, size, mark} = autoImpl ( data , options ) ;
17+ return { x, y, fx, fy, color, size, mark} ;
18+ }
19+
20+ function autoImpl ( data , options ) {
1621 options = normalizeOptions ( options ) ;
1722
23+ // Greedily materialize columns for type inference; we’ll need them anyway to
24+ // plot! Note that we don’t apply any type inference to the fx and fy
25+ // channels, if present; these are always ordinal (at least for now).
26+ const { x, y, color, size} = options ;
27+ const X = materializeValue ( data , x ) ;
28+ const Y = materializeValue ( data , y ) ;
29+ const C = materializeValue ( data , color ) ;
30+ const S = materializeValue ( data , size ) ;
31+
32+ // Compute the default options.
1833 let {
1934 fx,
2035 fy,
@@ -25,10 +40,6 @@ export function autoSpec(data, options) {
2540 mark
2641 } = options ;
2742
28- // Lazily materialize x and y columns for type inference, if needed.
29- const { x, y} = options ;
30- let X , Y ;
31-
3243 // Determine the default reducer, if any.
3344 if ( xReduce === undefined )
3445 xReduce = yReduce == null && xValue == null && sizeValue == null && yValue != null ? "count" : null ;
@@ -42,8 +53,8 @@ export function autoSpec(data, options) {
4253 colorReduce == null &&
4354 xReduce == null &&
4455 yReduce == null &&
45- ( xValue == null || isOrdinal ( ( X ??= materializeValue ( data , x ) ) ) ) &&
46- ( yValue == null || isOrdinal ( ( Y ??= materializeValue ( data , y ) ) ) )
56+ ( xValue == null || isOrdinal ( X ) ) &&
57+ ( yValue == null || isOrdinal ( Y ) )
4758 ) {
4859 sizeReduce = "count" ;
4960 }
@@ -62,121 +73,61 @@ export function autoSpec(data, options) {
6273 mark =
6374 sizeValue != null || sizeReduce != null
6475 ? "dot"
65- : xZero || yZero || colorReduce != null // histogram or heatmap
76+ : isZeroReducer ( xReduce ) || isZeroReducer ( yReduce ) || colorReduce != null // histogram or heatmap
6677 ? "bar"
6778 : xValue != null && yValue != null
68- ? isOrdinal ( ( X ??= materializeValue ( data , x ) ) ) ||
69- isOrdinal ( ( Y ??= materializeValue ( data , y ) ) ) ||
70- ( xReduce == null && yReduce == null && ! isMonotonic ( X ) && ! isMonotonic ( Y ) )
79+ ? isOrdinal ( X ) || isOrdinal ( Y ) || ( xReduce == null && yReduce == null && ! isMonotonic ( X ) && ! isMonotonic ( Y ) )
7180 ? "dot"
7281 : "line"
7382 : xValue != null || yValue != null
7483 ? "rule"
7584 : null ;
7685 }
7786
78- return {
79- fx : fx ?? null ,
80- fy : fy ?? null ,
81- x : {
82- value : xValue ?? null ,
83- reduce : xReduce ?? null ,
84- ...( xZero !== undefined && { zero : xZero } ) , // TODO realize default
85- ...xOptions
86- } ,
87- y : {
88- value : yValue ?? null ,
89- reduce : yReduce ?? null ,
90- ...( yZero !== undefined && { zero : yZero } ) , // TODO realize default
91- ...yOptions
92- } ,
93- color : {
94- value : colorValue ?? null ,
95- reduce : colorReduce ?? null ,
96- ...( colorColor !== undefined && { color : colorColor } )
97- } ,
98- size : {
99- value : sizeValue ?? null ,
100- reduce : sizeReduce ?? null
101- } ,
102- mark
103- } ;
104- }
105-
106- export function auto ( data , options ) {
107- options = normalizeOptions ( options ) ;
108-
109- // Greedily materialize columns for type inference; we’ll need them anyway to
110- // plot! Note that we don’t apply any type inference to the fx and fy
111- // channels, if present; these are always ordinal (at least for now).
112- const { x, y, color, size} = options ;
113- const X = materializeValue ( data , x ) ;
114- const Y = materializeValue ( data , y ) ;
115- const C = materializeValue ( data , color ) ;
116- const S = materializeValue ( data , size ) ;
117-
118- // Compute the default options via autoSpec.
119- let {
120- fx,
121- fy,
122- x : { reduce : xReduce , zero : xZero , ...xOptions } ,
123- y : { reduce : yReduce , zero : yZero , ...yOptions } ,
124- color : { color : colorColor , reduce : colorReduce } ,
125- size : { reduce : sizeReduce } ,
126- mark
127- } = autoSpec ( data , {
128- ...options ,
129- x : { ...x , value : X } ,
130- y : { ...y , value : Y } ,
131- color : { ...color , value : C } ,
132- size : { ...size , value : S }
133- } ) ;
134-
13587 let Z ; // may be set to null to disable series-by-color for line and area
13688 let colorMode ; // "fill" or "stroke"
13789
13890 // Determine the mark implementation.
139- if ( mark != null ) {
140- switch ( `${ mark } ` . toLowerCase ( ) ) {
141- case "dot" :
142- mark = dot ;
143- colorMode = "stroke" ;
144- break ;
145- case "line" :
146- mark = X && Y ? line : X ? lineX : lineY ; // 1d line by index
147- colorMode = "stroke" ;
148- if ( isHighCardinality ( C ) ) Z = null ; // TODO only if z not set by user
149- break ;
150- case "area" :
151- mark = yZero ? areaY : xZero || ( Y && isMonotonic ( Y ) ) ? areaX : areaY ; // favor areaY if unsure
152- colorMode = "fill" ;
153- if ( isHighCardinality ( C ) ) Z = null ; // TODO only if z not set by user
154- break ;
155- case "rule" :
156- mark = X ? ruleX : ruleY ;
157- colorMode = "stroke" ;
158- break ;
159- case "bar" :
160- mark = yZero
161- ? isOrdinalReduced ( xReduce , X )
162- ? barY
163- : rectY
164- : xZero
165- ? isOrdinalReduced ( yReduce , Y )
166- ? barX
167- : rectX
168- : isOrdinalReduced ( xReduce , X ) && isOrdinalReduced ( yReduce , Y )
169- ? cell
170- : isOrdinalReduced ( xReduce , X )
91+ let markImpl ;
92+ switch ( mark ) {
93+ case "dot" :
94+ markImpl = dot ;
95+ colorMode = "stroke" ;
96+ break ;
97+ case "line" :
98+ markImpl = X && Y ? line : X ? lineX : lineY ; // 1d line by index
99+ colorMode = "stroke" ;
100+ if ( isHighCardinality ( C ) ) Z = null ; // TODO only if z not set by user
101+ break ;
102+ case "area" :
103+ markImpl = yZero ? areaY : xZero || ( Y && isMonotonic ( Y ) ) ? areaX : areaY ; // favor areaY if unsure
104+ colorMode = "fill" ;
105+ if ( isHighCardinality ( C ) ) Z = null ; // TODO only if z not set by user
106+ break ;
107+ case "rule" :
108+ markImpl = X ? ruleX : ruleY ;
109+ colorMode = "stroke" ;
110+ break ;
111+ case "bar" :
112+ markImpl = yZero
113+ ? isOrdinalReduced ( xReduce , X )
171114 ? barY
172- : isOrdinalReduced ( yReduce , Y )
115+ : rectY
116+ : xZero
117+ ? isOrdinalReduced ( yReduce , Y )
173118 ? barX
174- : rect ;
175- colorMode = "fill" ;
176- break ;
177- default :
178- throw new Error ( `invalid mark: ${ mark } ` ) ;
179- }
119+ : rectX
120+ : isOrdinalReduced ( xReduce , X ) && isOrdinalReduced ( yReduce , Y )
121+ ? cell
122+ : isOrdinalReduced ( xReduce , X )
123+ ? barY
124+ : isOrdinalReduced ( yReduce , Y )
125+ ? barX
126+ : rectY ;
127+ colorMode = "fill" ;
128+ break ;
129+ default :
130+ throw new Error ( `invalid mark: ${ mark } ` ) ;
180131 }
181132
182133 // Determine the mark options.
@@ -189,44 +140,95 @@ export function auto(data, options) {
189140 z : Z ,
190141 r : S ?? undefined // treat null size as undefined for default constant radius
191142 } ;
192- let transform ;
143+ let transformImpl ;
193144 let transformOptions = { [ colorMode ] : colorReduce ?? undefined , r : sizeReduce ?? undefined } ;
194145 if ( xReduce != null && yReduce != null ) {
195146 throw new Error ( `cannot reduce both x and y` ) ; // for now at least
196147 } else if ( yReduce != null ) {
197148 transformOptions . y = yReduce ;
198- transform = isOrdinal ( X ) ? groupX : binX ;
149+ transformImpl = isOrdinal ( X ) ? groupX : binX ;
199150 } else if ( xReduce != null ) {
200151 transformOptions . x = xReduce ;
201- transform = isOrdinal ( Y ) ? groupY : binY ;
152+ transformImpl = isOrdinal ( Y ) ? groupY : binY ;
202153 } else if ( colorReduce != null || sizeReduce != null ) {
203154 if ( X && Y ) {
204- transform = isOrdinal ( X ) && isOrdinal ( Y ) ? group : isOrdinal ( X ) ? binY : isOrdinal ( Y ) ? binX : bin ;
155+ transformImpl = isOrdinal ( X ) && isOrdinal ( Y ) ? group : isOrdinal ( X ) ? binY : isOrdinal ( Y ) ? binX : bin ;
205156 } else if ( X ) {
206- transform = isOrdinal ( X ) ? groupX : binX ;
157+ transformImpl = isOrdinal ( X ) ? groupX : binX ;
207158 } else if ( Y ) {
208- transform = isOrdinal ( Y ) ? groupY : binY ;
159+ transformImpl = isOrdinal ( Y ) ? groupY : binY ;
209160 }
210161 }
211- if ( transform ) {
212- if ( transform === bin || transform === binX ) markOptions . x = { value : X , ...xOptions } ;
213- if ( transform === bin || transform === binY ) markOptions . y = { value : Y , ...yOptions } ;
214- markOptions = transform ( transformOptions , markOptions ) ;
215- }
162+
163+ // When using the bin transform, pass through additional options (e.g., thresholds).
164+ if ( transformImpl === bin || transformImpl === binX ) markOptions . x = { value : X , ...xOptions } ;
165+ if ( transformImpl === bin || transformImpl === binY ) markOptions . y = { value : Y , ...yOptions } ;
216166
217167 // If zero-ness is not specified, default based on whether the resolved mark
218- // type will include a zero baseline. TODO Move this to autoSpec.
168+ // type will include a zero baseline.
219169 if ( xZero === undefined )
220- xZero = X && transform !== binX && ( mark === barX || mark === areaX || mark === rectX || mark === ruleY ) ;
170+ xZero =
171+ X &&
172+ ! ( transformImpl === bin || transformImpl === binX ) &&
173+ ( markImpl === barX || markImpl === areaX || markImpl === rectX || markImpl === ruleY ) ;
221174 if ( yZero === undefined )
222- yZero = Y && transform !== binY && ( mark === barY || mark === areaY || mark === rectY || mark === ruleX ) ;
175+ yZero =
176+ Y &&
177+ ! ( transformImpl === bin || transformImpl === binY ) &&
178+ ( markImpl === barY || markImpl === areaY || markImpl === rectY || markImpl === ruleX ) ;
179+
180+ return {
181+ fx : fx ?? null ,
182+ fy : fy ?? null ,
183+ x : {
184+ value : xValue ?? null ,
185+ reduce : xReduce ?? null ,
186+ zero : ! ! xZero ,
187+ ...xOptions
188+ } ,
189+ y : {
190+ value : yValue ?? null ,
191+ reduce : yReduce ?? null ,
192+ zero : ! ! yZero ,
193+ ...yOptions
194+ } ,
195+ color : {
196+ value : colorValue ?? null ,
197+ reduce : colorReduce ?? null ,
198+ ...( colorColor !== undefined && { color : colorColor } )
199+ } ,
200+ size : {
201+ value : sizeValue ?? null ,
202+ reduce : sizeReduce ?? null
203+ } ,
204+ mark,
205+ markImpl,
206+ markOptions,
207+ transformImpl,
208+ transformOptions,
209+ colorMode
210+ } ;
211+ }
212+
213+ export function auto ( data , options ) {
214+ const {
215+ fx,
216+ fy,
217+ x : { zero : xZero } ,
218+ y : { zero : yZero } ,
219+ markImpl,
220+ markOptions,
221+ transformImpl,
222+ transformOptions,
223+ colorMode
224+ } = autoImpl ( data , options ) ;
223225
224226 // In the case of filled marks (particularly bars and areas) the frame and
225227 // rules should come after the mark; in the case of stroked marks
226228 // (particularly dots and lines) they should come before the mark.
227229 const frames = fx != null || fy != null ? frame ( { strokeOpacity : 0.1 } ) : null ;
228230 const rules = [ xZero ? ruleX ( [ 0 ] ) : null , yZero ? ruleY ( [ 0 ] ) : null ] ;
229- mark = mark ( data , markOptions ) ;
231+ const mark = markImpl ( data , transformImpl ? transformImpl ( transformOptions , markOptions ) : markOptions ) ;
230232 return colorMode === "stroke" ? marks ( frames , rules , mark ) : marks ( frames , mark , rules ) ;
231233}
232234
@@ -260,6 +262,7 @@ function normalizeOptions({x, y, color, size, fx, fy, mark} = {}) {
260262 if ( ! isOptions ( size ) ) size = makeOptions ( size ) ;
261263 if ( isOptions ( fx ) ) ( { value : fx } = makeOptions ( fx ) ) ;
262264 if ( isOptions ( fy ) ) ( { value : fy } = makeOptions ( fy ) ) ;
265+ if ( mark != null ) mark = `${ mark } ` . toLowerCase ( ) ;
263266 return { x, y, color, size, fx, fy, mark} ;
264267}
265268
0 commit comments