Skip to content

Commit

Permalink
Merge branch 'master' into add-dbl-click-for-main-pane
Browse files Browse the repository at this point in the history
  • Loading branch information
SlicedSilver committed Jul 19, 2023
2 parents bdb363d + 68c02fa commit 6b5ecf4
Show file tree
Hide file tree
Showing 10 changed files with 633 additions and 6 deletions.
8 changes: 4 additions & 4 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ module.exports = [
{
name: 'CJS',
path: 'dist/lightweight-charts.production.cjs',
limit: '47.05 KB',
limit: '47.07 KB',
},
{
name: 'ESM',
path: 'dist/lightweight-charts.production.mjs',
limit: '46.97 KB',
limit: '47.01 KB',
},
{
name: 'Standalone-ESM',
path: 'dist/lightweight-charts.standalone.production.mjs',
limit: '48.66 KB',
limit: '48.70 KB',
},
{
name: 'Standalone',
path: 'dist/lightweight-charts.standalone.production.js',
limit: '48.70 KB',
limit: '48.74 KB',
},
];
20 changes: 18 additions & 2 deletions src/model/price-range-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ import { isNumber } from '../helpers/strict-type-checks';

import { PriceRange } from './series-options';

function computeFiniteResult(
method: (...values: number[]) => number,
valueOne: number,
valueTwo: number,
fallback: number
): number {
const firstFinite = Number.isFinite(valueOne);
const secondFinite = Number.isFinite(valueTwo);

if (firstFinite && secondFinite) {
return method(valueOne, valueTwo);
}

return !firstFinite && !secondFinite ? fallback : (firstFinite ? valueOne : valueTwo);
}

