Files
codex-bot 38ec492ca3
wiki CI and deployment / verify-and-deploy (push) Successful in 16m28s
Publish reference layouts as API-authored text and image notes
2026-10-26 17:20:57 +08:00

19 lines
1.3 KiB
TypeScript

export type Box=[number,number,number,number];
export type TextRegion={box:Box;direction:string};
/** Associate residual glyphs with an existing semantic word/paragraph, never create a second word ID. */
export function textMergeTarget(fragment:Box,regions:TextRegion[]):number{
const[x,y,w,h]=fragment;let result=-1,best=Infinity;
for(const[index,r]of regions.entries()){
const[a,b,c,d]=r.box,vertical=r.direction==='vertical';
const gapX=Math.max(a-x-w,x-a-c,0),gapY=Math.max(b-y-h,y-b-d,0);
const overlapX=Math.max(0,Math.min(x+w,a+c)-Math.max(x,a)),overlapY=Math.max(0,Math.min(y+h,b+d)-Math.max(y,b));
const sameBaseline=overlapY/Math.max(1,Math.min(h,d))>=.55&&gapX<=Math.max(3,Math.min(7,d*.4));
const sameColumn=overlapX/Math.max(1,Math.min(w,c))>=.55&&gapY<=Math.max(2,Math.min(4,vertical?c*.3:2));
if(!(sameBaseline||sameColumn))continue;
// Nearby small footer blocks remain independent when the fragment has no baseline/column affinity.
const score=gapX+gapY+(vertical?(1-overlapX/Math.max(1,Math.min(w,c))):(1-overlapY/Math.max(1,Math.min(h,d))));
if(score<best){result=index;best=score}
}return result;
}
export function unionBoxes(a:Box,b:Box):Box{const x=Math.min(a[0],b[0]),y=Math.min(a[1],b[1]);return[x,y,Math.max(a[0]+a[2],b[0]+b[2])-x,Math.max(a[1]+a[3],b[1]+b[3])-y]}