* use UrlData type

* refactor SpecBuilderProps

* remove unused layout

* remove linear scale from band

* use src

* z axis options

* axisSelection on z

* add z scale

* add density treemap spec test

* stacks z scale for count

* show z scale

* remove zaxiscolor

* show z axis only in 3d view

* filter null scales

* show z axis

* minor version bump

* version bump + changelog

* eslint

* use vega-typings
This commit is contained in:
Dan Marshall 2021-06-22 13:46:12 -07:00 коммит произвёл GitHub
Родитель bd60883ccf
Коммит 2dc07f2d51
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
49 изменённых файлов: 599 добавлений и 503 удалений

Просмотреть файл

@ -56,6 +56,7 @@
<option>column.json</option>
<option>density-facet-cross.json</option>
<option>density-facet-wrap.json</option>
<option>density-treemap.json</option>
<option>density.json</option>
<option>scatter-facet-cross.json</option>
<option>scatter-facet-wrap.json</option>

Просмотреть файл

@ -0,0 +1,19 @@
{
"columns": {
"x": "Longitude",
"y": "Latitude",
"color": "Obama",
"z": "Education",
"sort": "State",
"size": "TotalPop"
},
"facetStyle": "wrap",
"scheme": "redblue",
"totalStyle": "sum-treemap",
"size": {
"height": 600,
"width": 800
},
"chart": "density",
"view": "2d"
}

Просмотреть файл

@ -1,5 +1,8 @@
# Change Log
## [3.3.0]
- Show the z axis scale in 3d
## [3.2.0]
- Fix for last bin of quantitative band scale
- Support virtual workspaces

Просмотреть файл

@ -2,7 +2,7 @@
"name": "azdata-sanddance",
"displayName": "SandDance for Azure Data Studio",
"description": "Visually explore, understand, and present your data.",
"version": "3.2.0",
"version": "3.3.0",
"icon": "sanddance-logo.png",
"preview": true,
"publisher": "msrvida",

Просмотреть файл

@ -1,5 +1,8 @@
# Change Log
## [3.3.0]
- Show the z axis scale in 3d
## [3.2.0]
- Fix for last bin of quantitative band scale
- Support virtual workspaces

Просмотреть файл

