@@ -2503,121 +2503,123 @@ var JSONResponse = {
25032503
25042504 // Draw bboxes
25052505 var bboxes = JSONResponse . getBboxes ( detection ) || [ ]
2506- bboxes ?. forEach ( ( item , index ) => {
2507- const isHovered = index === hoverBoxId ;
2508- const visible = ! visiblePaths || visiblePaths . length <= 0 || visiblePaths . includes ( item . path || item . id ) ;
2509- if ( ! visible ) {
2510- return ;
2511- }
2512-
2513- var [ x , y , w , h , d ] = JSONResponse . getXYWHD ( JSONResponse . getBbox ( item ) || [ ] ) ;
2514- const isRate = Math . abs ( x ) < 1 && Math . abs ( y ) < 1 && Math . abs ( w ) < 1 && Math . abs ( h ) < 1 ;
2515- x = isRate ? x * width : x * xRate ;
2516- y = isRate ? y * height : y * yRate ;
2517- w = isRate ? w * width : w * xRate ;
2518- h = isRate ? h * height : h * yRate ;
2519- const angle = item . degree || item . rotate || item . angle || item . perspective || d || 0 ;
2520-
2521- var color = item . color ;
2522- if ( options . styleOverride ) {
2523- const override = options . styleOverride ( item , item [ '@before' ] ) ;
2524- if ( override && override . color ) {
2525- color = override . color ;
2526- }
2527- }
2528-
2529- const [ r , g , b , a ] = color || [ 0 , 255 , 0 , 255 ] ;
2530- const rgba = `rgba(${ r } , ${ g } , ${ b } , ${ hoverBoxId != null || ! isHovered ? 0.3 : Math . min ( 0.5 , a < 1 ? a : a / 255 ) } )` ;
2531-
2532- const reversedRgba = `rgba(${ 255 - r } , ${ 255 - g } , ${ 255 - b } ` , $ { isHovered || hoverBoxId == null ? 1 : 0.3 } ) `;
2533- // const luma = 0.299 * r + 0.587 * g + 0.114 * b;
2534- const backgroundFill = rgba; // 还是有些看不清 luma > 186 ? 'rgba(0, 0, 0, 0.5)' : 'rgba(255, 255, 255, 0.5)';
2535-
2536- ctx.strokeStyle = isHovered ? reversedRgba : rgba;
2537- ctx.fillStyle = rgba;
2538-
2539- // Draw horizontal box
2540- ctx.strokeRect(x, y, w, h);
2541-
2542- // Optionally draw rotated box
2543- if (rotateBoxes && angle !== 0) {
2544- ctx.save();
2545- ctx.translate(x + w / 2, y + h / 2);
2546- ctx.rotate((angle * Math.PI) / 180);
2547- ctx.strokeRect(-w / 2, -h / 2, w, h);
2548- ctx.restore();
2549- }
2550-
2551- if (hoverBoxId != null && ! isHovered) {
2552- return
2553- }
2554-
2555- // Label
2556- const label = (isDiff ? (item['@before'] ? '- ' : '+ ') : '') + ` $ { item . ocr || item . label || '' } - $ { item . id || '' } ${( ( JSONResponse . getScore ( item ) || 0 ) * 100 ) . toFixed ( 0 ) } % $ { angle == 0 ? '' : ' ' + Math . round ( angle ) + '°' } `;
2557- // ctx.font = 'bold 36px';
2558- // const size = ctx.measureText(label);
2559- // const textHeight = size.height || height*0.1; // Math.max(height*0.1, size.height);
2560- // 让字号约为 canvas 高度的 2%,并限定 12~48px
2561- ctx . font = `bold ${ fontSize } px sans-serif` ;
2562- const size = ctx . measureText ( label ) ;
2563- // 自动从 font 里提取 px 字号
2564- const fontMatch = ctx . font . match ( / ( \d + ) p x / ) ;
2565- const textHeight = fontMatch ? parseInt ( fontMatch [ 1 ] ) : 36 ; // fallback 到 36px
2566- const textWidth = size . width ; // *textHeight/size.height;
2567- const padding = 2 ;
2568-
2569- let positions = [
2570- [ x , y - textHeight - padding ] ,
2571- [ x + w - textWidth , y - textHeight - padding ] ,
2572- [ x , y + h + padding ] ,
2573- [ x + w - textWidth , y + h + padding ]
2574- ] ;
2575-
2576- let labelX = x , labelY = y - textHeight - padding ;
2577- for ( const [ lx , ly ] of positions ) {
2578- const overlaps = placedLabels . some ( ( { x : ox , y : oy , w : ow , h : oh } ) =>
2579- lx < ox + ow && lx + textWidth > ox && ly < oy + oh && ly + textHeight > oy
2580- ) ;
2581- if ( ! overlaps && lx >= 0 && ly >= 0 && lx + textWidth <= canvas . width && ly + textHeight <= canvas . height ) {
2582- labelX = lx ;
2583- labelY = ly ;
2584- break ;
2585- }
2586- }
2506+ if ( bboxes instanceof Array ) {
2507+ bboxes ?. forEach ( ( item , index ) => {
2508+ const isHovered = index === hoverBoxId ;
2509+ const visible = ! visiblePaths || visiblePaths . length <= 0 || visiblePaths . includes ( item . path || item . id ) ;
2510+ if ( ! visible ) {
2511+ return ;
2512+ }
25872513
2588- placedLabels . push ( { x : labelX , y : labelY , w : textWidth , h : textHeight } ) ;
2514+ var [ x , y , w , h , d ] = JSONResponse . getXYWHD ( JSONResponse . getBbox ( item ) || [ ] ) ;
2515+ const isRate = Math . abs ( x ) < 1 && Math . abs ( y ) < 1 && Math . abs ( w ) < 1 && Math . abs ( h ) < 1 ;
2516+ x = isRate ? x * width : x * xRate ;
2517+ y = isRate ? y * height : y * yRate ;
2518+ w = isRate ? w * width : w * xRate ;
2519+ h = isRate ? h * height : h * yRate ;
2520+ const angle = item . degree || item . rotate || item . angle || item . perspective || d || 0 ;
2521+
2522+ var color = item . color ;
2523+ if ( options . styleOverride ) {
2524+ const override = options . styleOverride ( item , item [ '@before' ] ) ;
2525+ if ( override && override . color ) {
2526+ color = override . color ;
2527+ }
2528+ }
25892529
2590- ctx . save ( ) ;
2591- if ( rotateText && angle !== 0 ) {
2592- ctx . translate ( labelX + textWidth / 2 , labelY + textHeight / 2 ) ;
2593- ctx . rotate ( ( angle * Math . PI ) / 180 ) ;
2594- ctx . translate ( - textWidth / 2 , - textHeight / 2 ) ;
2595- labelX = 0 ;
2596- labelY = 0 ;
2597- }
2530+ const [ r , g , b , a ] = color || [ 0 , 255 , 0 , 255 ] ;
2531+ const rgba = `rgba(${ r } , ${ g } , ${ b } , ${ hoverBoxId != null || ! isHovered ? 0.3 : Math . min ( 0.5 , a < 1 ? a : a / 255 ) } )` ;
25982532
2599- if ( showLabelBackground ) {
2600- ctx . fillStyle = backgroundFill ;
2601- ctx . fillRect ( labelX - 2 , labelY - 1 , textWidth + 4 , textHeight + 2 ) ;
2602- }
2533+ const reversedRgba = `rgba(${ 255 - r } , ${ 255 - g } , ${ 255 - b } , ${ isHovered || hoverBoxId == null ? 1 : 0.3 } )` ;
2534+ // const luma = 0.299 * r + 0.587 * g + 0.114 * b;
2535+ const backgroundFill = rgba ; // 还是有些看不清 luma > 186 ? 'rgba(0, 0, 0, 0.5)' : 'rgba(255, 255, 255, 0.5)';
26032536
2604- ctx . fillStyle = showLabelBackground ? reversedRgba : rgba ;
2605- ctx . fillText ( label , labelX , labelY ) ;
2606- ctx . restore ( ) ;
2537+ ctx . strokeStyle = isHovered ? reversedRgba : rgba ;
2538+ ctx . fillStyle = rgba ;
26072539
2608- if ( markable && item [ '@before' ] != true ) {
2609- const isWrong = wrongs . indexOf ( isDiff ? item [ '@index' ] : index ) >= 0 ; // item.correct === false;
2610- // 绘制 √ 和 ×
2611- ctx . font = `bold ${ fontSize } px sans-serif` ;
2612- // ctx.fillStyle = isWrong ? 'red' : 'green';
2613- ctx . fillStyle = isWrong ? 'red' : 'green' ;
2614- const checkX = labelX + textWidth + 4 ;
2615- const checkY = labelY ;
2616- ctx . fillText ( isWrong ? '×' : '√' , checkX , checkY ) ;
2617- }
2540+ // Draw horizontal box
2541+ ctx . strokeRect ( x , y , w , h ) ;
26182542
2619- JSONResponse . drawDetections ( canvas , item , options , img , ctx ) ;
2620- } ) ;
2543+ // Optionally draw rotated box
2544+ if ( rotateBoxes && angle !== 0 ) {
2545+ ctx . save ( ) ;
2546+ ctx . translate ( x + w / 2 , y + h / 2 ) ;
2547+ ctx . rotate ( ( angle * Math . PI ) / 180 ) ;
2548+ ctx . strokeRect ( - w / 2 , - h / 2 , w , h ) ;
2549+ ctx . restore ( ) ;
2550+ }
2551+
2552+ if ( hoverBoxId != null && ! isHovered ) {
2553+ return
2554+ }
2555+
2556+ // Label
2557+ const label = ( isDiff ? ( item [ '@before' ] ? '- ' : '+ ' ) : '' ) + `${ item . ocr || item . label || '' } -${ item . id || '' } ${ ( ( JSONResponse . getScore ( item ) || 0 ) * 100 ) . toFixed ( 0 ) } %${ angle == 0 ? '' : ' ' + Math . round ( angle ) + '°' } ` ;
2558+ // ctx.font = 'bold 36px';
2559+ // const size = ctx.measureText(label);
2560+ // const textHeight = size.height || height*0.1; // Math.max(height*0.1, size.height);
2561+ // 让字号约为 canvas 高度的 2%,并限定 12~48px
2562+ ctx . font = `bold ${ fontSize } px sans-serif` ;
2563+ const size = ctx . measureText ( label ) ;
2564+ // 自动从 font 里提取 px 字号
2565+ const fontMatch = ctx . font . match ( / ( \d + ) p x / ) ;
2566+ const textHeight = fontMatch ? parseInt ( fontMatch [ 1 ] ) : 36 ; // fallback 到 36px
2567+ const textWidth = size . width ; // *textHeight/size.height;
2568+ const padding = 2 ;
2569+
2570+ let positions = [
2571+ [ x , y - textHeight - padding ] ,
2572+ [ x + w - textWidth , y - textHeight - padding ] ,
2573+ [ x , y + h + padding ] ,
2574+ [ x + w - textWidth , y + h + padding ]
2575+ ] ;
2576+
2577+ let labelX = x , labelY = y - textHeight - padding ;
2578+ for ( const [ lx , ly ] of positions ) {
2579+ const overlaps = placedLabels . some ( ( { x : ox , y : oy , w : ow , h : oh } ) =>
2580+ lx < ox + ow && lx + textWidth > ox && ly < oy + oh && ly + textHeight > oy
2581+ ) ;
2582+ if ( ! overlaps && lx >= 0 && ly >= 0 && lx + textWidth <= canvas . width && ly + textHeight <= canvas . height ) {
2583+ labelX = lx ;
2584+ labelY = ly ;
2585+ break ;
2586+ }
2587+ }
2588+
2589+ placedLabels . push ( { x : labelX , y : labelY , w : textWidth , h : textHeight } ) ;
2590+
2591+ ctx . save ( ) ;
2592+ if ( rotateText && angle !== 0 ) {
2593+ ctx . translate ( labelX + textWidth / 2 , labelY + textHeight / 2 ) ;
2594+ ctx . rotate ( ( angle * Math . PI ) / 180 ) ;
2595+ ctx . translate ( - textWidth / 2 , - textHeight / 2 ) ;
2596+ labelX = 0 ;
2597+ labelY = 0 ;
2598+ }
2599+
2600+ if ( showLabelBackground ) {
2601+ ctx . fillStyle = backgroundFill ;
2602+ ctx . fillRect ( labelX - 2 , labelY - 1 , textWidth + 4 , textHeight + 2 ) ;
2603+ }
2604+
2605+ ctx . fillStyle = showLabelBackground ? reversedRgba : rgba ;
2606+ ctx . fillText ( label , labelX , labelY ) ;
2607+ ctx . restore ( ) ;
2608+
2609+ if ( markable && item [ '@before' ] != true ) {
2610+ const isWrong = wrongs . indexOf ( isDiff ? item [ '@index' ] : index ) >= 0 ; // item.correct === false;
2611+ // 绘制 √ 和 ×
2612+ ctx . font = `bold ${ fontSize } px sans-serif` ;
2613+ // ctx.fillStyle = isWrong ? 'red' : 'green';
2614+ ctx . fillStyle = isWrong ? 'red' : 'green' ;
2615+ const checkX = labelX + textWidth + 4 ;
2616+ const checkY = labelY ;
2617+ ctx . fillText ( isWrong ? '×' : '√' , checkX , checkY ) ;
2618+ }
2619+
2620+ JSONResponse . drawDetections ( canvas , item , options , img , ctx ) ;
2621+ } ) ;
2622+ }
26212623
26222624 // Draw lines
26232625 var lines = JSONResponse . getLines ( detection ) ;
0 commit comments