82 lines
2.2 KiB
TypeScript
82 lines
2.2 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
}
|
|
}
|