mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
import type { Rect } from "@browser-skill/vom";
|
|
import type { Point, Region, ViewportRect } from "./geometry";
|
|
|
|
const RECT_EPSILON = 0.01;
|
|
|
|
export function surfaceVisibleRect(
|
|
liveRect: ViewportRect,
|
|
observedRect: Rect | undefined,
|
|
): Rect | null {
|
|
const observed = observedRect ?? {
|
|
x: liveRect.x,
|
|
y: liveRect.y,
|
|
w: liveRect.width,
|
|
h: liveRect.height,
|
|
};
|
|
const x = Math.max(liveRect.x, observed.x);
|
|
const y = Math.max(liveRect.y, observed.y);
|
|
const right = Math.min(liveRect.x + liveRect.width, observed.x + observed.w);
|
|
const bottom = Math.min(liveRect.y + liveRect.height, observed.y + observed.h);
|
|
return right > x && bottom > y ? { x, y, w: right - x, h: bottom - y } : null;
|
|
}
|
|
|
|
export function sameRect(a: Rect, b: Rect): boolean {
|
|
return (
|
|
Math.abs(a.x - b.x) <= RECT_EPSILON &&
|
|
Math.abs(a.y - b.y) <= RECT_EPSILON &&
|
|
Math.abs(a.w - b.w) <= RECT_EPSILON &&
|
|
Math.abs(a.h - b.h) <= RECT_EPSILON
|
|
);
|
|
}
|
|
|
|
export function mapImagePointToViewport(
|
|
rect: Rect,
|
|
imageWidth: number,
|
|
imageHeight: number,
|
|
imageX: number,
|
|
imageY: number,
|
|
): Point | null {
|
|
if (
|
|
!Number.isFinite(imageX) ||
|
|
!Number.isFinite(imageY) ||
|
|
!Number.isSafeInteger(imageWidth) ||
|
|
!Number.isSafeInteger(imageHeight) ||
|
|
imageWidth <= 0 ||
|
|
imageHeight <= 0 ||
|
|
imageX < 0 ||
|
|
imageY < 0 ||
|
|
imageX >= imageWidth ||
|
|
imageY >= imageHeight ||
|
|
!Number.isFinite(rect.x) ||
|
|
!Number.isFinite(rect.y) ||
|
|
!Number.isFinite(rect.w) ||
|
|
!Number.isFinite(rect.h) ||
|
|
rect.w <= 0 ||
|
|
rect.h <= 0
|
|
) {
|
|
return null;
|
|
}
|
|
return {
|
|
x: rect.x + (imageX / imageWidth) * rect.w,
|
|
y: rect.y + (imageY / imageHeight) * rect.h,
|
|
};
|
|
}
|
|
|
|
function pointInPolygon(point: Point, polygon: Point[]): boolean {
|
|
for (let index = 0; index < polygon.length; index += 1) {
|
|
const a = polygon[index];
|
|
const b = polygon[(index + 1) % polygon.length];
|
|
const cross = (point.x - a.x) * (b.y - a.y) - (point.y - a.y) * (b.x - a.x);
|
|
const withinX = point.x >= Math.min(a.x, b.x) - 1e-9 && point.x <= Math.max(a.x, b.x) + 1e-9;
|
|
const withinY = point.y >= Math.min(a.y, b.y) - 1e-9 && point.y <= Math.max(a.y, b.y) + 1e-9;
|
|
if (Math.abs(cross) <= 1e-9 && withinX && withinY) return true;
|
|
}
|
|
let inside = false;
|
|
for (let index = 0, previous = polygon.length - 1; index < polygon.length; previous = index++) {
|
|
const a = polygon[index];
|
|
const b = polygon[previous];
|
|
const intersects =
|
|
a.y > point.y !== b.y > point.y &&
|
|
point.x < ((b.x - a.x) * (point.y - a.y)) / (b.y - a.y) + a.x;
|
|
if (intersects) inside = !inside;
|
|
}
|
|
return inside;
|
|
}
|
|
|
|
export function pointInRegion(point: Point, region: Region): boolean {
|
|
return region.some((polygon) => polygon.length >= 3 && pointInPolygon(point, polygon));
|
|
}
|