@@ -10,8 +10,6 @@ import { calcFOV, calcCropRatio } from '@/lib/math/fov'
1010import { getSensor } from '@/lib/data/sensors'
1111import styles from './CropStrip.module.css'
1212
13- const REF_FOV = calcFOV ( 14 , 1.0 )
14-
1513/**
1614 * Compute the cover-fit source region for a given image drawn into a canvas.
1715 * Returns the visible region of the source image (sx, sy, sw, sh)
@@ -49,9 +47,10 @@ interface CropThumbProps {
4947 offset : { dx : number ; dy : number }
5048 cleanCanvasRef : React . RefObject < HTMLCanvasElement | null >
5149 sourceImageRef : React . RefObject < HTMLImageElement | null >
50+ sourceFocalLength ?: number | null
5251}
5352
54- function CropThumb ( { lens, orientation, color, lensIndex, onSelect, offset, cleanCanvasRef, sourceImageRef } : CropThumbProps ) {
53+ function CropThumb ( { lens, orientation, color, lensIndex, onSelect, offset, cleanCanvasRef, sourceImageRef, sourceFocalLength } : CropThumbProps ) {
5554 const canvasRef = useRef < HTMLCanvasElement > ( null )
5655
5756 const renderRef = useRef < ( ) => void > ( ( ) => { } )
@@ -82,15 +81,29 @@ function CropThumb({ lens, orientation, color, lensIndex, onSelect, offset, clea
8281 const mainW = mainCanvas . width
8382 const mainH = mainCanvas . height
8483
84+ const refFov = sourceFocalLength ? calcFOV ( sourceFocalLength , 1.0 ) : calcFOV ( 14 , 1.0 )
85+
8586 const ratioW = calcCropRatio (
8687 isPortrait ? fov . vertical : fov . horizontal ,
87- isPortrait ? REF_FOV . vertical : REF_FOV . horizontal ,
88+ isPortrait ? refFov . vertical : refFov . horizontal ,
8889 )
8990 const ratioH = calcCropRatio (
9091 isPortrait ? fov . horizontal : fov . vertical ,
91- isPortrait ? REF_FOV . horizontal : REF_FOV . vertical ,
92+ isPortrait ? refFov . horizontal : refFov . vertical ,
9293 )
9394
95+ if ( ratioW > 1.01 || ratioH > 1.01 ) {
96+ const fontSize = 11 * dpr
97+ ctx . font = `600 ${ fontSize } px -apple-system, BlinkMacSystemFont, sans-serif`
98+ ctx . fillStyle = 'rgba(0,0,0,0.4)'
99+ ctx . fillRect ( 0 , 0 , canvas . width , canvas . height )
100+ ctx . fillStyle = color
101+ const text = `${ lens . focalLength } mm — wider`
102+ const textW = ctx . measureText ( text ) . width
103+ ctx . fillText ( text , ( canvas . width - textW ) / 2 , ( canvas . height + fontSize ) / 2 )
104+ return
105+ }
106+
94107 // Rect position in canvas coordinates
95108 const rectW = mainW * ratioW
96109 const rectH = mainH * ratioH
@@ -116,7 +129,7 @@ function CropThumb({ lens, orientation, color, lensIndex, onSelect, offset, clea
116129
117130 renderRef . current = render
118131
119- useEffect ( render , [ lens , orientation , offset , cleanCanvasRef , sourceImageRef ] )
132+ useEffect ( render , [ lens , orientation , offset , cleanCanvasRef , sourceImageRef , sourceFocalLength , color ] )
120133
121134 useEffect ( ( ) => {
122135 const cleanCanvas = cleanCanvasRef . current
@@ -150,9 +163,10 @@ interface CropStripProps {
150163 onToggleExpand : ( ) => void
151164 cleanCanvasRef : React . RefObject < HTMLCanvasElement | null >
152165 sourceImageRef : React . RefObject < HTMLImageElement | null >
166+ sourceFocalLength ?: number | null
153167}
154168
155- export function CropStrip ( { lenses, orientation, activeLens, onSelectLens, offsets, expanded, onToggleExpand, cleanCanvasRef, sourceImageRef } : CropStripProps ) {
169+ export function CropStrip ( { lenses, orientation, activeLens, onSelectLens, offsets, expanded, onToggleExpand, cleanCanvasRef, sourceImageRef, sourceFocalLength } : CropStripProps ) {
156170 const t = useTranslations ( 'toolUI.fov-simulator' )
157171 return (
158172 < div className = { `${ styles . strip } ${ expanded ? styles . stripExpanded : '' } ` } >
@@ -176,6 +190,7 @@ export function CropStrip({ lenses, orientation, activeLens, onSelectLens, offse
176190 offset = { offsets [ i ] ?? { dx : 0 , dy : 0 } }
177191 cleanCanvasRef = { cleanCanvasRef }
178192 sourceImageRef = { sourceImageRef }
193+ sourceFocalLength = { sourceFocalLength }
179194 />
180195 ) ) }
181196 </ div >
0 commit comments