public build()

in packages/sanddance-specs/src/layouts/stack.ts [61:259]


    public build(): InnerScope {
        const { names, props } = this;
        const { globalScope, groupings, parentScope, sort } = props;
        const { sizeSignals } = parentScope;

        addTransforms(globalScope.data,
            {
                type: 'joinaggregate',
                groupby: getGroupBy(groupings).map(safeFieldName),
                ops: ['count'],
                as: [names.count]
            },
            {
                type: 'extent',
                field: names.count,
                signal: names.globalExtent
            },
            {
                type: 'stack',
                groupby: getGroupBy(groupings).map(safeFieldName),
                as: [names.stack0, names.stack1],
                ...sort && {
                    sort: {
                        field: safeFieldName(sort.name),
                        order: 'ascending'
                    }
                }
            }
        );

        addData(globalScope.scope,
            {
                name: names.sequence,
                transform: [
                    {
                        type: 'sequence',
                        start: 1,
                        stop: {
                            signal: `sqrt(${names.globalExtent}[1])`
                        }
                    },
                    {
                        type: 'formula',
                        expr: 'datum.data * datum.data',
                        as: 'squared'
                    },
                    {
                        type: 'formula',
                        expr: `ceil(${names.globalExtent}[1] / datum.squared)`,
                        as: 'maxlevels'
                    },
                    {
                        type: 'formula',
                        expr: `(${names.size} - (datum.data - 1) * datum.data) / datum.data`,
                        as: 'side'
                    },
                    {
                        type: 'formula',
                        expr: 'datum.side * datum.maxlevels + datum.maxlevels - 1',
                        as: 'sidecubeheight'
                    },
                    {
                        type: 'formula',
                        expr: `abs(${globalScope.zSize} - datum.sidecubeheight)`,
                        as: 'heightmatch'
                    },
                    {
                        type: 'collect',
                        sort: {
                            field: 'heightmatch',
                            order: 'ascending'
                        }
                    },
                    {
                        type: 'window',
                        ops: ['row_number']
                    },
                    {
                        type: 'filter',
                        expr: 'datum.row_number === 1'
                    }
                ]
            }
        );

        addSignals(globalScope.scope,
            {
                name: names.size,
                update: `min((${sizeSignals.layoutHeight}), (${sizeSignals.layoutWidth}))`
            },
            {
                name: names.squared,
                update: `data('${names.sequence}')[0].squared`
            },
            {
                name: names.sides,
                update: `sqrt(${names.squared})`
            },
            {
                name: names.cube,
                update: `(${names.size} - (${names.sides} - 1)) / ${names.sides}`
            },
            {
                name: names.maxLevels,
                update: `data('${names.sequence}')[0].maxlevels`
            },
            {
                name: names.maxCount,
                update: `${names.maxLevels} * ${names.squared}`
            }
        );

        const zLevel = `floor(datum[${JSON.stringify(names.stack0)}] / ${names.squared})`;
        const layerOrdinal = `(datum[${JSON.stringify(names.stack0)}] % ${names.squared})`;
        const cubeX = `(${layerOrdinal} % ${names.sides})`;
        const cubeY = `floor(${layerOrdinal} / ${names.sides})`;
        const groupX = `(${sizeSignals.layoutWidth} - ${names.size}) / 2`;
        const groupY = `(${sizeSignals.layoutHeight} - ${names.size}) / 2`;

        const offsets: LayoutOffsets = {
            x: addOffsets(parentScope.offsets.x, groupX, `${cubeX} * (${names.cube} + 1)`),
            y: addOffsets(parentScope.offsets.y, groupY, `${cubeY} * (${names.cube} + 1)`),
            h: names.size,
            w: names.size
        };

        const mark: RectMark = {
            type: 'rect',
            from: { data: this.names.levelDataName },
            encode: {
                update: {
                    z: {
                        signal: `${zLevel} * (${names.cube} + 1)`
                    },
                    height: {
                        signal: names.cube
                    },
                    width: {
                        signal: names.cube
                    },
                    depth: {
                        signal: names.cube
                    }
                }
            }
        };
        addMarks(globalScope.markGroup, mark);

        const zScale: LinearScale = {
            type: 'linear',
            name: names.zScale,
            domain: [
                0,
                {
                    signal: names.maxCount
                }
            ],
            range: [
                0,
                {
                    signal: `${names.maxLevels} * (${names.cube} + 1) - 1`
                }
            ],
            nice: false
        };

        return {
            offsets,
            mark,
            sizeSignals: {
                layoutHeight: names.size,
                layoutWidth: names.size
            },
            globalScales: {
                showAxes: true,
                scales: {
                    z: [zScale]
                }
            },
            encodingRuleMap: {
                y: [{
                    test: testForCollapseSelection(),
                    signal: parentScope.offsets.y
                }],
                z: [{
                    test: testForCollapseSelection(),
                    value: 0
                }],
                depth: [{
                    test: testForCollapseSelection(),
                    value: 0
                }],
                height: [{
                    test: testForCollapseSelection(),
                    value: 0
                }]
            }
        };
    }