Skip to content

Commit

Permalink
Merge pull request #1348 from tradingview/line_bases_series_point_mar…
Browse files Browse the repository at this point in the history
…kers

Implemented line-based series' point markers support
  • Loading branch information
SlicedSilver committed Jun 13, 2023
2 parents 8ee7bb9 + c68d77a commit ddcad5f
Show file tree
Hide file tree
Showing 19 changed files with 472 additions and 221 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: '46.83 KB',
limit: '46.94 KB',
},
{
name: 'ESM',
path: 'dist/lightweight-charts.production.mjs',
limit: '46.74 KB',
limit: '46.86 KB',
},
{
name: 'Standalone-ESM',
path: 'dist/lightweight-charts.standalone.production.mjs',
limit: '48.45 KB',
limit: '48.56 KB',
},
{
name: 'Standalone',
path: 'dist/lightweight-charts.standalone.production.js',
limit: '48.50 KB',
limit: '48.61 KB',
},
];
6 changes: 6 additions & 0 deletions src/api/options/series-options-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ export const lineStyleDefaults: LineStyleOptions = {
lineStyle: LineStyle.Solid,
lineWidth: 3,
lineType: LineType.Simple,
lineVisible: true,
crosshairMarkerVisible: true,
crosshairMarkerRadius: 4,
crosshairMarkerBorderColor: '',
crosshairMarkerBorderWidth: 2,
crosshairMarkerBackgroundColor: '',
lastPriceAnimation: LastPriceAnimationMode.Disabled,
pointMarkersVisible: false,
};

