11import type { Rect } from './snapshot.ts' ;
2+ import { findConnectedMaskComponents } from './screenshot-diff-components.ts' ;
23import type { ScreenshotOcrAnalysis , ScreenshotOcrBlock } from './screenshot-diff-ocr.ts' ;
34import type { ScreenshotDiffRegion } from './screenshot-diff-regions.ts' ;
5+ import {
6+ clamp ,
7+ expandRect ,
8+ intersectArea ,
9+ rectCenter ,
10+ squaredDistance ,
11+ unionRects ,
12+ } from './screenshot-geometry.ts' ;
413
514export type ScreenshotNonTextDelta = {
615 index : number ;
@@ -118,56 +127,37 @@ function findConnectedComponents(
118127 width : number ,
119128 height : number ,
120129) : MutableComponent [ ] {
121- const visited = new Uint8Array ( mask . length ) ;
122- const queue = new Int32Array ( mask . length ) ;
123- const components : MutableComponent [ ] = [ ] ;
124- for ( let pixelIndex = 0 ; pixelIndex < mask . length ; pixelIndex += 1 ) {
125- if ( mask [ pixelIndex ] !== 1 || visited [ pixelIndex ] === 1 ) continue ;
126- let queueStart = 0 ;
127- let queueEnd = 0 ;
128- queue [ queueEnd ] = pixelIndex ;
129- queueEnd += 1 ;
130- visited [ pixelIndex ] = 1 ;
131-
132- const startX = pixelIndex % width ;
133- const startY = Math . floor ( pixelIndex / width ) ;
134- const component : MutableComponent = {
135- minX : startX ,
136- minY : startY ,
137- maxX : startX ,
138- maxY : startY ,
139- differentPixels : 0 ,
140- } ;
130+ return findConnectedMaskComponents ( {
131+ mask,
132+ width,
133+ height,
134+ hooks : {
135+ create : ( pixelIndex ) => createComponent ( pixelIndex , width ) ,
136+ visit : ( component , pixelIndex ) => addPixelToComponent ( component , pixelIndex , width ) ,
137+ } ,
138+ } ) ;
139+ }
141140
142- while ( queueStart < queueEnd ) {
143- const currentIndex = queue [ queueStart ] ! ;
144- queueStart += 1 ;
145- const x = currentIndex % width ;
146- const y = Math . floor ( currentIndex / width ) ;
147- component . minX = Math . min ( component . minX , x ) ;
148- component . minY = Math . min ( component . minY , y ) ;
149- component . maxX = Math . max ( component . maxX , x ) ;
150- component . maxY = Math . max ( component . maxY , y ) ;
151- component . differentPixels += 1 ;
141+ function createComponent ( pixelIndex : number , width : number ) : MutableComponent {
142+ const startX = pixelIndex % width ;
143+ const startY = Math . floor ( pixelIndex / width ) ;
144+ return {
145+ minX : startX ,
146+ minY : startY ,
147+ maxX : startX ,
148+ maxY : startY ,
149+ differentPixels : 0 ,
150+ } ;
151+ }
152152
153- for ( let yOffset = - 1 ; yOffset <= 1 ; yOffset += 1 ) {
154- const neighborY = y + yOffset ;
155- if ( neighborY < 0 || neighborY >= height ) continue ;
156- for ( let xOffset = - 1 ; xOffset <= 1 ; xOffset += 1 ) {
157- if ( xOffset === 0 && yOffset === 0 ) continue ;
158- const neighborX = x + xOffset ;
159- if ( neighborX < 0 || neighborX >= width ) continue ;
160- const neighborIndex = neighborY * width + neighborX ;
161- if ( mask [ neighborIndex ] !== 1 || visited [ neighborIndex ] === 1 ) continue ;
162- visited [ neighborIndex ] = 1 ;
163- queue [ queueEnd ] = neighborIndex ;
164- queueEnd += 1 ;
165- }
166- }
167- }
168- components . push ( component ) ;
169- }
170- return components ;
153+ function addPixelToComponent ( component : MutableComponent , pixelIndex : number , width : number ) : void {
154+ const x = pixelIndex % width ;
155+ const y = Math . floor ( pixelIndex / width ) ;
156+ component . minX = Math . min ( component . minX , x ) ;
157+ component . minY = Math . min ( component . minY , y ) ;
158+ component . maxX = Math . max ( component . maxX , x ) ;
159+ component . maxY = Math . max ( component . maxY , y ) ;
160+ component . differentPixels += 1 ;
171161}
172162
173163function mergeNearbyComponents ( components : MutableComponent [ ] , gapPx : number ) : MutableComponent [ ] {
@@ -397,20 +387,6 @@ function findNearestText(
397387 return nearest ;
398388}
399389
400- function unionRects ( rects : Rect [ ] ) : Rect {
401- let minX = Number . POSITIVE_INFINITY ;
402- let minY = Number . POSITIVE_INFINITY ;
403- let maxX = Number . NEGATIVE_INFINITY ;
404- let maxY = Number . NEGATIVE_INFINITY ;
405- for ( const rect of rects ) {
406- minX = Math . min ( minX , rect . x ) ;
407- minY = Math . min ( minY , rect . y ) ;
408- maxX = Math . max ( maxX , rect . x + rect . width ) ;
409- maxY = Math . max ( maxY , rect . y + rect . height ) ;
410- }
411- return { x : minX , y : minY , width : maxX - minX , height : maxY - minY } ;
412- }
413-
414390function cleanOcrAnchorText ( text : string ) : string {
415391 return text
416392 . trim ( )
@@ -436,15 +412,6 @@ function componentToRect(component: MutableComponent): Rect {
436412 } ;
437413}
438414
439- function expandRect ( rect : Rect , padding : number ) : Rect {
440- return {
441- x : rect . x - padding ,
442- y : rect . y - padding ,
443- width : rect . width + padding * 2 ,
444- height : rect . height + padding * 2 ,
445- } ;
446- }
447-
448415function clearRect ( mask : Uint8Array , width : number , height : number , rect : Rect ) : void {
449416 const minX = clamp ( Math . floor ( rect . x ) , 0 , width - 1 ) ;
450417 const minY = clamp ( Math . floor ( rect . y ) , 0 , height - 1 ) ;
@@ -470,30 +437,9 @@ function componentsAreNear(
470437 ) ;
471438}
472439
473- function intersectArea ( left : Rect , right : Rect ) : number {
474- const minX = Math . max ( left . x , right . x ) ;
475- const minY = Math . max ( left . y , right . y ) ;
476- const maxX = Math . min ( left . x + left . width , right . x + right . width ) ;
477- const maxY = Math . min ( left . y + left . height , right . y + right . height ) ;
478- if ( maxX <= minX || maxY <= minY ) return 0 ;
479- return ( maxX - minX ) * ( maxY - minY ) ;
480- }
481-
482440function verticalOverlap ( left : Rect , right : Rect ) : number {
483441 return Math . max (
484442 0 ,
485443 Math . min ( left . y + left . height , right . y + right . height ) - Math . max ( left . y , right . y ) ,
486444 ) ;
487445}
488-
489- function rectCenter ( rect : Rect ) : { x : number ; y : number } {
490- return { x : rect . x + rect . width / 2 , y : rect . y + rect . height / 2 } ;
491- }
492-
493- function squaredDistance ( left : { x : number ; y : number } , right : { x : number ; y : number } ) : number {
494- return ( left . x - right . x ) ** 2 + ( left . y - right . y ) ** 2 ;
495- }
496-
497- function clamp ( value : number , min : number , max : number ) : number {
498- return Math . min ( Math . max ( value , min ) , max ) ;
499- }
0 commit comments