You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2021/12/29 06:13:37 UTC

[echarts] branch axis-align updated: feat(alignTicks): fix log axis align. add more test cases

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

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


The following commit(s) were added to refs/heads/axis-align by this push:
     new f862adc  feat(alignTicks): fix log axis align. add more test cases
f862adc is described below

commit f862adc182f2726a8dd84ff2691b8cfa8009f646
Author: pissang <bm...@gmail.com>
AuthorDate: Wed Dec 29 14:12:32 2021 +0800

    feat(alignTicks): fix log axis align. add more test cases
---
 src/coord/radar/Radar.ts   |   5 -
 src/scale/helper.ts        |  68 ++++++----
 src/util/number.ts         |  14 --
 test/axis-align-ticks.html | 332 ++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 326 insertions(+), 93 deletions(-)

diff --git a/src/coord/radar/Radar.ts b/src/coord/radar/Radar.ts
index f80ec8e..68fb878 100644
--- a/src/coord/radar/Radar.ts
+++ b/src/coord/radar/Radar.ts
@@ -22,17 +22,12 @@
 import IndicatorAxis from './IndicatorAxis';
 import IntervalScale from '../../scale/Interval';
 import * as numberUtil from '../../util/number';
-import {
-    getScaleExtent,
-    niceScaleExtent
-} from '../axisHelper';
 import { CoordinateSystemMaster, CoordinateSystem } from '../CoordinateSystem';
 import RadarModel from './RadarModel';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import { ScaleDataValue } from '../../util/types';
 import { ParsedModelFinder } from '../../util/model';
-import { parseAxisModelMinMax } from '../scaleRawExtentInfo';
 import { map, each } from 'zrender/src/core/util';
 import { alignScaleTicks } from '../../scale/helper';
 
diff --git a/src/scale/helper.ts b/src/scale/helper.ts
index bd1ce78..8c752fc 100644
--- a/src/scale/helper.ts
+++ b/src/scale/helper.ts
@@ -18,17 +18,14 @@
 */
 
 import { NumericAxisBaseOptionCommon } from '../coord/axisCommonTypes';
-import * as numberUtil from '../util/number';
+import {getPrecisionSafe, getPrecision, round, nice, quantityExponent} from '../util/number';
 import IntervalScale from './Interval';
-import { parseAxisModelMinMax } from '../coord/scaleRawExtentInfo';
-import { ScaleDataValue } from '../util/types';
-import { getScaleExtent, niceScaleExtent } from '../coord/axisHelper';
+import { getScaleExtent } from '../coord/axisHelper';
 import { AxisBaseModel } from '../coord/AxisBaseModel';
 import LogScale from './Log';
 import Scale from './Scale';
 import { warn } from '../util/log';
 
