private getHalfBarSpacing(): number {
const data = this.series.data() as readonly { time: Time }[];
if (data.length < 2) {
return 0;
}
const lastPoint = data[data.length - 1];
const previousPoint = data[data.length - 2];
const lastX = getXCoordinateFromTime(this.chart, lastPoint.time, this.series);
const previousX = getXCoordinateFromTime(this.chart, previousPoint.time, this.series);
if (lastX === null || previousX === null) {
return 0;
}
const spacing = Math.abs(Number(lastX) - Number(previousX));
if (!Number.isFinite(spacing)) {
return 0;
}
return spacing / 2;
}
private getGeometry(): RectangleGeometry | null {
if (this.startTime === null || this.endTime === null || this.startPrice === null || this.endPrice === null) {
return null;
}
const startX = getXCoordinateFromTime(this.chart, this.startTime, this.series);
const endX = getXCoordinateFromTime(this.chart, this.endTime, this.series);
const startY = getYCoordinateFromPrice(this.series, this.startPrice);
const endY = getYCoordinateFromPrice(this.series, this.endPrice);
if (startX === null || endX === null || startY === null || endY === null) {
return null;
}
const rawLeft = Math.min(Number(startX), Number(endX));
const rawRight = Math.max(Number(startX), Number(endX));
const halfBarSpacing = this.getHalfBarSpacing();
const left = Math.round(rawLeft - halfBarSpacing);
const right = Math.round(rawRight + halfBarSpacing);
const top = Math.round(Math.min(Number(startY), Number(endY)));
const bottom = Math.round(Math.max(Number(startY), Number(endY)));
const centerX = (left + right) / 2;
const centerY = (top + bottom) / 2;
return {
left,
right,
top,
bottom,
width: right - left,
height: bottom - top,
handles: {
nw: { x: left, y: top },
n: { x: centerX, y: top },
ne: { x: right, y: top },
e: { x: right, y: centerY },
se: { x: right, y: bottom },
s: { x: centerX, y: bottom },
sw: { x: left, y: bottom },
w: { x: left, y: centerY },
},
};
}