export const areaStyleDefaults: AreaStyleOptions = {
Expand All @@ -53,12 +55,14 @@ export const areaStyleDefaults: AreaStyleOptions = {
lineStyle: LineStyle.Solid,
lineWidth: 3,
lineType: LineType.Simple,
lineVisible: true,
crosshairMarkerVisible: true,
crosshairMarkerRadius: 4,
crosshairMarkerBorderColor: '',
crosshairMarkerBorderWidth: 2,
crosshairMarkerBackgroundColor: '',
lastPriceAnimation: LastPriceAnimationMode.Disabled,
pointMarkersVisible: false,
};

export const baselineStyleDefaults: BaselineStyleOptions = {
Expand All @@ -78,6 +82,7 @@ export const baselineStyleDefaults: BaselineStyleOptions = {
lineWidth: 3,
lineStyle: LineStyle.Solid,
lineType: LineType.Simple,
lineVisible: true,

crosshairMarkerVisible: true,
crosshairMarkerRadius: 4,
Expand All @@ -86,6 +91,7 @@ export const baselineStyleDefaults: BaselineStyleOptions = {
crosshairMarkerBackgroundColor: '',

lastPriceAnimation: LastPriceAnimationMode.Disabled,
pointMarkersVisible: false,
};

export const histogramStyleDefaults: HistogramStyleOptions = {
Expand Down
72 changes: 66 additions & 6 deletions src/model/series-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,26 @@ export interface LineStyleOptions {
*/
lineType: LineType;

/**
* Show series line.
*
* @defaultValue `true`
*/
lineVisible: boolean;

/**
* Show circle markers on each point.
*
* @defaultValue `false`
*/
pointMarkersVisible: boolean;
/**
* Circle markers radius in pixels.
*
* @defaultValue `undefined`
*/
pointMarkersRadius?: number;

/**
* Show the crosshair marker.
*
Expand All @@ -187,13 +207,13 @@ export interface LineStyleOptions {
*/
crosshairMarkerRadius: number;
/**
* Crosshair marker border color. An empty string falls back to the the color of the series under the crosshair.
* Crosshair marker border color. An empty string falls back to the color of the series under the crosshair.
*
* @defaultValue `''`
*/
crosshairMarkerBorderColor: string;
/**
* The crosshair marker background color. An empty string falls back to the the color of the series under the crosshair.
* The crosshair marker background color. An empty string falls back to the color of the series under the crosshair.
*
* @defaultValue `''`
*/
Expand Down Expand Up @@ -266,6 +286,26 @@ export interface AreaStyleOptions {
*/
lineType: LineType;

/**
* Show series line.
*
* @defaultValue `true`
*/
lineVisible: boolean;

/**
* Show circle markers on each point.
*
* @defaultValue `false`
*/
pointMarkersVisible: boolean;
/**
* Circle markers radius in pixels.
*
* @defaultValue `undefined`
*/
pointMarkersRadius?: number;

/**
* Show the crosshair marker.
*
Expand All @@ -279,13 +319,13 @@ export interface AreaStyleOptions {
*/
crosshairMarkerRadius: number;
/**
* Crosshair marker border color. An empty string falls back to the the color of the series under the crosshair.
* Crosshair marker border color. An empty string falls back to the color of the series under the crosshair.
*
* @defaultValue `''`
*/
crosshairMarkerBorderColor: string;
/**
* The crosshair marker background color. An empty string falls back to the the color of the series under the crosshair.
* The crosshair marker background color. An empty string falls back to the color of the series under the crosshair.
*
* @defaultValue `''`
*/
Expand Down Expand Up @@ -393,6 +433,26 @@ export interface BaselineStyleOptions {
*/
lineType: LineType;

/**
* Show series line.
*
* @defaultValue `true`
*/
lineVisible: boolean;

/**
* Show circle markers on each point.
*
* @defaultValue `false`
*/
pointMarkersVisible: boolean;
/**
* Circle markers radius in pixels.
*
* @defaultValue `undefined`
*/
pointMarkersRadius?: number;

/**
* Show the crosshair marker.
*
Expand All @@ -406,13 +466,13 @@ export interface BaselineStyleOptions {
*/
crosshairMarkerRadius: number;
/**
* Crosshair marker border color. An empty string falls back to the the color of the series under the crosshair.
* Crosshair marker border color. An empty string falls back to the color of the series under the crosshair.
*
* @defaultValue `''`
*/
crosshairMarkerBorderColor: string;
/**
* The crosshair marker background color. An empty string falls back to the the color of the series under the crosshair.
* The crosshair marker background color. An empty string falls back to the color of the series under the crosshair.
*
* @defaultValue `''`
*/
Expand Down
23 changes: 12 additions & 11 deletions src/renderers/area-renderer-base.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { MediaCoordinatesRenderingScope } from 'fancy-canvas';
import { BitmapCoordinatesRenderingScope } from 'fancy-canvas';

import { Coordinate } from '../model/coordinate';
import { PricedValue } from '../model/price-scale';
import { SeriesItemsIndexesRange, TimedValue } from '../model/time-data';

import { BitmapCoordinatesPaneRenderer } from './bitmap-coordinates-pane-renderer';
import { LinePoint, LineStyle, LineType, LineWidth, setLineStyle } from './draw-line';
import { MediaCoordinatesPaneRenderer } from './media-coordinates-pane-renderer';
import { walkLine } from './walk-line';

export type AreaFillItemBase = TimedValue & PricedValue & LinePoint;
Expand All @@ -25,26 +25,27 @@ export interface PaneRendererAreaDataBase<TItem extends AreaFillItemBase = AreaF

function finishStyledArea(
baseLevelCoordinate: Coordinate,
ctx: CanvasRenderingContext2D,
scope: BitmapCoordinatesRenderingScope,
style: CanvasRenderingContext2D['fillStyle'],
areaFirstItem: LinePoint,
newAreaFirstItem: LinePoint
): void {
ctx.lineTo(newAreaFirstItem.x, baseLevelCoordinate);
ctx.lineTo(areaFirstItem.x, baseLevelCoordinate);
ctx.closePath();
ctx.fillStyle = style;
ctx.fill();
const { context, horizontalPixelRatio, verticalPixelRatio } = scope;
context.lineTo(newAreaFirstItem.x * horizontalPixelRatio, baseLevelCoordinate * verticalPixelRatio);
context.lineTo(areaFirstItem.x * horizontalPixelRatio, baseLevelCoordinate * verticalPixelRatio);
context.closePath();
context.fillStyle = style;
context.fill();
}

export abstract class PaneRendererAreaBase<TData extends PaneRendererAreaDataBase> extends MediaCoordinatesPaneRenderer {
export abstract class PaneRendererAreaBase<TData extends PaneRendererAreaDataBase> extends BitmapCoordinatesPaneRenderer {
protected _data: TData | null = null;

public setData(data: TData): void {
this._data = data;
}

protected _drawImpl(renderingScope: MediaCoordinatesRenderingScope): void {
protected _drawImpl(renderingScope: BitmapCoordinatesRenderingScope): void {
if (this._data === null) {
return;
}
Expand All @@ -71,5 +72,5 @@ export abstract class PaneRendererAreaBase<TData extends PaneRendererAreaDataBas
walkLine(renderingScope, items, lineType, visibleRange, barWidth, this._fillStyle.bind(this), finishStyledArea.bind(null, baseLevelCoordinate));
}

protected abstract _fillStyle(renderingScope: MediaCoordinatesRenderingScope, item: TData['items'][0]): CanvasRenderingContext2D['fillStyle'];
protected abstract _fillStyle(renderingScope: BitmapCoordinatesRenderingScope, item: TData['items'][0]): CanvasRenderingContext2D['fillStyle'];
}
45 changes: 15 additions & 30 deletions src/renderers/area-renderer.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,28 @@
import { MediaCoordinatesRenderingScope } from 'fancy-canvas';
import { BitmapCoordinatesRenderingScope } from 'fancy-canvas';

import { Coordinate } from '../model/coordinate';
import { AreaFillColorerStyle } from '../model/series-bar-colorer';

import { AreaFillItemBase, PaneRendererAreaBase, PaneRendererAreaDataBase } from './area-renderer-base';
import { GradientStyleCache } from './gradient-style-cache';

export type AreaFillItem = AreaFillItemBase & AreaFillColorerStyle;
export interface PaneRendererAreaData extends PaneRendererAreaDataBase<AreaFillItem> {
}

interface AreaFillCache extends Record<keyof AreaFillColorerStyle, string> {
fillStyle: CanvasRenderingContext2D['fillStyle'];
bottom: Coordinate;
}

export class PaneRendererArea extends PaneRendererAreaBase<PaneRendererAreaData> {
private _fillCache: AreaFillCache | null = null;

protected override _fillStyle(renderingScope: MediaCoordinatesRenderingScope, item: AreaFillItem): CanvasRenderingContext2D['fillStyle'] {
const { context: ctx, mediaSize } = renderingScope;

const { topColor, bottomColor } = item;
const bottom = mediaSize.height as Coordinate;

if (
this._fillCache !== null &&
this._fillCache.topColor === topColor &&
this._fillCache.bottomColor === bottomColor &&
this._fillCache.bottom === bottom
) {
return this._fillCache.fillStyle;
}

const fillStyle = ctx.createLinearGradient(0, 0, 0, bottom);
fillStyle.addColorStop(0, topColor);
fillStyle.addColorStop(1, bottomColor);

this._fillCache = { topColor, bottomColor, fillStyle, bottom };

return fillStyle;
private readonly _fillCache: GradientStyleCache = new GradientStyleCache();

protected override _fillStyle(renderingScope: BitmapCoordinatesRenderingScope, item: AreaFillItem): CanvasRenderingContext2D['fillStyle'] {
return this._fillCache.get(
renderingScope,
{
topColor1: item.topColor,
topColor2: '',
bottomColor1: '',
bottomColor2: item.bottomColor,
bottom: renderingScope.bitmapSize.height as Coordinate,
}
);
}
}
62 changes: 15 additions & 47 deletions src/renderers/baseline-renderer-area.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,31 @@
import { MediaCoordinatesRenderingScope } from 'fancy-canvas';

import { clamp } from '../helpers/mathex';
import { BitmapCoordinatesRenderingScope } from 'fancy-canvas';

import { Coordinate } from '../model/coordinate';
import { BaselineFillColorerStyle } from '../model/series-bar-colorer';

import { AreaFillItemBase, PaneRendererAreaBase, PaneRendererAreaDataBase } from './area-renderer-base';
import { GradientStyleCache } from './gradient-style-cache';

export type BaselineFillItem = AreaFillItemBase & BaselineFillColorerStyle;
export interface PaneRendererBaselineData extends PaneRendererAreaDataBase<BaselineFillItem> {
}

interface BaselineFillCache extends Record<keyof BaselineFillColorerStyle, string> {
fillStyle: CanvasRenderingContext2D['fillStyle'];
baseLevelCoordinate: Coordinate;
bottom: Coordinate;
}
export class PaneRendererBaselineArea extends PaneRendererAreaBase<PaneRendererBaselineData> {
private _fillCache: BaselineFillCache | null = null;
private readonly _fillCache: GradientStyleCache = new GradientStyleCache();

protected override _fillStyle(renderingScope: MediaCoordinatesRenderingScope, item: BaselineFillItem): CanvasRenderingContext2D['fillStyle'] {
const { context: ctx, mediaSize } = renderingScope;
protected override _fillStyle(renderingScope: BitmapCoordinatesRenderingScope, item: BaselineFillItem): CanvasRenderingContext2D['fillStyle'] {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const data = this._data!;

const { topFillColor1, topFillColor2, bottomFillColor1, bottomFillColor2 } = item;
const baseLevelCoordinate = data.baseLevelCoordinate ?? mediaSize.height as Coordinate;
const bottom = mediaSize.height as Coordinate;

if (
this._fillCache !== null &&
this._fillCache.topFillColor1 === topFillColor1 &&
this._fillCache.topFillColor2 === topFillColor2 &&
this._fillCache.bottomFillColor1 === bottomFillColor1 &&
this._fillCache.bottomFillColor2 === bottomFillColor2 &&
this._fillCache.baseLevelCoordinate === baseLevelCoordinate &&
this._fillCache.bottom === bottom
) {
return this._fillCache.fillStyle;
}

const fillStyle = ctx.createLinearGradient(0, 0, 0, bottom);
const baselinePercent = clamp(baseLevelCoordinate / bottom, 0, 1);

fillStyle.addColorStop(0, topFillColor1);
fillStyle.addColorStop(baselinePercent, topFillColor2);
fillStyle.addColorStop(baselinePercent, bottomFillColor1);
fillStyle.addColorStop(1, bottomFillColor2);

this._fillCache = {
topFillColor1,
topFillColor2,
bottomFillColor1,
bottomFillColor2,
fillStyle,
baseLevelCoordinate,
bottom,
};

return fillStyle;
return this._fillCache.get(
renderingScope,
{
topColor1: item.topFillColor1,
topColor2: item.topFillColor2,
bottomColor1: item.bottomFillColor1,
bottomColor2: item.bottomFillColor2,
bottom: renderingScope.bitmapSize.height as Coordinate,
baseLevelCoordinate: data.baseLevelCoordinate,
}
);
}
}
Loading

0 comments on commit ddcad5f

Please sign in to comment.