LCOV - code coverage report
Current view: top level - src/simlab - PlanetaryGravityScenario.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 77 77
Test Date: 2026-04-10 19:03:25 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2025 aeml
       3              :  *
       4              :  * This program is free software: you can redistribute it and/or modify
       5              :  * it under the terms of the GNU General Public License as published by
       6              :  * the Free Software Foundation, either version 3 of the License, or
       7              :  * (at your option) any later version.
       8              :  *
       9              :  * This program is distributed in the hope that it will be useful,
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12              :  * GNU General Public License for more details.
      13              :  *
      14              :  * You should have received a copy of the GNU General Public License
      15              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : 
      18              : #include "simlab/Scenario.hpp"
      19              : #include "ecs/World.hpp"
      20              : #include "physics/Components.hpp"
      21              : #include "physics/Systems.hpp"
      22              : #include "jobs/JobSystem.hpp"
      23              : #include "ascii/TextRenderer.hpp"
      24              : #include "core/Logger.hpp"
      25              : #include <vector>
      26              : #include <cmath>
      27              : #include <random>
      28              : #include <iostream>
      29              : 
      30              : namespace simlab
      31              : {
      32              :     namespace
      33              :     {
      34              :         // Custom system for N-body gravity
      35              :         class PlanetaryGravitySystem : public ecs::ISystem
      36              :         {
      37              :         public:
      38          560 :             void Update(ecs::World& world, float dt) override
      39              :             {
      40              :                 // Simple approach: Apply force towards center (0,0) for all dynamic bodies
      41              :                 // Or true N-body if we want to be fancy, but O(N^2) is slow for many bodies without optimization.
      42              :                 // Let's do a massive central attractor at (0,0).
      43              :                 
      44          560 :                 const float G = 100.0f;
      45          560 :                 const float centerMass = 1000.0f;
      46              : 
      47          560 :                 world.ForEach<physics::TransformComponent>([&](ecs::EntityId id, physics::TransformComponent& t) {
      48        56560 :                     auto* body = world.GetComponent<physics::RigidBodyComponent>(id);
      49        56560 :                     if (body && body->invMass > 0.0f)
      50              :                     {
      51        56000 :                         float dx = 0.0f - t.x;
      52        56000 :                         float dy = 0.0f - t.y;
      53        56000 :                         float distSq = dx*dx + dy*dy;
      54        56000 :                         float dist = std::sqrt(distSq);
      55              :                         
      56        56000 :                         if (dist > 0.1f)
      57              :                         {
      58        56000 :                             float force = (G * centerMass * body->mass) / distSq;
      59        56000 :                             float fx = (dx / dist) * force;
      60        56000 :                             float fy = (dy / dist) * force;
      61              : 
      62              :                             // Apply force (modify velocity directly for this simple integrator)
      63        56000 :                             body->vx += (fx * body->invMass) * dt;
      64        56000 :                             body->vy += (fy * body->invMass) * dt;
      65              :                         }
      66              :                     }
      67        56560 :                 });
      68          560 :             }
      69              :         };
      70              :     }
      71              : 
      72              :     class PlanetaryGravityScenario : public IScenario
      73              :     {
      74              :     public:
      75           18 :         void Setup(ecs::World& world) override
      76              :         {
      77           18 :             m_renderer = std::make_unique<ascii::TextRenderer>(80, 40);
      78              :             
      79              :             // Physics setup
      80           18 :             physics::EnvironmentForces env;
      81           18 :             env.gravityY = 0.0f; // No global gravity, only radial
      82           18 :             env.drag = 0.0f; // Space has no drag
      83              :             
      84           18 :             auto physicsSystem = std::make_unique<physics::PhysicsSystem>();
      85           18 :             physics::PhysicsSettings settings;
      86           18 :             settings.substeps = 8;
      87           18 :             physicsSystem->SetSettings(settings);
      88           18 :             physicsSystem->SetEnvironment(env);
      89           18 :             physicsSystem->SetJobSystem(&m_jobSystem);
      90           18 :             world.AddSystem(std::move(physicsSystem));
      91              : 
      92              :             // Add custom gravity system
      93           18 :             world.AddSystem(std::make_unique<PlanetaryGravitySystem>());
      94              : 
      95              :             // Central Star (Static)
      96           18 :             auto star = world.CreateEntity();
      97           18 :             world.AddComponent<physics::TransformComponent>(star, 0.0f, 0.0f, 0.0f);
      98           18 :             auto& starBody = world.AddComponent<physics::RigidBodyComponent>(star);
      99           18 :             starBody.mass = 0.0f; // Static
     100           18 :             starBody.invMass = 0.0f;
     101           18 :             world.AddComponent<physics::CircleColliderComponent>(star, 2.0f);
     102              : 
     103              :             // Planets
     104           18 :             std::mt19937 rng(42);
     105           18 :             std::uniform_real_distribution<float> distAngle(0.0f, 6.28318f);
     106           18 :             std::uniform_real_distribution<float> distRadius(5.0f, 35.0f);
     107           18 :             std::uniform_real_distribution<float> distMass(0.5f, 2.0f);
     108              : 
     109         1818 :             for (int i = 0; i < 100; ++i)
     110              :             {
     111         1800 :                 float angle = distAngle(rng);
     112         1800 :                 float r = distRadius(rng);
     113         1800 :                 float mass = distMass(rng);
     114              : 
     115         1800 :                 float x = std::cos(angle) * r;
     116         1800 :                 float y = std::sin(angle) * r;
     117              : 
     118              :                 // Orbital velocity: v = sqrt(GM/r)
     119              :                 // Perpendicular to radius
     120         1800 :                 float v = std::sqrt(100.0f * 1000.0f / r);
     121         1800 :                 float vx = -std::sin(angle) * v;
     122         1800 :                 float vy = std::cos(angle) * v;
     123              : 
     124         1800 :                 auto p = world.CreateEntity();
     125         1800 :                 world.AddComponent<physics::TransformComponent>(p, x, y, 0.0f);
     126         1800 :                 auto& body = world.AddComponent<physics::RigidBodyComponent>(p);
     127         1800 :                 body.mass = mass;
     128         1800 :                 body.invMass = 1.0f / mass;
     129         1800 :                 body.vx = vx;
     130         1800 :                 body.vy = vy;
     131         1800 :                 body.restitution = 0.8f;
     132              :                 
     133         1800 :                 world.AddComponent<physics::CircleColliderComponent>(p, 0.5f);
     134         1800 :                 physics::ConfigureCircleInertia(body, 0.5f);
     135              :             }
     136           18 :         }
     137              : 
     138          565 :         void Update(ecs::World& world, float dt) override
     139              :         {
     140              :             (void)world;
     141              :             (void)dt;
     142          565 :         }
     143              : 
     144          204 :         void Render(ecs::World& world, std::ostream& out) override
     145              :         {
     146          204 :             m_renderer->Clear();
     147              :             
     148              :             // Draw Star
     149          204 :             m_renderer->DrawCircle(40, 20, 4, '@');
     150              : 
     151              :             // Draw Planets
     152          204 :             world.ForEach<physics::TransformComponent>([&](ecs::EntityId, physics::TransformComponent& t) {
     153              :                 // Map -40..40 to 0..80
     154        20604 :                 int sx = static_cast<int>(t.x + 40.0f);
     155        20604 :                 int sy = static_cast<int>(20.0f - t.y * 0.5f); // Aspect ratio correction roughly
     156              :                 
     157        20604 :                 if (sx >= 0 && sx < 80 && sy >= 0 && sy < 40)
     158              :                 {
     159        20604 :                     m_renderer->Put(sx, sy, 'o');
     160              :                 }
     161        20604 :             });
     162              : 
     163          204 :             m_renderer->SetHeadless(simlab::IsHeadlessRendering());
     164          204 :             m_renderer->PresentDiff(out);
     165          204 :         }
     166              : 
     167              :     private:
     168              :         std::unique_ptr<ascii::TextRenderer> m_renderer;
     169              :         jobs::JobSystem m_jobSystem;
     170              :     };
     171              : 
     172           23 :     std::unique_ptr<IScenario> CreatePlanetaryGravityScenario()
     173              :     {
     174           23 :         return std::make_unique<PlanetaryGravityScenario>();
     175              :     }
     176              : }
        

Generated by: LCOV version 2.0-1