You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by ov...@apache.org on 2022/07/04 09:32:27 UTC

[echarts] branch fix-17198 created (now 0c594936a)

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

ovilia pushed a change to branch fix-17198
in repository https://gitbox.apache.org/repos/asf/echarts.git


      at 0c594936a fix(axis): add time ticks with larger levels first

This branch includes the following new commits:

     new a9a392966 fix(axis): add time axis special tick logic
     new 0c594936a fix(axis): add time ticks with larger levels first

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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


[echarts] 02/02: fix(axis): add time ticks with larger levels first

Posted by ov...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ovilia pushed a commit to branch fix-17198
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 0c594936a71351e73eadff90f55d491a772a2613
Author: Ovilia <zw...@gmail.com>
AuthorDate: Mon Jul 4 17:31:17 2022 +0800

    fix(axis): add time ticks with larger levels first
---
 src/coord/Axis.ts                 |   4 +-
 src/coord/axisTickLabelBuilder.ts |  53 ++++++++++++++++--
 test/timeScale-formatter.html     | 115 ++++++++++++++++++++++++++++++++++----
 3 files changed, 153 insertions(+), 19 deletions(-)

diff --git a/src/coord/Axis.ts b/src/coord/Axis.ts
index ebbfbb221..59eaa3122 100644
--- a/src/coord/Axis.ts
+++ b/src/coord/Axis.ts
@@ -193,7 +193,6 @@ class Axis {
             this, ticksCoords, alignWithLabel, opt.clamp
         );
 
-        console.log('ticksCoords', ticksCoords);
         return ticksCoords;
     }
 
