Skip to content

Commit

Permalink
feat(size): support ordinal size channel (#5320)
Browse files Browse the repository at this point in the history
  • Loading branch information
pearmini authored Jul 17, 2023
1 parent 0c4fc99 commit 760eb75
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 22 deletions.
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.
22 changes: 22 additions & 0 deletions __tests__/plots/static/cars2-point-constant-color-size.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { G2Spec } from '../../../src';

// @todo Fix marker stroke of @antv/gui.
export function cars2PointConstantColorSize(): G2Spec {
return {
type: 'point',
padding: 'auto',
data: {
type: 'fetch',
value: 'data/cars2.csv',
},
encode: {
y: 'Miles_per_Gallon',
x: 'Horsepower',
size: 'Origin',
},
scale: {
x: { nice: true },
y: { nice: true },
},
};
}
21 changes: 21 additions & 0 deletions __tests__/plots/static/cars2-point-ordinal-size.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { G2Spec } from '../../../src';

// @todo Fix category item align in @antv/gui.
export function cars2PointOrdinalSize(): G2Spec {
return {
type: 'point',
padding: 'auto',
data: {
type: 'fetch',
value: 'data/cars2.csv',
},
encode: {
y: 'Miles_per_Gallon',
x: 'Horsepower',
color: 'Origin',
size: 'Origin',
shape: 'point',
},
scale: { x: { nice: true }, y: { nice: true } },
};
}
1 change: 1 addition & 0 deletions __tests__/plots/static/fuel-line-encode-propagate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function fuelLineEncodePropagate(): G2Spec {
},
x: { utc: true },
},
legend: { shape: false },
style: {
stroke: '#fff',
},
Expand Down
2 changes: 2 additions & 0 deletions __tests__/plots/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,5 @@ export { mockLineSmallInterval } from './mock-line-small-interval';
export { unemploymentAreaStackedLegendSize } from './unemployment-area-stacked-legend-size';
export { alphabetIntervalAutoPaddingSlider } from './alphabet-interval-auto-padding-slider';
export { countriesBubbleLegendSize } from './countries-bubble-legend-size';
export { cars2PointOrdinalSize } from './cars2-point-ordinal-size';
export { cars2PointConstantColorSize } from './cars2-point-constant-color-size';
12 changes: 7 additions & 5 deletions src/component/legendCategory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { DisplayObject } from '@antv/g';
import { Category } from '@antv/gui';
import { last } from '@antv/util';
import { format } from 'd3-format';
import { Identity, Point } from '@antv/scale';
import type {
FlexLayout,
G2Library,
Expand Down Expand Up @@ -127,9 +128,8 @@ function inferItemMarkerOpacity(scales: Scale[]) {

function inferItemMarkerSize(scales: Scale[], defaults: number) {
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;
if (scale instanceof Identity) return scale.map(NaN) * 2;
if (scale instanceof Point) return ({ id }) => scale.map(id) * 2;
return defaults;
}

Expand All @@ -150,16 +150,18 @@ function inferCategoryStyle(options, context: GuideComponentContext) {
? format(labelFormatter)
: labelFormatter;

// here must exists a color scale
const colorScale = scaleOf(scales, 'color');
const domain = domainOf(scales);
const colorOf = colorScale
? (d) => colorScale.map(d)
: () => context.theme.color;

return {
...baseStyle,
data: domain.map((d) => ({
id: d,
label: finalLabelFormatter(d),
color: colorScale.map(d),
color: colorOf(d),
})),
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/mark/point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Point.props = {
{ name: 'x', required: true },
{ name: 'y', required: true },
{ name: 'series', scale: 'band' },
{ name: 'size', scale: 'sqrt' },
{ name: 'size', quantitative: 'sqrt' },
{ name: 'dx', scale: 'identity' },
{ name: 'dy', scale: 'identity' },
],
Expand Down
25 changes: 24 additions & 1 deletion src/runtime/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,18 @@ function inferLegendComponentType(
combination,
option: combination.map((scale) => [scale.name, getScaleType(scale)]),
}));

// For category legend.
for (const { option, combination } of options) {
// If every scale is constant, do not display legend.
if (option.every((d) => d[1] === 'constant')) continue;
if (option.every((d) => d[1] === 'discrete' || d[1] === 'constant')) {
return ['legendCategory', combination] as [string, G2ScaleOptions[]];
}
}

// For reset legend.
// @todo Remove this.
for (const [componentType, accords] of LEGEND_INFER_STRATEGIES) {
for (const { option, combination } of options) {
if (accords.some((accord) => isEqual(sort(accord), sort(option)))) {
Expand Down Expand Up @@ -745,9 +757,20 @@ function computeCategoryLegendSize(
theme: G2Theme,
library: G2Library,
) {
const itemMakerSizeOf = () => {
const { itemMarkerSize } = component.style || {};
if (itemMarkerSize) return itemMarkerSize;
const { scales } = component;
const size = scales.find((d) => d.name === 'size');
if (!size) return itemMarkerSize;
return size.range[1] * 2;
};

const styleOf = () => {
const { legendCategory } = theme;
return deepMix({}, legendCategory, component.style);
return deepMix({}, legendCategory, component.style, {
itemMarkerSize: itemMakerSizeOf(),
});
};

const {
Expand Down
11 changes: 10 additions & 1 deletion src/runtime/mark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,14 @@ export async function initializeMark(
return false;
})
.flatMap((descriptor) => {
const { name, scale: scaleType, scaleKey, range } = descriptor;
const {
name,
scale: scaleType,
scaleKey,
range,
quantitative,
ordinal,
} = descriptor;
const valuesArray = nameChannels.filter(([channel]) =>
channel.startsWith(name),
);
Expand Down Expand Up @@ -106,6 +113,8 @@ export async function initializeMark(
type,
range: finalRange,
...scaleOptions,
quantitative,
ordinal,
},
};
});
Expand Down
43 changes: 29 additions & 14 deletions src/runtime/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ import { isTheta } from './coordinate';
import { useLibrary } from './library';
import { MarkChannel } from './types/mark';

export function inferScale(name, values, options, coordinates, theme, library) {
export function inferScale(
name: string,
values: Primitive[][],
options: Record<string, any>,
coordinates: G2CoordinateOptions[],
theme: G2Theme,
library: G2Library,
) {
const { guide = {} } = options;
const type = inferScaleType(name, values, options);
if (typeof type !== 'string') return options;
Expand Down Expand Up @@ -292,19 +299,19 @@ function inferScaleType(
values: Primitive[][],
options: G2ScaleOptions,
): string | ScaleComponent {
const { type, domain, range } = options;
const { type, domain, range, quantitative, ordinal } = options;
if (type !== undefined) return type;
if (isObject(values)) return 'identity';
if (typeof range === 'string') return 'linear';
if ((domain || range || []).length > 2) return asOrdinalType(name);
if ((domain || range || []).length > 2) return asOrdinalType(name, ordinal);
if (domain !== undefined) {
if (isOrdinal([domain])) return asOrdinalType(name);
if (isOrdinal([domain])) return asOrdinalType(name, ordinal);
if (isTemporal(values)) return 'time';
return asQuantitativeType(name, range);
return asQuantitativeType(name, range, quantitative);
}
if (isOrdinal(values)) return asOrdinalType(name);
if (isOrdinal(values)) return asOrdinalType(name, ordinal);
if (isTemporal(values)) return 'time';
return asQuantitativeType(name, range);
return asQuantitativeType(name, range, quantitative);
}

function inferScaleDomain(
Expand Down Expand Up @@ -361,8 +368,11 @@ function inferScaleRange(
return [rangeMin || r0, rangeMax || r1];
}
case 'band':
case 'point':
return [rangeMin || 0, rangeMax || 1];
case 'point': {
const min = name === 'size' ? 5 : 0;
const max = name === 'size' ? 10 : 1;
return [rangeMin || min, rangeMax || max];
}
case 'ordinal': {
return categoricalColors(values, options, domain, theme, library);
}
Expand Down Expand Up @@ -505,20 +515,25 @@ function inferPadding(
// The scale for enterDelay and enterDuration should has zero padding by default.
// Because there is no need to add extra delay for the start and the end.
if (name === 'enterDelay' || name === 'enterDuration') return 0;
if (type === 'band') {
return isTheta(coordinates) ? 0 : 0.1;
}
if (name === 'size') return 0;
if (type === 'band') return isTheta(coordinates) ? 0 : 0.1;
// Point scale need 0.5 padding to make interval between first and last point
// equal to other intervals in polar coordinate.
if (type === 'point') return 0.5;
return 0;
}

function asOrdinalType(name: string) {
function asOrdinalType(name: string, defaults: string) {
if (defaults) return defaults;
return isQuantitative(name) ? 'point' : 'ordinal';
}

function asQuantitativeType(name: string, range: Primitive[]) {
function asQuantitativeType(
name: string,
range: Primitive[],
defaults: string,
) {
if (defaults) return defaults;
if (name !== 'color') return 'linear';
return range ? 'linear' : 'sequential';
}
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export type ChannelValue = MaybeArray<Primitive>[];
export type Channel = {
name?: string;
scale?: string;
quantitative?: string;
ordinal?: string;
scaleName?: string;
required?: boolean;
value?: Primitive[];
Expand Down

0 comments on commit 760eb75

Please sign in to comment.