diff --git a/common/changes/@visactor/vgrammar-core/perf-move-enable-segments-to-spec_2023-11-21-09-33.json b/common/changes/@visactor/vgrammar-core/perf-move-enable-segments-to-spec_2023-11-21-09-33.json new file mode 100644 index 000000000..d95365b66 --- /dev/null +++ b/common/changes/@visactor/vgrammar-core/perf-move-enable-segments-to-spec_2023-11-21-09-33.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "perf: move `enableSegments` to spec\n\n", + "type": "none", + "packageName": "@visactor/vgrammar-core" + } + ], + "packageName": "@visactor/vgrammar-core", + "email": "dingling112@gmail.com" +} \ No newline at end of file diff --git a/docs/dev-demos/src/specs/area-mark.ts b/docs/dev-demos/src/specs/area-mark.ts index 63518acdd..4a10e33dd 100644 --- a/docs/dev-demos/src/specs/area-mark.ts +++ b/docs/dev-demos/src/specs/area-mark.ts @@ -4,10 +4,6 @@ export const spec = { padding: 5, signals: [ - { - id: 'enableSegments', - value: true - }, { id: 'defined', value: true @@ -119,12 +115,12 @@ export const spec = { // ease: "linear" } }, + enableSegments: true, encode: { enter: { // stroke: '#652c90' }, update: { - enableSegments: { signal: 'enableSegments' }, x: { scale: 'xscale', field: 'u' }, y1: { scale: 'yscale', value: 0 }, y: { scale: 'yscale', field: 'v' }, @@ -164,11 +160,6 @@ export const spec = { }; export const binds = [ - { - id: 'enableSegments', - value: true, - bind: { input: 'checkbox' } - }, { id: 'defined', value: true, diff --git a/docs/dev-demos/src/specs/line-chart-segments.ts b/docs/dev-demos/src/specs/line-chart-segments.ts index c5530ea5d..cb02bb20d 100644 --- a/docs/dev-demos/src/specs/line-chart-segments.ts +++ b/docs/dev-demos/src/specs/line-chart-segments.ts @@ -76,6 +76,7 @@ export const spec = { { type: 'line', id: 'line', + enableSegments: true, // animationState: 'appear', animation: { enter: { @@ -108,7 +109,6 @@ export const spec = { return []; }, lineWidth: 2, - enableSegments: true }, update: { // interpolate: { signal: 'interpolate' }, diff --git a/docs/dev-demos/src/specs/line-mark.ts b/docs/dev-demos/src/specs/line-mark.ts index 4479c5116..2497667e7 100644 --- a/docs/dev-demos/src/specs/line-mark.ts +++ b/docs/dev-demos/src/specs/line-mark.ts @@ -4,11 +4,6 @@ export const spec = { padding: 5, signals: [ - { - id: 'enableSegments', - value: true, - bind: { input: 'checkbox' } - }, { id: 'defined', value: true, @@ -149,12 +144,12 @@ export const spec = { // ease: "linear" } }, + enableSegments: true, encode: { enter: { stroke: '#652c90' }, update: { - enableSegments: { signal: 'enableSegments' }, x: { scale: 'xscale', field: 'u' }, y: { scale: 'yscale', field: 'v' }, defined: { @@ -187,11 +182,6 @@ export const spec = { }; export const binds = [ - { - id: 'enableSegments', - value: true, - bind: { input: 'checkbox' } - }, { id: 'defined', value: true, diff --git a/docs/dev-demos/src/specs/simple-area-chart.ts b/docs/dev-demos/src/specs/simple-area-chart.ts index 7227bbfb0..6eee70f19 100644 --- a/docs/dev-demos/src/specs/simple-area-chart.ts +++ b/docs/dev-demos/src/specs/simple-area-chart.ts @@ -67,9 +67,9 @@ export const spec = { type: 'area', id: 'area', from: { data: 'table' }, + enableSegments: true, encode: { update: { - enableSegments: true, x: { scale: 'xscale', field: 'u' }, y: { scale: 'yscale', value: 0 }, y1: { scale: 'yscale', field: 'v' }, diff --git a/docs/site/assets/examples/en/basic-mark-line/segmental-line.md b/docs/site/assets/examples/en/basic-mark-line/segmental-line.md index 0e221c6fb..a2550a956 100644 --- a/docs/site/assets/examples/en/basic-mark-line/segmental-line.md +++ b/docs/site/assets/examples/en/basic-mark-line/segmental-line.md @@ -10,7 +10,7 @@ cover: /vgrammar/preview/basic-mark-line-segmental-line_0.7.6.png ## Key Configuration -- `enableSegments`: When the visual channel `enableSegments` of the line is set to `true`, VGrammar will calculate the visual channel encoding of each point. If there are differences, it will automatically create a segmented style curve. +- `enableSegments`: When the spec `enableSegments` of the line is set to `true`, VGrammar will calculate the visual channel encoding of each point. If there are differences, it will automatically create a segmented style curve. ## Code Demonstration @@ -140,6 +140,7 @@ const spec = { { type: 'line', from: { data: 'table' }, + enableSegments: true, encode: { enter: { lineWidth: 2 @@ -147,7 +148,6 @@ const spec = { update: { x: { scale: 'xscale', field: 'time' }, y: { scale: 'yscale', field: 'value' }, - enableSegments: true, stroke: (datum, element, params) => { return datum.value > 0 ? '#6690F2' : '#FF8F62'; } diff --git a/docs/site/assets/examples/zh/basic-mark-line/segmental-line.md b/docs/site/assets/examples/zh/basic-mark-line/segmental-line.md index b7ca28390..96b9e42fd 100644 --- a/docs/site/assets/examples/zh/basic-mark-line/segmental-line.md +++ b/docs/site/assets/examples/zh/basic-mark-line/segmental-line.md @@ -10,7 +10,7 @@ cover: /vgrammar/preview/basic-mark-line-segmental-line_0.7.6.png ## 关键配置 -- `enableSegments` 当线的视觉通道`enableSegments`设置为`true`,VGrammar 会计算各个点的视觉通道编码,如果有差异,自动创建分段样式的曲线 +- `enableSegments` 当线的`enableSegments`设置为`true`,VGrammar 会计算各个点的视觉通道编码,如果有差异,自动创建分段样式的曲线 ## 代码演示 @@ -140,6 +140,7 @@ const spec = { { type: 'line', from: { data: 'table' }, + enableSegments: true, encode: { enter: { lineWidth: 2 @@ -147,7 +148,6 @@ const spec = { update: { x: { scale: 'xscale', field: 'time' }, y: { scale: 'yscale', field: 'value' }, - enableSegments: true, stroke: (datum, element, params) => { return datum.value > 0 ? '#6690F2' : '#FF8F62'; } diff --git a/packages/vgrammar-core/__tests__/mark/group-encode.test.ts b/packages/vgrammar-core/__tests__/mark/group-encode.test.ts index b8f5ded09..99f5126c9 100644 --- a/packages/vgrammar-core/__tests__/mark/group-encode.test.ts +++ b/packages/vgrammar-core/__tests__/mark/group-encode.test.ts @@ -41,7 +41,6 @@ test('group encode of collection mark', () => { const mark = new (Mark as any)(mockView, 'line') as IMark; mark.encodeState('group', { - enableSegments: true, fill: (datum: any) => (datum.group === '0' ? 'red' : 'green'), stroke: 'black' }); @@ -67,7 +66,6 @@ test('group encode of collection mark', () => { expect(mark.elements[0].getGraphicAttribute('stroke')).toEqual('black'); expect(mark.elements[0].getGraphicAttribute('points')).toEqual([ { - enableSegments: true, context: 1, x: 1, y: 10 @@ -82,7 +80,6 @@ test('group encode of collection mark', () => { expect(mark.elements[1].getGraphicAttribute('stroke')).toEqual('black'); expect(mark.elements[1].getGraphicAttribute('points')).toEqual([ { - enableSegments: true, context: 1, x: 1, y: 20 diff --git a/packages/vgrammar-core/__tests__/mark/line.test.ts b/packages/vgrammar-core/__tests__/mark/line.test.ts index 63330cfc0..793ad4e62 100644 --- a/packages/vgrammar-core/__tests__/mark/line.test.ts +++ b/packages/vgrammar-core/__tests__/mark/line.test.ts @@ -45,3 +45,93 @@ test('addState to line', function () { }); expect(element.getGraphicAttribute('fillOpacity')).toBe(0.5); }); + +test('enableSegments is false of line', function () { + const encode = { + update: { + fill: (datum: any) => (datum.key <= 1 ? 'red' : 'green'), + x: (datum: any) => datum.key, + y: 10 + } + }; + const element = createSimpleElement('line', { + markSpec: { + encode, + enableSegments: false + } + }); + + element.updateData('key', [{ key: 0 }, { key: 1 }, { key: 2 }, { key: 3 }], 'key', {} as any); + element.initGraphicItem(); + + // encode enter + expect(element.getGraphicAttribute('points')).toEqual(undefined); + element.encodeItems(element.items, encode, false, {}); + element.encodeGraphic(); + expect(element.getGraphicAttribute('points')).toEqual([ + { x: 0, y: 10, context: 0, fill: 'red' }, + { x: 1, y: 10, context: 1 }, + { x: 2, y: 10, context: 2 }, + { x: 3, y: 10, context: 3 } + ]); + expect(element.getGraphicItem().attribute).toEqual({ + fill: 'red', + points: [ + { x: 0, y: 10, context: 0, fill: 'red' }, + { x: 1, y: 10, context: 1 }, + { x: 2, y: 10, context: 2 }, + { x: 3, y: 10, context: 3 } + ], + segments: null + }); +}); + +test('enableSegments is true', function () { + const encode = { + update: { + stroke: (datum: any) => (datum.key <= 1 ? 'red' : 'green'), + x: (datum: any) => datum.key, + y: 10 + } + }; + const element = createSimpleElement('line', { + markSpec: { + encode, + enableSegments: true + } + }); + + element.updateData('key', [{ key: 0 }, { key: 1 }, { key: 2 }, { key: 3 }], 'key', {} as any); + element.initGraphicItem(); + + // encode enter + expect(element.getGraphicAttribute('points')).toEqual(undefined); + element.encodeItems(element.items, encode, false, {}); + element.encodeGraphic(); + expect(element.getGraphicItem().attribute).toEqual({ + points: null, + segments: [ + { + context: 0, + points: [ + { x: 0, y: 10, context: 0, stroke: 'red' }, + { x: 1, y: 10, context: 1, stroke: 'red' } + ], + stroke: 'red', + x: 0, + y: 0 + }, + { + context: 2, + points: [ + { x: 2, y: 10, context: 2, stroke: 'green' }, + { x: 3, y: 10, context: 3, stroke: 'green' } + ], + stroke: 'green', + x: 0, + y: 0 + } + ], + stroke: 'red' + }); +}); diff --git a/packages/vgrammar-core/src/graph/attributes/transform.ts b/packages/vgrammar-core/src/graph/attributes/transform.ts index fcf6ce7a3..fd39e3c00 100644 --- a/packages/vgrammar-core/src/graph/attributes/transform.ts +++ b/packages/vgrammar-core/src/graph/attributes/transform.ts @@ -54,7 +54,7 @@ export const transformsByType: Record = { ], [GrammarMarkType.line]: [ { - channels: ['x', 'y', 'defined', 'enableSegments'], + channels: ['x', 'y', 'defined'], transform: (graphicAttributes: any, nextAttrs: any, storedAttrs: any) => { graphicAttributes.x = 0; graphicAttributes.y = 0; diff --git a/packages/vgrammar-core/src/graph/element.ts b/packages/vgrammar-core/src/graph/element.ts index fd56867a5..5dcb0dc48 100644 --- a/packages/vgrammar-core/src/graph/element.ts +++ b/packages/vgrammar-core/src/graph/element.ts @@ -211,10 +211,6 @@ export class Element implements IElement { encodeGraphic(attrs?: any) { this.coordinateTransformEncode(this.items); - if (!isNil(attrs?.enableSegments) && this.items?.[0]?.nextAttrs) { - this.items[0].nextAttrs.enableSegments = attrs.enableSegments; - } - const graphicAttributes = this.transformElementItems(this.items, this.mark.markType); if (attrs) { @@ -269,7 +265,7 @@ export class Element implements IElement { const updateEncoder = encoders[BuiltInEncodeNames.update]; const enterEncoder = encoders[BuiltInEncodeNames.enter]; const exitEncoder = encoders[BuiltInEncodeNames.exit]; - const onlyFullEncodeFirst = this.mark.isLargeMode(); + const onlyFullEncodeFirst = this.mark.isLargeMode() || (isCollectionMark && !this.mark.getSpec().enableSegments); if (this.diffState === DiffState.enter) { if (enterEncoder) { @@ -470,7 +466,7 @@ export class Element implements IElement { ) { const lastPoints = this.getGraphicAttribute('points', false); const lastSegments = this.getGraphicAttribute('segments', false); - const enableSegments = item.nextAttrs.enableSegments ?? this.getGraphicAttribute('enableSegments', false); + const enableSegments = this.mark.getSpec().enableSegments; const itemNextAttrs = items.map(item => item.nextAttrs); const isProgressive = this.mark.isProgressive(); nextAttrs = Object.assign({}, nextAttrs); diff --git a/packages/vgrammar-core/src/graph/glyph-element.ts b/packages/vgrammar-core/src/graph/glyph-element.ts index 456a58250..5d2f4dd2a 100644 --- a/packages/vgrammar-core/src/graph/glyph-element.ts +++ b/packages/vgrammar-core/src/graph/glyph-element.ts @@ -254,10 +254,9 @@ export class GlyphElement extends Element implements IGlyphElement { } private _generateGlyphItems(markType: MarkType, items: MarkElementItem[], additionalAttributes: any) { - const nextAttrs = items[0]?.nextAttrs; const glyphItems = items.map(item => Object.assign({}, item, { nextAttrs: additionalAttributes })); - if ((CollectionMarkType as string[]).includes(markType) && nextAttrs.enableSegments) { + if ((CollectionMarkType as string[]).includes(markType) && this.mark.getSpec().enableSegments) { // segment mark require all items to apply additional attributes glyphItems.forEach((glyphItem, index) => { glyphItem.nextAttrs = Object.assign({}, items[index].nextAttrs, additionalAttributes); diff --git a/packages/vgrammar-core/src/types/mark.ts b/packages/vgrammar-core/src/types/mark.ts index 5cd07628c..6cf371f57 100644 --- a/packages/vgrammar-core/src/types/mark.ts +++ b/packages/vgrammar-core/src/types/mark.ts @@ -251,6 +251,11 @@ export interface IMarkConfig { morphElementKey?: string; /** transforms of attributes */ attributeTransforms?: AttributeTransform[]; + /** + * only used in line/area mark + * TODO + */ + enableSegments?: boolean; } /** diff --git a/packages/vgrammar-core/src/view/mark.ts b/packages/vgrammar-core/src/view/mark.ts index 6ae78ddac..80fb725fd 100644 --- a/packages/vgrammar-core/src/view/mark.ts +++ b/packages/vgrammar-core/src/view/mark.ts @@ -480,7 +480,8 @@ export class Mark extends GrammarBase implements IMark { 'morphKey', 'morphElementKey', 'attributeTransforms', - 'skipTheme' + 'skipTheme', + 'enableSegments' ]; if (config === null) { keys.forEach(key => {