You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
2.2 KiB
90 lines
2.2 KiB
|
4 months ago
|
import { linePtSegDistSq } from '../utils';
|
||
|
|
import type { ILine, IRectangle2 } from '../interfaces';
|
||
|
|
|
||
|
|
export function lineBoundingBox(line: ILine): IRectangle2 {
|
||
|
|
const minX = Math.min(line.x1, line.x2);
|
||
|
|
const maxX = Math.max(line.x1, line.x2);
|
||
|
|
const minY = Math.min(line.y1, line.y2);
|
||
|
|
const maxY = Math.max(line.y1, line.y2);
|
||
|
|
|
||
|
|
return {
|
||
|
|
x: minX,
|
||
|
|
y: minY,
|
||
|
|
x2: maxX,
|
||
|
|
y2: maxY,
|
||
|
|
width: maxX - minX,
|
||
|
|
height: maxY - minY,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export class Line {
|
||
|
|
constructor(
|
||
|
|
public x1: number,
|
||
|
|
public y1: number,
|
||
|
|
public x2: number,
|
||
|
|
public y2: number
|
||
|
|
) {}
|
||
|
|
|
||
|
|
equals(that: ILine) {
|
||
|
|
return this.x1 === that.x1 && this.y1 === that.y1 && this.x2 === that.x2 && this.y2 === that.y2;
|
||
|
|
}
|
||
|
|
|
||
|
|
draw(ctx: CanvasRenderingContext2D) {
|
||
|
|
ctx.moveTo(this.x1, this.y1);
|
||
|
|
ctx.lineTo(this.x2, this.y2);
|
||
|
|
}
|
||
|
|
|
||
|
|
toString() {
|
||
|
|
return `Line(from=(${this.x1},${this.y1}),to=(${this.x2},${this.y2}))`;
|
||
|
|
}
|
||
|
|
|
||
|
|
static from(l: { x1: number; y1: number; x2: number; y2: number }) {
|
||
|
|
return new Line(l.x1, l.y1, l.x2, l.y2);
|
||
|
|
}
|
||
|
|
|
||
|
|
// whether an infinite line to positive x from the point p will cut through the line
|
||
|
|
cuts(px: number, py: number) {
|
||
|
|
if (this.y1 === this.y2) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if ((py < this.y1 && py <= this.y2) || (py > this.y1 && py >= this.y2)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (px > this.x1 && px >= this.x2) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (px < this.x1 && px <= this.x2) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
const cross = this.x1 + ((py - this.y1) * (this.x2 - this.x1)) / (this.y2 - this.y1);
|
||
|
|
return px <= cross;
|
||
|
|
}
|
||
|
|
|
||
|
|
distSquare(x: number, y: number) {
|
||
|
|
return linePtSegDistSq(this.x1, this.y1, this.x2, this.y2, x, y);
|
||
|
|
}
|
||
|
|
|
||
|
|
ptClose(x: number, y: number, r: number) {
|
||
|
|
// check whether the point is outside the bounding rectangle with padding r
|
||
|
|
if (this.x1 < this.x2) {
|
||
|
|
if (x < this.x1 - r || x > this.x2 + r) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (x < this.x2 - r || x > this.x1 + r) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (this.y1 < this.y2) {
|
||
|
|
if (y < this.y1 - r || y > this.y2 + r) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (y < this.y2 - r || y > this.y1 + r) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|