@ -2,7 +2,7 @@
"name": "vscode-sanddance",
"displayName": "SandDance for VSCode",
"description": "Visually explore, understand, and present your data.",
"version": "3.2.0",
"version": "3.3.0",
"icon": "sanddance-logo.png",
"publisher": "msrvida",
"repository": {

Просмотреть файл

@ -4,7 +4,7 @@
"displayName": "SandDance 2019",
"guid": "SandDance201929976D117A654D0BAB8E96507442D80B",
"visualClassName": "Visual",
"version": "3.2.0",
"version": "3.3.0",
"description": "Visually explore, understand, and present your data.",
"supportUrl": "https://github.com/Microsoft/SandDance/issues",
"gitHubUrl": "https://github.com/microsoft/SandDance"

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "@msrvida/sanddance-specs",
"version": "1.3.0",
"version": "1.4.0",
"description": "SandDance Vega specification generator.",
"main": "dist/es6/index.js",
"types": "dist/es6/index.d.ts",

Просмотреть файл

@ -6,7 +6,7 @@ import { GlobalScope } from './globalScope';
import { AxisScale, AxisScales, GlobalScales } from './interfaces';
import { addAxes, addScales } from './scope';
import { SpecColumns, SpecViewOptions } from './types';
import { Column } from '@msrvida/chart-types';
import { Column, View } from '@msrvida/chart-types';
import {
Axis,
NewSignal,
@ -28,16 +28,17 @@ export interface AxesScopeMap {
}
interface Props {
globalScope: GlobalScope,
allGlobalScales: GlobalScales[],
axisScales: AxisScales,
plotOffsetSignals: { x: NewSignal, y: NewSignal },
axesOffsets: { x: number, y: number },
axesTitlePadding: { x: number, y: number },
labelBaseline: { x: TextBaselineValue, y: TextBaselineValue },
specColumns: SpecColumns,
specViewOptions: SpecViewOptions,
axesScopes: AxesScopeMap
globalScope: GlobalScope;
allGlobalScales: GlobalScales[];
axisScales: AxisScales;
plotOffsetSignals: { x: NewSignal, y: NewSignal };
axesOffsets: { x: number, y: number };
axesTitlePadding: { x: number, y: number };
labelBaseline: { x: TextBaselineValue, y: TextBaselineValue };
specColumns: SpecColumns;
specViewOptions: SpecViewOptions;
axesScopes: AxesScopeMap;
view: View;
}
export function addGlobalAxes(props: Props) {
@ -46,16 +47,27 @@ export function addGlobalAxes(props: Props) {
allGlobalScales.forEach(globalScales => {
const { scales } = globalScales;
for (let s in scales) {
let _scales: Scale[] = scales[s];
for (let xyz in scales) {
let _scales: Scale[] = scales[xyz];
if (_scales) {
addScales(scope, ..._scales);
if (globalScales.showAxes && axisScales && s !== 'z') {
let axisScale: AxisScale = axisScales[s];
let { showAxes } = globalScales;
let zindex: number = undefined;
if (xyz === 'z') {
showAxes = false;
if (props.view === '3d' && specViewOptions.zAxisOptions) {
if (specViewOptions.zAxisOptions.showZAxis) {
showAxes = true;
zindex = specViewOptions.zAxisOptions.zIndex;
}
}
}
if (showAxes && axisScales) {
let axisScale: AxisScale = axisScales[xyz];
if (axisScale) {
const lineColor = specViewOptions.colors.axisLine;
const horizontal = s === 'x';
const column: Column = specColumns[s] || { quantitative: true };
const horizontal = xyz === 'x';
const column: Column = specColumns[xyz] || { quantitative: true };
const title = axisScale.title;
const props: AxisProps = {
title,
@ -63,8 +75,9 @@ export function addGlobalAxes(props: Props) {
column,
specViewOptions,
lineColor,
titlePadding: axesTitlePadding[s],
labelBaseline: labelBaseline[s]
titlePadding: axesTitlePadding[xyz],
labelBaseline: labelBaseline[xyz],
zindex
};
axesScopes['main'].forEach(a => addAxes(a.scope, createAxis({
...props,
@ -74,8 +87,8 @@ export function addGlobalAxes(props: Props) {
showLines: a.lines
})));
if (axesScopes[s]) {
axesScopes[s].forEach(a => addAxes(a.scope, createAxis({
if (axesScopes[xyz]) {
axesScopes[xyz].forEach(a => addAxes(a.scope, createAxis({
...props,
scale: a.scale || _scales[0],
showTitle: a.title,
@ -84,9 +97,9 @@ export function addGlobalAxes(props: Props) {
})));
}
if (plotOffsetSignals[s] && axesOffsets[s]) {
const plotOffsetSignal = plotOffsetSignals[s] as NewSignal;
plotOffsetSignal.update = `${axesOffsets[s]}`;
if (plotOffsetSignals[xyz] && axesOffsets[xyz]) {
const plotOffsetSignal = plotOffsetSignals[xyz] as NewSignal;
plotOffsetSignal.update = `${axesOffsets[xyz]}`;
}
}
}
@ -107,11 +120,13 @@ interface AxisProps {
showLabels?: boolean;
titlePadding: number;
labelBaseline: TextBaselineValue;
zindex: number;
}
function createAxis(props: AxisProps) {
const { column, horizontal, labelBaseline, lineColor, scale, showLabels, showTitle, showLines, specViewOptions, title, titlePadding } = props;
const { column, horizontal, labelBaseline, lineColor, scale, showLabels, showTitle, showLines, specViewOptions, title, titlePadding, zindex } = props;
const axis: Axis = {
zindex,
scale: scale.name,
orient: horizontal ? 'bottom' : 'left',
domain: showLines,

Просмотреть файл

@ -1,13 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { getSpecBuilderForChart } from './charts';
import { getSpecBuilderPropsForChart } from './charts';
import { inferAll } from './inference';
import { SpecContext } from './types';
import { SpecResult } from './interfaces';
import { ValuesData } from 'vega-typings';
import { SpecBuilder } from './specBuilder';
export function build(context: SpecContext, currData: object[]): SpecResult {
const { specColumns } = context;
export function build(specContext: SpecContext, currData: object[]): SpecResult {
const { specColumns } = specContext;
const columns = [
specColumns.color,
specColumns.facet,
@ -21,12 +22,22 @@ export function build(context: SpecContext, currData: object[]): SpecResult {
];
inferAll(columns, currData);
const specBuilder = getSpecBuilderForChart(context);
const specBuilderProps = getSpecBuilderPropsForChart(specContext);
const specBuilder = new SpecBuilder(specBuilderProps, specContext);
let specResult: SpecResult;
if (specBuilder) {
try {
specResult = specBuilder.build();
const errors = specBuilder.validate();
if (errors.length) {
specResult = {
errors,
specCapabilities: specBuilderProps.specCapabilities,
vegaSpec: null
};
} else {
specResult = specBuilder.build();
}
}
catch (e) {
specResult = {
@ -43,7 +54,7 @@ export function build(context: SpecContext, currData: object[]): SpecResult {
specResult = {
specCapabilities: null,
vegaSpec: null,
errors: [`could not build spec for ${context.insight.chart}`]
errors: [`could not build spec for ${specContext.insight.chart}`]
};
}
return specResult;

Просмотреть файл

@ -3,12 +3,12 @@
import { SignalNames } from '../constants';
import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
import { AxisScale, AxisScales } from '../interfaces';
import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
import { Band, BandProps } from '../layouts/band';
import { AggregateContainerProps } from '../layouts/aggregateContainer';
import { BandProps } from '../layouts/band';
import { LayoutPair } from '../layouts/layout';
import { Square, SquareProps } from '../layouts/square';
import { Strip, StripProps } from '../layouts/strip';
import { Treemap, TreemapProps } from '../layouts/treemap';
import { SquareProps } from '../layouts/square';
import { StripProps } from '../layouts/strip';
import { TreemapProps } from '../layouts/treemap';
import { allowNoneForSize } from '../size';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
@ -35,7 +35,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: { title: specColumns.z && specColumns.z.name }
};
const layouts: LayoutPair[] = [{
layoutClass: Band,
layoutType: 'Band',
props: bandProps
}];
if (insight.totalStyle === 'sum-strip-percent') {
@ -50,7 +50,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
} else {
@ -63,7 +63,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
showAxes: true
};
layouts.push({
layoutClass: AggregateContainer,
layoutType: 'AggregateContainer',
props: aggProps
});
switch (insight.totalStyle) {
@ -77,7 +77,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Treemap,
layoutType: 'Treemap',
props: treemapProps
});
break;
@ -93,7 +93,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
break;
@ -109,7 +109,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
break;
@ -125,7 +125,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
maxGroupedFillSize: aggProps.globalAggregateMaxExtentScaledSignal
};
layouts.push({
layoutClass: Square,
layoutType: 'Square',
props: squareProps
});
break;
@ -147,6 +147,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
},
{
role: 'z',
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
allowNone: true
},
{

Просмотреть файл

@ -3,12 +3,12 @@
import { SignalNames } from '../constants';
import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
import { AxisScale, AxisScales } from '../interfaces';
import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
import { Band, BandProps } from '../layouts/band';
import { AggregateContainerProps } from '../layouts/aggregateContainer';
import { BandProps } from '../layouts/band';
import { LayoutPair } from '../layouts/layout';
import { Square, SquareProps } from '../layouts/square';
import { Strip, StripProps } from '../layouts/strip';
import { Treemap, TreemapProps } from '../layouts/treemap';
import { SquareProps } from '../layouts/square';
import { StripProps } from '../layouts/strip';
import { TreemapProps } from '../layouts/treemap';
import { allowNoneForSize } from '../size';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
@ -35,7 +35,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: { title: specColumns.z && specColumns.z.name }
};
const layouts: LayoutPair[] = [{
layoutClass: Band,
layoutType: 'Band',
props: bandProps
}];
if (insight.totalStyle === 'sum-strip-percent') {
@ -50,7 +50,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
} else {
@ -63,7 +63,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
showAxes: true
};
layouts.push({
layoutClass: AggregateContainer,
layoutType: 'AggregateContainer',
props: aggProps
});
switch (insight.totalStyle) {
@ -77,7 +77,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Treemap,
layoutType: 'Treemap',
props: treemapProps
});
break;
@ -93,7 +93,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
break;
@ -108,7 +108,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
break;
@ -124,7 +124,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
maxGroupedFillSize: aggProps.globalAggregateMaxExtentScaledSignal
};
layouts.push({
layoutClass: Square,
layoutType: 'Square',
props: squareProps
});
break;
@ -146,6 +146,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
},
{
role: 'z',
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
allowNone: true
},
{

Просмотреть файл

@ -3,12 +3,12 @@
import { SignalNames } from '../constants';
import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
import { AxisScales } from '../interfaces';
import { AggregateSquare, AggregateSquareProps } from '../layouts/aggregateSquare';
import { Band, BandProps } from '../layouts/band';
import { AggregateSquareProps } from '../layouts/aggregateSquare';
import { BandProps } from '../layouts/band';
import { LayoutPair } from '../layouts/layout';
import { Square, SquareProps } from '../layouts/square';
import { Strip, StripProps } from '../layouts/strip';
import { Treemap, TreemapProps } from '../layouts/treemap';
import { SquareProps } from '../layouts/square';
import { StripProps } from '../layouts/strip';
import { TreemapProps } from '../layouts/treemap';
import { allowNoneForSize } from '../size';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
@ -53,15 +53,15 @@ export default function (specContext: SpecContext): SpecBuilderProps {
};
const layouts: LayoutPair[] = [
{
layoutClass: Band,
layoutType: 'Band',
props: vBandProps
},
{
layoutClass: Band,
layoutType: 'Band',
props: hBandProps
},
{
layoutClass: AggregateSquare,
layoutType: 'AggregateSquare',
props: aggProps
}
];
@ -75,7 +75,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Treemap,
layoutType: 'Treemap',
props: treemapProps
});
break;
@ -90,7 +90,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
break;
@ -104,7 +104,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
z: specColumns.z
};
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
break;
@ -123,7 +123,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
squareProps.maxGroupedFillSize = aggMaxExtentScaled;
};
layouts.push({
layoutClass: Square,
layoutType: 'Square',
props: squareProps
});
break;
@ -149,6 +149,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
},
{
role: 'z',
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
allowNone: true
},
{

Просмотреть файл

@ -4,7 +4,7 @@ import { AxisScales } from '../interfaces';
import { SignalNames } from '../constants';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
import { Square, SquareProps } from '../layouts/square';
import { SquareProps } from '../layouts/square';
export default function (specContext: SpecContext): SpecBuilderProps {
const { specColumns } = specContext;
@ -21,7 +21,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
axisScales,
layouts: [
{
layoutClass: Square,
layoutType: 'Square',
props: squareProps
}
],
@ -30,6 +30,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
roles: [
{
role: 'z',
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
allowNone: true
},
{

Просмотреть файл

@ -8,8 +8,12 @@ import scatterplot from './scatterplot';
import stacks from './stacks';
import strips from './strips';
import treemap from './treemap';
import { SpecBuilder, SpecBuilderProps } from '../specBuilder';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
import { getFacetLayout } from '../facetLayout';
import { DiscreteColumn } from '../interfaces';
import { SignalNames } from '../constants';
import { defaultBins, maxbins } from '../defaults';
const map: { [chart: string]: (specContext: SpecContext) => SpecBuilderProps } = {
barchart: barchartV,
@ -23,13 +27,30 @@ const map: { [chart: string]: (specContext: SpecContext) => SpecBuilderProps } =
treemap
};
export function getSpecBuilderForChart(specContext: SpecContext) {
const { insight } = specContext;
let props: SpecBuilderProps;
export function getSpecBuilderPropsForChart(specContext: SpecContext) {
const { insight, specColumns, specViewOptions } = specContext;
const fn = map[insight.chart];
if (fn) {
props = fn(specContext);
return new SpecBuilder({ ...props, specContext });
const props = fn(specContext);
if (insight.columns.facet) {
const discreteFacetColumn: DiscreteColumn = {
column: specColumns.facet,
defaultBins,
maxbins,
maxbinsSignalDisplayName: specViewOptions.language.FacetMaxBins,
maxbinsSignalName: SignalNames.FacetBins
};
const discreteFacetVColumn: DiscreteColumn = {
column: specColumns.facetV,
defaultBins,
maxbins,
maxbinsSignalDisplayName: specViewOptions.language.FacetVMaxBins,
maxbinsSignalName: SignalNames.FacetVBins
};
const { facetLayout, layoutPair } = getFacetLayout(insight.facetStyle, discreteFacetColumn, discreteFacetVColumn, specViewOptions.colors.axisText);
props.layouts.unshift(layoutPair);
props.facetLayout = facetLayout;
}
return props;
}
}

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { AxisScales } from '../interfaces';
import { Scatter, ScatterProps } from '../layouts/scatter';
import { ScatterProps } from '../layouts/scatter';
import { SignalNames } from '../constants';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
@ -25,7 +25,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
axisScales,
layouts: [
{
layoutClass: Scatter,
layoutType: 'Scatter',
props: scatterProps
}
],
@ -42,6 +42,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
},
{
role: 'z',
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
allowNone: true
},
{

Просмотреть файл

@ -1,18 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { AxisScales } from '../interfaces';
import { Band, BandProps } from '../layouts/band';
import { BandProps } from '../layouts/band';
import { defaultBins, maxbins, minBarBandWidth } from '../defaults';
import { SignalNames } from '../constants';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
import { Stack, StackProps } from '../layouts/stack';
import { StackProps } from '../layouts/stack';
export default function (specContext: SpecContext): SpecBuilderProps {
const { specColumns } = specContext;
const { specColumns, specViewOptions } = specContext;
const axisScales: AxisScales = {
x: { title: specColumns.x && specColumns.x.name },
y: { title: specColumns.y && specColumns.y.name }
y: { title: specColumns.y && specColumns.y.name },
z: { title: specViewOptions.language.count }
};
const hBandProps: BandProps = {
excludeEncodingRuleMap: true,
@ -48,15 +49,15 @@ export default function (specContext: SpecContext): SpecBuilderProps {
customZScale: true,
layouts: [
{
layoutClass: Band,
layoutType: 'Band',
props: vBandProps
},
{
layoutClass: Band,
layoutType: 'Band',
props: hBandProps
},
{
layoutClass: Stack,
layoutType: 'Stack',
props: stackProps
}
],

Просмотреть файл

@ -1,12 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
import { AggregateContainerProps } from '../layouts/aggregateContainer';
import { AxisScales } from '../interfaces';
import { LayoutPair } from '../layouts/layout';
import { SignalNames } from '../constants';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
import { Strip, StripProps } from '../layouts/strip';
import { StripProps } from '../layouts/strip';
export default function (specContext: SpecContext): SpecBuilderProps {
const { specColumns } = specContext;
@ -37,12 +37,12 @@ export default function (specContext: SpecContext): SpecBuilderProps {
showAxes: false
};
layouts.push({
layoutClass: AggregateContainer,
layoutType: 'AggregateContainer',
props
});
}
layouts.push({
layoutClass: Strip,
layoutType: 'Strip',
props: stripProps
});
return {
@ -58,6 +58,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
},
{
role: 'z',
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
allowNone: true
},
{

Просмотреть файл

@ -4,9 +4,9 @@ import { AxisScales } from '../interfaces';
import { SignalNames } from '../constants';
import { SpecBuilderProps } from '../specBuilder';
import { SpecContext } from '../types';
import { Treemap, TreemapProps } from '../layouts/treemap';
import { TreemapProps } from '../layouts/treemap';
import { LayoutPair } from '../layouts/layout';
import { AggregateContainer, AggregateContainerProps } from '../layouts/aggregateContainer';
import { AggregateContainerProps } from '../layouts/aggregateContainer';
export default function (specContext: SpecContext): SpecBuilderProps {
const { specColumns, specViewOptions } = specContext;
@ -37,12 +37,12 @@ export default function (specContext: SpecContext): SpecBuilderProps {
showAxes: false
};
layouts.push({
layoutClass: AggregateContainer,
layoutType: 'AggregateContainer',
props
});
}
layouts.push({
layoutClass: Treemap,
layoutType: 'Treemap',
props: treemapProps
});
return {
@ -61,6 +61,7 @@ export default function (specContext: SpecContext): SpecBuilderProps {
},
{
role: 'z',
axisSelection: specColumns.z && specColumns.z.quantitative ? 'range' : 'exact',
allowNone: true
},
{

Просмотреть файл

@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Cross, CrossProps } from './layouts/cross';
import { CrossProps } from './layouts/cross';
import { DiscreteColumn } from './interfaces';
import {
facetPaddingBottom,
@ -10,26 +10,32 @@ import {
} from './defaults';
import { FacetStyle } from './types';
import { LayoutPair } from './layouts/layout';
import { Scale, Signal } from 'vega-typings';
import { SignalNames } from './constants';
import { Wrap, WrapProps } from './layouts/wrap';
import { WrapProps } from './layouts/wrap';
export interface FacetLayout {
layoutPair: LayoutPair;
plotPadding: { x: number, y: number };
scales: Scale[];
signals: Signal[];
export interface PlotPadding {
x: number;
y: number;
}
export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColumn, facetVColumn: DiscreteColumn, axisTextColor: string): FacetLayout {
export interface FacetPadding {
top: number;
left: number;
bottom: number;
}
export interface FacetLayout {
facetPadding: FacetPadding;
plotPadding: PlotPadding;
}
export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColumn, facetVColumn: DiscreteColumn, axisTextColor: string) {
let layoutPair: LayoutPair;
const scales: Scale[] = [];
let signals: Signal[];
const groupby = facetColumn;
const plotPadding = {
const plotPadding: PlotPadding = {
x: 0,
y: 0
};
let facetPadding: FacetPadding;
switch (facetStyle) {
case 'cross': {
const props: CrossProps = {
@ -39,23 +45,14 @@ export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColu
groupbyY: facetVColumn
};
layoutPair = {
layoutClass: Cross,
layoutType: 'Cross',
props
};
signals = [
{
name: SignalNames.FacetPaddingBottom,
update: `${facetPaddingBottom}`
},
{
name: SignalNames.FacetPaddingLeft,
update: `${facetPaddingLeft}`
},
{
name: SignalNames.FacetPaddingTop,
update: '0'
}
];
facetPadding = {
bottom: facetPaddingBottom,
left: facetPaddingLeft,
top: 0
};
plotPadding.y = facetPaddingTop;
plotPadding.x = facetPaddingRight;
break;
@ -68,26 +65,25 @@ export function getFacetLayout(facetStyle: FacetStyle, facetColumn: DiscreteColu
groupby
};
layoutPair = {
layoutClass: Wrap,
layoutType: 'Wrap',
props
};
signals = [
{
name: SignalNames.FacetPaddingBottom,
update: `${facetPaddingBottom}`
},
{
name: SignalNames.FacetPaddingLeft,
update: `${facetPaddingLeft}`
},
{
name: SignalNames.FacetPaddingTop,
update: `${facetPaddingTop}`
}
];
facetPadding =
{
bottom: facetPaddingBottom,
left: facetPaddingLeft,
top: facetPaddingTop
};
break;
}
}
return { layoutPair, plotPadding, scales, signals };
const facetLayout: FacetLayout = {
facetPadding,
plotPadding
};
return {
layoutPair,
facetLayout
};
}

Просмотреть файл

@ -81,10 +81,6 @@ export class AggregateSquare extends Layout {
layoutHeight: null,
layoutWidth: null
},
globalScales: {
showAxes: false,
scales: {}
},
encodingRuleMap: {
y: [{
test: testForCollapseSelection(),

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Layout, LayoutBuildProps, LayoutProps } from './layout';
import { AugmentBinnable, binnable, Binnable } from '../bin';
import { binnable, Binnable } from '../bin';
import { safeFieldName } from '../expr';
import {
DiscreteColumn,
@ -18,7 +18,7 @@ import {
} from '../scope';
import { testForCollapseSelection } from '../selection';
import { modifySignal } from '../signals';
import { BandScale, LinearScale, Scale } from 'vega-typings';
import { BandScale, Scale } from 'vega-typings';
export interface BandProps extends LayoutProps {
excludeEncodingRuleMap?: boolean;
@ -178,55 +178,6 @@ export class Band extends Layout {
const binField = safeFieldName(bin.fields[0]);
const scales: Scale[] = [];
// function axisScaleName(scaleName: string) {
// return `${scaleName}_axis`;
// }
// if (bin.discreteColumn.column.quantitative) {
// const { binSignal } = <AugmentBinnable>bin;
// let linearScale: LinearScale;
// if (horizontal) {
// linearScale = {
// type: 'linear',
// name: axisScaleName(names.yScale),
// range: [
// 0,
// {
// signal: parentScope.sizeSignals.layoutHeight
// }
// ],
// domain: {
// signal: `[${binSignal}.start, ${binSignal}.stop]`
// },
// bins: {
// signal: binSignal
// },
// reverse: true,
// zero: false
// };
// } else {
// linearScale = {
// type: 'linear',
// name: axisScaleName(names.xScale),
// range: [
// 0,
// {
// signal: parentScope.sizeSignals.layoutWidth
// }
// ],
// domain: {
// signal: `[${binSignal}.start, ${binSignal}.stop]`
// },
// bins: {
// signal: binSignal
// },
// zero: false
// };
// }
// scales.push(linearScale);
// }
let bandScale: BandScale;
if (horizontal) {
bandScale = {

Просмотреть файл

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { AggregateContainer } from './aggregateContainer';
import { AggregateSquare } from './aggregateSquare';
import { Band } from './band';
import { Cross } from './cross';
import { Layout } from './layout';
import { Scatter } from './scatter';
import { Square } from './square';
import { Stack } from './stack';
import { Strip } from './strip';
import { Treemap } from './treemap';
import { Wrap } from './wrap';
export type LayoutType =
'AggregateContainer' |
'AggregateSquare' |
'Band' |
'Cross' |
'Scatter' |
'Square' |
'Stack' |
'Strip' |
'Treemap' |
'Wrap'
;
export const layoutClasses: { [key in LayoutType]: typeof Layout } = {
AggregateContainer,
AggregateSquare,
Band,
Cross,
Scatter,
Square,
Stack,
Strip,
Treemap,
Wrap
};

Просмотреть файл

@ -1,5 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { LayoutType } from './index';
import { GlobalScope } from '../globalScope';
import {
AxisScales,
@ -13,7 +14,7 @@ export interface LayoutProps {
export interface LayoutPair {
props?: LayoutProps;
layoutClass: typeof Layout;
layoutType: LayoutType;
}
export interface LayoutBuildProps {

Просмотреть файл

@ -1,74 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Layout, LayoutBuildProps, LayoutProps } from './layout';
import { binnable, Binnable } from '../bin';
import { DiscreteColumn, InnerScope, Orientation } from '../interfaces';
import { addTransforms } from '../scope';
import { Mark } from 'vega-typings';
export interface SliceProps extends LayoutProps {
groupby: DiscreteColumn;
orientation: Orientation;
}
export class Slice extends Layout {
private bin: Binnable;
constructor(public props: SliceProps & LayoutBuildProps) {
super(props);
this.prefix = `slice_${this.id}`;
this.bin = binnable(this.prefix, props.globalScope.data.name, props.groupby);
}
public getGrouping() {
return this.bin.fields;
}
public build(): InnerScope {
const { bin, prefix, props } = this;
const { globalScope, parentScope } = props;
const facetDataName = `data_${prefix}_facet`;
if (bin.native === false) {
globalScope.scope.signals.push(...bin.signals);
addTransforms(globalScope.data, ...bin.transforms);
globalScope.scope.data.push(bin.dataSequence);
}
// const mark: Mark = {
// style: 'cell',
// name: prefix,
// type: 'group',
// from: {
// facet: {
// name: facetDataName,
// data: parentScope.dataName,
// groupby: bin.fields
// }
// },
// encode: {
// },
// marks: [
// {
// type: 'text',
// encode: {
// update: {
// text: {
// signal: `length(data(${JSON.stringify(facetDataName)}))`
// },
// fontSize: {
// value: 20
// }
// }
// }
// }
// ]
// };
// parentScope.scope.marks.push(mark);
return {
offsets: null,
sizeSignals: { layoutHeight: 'TODO', layoutWidth: 'TODO' }
};
}
}

Просмотреть файл

@ -50,7 +50,7 @@ export class Square extends Layout {
public build(): InnerScope {
const { names, prefix, props } = this;
const { fillDirection, globalScope, groupings, parentScope, collapseYHeight, sortBy, z } = props;
addZScale(z, globalScope.zSize, globalScope, names.zScale);
const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
addTransforms(globalScope.data, {
type: 'stack',
@ -112,6 +112,14 @@ export class Square extends Layout {
const { tx, ty } = this.transformXY(gap, levelSize, squaresPerBand);
return {
...z && {
globalScales: {
showAxes: true,
scales: {
z: [zScale]
}
}
},
offsets: {
x: addOffsets(parentScope.offsets.x, tx.expr),
y: addOffsets(parentScope.offsets.y, ty.expr),

Просмотреть файл

@ -13,7 +13,7 @@ import {
} from '../scope';
import { testForCollapseSelection } from '../selection';
import { Column } from '@msrvida/chart-types';
import { RectMark } from 'vega-typings';
import { LinearScale, RectMark } from 'vega-typings';
export interface StackProps extends LayoutProps {
sort: Column;
@ -32,7 +32,9 @@ export class Stack extends Layout {
sides: string,
size: string,
squared: string,
squaredExtent: string
maxCount: string,
maxLevels: string,
zScale: string,
};
constructor(public props: StackProps & LayoutBuildProps) {
@ -50,7 +52,9 @@ export class Stack extends Layout {
sides: `${p}_sides`,
size: `${p}_size`,
squared: `${p}_squared`,
squaredExtent: `${p}_squared_extent`
maxCount: `${p}_maxCount`,
maxLevels: `${p}_maxLevels`,
zScale: `${p}_zScale`,
};
}
@ -134,11 +138,6 @@ export class Stack extends Layout {
{
type: 'filter',
expr: 'datum.row_number === 1'
},
{
type: 'extent',
field: 'squared',
signal: names.squaredExtent
}
]
}
@ -151,7 +150,7 @@ export class Stack extends Layout {
},
{
name: names.squared,
update: `${names.squaredExtent}[0]`
update: `data('${names.sequence}')[0].squared`
},
{
name: names.sides,
@ -160,6 +159,14 @@ export class Stack extends Layout {
{
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}`
}
);
@ -199,6 +206,24 @@ export class Stack extends Layout {
};
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,
@ -207,8 +232,10 @@ export class Stack extends Layout {
layoutWidth: names.size
},
globalScales: {
showAxes: false,
scales: {}
showAxes: true,
scales: {
z: [zScale]
}
},
encodingRuleMap: {
y: [{

Просмотреть файл

@ -55,7 +55,7 @@ export class Strip extends Layout {
const { names, prefix, props } = this;
const { addPercentageScale, globalScope, groupings, orientation, size, sort, sortOrder, parentScope, z } = props;
addZScale(z, globalScope.zSize, globalScope, names.zScale);
const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
const horizontal = orientation === 'horizontal';
@ -182,7 +182,8 @@ export class Strip extends Layout {
showAxes: true,
scales: {
x: horizontal ? [percentageScale] : undefined,
y: horizontal ? undefined : [percentageScale]
y: horizontal ? undefined : [percentageScale],
z: zScale && [zScale]
}
},
offsets,

Просмотреть файл

@ -77,7 +77,7 @@ export class Treemap extends Layout {
const { names, props } = this;
const { globalScope, parentScope, treeMapMethod, z } = props;
addZScale(z, globalScope.zSize, globalScope, names.zScale);
const zScale = addZScale(z, globalScope.zSize, globalScope.data.name, names.zScale);
const offsets: LayoutOffsets = {
x: addOffsets(parentScope.offsets.x, fn(names.fieldX0)),
@ -101,6 +101,14 @@ export class Treemap extends Layout {
});
return {
...z && {
globalScales: {
showAxes: true,
scales: {
z: [zScale]
}
}
},
mark,
offsets,
sizeSignals: {

Просмотреть файл

@ -36,7 +36,7 @@ export function addScales(scope: Scope, ...scale: Scale[]) {
if (!scope.scales) {
scope.scales = [];
}
scope.scales.push(...scale);
scope.scales.push(...scale.filter(Boolean));
}
export function addSignals(scope: Scope, ...signal: Signal[]) {

Просмотреть файл

@ -9,29 +9,24 @@ import {
axesTitlePaddingFacetX,
axesTitlePaddingFacetY,
axesTitlePaddingX,
axesTitlePaddingY,
defaultBins,
maxbins
axesTitlePaddingY
} from './defaults';
import { minFacetHeight, minFacetWidth } from './defaults';
import { FacetLayout, getFacetLayout } from './facetLayout';
import { FacetLayout } from './facetLayout';
import { addFacetAxesGroupMarks } from './facetTitle';
import { fill, opacity } from './fill';
import { GlobalScope, GlobalSignals } from './globalScope';
import {
AxisScales,
DiscreteColumn,
EncodingRule,
GlobalScales,
Grouping,
InnerScope,
LayoutOffsets,
SpecResult
} from './interfaces';
import { LayoutBuildProps, LayoutPair, LayoutProps } from './layouts/layout';
import {
addData,
addScales,
addSignals
} from './scope';
import { textSignals } from './signals';
@ -41,6 +36,7 @@ import {
LinearScale,
Spec
} from 'vega-typings';
import { layoutClasses } from './layouts/index';
export interface SpecBuilderProps {
axisScales?: AxisScales;
@ -48,12 +44,13 @@ export interface SpecBuilderProps {
errors?: string[];
specCapabilities: SpecCapabilities;
customZScale?: boolean;
facetLayout?: FacetLayout;
}
export class SpecBuilder {
private globalSignals: GlobalSignals;
constructor(public props: SpecBuilderProps & { specContext: SpecContext }) {
constructor(public props: SpecBuilderProps, public specContext: SpecContext) {
this.globalSignals = {
minCellWidth: {
name: SignalNames.MinCellWidth,
@ -70,7 +67,8 @@ export class SpecBuilder {
}
public validate() {
const { specCapabilities, specContext } = this.props;
const { specContext } = this;
const { specCapabilities } = this.props;
const { roles } = specCapabilities;
const required = roles.filter(r => {
switch (typeof r.allowNone) {
@ -109,186 +107,174 @@ export class SpecBuilder {
}
public build(): SpecResult {
const { specCapabilities } = this.props;
const errors = this.validate();
if (errors.length) {
return {
errors,
specCapabilities,
vegaSpec: null
};
} else {
const { specContext } = this.props;
const { insight, specColumns, specViewOptions } = specContext;
const dataName = 'data_source';
const { vegaSpec, groupMark } = this.initSpec(dataName);
const { topColorField, colorDataName } = addColor({
scope: vegaSpec,
dataName,
specContext,
scaleName: ScaleNames.Color,
legendDataName: 'data_legend',
topLookupName: 'data_topcolorlookup',
colorReverseSignalName: SignalNames.ColorReverse
});
const globalScope = new GlobalScope({
dataName: colorDataName,
markGroup: groupMark,
scope: vegaSpec,
signals: this.globalSignals
});
let facetLayout: FacetLayout;
if (insight.columns.facet) {
const discreteFacetColumn: DiscreteColumn = {
column: specColumns.facet,
defaultBins,
maxbins,
maxbinsSignalDisplayName: specViewOptions.language.FacetMaxBins,
maxbinsSignalName: SignalNames.FacetBins
};
const discreteFacetVColumn: DiscreteColumn = {
column: specColumns.facetV,
defaultBins,
maxbins,
maxbinsSignalDisplayName: specViewOptions.language.FacetVMaxBins,
maxbinsSignalName: SignalNames.FacetVBins
};
facetLayout = getFacetLayout(insight.facetStyle, discreteFacetColumn, discreteFacetVColumn, specViewOptions.colors.axisText);
addSignals(vegaSpec, ...facetLayout.signals);
addScales(vegaSpec, ...facetLayout.scales);
this.props.layouts = [facetLayout.layoutPair, ...this.props.layouts];
this.globalSignals.plotOffsetTop.update = `${facetLayout.plotPadding.y}`;
this.globalSignals.plotOffsetRight.update = `${facetLayout.plotPadding.x}`;
const { specContext } = this;
const { facetLayout, specCapabilities } = this.props;
const { insight, specColumns, specViewOptions } = specContext;
const dataName = 'data_source';
const { vegaSpec, groupMark } = this.initSpec(dataName);
const { topColorField, colorDataName } = addColor({
scope: vegaSpec,
dataName,
specContext,
scaleName: ScaleNames.Color,
legendDataName: 'data_legend',
topLookupName: 'data_topcolorlookup',
colorReverseSignalName: SignalNames.ColorReverse
});
const globalScope = new GlobalScope({
dataName: colorDataName,
markGroup: groupMark,
scope: vegaSpec,
signals: this.globalSignals
});
if (facetLayout) {
addSignals(vegaSpec,
{
name: SignalNames.FacetPaddingBottom,
update: `${facetLayout.facetPadding.bottom}`
},
{
name: SignalNames.FacetPaddingLeft,
update: `${facetLayout.facetPadding.left}`
},
{
name: SignalNames.FacetPaddingTop,
update: `${facetLayout.facetPadding.top}`
}
);
this.globalSignals.plotOffsetTop.update = `${facetLayout.plotPadding.y}`;
this.globalSignals.plotOffsetRight.update = `${facetLayout.plotPadding.x}`;
}
const {
firstScope,
finalScope,
specResult,
allGlobalScales,
allEncodingRules
} = this.iterateLayouts(globalScope, (i, innerScope) => {
if (facetLayout && i === 0) {
globalScope.zSize = innerScope.offsets.h;
}
const {
firstScope,
finalScope,
specResult,
});
if (specResult) {
return specResult;
}
if (allGlobalScales.length > 0) {
const plotHeightOut = this.globalSignals.plotHeightOut.name;
const plotWidthOut = this.globalSignals.plotWidthOut.name;
const colTitleScale: LinearScale = {
type: 'linear',
name: 'scale_facet_col_title',
domain: [0, 1],
range: [0, { signal: plotWidthOut }]
};
const rowTitleScale: LinearScale = {
type: 'linear',
name: 'scale_facet_row_title',
domain: [0, 1],
range: [{ signal: plotHeightOut }, 0]
};
let axesScopes: AxesScopeMap = facetLayout ?
addFacetAxesGroupMarks({
globalScope: globalScope.scope,
plotScope: groupMark,
facetScope: firstScope,
colTitleScale,
rowTitleScale,
colSeqName: 'data_FacetCellColTitles',
rowSeqName: 'data_FacetCellRowTitles'
})
:
{
main: [{
scope: groupMark,
lines: true,
labels: true,
title: true
}]
};
addGlobalAxes({
globalScope,
allGlobalScales,
allEncodingRules
} = this.iterateLayouts(globalScope, (i, innerScope) => {
if (facetLayout && i === 0) {
globalScope.zSize = innerScope.offsets.h;
axisScales: this.props.axisScales,
plotOffsetSignals: { x: this.globalSignals.plotOffsetLeft, y: this.globalSignals.plotOffsetBottom },
axesOffsets: { x: axesOffsetX, y: axesOffsetY },
axesTitlePadding: facetLayout ? { x: axesTitlePaddingFacetX, y: axesTitlePaddingFacetY } : { x: axesTitlePaddingX, y: axesTitlePaddingY },
labelBaseline: { x: 'top', y: 'middle' },
specColumns,
specViewOptions,
axesScopes,
view: insight.view
});
}
//add mark to the final scope
if (finalScope.mark) {
const { update } = finalScope.mark.encode;
const outputDataName = 'output';
finalScope.mark.from.data = outputDataName;
addData(globalScope.markGroup,
{
name: outputDataName,
source: globalScope.markDataName,
transform: [
{
type: 'formula',
expr: finalScope.offsets.x,
as: FieldNames.OffsetX
},
{
type: 'formula',
expr: finalScope.offsets.y,
as: FieldNames.OffsetY
}
]
}
);
update.x = {
field: FieldNames.OffsetX
};
update.y = {
field: FieldNames.OffsetY
};
allEncodingRules.forEach(map => {
for (let key in map) {
if (update[key]) {
let arrIn = map[key];
if (!Array.isArray(update[key])) {
let value = update[key];
let arrOut = [];
update[key] = arrOut;
arrIn.forEach(rule => arrOut.push(rule));
arrOut.push(value);
} else {
let arrOut = update[key] as {}[];
arrIn.forEach(rule => arrOut.unshift(rule));
}
}
}
});
if (specResult) {
return specResult;
}
if (allGlobalScales.length > 0) {
const plotHeightOut = this.globalSignals.plotHeightOut.name;
const plotWidthOut = this.globalSignals.plotWidthOut.name;
const colTitleScale: LinearScale = {
type: 'linear',
name: 'scale_facet_col_title',
domain: [0, 1],
range: [0, { signal: plotWidthOut }]
};
const rowTitleScale: LinearScale = {
type: 'linear',
name: 'scale_facet_row_title',
domain: [0, 1],
range: [{ signal: plotHeightOut }, 0]
};
let axesScopes: AxesScopeMap = facetLayout ?
addFacetAxesGroupMarks({
globalScope: globalScope.scope,
plotScope: groupMark,
facetScope: firstScope,
colTitleScale,
rowTitleScale,
colSeqName: 'data_FacetCellColTitles',
rowSeqName: 'data_FacetCellRowTitles'
})
:
{
main: [{
scope: groupMark,
lines: true,
labels: true,
title: true
}]
};
addGlobalAxes({
globalScope,
allGlobalScales,
axisScales: this.props.axisScales,
plotOffsetSignals: { x: this.globalSignals.plotOffsetLeft, y: this.globalSignals.plotOffsetBottom },
axesOffsets: { x: axesOffsetX, y: axesOffsetY },
axesTitlePadding: facetLayout ? { x: axesTitlePaddingFacetX, y: axesTitlePaddingFacetY } : { x: axesTitlePaddingX, y: axesTitlePaddingY },
labelBaseline: { x: 'top', y: 'middle' },
specColumns,
specViewOptions,
axesScopes
});
}
//add mark to the final scope
if (finalScope.mark) {
const { update } = finalScope.mark.encode;
const outputDataName = 'output';
finalScope.mark.from.data = outputDataName;
addData(globalScope.markGroup,
{
name: outputDataName,
source: globalScope.markDataName,
transform: [
{
type: 'formula',
expr: finalScope.offsets.x,
as: FieldNames.OffsetX
},
{
type: 'formula',
expr: finalScope.offsets.y,
as: FieldNames.OffsetY
}
]
}
);
update.x = {
field: FieldNames.OffsetX
};
update.y = {
field: FieldNames.OffsetY
};
allEncodingRules.forEach(map => {
for (let key in map) {
if (update[key]) {
let arrIn = map[key];
if (!Array.isArray(update[key])) {
let value = update[key];
let arrOut = [];
update[key] = arrOut;
arrIn.forEach(rule => arrOut.push(rule));
arrOut.push(value);
} else {
let arrOut = update[key] as {}[];
arrIn.forEach(rule => arrOut.unshift(rule));
}
}
}
});
update.fill = fill(specContext, topColorField, ScaleNames.Color);
update.opacity = opacity(specContext);
}
return {
specCapabilities,
vegaSpec
};
update.fill = fill(specContext, topColorField, ScaleNames.Color);
update.opacity = opacity(specContext);
}
return {
specCapabilities,
vegaSpec
};
}
private initSpec(dataName: string) {
const { globalSignals } = this;
const { minCellWidth, minCellHeight, plotOffsetLeft, plotOffsetBottom, plotOffsetTop, plotOffsetRight, plotHeightOut, plotWidthOut } = globalSignals;
const { specContext } = this.props;
const { specContext } = this;
const { insight } = specContext;
const groupMark: GroupMark = {
type: 'group',
@ -410,11 +396,12 @@ export class SpecBuilder {
}
private createLayout(layoutPair: LayoutPair, buildProps: LayoutBuildProps) {
const { layoutClass, props } = layoutPair;
const { layoutType, props } = layoutPair;
const layoutBuildProps: LayoutProps & LayoutBuildProps = {
...props,
...buildProps
};
const layoutClass = layoutClasses[layoutType];
const layout = new layoutClass(layoutBuildProps);
layout.id = buildProps.id;
return layout;

Просмотреть файл

@ -353,6 +353,11 @@ export interface SpecColumns {
facetV?: Column;
}
export interface ZAxisOptions {
showZAxis?: boolean;
zIndex?: number;
}
export interface SpecViewOptions {
/**
@ -370,7 +375,9 @@ export interface SpecViewOptions {
*/
maxLegends: number;
tickSize: number
tickSize: number;
zAxisOptions?: ZAxisOptions;
}
export interface SpecContext {

Просмотреть файл

@ -1,19 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { SignalNames } from './constants';
import { GlobalScope } from './globalScope';
import { linearScale, pointScale } from './scales';
import { addScales } from './scope';
import { Column } from '@msrvida/chart-types';
import { RangeScheme } from 'vega-typings';
export function addZScale(z: Column, zSize: string, globalScope: GlobalScope, zScaleName: string) {
export function addZScale(z: Column, zSize: string, dataName: string, zScaleName: string) {
if (z) {
const zRange: RangeScheme = [0, { signal: `(${zSize}) * ${SignalNames.ZProportion}` }];
addScales(globalScope.scope, z.quantitative
const scale = z.quantitative
?
linearScale(zScaleName, globalScope.data.name, z.name, zRange, false, true)
linearScale(zScaleName, dataName, z.name, zRange, false, true)
:
pointScale(zScaleName, globalScope.data.name, zRange, z.name, false));
pointScale(zScaleName, dataName, zRange, z.name, false)
;
return scale;
}
}

Просмотреть файл

@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { build, getColumnsFromData, getSpecColumns, Insight, SpecContext, SpecResult, SpecViewOptions } from '../dist/es6';
import { build, getColumnsFromData, getSpecColumns, Insight, SpecContext, SpecViewOptions } from '../src/index';
import { Column } from '@msrvida/chart-types';
import * as Vega from 'vega-typings';

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "@msrvida/sanddance",
"version": "3.1.1",
"version": "3.2.0",
"description": "SandDance visualization canvas component.",
"main": "dist/es6/index.js",
"types": "dist/es6/index.d.ts",

Просмотреть файл

@ -30,6 +30,7 @@ export function recolorAxes(stage: VegaDeckGl.types.Stage, oldColors: SpecColorS
let axes: {
x: VegaDeckGl.types.Axis[];
y: VegaDeckGl.types.Axis[];
z: VegaDeckGl.types.Axis[];
};
let textData: VegaDeckGl.types.VegaTextLayerDatum[];
@ -38,7 +39,8 @@ export function recolorAxes(stage: VegaDeckGl.types.Stage, oldColors: SpecColorS
const textColor = VegaDeckGl.util.colorFromString(newColors.axisText || oldColors.axisText);
axes = {
x: cloneAxis(stage.axes.x, lineColor, textColor),
y: cloneAxis(stage.axes.y, lineColor, textColor)
y: cloneAxis(stage.axes.y, lineColor, textColor),
z: cloneAxis(stage.axes.z, lineColor, textColor)
};
}

Просмотреть файл

@ -16,11 +16,11 @@ export class CharacterSet {
if (!this.chars) {
const map: { [char: string]: true } = {};
const addText = (text: string) => {
Array.from(text).forEach(char => { map[char] = true });
Array.from(text).forEach(char => { map[char] = true; });
};
stage.textData.forEach(t => addText(t.text));
const { x, y } = stage.axes;
[x, y].forEach(axes => {
const { x, y, z } = stage.axes;
[x, y, z].forEach(axes => {
axes.forEach(axis => {
if (axis.tickText) axis.tickText.forEach(t => addText(t.text));
if (axis.title) addText(axis.title.text);

Просмотреть файл

@ -55,6 +55,8 @@ import { CharacterSet } from './characterSet';
const { defaultView } = VegaDeckGl.defaults;
const zAxisZindex = 1010;
let didRegisterColorSchemes = false;
/**
@ -280,7 +282,17 @@ export class Viewer {
private async renderNewLayout(signalValues: SignalValues, presenterConfig?: VegaDeckGl.types.PresenterConfig, view?: View) {
const currData = this._dataScope.currentData();
const context: SpecContext = { specColumns: this.getSpecColumnsWithFilteredStats(), insight: this.insight, specViewOptions: this.options };
const context: SpecContext = {
specColumns: this.getSpecColumnsWithFilteredStats(),
insight: this.insight,
specViewOptions: {
...this.options,
zAxisOptions: {
showZAxis: true,
zIndex: zAxisZindex
}
}
};
const specResult = build(context, currData);
if (!specResult.errors) {
const uiValues = extractSignalValuesFromView(this.vegaViewGl, this.vegaSpec);
@ -312,7 +324,7 @@ export class Viewer {
this.vegaSpec.signals.forEach(s => {
this.vegaViewGl.addSignalListener(s.name, handler);
})
});
//capture new color color contexts via signals
this.configForSignalCapture(config.presenterConfig);
@ -622,6 +634,7 @@ export class Viewer {
private createConfig(c?: VegaDeckGl.types.PresenterConfig): VegaDeckGl.types.ViewGlConfig {
const { getTextColor, getTextHighlightColor, getTextHighlightAlphaCutoff, onTextClick } = this.options;
const defaultPresenterConfig: VegaDeckGl.types.PresenterConfig = {
zAxisZindex,
getCharacterSet: stage => this._characterSet.getCharacterSet(stage),
getTextColor,
getTextHighlightColor,
@ -679,7 +692,7 @@ export class Viewer {
if (this.options.onBeforeCreateLayers) {
defaultPresenterConfig.preLayer = stage => {
this.options.onBeforeCreateLayers(stage, this.specCapabilities);
}
};
}
const config: VegaDeckGl.types.ViewGlConfig = {
presenter: this.presenter,

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "@msrvida/vega-deck.gl",
"version": "3.2.1",
"version": "3.3.0",
"description": "Deck.gl renderer for Vega",
"main": "dist/es6/index.js",
"files": [

Просмотреть файл

@ -50,7 +50,8 @@ export function createStage(view: View) {
polygonData: [],
axes: {
x: [],
y: []
y: [],
z: [],
},
gridLines: [],
textData: [],

Просмотреть файл

@ -30,11 +30,14 @@ export interface TickText extends VegaTextLayerDatum {
value: number | string;
}
export type AxisRole = 'x' | 'y' | 'z';
export interface Axis {
domain: StyledLine;
ticks: StyledLine[];
tickText: TickText[];
title?: VegaTextLayerDatum;
role?: AxisRole;
}
/**
@ -69,7 +72,7 @@ export interface Path {
}
export interface Polygon {
positions: Position[];
positions: Position[];
strokeColor: RGBAColor;
fillColor: RGBAColor;
strokeWidth: number;
@ -102,6 +105,7 @@ export interface Stage {
axes?: {
x?: Axis[];
y?: Axis[];
z?: Axis[];
};
textData?: VegaTextLayerDatum[];
view?: View;
@ -171,6 +175,7 @@ export interface PresenterConfig {
onSceneRectAssignCubeOrdinal?: (d: object) => number | undefined;
onTargetViewState?: (height: number, width: number) => { height: number, width: number, newViewStateTarget?: boolean };
preserveDrawingBuffer?: boolean;
zAxisZindex?: number;
}
export interface PresenterStyle {

Просмотреть файл

@ -32,10 +32,10 @@ export function getLayers(
guideLines: StyledLine[]
): Layer<any>[] {
const cubeLayer = newCubeLayer(presenter, config, stage.cubeData, presenter.style.highlightColor, lightSettings, lightingMix, interpolator);
const { x, y } = stage.axes;
const { x, y, z } = stage.axes;
const lines = concat(stage.gridLines, guideLines);
const texts = [...stage.textData];
[x, y].forEach(axes => {
[x, y, z].forEach(axes => {
axes.forEach(axis => {
if (axis.domain) lines.push(axis.domain);
if (axis.ticks) lines.push.apply(lines, axis.ticks);

Просмотреть файл

@ -2,13 +2,14 @@
// Licensed under the MIT license.
import { Axis, Stage } from '../interfaces';
import { RGBAColor } from '@deck.gl/core/utils/color';
import { Scene, SceneGroup } from 'vega-typings';
import { Mark, Orient, Scene, SceneGroup } from 'vega-typings';
export enum GroupType {
none = 0,
legend = 1,
xAxis = 2,
yAxis = 3
yAxis = 3,
zAxis = 4,
}
export interface MarkStagerOptions {
@ -16,12 +17,14 @@ export interface MarkStagerOptions {
currAxis: Axis;
defaultCubeColor: RGBAColor;
assignCubeOrdinal: (d: object) => number | undefined;
zAxisZindex: number;
}
//TODO - use vega-typings below
export type SceneGroup2 = SceneGroup & {
export type AxisSceneGroup = SceneGroup & {
datum?: any;
orient?: 'bottom' | 'left' | 'right' | 'top';
orient?: Orient;
mark: Mark
};
export interface LabelDatum {
value: any;

Просмотреть файл

@ -6,6 +6,7 @@ import { GroupType, MarkStager, MarkStagerOptions } from './interfaces';
import { lineZ } from '../defaults';
import { Scene, SceneLine } from 'vega-typings';
import { Stage, StyledLine } from '../interfaces';
import { zSwap } from '../zaxis';
const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scene, x: number, y: number, groupType: GroupType) => {
@ -19,8 +20,16 @@ const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene:
const lineItem = styledLine(x1 + x, y1 + y, x2 + x, y2 + y, item.stroke, item.strokeWidth);
if (item.mark.role === 'axis-tick') {
if (options.currAxis.role === 'z') {
zSwap(lineItem.sourcePosition);
zSwap(lineItem.targetPosition);
}
options.currAxis.ticks.push(lineItem);
} else if (item.mark.role === 'axis-domain') {
if (options.currAxis.role === 'z') {
zSwap(lineItem.sourcePosition);
zSwap(lineItem.targetPosition);
}
options.currAxis.domain = lineItem;
} else {
stage.gridLines.push(lineItem);

Просмотреть файл

@ -16,6 +16,7 @@ import {
SceneTextBaseline
} from 'vega-typings';
import { Stage, TickText, VegaTextLayerDatum } from '../interfaces';
import { zSwap } from '../zaxis';
interface SceneText2 extends SceneText {
metaData?: any;
@ -49,8 +50,14 @@ const markStager: MarkStager = (options: MarkStagerOptions, stage: Stage, scene:
if (item.mark.role === 'axis-label') {
const tickText = textItem as TickText;
tickText.value = (item.datum as LabelDatum).value;
if (options.currAxis.role === 'z') {
zSwap(tickText.position);
}
options.currAxis.tickText.push(tickText);
} else if (item.mark.role === 'axis-title') {
if (options.currAxis.role === 'z') {
zSwap(textItem.position);
}
options.currAxis.title = textItem;
} else {
stage.textData.push(textItem);

Просмотреть файл

@ -160,7 +160,8 @@ export class Presenter {
maxOrdinal: 0,
currAxis: null,
defaultCubeColor: this.style.defaultCubeColor,
assignCubeOrdinal: (config && config.onSceneRectAssignCubeOrdinal) || (() => options.maxOrdinal++)
assignCubeOrdinal: config?.onSceneRectAssignCubeOrdinal || (() => options.maxOrdinal++),
zAxisZindex: config?.zAxisZindex
};
//determine if this is a vega scene
if (scene.marktype) {
@ -181,7 +182,7 @@ export class Presenter {
let glOptions: WebGLContextAttributes;
if (config && config.preserveDrawingBuffer) {
glOptions = { preserveDrawingBuffer: true }
glOptions = { preserveDrawingBuffer: true };
}
const deckProps: DeckGLInternalProps = {

Просмотреть файл

@ -9,6 +9,7 @@ import area from './marks/area';
import {
Axis,
AxisRole,
FacetRect,
Stage,
StyledLine
@ -20,30 +21,33 @@ import {
GroupType,
MarkStager,
MarkStagerOptions,
SceneGroup2
AxisSceneGroup
} from './marks/interfaces';
import { Scene } from 'vega-typings';
import { Orient, Scene, SceneGroup } from 'vega-typings';
interface VegaAxisDatum {
domain: boolean;
grid: boolean;
labels: boolean;
orient: 'bottom' | 'left' | 'right' | 'top';
orient: Orient;
ticks: boolean;
title: boolean;
}
function getOrientItem(group: SceneGroup2): { orient?: 'bottom' | 'left' | 'right' | 'top'; } {
function getOrientItem(group: AxisSceneGroup): { orient?: Orient; } {
if (group.orient) {
return group;
}
return group.datum as VegaAxisDatum;
}
function convertGroupRole(group: SceneGroup2): GroupType {
function convertGroupRole(group: SceneGroup, options: MarkStagerOptions): GroupType {
if (group.mark.role === 'legend') return GroupType.legend;
if (group.mark.role === 'axis') {
const orientItem = getOrientItem(group);
if (((group as AxisSceneGroup).mark).zindex === options.zAxisZindex && options.zAxisZindex !== undefined) {
return GroupType.zAxis;
}
const orientItem = getOrientItem(group as AxisSceneGroup);
if (orientItem) {
switch (orientItem.orient) {
case 'bottom':
@ -59,7 +63,7 @@ function convertGroupRole(group: SceneGroup2): GroupType {
const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scene, x: number, y: number, groupType: GroupType) => {
base.vega.sceneVisit(scene, function (g: SceneGroup2) {
base.vega.sceneVisit(scene, function (g: SceneGroup) {
const gx = g.x || 0, gy = g.y || 0;
if (g.context && g.context.background && !stage.backgroundColor) {
@ -73,7 +77,7 @@ const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scen
stage.facets.push(facetRect);
}
groupType = convertGroupRole(g) || groupType;
groupType = convertGroupRole(g, options) || groupType;
setCurrentAxis(options, stage, groupType);
// draw group contents
@ -86,12 +90,19 @@ const group: MarkStager = (options: MarkStagerOptions, stage: Stage, scene: Scen
function setCurrentAxis(options: MarkStagerOptions, stage: Stage, groupType: GroupType) {
let axes: Axis[];
let role: AxisRole;
switch (groupType) {
case GroupType.xAxis:
axes = stage.axes.x;
role = 'x';
break;
case GroupType.yAxis:
axes = stage.axes.y;
role = 'y';
break;
case GroupType.zAxis:
axes = stage.axes.z;
role = 'z';
break;
default:
return;
@ -99,7 +110,8 @@ function setCurrentAxis(options: MarkStagerOptions, stage: Stage, groupType: Gro
options.currAxis = {
domain: null,
tickText: [],
ticks: []
ticks: [],
role
};
axes.push(options.currAxis);
}

Просмотреть файл

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Position } from '@deck.gl/core';
import { lineZ } from './defaults';
import { Vec3 } from './interfaces';
export function zSwap(v3: Vec3 | Position) {
let temp = -v3[1]; //negeative y to positive z
if(v3[0]===lineZ) {
v3[0] = 0;
}
v3[1] = v3[2];
v3[2] = temp;
}

Просмотреть файл

@ -70,7 +70,7 @@ export function unitizeBar(inputSpec: TopLevelUnitSpec, outputSpec: Vega.Spec, u
data0.source = 'source_00';
delete data0Url.url;
delete data0.format;
delete data0Url.format;
if (info.quantitativeBand) {
bandBinTransform = findBinTransform(data0, info.bandEncoding.field).transform;