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

            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 <vector>
      25              : #include <random>
      26              : #include <iostream>
      27              : 
      28              : namespace simlab
      29              : {
      30              :     class ParticleFluidScenario : public IScenario
      31              :     {
      32              :     public:
      33            5 :         void Setup(ecs::World& world) override
      34              :         {
      35            5 :             m_renderer = std::make_unique<ascii::TextRenderer>(80, 40);
      36              :             
      37            5 :             physics::EnvironmentForces env;
      38            5 :             env.gravityY = -9.81f;
      39              :             
      40            5 :             auto physicsSystem = std::make_unique<physics::PhysicsSystem>();
      41            5 :             physics::PhysicsSettings settings;
      42            5 :             settings.substeps = 8; // Increased substeps for stability
      43            5 :             physicsSystem->SetSettings(settings);
      44            5 :             physicsSystem->SetEnvironment(env);
      45            5 :             physicsSystem->SetJobSystem(&m_jobSystem);
      46            5 :             world.AddSystem(std::move(physicsSystem));
      47              : 
      48              :             // Container (Closed box)
      49              :             // Visible range: X[-20, 20], Y[-15, 25]
      50              :             // Walls made very thick (100.0f) to prevent tunneling
      51              :             // Inner faces at: Left -18.5, Right 18.5, Bottom -13.5, Top 23.5
      52            5 :             CreateWall(world, -68.5f, 5.0f, 100.0f, 40.0f); // Left
      53            5 :             CreateWall(world, 68.5f, 5.0f, 100.0f, 40.0f);  // Right
      54            5 :             CreateWall(world, 0.0f, -63.5f, 40.0f, 100.0f); // Bottom
      55            5 :             CreateWall(world, 0.0f, 73.5f, 40.0f, 100.0f);  // Top
      56              : 
      57              :             // Particles
      58            5 :             std::mt19937 rng(123);
      59            5 :             std::uniform_real_distribution<float> distX(-15.0f, 15.0f);
      60            5 :             std::uniform_real_distribution<float> distY(-5.0f, 15.0f);
      61              : 
      62          505 :             for (int i = 0; i < 100; ++i)
      63              :             {
      64          500 :                 float x = distX(rng);
      65          500 :                 float y = distY(rng);
      66          500 :                 auto p = world.CreateEntity();
      67          500 :                 world.AddComponent<physics::TransformComponent>(p, x, y, 0.0f);
      68          500 :                 auto& b = world.AddComponent<physics::RigidBodyComponent>(p);
      69          500 :                 b.mass = 0.1f; b.invMass = 10.0f; b.restitution = 0.9f; b.friction = 0.0f; // High restitution for bouncing
      70          500 :                 b.lastX = x;
      71          500 :                 b.lastY = y;
      72          500 :                 world.AddComponent<physics::CircleColliderComponent>(p, 0.3f);
      73              :             }
      74            5 :         }
      75              : 
      76           20 :         void CreateWall(ecs::World& world, float x, float y, float w, float h)
      77              :         {
      78           20 :             auto wall = world.CreateEntity();
      79           20 :             world.AddComponent<physics::TransformComponent>(wall, x, y, 0.0f);
      80           20 :             auto& b = world.AddComponent<physics::RigidBodyComponent>(wall);
      81           20 :             b.mass = 0.0f; b.invMass = 0.0f;
      82              :             // AABB must be in world space. Since static bodies don't update AABB, we must set it correctly here.
      83           20 :             world.AddComponent<physics::AABBComponent>(wall, x - w/2, y - h/2, x + w/2, y + h/2);
      84           20 :         }
      85              : 
      86          542 :         void Update(ecs::World& world, float dt) override
      87              :         {
      88              :             (void)world;
      89              :             (void)dt;
      90          542 :         }
      91              : 
      92          181 :         void Render(ecs::World& world, std::ostream& out) override
      93              :         {
      94          181 :             m_renderer->Clear();
      95              :             
      96          181 :             world.ForEach<physics::TransformComponent>([&](ecs::EntityId id, physics::TransformComponent& t) {
      97              :                 // Map -20..20 to 0..80
      98        18985 :                 int sx = static_cast<int>((t.x + 20.0f) * 2.0f);
      99        18985 :                 int sy = static_cast<int>(40.0f - (t.y + 15.0f));
     100              : 
     101        18985 :                 if (sx >= 0 && sx < 80 && sy >= 0 && sy < 40)
     102              :                 {
     103        18226 :                     char c = '.';
     104        18226 :                     if (world.GetComponent<physics::AABBComponent>(id)) c = '#';
     105        18226 :                     m_renderer->Put(sx, sy, c);
     106              :                 }
     107        18985 :             });
     108              : 
     109          181 :             m_renderer->SetHeadless(simlab::IsHeadlessRendering());
     110          181 :             m_renderer->PresentDiff(out);
     111          181 :         }
     112              : 
     113              :     private:
     114              :         std::unique_ptr<ascii::TextRenderer> m_renderer;
     115              :         jobs::JobSystem m_jobSystem;
     116              :     };
     117              : 
     118            8 :     std::unique_ptr<IScenario> CreateParticleFluidScenario()
     119              :     {
     120            8 :         return std::make_unique<ParticleFluidScenario>();
     121              :     }
     122              : }
        

Generated by: LCOV version 2.0-1