Compare commits

...

10 commits

Author SHA1 Message Date
Sara Gerretsen a3396bb6af feat: converted project to template 2025-09-24 22:23:30 +02:00
Sara Gerretsen 0f80dc8ca6 fix: inverted x scroll 2025-09-24 14:22:39 +02:00
Sara Gerretsen 768c467572 feat: added README.md 2025-09-23 16:19:47 +02:00
Sara Gerretsen 577f7c953b chore: minor formatting adjustment 2025-09-23 16:05:06 +02:00
Sara Gerretsen f20139d0fa chore: reduced padding on panel containers 2025-09-23 15:37:11 +02:00
Sara Gerretsen 19f21e5867 chore: lowered random button's field size 2025-09-23 15:11:14 +02:00
Sara Gerretsen e5edc2e8f7 feat: implemented simulation threading toggle 2025-09-23 15:05:55 +02:00
Sara Gerretsen 5484039e3e feat: added basic culling to cell renderer 2025-09-23 14:25:26 +02:00
Sara Gerretsen 658b96b201 feat: set task split to hardware_concurrency() 2025-09-23 14:25:08 +02:00
Sara Gerretsen 74e6456343 chore: some comments on variables in simulation.cpp 2025-09-23 14:24:47 +02:00
11 changed files with 144 additions and 547 deletions

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.21)
project(GameOfLife)
project(CHANGEME)
set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/bin")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
@ -15,11 +15,11 @@ add_subdirectory(vendor/SDL3/ EXCLUDE_FROM_ALL)
set(SDLTTF_VENDORED ON)
add_subdirectory(vendor/SDL3_ttf/ EXCLUDE_FROM_ALL)
add_executable(GameOfLife ${source_files})
target_link_libraries(GameOfLife PRIVATE SDL3_ttf::SDL3_ttf SDL3::SDL3)
add_executable(CHANGEME ${source_files})
target_link_libraries(CHANGEME PRIVATE SDL3_ttf::SDL3_ttf SDL3::SDL3)
add_custom_target(copy_assets
COMMAND ${CMAKE_COMMAND} -E
copy_directory ${CMAKE_SOURCE_DIR}/assets/ ${CMAKE_BINARY_DIR}/assets
)
add_dependencies(GameOfLife copy_assets)
add_dependencies(CHANGEME copy_assets)

119
README.md Normal file
View file

