import { isNil } from 'lodash-es'; interface RangeTypeData { square: { x: number; y: number; d: number }; rect: { x: number; y: number; w: number; h: number }; } type InRangeFn, T> = (item: E, data: T) => boolean; export class Range { static rangeType: Record = {}; /** * 获取一个范围类型,并进行判断 * @param type 范围类型 */ type( type: T ): T extends keyof RangeTypeData ? RangeType : RangeType { return Range.rangeType[type] as T extends keyof RangeTypeData ? RangeType : RangeType; } /** * 注册一个新的范围类型 * @param type 范围类型 * @param fn 判断是否在范围内的函数 */ static register( type: K, fn: InRangeFn, RangeTypeData[K]> ): void; static register(type: string, fn: InRangeFn, any>): void; static register(type: string, fn: InRangeFn, any>): void { const range = new RangeType(type, fn); this.rangeType[type] = range; } } class RangeType { readonly type: string; /** * 判断一个元素是否在指定范围内 * @param item 要判断的元素 * @param data 范围数据 */ readonly inRange: InRangeFn, Type>; constructor(type: string, fn: InRangeFn, Type>) { this.type = type; this.inRange = fn; } /** * 扫描一个列表中所有在范围内的元素 * @param items 元素列表 * @param data 范围数据 */ scan>(items: Iterable, data: Type): T[] { const res: T[] = []; for (const ele of items) { if (this.inRange(ele, data)) { res.push(ele); } } return res; } } Range.register('square', (item, { x, y, d }) => { if (isNil(item.x) || isNil(item.y)) return false; const r = Math.floor(d / 2); return Math.abs(item.x - x) <= r && Math.abs(item.y - y) <= r; }); Range.register('rect', (item, { x, y, w, h }) => { if (isNil(item.x) || isNil(item.y)) return false; const ex = x + w; const ey = y + h; return item.x >= x && item.y >= y && item.x < ex && item.y < ey; });