export interface Point { x: number; y: number; } export class PointLine { data: Point[]; constructor() { this.data = []; } find_index(x: number) { if (this.data.length == 0) { return 0; } // left should be too early to insert // right should be too late to insert let left = 0; let size = this.data.length; while (size > 1) { const half = Math.floor(size / 2); const mid = left + half; const is_less = this.data[mid].x < x; if (is_less) { left = mid; } size -= half; } return left + (this.data[left].x < x ? 1 : 0); } insert(point: Point, maximum_separation?: number) { const index = this.find_index(point.x); this.data.splice(index, 0, point); if (maximum_separation !== undefined) { this.reduce_to_maximum_separation(maximum_separation, [ index - 1, index + 1, ]); } } reduce_to_maximum_separation( maximum_separation: number, range?: [number, number], ) { if (maximum_separation <= 0) { return; } // Add a default range if it does not exist range = range || [1, this.data.length - 2]; // clamp it to the bounds range = [ Math.max(1, range[0]), Math.min(this.data.length - 2, range[1]), ]; // Loop over the indices in the range (backwards so removals don't break anything) for (let i = range[1]; i >= range[0]; i--) { const x_previous = this.data[i - 1].x; const x_current = this.data[i].x; const x_next = this.data[i + 1].x; const separation_before = x_current - x_previous; const separation_after = x_next - x_current; // If the data points are too close on both sides then delete this point if ( separation_before < maximum_separation && separation_after < maximum_separation ) { this.data.splice(i, 1); } } } }