From 506d40be524fd58bb35d19166f4eb9d2d2f95f57 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Wed, 3 Jul 2024 11:10:56 +0300 Subject: [PATCH 1/3] 1620: Fixed case with a lot of labels --- src/gui/price-axis-widget.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/gui/price-axis-widget.ts b/src/gui/price-axis-widget.ts index 7adabfc66e..df6575220d 100644 --- a/src/gui/price-axis-widget.ts +++ b/src/gui/price-axis-widget.ts @@ -66,7 +66,13 @@ function buildPriceAxisViewsGetter( }; } -function recalculateOverlapping(views: IPriceAxisView[], direction: 1 | -1, scaleHeight: number, rendererOptions: Readonly): void { +function recalculateOverlapping( + views: IPriceAxisView[], + direction: 1 | -1, + scaleHeight: number, + center: number, + rendererOptions: Readonly +): void { if (!views.length) { return; } @@ -74,8 +80,8 @@ function recalculateOverlapping(views: IPriceAxisView[], direction: 1 | -1, scal const initLabelHeight = views[0].height(rendererOptions, true); let spaceBeforeCurrentGroup = direction === 1 - ? scaleHeight / 2 - (views[0].getFixedCoordinate() - initLabelHeight / 2) - : views[0].getFixedCoordinate() - initLabelHeight / 2 - scaleHeight / 2; + ? center - (views[0].getFixedCoordinate() - initLabelHeight / 2) + : views[0].getFixedCoordinate() - initLabelHeight / 2 - center; spaceBeforeCurrentGroup = Math.max(0, spaceBeforeCurrentGroup); for (let i = 1; i < views.length; i++) { @@ -643,8 +649,8 @@ export class PriceAxisWidget implements IDestroyable { } } - recalculateOverlapping(top, 1, this._size.height, rendererOptions); - recalculateOverlapping(bottom, -1, this._size.height, rendererOptions); + recalculateOverlapping(top, 1, this._size.height, center, rendererOptions); + recalculateOverlapping(bottom, -1, this._size.height, center, rendererOptions); } private _drawBackLabels(target: CanvasRenderingTarget2D): void { From edd51003099cf64a7639cfbb4fe72170c7006c4b Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Fri, 5 Jul 2024 11:47:32 +0300 Subject: [PATCH 2/3] 1620: Align labels based on center pixel, not first source --- src/gui/price-axis-widget.ts | 13 +--- .../price-scale/improve-alignment-2.js | 78 +++++++++++++++++++ 2 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 tests/e2e/graphics/test-cases/price-scale/improve-alignment-2.js diff --git a/src/gui/price-axis-widget.ts b/src/gui/price-axis-widget.ts index df6575220d..7f4b94468f 100644 --- a/src/gui/price-axis-widget.ts +++ b/src/gui/price-axis-widget.ts @@ -566,7 +566,7 @@ export class PriceAxisWidget implements IDestroyable { if (this._size === null || this._priceScale === null) { return; } - let center = this._size.height / 2; + const center = this._size.height / 2; const views: IPriceAxisView[] = []; const orderedSources = this._priceScale.orderedSources().slice(); // Copy of array @@ -585,8 +585,6 @@ export class PriceAxisWidget implements IDestroyable { }); } - // we can use any, but let's use the first source as "center" one - const centerSource = this._priceScale.dataSources()[0]; const priceScale = this._priceScale; const updateForSources = (sources: IDataSource[]) => { @@ -599,9 +597,6 @@ export class PriceAxisWidget implements IDestroyable { views.push(view); } }); - if (centerSource === source && sourceViews.length > 0) { - center = sourceViews[0].coordinate(); - } }); }; @@ -630,13 +625,13 @@ export class PriceAxisWidget implements IDestroyable { // sort top from center to top top.sort((l: IPriceAxisView, r: IPriceAxisView) => r.coordinate() - l.coordinate()); + bottom.sort((l: IPriceAxisView, r: IPriceAxisView) => l.coordinate() - r.coordinate()); + // share center label if (top.length && bottom.length) { - bottom.push(top[0]); + bottom.unshift(top[0]); } - bottom.sort((l: IPriceAxisView, r: IPriceAxisView) => l.coordinate() - r.coordinate()); - for (const view of views) { const halfHeight = Math.floor(view.height(rendererOptions) / 2); const coordinate = view.coordinate(); diff --git a/tests/e2e/graphics/test-cases/price-scale/improve-alignment-2.js b/tests/e2e/graphics/test-cases/price-scale/improve-alignment-2.js new file mode 100644 index 0000000000..a0acd6967c --- /dev/null +++ b/tests/e2e/graphics/test-cases/price-scale/improve-alignment-2.js @@ -0,0 +1,78 @@ +function runTestCase(container) { + const chartOptions = { + height: 320, + width: 600, + rightPriceScale: { + scaleMargins: { + top: 0, + bottom: 0, + }, + entireTextOnly: true, + alignLabels: true, + }, + }; + + const chart = (window.chart = LightweightCharts.createChart( + container, + chartOptions + )); + + const levels = [ + 25.44, + 17.96, + 14.14, + 4.23, + 4.08, + 3.42, + 3.09, + 2.78, + 2.24, + 2.14, + 2.05, + 1.92, + 1.69, + 1.67, + 1.47, + 1.32, + 1.11, + 0.90712, + 0.63113, + 0.40527, + ].map(price => ({ price })); + + const colorByIndex = index => { + const r = index & 0b10000 >> 4; + const g = index & 0b01100 >> 2; + const b = index & 0b00011; + return `rgb(${Math.floor(r * 255)}, ${Math.floor(g * 255 / 4)}, ${Math.floor(b * 255 / 4)})`; + }; + + for (let i = 0; i < levels.length; i++) { + const s = chart.addLineSeries({ + color: colorByIndex(i), + }); + s.setData([ + { time: 10000, value: levels[i].price }, + { time: 20000, value: levels[i].price }, + ]); + levels[i].series = s; + } + + for (let i = 0; i < 4; i++) { + chart.removeSeries(levels[i].series); + delete levels[i].series; + } + + for (let i = 3; i >= 2; i--) { + const s = chart.addLineSeries({ + color: colorByIndex(i), + }); + s.setData([ + { time: 10000, value: levels[i].price }, + { time: 20000, value: levels[i].price }, + ]); + levels[i].series = s; + } + + chart.timeScale().fitContent(); +} From eb791e0af91caa171228429dd84edd295377f150 Mon Sep 17 00:00:00 2001 From: Eugene Korobko Date: Fri, 5 Jul 2024 17:02:19 +0300 Subject: [PATCH 3/3] 1620: Fixed tests and review issues --- src/gui/price-axis-widget.ts | 20 +++++++------------ .../price-scale/improve-alignment-2.js | 2 +- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/gui/price-axis-widget.ts b/src/gui/price-axis-widget.ts index 7f4b94468f..857522d1e9 100644 --- a/src/gui/price-axis-widget.ts +++ b/src/gui/price-axis-widget.ts @@ -70,13 +70,13 @@ function recalculateOverlapping( views: IPriceAxisView[], direction: 1 | -1, scaleHeight: number, - center: number, rendererOptions: Readonly ): void { if (!views.length) { return; } let currentGroupStart = 0; + const center = scaleHeight / 2; const initLabelHeight = views[0].height(rendererOptions, true); let spaceBeforeCurrentGroup = direction === 1 @@ -566,8 +566,6 @@ export class PriceAxisWidget implements IDestroyable { if (this._size === null || this._priceScale === null) { return; } - const center = this._size.height / 2; - const views: IPriceAxisView[] = []; const orderedSources = this._priceScale.orderedSources().slice(); // Copy of array const pane = this._pane; @@ -610,28 +608,24 @@ export class PriceAxisWidget implements IDestroyable { return; } - this._fixLabelOverlap(views, rendererOptions, center); + this._fixLabelOverlap(views, rendererOptions); } - private _fixLabelOverlap(views: IPriceAxisView[], rendererOptions: Readonly, center: number): void { + private _fixLabelOverlap(views: IPriceAxisView[], rendererOptions: Readonly): void { if (this._size === null) { return; } + const center = this._size.height / 2; + // split into two parts const top = views.filter((view: IPriceAxisView) => view.coordinate() <= center); const bottom = views.filter((view: IPriceAxisView) => view.coordinate() > center); // sort top from center to top top.sort((l: IPriceAxisView, r: IPriceAxisView) => r.coordinate() - l.coordinate()); - bottom.sort((l: IPriceAxisView, r: IPriceAxisView) => l.coordinate() - r.coordinate()); - // share center label - if (top.length && bottom.length) { - bottom.unshift(top[0]); - } - for (const view of views) { const halfHeight = Math.floor(view.height(rendererOptions) / 2); const coordinate = view.coordinate(); @@ -644,8 +638,8 @@ export class PriceAxisWidget implements IDestroyable { } } - recalculateOverlapping(top, 1, this._size.height, center, rendererOptions); - recalculateOverlapping(bottom, -1, this._size.height, center, rendererOptions); + recalculateOverlapping(top, 1, this._size.height, rendererOptions); + recalculateOverlapping(bottom, -1, this._size.height, rendererOptions); } private _drawBackLabels(target: CanvasRenderingTarget2D): void { diff --git a/tests/e2e/graphics/test-cases/price-scale/improve-alignment-2.js b/tests/e2e/graphics/test-cases/price-scale/improve-alignment-2.js index a0acd6967c..fd050db8ab 100644 --- a/tests/e2e/graphics/test-cases/price-scale/improve-alignment-2.js +++ b/tests/e2e/graphics/test-cases/price-scale/improve-alignment-2.js @@ -1,6 +1,6 @@ function runTestCase(container) { const chartOptions = { - height: 320, + height: 500, width: 600, rightPriceScale: { scaleMargins: {