From 45d19f3a8e25bd888942d3dbf509e30db1a42076 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 15 Oct 2026 07:23:26 +0200 Subject: [PATCH] PotentialOwnerTests: authored curves read from Settings.asset; zero pinned from just below; the core bowl sampled to 0.99R TheAuthoredCurvesBuildAtEveryMass loads PlanetSettings from the asset the way RunStartTests does. The unphysical-strength theory adds -1e-6 and -float.Epsilon and a new test builds zero and negative zero. TheCoreIsTheBowlUpToItsEdge compares the drop from a lone body's centre with c r^2/2R^2 at 0.5R to 0.99R, tightly enough that a join at 0.95R fails. Not built or run locally; verified in the Yggdrasil job. Co-Authored-By: Claude Opus 5.5 --- .../PotentialOwnerTests.cs | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/tests/Aetheria.Shared.Tests/PotentialOwnerTests.cs b/tests/Aetheria.Shared.Tests/PotentialOwnerTests.cs index aa6aa626a..fda655819 100644 --- a/tests/Aetheria.Shared.Tests/PotentialOwnerTests.cs +++ b/tests/Aetheria.Shared.Tests/PotentialOwnerTests.cs @@ -185,11 +185,13 @@ public sealed class PotentialOwnerTests : IDisposable Assert.True(threw > 0 && built > 0, "the sweep holds masses on both sides of the crossing"); } - // The shipped curves keep the core inside the Roche limit over the whole generated mass range. + // The shipped curves keep the core inside the Roche limit over the whole generated mass range. The curves are the ones + // Settings.asset carries, read the way RunStartTests reads them, so a retune of the asset is judged here. [Fact] public void TheAuthoredCurvesBuildAtEveryMass() { - var planets = Curves(); + var planets = AuthoredSettings.Load(RunStartTests.FindRepoRoot()).Read("PlanetSettings"); + Assert.True(planets.WellStrength.Evaluate(100f) > 0f && planets.CoreRadius.Evaluate(100f) > 0f, "the authored asset carries a well"); for (var exponent10 = -3f; exponent10 <= 8f; exponent10 += .25f) ZoneOf(planets, (pow(10f, exponent10), float2.zero)); } @@ -201,6 +203,8 @@ public sealed class PotentialOwnerTests : IDisposable [InlineData(float.NaN)] [InlineData(float.PositiveInfinity)] [InlineData(float.NegativeInfinity)] + [InlineData(-1e-6f)] + [InlineData(-float.Epsilon)] public void AnUnphysicalWellStrengthThrowsAtConstruction(float strength) { var planets = Curves(); @@ -211,6 +215,24 @@ public sealed class PotentialOwnerTests : IDisposable Assert.Throws(() => ZoneOf(planets, (100f, float2.zero))); } + // Zero is the boundary: a well of no strength builds beside the authored core and Roche curves (it digs nothing), where + // the smallest negative strength does not. + [Theory] + [InlineData(0f)] + [InlineData(-0f)] + public void AWellOfNoStrengthBuildsAndDigsNothing(float strength) + { + var planets = Curves(); + planets.WellStrength = new ExponentialCurve { Constant = strength }; + var zone = ZoneOf(planets, (100f, float2.zero)); + foreach (var p in new[] { float2.zero, float2(3f, 1f), float2(40f, -9f), float2(900f, 0f) }) + { + Assert.Equal(0f, zone.GetHeight(p)); + Assert.Equal(0f, zone.GetForce(p).x); + Assert.Equal(0f, zone.GetForce(p).y); + } + } + // The well is authored once, by curves of mass (target: no per-body authoring of a core or Roche limit): a body whose // record carries other radius, gravity-radius or gravity-depth multipliers has the same strength, core and Roche limit, // and pulls and digs exactly as its plain twin. The multipliers are brush inputs for the renderer, nothing else. @@ -322,6 +344,31 @@ public sealed class PotentialOwnerTests : IDisposable } } + // Inside the core the potential is the bowl c r^2 / 2R^2 all the way out to R: the drop from a lone body's centre to a point + // at r is exactly that, from the middle of the core to a hair inside its edge (the log form agrees with the bowl to second + // order at R, so only a tight tolerance sees a join that sits just inside it). + [Fact] + public void TheCoreIsTheBowlUpToItsEdge() + { + foreach (var k in new[] { 1f, 10f }) + { + var planets = Curves(k); + foreach (var mass in new[] { 29.2f, 14152.4f }) + { + var zone = ZoneOf(planets, (mass, float2.zero)); + var c = planets.WellStrength.Evaluate(mass); + var core = planets.CoreRadius.Evaluate(mass); + foreach (var fraction in new[] { .5f, .75f, .9f, .95f, .98f, .99f }) + foreach (var angle in new[] { 0f, 2.3f }) + { + var r = fraction * core; + var drop = (zone.GetHeight(new float2(r * cos(angle), r * sin(angle))) - zone.GetHeight(float2.zero)) * k; + Near(c * fraction * fraction / 2f, drop, 2e-5f); + } + } + } + } + [Fact] public void TheCoreMeetsTheTailAtItsRadius() {