Skip to content

Commit

Permalink
feat: legend support constant scale
Browse files Browse the repository at this point in the history
  • Loading branch information
Aarebecca committed Feb 26, 2023
1 parent 47bc8b2 commit d7136b4
Show file tree
Hide file tree
Showing 62 changed files with 148 additions and 53 deletions.
Binary file modified __tests__/integration/snapshots/api/chart-auto-fit/step0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified __tests__/integration/snapshots/api/chart-auto-fit/step1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified __tests__/integration/snapshots/api/chartChangeSize.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified __tests__/integration/snapshots/api/chartRender.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified __tests__/integration/snapshots/api/markChangeData.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified __tests__/integration/snapshots/api/registerDataTransform.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified __tests__/integration/snapshots/static/athletesRectBin.png
Binary file modified __tests__/integration/snapshots/static/cars2PointJitterY.png
Binary file modified __tests__/integration/snapshots/static/flareTreemapCustom.png
Binary file modified __tests__/integration/snapshots/static/housePricePointShapes.png
Binary file modified __tests__/integration/snapshots/static/ordersLineMissingField.png
Binary file modified __tests__/integration/snapshots/static/penguinsPointFacetRect.png
Binary file modified __tests__/integration/snapshots/static/profitIntervalRange.png
26 changes: 0 additions & 26 deletions __tests__/plots/static/athletes-rect-bin-item-marker.ts

This file was deleted.

1 change: 0 additions & 1 deletion __tests__/plots/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ export { alphabetIntervalSpaceLayer } from './alphabet-interval-space-layer';
export { unemployment2RectBinX } from './unemployment2-rect-binx';
export { athletesRectBinX } from './athletes-rect-binx';
export { athletesRectBin } from './athletes-rect-bin';
export { athletesRectBinItemMarker } from './athletes-rect-bin-item-marker';
export { moviesPointBin } from './movies-point-bin';
export { moviesRectBin } from './movies-rect-bin';
export { moviesRectBinOpacity } from './movies-rect-bin-opacity';
Expand Down
22 changes: 17 additions & 5 deletions src/component/legendCategory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
inferComponentLayout,
titleContent,
scaleOf,
domainOf,
adaptor,
} from './utils';