@ -0,0 +1,119 @@
# Clay SDL3 Template
## Using Template
### Just use [`just`](https://just.systems/man/en/)
Run `just set-project-name {project name}` first.
And remember to `git remote set-url origin {repo}`.
### Otherwise
Modify the CMakeLists.txt manually, just replace CHANGEME with whatever your binary should be called.
## Compiling
> Remember to `git submodule update --init --recursive`!
### Just again
`just configure` runs cmake configuration and generates a `compile_commands.json`.
`just build` runs the cmake build.
### CMake works too, I guess
`cmake -S. -Bbuild`
`cmake --build build`
Same as always
## Files
### thread_pool.h/cpp
Thread pool used for parallel GoL generation ticking.
### application.h/cpp
UI and layout logic.
### style.h/cpp
Reusable UI styling
### resources.h/cpp
Font loading.
### elements.h/cpp
Reusable UI elements
### input.h/cpp
Handling of input events.
### main.cpp:
Entrypoint, setup, and main application loop.
## Prerequisites
* CMake 3.21 or higher
* Compiler capable of C++23 and C23.
## Dependencies
### SDL3
Included as git submodule at `vendor/SDL3/` and dynamically linked.
### SDL3_ttf
Included as git submodule at `vendor/SDL3_ttf/` and dynamically linked.
### Clay
Included as files in `vendor/clay/clay.h`; Single header library.
### Clay SDL3 renderer
Included as files in `vendor/renderer/` and compiled as part of the project.
> Note: Mildly modified from the official Clay_SDL3_renderer to enable more advanced styling.
## Code Standards
* Keep program structure as simple as possible. No `class Application {` class or other Java-isms. Prefer namespaces with static lifetime variables.
* Use STL where possible. Don't reinvent the wheel.
* K&R brackets. With notable exceptions(1)
* camelCase for variables, PascalCase for types and functions (that's what SDL and Clay do).
* In class member functions, always use `this->` to access member variables.
* const applies to the name to it's left, so it goes after the type, not `const int x;` but `int const x`.
* \* and & flush with the declaration name. `Type const &Function(Type &inRef)`
> (1) Bracket exceptions:
> * using scoped_lock in arbitrary blocks, prefer tailed lisp brackets
```
struct Data {
int x{ 0 }, y{ 0 };
};
void MyFunction(Data const &data) { // K&R here
DoAsynchronousThings();
{ scoped_lock lock{ myMutex };
myVariable++;
} // not quite lisp, lisp with a tail
DoMoreAsynchronousThings();
}
```

View file

@ -1,14 +1,19 @@
build:
# BUILDING
bear -- cmake --build build
cmake --build build
run:
cd bin/ && DiceGui
cd bin/ && CHANGEME
configure:
# CONFIGURING WITH PREMAKE
cmake -S. -Bbuild
cmake -S. -Bbuild -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
clean:
# CLEANING BUILD ARTEFACTS
rm -r bin/**
rm -r build/**
set-project-name projectname: clean
git remote set-url origin ""
sed -i "s/CHANGEME/{{projectname}}/g" ./CMakeLists.txt ./justfile

View file

@ -1,172 +1,24 @@
#include "application.h"
#include "style.h"
#include "elements.h"
#include "simulation.h"
#include <SDL3/SDL.h>
#include <clay/clay.h>
namespace application {
static bool isSimulating{ false };
static bool lockFramerate{ true };
static void SetSimulatingButton(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) {
if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
isSimulating = data == 1;
}
}
static void RandomizeFieldButton(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) {
if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
simulation::InitializeRandom(2, 1000);
}
}
static void StepSimulationButton(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) {
if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
simulation::Step();
}
}
static void StepControl() {
CLAY_AUTO_ID(style::PanelContainer(1, {
.layout = {
.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT() },
.childGap = 16,
.layoutDirection = CLAY_TOP_TO_BOTTOM,
},
})) {
CLAY_AUTO_ID({
.layout = {
.childGap = 16
}
}) {
if (isSimulating) {
elements::TextButton(CLAY_STRING("Pause"), style::warningButton, &SetSimulatingButton, false);
} else {
elements::TextButton(CLAY_STRING("Start"), style::proceedButton, &SetSimulatingButton, true);
elements::TextButton(CLAY_STRING("Step"), style::actionButton, &StepSimulationButton);
}
}
elements::Toggle(CLAY_STRING("Show Changes"), style::actionButton, simulation::drawDebugInfo);
elements::Toggle(CLAY_STRING("Lock Speed"), style::actionButton, lockFramerate);
}
}
static void DebugInfoLegend() {
Clay_ElementDeclaration style{style::PanelContainer(1, {
.layout = {
.padding = {
.left = 20, .right = 0, .top = 0, .bottom = 0
},
.childGap = 10,
.layoutDirection = CLAY_TOP_TO_BOTTOM,
}
})
};
style.cornerRadius = { 0, style::defaultRadius, 0, style::defaultRadius };
CLAY_AUTO_ID(style) {
CLAY_AUTO_ID({
.layout = {
.childGap = 10,
},
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = { CLAY_SIZING_FIXED(20), CLAY_SIZING_FIXED(20) }
},
.border = { {255, 0, 255, 255 }, CLAY_BORDER_OUTSIDE(3) }
});
elements::Body(CLAY_STRING("Underpopulated"), {
.textColor = style::TextColor(0)
});
}
CLAY_AUTO_ID({
.layout = {
.childGap = 10,
}
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = { CLAY_SIZING_FIXED(20), CLAY_SIZING_FIXED(20) }
},
.border = { {255, 0, 0, 255 }, CLAY_BORDER_OUTSIDE(3) }
});
elements::Body(CLAY_STRING("Overpopulated"), {
.textColor = style::TextColor(0)
});
}
CLAY_AUTO_ID({
.layout = {
.childGap = 10,
}
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = { CLAY_SIZING_FIXED(20), CLAY_SIZING_FIXED(20) }
},
.border = { {0, 255, 0, 255 }, CLAY_BORDER_OUTSIDE(3) }
});
elements::Body(CLAY_STRING("Born"), {
.textColor = style::TextColor(0)
});
}
elements::Body(CLAY_STRING("Warning:\nDrawing simulation changes\ngreatly lowers performance."), {
.textColor = style::TextColor(2)
});
}
}
static void PrimaryControls() {
static bool isOpen{ true };
if (isOpen) {
CLAY_AUTO_ID(style::LeftPanelContainer(0, {
.layout = {
.sizing = { CLAY_SIZING_FIT(.18) },
}
})) {
CLAY_AUTO_ID({
.layout = {
.childGap = 16,
.layoutDirection = CLAY_TOP_TO_BOTTOM,
},
.clip = {
true, true, Clay_GetScrollOffset()
}
}) {
StepControl();
if (!isSimulating) {
CLAY_AUTO_ID(style::LeftPanelContainer(1, {
.layout = { .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT() } }
})) {
elements::TextButton(CLAY_STRING("Randomize"), style::actionButton, &RandomizeFieldButton);
}
}
}
}
}
if (simulation::drawDebugInfo) {
DebugInfoLegend();
}
}
static void TryStep() {
static uint64_t lastStep = 0;
if (isSimulating) {
double deltaTime{ (double)(SDL_GetTicks() - lastStep) * 0.001 };
constexpr double targetDeltaTime = 1.0/24.0;
if (!lockFramerate || deltaTime > targetDeltaTime) {
lastStep = SDL_GetTicks();
simulation::Step();
}
}
static void SampleHeader() {
elements::Header(CLAY_STRING("Left Panel"), 2, {
.textColor = style::color::white
});
}
Clay_RenderCommandArray RenderApplication() {
TryStep();
Clay_BeginLayout();
CLAY(CLAY_ID("OuterContainer"), style::Window()) {
simulation::SetSimulationHovered(Clay_Hovered());
PrimaryControls();
CLAY_AUTO_ID(style::PanelContainer(0, {
.layout = { .sizing = { CLAY_SIZING_PERCENT(0.15), CLAY_SIZING_GROW() } }
})) {
SampleHeader();
}
}
return Clay_EndLayout();
}

View file

@ -5,7 +5,6 @@
namespace elements {
typedef void(*OnHoveredFn)(Clay_ElementId element, Clay_PointerData pointer, intptr_t data);
void TextButton(Clay_String text, Clay_Color color, OnHoveredFn onHovered, intptr_t onHoveredData = 0);
void Toggle(Clay_String label, Clay_Color selected, bool &state);
void Body(Clay_String string, Clay_TextElementConfig baseCfg = {});

View file

@ -13,9 +13,9 @@ void HandleEvent(SDL_Event const &event) {
switch (event.type) {
case SDL_EVENT_MOUSE_WHEEL:
if (shiftDown) {
scrollMotion = (Clay_Vector2) { event.wheel.y * 4.f, -event.wheel.x * 4.f };
scrollMotion = (Clay_Vector2) { event.wheel.y * 4.f, -event.wheel.x * -4.f };
} else {
scrollMotion = (Clay_Vector2) { -event.wheel.x * 4.f, event.wheel.y * 4.f };
scrollMotion = (Clay_Vector2) { -event.wheel.x * -4.f, event.wheel.y * 4.f };
}
break;
case SDL_EVENT_MOUSE_MOTION:

View file

@ -3,7 +3,6 @@
#include "application.h"
#include "input.h"
#include "resources.h"
#include "simulation.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_events.h>
@ -133,7 +132,6 @@ int main(int argc, char *argv[]) {
Clay_UpdateScrollContainers(true, input::scrollMotion, deltaTime);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
simulation::Draw(renderer, 0.01);
Clay_RenderCommandArray commands{ application::RenderApplication() };
SDL_Clay_RenderClayCommands(&backendData, &commands);
SDL_RenderPresent(renderer);

View file

@ -1,327 +0,0 @@
#include "simulation.h"
#include "SDL3/SDL_timer.h"
#include "input.h"
#include "thread_pool.h"
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_render.h>
#include <algorithm>
#include <clay/clay.h>
#include <cstdint>
#include <mutex>
#include <set>
#include <vector>
#ifdef __glibc_likely
#define likely(cond_) __glibc_likely(cond_)
#else
#define likely(cond_) (cond_)
#endif
#define SIM_MULTITHREADING 1
namespace simulation {
bool drawDebugInfo{ true };
struct ThreadWorkload {
size_t seg_idx{0}, seg_len{1};
std::mutex mtx{};
std::vector<Cell> changes{};
};
static std::set<Cell> living{};
static std::vector<std::shared_ptr<ThreadWorkload>> overpopulated{};
static std::vector<std::shared_ptr<ThreadWorkload>> underpopulated{};
static std::vector<std::shared_ptr<ThreadWorkload>> born{};
static uint64_t generationStartTime{ 0 };
static unsigned tasks{ 0 }; std::mutex tasksMutex{};
static std::condition_variable tasksChanged{};
CellIterator::CellIterator(Cell begin, Cell end)
: state{ begin } , begin{ begin }, end{ end } {}
CellIterator::CellIterator(Cell begin, Cell end, Cell state)
: state{ state } , begin{ begin }, end{ end } {}
static inline bool CellIsAlive(Cell const &cell) {
return living.contains(cell);
}
CellIterator &CellIterator::operator++() {
++(this->state.x);
if (this->state.x == this->end.x) {
this->state.x = this->begin.x;
this->state.y = SDL_min(this->state.y + 1, this->end.y);
}
return *this;
}
bool CellIterator::operator==(CellIterator const &src) const {
return src.begin == this->begin && src.end == this->end && (src.state == this->state || src.at_end() == this->at_end());
}
bool CellIterator::operator!=(CellIterator const &src) const {
return src.begin != this->begin || src.end != this->end || (src.state != this->state && src.at_end() != this->at_end());
}
Cell const &CellIterator::operator*() const {
return state;
}
bool CellIterator::at_end() const {
return this->state.y == this->end.y;
}
static size_t CountNeighbors(Cell const &cell) {
size_t count{ 0 };
for (Cell const &c : CellRange{ {cell.x - 1, cell.y - 1}, { cell.x + 2, cell.y + 2} }) {
if (c != cell && CellIsAlive(c)) {
++count;
}
}
return count;
}
static void BlockUntilTasksDone() {
std::unique_lock lock{ tasksMutex };
while (tasks > 0) {
tasksChanged.wait(lock);
}
}
static void TaskBegin() {
std::scoped_lock lock{ tasksMutex };
tasks++;
}
static void TaskComplete() {
std::scoped_lock lock{ tasksMutex };
if (--tasks == 0) {
SDL_Log("Generation Complete %lfs", (double)(SDL_GetTicksNS() - generationStartTime) * 0.000000001);
}
tasksChanged.notify_all();
}
static void FindOverpopulated(std::shared_ptr<ThreadWorkload> wl) {
TaskBegin();
std::scoped_lock lock{ wl->mtx };
wl->changes.clear();
std::set<Cell>::iterator begin{ living.begin() };
std::set<Cell>::iterator end{ living.begin() };
std::advance(begin, wl->seg_idx * wl->seg_len);
std::advance(end, wl->seg_idx * wl->seg_len + wl->seg_len);
std::copy_if(begin, end, std::back_inserter(wl->changes),
[&](Cell const &c) -> bool {
return CountNeighbors(c) > 3;
});
TaskComplete();
}
static void FindUnderpopulated(std::shared_ptr<ThreadWorkload> wl) {
TaskBegin();
std::scoped_lock lock{ wl->mtx };
wl->changes.clear();
std::set<Cell>::iterator begin{ living.begin() };
std::set<Cell>::iterator end{ living.begin() };
std::advance(begin, wl->seg_idx * wl->seg_len);
std::advance(end, wl->seg_idx * wl->seg_len + wl->seg_len);
std::copy_if(begin, end, std::back_inserter(wl->changes),
[&](Cell const &c) -> bool {
return CountNeighbors(c) < 2;
});
TaskComplete();
}
static void FindBorn(std::shared_ptr<ThreadWorkload> wl) {
TaskBegin();
std::scoped_lock lock{ wl->mtx };
wl->changes.clear();
std::set<Cell>::iterator itr{ living.begin() };
std::advance(itr, wl->seg_idx * wl->seg_len);
std::for_each_n(itr, wl->seg_len, [&wl](Cell const &center){
CellRange range{{ center.x - 1, center.y - 1 }, {center.x + 2, center.y + 2 }};
std::copy_if(range.begin(), range.end(), std::back_inserter(wl->changes),
[&](Cell const &c) -> bool {
return !CellIsAlive(c) && CountNeighbors(c) == 3;
});
});
TaskComplete();
}
static void PopulateChanges() {
static bool first_run{ true };
#if SIM_MULTITHREADING
BlockUntilTasksDone();
constexpr size_t split{ 4 };
if (first_run) {
first_run = false;
born.resize(split);
underpopulated.resize(split);
overpopulated.resize(split);
}
SDL_Log("Multithreading ON");
generationStartTime = SDL_GetTicksNS();
size_t const seg_length{ living.size() / split };
for (size_t i{ 0 }; i < split; ++i) {
if (overpopulated[i] == nullptr)
overpopulated[i] = std::make_shared<ThreadWorkload>();
{ std::scoped_lock lock{ overpopulated[i]->mtx };
overpopulated[i]->seg_idx = i; overpopulated[i]->seg_len = seg_length;
threading::tasks.ScheduleTask(std::bind(FindOverpopulated, overpopulated[i]));
}
if (underpopulated[i] == nullptr)
underpopulated[i] = std::make_shared<ThreadWorkload>();
{ std::scoped_lock lock{ underpopulated[i]->mtx };
underpopulated[i]->seg_idx = i; underpopulated[i]->seg_len = seg_length;
threading::tasks.ScheduleTask(std::bind(FindUnderpopulated, underpopulated[i]));
}
if (born[i] == nullptr)
born[i] = std::make_shared<ThreadWorkload>();
{ std::scoped_lock lock{ born[i]->mtx };
born[i]->seg_idx = i; born[i]->seg_len = seg_length;
threading::tasks.ScheduleTask(std::bind(FindBorn, born[i]));
}
}
#else
if (first_run) {
first_run = false;
overpopulated.emplace_back(std::make_shared<ThreadWorkload>());
overpopulated[0]->seg_idx = 0;
overpopulated[0]->seg_len = living.size();
underpopulated.emplace_back(std::make_shared<ThreadWorkload>());
underpopulated[0]->seg_idx = 0;
underpopulated[0]->seg_len = living.size();
born.emplace_back(std::make_shared<ThreadWorkload>());
born[0]->seg_idx = 0;
born[0]->seg_len = living.size();
}
SDL_Log("Multithreading OFF");
{ std::scoped_lock lock{ tasksMutex };
tasks = 3; }
generationStartTime = SDL_GetTicksNS();
FindOverpopulated(overpopulated[0]);
FindUnderpopulated(underpopulated[0]);
FindBorn(born[0]);
#endif
}
void InitializeRandom(size_t livingChance, int64_t fillArea) {
BlockUntilTasksDone();
living.clear();
Cell itr{ 0, 0 };
while (itr.y < fillArea) {
if (std::rand() % livingChance == 0 ) {
living.insert(itr);
}
++itr.x;
if (itr.x == fillArea) {
itr.x = 0;
++itr.y;
}
}
PopulateChanges();
}
void Step() {
BlockUntilTasksDone();
for (std::shared_ptr<ThreadWorkload> wl : underpopulated) {
if (wl == nullptr) continue;
std::scoped_lock l{ wl->mtx };
for (Cell const &cell : wl->changes)
living.erase(cell);
}
for (std::shared_ptr<ThreadWorkload> wl : overpopulated) {
if (wl == nullptr) continue;
std::scoped_lock l{ wl->mtx };
for (Cell const &cell : wl->changes)
living.erase(cell);
}
for (std::shared_ptr<ThreadWorkload> wl : born) {
if (wl == nullptr) continue;
std::scoped_lock l{ wl->mtx };
for (Cell const &cell : wl->changes)
living.insert(cell);
}
PopulateChanges();
}
static bool simulationHovered{ false };
static SDL_FPoint viewOffset{ 0, 0 };
void Draw(SDL_Renderer *renderer, double cellSizePercent) {
if (simulationHovered) {
viewOffset.x += input::scrollMotion.x;
viewOffset.y += input::scrollMotion.y;
}
int w;
SDL_GetCurrentRenderOutputSize(renderer, &w, nullptr);
float const cellWidth = static_cast<float>(w) * cellSizePercent;
SDL_FRect cellRect{
0, 0, cellWidth, cellWidth
};
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
for (Cell const &cell : living) {
cellRect.x = (viewOffset.x + cell.x) * cellRect.w;
cellRect.y = (viewOffset.y + cell.y) * cellRect.h;
SDL_RenderFillRect(renderer, &cellRect);
}
if (!drawDebugInfo) {
return;
}
BlockUntilTasksDone();
for (std::shared_ptr<ThreadWorkload> wl : overpopulated) {
std::scoped_lock l{ wl->mtx };
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
for (Cell const &cell : wl->changes) {
cellRect.x = (viewOffset.x + cell.x) * cellRect.w;
cellRect.y = (viewOffset.y + cell.y) * cellRect.h;
SDL_RenderRect(renderer, &cellRect);
}
}
for (std::shared_ptr<ThreadWorkload> wl : underpopulated) {
std::scoped_lock l{ wl->mtx };
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
for (Cell const &cell : wl->changes) {
cellRect.x = (viewOffset.x + cell.x) * cellRect.w;
cellRect.y = (viewOffset.y + cell.y) * cellRect.h;
SDL_RenderRect(renderer, &cellRect);
}
}
for (std::shared_ptr<ThreadWorkload> wl : born) {
std::scoped_lock l{ wl->mtx };
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
for (Cell const &cell : wl->changes) {
cellRect.x = (viewOffset.x + cell.x) * cellRect.w;
cellRect.y = (viewOffset.y + cell.y) * cellRect.h;
SDL_RenderRect(renderer, &cellRect);
}
}
}
void SetSimulationHovered(bool value) {
simulationHovered = value;
}
bool operator==(Cell const &lhs, Cell const &rhs) {
return lhs.x == rhs.x && lhs.y == rhs.y;
}
bool operator!=(Cell const &lhs, Cell const &rhs) {
return lhs.x != rhs.x || lhs.y != rhs.y;
}
bool operator<(Cell const &lhs, Cell const &rhs) {
if (lhs.y == rhs.y) return lhs.x < rhs.x;
else return lhs.y < rhs.y;
}
}

View file

@ -1,48 +0,0 @@
#ifndef SIMULATION_H
#define SIMULATION_H
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_render.h>
#include <clay/clay.h>
namespace simulation {
struct Cell {
int64_t x{ 0 }, y{ 0 };
};
class CellIterator {
friend class CellRange;
public: CellIterator(Cell begin, Cell end);
CellIterator &operator++();
CellIterator &operator=(CellIterator const &src);
bool operator==(CellIterator const &src) const;
bool operator!=(CellIterator const &src) const;
Cell const &operator*() const;
bool at_end() const;
private: CellIterator(Cell begin, Cell end, Cell state);
Cell state{ 0, 0 };
Cell begin{ 0, 0 }, end{ 0, 0 };
};
class CellRange {
private:
Cell beginCell{ 0, 0 }, endCell{ 0, 0 };
public: CellRange(Cell begin, Cell end) : beginCell{ begin }, endCell{ end } {}
CellIterator begin() { return CellIterator{ beginCell, endCell }; }
CellIterator end() { return CellIterator{ beginCell, endCell, endCell }; }
};
extern bool drawDebugInfo;
void InitializeRandom(size_t livingChance, int64_t fillArea);
void Step();
void Draw(SDL_Renderer *renderer, double cellSizePercent);
void SetSimulationHovered(bool value);
bool operator==(Cell const &lhs, Cell const &rhs);
bool operator!=(Cell const &lhs, Cell const &rhs);
bool operator<(Cell const &lhs, Cell const &rhs);
}
#endif // !SIMULATION_H

View file

@ -19,7 +19,7 @@ Clay_ElementDeclaration PanelContainer(size_t depth, Clay_ElementDeclaration bas
CLAY_BORDER_OUTSIDE(2)
};
baseCfg.cornerRadius = defaultRadiusAll;
baseCfg.layout.padding = CLAY_PADDING_ALL(16);
baseCfg.layout.padding = CLAY_PADDING_ALL(8);
return baseCfg;
}

View file

@ -35,8 +35,7 @@ void ThreadPool::ScheduleTask(TaskFunc fn) {
void ThreadPool::ThreadFn() {
TaskFunc function;
for(;;) {
{
std::unique_lock<std::mutex> lock{ this->lock };
{ std::unique_lock<std::mutex> lock{ this->lock };
while (!this->shutdown && this->taskQueue.empty()) {
this->threadNotifier.wait(lock);
}