Skip to content

Fix solar chart to use variable amplitude instead of moving horizon#135

Open
daneden wants to merge 5 commits intomainfrom
claude/solar-path-amplitude-e2P4v
Open

Fix solar chart to use variable amplitude instead of moving horizon#135
daneden wants to merge 5 commits intomainfrom
claude/solar-path-amplitude-e2P4v

Conversation

@daneden
Copy link
Owner

@daneden daneden commented Feb 22, 2026

The chart previously used a fixed-amplitude sine curve (always -1 to +1)
and positioned the "horizon" line wherever the curve happened to be at
sunrise time, causing the horizon to drift up in winter and down in summer.

Now yValue(for:) subtracts the sine value at sunrise so that y=0 is always
the horizon. The arc's amplitude above y=0 at solar noon scales naturally
with daylight duration: larger on long summer days, smaller on short winter
days — matching how the sun actually arcs higher in the sky in summer.

The horizonY calculation in chartOverlayContent already derives from
yValue(for: safeSunrise), so it automatically locks to y=0 with no
additional changes needed.

https://claude.ai/code/session_01UmjLxdTg5ZePkPaQDBe4rf

claude and others added 5 commits February 22, 2026 23:31
The chart previously used a fixed-amplitude sine curve (always -1 to +1)
and positioned the "horizon" line wherever the curve happened to be at
sunrise time, causing the horizon to drift up in winter and down in summer.

Now yValue(for:) subtracts the sine value at sunrise so that y=0 is always
the horizon. The arc's amplitude above y=0 at solar noon scales naturally
with daylight duration: larger on long summer days, smaller on short winter
days — matching how the sun actually arcs higher in the sky in summer.

The horizonY calculation in chartOverlayContent already derives from
yValue(for: safeSunrise), so it automatically locks to y=0 with no
additional changes needed.

https://claude.ai/code/session_01UmjLxdTg5ZePkPaQDBe4rf
Draws two faint dashed reference arcs alongside the current day's path:
one for the summer solstice (longest day) and one for the winter solstice
(shortest day). This lets users see at a glance where today falls between
the two extremes throughout the year.

Implementation:
- solsticeNTSolars computes NTSolar for Jun 21 and Dec 21, then labels
  them longer/shorter by comparing daylightDuration — automatically
  correct for both hemispheres.
- todayEquivalentSunrise maps a solstice's sunrise time-of-day onto
  today's x-axis as a simple time-offset from midnight, so the path is
  drawn against the same 24-hour range. Polar day (safeSunrise = endOfDay)
  and polar night (safeSunrise = noon) both map through cleanly.
- yValue(for:withSunriseAt:) applies the same amplitude formula as the
  main path but with a supplied sunrise reference, keeping the horizon
  consistent at y=0 across all three curves.
- If either solstice NTSolar can't be computed the comparison paths are
  simply omitted with no visual or runtime impact.

https://claude.ai/code/session_01UmjLxdTg5ZePkPaQDBe4rf
Previously the chart used a manually-crafted sine wave whose only
relationship to astronomy was the sunrise/sunset times (used to
position the horizon and scale the amplitude). The curve was always
perfectly sinusoidal regardless of latitude or time of year.

This replaces that with actual altitude in degrees above/below the
horizon, computed from first principles using the same Schlyter
algorithms already present in NTSolar.

NTSolar gains altitude(at:) using the standard spherical-astronomy
formula: sin(alt) = sin(lat)sin(dec) + cos(lat)cos(dec)cos(HA), where
RA/dec come from sun_RA_dec() and the local hour angle is derived from
GMST0() + UT + longitude — all existing private helpers, so no new
dependencies are introduced.

DaylightChart changes:
- yValue(for:) now returns solar.altitude(at:) in degrees
- yValue(for:solsticeSolar:) maps today's time-of-day to the solstice
  date and calls solsticeSolar.altitude(at:), replacing the old
  sine-offset approach for the comparison paths
- effectiveYScale auto-sizes from summer solstice noon altitude so
  the chart scale is stable year-round and location-appropriate
- horizonY uses proxy.position(forY: 0.0) directly since 0° altitude
  is exactly the geometric horizon
- yScale is now optional (nil = auto); widget override removed
- Dead code removed: progressValue, culminationDelta, noonish,
  dayLength, endOfDay, daylightProportion, todayEquivalentSunrise,
  yValue(for:withSunriseAt:)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the manual Path-based drawing in chartBackground with native
LineMark series inside Chart{}, applying .interpolationMethod(.catmullRom)
for smooth curves. This eliminates the jagged appearance at equatorial
locations caused by the low 30-minute sample rate with straight-line segments.

The solstice comparison paths and today's solar path are all now LineMark
series, with .chartLegend(.hidden) suppressing the auto-generated legend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants