11import { Grating } from "../shared/slit.js" ;
22import { i2h , interpolate , w2h } from "../utils/color.js" ;
33
4- // ily please work
4+ // this is the very last attempt it will work
55
66class GratingFFTSimulation {
77 constructor ( cvs , ctx , density = 1000 , wavelength = 500e-9 , slitWidth = 2e-6 , distanceToScreen = 2.0 ) {
@@ -133,52 +133,26 @@ class GratingFFTSimulation {
133133 return spec ;
134134 }
135135
136- // Calculate discrete diffraction orders using simplified approach
137- // Peak width depends on number of slits and distance
138136 calculateDiffractionOrders ( ) {
139- const d = 1e-3 / this . density ; // grating spacing in meters
140137 const orders = [ ] ;
141-
142- // Fixed number of orders to always display
143138 const ordersToShow = [ - 7 , - 6 , - 5 , - 4 , - 3 , - 2 , - 1 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
144139
145140 for ( const m of ordersToShow ) {
146- const sinTheta = m * this . wavelength / d ;
147-
148- // Even if sinTheta > 1 (physically impossible), we'll place it for visibility
149- let theta ;
150- if ( Math . abs ( sinTheta ) <= 1 ) {
151- theta = Math . asin ( sinTheta ) ;
152- } else {
153- // Extrapolate beyond physical limits for visualization
154- theta = Math . sign ( sinTheta ) * Math . PI / 2 * Math . min ( Math . abs ( sinTheta ) , 3 ) ;
155- }
156-
157- // Position on screen - scale to ensure visibility
158- // Spacing depends on wavelength: longer wavelength = wider spacing
159- // Using 500nm as baseline wavelength
160141 const wavelengthFactor = this . wavelength / 500e-9 ;
161- const baseSpacing = 120 * wavelengthFactor ; // spacing scales with wavelength
142+ const baseSpacing = 120 * Math . pow ( wavelengthFactor , 0.5 ) ;
162143 const xPos = this . cvs . width / 2 + m * baseSpacing * ( this . distanceToScreen / 1.5 ) ;
163144
164- // Calculate envelope intensity for this position - ALL orders touch envelope
165145 const centerX = this . cvs . width / 2 ;
166146 const dx = ( xPos - centerX ) / ( this . cvs . width * 0.3 ) ;
167147 const envelopeIntensity = Math . exp ( - dx * dx ) ;
168148
169- // All orders match envelope height
170- const intensity = envelopeIntensity ;
171-
172- // Peak width depends on:
173- // 1. Number of slits (more slits = narrower peaks) - increased sensitivity
174- // 2. Distance (farther = wider peaks due to diffraction spreading) - increased sensitivity
175149 const effectiveSlits = this . illuminatedWidthPx / ( 1000 / this . density ) ;
176- const slitFactor = 50 / Math . sqrt ( effectiveSlits ) ; // Increased from 30 for more pronounced effect
177- const distanceFactor = Math . pow ( this . distanceToScreen / 1.0 , 1.2 ) ; // Increased exponent and adjusted baseline for more visible change
178- const peakWidth = Math . max ( 3 , slitFactor * distanceFactor ) ;
150+ const slitFactor = 20 / Math . sqrt ( effectiveSlits ) ;
151+ const distanceFactor = Math . pow ( this . distanceToScreen / 1.0 , 0.6 ) ;
152+ const peakWidth = Math . max ( 2 , slitFactor * distanceFactor ) ;
179153
180154 if ( xPos >= - 50 && xPos < this . cvs . width + 50 ) {
181- orders . push ( { order : m , x : xPos , intensity : intensity , width : peakWidth } ) ;
155+ orders . push ( { order : m , x : xPos , intensity : envelopeIntensity , width : peakWidth } ) ;
182156 }
183157 }
184158
@@ -190,21 +164,15 @@ class GratingFFTSimulation {
190164 const screenIntensity = new Float32Array ( this . cvs . width ) ;
191165 screenIntensity . fill ( 0 ) ;
192166
193- // Use discrete orders with simplified positioning
194167 this . diffractionOrders = this . calculateDiffractionOrders ( ) ;
195168
196- // Apply density effect: higher density = wider spacing
197- const densityFactor = this . density / 700 ; // normalized to 700 lines/mm baseline
198-
199- // Apply distance effect: farther = wider spacing (very subtle - only 10% range)
200- // Scale from 1.0 to 2.0 meters -> factor from 0.95 to 1.05 (10% range)
201- const distanceFactor = 0.95 + ( this . distanceToScreen - 1.0 ) * 0.1 ;
169+ const densityFactor = Math . pow ( this . density / 700 , 0.5 ) ;
170+ const distanceFactor = 0.95 + ( this . distanceToScreen - 1.0 ) * 0.05 ;
202171
203172 for ( const order of this . diffractionOrders ) {
204- // Adjust position based on density and distance
205173 const adjustedX = this . cvs . width / 2 + ( order . x - this . cvs . width / 2 ) * densityFactor * distanceFactor ;
206174
207- const width = order . width ; // Use calculated width based on number of slits
175+ const width = order . width ;
208176 for ( let dx = - width * 2 ; dx <= width * 2 ; dx ++ ) {
209177 const x = Math . round ( adjustedX + dx ) ;
210178 if ( x >= 0 && x < this . cvs . width ) {
@@ -214,13 +182,11 @@ class GratingFFTSimulation {
214182 }
215183 }
216184
217- // Store adjusted positions for rendering
218185 this . diffractionOrders = this . diffractionOrders . map ( order => ( {
219186 ...order ,
220187 x : this . cvs . width / 2 + ( order . x - this . cvs . width / 2 ) * densityFactor * distanceFactor
221188 } ) ) ;
222189
223- // normalize
224190 let max = 0 ;
225191 for ( let i = 0 ; i < screenIntensity . length ; i ++ ) if ( screenIntensity [ i ] > max ) max = screenIntensity [ i ] ;
226192 if ( max > 0 ) for ( let i = 0 ; i < screenIntensity . length ; i ++ ) screenIntensity [ i ] /= max ;
@@ -273,28 +239,21 @@ class GratingFFTSimulation {
273239 const centerX = this . cvs . width / 2 ;
274240 const normalizedX = ( x - centerX ) / envelopeWidth ;
275241
276- // Use sinc function to create proper zeros between lobes
277- // sinc(x) = sin(πx)/(πx), and intensity is sinc²(x)
278- const beta = normalizedX * 1.5 ; // Reduced from 1.8 to make lobes wider
242+ const beta = normalizedX * 1.8 ;
279243
280244 let sincValue ;
281245 if ( Math . abs ( beta ) < 0.001 ) {
282- sincValue = 1 ; // Limit as beta approaches 0
246+ sincValue = 1 ;
283247 } else {
284248 sincValue = Math . sin ( Math . PI * beta ) / ( Math . PI * beta ) ;
285249 }
286250
287- // Square the sinc to get intensity, and scale to get proper side lobe heights
288251 let intensity = sincValue * sincValue ;
289252
290- // Scale to make side lobes about 50% of main lobe
291- // Natural sinc² gives ~4.5% for first side lobe, so we boost it
292253 if ( Math . abs ( beta ) > 1.0 && Math . abs ( beta ) < 2.0 ) {
293- // First side lobes
294- intensity *= 11 ; // Boost to ~50%
254+ intensity *= 3 ;
295255 } else if ( Math . abs ( beta ) >= 2.0 && Math . abs ( beta ) < 3.0 ) {
296- // Second side lobes
297- intensity *= 8 ; // Boost to ~35%
256+ intensity *= 2 ;
298257 }
299258
300259 return Math . max ( 0 , intensity ) ;
@@ -305,9 +264,9 @@ class GratingFFTSimulation {
305264 const envelopeIntensity = singleSlitEnvelope ( order . x ) ;
306265 return {
307266 x : order . x ,
308- height : envelopeIntensity * maxHeight * 0.98 , // Scale to 98% to stay below envelope
267+ height : envelopeIntensity * maxHeight * 0.98 ,
309268 order : order . order ,
310- envelopeHeight : envelopeIntensity * maxHeight // Store full envelope height for comparison
269+ envelopeHeight : envelopeIntensity * maxHeight
311270 } ;
312271 } ) ;
313272
@@ -333,7 +292,7 @@ class GratingFFTSimulation {
333292 // Helper function to clamp y value to envelope
334293 const clampToEnvelope = ( x , y ) => {
335294 const envelopeY = screenY - singleSlitEnvelope ( x ) * maxHeight ;
336- return Math . max ( y , envelopeY ) ; // Don't go above envelope (lower y = higher on screen)
295+ return Math . max ( y , envelopeY ) ;
337296 } ;
338297
339298 // Draw smooth sinusoidal curve through each peak
@@ -345,15 +304,14 @@ class GratingFFTSimulation {
345304 // Calculate spacing for first peak
346305 const firstSpacing = nextPeak ? ( nextPeak . x - currentPeak . x ) : ( currentPeak . x - startX ) ;
347306
348- // Sharp rise approaching first peak - sample and clamp points along the curve
307+ // Sharp rise approaching first peak
349308 const approachX = currentPeak . x - firstSpacing * 0.06 ;
350309 const numSamples = 20 ;
351310
352311 for ( let s = 0 ; s <= numSamples ; s ++ ) {
353312 const t = s / numSamples ;
354313 const x = startX + t * ( approachX - startX ) ;
355314
356- // Bezier calculation
357315 const controlX1 = startX + ( approachX - startX ) * 0.7 ;
358316 const controlY1 = screenY ;
359317 const controlX2 = approachX - ( approachX - startX ) * 0.1 ;
@@ -368,7 +326,7 @@ class GratingFFTSimulation {
368326 else ctx . lineTo ( bx , clampedY ) ;
369327 }
370328
371- // Rounded peak top - sample and clamp
329+ // Rounded peak top
372330 const peakStartX = approachX ;
373331 const peakEndX = currentPeak . x + firstSpacing * 0.06 ;
374332
@@ -482,15 +440,14 @@ class GratingFFTSimulation {
482440 ctx . globalAlpha = 1.0 ;
483441 ctx . stroke ( ) ;
484442
485- // Draw smooth dotted white envelope curve OVER the peaks showing side lobes
443+ // Draw smooth dotted white envelope curve
486444 ctx . lineWidth = 2 ;
487445 ctx . strokeStyle = '#ffffff' ;
488446 ctx . globalAlpha = 0.7 ;
489447 ctx . setLineDash ( [ 5 , 5 ] ) ;
490448
491449 ctx . beginPath ( ) ;
492450
493- const centerX = this . cvs . width / 2 ;
494451 const numPoints = 400 ;
495452 let started = false ;
496453
@@ -529,7 +486,7 @@ class GratingFFTSimulation {
529486 const x = Math . round ( order . x ) ;
530487 const v = order . intensity ;
531488 const h = Math . round ( v * ( this . cvs . height * 0.45 ) ) ;
532- const width = Math . round ( order . width ) ; // Use calculated width
489+ const width = Math . round ( order . width ) ;
533490
534491 if ( h > 0 ) {
535492 for ( let dx = - width ; dx <= width ; dx ++ ) {
@@ -568,11 +525,11 @@ class GratingFFTSimulation {
568525 const envelopeWidthFactor = 1.0 + ( this . distanceToScreen - 1.0 ) * 0.4 ;
569526 const envelopeWidth = this . cvs . width * 0.3 * envelopeWidthFactor ;
570527
571- // Single-slit envelope function (same as in drawIntensityPlot)
528+ // Single-slit envelope function
572529 const singleSlitEnvelope = ( x ) => {
573530 const centerX = this . cvs . width / 2 ;
574531 const normalizedX = ( x - centerX ) / envelopeWidth ;
575- const beta = normalizedX * 1.5 ;
532+ const beta = normalizedX * 1.8 ;
576533
577534 let sincValue ;
578535 if ( Math . abs ( beta ) < 0.001 ) {
@@ -584,9 +541,9 @@ class GratingFFTSimulation {
584541 let intensity = sincValue * sincValue ;
585542
586543 if ( Math . abs ( beta ) > 1.0 && Math . abs ( beta ) < 2.0 ) {
587- intensity *= 11 ;
544+ intensity *= 3 ;
588545 } else if ( Math . abs ( beta ) >= 2.0 && Math . abs ( beta ) < 3.0 ) {
589- intensity *= 8 ;
546+ intensity *= 2 ;
590547 }
591548
592549 return Math . max ( 0 , intensity ) ;
@@ -651,7 +608,6 @@ class GratingFFTSimulation {
651608 if ( prevY === this . screen . y ) return ;
652609
653610 // Update distance based on new Y position
654- // Y from maxY (0.75*height) = 1.0m to minY (0.25*height) = 2.0m
655611 const fraction = ( this . screen . maxY - this . screen . y ) / ( this . screen . maxY - this . screen . minY ) ;
656612 this . distanceToScreen = 1.0 + fraction * 1.0 ;
657613
0 commit comments