1
- import { extent , namespaces } from "d3" ;
1
+ import { extent , namespaces , polygonCentroid } from "d3" ;
2
+ import { valueObject } from "../channel.js" ;
2
3
import { create } from "../context.js" ;
3
4
import { composeRender } from "../mark.js" ;
4
5
import { hasXY , identity , indexOf } from "../options.js" ;
5
6
import { applyChannelStyles , applyDirectStyles , applyIndirectStyles , getPatternId } from "../style.js" ;
6
7
import { template } from "../template.js" ;
8
+ import { initializer } from "../transforms/basic.js" ;
7
9
import { maybeIdentityX , maybeIdentityY } from "../transforms/identity.js" ;
8
10
import { maybeIntervalX , maybeIntervalY } from "../transforms/interval.js" ;
9
11
import { maybeStackX , maybeStackY } from "../transforms/stack.js" ;
@@ -14,8 +16,10 @@ const waffleDefaults = {
14
16
} ;
15
17
16
18
export class WaffleX extends BarX {
17
- constructor ( data , { unit = 1 , gap = 1 , round, render, multiple, ...options } = { } ) {
18
- super ( data , { ...options , render : composeRender ( render , waffleRender ( "x" ) ) } , waffleDefaults ) ;
19
+ constructor ( data , { unit = 1 , gap = 1 , round, render, multiple, tip, ...options } = { } ) {
20
+ options = initializer ( { ...options , render : composeRender ( render , waffleRender ( "x" ) ) } , waffleInitializer ( "x" ) ) ;
21
+ if ( tip ) options = initializer ( { ...options , tip} , waffleTipInitializer ( "x" ) ) ;
22
+ super ( data , options , waffleDefaults ) ;
19
23
this . unit = Math . max ( 0 , unit ) ;
20
24
this . gap = + gap ;
21
25
this . round = maybeRound ( round ) ;
@@ -24,19 +28,22 @@ export class WaffleX extends BarX {
24
28
}
25
29
26
30
export class WaffleY extends BarY {
27
- constructor ( data , { unit = 1 , gap = 1 , round, render, multiple, ...options } = { } ) {
28
- super ( data , { ...options , render : composeRender ( render , waffleRender ( "y" ) ) } , waffleDefaults ) ;
31
+ constructor ( data , { unit = 1 , gap = 1 , round, render, multiple, tip, ...options } = { } ) {
32
+ options = initializer ( { ...options , render : composeRender ( render , waffleRender ( "y" ) ) } , waffleInitializer ( "y" ) ) ;
33
+ if ( tip ) options = initializer ( { ...options , tip} , waffleTipInitializer ( "y" ) ) ;
34
+ super ( data , options , waffleDefaults ) ;
29
35
this . unit = Math . max ( 0 , unit ) ;
30
36
this . gap = + gap ;
31
37
this . round = maybeRound ( round ) ;
32
38
this . multiple = maybeMultiple ( multiple ) ;
33
39
}
34
40
}
35
41
36
- function waffleRender ( y ) {
37
- return function ( index , scales , values , dimensions , context ) {
38
- const { unit, gap, rx, ry, round} = this ;
39
- const { document} = context ;
42
+ function waffleInitializer ( y ) {
43
+ return function ( data , facets , channels , scales , dimensions ) {
44
+ const { round, unit} = this ;
45
+
46
+ const values = valueObject ( channels , scales ) ;
40
47
const Y1 = values . channels [ `${ y } 1` ] . value ;
41
48
const Y2 = values . channels [ `${ y } 2` ] . value ;
42
49
@@ -56,9 +63,65 @@ function waffleRender(y) {
56
63
57
64
// TODO insets?
58
65
const transform = y === "y" ? ( [ x , y ] ) => [ x * cx , - y * cy ] : ( [ x , y ] ) => [ y * cy , x * cx ] ;
66
+ const P = Array . from ( Y1 , ( _ , i ) => wafflePoints ( round ( Y1 [ i ] / unit ) , round ( Y2 [ i ] / unit ) , multiple ) . map ( transform ) ) ;
67
+
59
68
const tx = ( barwidth - multiple * cx ) / 2 ;
60
- const x0 = typeof barx === "function" ? ( i ) => barx ( i ) + tx : barx + tx ;
61
- const y0 = scales [ y ] ( 0 ) ;
69
+ this . x0 = typeof barx === "function" ? ( i ) => barx ( i ) + tx : barx + tx ;
70
+ this . y0 = scales [ y ] ( 0 ) ;
71
+ this . cx = cx ;
72
+ this . cy = cy ;
73
+ this . barwidth = barwidth ;
74
+ this . barx = barx ;
75
+ this . multiple = multiple ;
76
+
77
+ return { channels : { polygon : { value : P , source : null } } } ;
78
+ } ;
79
+ }
80
+
81
+ function waffleTipInitializer ( y ) {
82
+ return function ( data , facets , channels ) {
83
+ const { x0, y0, barwidth} = this ;
84
+ const P = channels . polygon . value ;
85
+ const n = P . length ;
86
+ const tx = typeof x0 === "function" ? ( i ) => x0 ( i ) - barwidth / 2 : ( ) => x0 ;
87
+ const ty = typeof y0 === "function" ? y0 : ( ) => y0 ;
88
+
89
+ const X = new Float64Array ( n ) ;
90
+ const Y = new Float64Array ( n ) ;
91
+
92
+ const [ ix , iy ] = y === "y" ? [ 0 , 1 ] : [ 1 , 0 ] ;
93
+ for ( let i = 0 ; i < n ; ++ i ) {
94
+ const c = polygonCentroid ( P [ i ] ) ;
95
+ X [ i ] = c [ ix ] + tx ( i ) ;
96
+ Y [ i ] = c [ iy ] + ty ( i ) ;
97
+ }
98
+
99
+ // restore the tip value for y
100
+ const source = channels [ `${ y } 2` ] . hint ?. length
101
+ ? {
102
+ ...channels [ `${ y } 1` ] ,
103
+ value : Array . from ( channels [ `${ y } 1` ] . value , ( d , i ) => channels [ `${ y } 2` ] . value [ i ] - d ) ,
104
+ hint : { single : true }
105
+ }
106
+ : null ;
107
+
108
+ const x = y === "y" ? "x" : "y" ;
109
+ return {
110
+ channels : {
111
+ [ `${ x } 1` ] : { value : X , scale : null , source : null } ,
112
+ [ `${ x } 2` ] : { value : X , scale : null , source : null } ,
113
+ [ `${ y } 1` ] : { value : Y , scale : null , source} ,
114
+ [ `${ y } 2` ] : { value : Y , scale : null , source}
115
+ }
116
+ } ;
117
+ } ;
118
+ }
119
+
120
+ function waffleRender ( y ) {
121
+ return function ( index , scales , values , dimensions , context ) {
122
+ const { gap, cx, cy, rx, ry, x0, y0} = this ;
123
+ const { document} = context ;
124
+ const polygon = values . channels . polygon . value ;
62
125
63
126
// Create a base pattern with shared attributes for cloning.
64
127
const patternId = getPatternId ( ) ;
@@ -95,13 +158,7 @@ function waffleRender(y) {
95
158
. enter ( )
96
159
. append ( "path" )
97
160
. attr ( "transform" , y === "y" ? template `translate(${ x0 } ,${ y0 } )` : template `translate(${ y0 } ,${ x0 } )` )
98
- . attr (
99
- "d" ,
100
- ( i ) =>
101
- `M${ wafflePoints ( round ( Y1 [ i ] / unit ) , round ( Y2 [ i ] / unit ) , multiple )
102
- . map ( transform )
103
- . join ( "L" ) } Z`
104
- )
161
+ . attr ( "d" , ( i ) => `M${ polygon [ i ] . join ( "L" ) } Z` )
105
162
. attr ( "fill" , ( i ) => `url(#${ patternId } -${ i } )` )
106
163
. attr ( "stroke" , this . stroke == null ? null : ( i ) => `url(#${ patternId } -${ i } )` )
107
164
)
@@ -198,12 +255,12 @@ function spread(domain) {
198
255
return max - min ;
199
256
}
200
257
201
- export function waffleX ( data , options = { } ) {
258
+ export function waffleX ( data , { tip , ... options } = { } ) {
202
259
if ( ! hasXY ( options ) ) options = { ...options , y : indexOf , x2 : identity } ;
203
- return new WaffleX ( data , maybeStackX ( maybeIntervalX ( maybeIdentityX ( options ) ) ) ) ;
260
+ return new WaffleX ( data , { tip , ... maybeStackX ( maybeIntervalX ( maybeIdentityX ( options ) ) ) } ) ;
204
261
}
205
262
206
- export function waffleY ( data , options = { } ) {
263
+ export function waffleY ( data , { tip , ... options } = { } ) {
207
264
if ( ! hasXY ( options ) ) options = { ...options , x : indexOf , y2 : identity } ;
208
- return new WaffleY ( data , maybeStackY ( maybeIntervalY ( maybeIdentityY ( options ) ) ) ) ;
265
+ return new WaffleY ( data , { tip , ... maybeStackY ( maybeIntervalY ( maybeIdentityY ( options ) ) ) } ) ;
209
266
}
0 commit comments