-const roundNumber = numberUtil.round;
 const mathLog = Math.log;
 
 type intervalScaleNiceTicksResult = {
@@ -37,6 +34,16 @@ type intervalScaleNiceTicksResult = {
     niceTickExtent: [number, number]
 };
 
+function isValueNice(val: number) {
+    const exp10 = Math.pow(10, quantityExponent(Math.abs(val)));
+    const f = Math.abs(val / exp10);
+    return f === 0
+        || f === 1
+        || f === 2
+        || f === 3
+        || f === 5;
+}
+
 export function isIntervalOrLogScale(scale: Scale): scale is LogScale | IntervalScale {
     return scale.type === 'interval' || scale.type === 'log';
 }
@@ -55,7 +62,7 @@ export function intervalScaleNiceTicks(
     const result = {} as intervalScaleNiceTicksResult;
 
     const span = extent[1] - extent[0];
-    let interval = result.interval = numberUtil.nice(span / splitNumber, true);
+    let interval = result.interval = nice(span / splitNumber, true);
     if (minInterval != null && interval < minInterval) {
         interval = result.interval = minInterval;
     }
@@ -66,8 +73,8 @@ export function intervalScaleNiceTicks(
     const precision = result.intervalPrecision = getIntervalPrecision(interval);
     // Niced extent inside original extent
     const niceTickExtent = result.niceTickExtent = [
-        roundNumber(Math.ceil(extent[0] / interval) * interval, precision),
-        roundNumber(Math.floor(extent[1] / interval) * interval, precision)
+        round(Math.ceil(extent[0] / interval) * interval, precision),
+        round(Math.floor(extent[1] / interval) * interval, precision)
     ];
 
     fixExtent(niceTickExtent, extent);
@@ -76,7 +83,7 @@ export function intervalScaleNiceTicks(
 }
 
 function increaseInterval(interval: number) {
-    const exp10 = Math.pow(10, numberUtil.quantityExponent(interval));
+    const exp10 = Math.pow(10, quantityExponent(interval));
     // Increase interval
     let f = interval / exp10;
     if (!f) {
@@ -111,14 +118,24 @@ export function alignScaleTicks(
     const alignToSplitNumber = alignToTicks.length - 1;
     const alignToInterval = intervalScaleProto.getInterval.call(alignToScale);
 
-    const fixedMin = parseAxisModelMinMax(scale, axisModel.get('min', true) as ScaleDataValue);
-    const fixedMax = parseAxisModelMinMax(scale, axisModel.get('max', true) as ScaleDataValue);
+    const scaleExtent = getScaleExtent(scale, axisModel);
+    let rawExtent = scaleExtent.extent;
+    const isMinFixed = scaleExtent.fixMin;
+    const isMaxFixed = scaleExtent.fixMax;
+
+    if (scale.type === 'log') {
+        const logBase = mathLog((scale as LogScale).base);
+        rawExtent = [mathLog(rawExtent[0]) / logBase, mathLog(rawExtent[1]) / logBase];
+    }
 
-    niceScaleExtent(scale, axisModel);
+    scale.setExtent(rawExtent[0], rawExtent[1]);
+    scale.calcNiceExtent({
+        splitNumber: alignToSplitNumber,
+        fixMin: isMinFixed,
+        fixMax: isMaxFixed
+    });
     const extent = intervalScaleProto.getExtent.call(scale);
-    const rawExtent = getScaleExtent(scale, axisModel).extent;
-    const isMinFixed = fixedMin != null;
-    const isMaxFixed = fixedMax != null;
+
     // Need to update the rawExtent.
     // Because value in rawExtent may be not parsed. e.g. 'dataMin', 'dataMax'
     if (isMinFixed) {
@@ -157,18 +174,20 @@ export function alignScaleTicks(
         if (nicedSplitNumber > alignToSplitNumber) {
             interval = increaseInterval(interval);
         }
-        max = Math.ceil(rawExtent[1] / interval) * interval;
-        min = numberUtil.round(max - interval * alignToSplitNumber);
 
+        const range = interval * alignToSplitNumber;
+        max = Math.ceil(rawExtent[1] / interval) * interval;
+        min = round(max - range);
         // Not change the result that crossing zero.
         if (min < 0 && rawExtent[0] >= 0) {
             min = 0;
-            max = numberUtil.round(interval * alignToSplitNumber);
+            max = round(range);
         }
         else if (max > 0 && rawExtent[1] <= 0) {
             max = 0;
-            min = numberUtil.round((-interval * alignToSplitNumber));
+            min = -round(range);
         }
+
     }
 
     // Adjust min, max based on the extent of alignTo. When min or max is set in alignTo scale
@@ -184,13 +203,12 @@ export function alignScaleTicks(
 
     if (__DEV__) {
         const ticks = intervalScaleProto.getTicks.call(scale);
-        // if (ticks.length !== alignToScale.getTicks().length) {
-        //     debugger
-        // }
-        if (ticks[1] && ticks[1].value % interval) {
+        if (ticks[1]
+            && (!isValueNice(interval) || getPrecisionSafe(ticks[1].value) > getPrecisionSafe(interval))
+        ) {
             warn(
                 // eslint-disable-next-line
-                `The ticks may be not displayed nice if when set min: ${fixedMin}, max: ${fixedMax} and alignTicks: true`
+                `The ticks may be not displayed nice if when set min: ${axisModel.get('min')}, max: ${axisModel.get('max')} and alignTicks: true`
             );
         }
     }
@@ -201,7 +219,7 @@ export function alignScaleTicks(
  */
 export function getIntervalPrecision(interval: number): number {
     // Tow more digital for tick.
-    return numberUtil.getPrecision(interval) + 2;
+    return getPrecision(interval) + 2;
 }
 
 function clamp(
diff --git a/src/util/number.ts b/src/util/number.ts
index 20c1ac9..2ec02f3 100644
--- a/src/util/number.ts
+++ b/src/util/number.ts
@@ -483,20 +483,6 @@ export function nice(val: number, round?: boolean): number {
     return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
 }
 
-// /**
-//  * Check if value is a nice number.
-//  * @param val
-//  */
-// export function isValueNice(val: number) {
-//     const exp10 = Math.pow(10, quantityExponent(Math.abs(val)));
-//     const f = Math.abs(val / exp10);
-//     return f === 0
-//         || f === 1
-//         || f === 2
-//         || f === 3
-//         || f === 5;
-// }
-
 /**
  * This code was copied from "d3.js"
  * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>.
diff --git a/test/axis-align-ticks.html b/test/axis-align-ticks.html
index 1b38f8f..c1e6250 100644
--- a/test/axis-align-ticks.html
+++ b/test/axis-align-ticks.html
@@ -38,44 +38,28 @@ 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>
-        require([
-            'echarts',
-            // 'map/js/china',
-            // './data/nutrients.json'
-        ], function (echarts) {
-            var option;
-            const colors = ["#5470C6", "#91CC75", "#EE6666"];
-            option = {
+            var colors = ['#5470C6', '#91CC75', '#EE6666'];
+            var commonOption = {
                 color: colors,
-                tooltip: {
-                    trigger: "axis",
-                    axisPointer: {
-                        type: "cross"
-                    }
-                },
                 grid: {
-                    right: "20%"
-                },
-                toolbox: {
-                    feature: {
-                        dataView: { show: true, readOnly: false },
-                        restore: { show: true },
-                        saveAsImage: { show: true }
-                    }
+                    right: '20%'
                 },
                 legend: {
-                    data: ["Evaporation", "Precipitation", "Temperature"]
+                    data: ['Evaporation', 'Precipitation', 'Temperature']
                 },
                 xAxis: [
                     {
-                        type: "category",
+                        type: 'category',
                         axisTick: {
                             alignWithLabel: true
                         },
@@ -85,9 +69,10 @@ under the License.
                 ],
                 yAxis: [
                     {
-                        type: "value",
-                        name: "Evaporation",
-                        position: "right",
+                        type: 'value',
+                        name: 'Evaporation',
+                        position: 'right',
+                        alignTicks: true,
                         axisLine: {
                             show: true,
                             lineStyle: {
@@ -95,13 +80,13 @@ under the License.
                             }
                         },
                         axisLabel: {
-                            formatter: "{value} ml"
+                            formatter: '{value} ml'
                         }
                     },
                     {
-                        type: "value",
-                        name: "Precipitation",
-                        position: "right",
+                        type: 'value',
+                        name: 'Precipitation',
+                        position: 'right',
                         alignTicks: true,
                         offset: 80,
                         axisLine: {
@@ -111,13 +96,13 @@ under the License.
                             }
                         },
                         axisLabel: {
-                            formatter: "{value} ml"
+                            formatter: '{value} ml'
                         }
                     },
                     {
-                        type: "value",
-                        name: "温度",
-                        position: "left",
+                        type: 'value',
+                        name: '温度',
+                        position: 'left',
                         alignTicks: true,
                         axisLine: {
                             show: true,
@@ -126,22 +111,22 @@ under the License.
                             }
                         },
                         axisLabel: {
-                            formatter: "{value} °C"
+                            formatter: '{value} °C'
                         }
                     }
                 ],
                 series: [
                     {
-                        name: "Evaporation",
-                        type: "bar",
+                        name: 'Evaporation',
+                        type: 'bar',
                         data: [
                             2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4,
                             3.3
                         ]
                     },
                     {
-                        name: "Precipitation",
-                        type: "bar",
+                        name: 'Precipitation',
+                        type: 'bar',
                         yAxisIndex: 1,
                         data: [
                             2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0,
@@ -149,8 +134,8 @@ under the License.
                         ]
                     },
                     {
-                        name: "Temperature",
-                        type: "line",
+                        name: 'Temperature',
+                        type: 'line',
                         yAxisIndex: 2,
                         data: [
                             2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2
@@ -158,20 +143,269 @@ under the License.
                     }
                 ]
             };
+        </script>
+
+
+
+        <script>
+        require([
+            'echarts'
+        ], function (echarts) {
+            var option = JSON.parse(JSON.stringify(commonOption));
 
             var chart = testHelper.create(echarts, 'main0', {
                 title: [
-                    'Should align to the first axis'
+                    'Should align to the first axis if all set alignTicks'
                 ],
                 option: option
-                // height: 300,
-                // buttons: [{text: 'btn-txt', onclick: function () {}}],
-                // recordCanvas: true,
             });
         });
         </script>
 
 
+        <script>
+            require([
+                'echarts'
+            ], function (echarts) {
+                var option = JSON.parse(JSON.stringify(commonOption));
+
+                const tmp = option.yAxis;
+                option.yAxis = option.xAxis;
+                option.xAxis = tmp;
+                option.xAxis[1].alignTicks = false;
+                option.xAxis[1].splitNumber = 20;
+                option.xAxis[1].offset = 30;
+                option.grid.top = 100;
+
+                option.series[0].xAxisIndex = 0;
+                option.series[1].xAxisIndex = 1;
+                option.series[2].xAxisIndex = 2;
+
+                option.series[0].yAxisIndex = 0;
+                option.series[1].yAxisIndex = 0;
+                option.series[2].yAxisIndex = 0;
+
+                var chart = testHelper.create(echarts, 'main1', {
+                    title: [
+                        'xAxis can alignTicks',
+                        'Should align to the second xAxis with splitNumber: 20'
+                    ],
+                    option: option
+                });
+            });
+        </script>
+
+        <script>
+            require([
+                'echarts'
+            ], function (echarts) {
+
+                var option = JSON.parse(JSON.stringify(commonOption));
+                option.yAxis.unshift({
+                    type: 'category',
+                    offset: 50,
+                    data: ['foo', 'bar', 'baz']
+                });
+                option.series[0].yAxisIndex = 1;
+                option.series[1].yAxisIndex = 2;
+                option.series[2].yAxisIndex = 3;
+
+                var chart = testHelper.create(echarts, 'main2', {
+                    title: [
+                        'Should not align to the category axis'
+                    ],
+                    option: option
+                });
+            });
+        </script>
+
+        <script>
+            require([
+                'echarts'
+            ], function (echarts) {
+
+                var option = {
+                    legend: {},
+                    xAxis: {
+                        type: 'category',
+                        name: 'x',
+                        splitLine: { show: false },
+                        data: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
+                    },
+                    yAxis: [{
+                        type: 'log',
+                        alignTicks: true,
+                        minorSplitLine: {
+                            show: true
+                        },
+                    }, {
+                        type: 'value'
+                    }],
+                    series: [
+                        {
+                            name: 'Log',
+                            type: 'line',
+                            data: [1, 3, 9, 27, 81, 247, 741, 2223, 6669]
+                        },
+                        {
+                            name: 'Linear',
+                            type: 'line',
+                            yAxisIndex: 1,
+                            data: [1, 2, 3, 4, 2, 8, 11, 3, 1]
+                        }
+                    ]
+                }
+
+                var chart = testHelper.create(echarts, 'main3', {
+                    title: [
+                        'Log axis can alignTicks to value axis'
+                    ],
+                    option: option
+                });
+            });
+        </script>
+
+        <script>
+            require([
+                'echarts'
+            ], function (echarts) {
+
+                var option = {
+                    legend: {},
+                    xAxis: {
+                        type: 'category',
+                        name: 'x',
+                        splitLine: { show: false },
+                        data: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
+                    },
+                    yAxis: [{
+                        type: 'log',
+                        minorSplitLine: {
+                            show: true
+                        },
+                    }, {
+                        alignTicks: true,
+                        type: 'value'
+                    }],
+                    series: [
+                        {
+                            name: 'Log',
+                            type: 'line',
+                            data: [1, 3, 9, 27, 81, 247, 741, 2223, 6669]
+                        },
+                        {
+                            name: 'Linear',
+                            type: 'line',
+                            yAxisIndex: 1,
+                            data: [1, 2, 3, 4, 2, 8, 11, 3, 1]
+                        }
+                    ]
+                }
+
+                var chart = testHelper.create(echarts, 'main4', {
+                    title: [
+                        'Value axis can alignTicks to log axis'
+                    ],
+                    option: option
+                });
+            });
+        </script>
+
+        <script>
+            require([
+                'echarts'
+            ], function (echarts) {
+                var option = {
+                    title: {
+                        text: 'Rainfall and Flow Relationship',
+                        left: 'center'
+                    },
+                    grid: {
+                        bottom: 80
+                    },
+                    legend: {
+                        data: ['Flow', 'Rainfall'],
+                        left: 10
+                    },
+                    dataZoom: [
+                        {
+                            show: true,
+                            realtime: true,
+                            start: 65,
+                            end: 85
+                        },
+                        {
+                            type: 'inside',
+                            realtime: true,
+                            start: 65,
+                            end: 85
+                        }
+                    ],
+                    xAxis: [
+                        {
+                            type: 'category',
+                            boundaryGap: false,
+                            axisLine: { onZero: false },
+                            // prettier-ignore
+                            data: [
+                                '2009/6/12 2:00', '2009/6/12 3:00', '2009/6/12 4:00', '2009/6/12 5:00', '2009/6/12 6:00', '2009/6/12 7:00', '2009/6/12 8:00', '2009/6/12 9:00', '2009/6/12 10:00', '2009/6/12 11:00', '2009/6/12 12:00', '2009/6/12 13:00', '2009/6/12 14:00', '2009/6/12 15:00', '2009/6/12 16:00', '2009/6/12 17:00', '2009/6/12 18:00', '2009/6/12 19:00', '2009/6/12 20:00', '2009/6/12 21:00', '2009/6/12 22:00', '2009/6/12 23:00', '2009/6/13 0:00', '2009/6/13 1:00', '2009/6/13 2:0 [...]
+                            ].map(function (str) {
+                                return str.replace(' ', '\n');
+                            })
+                        }
+                    ],
+                    yAxis: [
+                        {
+                            name: 'Flow(m^3/s)',
+                            type: 'value'
+                        },
+                        {
+                            name: 'Rainfall(mm)',
+                            nameLocation: 'start',
+                            type: 'value',
+                            alignTicks: true,
+                            inverse: true
+                        }
+                    ],
+                    series: [
+                        {
+                            name: 'Flow',
+                            type: 'line',
+                            areaStyle: {},
+                            lineStyle: {
+                                width: 1
+                            },
+                            showSymbol: false,
+                            // prettier-ignore
+                            data: [
+                                0.97, 0.96, 0.96, 0.95, 0.95, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94, 0.94 [...]
+                            ]
+                        },
+                        {
+                            name: 'Rainfall',
+                            type: 'line',
+                            yAxisIndex: 1,
+                            areaStyle: {},
+                            lineStyle: {
+                                width: 1
+                            },
+                            showSymbol: false,
+                            // prettier-ignore
+                            data: [
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.005, 0.017, 0.017, 0.017, 0.017, 0.011, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.021, 0.026, 0.03, 0.036, 0.036, 0.195, 0.221, 0.019, 0.013, 0.017, 0.03, 0.03, 0.03, 0.046, 0.045, 0.038, 0.084, 0.0 [...]
+                            ]
+                        }
+                    ]
+                };
+
+                var chart = testHelper.create(echarts, 'main5', {
+                    title: [
+                        'Should always been aligned in dataZoom'
+                    ],
+                    option: option
+                });
+            });
+        </script>
     </body>
 </html>
 

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