Skip to content

Commit a14490f

Browse files
committed
fix: resolve discrete polygon fallacy and poisson source distribution
1 parent 060171d commit a14490f

1 file changed

Lines changed: 37 additions & 53 deletions

File tree

  • projects/arithmetica-lucis/prototype/mobius-caustic-solver/src

projects/arithmetica-lucis/prototype/mobius-caustic-solver/src/main.js

Lines changed: 37 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ try {
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

Comments
 (0)