import { BoundsKeyX, BoundsKeyY, createResizeTransform, CreateResizeTransformOptions, Point, } from "@noya-app/noya-geometry"; import { getResizePositionForBounds } from "@noya-app/noya-geometry"; import { Rect } from "@noya-app/noya-geometry"; import { BaseCanvasData, getCanvasTransform } from "../plugins/baseCanvas"; type GetViewportOriginOptions = { data: BaseCanvasData; rect: Rect; /** percentage of overlap to consider as "within viewport" */ overlapThreshold?: number; } & Omit; type SingleAxisLineSegment = [number, number]; function calculateLineOverlap( line1: SingleAxisLineSegment, line2: SingleAxisLineSegment ) { const [start1, end1] = line1; const [start2, end2] = line2; const overlap = Math.min(end1, end2) - Math.max(start1, start2); return overlap; } export function getViewportOriginContainingRect({ data, rect: rectToContain, overlapThreshold = 1, ...options }: GetViewportOriginOptions) { const { x: xOverlapFraction, y: yOverlapFraction } = getRectOverlapFraction({ data, rect: rectToContain, }); const canvasCenterPoint = { x: data.size.width / 2, y: data.size.height / 2, }; const newRectCenterPoint = { x: rectToContain.x + rectToContain.width / 2, y: rectToContain.y + rectToContain.height / 2, }; const originTransform = getCanvasTransform(data); const translatedRectCenterPoint = originTransform.applyTo(newRectCenterPoint); const xAnchor: BoundsKeyX = translatedRectCenterPoint.x < canvasCenterPoint.x ? "minX" : "maxX"; const yAnchor: BoundsKeyY = translatedRectCenterPoint.y < canvasCenterPoint.y ? "minY" : "maxY"; const resizePosition = getResizePositionForBounds({ x: xAnchor, y: yAnchor, }); const transform = createResizeTransform(rectToContain, data.size, { ...options, resizePosition, }); return { x: xOverlapFraction < overlapThreshold ? transform.tx : data.origin.x, y: yOverlapFraction < overlapThreshold ? transform.ty : data.origin.y, }; } export function isRectInViewport({ data, rect: rectToContain, fractionOfRectVisible = 1, }: { data: BaseCanvasData; rect: Rect; fractionOfRectVisible?: number; }) { const { x: xOverlapFraction, y: yOverlapFraction } = getRectOverlapFraction({ data, rect: rectToContain, }); return ( xOverlapFraction >= fractionOfRectVisible && yOverlapFraction >= fractionOfRectVisible ); } function getRectOverlapFraction({ data, rect: rectToContain, }: { data: BaseCanvasData; rect: Rect; }): Point { const originTransform = getCanvasTransform(data); const translatedRect = originTransform.applyTo(rectToContain); const xOverlap = calculateLineOverlap( [translatedRect.x, translatedRect.x + translatedRect.width], [0, data.size.width] ); const xOverlapFraction = xOverlap / translatedRect.width; const yOverlap = calculateLineOverlap( [translatedRect.y, translatedRect.y + translatedRect.height], [0, data.size.height] ); const yOverlapFraction = yOverlap / translatedRect.height; return { x: xOverlapFraction, y: yOverlapFraction, }; }