@@ -222,7 +221,8 @@ class Axis {
     }
 
     getViewLabels(): ReturnType<typeof createAxisLabels>['labels'] {
-        return createAxisLabels(this).labels;
+        const labels = createAxisLabels(this).labels;
+        return labels;
     }
 
     getLabelModel(): Model<AxisBaseOption['axisLabel']> {
diff --git a/src/coord/axisTickLabelBuilder.ts b/src/coord/axisTickLabelBuilder.ts
index b56442573..9c6be6c30 100644
--- a/src/coord/axisTickLabelBuilder.ts
+++ b/src/coord/axisTickLabelBuilder.ts
@@ -32,6 +32,7 @@ import OrdinalScale from '../scale/Ordinal';
 import { AxisBaseModel } from './AxisBaseModel';
 import type Axis2D from './cartesian/Axis2D';
 import { TimeScaleTick } from '../util/types';
+import { BoundingRect } from 'zrender';
 
 type CacheKey = string | number;
 
@@ -167,23 +168,46 @@ function makeNonOverlappedTimeLabels(axis: Axis, onlyTick?: boolean) {
     const ticks = axis.scale.getTicks() as TimeScaleTick[];
     const ordinalScale = axis.scale as OrdinalScale;
     const labelFormatter = makeLabelFormatter(axis);
+    const font = axis.getLabelModel().getFont();
 
     const result: (MakeLabelsResultObj | number)[] = [];
+    const boundingRects: BoundingRect[] = [];
+
+    function isOverlap(rect: BoundingRect) {
+        /**
+         * `rotate` is not considered because for time axis,
+         * the interval is a suggestion value, not a precise value.
+         * So if there is no overlap without rotate, there should be
+         * no overlap with rotate and we don't have to make tick labels
+         * as condense as possible as in the case of category axes.
+         */
+        for (let i = 0; i < boundingRects.length; i++) {
+            if (rect.intersect(boundingRects[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-    function addItem(tickValue: number) {
-        const tickObj = { value: tickValue };
+    function addItem(tickValue: number, tickLevel: number) {
+        const tickObj = { value: tickValue, level: tickLevel };
         result.push(onlyTick
             ? tickValue
             : {
                 formattedLabel: labelFormatter(tickObj),
                 rawLabel: ordinalScale.getLabel(tickObj),  // TODO: ?
-                tickValue: tickValue
+                tickValue: tickValue,
+                level: tickLevel
             }
         );
     }
 
     let lastMaxLevel = Number.MAX_VALUE;
     let maxLevel;
+    /**
+     * Loop through the ticks with larger levels to smaller levels so that if
+     * the ticks are overlapped, we can use the level of the higher level.
+     */
     while (true) {
         maxLevel = -1;
 
@@ -200,9 +224,25 @@ function makeNonOverlappedTimeLabels(axis: Axis, onlyTick?: boolean) {
         for (let i = 0; i < ticks.length; i++) {
             const tick = ticks[i];
             if (tick.level === maxLevel) {
-                // Add the tick only if it has no overlap with current ones
-                 // TODO:
-                addItem(tick.value);
+                // Check if this tick is overlapped with added ticks
+                const rect = textContain.getBoundingRect(
+                    labelFormatter({
+                        value: tick.value,
+                        level: tick.level
+                    }),
+                    font,
+                    'center',
+                    'top'
+                );
+                // The same magic number as in calculateCategoryInterval
+                const padding = 0.15;
+                rect.x += axis.dataToCoord(tick.value) - rect.width * padding;
+                rect.width *= (1 + padding * 2);
+                if (!isOverlap(rect)) {
+                    // Add the tick only if it has no overlap with current ones
+                    addItem(tick.value, tick.level);
+                    boundingRects.push(rect);
+                }
             }
         }
 
@@ -443,6 +483,7 @@ interface MakeLabelsResultObj {
     formattedLabel: string
     rawLabel: string
     tickValue: number
+    level?: number
 }
 
 function makeLabelsByNumericCategoryInterval(axis: Axis, categoryInterval: number): MakeLabelsResultObj[];
diff --git a/test/timeScale-formatter.html b/test/timeScale-formatter.html
index 128548bf1..c139eab53 100644
--- a/test/timeScale-formatter.html
+++ b/test/timeScale-formatter.html
@@ -38,21 +38,12 @@ under the License.
 
 
         <div id="main0"></div>
-
-
         <div id="main1"></div>
-
-
         <div id="main2"></div>
-
-
         <div id="main3"></div>
-
-
         <div id="main4"></div>
-
-
         <div id="main5"></div>
+        <div id="main6"></div>
 
 
         <script>
@@ -360,6 +351,108 @@ under the License.
         </script>
 
 
+
+        <script>
+        require(['echarts'/*, 'map/js/china' */], function (echarts) {
+            var option = {
+                grid: {
+                    left: 100,
+                    width: 600
+                },
+                series: [
+                    {
+                        data: [
+                            [ '2022-02-11', '0' ], [ '2022-02-14', '0.005' ], [ '2022-02-15', '0.286' ], [ '2022-02-16', '0.462' ],
+                            [ '2022-02-17', '0.162' ], [ '2022-02-18', '0.033' ], [ '2022-02-21', '0.033' ], [ '2022-02-22', '0.128' ],
+                            [ '2022-02-23', '0.366' ], [ '2022-02-24', '0.104' ], [ '2022-02-25', '0.503' ], [ '2022-02-28', '1.015' ],
+                            [ '2022-03-01', '1.669' ], [ '2022-03-02', '2.689' ], [ '2022-03-03', '2.457' ], [ '2022-03-04', '2.893' ],
+                            [ '2022-03-07', '2.766' ], [ '2022-03-08', '3.595' ], [ '2022-03-09', '2.237' ], [ '2022-03-10', '1.988' ],
+                            [ '2022-03-11', '1.763' ], [ '2022-03-14', '0.519' ], [ '2022-03-15', '0.341' ], [ '2022-03-16', '0.935' ],
+                            [ '2022-03-17', '2.164' ], [ '2022-03-18', '2.457' ], [ '2022-03-21', '2.838' ], [ '2022-03-22', '2.861' ],
+                            [ '2022-03-23', '3.104' ], [ '2022-03-24', '3.158' ], [ '2022-03-25', '3.293' ], [ '2022-03-28', '2.364' ],
+                            [ '2022-03-29', '3.069' ], [ '2022-03-30', '3.033' ], [ '2022-03-31', '2.236' ], [ '2022-04-01', '2.507' ],
+                            [ '2022-04-04', '2.755' ], [ '2022-04-05', '1.842' ], [ '2022-04-06', '1.306' ], [ '2022-04-07', '1.201' ],
+                            [ '2022-04-08', '1.212' ], [ '2022-04-11', '0.738' ], [ '2022-04-12', '1.308' ], [ '2022-04-13', '2.167' ],
+                            [ '2022-04-14', '2.057' ], [ '2022-04-15', '2.057' ], [ '2022-04-18', '2.065' ], [ '2022-04-19', '1.85' ],
+                            [ '2022-04-20', '2.082' ], [ '2022-04-21', '1.307' ], [ '2022-04-22', '0.284' ], [ '2022-04-25', '0.04' ],
+                            [ '2022-04-26', '0.527' ], [ '2022-04-27', '0.577' ], [ '2022-04-28', '0.089' ], [ '2022-04-29', '0.836' ],
+                            [ '2022-05-02', '0.727' ], [ '2022-05-03', '0.647' ], [ '2022-05-04', '0.662' ], [ '2022-05-05', '0.493' ],
+                            [ '2022-05-06', '0.844' ], [ '2022-05-09', '2.828' ], [ '2022-05-10', '2.779' ], [ '2022-05-11', '3.038' ]
+                        ],
+                        type: 'line'
+                    }
+                ],
+                xAxis: {
+                    type: 'time',
+                    axisLabel: {
+                        rich: {
+                            monthStyle: {
+                                color: '#f00'
+                            }
+                        }
+                    }
+                },
+                yAxis: {
+                    type: 'value'
+                }
+            };
+
+            var chart = testHelper.create(echarts, 'main6', {
+                title: [
+                    'Callback formatter'
+                ],
+                option: option
+                // height: 300,
+                // buttons: [{text: 'btn-txt', onclick: function () {}}],
+                // recordCanvas: true,
+            });
+        });
+        </script>
+
+
+
+        <script>
+        require(['echarts'/*, 'map/js/china' */], function (echarts) {
+            var option;
+
+            const time = new Date('2010-01-01 00:00:00');
+            const data = [];
+            for (let i = 0; i < 30; ++i) {
+                data.push([time.getTime(), i * 10 + Math.random() * 100]);
+                time.setMonth(time.getMonth() + 1);
+            }
+
+            option = {
+                xAxis: {
+                    type: 'time',
+                    axisLabel: {
+                        formatter: function (time) {
+                            const date = new Date(time);
+                            return 'long text long text ' + date.getFullYear() + '-' + (date.getMonth() + 1);
+                        }
+                    }
+                },
+                yAxis: {
+                    type: 'value'
+                },
+                series: {
+                    type: 'line',
+                    data: data
+                }
+            };
+
+            var chart = testHelper.create(echarts, 'main6', {
+                title: [
+                    'Callback formatter'
+                ],
+                option: option
+                // height: 300,
+                // buttons: [{text: 'btn-txt', onclick: function () {}}],
+                // recordCanvas: true,
+            });
+        });
+        </script>
+
+
     </body>
 </html>
-


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


[echarts] 01/02: fix(axis): add time axis special tick logic

Posted by ov...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ovilia pushed a commit to branch fix-17198
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit a9a39296669299ed5951526bfe8c6cc2859296e7
Author: Ovilia <zw...@gmail.com>
AuthorDate: Mon Jul 4 15:04:01 2022 +0800

    fix(axis): add time axis special tick logic
---
 src/coord/Axis.ts                 |   1 +
 src/coord/axisTickLabelBuilder.ts | 112 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 113 insertions(+)

diff --git a/src/coord/Axis.ts b/src/coord/Axis.ts
index 7a334a0a4..ebbfbb221 100644
--- a/src/coord/Axis.ts
+++ b/src/coord/Axis.ts
@@ -193,6 +193,7 @@ class Axis {
             this, ticksCoords, alignWithLabel, opt.clamp
         );
 
+        console.log('ticksCoords', ticksCoords);
         return ticksCoords;
     }
 
diff --git a/src/coord/axisTickLabelBuilder.ts b/src/coord/axisTickLabelBuilder.ts
index fbc2e5c97..b56442573 100644
--- a/src/coord/axisTickLabelBuilder.ts
+++ b/src/coord/axisTickLabelBuilder.ts
@@ -31,6 +31,7 @@ import { AxisBaseOption } from './axisCommonTypes';
 import OrdinalScale from '../scale/Ordinal';
 import { AxisBaseModel } from './AxisBaseModel';
 import type Axis2D from './cartesian/Axis2D';
+import { TimeScaleTick } from '../util/types';
 
 type CacheKey = string | number;
 
@@ -72,6 +73,8 @@ export function createAxisLabels(axis: Axis): {
     // Only ordinal scale support tick interval
     return axis.type === 'category'
         ? makeCategoryLabels(axis)
+        : axis.type === 'time'
+        ? makeTimeLabels(axis)
         : makeRealNumberLabels(axis);
 }
 
@@ -90,6 +93,8 @@ export function createAxisTicks(axis: Axis, tickModel: AxisBaseModel): {
     // Only ordinal scale support tick interval
     return axis.type === 'category'
         ? makeCategoryTicks(axis, tickModel)
+        : axis.type === 'time'
+        ? makeTimeTicks(axis, tickModel)
         : {ticks: zrUtil.map(axis.scale.getTicks(), tick => tick.value) };
 }
 
@@ -102,6 +107,15 @@ function makeCategoryLabels(axis: Axis) {
         : result;
 }
 
+function makeTimeLabels(axis: Axis) {
+    const labelModel = axis.getLabelModel();
+    const result = makeTimeLabelsActually(axis, labelModel);
+
+    return (!labelModel.get('show') || axis.scale.isBlank())
+        ? {labels: []}
+        : result;
+}
+
 function makeCategoryLabelsActually(axis: Axis, labelModel: Model<AxisBaseOption['axisLabel']>) {
     const labelsCache = getListCache(axis, 'labels');
     const optionLabelInterval = getOptionCategoryInterval(labelModel);
@@ -129,6 +143,104 @@ function makeCategoryLabelsActually(axis: Axis, labelModel: Model<AxisBaseOption
     });
 }
 
+function makeTimeLabelsActually(axis: Axis, labelModel: Model<AxisBaseOption['axisLabel']>) {
+    const labelsCache = getListCache(axis, 'labels');
+    const timeKey = 'time'; // TODO: change key name
+    const result = listCacheGet(labelsCache, timeKey);
+
+    if (result) {
+        return result;
+    }
+
+    const labels = makeNonOverlappedTimeLabels(axis);
+
+    // Cache to avoid calling interval function repeatly.
+    return listCacheSet(labelsCache, timeKey, {
+        labels: labels
+    });
+}
+
+function makeNonOverlappedTimeLabels(axis: Axis): MakeLabelsResultObj[];
+function makeNonOverlappedTimeLabels(axis: Axis, onlyTick: false): MakeLabelsResultObj[];
+function makeNonOverlappedTimeLabels(axis: Axis, onlyTick: true): number[];
+function makeNonOverlappedTimeLabels(axis: Axis, onlyTick?: boolean) {
+    const ticks = axis.scale.getTicks() as TimeScaleTick[];
+    const ordinalScale = axis.scale as OrdinalScale;
+    const labelFormatter = makeLabelFormatter(axis);
+
+    const result: (MakeLabelsResultObj | number)[] = [];
+
+    function addItem(tickValue: number) {
+        const tickObj = { value: tickValue };
+        result.push(onlyTick
+            ? tickValue
+            : {
+                formattedLabel: labelFormatter(tickObj),
+                rawLabel: ordinalScale.getLabel(tickObj),  // TODO: ?
+                tickValue: tickValue
+            }
+        );
+    }
+
+    let lastMaxLevel = Number.MAX_VALUE;
+    let maxLevel;
+    while (true) {
+        maxLevel = -1;
+
+        for (let i = 0; i < ticks.length; i++) {
+            if (ticks[i].level > maxLevel && ticks[i].level < lastMaxLevel) {
+                maxLevel = ticks[i].level;
+            }
+        }
+
+        if (maxLevel < 0) {
+            break;
+        }
+
+        for (let i = 0; i < ticks.length; i++) {
+            const tick = ticks[i];
+            if (tick.level === maxLevel) {
+                // Add the tick only if it has no overlap with current ones
+                 // TODO:
+                addItem(tick.value);
+            }
+        }
+
+        if (maxLevel <= 0) {
+            break;
+        }
+        lastMaxLevel = maxLevel;
+    }
+    return result;
+}
+
+function makeTimeTicks(axis: Axis, tickModel: AxisBaseModel) {
+    const ticksCache = getListCache(axis, 'ticks');
+    const result = listCacheGet(ticksCache, 'time');
+
+    if (result) {
+        return result;
+    }
+
+    let ticks: number[];
+
+    // Optimize for the case that large category data and no label displayed,
+    // we should not return all ticks.
+    if (!tickModel.get('show') || axis.scale.isBlank()) {
+        ticks = [];
+    }
+
+    const labelsResult = makeTimeLabelsActually(axis, axis.getLabelModel());
+    ticks = zrUtil.map(labelsResult.labels, function (labelItem) {
+        return labelItem.tickValue;
+    });
+
+    // Cache to avoid calling interval function repeatly.
+    return listCacheSet(ticksCache, 'time', {
+        ticks: ticks
+    });
+}
+
 function makeCategoryTicks(axis: Axis, tickModel: AxisBaseModel) {
     const ticksCache = getListCache(axis, 'ticks');
     const optionTickInterval = getOptionCategoryInterval(tickModel);


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