This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch label-enhancement
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git


The following commit(s) were added to refs/heads/label-enhancement by this push:
     new 2b8da72  feat: improve label layouting. displayed by priority(default 
to be size of area).
2b8da72 is described below

commit 2b8da727e2edd9e61d65c3029cb13d17c9cecf8e
Author: pissang <bm2736...@gmail.com>
AuthorDate: Thu Apr 23 17:00:17 2020 +0800

    feat: improve label layouting. displayed by priority(default to be size of 
area).
---
 src/util/LabelManager.ts | 225 +++++++++++++++++++++++++++--------------------
 1 file changed, 128 insertions(+), 97 deletions(-)

diff --git a/src/util/LabelManager.ts b/src/util/LabelManager.ts
index b8b9055..0cdf2f3 100644
--- a/src/util/LabelManager.ts
+++ b/src/util/LabelManager.ts
@@ -28,8 +28,10 @@ import {
     LabelLayoutOptionCallbackParams
 } from './types';
 import { parsePercent } from './number';
-import SeriesModel from '../model/Series';
 import ChartView from '../view/Chart';
+import { ElementTextConfig } from 'zrender/src/Element';
+import { RectLike } from 'zrender/src/core/BoundingRect';
+import Transformable from 'zrender/src/core/Transformable';
 
 interface DisplayedLabelItem {
     label: ZRText
@@ -40,58 +42,49 @@ interface DisplayedLabelItem {
     transform: MatrixArray
 }
 
-interface LabelItem {
+interface LabelLayoutDesc {
     label: ZRText
     seriesIndex: number
     dataIndex: number
     layoutOption: LabelLayoutOptionCallback | LabelLayoutOption
-}
 
-interface LabelLayoutInnerConfig {
     overlap: LabelLayoutOption['overlap']
     overlapMargin: LabelLayoutOption['overlapMargin']
+
+    hostRect: RectLike
+    priority: number
+
+    defaultAttr: SavedLabelAttr
 }
 
 interface SavedLabelAttr {
-    x?: number
-    y?: number
-    rotation?: number
-    align?: ZRTextAlign
-    verticalAlign?: ZRTextVerticalAlign
-    width?: number
-    height?: number
-}
+    x: number
+    y: number
+    rotation: number
 
-function prepareLayoutCallbackParams(
-    label: ZRText,
-    dataIndex: number,
-    seriesIndex: number
-): LabelLayoutOptionCallbackParams {
-    const host = label.__hostTarget;
-    const labelTransform = label.getComputedTransform();
-    const labelRect = label.getBoundingRect().plain();
-    BoundingRect.applyTransform(labelRect, labelRect, labelTransform);
-    let x = 0;
-    let y = 0;
-    if (labelTransform) {
-        x = labelTransform[4];
-        y = labelTransform[5];
-    }
+    align: ZRTextAlign
+    verticalAlign: ZRTextVerticalAlign
+    width: number
+    height: number
 
-    let hostRect;
-    if (host) {
-        hostRect = host.getBoundingRect().plain();
-        const transform = host.getComputedTransform();
-        BoundingRect.applyTransform(hostRect, hostRect, transform);
-    }
+    // Configuration in attached element
+    attachedPos: ElementTextConfig['position']
+    attachedRot: ElementTextConfig['rotation']
 
+    rect: RectLike
+}
+
+function prepareLayoutCallbackParams(labelItem: LabelLayoutDesc): 
LabelLayoutOptionCallbackParams {
+    const labelAttr = labelItem.defaultAttr;
+    const label = labelItem.label;
     return {
-        dataIndex,
-        seriesIndex,
-        text: label.style.text,
-        rect: hostRect,
-        labelRect: labelRect,
-        x, y,
+        dataIndex: labelItem.dataIndex,
+        seriesIndex: labelItem.seriesIndex,
+        text: labelItem.label.style.text,
+        rect: labelItem.hostRect,
+        labelRect: labelAttr.rect,
+        x: labelAttr.x,
+        y: labelAttr.y,
         align: label.style.align,
         verticalAlign: label.style.verticalAlign
     };
@@ -99,20 +92,16 @@ function prepareLayoutCallbackParams(
 
 const LABEL_OPTION_TO_STYLE_KEYS = ['align', 'verticalAlign', 'width', 
'height'] as const;
 
-class LabelManager {
+const dummyTransformable = new Transformable();
 
-    private _labelList: LabelItem[] = [];
-    private _labelLayoutConfig: LabelLayoutInnerConfig[] = [];
+class LabelManager {
 
-    // Save default label attributes.
-    // For restore if developers want get back to default value in callback.
-    private _defaultLabelAttr: SavedLabelAttr[] = [];
+    private _labelList: LabelLayoutDesc[] = [];
 
     constructor() {}
 
     clearLabels() {
         this._labelList = [];
-        this._labelLayoutConfig = [];
     }
 
     /**
@@ -122,26 +111,59 @@ class LabelManager {
      * @param label
      * @param layoutOption
      */
-    addLabel(dataIndex: number, seriesIndex: number, label: ZRText, 
layoutOption: LabelItem['layoutOption']) {
+    addLabel(dataIndex: number, seriesIndex: number, label: ZRText, 
layoutOption: LabelLayoutDesc['layoutOption']) {
+        const labelStyle = label.style;
+        const hostEl = label.__hostTarget;
+        const textConfig = hostEl.textConfig || {};
+
+        const labelTransform = label.getComputedTransform();
+        const labelRect = label.getBoundingRect().plain();
+        BoundingRect.applyTransform(labelRect, labelRect, labelTransform);
+
+        dummyTransformable.setLocalTransform(labelTransform);
+
+        const host = label.__hostTarget;
+        let hostRect;
+        if (host) {
+            hostRect = host.getBoundingRect().plain();
+            const transform = host.getComputedTransform();
+            BoundingRect.applyTransform(hostRect, hostRect, transform);
+        }
+
         this._labelList.push({
             seriesIndex,
             dataIndex,
             label,
-            layoutOption
-        });
-        // Push an empty config. Will be updated in updateLayoutConfig
-        this._labelLayoutConfig.push({} as LabelLayoutInnerConfig);
+            layoutOption,
 
-        const labelStyle = label.style;
-        this._defaultLabelAttr.push({
-            x: label.x,
-            y: label.y,
-            rotation: label.rotation,
-            align: labelStyle.align,
-            verticalAlign: labelStyle.verticalAlign,
-            width: labelStyle.width,
-            height: labelStyle.height
+            hostRect,
+
+            overlap: 'hidden',
+            overlapMargin: 0,
+
+            // Label with lower priority will be hidden when overlapped
+            // Use rect size as default priority
+            priority: hostRect ? hostRect.width * hostRect.height : 0,
+
+            // Save default label attributes.
+            // For restore if developers want get back to default value in 
callback.
+            defaultAttr: {
+                x: dummyTransformable.x,
+                y: dummyTransformable.y,
+                rotation: dummyTransformable.rotation,
+
+                rect: labelRect,
+
+                align: labelStyle.align,
+                verticalAlign: labelStyle.verticalAlign,
+                width: labelStyle.width,
+                height: labelStyle.height,
+
+                attachedPos: textConfig.position,
+                attachedRot: textConfig.rotation
+            }
         });
+
     }
 
     addLabelsOfSeries(chartView: ChartView) {
@@ -168,12 +190,11 @@ class LabelManager {
             const labelItem = this._labelList[i];
             const label = labelItem.label;
             const hostEl = label.__hostTarget;
-            const layoutConfig = this._labelLayoutConfig[i];
-            const defaultLabelAttr = this._defaultLabelAttr[i];
+            const defaultLabelAttr = labelItem.defaultAttr;
             let layoutOption;
             if (typeof labelItem.layoutOption === 'function') {
                 layoutOption = labelItem.layoutOption(
-                    prepareLayoutCallbackParams(label, labelItem.dataIndex, 
labelItem.seriesIndex)
+                    prepareLayoutCallbackParams(labelItem)
                 );
             }
             else {
@@ -181,48 +202,51 @@ class LabelManager {
             }
 
             layoutOption = layoutOption || {};
-            // if (hostEl) {
-            //         // Ignore position and rotation config on the host el.
-            //     hostEl.setTextConfig({
-            //         position: null,
-            //         rotation: null
-            //     });
-            // }
-            // label.x = layoutOption.x != null
-            //     ? parsePercent(layoutOption.x, width)
-            //     // Restore to default value if developers don't given a 
value.
-            //     : defaultLabelAttr.x;
-
-            // label.y = layoutOption.y != null
-            //     ? parsePercent(layoutOption.y, height)
-            //     : defaultLabelAttr.y;
-
-            // label.rotation = layoutOption.rotation != null
-            //     ? layoutOption.rotation : defaultLabelAttr.rotation;
-
-            // label.x += layoutOption.dx || 0;
-            // label.y += layoutOption.dy || 0;
-
-            // for (let k = 0; k < LABEL_OPTION_TO_STYLE_KEYS.length; k++) {
-            //     const key = LABEL_OPTION_TO_STYLE_KEYS[k];
-            //     label.setStyle(key, layoutOption[key] != null ? 
layoutOption[key] : defaultLabelAttr[key]);
-            // }
-
-            layoutConfig.overlap = layoutOption.overlap;
-            layoutConfig.overlapMargin = layoutOption.overlapMargin;
+            if (hostEl) {
+                hostEl.setTextConfig({
+                    // Ignore position and rotation config on the host el if x 
or y is changed.
+                    position: (layoutOption.x != null || layoutOption.y != 
null)
+                        ? null : defaultLabelAttr.attachedPos,
+                    // Ignore rotation config on the host el if rotation is 
changed.
+                    rotation: layoutOption.rotation != null ? null : 
defaultLabelAttr.attachedRot,
+                    offset: [layoutOption.dx || 0, layoutOption.dy || 0]
+                });
+            }
+            label.x = layoutOption.x != null
+                ? parsePercent(layoutOption.x, width)
+                // Restore to default value if developers don't given a value.
+                : defaultLabelAttr.x;
+
+            label.y = layoutOption.y != null
+                ? parsePercent(layoutOption.y, height)
+                : defaultLabelAttr.y;
+
+            label.rotation = layoutOption.rotation != null
+                ? layoutOption.rotation : defaultLabelAttr.rotation;
+
+            for (let k = 0; k < LABEL_OPTION_TO_STYLE_KEYS.length; k++) {
+                const key = LABEL_OPTION_TO_STYLE_KEYS[k];
+                label.setStyle(key, layoutOption[key] != null ? 
layoutOption[key] : defaultLabelAttr[key]);
+            }
+
+            labelItem.overlap = layoutOption.overlap;
+            labelItem.overlapMargin = layoutOption.overlapMargin;
         }
     }
 
     layout() {
-        // TODO: sort by priority
+        // TODO: sort by priority(area)
         const labelList = this._labelList;
 
         const displayedLabels: DisplayedLabelItem[] = [];
         const mvt = new Point();
 
+        labelList.sort(function (a, b) {
+            return b.priority - a.priority;
+        });
+
         for (let i = 0; i < labelList.length; i++) {
             const labelItem = labelList[i];
-            const layoutConfig = this._labelLayoutConfig[i];
             const label = labelItem.label;
             const transform = label.getComputedTransform();
             // NOTE: Get bounding rect after getComputedTransform, or label 
may not been updated by the host el.
@@ -234,7 +258,7 @@ class LabelManager {
 
             let obb = isAxisAligned ? new OrientedBoundingRect(localRect, 
transform) : null;
             let overlapped = false;
-            const overlapMargin = layoutConfig.overlapMargin || 0;
+            const overlapMargin = labelItem.overlapMargin || 0;
             const marginSqr = overlapMargin * overlapMargin;
             for (let j = 0; j < displayedLabels.length; j++) {
                 const existsTextCfg = displayedLabels[j];
@@ -262,11 +286,18 @@ class LabelManager {
                 }
             }
 
+            // TODO Callback to determine if this overlap should be handled?
             if (overlapped) {
-                label.hide();
+                // label.setStyle({ opacity: 0.1 });
+                // label.z = 0;
+                // Use invisible instead of ignore because ignored label won't 
be updated in the host.
+                label.attr('invisible', true);
             }
             else {
-                label.show();
+                // TODO Restore z
+                // label.setStyle({ opacity: 1 });
+                label.attr('invisible', false);
+
                 displayedLabels.push({
                     label,
                     rect: globalRect,


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org
For additional commands, e-mail: commits-h...@echarts.apache.org

Reply via email to