diff --git a/__tests__/integration/snapshots/static/bodyPointScatterPlot.png b/__tests__/integration/snapshots/static/bodyPointScatterPlot.png new file mode 100644 index 0000000000..4602044c0b Binary files /dev/null and b/__tests__/integration/snapshots/static/bodyPointScatterPlot.png differ diff --git a/__tests__/integration/snapshots/static/moviesPointBin.png b/__tests__/integration/snapshots/static/moviesPointBin.png index d9b2235dbc..157b957816 100644 Binary files a/__tests__/integration/snapshots/static/moviesPointBin.png and b/__tests__/integration/snapshots/static/moviesPointBin.png differ diff --git a/__tests__/plots/static/body-point-scatter-plot.ts b/__tests__/plots/static/body-point-scatter-plot.ts new file mode 100644 index 0000000000..2cb5721205 --- /dev/null +++ b/__tests__/plots/static/body-point-scatter-plot.ts @@ -0,0 +1,19 @@ +import { G2Spec } from '../../../src'; + +export function bodyPointScatterPlot(): G2Spec { + return { + type: 'point', + data: { + type: 'fetch', + value: 'data/body.json', + }, + encode: { + x: 'height', + y: 'weight', + size: 'weight', + color: 'red', + }, + }; +} + +bodyPointScatterPlot.maxError = 100; diff --git a/__tests__/plots/static/index.ts b/__tests__/plots/static/index.ts index f2ae564e02..f065aa27ff 100644 --- a/__tests__/plots/static/index.ts +++ b/__tests__/plots/static/index.ts @@ -5,6 +5,7 @@ export { alphabetIntervalLabelContrastReverse } from './alphabet-interval-label- export { alphabetIntervalDataSort } from './alphabet-interval-data-sort'; export { alphabetIntervalFunnel } from './alphabet-interval-funnel'; export { alphabetIntervalPyramid } from './alphabet-interval-pyramid'; +export { bodyPointScatterPlot } from './body-point-scatter-plot'; export { gammaRandomLineSortXQuantitative } from './gamma-random-line-sortx-quantitative'; export { alphabetIntervalTransposed } from './alphabet-interval-transposed'; export { stateAgesIntervalStacked } from './stateages-interval-stacked'; diff --git a/src/component/constant.ts b/src/component/constant.ts new file mode 100644 index 0000000000..f3aada31d6 --- /dev/null +++ b/src/component/constant.ts @@ -0,0 +1,237 @@ +// [legend type, [channels, scale type][][]] +type InferStrategy = [string, [string, string][][]]; + +/** + * @examples + * ✅ + * color - `discrete`, shape - `constant` + * legendCategory.rule[27] is matched + * + * ❎ + * shape - `discrete`, size - `constant` + * There are no rules to match + * + */ +export const LEGEND_INFER_STRATEGIES: InferStrategy[] = [ + [ + 'legendCategory', + [ + [ + ['color', 'discrete'], + ['opacity', 'discrete'], + ['shape', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['opacity', 'constant'], + ['shape', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['opacity', 'discrete'], + ['shape', 'constant'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['opacity', 'constant'], + ['shape', 'constant'], + ['size', 'constant'], + ], + [ + ['color', 'constant'], + ['opacity', 'discrete'], + ['shape', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'constant'], + ['opacity', 'constant'], + ['shape', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'constant'], + ['opacity', 'discrete'], + ['shape', 'constant'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['shape', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['opacity', 'discrete'], + ['shape', 'discrete'], + ], + [ + ['color', 'discrete'], + ['opacity', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['opacity', 'constant'], + ['shape', 'discrete'], + ], + [ + ['color', 'discrete'], + ['opacity', 'constant'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['shape', 'constant'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['opacity', 'discrete'], + ['shape', 'constant'], + ], + [ + ['color', 'discrete'], + ['opacity', 'constant'], + ['shape', 'constant'], + ], + [ + ['color', 'constant'], + ['shape', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'constant'], + ['opacity', 'discrete'], + ['shape', 'discrete'], + ], + [ + ['color', 'constant'], + ['opacity', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'constant'], + ['opacity', 'constant'], + ['shape', 'discrete'], + ], + // [ + // ['color', 'constant'], + // ['opacity', 'constant'], + // ['size', 'constant'], + // ], + // [ + // ['color', 'constant'], + // ['shape', 'constant'], + // ['size', 'constant'], + // ], + [ + ['color', 'constant'], + ['opacity', 'discrete'], + ['shape', 'constant'], + ], + [ + ['color', 'discrete'], + ['shape', 'discrete'], + ], + [ + ['color', 'discrete'], + ['size', 'constant'], + ], + [ + ['color', 'discrete'], + ['opacity', 'discrete'], + ], + [ + ['color', 'discrete'], + ['opacity', 'constant'], + ], + [ + ['color', 'discrete'], + ['shape', 'constant'], + ], + [ + ['color', 'constant'], + ['shape', 'discrete'], + ], + [ + ['color', 'constant'], + ['size', 'constant'], + ], + [ + ['color', 'constant'], + ['opacity', 'discrete'], + ], + // [ + // ['color', 'constant'], + // ['opacity', 'constant'], + // ], + // [ + // ['color', 'constant'], + // ['shape', 'constant'], + // ], + [['color', 'discrete']], + // [['color', 'constant']], + ], + ], + [ + 'legendContinuousSize', + [ + [ + ['color', 'continuous'], + ['opacity', 'continuous'], + ['size', 'continuous'], + ], + [ + ['color', 'constant'], + ['opacity', 'continuous'], + ['size', 'continuous'], + ], + [ + ['color', 'continuous'], + ['size', 'continuous'], + ], + [ + ['color', 'constant'], + ['size', 'continuous'], + ], + ], + ], + [ + 'legendContinuousBlockSize', + [ + [ + ['color', 'distribution'], + ['opacity', 'distribution'], + ['size', 'distribution'], + ], + [ + ['color', 'distribution'], + ['size', 'distribution'], + ], + ], + ], + [ + 'legendContinuousBlock', + [ + [ + ['color', 'distribution'], + ['opacity', 'continuous'], + ], + [['color', 'distribution']], + ], + ], + [ + 'legendContinuous', + [ + [ + ['color', 'continuous'], + ['opacity', 'continuous'], + ], + [['color', 'continuous']], + ], + ], +]; diff --git a/src/component/legendContinuousBlockSize.ts b/src/component/legendContinuousBlockSize.ts index 8e0e29785a..efa9964139 100644 --- a/src/component/legendContinuousBlockSize.ts +++ b/src/component/legendContinuousBlockSize.ts @@ -1,14 +1,13 @@ import { GuideComponentComponent as GCC } from '../runtime'; import { LegendContinuous, LegendContinuousOptions } from './legendContinuous'; +import { LegendContinuousSize } from './legendContinuousSize'; export type LegendContinuousBlockSizeOptions = LegendContinuousOptions; export const LegendContinuousBlockSize: GCC< LegendContinuousBlockSizeOptions > = (options) => { - return LegendContinuous( - Object.assign({}, { type: 'size', block: true, tick: false }, options), - ); + return LegendContinuousSize(Object.assign({}, { block: true }, options)); }; LegendContinuousBlockSize.props = { diff --git a/src/component/legendContinuousSize.ts b/src/component/legendContinuousSize.ts index 2a415c873c..2f3c32aae5 100644 --- a/src/component/legendContinuousSize.ts +++ b/src/component/legendContinuousSize.ts @@ -1,4 +1,5 @@ import { GuideComponentComponent as GCC } from '../runtime'; +import { scaleOf } from './utils'; import { LegendContinuous, LegendContinuousOptions } from './legendContinuous'; export type LegendContinuousSizeOptions = LegendContinuousOptions; @@ -6,18 +7,26 @@ export type LegendContinuousSizeOptions = LegendContinuousOptions; export const LegendContinuousSize: GCC = ( options, ) => { - return LegendContinuous( - Object.assign( - {}, - { - type: 'size', - tick: false, - labelFilter: (datum, index, data) => - index === 0 || index === data.length - 1, - }, - options, - ), - ); + return (context) => { + const { scales } = context; + const sizeScale = scaleOf(scales, 'size'); + return LegendContinuous( + Object.assign( + {}, + { + type: 'size', + tick: false, + data: sizeScale.getOptions().domain.map((value, index) => ({ + value, + label: String(value), + })), + labelFilter: (datum, index, data) => + index === 0 || index === data.length - 1, + }, + options, + ), + )(context); + }; }; LegendContinuousSize.props = { diff --git a/src/runtime/component.ts b/src/runtime/component.ts index 7101887f22..9513c30d0b 100644 --- a/src/runtime/component.ts +++ b/src/runtime/component.ts @@ -12,6 +12,7 @@ import { } from '../coordinate'; import { combine } from '../utils/array'; import { defined } from '../utils/helper'; +import { LEGEND_INFER_STRATEGIES } from '../component/constant'; import { coordOf, isHelix, @@ -191,103 +192,6 @@ function inferLegendComponentType( if (scalesByField.size === 0) return []; - /** - * - * @param arr - * @param main necessary channel - * @returns - */ - const createStrategy = (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', createCategoryStrategy()], - [ - 'legendContinuousSize', - [ - [ - ['color', 'continuous'], - ['opacity', 'continuous'], - ['size', 'continuous'], - ], - [ - ['color', 'constant'], - ['opacity', 'continuous'], - ['size', 'continuous'], - ], - [ - ['color', 'continuous'], - ['size', 'continuous'], - ], - [ - ['color', 'constant'], - ['size', 'continuous'], - ], - ], - ], - [ - 'legendContinuousBlockSize', - [ - [ - ['color', 'distribution'], - ['opacity', 'distribution'], - ['size', 'distribution'], - ], - [ - ['color', 'distribution'], - ['size', 'distribution'], - ], - ], - ], - [ - 'legendContinuousBlock', - createStrategy([['opacity', 'continuous']], ['color', 'distribution']), - ], - [ - 'legendContinuous', - createStrategy([['opacity', 'continuous']], ['color', 'continuous']), - ], - ]; function getScaleType(scale: G2ScaleOptions): string { const { type } = scale; if (typeof type !== 'string') return null; @@ -308,7 +212,7 @@ function inferLegendComponentType( const sort = (arr: string[][]) => arr.sort((a, b) => a[0].localeCompare(b[0])); - for (const [componentType, accords] of strategy) { + for (const [componentType, accords] of LEGEND_INFER_STRATEGIES) { for (const { option, combination } of options) { if (accords.some((accord) => isEqual(sort(accord), sort(option)))) { return [componentType, combination] as [string, G2ScaleOptions[]];