@@ -358,7 +358,6 @@ export function createFlipBurnHandler(
358358 getResults: () => (HTMLElement | null)[],
359359 chartRegistry: { current: ChartRegistry }
360360): () => void {
361- let pendingRAF: number | null = null;
362361 let pendingCalculation: number | null = null;
363362
364363 return () => {
@@ -381,10 +380,6 @@ export function createFlipBurnHandler(
381380 if (!accelInput || !distanceInput || !dryMassInput || !efficiencyInput) return;
382381
383382 // Cancel pending calculation to prevent race condition
384- if (pendingRAF !== null) {
385- cancelAnimationFrame(pendingRAF);
386- pendingRAF = null;
387- }
388383 if (pendingCalculation !== null) {
389384 clearTimeout(pendingCalculation);
390385 pendingCalculation = null;
@@ -463,77 +458,74 @@ export function createFlipBurnHandler(
463458 if (resultFlipStars) resultFlipStars.textContent = "";
464459 if (resultFlipGalaxyFraction) resultFlipGalaxyFraction.textContent = "";
465460
466- // Allow UI to update before heavy calculation
467- pendingRAF = requestAnimationFrame(() => {
468- pendingRAF = null;
469- pendingCalculation = window.setTimeout(() => {
470- // Use validated values from above
471- const accel = rl.g.mul(accelGStr);
472- const m = rl.ensure(distanceLightYearsStr).mul(rl.lightYear);
473- const res = rl.flipAndBurn(accel, m);
474- const peak = res.peakVelocity.div(rl.c);
475- const lorentz = res.lorentzFactor;
476- const metre = rl.formatSignificant(rl.one.div(lorentz), "0", 2);
477- const sec = rl.formatSignificant(rl.one.mul(lorentz), "0", 2);
478-
479- // Calculate fuel mass
480- const dryMass = rl.ensure(dryMassStr);
481- const efficiency = rl.ensure(efficiencyStr);
482- const fuelFraction = rl.pionRocketFuelFraction(res.properTime, accel, efficiency);
483- const fuelMass = fuelFraction.mul(dryMass).div(rl.one.minus(fuelFraction));
484- const fuelPercent = fuelFraction.mul(100);
485-
486- if (resultFlip1) {
487- const f = rl.formatDurationAutoUnit(res.properTime);
488- setElement(resultFlip1, f.value, f.units);
489- }
490- if (resultFlip2) setElement(resultFlip2, rl.formatSignificant(peak, "9", 2), "c");
491- if (resultFlip4) {
492- const coordFormatted = rl.formatDurationAutoUnit(res.coordTime);
493- const diffFormatted = rl.formatDurationAutoUnit(res.coordTime.minus(res.properTime));
494- setElement(
495- resultFlip4,
496- `${coordFormatted.value} ${coordFormatted.units} (+${diffFormatted.value} ${diffFormatted.units})`,
497- ""
498- );
499- }
500- if (resultFlip3) setElement(resultFlip3, rl.formatSignificant(lorentz, "0", 2), "");
501- if (resultFlip5) setElement(resultFlip5, `1m becomes ${metre}m`, "");
502- if (resultFlip6) setElement(resultFlip6, `1s becomes ${sec}s`, "");
503- if (resultFlipFuel) setElement(resultFlipFuel, rl.formatMassWithUnit(fuelMass), "");
504- if (resultFlipFuelFraction)
505- setElement(resultFlipFuelFraction, rl.formatSignificant(fuelPercent, "9", 2), "%");
506-
507- // Update charts - parseFloat is OK here as Chart.js only needs limited precision for display
508- const accelG = parseFloat(accelGStr);
509- const distanceLightYears = parseFloat(distanceLightYearsStr);
461+ // Yield to the event loop before heavy calculation so "Working..." renders
462+ pendingCalculation = window.setTimeout(() => {
463+ // Use validated values from above
464+ const accel = rl.g.mul(accelGStr);
465+ const m = rl.ensure(distanceLightYearsStr).mul(rl.lightYear);
466+ const res = rl.flipAndBurn(accel, m);
467+ const peak = res.peakVelocity.div(rl.c);
468+ const lorentz = res.lorentzFactor;
469+ const metre = rl.formatSignificant(rl.one.div(lorentz), "0", 2);
470+ const sec = rl.formatSignificant(rl.one.mul(lorentz), "0", 2);
471+
472+ // Calculate fuel mass
473+ const dryMass = rl.ensure(dryMassStr);
474+ const efficiency = rl.ensure(efficiencyStr);
475+ const fuelFraction = rl.pionRocketFuelFraction(res.properTime, accel, efficiency);
476+ const fuelMass = fuelFraction.mul(dryMass).div(rl.one.minus(fuelFraction));
477+ const fuelPercent = fuelFraction.mul(100);
478+
479+ if (resultFlip1) {
480+ const f = rl.formatDurationAutoUnit(res.properTime);
481+ setElement(resultFlip1, f.value, f.units);
482+ }
483+ if (resultFlip2) setElement(resultFlip2, rl.formatSignificant(peak, "9", 2), "c");
484+ if (resultFlip4) {
485+ const coordFormatted = rl.formatDurationAutoUnit(res.coordTime);
486+ const diffFormatted = rl.formatDurationAutoUnit(res.coordTime.minus(res.properTime));
487+ setElement(
488+ resultFlip4,
489+ `${coordFormatted.value} ${coordFormatted.units} (+${diffFormatted.value} ${diffFormatted.units})`,
490+ ""
491+ );
492+ }
493+ if (resultFlip3) setElement(resultFlip3, rl.formatSignificant(lorentz, "0", 2), "");
494+ if (resultFlip5) setElement(resultFlip5, `1m becomes ${metre}m`, "");
495+ if (resultFlip6) setElement(resultFlip6, `1s becomes ${sec}s`, "");
496+ if (resultFlipFuel) setElement(resultFlipFuel, rl.formatMassWithUnit(fuelMass), "");
497+ if (resultFlipFuelFraction)
498+ setElement(resultFlipFuelFraction, rl.formatSignificant(fuelPercent, "9", 2), "%");
510499
511- // Estimate stars in range
512- if (distanceLightYears >= 100000) {
513- // At or above 100k ly, show "Entire galaxy"
514- if (resultFlipStars) setElement(resultFlipStars, "Entire galaxy", "");
515- if (resultFlipGalaxyFraction) setElement(resultFlipGalaxyFraction, "100", "%");
516- } else {
517- const starEstimate = extra.estimateStarsInSphere(distanceLightYears);
518- const starsFormatted = extra.formatStarCount(starEstimate.stars);
519- const fractionPercent = rl.formatSignificant(
520- new Decimal(starEstimate.fraction * 100),
521- "0",
522- 1
523- );
524- if (resultFlipStars) setElement(resultFlipStars, starsFormatted, "");
525- if (resultFlipGalaxyFraction) setElement(resultFlipGalaxyFraction, fractionPercent, "%");
526- }
527- const efficiencyNum = parseFloat(efficiencyStr);
528- const data = generateFlipBurnChartData(accelG, distanceLightYears, efficiencyNum);
529- chartRegistry.current = updateFlipBurnCharts(chartRegistry.current, data, efficiencyNum, {
530- velocity: chartTimeModes.flipVelocity,
531- lorentz: chartTimeModes.flipLorentz,
532- rapidity: chartTimeModes.flipRapidity,
533- });
534- pendingCalculation = null;
535- }, 0);
536- });
500+ // Update charts - parseFloat is OK here as Chart.js only needs limited precision for display
501+ const accelG = parseFloat(accelGStr);
502+ const distanceLightYears = parseFloat(distanceLightYearsStr);
503+
504+ // Estimate stars in range
505+ if (distanceLightYears >= 100000) {
506+ // At or above 100k ly, show "Entire galaxy"
507+ if (resultFlipStars) setElement(resultFlipStars, "Entire galaxy", "");
508+ if (resultFlipGalaxyFraction) setElement(resultFlipGalaxyFraction, "100", "%");
509+ } else {
510+ const starEstimate = extra.estimateStarsInSphere(distanceLightYears);
511+ const starsFormatted = extra.formatStarCount(starEstimate.stars);
512+ const fractionPercent = rl.formatSignificant(
513+ new Decimal(starEstimate.fraction * 100),
514+ "0",
515+ 1
516+ );
517+ if (resultFlipStars) setElement(resultFlipStars, starsFormatted, "");
518+ if (resultFlipGalaxyFraction) setElement(resultFlipGalaxyFraction, fractionPercent, "%");
519+ }
520+ const efficiencyNum = parseFloat(efficiencyStr);
521+ const data = generateFlipBurnChartData(accelG, distanceLightYears, efficiencyNum);
522+ chartRegistry.current = updateFlipBurnCharts(chartRegistry.current, data, efficiencyNum, {
523+ velocity: chartTimeModes.flipVelocity,
524+ lorentz: chartTimeModes.flipLorentz,
525+ rapidity: chartTimeModes.flipRapidity,
526+ });
527+ pendingCalculation = null;
528+ }, 0);
537529 };
538530}
539531
0 commit comments