-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added pixel rendering guide for plugin developers
- Loading branch information
1 parent
ddcad5f
commit 58c4579
Showing
7 changed files
with
572 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
label: "Pixel Perfect Rendering" | ||
position: 5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
* an 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 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 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, | ||
}; | ||
} | ||
``` | ||
|
||
## Defaults 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) |
2 changes: 2 additions & 0 deletions
2
website/docs/plugins/pixel-perfect-rendering/widths/_category_.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
81
website/docs/plugins/pixel-perfect-rendering/widths/candlestick.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
``` |
Oops, something went wrong.