@@ -13,13 +13,13 @@ import styles from './FovSimulator.module.css'
1313
1414export type { OverlayOffsets } from './canvasTypes'
1515
16- const REF_FOV = calcFOV ( 14 , 1.0 )
17-
18- export function Canvas ( { lenses, imageIndex, orientation, canvasRef, cleanCanvasRef, distance, showGuides, activeLens, offsets, onOffsetsChange, customImageSrc, sourceImageRef } : CanvasProps ) {
16+ export function Canvas ( { lenses, imageIndex, orientation, canvasRef, cleanCanvasRef, distance, showGuides, activeLens, offsets, onOffsetsChange, customImageSrc, sourceImageRef, sourceFocalLength } : CanvasProps ) {
1917 const imageRef = useRef < HTMLImageElement | null > ( null )
2018 const animFrameRef = useRef < number > ( 0 )
2119 const drawnRectsRef = useRef < Rect [ ] > ( [ ] )
2220
21+ const refFov = sourceFocalLength ? calcFOV ( sourceFocalLength , 1.0 ) : calcFOV ( 14 , 1.0 )
22+
2323 const fovs = lenses . map ( ( lens ) => calcFOV ( getSensor ( lens . sensorId ) . cropFactor , 1 ) . horizontal === 0 ? calcFOV ( lens . focalLength , 1 ) : calcFOV ( lens . focalLength , getSensor ( lens . sensorId ) . cropFactor ) )
2424
2525 const {
@@ -31,13 +31,13 @@ export function Canvas({ lenses, imageIndex, orientation, canvasRef, cleanCanvas
3131 const w = canvas . width ; const h = canvas . height
3232 const isPortrait = orientation === 'portrait'
3333 return fovs . map ( ( fov , i ) => {
34- const ratioW = calcCropRatio ( isPortrait ? fov . vertical : fov . horizontal , isPortrait ? REF_FOV . vertical : REF_FOV . horizontal )
35- const ratioH = calcCropRatio ( isPortrait ? fov . horizontal : fov . vertical , isPortrait ? REF_FOV . horizontal : REF_FOV . vertical )
34+ const ratioW = calcCropRatio ( isPortrait ? fov . vertical : fov . horizontal , isPortrait ? refFov . vertical : refFov . horizontal )
35+ const ratioH = calcCropRatio ( isPortrait ? fov . horizontal : fov . vertical , isPortrait ? refFov . horizontal : refFov . vertical )
3636 const rw = w * ratioW ; const rh = h * ratioH
3737 const off = offsets [ i ] ?? { dx : 0 , dy : 0 }
3838 return { x : ( w - rw ) / 2 + off . dx , y : ( h - rh ) / 2 + off . dy , w : rw , h : rh , color : LENS_COLORS [ i ] , label : LENS_LABELS [ i ] , index : i , focalLength : lenses [ i ] . focalLength , fov }
3939 } )
40- } , [ fovs , lenses , offsets , orientation ] )
40+ } , [ fovs , lenses , offsets , orientation , refFov ] )
4141
4242 const draw = useCallback ( ( ) => {
4343 const canvas = canvasRef . current ; const img = imageRef . current
@@ -52,15 +52,25 @@ export function Canvas({ lenses, imageIndex, orientation, canvasRef, cleanCanvas
5252
5353 const rects = computeRects ( canvas )
5454 if ( rects . length === 0 ) return
55- drawOverlayBorders ( ctx , rects , w , h , dpr )
5655
57- if ( showGuides && rects [ activeLens ] ) {
56+ const widerIndices = new Set < number > ( )
57+ for ( let i = 0 ; i < rects . length ; i ++ ) {
58+ if ( rects [ i ] . w > w * 1.01 || rects [ i ] . h > h * 1.01 ) widerIndices . add ( i )
59+ }
60+
61+ const visibleRects = rects . filter ( ( _ , i ) => ! widerIndices . has ( i ) )
62+ if ( visibleRects . length > 0 ) drawOverlayBorders ( ctx , visibleRects , w , h , dpr )
63+
64+ if ( showGuides && rects [ activeLens ] && ! widerIndices . has ( activeLens ) ) {
5865 const activeFov = fovs [ activeLens ]
5966 const verticalFOV = orientation === 'portrait' ? activeFov . horizontal : activeFov . vertical
6067 drawFramingGuides ( ctx , rects [ activeLens ] , calcFrameWidth ( verticalFOV , distance ) , dpr )
6168 }
6269
63- drawLensLabels ( ctx , rects , lenses , dpr )
70+ drawLensLabels ( ctx , visibleRects , lenses , dpr )
71+
72+ if ( widerIndices . size > 0 ) drawWiderLabels ( ctx , rects , widerIndices , dpr )
73+
6474 drawnRectsRef . current = rects
6575 cleanCanvas ?. dispatchEvent ( new Event ( 'draw' ) )
6676 } , [ canvasRef , cleanCanvasRef , computeRects , showGuides , activeLens , distance , fovs , orientation , lenses ] )
@@ -162,3 +172,23 @@ function drawLensLabels(ctx: CanvasRenderingContext2D, rects: Rect[], lenses: Le
162172 }
163173 }
164174}
175+
176+ function drawWiderLabels ( ctx : CanvasRenderingContext2D , rects : Rect [ ] , widerIndices : Set < number > , dpr : number ) {
177+ const fontSize = 13 * dpr
178+ ctx . font = `600 ${ fontSize } px -apple-system, BlinkMacSystemFont, sans-serif`
179+ const padX = 10 * dpr ; const padY = 6 * dpr
180+ let yOffset = 0
181+ for ( const i of widerIndices ) {
182+ const r = rects [ i ]
183+ const text = `${ LENS_LABELS [ i ] } — ${ r . focalLength } mm — wider than source`
184+ const textW = ctx . measureText ( text ) . width
185+ const pillW = textW + padX * 2 ; const pillH = fontSize + padY * 2
186+ const px = ( ctx . canvas . width - pillW ) / 2
187+ const py = 16 * dpr + yOffset
188+ ctx . fillStyle = 'rgba(0, 0, 0, 0.6)'
189+ ctx . beginPath ( ) ; ctx . roundRect ( px , py , pillW , pillH , 4 * dpr ) ; ctx . fill ( )
190+ ctx . fillStyle = r . color
191+ ctx . fillText ( text , px + padX , py + padY + fontSize * 0.8 )
192+ yOffset += pillH + 4 * dpr
193+ }
194+ }
0 commit comments