export class PriceRangeImpl {
private _minValue: number;
private _maxValue!: number;
Expand Down Expand Up @@ -43,8 +59,8 @@ export class PriceRangeImpl {
return this;
}
return new PriceRangeImpl(
Math.min(this.minValue(), anotherRange.minValue()),
Math.max(this.maxValue(), anotherRange.maxValue())
computeFiniteResult(Math.min, this.minValue(), anotherRange.minValue(), -Infinity),
computeFiniteResult(Math.max, this.maxValue(), anotherRange.maxValue(), Infinity)
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
function runTestCase(container) {
const chartOptions = {
rightPriceScale: {
borderVisible: false,
mode: 2,
},
layout: {
textColor: 'black',
background: { type: 'solid', color: 'white' },
},
};
const chart = (window.chart = LightweightCharts.createChart(
container,
chartOptions
));

/*
* We expect the blue series to NOT be visible
* and the red series to BE visible
*/

const series1 = chart.addLineSeries({
color: '#2962FF',
});
series1.setData([
{ time: 1522033200, value: 0 },
{ time: 1529895600, value: -3 },
{ time: 1537758000, value: 3 },
]);
const series2 = chart.addLineSeries({
color: '#FF2962',
});
series2.setData([
{ time: 1522033200, value: 1 },
{ time: 1529895600, value: -1 },
{ time: 1537758000, value: 2 },
]);
chart.timeScale().fitContent();
}
2 changes: 2 additions & 0 deletions website/docs/plugins/pixel-perfect-rendering/_category_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
label: "Pixel Perfect Rendering"
position: 5
101 changes: 101 additions & 0 deletions website/docs/plugins/pixel-perfect-rendering/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
sidebar_position: 0
sidebar_label: Pixel Perfect Rendering
pagination_title: Pixel Perfect Rendering
title: Best Practices for Pixel Perfect Rendering in Canvas Drawings
description: Best Practices for Pixel Perfect Rendering in Canvas Drawings when creating plugins for the Lightweight Charts
keywords:
- plugins
- extensions
- rendering
- canvas
- bitmap
- media
- pixels
pagination_prev: null
---

To achieve crisp pixel perfect rendering for your plugins, it is recommended that the canvas drawings are created using bitmap coordinates. The difference between media and bitmap coordinate spaces is discussed on the [Canvas Rendering Target](../canvas-rendering-target.md) page. **Essentially, all drawing actions should use integer positions and dimensions when on the bitmap coordinate space.**

To ensure consistency between your plugins and the library's built-in logic for rendering points on the chart, use of the following calculation functions.

:::info

Variable names containing `media` refer to positions / dimensions specified using the media coordinate space (such as the x and y coordinates provided by the library to the renderers), and names containing `bitmap` refer to positions / dimensions on the bitmap coordinate space (actual device screen pixels).

:::

## Centered Shapes

If you need to draw a shape which is centred on a position (for example a price or x coordinate) and has a desired width then you could use the `positionsLine` function presented below. This can be used for drawing a horizontal line at a specific price, or a vertical line aligned with the centre of series point.

```typescript
interface BitmapPositionLength {
/** coordinate for use with a bitmap rendering scope */
position: number;
/** length for use with a bitmap rendering scope */
length: number;
}

function centreOffset(lineBitmapWidth: number): number {
return Math.floor(lineBitmapWidth * 0.5);
}

/**
* Calculates the bitmap position for an item with a desired length (height or width), and centred according to
* a position coordinate defined in media sizing.
* @param positionMedia - position coordinate for the bar (in media coordinates)
* @param pixelRatio - pixel ratio. Either horizontal for x positions, or vertical for y positions
* @param desiredWidthMedia - desired width (in media coordinates)
* @returns Position of the start point and length dimension.
*/
export function positionsLine(
positionMedia: number,
pixelRatio: number,
desiredWidthMedia: number = 1,
widthIsBitmap?: boolean
): BitmapPositionLength {
const scaledPosition = Math.round(pixelRatio * positionMedia);
const lineBitmapWidth = widthIsBitmap
? desiredWidthMedia
: Math.round(desiredWidthMedia * pixelRatio);
const offset = centreOffset(lineBitmapWidth);
const position = scaledPosition - offset;
return { position, length: lineBitmapWidth };
}
```

## Dual Point Shapes

If you need to draw a shape between two coordinates (for example, y coordinates for a high and low price) then you can use the `positionsBox` function as presented below.

```typescript
/**
* Determines the bitmap position and length for a dimension of a shape to be drawn.
* @param position1Media - media coordinate for the first point
* @param position2Media - media coordinate for the second point
* @param pixelRatio - pixel ratio for the corresponding axis (vertical or horizontal)
* @returns Position of the start point and length dimension.
*/
export function positionsBox(
position1Media: number,
position2Media: number,
pixelRatio: number
): BitmapPositionLength {
const scaledPosition1 = Math.round(pixelRatio * position1Media);
const scaledPosition2 = Math.round(pixelRatio * position2Media);
return {
position: Math.min(scaledPosition1, scaledPosition2),
length: Math.abs(scaledPosition2 - scaledPosition1) + 1,
};
}
```

## Default Widths

Please refer to the following pages for functions defining the default widths of shapes drawn by the library:

- [Crosshair and Grid Lines](./widths/crosshair.md)
- [Candlesticks](./widths/candlestick.md)
- [Columns (Histogram)](./widths/columns.md)
- [Full Bar Width](./widths/full-bar-width.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
label: "Default Widths"
position: 0
81 changes: 81 additions & 0 deletions website/docs/plugins/pixel-perfect-rendering/widths/candlestick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
sidebar_position: 0
sidebar_label: Candlesticks
pagination_title: Candlestick Widths
title: Candlestick Width Calculations
description: Describes the calculation for candlestick body widths
keywords:
- plugins
- extensions
- rendering
- canvas
- bitmap
- media
- pixels
- candlestick
- width
---

:::tip

It is recommend that you first read the [Pixel Perfect Rendering](../index.md) page.

:::

The following functions can be used to get the calculated width that the library would use for a candlestick at a specific bar spacing and device pixel ratio.

Below a bar spacing of 4, the library will attempt to use as large a width as possible without the possibility of overlapping, whilst above 4 then the width will start to trend towards an 80% width of the available space.

:::warning

It is expected that candles can overlap slightly at smaller bar spacings (more pronounced on lower resolution devices). This produces a more readable chart. If you need to ensure that bars can never overlap then rather use the widths for [Columns](./columns.md) or the [full bar width](./full-bar-width.md) calculation.

:::

```typescript
function optimalCandlestickWidth(
barSpacing: number,
pixelRatio: number
): number {
const barSpacingSpecialCaseFrom = 2.5;
const barSpacingSpecialCaseTo = 4;
const barSpacingSpecialCaseCoeff = 3;
if (barSpacing >= barSpacingSpecialCaseFrom && barSpacing <= barSpacingSpecialCaseTo) {
return Math.floor(barSpacingSpecialCaseCoeff * pixelRatio);
}
// coeff should be 1 on small barspacing and go to 0.8 while bar spacing grows
const barSpacingReducingCoeff = 0.2;
const coeff =
1 -
(barSpacingReducingCoeff *
Math.atan(
Math.max(barSpacingSpecialCaseTo, barSpacing) - barSpacingSpecialCaseTo
)) /
(Math.PI * 0.5);
const res = Math.floor(barSpacing * coeff * pixelRatio);
const scaledBarSpacing = Math.floor(barSpacing * pixelRatio);
const optimal = Math.min(res, scaledBarSpacing);
return Math.max(Math.floor(pixelRatio), optimal);
}

/**
* Calculates the candlestick width that the library would use for the current
* bar spacing.
* @param barSpacing bar spacing in media coordinates
* @param horizontalPixelRatio - horizontal pixel ratio
* @returns The width (in bitmap coordinates) that the chart would use to draw a candle body
*/
export function candlestickWidth(
barSpacing: number,
horizontalPixelRatio: number
): number {
let width = optimalCandlestickWidth(barSpacing, horizontalPixelRatio);
if (width >= 2) {
const wickWidth = Math.floor(horizontalPixelRatio);
if (wickWidth % 2 !== width % 2) {
width--;
}
}
return width;
}
```
Loading

0 comments on commit 6b5ecf4

Please sign in to comment.