@@ -5,7 +5,6 @@ import { Complex } from "./core/complex.js";
55import { Solver } from "./physics/solver.js" ;
66import { Mobius } from "./core/mobius.js" ;
77import { Logger } from "./core/logger.js" ;
8- import { PerformanceMonitor } from "./core/performance_monitor.js" ;
98import { Baseline } from "./physics/baseline.js" ;
109
1110console . log ( "Möbius Caustic Solver: Initializing..." ) ;
3736 // 1. Circular Mirror (approximated by segments)
3837 // Radius 0.9 (Near Boundary), Centered at Origin.
3938 // This creates a large Concave Mirror relative to the internal source.
40- const CIRCLE_SEGMENTS = 128 ; // Sufficient for visual smoothness (Was 720) -> 720 is sufficient for smooth visual
39+ const CIRCLE_SEGMENTS = 128 ; // Sufficient for visual smoothness
4140 const CIRCLE_RADIUS = 0.9 ;
4241 for ( let i = 0 ; i < CIRCLE_SEGMENTS ; i ++ ) {
4342 const t0 = ( i / CIRCLE_SEGMENTS ) * 2 * Math . PI ;
7675
7776 // --- INTERACTION STATE ---
7877 console . log ( "Main: Initializing Interaction State" ) ;
79- // We need to store the "Base Beams" and then transform them?
80- // Or just update the source position and re-generate the fan?
81- // Re-generating the fan for a point source inside the disk:
82- // A "Point Source" emits rays in all directions.
83- // We can define the beams by 360 degrees around the source point.
84- // Warning: "trace" in Solver expects `source` object to have {p1, p2} on boundary?
85- // Solver.solve says: "Initial Source ... is logically the origin ... source passed in is {p1, p2} on boundary".
86- // This implies the Solver assumes the source is the Origin (0,0) and the targets are p1,p2.
87- // To move the source, we can logically move the *Scene* or move the *Solver's Origin*.
88- // BETTER: The requested "Möbius Drag" applies a transformation to the *entire space* relative to the observer.
89- // "Apply this matrix to the Light Source Interval before solving."
90- // If we transform the Target Interval (p1, p2) by M, and the Source (0,0) by M...
91- // Source becomes M(0). Target becomes M(p1), M(p2).
92- // Solver logic: `trace(source, p1, p2)`
93- // So `source` argument in `trace` allows non-zero origin!
94- // YES. `trace(source, p1, p2...)`.
95- // But `solve(source)` hardcodes `initialSource = new Complex(0,0)`.
96- // I MUST UPDATE SOLVER TO ACCEPT SOURCE POSITION.
97-
98- // Let's update Solver first?
99- // Or just hack it: The "Source Object" passed to solver can contain the origin?
100- // Currently: `solver.solve(source)` takes `source` from `scene.sources`.
101- // `scene.sources` elements have {p1, p2}.
102- // I will add `origin` to `scene.sources` elements.
103-
104- // --- INTERACTION STATE ---
10578
10679 function updateScene ( ) {
107- // Logger.debug(`Main: updateScene() triggered. MousePos: ${mousePos.toString()}`, "Main");
10880 scene . sources = [ ] ;
109- // Generate fan from the current mouse position (or transformed origin)
110- // Actually, "Möbius Drag" means we observe the universe from a different point.
111- // Equivalent to moving the source.
112-
113- // Let's just place the source at `mousePos`.
114- // Beams go from `mousePos` to the boundary.
115- // To generate a uniform fan *at the source*, we need equal angles at the source.
116- // Geodesics from `mousePos` to boundary points.
117- // We iterate angle 0..2PI at `mousePos`.
118- // Find where the ray exits the disk.
119- // Ray from `z` at angle `theta`:
120- // It's a geodesic.
121- // Calculating the boundary endpoint for a geodesic starting at `z` with direction `theta` is complex.
122-
123- // ALTERNATIVE (Simpler for Prototype):
124- // Define the beams as emanating from (0,0) to boundary (uniform).
125- // THEN transform the *endpoints* and the *source* by the Möbius transform M.
126- // M moves 0 to `mousePos`.
127- // M(z) = (z + a) / (1 + conj(a)z) where a = mousePos.
128- // This preserves angular uniformity at the new source!
129- // This is "Möbius Translation" exactly.
13081
13182 // 1. Generate Uniform Fan at Origin
13283 const fan = [ ] ;
@@ -185,105 +136,17 @@ try {
185136 const baseline = new Baseline ( ) ;
186137 const SHOW_BASELINE = false ; // Disable for performance
187138
188- // --- ROBUSTNESS: Performance Watchdog ---
189- const perfMonitor = new PerformanceMonitor ( {
190- onDegrade : ( msg ) => {
191- // Degradation Logic
192- if ( currentQuality === "HIGH" ) currentQuality = "MEDIUM" ;
193- else if ( currentQuality === "MEDIUM" ) currentQuality = "LOW" ;
194- else if ( currentQuality === "LOW" ) {
195- NUM_BEAMS = 20 ;
196- solver . maxDepth = 5 ;
197- }
198- // Apply
199- if ( QUALITY_SETTINGS [ currentQuality ] ) {
200- NUM_BEAMS = QUALITY_SETTINGS [ currentQuality ] . beams ;
201- solver . maxDepth = QUALITY_SETTINGS [ currentQuality ] . depth ;
202- }
203- updateDashboard ( ) ;
204- updateScene ( ) ;
205- } ,
206- onStatsUpdate : ( fps , latency ) => {
207- const statsMsg = `FPS: ${ fps } | FrameTime: ${ latency } ms | Quality: ${ currentQuality } ` ;
208- Logger . info ( statsMsg , "PerfStats" ) ;
209- if ( document . getElementById ( "ui-fps" ) )
210- document . getElementById ( "ui-fps" ) . innerText = fps ;
211- if ( document . getElementById ( "ui-latency" ) )
212- document . getElementById ( "ui-latency" ) . innerText = `${ latency } ms` ;
213- // Clear alert logic if needed
214- } ,
215- onGraphDraw : ( data ) => {
216- drawPerfGraph ( data ) ;
217- } ,
218- } ) ;
219-
220139 function updateDashboard ( ) {
221140 if ( ! document . getElementById ( "ui-status" ) ) return ;
222- document . getElementById ( "ui-status" ) . innerText = perfMonitor . stats . degraded
223- ? "DEGRADED"
224- : "RUNNING" ;
225- document . getElementById ( "ui-status" ) . style . color = perfMonitor . stats
226- . degraded
227- ? "orange"
228- : "#0f0" ;
141+ document . getElementById ( "ui-status" ) . innerText = "RUNNING" ;
142+ document . getElementById ( "ui-status" ) . style . color = "#0f0" ;
229143 document . getElementById (
230144 "ui-quality"
231145 ) . innerText = `${ currentQuality } (${ NUM_BEAMS } beams)` ;
232146 }
233147
234- function drawPerfGraph ( data ) {
235- const canvas = document . getElementById ( "perf-graph" ) ;
236- if ( ! canvas ) return ;
237- const ctx = canvas . getContext ( "2d" ) ;
238- const width = canvas . width ;
239- const height = canvas . height ;
240-
241- ctx . fillStyle = "#222" ;
242- ctx . fillRect ( 0 , 0 , width , height ) ;
243-
244- const maxMs = 40 ;
245- const yScale = height / maxMs ;
246-
247- // 12ms Line
248- const y12 = height - 12 * yScale ;
249- ctx . beginPath ( ) ;
250- ctx . strokeStyle = "#4da6ff" ;
251- ctx . lineWidth = 1 ;
252- ctx . setLineDash ( [ 2 , 2 ] ) ;
253- ctx . moveTo ( 0 , y12 ) ;
254- ctx . lineTo ( width , y12 ) ;
255- ctx . stroke ( ) ;
256-
257- // 33ms Line
258- const y33 = height - 33 * yScale ;
259- ctx . beginPath ( ) ;
260- ctx . strokeStyle = "#ff4444" ;
261- ctx . setLineDash ( [ 2 , 2 ] ) ;
262- ctx . moveTo ( 0 , y33 ) ;
263- ctx . lineTo ( width , y33 ) ;
264- ctx . stroke ( ) ;
265- ctx . setLineDash ( [ ] ) ;
266-
267- // Data
268- ctx . beginPath ( ) ;
269- ctx . strokeStyle = "#00ff00" ;
270- ctx . lineWidth = 2 ;
271- for ( let i = 0 ; i < data . length ; i ++ ) {
272- const val = data [ i ] ;
273- const x = i ;
274- const y = height - val * yScale ;
275- if ( val > 12 ) ctx . strokeStyle = "#ffff00" ;
276- if ( val > 33 ) ctx . strokeStyle = "#ff0000" ;
277- if ( i === 0 ) ctx . moveTo ( x , y ) ;
278- else ctx . lineTo ( x , y ) ;
279- }
280- ctx . stroke ( ) ;
281- }
282-
283148 // --- Main Loop ---
284149 function mainLoop ( ) {
285- perfMonitor . update ( ) ; // Watchdog & Graph
286-
287150 viewport . clear ( ) ;
288151 artist . drawDiskBoundary ( ) ;
289152
@@ -294,40 +157,26 @@ try {
294157
295158 // --- BASELINE VERIFICATION ---
296159 if ( SHOW_BASELINE ) {
297- // Generate standard particles from the same source
298- // We take the first source for simplicity
299- if ( scene . sources . length > 0 ) {
300160 // Source object in scene has {origin, p1, p2}.
301- // The `origin` is the transformed origin (the actual source point).
302- // Since we are validating the physics, we should use that same point.
303- const s = scene . sources [ 0 ] . origin ;
304- if ( perfMonitor . stats . fps > 20 ) {
305- // Only if performant
161+ if ( scene . sources . length > 0 ) {
162+ const s = scene . sources [ 0 ] . origin ;
306163 const particles = baseline . generateParticles ( s , 1000 ) ; // 1000 dots/frame
307164 artist . ctx . fillStyle = "rgba(255, 255, 255, 0.5)" ;
308165 for ( const p of particles ) {
309- const sc = viewport . diskToScreen ( p ) ;
310- artist . ctx . fillRect ( sc . x , sc . y , 1 , 1 ) ;
166+ const sc = viewport . diskToScreen ( p ) ;
167+ artist . ctx . fillRect ( sc . x , sc . y , 1 , 1 ) ;
311168 }
312169 }
313- }
314170 }
315171
316- // Trace and draw each beam
317172 // Trace and draw each beam
318173 const frameStart = performance . now ( ) ;
319- const FRAME_BUDGET = 32 ; // User Requirement: 32ms (30 FPS)
174+ const FRAME_BUDGET = 32 ;
320175
321176 for ( const source of scene . sources ) {
322177 const elapsed = performance . now ( ) - frameStart ;
323178 if ( elapsed > FRAME_BUDGET ) {
324- // User Req: "Consider this an error"
325- Logger . error (
326- `Frame Budget Exceeded (${ Math . round ( elapsed ) } ms). Aborting Frame.` ,
327- "Budget"
328- ) ;
329- // Trigger degradation for NEXT frame
330- perfMonitor . markBudgetExceeded ( ) ;
179+ // Basic budget check (silent now)
331180 break ; // Stop processing sources
332181 }
333182
@@ -343,6 +192,7 @@ try {
343192 }
344193 } catch ( e ) {
345194 // Budget exceeded inside solver or other error
195+ console . warn ( e ) ;
346196 }
347197 }
348198
@@ -357,18 +207,7 @@ try {
357207 let rayCount = BEAM_QUALITY_LOW ; // Store preferred quality
358208
359209 viewport . canvas . addEventListener ( "mousemove" , ( e ) => {
360- const inputLatency = Date . now ( ) - e . timeStamp ;
361- if ( inputLatency > 16 ) {
362- Logger . warn ( `Input Lag Detected (${ inputLatency } ms).` , "Input" ) ;
363- // Could trigger degradation here too
364- }
365-
366- // Dynamic Resolution: Force LOW quality during movement
367- // if (!perfMonitor.stats.degraded && currentQuality !== "LOW") {
368- // NUM_BEAMS = QUALITY_SETTINGS["LOW"].beams;
369- // solver.maxDepth = QUALITY_SETTINGS["LOW"].depth;
370- // }
371-
210+ // Dynamic Resolution: Force LOW quality during movement (simplified)
372211 const rect = viewport . canvas . getBoundingClientRect ( ) ;
373212 const x = e . clientX - rect . left ;
374213 const y = e . clientY - rect . top ;
@@ -384,9 +223,6 @@ try {
384223 }
385224
386225 updateScene ( ) ;
387-
388- // Debounce return to high quality?
389- // For now, let's keep it simple. The Frame Budget limiter is the primary guard.
390226 } ) ;
391227
392228 // Start
0 commit comments