Compare commits
19 Commits
master
...
00858cf74f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00858cf74f | ||
|
|
3e065e50a9 | ||
|
|
ab0f152742 | ||
|
|
014821ceb5 | ||
|
|
1bd8a9f7b2 | ||
|
|
6f620644bf | ||
|
|
0008c31f53 | ||
|
|
f8068c4a15 | ||
|
|
66c56f2c15 | ||
|
|
6247f2af49 | ||
|
|
40e07e03f2 | ||
|
|
0abd353368 | ||
|
|
0ac316ada4 | ||
|
|
a03fa13a07 | ||
|
|
ff9a2851d4 | ||
|
|
6c2bc93874 | ||
|
|
7a4d158d2f | ||
|
|
e032303773 | ||
|
|
9821a2ab15 |
39
README.org
39
README.org
@@ -20,28 +20,8 @@ the generator fraction is in green.
|
||||
This was done just for fun really, but it's quite fun to see it
|
||||
generate a dense number line over many iterations.
|
||||
* TODOs
|
||||
** TODO Tree visualisation
|
||||
Instead of a number line, how about visualising the actual tree at
|
||||
work as a graph of nodes? Maybe colouring nodes based on where it is
|
||||
on the number line.
|
||||
** TODO Don't walk the tree everytime we compute_bounds
|
||||
[[file:src/state.cpp::void DrawState::compute_bounds()][location]]
|
||||
|
||||
We already have the latest bound nodes so we're part-way through the
|
||||
tree. Just keep going down what we have so far surely? Even better,
|
||||
don't use nodes _at all_. Run with an index!
|
||||
** DONE Fix weird issue at past 100K nodes
|
||||
std::vector seems to crap itself past 100K nodes - we keep getting
|
||||
heap-use-after-free issues when trying to access the allocator nodes
|
||||
at that point. Seemingly random. What's going on?
|
||||
|
||||
Solution: _everytime_ we want to access the allocator for nontrivial
|
||||
(like memory reads) we need to lock the mutex. No two ways about it.
|
||||
All draw functions were causing the issue.
|
||||
** DONE Prettify code base
|
||||
It's a big blob of code currently in the graphics portion. Not very
|
||||
pretty but it gets the job done. Try modularisation.
|
||||
** DONE Multithreading
|
||||
** TODO Multithreading
|
||||
SCHEDULED: <2025-11-18 Tue>
|
||||
Currently single threaded. A multithreaded implementation could have
|
||||
multiple nodes generated at once, which would speed up the
|
||||
implementation.
|
||||
@@ -49,7 +29,7 @@ implementation.
|
||||
Might need to study my current implementation to see if it could be
|
||||
done better.
|
||||
|
||||
*** DONE Formalise state structure (separate drawing functions)
|
||||
*** TODO Formalise state structure (separate drawing functions)
|
||||
Make a dedicated header and fit the necessary functions to our state
|
||||
structure: [[file:src/main.cpp::struct State][state structure]].
|
||||
|
||||
@@ -60,14 +40,14 @@ A good working name would be ~cw_state~.
|
||||
|
||||
We could then have a separate structure for the drawing context
|
||||
(~cw_draw~) which can update itself by reading the ~cw_state~.
|
||||
*** DONE Setup a queue and allocator mutex on state
|
||||
*** TODO Setup a queue and allocator mutex on state
|
||||
We need one for the queue so we can make clean pushes and pops, and
|
||||
one for the allocator to ensure we're not messing up our indices in
|
||||
the nodes.
|
||||
|
||||
We could just set these up in the state structure itself to make
|
||||
lookup easier.
|
||||
*** DONE Make iterate use the state structure and mutexes
|
||||
*** TODO Make iterate use the state structure and mutexes
|
||||
[[file:src/numerics.cpp::std::tuple<Fraction, Fraction, Fraction>
|
||||
iterate(std::queue<word_t> &queue,]]
|
||||
|
||||
@@ -84,4 +64,11 @@ iterate(std::queue<word_t> &queue,]]
|
||||
|
||||
I think this scheme minimises the any mutex is locked to just the bare
|
||||
minimum, ensuring other threads can get in on the action.
|
||||
*** DONE Setup a thread pool utilising state and iterate
|
||||
*** TODO Setup a thread pool utilising state and iterate
|
||||
** TODO Prettify code base
|
||||
It's a big blob of code currently in the graphics portion. Not very
|
||||
pretty but it gets the job done. Try modularisation.
|
||||
** TODO Tree visualisation
|
||||
Instead of a number line, how about visualising the actual tree at
|
||||
work as a graph of nodes? Maybe colouring nodes based on where it is
|
||||
on the number line.
|
||||
|
||||
20
build.sh
20
build.sh
@@ -3,23 +3,13 @@
|
||||
set -xe
|
||||
|
||||
OUT="cw_tree.out"
|
||||
GFLAGS="-Wall -Wextra -Wswitch-enum -std=c++17 -Iraylib-5.5_linux_amd64/include"
|
||||
LIBS="-Lraylib-5.5_linux_amd64/lib -l:libraylib.a" # link statically with raylib
|
||||
VARFLAGS="-DTHREAD_PAUSE_MS=1000 -DTHREAD_GENERAL_MS=1"
|
||||
|
||||
GFLAGS="-Wall -Wextra -Wswitch-enum -std=c++17"
|
||||
DFLAGS="-ggdb -fsanitize=address -fsanitize=undefined"
|
||||
RFLAGS="-O2"
|
||||
CFLAGS="$GFLAGS $VARFLAGS $DFLAGS"
|
||||
if [ "$1" = "release" ]
|
||||
then
|
||||
CFLAGS="$GFLAGS $VARFLAGS $RFLAGS"
|
||||
shift 1
|
||||
elif [ "$1" = "debug" ]
|
||||
then
|
||||
shift 1
|
||||
fi
|
||||
CFLAGS="$GFLAGS $DFLAGS"
|
||||
LIBS="-lraylib -lm"
|
||||
|
||||
c++ $CFLAGS -o $OUT src/node.cpp src/state.cpp src/worker.cpp src/numerics.cpp src/main.cpp $LIBS
|
||||
|
||||
c++ $CFLAGS -o $OUT src/node.cpp src/state.cpp src/worker.cpp src/main.cpp $LIBS
|
||||
if [ "$1" = "run" ]
|
||||
then
|
||||
./$OUT
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
|
||||
|
||||
This software is provided "as-is", without any express or implied warranty. In no event
|
||||
will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial
|
||||
applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you
|
||||
wrote the original software. If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented
|
||||
as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -1,150 +0,0 @@
|
||||
<img align="left" style="width:260px" src="https://github.com/raysan5/raylib/blob/master/logo/raylib_logo_animation.gif" width="288px">
|
||||
|
||||
**raylib is a simple and easy-to-use library to enjoy videogames programming.**
|
||||
|
||||
raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's especially well suited for prototyping, tooling, graphical applications, embedded systems and education.
|
||||
|
||||
*NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.*
|
||||
|
||||
Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
[](https://github.com/raysan5/raylib/releases)
|
||||
[](https://github.com/raysan5/raylib/stargazers)
|
||||
[](https://github.com/raysan5/raylib/commits/master)
|
||||
[](https://github.com/sponsors/raysan5)
|
||||
[](https://repology.org/project/raylib/versions)
|
||||
[](LICENSE)
|
||||
|
||||
[](https://discord.gg/raylib)
|
||||
[](https://www.reddit.com/r/raylib/)
|
||||
[](https://www.youtube.com/c/raylib)
|
||||
[](https://www.twitch.tv/raysan5)
|
||||
|
||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows)
|
||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux)
|
||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS)
|
||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly)
|
||||
|
||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds)
|
||||
[](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml)
|
||||
[](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml)
|
||||
|
||||
features
|
||||
--------
|
||||
- **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external)
|
||||
- Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!**
|
||||
- Written in plain C code (C99) using PascalCase/camelCase notation
|
||||
- Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0**)
|
||||
- **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h)
|
||||
- Multiple **Fonts** formats supported (TTF, OTF, FNT, BDF, sprite fonts)
|
||||
- Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC)
|
||||
- **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more!
|
||||
- Flexible Materials system, supporting classic maps and **PBR maps**
|
||||
- **Animated 3D models** supported (skeletal bones animation) (IQM, M3D, glTF)
|
||||
- Shaders support, including model shaders and **postprocessing** shaders
|
||||
- **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h)
|
||||
- Audio loading and playing with streaming support (WAV, QOA, OGG, MP3, FLAC, XM, MOD)
|
||||
- **VR stereo rendering** support with configurable HMD device parameters
|
||||
- Huge examples collection with [+140 code examples](https://github.com/raysan5/raylib/tree/master/examples)!
|
||||
- Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
|
||||
- **Free and open source**
|
||||
|
||||
basic example
|
||||
--------------
|
||||
This is a basic raylib example, it creates a window and draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window).
|
||||
```c
|
||||
#include "raylib.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
InitWindow(800, 450, "raylib [core] example - basic window");
|
||||
|
||||
while (!WindowShouldClose())
|
||||
{
|
||||
BeginDrawing();
|
||||
ClearBackground(RAYWHITE);
|
||||
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
CloseWindow();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
build and installation
|
||||
----------------------
|
||||
|
||||
raylib binary releases for Windows, Linux, macOS, Android and HTML5 are available at the [Github Releases page](https://github.com/raysan5/raylib/releases).
|
||||
|
||||
raylib is also available via multiple package managers on multiple OS distributions.
|
||||
|
||||
#### Installing and building raylib on multiple platforms
|
||||
|
||||
[raylib Wiki](https://github.com/raysan5/raylib/wiki#development-platforms) contains detailed instructions on building and usage on multiple platforms.
|
||||
|
||||
- [Working on Windows](https://github.com/raysan5/raylib/wiki/Working-on-Windows)
|
||||
- [Working on macOS](https://github.com/raysan5/raylib/wiki/Working-on-macOS)
|
||||
- [Working on GNU Linux](https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux)
|
||||
- [Working on Chrome OS](https://github.com/raysan5/raylib/wiki/Working-on-Chrome-OS)
|
||||
- [Working on FreeBSD](https://github.com/raysan5/raylib/wiki/Working-on-FreeBSD)
|
||||
- [Working on Raspberry Pi](https://github.com/raysan5/raylib/wiki/Working-on-Raspberry-Pi)
|
||||
- [Working for Android](https://github.com/raysan5/raylib/wiki/Working-for-Android)
|
||||
- [Working for Web (HTML5)](https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5))
|
||||
- [Working anywhere with CMake](https://github.com/raysan5/raylib/wiki/Working-with-CMake)
|
||||
|
||||
*Note that the Wiki is open for edit, if you find some issues while building raylib for your target platform, feel free to edit the Wiki or open an issue related to it.*
|
||||
|
||||
#### Setup raylib with multiple IDEs
|
||||
|
||||
raylib has been developed on Windows platform using [Notepad++](https://notepad-plus-plus.org/) and [MinGW GCC](https://www.mingw-w64.org/) compiler but it can be used with other IDEs on multiple platforms.
|
||||
|
||||
[Projects directory](https://github.com/raysan5/raylib/tree/master/projects) contains several ready-to-use **project templates** to build raylib and code examples with multiple IDEs.
|
||||
|
||||
*Note that there are lots of IDEs supported, some of the provided templates could require some review, so please, if you find some issue with a template or you think they could be improved, feel free to send a PR or open a related issue.*
|
||||
|
||||
learning and docs
|
||||
------------------
|
||||
|
||||
raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works.
|
||||
|
||||
Some additional documentation about raylib design can be found in [raylib GitHub Wiki](https://github.com/raysan5/raylib/wiki). Here are the relevant links:
|
||||
|
||||
- [raylib cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html)
|
||||
- [raylib architecture](https://github.com/raysan5/raylib/wiki/raylib-architecture)
|
||||
- [raylib library design](https://github.com/raysan5/raylib/wiki)
|
||||
- [raylib examples collection](https://github.com/raysan5/raylib/tree/master/examples)
|
||||
- [raylib games collection](https://github.com/raysan5/raylib-games)
|
||||
|
||||
|
||||
contact and networks
|
||||
---------------------
|
||||
|
||||
raylib is present in several networks and raylib community is growing everyday. If you are using raylib and enjoying it, feel free to join us in any of these networks. The most active network is our [Discord server](https://discord.gg/raylib)! :)
|
||||
|
||||
- Webpage: [https://www.raylib.com](https://www.raylib.com)
|
||||
- Discord: [https://discord.gg/raylib](https://discord.gg/raylib)
|
||||
- Twitter: [https://www.twitter.com/raysan5](https://www.twitter.com/raysan5)
|
||||
- Twitch: [https://www.twitch.tv/raysan5](https://www.twitch.tv/raysan5)
|
||||
- Reddit: [https://www.reddit.com/r/raylib](https://www.reddit.com/r/raylib)
|
||||
- Patreon: [https://www.patreon.com/raylib](https://www.patreon.com/raylib)
|
||||
- YouTube: [https://www.youtube.com/channel/raylib](https://www.youtube.com/c/raylib)
|
||||
|
||||
contributors
|
||||
------------
|
||||
|
||||
<a href="https://github.com/raysan5/raylib/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=raysan5/raylib&max=500&columns=20&anon=1" />
|
||||
</a>
|
||||
|
||||
license
|
||||
-------
|
||||
|
||||
raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details.
|
||||
|
||||
raylib uses internally some libraries for window/graphics/inputs management and also to support different file formats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on [raylib Wiki](https://github.com/raysan5/raylib/wiki) for details.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
295
src/main.cpp
295
src/main.cpp
@@ -15,31 +15,30 @@
|
||||
#include <tuple>
|
||||
|
||||
#include <raylib.h>
|
||||
#include <raymath.h>
|
||||
|
||||
#include "base.hpp"
|
||||
#include "node.hpp"
|
||||
#include "numerics.hpp"
|
||||
#include "worker.hpp"
|
||||
|
||||
#define WIDTH 1024
|
||||
#define HEIGHT 800
|
||||
#define HEIGHT 1024
|
||||
#define FONT_SIZE 20
|
||||
#define CIRCLE_SIZE 2
|
||||
#define LINE_TOP (7 * HEIGHT / 16)
|
||||
#define LINE_BOTTOM (9 * HEIGHT / 16)
|
||||
#define N_THREADS 15
|
||||
|
||||
using cw::state::DrawState;
|
||||
using cw::state::State;
|
||||
|
||||
std::pair<std::string, int> fraction_to_string(cw::node::Fraction f)
|
||||
std::pair<std::string, int> fraction_to_string(Fraction f)
|
||||
{
|
||||
std::string s{to_string(f)};
|
||||
int width = MeasureText(s.c_str(), FONT_SIZE);
|
||||
return std::make_pair(s, width);
|
||||
}
|
||||
|
||||
void draw_fraction(cw::node::Fraction f, u64 x, u64 y)
|
||||
void draw_fraction(Fraction f, word_t x, word_t y)
|
||||
{
|
||||
std::string s;
|
||||
int width;
|
||||
@@ -48,46 +47,36 @@ void draw_fraction(cw::node::Fraction f, u64 x, u64 y)
|
||||
DrawText(s.c_str(), x - width / 2, y - FONT_SIZE, FONT_SIZE, WHITE);
|
||||
}
|
||||
|
||||
void draw_tree(DrawState &ds, State &state)
|
||||
constexpr u64 clamp_to_width(const DrawState &ds, f64 val)
|
||||
{
|
||||
return (WIDTH / (ds.bounds.upper_val - ds.bounds.lower_val)) *
|
||||
(val - ds.bounds.lower_val);
|
||||
}
|
||||
|
||||
void draw_tree(const DrawState &ds)
|
||||
{
|
||||
// Number line
|
||||
DrawLine(0, HEIGHT / 2, WIDTH, HEIGHT / 2, WHITE);
|
||||
|
||||
// Bounds
|
||||
state.mutex.lock();
|
||||
std::stack<std::pair<u64, f64>> stack;
|
||||
cw::node::Node n = state.allocator.get_val(0);
|
||||
stack.push(std::make_pair(0, n.value.norm));
|
||||
u64 lower_x = clamp_to_width(ds, ds.bounds.leftmost.value.norm);
|
||||
u64 upper_x = clamp_to_width(ds, ds.bounds.rightmost.value.norm);
|
||||
DrawLine(lower_x, LINE_TOP, lower_x, LINE_BOTTOM, WHITE);
|
||||
DrawLine(upper_x, LINE_TOP, upper_x, LINE_BOTTOM, WHITE);
|
||||
|
||||
std::stack<cw::node::Node> stack;
|
||||
stack.push(ds.state.allocator.get_val(0));
|
||||
while (!stack.empty())
|
||||
{
|
||||
u64 node_index;
|
||||
f64 norm;
|
||||
std::tie(node_index, norm) = stack.top();
|
||||
auto n = stack.top();
|
||||
stack.pop();
|
||||
u64 x = Remap(norm, ds.bounds.lower_val, ds.bounds.upper_val, 0, WIDTH);
|
||||
u64 x = clamp_to_width(ds, n.value.norm);
|
||||
DrawLine(x, LINE_TOP, x, LINE_BOTTOM, RED);
|
||||
|
||||
cw::node::Node n = state.allocator.get_val(node_index);
|
||||
if (n.left >= 0)
|
||||
{
|
||||
cw::node::Node left = state.allocator.get_val(n.left);
|
||||
stack.push(std::make_pair(n.left, left.value.norm));
|
||||
}
|
||||
stack.push(ds.state.allocator.get_val(n.left));
|
||||
if (n.right >= 0)
|
||||
{
|
||||
cw::node::Node right = state.allocator.get_val(n.right);
|
||||
stack.push(std::make_pair(n.right, right.value.norm));
|
||||
}
|
||||
stack.push(ds.state.allocator.get_val(n.right));
|
||||
}
|
||||
state.mutex.unlock();
|
||||
|
||||
DrawLine(0, LINE_TOP, 0, LINE_BOTTOM, WHITE);
|
||||
DrawText("0", 0, LINE_TOP - FONT_SIZE, FONT_SIZE, WHITE);
|
||||
DrawLine(WIDTH, LINE_TOP, WIDTH, LINE_BOTTOM, WHITE);
|
||||
char buffer[64];
|
||||
sprintf(buffer, "%d", (int)ds.bounds.upper_val);
|
||||
DrawText(buffer, WIDTH - (FONT_SIZE / 2), LINE_TOP - FONT_SIZE, FONT_SIZE,
|
||||
WHITE);
|
||||
}
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
@@ -95,19 +84,14 @@ using Ms = std::chrono::milliseconds;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Init general state
|
||||
cw::state::State state;
|
||||
cw::state::DrawState draw_state{state};
|
||||
|
||||
// Init timer
|
||||
auto time_current = Clock::now();
|
||||
auto time_previous = time_current;
|
||||
constexpr auto time_delta = 0;
|
||||
|
||||
// Init general state
|
||||
cw::state::State state;
|
||||
state.stop_work = false;
|
||||
state.pause_work = false;
|
||||
state.allocator.alloc(cw::node::Node{{1, 1}, -1, -1});
|
||||
state.queue.push(0);
|
||||
|
||||
cw::state::DrawState draw_state{state};
|
||||
constexpr auto time_delta = 1;
|
||||
|
||||
// Init meta text (counter, iterations, etc)
|
||||
u64 count = 1, prev_count = 0;
|
||||
@@ -115,34 +99,32 @@ int main(void)
|
||||
std::string format_str;
|
||||
u64 format_str_width = 0;
|
||||
|
||||
// Init threads
|
||||
std::thread threads[N_THREADS];
|
||||
for (auto i = 0; i < N_THREADS; ++i)
|
||||
{
|
||||
threads[i] = std::move(std::thread(cw::worker::worker, std::ref(state)));
|
||||
}
|
||||
// Setup our first node (1/1)
|
||||
state.allocator.alloc(cw::node::Node{{1, 1}, -1, -1});
|
||||
state.queue.push(0);
|
||||
|
||||
// Init thread
|
||||
std::thread threads[] = {
|
||||
std::thread(cw::worker::worker, std::ref(state)),
|
||||
std::thread(cw::worker::worker, std::ref(state)),
|
||||
std::thread(cw::worker::worker, std::ref(state)),
|
||||
std::thread(cw::worker::worker, std::ref(state)),
|
||||
};
|
||||
|
||||
// Setup raylib window
|
||||
InitWindow(WIDTH, HEIGHT, "Calkin-Wilf tree");
|
||||
SetTargetFPS(60);
|
||||
|
||||
// setup camera
|
||||
Camera2D camera;
|
||||
camera.target = {.x = 0, .y = 0};
|
||||
camera.offset = {.x = WIDTH / 16, .y = 0};
|
||||
camera.rotation = 0.0f;
|
||||
camera.zoom = 0.8f;
|
||||
|
||||
while (!WindowShouldClose())
|
||||
{
|
||||
// Update
|
||||
time_current = Clock::now();
|
||||
count = state.allocator.vec.size();
|
||||
if (!state.pause_work &&
|
||||
std::chrono::duration_cast<Ms>(time_current - time_previous).count() >=
|
||||
time_delta)
|
||||
{
|
||||
time_previous = time_current;
|
||||
count = state.allocator.vec.size();
|
||||
}
|
||||
|
||||
if (prev_count != count)
|
||||
@@ -159,34 +141,13 @@ int main(void)
|
||||
format_str_width = MeasureText(format_str.c_str(), FONT_SIZE * 2);
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KEY_SPACE))
|
||||
state.pause_work = !state.pause_work;
|
||||
|
||||
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
|
||||
{
|
||||
Vector2 delta = GetMouseDelta();
|
||||
delta = Vector2Scale(delta, -1.0f / camera.zoom);
|
||||
camera.target = Vector2Add(camera.target, delta);
|
||||
}
|
||||
float wheel = GetMouseWheelMove();
|
||||
if (wheel != 0)
|
||||
{
|
||||
Vector2 mouse_to_world = GetScreenToWorld2D(GetMousePosition(), camera);
|
||||
camera.offset = GetMousePosition();
|
||||
camera.target = mouse_to_world;
|
||||
camera.zoom = Clamp(camera.zoom + (wheel * 0.1f), 0.125, 64);
|
||||
printf("%lf\n", camera.zoom);
|
||||
}
|
||||
|
||||
// Draw
|
||||
|
||||
ClearBackground(BLACK);
|
||||
BeginDrawing();
|
||||
BeginMode2D(camera);
|
||||
draw_tree(draw_state, state);
|
||||
EndMode2D();
|
||||
DrawText(format_str.c_str(), (31 * WIDTH / 32) - format_str_width / 2,
|
||||
HEIGHT / 32, FONT_SIZE, WHITE);
|
||||
draw_tree(draw_state);
|
||||
DrawText(format_str.c_str(), WIDTH / 2 - format_str_width / 2, HEIGHT / 8,
|
||||
FONT_SIZE, WHITE);
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
@@ -200,6 +161,178 @@ int main(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct State
|
||||
{
|
||||
NodeAllocator allocator;
|
||||
std::queue<word_t> iteration_queue;
|
||||
word_t root;
|
||||
|
||||
struct Bounds
|
||||
{
|
||||
Node leftmost, rightmost;
|
||||
long double lower, upper;
|
||||
} bounds;
|
||||
|
||||
struct Iteration
|
||||
{
|
||||
Fraction left, centre, right;
|
||||
} iteration;
|
||||
|
||||
State(const Fraction start) : allocator{256}
|
||||
{
|
||||
root = allocator.alloc(start);
|
||||
iteration_queue.push(root);
|
||||
bounds.leftmost = allocator.getVal(root);
|
||||
bounds.rightmost = allocator.getVal(root);
|
||||
compute_bounds();
|
||||
}
|
||||
|
||||
void do_iteration(void)
|
||||
{
|
||||
std::tie(iteration.left, iteration.centre, iteration.right) =
|
||||
iterate(iteration_queue, allocator);
|
||||
compute_bound_nodes();
|
||||
compute_bounds();
|
||||
}
|
||||
|
||||
void compute_bounds()
|
||||
{
|
||||
bounds.lower = std::floorl(bounds.leftmost.value.norm);
|
||||
bounds.upper = std::ceill(bounds.rightmost.value.norm);
|
||||
}
|
||||
|
||||
void compute_bound_nodes()
|
||||
{
|
||||
bounds.leftmost = allocator.getVal(0);
|
||||
while (bounds.leftmost.left.has_value())
|
||||
bounds.leftmost = allocator.getVal(bounds.leftmost.left.value());
|
||||
|
||||
bounds.rightmost = allocator.getVal(0);
|
||||
while (bounds.rightmost.right.has_value())
|
||||
bounds.rightmost = allocator.getVal(bounds.rightmost.right.value());
|
||||
}
|
||||
|
||||
constexpr word_t clamp_to_width(long double value)
|
||||
{
|
||||
return (WIDTH / (bounds.upper - bounds.lower)) * (value - bounds.lower);
|
||||
}
|
||||
|
||||
void draw_bounds()
|
||||
{
|
||||
word_t lower_x = clamp_to_width(bounds.leftmost.value.norm);
|
||||
word_t upper_x = clamp_to_width(bounds.rightmost.value.norm);
|
||||
DrawLine(lower_x, LINE_TOP, lower_x, LINE_BOTTOM, WHITE);
|
||||
DrawLine(upper_x, LINE_TOP, upper_x, LINE_BOTTOM, WHITE);
|
||||
}
|
||||
|
||||
void draw_nodes()
|
||||
{
|
||||
std::stack<Node> stack;
|
||||
stack.push(allocator.getVal(0));
|
||||
while (!stack.empty())
|
||||
{
|
||||
Node n = stack.top();
|
||||
stack.pop();
|
||||
word_t x = clamp_to_width(n.value.norm);
|
||||
DrawLine(x, LINE_TOP, x, LINE_BOTTOM, RED);
|
||||
if (n.left.has_value())
|
||||
stack.push(allocator.getVal(n.left.value()));
|
||||
if (n.right.has_value())
|
||||
stack.push(allocator.getVal(n.right.value()));
|
||||
}
|
||||
}
|
||||
|
||||
void draw_iteration_nodes()
|
||||
{
|
||||
word_t x_left = clamp_to_width(iteration.left.norm);
|
||||
word_t x_centre = clamp_to_width(iteration.centre.norm);
|
||||
word_t x_right = clamp_to_width(iteration.right.norm);
|
||||
DrawLine(x_left, LINE_TOP, x_left, LINE_BOTTOM, BLUE);
|
||||
DrawLine(x_right, LINE_TOP, x_right, LINE_BOTTOM, BLUE);
|
||||
DrawLine(x_centre, LINE_TOP, x_centre, LINE_BOTTOM, GREEN);
|
||||
}
|
||||
};
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using Ms = std::chrono::milliseconds;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Setup state
|
||||
State state{{1, 1}};
|
||||
|
||||
// Setup meta text (counter, iterations, etc)
|
||||
word_t count = 1, prev_count = 0;
|
||||
std::stringstream format_stream;
|
||||
std::string format_str;
|
||||
word_t format_str_width = 0;
|
||||
|
||||
// Setup timer
|
||||
bool is_playing = false;
|
||||
auto time_current = Clock::now();
|
||||
auto time_previous = time_current;
|
||||
constexpr auto time_delta = 1;
|
||||
|
||||
InitWindow(WIDTH, HEIGHT, "Calkin-Wilf Tree");
|
||||
while (!WindowShouldClose())
|
||||
{
|
||||
// timer logic
|
||||
time_current = Clock::now();
|
||||
if (is_playing &&
|
||||
std::chrono::duration_cast<Ms>(time_current - time_previous).count() >=
|
||||
time_delta)
|
||||
{
|
||||
time_previous = time_current;
|
||||
state.do_iteration();
|
||||
count += 2;
|
||||
}
|
||||
|
||||
// Input logic
|
||||
if (IsKeyDown(KEY_SPACE))
|
||||
{
|
||||
is_playing = true;
|
||||
}
|
||||
else if (IsKeyUp(KEY_SPACE))
|
||||
{
|
||||
is_playing = false;
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KEY_PERIOD))
|
||||
{
|
||||
state.do_iteration();
|
||||
count += 2;
|
||||
}
|
||||
|
||||
// Meta text logic
|
||||
if (prev_count != count)
|
||||
{
|
||||
prev_count = count;
|
||||
format_stream << "Count=" << count << "\n\n"
|
||||
<< "Iterations=" << (count - 1) / 2 << "\n\n"
|
||||
<< "Lower=" << to_string(state.bounds.leftmost.value)
|
||||
<< "\n\n"
|
||||
<< "Upper=" << to_string(state.bounds.rightmost.value);
|
||||
format_str = format_stream.str();
|
||||
format_stream.str("");
|
||||
format_str_width = MeasureText(format_str.c_str(), FONT_SIZE * 2);
|
||||
}
|
||||
|
||||
ClearBackground(BLACK);
|
||||
BeginDrawing();
|
||||
DrawLine(0, HEIGHT / 2, WIDTH, HEIGHT / 2, WHITE);
|
||||
state.draw_nodes();
|
||||
state.draw_bounds();
|
||||
state.draw_iteration_nodes();
|
||||
DrawText(format_str.c_str(), WIDTH / 2 - format_str_width / 2, HEIGHT / 8,
|
||||
FONT_SIZE, WHITE);
|
||||
EndDrawing();
|
||||
}
|
||||
CloseWindow();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Copyright (C) 2024, 2025 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
|
||||
153
src/numerics.cpp
Normal file
153
src/numerics.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
/* numerics.cpp: Implementation of numerics
|
||||
* Created: 2024-07-26
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include "./numerics.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
Fraction::Fraction(word_t numerator, word_t denominator)
|
||||
: numerator{numerator}, denominator{denominator},
|
||||
norm{numerator / ((long double)denominator)}
|
||||
{
|
||||
word_t hcf = gcd(MIN(numerator, denominator), MAX(numerator, denominator));
|
||||
numerator /= hcf;
|
||||
denominator /= hcf;
|
||||
}
|
||||
|
||||
// floating point arithmetic inaccuracies blah blah blah better to use
|
||||
// simplified fractions here
|
||||
|
||||
bool Fraction::operator<(const Fraction other)
|
||||
{
|
||||
if (other.denominator == denominator)
|
||||
return numerator < other.numerator;
|
||||
// TODO: Is it better to use the GCD?
|
||||
return (numerator * other.denominator) < (other.numerator * denominator);
|
||||
}
|
||||
|
||||
bool Fraction::operator==(const Fraction &other)
|
||||
{
|
||||
return numerator == other.numerator && denominator == other.denominator;
|
||||
}
|
||||
|
||||
Node::Node(Fraction val, index_t left, index_t right)
|
||||
: value{val}, left{left}, right{right}
|
||||
{
|
||||
}
|
||||
|
||||
NodeAllocator::NodeAllocator(word_t capacity)
|
||||
{
|
||||
vec.reserve(capacity);
|
||||
}
|
||||
|
||||
word_t NodeAllocator::alloc(Node n)
|
||||
{
|
||||
word_t ind = vec.size();
|
||||
vec.push_back(n);
|
||||
return ind;
|
||||
}
|
||||
|
||||
// WHY DO I NEED TO DO IT TWICE REEEEEEE
|
||||
Node &NodeAllocator::getRef(word_t n)
|
||||
{
|
||||
if (n >= vec.size())
|
||||
return vec[0];
|
||||
return vec[n];
|
||||
}
|
||||
|
||||
Node NodeAllocator::getVal(word_t n) const
|
||||
{
|
||||
if (n >= vec.size())
|
||||
return vec[0];
|
||||
return vec[n];
|
||||
}
|
||||
|
||||
word_t gcd(word_t a, word_t b)
|
||||
{
|
||||
if (a == b)
|
||||
return a;
|
||||
else if (a <= 1 || b <= 1)
|
||||
return 1;
|
||||
for (word_t r = b % a; r != 0; b = a, a = r, r = b % a)
|
||||
continue;
|
||||
return a;
|
||||
}
|
||||
|
||||
std::tuple<Fraction, Fraction, Fraction> iterate(std::queue<word_t> &queue,
|
||||
NodeAllocator &allocator)
|
||||
{
|
||||
if (queue.empty())
|
||||
return {};
|
||||
word_t index = queue.front();
|
||||
Node node = allocator.getVal(index);
|
||||
if (!node.left.has_value())
|
||||
{
|
||||
allocator.getRef(index).left = allocator.alloc(Fraction{
|
||||
node.value.numerator, node.value.numerator + node.value.denominator});
|
||||
}
|
||||
if (!node.right.has_value())
|
||||
{
|
||||
allocator.getRef(index).right = allocator.alloc(Fraction{
|
||||
node.value.numerator + node.value.denominator, node.value.denominator});
|
||||
}
|
||||
queue.pop();
|
||||
queue.push(allocator.getVal(index).left.value());
|
||||
queue.push(allocator.getVal(index).right.value());
|
||||
node = allocator.getVal(index);
|
||||
// NOTE: We can be assured that left and right DO have values
|
||||
return std::tuple(allocator.getVal(node.left.value()).value, node.value,
|
||||
allocator.getVal(node.right.value()).value);
|
||||
}
|
||||
|
||||
std::string to_string(const Fraction &f)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << f.numerator << "/" << f.denominator;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void indent_depth(int depth, std::stringstream &ss)
|
||||
{
|
||||
for (int i = 0; i < depth; ++i)
|
||||
ss << " ";
|
||||
}
|
||||
|
||||
std::string to_string(const NodeAllocator &allocator, const index_t n,
|
||||
int depth)
|
||||
{
|
||||
if (!n.has_value())
|
||||
return "NIL";
|
||||
std::stringstream ss;
|
||||
Node x = allocator.getVal(n.value());
|
||||
ss << "(" << to_string(x.value) << "\n";
|
||||
indent_depth(depth, ss);
|
||||
if (x.left == -1)
|
||||
ss << "NIL";
|
||||
else
|
||||
ss << to_string(allocator, x.left, depth + 1);
|
||||
ss << "\n";
|
||||
indent_depth(depth, ss);
|
||||
if (x.right == -1)
|
||||
ss << "NIL";
|
||||
else
|
||||
ss << to_string(allocator, x.right, depth + 1);
|
||||
ss << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/* Copyright (C) 2024, 2025 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the GNU General
|
||||
* Public License Version 2, which you should have received a copy of along with
|
||||
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
72
src/numerics.hpp
Normal file
72
src/numerics.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/* numerics.hpp: Computation necessary for generating the tree
|
||||
* Created: 2024-07-26
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#ifndef NUMERICS_HPP
|
||||
#define NUMERICS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||
#define MAX(A, B) ((B) < (A) ? (A) : (B))
|
||||
typedef uint64_t word_t;
|
||||
typedef std::optional<word_t> index_t;
|
||||
|
||||
struct Fraction
|
||||
{
|
||||
word_t numerator, denominator;
|
||||
long double norm;
|
||||
|
||||
Fraction(word_t numerator = 0, word_t denominator = 1);
|
||||
bool operator<(const Fraction other);
|
||||
bool operator==(const Fraction &other);
|
||||
};
|
||||
|
||||
struct Node
|
||||
{
|
||||
Fraction value;
|
||||
index_t left, right;
|
||||
|
||||
Node(Fraction val = {}, index_t left = std::nullopt,
|
||||
index_t right = std::nullopt);
|
||||
};
|
||||
|
||||
struct NodeAllocator
|
||||
{
|
||||
std::vector<Node> vec;
|
||||
|
||||
NodeAllocator(word_t capacity = 0);
|
||||
word_t alloc(Node n);
|
||||
Node getVal(word_t n) const;
|
||||
Node &getRef(word_t n);
|
||||
};
|
||||
|
||||
word_t gcd(word_t a, word_t b);
|
||||
std::tuple<Fraction, Fraction, Fraction> iterate(std::queue<word_t> &queue,
|
||||
NodeAllocator &allocator);
|
||||
|
||||
std::string to_string(const Fraction &);
|
||||
std::string to_string(const NodeAllocator &, const index_t, int depth = 1);
|
||||
|
||||
#endif
|
||||
|
||||
/* Copyright (C) 2024, 2025 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the GNU General
|
||||
* Public License Version 2, which you should have received a copy of along with
|
||||
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -12,7 +12,6 @@ namespace cw::state
|
||||
{
|
||||
void DrawState::compute_bounds()
|
||||
{
|
||||
state.mutex.lock();
|
||||
bounds.leftmost = state.allocator.get_val(0);
|
||||
while (bounds.leftmost.left >= 0)
|
||||
bounds.leftmost = state.allocator.get_val(bounds.leftmost.left);
|
||||
@@ -20,8 +19,8 @@ namespace cw::state
|
||||
bounds.rightmost = state.allocator.get_val(0);
|
||||
while (bounds.rightmost.right >= 0)
|
||||
bounds.rightmost = state.allocator.get_val(bounds.rightmost.right);
|
||||
state.mutex.unlock();
|
||||
|
||||
bounds.lower_val = std::floorl(bounds.leftmost.value.norm);
|
||||
bounds.upper_val = std::ceill(bounds.rightmost.value.norm);
|
||||
}
|
||||
} // namespace cw::state
|
||||
|
||||
@@ -22,25 +22,22 @@ namespace cw::state
|
||||
std::queue<u64> queue;
|
||||
|
||||
bool pause_work, stop_work;
|
||||
std::mutex mutex;
|
||||
std::mutex mutex_queue;
|
||||
std::mutex mutex_allocator;
|
||||
|
||||
State(void) {};
|
||||
};
|
||||
|
||||
struct DrawState
|
||||
{
|
||||
State &state;
|
||||
const State &state;
|
||||
struct Bounds
|
||||
{
|
||||
cw::node::Node leftmost, rightmost;
|
||||
f64 lower_val, upper_val;
|
||||
} bounds;
|
||||
|
||||
DrawState(State &state) : state{state}
|
||||
{
|
||||
// lim n -> -∞
|
||||
bounds.lower_val = 0;
|
||||
};
|
||||
DrawState(const State &state) : state{state} {};
|
||||
|
||||
void compute_bounds(void);
|
||||
};
|
||||
|
||||
@@ -16,40 +16,46 @@ namespace cw::worker
|
||||
using cw::node::Fraction;
|
||||
using cw::node::Node;
|
||||
|
||||
std::tuple<u64, u64> generate_children(NodeAllocator &allocator, u64 index)
|
||||
{
|
||||
Node node = allocator.get_val(index);
|
||||
if (node.left < 0)
|
||||
{
|
||||
allocator.get_ref(index).left = allocator.alloc(Fraction{
|
||||
node.value.numerator, node.value.numerator + node.value.denominator});
|
||||
}
|
||||
if (node.right < 0)
|
||||
{
|
||||
allocator.get_ref(index).right = allocator.alloc(
|
||||
Fraction{node.value.numerator + node.value.denominator,
|
||||
node.value.denominator});
|
||||
}
|
||||
return {node.left, node.right};
|
||||
}
|
||||
|
||||
void do_iteration(State &state)
|
||||
{
|
||||
state.mutex.lock();
|
||||
state.mutex_queue.lock();
|
||||
if (state.queue.empty())
|
||||
{
|
||||
// Unlock since there isn't any work to be done.
|
||||
state.mutex.unlock();
|
||||
state.mutex_queue.unlock();
|
||||
return;
|
||||
}
|
||||
u64 index = state.queue.front();
|
||||
state.queue.pop();
|
||||
state.mutex_queue.unlock();
|
||||
|
||||
Node node = state.allocator.get_val(index);
|
||||
u64 left_child, right_child;
|
||||
state.mutex_allocator.lock();
|
||||
std::tie(left_child, right_child) =
|
||||
generate_children(state.allocator, index);
|
||||
state.mutex_allocator.unlock();
|
||||
|
||||
i64 left = node.left, right = node.right;
|
||||
if (left < 0)
|
||||
{
|
||||
left = state.allocator.alloc(Fraction{
|
||||
node.value.numerator, node.value.numerator + node.value.denominator});
|
||||
}
|
||||
if (right < 0)
|
||||
{
|
||||
right = state.allocator.alloc(
|
||||
Fraction{node.value.numerator + node.value.denominator,
|
||||
node.value.denominator});
|
||||
}
|
||||
|
||||
Node &node_ref = state.allocator.get_ref(index);
|
||||
node_ref.left = left;
|
||||
node_ref.right = right;
|
||||
|
||||
state.queue.push(left);
|
||||
state.queue.push(right);
|
||||
state.mutex.unlock();
|
||||
state.mutex_queue.lock();
|
||||
state.queue.push(left_child);
|
||||
state.queue.push(right_child);
|
||||
state.mutex_queue.unlock();
|
||||
}
|
||||
|
||||
void worker(State &state)
|
||||
|
||||
@@ -13,21 +13,16 @@
|
||||
|
||||
#include "state.hpp"
|
||||
|
||||
#ifndef THREAD_PAUSE_MS
|
||||
#define THREAD_PAUSE_MS 1000
|
||||
#endif
|
||||
#ifndef THREAD_GENERAL_MS
|
||||
#define THREAD_GENERAL_MS 10
|
||||
#endif
|
||||
|
||||
namespace cw::worker
|
||||
{
|
||||
using cw::node::NodeAllocator;
|
||||
using cw::state::State;
|
||||
constexpr auto THREAD_PAUSE_DELAY =
|
||||
std::chrono::milliseconds(THREAD_PAUSE_MS);
|
||||
constexpr auto THREAD_GENERAL_DELAY =
|
||||
std::chrono::milliseconds(THREAD_GENERAL_MS);
|
||||
constexpr auto THREAD_PAUSE_DELAY = std::chrono::milliseconds(1000);
|
||||
constexpr auto THREAD_GENERAL_DELAY = std::chrono::milliseconds(1);
|
||||
|
||||
// Given `index`, return the indices of its children in the tree. If not
|
||||
// present already, generate them using the allocator.
|
||||
std::tuple<u64, u64> generate_children(NodeAllocator &allocator, u64 index);
|
||||
|
||||
// Performs a single iteration which consists of the following:
|
||||
// 1) pop an index off the iteration queue
|
||||
|
||||
Reference in New Issue
Block a user