Expand Down Expand Up @@ -90,10 +91,10 @@ function inferItemMarker(

const { itemMarker } = options;
if (shapeScale && !itemMarker) {
const { range } = shapeScale.getOptions();
const { domain } = shapeScale.getOptions();

return (d, i) =>
createShape(range[i], library, coordinate, theme, {
createShape(shapeScale.map(domain[i]), library, coordinate, theme, {
color: d.color,
});
}
Expand All @@ -115,6 +116,14 @@ function inferItemMarkerOpacity(scales: Scale[]) {
return undefined;
}

function inferItemMarkerSize(scales: Scale[]) {
const scale = scaleOf(scales, 'size');
// only support constant size scale.
// size in category legend means the marker radius.
if (scale) return scale.map(NaN) * 2;
return 8;
}

function inferCategoryStyle(
scales: Scale[],
options: LegendCategoryOptions,
Expand All @@ -126,6 +135,7 @@ function inferCategoryStyle(

const baseStyle = {
itemMarker: inferItemMarker(scales, options, library, coordinate, theme),
itemMarkerSize: inferItemMarkerSize(scales),
itemMarkerOpacity: inferItemMarkerOpacity(scales),
};

Expand All @@ -135,14 +145,15 @@ function inferCategoryStyle(
: labelFormatter;

// here must exists a color scale
const scale = scaleOf(scales, 'color');
const colorScale = scaleOf(scales, 'color');
const domain = domainOf(scales);

return {
...baseStyle,
data: scale.getOptions().domain.map((d) => ({
data: domain.map((d) => ({
id: d,
label: finalLabelFormatter(d),
color: scale.map(d),
color: colorScale.map(d),
})),
};
}
Expand Down Expand Up @@ -206,6 +217,7 @@ export const LegendCategory: GCC<LegendCategoryOptions> = (options) => {
...finalLayout,
},
});

layoutWrapper.appendChild(
new Category({
className: 'legend-category',
Expand Down
7 changes: 7 additions & 0 deletions src/component/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ export function scaleOf(scales: Scale[], type: string): Scale | undefined {
return scales.filter((s) => s.getOptions().name === type)?.[0];
}

export function domainOf(scales: Scale[]): any[] {
// to get a available scale's domain
return scales
.find((scale) => scale.getOptions().domain.length > 0)
.getOptions().domain;
}

export function adaptor(style: Record<string, any>) {
const newStyle: any = {
style: {},
Expand Down
87 changes: 70 additions & 17 deletions src/runtime/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
G2View,
} from './types/options';
import {
ConstantScale,
ContinuousScale,
DiscreteScale,
DistributionScale,
Expand Down Expand Up @@ -153,30 +154,74 @@ function inferLegendComponentType(
scales: G2ScaleOptions[],
coordinates: G2CoordinateOptions[],
) {
const scalesByField = group(scales, (d) => d.field || d.name);
const acceptScales = scales.filter((scale) =>
typeof scale.type === 'string'
? ['shape', 'size', 'color', 'opacity'].includes(scale.name)
: true,
);

// exclude the scales that all type are constant
const scalesByField = new Map(
Array.from(
group(acceptScales, (d) => d.field || '__internal_unset__'),
).filter(([_, scales]) =>
scales.some((scale) => scale.type !== 'constant'),
),
) as Map<string, G2ScaleOptions[]>;

if (scalesByField.size === 0) return [];

/**
*
* @param arr
* @param main necessary channel
* @returns
*/
const createStrategy = <T>(arr: T[], main: T): T[][] => {
const result = combine(arr);
result.forEach((c) => c.unshift(main));
result.push([main]);
return result.sort((a, b) => b.length - a.length);
};

const createCategoryStrategy = () => {
// category legend only support constant size
const color = [
['color', 'discrete'],
['color', 'constant'],
];
const shape = [
['shape', 'discrete'],
['shape', 'constant'],
];
const size = [['size', 'constant']];
const opacity = [
['opacity', 'discrete'],
['opacity', 'constant'],
];

const stg: [string, string][][] = [];
for (const cr of color) {
for (const sp of shape) {
for (const sz of size) {
for (const op of opacity) {
if (![cr, sp, sz, op].every((d) => d[1] === 'constant')) {
stg.push(
...(createStrategy([sp, sz, op], cr) as [string, string][][]),
);
}
}
}
}
}
// refactor above code

return stg.sort((a, b) => b.length - a.length);
};

// [legend type, [[channels, scale types]]][]
const strategy: [string, [string, string][][]][] = [
[
'legendCategory',
createStrategy(
[
// @todo now haven't provide constant scale of color channel,
// so won't display the single shape scale.
['shape', 'discrete'],
['size', 'discrete'],
['opacity', 'discrete'],
],
['color', 'discrete'],
),
],
['legendCategory', createCategoryStrategy()],
[
'legendContinuousSize',
[
Expand All @@ -185,10 +230,19 @@ function inferLegendComponentType(
['opacity', 'continuous'],
['size', 'continuous'],
],
[
['color', 'constant'],
['opacity', 'continuous'],
['size', 'continuous'],
],
[
['color', 'continuous'],
['size', 'continuous'],
],
[
['color', 'constant'],
['size', 'continuous'],
],
],
],
[
Expand All @@ -214,18 +268,18 @@ function inferLegendComponentType(
createStrategy([['opacity', 'continuous']], ['color', 'continuous']),
],
];

function getScaleType(scale: G2ScaleOptions): string {
const { type } = scale;
if (typeof type !== 'string') return null;
if (type in ContinuousScale) return 'continuous';
if (type in DiscreteScale) return 'discrete';
if (type in DistributionScale) return 'distribution';
if (type in ConstantScale) return 'constant';
return null;
}

const components = Array.from(scalesByField)
.map(([, scs]) => {
.map(([channel, scs]) => {
const combinations = combine(scs).sort((a, b) => b.length - a.length);
const options = combinations.map((combination) => ({
combination,
Expand Down Expand Up @@ -286,7 +340,6 @@ function inferComponentsType(
coordinates: G2CoordinateOptions[],
): [string | GCC, G2ScaleOptions[]][] {
const avaliableScales = scales.filter((scale) => isValidScale(scale));

return [
...inferLegendComponentType(avaliableScales, coordinates),
...inferAxisComponentType(avaliableScales, coordinates),
Expand Down
50 changes: 48 additions & 2 deletions src/runtime/plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import {
G2Library,
G2Mark,
G2MarkOptions,
G2ScaleOptions,
G2ShapeOptions,
G2ThemeOptions,
G2View,
Expand Down Expand Up @@ -486,14 +487,17 @@ function initializeState(
} = options;

const theme = useTheme(inferTheme(partialTheme));

// Infer components and compute layout.
const states = Array.from(markState.values());
const scales = Array.from(
new Set(states.flatMap((d) => d.channels.map((d) => d.scale))),
);

const components = inferComponent(Array.from(scales), options, library);
const components = inferComponent(
inferComponentScales(Array.from(scales), states, markState),
options,
library,
);
const layout = computeLayout(components, options);
const coordinate = createCoordinate(layout, options, library);
const framedStyle = frame
Expand Down Expand Up @@ -1383,6 +1387,48 @@ function applyClip(selection, clip?: boolean) {
});
}

function inferComponentScales(
scales: G2ScaleOptions[],
states: G2MarkState[],
markState: Map<G2Mark, G2MarkState>,
): G2ScaleOptions[] {
// add shape scale to state.

// for cell, omit shape scale.
// @todo support shape scale for cell.
for (const [key] of markState.entries()) {
if (key.type === 'cell') {
return scales.filter((scale) => scale.name !== 'shape');
}
}

// can't infer shape scale if there are multiple states.
if (states.length !== 1 || scales.some((scale) => scale.name === 'shape')) {
return scales;
}

const { defaultShape: shape } = states[0];
const acceptMarkTypes = ['point', 'line', 'rect', 'hollow'];
if (!acceptMarkTypes.includes(shape)) return scales;
const shapeMap = {
point: 'point',
line: 'hyphen',
rect: 'square',
hollow: 'hollow',
};

// create shape scale
const field = scales.find((scale) => scale.name === 'color')?.field || null;
const shapeScale = {
field,
name: 'shape',
type: 'constant',
domain: [],
range: [shapeMap[shape]],
};
return [...scales, shapeScale];
}

export function applyStyle(
selection: Selection,
style: Record<string, Primitive>,
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/types/component.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Coordinate, Transformation } from '@antv/coord';
import { DisplayObject, IAnimation as GAnimation } from '@antv/g';
import { G2ViewTree } from './options';
import {
G2Theme,
G2ViewInstance,
GuideComponentPosition,
GuideComponentOrientation,
GuideComponentPosition,
IndexedValue,
Layout,
Vector2,
} from './common';
import { DataComponent } from './data';
import { Encode, EncodeComponent } from './encode';
import { Mark, MarkComponent } from './mark';
import { G2ViewTree } from './options';
import { Transform, TransformComponent } from './transform';

export type G2ComponentNamespaces =
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/types/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ export const DiscreteScale = {
band: 'band',
point: 'point',
} as const;

export const ConstantScale = {
constant: 'constant',
} as const;

0 comments on commit d7136b4

Please sign in to comment.