3636 // 1. Circular Mirror (approximated by segments)
3737 // Radius 0.9 (Near Boundary), Centered at Origin.
3838 // This creates a large Concave Mirror relative to the internal source.
39- const CIRCLE_SEGMENTS = 128 ; // Sufficient for visual smoothness
39+ const CIRCLE_SEGMENTS = 720 ; // Increased to 720 to avoid Discrete Polygon Fallacy
4040 const CIRCLE_RADIUS = 0.9 ;
4141 for ( let i = 0 ; i < CIRCLE_SEGMENTS ; i ++ ) {
4242 const t0 = ( i / CIRCLE_SEGMENTS ) * 2 * Math . PI ;
@@ -60,11 +60,11 @@ try {
6060 // 1080 Beams for "Retina" resolution and razor-sharp caustics.
6161 // ROBUSTNESS: Variable Quality Settings
6262 const QUALITY_SETTINGS = {
63- HIGH : { beams : 30 , depth : 20 } ,
64- MEDIUM : { beams : 15 , depth : 15 } ,
65- LOW : { beams : 5 , depth : 10 } ,
63+ HIGH : { beams : 720 , depth : 20 } ,
64+ MEDIUM : { beams : 360 , depth : 15 } ,
65+ LOW : { beams : 120 , depth : 10 } ,
6666 } ;
67- let currentQuality = "LOW " ; // Start conservative for instant load
67+ let currentQuality = "MEDIUM " ; // Start with reasonable quality
6868 let NUM_BEAMS = QUALITY_SETTINGS [ currentQuality ] . beams ;
6969 let MAX_DEPTH = QUALITY_SETTINGS [ currentQuality ] . depth ;
7070
@@ -73,64 +73,48 @@ try {
7373 // --- INTERACTION STATE ---
7474 let mousePos = INITIAL_SOURCE_POS . clone ( ) ; // Correctly initialize to break symmetryeffect
7575
76+ // Initialize Solver EARLY so we can use its math helpers in updateScene
77+ console . log ( "Main: Creating Solver" ) ;
78+ const solver = new Solver ( scene ) ;
79+ solver . maxDepth = MAX_DEPTH ; // Sync initial depth
80+
7681 // --- INTERACTION STATE ---
7782 console . log ( "Main: Initializing Interaction State" ) ;
7883
7984 function updateScene ( ) {
8085 scene . sources = [ ] ;
8186
82- // 1. Generate Uniform Fan at Origin
83- const fan = [ ] ;
84- for ( let i = 0 ; i < NUM_BEAMS ; i ++ ) {
85- const theta1 = ( i / NUM_BEAMS ) * 2 * Math . PI ;
86- const theta2 = ( ( i + 1 ) / NUM_BEAMS ) * 2 * Math . PI ;
87- fan . push ( {
88- p1 : new Complex ( Math . cos ( theta1 ) , Math . sin ( theta1 ) ) ,
89- p2 : new Complex ( Math . cos ( theta2 ) , Math . sin ( theta2 ) ) ,
90- } ) ;
91- }
87+ // FIX: POISSON SOURCE DISTRIBUTION
88+ // Instead of transforming a uniform fan from origin (which concentrates rays like a comet),
89+ // we generate a uniform fan AT the source position in the Hyperbolic Angle metric.
90+ // This ensures isotropic emission.
9291
93- // 2. Apply Transform
94- const a = mousePos ;
95- // Pre-calculate M coefficients?
96- // M(z) = (z + a)/(1 + a_bar * z)
97- // Using Mobius class? "a, b, c, d"
98- // (1*z + a) / (conj(a)*z + 1)
99- // A=1, B=a, C=conj(a), D=1.
100- const A = new Complex ( 1 , 0 ) ;
101- const B = a ;
102- const C = new Complex ( a . re , - a . im ) ; // conj(a)
103- const D = new Complex ( 1 , 0 ) ;
104- const M = new Mobius ( A , B , C , D ) ;
92+ // We use Solver.projectAngleToBoundary(source, angle) to find the endpoints.
10593
106- const transformedOrigin = new Complex ( 0 , 0 ) ; // Will become 'a'
107- M . apply ( transformedOrigin , new Complex ( 0 , 0 ) ) ;
94+ const startAngle = 0 ;
95+ const endAngle = 2 * Math . PI ;
96+ const step = ( endAngle - startAngle ) / NUM_BEAMS ;
10897
109- const temp = new Complex ( ) ;
110-
111- for ( const beam of fan ) {
112- const tP1 = new Complex ( ) ;
113- M . apply ( tP1 , beam . p1 ) ;
98+ for ( let i = 0 ; i < NUM_BEAMS ; i ++ ) {
99+ const theta1 = startAngle + i * step ;
100+ const theta2 = startAngle + ( i + 1 ) * step ;
114101
115- const tP2 = new Complex ( ) ;
116- M . apply ( tP2 , beam . p2 ) ;
102+ // Use the solver's utility to map local angle -> boundary point
103+ const p1 = solver . projectAngleToBoundary ( mousePos , theta1 ) ;
104+ const p2 = solver . projectAngleToBoundary ( mousePos , theta2 ) ;
117105
118- // Push to scene with EXPLICIT origin
119106 scene . sources . push ( {
120- origin : transformedOrigin ,
121- p1 : tP1 ,
122- p2 : tP2 ,
107+ origin : mousePos , // The physical source location
108+ p1 : p1 ,
109+ p2 : p2 ,
123110 } ) ;
124111 }
125112 }
126113
127114 // Initial Update
128115 updateScene ( ) ;
129116
130- // Initialize Solver
131- console . log ( "Main: Creating Solver" ) ;
132- const solver = new Solver ( scene ) ;
133- solver . maxDepth = MAX_DEPTH ; // Sync initial depth
117+ // (Solver already created above)
134118
135119 console . log ( "Main: Creating Baseline Verification" ) ;
136120 const baseline = new Baseline ( ) ;
@@ -157,16 +141,16 @@ try {
157141
158142 // --- BASELINE VERIFICATION ---
159143 if ( SHOW_BASELINE ) {
160- // Source object in scene has {origin, p1, p2}.
161- if ( scene . sources . length > 0 ) {
162- const s = scene . sources [ 0 ] . origin ;
163- const particles = baseline . generateParticles ( s , 1000 ) ; // 1000 dots/frame
164- artist . ctx . fillStyle = "rgba(255, 255, 255, 0.5)" ;
165- for ( const p of particles ) {
166- const sc = viewport . diskToScreen ( p ) ;
167- artist . ctx . fillRect ( sc . x , sc . y , 1 , 1 ) ;
168- }
144+ // Source object in scene has {origin, p1, p2}.
145+ if ( scene . sources . length > 0 ) {
146+ const s = scene . sources [ 0 ] . origin ;
147+ const particles = baseline . generateParticles ( s , 1000 ) ; // 1000 dots/frame
148+ artist . ctx . fillStyle = "rgba(255, 255, 255, 0.5)" ;
149+ for ( const p of particles ) {
150+ const sc = viewport . diskToScreen ( p ) ;
151+ artist . ctx . fillRect ( sc . x , sc . y , 1 , 1 ) ;
169152 }
153+ }
170154 }
171155
172156 // Trace and draw each beam
0 commit comments