import { ScaleContinuousNumeric } from "d3-scale"; import * as React from "react"; import { isDefined, isNotDefined, getAxisCanvas, GenericChartComponent } from "@fishbot/chart-core"; export interface PointAndFigureSeriesProps { readonly className?: string; readonly clip?: boolean; readonly fill?: { up: string; down: string; }; readonly stroke?: { up: string; down: string; }; readonly strokeWidth?: number; } export class PointAndFigureSeries extends React.Component { public static defaultProps = { className: "react-financial-charts-point-and-figure", strokeWidth: 1, stroke: { up: "#6BA583", down: "#FF0000", }, fill: { up: "none", down: "none", }, clip: true, }; public render() { const { clip } = this.props; return ( ); } private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => { const { xAccessor, xScale, chartConfig: { yScale }, plotData, } = moreProps; const columns = this.getColumns(xScale, xAccessor, yScale, plotData); this.drawOnCanvasPrivate(ctx, this.props, columns); }; private readonly getColumns = ( xScale: ScaleContinuousNumeric, xAccessor: any, yScale: ScaleContinuousNumeric, plotData: any[], ) => { const width = xScale(xAccessor(plotData[plotData.length - 1])) - xScale(xAccessor(plotData[0])); const columnWidth = width / (plotData.length - 1); let anyBox; let j = 0; while (isNotDefined(anyBox)) { if (isDefined(plotData[j].close)) { anyBox = plotData[j].boxes[0]; } else { break; } j++; } const boxHeight = Math.abs(yScale(anyBox.open) - yScale(anyBox.close)); const columns = plotData .filter((d) => isDefined(d.close)) .map((d) => { const boxes = d.boxes.map((box: any) => ({ columnWidth, boxHeight, open: yScale(box.open), close: yScale(box.close), })); const xOffset = xScale(xAccessor(d)) - columnWidth / 2; return { boxes, direction: d.direction, offset: [xOffset, 0], }; }); return columns; }; private readonly drawOnCanvasPrivate = (ctx: CanvasRenderingContext2D, props: any, columns: any[]) => { const { stroke, fill, strokeWidth } = props; ctx.lineWidth = strokeWidth; columns.forEach((col) => { const [offsetX, offsetY] = col.offset; col.boxes.forEach((box: any) => { if (col.direction > 0) { ctx.fillStyle = fill.up; ctx.strokeStyle = stroke.up; ctx.beginPath(); ctx.moveTo(offsetX, offsetY + box.open); ctx.lineTo(offsetX + box.columnWidth, offsetY + box.close); ctx.moveTo(offsetX, offsetY + box.close); ctx.lineTo(offsetX + box.columnWidth, offsetY + box.open); ctx.stroke(); } else { ctx.fillStyle = fill.down; ctx.strokeStyle = stroke.down; ctx.beginPath(); const [x, y] = [offsetX + box.columnWidth / 2, offsetY + box.open + box.boxHeight / 2]; const [rx, ry] = [box.columnWidth / 2, box.boxHeight / 2]; ctx.ellipse(x, y, rx, ry, 0, 0, 2 * Math.PI); ctx.stroke(); } }); }); ctx.stroke(); }; }