diff --git a/.gitignore b/.gitignore
index 4da1bd7..8fc41a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,15 @@
app_run_tree/
meta_run_tree/
+*.exe
+*.pdb
+*.o
process/
reference/
working_data/
+nssm_log.log
+sysroot/
+*.DS_Store
+*.vscode
+*.vscode/*
+*.dSYM
+run_tree/data/live_data
diff --git a/README.md b/README.md
index ddbba99..35225ed 100644
--- a/README.md
+++ b/README.md
@@ -7,13 +7,17 @@ Building Lumenarium requires having MSVC installed (sorry, Windows only for now!
2. Run the appropriate build batch file
- for Windows: use `build\build_app_msvc_win32_debug.bat`
- other platforms coming soon
+3. Build scripts will output executables into the app_run_tree directory, by platform
## Run Lumenarium
Windows - Debug
-1. Just run `win32_msvc\debug\win32_foldhaus.exe`
+1. Run `app_run_tree\win32_msvc\debug\win32_foldhaus.exe`
+
+If you want to run in headless mode:
+1. Run `app_run_tree\win32_msvc\debug\win32_foldhaus.exe -headless`
## Debug Lumenarium
-###Windows
+### Windows
Building in debug mode outputs pdb file info that can be read by Visual Studio or RemedyBG (preferred debugging solution, but ymmv). You can just open the exe in either application and it'll find the pdb file in the same directory
## What Is Lumenarium?
diff --git a/admin.txt b/admin.txt
new file mode 100644
index 0000000..98ecab0
--- /dev/null
+++ b/admin.txt
@@ -0,0 +1,28 @@
+# Project Admin Stuff
+
+## Priorities
+
+1. Upgrade Lumenarium's plumbing
+2. Begin work on Incenter
+
+## TODO
+
+1. Upgrade Lumenarium's plumbing
+ x. switch over to compiling with clang & bash based build scripts
+ x. better platform layer separation
+ 3. osx and webgl layers, possibly linux?
+ x. remove dll compiling, just build all in one go
+ 5. improve ui
+ [ ] get widgets and widget ids working
+ - see a trick of fate
+ [ ] clip widgets to regions
+ [ ] text rendering
+ [ ] interaction
+ [ ] layout manager
+ - do layout the way youre doing styling - optional pointer to a struct
+ and fallback on some global default
+
+2. Incenter
+ 1. Sculpture generation from list of lat-long coordinates
+ 2.
+
diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim
new file mode 100644
index 0000000..42794d8
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/ambient_patterns/ambient_1.foldanim
@@ -0,0 +1,56 @@
+lumenarium_animation_file;
+animation_name: "digital_fire";
+layers_count: 3;
+blocks_count: 4;
+playable_range:{
+ min: 0;
+ max: 10000;
+};
+layers:{
+ layer:{
+ name: "mask1";
+ blend: "Add";
+ };
+ layer:{
+ name: "mask2";
+ blend: "Add";
+ };
+ layer:{
+ name: "color";
+ blend: "Multiply";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 19;
+ };
+ layer_index: 0;
+ animation_name: "Pattern_BulbMask";
+ };
+ block:{
+ frame_range:{
+ min: 0;
+ max: 10000;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_Leafy";
+ };
+ block:{
+ frame_range:{
+ min: 0;
+ max: 5156;
+ };
+ layer_index: 2;
+ animation_name: "Pattern_Rainbow";
+ };
+ block:{
+ frame_range:{
+ min: 5040;
+ max: 10000;
+ };
+ layer_index: 2;
+ animation_name: "Pattern_Wavy";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim
new file mode 100644
index 0000000..562f43d
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/ambient_patterns/fishy_0.foldanim
@@ -0,0 +1,48 @@
+lumenarium_animation_file;
+animation_name: "fishy_0";
+layers_count: 3;
+blocks_count: 3;
+playable_range:{
+ min: 0;
+ max: 3600;
+};
+layers:{
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+ layer:{
+ name: "Color";
+ blend: "Multiply";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 3600;
+ };
+ layer_index: 2;
+ animation_name: "Pattern_HueShift";
+ };
+ block:{
+ frame_range:{
+ min: 0;
+ max: 3600;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_StemSolid";
+ };
+ block:{
+ frame_range:{
+ min: 0;
+ max: 3600;
+ };
+ layer_index: 0;
+ animation_name: "Pattern_VerticalLines";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/patchy_loading_bar.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/patchy_loading_bar.foldanim
new file mode 100644
index 0000000..1fe290a
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/ambient_patterns/patchy_loading_bar.foldanim
@@ -0,0 +1,36 @@
+lumenarium_animation_file;
+animation_name: "patchy_loading_bar_0";
+layers_count: 2;
+blocks_count: 2;
+playable_range:{
+ min: 0;
+ max: 3350;
+};
+layers:{
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+ layer:{
+ name: "[New Layer]";
+ blend: "Multiply";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 3600;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_Patchy";
+ };
+ block:{
+ frame_range:{
+ min: 0;
+ max: 3600;
+ };
+ layer_index: 0;
+ animation_name: "Pattern_GrowFadeMask";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/primary_hue_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/primary_hue_0.foldanim
new file mode 100644
index 0000000..66670b8
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/ambient_patterns/primary_hue_0.foldanim
@@ -0,0 +1,28 @@
+lumenarium_animation_file;
+animation_name: "primary_hue_0";
+layers_count: 2;
+blocks_count: 1;
+playable_range:{
+ min: 0;
+ max: 360;
+};
+layers:{
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 36000;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_PrimaryHue";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim
new file mode 100644
index 0000000..edb16ec
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_0.foldanim
@@ -0,0 +1,32 @@
+lumenarium_animation_file;
+animation_name: "rainbow";
+layers_count: 3;
+blocks_count: 1;
+playable_range:{
+ min: 0;
+ max: 36000;
+};
+layers:{
+ layer:{
+ name: "Mask";
+ blend: "Add";
+ };
+ layer:{
+ name: "Color";
+ blend: "Add";
+ };
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 36000;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_Rainbow";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_loading_bar.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_loading_bar.foldanim
new file mode 100644
index 0000000..6f6f0c6
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/ambient_patterns/rainbow_loading_bar.foldanim
@@ -0,0 +1,28 @@
+lumenarium_animation_file;
+animation_name: "rainbow_loading_bar_0";
+layers_count: 2;
+blocks_count: 1;
+playable_range:{
+ min: 0;
+ max: 9000;
+};
+layers:{
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 9000;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_RainbowLoadingBar";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim b/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim
new file mode 100644
index 0000000..bc4979c
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/ambient_patterns/wavy_0.foldanim
@@ -0,0 +1,36 @@
+lumenarium_animation_file;
+animation_name: "wavy_0";
+layers_count: 2;
+blocks_count: 2;
+playable_range:{
+ min: 0;
+ max: 7200;
+};
+layers:{
+ layer:{
+ name: "[New Layer]";
+ blend: "Overwrite";
+ };
+ layer:{
+ name: "Color";
+ blend: "Multiply";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 7200;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_Wavy";
+ };
+ block:{
+ frame_range:{
+ min: 78;
+ max: 3571;
+ };
+ layer_index: 0;
+ animation_name: "Pattern_Leafy";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim
new file mode 100644
index 0000000..9a5339b
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/audio_responses/voice_command.foldanim
@@ -0,0 +1,36 @@
+lumenarium_animation_file;
+animation_name: "voice_anim";
+layers_count: 2;
+blocks_count: 2;
+playable_range:{
+ min: 0;
+ max: 3600;
+};
+layers:{
+ layer:{
+ name: "Base";
+ blend: "Add";
+ };
+ layer:{
+ name: "Add In";
+ blend: "Overwrite";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 3600;
+ };
+ layer_index: 0;
+ animation_name: "Pattern_VoicePattern";
+ };
+ block:{
+ frame_range:{
+ min: 0;
+ max: 3600;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_VoiceAddIns";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/awaken.foldanim b/app_run_tree/data/blumen_animations/awaken.foldanim
new file mode 100644
index 0000000..b82e48f
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/awaken.foldanim
@@ -0,0 +1,88 @@
+lumenarium_animation_file;
+animation_name: "awaken";
+layers_count: 3;
+blocks_count: 8;
+playable_range:{
+ min: 0;
+ max: 7200;
+};
+layers:{
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+ layer:{
+ name: "[New Layer]";
+ blend: "Add";
+ };
+ layer:{
+ name: "[New Layer]";
+ blend: "Multiply";
+ };
+};
+blocks:{
+ block:{
+ frame_range:{
+ min: 0;
+ max: 1363;
+ };
+ layer_index: 0;
+ animation_name: "Pattern_Leafy";
+ };
+ block:{
+ frame_range:{
+ min: 1169;
+ max: 7200;
+ };
+ layer_index: 0;
+ animation_name: "Pattern_BulbMask";
+ };
+ block:{
+ frame_range:{
+ min: 5525;
+ max: 7200;
+ };
+ layer_index: 2;
+ animation_name: "Pattern_Wavy";
+ };
+ block:{
+ frame_range:{
+ min: 2135;
+ max: 2555;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_None";
+ };
+ block:{
+ frame_range:{
+ min: 2470;
+ max: 7200;
+ };
+ layer_index: 1;
+ animation_name: "Pattern_StemSolid";
+ };
+ block:{
+ frame_range:{
+ min: 0;
+ max: 1917;
+ };
+ layer_index: 2;
+ animation_name: "Pattern_Blue";
+ };
+ block:{
+ frame_range:{
+ min: 1823;
+ max: 3803;
+ };
+ layer_index: 2;
+ animation_name: "Pattern_RainbowLoadingBar";
+ };
+ block:{
+ frame_range:{
+ min: 3670;
+ max: 5720;
+ };
+ layer_index: 2;
+ animation_name: "Pattern_Rainbow";
+ };
+};
diff --git a/app_run_tree/data/blumen_animations/off_anim.foldanim b/app_run_tree/data/blumen_animations/off_anim.foldanim
new file mode 100644
index 0000000..d7a3d45
--- /dev/null
+++ b/app_run_tree/data/blumen_animations/off_anim.foldanim
@@ -0,0 +1,12 @@
+lumenarium_animation_file;
+animation_name: "off_anim";
+layers_count: 0;
+blocks_count: 0;
+playable_range:{
+ min: 0;
+ max: 2;
+};
+layers:{
+};
+blocks:{
+};
diff --git a/bin/compile.bat b/bin/compile.bat
new file mode 100644
index 0000000..b623ea0
--- /dev/null
+++ b/bin/compile.bat
@@ -0,0 +1,2 @@
+@echo off
+build\build_app_msvc_win32_debug.bat
\ No newline at end of file
diff --git a/debug.bat b/bin/debug.bat
similarity index 100%
rename from debug.bat
rename to bin/debug.bat
diff --git a/bin/gen_incenter_cities.js b/bin/gen_incenter_cities.js
new file mode 100644
index 0000000..bf78e0c
--- /dev/null
+++ b/bin/gen_incenter_cities.js
@@ -0,0 +1,89 @@
+const fs = require("fs");
+
+const IN_FILE_PATH_PRIMARY = "../run_tree/data/cities_final.json";
+const IN_FILE_PATH_SECONDARY = "../run_tree/data/cities_secondary_final.json";
+const OUT_FILE_PATH = "../src_v2/user_space/incenter_gen_cities.h"
+
+function print_city_desc (city, prefix, dest, gets_own_universe)
+{
+ const city_ascii = city.city_ascii
+ .toLowerCase()
+ .replaceAll(' ', '_')
+ .replaceAll('-', '_')
+ .replaceAll('\'', '')
+ .replaceAll('`', '');
+
+ const city_id = `${prefix}_${city_ascii}`;
+ const { lat, lng } = city;
+
+ dest.enum_out += ` ${city_id} = ${dest.enum_counter++},\n`;
+
+ const universe = gets_own_universe ? city_id : "incenter_secondary_city_universe";
+
+ dest.desc_out += ` [${city_id}] = {
+ .id = ${city_id},
+ .lat = ${lat},
+ .lon = ${lng},
+ .sacn_universe = ${universe},
+ },\n`;
+
+ dest.strings_out += ` [${city_id}] = "${city_id}",\n`;
+}
+
+function main ()
+{
+ const primary_file = fs.readFileSync(IN_FILE_PATH_PRIMARY, {});
+ const primary_json = JSON.parse(primary_file);
+
+ const secondary_file = fs.readFileSync(IN_FILE_PATH_SECONDARY, {});
+ const secondary_json = JSON.parse(secondary_file);
+
+ let out = "// NOTE: This file is autogenerated by csv_to_cstruct.js\n";
+
+ let enum_counter = 0;
+ let enum_out = "// NOTE: These are values for Incenter_City_Id\nenum {\n";
+ enum_out += ` city_black_rock = ${enum_counter++},\n`;
+
+ let desc_out = "global Incenter_City_Desc city_descs[] = {\n";
+
+ let strings_out = "global char* city_strings[] = {\n";
+
+ let dest = {
+ enum_counter,
+ enum_out,
+ desc_out,
+ strings_out,
+ };
+
+ primary_json.forEach((city) => {
+ print_city_desc(city, "city", dest, true);
+ });
+
+ // Add Black Rock City
+ dest.desc_out += `\n // Black Rock City\n [city_black_rock] = {
+ .id = city_black_rock,
+ .lat = -90.0f,
+ .lon = 0,
+ .sacn_universe = city_black_rock,
+ },\n`;
+
+ dest.strings_out += ` [city_black_rock] = "city_black_rock",\n`;
+ dest.enum_out += " city_count,\n";
+ dest.enum_out += " city_secondary_first = city_count + 1,\n";
+
+ secondary_json.forEach((city) => {
+ print_city_desc(city, "city_secondary", dest, false);
+ });
+
+ dest.enum_out += " city_secondary_count,\n";
+ dest.enum_out += "};\n\n";
+ dest.desc_out += "};\n\n";
+ dest.strings_out += "};\n\n";
+
+ out += dest.enum_out;
+ out += dest.desc_out;
+ out += dest.strings_out;
+ fs.writeFileSync(OUT_FILE_PATH, out, {});
+}
+
+main();
diff --git a/run.bat b/bin/run.bat
similarity index 100%
rename from run.bat
rename to bin/run.bat
diff --git a/build/build.sh b/build/build.sh
new file mode 100755
index 0000000..832c948
--- /dev/null
+++ b/build/build.sh
@@ -0,0 +1,9 @@
+set -e
+
+SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}")
+$SCRIPT_REL_DIR/build_.sh debug osx arm64
+# $SCRIPT_REL_DIR/build_.sh debug wasm intel
+
+# pushd "run_tree/raspi/arm64/debug"
+# clang -o lumenarium /home/pi/dev/Lumenarium/src_v2/platform/raspi/lumenarium_first_raspi.c -lm
+# popd
diff --git a/build/build_.sh b/build/build_.sh
new file mode 100755
index 0000000..68daf04
--- /dev/null
+++ b/build/build_.sh
@@ -0,0 +1,378 @@
+#!/bin/bash
+
+# Ensure an error makes the script bail
+set -e
+
+# --------------------------------------------
+# Usage
+
+print_usage () {
+ echo
+ echo Build Command Syntax:
+ echo " $0 [mode] [platform] [arch]"
+ echo
+ echo "Release Mode Options:"
+ echo " debug"
+ echo " prod"
+ echo
+ echo "Platform Options:"
+ echo " win32"
+ echo " osx"
+ echo " wasm"
+ echo " raspi"
+ echo
+ echo "Arch Options: (architecture)"
+ echo " intel (valid with Platform Win32 and OSX) (default)"
+ echo " arm64 (only valid for Platform OSX)"
+}
+
+# --------------------------------------------
+# Arguments
+MODE=$1
+PLATFORM=$2
+ARCH=$3
+PACKAGE=$4
+
+if [ "${MODE}" == "" ] | [ "${PLATFORM}" == "" ]
+then
+ print_usage
+ exit 0
+fi
+
+# Default to Intel architecture if none provided
+if [ "${ARCH}" == "" ]
+then
+ ARCH="intel"
+fi
+
+if [ "${ARCH}" != "intel" ] && [ "${ARCH}" != "arm64" ]
+then
+ echo "Uknown target architecture: ${ARCH}"
+ print_usage
+ exit 0
+
+fi
+
+# --------------------------------------------
+# Utilities
+
+pushdir () {
+ command pushd "$@" > /dev/null
+}
+
+popdir () {
+ command popd "$@" > /dev/null
+}
+
+add_flag () {
+ local -n ref=$1
+ ref="$ref $2"
+}
+
+# --------------------------------------------
+# Getting Project Path
+#
+# Project is stored in PROJECT_PATH
+
+SCRIPT_REL_DIR=$(dirname "${BASH_SOURCE[0]}")
+pushdir $SCRIPT_REL_DIR
+pushdir ..
+PROJECT_PATH=$(pwd)
+popdir
+popdir
+
+# --------------------------------------------
+# Platform/Mode Specific Variables
+
+# Compiler Selection
+
+Compiler_win32="cl"
+Compiler_osx="clang"
+Compiler_raspi="clang"
+WasiSdk="/c/drive/apps/wasi-sdk"
+Compiler_wasm="$WasiSdk/bin/clang++"
+Compiler_linux="clang++"
+
+# Platform Entry Points
+
+PlatformEntry_win32="src_v2/platform/win32/lumenarium_first_win32.cpp"
+PlatformEntry_osx="src_v2/platform/osx/lumenarium_first_osx.c"
+PlatformEntry_wasm="src_v2/platform/wasm/lumenarium_first_wasm.cpp"
+PlatformEntry_linux="src_v2/platform/linux/lumenarium_first_linux.cpp"
+PlatformEntry_raspi="src_v2/platform/raspi/lumenarium_first_raspi.c"
+
+# Intermediate Outputs
+
+CompilerOutput_win32="lumenarium.o"
+CompilerOutput_osx="lumenarium"
+CompilerOutput_wasm="lumenarium.wasm"
+CompilerOutput_linux=""
+CompilerOutput_raspi="lumenarium"
+
+# Executables
+
+LinkerOutput_win32="lumenarium.exe"
+LinkerOutput_osx="lumenarium"
+LinkerOutput_wasm="lumenarium.wasm"
+LinkerOutput_linux=""
+LinkerOutput_raspi="lumenarium"
+
+# Wasm Sys Root
+WasmSysRoot="${PROJECT_PATH}/src_v2/platform/wasm/sysroot/"
+
+# Compiler Flags
+
+CompilerFlags_win32="-nologo"
+CompilerFlags_win32+=" -FC" # display errors with full path
+CompilerFlags_win32+=" -WX" # treat warnings as errors
+CompilerFlags_win32+=" -W4" # output warning level
+CompilerFlags_win32+=" -Z7" # generate C compatible debug info
+# CompilerFlags_win32+="-Oi" # generate intrinsic functions
+# CompilerFlags_win32+="-MTd" # create a debug multithreaded exe w/ Libcmtd.lib
+# CompilerFlags_win32+="-fp:fast" # fast floating point model
+CompilerFlags_win32+=" -wd4505" #
+CompilerFlags_win32+=" -wd4100" #
+CompilerFlags_win32+=" -wd4189" #
+CompilerFlags_win32+=" -wd4702" #
+CompilerFlags_win32+=" -wd4996" # _CRT_SECURE_NO_WARNINGS
+
+CompilerFlags_osx=""
+
+CompilerFlags_wasm=""
+CompilerFlags_wasm+=" -Wno-writable-strings" #
+CompilerFlags_wasm+=" --target=wasm32" #
+CompilerFlags_wasm+=" -nostdlib" #
+CompilerFlags_wasm+=" -Wl,--no-entry" #
+CompilerFlags_wasm+=" -Wl,--allow-undefined" #
+CompilerFlags_wasm+=" -Wl,--export-all" #
+
+CompilerFlags_linux=" -pthread"
+
+CompilerFlags_raspi=" -pthread" # "--target=arm-rpi-linux-gnueabihf" # "--target=arm-linux-gnueabihf" #target
+CompilerFlags_raspi+=" -lm" # link with local system math libraries
+
+
+CompilerFlags_DEBUG_win32=""
+CompilerFlags_DEBUG_win32+=" -Od" #
+CompilerFlags_DEBUG_win32+=" -Zi" #
+CompilerFlags_DEBUG_win32+=" -DDEBUG" #
+# add_flag CompilerFlags_DEBUG_win32 "-DPRINT_ASSERTS"
+
+CompilerFlags_DEBUG="-O0"
+CompilerFlags_DEBUG+=" -g" #
+CompilerFlags_DEBUG+=" -DDEBUG" #
+if [ "${PLATFORM}" != "raspi" ]
+then
+ CompilerFlags_DEBUG+=" -fsanitize=address" #address sanitizer
+fi
+
+CompilerFlags_PROD=" -O3"
+
+# Compiler flags that no matter what, we want to define
+# for the most part these pass the build parameters into the executable
+CompilerFlags_common=" -DPLATFORM_${PLATFORM}=1 -DMODE_${MODE}=1 -DARCH_${ARCH}=1"
+
+# Linker Flags
+
+LinkerFlags_win32=" -NOLOGO"
+LinkerFlags_win32+=" -incremental:no" #
+LinkerFlags_win32+=" -subsystem:windows" #
+# add_flag LinkerFlags_win32 "-entry:WinMain" #
+LinkerFlags_win32+=" -opt:ref" # eliminate functions that are never referenced
+
+LinkerFlags_osx=""
+
+LinkerFlags_wasm="--no-entry"
+LinkerFlags_wasm+=" --export-dynamic" #
+LinkerFlags_wasm+=" --unresolved-symbols=import-functions" #
+
+LinkerFlags_linux=""
+LinkerFlags_raspi="-fuse-ld=lld"
+
+LinkerFlags_DEBUG="-debug"
+LinkerFlags_PROD=""
+
+# Linker Libs
+
+LinkerLibs_win32="user32.lib kernel32.lib gdi32.lib opengl32.lib"
+# winmm.lib gdi32.lib dsound.lib Ws2_32.lib Comdlg32.lib Winspool.lib"
+
+LinkerLibs_osx="-framework OpenGL -framework Cocoa -framework IOKit ${PROJECT_PATH}/src_v2/libs/glfw_osx/lib-universal/libglfw3.a"
+LinkerLibs_wasm=""
+LinkerLibs_linux=""
+LinkerLibs_raspi=""
+
+# --------------------------------------------
+# Varible Selection
+
+# Select Platform Variables
+
+if [ "${PLATFORM}" == "win32" ]
+then
+ Compiler=$Compiler_win32
+ PlatformEntry=$PlatformEntry_win32
+ CompilerFlags=$CompilerFlags_win32
+ CompilerOutput=$CompilerOutput_win32
+ LinkerOutput=$LinkerOutput_win32
+ LinkerFlags=$LinkerFlags_win32
+ LinkerLibs=$LinkerLibs_win32
+
+elif [ "${PLATFORM}" == "osx" ]
+then
+ Compiler=$Compiler_osx
+ PlatformEntry=$PlatformEntry_osx
+ CompilerFlags=$CompilerFlags_osx
+ CompilerOutput=$CompilerOutput_osx
+ LinkerOutput=$LinkerOutput_osx
+ LinkerFlags=$LinkerFlags_osx
+ LinkerLibs=$LinkerLibs_osx
+
+ if [ "${ARCH}" == "arm64" ]
+ then
+ CompilerFlags="${CompilerFlags} -arch arm64"
+ elif [ "${ARCH}" == "intel" ]
+ then
+ CompilerFlags="${CompilerFlags} -arch x86_64"
+ else
+ echo "ERROR: Unrecognized Arch: ${ARCH}"
+ exit 0
+ fi
+
+elif [ "${PLATFORM}" == "wasm" ]
+then
+ Compiler=$Compiler_wasm
+ PlatformEntry=$PlatformEntry_wasm
+ CompilerFlags=$CompilerFlags_wasm
+ CompilerOutput=$CompilerOutput_wasm
+ LinkerOutput=$LinkerOutput_wasm
+ LinkerFlags=$LinkerFlags_wasm
+ LinkerLibs=$LinkerLibs_wasm
+
+elif [ "${PLATFORM}" == "linux" ]
+then
+ Compiler=$Compiler_linux
+ PlatformEntry=$PlatformEntry_linux
+ CompilerFlags=$CompilerFlags_linux
+ CompilerOutput=$CompilerOutput_linux
+ LinkerOutput=$LinkerOutput_linux
+ LinkerFlags=$LinkerFlags_linux
+ LinkerLibs=$LinkerLibs_linux
+
+elif [ "${PLATFORM}" == "raspi" ]
+then
+ Compiler=$Compiler_raspi
+ PlatformEntry=$PlatformEntry_raspi
+ CompilerFlags=$CompilerFlags_raspi
+ CompilerOutput=$CompilerOutput_raspi
+ LinkerOutput=$LinkerOutput_raspi
+ LinkerFlags=$LinkerFlags_raspi
+ LinkerLibs=$LinkerLibs_raspi
+else
+ echo "Attempting to build for an unknown platform: ${PLATFORM}"
+ print_usage
+ exit 0
+
+fi
+
+# Select Release Mode Variables
+
+if [ "${MODE}" == "debug" ]
+then
+ if [ $PLATFORM == "win32" ]
+ then
+ CompilerFlags="${CompilerFlags} ${CompilerFlags_DEBUG_win32}"
+ else
+ CompilerFlags="${CompilerFlags} ${CompilerFlags_DEBUG}"
+ fi
+
+ LinkerFlags="${LinkerFlags} ${LinkerFlags_DEBUG}"
+
+elif [ "${MODE}" == "prod" ]
+then
+ CompilerFlags="${CompilerFlags} ${CompilerFlags_PROD}"
+ LinkerFlags="${LinkerFlags} ${LinkerFlags_PROD}"
+
+else
+ echo "Attempting to build for an unknown release mode: ${MODE}"
+ print_usage
+ exit 0
+
+fi
+
+# Common Flags
+CompilerFlags="${CompilerFlags} ${CompilerFlags_common}"
+
+# --------------------------------------------
+# Build Path Construction
+#
+# This determines where the generated executable will
+# be located. In general, it can be found at
+# project_path/run_tree/platform/arch/release_mode/lumenarium.exe
+#
+# This section also ensures that the path requested actually exists
+
+BuildDir="${PROJECT_PATH}/run_tree/${PLATFORM}/${ARCH}/${MODE}"
+EntryPath="${PROJECT_PATH}/${PlatformEntry}"
+
+# Exception for wasm, which doesn't care about cpu architecture
+if [ $PLATFORM == "wasm" ]
+then
+ BuildDir="${PROJECT_PATH}/run_tree/${PLATFORM}/${MODE}"
+
+fi
+
+# Make the build directory,
+# "-p" flag makes it make the entire tree, and not emit errors if it
+# exists.
+mkdir -p "${BuildDir}"
+
+# --------------------------------------------
+# Compilation
+
+echo "Building To: ${BuildDir}/${LinkerOutput}"
+echo
+pushdir $BuildDir
+
+echo "Cleaning: ${CompilerOutput} and ${LinkerOutput}"
+rm -rf ${CompilerOutput} ${LinkerOutput}
+
+echo "COMPILING..."
+if [ $PLATFORM == "win32" ]
+then
+ $Compiler \
+ $CompilerFlags \
+ $EntryPath \
+ -link \
+ $LinkerFlags \
+ $LinkerLibs \
+ -OUT:${LinkerOutput}
+
+elif [ $PLATFORM == "wasm" ]
+then
+ $Compiler \
+ $CompilerFlags \
+ -o $LinkerOutput \
+ $EntryPath
+ cp \
+ "${PROJECT_PATH}/src_v2/platform/wasm/lumenarium_wasm_imports.js" \
+ ./lumenarium_wasm_imports.js
+
+else
+
+ # Preprocessing Steps
+ ConvertCsvEntry="${PROJECT_PATH}/src_v2/tools/convert_csv.c"
+ $Compiler \
+ -o convert_csv \
+ $CompilerFlags \
+ $ConvertCsvEntry \
+ $LinkerLibs
+
+ ./convert_csv
+
+ echo "$Compiler -o $LinkerOutput $CompilerFlags $EntryPath $LinkerLibs"
+ $Compiler -o $LinkerOutput $CompilerFlags $EntryPath $LinkerLibs
+
+fi
+
+echo "Finished..."
+popdir
diff --git a/build/build_app_msvc_win32_debug.bat b/build/build_app_msvc_win32_debug.bat
index d69d5db..9f523a3 100644
--- a/build/build_app_msvc_win32_debug.bat
+++ b/build/build_app_msvc_win32_debug.bat
@@ -8,7 +8,7 @@ call %MyPath%\setup_cl.bat
SET CommonCompilerFlags=-nologo -DDEBUG=1 -DPLATFORM_WINDOWS -FC -WX -W4 -Z7 -Oi -GR- -EHsc -EHa- -MTd -fp:fast -fp:except- -IC:\programs-dev\gs_libs\src
-SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -O2 %CommonCompilerFlags%
+SET CommonCompilerFlags=-wd4127 -wd4702 -wd4101 -wd4505 -wd4100 -wd4189 -wd4244 -wd4201 -wd4996 -I%CommonLibs% -Od %CommonCompilerFlags%
SET CommonLinkerFlags= -opt:ref -incremental:no
@@ -20,20 +20,27 @@ del *.pdb > NUL 2> NUL
echo WAITING FOR PDB TO WRITE > lock.tmp
-cl %CommonCompilerFlags% %SourceCodePath%\foldhaus_app.cpp /Fefoldhaus.dll /LD /link %CommonLinkerFlags% %DLLExports%
+cl %CommonCompilerFlags% %SourceCodePath%\foldhaus_app.cpp /Fefoldhaus.dll /LD /link %CommoLinkerFlags% %DLLExports%
SET LastError=%ERRORLEVEL%
del lock.tmp
-cl %CommonCompilerFlags% %SourceCodePath%\platform_win32\win32_foldhaus.cpp /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib opengl32.lib dsound.lib Ws2_32.lib Comdlg32.lib
+cl %CommonCompilerFlags% %SourceCodePath%\platform_win32\win32_foldhaus.cpp /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib opengl32.lib dsound.lib Ws2_32.lib Comdlg32.lib Winspool.lib
REM COMPILE UTILITY EXES
-cl %CommonCompilerFlags% %ProjectDevPath%\src\serial_monitor\first.cpp /Feserial_monitor.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib
+cl %CommonCompilerFlags% %ProjectDevPath%\src\serial_monitor\first.cpp /Feserial_monitor.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib Winspool.lib
cl %CommonCompilerFlags% %ProjectDevPath%\src\sculpture_gen\gen_blumen_lumen.cpp /Fegen_blumen_lumen.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib
+REM COMPILE AND RUN TESTS
+cl %CommonCompilerFlags% %ProjectDevPath%\src\tests\sanity_tests.cpp /Fesanity_tests.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib
+
+ECHO SANITY TESTS BEGIN
+sanity_tests.exe
+ECHO SANITY TESTS END
+
popd
call %MyPath%\_postbuild_win32.bat
\ No newline at end of file
diff --git a/msdev.bat b/msdev.bat
deleted file mode 100644
index 31ca5aa..0000000
--- a/msdev.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-@echo off
-
-remedybg build\win32_foldhaus.rdbg
\ No newline at end of file
diff --git a/project.4coder b/project.4coder
index 51d1801..c48210c 100644
--- a/project.4coder
+++ b/project.4coder
@@ -1,39 +1,49 @@
version(1);
project_name = "main.exe";
patterns = {
-"*.c",
-"*.cpp",
-"*.h",
-"*.m",
-"*.bat",
-"*.sh",
-"*.4coder",
+ "*.js",
+ "*.c",
+ "*.cpp",
+ "*.h",
+ "*.m",
+ "*.bat",
+ "*.sh",
+ "*.4coder",
};
blacklist_patterns = {
-".*",
+ ".*",
};
load_paths_base = {
- { "src", .relative = true, .recursive = true, },
- { "meta", .relative = true, .recursive = true, },
- { "gs_libs", .relative = true, .recursive = true, },
+ { ".", .relative = true, .recursive = false, },
+ { "build", .relative = true, .recursive = false, },
+// { "src", .relative = true, .recursive = true, },
+ { "src_v2", .relative = true, .recursive = true, },
+ { "meta", .relative = true, .recursive = true, },
+ { "gs_libs", .relative = true, .recursive = true, },
};
load_paths = {
- { load_paths_base, .os = "win", },
- { load_paths_base, .os = "linux", },
- { load_paths_base, .os = "mac", },
+ { load_paths_base, .os = "win", },
+ { load_paths_base, .os = "linux", },
+ { load_paths_base, .os = "mac", },
};
+enable_virtual_whitespace = true;
command_list = {
- { .name = "build_application",
- .out = "*app compilation*", .footer_panel = false, .save_dirty_files = true,
- .cmd = { { "build\build_app_msvc_win32_debug.bat" , .os = "win" },
+ { .name = "build_application",
+ .out = "*compilation*", .footer_panel = false, .save_dirty_files = true,
+ .cmd = { { "build\build_app_msvc_win32_debug.bat" , .os = "win" },
{ "./build.sh", .os = "linux" },
{ "./build.sh", .os = "mac" }, }, },
- { .name = "build_meta",
- .out = "*meta compilation*", .footer_panel = false, .save_dirty_files = true,
- .cmd = { { "build\build_meta_msvc_win32_debug.bat" , .os = "win" },
+ { .name = "build_meta",
+ .out = "*compilation*", .footer_panel = true, .save_dirty_files = true,
+ .cmd = { { "build\build_meta_msvc_win32_debug.bat" , .os = "win" },
{ "./build_meta.sh", .os = "linux" },
{ "./build_meta.sh", .os = "mac" }, }, },
+ { .name = "build_v2",
+ .out = "*compilation*", .footer_panel = true, .save_dirty_files = true,
+ .cmd = { { "bash build\build.sh" , .os = "win" },
+ { "build/build.sh", .os = "linux" },
+ { "build/build.sh", .os = "mac" }, }, },
};
-fkey_command[1] = "build_application";
+fkey_command[1] = "build_v2";
fkey_command[2] = "build_meta";
diff --git a/run_tree/data/cities_final.json b/run_tree/data/cities_final.json
new file mode 100644
index 0000000..f489ff9
--- /dev/null
+++ b/run_tree/data/cities_final.json
@@ -0,0 +1,613 @@
+[
+ {
+ "city": "Bucharest",
+ "city_ascii": "Bucharest",
+ "lat": 44.4,
+ "lng": 26.0833,
+ "country": "Romania",
+ "iso2": "RO",
+ "iso3": "ROU",
+ "admin_name": "Bucureşti",
+ "capital": "primary",
+ "population": 1883425,
+ "id": 1642414442
+ },
+ {
+ "city": "Brisbane",
+ "city_ascii": "Brisbane",
+ "lat": -27.4678,
+ "lng": 153.0281,
+ "country": "Australia",
+ "iso2": "AU",
+ "iso3": "AUS",
+ "admin_name": "Queensland",
+ "capital": "admin",
+ "population": 2360241,
+ "id": 1036192929
+ },
+ {
+ "city": "Chengdu",
+ "city_ascii": "Chengdu",
+ "lat": 30.66,
+ "lng": 104.0633,
+ "country": "China",
+ "iso2": "CN",
+ "iso3": "CHN",
+ "admin_name": "Sichuan",
+ "capital": "admin",
+ "population": 11920000,
+ "id": 1156421555
+ },
+ {
+ "city": "New Delhi",
+ "city_ascii": "New Delhi",
+ "lat": 28.6139,
+ "lng": 77.209,
+ "country": "India",
+ "iso2": "IN",
+ "iso3": "IND",
+ "admin_name": "Delhi",
+ "capital": "primary",
+ "population": 249998,
+ "id": 1356215164
+ },
+ {
+ "city": "Paris",
+ "city_ascii": "Paris",
+ "lat": 48.8566,
+ "lng": 2.3522,
+ "country": "France",
+ "iso2": "FR",
+ "iso3": "FRA",
+ "admin_name": "Île-de-France",
+ "capital": "primary",
+ "population": 11027000,
+ "id": 1250015082
+ },
+ {
+ "city": "San Francisco",
+ "city_ascii": "San Francisco",
+ "lat": 37.7562,
+ "lng": -122.443,
+ "country": "United States",
+ "iso2": "US",
+ "iso3": "USA",
+ "admin_name": "California",
+ "capital": "",
+ "population": 3592294,
+ "id": 1840021543
+ },
+ {
+ "city": "Denver",
+ "city_ascii": "Denver",
+ "lat": 39.7621,
+ "lng": -104.8759,
+ "country": "United States",
+ "iso2": "US",
+ "iso3": "USA",
+ "admin_name": "Colorado",
+ "capital": "admin",
+ "population": 2876625,
+ "id": 1840018789
+ },
+ {
+ "city": "Ankara",
+ "city_ascii": "Ankara",
+ "lat": 39.93,
+ "lng": 32.85,
+ "country": "Turkey",
+ "iso2": "TR",
+ "iso3": "TUR",
+ "admin_name": "Ankara",
+ "capital": "primary",
+ "population": 77168,
+ "id": 1792572891
+ },
+ {
+ "city": "Harare",
+ "city_ascii": "Harare",
+ "lat": -17.8292,
+ "lng": 31.0522,
+ "country": "Zimbabwe",
+ "iso2": "ZW",
+ "iso3": "ZWE",
+ "admin_name": "Harare",
+ "capital": "primary",
+ "population": 2150000,
+ "id": 1716196799
+ },
+ {
+ "city": "Hanoi",
+ "city_ascii": "Hanoi",
+ "lat": 21.0245,
+ "lng": 105.8412,
+ "country": "Vietnam",
+ "iso2": "VN",
+ "iso3": "VNM",
+ "admin_name": "Hà Nội",
+ "capital": "primary",
+ "population": 8246600,
+ "id": 1704413791
+ },
+ {
+ "city": "Washington",
+ "city_ascii": "Washington",
+ "lat": 38.9047,
+ "lng": -77.0163,
+ "country": "United States",
+ "iso2": "US",
+ "iso3": "USA",
+ "admin_name": "District of Columbia",
+ "capital": "primary",
+ "population": 5379184,
+ "id": 1840006060
+ },
+ {
+ "city": "Bangkok",
+ "city_ascii": "Bangkok",
+ "lat": 13.75,
+ "lng": 100.5167,
+ "country": "Thailand",
+ "iso2": "TH",
+ "iso3": "THA",
+ "admin_name": "Krung Thep Maha Nakhon",
+ "capital": "primary",
+ "population": 17573000,
+ "id": 1764068610
+ },
+ {
+ "city": "Tunis",
+ "city_ascii": "Tunis",
+ "lat": 36.8008,
+ "lng": 10.18,
+ "country": "Tunisia",
+ "iso2": "TN",
+ "iso3": "TUN",
+ "admin_name": "Tunis",
+ "capital": "primary",
+ "population": 1056247,
+ "id": 1788742103
+ },
+ {
+ "city": "Seoul",
+ "city_ascii": "Seoul",
+ "lat": 37.56,
+ "lng": 126.99,
+ "country": "South Korea",
+ "iso2": "KR",
+ "iso3": "KOR",
+ "admin_name": "Seoul",
+ "capital": "primary",
+ "population": 22394000,
+ "id": 1410836482
+ },
+ {
+ "city": "Belgrade",
+ "city_ascii": "Belgrade",
+ "lat": 44.8167,
+ "lng": 20.4667,
+ "country": "Serbia",
+ "iso2": "RS",
+ "iso3": "SRB",
+ "admin_name": "Beograd",
+ "capital": "primary",
+ "population": 1378682,
+ "id": 1688374696
+ },
+ {
+ "city": "Moscow",
+ "city_ascii": "Moscow",
+ "lat": 55.7558,
+ "lng": 37.6178,
+ "country": "Russia",
+ "iso2": "RU",
+ "iso3": "RUS",
+ "admin_name": "Moskva",
+ "capital": "primary",
+ "population": 17693000,
+ "id": 1643318494
+ },
+ {
+ "city": "Lima",
+ "city_ascii": "Lima",
+ "lat": -12.06,
+ "lng": -77.0375,
+ "country": "Peru",
+ "iso2": "PE",
+ "iso3": "PER",
+ "admin_name": "Lima",
+ "capital": "primary",
+ "population": 8992000,
+ "id": 1604728603
+ },
+ {
+ "city": "Islamabad",
+ "city_ascii": "Islamabad",
+ "lat": 33.6989,
+ "lng": 73.0369,
+ "country": "Pakistan",
+ "iso2": "PK",
+ "iso3": "PAK",
+ "admin_name": "Islāmābād",
+ "capital": "primary",
+ "population": 1014825,
+ "id": 1586306717
+ },
+ {
+ "city": "Abuja",
+ "city_ascii": "Abuja",
+ "lat": 9.0556,
+ "lng": 7.4914,
+ "country": "Nigeria",
+ "iso2": "NG",
+ "iso3": "NGA",
+ "admin_name": "Federal Capital Territory",
+ "capital": "primary",
+ "population": 1235880,
+ "id": 1566342270
+ },
+ {
+ "city": "Managua",
+ "city_ascii": "Managua",
+ "lat": 12.15,
+ "lng": -86.2667,
+ "country": "Nicaragua",
+ "iso2": "NI",
+ "iso3": "NIC",
+ "admin_name": "Managua",
+ "capital": "primary",
+ "population": 1028808,
+ "id": 1558296252
+ },
+ {
+ "city": "Amsterdam",
+ "city_ascii": "Amsterdam",
+ "lat": 52.3667,
+ "lng": 4.8833,
+ "country": "Netherlands",
+ "iso2": "NL",
+ "iso3": "NLD",
+ "admin_name": "Noord-Holland",
+ "capital": "primary",
+ "population": 862965,
+ "id": 1528355309
+ },
+ {
+ "city": "Rabat",
+ "city_ascii": "Rabat",
+ "lat": 34.0253,
+ "lng": -6.8361,
+ "country": "Morocco",
+ "iso2": "MA",
+ "iso3": "MAR",
+ "admin_name": "Rabat-Salé-Kénitra",
+ "capital": "primary",
+ "population": 572717,
+ "id": 1504023252
+ },
+ {
+ "city": "Ulaanbaatar",
+ "city_ascii": "Ulaanbaatar",
+ "lat": 47.9214,
+ "lng": 106.9055,
+ "country": "Mongolia",
+ "iso2": "MN",
+ "iso3": "MNG",
+ "admin_name": "Ulaanbaatar",
+ "capital": "primary",
+ "population": 1396288,
+ "id": 1496024767
+ },
+ {
+ "city": "Mexico City",
+ "city_ascii": "Mexico City",
+ "lat": 19.4333,
+ "lng": -99.1333,
+ "country": "Mexico",
+ "iso2": "MX",
+ "iso3": "MEX",
+ "admin_name": "Ciudad de México",
+ "capital": "primary",
+ "population": 21505000,
+ "id": 1484247881
+ },
+ {
+ "city": "Nairobi",
+ "city_ascii": "Nairobi",
+ "lat": -1.2864,
+ "lng": 36.8172,
+ "country": "Kenya",
+ "iso2": "KE",
+ "iso3": "KEN",
+ "admin_name": "Nairobi City",
+ "capital": "primary",
+ "population": 5545000,
+ "id": 1404000661
+ },
+ {
+ "city": "Tokyo",
+ "city_ascii": "Tokyo",
+ "lat": 35.6839,
+ "lng": 139.7744,
+ "country": "Japan",
+ "iso2": "JP",
+ "iso3": "JPN",
+ "admin_name": "Tōkyō",
+ "capital": "primary",
+ "population": 39105000,
+ "id": 1392685764
+ },
+ {
+ "city": "Baghdad",
+ "city_ascii": "Baghdad",
+ "lat": 33.35,
+ "lng": 44.4167,
+ "country": "Iraq",
+ "iso2": "IQ",
+ "iso3": "IRQ",
+ "admin_name": "Baghdād",
+ "capital": "primary",
+ "population": 6107000,
+ "id": 1368596238
+ },
+ {
+ "city": "Tehran",
+ "city_ascii": "Tehran",
+ "lat": 35.7,
+ "lng": 51.4167,
+ "country": "Iran",
+ "iso2": "IR",
+ "iso3": "IRN",
+ "admin_name": "Tehrān",
+ "capital": "primary",
+ "population": 13819000,
+ "id": 1364305026
+ },
+ {
+ "city": "Jakarta",
+ "city_ascii": "Jakarta",
+ "lat": -6.2146,
+ "lng": 106.8451,
+ "country": "Indonesia",
+ "iso2": "ID",
+ "iso3": "IDN",
+ "admin_name": "Jakarta",
+ "capital": "primary",
+ "population": 35362000,
+ "id": 1360771077
+ },
+ {
+ "city": "Guatemala City",
+ "city_ascii": "Guatemala City",
+ "lat": 14.6099,
+ "lng": -90.5252,
+ "country": "Guatemala",
+ "iso2": "GT",
+ "iso3": "GTM",
+ "admin_name": "Guatemala",
+ "capital": "primary",
+ "population": 2934841,
+ "id": 1320197916
+ },
+ {
+ "city": "Berlin",
+ "city_ascii": "Berlin",
+ "lat": 52.5167,
+ "lng": 13.3833,
+ "country": "Germany",
+ "iso2": "DE",
+ "iso3": "DEU",
+ "admin_name": "Berlin",
+ "capital": "primary",
+ "population": 3664088,
+ "id": 1276451290
+ },
+ {
+ "city": "Addis Ababa",
+ "city_ascii": "Addis Ababa",
+ "lat": 9.0272,
+ "lng": 38.7369,
+ "country": "Ethiopia",
+ "iso2": "ET",
+ "iso3": "ETH",
+ "admin_name": "Ādīs Ābeba",
+ "capital": "primary",
+ "population": 3041002,
+ "id": 1231824991
+ },
+ {
+ "city": "Cairo",
+ "city_ascii": "Cairo",
+ "lat": 30.0444,
+ "lng": 31.2358,
+ "country": "Egypt",
+ "iso2": "EG",
+ "iso3": "EGY",
+ "admin_name": "Al Qāhirah",
+ "capital": "primary",
+ "population": 19787000,
+ "id": 1818253931
+ },
+ {
+ "city": "Quito",
+ "city_ascii": "Quito",
+ "lat": -0.22,
+ "lng": -78.5125,
+ "country": "Ecuador",
+ "iso2": "EC",
+ "iso3": "ECU",
+ "admin_name": "Pichincha",
+ "capital": "primary",
+ "population": 2011388,
+ "id": 1218441993
+ },
+ {
+ "city": "Bogotá",
+ "city_ascii": "Bogota",
+ "lat": 4.6126,
+ "lng": -74.0705,
+ "country": "Colombia",
+ "iso2": "CO",
+ "iso3": "COL",
+ "admin_name": "Bogotá",
+ "capital": "primary",
+ "population": 7743955,
+ "id": 1170483426
+ },
+ {
+ "city": "Beijing",
+ "city_ascii": "Beijing",
+ "lat": 39.904,
+ "lng": 116.4075,
+ "country": "China",
+ "iso2": "CN",
+ "iso3": "CHN",
+ "admin_name": "Beijing",
+ "capital": "primary",
+ "population": 19437000,
+ "id": 1156228865
+ },
+ {
+ "city": "Accra",
+ "city_ascii": "Accra",
+ "lat": 5.6037,
+ "lng": -0.187,
+ "country": "Ghana",
+ "iso2": "GH",
+ "iso3": "GHA",
+ "admin_name": "Greater Accra",
+ "capital": "primary",
+ "population": 2291352,
+ "id": 1288299415
+ },
+ {
+ "city": "Ottawa",
+ "city_ascii": "Ottawa",
+ "lat": 45.4247,
+ "lng": -75.695,
+ "country": "Canada",
+ "iso2": "CA",
+ "iso3": "CAN",
+ "admin_name": "Ontario",
+ "capital": "primary",
+ "population": 989567,
+ "id": 1124399363
+ },
+ {
+ "city": "Brasília",
+ "city_ascii": "Brasilia",
+ "lat": -15.7939,
+ "lng": -47.8828,
+ "country": "Brazil",
+ "iso2": "BR",
+ "iso3": "BRA",
+ "admin_name": "Distrito Federal",
+ "capital": "primary",
+ "population": 3015268,
+ "id": 1076144436
+ },
+ {
+ "city": "La Paz",
+ "city_ascii": "La Paz",
+ "lat": -16.4942,
+ "lng": -68.1475,
+ "country": "Bolivia",
+ "iso2": "BO",
+ "iso3": "BOL",
+ "admin_name": "La Paz",
+ "capital": "primary",
+ "population": 2867504,
+ "id": 1068000064
+ },
+ {
+ "city": "Dhaka",
+ "city_ascii": "Dhaka",
+ "lat": 23.7289,
+ "lng": 90.3944,
+ "country": "Bangladesh",
+ "iso2": "BD",
+ "iso3": "BGD",
+ "admin_name": "Dhaka",
+ "capital": "primary",
+ "population": 16839000,
+ "id": 1050529279
+ },
+ {
+ "city": "Yerevan",
+ "city_ascii": "Yerevan",
+ "lat": 40.1814,
+ "lng": 44.5144,
+ "country": "Armenia",
+ "iso2": "AM",
+ "iso3": "ARM",
+ "admin_name": "Yerevan",
+ "capital": "primary",
+ "population": 1075800,
+ "id": 1051074169
+ },
+ {
+ "city": "Chicago",
+ "city_ascii": "Chicago",
+ "lat": 41.8373,
+ "lng": -87.6862,
+ "country": "United States",
+ "iso2": "US",
+ "iso3": "USA",
+ "admin_name": "Illinois",
+ "capital": "",
+ "population": 8604203,
+ "id": 1840000494
+ },
+ {
+ "city": "Kyiv",
+ "city_ascii": "Kyiv",
+ "lat": 50.45,
+ "lng": 30.5236,
+ "country": "Ukraine",
+ "iso2": "UA",
+ "iso3": "UKR",
+ "admin_name": "Kyyiv, Misto",
+ "capital": "primary",
+ "population": 2963199,
+ "id": 1804382913
+ },
+ {
+ "city": "Dubai",
+ "city_ascii": "Dubai",
+ "lat": 25.2697,
+ "lng": 55.3094,
+ "country": "United Arab Emirates",
+ "iso2": "AE",
+ "iso3": "ARE",
+ "admin_name": "Dubayy",
+ "capital": "admin",
+ "population": 2502715,
+ "id": 1784736618
+ },
+ {
+ "city": "Mumbai",
+ "city_ascii": "Mumbai",
+ "lat": 19.0758,
+ "lng": 72.8775,
+ "country": "India",
+ "iso2": "IN",
+ "iso3": "IND",
+ "admin_name": "Mahārāshtra",
+ "capital": "admin",
+ "population": 22186000,
+ "id": 1356226629
+ },
+ {
+ "city": "Madrid",
+ "city_ascii": "Madrid",
+ "lat": 40.4167,
+ "lng": -3.7167,
+ "country": "Spain",
+ "iso2": "ES",
+ "iso3": "ESP",
+ "admin_name": "Madrid",
+ "capital": "primary",
+ "population": 6006000,
+ "id": 1724616994
+ }
+]
\ No newline at end of file
diff --git a/run_tree/data/cities_secondary_final.json b/run_tree/data/cities_secondary_final.json
new file mode 100644
index 0000000..33896c2
--- /dev/null
+++ b/run_tree/data/cities_secondary_final.json
@@ -0,0 +1,3554 @@
+[
+ {
+ "": 17749,
+ "city": "Lagos",
+ "city_ascii": "Lagos",
+ "lat": 6.45,
+ "lng": 3.4,
+ "country": "Nigeria",
+ "iso2": "NG",
+ "iso3": "NGA",
+ "admin_name": "Lagos",
+ "capital": "minor",
+ "population": 15487000,
+ "id": 1566593751,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 18286,
+ "city": "Kinshasa",
+ "city_ascii": "Kinshasa",
+ "lat": -4.3317,
+ "lng": 15.3139,
+ "country": "Congo (Kinshasa)",
+ "iso2": "CD",
+ "iso3": "COD",
+ "admin_name": "Kinshasa",
+ "capital": "primary",
+ "population": 15056000,
+ "id": 1180000363,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 19313,
+ "city": "Luanda",
+ "city_ascii": "Luanda",
+ "lat": -8.8383,
+ "lng": 13.2344,
+ "country": "Angola",
+ "iso2": "AO",
+ "iso3": "AGO",
+ "admin_name": "Luanda",
+ "capital": "primary",
+ "population": 8883000,
+ "id": 1024949724,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 19441,
+ "city": "Dar es Salaam",
+ "city_ascii": "Dar es Salaam",
+ "lat": -6.8,
+ "lng": 39.2833,
+ "country": "Tanzania",
+ "iso2": "TZ",
+ "iso3": "TZA",
+ "admin_name": "Dar es Salaam",
+ "capital": "primary",
+ "population": 7461000,
+ "id": 1834843853,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 19717,
+ "city": "Khartoum",
+ "city_ascii": "Khartoum",
+ "lat": 15.6031,
+ "lng": 32.5265,
+ "country": "Sudan",
+ "iso2": "SD",
+ "iso3": "SDN",
+ "admin_name": "Khartoum",
+ "capital": "primary",
+ "population": 6017000,
+ "id": 1729268475,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 19756,
+ "city": "Giza",
+ "city_ascii": "Giza",
+ "lat": 29.987,
+ "lng": 31.2118,
+ "country": "Egypt",
+ "iso2": "EG",
+ "iso3": "EGY",
+ "admin_name": "Al Jīzah",
+ "capital": "admin",
+ "population": 5598402,
+ "id": 1818925479,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 23020,
+ "city": "Abidjan",
+ "city_ascii": "Abidjan",
+ "lat": 5.3364,
+ "lng": -4.0267,
+ "country": "Côte d'Ivoire",
+ "iso2": "CI",
+ "iso3": "CIV",
+ "admin_name": "Abidjan",
+ "capital": "primary",
+ "population": 4980000,
+ "id": 1384207980,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 25018,
+ "city": "Johannesburg",
+ "city_ascii": "Johannesburg",
+ "lat": -26.2044,
+ "lng": 28.0416,
+ "country": "South Africa",
+ "iso2": "ZA",
+ "iso3": "ZAF",
+ "admin_name": "Gauteng",
+ "capital": "admin",
+ "population": 4434827,
+ "id": 1710550792,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 25127,
+ "city": "Casablanca",
+ "city_ascii": "Casablanca",
+ "lat": 33.5992,
+ "lng": -7.62,
+ "country": "Morocco",
+ "iso2": "MA",
+ "iso3": "MAR",
+ "admin_name": "Casablanca-Settat",
+ "capital": "admin",
+ "population": 4370000,
+ "id": 1504175315,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 25967,
+ "city": "Algiers",
+ "city_ascii": "Algiers",
+ "lat": 36.7764,
+ "lng": 3.0586,
+ "country": "Algeria",
+ "iso2": "DZ",
+ "iso3": "DZA",
+ "admin_name": "Alger",
+ "capital": "primary",
+ "population": 3415811,
+ "id": 1012973369,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 28906,
+ "city": "Antananarivo",
+ "city_ascii": "Antananarivo",
+ "lat": -18.9386,
+ "lng": 47.5214,
+ "country": "Madagascar",
+ "iso2": "MG",
+ "iso3": "MDG",
+ "admin_name": "Antananarivo",
+ "capital": "primary",
+ "population": 2610000,
+ "id": 1450978461,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 29137,
+ "city": "Ouagadougou",
+ "city_ascii": "Ouagadougou",
+ "lat": 12.3686,
+ "lng": -1.5275,
+ "country": "Burkina Faso",
+ "iso2": "BF",
+ "iso3": "BFA",
+ "admin_name": "Centre",
+ "capital": "primary",
+ "population": 2453496,
+ "id": 1854029208,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 29177,
+ "city": "Yaoundé",
+ "city_ascii": "Yaounde",
+ "lat": 3.8578,
+ "lng": 11.5181,
+ "country": "Cameroon",
+ "iso2": "CM",
+ "iso3": "CMR",
+ "admin_name": "Centre",
+ "capital": "primary",
+ "population": 2440462,
+ "id": 1120298240,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 29426,
+ "city": "Mogadishu",
+ "city_ascii": "Mogadishu",
+ "lat": 2.0408,
+ "lng": 45.3425,
+ "country": "Somalia",
+ "iso2": "SO",
+ "iso3": "SOM",
+ "admin_name": "Banaadir",
+ "capital": "primary",
+ "population": 2120000,
+ "id": 1706893395,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 29455,
+ "city": "Kumasi",
+ "city_ascii": "Kumasi",
+ "lat": 6.6833,
+ "lng": -1.6167,
+ "country": "Ghana",
+ "iso2": "GH",
+ "iso3": "GHA",
+ "admin_name": "Ashanti",
+ "capital": "admin",
+ "population": 2069350,
+ "id": 1288181103,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 29484,
+ "city": "Bamako",
+ "city_ascii": "Bamako",
+ "lat": 12.6458,
+ "lng": -7.9922,
+ "country": "Mali",
+ "iso2": "ML",
+ "iso3": "MLI",
+ "admin_name": "Bamako",
+ "capital": "primary",
+ "population": 2009109,
+ "id": 1466965925,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 30180,
+ "city": "Blantyre",
+ "city_ascii": "Blantyre",
+ "lat": -15.7861,
+ "lng": 35.0058,
+ "country": "Malawi",
+ "iso2": "MW",
+ "iso3": "MWI",
+ "admin_name": "Blantyre",
+ "capital": "admin",
+ "population": 1895973,
+ "id": 1454145012,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 32851,
+ "city": "Meru",
+ "city_ascii": "Meru",
+ "lat": 0.05,
+ "lng": 37.65,
+ "country": "Kenya",
+ "iso2": "KE",
+ "iso3": "KEN",
+ "admin_name": "Meru",
+ "capital": "admin",
+ "population": 1833000,
+ "id": 1404588252,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 32913,
+ "city": "Brazzaville",
+ "city_ascii": "Brazzaville",
+ "lat": -4.2667,
+ "lng": 15.2833,
+ "country": "Congo (Brazzaville)",
+ "iso2": "CG",
+ "iso3": "COG",
+ "admin_name": "Brazzaville",
+ "capital": "primary",
+ "population": 1827000,
+ "id": 1178340306,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 33690,
+ "city": "Lusaka",
+ "city_ascii": "Lusaka",
+ "lat": -15.4167,
+ "lng": 28.2833,
+ "country": "Zambia",
+ "iso2": "ZM",
+ "iso3": "ZMB",
+ "admin_name": "Lusaka",
+ "capital": "primary",
+ "population": 1742979,
+ "id": 1894157390,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 34034,
+ "city": "Conakry",
+ "city_ascii": "Conakry",
+ "lat": 9.538,
+ "lng": -13.6773,
+ "country": "Guinea",
+ "iso2": "GN",
+ "iso3": "GIN",
+ "admin_name": "Conakry",
+ "capital": "primary",
+ "population": 1667864,
+ "id": 1324568159,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 34063,
+ "city": "Kampala",
+ "city_ascii": "Kampala",
+ "lat": 0.3136,
+ "lng": 32.5811,
+ "country": "Uganda",
+ "iso2": "UG",
+ "iso3": "UGA",
+ "admin_name": "Kampala",
+ "capital": "primary",
+ "population": 1659600,
+ "id": 1800406299,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35321,
+ "city": "Maputo",
+ "city_ascii": "Maputo",
+ "lat": -25.9153,
+ "lng": 32.5764,
+ "country": "Mozambique",
+ "iso2": "MZ",
+ "iso3": "MOZ",
+ "admin_name": "Maputo",
+ "capital": "primary",
+ "population": 1191613,
+ "id": 1508074843,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35370,
+ "city": "Pikine",
+ "city_ascii": "Pikine",
+ "lat": 14.75,
+ "lng": -17.4,
+ "country": "Senegal",
+ "iso2": "SN",
+ "iso3": "SEN",
+ "admin_name": "Dakar",
+ "capital": "",
+ "population": 1170791,
+ "id": 1686165071,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35392,
+ "city": "Kigali",
+ "city_ascii": "Kigali",
+ "lat": -1.9536,
+ "lng": 30.0606,
+ "country": "Rwanda",
+ "iso2": "RW",
+ "iso3": "RWA",
+ "admin_name": "Kigali",
+ "capital": "primary",
+ "population": 1156663,
+ "id": 1646923541,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35511,
+ "city": "Tripoli",
+ "city_ascii": "Tripoli",
+ "lat": 32.8752,
+ "lng": 13.1875,
+ "country": "Libya",
+ "iso2": "LY",
+ "iso3": "LBY",
+ "admin_name": "Ţarābulus",
+ "capital": "primary",
+ "population": 1126000,
+ "id": 1434201852,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35593,
+ "city": "N’Djamena",
+ "city_ascii": "N'Djamena",
+ "lat": 12.11,
+ "lng": 15.05,
+ "country": "Chad",
+ "iso2": "TD",
+ "iso3": "TCD",
+ "admin_name": "Ville de N’Djaména",
+ "capital": "primary",
+ "population": 1092066,
+ "id": 1148708596,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35635,
+ "city": "Nouakchott",
+ "city_ascii": "Nouakchott",
+ "lat": 18.0858,
+ "lng": -15.9785,
+ "country": "Mauritania",
+ "iso2": "MR",
+ "iso3": "MRT",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 1077169,
+ "id": 1478414984,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35738,
+ "city": "Niamey",
+ "city_ascii": "Niamey",
+ "lat": 13.5086,
+ "lng": 2.1111,
+ "country": "Niger",
+ "iso2": "NE",
+ "iso3": "NER",
+ "admin_name": "Niamey",
+ "capital": "primary",
+ "population": 1026848,
+ "id": 1562932886,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35807,
+ "city": "Monrovia",
+ "city_ascii": "Monrovia",
+ "lat": 6.3106,
+ "lng": -10.8047,
+ "country": "Liberia",
+ "iso2": "LR",
+ "iso3": "LBR",
+ "admin_name": "Montserrado",
+ "capital": "primary",
+ "population": 1021762,
+ "id": 1430477826,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35891,
+ "city": "Asmara",
+ "city_ascii": "Asmara",
+ "lat": 15.3333,
+ "lng": 38.9167,
+ "country": "Eritrea",
+ "iso2": "ER",
+ "iso3": "ERI",
+ "admin_name": "Ma’ākel",
+ "capital": "primary",
+ "population": 963000,
+ "id": 1232791236,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 35906,
+ "city": "Freetown",
+ "city_ascii": "Freetown",
+ "lat": 8.4833,
+ "lng": -13.2331,
+ "country": "Sierra Leone",
+ "iso2": "SL",
+ "iso3": "SLE",
+ "admin_name": "Western Area",
+ "capital": "primary",
+ "population": 951000,
+ "id": 1694085071,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 36008,
+ "city": "Bangui",
+ "city_ascii": "Bangui",
+ "lat": 4.3732,
+ "lng": 18.5628,
+ "country": "Central African Republic",
+ "iso2": "CF",
+ "iso3": "CAF",
+ "admin_name": "Bangui",
+ "capital": "primary",
+ "population": 889231,
+ "id": 1140080881,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 36055,
+ "city": "Lomé",
+ "city_ascii": "Lome",
+ "lat": 6.1319,
+ "lng": 1.2228,
+ "country": "Togo",
+ "iso2": "TG",
+ "iso3": "TGO",
+ "admin_name": "Maritime",
+ "capital": "primary",
+ "population": 837437,
+ "id": 1768409132,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 36064,
+ "city": "Libreville",
+ "city_ascii": "Libreville",
+ "lat": 0.3901,
+ "lng": 9.4544,
+ "country": "Gabon",
+ "iso2": "GA",
+ "iso3": "GAB",
+ "admin_name": "Estuaire",
+ "capital": "primary",
+ "population": 797003,
+ "id": 1266952885,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 36156,
+ "city": "Cotonou",
+ "city_ascii": "Cotonou",
+ "lat": 6.402,
+ "lng": 2.5180000000000002,
+ "country": "Benin",
+ "iso2": "BJ",
+ "iso3": "BEN",
+ "admin_name": "Littoral",
+ "capital": "primary",
+ "population": 762000,
+ "id": 1204955174,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 36489,
+ "city": "Bujumbura",
+ "city_ascii": "Bujumbura",
+ "lat": -3.3825,
+ "lng": 29.3611,
+ "country": "Burundi",
+ "iso2": "BI",
+ "iso3": "BDI",
+ "admin_name": "Bujumbura Mairie",
+ "capital": "primary",
+ "population": 658859,
+ "id": 1108101689,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 37050,
+ "city": "Djibouti",
+ "city_ascii": "Djibouti",
+ "lat": 11.595,
+ "lng": 43.1481,
+ "country": "Djibouti",
+ "iso2": "DJ",
+ "iso3": "DJI",
+ "admin_name": "Djibouti",
+ "capital": "primary",
+ "population": 562000,
+ "id": 1262028958,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 37993,
+ "city": "Bissau",
+ "city_ascii": "Bissau",
+ "lat": 11.8592,
+ "lng": -15.5956,
+ "country": "Guinea-Bissau",
+ "iso2": "GW",
+ "iso3": "GNB",
+ "admin_name": "Bissau",
+ "capital": "primary",
+ "population": 395954,
+ "id": 1624168850,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38004,
+ "city": "Juba",
+ "city_ascii": "Juba",
+ "lat": 4.85,
+ "lng": 31.6,
+ "country": "South Sudan",
+ "iso2": "SS",
+ "iso3": "SSD",
+ "admin_name": "Central Equatoria",
+ "capital": "primary",
+ "population": 372410,
+ "id": 1728444337,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38021,
+ "city": "Maseru",
+ "city_ascii": "Maseru",
+ "lat": -29.31,
+ "lng": 27.48,
+ "country": "Lesotho",
+ "iso2": "LS",
+ "iso3": "LSO",
+ "admin_name": "Maseru",
+ "capital": "primary",
+ "population": 330790,
+ "id": 1426977668,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38052,
+ "city": "Windhoek",
+ "city_ascii": "Windhoek",
+ "lat": -22.57,
+ "lng": 17.0836,
+ "country": "Namibia",
+ "iso2": "",
+ "iso3": "NAM",
+ "admin_name": "Khomas",
+ "capital": "primary",
+ "population": 322500,
+ "id": 1516802003,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38376,
+ "city": "Gaborone",
+ "city_ascii": "Gaborone",
+ "lat": -24.6569,
+ "lng": 25.9086,
+ "country": "Botswana",
+ "iso2": "BW",
+ "iso3": "BWA",
+ "admin_name": "Gaborone",
+ "capital": "primary",
+ "population": 231626,
+ "id": 1072756768,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38464,
+ "city": "Saint-Denis",
+ "city_ascii": "Saint-Denis",
+ "lat": -20.8789,
+ "lng": 55.4481,
+ "country": "Reunion",
+ "iso2": "RE",
+ "iso3": "REU",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 190047,
+ "id": 1638024662,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38466,
+ "city": "Malabo",
+ "city_ascii": "Malabo",
+ "lat": 3.7521,
+ "lng": 8.7737,
+ "country": "Equatorial Guinea",
+ "iso2": "GQ",
+ "iso3": "GNQ",
+ "admin_name": "Bioko Norte",
+ "capital": "primary",
+ "population": 187302,
+ "id": 1226861333,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38508,
+ "city": "Port Louis",
+ "city_ascii": "Port Louis",
+ "lat": -20.1667,
+ "lng": 57.5,
+ "country": "Mauritius",
+ "iso2": "MU",
+ "iso3": "MUS",
+ "admin_name": "Port Louis",
+ "capital": "primary",
+ "population": 149194,
+ "id": 1480131261,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38910,
+ "city": "Praia",
+ "city_ascii": "Praia",
+ "lat": 14.9177,
+ "lng": -23.5092,
+ "country": "Cabo Verde",
+ "iso2": "CV",
+ "iso3": "CPV",
+ "admin_name": "Praia",
+ "capital": "primary",
+ "population": 127832,
+ "id": 1132398770,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38934,
+ "city": "Moroni",
+ "city_ascii": "Moroni",
+ "lat": -11.7036,
+ "lng": 43.2536,
+ "country": "Comoros",
+ "iso2": "KM",
+ "iso3": "COM",
+ "admin_name": "Grande Comore",
+ "capital": "primary",
+ "population": 111329,
+ "id": 1174793581,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38959,
+ "city": "Mbabane",
+ "city_ascii": "Mbabane",
+ "lat": -26.3208,
+ "lng": 31.1617,
+ "country": "Swaziland",
+ "iso2": "SZ",
+ "iso3": "SWZ",
+ "admin_name": "Hhohho",
+ "capital": "primary",
+ "population": 94874,
+ "id": 1748811945,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 38997,
+ "city": "São Tomé",
+ "city_ascii": "Sao Tome",
+ "lat": 0.3333,
+ "lng": 6.7333,
+ "country": "Sao Tome And Principe",
+ "iso2": "ST",
+ "iso3": "STP",
+ "admin_name": "São Tomé",
+ "capital": "primary",
+ "population": 56166,
+ "id": 1678301324,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 39058,
+ "city": "Banjul",
+ "city_ascii": "Banjul",
+ "lat": 13.4531,
+ "lng": -16.5775,
+ "country": "The Gambia",
+ "iso2": "GM",
+ "iso3": "GMB",
+ "admin_name": "Banjul",
+ "capital": "primary",
+ "population": 31356,
+ "id": 1270723713,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 39067,
+ "city": "Mamoudzou",
+ "city_ascii": "Mamoudzou",
+ "lat": -12.7871,
+ "lng": 45.275,
+ "country": "Mayotte",
+ "iso2": "YT",
+ "iso3": "MYT",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 32057,
+ "id": 1175099654,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 39072,
+ "city": "Victoria",
+ "city_ascii": "Victoria",
+ "lat": -4.6236,
+ "lng": 55.4544,
+ "country": "Seychelles",
+ "iso2": "SC",
+ "iso3": "SYC",
+ "admin_name": "English River",
+ "capital": "primary",
+ "population": 26450,
+ "id": 1690193579,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 39277,
+ "city": "Jamestown",
+ "city_ascii": "Jamestown",
+ "lat": -15.9251,
+ "lng": -5.7179,
+ "country": "Saint Helena, Ascension, And Tristan Da Cunha",
+ "iso2": "SH",
+ "iso3": "SHN",
+ "admin_name": "Saint Helena",
+ "capital": "primary",
+ "population": 714,
+ "id": 1654761576,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 41316,
+ "city": "Bulawayo",
+ "city_ascii": "Bulawayo",
+ "lat": -20.1667,
+ "lng": 28.5667,
+ "country": "Zimbabwe",
+ "iso2": "ZW",
+ "iso3": "ZWE",
+ "admin_name": "Bulawayo",
+ "capital": "admin",
+ "population": 653337,
+ "id": 1716032632,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 41529,
+ "city": "Sidi Bouzid",
+ "city_ascii": "Sidi Bouzid",
+ "lat": 35.0381,
+ "lng": 9.4858,
+ "country": "Tunisia",
+ "iso2": "TN",
+ "iso3": "TUN",
+ "admin_name": "Sidi Bouzid",
+ "capital": "admin",
+ "population": 429912,
+ "id": 1788196439,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 41767,
+ "city": "Nazrēt",
+ "city_ascii": "Nazret",
+ "lat": 8.55,
+ "lng": 39.2667,
+ "country": "Ethiopia",
+ "iso2": "ET",
+ "iso3": "ETH",
+ "admin_name": "Oromīya",
+ "capital": "",
+ "population": 324000,
+ "id": 1231826873,
+ "_merge": "left_only",
+ "Continent": "Africa"
+ },
+ {
+ "": 0,
+ "city": "Delhi",
+ "city_ascii": "Delhi",
+ "lat": 28.6667,
+ "lng": 77.2167,
+ "country": "India",
+ "iso2": "IN",
+ "iso3": "IND",
+ "admin_name": "Delhi",
+ "capital": "admin",
+ "population": 31870000,
+ "id": 1356872604,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 1598,
+ "city": "Manila",
+ "city_ascii": "Manila",
+ "lat": 14.6,
+ "lng": 120.9833,
+ "country": "Philippines",
+ "iso2": "PH",
+ "iso3": "PHL",
+ "admin_name": "Manila",
+ "capital": "primary",
+ "population": 23971000,
+ "id": 1608618140,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 6741,
+ "city": "Shanghai",
+ "city_ascii": "Shanghai",
+ "lat": 31.1667,
+ "lng": 121.4667,
+ "country": "China",
+ "iso2": "CN",
+ "iso3": "CHN",
+ "admin_name": "Shanghai",
+ "capital": "admin",
+ "population": 22118000,
+ "id": 1156073548,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 16423,
+ "city": "Ōsaka",
+ "city_ascii": "Osaka",
+ "lat": 34.751999999999995,
+ "lng": 135.4582,
+ "country": "Japan",
+ "iso2": "JP",
+ "iso3": "JPN",
+ "admin_name": "Ōsaka",
+ "capital": "admin",
+ "population": 15490000,
+ "id": 1392419823,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 17825,
+ "city": "Istanbul",
+ "city_ascii": "Istanbul",
+ "lat": 41.01,
+ "lng": 28.9603,
+ "country": "Turkey",
+ "iso2": "TR",
+ "iso3": "TUR",
+ "admin_name": "İstanbul",
+ "capital": "admin",
+ "population": 15311000,
+ "id": 1792756324,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 18175,
+ "city": "Karachi",
+ "city_ascii": "Karachi",
+ "lat": 24.86,
+ "lng": 67.01,
+ "country": "Pakistan",
+ "iso2": "PK",
+ "iso3": "PAK",
+ "admin_name": "Sindh",
+ "capital": "admin",
+ "population": 15292000,
+ "id": 1586129469,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 18376,
+ "city": "Ho Chi Minh City",
+ "city_ascii": "Ho Chi Minh City",
+ "lat": 10.8167,
+ "lng": 106.6333,
+ "country": "Vietnam",
+ "iso2": "VN",
+ "iso3": "VNM",
+ "admin_name": "Hồ Chí Minh",
+ "capital": "admin",
+ "population": 13954000,
+ "id": 1704774326,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 19406,
+ "city": "Kuala Lumpur",
+ "city_ascii": "Kuala Lumpur",
+ "lat": 3.1478,
+ "lng": 101.6953,
+ "country": "Malaysia",
+ "iso2": "MY",
+ "iso3": "MYS",
+ "admin_name": "Kuala Lumpur",
+ "capital": "primary",
+ "population": 8639000,
+ "id": 1458988644,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 19505,
+ "city": "Hong Kong",
+ "city_ascii": "Hong Kong",
+ "lat": 22.3069,
+ "lng": 114.1831,
+ "country": "Hong Kong",
+ "iso2": "HK",
+ "iso3": "HKG",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 7398000,
+ "id": 1344982653,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 19677,
+ "city": "Riyadh",
+ "city_ascii": "Riyadh",
+ "lat": 24.65,
+ "lng": 46.71,
+ "country": "Saudi Arabia",
+ "iso2": "SA",
+ "iso3": "SAU",
+ "admin_name": "Ar Riyāḑ",
+ "capital": "primary",
+ "population": 6889000,
+ "id": 1682999334,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 20850,
+ "city": "Rangoon",
+ "city_ascii": "Rangoon",
+ "lat": 16.795,
+ "lng": 96.16,
+ "country": "Myanmar",
+ "iso2": "MM",
+ "iso3": "MMR",
+ "admin_name": "Yangon",
+ "capital": "primary",
+ "population": 5430000,
+ "id": 1104616656,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 23019,
+ "city": "Singapore",
+ "city_ascii": "Singapore",
+ "lat": 1.3,
+ "lng": 103.8,
+ "country": "Singapore",
+ "iso2": "SG",
+ "iso3": "SGP",
+ "admin_name": "Central Singapore",
+ "capital": "primary",
+ "population": 5271000,
+ "id": 1702341327,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 25827,
+ "city": "Kabul",
+ "city_ascii": "Kabul",
+ "lat": 34.5328,
+ "lng": 69.1658,
+ "country": "Afghanistan",
+ "iso2": "AF",
+ "iso3": "AFG",
+ "admin_name": "Kābul",
+ "capital": "primary",
+ "population": 4273156,
+ "id": 1004993580,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 25872,
+ "city": "Amman",
+ "city_ascii": "Amman",
+ "lat": 31.95,
+ "lng": 35.9333,
+ "country": "Jordan",
+ "iso2": "JO",
+ "iso3": "JOR",
+ "admin_name": "Al ‘Āşimah",
+ "capital": "primary",
+ "population": 4007526,
+ "id": 1400522593,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 25891,
+ "city": "Busan",
+ "city_ascii": "Busan",
+ "lat": 35.1,
+ "lng": 129.0403,
+ "country": "South Korea",
+ "iso2": "KR",
+ "iso3": "KOR",
+ "admin_name": "Busan",
+ "capital": "admin",
+ "population": 3453198,
+ "id": 1410601465,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 26206,
+ "city": "Mashhad",
+ "city_ascii": "Mashhad",
+ "lat": 36.3069,
+ "lng": 59.6042,
+ "country": "Iran",
+ "iso2": "IR",
+ "iso3": "IRN",
+ "admin_name": "Khorāsān-e Raẕavī",
+ "capital": "admin",
+ "population": 3001184,
+ "id": 1364123206,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 26467,
+ "city": "Kuwait City",
+ "city_ascii": "Kuwait City",
+ "lat": 29.375,
+ "lng": 47.98,
+ "country": "Kuwait",
+ "iso2": "KW",
+ "iso3": "KWT",
+ "admin_name": "Al ‘Āşimah",
+ "capital": "primary",
+ "population": 2989000,
+ "id": 1414102075,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 26471,
+ "city": "Sanaa",
+ "city_ascii": "Sanaa",
+ "lat": 15.35,
+ "lng": 44.2,
+ "country": "Yemen",
+ "iso2": "YE",
+ "iso3": "YEM",
+ "admin_name": "Amānat al ‘Āşimah",
+ "capital": "primary",
+ "population": 2957000,
+ "id": 1887750814,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 26513,
+ "city": "Bandung",
+ "city_ascii": "Bandung",
+ "lat": -6.95,
+ "lng": 107.5667,
+ "country": "Indonesia",
+ "iso2": "ID",
+ "iso3": "IDN",
+ "admin_name": "Jawa Barat",
+ "capital": "admin",
+ "population": 2875673,
+ "id": 1360313023,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 28792,
+ "city": "Pyongyang",
+ "city_ascii": "Pyongyang",
+ "lat": 39.03,
+ "lng": 125.73,
+ "country": "North Korea",
+ "iso2": "KP",
+ "iso3": "PRK",
+ "admin_name": "P’yŏngyang",
+ "capital": "primary",
+ "population": 2863000,
+ "id": 1408738594,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 28935,
+ "city": "Chattogram",
+ "city_ascii": "Chattogram",
+ "lat": 22.335,
+ "lng": 91.8325,
+ "country": "Bangladesh",
+ "iso2": "BD",
+ "iso3": "BGD",
+ "admin_name": "Chittagong",
+ "capital": "admin",
+ "population": 2581643,
+ "id": 1050830722,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 29078,
+ "city": "Tashkent",
+ "city_ascii": "Tashkent",
+ "lat": 41.3,
+ "lng": 69.2667,
+ "country": "Uzbekistan",
+ "iso2": "UZ",
+ "iso3": "UZB",
+ "admin_name": "Toshkent",
+ "capital": "primary",
+ "population": 2497900,
+ "id": 1860331871,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 29213,
+ "city": "Baku",
+ "city_ascii": "Baku",
+ "lat": 40.3667,
+ "lng": 49.8352,
+ "country": "Azerbaijan",
+ "iso2": "AZ",
+ "iso3": "AZE",
+ "admin_name": "Bakı",
+ "capital": "primary",
+ "population": 2181800,
+ "id": 1031946365,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 29399,
+ "city": "Phnom Penh",
+ "city_ascii": "Phnom Penh",
+ "lat": 11.5696,
+ "lng": 104.921,
+ "country": "Cambodia",
+ "iso2": "KH",
+ "iso3": "KHM",
+ "admin_name": "Phnom Penh",
+ "capital": "primary",
+ "population": 2129371,
+ "id": 1116260534,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 29702,
+ "city": "Aleppo",
+ "city_ascii": "Aleppo",
+ "lat": 36.2,
+ "lng": 37.15,
+ "country": "Syria",
+ "iso2": "SY",
+ "iso3": "SYR",
+ "admin_name": "Ḩalab",
+ "capital": "admin",
+ "population": 1916781,
+ "id": 1760366651,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 29814,
+ "city": "Almaty",
+ "city_ascii": "Almaty",
+ "lat": 43.25,
+ "lng": 76.9,
+ "country": "Kazakhstan",
+ "iso2": "KZ",
+ "iso3": "KAZ",
+ "admin_name": "Almaty",
+ "capital": "admin",
+ "population": 1916822,
+ "id": 1398351701,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 33991,
+ "city": "Mosul",
+ "city_ascii": "Mosul",
+ "lat": 36.3667,
+ "lng": 43.1167,
+ "country": "Iraq",
+ "iso2": "IQ",
+ "iso3": "IRQ",
+ "admin_name": "Nīnawá",
+ "capital": "admin",
+ "population": 1694000,
+ "id": 1368194914,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 34194,
+ "city": "Abu Dhabi",
+ "city_ascii": "Abu Dhabi",
+ "lat": 24.4511,
+ "lng": 54.3969,
+ "country": "United Arab Emirates",
+ "iso2": "AE",
+ "iso3": "ARE",
+ "admin_name": "Abū Z̧aby",
+ "capital": "primary",
+ "population": 1483000,
+ "id": 1784176710,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 34737,
+ "city": "Muscat",
+ "city_ascii": "Muscat",
+ "lat": 23.6139,
+ "lng": 58.5922,
+ "country": "Oman",
+ "iso2": "OM",
+ "iso3": "OMN",
+ "admin_name": "Masqaţ",
+ "capital": "primary",
+ "population": 1421409,
+ "id": 1512035506,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 35362,
+ "city": "Doha",
+ "city_ascii": "Doha",
+ "lat": 25.3,
+ "lng": 51.5333,
+ "country": "Qatar",
+ "iso2": "QA",
+ "iso3": "QAT",
+ "admin_name": "Ad Dawḩah",
+ "capital": "primary",
+ "population": 1186023,
+ "id": 1634459660,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 35552,
+ "city": "Tbilisi",
+ "city_ascii": "Tbilisi",
+ "lat": 41.7225,
+ "lng": 44.7925,
+ "country": "Georgia",
+ "iso2": "GE",
+ "iso3": "GEO",
+ "admin_name": "Tbilisi",
+ "capital": "primary",
+ "population": 1118035,
+ "id": 1268203191,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 35657,
+ "city": "Bishkek",
+ "city_ascii": "Bishkek",
+ "lat": 42.8667,
+ "lng": 74.5667,
+ "country": "Kyrgyzstan",
+ "iso2": "KG",
+ "iso3": "KGZ",
+ "admin_name": "Bishkek",
+ "capital": "primary",
+ "population": 1053900,
+ "id": 1417191971,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 35695,
+ "city": "Kathmandu",
+ "city_ascii": "Kathmandu",
+ "lat": 27.7167,
+ "lng": 85.3667,
+ "country": "Nepal",
+ "iso2": "NP",
+ "iso3": "NPL",
+ "admin_name": "Bāgmatī",
+ "capital": "primary",
+ "population": 975453,
+ "id": 1524589448,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 35912,
+ "city": "Vientiane",
+ "city_ascii": "Vientiane",
+ "lat": 17.9667,
+ "lng": 102.6,
+ "country": "Laos",
+ "iso2": "LA",
+ "iso3": "LAO",
+ "admin_name": "Viangchan",
+ "capital": "primary",
+ "population": 948487,
+ "id": 1418732714,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 35931,
+ "city": "Jerusalem",
+ "city_ascii": "Jerusalem",
+ "lat": 31.7833,
+ "lng": 35.2167,
+ "country": "Israel",
+ "iso2": "IL",
+ "iso3": "ISR",
+ "admin_name": "Jerusalem",
+ "capital": "primary",
+ "population": 919438,
+ "id": 1376261644,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 36140,
+ "city": "Dushanbe",
+ "city_ascii": "Dushanbe",
+ "lat": 38.5731,
+ "lng": 68.7864,
+ "country": "Tajikistan",
+ "iso2": "TJ",
+ "iso3": "TJK",
+ "admin_name": "Dushanbe",
+ "capital": "primary",
+ "population": 778500,
+ "id": 1762930616,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 36169,
+ "city": "Colombo",
+ "city_ascii": "Colombo",
+ "lat": 6.9167,
+ "lng": 79.8333,
+ "country": "Sri Lanka",
+ "iso2": "LK",
+ "iso3": "LKA",
+ "admin_name": "Western",
+ "capital": "primary",
+ "population": 752993,
+ "id": 1144251314,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 37930,
+ "city": "Beirut",
+ "city_ascii": "Beirut",
+ "lat": 33.8869,
+ "lng": 35.5131,
+ "country": "Lebanon",
+ "iso2": "LB",
+ "iso3": "LBN",
+ "admin_name": "Beyrouth",
+ "capital": "primary",
+ "population": 361366,
+ "id": 1422847713,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 38032,
+ "city": "Nicosia",
+ "city_ascii": "Nicosia",
+ "lat": 35.1725,
+ "lng": 33.365,
+ "country": "Cyprus",
+ "iso2": "CY",
+ "iso3": "CYP",
+ "admin_name": "Lefkosía",
+ "capital": "primary",
+ "population": 330000,
+ "id": 1196944155,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 38417,
+ "city": "Dili",
+ "city_ascii": "Dili",
+ "lat": -8.5536,
+ "lng": 125.5783,
+ "country": "Timor-Leste",
+ "iso2": "TL",
+ "iso3": "TLS",
+ "admin_name": "Díli",
+ "capital": "primary",
+ "population": 222323,
+ "id": 1626308942,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 38430,
+ "city": "Male",
+ "city_ascii": "Male",
+ "lat": 4.175,
+ "lng": 73.5083,
+ "country": "Maldives",
+ "iso2": "MV",
+ "iso3": "MDV",
+ "admin_name": "Maale",
+ "capital": "primary",
+ "population": 133019,
+ "id": 1462441685,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 38504,
+ "city": "Manama",
+ "city_ascii": "Manama",
+ "lat": 26.225,
+ "lng": 50.5775,
+ "country": "Bahrain",
+ "iso2": "BH",
+ "iso3": "BHR",
+ "admin_name": "Al ‘Āşimah",
+ "capital": "primary",
+ "population": 157474,
+ "id": 1048989486,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 38939,
+ "city": "Thimphu",
+ "city_ascii": "Thimphu",
+ "lat": 27.4833,
+ "lng": 89.6333,
+ "country": "Bhutan",
+ "iso2": "BT",
+ "iso3": "BTN",
+ "admin_name": "Thimphu",
+ "capital": "primary",
+ "population": 104000,
+ "id": 1064010361,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 39011,
+ "city": "Bandar Seri Begawan",
+ "city_ascii": "Bandar Seri Begawan",
+ "lat": 4.9167,
+ "lng": 114.9167,
+ "country": "Brunei",
+ "iso2": "BN",
+ "iso3": "BRN",
+ "admin_name": "Brunei and Muara",
+ "capital": "primary",
+ "population": 50000,
+ "id": 1096465895,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 39084,
+ "city": "Ashgabat",
+ "city_ascii": "Ashgabat",
+ "lat": 37.95,
+ "lng": 58.3833,
+ "country": "Turkmenistan",
+ "iso2": "TM",
+ "iso3": "TKM",
+ "admin_name": "Ahal",
+ "capital": "primary",
+ "population": 19426,
+ "id": 1795049992,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 41315,
+ "city": "Macau",
+ "city_ascii": "Macau",
+ "lat": 22.203000000000003,
+ "lng": 113.545,
+ "country": "Macau",
+ "iso2": "MO",
+ "iso3": "MAC",
+ "admin_name": "",
+ "capital": "",
+ "population": 568700,
+ "id": 1446227359,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 42286,
+ "city": "Nonthaburi",
+ "city_ascii": "Nonthaburi",
+ "lat": 13.8667,
+ "lng": 100.5167,
+ "country": "Thailand",
+ "iso2": "TH",
+ "iso3": "THA",
+ "admin_name": "Nonthaburi",
+ "capital": "admin",
+ "population": 255671,
+ "id": 1764667935,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 42695,
+ "city": "Mörön",
+ "city_ascii": "Moron",
+ "lat": 49.6375,
+ "lng": 100.1614,
+ "country": "Mongolia",
+ "iso2": "MN",
+ "iso3": "MNG",
+ "admin_name": "Hövsgöl",
+ "capital": "admin",
+ "population": 134530,
+ "id": 1496482400,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 42721,
+ "city": "Gyumri",
+ "city_ascii": "Gyumri",
+ "lat": 40.7894,
+ "lng": 43.8475,
+ "country": "Armenia",
+ "iso2": "AM",
+ "iso3": "ARM",
+ "admin_name": "Shirak",
+ "capital": "admin",
+ "population": 121976,
+ "id": 1051341374,
+ "_merge": "left_only",
+ "Continent": "Asia"
+ },
+ {
+ "": 18455,
+ "city": "London",
+ "city_ascii": "London",
+ "lat": 51.5072,
+ "lng": -0.1275,
+ "country": "United Kingdom",
+ "iso2": "GB",
+ "iso3": "GBR",
+ "admin_name": "London, City of",
+ "capital": "primary",
+ "population": 11120000,
+ "id": 1826645935,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 21515,
+ "city": "Saint Petersburg",
+ "city_ascii": "Saint Petersburg",
+ "lat": 59.95,
+ "lng": 30.3167,
+ "country": "Russia",
+ "iso2": "RU",
+ "iso3": "RUS",
+ "admin_name": "Sankt-Peterburg",
+ "capital": "admin",
+ "population": 5384342,
+ "id": 1643616350,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 23404,
+ "city": "Barcelona",
+ "city_ascii": "Barcelona",
+ "lat": 41.3825,
+ "lng": 2.1769,
+ "country": "Spain",
+ "iso2": "ES",
+ "iso3": "ESP",
+ "admin_name": "Catalonia",
+ "capital": "admin",
+ "population": 4735000,
+ "id": 1724594040,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 26652,
+ "city": "Rome",
+ "city_ascii": "Rome",
+ "lat": 41.8931,
+ "lng": 12.4828,
+ "country": "Italy",
+ "iso2": "IT",
+ "iso3": "ITA",
+ "admin_name": "Lazio",
+ "capital": "primary",
+ "population": 2872800,
+ "id": 1380382862,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 29517,
+ "city": "Minsk",
+ "city_ascii": "Minsk",
+ "lat": 53.9022,
+ "lng": 27.5618,
+ "country": "Belarus",
+ "iso2": "BY",
+ "iso3": "BLR",
+ "admin_name": "Minsk",
+ "capital": "primary",
+ "population": 2009786,
+ "id": 1112348503,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 29943,
+ "city": "Vienna",
+ "city_ascii": "Vienna",
+ "lat": 48.2083,
+ "lng": 16.3725,
+ "country": "Austria",
+ "iso2": "AT",
+ "iso3": "AUT",
+ "admin_name": "Wien",
+ "capital": "primary",
+ "population": 1911191,
+ "id": 1040261752,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 30209,
+ "city": "Hamburg",
+ "city_ascii": "Hamburg",
+ "lat": 53.55,
+ "lng": 10,
+ "country": "Germany",
+ "iso2": "DE",
+ "iso3": "DEU",
+ "admin_name": "Hamburg",
+ "capital": "admin",
+ "population": 1852478,
+ "id": 1276041799,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 32931,
+ "city": "Warsaw",
+ "city_ascii": "Warsaw",
+ "lat": 52.23,
+ "lng": 21.0111,
+ "country": "Poland",
+ "iso2": "PL",
+ "iso3": "POL",
+ "admin_name": "Mazowieckie",
+ "capital": "primary",
+ "population": 1790658,
+ "id": 1616024847,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 33222,
+ "city": "Brussels",
+ "city_ascii": "Brussels",
+ "lat": 50.8353,
+ "lng": 4.3314,
+ "country": "Belgium",
+ "iso2": "BE",
+ "iso3": "BEL",
+ "admin_name": "Brussels-Capital Region",
+ "capital": "primary",
+ "population": 1743000,
+ "id": 1056469830,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 33725,
+ "city": "Budapest",
+ "city_ascii": "Budapest",
+ "lat": 47.4983,
+ "lng": 19.0408,
+ "country": "Hungary",
+ "iso2": "HU",
+ "iso3": "HUN",
+ "admin_name": "Budapest",
+ "capital": "primary",
+ "population": 1723836,
+ "id": 1348611435,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 34202,
+ "city": "Kharkiv",
+ "city_ascii": "Kharkiv",
+ "lat": 50,
+ "lng": 36.2292,
+ "country": "Ukraine",
+ "iso2": "UA",
+ "iso3": "UKR",
+ "admin_name": "Kharkivs’ka Oblast’",
+ "capital": "admin",
+ "population": 1446107,
+ "id": 1804588111,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 34825,
+ "city": "Prague",
+ "city_ascii": "Prague",
+ "lat": 50.0833,
+ "lng": 14.4167,
+ "country": "Czechia",
+ "iso2": "CZ",
+ "iso3": "CZE",
+ "admin_name": "Praha, Hlavní Město",
+ "capital": "primary",
+ "population": 1335084,
+ "id": 1203744823,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 35155,
+ "city": "Sofia",
+ "city_ascii": "Sofia",
+ "lat": 42.6979,
+ "lng": 23.3217,
+ "country": "Bulgaria",
+ "iso2": "BG",
+ "iso3": "BGR",
+ "admin_name": "Sofia-Grad",
+ "capital": "primary",
+ "population": 1277411,
+ "id": 1100762037,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 35623,
+ "city": "Copenhagen",
+ "city_ascii": "Copenhagen",
+ "lat": 55.6805,
+ "lng": 12.5615,
+ "country": "Denmark",
+ "iso2": "DK",
+ "iso3": "DNK",
+ "admin_name": "Hovedstaden",
+ "capital": "primary",
+ "population": 1085000,
+ "id": 1208763942,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 35839,
+ "city": "Stockholm",
+ "city_ascii": "Stockholm",
+ "lat": 59.3294,
+ "lng": 18.0686,
+ "country": "Sweden",
+ "iso2": "SE",
+ "iso3": "SWE",
+ "admin_name": "Stockholm",
+ "capital": "primary",
+ "population": 975819,
+ "id": 1752425602,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 36083,
+ "city": "Zagreb",
+ "city_ascii": "Zagreb",
+ "lat": 45.8131,
+ "lng": 15.9772,
+ "country": "Croatia",
+ "iso2": "HR",
+ "iso3": "HRV",
+ "admin_name": "Zagreb, Grad",
+ "capital": "primary",
+ "population": 790017,
+ "id": 1191233290,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 36191,
+ "city": "Oslo",
+ "city_ascii": "Oslo",
+ "lat": 59.9111,
+ "lng": 10.7528,
+ "country": "Norway",
+ "iso2": "NO",
+ "iso3": "NOR",
+ "admin_name": "Oslo",
+ "capital": "primary",
+ "population": 693494,
+ "id": 1578324706,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 36352,
+ "city": "Athens",
+ "city_ascii": "Athens",
+ "lat": 37.9842,
+ "lng": 23.7281,
+ "country": "Greece",
+ "iso2": "GR",
+ "iso3": "GRC",
+ "admin_name": "Attikí",
+ "capital": "primary",
+ "population": 664046,
+ "id": 1300715560,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 36508,
+ "city": "Helsinki",
+ "city_ascii": "Helsinki",
+ "lat": 60.1756,
+ "lng": 24.9342,
+ "country": "Finland",
+ "iso2": "FI",
+ "iso3": "FIN",
+ "admin_name": "Uusimaa",
+ "capital": "primary",
+ "population": 642045,
+ "id": 1246177997,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 36720,
+ "city": "Skopje",
+ "city_ascii": "Skopje",
+ "lat": 41.9833,
+ "lng": 21.4333,
+ "country": "Macedonia",
+ "iso2": "MK",
+ "iso3": "MKD",
+ "admin_name": "Skopje",
+ "capital": "primary",
+ "population": 640000,
+ "id": 1807600615,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 36791,
+ "city": "Chisinau",
+ "city_ascii": "Chisinau",
+ "lat": 47.0228,
+ "lng": 28.8353,
+ "country": "Moldova",
+ "iso2": "MD",
+ "iso3": "MDA",
+ "admin_name": "Chişinău",
+ "capital": "primary",
+ "population": 639000,
+ "id": 1498011437,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 36872,
+ "city": "Riga",
+ "city_ascii": "Riga",
+ "lat": 56.9475,
+ "lng": 24.1069,
+ "country": "Latvia",
+ "iso2": "LV",
+ "iso3": "LVA",
+ "admin_name": "Rīga",
+ "capital": "primary",
+ "population": 614618,
+ "id": 1428586192,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 36933,
+ "city": "Vilnius",
+ "city_ascii": "Vilnius",
+ "lat": 54.6833,
+ "lng": 25.2833,
+ "country": "Lithuania",
+ "iso2": "LT",
+ "iso3": "LTU",
+ "admin_name": "Vilniaus Miestas",
+ "capital": "primary",
+ "population": 574147,
+ "id": 1440887149,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 37056,
+ "city": "Dublin",
+ "city_ascii": "Dublin",
+ "lat": 53.3497,
+ "lng": -6.2603,
+ "country": "Ireland",
+ "iso2": "IE",
+ "iso3": "IRL",
+ "admin_name": "Dublin",
+ "capital": "primary",
+ "population": 553165,
+ "id": 1372595407,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 37097,
+ "city": "The Hague",
+ "city_ascii": "The Hague",
+ "lat": 52.08,
+ "lng": 4.31,
+ "country": "Netherlands",
+ "iso2": "NL",
+ "iso3": "NLD",
+ "admin_name": "Zuid-Holland",
+ "capital": "primary",
+ "population": 537833,
+ "id": 1528799905,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 37471,
+ "city": "Lisbon",
+ "city_ascii": "Lisbon",
+ "lat": 38.708,
+ "lng": -9.139,
+ "country": "Portugal",
+ "iso2": "PT",
+ "iso3": "PRT",
+ "admin_name": "Lisboa",
+ "capital": "primary",
+ "population": 506654,
+ "id": 1620619017,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 37768,
+ "city": "Bratislava",
+ "city_ascii": "Bratislava",
+ "lat": 48.1447,
+ "lng": 17.1128,
+ "country": "Slovakia",
+ "iso2": "SK",
+ "iso3": "SVK",
+ "admin_name": "Bratislavský",
+ "capital": "primary",
+ "population": 475503,
+ "id": 1703195001,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 37905,
+ "city": "Tallinn",
+ "city_ascii": "Tallinn",
+ "lat": 59.4372,
+ "lng": 24.745,
+ "country": "Estonia",
+ "iso2": "EE",
+ "iso3": "EST",
+ "admin_name": "Harjumaa",
+ "capital": "primary",
+ "population": 438341,
+ "id": 1233260021,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 37942,
+ "city": "Tirana",
+ "city_ascii": "Tirana",
+ "lat": 41.33,
+ "lng": 19.82,
+ "country": "Albania",
+ "iso2": "AL",
+ "iso3": "ALB",
+ "admin_name": "Tiranë",
+ "capital": "primary",
+ "population": 418495,
+ "id": 1008162156,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 38130,
+ "city": "Ljubljana",
+ "city_ascii": "Ljubljana",
+ "lat": 46.05,
+ "lng": 14.5167,
+ "country": "Slovenia",
+ "iso2": "SI",
+ "iso3": "SVN",
+ "admin_name": "Ljubljana",
+ "capital": "primary",
+ "population": 286745,
+ "id": 1705917455,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 38339,
+ "city": "Sarajevo",
+ "city_ascii": "Sarajevo",
+ "lat": 43.8563,
+ "lng": 18.4132,
+ "country": "Bosnia And Herzegovina",
+ "iso2": "BA",
+ "iso3": "BIH",
+ "admin_name": "Bosnia and Herzegovina, Federation of",
+ "capital": "primary",
+ "population": 275524,
+ "id": 1070966777,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 38463,
+ "city": "Gibraltar",
+ "city_ascii": "Gibraltar",
+ "lat": 36.1324,
+ "lng": -5.3781,
+ "country": "Gibraltar",
+ "iso2": "GI",
+ "iso3": "GIB",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 187083,
+ "id": 1292385245,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 38480,
+ "city": "Podgorica",
+ "city_ascii": "Podgorica",
+ "lat": 42.4397,
+ "lng": 19.2661,
+ "country": "Montenegro",
+ "iso2": "ME",
+ "iso3": "MNE",
+ "admin_name": "Podgorica",
+ "capital": "primary",
+ "population": 174515,
+ "id": 1499454516,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 38515,
+ "city": "Bern",
+ "city_ascii": "Bern",
+ "lat": 46.948,
+ "lng": 7.4474,
+ "country": "Switzerland",
+ "iso2": "CH",
+ "iso3": "CHE",
+ "admin_name": "Bern",
+ "capital": "primary",
+ "population": 133798,
+ "id": 1756374318,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 38874,
+ "city": "Luxembourg",
+ "city_ascii": "Luxembourg",
+ "lat": 49.6106,
+ "lng": 6.1328,
+ "country": "Luxembourg",
+ "iso2": "LU",
+ "iso3": "LUX",
+ "admin_name": "Luxembourg",
+ "capital": "primary",
+ "population": 128512,
+ "id": 1442262731,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 38897,
+ "city": "Reykjavík",
+ "city_ascii": "Reykjavik",
+ "lat": 64.1475,
+ "lng": -21.935,
+ "country": "Iceland",
+ "iso2": "IS",
+ "iso3": "ISL",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 128793,
+ "id": 1352327190,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39022,
+ "city": "Monaco",
+ "city_ascii": "Monaco",
+ "lat": 43.7396,
+ "lng": 7.4069,
+ "country": "Monaco",
+ "iso2": "MC",
+ "iso3": "MCO",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 36371,
+ "id": 1492854256,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39057,
+ "city": "Saint Helier",
+ "city_ascii": "Saint Helier",
+ "lat": 49.1858,
+ "lng": -2.11,
+ "country": "Jersey",
+ "iso2": "JE",
+ "iso3": "JEY",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 33522,
+ "id": 1832490253,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39069,
+ "city": "Douglas",
+ "city_ascii": "Douglas",
+ "lat": 54.15,
+ "lng": -4.4819,
+ "country": "Isle Of Man",
+ "iso2": "IM",
+ "iso3": "IMN",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 27938,
+ "id": 1833688345,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39074,
+ "city": "Andorra la Vella",
+ "city_ascii": "Andorra la Vella",
+ "lat": 42.5,
+ "lng": 1.5,
+ "country": "Andorra",
+ "iso2": "AD",
+ "iso3": "AND",
+ "admin_name": "Andorra la Vella",
+ "capital": "primary",
+ "population": 22615,
+ "id": 1020828846,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39141,
+ "city": "Tórshavn",
+ "city_ascii": "Torshavn",
+ "lat": 62,
+ "lng": -6.7833,
+ "country": "Faroe Islands",
+ "iso2": "FO",
+ "iso3": "FRO",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 13326,
+ "id": 1234904517,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39171,
+ "city": "Valletta",
+ "city_ascii": "Valletta",
+ "lat": 35.8978,
+ "lng": 14.5125,
+ "country": "Malta",
+ "iso2": "MT",
+ "iso3": "MLT",
+ "admin_name": "Valletta",
+ "capital": "primary",
+ "population": 6444,
+ "id": 1470574399,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39246,
+ "city": "Vaduz",
+ "city_ascii": "Vaduz",
+ "lat": 47.1397,
+ "lng": 9.5219,
+ "country": "Liechtenstein",
+ "iso2": "LI",
+ "iso3": "LIE",
+ "admin_name": "Vaduz",
+ "capital": "primary",
+ "population": 5668,
+ "id": 1438317747,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39259,
+ "city": "San Marino",
+ "city_ascii": "San Marino",
+ "lat": 43.931999999999995,
+ "lng": 12.4484,
+ "country": "San Marino",
+ "iso2": "SM",
+ "iso3": "SMR",
+ "admin_name": "San Marino Città",
+ "capital": "primary",
+ "population": 4040,
+ "id": 1674741947,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39270,
+ "city": "Vatican City",
+ "city_ascii": "Vatican City",
+ "lat": 41.9033,
+ "lng": 12.4534,
+ "country": "Vatican City",
+ "iso2": "VA",
+ "iso3": "VAT",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 825,
+ "id": 1336000000,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 39297,
+ "city": "Marseille",
+ "city_ascii": "Marseille",
+ "lat": 43.2964,
+ "lng": 5.37,
+ "country": "France",
+ "iso2": "FR",
+ "iso3": "FRA",
+ "admin_name": "Provence-Alpes-Côte d’Azur",
+ "capital": "admin",
+ "population": 868277,
+ "id": 1250774071,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 41616,
+ "city": "Novi Sad",
+ "city_ascii": "Novi Sad",
+ "lat": 45.2644,
+ "lng": 19.8317,
+ "country": "Serbia",
+ "iso2": "RS",
+ "iso3": "SRB",
+ "admin_name": "Novi Sad",
+ "capital": "admin",
+ "population": 380000,
+ "id": 1688169087,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 41813,
+ "city": "Cluj-Napoca",
+ "city_ascii": "Cluj-Napoca",
+ "lat": 46.78,
+ "lng": 23.5594,
+ "country": "Romania",
+ "iso2": "RO",
+ "iso3": "ROU",
+ "admin_name": "Cluj",
+ "capital": "admin",
+ "population": 324576,
+ "id": 1642503974,
+ "_merge": "left_only",
+ "Continent": "Europe"
+ },
+ {
+ "": 8284,
+ "city": "New York",
+ "city_ascii": "New York",
+ "lat": 40.6943,
+ "lng": -73.9249,
+ "country": "United States",
+ "iso2": "US",
+ "iso3": "USA",
+ "admin_name": "New York",
+ "capital": "",
+ "population": 18713220,
+ "id": 1840034016,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 19867,
+ "city": "Guadalajara",
+ "city_ascii": "Guadalajara",
+ "lat": 20.6767,
+ "lng": -103.3475,
+ "country": "Mexico",
+ "iso2": "MX",
+ "iso3": "MEX",
+ "admin_name": "Jalisco",
+ "capital": "admin",
+ "population": 5437000,
+ "id": 1484950208,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 20891,
+ "city": "Toronto",
+ "city_ascii": "Toronto",
+ "lat": 43.7417,
+ "lng": -79.3733,
+ "country": "Canada",
+ "iso2": "CA",
+ "iso3": "CAN",
+ "admin_name": "Ontario",
+ "capital": "admin",
+ "population": 5429524,
+ "id": 1124279679,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 28953,
+ "city": "Santo Domingo",
+ "city_ascii": "Santo Domingo",
+ "lat": 18.4764,
+ "lng": -69.8933,
+ "country": "Dominican Republic",
+ "iso2": "DO",
+ "iso3": "DOM",
+ "admin_name": "Ozama",
+ "capital": "primary",
+ "population": 2581827,
+ "id": 1214636202,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 29309,
+ "city": "Havana",
+ "city_ascii": "Havana",
+ "lat": 23.1367,
+ "lng": -82.3589,
+ "country": "Cuba",
+ "iso2": "CU",
+ "iso3": "CUB",
+ "admin_name": "La Habana",
+ "capital": "primary",
+ "population": 2141652,
+ "id": 1192752771,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 35282,
+ "city": "Comayagüela",
+ "city_ascii": "Comayaguela",
+ "lat": 14.0833,
+ "lng": -87.2167,
+ "country": "Honduras",
+ "iso2": "HN",
+ "iso3": "HND",
+ "admin_name": "Francisco Morazán",
+ "capital": "",
+ "population": 1250000,
+ "id": 1340762485,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 35824,
+ "city": "Port-au-Prince",
+ "city_ascii": "Port-au-Prince",
+ "lat": 18.5425,
+ "lng": -72.3386,
+ "country": "Haiti",
+ "iso2": "HT",
+ "iso3": "HTI",
+ "admin_name": "Ouest",
+ "capital": "primary",
+ "population": 987310,
+ "id": 1332401940,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 36033,
+ "city": "Panama City",
+ "city_ascii": "Panama City",
+ "lat": 9,
+ "lng": -79.5,
+ "country": "Panama",
+ "iso2": "PA",
+ "iso3": "PAN",
+ "admin_name": "Panamá",
+ "capital": "primary",
+ "population": 880691,
+ "id": 1591672475,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 36915,
+ "city": "Kingston",
+ "city_ascii": "Kingston",
+ "lat": 17.9714,
+ "lng": -76.7931,
+ "country": "Jamaica",
+ "iso2": "JM",
+ "iso3": "JAM",
+ "admin_name": "Kingston",
+ "capital": "primary",
+ "population": 580000,
+ "id": 1388709177,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 36995,
+ "city": "San Salvador",
+ "city_ascii": "San Salvador",
+ "lat": 13.6989,
+ "lng": -89.1914,
+ "country": "El Salvador",
+ "iso2": "SV",
+ "iso3": "SLV",
+ "admin_name": "San Salvador",
+ "capital": "primary",
+ "population": 567698,
+ "id": 1222647454,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 38112,
+ "city": "San José",
+ "city_ascii": "San Jose",
+ "lat": 9.9333,
+ "lng": -84.0833,
+ "country": "Costa Rica",
+ "iso2": "CR",
+ "iso3": "CRI",
+ "admin_name": "San José",
+ "capital": "primary",
+ "population": 288054,
+ "id": 1188749877,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 38372,
+ "city": "Nassau",
+ "city_ascii": "Nassau",
+ "lat": 25.0667,
+ "lng": -77.3333,
+ "country": "The Bahamas",
+ "iso2": "BS",
+ "iso3": "BHS",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 274400,
+ "id": 1044318561,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 38375,
+ "city": "Fort-de-France",
+ "city_ascii": "Fort-de-France",
+ "lat": 14.6104,
+ "lng": -61.08,
+ "country": "Martinique",
+ "iso2": "MQ",
+ "iso3": "MTQ",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 253995,
+ "id": 1474969110,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 38514,
+ "city": "Willemstad",
+ "city_ascii": "Willemstad",
+ "lat": 12.107999999999999,
+ "lng": -68.935,
+ "country": "Curaçao",
+ "iso2": "CW",
+ "iso3": "CUW",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 150000,
+ "id": 1531000000,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 38933,
+ "city": "Bridgetown",
+ "city_ascii": "Bridgetown",
+ "lat": 13.0975,
+ "lng": -59.6167,
+ "country": "Barbados",
+ "iso2": "BB",
+ "iso3": "BRB",
+ "admin_name": "Saint Michael",
+ "capital": "primary",
+ "population": 110000,
+ "id": 1052274244,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 38988,
+ "city": "Castries",
+ "city_ascii": "Castries",
+ "lat": 14.0167,
+ "lng": -60.9833,
+ "country": "Saint Lucia",
+ "iso2": "LC",
+ "iso3": "LCA",
+ "admin_name": "Castries",
+ "capital": "primary",
+ "population": 70000,
+ "id": 1662922505,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39010,
+ "city": "Hamilton",
+ "city_ascii": "Hamilton",
+ "lat": 32.2942,
+ "lng": -64.7839,
+ "country": "Bermuda",
+ "iso2": "BM",
+ "iso3": "BMU",
+ "admin_name": "Hamilton",
+ "capital": "primary",
+ "population": 52320,
+ "id": 1060000000,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39023,
+ "city": "Port of Spain",
+ "city_ascii": "Port of Spain",
+ "lat": 10.6667,
+ "lng": -61.5167,
+ "country": "Trinidad And Tobago",
+ "iso2": "TT",
+ "iso3": "TTO",
+ "admin_name": "Port of Spain",
+ "capital": "primary",
+ "population": 37074,
+ "id": 1780670676,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39055,
+ "city": "Oranjestad",
+ "city_ascii": "Oranjestad",
+ "lat": 12.5186,
+ "lng": -70.0358,
+ "country": "Aruba",
+ "iso2": "AW",
+ "iso3": "ABW",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 34980,
+ "id": 1533513057,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39071,
+ "city": "George Town",
+ "city_ascii": "George Town",
+ "lat": 19.2866,
+ "lng": -81.3744,
+ "country": "Cayman Islands",
+ "iso2": "KY",
+ "iso3": "CYM",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 27704,
+ "id": 1136159124,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39073,
+ "city": "Kingstown",
+ "city_ascii": "Kingstown",
+ "lat": 13.1667,
+ "lng": -61.2333,
+ "country": "Saint Vincent And The Grenadines",
+ "iso2": "VC",
+ "iso3": "VCT",
+ "admin_name": "Saint George",
+ "capital": "primary",
+ "population": 25000,
+ "id": 1670376659,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39081,
+ "city": "Saint John’s",
+ "city_ascii": "Saint John's",
+ "lat": 17.1211,
+ "lng": -61.8447,
+ "country": "Antigua And Barbuda",
+ "iso2": "AG",
+ "iso3": "ATG",
+ "admin_name": "Saint John",
+ "capital": "primary",
+ "population": 21926,
+ "id": 1028912067,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39108,
+ "city": "Nuuk",
+ "city_ascii": "Nuuk",
+ "lat": 64.175,
+ "lng": -51.7333,
+ "country": "Greenland",
+ "iso2": "GL",
+ "iso3": "GRL",
+ "admin_name": "Sermersooq",
+ "capital": "primary",
+ "population": 18326,
+ "id": 1304028354,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39130,
+ "city": "Belmopan",
+ "city_ascii": "Belmopan",
+ "lat": 17.25,
+ "lng": -88.7675,
+ "country": "Belize",
+ "iso2": "BZ",
+ "iso3": "BLZ",
+ "admin_name": "Cayo",
+ "capital": "primary",
+ "population": 17222,
+ "id": 1084586375,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39139,
+ "city": "Roseau",
+ "city_ascii": "Roseau",
+ "lat": 15.3,
+ "lng": -61.3833,
+ "country": "Dominica",
+ "iso2": "DM",
+ "iso3": "DMA",
+ "admin_name": "Saint George",
+ "capital": "primary",
+ "population": 16582,
+ "id": 1212060440,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39140,
+ "city": "Basseterre",
+ "city_ascii": "Basseterre",
+ "lat": 17.2983,
+ "lng": -62.7342,
+ "country": "Saint Kitts And Nevis",
+ "iso2": "KN",
+ "iso3": "KNA",
+ "admin_name": "Saint George Basseterre",
+ "capital": "primary",
+ "population": 13220,
+ "id": 1659198919,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39239,
+ "city": "Grand Turk",
+ "city_ascii": "Grand Turk",
+ "lat": 21.4664,
+ "lng": -71.13600000000001,
+ "country": "Turks And Caicos Islands",
+ "iso2": "TC",
+ "iso3": "TCA",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 5801,
+ "id": 1796965313,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39258,
+ "city": "Saint George’s",
+ "city_ascii": "Saint George's",
+ "lat": 12.0444,
+ "lng": -61.7417,
+ "country": "Grenada",
+ "iso2": "GD",
+ "iso3": "GRD",
+ "admin_name": "Saint George",
+ "capital": "primary",
+ "population": 4315,
+ "id": 1308891766,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39272,
+ "city": "Basse-Terre",
+ "city_ascii": "Basse-Terre",
+ "lat": 16.0104,
+ "lng": -61.7055,
+ "country": "Guadeloupe",
+ "iso2": "GP",
+ "iso3": "GLP",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 307,
+ "id": 1312938008,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 39280,
+ "city": "Brades",
+ "city_ascii": "Brades",
+ "lat": 16.7928,
+ "lng": -62.2106,
+ "country": "Montserrat",
+ "iso2": "MS",
+ "iso3": "MSR",
+ "admin_name": "Saint Peter",
+ "capital": "primary",
+ "population": 391,
+ "id": 1500971198,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 41341,
+ "city": "Villa Nueva",
+ "city_ascii": "Villa Nueva",
+ "lat": 14.5314,
+ "lng": -90.5964,
+ "country": "Guatemala",
+ "iso2": "GT",
+ "iso3": "GTM",
+ "admin_name": "Guatemala",
+ "capital": "minor",
+ "population": 618397,
+ "id": 1320353009,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 42179,
+ "city": "San Juan",
+ "city_ascii": "San Juan",
+ "lat": 18.4037,
+ "lng": -66.0636,
+ "country": "Puerto Rico",
+ "iso2": "PR",
+ "iso3": "PRI",
+ "admin_name": "Puerto Rico",
+ "capital": "primary",
+ "population": 323279,
+ "id": 1630035577,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 42235,
+ "city": "Boaco",
+ "city_ascii": "Boaco",
+ "lat": 12.4667,
+ "lng": -85.6667,
+ "country": "Nicaragua",
+ "iso2": "NI",
+ "iso3": "NIC",
+ "admin_name": "Boaco",
+ "capital": "admin",
+ "population": 317000,
+ "id": 1558023610,
+ "_merge": "left_only",
+ "Continent": "North America"
+ },
+ {
+ "": 23046,
+ "city": "Sydney",
+ "city_ascii": "Sydney",
+ "lat": -33.865,
+ "lng": 151.2094,
+ "country": "Australia",
+ "iso2": "AU",
+ "iso3": "AUS",
+ "admin_name": "New South Wales",
+ "capital": "admin",
+ "population": 4840600,
+ "id": 1036074917,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 34753,
+ "city": "Auckland",
+ "city_ascii": "Auckland",
+ "lat": -36.85,
+ "lng": 174.7833,
+ "country": "New Zealand",
+ "iso2": "NZ",
+ "iso3": "NZL",
+ "admin_name": "Auckland",
+ "capital": "admin",
+ "population": 1346091,
+ "id": 1554435911,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 38084,
+ "city": "Port Moresby",
+ "city_ascii": "Port Moresby",
+ "lat": -9.4789,
+ "lng": 147.1494,
+ "country": "Papua New Guinea",
+ "iso2": "PG",
+ "iso3": "PNG",
+ "admin_name": "National Capital",
+ "capital": "primary",
+ "population": 317374,
+ "id": 1598685395,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 38873,
+ "city": "Papeete",
+ "city_ascii": "Papeete",
+ "lat": -17.5334,
+ "lng": -149.5667,
+ "country": "French Polynesia",
+ "iso2": "PF",
+ "iso3": "PYF",
+ "admin_name": "Îles du Vent",
+ "capital": "primary",
+ "population": 131695,
+ "id": 1258907380,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 38968,
+ "city": "Nouméa",
+ "city_ascii": "Noumea",
+ "lat": -22.2625,
+ "lng": 166.4443,
+ "country": "New Caledonia",
+ "iso2": "NC",
+ "iso3": "NCL",
+ "admin_name": "Province Sud",
+ "capital": "primary",
+ "population": 93060,
+ "id": 1540958092,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 38971,
+ "city": "Honiara",
+ "city_ascii": "Honiara",
+ "lat": -9.4333,
+ "lng": 159.95,
+ "country": "Solomon Islands",
+ "iso2": "SB",
+ "iso3": "SLB",
+ "admin_name": "Honiara",
+ "capital": "primary",
+ "population": 84520,
+ "id": 1090737486,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 38980,
+ "city": "Suva",
+ "city_ascii": "Suva",
+ "lat": -18.1333,
+ "lng": 178.4333,
+ "country": "Fiji",
+ "iso2": "FJ",
+ "iso3": "FJI",
+ "admin_name": "Rewa",
+ "capital": "primary",
+ "population": 88271,
+ "id": 1242615095,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39004,
+ "city": "Port-Vila",
+ "city_ascii": "Port-Vila",
+ "lat": -17.7333,
+ "lng": 168.3167,
+ "country": "Vanuatu",
+ "iso2": "VU",
+ "iso3": "VUT",
+ "admin_name": "Shefa",
+ "capital": "primary",
+ "population": 51437,
+ "id": 1548805075,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39041,
+ "city": "Apia",
+ "city_ascii": "Apia",
+ "lat": -13.8333,
+ "lng": -171.8333,
+ "country": "Samoa",
+ "iso2": "WS",
+ "iso3": "WSM",
+ "admin_name": "Tuamasaga",
+ "capital": "primary",
+ "population": 37708,
+ "id": 1882489296,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39053,
+ "city": "Tarawa",
+ "city_ascii": "Tarawa",
+ "lat": 1.3382,
+ "lng": 173.0176,
+ "country": "Kiribati",
+ "iso2": "KI",
+ "iso3": "KIR",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 28802,
+ "id": 1296152641,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39068,
+ "city": "Majuro",
+ "city_ascii": "Majuro",
+ "lat": 7.0918,
+ "lng": 171.3802,
+ "country": "Marshall Islands",
+ "iso2": "MH",
+ "iso3": "MHL",
+ "admin_name": "Majuro",
+ "capital": "primary",
+ "population": 30000,
+ "id": 1584000000,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39082,
+ "city": "Nuku‘alofa",
+ "city_ascii": "Nuku`alofa",
+ "lat": -21.1347,
+ "lng": -175.2083,
+ "country": "Tonga",
+ "iso2": "TO",
+ "iso3": "TON",
+ "admin_name": "Tongatapu",
+ "capital": "primary",
+ "population": 23221,
+ "id": 1776737461,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39170,
+ "city": "Pago Pago",
+ "city_ascii": "Pago Pago",
+ "lat": -14.274000000000001,
+ "lng": -170.7046,
+ "country": "American Samoa",
+ "iso2": "AS",
+ "iso3": "ASM",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 12576,
+ "id": 1016976740,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39240,
+ "city": "Palikir",
+ "city_ascii": "Palikir",
+ "lat": 6.9178,
+ "lng": 158.185,
+ "country": "Federated States of Micronesia",
+ "iso2": "FM",
+ "iso3": "FSM",
+ "admin_name": "Pohnpei",
+ "capital": "primary",
+ "population": 6227,
+ "id": 1583008885,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39245,
+ "city": "Funafuti",
+ "city_ascii": "Funafuti",
+ "lat": -8.5243,
+ "lng": 179.1942,
+ "country": "Tuvalu",
+ "iso2": "TV",
+ "iso3": "TUV",
+ "admin_name": "Funafuti",
+ "capital": "primary",
+ "population": 6025,
+ "id": 1798251391,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39257,
+ "city": "Avarua",
+ "city_ascii": "Avarua",
+ "lat": -21.206999999999997,
+ "lng": -159.77100000000002,
+ "country": "Cook Islands",
+ "iso2": "CK",
+ "iso3": "COK",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 5445,
+ "id": 1184217570,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39268,
+ "city": "Capitol Hill",
+ "city_ascii": "Capitol Hill",
+ "lat": 15.2137,
+ "lng": 145.7546,
+ "country": "Northern Mariana Islands",
+ "iso2": "MP",
+ "iso3": "MNP",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 2500,
+ "id": 1580556972,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39271,
+ "city": "Alofi",
+ "city_ascii": "Alofi",
+ "lat": -19.055999999999997,
+ "lng": -169.921,
+ "country": "Niue",
+ "iso2": "NU",
+ "iso3": "NIU",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 1611,
+ "id": 1570882835,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39274,
+ "city": "Hagåtña",
+ "city_ascii": "Hagta",
+ "lat": 13.4745,
+ "lng": 144.7504,
+ "country": "Guam",
+ "iso2": "GU",
+ "iso3": "GUM",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 1051,
+ "id": 1316937540,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 39285,
+ "city": "Ngerulmud",
+ "city_ascii": "Ngerulmud",
+ "lat": 7.5006,
+ "lng": 134.6242,
+ "country": "Palau",
+ "iso2": "PW",
+ "iso3": "PLW",
+ "admin_name": "Melekeok",
+ "capital": "primary",
+ "population": 271,
+ "id": 1585525081,
+ "_merge": "left_only",
+ "Continent": "Oceania"
+ },
+ {
+ "": 3138,
+ "city": "São Paulo",
+ "city_ascii": "Sao Paulo",
+ "lat": -23.5504,
+ "lng": -46.6339,
+ "country": "Brazil",
+ "iso2": "BR",
+ "iso3": "BRA",
+ "admin_name": "São Paulo",
+ "capital": "admin",
+ "population": 22495000,
+ "id": 1076532519,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 16104,
+ "city": "Buenos Aires",
+ "city_ascii": "Buenos Aires",
+ "lat": -34.5997,
+ "lng": -58.3819,
+ "country": "Argentina",
+ "iso2": "AR",
+ "iso3": "ARG",
+ "admin_name": "Buenos Aires, Ciudad Autónoma de",
+ "capital": "primary",
+ "population": 16216000,
+ "id": 1032717330,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 19506,
+ "city": "Santiago",
+ "city_ascii": "Santiago",
+ "lat": -33.45,
+ "lng": -70.6667,
+ "country": "Chile",
+ "iso2": "CL",
+ "iso3": "CHL",
+ "admin_name": "Región Metropolitana",
+ "capital": "primary",
+ "population": 7026000,
+ "id": 1152554349,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 24538,
+ "city": "Timbío",
+ "city_ascii": "Timbio",
+ "lat": 2.3528,
+ "lng": -76.6819,
+ "country": "Colombia",
+ "iso2": "CO",
+ "iso3": "COL",
+ "admin_name": "Cauca",
+ "capital": "minor",
+ "population": 4444444,
+ "id": 1170815311,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 26124,
+ "city": "Santa Cruz",
+ "city_ascii": "Santa Cruz",
+ "lat": -17.7892,
+ "lng": -63.1975,
+ "country": "Bolivia",
+ "iso2": "BO",
+ "iso3": "BOL",
+ "admin_name": "Santa Cruz",
+ "capital": "admin",
+ "population": 3151676,
+ "id": 1068129363,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 28829,
+ "city": "Guayaquil",
+ "city_ascii": "Guayaquil",
+ "lat": -2.19,
+ "lng": -79.8875,
+ "country": "Ecuador",
+ "iso2": "EC",
+ "iso3": "ECU",
+ "admin_name": "Guayas",
+ "capital": "admin",
+ "population": 2723665,
+ "id": 1218802178,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 29605,
+ "city": "Caracas",
+ "city_ascii": "Caracas",
+ "lat": 10.5,
+ "lng": -66.9333,
+ "country": "Venezuela",
+ "iso2": "VE",
+ "iso3": "VEN",
+ "admin_name": "Distrito Capital",
+ "capital": "primary",
+ "population": 1943901,
+ "id": 1862748204,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 35098,
+ "city": "Montevideo",
+ "city_ascii": "Montevideo",
+ "lat": -34.8667,
+ "lng": -56.1667,
+ "country": "Uruguay",
+ "iso2": "UY",
+ "iso3": "URY",
+ "admin_name": "Montevideo",
+ "capital": "primary",
+ "population": 1319108,
+ "id": 1858107000,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 35407,
+ "city": "Callao",
+ "city_ascii": "Callao",
+ "lat": -12.0611,
+ "lng": -77.1333,
+ "country": "Peru",
+ "iso2": "PE",
+ "iso3": "PER",
+ "admin_name": "Callao",
+ "capital": "admin",
+ "population": 1129854,
+ "id": 1604422141,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 37427,
+ "city": "Asunción",
+ "city_ascii": "Asuncion",
+ "lat": -25.3,
+ "lng": -57.6333,
+ "country": "Paraguay",
+ "iso2": "PY",
+ "iso3": "PRY",
+ "admin_name": "Asunción",
+ "capital": "primary",
+ "population": 524190,
+ "id": 1600057911,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 38403,
+ "city": "Paramaribo",
+ "city_ascii": "Paramaribo",
+ "lat": 5.8667,
+ "lng": -55.1667,
+ "country": "Suriname",
+ "iso2": "SR",
+ "iso3": "SUR",
+ "admin_name": "Paramaribo",
+ "capital": "primary",
+ "population": 223757,
+ "id": 1740518660,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 38451,
+ "city": "Georgetown",
+ "city_ascii": "Georgetown",
+ "lat": 6.7833,
+ "lng": -58.1667,
+ "country": "Guyana",
+ "iso2": "GY",
+ "iso3": "GUY",
+ "admin_name": "Demerara-Mahaica",
+ "capital": "primary",
+ "population": 200500,
+ "id": 1328160906,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 38990,
+ "city": "Cayenne",
+ "city_ascii": "Cayenne",
+ "lat": 4.933,
+ "lng": -52.33,
+ "country": "French Guiana",
+ "iso2": "GF",
+ "iso3": "GUF",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 61550,
+ "id": 1254304969,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 39269,
+ "city": "Stanley",
+ "city_ascii": "Stanley",
+ "lat": -51.7,
+ "lng": -57.85,
+ "country": "Falkland Islands (Islas Malvinas)",
+ "iso2": "FK",
+ "iso3": "FLK",
+ "admin_name": "",
+ "capital": "primary",
+ "population": 2213,
+ "id": 1238780509,
+ "_merge": "left_only",
+ "Continent": "South America"
+ },
+ {
+ "": 39296,
+ "city": "Grytviken",
+ "city_ascii": "Grytviken",
+ "lat": -54.2806,
+ "lng": -36.508,
+ "country": "South Georgia And South Sandwich Islands",
+ "iso2": "GS",
+ "iso3": "SGS",
+ "admin_name": "",
+ "capital": "",
+ "population": 99,
+ "id": 1239048837,
+ "_merge": "left_only",
+ "Continent": "South America"
+ }
+]
\ No newline at end of file
diff --git a/run_tree/data/font.ttf b/run_tree/data/font.ttf
new file mode 100644
index 0000000..06aafc0
Binary files /dev/null and b/run_tree/data/font.ttf differ
diff --git a/run_tree/data/incenter_data/c/question_1.h b/run_tree/data/incenter_data/c/question_1.h
new file mode 100644
index 0000000..252704f
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_1.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_1_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.7211155378486056, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.6813559322033899, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.5084745762711864, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.6507936507936508, },
+[4] = { city_paris, 2022, MONTH_jan, 0.5611355457816873, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.7130232558139535, },
+[6] = { city_denver, 2022, MONTH_jan, 0.7130232558139535, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.8550185873605948, },
+[8] = { city_harare, 2022, MONTH_jan, 0.5, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.3597560975609756, },
+[10] = { city_washington, 2022, MONTH_jan, 0.7130232558139535, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.5555555555555556, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.5942028985507246, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.3597560975609756, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.632, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.5081967213114754, },
+[16] = { city_lima, 2022, MONTH_jan, 0.5, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.6526315789473685, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.8888888888888888, },
+[19] = { city_managua, 2022, MONTH_jan, 0.6774193548387096, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.6104783599088838, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.4523809523809524, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.5084745762711864, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.5747718230751229, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.5, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.3949372298826919, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.7, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.7, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.45276162790697677, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.6774193548387096, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.6714178544636159, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.5, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.5942028985507246, },
+[33] = { city_quito, 2022, MONTH_jan, 0.48148148148148145, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.5698324022346368, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.5084745762711864, },
+[36] = { city_accra, 2022, MONTH_jan, 0.5, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.7213114754098361, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.7555555555555555, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.7555555555555555, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.7348066298342542, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.8550185873605948, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.7130232558139535, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.46875, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.7, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.6507936507936508, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.5716845878136201, },
+};
+global u32 question_1_len = sizeof(question_1_data) / sizeof(question_1_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_10.h b/run_tree/data/incenter_data/c/question_10.h
new file mode 100644
index 0000000..777ea36
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_10.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_10_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.7907915993537964, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.9196675900277008, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.8922570016474465, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.8931259407927747, },
+[4] = { city_paris, 2022, MONTH_jan, 0.8915187376725838, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.8767605633802817, },
+[6] = { city_denver, 2022, MONTH_jan, 0.8822393822393823, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.8529289572081429, },
+[8] = { city_harare, 2022, MONTH_jan, 0.5197693574958814, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.9733333333333334, },
+[10] = { city_washington, 2022, MONTH_jan, 0.885, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.8386880856760375, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.7669983416252073, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.8907630522088353, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.8032629558541267, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.8402501421262081, },
+[16] = { city_lima, 2022, MONTH_jan, 0.8164874551971326, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.8931259407927747, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.7283049472830495, },
+[19] = { city_managua, 2022, MONTH_jan, 0.8558333333333333, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.9145383104125737, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.8283333333333334, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.6910866910866911, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.9178632969557725, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.8371907422186752, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.9025679758308157, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.7039864291772688, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.6967267869071476, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.939043451078462, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.9072416598860863, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.8915187376725838, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.8747967479674796, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.7172643869891576, },
+[33] = { city_quito, 2022, MONTH_jan, 0.8932203389830509, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.8940789473684211, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.8922570016474465, },
+[36] = { city_accra, 2022, MONTH_jan, 0.8371907422186752, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.8638626182180189, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.9008595988538682, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.7321688500727802, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.9066666666666666, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.8845836768342952, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.8559870550161812, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.8059105431309904, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.7172643869891576, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.8931259407927747, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.8915187376725838, },
+};
+global u32 question_10_len = sizeof(question_10_data) / sizeof(question_10_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_11.h b/run_tree/data/incenter_data/c/question_11.h
new file mode 100644
index 0000000..d9efea5
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_11.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_11_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.7527472527472527, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.7346938775510204, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.75, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.8222222222222222, },
+[4] = { city_paris, 2022, MONTH_jan, 0.5690460306871248, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.7624035281146637, },
+[6] = { city_denver, 2022, MONTH_jan, 0.7624035281146637, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.6437054631828979, },
+[8] = { city_harare, 2022, MONTH_jan, 0.8, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.8148148148148148, },
+[10] = { city_washington, 2022, MONTH_jan, 0.7624035281146637, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.5384615384615384, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.8431372549019608, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.8148148148148148, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.6818181818181818, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.4065934065934066, },
+[16] = { city_lima, 2022, MONTH_jan, 0.8235294117647058, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.8105263157894737, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.7142857142857143, },
+[19] = { city_managua, 2022, MONTH_jan, 0.8571428571428571, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.7003676470588235, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.7058823529411765, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.75, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.7737030411449016, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.8, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.4397765363128492, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.7, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.7, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.8859470468431772, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.8571428571428571, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.6584922797456857, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.8, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.8431372549019608, },
+[33] = { city_quito, 2022, MONTH_jan, 0.8181818181818182, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.7089552238805971, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.75, },
+[36] = { city_accra, 2022, MONTH_jan, 0.8, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.6509695290858726, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.8969465648854962, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.8969465648854962, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.8523206751054853, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.6437054631828979, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.7624035281146637, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.8181818181818182, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.7, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.8222222222222222, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.7440191387559809, },
+};
+global u32 question_11_len = sizeof(question_11_data) / sizeof(question_11_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_12.h b/run_tree/data/incenter_data/c/question_12.h
new file mode 100644
index 0000000..1ebdb9f
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_12.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_12_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.4789180588703262, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.827357970215113, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.600926538716082, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.42355889724310775, },
+[4] = { city_paris, 2022, MONTH_jan, 0.8413114754098361, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.6807511737089202, },
+[6] = { city_denver, 2022, MONTH_jan, 0.6730769230769231, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.6662309368191721, },
+[8] = { city_harare, 2022, MONTH_jan, 0.7267489711934156, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.4633333333333333, },
+[10] = { city_washington, 2022, MONTH_jan, 0.7408637873754153, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.522, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.8239731768650461, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.5084337349397591, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.6408518877057116, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.5629834254143646, },
+[16] = { city_lima, 2022, MONTH_jan, 0.6192857142857143, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.42355889724310775, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.6176232821341956, },
+[19] = { city_managua, 2022, MONTH_jan, 0.7441666666666666, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.8019323671497585, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.5075, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.4340659340659341, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.6657093624353819, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.5442338072669827, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.6260162601626016, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.7216666666666667, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.40493662441627754, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.470625, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.7831558567279767, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.8413114754098361, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.45934959349593496, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.7814845704753962, },
+[33] = { city_quito, 2022, MONTH_jan, 0.6958333333333333, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.7730263157894737, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.600926538716082, },
+[36] = { city_accra, 2022, MONTH_jan, 0.5442338072669827, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.7073170731707317, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.6152099886492622, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.7289946576007771, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.7075, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.6059113300492611, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.6844660194174758, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.4786656322730799, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.7814845704753962, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.42355889724310775, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.8413114754098361, },
+};
+global u32 question_12_len = sizeof(question_12_data) / sizeof(question_12_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_13.h b/run_tree/data/incenter_data/c/question_13.h
new file mode 100644
index 0000000..426020d
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_13.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_13_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.08285385500575373, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.09841628959276018, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.08166666666666667, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.20042987641053198, },
+[4] = { city_paris, 2022, MONTH_jan, 0.031207598371777476, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.1670616113744076, },
+[6] = { city_denver, 2022, MONTH_jan, 0.15145631067961166, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.14479830148619957, },
+[8] = { city_harare, 2022, MONTH_jan, 0.3549459684123026, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.08166666666666667, },
+[10] = { city_washington, 2022, MONTH_jan, 0.14214046822742474, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.18401611820013433, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.28439597315436244, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.157429718875502, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.0691333982473223, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.07294264339152119, },
+[16] = { city_lima, 2022, MONTH_jan, 0.18324607329842932, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.20042987641053198, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.18892508143322476, },
+[19] = { city_managua, 2022, MONTH_jan, 0.11083333333333334, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.09620098039215687, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.5508333333333333, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.2658924205378973, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.3469387755102041, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.37571312143439284, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.0972644376899696, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.16893039049235994, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.07811447811447811, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.0608640807316304, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.322213181448332, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.031207598371777476, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.15637860082304528, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.16893039049235994, },
+[33] = { city_quito, 2022, MONTH_jan, 0.14830875975715524, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.13355263157894737, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.08166666666666667, },
+[36] = { city_accra, 2022, MONTH_jan, 0.37571312143439284, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.14758586361373818, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.20054384772263767, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.14959016393442623, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.13162544169611307, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.03935599284436494, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.1563517915309446, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.055281342546890426, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.16893039049235994, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.20042987641053198, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.031207598371777476, },
+};
+global u32 question_13_len = sizeof(question_13_data) / sizeof(question_13_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_14.h b/run_tree/data/incenter_data/c/question_14.h
new file mode 100644
index 0000000..fdfb753
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_14.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_14_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.6968174204355109, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.4254484304932735, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.15694768410529825, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.37493632195618953, },
+[4] = { city_paris, 2022, MONTH_jan, 0.31785003317850036, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.6515513126491647, },
+[6] = { city_denver, 2022, MONTH_jan, 0.700587084148728, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.39265898420828, },
+[8] = { city_harare, 2022, MONTH_jan, 0.76814011676397, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.145, },
+[10] = { city_washington, 2022, MONTH_jan, 0.6761744966442953, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.529689608636977, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.7771236333052985, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.09799196787148594, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.6625891946992865, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.5068903535050928, },
+[16] = { city_lima, 2022, MONTH_jan, 0.7326086956521739, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.37493632195618953, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.6726238830219334, },
+[19] = { city_managua, 2022, MONTH_jan, 0.6166666666666667, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.37310308738880166, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.5525, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.43933823529411764, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.6502890173410405, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.5854632587859425, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.4491803278688525, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.74, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.48944860449285227, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.3997445721583653, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.7050538525269263, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.31785003317850036, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.6325350949628407, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.74, },
+[33] = { city_quito, 2022, MONTH_jan, 0.5373635600335852, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.7315789473684211, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.15694768410529825, },
+[36] = { city_accra, 2022, MONTH_jan, 0.5854632587859425, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.2929318068690891, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.8714953271028038, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.48205383848454636, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.215, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.7328308207705193, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.6830870279146142, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.7300813008130081, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.74, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.37493632195618953, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.31785003317850036, },
+};
+global u32 question_14_len = sizeof(question_14_data) / sizeof(question_14_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_15.h b/run_tree/data/incenter_data/c/question_15.h
new file mode 100644
index 0000000..95199c2
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_15.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_15_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.8681318681318682, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.842741935483871, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.75, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.8444444444444444, },
+[4] = { city_paris, 2022, MONTH_jan, 0.7975257342525262, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.8898026315789473, },
+[6] = { city_denver, 2022, MONTH_jan, 0.8898026315789473, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.8466111771700356, },
+[8] = { city_harare, 2022, MONTH_jan, 1, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.8507462686567164, },
+[10] = { city_washington, 2022, MONTH_jan, 0.8898026315789473, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.7692307692307693, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.8653846153846154, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.8507462686567164, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.8838383838383839, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.7934782608695652, },
+[16] = { city_lima, 2022, MONTH_jan, 0.9705882352941176, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.8121827411167513, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.8571428571428571, },
+[19] = { city_managua, 2022, MONTH_jan, 1, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.9004566210045662, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.7352941176470589, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.75, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.8772086117297698, },
+[24] = { city_nairobi, 2022, MONTH_jan, 1, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.41568278012920473, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.8, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.8, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.8562753036437247, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 1, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.8590785907859079, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 1, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.8653846153846154, },
+[33] = { city_quito, 2022, MONTH_jan, 0.8695652173913043, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.8656716417910447, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.75, },
+[36] = { city_accra, 2022, MONTH_jan, 1, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.8402203856749312, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.8950381679389313, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.8950381679389313, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.9041666666666667, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.8466111771700356, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.8898026315789473, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.8636363636363636, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.8, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.8444444444444444, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.8524590163934426, },
+};
+global u32 question_15_len = sizeof(question_15_data) / sizeof(question_15_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_16.h b/run_tree/data/incenter_data/c/question_16.h
new file mode 100644
index 0000000..1bedb2e
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_16.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_16_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.6252144082332761, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.21300448430493274, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.8102633355840648, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.7666836474783495, },
+[4] = { city_paris, 2022, MONTH_jan, 0.1596806387225549, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.23954372623574144, },
+[6] = { city_denver, 2022, MONTH_jan, 0.21940928270042195, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.8744531933508312, },
+[8] = { city_harare, 2022, MONTH_jan, 0.9385004212299916, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.8875, },
+[10] = { city_washington, 2022, MONTH_jan, 0.2643884892086331, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.6720647773279352, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.8076580587711487, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.8481927710843373, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.763023493360572, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.738166566806471, },
+[16] = { city_lima, 2022, MONTH_jan, 0.44288872512896094, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.7666836474783495, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.9618196588139724, },
+[19] = { city_managua, 2022, MONTH_jan, 0.4816666666666667, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.27867095391211144, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.91, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.7175245098039216, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.3929398148148148, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.8004926108374384, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.5046491969568893, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.9116666666666666, },
+[27] = { city_tehran, 2022, MONTH_jan, 1, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.9195402298850575, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.6255178127589064, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.1596806387225549, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.8911055694098088, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.9116666666666666, },
+[33] = { city_quito, 2022, MONTH_jan, 0.4869857262804366, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.4, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.8102633355840648, },
+[36] = { city_accra, 2022, MONTH_jan, 0.8004926108374384, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.20034843205574912, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.1588785046728972, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.43232438606510565, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.9798826487845766, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.8872881355932203, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.2586805555555556, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.7552447552447552, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.9116666666666666, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.7666836474783495, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.1596806387225549, },
+};
+global u32 question_16_len = sizeof(question_16_data) / sizeof(question_16_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_17.h b/run_tree/data/incenter_data/c/question_17.h
new file mode 100644
index 0000000..50f8064
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_17.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_17_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.3517305893358279, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.7251693002257337, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.6752336448598131, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.28718882817243474, },
+[4] = { city_paris, 2022, MONTH_jan, 0.8590694538098449, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.7202881152460985, },
+[6] = { city_denver, 2022, MONTH_jan, 0.7637795275590551, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.22046174739701221, },
+[8] = { city_harare, 2022, MONTH_jan, 0.4797297297297297, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.08583333333333333, },
+[10] = { city_washington, 2022, MONTH_jan, 0.7687074829931972, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.39174560216508797, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.18357933579335795, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.242570281124498, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.23599137931034483, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.3582286847323199, },
+[16] = { city_lima, 2022, MONTH_jan, 0.21016166281755197, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.28718882817243474, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.4920502092050209, },
+[19] = { city_managua, 2022, MONTH_jan, 0.3875, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.5708571428571428, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.28583333333333333, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.5067401960784313, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.1445221445221445, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.5110356536502547, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.6290018832391714, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.475736568457539, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.7332421340629275, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.3570284982388729, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.5788135593220339, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.8590694538098449, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.17217981340118746, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.475736568457539, },
+[33] = { city_quito, 2022, MONTH_jan, 0.4667235494880546, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.6217105263157895, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.6752336448598131, },
+[36] = { city_accra, 2022, MONTH_jan, 0.5110356536502547, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.7775012444001991, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.5541561712846348, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.5235414534288638, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.34421364985163205, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.365832614322692, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.760797342192691, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.28751311647429173, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.475736568457539, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.28718882817243474, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.8590694538098449, },
+};
+global u32 question_17_len = sizeof(question_17_data) / sizeof(question_17_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_18.h b/run_tree/data/incenter_data/c/question_18.h
new file mode 100644
index 0000000..da16a28
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_18.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_18_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.40350877192982454, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.6732617297908423, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.6871880199667221, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.4288, },
+[4] = { city_paris, 2022, MONTH_jan, 0.6646090534979424, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.5142857142857142, },
+[6] = { city_denver, 2022, MONTH_jan, 0.528957528957529, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.5759139784946237, },
+[8] = { city_harare, 2022, MONTH_jan, 0.538971807628524, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.7172643869891576, },
+[10] = { city_washington, 2022, MONTH_jan, 0.5456081081081081, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.5256756756756756, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.34995625546806647, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.5742971887550201, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.46930280957336107, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.48936170212765956, },
+[16] = { city_lima, 2022, MONTH_jan, 0.5945144551519644, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.4288, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.41254125412541254, },
+[19] = { city_managua, 2022, MONTH_jan, 0.6333333333333333, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.7243367935409458, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.5133333333333333, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.5415140415140415, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.5577712609970674, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.4683127572016461, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.5072463768115942, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.43166666666666664, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.6534320323014805, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.733607855559075, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.5899339933993399, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.6646090534979424, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.46716541978387366, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.3588290840415486, },
+[33] = { city_quito, 2022, MONTH_jan, 0.5719207579672696, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.6966887417218544, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.6871880199667221, },
+[36] = { city_accra, 2022, MONTH_jan, 0.4683127572016461, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.6124937779990045, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.644878706199461, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.7341961174713788, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.4765694076038904, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.5004262574595055, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.5481239804241436, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.5114235500878734, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.3588290840415486, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.4288, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.6646090534979424, },
+};
+global u32 question_18_len = sizeof(question_18_data) / sizeof(question_18_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_19.h b/run_tree/data/incenter_data/c/question_19.h
new file mode 100644
index 0000000..2e2ca80
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_19.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_19_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.7082644628099174, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.8521205357142857, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.9380856760374833, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.7483477376715811, },
+[4] = { city_paris, 2022, MONTH_jan, 0.8271523178807947, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.7714285714285715, },
+[6] = { city_denver, 2022, MONTH_jan, 0.7393822393822393, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.735936188077246, },
+[8] = { city_harare, 2022, MONTH_jan, 0.7394190871369295, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.9416666666666667, },
+[10] = { city_washington, 2022, MONTH_jan, 0.8, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.7437457741717377, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.5396825396825397, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.9164658634538153, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.6821782178217822, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.8499717992103779, },
+[16] = { city_lima, 2022, MONTH_jan, 0.5747211895910781, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.7483477376715811, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.6848534201954397, },
+[19] = { city_managua, 2022, MONTH_jan, 0.55, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.8363922391190352, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.82, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.7635919364691509, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.6364685516445471, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.6780876494023904, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.7748344370860927, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.78, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.6942204301075269, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.8313479623824451, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.6363636363636364, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.8271523178807947, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.7589134125636672, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.5408333333333334, },
+[33] = { city_quito, 2022, MONTH_jan, 0.5179916317991632, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.5506578947368421, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.9380856760374833, },
+[36] = { city_accra, 2022, MONTH_jan, 0.6780876494023904, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.8128422100547537, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.5583962812318419, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.5051749630359783, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.9416666666666667, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.6306156405990017, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.7963875205254516, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.830249396621078, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.5408333333333334, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.7483477376715811, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.8271523178807947, },
+};
+global u32 question_19_len = sizeof(question_19_data) / sizeof(question_19_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_2.h b/run_tree/data/incenter_data/c/question_2.h
new file mode 100644
index 0000000..9271e22
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_2.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_2_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.8087649402390438, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.7064846416382252, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.6440677966101694, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.7936507936507936, },
+[4] = { city_paris, 2022, MONTH_jan, 0.6832853946947907, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.814797580269893, },
+[6] = { city_denver, 2022, MONTH_jan, 0.814797580269893, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.870817843866171, },
+[8] = { city_harare, 2022, MONTH_jan, 0.7857142857142857, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.6257668711656442, },
+[10] = { city_washington, 2022, MONTH_jan, 0.814797580269893, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.47058823529411764, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.6571428571428571, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.6257668711656442, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.8, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.8032786885245902, },
+[16] = { city_lima, 2022, MONTH_jan, 0.8, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.7448275862068966, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.9411764705882353, },
+[19] = { city_managua, 2022, MONTH_jan, 0.8125, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.5527714502657555, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.7619047619047619, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.6440677966101694, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.825, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.7857142857142857, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.665843621399177, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.7058823529411765, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.7058823529411765, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.7700145560407569, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.8125, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.6601796407185628, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.7857142857142857, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.6571428571428571, },
+[33] = { city_quito, 2022, MONTH_jan, 0.7241379310344828, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.7722222222222223, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.6440677966101694, },
+[36] = { city_accra, 2022, MONTH_jan, 0.7857142857142857, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.7915690866510539, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.8020833333333334, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.8020833333333334, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.8126721763085399, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.870817843866171, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.814797580269893, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.8125, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.7058823529411765, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.7936507936507936, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.7365591397849462, },
+};
+global u32 question_2_len = sizeof(question_2_data) / sizeof(question_2_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_20.h b/run_tree/data/incenter_data/c/question_20.h
new file mode 100644
index 0000000..e204bd2
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_20.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_20_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.008906882591093117, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.06120157215047726, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.014885874958650347, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.05456570155902005, },
+[4] = { city_paris, 2022, MONTH_jan, 0.07147540983606557, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.06863905325443787, },
+[6] = { city_denver, 2022, MONTH_jan, 0.06395348837209303, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.007591733445803458, },
+[8] = { city_harare, 2022, MONTH_jan, 0.07966804979253111, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.021666666666666667, },
+[10] = { city_washington, 2022, MONTH_jan, 0.05852842809364549, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.13266666666666665, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.009369676320272573, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.01606425702811245, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.014691478942213516, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.0061624649859943975, },
+[16] = { city_lima, 2022, MONTH_jan, 0.023038156947444204, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.05456570155902005, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.09248554913294797, },
+[19] = { city_managua, 2022, MONTH_jan, 0.11166666666666666, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.01694915254237288, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.041666666666666664, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.11233211233211234, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.047756041426927506, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.1770573566084788, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.0037425149700598802, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.037800687285223365, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.06412825651302605, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.2173776662484316, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.06795302013422819, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.07147540983606557, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.11595394736842106, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.0016680567139282735, },
+[33] = { city_quito, 2022, MONTH_jan, 0.05396290050590219, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.1118421052631579, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.014885874958650347, },
+[36] = { city_accra, 2022, MONTH_jan, 0.1770573566084788, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.05301144848183176, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.017452006980802792, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.08312958435207823, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.03836317135549872, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.02875924404272802, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.06188925081433225, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.01675977653631285, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.0016680567139282735, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.05456570155902005, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.07147540983606557, },
+};
+global u32 question_20_len = sizeof(question_20_data) / sizeof(question_20_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_21.h b/run_tree/data/incenter_data/c/question_21.h
new file mode 100644
index 0000000..1941474
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_21.h
@@ -0,0 +1,1951 @@
+static Incenter_Data_Row question_21_data[] = {
+[0] = { city_brisbane, 1965, MONTH_jan, 0.05562621593475342, },
+[1] = { city_brisbane, 1966, MONTH_jan, 0.05071894645690918, },
+[2] = { city_brisbane, 1967, MONTH_jan, 0.049495487213134765, },
+[3] = { city_brisbane, 1968, MONTH_jan, 0.04865467548370361, },
+[4] = { city_brisbane, 1969, MONTH_jan, 0.05086987972259521, },
+[5] = { city_brisbane, 1970, MONTH_jan, 0.05421450614929199, },
+[6] = { city_brisbane, 1971, MONTH_jan, 0.05822727203369141, },
+[7] = { city_brisbane, 1972, MONTH_jan, 0.05599285125732422, },
+[8] = { city_brisbane, 1973, MONTH_jan, 0.055886645317077634, },
+[9] = { city_brisbane, 1974, MONTH_jan, 0.05955109596252441, },
+[10] = { city_brisbane, 1975, MONTH_jan, 0.06297008037567138, },
+[11] = { city_brisbane, 1976, MONTH_jan, 0.05793421268463135, },
+[12] = { city_brisbane, 1977, MONTH_jan, 0.05269774913787842, },
+[13] = { city_brisbane, 1978, MONTH_jan, 0.0550869607925415, },
+[14] = { city_brisbane, 1979, MONTH_jan, 0.051658406257629394, },
+[15] = { city_brisbane, 1980, MONTH_jan, 0.04862271785736084, },
+[16] = { city_brisbane, 1981, MONTH_jan, 0.04930354595184326, },
+[17] = { city_brisbane, 1982, MONTH_jan, 0.046367826461791994, },
+[18] = { city_brisbane, 1983, MONTH_jan, 0.045344815254211426, },
+[19] = { city_brisbane, 1984, MONTH_jan, 0.04718454360961914, },
+[20] = { city_brisbane, 1985, MONTH_jan, 0.04863154888153076, },
+[21] = { city_brisbane, 1986, MONTH_jan, 0.047985596656799315, },
+[22] = { city_brisbane, 1987, MONTH_jan, 0.04532893180847168, },
+[23] = { city_brisbane, 1988, MONTH_jan, 0.046416802406311033, },
+[24] = { city_brisbane, 1989, MONTH_jan, 0.044199533462524414, },
+[25] = { city_brisbane, 1990, MONTH_jan, 0.044593911170959476, },
+[26] = { city_brisbane, 1991, MONTH_jan, 0.047400617599487306, },
+[27] = { city_brisbane, 1992, MONTH_jan, 0.04731717109680176, },
+[28] = { city_brisbane, 1993, MONTH_jan, 0.0475942850112915, },
+[29] = { city_brisbane, 1994, MONTH_jan, 0.045271806716918946, },
+[30] = { city_brisbane, 1995, MONTH_jan, 0.04239725112915039, },
+[31] = { city_brisbane, 1996, MONTH_jan, 0.0423659086227417, },
+[32] = { city_brisbane, 1997, MONTH_jan, 0.04145838737487793, },
+[33] = { city_brisbane, 1998, MONTH_jan, 0.04037755966186524, },
+[34] = { city_brisbane, 1999, MONTH_jan, 0.04008933544158935, },
+[35] = { city_brisbane, 2000, MONTH_jan, 0.03988877773284912, },
+[36] = { city_brisbane, 2001, MONTH_jan, 0.0387076210975647, },
+[37] = { city_brisbane, 2002, MONTH_jan, 0.03931450366973877, },
+[38] = { city_brisbane, 2003, MONTH_jan, 0.040149688720703125, },
+[39] = { city_brisbane, 2004, MONTH_jan, 0.04036560535430908, },
+[40] = { city_brisbane, 2005, MONTH_jan, 0.04313553333282471, },
+[41] = { city_brisbane, 2006, MONTH_jan, 0.04233413219451904, },
+[42] = { city_brisbane, 2007, MONTH_jan, 0.039767227172851555, },
+[43] = { city_brisbane, 2008, MONTH_jan, 0.03681326389312744, },
+[44] = { city_brisbane, 2009, MONTH_jan, 0.03920266151428222, },
+[45] = { city_brisbane, 2010, MONTH_jan, 0.04312804698944092, },
+[46] = { city_brisbane, 2011, MONTH_jan, 0.05698980808258056, },
+[47] = { city_brisbane, 2012, MONTH_jan, 0.05700209140777588, },
+[48] = { city_brisbane, 2013, MONTH_jan, 0.06525868892669678, },
+[49] = { city_brisbane, 2014, MONTH_jan, 0.05979823112487793, },
+[50] = { city_brisbane, 2015, MONTH_jan, 0.06290020942687988, },
+[51] = { city_brisbane, 2016, MONTH_jan, 0.07189175605773926, },
+[52] = { city_brisbane, 2017, MONTH_jan, 0.06750126838684083, },
+[53] = { city_brisbane, 2018, MONTH_jan, 0.08300061225891113, },
+[54] = { city_brisbane, 2019, MONTH_jan, 0.08880722045898437, },
+[55] = { city_brisbane, 2020, MONTH_jan, 0.10789811134338381, },
+[56] = { city_brisbane, 2021, MONTH_jan, 0.12933531761169434, },
+[57] = { city_dhaka, 1971, MONTH_jan, 0.03859757661819458, },
+[58] = { city_dhaka, 1972, MONTH_jan, 0.03406534433364868, },
+[59] = { city_dhaka, 1973, MONTH_jan, 0.050243868827819824, },
+[60] = { city_dhaka, 1974, MONTH_jan, 0.03463459491729736, },
+[61] = { city_dhaka, 1975, MONTH_jan, 0.06043976306915283, },
+[62] = { city_dhaka, 1976, MONTH_jan, 0.05868124008178711, },
+[63] = { city_dhaka, 1977, MONTH_jan, 0.05079775810241699, },
+[64] = { city_dhaka, 1978, MONTH_jan, 0.05536711692810058, },
+[65] = { city_dhaka, 1979, MONTH_jan, 0.05825671672821045, },
+[66] = { city_dhaka, 1980, MONTH_jan, 0.049608802795410155, },
+[67] = { city_dhaka, 1981, MONTH_jan, 0.05016649723052979, },
+[68] = { city_dhaka, 1982, MONTH_jan, 0.03805250644683838, },
+[69] = { city_dhaka, 1983, MONTH_jan, 0.048706588745117185, },
+[70] = { city_dhaka, 1984, MONTH_jan, 0.06105583190917969, },
+[71] = { city_dhaka, 1985, MONTH_jan, 0.04526681423187256, },
+[72] = { city_dhaka, 1986, MONTH_jan, 0.025327975749969478, },
+[73] = { city_dhaka, 1987, MONTH_jan, 0.026365013122558595, },
+[74] = { city_dhaka, 1988, MONTH_jan, 0.030908639430999755, },
+[75] = { city_dhaka, 1989, MONTH_jan, 0.03907864093780518, },
+[76] = { city_dhaka, 1990, MONTH_jan, 0.03539271354675293, },
+[77] = { city_dhaka, 1991, MONTH_jan, 0.03477609157562256, },
+[78] = { city_dhaka, 1992, MONTH_jan, 0.030288853645324708, },
+[79] = { city_dhaka, 1993, MONTH_jan, 0.02104328870773315, },
+[80] = { city_dhaka, 1994, MONTH_jan, 0.02761680603027344, },
+[81] = { city_dhaka, 1995, MONTH_jan, 0.010269947052001951, },
+[82] = { city_dhaka, 1996, MONTH_jan, 0.01947564125061035, },
+[83] = { city_dhaka, 1997, MONTH_jan, 0.018034275770187375, },
+[84] = { city_dhaka, 1998, MONTH_jan, 0.020371236801147462, },
+[85] = { city_dhaka, 1999, MONTH_jan, 0.0193972909450531, },
+[86] = { city_dhaka, 2000, MONTH_jan, 0.016270726919174194, },
+[87] = { city_dhaka, 2001, MONTH_jan, 0.01875003218650818, },
+[88] = { city_dhaka, 2002, MONTH_jan, 0.01362207531929016, },
+[89] = { city_dhaka, 2003, MONTH_jan, 0.012784041166305542, },
+[90] = { city_dhaka, 2004, MONTH_jan, 0.012296963930130005, },
+[91] = { city_dhaka, 2005, MONTH_jan, 0.011353458166122437, },
+[92] = { city_dhaka, 2006, MONTH_jan, 0.010463094711303711, },
+[93] = { city_dhaka, 2007, MONTH_jan, 0.010207374095916748, },
+[94] = { city_dhaka, 2008, MONTH_jan, 0.012218128442764282, },
+[95] = { city_dhaka, 2009, MONTH_jan, 0.0051176011562347416, },
+[96] = { city_dhaka, 2010, MONTH_jan, 0.008648931980133057, },
+[97] = { city_dhaka, 2011, MONTH_jan, 0.009513694643974304, },
+[98] = { city_dhaka, 2012, MONTH_jan, 0.008256486654281615, },
+[99] = { city_dhaka, 2013, MONTH_jan, 0.007885019183158875, },
+[100] = { city_dhaka, 2014, MONTH_jan, 0.0064616197347640994, },
+[101] = { city_dhaka, 2015, MONTH_jan, 0.008184085488319398, },
+[102] = { city_dhaka, 2016, MONTH_jan, 0.008060975074768066, },
+[103] = { city_dhaka, 2017, MONTH_jan, 0.00894644320011139, },
+[104] = { city_dhaka, 2018, MONTH_jan, 0.007471175193786621, },
+[105] = { city_dhaka, 2019, MONTH_jan, 0.00675128698348999, },
+[106] = { city_dhaka, 2020, MONTH_jan, 0.006580818295478821, },
+[107] = { city_dhaka, 2021, MONTH_jan, 0.0065014690160751345, },
+[108] = { city_brasilia, 1965, MONTH_jan, 0.2754872131347656, },
+[109] = { city_brasilia, 1966, MONTH_jan, 0.27618213653564455, },
+[110] = { city_brasilia, 1967, MONTH_jan, 0.27880290985107425, },
+[111] = { city_brasilia, 1968, MONTH_jan, 0.2555575370788575, },
+[112] = { city_brasilia, 1969, MONTH_jan, 0.2505621910095215, },
+[113] = { city_brasilia, 1970, MONTH_jan, 0.2690108108520508, },
+[114] = { city_brasilia, 1971, MONTH_jan, 0.26817771911621097, },
+[115] = { city_brasilia, 1972, MONTH_jan, 0.2739073371887207, },
+[116] = { city_brasilia, 1973, MONTH_jan, 0.2610169982910156, },
+[117] = { city_brasilia, 1974, MONTH_jan, 0.2692813491821289, },
+[118] = { city_brasilia, 1975, MONTH_jan, 0.279958610534668, },
+[119] = { city_brasilia, 1976, MONTH_jan, 0.2916008758544922, },
+[120] = { city_brasilia, 1977, MONTH_jan, 0.3108434295654297, },
+[121] = { city_brasilia, 1978, MONTH_jan, 0.31529354095458983, },
+[122] = { city_brasilia, 1979, MONTH_jan, 0.3338748931884766, },
+[123] = { city_brasilia, 1980, MONTH_jan, 0.3618800735473633, },
+[124] = { city_brasilia, 1981, MONTH_jan, 0.3750487899780273, },
+[125] = { city_brasilia, 1982, MONTH_jan, 0.3909782028198242, },
+[126] = { city_brasilia, 1983, MONTH_jan, 0.41867771148681643, },
+[127] = { city_brasilia, 1984, MONTH_jan, 0.43218948364257814, },
+[128] = { city_brasilia, 1985, MONTH_jan, 0.43929534912109375, },
+[129] = { city_brasilia, 1986, MONTH_jan, 0.4321952438354492, },
+[130] = { city_brasilia, 1987, MONTH_jan, 0.42777210235595703, },
+[131] = { city_brasilia, 1988, MONTH_jan, 0.4401119613647461, },
+[132] = { city_brasilia, 1989, MONTH_jan, 0.44574317932128904, },
+[133] = { city_brasilia, 1990, MONTH_jan, 0.4527548599243164, },
+[134] = { city_brasilia, 1991, MONTH_jan, 0.4586178970336914, },
+[135] = { city_brasilia, 1992, MONTH_jan, 0.44902706146240234, },
+[136] = { city_brasilia, 1993, MONTH_jan, 0.4515962982177734, },
+[137] = { city_brasilia, 1994, MONTH_jan, 0.44565601348876954, },
+[138] = { city_brasilia, 1995, MONTH_jan, 0.4434580993652344, },
+[139] = { city_brasilia, 1996, MONTH_jan, 0.44081958770751956, },
+[140] = { city_brasilia, 1997, MONTH_jan, 0.43489933013916016, },
+[141] = { city_brasilia, 1998, MONTH_jan, 0.43571971893310546, },
+[142] = { city_brasilia, 1999, MONTH_jan, 0.43143802642822265, },
+[143] = { city_brasilia, 2000, MONTH_jan, 0.4328843307495117, },
+[144] = { city_brasilia, 2001, MONTH_jan, 0.39111621856689455, },
+[145] = { city_brasilia, 2002, MONTH_jan, 0.4075665283203125, },
+[146] = { city_brasilia, 2003, MONTH_jan, 0.4228412628173828, },
+[147] = { city_brasilia, 2004, MONTH_jan, 0.4228615570068359, },
+[148] = { city_brasilia, 2005, MONTH_jan, 0.4314569854736328, },
+[149] = { city_brasilia, 2006, MONTH_jan, 0.4304596328735352, },
+[150] = { city_brasilia, 2007, MONTH_jan, 0.4438022613525391, },
+[151] = { city_brasilia, 2008, MONTH_jan, 0.4336677551269531, },
+[152] = { city_brasilia, 2009, MONTH_jan, 0.4630883026123047, },
+[153] = { city_brasilia, 2010, MONTH_jan, 0.4424653625488281, },
+[154] = { city_brasilia, 2011, MONTH_jan, 0.4391006088256836, },
+[155] = { city_brasilia, 2012, MONTH_jan, 0.42066368103027346, },
+[156] = { city_brasilia, 2013, MONTH_jan, 0.39836597442626953, },
+[157] = { city_brasilia, 2014, MONTH_jan, 0.38852745056152344, },
+[158] = { city_brasilia, 2015, MONTH_jan, 0.4060352325439453, },
+[159] = { city_brasilia, 2016, MONTH_jan, 0.43538700103759764, },
+[160] = { city_brasilia, 2017, MONTH_jan, 0.4328067779541016, },
+[161] = { city_brasilia, 2018, MONTH_jan, 0.46080497741699217, },
+[162] = { city_brasilia, 2019, MONTH_jan, 0.4730484390258789, },
+[163] = { city_brasilia, 2020, MONTH_jan, 0.4947188186645508, },
+[164] = { city_brasilia, 2021, MONTH_jan, 0.46219749450683595, },
+[165] = { city_ottawa, 1965, MONTH_jan, 0.2513864898681641, },
+[166] = { city_ottawa, 1966, MONTH_jan, 0.2619474792480469, },
+[167] = { city_ottawa, 1967, MONTH_jan, 0.2561042594909668, },
+[168] = { city_ottawa, 1968, MONTH_jan, 0.24474010467529297, },
+[169] = { city_ottawa, 1969, MONTH_jan, 0.25521268844604494, },
+[170] = { city_ottawa, 1970, MONTH_jan, 0.2502507591247558, },
+[171] = { city_ottawa, 1971, MONTH_jan, 0.24927824020385747, },
+[172] = { city_ottawa, 1972, MONTH_jan, 0.25893644332885746, },
+[173] = { city_ottawa, 1973, MONTH_jan, 0.2555288314819336, },
+[174] = { city_ottawa, 1974, MONTH_jan, 0.2681158256530762, },
+[175] = { city_ottawa, 1975, MONTH_jan, 0.2615631484985352, },
+[176] = { city_ottawa, 1976, MONTH_jan, 0.2633203506469727, },
+[177] = { city_ottawa, 1977, MONTH_jan, 0.25808473587036135, },
+[178] = { city_ottawa, 1978, MONTH_jan, 0.2705876922607422, },
+[179] = { city_ottawa, 1979, MONTH_jan, 0.2729702377319336, },
+[180] = { city_ottawa, 1980, MONTH_jan, 0.2753968620300293, },
+[181] = { city_ottawa, 1981, MONTH_jan, 0.29403928756713865, },
+[182] = { city_ottawa, 1982, MONTH_jan, 0.29416236877441404, },
+[183] = { city_ottawa, 1983, MONTH_jan, 0.30499225616455083, },
+[184] = { city_ottawa, 1984, MONTH_jan, 0.30855842590332033, },
+[185] = { city_ottawa, 1985, MONTH_jan, 0.3161163330078125, },
+[186] = { city_ottawa, 1986, MONTH_jan, 0.32045928955078123, },
+[187] = { city_ottawa, 1987, MONTH_jan, 0.316495418548584, },
+[188] = { city_ottawa, 1988, MONTH_jan, 0.29611337661743165, },
+[189] = { city_ottawa, 1989, MONTH_jan, 0.2781739616394043, },
+[190] = { city_ottawa, 1990, MONTH_jan, 0.29480998992919916, },
+[191] = { city_ottawa, 1991, MONTH_jan, 0.30499427795410156, },
+[192] = { city_ottawa, 1992, MONTH_jan, 0.30512109756469724, },
+[193] = { city_ottawa, 1993, MONTH_jan, 0.3045089340209961, },
+[194] = { city_ottawa, 1994, MONTH_jan, 0.2992730140686035, },
+[195] = { city_ottawa, 1995, MONTH_jan, 0.2988811683654785, },
+[196] = { city_ottawa, 1996, MONTH_jan, 0.3087930679321289, },
+[197] = { city_ottawa, 1997, MONTH_jan, 0.3028490447998047, },
+[198] = { city_ottawa, 1998, MONTH_jan, 0.29307371139526367, },
+[199] = { city_ottawa, 1999, MONTH_jan, 0.29588794708251953, },
+[200] = { city_ottawa, 2000, MONTH_jan, 0.297678165435791, },
+[201] = { city_ottawa, 2001, MONTH_jan, 0.282014102935791, },
+[202] = { city_ottawa, 2002, MONTH_jan, 0.2854655647277832, },
+[203] = { city_ottawa, 2003, MONTH_jan, 0.2718001365661621, },
+[204] = { city_ottawa, 2004, MONTH_jan, 0.2682792282104492, },
+[205] = { city_ottawa, 2005, MONTH_jan, 0.2826547622680664, },
+[206] = { city_ottawa, 2006, MONTH_jan, 0.2761646842956543, },
+[207] = { city_ottawa, 2007, MONTH_jan, 0.2769431495666504, },
+[208] = { city_ottawa, 2008, MONTH_jan, 0.2859009170532227, },
+[209] = { city_ottawa, 2009, MONTH_jan, 0.2920944976806641, },
+[210] = { city_ottawa, 2010, MONTH_jan, 0.27789688110351557, },
+[211] = { city_ottawa, 2011, MONTH_jan, 0.2862636947631836, },
+[212] = { city_ottawa, 2012, MONTH_jan, 0.2897691345214844, },
+[213] = { city_ottawa, 2013, MONTH_jan, 0.2894182777404785, },
+[214] = { city_ottawa, 2014, MONTH_jan, 0.2814776039123535, },
+[215] = { city_ottawa, 2015, MONTH_jan, 0.2876414680480957, },
+[216] = { city_ottawa, 2016, MONTH_jan, 0.29652599334716795, },
+[217] = { city_ottawa, 2017, MONTH_jan, 0.2967363929748535, },
+[218] = { city_ottawa, 2018, MONTH_jan, 0.28814517974853515, },
+[219] = { city_ottawa, 2019, MONTH_jan, 0.2858637619018555, },
+[220] = { city_ottawa, 2020, MONTH_jan, 0.3054215049743652, },
+[221] = { city_ottawa, 2021, MONTH_jan, 0.29888439178466797, },
+[222] = { city_chengdu, 1965, MONTH_jan, 0.042534070014953615, },
+[223] = { city_beijing, 1965, MONTH_jan, 0.042534070014953615, },
+[224] = { city_chengdu, 1966, MONTH_jan, 0.039831228256225586, },
+[225] = { city_beijing, 1966, MONTH_jan, 0.039831228256225586, },
+[226] = { city_chengdu, 1967, MONTH_jan, 0.038183979988098145, },
+[227] = { city_beijing, 1967, MONTH_jan, 0.038183979988098145, },
+[228] = { city_chengdu, 1968, MONTH_jan, 0.04458870887756348, },
+[229] = { city_beijing, 1968, MONTH_jan, 0.04458870887756348, },
+[230] = { city_chengdu, 1969, MONTH_jan, 0.03727402210235596, },
+[231] = { city_beijing, 1969, MONTH_jan, 0.03727402210235596, },
+[232] = { city_chengdu, 1970, MONTH_jan, 0.03009592294692993, },
+[233] = { city_beijing, 1970, MONTH_jan, 0.03009592294692993, },
+[234] = { city_chengdu, 1971, MONTH_jan, 0.030704047679901123, },
+[235] = { city_beijing, 1971, MONTH_jan, 0.030704047679901123, },
+[236] = { city_chengdu, 1972, MONTH_jan, 0.0322759747505188, },
+[237] = { city_beijing, 1972, MONTH_jan, 0.0322759747505188, },
+[238] = { city_chengdu, 1973, MONTH_jan, 0.03417015075683594, },
+[239] = { city_beijing, 1973, MONTH_jan, 0.03417015075683594, },
+[240] = { city_chengdu, 1974, MONTH_jan, 0.03749354600906372, },
+[241] = { city_beijing, 1974, MONTH_jan, 0.03749354600906372, },
+[242] = { city_chengdu, 1975, MONTH_jan, 0.035038237571716306, },
+[243] = { city_beijing, 1975, MONTH_jan, 0.035038237571716306, },
+[244] = { city_chengdu, 1976, MONTH_jan, 0.03372568368911743, },
+[245] = { city_beijing, 1976, MONTH_jan, 0.03372568368911743, },
+[246] = { city_chengdu, 1977, MONTH_jan, 0.03228713989257812, },
+[247] = { city_beijing, 1977, MONTH_jan, 0.03228713989257812, },
+[248] = { city_chengdu, 1978, MONTH_jan, 0.027594637870788575, },
+[249] = { city_beijing, 1978, MONTH_jan, 0.027594637870788575, },
+[250] = { city_chengdu, 1979, MONTH_jan, 0.03008899211883545, },
+[251] = { city_beijing, 1979, MONTH_jan, 0.03008899211883545, },
+[252] = { city_chengdu, 1980, MONTH_jan, 0.035661976337432864, },
+[253] = { city_beijing, 1980, MONTH_jan, 0.035661976337432864, },
+[254] = { city_chengdu, 1981, MONTH_jan, 0.040664968490600584, },
+[255] = { city_beijing, 1981, MONTH_jan, 0.040664968490600584, },
+[256] = { city_chengdu, 1982, MONTH_jan, 0.044212722778320314, },
+[257] = { city_beijing, 1982, MONTH_jan, 0.044212722778320314, },
+[258] = { city_chengdu, 1983, MONTH_jan, 0.048234906196594235, },
+[259] = { city_beijing, 1983, MONTH_jan, 0.048234906196594235, },
+[260] = { city_chengdu, 1984, MONTH_jan, 0.045154547691345213, },
+[261] = { city_beijing, 1984, MONTH_jan, 0.045154547691345213, },
+[262] = { city_chengdu, 1985, MONTH_jan, 0.04441830158233642, },
+[263] = { city_beijing, 1985, MONTH_jan, 0.04441830158233642, },
+[264] = { city_chengdu, 1986, MONTH_jan, 0.043385276794433596, },
+[265] = { city_beijing, 1986, MONTH_jan, 0.043385276794433596, },
+[266] = { city_chengdu, 1987, MONTH_jan, 0.04255547523498535, },
+[267] = { city_beijing, 1987, MONTH_jan, 0.04255547523498535, },
+[268] = { city_chengdu, 1988, MONTH_jan, 0.043230724334716794, },
+[269] = { city_beijing, 1988, MONTH_jan, 0.043230724334716794, },
+[270] = { city_chengdu, 1989, MONTH_jan, 0.044680271148681644, },
+[271] = { city_beijing, 1989, MONTH_jan, 0.044680271148681644, },
+[272] = { city_chengdu, 1990, MONTH_jan, 0.04723667621612549, },
+[273] = { city_beijing, 1990, MONTH_jan, 0.04723667621612549, },
+[274] = { city_chengdu, 1991, MONTH_jan, 0.04424777984619141, },
+[275] = { city_beijing, 1991, MONTH_jan, 0.04424777984619141, },
+[276] = { city_chengdu, 1992, MONTH_jan, 0.04412701606750488, },
+[277] = { city_beijing, 1992, MONTH_jan, 0.04412701606750488, },
+[278] = { city_chengdu, 1993, MONTH_jan, 0.04757860660552979, },
+[279] = { city_beijing, 1993, MONTH_jan, 0.04757860660552979, },
+[280] = { city_chengdu, 1994, MONTH_jan, 0.04952474117279053, },
+[281] = { city_beijing, 1994, MONTH_jan, 0.04952474117279053, },
+[282] = { city_chengdu, 1995, MONTH_jan, 0.05553820133209229, },
+[283] = { city_beijing, 1995, MONTH_jan, 0.05553820133209229, },
+[284] = { city_chengdu, 1996, MONTH_jan, 0.05143133163452149, },
+[285] = { city_beijing, 1996, MONTH_jan, 0.05143133163452149, },
+[286] = { city_chengdu, 1997, MONTH_jan, 0.05361733913421631, },
+[287] = { city_beijing, 1997, MONTH_jan, 0.05361733913421631, },
+[288] = { city_chengdu, 1998, MONTH_jan, 0.054277782440185544, },
+[289] = { city_beijing, 1998, MONTH_jan, 0.054277782440185544, },
+[290] = { city_chengdu, 1999, MONTH_jan, 0.05188285827636719, },
+[291] = { city_beijing, 1999, MONTH_jan, 0.05188285827636719, },
+[292] = { city_chengdu, 2000, MONTH_jan, 0.05656925201416016, },
+[293] = { city_beijing, 2000, MONTH_jan, 0.05656925201416016, },
+[294] = { city_chengdu, 2001, MONTH_jan, 0.06618624210357665, },
+[295] = { city_beijing, 2001, MONTH_jan, 0.06618624210357665, },
+[296] = { city_chengdu, 2002, MONTH_jan, 0.0628048849105835, },
+[297] = { city_beijing, 2002, MONTH_jan, 0.0628048849105835, },
+[298] = { city_chengdu, 2003, MONTH_jan, 0.05301109313964844, },
+[299] = { city_beijing, 2003, MONTH_jan, 0.05301109313964844, },
+[300] = { city_chengdu, 2004, MONTH_jan, 0.05600940227508545, },
+[301] = { city_beijing, 2004, MONTH_jan, 0.05600940227508545, },
+[302] = { city_chengdu, 2005, MONTH_jan, 0.05554481029510498, },
+[303] = { city_beijing, 2005, MONTH_jan, 0.05554481029510498, },
+[304] = { city_chengdu, 2006, MONTH_jan, 0.055779824256896975, },
+[305] = { city_beijing, 2006, MONTH_jan, 0.055779824256896975, },
+[306] = { city_chengdu, 2007, MONTH_jan, 0.0571853494644165, },
+[307] = { city_beijing, 2007, MONTH_jan, 0.0571853494644165, },
+[308] = { city_chengdu, 2008, MONTH_jan, 0.07268667221069336, },
+[309] = { city_beijing, 2008, MONTH_jan, 0.07268667221069336, },
+[310] = { city_chengdu, 2009, MONTH_jan, 0.06935531616210938, },
+[311] = { city_beijing, 2009, MONTH_jan, 0.06935531616210938, },
+[312] = { city_chengdu, 2010, MONTH_jan, 0.07615997791290283, },
+[313] = { city_beijing, 2010, MONTH_jan, 0.07615997791290283, },
+[314] = { city_chengdu, 2011, MONTH_jan, 0.07084204196929932, },
+[315] = { city_beijing, 2011, MONTH_jan, 0.07084204196929932, },
+[316] = { city_chengdu, 2012, MONTH_jan, 0.08522597312927246, },
+[317] = { city_beijing, 2012, MONTH_jan, 0.08522597312927246, },
+[318] = { city_chengdu, 2013, MONTH_jan, 0.08959421157836914, },
+[319] = { city_beijing, 2013, MONTH_jan, 0.08959421157836914, },
+[320] = { city_chengdu, 2014, MONTH_jan, 0.10197330474853515, },
+[321] = { city_beijing, 2014, MONTH_jan, 0.10197330474853515, },
+[322] = { city_chengdu, 2015, MONTH_jan, 0.10787498474121093, },
+[323] = { city_beijing, 2015, MONTH_jan, 0.10787498474121093, },
+[324] = { city_chengdu, 2016, MONTH_jan, 0.11530946731567385, },
+[325] = { city_beijing, 2016, MONTH_jan, 0.11530946731567385, },
+[326] = { city_chengdu, 2017, MONTH_jan, 0.12138820648193359, },
+[327] = { city_beijing, 2017, MONTH_jan, 0.12138820648193359, },
+[328] = { city_chengdu, 2018, MONTH_jan, 0.12807393074035645, },
+[329] = { city_beijing, 2018, MONTH_jan, 0.12807393074035645, },
+[330] = { city_chengdu, 2019, MONTH_jan, 0.1351595973968506, },
+[331] = { city_beijing, 2019, MONTH_jan, 0.1351595973968506, },
+[332] = { city_chengdu, 2020, MONTH_jan, 0.1424287223815918, },
+[333] = { city_beijing, 2020, MONTH_jan, 0.1424287223815918, },
+[334] = { city_chengdu, 2021, MONTH_jan, 0.14946244239807127, },
+[335] = { city_beijing, 2021, MONTH_jan, 0.14946244239807127, },
+[336] = { city_bogota, 1965, MONTH_jan, 0.11788208961486817, },
+[337] = { city_bogota, 1966, MONTH_jan, 0.12017394065856933, },
+[338] = { city_bogota, 1967, MONTH_jan, 0.12580106735229493, },
+[339] = { city_bogota, 1968, MONTH_jan, 0.13300461769104005, },
+[340] = { city_bogota, 1969, MONTH_jan, 0.1452613925933838, },
+[341] = { city_bogota, 1970, MONTH_jan, 0.14817516326904298, },
+[342] = { city_bogota, 1971, MONTH_jan, 0.15987798690795899, },
+[343] = { city_bogota, 1972, MONTH_jan, 0.15767953872680665, },
+[344] = { city_bogota, 1973, MONTH_jan, 0.17170427322387696, },
+[345] = { city_bogota, 1974, MONTH_jan, 0.17395509719848634, },
+[346] = { city_bogota, 1975, MONTH_jan, 0.19235607147216796, },
+[347] = { city_bogota, 1976, MONTH_jan, 0.1940712547302246, },
+[348] = { city_bogota, 1977, MONTH_jan, 0.1936701011657715, },
+[349] = { city_bogota, 1978, MONTH_jan, 0.20862884521484376, },
+[350] = { city_bogota, 1979, MONTH_jan, 0.2154510498046875, },
+[351] = { city_bogota, 1980, MONTH_jan, 0.23044971466064454, },
+[352] = { city_bogota, 1981, MONTH_jan, 0.2221888542175293, },
+[353] = { city_bogota, 1982, MONTH_jan, 0.22697723388671875, },
+[354] = { city_bogota, 1983, MONTH_jan, 0.2179018783569336, },
+[355] = { city_bogota, 1984, MONTH_jan, 0.23639833450317382, },
+[356] = { city_bogota, 1985, MONTH_jan, 0.2444196128845215, },
+[357] = { city_bogota, 1986, MONTH_jan, 0.27276325225830084, },
+[358] = { city_bogota, 1987, MONTH_jan, 0.2731085395812988, },
+[359] = { city_bogota, 1988, MONTH_jan, 0.28748077392578125, },
+[360] = { city_bogota, 1989, MONTH_jan, 0.29793998718261716, },
+[361] = { city_bogota, 1990, MONTH_jan, 0.32640933990478516, },
+[362] = { city_bogota, 1991, MONTH_jan, 0.3169980049133301, },
+[363] = { city_bogota, 1992, MONTH_jan, 0.2460545539855957, },
+[364] = { city_bogota, 1993, MONTH_jan, 0.27847396850585937, },
+[365] = { city_bogota, 1994, MONTH_jan, 0.2980908203125, },
+[366] = { city_bogota, 1995, MONTH_jan, 0.2933157730102539, },
+[367] = { city_bogota, 1996, MONTH_jan, 0.31475914001464844, },
+[368] = { city_bogota, 1997, MONTH_jan, 0.27334064483642573, },
+[369] = { city_bogota, 1998, MONTH_jan, 0.26691463470458987, },
+[370] = { city_bogota, 1999, MONTH_jan, 0.3137824821472168, },
+[371] = { city_bogota, 2000, MONTH_jan, 0.29556066513061524, },
+[372] = { city_bogota, 2001, MONTH_jan, 0.3060448455810547, },
+[373] = { city_bogota, 2002, MONTH_jan, 0.32692554473876956, },
+[374] = { city_bogota, 2003, MONTH_jan, 0.3338913345336914, },
+[375] = { city_bogota, 2004, MONTH_jan, 0.3450545501708984, },
+[376] = { city_bogota, 2005, MONTH_jan, 0.35053531646728514, },
+[377] = { city_bogota, 2006, MONTH_jan, 0.34142719268798827, },
+[378] = { city_bogota, 2007, MONTH_jan, 0.35182762145996094, },
+[379] = { city_bogota, 2008, MONTH_jan, 0.33941410064697264, },
+[380] = { city_bogota, 2009, MONTH_jan, 0.31377744674682617, },
+[381] = { city_bogota, 2010, MONTH_jan, 0.30092037200927735, },
+[382] = { city_bogota, 2011, MONTH_jan, 0.34515674591064455, },
+[383] = { city_bogota, 2012, MONTH_jan, 0.3174613380432129, },
+[384] = { city_bogota, 2013, MONTH_jan, 0.29385107040405273, },
+[385] = { city_bogota, 2014, MONTH_jan, 0.2836468505859375, },
+[386] = { city_bogota, 2015, MONTH_jan, 0.280216178894043, },
+[387] = { city_bogota, 2016, MONTH_jan, 0.2770881271362305, },
+[388] = { city_bogota, 2017, MONTH_jan, 0.3282905960083008, },
+[389] = { city_bogota, 2018, MONTH_jan, 0.31999549865722654, },
+[390] = { city_bogota, 2019, MONTH_jan, 0.29890342712402346, },
+[391] = { city_bogota, 2020, MONTH_jan, 0.3077278709411621, },
+[392] = { city_bogota, 2021, MONTH_jan, 0.33020408630371095, },
+[393] = { city_quito, 1965, MONTH_jan, 0.0850521469116211, },
+[394] = { city_quito, 1966, MONTH_jan, 0.0924820613861084, },
+[395] = { city_quito, 1967, MONTH_jan, 0.09417126655578612, },
+[396] = { city_quito, 1968, MONTH_jan, 0.08330953598022461, },
+[397] = { city_quito, 1969, MONTH_jan, 0.08701672554016113, },
+[398] = { city_quito, 1970, MONTH_jan, 0.08106535911560059, },
+[399] = { city_quito, 1971, MONTH_jan, 0.08015813827514648, },
+[400] = { city_quito, 1972, MONTH_jan, 0.07758965969085693, },
+[401] = { city_quito, 1973, MONTH_jan, 0.07086233615875244, },
+[402] = { city_quito, 1974, MONTH_jan, 0.06978053569793702, },
+[403] = { city_quito, 1975, MONTH_jan, 0.08653943061828613, },
+[404] = { city_quito, 1976, MONTH_jan, 0.07572480201721192, },
+[405] = { city_quito, 1977, MONTH_jan, 0.05873260498046875, },
+[406] = { city_quito, 1978, MONTH_jan, 0.07960052967071533, },
+[407] = { city_quito, 1979, MONTH_jan, 0.0685176420211792, },
+[408] = { city_quito, 1980, MONTH_jan, 0.06642500877380371, },
+[409] = { city_quito, 1981, MONTH_jan, 0.05350114345550537, },
+[410] = { city_quito, 1982, MONTH_jan, 0.0559368371963501, },
+[411] = { city_quito, 1983, MONTH_jan, 0.10982114791870115, },
+[412] = { city_quito, 1984, MONTH_jan, 0.18537538528442382, },
+[413] = { city_quito, 1985, MONTH_jan, 0.15903298377990724, },
+[414] = { city_quito, 1986, MONTH_jan, 0.18563940048217772, },
+[415] = { city_quito, 1987, MONTH_jan, 0.2051675033569336, },
+[416] = { city_quito, 1988, MONTH_jan, 0.18759294509887695, },
+[417] = { city_quito, 1989, MONTH_jan, 0.1867353248596191, },
+[418] = { city_quito, 1990, MONTH_jan, 0.18208444595336912, },
+[419] = { city_quito, 1991, MONTH_jan, 0.16781824111938476, },
+[420] = { city_quito, 1992, MONTH_jan, 0.1652834701538086, },
+[421] = { city_quito, 1993, MONTH_jan, 0.18627613067626952, },
+[422] = { city_quito, 1994, MONTH_jan, 0.19351001739501952, },
+[423] = { city_quito, 1995, MONTH_jan, 0.160936336517334, },
+[424] = { city_quito, 1996, MONTH_jan, 0.17381893157958983, },
+[425] = { city_quito, 1997, MONTH_jan, 0.16071613311767577, },
+[426] = { city_quito, 1998, MONTH_jan, 0.15920068740844728, },
+[427] = { city_quito, 1999, MONTH_jan, 0.19251895904541017, },
+[428] = { city_quito, 2000, MONTH_jan, 0.21613927841186523, },
+[429] = { city_quito, 2001, MONTH_jan, 0.19692354202270507, },
+[430] = { city_quito, 2002, MONTH_jan, 0.21095178604125978, },
+[431] = { city_quito, 2003, MONTH_jan, 0.19533443450927734, },
+[432] = { city_quito, 2004, MONTH_jan, 0.1873490524291992, },
+[433] = { city_quito, 2005, MONTH_jan, 0.1659660530090332, },
+[434] = { city_quito, 2006, MONTH_jan, 0.16251609802246095, },
+[435] = { city_quito, 2007, MONTH_jan, 0.1955727195739746, },
+[436] = { city_quito, 2008, MONTH_jan, 0.22617521286010744, },
+[437] = { city_quito, 2009, MONTH_jan, 0.18929243087768555, },
+[438] = { city_quito, 2010, MONTH_jan, 0.15936485290527344, },
+[439] = { city_quito, 2011, MONTH_jan, 0.19198841094970703, },
+[440] = { city_quito, 2012, MONTH_jan, 0.19840085983276368, },
+[441] = { city_quito, 2013, MONTH_jan, 0.1737746238708496, },
+[442] = { city_quito, 2014, MONTH_jan, 0.1727614212036133, },
+[443] = { city_quito, 2015, MONTH_jan, 0.19640232086181642, },
+[444] = { city_quito, 2016, MONTH_jan, 0.2362802505493164, },
+[445] = { city_quito, 2017, MONTH_jan, 0.2833585739135743, },
+[446] = { city_quito, 2018, MONTH_jan, 0.2738693428039551, },
+[447] = { city_quito, 2019, MONTH_jan, 0.31769020080566407, },
+[448] = { city_quito, 2020, MONTH_jan, 0.3603577423095703, },
+[449] = { city_quito, 2021, MONTH_jan, 0.32354198455810546, },
+[450] = { city_cairo, 1965, MONTH_jan, 0.055878944396972656, },
+[451] = { city_cairo, 1966, MONTH_jan, 0.055950436592102054, },
+[452] = { city_cairo, 1967, MONTH_jan, 0.07041625022888183, },
+[453] = { city_cairo, 1968, MONTH_jan, 0.09956792831420898, },
+[454] = { city_cairo, 1969, MONTH_jan, 0.16082429885864258, },
+[455] = { city_cairo, 1970, MONTH_jan, 0.1516463565826416, },
+[456] = { city_cairo, 1971, MONTH_jan, 0.1587799835205078, },
+[457] = { city_cairo, 1972, MONTH_jan, 0.14635689735412596, },
+[458] = { city_cairo, 1973, MONTH_jan, 0.15458051681518556, },
+[459] = { city_cairo, 1974, MONTH_jan, 0.16143081665039063, },
+[460] = { city_cairo, 1975, MONTH_jan, 0.16160579681396484, },
+[461] = { city_cairo, 1976, MONTH_jan, 0.15923643112182614, },
+[462] = { city_cairo, 1977, MONTH_jan, 0.16571006774902344, },
+[463] = { city_cairo, 1978, MONTH_jan, 0.17237102508544921, },
+[464] = { city_cairo, 1979, MONTH_jan, 0.15267534255981444, },
+[465] = { city_cairo, 1980, MONTH_jan, 0.13694029808044433, },
+[466] = { city_cairo, 1981, MONTH_jan, 0.12526339530944824, },
+[467] = { city_cairo, 1982, MONTH_jan, 0.11533038139343262, },
+[468] = { city_cairo, 1983, MONTH_jan, 0.09980415344238279, },
+[469] = { city_cairo, 1984, MONTH_jan, 0.08979602813720704, },
+[470] = { city_cairo, 1985, MONTH_jan, 0.08225811958312988, },
+[471] = { city_cairo, 1986, MONTH_jan, 0.07888433456420899, },
+[472] = { city_cairo, 1987, MONTH_jan, 0.0743703556060791, },
+[473] = { city_cairo, 1988, MONTH_jan, 0.07214404582977295, },
+[474] = { city_cairo, 1989, MONTH_jan, 0.07353952407836914, },
+[475] = { city_cairo, 1990, MONTH_jan, 0.07520865440368653, },
+[476] = { city_cairo, 1991, MONTH_jan, 0.07396338939666748, },
+[477] = { city_cairo, 1992, MONTH_jan, 0.07380685806274415, },
+[478] = { city_cairo, 1993, MONTH_jan, 0.07595428943634033, },
+[479] = { city_cairo, 1994, MONTH_jan, 0.07676783084869385, },
+[480] = { city_cairo, 1995, MONTH_jan, 0.07558946132659912, },
+[481] = { city_cairo, 1996, MONTH_jan, 0.07361753940582276, },
+[482] = { city_cairo, 1997, MONTH_jan, 0.07243848800659179, },
+[483] = { city_cairo, 1998, MONTH_jan, 0.07154178619384766, },
+[484] = { city_cairo, 1999, MONTH_jan, 0.07409151077270508, },
+[485] = { city_cairo, 2000, MONTH_jan, 0.07416068077087402, },
+[486] = { city_cairo, 2001, MONTH_jan, 0.07225909233093261, },
+[487] = { city_cairo, 2002, MONTH_jan, 0.06795775890350342, },
+[488] = { city_cairo, 2003, MONTH_jan, 0.059376978874206544, },
+[489] = { city_cairo, 2004, MONTH_jan, 0.05634644031524658, },
+[490] = { city_cairo, 2005, MONTH_jan, 0.05385705947875977, },
+[491] = { city_cairo, 2006, MONTH_jan, 0.05172313690185547, },
+[492] = { city_cairo, 2007, MONTH_jan, 0.053631553649902346, },
+[493] = { city_cairo, 2008, MONTH_jan, 0.05414237022399902, },
+[494] = { city_cairo, 2009, MONTH_jan, 0.04800594806671143, },
+[495] = { city_cairo, 2010, MONTH_jan, 0.04461719036102295, },
+[496] = { city_cairo, 2011, MONTH_jan, 0.04331544399261475, },
+[497] = { city_cairo, 2012, MONTH_jan, 0.041802897453308105, },
+[498] = { city_cairo, 2013, MONTH_jan, 0.04177184581756592, },
+[499] = { city_cairo, 2014, MONTH_jan, 0.04181652545928955, },
+[500] = { city_cairo, 2015, MONTH_jan, 0.043288531303405764, },
+[501] = { city_cairo, 2016, MONTH_jan, 0.041314678192138674, },
+[502] = { city_cairo, 2017, MONTH_jan, 0.038699905872344974, },
+[503] = { city_cairo, 2018, MONTH_jan, 0.04055405616760254, },
+[504] = { city_cairo, 2019, MONTH_jan, 0.05169897556304932, },
+[505] = { city_cairo, 2020, MONTH_jan, 0.06564623832702637, },
+[506] = { city_cairo, 2021, MONTH_jan, 0.06230570793151855, },
+[507] = { city_paris, 1965, MONTH_jan, 0.10584983825683594, },
+[508] = { city_paris, 1966, MONTH_jan, 0.1160239315032959, },
+[509] = { city_paris, 1967, MONTH_jan, 0.09537199020385742, },
+[510] = { city_paris, 1968, MONTH_jan, 0.10092880249023438, },
+[511] = { city_paris, 1969, MONTH_jan, 0.09634167671203611, },
+[512] = { city_paris, 1970, MONTH_jan, 0.09537435531616213, },
+[513] = { city_paris, 1971, MONTH_jan, 0.07942842483520508, },
+[514] = { city_paris, 1972, MONTH_jan, 0.07446317195892334, },
+[515] = { city_paris, 1973, MONTH_jan, 0.06709061622619629, },
+[516] = { city_paris, 1974, MONTH_jan, 0.08016220092773438, },
+[517] = { city_paris, 1975, MONTH_jan, 0.09053054809570313, },
+[518] = { city_paris, 1976, MONTH_jan, 0.06899677753448487, },
+[519] = { city_paris, 1977, MONTH_jan, 0.1059511947631836, },
+[520] = { city_paris, 1978, MONTH_jan, 0.09109526634216308, },
+[521] = { city_paris, 1979, MONTH_jan, 0.08611678123474122, },
+[522] = { city_paris, 1980, MONTH_jan, 0.09114398002624512, },
+[523] = { city_paris, 1981, MONTH_jan, 0.09551752090454102, },
+[524] = { city_paris, 1982, MONTH_jan, 0.09705157279968261, },
+[525] = { city_paris, 1983, MONTH_jan, 0.09325616836547851, },
+[526] = { city_paris, 1984, MONTH_jan, 0.08554503440856934, },
+[527] = { city_paris, 1985, MONTH_jan, 0.07887791156768799, },
+[528] = { city_paris, 1986, MONTH_jan, 0.07764792442321777, },
+[529] = { city_paris, 1987, MONTH_jan, 0.0858464527130127, },
+[530] = { city_paris, 1988, MONTH_jan, 0.09174312591552734, },
+[531] = { city_paris, 1989, MONTH_jan, 0.055997366905212405, },
+[532] = { city_paris, 1990, MONTH_jan, 0.06222640991210938, },
+[533] = { city_paris, 1991, MONTH_jan, 0.0626654052734375, },
+[534] = { city_paris, 1992, MONTH_jan, 0.07399243831634522, },
+[535] = { city_paris, 1993, MONTH_jan, 0.06968883514404296, },
+[536] = { city_paris, 1994, MONTH_jan, 0.08563506126403808, },
+[537] = { city_paris, 1995, MONTH_jan, 0.07823903083801269, },
+[538] = { city_paris, 1996, MONTH_jan, 0.06882894039154053, },
+[539] = { city_paris, 1997, MONTH_jan, 0.0680388879776001, },
+[540] = { city_paris, 1998, MONTH_jan, 0.06470213890075684, },
+[541] = { city_paris, 1999, MONTH_jan, 0.07388863086700441, },
+[542] = { city_paris, 2000, MONTH_jan, 0.06744560718536377, },
+[543] = { city_paris, 2001, MONTH_jan, 0.07405366897583007, },
+[544] = { city_paris, 2002, MONTH_jan, 0.06175621032714844, },
+[545] = { city_paris, 2003, MONTH_jan, 0.059621820449829104, },
+[546] = { city_paris, 2004, MONTH_jan, 0.05970314979553223, },
+[547] = { city_paris, 2005, MONTH_jan, 0.05365334033966065, },
+[548] = { city_paris, 2006, MONTH_jan, 0.059697318077087405, },
+[549] = { city_paris, 2007, MONTH_jan, 0.06638713359832764, },
+[550] = { city_paris, 2008, MONTH_jan, 0.07632267475128174, },
+[551] = { city_paris, 2009, MONTH_jan, 0.0770644998550415, },
+[552] = { city_paris, 2010, MONTH_jan, 0.08220292091369628, },
+[553] = { city_paris, 2011, MONTH_jan, 0.0718551778793335, },
+[554] = { city_paris, 2012, MONTH_jan, 0.0913736343383789, },
+[555] = { city_paris, 2013, MONTH_jan, 0.10459434509277343, },
+[556] = { city_paris, 2014, MONTH_jan, 0.1045927906036377, },
+[557] = { city_paris, 2015, MONTH_jan, 0.10147398948669434, },
+[558] = { city_paris, 2016, MONTH_jan, 0.1097524642944336, },
+[559] = { city_paris, 2017, MONTH_jan, 0.10437264442443847, },
+[560] = { city_paris, 2018, MONTH_jan, 0.12132494926452636, },
+[561] = { city_paris, 2019, MONTH_jan, 0.12384904861450195, },
+[562] = { city_paris, 2020, MONTH_jan, 0.1478874683380127, },
+[563] = { city_paris, 2021, MONTH_jan, 0.13671010971069336, },
+[564] = { city_berlin, 1965, MONTH_jan, 0.016145030260086058, },
+[565] = { city_berlin, 1966, MONTH_jan, 0.01741612911224365, },
+[566] = { city_berlin, 1967, MONTH_jan, 0.01658253788948059, },
+[567] = { city_berlin, 1968, MONTH_jan, 0.01576139807701111, },
+[568] = { city_berlin, 1969, MONTH_jan, 0.011857671737670899, },
+[569] = { city_berlin, 1970, MONTH_jan, 0.015171291828155518, },
+[570] = { city_berlin, 1971, MONTH_jan, 0.01179680585861206, },
+[571] = { city_berlin, 1972, MONTH_jan, 0.011219959259033203, },
+[572] = { city_berlin, 1973, MONTH_jan, 0.012137141227722168, },
+[573] = { city_berlin, 1974, MONTH_jan, 0.014188613891601563, },
+[574] = { city_berlin, 1975, MONTH_jan, 0.014353294372558594, },
+[575] = { city_berlin, 1976, MONTH_jan, 0.011208523511886597, },
+[576] = { city_berlin, 1977, MONTH_jan, 0.013967294692993164, },
+[577] = { city_berlin, 1978, MONTH_jan, 0.01408961057662964, },
+[578] = { city_berlin, 1979, MONTH_jan, 0.013454017639160156, },
+[579] = { city_berlin, 1980, MONTH_jan, 0.01478190541267395, },
+[580] = { city_berlin, 1981, MONTH_jan, 0.015739785432815553, },
+[581] = { city_berlin, 1982, MONTH_jan, 0.01617851257324219, },
+[582] = { city_berlin, 1983, MONTH_jan, 0.014992613792419434, },
+[583] = { city_berlin, 1984, MONTH_jan, 0.0143718421459198, },
+[584] = { city_berlin, 1985, MONTH_jan, 0.013216147422790528, },
+[585] = { city_berlin, 1986, MONTH_jan, 0.014105298519134519, },
+[586] = { city_berlin, 1987, MONTH_jan, 0.015094993114471435, },
+[587] = { city_berlin, 1988, MONTH_jan, 0.015009332895278931, },
+[588] = { city_berlin, 1989, MONTH_jan, 0.013991726636886597, },
+[589] = { city_berlin, 1990, MONTH_jan, 0.013369400501251221, },
+[590] = { city_berlin, 1991, MONTH_jan, 0.012709577083587647, },
+[591] = { city_berlin, 1992, MONTH_jan, 0.015204551219940186, },
+[592] = { city_berlin, 1993, MONTH_jan, 0.01583544135093689, },
+[593] = { city_berlin, 1994, MONTH_jan, 0.01739633440971374, },
+[594] = { city_berlin, 1995, MONTH_jan, 0.01889189839363098, },
+[595] = { city_berlin, 1996, MONTH_jan, 0.016823945045471193, },
+[596] = { city_berlin, 1997, MONTH_jan, 0.018035167455673216, },
+[597] = { city_berlin, 1998, MONTH_jan, 0.019705649614334103, },
+[598] = { city_berlin, 1999, MONTH_jan, 0.02227585792541504, },
+[599] = { city_berlin, 2000, MONTH_jan, 0.028871116638183595, },
+[600] = { city_berlin, 2001, MONTH_jan, 0.029411675930023192, },
+[601] = { city_berlin, 2002, MONTH_jan, 0.03538631916046143, },
+[602] = { city_berlin, 2003, MONTH_jan, 0.03641989707946778, },
+[603] = { city_berlin, 2004, MONTH_jan, 0.04516597747802734, },
+[604] = { city_berlin, 2005, MONTH_jan, 0.052084193229675294, },
+[605] = { city_berlin, 2006, MONTH_jan, 0.060263047218322756, },
+[606] = { city_berlin, 2007, MONTH_jan, 0.07653833389282226, },
+[607] = { city_berlin, 2008, MONTH_jan, 0.07817628383636474, },
+[608] = { city_berlin, 2009, MONTH_jan, 0.08454279899597168, },
+[609] = { city_berlin, 2010, MONTH_jan, 0.08792469024658203, },
+[610] = { city_berlin, 2011, MONTH_jan, 0.10566965103149414, },
+[611] = { city_berlin, 2012, MONTH_jan, 0.11899699211120604, },
+[612] = { city_berlin, 2013, MONTH_jan, 0.12063335418701172, },
+[613] = { city_berlin, 2014, MONTH_jan, 0.13278846740722655, },
+[614] = { city_berlin, 2015, MONTH_jan, 0.14823543548583984, },
+[615] = { city_berlin, 2016, MONTH_jan, 0.14607312202453612, },
+[616] = { city_berlin, 2017, MONTH_jan, 0.16196212768554688, },
+[617] = { city_berlin, 2018, MONTH_jan, 0.17084951400756837, },
+[618] = { city_berlin, 2019, MONTH_jan, 0.18768304824829102, },
+[619] = { city_berlin, 2020, MONTH_jan, 0.21122827529907226, },
+[620] = { city_berlin, 2021, MONTH_jan, 0.19453121185302735, },
+[621] = { city_new_delhi, 1965, MONTH_jan, 0.09149455070495603, },
+[622] = { city_mumbai, 1965, MONTH_jan, 0.09149455070495603, },
+[623] = { city_new_delhi, 1966, MONTH_jan, 0.09270711898803713, },
+[624] = { city_mumbai, 1966, MONTH_jan, 0.09270711898803713, },
+[625] = { city_new_delhi, 1967, MONTH_jan, 0.10058605194091796, },
+[626] = { city_mumbai, 1967, MONTH_jan, 0.10058605194091796, },
+[627] = { city_new_delhi, 1968, MONTH_jan, 0.10857532501220703, },
+[628] = { city_mumbai, 1968, MONTH_jan, 0.10857532501220703, },
+[629] = { city_new_delhi, 1969, MONTH_jan, 0.10873967170715332, },
+[630] = { city_mumbai, 1969, MONTH_jan, 0.10873967170715332, },
+[631] = { city_new_delhi, 1970, MONTH_jan, 0.11784119606018066, },
+[632] = { city_mumbai, 1970, MONTH_jan, 0.11784119606018066, },
+[633] = { city_new_delhi, 1971, MONTH_jan, 0.1242677116394043, },
+[634] = { city_mumbai, 1971, MONTH_jan, 0.1242677116394043, },
+[635] = { city_new_delhi, 1972, MONTH_jan, 0.11463116645812989, },
+[636] = { city_mumbai, 1972, MONTH_jan, 0.11463116645812989, },
+[637] = { city_new_delhi, 1973, MONTH_jan, 0.11957463264465332, },
+[638] = { city_mumbai, 1973, MONTH_jan, 0.11957463264465332, },
+[639] = { city_new_delhi, 1974, MONTH_jan, 0.1086085319519043, },
+[640] = { city_mumbai, 1974, MONTH_jan, 0.1086085319519043, },
+[641] = { city_new_delhi, 1975, MONTH_jan, 0.12067704200744628, },
+[642] = { city_mumbai, 1975, MONTH_jan, 0.12067704200744628, },
+[643] = { city_new_delhi, 1976, MONTH_jan, 0.12036797523498535, },
+[644] = { city_mumbai, 1976, MONTH_jan, 0.12036797523498535, },
+[645] = { city_new_delhi, 1977, MONTH_jan, 0.12415875434875488, },
+[646] = { city_mumbai, 1977, MONTH_jan, 0.12415875434875488, },
+[647] = { city_new_delhi, 1978, MONTH_jan, 0.14818278312683103, },
+[648] = { city_mumbai, 1978, MONTH_jan, 0.14818278312683103, },
+[649] = { city_new_delhi, 1979, MONTH_jan, 0.13540278434753417, },
+[650] = { city_mumbai, 1979, MONTH_jan, 0.13540278434753417, },
+[651] = { city_new_delhi, 1980, MONTH_jan, 0.1345311737060547, },
+[652] = { city_mumbai, 1980, MONTH_jan, 0.1345311737060547, },
+[653] = { city_new_delhi, 1981, MONTH_jan, 0.12948171615600587, },
+[654] = { city_mumbai, 1981, MONTH_jan, 0.12948171615600587, },
+[655] = { city_new_delhi, 1982, MONTH_jan, 0.11035112380981443, },
+[656] = { city_mumbai, 1982, MONTH_jan, 0.11035112380981443, },
+[657] = { city_new_delhi, 1983, MONTH_jan, 0.10110920906066896, },
+[658] = { city_mumbai, 1983, MONTH_jan, 0.10110920906066896, },
+[659] = { city_new_delhi, 1984, MONTH_jan, 0.10576085090637209, },
+[660] = { city_mumbai, 1984, MONTH_jan, 0.10576085090637209, },
+[661] = { city_new_delhi, 1985, MONTH_jan, 0.09670783996582032, },
+[662] = { city_mumbai, 1985, MONTH_jan, 0.09670783996582032, },
+[663] = { city_new_delhi, 1986, MONTH_jan, 0.0916761875152588, },
+[664] = { city_mumbai, 1986, MONTH_jan, 0.0916761875152588, },
+[665] = { city_new_delhi, 1987, MONTH_jan, 0.08030695915222168, },
+[666] = { city_mumbai, 1987, MONTH_jan, 0.08030695915222168, },
+[667] = { city_new_delhi, 1988, MONTH_jan, 0.08229918479919433, },
+[668] = { city_mumbai, 1988, MONTH_jan, 0.08229918479919433, },
+[669] = { city_new_delhi, 1989, MONTH_jan, 0.08742303848266601, },
+[670] = { city_mumbai, 1989, MONTH_jan, 0.08742303848266601, },
+[671] = { city_new_delhi, 1990, MONTH_jan, 0.08542831420898438, },
+[672] = { city_mumbai, 1990, MONTH_jan, 0.08542831420898438, },
+[673] = { city_new_delhi, 1991, MONTH_jan, 0.09029864311218262, },
+[674] = { city_mumbai, 1991, MONTH_jan, 0.09029864311218262, },
+[675] = { city_new_delhi, 1992, MONTH_jan, 0.08140623092651367, },
+[676] = { city_mumbai, 1992, MONTH_jan, 0.08140623092651367, },
+[677] = { city_new_delhi, 1993, MONTH_jan, 0.08035937309265137, },
+[678] = { city_mumbai, 1993, MONTH_jan, 0.08035937309265137, },
+[679] = { city_new_delhi, 1994, MONTH_jan, 0.08667225837707519, },
+[680] = { city_mumbai, 1994, MONTH_jan, 0.08667225837707519, },
+[681] = { city_new_delhi, 1995, MONTH_jan, 0.07686767578125, },
+[682] = { city_mumbai, 1995, MONTH_jan, 0.07686767578125, },
+[683] = { city_new_delhi, 1996, MONTH_jan, 0.06789632797241212, },
+[684] = { city_mumbai, 1996, MONTH_jan, 0.06789632797241212, },
+[685] = { city_new_delhi, 1997, MONTH_jan, 0.06570899486541748, },
+[686] = { city_mumbai, 1997, MONTH_jan, 0.06570899486541748, },
+[687] = { city_new_delhi, 1998, MONTH_jan, 0.07381818294525147, },
+[688] = { city_mumbai, 1998, MONTH_jan, 0.07381818294525147, },
+[689] = { city_new_delhi, 1999, MONTH_jan, 0.0712459659576416, },
+[690] = { city_mumbai, 1999, MONTH_jan, 0.0712459659576416, },
+[691] = { city_new_delhi, 2000, MONTH_jan, 0.06376289367675782, },
+[692] = { city_mumbai, 2000, MONTH_jan, 0.06376289367675782, },
+[693] = { city_new_delhi, 2001, MONTH_jan, 0.0599252462387085, },
+[694] = { city_mumbai, 2001, MONTH_jan, 0.0599252462387085, },
+[695] = { city_new_delhi, 2002, MONTH_jan, 0.057318320274353025, },
+[696] = { city_mumbai, 2002, MONTH_jan, 0.057318320274353025, },
+[697] = { city_new_delhi, 2003, MONTH_jan, 0.05624061584472656, },
+[698] = { city_mumbai, 2003, MONTH_jan, 0.05624061584472656, },
+[699] = { city_new_delhi, 2004, MONTH_jan, 0.07460848808288574, },
+[700] = { city_mumbai, 2004, MONTH_jan, 0.07460848808288574, },
+[701] = { city_new_delhi, 2005, MONTH_jan, 0.06894316673278808, },
+[702] = { city_mumbai, 2005, MONTH_jan, 0.06894316673278808, },
+[703] = { city_new_delhi, 2006, MONTH_jan, 0.07706239700317383, },
+[704] = { city_mumbai, 2006, MONTH_jan, 0.07706239700317383, },
+[705] = { city_new_delhi, 2007, MONTH_jan, 0.07855644702911377, },
+[706] = { city_mumbai, 2007, MONTH_jan, 0.07855644702911377, },
+[707] = { city_new_delhi, 2008, MONTH_jan, 0.07257065296173096, },
+[708] = { city_mumbai, 2008, MONTH_jan, 0.07257065296173096, },
+[709] = { city_new_delhi, 2009, MONTH_jan, 0.06473570346832275, },
+[710] = { city_mumbai, 2009, MONTH_jan, 0.06473570346832275, },
+[711] = { city_new_delhi, 2010, MONTH_jan, 0.0658367109298706, },
+[712] = { city_mumbai, 2010, MONTH_jan, 0.0658367109298706, },
+[713] = { city_new_delhi, 2011, MONTH_jan, 0.075013427734375, },
+[714] = { city_mumbai, 2011, MONTH_jan, 0.075013427734375, },
+[715] = { city_new_delhi, 2012, MONTH_jan, 0.06727289199829102, },
+[716] = { city_mumbai, 2012, MONTH_jan, 0.06727289199829102, },
+[717] = { city_new_delhi, 2013, MONTH_jan, 0.07329665184020996, },
+[718] = { city_mumbai, 2013, MONTH_jan, 0.07329665184020996, },
+[719] = { city_new_delhi, 2014, MONTH_jan, 0.07344336032867432, },
+[720] = { city_mumbai, 2014, MONTH_jan, 0.07344336032867432, },
+[721] = { city_new_delhi, 2015, MONTH_jan, 0.07184645652770996, },
+[722] = { city_mumbai, 2015, MONTH_jan, 0.07184645652770996, },
+[723] = { city_new_delhi, 2016, MONTH_jan, 0.07011397361755371, },
+[724] = { city_mumbai, 2016, MONTH_jan, 0.07011397361755371, },
+[725] = { city_new_delhi, 2017, MONTH_jan, 0.07511875152587891, },
+[726] = { city_mumbai, 2017, MONTH_jan, 0.07511875152587891, },
+[727] = { city_new_delhi, 2018, MONTH_jan, 0.07912805557250976, },
+[728] = { city_mumbai, 2018, MONTH_jan, 0.07912805557250976, },
+[729] = { city_new_delhi, 2019, MONTH_jan, 0.08849618911743164, },
+[730] = { city_mumbai, 2019, MONTH_jan, 0.08849618911743164, },
+[731] = { city_new_delhi, 2020, MONTH_jan, 0.09730036735534668, },
+[732] = { city_mumbai, 2020, MONTH_jan, 0.09730036735534668, },
+[733] = { city_new_delhi, 2021, MONTH_jan, 0.09310850143432615, },
+[734] = { city_mumbai, 2021, MONTH_jan, 0.09310850143432615, },
+[735] = { city_jakarta, 1965, MONTH_jan, 0.06385928153991699, },
+[736] = { city_jakarta, 1966, MONTH_jan, 0.08364607810974121, },
+[737] = { city_jakarta, 1967, MONTH_jan, 0.09505834579467771, },
+[738] = { city_jakarta, 1968, MONTH_jan, 0.1101013946533203, },
+[739] = { city_jakarta, 1969, MONTH_jan, 0.10201886177062988, },
+[740] = { city_jakarta, 1970, MONTH_jan, 0.11570672035217283, },
+[741] = { city_jakarta, 1971, MONTH_jan, 0.04001150131225586, },
+[742] = { city_jakarta, 1972, MONTH_jan, 0.03373836040496826, },
+[743] = { city_jakarta, 1973, MONTH_jan, 0.037743794918060306, },
+[744] = { city_jakarta, 1974, MONTH_jan, 0.039022941589355466, },
+[745] = { city_jakarta, 1975, MONTH_jan, 0.03342118501663208, },
+[746] = { city_jakarta, 1976, MONTH_jan, 0.018672399520874024, },
+[747] = { city_jakarta, 1977, MONTH_jan, 0.023478298187255858, },
+[748] = { city_jakarta, 1978, MONTH_jan, 0.014906297922134399, },
+[749] = { city_jakarta, 1979, MONTH_jan, 0.014562846422195437, },
+[750] = { city_jakarta, 1980, MONTH_jan, 0.012272299528121948, },
+[751] = { city_jakarta, 1981, MONTH_jan, 0.011406815052032471, },
+[752] = { city_jakarta, 1982, MONTH_jan, 0.012204515933990481, },
+[753] = { city_jakarta, 1983, MONTH_jan, 0.017323704957962035, },
+[754] = { city_jakarta, 1984, MONTH_jan, 0.017180938720703125, },
+[755] = { city_jakarta, 1985, MONTH_jan, 0.01980858683586121, },
+[756] = { city_jakarta, 1986, MONTH_jan, 0.02986825704574585, },
+[757] = { city_jakarta, 1987, MONTH_jan, 0.029112305641174317, },
+[758] = { city_jakarta, 1988, MONTH_jan, 0.0302899694442749, },
+[759] = { city_jakarta, 1989, MONTH_jan, 0.03408119440078736, },
+[760] = { city_jakarta, 1990, MONTH_jan, 0.03731863260269165, },
+[761] = { city_jakarta, 1991, MONTH_jan, 0.03812469959259033, },
+[762] = { city_jakarta, 1992, MONTH_jan, 0.044085426330566405, },
+[763] = { city_jakarta, 1993, MONTH_jan, 0.03847653388977051, },
+[764] = { city_jakarta, 1994, MONTH_jan, 0.03521223306655884, },
+[765] = { city_jakarta, 1995, MONTH_jan, 0.035392696857452395, },
+[766] = { city_jakarta, 1996, MONTH_jan, 0.03508815288543701, },
+[767] = { city_jakarta, 1997, MONTH_jan, 0.02477278470993042, },
+[768] = { city_jakarta, 1998, MONTH_jan, 0.04014904499053955, },
+[769] = { city_jakarta, 1999, MONTH_jan, 0.03852458000183105, },
+[770] = { city_jakarta, 2000, MONTH_jan, 0.03733640432357788, },
+[771] = { city_jakarta, 2001, MONTH_jan, 0.04112386703491211, },
+[772] = { city_jakarta, 2002, MONTH_jan, 0.03683813571929931, },
+[773] = { city_jakarta, 2003, MONTH_jan, 0.03221094608306885, },
+[774] = { city_jakarta, 2004, MONTH_jan, 0.034259636402130124, },
+[775] = { city_jakarta, 2005, MONTH_jan, 0.035274262428283694, },
+[776] = { city_jakarta, 2006, MONTH_jan, 0.03220308542251587, },
+[777] = { city_jakarta, 2007, MONTH_jan, 0.03370551109313965, },
+[778] = { city_jakarta, 2008, MONTH_jan, 0.03586532115936279, },
+[779] = { city_jakarta, 2009, MONTH_jan, 0.03655174016952514, },
+[780] = { city_jakarta, 2010, MONTH_jan, 0.043262209892272946, },
+[781] = { city_jakarta, 2011, MONTH_jan, 0.03388597726821899, },
+[782] = { city_jakarta, 2012, MONTH_jan, 0.034713633060455315, },
+[783] = { city_jakarta, 2013, MONTH_jan, 0.0433060359954834, },
+[784] = { city_jakarta, 2014, MONTH_jan, 0.044786324501037596, },
+[785] = { city_jakarta, 2015, MONTH_jan, 0.037929575443267825, },
+[786] = { city_jakarta, 2016, MONTH_jan, 0.05479020118713379, },
+[787] = { city_jakarta, 2017, MONTH_jan, 0.05283773899078369, },
+[788] = { city_jakarta, 2018, MONTH_jan, 0.07393457889556884, },
+[789] = { city_jakarta, 2019, MONTH_jan, 0.07870308876037597, },
+[790] = { city_jakarta, 2020, MONTH_jan, 0.09860146522521973, },
+[791] = { city_jakarta, 2021, MONTH_jan, 0.10385714530944824, },
+[792] = { city_tehran, 1965, MONTH_jan, 0.052250761985778806, },
+[793] = { city_tehran, 1966, MONTH_jan, 0.05151486396789551, },
+[794] = { city_tehran, 1967, MONTH_jan, 0.050412230491638184, },
+[795] = { city_tehran, 1968, MONTH_jan, 0.04931734085083008, },
+[796] = { city_tehran, 1969, MONTH_jan, 0.04837111473083496, },
+[797] = { city_tehran, 1970, MONTH_jan, 0.04415298461914063, },
+[798] = { city_tehran, 1971, MONTH_jan, 0.04332554340362549, },
+[799] = { city_tehran, 1972, MONTH_jan, 0.0501894760131836, },
+[800] = { city_tehran, 1973, MONTH_jan, 0.03557704448699951, },
+[801] = { city_tehran, 1974, MONTH_jan, 0.03455620527267456, },
+[802] = { city_tehran, 1975, MONTH_jan, 0.031140482425689696, },
+[803] = { city_tehran, 1976, MONTH_jan, 0.0309524917602539, },
+[804] = { city_tehran, 1977, MONTH_jan, 0.02825014591217041, },
+[805] = { city_tehran, 1978, MONTH_jan, 0.04526003360748291, },
+[806] = { city_tehran, 1979, MONTH_jan, 0.036342711448669435, },
+[807] = { city_tehran, 1980, MONTH_jan, 0.04064725875854492, },
+[808] = { city_tehran, 1981, MONTH_jan, 0.044219164848327636, },
+[809] = { city_tehran, 1982, MONTH_jan, 0.04111237049102783, },
+[810] = { city_tehran, 1983, MONTH_jan, 0.033884649276733396, },
+[811] = { city_tehran, 1984, MONTH_jan, 0.029317567348480223, },
+[812] = { city_tehran, 1985, MONTH_jan, 0.027088303565979004, },
+[813] = { city_tehran, 1986, MONTH_jan, 0.03455609560012817, },
+[814] = { city_tehran, 1987, MONTH_jan, 0.03573478698730469, },
+[815] = { city_tehran, 1988, MONTH_jan, 0.036968004703521726, },
+[816] = { city_tehran, 1989, MONTH_jan, 0.027610442638397216, },
+[817] = { city_tehran, 1990, MONTH_jan, 0.026902496814727783, },
+[818] = { city_tehran, 1991, MONTH_jan, 0.018835831880569455, },
+[819] = { city_tehran, 1992, MONTH_jan, 0.02795917510986328, },
+[820] = { city_tehran, 1993, MONTH_jan, 0.034577884674072266, },
+[821] = { city_tehran, 1994, MONTH_jan, 0.020214617252349857, },
+[822] = { city_tehran, 1995, MONTH_jan, 0.023263061046600343, },
+[823] = { city_tehran, 1996, MONTH_jan, 0.022645998001098632, },
+[824] = { city_tehran, 1997, MONTH_jan, 0.014810699224472045, },
+[825] = { city_tehran, 1998, MONTH_jan, 0.017921339273452762, },
+[826] = { city_tehran, 1999, MONTH_jan, 0.011498689651489258, },
+[827] = { city_tehran, 2000, MONTH_jan, 0.00808289110660553, },
+[828] = { city_tehran, 2001, MONTH_jan, 0.008181908130645753, },
+[829] = { city_tehran, 2002, MONTH_jan, 0.014433478116989135, },
+[830] = { city_tehran, 2003, MONTH_jan, 0.016837654113769532, },
+[831] = { city_tehran, 2004, MONTH_jan, 0.01884332299232483, },
+[832] = { city_tehran, 2005, MONTH_jan, 0.021426386833190918, },
+[833] = { city_tehran, 2006, MONTH_jan, 0.025007379055023194, },
+[834] = { city_tehran, 2007, MONTH_jan, 0.022706358432769774, },
+[835] = { city_tehran, 2008, MONTH_jan, 0.009235934615135192, },
+[836] = { city_tehran, 2009, MONTH_jan, 0.008136967420578003, },
+[837] = { city_tehran, 2010, MONTH_jan, 0.011950724124908448, },
+[838] = { city_tehran, 2011, MONTH_jan, 0.011898080110549929, },
+[839] = { city_tehran, 2012, MONTH_jan, 0.013722978830337525, },
+[840] = { city_tehran, 2013, MONTH_jan, 0.01525709867477417, },
+[841] = { city_tehran, 2014, MONTH_jan, 0.014402105808258056, },
+[842] = { city_tehran, 2015, MONTH_jan, 0.013088065385818479, },
+[843] = { city_tehran, 2016, MONTH_jan, 0.014465060234069824, },
+[844] = { city_tehran, 2017, MONTH_jan, 0.015620874166488647, },
+[845] = { city_tehran, 2018, MONTH_jan, 0.009227302074432371, },
+[846] = { city_tehran, 2019, MONTH_jan, 0.028368299007415772, },
+[847] = { city_tehran, 2020, MONTH_jan, 0.019643505811691286, },
+[848] = { city_tehran, 2021, MONTH_jan, 0.012903937101364137, },
+[849] = { city_baghdad, 1965, MONTH_jan, 0.009088937044143675, },
+[850] = { city_baghdad, 1966, MONTH_jan, 0.010816304683685305, },
+[851] = { city_baghdad, 1967, MONTH_jan, 0.012241523265838623, },
+[852] = { city_baghdad, 1968, MONTH_jan, 0.013923404216766355, },
+[853] = { city_baghdad, 1969, MONTH_jan, 0.013289313316345218, },
+[854] = { city_baghdad, 1970, MONTH_jan, 0.01301562786102295, },
+[855] = { city_baghdad, 1971, MONTH_jan, 0.013798673152923584, },
+[856] = { city_baghdad, 1972, MONTH_jan, 0.011780089139938355, },
+[857] = { city_baghdad, 1973, MONTH_jan, 0.01737240195274353, },
+[858] = { city_baghdad, 1974, MONTH_jan, 0.02446626663208008, },
+[859] = { city_baghdad, 1975, MONTH_jan, 0.02579850673675537, },
+[860] = { city_baghdad, 1976, MONTH_jan, 0.020427801609039307, },
+[861] = { city_baghdad, 1977, MONTH_jan, 0.02406090497970581, },
+[862] = { city_baghdad, 1978, MONTH_jan, 0.03056793212890625, },
+[863] = { city_baghdad, 1979, MONTH_jan, 0.03528018236160278, },
+[864] = { city_baghdad, 1980, MONTH_jan, 0.020129208564758302, },
+[865] = { city_baghdad, 1981, MONTH_jan, 0.019036805629730223, },
+[866] = { city_baghdad, 1982, MONTH_jan, 0.01808008313179016, },
+[867] = { city_baghdad, 1983, MONTH_jan, 0.01574033737182617, },
+[868] = { city_baghdad, 1984, MONTH_jan, 0.014442424774169921, },
+[869] = { city_baghdad, 1985, MONTH_jan, 0.012590632438659669, },
+[870] = { city_baghdad, 1986, MONTH_jan, 0.010752139091491699, },
+[871] = { city_baghdad, 1987, MONTH_jan, 0.03949446678161621, },
+[872] = { city_baghdad, 1988, MONTH_jan, 0.03410572290420532, },
+[873] = { city_baghdad, 1989, MONTH_jan, 0.030038673877716065, },
+[874] = { city_baghdad, 1990, MONTH_jan, 0.0578435230255127, },
+[875] = { city_baghdad, 1991, MONTH_jan, 0.0428729248046875, },
+[876] = { city_baghdad, 1992, MONTH_jan, 0.06584853172302246, },
+[877] = { city_baghdad, 1993, MONTH_jan, 0.058610520362854, },
+[878] = { city_baghdad, 1994, MONTH_jan, 0.05243114471435547, },
+[879] = { city_baghdad, 1995, MONTH_jan, 0.05992914199829102, },
+[880] = { city_baghdad, 1996, MONTH_jan, 0.054542551040649416, },
+[881] = { city_baghdad, 1997, MONTH_jan, 0.046753549575805665, },
+[882] = { city_baghdad, 1998, MONTH_jan, 0.05869803905487061, },
+[883] = { city_baghdad, 1999, MONTH_jan, 0.04700111389160156, },
+[884] = { city_baghdad, 2000, MONTH_jan, 0.029294536113739018, },
+[885] = { city_baghdad, 2001, MONTH_jan, 0.03016484260559082, },
+[886] = { city_baghdad, 2002, MONTH_jan, 0.03885046482086182, },
+[887] = { city_baghdad, 2003, MONTH_jan, 0.04535982608795166, },
+[888] = { city_baghdad, 2004, MONTH_jan, 0.05081915855407715, },
+[889] = { city_baghdad, 2005, MONTH_jan, 0.04544925212860108, },
+[890] = { city_baghdad, 2006, MONTH_jan, 0.0473859977722168, },
+[891] = { city_baghdad, 2007, MONTH_jan, 0.042728943824768065, },
+[892] = { city_baghdad, 2008, MONTH_jan, 0.02381868362426758, },
+[893] = { city_baghdad, 2009, MONTH_jan, 0.023280267715454103, },
+[894] = { city_baghdad, 2010, MONTH_jan, 0.026375570297241212, },
+[895] = { city_baghdad, 2011, MONTH_jan, 0.023962152004241944, },
+[896] = { city_baghdad, 2012, MONTH_jan, 0.02823270320892334, },
+[897] = { city_baghdad, 2013, MONTH_jan, 0.026760001182556153, },
+[898] = { city_baghdad, 2014, MONTH_jan, 0.01738718867301941, },
+[899] = { city_baghdad, 2015, MONTH_jan, 0.016102989912033083, },
+[900] = { city_baghdad, 2016, MONTH_jan, 0.01836539387702942, },
+[901] = { city_baghdad, 2017, MONTH_jan, 0.011253452301025391, },
+[902] = { city_baghdad, 2018, MONTH_jan, 0.009119535088539124, },
+[903] = { city_baghdad, 2019, MONTH_jan, 0.022772881984710693, },
+[904] = { city_baghdad, 2020, MONTH_jan, 0.016468850374221803, },
+[905] = { city_baghdad, 2021, MONTH_jan, 0.015024909973144531, },
+[906] = { city_tokyo, 1965, MONTH_jan, 0.12335086822509765, },
+[907] = { city_tokyo, 1966, MONTH_jan, 0.11651081085205078, },
+[908] = { city_tokyo, 1967, MONTH_jan, 0.08793859481811524, },
+[909] = { city_tokyo, 1968, MONTH_jan, 0.08451984405517578, },
+[910] = { city_tokyo, 1969, MONTH_jan, 0.0761188268661499, },
+[911] = { city_tokyo, 1970, MONTH_jan, 0.06836434364318848, },
+[912] = { city_tokyo, 1971, MONTH_jan, 0.07166181564331055, },
+[913] = { city_tokyo, 1972, MONTH_jan, 0.06914736270904541, },
+[914] = { city_tokyo, 1973, MONTH_jan, 0.04894681930541992, },
+[915] = { city_tokyo, 1974, MONTH_jan, 0.060309801101684574, },
+[916] = { city_tokyo, 1975, MONTH_jan, 0.06373206615447997, },
+[917] = { city_tokyo, 1976, MONTH_jan, 0.06161350250244141, },
+[918] = { city_tokyo, 1977, MONTH_jan, 0.05355945110321045, },
+[919] = { city_tokyo, 1978, MONTH_jan, 0.049568381309509274, },
+[920] = { city_tokyo, 1979, MONTH_jan, 0.055059475898742674, },
+[921] = { city_tokyo, 1980, MONTH_jan, 0.06194887161254883, },
+[922] = { city_tokyo, 1981, MONTH_jan, 0.062369403839111326, },
+[923] = { city_tokyo, 1982, MONTH_jan, 0.06735273361206055, },
+[924] = { city_tokyo, 1983, MONTH_jan, 0.06926650524139405, },
+[925] = { city_tokyo, 1984, MONTH_jan, 0.05621933937072754, },
+[926] = { city_tokyo, 1985, MONTH_jan, 0.06361672401428223, },
+[927] = { city_tokyo, 1986, MONTH_jan, 0.06296619892120361, },
+[928] = { city_tokyo, 1987, MONTH_jan, 0.058519330024719235, },
+[929] = { city_tokyo, 1988, MONTH_jan, 0.06523026466369629, },
+[930] = { city_tokyo, 1989, MONTH_jan, 0.06458432197570801, },
+[931] = { city_tokyo, 1990, MONTH_jan, 0.05605645179748535, },
+[932] = { city_tokyo, 1991, MONTH_jan, 0.059171295166015624, },
+[933] = { city_tokyo, 1992, MONTH_jan, 0.050801749229431155, },
+[934] = { city_tokyo, 1993, MONTH_jan, 0.05666120529174805, },
+[935] = { city_tokyo, 1994, MONTH_jan, 0.04097033977508545, },
+[936] = { city_tokyo, 1995, MONTH_jan, 0.04652009010314941, },
+[937] = { city_tokyo, 1996, MONTH_jan, 0.045326762199401856, },
+[938] = { city_tokyo, 1997, MONTH_jan, 0.04951632499694824, },
+[939] = { city_tokyo, 1998, MONTH_jan, 0.05170876026153565, },
+[940] = { city_tokyo, 1999, MONTH_jan, 0.048562874794006346, },
+[941] = { city_tokyo, 2000, MONTH_jan, 0.0482745361328125, },
+[942] = { city_tokyo, 2001, MONTH_jan, 0.047163658142089844, },
+[943] = { city_tokyo, 2002, MONTH_jan, 0.04702637195587158, },
+[944] = { city_tokyo, 2003, MONTH_jan, 0.05332041263580322, },
+[945] = { city_tokyo, 2004, MONTH_jan, 0.052576699256896973, },
+[946] = { city_tokyo, 2005, MONTH_jan, 0.04790322780609131, },
+[947] = { city_tokyo, 2006, MONTH_jan, 0.053090085983276365, },
+[948] = { city_tokyo, 2007, MONTH_jan, 0.04761953353881836, },
+[949] = { city_tokyo, 2008, MONTH_jan, 0.04830859184265137, },
+[950] = { city_tokyo, 2009, MONTH_jan, 0.05021396160125732, },
+[951] = { city_tokyo, 2010, MONTH_jan, 0.05684884071350098, },
+[952] = { city_tokyo, 2011, MONTH_jan, 0.057331571578979494, },
+[953] = { city_tokyo, 2012, MONTH_jan, 0.05650204658508301, },
+[954] = { city_tokyo, 2013, MONTH_jan, 0.061299676895141604, },
+[955] = { city_tokyo, 2014, MONTH_jan, 0.06935917377471924, },
+[956] = { city_tokyo, 2015, MONTH_jan, 0.0806879711151123, },
+[957] = { city_tokyo, 2016, MONTH_jan, 0.07975860595703126, },
+[958] = { city_tokyo, 2017, MONTH_jan, 0.08634003639221191, },
+[959] = { city_tokyo, 2018, MONTH_jan, 0.09322304725646972, },
+[960] = { city_tokyo, 2019, MONTH_jan, 0.09565926551818847, },
+[961] = { city_tokyo, 2020, MONTH_jan, 0.11155339241027833, },
+[962] = { city_tokyo, 2021, MONTH_jan, 0.11428995132446287, },
+[963] = { city_mexico_city, 1965, MONTH_jan, 0.08934722900390625, },
+[964] = { city_mexico_city, 1966, MONTH_jan, 0.09633110046386721, },
+[965] = { city_mexico_city, 1967, MONTH_jan, 0.1029133129119873, },
+[966] = { city_mexico_city, 1968, MONTH_jan, 0.1084283447265625, },
+[967] = { city_mexico_city, 1969, MONTH_jan, 0.10475299835205078, },
+[968] = { city_mexico_city, 1970, MONTH_jan, 0.1098171329498291, },
+[969] = { city_mexico_city, 1971, MONTH_jan, 0.10142724990844729, },
+[970] = { city_mexico_city, 1972, MONTH_jan, 0.09780027389526368, },
+[971] = { city_mexico_city, 1973, MONTH_jan, 0.09648298263549805, },
+[972] = { city_mexico_city, 1974, MONTH_jan, 0.09094654083251952, },
+[973] = { city_mexico_city, 1975, MONTH_jan, 0.07784452438354492, },
+[974] = { city_mexico_city, 1976, MONTH_jan, 0.08308449745178223, },
+[975] = { city_mexico_city, 1977, MONTH_jan, 0.08738277435302734, },
+[976] = { city_mexico_city, 1978, MONTH_jan, 0.0659796380996704, },
+[977] = { city_mexico_city, 1979, MONTH_jan, 0.06747371673583985, },
+[978] = { city_mexico_city, 1980, MONTH_jan, 0.058035578727722165, },
+[979] = { city_mexico_city, 1981, MONTH_jan, 0.07537362575531006, },
+[980] = { city_mexico_city, 1982, MONTH_jan, 0.06843096733093262, },
+[981] = { city_mexico_city, 1983, MONTH_jan, 0.06308685779571534, },
+[982] = { city_mexico_city, 1984, MONTH_jan, 0.0683129644393921, },
+[983] = { city_mexico_city, 1985, MONTH_jan, 0.07820509910583497, },
+[984] = { city_mexico_city, 1986, MONTH_jan, 0.0659659194946289, },
+[985] = { city_mexico_city, 1987, MONTH_jan, 0.06118594646453857, },
+[986] = { city_mexico_city, 1988, MONTH_jan, 0.06784612655639649, },
+[987] = { city_mexico_city, 1989, MONTH_jan, 0.07293380260467529, },
+[988] = { city_mexico_city, 1990, MONTH_jan, 0.06830843448638917, },
+[989] = { city_mexico_city, 1991, MONTH_jan, 0.06324604988098144, },
+[990] = { city_mexico_city, 1992, MONTH_jan, 0.0725102186203003, },
+[991] = { city_mexico_city, 1993, MONTH_jan, 0.07209392070770264, },
+[992] = { city_mexico_city, 1994, MONTH_jan, 0.05448117256164551, },
+[993] = { city_mexico_city, 1995, MONTH_jan, 0.07110982418060302, },
+[994] = { city_mexico_city, 1996, MONTH_jan, 0.0760121488571167, },
+[995] = { city_mexico_city, 1997, MONTH_jan, 0.06423584938049316, },
+[996] = { city_mexico_city, 1998, MONTH_jan, 0.05862017154693604, },
+[997] = { city_mexico_city, 1999, MONTH_jan, 0.07305535793304443, },
+[998] = { city_mexico_city, 2000, MONTH_jan, 0.0715089750289917, },
+[999] = { city_mexico_city, 2001, MONTH_jan, 0.06339307308197022, },
+[1000] = { city_mexico_city, 2002, MONTH_jan, 0.05449038505554199, },
+[1001] = { city_mexico_city, 2003, MONTH_jan, 0.046524553298950194, },
+[1002] = { city_mexico_city, 2004, MONTH_jan, 0.053032422065734865, },
+[1003] = { city_mexico_city, 2005, MONTH_jan, 0.05473337173461914, },
+[1004] = { city_mexico_city, 2006, MONTH_jan, 0.055860862731933594, },
+[1005] = { city_mexico_city, 2007, MONTH_jan, 0.052095470428466795, },
+[1006] = { city_mexico_city, 2008, MONTH_jan, 0.06696115970611573, },
+[1007] = { city_mexico_city, 2009, MONTH_jan, 0.04956578254699707, },
+[1008] = { city_mexico_city, 2010, MONTH_jan, 0.06283123970031738, },
+[1009] = { city_mexico_city, 2011, MONTH_jan, 0.058868541717529296, },
+[1010] = { city_mexico_city, 2012, MONTH_jan, 0.054620795249938965, },
+[1011] = { city_mexico_city, 2013, MONTH_jan, 0.05060070037841797, },
+[1012] = { city_mexico_city, 2014, MONTH_jan, 0.06727101802825927, },
+[1013] = { city_mexico_city, 2015, MONTH_jan, 0.060522632598876955, },
+[1014] = { city_mexico_city, 2016, MONTH_jan, 0.06131492614746094, },
+[1015] = { city_mexico_city, 2017, MONTH_jan, 0.06372722148895264, },
+[1016] = { city_mexico_city, 2018, MONTH_jan, 0.06939361095428467, },
+[1017] = { city_mexico_city, 2019, MONTH_jan, 0.0705077075958252, },
+[1018] = { city_mexico_city, 2020, MONTH_jan, 0.09475372314453123, },
+[1019] = { city_mexico_city, 2021, MONTH_jan, 0.1051475429534912, },
+[1020] = { city_rabat, 1965, MONTH_jan, 0.2022709846496582, },
+[1021] = { city_rabat, 1966, MONTH_jan, 0.1334361457824707, },
+[1022] = { city_rabat, 1967, MONTH_jan, 0.11197633743286133, },
+[1023] = { city_rabat, 1968, MONTH_jan, 0.11839537620544434, },
+[1024] = { city_rabat, 1969, MONTH_jan, 0.13958054542541504, },
+[1025] = { city_rabat, 1970, MONTH_jan, 0.13115361213684082, },
+[1026] = { city_rabat, 1971, MONTH_jan, 0.1392387866973877, },
+[1027] = { city_rabat, 1972, MONTH_jan, 0.13526378631591796, },
+[1028] = { city_rabat, 1973, MONTH_jan, 0.09094624519348145, },
+[1029] = { city_rabat, 1974, MONTH_jan, 0.09542439460754397, },
+[1030] = { city_rabat, 1975, MONTH_jan, 0.0713439130783081, },
+[1031] = { city_rabat, 1976, MONTH_jan, 0.06466088294982911, },
+[1032] = { city_rabat, 1977, MONTH_jan, 0.07884840011596679, },
+[1033] = { city_rabat, 1978, MONTH_jan, 0.0770575761795044, },
+[1034] = { city_rabat, 1979, MONTH_jan, 0.0771904230117798, },
+[1035] = { city_rabat, 1980, MONTH_jan, 0.07371124744415283, },
+[1036] = { city_rabat, 1981, MONTH_jan, 0.05136460304260254, },
+[1037] = { city_rabat, 1982, MONTH_jan, 0.027842175960540772, },
+[1038] = { city_rabat, 1983, MONTH_jan, 0.02280400514602661, },
+[1039] = { city_rabat, 1984, MONTH_jan, 0.01712327241897583, },
+[1040] = { city_rabat, 1985, MONTH_jan, 0.02200667381286621, },
+[1041] = { city_rabat, 1986, MONTH_jan, 0.028153402805328367, },
+[1042] = { city_rabat, 1987, MONTH_jan, 0.03506812572479248, },
+[1043] = { city_rabat, 1988, MONTH_jan, 0.037663094997406006, },
+[1044] = { city_rabat, 1989, MONTH_jan, 0.04231739044189453, },
+[1045] = { city_rabat, 1990, MONTH_jan, 0.04359245777130127, },
+[1046] = { city_rabat, 1991, MONTH_jan, 0.04325900077819824, },
+[1047] = { city_rabat, 1992, MONTH_jan, 0.030396227836608888, },
+[1048] = { city_rabat, 1993, MONTH_jan, 0.013822969198226929, },
+[1049] = { city_rabat, 1994, MONTH_jan, 0.023779470920562745, },
+[1050] = { city_rabat, 1995, MONTH_jan, 0.01763482928276062, },
+[1051] = { city_rabat, 1996, MONTH_jan, 0.05402516841888428, },
+[1052] = { city_rabat, 1997, MONTH_jan, 0.05531386375427246, },
+[1053] = { city_rabat, 1998, MONTH_jan, 0.04600728511810303, },
+[1054] = { city_rabat, 1999, MONTH_jan, 0.020588018894195557, },
+[1055] = { city_rabat, 2000, MONTH_jan, 0.019685844182968138, },
+[1056] = { city_rabat, 2001, MONTH_jan, 0.024725899696350098, },
+[1057] = { city_rabat, 2002, MONTH_jan, 0.02324991226196289, },
+[1058] = { city_rabat, 2003, MONTH_jan, 0.03657069444656372, },
+[1059] = { city_rabat, 2004, MONTH_jan, 0.03473497629165649, },
+[1060] = { city_rabat, 2005, MONTH_jan, 0.020816917419433593, },
+[1061] = { city_rabat, 2006, MONTH_jan, 0.020182490348815918, },
+[1062] = { city_rabat, 2007, MONTH_jan, 0.02050191640853882, },
+[1063] = { city_rabat, 2008, MONTH_jan, 0.018975967168807985, },
+[1064] = { city_rabat, 2009, MONTH_jan, 0.04711688995361328, },
+[1065] = { city_rabat, 2010, MONTH_jan, 0.05887355327606201, },
+[1066] = { city_rabat, 2011, MONTH_jan, 0.0364383602142334, },
+[1067] = { city_rabat, 2012, MONTH_jan, 0.031137833595275877, },
+[1068] = { city_rabat, 2013, MONTH_jan, 0.05110578060150146, },
+[1069] = { city_rabat, 2014, MONTH_jan, 0.04458736419677734, },
+[1070] = { city_rabat, 2015, MONTH_jan, 0.053888487815856936, },
+[1071] = { city_rabat, 2016, MONTH_jan, 0.05595184803009033, },
+[1072] = { city_rabat, 2017, MONTH_jan, 0.05286826610565185, },
+[1073] = { city_rabat, 2018, MONTH_jan, 0.0710693359375, },
+[1074] = { city_rabat, 2019, MONTH_jan, 0.07547047138214111, },
+[1075] = { city_rabat, 2020, MONTH_jan, 0.07633952140808105, },
+[1076] = { city_rabat, 2021, MONTH_jan, 0.0763616943359375, },
+[1077] = { city_amsterdam, 1965, MONTH_jan, 0, },
+[1078] = { city_amsterdam, 1966, MONTH_jan, 0, },
+[1079] = { city_amsterdam, 1967, MONTH_jan, 0, },
+[1080] = { city_amsterdam, 1968, MONTH_jan, 0, },
+[1081] = { city_amsterdam, 1969, MONTH_jan, 0, },
+[1082] = { city_amsterdam, 1970, MONTH_jan, 0, },
+[1083] = { city_amsterdam, 1971, MONTH_jan, 0, },
+[1084] = { city_amsterdam, 1972, MONTH_jan, 0, },
+[1085] = { city_amsterdam, 1973, MONTH_jan, 0, },
+[1086] = { city_amsterdam, 1974, MONTH_jan, 0, },
+[1087] = { city_amsterdam, 1975, MONTH_jan, 0.0031152141094207765, },
+[1088] = { city_amsterdam, 1976, MONTH_jan, 0.0031017714738845823, },
+[1089] = { city_amsterdam, 1977, MONTH_jan, 0.0031403565406799318, },
+[1090] = { city_amsterdam, 1978, MONTH_jan, 0.004299201965332031, },
+[1091] = { city_amsterdam, 1979, MONTH_jan, 0.0038009288907051085, },
+[1092] = { city_amsterdam, 1980, MONTH_jan, 0.0037478047609329226, },
+[1093] = { city_amsterdam, 1981, MONTH_jan, 0.004187572598457336, },
+[1094] = { city_amsterdam, 1982, MONTH_jan, 0.0011066412925720215, },
+[1095] = { city_amsterdam, 1983, MONTH_jan, 9.600715339183807e-4, },
+[1096] = { city_amsterdam, 1984, MONTH_jan, 6.805820390582081e-5, },
+[1097] = { city_amsterdam, 1985, MONTH_jan, 0.0017216096818447114, },
+[1098] = { city_amsterdam, 1986, MONTH_jan, 0.002057678997516632, },
+[1099] = { city_amsterdam, 1987, MONTH_jan, 0.002552417516708374, },
+[1100] = { city_amsterdam, 1988, MONTH_jan, 0.003074186444282532, },
+[1101] = { city_amsterdam, 1989, MONTH_jan, 0.003433368802070618, },
+[1102] = { city_amsterdam, 1990, MONTH_jan, 0.0028062096238136293, },
+[1103] = { city_amsterdam, 1991, MONTH_jan, 0.003191479742527008, },
+[1104] = { city_amsterdam, 1992, MONTH_jan, 0.0033999988436698913, },
+[1105] = { city_amsterdam, 1993, MONTH_jan, 0.0037428748607635496, },
+[1106] = { city_amsterdam, 1994, MONTH_jan, 0.00426658183336258, },
+[1107] = { city_amsterdam, 1995, MONTH_jan, 0.0045612013339996335, },
+[1108] = { city_amsterdam, 1996, MONTH_jan, 0.0056391310691833495, },
+[1109] = { city_amsterdam, 1997, MONTH_jan, 0.0063600361347198485, },
+[1110] = { city_amsterdam, 1998, MONTH_jan, 0.007268246412277222, },
+[1111] = { city_amsterdam, 1999, MONTH_jan, 0.007947908639907837, },
+[1112] = { city_amsterdam, 2000, MONTH_jan, 0.00925859272480011, },
+[1113] = { city_amsterdam, 2001, MONTH_jan, 0.009883764982223512, },
+[1114] = { city_amsterdam, 2002, MONTH_jan, 0.011863465309143067, },
+[1115] = { city_amsterdam, 2003, MONTH_jan, 0.011635091304779053, },
+[1116] = { city_amsterdam, 2004, MONTH_jan, 0.015031250715255734, },
+[1117] = { city_amsterdam, 2005, MONTH_jan, 0.020780501365661622, },
+[1118] = { city_amsterdam, 2006, MONTH_jan, 0.023150839805603028, },
+[1119] = { city_amsterdam, 2007, MONTH_jan, 0.024174244403839112, },
+[1120] = { city_amsterdam, 2008, MONTH_jan, 0.02913216352462769, },
+[1121] = { city_amsterdam, 2009, MONTH_jan, 0.034302778244018554, },
+[1122] = { city_amsterdam, 2010, MONTH_jan, 0.032134413719177246, },
+[1123] = { city_amsterdam, 2011, MONTH_jan, 0.03811289310455322, },
+[1124] = { city_amsterdam, 2012, MONTH_jan, 0.03963631391525269, },
+[1125] = { city_amsterdam, 2013, MONTH_jan, 0.03945788860321045, },
+[1126] = { city_amsterdam, 2014, MONTH_jan, 0.03978432416915893, },
+[1127] = { city_amsterdam, 2015, MONTH_jan, 0.04503983974456787, },
+[1128] = { city_amsterdam, 2016, MONTH_jan, 0.04676411151885986, },
+[1129] = { city_amsterdam, 2017, MONTH_jan, 0.05489981651306152, },
+[1130] = { city_amsterdam, 2018, MONTH_jan, 0.059912681579589844, },
+[1131] = { city_amsterdam, 2019, MONTH_jan, 0.07302987575531006, },
+[1132] = { city_amsterdam, 2020, MONTH_jan, 0.10758957862854004, },
+[1133] = { city_amsterdam, 2021, MONTH_jan, 0.12373795509338381, },
+[1134] = { city_islamabad, 1965, MONTH_jan, 0.07898932456970215, },
+[1135] = { city_islamabad, 1966, MONTH_jan, 0.07840171337127684, },
+[1136] = { city_islamabad, 1967, MONTH_jan, 0.0839201831817627, },
+[1137] = { city_islamabad, 1968, MONTH_jan, 0.08885177612304687, },
+[1138] = { city_islamabad, 1969, MONTH_jan, 0.09329750061035157, },
+[1139] = { city_islamabad, 1970, MONTH_jan, 0.08390380859375, },
+[1140] = { city_islamabad, 1971, MONTH_jan, 0.1171886157989502, },
+[1141] = { city_islamabad, 1972, MONTH_jan, 0.1325389575958252, },
+[1142] = { city_islamabad, 1973, MONTH_jan, 0.1308777618408203, },
+[1143] = { city_islamabad, 1974, MONTH_jan, 0.12144384384155271, },
+[1144] = { city_islamabad, 1975, MONTH_jan, 0.13310853958129884, },
+[1145] = { city_islamabad, 1976, MONTH_jan, 0.1423151779174805, },
+[1146] = { city_islamabad, 1977, MONTH_jan, 0.1562037467956543, },
+[1147] = { city_islamabad, 1978, MONTH_jan, 0.18110393524169918, },
+[1148] = { city_islamabad, 1979, MONTH_jan, 0.17961801528930665, },
+[1149] = { city_islamabad, 1980, MONTH_jan, 0.16982366561889647, },
+[1150] = { city_islamabad, 1981, MONTH_jan, 0.16494283676147461, },
+[1151] = { city_islamabad, 1982, MONTH_jan, 0.1682915496826172, },
+[1152] = { city_islamabad, 1983, MONTH_jan, 0.18234487533569335, },
+[1153] = { city_islamabad, 1984, MONTH_jan, 0.1785936164855957, },
+[1154] = { city_islamabad, 1985, MONTH_jan, 0.17652673721313478, },
+[1155] = { city_islamabad, 1986, MONTH_jan, 0.1592543983459473, },
+[1156] = { city_islamabad, 1987, MONTH_jan, 0.1866611099243164, },
+[1157] = { city_islamabad, 1988, MONTH_jan, 0.1841936492919922, },
+[1158] = { city_islamabad, 1989, MONTH_jan, 0.17649538040161133, },
+[1159] = { city_islamabad, 1990, MONTH_jan, 0.16537376403808593, },
+[1160] = { city_islamabad, 1991, MONTH_jan, 0.16701480865478516, },
+[1161] = { city_islamabad, 1992, MONTH_jan, 0.17344545364379882, },
+[1162] = { city_islamabad, 1993, MONTH_jan, 0.17234846115112304, },
+[1163] = { city_islamabad, 1994, MONTH_jan, 0.16347652435302734, },
+[1164] = { city_islamabad, 1995, MONTH_jan, 0.16378124237060546, },
+[1165] = { city_islamabad, 1996, MONTH_jan, 0.1676286506652832, },
+[1166] = { city_islamabad, 1997, MONTH_jan, 0.1289863395690918, },
+[1167] = { city_islamabad, 1998, MONTH_jan, 0.15707118988037108, },
+[1168] = { city_islamabad, 1999, MONTH_jan, 0.13423368453979492, },
+[1169] = { city_islamabad, 2000, MONTH_jan, 0.11344158172607421, },
+[1170] = { city_islamabad, 2001, MONTH_jan, 0.1141504955291748, },
+[1171] = { city_islamabad, 2002, MONTH_jan, 0.12188603401184082, },
+[1172] = { city_islamabad, 2003, MONTH_jan, 0.13333745002746583, },
+[1173] = { city_islamabad, 2004, MONTH_jan, 0.12676342964172363, },
+[1174] = { city_islamabad, 2005, MONTH_jan, 0.1359751033782959, },
+[1175] = { city_islamabad, 2006, MONTH_jan, 0.12633492469787597, },
+[1176] = { city_islamabad, 2007, MONTH_jan, 0.12365367889404297, },
+[1177] = { city_islamabad, 2008, MONTH_jan, 0.10553434371948242, },
+[1178] = { city_islamabad, 2009, MONTH_jan, 0.10885856628417968, },
+[1179] = { city_islamabad, 2010, MONTH_jan, 0.11174628257751465, },
+[1180] = { city_islamabad, 2011, MONTH_jan, 0.1141127872467041, },
+[1181] = { city_islamabad, 2012, MONTH_jan, 0.04317787647247315, },
+[1182] = { city_islamabad, 2013, MONTH_jan, 0.11522516250610351, },
+[1183] = { city_islamabad, 2014, MONTH_jan, 0.11496394157409667, },
+[1184] = { city_islamabad, 2015, MONTH_jan, 0.11174578666687011, },
+[1185] = { city_islamabad, 2016, MONTH_jan, 0.11086973190307615, },
+[1186] = { city_islamabad, 2017, MONTH_jan, 0.09674949645996093, },
+[1187] = { city_islamabad, 2018, MONTH_jan, 0.09416380882263184, },
+[1188] = { city_islamabad, 2019, MONTH_jan, 0.11015737533569336, },
+[1189] = { city_islamabad, 2020, MONTH_jan, 0.12021024703979492, },
+[1190] = { city_islamabad, 2021, MONTH_jan, 0.10622897148132324, },
+[1191] = { city_lima, 1965, MONTH_jan, 0.1374032688140869, },
+[1192] = { city_lima, 1966, MONTH_jan, 0.12172947883605956, },
+[1193] = { city_lima, 1967, MONTH_jan, 0.13394146919250488, },
+[1194] = { city_lima, 1968, MONTH_jan, 0.1411124134063721, },
+[1195] = { city_lima, 1969, MONTH_jan, 0.1524188995361328, },
+[1196] = { city_lima, 1970, MONTH_jan, 0.14120806694030763, },
+[1197] = { city_lima, 1971, MONTH_jan, 0.17272397994995117, },
+[1198] = { city_lima, 1972, MONTH_jan, 0.19887008666992187, },
+[1199] = { city_lima, 1973, MONTH_jan, 0.1857759666442871, },
+[1200] = { city_lima, 1974, MONTH_jan, 0.17592985153198243, },
+[1201] = { city_lima, 1975, MONTH_jan, 0.1786097526550293, },
+[1202] = { city_lima, 1976, MONTH_jan, 0.18501680374145507, },
+[1203] = { city_lima, 1977, MONTH_jan, 0.1907871437072754, },
+[1204] = { city_lima, 1978, MONTH_jan, 0.19592239379882812, },
+[1205] = { city_lima, 1979, MONTH_jan, 0.20157331466674805, },
+[1206] = { city_lima, 1980, MONTH_jan, 0.19488487243652344, },
+[1207] = { city_lima, 1981, MONTH_jan, 0.21093349456787108, },
+[1208] = { city_lima, 1982, MONTH_jan, 0.22348915100097655, },
+[1209] = { city_lima, 1983, MONTH_jan, 0.2438044166564942, },
+[1210] = { city_lima, 1984, MONTH_jan, 0.24780765533447266, },
+[1211] = { city_lima, 1985, MONTH_jan, 0.2695251083374023, },
+[1212] = { city_lima, 1986, MONTH_jan, 0.269285831451416, },
+[1213] = { city_lima, 1987, MONTH_jan, 0.2701639747619629, },
+[1214] = { city_lima, 1988, MONTH_jan, 0.2654584693908691, },
+[1215] = { city_lima, 1989, MONTH_jan, 0.2911813545227051, },
+[1216] = { city_lima, 1990, MONTH_jan, 0.2930202865600586, },
+[1217] = { city_lima, 1991, MONTH_jan, 0.32345062255859375, },
+[1218] = { city_lima, 1992, MONTH_jan, 0.2806740951538086, },
+[1219] = { city_lima, 1993, MONTH_jan, 0.3119631767272949, },
+[1220] = { city_lima, 1994, MONTH_jan, 0.309783992767334, },
+[1221] = { city_lima, 1995, MONTH_jan, 0.28913646697998047, },
+[1222] = { city_lima, 1996, MONTH_jan, 0.2877692604064942, },
+[1223] = { city_lima, 1997, MONTH_jan, 0.28343660354614253, },
+[1224] = { city_lima, 1998, MONTH_jan, 0.2941726493835449, },
+[1225] = { city_lima, 1999, MONTH_jan, 0.301059398651123, },
+[1226] = { city_lima, 2000, MONTH_jan, 0.3249596405029297, },
+[1227] = { city_lima, 2001, MONTH_jan, 0.3552646255493164, },
+[1228] = { city_lima, 2002, MONTH_jan, 0.3535195541381836, },
+[1229] = { city_lima, 2003, MONTH_jan, 0.3660024261474609, },
+[1230] = { city_lima, 2004, MONTH_jan, 0.3229750442504883, },
+[1231] = { city_lima, 2005, MONTH_jan, 0.3184630966186523, },
+[1232] = { city_lima, 2006, MONTH_jan, 0.34128055572509763, },
+[1233] = { city_lima, 2007, MONTH_jan, 0.3103961563110352, },
+[1234] = { city_lima, 2008, MONTH_jan, 0.28021553039550784, },
+[1235] = { city_lima, 2009, MONTH_jan, 0.2833369064331055, },
+[1236] = { city_lima, 2010, MONTH_jan, 0.25213640213012695, },
+[1237] = { city_lima, 2011, MONTH_jan, 0.24354307174682618, },
+[1238] = { city_lima, 2012, MONTH_jan, 0.2401105308532715, },
+[1239] = { city_lima, 2013, MONTH_jan, 0.24136716842651368, },
+[1240] = { city_lima, 2014, MONTH_jan, 0.23940710067749024, },
+[1241] = { city_lima, 2015, MONTH_jan, 0.24024496078491211, },
+[1242] = { city_lima, 2016, MONTH_jan, 0.2257667922973633, },
+[1243] = { city_lima, 2017, MONTH_jan, 0.2617181777954102, },
+[1244] = { city_lima, 2018, MONTH_jan, 0.26795402526855466, },
+[1245] = { city_lima, 2019, MONTH_jan, 0.2662133598327637, },
+[1246] = { city_lima, 2020, MONTH_jan, 0.30744228363037107, },
+[1247] = { city_lima, 2021, MONTH_jan, 0.27741008758544916, },
+[1248] = { city_bucharest, 1965, MONTH_jan, 0.01066218614578247, },
+[1249] = { city_bucharest, 1966, MONTH_jan, 0.0102953040599823, },
+[1250] = { city_bucharest, 1967, MONTH_jan, 0.01315669298171997, },
+[1251] = { city_bucharest, 1968, MONTH_jan, 0.01308521866798401, },
+[1252] = { city_bucharest, 1969, MONTH_jan, 0.01615439534187317, },
+[1253] = { city_bucharest, 1970, MONTH_jan, 0.01898038387298584, },
+[1254] = { city_bucharest, 1971, MONTH_jan, 0.028985319137573243, },
+[1255] = { city_bucharest, 1972, MONTH_jan, 0.0449291467666626, },
+[1256] = { city_bucharest, 1973, MONTH_jan, 0.04244083404541016, },
+[1257] = { city_bucharest, 1974, MONTH_jan, 0.04733458995819092, },
+[1258] = { city_bucharest, 1975, MONTH_jan, 0.04793048858642578, },
+[1259] = { city_bucharest, 1976, MONTH_jan, 0.03851948022842407, },
+[1260] = { city_bucharest, 1977, MONTH_jan, 0.041872053146362304, },
+[1261] = { city_bucharest, 1978, MONTH_jan, 0.04415801525115967, },
+[1262] = { city_bucharest, 1979, MONTH_jan, 0.04625902652740479, },
+[1263] = { city_bucharest, 1980, MONTH_jan, 0.05105916500091553, },
+[1264] = { city_bucharest, 1981, MONTH_jan, 0.051341099739074705, },
+[1265] = { city_bucharest, 1982, MONTH_jan, 0.04819664478302002, },
+[1266] = { city_bucharest, 1983, MONTH_jan, 0.041099562644958496, },
+[1267] = { city_bucharest, 1984, MONTH_jan, 0.04670980453491211, },
+[1268] = { city_bucharest, 1985, MONTH_jan, 0.052583565711975096, },
+[1269] = { city_bucharest, 1986, MONTH_jan, 0.0437827730178833, },
+[1270] = { city_bucharest, 1987, MONTH_jan, 0.04377776622772217, },
+[1271] = { city_bucharest, 1988, MONTH_jan, 0.05665992736816406, },
+[1272] = { city_bucharest, 1989, MONTH_jan, 0.05235843181610107, },
+[1273] = { city_bucharest, 1990, MONTH_jan, 0.04590248107910156, },
+[1274] = { city_bucharest, 1991, MONTH_jan, 0.06907992839813232, },
+[1275] = { city_bucharest, 1992, MONTH_jan, 0.06283785343170166, },
+[1276] = { city_bucharest, 1993, MONTH_jan, 0.07054651737213134, },
+[1277] = { city_bucharest, 1994, MONTH_jan, 0.07641243934631348, },
+[1278] = { city_bucharest, 1995, MONTH_jan, 0.08897040367126464, },
+[1279] = { city_bucharest, 1996, MONTH_jan, 0.08448899269104004, },
+[1280] = { city_bucharest, 1997, MONTH_jan, 0.09899968147277832, },
+[1281] = { city_bucharest, 1998, MONTH_jan, 0.11665589332580567, },
+[1282] = { city_bucharest, 1999, MONTH_jan, 0.12841182708740234, },
+[1283] = { city_bucharest, 2000, MONTH_jan, 0.10396162033081056, },
+[1284] = { city_bucharest, 2001, MONTH_jan, 0.10312889099121093, },
+[1285] = { city_bucharest, 2002, MONTH_jan, 0.10615109443664551, },
+[1286] = { city_bucharest, 2003, MONTH_jan, 0.08716782569885254, },
+[1287] = { city_bucharest, 2004, MONTH_jan, 0.10407816886901855, },
+[1288] = { city_bucharest, 2005, MONTH_jan, 0.1262943172454834, },
+[1289] = { city_bucharest, 2006, MONTH_jan, 0.11172654151916504, },
+[1290] = { city_bucharest, 2007, MONTH_jan, 0.10032530784606934, },
+[1291] = { city_bucharest, 2008, MONTH_jan, 0.10699505805969238, },
+[1292] = { city_bucharest, 2009, MONTH_jan, 0.10989452362060549, },
+[1293] = { city_bucharest, 2010, MONTH_jan, 0.14136738777160646, },
+[1294] = { city_bucharest, 2011, MONTH_jan, 0.11033339500427246, },
+[1295] = { city_bucharest, 2012, MONTH_jan, 0.1043911838531494, },
+[1296] = { city_bucharest, 2013, MONTH_jan, 0.14889094352722168, },
+[1297] = { city_bucharest, 2014, MONTH_jan, 0.17897146224975585, },
+[1298] = { city_bucharest, 2015, MONTH_jan, 0.1851751136779785, },
+[1299] = { city_bucharest, 2016, MONTH_jan, 0.18943086624145508, },
+[1300] = { city_bucharest, 2017, MONTH_jan, 0.16684114456176757, },
+[1301] = { city_bucharest, 2018, MONTH_jan, 0.17542226791381835, },
+[1302] = { city_bucharest, 2019, MONTH_jan, 0.16837947845458984, },
+[1303] = { city_bucharest, 2020, MONTH_jan, 0.17579639434814454, },
+[1304] = { city_bucharest, 2021, MONTH_jan, 0.1748103141784668, },
+[1305] = { city_moscow, 1985, MONTH_jan, 0.049429731369018556, },
+[1306] = { city_moscow, 1986, MONTH_jan, 0.04996936798095703, },
+[1307] = { city_moscow, 1987, MONTH_jan, 0.0478553295135498, },
+[1308] = { city_moscow, 1988, MONTH_jan, 0.046569442749023436, },
+[1309] = { city_moscow, 1989, MONTH_jan, 0.04605263233184814, },
+[1310] = { city_moscow, 1990, MONTH_jan, 0.04898304462432861, },
+[1311] = { city_moscow, 1991, MONTH_jan, 0.050031914710998535, },
+[1312] = { city_moscow, 1992, MONTH_jan, 0.05336780071258545, },
+[1313] = { city_moscow, 1993, MONTH_jan, 0.0578029727935791, },
+[1314] = { city_moscow, 1994, MONTH_jan, 0.06394296646118164, },
+[1315] = { city_moscow, 1995, MONTH_jan, 0.06769153594970703, },
+[1316] = { city_moscow, 1996, MONTH_jan, 0.06157421112060547, },
+[1317] = { city_moscow, 1997, MONTH_jan, 0.06642446517944336, },
+[1318] = { city_moscow, 1998, MONTH_jan, 0.06699800491333008, },
+[1319] = { city_moscow, 1999, MONTH_jan, 0.0669929838180542, },
+[1320] = { city_moscow, 2000, MONTH_jan, 0.06728621482849122, },
+[1321] = { city_moscow, 2001, MONTH_jan, 0.06975970268249512, },
+[1322] = { city_moscow, 2002, MONTH_jan, 0.06492728233337403, },
+[1323] = { city_moscow, 2003, MONTH_jan, 0.060753769874572754, },
+[1324] = { city_moscow, 2004, MONTH_jan, 0.06754271030426025, },
+[1325] = { city_moscow, 2005, MONTH_jan, 0.06603690147399903, },
+[1326] = { city_moscow, 2006, MONTH_jan, 0.06309632778167724, },
+[1327] = { city_moscow, 2007, MONTH_jan, 0.06371350765228272, },
+[1328] = { city_moscow, 2008, MONTH_jan, 0.05867445468902588, },
+[1329] = { city_moscow, 2009, MONTH_jan, 0.06485455513000488, },
+[1330] = { city_moscow, 2010, MONTH_jan, 0.059318561553955075, },
+[1331] = { city_moscow, 2011, MONTH_jan, 0.05585275650024414, },
+[1332] = { city_moscow, 2012, MONTH_jan, 0.0555089282989502, },
+[1333] = { city_moscow, 2013, MONTH_jan, 0.061906685829162596, },
+[1334] = { city_moscow, 2014, MONTH_jan, 0.0588721513748169, },
+[1335] = { city_moscow, 2015, MONTH_jan, 0.05773604393005371, },
+[1336] = { city_moscow, 2016, MONTH_jan, 0.06171404838562012, },
+[1337] = { city_moscow, 2017, MONTH_jan, 0.061212682723999025, },
+[1338] = { city_moscow, 2018, MONTH_jan, 0.06046762466430664, },
+[1339] = { city_moscow, 2019, MONTH_jan, 0.06201229095458984, },
+[1340] = { city_moscow, 2020, MONTH_jan, 0.07069781303405762, },
+[1341] = { city_moscow, 2021, MONTH_jan, 0.06620289325714111, },
+[1342] = { city_seoul, 1965, MONTH_jan, 0.028078150749206544, },
+[1343] = { city_seoul, 1966, MONTH_jan, 0.03209915399551391, },
+[1344] = { city_seoul, 1967, MONTH_jan, 0.027027177810668945, },
+[1345] = { city_seoul, 1968, MONTH_jan, 0.022995977401733397, },
+[1346] = { city_seoul, 1969, MONTH_jan, 0.029266607761383057, },
+[1347] = { city_seoul, 1970, MONTH_jan, 0.02155395030975342, },
+[1348] = { city_seoul, 1971, MONTH_jan, 0.021541755199432373, },
+[1349] = { city_seoul, 1972, MONTH_jan, 0.02153486490249634, },
+[1350] = { city_seoul, 1973, MONTH_jan, 0.016347564458847046, },
+[1351] = { city_seoul, 1974, MONTH_jan, 0.02332909107208252, },
+[1352] = { city_seoul, 1975, MONTH_jan, 0.0187353253364563, },
+[1353] = { city_seoul, 1976, MONTH_jan, 0.017800427675247192, },
+[1354] = { city_seoul, 1977, MONTH_jan, 0.012054132223129272, },
+[1355] = { city_seoul, 1978, MONTH_jan, 0.01396591067314148, },
+[1356] = { city_seoul, 1979, MONTH_jan, 0.01539095163345337, },
+[1357] = { city_seoul, 1980, MONTH_jan, 0.012506891489028931, },
+[1358] = { city_seoul, 1981, MONTH_jan, 0.01615981578826904, },
+[1359] = { city_seoul, 1982, MONTH_jan, 0.010666205883026122, },
+[1360] = { city_seoul, 1983, MONTH_jan, 0.013120517730712891, },
+[1361] = { city_seoul, 1984, MONTH_jan, 0.010462830066680908, },
+[1362] = { city_seoul, 1985, MONTH_jan, 0.015088706016540528, },
+[1363] = { city_seoul, 1986, MONTH_jan, 0.013128159046173095, },
+[1364] = { city_seoul, 1987, MONTH_jan, 0.015100772380828855, },
+[1365] = { city_seoul, 1988, MONTH_jan, 0.006779970526695251, },
+[1366] = { city_seoul, 1989, MONTH_jan, 0.009301869869232178, },
+[1367] = { city_seoul, 1990, MONTH_jan, 0.012995164394378662, },
+[1368] = { city_seoul, 1991, MONTH_jan, 0.008552601337432861, },
+[1369] = { city_seoul, 1992, MONTH_jan, 0.006740404963493347, },
+[1370] = { city_seoul, 1993, MONTH_jan, 0.008389674425125122, },
+[1371] = { city_seoul, 1994, MONTH_jan, 0.004319862127304077, },
+[1372] = { city_seoul, 1995, MONTH_jan, 0.0051026403903961185, },
+[1373] = { city_seoul, 1996, MONTH_jan, 0.004361970424652099, },
+[1374] = { city_seoul, 1997, MONTH_jan, 0.004026863574981689, },
+[1375] = { city_seoul, 1998, MONTH_jan, 0.006597698330879211, },
+[1376] = { city_seoul, 1999, MONTH_jan, 0.00593690276145935, },
+[1377] = { city_seoul, 2000, MONTH_jan, 0.005420218110084534, },
+[1378] = { city_seoul, 2001, MONTH_jan, 0.0031219828128814697, },
+[1379] = { city_seoul, 2002, MONTH_jan, 0.0041857692599296566, },
+[1380] = { city_seoul, 2003, MONTH_jan, 0.005985386967658997, },
+[1381] = { city_seoul, 2004, MONTH_jan, 0.005346380472183228, },
+[1382] = { city_seoul, 2005, MONTH_jan, 0.00453538864850998, },
+[1383] = { city_seoul, 2006, MONTH_jan, 0.004502668976783752, },
+[1384] = { city_seoul, 2007, MONTH_jan, 0.005043082237243653, },
+[1385] = { city_seoul, 2008, MONTH_jan, 0.005293462872505188, },
+[1386] = { city_seoul, 2009, MONTH_jan, 0.005700946450233459, },
+[1387] = { city_seoul, 2010, MONTH_jan, 0.009195590615272522, },
+[1388] = { city_seoul, 2011, MONTH_jan, 0.012539451122283935, },
+[1389] = { city_seoul, 2012, MONTH_jan, 0.01288482666015625, },
+[1390] = { city_seoul, 2013, MONTH_jan, 0.015155628919601441, },
+[1391] = { city_seoul, 2014, MONTH_jan, 0.017640750408172607, },
+[1392] = { city_seoul, 2015, MONTH_jan, 0.019041190147399904, },
+[1393] = { city_seoul, 2016, MONTH_jan, 0.020268120765686036, },
+[1394] = { city_seoul, 2017, MONTH_jan, 0.024301843643188478, },
+[1395] = { city_seoul, 2018, MONTH_jan, 0.02746018409729004, },
+[1396] = { city_seoul, 2019, MONTH_jan, 0.029800333976745606, },
+[1397] = { city_seoul, 2020, MONTH_jan, 0.03142855405807495, },
+[1398] = { city_seoul, 2021, MONTH_jan, 0.037249858379364016, },
+[1399] = { city_madrid, 1965, MONTH_jan, 0.17253438949584962, },
+[1400] = { city_madrid, 1966, MONTH_jan, 0.2111080551147461, },
+[1401] = { city_madrid, 1967, MONTH_jan, 0.16279375076293945, },
+[1402] = { city_madrid, 1968, MONTH_jan, 0.1665400505065918, },
+[1403] = { city_madrid, 1969, MONTH_jan, 0.18460060119628902, },
+[1404] = { city_madrid, 1970, MONTH_jan, 0.1555931854248047, },
+[1405] = { city_madrid, 1971, MONTH_jan, 0.16187814712524415, },
+[1406] = { city_madrid, 1972, MONTH_jan, 0.16690349578857422, },
+[1407] = { city_madrid, 1973, MONTH_jan, 0.12429852485656738, },
+[1408] = { city_madrid, 1974, MONTH_jan, 0.12622825622558595, },
+[1409] = { city_madrid, 1975, MONTH_jan, 0.10509173393249512, },
+[1410] = { city_madrid, 1976, MONTH_jan, 0.07937400817871093, },
+[1411] = { city_madrid, 1977, MONTH_jan, 0.14476511001586914, },
+[1412] = { city_madrid, 1978, MONTH_jan, 0.14644488334655761, },
+[1413] = { city_madrid, 1979, MONTH_jan, 0.15632555961608888, },
+[1414] = { city_madrid, 1980, MONTH_jan, 0.09981171607971191, },
+[1415] = { city_madrid, 1981, MONTH_jan, 0.07407119750976564, },
+[1416] = { city_madrid, 1982, MONTH_jan, 0.08885108947753906, },
+[1417] = { city_madrid, 1983, MONTH_jan, 0.08936359405517579, },
+[1418] = { city_madrid, 1984, MONTH_jan, 0.10203864097595217, },
+[1419] = { city_madrid, 1985, MONTH_jan, 0.10242602348327637, },
+[1420] = { city_madrid, 1986, MONTH_jan, 0.08501705169677734, },
+[1421] = { city_madrid, 1987, MONTH_jan, 0.08477232933044433, },
+[1422] = { city_madrid, 1988, MONTH_jan, 0.10430665016174316, },
+[1423] = { city_madrid, 1989, MONTH_jan, 0.055330204963684085, },
+[1424] = { city_madrid, 1990, MONTH_jan, 0.07198870182037354, },
+[1425] = { city_madrid, 1991, MONTH_jan, 0.07452907562255859, },
+[1426] = { city_madrid, 1992, MONTH_jan, 0.05013250350952148, },
+[1427] = { city_madrid, 1993, MONTH_jan, 0.0662508773803711, },
+[1428] = { city_madrid, 1994, MONTH_jan, 0.07313819408416748, },
+[1429] = { city_madrid, 1995, MONTH_jan, 0.05938282489776611, },
+[1430] = { city_madrid, 1996, MONTH_jan, 0.09653098106384275, },
+[1431] = { city_madrid, 1997, MONTH_jan, 0.08211402893066407, },
+[1432] = { city_madrid, 1998, MONTH_jan, 0.07815210342407226, },
+[1433] = { city_madrid, 1999, MONTH_jan, 0.05629496574401856, },
+[1434] = { city_madrid, 2000, MONTH_jan, 0.07008230686187744, },
+[1435] = { city_madrid, 2001, MONTH_jan, 0.09167547225952148, },
+[1436] = { city_madrid, 2002, MONTH_jan, 0.0644627046585083, },
+[1437] = { city_madrid, 2003, MONTH_jan, 0.09758903503417969, },
+[1438] = { city_madrid, 2004, MONTH_jan, 0.0844090461730957, },
+[1439] = { city_madrid, 2005, MONTH_jan, 0.06923494338989258, },
+[1440] = { city_madrid, 2006, MONTH_jan, 0.08360922813415528, },
+[1441] = { city_madrid, 2007, MONTH_jan, 0.09064817428588867, },
+[1442] = { city_madrid, 2008, MONTH_jan, 0.09948370933532717, },
+[1443] = { city_madrid, 2009, MONTH_jan, 0.12662100791931152, },
+[1444] = { city_madrid, 2010, MONTH_jan, 0.16742803573608397, },
+[1445] = { city_madrid, 2011, MONTH_jan, 0.15473434448242188, },
+[1446] = { city_madrid, 2012, MONTH_jan, 0.15889772415161132, },
+[1447] = { city_madrid, 2013, MONTH_jan, 0.19901302337646484, },
+[1448] = { city_madrid, 2014, MONTH_jan, 0.2008855438232422, },
+[1449] = { city_madrid, 2015, MONTH_jan, 0.17507902145385743, },
+[1450] = { city_madrid, 2016, MONTH_jan, 0.18609848022460937, },
+[1451] = { city_madrid, 2017, MONTH_jan, 0.15682350158691405, },
+[1452] = { city_madrid, 2018, MONTH_jan, 0.18230533599853516, },
+[1453] = { city_madrid, 2019, MONTH_jan, 0.17375850677490234, },
+[1454] = { city_madrid, 2020, MONTH_jan, 0.22102653503417968, },
+[1455] = { city_madrid, 2021, MONTH_jan, 0.22341663360595704, },
+[1456] = { city_bangkok, 1965, MONTH_jan, 0.08098695755004882, },
+[1457] = { city_bangkok, 1966, MONTH_jan, 0.08597766876220703, },
+[1458] = { city_bangkok, 1967, MONTH_jan, 0.09705655097961426, },
+[1459] = { city_bangkok, 1968, MONTH_jan, 0.07769661903381347, },
+[1460] = { city_bangkok, 1969, MONTH_jan, 0.054768366813659666, },
+[1461] = { city_bangkok, 1970, MONTH_jan, 0.07854705810546875, },
+[1462] = { city_bangkok, 1971, MONTH_jan, 0.08126887321472168, },
+[1463] = { city_bangkok, 1972, MONTH_jan, 0.056506228446960446, },
+[1464] = { city_bangkok, 1973, MONTH_jan, 0.05797545909881592, },
+[1465] = { city_bangkok, 1974, MONTH_jan, 0.07285396575927734, },
+[1466] = { city_bangkok, 1975, MONTH_jan, 0.09127364158630373, },
+[1467] = { city_bangkok, 1976, MONTH_jan, 0.09248512268066406, },
+[1468] = { city_bangkok, 1977, MONTH_jan, 0.07547190666198732, },
+[1469] = { city_bangkok, 1978, MONTH_jan, 0.045728025436401365, },
+[1470] = { city_bangkok, 1979, MONTH_jan, 0.06653911590576173, },
+[1471] = { city_bangkok, 1980, MONTH_jan, 0.02589577913284302, },
+[1472] = { city_bangkok, 1981, MONTH_jan, 0.06015193939208984, },
+[1473] = { city_bangkok, 1982, MONTH_jan, 0.07570967674255372, },
+[1474] = { city_bangkok, 1983, MONTH_jan, 0.06589135646820068, },
+[1475] = { city_bangkok, 1984, MONTH_jan, 0.06626251220703125, },
+[1476] = { city_bangkok, 1985, MONTH_jan, 0.05735532283782959, },
+[1477] = { city_bangkok, 1986, MONTH_jan, 0.08074996948242187, },
+[1478] = { city_bangkok, 1987, MONTH_jan, 0.05194031715393067, },
+[1479] = { city_bangkok, 1988, MONTH_jan, 0.04262178421020508, },
+[1480] = { city_bangkok, 1989, MONTH_jan, 0.05435298919677734, },
+[1481] = { city_bangkok, 1990, MONTH_jan, 0.04073401927947998, },
+[1482] = { city_bangkok, 1991, MONTH_jan, 0.03400254249572754, },
+[1483] = { city_bangkok, 1992, MONTH_jan, 0.028629646301269532, },
+[1484] = { city_bangkok, 1993, MONTH_jan, 0.021971902847290038, },
+[1485] = { city_bangkok, 1994, MONTH_jan, 0.02397928237915039, },
+[1486] = { city_bangkok, 1995, MONTH_jan, 0.032421882152557376, },
+[1487] = { city_bangkok, 1996, MONTH_jan, 0.031586606502532956, },
+[1488] = { city_bangkok, 1997, MONTH_jan, 0.02977715253829956, },
+[1489] = { city_bangkok, 1998, MONTH_jan, 0.02375905752182007, },
+[1490] = { city_bangkok, 1999, MONTH_jan, 0.01608154773712158, },
+[1491] = { city_bangkok, 2000, MONTH_jan, 0.025746095180511474, },
+[1492] = { city_bangkok, 2001, MONTH_jan, 0.025714476108551026, },
+[1493] = { city_bangkok, 2002, MONTH_jan, 0.02818033695220947, },
+[1494] = { city_bangkok, 2003, MONTH_jan, 0.027227082252502442, },
+[1495] = { city_bangkok, 2004, MONTH_jan, 0.02313349723815918, },
+[1496] = { city_bangkok, 2005, MONTH_jan, 0.0227521014213562, },
+[1497] = { city_bangkok, 2006, MONTH_jan, 0.029778263568878173, },
+[1498] = { city_bangkok, 2007, MONTH_jan, 0.029363691806793213, },
+[1499] = { city_bangkok, 2008, MONTH_jan, 0.029482808113098145, },
+[1500] = { city_bangkok, 2009, MONTH_jan, 0.030501224994659425, },
+[1501] = { city_bangkok, 2010, MONTH_jan, 0.028064701557159424, },
+[1502] = { city_bangkok, 2011, MONTH_jan, 0.03457339763641357, },
+[1503] = { city_bangkok, 2012, MONTH_jan, 0.03770574569702149, },
+[1504] = { city_bangkok, 2013, MONTH_jan, 0.0385915493965149, },
+[1505] = { city_bangkok, 2014, MONTH_jan, 0.04258995532989502, },
+[1506] = { city_bangkok, 2015, MONTH_jan, 0.04190981388092041, },
+[1507] = { city_bangkok, 2016, MONTH_jan, 0.045999755859375, },
+[1508] = { city_bangkok, 2017, MONTH_jan, 0.05306326389312744, },
+[1509] = { city_bangkok, 2018, MONTH_jan, 0.06380221366882324, },
+[1510] = { city_bangkok, 2019, MONTH_jan, 0.06956219673156738, },
+[1511] = { city_bangkok, 2020, MONTH_jan, 0.0699142074584961, },
+[1512] = { city_bangkok, 2021, MONTH_jan, 0.07113617897033692, },
+[1513] = { city_ankara, 1965, MONTH_jan, 0.07396099090576172, },
+[1514] = { city_ankara, 1966, MONTH_jan, 0.06960969448089599, },
+[1515] = { city_ankara, 1967, MONTH_jan, 0.06997379779815674, },
+[1516] = { city_ankara, 1968, MONTH_jan, 0.0787749719619751, },
+[1517] = { city_ankara, 1969, MONTH_jan, 0.07791776180267335, },
+[1518] = { city_ankara, 1970, MONTH_jan, 0.06475101470947266, },
+[1519] = { city_ankara, 1971, MONTH_jan, 0.05071615219116211, },
+[1520] = { city_ankara, 1972, MONTH_jan, 0.05530313968658447, },
+[1521] = { city_ankara, 1973, MONTH_jan, 0.04050086498260498, },
+[1522] = { city_ankara, 1974, MONTH_jan, 0.05047706604003906, },
+[1523] = { city_ankara, 1975, MONTH_jan, 0.07770755290985107, },
+[1524] = { city_ankara, 1976, MONTH_jan, 0.09604342460632324, },
+[1525] = { city_ankara, 1977, MONTH_jan, 0.09040463447570801, },
+[1526] = { city_ankara, 1978, MONTH_jan, 0.0920789909362793, },
+[1527] = { city_ankara, 1979, MONTH_jan, 0.10752221107482911, },
+[1528] = { city_ankara, 1980, MONTH_jan, 0.11387236595153809, },
+[1529] = { city_ankara, 1981, MONTH_jan, 0.1250270175933838, },
+[1530] = { city_ankara, 1982, MONTH_jan, 0.12745107650756837, },
+[1531] = { city_ankara, 1983, MONTH_jan, 0.09873126983642579, },
+[1532] = { city_ankara, 1984, MONTH_jan, 0.11116494178771973, },
+[1533] = { city_ankara, 1985, MONTH_jan, 0.09229307174682616, },
+[1534] = { city_ankara, 1986, MONTH_jan, 0.08374287605285645, },
+[1535] = { city_ankara, 1987, MONTH_jan, 0.11513276100158691, },
+[1536] = { city_ankara, 1988, MONTH_jan, 0.16268531799316407, },
+[1537] = { city_ankara, 1989, MONTH_jan, 0.10282753944396973, },
+[1538] = { city_ankara, 1990, MONTH_jan, 0.12219701766967772, },
+[1539] = { city_ankara, 1991, MONTH_jan, 0.11752680778503417, },
+[1540] = { city_ankara, 1992, MONTH_jan, 0.1307958698272705, },
+[1541] = { city_ankara, 1993, MONTH_jan, 0.15372352600097655, },
+[1542] = { city_ankara, 1994, MONTH_jan, 0.14304976463317873, },
+[1543] = { city_ankara, 1995, MONTH_jan, 0.14924197196960448, },
+[1544] = { city_ankara, 1996, MONTH_jan, 0.15543943405151367, },
+[1545] = { city_ankara, 1997, MONTH_jan, 0.14665958404541016, },
+[1546] = { city_ankara, 1998, MONTH_jan, 0.15141606330871582, },
+[1547] = { city_ankara, 1999, MONTH_jan, 0.12753751754760742, },
+[1548] = { city_ankara, 2000, MONTH_jan, 0.10657172203063965, },
+[1549] = { city_ankara, 2001, MONTH_jan, 0.09108513832092284, },
+[1550] = { city_ankara, 2002, MONTH_jan, 0.11535796165466308, },
+[1551] = { city_ankara, 2003, MONTH_jan, 0.11330117225646973, },
+[1552] = { city_ankara, 2004, MONTH_jan, 0.1369169235229492, },
+[1553] = { city_ankara, 2005, MONTH_jan, 0.11418582916259766, },
+[1554] = { city_ankara, 2006, MONTH_jan, 0.11456920623779297, },
+[1555] = { city_ankara, 2007, MONTH_jan, 0.08779489517211914, },
+[1556] = { city_ankara, 2008, MONTH_jan, 0.08211599349975586, },
+[1557] = { city_ankara, 2009, MONTH_jan, 0.08923343658447265, },
+[1558] = { city_ankara, 2010, MONTH_jan, 0.12280028343200683, },
+[1559] = { city_ankara, 2011, MONTH_jan, 0.11913782119750976, },
+[1560] = { city_ankara, 2012, MONTH_jan, 0.1252276611328125, },
+[1561] = { city_ankara, 2013, MONTH_jan, 0.1331322193145752, },
+[1562] = { city_ankara, 2014, MONTH_jan, 0.09769227027893067, },
+[1563] = { city_ankara, 2015, MONTH_jan, 0.1406381320953369, },
+[1564] = { city_ankara, 2016, MONTH_jan, 0.14479534149169923, },
+[1565] = { city_ankara, 2017, MONTH_jan, 0.13170472145080567, },
+[1566] = { city_ankara, 2018, MONTH_jan, 0.1484662437438965, },
+[1567] = { city_ankara, 2019, MONTH_jan, 0.1925493621826172, },
+[1568] = { city_ankara, 2020, MONTH_jan, 0.19150110244750976, },
+[1569] = { city_ankara, 2021, MONTH_jan, 0.16517044067382813, },
+[1570] = { city_kyiv, 1985, MONTH_jan, 0.011391943693161011, },
+[1571] = { city_kyiv, 1986, MONTH_jan, 0.01128100037574768, },
+[1572] = { city_kyiv, 1987, MONTH_jan, 0.009946019649505616, },
+[1573] = { city_kyiv, 1988, MONTH_jan, 0.012357125282287598, },
+[1574] = { city_kyiv, 1989, MONTH_jan, 0.010963214635848999, },
+[1575] = { city_kyiv, 1990, MONTH_jan, 0.009731683731079101, },
+[1576] = { city_kyiv, 1991, MONTH_jan, 0.01156305193901062, },
+[1577] = { city_kyiv, 1992, MONTH_jan, 0.00921140432357788, },
+[1578] = { city_kyiv, 1993, MONTH_jan, 0.014715107679367062, },
+[1579] = { city_kyiv, 1994, MONTH_jan, 0.018900632858276367, },
+[1580] = { city_kyiv, 1995, MONTH_jan, 0.015910707712173462, },
+[1581] = { city_kyiv, 1996, MONTH_jan, 0.014804906845092771, },
+[1582] = { city_kyiv, 1997, MONTH_jan, 0.017526177167892457, },
+[1583] = { city_kyiv, 1998, MONTH_jan, 0.02900869369506836, },
+[1584] = { city_kyiv, 1999, MONTH_jan, 0.026803247928619385, },
+[1585] = { city_kyiv, 2000, MONTH_jan, 0.021038007736206055, },
+[1586] = { city_kyiv, 2001, MONTH_jan, 0.022646660804748534, },
+[1587] = { city_kyiv, 2002, MONTH_jan, 0.018132338523864745, },
+[1588] = { city_kyiv, 2003, MONTH_jan, 0.01688597321510315, },
+[1589] = { city_kyiv, 2004, MONTH_jan, 0.021416072845458985, },
+[1590] = { city_kyiv, 2005, MONTH_jan, 0.022117321491241456, },
+[1591] = { city_kyiv, 2006, MONTH_jan, 0.022632830142974857, },
+[1592] = { city_kyiv, 2007, MONTH_jan, 0.018360310792922975, },
+[1593] = { city_kyiv, 2008, MONTH_jan, 0.02132999897003174, },
+[1594] = { city_kyiv, 2009, MONTH_jan, 0.025476245880126952, },
+[1595] = { city_kyiv, 2010, MONTH_jan, 0.02610059976577759, },
+[1596] = { city_kyiv, 2011, MONTH_jan, 0.020985288619995116, },
+[1597] = { city_kyiv, 2012, MONTH_jan, 0.021388649940490723, },
+[1598] = { city_kyiv, 2013, MONTH_jan, 0.03011183738708496, },
+[1599] = { city_kyiv, 2014, MONTH_jan, 0.022846858501434326, },
+[1600] = { city_kyiv, 2015, MONTH_jan, 0.01913326621055603, },
+[1601] = { city_kyiv, 2016, MONTH_jan, 0.02366657018661499, },
+[1602] = { city_kyiv, 2017, MONTH_jan, 0.02975400447845459, },
+[1603] = { city_kyiv, 2018, MONTH_jan, 0.03421227693557739, },
+[1604] = { city_kyiv, 2019, MONTH_jan, 0.03290361166000366, },
+[1605] = { city_kyiv, 2020, MONTH_jan, 0.048960785865783694, },
+[1606] = { city_kyiv, 2021, MONTH_jan, 0.06100841522216797, },
+[1607] = { city_dubai, 1965, MONTH_jan, 0, },
+[1608] = { city_dubai, 1966, MONTH_jan, 0, },
+[1609] = { city_dubai, 1967, MONTH_jan, 0, },
+[1610] = { city_dubai, 1968, MONTH_jan, 0, },
+[1611] = { city_dubai, 1969, MONTH_jan, 0, },
+[1612] = { city_dubai, 1970, MONTH_jan, 0, },
+[1613] = { city_dubai, 1971, MONTH_jan, 0, },
+[1614] = { city_dubai, 1972, MONTH_jan, 0, },
+[1615] = { city_dubai, 1973, MONTH_jan, 0, },
+[1616] = { city_dubai, 1974, MONTH_jan, 0, },
+[1617] = { city_dubai, 1975, MONTH_jan, 0, },
+[1618] = { city_dubai, 1976, MONTH_jan, 0, },
+[1619] = { city_dubai, 1977, MONTH_jan, 0, },
+[1620] = { city_dubai, 1978, MONTH_jan, 0, },
+[1621] = { city_dubai, 1979, MONTH_jan, 0, },
+[1622] = { city_dubai, 1980, MONTH_jan, 0, },
+[1623] = { city_dubai, 1981, MONTH_jan, 0, },
+[1624] = { city_dubai, 1982, MONTH_jan, 0, },
+[1625] = { city_dubai, 1983, MONTH_jan, 0, },
+[1626] = { city_dubai, 1984, MONTH_jan, 0, },
+[1627] = { city_dubai, 1985, MONTH_jan, 0, },
+[1628] = { city_dubai, 1986, MONTH_jan, 0, },
+[1629] = { city_dubai, 1987, MONTH_jan, 0, },
+[1630] = { city_dubai, 1988, MONTH_jan, 0, },
+[1631] = { city_dubai, 1989, MONTH_jan, 0, },
+[1632] = { city_dubai, 1990, MONTH_jan, 0, },
+[1633] = { city_dubai, 1991, MONTH_jan, 0, },
+[1634] = { city_dubai, 1992, MONTH_jan, 0, },
+[1635] = { city_dubai, 1993, MONTH_jan, 0, },
+[1636] = { city_dubai, 1994, MONTH_jan, 0, },
+[1637] = { city_dubai, 1995, MONTH_jan, 0, },
+[1638] = { city_dubai, 1996, MONTH_jan, 0, },
+[1639] = { city_dubai, 1997, MONTH_jan, 0, },
+[1640] = { city_dubai, 1998, MONTH_jan, 0, },
+[1641] = { city_dubai, 1999, MONTH_jan, 0, },
+[1642] = { city_dubai, 2000, MONTH_jan, 0, },
+[1643] = { city_dubai, 2001, MONTH_jan, 0, },
+[1644] = { city_dubai, 2002, MONTH_jan, 0, },
+[1645] = { city_dubai, 2003, MONTH_jan, 0, },
+[1646] = { city_dubai, 2004, MONTH_jan, 0, },
+[1647] = { city_dubai, 2005, MONTH_jan, 0, },
+[1648] = { city_dubai, 2006, MONTH_jan, 0, },
+[1649] = { city_dubai, 2007, MONTH_jan, 0, },
+[1650] = { city_dubai, 2008, MONTH_jan, 0, },
+[1651] = { city_dubai, 2009, MONTH_jan, 1.91772636026144e-5, },
+[1652] = { city_dubai, 2010, MONTH_jan, 5.25837112218141e-5, },
+[1653] = { city_dubai, 2011, MONTH_jan, 5.59225212782621e-5, },
+[1654] = { city_dubai, 2012, MONTH_jan, 6.18843780830502e-5, },
+[1655] = { city_dubai, 2013, MONTH_jan, 2.0930014550685878e-4, },
+[1656] = { city_dubai, 2014, MONTH_jan, 7.691703736782074e-4, },
+[1657] = { city_dubai, 2015, MONTH_jan, 6.952883303165436e-4, },
+[1658] = { city_dubai, 2016, MONTH_jan, 6.702232360839843e-4, },
+[1659] = { city_dubai, 2017, MONTH_jan, 0.0015412747859954834, },
+[1660] = { city_dubai, 2018, MONTH_jan, 0.0027376759052276612, },
+[1661] = { city_dubai, 2019, MONTH_jan, 0.007918253540992737, },
+[1662] = { city_dubai, 2020, MONTH_jan, 0.010150353908538818, },
+[1663] = { city_san_francisco, 1965, MONTH_jan, 0.043688697814941405, },
+[1664] = { city_denver, 1965, MONTH_jan, 0.043688697814941405, },
+[1665] = { city_washington, 1965, MONTH_jan, 0.043688697814941405, },
+[1666] = { city_chicago, 1965, MONTH_jan, 0.043688697814941405, },
+[1667] = { city_san_francisco, 1966, MONTH_jan, 0.041714019775390625, },
+[1668] = { city_denver, 1966, MONTH_jan, 0.041714019775390625, },
+[1669] = { city_washington, 1966, MONTH_jan, 0.041714019775390625, },
+[1670] = { city_chicago, 1966, MONTH_jan, 0.041714019775390625, },
+[1671] = { city_san_francisco, 1967, MONTH_jan, 0.04542215824127197, },
+[1672] = { city_denver, 1967, MONTH_jan, 0.04542215824127197, },
+[1673] = { city_washington, 1967, MONTH_jan, 0.04542215824127197, },
+[1674] = { city_chicago, 1967, MONTH_jan, 0.04542215824127197, },
+[1675] = { city_san_francisco, 1968, MONTH_jan, 0.04330973625183106, },
+[1676] = { city_denver, 1968, MONTH_jan, 0.04330973625183106, },
+[1677] = { city_washington, 1968, MONTH_jan, 0.04330973625183106, },
+[1678] = { city_chicago, 1968, MONTH_jan, 0.04330973625183106, },
+[1679] = { city_san_francisco, 1969, MONTH_jan, 0.04598878383636475, },
+[1680] = { city_denver, 1969, MONTH_jan, 0.04598878383636475, },
+[1681] = { city_washington, 1969, MONTH_jan, 0.04598878383636475, },
+[1682] = { city_chicago, 1969, MONTH_jan, 0.04598878383636475, },
+[1683] = { city_san_francisco, 1970, MONTH_jan, 0.044008030891418456, },
+[1684] = { city_denver, 1970, MONTH_jan, 0.044008030891418456, },
+[1685] = { city_washington, 1970, MONTH_jan, 0.044008030891418456, },
+[1686] = { city_chicago, 1970, MONTH_jan, 0.044008030891418456, },
+[1687] = { city_san_francisco, 1971, MONTH_jan, 0.046144747734069826, },
+[1688] = { city_denver, 1971, MONTH_jan, 0.046144747734069826, },
+[1689] = { city_washington, 1971, MONTH_jan, 0.046144747734069826, },
+[1690] = { city_chicago, 1971, MONTH_jan, 0.046144747734069826, },
+[1691] = { city_san_francisco, 1972, MONTH_jan, 0.045260472297668455, },
+[1692] = { city_denver, 1972, MONTH_jan, 0.045260472297668455, },
+[1693] = { city_washington, 1972, MONTH_jan, 0.045260472297668455, },
+[1694] = { city_chicago, 1972, MONTH_jan, 0.045260472297668455, },
+[1695] = { city_san_francisco, 1973, MONTH_jan, 0.043547596931457516, },
+[1696] = { city_denver, 1973, MONTH_jan, 0.043547596931457516, },
+[1697] = { city_washington, 1973, MONTH_jan, 0.043547596931457516, },
+[1698] = { city_chicago, 1973, MONTH_jan, 0.043547596931457516, },
+[1699] = { city_san_francisco, 1974, MONTH_jan, 0.04902864933013916, },
+[1700] = { city_denver, 1974, MONTH_jan, 0.04902864933013916, },
+[1701] = { city_washington, 1974, MONTH_jan, 0.04902864933013916, },
+[1702] = { city_chicago, 1974, MONTH_jan, 0.04902864933013916, },
+[1703] = { city_san_francisco, 1975, MONTH_jan, 0.050069751739501955, },
+[1704] = { city_denver, 1975, MONTH_jan, 0.050069751739501955, },
+[1705] = { city_washington, 1975, MONTH_jan, 0.050069751739501955, },
+[1706] = { city_chicago, 1975, MONTH_jan, 0.050069751739501955, },
+[1707] = { city_san_francisco, 1976, MONTH_jan, 0.045527076721191405, },
+[1708] = { city_denver, 1976, MONTH_jan, 0.045527076721191405, },
+[1709] = { city_washington, 1976, MONTH_jan, 0.045527076721191405, },
+[1710] = { city_chicago, 1976, MONTH_jan, 0.045527076721191405, },
+[1711] = { city_san_francisco, 1977, MONTH_jan, 0.03549314022064209, },
+[1712] = { city_denver, 1977, MONTH_jan, 0.03549314022064209, },
+[1713] = { city_washington, 1977, MONTH_jan, 0.03549314022064209, },
+[1714] = { city_chicago, 1977, MONTH_jan, 0.03549314022064209, },
+[1715] = { city_san_francisco, 1978, MONTH_jan, 0.043275704383850096, },
+[1716] = { city_denver, 1978, MONTH_jan, 0.043275704383850096, },
+[1717] = { city_washington, 1978, MONTH_jan, 0.043275704383850096, },
+[1718] = { city_chicago, 1978, MONTH_jan, 0.043275704383850096, },
+[1719] = { city_san_francisco, 1979, MONTH_jan, 0.0429140567779541, },
+[1720] = { city_denver, 1979, MONTH_jan, 0.0429140567779541, },
+[1721] = { city_washington, 1979, MONTH_jan, 0.0429140567779541, },
+[1722] = { city_chicago, 1979, MONTH_jan, 0.0429140567779541, },
+[1723] = { city_san_francisco, 1980, MONTH_jan, 0.044192209243774414, },
+[1724] = { city_denver, 1980, MONTH_jan, 0.044192209243774414, },
+[1725] = { city_washington, 1980, MONTH_jan, 0.044192209243774414, },
+[1726] = { city_chicago, 1980, MONTH_jan, 0.044192209243774414, },
+[1727] = { city_san_francisco, 1981, MONTH_jan, 0.04336748600006104, },
+[1728] = { city_denver, 1981, MONTH_jan, 0.04336748600006104, },
+[1729] = { city_washington, 1981, MONTH_jan, 0.04336748600006104, },
+[1730] = { city_chicago, 1981, MONTH_jan, 0.04336748600006104, },
+[1731] = { city_san_francisco, 1982, MONTH_jan, 0.05256711483001709, },
+[1732] = { city_denver, 1982, MONTH_jan, 0.05256711483001709, },
+[1733] = { city_washington, 1982, MONTH_jan, 0.05256711483001709, },
+[1734] = { city_chicago, 1982, MONTH_jan, 0.05256711483001709, },
+[1735] = { city_san_francisco, 1983, MONTH_jan, 0.056848154067993165, },
+[1736] = { city_denver, 1983, MONTH_jan, 0.056848154067993165, },
+[1737] = { city_washington, 1983, MONTH_jan, 0.056848154067993165, },
+[1738] = { city_chicago, 1983, MONTH_jan, 0.056848154067993165, },
+[1739] = { city_san_francisco, 1984, MONTH_jan, 0.05334360599517822, },
+[1740] = { city_denver, 1984, MONTH_jan, 0.05334360599517822, },
+[1741] = { city_washington, 1984, MONTH_jan, 0.05334360599517822, },
+[1742] = { city_chicago, 1984, MONTH_jan, 0.05334360599517822, },
+[1743] = { city_san_francisco, 1985, MONTH_jan, 0.04765776157379151, },
+[1744] = { city_denver, 1985, MONTH_jan, 0.04765776157379151, },
+[1745] = { city_washington, 1985, MONTH_jan, 0.04765776157379151, },
+[1746] = { city_chicago, 1985, MONTH_jan, 0.04765776157379151, },
+[1747] = { city_san_francisco, 1986, MONTH_jan, 0.048984041213989256, },
+[1748] = { city_denver, 1986, MONTH_jan, 0.048984041213989256, },
+[1749] = { city_washington, 1986, MONTH_jan, 0.048984041213989256, },
+[1750] = { city_chicago, 1986, MONTH_jan, 0.048984041213989256, },
+[1751] = { city_san_francisco, 1987, MONTH_jan, 0.04214753627777099, },
+[1752] = { city_denver, 1987, MONTH_jan, 0.04214753627777099, },
+[1753] = { city_washington, 1987, MONTH_jan, 0.04214753627777099, },
+[1754] = { city_chicago, 1987, MONTH_jan, 0.04214753627777099, },
+[1755] = { city_san_francisco, 1988, MONTH_jan, 0.03664999723434448, },
+[1756] = { city_denver, 1988, MONTH_jan, 0.03664999723434448, },
+[1757] = { city_washington, 1988, MONTH_jan, 0.03664999723434448, },
+[1758] = { city_chicago, 1988, MONTH_jan, 0.03664999723434448, },
+[1759] = { city_san_francisco, 1989, MONTH_jan, 0.043015427589416504, },
+[1760] = { city_denver, 1989, MONTH_jan, 0.043015427589416504, },
+[1761] = { city_washington, 1989, MONTH_jan, 0.043015427589416504, },
+[1762] = { city_chicago, 1989, MONTH_jan, 0.043015427589416504, },
+[1763] = { city_san_francisco, 1990, MONTH_jan, 0.047246766090393064, },
+[1764] = { city_denver, 1990, MONTH_jan, 0.047246766090393064, },
+[1765] = { city_washington, 1990, MONTH_jan, 0.047246766090393064, },
+[1766] = { city_chicago, 1990, MONTH_jan, 0.047246766090393064, },
+[1767] = { city_san_francisco, 1991, MONTH_jan, 0.0472428560256958, },
+[1768] = { city_denver, 1991, MONTH_jan, 0.0472428560256958, },
+[1769] = { city_washington, 1991, MONTH_jan, 0.0472428560256958, },
+[1770] = { city_chicago, 1991, MONTH_jan, 0.0472428560256958, },
+[1771] = { city_san_francisco, 1992, MONTH_jan, 0.042612309455871585, },
+[1772] = { city_denver, 1992, MONTH_jan, 0.042612309455871585, },
+[1773] = { city_washington, 1992, MONTH_jan, 0.042612309455871585, },
+[1774] = { city_chicago, 1992, MONTH_jan, 0.042612309455871585, },
+[1775] = { city_san_francisco, 1993, MONTH_jan, 0.045714097023010256, },
+[1776] = { city_denver, 1993, MONTH_jan, 0.045714097023010256, },
+[1777] = { city_washington, 1993, MONTH_jan, 0.045714097023010256, },
+[1778] = { city_chicago, 1993, MONTH_jan, 0.045714097023010256, },
+[1779] = { city_san_francisco, 1994, MONTH_jan, 0.0425481653213501, },
+[1780] = { city_denver, 1994, MONTH_jan, 0.0425481653213501, },
+[1781] = { city_washington, 1994, MONTH_jan, 0.0425481653213501, },
+[1782] = { city_chicago, 1994, MONTH_jan, 0.0425481653213501, },
+[1783] = { city_san_francisco, 1995, MONTH_jan, 0.04762141227722168, },
+[1784] = { city_denver, 1995, MONTH_jan, 0.04762141227722168, },
+[1785] = { city_washington, 1995, MONTH_jan, 0.04762141227722168, },
+[1786] = { city_chicago, 1995, MONTH_jan, 0.04762141227722168, },
+[1787] = { city_san_francisco, 1996, MONTH_jan, 0.050185713768005374, },
+[1788] = { city_denver, 1996, MONTH_jan, 0.050185713768005374, },
+[1789] = { city_washington, 1996, MONTH_jan, 0.050185713768005374, },
+[1790] = { city_chicago, 1996, MONTH_jan, 0.050185713768005374, },
+[1791] = { city_san_francisco, 1997, MONTH_jan, 0.051152210235595706, },
+[1792] = { city_denver, 1997, MONTH_jan, 0.051152210235595706, },
+[1793] = { city_washington, 1997, MONTH_jan, 0.051152210235595706, },
+[1794] = { city_chicago, 1997, MONTH_jan, 0.051152210235595706, },
+[1795] = { city_san_francisco, 1998, MONTH_jan, 0.046957569122314455, },
+[1796] = { city_denver, 1998, MONTH_jan, 0.046957569122314455, },
+[1797] = { city_washington, 1998, MONTH_jan, 0.046957569122314455, },
+[1798] = { city_chicago, 1998, MONTH_jan, 0.046957569122314455, },
+[1799] = { city_san_francisco, 1999, MONTH_jan, 0.04583451747894287, },
+[1800] = { city_denver, 1999, MONTH_jan, 0.04583451747894287, },
+[1801] = { city_washington, 1999, MONTH_jan, 0.04583451747894287, },
+[1802] = { city_chicago, 1999, MONTH_jan, 0.04583451747894287, },
+[1803] = { city_san_francisco, 2000, MONTH_jan, 0.0402735710144043, },
+[1804] = { city_denver, 2000, MONTH_jan, 0.0402735710144043, },
+[1805] = { city_washington, 2000, MONTH_jan, 0.0402735710144043, },
+[1806] = { city_chicago, 2000, MONTH_jan, 0.0402735710144043, },
+[1807] = { city_san_francisco, 2001, MONTH_jan, 0.034213709831237796, },
+[1808] = { city_denver, 2001, MONTH_jan, 0.034213709831237796, },
+[1809] = { city_washington, 2001, MONTH_jan, 0.034213709831237796, },
+[1810] = { city_chicago, 2001, MONTH_jan, 0.034213709831237796, },
+[1811] = { city_san_francisco, 2002, MONTH_jan, 0.040155067443847656, },
+[1812] = { city_denver, 2002, MONTH_jan, 0.040155067443847656, },
+[1813] = { city_washington, 2002, MONTH_jan, 0.040155067443847656, },
+[1814] = { city_chicago, 2002, MONTH_jan, 0.040155067443847656, },
+[1815] = { city_san_francisco, 2003, MONTH_jan, 0.041800403594970705, },
+[1816] = { city_denver, 2003, MONTH_jan, 0.041800403594970705, },
+[1817] = { city_washington, 2003, MONTH_jan, 0.041800403594970705, },
+[1818] = { city_chicago, 2003, MONTH_jan, 0.041800403594970705, },
+[1819] = { city_san_francisco, 2004, MONTH_jan, 0.04101511478424072, },
+[1820] = { city_denver, 2004, MONTH_jan, 0.04101511478424072, },
+[1821] = { city_washington, 2004, MONTH_jan, 0.04101511478424072, },
+[1822] = { city_chicago, 2004, MONTH_jan, 0.04101511478424072, },
+[1823] = { city_san_francisco, 2005, MONTH_jan, 0.04219231128692627, },
+[1824] = { city_denver, 2005, MONTH_jan, 0.04219231128692627, },
+[1825] = { city_washington, 2005, MONTH_jan, 0.04219231128692627, },
+[1826] = { city_chicago, 2005, MONTH_jan, 0.04219231128692627, },
+[1827] = { city_san_francisco, 2006, MONTH_jan, 0.04678860664367676, },
+[1828] = { city_denver, 2006, MONTH_jan, 0.04678860664367676, },
+[1829] = { city_washington, 2006, MONTH_jan, 0.04678860664367676, },
+[1830] = { city_chicago, 2006, MONTH_jan, 0.04678860664367676, },
+[1831] = { city_san_francisco, 2007, MONTH_jan, 0.04373008728027344, },
+[1832] = { city_denver, 2007, MONTH_jan, 0.04373008728027344, },
+[1833] = { city_washington, 2007, MONTH_jan, 0.04373008728027344, },
+[1834] = { city_chicago, 2007, MONTH_jan, 0.04373008728027344, },
+[1835] = { city_san_francisco, 2008, MONTH_jan, 0.050045504570007324, },
+[1836] = { city_denver, 2008, MONTH_jan, 0.050045504570007324, },
+[1837] = { city_washington, 2008, MONTH_jan, 0.050045504570007324, },
+[1838] = { city_chicago, 2008, MONTH_jan, 0.050045504570007324, },
+[1839] = { city_san_francisco, 2009, MONTH_jan, 0.0580000638961792, },
+[1840] = { city_denver, 2009, MONTH_jan, 0.0580000638961792, },
+[1841] = { city_washington, 2009, MONTH_jan, 0.0580000638961792, },
+[1842] = { city_chicago, 2009, MONTH_jan, 0.0580000638961792, },
+[1843] = { city_san_francisco, 2010, MONTH_jan, 0.05840554714202881, },
+[1844] = { city_denver, 2010, MONTH_jan, 0.05840554714202881, },
+[1845] = { city_washington, 2010, MONTH_jan, 0.05840554714202881, },
+[1846] = { city_chicago, 2010, MONTH_jan, 0.05840554714202881, },
+[1847] = { city_san_francisco, 2011, MONTH_jan, 0.06897319316864013, },
+[1848] = { city_denver, 2011, MONTH_jan, 0.06897319316864013, },
+[1849] = { city_washington, 2011, MONTH_jan, 0.06897319316864013, },
+[1850] = { city_chicago, 2011, MONTH_jan, 0.06897319316864013, },
+[1851] = { city_san_francisco, 2012, MONTH_jan, 0.06886333942413331, },
+[1852] = { city_denver, 2012, MONTH_jan, 0.06886333942413331, },
+[1853] = { city_washington, 2012, MONTH_jan, 0.06886333942413331, },
+[1854] = { city_chicago, 2012, MONTH_jan, 0.06886333942413331, },
+[1855] = { city_san_francisco, 2013, MONTH_jan, 0.07134616374969482, },
+[1856] = { city_denver, 2013, MONTH_jan, 0.07134616374969482, },
+[1857] = { city_washington, 2013, MONTH_jan, 0.07134616374969482, },
+[1858] = { city_chicago, 2013, MONTH_jan, 0.07134616374969482, },
+[1859] = { city_san_francisco, 2014, MONTH_jan, 0.07262890815734863, },
+[1860] = { city_denver, 2014, MONTH_jan, 0.07262890815734863, },
+[1861] = { city_washington, 2014, MONTH_jan, 0.07262890815734863, },
+[1862] = { city_chicago, 2014, MONTH_jan, 0.07262890815734863, },
+[1863] = { city_san_francisco, 2015, MONTH_jan, 0.07468742847442628, },
+[1864] = { city_denver, 2015, MONTH_jan, 0.07468742847442628, },
+[1865] = { city_washington, 2015, MONTH_jan, 0.07468742847442628, },
+[1866] = { city_chicago, 2015, MONTH_jan, 0.07468742847442628, },
+[1867] = { city_san_francisco, 2016, MONTH_jan, 0.08285085678100586, },
+[1868] = { city_denver, 2016, MONTH_jan, 0.08285085678100586, },
+[1869] = { city_washington, 2016, MONTH_jan, 0.08285085678100586, },
+[1870] = { city_chicago, 2016, MONTH_jan, 0.08285085678100586, },
+[1871] = { city_san_francisco, 2017, MONTH_jan, 0.09082049369812012, },
+[1872] = { city_denver, 2017, MONTH_jan, 0.09082049369812012, },
+[1873] = { city_washington, 2017, MONTH_jan, 0.09082049369812012, },
+[1874] = { city_chicago, 2017, MONTH_jan, 0.09082049369812012, },
+[1875] = { city_san_francisco, 2018, MONTH_jan, 0.0896493911743164, },
+[1876] = { city_denver, 2018, MONTH_jan, 0.0896493911743164, },
+[1877] = { city_washington, 2018, MONTH_jan, 0.0896493911743164, },
+[1878] = { city_chicago, 2018, MONTH_jan, 0.0896493911743164, },
+[1879] = { city_san_francisco, 2019, MONTH_jan, 0.09315262794494628, },
+[1880] = { city_denver, 2019, MONTH_jan, 0.09315262794494628, },
+[1881] = { city_washington, 2019, MONTH_jan, 0.09315262794494628, },
+[1882] = { city_chicago, 2019, MONTH_jan, 0.09315262794494628, },
+[1883] = { city_san_francisco, 2020, MONTH_jan, 0.10532232284545899, },
+[1884] = { city_denver, 2020, MONTH_jan, 0.10532232284545899, },
+[1885] = { city_washington, 2020, MONTH_jan, 0.10532232284545899, },
+[1886] = { city_chicago, 2020, MONTH_jan, 0.10532232284545899, },
+[1887] = { city_san_francisco, 2021, MONTH_jan, 0.10655990600585938, },
+[1888] = { city_denver, 2021, MONTH_jan, 0.10655990600585938, },
+[1889] = { city_washington, 2021, MONTH_jan, 0.10655990600585938, },
+[1890] = { city_chicago, 2021, MONTH_jan, 0.10655990600585938, },
+[1891] = { city_hanoi, 1965, MONTH_jan, 0.034132664203643796, },
+[1892] = { city_hanoi, 1966, MONTH_jan, 0.02240062952041626, },
+[1893] = { city_hanoi, 1967, MONTH_jan, 0.017923457622528075, },
+[1894] = { city_hanoi, 1968, MONTH_jan, 0.01929360270500183, },
+[1895] = { city_hanoi, 1969, MONTH_jan, 0.01837692379951477, },
+[1896] = { city_hanoi, 1970, MONTH_jan, 0.019573317766189577, },
+[1897] = { city_hanoi, 1971, MONTH_jan, 0.022463304996490477, },
+[1898] = { city_hanoi, 1972, MONTH_jan, 0.01592976570129394, },
+[1899] = { city_hanoi, 1973, MONTH_jan, 0.015021532773971558, },
+[1900] = { city_hanoi, 1974, MONTH_jan, 0.020197300910949706, },
+[1901] = { city_hanoi, 1975, MONTH_jan, 0.02163102149963379, },
+[1902] = { city_hanoi, 1976, MONTH_jan, 0.041627111434936526, },
+[1903] = { city_hanoi, 1977, MONTH_jan, 0.04191826820373535, },
+[1904] = { city_hanoi, 1978, MONTH_jan, 0.05387956142425537, },
+[1905] = { city_hanoi, 1979, MONTH_jan, 0.062493300437927245, },
+[1906] = { city_hanoi, 1980, MONTH_jan, 0.08125602722167968, },
+[1907] = { city_hanoi, 1981, MONTH_jan, 0.08210463523864746, },
+[1908] = { city_hanoi, 1982, MONTH_jan, 0.07957214832305909, },
+[1909] = { city_hanoi, 1983, MONTH_jan, 0.059674644470214845, },
+[1910] = { city_hanoi, 1984, MONTH_jan, 0.07733730316162109, },
+[1911] = { city_hanoi, 1985, MONTH_jan, 0.07084222316741944, },
+[1912] = { city_hanoi, 1986, MONTH_jan, 0.062252769470214846, },
+[1913] = { city_hanoi, 1987, MONTH_jan, 0.05438616275787354, },
+[1914] = { city_hanoi, 1988, MONTH_jan, 0.07041000366210938, },
+[1915] = { city_hanoi, 1989, MONTH_jan, 0.15831216812133786, },
+[1916] = { city_hanoi, 1990, MONTH_jan, 0.20584053039550781, },
+[1917] = { city_hanoi, 1991, MONTH_jan, 0.2379098892211914, },
+[1918] = { city_hanoi, 1992, MONTH_jan, 0.24912485122680664, },
+[1919] = { city_hanoi, 1993, MONTH_jan, 0.24039852142333984, },
+[1920] = { city_hanoi, 1994, MONTH_jan, 0.2497269630432129, },
+[1921] = { city_hanoi, 1995, MONTH_jan, 0.24608880996704102, },
+[1922] = { city_hanoi, 1996, MONTH_jan, 0.24358139038085938, },
+[1923] = { city_hanoi, 1997, MONTH_jan, 0.20527612686157226, },
+[1924] = { city_hanoi, 1998, MONTH_jan, 0.1806495475769043, },
+[1925] = { city_hanoi, 1999, MONTH_jan, 0.2165902328491211, },
+[1926] = { city_hanoi, 2000, MONTH_jan, 0.20092161178588866, },
+[1927] = { city_hanoi, 2001, MONTH_jan, 0.22102148056030274, },
+[1928] = { city_hanoi, 2002, MONTH_jan, 0.20062660217285155, },
+[1929] = { city_hanoi, 2003, MONTH_jan, 0.19297657012939454, },
+[1930] = { city_hanoi, 2004, MONTH_jan, 0.14763954162597656, },
+[1931] = { city_hanoi, 2005, MONTH_jan, 0.1284674835205078, },
+[1932] = { city_hanoi, 2006, MONTH_jan, 0.16607240676879884, },
+[1933] = { city_hanoi, 2007, MONTH_jan, 0.17202272415161132, },
+[1934] = { city_hanoi, 2008, MONTH_jan, 0.16009517669677734, },
+[1935] = { city_hanoi, 2009, MONTH_jan, 0.17948020935058595, },
+[1936] = { city_hanoi, 2010, MONTH_jan, 0.14711740493774414, },
+[1937] = { city_hanoi, 2011, MONTH_jan, 0.1902745246887207, },
+[1938] = { city_hanoi, 2012, MONTH_jan, 0.2338884544372558, },
+[1939] = { city_hanoi, 2013, MONTH_jan, 0.23877511978149418, },
+[1940] = { city_hanoi, 2014, MONTH_jan, 0.23073970794677734, },
+[1941] = { city_hanoi, 2015, MONTH_jan, 0.18584493637084962, },
+[1942] = { city_hanoi, 2016, MONTH_jan, 0.19181915283203124, },
+[1943] = { city_hanoi, 2017, MONTH_jan, 0.24208347320556642, },
+[1944] = { city_hanoi, 2018, MONTH_jan, 0.20761816024780275, },
+[1945] = { city_hanoi, 2019, MONTH_jan, 0.15891139030456544, },
+[1946] = { city_hanoi, 2020, MONTH_jan, 0.19136663436889648, },
+[1947] = { city_hanoi, 2021, MONTH_jan, 0.2273440742492676, },
+};
+global u32 question_21_len = sizeof(question_21_data) / sizeof(question_21_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_3.h b/run_tree/data/incenter_data/c/question_3.h
new file mode 100644
index 0000000..3b4fb11
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_3.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_3_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.601593625498008, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.6060606060606061, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.5254237288135594, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.6507936507936508, },
+[4] = { city_paris, 2022, MONTH_jan, 0.569806492883416, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.6120930232558139, },
+[6] = { city_denver, 2022, MONTH_jan, 0.6120930232558139, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.7948003714020427, },
+[8] = { city_harare, 2022, MONTH_jan, 0.5714285714285714, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.6158536585365854, },
+[10] = { city_washington, 2022, MONTH_jan, 0.6120930232558139, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.5555555555555556, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.5142857142857142, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.6158536585365854, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.6169354838709677, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.5819672131147541, },
+[16] = { city_lima, 2022, MONTH_jan, 0.425, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.6363636363636364, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.8333333333333334, },
+[19] = { city_managua, 2022, MONTH_jan, 0.6774193548387096, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.464638783269962, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.6190476190476191, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.5254237288135594, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.5176882661996497, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.5714285714285714, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.5993009868421053, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.5, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.5, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.6011644832605532, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.6774193548387096, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.6111111111111112, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.5714285714285714, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.5142857142857142, },
+[33] = { city_quito, 2022, MONTH_jan, 0.4827586206896552, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.45, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.5254237288135594, },
+[36] = { city_accra, 2022, MONTH_jan, 0.5714285714285714, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.6352941176470588, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.7062314540059347, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.7062314540059347, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.7107438016528925, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.7948003714020427, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.6120930232558139, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.59375, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.5, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.6507936507936508, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.4838709677419355, },
+};
+global u32 question_3_len = sizeof(question_3_data) / sizeof(question_3_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_4.h b/run_tree/data/incenter_data/c/question_4.h
new file mode 100644
index 0000000..81c2134
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_4.h
@@ -0,0 +1,1366 @@
+static Incenter_Data_Row question_4_data[] = {
+[0] = { city_abuja, 2020, MONTH_jan, 0, },
+[1] = { city_abuja, 2020, MONTH_feb, 0, },
+[2] = { city_abuja, 2020, MONTH_mar, 0.012232415902140673, },
+[3] = { city_abuja, 2020, MONTH_apr, 0.14984709480122324, },
+[4] = { city_abuja, 2020, MONTH_may, 0.672782874617737, },
+[5] = { city_abuja, 2020, MONTH_jun, 0.9174311926605505, },
+[6] = { city_abuja, 2020, MONTH_jul, 0.9327217125382263, },
+[7] = { city_abuja, 2020, MONTH_aug, 0.41284403669724773, },
+[8] = { city_abuja, 2020, MONTH_sep, 0.2996941896024465, },
+[9] = { city_abuja, 2020, MONTH_oct, 0.10091743119266056, },
+[10] = { city_abuja, 2020, MONTH_nov, 0.08868501529051988, },
+[11] = { city_abuja, 2020, MONTH_dec, 0.3211009174311927, },
+[12] = { city_abuja, 2021, MONTH_jan, 0.9174311926605505, },
+[13] = { city_abuja, 2021, MONTH_feb, 1, },
+[14] = { city_abuja, 2021, MONTH_mar, 0.4617737003058104, },
+[15] = { city_abuja, 2021, MONTH_apr, 0.021406727828746176, },
+[16] = { city_abuja, 2021, MONTH_may, 0.024464831804281346, },
+[17] = { city_abuja, 2021, MONTH_jun, 0.14984709480122324, },
+[18] = { city_abuja, 2021, MONTH_jul, 0.08868501529051988, },
+[19] = { city_abuja, 2021, MONTH_aug, 0.9357798165137615, },
+[20] = { city_abuja, 2021, MONTH_sep, 0.7553516819571865, },
+[21] = { city_abuja, 2021, MONTH_oct, 0.5902140672782875, },
+[22] = { city_abuja, 2021, MONTH_nov, 0.24770642201834864, },
+[23] = { city_abuja, 2021, MONTH_dec, 0.1651376146788991, },
+[24] = { city_abuja, 2022, MONTH_jan, 0.3211009174311927, },
+[25] = { city_abuja, 2022, MONTH_feb, 0.021406727828746176, },
+[26] = { city_abuja, 2022, MONTH_mar, 0, },
+[27] = { city_abuja, 2022, MONTH_apr, 0.0030581039755351682, },
+[28] = { city_abuja, 2022, MONTH_may, 0, },
+[29] = { city_accra, 2020, MONTH_jan, 0, },
+[30] = { city_accra, 2020, MONTH_feb, 0, },
+[31] = { city_accra, 2020, MONTH_mar, 0.017467248908296942, },
+[32] = { city_accra, 2020, MONTH_apr, 0.05240174672489083, },
+[33] = { city_accra, 2020, MONTH_may, 0.08296943231441048, },
+[34] = { city_accra, 2020, MONTH_jun, 0.33624454148471616, },
+[35] = { city_accra, 2020, MONTH_jul, 0.27510917030567683, },
+[36] = { city_accra, 2020, MONTH_aug, 0.4410480349344978, },
+[37] = { city_accra, 2020, MONTH_sep, 0.1091703056768559, },
+[38] = { city_accra, 2020, MONTH_oct, 0.08296943231441048, },
+[39] = { city_accra, 2020, MONTH_nov, 0.021834061135371178, },
+[40] = { city_accra, 2020, MONTH_dec, 0.043668122270742356, },
+[41] = { city_accra, 2021, MONTH_jan, 0.388646288209607, },
+[42] = { city_accra, 2021, MONTH_feb, 0.8165938864628821, },
+[43] = { city_accra, 2021, MONTH_mar, 0.5807860262008734, },
+[44] = { city_accra, 2021, MONTH_apr, 0.15283842794759825, },
+[45] = { city_accra, 2021, MONTH_may, 0.026200873362445413, },
+[46] = { city_accra, 2021, MONTH_jun, 0.048034934497816595, },
+[47] = { city_accra, 2021, MONTH_jul, 0.11790393013100436, },
+[48] = { city_accra, 2021, MONTH_aug, 1, },
+[49] = { city_accra, 2021, MONTH_sep, 0.4585152838427948, },
+[50] = { city_accra, 2021, MONTH_oct, 0.13537117903930132, },
+[51] = { city_accra, 2021, MONTH_nov, 0.16593886462882096, },
+[52] = { city_accra, 2021, MONTH_dec, 0.34934497816593885, },
+[53] = { city_accra, 2022, MONTH_jan, 0.4279475982532751, },
+[54] = { city_accra, 2022, MONTH_feb, 0.16593886462882096, },
+[55] = { city_accra, 2022, MONTH_mar, 0.013100436681222707, },
+[56] = { city_accra, 2022, MONTH_apr, 0, },
+[57] = { city_accra, 2022, MONTH_may, 0, },
+[58] = { city_addis_ababa, 2020, MONTH_jan, 0, },
+[59] = { city_addis_ababa, 2020, MONTH_feb, 0, },
+[60] = { city_addis_ababa, 2020, MONTH_mar, 0, },
+[61] = { city_addis_ababa, 2020, MONTH_apr, 0.026172300981461286, },
+[62] = { city_addis_ababa, 2020, MONTH_may, 0.004362050163576881, },
+[63] = { city_addis_ababa, 2020, MONTH_jun, 0.10032715376226826, },
+[64] = { city_addis_ababa, 2020, MONTH_jul, 0.1723009814612868, },
+[65] = { city_addis_ababa, 2020, MONTH_aug, 0.5627044711014176, },
+[66] = { city_addis_ababa, 2020, MONTH_sep, 0.4340239912758997, },
+[67] = { city_addis_ababa, 2020, MONTH_oct, 0.2966194111232279, },
+[68] = { city_addis_ababa, 2020, MONTH_nov, 0.257360959651036, },
+[69] = { city_addis_ababa, 2020, MONTH_dec, 0.23773173391494, },
+[70] = { city_addis_ababa, 2021, MONTH_jan, 0.18865866957470012, },
+[71] = { city_addis_ababa, 2021, MONTH_feb, 0.28680479825517996, },
+[72] = { city_addis_ababa, 2021, MONTH_mar, 0.5310796074154853, },
+[73] = { city_addis_ababa, 2021, MONTH_apr, 0.8909487459105779, },
+[74] = { city_addis_ababa, 2021, MONTH_may, 0.5419847328244275, },
+[75] = { city_addis_ababa, 2021, MONTH_jun, 0.17993456924754633, },
+[76] = { city_addis_ababa, 2021, MONTH_jul, 0.06870229007633588, },
+[77] = { city_addis_ababa, 2021, MONTH_aug, 0.302071973827699, },
+[78] = { city_addis_ababa, 2021, MONTH_sep, 0.9531079607415486, },
+[79] = { city_addis_ababa, 2021, MONTH_oct, 1, },
+[80] = { city_addis_ababa, 2021, MONTH_nov, 0.3260632497273719, },
+[81] = { city_addis_ababa, 2021, MONTH_dec, 0.19193020719738277, },
+[82] = { city_addis_ababa, 2022, MONTH_jan, 0.4416575790621592, },
+[83] = { city_addis_ababa, 2022, MONTH_feb, 0.1406761177753544, },
+[84] = { city_addis_ababa, 2022, MONTH_mar, 0.03489640130861505, },
+[85] = { city_addis_ababa, 2022, MONTH_apr, 0.019629225736095966, },
+[86] = { city_addis_ababa, 2022, MONTH_may, 0.003271537622682661, },
+[87] = { city_amsterdam, 2020, MONTH_jan, 0, },
+[88] = { city_amsterdam, 2020, MONTH_feb, 0, },
+[89] = { city_amsterdam, 2020, MONTH_mar, 0.22048475371383894, },
+[90] = { city_amsterdam, 2020, MONTH_apr, 1, },
+[91] = { city_amsterdam, 2020, MONTH_may, 0.32212666145426117, },
+[92] = { city_amsterdam, 2020, MONTH_jun, 0.041959864477456345, },
+[93] = { city_amsterdam, 2020, MONTH_jul, 0.01667969768047954, },
+[94] = { city_amsterdam, 2020, MONTH_aug, 0.019546520719311962, },
+[95] = { city_amsterdam, 2020, MONTH_sep, 0.04508730779254626, },
+[96] = { city_amsterdam, 2020, MONTH_oct, 0.24811050299713316, },
+[97] = { city_amsterdam, 2020, MONTH_nov, 0.5173312483711233, },
+[98] = { city_amsterdam, 2020, MONTH_dec, 0.5129007036747459, },
+[99] = { city_amsterdam, 2021, MONTH_jan, 0.689080010424811, },
+[100] = { city_amsterdam, 2021, MONTH_feb, 0.4162105811832161, },
+[101] = { city_amsterdam, 2021, MONTH_mar, 0.2509773260359656, },
+[102] = { city_amsterdam, 2021, MONTH_apr, 0.16080271045087308, },
+[103] = { city_amsterdam, 2021, MONTH_may, 0.1308313786812614, },
+[104] = { city_amsterdam, 2021, MONTH_jun, 0.03205629397967162, },
+[105] = { city_amsterdam, 2021, MONTH_jul, 0.019807140995569454, },
+[106] = { city_amsterdam, 2021, MONTH_aug, 0.046390409173833726, },
+[107] = { city_amsterdam, 2021, MONTH_sep, 0.04430544696377378, },
+[108] = { city_amsterdam, 2021, MONTH_oct, 0.05968204326296586, },
+[109] = { city_amsterdam, 2021, MONTH_nov, 0.24758926244461818, },
+[110] = { city_amsterdam, 2021, MONTH_dec, 0.40109460516028145, },
+[111] = { city_amsterdam, 2022, MONTH_jan, 0.09747198332030232, },
+[112] = { city_amsterdam, 2022, MONTH_feb, 0.07453739900964294, },
+[113] = { city_amsterdam, 2022, MONTH_mar, 0.10685431326557206, },
+[114] = { city_amsterdam, 2022, MONTH_apr, 0.07479801928590045, },
+[115] = { city_amsterdam, 2022, MONTH_may, 0.019807140995569454, },
+[116] = { city_ankara, 2020, MONTH_jan, 0, },
+[117] = { city_ankara, 2020, MONTH_feb, 0, },
+[118] = { city_ankara, 2020, MONTH_mar, 0.020114942528735632, },
+[119] = { city_ankara, 2020, MONTH_apr, 0.3487787356321839, },
+[120] = { city_ankara, 2020, MONTH_may, 0.17169540229885058, },
+[121] = { city_ankara, 2020, MONTH_jun, 0.07183908045977011, },
+[122] = { city_ankara, 2020, MONTH_jul, 0.06693007662835249, },
+[123] = { city_ankara, 2020, MONTH_aug, 0.07806513409961686, },
+[124] = { city_ankara, 2020, MONTH_sep, 0.21599616858237547, },
+[125] = { city_ankara, 2020, MONTH_oct, 0.24509099616858238, },
+[126] = { city_ankara, 2020, MONTH_nov, 0.4048132183908046, },
+[127] = { city_ankara, 2020, MONTH_dec, 0.8481800766283525, },
+[128] = { city_ankara, 2021, MONTH_jan, 0.6253591954022989, },
+[129] = { city_ankara, 2021, MONTH_feb, 0.3158524904214559, },
+[130] = { city_ankara, 2021, MONTH_mar, 0.3450670498084291, },
+[131] = { city_ankara, 2021, MONTH_apr, 1, },
+[132] = { city_ankara, 2021, MONTH_may, 0.9181034482758621, },
+[133] = { city_ankara, 2021, MONTH_jun, 0.273227969348659, },
+[134] = { city_ankara, 2021, MONTH_jul, 0.1875, },
+[135] = { city_ankara, 2021, MONTH_aug, 0.6232040229885057, },
+[136] = { city_ankara, 2021, MONTH_sep, 0.8836206896551724, },
+[137] = { city_ankara, 2021, MONTH_oct, 0.7868773946360154, },
+[138] = { city_ankara, 2021, MONTH_nov, 0.7453304597701149, },
+[139] = { city_ankara, 2021, MONTH_dec, 0.6660680076628352, },
+[140] = { city_ankara, 2022, MONTH_jan, 0.6029693486590039, },
+[141] = { city_ankara, 2022, MONTH_feb, 0.8378831417624522, },
+[142] = { city_ankara, 2022, MONTH_mar, 0.4483955938697318, },
+[143] = { city_ankara, 2022, MONTH_apr, 0.09375, },
+[144] = { city_ankara, 2022, MONTH_may, 0.024066091954022987, },
+[145] = { city_baghdad, 2020, MONTH_jan, 0, },
+[146] = { city_baghdad, 2020, MONTH_feb, 0, },
+[147] = { city_baghdad, 2020, MONTH_mar, 0.016242937853107344, },
+[148] = { city_baghdad, 2020, MONTH_apr, 0.016242937853107344, },
+[149] = { city_baghdad, 2020, MONTH_may, 0.03637005649717514, },
+[150] = { city_baghdad, 2020, MONTH_jun, 0.5805084745762712, },
+[151] = { city_baghdad, 2020, MONTH_jul, 1, },
+[152] = { city_baghdad, 2020, MONTH_aug, 0.807909604519774, },
+[153] = { city_baghdad, 2020, MONTH_sep, 0.763771186440678, },
+[154] = { city_baghdad, 2020, MONTH_oct, 0.614406779661017, },
+[155] = { city_baghdad, 2020, MONTH_nov, 0.4809322033898305, },
+[156] = { city_baghdad, 2020, MONTH_dec, 0.2062146892655367, },
+[157] = { city_baghdad, 2021, MONTH_jan, 0.08227401129943503, },
+[158] = { city_baghdad, 2021, MONTH_feb, 0.12076271186440678, },
+[159] = { city_baghdad, 2021, MONTH_mar, 0.3188559322033898, },
+[160] = { city_baghdad, 2021, MONTH_apr, 0.4050141242937853, },
+[161] = { city_baghdad, 2021, MONTH_may, 0.3241525423728814, },
+[162] = { city_baghdad, 2021, MONTH_jun, 0.2842514124293785, },
+[163] = { city_baghdad, 2021, MONTH_jul, 0.5081214689265536, },
+[164] = { city_baghdad, 2021, MONTH_aug, 0.7658898305084746, },
+[165] = { city_baghdad, 2021, MONTH_sep, 0.5144774011299436, },
+[166] = { city_baghdad, 2021, MONTH_oct, 0.3237994350282486, },
+[167] = { city_baghdad, 2021, MONTH_nov, 0.23622881355932204, },
+[168] = { city_baghdad, 2021, MONTH_dec, 0.12252824858757062, },
+[169] = { city_baghdad, 2022, MONTH_jan, 0.07838983050847458, },
+[170] = { city_baghdad, 2022, MONTH_feb, 0.21151129943502825, },
+[171] = { city_baghdad, 2022, MONTH_mar, 0.06638418079096045, },
+[172] = { city_baghdad, 2022, MONTH_apr, 0.01694915254237288, },
+[173] = { city_baghdad, 2022, MONTH_may, 0.002824858757062147, },
+[174] = { city_bangkok, 2020, MONTH_jan, 0, },
+[175] = { city_bangkok, 2020, MONTH_feb, 0, },
+[176] = { city_bangkok, 2020, MONTH_mar, 0.0014854426619132501, },
+[177] = { city_bangkok, 2020, MONTH_apr, 0.006535947712418301, },
+[178] = { city_bangkok, 2020, MONTH_may, 4.45632798573975e-4, },
+[179] = { city_bangkok, 2020, MONTH_jun, 1.4854426619132502e-4, },
+[180] = { city_bangkok, 2020, MONTH_jul, 0, },
+[181] = { city_bangkok, 2020, MONTH_aug, 0, },
+[182] = { city_bangkok, 2020, MONTH_sep, 1.4854426619132502e-4, },
+[183] = { city_bangkok, 2020, MONTH_oct, 0, },
+[184] = { city_bangkok, 2020, MONTH_nov, 1.4854426619132502e-4, },
+[185] = { city_bangkok, 2020, MONTH_dec, 1.4854426619132502e-4, },
+[186] = { city_bangkok, 2021, MONTH_jan, 0.0023767082590612004, },
+[187] = { city_bangkok, 2021, MONTH_feb, 8.9126559714795e-4, },
+[188] = { city_bangkok, 2021, MONTH_mar, 0.0016339869281045752, },
+[189] = { city_bangkok, 2021, MONTH_apr, 0.016191325014854426, },
+[190] = { city_bangkok, 2021, MONTH_may, 0.12299465240641712, },
+[191] = { city_bangkok, 2021, MONTH_jun, 0.1473559120617944, },
+[192] = { city_bangkok, 2021, MONTH_jul, 0.4209744503862151, },
+[193] = { city_bangkok, 2021, MONTH_aug, 1, },
+[194] = { city_bangkok, 2021, MONTH_sep, 0.7632204396910279, },
+[195] = { city_bangkok, 2021, MONTH_oct, 0.3680926916221034, },
+[196] = { city_bangkok, 2021, MONTH_nov, 0.232620320855615, },
+[197] = { city_bangkok, 2021, MONTH_dec, 0.13770053475935828, },
+[198] = { city_bangkok, 2022, MONTH_jan, 0.07055852644087938, },
+[199] = { city_bangkok, 2022, MONTH_feb, 0.11289364230540701, },
+[200] = { city_bangkok, 2022, MONTH_mar, 0.3263517528223411, },
+[201] = { city_bangkok, 2022, MONTH_apr, 0.5044563279857398, },
+[202] = { city_bangkok, 2022, MONTH_may, 0.22177658942364825, },
+[203] = { city_beijing, 2020, MONTH_jan, 0, },
+[204] = { city_beijing, 2020, MONTH_feb, 0.3723932472691162, },
+[205] = { city_beijing, 2020, MONTH_mar, 0.06752730883813307, },
+[206] = { city_beijing, 2020, MONTH_apr, 0.1885373811888211, },
+[207] = { city_beijing, 2020, MONTH_may, 2.837281883955171e-4, },
+[208] = { city_beijing, 2020, MONTH_jun, 4.2559228259327563e-4, },
+[209] = { city_beijing, 2020, MONTH_jul, 0.0025535536955596538, },
+[210] = { city_beijing, 2020, MONTH_aug, 0.00893743793445879, },
+[211] = { city_beijing, 2020, MONTH_sep, 0.0024116896013618955, },
+[212] = { city_beijing, 2020, MONTH_oct, 0, },
+[213] = { city_beijing, 2020, MONTH_nov, 5.674563767910342e-4, },
+[214] = { city_beijing, 2020, MONTH_dec, 0.005390835579514825, },
+[215] = { city_beijing, 2021, MONTH_jan, 0.004965243296921549, },
+[216] = { city_beijing, 2021, MONTH_feb, 0.002837281883955171, },
+[217] = { city_beijing, 2021, MONTH_mar, 0.0011349127535820683, },
+[218] = { city_beijing, 2021, MONTH_apr, 8.511845651865513e-4, },
+[219] = { city_beijing, 2021, MONTH_may, 0.013902681231380337, },
+[220] = { city_beijing, 2021, MONTH_jun, 0.07589729039580083, },
+[221] = { city_beijing, 2021, MONTH_jul, 0.02057029365867499, },
+[222] = { city_beijing, 2021, MONTH_aug, 0.006667612427294652, },
+[223] = { city_beijing, 2021, MONTH_sep, 0.0012767768477798269, },
+[224] = { city_beijing, 2021, MONTH_oct, 7.093204709887927e-4, },
+[225] = { city_beijing, 2021, MONTH_nov, 1.4186409419775854e-4, },
+[226] = { city_beijing, 2021, MONTH_dec, 2.837281883955171e-4, },
+[227] = { city_beijing, 2022, MONTH_jan, 1.4186409419775854e-4, },
+[228] = { city_beijing, 2022, MONTH_feb, 0.06355511420059583, },
+[229] = { city_beijing, 2022, MONTH_mar, 1, },
+[230] = { city_beijing, 2022, MONTH_apr, 0.2816002269825507, },
+[231] = { city_beijing, 2022, MONTH_may, 0.22513831749184282, },
+[232] = { city_belgrade, 2020, MONTH_jan, 0, },
+[233] = { city_belgrade, 2020, MONTH_feb, 0, },
+[234] = { city_belgrade, 2020, MONTH_mar, 0.007441327990841442, },
+[235] = { city_belgrade, 2020, MONTH_apr, 0.09158557527189468, },
+[236] = { city_belgrade, 2020, MONTH_may, 0.03949627933600458, },
+[237] = { city_belgrade, 2020, MONTH_jun, 0.018317115054378934, },
+[238] = { city_belgrade, 2020, MONTH_jul, 0.16657126502575845, },
+[239] = { city_belgrade, 2020, MONTH_aug, 0.0835718374356039, },
+[240] = { city_belgrade, 2020, MONTH_sep, 0.021751574127074985, },
+[241] = { city_belgrade, 2020, MONTH_oct, 0.03720663995420721, },
+[242] = { city_belgrade, 2020, MONTH_nov, 0.42072123640526615, },
+[243] = { city_belgrade, 2020, MONTH_dec, 0.9238694905552376, },
+[244] = { city_belgrade, 2021, MONTH_jan, 0.47910704064109905, },
+[245] = { city_belgrade, 2021, MONTH_feb, 0.2455638236977676, },
+[246] = { city_belgrade, 2021, MONTH_mar, 0.4813966800228964, },
+[247] = { city_belgrade, 2021, MONTH_apr, 0.6107613050944476, },
+[248] = { city_belgrade, 2021, MONTH_may, 0.2959358900973097, },
+[249] = { city_belgrade, 2021, MONTH_jun, 0.10818546078992558, },
+[250] = { city_belgrade, 2021, MONTH_jul, 0.03949627933600458, },
+[251] = { city_belgrade, 2021, MONTH_aug, 0.09502003434459072, },
+[252] = { city_belgrade, 2021, MONTH_sep, 0.5203205495134516, },
+[253] = { city_belgrade, 2021, MONTH_oct, 0.9748139668002289, },
+[254] = { city_belgrade, 2021, MONTH_nov, 1, },
+[255] = { city_belgrade, 2021, MONTH_dec, 0.6016027475672582, },
+[256] = { city_belgrade, 2022, MONTH_jan, 0.5048654836863194, },
+[257] = { city_belgrade, 2022, MONTH_feb, 0.9324556382369776, },
+[258] = { city_belgrade, 2022, MONTH_mar, 0.3371493989696623, },
+[259] = { city_belgrade, 2022, MONTH_apr, 0.11219232970807098, },
+[260] = { city_belgrade, 2022, MONTH_may, 0.05495134516313681, },
+[261] = { city_berlin, 2020, MONTH_jan, 0, },
+[262] = { city_berlin, 2020, MONTH_feb, 3.875217981011432e-5, },
+[263] = { city_berlin, 2020, MONTH_mar, 0.08913001356326293, },
+[264] = { city_berlin, 2020, MONTH_apr, 0.23445068785119164, },
+[265] = { city_berlin, 2020, MONTH_may, 0.029722921914357683, },
+[266] = { city_berlin, 2020, MONTH_jun, 0.0056965704320868045, },
+[267] = { city_berlin, 2020, MONTH_jul, 0.005076535555124976, },
+[268] = { city_berlin, 2020, MONTH_aug, 0.004999031195504747, },
+[269] = { city_berlin, 2020, MONTH_sep, 0.012749467157527611, },
+[270] = { city_berlin, 2020, MONTH_oct, 0.11893043983724085, },
+[271] = { city_berlin, 2020, MONTH_nov, 0.44944778143770586, },
+[272] = { city_berlin, 2020, MONTH_dec, 1, },
+[273] = { city_berlin, 2021, MONTH_jan, 0.7780662662274753, },
+[274] = { city_berlin, 2021, MONTH_feb, 0.2610734353807402, },
+[275] = { city_berlin, 2021, MONTH_mar, 0.21472582832784345, },
+[276] = { city_berlin, 2021, MONTH_apr, 0.26177097461732224, },
+[277] = { city_berlin, 2021, MONTH_may, 0.10831234256926953, },
+[278] = { city_berlin, 2021, MONTH_jun, 0.011393140864173609, },
+[279] = { city_berlin, 2021, MONTH_jul, 0.0063553574888587485, },
+[280] = { city_berlin, 2021, MONTH_aug, 0.033946909513660146, },
+[281] = { city_berlin, 2021, MONTH_sep, 0.06859135826390235, },
+[282] = { city_berlin, 2021, MONTH_oct, 0.13466382484014724, },
+[283] = { city_berlin, 2021, MONTH_nov, 0.3957372602208874, },
+[284] = { city_berlin, 2021, MONTH_dec, 0.346444487502422, },
+[285] = { city_berlin, 2022, MONTH_jan, 0.14357682619647355, },
+[286] = { city_berlin, 2022, MONTH_feb, 0.24545630691726408, },
+[287] = { city_berlin, 2022, MONTH_mar, 0.2939740360395272, },
+[288] = { city_berlin, 2022, MONTH_apr, 0.1602402635148227, },
+[289] = { city_berlin, 2022, MONTH_may, 0.05619066072466576, },
+[290] = { city_bogota, 2020, MONTH_jan, 0, },
+[291] = { city_bogota, 2020, MONTH_feb, 0, },
+[292] = { city_bogota, 2020, MONTH_mar, 5.688605722737357e-4, },
+[293] = { city_bogota, 2020, MONTH_apr, 0.014733488821889755, },
+[294] = { city_bogota, 2020, MONTH_may, 0.033221457420786166, },
+[295] = { city_bogota, 2020, MONTH_jun, 0.12816428693327264, },
+[296] = { city_bogota, 2020, MONTH_jul, 0.3611126912793674, },
+[297] = { city_bogota, 2020, MONTH_aug, 0.54667500995506, },
+[298] = { city_bogota, 2020, MONTH_sep, 0.374139598384436, },
+[299] = { city_bogota, 2020, MONTH_oct, 0.3006428124466693, },
+[300] = { city_bogota, 2020, MONTH_nov, 0.31145116331987027, },
+[301] = { city_bogota, 2020, MONTH_dec, 0.35377438989703625, },
+[302] = { city_bogota, 2021, MONTH_jan, 0.6066329142727117, },
+[303] = { city_bogota, 2021, MONTH_feb, 0.3546276807554468, },
+[304] = { city_bogota, 2021, MONTH_mar, 0.2025712497866773, },
+[305] = { city_bogota, 2021, MONTH_apr, 0.5487229080152455, },
+[306] = { city_bogota, 2021, MONTH_may, 0.8545423516696058, },
+[307] = { city_bogota, 2021, MONTH_jun, 1, },
+[308] = { city_bogota, 2021, MONTH_jul, 0.8419136469651288, },
+[309] = { city_bogota, 2021, MONTH_aug, 0.26651117811024516, },
+[310] = { city_bogota, 2021, MONTH_sep, 0.08009556857614199, },
+[311] = { city_bogota, 2021, MONTH_oct, 0.05722737357073781, },
+[312] = { city_bogota, 2021, MONTH_nov, 0.06894590135957676, },
+[313] = { city_bogota, 2021, MONTH_dec, 0.08129017577791683, },
+[314] = { city_bogota, 2022, MONTH_jan, 0.22561010296376358, },
+[315] = { city_bogota, 2022, MONTH_feb, 0.27111894874566245, },
+[316] = { city_bogota, 2022, MONTH_mar, 0.05671539905569145, },
+[317] = { city_bogota, 2022, MONTH_apr, 0.011035895102110473, },
+[318] = { city_bogota, 2022, MONTH_may, 0.003697593719779282, },
+[319] = { city_brasilia, 2020, MONTH_jan, 0, },
+[320] = { city_brasilia, 2020, MONTH_feb, 0, },
+[321] = { city_brasilia, 2020, MONTH_mar, 0.0016129223543922485, },
+[322] = { city_brasilia, 2020, MONTH_apr, 0.05788730891021004, },
+[323] = { city_brasilia, 2020, MONTH_may, 0.27112513193942056, },
+[324] = { city_brasilia, 2020, MONTH_jun, 0.35275560668413997, },
+[325] = { city_brasilia, 2020, MONTH_jul, 0.38558332048529986, },
+[326] = { city_brasilia, 2020, MONTH_aug, 0.3596816850294714, },
+[327] = { city_brasilia, 2020, MONTH_sep, 0.2561225820989338, },
+[328] = { city_brasilia, 2020, MONTH_oct, 0.20055977893475965, },
+[329] = { city_brasilia, 2020, MONTH_nov, 0.16119735765367235, },
+[330] = { city_brasilia, 2020, MONTH_dec, 0.23861763066450029, },
+[331] = { city_brasilia, 2021, MONTH_jan, 0.35561379997390863, },
+[332] = { city_brasilia, 2021, MONTH_feb, 0.35779598904161575, },
+[333] = { city_brasilia, 2021, MONTH_mar, 0.7238107662567156, },
+[334] = { city_brasilia, 2021, MONTH_apr, 1, },
+[335] = { city_brasilia, 2021, MONTH_may, 0.7456445166569812, },
+[336] = { city_brasilia, 2021, MONTH_jun, 0.6289804195970066, },
+[337] = { city_brasilia, 2021, MONTH_jul, 0.4791921156560206, },
+[338] = { city_brasilia, 2021, MONTH_aug, 0.2942515921678388, },
+[339] = { city_brasilia, 2021, MONTH_sep, 0.19139221290575079, },
+[340] = { city_brasilia, 2021, MONTH_oct, 0.14250643389983278, },
+[341] = { city_brasilia, 2021, MONTH_nov, 0.0808358732907174, },
+[342] = { city_brasilia, 2021, MONTH_dec, 0.053831283577841296, },
+[343] = { city_brasilia, 2022, MONTH_jan, 0.09140288665662544, },
+[344] = { city_brasilia, 2022, MONTH_feb, 0.2655273425918239, },
+[345] = { city_brasilia, 2022, MONTH_mar, 0.1224872211482584, },
+[346] = { city_brasilia, 2022, MONTH_apr, 0.04724913720513763, },
+[347] = { city_brasilia, 2022, MONTH_may, 0.03828318647042778, },
+[348] = { city_brisbane, 2020, MONTH_jan, 0, },
+[349] = { city_brisbane, 2020, MONTH_feb, 0, },
+[350] = { city_brisbane, 2020, MONTH_mar, 0.009825327510917031, },
+[351] = { city_brisbane, 2020, MONTH_apr, 0.039301310043668124, },
+[352] = { city_brisbane, 2020, MONTH_may, 0.006550218340611353, },
+[353] = { city_brisbane, 2020, MONTH_jun, 0.001091703056768559, },
+[354] = { city_brisbane, 2020, MONTH_jul, 0.04639737991266375, },
+[355] = { city_brisbane, 2020, MONTH_aug, 0.23034934497816595, },
+[356] = { city_brisbane, 2020, MONTH_sep, 0.14792576419213974, },
+[357] = { city_brisbane, 2020, MONTH_oct, 0.013646288209606987, },
+[358] = { city_brisbane, 2020, MONTH_nov, 0, },
+[359] = { city_brisbane, 2020, MONTH_dec, 0.001091703056768559, },
+[360] = { city_brisbane, 2021, MONTH_jan, 0, },
+[361] = { city_brisbane, 2021, MONTH_feb, 0, },
+[362] = { city_brisbane, 2021, MONTH_mar, 0, },
+[363] = { city_brisbane, 2021, MONTH_apr, 5.458515283842794e-4, },
+[364] = { city_brisbane, 2021, MONTH_may, 0, },
+[365] = { city_brisbane, 2021, MONTH_jun, 0, },
+[366] = { city_brisbane, 2021, MONTH_jul, 0.007096069868995633, },
+[367] = { city_brisbane, 2021, MONTH_aug, 0.04312227074235808, },
+[368] = { city_brisbane, 2021, MONTH_sep, 0.15065502183406113, },
+[369] = { city_brisbane, 2021, MONTH_oct, 0.2423580786026201, },
+[370] = { city_brisbane, 2021, MONTH_nov, 0.15010917030567686, },
+[371] = { city_brisbane, 2021, MONTH_dec, 0.125, },
+[372] = { city_brisbane, 2022, MONTH_jan, 0.8133187772925764, },
+[373] = { city_brisbane, 2022, MONTH_feb, 0.7876637554585153, },
+[374] = { city_brisbane, 2022, MONTH_mar, 0.4383187772925764, },
+[375] = { city_brisbane, 2022, MONTH_apr, 0.6697598253275109, },
+[376] = { city_brisbane, 2022, MONTH_may, 0.6986899563318777, },
+[377] = { city_bucharest, 2020, MONTH_jan, 0, },
+[378] = { city_bucharest, 2020, MONTH_feb, 0, },
+[379] = { city_bucharest, 2020, MONTH_mar, 0.006214743283296682, },
+[380] = { city_bucharest, 2020, MONTH_apr, 0.06004398126015872, },
+[381] = { city_bucharest, 2020, MONTH_may, 0.05411607228224496, },
+[382] = { city_bucharest, 2020, MONTH_jun, 0.03585428817286548, },
+[383] = { city_bucharest, 2020, MONTH_jul, 0.06405966153551965, },
+[384] = { city_bucharest, 2020, MONTH_aug, 0.12180896835261497, },
+[385] = { city_bucharest, 2020, MONTH_sep, 0.1160722822449565, },
+[386] = { city_bucharest, 2020, MONTH_oct, 0.19839372788985563, },
+[387] = { city_bucharest, 2020, MONTH_nov, 0.4136150683621761, },
+[388] = { city_bucharest, 2020, MONTH_dec, 0.4209771488670045, },
+[389] = { city_bucharest, 2021, MONTH_jan, 0.2550913089205469, },
+[390] = { city_bucharest, 2021, MONTH_feb, 0.19342193326321827, },
+[391] = { city_bucharest, 2021, MONTH_mar, 0.29849890046849603, },
+[392] = { city_bucharest, 2021, MONTH_apr, 0.4361793670522995, },
+[393] = { city_bucharest, 2021, MONTH_may, 0.22038435796921313, },
+[394] = { city_bucharest, 2021, MONTH_jun, 0.3182904675399178, },
+[395] = { city_bucharest, 2021, MONTH_jul, 0.06434649584090257, },
+[396] = { city_bucharest, 2021, MONTH_aug, 0.02495458456831437, },
+[397] = { city_bucharest, 2021, MONTH_sep, 0.2223921981068936, },
+[398] = { city_bucharest, 2021, MONTH_oct, 1, },
+[399] = { city_bucharest, 2021, MONTH_nov, 0.8660483793861746, },
+[400] = { city_bucharest, 2021, MONTH_dec, 0.22296586671765944, },
+[401] = { city_bucharest, 2022, MONTH_jan, 0.1214265226121044, },
+[402] = { city_bucharest, 2022, MONTH_feb, 0.3221149249450234, },
+[403] = { city_bucharest, 2022, MONTH_mar, 0.1568983650444593, },
+[404] = { city_bucharest, 2022, MONTH_apr, 0.046275934601778375, },
+[405] = { city_bucharest, 2022, MONTH_may, 0.019122287025528255, },
+[406] = { city_cairo, 2020, MONTH_jan, 0, },
+[407] = { city_cairo, 2020, MONTH_feb, 0, },
+[408] = { city_cairo, 2020, MONTH_mar, 0.02092904543134252, },
+[409] = { city_cairo, 2020, MONTH_apr, 0.17304747320061256, },
+[410] = { city_cairo, 2020, MONTH_may, 0.2720775906074528, },
+[411] = { city_cairo, 2020, MONTH_jun, 1, },
+[412] = { city_cairo, 2020, MONTH_jul, 0.9709035222052067, },
+[413] = { city_cairo, 2020, MONTH_aug, 0.31904032669729454, },
+[414] = { city_cairo, 2020, MONTH_sep, 0.2628892291985707, },
+[415] = { city_cairo, 2020, MONTH_oct, 0.1755997958141909, },
+[416] = { city_cairo, 2020, MONTH_nov, 0.19295558958652373, },
+[417] = { city_cairo, 2020, MONTH_dec, 0.47983665135273096, },
+[418] = { city_cairo, 2021, MONTH_jan, 0.8611536498213375, },
+[419] = { city_cairo, 2021, MONTH_feb, 0.7023991832567636, },
+[420] = { city_cairo, 2021, MONTH_mar, 0.6722817764165391, },
+[421] = { city_cairo, 2021, MONTH_apr, 0.6748340990301174, },
+[422] = { city_cairo, 2021, MONTH_may, 0.9030117406840225, },
+[423] = { city_cairo, 2021, MONTH_jun, 0.5620214395099541, },
+[424] = { city_cairo, 2021, MONTH_jul, 0.18887187340479836, },
+[425] = { city_cairo, 2021, MONTH_aug, 0.1066870852475753, },
+[426] = { city_cairo, 2021, MONTH_sep, 0.2894333843797856, },
+[427] = { city_cairo, 2021, MONTH_oct, 0.6625829504849413, },
+[428] = { city_cairo, 2021, MONTH_nov, 0.9290454313425217, },
+[429] = { city_cairo, 2021, MONTH_dec, 0.6712608473711077, },
+[430] = { city_cairo, 2022, MONTH_jan, 0.4476773864216437, },
+[431] = { city_cairo, 2022, MONTH_feb, 0.733027054619704, },
+[432] = { city_cairo, 2022, MONTH_mar, 0.19244512506380806, },
+[433] = { city_cairo, 2022, MONTH_apr, 0.09647779479326186, },
+[434] = { city_cairo, 2022, MONTH_may, 0.05717202654415518, },
+[435] = { city_chengdu, 2020, MONTH_jan, 0, },
+[436] = { city_chengdu, 2020, MONTH_feb, 0.3723932472691162, },
+[437] = { city_chengdu, 2020, MONTH_mar, 0.06752730883813307, },
+[438] = { city_chengdu, 2020, MONTH_apr, 0.1885373811888211, },
+[439] = { city_chengdu, 2020, MONTH_may, 2.837281883955171e-4, },
+[440] = { city_chengdu, 2020, MONTH_jun, 4.2559228259327563e-4, },
+[441] = { city_chengdu, 2020, MONTH_jul, 0.0025535536955596538, },
+[442] = { city_chengdu, 2020, MONTH_aug, 0.00893743793445879, },
+[443] = { city_chengdu, 2020, MONTH_sep, 0.0024116896013618955, },
+[444] = { city_chengdu, 2020, MONTH_oct, 0, },
+[445] = { city_chengdu, 2020, MONTH_nov, 5.674563767910342e-4, },
+[446] = { city_chengdu, 2020, MONTH_dec, 0.005390835579514825, },
+[447] = { city_chengdu, 2021, MONTH_jan, 0.004965243296921549, },
+[448] = { city_chengdu, 2021, MONTH_feb, 0.002837281883955171, },
+[449] = { city_chengdu, 2021, MONTH_mar, 0.0011349127535820683, },
+[450] = { city_chengdu, 2021, MONTH_apr, 8.511845651865513e-4, },
+[451] = { city_chengdu, 2021, MONTH_may, 0.013902681231380337, },
+[452] = { city_chengdu, 2021, MONTH_jun, 0.07589729039580083, },
+[453] = { city_chengdu, 2021, MONTH_jul, 0.02057029365867499, },
+[454] = { city_chengdu, 2021, MONTH_aug, 0.006667612427294652, },
+[455] = { city_chengdu, 2021, MONTH_sep, 0.0012767768477798269, },
+[456] = { city_chengdu, 2021, MONTH_oct, 7.093204709887927e-4, },
+[457] = { city_chengdu, 2021, MONTH_nov, 1.4186409419775854e-4, },
+[458] = { city_chengdu, 2021, MONTH_dec, 2.837281883955171e-4, },
+[459] = { city_chengdu, 2022, MONTH_jan, 1.4186409419775854e-4, },
+[460] = { city_chengdu, 2022, MONTH_feb, 0.06355511420059583, },
+[461] = { city_chengdu, 2022, MONTH_mar, 1, },
+[462] = { city_chengdu, 2022, MONTH_apr, 0.2816002269825507, },
+[463] = { city_chengdu, 2022, MONTH_may, 0.22513831749184282, },
+[464] = { city_dhaka, 2020, MONTH_jan, 0, },
+[465] = { city_dhaka, 2020, MONTH_feb, 0, },
+[466] = { city_dhaka, 2020, MONTH_mar, 8.087997411840828e-4, },
+[467] = { city_dhaka, 2020, MONTH_apr, 0.0263668715626011, },
+[468] = { city_dhaka, 2020, MONTH_may, 0.07796829505014559, },
+[469] = { city_dhaka, 2020, MONTH_jun, 0.19362665803946943, },
+[470] = { city_dhaka, 2020, MONTH_jul, 0.20446457457133613, },
+[471] = { city_dhaka, 2020, MONTH_aug, 0.18925913943707537, },
+[472] = { city_dhaka, 2020, MONTH_sep, 0.15690714978971207, },
+[473] = { city_dhaka, 2020, MONTH_oct, 0.10870268521514073, },
+[474] = { city_dhaka, 2020, MONTH_nov, 0.11662892267874474, },
+[475] = { city_dhaka, 2020, MONTH_dec, 0.14801035263668716, },
+[476] = { city_dhaka, 2021, MONTH_jan, 0.0918796505985118, },
+[477] = { city_dhaka, 2021, MONTH_feb, 0.045454545454545456, },
+[478] = { city_dhaka, 2021, MONTH_mar, 0.10320284697508897, },
+[479] = { city_dhaka, 2021, MONTH_apr, 0.38887091556130704, },
+[480] = { city_dhaka, 2021, MONTH_may, 0.18909737948883856, },
+[481] = { city_dhaka, 2021, MONTH_jun, 0.3047557424781624, },
+[482] = { city_dhaka, 2021, MONTH_jul, 1, },
+[483] = { city_dhaka, 2021, MONTH_aug, 0.8912973147848593, },
+[484] = { city_dhaka, 2021, MONTH_sep, 0.21271433193141379, },
+[485] = { city_dhaka, 2021, MONTH_oct, 0.05791006146878033, },
+[486] = { city_dhaka, 2021, MONTH_nov, 0.018278874150760273, },
+[487] = { city_dhaka, 2021, MONTH_dec, 0.014720155289550308, },
+[488] = { city_dhaka, 2022, MONTH_jan, 0.05208670333225494, },
+[489] = { city_dhaka, 2022, MONTH_feb, 0.10401164671627305, },
+[490] = { city_dhaka, 2022, MONTH_mar, 0.013749595600129408, },
+[491] = { city_dhaka, 2022, MONTH_apr, 8.087997411840828e-4, },
+[492] = { city_dhaka, 2022, MONTH_may, 6.470397929472663e-4, },
+[493] = { city_dubai, 2020, MONTH_jan, 0, },
+[494] = { city_dubai, 2020, MONTH_feb, 0, },
+[495] = { city_dubai, 2020, MONTH_mar, 0.016, },
+[496] = { city_dubai, 2020, MONTH_apr, 0.24533333333333332, },
+[497] = { city_dubai, 2020, MONTH_may, 0.43733333333333335, },
+[498] = { city_dubai, 2020, MONTH_jun, 0.13866666666666666, },
+[499] = { city_dubai, 2020, MONTH_jul, 0.09333333333333334, },
+[500] = { city_dubai, 2020, MONTH_aug, 0.088, },
+[501] = { city_dubai, 2020, MONTH_sep, 0.09066666666666667, },
+[502] = { city_dubai, 2020, MONTH_oct, 0.19733333333333333, },
+[503] = { city_dubai, 2020, MONTH_nov, 0.21333333333333335, },
+[504] = { city_dubai, 2020, MONTH_dec, 0.25333333333333335, },
+[505] = { city_dubai, 2021, MONTH_jan, 0.4613333333333333, },
+[506] = { city_dubai, 2021, MONTH_feb, 1, },
+[507] = { city_dubai, 2021, MONTH_mar, 0.744, },
+[508] = { city_dubai, 2021, MONTH_apr, 0.24533333333333332, },
+[509] = { city_dubai, 2021, MONTH_may, 0.248, },
+[510] = { city_dubai, 2021, MONTH_jun, 0.3466666666666667, },
+[511] = { city_dubai, 2021, MONTH_jul, 0.3626666666666667, },
+[512] = { city_dubai, 2021, MONTH_aug, 0.256, },
+[513] = { city_dubai, 2021, MONTH_sep, 0.14933333333333335, },
+[514] = { city_dubai, 2021, MONTH_oct, 0.10933333333333334, },
+[515] = { city_dubai, 2021, MONTH_nov, 0.02666666666666667, },
+[516] = { city_dubai, 2021, MONTH_dec, 0.042666666666666665, },
+[517] = { city_dubai, 2022, MONTH_jan, 0.208, },
+[518] = { city_dubai, 2022, MONTH_feb, 0.16266666666666665, },
+[519] = { city_dubai, 2022, MONTH_mar, 0.0026666666666666666, },
+[520] = { city_dubai, 2022, MONTH_apr, 0, },
+[521] = { city_dubai, 2022, MONTH_may, 0.008, },
+[522] = { city_guatemala_city, 2020, MONTH_jan, 0, },
+[523] = { city_guatemala_city, 2020, MONTH_feb, 0, },
+[524] = { city_guatemala_city, 2020, MONTH_mar, 6.101281269066504e-4, },
+[525] = { city_guatemala_city, 2020, MONTH_apr, 0.009151921903599756, },
+[526] = { city_guatemala_city, 2020, MONTH_may, 0.04514948139109213, },
+[527] = { city_guatemala_city, 2020, MONTH_jun, 0.3886516168395363, },
+[528] = { city_guatemala_city, 2020, MONTH_jul, 0.6955460646735815, },
+[529] = { city_guatemala_city, 2020, MONTH_aug, 0.5326418547895058, },
+[530] = { city_guatemala_city, 2020, MONTH_sep, 0.3038438071995119, },
+[531] = { city_guatemala_city, 2020, MONTH_oct, 0.2904209884075656, },
+[532] = { city_guatemala_city, 2020, MONTH_nov, 0.27577791336180596, },
+[533] = { city_guatemala_city, 2020, MONTH_dec, 0.3886516168395363, },
+[534] = { city_guatemala_city, 2021, MONTH_jan, 0.49725442342892007, },
+[535] = { city_guatemala_city, 2021, MONTH_feb, 0.4612568639414277, },
+[536] = { city_guatemala_city, 2021, MONTH_mar, 0.27394752898108604, },
+[537] = { city_guatemala_city, 2021, MONTH_apr, 0.41305674191580233, },
+[538] = { city_guatemala_city, 2021, MONTH_may, 0.3886516168395363, },
+[539] = { city_guatemala_city, 2021, MONTH_jun, 0.6162294081757169, },
+[540] = { city_guatemala_city, 2021, MONTH_jul, 0.6717510677242221, },
+[541] = { city_guatemala_city, 2021, MONTH_aug, 0.9993898718730934, },
+[542] = { city_guatemala_city, 2021, MONTH_sep, 1, },
+[543] = { city_guatemala_city, 2021, MONTH_oct, 0.9121415497254424, },
+[544] = { city_guatemala_city, 2021, MONTH_nov, 0.5539963392312386, },
+[545] = { city_guatemala_city, 2021, MONTH_dec, 0.10616229408175717, },
+[546] = { city_guatemala_city, 2022, MONTH_jan, 0.16534472239170225, },
+[547] = { city_guatemala_city, 2022, MONTH_feb, 0.3587553386211104, },
+[548] = { city_guatemala_city, 2022, MONTH_mar, 0.20378279438682123, },
+[549] = { city_guatemala_city, 2022, MONTH_apr, 0.1525320317266626, },
+[550] = { city_guatemala_city, 2022, MONTH_may, 0.348993288590604, },
+[551] = { city_hanoi, 2020, MONTH_jan, 0, },
+[552] = { city_hanoi, 2020, MONTH_feb, 0, },
+[553] = { city_hanoi, 2020, MONTH_mar, 0, },
+[554] = { city_hanoi, 2020, MONTH_apr, 0, },
+[555] = { city_hanoi, 2020, MONTH_may, 0, },
+[556] = { city_hanoi, 2020, MONTH_jun, 0, },
+[557] = { city_hanoi, 2020, MONTH_jul, 0, },
+[558] = { city_hanoi, 2020, MONTH_aug, 0.0032313440371604563, },
+[559] = { city_hanoi, 2020, MONTH_sep, 3.029385034837928e-4, },
+[560] = { city_hanoi, 2020, MONTH_oct, 0, },
+[561] = { city_hanoi, 2020, MONTH_nov, 0, },
+[562] = { city_hanoi, 2020, MONTH_dec, 0, },
+[563] = { city_hanoi, 2021, MONTH_jan, 0, },
+[564] = { city_hanoi, 2021, MONTH_feb, 0, },
+[565] = { city_hanoi, 2021, MONTH_mar, 0, },
+[566] = { city_hanoi, 2021, MONTH_apr, 0, },
+[567] = { city_hanoi, 2021, MONTH_may, 0.0012117540139351712, },
+[568] = { city_hanoi, 2021, MONTH_jun, 0.003736241542966778, },
+[569] = { city_hanoi, 2021, MONTH_jul, 0.10875492275068162, },
+[570] = { city_hanoi, 2021, MONTH_aug, 1, },
+[571] = { city_hanoi, 2021, MONTH_sep, 0.8112693123295971, },
+[572] = { city_hanoi, 2021, MONTH_oct, 0.2960718974048268, },
+[573] = { city_hanoi, 2021, MONTH_nov, 0.30546299101282437, },
+[574] = { city_hanoi, 2021, MONTH_dec, 0.7182671917600727, },
+[575] = { city_hanoi, 2022, MONTH_jan, 0.5553872563869534, },
+[576] = { city_hanoi, 2022, MONTH_feb, 0.2500252448752903, },
+[577] = { city_hanoi, 2022, MONTH_mar, 0.23326264768252045, },
+[578] = { city_hanoi, 2022, MONTH_apr, 0.05897202867817833, },
+[579] = { city_hanoi, 2022, MONTH_may, 0.004039180046450571, },
+[580] = { city_harare, 2020, MONTH_jan, 0, },
+[581] = { city_harare, 2020, MONTH_feb, 0, },
+[582] = { city_harare, 2020, MONTH_mar, 0.003470213996529786, },
+[583] = { city_harare, 2020, MONTH_apr, 0.001156737998843262, },
+[584] = { city_harare, 2020, MONTH_may, 0, },
+[585] = { city_harare, 2020, MONTH_jun, 0.001735106998264893, },
+[586] = { city_harare, 2020, MONTH_jul, 0.026604973973395025, },
+[587] = { city_harare, 2020, MONTH_aug, 0.08155002891844997, },
+[588] = { city_harare, 2020, MONTH_sep, 0.017929438982070563, },
+[589] = { city_harare, 2020, MONTH_oct, 0.008097165991902834, },
+[590] = { city_harare, 2020, MONTH_nov, 0.019086176980913822, },
+[591] = { city_harare, 2020, MONTH_dec, 0.048582995951417005, },
+[592] = { city_harare, 2021, MONTH_jan, 0.4817813765182186, },
+[593] = { city_harare, 2021, MONTH_feb, 0.15615962984384038, },
+[594] = { city_harare, 2021, MONTH_mar, 0.0335454019664546, },
+[595] = { city_harare, 2021, MONTH_apr, 0.026604973973395025, },
+[596] = { city_harare, 2021, MONTH_may, 0.015615962984384037, },
+[597] = { city_harare, 2021, MONTH_jun, 0.09658762290341237, },
+[598] = { city_harare, 2021, MONTH_jul, 1, },
+[599] = { city_harare, 2021, MONTH_aug, 0.5355696934644303, },
+[600] = { city_harare, 2021, MONTH_sep, 0.1156737998843262, },
+[601] = { city_harare, 2021, MONTH_oct, 0.035280508964719494, },
+[602] = { city_harare, 2021, MONTH_nov, 0.0167727009832273, },
+[603] = { city_harare, 2021, MONTH_dec, 0.1683053788316946, },
+[604] = { city_harare, 2022, MONTH_jan, 0.19664545980335454, },
+[605] = { city_harare, 2022, MONTH_feb, 0.03296703296703297, },
+[606] = { city_harare, 2022, MONTH_mar, 0.026604973973395025, },
+[607] = { city_harare, 2022, MONTH_apr, 0.0167727009832273, },
+[608] = { city_harare, 2022, MONTH_may, 0.019664545980335454, },
+[609] = { city_islamabad, 2020, MONTH_jan, 0, },
+[610] = { city_islamabad, 2020, MONTH_feb, 0, },
+[611] = { city_islamabad, 2020, MONTH_mar, 0.007521058965102286, },
+[612] = { city_islamabad, 2020, MONTH_apr, 0.09657039711191336, },
+[613] = { city_islamabad, 2020, MONTH_may, 0.34205776173285196, },
+[614] = { city_islamabad, 2020, MONTH_jun, 0.848676293622142, },
+[615] = { city_islamabad, 2020, MONTH_jul, 0.4954873646209386, },
+[616] = { city_islamabad, 2020, MONTH_aug, 0.10318892900120337, },
+[617] = { city_islamabad, 2020, MONTH_sep, 0.05565583634175692, },
+[618] = { city_islamabad, 2020, MONTH_oct, 0.0950661853188929, },
+[619] = { city_islamabad, 2020, MONTH_nov, 0.35800240673886885, },
+[620] = { city_islamabad, 2020, MONTH_dec, 0.6203369434416366, },
+[621] = { city_islamabad, 2021, MONTH_jan, 0.4741275571600481, },
+[622] = { city_islamabad, 2021, MONTH_feb, 0.36522262334536704, },
+[623] = { city_islamabad, 2021, MONTH_mar, 0.45697954271961494, },
+[624] = { city_islamabad, 2021, MONTH_apr, 1, },
+[625] = { city_islamabad, 2021, MONTH_may, 0.9193742478941035, },
+[626] = { city_islamabad, 2021, MONTH_jun, 0.4566787003610108, },
+[627] = { city_islamabad, 2021, MONTH_jul, 0.3131768953068592, },
+[628] = { city_islamabad, 2021, MONTH_aug, 0.7145006016847172, },
+[629] = { city_islamabad, 2021, MONTH_sep, 0.6077015643802648, },
+[630] = { city_islamabad, 2021, MONTH_oct, 0.2253309265944645, },
+[631] = { city_islamabad, 2021, MONTH_nov, 0.08393501805054152, },
+[632] = { city_islamabad, 2021, MONTH_dec, 0.06107099879663057, },
+[633] = { city_islamabad, 2022, MONTH_jan, 0.0983754512635379, },
+[634] = { city_islamabad, 2022, MONTH_feb, 0.2782791817087846, },
+[635] = { city_islamabad, 2022, MONTH_mar, 0.052948255114320095, },
+[636] = { city_islamabad, 2022, MONTH_apr, 0.006016847172081829, },
+[637] = { city_islamabad, 2022, MONTH_may, 0.0030084235860409147, },
+[638] = { city_jakarta, 2020, MONTH_jan, 0, },
+[639] = { city_jakarta, 2020, MONTH_feb, 0, },
+[640] = { city_jakarta, 2020, MONTH_mar, 0.003495784495167592, },
+[641] = { city_jakarta, 2020, MONTH_apr, 0.016862019329631914, },
+[642] = { city_jakarta, 2020, MONTH_may, 0.02110322845979848, },
+[643] = { city_jakarta, 2020, MONTH_jun, 0.032464528069093154, },
+[644] = { city_jakarta, 2020, MONTH_jul, 0.057963191445609706, },
+[645] = { city_jakarta, 2020, MONTH_aug, 0.05876002467612585, },
+[646] = { city_jakarta, 2020, MONTH_sep, 0.08541538145177874, },
+[647] = { city_jakarta, 2020, MONTH_oct, 0.0804287476866132, },
+[648] = { city_jakarta, 2020, MONTH_nov, 0.07906641990540818, },
+[649] = { city_jakarta, 2020, MONTH_dec, 0.13348241826033314, },
+[650] = { city_jakarta, 2021, MONTH_jan, 0.20203578038247996, },
+[651] = { city_jakarta, 2021, MONTH_feb, 0.15854410857495374, },
+[652] = { city_jakarta, 2021, MONTH_mar, 0.12060456508328192, },
+[653] = { city_jakarta, 2021, MONTH_apr, 0.11985914044828296, },
+[654] = { city_jakarta, 2021, MONTH_may, 0.12998663376516553, },
+[655] = { city_jakarta, 2021, MONTH_jun, 0.20339810816368498, },
+[656] = { city_jakarta, 2021, MONTH_jul, 0.9157927205428747, },
+[657] = { city_jakarta, 2021, MONTH_aug, 1, },
+[658] = { city_jakarta, 2021, MONTH_sep, 0.22917951881554596, },
+[659] = { city_jakarta, 2021, MONTH_oct, 0.037682500514085955, },
+[660] = { city_jakarta, 2021, MONTH_nov, 0.010924326547398725, },
+[661] = { city_jakarta, 2021, MONTH_dec, 0.006785934608266502, },
+[662] = { city_jakarta, 2022, MONTH_jan, 0.0058091712934402635, },
+[663] = { city_jakarta, 2022, MONTH_feb, 0.10320275550071972, },
+[664] = { city_jakarta, 2022, MONTH_mar, 0.17360682706148467, },
+[665] = { city_jakarta, 2022, MONTH_apr, 0.030022619782027554, },
+[666] = { city_jakarta, 2022, MONTH_may, 0.008585235451367468, },
+[667] = { city_kyiv, 2020, MONTH_jan, 0, },
+[668] = { city_kyiv, 2020, MONTH_feb, 0, },
+[669] = { city_kyiv, 2020, MONTH_mar, 7.124849282034418e-4, },
+[670] = { city_kyiv, 2020, MONTH_apr, 0.013592020168804122, },
+[671] = { city_kyiv, 2020, MONTH_may, 0.024498520223610654, },
+[672] = { city_kyiv, 2020, MONTH_jun, 0.02471774635536556, },
+[673] = { city_kyiv, 2020, MONTH_jul, 0.029266688589279843, },
+[674] = { city_kyiv, 2020, MONTH_aug, 0.04735284445905952, },
+[675] = { city_kyiv, 2020, MONTH_sep, 0.08615586977967774, },
+[676] = { city_kyiv, 2020, MONTH_oct, 0.16809163652307355, },
+[677] = { city_kyiv, 2020, MONTH_nov, 0.2812123205086046, },
+[678] = { city_kyiv, 2020, MONTH_dec, 0.3401293434177354, },
+[679] = { city_kyiv, 2021, MONTH_jan, 0.22876246848624357, },
+[680] = { city_kyiv, 2021, MONTH_feb, 0.17949139537432862, },
+[681] = { city_kyiv, 2021, MONTH_mar, 0.37504110489970405, },
+[682] = { city_kyiv, 2021, MONTH_apr, 0.6171215608900581, },
+[683] = { city_kyiv, 2021, MONTH_may, 0.35355694398772336, },
+[684] = { city_kyiv, 2021, MONTH_jun, 0.09887098542146223, },
+[685] = { city_kyiv, 2021, MONTH_jul, 0.03315795242792941, },
+[686] = { city_kyiv, 2021, MONTH_aug, 0.046256713800284996, },
+[687] = { city_kyiv, 2021, MONTH_sep, 0.13619423435273484, },
+[688] = { city_kyiv, 2021, MONTH_oct, 0.6278088348131097, },
+[689] = { city_kyiv, 2021, MONTH_nov, 1, },
+[690] = { city_kyiv, 2021, MONTH_dec, 0.5439000328839197, },
+[691] = { city_kyiv, 2022, MONTH_jan, 0.23588731776827798, },
+[692] = { city_kyiv, 2022, MONTH_feb, 0.3148635317329826, },
+[693] = { city_kyiv, 2022, MONTH_mar, 0.11010632467390113, },
+[694] = { city_kyiv, 2022, MONTH_apr, 0.024662939822426833, },
+[695] = { city_kyiv, 2022, MONTH_may, 0.00887865833607366, },
+[696] = { city_la_paz, 2020, MONTH_jan, 0, },
+[697] = { city_la_paz, 2020, MONTH_feb, 0, },
+[698] = { city_la_paz, 2020, MONTH_mar, 0.001688048615800135, },
+[699] = { city_la_paz, 2020, MONTH_apr, 0.01688048615800135, },
+[700] = { city_la_paz, 2020, MONTH_may, 0.08271438217420661, },
+[701] = { city_la_paz, 2020, MONTH_jun, 0.24105334233625927, },
+[702] = { city_la_paz, 2020, MONTH_jul, 0.6056718433490884, },
+[703] = { city_la_paz, 2020, MONTH_aug, 0.7191087103308575, },
+[704] = { city_la_paz, 2020, MONTH_sep, 1, },
+[705] = { city_la_paz, 2020, MONTH_oct, 0.27177582714382176, },
+[706] = { city_la_paz, 2020, MONTH_nov, 0.08237677245104659, },
+[707] = { city_la_paz, 2020, MONTH_dec, 0.06279540850776502, },
+[708] = { city_la_paz, 2021, MONTH_jan, 0.3683322079675895, },
+[709] = { city_la_paz, 2021, MONTH_feb, 0.4669142471303174, },
+[710] = { city_la_paz, 2021, MONTH_mar, 0.20324105334233625, },
+[711] = { city_la_paz, 2021, MONTH_apr, 0.23936529372045914, },
+[712] = { city_la_paz, 2021, MONTH_may, 0.5114787305874409, },
+[713] = { city_la_paz, 2021, MONTH_jun, 0.7413909520594193, },
+[714] = { city_la_paz, 2021, MONTH_jul, 0.38926401080351114, },
+[715] = { city_la_paz, 2021, MONTH_aug, 0.21168129642133693, },
+[716] = { city_la_paz, 2021, MONTH_sep, 0.10297096556380823, },
+[717] = { city_la_paz, 2021, MONTH_oct, 0.07022282241728561, },
+[718] = { city_la_paz, 2021, MONTH_nov, 0.0800135043889264, },
+[719] = { city_la_paz, 2021, MONTH_dec, 0.1650911546252532, },
+[720] = { city_la_paz, 2022, MONTH_jan, 0.4189736664415935, },
+[721] = { city_la_paz, 2022, MONTH_feb, 0.1826468602295746, },
+[722] = { city_la_paz, 2022, MONTH_mar, 0.15597569209993248, },
+[723] = { city_la_paz, 2022, MONTH_apr, 0.0054017555705604325, },
+[724] = { city_la_paz, 2022, MONTH_may, 0.012491559756921, },
+[725] = { city_lima, 2020, MONTH_jan, 0, },
+[726] = { city_lima, 2020, MONTH_feb, 0, },
+[727] = { city_lima, 2020, MONTH_mar, 0.0040497911160371726, },
+[728] = { city_lima, 2020, MONTH_apr, 0.17098644385710632, },
+[729] = { city_lima, 2020, MONTH_may, 0.6555119788558275, },
+[730] = { city_lima, 2020, MONTH_jun, 0.7456304885326968, },
+[731] = { city_lima, 2020, MONTH_jul, 0.7682240600221673, },
+[732] = { city_lima, 2020, MONTH_aug, 0.7700571233694262, },
+[733] = { city_lima, 2020, MONTH_sep, 0.3826839457754284, },
+[734] = { city_lima, 2020, MONTH_oct, 0.1922585045613437, },
+[735] = { city_lima, 2020, MONTH_nov, 0.1284849518288004, },
+[736] = { city_lima, 2020, MONTH_dec, 0.14945860687185608, },
+[737] = { city_lima, 2021, MONTH_jan, 0.41746951999317927, },
+[738] = { city_lima, 2021, MONTH_feb, 0.784423224486316, },
+[739] = { city_lima, 2021, MONTH_mar, 0.8913803393298662, },
+[740] = { city_lima, 2021, MONTH_apr, 1, },
+[741] = { city_lima, 2021, MONTH_may, 0.7605934009719498, },
+[742] = { city_lima, 2021, MONTH_jun, 0.3705772018074857, },
+[743] = { city_lima, 2021, MONTH_jul, 0.1726916190638588, },
+[744] = { city_lima, 2021, MONTH_aug, 0.08504561343678063, },
+[745] = { city_lima, 2021, MONTH_sep, 0.04774490578906983, },
+[746] = { city_lima, 2021, MONTH_oct, 0.03700230198652912, },
+[747] = { city_lima, 2021, MONTH_nov, 0.03883536533378805, },
+[748] = { city_lima, 2021, MONTH_dec, 0.06292096512916702, },
+[749] = { city_lima, 2022, MONTH_jan, 0.11778497740642851, },
+[750] = { city_lima, 2022, MONTH_feb, 0.21570466365419047, },
+[751] = { city_lima, 2022, MONTH_mar, 0.07553926165913548, },
+[752] = { city_lima, 2022, MONTH_apr, 0.026387586324494842, },
+[753] = { city_lima, 2022, MONTH_may, 0.01598601756330463, },
+[754] = { city_madrid, 2020, MONTH_jan, 0, },
+[755] = { city_madrid, 2020, MONTH_feb, 5.852744937375629e-5, },
+[756] = { city_madrid, 2020, MONTH_mar, 0.5019314058293339, },
+[757] = { city_madrid, 2020, MONTH_apr, 1, },
+[758] = { city_madrid, 2020, MONTH_may, 0.2004565141051153, },
+[759] = { city_madrid, 2020, MONTH_jun, 0.038745171485426666, },
+[760] = { city_madrid, 2020, MONTH_jul, 0.010827578134144915, },
+[761] = { city_madrid, 2020, MONTH_aug, 0.062214678684302936, },
+[762] = { city_madrid, 2020, MONTH_sep, 0.18307386164110967, },
+[763] = { city_madrid, 2020, MONTH_oct, 0.3029380779585626, },
+[764] = { city_madrid, 2020, MONTH_nov, 0.5457684654102775, },
+[765] = { city_madrid, 2020, MONTH_dec, 0.3399859534121503, },
+[766] = { city_madrid, 2021, MONTH_jan, 0.6453821842444106, },
+[767] = { city_madrid, 2021, MONTH_feb, 0.5533770338288657, },
+[768] = { city_madrid, 2021, MONTH_mar, 0.18658550860353507, },
+[769] = { city_madrid, 2021, MONTH_apr, 0.13666159428772093, },
+[770] = { city_madrid, 2021, MONTH_may, 0.08673767997190683, },
+[771] = { city_madrid, 2021, MONTH_jun, 0.03775020484607281, },
+[772] = { city_madrid, 2021, MONTH_jul, 0.059229778766241366, },
+[773] = { city_madrid, 2021, MONTH_aug, 0.1822544773498771, },
+[774] = { city_madrid, 2021, MONTH_sep, 0.093585391548636307, },
+[775] = { city_madrid, 2021, MONTH_oct, 0.03494088727613251, },
+[776] = { city_madrid, 2021, MONTH_nov, 0.03616996371298139, },
+[777] = { city_madrid, 2021, MONTH_dec, 0.1144796909750673, },
+[778] = { city_madrid, 2022, MONTH_jan, 0.32476881657497364, },
+[779] = { city_madrid, 2022, MONTH_feb, 0.27689336298724104, },
+[780] = { city_madrid, 2022, MONTH_mar, 0.10125248741659838, },
+[781] = { city_madrid, 2022, MONTH_apr, 0.08351867025635022, },
+[782] = { city_madrid, 2022, MONTH_may, 0.10722228725272152, },
+[783] = { city_managua, 2020, MONTH_jan, 0, },
+[784] = { city_managua, 2020, MONTH_feb, 0, },
+[785] = { city_managua, 2020, MONTH_mar, 0.023809523809523808, },
+[786] = { city_managua, 2020, MONTH_apr, 0.047619047619047616, },
+[787] = { city_managua, 2020, MONTH_may, 0.7619047619047619, },
+[788] = { city_managua, 2020, MONTH_jun, 0.9285714285714286, },
+[789] = { city_managua, 2020, MONTH_jul, 1, },
+[790] = { city_managua, 2020, MONTH_aug, 0.5, },
+[791] = { city_managua, 2020, MONTH_sep, 0.3333333333333333, },
+[792] = { city_managua, 2020, MONTH_oct, 0.11904761904761904, },
+[793] = { city_managua, 2020, MONTH_nov, 0.09523809523809523, },
+[794] = { city_managua, 2020, MONTH_dec, 0.11904761904761904, },
+[795] = { city_managua, 2021, MONTH_jan, 0.09523809523809523, },
+[796] = { city_managua, 2021, MONTH_feb, 0.09523809523809523, },
+[797] = { city_managua, 2021, MONTH_mar, 0.11904761904761904, },
+[798] = { city_managua, 2021, MONTH_apr, 0.09523809523809523, },
+[799] = { city_managua, 2021, MONTH_may, 0.09523809523809523, },
+[800] = { city_managua, 2021, MONTH_jun, 0.11904761904761904, },
+[801] = { city_managua, 2021, MONTH_jul, 0.09523809523809523, },
+[802] = { city_managua, 2021, MONTH_aug, 0.09523809523809523, },
+[803] = { city_managua, 2021, MONTH_sep, 0.11904761904761904, },
+[804] = { city_managua, 2021, MONTH_oct, 0.09523809523809523, },
+[805] = { city_managua, 2021, MONTH_nov, 0.09523809523809523, },
+[806] = { city_managua, 2021, MONTH_dec, 0.11904761904761904, },
+[807] = { city_managua, 2022, MONTH_jan, 0.07142857142857142, },
+[808] = { city_managua, 2022, MONTH_feb, 0.09523809523809523, },
+[809] = { city_managua, 2022, MONTH_mar, 0.14285714285714285, },
+[810] = { city_managua, 2022, MONTH_apr, 0.09523809523809523, },
+[811] = { city_managua, 2022, MONTH_may, 0.09523809523809523, },
+[812] = { city_mexico_city, 2020, MONTH_jan, 0, },
+[813] = { city_mexico_city, 2020, MONTH_feb, 0, },
+[814] = { city_mexico_city, 2020, MONTH_mar, 0.0013482679941920763, },
+[815] = { city_mexico_city, 2020, MONTH_apr, 0.1015608794855839, },
+[816] = { city_mexico_city, 2020, MONTH_may, 0.40523231694669154, },
+[817] = { city_mexico_city, 2020, MONTH_jun, 0.5275876374196224, },
+[818] = { city_mexico_city, 2020, MONTH_jul, 0.6168066791122174, },
+[819] = { city_mexico_city, 2020, MONTH_aug, 0.4998703588467123, },
+[820] = { city_mexico_city, 2020, MONTH_sep, 0.34160443891308856, },
+[821] = { city_mexico_city, 2020, MONTH_oct, 0.32690313213026345, },
+[822] = { city_mexico_city, 2020, MONTH_nov, 0.4108328147687202, },
+[823] = { city_mexico_city, 2020, MONTH_dec, 0.5958566687409251, },
+[824] = { city_mexico_city, 2021, MONTH_jan, 1, },
+[825] = { city_mexico_city, 2021, MONTH_feb, 0.6428386226923875, },
+[826] = { city_mexico_city, 2021, MONTH_mar, 0.3536869943995022, },
+[827] = { city_mexico_city, 2021, MONTH_apr, 0.20423667288944203, },
+[828] = { city_mexico_city, 2021, MONTH_may, 0.10806886538062642, },
+[829] = { city_mexico_city, 2021, MONTH_jun, 0.080196017423771, },
+[830] = { city_mexico_city, 2021, MONTH_jul, 0.20589607965152457, },
+[831] = { city_mexico_city, 2021, MONTH_aug, 0.5757104335200166, },
+[832] = { city_mexico_city, 2021, MONTH_sep, 0.4466397013067828, },
+[833] = { city_mexico_city, 2021, MONTH_oct, 0.22204936735117195, },
+[834] = { city_mexico_city, 2021, MONTH_nov, 0.11226923874714789, },
+[835] = { city_mexico_city, 2021, MONTH_dec, 0.09852727649865173, },
+[836] = { city_mexico_city, 2022, MONTH_jan, 0.2316946691557768, },
+[837] = { city_mexico_city, 2022, MONTH_feb, 0.27434660858743, },
+[838] = { city_mexico_city, 2022, MONTH_mar, 0.04877100186683261, },
+[839] = { city_mexico_city, 2022, MONTH_apr, 0.008893383115536196, },
+[840] = { city_mexico_city, 2022, MONTH_may, 0.004252229827836548, },
+[841] = { city_moscow, 2020, MONTH_jan, 0, },
+[842] = { city_moscow, 2020, MONTH_feb, 0, },
+[843] = { city_moscow, 2020, MONTH_mar, 4.6378393125085253e-4, },
+[844] = { city_moscow, 2020, MONTH_apr, 0.028809166552994134, },
+[845] = { city_moscow, 2020, MONTH_may, 0.09875869594871095, },
+[846] = { city_moscow, 2020, MONTH_jun, 0.1262310735233938, },
+[847] = { city_moscow, 2020, MONTH_jul, 0.12666757604692402, },
+[848] = { city_moscow, 2020, MONTH_aug, 0.08765516300641113, },
+[849] = { city_moscow, 2020, MONTH_sep, 0.09673987177738372, },
+[850] = { city_moscow, 2020, MONTH_oct, 0.1982812713135998, },
+[851] = { city_moscow, 2020, MONTH_nov, 0.32478515891419996, },
+[852] = { city_moscow, 2020, MONTH_dec, 0.4671668258082117, },
+[853] = { city_moscow, 2021, MONTH_jan, 0.4409493929886782, },
+[854] = { city_moscow, 2021, MONTH_feb, 0.3530214159050607, },
+[855] = { city_moscow, 2021, MONTH_mar, 0.3472377574682854, },
+[856] = { city_moscow, 2021, MONTH_apr, 0.3076797162733597, },
+[857] = { city_moscow, 2021, MONTH_may, 0.3102714500068203, },
+[858] = { city_moscow, 2021, MONTH_jun, 0.37410994407311415, },
+[859] = { city_moscow, 2021, MONTH_jul, 0.6369935888691857, },
+[860] = { city_moscow, 2021, MONTH_aug, 0.6727867957986632, },
+[861] = { city_moscow, 2021, MONTH_sep, 0.655599508934661, },
+[862] = { city_moscow, 2021, MONTH_oct, 0.8534442777247306, },
+[863] = { city_moscow, 2021, MONTH_nov, 1, },
+[864] = { city_moscow, 2021, MONTH_dec, 0.9184831537307325, },
+[865] = { city_moscow, 2022, MONTH_jan, 0.6135315782294366, },
+[866] = { city_moscow, 2022, MONTH_feb, 0.5541126722138863, },
+[867] = { city_moscow, 2022, MONTH_mar, 0.46547537852953214, },
+[868] = { city_moscow, 2022, MONTH_apr, 0.1953894420952121, },
+[869] = { city_moscow, 2022, MONTH_may, 0.0882007911608239, },
+[870] = { city_mumbai, 2020, MONTH_jan, 0, },
+[871] = { city_mumbai, 2020, MONTH_feb, 0, },
+[872] = { city_mumbai, 2020, MONTH_mar, 2.6496646518175043e-4, },
+[873] = { city_mumbai, 2020, MONTH_apr, 0.008627970522480748, },
+[874] = { city_mumbai, 2020, MONTH_may, 0.03386602633104248, },
+[875] = { city_mumbai, 2020, MONTH_jun, 0.09711848969114846, },
+[876] = { city_mumbai, 2020, MONTH_jul, 0.15611492920427258, },
+[877] = { city_mumbai, 2020, MONTH_aug, 0.23782396290469487, },
+[878] = { city_mumbai, 2020, MONTH_sep, 0.2734785128757142, },
+[879] = { city_mumbai, 2020, MONTH_oct, 0.1999171979796307, },
+[880] = { city_mumbai, 2020, MONTH_nov, 0.1283265711683365, },
+[881] = { city_mumbai, 2020, MONTH_dec, 0.09604206342634761, },
+[882] = { city_mumbai, 2021, MONTH_jan, 0.04583919847644283, },
+[883] = { city_mumbai, 2021, MONTH_feb, 0.02299412105655378, },
+[884] = { city_mumbai, 2021, MONTH_mar, 0.04485385443404819, },
+[885] = { city_mumbai, 2021, MONTH_apr, 0.37974662581766994, },
+[886] = { city_mumbai, 2021, MONTH_may, 1, },
+[887] = { city_mumbai, 2021, MONTH_jun, 0.5742651320692225, },
+[888] = { city_mumbai, 2021, MONTH_jul, 0.2099528028483895, },
+[889] = { city_mumbai, 2021, MONTH_aug, 0.1221329800447131, },
+[890] = { city_mumbai, 2021, MONTH_sep, 0.07867847975490602, },
+[891] = { city_mumbai, 2021, MONTH_oct, 0.0838287654218763, },
+[892] = { city_mumbai, 2021, MONTH_nov, 0.0893765007866192, },
+[893] = { city_mumbai, 2021, MONTH_dec, 0.10019044464684938, },
+[894] = { city_mumbai, 2022, MONTH_jan, 0.11567442245590792, },
+[895] = { city_mumbai, 2022, MONTH_feb, 0.15560983688001986, },
+[896] = { city_mumbai, 2022, MONTH_mar, 0.0603295520410698, },
+[897] = { city_mumbai, 2022, MONTH_apr, 0.02214126024675002, },
+[898] = { city_mumbai, 2022, MONTH_may, 0.006847727084540863, },
+[899] = { city_nairobi, 2020, MONTH_jan, 0, },
+[900] = { city_nairobi, 2020, MONTH_feb, 0, },
+[901] = { city_nairobi, 2020, MONTH_mar, 0.005037783375314861, },
+[902] = { city_nairobi, 2020, MONTH_apr, 0.012594458438287154, },
+[903] = { city_nairobi, 2020, MONTH_may, 0.061712846347607056, },
+[904] = { city_nairobi, 2020, MONTH_jun, 0.10201511335012595, },
+[905] = { city_nairobi, 2020, MONTH_jul, 0.22795969773299748, },
+[906] = { city_nairobi, 2020, MONTH_aug, 0.3136020151133501, },
+[907] = { city_nairobi, 2020, MONTH_sep, 0.16750629722921914, },
+[908] = { city_nairobi, 2020, MONTH_oct, 0.345088161209068, },
+[909] = { city_nairobi, 2020, MONTH_nov, 0.593198992443325, },
+[910] = { city_nairobi, 2020, MONTH_dec, 0.2707808564231738, },
+[911] = { city_nairobi, 2021, MONTH_jan, 0.11083123425692695, },
+[912] = { city_nairobi, 2021, MONTH_feb, 0.12468513853904283, },
+[913] = { city_nairobi, 2021, MONTH_mar, 0.3690176322418136, },
+[914] = { city_nairobi, 2021, MONTH_apr, 0.7052896725440806, },
+[915] = { city_nairobi, 2021, MONTH_may, 0.5667506297229219, },
+[916] = { city_nairobi, 2021, MONTH_jun, 0.5843828715365239, },
+[917] = { city_nairobi, 2021, MONTH_jul, 0.38413098236775817, },
+[918] = { city_nairobi, 2021, MONTH_aug, 1, },
+[919] = { city_nairobi, 2021, MONTH_sep, 0.5025188916876574, },
+[920] = { city_nairobi, 2021, MONTH_oct, 0.1977329974811083, },
+[921] = { city_nairobi, 2021, MONTH_nov, 0.07304785894206549, },
+[922] = { city_nairobi, 2021, MONTH_dec, 0.05289672544080604, },
+[923] = { city_nairobi, 2022, MONTH_jan, 0.25692695214105793, },
+[924] = { city_nairobi, 2022, MONTH_feb, 0.0743073047858942, },
+[925] = { city_nairobi, 2022, MONTH_mar, 0.011335012594458438, },
+[926] = { city_nairobi, 2022, MONTH_apr, 0.0012594458438287153, },
+[927] = { city_nairobi, 2022, MONTH_may, 0.0025188916876574307, },
+[928] = { city_new_delhi, 2020, MONTH_jan, 0, },
+[929] = { city_new_delhi, 2020, MONTH_feb, 0, },
+[930] = { city_new_delhi, 2020, MONTH_mar, 2.6496646518175043e-4, },
+[931] = { city_new_delhi, 2020, MONTH_apr, 0.008627970522480748, },
+[932] = { city_new_delhi, 2020, MONTH_may, 0.03386602633104248, },
+[933] = { city_new_delhi, 2020, MONTH_jun, 0.09711848969114846, },
+[934] = { city_new_delhi, 2020, MONTH_jul, 0.15611492920427258, },
+[935] = { city_new_delhi, 2020, MONTH_aug, 0.23782396290469487, },
+[936] = { city_new_delhi, 2020, MONTH_sep, 0.2734785128757142, },
+[937] = { city_new_delhi, 2020, MONTH_oct, 0.1999171979796307, },
+[938] = { city_new_delhi, 2020, MONTH_nov, 0.1283265711683365, },
+[939] = { city_new_delhi, 2020, MONTH_dec, 0.09604206342634761, },
+[940] = { city_new_delhi, 2021, MONTH_jan, 0.04583919847644283, },
+[941] = { city_new_delhi, 2021, MONTH_feb, 0.02299412105655378, },
+[942] = { city_new_delhi, 2021, MONTH_mar, 0.04485385443404819, },
+[943] = { city_new_delhi, 2021, MONTH_apr, 0.37974662581766994, },
+[944] = { city_new_delhi, 2021, MONTH_may, 1, },
+[945] = { city_new_delhi, 2021, MONTH_jun, 0.5742651320692225, },
+[946] = { city_new_delhi, 2021, MONTH_jul, 0.2099528028483895, },
+[947] = { city_new_delhi, 2021, MONTH_aug, 0.1221329800447131, },
+[948] = { city_new_delhi, 2021, MONTH_sep, 0.07867847975490602, },
+[949] = { city_new_delhi, 2021, MONTH_oct, 0.0838287654218763, },
+[950] = { city_new_delhi, 2021, MONTH_nov, 0.0893765007866192, },
+[951] = { city_new_delhi, 2021, MONTH_dec, 0.10019044464684938, },
+[952] = { city_new_delhi, 2022, MONTH_jan, 0.11567442245590792, },
+[953] = { city_new_delhi, 2022, MONTH_feb, 0.15560983688001986, },
+[954] = { city_new_delhi, 2022, MONTH_mar, 0.0603295520410698, },
+[955] = { city_new_delhi, 2022, MONTH_apr, 0.02214126024675002, },
+[956] = { city_new_delhi, 2022, MONTH_may, 0.006847727084540863, },
+[957] = { city_ottawa, 2020, MONTH_jan, 0, },
+[958] = { city_ottawa, 2020, MONTH_feb, 0, },
+[959] = { city_ottawa, 2020, MONTH_mar, 0.009571558796718322, },
+[960] = { city_ottawa, 2020, MONTH_apr, 0.6406107566089334, },
+[961] = { city_ottawa, 2020, MONTH_may, 0.9357338195077484, },
+[962] = { city_ottawa, 2020, MONTH_jun, 0.3484503190519599, },
+[963] = { city_ottawa, 2020, MONTH_jul, 0.08956244302643573, },
+[964] = { city_ottawa, 2020, MONTH_aug, 0.04421148587055606, },
+[965] = { city_ottawa, 2020, MONTH_sep, 0.037374658158614404, },
+[966] = { city_ottawa, 2020, MONTH_oct, 0.17889699179580676, },
+[967] = { city_ottawa, 2020, MONTH_nov, 0.43094804010938925, },
+[968] = { city_ottawa, 2020, MONTH_dec, 0.7654968094804011, },
+[969] = { city_ottawa, 2021, MONTH_jan, 1, },
+[970] = { city_ottawa, 2021, MONTH_feb, 0.4731084776663628, },
+[971] = { city_ottawa, 2021, MONTH_mar, 0.21923427529626252, },
+[972] = { city_ottawa, 2021, MONTH_apr, 0.270510483135825, },
+[973] = { city_ottawa, 2021, MONTH_may, 0.3076572470373747, },
+[974] = { city_ottawa, 2021, MONTH_jun, 0.17069279854147676, },
+[975] = { city_ottawa, 2021, MONTH_jul, 0.07338195077484047, },
+[976] = { city_ottawa, 2021, MONTH_aug, 0.0736098450319052, },
+[977] = { city_ottawa, 2021, MONTH_sep, 0.1927985414767548, },
+[978] = { city_ottawa, 2021, MONTH_oct, 0.2716499544211486, },
+[979] = { city_ottawa, 2021, MONTH_nov, 0.15633546034639928, },
+[980] = { city_ottawa, 2021, MONTH_dec, 0.13742023701002734, },
+[981] = { city_ottawa, 2022, MONTH_jan, 0.7474931631722881, },
+[982] = { city_ottawa, 2022, MONTH_feb, 0.6488149498632635, },
+[983] = { city_ottawa, 2022, MONTH_mar, 0.33887876025524155, },
+[984] = { city_ottawa, 2022, MONTH_apr, 0.3669097538742024, },
+[985] = { city_ottawa, 2022, MONTH_may, 0.4453053783044667, },
+[986] = { city_paris, 2020, MONTH_jan, 0, },
+[987] = { city_paris, 2020, MONTH_feb, 9.50705899130104e-5, },
+[988] = { city_paris, 2020, MONTH_mar, 0.1433189142938632, },
+[989] = { city_paris, 2020, MONTH_apr, 1, },
+[990] = { city_paris, 2020, MONTH_may, 0.22165708038218376, },
+[991] = { city_paris, 2020, MONTH_jun, 0.04815325379093977, },
+[992] = { city_paris, 2020, MONTH_jul, 0.019299329752341114, },
+[993] = { city_paris, 2020, MONTH_aug, 0.015734182630603222, },
+[994] = { city_paris, 2020, MONTH_sep, 0.05813566573180587, },
+[995] = { city_paris, 2020, MONTH_oct, 0.21737890383609831, },
+[996] = { city_paris, 2020, MONTH_nov, 0.7463992014070447, },
+[997] = { city_paris, 2020, MONTH_dec, 0.5722774159813662, },
+[998] = { city_paris, 2021, MONTH_jan, 0.5448495507914627, },
+[999] = { city_paris, 2021, MONTH_feb, 0.49341636164852404, },
+[1000] = { city_paris, 2021, MONTH_mar, 0.42235109568854873, },
+[1001] = { city_paris, 2021, MONTH_apr, 0.4173598897181157, },
+[1002] = { city_paris, 2021, MONTH_may, 0.23991063364548176, },
+[1003] = { city_paris, 2021, MONTH_jun, 0.07367970718258307, },
+[1004] = { city_paris, 2021, MONTH_jul, 0.03085040642677188, },
+[1005] = { city_paris, 2021, MONTH_aug, 0.08803536625944763, },
+[1006] = { city_paris, 2021, MONTH_sep, 0.08480296620240528, },
+[1007] = { city_paris, 2021, MONTH_oct, 0.03988211246850787, },
+[1008] = { city_paris, 2021, MONTH_nov, 0.06241384227789133, },
+[1009] = { city_paris, 2021, MONTH_dec, 0.20996339782288348, },
+[1010] = { city_paris, 2022, MONTH_jan, 0.32376289394875696, },
+[1011] = { city_paris, 2022, MONTH_feb, 0.3461995531682274, },
+[1012] = { city_paris, 2022, MONTH_mar, 0.18514997385558776, },
+[1013] = { city_paris, 2022, MONTH_apr, 0.1745496030802871, },
+[1014] = { city_paris, 2022, MONTH_may, 0.1104244901839616, },
+[1015] = { city_quito, 2020, MONTH_jan, 0, },
+[1016] = { city_quito, 2020, MONTH_feb, 0, },
+[1017] = { city_quito, 2020, MONTH_mar, 0.005972526378658172, },
+[1018] = { city_quito, 2020, MONTH_apr, 0.08192315349392792, },
+[1019] = { city_quito, 2020, MONTH_may, 0.24517220784391797, },
+[1020] = { city_quito, 2020, MONTH_jun, 0.11507067489548078, },
+[1021] = { city_quito, 2020, MONTH_jul, 0.11497113278916982, },
+[1022] = { city_quito, 2020, MONTH_aug, 0.08938881146725065, },
+[1023] = { city_quito, 2020, MONTH_sep, 0.4735217997212821, },
+[1024] = { city_quito, 2020, MONTH_oct, 0.1313955803304798, },
+[1025] = { city_quito, 2020, MONTH_nov, 0.0787378060919769, },
+[1026] = { city_quito, 2020, MONTH_dec, 0.059725263786581724, },
+[1027] = { city_quito, 2021, MONTH_jan, 0.08242086402548278, },
+[1028] = { city_quito, 2021, MONTH_feb, 0.08580529564005575, },
+[1029] = { city_quito, 2021, MONTH_mar, 0.1062114274338045, },
+[1030] = { city_quito, 2021, MONTH_apr, 0.17638861238303802, },
+[1031] = { city_quito, 2021, MONTH_may, 0.1983874178777623, },
+[1032] = { city_quito, 2021, MONTH_jun, 0.09954210631096955, },
+[1033] = { city_quito, 2021, MONTH_jul, 1, },
+[1034] = { city_quito, 2021, MONTH_aug, 0.0650009954210631, },
+[1035] = { city_quito, 2021, MONTH_sep, 0.05156281106908222, },
+[1036] = { city_quito, 2021, MONTH_oct, 0.02150109496316942, },
+[1037] = { city_quito, 2021, MONTH_nov, 0.025482779215608202, },
+[1038] = { city_quito, 2021, MONTH_dec, 0.04330081624527175, },
+[1039] = { city_quito, 2022, MONTH_jan, 0.0840135377264583, },
+[1040] = { city_quito, 2022, MONTH_feb, 0.07276527971331874, },
+[1041] = { city_quito, 2022, MONTH_mar, 0.017618952817041608, },
+[1042] = { city_quito, 2022, MONTH_apr, 0.016623531753931914, },
+[1043] = { city_quito, 2022, MONTH_may, 0.00437985267768266, },
+[1044] = { city_rabat, 2020, MONTH_jan, 0, },
+[1045] = { city_rabat, 2020, MONTH_feb, 0, },
+[1046] = { city_rabat, 2020, MONTH_mar, 0.011752136752136752, },
+[1047] = { city_rabat, 2020, MONTH_apr, 0.04807692307692308, },
+[1048] = { city_rabat, 2020, MONTH_may, 0.01282051282051282, },
+[1049] = { city_rabat, 2020, MONTH_jun, 0.007478632478632479, },
+[1050] = { city_rabat, 2020, MONTH_jul, 0.04309116809116809, },
+[1051] = { city_rabat, 2020, MONTH_aug, 0.2724358974358974, },
+[1052] = { city_rabat, 2020, MONTH_sep, 0.37072649572649574, },
+[1053] = { city_rabat, 2020, MONTH_oct, 0.5245726495726496, },
+[1054] = { city_rabat, 2020, MONTH_nov, 0.7706552706552706, },
+[1055] = { city_rabat, 2020, MONTH_dec, 0.5576923076923077, },
+[1056] = { city_rabat, 2021, MONTH_jan, 0.32193732193732194, },
+[1057] = { city_rabat, 2021, MONTH_feb, 0.1267806267806268, },
+[1058] = { city_rabat, 2021, MONTH_mar, 0.07051282051282051, },
+[1059] = { city_rabat, 2021, MONTH_apr, 0.07371794871794872, },
+[1060] = { city_rabat, 2021, MONTH_may, 0.0438034188034188, },
+[1061] = { city_rabat, 2021, MONTH_jun, 0.053062678062678066, },
+[1062] = { city_rabat, 2021, MONTH_jul, 0.15669515669515668, },
+[1063] = { city_rabat, 2021, MONTH_aug, 1, },
+[1064] = { city_rabat, 2021, MONTH_sep, 0.6082621082621082, },
+[1065] = { city_rabat, 2021, MONTH_oct, 0.1492165242165242, },
+[1066] = { city_rabat, 2021, MONTH_nov, 0.038461538461538464, },
+[1067] = { city_rabat, 2021, MONTH_dec, 0.024572649572649572, },
+[1068] = { city_rabat, 2022, MONTH_jan, 0.18447293447293447, },
+[1069] = { city_rabat, 2022, MONTH_feb, 0.21937321937321938, },
+[1070] = { city_rabat, 2022, MONTH_mar, 0.0292022792022792, },
+[1071] = { city_rabat, 2022, MONTH_apr, 0.003205128205128205, },
+[1072] = { city_rabat, 2022, MONTH_may, 0.002492877492877493, },
+[1073] = { city_seoul, 2020, MONTH_jan, 0, },
+[1074] = { city_seoul, 2020, MONTH_feb, 0.0019579050416054823, },
+[1075] = { city_seoul, 2020, MONTH_mar, 0.017865883504650026, },
+[1076] = { city_seoul, 2020, MONTH_apr, 0.010401370533529124, },
+[1077] = { city_seoul, 2020, MONTH_may, 0.0028144884973078806, },
+[1078] = { city_seoul, 2020, MONTH_jun, 0.0014684287812041115, },
+[1079] = { city_seoul, 2020, MONTH_jul, 0.00232501223690651, },
+[1080] = { city_seoul, 2020, MONTH_aug, 0.0028144884973078806, },
+[1081] = { city_seoul, 2020, MONTH_sep, 0.010890846793930495, },
+[1082] = { city_seoul, 2020, MONTH_oct, 0.006240822320117474, },
+[1083] = { city_seoul, 2020, MONTH_nov, 0.0075868820362212435, },
+[1084] = { city_seoul, 2020, MONTH_dec, 0.045766030347528144, },
+[1085] = { city_seoul, 2021, MONTH_jan, 0.06363191385217817, },
+[1086] = { city_seoul, 2021, MONTH_feb, 0.022393538913362702, },
+[1087] = { city_seoul, 2021, MONTH_mar, 0.01566324033284386, },
+[1088] = { city_seoul, 2021, MONTH_apr, 0.011869799314733235, },
+[1089] = { city_seoul, 2021, MONTH_may, 0.016030347528144886, },
+[1090] = { city_seoul, 2021, MONTH_jun, 0.007219774840920215, },
+[1091] = { city_seoul, 2021, MONTH_jul, 0.009422418012726383, },
+[1092] = { city_seoul, 2021, MONTH_aug, 0.0232501223690651, },
+[1093] = { city_seoul, 2021, MONTH_sep, 0.023984336759667158, },
+[1094] = { city_seoul, 2021, MONTH_oct, 0.04503181595692609, },
+[1095] = { city_seoul, 2021, MONTH_nov, 0.09483602545276554, },
+[1096] = { city_seoul, 2021, MONTH_dec, 0.23727361722956436, },
+[1097] = { city_seoul, 2022, MONTH_jan, 0.14586392559960842, },
+[1098] = { city_seoul, 2022, MONTH_feb, 0.15944689182574645, },
+[1099] = { city_seoul, 2022, MONTH_mar, 1, },
+[1100] = { city_seoul, 2022, MONTH_apr, 0.8032305433186491, },
+[1101] = { city_seoul, 2022, MONTH_may, 0.16911404796867352, },
+[1102] = { city_tehran, 2020, MONTH_jan, 0, },
+[1103] = { city_tehran, 2020, MONTH_feb, 0.00255845778544654, },
+[1104] = { city_tehran, 2020, MONTH_mar, 0.16986969714999703, },
+[1105] = { city_tehran, 2020, MONTH_apr, 0.18200749687630155, },
+[1106] = { city_tehran, 2020, MONTH_may, 0.10572975545903493, },
+[1107] = { city_tehran, 2020, MONTH_jun, 0.1746891176295591, },
+[1108] = { city_tehran, 2020, MONTH_jul, 0.3509847087523056, },
+[1109] = { city_tehran, 2020, MONTH_aug, 0.2911286963765098, },
+[1110] = { city_tehran, 2020, MONTH_sep, 0.2691735586362825, },
+[1111] = { city_tehran, 2020, MONTH_oct, 0.5052656631165586, },
+[1112] = { city_tehran, 2020, MONTH_nov, 0.7971083477122628, },
+[1113] = { city_tehran, 2020, MONTH_dec, 0.42958291188195397, },
+[1114] = { city_tehran, 2021, MONTH_jan, 0.1662402570357589, },
+[1115] = { city_tehran, 2021, MONTH_feb, 0.12441244719462129, },
+[1116] = { city_tehran, 2021, MONTH_mar, 0.1540429582911882, },
+[1117] = { city_tehran, 2021, MONTH_apr, 0.5225203784137562, },
+[1118] = { city_tehran, 2021, MONTH_may, 0.5109775688701137, },
+[1119] = { city_tehran, 2021, MONTH_jun, 0.24918188849883977, },
+[1120] = { city_tehran, 2021, MONTH_jul, 0.3699053965609567, },
+[1121] = { city_tehran, 2021, MONTH_aug, 1, },
+[1122] = { city_tehran, 2021, MONTH_sep, 0.7740227286249777, },
+[1123] = { city_tehran, 2021, MONTH_oct, 0.3549711429761409, },
+[1124] = { city_tehran, 2021, MONTH_nov, 0.213303980484322, },
+[1125] = { city_tehran, 2021, MONTH_dec, 0.11072767299339561, },
+[1126] = { city_tehran, 2022, MONTH_jan, 0.050693163562801215, },
+[1127] = { city_tehran, 2022, MONTH_feb, 0.25031236984589755, },
+[1128] = { city_tehran, 2022, MONTH_mar, 0.20985303742488248, },
+[1129] = { city_tehran, 2022, MONTH_apr, 0.05438210269530553, },
+[1130] = { city_tehran, 2022, MONTH_may, 0.014160766347355268, },
+[1131] = { city_tokyo, 2020, MONTH_jan, 0, },
+[1132] = { city_tokyo, 2020, MONTH_feb, 0.0010548523206751054, },
+[1133] = { city_tokyo, 2020, MONTH_mar, 0.010759493670886076, },
+[1134] = { city_tokyo, 2020, MONTH_apr, 0.07573839662447257, },
+[1135] = { city_tokyo, 2020, MONTH_may, 0.10042194092827005, },
+[1136] = { city_tokyo, 2020, MONTH_jun, 0.01751054852320675, },
+[1137] = { city_tokyo, 2020, MONTH_jul, 0.006751054852320675, },
+[1138] = { city_tokyo, 2020, MONTH_aug, 0.05759493670886076, },
+[1139] = { city_tokyo, 2020, MONTH_sep, 0.060126582278481014, },
+[1140] = { city_tokyo, 2020, MONTH_oct, 0.04029535864978903, },
+[1141] = { city_tokyo, 2020, MONTH_nov, 0.07679324894514768, },
+[1142] = { city_tokyo, 2020, MONTH_dec, 0.2732067510548523, },
+[1143] = { city_tokyo, 2021, MONTH_jan, 0.47257383966244726, },
+[1144] = { city_tokyo, 2021, MONTH_feb, 0.46540084388185654, },
+[1145] = { city_tokyo, 2021, MONTH_mar, 0.26434599156118144, },
+[1146] = { city_tokyo, 2021, MONTH_apr, 0.22805907172995782, },
+[1147] = { city_tokyo, 2021, MONTH_may, 0.5850210970464135, },
+[1148] = { city_tokyo, 2021, MONTH_jun, 0.3740506329113924, },
+[1149] = { city_tokyo, 2021, MONTH_jul, 0.09367088607594937, },
+[1150] = { city_tokyo, 2021, MONTH_aug, 0.17088607594936708, },
+[1151] = { city_tokyo, 2021, MONTH_sep, 0.33987341772151897, },
+[1152] = { city_tokyo, 2021, MONTH_oct, 0.13839662447257384, },
+[1153] = { city_tokyo, 2021, MONTH_nov, 0.02067510548523207, },
+[1154] = { city_tokyo, 2021, MONTH_dec, 0.007172995780590718, },
+[1155] = { city_tokyo, 2022, MONTH_jan, 0.07827004219409282, },
+[1156] = { city_tokyo, 2022, MONTH_feb, 0.950632911392405, },
+[1157] = { city_tokyo, 2022, MONTH_mar, 1, },
+[1158] = { city_tokyo, 2022, MONTH_apr, 0.32447257383966244, },
+[1159] = { city_tokyo, 2022, MONTH_may, 0.21814345991561182, },
+[1160] = { city_tunis, 2020, MONTH_jan, 0, },
+[1161] = { city_tunis, 2020, MONTH_feb, 0, },
+[1162] = { city_tunis, 2020, MONTH_mar, 0.001858352260995251, },
+[1163] = { city_tunis, 2020, MONTH_apr, 0.006400991121205864, },
+[1164] = { city_tunis, 2020, MONTH_may, 0.001651868676440223, },
+[1165] = { city_tunis, 2020, MONTH_jun, 4.1296716911005574e-4, },
+[1166] = { city_tunis, 2020, MONTH_jul, 0, },
+[1167] = { city_tunis, 2020, MONTH_aug, 0.005575056782985753, },
+[1168] = { city_tunis, 2020, MONTH_sep, 0.03881891389634524, },
+[1169] = { city_tunis, 2020, MONTH_oct, 0.21722073095188932, },
+[1170] = { city_tunis, 2020, MONTH_nov, 0.39273177782366303, },
+[1171] = { city_tunis, 2020, MONTH_dec, 0.2892835019615941, },
+[1172] = { city_tunis, 2021, MONTH_jan, 0.4253561841833574, },
+[1173] = { city_tunis, 2021, MONTH_feb, 0.26718975841420606, },
+[1174] = { city_tunis, 2021, MONTH_mar, 0.1680776378277927, },
+[1175] = { city_tunis, 2021, MONTH_apr, 0.38261408218046666, },
+[1176] = { city_tunis, 2021, MONTH_may, 0.40925046458806524, },
+[1177] = { city_tunis, 2021, MONTH_jun, 0.4583935577121619, },
+[1178] = { city_tunis, 2021, MONTH_jul, 1, },
+[1179] = { city_tunis, 2021, MONTH_aug, 0.7774106958496799, },
+[1180] = { city_tunis, 2021, MONTH_sep, 0.2925872393144745, },
+[1181] = { city_tunis, 2021, MONTH_oct, 0.07639892628536031, },
+[1182] = { city_tunis, 2021, MONTH_nov, 0.02622341523848854, },
+[1183] = { city_tunis, 2021, MONTH_dec, 0.041090233326450544, },
+[1184] = { city_tunis, 2022, MONTH_jan, 0.1459838942804047, },
+[1185] = { city_tunis, 2022, MONTH_feb, 0.31013834400165186, },
+[1186] = { city_tunis, 2022, MONTH_mar, 0.11356597150526533, },
+[1187] = { city_tunis, 2022, MONTH_apr, 0.04687177369399133, },
+[1188] = { city_tunis, 2022, MONTH_may, 0.016931653933512286, },
+[1189] = { city_ulaanbaatar, 2020, MONTH_jan, 0, },
+[1190] = { city_ulaanbaatar, 2020, MONTH_feb, 0, },
+[1191] = { city_ulaanbaatar, 2020, MONTH_mar, 0, },
+[1192] = { city_ulaanbaatar, 2020, MONTH_apr, 0, },
+[1193] = { city_ulaanbaatar, 2020, MONTH_may, 0, },
+[1194] = { city_ulaanbaatar, 2020, MONTH_jun, 0, },
+[1195] = { city_ulaanbaatar, 2020, MONTH_jul, 0, },
+[1196] = { city_ulaanbaatar, 2020, MONTH_aug, 0, },
+[1197] = { city_ulaanbaatar, 2020, MONTH_sep, 0, },
+[1198] = { city_ulaanbaatar, 2020, MONTH_oct, 0, },
+[1199] = { city_ulaanbaatar, 2020, MONTH_nov, 0, },
+[1200] = { city_ulaanbaatar, 2020, MONTH_dec, 0.002188183807439825, },
+[1201] = { city_ulaanbaatar, 2021, MONTH_jan, 0.002188183807439825, },
+[1202] = { city_ulaanbaatar, 2021, MONTH_feb, 0, },
+[1203] = { city_ulaanbaatar, 2021, MONTH_mar, 0.01312910284463895, },
+[1204] = { city_ulaanbaatar, 2021, MONTH_apr, 0.24070021881838075, },
+[1205] = { city_ulaanbaatar, 2021, MONTH_may, 0.29540481400437635, },
+[1206] = { city_ulaanbaatar, 2021, MONTH_jun, 0.5317286652078774, },
+[1207] = { city_ulaanbaatar, 2021, MONTH_jul, 0.474835886214442, },
+[1208] = { city_ulaanbaatar, 2021, MONTH_aug, 0.26914660831509846, },
+[1209] = { city_ulaanbaatar, 2021, MONTH_sep, 0.8052516411378556, },
+[1210] = { city_ulaanbaatar, 2021, MONTH_oct, 1, },
+[1211] = { city_ulaanbaatar, 2021, MONTH_nov, 0.5886214442013129, },
+[1212] = { city_ulaanbaatar, 2021, MONTH_dec, 0.12253829321663019, },
+[1213] = { city_ulaanbaatar, 2022, MONTH_jan, 0.10940919037199125, },
+[1214] = { city_ulaanbaatar, 2022, MONTH_feb, 0.13129102844638948, },
+[1215] = { city_ulaanbaatar, 2022, MONTH_mar, 0.0262582056892779, },
+[1216] = { city_ulaanbaatar, 2022, MONTH_apr, 0.00437636761487965, },
+[1217] = { city_ulaanbaatar, 2022, MONTH_may, 0.010940919037199124, },
+[1218] = { city_yerevan, 2020, MONTH_jan, 0, },
+[1219] = { city_yerevan, 2020, MONTH_feb, 0, },
+[1220] = { city_yerevan, 2020, MONTH_mar, 0.002421307506053269, },
+[1221] = { city_yerevan, 2020, MONTH_apr, 0.023405972558514933, },
+[1222] = { city_yerevan, 2020, MONTH_may, 0.07990314769975787, },
+[1223] = { city_yerevan, 2020, MONTH_jun, 0.25181598062953997, },
+[1224] = { city_yerevan, 2020, MONTH_jul, 0.23809523809523808, },
+[1225] = { city_yerevan, 2020, MONTH_aug, 0.11380145278450363, },
+[1226] = { city_yerevan, 2020, MONTH_sep, 0.0645682001614205, },
+[1227] = { city_yerevan, 2020, MONTH_oct, 0.3083131557707829, },
+[1228] = { city_yerevan, 2020, MONTH_nov, 0.6642453591606134, },
+[1229] = { city_yerevan, 2020, MONTH_dec, 0.5318805488297014, },
+[1230] = { city_yerevan, 2021, MONTH_jan, 0.20742534301856336, },
+[1231] = { city_yerevan, 2021, MONTH_feb, 0.0903954802259887, },
+[1232] = { city_yerevan, 2021, MONTH_mar, 0.26069410815173527, },
+[1233] = { city_yerevan, 2021, MONTH_apr, 0.47861178369652946, },
+[1234] = { city_yerevan, 2021, MONTH_may, 0.26634382566585957, },
+[1235] = { city_yerevan, 2021, MONTH_jun, 0.06133979015334948, },
+[1236] = { city_yerevan, 2021, MONTH_jul, 0.08071025020177562, },
+[1237] = { city_yerevan, 2021, MONTH_aug, 0.18563357546408393, },
+[1238] = { city_yerevan, 2021, MONTH_sep, 0.3833736884584342, },
+[1239] = { city_yerevan, 2021, MONTH_oct, 0.814366424535916, },
+[1240] = { city_yerevan, 2021, MONTH_nov, 1, },
+[1241] = { city_yerevan, 2021, MONTH_dec, 0.3268765133171913, },
+[1242] = { city_yerevan, 2022, MONTH_jan, 0.06537530266343826, },
+[1243] = { city_yerevan, 2022, MONTH_feb, 0.32929782082324455, },
+[1244] = { city_yerevan, 2022, MONTH_mar, 0.12267958030669895, },
+[1245] = { city_yerevan, 2022, MONTH_apr, 0.007263922518159807, },
+[1246] = { city_yerevan, 2022, MONTH_may, 0.002421307506053269, },
+[1247] = { city_chicago, 2020, MONTH_jan, 0, },
+[1248] = { city_chicago, 2020, MONTH_feb, 0, },
+[1249] = { city_chicago, 2020, MONTH_mar, 0.021555197421434327, },
+[1250] = { city_chicago, 2020, MONTH_apr, 0.4540692989524577, },
+[1251] = { city_chicago, 2020, MONTH_may, 0.6174456083803385, },
+[1252] = { city_chicago, 2020, MONTH_jun, 0.3444802578565673, },
+[1253] = { city_chicago, 2020, MONTH_jul, 0.11422240128928283, },
+[1254] = { city_chicago, 2020, MONTH_aug, 0.11180499597099114, },
+[1255] = { city_chicago, 2020, MONTH_sep, 0.13638195004029008, },
+[1256] = { city_chicago, 2020, MONTH_oct, 0.22058823529411764, },
+[1257] = { city_chicago, 2020, MONTH_nov, 0.6013295729250604, },
+[1258] = { city_chicago, 2020, MONTH_dec, 1, },
+[1259] = { city_chicago, 2021, MONTH_jan, 0.6573327961321515, },
+[1260] = { city_chicago, 2021, MONTH_feb, 0.3007655116841257, },
+[1261] = { city_chicago, 2021, MONTH_mar, 0.17002417405318293, },
+[1262] = { city_chicago, 2021, MONTH_apr, 0.1434327155519742, },
+[1263] = { city_chicago, 2021, MONTH_may, 0.18775181305398872, },
+[1264] = { city_chicago, 2021, MONTH_jun, 0.09085414987912974, },
+[1265] = { city_chicago, 2021, MONTH_jul, 0.048751007252215955, },
+[1266] = { city_chicago, 2021, MONTH_aug, 0.11764705882352941, },
+[1267] = { city_chicago, 2021, MONTH_sep, 0.2346897663174859, },
+[1268] = { city_chicago, 2021, MONTH_oct, 0.19238517324738114, },
+[1269] = { city_chicago, 2021, MONTH_nov, 0.1522965350523771, },
+[1270] = { city_chicago, 2021, MONTH_dec, 0.33058017727639, },
+[1271] = { city_chicago, 2022, MONTH_jan, 0.7427477840451249, },
+[1272] = { city_chicago, 2022, MONTH_feb, 0.4443996776792909, },
+[1273] = { city_chicago, 2022, MONTH_mar, 0.1524979854955681, },
+[1274] = { city_chicago, 2022, MONTH_apr, 0.04653505237711523, },
+[1275] = { city_chicago, 2022, MONTH_may, 0.021555197421434327, },
+[1276] = { city_denver, 2020, MONTH_jan, 0, },
+[1277] = { city_denver, 2020, MONTH_feb, 0, },
+[1278] = { city_denver, 2020, MONTH_mar, 0.03793293018141836, },
+[1279] = { city_denver, 2020, MONTH_apr, 0.3881253435953821, },
+[1280] = { city_denver, 2020, MONTH_may, 0.3683342495876855, },
+[1281] = { city_denver, 2020, MONTH_jun, 0.13468938977460143, },
+[1282] = { city_denver, 2020, MONTH_jul, 0.08301264431006047, },
+[1283] = { city_denver, 2020, MONTH_aug, 0.06102253985706432, },
+[1284] = { city_denver, 2020, MONTH_sep, 0.059923034634414514, },
+[1285] = { city_denver, 2020, MONTH_oct, 0.13633864760857614, },
+[1286] = { city_denver, 2020, MONTH_nov, 0.41286421110500277, },
+[1287] = { city_denver, 2020, MONTH_dec, 1, },
+[1288] = { city_denver, 2021, MONTH_jan, 0.45739417262231996, },
+[1289] = { city_denver, 2021, MONTH_feb, 0.18361737218251786, },
+[1290] = { city_denver, 2021, MONTH_mar, 0.07146783947223749, },
+[1291] = { city_denver, 2021, MONTH_apr, 0.14128642111050027, },
+[1292] = { city_denver, 2021, MONTH_may, 0.159978009895547, },
+[1293] = { city_denver, 2021, MONTH_jun, 0.12314458493677845, },
+[1294] = { city_denver, 2021, MONTH_jul, 0.08246289169873557, },
+[1295] = { city_denver, 2021, MONTH_aug, 0.12204507971412865, },
+[1296] = { city_denver, 2021, MONTH_sep, 0.22649807586586038, },
+[1297] = { city_denver, 2021, MONTH_oct, 0.35459043430456294, },
+[1298] = { city_denver, 2021, MONTH_nov, 0.5536008796041781, },
+[1299] = { city_denver, 2021, MONTH_dec, 0.6030786146234195, },
+[1300] = { city_denver, 2022, MONTH_jan, 0.4722374931280924, },
+[1301] = { city_denver, 2022, MONTH_feb, 0.3672347443650357, },
+[1302] = { city_denver, 2022, MONTH_mar, 0.09840571742715779, },
+[1303] = { city_denver, 2022, MONTH_apr, 0.09895547003848268, },
+[1304] = { city_denver, 2022, MONTH_may, 0.18526663001649257, },
+[1305] = { city_san_francisco, 2020, MONTH_jan, 0, },
+[1306] = { city_san_francisco, 2020, MONTH_feb, 0, },
+[1307] = { city_san_francisco, 2020, MONTH_mar, 0.012296999264853305, },
+[1308] = { city_san_francisco, 2020, MONTH_apr, 0.12517543273407739, },
+[1309] = { city_san_francisco, 2020, MONTH_may, 0.146026866270133, },
+[1310] = { city_san_francisco, 2020, MONTH_jun, 0.12303682416627681, },
+[1311] = { city_san_francisco, 2020, MONTH_jul, 0.20978413419768763, },
+[1312] = { city_san_francisco, 2020, MONTH_aug, 0.2538261043908307, },
+[1313] = { city_san_francisco, 2020, MONTH_sep, 0.19234110806656418, },
+[1314] = { city_san_francisco, 2020, MONTH_oct, 0.11782396578226291, },
+[1315] = { city_san_francisco, 2020, MONTH_nov, 0.1034551894673528, },
+[1316] = { city_san_francisco, 2020, MONTH_dec, 0.45151373387689636, },
+[1317] = { city_san_francisco, 2021, MONTH_jan, 1, },
+[1318] = { city_san_francisco, 2021, MONTH_feb, 0.7541936777384214, },
+[1319] = { city_san_francisco, 2021, MONTH_mar, 0.47096170554033284, },
+[1320] = { city_san_francisco, 2021, MONTH_apr, 0.16681146828844484, },
+[1321] = { city_san_francisco, 2021, MONTH_may, 0.09977945599144557, },
+[1322] = { city_san_francisco, 2021, MONTH_jun, 0.027601416828176167, },
+[1323] = { city_san_francisco, 2021, MONTH_jul, 0.05045779589654481, },
+[1324] = { city_san_francisco, 2021, MONTH_aug, 0.09663837465748847, },
+[1325] = { city_san_francisco, 2021, MONTH_sep, 0.22822963309496758, },
+[1326] = { city_san_francisco, 2021, MONTH_oct, 0.19153912985363897, },
+[1327] = { city_san_francisco, 2021, MONTH_nov, 0.16881641382075788, },
+[1328] = { city_san_francisco, 2021, MONTH_dec, 0.13626946467954287, },
+[1329] = { city_san_francisco, 2022, MONTH_jan, 0.23778654013232642, },
+[1330] = { city_san_francisco, 2022, MONTH_feb, 0.3540065494887389, },
+[1331] = { city_san_francisco, 2022, MONTH_mar, 0.23083606228697454, },
+[1332] = { city_san_francisco, 2022, MONTH_apr, 0.09363095635901891, },
+[1333] = { city_san_francisco, 2022, MONTH_may, 0.0360890195816347, },
+[1334] = { city_washington, 2020, MONTH_jan, 0, },
+[1335] = { city_washington, 2020, MONTH_feb, 0, },
+[1336] = { city_washington, 2020, MONTH_mar, 0.0371900826446281, },
+[1337] = { city_washington, 2020, MONTH_apr, 0.8884297520661157, },
+[1338] = { city_washington, 2020, MONTH_may, 1, },
+[1339] = { city_washington, 2020, MONTH_jun, 0.3512396694214876, },
+[1340] = { city_washington, 2020, MONTH_jul, 0.14049586776859505, },
+[1341] = { city_washington, 2020, MONTH_aug, 0.09090909090909091, },
+[1342] = { city_washington, 2020, MONTH_sep, 0.08264462809917356, },
+[1343] = { city_washington, 2020, MONTH_oct, 0.07851239669421488, },
+[1344] = { city_washington, 2020, MONTH_nov, 0.14049586776859505, },
+[1345] = { city_washington, 2020, MONTH_dec, 0.4380165289256198, },
+[1346] = { city_washington, 2021, MONTH_jan, 0.5247933884297521, },
+[1347] = { city_washington, 2021, MONTH_feb, 0.4297520661157025, },
+[1348] = { city_washington, 2021, MONTH_mar, 0.19421487603305784, },
+[1349] = { city_washington, 2021, MONTH_apr, 0.16942148760330578, },
+[1350] = { city_washington, 2021, MONTH_may, 0.1115702479338843, },
+[1351] = { city_washington, 2021, MONTH_jun, 0.0371900826446281, },
+[1352] = { city_washington, 2021, MONTH_jul, 0.03305785123966942, },
+[1353] = { city_washington, 2021, MONTH_aug, 0.045454545454545456, },
+[1354] = { city_washington, 2021, MONTH_sep, 0.05785123966942149, },
+[1355] = { city_washington, 2021, MONTH_oct, 0.06611570247933884, },
+[1356] = { city_washington, 2021, MONTH_nov, 0.024793388429752067, },
+[1357] = { city_washington, 2021, MONTH_dec, 0.06198347107438017, },
+[1358] = { city_washington, 2022, MONTH_jan, 0.32231404958677684, },
+[1359] = { city_washington, 2022, MONTH_feb, 0.11983471074380166, },
+[1360] = { city_washington, 2022, MONTH_mar, 0.05371900826446281, },
+[1361] = { city_washington, 2022, MONTH_apr, 0.0371900826446281, },
+[1362] = { city_washington, 2022, MONTH_may, 0, },
+};
+global u32 question_4_len = sizeof(question_4_data) / sizeof(question_4_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_5.h b/run_tree/data/incenter_data/c/question_5.h
new file mode 100644
index 0000000..995972e
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_5.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_5_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.601593625498008, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.6060606060606061, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.5254237288135594, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.6507936507936508, },
+[4] = { city_paris, 2022, MONTH_jan, 0.569806492883416, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.6120930232558139, },
+[6] = { city_denver, 2022, MONTH_jan, 0.6120930232558139, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.7948003714020427, },
+[8] = { city_harare, 2022, MONTH_jan, 0.5714285714285714, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.6158536585365854, },
+[10] = { city_washington, 2022, MONTH_jan, 0.6120930232558139, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.5555555555555556, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.5142857142857142, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.6158536585365854, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.6169354838709677, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.5819672131147541, },
+[16] = { city_lima, 2022, MONTH_jan, 0.425, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.6363636363636364, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.8333333333333334, },
+[19] = { city_managua, 2022, MONTH_jan, 0.6774193548387096, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.464638783269962, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.6190476190476191, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.5254237288135594, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.5176882661996497, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.5714285714285714, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.5993009868421053, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.5, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.5, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.6011644832605532, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.6774193548387096, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.6111111111111112, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.5714285714285714, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.5142857142857142, },
+[33] = { city_quito, 2022, MONTH_jan, 0.4827586206896552, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.45, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.5254237288135594, },
+[36] = { city_accra, 2022, MONTH_jan, 0.5714285714285714, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.6352941176470588, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.7062314540059347, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.7062314540059347, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.7107438016528925, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.7948003714020427, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.6120930232558139, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.59375, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.5, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.6507936507936508, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.4838709677419355, },
+};
+global u32 question_5_len = sizeof(question_5_data) / sizeof(question_5_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_6.h b/run_tree/data/incenter_data/c/question_6.h
new file mode 100644
index 0000000..ac29d6e
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_6.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_6_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.2964426877470356, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.3141891891891892, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.4745762711864407, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.4603174603174603, },
+[4] = { city_paris, 2022, MONTH_jan, 0.3169346452283817, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.3714551371455137, },
+[6] = { city_denver, 2022, MONTH_jan, 0.3714551371455137, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.5386046511627907, },
+[8] = { city_harare, 2022, MONTH_jan, 0.21428571428571427, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.4878048780487805, },
+[10] = { city_washington, 2022, MONTH_jan, 0.3714551371455137, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.5, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.4647887323943662, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.4878048780487805, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.3089430894308943, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.3442622950819672, },
+[16] = { city_lima, 2022, MONTH_jan, 0.225, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.3719298245614035, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.4117647058823529, },
+[19] = { city_managua, 2022, MONTH_jan, 0.375, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.2883156297420334, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.2926829268292683, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.4745762711864407, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.3334112422578006, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.21428571428571427, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.8559967084961942, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.3137254901960784, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.3137254901960784, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.36627906976744184, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.375, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.3335826477187734, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.21428571428571427, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.4647887323943662, },
+[33] = { city_quito, 2022, MONTH_jan, 0.27586206896551724, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.3111111111111111, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.4745762711864407, },
+[36] = { city_accra, 2022, MONTH_jan, 0.21428571428571427, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.3568075117370892, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.5705794947994056, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.5705794947994056, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.3159340659340659, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.5386046511627907, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.3714551371455137, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.3939393939393939, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.3137254901960784, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.4603174603174603, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.37209302325581395, },
+};
+global u32 question_6_len = sizeof(question_6_data) / sizeof(question_6_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_7.h b/run_tree/data/incenter_data/c/question_7.h
new file mode 100644
index 0000000..8458133
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_7.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_7_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.9065934065934066, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.8987854251012146, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.8, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.8666666666666667, },
+[4] = { city_paris, 2022, MONTH_jan, 0.8818422046055115, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.9293150684931507, },
+[6] = { city_denver, 2022, MONTH_jan, 0.9293150684931507, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.9027283511269276, },
+[8] = { city_harare, 2022, MONTH_jan, 1 , },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.9253731343283582, },
+[10] = { city_washington, 2022, MONTH_jan, 0.9293150684931507, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.7692307692307693, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.9056603773584906, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.9253731343283582, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.9090909090909091, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.7934782608695652, },
+[16] = { city_lima, 2022, MONTH_jan, 0.9705882352941176, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.8928571428571429, },
+[18] = { city_abuja, 2022, MONTH_jan, 1 , },
+[19] = { city_managua, 2022, MONTH_jan, 0.9047619047619048, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.924908424908425, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.9714285714285714, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.8, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.9220470098185064, },
+[24] = { city_nairobi, 2022, MONTH_jan, 1, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.5873192436040044, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.925, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.925, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.8819938962360122, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.9047619047619048, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.9141824751580849, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 1, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.9056603773584906, },
+[33] = { city_quito, 2022, MONTH_jan, 0.9565217391304348, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.8592592592592593, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.8, },
+[36] = { city_accra, 2022, MONTH_jan, 1, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.8787878787878788, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.9236641221374046, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.9236641221374046, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.91701244813278, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.9027283511269276, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.9293150684931507, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.9090909090909091, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.925, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.8666666666666667, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.8149882903981265, },
+};
+global u32 question_7_len = sizeof(question_7_data) / sizeof(question_7_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_8.h b/run_tree/data/incenter_data/c/question_8.h
new file mode 100644
index 0000000..c956c8e
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_8.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_8_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.9398907103825137, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.9061224489795918, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.8717948717948718, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 1, },
+[4] = { city_paris, 2022, MONTH_jan, 0.898988944533686, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.9071938495332235, },
+[6] = { city_denver, 2022, MONTH_jan, 0.9071938495332235, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.9175627240143369, },
+[8] = { city_harare, 2022, MONTH_jan, 1, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.8962962962962963, },
+[10] = { city_washington, 2022, MONTH_jan, 0.9071938495332235, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.9230769230769231, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.8846153846153846, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.8962962962962963, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.9179487179487179, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.8571428571428571, },
+[16] = { city_lima, 2022, MONTH_jan, 0.9411764705882353, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.8290155440414507, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.8571428571428571, },
+[19] = { city_managua, 2022, MONTH_jan, 0.9523809523809523, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.8682027649769585, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.9411764705882353, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.8717948717948718, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.8594192107222636, },
+[24] = { city_nairobi, 2022, MONTH_jan, 1, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.5911220165068035, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.975, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.975, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.9158215010141988, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.9523809523809523, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.9038112522686026, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 1, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.8846153846153846, },
+[33] = { city_quito, 2022, MONTH_jan, 1, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.8712121212121212, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.8717948717948718, },
+[36] = { city_accra, 2022, MONTH_jan, 1, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.8953168044077136, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.9504761904761905, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.9504761904761905, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.9159663865546218, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.9175627240143369, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.9071938495332235, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.9090909090909091, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.975, },
+[45] = { city_mumbai, 2022, MONTH_jan, 1, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.9479905437352246, },
+};
+global u32 question_8_len = sizeof(question_8_data) / sizeof(question_8_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/c/question_9.h b/run_tree/data/incenter_data/c/question_9.h
new file mode 100644
index 0000000..1333084
--- /dev/null
+++ b/run_tree/data/incenter_data/c/question_9.h
@@ -0,0 +1,50 @@
+static Incenter_Data_Row question_9_data[] = {
+[0] = { city_bucharest, 2022, MONTH_jan, 0.9276054097056484, },
+[1] = { city_brisbane, 2022, MONTH_jan, 0.9355913381454747, },
+[2] = { city_chengdu, 2022, MONTH_jan, 0.874793524942187, },
+[3] = { city_new_delhi, 2022, MONTH_jan, 0.947289156626506, },
+[4] = { city_paris, 2022, MONTH_jan, 0.9259986902423052, },
+[5] = { city_san_francisco, 2022, MONTH_jan, 0.9016393442622951, },
+[6] = { city_denver, 2022, MONTH_jan, 0.9326923076923077, },
+[7] = { city_ankara, 2022, MONTH_jan, 0.9283050145047659, },
+[8] = { city_harare, 2022, MONTH_jan, 0.966996699669967, },
+[9] = { city_hanoi, 2022, MONTH_jan, 0.9691666666666666, },
+[10] = { city_washington, 2022, MONTH_jan, 0.9068219633943427, },
+[11] = { city_bangkok, 2022, MONTH_jan, 0.9223040857334226, },
+[12] = { city_tunis, 2022, MONTH_jan, 0.9776490066225165, },
+[13] = { city_seoul, 2022, MONTH_jan, 0.9156626506024096, },
+[14] = { city_belgrade, 2022, MONTH_jan, 0.946360153256705, },
+[15] = { city_moscow, 2022, MONTH_jan, 0.8717379233759023, },
+[16] = { city_lima, 2022, MONTH_jan, 0.8871428571428571, },
+[17] = { city_islamabad, 2022, MONTH_jan, 0.947289156626506, },
+[18] = { city_abuja, 2022, MONTH_jan, 0.9919093851132686, },
+[19] = { city_managua, 2022, MONTH_jan, 0.84, },
+[20] = { city_amsterdam, 2022, MONTH_jan, 0.8231389284020862, },
+[21] = { city_rabat, 2022, MONTH_jan, 0.8991666666666667, },
+[22] = { city_ulaanbaatar, 2022, MONTH_jan, 0.8192918192918193, },
+[23] = { city_mexico_city, 2022, MONTH_jan, 0.9753015508328546, },
+[24] = { city_nairobi, 2022, MONTH_jan, 0.9818181818181818, },
+[25] = { city_tokyo, 2022, MONTH_jan, 0.9380134428678119, },
+[26] = { city_baghdad, 2022, MONTH_jan, 0.9808013355592654, },
+[27] = { city_tehran, 2022, MONTH_jan, 0.9425901201602136, },
+[28] = { city_jakarta, 2022, MONTH_jan, 0.98875, },
+[29] = { city_guatemala_city, 2022, MONTH_jan, 0.9072416598860863, },
+[30] = { city_berlin, 2022, MONTH_jan, 0.9259986902423052, },
+[31] = { city_addis_ababa, 2022, MONTH_jan, 0.9536585365853658, },
+[32] = { city_cairo, 2022, MONTH_jan, 0.9975, },
+[33] = { city_quito, 2022, MONTH_jan, 0.9073455759599333, },
+[34] = { city_bogota, 2022, MONTH_jan, 0.8782894736842105, },
+[35] = { city_beijing, 2022, MONTH_jan, 0.874793524942187, },
+[36] = { city_accra, 2022, MONTH_jan, 0.9818181818181818, },
+[37] = { city_ottawa, 2022, MONTH_jan, 0.8504230960676954, },
+[38] = { city_brasilia, 2022, MONTH_jan, 0.8677639046538025, },
+[39] = { city_la_paz, 2022, MONTH_jan, 0.8522286821705426, },
+[40] = { city_dhaka, 2022, MONTH_jan, 0.99, },
+[41] = { city_yerevan, 2022, MONTH_jan, 0.9705641864268193, },
+[42] = { city_chicago, 2022, MONTH_jan, 0.9385113268608414, },
+[43] = { city_kyiv, 2022, MONTH_jan, 0.8817120622568093, },
+[44] = { city_dubai, 2022, MONTH_jan, 0.9975, },
+[45] = { city_mumbai, 2022, MONTH_jan, 0.947289156626506, },
+[46] = { city_madrid, 2022, MONTH_jan, 0.9259986902423052, },
+};
+global u32 question_9_len = sizeof(question_9_data) / sizeof(question_9_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_data/csv/question_1.csv b/run_tree/data/incenter_data/csv/question_1.csv
new file mode 100644
index 0000000..22b6766
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_1.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.7211155378486056
+city_brisbane,2022,MONTH_jan,0.6813559322033899
+city_chengdu,2022,MONTH_jan,0.5084745762711864
+city_new_delhi,2022,MONTH_jan,0.6507936507936508
+city_paris,2022,MONTH_jan,0.5611355457816873
+city_san_francisco,2022,MONTH_jan,0.7130232558139535
+city_denver,2022,MONTH_jan,0.7130232558139535
+city_ankara,2022,MONTH_jan,0.8550185873605948
+city_harare,2022,MONTH_jan,0.5
+city_hanoi,2022,MONTH_jan,0.3597560975609756
+city_washington,2022,MONTH_jan,0.7130232558139535
+city_bangkok,2022,MONTH_jan,0.5555555555555556
+city_tunis,2022,MONTH_jan,0.5942028985507246
+city_seoul,2022,MONTH_jan,0.3597560975609756
+city_belgrade,2022,MONTH_jan,0.632
+city_moscow,2022,MONTH_jan,0.5081967213114754
+city_lima,2022,MONTH_jan,0.5
+city_islamabad,2022,MONTH_jan,0.6526315789473685
+city_abuja,2022,MONTH_jan,0.8888888888888888
+city_managua,2022,MONTH_jan,0.6774193548387096
+city_amsterdam,2022,MONTH_jan,0.6104783599088838
+city_rabat,2022,MONTH_jan,0.4523809523809524
+city_ulaanbaatar,2022,MONTH_jan,0.5084745762711864
+city_mexico_city,2022,MONTH_jan,0.5747718230751229
+city_nairobi,2022,MONTH_jan,0.5
+city_tokyo,2022,MONTH_jan,0.3949372298826919
+city_baghdad,2022,MONTH_jan,0.7
+city_tehran,2022,MONTH_jan,0.7
+city_jakarta,2022,MONTH_jan,0.45276162790697677
+city_guatemala_city,2022,MONTH_jan,0.6774193548387096
+city_berlin,2022,MONTH_jan,0.6714178544636159
+city_addis_ababa,2022,MONTH_jan,0.5
+city_cairo,2022,MONTH_jan,0.5942028985507246
+city_quito,2022,MONTH_jan,0.48148148148148145
+city_bogota,2022,MONTH_jan,0.5698324022346368
+city_beijing,2022,MONTH_jan,0.5084745762711864
+city_accra,2022,MONTH_jan,0.5
+city_ottawa,2022,MONTH_jan,0.7213114754098361
+city_brasilia,2022,MONTH_jan,0.7555555555555555
+city_la_paz,2022,MONTH_jan,0.7555555555555555
+city_dhaka,2022,MONTH_jan,0.7348066298342542
+city_yerevan,2022,MONTH_jan,0.8550185873605948
+city_chicago,2022,MONTH_jan,0.7130232558139535
+city_kyiv,2022,MONTH_jan,0.46875
+city_dubai,2022,MONTH_jan,0.7
+city_mumbai,2022,MONTH_jan,0.6507936507936508
+city_madrid,2022,MONTH_jan,0.5716845878136201
diff --git a/run_tree/data/incenter_data/csv/question_10.csv b/run_tree/data/incenter_data/csv/question_10.csv
new file mode 100644
index 0000000..716ff49
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_10.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.7907915993537964
+city_brisbane,2022,MONTH_jan,0.9196675900277008
+city_chengdu,2022,MONTH_jan,0.8922570016474465
+city_new_delhi,2022,MONTH_jan,0.8931259407927747
+city_paris,2022,MONTH_jan,0.8915187376725838
+city_san_francisco,2022,MONTH_jan,0.8767605633802817
+city_denver,2022,MONTH_jan,0.8822393822393823
+city_ankara,2022,MONTH_jan,0.8529289572081429
+city_harare,2022,MONTH_jan,0.5197693574958814
+city_hanoi,2022,MONTH_jan,0.9733333333333334
+city_washington,2022,MONTH_jan,0.885
+city_bangkok,2022,MONTH_jan,0.8386880856760375
+city_tunis,2022,MONTH_jan,0.7669983416252073
+city_seoul,2022,MONTH_jan,0.8907630522088353
+city_belgrade,2022,MONTH_jan,0.8032629558541267
+city_moscow,2022,MONTH_jan,0.8402501421262081
+city_lima,2022,MONTH_jan,0.8164874551971326
+city_islamabad,2022,MONTH_jan,0.8931259407927747
+city_abuja,2022,MONTH_jan,0.7283049472830495
+city_managua,2022,MONTH_jan,0.8558333333333333
+city_amsterdam,2022,MONTH_jan,0.9145383104125737
+city_rabat,2022,MONTH_jan,0.8283333333333334
+city_ulaanbaatar,2022,MONTH_jan,0.6910866910866911
+city_mexico_city,2022,MONTH_jan,0.9178632969557725
+city_nairobi,2022,MONTH_jan,0.8371907422186752
+city_tokyo,2022,MONTH_jan,0.9025679758308157
+city_baghdad,2022,MONTH_jan,0.7039864291772688
+city_tehran,2022,MONTH_jan,0.6967267869071476
+city_jakarta,2022,MONTH_jan,0.939043451078462
+city_guatemala_city,2022,MONTH_jan,0.9072416598860863
+city_berlin,2022,MONTH_jan,0.8915187376725838
+city_addis_ababa,2022,MONTH_jan,0.8747967479674796
+city_cairo,2022,MONTH_jan,0.7172643869891576
+city_quito,2022,MONTH_jan,0.8932203389830509
+city_bogota,2022,MONTH_jan,0.8940789473684211
+city_beijing,2022,MONTH_jan,0.8922570016474465
+city_accra,2022,MONTH_jan,0.8371907422186752
+city_ottawa,2022,MONTH_jan,0.8638626182180189
+city_brasilia,2022,MONTH_jan,0.9008595988538682
+city_la_paz,2022,MONTH_jan,0.7321688500727802
+city_dhaka,2022,MONTH_jan,0.9066666666666666
+city_yerevan,2022,MONTH_jan,0.8845836768342952
+city_chicago,2022,MONTH_jan,0.8559870550161812
+city_kyiv,2022,MONTH_jan,0.8059105431309904
+city_dubai,2022,MONTH_jan,0.7172643869891576
+city_mumbai,2022,MONTH_jan,0.8931259407927747
+city_madrid,2022,MONTH_jan,0.8915187376725838
diff --git a/run_tree/data/incenter_data/csv/question_11.csv b/run_tree/data/incenter_data/csv/question_11.csv
new file mode 100644
index 0000000..2781b08
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_11.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.7527472527472527
+city_brisbane,2022,MONTH_jan,0.7346938775510204
+city_chengdu,2022,MONTH_jan,0.75
+city_new_delhi,2022,MONTH_jan,0.8222222222222222
+city_paris,2022,MONTH_jan,0.5690460306871248
+city_san_francisco,2022,MONTH_jan,0.7624035281146637
+city_denver,2022,MONTH_jan,0.7624035281146637
+city_ankara,2022,MONTH_jan,0.6437054631828979
+city_harare,2022,MONTH_jan,0.8
+city_hanoi,2022,MONTH_jan,0.8148148148148148
+city_washington,2022,MONTH_jan,0.7624035281146637
+city_bangkok,2022,MONTH_jan,0.5384615384615384
+city_tunis,2022,MONTH_jan,0.8431372549019608
+city_seoul,2022,MONTH_jan,0.8148148148148148
+city_belgrade,2022,MONTH_jan,0.6818181818181818
+city_moscow,2022,MONTH_jan,0.4065934065934066
+city_lima,2022,MONTH_jan,0.8235294117647058
+city_islamabad,2022,MONTH_jan,0.8105263157894737
+city_abuja,2022,MONTH_jan,0.7142857142857143
+city_managua,2022,MONTH_jan,0.8571428571428571
+city_amsterdam,2022,MONTH_jan,0.7003676470588235
+city_rabat,2022,MONTH_jan,0.7058823529411765
+city_ulaanbaatar,2022,MONTH_jan,0.75
+city_mexico_city,2022,MONTH_jan,0.7737030411449016
+city_nairobi,2022,MONTH_jan,0.8
+city_tokyo,2022,MONTH_jan,0.4397765363128492
+city_baghdad,2022,MONTH_jan,0.7
+city_tehran,2022,MONTH_jan,0.7
+city_jakarta,2022,MONTH_jan,0.8859470468431772
+city_guatemala_city,2022,MONTH_jan,0.8571428571428571
+city_berlin,2022,MONTH_jan,0.6584922797456857
+city_addis_ababa,2022,MONTH_jan,0.8
+city_cairo,2022,MONTH_jan,0.8431372549019608
+city_quito,2022,MONTH_jan,0.8181818181818182
+city_bogota,2022,MONTH_jan,0.7089552238805971
+city_beijing,2022,MONTH_jan,0.75
+city_accra,2022,MONTH_jan,0.8
+city_ottawa,2022,MONTH_jan,0.6509695290858726
+city_brasilia,2022,MONTH_jan,0.8969465648854962
+city_la_paz,2022,MONTH_jan,0.8969465648854962
+city_dhaka,2022,MONTH_jan,0.8523206751054853
+city_yerevan,2022,MONTH_jan,0.6437054631828979
+city_chicago,2022,MONTH_jan,0.7624035281146637
+city_kyiv,2022,MONTH_jan,0.8181818181818182
+city_dubai,2022,MONTH_jan,0.7
+city_mumbai,2022,MONTH_jan,0.8222222222222222
+city_madrid,2022,MONTH_jan,0.7440191387559809
diff --git a/run_tree/data/incenter_data/csv/question_12.csv b/run_tree/data/incenter_data/csv/question_12.csv
new file mode 100644
index 0000000..4a537d1
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_12.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.4789180588703262
+city_brisbane,2022,MONTH_jan,0.827357970215113
+city_chengdu,2022,MONTH_jan,0.600926538716082
+city_new_delhi,2022,MONTH_jan,0.42355889724310775
+city_paris,2022,MONTH_jan,0.8413114754098361
+city_san_francisco,2022,MONTH_jan,0.6807511737089202
+city_denver,2022,MONTH_jan,0.6730769230769231
+city_ankara,2022,MONTH_jan,0.6662309368191721
+city_harare,2022,MONTH_jan,0.7267489711934156
+city_hanoi,2022,MONTH_jan,0.4633333333333333
+city_washington,2022,MONTH_jan,0.7408637873754153
+city_bangkok,2022,MONTH_jan,0.522
+city_tunis,2022,MONTH_jan,0.8239731768650461
+city_seoul,2022,MONTH_jan,0.5084337349397591
+city_belgrade,2022,MONTH_jan,0.6408518877057116
+city_moscow,2022,MONTH_jan,0.5629834254143646
+city_lima,2022,MONTH_jan,0.6192857142857143
+city_islamabad,2022,MONTH_jan,0.42355889724310775
+city_abuja,2022,MONTH_jan,0.6176232821341956
+city_managua,2022,MONTH_jan,0.7441666666666666
+city_amsterdam,2022,MONTH_jan,0.8019323671497585
+city_rabat,2022,MONTH_jan,0.5075
+city_ulaanbaatar,2022,MONTH_jan,0.4340659340659341
+city_mexico_city,2022,MONTH_jan,0.6657093624353819
+city_nairobi,2022,MONTH_jan,0.5442338072669827
+city_tokyo,2022,MONTH_jan,0.6260162601626016
+city_baghdad,2022,MONTH_jan,0.7216666666666667
+city_tehran,2022,MONTH_jan,0.40493662441627754
+city_jakarta,2022,MONTH_jan,0.470625
+city_guatemala_city,2022,MONTH_jan,0.7831558567279767
+city_berlin,2022,MONTH_jan,0.8413114754098361
+city_addis_ababa,2022,MONTH_jan,0.45934959349593496
+city_cairo,2022,MONTH_jan,0.7814845704753962
+city_quito,2022,MONTH_jan,0.6958333333333333
+city_bogota,2022,MONTH_jan,0.7730263157894737
+city_beijing,2022,MONTH_jan,0.600926538716082
+city_accra,2022,MONTH_jan,0.5442338072669827
+city_ottawa,2022,MONTH_jan,0.7073170731707317
+city_brasilia,2022,MONTH_jan,0.6152099886492622
+city_la_paz,2022,MONTH_jan,0.7289946576007771
+city_dhaka,2022,MONTH_jan,0.7075
+city_yerevan,2022,MONTH_jan,0.6059113300492611
+city_chicago,2022,MONTH_jan,0.6844660194174758
+city_kyiv,2022,MONTH_jan,0.4786656322730799
+city_dubai,2022,MONTH_jan,0.7814845704753962
+city_mumbai,2022,MONTH_jan,0.42355889724310775
+city_madrid,2022,MONTH_jan,0.8413114754098361
diff --git a/run_tree/data/incenter_data/csv/question_13.csv b/run_tree/data/incenter_data/csv/question_13.csv
new file mode 100644
index 0000000..25094a4
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_13.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.08285385500575373
+city_brisbane,2022,MONTH_jan,0.09841628959276018
+city_chengdu,2022,MONTH_jan,0.08166666666666667
+city_new_delhi,2022,MONTH_jan,0.20042987641053198
+city_paris,2022,MONTH_jan,0.031207598371777476
+city_san_francisco,2022,MONTH_jan,0.1670616113744076
+city_denver,2022,MONTH_jan,0.15145631067961166
+city_ankara,2022,MONTH_jan,0.14479830148619957
+city_harare,2022,MONTH_jan,0.3549459684123026
+city_hanoi,2022,MONTH_jan,0.08166666666666667
+city_washington,2022,MONTH_jan,0.14214046822742474
+city_bangkok,2022,MONTH_jan,0.18401611820013433
+city_tunis,2022,MONTH_jan,0.28439597315436244
+city_seoul,2022,MONTH_jan,0.157429718875502
+city_belgrade,2022,MONTH_jan,0.0691333982473223
+city_moscow,2022,MONTH_jan,0.07294264339152119
+city_lima,2022,MONTH_jan,0.18324607329842932
+city_islamabad,2022,MONTH_jan,0.20042987641053198
+city_abuja,2022,MONTH_jan,0.18892508143322476
+city_managua,2022,MONTH_jan,0.11083333333333334
+city_amsterdam,2022,MONTH_jan,0.09620098039215687
+city_rabat,2022,MONTH_jan,0.5508333333333333
+city_ulaanbaatar,2022,MONTH_jan,0.2658924205378973
+city_mexico_city,2022,MONTH_jan,0.3469387755102041
+city_nairobi,2022,MONTH_jan,0.37571312143439284
+city_tokyo,2022,MONTH_jan,0.0972644376899696
+city_baghdad,2022,MONTH_jan,0.16893039049235994
+city_tehran,2022,MONTH_jan,0.07811447811447811
+city_jakarta,2022,MONTH_jan,0.0608640807316304
+city_guatemala_city,2022,MONTH_jan,0.322213181448332
+city_berlin,2022,MONTH_jan,0.031207598371777476
+city_addis_ababa,2022,MONTH_jan,0.15637860082304528
+city_cairo,2022,MONTH_jan,0.16893039049235994
+city_quito,2022,MONTH_jan,0.14830875975715524
+city_bogota,2022,MONTH_jan,0.13355263157894737
+city_beijing,2022,MONTH_jan,0.08166666666666667
+city_accra,2022,MONTH_jan,0.37571312143439284
+city_ottawa,2022,MONTH_jan,0.14758586361373818
+city_brasilia,2022,MONTH_jan,0.20054384772263767
+city_la_paz,2022,MONTH_jan,0.14959016393442623
+city_dhaka,2022,MONTH_jan,0.13162544169611307
+city_yerevan,2022,MONTH_jan,0.03935599284436494
+city_chicago,2022,MONTH_jan,0.1563517915309446
+city_kyiv,2022,MONTH_jan,0.055281342546890426
+city_dubai,2022,MONTH_jan,0.16893039049235994
+city_mumbai,2022,MONTH_jan,0.20042987641053198
+city_madrid,2022,MONTH_jan,0.031207598371777476
diff --git a/run_tree/data/incenter_data/csv/question_14.csv b/run_tree/data/incenter_data/csv/question_14.csv
new file mode 100644
index 0000000..c5478e7
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_14.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.6968174204355109
+city_brisbane,2022,MONTH_jan,0.4254484304932735
+city_chengdu,2022,MONTH_jan,0.15694768410529825
+city_new_delhi,2022,MONTH_jan,0.37493632195618953
+city_paris,2022,MONTH_jan,0.31785003317850036
+city_san_francisco,2022,MONTH_jan,0.6515513126491647
+city_denver,2022,MONTH_jan,0.700587084148728
+city_ankara,2022,MONTH_jan,0.39265898420828
+city_harare,2022,MONTH_jan,0.76814011676397
+city_hanoi,2022,MONTH_jan,0.145
+city_washington,2022,MONTH_jan,0.6761744966442953
+city_bangkok,2022,MONTH_jan,0.529689608636977
+city_tunis,2022,MONTH_jan,0.7771236333052985
+city_seoul,2022,MONTH_jan,0.09799196787148594
+city_belgrade,2022,MONTH_jan,0.6625891946992865
+city_moscow,2022,MONTH_jan,0.5068903535050928
+city_lima,2022,MONTH_jan,0.7326086956521739
+city_islamabad,2022,MONTH_jan,0.37493632195618953
+city_abuja,2022,MONTH_jan,0.6726238830219334
+city_managua,2022,MONTH_jan,0.6166666666666667
+city_amsterdam,2022,MONTH_jan,0.37310308738880166
+city_rabat,2022,MONTH_jan,0.5525
+city_ulaanbaatar,2022,MONTH_jan,0.43933823529411764
+city_mexico_city,2022,MONTH_jan,0.6502890173410405
+city_nairobi,2022,MONTH_jan,0.5854632587859425
+city_tokyo,2022,MONTH_jan,0.4491803278688525
+city_baghdad,2022,MONTH_jan,0.74
+city_tehran,2022,MONTH_jan,0.48944860449285227
+city_jakarta,2022,MONTH_jan,0.3997445721583653
+city_guatemala_city,2022,MONTH_jan,0.7050538525269263
+city_berlin,2022,MONTH_jan,0.31785003317850036
+city_addis_ababa,2022,MONTH_jan,0.6325350949628407
+city_cairo,2022,MONTH_jan,0.74
+city_quito,2022,MONTH_jan,0.5373635600335852
+city_bogota,2022,MONTH_jan,0.7315789473684211
+city_beijing,2022,MONTH_jan,0.15694768410529825
+city_accra,2022,MONTH_jan,0.5854632587859425
+city_ottawa,2022,MONTH_jan,0.2929318068690891
+city_brasilia,2022,MONTH_jan,0.8714953271028038
+city_la_paz,2022,MONTH_jan,0.48205383848454636
+city_dhaka,2022,MONTH_jan,0.215
+city_yerevan,2022,MONTH_jan,0.7328308207705193
+city_chicago,2022,MONTH_jan,0.6830870279146142
+city_kyiv,2022,MONTH_jan,0.7300813008130081
+city_dubai,2022,MONTH_jan,0.74
+city_mumbai,2022,MONTH_jan,0.37493632195618953
+city_madrid,2022,MONTH_jan,0.31785003317850036
diff --git a/run_tree/data/incenter_data/csv/question_15.csv b/run_tree/data/incenter_data/csv/question_15.csv
new file mode 100644
index 0000000..f690ecf
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_15.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.8681318681318682
+city_brisbane,2022,MONTH_jan,0.842741935483871
+city_chengdu,2022,MONTH_jan,0.75
+city_new_delhi,2022,MONTH_jan,0.8444444444444444
+city_paris,2022,MONTH_jan,0.7975257342525262
+city_san_francisco,2022,MONTH_jan,0.8898026315789473
+city_denver,2022,MONTH_jan,0.8898026315789473
+city_ankara,2022,MONTH_jan,0.8466111771700356
+city_harare,2022,MONTH_jan,1
+city_hanoi,2022,MONTH_jan,0.8507462686567164
+city_washington,2022,MONTH_jan,0.8898026315789473
+city_bangkok,2022,MONTH_jan,0.7692307692307693
+city_tunis,2022,MONTH_jan,0.8653846153846154
+city_seoul,2022,MONTH_jan,0.8507462686567164
+city_belgrade,2022,MONTH_jan,0.8838383838383839
+city_moscow,2022,MONTH_jan,0.7934782608695652
+city_lima,2022,MONTH_jan,0.9705882352941176
+city_islamabad,2022,MONTH_jan,0.8121827411167513
+city_abuja,2022,MONTH_jan,0.8571428571428571
+city_managua,2022,MONTH_jan,1
+city_amsterdam,2022,MONTH_jan,0.9004566210045662
+city_rabat,2022,MONTH_jan,0.7352941176470589
+city_ulaanbaatar,2022,MONTH_jan,0.75
+city_mexico_city,2022,MONTH_jan,0.8772086117297698
+city_nairobi,2022,MONTH_jan,1
+city_tokyo,2022,MONTH_jan,0.41568278012920473
+city_baghdad,2022,MONTH_jan,0.8
+city_tehran,2022,MONTH_jan,0.8
+city_jakarta,2022,MONTH_jan,0.8562753036437247
+city_guatemala_city,2022,MONTH_jan,1
+city_berlin,2022,MONTH_jan,0.8590785907859079
+city_addis_ababa,2022,MONTH_jan,1
+city_cairo,2022,MONTH_jan,0.8653846153846154
+city_quito,2022,MONTH_jan,0.8695652173913043
+city_bogota,2022,MONTH_jan,0.8656716417910447
+city_beijing,2022,MONTH_jan,0.75
+city_accra,2022,MONTH_jan,1
+city_ottawa,2022,MONTH_jan,0.8402203856749312
+city_brasilia,2022,MONTH_jan,0.8950381679389313
+city_la_paz,2022,MONTH_jan,0.8950381679389313
+city_dhaka,2022,MONTH_jan,0.9041666666666667
+city_yerevan,2022,MONTH_jan,0.8466111771700356
+city_chicago,2022,MONTH_jan,0.8898026315789473
+city_kyiv,2022,MONTH_jan,0.8636363636363636
+city_dubai,2022,MONTH_jan,0.8
+city_mumbai,2022,MONTH_jan,0.8444444444444444
+city_madrid,2022,MONTH_jan,0.8524590163934426
diff --git a/run_tree/data/incenter_data/csv/question_16.csv b/run_tree/data/incenter_data/csv/question_16.csv
new file mode 100644
index 0000000..8964d0e
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_16.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.6252144082332761
+city_brisbane,2022,MONTH_jan,0.21300448430493274
+city_chengdu,2022,MONTH_jan,0.8102633355840648
+city_new_delhi,2022,MONTH_jan,0.7666836474783495
+city_paris,2022,MONTH_jan,0.1596806387225549
+city_san_francisco,2022,MONTH_jan,0.23954372623574144
+city_denver,2022,MONTH_jan,0.21940928270042195
+city_ankara,2022,MONTH_jan,0.8744531933508312
+city_harare,2022,MONTH_jan,0.9385004212299916
+city_hanoi,2022,MONTH_jan,0.8875
+city_washington,2022,MONTH_jan,0.2643884892086331
+city_bangkok,2022,MONTH_jan,0.6720647773279352
+city_tunis,2022,MONTH_jan,0.8076580587711487
+city_seoul,2022,MONTH_jan,0.8481927710843373
+city_belgrade,2022,MONTH_jan,0.763023493360572
+city_moscow,2022,MONTH_jan,0.738166566806471
+city_lima,2022,MONTH_jan,0.44288872512896094
+city_islamabad,2022,MONTH_jan,0.7666836474783495
+city_abuja,2022,MONTH_jan,0.9618196588139724
+city_managua,2022,MONTH_jan,0.4816666666666667
+city_amsterdam,2022,MONTH_jan,0.27867095391211144
+city_rabat,2022,MONTH_jan,0.91
+city_ulaanbaatar,2022,MONTH_jan,0.7175245098039216
+city_mexico_city,2022,MONTH_jan,0.3929398148148148
+city_nairobi,2022,MONTH_jan,0.8004926108374384
+city_tokyo,2022,MONTH_jan,0.5046491969568893
+city_baghdad,2022,MONTH_jan,0.9116666666666666
+city_tehran,2022,MONTH_jan,1
+city_jakarta,2022,MONTH_jan,0.9195402298850575
+city_guatemala_city,2022,MONTH_jan,0.6255178127589064
+city_berlin,2022,MONTH_jan,0.1596806387225549
+city_addis_ababa,2022,MONTH_jan,0.8911055694098088
+city_cairo,2022,MONTH_jan,0.9116666666666666
+city_quito,2022,MONTH_jan,0.4869857262804366
+city_bogota,2022,MONTH_jan,0.4
+city_beijing,2022,MONTH_jan,0.8102633355840648
+city_accra,2022,MONTH_jan,0.8004926108374384
+city_ottawa,2022,MONTH_jan,0.20034843205574912
+city_brasilia,2022,MONTH_jan,0.1588785046728972
+city_la_paz,2022,MONTH_jan,0.43232438606510565
+city_dhaka,2022,MONTH_jan,0.9798826487845766
+city_yerevan,2022,MONTH_jan,0.8872881355932203
+city_chicago,2022,MONTH_jan,0.2586805555555556
+city_kyiv,2022,MONTH_jan,0.7552447552447552
+city_dubai,2022,MONTH_jan,0.9116666666666666
+city_mumbai,2022,MONTH_jan,0.7666836474783495
+city_madrid,2022,MONTH_jan,0.1596806387225549
diff --git a/run_tree/data/incenter_data/csv/question_17.csv b/run_tree/data/incenter_data/csv/question_17.csv
new file mode 100644
index 0000000..b6a7a65
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_17.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.3517305893358279
+city_brisbane,2022,MONTH_jan,0.7251693002257337
+city_chengdu,2022,MONTH_jan,0.6752336448598131
+city_new_delhi,2022,MONTH_jan,0.28718882817243474
+city_paris,2022,MONTH_jan,0.8590694538098449
+city_san_francisco,2022,MONTH_jan,0.7202881152460985
+city_denver,2022,MONTH_jan,0.7637795275590551
+city_ankara,2022,MONTH_jan,0.22046174739701221
+city_harare,2022,MONTH_jan,0.4797297297297297
+city_hanoi,2022,MONTH_jan,0.08583333333333333
+city_washington,2022,MONTH_jan,0.7687074829931972
+city_bangkok,2022,MONTH_jan,0.39174560216508797
+city_tunis,2022,MONTH_jan,0.18357933579335795
+city_seoul,2022,MONTH_jan,0.242570281124498
+city_belgrade,2022,MONTH_jan,0.23599137931034483
+city_moscow,2022,MONTH_jan,0.3582286847323199
+city_lima,2022,MONTH_jan,0.21016166281755197
+city_islamabad,2022,MONTH_jan,0.28718882817243474
+city_abuja,2022,MONTH_jan,0.4920502092050209
+city_managua,2022,MONTH_jan,0.3875
+city_amsterdam,2022,MONTH_jan,0.5708571428571428
+city_rabat,2022,MONTH_jan,0.28583333333333333
+city_ulaanbaatar,2022,MONTH_jan,0.5067401960784313
+city_mexico_city,2022,MONTH_jan,0.1445221445221445
+city_nairobi,2022,MONTH_jan,0.5110356536502547
+city_tokyo,2022,MONTH_jan,0.6290018832391714
+city_baghdad,2022,MONTH_jan,0.475736568457539
+city_tehran,2022,MONTH_jan,0.7332421340629275
+city_jakarta,2022,MONTH_jan,0.3570284982388729
+city_guatemala_city,2022,MONTH_jan,0.5788135593220339
+city_berlin,2022,MONTH_jan,0.8590694538098449
+city_addis_ababa,2022,MONTH_jan,0.17217981340118746
+city_cairo,2022,MONTH_jan,0.475736568457539
+city_quito,2022,MONTH_jan,0.4667235494880546
+city_bogota,2022,MONTH_jan,0.6217105263157895
+city_beijing,2022,MONTH_jan,0.6752336448598131
+city_accra,2022,MONTH_jan,0.5110356536502547
+city_ottawa,2022,MONTH_jan,0.7775012444001991
+city_brasilia,2022,MONTH_jan,0.5541561712846348
+city_la_paz,2022,MONTH_jan,0.5235414534288638
+city_dhaka,2022,MONTH_jan,0.34421364985163205
+city_yerevan,2022,MONTH_jan,0.365832614322692
+city_chicago,2022,MONTH_jan,0.760797342192691
+city_kyiv,2022,MONTH_jan,0.28751311647429173
+city_dubai,2022,MONTH_jan,0.475736568457539
+city_mumbai,2022,MONTH_jan,0.28718882817243474
+city_madrid,2022,MONTH_jan,0.8590694538098449
diff --git a/run_tree/data/incenter_data/csv/question_18.csv b/run_tree/data/incenter_data/csv/question_18.csv
new file mode 100644
index 0000000..952767e
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_18.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.40350877192982454
+city_brisbane,2022,MONTH_jan,0.6732617297908423
+city_chengdu,2022,MONTH_jan,0.6871880199667221
+city_new_delhi,2022,MONTH_jan,0.4288
+city_paris,2022,MONTH_jan,0.6646090534979424
+city_san_francisco,2022,MONTH_jan,0.5142857142857142
+city_denver,2022,MONTH_jan,0.528957528957529
+city_ankara,2022,MONTH_jan,0.5759139784946237
+city_harare,2022,MONTH_jan,0.538971807628524
+city_hanoi,2022,MONTH_jan,0.7172643869891576
+city_washington,2022,MONTH_jan,0.5456081081081081
+city_bangkok,2022,MONTH_jan,0.5256756756756756
+city_tunis,2022,MONTH_jan,0.34995625546806647
+city_seoul,2022,MONTH_jan,0.5742971887550201
+city_belgrade,2022,MONTH_jan,0.46930280957336107
+city_moscow,2022,MONTH_jan,0.48936170212765956
+city_lima,2022,MONTH_jan,0.5945144551519644
+city_islamabad,2022,MONTH_jan,0.4288
+city_abuja,2022,MONTH_jan,0.41254125412541254
+city_managua,2022,MONTH_jan,0.6333333333333333
+city_amsterdam,2022,MONTH_jan,0.7243367935409458
+city_rabat,2022,MONTH_jan,0.5133333333333333
+city_ulaanbaatar,2022,MONTH_jan,0.5415140415140415
+city_mexico_city,2022,MONTH_jan,0.5577712609970674
+city_nairobi,2022,MONTH_jan,0.4683127572016461
+city_tokyo,2022,MONTH_jan,0.5072463768115942
+city_baghdad,2022,MONTH_jan,0.43166666666666664
+city_tehran,2022,MONTH_jan,0.6534320323014805
+city_jakarta,2022,MONTH_jan,0.733607855559075
+city_guatemala_city,2022,MONTH_jan,0.5899339933993399
+city_berlin,2022,MONTH_jan,0.6646090534979424
+city_addis_ababa,2022,MONTH_jan,0.46716541978387366
+city_cairo,2022,MONTH_jan,0.3588290840415486
+city_quito,2022,MONTH_jan,0.5719207579672696
+city_bogota,2022,MONTH_jan,0.6966887417218544
+city_beijing,2022,MONTH_jan,0.6871880199667221
+city_accra,2022,MONTH_jan,0.4683127572016461
+city_ottawa,2022,MONTH_jan,0.6124937779990045
+city_brasilia,2022,MONTH_jan,0.644878706199461
+city_la_paz,2022,MONTH_jan,0.7341961174713788
+city_dhaka,2022,MONTH_jan,0.4765694076038904
+city_yerevan,2022,MONTH_jan,0.5004262574595055
+city_chicago,2022,MONTH_jan,0.5481239804241436
+city_kyiv,2022,MONTH_jan,0.5114235500878734
+city_dubai,2022,MONTH_jan,0.3588290840415486
+city_mumbai,2022,MONTH_jan,0.4288
+city_madrid,2022,MONTH_jan,0.6646090534979424
diff --git a/run_tree/data/incenter_data/csv/question_19.csv b/run_tree/data/incenter_data/csv/question_19.csv
new file mode 100644
index 0000000..9ffecf9
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_19.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.7082644628099174
+city_brisbane,2022,MONTH_jan,0.8521205357142857
+city_chengdu,2022,MONTH_jan,0.9380856760374833
+city_new_delhi,2022,MONTH_jan,0.7483477376715811
+city_paris,2022,MONTH_jan,0.8271523178807947
+city_san_francisco,2022,MONTH_jan,0.7714285714285715
+city_denver,2022,MONTH_jan,0.7393822393822393
+city_ankara,2022,MONTH_jan,0.735936188077246
+city_harare,2022,MONTH_jan,0.7394190871369295
+city_hanoi,2022,MONTH_jan,0.9416666666666667
+city_washington,2022,MONTH_jan,0.8
+city_bangkok,2022,MONTH_jan,0.7437457741717377
+city_tunis,2022,MONTH_jan,0.5396825396825397
+city_seoul,2022,MONTH_jan,0.9164658634538153
+city_belgrade,2022,MONTH_jan,0.6821782178217822
+city_moscow,2022,MONTH_jan,0.8499717992103779
+city_lima,2022,MONTH_jan,0.5747211895910781
+city_islamabad,2022,MONTH_jan,0.7483477376715811
+city_abuja,2022,MONTH_jan,0.6848534201954397
+city_managua,2022,MONTH_jan,0.55
+city_amsterdam,2022,MONTH_jan,0.8363922391190352
+city_rabat,2022,MONTH_jan,0.82
+city_ulaanbaatar,2022,MONTH_jan,0.7635919364691509
+city_mexico_city,2022,MONTH_jan,0.6364685516445471
+city_nairobi,2022,MONTH_jan,0.6780876494023904
+city_tokyo,2022,MONTH_jan,0.7748344370860927
+city_baghdad,2022,MONTH_jan,0.78
+city_tehran,2022,MONTH_jan,0.6942204301075269
+city_jakarta,2022,MONTH_jan,0.8313479623824451
+city_guatemala_city,2022,MONTH_jan,0.6363636363636364
+city_berlin,2022,MONTH_jan,0.8271523178807947
+city_addis_ababa,2022,MONTH_jan,0.7589134125636672
+city_cairo,2022,MONTH_jan,0.5408333333333334
+city_quito,2022,MONTH_jan,0.5179916317991632
+city_bogota,2022,MONTH_jan,0.5506578947368421
+city_beijing,2022,MONTH_jan,0.9380856760374833
+city_accra,2022,MONTH_jan,0.6780876494023904
+city_ottawa,2022,MONTH_jan,0.8128422100547537
+city_brasilia,2022,MONTH_jan,0.5583962812318419
+city_la_paz,2022,MONTH_jan,0.5051749630359783
+city_dhaka,2022,MONTH_jan,0.9416666666666667
+city_yerevan,2022,MONTH_jan,0.6306156405990017
+city_chicago,2022,MONTH_jan,0.7963875205254516
+city_kyiv,2022,MONTH_jan,0.830249396621078
+city_dubai,2022,MONTH_jan,0.5408333333333334
+city_mumbai,2022,MONTH_jan,0.7483477376715811
+city_madrid,2022,MONTH_jan,0.8271523178807947
diff --git a/run_tree/data/incenter_data/csv/question_2.csv b/run_tree/data/incenter_data/csv/question_2.csv
new file mode 100644
index 0000000..bafb750
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_2.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.8087649402390438
+city_brisbane,2022,MONTH_jan,0.7064846416382252
+city_chengdu,2022,MONTH_jan,0.6440677966101694
+city_new_delhi,2022,MONTH_jan,0.7936507936507936
+city_paris,2022,MONTH_jan,0.6832853946947907
+city_san_francisco,2022,MONTH_jan,0.814797580269893
+city_denver,2022,MONTH_jan,0.814797580269893
+city_ankara,2022,MONTH_jan,0.870817843866171
+city_harare,2022,MONTH_jan,0.7857142857142857
+city_hanoi,2022,MONTH_jan,0.6257668711656442
+city_washington,2022,MONTH_jan,0.814797580269893
+city_bangkok,2022,MONTH_jan,0.47058823529411764
+city_tunis,2022,MONTH_jan,0.6571428571428571
+city_seoul,2022,MONTH_jan,0.6257668711656442
+city_belgrade,2022,MONTH_jan,0.8
+city_moscow,2022,MONTH_jan,0.8032786885245902
+city_lima,2022,MONTH_jan,0.8
+city_islamabad,2022,MONTH_jan,0.7448275862068966
+city_abuja,2022,MONTH_jan,0.9411764705882353
+city_managua,2022,MONTH_jan,0.8125
+city_amsterdam,2022,MONTH_jan,0.5527714502657555
+city_rabat,2022,MONTH_jan,0.7619047619047619
+city_ulaanbaatar,2022,MONTH_jan,0.6440677966101694
+city_mexico_city,2022,MONTH_jan,0.825
+city_nairobi,2022,MONTH_jan,0.7857142857142857
+city_tokyo,2022,MONTH_jan,0.665843621399177
+city_baghdad,2022,MONTH_jan,0.7058823529411765
+city_tehran,2022,MONTH_jan,0.7058823529411765
+city_jakarta,2022,MONTH_jan,0.7700145560407569
+city_guatemala_city,2022,MONTH_jan,0.8125
+city_berlin,2022,MONTH_jan,0.6601796407185628
+city_addis_ababa,2022,MONTH_jan,0.7857142857142857
+city_cairo,2022,MONTH_jan,0.6571428571428571
+city_quito,2022,MONTH_jan,0.7241379310344828
+city_bogota,2022,MONTH_jan,0.7722222222222223
+city_beijing,2022,MONTH_jan,0.6440677966101694
+city_accra,2022,MONTH_jan,0.7857142857142857
+city_ottawa,2022,MONTH_jan,0.7915690866510539
+city_brasilia,2022,MONTH_jan,0.8020833333333334
+city_la_paz,2022,MONTH_jan,0.8020833333333334
+city_dhaka,2022,MONTH_jan,0.8126721763085399
+city_yerevan,2022,MONTH_jan,0.870817843866171
+city_chicago,2022,MONTH_jan,0.814797580269893
+city_kyiv,2022,MONTH_jan,0.8125
+city_dubai,2022,MONTH_jan,0.7058823529411765
+city_mumbai,2022,MONTH_jan,0.7936507936507936
+city_madrid,2022,MONTH_jan,0.7365591397849462
diff --git a/run_tree/data/incenter_data/csv/question_20.csv b/run_tree/data/incenter_data/csv/question_20.csv
new file mode 100644
index 0000000..3726a6d
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_20.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.008906882591093117
+city_brisbane,2022,MONTH_jan,0.06120157215047726
+city_chengdu,2022,MONTH_jan,0.014885874958650347
+city_new_delhi,2022,MONTH_jan,0.05456570155902005
+city_paris,2022,MONTH_jan,0.07147540983606557
+city_san_francisco,2022,MONTH_jan,0.06863905325443787
+city_denver,2022,MONTH_jan,0.06395348837209303
+city_ankara,2022,MONTH_jan,0.007591733445803458
+city_harare,2022,MONTH_jan,0.07966804979253111
+city_hanoi,2022,MONTH_jan,0.021666666666666667
+city_washington,2022,MONTH_jan,0.05852842809364549
+city_bangkok,2022,MONTH_jan,0.13266666666666665
+city_tunis,2022,MONTH_jan,0.009369676320272573
+city_seoul,2022,MONTH_jan,0.01606425702811245
+city_belgrade,2022,MONTH_jan,0.014691478942213516
+city_moscow,2022,MONTH_jan,0.0061624649859943975
+city_lima,2022,MONTH_jan,0.023038156947444204
+city_islamabad,2022,MONTH_jan,0.05456570155902005
+city_abuja,2022,MONTH_jan,0.09248554913294797
+city_managua,2022,MONTH_jan,0.11166666666666666
+city_amsterdam,2022,MONTH_jan,0.01694915254237288
+city_rabat,2022,MONTH_jan,0.041666666666666664
+city_ulaanbaatar,2022,MONTH_jan,0.11233211233211234
+city_mexico_city,2022,MONTH_jan,0.047756041426927506
+city_nairobi,2022,MONTH_jan,0.1770573566084788
+city_tokyo,2022,MONTH_jan,0.0037425149700598802
+city_baghdad,2022,MONTH_jan,0.037800687285223365
+city_tehran,2022,MONTH_jan,0.06412825651302605
+city_jakarta,2022,MONTH_jan,0.2173776662484316
+city_guatemala_city,2022,MONTH_jan,0.06795302013422819
+city_berlin,2022,MONTH_jan,0.07147540983606557
+city_addis_ababa,2022,MONTH_jan,0.11595394736842106
+city_cairo,2022,MONTH_jan,0.0016680567139282735
+city_quito,2022,MONTH_jan,0.05396290050590219
+city_bogota,2022,MONTH_jan,0.1118421052631579
+city_beijing,2022,MONTH_jan,0.014885874958650347
+city_accra,2022,MONTH_jan,0.1770573566084788
+city_ottawa,2022,MONTH_jan,0.05301144848183176
+city_brasilia,2022,MONTH_jan,0.017452006980802792
+city_la_paz,2022,MONTH_jan,0.08312958435207823
+city_dhaka,2022,MONTH_jan,0.03836317135549872
+city_yerevan,2022,MONTH_jan,0.02875924404272802
+city_chicago,2022,MONTH_jan,0.06188925081433225
+city_kyiv,2022,MONTH_jan,0.01675977653631285
+city_dubai,2022,MONTH_jan,0.0016680567139282735
+city_mumbai,2022,MONTH_jan,0.05456570155902005
+city_madrid,2022,MONTH_jan,0.07147540983606557
diff --git a/run_tree/data/incenter_data/csv/question_21.csv b/run_tree/data/incenter_data/csv/question_21.csv
new file mode 100644
index 0000000..85c6a6f
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_21.csv
@@ -0,0 +1,1949 @@
+city_data,year,month,prop
+city_brisbane,1965,MONTH_jan,0.05562621593475342
+city_brisbane,1966,MONTH_jan,0.05071894645690918
+city_brisbane,1967,MONTH_jan,0.049495487213134765
+city_brisbane,1968,MONTH_jan,0.04865467548370361
+city_brisbane,1969,MONTH_jan,0.05086987972259521
+city_brisbane,1970,MONTH_jan,0.05421450614929199
+city_brisbane,1971,MONTH_jan,0.05822727203369141
+city_brisbane,1972,MONTH_jan,0.05599285125732422
+city_brisbane,1973,MONTH_jan,0.055886645317077634
+city_brisbane,1974,MONTH_jan,0.05955109596252441
+city_brisbane,1975,MONTH_jan,0.06297008037567138
+city_brisbane,1976,MONTH_jan,0.05793421268463135
+city_brisbane,1977,MONTH_jan,0.05269774913787842
+city_brisbane,1978,MONTH_jan,0.0550869607925415
+city_brisbane,1979,MONTH_jan,0.051658406257629394
+city_brisbane,1980,MONTH_jan,0.04862271785736084
+city_brisbane,1981,MONTH_jan,0.04930354595184326
+city_brisbane,1982,MONTH_jan,0.046367826461791994
+city_brisbane,1983,MONTH_jan,0.045344815254211426
+city_brisbane,1984,MONTH_jan,0.04718454360961914
+city_brisbane,1985,MONTH_jan,0.04863154888153076
+city_brisbane,1986,MONTH_jan,0.047985596656799315
+city_brisbane,1987,MONTH_jan,0.04532893180847168
+city_brisbane,1988,MONTH_jan,0.046416802406311033
+city_brisbane,1989,MONTH_jan,0.044199533462524414
+city_brisbane,1990,MONTH_jan,0.044593911170959476
+city_brisbane,1991,MONTH_jan,0.047400617599487306
+city_brisbane,1992,MONTH_jan,0.04731717109680176
+city_brisbane,1993,MONTH_jan,0.0475942850112915
+city_brisbane,1994,MONTH_jan,0.045271806716918946
+city_brisbane,1995,MONTH_jan,0.04239725112915039
+city_brisbane,1996,MONTH_jan,0.0423659086227417
+city_brisbane,1997,MONTH_jan,0.04145838737487793
+city_brisbane,1998,MONTH_jan,0.04037755966186524
+city_brisbane,1999,MONTH_jan,0.04008933544158935
+city_brisbane,2000,MONTH_jan,0.03988877773284912
+city_brisbane,2001,MONTH_jan,0.0387076210975647
+city_brisbane,2002,MONTH_jan,0.03931450366973877
+city_brisbane,2003,MONTH_jan,0.040149688720703125
+city_brisbane,2004,MONTH_jan,0.04036560535430908
+city_brisbane,2005,MONTH_jan,0.04313553333282471
+city_brisbane,2006,MONTH_jan,0.04233413219451904
+city_brisbane,2007,MONTH_jan,0.039767227172851555
+city_brisbane,2008,MONTH_jan,0.03681326389312744
+city_brisbane,2009,MONTH_jan,0.03920266151428222
+city_brisbane,2010,MONTH_jan,0.04312804698944092
+city_brisbane,2011,MONTH_jan,0.05698980808258056
+city_brisbane,2012,MONTH_jan,0.05700209140777588
+city_brisbane,2013,MONTH_jan,0.06525868892669678
+city_brisbane,2014,MONTH_jan,0.05979823112487793
+city_brisbane,2015,MONTH_jan,0.06290020942687988
+city_brisbane,2016,MONTH_jan,0.07189175605773926
+city_brisbane,2017,MONTH_jan,0.06750126838684083
+city_brisbane,2018,MONTH_jan,0.08300061225891113
+city_brisbane,2019,MONTH_jan,0.08880722045898437
+city_brisbane,2020,MONTH_jan,0.10789811134338381
+city_brisbane,2021,MONTH_jan,0.12933531761169434
+city_dhaka,1971,MONTH_jan,0.03859757661819458
+city_dhaka,1972,MONTH_jan,0.03406534433364868
+city_dhaka,1973,MONTH_jan,0.050243868827819824
+city_dhaka,1974,MONTH_jan,0.03463459491729736
+city_dhaka,1975,MONTH_jan,0.06043976306915283
+city_dhaka,1976,MONTH_jan,0.05868124008178711
+city_dhaka,1977,MONTH_jan,0.05079775810241699
+city_dhaka,1978,MONTH_jan,0.05536711692810058
+city_dhaka,1979,MONTH_jan,0.05825671672821045
+city_dhaka,1980,MONTH_jan,0.049608802795410155
+city_dhaka,1981,MONTH_jan,0.05016649723052979
+city_dhaka,1982,MONTH_jan,0.03805250644683838
+city_dhaka,1983,MONTH_jan,0.048706588745117185
+city_dhaka,1984,MONTH_jan,0.06105583190917969
+city_dhaka,1985,MONTH_jan,0.04526681423187256
+city_dhaka,1986,MONTH_jan,0.025327975749969478
+city_dhaka,1987,MONTH_jan,0.026365013122558595
+city_dhaka,1988,MONTH_jan,0.030908639430999755
+city_dhaka,1989,MONTH_jan,0.03907864093780518
+city_dhaka,1990,MONTH_jan,0.03539271354675293
+city_dhaka,1991,MONTH_jan,0.03477609157562256
+city_dhaka,1992,MONTH_jan,0.030288853645324708
+city_dhaka,1993,MONTH_jan,0.02104328870773315
+city_dhaka,1994,MONTH_jan,0.02761680603027344
+city_dhaka,1995,MONTH_jan,0.010269947052001951
+city_dhaka,1996,MONTH_jan,0.01947564125061035
+city_dhaka,1997,MONTH_jan,0.018034275770187375
+city_dhaka,1998,MONTH_jan,0.020371236801147462
+city_dhaka,1999,MONTH_jan,0.0193972909450531
+city_dhaka,2000,MONTH_jan,0.016270726919174194
+city_dhaka,2001,MONTH_jan,0.01875003218650818
+city_dhaka,2002,MONTH_jan,0.01362207531929016
+city_dhaka,2003,MONTH_jan,0.012784041166305542
+city_dhaka,2004,MONTH_jan,0.012296963930130005
+city_dhaka,2005,MONTH_jan,0.011353458166122437
+city_dhaka,2006,MONTH_jan,0.010463094711303711
+city_dhaka,2007,MONTH_jan,0.010207374095916748
+city_dhaka,2008,MONTH_jan,0.012218128442764282
+city_dhaka,2009,MONTH_jan,0.0051176011562347416
+city_dhaka,2010,MONTH_jan,0.008648931980133057
+city_dhaka,2011,MONTH_jan,0.009513694643974304
+city_dhaka,2012,MONTH_jan,0.008256486654281615
+city_dhaka,2013,MONTH_jan,0.007885019183158875
+city_dhaka,2014,MONTH_jan,0.0064616197347640994
+city_dhaka,2015,MONTH_jan,0.008184085488319398
+city_dhaka,2016,MONTH_jan,0.008060975074768066
+city_dhaka,2017,MONTH_jan,0.00894644320011139
+city_dhaka,2018,MONTH_jan,0.007471175193786621
+city_dhaka,2019,MONTH_jan,0.00675128698348999
+city_dhaka,2020,MONTH_jan,0.006580818295478821
+city_dhaka,2021,MONTH_jan,0.0065014690160751345
+city_brasilia,1965,MONTH_jan,0.2754872131347656
+city_brasilia,1966,MONTH_jan,0.27618213653564455
+city_brasilia,1967,MONTH_jan,0.27880290985107425
+city_brasilia,1968,MONTH_jan,0.2555575370788575
+city_brasilia,1969,MONTH_jan,0.2505621910095215
+city_brasilia,1970,MONTH_jan,0.2690108108520508
+city_brasilia,1971,MONTH_jan,0.26817771911621097
+city_brasilia,1972,MONTH_jan,0.2739073371887207
+city_brasilia,1973,MONTH_jan,0.2610169982910156
+city_brasilia,1974,MONTH_jan,0.2692813491821289
+city_brasilia,1975,MONTH_jan,0.279958610534668
+city_brasilia,1976,MONTH_jan,0.2916008758544922
+city_brasilia,1977,MONTH_jan,0.3108434295654297
+city_brasilia,1978,MONTH_jan,0.31529354095458983
+city_brasilia,1979,MONTH_jan,0.3338748931884766
+city_brasilia,1980,MONTH_jan,0.3618800735473633
+city_brasilia,1981,MONTH_jan,0.3750487899780273
+city_brasilia,1982,MONTH_jan,0.3909782028198242
+city_brasilia,1983,MONTH_jan,0.41867771148681643
+city_brasilia,1984,MONTH_jan,0.43218948364257814
+city_brasilia,1985,MONTH_jan,0.43929534912109375
+city_brasilia,1986,MONTH_jan,0.4321952438354492
+city_brasilia,1987,MONTH_jan,0.42777210235595703
+city_brasilia,1988,MONTH_jan,0.4401119613647461
+city_brasilia,1989,MONTH_jan,0.44574317932128904
+city_brasilia,1990,MONTH_jan,0.4527548599243164
+city_brasilia,1991,MONTH_jan,0.4586178970336914
+city_brasilia,1992,MONTH_jan,0.44902706146240234
+city_brasilia,1993,MONTH_jan,0.4515962982177734
+city_brasilia,1994,MONTH_jan,0.44565601348876954
+city_brasilia,1995,MONTH_jan,0.4434580993652344
+city_brasilia,1996,MONTH_jan,0.44081958770751956
+city_brasilia,1997,MONTH_jan,0.43489933013916016
+city_brasilia,1998,MONTH_jan,0.43571971893310546
+city_brasilia,1999,MONTH_jan,0.43143802642822265
+city_brasilia,2000,MONTH_jan,0.4328843307495117
+city_brasilia,2001,MONTH_jan,0.39111621856689455
+city_brasilia,2002,MONTH_jan,0.4075665283203125
+city_brasilia,2003,MONTH_jan,0.4228412628173828
+city_brasilia,2004,MONTH_jan,0.4228615570068359
+city_brasilia,2005,MONTH_jan,0.4314569854736328
+city_brasilia,2006,MONTH_jan,0.4304596328735352
+city_brasilia,2007,MONTH_jan,0.4438022613525391
+city_brasilia,2008,MONTH_jan,0.4336677551269531
+city_brasilia,2009,MONTH_jan,0.4630883026123047
+city_brasilia,2010,MONTH_jan,0.4424653625488281
+city_brasilia,2011,MONTH_jan,0.4391006088256836
+city_brasilia,2012,MONTH_jan,0.42066368103027346
+city_brasilia,2013,MONTH_jan,0.39836597442626953
+city_brasilia,2014,MONTH_jan,0.38852745056152344
+city_brasilia,2015,MONTH_jan,0.4060352325439453
+city_brasilia,2016,MONTH_jan,0.43538700103759764
+city_brasilia,2017,MONTH_jan,0.4328067779541016
+city_brasilia,2018,MONTH_jan,0.46080497741699217
+city_brasilia,2019,MONTH_jan,0.4730484390258789
+city_brasilia,2020,MONTH_jan,0.4947188186645508
+city_brasilia,2021,MONTH_jan,0.46219749450683595
+city_ottawa,1965,MONTH_jan,0.2513864898681641
+city_ottawa,1966,MONTH_jan,0.2619474792480469
+city_ottawa,1967,MONTH_jan,0.2561042594909668
+city_ottawa,1968,MONTH_jan,0.24474010467529297
+city_ottawa,1969,MONTH_jan,0.25521268844604494
+city_ottawa,1970,MONTH_jan,0.2502507591247558
+city_ottawa,1971,MONTH_jan,0.24927824020385747
+city_ottawa,1972,MONTH_jan,0.25893644332885746
+city_ottawa,1973,MONTH_jan,0.2555288314819336
+city_ottawa,1974,MONTH_jan,0.2681158256530762
+city_ottawa,1975,MONTH_jan,0.2615631484985352
+city_ottawa,1976,MONTH_jan,0.2633203506469727
+city_ottawa,1977,MONTH_jan,0.25808473587036135
+city_ottawa,1978,MONTH_jan,0.2705876922607422
+city_ottawa,1979,MONTH_jan,0.2729702377319336
+city_ottawa,1980,MONTH_jan,0.2753968620300293
+city_ottawa,1981,MONTH_jan,0.29403928756713865
+city_ottawa,1982,MONTH_jan,0.29416236877441404
+city_ottawa,1983,MONTH_jan,0.30499225616455083
+city_ottawa,1984,MONTH_jan,0.30855842590332033
+city_ottawa,1985,MONTH_jan,0.3161163330078125
+city_ottawa,1986,MONTH_jan,0.32045928955078123
+city_ottawa,1987,MONTH_jan,0.316495418548584
+city_ottawa,1988,MONTH_jan,0.29611337661743165
+city_ottawa,1989,MONTH_jan,0.2781739616394043
+city_ottawa,1990,MONTH_jan,0.29480998992919916
+city_ottawa,1991,MONTH_jan,0.30499427795410156
+city_ottawa,1992,MONTH_jan,0.30512109756469724
+city_ottawa,1993,MONTH_jan,0.3045089340209961
+city_ottawa,1994,MONTH_jan,0.2992730140686035
+city_ottawa,1995,MONTH_jan,0.2988811683654785
+city_ottawa,1996,MONTH_jan,0.3087930679321289
+city_ottawa,1997,MONTH_jan,0.3028490447998047
+city_ottawa,1998,MONTH_jan,0.29307371139526367
+city_ottawa,1999,MONTH_jan,0.29588794708251953
+city_ottawa,2000,MONTH_jan,0.297678165435791
+city_ottawa,2001,MONTH_jan,0.282014102935791
+city_ottawa,2002,MONTH_jan,0.2854655647277832
+city_ottawa,2003,MONTH_jan,0.2718001365661621
+city_ottawa,2004,MONTH_jan,0.2682792282104492
+city_ottawa,2005,MONTH_jan,0.2826547622680664
+city_ottawa,2006,MONTH_jan,0.2761646842956543
+city_ottawa,2007,MONTH_jan,0.2769431495666504
+city_ottawa,2008,MONTH_jan,0.2859009170532227
+city_ottawa,2009,MONTH_jan,0.2920944976806641
+city_ottawa,2010,MONTH_jan,0.27789688110351557
+city_ottawa,2011,MONTH_jan,0.2862636947631836
+city_ottawa,2012,MONTH_jan,0.2897691345214844
+city_ottawa,2013,MONTH_jan,0.2894182777404785
+city_ottawa,2014,MONTH_jan,0.2814776039123535
+city_ottawa,2015,MONTH_jan,0.2876414680480957
+city_ottawa,2016,MONTH_jan,0.29652599334716795
+city_ottawa,2017,MONTH_jan,0.2967363929748535
+city_ottawa,2018,MONTH_jan,0.28814517974853515
+city_ottawa,2019,MONTH_jan,0.2858637619018555
+city_ottawa,2020,MONTH_jan,0.3054215049743652
+city_ottawa,2021,MONTH_jan,0.29888439178466797
+city_chengdu,1965,MONTH_jan,0.042534070014953615
+city_beijing,1965,MONTH_jan,0.042534070014953615
+city_chengdu,1966,MONTH_jan,0.039831228256225586
+city_beijing,1966,MONTH_jan,0.039831228256225586
+city_chengdu,1967,MONTH_jan,0.038183979988098145
+city_beijing,1967,MONTH_jan,0.038183979988098145
+city_chengdu,1968,MONTH_jan,0.04458870887756348
+city_beijing,1968,MONTH_jan,0.04458870887756348
+city_chengdu,1969,MONTH_jan,0.03727402210235596
+city_beijing,1969,MONTH_jan,0.03727402210235596
+city_chengdu,1970,MONTH_jan,0.03009592294692993
+city_beijing,1970,MONTH_jan,0.03009592294692993
+city_chengdu,1971,MONTH_jan,0.030704047679901123
+city_beijing,1971,MONTH_jan,0.030704047679901123
+city_chengdu,1972,MONTH_jan,0.0322759747505188
+city_beijing,1972,MONTH_jan,0.0322759747505188
+city_chengdu,1973,MONTH_jan,0.03417015075683594
+city_beijing,1973,MONTH_jan,0.03417015075683594
+city_chengdu,1974,MONTH_jan,0.03749354600906372
+city_beijing,1974,MONTH_jan,0.03749354600906372
+city_chengdu,1975,MONTH_jan,0.035038237571716306
+city_beijing,1975,MONTH_jan,0.035038237571716306
+city_chengdu,1976,MONTH_jan,0.03372568368911743
+city_beijing,1976,MONTH_jan,0.03372568368911743
+city_chengdu,1977,MONTH_jan,0.03228713989257812
+city_beijing,1977,MONTH_jan,0.03228713989257812
+city_chengdu,1978,MONTH_jan,0.027594637870788575
+city_beijing,1978,MONTH_jan,0.027594637870788575
+city_chengdu,1979,MONTH_jan,0.03008899211883545
+city_beijing,1979,MONTH_jan,0.03008899211883545
+city_chengdu,1980,MONTH_jan,0.035661976337432864
+city_beijing,1980,MONTH_jan,0.035661976337432864
+city_chengdu,1981,MONTH_jan,0.040664968490600584
+city_beijing,1981,MONTH_jan,0.040664968490600584
+city_chengdu,1982,MONTH_jan,0.044212722778320314
+city_beijing,1982,MONTH_jan,0.044212722778320314
+city_chengdu,1983,MONTH_jan,0.048234906196594235
+city_beijing,1983,MONTH_jan,0.048234906196594235
+city_chengdu,1984,MONTH_jan,0.045154547691345213
+city_beijing,1984,MONTH_jan,0.045154547691345213
+city_chengdu,1985,MONTH_jan,0.04441830158233642
+city_beijing,1985,MONTH_jan,0.04441830158233642
+city_chengdu,1986,MONTH_jan,0.043385276794433596
+city_beijing,1986,MONTH_jan,0.043385276794433596
+city_chengdu,1987,MONTH_jan,0.04255547523498535
+city_beijing,1987,MONTH_jan,0.04255547523498535
+city_chengdu,1988,MONTH_jan,0.043230724334716794
+city_beijing,1988,MONTH_jan,0.043230724334716794
+city_chengdu,1989,MONTH_jan,0.044680271148681644
+city_beijing,1989,MONTH_jan,0.044680271148681644
+city_chengdu,1990,MONTH_jan,0.04723667621612549
+city_beijing,1990,MONTH_jan,0.04723667621612549
+city_chengdu,1991,MONTH_jan,0.04424777984619141
+city_beijing,1991,MONTH_jan,0.04424777984619141
+city_chengdu,1992,MONTH_jan,0.04412701606750488
+city_beijing,1992,MONTH_jan,0.04412701606750488
+city_chengdu,1993,MONTH_jan,0.04757860660552979
+city_beijing,1993,MONTH_jan,0.04757860660552979
+city_chengdu,1994,MONTH_jan,0.04952474117279053
+city_beijing,1994,MONTH_jan,0.04952474117279053
+city_chengdu,1995,MONTH_jan,0.05553820133209229
+city_beijing,1995,MONTH_jan,0.05553820133209229
+city_chengdu,1996,MONTH_jan,0.05143133163452149
+city_beijing,1996,MONTH_jan,0.05143133163452149
+city_chengdu,1997,MONTH_jan,0.05361733913421631
+city_beijing,1997,MONTH_jan,0.05361733913421631
+city_chengdu,1998,MONTH_jan,0.054277782440185544
+city_beijing,1998,MONTH_jan,0.054277782440185544
+city_chengdu,1999,MONTH_jan,0.05188285827636719
+city_beijing,1999,MONTH_jan,0.05188285827636719
+city_chengdu,2000,MONTH_jan,0.05656925201416016
+city_beijing,2000,MONTH_jan,0.05656925201416016
+city_chengdu,2001,MONTH_jan,0.06618624210357665
+city_beijing,2001,MONTH_jan,0.06618624210357665
+city_chengdu,2002,MONTH_jan,0.0628048849105835
+city_beijing,2002,MONTH_jan,0.0628048849105835
+city_chengdu,2003,MONTH_jan,0.05301109313964844
+city_beijing,2003,MONTH_jan,0.05301109313964844
+city_chengdu,2004,MONTH_jan,0.05600940227508545
+city_beijing,2004,MONTH_jan,0.05600940227508545
+city_chengdu,2005,MONTH_jan,0.05554481029510498
+city_beijing,2005,MONTH_jan,0.05554481029510498
+city_chengdu,2006,MONTH_jan,0.055779824256896975
+city_beijing,2006,MONTH_jan,0.055779824256896975
+city_chengdu,2007,MONTH_jan,0.0571853494644165
+city_beijing,2007,MONTH_jan,0.0571853494644165
+city_chengdu,2008,MONTH_jan,0.07268667221069336
+city_beijing,2008,MONTH_jan,0.07268667221069336
+city_chengdu,2009,MONTH_jan,0.06935531616210938
+city_beijing,2009,MONTH_jan,0.06935531616210938
+city_chengdu,2010,MONTH_jan,0.07615997791290283
+city_beijing,2010,MONTH_jan,0.07615997791290283
+city_chengdu,2011,MONTH_jan,0.07084204196929932
+city_beijing,2011,MONTH_jan,0.07084204196929932
+city_chengdu,2012,MONTH_jan,0.08522597312927246
+city_beijing,2012,MONTH_jan,0.08522597312927246
+city_chengdu,2013,MONTH_jan,0.08959421157836914
+city_beijing,2013,MONTH_jan,0.08959421157836914
+city_chengdu,2014,MONTH_jan,0.10197330474853515
+city_beijing,2014,MONTH_jan,0.10197330474853515
+city_chengdu,2015,MONTH_jan,0.10787498474121093
+city_beijing,2015,MONTH_jan,0.10787498474121093
+city_chengdu,2016,MONTH_jan,0.11530946731567385
+city_beijing,2016,MONTH_jan,0.11530946731567385
+city_chengdu,2017,MONTH_jan,0.12138820648193359
+city_beijing,2017,MONTH_jan,0.12138820648193359
+city_chengdu,2018,MONTH_jan,0.12807393074035645
+city_beijing,2018,MONTH_jan,0.12807393074035645
+city_chengdu,2019,MONTH_jan,0.1351595973968506
+city_beijing,2019,MONTH_jan,0.1351595973968506
+city_chengdu,2020,MONTH_jan,0.1424287223815918
+city_beijing,2020,MONTH_jan,0.1424287223815918
+city_chengdu,2021,MONTH_jan,0.14946244239807127
+city_beijing,2021,MONTH_jan,0.14946244239807127
+city_bogota,1965,MONTH_jan,0.11788208961486817
+city_bogota,1966,MONTH_jan,0.12017394065856933
+city_bogota,1967,MONTH_jan,0.12580106735229493
+city_bogota,1968,MONTH_jan,0.13300461769104005
+city_bogota,1969,MONTH_jan,0.1452613925933838
+city_bogota,1970,MONTH_jan,0.14817516326904298
+city_bogota,1971,MONTH_jan,0.15987798690795899
+city_bogota,1972,MONTH_jan,0.15767953872680665
+city_bogota,1973,MONTH_jan,0.17170427322387696
+city_bogota,1974,MONTH_jan,0.17395509719848634
+city_bogota,1975,MONTH_jan,0.19235607147216796
+city_bogota,1976,MONTH_jan,0.1940712547302246
+city_bogota,1977,MONTH_jan,0.1936701011657715
+city_bogota,1978,MONTH_jan,0.20862884521484376
+city_bogota,1979,MONTH_jan,0.2154510498046875
+city_bogota,1980,MONTH_jan,0.23044971466064454
+city_bogota,1981,MONTH_jan,0.2221888542175293
+city_bogota,1982,MONTH_jan,0.22697723388671875
+city_bogota,1983,MONTH_jan,0.2179018783569336
+city_bogota,1984,MONTH_jan,0.23639833450317382
+city_bogota,1985,MONTH_jan,0.2444196128845215
+city_bogota,1986,MONTH_jan,0.27276325225830084
+city_bogota,1987,MONTH_jan,0.2731085395812988
+city_bogota,1988,MONTH_jan,0.28748077392578125
+city_bogota,1989,MONTH_jan,0.29793998718261716
+city_bogota,1990,MONTH_jan,0.32640933990478516
+city_bogota,1991,MONTH_jan,0.3169980049133301
+city_bogota,1992,MONTH_jan,0.2460545539855957
+city_bogota,1993,MONTH_jan,0.27847396850585937
+city_bogota,1994,MONTH_jan,0.2980908203125
+city_bogota,1995,MONTH_jan,0.2933157730102539
+city_bogota,1996,MONTH_jan,0.31475914001464844
+city_bogota,1997,MONTH_jan,0.27334064483642573
+city_bogota,1998,MONTH_jan,0.26691463470458987
+city_bogota,1999,MONTH_jan,0.3137824821472168
+city_bogota,2000,MONTH_jan,0.29556066513061524
+city_bogota,2001,MONTH_jan,0.3060448455810547
+city_bogota,2002,MONTH_jan,0.32692554473876956
+city_bogota,2003,MONTH_jan,0.3338913345336914
+city_bogota,2004,MONTH_jan,0.3450545501708984
+city_bogota,2005,MONTH_jan,0.35053531646728514
+city_bogota,2006,MONTH_jan,0.34142719268798827
+city_bogota,2007,MONTH_jan,0.35182762145996094
+city_bogota,2008,MONTH_jan,0.33941410064697264
+city_bogota,2009,MONTH_jan,0.31377744674682617
+city_bogota,2010,MONTH_jan,0.30092037200927735
+city_bogota,2011,MONTH_jan,0.34515674591064455
+city_bogota,2012,MONTH_jan,0.3174613380432129
+city_bogota,2013,MONTH_jan,0.29385107040405273
+city_bogota,2014,MONTH_jan,0.2836468505859375
+city_bogota,2015,MONTH_jan,0.280216178894043
+city_bogota,2016,MONTH_jan,0.2770881271362305
+city_bogota,2017,MONTH_jan,0.3282905960083008
+city_bogota,2018,MONTH_jan,0.31999549865722654
+city_bogota,2019,MONTH_jan,0.29890342712402346
+city_bogota,2020,MONTH_jan,0.3077278709411621
+city_bogota,2021,MONTH_jan,0.33020408630371095
+city_quito,1965,MONTH_jan,0.0850521469116211
+city_quito,1966,MONTH_jan,0.0924820613861084
+city_quito,1967,MONTH_jan,0.09417126655578612
+city_quito,1968,MONTH_jan,0.08330953598022461
+city_quito,1969,MONTH_jan,0.08701672554016113
+city_quito,1970,MONTH_jan,0.08106535911560059
+city_quito,1971,MONTH_jan,0.08015813827514648
+city_quito,1972,MONTH_jan,0.07758965969085693
+city_quito,1973,MONTH_jan,0.07086233615875244
+city_quito,1974,MONTH_jan,0.06978053569793702
+city_quito,1975,MONTH_jan,0.08653943061828613
+city_quito,1976,MONTH_jan,0.07572480201721192
+city_quito,1977,MONTH_jan,0.05873260498046875
+city_quito,1978,MONTH_jan,0.07960052967071533
+city_quito,1979,MONTH_jan,0.0685176420211792
+city_quito,1980,MONTH_jan,0.06642500877380371
+city_quito,1981,MONTH_jan,0.05350114345550537
+city_quito,1982,MONTH_jan,0.0559368371963501
+city_quito,1983,MONTH_jan,0.10982114791870115
+city_quito,1984,MONTH_jan,0.18537538528442382
+city_quito,1985,MONTH_jan,0.15903298377990724
+city_quito,1986,MONTH_jan,0.18563940048217772
+city_quito,1987,MONTH_jan,0.2051675033569336
+city_quito,1988,MONTH_jan,0.18759294509887695
+city_quito,1989,MONTH_jan,0.1867353248596191
+city_quito,1990,MONTH_jan,0.18208444595336912
+city_quito,1991,MONTH_jan,0.16781824111938476
+city_quito,1992,MONTH_jan,0.1652834701538086
+city_quito,1993,MONTH_jan,0.18627613067626952
+city_quito,1994,MONTH_jan,0.19351001739501952
+city_quito,1995,MONTH_jan,0.160936336517334
+city_quito,1996,MONTH_jan,0.17381893157958983
+city_quito,1997,MONTH_jan,0.16071613311767577
+city_quito,1998,MONTH_jan,0.15920068740844728
+city_quito,1999,MONTH_jan,0.19251895904541017
+city_quito,2000,MONTH_jan,0.21613927841186523
+city_quito,2001,MONTH_jan,0.19692354202270507
+city_quito,2002,MONTH_jan,0.21095178604125978
+city_quito,2003,MONTH_jan,0.19533443450927734
+city_quito,2004,MONTH_jan,0.1873490524291992
+city_quito,2005,MONTH_jan,0.1659660530090332
+city_quito,2006,MONTH_jan,0.16251609802246095
+city_quito,2007,MONTH_jan,0.1955727195739746
+city_quito,2008,MONTH_jan,0.22617521286010744
+city_quito,2009,MONTH_jan,0.18929243087768555
+city_quito,2010,MONTH_jan,0.15936485290527344
+city_quito,2011,MONTH_jan,0.19198841094970703
+city_quito,2012,MONTH_jan,0.19840085983276368
+city_quito,2013,MONTH_jan,0.1737746238708496
+city_quito,2014,MONTH_jan,0.1727614212036133
+city_quito,2015,MONTH_jan,0.19640232086181642
+city_quito,2016,MONTH_jan,0.2362802505493164
+city_quito,2017,MONTH_jan,0.2833585739135743
+city_quito,2018,MONTH_jan,0.2738693428039551
+city_quito,2019,MONTH_jan,0.31769020080566407
+city_quito,2020,MONTH_jan,0.3603577423095703
+city_quito,2021,MONTH_jan,0.32354198455810546
+city_cairo,1965,MONTH_jan,0.055878944396972656
+city_cairo,1966,MONTH_jan,0.055950436592102054
+city_cairo,1967,MONTH_jan,0.07041625022888183
+city_cairo,1968,MONTH_jan,0.09956792831420898
+city_cairo,1969,MONTH_jan,0.16082429885864258
+city_cairo,1970,MONTH_jan,0.1516463565826416
+city_cairo,1971,MONTH_jan,0.1587799835205078
+city_cairo,1972,MONTH_jan,0.14635689735412596
+city_cairo,1973,MONTH_jan,0.15458051681518556
+city_cairo,1974,MONTH_jan,0.16143081665039063
+city_cairo,1975,MONTH_jan,0.16160579681396484
+city_cairo,1976,MONTH_jan,0.15923643112182614
+city_cairo,1977,MONTH_jan,0.16571006774902344
+city_cairo,1978,MONTH_jan,0.17237102508544921
+city_cairo,1979,MONTH_jan,0.15267534255981444
+city_cairo,1980,MONTH_jan,0.13694029808044433
+city_cairo,1981,MONTH_jan,0.12526339530944824
+city_cairo,1982,MONTH_jan,0.11533038139343262
+city_cairo,1983,MONTH_jan,0.09980415344238279
+city_cairo,1984,MONTH_jan,0.08979602813720704
+city_cairo,1985,MONTH_jan,0.08225811958312988
+city_cairo,1986,MONTH_jan,0.07888433456420899
+city_cairo,1987,MONTH_jan,0.0743703556060791
+city_cairo,1988,MONTH_jan,0.07214404582977295
+city_cairo,1989,MONTH_jan,0.07353952407836914
+city_cairo,1990,MONTH_jan,0.07520865440368653
+city_cairo,1991,MONTH_jan,0.07396338939666748
+city_cairo,1992,MONTH_jan,0.07380685806274415
+city_cairo,1993,MONTH_jan,0.07595428943634033
+city_cairo,1994,MONTH_jan,0.07676783084869385
+city_cairo,1995,MONTH_jan,0.07558946132659912
+city_cairo,1996,MONTH_jan,0.07361753940582276
+city_cairo,1997,MONTH_jan,0.07243848800659179
+city_cairo,1998,MONTH_jan,0.07154178619384766
+city_cairo,1999,MONTH_jan,0.07409151077270508
+city_cairo,2000,MONTH_jan,0.07416068077087402
+city_cairo,2001,MONTH_jan,0.07225909233093261
+city_cairo,2002,MONTH_jan,0.06795775890350342
+city_cairo,2003,MONTH_jan,0.059376978874206544
+city_cairo,2004,MONTH_jan,0.05634644031524658
+city_cairo,2005,MONTH_jan,0.05385705947875977
+city_cairo,2006,MONTH_jan,0.05172313690185547
+city_cairo,2007,MONTH_jan,0.053631553649902346
+city_cairo,2008,MONTH_jan,0.05414237022399902
+city_cairo,2009,MONTH_jan,0.04800594806671143
+city_cairo,2010,MONTH_jan,0.04461719036102295
+city_cairo,2011,MONTH_jan,0.04331544399261475
+city_cairo,2012,MONTH_jan,0.041802897453308105
+city_cairo,2013,MONTH_jan,0.04177184581756592
+city_cairo,2014,MONTH_jan,0.04181652545928955
+city_cairo,2015,MONTH_jan,0.043288531303405764
+city_cairo,2016,MONTH_jan,0.041314678192138674
+city_cairo,2017,MONTH_jan,0.038699905872344974
+city_cairo,2018,MONTH_jan,0.04055405616760254
+city_cairo,2019,MONTH_jan,0.05169897556304932
+city_cairo,2020,MONTH_jan,0.06564623832702637
+city_cairo,2021,MONTH_jan,0.06230570793151855
+city_paris,1965,MONTH_jan,0.10584983825683594
+city_paris,1966,MONTH_jan,0.1160239315032959
+city_paris,1967,MONTH_jan,0.09537199020385742
+city_paris,1968,MONTH_jan,0.10092880249023438
+city_paris,1969,MONTH_jan,0.09634167671203611
+city_paris,1970,MONTH_jan,0.09537435531616213
+city_paris,1971,MONTH_jan,0.07942842483520508
+city_paris,1972,MONTH_jan,0.07446317195892334
+city_paris,1973,MONTH_jan,0.06709061622619629
+city_paris,1974,MONTH_jan,0.08016220092773438
+city_paris,1975,MONTH_jan,0.09053054809570313
+city_paris,1976,MONTH_jan,0.06899677753448487
+city_paris,1977,MONTH_jan,0.1059511947631836
+city_paris,1978,MONTH_jan,0.09109526634216308
+city_paris,1979,MONTH_jan,0.08611678123474122
+city_paris,1980,MONTH_jan,0.09114398002624512
+city_paris,1981,MONTH_jan,0.09551752090454102
+city_paris,1982,MONTH_jan,0.09705157279968261
+city_paris,1983,MONTH_jan,0.09325616836547851
+city_paris,1984,MONTH_jan,0.08554503440856934
+city_paris,1985,MONTH_jan,0.07887791156768799
+city_paris,1986,MONTH_jan,0.07764792442321777
+city_paris,1987,MONTH_jan,0.0858464527130127
+city_paris,1988,MONTH_jan,0.09174312591552734
+city_paris,1989,MONTH_jan,0.055997366905212405
+city_paris,1990,MONTH_jan,0.06222640991210938
+city_paris,1991,MONTH_jan,0.0626654052734375
+city_paris,1992,MONTH_jan,0.07399243831634522
+city_paris,1993,MONTH_jan,0.06968883514404296
+city_paris,1994,MONTH_jan,0.08563506126403808
+city_paris,1995,MONTH_jan,0.07823903083801269
+city_paris,1996,MONTH_jan,0.06882894039154053
+city_paris,1997,MONTH_jan,0.0680388879776001
+city_paris,1998,MONTH_jan,0.06470213890075684
+city_paris,1999,MONTH_jan,0.07388863086700441
+city_paris,2000,MONTH_jan,0.06744560718536377
+city_paris,2001,MONTH_jan,0.07405366897583007
+city_paris,2002,MONTH_jan,0.06175621032714844
+city_paris,2003,MONTH_jan,0.059621820449829104
+city_paris,2004,MONTH_jan,0.05970314979553223
+city_paris,2005,MONTH_jan,0.05365334033966065
+city_paris,2006,MONTH_jan,0.059697318077087405
+city_paris,2007,MONTH_jan,0.06638713359832764
+city_paris,2008,MONTH_jan,0.07632267475128174
+city_paris,2009,MONTH_jan,0.0770644998550415
+city_paris,2010,MONTH_jan,0.08220292091369628
+city_paris,2011,MONTH_jan,0.0718551778793335
+city_paris,2012,MONTH_jan,0.0913736343383789
+city_paris,2013,MONTH_jan,0.10459434509277343
+city_paris,2014,MONTH_jan,0.1045927906036377
+city_paris,2015,MONTH_jan,0.10147398948669434
+city_paris,2016,MONTH_jan,0.1097524642944336
+city_paris,2017,MONTH_jan,0.10437264442443847
+city_paris,2018,MONTH_jan,0.12132494926452636
+city_paris,2019,MONTH_jan,0.12384904861450195
+city_paris,2020,MONTH_jan,0.1478874683380127
+city_paris,2021,MONTH_jan,0.13671010971069336
+city_berlin,1965,MONTH_jan,0.016145030260086058
+city_berlin,1966,MONTH_jan,0.01741612911224365
+city_berlin,1967,MONTH_jan,0.01658253788948059
+city_berlin,1968,MONTH_jan,0.01576139807701111
+city_berlin,1969,MONTH_jan,0.011857671737670899
+city_berlin,1970,MONTH_jan,0.015171291828155518
+city_berlin,1971,MONTH_jan,0.01179680585861206
+city_berlin,1972,MONTH_jan,0.011219959259033203
+city_berlin,1973,MONTH_jan,0.012137141227722168
+city_berlin,1974,MONTH_jan,0.014188613891601563
+city_berlin,1975,MONTH_jan,0.014353294372558594
+city_berlin,1976,MONTH_jan,0.011208523511886597
+city_berlin,1977,MONTH_jan,0.013967294692993164
+city_berlin,1978,MONTH_jan,0.01408961057662964
+city_berlin,1979,MONTH_jan,0.013454017639160156
+city_berlin,1980,MONTH_jan,0.01478190541267395
+city_berlin,1981,MONTH_jan,0.015739785432815553
+city_berlin,1982,MONTH_jan,0.01617851257324219
+city_berlin,1983,MONTH_jan,0.014992613792419434
+city_berlin,1984,MONTH_jan,0.0143718421459198
+city_berlin,1985,MONTH_jan,0.013216147422790528
+city_berlin,1986,MONTH_jan,0.014105298519134519
+city_berlin,1987,MONTH_jan,0.015094993114471435
+city_berlin,1988,MONTH_jan,0.015009332895278931
+city_berlin,1989,MONTH_jan,0.013991726636886597
+city_berlin,1990,MONTH_jan,0.013369400501251221
+city_berlin,1991,MONTH_jan,0.012709577083587647
+city_berlin,1992,MONTH_jan,0.015204551219940186
+city_berlin,1993,MONTH_jan,0.01583544135093689
+city_berlin,1994,MONTH_jan,0.01739633440971374
+city_berlin,1995,MONTH_jan,0.01889189839363098
+city_berlin,1996,MONTH_jan,0.016823945045471193
+city_berlin,1997,MONTH_jan,0.018035167455673216
+city_berlin,1998,MONTH_jan,0.019705649614334103
+city_berlin,1999,MONTH_jan,0.02227585792541504
+city_berlin,2000,MONTH_jan,0.028871116638183595
+city_berlin,2001,MONTH_jan,0.029411675930023192
+city_berlin,2002,MONTH_jan,0.03538631916046143
+city_berlin,2003,MONTH_jan,0.03641989707946778
+city_berlin,2004,MONTH_jan,0.04516597747802734
+city_berlin,2005,MONTH_jan,0.052084193229675294
+city_berlin,2006,MONTH_jan,0.060263047218322756
+city_berlin,2007,MONTH_jan,0.07653833389282226
+city_berlin,2008,MONTH_jan,0.07817628383636474
+city_berlin,2009,MONTH_jan,0.08454279899597168
+city_berlin,2010,MONTH_jan,0.08792469024658203
+city_berlin,2011,MONTH_jan,0.10566965103149414
+city_berlin,2012,MONTH_jan,0.11899699211120604
+city_berlin,2013,MONTH_jan,0.12063335418701172
+city_berlin,2014,MONTH_jan,0.13278846740722655
+city_berlin,2015,MONTH_jan,0.14823543548583984
+city_berlin,2016,MONTH_jan,0.14607312202453612
+city_berlin,2017,MONTH_jan,0.16196212768554688
+city_berlin,2018,MONTH_jan,0.17084951400756837
+city_berlin,2019,MONTH_jan,0.18768304824829102
+city_berlin,2020,MONTH_jan,0.21122827529907226
+city_berlin,2021,MONTH_jan,0.19453121185302735
+city_new_delhi,1965,MONTH_jan,0.09149455070495603
+city_mumbai,1965,MONTH_jan,0.09149455070495603
+city_new_delhi,1966,MONTH_jan,0.09270711898803713
+city_mumbai,1966,MONTH_jan,0.09270711898803713
+city_new_delhi,1967,MONTH_jan,0.10058605194091796
+city_mumbai,1967,MONTH_jan,0.10058605194091796
+city_new_delhi,1968,MONTH_jan,0.10857532501220703
+city_mumbai,1968,MONTH_jan,0.10857532501220703
+city_new_delhi,1969,MONTH_jan,0.10873967170715332
+city_mumbai,1969,MONTH_jan,0.10873967170715332
+city_new_delhi,1970,MONTH_jan,0.11784119606018066
+city_mumbai,1970,MONTH_jan,0.11784119606018066
+city_new_delhi,1971,MONTH_jan,0.1242677116394043
+city_mumbai,1971,MONTH_jan,0.1242677116394043
+city_new_delhi,1972,MONTH_jan,0.11463116645812989
+city_mumbai,1972,MONTH_jan,0.11463116645812989
+city_new_delhi,1973,MONTH_jan,0.11957463264465332
+city_mumbai,1973,MONTH_jan,0.11957463264465332
+city_new_delhi,1974,MONTH_jan,0.1086085319519043
+city_mumbai,1974,MONTH_jan,0.1086085319519043
+city_new_delhi,1975,MONTH_jan,0.12067704200744628
+city_mumbai,1975,MONTH_jan,0.12067704200744628
+city_new_delhi,1976,MONTH_jan,0.12036797523498535
+city_mumbai,1976,MONTH_jan,0.12036797523498535
+city_new_delhi,1977,MONTH_jan,0.12415875434875488
+city_mumbai,1977,MONTH_jan,0.12415875434875488
+city_new_delhi,1978,MONTH_jan,0.14818278312683103
+city_mumbai,1978,MONTH_jan,0.14818278312683103
+city_new_delhi,1979,MONTH_jan,0.13540278434753417
+city_mumbai,1979,MONTH_jan,0.13540278434753417
+city_new_delhi,1980,MONTH_jan,0.1345311737060547
+city_mumbai,1980,MONTH_jan,0.1345311737060547
+city_new_delhi,1981,MONTH_jan,0.12948171615600587
+city_mumbai,1981,MONTH_jan,0.12948171615600587
+city_new_delhi,1982,MONTH_jan,0.11035112380981443
+city_mumbai,1982,MONTH_jan,0.11035112380981443
+city_new_delhi,1983,MONTH_jan,0.10110920906066896
+city_mumbai,1983,MONTH_jan,0.10110920906066896
+city_new_delhi,1984,MONTH_jan,0.10576085090637209
+city_mumbai,1984,MONTH_jan,0.10576085090637209
+city_new_delhi,1985,MONTH_jan,0.09670783996582032
+city_mumbai,1985,MONTH_jan,0.09670783996582032
+city_new_delhi,1986,MONTH_jan,0.0916761875152588
+city_mumbai,1986,MONTH_jan,0.0916761875152588
+city_new_delhi,1987,MONTH_jan,0.08030695915222168
+city_mumbai,1987,MONTH_jan,0.08030695915222168
+city_new_delhi,1988,MONTH_jan,0.08229918479919433
+city_mumbai,1988,MONTH_jan,0.08229918479919433
+city_new_delhi,1989,MONTH_jan,0.08742303848266601
+city_mumbai,1989,MONTH_jan,0.08742303848266601
+city_new_delhi,1990,MONTH_jan,0.08542831420898438
+city_mumbai,1990,MONTH_jan,0.08542831420898438
+city_new_delhi,1991,MONTH_jan,0.09029864311218262
+city_mumbai,1991,MONTH_jan,0.09029864311218262
+city_new_delhi,1992,MONTH_jan,0.08140623092651367
+city_mumbai,1992,MONTH_jan,0.08140623092651367
+city_new_delhi,1993,MONTH_jan,0.08035937309265137
+city_mumbai,1993,MONTH_jan,0.08035937309265137
+city_new_delhi,1994,MONTH_jan,0.08667225837707519
+city_mumbai,1994,MONTH_jan,0.08667225837707519
+city_new_delhi,1995,MONTH_jan,0.07686767578125
+city_mumbai,1995,MONTH_jan,0.07686767578125
+city_new_delhi,1996,MONTH_jan,0.06789632797241212
+city_mumbai,1996,MONTH_jan,0.06789632797241212
+city_new_delhi,1997,MONTH_jan,0.06570899486541748
+city_mumbai,1997,MONTH_jan,0.06570899486541748
+city_new_delhi,1998,MONTH_jan,0.07381818294525147
+city_mumbai,1998,MONTH_jan,0.07381818294525147
+city_new_delhi,1999,MONTH_jan,0.0712459659576416
+city_mumbai,1999,MONTH_jan,0.0712459659576416
+city_new_delhi,2000,MONTH_jan,0.06376289367675782
+city_mumbai,2000,MONTH_jan,0.06376289367675782
+city_new_delhi,2001,MONTH_jan,0.0599252462387085
+city_mumbai,2001,MONTH_jan,0.0599252462387085
+city_new_delhi,2002,MONTH_jan,0.057318320274353025
+city_mumbai,2002,MONTH_jan,0.057318320274353025
+city_new_delhi,2003,MONTH_jan,0.05624061584472656
+city_mumbai,2003,MONTH_jan,0.05624061584472656
+city_new_delhi,2004,MONTH_jan,0.07460848808288574
+city_mumbai,2004,MONTH_jan,0.07460848808288574
+city_new_delhi,2005,MONTH_jan,0.06894316673278808
+city_mumbai,2005,MONTH_jan,0.06894316673278808
+city_new_delhi,2006,MONTH_jan,0.07706239700317383
+city_mumbai,2006,MONTH_jan,0.07706239700317383
+city_new_delhi,2007,MONTH_jan,0.07855644702911377
+city_mumbai,2007,MONTH_jan,0.07855644702911377
+city_new_delhi,2008,MONTH_jan,0.07257065296173096
+city_mumbai,2008,MONTH_jan,0.07257065296173096
+city_new_delhi,2009,MONTH_jan,0.06473570346832275
+city_mumbai,2009,MONTH_jan,0.06473570346832275
+city_new_delhi,2010,MONTH_jan,0.0658367109298706
+city_mumbai,2010,MONTH_jan,0.0658367109298706
+city_new_delhi,2011,MONTH_jan,0.075013427734375
+city_mumbai,2011,MONTH_jan,0.075013427734375
+city_new_delhi,2012,MONTH_jan,0.06727289199829102
+city_mumbai,2012,MONTH_jan,0.06727289199829102
+city_new_delhi,2013,MONTH_jan,0.07329665184020996
+city_mumbai,2013,MONTH_jan,0.07329665184020996
+city_new_delhi,2014,MONTH_jan,0.07344336032867432
+city_mumbai,2014,MONTH_jan,0.07344336032867432
+city_new_delhi,2015,MONTH_jan,0.07184645652770996
+city_mumbai,2015,MONTH_jan,0.07184645652770996
+city_new_delhi,2016,MONTH_jan,0.07011397361755371
+city_mumbai,2016,MONTH_jan,0.07011397361755371
+city_new_delhi,2017,MONTH_jan,0.07511875152587891
+city_mumbai,2017,MONTH_jan,0.07511875152587891
+city_new_delhi,2018,MONTH_jan,0.07912805557250976
+city_mumbai,2018,MONTH_jan,0.07912805557250976
+city_new_delhi,2019,MONTH_jan,0.08849618911743164
+city_mumbai,2019,MONTH_jan,0.08849618911743164
+city_new_delhi,2020,MONTH_jan,0.09730036735534668
+city_mumbai,2020,MONTH_jan,0.09730036735534668
+city_new_delhi,2021,MONTH_jan,0.09310850143432615
+city_mumbai,2021,MONTH_jan,0.09310850143432615
+city_jakarta,1965,MONTH_jan,0.06385928153991699
+city_jakarta,1966,MONTH_jan,0.08364607810974121
+city_jakarta,1967,MONTH_jan,0.09505834579467771
+city_jakarta,1968,MONTH_jan,0.1101013946533203
+city_jakarta,1969,MONTH_jan,0.10201886177062988
+city_jakarta,1970,MONTH_jan,0.11570672035217283
+city_jakarta,1971,MONTH_jan,0.04001150131225586
+city_jakarta,1972,MONTH_jan,0.03373836040496826
+city_jakarta,1973,MONTH_jan,0.037743794918060306
+city_jakarta,1974,MONTH_jan,0.039022941589355466
+city_jakarta,1975,MONTH_jan,0.03342118501663208
+city_jakarta,1976,MONTH_jan,0.018672399520874024
+city_jakarta,1977,MONTH_jan,0.023478298187255858
+city_jakarta,1978,MONTH_jan,0.014906297922134399
+city_jakarta,1979,MONTH_jan,0.014562846422195437
+city_jakarta,1980,MONTH_jan,0.012272299528121948
+city_jakarta,1981,MONTH_jan,0.011406815052032471
+city_jakarta,1982,MONTH_jan,0.012204515933990481
+city_jakarta,1983,MONTH_jan,0.017323704957962035
+city_jakarta,1984,MONTH_jan,0.017180938720703125
+city_jakarta,1985,MONTH_jan,0.01980858683586121
+city_jakarta,1986,MONTH_jan,0.02986825704574585
+city_jakarta,1987,MONTH_jan,0.029112305641174317
+city_jakarta,1988,MONTH_jan,0.0302899694442749
+city_jakarta,1989,MONTH_jan,0.03408119440078736
+city_jakarta,1990,MONTH_jan,0.03731863260269165
+city_jakarta,1991,MONTH_jan,0.03812469959259033
+city_jakarta,1992,MONTH_jan,0.044085426330566405
+city_jakarta,1993,MONTH_jan,0.03847653388977051
+city_jakarta,1994,MONTH_jan,0.03521223306655884
+city_jakarta,1995,MONTH_jan,0.035392696857452395
+city_jakarta,1996,MONTH_jan,0.03508815288543701
+city_jakarta,1997,MONTH_jan,0.02477278470993042
+city_jakarta,1998,MONTH_jan,0.04014904499053955
+city_jakarta,1999,MONTH_jan,0.03852458000183105
+city_jakarta,2000,MONTH_jan,0.03733640432357788
+city_jakarta,2001,MONTH_jan,0.04112386703491211
+city_jakarta,2002,MONTH_jan,0.03683813571929931
+city_jakarta,2003,MONTH_jan,0.03221094608306885
+city_jakarta,2004,MONTH_jan,0.034259636402130124
+city_jakarta,2005,MONTH_jan,0.035274262428283694
+city_jakarta,2006,MONTH_jan,0.03220308542251587
+city_jakarta,2007,MONTH_jan,0.03370551109313965
+city_jakarta,2008,MONTH_jan,0.03586532115936279
+city_jakarta,2009,MONTH_jan,0.03655174016952514
+city_jakarta,2010,MONTH_jan,0.043262209892272946
+city_jakarta,2011,MONTH_jan,0.03388597726821899
+city_jakarta,2012,MONTH_jan,0.034713633060455315
+city_jakarta,2013,MONTH_jan,0.0433060359954834
+city_jakarta,2014,MONTH_jan,0.044786324501037596
+city_jakarta,2015,MONTH_jan,0.037929575443267825
+city_jakarta,2016,MONTH_jan,0.05479020118713379
+city_jakarta,2017,MONTH_jan,0.05283773899078369
+city_jakarta,2018,MONTH_jan,0.07393457889556884
+city_jakarta,2019,MONTH_jan,0.07870308876037597
+city_jakarta,2020,MONTH_jan,0.09860146522521973
+city_jakarta,2021,MONTH_jan,0.10385714530944824
+city_tehran,1965,MONTH_jan,0.052250761985778806
+city_tehran,1966,MONTH_jan,0.05151486396789551
+city_tehran,1967,MONTH_jan,0.050412230491638184
+city_tehran,1968,MONTH_jan,0.04931734085083008
+city_tehran,1969,MONTH_jan,0.04837111473083496
+city_tehran,1970,MONTH_jan,0.04415298461914063
+city_tehran,1971,MONTH_jan,0.04332554340362549
+city_tehran,1972,MONTH_jan,0.0501894760131836
+city_tehran,1973,MONTH_jan,0.03557704448699951
+city_tehran,1974,MONTH_jan,0.03455620527267456
+city_tehran,1975,MONTH_jan,0.031140482425689696
+city_tehran,1976,MONTH_jan,0.0309524917602539
+city_tehran,1977,MONTH_jan,0.02825014591217041
+city_tehran,1978,MONTH_jan,0.04526003360748291
+city_tehran,1979,MONTH_jan,0.036342711448669435
+city_tehran,1980,MONTH_jan,0.04064725875854492
+city_tehran,1981,MONTH_jan,0.044219164848327636
+city_tehran,1982,MONTH_jan,0.04111237049102783
+city_tehran,1983,MONTH_jan,0.033884649276733396
+city_tehran,1984,MONTH_jan,0.029317567348480223
+city_tehran,1985,MONTH_jan,0.027088303565979004
+city_tehran,1986,MONTH_jan,0.03455609560012817
+city_tehran,1987,MONTH_jan,0.03573478698730469
+city_tehran,1988,MONTH_jan,0.036968004703521726
+city_tehran,1989,MONTH_jan,0.027610442638397216
+city_tehran,1990,MONTH_jan,0.026902496814727783
+city_tehran,1991,MONTH_jan,0.018835831880569455
+city_tehran,1992,MONTH_jan,0.02795917510986328
+city_tehran,1993,MONTH_jan,0.034577884674072266
+city_tehran,1994,MONTH_jan,0.020214617252349857
+city_tehran,1995,MONTH_jan,0.023263061046600343
+city_tehran,1996,MONTH_jan,0.022645998001098632
+city_tehran,1997,MONTH_jan,0.014810699224472045
+city_tehran,1998,MONTH_jan,0.017921339273452762
+city_tehran,1999,MONTH_jan,0.011498689651489258
+city_tehran,2000,MONTH_jan,0.00808289110660553
+city_tehran,2001,MONTH_jan,0.008181908130645753
+city_tehran,2002,MONTH_jan,0.014433478116989135
+city_tehran,2003,MONTH_jan,0.016837654113769532
+city_tehran,2004,MONTH_jan,0.01884332299232483
+city_tehran,2005,MONTH_jan,0.021426386833190918
+city_tehran,2006,MONTH_jan,0.025007379055023194
+city_tehran,2007,MONTH_jan,0.022706358432769774
+city_tehran,2008,MONTH_jan,0.009235934615135192
+city_tehran,2009,MONTH_jan,0.008136967420578003
+city_tehran,2010,MONTH_jan,0.011950724124908448
+city_tehran,2011,MONTH_jan,0.011898080110549929
+city_tehran,2012,MONTH_jan,0.013722978830337525
+city_tehran,2013,MONTH_jan,0.01525709867477417
+city_tehran,2014,MONTH_jan,0.014402105808258056
+city_tehran,2015,MONTH_jan,0.013088065385818479
+city_tehran,2016,MONTH_jan,0.014465060234069824
+city_tehran,2017,MONTH_jan,0.015620874166488647
+city_tehran,2018,MONTH_jan,0.009227302074432371
+city_tehran,2019,MONTH_jan,0.028368299007415772
+city_tehran,2020,MONTH_jan,0.019643505811691286
+city_tehran,2021,MONTH_jan,0.012903937101364137
+city_baghdad,1965,MONTH_jan,0.009088937044143675
+city_baghdad,1966,MONTH_jan,0.010816304683685305
+city_baghdad,1967,MONTH_jan,0.012241523265838623
+city_baghdad,1968,MONTH_jan,0.013923404216766355
+city_baghdad,1969,MONTH_jan,0.013289313316345218
+city_baghdad,1970,MONTH_jan,0.01301562786102295
+city_baghdad,1971,MONTH_jan,0.013798673152923584
+city_baghdad,1972,MONTH_jan,0.011780089139938355
+city_baghdad,1973,MONTH_jan,0.01737240195274353
+city_baghdad,1974,MONTH_jan,0.02446626663208008
+city_baghdad,1975,MONTH_jan,0.02579850673675537
+city_baghdad,1976,MONTH_jan,0.020427801609039307
+city_baghdad,1977,MONTH_jan,0.02406090497970581
+city_baghdad,1978,MONTH_jan,0.03056793212890625
+city_baghdad,1979,MONTH_jan,0.03528018236160278
+city_baghdad,1980,MONTH_jan,0.020129208564758302
+city_baghdad,1981,MONTH_jan,0.019036805629730223
+city_baghdad,1982,MONTH_jan,0.01808008313179016
+city_baghdad,1983,MONTH_jan,0.01574033737182617
+city_baghdad,1984,MONTH_jan,0.014442424774169921
+city_baghdad,1985,MONTH_jan,0.012590632438659669
+city_baghdad,1986,MONTH_jan,0.010752139091491699
+city_baghdad,1987,MONTH_jan,0.03949446678161621
+city_baghdad,1988,MONTH_jan,0.03410572290420532
+city_baghdad,1989,MONTH_jan,0.030038673877716065
+city_baghdad,1990,MONTH_jan,0.0578435230255127
+city_baghdad,1991,MONTH_jan,0.0428729248046875
+city_baghdad,1992,MONTH_jan,0.06584853172302246
+city_baghdad,1993,MONTH_jan,0.058610520362854
+city_baghdad,1994,MONTH_jan,0.05243114471435547
+city_baghdad,1995,MONTH_jan,0.05992914199829102
+city_baghdad,1996,MONTH_jan,0.054542551040649416
+city_baghdad,1997,MONTH_jan,0.046753549575805665
+city_baghdad,1998,MONTH_jan,0.05869803905487061
+city_baghdad,1999,MONTH_jan,0.04700111389160156
+city_baghdad,2000,MONTH_jan,0.029294536113739018
+city_baghdad,2001,MONTH_jan,0.03016484260559082
+city_baghdad,2002,MONTH_jan,0.03885046482086182
+city_baghdad,2003,MONTH_jan,0.04535982608795166
+city_baghdad,2004,MONTH_jan,0.05081915855407715
+city_baghdad,2005,MONTH_jan,0.04544925212860108
+city_baghdad,2006,MONTH_jan,0.0473859977722168
+city_baghdad,2007,MONTH_jan,0.042728943824768065
+city_baghdad,2008,MONTH_jan,0.02381868362426758
+city_baghdad,2009,MONTH_jan,0.023280267715454103
+city_baghdad,2010,MONTH_jan,0.026375570297241212
+city_baghdad,2011,MONTH_jan,0.023962152004241944
+city_baghdad,2012,MONTH_jan,0.02823270320892334
+city_baghdad,2013,MONTH_jan,0.026760001182556153
+city_baghdad,2014,MONTH_jan,0.01738718867301941
+city_baghdad,2015,MONTH_jan,0.016102989912033083
+city_baghdad,2016,MONTH_jan,0.01836539387702942
+city_baghdad,2017,MONTH_jan,0.011253452301025391
+city_baghdad,2018,MONTH_jan,0.009119535088539124
+city_baghdad,2019,MONTH_jan,0.022772881984710693
+city_baghdad,2020,MONTH_jan,0.016468850374221803
+city_baghdad,2021,MONTH_jan,0.015024909973144531
+city_tokyo,1965,MONTH_jan,0.12335086822509765
+city_tokyo,1966,MONTH_jan,0.11651081085205078
+city_tokyo,1967,MONTH_jan,0.08793859481811524
+city_tokyo,1968,MONTH_jan,0.08451984405517578
+city_tokyo,1969,MONTH_jan,0.0761188268661499
+city_tokyo,1970,MONTH_jan,0.06836434364318848
+city_tokyo,1971,MONTH_jan,0.07166181564331055
+city_tokyo,1972,MONTH_jan,0.06914736270904541
+city_tokyo,1973,MONTH_jan,0.04894681930541992
+city_tokyo,1974,MONTH_jan,0.060309801101684574
+city_tokyo,1975,MONTH_jan,0.06373206615447997
+city_tokyo,1976,MONTH_jan,0.06161350250244141
+city_tokyo,1977,MONTH_jan,0.05355945110321045
+city_tokyo,1978,MONTH_jan,0.049568381309509274
+city_tokyo,1979,MONTH_jan,0.055059475898742674
+city_tokyo,1980,MONTH_jan,0.06194887161254883
+city_tokyo,1981,MONTH_jan,0.062369403839111326
+city_tokyo,1982,MONTH_jan,0.06735273361206055
+city_tokyo,1983,MONTH_jan,0.06926650524139405
+city_tokyo,1984,MONTH_jan,0.05621933937072754
+city_tokyo,1985,MONTH_jan,0.06361672401428223
+city_tokyo,1986,MONTH_jan,0.06296619892120361
+city_tokyo,1987,MONTH_jan,0.058519330024719235
+city_tokyo,1988,MONTH_jan,0.06523026466369629
+city_tokyo,1989,MONTH_jan,0.06458432197570801
+city_tokyo,1990,MONTH_jan,0.05605645179748535
+city_tokyo,1991,MONTH_jan,0.059171295166015624
+city_tokyo,1992,MONTH_jan,0.050801749229431155
+city_tokyo,1993,MONTH_jan,0.05666120529174805
+city_tokyo,1994,MONTH_jan,0.04097033977508545
+city_tokyo,1995,MONTH_jan,0.04652009010314941
+city_tokyo,1996,MONTH_jan,0.045326762199401856
+city_tokyo,1997,MONTH_jan,0.04951632499694824
+city_tokyo,1998,MONTH_jan,0.05170876026153565
+city_tokyo,1999,MONTH_jan,0.048562874794006346
+city_tokyo,2000,MONTH_jan,0.0482745361328125
+city_tokyo,2001,MONTH_jan,0.047163658142089844
+city_tokyo,2002,MONTH_jan,0.04702637195587158
+city_tokyo,2003,MONTH_jan,0.05332041263580322
+city_tokyo,2004,MONTH_jan,0.052576699256896973
+city_tokyo,2005,MONTH_jan,0.04790322780609131
+city_tokyo,2006,MONTH_jan,0.053090085983276365
+city_tokyo,2007,MONTH_jan,0.04761953353881836
+city_tokyo,2008,MONTH_jan,0.04830859184265137
+city_tokyo,2009,MONTH_jan,0.05021396160125732
+city_tokyo,2010,MONTH_jan,0.05684884071350098
+city_tokyo,2011,MONTH_jan,0.057331571578979494
+city_tokyo,2012,MONTH_jan,0.05650204658508301
+city_tokyo,2013,MONTH_jan,0.061299676895141604
+city_tokyo,2014,MONTH_jan,0.06935917377471924
+city_tokyo,2015,MONTH_jan,0.0806879711151123
+city_tokyo,2016,MONTH_jan,0.07975860595703126
+city_tokyo,2017,MONTH_jan,0.08634003639221191
+city_tokyo,2018,MONTH_jan,0.09322304725646972
+city_tokyo,2019,MONTH_jan,0.09565926551818847
+city_tokyo,2020,MONTH_jan,0.11155339241027833
+city_tokyo,2021,MONTH_jan,0.11428995132446287
+city_mexico_city,1965,MONTH_jan,0.08934722900390625
+city_mexico_city,1966,MONTH_jan,0.09633110046386721
+city_mexico_city,1967,MONTH_jan,0.1029133129119873
+city_mexico_city,1968,MONTH_jan,0.1084283447265625
+city_mexico_city,1969,MONTH_jan,0.10475299835205078
+city_mexico_city,1970,MONTH_jan,0.1098171329498291
+city_mexico_city,1971,MONTH_jan,0.10142724990844729
+city_mexico_city,1972,MONTH_jan,0.09780027389526368
+city_mexico_city,1973,MONTH_jan,0.09648298263549805
+city_mexico_city,1974,MONTH_jan,0.09094654083251952
+city_mexico_city,1975,MONTH_jan,0.07784452438354492
+city_mexico_city,1976,MONTH_jan,0.08308449745178223
+city_mexico_city,1977,MONTH_jan,0.08738277435302734
+city_mexico_city,1978,MONTH_jan,0.0659796380996704
+city_mexico_city,1979,MONTH_jan,0.06747371673583985
+city_mexico_city,1980,MONTH_jan,0.058035578727722165
+city_mexico_city,1981,MONTH_jan,0.07537362575531006
+city_mexico_city,1982,MONTH_jan,0.06843096733093262
+city_mexico_city,1983,MONTH_jan,0.06308685779571534
+city_mexico_city,1984,MONTH_jan,0.0683129644393921
+city_mexico_city,1985,MONTH_jan,0.07820509910583497
+city_mexico_city,1986,MONTH_jan,0.0659659194946289
+city_mexico_city,1987,MONTH_jan,0.06118594646453857
+city_mexico_city,1988,MONTH_jan,0.06784612655639649
+city_mexico_city,1989,MONTH_jan,0.07293380260467529
+city_mexico_city,1990,MONTH_jan,0.06830843448638917
+city_mexico_city,1991,MONTH_jan,0.06324604988098144
+city_mexico_city,1992,MONTH_jan,0.0725102186203003
+city_mexico_city,1993,MONTH_jan,0.07209392070770264
+city_mexico_city,1994,MONTH_jan,0.05448117256164551
+city_mexico_city,1995,MONTH_jan,0.07110982418060302
+city_mexico_city,1996,MONTH_jan,0.0760121488571167
+city_mexico_city,1997,MONTH_jan,0.06423584938049316
+city_mexico_city,1998,MONTH_jan,0.05862017154693604
+city_mexico_city,1999,MONTH_jan,0.07305535793304443
+city_mexico_city,2000,MONTH_jan,0.0715089750289917
+city_mexico_city,2001,MONTH_jan,0.06339307308197022
+city_mexico_city,2002,MONTH_jan,0.05449038505554199
+city_mexico_city,2003,MONTH_jan,0.046524553298950194
+city_mexico_city,2004,MONTH_jan,0.053032422065734865
+city_mexico_city,2005,MONTH_jan,0.05473337173461914
+city_mexico_city,2006,MONTH_jan,0.055860862731933594
+city_mexico_city,2007,MONTH_jan,0.052095470428466795
+city_mexico_city,2008,MONTH_jan,0.06696115970611573
+city_mexico_city,2009,MONTH_jan,0.04956578254699707
+city_mexico_city,2010,MONTH_jan,0.06283123970031738
+city_mexico_city,2011,MONTH_jan,0.058868541717529296
+city_mexico_city,2012,MONTH_jan,0.054620795249938965
+city_mexico_city,2013,MONTH_jan,0.05060070037841797
+city_mexico_city,2014,MONTH_jan,0.06727101802825927
+city_mexico_city,2015,MONTH_jan,0.060522632598876955
+city_mexico_city,2016,MONTH_jan,0.06131492614746094
+city_mexico_city,2017,MONTH_jan,0.06372722148895264
+city_mexico_city,2018,MONTH_jan,0.06939361095428467
+city_mexico_city,2019,MONTH_jan,0.0705077075958252
+city_mexico_city,2020,MONTH_jan,0.09475372314453123
+city_mexico_city,2021,MONTH_jan,0.1051475429534912
+city_rabat,1965,MONTH_jan,0.2022709846496582
+city_rabat,1966,MONTH_jan,0.1334361457824707
+city_rabat,1967,MONTH_jan,0.11197633743286133
+city_rabat,1968,MONTH_jan,0.11839537620544434
+city_rabat,1969,MONTH_jan,0.13958054542541504
+city_rabat,1970,MONTH_jan,0.13115361213684082
+city_rabat,1971,MONTH_jan,0.1392387866973877
+city_rabat,1972,MONTH_jan,0.13526378631591796
+city_rabat,1973,MONTH_jan,0.09094624519348145
+city_rabat,1974,MONTH_jan,0.09542439460754397
+city_rabat,1975,MONTH_jan,0.0713439130783081
+city_rabat,1976,MONTH_jan,0.06466088294982911
+city_rabat,1977,MONTH_jan,0.07884840011596679
+city_rabat,1978,MONTH_jan,0.0770575761795044
+city_rabat,1979,MONTH_jan,0.0771904230117798
+city_rabat,1980,MONTH_jan,0.07371124744415283
+city_rabat,1981,MONTH_jan,0.05136460304260254
+city_rabat,1982,MONTH_jan,0.027842175960540772
+city_rabat,1983,MONTH_jan,0.02280400514602661
+city_rabat,1984,MONTH_jan,0.01712327241897583
+city_rabat,1985,MONTH_jan,0.02200667381286621
+city_rabat,1986,MONTH_jan,0.028153402805328367
+city_rabat,1987,MONTH_jan,0.03506812572479248
+city_rabat,1988,MONTH_jan,0.037663094997406006
+city_rabat,1989,MONTH_jan,0.04231739044189453
+city_rabat,1990,MONTH_jan,0.04359245777130127
+city_rabat,1991,MONTH_jan,0.04325900077819824
+city_rabat,1992,MONTH_jan,0.030396227836608888
+city_rabat,1993,MONTH_jan,0.013822969198226929
+city_rabat,1994,MONTH_jan,0.023779470920562745
+city_rabat,1995,MONTH_jan,0.01763482928276062
+city_rabat,1996,MONTH_jan,0.05402516841888428
+city_rabat,1997,MONTH_jan,0.05531386375427246
+city_rabat,1998,MONTH_jan,0.04600728511810303
+city_rabat,1999,MONTH_jan,0.020588018894195557
+city_rabat,2000,MONTH_jan,0.019685844182968138
+city_rabat,2001,MONTH_jan,0.024725899696350098
+city_rabat,2002,MONTH_jan,0.02324991226196289
+city_rabat,2003,MONTH_jan,0.03657069444656372
+city_rabat,2004,MONTH_jan,0.03473497629165649
+city_rabat,2005,MONTH_jan,0.020816917419433593
+city_rabat,2006,MONTH_jan,0.020182490348815918
+city_rabat,2007,MONTH_jan,0.02050191640853882
+city_rabat,2008,MONTH_jan,0.018975967168807985
+city_rabat,2009,MONTH_jan,0.04711688995361328
+city_rabat,2010,MONTH_jan,0.05887355327606201
+city_rabat,2011,MONTH_jan,0.0364383602142334
+city_rabat,2012,MONTH_jan,0.031137833595275877
+city_rabat,2013,MONTH_jan,0.05110578060150146
+city_rabat,2014,MONTH_jan,0.04458736419677734
+city_rabat,2015,MONTH_jan,0.053888487815856936
+city_rabat,2016,MONTH_jan,0.05595184803009033
+city_rabat,2017,MONTH_jan,0.05286826610565185
+city_rabat,2018,MONTH_jan,0.0710693359375
+city_rabat,2019,MONTH_jan,0.07547047138214111
+city_rabat,2020,MONTH_jan,0.07633952140808105
+city_rabat,2021,MONTH_jan,0.0763616943359375
+city_amsterdam,1965,MONTH_jan,0
+city_amsterdam,1966,MONTH_jan,0
+city_amsterdam,1967,MONTH_jan,0
+city_amsterdam,1968,MONTH_jan,0
+city_amsterdam,1969,MONTH_jan,0
+city_amsterdam,1970,MONTH_jan,0
+city_amsterdam,1971,MONTH_jan,0
+city_amsterdam,1972,MONTH_jan,0
+city_amsterdam,1973,MONTH_jan,0
+city_amsterdam,1974,MONTH_jan,0
+city_amsterdam,1975,MONTH_jan,0.0031152141094207765
+city_amsterdam,1976,MONTH_jan,0.0031017714738845823
+city_amsterdam,1977,MONTH_jan,0.0031403565406799318
+city_amsterdam,1978,MONTH_jan,0.004299201965332031
+city_amsterdam,1979,MONTH_jan,0.0038009288907051085
+city_amsterdam,1980,MONTH_jan,0.0037478047609329226
+city_amsterdam,1981,MONTH_jan,0.004187572598457336
+city_amsterdam,1982,MONTH_jan,0.0011066412925720215
+city_amsterdam,1983,MONTH_jan,9.600715339183807e-4
+city_amsterdam,1984,MONTH_jan,6.805820390582081e-5
+city_amsterdam,1985,MONTH_jan,0.0017216096818447114
+city_amsterdam,1986,MONTH_jan,0.002057678997516632
+city_amsterdam,1987,MONTH_jan,0.002552417516708374
+city_amsterdam,1988,MONTH_jan,0.003074186444282532
+city_amsterdam,1989,MONTH_jan,0.003433368802070618
+city_amsterdam,1990,MONTH_jan,0.0028062096238136293
+city_amsterdam,1991,MONTH_jan,0.003191479742527008
+city_amsterdam,1992,MONTH_jan,0.0033999988436698913
+city_amsterdam,1993,MONTH_jan,0.0037428748607635496
+city_amsterdam,1994,MONTH_jan,0.00426658183336258
+city_amsterdam,1995,MONTH_jan,0.0045612013339996335
+city_amsterdam,1996,MONTH_jan,0.0056391310691833495
+city_amsterdam,1997,MONTH_jan,0.0063600361347198485
+city_amsterdam,1998,MONTH_jan,0.007268246412277222
+city_amsterdam,1999,MONTH_jan,0.007947908639907837
+city_amsterdam,2000,MONTH_jan,0.00925859272480011
+city_amsterdam,2001,MONTH_jan,0.009883764982223512
+city_amsterdam,2002,MONTH_jan,0.011863465309143067
+city_amsterdam,2003,MONTH_jan,0.011635091304779053
+city_amsterdam,2004,MONTH_jan,0.015031250715255734
+city_amsterdam,2005,MONTH_jan,0.020780501365661622
+city_amsterdam,2006,MONTH_jan,0.023150839805603028
+city_amsterdam,2007,MONTH_jan,0.024174244403839112
+city_amsterdam,2008,MONTH_jan,0.02913216352462769
+city_amsterdam,2009,MONTH_jan,0.034302778244018554
+city_amsterdam,2010,MONTH_jan,0.032134413719177246
+city_amsterdam,2011,MONTH_jan,0.03811289310455322
+city_amsterdam,2012,MONTH_jan,0.03963631391525269
+city_amsterdam,2013,MONTH_jan,0.03945788860321045
+city_amsterdam,2014,MONTH_jan,0.03978432416915893
+city_amsterdam,2015,MONTH_jan,0.04503983974456787
+city_amsterdam,2016,MONTH_jan,0.04676411151885986
+city_amsterdam,2017,MONTH_jan,0.05489981651306152
+city_amsterdam,2018,MONTH_jan,0.059912681579589844
+city_amsterdam,2019,MONTH_jan,0.07302987575531006
+city_amsterdam,2020,MONTH_jan,0.10758957862854004
+city_amsterdam,2021,MONTH_jan,0.12373795509338381
+city_islamabad,1965,MONTH_jan,0.07898932456970215
+city_islamabad,1966,MONTH_jan,0.07840171337127684
+city_islamabad,1967,MONTH_jan,0.0839201831817627
+city_islamabad,1968,MONTH_jan,0.08885177612304687
+city_islamabad,1969,MONTH_jan,0.09329750061035157
+city_islamabad,1970,MONTH_jan,0.08390380859375
+city_islamabad,1971,MONTH_jan,0.1171886157989502
+city_islamabad,1972,MONTH_jan,0.1325389575958252
+city_islamabad,1973,MONTH_jan,0.1308777618408203
+city_islamabad,1974,MONTH_jan,0.12144384384155271
+city_islamabad,1975,MONTH_jan,0.13310853958129884
+city_islamabad,1976,MONTH_jan,0.1423151779174805
+city_islamabad,1977,MONTH_jan,0.1562037467956543
+city_islamabad,1978,MONTH_jan,0.18110393524169918
+city_islamabad,1979,MONTH_jan,0.17961801528930665
+city_islamabad,1980,MONTH_jan,0.16982366561889647
+city_islamabad,1981,MONTH_jan,0.16494283676147461
+city_islamabad,1982,MONTH_jan,0.1682915496826172
+city_islamabad,1983,MONTH_jan,0.18234487533569335
+city_islamabad,1984,MONTH_jan,0.1785936164855957
+city_islamabad,1985,MONTH_jan,0.17652673721313478
+city_islamabad,1986,MONTH_jan,0.1592543983459473
+city_islamabad,1987,MONTH_jan,0.1866611099243164
+city_islamabad,1988,MONTH_jan,0.1841936492919922
+city_islamabad,1989,MONTH_jan,0.17649538040161133
+city_islamabad,1990,MONTH_jan,0.16537376403808593
+city_islamabad,1991,MONTH_jan,0.16701480865478516
+city_islamabad,1992,MONTH_jan,0.17344545364379882
+city_islamabad,1993,MONTH_jan,0.17234846115112304
+city_islamabad,1994,MONTH_jan,0.16347652435302734
+city_islamabad,1995,MONTH_jan,0.16378124237060546
+city_islamabad,1996,MONTH_jan,0.1676286506652832
+city_islamabad,1997,MONTH_jan,0.1289863395690918
+city_islamabad,1998,MONTH_jan,0.15707118988037108
+city_islamabad,1999,MONTH_jan,0.13423368453979492
+city_islamabad,2000,MONTH_jan,0.11344158172607421
+city_islamabad,2001,MONTH_jan,0.1141504955291748
+city_islamabad,2002,MONTH_jan,0.12188603401184082
+city_islamabad,2003,MONTH_jan,0.13333745002746583
+city_islamabad,2004,MONTH_jan,0.12676342964172363
+city_islamabad,2005,MONTH_jan,0.1359751033782959
+city_islamabad,2006,MONTH_jan,0.12633492469787597
+city_islamabad,2007,MONTH_jan,0.12365367889404297
+city_islamabad,2008,MONTH_jan,0.10553434371948242
+city_islamabad,2009,MONTH_jan,0.10885856628417968
+city_islamabad,2010,MONTH_jan,0.11174628257751465
+city_islamabad,2011,MONTH_jan,0.1141127872467041
+city_islamabad,2012,MONTH_jan,0.04317787647247315
+city_islamabad,2013,MONTH_jan,0.11522516250610351
+city_islamabad,2014,MONTH_jan,0.11496394157409667
+city_islamabad,2015,MONTH_jan,0.11174578666687011
+city_islamabad,2016,MONTH_jan,0.11086973190307615
+city_islamabad,2017,MONTH_jan,0.09674949645996093
+city_islamabad,2018,MONTH_jan,0.09416380882263184
+city_islamabad,2019,MONTH_jan,0.11015737533569336
+city_islamabad,2020,MONTH_jan,0.12021024703979492
+city_islamabad,2021,MONTH_jan,0.10622897148132324
+city_lima,1965,MONTH_jan,0.1374032688140869
+city_lima,1966,MONTH_jan,0.12172947883605956
+city_lima,1967,MONTH_jan,0.13394146919250488
+city_lima,1968,MONTH_jan,0.1411124134063721
+city_lima,1969,MONTH_jan,0.1524188995361328
+city_lima,1970,MONTH_jan,0.14120806694030763
+city_lima,1971,MONTH_jan,0.17272397994995117
+city_lima,1972,MONTH_jan,0.19887008666992187
+city_lima,1973,MONTH_jan,0.1857759666442871
+city_lima,1974,MONTH_jan,0.17592985153198243
+city_lima,1975,MONTH_jan,0.1786097526550293
+city_lima,1976,MONTH_jan,0.18501680374145507
+city_lima,1977,MONTH_jan,0.1907871437072754
+city_lima,1978,MONTH_jan,0.19592239379882812
+city_lima,1979,MONTH_jan,0.20157331466674805
+city_lima,1980,MONTH_jan,0.19488487243652344
+city_lima,1981,MONTH_jan,0.21093349456787108
+city_lima,1982,MONTH_jan,0.22348915100097655
+city_lima,1983,MONTH_jan,0.2438044166564942
+city_lima,1984,MONTH_jan,0.24780765533447266
+city_lima,1985,MONTH_jan,0.2695251083374023
+city_lima,1986,MONTH_jan,0.269285831451416
+city_lima,1987,MONTH_jan,0.2701639747619629
+city_lima,1988,MONTH_jan,0.2654584693908691
+city_lima,1989,MONTH_jan,0.2911813545227051
+city_lima,1990,MONTH_jan,0.2930202865600586
+city_lima,1991,MONTH_jan,0.32345062255859375
+city_lima,1992,MONTH_jan,0.2806740951538086
+city_lima,1993,MONTH_jan,0.3119631767272949
+city_lima,1994,MONTH_jan,0.309783992767334
+city_lima,1995,MONTH_jan,0.28913646697998047
+city_lima,1996,MONTH_jan,0.2877692604064942
+city_lima,1997,MONTH_jan,0.28343660354614253
+city_lima,1998,MONTH_jan,0.2941726493835449
+city_lima,1999,MONTH_jan,0.301059398651123
+city_lima,2000,MONTH_jan,0.3249596405029297
+city_lima,2001,MONTH_jan,0.3552646255493164
+city_lima,2002,MONTH_jan,0.3535195541381836
+city_lima,2003,MONTH_jan,0.3660024261474609
+city_lima,2004,MONTH_jan,0.3229750442504883
+city_lima,2005,MONTH_jan,0.3184630966186523
+city_lima,2006,MONTH_jan,0.34128055572509763
+city_lima,2007,MONTH_jan,0.3103961563110352
+city_lima,2008,MONTH_jan,0.28021553039550784
+city_lima,2009,MONTH_jan,0.2833369064331055
+city_lima,2010,MONTH_jan,0.25213640213012695
+city_lima,2011,MONTH_jan,0.24354307174682618
+city_lima,2012,MONTH_jan,0.2401105308532715
+city_lima,2013,MONTH_jan,0.24136716842651368
+city_lima,2014,MONTH_jan,0.23940710067749024
+city_lima,2015,MONTH_jan,0.24024496078491211
+city_lima,2016,MONTH_jan,0.2257667922973633
+city_lima,2017,MONTH_jan,0.2617181777954102
+city_lima,2018,MONTH_jan,0.26795402526855466
+city_lima,2019,MONTH_jan,0.2662133598327637
+city_lima,2020,MONTH_jan,0.30744228363037107
+city_lima,2021,MONTH_jan,0.27741008758544916
+city_bucharest,1965,MONTH_jan,0.01066218614578247
+city_bucharest,1966,MONTH_jan,0.0102953040599823
+city_bucharest,1967,MONTH_jan,0.01315669298171997
+city_bucharest,1968,MONTH_jan,0.01308521866798401
+city_bucharest,1969,MONTH_jan,0.01615439534187317
+city_bucharest,1970,MONTH_jan,0.01898038387298584
+city_bucharest,1971,MONTH_jan,0.028985319137573243
+city_bucharest,1972,MONTH_jan,0.0449291467666626
+city_bucharest,1973,MONTH_jan,0.04244083404541016
+city_bucharest,1974,MONTH_jan,0.04733458995819092
+city_bucharest,1975,MONTH_jan,0.04793048858642578
+city_bucharest,1976,MONTH_jan,0.03851948022842407
+city_bucharest,1977,MONTH_jan,0.041872053146362304
+city_bucharest,1978,MONTH_jan,0.04415801525115967
+city_bucharest,1979,MONTH_jan,0.04625902652740479
+city_bucharest,1980,MONTH_jan,0.05105916500091553
+city_bucharest,1981,MONTH_jan,0.051341099739074705
+city_bucharest,1982,MONTH_jan,0.04819664478302002
+city_bucharest,1983,MONTH_jan,0.041099562644958496
+city_bucharest,1984,MONTH_jan,0.04670980453491211
+city_bucharest,1985,MONTH_jan,0.052583565711975096
+city_bucharest,1986,MONTH_jan,0.0437827730178833
+city_bucharest,1987,MONTH_jan,0.04377776622772217
+city_bucharest,1988,MONTH_jan,0.05665992736816406
+city_bucharest,1989,MONTH_jan,0.05235843181610107
+city_bucharest,1990,MONTH_jan,0.04590248107910156
+city_bucharest,1991,MONTH_jan,0.06907992839813232
+city_bucharest,1992,MONTH_jan,0.06283785343170166
+city_bucharest,1993,MONTH_jan,0.07054651737213134
+city_bucharest,1994,MONTH_jan,0.07641243934631348
+city_bucharest,1995,MONTH_jan,0.08897040367126464
+city_bucharest,1996,MONTH_jan,0.08448899269104004
+city_bucharest,1997,MONTH_jan,0.09899968147277832
+city_bucharest,1998,MONTH_jan,0.11665589332580567
+city_bucharest,1999,MONTH_jan,0.12841182708740234
+city_bucharest,2000,MONTH_jan,0.10396162033081056
+city_bucharest,2001,MONTH_jan,0.10312889099121093
+city_bucharest,2002,MONTH_jan,0.10615109443664551
+city_bucharest,2003,MONTH_jan,0.08716782569885254
+city_bucharest,2004,MONTH_jan,0.10407816886901855
+city_bucharest,2005,MONTH_jan,0.1262943172454834
+city_bucharest,2006,MONTH_jan,0.11172654151916504
+city_bucharest,2007,MONTH_jan,0.10032530784606934
+city_bucharest,2008,MONTH_jan,0.10699505805969238
+city_bucharest,2009,MONTH_jan,0.10989452362060549
+city_bucharest,2010,MONTH_jan,0.14136738777160646
+city_bucharest,2011,MONTH_jan,0.11033339500427246
+city_bucharest,2012,MONTH_jan,0.1043911838531494
+city_bucharest,2013,MONTH_jan,0.14889094352722168
+city_bucharest,2014,MONTH_jan,0.17897146224975585
+city_bucharest,2015,MONTH_jan,0.1851751136779785
+city_bucharest,2016,MONTH_jan,0.18943086624145508
+city_bucharest,2017,MONTH_jan,0.16684114456176757
+city_bucharest,2018,MONTH_jan,0.17542226791381835
+city_bucharest,2019,MONTH_jan,0.16837947845458984
+city_bucharest,2020,MONTH_jan,0.17579639434814454
+city_bucharest,2021,MONTH_jan,0.1748103141784668
+city_moscow,1985,MONTH_jan,0.049429731369018556
+city_moscow,1986,MONTH_jan,0.04996936798095703
+city_moscow,1987,MONTH_jan,0.0478553295135498
+city_moscow,1988,MONTH_jan,0.046569442749023436
+city_moscow,1989,MONTH_jan,0.04605263233184814
+city_moscow,1990,MONTH_jan,0.04898304462432861
+city_moscow,1991,MONTH_jan,0.050031914710998535
+city_moscow,1992,MONTH_jan,0.05336780071258545
+city_moscow,1993,MONTH_jan,0.0578029727935791
+city_moscow,1994,MONTH_jan,0.06394296646118164
+city_moscow,1995,MONTH_jan,0.06769153594970703
+city_moscow,1996,MONTH_jan,0.06157421112060547
+city_moscow,1997,MONTH_jan,0.06642446517944336
+city_moscow,1998,MONTH_jan,0.06699800491333008
+city_moscow,1999,MONTH_jan,0.0669929838180542
+city_moscow,2000,MONTH_jan,0.06728621482849122
+city_moscow,2001,MONTH_jan,0.06975970268249512
+city_moscow,2002,MONTH_jan,0.06492728233337403
+city_moscow,2003,MONTH_jan,0.060753769874572754
+city_moscow,2004,MONTH_jan,0.06754271030426025
+city_moscow,2005,MONTH_jan,0.06603690147399903
+city_moscow,2006,MONTH_jan,0.06309632778167724
+city_moscow,2007,MONTH_jan,0.06371350765228272
+city_moscow,2008,MONTH_jan,0.05867445468902588
+city_moscow,2009,MONTH_jan,0.06485455513000488
+city_moscow,2010,MONTH_jan,0.059318561553955075
+city_moscow,2011,MONTH_jan,0.05585275650024414
+city_moscow,2012,MONTH_jan,0.0555089282989502
+city_moscow,2013,MONTH_jan,0.061906685829162596
+city_moscow,2014,MONTH_jan,0.0588721513748169
+city_moscow,2015,MONTH_jan,0.05773604393005371
+city_moscow,2016,MONTH_jan,0.06171404838562012
+city_moscow,2017,MONTH_jan,0.061212682723999025
+city_moscow,2018,MONTH_jan,0.06046762466430664
+city_moscow,2019,MONTH_jan,0.06201229095458984
+city_moscow,2020,MONTH_jan,0.07069781303405762
+city_moscow,2021,MONTH_jan,0.06620289325714111
+city_seoul,1965,MONTH_jan,0.028078150749206544
+city_seoul,1966,MONTH_jan,0.03209915399551391
+city_seoul,1967,MONTH_jan,0.027027177810668945
+city_seoul,1968,MONTH_jan,0.022995977401733397
+city_seoul,1969,MONTH_jan,0.029266607761383057
+city_seoul,1970,MONTH_jan,0.02155395030975342
+city_seoul,1971,MONTH_jan,0.021541755199432373
+city_seoul,1972,MONTH_jan,0.02153486490249634
+city_seoul,1973,MONTH_jan,0.016347564458847046
+city_seoul,1974,MONTH_jan,0.02332909107208252
+city_seoul,1975,MONTH_jan,0.0187353253364563
+city_seoul,1976,MONTH_jan,0.017800427675247192
+city_seoul,1977,MONTH_jan,0.012054132223129272
+city_seoul,1978,MONTH_jan,0.01396591067314148
+city_seoul,1979,MONTH_jan,0.01539095163345337
+city_seoul,1980,MONTH_jan,0.012506891489028931
+city_seoul,1981,MONTH_jan,0.01615981578826904
+city_seoul,1982,MONTH_jan,0.010666205883026122
+city_seoul,1983,MONTH_jan,0.013120517730712891
+city_seoul,1984,MONTH_jan,0.010462830066680908
+city_seoul,1985,MONTH_jan,0.015088706016540528
+city_seoul,1986,MONTH_jan,0.013128159046173095
+city_seoul,1987,MONTH_jan,0.015100772380828855
+city_seoul,1988,MONTH_jan,0.006779970526695251
+city_seoul,1989,MONTH_jan,0.009301869869232178
+city_seoul,1990,MONTH_jan,0.012995164394378662
+city_seoul,1991,MONTH_jan,0.008552601337432861
+city_seoul,1992,MONTH_jan,0.006740404963493347
+city_seoul,1993,MONTH_jan,0.008389674425125122
+city_seoul,1994,MONTH_jan,0.004319862127304077
+city_seoul,1995,MONTH_jan,0.0051026403903961185
+city_seoul,1996,MONTH_jan,0.004361970424652099
+city_seoul,1997,MONTH_jan,0.004026863574981689
+city_seoul,1998,MONTH_jan,0.006597698330879211
+city_seoul,1999,MONTH_jan,0.00593690276145935
+city_seoul,2000,MONTH_jan,0.005420218110084534
+city_seoul,2001,MONTH_jan,0.0031219828128814697
+city_seoul,2002,MONTH_jan,0.0041857692599296566
+city_seoul,2003,MONTH_jan,0.005985386967658997
+city_seoul,2004,MONTH_jan,0.005346380472183228
+city_seoul,2005,MONTH_jan,0.00453538864850998
+city_seoul,2006,MONTH_jan,0.004502668976783752
+city_seoul,2007,MONTH_jan,0.005043082237243653
+city_seoul,2008,MONTH_jan,0.005293462872505188
+city_seoul,2009,MONTH_jan,0.005700946450233459
+city_seoul,2010,MONTH_jan,0.009195590615272522
+city_seoul,2011,MONTH_jan,0.012539451122283935
+city_seoul,2012,MONTH_jan,0.01288482666015625
+city_seoul,2013,MONTH_jan,0.015155628919601441
+city_seoul,2014,MONTH_jan,0.017640750408172607
+city_seoul,2015,MONTH_jan,0.019041190147399904
+city_seoul,2016,MONTH_jan,0.020268120765686036
+city_seoul,2017,MONTH_jan,0.024301843643188478
+city_seoul,2018,MONTH_jan,0.02746018409729004
+city_seoul,2019,MONTH_jan,0.029800333976745606
+city_seoul,2020,MONTH_jan,0.03142855405807495
+city_seoul,2021,MONTH_jan,0.037249858379364016
+city_madrid,1965,MONTH_jan,0.17253438949584962
+city_madrid,1966,MONTH_jan,0.2111080551147461
+city_madrid,1967,MONTH_jan,0.16279375076293945
+city_madrid,1968,MONTH_jan,0.1665400505065918
+city_madrid,1969,MONTH_jan,0.18460060119628902
+city_madrid,1970,MONTH_jan,0.1555931854248047
+city_madrid,1971,MONTH_jan,0.16187814712524415
+city_madrid,1972,MONTH_jan,0.16690349578857422
+city_madrid,1973,MONTH_jan,0.12429852485656738
+city_madrid,1974,MONTH_jan,0.12622825622558595
+city_madrid,1975,MONTH_jan,0.10509173393249512
+city_madrid,1976,MONTH_jan,0.07937400817871093
+city_madrid,1977,MONTH_jan,0.14476511001586914
+city_madrid,1978,MONTH_jan,0.14644488334655761
+city_madrid,1979,MONTH_jan,0.15632555961608888
+city_madrid,1980,MONTH_jan,0.09981171607971191
+city_madrid,1981,MONTH_jan,0.07407119750976564
+city_madrid,1982,MONTH_jan,0.08885108947753906
+city_madrid,1983,MONTH_jan,0.08936359405517579
+city_madrid,1984,MONTH_jan,0.10203864097595217
+city_madrid,1985,MONTH_jan,0.10242602348327637
+city_madrid,1986,MONTH_jan,0.08501705169677734
+city_madrid,1987,MONTH_jan,0.08477232933044433
+city_madrid,1988,MONTH_jan,0.10430665016174316
+city_madrid,1989,MONTH_jan,0.055330204963684085
+city_madrid,1990,MONTH_jan,0.07198870182037354
+city_madrid,1991,MONTH_jan,0.07452907562255859
+city_madrid,1992,MONTH_jan,0.05013250350952148
+city_madrid,1993,MONTH_jan,0.0662508773803711
+city_madrid,1994,MONTH_jan,0.07313819408416748
+city_madrid,1995,MONTH_jan,0.05938282489776611
+city_madrid,1996,MONTH_jan,0.09653098106384275
+city_madrid,1997,MONTH_jan,0.08211402893066407
+city_madrid,1998,MONTH_jan,0.07815210342407226
+city_madrid,1999,MONTH_jan,0.05629496574401856
+city_madrid,2000,MONTH_jan,0.07008230686187744
+city_madrid,2001,MONTH_jan,0.09167547225952148
+city_madrid,2002,MONTH_jan,0.0644627046585083
+city_madrid,2003,MONTH_jan,0.09758903503417969
+city_madrid,2004,MONTH_jan,0.0844090461730957
+city_madrid,2005,MONTH_jan,0.06923494338989258
+city_madrid,2006,MONTH_jan,0.08360922813415528
+city_madrid,2007,MONTH_jan,0.09064817428588867
+city_madrid,2008,MONTH_jan,0.09948370933532717
+city_madrid,2009,MONTH_jan,0.12662100791931152
+city_madrid,2010,MONTH_jan,0.16742803573608397
+city_madrid,2011,MONTH_jan,0.15473434448242188
+city_madrid,2012,MONTH_jan,0.15889772415161132
+city_madrid,2013,MONTH_jan,0.19901302337646484
+city_madrid,2014,MONTH_jan,0.2008855438232422
+city_madrid,2015,MONTH_jan,0.17507902145385743
+city_madrid,2016,MONTH_jan,0.18609848022460937
+city_madrid,2017,MONTH_jan,0.15682350158691405
+city_madrid,2018,MONTH_jan,0.18230533599853516
+city_madrid,2019,MONTH_jan,0.17375850677490234
+city_madrid,2020,MONTH_jan,0.22102653503417968
+city_madrid,2021,MONTH_jan,0.22341663360595704
+city_bangkok,1965,MONTH_jan,0.08098695755004882
+city_bangkok,1966,MONTH_jan,0.08597766876220703
+city_bangkok,1967,MONTH_jan,0.09705655097961426
+city_bangkok,1968,MONTH_jan,0.07769661903381347
+city_bangkok,1969,MONTH_jan,0.054768366813659666
+city_bangkok,1970,MONTH_jan,0.07854705810546875
+city_bangkok,1971,MONTH_jan,0.08126887321472168
+city_bangkok,1972,MONTH_jan,0.056506228446960446
+city_bangkok,1973,MONTH_jan,0.05797545909881592
+city_bangkok,1974,MONTH_jan,0.07285396575927734
+city_bangkok,1975,MONTH_jan,0.09127364158630373
+city_bangkok,1976,MONTH_jan,0.09248512268066406
+city_bangkok,1977,MONTH_jan,0.07547190666198732
+city_bangkok,1978,MONTH_jan,0.045728025436401365
+city_bangkok,1979,MONTH_jan,0.06653911590576173
+city_bangkok,1980,MONTH_jan,0.02589577913284302
+city_bangkok,1981,MONTH_jan,0.06015193939208984
+city_bangkok,1982,MONTH_jan,0.07570967674255372
+city_bangkok,1983,MONTH_jan,0.06589135646820068
+city_bangkok,1984,MONTH_jan,0.06626251220703125
+city_bangkok,1985,MONTH_jan,0.05735532283782959
+city_bangkok,1986,MONTH_jan,0.08074996948242187
+city_bangkok,1987,MONTH_jan,0.05194031715393067
+city_bangkok,1988,MONTH_jan,0.04262178421020508
+city_bangkok,1989,MONTH_jan,0.05435298919677734
+city_bangkok,1990,MONTH_jan,0.04073401927947998
+city_bangkok,1991,MONTH_jan,0.03400254249572754
+city_bangkok,1992,MONTH_jan,0.028629646301269532
+city_bangkok,1993,MONTH_jan,0.021971902847290038
+city_bangkok,1994,MONTH_jan,0.02397928237915039
+city_bangkok,1995,MONTH_jan,0.032421882152557376
+city_bangkok,1996,MONTH_jan,0.031586606502532956
+city_bangkok,1997,MONTH_jan,0.02977715253829956
+city_bangkok,1998,MONTH_jan,0.02375905752182007
+city_bangkok,1999,MONTH_jan,0.01608154773712158
+city_bangkok,2000,MONTH_jan,0.025746095180511474
+city_bangkok,2001,MONTH_jan,0.025714476108551026
+city_bangkok,2002,MONTH_jan,0.02818033695220947
+city_bangkok,2003,MONTH_jan,0.027227082252502442
+city_bangkok,2004,MONTH_jan,0.02313349723815918
+city_bangkok,2005,MONTH_jan,0.0227521014213562
+city_bangkok,2006,MONTH_jan,0.029778263568878173
+city_bangkok,2007,MONTH_jan,0.029363691806793213
+city_bangkok,2008,MONTH_jan,0.029482808113098145
+city_bangkok,2009,MONTH_jan,0.030501224994659425
+city_bangkok,2010,MONTH_jan,0.028064701557159424
+city_bangkok,2011,MONTH_jan,0.03457339763641357
+city_bangkok,2012,MONTH_jan,0.03770574569702149
+city_bangkok,2013,MONTH_jan,0.0385915493965149
+city_bangkok,2014,MONTH_jan,0.04258995532989502
+city_bangkok,2015,MONTH_jan,0.04190981388092041
+city_bangkok,2016,MONTH_jan,0.045999755859375
+city_bangkok,2017,MONTH_jan,0.05306326389312744
+city_bangkok,2018,MONTH_jan,0.06380221366882324
+city_bangkok,2019,MONTH_jan,0.06956219673156738
+city_bangkok,2020,MONTH_jan,0.0699142074584961
+city_bangkok,2021,MONTH_jan,0.07113617897033692
+city_ankara,1965,MONTH_jan,0.07396099090576172
+city_ankara,1966,MONTH_jan,0.06960969448089599
+city_ankara,1967,MONTH_jan,0.06997379779815674
+city_ankara,1968,MONTH_jan,0.0787749719619751
+city_ankara,1969,MONTH_jan,0.07791776180267335
+city_ankara,1970,MONTH_jan,0.06475101470947266
+city_ankara,1971,MONTH_jan,0.05071615219116211
+city_ankara,1972,MONTH_jan,0.05530313968658447
+city_ankara,1973,MONTH_jan,0.04050086498260498
+city_ankara,1974,MONTH_jan,0.05047706604003906
+city_ankara,1975,MONTH_jan,0.07770755290985107
+city_ankara,1976,MONTH_jan,0.09604342460632324
+city_ankara,1977,MONTH_jan,0.09040463447570801
+city_ankara,1978,MONTH_jan,0.0920789909362793
+city_ankara,1979,MONTH_jan,0.10752221107482911
+city_ankara,1980,MONTH_jan,0.11387236595153809
+city_ankara,1981,MONTH_jan,0.1250270175933838
+city_ankara,1982,MONTH_jan,0.12745107650756837
+city_ankara,1983,MONTH_jan,0.09873126983642579
+city_ankara,1984,MONTH_jan,0.11116494178771973
+city_ankara,1985,MONTH_jan,0.09229307174682616
+city_ankara,1986,MONTH_jan,0.08374287605285645
+city_ankara,1987,MONTH_jan,0.11513276100158691
+city_ankara,1988,MONTH_jan,0.16268531799316407
+city_ankara,1989,MONTH_jan,0.10282753944396973
+city_ankara,1990,MONTH_jan,0.12219701766967772
+city_ankara,1991,MONTH_jan,0.11752680778503417
+city_ankara,1992,MONTH_jan,0.1307958698272705
+city_ankara,1993,MONTH_jan,0.15372352600097655
+city_ankara,1994,MONTH_jan,0.14304976463317873
+city_ankara,1995,MONTH_jan,0.14924197196960448
+city_ankara,1996,MONTH_jan,0.15543943405151367
+city_ankara,1997,MONTH_jan,0.14665958404541016
+city_ankara,1998,MONTH_jan,0.15141606330871582
+city_ankara,1999,MONTH_jan,0.12753751754760742
+city_ankara,2000,MONTH_jan,0.10657172203063965
+city_ankara,2001,MONTH_jan,0.09108513832092284
+city_ankara,2002,MONTH_jan,0.11535796165466308
+city_ankara,2003,MONTH_jan,0.11330117225646973
+city_ankara,2004,MONTH_jan,0.1369169235229492
+city_ankara,2005,MONTH_jan,0.11418582916259766
+city_ankara,2006,MONTH_jan,0.11456920623779297
+city_ankara,2007,MONTH_jan,0.08779489517211914
+city_ankara,2008,MONTH_jan,0.08211599349975586
+city_ankara,2009,MONTH_jan,0.08923343658447265
+city_ankara,2010,MONTH_jan,0.12280028343200683
+city_ankara,2011,MONTH_jan,0.11913782119750976
+city_ankara,2012,MONTH_jan,0.1252276611328125
+city_ankara,2013,MONTH_jan,0.1331322193145752
+city_ankara,2014,MONTH_jan,0.09769227027893067
+city_ankara,2015,MONTH_jan,0.1406381320953369
+city_ankara,2016,MONTH_jan,0.14479534149169923
+city_ankara,2017,MONTH_jan,0.13170472145080567
+city_ankara,2018,MONTH_jan,0.1484662437438965
+city_ankara,2019,MONTH_jan,0.1925493621826172
+city_ankara,2020,MONTH_jan,0.19150110244750976
+city_ankara,2021,MONTH_jan,0.16517044067382813
+city_kyiv,1985,MONTH_jan,0.011391943693161011
+city_kyiv,1986,MONTH_jan,0.01128100037574768
+city_kyiv,1987,MONTH_jan,0.009946019649505616
+city_kyiv,1988,MONTH_jan,0.012357125282287598
+city_kyiv,1989,MONTH_jan,0.010963214635848999
+city_kyiv,1990,MONTH_jan,0.009731683731079101
+city_kyiv,1991,MONTH_jan,0.01156305193901062
+city_kyiv,1992,MONTH_jan,0.00921140432357788
+city_kyiv,1993,MONTH_jan,0.014715107679367062
+city_kyiv,1994,MONTH_jan,0.018900632858276367
+city_kyiv,1995,MONTH_jan,0.015910707712173462
+city_kyiv,1996,MONTH_jan,0.014804906845092771
+city_kyiv,1997,MONTH_jan,0.017526177167892457
+city_kyiv,1998,MONTH_jan,0.02900869369506836
+city_kyiv,1999,MONTH_jan,0.026803247928619385
+city_kyiv,2000,MONTH_jan,0.021038007736206055
+city_kyiv,2001,MONTH_jan,0.022646660804748534
+city_kyiv,2002,MONTH_jan,0.018132338523864745
+city_kyiv,2003,MONTH_jan,0.01688597321510315
+city_kyiv,2004,MONTH_jan,0.021416072845458985
+city_kyiv,2005,MONTH_jan,0.022117321491241456
+city_kyiv,2006,MONTH_jan,0.022632830142974857
+city_kyiv,2007,MONTH_jan,0.018360310792922975
+city_kyiv,2008,MONTH_jan,0.02132999897003174
+city_kyiv,2009,MONTH_jan,0.025476245880126952
+city_kyiv,2010,MONTH_jan,0.02610059976577759
+city_kyiv,2011,MONTH_jan,0.020985288619995116
+city_kyiv,2012,MONTH_jan,0.021388649940490723
+city_kyiv,2013,MONTH_jan,0.03011183738708496
+city_kyiv,2014,MONTH_jan,0.022846858501434326
+city_kyiv,2015,MONTH_jan,0.01913326621055603
+city_kyiv,2016,MONTH_jan,0.02366657018661499
+city_kyiv,2017,MONTH_jan,0.02975400447845459
+city_kyiv,2018,MONTH_jan,0.03421227693557739
+city_kyiv,2019,MONTH_jan,0.03290361166000366
+city_kyiv,2020,MONTH_jan,0.048960785865783694
+city_kyiv,2021,MONTH_jan,0.06100841522216797
+city_dubai,1965,MONTH_jan,0
+city_dubai,1966,MONTH_jan,0
+city_dubai,1967,MONTH_jan,0
+city_dubai,1968,MONTH_jan,0
+city_dubai,1969,MONTH_jan,0
+city_dubai,1970,MONTH_jan,0
+city_dubai,1971,MONTH_jan,0
+city_dubai,1972,MONTH_jan,0
+city_dubai,1973,MONTH_jan,0
+city_dubai,1974,MONTH_jan,0
+city_dubai,1975,MONTH_jan,0
+city_dubai,1976,MONTH_jan,0
+city_dubai,1977,MONTH_jan,0
+city_dubai,1978,MONTH_jan,0
+city_dubai,1979,MONTH_jan,0
+city_dubai,1980,MONTH_jan,0
+city_dubai,1981,MONTH_jan,0
+city_dubai,1982,MONTH_jan,0
+city_dubai,1983,MONTH_jan,0
+city_dubai,1984,MONTH_jan,0
+city_dubai,1985,MONTH_jan,0
+city_dubai,1986,MONTH_jan,0
+city_dubai,1987,MONTH_jan,0
+city_dubai,1988,MONTH_jan,0
+city_dubai,1989,MONTH_jan,0
+city_dubai,1990,MONTH_jan,0
+city_dubai,1991,MONTH_jan,0
+city_dubai,1992,MONTH_jan,0
+city_dubai,1993,MONTH_jan,0
+city_dubai,1994,MONTH_jan,0
+city_dubai,1995,MONTH_jan,0
+city_dubai,1996,MONTH_jan,0
+city_dubai,1997,MONTH_jan,0
+city_dubai,1998,MONTH_jan,0
+city_dubai,1999,MONTH_jan,0
+city_dubai,2000,MONTH_jan,0
+city_dubai,2001,MONTH_jan,0
+city_dubai,2002,MONTH_jan,0
+city_dubai,2003,MONTH_jan,0
+city_dubai,2004,MONTH_jan,0
+city_dubai,2005,MONTH_jan,0
+city_dubai,2006,MONTH_jan,0
+city_dubai,2007,MONTH_jan,0
+city_dubai,2008,MONTH_jan,0
+city_dubai,2009,MONTH_jan,1.91772636026144e-5
+city_dubai,2010,MONTH_jan,5.25837112218141e-5
+city_dubai,2011,MONTH_jan,5.59225212782621e-5
+city_dubai,2012,MONTH_jan,6.18843780830502e-5
+city_dubai,2013,MONTH_jan,2.0930014550685878e-4
+city_dubai,2014,MONTH_jan,7.691703736782074e-4
+city_dubai,2015,MONTH_jan,6.952883303165436e-4
+city_dubai,2016,MONTH_jan,6.702232360839843e-4
+city_dubai,2017,MONTH_jan,0.0015412747859954834
+city_dubai,2018,MONTH_jan,0.0027376759052276612
+city_dubai,2019,MONTH_jan,0.007918253540992737
+city_dubai,2020,MONTH_jan,0.010150353908538818
+city_san_francisco,1965,MONTH_jan,0.043688697814941405
+city_denver,1965,MONTH_jan,0.043688697814941405
+city_washington,1965,MONTH_jan,0.043688697814941405
+city_chicago,1965,MONTH_jan,0.043688697814941405
+city_san_francisco,1966,MONTH_jan,0.041714019775390625
+city_denver,1966,MONTH_jan,0.041714019775390625
+city_washington,1966,MONTH_jan,0.041714019775390625
+city_chicago,1966,MONTH_jan,0.041714019775390625
+city_san_francisco,1967,MONTH_jan,0.04542215824127197
+city_denver,1967,MONTH_jan,0.04542215824127197
+city_washington,1967,MONTH_jan,0.04542215824127197
+city_chicago,1967,MONTH_jan,0.04542215824127197
+city_san_francisco,1968,MONTH_jan,0.04330973625183106
+city_denver,1968,MONTH_jan,0.04330973625183106
+city_washington,1968,MONTH_jan,0.04330973625183106
+city_chicago,1968,MONTH_jan,0.04330973625183106
+city_san_francisco,1969,MONTH_jan,0.04598878383636475
+city_denver,1969,MONTH_jan,0.04598878383636475
+city_washington,1969,MONTH_jan,0.04598878383636475
+city_chicago,1969,MONTH_jan,0.04598878383636475
+city_san_francisco,1970,MONTH_jan,0.044008030891418456
+city_denver,1970,MONTH_jan,0.044008030891418456
+city_washington,1970,MONTH_jan,0.044008030891418456
+city_chicago,1970,MONTH_jan,0.044008030891418456
+city_san_francisco,1971,MONTH_jan,0.046144747734069826
+city_denver,1971,MONTH_jan,0.046144747734069826
+city_washington,1971,MONTH_jan,0.046144747734069826
+city_chicago,1971,MONTH_jan,0.046144747734069826
+city_san_francisco,1972,MONTH_jan,0.045260472297668455
+city_denver,1972,MONTH_jan,0.045260472297668455
+city_washington,1972,MONTH_jan,0.045260472297668455
+city_chicago,1972,MONTH_jan,0.045260472297668455
+city_san_francisco,1973,MONTH_jan,0.043547596931457516
+city_denver,1973,MONTH_jan,0.043547596931457516
+city_washington,1973,MONTH_jan,0.043547596931457516
+city_chicago,1973,MONTH_jan,0.043547596931457516
+city_san_francisco,1974,MONTH_jan,0.04902864933013916
+city_denver,1974,MONTH_jan,0.04902864933013916
+city_washington,1974,MONTH_jan,0.04902864933013916
+city_chicago,1974,MONTH_jan,0.04902864933013916
+city_san_francisco,1975,MONTH_jan,0.050069751739501955
+city_denver,1975,MONTH_jan,0.050069751739501955
+city_washington,1975,MONTH_jan,0.050069751739501955
+city_chicago,1975,MONTH_jan,0.050069751739501955
+city_san_francisco,1976,MONTH_jan,0.045527076721191405
+city_denver,1976,MONTH_jan,0.045527076721191405
+city_washington,1976,MONTH_jan,0.045527076721191405
+city_chicago,1976,MONTH_jan,0.045527076721191405
+city_san_francisco,1977,MONTH_jan,0.03549314022064209
+city_denver,1977,MONTH_jan,0.03549314022064209
+city_washington,1977,MONTH_jan,0.03549314022064209
+city_chicago,1977,MONTH_jan,0.03549314022064209
+city_san_francisco,1978,MONTH_jan,0.043275704383850096
+city_denver,1978,MONTH_jan,0.043275704383850096
+city_washington,1978,MONTH_jan,0.043275704383850096
+city_chicago,1978,MONTH_jan,0.043275704383850096
+city_san_francisco,1979,MONTH_jan,0.0429140567779541
+city_denver,1979,MONTH_jan,0.0429140567779541
+city_washington,1979,MONTH_jan,0.0429140567779541
+city_chicago,1979,MONTH_jan,0.0429140567779541
+city_san_francisco,1980,MONTH_jan,0.044192209243774414
+city_denver,1980,MONTH_jan,0.044192209243774414
+city_washington,1980,MONTH_jan,0.044192209243774414
+city_chicago,1980,MONTH_jan,0.044192209243774414
+city_san_francisco,1981,MONTH_jan,0.04336748600006104
+city_denver,1981,MONTH_jan,0.04336748600006104
+city_washington,1981,MONTH_jan,0.04336748600006104
+city_chicago,1981,MONTH_jan,0.04336748600006104
+city_san_francisco,1982,MONTH_jan,0.05256711483001709
+city_denver,1982,MONTH_jan,0.05256711483001709
+city_washington,1982,MONTH_jan,0.05256711483001709
+city_chicago,1982,MONTH_jan,0.05256711483001709
+city_san_francisco,1983,MONTH_jan,0.056848154067993165
+city_denver,1983,MONTH_jan,0.056848154067993165
+city_washington,1983,MONTH_jan,0.056848154067993165
+city_chicago,1983,MONTH_jan,0.056848154067993165
+city_san_francisco,1984,MONTH_jan,0.05334360599517822
+city_denver,1984,MONTH_jan,0.05334360599517822
+city_washington,1984,MONTH_jan,0.05334360599517822
+city_chicago,1984,MONTH_jan,0.05334360599517822
+city_san_francisco,1985,MONTH_jan,0.04765776157379151
+city_denver,1985,MONTH_jan,0.04765776157379151
+city_washington,1985,MONTH_jan,0.04765776157379151
+city_chicago,1985,MONTH_jan,0.04765776157379151
+city_san_francisco,1986,MONTH_jan,0.048984041213989256
+city_denver,1986,MONTH_jan,0.048984041213989256
+city_washington,1986,MONTH_jan,0.048984041213989256
+city_chicago,1986,MONTH_jan,0.048984041213989256
+city_san_francisco,1987,MONTH_jan,0.04214753627777099
+city_denver,1987,MONTH_jan,0.04214753627777099
+city_washington,1987,MONTH_jan,0.04214753627777099
+city_chicago,1987,MONTH_jan,0.04214753627777099
+city_san_francisco,1988,MONTH_jan,0.03664999723434448
+city_denver,1988,MONTH_jan,0.03664999723434448
+city_washington,1988,MONTH_jan,0.03664999723434448
+city_chicago,1988,MONTH_jan,0.03664999723434448
+city_san_francisco,1989,MONTH_jan,0.043015427589416504
+city_denver,1989,MONTH_jan,0.043015427589416504
+city_washington,1989,MONTH_jan,0.043015427589416504
+city_chicago,1989,MONTH_jan,0.043015427589416504
+city_san_francisco,1990,MONTH_jan,0.047246766090393064
+city_denver,1990,MONTH_jan,0.047246766090393064
+city_washington,1990,MONTH_jan,0.047246766090393064
+city_chicago,1990,MONTH_jan,0.047246766090393064
+city_san_francisco,1991,MONTH_jan,0.0472428560256958
+city_denver,1991,MONTH_jan,0.0472428560256958
+city_washington,1991,MONTH_jan,0.0472428560256958
+city_chicago,1991,MONTH_jan,0.0472428560256958
+city_san_francisco,1992,MONTH_jan,0.042612309455871585
+city_denver,1992,MONTH_jan,0.042612309455871585
+city_washington,1992,MONTH_jan,0.042612309455871585
+city_chicago,1992,MONTH_jan,0.042612309455871585
+city_san_francisco,1993,MONTH_jan,0.045714097023010256
+city_denver,1993,MONTH_jan,0.045714097023010256
+city_washington,1993,MONTH_jan,0.045714097023010256
+city_chicago,1993,MONTH_jan,0.045714097023010256
+city_san_francisco,1994,MONTH_jan,0.0425481653213501
+city_denver,1994,MONTH_jan,0.0425481653213501
+city_washington,1994,MONTH_jan,0.0425481653213501
+city_chicago,1994,MONTH_jan,0.0425481653213501
+city_san_francisco,1995,MONTH_jan,0.04762141227722168
+city_denver,1995,MONTH_jan,0.04762141227722168
+city_washington,1995,MONTH_jan,0.04762141227722168
+city_chicago,1995,MONTH_jan,0.04762141227722168
+city_san_francisco,1996,MONTH_jan,0.050185713768005374
+city_denver,1996,MONTH_jan,0.050185713768005374
+city_washington,1996,MONTH_jan,0.050185713768005374
+city_chicago,1996,MONTH_jan,0.050185713768005374
+city_san_francisco,1997,MONTH_jan,0.051152210235595706
+city_denver,1997,MONTH_jan,0.051152210235595706
+city_washington,1997,MONTH_jan,0.051152210235595706
+city_chicago,1997,MONTH_jan,0.051152210235595706
+city_san_francisco,1998,MONTH_jan,0.046957569122314455
+city_denver,1998,MONTH_jan,0.046957569122314455
+city_washington,1998,MONTH_jan,0.046957569122314455
+city_chicago,1998,MONTH_jan,0.046957569122314455
+city_san_francisco,1999,MONTH_jan,0.04583451747894287
+city_denver,1999,MONTH_jan,0.04583451747894287
+city_washington,1999,MONTH_jan,0.04583451747894287
+city_chicago,1999,MONTH_jan,0.04583451747894287
+city_san_francisco,2000,MONTH_jan,0.0402735710144043
+city_denver,2000,MONTH_jan,0.0402735710144043
+city_washington,2000,MONTH_jan,0.0402735710144043
+city_chicago,2000,MONTH_jan,0.0402735710144043
+city_san_francisco,2001,MONTH_jan,0.034213709831237796
+city_denver,2001,MONTH_jan,0.034213709831237796
+city_washington,2001,MONTH_jan,0.034213709831237796
+city_chicago,2001,MONTH_jan,0.034213709831237796
+city_san_francisco,2002,MONTH_jan,0.040155067443847656
+city_denver,2002,MONTH_jan,0.040155067443847656
+city_washington,2002,MONTH_jan,0.040155067443847656
+city_chicago,2002,MONTH_jan,0.040155067443847656
+city_san_francisco,2003,MONTH_jan,0.041800403594970705
+city_denver,2003,MONTH_jan,0.041800403594970705
+city_washington,2003,MONTH_jan,0.041800403594970705
+city_chicago,2003,MONTH_jan,0.041800403594970705
+city_san_francisco,2004,MONTH_jan,0.04101511478424072
+city_denver,2004,MONTH_jan,0.04101511478424072
+city_washington,2004,MONTH_jan,0.04101511478424072
+city_chicago,2004,MONTH_jan,0.04101511478424072
+city_san_francisco,2005,MONTH_jan,0.04219231128692627
+city_denver,2005,MONTH_jan,0.04219231128692627
+city_washington,2005,MONTH_jan,0.04219231128692627
+city_chicago,2005,MONTH_jan,0.04219231128692627
+city_san_francisco,2006,MONTH_jan,0.04678860664367676
+city_denver,2006,MONTH_jan,0.04678860664367676
+city_washington,2006,MONTH_jan,0.04678860664367676
+city_chicago,2006,MONTH_jan,0.04678860664367676
+city_san_francisco,2007,MONTH_jan,0.04373008728027344
+city_denver,2007,MONTH_jan,0.04373008728027344
+city_washington,2007,MONTH_jan,0.04373008728027344
+city_chicago,2007,MONTH_jan,0.04373008728027344
+city_san_francisco,2008,MONTH_jan,0.050045504570007324
+city_denver,2008,MONTH_jan,0.050045504570007324
+city_washington,2008,MONTH_jan,0.050045504570007324
+city_chicago,2008,MONTH_jan,0.050045504570007324
+city_san_francisco,2009,MONTH_jan,0.0580000638961792
+city_denver,2009,MONTH_jan,0.0580000638961792
+city_washington,2009,MONTH_jan,0.0580000638961792
+city_chicago,2009,MONTH_jan,0.0580000638961792
+city_san_francisco,2010,MONTH_jan,0.05840554714202881
+city_denver,2010,MONTH_jan,0.05840554714202881
+city_washington,2010,MONTH_jan,0.05840554714202881
+city_chicago,2010,MONTH_jan,0.05840554714202881
+city_san_francisco,2011,MONTH_jan,0.06897319316864013
+city_denver,2011,MONTH_jan,0.06897319316864013
+city_washington,2011,MONTH_jan,0.06897319316864013
+city_chicago,2011,MONTH_jan,0.06897319316864013
+city_san_francisco,2012,MONTH_jan,0.06886333942413331
+city_denver,2012,MONTH_jan,0.06886333942413331
+city_washington,2012,MONTH_jan,0.06886333942413331
+city_chicago,2012,MONTH_jan,0.06886333942413331
+city_san_francisco,2013,MONTH_jan,0.07134616374969482
+city_denver,2013,MONTH_jan,0.07134616374969482
+city_washington,2013,MONTH_jan,0.07134616374969482
+city_chicago,2013,MONTH_jan,0.07134616374969482
+city_san_francisco,2014,MONTH_jan,0.07262890815734863
+city_denver,2014,MONTH_jan,0.07262890815734863
+city_washington,2014,MONTH_jan,0.07262890815734863
+city_chicago,2014,MONTH_jan,0.07262890815734863
+city_san_francisco,2015,MONTH_jan,0.07468742847442628
+city_denver,2015,MONTH_jan,0.07468742847442628
+city_washington,2015,MONTH_jan,0.07468742847442628
+city_chicago,2015,MONTH_jan,0.07468742847442628
+city_san_francisco,2016,MONTH_jan,0.08285085678100586
+city_denver,2016,MONTH_jan,0.08285085678100586
+city_washington,2016,MONTH_jan,0.08285085678100586
+city_chicago,2016,MONTH_jan,0.08285085678100586
+city_san_francisco,2017,MONTH_jan,0.09082049369812012
+city_denver,2017,MONTH_jan,0.09082049369812012
+city_washington,2017,MONTH_jan,0.09082049369812012
+city_chicago,2017,MONTH_jan,0.09082049369812012
+city_san_francisco,2018,MONTH_jan,0.0896493911743164
+city_denver,2018,MONTH_jan,0.0896493911743164
+city_washington,2018,MONTH_jan,0.0896493911743164
+city_chicago,2018,MONTH_jan,0.0896493911743164
+city_san_francisco,2019,MONTH_jan,0.09315262794494628
+city_denver,2019,MONTH_jan,0.09315262794494628
+city_washington,2019,MONTH_jan,0.09315262794494628
+city_chicago,2019,MONTH_jan,0.09315262794494628
+city_san_francisco,2020,MONTH_jan,0.10532232284545899
+city_denver,2020,MONTH_jan,0.10532232284545899
+city_washington,2020,MONTH_jan,0.10532232284545899
+city_chicago,2020,MONTH_jan,0.10532232284545899
+city_san_francisco,2021,MONTH_jan,0.10655990600585938
+city_denver,2021,MONTH_jan,0.10655990600585938
+city_washington,2021,MONTH_jan,0.10655990600585938
+city_chicago,2021,MONTH_jan,0.10655990600585938
+city_hanoi,1965,MONTH_jan,0.034132664203643796
+city_hanoi,1966,MONTH_jan,0.02240062952041626
+city_hanoi,1967,MONTH_jan,0.017923457622528075
+city_hanoi,1968,MONTH_jan,0.01929360270500183
+city_hanoi,1969,MONTH_jan,0.01837692379951477
+city_hanoi,1970,MONTH_jan,0.019573317766189577
+city_hanoi,1971,MONTH_jan,0.022463304996490477
+city_hanoi,1972,MONTH_jan,0.01592976570129394
+city_hanoi,1973,MONTH_jan,0.015021532773971558
+city_hanoi,1974,MONTH_jan,0.020197300910949706
+city_hanoi,1975,MONTH_jan,0.02163102149963379
+city_hanoi,1976,MONTH_jan,0.041627111434936526
+city_hanoi,1977,MONTH_jan,0.04191826820373535
+city_hanoi,1978,MONTH_jan,0.05387956142425537
+city_hanoi,1979,MONTH_jan,0.062493300437927245
+city_hanoi,1980,MONTH_jan,0.08125602722167968
+city_hanoi,1981,MONTH_jan,0.08210463523864746
+city_hanoi,1982,MONTH_jan,0.07957214832305909
+city_hanoi,1983,MONTH_jan,0.059674644470214845
+city_hanoi,1984,MONTH_jan,0.07733730316162109
+city_hanoi,1985,MONTH_jan,0.07084222316741944
+city_hanoi,1986,MONTH_jan,0.062252769470214846
+city_hanoi,1987,MONTH_jan,0.05438616275787354
+city_hanoi,1988,MONTH_jan,0.07041000366210938
+city_hanoi,1989,MONTH_jan,0.15831216812133786
+city_hanoi,1990,MONTH_jan,0.20584053039550781
+city_hanoi,1991,MONTH_jan,0.2379098892211914
+city_hanoi,1992,MONTH_jan,0.24912485122680664
+city_hanoi,1993,MONTH_jan,0.24039852142333984
+city_hanoi,1994,MONTH_jan,0.2497269630432129
+city_hanoi,1995,MONTH_jan,0.24608880996704102
+city_hanoi,1996,MONTH_jan,0.24358139038085938
+city_hanoi,1997,MONTH_jan,0.20527612686157226
+city_hanoi,1998,MONTH_jan,0.1806495475769043
+city_hanoi,1999,MONTH_jan,0.2165902328491211
+city_hanoi,2000,MONTH_jan,0.20092161178588866
+city_hanoi,2001,MONTH_jan,0.22102148056030274
+city_hanoi,2002,MONTH_jan,0.20062660217285155
+city_hanoi,2003,MONTH_jan,0.19297657012939454
+city_hanoi,2004,MONTH_jan,0.14763954162597656
+city_hanoi,2005,MONTH_jan,0.1284674835205078
+city_hanoi,2006,MONTH_jan,0.16607240676879884
+city_hanoi,2007,MONTH_jan,0.17202272415161132
+city_hanoi,2008,MONTH_jan,0.16009517669677734
+city_hanoi,2009,MONTH_jan,0.17948020935058595
+city_hanoi,2010,MONTH_jan,0.14711740493774414
+city_hanoi,2011,MONTH_jan,0.1902745246887207
+city_hanoi,2012,MONTH_jan,0.2338884544372558
+city_hanoi,2013,MONTH_jan,0.23877511978149418
+city_hanoi,2014,MONTH_jan,0.23073970794677734
+city_hanoi,2015,MONTH_jan,0.18584493637084962
+city_hanoi,2016,MONTH_jan,0.19181915283203124
+city_hanoi,2017,MONTH_jan,0.24208347320556642
+city_hanoi,2018,MONTH_jan,0.20761816024780275
+city_hanoi,2019,MONTH_jan,0.15891139030456544
+city_hanoi,2020,MONTH_jan,0.19136663436889648
+city_hanoi,2021,MONTH_jan,0.2273440742492676
diff --git a/run_tree/data/incenter_data/csv/question_3.csv b/run_tree/data/incenter_data/csv/question_3.csv
new file mode 100644
index 0000000..6d63a0a
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_3.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.601593625498008
+city_brisbane,2022,MONTH_jan,0.6060606060606061
+city_chengdu,2022,MONTH_jan,0.5254237288135594
+city_new_delhi,2022,MONTH_jan,0.6507936507936508
+city_paris,2022,MONTH_jan,0.569806492883416
+city_san_francisco,2022,MONTH_jan,0.6120930232558139
+city_denver,2022,MONTH_jan,0.6120930232558139
+city_ankara,2022,MONTH_jan,0.7948003714020427
+city_harare,2022,MONTH_jan,0.5714285714285714
+city_hanoi,2022,MONTH_jan,0.6158536585365854
+city_washington,2022,MONTH_jan,0.6120930232558139
+city_bangkok,2022,MONTH_jan,0.5555555555555556
+city_tunis,2022,MONTH_jan,0.5142857142857142
+city_seoul,2022,MONTH_jan,0.6158536585365854
+city_belgrade,2022,MONTH_jan,0.6169354838709677
+city_moscow,2022,MONTH_jan,0.5819672131147541
+city_lima,2022,MONTH_jan,0.425
+city_islamabad,2022,MONTH_jan,0.6363636363636364
+city_abuja,2022,MONTH_jan,0.8333333333333334
+city_managua,2022,MONTH_jan,0.6774193548387096
+city_amsterdam,2022,MONTH_jan,0.464638783269962
+city_rabat,2022,MONTH_jan,0.6190476190476191
+city_ulaanbaatar,2022,MONTH_jan,0.5254237288135594
+city_mexico_city,2022,MONTH_jan,0.5176882661996497
+city_nairobi,2022,MONTH_jan,0.5714285714285714
+city_tokyo,2022,MONTH_jan,0.5993009868421053
+city_baghdad,2022,MONTH_jan,0.5
+city_tehran,2022,MONTH_jan,0.5
+city_jakarta,2022,MONTH_jan,0.6011644832605532
+city_guatemala_city,2022,MONTH_jan,0.6774193548387096
+city_berlin,2022,MONTH_jan,0.6111111111111112
+city_addis_ababa,2022,MONTH_jan,0.5714285714285714
+city_cairo,2022,MONTH_jan,0.5142857142857142
+city_quito,2022,MONTH_jan,0.4827586206896552
+city_bogota,2022,MONTH_jan,0.45
+city_beijing,2022,MONTH_jan,0.5254237288135594
+city_accra,2022,MONTH_jan,0.5714285714285714
+city_ottawa,2022,MONTH_jan,0.6352941176470588
+city_brasilia,2022,MONTH_jan,0.7062314540059347
+city_la_paz,2022,MONTH_jan,0.7062314540059347
+city_dhaka,2022,MONTH_jan,0.7107438016528925
+city_yerevan,2022,MONTH_jan,0.7948003714020427
+city_chicago,2022,MONTH_jan,0.6120930232558139
+city_kyiv,2022,MONTH_jan,0.59375
+city_dubai,2022,MONTH_jan,0.5
+city_mumbai,2022,MONTH_jan,0.6507936507936508
+city_madrid,2022,MONTH_jan,0.4838709677419355
diff --git a/run_tree/data/incenter_data/csv/question_4.csv b/run_tree/data/incenter_data/csv/question_4.csv
new file mode 100644
index 0000000..18511d9
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_4.csv
@@ -0,0 +1,1364 @@
+city_data,year,month,prop
+city_abuja,2020,MONTH_jan,0
+city_abuja,2020,MONTH_feb,0
+city_abuja,2020,MONTH_mar,0.012232415902140673
+city_abuja,2020,MONTH_apr,0.14984709480122324
+city_abuja,2020,MONTH_may,0.672782874617737
+city_abuja,2020,MONTH_jun,0.9174311926605505
+city_abuja,2020,MONTH_jul,0.9327217125382263
+city_abuja,2020,MONTH_aug,0.41284403669724773
+city_abuja,2020,MONTH_sep,0.2996941896024465
+city_abuja,2020,MONTH_oct,0.10091743119266056
+city_abuja,2020,MONTH_nov,0.08868501529051988
+city_abuja,2020,MONTH_dec,0.3211009174311927
+city_abuja,2021,MONTH_jan,0.9174311926605505
+city_abuja,2021,MONTH_feb,1
+city_abuja,2021,MONTH_mar,0.4617737003058104
+city_abuja,2021,MONTH_apr,0.021406727828746176
+city_abuja,2021,MONTH_may,0.024464831804281346
+city_abuja,2021,MONTH_jun,0.14984709480122324
+city_abuja,2021,MONTH_jul,0.08868501529051988
+city_abuja,2021,MONTH_aug,0.9357798165137615
+city_abuja,2021,MONTH_sep,0.7553516819571865
+city_abuja,2021,MONTH_oct,0.5902140672782875
+city_abuja,2021,MONTH_nov,0.24770642201834864
+city_abuja,2021,MONTH_dec,0.1651376146788991
+city_abuja,2022,MONTH_jan,0.3211009174311927
+city_abuja,2022,MONTH_feb,0.021406727828746176
+city_abuja,2022,MONTH_mar,0
+city_abuja,2022,MONTH_apr,0.0030581039755351682
+city_abuja,2022,MONTH_may,0
+city_accra,2020,MONTH_jan,0
+city_accra,2020,MONTH_feb,0
+city_accra,2020,MONTH_mar,0.017467248908296942
+city_accra,2020,MONTH_apr,0.05240174672489083
+city_accra,2020,MONTH_may,0.08296943231441048
+city_accra,2020,MONTH_jun,0.33624454148471616
+city_accra,2020,MONTH_jul,0.27510917030567683
+city_accra,2020,MONTH_aug,0.4410480349344978
+city_accra,2020,MONTH_sep,0.1091703056768559
+city_accra,2020,MONTH_oct,0.08296943231441048
+city_accra,2020,MONTH_nov,0.021834061135371178
+city_accra,2020,MONTH_dec,0.043668122270742356
+city_accra,2021,MONTH_jan,0.388646288209607
+city_accra,2021,MONTH_feb,0.8165938864628821
+city_accra,2021,MONTH_mar,0.5807860262008734
+city_accra,2021,MONTH_apr,0.15283842794759825
+city_accra,2021,MONTH_may,0.026200873362445413
+city_accra,2021,MONTH_jun,0.048034934497816595
+city_accra,2021,MONTH_jul,0.11790393013100436
+city_accra,2021,MONTH_aug,1
+city_accra,2021,MONTH_sep,0.4585152838427948
+city_accra,2021,MONTH_oct,0.13537117903930132
+city_accra,2021,MONTH_nov,0.16593886462882096
+city_accra,2021,MONTH_dec,0.34934497816593885
+city_accra,2022,MONTH_jan,0.4279475982532751
+city_accra,2022,MONTH_feb,0.16593886462882096
+city_accra,2022,MONTH_mar,0.013100436681222707
+city_accra,2022,MONTH_apr,0
+city_accra,2022,MONTH_may,0
+city_addis_ababa,2020,MONTH_jan,0
+city_addis_ababa,2020,MONTH_feb,0
+city_addis_ababa,2020,MONTH_mar,0
+city_addis_ababa,2020,MONTH_apr,0.026172300981461286
+city_addis_ababa,2020,MONTH_may,0.004362050163576881
+city_addis_ababa,2020,MONTH_jun,0.10032715376226826
+city_addis_ababa,2020,MONTH_jul,0.1723009814612868
+city_addis_ababa,2020,MONTH_aug,0.5627044711014176
+city_addis_ababa,2020,MONTH_sep,0.4340239912758997
+city_addis_ababa,2020,MONTH_oct,0.2966194111232279
+city_addis_ababa,2020,MONTH_nov,0.257360959651036
+city_addis_ababa,2020,MONTH_dec,0.23773173391494
+city_addis_ababa,2021,MONTH_jan,0.18865866957470012
+city_addis_ababa,2021,MONTH_feb,0.28680479825517996
+city_addis_ababa,2021,MONTH_mar,0.5310796074154853
+city_addis_ababa,2021,MONTH_apr,0.8909487459105779
+city_addis_ababa,2021,MONTH_may,0.5419847328244275
+city_addis_ababa,2021,MONTH_jun,0.17993456924754633
+city_addis_ababa,2021,MONTH_jul,0.06870229007633588
+city_addis_ababa,2021,MONTH_aug,0.302071973827699
+city_addis_ababa,2021,MONTH_sep,0.9531079607415486
+city_addis_ababa,2021,MONTH_oct,1
+city_addis_ababa,2021,MONTH_nov,0.3260632497273719
+city_addis_ababa,2021,MONTH_dec,0.19193020719738277
+city_addis_ababa,2022,MONTH_jan,0.4416575790621592
+city_addis_ababa,2022,MONTH_feb,0.1406761177753544
+city_addis_ababa,2022,MONTH_mar,0.03489640130861505
+city_addis_ababa,2022,MONTH_apr,0.019629225736095966
+city_addis_ababa,2022,MONTH_may,0.003271537622682661
+city_amsterdam,2020,MONTH_jan,0
+city_amsterdam,2020,MONTH_feb,0
+city_amsterdam,2020,MONTH_mar,0.22048475371383894
+city_amsterdam,2020,MONTH_apr,1
+city_amsterdam,2020,MONTH_may,0.32212666145426117
+city_amsterdam,2020,MONTH_jun,0.041959864477456345
+city_amsterdam,2020,MONTH_jul,0.01667969768047954
+city_amsterdam,2020,MONTH_aug,0.019546520719311962
+city_amsterdam,2020,MONTH_sep,0.04508730779254626
+city_amsterdam,2020,MONTH_oct,0.24811050299713316
+city_amsterdam,2020,MONTH_nov,0.5173312483711233
+city_amsterdam,2020,MONTH_dec,0.5129007036747459
+city_amsterdam,2021,MONTH_jan,0.689080010424811
+city_amsterdam,2021,MONTH_feb,0.4162105811832161
+city_amsterdam,2021,MONTH_mar,0.2509773260359656
+city_amsterdam,2021,MONTH_apr,0.16080271045087308
+city_amsterdam,2021,MONTH_may,0.1308313786812614
+city_amsterdam,2021,MONTH_jun,0.03205629397967162
+city_amsterdam,2021,MONTH_jul,0.019807140995569454
+city_amsterdam,2021,MONTH_aug,0.046390409173833726
+city_amsterdam,2021,MONTH_sep,0.04430544696377378
+city_amsterdam,2021,MONTH_oct,0.05968204326296586
+city_amsterdam,2021,MONTH_nov,0.24758926244461818
+city_amsterdam,2021,MONTH_dec,0.40109460516028145
+city_amsterdam,2022,MONTH_jan,0.09747198332030232
+city_amsterdam,2022,MONTH_feb,0.07453739900964294
+city_amsterdam,2022,MONTH_mar,0.10685431326557206
+city_amsterdam,2022,MONTH_apr,0.07479801928590045
+city_amsterdam,2022,MONTH_may,0.019807140995569454
+city_ankara,2020,MONTH_jan,0
+city_ankara,2020,MONTH_feb,0
+city_ankara,2020,MONTH_mar,0.020114942528735632
+city_ankara,2020,MONTH_apr,0.3487787356321839
+city_ankara,2020,MONTH_may,0.17169540229885058
+city_ankara,2020,MONTH_jun,0.07183908045977011
+city_ankara,2020,MONTH_jul,0.06693007662835249
+city_ankara,2020,MONTH_aug,0.07806513409961686
+city_ankara,2020,MONTH_sep,0.21599616858237547
+city_ankara,2020,MONTH_oct,0.24509099616858238
+city_ankara,2020,MONTH_nov,0.4048132183908046
+city_ankara,2020,MONTH_dec,0.8481800766283525
+city_ankara,2021,MONTH_jan,0.6253591954022989
+city_ankara,2021,MONTH_feb,0.3158524904214559
+city_ankara,2021,MONTH_mar,0.3450670498084291
+city_ankara,2021,MONTH_apr,1
+city_ankara,2021,MONTH_may,0.9181034482758621
+city_ankara,2021,MONTH_jun,0.273227969348659
+city_ankara,2021,MONTH_jul,0.1875
+city_ankara,2021,MONTH_aug,0.6232040229885057
+city_ankara,2021,MONTH_sep,0.8836206896551724
+city_ankara,2021,MONTH_oct,0.7868773946360154
+city_ankara,2021,MONTH_nov,0.7453304597701149
+city_ankara,2021,MONTH_dec,0.6660680076628352
+city_ankara,2022,MONTH_jan,0.6029693486590039
+city_ankara,2022,MONTH_feb,0.8378831417624522
+city_ankara,2022,MONTH_mar,0.4483955938697318
+city_ankara,2022,MONTH_apr,0.09375
+city_ankara,2022,MONTH_may,0.024066091954022987
+city_baghdad,2020,MONTH_jan,0
+city_baghdad,2020,MONTH_feb,0
+city_baghdad,2020,MONTH_mar,0.016242937853107344
+city_baghdad,2020,MONTH_apr,0.016242937853107344
+city_baghdad,2020,MONTH_may,0.03637005649717514
+city_baghdad,2020,MONTH_jun,0.5805084745762712
+city_baghdad,2020,MONTH_jul,1
+city_baghdad,2020,MONTH_aug,0.807909604519774
+city_baghdad,2020,MONTH_sep,0.763771186440678
+city_baghdad,2020,MONTH_oct,0.614406779661017
+city_baghdad,2020,MONTH_nov,0.4809322033898305
+city_baghdad,2020,MONTH_dec,0.2062146892655367
+city_baghdad,2021,MONTH_jan,0.08227401129943503
+city_baghdad,2021,MONTH_feb,0.12076271186440678
+city_baghdad,2021,MONTH_mar,0.3188559322033898
+city_baghdad,2021,MONTH_apr,0.4050141242937853
+city_baghdad,2021,MONTH_may,0.3241525423728814
+city_baghdad,2021,MONTH_jun,0.2842514124293785
+city_baghdad,2021,MONTH_jul,0.5081214689265536
+city_baghdad,2021,MONTH_aug,0.7658898305084746
+city_baghdad,2021,MONTH_sep,0.5144774011299436
+city_baghdad,2021,MONTH_oct,0.3237994350282486
+city_baghdad,2021,MONTH_nov,0.23622881355932204
+city_baghdad,2021,MONTH_dec,0.12252824858757062
+city_baghdad,2022,MONTH_jan,0.07838983050847458
+city_baghdad,2022,MONTH_feb,0.21151129943502825
+city_baghdad,2022,MONTH_mar,0.06638418079096045
+city_baghdad,2022,MONTH_apr,0.01694915254237288
+city_baghdad,2022,MONTH_may,0.002824858757062147
+city_bangkok,2020,MONTH_jan,0
+city_bangkok,2020,MONTH_feb,0
+city_bangkok,2020,MONTH_mar,0.0014854426619132501
+city_bangkok,2020,MONTH_apr,0.006535947712418301
+city_bangkok,2020,MONTH_may,4.45632798573975e-4
+city_bangkok,2020,MONTH_jun,1.4854426619132502e-4
+city_bangkok,2020,MONTH_jul,0
+city_bangkok,2020,MONTH_aug,0
+city_bangkok,2020,MONTH_sep,1.4854426619132502e-4
+city_bangkok,2020,MONTH_oct,0
+city_bangkok,2020,MONTH_nov,1.4854426619132502e-4
+city_bangkok,2020,MONTH_dec,1.4854426619132502e-4
+city_bangkok,2021,MONTH_jan,0.0023767082590612004
+city_bangkok,2021,MONTH_feb,8.9126559714795e-4
+city_bangkok,2021,MONTH_mar,0.0016339869281045752
+city_bangkok,2021,MONTH_apr,0.016191325014854426
+city_bangkok,2021,MONTH_may,0.12299465240641712
+city_bangkok,2021,MONTH_jun,0.1473559120617944
+city_bangkok,2021,MONTH_jul,0.4209744503862151
+city_bangkok,2021,MONTH_aug,1
+city_bangkok,2021,MONTH_sep,0.7632204396910279
+city_bangkok,2021,MONTH_oct,0.3680926916221034
+city_bangkok,2021,MONTH_nov,0.232620320855615
+city_bangkok,2021,MONTH_dec,0.13770053475935828
+city_bangkok,2022,MONTH_jan,0.07055852644087938
+city_bangkok,2022,MONTH_feb,0.11289364230540701
+city_bangkok,2022,MONTH_mar,0.3263517528223411
+city_bangkok,2022,MONTH_apr,0.5044563279857398
+city_bangkok,2022,MONTH_may,0.22177658942364825
+city_beijing,2020,MONTH_jan,0
+city_beijing,2020,MONTH_feb,0.3723932472691162
+city_beijing,2020,MONTH_mar,0.06752730883813307
+city_beijing,2020,MONTH_apr,0.1885373811888211
+city_beijing,2020,MONTH_may,2.837281883955171e-4
+city_beijing,2020,MONTH_jun,4.2559228259327563e-4
+city_beijing,2020,MONTH_jul,0.0025535536955596538
+city_beijing,2020,MONTH_aug,0.00893743793445879
+city_beijing,2020,MONTH_sep,0.0024116896013618955
+city_beijing,2020,MONTH_oct,0
+city_beijing,2020,MONTH_nov,5.674563767910342e-4
+city_beijing,2020,MONTH_dec,0.005390835579514825
+city_beijing,2021,MONTH_jan,0.004965243296921549
+city_beijing,2021,MONTH_feb,0.002837281883955171
+city_beijing,2021,MONTH_mar,0.0011349127535820683
+city_beijing,2021,MONTH_apr,8.511845651865513e-4
+city_beijing,2021,MONTH_may,0.013902681231380337
+city_beijing,2021,MONTH_jun,0.07589729039580083
+city_beijing,2021,MONTH_jul,0.02057029365867499
+city_beijing,2021,MONTH_aug,0.006667612427294652
+city_beijing,2021,MONTH_sep,0.0012767768477798269
+city_beijing,2021,MONTH_oct,7.093204709887927e-4
+city_beijing,2021,MONTH_nov,1.4186409419775854e-4
+city_beijing,2021,MONTH_dec,2.837281883955171e-4
+city_beijing,2022,MONTH_jan,1.4186409419775854e-4
+city_beijing,2022,MONTH_feb,0.06355511420059583
+city_beijing,2022,MONTH_mar,1
+city_beijing,2022,MONTH_apr,0.2816002269825507
+city_beijing,2022,MONTH_may,0.22513831749184282
+city_belgrade,2020,MONTH_jan,0
+city_belgrade,2020,MONTH_feb,0
+city_belgrade,2020,MONTH_mar,0.007441327990841442
+city_belgrade,2020,MONTH_apr,0.09158557527189468
+city_belgrade,2020,MONTH_may,0.03949627933600458
+city_belgrade,2020,MONTH_jun,0.018317115054378934
+city_belgrade,2020,MONTH_jul,0.16657126502575845
+city_belgrade,2020,MONTH_aug,0.0835718374356039
+city_belgrade,2020,MONTH_sep,0.021751574127074985
+city_belgrade,2020,MONTH_oct,0.03720663995420721
+city_belgrade,2020,MONTH_nov,0.42072123640526615
+city_belgrade,2020,MONTH_dec,0.9238694905552376
+city_belgrade,2021,MONTH_jan,0.47910704064109905
+city_belgrade,2021,MONTH_feb,0.2455638236977676
+city_belgrade,2021,MONTH_mar,0.4813966800228964
+city_belgrade,2021,MONTH_apr,0.6107613050944476
+city_belgrade,2021,MONTH_may,0.2959358900973097
+city_belgrade,2021,MONTH_jun,0.10818546078992558
+city_belgrade,2021,MONTH_jul,0.03949627933600458
+city_belgrade,2021,MONTH_aug,0.09502003434459072
+city_belgrade,2021,MONTH_sep,0.5203205495134516
+city_belgrade,2021,MONTH_oct,0.9748139668002289
+city_belgrade,2021,MONTH_nov,1
+city_belgrade,2021,MONTH_dec,0.6016027475672582
+city_belgrade,2022,MONTH_jan,0.5048654836863194
+city_belgrade,2022,MONTH_feb,0.9324556382369776
+city_belgrade,2022,MONTH_mar,0.3371493989696623
+city_belgrade,2022,MONTH_apr,0.11219232970807098
+city_belgrade,2022,MONTH_may,0.05495134516313681
+city_berlin,2020,MONTH_jan,0
+city_berlin,2020,MONTH_feb,3.875217981011432e-5
+city_berlin,2020,MONTH_mar,0.08913001356326293
+city_berlin,2020,MONTH_apr,0.23445068785119164
+city_berlin,2020,MONTH_may,0.029722921914357683
+city_berlin,2020,MONTH_jun,0.0056965704320868045
+city_berlin,2020,MONTH_jul,0.005076535555124976
+city_berlin,2020,MONTH_aug,0.004999031195504747
+city_berlin,2020,MONTH_sep,0.012749467157527611
+city_berlin,2020,MONTH_oct,0.11893043983724085
+city_berlin,2020,MONTH_nov,0.44944778143770586
+city_berlin,2020,MONTH_dec,1
+city_berlin,2021,MONTH_jan,0.7780662662274753
+city_berlin,2021,MONTH_feb,0.2610734353807402
+city_berlin,2021,MONTH_mar,0.21472582832784345
+city_berlin,2021,MONTH_apr,0.26177097461732224
+city_berlin,2021,MONTH_may,0.10831234256926953
+city_berlin,2021,MONTH_jun,0.011393140864173609
+city_berlin,2021,MONTH_jul,0.0063553574888587485
+city_berlin,2021,MONTH_aug,0.033946909513660146
+city_berlin,2021,MONTH_sep,0.06859135826390235
+city_berlin,2021,MONTH_oct,0.13466382484014724
+city_berlin,2021,MONTH_nov,0.3957372602208874
+city_berlin,2021,MONTH_dec,0.346444487502422
+city_berlin,2022,MONTH_jan,0.14357682619647355
+city_berlin,2022,MONTH_feb,0.24545630691726408
+city_berlin,2022,MONTH_mar,0.2939740360395272
+city_berlin,2022,MONTH_apr,0.1602402635148227
+city_berlin,2022,MONTH_may,0.05619066072466576
+city_bogota,2020,MONTH_jan,0
+city_bogota,2020,MONTH_feb,0
+city_bogota,2020,MONTH_mar,5.688605722737357e-4
+city_bogota,2020,MONTH_apr,0.014733488821889755
+city_bogota,2020,MONTH_may,0.033221457420786166
+city_bogota,2020,MONTH_jun,0.12816428693327264
+city_bogota,2020,MONTH_jul,0.3611126912793674
+city_bogota,2020,MONTH_aug,0.54667500995506
+city_bogota,2020,MONTH_sep,0.374139598384436
+city_bogota,2020,MONTH_oct,0.3006428124466693
+city_bogota,2020,MONTH_nov,0.31145116331987027
+city_bogota,2020,MONTH_dec,0.35377438989703625
+city_bogota,2021,MONTH_jan,0.6066329142727117
+city_bogota,2021,MONTH_feb,0.3546276807554468
+city_bogota,2021,MONTH_mar,0.2025712497866773
+city_bogota,2021,MONTH_apr,0.5487229080152455
+city_bogota,2021,MONTH_may,0.8545423516696058
+city_bogota,2021,MONTH_jun,1
+city_bogota,2021,MONTH_jul,0.8419136469651288
+city_bogota,2021,MONTH_aug,0.26651117811024516
+city_bogota,2021,MONTH_sep,0.08009556857614199
+city_bogota,2021,MONTH_oct,0.05722737357073781
+city_bogota,2021,MONTH_nov,0.06894590135957676
+city_bogota,2021,MONTH_dec,0.08129017577791683
+city_bogota,2022,MONTH_jan,0.22561010296376358
+city_bogota,2022,MONTH_feb,0.27111894874566245
+city_bogota,2022,MONTH_mar,0.05671539905569145
+city_bogota,2022,MONTH_apr,0.011035895102110473
+city_bogota,2022,MONTH_may,0.003697593719779282
+city_brasilia,2020,MONTH_jan,0
+city_brasilia,2020,MONTH_feb,0
+city_brasilia,2020,MONTH_mar,0.0016129223543922485
+city_brasilia,2020,MONTH_apr,0.05788730891021004
+city_brasilia,2020,MONTH_may,0.27112513193942056
+city_brasilia,2020,MONTH_jun,0.35275560668413997
+city_brasilia,2020,MONTH_jul,0.38558332048529986
+city_brasilia,2020,MONTH_aug,0.3596816850294714
+city_brasilia,2020,MONTH_sep,0.2561225820989338
+city_brasilia,2020,MONTH_oct,0.20055977893475965
+city_brasilia,2020,MONTH_nov,0.16119735765367235
+city_brasilia,2020,MONTH_dec,0.23861763066450029
+city_brasilia,2021,MONTH_jan,0.35561379997390863
+city_brasilia,2021,MONTH_feb,0.35779598904161575
+city_brasilia,2021,MONTH_mar,0.7238107662567156
+city_brasilia,2021,MONTH_apr,1
+city_brasilia,2021,MONTH_may,0.7456445166569812
+city_brasilia,2021,MONTH_jun,0.6289804195970066
+city_brasilia,2021,MONTH_jul,0.4791921156560206
+city_brasilia,2021,MONTH_aug,0.2942515921678388
+city_brasilia,2021,MONTH_sep,0.19139221290575079
+city_brasilia,2021,MONTH_oct,0.14250643389983278
+city_brasilia,2021,MONTH_nov,0.0808358732907174
+city_brasilia,2021,MONTH_dec,0.053831283577841296
+city_brasilia,2022,MONTH_jan,0.09140288665662544
+city_brasilia,2022,MONTH_feb,0.2655273425918239
+city_brasilia,2022,MONTH_mar,0.1224872211482584
+city_brasilia,2022,MONTH_apr,0.04724913720513763
+city_brasilia,2022,MONTH_may,0.03828318647042778
+city_brisbane,2020,MONTH_jan,0
+city_brisbane,2020,MONTH_feb,0
+city_brisbane,2020,MONTH_mar,0.009825327510917031
+city_brisbane,2020,MONTH_apr,0.039301310043668124
+city_brisbane,2020,MONTH_may,0.006550218340611353
+city_brisbane,2020,MONTH_jun,0.001091703056768559
+city_brisbane,2020,MONTH_jul,0.04639737991266375
+city_brisbane,2020,MONTH_aug,0.23034934497816595
+city_brisbane,2020,MONTH_sep,0.14792576419213974
+city_brisbane,2020,MONTH_oct,0.013646288209606987
+city_brisbane,2020,MONTH_nov,0
+city_brisbane,2020,MONTH_dec,0.001091703056768559
+city_brisbane,2021,MONTH_jan,0
+city_brisbane,2021,MONTH_feb,0
+city_brisbane,2021,MONTH_mar,0
+city_brisbane,2021,MONTH_apr,5.458515283842794e-4
+city_brisbane,2021,MONTH_may,0
+city_brisbane,2021,MONTH_jun,0
+city_brisbane,2021,MONTH_jul,0.007096069868995633
+city_brisbane,2021,MONTH_aug,0.04312227074235808
+city_brisbane,2021,MONTH_sep,0.15065502183406113
+city_brisbane,2021,MONTH_oct,0.2423580786026201
+city_brisbane,2021,MONTH_nov,0.15010917030567686
+city_brisbane,2021,MONTH_dec,0.125
+city_brisbane,2022,MONTH_jan,0.8133187772925764
+city_brisbane,2022,MONTH_feb,0.7876637554585153
+city_brisbane,2022,MONTH_mar,0.4383187772925764
+city_brisbane,2022,MONTH_apr,0.6697598253275109
+city_brisbane,2022,MONTH_may,0.6986899563318777
+city_bucharest,2020,MONTH_jan,0
+city_bucharest,2020,MONTH_feb,0
+city_bucharest,2020,MONTH_mar,0.006214743283296682
+city_bucharest,2020,MONTH_apr,0.06004398126015872
+city_bucharest,2020,MONTH_may,0.05411607228224496
+city_bucharest,2020,MONTH_jun,0.03585428817286548
+city_bucharest,2020,MONTH_jul,0.06405966153551965
+city_bucharest,2020,MONTH_aug,0.12180896835261497
+city_bucharest,2020,MONTH_sep,0.1160722822449565
+city_bucharest,2020,MONTH_oct,0.19839372788985563
+city_bucharest,2020,MONTH_nov,0.4136150683621761
+city_bucharest,2020,MONTH_dec,0.4209771488670045
+city_bucharest,2021,MONTH_jan,0.2550913089205469
+city_bucharest,2021,MONTH_feb,0.19342193326321827
+city_bucharest,2021,MONTH_mar,0.29849890046849603
+city_bucharest,2021,MONTH_apr,0.4361793670522995
+city_bucharest,2021,MONTH_may,0.22038435796921313
+city_bucharest,2021,MONTH_jun,0.3182904675399178
+city_bucharest,2021,MONTH_jul,0.06434649584090257
+city_bucharest,2021,MONTH_aug,0.02495458456831437
+city_bucharest,2021,MONTH_sep,0.2223921981068936
+city_bucharest,2021,MONTH_oct,1
+city_bucharest,2021,MONTH_nov,0.8660483793861746
+city_bucharest,2021,MONTH_dec,0.22296586671765944
+city_bucharest,2022,MONTH_jan,0.1214265226121044
+city_bucharest,2022,MONTH_feb,0.3221149249450234
+city_bucharest,2022,MONTH_mar,0.1568983650444593
+city_bucharest,2022,MONTH_apr,0.046275934601778375
+city_bucharest,2022,MONTH_may,0.019122287025528255
+city_cairo,2020,MONTH_jan,0
+city_cairo,2020,MONTH_feb,0
+city_cairo,2020,MONTH_mar,0.02092904543134252
+city_cairo,2020,MONTH_apr,0.17304747320061256
+city_cairo,2020,MONTH_may,0.2720775906074528
+city_cairo,2020,MONTH_jun,1
+city_cairo,2020,MONTH_jul,0.9709035222052067
+city_cairo,2020,MONTH_aug,0.31904032669729454
+city_cairo,2020,MONTH_sep,0.2628892291985707
+city_cairo,2020,MONTH_oct,0.1755997958141909
+city_cairo,2020,MONTH_nov,0.19295558958652373
+city_cairo,2020,MONTH_dec,0.47983665135273096
+city_cairo,2021,MONTH_jan,0.8611536498213375
+city_cairo,2021,MONTH_feb,0.7023991832567636
+city_cairo,2021,MONTH_mar,0.6722817764165391
+city_cairo,2021,MONTH_apr,0.6748340990301174
+city_cairo,2021,MONTH_may,0.9030117406840225
+city_cairo,2021,MONTH_jun,0.5620214395099541
+city_cairo,2021,MONTH_jul,0.18887187340479836
+city_cairo,2021,MONTH_aug,0.1066870852475753
+city_cairo,2021,MONTH_sep,0.2894333843797856
+city_cairo,2021,MONTH_oct,0.6625829504849413
+city_cairo,2021,MONTH_nov,0.9290454313425217
+city_cairo,2021,MONTH_dec,0.6712608473711077
+city_cairo,2022,MONTH_jan,0.4476773864216437
+city_cairo,2022,MONTH_feb,0.733027054619704
+city_cairo,2022,MONTH_mar,0.19244512506380806
+city_cairo,2022,MONTH_apr,0.09647779479326186
+city_cairo,2022,MONTH_may,0.05717202654415518
+city_chengdu,2020,MONTH_jan,0
+city_chengdu,2020,MONTH_feb,0.3723932472691162
+city_chengdu,2020,MONTH_mar,0.06752730883813307
+city_chengdu,2020,MONTH_apr,0.1885373811888211
+city_chengdu,2020,MONTH_may,2.837281883955171e-4
+city_chengdu,2020,MONTH_jun,4.2559228259327563e-4
+city_chengdu,2020,MONTH_jul,0.0025535536955596538
+city_chengdu,2020,MONTH_aug,0.00893743793445879
+city_chengdu,2020,MONTH_sep,0.0024116896013618955
+city_chengdu,2020,MONTH_oct,0
+city_chengdu,2020,MONTH_nov,5.674563767910342e-4
+city_chengdu,2020,MONTH_dec,0.005390835579514825
+city_chengdu,2021,MONTH_jan,0.004965243296921549
+city_chengdu,2021,MONTH_feb,0.002837281883955171
+city_chengdu,2021,MONTH_mar,0.0011349127535820683
+city_chengdu,2021,MONTH_apr,8.511845651865513e-4
+city_chengdu,2021,MONTH_may,0.013902681231380337
+city_chengdu,2021,MONTH_jun,0.07589729039580083
+city_chengdu,2021,MONTH_jul,0.02057029365867499
+city_chengdu,2021,MONTH_aug,0.006667612427294652
+city_chengdu,2021,MONTH_sep,0.0012767768477798269
+city_chengdu,2021,MONTH_oct,7.093204709887927e-4
+city_chengdu,2021,MONTH_nov,1.4186409419775854e-4
+city_chengdu,2021,MONTH_dec,2.837281883955171e-4
+city_chengdu,2022,MONTH_jan,1.4186409419775854e-4
+city_chengdu,2022,MONTH_feb,0.06355511420059583
+city_chengdu,2022,MONTH_mar,1
+city_chengdu,2022,MONTH_apr,0.2816002269825507
+city_chengdu,2022,MONTH_may,0.22513831749184282
+city_dhaka,2020,MONTH_jan,0
+city_dhaka,2020,MONTH_feb,0
+city_dhaka,2020,MONTH_mar,8.087997411840828e-4
+city_dhaka,2020,MONTH_apr,0.0263668715626011
+city_dhaka,2020,MONTH_may,0.07796829505014559
+city_dhaka,2020,MONTH_jun,0.19362665803946943
+city_dhaka,2020,MONTH_jul,0.20446457457133613
+city_dhaka,2020,MONTH_aug,0.18925913943707537
+city_dhaka,2020,MONTH_sep,0.15690714978971207
+city_dhaka,2020,MONTH_oct,0.10870268521514073
+city_dhaka,2020,MONTH_nov,0.11662892267874474
+city_dhaka,2020,MONTH_dec,0.14801035263668716
+city_dhaka,2021,MONTH_jan,0.0918796505985118
+city_dhaka,2021,MONTH_feb,0.045454545454545456
+city_dhaka,2021,MONTH_mar,0.10320284697508897
+city_dhaka,2021,MONTH_apr,0.38887091556130704
+city_dhaka,2021,MONTH_may,0.18909737948883856
+city_dhaka,2021,MONTH_jun,0.3047557424781624
+city_dhaka,2021,MONTH_jul,1
+city_dhaka,2021,MONTH_aug,0.8912973147848593
+city_dhaka,2021,MONTH_sep,0.21271433193141379
+city_dhaka,2021,MONTH_oct,0.05791006146878033
+city_dhaka,2021,MONTH_nov,0.018278874150760273
+city_dhaka,2021,MONTH_dec,0.014720155289550308
+city_dhaka,2022,MONTH_jan,0.05208670333225494
+city_dhaka,2022,MONTH_feb,0.10401164671627305
+city_dhaka,2022,MONTH_mar,0.013749595600129408
+city_dhaka,2022,MONTH_apr,8.087997411840828e-4
+city_dhaka,2022,MONTH_may,6.470397929472663e-4
+city_dubai,2020,MONTH_jan,0
+city_dubai,2020,MONTH_feb,0
+city_dubai,2020,MONTH_mar,0.016
+city_dubai,2020,MONTH_apr,0.24533333333333332
+city_dubai,2020,MONTH_may,0.43733333333333335
+city_dubai,2020,MONTH_jun,0.13866666666666666
+city_dubai,2020,MONTH_jul,0.09333333333333334
+city_dubai,2020,MONTH_aug,0.088
+city_dubai,2020,MONTH_sep,0.09066666666666667
+city_dubai,2020,MONTH_oct,0.19733333333333333
+city_dubai,2020,MONTH_nov,0.21333333333333335
+city_dubai,2020,MONTH_dec,0.25333333333333335
+city_dubai,2021,MONTH_jan,0.4613333333333333
+city_dubai,2021,MONTH_feb,1
+city_dubai,2021,MONTH_mar,0.744
+city_dubai,2021,MONTH_apr,0.24533333333333332
+city_dubai,2021,MONTH_may,0.248
+city_dubai,2021,MONTH_jun,0.3466666666666667
+city_dubai,2021,MONTH_jul,0.3626666666666667
+city_dubai,2021,MONTH_aug,0.256
+city_dubai,2021,MONTH_sep,0.14933333333333335
+city_dubai,2021,MONTH_oct,0.10933333333333334
+city_dubai,2021,MONTH_nov,0.02666666666666667
+city_dubai,2021,MONTH_dec,0.042666666666666665
+city_dubai,2022,MONTH_jan,0.208
+city_dubai,2022,MONTH_feb,0.16266666666666665
+city_dubai,2022,MONTH_mar,0.0026666666666666666
+city_dubai,2022,MONTH_apr,0
+city_dubai,2022,MONTH_may,0.008
+city_guatemala_city,2020,MONTH_jan,0
+city_guatemala_city,2020,MONTH_feb,0
+city_guatemala_city,2020,MONTH_mar,6.101281269066504e-4
+city_guatemala_city,2020,MONTH_apr,0.009151921903599756
+city_guatemala_city,2020,MONTH_may,0.04514948139109213
+city_guatemala_city,2020,MONTH_jun,0.3886516168395363
+city_guatemala_city,2020,MONTH_jul,0.6955460646735815
+city_guatemala_city,2020,MONTH_aug,0.5326418547895058
+city_guatemala_city,2020,MONTH_sep,0.3038438071995119
+city_guatemala_city,2020,MONTH_oct,0.2904209884075656
+city_guatemala_city,2020,MONTH_nov,0.27577791336180596
+city_guatemala_city,2020,MONTH_dec,0.3886516168395363
+city_guatemala_city,2021,MONTH_jan,0.49725442342892007
+city_guatemala_city,2021,MONTH_feb,0.4612568639414277
+city_guatemala_city,2021,MONTH_mar,0.27394752898108604
+city_guatemala_city,2021,MONTH_apr,0.41305674191580233
+city_guatemala_city,2021,MONTH_may,0.3886516168395363
+city_guatemala_city,2021,MONTH_jun,0.6162294081757169
+city_guatemala_city,2021,MONTH_jul,0.6717510677242221
+city_guatemala_city,2021,MONTH_aug,0.9993898718730934
+city_guatemala_city,2021,MONTH_sep,1
+city_guatemala_city,2021,MONTH_oct,0.9121415497254424
+city_guatemala_city,2021,MONTH_nov,0.5539963392312386
+city_guatemala_city,2021,MONTH_dec,0.10616229408175717
+city_guatemala_city,2022,MONTH_jan,0.16534472239170225
+city_guatemala_city,2022,MONTH_feb,0.3587553386211104
+city_guatemala_city,2022,MONTH_mar,0.20378279438682123
+city_guatemala_city,2022,MONTH_apr,0.1525320317266626
+city_guatemala_city,2022,MONTH_may,0.348993288590604
+city_hanoi,2020,MONTH_jan,0
+city_hanoi,2020,MONTH_feb,0
+city_hanoi,2020,MONTH_mar,0
+city_hanoi,2020,MONTH_apr,0
+city_hanoi,2020,MONTH_may,0
+city_hanoi,2020,MONTH_jun,0
+city_hanoi,2020,MONTH_jul,0
+city_hanoi,2020,MONTH_aug,0.0032313440371604563
+city_hanoi,2020,MONTH_sep,3.029385034837928e-4
+city_hanoi,2020,MONTH_oct,0
+city_hanoi,2020,MONTH_nov,0
+city_hanoi,2020,MONTH_dec,0
+city_hanoi,2021,MONTH_jan,0
+city_hanoi,2021,MONTH_feb,0
+city_hanoi,2021,MONTH_mar,0
+city_hanoi,2021,MONTH_apr,0
+city_hanoi,2021,MONTH_may,0.0012117540139351712
+city_hanoi,2021,MONTH_jun,0.003736241542966778
+city_hanoi,2021,MONTH_jul,0.10875492275068162
+city_hanoi,2021,MONTH_aug,1
+city_hanoi,2021,MONTH_sep,0.8112693123295971
+city_hanoi,2021,MONTH_oct,0.2960718974048268
+city_hanoi,2021,MONTH_nov,0.30546299101282437
+city_hanoi,2021,MONTH_dec,0.7182671917600727
+city_hanoi,2022,MONTH_jan,0.5553872563869534
+city_hanoi,2022,MONTH_feb,0.2500252448752903
+city_hanoi,2022,MONTH_mar,0.23326264768252045
+city_hanoi,2022,MONTH_apr,0.05897202867817833
+city_hanoi,2022,MONTH_may,0.004039180046450571
+city_harare,2020,MONTH_jan,0
+city_harare,2020,MONTH_feb,0
+city_harare,2020,MONTH_mar,0.003470213996529786
+city_harare,2020,MONTH_apr,0.001156737998843262
+city_harare,2020,MONTH_may,0
+city_harare,2020,MONTH_jun,0.001735106998264893
+city_harare,2020,MONTH_jul,0.026604973973395025
+city_harare,2020,MONTH_aug,0.08155002891844997
+city_harare,2020,MONTH_sep,0.017929438982070563
+city_harare,2020,MONTH_oct,0.008097165991902834
+city_harare,2020,MONTH_nov,0.019086176980913822
+city_harare,2020,MONTH_dec,0.048582995951417005
+city_harare,2021,MONTH_jan,0.4817813765182186
+city_harare,2021,MONTH_feb,0.15615962984384038
+city_harare,2021,MONTH_mar,0.0335454019664546
+city_harare,2021,MONTH_apr,0.026604973973395025
+city_harare,2021,MONTH_may,0.015615962984384037
+city_harare,2021,MONTH_jun,0.09658762290341237
+city_harare,2021,MONTH_jul,1
+city_harare,2021,MONTH_aug,0.5355696934644303
+city_harare,2021,MONTH_sep,0.1156737998843262
+city_harare,2021,MONTH_oct,0.035280508964719494
+city_harare,2021,MONTH_nov,0.0167727009832273
+city_harare,2021,MONTH_dec,0.1683053788316946
+city_harare,2022,MONTH_jan,0.19664545980335454
+city_harare,2022,MONTH_feb,0.03296703296703297
+city_harare,2022,MONTH_mar,0.026604973973395025
+city_harare,2022,MONTH_apr,0.0167727009832273
+city_harare,2022,MONTH_may,0.019664545980335454
+city_islamabad,2020,MONTH_jan,0
+city_islamabad,2020,MONTH_feb,0
+city_islamabad,2020,MONTH_mar,0.007521058965102286
+city_islamabad,2020,MONTH_apr,0.09657039711191336
+city_islamabad,2020,MONTH_may,0.34205776173285196
+city_islamabad,2020,MONTH_jun,0.848676293622142
+city_islamabad,2020,MONTH_jul,0.4954873646209386
+city_islamabad,2020,MONTH_aug,0.10318892900120337
+city_islamabad,2020,MONTH_sep,0.05565583634175692
+city_islamabad,2020,MONTH_oct,0.0950661853188929
+city_islamabad,2020,MONTH_nov,0.35800240673886885
+city_islamabad,2020,MONTH_dec,0.6203369434416366
+city_islamabad,2021,MONTH_jan,0.4741275571600481
+city_islamabad,2021,MONTH_feb,0.36522262334536704
+city_islamabad,2021,MONTH_mar,0.45697954271961494
+city_islamabad,2021,MONTH_apr,1
+city_islamabad,2021,MONTH_may,0.9193742478941035
+city_islamabad,2021,MONTH_jun,0.4566787003610108
+city_islamabad,2021,MONTH_jul,0.3131768953068592
+city_islamabad,2021,MONTH_aug,0.7145006016847172
+city_islamabad,2021,MONTH_sep,0.6077015643802648
+city_islamabad,2021,MONTH_oct,0.2253309265944645
+city_islamabad,2021,MONTH_nov,0.08393501805054152
+city_islamabad,2021,MONTH_dec,0.06107099879663057
+city_islamabad,2022,MONTH_jan,0.0983754512635379
+city_islamabad,2022,MONTH_feb,0.2782791817087846
+city_islamabad,2022,MONTH_mar,0.052948255114320095
+city_islamabad,2022,MONTH_apr,0.006016847172081829
+city_islamabad,2022,MONTH_may,0.0030084235860409147
+city_jakarta,2020,MONTH_jan,0
+city_jakarta,2020,MONTH_feb,0
+city_jakarta,2020,MONTH_mar,0.003495784495167592
+city_jakarta,2020,MONTH_apr,0.016862019329631914
+city_jakarta,2020,MONTH_may,0.02110322845979848
+city_jakarta,2020,MONTH_jun,0.032464528069093154
+city_jakarta,2020,MONTH_jul,0.057963191445609706
+city_jakarta,2020,MONTH_aug,0.05876002467612585
+city_jakarta,2020,MONTH_sep,0.08541538145177874
+city_jakarta,2020,MONTH_oct,0.0804287476866132
+city_jakarta,2020,MONTH_nov,0.07906641990540818
+city_jakarta,2020,MONTH_dec,0.13348241826033314
+city_jakarta,2021,MONTH_jan,0.20203578038247996
+city_jakarta,2021,MONTH_feb,0.15854410857495374
+city_jakarta,2021,MONTH_mar,0.12060456508328192
+city_jakarta,2021,MONTH_apr,0.11985914044828296
+city_jakarta,2021,MONTH_may,0.12998663376516553
+city_jakarta,2021,MONTH_jun,0.20339810816368498
+city_jakarta,2021,MONTH_jul,0.9157927205428747
+city_jakarta,2021,MONTH_aug,1
+city_jakarta,2021,MONTH_sep,0.22917951881554596
+city_jakarta,2021,MONTH_oct,0.037682500514085955
+city_jakarta,2021,MONTH_nov,0.010924326547398725
+city_jakarta,2021,MONTH_dec,0.006785934608266502
+city_jakarta,2022,MONTH_jan,0.0058091712934402635
+city_jakarta,2022,MONTH_feb,0.10320275550071972
+city_jakarta,2022,MONTH_mar,0.17360682706148467
+city_jakarta,2022,MONTH_apr,0.030022619782027554
+city_jakarta,2022,MONTH_may,0.008585235451367468
+city_kyiv,2020,MONTH_jan,0
+city_kyiv,2020,MONTH_feb,0
+city_kyiv,2020,MONTH_mar,7.124849282034418e-4
+city_kyiv,2020,MONTH_apr,0.013592020168804122
+city_kyiv,2020,MONTH_may,0.024498520223610654
+city_kyiv,2020,MONTH_jun,0.02471774635536556
+city_kyiv,2020,MONTH_jul,0.029266688589279843
+city_kyiv,2020,MONTH_aug,0.04735284445905952
+city_kyiv,2020,MONTH_sep,0.08615586977967774
+city_kyiv,2020,MONTH_oct,0.16809163652307355
+city_kyiv,2020,MONTH_nov,0.2812123205086046
+city_kyiv,2020,MONTH_dec,0.3401293434177354
+city_kyiv,2021,MONTH_jan,0.22876246848624357
+city_kyiv,2021,MONTH_feb,0.17949139537432862
+city_kyiv,2021,MONTH_mar,0.37504110489970405
+city_kyiv,2021,MONTH_apr,0.6171215608900581
+city_kyiv,2021,MONTH_may,0.35355694398772336
+city_kyiv,2021,MONTH_jun,0.09887098542146223
+city_kyiv,2021,MONTH_jul,0.03315795242792941
+city_kyiv,2021,MONTH_aug,0.046256713800284996
+city_kyiv,2021,MONTH_sep,0.13619423435273484
+city_kyiv,2021,MONTH_oct,0.6278088348131097
+city_kyiv,2021,MONTH_nov,1
+city_kyiv,2021,MONTH_dec,0.5439000328839197
+city_kyiv,2022,MONTH_jan,0.23588731776827798
+city_kyiv,2022,MONTH_feb,0.3148635317329826
+city_kyiv,2022,MONTH_mar,0.11010632467390113
+city_kyiv,2022,MONTH_apr,0.024662939822426833
+city_kyiv,2022,MONTH_may,0.00887865833607366
+city_la_paz,2020,MONTH_jan,0
+city_la_paz,2020,MONTH_feb,0
+city_la_paz,2020,MONTH_mar,0.001688048615800135
+city_la_paz,2020,MONTH_apr,0.01688048615800135
+city_la_paz,2020,MONTH_may,0.08271438217420661
+city_la_paz,2020,MONTH_jun,0.24105334233625927
+city_la_paz,2020,MONTH_jul,0.6056718433490884
+city_la_paz,2020,MONTH_aug,0.7191087103308575
+city_la_paz,2020,MONTH_sep,1
+city_la_paz,2020,MONTH_oct,0.27177582714382176
+city_la_paz,2020,MONTH_nov,0.08237677245104659
+city_la_paz,2020,MONTH_dec,0.06279540850776502
+city_la_paz,2021,MONTH_jan,0.3683322079675895
+city_la_paz,2021,MONTH_feb,0.4669142471303174
+city_la_paz,2021,MONTH_mar,0.20324105334233625
+city_la_paz,2021,MONTH_apr,0.23936529372045914
+city_la_paz,2021,MONTH_may,0.5114787305874409
+city_la_paz,2021,MONTH_jun,0.7413909520594193
+city_la_paz,2021,MONTH_jul,0.38926401080351114
+city_la_paz,2021,MONTH_aug,0.21168129642133693
+city_la_paz,2021,MONTH_sep,0.10297096556380823
+city_la_paz,2021,MONTH_oct,0.07022282241728561
+city_la_paz,2021,MONTH_nov,0.0800135043889264
+city_la_paz,2021,MONTH_dec,0.1650911546252532
+city_la_paz,2022,MONTH_jan,0.4189736664415935
+city_la_paz,2022,MONTH_feb,0.1826468602295746
+city_la_paz,2022,MONTH_mar,0.15597569209993248
+city_la_paz,2022,MONTH_apr,0.0054017555705604325
+city_la_paz,2022,MONTH_may,0.012491559756921
+city_lima,2020,MONTH_jan,0
+city_lima,2020,MONTH_feb,0
+city_lima,2020,MONTH_mar,0.0040497911160371726
+city_lima,2020,MONTH_apr,0.17098644385710632
+city_lima,2020,MONTH_may,0.6555119788558275
+city_lima,2020,MONTH_jun,0.7456304885326968
+city_lima,2020,MONTH_jul,0.7682240600221673
+city_lima,2020,MONTH_aug,0.7700571233694262
+city_lima,2020,MONTH_sep,0.3826839457754284
+city_lima,2020,MONTH_oct,0.1922585045613437
+city_lima,2020,MONTH_nov,0.1284849518288004
+city_lima,2020,MONTH_dec,0.14945860687185608
+city_lima,2021,MONTH_jan,0.41746951999317927
+city_lima,2021,MONTH_feb,0.784423224486316
+city_lima,2021,MONTH_mar,0.8913803393298662
+city_lima,2021,MONTH_apr,1
+city_lima,2021,MONTH_may,0.7605934009719498
+city_lima,2021,MONTH_jun,0.3705772018074857
+city_lima,2021,MONTH_jul,0.1726916190638588
+city_lima,2021,MONTH_aug,0.08504561343678063
+city_lima,2021,MONTH_sep,0.04774490578906983
+city_lima,2021,MONTH_oct,0.03700230198652912
+city_lima,2021,MONTH_nov,0.03883536533378805
+city_lima,2021,MONTH_dec,0.06292096512916702
+city_lima,2022,MONTH_jan,0.11778497740642851
+city_lima,2022,MONTH_feb,0.21570466365419047
+city_lima,2022,MONTH_mar,0.07553926165913548
+city_lima,2022,MONTH_apr,0.026387586324494842
+city_lima,2022,MONTH_may,0.01598601756330463
+city_madrid,2020,MONTH_jan,0
+city_madrid,2020,MONTH_feb,5.852744937375629e-5
+city_madrid,2020,MONTH_mar,0.5019314058293339
+city_madrid,2020,MONTH_apr,1
+city_madrid,2020,MONTH_may,0.2004565141051153
+city_madrid,2020,MONTH_jun,0.038745171485426666
+city_madrid,2020,MONTH_jul,0.010827578134144915
+city_madrid,2020,MONTH_aug,0.062214678684302936
+city_madrid,2020,MONTH_sep,0.18307386164110967
+city_madrid,2020,MONTH_oct,0.3029380779585626
+city_madrid,2020,MONTH_nov,0.5457684654102775
+city_madrid,2020,MONTH_dec,0.3399859534121503
+city_madrid,2021,MONTH_jan,0.6453821842444106
+city_madrid,2021,MONTH_feb,0.5533770338288657
+city_madrid,2021,MONTH_mar,0.18658550860353507
+city_madrid,2021,MONTH_apr,0.13666159428772093
+city_madrid,2021,MONTH_may,0.08673767997190683
+city_madrid,2021,MONTH_jun,0.03775020484607281
+city_madrid,2021,MONTH_jul,0.059229778766241366
+city_madrid,2021,MONTH_aug,0.1822544773498771
+city_madrid,2021,MONTH_sep,0.093585391548636307
+city_madrid,2021,MONTH_oct,0.03494088727613251
+city_madrid,2021,MONTH_nov,0.03616996371298139
+city_madrid,2021,MONTH_dec,0.1144796909750673
+city_madrid,2022,MONTH_jan,0.32476881657497364
+city_madrid,2022,MONTH_feb,0.27689336298724104
+city_madrid,2022,MONTH_mar,0.10125248741659838
+city_madrid,2022,MONTH_apr,0.08351867025635022
+city_madrid,2022,MONTH_may,0.10722228725272152
+city_managua,2020,MONTH_jan,0
+city_managua,2020,MONTH_feb,0
+city_managua,2020,MONTH_mar,0.023809523809523808
+city_managua,2020,MONTH_apr,0.047619047619047616
+city_managua,2020,MONTH_may,0.7619047619047619
+city_managua,2020,MONTH_jun,0.9285714285714286
+city_managua,2020,MONTH_jul,1
+city_managua,2020,MONTH_aug,0.5
+city_managua,2020,MONTH_sep,0.3333333333333333
+city_managua,2020,MONTH_oct,0.11904761904761904
+city_managua,2020,MONTH_nov,0.09523809523809523
+city_managua,2020,MONTH_dec,0.11904761904761904
+city_managua,2021,MONTH_jan,0.09523809523809523
+city_managua,2021,MONTH_feb,0.09523809523809523
+city_managua,2021,MONTH_mar,0.11904761904761904
+city_managua,2021,MONTH_apr,0.09523809523809523
+city_managua,2021,MONTH_may,0.09523809523809523
+city_managua,2021,MONTH_jun,0.11904761904761904
+city_managua,2021,MONTH_jul,0.09523809523809523
+city_managua,2021,MONTH_aug,0.09523809523809523
+city_managua,2021,MONTH_sep,0.11904761904761904
+city_managua,2021,MONTH_oct,0.09523809523809523
+city_managua,2021,MONTH_nov,0.09523809523809523
+city_managua,2021,MONTH_dec,0.11904761904761904
+city_managua,2022,MONTH_jan,0.07142857142857142
+city_managua,2022,MONTH_feb,0.09523809523809523
+city_managua,2022,MONTH_mar,0.14285714285714285
+city_managua,2022,MONTH_apr,0.09523809523809523
+city_managua,2022,MONTH_may,0.09523809523809523
+city_mexico_city,2020,MONTH_jan,0
+city_mexico_city,2020,MONTH_feb,0
+city_mexico_city,2020,MONTH_mar,0.0013482679941920763
+city_mexico_city,2020,MONTH_apr,0.1015608794855839
+city_mexico_city,2020,MONTH_may,0.40523231694669154
+city_mexico_city,2020,MONTH_jun,0.5275876374196224
+city_mexico_city,2020,MONTH_jul,0.6168066791122174
+city_mexico_city,2020,MONTH_aug,0.4998703588467123
+city_mexico_city,2020,MONTH_sep,0.34160443891308856
+city_mexico_city,2020,MONTH_oct,0.32690313213026345
+city_mexico_city,2020,MONTH_nov,0.4108328147687202
+city_mexico_city,2020,MONTH_dec,0.5958566687409251
+city_mexico_city,2021,MONTH_jan,1
+city_mexico_city,2021,MONTH_feb,0.6428386226923875
+city_mexico_city,2021,MONTH_mar,0.3536869943995022
+city_mexico_city,2021,MONTH_apr,0.20423667288944203
+city_mexico_city,2021,MONTH_may,0.10806886538062642
+city_mexico_city,2021,MONTH_jun,0.080196017423771
+city_mexico_city,2021,MONTH_jul,0.20589607965152457
+city_mexico_city,2021,MONTH_aug,0.5757104335200166
+city_mexico_city,2021,MONTH_sep,0.4466397013067828
+city_mexico_city,2021,MONTH_oct,0.22204936735117195
+city_mexico_city,2021,MONTH_nov,0.11226923874714789
+city_mexico_city,2021,MONTH_dec,0.09852727649865173
+city_mexico_city,2022,MONTH_jan,0.2316946691557768
+city_mexico_city,2022,MONTH_feb,0.27434660858743
+city_mexico_city,2022,MONTH_mar,0.04877100186683261
+city_mexico_city,2022,MONTH_apr,0.008893383115536196
+city_mexico_city,2022,MONTH_may,0.004252229827836548
+city_moscow,2020,MONTH_jan,0
+city_moscow,2020,MONTH_feb,0
+city_moscow,2020,MONTH_mar,4.6378393125085253e-4
+city_moscow,2020,MONTH_apr,0.028809166552994134
+city_moscow,2020,MONTH_may,0.09875869594871095
+city_moscow,2020,MONTH_jun,0.1262310735233938
+city_moscow,2020,MONTH_jul,0.12666757604692402
+city_moscow,2020,MONTH_aug,0.08765516300641113
+city_moscow,2020,MONTH_sep,0.09673987177738372
+city_moscow,2020,MONTH_oct,0.1982812713135998
+city_moscow,2020,MONTH_nov,0.32478515891419996
+city_moscow,2020,MONTH_dec,0.4671668258082117
+city_moscow,2021,MONTH_jan,0.4409493929886782
+city_moscow,2021,MONTH_feb,0.3530214159050607
+city_moscow,2021,MONTH_mar,0.3472377574682854
+city_moscow,2021,MONTH_apr,0.3076797162733597
+city_moscow,2021,MONTH_may,0.3102714500068203
+city_moscow,2021,MONTH_jun,0.37410994407311415
+city_moscow,2021,MONTH_jul,0.6369935888691857
+city_moscow,2021,MONTH_aug,0.6727867957986632
+city_moscow,2021,MONTH_sep,0.655599508934661
+city_moscow,2021,MONTH_oct,0.8534442777247306
+city_moscow,2021,MONTH_nov,1
+city_moscow,2021,MONTH_dec,0.9184831537307325
+city_moscow,2022,MONTH_jan,0.6135315782294366
+city_moscow,2022,MONTH_feb,0.5541126722138863
+city_moscow,2022,MONTH_mar,0.46547537852953214
+city_moscow,2022,MONTH_apr,0.1953894420952121
+city_moscow,2022,MONTH_may,0.0882007911608239
+city_mumbai,2020,MONTH_jan,0
+city_mumbai,2020,MONTH_feb,0
+city_mumbai,2020,MONTH_mar,2.6496646518175043e-4
+city_mumbai,2020,MONTH_apr,0.008627970522480748
+city_mumbai,2020,MONTH_may,0.03386602633104248
+city_mumbai,2020,MONTH_jun,0.09711848969114846
+city_mumbai,2020,MONTH_jul,0.15611492920427258
+city_mumbai,2020,MONTH_aug,0.23782396290469487
+city_mumbai,2020,MONTH_sep,0.2734785128757142
+city_mumbai,2020,MONTH_oct,0.1999171979796307
+city_mumbai,2020,MONTH_nov,0.1283265711683365
+city_mumbai,2020,MONTH_dec,0.09604206342634761
+city_mumbai,2021,MONTH_jan,0.04583919847644283
+city_mumbai,2021,MONTH_feb,0.02299412105655378
+city_mumbai,2021,MONTH_mar,0.04485385443404819
+city_mumbai,2021,MONTH_apr,0.37974662581766994
+city_mumbai,2021,MONTH_may,1
+city_mumbai,2021,MONTH_jun,0.5742651320692225
+city_mumbai,2021,MONTH_jul,0.2099528028483895
+city_mumbai,2021,MONTH_aug,0.1221329800447131
+city_mumbai,2021,MONTH_sep,0.07867847975490602
+city_mumbai,2021,MONTH_oct,0.0838287654218763
+city_mumbai,2021,MONTH_nov,0.0893765007866192
+city_mumbai,2021,MONTH_dec,0.10019044464684938
+city_mumbai,2022,MONTH_jan,0.11567442245590792
+city_mumbai,2022,MONTH_feb,0.15560983688001986
+city_mumbai,2022,MONTH_mar,0.0603295520410698
+city_mumbai,2022,MONTH_apr,0.02214126024675002
+city_mumbai,2022,MONTH_may,0.006847727084540863
+city_nairobi,2020,MONTH_jan,0
+city_nairobi,2020,MONTH_feb,0
+city_nairobi,2020,MONTH_mar,0.005037783375314861
+city_nairobi,2020,MONTH_apr,0.012594458438287154
+city_nairobi,2020,MONTH_may,0.061712846347607056
+city_nairobi,2020,MONTH_jun,0.10201511335012595
+city_nairobi,2020,MONTH_jul,0.22795969773299748
+city_nairobi,2020,MONTH_aug,0.3136020151133501
+city_nairobi,2020,MONTH_sep,0.16750629722921914
+city_nairobi,2020,MONTH_oct,0.345088161209068
+city_nairobi,2020,MONTH_nov,0.593198992443325
+city_nairobi,2020,MONTH_dec,0.2707808564231738
+city_nairobi,2021,MONTH_jan,0.11083123425692695
+city_nairobi,2021,MONTH_feb,0.12468513853904283
+city_nairobi,2021,MONTH_mar,0.3690176322418136
+city_nairobi,2021,MONTH_apr,0.7052896725440806
+city_nairobi,2021,MONTH_may,0.5667506297229219
+city_nairobi,2021,MONTH_jun,0.5843828715365239
+city_nairobi,2021,MONTH_jul,0.38413098236775817
+city_nairobi,2021,MONTH_aug,1
+city_nairobi,2021,MONTH_sep,0.5025188916876574
+city_nairobi,2021,MONTH_oct,0.1977329974811083
+city_nairobi,2021,MONTH_nov,0.07304785894206549
+city_nairobi,2021,MONTH_dec,0.05289672544080604
+city_nairobi,2022,MONTH_jan,0.25692695214105793
+city_nairobi,2022,MONTH_feb,0.0743073047858942
+city_nairobi,2022,MONTH_mar,0.011335012594458438
+city_nairobi,2022,MONTH_apr,0.0012594458438287153
+city_nairobi,2022,MONTH_may,0.0025188916876574307
+city_new_delhi,2020,MONTH_jan,0
+city_new_delhi,2020,MONTH_feb,0
+city_new_delhi,2020,MONTH_mar,2.6496646518175043e-4
+city_new_delhi,2020,MONTH_apr,0.008627970522480748
+city_new_delhi,2020,MONTH_may,0.03386602633104248
+city_new_delhi,2020,MONTH_jun,0.09711848969114846
+city_new_delhi,2020,MONTH_jul,0.15611492920427258
+city_new_delhi,2020,MONTH_aug,0.23782396290469487
+city_new_delhi,2020,MONTH_sep,0.2734785128757142
+city_new_delhi,2020,MONTH_oct,0.1999171979796307
+city_new_delhi,2020,MONTH_nov,0.1283265711683365
+city_new_delhi,2020,MONTH_dec,0.09604206342634761
+city_new_delhi,2021,MONTH_jan,0.04583919847644283
+city_new_delhi,2021,MONTH_feb,0.02299412105655378
+city_new_delhi,2021,MONTH_mar,0.04485385443404819
+city_new_delhi,2021,MONTH_apr,0.37974662581766994
+city_new_delhi,2021,MONTH_may,1
+city_new_delhi,2021,MONTH_jun,0.5742651320692225
+city_new_delhi,2021,MONTH_jul,0.2099528028483895
+city_new_delhi,2021,MONTH_aug,0.1221329800447131
+city_new_delhi,2021,MONTH_sep,0.07867847975490602
+city_new_delhi,2021,MONTH_oct,0.0838287654218763
+city_new_delhi,2021,MONTH_nov,0.0893765007866192
+city_new_delhi,2021,MONTH_dec,0.10019044464684938
+city_new_delhi,2022,MONTH_jan,0.11567442245590792
+city_new_delhi,2022,MONTH_feb,0.15560983688001986
+city_new_delhi,2022,MONTH_mar,0.0603295520410698
+city_new_delhi,2022,MONTH_apr,0.02214126024675002
+city_new_delhi,2022,MONTH_may,0.006847727084540863
+city_ottawa,2020,MONTH_jan,0
+city_ottawa,2020,MONTH_feb,0
+city_ottawa,2020,MONTH_mar,0.009571558796718322
+city_ottawa,2020,MONTH_apr,0.6406107566089334
+city_ottawa,2020,MONTH_may,0.9357338195077484
+city_ottawa,2020,MONTH_jun,0.3484503190519599
+city_ottawa,2020,MONTH_jul,0.08956244302643573
+city_ottawa,2020,MONTH_aug,0.04421148587055606
+city_ottawa,2020,MONTH_sep,0.037374658158614404
+city_ottawa,2020,MONTH_oct,0.17889699179580676
+city_ottawa,2020,MONTH_nov,0.43094804010938925
+city_ottawa,2020,MONTH_dec,0.7654968094804011
+city_ottawa,2021,MONTH_jan,1
+city_ottawa,2021,MONTH_feb,0.4731084776663628
+city_ottawa,2021,MONTH_mar,0.21923427529626252
+city_ottawa,2021,MONTH_apr,0.270510483135825
+city_ottawa,2021,MONTH_may,0.3076572470373747
+city_ottawa,2021,MONTH_jun,0.17069279854147676
+city_ottawa,2021,MONTH_jul,0.07338195077484047
+city_ottawa,2021,MONTH_aug,0.0736098450319052
+city_ottawa,2021,MONTH_sep,0.1927985414767548
+city_ottawa,2021,MONTH_oct,0.2716499544211486
+city_ottawa,2021,MONTH_nov,0.15633546034639928
+city_ottawa,2021,MONTH_dec,0.13742023701002734
+city_ottawa,2022,MONTH_jan,0.7474931631722881
+city_ottawa,2022,MONTH_feb,0.6488149498632635
+city_ottawa,2022,MONTH_mar,0.33887876025524155
+city_ottawa,2022,MONTH_apr,0.3669097538742024
+city_ottawa,2022,MONTH_may,0.4453053783044667
+city_paris,2020,MONTH_jan,0
+city_paris,2020,MONTH_feb,9.50705899130104e-5
+city_paris,2020,MONTH_mar,0.1433189142938632
+city_paris,2020,MONTH_apr,1
+city_paris,2020,MONTH_may,0.22165708038218376
+city_paris,2020,MONTH_jun,0.04815325379093977
+city_paris,2020,MONTH_jul,0.019299329752341114
+city_paris,2020,MONTH_aug,0.015734182630603222
+city_paris,2020,MONTH_sep,0.05813566573180587
+city_paris,2020,MONTH_oct,0.21737890383609831
+city_paris,2020,MONTH_nov,0.7463992014070447
+city_paris,2020,MONTH_dec,0.5722774159813662
+city_paris,2021,MONTH_jan,0.5448495507914627
+city_paris,2021,MONTH_feb,0.49341636164852404
+city_paris,2021,MONTH_mar,0.42235109568854873
+city_paris,2021,MONTH_apr,0.4173598897181157
+city_paris,2021,MONTH_may,0.23991063364548176
+city_paris,2021,MONTH_jun,0.07367970718258307
+city_paris,2021,MONTH_jul,0.03085040642677188
+city_paris,2021,MONTH_aug,0.08803536625944763
+city_paris,2021,MONTH_sep,0.08480296620240528
+city_paris,2021,MONTH_oct,0.03988211246850787
+city_paris,2021,MONTH_nov,0.06241384227789133
+city_paris,2021,MONTH_dec,0.20996339782288348
+city_paris,2022,MONTH_jan,0.32376289394875696
+city_paris,2022,MONTH_feb,0.3461995531682274
+city_paris,2022,MONTH_mar,0.18514997385558776
+city_paris,2022,MONTH_apr,0.1745496030802871
+city_paris,2022,MONTH_may,0.1104244901839616
+city_quito,2020,MONTH_jan,0
+city_quito,2020,MONTH_feb,0
+city_quito,2020,MONTH_mar,0.005972526378658172
+city_quito,2020,MONTH_apr,0.08192315349392792
+city_quito,2020,MONTH_may,0.24517220784391797
+city_quito,2020,MONTH_jun,0.11507067489548078
+city_quito,2020,MONTH_jul,0.11497113278916982
+city_quito,2020,MONTH_aug,0.08938881146725065
+city_quito,2020,MONTH_sep,0.4735217997212821
+city_quito,2020,MONTH_oct,0.1313955803304798
+city_quito,2020,MONTH_nov,0.0787378060919769
+city_quito,2020,MONTH_dec,0.059725263786581724
+city_quito,2021,MONTH_jan,0.08242086402548278
+city_quito,2021,MONTH_feb,0.08580529564005575
+city_quito,2021,MONTH_mar,0.1062114274338045
+city_quito,2021,MONTH_apr,0.17638861238303802
+city_quito,2021,MONTH_may,0.1983874178777623
+city_quito,2021,MONTH_jun,0.09954210631096955
+city_quito,2021,MONTH_jul,1
+city_quito,2021,MONTH_aug,0.0650009954210631
+city_quito,2021,MONTH_sep,0.05156281106908222
+city_quito,2021,MONTH_oct,0.02150109496316942
+city_quito,2021,MONTH_nov,0.025482779215608202
+city_quito,2021,MONTH_dec,0.04330081624527175
+city_quito,2022,MONTH_jan,0.0840135377264583
+city_quito,2022,MONTH_feb,0.07276527971331874
+city_quito,2022,MONTH_mar,0.017618952817041608
+city_quito,2022,MONTH_apr,0.016623531753931914
+city_quito,2022,MONTH_may,0.00437985267768266
+city_rabat,2020,MONTH_jan,0
+city_rabat,2020,MONTH_feb,0
+city_rabat,2020,MONTH_mar,0.011752136752136752
+city_rabat,2020,MONTH_apr,0.04807692307692308
+city_rabat,2020,MONTH_may,0.01282051282051282
+city_rabat,2020,MONTH_jun,0.007478632478632479
+city_rabat,2020,MONTH_jul,0.04309116809116809
+city_rabat,2020,MONTH_aug,0.2724358974358974
+city_rabat,2020,MONTH_sep,0.37072649572649574
+city_rabat,2020,MONTH_oct,0.5245726495726496
+city_rabat,2020,MONTH_nov,0.7706552706552706
+city_rabat,2020,MONTH_dec,0.5576923076923077
+city_rabat,2021,MONTH_jan,0.32193732193732194
+city_rabat,2021,MONTH_feb,0.1267806267806268
+city_rabat,2021,MONTH_mar,0.07051282051282051
+city_rabat,2021,MONTH_apr,0.07371794871794872
+city_rabat,2021,MONTH_may,0.0438034188034188
+city_rabat,2021,MONTH_jun,0.053062678062678066
+city_rabat,2021,MONTH_jul,0.15669515669515668
+city_rabat,2021,MONTH_aug,1
+city_rabat,2021,MONTH_sep,0.6082621082621082
+city_rabat,2021,MONTH_oct,0.1492165242165242
+city_rabat,2021,MONTH_nov,0.038461538461538464
+city_rabat,2021,MONTH_dec,0.024572649572649572
+city_rabat,2022,MONTH_jan,0.18447293447293447
+city_rabat,2022,MONTH_feb,0.21937321937321938
+city_rabat,2022,MONTH_mar,0.0292022792022792
+city_rabat,2022,MONTH_apr,0.003205128205128205
+city_rabat,2022,MONTH_may,0.002492877492877493
+city_seoul,2020,MONTH_jan,0
+city_seoul,2020,MONTH_feb,0.0019579050416054823
+city_seoul,2020,MONTH_mar,0.017865883504650026
+city_seoul,2020,MONTH_apr,0.010401370533529124
+city_seoul,2020,MONTH_may,0.0028144884973078806
+city_seoul,2020,MONTH_jun,0.0014684287812041115
+city_seoul,2020,MONTH_jul,0.00232501223690651
+city_seoul,2020,MONTH_aug,0.0028144884973078806
+city_seoul,2020,MONTH_sep,0.010890846793930495
+city_seoul,2020,MONTH_oct,0.006240822320117474
+city_seoul,2020,MONTH_nov,0.0075868820362212435
+city_seoul,2020,MONTH_dec,0.045766030347528144
+city_seoul,2021,MONTH_jan,0.06363191385217817
+city_seoul,2021,MONTH_feb,0.022393538913362702
+city_seoul,2021,MONTH_mar,0.01566324033284386
+city_seoul,2021,MONTH_apr,0.011869799314733235
+city_seoul,2021,MONTH_may,0.016030347528144886
+city_seoul,2021,MONTH_jun,0.007219774840920215
+city_seoul,2021,MONTH_jul,0.009422418012726383
+city_seoul,2021,MONTH_aug,0.0232501223690651
+city_seoul,2021,MONTH_sep,0.023984336759667158
+city_seoul,2021,MONTH_oct,0.04503181595692609
+city_seoul,2021,MONTH_nov,0.09483602545276554
+city_seoul,2021,MONTH_dec,0.23727361722956436
+city_seoul,2022,MONTH_jan,0.14586392559960842
+city_seoul,2022,MONTH_feb,0.15944689182574645
+city_seoul,2022,MONTH_mar,1
+city_seoul,2022,MONTH_apr,0.8032305433186491
+city_seoul,2022,MONTH_may,0.16911404796867352
+city_tehran,2020,MONTH_jan,0
+city_tehran,2020,MONTH_feb,0.00255845778544654
+city_tehran,2020,MONTH_mar,0.16986969714999703
+city_tehran,2020,MONTH_apr,0.18200749687630155
+city_tehran,2020,MONTH_may,0.10572975545903493
+city_tehran,2020,MONTH_jun,0.1746891176295591
+city_tehran,2020,MONTH_jul,0.3509847087523056
+city_tehran,2020,MONTH_aug,0.2911286963765098
+city_tehran,2020,MONTH_sep,0.2691735586362825
+city_tehran,2020,MONTH_oct,0.5052656631165586
+city_tehran,2020,MONTH_nov,0.7971083477122628
+city_tehran,2020,MONTH_dec,0.42958291188195397
+city_tehran,2021,MONTH_jan,0.1662402570357589
+city_tehran,2021,MONTH_feb,0.12441244719462129
+city_tehran,2021,MONTH_mar,0.1540429582911882
+city_tehran,2021,MONTH_apr,0.5225203784137562
+city_tehran,2021,MONTH_may,0.5109775688701137
+city_tehran,2021,MONTH_jun,0.24918188849883977
+city_tehran,2021,MONTH_jul,0.3699053965609567
+city_tehran,2021,MONTH_aug,1
+city_tehran,2021,MONTH_sep,0.7740227286249777
+city_tehran,2021,MONTH_oct,0.3549711429761409
+city_tehran,2021,MONTH_nov,0.213303980484322
+city_tehran,2021,MONTH_dec,0.11072767299339561
+city_tehran,2022,MONTH_jan,0.050693163562801215
+city_tehran,2022,MONTH_feb,0.25031236984589755
+city_tehran,2022,MONTH_mar,0.20985303742488248
+city_tehran,2022,MONTH_apr,0.05438210269530553
+city_tehran,2022,MONTH_may,0.014160766347355268
+city_tokyo,2020,MONTH_jan,0
+city_tokyo,2020,MONTH_feb,0.0010548523206751054
+city_tokyo,2020,MONTH_mar,0.010759493670886076
+city_tokyo,2020,MONTH_apr,0.07573839662447257
+city_tokyo,2020,MONTH_may,0.10042194092827005
+city_tokyo,2020,MONTH_jun,0.01751054852320675
+city_tokyo,2020,MONTH_jul,0.006751054852320675
+city_tokyo,2020,MONTH_aug,0.05759493670886076
+city_tokyo,2020,MONTH_sep,0.060126582278481014
+city_tokyo,2020,MONTH_oct,0.04029535864978903
+city_tokyo,2020,MONTH_nov,0.07679324894514768
+city_tokyo,2020,MONTH_dec,0.2732067510548523
+city_tokyo,2021,MONTH_jan,0.47257383966244726
+city_tokyo,2021,MONTH_feb,0.46540084388185654
+city_tokyo,2021,MONTH_mar,0.26434599156118144
+city_tokyo,2021,MONTH_apr,0.22805907172995782
+city_tokyo,2021,MONTH_may,0.5850210970464135
+city_tokyo,2021,MONTH_jun,0.3740506329113924
+city_tokyo,2021,MONTH_jul,0.09367088607594937
+city_tokyo,2021,MONTH_aug,0.17088607594936708
+city_tokyo,2021,MONTH_sep,0.33987341772151897
+city_tokyo,2021,MONTH_oct,0.13839662447257384
+city_tokyo,2021,MONTH_nov,0.02067510548523207
+city_tokyo,2021,MONTH_dec,0.007172995780590718
+city_tokyo,2022,MONTH_jan,0.07827004219409282
+city_tokyo,2022,MONTH_feb,0.950632911392405
+city_tokyo,2022,MONTH_mar,1
+city_tokyo,2022,MONTH_apr,0.32447257383966244
+city_tokyo,2022,MONTH_may,0.21814345991561182
+city_tunis,2020,MONTH_jan,0
+city_tunis,2020,MONTH_feb,0
+city_tunis,2020,MONTH_mar,0.001858352260995251
+city_tunis,2020,MONTH_apr,0.006400991121205864
+city_tunis,2020,MONTH_may,0.001651868676440223
+city_tunis,2020,MONTH_jun,4.1296716911005574e-4
+city_tunis,2020,MONTH_jul,0
+city_tunis,2020,MONTH_aug,0.005575056782985753
+city_tunis,2020,MONTH_sep,0.03881891389634524
+city_tunis,2020,MONTH_oct,0.21722073095188932
+city_tunis,2020,MONTH_nov,0.39273177782366303
+city_tunis,2020,MONTH_dec,0.2892835019615941
+city_tunis,2021,MONTH_jan,0.4253561841833574
+city_tunis,2021,MONTH_feb,0.26718975841420606
+city_tunis,2021,MONTH_mar,0.1680776378277927
+city_tunis,2021,MONTH_apr,0.38261408218046666
+city_tunis,2021,MONTH_may,0.40925046458806524
+city_tunis,2021,MONTH_jun,0.4583935577121619
+city_tunis,2021,MONTH_jul,1
+city_tunis,2021,MONTH_aug,0.7774106958496799
+city_tunis,2021,MONTH_sep,0.2925872393144745
+city_tunis,2021,MONTH_oct,0.07639892628536031
+city_tunis,2021,MONTH_nov,0.02622341523848854
+city_tunis,2021,MONTH_dec,0.041090233326450544
+city_tunis,2022,MONTH_jan,0.1459838942804047
+city_tunis,2022,MONTH_feb,0.31013834400165186
+city_tunis,2022,MONTH_mar,0.11356597150526533
+city_tunis,2022,MONTH_apr,0.04687177369399133
+city_tunis,2022,MONTH_may,0.016931653933512286
+city_ulaanbaatar,2020,MONTH_jan,0
+city_ulaanbaatar,2020,MONTH_feb,0
+city_ulaanbaatar,2020,MONTH_mar,0
+city_ulaanbaatar,2020,MONTH_apr,0
+city_ulaanbaatar,2020,MONTH_may,0
+city_ulaanbaatar,2020,MONTH_jun,0
+city_ulaanbaatar,2020,MONTH_jul,0
+city_ulaanbaatar,2020,MONTH_aug,0
+city_ulaanbaatar,2020,MONTH_sep,0
+city_ulaanbaatar,2020,MONTH_oct,0
+city_ulaanbaatar,2020,MONTH_nov,0
+city_ulaanbaatar,2020,MONTH_dec,0.002188183807439825
+city_ulaanbaatar,2021,MONTH_jan,0.002188183807439825
+city_ulaanbaatar,2021,MONTH_feb,0
+city_ulaanbaatar,2021,MONTH_mar,0.01312910284463895
+city_ulaanbaatar,2021,MONTH_apr,0.24070021881838075
+city_ulaanbaatar,2021,MONTH_may,0.29540481400437635
+city_ulaanbaatar,2021,MONTH_jun,0.5317286652078774
+city_ulaanbaatar,2021,MONTH_jul,0.474835886214442
+city_ulaanbaatar,2021,MONTH_aug,0.26914660831509846
+city_ulaanbaatar,2021,MONTH_sep,0.8052516411378556
+city_ulaanbaatar,2021,MONTH_oct,1
+city_ulaanbaatar,2021,MONTH_nov,0.5886214442013129
+city_ulaanbaatar,2021,MONTH_dec,0.12253829321663019
+city_ulaanbaatar,2022,MONTH_jan,0.10940919037199125
+city_ulaanbaatar,2022,MONTH_feb,0.13129102844638948
+city_ulaanbaatar,2022,MONTH_mar,0.0262582056892779
+city_ulaanbaatar,2022,MONTH_apr,0.00437636761487965
+city_ulaanbaatar,2022,MONTH_may,0.010940919037199124
+city_yerevan,2020,MONTH_jan,0
+city_yerevan,2020,MONTH_feb,0
+city_yerevan,2020,MONTH_mar,0.002421307506053269
+city_yerevan,2020,MONTH_apr,0.023405972558514933
+city_yerevan,2020,MONTH_may,0.07990314769975787
+city_yerevan,2020,MONTH_jun,0.25181598062953997
+city_yerevan,2020,MONTH_jul,0.23809523809523808
+city_yerevan,2020,MONTH_aug,0.11380145278450363
+city_yerevan,2020,MONTH_sep,0.0645682001614205
+city_yerevan,2020,MONTH_oct,0.3083131557707829
+city_yerevan,2020,MONTH_nov,0.6642453591606134
+city_yerevan,2020,MONTH_dec,0.5318805488297014
+city_yerevan,2021,MONTH_jan,0.20742534301856336
+city_yerevan,2021,MONTH_feb,0.0903954802259887
+city_yerevan,2021,MONTH_mar,0.26069410815173527
+city_yerevan,2021,MONTH_apr,0.47861178369652946
+city_yerevan,2021,MONTH_may,0.26634382566585957
+city_yerevan,2021,MONTH_jun,0.06133979015334948
+city_yerevan,2021,MONTH_jul,0.08071025020177562
+city_yerevan,2021,MONTH_aug,0.18563357546408393
+city_yerevan,2021,MONTH_sep,0.3833736884584342
+city_yerevan,2021,MONTH_oct,0.814366424535916
+city_yerevan,2021,MONTH_nov,1
+city_yerevan,2021,MONTH_dec,0.3268765133171913
+city_yerevan,2022,MONTH_jan,0.06537530266343826
+city_yerevan,2022,MONTH_feb,0.32929782082324455
+city_yerevan,2022,MONTH_mar,0.12267958030669895
+city_yerevan,2022,MONTH_apr,0.007263922518159807
+city_yerevan,2022,MONTH_may,0.002421307506053269
+city_chicago,2020,MONTH_jan,0
+city_chicago,2020,MONTH_feb,0
+city_chicago,2020,MONTH_mar,0.021555197421434327
+city_chicago,2020,MONTH_apr,0.4540692989524577
+city_chicago,2020,MONTH_may,0.6174456083803385
+city_chicago,2020,MONTH_jun,0.3444802578565673
+city_chicago,2020,MONTH_jul,0.11422240128928283
+city_chicago,2020,MONTH_aug,0.11180499597099114
+city_chicago,2020,MONTH_sep,0.13638195004029008
+city_chicago,2020,MONTH_oct,0.22058823529411764
+city_chicago,2020,MONTH_nov,0.6013295729250604
+city_chicago,2020,MONTH_dec,1
+city_chicago,2021,MONTH_jan,0.6573327961321515
+city_chicago,2021,MONTH_feb,0.3007655116841257
+city_chicago,2021,MONTH_mar,0.17002417405318293
+city_chicago,2021,MONTH_apr,0.1434327155519742
+city_chicago,2021,MONTH_may,0.18775181305398872
+city_chicago,2021,MONTH_jun,0.09085414987912974
+city_chicago,2021,MONTH_jul,0.048751007252215955
+city_chicago,2021,MONTH_aug,0.11764705882352941
+city_chicago,2021,MONTH_sep,0.2346897663174859
+city_chicago,2021,MONTH_oct,0.19238517324738114
+city_chicago,2021,MONTH_nov,0.1522965350523771
+city_chicago,2021,MONTH_dec,0.33058017727639
+city_chicago,2022,MONTH_jan,0.7427477840451249
+city_chicago,2022,MONTH_feb,0.4443996776792909
+city_chicago,2022,MONTH_mar,0.1524979854955681
+city_chicago,2022,MONTH_apr,0.04653505237711523
+city_chicago,2022,MONTH_may,0.021555197421434327
+city_denver,2020,MONTH_jan,0
+city_denver,2020,MONTH_feb,0
+city_denver,2020,MONTH_mar,0.03793293018141836
+city_denver,2020,MONTH_apr,0.3881253435953821
+city_denver,2020,MONTH_may,0.3683342495876855
+city_denver,2020,MONTH_jun,0.13468938977460143
+city_denver,2020,MONTH_jul,0.08301264431006047
+city_denver,2020,MONTH_aug,0.06102253985706432
+city_denver,2020,MONTH_sep,0.059923034634414514
+city_denver,2020,MONTH_oct,0.13633864760857614
+city_denver,2020,MONTH_nov,0.41286421110500277
+city_denver,2020,MONTH_dec,1
+city_denver,2021,MONTH_jan,0.45739417262231996
+city_denver,2021,MONTH_feb,0.18361737218251786
+city_denver,2021,MONTH_mar,0.07146783947223749
+city_denver,2021,MONTH_apr,0.14128642111050027
+city_denver,2021,MONTH_may,0.159978009895547
+city_denver,2021,MONTH_jun,0.12314458493677845
+city_denver,2021,MONTH_jul,0.08246289169873557
+city_denver,2021,MONTH_aug,0.12204507971412865
+city_denver,2021,MONTH_sep,0.22649807586586038
+city_denver,2021,MONTH_oct,0.35459043430456294
+city_denver,2021,MONTH_nov,0.5536008796041781
+city_denver,2021,MONTH_dec,0.6030786146234195
+city_denver,2022,MONTH_jan,0.4722374931280924
+city_denver,2022,MONTH_feb,0.3672347443650357
+city_denver,2022,MONTH_mar,0.09840571742715779
+city_denver,2022,MONTH_apr,0.09895547003848268
+city_denver,2022,MONTH_may,0.18526663001649257
+city_san_francisco,2020,MONTH_jan,0
+city_san_francisco,2020,MONTH_feb,0
+city_san_francisco,2020,MONTH_mar,0.012296999264853305
+city_san_francisco,2020,MONTH_apr,0.12517543273407739
+city_san_francisco,2020,MONTH_may,0.146026866270133
+city_san_francisco,2020,MONTH_jun,0.12303682416627681
+city_san_francisco,2020,MONTH_jul,0.20978413419768763
+city_san_francisco,2020,MONTH_aug,0.2538261043908307
+city_san_francisco,2020,MONTH_sep,0.19234110806656418
+city_san_francisco,2020,MONTH_oct,0.11782396578226291
+city_san_francisco,2020,MONTH_nov,0.1034551894673528
+city_san_francisco,2020,MONTH_dec,0.45151373387689636
+city_san_francisco,2021,MONTH_jan,1
+city_san_francisco,2021,MONTH_feb,0.7541936777384214
+city_san_francisco,2021,MONTH_mar,0.47096170554033284
+city_san_francisco,2021,MONTH_apr,0.16681146828844484
+city_san_francisco,2021,MONTH_may,0.09977945599144557
+city_san_francisco,2021,MONTH_jun,0.027601416828176167
+city_san_francisco,2021,MONTH_jul,0.05045779589654481
+city_san_francisco,2021,MONTH_aug,0.09663837465748847
+city_san_francisco,2021,MONTH_sep,0.22822963309496758
+city_san_francisco,2021,MONTH_oct,0.19153912985363897
+city_san_francisco,2021,MONTH_nov,0.16881641382075788
+city_san_francisco,2021,MONTH_dec,0.13626946467954287
+city_san_francisco,2022,MONTH_jan,0.23778654013232642
+city_san_francisco,2022,MONTH_feb,0.3540065494887389
+city_san_francisco,2022,MONTH_mar,0.23083606228697454
+city_san_francisco,2022,MONTH_apr,0.09363095635901891
+city_san_francisco,2022,MONTH_may,0.0360890195816347
+city_washington,2020,MONTH_jan,0
+city_washington,2020,MONTH_feb,0
+city_washington,2020,MONTH_mar,0.0371900826446281
+city_washington,2020,MONTH_apr,0.8884297520661157
+city_washington,2020,MONTH_may,1
+city_washington,2020,MONTH_jun,0.3512396694214876
+city_washington,2020,MONTH_jul,0.14049586776859505
+city_washington,2020,MONTH_aug,0.09090909090909091
+city_washington,2020,MONTH_sep,0.08264462809917356
+city_washington,2020,MONTH_oct,0.07851239669421488
+city_washington,2020,MONTH_nov,0.14049586776859505
+city_washington,2020,MONTH_dec,0.4380165289256198
+city_washington,2021,MONTH_jan,0.5247933884297521
+city_washington,2021,MONTH_feb,0.4297520661157025
+city_washington,2021,MONTH_mar,0.19421487603305784
+city_washington,2021,MONTH_apr,0.16942148760330578
+city_washington,2021,MONTH_may,0.1115702479338843
+city_washington,2021,MONTH_jun,0.0371900826446281
+city_washington,2021,MONTH_jul,0.03305785123966942
+city_washington,2021,MONTH_aug,0.045454545454545456
+city_washington,2021,MONTH_sep,0.05785123966942149
+city_washington,2021,MONTH_oct,0.06611570247933884
+city_washington,2021,MONTH_nov,0.024793388429752067
+city_washington,2021,MONTH_dec,0.06198347107438017
+city_washington,2022,MONTH_jan,0.32231404958677684
+city_washington,2022,MONTH_feb,0.11983471074380166
+city_washington,2022,MONTH_mar,0.05371900826446281
+city_washington,2022,MONTH_apr,0.0371900826446281
+city_washington,2022,MONTH_may,0
diff --git a/run_tree/data/incenter_data/csv/question_5.csv b/run_tree/data/incenter_data/csv/question_5.csv
new file mode 100644
index 0000000..6d63a0a
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_5.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.601593625498008
+city_brisbane,2022,MONTH_jan,0.6060606060606061
+city_chengdu,2022,MONTH_jan,0.5254237288135594
+city_new_delhi,2022,MONTH_jan,0.6507936507936508
+city_paris,2022,MONTH_jan,0.569806492883416
+city_san_francisco,2022,MONTH_jan,0.6120930232558139
+city_denver,2022,MONTH_jan,0.6120930232558139
+city_ankara,2022,MONTH_jan,0.7948003714020427
+city_harare,2022,MONTH_jan,0.5714285714285714
+city_hanoi,2022,MONTH_jan,0.6158536585365854
+city_washington,2022,MONTH_jan,0.6120930232558139
+city_bangkok,2022,MONTH_jan,0.5555555555555556
+city_tunis,2022,MONTH_jan,0.5142857142857142
+city_seoul,2022,MONTH_jan,0.6158536585365854
+city_belgrade,2022,MONTH_jan,0.6169354838709677
+city_moscow,2022,MONTH_jan,0.5819672131147541
+city_lima,2022,MONTH_jan,0.425
+city_islamabad,2022,MONTH_jan,0.6363636363636364
+city_abuja,2022,MONTH_jan,0.8333333333333334
+city_managua,2022,MONTH_jan,0.6774193548387096
+city_amsterdam,2022,MONTH_jan,0.464638783269962
+city_rabat,2022,MONTH_jan,0.6190476190476191
+city_ulaanbaatar,2022,MONTH_jan,0.5254237288135594
+city_mexico_city,2022,MONTH_jan,0.5176882661996497
+city_nairobi,2022,MONTH_jan,0.5714285714285714
+city_tokyo,2022,MONTH_jan,0.5993009868421053
+city_baghdad,2022,MONTH_jan,0.5
+city_tehran,2022,MONTH_jan,0.5
+city_jakarta,2022,MONTH_jan,0.6011644832605532
+city_guatemala_city,2022,MONTH_jan,0.6774193548387096
+city_berlin,2022,MONTH_jan,0.6111111111111112
+city_addis_ababa,2022,MONTH_jan,0.5714285714285714
+city_cairo,2022,MONTH_jan,0.5142857142857142
+city_quito,2022,MONTH_jan,0.4827586206896552
+city_bogota,2022,MONTH_jan,0.45
+city_beijing,2022,MONTH_jan,0.5254237288135594
+city_accra,2022,MONTH_jan,0.5714285714285714
+city_ottawa,2022,MONTH_jan,0.6352941176470588
+city_brasilia,2022,MONTH_jan,0.7062314540059347
+city_la_paz,2022,MONTH_jan,0.7062314540059347
+city_dhaka,2022,MONTH_jan,0.7107438016528925
+city_yerevan,2022,MONTH_jan,0.7948003714020427
+city_chicago,2022,MONTH_jan,0.6120930232558139
+city_kyiv,2022,MONTH_jan,0.59375
+city_dubai,2022,MONTH_jan,0.5
+city_mumbai,2022,MONTH_jan,0.6507936507936508
+city_madrid,2022,MONTH_jan,0.4838709677419355
diff --git a/run_tree/data/incenter_data/csv/question_6.csv b/run_tree/data/incenter_data/csv/question_6.csv
new file mode 100644
index 0000000..6c7c9d3
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_6.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.2964426877470356
+city_brisbane,2022,MONTH_jan,0.3141891891891892
+city_chengdu,2022,MONTH_jan,0.4745762711864407
+city_new_delhi,2022,MONTH_jan,0.4603174603174603
+city_paris,2022,MONTH_jan,0.3169346452283817
+city_san_francisco,2022,MONTH_jan,0.3714551371455137
+city_denver,2022,MONTH_jan,0.3714551371455137
+city_ankara,2022,MONTH_jan,0.5386046511627907
+city_harare,2022,MONTH_jan,0.21428571428571427
+city_hanoi,2022,MONTH_jan,0.4878048780487805
+city_washington,2022,MONTH_jan,0.3714551371455137
+city_bangkok,2022,MONTH_jan,0.5
+city_tunis,2022,MONTH_jan,0.4647887323943662
+city_seoul,2022,MONTH_jan,0.4878048780487805
+city_belgrade,2022,MONTH_jan,0.3089430894308943
+city_moscow,2022,MONTH_jan,0.3442622950819672
+city_lima,2022,MONTH_jan,0.225
+city_islamabad,2022,MONTH_jan,0.3719298245614035
+city_abuja,2022,MONTH_jan,0.4117647058823529
+city_managua,2022,MONTH_jan,0.375
+city_amsterdam,2022,MONTH_jan,0.2883156297420334
+city_rabat,2022,MONTH_jan,0.2926829268292683
+city_ulaanbaatar,2022,MONTH_jan,0.4745762711864407
+city_mexico_city,2022,MONTH_jan,0.3334112422578006
+city_nairobi,2022,MONTH_jan,0.21428571428571427
+city_tokyo,2022,MONTH_jan,0.8559967084961942
+city_baghdad,2022,MONTH_jan,0.3137254901960784
+city_tehran,2022,MONTH_jan,0.3137254901960784
+city_jakarta,2022,MONTH_jan,0.36627906976744184
+city_guatemala_city,2022,MONTH_jan,0.375
+city_berlin,2022,MONTH_jan,0.3335826477187734
+city_addis_ababa,2022,MONTH_jan,0.21428571428571427
+city_cairo,2022,MONTH_jan,0.4647887323943662
+city_quito,2022,MONTH_jan,0.27586206896551724
+city_bogota,2022,MONTH_jan,0.3111111111111111
+city_beijing,2022,MONTH_jan,0.4745762711864407
+city_accra,2022,MONTH_jan,0.21428571428571427
+city_ottawa,2022,MONTH_jan,0.3568075117370892
+city_brasilia,2022,MONTH_jan,0.5705794947994056
+city_la_paz,2022,MONTH_jan,0.5705794947994056
+city_dhaka,2022,MONTH_jan,0.3159340659340659
+city_yerevan,2022,MONTH_jan,0.5386046511627907
+city_chicago,2022,MONTH_jan,0.3714551371455137
+city_kyiv,2022,MONTH_jan,0.3939393939393939
+city_dubai,2022,MONTH_jan,0.3137254901960784
+city_mumbai,2022,MONTH_jan,0.4603174603174603
+city_madrid,2022,MONTH_jan,0.37209302325581395
diff --git a/run_tree/data/incenter_data/csv/question_7.csv b/run_tree/data/incenter_data/csv/question_7.csv
new file mode 100644
index 0000000..70d0c72
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_7.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.9065934065934066
+city_brisbane,2022,MONTH_jan,0.8987854251012146
+city_chengdu,2022,MONTH_jan,0.8
+city_new_delhi,2022,MONTH_jan,0.8666666666666667
+city_paris,2022,MONTH_jan,0.8818422046055115
+city_san_francisco,2022,MONTH_jan,0.9293150684931507
+city_denver,2022,MONTH_jan,0.9293150684931507
+city_ankara,2022,MONTH_jan,0.9027283511269276
+city_harare,2022,MONTH_jan,1
+city_hanoi,2022,MONTH_jan,0.9253731343283582
+city_washington,2022,MONTH_jan,0.9293150684931507
+city_bangkok,2022,MONTH_jan,0.7692307692307693
+city_tunis,2022,MONTH_jan,0.9056603773584906
+city_seoul,2022,MONTH_jan,0.9253731343283582
+city_belgrade,2022,MONTH_jan,0.9090909090909091
+city_moscow,2022,MONTH_jan,0.7934782608695652
+city_lima,2022,MONTH_jan,0.9705882352941176
+city_islamabad,2022,MONTH_jan,0.8928571428571429
+city_abuja,2022,MONTH_jan,1
+city_managua,2022,MONTH_jan,0.9047619047619048
+city_amsterdam,2022,MONTH_jan,0.924908424908425
+city_rabat,2022,MONTH_jan,0.9714285714285714
+city_ulaanbaatar,2022,MONTH_jan,0.8
+city_mexico_city,2022,MONTH_jan,0.9220470098185064
+city_nairobi,2022,MONTH_jan,1
+city_tokyo,2022,MONTH_jan,0.5873192436040044
+city_baghdad,2022,MONTH_jan,0.925
+city_tehran,2022,MONTH_jan,0.925
+city_jakarta,2022,MONTH_jan,0.8819938962360122
+city_guatemala_city,2022,MONTH_jan,0.9047619047619048
+city_berlin,2022,MONTH_jan,0.9141824751580849
+city_addis_ababa,2022,MONTH_jan,1
+city_cairo,2022,MONTH_jan,0.9056603773584906
+city_quito,2022,MONTH_jan,0.9565217391304348
+city_bogota,2022,MONTH_jan,0.8592592592592593
+city_beijing,2022,MONTH_jan,0.8
+city_accra,2022,MONTH_jan,1
+city_ottawa,2022,MONTH_jan,0.8787878787878788
+city_brasilia,2022,MONTH_jan,0.9236641221374046
+city_la_paz,2022,MONTH_jan,0.9236641221374046
+city_dhaka,2022,MONTH_jan,0.91701244813278
+city_yerevan,2022,MONTH_jan,0.9027283511269276
+city_chicago,2022,MONTH_jan,0.9293150684931507
+city_kyiv,2022,MONTH_jan,0.9090909090909091
+city_dubai,2022,MONTH_jan,0.925
+city_mumbai,2022,MONTH_jan,0.8666666666666667
+city_madrid,2022,MONTH_jan,0.8149882903981265
diff --git a/run_tree/data/incenter_data/csv/question_8.csv b/run_tree/data/incenter_data/csv/question_8.csv
new file mode 100644
index 0000000..7bea2f8
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_8.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.9398907103825137
+city_brisbane,2022,MONTH_jan,0.9061224489795918
+city_chengdu,2022,MONTH_jan,0.8717948717948718
+city_new_delhi,2022,MONTH_jan,1
+city_paris,2022,MONTH_jan,0.898988944533686
+city_san_francisco,2022,MONTH_jan,0.9071938495332235
+city_denver,2022,MONTH_jan,0.9071938495332235
+city_ankara,2022,MONTH_jan,0.9175627240143369
+city_harare,2022,MONTH_jan,1
+city_hanoi,2022,MONTH_jan,0.8962962962962963
+city_washington,2022,MONTH_jan,0.9071938495332235
+city_bangkok,2022,MONTH_jan,0.9230769230769231
+city_tunis,2022,MONTH_jan,0.8846153846153846
+city_seoul,2022,MONTH_jan,0.8962962962962963
+city_belgrade,2022,MONTH_jan,0.9179487179487179
+city_moscow,2022,MONTH_jan,0.8571428571428571
+city_lima,2022,MONTH_jan,0.9411764705882353
+city_islamabad,2022,MONTH_jan,0.8290155440414507
+city_abuja,2022,MONTH_jan,0.8571428571428571
+city_managua,2022,MONTH_jan,0.9523809523809523
+city_amsterdam,2022,MONTH_jan,0.8682027649769585
+city_rabat,2022,MONTH_jan,0.9411764705882353
+city_ulaanbaatar,2022,MONTH_jan,0.8717948717948718
+city_mexico_city,2022,MONTH_jan,0.8594192107222636
+city_nairobi,2022,MONTH_jan,1
+city_tokyo,2022,MONTH_jan,0.5911220165068035
+city_baghdad,2022,MONTH_jan,0.975
+city_tehran,2022,MONTH_jan,0.975
+city_jakarta,2022,MONTH_jan,0.9158215010141988
+city_guatemala_city,2022,MONTH_jan,0.9523809523809523
+city_berlin,2022,MONTH_jan,0.9038112522686026
+city_addis_ababa,2022,MONTH_jan,1
+city_cairo,2022,MONTH_jan,0.8846153846153846
+city_quito,2022,MONTH_jan,1
+city_bogota,2022,MONTH_jan,0.8712121212121212
+city_beijing,2022,MONTH_jan,0.8717948717948718
+city_accra,2022,MONTH_jan,1
+city_ottawa,2022,MONTH_jan,0.8953168044077136
+city_brasilia,2022,MONTH_jan,0.9504761904761905
+city_la_paz,2022,MONTH_jan,0.9504761904761905
+city_dhaka,2022,MONTH_jan,0.9159663865546218
+city_yerevan,2022,MONTH_jan,0.9175627240143369
+city_chicago,2022,MONTH_jan,0.9071938495332235
+city_kyiv,2022,MONTH_jan,0.9090909090909091
+city_dubai,2022,MONTH_jan,0.975
+city_mumbai,2022,MONTH_jan,1
+city_madrid,2022,MONTH_jan,0.9479905437352246
diff --git a/run_tree/data/incenter_data/csv/question_9.csv b/run_tree/data/incenter_data/csv/question_9.csv
new file mode 100644
index 0000000..a3f19d9
--- /dev/null
+++ b/run_tree/data/incenter_data/csv/question_9.csv
@@ -0,0 +1,48 @@
+city_data,year,month,prop
+city_bucharest,2022,MONTH_jan,0.9276054097056484
+city_brisbane,2022,MONTH_jan,0.9355913381454747
+city_chengdu,2022,MONTH_jan,0.874793524942187
+city_new_delhi,2022,MONTH_jan,0.947289156626506
+city_paris,2022,MONTH_jan,0.9259986902423052
+city_san_francisco,2022,MONTH_jan,0.9016393442622951
+city_denver,2022,MONTH_jan,0.9326923076923077
+city_ankara,2022,MONTH_jan,0.9283050145047659
+city_harare,2022,MONTH_jan,0.966996699669967
+city_hanoi,2022,MONTH_jan,0.9691666666666666
+city_washington,2022,MONTH_jan,0.9068219633943427
+city_bangkok,2022,MONTH_jan,0.9223040857334226
+city_tunis,2022,MONTH_jan,0.9776490066225165
+city_seoul,2022,MONTH_jan,0.9156626506024096
+city_belgrade,2022,MONTH_jan,0.946360153256705
+city_moscow,2022,MONTH_jan,0.8717379233759023
+city_lima,2022,MONTH_jan,0.8871428571428571
+city_islamabad,2022,MONTH_jan,0.947289156626506
+city_abuja,2022,MONTH_jan,0.9919093851132686
+city_managua,2022,MONTH_jan,0.84
+city_amsterdam,2022,MONTH_jan,0.8231389284020862
+city_rabat,2022,MONTH_jan,0.8991666666666667
+city_ulaanbaatar,2022,MONTH_jan,0.8192918192918193
+city_mexico_city,2022,MONTH_jan,0.9753015508328546
+city_nairobi,2022,MONTH_jan,0.9818181818181818
+city_tokyo,2022,MONTH_jan,0.9380134428678119
+city_baghdad,2022,MONTH_jan,0.9808013355592654
+city_tehran,2022,MONTH_jan,0.9425901201602136
+city_jakarta,2022,MONTH_jan,0.98875
+city_guatemala_city,2022,MONTH_jan,0.9072416598860863
+city_berlin,2022,MONTH_jan,0.9259986902423052
+city_addis_ababa,2022,MONTH_jan,0.9536585365853658
+city_cairo,2022,MONTH_jan,0.9975
+city_quito,2022,MONTH_jan,0.9073455759599333
+city_bogota,2022,MONTH_jan,0.8782894736842105
+city_beijing,2022,MONTH_jan,0.874793524942187
+city_accra,2022,MONTH_jan,0.9818181818181818
+city_ottawa,2022,MONTH_jan,0.8504230960676954
+city_brasilia,2022,MONTH_jan,0.8677639046538025
+city_la_paz,2022,MONTH_jan,0.8522286821705426
+city_dhaka,2022,MONTH_jan,0.99
+city_yerevan,2022,MONTH_jan,0.9705641864268193
+city_chicago,2022,MONTH_jan,0.9385113268608414
+city_kyiv,2022,MONTH_jan,0.8817120622568093
+city_dubai,2022,MONTH_jan,0.9975
+city_mumbai,2022,MONTH_jan,0.947289156626506
+city_madrid,2022,MONTH_jan,0.9259986902423052
diff --git a/run_tree/data/incenter_test_data.c b/run_tree/data/incenter_test_data.c
new file mode 100644
index 0000000..15a17af
--- /dev/null
+++ b/run_tree/data/incenter_test_data.c
@@ -0,0 +1,551 @@
+typedef struct {
+ Incenter_City_Id city;
+ u32 year;
+ Incenter_Month_Id month;
+ r32 value_0;
+ r32 value_1;
+ r32 value_2;
+} Incenter_Test_Data_Row;
+static Incenter_Test_Data_Row test_data[] = {
+[36] = { city_yerevan, 2019, MONTH_Feb, 0.222480434220212f, 0, 0 },
+[37] = { city_yerevan, 2019, MONTH_Apr, 0.4029543563896680f, 0, 0 },
+[38] = { city_yerevan, 2019, MONTH_Jun, 0.0084130412911773f, 0, 0 },
+[39] = { city_yerevan, 2019, MONTH_Oct, 0.4935333893524508f, 0, 0 },
+[40] = { city_yerevan, 2020, MONTH_Mar, 0.795608809243028f, 0, 0 },
+[41] = { city_yerevan, 2020, MONTH_May, 0.871852281333013f, 0, 0 },
+[42] = { city_yerevan, 2020, MONTH_Oct, 0.757850269654449f, 0, 0 },
+[43] = { city_yerevan, 2020, MONTH_Dec, 0.305973943715009f, 0, 0 },
+[44] = { city_yerevan, 2021, MONTH_Mar, 0.575105708333009f, 0, 0 },
+[45] = { city_yerevan, 2021, MONTH_May, 0.3241565018180455f, 0, 0 },
+[46] = { city_yerevan, 2021, MONTH_Oct, 0.1391937656411673f, 0, 0 },
+[47] = { city_yerevan, 2021, MONTH_Dec, 0.408290129008908f, 0, 0 },
+[60] = { city_dhaka, 2019, MONTH_Mar, 0.696487045082252f, 0, 0 },
+[61] = { city_dhaka, 2019, MONTH_May, 0.926874194615664f, 0, 0 },
+[62] = { city_dhaka, 2019, MONTH_Oct, 0.065784704818257f, 0, 0 },
+[63] = { city_dhaka, 2019, MONTH_Dec, 0.267978732515615f, 0, 0 },
+[64] = { city_dhaka, 2020, MONTH_Feb, 0.741594899662727f, 0, 0 },
+[65] = { city_dhaka, 2020, MONTH_Apr, 0.888792879778451f, 0, 0 },
+[66] = { city_dhaka, 2020, MONTH_Jun, 0.952616025778708f, 0, 0 },
+[67] = { city_dhaka, 2020, MONTH_Oct, 0.539424539962102f, 0, 0 },
+[68] = { city_dhaka, 2021, MONTH_Feb, 0.783291871057349f, 0, 0 },
+[69] = { city_dhaka, 2021, MONTH_Apr, 0.1670300347942025f, 0, 0 },
+[70] = { city_dhaka, 2021, MONTH_Jun, 0.916080035896739f, 0, 0 },
+[71] = { city_dhaka, 2021, MONTH_Oct, 0.04557651966926190f, 0, 0 },
+[72] = { city_sucre, 2019, MONTH_Feb, 0.905795384338579f, 0, 0 },
+[73] = { city_sucre, 2019, MONTH_Apr, 0.04894925227303792f, 0, 0 },
+[74] = { city_sucre, 2019, MONTH_Jun, 0.3974826752695833f, 0, 0 },
+[75] = { city_sucre, 2019, MONTH_Oct, 0.3240253361391306f, 0, 0 },
+[76] = { city_sucre, 2020, MONTH_Feb, 0.255506118683492f, 0, 0 },
+[77] = { city_sucre, 2020, MONTH_Apr, 0.683469845394748f, 0, 0 },
+[78] = { city_sucre, 2020, MONTH_Jun, 0.0860787488725892f, 0, 0 },
+[79] = { city_sucre, 2020, MONTH_Oct, 0.480200082551104f, 0, 0 },
+[80] = { city_sucre, 2021, MONTH_Apr, 0.933388110124702f, 0, 0 },
+[81] = { city_sucre, 2021, MONTH_Jul, 0.826055726458284f, 0, 0 },
+[82] = { city_sucre, 2021, MONTH_Sep, 0.655861858686346f, 0, 0 },
+[83] = { city_sucre, 2022, MONTH_Jan, 0.995135577230638f, 0, 0 },
+[84] = { city_brasilia, 2019, MONTH_Apr, 0.934349752963540f, 0, 0 },
+[85] = { city_brasilia, 2019, MONTH_Jul, 0.557085223032821f, 0, 0 },
+[86] = { city_brasilia, 2019, MONTH_Sep, 0.224253014743045f, 0, 0 },
+[87] = { city_brasilia, 2020, MONTH_Jan, 0.3792768359577919f, 0, 0 },
+[88] = { city_brasilia, 2020, MONTH_Feb, 0.134570768681787f, 0, 0 },
+[89] = { city_brasilia, 2020, MONTH_Apr, 0.2982662489386165f, 0, 0 },
+[90] = { city_brasilia, 2020, MONTH_Jun, 0.847116114015527f, 0, 0 },
+[91] = { city_brasilia, 2020, MONTH_Oct, 0.710668345133466f, 0, 0 },
+[92] = { city_brasilia, 2021, MONTH_Apr, 0.882130538520757f, 0, 0 },
+[93] = { city_brasilia, 2021, MONTH_Jul, 0.634126485396549f, 0, 0 },
+[94] = { city_brasilia, 2021, MONTH_Sep, 0.879304288498351f, 0, 0 },
+[95] = { city_brasilia, 2022, MONTH_Jan, 0.367836889045680f, 0, 0 },
+[96] = { city_ottawa, 2019, MONTH_Feb, 0.4644661026237456f, 0, 0 },
+[97] = { city_ottawa, 2019, MONTH_Apr, 0.27044176317875f, 0, 0 },
+[98] = { city_ottawa, 2019, MONTH_Jun, 0.78502064470336f, 0, 0 },
+[99] = { city_ottawa, 2019, MONTH_Oct, 0.439787889686537f, 0, 0 },
+[100] = { city_ottawa, 2020, MONTH_Mar, 0.625070053347673f, 0, 0 },
+[101] = { city_ottawa, 2020, MONTH_May, 0.854391809397701f, 0, 0 },
+[102] = { city_ottawa, 2020, MONTH_Oct, 0.1092476526609484f, 0, 0 },
+[103] = { city_ottawa, 2020, MONTH_Dec, 0.0698930895777035f, 0, 0 },
+[104] = { city_ottawa, 2021, MONTH_May, 0.0219222869517711f, 0, 0 },
+[105] = { city_ottawa, 2021, MONTH_Aug, 0.3118047013029038f, 0, 0 },
+[106] = { city_ottawa, 2021, MONTH_Nov, 0.800964131022624f, 0, 0 },
+[107] = { city_ottawa, 2021, MONTH_Dec, 0.893928203453701f, 0, 0 },
+[108] = { city_santiago, 2019, MONTH_Feb, 0.260406662278948f, 0, 0 },
+[109] = { city_santiago, 2019, MONTH_Apr, 0.0440821965051458f, 0, 0 },
+[110] = { city_santiago, 2019, MONTH_Jun, 0.03370085386772181f, 0, 0 },
+[111] = { city_santiago, 2019, MONTH_Oct, 0.0702001450638586f, 0, 0 },
+[112] = { city_santiago, 2020, MONTH_Mar, 0.66916706280028f, 0, 0 },
+[113] = { city_santiago, 2020, MONTH_May, 0.394667241732750f, 0, 0 },
+[114] = { city_santiago, 2020, MONTH_Oct, 0.9696170778040f, 0, 0 },
+[115] = { city_santiago, 2020, MONTH_Dec, 0.692669953953974f, 0, 0 },
+[116] = { city_santiago, 2021, MONTH_Feb, 0.885578817071499f, 0, 0 },
+[117] = { city_santiago, 2021, MONTH_Apr, 0.02248585274337977f, 0, 0 },
+[118] = { city_santiago, 2021, MONTH_Jun, 0.01661168283700642f, 0, 0 },
+[119] = { city_santiago, 2021, MONTH_Oct, 0.738806466943267f, 0, 0 },
+[120] = { city_beijing, 2019, MONTH_Mar, 0.398464980892838f, 0, 0 },
+[121] = { city_beijing, 2019, MONTH_May, 0.551648164638685f, 0, 0 },
+[122] = { city_beijing, 2019, MONTH_Oct, 0.021236210735964f, 0, 0 },
+[123] = { city_beijing, 2019, MONTH_Dec, 0.2339279354599085f, 0, 0 },
+[124] = { city_beijing, 2020, MONTH_Mar, 0.897658054227696f, 0, 0 },
+[125] = { city_beijing, 2020, MONTH_May, 0.512517872887249f, 0, 0 },
+[126] = { city_beijing, 2020, MONTH_Oct, 0.0988590392525249f, 0, 0 },
+[127] = { city_beijing, 2020, MONTH_Dec, 0.99615807233472f, 0, 0 },
+[128] = { city_beijing, 2021, MONTH_May, 0.391024451068782f, 0, 0 },
+[129] = { city_beijing, 2021, MONTH_Aug, 0.591905762173565f, 0, 0 },
+[130] = { city_beijing, 2021, MONTH_Nov, 0.2283096306364279f, 0, 0 },
+[131] = { city_beijing, 2021, MONTH_Dec, 0.821811925597835f, 0, 0 },
+[132] = { city_bogota, 2019, MONTH_Feb, 0.04055961855563494f, 0, 0 },
+[133] = { city_bogota, 2019, MONTH_Apr, 0.669754494655513f, 0, 0 },
+[134] = { city_bogota, 2019, MONTH_Jun, 0.701264834768624f, 0, 0 },
+[135] = { city_bogota, 2019, MONTH_Oct, 0.926643006799590f, 0, 0 },
+[136] = { city_bogota, 2020, MONTH_May, 0.926535096485845f, 0, 0 },
+[137] = { city_bogota, 2020, MONTH_Aug, 0.562271964832498f, 0, 0 },
+[138] = { city_bogota, 2020, MONTH_Nov, 0.1850507621318452f, 0, 0 },
+[139] = { city_bogota, 2020, MONTH_Dec, 0.1320097943504098f, 0, 0 },
+[140] = { city_bogota, 2021, MONTH_Apr, 0.2549272436870123f, 0, 0 },
+[141] = { city_bogota, 2021, MONTH_Jul, 0.655188137041105f, 0, 0 },
+[142] = { city_bogota, 2021, MONTH_Sep, 0.330150715675793f, 0, 0 },
+[143] = { city_bogota, 2022, MONTH_Jan, 0.931982538146994f, 0, 0 },
+[144] = { city_nicosia, 2019, MONTH_Feb, 0.811253678048109f, 0, 0 },
+[145] = { city_nicosia, 2019, MONTH_Apr, 0.712224222819743f, 0, 0 },
+[146] = { city_nicosia, 2019, MONTH_Jun, 0.399273172446563f, 0, 0 },
+[147] = { city_nicosia, 2019, MONTH_Oct, 0.056092503429609f, 0, 0 },
+[148] = { city_nicosia, 2020, MONTH_Mar, 0.60563587382731f, 0, 0 },
+[149] = { city_nicosia, 2020, MONTH_May, 0.815528514704807f, 0, 0 },
+[150] = { city_nicosia, 2020, MONTH_Oct, 0.728314034392938f, 0, 0 },
+[151] = { city_nicosia, 2020, MONTH_Dec, 0.159377338887028f, 0, 0 },
+[152] = { city_nicosia, 2021, MONTH_May, 0.533098572342027f, 0, 0 },
+[153] = { city_nicosia, 2021, MONTH_Aug, 0.4664341277469371f, 0, 0 },
+[154] = { city_nicosia, 2021, MONTH_Nov, 0.730237961803562f, 0, 0 },
+[155] = { city_nicosia, 2021, MONTH_Dec, 0.597177425986761f, 0, 0 },
+[156] = { city_quito, 2019, MONTH_May, 0.3900500941853586f, 0, 0 },
+[157] = { city_quito, 2019, MONTH_Aug, 0.902510252650640f, 0, 0 },
+[158] = { city_quito, 2019, MONTH_Nov, 0.50747923203348f, 0, 0 },
+[159] = { city_quito, 2019, MONTH_Dec, 0.504675362331222f, 0, 0 },
+[160] = { city_quito, 2020, MONTH_Mar, 0.893239434326622f, 0, 0 },
+[161] = { city_quito, 2020, MONTH_May, 0.1972236787064364f, 0, 0 },
+[162] = { city_quito, 2020, MONTH_Oct, 0.461624391667064f, 0, 0 },
+[163] = { city_quito, 2020, MONTH_Dec, 0.2292995261096597f, 0, 0 },
+[164] = { city_quito, 2021, MONTH_Apr, 0.67583955263968f, 0, 0 },
+[165] = { city_quito, 2021, MONTH_Jul, 0.752273762232872f, 0, 0 },
+[166] = { city_quito, 2021, MONTH_Sep, 0.754769476238442f, 0, 0 },
+[167] = { city_quito, 2022, MONTH_Jan, 0.522476871813856f, 0, 0 },
+[168] = { city_cairo, 2019, MONTH_Apr, 0.1225197816901859f, 0, 0 },
+[169] = { city_cairo, 2019, MONTH_Jul, 0.64021444184204f, 0, 0 },
+[170] = { city_cairo, 2019, MONTH_Sep, 0.1129228329032401f, 0, 0 },
+[171] = { city_cairo, 2020, MONTH_Jan, 0.44379330991469f, 0, 0 },
+[172] = { city_cairo, 2020, MONTH_May, 0.369533191151981f, 0, 0 },
+[173] = { city_cairo, 2020, MONTH_Aug, 0.087571921991018f, 0, 0 },
+[174] = { city_cairo, 2020, MONTH_Nov, 0.130023762282846f, 0, 0 },
+[175] = { city_cairo, 2020, MONTH_Dec, 0.619080240994548f, 0, 0 },
+[176] = { city_cairo, 2021, MONTH_May, 0.265431673532736f, 0, 0 },
+[177] = { city_cairo, 2021, MONTH_Aug, 0.0838086831407579f, 0, 0 },
+[178] = { city_cairo, 2021, MONTH_Nov, 0.002175360042457663f, 0, 0 },
+[179] = { city_cairo, 2021, MONTH_Dec, 0.342714385178964f, 0, 0 },
+[180] = { city_addis_ababa, 2019, MONTH_Feb, 0.527483000268353f, 0, 0 },
+[181] = { city_addis_ababa, 2019, MONTH_Apr, 0.02860178841412208f, 0, 0 },
+[182] = { city_addis_ababa, 2019, MONTH_Jun, 0.2240604563219019f, 0, 0 },
+[183] = { city_addis_ababa, 2019, MONTH_Oct, 0.157084009337969f, 0, 0 },
+[184] = { city_addis_ababa, 2020, MONTH_Apr, 0.469697598033293f, 0, 0 },
+[185] = { city_addis_ababa, 2020, MONTH_Jul, 0.701891216846054f, 0, 0 },
+[186] = { city_addis_ababa, 2020, MONTH_Sep, 0.670718895080620f, 0, 0 },
+[187] = { city_addis_ababa, 2021, MONTH_Jan, 0.2222353125566932f, 0, 0 },
+[188] = { city_addis_ababa, 2021, MONTH_Mar, 0.739784695704446f, 0, 0 },
+[189] = { city_addis_ababa, 2021, MONTH_May, 0.296333188085118f, 0, 0 },
+[190] = { city_addis_ababa, 2021, MONTH_Oct, 0.2272026794508933f, 0, 0 },
+[191] = { city_addis_ababa, 2021, MONTH_Dec, 0.97410047246957f, 0, 0 },
+[192] = { city_berlin, 2019, MONTH_May, 0.966330648602629f, 0, 0 },
+[193] = { city_berlin, 2019, MONTH_Aug, 0.0176205852695634f, 0, 0 },
+[194] = { city_berlin, 2019, MONTH_Nov, 0.584547668032789f, 0, 0 },
+[195] = { city_berlin, 2019, MONTH_Dec, 0.0526735938814365f, 0, 0 },
+[196] = { city_berlin, 2020, MONTH_Feb, 0.00291208011562660f, 0, 0 },
+[197] = { city_berlin, 2020, MONTH_Apr, 0.46161889811961f, 0, 0 },
+[198] = { city_berlin, 2020, MONTH_Jun, 0.918717731989696f, 0, 0 },
+[199] = { city_berlin, 2020, MONTH_Oct, 0.526922668293464f, 0, 0 },
+[200] = { city_berlin, 2021, MONTH_Feb, 0.407533147535702f, 0, 0 },
+[201] = { city_berlin, 2021, MONTH_Apr, 0.260631254569428f, 0, 0 },
+[202] = { city_berlin, 2021, MONTH_Jun, 0.850691099863162f, 0, 0 },
+[203] = { city_berlin, 2021, MONTH_Oct, 0.447745362619697f, 0, 0 },
+[204] = { city_athens, 2019, MONTH_Apr, 0.475810196278598f, 0, 0 },
+[205] = { city_athens, 2019, MONTH_Jul, 0.727351163708599f, 0, 0 },
+[206] = { city_athens, 2019, MONTH_Sep, 0.1173467409143746f, 0, 0 },
+[207] = { city_athens, 2020, MONTH_Jan, 0.701133914670033f, 0, 0 },
+[208] = { city_athens, 2020, MONTH_May, 0.502187471083579f, 0, 0 },
+[209] = { city_athens, 2020, MONTH_Aug, 0.722469653059002f, 0, 0 },
+[210] = { city_athens, 2020, MONTH_Nov, 0.0705704436871592f, 0, 0 },
+[211] = { city_athens, 2020, MONTH_Dec, 0.412202187265480f, 0, 0 },
+[212] = { city_athens, 2021, MONTH_Apr, 0.789529374678307f, 0, 0 },
+[213] = { city_athens, 2021, MONTH_Jul, 0.4263051767962031f, 0, 0 },
+[214] = { city_athens, 2021, MONTH_Sep, 0.422014815723197f, 0, 0 },
+[215] = { city_athens, 2022, MONTH_Jan, 0.0793089415950993f, 0, 0 },
+[216] = { city_guatemala_city, 2019, MONTH_Feb, 0.560605380854630f, 0, 0 },
+[217] = { city_guatemala_city, 2019, MONTH_Apr, 0.3382526681121018f, 0, 0 },
+[218] = { city_guatemala_city, 2019, MONTH_Jun, 0.395812130966940f, 0, 0 },
+[219] = { city_guatemala_city, 2019, MONTH_Oct, 0.812327694885220f, 0, 0 },
+[220] = { city_guatemala_city, 2020, MONTH_May, 0.654610646195839f, 0, 0 },
+[221] = { city_guatemala_city, 2020, MONTH_Aug, 0.365603885209179f, 0, 0 },
+[222] = { city_guatemala_city, 2020, MONTH_Nov, 0.299155503687062f, 0, 0 },
+[223] = { city_guatemala_city, 2020, MONTH_Dec, 0.184138836513171f, 0, 0 },
+[224] = { city_guatemala_city, 2021, MONTH_Mar, 0.704976319975955f, 0, 0 },
+[225] = { city_guatemala_city, 2021, MONTH_May, 0.0361929491443968f, 0, 0 },
+[226] = { city_guatemala_city, 2021, MONTH_Oct, 0.64197021948932f, 0, 0 },
+[227] = { city_guatemala_city, 2021, MONTH_Dec, 0.729014435225115f, 0, 0 },
+[228] = { city_jakarta, 2019, MONTH_May, 0.4873064888015896f, 0, 0 },
+[229] = { city_jakarta, 2019, MONTH_Aug, 0.1332481199937597f, 0, 0 },
+[230] = { city_jakarta, 2019, MONTH_Nov, 0.745745949826538f, 0, 0 },
+[231] = { city_jakarta, 2019, MONTH_Dec, 0.677061592008742f, 0, 0 },
+[232] = { city_jakarta, 2020, MONTH_Feb, 0.097222170637766f, 0, 0 },
+[233] = { city_jakarta, 2020, MONTH_Apr, 0.454813364724139f, 0, 0 },
+[234] = { city_jakarta, 2020, MONTH_Jun, 0.599030699042925f, 0, 0 },
+[235] = { city_jakarta, 2020, MONTH_Oct, 0.4625443588285278f, 0, 0 },
+[236] = { city_jakarta, 2021, MONTH_Feb, 0.487754108957089f, 0, 0 },
+[237] = { city_jakarta, 2021, MONTH_Apr, 0.317627211073128f, 0, 0 },
+[238] = { city_jakarta, 2021, MONTH_Jun, 0.4601649895962731f, 0, 0 },
+[239] = { city_jakarta, 2021, MONTH_Oct, 0.819518311504193f, 0, 0 },
+[240] = { city_tehran, 2019, MONTH_Apr, 0.301861333672369f, 0, 0 },
+[241] = { city_tehran, 2019, MONTH_Jul, 0.690444209645852f, 0, 0 },
+[242] = { city_tehran, 2019, MONTH_Sep, 0.4404145375380147f, 0, 0 },
+[243] = { city_tehran, 2020, MONTH_Jan, 0.512216673447640f, 0, 0 },
+[244] = { city_tehran, 2020, MONTH_Mar, 0.404877102071919f, 0, 0 },
+[245] = { city_tehran, 2020, MONTH_May, 0.0425131526773253f, 0, 0 },
+[246] = { city_tehran, 2020, MONTH_Oct, 0.2593536969506149f, 0, 0 },
+[247] = { city_tehran, 2020, MONTH_Dec, 0.783890028761998f, 0, 0 },
+[248] = { city_tehran, 2021, MONTH_Mar, 0.94926354604200f, 0, 0 },
+[249] = { city_tehran, 2021, MONTH_May, 0.0569072784620124f, 0, 0 },
+[250] = { city_tehran, 2021, MONTH_Oct, 0.577568094487137f, 0, 0 },
+[251] = { city_tehran, 2021, MONTH_Dec, 0.618906187850862f, 0, 0 },
+[252] = { city_baghdad, 2019, MONTH_Mar, 0.2041098782632676f, 0, 0 },
+[253] = { city_baghdad, 2019, MONTH_May, 0.418410741640879f, 0, 0 },
+[254] = { city_baghdad, 2019, MONTH_Oct, 0.4045113808247624f, 0, 0 },
+[255] = { city_baghdad, 2019, MONTH_Dec, 0.0935036317264107f, 0, 0 },
+[256] = { city_baghdad, 2020, MONTH_May, 0.881388063687282f, 0, 0 },
+[257] = { city_baghdad, 2020, MONTH_Aug, 0.542844838193968f, 0, 0 },
+[258] = { city_baghdad, 2020, MONTH_Nov, 0.919644994222689f, 0, 0 },
+[259] = { city_baghdad, 2020, MONTH_Dec, 0.4012535241874899f, 0, 0 },
+[260] = { city_baghdad, 2021, MONTH_Feb, 0.3336959972932559f, 0, 0 },
+[261] = { city_baghdad, 2021, MONTH_Apr, 0.1796197721242466f, 0, 0 },
+[262] = { city_baghdad, 2021, MONTH_Jun, 0.828742767016648f, 0, 0 },
+[263] = { city_baghdad, 2021, MONTH_Oct, 0.1843548012255065f, 0, 0 },
+[264] = { city_tokyo, 2019, MONTH_May, 0.155891108992637f, 0, 0 },
+[265] = { city_tokyo, 2019, MONTH_Aug, 0.710329744155827f, 0, 0 },
+[266] = { city_tokyo, 2019, MONTH_Nov, 0.767037295899860f, 0, 0 },
+[267] = { city_tokyo, 2019, MONTH_Dec, 0.3455214091470476f, 0, 0 },
+[268] = { city_tokyo, 2020, MONTH_Feb, 0.3474433981102914f, 0, 0 },
+[269] = { city_tokyo, 2020, MONTH_Apr, 0.945631216030701f, 0, 0 },
+[270] = { city_tokyo, 2020, MONTH_Jun, 0.696669631520124f, 0, 0 },
+[271] = { city_tokyo, 2020, MONTH_Oct, 0.722151370281128f, 0, 0 },
+[272] = { city_tokyo, 2021, MONTH_Apr, 0.124744692100837f, 0, 0 },
+[273] = { city_tokyo, 2021, MONTH_Jul, 0.780248733655871f, 0, 0 },
+[274] = { city_tokyo, 2021, MONTH_Sep, 0.802652502830138f, 0, 0 },
+[275] = { city_tokyo, 2022, MONTH_Jan, 0.1950030931830898f, 0, 0 },
+[276] = { city_amman, 2019, MONTH_Apr, 0.913564776301748f, 0, 0 },
+[277] = { city_amman, 2019, MONTH_Jul, 0.51995965182440f, 0, 0 },
+[278] = { city_amman, 2019, MONTH_Sep, 0.0734962371079785f, 0, 0 },
+[279] = { city_amman, 2020, MONTH_Jan, 0.820928052342723f, 0, 0 },
+[280] = { city_amman, 2020, MONTH_Feb, 0.858049473312560f, 0, 0 },
+[281] = { city_amman, 2020, MONTH_Apr, 0.472228820975274f, 0, 0 },
+[282] = { city_amman, 2020, MONTH_Jun, 0.859450329852867f, 0, 0 },
+[283] = { city_amman, 2020, MONTH_Oct, 0.734397157087221f, 0, 0 },
+[284] = { city_amman, 2021, MONTH_Feb, 0.471531691187588f, 0, 0 },
+[285] = { city_amman, 2021, MONTH_Apr, 0.74666411798262f, 0, 0 },
+[286] = { city_amman, 2021, MONTH_Jun, 0.0449755300464772f, 0, 0 },
+[287] = { city_amman, 2021, MONTH_Oct, 0.989728098238887f, 0, 0 },
+[288] = { city_nairobi, 2019, MONTH_May, 0.353258469908758f, 0, 0 },
+[289] = { city_nairobi, 2019, MONTH_Aug, 0.637265803224577f, 0, 0 },
+[290] = { city_nairobi, 2019, MONTH_Nov, 0.652963298166620f, 0, 0 },
+[291] = { city_nairobi, 2019, MONTH_Dec, 0.980136667400578f, 0, 0 },
+[292] = { city_nairobi, 2020, MONTH_May, 0.1123254499523785f, 0, 0 },
+[293] = { city_nairobi, 2020, MONTH_Aug, 0.636340674934326f, 0, 0 },
+[294] = { city_nairobi, 2020, MONTH_Nov, 0.457205051760276f, 0, 0 },
+[295] = { city_nairobi, 2020, MONTH_Dec, 0.501683805374266f, 0, 0 },
+[296] = { city_nairobi, 2021, MONTH_May, 0.287133545101559f, 0, 0 },
+[297] = { city_nairobi, 2021, MONTH_Aug, 0.613486374545206f, 0, 0 },
+[298] = { city_nairobi, 2021, MONTH_Nov, 0.664668792823215f, 0, 0 },
+[299] = { city_nairobi, 2021, MONTH_Dec, 0.460460310851094f, 0, 0 },
+[300] = { city_beirut, 2019, MONTH_May, 0.2065707928015757f, 0, 0 },
+[301] = { city_beirut, 2019, MONTH_Aug, 0.0806051251653264f, 0, 0 },
+[302] = { city_beirut, 2019, MONTH_Nov, 0.312175807704065f, 0, 0 },
+[303] = { city_beirut, 2019, MONTH_Dec, 0.570445032963265f, 0, 0 },
+[304] = { city_beirut, 2020, MONTH_May, 0.4971129813508322f, 0, 0 },
+[305] = { city_beirut, 2020, MONTH_Aug, 0.265179951577338f, 0, 0 },
+[306] = { city_beirut, 2020, MONTH_Nov, 0.3758296973795538f, 0, 0 },
+[307] = { city_beirut, 2020, MONTH_Dec, 0.2321419145383638f, 0, 0 },
+[308] = { city_beirut, 2021, MONTH_Feb, 0.976920552803965f, 0, 0 },
+[309] = { city_beirut, 2021, MONTH_Apr, 0.571447163905541f, 0, 0 },
+[310] = { city_beirut, 2021, MONTH_Jun, 0.786651053940900f, 0, 0 },
+[311] = { city_beirut, 2021, MONTH_Oct, 0.1096868650743059f, 0, 0 },
+[312] = { city_tripoli, 2019, MONTH_Apr, 0.345925686602871f, 0, 0 },
+[313] = { city_tripoli, 2019, MONTH_Jul, 0.876290377343576f, 0, 0 },
+[314] = { city_tripoli, 2019, MONTH_Sep, 0.790275244077456f, 0, 0 },
+[315] = { city_tripoli, 2020, MONTH_Jan, 0.3965589139790378f, 0, 0 },
+[316] = { city_tripoli, 2020, MONTH_Mar, 0.285348770715926f, 0, 0 },
+[317] = { city_tripoli, 2020, MONTH_May, 0.496333626012515f, 0, 0 },
+[318] = { city_tripoli, 2020, MONTH_Oct, 0.01792744471676621f, 0, 0 },
+[319] = { city_tripoli, 2020, MONTH_Dec, 0.4947927621449260f, 0, 0 },
+[320] = { city_tripoli, 2021, MONTH_May, 0.1308448333921825f, 0, 0 },
+[321] = { city_tripoli, 2021, MONTH_Aug, 0.397296712656376f, 0, 0 },
+[322] = { city_tripoli, 2021, MONTH_Nov, 0.4494511222856165f, 0, 0 },
+[323] = { city_tripoli, 2021, MONTH_Dec, 0.918909285370890f, 0, 0 },
+[324] = { city_kuala_lumpur, 2019, MONTH_Feb, 0.1675625833439882f, 0, 0 },
+[325] = { city_kuala_lumpur, 2019, MONTH_Apr, 0.729200169873379f, 0, 0 },
+[326] = { city_kuala_lumpur, 2019, MONTH_Jun, 0.515728121325297f, 0, 0 },
+[327] = { city_kuala_lumpur, 2019, MONTH_Oct, 0.801495874749624f, 0, 0 },
+[328] = { city_kuala_lumpur, 2020, MONTH_May, 0.860285410440025f, 0, 0 },
+[329] = { city_kuala_lumpur, 2020, MONTH_Aug, 0.4333367319964f, 0, 0 },
+[330] = { city_kuala_lumpur, 2020, MONTH_Nov, 0.373641801398257f, 0, 0 },
+[331] = { city_kuala_lumpur, 2020, MONTH_Dec, 0.993321214324000f, 0, 0 },
+[332] = { city_kuala_lumpur, 2021, MONTH_May, 0.955896960620464f, 0, 0 },
+[333] = { city_kuala_lumpur, 2021, MONTH_Aug, 0.847466374787605f, 0, 0 },
+[334] = { city_kuala_lumpur, 2021, MONTH_Nov, 0.725930991736956f, 0, 0 },
+[335] = { city_kuala_lumpur, 2021, MONTH_Dec, 0.0771676585796252f, 0, 0 },
+[336] = { city_male, 2019, MONTH_May, 0.1185919416224812f, 0, 0 },
+[337] = { city_male, 2019, MONTH_Aug, 0.1093303633227156f, 0, 0 },
+[338] = { city_male, 2019, MONTH_Nov, 0.351298531207944f, 0, 0 },
+[339] = { city_male, 2019, MONTH_Dec, 0.939014019805290f, 0, 0 },
+[340] = { city_male, 2020, MONTH_May, 0.905825718197400f, 0, 0 },
+[341] = { city_male, 2020, MONTH_Aug, 0.4774451158431509f, 0, 0 },
+[342] = { city_male, 2020, MONTH_Nov, 0.865861494206516f, 0, 0 },
+[343] = { city_male, 2020, MONTH_Dec, 0.3879887276689698f, 0, 0 },
+[344] = { city_male, 2021, MONTH_Feb, 0.762094702658892f, 0, 0 },
+[345] = { city_male, 2021, MONTH_Apr, 0.930703136536702f, 0, 0 },
+[346] = { city_male, 2021, MONTH_Jun, 0.413267471538170f, 0, 0 },
+[347] = { city_male, 2021, MONTH_Oct, 0.0715077259848261f, 0, 0 },
+[348] = { city_mexico_city, 2019, MONTH_Mar, 0.282689271758157f, 0, 0 },
+[349] = { city_mexico_city, 2019, MONTH_May, 0.692801392386349f, 0, 0 },
+[350] = { city_mexico_city, 2019, MONTH_Oct, 0.856300831164312f, 0, 0 },
+[351] = { city_mexico_city, 2019, MONTH_Dec, 0.62752648352262f, 0, 0 },
+[352] = { city_mexico_city, 2020, MONTH_Apr, 0.992877682455008f, 0, 0 },
+[353] = { city_mexico_city, 2020, MONTH_Jul, 0.3288944801257265f, 0, 0 },
+[354] = { city_mexico_city, 2020, MONTH_Sep, 0.567301662762395f, 0, 0 },
+[355] = { city_mexico_city, 2021, MONTH_Jan, 0.653652403202057f, 0, 0 },
+[356] = { city_mexico_city, 2021, MONTH_Feb, 0.3617368415256383f, 0, 0 },
+[357] = { city_mexico_city, 2021, MONTH_Apr, 0.1999051783032370f, 0, 0 },
+[358] = { city_mexico_city, 2021, MONTH_Jun, 0.2234520522974474f, 0, 0 },
+[359] = { city_mexico_city, 2021, MONTH_Oct, 0.2249265703960294f, 0, 0 },
+[360] = { city_ulan_bator, 2019, MONTH_Feb, 0.0973111569162685f, 0, 0 },
+[361] = { city_ulan_bator, 2019, MONTH_Apr, 0.717625035182259f, 0, 0 },
+[362] = { city_ulan_bator, 2019, MONTH_Jun, 0.68888115436561f, 0, 0 },
+[363] = { city_ulan_bator, 2019, MONTH_Oct, 0.317598068282712f, 0, 0 },
+[364] = { city_ulan_bator, 2020, MONTH_Mar, 0.532042281420785f, 0, 0 },
+[365] = { city_ulan_bator, 2020, MONTH_May, 0.937731820508145f, 0, 0 },
+[366] = { city_ulan_bator, 2020, MONTH_Oct, 0.1507514606104898f, 0, 0 },
+[367] = { city_ulan_bator, 2020, MONTH_Dec, 0.0210516496711773f, 0, 0 },
+[368] = { city_ulan_bator, 2021, MONTH_Feb, 0.695258364407790f, 0, 0 },
+[369] = { city_ulan_bator, 2021, MONTH_Apr, 0.2133368779237394f, 0, 0 },
+[370] = { city_ulan_bator, 2021, MONTH_Jun, 0.4350453296437505f, 0, 0 },
+[371] = { city_ulan_bator, 2021, MONTH_Oct, 0.887232414698376f, 0, 0 },
+[372] = { city_rabat, 2019, MONTH_Apr, 0.4848684557100414f, 0, 0 },
+[373] = { city_rabat, 2019, MONTH_Jul, 0.830378389707985f, 0, 0 },
+[374] = { city_rabat, 2019, MONTH_Sep, 0.267946912678618f, 0, 0 },
+[375] = { city_rabat, 2020, MONTH_Jan, 0.21686208646062f, 0, 0 },
+[376] = { city_rabat, 2020, MONTH_Feb, 0.639484185249096f, 0, 0 },
+[377] = { city_rabat, 2020, MONTH_Apr, 0.795734886425641f, 0, 0 },
+[378] = { city_rabat, 2020, MONTH_Jun, 0.3544991360983855f, 0, 0 },
+[379] = { city_rabat, 2020, MONTH_Oct, 0.88478654358351f, 0, 0 },
+[380] = { city_rabat, 2021, MONTH_Feb, 0.754599832364994f, 0, 0 },
+[381] = { city_rabat, 2021, MONTH_Apr, 0.4417992899161763f, 0, 0 },
+[382] = { city_rabat, 2021, MONTH_Jun, 0.607116720946833f, 0, 0 },
+[383] = { city_rabat, 2021, MONTH_Oct, 0.358623776327881f, 0, 0 },
+[384] = { city_amsterdam, 2019, MONTH_Feb, 0.731625264443053f, 0, 0 },
+[385] = { city_amsterdam, 2019, MONTH_Apr, 0.566868738166083f, 0, 0 },
+[386] = { city_amsterdam, 2019, MONTH_Jun, 0.557667088829960f, 0, 0 },
+[387] = { city_amsterdam, 2019, MONTH_Oct, 0.4165140225359793f, 0, 0 },
+[388] = { city_amsterdam, 2020, MONTH_Mar, 0.1369118545708988f, 0, 0 },
+[389] = { city_amsterdam, 2020, MONTH_May, 0.996381145058562f, 0, 0 },
+[390] = { city_amsterdam, 2020, MONTH_Oct, 0.567884655995259f, 0, 0 },
+[391] = { city_amsterdam, 2020, MONTH_Dec, 0.3599824946665225f, 0, 0 },
+[392] = { city_amsterdam, 2021, MONTH_May, 0.3617612455991281f, 0, 0 },
+[393] = { city_amsterdam, 2021, MONTH_Aug, 0.145208263117762f, 0, 0 },
+[394] = { city_amsterdam, 2021, MONTH_Nov, 0.405925018466976f, 0, 0 },
+[395] = { city_amsterdam, 2021, MONTH_Dec, 0.466865583671862f, 0, 0 },
+[396] = { city_wellington, 2019, MONTH_Mar, 0.2399790368405242f, 0, 0 },
+[397] = { city_wellington, 2019, MONTH_May, 0.0513312639497346f, 0, 0 },
+[398] = { city_wellington, 2019, MONTH_Oct, 0.154820278468702f, 0, 0 },
+[399] = { city_wellington, 2019, MONTH_Dec, 0.342480035947826f, 0, 0 },
+[400] = { city_wellington, 2020, MONTH_Feb, 0.02270196410158653f, 0, 0 },
+[401] = { city_wellington, 2020, MONTH_Apr, 0.549977096428455f, 0, 0 },
+[402] = { city_wellington, 2020, MONTH_Jun, 0.825352479423318f, 0, 0 },
+[403] = { city_wellington, 2020, MONTH_Oct, 0.00533147733597216f, 0, 0 },
+[404] = { city_wellington, 2021, MONTH_May, 0.92165713944193f, 0, 0 },
+[405] = { city_wellington, 2021, MONTH_Aug, 0.1030939587069419f, 0, 0 },
+[406] = { city_wellington, 2021, MONTH_Nov, 0.626356522706967f, 0, 0 },
+[407] = { city_wellington, 2021, MONTH_Dec, 0.202081862772906f, 0, 0 },
+[408] = { city_managua, 2019, MONTH_Apr, 0.0558278728762413f, 0, 0 },
+[409] = { city_managua, 2019, MONTH_Jul, 0.4838275959297315f, 0, 0 },
+[410] = { city_managua, 2019, MONTH_Sep, 0.982787118880607f, 0, 0 },
+[411] = { city_managua, 2020, MONTH_Jan, 0.0001771442144287149f, 0, 0 },
+[412] = { city_managua, 2020, MONTH_Feb, 0.924958034469370f, 0, 0 },
+[413] = { city_managua, 2020, MONTH_Apr, 0.804497555444690f, 0, 0 },
+[414] = { city_managua, 2020, MONTH_Jun, 0.562726395776656f, 0, 0 },
+[415] = { city_managua, 2020, MONTH_Oct, 0.323725027619491f, 0, 0 },
+[416] = { city_managua, 2021, MONTH_Mar, 0.02044880108483926f, 0, 0 },
+[417] = { city_managua, 2021, MONTH_May, 0.890130453740734f, 0, 0 },
+[418] = { city_managua, 2021, MONTH_Oct, 0.731841044395474f, 0, 0 },
+[419] = { city_managua, 2021, MONTH_Dec, 0.884381400825929f, 0, 0 },
+[420] = { city_abuja, 2019, MONTH_May, 0.66478819034649f, 0, 0 },
+[421] = { city_abuja, 2019, MONTH_Aug, 0.3457386667673525f, 0, 0 },
+[422] = { city_abuja, 2019, MONTH_Nov, 0.092988823835393f, 0, 0 },
+[423] = { city_abuja, 2019, MONTH_Dec, 0.824262528959534f, 0, 0 },
+[424] = { city_abuja, 2020, MONTH_Apr, 0.61049766413043f, 0, 0 },
+[425] = { city_abuja, 2020, MONTH_Jul, 0.1304592836307013f, 0, 0 },
+[426] = { city_abuja, 2020, MONTH_Sep, 0.593187180006649f, 0, 0 },
+[427] = { city_abuja, 2021, MONTH_Jan, 0.876199072732621f, 0, 0 },
+[428] = { city_abuja, 2021, MONTH_Mar, 0.0329722159321588f, 0, 0 },
+[429] = { city_abuja, 2021, MONTH_May, 0.266368310626165f, 0, 0 },
+[430] = { city_abuja, 2021, MONTH_Oct, 0.475247255909794f, 0, 0 },
+[431] = { city_abuja, 2021, MONTH_Dec, 0.2181789145549840f, 0, 0 },
+[432] = { city_islamabad, 2019, MONTH_Feb, 0.689456535686944f, 0, 0 },
+[433] = { city_islamabad, 2019, MONTH_Apr, 0.0447373363978489f, 0, 0 },
+[434] = { city_islamabad, 2019, MONTH_Jun, 0.822514095879552f, 0, 0 },
+[435] = { city_islamabad, 2019, MONTH_Oct, 0.916703876449938f, 0, 0 },
+[436] = { city_islamabad, 2020, MONTH_Mar, 0.1893115617875816f, 0, 0 },
+[437] = { city_islamabad, 2020, MONTH_May, 0.86726306770825f, 0, 0 },
+[438] = { city_islamabad, 2020, MONTH_Oct, 0.905215849243983f, 0, 0 },
+[439] = { city_islamabad, 2020, MONTH_Dec, 0.254940376897477f, 0, 0 },
+[440] = { city_islamabad, 2021, MONTH_Mar, 0.281039684229208f, 0, 0 },
+[441] = { city_islamabad, 2021, MONTH_May, 0.887339106739559f, 0, 0 },
+[442] = { city_islamabad, 2021, MONTH_Oct, 0.933048865779014f, 0, 0 },
+[443] = { city_islamabad, 2021, MONTH_Dec, 0.0340736858267484f, 0, 0 },
+[444] = { city_lima, 2019, MONTH_Feb, 0.328346577769536f, 0, 0 },
+[445] = { city_lima, 2019, MONTH_Apr, 0.2756724157263651f, 0, 0 },
+[446] = { city_lima, 2019, MONTH_Jun, 0.609815087863952f, 0, 0 },
+[447] = { city_lima, 2019, MONTH_Oct, 0.782731155908874f, 0, 0 },
+[448] = { city_lima, 2020, MONTH_May, 0.000478011020313862f, 0, 0 },
+[449] = { city_lima, 2020, MONTH_Aug, 0.3235917681085961f, 0, 0 },
+[450] = { city_lima, 2020, MONTH_Nov, 0.792016455238391f, 0, 0 },
+[451] = { city_lima, 2020, MONTH_Dec, 0.0868050405329673f, 0, 0 },
+[452] = { city_lima, 2021, MONTH_Feb, 0.281555206177401f, 0, 0 },
+[453] = { city_lima, 2021, MONTH_Apr, 0.794656766555093f, 0, 0 },
+[454] = { city_lima, 2021, MONTH_Jun, 0.934873911408016f, 0, 0 },
+[455] = { city_lima, 2021, MONTH_Oct, 0.966444893770645f, 0, 0 },
+[456] = { city_bucharest, 2019, MONTH_Apr, 0.275421524025559f, 0, 0 },
+[457] = { city_bucharest, 2019, MONTH_Jul, 0.934257554721354f, 0, 0 },
+[458] = { city_bucharest, 2019, MONTH_Sep, 0.456175571716915f, 0, 0 },
+[459] = { city_bucharest, 2020, MONTH_Jan, 0.581535351702321f, 0, 0 },
+[460] = { city_bucharest, 2020, MONTH_Feb, 0.0955369623031351f, 0, 0 },
+[461] = { city_bucharest, 2020, MONTH_Apr, 0.797724127406068f, 0, 0 },
+[462] = { city_bucharest, 2020, MONTH_Jun, 0.1158114218862972f, 0, 0 },
+[463] = { city_bucharest, 2020, MONTH_Oct, 0.60936646395483f, 0, 0 },
+[464] = { city_bucharest, 2021, MONTH_May, 0.778649374817847f, 0, 0 },
+[465] = { city_bucharest, 2021, MONTH_Aug, 0.370947549484814f, 0, 0 },
+[466] = { city_bucharest, 2021, MONTH_Nov, 0.0640963197950903f, 0, 0 },
+[467] = { city_bucharest, 2021, MONTH_Dec, 0.643541163489257f, 0, 0 },
+[468] = { city_moscow, 2019, MONTH_Mar, 0.301010635427779f, 0, 0 },
+[469] = { city_moscow, 2019, MONTH_May, 0.456078321317001f, 0, 0 },
+[470] = { city_moscow, 2019, MONTH_Oct, 0.0538331203062519f, 0, 0 },
+[471] = { city_moscow, 2019, MONTH_Dec, 0.75192484013845f, 0, 0 },
+[472] = { city_moscow, 2020, MONTH_May, 0.872306088053229f, 0, 0 },
+[473] = { city_moscow, 2020, MONTH_Aug, 0.1239402065770388f, 0, 0 },
+[474] = { city_moscow, 2020, MONTH_Nov, 0.2020263441001254f, 0, 0 },
+[475] = { city_moscow, 2020, MONTH_Dec, 0.278274013412570f, 0, 0 },
+[476] = { city_moscow, 2021, MONTH_May, 0.2488571531010973f, 0, 0 },
+[477] = { city_moscow, 2021, MONTH_Aug, 0.0489617026735895f, 0, 0 },
+[478] = { city_moscow, 2021, MONTH_Nov, 0.868455790657478f, 0, 0 },
+[479] = { city_moscow, 2021, MONTH_Dec, 0.54311463810805f, 0, 0 },
+[480] = { city_belgrade, 2019, MONTH_May, 0.552246653164450f, 0, 0 },
+[481] = { city_belgrade, 2019, MONTH_Aug, 0.608445734269070f, 0, 0 },
+[482] = { city_belgrade, 2019, MONTH_Nov, 0.904417407475315f, 0, 0 },
+[483] = { city_belgrade, 2019, MONTH_Dec, 0.0706690624583042f, 0, 0 },
+[484] = { city_belgrade, 2020, MONTH_May, 0.000863732217009305f, 0, 0 },
+[485] = { city_belgrade, 2020, MONTH_Aug, 0.328824489783167f, 0, 0 },
+[486] = { city_belgrade, 2020, MONTH_Nov, 0.36976843567074f, 0, 0 },
+[487] = { city_belgrade, 2020, MONTH_Dec, 0.961964263330105f, 0, 0 },
+[488] = { city_belgrade, 2021, MONTH_Mar, 0.3057188881540604f, 0, 0 },
+[489] = { city_belgrade, 2021, MONTH_May, 0.449818470067468f, 0, 0 },
+[490] = { city_belgrade, 2021, MONTH_Oct, 0.71154904735186f, 0, 0 },
+[491] = { city_belgrade, 2021, MONTH_Dec, 0.1308593962428957f, 0, 0 },
+[492] = { city_singapore, 2019, MONTH_Feb, 0.193694292780376f, 0, 0 },
+[493] = { city_singapore, 2019, MONTH_Apr, 0.318702929890905f, 0, 0 },
+[494] = { city_singapore, 2019, MONTH_Jun, 0.645215799082995f, 0, 0 },
+[495] = { city_singapore, 2019, MONTH_Oct, 0.591187969918129f, 0, 0 },
+[496] = { city_singapore, 2020, MONTH_May, 0.699396534256987f, 0, 0 },
+[497] = { city_singapore, 2020, MONTH_Aug, 0.2780977621387803f, 0, 0 },
+[498] = { city_singapore, 2020, MONTH_Nov, 0.455489668769613f, 0, 0 },
+[499] = { city_singapore, 2020, MONTH_Dec, 0.724372367679592f, 0, 0 },
+[500] = { city_singapore, 2021, MONTH_Apr, 0.0229979967292621f, 0, 0 },
+[501] = { city_singapore, 2021, MONTH_Jul, 0.0249669479037042f, 0, 0 },
+[502] = { city_singapore, 2021, MONTH_Sep, 0.879108640287500f, 0, 0 },
+[503] = { city_singapore, 2022, MONTH_Jan, 0.612089991020752f, 0, 0 },
+[504] = { city_seoul, 2019, MONTH_May, 0.277150821785431f, 0, 0 },
+[505] = { city_seoul, 2019, MONTH_Aug, 0.3348478329043680f, 0, 0 },
+[506] = { city_seoul, 2019, MONTH_Nov, 0.4503487807607835f, 0, 0 },
+[507] = { city_seoul, 2019, MONTH_Dec, 0.2103442020665882f, 0, 0 },
+[508] = { city_seoul, 2020, MONTH_May, 0.3640140905380354f, 0, 0 },
+[509] = { city_seoul, 2020, MONTH_Aug, 0.706729161499808f, 0, 0 },
+[510] = { city_seoul, 2020, MONTH_Nov, 0.885612236808133f, 0, 0 },
+[511] = { city_seoul, 2020, MONTH_Dec, 0.519775809368071f, 0, 0 },
+[512] = { city_seoul, 2021, MONTH_Apr, 0.732835881732319f, 0, 0 },
+[513] = { city_seoul, 2021, MONTH_Jul, 0.903712712868978f, 0, 0 },
+[514] = { city_seoul, 2021, MONTH_Sep, 0.579911957881779f, 0, 0 },
+[515] = { city_seoul, 2022, MONTH_Jan, 0.523793425841401f, 0, 0 },
+[516] = { city_tunis, 2019, MONTH_Mar, 0.0982735360423472f, 0, 0 },
+[517] = { city_tunis, 2019, MONTH_May, 0.2783517128913841f, 0, 0 },
+[518] = { city_tunis, 2019, MONTH_Oct, 0.0179865221608522f, 0, 0 },
+[519] = { city_tunis, 2019, MONTH_Dec, 0.594911360748928f, 0, 0 },
+[520] = { city_tunis, 2020, MONTH_Mar, 0.4492429946395797f, 0, 0 },
+[521] = { city_tunis, 2020, MONTH_May, 0.285860802759041f, 0, 0 },
+[522] = { city_tunis, 2020, MONTH_Oct, 0.532021890496857f, 0, 0 },
+[523] = { city_tunis, 2020, MONTH_Dec, 0.664899909160940f, 0, 0 },
+[524] = { city_tunis, 2021, MONTH_Apr, 0.373665071634036f, 0, 0 },
+[525] = { city_tunis, 2021, MONTH_Jul, 0.965776337366f, 0, 0 },
+[526] = { city_tunis, 2021, MONTH_Sep, 0.659560620156704f, 0, 0 },
+[527] = { city_tunis, 2022, MONTH_Jan, 0.624321203896919f, 0, 0 },
+[528] = { city_bangkok, 2019, MONTH_Feb, 0.257845660876937f, 0, 0 },
+[529] = { city_bangkok, 2019, MONTH_Apr, 0.224888541670249f, 0, 0 },
+[530] = { city_bangkok, 2019, MONTH_Jun, 0.619847391242953f, 0, 0 },
+[531] = { city_bangkok, 2019, MONTH_Oct, 0.850551515098475f, 0, 0 },
+[532] = { city_bangkok, 2020, MONTH_Feb, 0.911861784979914f, 0, 0 },
+[533] = { city_bangkok, 2020, MONTH_Apr, 0.854345367132697f, 0, 0 },
+[534] = { city_bangkok, 2020, MONTH_Jun, 0.976580885030065f, 0, 0 },
+[535] = { city_bangkok, 2020, MONTH_Oct, 0.515880023341469f, 0, 0 },
+[536] = { city_bangkok, 2021, MONTH_Apr, 0.05074702972151967f, 0, 0 },
+[537] = { city_bangkok, 2021, MONTH_Jul, 0.4253267917878572f, 0, 0 },
+[538] = { city_bangkok, 2021, MONTH_Sep, 0.915287731012829f, 0, 0 },
+[539] = { city_bangkok, 2022, MONTH_Jan, 0.258306431399695f, 0, 0 },
+[540] = { city_washington_dc, 2019, MONTH_May, 0.643359616915746f, 0, 0 },
+[541] = { city_washington_dc, 2019, MONTH_Aug, 0.370206005261732f, 0, 0 },
+[542] = { city_washington_dc, 2019, MONTH_Nov, 0.0623440040458129f, 0, 0 },
+[543] = { city_washington_dc, 2019, MONTH_Dec, 0.816457481734427f, 0, 0 },
+[544] = { city_washington_dc, 2020, MONTH_May, 0.538708625763008f, 0, 0 },
+[545] = { city_washington_dc, 2020, MONTH_Aug, 0.650384533924187f, 0, 0 },
+[546] = { city_washington_dc, 2020, MONTH_Nov, 0.116951545075829f, 0, 0 },
+[547] = { city_washington_dc, 2020, MONTH_Dec, 0.148219641272978f, 0, 0 },
+[548] = { city_washington_dc, 2021, MONTH_Apr, 0.02100655560513664f, 0, 0 },
+[549] = { city_washington_dc, 2021, MONTH_Jul, 0.871980590290593f, 0, 0 },
+[550] = { city_washington_dc, 2021, MONTH_Sep, 0.282318232339043f, 0, 0 },
+[551] = { city_washington_dc, 2022, MONTH_Jan, 0.805829276934675f, 0, 0 },
+[552] = { city_hanoi, 2019, MONTH_May, 0.41659113260472f, 0, 0 },
+[553] = { city_hanoi, 2019, MONTH_Aug, 0.1152641995827389f, 0, 0 },
+[554] = { city_hanoi, 2019, MONTH_Nov, 0.360035965294691f, 0, 0 },
+[555] = { city_hanoi, 2019, MONTH_Dec, 0.784196077332438f, 0, 0 },
+[556] = { city_hanoi, 2020, MONTH_May, 0.761414062564656f, 0, 0 },
+[557] = { city_hanoi, 2020, MONTH_Aug, 0.972249586627066f, 0, 0 },
+[558] = { city_hanoi, 2020, MONTH_Nov, 0.477242926987743f, 0, 0 },
+[559] = { city_hanoi, 2020, MONTH_Dec, 0.548426649668160f, 0, 0 },
+[560] = { city_hanoi, 2021, MONTH_May, 0.548248192976479f, 0, 0 },
+[561] = { city_hanoi, 2021, MONTH_Aug, 0.591016543120125f, 0, 0 },
+[562] = { city_hanoi, 2021, MONTH_Nov, 0.788317457936361f, 0, 0 },
+[563] = { city_hanoi, 2021, MONTH_Dec, 0.800132571339982f, 0, 0 },
+[564] = { city_harare, 2019, MONTH_Mar, 0.729786759502753f, 0, 0 },
+[565] = { city_harare, 2019, MONTH_May, 0.907286585478762f, 0, 0 },
+[566] = { city_harare, 2019, MONTH_Oct, 0.750261430981712f, 0, 0 },
+[567] = { city_harare, 2019, MONTH_Dec, 0.493438071814215f, 0, 0 },
+[568] = { city_harare, 2020, MONTH_Feb, 0.65132695252707f, 0, 0 },
+[569] = { city_harare, 2020, MONTH_Apr, 0.674409513858446f, 0, 0 },
+[570] = { city_harare, 2020, MONTH_Jun, 0.613534154048466f, 0, 0 },
+[571] = { city_harare, 2020, MONTH_Oct, 0.332698486042969f, 0, 0 },
+[572] = { city_harare, 2021, MONTH_Apr, 0.326843377521804f, 0, 0 },
+[573] = { city_harare, 2021, MONTH_Jul, 0.95259836088787f, 0, 0 },
+[574] = { city_harare, 2021, MONTH_Sep, 0.634598307047354f, 0, 0 },
+[575] = { city_harare, 2022, MONTH_Jan, 0.970130180099394f, 0, 0 },
+[576] = { city_ankara, 2019, MONTH_Feb, 0.1067739057302146f, 0, 0 },
+[577] = { city_ankara, 2019, MONTH_Apr, 0.636578294822275f, 0, 0 },
+[578] = { city_ankara, 2019, MONTH_Jun, 0.1065001987771908f, 0, 0 },
+[579] = { city_ankara, 2019, MONTH_Oct, 0.395046230673565f, 0, 0 },
+[580] = { city_ankara, 2020, MONTH_May, 0.516626872063971f, 0, 0 },
+[581] = { city_ankara, 2020, MONTH_Aug, 0.707711604441861f, 0, 0 },
+[582] = { city_ankara, 2020, MONTH_Nov, 0.492978525577345f, 0, 0 },
+[583] = { city_ankara, 2020, MONTH_Dec, 0.417615483179953f, 0, 0 },
+[584] = { city_ankara, 2021, MONTH_Mar, 0.3916401752130632f, 0, 0 },
+[585] = { city_ankara, 2021, MONTH_May, 0.642573767822410f, 0, 0 },
+[586] = { city_ankara, 2021, MONTH_Oct, 0.2332685344442790f, 0, 0 },
+[587] = { city_ankara, 2021, MONTH_Dec, 0.348302140835808f, 0, 0 },
+};
+global u32 test_data_len = sizeof(test_data) / sizeof(test_data[0]);
\ No newline at end of file
diff --git a/run_tree/data/incenter_test_data_clean.csv b/run_tree/data/incenter_test_data_clean.csv
new file mode 100644
index 0000000..5a6f382
--- /dev/null
+++ b/run_tree/data/incenter_test_data_clean.csv
@@ -0,0 +1,588 @@
+0,city_kiev,2019,MONTH_Mar,0.8208904644894152
+1,city_kiev,2019,MONTH_May,0.560734073251748
+2,city_kiev,2019,MONTH_Oct,0.6374267513460055
+3,city_kiev,2019,MONTH_Dec,0.808628906405562
+4,city_kiev,2020,MONTH_Apr,0.5930763917923403
+5,city_kiev,2020,MONTH_Jul,0.8580149759139355
+6,city_kiev,2020,MONTH_Sep,0.1797655715379879
+7,city_kiev,2021,MONTH_Jan,0.2632534693864327
+8,city_kiev,2021,MONTH_Mar,0.8414969999187483
+9,city_kiev,2021,MONTH_May,0.5374924169034345
+10,city_kiev,2021,MONTH_Oct,0.4069815620754663
+11,city_kiev,2021,MONTH_Dec,0.767921059845557
+12,city_buenos_aires,2019,MONTH_Mar,0.8774591161262479
+13,city_buenos_aires,2019,MONTH_May,0.20624465389973745
+14,city_buenos_aires,2019,MONTH_Oct,0.9847976477350923
+15,city_buenos_aires,2019,MONTH_Dec,0.06729540587017169
+16,city_buenos_aires,2020,MONTH_Apr,0.8985765810829179
+17,city_buenos_aires,2020,MONTH_Jul,0.8147416450345571
+18,city_buenos_aires,2020,MONTH_Sep,0.39419316092894296
+19,city_buenos_aires,2021,MONTH_Jan,0.2844815741140395
+20,city_buenos_aires,2021,MONTH_Mar,0.9929587462564623
+21,city_buenos_aires,2021,MONTH_May,0.1550649172505355
+22,city_buenos_aires,2021,MONTH_Oct,0.6522096246196023
+23,city_buenos_aires,2021,MONTH_Dec,0.7408605917514841
+24,city_canberra,2019,MONTH_May,0.13386899374995143
+25,city_canberra,2019,MONTH_Aug,0.01251845204640567
+26,city_canberra,2019,MONTH_Nov,0.31069477884938224
+27,city_canberra,2019,MONTH_Dec,0.6144432795767782
+28,city_canberra,2020,MONTH_Apr,0.38215052324569254
+29,city_canberra,2020,MONTH_Jul,0.49258667615249774
+30,city_canberra,2020,MONTH_Sep,0.014024575946441353
+31,city_canberra,2021,MONTH_Jan,0.6302469024538054
+32,city_canberra,2021,MONTH_Mar,0.6657411558237325
+33,city_canberra,2021,MONTH_May,0.6375153743649629
+34,city_canberra,2021,MONTH_Oct,0.4355080346524989
+35,city_canberra,2021,MONTH_Dec,0.7842671017087504
+36,city_yerevan,2019,MONTH_Feb,0.2224804342202128
+37,city_yerevan,2019,MONTH_Apr,0.40295435638966803
+38,city_yerevan,2019,MONTH_Jun,0.00841304129117737
+39,city_yerevan,2019,MONTH_Oct,0.49353338935245084
+40,city_yerevan,2020,MONTH_Mar,0.7956088092430287
+41,city_yerevan,2020,MONTH_May,0.8718522813330138
+42,city_yerevan,2020,MONTH_Oct,0.7578502696544491
+43,city_yerevan,2020,MONTH_Dec,0.3059739437150092
+44,city_yerevan,2021,MONTH_Mar,0.5751057083330098
+45,city_yerevan,2021,MONTH_May,0.32415650181804556
+46,city_yerevan,2021,MONTH_Oct,0.13919376564116737
+47,city_yerevan,2021,MONTH_Dec,0.4082901290089086
+48,city_bishkek,2019,MONTH_Feb,0.8545389644898143
+49,city_bishkek,2019,MONTH_Apr,0.8679714734156914
+50,city_bishkek,2019,MONTH_Jun,0.9793901564982332
+51,city_bishkek,2019,MONTH_Oct,0.8289790309876645
+52,city_bishkek,2020,MONTH_Apr,0.1458951287005129
+53,city_bishkek,2020,MONTH_Jul,0.8034467113679751
+54,city_bishkek,2020,MONTH_Sep,0.34789595434452725
+55,city_bishkek,2021,MONTH_Jan,0.07184230396111713
+56,city_bishkek,2021,MONTH_Mar,0.7836073496687682
+57,city_bishkek,2021,MONTH_May,0.7217500839378747
+58,city_bishkek,2021,MONTH_Oct,0.28313109637712275
+59,city_bishkek,2021,MONTH_Dec,0.1071367737747645
+60,city_dhaka,2019,MONTH_Mar,0.6964870450822525
+61,city_dhaka,2019,MONTH_May,0.9268741946156642
+62,city_dhaka,2019,MONTH_Oct,0.0657847048182576
+63,city_dhaka,2019,MONTH_Dec,0.2679787325156153
+64,city_dhaka,2020,MONTH_Feb,0.7415948996627278
+65,city_dhaka,2020,MONTH_Apr,0.8887928797784511
+66,city_dhaka,2020,MONTH_Jun,0.9526160257787081
+67,city_dhaka,2020,MONTH_Oct,0.5394245399621023
+68,city_dhaka,2021,MONTH_Feb,0.7832918710573495
+69,city_dhaka,2021,MONTH_Apr,0.16703003479420253
+70,city_dhaka,2021,MONTH_Jun,0.9160800358967391
+71,city_dhaka,2021,MONTH_Oct,0.045576519669261906
+72,city_sucre,2019,MONTH_Feb,0.9057953843385798
+73,city_sucre,2019,MONTH_Apr,0.048949252273037924
+74,city_sucre,2019,MONTH_Jun,0.39748267526958336
+75,city_sucre,2019,MONTH_Oct,0.32402533613913065
+76,city_sucre,2020,MONTH_Feb,0.2555061186834925
+77,city_sucre,2020,MONTH_Apr,0.6834698453947482
+78,city_sucre,2020,MONTH_Jun,0.08607874887258926
+79,city_sucre,2020,MONTH_Oct,0.4802000825511048
+80,city_sucre,2021,MONTH_Apr,0.9333881101247026
+81,city_sucre,2021,MONTH_Jul,0.8260557264582842
+82,city_sucre,2021,MONTH_Sep,0.6558618586863463
+83,city_sucre,2022,MONTH_Jan,0.9951355772306388
+84,city_brasilia,2019,MONTH_Apr,0.9343497529635403
+85,city_brasilia,2019,MONTH_Jul,0.5570852230328219
+86,city_brasilia,2019,MONTH_Sep,0.2242530147430456
+87,city_brasilia,2020,MONTH_Jan,0.37927683595779194
+88,city_brasilia,2020,MONTH_Feb,0.1345707686817872
+89,city_brasilia,2020,MONTH_Apr,0.29826624893861653
+90,city_brasilia,2020,MONTH_Jun,0.8471161140155271
+91,city_brasilia,2020,MONTH_Oct,0.7106683451334669
+92,city_brasilia,2021,MONTH_Apr,0.8821305385207572
+93,city_brasilia,2021,MONTH_Jul,0.6341264853965499
+94,city_brasilia,2021,MONTH_Sep,0.8793042884983516
+95,city_brasilia,2022,MONTH_Jan,0.3678368890456808
+96,city_ottawa,2019,MONTH_Feb,0.46446610262374566
+97,city_ottawa,2019,MONTH_Apr,0.270441763178759
+98,city_ottawa,2019,MONTH_Jun,0.785020644703367
+99,city_ottawa,2019,MONTH_Oct,0.4397878896865377
+100,city_ottawa,2020,MONTH_Mar,0.6250700533476734
+101,city_ottawa,2020,MONTH_May,0.8543918093977015
+102,city_ottawa,2020,MONTH_Oct,0.10924765266094849
+103,city_ottawa,2020,MONTH_Dec,0.06989308957770357
+104,city_ottawa,2021,MONTH_May,0.02192228695177112
+105,city_ottawa,2021,MONTH_Aug,0.31180470130290383
+106,city_ottawa,2021,MONTH_Nov,0.8009641310226249
+107,city_ottawa,2021,MONTH_Dec,0.8939282034537013
+108,city_santiago,2019,MONTH_Feb,0.2604066622789484
+109,city_santiago,2019,MONTH_Apr,0.04408219650514589
+110,city_santiago,2019,MONTH_Jun,0.033700853867721814
+111,city_santiago,2019,MONTH_Oct,0.07020014506385863
+112,city_santiago,2020,MONTH_Mar,0.669167062800284
+113,city_santiago,2020,MONTH_May,0.3946672417327506
+114,city_santiago,2020,MONTH_Oct,0.96961707780403
+115,city_santiago,2020,MONTH_Dec,0.6926699539539745
+116,city_santiago,2021,MONTH_Feb,0.8855788170714998
+117,city_santiago,2021,MONTH_Apr,0.022485852743379775
+118,city_santiago,2021,MONTH_Jun,0.016611682837006425
+119,city_santiago,2021,MONTH_Oct,0.7388064669432675
+120,city_beijing,2019,MONTH_Mar,0.3984649808928382
+121,city_beijing,2019,MONTH_May,0.5516481646386852
+122,city_beijing,2019,MONTH_Oct,0.0212362107359646
+123,city_beijing,2019,MONTH_Dec,0.23392793545990853
+124,city_beijing,2020,MONTH_Mar,0.8976580542276967
+125,city_beijing,2020,MONTH_May,0.5125178728872494
+126,city_beijing,2020,MONTH_Oct,0.09885903925252493
+127,city_beijing,2020,MONTH_Dec,0.996158072334728
+128,city_beijing,2021,MONTH_May,0.3910244510687829
+129,city_beijing,2021,MONTH_Aug,0.5919057621735655
+130,city_beijing,2021,MONTH_Nov,0.22830963063642795
+131,city_beijing,2021,MONTH_Dec,0.8218119255978358
+132,city_bogota,2019,MONTH_Feb,0.040559618555634946
+133,city_bogota,2019,MONTH_Apr,0.6697544946555136
+134,city_bogota,2019,MONTH_Jun,0.7012648347686247
+135,city_bogota,2019,MONTH_Oct,0.9266430067995907
+136,city_bogota,2020,MONTH_May,0.9265350964858453
+137,city_bogota,2020,MONTH_Aug,0.5622719648324987
+138,city_bogota,2020,MONTH_Nov,0.18505076213184524
+139,city_bogota,2020,MONTH_Dec,0.13200979435040983
+140,city_bogota,2021,MONTH_Apr,0.25492724368701236
+141,city_bogota,2021,MONTH_Jul,0.6551881370411056
+142,city_bogota,2021,MONTH_Sep,0.3301507156757937
+143,city_bogota,2022,MONTH_Jan,0.9319825381469947
+144,city_nicosia,2019,MONTH_Feb,0.8112536780481092
+145,city_nicosia,2019,MONTH_Apr,0.7122242228197432
+146,city_nicosia,2019,MONTH_Jun,0.3992731724465637
+147,city_nicosia,2019,MONTH_Oct,0.0560925034296097
+148,city_nicosia,2020,MONTH_Mar,0.605635873827316
+149,city_nicosia,2020,MONTH_May,0.8155285147048078
+150,city_nicosia,2020,MONTH_Oct,0.7283140343929381
+151,city_nicosia,2020,MONTH_Dec,0.1593773388870281
+152,city_nicosia,2021,MONTH_May,0.5330985723420278
+153,city_nicosia,2021,MONTH_Aug,0.46643412774693715
+154,city_nicosia,2021,MONTH_Nov,0.7302379618035622
+155,city_nicosia,2021,MONTH_Dec,0.5971774259867612
+156,city_quito,2019,MONTH_May,0.39005009418535863
+157,city_quito,2019,MONTH_Aug,0.9025102526506403
+158,city_quito,2019,MONTH_Nov,0.507479232033487
+159,city_quito,2019,MONTH_Dec,0.5046753623312223
+160,city_quito,2020,MONTH_Mar,0.8932394343266225
+161,city_quito,2020,MONTH_May,0.19722367870643642
+162,city_quito,2020,MONTH_Oct,0.4616243916670646
+163,city_quito,2020,MONTH_Dec,0.22929952610965976
+164,city_quito,2021,MONTH_Apr,0.675839552639689
+165,city_quito,2021,MONTH_Jul,0.7522737622328722
+166,city_quito,2021,MONTH_Sep,0.7547694762384426
+167,city_quito,2022,MONTH_Jan,0.5224768718138563
+168,city_cairo,2019,MONTH_Apr,0.12251978169018596
+169,city_cairo,2019,MONTH_Jul,0.640214441842041
+170,city_cairo,2019,MONTH_Sep,0.11292283290324012
+171,city_cairo,2020,MONTH_Jan,0.443793309914693
+172,city_cairo,2020,MONTH_May,0.3695331911519818
+173,city_cairo,2020,MONTH_Aug,0.0875719219910186
+174,city_cairo,2020,MONTH_Nov,0.1300237622828465
+175,city_cairo,2020,MONTH_Dec,0.6190802409945482
+176,city_cairo,2021,MONTH_May,0.2654316735327361
+177,city_cairo,2021,MONTH_Aug,0.08380868314075796
+178,city_cairo,2021,MONTH_Nov,0.0021753600424576636
+179,city_cairo,2021,MONTH_Dec,0.3427143851789648
+180,city_addis_ababa,2019,MONTH_Feb,0.5274830002683538
+181,city_addis_ababa,2019,MONTH_Apr,0.028601788414122087
+182,city_addis_ababa,2019,MONTH_Jun,0.22406045632190197
+183,city_addis_ababa,2019,MONTH_Oct,0.1570840093379693
+184,city_addis_ababa,2020,MONTH_Apr,0.4696975980332939
+185,city_addis_ababa,2020,MONTH_Jul,0.7018912168460542
+186,city_addis_ababa,2020,MONTH_Sep,0.6707188950806201
+187,city_addis_ababa,2021,MONTH_Jan,0.22223531255669327
+188,city_addis_ababa,2021,MONTH_Mar,0.7397846957044468
+189,city_addis_ababa,2021,MONTH_May,0.2963331880851181
+190,city_addis_ababa,2021,MONTH_Oct,0.22720267945089334
+191,city_addis_ababa,2021,MONTH_Dec,0.974100472469575
+192,city_berlin,2019,MONTH_May,0.9663306486026292
+193,city_berlin,2019,MONTH_Aug,0.01762058526956345
+194,city_berlin,2019,MONTH_Nov,0.5845476680327896
+195,city_berlin,2019,MONTH_Dec,0.05267359388143655
+196,city_berlin,2020,MONTH_Feb,0.002912080115626603
+197,city_berlin,2020,MONTH_Apr,0.461618898119614
+198,city_berlin,2020,MONTH_Jun,0.9187177319896965
+199,city_berlin,2020,MONTH_Oct,0.5269226682934646
+200,city_berlin,2021,MONTH_Feb,0.4075331475357029
+201,city_berlin,2021,MONTH_Apr,0.2606312545694286
+202,city_berlin,2021,MONTH_Jun,0.8506910998631624
+203,city_berlin,2021,MONTH_Oct,0.4477453626196978
+204,city_athens,2019,MONTH_Apr,0.4758101962785981
+205,city_athens,2019,MONTH_Jul,0.7273511637085998
+206,city_athens,2019,MONTH_Sep,0.11734674091437469
+207,city_athens,2020,MONTH_Jan,0.7011339146700339
+208,city_athens,2020,MONTH_May,0.5021874710835791
+209,city_athens,2020,MONTH_Aug,0.7224696530590028
+210,city_athens,2020,MONTH_Nov,0.07057044368715926
+211,city_athens,2020,MONTH_Dec,0.4122021872654802
+212,city_athens,2021,MONTH_Apr,0.7895293746783079
+213,city_athens,2021,MONTH_Jul,0.42630517679620317
+214,city_athens,2021,MONTH_Sep,0.4220148157231979
+215,city_athens,2022,MONTH_Jan,0.07930894159509938
+216,city_guatemala_city,2019,MONTH_Feb,0.5606053808546305
+217,city_guatemala_city,2019,MONTH_Apr,0.33825266811210186
+218,city_guatemala_city,2019,MONTH_Jun,0.3958121309669401
+219,city_guatemala_city,2019,MONTH_Oct,0.8123276948852205
+220,city_guatemala_city,2020,MONTH_May,0.6546106461958394
+221,city_guatemala_city,2020,MONTH_Aug,0.3656038852091794
+222,city_guatemala_city,2020,MONTH_Nov,0.2991555036870628
+223,city_guatemala_city,2020,MONTH_Dec,0.1841388365131713
+224,city_guatemala_city,2021,MONTH_Mar,0.7049763199759551
+225,city_guatemala_city,2021,MONTH_May,0.03619294914439686
+226,city_guatemala_city,2021,MONTH_Oct,0.641970219489322
+227,city_guatemala_city,2021,MONTH_Dec,0.7290144352251158
+228,city_jakarta,2019,MONTH_May,0.48730648880158967
+229,city_jakarta,2019,MONTH_Aug,0.13324811999375974
+230,city_jakarta,2019,MONTH_Nov,0.7457459498265384
+231,city_jakarta,2019,MONTH_Dec,0.6770615920087426
+232,city_jakarta,2020,MONTH_Feb,0.0972221706377665
+233,city_jakarta,2020,MONTH_Apr,0.4548133647241398
+234,city_jakarta,2020,MONTH_Jun,0.5990306990429254
+235,city_jakarta,2020,MONTH_Oct,0.46254435882852785
+236,city_jakarta,2021,MONTH_Feb,0.4877541089570895
+237,city_jakarta,2021,MONTH_Apr,0.3176272110731281
+238,city_jakarta,2021,MONTH_Jun,0.46016498959627317
+239,city_jakarta,2021,MONTH_Oct,0.8195183115041934
+240,city_tehran,2019,MONTH_Apr,0.3018613336723692
+241,city_tehran,2019,MONTH_Jul,0.6904442096458521
+242,city_tehran,2019,MONTH_Sep,0.44041453753801474
+243,city_tehran,2020,MONTH_Jan,0.5122166734476404
+244,city_tehran,2020,MONTH_Mar,0.4048771020719195
+245,city_tehran,2020,MONTH_May,0.04251315267732536
+246,city_tehran,2020,MONTH_Oct,0.25935369695061494
+247,city_tehran,2020,MONTH_Dec,0.7838900287619984
+248,city_tehran,2021,MONTH_Mar,0.949263546042006
+249,city_tehran,2021,MONTH_May,0.05690727846201249
+250,city_tehran,2021,MONTH_Oct,0.5775680944871373
+251,city_tehran,2021,MONTH_Dec,0.6189061878508625
+252,city_baghdad,2019,MONTH_Mar,0.20410987826326765
+253,city_baghdad,2019,MONTH_May,0.4184107416408793
+254,city_baghdad,2019,MONTH_Oct,0.40451138082476246
+255,city_baghdad,2019,MONTH_Dec,0.09350363172641074
+256,city_baghdad,2020,MONTH_May,0.8813880636872825
+257,city_baghdad,2020,MONTH_Aug,0.5428448381939682
+258,city_baghdad,2020,MONTH_Nov,0.9196449942226893
+259,city_baghdad,2020,MONTH_Dec,0.40125352418748994
+260,city_baghdad,2021,MONTH_Feb,0.33369599729325594
+261,city_baghdad,2021,MONTH_Apr,0.17961977212424662
+262,city_baghdad,2021,MONTH_Jun,0.8287427670166482
+263,city_baghdad,2021,MONTH_Oct,0.18435480122550651
+264,city_tokyo,2019,MONTH_May,0.1558911089926377
+265,city_tokyo,2019,MONTH_Aug,0.7103297441558276
+266,city_tokyo,2019,MONTH_Nov,0.7670372958998607
+267,city_tokyo,2019,MONTH_Dec,0.34552140914704765
+268,city_tokyo,2020,MONTH_Feb,0.34744339811029146
+269,city_tokyo,2020,MONTH_Apr,0.9456312160307011
+270,city_tokyo,2020,MONTH_Jun,0.6966696315201248
+271,city_tokyo,2020,MONTH_Oct,0.7221513702811281
+272,city_tokyo,2021,MONTH_Apr,0.1247446921008375
+273,city_tokyo,2021,MONTH_Jul,0.7802487336558712
+274,city_tokyo,2021,MONTH_Sep,0.8026525028301382
+275,city_tokyo,2022,MONTH_Jan,0.19500309318308984
+276,city_amman,2019,MONTH_Apr,0.9135647763017481
+277,city_amman,2019,MONTH_Jul,0.519959651824404
+278,city_amman,2019,MONTH_Sep,0.07349623710797859
+279,city_amman,2020,MONTH_Jan,0.8209280523427239
+280,city_amman,2020,MONTH_Feb,0.8580494733125601
+281,city_amman,2020,MONTH_Apr,0.4722288209752745
+282,city_amman,2020,MONTH_Jun,0.8594503298528673
+283,city_amman,2020,MONTH_Oct,0.7343971570872213
+284,city_amman,2021,MONTH_Feb,0.4715316911875882
+285,city_amman,2021,MONTH_Apr,0.746664117982623
+286,city_amman,2021,MONTH_Jun,0.04497553004647725
+287,city_amman,2021,MONTH_Oct,0.9897280982388877
+288,city_nairobi,2019,MONTH_May,0.3532584699087581
+289,city_nairobi,2019,MONTH_Aug,0.6372658032245778
+290,city_nairobi,2019,MONTH_Nov,0.6529632981666209
+291,city_nairobi,2019,MONTH_Dec,0.9801366674005787
+292,city_nairobi,2020,MONTH_May,0.11232544995237859
+293,city_nairobi,2020,MONTH_Aug,0.6363406749343267
+294,city_nairobi,2020,MONTH_Nov,0.4572050517602766
+295,city_nairobi,2020,MONTH_Dec,0.5016838053742663
+296,city_nairobi,2021,MONTH_May,0.2871335451015594
+297,city_nairobi,2021,MONTH_Aug,0.6134863745452062
+298,city_nairobi,2021,MONTH_Nov,0.6646687928232157
+299,city_nairobi,2021,MONTH_Dec,0.4604603108510942
+300,city_beirut,2019,MONTH_May,0.20657079280157575
+301,city_beirut,2019,MONTH_Aug,0.08060512516532647
+302,city_beirut,2019,MONTH_Nov,0.3121758077040654
+303,city_beirut,2019,MONTH_Dec,0.5704450329632651
+304,city_beirut,2020,MONTH_May,0.49711298135083226
+305,city_beirut,2020,MONTH_Aug,0.2651799515773383
+306,city_beirut,2020,MONTH_Nov,0.37582969737955385
+307,city_beirut,2020,MONTH_Dec,0.23214191453836386
+308,city_beirut,2021,MONTH_Feb,0.9769205528039658
+309,city_beirut,2021,MONTH_Apr,0.5714471639055412
+310,city_beirut,2021,MONTH_Jun,0.7866510539409003
+311,city_beirut,2021,MONTH_Oct,0.10968686507430592
+312,city_tripoli,2019,MONTH_Apr,0.3459256866028718
+313,city_tripoli,2019,MONTH_Jul,0.8762903773435768
+314,city_tripoli,2019,MONTH_Sep,0.7902752440774569
+315,city_tripoli,2020,MONTH_Jan,0.39655891397903786
+316,city_tripoli,2020,MONTH_Mar,0.2853487707159261
+317,city_tripoli,2020,MONTH_May,0.4963336260125154
+318,city_tripoli,2020,MONTH_Oct,0.017927444716766217
+319,city_tripoli,2020,MONTH_Dec,0.49479276214492607
+320,city_tripoli,2021,MONTH_May,0.13084483339218256
+321,city_tripoli,2021,MONTH_Aug,0.3972967126563762
+322,city_tripoli,2021,MONTH_Nov,0.44945112228561657
+323,city_tripoli,2021,MONTH_Dec,0.9189092853708906
+324,city_kuala_lumpur,2019,MONTH_Feb,0.16756258334398821
+325,city_kuala_lumpur,2019,MONTH_Apr,0.7292001698733792
+326,city_kuala_lumpur,2019,MONTH_Jun,0.5157281213252976
+327,city_kuala_lumpur,2019,MONTH_Oct,0.8014958747496247
+328,city_kuala_lumpur,2020,MONTH_May,0.8602854104400254
+329,city_kuala_lumpur,2020,MONTH_Aug,0.43333673199642
+330,city_kuala_lumpur,2020,MONTH_Nov,0.3736418013982573
+331,city_kuala_lumpur,2020,MONTH_Dec,0.9933212143240009
+332,city_kuala_lumpur,2021,MONTH_May,0.9558969606204645
+333,city_kuala_lumpur,2021,MONTH_Aug,0.8474663747876059
+334,city_kuala_lumpur,2021,MONTH_Nov,0.7259309917369569
+335,city_kuala_lumpur,2021,MONTH_Dec,0.07716765857962526
+336,city_male,2019,MONTH_May,0.11859194162248121
+337,city_male,2019,MONTH_Aug,0.10933036332271562
+338,city_male,2019,MONTH_Nov,0.3512985312079444
+339,city_male,2019,MONTH_Dec,0.9390140198052906
+340,city_male,2020,MONTH_May,0.9058257181974001
+341,city_male,2020,MONTH_Aug,0.47744511584315097
+342,city_male,2020,MONTH_Nov,0.8658614942065163
+343,city_male,2020,MONTH_Dec,0.38798872766896986
+344,city_male,2021,MONTH_Feb,0.7620947026588929
+345,city_male,2021,MONTH_Apr,0.9307031365367027
+346,city_male,2021,MONTH_Jun,0.4132674715381708
+347,city_male,2021,MONTH_Oct,0.07150772598482613
+348,city_mexico_city,2019,MONTH_Mar,0.2826892717581577
+349,city_mexico_city,2019,MONTH_May,0.6928013923863494
+350,city_mexico_city,2019,MONTH_Oct,0.8563008311643129
+351,city_mexico_city,2019,MONTH_Dec,0.627526483522628
+352,city_mexico_city,2020,MONTH_Apr,0.9928776824550084
+353,city_mexico_city,2020,MONTH_Jul,0.32889448012572653
+354,city_mexico_city,2020,MONTH_Sep,0.5673016627623955
+355,city_mexico_city,2021,MONTH_Jan,0.6536524032020574
+356,city_mexico_city,2021,MONTH_Feb,0.36173684152563834
+357,city_mexico_city,2021,MONTH_Apr,0.19990517830323706
+358,city_mexico_city,2021,MONTH_Jun,0.22345205229744747
+359,city_mexico_city,2021,MONTH_Oct,0.22492657039602948
+360,city_ulan_bator,2019,MONTH_Feb,0.09731115691626857
+361,city_ulan_bator,2019,MONTH_Apr,0.7176250351822594
+362,city_ulan_bator,2019,MONTH_Jun,0.688881154365618
+363,city_ulan_bator,2019,MONTH_Oct,0.3175980682827121
+364,city_ulan_bator,2020,MONTH_Mar,0.5320422814207855
+365,city_ulan_bator,2020,MONTH_May,0.9377318205081451
+366,city_ulan_bator,2020,MONTH_Oct,0.15075146061048983
+367,city_ulan_bator,2020,MONTH_Dec,0.02105164967117734
+368,city_ulan_bator,2021,MONTH_Feb,0.6952583644077902
+369,city_ulan_bator,2021,MONTH_Apr,0.21333687792373945
+370,city_ulan_bator,2021,MONTH_Jun,0.43504532964375053
+371,city_ulan_bator,2021,MONTH_Oct,0.8872324146983761
+372,city_rabat,2019,MONTH_Apr,0.48486845571004145
+373,city_rabat,2019,MONTH_Jul,0.8303783897079856
+374,city_rabat,2019,MONTH_Sep,0.2679469126786186
+375,city_rabat,2020,MONTH_Jan,0.216862086460625
+376,city_rabat,2020,MONTH_Feb,0.6394841852490968
+377,city_rabat,2020,MONTH_Apr,0.7957348864256418
+378,city_rabat,2020,MONTH_Jun,0.35449913609838557
+379,city_rabat,2020,MONTH_Oct,0.884786543583512
+380,city_rabat,2021,MONTH_Feb,0.7545998323649945
+381,city_rabat,2021,MONTH_Apr,0.44179928991617634
+382,city_rabat,2021,MONTH_Jun,0.6071167209468333
+383,city_rabat,2021,MONTH_Oct,0.3586237763278812
+384,city_amsterdam,2019,MONTH_Feb,0.7316252644430535
+385,city_amsterdam,2019,MONTH_Apr,0.5668687381660837
+386,city_amsterdam,2019,MONTH_Jun,0.5576670888299602
+387,city_amsterdam,2019,MONTH_Oct,0.41651402253597936
+388,city_amsterdam,2020,MONTH_Mar,0.13691185457089883
+389,city_amsterdam,2020,MONTH_May,0.9963811450585627
+390,city_amsterdam,2020,MONTH_Oct,0.5678846559952594
+391,city_amsterdam,2020,MONTH_Dec,0.35998249466652255
+392,city_amsterdam,2021,MONTH_May,0.36176124559912815
+393,city_amsterdam,2021,MONTH_Aug,0.1452082631177628
+394,city_amsterdam,2021,MONTH_Nov,0.4059250184669765
+395,city_amsterdam,2021,MONTH_Dec,0.4668655836718629
+396,city_wellington,2019,MONTH_Mar,0.23997903684052424
+397,city_wellington,2019,MONTH_May,0.05133126394973464
+398,city_wellington,2019,MONTH_Oct,0.1548202784687026
+399,city_wellington,2019,MONTH_Dec,0.3424800359478265
+400,city_wellington,2020,MONTH_Feb,0.022701964101586536
+401,city_wellington,2020,MONTH_Apr,0.5499770964284553
+402,city_wellington,2020,MONTH_Jun,0.8253524794233187
+403,city_wellington,2020,MONTH_Oct,0.005331477335972168
+404,city_wellington,2021,MONTH_May,0.921657139441937
+405,city_wellington,2021,MONTH_Aug,0.10309395870694193
+406,city_wellington,2021,MONTH_Nov,0.6263565227069673
+407,city_wellington,2021,MONTH_Dec,0.2020818627729063
+408,city_managua,2019,MONTH_Apr,0.05582787287624136
+409,city_managua,2019,MONTH_Jul,0.48382759592973157
+410,city_managua,2019,MONTH_Sep,0.9827871188806075
+411,city_managua,2020,MONTH_Jan,0.00017714421442871497
+412,city_managua,2020,MONTH_Feb,0.9249580344693701
+413,city_managua,2020,MONTH_Apr,0.8044975554446903
+414,city_managua,2020,MONTH_Jun,0.5627263957766564
+415,city_managua,2020,MONTH_Oct,0.3237250276194914
+416,city_managua,2021,MONTH_Mar,0.020448801084839263
+417,city_managua,2021,MONTH_May,0.8901304537407349
+418,city_managua,2021,MONTH_Oct,0.7318410443954749
+419,city_managua,2021,MONTH_Dec,0.8843814008259293
+420,city_abuja,2019,MONTH_May,0.664788190346494
+421,city_abuja,2019,MONTH_Aug,0.34573866676735254
+422,city_abuja,2019,MONTH_Nov,0.0929888238353932
+423,city_abuja,2019,MONTH_Dec,0.8242625289595346
+424,city_abuja,2020,MONTH_Apr,0.610497664130435
+425,city_abuja,2020,MONTH_Jul,0.13045928363070136
+426,city_abuja,2020,MONTH_Sep,0.5931871800066496
+427,city_abuja,2021,MONTH_Jan,0.8761990727326217
+428,city_abuja,2021,MONTH_Mar,0.03297221593215882
+429,city_abuja,2021,MONTH_May,0.2663683106261656
+430,city_abuja,2021,MONTH_Oct,0.4752472559097941
+431,city_abuja,2021,MONTH_Dec,0.21817891455498406
+432,city_islamabad,2019,MONTH_Feb,0.6894565356869449
+433,city_islamabad,2019,MONTH_Apr,0.04473733639784894
+434,city_islamabad,2019,MONTH_Jun,0.8225140958795525
+435,city_islamabad,2019,MONTH_Oct,0.9167038764499388
+436,city_islamabad,2020,MONTH_Mar,0.18931156178758168
+437,city_islamabad,2020,MONTH_May,0.867263067708257
+438,city_islamabad,2020,MONTH_Oct,0.9052158492439835
+439,city_islamabad,2020,MONTH_Dec,0.2549403768974773
+440,city_islamabad,2021,MONTH_Mar,0.2810396842292088
+441,city_islamabad,2021,MONTH_May,0.8873391067395595
+442,city_islamabad,2021,MONTH_Oct,0.9330488657790148
+443,city_islamabad,2021,MONTH_Dec,0.03407368582674841
+444,city_lima,2019,MONTH_Feb,0.3283465777695367
+445,city_lima,2019,MONTH_Apr,0.27567241572636514
+446,city_lima,2019,MONTH_Jun,0.6098150878639521
+447,city_lima,2019,MONTH_Oct,0.7827311559088742
+448,city_lima,2020,MONTH_May,0.0004780110203138621
+449,city_lima,2020,MONTH_Aug,0.32359176810859613
+450,city_lima,2020,MONTH_Nov,0.7920164552383919
+451,city_lima,2020,MONTH_Dec,0.08680504053296734
+452,city_lima,2021,MONTH_Feb,0.2815552061774018
+453,city_lima,2021,MONTH_Apr,0.7946567665550932
+454,city_lima,2021,MONTH_Jun,0.9348739114080165
+455,city_lima,2021,MONTH_Oct,0.9664448937706452
+456,city_bucharest,2019,MONTH_Apr,0.2754215240255594
+457,city_bucharest,2019,MONTH_Jul,0.9342575547213549
+458,city_bucharest,2019,MONTH_Sep,0.4561755717169159
+459,city_bucharest,2020,MONTH_Jan,0.5815353517023211
+460,city_bucharest,2020,MONTH_Feb,0.09553696230313513
+461,city_bucharest,2020,MONTH_Apr,0.7977241274060681
+462,city_bucharest,2020,MONTH_Jun,0.11581142188629723
+463,city_bucharest,2020,MONTH_Oct,0.609366463954831
+464,city_bucharest,2021,MONTH_May,0.7786493748178472
+465,city_bucharest,2021,MONTH_Aug,0.3709475494848148
+466,city_bucharest,2021,MONTH_Nov,0.06409631979509034
+467,city_bucharest,2021,MONTH_Dec,0.6435411634892579
+468,city_moscow,2019,MONTH_Mar,0.3010106354277793
+469,city_moscow,2019,MONTH_May,0.4560783213170011
+470,city_moscow,2019,MONTH_Oct,0.05383312030625198
+471,city_moscow,2019,MONTH_Dec,0.751924840138458
+472,city_moscow,2020,MONTH_May,0.8723060880532295
+473,city_moscow,2020,MONTH_Aug,0.12394020657703886
+474,city_moscow,2020,MONTH_Nov,0.20202634410012543
+475,city_moscow,2020,MONTH_Dec,0.2782740134125702
+476,city_moscow,2021,MONTH_May,0.24885715310109735
+477,city_moscow,2021,MONTH_Aug,0.04896170267358957
+478,city_moscow,2021,MONTH_Nov,0.8684557906574781
+479,city_moscow,2021,MONTH_Dec,0.543114638108053
+480,city_belgrade,2019,MONTH_May,0.5522466531644503
+481,city_belgrade,2019,MONTH_Aug,0.6084457342690707
+482,city_belgrade,2019,MONTH_Nov,0.9044174074753157
+483,city_belgrade,2019,MONTH_Dec,0.07066906245830429
+484,city_belgrade,2020,MONTH_May,0.0008637322170093054
+485,city_belgrade,2020,MONTH_Aug,0.3288244897831679
+486,city_belgrade,2020,MONTH_Nov,0.369768435670744
+487,city_belgrade,2020,MONTH_Dec,0.9619642633301051
+488,city_belgrade,2021,MONTH_Mar,0.30571888815406045
+489,city_belgrade,2021,MONTH_May,0.4498184700674681
+490,city_belgrade,2021,MONTH_Oct,0.711549047351862
+491,city_belgrade,2021,MONTH_Dec,0.13085939624289578
+492,city_singapore,2019,MONTH_Feb,0.1936942927803761
+493,city_singapore,2019,MONTH_Apr,0.3187029298909052
+494,city_singapore,2019,MONTH_Jun,0.6452157990829956
+495,city_singapore,2019,MONTH_Oct,0.5911879699181295
+496,city_singapore,2020,MONTH_May,0.6993965342569872
+497,city_singapore,2020,MONTH_Aug,0.27809776213878035
+498,city_singapore,2020,MONTH_Nov,0.4554896687696134
+499,city_singapore,2020,MONTH_Dec,0.7243723676795923
+500,city_singapore,2021,MONTH_Apr,0.02299799672926217
+501,city_singapore,2021,MONTH_Jul,0.02496694790370424
+502,city_singapore,2021,MONTH_Sep,0.8791086402875006
+503,city_singapore,2022,MONTH_Jan,0.6120899910207525
+504,city_seoul,2019,MONTH_May,0.2771508217854315
+505,city_seoul,2019,MONTH_Aug,0.33484783290436804
+506,city_seoul,2019,MONTH_Nov,0.45034878076078355
+507,city_seoul,2019,MONTH_Dec,0.21034420206658822
+508,city_seoul,2020,MONTH_May,0.36401409053803546
+509,city_seoul,2020,MONTH_Aug,0.7067291614998081
+510,city_seoul,2020,MONTH_Nov,0.8856122368081336
+511,city_seoul,2020,MONTH_Dec,0.5197758093680719
+512,city_seoul,2021,MONTH_Apr,0.7328358817323198
+513,city_seoul,2021,MONTH_Jul,0.9037127128689783
+514,city_seoul,2021,MONTH_Sep,0.5799119578817793
+515,city_seoul,2022,MONTH_Jan,0.5237934258414012
+516,city_tunis,2019,MONTH_Mar,0.09827353604234723
+517,city_tunis,2019,MONTH_May,0.27835171289138416
+518,city_tunis,2019,MONTH_Oct,0.01798652216085228
+519,city_tunis,2019,MONTH_Dec,0.5949113607489287
+520,city_tunis,2020,MONTH_Mar,0.44924299463957973
+521,city_tunis,2020,MONTH_May,0.2858608027590416
+522,city_tunis,2020,MONTH_Oct,0.5320218904968578
+523,city_tunis,2020,MONTH_Dec,0.6648999091609407
+524,city_tunis,2021,MONTH_Apr,0.3736650716340366
+525,city_tunis,2021,MONTH_Jul,0.9657763373669
+526,city_tunis,2021,MONTH_Sep,0.6595606201567045
+527,city_tunis,2022,MONTH_Jan,0.6243212038969197
+528,city_bangkok,2019,MONTH_Feb,0.2578456608769377
+529,city_bangkok,2019,MONTH_Apr,0.2248885416702492
+530,city_bangkok,2019,MONTH_Jun,0.6198473912429538
+531,city_bangkok,2019,MONTH_Oct,0.8505515150984757
+532,city_bangkok,2020,MONTH_Feb,0.9118617849799147
+533,city_bangkok,2020,MONTH_Apr,0.8543453671326979
+534,city_bangkok,2020,MONTH_Jun,0.9765808850300658
+535,city_bangkok,2020,MONTH_Oct,0.5158800233414695
+536,city_bangkok,2021,MONTH_Apr,0.050747029721519676
+537,city_bangkok,2021,MONTH_Jul,0.42532679178785726
+538,city_bangkok,2021,MONTH_Sep,0.9152877310128299
+539,city_bangkok,2022,MONTH_Jan,0.2583064313996952
+540,city_washington_dc,2019,MONTH_May,0.6433596169157464
+541,city_washington_dc,2019,MONTH_Aug,0.3702060052617322
+542,city_washington_dc,2019,MONTH_Nov,0.06234400404581297
+543,city_washington_dc,2019,MONTH_Dec,0.8164574817344272
+544,city_washington_dc,2020,MONTH_May,0.5387086257630082
+545,city_washington_dc,2020,MONTH_Aug,0.6503845339241877
+546,city_washington_dc,2020,MONTH_Nov,0.1169515450758295
+547,city_washington_dc,2020,MONTH_Dec,0.1482196412729787
+548,city_washington_dc,2021,MONTH_Apr,0.021006555605136645
+549,city_washington_dc,2021,MONTH_Jul,0.8719805902905939
+550,city_washington_dc,2021,MONTH_Sep,0.2823182323390435
+551,city_washington_dc,2022,MONTH_Jan,0.8058292769346758
+552,city_hanoi,2019,MONTH_May,0.416591132604722
+553,city_hanoi,2019,MONTH_Aug,0.11526419958273892
+554,city_hanoi,2019,MONTH_Nov,0.3600359652946916
+555,city_hanoi,2019,MONTH_Dec,0.7841960773324388
+556,city_hanoi,2020,MONTH_May,0.7614140625646568
+557,city_hanoi,2020,MONTH_Aug,0.9722495866270668
+558,city_hanoi,2020,MONTH_Nov,0.4772429269877435
+559,city_hanoi,2020,MONTH_Dec,0.5484266496681606
+560,city_hanoi,2021,MONTH_May,0.5482481929764795
+561,city_hanoi,2021,MONTH_Aug,0.5910165431201254
+562,city_hanoi,2021,MONTH_Nov,0.7883174579363615
+563,city_hanoi,2021,MONTH_Dec,0.8001325713399826
+564,city_harare,2019,MONTH_Mar,0.7297867595027538
+565,city_harare,2019,MONTH_May,0.9072865854787621
+566,city_harare,2019,MONTH_Oct,0.7502614309817123
+567,city_harare,2019,MONTH_Dec,0.4934380718142153
+568,city_harare,2020,MONTH_Feb,0.651326952527076
+569,city_harare,2020,MONTH_Apr,0.6744095138584462
+570,city_harare,2020,MONTH_Jun,0.6135341540484665
+571,city_harare,2020,MONTH_Oct,0.3326984860429695
+572,city_harare,2021,MONTH_Apr,0.3268433775218048
+573,city_harare,2021,MONTH_Jul,0.952598360887876
+574,city_harare,2021,MONTH_Sep,0.6345983070473546
+575,city_harare,2022,MONTH_Jan,0.9701301800993941
+576,city_ankara,2019,MONTH_Feb,0.10677390573021461
+577,city_ankara,2019,MONTH_Apr,0.6365782948222759
+578,city_ankara,2019,MONTH_Jun,0.10650019877719086
+579,city_ankara,2019,MONTH_Oct,0.3950462306735656
+580,city_ankara,2020,MONTH_May,0.5166268720639717
+581,city_ankara,2020,MONTH_Aug,0.7077116044418617
+582,city_ankara,2020,MONTH_Nov,0.4929785255773451
+583,city_ankara,2020,MONTH_Dec,0.4176154831799538
+584,city_ankara,2021,MONTH_Mar,0.39164017521306327
+585,city_ankara,2021,MONTH_May,0.6425737678224106
+586,city_ankara,2021,MONTH_Oct,0.23326853444427909
+587,city_ankara,2021,MONTH_Dec,0.3483021408358088
\ No newline at end of file
diff --git a/run_tree/data/incenter_test_data_clean_old.csv b/run_tree/data/incenter_test_data_clean_old.csv
new file mode 100644
index 0000000..914635b
--- /dev/null
+++ b/run_tree/data/incenter_test_data_clean_old.csv
@@ -0,0 +1,490 @@
+0,city_kiev,2019-Jan,0.4135864139,0.01956557108,0.3199632899
+1,city_kiev,2019-May,0.5108363835,0.8589876789,0.2033754817
+2,city_kiev,2019-Sep,0.3098831619,0.2965779176,0.8780642384
+3,city_kiev,2020-Jan,0.6513288588,0.969807378,0.4734936057
+4,city_kiev,2020-May,0.4017988233,0.08149953656,0.1917572298
+5,city_kiev,2020-Sep,0.2700455917,0.6525809233,0.3808256497
+6,city_kiev,2021-Jan,0.9957073392,0.8955640153,0.08200560685
+7,city_kiev,2021-May,0.08359383932,0.878866307,0.2737393541
+8,city_kiev,2021-Sep,0.3864062227,0.8355196902,0.1672691656
+9,city_kiev,2022-Jan,0.03859181836,0.8382749998,0.8116680932
+10,city_buenos_aires,2019-Jan,0.9308313567,0.6466857107,0.8641769833
+11,city_buenos_aires,2019-May,0.3700134984,0.7837693637,0.4343937489
+12,city_buenos_aires,2019-Sep,0.5994078053,0.2148970716,0.2135238874
+13,city_buenos_aires,2020-Jan,0.4941897857,0.9995185631,0.4317838305
+14,city_buenos_aires,2020-May,0.3257251742,0.1786902536,0.9581480625
+15,city_buenos_aires,2020-Sep,0.4133663483,0.4895787017,0.3266269703
+16,city_buenos_aires,2021-Jan,0.4185051363,0.6196468097,0.8595087401
+17,city_buenos_aires,2021-May,0.967209952,0.9684863108,0.1240276343
+18,city_buenos_aires,2021-Sep,0.9547593981,0.4235904569,0.7747874597
+19,city_buenos_aires,2022-Jan,0.7438244778,0.63418064,0.08865491745
+20,city_canberra,2019-Jan,0.642174332,0.2388107887,0.4174561786
+21,city_canberra,2019-May,0.275049574,0.5548509861,0.6932049183
+22,city_canberra,2019-Sep,0.7137277005,0.2288560273,0.3374528905
+23,city_canberra,2020-Jan,0.7776833239,0.2323082658,0.412349501
+24,city_canberra,2020-May,0.240911076,0.7393032141,0.03500517153
+25,city_canberra,2020-Sep,0.3222924521,0.6443196701,0.8722973134
+26,city_canberra,2021-Jan,0.03333787808,0.942723093,0.9307922515
+27,city_canberra,2021-May,0.6754651903,0.8715185605,0.7317628479
+28,city_canberra,2021-Sep,0.6679682277,0.3739222301,0.1775886144
+29,city_canberra,2022-Jan,0.5068355685,0.1635081943,0.1361671877
+30,city_yerevan,2019-Jan,0.2147489967,0.1612449722,0.8831791504
+31,city_yerevan,2019-May,0.9215334967,0.5782146645,0.4315847514
+32,city_yerevan,2019-Sep,0.08998384769,0.243931627,0.9968073448
+33,city_yerevan,2020-Jan,0.6352732643,0.1800861503,0.09468051361
+34,city_yerevan,2020-May,0.9732926191,0.3591501359,0.2549700998
+35,city_yerevan,2020-Sep,0.8958159719,0.9162956265,0.1139302618
+36,city_yerevan,2021-Jan,0.03360279599,0.999251994,0.6504557519
+37,city_yerevan,2021-May,0.2320210872,0.7948166485,0.3111218033
+38,city_yerevan,2021-Sep,0.7518920271,0.2746721747,0.1822453912
+39,city_yerevan,2022-Jan,0.9828234148,0.07410452188,0.8394608475
+40,city_bishkek,2019-Jan,0.1452622254,0.7491750047,0.9541456661
+41,city_bishkek,2019-May,0.5750325423,0.03818584404,0.4018200559
+42,city_bishkek,2019-Sep,0.2234510709,0.8535100292,0.2897079852
+43,city_bishkek,2020-Jan,0.5124122989,0.5110659435,0.3112428697
+44,city_bishkek,2020-May,0.6543130255,0.04240204986,0.622299684
+45,city_bishkek,2020-Sep,0.291880605,0.4159968733,0.2820567858
+46,city_bishkek,2021-Jan,0.04837900082,0.6784619452,0.1266086804
+47,city_bishkek,2021-May,0.7297099912,0.5149978596,0.5162051605
+48,city_bishkek,2021-Sep,0.5357617635,0.1818118381,0.8049034964
+49,city_bishkek,2022-Jan,0.8776874504,0.526233849,0.5263148206
+50,city_dhaka,2019-Jan,0.6751063458,0.1258686413,0.1016408419
+51,city_dhaka,2019-May,0.1479538758,0.137851628,0.5491982115
+52,city_dhaka,2019-Sep,0.0975619112,0.07457551448,0.03538831459
+53,city_dhaka,2020-Jan,0.4430038543,0.1828778611,0.2944631539
+54,city_dhaka,2020-May,0.4983727344,0.2630854991,0.3943451107
+55,city_dhaka,2020-Sep,0.4484292451,0.2339481189,0.2726949863
+56,city_dhaka,2021-Jan,0.5386010889,0.6933623389,0.4272398241
+57,city_dhaka,2021-May,0.8000427347,0.1994170511,0.390616751
+58,city_dhaka,2021-Sep,0.6004021175,0.3635764249,0.7907815985
+59,city_dhaka,2022-Jan,0.7859317154,0.9442375658,0.4927301142
+60,city_sucre,2019-Jan,0.5256472928,0.6907740522,0.615610268
+61,city_sucre,2019-May,0.09963983779,0.7664825572,0.06144303964
+62,city_sucre,2019-Sep,0.2666295548,0.8243600228,0.9654478129
+63,city_sucre,2020-Jan,0.8832832537,0.8316651207,0.506107938
+64,city_sucre,2020-May,0.5119837485,0.8836395641,0.5570675648
+65,city_sucre,2020-Sep,0.2845784627,0.9690913465,0.8959150557
+66,city_sucre,2021-Jan,0.6332564923,0.6389759815,0.1370995872
+67,city_sucre,2021-May,0.3936684553,0.5841123856,0.7516655319
+68,city_sucre,2021-Sep,0.2701367573,0.3501915684,0.6336972499
+69,city_sucre,2022-Jan,0.5919516162,0.4011786881,0.401258499
+70,city_brasilia,2019-Jan,0.9362317028,0.7884664385,0.422647608
+71,city_brasilia,2019-May,0.8524471342,0.430337496,0.6524337378
+72,city_brasilia,2019-Sep,0.3174124746,0.2555113355,0.7316351622
+73,city_brasilia,2020-Jan,0.7486195535,0.9015460575,0.7223646562
+74,city_brasilia,2020-May,0.3439380221,0.4736994161,0.2520318998
+75,city_brasilia,2020-Sep,0.3628079446,0.8676514395,0.03414644115
+76,city_brasilia,2021-Jan,0.7881248045,0.07831938111,0.1207919623
+77,city_brasilia,2021-May,0.9250479078,0.8901303748,0.07106044929
+78,city_brasilia,2021-Sep,0.4424696223,0.1006045971,0.5623995233
+79,city_brasilia,2022-Jan,0.774905499,0.2060788813,0.8296158878
+80,city_ottawa,2019-Jan,0.415154305,0.1059680177,0.3308787448
+81,city_ottawa,2019-May,0.6493463515,0.8937871978,0.3920436129
+82,city_ottawa,2019-Sep,0.7302853507,0.959377626,0.6185701564
+83,city_ottawa,2020-Jan,0.7511642768,0.9351348965,0.3946346882
+84,city_ottawa,2020-May,0.7724624372,0.9600619198,0.7859329103
+85,city_ottawa,2020-Sep,0.3041129836,0.7255126855,0.5221992715
+86,city_ottawa,2021-Jan,0.2503068936,0.3966644239,0.9602804747
+87,city_ottawa,2021-May,0.9312852988,0.3889126695,0.7929940878
+88,city_ottawa,2021-Sep,0.4981112549,0.1059935693,0.8574743066
+89,city_ottawa,2022-Jan,0.2752719283,0.719554905,0.6726537328
+90,city_santiago,2019-Jan,0.7354927387,0.4213001179,0.8802422329
+91,city_santiago,2019-May,0.7656384917,0.04327784254,0.4415898581
+92,city_santiago,2019-Sep,0.9611505259,0.6413038085,0.3379016843
+93,city_santiago,2020-Jan,0.2073040591,0.9427695446,0.6375111631
+94,city_santiago,2020-May,0.3194905106,0.7217433052,0.5085047009
+95,city_santiago,2020-Sep,0.3848264613,0.4960023984,0.9355666638
+96,city_santiago,2021-Jan,0.8416596967,0.8012664085,0.9257390628
+97,city_santiago,2021-May,0.04492334998,0.5346889758,0.7850799406
+98,city_santiago,2021-Sep,0.2037147168,0.5946083862,0.2569824786
+99,city_santiago,2022-Jan,0.1393061974,0.3129179343,0.952991536
+100,city_beijing,2019-Jan,0.3178648282,0.6780576423,0.310220077
+101,city_beijing,2019-May,0.08431258495,0.4251703457,0.325551065
+102,city_beijing,2019-Sep,0.9445905243,0.6002283849,0.2930351001
+103,city_beijing,2020-Jan,0.2790601521,0.6238888439,0.1195087407
+104,city_beijing,2020-May,0.7794297238,0.1274087865,0.8642829933
+105,city_beijing,2020-Sep,0.1180488469,0.6663626158,0.2607091672
+106,city_beijing,2021-Jan,0.0704770744,0.1823784313,0.266078728
+107,city_beijing,2021-May,0.6136936433,0.9076039223,0.7423526451
+108,city_beijing,2021-Sep,0.1818364902,0.8480116889,0.1921589399
+109,city_beijing,2022-Jan,0.9251074867,0.9359530202,0.1770008157
+110,city_bogota,2019-Jan,0.624155697,0.03153673702,0.194147822
+111,city_bogota,2019-May,0.6693112776,0.9194170452,0.7562801226
+112,city_bogota,2019-Sep,0.2270854552,0.5727321164,0.1383067024
+113,city_bogota,2020-Jan,0.1680212218,0.9342808077,0.8749805221
+114,city_bogota,2020-May,0.7202858441,0.9917960947,0.6322514442
+115,city_bogota,2020-Sep,0.2521898539,0.6263208583,0.4677228154
+116,city_bogota,2021-Jan,0.1522785431,0.3525519967,0.7373106672
+117,city_bogota,2021-May,0.5294850571,0.5696053899,0.8817775003
+118,city_bogota,2021-Sep,0.4124724998,0.8456196102,0.6640041077
+119,city_bogota,2022-Jan,0.3733020161,0.02006290035,0.7176733233
+120,city_nicosia,2019-Jan,0.05459926779,0.5053119181,0.7161425568
+121,city_nicosia,2019-May,0.1106513246,0.05436806241,0.3515319482
+122,city_nicosia,2019-Sep,0.2890129589,0.5327814479,0.0564076411
+123,city_nicosia,2020-Jan,0.5163181441,0.6338465939,0.6444040696
+124,city_nicosia,2020-May,0.368582402,0.2737939017,0.5093951622
+125,city_nicosia,2020-Sep,0.471424489,0.7035862994,0.2042673249
+126,city_nicosia,2021-Jan,0.6501679348,0.3863401401,0.06039493853
+127,city_nicosia,2021-May,0.8365728469,0.9942683732,0.2896511837
+128,city_nicosia,2021-Sep,0.995211768,0.4426742775,0.2572311547
+129,city_nicosia,2022-Jan,0.4359362671,0.3118280621,0.6705883368
+130,city_quito,2019-Jan,0.008417639206,0.3014245876,0.3052574326
+131,city_quito,2019-May,0.9857935547,0.1817759722,0.6952597878
+132,city_quito,2019-Sep,0.5276216133,0.130251563,0.3240003865
+133,city_quito,2020-Jan,0.9035196298,0.05698437866,0.7702244321
+134,city_quito,2020-May,0.3066932204,0.4436222048,0.260779084
+135,city_quito,2020-Sep,0.7272718268,0.5725148062,0.9164124193
+136,city_quito,2021-Jan,0.1059151872,0.08895582061,0.2928971124
+137,city_quito,2021-May,0.5118712419,0.3026817401,0.4279458258
+138,city_quito,2021-Sep,0.3330274894,0.4468848481,0.1236207096
+139,city_quito,2022-Jan,0.1244205704,0.1358899607,0.09450148278
+140,city_cairo,2019-Jan,0.4615407783,0.9520223142,0.5749502467
+141,city_cairo,2019-May,0.475409533,0.8210527062,0.4346789398
+142,city_cairo,2019-Sep,0.8066731319,0.6534694757,0.02837567269
+143,city_cairo,2020-Jan,0.8670750153,0.3625430406,0.3825884646
+144,city_cairo,2020-May,0.4951683874,0.3254080811,0.3654370688
+145,city_cairo,2020-Sep,0.7770142608,0.3716663152,0.7309639163
+146,city_cairo,2021-Jan,0.7855635031,0.066121297,0.4673674219
+147,city_cairo,2021-May,0.1730690216,0.6707204192,0.04674807743
+148,city_cairo,2021-Sep,0.489859121,0.7975075822,0.4092845343
+149,city_cairo,2022-Jan,0.4026321904,0.3023607084,0.4688463182
+150,city_addis_ababa,2019-Jan,0.4158713422,0.723030428,0.7597643722
+151,city_addis_ababa,2019-May,0.6785635739,0.1609066638,0.3381258458
+152,city_addis_ababa,2019-Sep,0.4569397919,0.526164665,0.1769840336
+153,city_addis_ababa,2020-Jan,0.6383916423,0.9232172286,0.1182792853
+154,city_addis_ababa,2020-May,0.7178022526,0.2387936324,0.02385348652
+155,city_addis_ababa,2020-Sep,0.9849294332,0.7956996228,0.4222712868
+156,city_addis_ababa,2021-Jan,0.07887778219,0.69387249,0.1295531304
+157,city_addis_ababa,2021-May,0.1808748003,0.06275186687,0.07472647253
+158,city_addis_ababa,2021-Sep,0.4839227903,0.4053925375,0.5147437288
+159,city_addis_ababa,2022-Jan,0.7803588092,0.727980068,0.6142244773
+160,city_berlin,2019-Jan,0.8326116144,0.1655051488,0.9828841108
+161,city_berlin,2019-May,0.250598596,0.8092499198,0.2239228888
+162,city_berlin,2019-Sep,0.8741896975,0.2665338382,0.1668458375
+163,city_berlin,2020-Jan,0.8399662922,0.7424132106,0.7362431642
+164,city_berlin,2020-May,0.6234997751,0.1409901662,0.1120024816
+165,city_berlin,2020-Sep,0.5161994486,0.5217599377,0.2535940892
+166,city_berlin,2021-Jan,0.1199007456,0.4344814743,0.1242415921
+167,city_berlin,2021-May,0.7303554923,0.3020973192,0.07171730289
+168,city_berlin,2021-Sep,0.7721955593,0.1087218774,0.6464209647
+169,city_berlin,2022-Jan,0.7614676523,0.08317084863,0.2325090714
+170,city_athens,2019-Jan,0.5327877289,0.9744463465,0.08180874782
+171,city_athens,2019-May,0.3652119168,0.01078413186,0.3276447375
+172,city_athens,2019-Sep,0.02943374111,0.9488744953,0.839420683
+173,city_athens,2020-Jan,0.8262571714,0.3638952168,0.00987960679
+174,city_athens,2020-May,0.5744768806,0.345623497,0.02740369312
+175,city_athens,2020-Sep,0.1795134643,0.3635695132,0.5559708634
+176,city_athens,2021-Jan,0.7699177565,0.392814441,0.61825789
+177,city_athens,2021-May,0.2212081657,0.9191236281,0.2313453472
+178,city_athens,2021-Sep,0.6074897578,0.7022955185,0.9424673176
+179,city_athens,2022-Jan,0.8794169545,0.3645845791,0.03908482265
+180,city_guatemala_city,2019-Jan,0.2898139176,0.763974548,0.1414512253
+181,city_guatemala_city,2019-May,0.6106569652,0.8240962332,0.6939830925
+182,city_guatemala_city,2019-Sep,0.5861384162,0.04609006852,0.8409472079
+183,city_guatemala_city,2020-Jan,0.7872155024,0.1606561264,0.2650589027
+184,city_guatemala_city,2020-May,0.1910735766,0.557900717,0.600110758
+185,city_guatemala_city,2020-Sep,0.6290094549,0.2562474722,0.7560094788
+186,city_guatemala_city,2021-Jan,0.4257358075,0.1027767675,0.4144983414
+187,city_guatemala_city,2021-May,0.7099732679,0.08313104975,0.284854386
+188,city_guatemala_city,2021-Sep,0.892413365,0.5532906407,0.2968339741
+189,city_guatemala_city,2022-Jan,0.8273477199,0.7395492724,0.9457886281
+190,city_jakarta,2019-Jan,0.9193766389,0.562096263,0.08597256563
+191,city_jakarta,2019-May,0.9613467154,0.6769316276,0.6942166445
+192,city_jakarta,2019-Sep,0.8911849078,0.7562774935,0.931896191
+193,city_jakarta,2020-Jan,0.4624306474,0.2038734797,0.9108010452
+194,city_jakarta,2020-May,0.7789672101,0.4319469274,0.5955267956
+195,city_jakarta,2020-Sep,0.937567279,0.5425494534,0.1971329509
+196,city_jakarta,2021-Jan,0.05969973106,0.8612950096,0.1110350332
+197,city_jakarta,2021-May,0.2180583953,0.129191418,0.8485609101
+198,city_jakarta,2021-Sep,0.5603349001,0.4438715596,0.3221066821
+199,city_jakarta,2022-Jan,0.5924979531,0.7839538361,0.9407650221
+200,city_tehran,2019-Jan,0.5292539582,0.9150355842,0.5624261915
+201,city_tehran,2019-May,0.7509693779,0.2702537133,0.05959118151
+202,city_tehran,2019-Sep,0.0004306332507,0.5449529067,0.7126608061
+203,city_tehran,2020-Jan,0.1082794742,0.4536112847,0.9691479921
+204,city_tehran,2020-May,0.3819988509,0.6632068954,0.5233308183
+205,city_tehran,2020-Sep,0.9333775196,0.6158183662,0.6980507813
+206,city_tehran,2021-Jan,0.4710513182,0.05454750359,0.5645271704
+207,city_tehran,2021-May,0.2485026502,0.7312942592,0.2627718368
+208,city_tehran,2021-Sep,0.01302679826,0.336700741,0.6003091579
+209,city_tehran,2022-Jan,0.819297579,0.9598619279,0.6779261352
+210,city_baghdad,2019-Jan,0.6571740151,0.7467470149,0.6081817043
+211,city_baghdad,2019-May,0.8853581326,0.7189311143,0.7448624368
+212,city_baghdad,2019-Sep,0.6146196468,0.6768913331,0.7571456788
+213,city_baghdad,2020-Jan,0.08117439619,0.8747030021,0.7191705185
+214,city_baghdad,2020-May,0.3522675259,0.8238104615,0.1537342642
+215,city_baghdad,2020-Sep,0.2356301438,0.3576600925,0.6118789786
+216,city_baghdad,2021-Jan,0.4391707399,0.4474647074,0.6430828897
+217,city_baghdad,2021-May,0.268736745,0.394059054,0.119349115
+218,city_baghdad,2021-Sep,0.2073373839,0.407147767,0.8267744414
+219,city_baghdad,2022-Jan,0.01562994523,0.7208303197,0.3132938272
+220,city_tokyo,2019-Jan,0.8352048365,0.4889643586,0.5708637608
+221,city_tokyo,2019-May,0.07092244122,0.7175987454,0.548696431
+222,city_tokyo,2019-Sep,0.435831484,0.7019761931,0.8220189625
+223,city_tokyo,2020-Jan,0.5223738584,0.9554340802,0.7499007696
+224,city_tokyo,2020-May,0.303390769,0.9935451138,0.9673256357
+225,city_tokyo,2020-Sep,0.4384910546,0.06373422983,0.9278227045
+226,city_tokyo,2021-Jan,0.4244047664,0.8205993601,0.3039457448
+227,city_tokyo,2021-May,0.01059055428,0.1387852586,0.202489681
+228,city_tokyo,2021-Sep,0.8243603537,0.01143751208,0.6110956967
+229,city_tokyo,2022-Jan,0.9934216564,0.5012722622,0.8378971436
+230,city_amman,2019-Jan,0.1540357106,0.6803451745,0.7406204618
+231,city_amman,2019-May,0.3376841081,0.8528543434,0.8947938379
+232,city_amman,2019-Sep,0.8652355208,0.7611803277,0.4579395501
+233,city_amman,2020-Jan,0.9236248973,0.597652822,0.3755196877
+234,city_amman,2020-May,0.4995982826,0.924389631,0.306112936
+235,city_amman,2020-Sep,0.5400326648,0.09029500814,0.474054147
+236,city_amman,2021-Jan,0.08564701964,0.3066904227,0.7320517679
+237,city_amman,2021-May,0.4889587211,0.2702591115,0.7290170229
+238,city_amman,2021-Sep,0.0335487674,0.2504805992,0.7181076944
+239,city_amman,2022-Jan,0.3418902923,0.8970783354,0.1739518732
+240,city_nairobi,2019-Jan,0.8272692723,0.2193240076,0.02060070785
+241,city_nairobi,2019-May,0.25413013,0.273279121,0.9193914915
+242,city_nairobi,2019-Sep,0.2576763868,0.8634612874,0.4355158525
+243,city_nairobi,2020-Jan,0.9763060138,0.1430317922,0.6483186049
+244,city_nairobi,2020-May,0.6939313329,0.5892953211,0.9663935709
+245,city_nairobi,2020-Sep,0.2764149061,0.1774120717,0.7812622959
+246,city_nairobi,2021-Jan,0.7707636847,0.8689268495,0.1363146058
+247,city_nairobi,2021-May,0.9163507683,0.2515377182,0.07565517255
+248,city_nairobi,2021-Sep,0.7513935872,0.1532284207,0.7363183336
+249,city_nairobi,2022-Jan,0.1767128894,0.1454489297,0.3971665474
+250,city_beirut,2019-Jan,0.788339519,0.5127333364,0.8333304991
+251,city_beirut,2019-May,0.7230935424,0.2298084849,0.5472381483
+252,city_beirut,2019-Sep,0.5909867246,0.02179296134,0.7592174254
+253,city_beirut,2020-Jan,0.9340891126,0.8696574846,0.5400465709
+254,city_beirut,2020-May,0.0276754093,0.8986496952,0.5265469731
+255,city_beirut,2020-Sep,0.4649235896,0.3356422036,0.5517011162
+256,city_beirut,2021-Jan,0.7389463326,0.947502386,0.6815278477
+257,city_beirut,2021-May,0.7670585948,0.6473540111,0.5277366776
+258,city_beirut,2021-Sep,0.5942151705,0.4821676255,0.1016388854
+259,city_beirut,2022-Jan,0.5882001691,0.361997451,0.293222771
+260,city_tripoli,2019-Jan,0.2677810804,0.2357072677,0.3565856652
+261,city_tripoli,2019-May,0.07104588209,0.5513605839,0.0451456715
+262,city_tripoli,2019-Sep,0.4257855731,0.616771154,0.5592935074
+263,city_tripoli,2020-Jan,0.3449656127,0.687252387,0.2071332881
+264,city_tripoli,2020-May,0.05954930585,0.08429442185,0.5561507364
+265,city_tripoli,2020-Sep,0.5287426821,0.7518475156,0.605454666
+266,city_tripoli,2021-Jan,0.926167857,0.4054919777,0.01780159166
+267,city_tripoli,2021-May,0.1158836845,0.1263661794,0.5107072656
+268,city_tripoli,2021-Sep,0.3098832321,0.8877507511,0.9402516681
+269,city_tripoli,2022-Jan,0.2471150536,0.9391734631,0.05978336229
+270,city_kuala_lumpur,2019-Jan,0.6863528274,0.8697980657,0.2282523362
+271,city_kuala_lumpur,2019-May,0.8358905297,0.9856091378,0.9827359299
+272,city_kuala_lumpur,2019-Sep,0.8423115828,0.8475761347,0.6705055844
+273,city_kuala_lumpur,2020-Jan,0.352567735,0.1954722531,0.2030722961
+274,city_kuala_lumpur,2020-May,0.04741808923,0.210640203,0.01469151867
+275,city_kuala_lumpur,2020-Sep,0.957184061,0.4772033411,0.06693120676
+276,city_kuala_lumpur,2021-Jan,0.8694608057,0.4899108808,0.6298810317
+277,city_kuala_lumpur,2021-May,0.03785739177,0.215056941,0.4898345725
+278,city_kuala_lumpur,2021-Sep,0.8474861196,0.8203214118,0.9639926419
+279,city_kuala_lumpur,2022-Jan,0.09388447049,0.8918711252,0.8982826914
+280,city_male,2019-Jan,0.9535318247,0.150372675,0.945000347
+281,city_male,2019-May,0.7412962727,0.003038298983,0.4180035581
+282,city_male,2019-Sep,0.2345256191,0.9034615199,0.4484671987
+283,city_male,2020-Jan,0.04034677353,0.1748185368,0.4505723828
+284,city_male,2020-May,0.5639263503,0.2796195837,0.2018342222
+285,city_male,2020-Sep,0.2502278203,0.5534360568,0.4339739447
+286,city_male,2021-Jan,0.3031630191,0.07439445923,0.4700919847
+287,city_male,2021-May,0.8624679204,0.2578625741,0.09027702491
+288,city_male,2021-Sep,0.2454379645,0.9075344381,0.05214485101
+289,city_male,2022-Jan,0.3611491011,0.9851549825,0.4531224627
+290,city_mexico_city,2019-Jan,0.5367583915,0.258080948,0.6816746827
+291,city_mexico_city,2019-May,0.9853964418,0.6359366543,0.4883879642
+292,city_mexico_city,2019-Sep,0.9563011498,0.0299396029,0.9594745399
+293,city_mexico_city,2020-Jan,0.4688441336,0.8191478053,0.8617118239
+294,city_mexico_city,2020-May,0.1927625183,0.5748900138,0.8513407749
+295,city_mexico_city,2020-Sep,0.4454512258,0.5824312156,0.9636387659
+296,city_mexico_city,2021-Jan,0.9859257045,0.1759599736,0.4286920936
+297,city_mexico_city,2021-May,0.363776648,0.1809230842,0.2368178691
+298,city_mexico_city,2021-Sep,0.6824795264,0.2990136144,0.0405011663
+299,city_mexico_city,2022-Jan,0.8905587395,0.1905990529,0.4882507814
+300,city_ulan_bator,2019-Jan,0.6741485464,0.717576578,0.4293573811
+301,city_ulan_bator,2019-May,0.3262047159,0.8716206568,0.5458236269
+302,city_ulan_bator,2019-Sep,0.6265632177,0.2317454106,0.7036908001
+303,city_ulan_bator,2020-Jan,0.5113218116,0.221105242,0.888826252
+304,city_ulan_bator,2020-May,0.3906062072,0.66427438,0.2935980572
+305,city_ulan_bator,2020-Sep,0.3971036952,0.01783759712,0.8998790336
+306,city_ulan_bator,2021-Jan,0.2637427101,0.2385390121,0.6249611526
+307,city_ulan_bator,2021-May,0.912242776,0.08731497158,0.6532080205
+308,city_ulan_bator,2021-Sep,0.397708583,0.8048328144,0.7926631264
+309,city_ulan_bator,2022-Jan,0.9942061508,0.2470806419,0.4383188216
+310,city_rabat,2019-Jan,0.2230411495,0.3676032952,0.4107616888
+311,city_rabat,2019-May,0.1492052294,0.3892316663,0.1094391123
+312,city_rabat,2019-Sep,0.5472040034,0.9976653002,0.1954636489
+313,city_rabat,2020-Jan,0.9966868022,0.2096797583,0.3057921875
+314,city_rabat,2020-May,0.2045133008,0.2371876672,0.4427255118
+315,city_rabat,2020-Sep,0.925035033,0.584423711,0.6622095958
+316,city_rabat,2021-Jan,0.09596785333,0.1155214101,0.9929036089
+317,city_rabat,2021-May,0.5279036078,0.2882972716,0.2605861754
+318,city_rabat,2021-Sep,0.9105437212,0.3634054264,0.5256147774
+319,city_rabat,2022-Jan,0.7458623714,0.1574213382,0.6264820458
+320,city_amsterdam,2019-Jan,0.799256021,0.3395427351,0.4031577596
+321,city_amsterdam,2019-May,0.1804581241,0.845954797,0.3228938347
+322,city_amsterdam,2019-Sep,0.1970508803,0.04130658614,0.5235063506
+323,city_amsterdam,2020-Jan,0.9242590394,0.1852741273,0.3749524614
+324,city_amsterdam,2020-May,0.8783562576,0.08261349126,0.3541495132
+325,city_amsterdam,2020-Sep,0.8995390999,0.8467088641,0.8145635751
+326,city_amsterdam,2021-Jan,0.6193291884,0.3946284311,0.6509909684
+327,city_amsterdam,2021-May,0.7058310687,0.2254146029,0.3006021917
+328,city_amsterdam,2021-Sep,0.6448915078,0.2207063867,0.7670154359
+329,city_amsterdam,2022-Jan,0.6566724581,0.4963253272,0.5882010246
+330,city_wellington,2019-Jan,0.975771214,0.7828569709,0.8579568289
+331,city_wellington,2019-May,0.4808303848,0.2147995042,0.5427822914
+332,city_wellington,2019-Sep,0.982612098,0.2017583263,0.6818840957
+333,city_wellington,2020-Jan,0.8282939128,0.0173039828,0.8484000112
+334,city_wellington,2020-May,0.7067879274,0.1232934138,0.1583243715
+335,city_wellington,2020-Sep,0.05426185515,0.9523095362,0.8600642333
+336,city_wellington,2021-Jan,0.849837313,0.6723412286,0.3539009568
+337,city_wellington,2021-May,0.851419205,0.08734822939,0.3631028432
+338,city_wellington,2021-Sep,0.5211397365,0.601091111,0.8736619915
+339,city_wellington,2022-Jan,0.0004370141048,0.7856350051,0.7967341982
+340,city_managua,2019-Jan,0.4449076437,0.07095081666,0.2226768192
+341,city_managua,2019-May,0.9058054532,0.9825352592,0.8801532619
+342,city_managua,2019-Sep,0.4664849351,0.1507856153,0.6859989836
+343,city_managua,2020-Jan,0.7063256142,0.8480495571,0.6506210446
+344,city_managua,2020-May,0.5672273277,0.1732735134,0.0473325036
+345,city_managua,2020-Sep,0.6152126641,0.262875193,0.06784724677
+346,city_managua,2021-Jan,0.5630548661,0.139303683,0.2497055152
+347,city_managua,2021-May,0.7090141669,0.6050612033,0.717160999
+348,city_managua,2021-Sep,0.6726886326,0.3955881513,0.05659926761
+349,city_managua,2022-Jan,0.5118331134,0.9697227523,0.6694463186
+350,city_abuja,2019-Jan,0.7215577985,0.8069645818,0.2144030138
+351,city_abuja,2019-May,0.2389187672,0.1132551265,0.6855980263
+352,city_abuja,2019-Sep,0.3713433464,0.8430974465,0.7186428163
+353,city_abuja,2020-Jan,0.3213201344,0.9099336087,0.8449178856
+354,city_abuja,2020-May,0.8469259071,0.5524627768,0.9260609535
+355,city_abuja,2020-Sep,0.4366534985,0.4725483191,0.6004087497
+356,city_abuja,2021-Jan,0.6629119774,0.2245666268,0.2164730197
+357,city_abuja,2021-May,0.7151602496,0.852215806,0.9994776477
+358,city_abuja,2021-Sep,0.2657906656,0.8775827085,0.02569985435
+359,city_abuja,2022-Jan,0.6656366265,0.5595354603,0.3052805174
+360,city_islamabad,2019-Jan,0.5657435109,0.06594882308,0.05639372024
+361,city_islamabad,2019-May,0.3617440488,0.8182270972,0.5020515613
+362,city_islamabad,2019-Sep,0.8972054882,0.5389161919,0.08204922443
+363,city_islamabad,2020-Jan,0.6361910136,0.7719467598,0.3555156382
+364,city_islamabad,2020-May,0.5658655482,0.9978291866,0.9204755129
+365,city_islamabad,2020-Sep,0.5229417395,0.4433901007,0.3977836013
+366,city_islamabad,2021-Jan,0.9304358199,0.5494074576,0.689688983
+367,city_islamabad,2021-May,0.04229158644,0.4005375448,0.4328307124
+368,city_islamabad,2021-Sep,0.3071366189,0.4742819877,0.8376670766
+369,city_islamabad,2022-Jan,0.9667463886,0.8263189953,0.9260605166
+370,city_lima,2019-Jan,0.004449000129,0.5359554688,0.5170556156
+371,city_lima,2019-May,0.0040391066,0.727889608,0.3979039803
+372,city_lima,2019-Sep,0.4752783789,0.286290551,0.9038587969
+373,city_lima,2020-Jan,0.5243305401,0.9542521021,0.8437255443
+374,city_lima,2020-May,0.5189281056,0.9532682341,0.04858519966
+375,city_lima,2020-Sep,0.9698181754,0.2626736181,0.07547748485
+376,city_lima,2021-Jan,0.42487374,0.7219955088,0.03794786379
+377,city_lima,2021-May,0.1580065461,0.7412839844,0.78239396
+378,city_lima,2021-Sep,0.5313078226,0.6906399598,0.1497155574
+379,city_lima,2022-Jan,0.7137341222,0.9608880466,0.7117129761
+380,city_bucharest,2019-Jan,0.9989856747,0.3173551828,0.3589126521
+381,city_bucharest,2019-May,0.7987576639,0.431771908,0.1180147721
+382,city_bucharest,2019-Sep,0.4474069189,0.716171076,0.3027452883
+383,city_bucharest,2020-Jan,0.9724268855,0.3257941961,0.8441895702
+384,city_bucharest,2020-May,0.9980411751,0.1256631536,0.07115468458
+385,city_bucharest,2020-Sep,0.1213183636,0.3941146639,0.06092239127
+386,city_bucharest,2021-Jan,0.4205241143,0.1941809277,0.4877147348
+387,city_bucharest,2021-May,0.3360387926,0.3922642193,0.2054573166
+388,city_bucharest,2021-Sep,0.06667197523,0.1235198397,0.5762869522
+389,city_bucharest,2022-Jan,0.306929458,0.7091646997,0.8807736754
+390,city_moscow,2019-Jan,0.4884820054,0.3664299465,0.5947931914
+391,city_moscow,2019-May,0.8045965787,0.645890862,0.6135267009
+392,city_moscow,2019-Sep,0.5168853064,0.005617348514,0.03449093575
+393,city_moscow,2020-Jan,0.5083457969,0.1835582116,0.9683744996
+394,city_moscow,2020-May,0.1155265419,0.02495497214,0.9077392365
+395,city_moscow,2020-Sep,0.3679246739,0.4012096928,0.1788263876
+396,city_moscow,2021-Jan,0.04563746129,0.5524970339,0.2758883611
+397,city_moscow,2021-May,0.874515788,0.5738406989,0.4389562884
+398,city_moscow,2021-Sep,0.5644210913,0.1248259343,0.3045741317
+399,city_moscow,2022-Jan,0.02472030916,0.5212881284,0.6865817501
+400,city_belgrade,2019-Jan,0.2054378336,0.3388140331,0.1560520775
+401,city_belgrade,2019-May,0.8233419498,0.06099252492,0.3285799916
+402,city_belgrade,2019-Sep,0.737910215,0.0218623579,0.8147299491
+403,city_belgrade,2020-Jan,0.9112117631,0.9656860025,0.3786632918
+404,city_belgrade,2020-May,0.7803837773,0.09974279125,0.003345917394
+405,city_belgrade,2020-Sep,0.2159103445,0.4208667863,0.306492557
+406,city_belgrade,2021-Jan,0.8099756545,0.1659555892,0.5686013193
+407,city_belgrade,2021-May,0.9153223024,0.7262449027,0.5551206235
+408,city_belgrade,2021-Sep,0.5130398179,0.01271602991,0.9210065704
+409,city_belgrade,2022-Jan,0.815222032,0.2659741857,0.2871871833
+410,city_singapore,2019-Jan,0.2134172681,0.8580276605,0.9601842113
+411,city_singapore,2019-May,0.6023894791,0.4216392972,0.7161700994
+412,city_singapore,2019-Sep,0.6858661731,0.7862881275,0.2316293129
+413,city_singapore,2020-Jan,0.6841130768,0.2949128642,0.5366420271
+414,city_singapore,2020-May,0.498180307,0.47856757,0.5634083248
+415,city_singapore,2020-Sep,0.005418301445,0.6835751602,0.5749891016
+416,city_singapore,2021-Jan,0.2189054618,0.9144602713,0.7515237128
+417,city_singapore,2021-May,0.4224346227,0.2277588726,0.5884623504
+418,city_singapore,2021-Sep,0.00726630776,0.4287783353,0.9893778021
+419,city_singapore,2022-Jan,0.415505623,0.5043323742,0.04127731435
+420,city_seoul,2019-Jan,0.4158292137,0.3235311166,0.3313557261
+421,city_seoul,2019-May,0.3720615125,0.5183966398,0.5281903814
+422,city_seoul,2019-Sep,0.2898671082,0.1498256654,0.6543104049
+423,city_seoul,2020-Jan,0.2590961078,0.03198788857,0.7939989818
+424,city_seoul,2020-May,0.158505935,0.646670114,0.8497340793
+425,city_seoul,2020-Sep,0.7733778533,0.8329950401,0.6951126836
+426,city_seoul,2021-Jan,0.2333382418,0.3836525618,0.7553079657
+427,city_seoul,2021-May,0.7819967758,0.974315225,0.07959266939
+428,city_seoul,2021-Sep,0.5964142264,0.248241969,0.6541351389
+429,city_seoul,2022-Jan,0.90841441,0.1943781343,0.09601869236
+430,city_tunis,2019-Jan,0.6652183138,0.4894266427,0.4240159206
+431,city_tunis,2019-May,0.9884960516,0.9755110617,0.06384271908
+432,city_tunis,2019-Sep,0.680123991,0.3601116984,0.5918194417
+433,city_tunis,2020-Jan,0.1473826674,0.9219684102,0.513104658
+434,city_tunis,2020-May,0.7317874879,0.7317059155,0.5088701786
+435,city_tunis,2020-Sep,0.978750762,0.8405181067,0.9071571207
+436,city_tunis,2021-Jan,0.4223105477,0.182839254,0.9445235115
+437,city_tunis,2021-May,0.1376962812,0.3380520816,0.9820850094
+438,city_tunis,2021-Sep,0.9888050345,0.7938678388,0.4087101449
+439,city_tunis,2022-Jan,0.08062628741,0.811235545,0.6412991336
+440,city_bangkok,2019-Jan,0.7740886353,0.00393096204,0.9355208309
+441,city_bangkok,2019-May,0.9261865051,0.5543156401,0.2301005313
+442,city_bangkok,2019-Sep,0.0036399897,0.5151920906,0.7759955482
+443,city_bangkok,2020-Jan,0.4058027436,0.03111692238,0.5575806328
+444,city_bangkok,2020-May,0.657934059,0.5883429191,0.4110502161
+445,city_bangkok,2020-Sep,0.2211613657,0.8307157083,0.5495225194
+446,city_bangkok,2021-Jan,0.9381384113,0.3390859316,0.3941873429
+447,city_bangkok,2021-May,0.1913245397,0.8985675591,0.1613629563
+448,city_bangkok,2021-Sep,0.4849294818,0.07434437657,0.212956493
+449,city_bangkok,2022-Jan,0.5632024472,0.05342124515,0.608214207
+450,city_washington_d.c.,2019-Jan,0.706354796,0.5566582481,0.3888501308
+451,city_washington_d.c.,2019-May,0.3967146921,0.6871371398,0.3749746333
+452,city_washington_d.c.,2019-Sep,0.1461645524,0.07178227096,0.008434495791
+453,city_washington_d.c.,2020-Jan,0.7767257209,0.6806659613,0.2452377716
+454,city_washington_d.c.,2020-May,0.03369730849,0.4195955155,0.8280863275
+455,city_washington_d.c.,2020-Sep,0.7276129361,0.1779246857,0.1500722549
+456,city_washington_d.c.,2021-Jan,0.9155963204,0.9973251079,0.701925965
+457,city_washington_d.c.,2021-May,0.1160842378,0.1693766214,0.6736147037
+458,city_washington_d.c.,2021-Sep,0.4549449302,0.9554399549,0.7586370391
+459,city_washington_d.c.,2022-Jan,0.4006997702,0.955893003,0.1364601882
+460,city_hanoi,2019-Jan,0.4131646918,0.6762825725,0.5131651918
+461,city_hanoi,2019-May,0.219613407,0.4764566921,0.5947576005
+462,city_hanoi,2019-Sep,0.7775954747,0.5535598775,0.9161480338
+463,city_hanoi,2020-Jan,0.1749110224,0.03153303649,0.2325527303
+464,city_hanoi,2020-May,0.6513750193,0.9378385276,0.6723599033
+465,city_hanoi,2020-Sep,0.8846442259,0.5905572431,0.8375318419
+466,city_hanoi,2021-Jan,0.04438681781,0.7197215338,0.5135403183
+467,city_hanoi,2021-May,0.8841873451,0.01386415254,0.9290412269
+468,city_hanoi,2021-Sep,0.9471121112,0.8845281879,0.4825962156
+469,city_hanoi,2022-Jan,0.6399693588,0.3162389532,0.351121779
+470,city_harare,2019-Jan,0.05113965261,0.6079969608,0.7127907344
+471,city_harare,2019-May,0.5499694149,0.7112740295,0.8657201091
+472,city_harare,2019-Sep,0.6705953617,0.8481746283,0.7781915229
+473,city_harare,2020-Jan,0.5448455637,0.761481783,0.3161047732
+474,city_harare,2020-May,0.02555600558,0.8157707267,0.05503697195
+475,city_harare,2020-Sep,0.627355327,0.5530698033,0.5992110078
+476,city_harare,2021-Jan,0.6049262896,0.527575273,0.5758586976
+477,city_harare,2021-May,0.4274464672,0.22878636,0.1059028406
+478,city_harare,2021-Sep,0.3598503868,0.4448781561,0.1940659583
+479,city_harare,2022-Jan,0.2554936994,0.04221218981,0.8283157554
+480,city_ankara,2019-Jan,0.5773037837,0.2326703291,0.5464883464
+481,city_ankara,2019-May,0.7535813143,0.4992113773,0.9809685139
+482,city_ankara,2019-Sep,0.5976797307,0.794414639,0.8742555726
+483,city_ankara,2020-Jan,0.08520386108,0.8773860695,0.3858137862
+484,city_ankara,2020-May,0.004111445517,0.8040046923,0.1569392782
+485,city_ankara,2020-Sep,0.2601369272,0.2083602167,0.8048114997
+486,city_ankara,2021-Jan,0.06646180846,0.1508074934,0.7621242697
+487,city_ankara,2021-May,0.2056833359,0.4014711821,0.5422262985
+488,city_ankara,2021-Sep,0.2211609615,0.7420965859,0.9192380563
+489,city_ankara,2022-Jan,0.5664276514,0.1191242719,0.5142831272
\ No newline at end of file
diff --git a/run_tree/osx/arm64/debug/convert_csv b/run_tree/osx/arm64/debug/convert_csv
new file mode 100755
index 0000000..e1790c9
Binary files /dev/null and b/run_tree/osx/arm64/debug/convert_csv differ
diff --git a/run_tree/osx/arm64/debug/lumenarium b/run_tree/osx/arm64/debug/lumenarium
new file mode 100755
index 0000000..2d5ff7e
Binary files /dev/null and b/run_tree/osx/arm64/debug/lumenarium differ
diff --git a/run_tree/osx/arm64/prod/lumenarium b/run_tree/osx/arm64/prod/lumenarium
new file mode 100755
index 0000000..7eb2057
Binary files /dev/null and b/run_tree/osx/arm64/prod/lumenarium differ
diff --git a/run_tree/osx/arm64/prod/lumenarium_arm64 b/run_tree/osx/arm64/prod/lumenarium_arm64
new file mode 100755
index 0000000..e3c8be8
Binary files /dev/null and b/run_tree/osx/arm64/prod/lumenarium_arm64 differ
diff --git a/run_tree/osx/intel/debug/lumenarium b/run_tree/osx/intel/debug/lumenarium
new file mode 100755
index 0000000..9cce34e
Binary files /dev/null and b/run_tree/osx/intel/debug/lumenarium differ
diff --git a/run_tree/osx/intel/prod/lumenarium b/run_tree/osx/intel/prod/lumenarium
new file mode 100755
index 0000000..9ed88e2
Binary files /dev/null and b/run_tree/osx/intel/prod/lumenarium differ
diff --git a/run_tree/raspi/arm64/debug/convert_csv b/run_tree/raspi/arm64/debug/convert_csv
new file mode 100755
index 0000000..1dce8d3
Binary files /dev/null and b/run_tree/raspi/arm64/debug/convert_csv differ
diff --git a/run_tree/raspi/arm64/debug/lumenarium b/run_tree/raspi/arm64/debug/lumenarium
new file mode 100755
index 0000000..eed03e6
Binary files /dev/null and b/run_tree/raspi/arm64/debug/lumenarium differ
diff --git a/run_tree/raspi/arm64/prod/convert_csv b/run_tree/raspi/arm64/prod/convert_csv
new file mode 100755
index 0000000..5578c4d
Binary files /dev/null and b/run_tree/raspi/arm64/prod/convert_csv differ
diff --git a/run_tree/raspi/intel/debug/lumenarium b/run_tree/raspi/intel/debug/lumenarium
new file mode 100755
index 0000000..09a9d39
Binary files /dev/null and b/run_tree/raspi/intel/debug/lumenarium differ
diff --git a/run_tree/wasm/debug/loader.html b/run_tree/wasm/debug/loader.html
new file mode 100644
index 0000000..840c19b
--- /dev/null
+++ b/run_tree/wasm/debug/loader.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/run_tree/wasm/debug/loader.js b/run_tree/wasm/debug/loader.js
new file mode 100644
index 0000000..3573534
--- /dev/null
+++ b/run_tree/wasm/debug/loader.js
@@ -0,0 +1,39 @@
+
+let module = null;
+let instance = null;
+
+
+async function load_webassembly_module ()
+{
+ lumenarium_wasm_imports = webgl_add_imports("#gl_canvas", lumenarium_wasm_imports);
+
+ const path = "lumenarium.wasm";
+ const promise = fetch(path);
+ const module = await WebAssembly.compileStreaming(promise);
+
+ let memory = new WebAssembly.Memory({ initial: 2 });
+ const env = {
+ memory,
+ ...lumenarium_wasm_imports,
+ };
+
+ let table = new WebAssembly.Table({ element: "anyfunc", initial: 32, });
+
+ instance = await WebAssembly.instantiate(module, { env })
+ .then((res, err) => {
+ return res;
+ })
+ .catch((a, b) => {
+ console.log(a,b);
+ });
+ lumenarium_wasm_instance = instance;
+
+ // If function '__wasm_call_ctors' (global C++ constructors) exists, call it
+ if (instance.exports.__wasm_call_ctors) instance.exports.__wasm_call_ctors();
+
+ // If function 'main' exists, call it with dummy arguments
+ let result = 0;
+ if (instance.exports.main) result = instance.exports.main();
+}
+
+window.addEventListener("load", load_webassembly_module)
diff --git a/run_tree/wasm/debug/lumenarium_wasm_imports.js b/run_tree/wasm/debug/lumenarium_wasm_imports.js
new file mode 100644
index 0000000..ff42920
--- /dev/null
+++ b/run_tree/wasm/debug/lumenarium_wasm_imports.js
@@ -0,0 +1,507 @@
+var lumenarium_wasm_module = null;
+var lumenarium_wasm_instance = null;
+
+var WASM_PAGE_SIZE = 65536;
+
+function wasm_mem_get_u8_arr(inst, ptr, size)
+{
+ let view = new Uint8Array(inst.exports.memory.buffer, ptr, size);
+ return view;
+}
+
+function wasm_read_string(inst, ptr, len)
+{
+ let view = wasm_mem_get_u8_arr(inst, ptr, len);
+ let string = '';
+ for (let i = 0; i < len; i++)
+ {
+ string += String.fromCharCode(view[i]);
+ }
+ return string;
+}
+
+function wasm_write_bytes(inst, src, ptr, len)
+{
+ let view = wasm_mem_get_u8_arr(inst, ptr, len);
+ for (let i = 0; i < len; i++) view[i] = src[i];
+}
+
+function wasm_get_proc(inst, proc_ptr)
+{
+ let result = inst.exports.__indirect_function_table.get(proc_ptr);
+ return result;
+}
+
+function fract (v) { return v % 1; }
+
+function u32_to_byte_array_32 (v)
+{
+ let result = [0, 0, 0, 0];
+ result[0] = (v & 0xff);
+ result[1] = (((v - result[0]) >> 8 ) & 0xff);
+ result[2] = (((v - result[1]) >> 16) & 0xff);
+ result[3] = (((v - result[2]) >> 24) & 0xff);
+ return result;
+}
+
+function byte_array_32_to_u32 (arr)
+{
+ // NOTE(PS): the '>>>' operators in this function deal with the fact
+ // that bit shift operators convert numbers to s32's. The >>> just
+ // converts them back to u32s
+ let r0 = ((arr[0] & 0xff) << 0 );
+ let r1 = ((arr[1] & 0xff) << 8 );
+ let r2 = ((arr[2] & 0xff) << 16);
+ let r3 = (((arr[3] & 0xff) << 24) >>> 0);
+ let result = (r0 | r1 | r2 | r3) >>> 0;
+ return result;
+}
+
+function put_u32 (ptr, value)
+{
+ let src = u32_to_byte_array_32(value);
+ wasm_write_bytes(lumenarium_wasm_instance, src, ptr, 4);
+}
+
+var lumenarium_wasm_imports = {
+
+ memset: (dst, size, value) => {
+ let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size);
+ for (let i = 0; i < size; i++)
+ {
+ view_dst[i] = value;
+ }
+ },
+
+ memcpy: (dst, src, size) => {
+ let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size);
+ let view_src = wasm_mem_get_u8_arr(lumenarium_wasm_instance, src, size);
+ for (let i = 0; i < size; i++)
+ {
+ view_dst[i] = view_src[i];
+ }
+ },
+
+ wasm_assert_always: (file, file_len, line) => {
+ let file_str = wasm_read_string(lumenarium_wasm_instance, file, file_len);
+ console.assert(false, "At: " + file_str + "::" + line);
+ },
+
+ wasm_get_memory_size: () => {
+ return instance.exports.memory.buffer.byteLength;
+ },
+
+ wasm_mem_grow: (new_size) => {
+ let new_size_ = new_size >>> 0;
+ let pages = new_size_ / WASM_PAGE_SIZE;
+ let pages_rem = fract(pages);
+ if (pages_rem > 0) pages = Math.floor(pages) + 1;
+ let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength;
+ let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages);
+
+ console.log("mem_grow\n",
+ "req size: ", new_size_, "\n",
+ "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n",
+ "old size: ", size_before, "\n",
+ "grew by: ", (pages * WASM_PAGE_SIZE), "\n",
+ "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, "");
+ },
+
+ malloc: (size) => {
+
+ },
+
+ free: (base) => {
+
+ },
+
+ sin: Math.sin,
+ sinf: Math.sin,
+ cos: Math.cos,
+ cosf: Math.cos,
+ tan: Math.tan,
+ tanf: Math.tan,
+ asin: Math.asin,
+ asinf: Math.asin,
+ acos: Math.acos,
+ acosf: Math.acos,
+ atan: Math.atan,
+ atanf: Math.atan,
+ pow: Math.pow,
+ powf: Math.pow,
+ fmodf: (f,d) => { return f % d; },
+ strlen: (ptr) => {
+ let len = 0;
+ let len_checked = 0;
+ let len_to_check = 256;
+ let found_end = false;
+ while (true)
+ {
+ let string = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, len_checked);
+ for (let i = len_checked; i < len_to_check; i++)
+ {
+ if (string[i] == 0)
+ {
+ len = i;
+ break;
+ }
+ }
+ len_checked *= 2;
+ }
+ return len_checked;
+ },
+
+ wasm_platform_file_async_work_on_job: (path, path_len, data, data_size, read, write) => {
+
+ },
+
+ wasm_performance_now: () => {
+ return performance.now();
+ },
+
+ wasm_sleep: (milliseconds) => {
+ let start = Date.now();
+ for (let at = Date.now(); (at - start) < milliseconds; at = Date.now()) {}
+ },
+
+ wasm_fetch: async (file_path, file_path_len, dest, dest_size) => {
+ let path = wasm_read_string(lumenarium_wasm_instance, file_path, file_path_len);
+ fetch(path)
+ .then(async (res) => {
+ // TODO(PS): success checking
+ let reader = res.body.getReader();
+ let read_res = { done: false };
+
+ let view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dest, dest_size);
+ let last_write = 0;
+ while (!read_res.done)
+ {
+ read_res = await reader.read();
+ if (read_res.done) break;
+
+ let len = read_res.value.length;
+ let write_end = last_write + len;
+ for (let i = last_write; i < write_end; i++)
+ {
+ view[i] = read_res.value[i - last_write];
+ }
+ last_write = write_end + 1;
+ }
+ });
+ return 0;
+ },
+
+ wasm_request_animation_frame: (cb) => {
+ let cb_proc = wasm_get_proc(lumenarium_wasm_instance, cb);
+ window.requestAnimationFrame(cb_proc);
+ },
+
+ print: (str_base, len) => {
+ let string = wasm_read_string(lumenarium_wasm_instance, str_base, len);
+ console.log(string);
+ },
+
+ wasm_get_canvas_dim: (w_ptr, h_ptr) => {
+ const canvas = document.querySelector("#gl_canvas");
+
+ let w_view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, w_ptr, 4);
+ let w = canvas.width;
+ let wb = u32_to_byte_array_32(w);
+ for (let i = 0; i < 4; i++) w_view[i] = wb[i];
+
+ let h_view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, h_ptr, 4);
+ let h = canvas.height;
+ let hb = u32_to_byte_array_32(h);
+ for (let i = 0; i < 4; i++) h_view[i] = hb[i];
+ },
+};
+
+///////////////////////////////////////
+// Web GL Imports
+
+let gl = null;
+let gl_error = false;
+
+function glErrorReport(outer_args) {
+ const err = gl.getError();
+ if (err == gl.NO_ERROR) return;
+
+ gl_error = true;
+ let msg = "";
+ switch (err) {
+ case gl.NO_ERROR: { msg = "NO_ERROR"; } break;
+ case gl.INVALID_ENUM: { msg = "INVALID_ENUM"; } break;
+ case gl.INVALID_VALUE: { msg = "INVALID_VALUE"; } break;
+ case gl.INVALID_OPERATION: { msg = "INVALID_OPERATION"; } break;
+ case gl.INVALID_FRAMEBUFFER_OPERATION: { msg = "INVALID_FRAMEBUFFER_OPERATION"; } break;
+ case gl.OUT_OF_MEMORY: { msg = "OUT_OF_MEMORY"; } break;
+ case gl.CONTEXT_LOST_WEBGL: { msg = "CONTEXT_LOST_WEBGL"; } break;
+ default: { msg = "Uknown error"; } break;
+ }
+ console.error(`WebGL Error: ${msg} ${err}`, outer_args);
+}
+
+// NOTE(PS): it seems like its not enough to set
+// the values of imports to gl.function
+// ie. imports.glClearColor = gl.clearColor
+// instead we need to wrap them for some reason.
+// Not sure why
+function glClearColor (r, g, b, a) { return gl.clearColor(r,g,b,a); }
+function glEnable(v) {
+ const r = gl.enable(v);
+ glErrorReport(arguments);
+ return r;
+}
+function glDisable(v) {
+ const r = gl.disable(v);
+ glErrorReport(arguments);
+ return r;
+}
+function glBlendFunc(a,b) {
+ const r = gl.blendFunc(a,b);
+ glErrorReport(arguments);
+ return r;
+}
+function glViewport(xmin, ymin, xmax, ymax) { return gl.viewport(xmin,ymin,xmax,ymax); }
+function glDepthFunc(v) {
+ const r = gl.depthFunc(v);
+ glErrorReport(arguments);
+ return r;
+}
+function glClear(mask) {
+ const r = gl.clear(mask);
+ glErrorReport(arguments);
+ return r;
+}
+
+let glBuffers = [];
+let glShaders = [];
+let glPrograms = [];
+let glTextures = [];
+function gl_get_managed_resource(arr, id) {
+ if (id == 0) return null;
+ return arr[id - 1];
+}
+function gl_get_buffer(id) { return gl_get_managed_resource(glBuffers, id); }
+function gl_get_shader(id) { return gl_get_managed_resource(glShaders, id); }
+function gl_get_program(id) { return gl_get_managed_resource(glPrograms, id); }
+function gl_get_texture(id) { return gl_get_managed_resource(glTextures, id); }
+
+function glCreateBuffer() {
+ let buffer = gl.createBuffer();
+ glErrorReport(arguments);
+ let new_len = glBuffers.push(buffer);
+ return new_len;
+}
+
+function glBindBuffer(buffer_kind, buffer_id)
+{
+ const r = gl.bindBuffer(buffer_kind, gl_get_buffer(buffer_id));
+ glErrorReport(arguments);
+ return r;
+}
+
+function glBufferData(target, size, ptr, usage)
+{
+ let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size);
+ const r = gl.bufferData(target, data, usage);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glBufferSubData(target, offset, size, ptr)
+{
+ let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size);
+ const r = gl.bufferSubData(target, offset, data, 0, size);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glCreateShader(kind)
+{
+ let shader = gl.createShader(kind);
+ glErrorReport(arguments);
+ let new_len = glShaders.push(shader);
+ return new_len;
+}
+
+function glShaderSource(shader_id, shader_code, shader_code_len)
+{
+ let str = wasm_read_string(lumenarium_wasm_instance, shader_code, shader_code_len);
+ const r = gl.shaderSource(gl_get_shader(shader_id), str);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glCompileShader(shader_id)
+{
+ let s = gl_get_shader(shader_id);
+ let r = gl.compileShader(s);
+ glErrorReport(arguments);
+ let m = gl.getShaderInfoLog(s);
+ glErrorReport(arguments);
+ if (m.length > 0)
+ {
+ console.error("glCompileShader: \n\n" + m);
+ }
+}
+
+function glCreateProgram()
+{
+ let prog = gl.createProgram();
+ glErrorReport(arguments);
+ let new_len = glPrograms.push(prog);
+ return new_len;
+}
+
+function glAttachShader(program, shader)
+{
+ let s = gl_get_shader(shader);
+ let p = gl_get_program(program);
+ const r = gl.attachShader(p, s);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glLinkProgram(program)
+{
+ let p = gl_get_program(program);
+ gl.linkProgram(p);
+ if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
+ var info = gl.getProgramInfoLog(p);
+ console.error("Failed to compile WebGL program. \n\n"+info);
+ }
+}
+
+function glUseProgram(program)
+{
+ let p = gl_get_program(program);
+ const r = gl.useProgram(p);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glGetAttribLocation(program, name, name_len)
+{
+ let str = wasm_read_string(lumenarium_wasm_instance, name, name_len);
+ const r = gl.getAttribLocation(gl_get_program(program), str);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glVertexAttribPointer(attr, size, type, normalized, stride, pointer)
+{
+ const r = gl.vertexAttribPointer(attr, size, type, normalized, stride, pointer);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glEnableVertexAttribArray(index)
+{
+ const r = gl.enableVertexAttribArray(index);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glDrawElements(type, index_count, ele_type, indices)
+{
+ const r = gl.drawElements(type, index_count, ele_type, indices);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glGenTextures(count, ids_ptr, ids_size)
+{
+ for (let i = 0; i < count; i++)
+ {
+ const tex = gl.createTexture();
+ glErrorReport(arguments);
+ let new_len = glTextures.push(tex);
+ put_u32(ids_ptr + (i * 4), new_len);
+ }
+}
+
+function glBindTexture(slot, id)
+{
+ let tex = gl_get_texture(id);
+ const r = gl.bindTexture(slot, tex);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glTexParameteri(slot, param, value)
+{
+ const r = gl.texParameteri(slot, param, value);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glTexImage2D(target, level, internalformat, width, height, border, format, type, data_ptr, data_size)
+{
+ const data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, data_ptr, data_size);
+ const r = gl.texImage2D(target, level, internalformat, width, height, border, format, type, data);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glTexSubImage2D(target, level, offsetx, offsety, width, height, format, type, data_ptr, data_size)
+{
+ const data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, data_ptr, data_size);
+ const r = gl.texSubImage2D(target, level, offsetx, offsety, width, height, format, type, data);
+ glErrorReport(arguments);
+ return r;
+}
+
+function glGetUniformLocation(program, name, name_len)
+{
+ // TODO(PS): complete
+ return 0;
+}
+
+function glUniformMatrix4fv()
+{
+ // TODO(PS):
+}
+
+function webgl_add_imports (canvas_selector, imports) {
+ const canvas = document.querySelector(canvas_selector);
+ if (!canvas) return console.error("no canvas");
+
+ gl = canvas.getContext("webgl2");
+ if (gl === null) return console.error("no webgl ctx");
+
+ imports.glHadError = () => { return gl_error; };
+ imports.glClearColor = glClearColor;
+ imports.glEnable = glEnable;
+ imports.glDisable = glDisable;
+ imports.glBlendFunc = glBlendFunc;
+ imports.glViewport = glViewport;
+ imports.glDepthFunc = glDepthFunc;
+ imports.glClear = glClear;
+
+ imports.glCreateBuffer = glCreateBuffer;
+ imports.glBindBuffer = glBindBuffer;
+ imports.glBufferData = glBufferData;
+ imports.glBufferSubData = glBufferSubData;
+ imports.glCreateShader = glCreateShader;
+ imports.glShaderSource = glShaderSource;
+ imports.glCompileShader = glCompileShader;
+ imports.glCreateProgram = glCreateProgram;
+ imports.glAttachShader = glAttachShader;
+ imports.glLinkProgram = glLinkProgram;
+ imports.glUseProgram = glUseProgram;
+ imports.glGetAttribLocation = glGetAttribLocation;
+ imports.glVertexAttribPointer = glVertexAttribPointer;
+ imports.glEnableVertexAttribArray = glEnableVertexAttribArray;
+ imports.glDrawElements = glDrawElements;
+ imports.glGenTextures = glGenTextures;
+ imports.glBindTexture = glBindTexture;
+ imports.glTexParameteri = glTexParameteri;
+ imports.glTexImage2D = glTexImage2D;
+ imports.glTexSubImage2D = glTexSubImage2D;
+ imports.glBindTexture = glBindTexture;
+ imports.glGetUniformLocation = glGetUniformLocation;
+ imports.glUniformMatrix4fv = glUniformMatrix4fv;
+
+ return imports;
+}
\ No newline at end of file
diff --git a/run_tree/wasm/debug/text.txt b/run_tree/wasm/debug/text.txt
new file mode 100644
index 0000000..ccfbc29
Binary files /dev/null and b/run_tree/wasm/debug/text.txt differ
diff --git a/run_tree/webgl/debug/lumenarium_wasm_imports.js b/run_tree/webgl/debug/lumenarium_wasm_imports.js
new file mode 100644
index 0000000..b7808c3
--- /dev/null
+++ b/run_tree/webgl/debug/lumenarium_wasm_imports.js
@@ -0,0 +1,167 @@
+var lumenarium_wasm_module = null;
+var lumenarium_wasm_instance = null;
+
+var WASM_PAGE_SIZE = 65536;
+
+function wasm_mem_get_u8_arr(inst, ptr, size)
+{
+ let view = new Uint8Array(inst.exports.memory.buffer, ptr, size);
+ return view;
+}
+
+function wasm_read_string(inst, ptr, len)
+{
+ let view = wasm_mem_get_u8_arr(inst, ptr, len);
+ let string = '';
+ for (let i = 0; i < len; i++)
+ {
+ string += String.fromCharCode(view[i]);
+ }
+ return string;
+}
+
+function wasm_write_bytes(inst, src, ptr, len)
+{
+ let view = wasm_mem_get_u8_arr(inst, ptr, len);
+ for (let i = 0; i < len; i++) view[i] = src[i];
+}
+
+function wasm_get_proc(inst, proc_ptr)
+{
+ let result = inst.exports.__indirect_function_table.get(proc_ptr);
+ return result;
+}
+
+function fract (v) { return v % 1; }
+
+var lumenarium_wasm_imports = {
+
+ memset: (dst, size, value) => {
+ let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size);
+ for (let i = 0; i < size; i++)
+ {
+ view_dst[i] = value;
+ }
+ },
+
+ memcpy: (dst, src, size) => {
+ let view_dst = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dst, size);
+ let view_src = wasm_mem_get_u8_arr(lumenarium_wasm_instance, src, size);
+ for (let i = 0; i < size; i++)
+ {
+ view_dst[i] = view_src[i];
+ }
+ },
+
+ wasm_assert_always: () => {
+ console.assert(false);
+ },
+
+ wasm_get_memory_size: () => {
+ return instance.exports.memory.buffer.byteLength;
+ },
+
+ wasm_mem_grow: (new_size) => {
+ let pages = new_size / WASM_PAGE_SIZE;
+ let pages_rem = fract(pages);
+ if (pages_rem > 0) pages = Math.floor(pages) + 1;
+ let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength;
+ let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages);
+
+ console.log("mem_grow\n",
+ "req size: ", new_size, "\n",
+ "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n",
+ "old size: ", size_before, "\n",
+ "grew by: ", (pages * WASM_PAGE_SIZE), "\n",
+ "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, "");
+ },
+
+ wasm_performance_now: () => {
+ return performance.now();
+ },
+
+ wasm_sleep: (milliseconds) => {
+ let start = Date.now();
+ for (let at = Date.now(); (at - start) < milliseconds; at = Date.now()) {}
+ },
+
+ wasm_fetch: async (file_path, file_path_len, dest, dest_size) => {
+ let path = wasm_read_string(lumenarium_wasm_instance, file_path, file_path_len);
+ fetch(path)
+ .then(async (res) => {
+ // TODO(PS): success checking
+ let reader = res.body.getReader();
+ let read_res = { done: false };
+
+ let view = wasm_mem_get_u8_arr(lumenarium_wasm_instance, dest, dest_size);
+ let last_write = 0;
+ while (!read_res.done)
+ {
+ read_res = await reader.read();
+ if (read_res.done) break;
+
+ let len = read_res.value.length;
+ let write_end = last_write + len;
+ for (let i = last_write; i < write_end; i++)
+ {
+ view[i] = read_res.value[i - last_write];
+ }
+ last_write = write_end + 1;
+ }
+ });
+ return 0;
+ },
+
+ wasm_request_animation_frame: (cb) => {
+ let cb_proc = wasm_get_proc(lumenarium_wasm_instance, cb);
+ window.requestAnimationFrame(cb_proc);
+ },
+
+ print: (str_base, len) => {
+ let string = wasm_read_string(lumenarium_wasm_instance, str_base, len);
+ console.log(string);
+ },
+};
+
+let gl = null;
+
+function glClearColor (r, g, b, a) { gl.clearColor(r,g,b,a); }
+function glEnable(v) { gl.enable(v); }
+function glDisable(v) { gl.disable(v); }
+function glBlendFunc(a,b) { gl.blendFunc(a,b); }
+function glViewport(xmin, ymin, xmax, ymax) { gl.viewport(xmin,ymin,xmax,ymax); }
+function glDepthFunc(v) { gl.depthFunc(v); }
+function glClear(mask) { gl.clear(mask); }
+
+function webgl_add_imports (canvas_selector, imports) {
+ const canvas = document.querySelector(canvas_selector);
+ if (!canvas) return console.error("no canvas");
+
+ gl = canvas.getContext("webgl");
+ if (gl === null) return console.error("no webgl ctx");
+
+ ///////////////////////////////////////
+ // Constants
+
+ imports.GL_TEXTURE_2D = gl.TEXTURE_2D;
+ imports.GL_BLEND = gl.BLEND;
+ imports.GL_SRC_ALPHA = gl.SRC_ALPHA;
+ imports.GL_ONE_MINUS_SRC_ALPHA = gl.ONE_MINUS_SRC_ALPHA;
+ imports.GL_DEPTH_TEST = gl.DEPTH_TEST;
+ imports.GL_LESS = gl.LESS;
+ imports.GL_COLOR_BUFFER_BIT = gl.COLOR_BUFFER_BIT;
+ imports.GL_DEPTH_BUFFER_BIT = gl.DEPTH_BUFFER_BIT;
+
+ ///////////////////////////////////////
+ // Functions
+
+ imports.glClearColor = glClearColor;
+ imports.glEnable = glEnable;
+ imports.glDisable = glDisable;
+ imports.glBlendFunc = glBlendFunc;
+ imports.glViewport = glViewport;
+ imports.glDepthFunc = glDepthFunc;
+ imports.glClear = glClear;
+
+ return imports;
+}
\ No newline at end of file
diff --git a/run_tree/win32/intel/debug/debug.rdbg b/run_tree/win32/intel/debug/debug.rdbg
new file mode 100644
index 0000000..715158d
Binary files /dev/null and b/run_tree/win32/intel/debug/debug.rdbg differ
diff --git a/run_tree/win32/intel/debug/err.txt b/run_tree/win32/intel/debug/err.txt
new file mode 100644
index 0000000..9002a22
--- /dev/null
+++ b/run_tree/win32/intel/debug/err.txt
@@ -0,0 +1 @@
+OpenGL Version: 3.3.0 - Build 27.20.100.9778
diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj
new file mode 100644
index 0000000..0420798
Binary files /dev/null and b/run_tree/win32/intel/debug/lumenarium_first_win32.obj differ
diff --git a/run_tree/win32/intel/debug/test.obj b/run_tree/win32/intel/debug/test.obj
new file mode 100644
index 0000000..6497809
Binary files /dev/null and b/run_tree/win32/intel/debug/test.obj differ
diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp
deleted file mode 100644
index 323b8ae..0000000
--- a/src/app/blumen_lumen.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-//
-// File: blumen_lumen.cpp
-// Author: Peter Slattery
-// Creation Date: 2021-01-23
-//
-#ifndef BLUMEN_LUMEN_CPP
-
-internal void
-BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData)
-{
- mic_listen_job_data* Data = (mic_listen_job_data*)UserData;
-
- gs_data Msg = {};
-
- u8 WeathermanIPAddr[4] = {};
- WeathermanIPAddr[0] = 127;
- WeathermanIPAddr[1] = 0;
- WeathermanIPAddr[2] = 0;
- WeathermanIPAddr[3] = 1;
-
- u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr);
- u32 WeathermanPort = 20185;
-
- while (*Data->Running)
- {
-#if 1
- if (SocketQueryStatus(Data->SocketManager, Data->ListenSocket))
- {
- // TODO(pjs): Removing this block for now - nothing is wrong with it except that SocketPeek is still blocking for some reason
- if (SocketPeek(Data->SocketManager, Data->ListenSocket))
- {
- // TODO(pjs): Make this a peek operation
- Msg = SocketRecieve(Data->SocketManager, Data->ListenSocket, Ctx->Transient);
- if (Msg.Size > 0)
- {
- Data->MicPacketBuffer->Values[Data->MicPacketBuffer->WriteHead++] = Msg;
- if (Data->MicPacketBuffer->WriteHead >= PACKETS_MAX)
- {
- Data->MicPacketBuffer->WriteHead = 0;
- }
- }
- }
-#endif
-
- while (Data->OutgoingMsgQueue->ReadHead != Data->OutgoingMsgQueue->WriteHead)
- {
- u32 ReadIndex = Data->OutgoingMsgQueue->ReadHead++;
- if (Data->OutgoingMsgQueue->ReadHead >= BLUMEN_MESSAGE_QUEUE_COUNT)
- {
- Data->OutgoingMsgQueue->ReadHead = 0;
- }
-
- Msg = Data->OutgoingMsgQueue->Buffers[ReadIndex];
- u32 Address = WeathermanIPV4;
- u32 Port = WeathermanPort;
- s32 Flags = 0;
- SocketSend(Data->SocketManager, Data->ListenSocket, Address, Port, Msg, Flags);
- }
- }
- }
-
- CloseSocket(Data->SocketManager, Data->ListenSocket);
-}
-
-internal void
-BlumenLumen_LoadPatterns(app_state* State)
-{
- animation_pattern_array* Patterns = &State->Patterns;
- if (Patterns->CountMax == 0)
- {
- *Patterns = Patterns_Create(&State->Permanent, 32);
- }
-
- Patterns->Count = 0;
- Patterns_PushPattern(Patterns, TestPatternOne);
- Patterns_PushPattern(Patterns, TestPatternTwo);
- Patterns_PushPattern(Patterns, TestPatternThree);
- Patterns_PushPattern(Patterns, Pattern_AllGreen);
- Patterns_PushPattern(Patterns, Pattern_HueShift);
- Patterns_PushPattern(Patterns, Pattern_HueFade);
- Patterns_PushPattern(Patterns, Pattern_Spots);
- Patterns_PushPattern(Patterns, Pattern_LighthouseRainbow);
- Patterns_PushPattern(Patterns, Pattern_SmoothGrowRainbow);
- Patterns_PushPattern(Patterns, Pattern_GrowAndFade);
- Patterns_PushPattern(Patterns, Pattern_ColorToWhite);
- Patterns_PushPattern(Patterns, Pattern_Blue);
- Patterns_PushPattern(Patterns, Pattern_Green);
- Patterns_PushPattern(Patterns, Pattern_FlowerColors);
- Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite);
- Patterns_PushPattern(Patterns, Pattern_BasicFlowers);
-}
-
-internal pixel
-TEMP_Saturate(pixel P)
-{
- v4 CRGB = v4{ (r32)P.R / 255.f, (r32)P.G / 255.f, (r32)P.B / 255.f, 1.f };
- v4 CHSV = RGBToHSV(CRGB);
- if (CHSV.g > .3f)
- {
- CHSV.g = 1;
- CRGB = HSVToRGB(CHSV);
- }
- return V4ToRGBPixel(CRGB);
-}
-
-internal gs_data
-BlumenLumen_CustomInit(app_state* State, context Context)
-{
- // This is memory for any custom data that we want to use
- // as a part of a particular sculpture.
- // By returning it from here, it will be sent as an argument to
- // the sculpture's CustomUpdate function;
- gs_data Result = {};
-
- Result = PushSizeToData(&State->Permanent, sizeof(blumen_lumen_state));
-
- blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory;
- BLState->Running = true;
-
- BLState->MicListenJobData.Running = &BLState->Running;
- BLState->MicListenJobData.SocketManager = Context.SocketManager;
- BLState->MicListenJobData.MicPacketBuffer = &BLState->MicPacketBuffer;
- BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue;
- BLState->MicListenJobData.ListenSocket = CreateSocket(Context.SocketManager, "127.0.0.1", "20185");
-
- //BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData);
-
- gs_const_string SculpturePath = ConstString("data/test_blumen.fold");
- LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog);
-
- { // Animation PLAYGROUND
- animation Anim0 = {0};
- Anim0.Name = PushStringF(&State->Permanent, 256, "test_anim_zero");
- Anim0.Layers = AnimLayerArray_Create(State->AnimationSystem.Storage, 8);
- Anim0.Blocks_ = AnimBlockArray_Create(State->AnimationSystem.Storage, 8);
- Anim0.PlayableRange.Min = 0;
- Anim0.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem);
- Animation_AddLayer(&Anim0, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem);
-
- Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 0);
-
- AnimationArray_Push(&State->AnimationSystem.Animations, Anim0);
-
- animation Anim1 = {0};
- Anim1.Name = PushStringF(&State->Permanent, 256, "test_anim_one");
- Anim1.Layers = AnimLayerArray_Create(State->AnimationSystem.Storage, 8);
- Anim1.Blocks_ = AnimBlockArray_Create(State->AnimationSystem.Storage, 8);
- Anim1.PlayableRange.Min = 0;
- Anim1.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem);
- Animation_AddLayer(&Anim1, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem);
-
- Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0);
-
- AnimationArray_Push(&State->AnimationSystem.Animations, Anim1);
-
- animation Anim2 = {0};
- Anim2.Name = PushStringF(&State->Permanent, 256, "i_love_you");
- Anim2.Layers = AnimLayerArray_Create(State->AnimationSystem.Storage, 8);
- Anim2.Blocks_ = AnimBlockArray_Create(State->AnimationSystem.Storage, 8);
- Anim2.PlayableRange.Min = 0;
- Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem);
- Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem);
-
- Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(10), 0);
-
- AnimationArray_Push(&State->AnimationSystem.Animations, Anim2);
-
- State->AnimationSystem.TimelineShouldAdvance = true;
- } // End Animation Playground
-
- for (u32 i = 0; i < FLOWER_COLORS_COUNT; i++)
- {
- FlowerAColors[i] = TEMP_Saturate(FlowerAColors[i]);
- FlowerBColors[i] = TEMP_Saturate(FlowerBColors[i]);
- FlowerCColors[i] = TEMP_Saturate(FlowerCColors[i]);
- }
-
- return Result;
-}
-
-internal void
-BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context)
-{
- blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory;
-
- MotorTimeElapsed += Context->DeltaTime;
-
- gs_string BlueString = MakeString("blue");
- gs_string GreenString = MakeString("green");
- gs_string ILoveYouString = MakeString("i_love_you");
-
- while (BLState->MicPacketBuffer.ReadHead != BLState->MicPacketBuffer.WriteHead)
- {
- gs_data PacketData = BLState->MicPacketBuffer.Values[BLState->MicPacketBuffer.ReadHead++];
- microphone_packet Packet = *(microphone_packet*)PacketData.Memory;
-
- u32 NameLen = CStringLength(Packet.AnimationFileName);
- if (StringEqualsCharArray(BlueString.ConstString, Packet.AnimationFileName, NameLen))
- {
- State->AnimationSystem.ActiveAnimationIndex = 0;
- }
- else if (StringEqualsCharArray(GreenString.ConstString, Packet.AnimationFileName, NameLen))
- {
- State->AnimationSystem.ActiveAnimationIndex = 1;
- }
- else if (StringEqualsCharArray(ILoveYouString.ConstString, Packet.AnimationFileName, NameLen))
- {
- State->AnimationSystem.ActiveAnimationIndex = 2;
- }
-
- if (BLState->MicPacketBuffer.ReadHead >= PACKETS_MAX)
- {
- BLState->MicPacketBuffer.ReadHead = 0;
- }
- }
-
- if (MotorTimeElapsed > 60)
- {
- // NOTE(pjs):
- MotorTimeElapsed = 0;
- u8 Position = LastPosition;
- if (LastPosition == 2)
- {
- LastPosition = 1;
- }
- else
- {
- LastPosition = 2;
- }
-
- if ((BLState->OutgoingMsgQueue.WriteHead >= BLState->OutgoingMsgQueue.ReadHead) ||
- (BLState->OutgoingMsgQueue.WriteHead < BLState->OutgoingMsgQueue.ReadHead))
- {
- u32 WriteIndex = BLState->OutgoingMsgQueue.WriteHead;
-
- gs_data* Msg = BLState->OutgoingMsgQueue.Buffers + WriteIndex;
- if (Msg->Size == 0)
- {
- *Msg = PushSizeToData(&State->Permanent, sizeof(motor_packet));
- }
- motor_packet* Packet = (motor_packet*)Msg->Memory;
- Packet->FlowerPositions[0] = Position;
- Packet->FlowerPositions[1] = Position;
- Packet->FlowerPositions[2] = Position;
-
- // NOTE(pjs): We increment the write head AFTER we've written so that
- // the network thread doesn't think the buffer is ready to send before
- // the data is set. We want to avoid the case of:
- // 1. Main Thread increments write head to 1
- // 2. Network Thread thinks theres a new message to send at 0
- // 3. Network Thread sends the message at 0
- // 4. Main Thread sets the message at 0
- BLState->OutgoingMsgQueue.WriteHead += 1;
- if (BLState->OutgoingMsgQueue.WriteHead >= BLUMEN_MESSAGE_QUEUE_COUNT)
- {
- BLState->OutgoingMsgQueue.WriteHead = 0;
- }
- }
- }
-}
-
-US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup)
-{
- blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory;
- BLState->Running = false;
-}
-
-internal user_space_desc
-BlumenLumen_UserSpaceCreate()
-{
- user_space_desc Result = {};
- Result.LoadPatterns = BlumenLumen_LoadPatterns;
- Result.CustomInit = BlumenLumen_CustomInit;
- Result.CustomUpdate = BlumenLumen_CustomUpdate;
- Result.CustomCleanup = BlumenLumen_CustomCleanup;
- return Result;
-}
-
-#define BLUMEN_LUMEN_CPP
-#endif // BLUMEN_LUMEN_CPP
\ No newline at end of file
diff --git a/src/app/blumen_lumen.h b/src/app/blumen_lumen.h
deleted file mode 100644
index 0ea9db5..0000000
--- a/src/app/blumen_lumen.h
+++ /dev/null
@@ -1,119 +0,0 @@
-//
-// File: blumen_lumen.h
-// Author: Peter Slattery
-// Creation Date: 2021-01-15
-//
-#ifndef BLUMEN_LUMEN_H
-
-typedef struct motor_packet
-{
- u8 FlowerPositions[3];
-} motor_packet;
-
-#pragma pack(push, 1)
-struct microphone_packet
-{
- b8 ChangeAnimation;
- char AnimationFileName[32];
- b8 SetLayer;
- char LayerName[32];
- r32 LayerOpacity;
- b8 SetLayerParamColor;
- char LayerParamColor[7];
- r32 OverrideDuration;
-};
-#pragma pack(pop)
-
-#define BLUMEN_MESSAGE_QUEUE_COUNT 32
-typedef struct blumen_network_msg_queue
-{
- gs_data Buffers[BLUMEN_MESSAGE_QUEUE_COUNT];
- u32 WriteHead;
- u32 ReadHead;
-} blumen_network_msg_queue;
-
-// TODO(pjs): Refactor this -> blumen_network_job_state
-struct mic_listen_job_data
-{
- bool* Running;
-
- platform_socket_manager* SocketManager;
- packet_ringbuffer* MicPacketBuffer;
- platform_socket_handle_ ListenSocket;
-
- blumen_network_msg_queue* OutgoingMsgQueue;
-};
-
-struct blumen_lumen_state
-{
- bool Running;
-
- packet_ringbuffer MicPacketBuffer;
- blumen_network_msg_queue OutgoingMsgQueue;
-
- temp_job_req JobReq;
-
-
- platform_thread_handle MicListenThread;
- mic_listen_job_data MicListenJobData;
-};
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-// If you change anything, exit lumenarium if its running
-// then in this application hit f1 to compile then
-// go to remedybg (the debugger) and hit f5
-
-
-// don't touch this
-u8 LastPosition = 1;
-
-u8 ClosedValue = 1;
-u8 OpenValue = 2;
-
-
-r64 MotorTimeElapsed = 0;
-r64 OpenClosePeriod = 15.0f;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#define BLUMEN_LUMEN_H
-#endif // BLUMEN_LUMEN_H
\ No newline at end of file
diff --git a/src/app/editor/foldhaus_editor.cpp b/src/app/editor/foldhaus_editor.cpp
index 6d71ca3..8db76a9 100644
--- a/src/app/editor/foldhaus_editor.cpp
+++ b/src/app/editor/foldhaus_editor.cpp
@@ -8,149 +8,147 @@
internal void
Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue, mouse_state Mouse, context Context)
{
- DEBUG_TRACK_FUNCTION;
+ DEBUG_TRACK_FUNCTION;
+
+ b32 MouseInputHandled = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State);
+
+ gs_string TextInputString = PushString(State->Transient, 32);
+
+ panel* ActivePanel = PanelSystem_GetPanelContainingPoint(&State->PanelSystem, Mouse.Pos);
+ if (ActivePanel)
+ {
+ panel_definition ActiveDef = State->PanelSystem.PanelDefs[ActivePanel->TypeIndex];
- b32 MouseInputHandled = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State);
-
- gs_string TextInputString = PushString(State->Transient, 32);
-
- panel* ActivePanel = PanelSystem_GetPanelContainingPoint(&State->PanelSystem, Mouse.Pos);
- if (ActivePanel)
+ input_command_registry ActiveCommands = {};
+ if (State->Modes.ActiveModesCount > 0)
{
- panel_definition ActiveDef = State->PanelSystem.PanelDefs[ActivePanel->TypeIndex];
-
- input_command_registry ActiveCommands = {};
- if (State->Modes.ActiveModesCount > 0)
- {
- ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands;
- }
- else if (ActiveDef.InputCommands)
- {
- ActiveCommands.Commands = ActiveDef.InputCommands;
- ActiveCommands.Size = sizeof(*ActiveDef.InputCommands) / sizeof(ActiveDef.InputCommands[0]);
- ActiveCommands.Used = ActiveCommands.Size;
- }
-
- // Fill up the command queue
- for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++)
- {
- input_entry Event = InputQueue.Entries[EventIdx];
-
- bool IsMouseEvent = (Event.Key == KeyCode_MouseLeftButton ||
- Event.Key == KeyCode_MouseMiddleButton ||
- Event.Key == KeyCode_MouseRightButton);
- if (IsMouseEvent && MouseInputHandled)
- {
- continue;
- }
-
- // NOTE(Peter): These are in the order Down, Up, Held because we want to privalege
- // Down and Up over Held. In other words, we don't want to call a Held command on the
- // frame when the button was released, even if the command is registered to both events
- if (KeyTransitionedDown(Event))
- {
- if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue))
- {
- char KeyASCII = KeyCodeToChar(Event.Key);
- if (KeyASCII)
- {
- OutChar(&TextInputString, KeyASCII);
- }
- }
- }
- else if (KeyTransitionedUp(Event))
- {
- FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue);
- }
- else if (KeyHeldDown(Event))
- {
- if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue))
- {
- char KeyASCII = KeyCodeToChar(Event.Key);
- if (KeyASCII)
- {
- OutChar(&TextInputString, KeyASCII);
- }
- }
- }
- }
+ ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands;
+ }
+ else if (ActiveDef.InputCommands)
+ {
+ ActiveCommands.Commands = ActiveDef.InputCommands;
+ ActiveCommands.Size = sizeof(*ActiveDef.InputCommands) / sizeof(ActiveDef.InputCommands[0]);
+ ActiveCommands.Used = ActiveCommands.Size;
}
- // Execute all commands in CommandQueue
- for (s32 CommandIdx = State->CommandQueue.Used - 1; CommandIdx >= 0; CommandIdx--)
+ // Fill up the command queue
+ for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++)
{
- command_queue_entry* Entry = &State->CommandQueue.Commands[CommandIdx];
- if (Entry->Command.Proc)
+ input_entry Event = InputQueue.Entries[EventIdx];
+
+ bool IsMouseEvent = (Event.Key == KeyCode_MouseLeftButton ||
+ Event.Key == KeyCode_MouseMiddleButton ||
+ Event.Key == KeyCode_MouseRightButton);
+ if (IsMouseEvent && MouseInputHandled)
+ {
+ continue;
+ }
+
+ // NOTE(Peter): These are in the order Down, Up, Held because we want to privalege
+ // Down and Up over Held. In other words, we don't want to call a Held command on the
+ // frame when the button was released, even if the command is registered to both events
+ if (KeyTransitionedDown(Event))
+ {
+ if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue))
{
- Entry->Command.Proc(State, Entry->Event, Mouse, Context, ActivePanel);
+ char KeyASCII = KeyCodeToChar(Event.Key);
+ if (KeyASCII)
+ {
+ OutChar(&TextInputString, KeyASCII);
+ }
}
- else
+ }
+ else if (KeyTransitionedUp(Event))
+ {
+ FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue);
+ }
+ else if (KeyHeldDown(Event))
+ {
+ if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue))
{
- EndCurrentOperationMode(State);
+ char KeyASCII = KeyCodeToChar(Event.Key);
+ if (KeyASCII)
+ {
+ OutChar(&TextInputString, KeyASCII);
+ }
}
+ }
}
-
- State->Interface.TempInputString = TextInputString.ConstString;
-
- ClearCommandQueue(&State->CommandQueue);
+ }
+
+ // Execute all commands in CommandQueue
+ for (s32 CommandIdx = State->CommandQueue.Used - 1; CommandIdx >= 0; CommandIdx--)
+ {
+ command_queue_entry* Entry = &State->CommandQueue.Commands[CommandIdx];
+ if (Entry->Command.Proc)
+ {
+ Entry->Command.Proc(State, Entry->Event, Mouse, Context, ActivePanel);
+ }
+ else
+ {
+ EndCurrentOperationMode(State);
+ }
+ }
+
+ State->Interface.TempInputString = TextInputString.ConstString;
+
+ ClearCommandQueue(&State->CommandQueue);
}
internal void
Editor_Update(app_state* State, context* Context, input_queue InputQueue)
{
- Context->Mouse.CursorType = CursorType_Arrow;
- State->WindowBounds = Context->WindowBounds;
- State->Interface.Mouse = Context->Mouse;
-
- State->Interface.HotWidgetFramesSinceUpdate += 1;
- if (State->Interface.HotWidgetFramesSinceUpdate > 1)
- {
- State->Interface.HotWidget = {};
- }
-
- Assert(State->Interface.PerFrameMemory &&
- (u64)State->Interface.PerFrameMemory != 0x5);
-
- PanelSystem_UpdateLayout(&State->PanelSystem, State->WindowBounds);
- Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
+ Context->Mouse.CursorType = CursorType_Arrow;
+ State->WindowBounds = Context->WindowBounds;
+ State->Interface.Mouse = Context->Mouse;
+
+ State->Interface.HotWidgetFramesSinceUpdate += 1;
+ if (State->Interface.HotWidgetFramesSinceUpdate > 1)
+ {
+ State->Interface.HotWidget = {};
+ }
+
+ Assert(State->Interface.PerFrameMemory &&
+ (u64)State->Interface.PerFrameMemory != 0x5);
+
+ PanelSystem_UpdateLayout(&State->PanelSystem, State->WindowBounds);
+ Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
}
internal void
Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer)
{
- State->Interface.WindowBounds = Context->WindowBounds;
- PushRenderOrthographic(RenderBuffer, State->WindowBounds);
- PushRenderClearScreen(RenderBuffer);
+ State->Interface.WindowBounds = Context->WindowBounds;
+ PushRenderOrthographic(RenderBuffer, State->WindowBounds);
+ PushRenderClearScreen(RenderBuffer);
+
+
+ ui_InterfaceReset(&State->Interface);
+ State->Interface.RenderBuffer = RenderBuffer;
+ ui_PushLayout(&State->Interface, Context->WindowBounds, LayoutDirection_TopDown, MakeString("Editor Layout"));
+ {
+ DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context);
-
- ui_InterfaceReset(&State->Interface);
- State->Interface.RenderBuffer = RenderBuffer;
- ui_PushLayout(&State->Interface, Context->WindowBounds, LayoutDirection_TopDown, MakeString("Editor Layout"));
+ for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
{
- DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context);
-
- for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
- {
- operation_mode OperationMode = State->Modes.ActiveModes[m];
- if (OperationMode.Render != 0)
- {
- OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context);
- }
- }
+ operation_mode OperationMode = State->Modes.ActiveModes[m];
+ if (OperationMode.Render != 0)
+ {
+ OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context);
+ }
}
- ui_PopLayout(&State->Interface, MakeString("Editor Layout"));
-
-
- // Draw the Interface
- if (State->Interface.DrawOrderRoot != 0)
- {
- ui_widget Widget = *State->Interface.DrawOrderRoot;
- Editor_DrawWidget(State, Context, RenderBuffer, Widget, Context->WindowBounds);
- }
-
- Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext);
- ResetWorkQueue(Context->GeneralWorkQueue);
-
+ }
+ ui_PopLayout(&State->Interface, MakeString("Editor Layout"));
+
+
+ // Draw the Interface
+ if (State->Interface.DrawOrderRoot != 0)
+ {
+ ui_widget* Widget = State->Interface.DrawOrderRoot;
+ Editor_DrawWidgetList(State, Context, RenderBuffer, Widget, Context->WindowBounds);
+ }
+
+ Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext);
}
diff --git a/src/app/editor/foldhaus_editor_draw.h b/src/app/editor/foldhaus_editor_draw.h
index 00c7949..bad44a2 100644
--- a/src/app/editor/foldhaus_editor_draw.h
+++ b/src/app/editor/foldhaus_editor_draw.h
@@ -6,12 +6,12 @@
#ifndef FOLDHAUS_EDITOR_DRAW_H
internal void
-Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color)
+Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color, s32 CursorPosition)
{
gs_string Temp = PushString(State->Transient, 256);
PrintF(&Temp, "%d", Widget.Id.Id);
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer,
- Widget.String.Length,
+ Widget.String.Length + 1,
State->Interface.Style.Font->BitmapMemory,
State->Interface.Style.Font->BitmapTextureHandle,
State->Interface.Style.Font->BitmapWidth,
@@ -25,7 +25,8 @@ Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffe
{
case Align_Left:
{
- RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color);
+ RegisterPosition = DrawStringLeftAligned(RenderBuffer,
+ &BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color, CursorPosition, GreenV4);
}break;
case Align_Right:
@@ -76,12 +77,13 @@ Editor_GetWidgetFillBounds(ui_widget Widget)
return Result;
}
+internal void Editor_DrawWidgetList(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ParentClipBounds);
+
internal void
-Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ParentClipBounds)
+Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 WidgetParentUnion)
{
- rect2 WidgetParentUnion = Widget.Bounds;
- WidgetParentUnion = Rect2Union(Widget.Bounds, ParentClipBounds);
-
+ bool IsActiveWidget = ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget);
+ ;
if (!Widget.Parent || (Rect2Area(WidgetParentUnion) > 0))
{
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground))
@@ -101,7 +103,13 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0)
{
v4 Color = State->Interface.Style.TextColor;
- Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color);
+ s32 CursorPosition = -1;
+ if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_Typable) && IsActiveWidget)
+ {
+ CursorPosition = State->Interface.CursorPosition;
+ }
+
+ Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color, CursorPosition);
}
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) ||
@@ -120,13 +128,15 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0)
{
- // TODO(pjs): add this color to the style
v4 TextColor = BlackV4;
- Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor);
+ Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor, -1);
}
}
- if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline))
+ bool DrawOutline = ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline);
+ DrawOutline |= ui_WidgetIsFlagSet(Widget, UIWidgetFlag_Typable) && IsActiveWidget;
+
+ if (DrawOutline)
{
// TODO(pjs): replace these with values from the style
r32 Thickness = 1.0f;
@@ -134,18 +144,27 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren
PushRenderBoundingBox2D(RenderBuffer, WidgetParentUnion.Min, WidgetParentUnion.Max, Thickness, Color);
}
}
-
- if (Widget.ChildrenRoot)
- {
- Editor_DrawWidget(State, Context, RenderBuffer, *Widget.ChildrenRoot, WidgetParentUnion);
- }
-
- if (Widget.Next)
- {
- Editor_DrawWidget(State, Context, RenderBuffer, *Widget.Next, ParentClipBounds);
- }
}
+internal void Editor_DrawWidgetList(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget* Widget, rect2 ParentClipBounds)
+{
+ ui_widget* WidgetAt = Widget;
+ while (WidgetAt)
+ {
+ rect2 WidgetParentUnion = WidgetAt->Bounds;
+ WidgetParentUnion = Rect2Union(WidgetAt->Bounds, ParentClipBounds);
+
+ Editor_DrawWidget(State, Context, RenderBuffer, *WidgetAt, WidgetParentUnion);
+
+ if (WidgetAt->ChildrenRoot)
+ {
+ Editor_DrawWidgetList(State, Context, RenderBuffer, WidgetAt->ChildrenRoot, WidgetParentUnion);
+ }
+
+ WidgetAt = WidgetAt->Next;
+ }
+}
+
#define FOLDHAUS_EDITOR_DRAW_H
#endif // FOLDHAUS_EDITOR_DRAW_H
\ No newline at end of file
diff --git a/src/app/editor/foldhaus_operation_mode.h b/src/app/editor/foldhaus_operation_mode.h
index 81e12fe..375d342 100644
--- a/src/app/editor/foldhaus_operation_mode.h
+++ b/src/app/editor/foldhaus_operation_mode.h
@@ -14,78 +14,77 @@ typedef OPERATION_RENDER_PROC(operation_render_proc);
struct operation_mode
{
- input_command_registry Commands;
- operation_render_proc* Render;
- gs_memory_cursor Memory;
- u8* OpStateMemory;
+ input_command_registry Commands;
+ operation_render_proc* Render;
+ gs_memory_cursor Memory;
+ u8* OpStateMemory;
};
#define OPERATION_MODES_MAX 32
struct operation_mode_system
{
- s32 ActiveModesCount;
- operation_mode ActiveModes[OPERATION_MODES_MAX];
- //arena_snapshot ModeMemorySnapshots[OPERATION_MODES_MAX];
- gs_data_array ModeMemoryPagesFreeList;
-
- // NOTE(Peter): This acts as mode scoped memory. When a mode gets activated, it can allocate
- // temporary memory which then gets freed when the mode is deactivated
- gs_memory_arena Arena;
+ s32 ActiveModesCount;
+ operation_mode ActiveModes[OPERATION_MODES_MAX];
+ //arena_snapshot ModeMemorySnapshots[OPERATION_MODES_MAX];
+ gs_data_array ModeMemoryPagesFreeList;
+
+ // NOTE(Peter): This acts as mode scoped memory. When a mode gets activated, it can allocate
+ // temporary memory which then gets freed when the mode is deactivated
+ gs_memory_arena Arena;
};
internal operation_mode_system
OperationModeSystemInit(gs_memory_arena* Storage, gs_thread_context ThreadContext)
{
- operation_mode_system Result = {0};
- // TODO(Peter): Do we really need an arena? Can this just operate in constant memory footprint?
- Result.Arena.Allocator = ThreadContext.Allocator;
-
- Result.ModeMemoryPagesFreeList.CountMax = 32; // TODO(Peter): Static number of modes that can be active simultaneously
- Result.ModeMemoryPagesFreeList.Data = PushArray(Storage, gs_data, Result.ModeMemoryPagesFreeList.CountMax);
- for (u32 Page = 0; Page < Result.ModeMemoryPagesFreeList.CountMax; Page++)
- {
- // TODO(Peter): 4k pages = page size on windows
- Result.ModeMemoryPagesFreeList.Data[Page] = PushSizeToData(Storage, KB(4));
- }
- Result.ModeMemoryPagesFreeList.Count = Result.ModeMemoryPagesFreeList.CountMax;
-
- return Result;
+ operation_mode_system Result = {0};
+ // TODO(Peter): Do we really need an arena? Can this just operate in constant memory footprint?
+ Result.Arena.Allocator = ThreadContext.Allocator;
+
+ Result.ModeMemoryPagesFreeList.CountMax = 8;
+ Result.ModeMemoryPagesFreeList.Data = PushArray(Storage, gs_data, Result.ModeMemoryPagesFreeList.CountMax);
+ for (u32 Page = 0; Page < Result.ModeMemoryPagesFreeList.CountMax; Page++)
+ {
+ Result.ModeMemoryPagesFreeList.Data[Page] = PushSize(Storage, KB(4));
+ }
+ Result.ModeMemoryPagesFreeList.Count = Result.ModeMemoryPagesFreeList.CountMax;
+
+ return Result;
}
internal gs_data
OperationModeTakeMemoryPage(operation_mode_system* System)
{
- Assert(System->ModeMemoryPagesFreeList.Count > 0);
- gs_data Result = {0};
- System->ModeMemoryPagesFreeList.Count -= 1;
- u64 LastIndex = System->ModeMemoryPagesFreeList.Count;
- Result = System->ModeMemoryPagesFreeList.Data[LastIndex];
- return Result;
+ Assert(System->ModeMemoryPagesFreeList.Count > 0);
+ gs_data Result = {0};
+ System->ModeMemoryPagesFreeList.Count -= 1;
+ u64 LastIndex = System->ModeMemoryPagesFreeList.Count;
+ Result = System->ModeMemoryPagesFreeList.Data[LastIndex];
+ return Result;
}
internal void
OperationModeFreeMemoryPage(operation_mode_system* System, gs_data Data)
{
- Assert(System->ModeMemoryPagesFreeList.Count < System->ModeMemoryPagesFreeList.CountMax);
- u64 LastIndex = System->ModeMemoryPagesFreeList.Count;
- System->ModeMemoryPagesFreeList.Count += 1;
- System->ModeMemoryPagesFreeList.Data[LastIndex] = Data;
+ Assert(System->ModeMemoryPagesFreeList.Count < System->ModeMemoryPagesFreeList.CountMax);
+ u64 LastIndex = System->ModeMemoryPagesFreeList.Count;
+ System->ModeMemoryPagesFreeList.Count += 1;
+ System->ModeMemoryPagesFreeList.Data[LastIndex] = Data;
}
internal operation_mode*
ActivateOperationMode (operation_mode_system* System, operation_render_proc* RenderProc)
{
- Assert(System->ActiveModesCount < OPERATION_MODES_MAX);
- operation_mode* Result = 0;
- s32 ModeIndex = System->ActiveModesCount++;
-
- //System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(&System->Arena);
-
- Result = &System->ActiveModes[ModeIndex];
- Result->Memory = CreateMemoryCursor(OperationModeTakeMemoryPage(System));
- Result->Render = RenderProc;
-
- return Result;
+ Assert(System->ActiveModesCount < OPERATION_MODES_MAX);
+ operation_mode* Result = 0;
+ s32 ModeIndex = System->ActiveModesCount++;
+
+ //System->ModeMemorySnapshots[ModeIndex] = TakeSnapshotOfArena(&System->Arena);
+
+ Result = &System->ActiveModes[ModeIndex];
+ Result->Memory = MemoryCursorCreateFromData(OperationModeTakeMemoryPage(System));
+ Result->Render = RenderProc;
+
+ return Result;
}
#define ActivateOperationModeWithCommands(sys, cmds, render) \
@@ -94,30 +93,30 @@ ActivateOperationModeWithCommands_(sys, cmds, (s32)(sizeof(cmds) / sizeof(cmds[0
internal operation_mode*
ActivateOperationModeWithCommands_(operation_mode_system* System, input_command* Commands, s32 CommandsCount, operation_render_proc* RenderProc)
{
- operation_mode* NewMode = ActivateOperationMode(System, RenderProc);
-
+ operation_mode* NewMode = ActivateOperationMode(System, RenderProc);
+
#if 0
- InitializeInputCommandRegistry(&NewMode->Commands, CommandsCount, &System->Arena);
- for (s32 i = 0; i < CommandsCount; i++)
- {
- input_command Command = Commands[i];
- RegisterKeyPressCommand(&NewMode->Commands, Command.Key, Command.Flags, Command.Mdfr, Command.Proc);
- }
+ InitializeInputCommandRegistry(&NewMode->Commands, CommandsCount, &System->Arena);
+ for (s32 i = 0; i < CommandsCount; i++)
+ {
+ input_command Command = Commands[i];
+ RegisterKeyPressCommand(&NewMode->Commands, Command.Key, Command.Flags, Command.Mdfr, Command.Proc);
+ }
#else
- NewMode->Commands.Commands = Commands;
- NewMode->Commands.Size = CommandsCount;
- NewMode->Commands.Used = CommandsCount;
+ NewMode->Commands.Commands = Commands;
+ NewMode->Commands.Size = CommandsCount;
+ NewMode->Commands.Used = CommandsCount;
#endif
- return NewMode;
+ return NewMode;
}
internal void
DeactivateCurrentOperationMode (operation_mode_system* System)
{
- Assert(System->ActiveModesCount > 0);
- s32 ModeIndex = --System->ActiveModesCount;
- OperationModeFreeMemoryPage(System, System->ActiveModes[ModeIndex].Memory.Data);
- //ClearArenaToSnapshot(&System->Arena, System->ModeMemorySnapshots[ModeIndex]);
+ Assert(System->ActiveModesCount > 0);
+ s32 ModeIndex = --System->ActiveModesCount;
+ OperationModeFreeMemoryPage(System, System->ActiveModes[ModeIndex].Memory.Data);
+ //ClearArenaToSnapshot(&System->Arena, System->ModeMemorySnapshots[ModeIndex]);
}
#define CreateOperationState(mode, modeSystem, stateType) \
@@ -130,12 +129,12 @@ DeactivateCurrentOperationMode (operation_mode_system* System)
internal u8*
CreateOperationState_ (operation_mode* Mode, operation_mode_system* System, s32 StateSize)
{
- // NOTE(Peter): This isn't a problem if this fires, it just means our page size is too small,
- // and its time to make the pages dynamically sized
- Assert(Mode->Memory.Data.Size >= StateSize);
- u8* Result = PushSizeOnCursor(&Mode->Memory, StateSize).Memory;
- Mode->OpStateMemory = Result;
- return Result;
+ // NOTE(Peter): This isn't a problem if this fires, it just means our page size is too small,
+ // and its time to make the pages dynamically sized
+ Assert(Mode->Memory.Data.Size >= StateSize);
+ u8* Result = MemoryCursorPushSize(&Mode->Memory, StateSize).Memory;
+ Mode->OpStateMemory = Result;
+ return Result;
}
diff --git a/src/app/editor/foldhaus_panel.h b/src/app/editor/foldhaus_panel.h
index aec8cb9..b8a59a8 100644
--- a/src/app/editor/foldhaus_panel.h
+++ b/src/app/editor/foldhaus_panel.h
@@ -11,11 +11,11 @@
enum panel_split_direction
{
- PanelSplit_NoSplit,
- PanelSplit_Horizontal,
- PanelSplit_Vertical,
-
- PanelSplit_Count,
+ PanelSplit_NoSplit,
+ PanelSplit_Horizontal,
+ PanelSplit_Vertical,
+
+ PanelSplit_Count,
};
typedef struct panel panel;
@@ -25,32 +25,32 @@ typedef PANEL_MODAL_OVERRIDE_CALLBACK(panel_modal_override_callback);
struct panel
{
- s32 TypeIndex;
- gs_data StateMemory;
-
- panel* ModalOverride;
- panel* IsModalOverrideFor;
- panel_modal_override_callback* ModalOverrideCB;
-
- rect2 Bounds;
- panel_split_direction SplitDirection;
- r32 SplitPercent;
-
- panel* Parent;
-
- union{
- panel* Left;
- panel* Top;
- };
- union{
- panel* Right;
- panel* Bottom;
- };
+ s32 TypeIndex;
+ gs_data StateMemory;
+
+ panel* ModalOverride;
+ panel* IsModalOverrideFor;
+ panel_modal_override_callback* ModalOverrideCB;
+
+ rect2 Bounds;
+ panel_split_direction SplitDirection;
+ r32 SplitPercent;
+
+ panel* Parent;
+
+ union{
+ panel* Left;
+ panel* Top;
+ };
+ union{
+ panel* Right;
+ panel* Bottom;
+ };
};
struct free_panel
{
- free_panel* Next;
+ free_panel* Next;
};
#define PANEL_INIT_PROC(name) void name(panel* Panel, app_state* State, context Context)
@@ -65,25 +65,25 @@ typedef PANEL_RENDER_PROC(panel_render_proc);
// NOTE(Peter): This is used by the meta system to generate panel type info
struct panel_definition
{
- char* PanelName;
- s32 PanelNameLength;
- panel_init_proc* Init;
- panel_cleanup_proc* Cleanup;
- panel_render_proc* Render;
- input_command* InputCommands;
- s32 InputCommandsCount;
+ char* PanelName;
+ s32 PanelNameLength;
+ panel_init_proc* Init;
+ panel_cleanup_proc* Cleanup;
+ panel_render_proc* Render;
+ input_command* InputCommands;
+ s32 InputCommandsCount;
};
#define PANELS_MAX 16
struct panel_system
{
- panel_definition* PanelDefs;
- u32 PanelDefsCount;
-
- panel* Panels;
- u32 PanelsUsed;
-
- free_panel* FreeList;
+ panel_definition* PanelDefs;
+ u32 PanelDefsCount;
+
+ panel* Panels;
+ u32 PanelsUsed;
+
+ free_panel* FreeList;
};
/////////////////////////////////
@@ -95,164 +95,164 @@ struct panel_system
internal void
PanelSystem_Init(panel_system* PanelSystem, panel_definition* PanelDefs, u32 PanelDefsCount, gs_memory_arena* Storage)
{
- PanelSystem->FreeList = 0;
- PanelSystem->PanelDefs = PanelDefs;
- PanelSystem->PanelDefsCount = PanelDefsCount;
-
- PanelSystem->Panels = PushArray(Storage, panel, PANELS_MAX);
+ PanelSystem->FreeList = 0;
+ PanelSystem->PanelDefs = PanelDefs;
+ PanelSystem->PanelDefsCount = PanelDefsCount;
+
+ PanelSystem->Panels = PushArray(Storage, panel, PANELS_MAX);
}
internal panel*
PanelSystem_TakePanel(panel_system* PanelSystem)
{
- panel* FreeEntry = 0;
- if (PanelSystem->FreeList != 0)
- {
- free_panel* FreePanel = PanelSystem->FreeList;
- PanelSystem->FreeList = FreePanel->Next;
- FreeEntry = (panel*)FreePanel;
- }
- else
- {
- Assert(PanelSystem->PanelsUsed < PANELS_MAX);
- FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++;
- }
- return FreeEntry;
+ panel* FreeEntry = 0;
+ if (PanelSystem->FreeList != 0)
+ {
+ free_panel* FreePanel = PanelSystem->FreeList;
+ PanelSystem->FreeList = FreePanel->Next;
+ FreeEntry = (panel*)FreePanel;
+ }
+ else
+ {
+ Assert(PanelSystem->PanelsUsed < PANELS_MAX);
+ FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++;
+ }
+ return FreeEntry;
}
internal void
PanelSystem_FreePanel(panel* Panel, panel_system* PanelSystem)
{
- Assert(Panel >= PanelSystem->Panels && Panel <= PanelSystem->Panels + PANELS_MAX);
-
- free_panel* FreeEntry = (free_panel*)Panel;
- FreeEntry->Next = PanelSystem->FreeList;
- PanelSystem->FreeList = FreeEntry;
+ Assert(Panel >= PanelSystem->Panels && Panel <= PanelSystem->Panels + PANELS_MAX);
+
+ free_panel* FreeEntry = (free_panel*)Panel;
+ FreeEntry->Next = PanelSystem->FreeList;
+ PanelSystem->FreeList = FreeEntry;
}
internal void
PanelSystem_FreePanelRecursive(panel* Panel, panel_system* PanelSystem)
{
- if (Panel->SplitDirection != PanelSplit_NoSplit)
- {
- PanelSystem_FreePanelRecursive(Panel->Left, PanelSystem);
- PanelSystem_FreePanelRecursive(Panel->Right, PanelSystem);
- }
- PanelSystem_FreePanel(Panel, PanelSystem);
+ if (Panel->SplitDirection != PanelSplit_NoSplit)
+ {
+ PanelSystem_FreePanelRecursive(Panel->Left, PanelSystem);
+ PanelSystem_FreePanelRecursive(Panel->Right, PanelSystem);
+ }
+ PanelSystem_FreePanel(Panel, PanelSystem);
}
internal panel*
Panel_GetModalOverride(panel* Panel)
{
- panel* Result = Panel;
- if (Panel->ModalOverride != 0)
- {
- Result = Panel_GetModalOverride(Panel->ModalOverride);
- }
- return Result;
+ panel* Result = Panel;
+ if (Panel->ModalOverride != 0)
+ {
+ Result = Panel_GetModalOverride(Panel->ModalOverride);
+ }
+ return Result;
}
internal void
Panel_PushModalOverride(panel* Root, panel* Override, panel_modal_override_callback* Callback)
{
- Root->ModalOverride = Override;
- Root->ModalOverrideCB = Callback;
- Override->IsModalOverrideFor = Root;
- Override->Bounds = Root->Bounds;
+ Root->ModalOverride = Override;
+ Root->ModalOverrideCB = Callback;
+ Override->IsModalOverrideFor = Root;
+ Override->Bounds = Root->Bounds;
}
internal void
Panel_PopModalOverride(panel* Parent, panel_system* System)
{
- // TODO(pjs): Free the overrided panel
- PanelSystem_FreePanel(Parent->ModalOverride, System);
- Parent->ModalOverride = 0;
+ // TODO(pjs): Free the overrided panel
+ PanelSystem_FreePanel(Parent->ModalOverride, System);
+ Parent->ModalOverride = 0;
}
internal void
Panel_SetCurrentType(panel* Panel, panel_system* System, s32 NewPanelType, gs_data TypeStateMemory, app_state* State, context Context)
{
- s32 OldTypeIndex = Panel->TypeIndex;
-
- Panel->TypeIndex = NewPanelType;
- Panel->StateMemory = TypeStateMemory;
-
- if(OldTypeIndex >= 0)
- {
- System->PanelDefs[OldTypeIndex].Cleanup(Panel, State);
- }
+ s32 OldTypeIndex = Panel->TypeIndex;
+
+ Panel->TypeIndex = NewPanelType;
+ Panel->StateMemory = TypeStateMemory;
+
+ if(OldTypeIndex >= 0)
+ {
+ System->PanelDefs[OldTypeIndex].Cleanup(Panel, State);
+ }
}
internal void
Panel_SetType(panel* Panel, panel_system* System, s32 NewPanelTypeIndex, app_state* State, context Context)
{
- gs_data EmptyStateData = {0};
- Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context);
- System->PanelDefs[NewPanelTypeIndex].Init(Panel, State, Context);
+ gs_data EmptyStateData = {0};
+ Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context);
+ System->PanelDefs[NewPanelTypeIndex].Init(Panel, State, Context);
}
#define Panel_GetStateStruct(p, type) (type*)Panel_GetStateMemory((p), sizeof(type)).Memory
internal gs_data
Panel_GetStateMemory(panel* Panel, u64 Size)
{
- Assert(Panel->StateMemory.Size == Size);
- gs_data Result = Panel->StateMemory;
- return Result;
+ Assert(Panel->StateMemory.Size == Size);
+ gs_data Result = Panel->StateMemory;
+ return Result;
}
internal panel*
PanelSystem_PushPanel(panel_system* PanelSystem, s32 PanelTypeIndex, app_state* State, context Context)
{
- panel* Panel = PanelSystem_TakePanel(PanelSystem);
- Panel_SetType(Panel, PanelSystem, PanelTypeIndex, State, Context);
- return Panel;
+ panel* Panel = PanelSystem_TakePanel(PanelSystem);
+ Panel_SetType(Panel, PanelSystem, PanelTypeIndex, State, Context);
+ return Panel;
}
internal void
SplitPanel(panel* Parent, r32 Percent, panel_split_direction SplitDirection, panel_system* PanelSystem, app_state* State, context Context)
{
- if (Percent >= 0.0f && Percent <= 1.0f)
- {
- Parent->SplitDirection = SplitDirection;
- Parent->SplitPercent = Percent;
-
- s32 ParentTypeIndex = Parent->TypeIndex;
- gs_data ParentStateMemory = Parent->StateMemory;
-
- Parent->Left = PanelSystem_TakePanel(PanelSystem);
- Panel_SetCurrentType(Parent->Left, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
- Parent->Left->Parent = Parent;
-
- Parent->Right = PanelSystem_TakePanel(PanelSystem);
- Panel_SetCurrentType(Parent->Right, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
- Parent->Right->Parent = Parent;
- }
+ if (Percent >= 0.0f && Percent <= 1.0f)
+ {
+ Parent->SplitDirection = SplitDirection;
+ Parent->SplitPercent = Percent;
+
+ s32 ParentTypeIndex = Parent->TypeIndex;
+ gs_data ParentStateMemory = Parent->StateMemory;
+
+ Parent->Left = PanelSystem_TakePanel(PanelSystem);
+ Panel_SetCurrentType(Parent->Left, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
+ Parent->Left->Parent = Parent;
+
+ Parent->Right = PanelSystem_TakePanel(PanelSystem);
+ Panel_SetCurrentType(Parent->Right, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
+ Parent->Right->Parent = Parent;
+ }
}
internal void
SplitPanelVertically(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context)
{
- SplitPanel(Parent, Percent, PanelSplit_Vertical, PanelSystem, State, Context);
+ SplitPanel(Parent, Percent, PanelSplit_Vertical, PanelSystem, State, Context);
}
internal void
SplitPanelHorizontally(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context)
{
- SplitPanel(Parent, Percent, PanelSplit_Horizontal, PanelSystem, State, Context);
+ SplitPanel(Parent, Percent, PanelSplit_Horizontal, PanelSystem, State, Context);
}
internal void
ConsolidatePanelsKeepOne(panel* Parent, panel* PanelToKeep, panel_system* PanelSystem)
{
- panel* LeftChild = Parent->Left;
- panel* RightChild = Parent->Right;
-
- panel* PanelToDestroy = PanelToKeep == LeftChild ? RightChild : LeftChild;
-
- *Parent = *PanelToKeep;
-
- PanelSystem_FreePanel(PanelToKeep, PanelSystem);
- PanelSystem_FreePanelRecursive(PanelToDestroy, PanelSystem);
+ panel* LeftChild = Parent->Left;
+ panel* RightChild = Parent->Right;
+
+ panel* PanelToDestroy = PanelToKeep == LeftChild ? RightChild : LeftChild;
+
+ *Parent = *PanelToKeep;
+
+ PanelSystem_FreePanel(PanelToKeep, PanelSystem);
+ PanelSystem_FreePanelRecursive(PanelToDestroy, PanelSystem);
}
/////////////////////////////////
@@ -264,178 +264,178 @@ ConsolidatePanelsKeepOne(panel* Parent, panel* PanelToKeep, panel_system* PanelS
internal rect2
GetTopPanelBounds(panel* Panel)
{
- rect2 Result = {};
- Result.Min = v2{
- Panel->Bounds.Min.x,
- LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y)
- };
- Result.Max = Panel->Bounds.Max;
- return Result;
+ rect2 Result = {};
+ Result.Min = v2{
+ Panel->Bounds.Min.x,
+ LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y)
+ };
+ Result.Max = Panel->Bounds.Max;
+ return Result;
}
internal rect2
GetBottomPanelBounds(panel* Panel)
{
- rect2 Result = {};
- Result.Min = Panel->Bounds.Min;
- Result.Max = v2{
- Panel->Bounds.Max.x,
- LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y)
- };
- return Result;
+ rect2 Result = {};
+ Result.Min = Panel->Bounds.Min;
+ Result.Max = v2{
+ Panel->Bounds.Max.x,
+ LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y)
+ };
+ return Result;
}
internal rect2
GetRightPanelBounds(panel* Panel)
{
- rect2 Result = {};
- Result.Min = v2{
- LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x),
- Panel->Bounds.Min.y
- };
- Result.Max = Panel->Bounds.Max;
- return Result;
+ rect2 Result = {};
+ Result.Min = v2{
+ LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x),
+ Panel->Bounds.Min.y
+ };
+ Result.Max = Panel->Bounds.Max;
+ return Result;
}
internal rect2
GetLeftPanelBounds(panel* Panel)
{
- rect2 Result = {};
- Result.Min = Panel->Bounds.Min;
- Result.Max = v2{
- LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x),
- Panel->Bounds.Max.y
- };
- return Result;
+ rect2 Result = {};
+ Result.Min = Panel->Bounds.Min;
+ Result.Max = v2{
+ LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x),
+ Panel->Bounds.Max.y
+ };
+ return Result;
}
internal rect2
GetTopPanelBounds(panel* Panel, rect2 PanelBounds)
{
- rect2 Result = {};
- Result.Min = v2{
- PanelBounds.Min.x,
- LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y)
- };
- Result.Max = PanelBounds.Max;
- return Result;
+ rect2 Result = {};
+ Result.Min = v2{
+ PanelBounds.Min.x,
+ LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y)
+ };
+ Result.Max = PanelBounds.Max;
+ return Result;
}
internal rect2
GetBottomPanelBounds(panel* Panel, rect2 PanelBounds)
{
- rect2 Result = {};
- Result.Min = PanelBounds.Min;
- Result.Max = v2{
- PanelBounds.Max.x,
- LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y)
- };
- return Result;
+ rect2 Result = {};
+ Result.Min = PanelBounds.Min;
+ Result.Max = v2{
+ PanelBounds.Max.x,
+ LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y)
+ };
+ return Result;
}
internal rect2
GetRightPanelBounds(panel* Panel, rect2 PanelBounds)
{
- rect2 Result = {};
- Result.Min = v2{
- LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x),
- PanelBounds.Min.y
- };
- Result.Max = PanelBounds.Max;
- return Result;
+ rect2 Result = {};
+ Result.Min = v2{
+ LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x),
+ PanelBounds.Min.y
+ };
+ Result.Max = PanelBounds.Max;
+ return Result;
}
internal rect2
GetLeftPanelBounds(panel* Panel, rect2 PanelBounds)
{
- rect2 Result = {};
- Result.Min = PanelBounds.Min;
- Result.Max = v2{
- LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x),
- PanelBounds.Max.y
- };
- return Result;
+ rect2 Result = {};
+ Result.Min = PanelBounds.Min;
+ Result.Max = v2{
+ LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x),
+ PanelBounds.Max.y
+ };
+ return Result;
}
internal void
Panel_UpdateLayout(panel* Panel, rect2 Bounds)
{
- Panel->Bounds = Bounds;
-
- if (Panel->SplitDirection != PanelSplit_NoSplit)
+ Panel->Bounds = Bounds;
+
+ if (Panel->SplitDirection != PanelSplit_NoSplit)
+ {
+ rect2 LeftOrTopBounds = {};
+ rect2 RightOrBottomBounds = {};
+ switch (Panel->SplitDirection)
{
- rect2 LeftOrTopBounds = {};
- rect2 RightOrBottomBounds = {};
- switch (Panel->SplitDirection)
- {
- case PanelSplit_Horizontal:
- {
- LeftOrTopBounds = GetTopPanelBounds(Panel);
- RightOrBottomBounds = GetBottomPanelBounds(Panel);
- } break;
-
- case PanelSplit_Vertical:
- {
- LeftOrTopBounds = GetLeftPanelBounds(Panel);
- RightOrBottomBounds = GetRightPanelBounds(Panel);
- } break;
-
- InvalidDefaultCase;
- }
-
- Panel_UpdateLayout(Panel->Left, LeftOrTopBounds);
- Panel_UpdateLayout(Panel->Right, RightOrBottomBounds);
+ case PanelSplit_Horizontal:
+ {
+ LeftOrTopBounds = GetTopPanelBounds(Panel);
+ RightOrBottomBounds = GetBottomPanelBounds(Panel);
+ } break;
+
+ case PanelSplit_Vertical:
+ {
+ LeftOrTopBounds = GetLeftPanelBounds(Panel);
+ RightOrBottomBounds = GetRightPanelBounds(Panel);
+ } break;
+
+ InvalidDefaultCase;
}
+
+ Panel_UpdateLayout(Panel->Left, LeftOrTopBounds);
+ Panel_UpdateLayout(Panel->Right, RightOrBottomBounds);
+ }
}
internal void
PanelSystem_UpdateLayout(panel_system* System, rect2 WindowBounds)
{
- panel* Root = System->Panels;
- Panel_UpdateLayout(Root, WindowBounds);
+ panel* Root = System->Panels;
+ Panel_UpdateLayout(Root, WindowBounds);
}
internal panel*
GetPanelContainingPoint(panel* Panel, v2 Point)
{
- panel* Result = 0;
-
- if (PointIsInRect(Panel->Bounds, Point))
+ panel* Result = 0;
+
+ if (PointIsInRect(Panel->Bounds, Point))
+ {
+ switch (Panel->SplitDirection)
{
- switch (Panel->SplitDirection)
+ case PanelSplit_NoSplit:
+ {
+ Result = Panel;
+ }break;
+
+ case PanelSplit_Vertical:
+ case PanelSplit_Horizontal:
+ {asdfasdfasdfasdfasdf
+ if (PointIsInRect(Panel->Left->Bounds, Point))
{
- case PanelSplit_NoSplit:
- {
- Result = Panel;
- }break;
-
- case PanelSplit_Vertical:
- case PanelSplit_Horizontal:
- {
- if (PointIsInRect(Panel->Left->Bounds, Point))
- {
- Result = GetPanelContainingPoint(Panel->Left, Point);
- }
- else if (PointIsInRect(Panel->Right->Bounds, Point))
- {
- Result = GetPanelContainingPoint(Panel->Right, Point);
- }
- }break;
-
- InvalidDefaultCase;
+ Result = GetPanelContainingPoint(Panel->Left, Point);
}
+ else if (PointIsInRect(Panel->Right->Bounds, Point))
+ {
+ Result = GetPanelContainingPoint(Panel->Right, Point);
+ }
+ }break;
+
+ InvalidDefaultCase;
}
-
- return Result;
+ }
+
+ return Result;
}
internal panel*
PanelSystem_GetPanelContainingPoint(panel_system* System, v2 Point)
{
- panel* Result = 0;
- panel* Root = System->Panels;
- Result = GetPanelContainingPoint(Root, Point);
- return Result;
+ panel* Result = 0;
+ panel* Root = System->Panels;
+ Result = GetPanelContainingPoint(Root, Point);
+ return Result;
}
#define FOLDHAUS_PANEL_H
diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h
new file mode 100644
index 0000000..d618089
--- /dev/null
+++ b/src/app/editor/interface.h
@@ -0,0 +1,1677 @@
+//
+// File: interface.h
+// Author: Peter Slattery
+// Creation Date: 2020-01-01
+//
+#ifndef INTERFACE_H
+
+#define InterfaceAssert(IMemPtr) Assert(IMemPtr && (u64)IMemPtr != 0x5 && (u64)IMemPtr != 0xC)
+
+enum gs_string_alignment
+{
+ Align_Left,
+ Align_Center,
+ Align_Right,
+};
+
+internal void
+ClipUVRect(rect2* Bounds, rect2* UVs, rect2 ClippingBox)
+{
+ rect2 NewBounds = Rect2Union(*Bounds, ClippingBox);
+
+ r32 OldWidth = Rect2Width(*Bounds);
+ r32 OldHeight = Rect2Height(*Bounds);
+
+ v2 MinInsetPercent = v2{
+ (NewBounds.Min.x - Bounds->Min.x) / OldWidth,
+ (NewBounds.Min.y - Bounds->Min.y) / OldHeight,
+ };
+
+ v2 MaxInsetPercent = v2{
+ (NewBounds.Max.x - Bounds->Min.x) / OldWidth,
+ (NewBounds.Max.y - Bounds->Min.y) / OldHeight,
+ };
+
+ UVs->Min.x = LerpR32(MinInsetPercent.x, UVs->Min.x, UVs->Max.x);
+ UVs->Min.y = LerpR32(MinInsetPercent.y, UVs->Min.y, UVs->Max.y);
+ UVs->Max.x = LerpR32(MaxInsetPercent.x, UVs->Min.x, UVs->Max.x);
+ UVs->Max.y = LerpR32(MaxInsetPercent.y, UVs->Min.y, UVs->Max.y);
+
+ *Bounds = NewBounds;
+}
+
+internal void
+DrawCharacter_ (render_quad_batch_constructor* BatchConstructor, r32 MinX, r32 MinY, codepoint_bitmap CodepointInfo, rect2 ClippingBox, v4 Color)
+{
+ rect2 Bounds = {};
+ Bounds.Min.x = FloorR32(MinX);
+ Bounds.Min.y = FloorR32(MinY);
+ Bounds.Max.x = Bounds.Min.x + (CodepointInfo.Width);
+ Bounds.Max.y = Bounds.Min.y + (CodepointInfo.Height);
+
+ rect2 UVBounds = {};
+ UVBounds.Min = CodepointInfo.UVMin;
+ UVBounds.Max = CodepointInfo.UVMax;
+
+ ClipUVRect(&Bounds, &UVBounds, ClippingBox);
+
+ s32 AlignedMinX = (s32)(MinX);
+ s32 AlignedMinY = (s32)(MinY);
+ s32 AlignedMaxX = AlignedMinX + (CodepointInfo.Width);
+ s32 AlignedMaxY = AlignedMinY + (CodepointInfo.Height);
+
+ PushQuad2DOnBatch(BatchConstructor,
+ Rect2BottomLeft(Bounds), Rect2BottomRight(Bounds),
+ Rect2TopRight(Bounds), Rect2TopLeft(Bounds),
+ UVBounds.Min, UVBounds.Max,
+ Color);
+}
+
+internal v2
+DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, rect2 ClippingBox, v4 Color)
+{
+ s32 GlyphDataIndex = GetIndexForCodepoint(Font, C);
+ codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex];
+
+ // NOTE(Peter):
+ r32 MinX = Position.x + CodepointInfo.XOffset;
+ r32 MinY = Position.y + CodepointInfo.YOffset;
+ DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color);
+
+ // NOTE(Peter):
+ v2 PointAfterCharacter = v2{Position.x + CodepointInfo.Width, Position.y};
+ return PointAfterCharacter;
+}
+
+internal v2
+DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, rect2 ClippingBox, v4 Color)
+{
+ s32 GlyphDataIndex = GetIndexForCodepoint(Font, C);
+ codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex];
+
+ // NOTE(Peter):
+ r32 MinX = Position.x - (CodepointInfo.XOffset + CodepointInfo.Width);
+ r32 MinY = Position.y + CodepointInfo.YOffset + CodepointInfo.YOffset;
+ DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color);
+
+ // NOTE(Peter):
+ v2 PointAfterCharacter = v2{Position.x-(CodepointInfo.Width + CodepointInfo.XOffset), Position.y};
+ return PointAfterCharacter;
+}
+
+internal void
+DrawCursor (render_command_buffer* RenderBuffer, v2 RegisterPosition, bitmap_font* Font, v4 Color)
+{
+ rect2 CursorRect = {};
+ CursorRect.Min = RegisterPosition;
+ CursorRect.Max = CursorRect.Min + v2{5, (r32)Font->Ascent};
+ PushRenderQuad2D(RenderBuffer, CursorRect, Color);
+}
+
+internal v2
+DrawStringLeftAligned (render_command_buffer* RenderBuffer, render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color, s32 CursorBeforeIndex, v4 CursorColor)
+{
+ v2 RegisterPosition = InitialRegisterPosition;
+ char* C = gs_string;
+ for (s32 i = 0; i < Length; i++)
+ {
+ if (i == CursorBeforeIndex)
+ {
+ DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor);
+ }
+
+ v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color);
+ RegisterPosition.x = PositionAfterCharacter.x;
+ C++;
+ }
+ if (CursorBeforeIndex == Length)
+ {
+ DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor);
+ }
+ return RegisterPosition;
+}
+
+internal v2
+DrawStringRightAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color)
+{
+ v2 RegisterPosition = InitialRegisterPosition;
+ char* C = gs_string + Length - 1;
+ for (s32 i = Length - 1; i >= 0; i--)
+ {
+ v2 PositionAfterCharacter = DrawCharacterRightAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color);
+ RegisterPosition.x = PositionAfterCharacter.x;
+ C--;
+ }
+ return RegisterPosition;
+}
+
+internal v2
+DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* Font, v2 Position, v4 Color, s32 CursorPosition, v4 CursorColor, gs_string_alignment Alignment = Align_Left)
+{
+ DEBUG_TRACK_FUNCTION;
+ v2 LowerRight = Position;
+
+ render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length + 1,
+ Font->BitmapMemory,
+ Font->BitmapTextureHandle,
+ Font->BitmapWidth,
+ Font->BitmapHeight,
+ Font->BitmapBytesPerPixel,
+ Font->BitmapStride);
+
+ // TODO(pjs): I don't like this solution but it'll do for now and I want to focus on other problems
+ // especially since I think this one will go away once I finish the ui overhaul
+ rect2 InfiniteClipBox = {};
+ InfiniteClipBox.Min.x = -100000;
+ InfiniteClipBox.Min.y = -100000;
+ InfiniteClipBox.Max.x = 100000;
+ InfiniteClipBox.Max.y = 100000;
+
+ v2 RegisterPosition = Position;
+ if (Alignment == Align_Left)
+ {
+ RegisterPosition = DrawStringLeftAligned(RenderBuffer, &BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color, CursorPosition, CursorColor);
+ }
+ else if (Alignment == Align_Right)
+ {
+ RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color);
+ }
+ else
+ {
+ InvalidCodePath;
+ }
+
+ LowerRight.x = RegisterPosition.x;
+
+ return LowerRight;
+}
+
+internal void
+DrawCursor (render_quad_batch_constructor* BatchConstructor, v2 Position, v4 Color, bitmap_font Font)
+{
+ v2 Min = Position;
+ v2 Max = Position + v2{(r32)Font.MaxCharWidth, (r32)(Font.Ascent + Font.Descent)};
+ PushQuad2DOnBatch(BatchConstructor, Min, Max, Color);
+}
+
+#if 0
+internal v2
+DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32 CursorPosition, bitmap_font* Font, v2 Position, v4 Color, v4 CursorColor, gs_string_alignment Alignment = Align_Left)
+{
+ DEBUG_TRACK_FUNCTION;
+ v2 LowerRight = Position;
+
+ // NOTE(Peter): We push this on first so that the cursor will be drawn underneath any character it may overlap with
+ render_quad_batch_constructor CursorBatch = PushRenderQuad2DBatch(RenderBuffer, 1);
+ render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length,
+ Font->BitmapMemory,
+ Font->BitmapTextureHandle,
+ Font->BitmapWidth,
+ Font->BitmapHeight,
+ Font->BitmapBytesPerPixel,
+ Font->BitmapStride);
+
+ v2 RegisterPosition = Position;
+ if (Alignment == Align_Left)
+ {
+ RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, Color);
+ DrawCursor(&CursorBatch, RegisterPosition, GreenV4, *Font);
+ if (String.Length - CursorPosition > 0)
+ {
+ RegisterPosition = DrawStringLeftAligned(&BatchConstructor,
+ String.Length - CursorPosition,
+ String.Str + CursorPosition,
+ RegisterPosition, Font, Color);
+ }
+ }
+ else if (Alignment == Align_Right)
+ {
+ RegisterPosition = DrawStringRightAligned(&BatchConstructor,
+ CursorPosition, String.Str,
+ RegisterPosition, Font, Color);
+ DrawCursor(&CursorBatch, RegisterPosition, GreenV4, *Font);
+ if (String.Length - CursorPosition > 0)
+ {
+ RegisterPosition = DrawStringRightAligned(&BatchConstructor,
+ String.Length - CursorPosition,
+ String.Str + CursorPosition,
+ RegisterPosition, Font, Color);
+ }
+ }
+ else
+ {
+ InvalidCodePath;
+ }
+
+ LowerRight.x = RegisterPosition.x;
+ return LowerRight;
+}
+#endif
+
+enum ui_widget_flag
+{
+ UIWidgetFlag_ExpandsToFitChildren,
+
+ UIWidgetFlag_DrawBackground,
+ UIWidgetFlag_DrawString,
+ UIWidgetFlag_DrawOutline,
+ UIWidgetFlag_DrawHorizontalFill,
+ UIWidgetFlag_DrawVerticalFill,
+ UIWidgetFlag_DrawFillReversed,
+ UIWidgetFlag_DrawFillAsHandle,
+
+ UIWidgetFlag_Clickable,
+ UIWidgetFlag_Selectable,
+ UIWidgetFlag_Typable,
+};
+
+struct ui_widget_id
+{
+ u64 Id;
+ u64 ZIndex;
+};
+
+enum ui_layout_direction
+{
+ LayoutDirection_TopDown,
+ LayoutDirection_BottomUp,
+ LayoutDirection_Inherit,
+};
+
+struct ui_column
+{
+ r32 XMin;
+ r32 XMax;
+};
+
+struct ui_widget
+{
+ ui_widget_id Id;
+
+ gs_string String;
+ gs_string_alignment Alignment;
+
+ rect2 Bounds;
+ u64 Flags;
+
+ ui_widget* Next;
+
+ // Slider
+ r32 FillPercent;
+
+ // Layout
+ ui_widget* Parent;
+
+ v2 Margin;
+ r32 RowHeight;
+ r32 RowYAt;
+
+ ui_layout_direction FillDirection;
+
+ ui_column* Columns;
+ u32 ColumnsCount;
+ u32 ColumnsFilled;
+
+ // NOTE(pjs): I'm not sure this will stay but
+ // its here so that when we end things like a dropdown,
+ // we can check the retained state of that dropdown
+ ui_widget_id WidgetReference;
+
+ u64 ChildZIndexOffset;
+
+ ui_widget* ChildrenRoot;
+ ui_widget* ChildrenHead;
+ u32 ChildCount;
+};
+
+struct ui_eval_result
+{
+ bool Clicked;
+ bool Held;
+ v2 DragDelta;
+};
+
+struct interface_config
+{
+ v4 PanelBG;
+
+ // TODO(pjs): Turn these into _Default, _Hot, _Active
+ v4 ButtonColor_Inactive, ButtonColor_Active, ButtonColor_Selected;
+
+ v4 TextColor;
+
+#define LIST_BG_COLORS_COUNT 2
+ v4 ListBGColors[LIST_BG_COLORS_COUNT];
+ v4 ListBGHover;
+ v4 ListBGSelected;
+
+ bitmap_font* Font;
+ r32 FontSize;
+ v2 Margin;
+ r32 RowHeight;
+};
+
+struct ui_widget_retained_state
+{
+ ui_widget_id Id;
+ bool Value;
+ r32 InitialValueR32;
+ u32 FramesSinceAccess;
+
+ // For use in layouts that allow you to scroll / pan
+ v2 ChildrenDrawOffset;
+
+ gs_string EditString;
+
+ // For dropdowns and rows to be able to error check not closing
+ // a layout you open
+ u32 MaxChildren;
+};
+
+struct ui_interface
+{
+ interface_config Style;
+
+ mouse_state Mouse;
+ rect2 WindowBounds;
+
+ // A per-frame string of the characters which have been typed
+ gs_const_string TempInputString;
+ u64 CursorPosition;
+
+ render_command_buffer* RenderBuffer;
+
+ ui_widget* Widgets;
+ u64 WidgetsCount;
+ u64 WidgetsCountMax;
+
+ ui_widget* DrawOrderHead;
+ ui_widget* DrawOrderRoot;
+
+ ui_widget_id HotWidget;
+ // This should really never get higher than 1 or 2
+ u8 HotWidgetFramesSinceUpdate;
+
+ ui_widget_id ActiveWidget;
+ ui_widget_id LastActiveWidget;
+
+ ui_widget* ActiveLayout;
+
+#define RETAINED_STATE_MAX 128
+ ui_widget_retained_state RetainedState[RETAINED_STATE_MAX];
+ u64 RetainedStateCount;
+
+ gs_memory_arena* PerFrameMemory;
+
+ // TODO(pjs): DONT USE THIS
+ // Right now you only need this to create EditStrings for ui_widget_retained_state's
+ // and even for those, you eventually want a better solution than "create a string and it lives forever"
+ // TODO(pjs): Get rid of the need for this vvv
+ gs_memory_arena* Permanent;
+};
+
+internal void
+ui_InterfaceReset(ui_interface* Interface)
+{
+ Interface->WidgetsCount = 0;
+ Interface->DrawOrderHead = 0;
+ Interface->DrawOrderRoot = 0;
+ MemoryArenaClear(Interface->PerFrameMemory);
+ InterfaceAssert(Interface->PerFrameMemory);
+
+ for (u32 i = 0; i < Interface->RetainedStateCount; i++)
+ {
+ Interface->RetainedState[i].FramesSinceAccess += 1;
+ if (Interface->RetainedState[i].FramesSinceAccess > 1)
+ {
+ Interface->RetainedState[i].Id = {0};
+ }
+ }
+
+ Interface->LastActiveWidget = Interface->ActiveWidget;
+}
+
+internal bool
+ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B)
+{
+ bool Result = (A.Id == B.Id);// && (A.ParentId == B.ParentId);
+ return Result;
+}
+
+internal void
+ui_WidgetSetFlag(ui_widget* Widget, u64 Flag)
+{
+ u64 Value = ((u64)1 << Flag);
+ Widget->Flags = Widget->Flags | Value;
+}
+
+internal void
+ui_WidgetClearFlag(ui_widget* Widget, u64 Flag)
+{
+ u64 Value = ((u64)1 << Flag);
+ Widget->Flags = Widget->Flags & ~Value;
+}
+
+internal bool
+ui_WidgetIsFlagSet(ui_widget Widget, u64 Flag)
+{
+ u64 Value = ((u64)1 << Flag);
+ bool Result = (Widget.Flags & Value);
+ return Result;
+}
+
+internal void
+ui_WidgetSetChildrenPopover(ui_widget* Widget)
+{
+ Widget->ChildZIndexOffset = 1000;
+}
+
+internal ui_widget*
+ui_WidgetGetWidgetWithId(ui_widget* Parent, ui_widget_id Id)
+{
+ ui_widget* Result = 0;
+
+ if (ui_WidgetIdsEqual(Parent->Id, Id))
+ {
+ Result = Parent;
+ }
+ else if (Parent->ChildrenRoot != 0)
+ {
+ for (ui_widget* At = Parent->ChildrenRoot; At != 0; At = At->Next)
+ {
+ Result = ui_WidgetGetWidgetWithId(At, Id);
+ if (Result != 0)
+ {
+ break;
+ }
+ }
+ }
+
+ return Result;
+}
+
+internal ui_widget*
+ui_InterfaceGetWidgetWithId(ui_interface* Interface, ui_widget_id Id)
+{
+ ui_widget* Result = 0;
+
+ for (ui_widget* At = Interface->DrawOrderRoot; At != 0; At = At->Next)
+ {
+ Result = ui_WidgetGetWidgetWithId(At, Id);
+ if (Result != 0)
+ {
+ break;
+ }
+ }
+
+ return Result;
+}
+
+internal ui_widget_retained_state*
+ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id)
+{
+ ui_widget_retained_state* Result = 0;
+ for (u64 i = 0; i < Interface->RetainedStateCount; i++)
+ {
+ if (ui_WidgetIdsEqual(Interface->RetainedState[i].Id, Id))
+ {
+ // NOTE(PS): If we are accessing a retained state, it shouldn't
+ // have been accessed longer ago than the last frame
+ //Assert(Interface->RetainedState[i].FramesSinceAccess <= 2);
+ Interface->RetainedState[i].FramesSinceAccess = 0;
+ Result = Interface->RetainedState + i;
+ break;
+ }
+ }
+ return Result;
+}
+
+internal ui_widget_retained_state*
+ui_CreateRetainedState(ui_interface* Interface, ui_widget* Widget)
+{
+ u64 Index = RETAINED_STATE_MAX;
+ if (Interface->RetainedStateCount >= RETAINED_STATE_MAX)
+ {
+ for (u32 i = 0; i < Interface->RetainedStateCount; i++)
+ {
+ if (Interface->RetainedState[i].FramesSinceAccess > 0)
+ {
+ Index = i;
+ break;
+ }
+ }
+ } else {
+ Index = Interface->RetainedStateCount++;
+ }
+
+ Assert(Index < RETAINED_STATE_MAX);
+ ui_widget_retained_state* Result = Interface->RetainedState + Index;
+ Result->Id = Widget->Id;
+ if (Result->EditString.Size != 256)
+ {
+ Result->EditString = PushString(Interface->Permanent, 256);
+ }
+ return Result;
+}
+
+internal ui_widget_retained_state*
+ui_GetOrCreateRetainedState(ui_interface* Interface, ui_widget* Widget)
+{
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
+ if (!State)
+ {
+ State = ui_CreateRetainedState(Interface, Widget);
+ }
+ return State;
+}
+
+internal ui_widget*
+ui_CreateWidget(ui_interface* Interface, gs_string String)
+{
+ InterfaceAssert(Interface->PerFrameMemory);
+ Assert(Interface->WidgetsCount < Interface->WidgetsCountMax);
+ u64 Index = Interface->WidgetsCount++;
+ ui_widget* Result = Interface->Widgets + Index;
+ ZeroStruct(Result);
+
+ Result->Parent = Interface->ActiveLayout;
+
+ u64 Id = HashDJB2ToU64(StringExpand(String));
+ if (Result->Parent)
+ {
+ Id = HashAppendDJB2ToU32(Id, Result->Parent->Id.Id);
+ Id = HashAppendDJB2ToU32(Id, Result->Parent->ChildCount);
+ //Result->Id.ParentId = Result->Parent->Id.Id;
+ }
+ Result->Id.Id = Id;
+
+ u64 ZIndex = Index + 1;
+ if (Result->Parent)
+ {
+ Result->ChildZIndexOffset += Result->Parent->ChildZIndexOffset;
+ ZIndex += Result->Parent->ChildZIndexOffset;
+ }
+ Result->Id.ZIndex = ZIndex;
+
+ Result->String = PushStringCopy(Interface->PerFrameMemory, String.ConstString);
+ InterfaceAssert(Interface->PerFrameMemory);
+ Result->Alignment = Align_Left;
+ Result->Next = 0;
+ Result->ChildrenRoot = 0;
+ Result->ChildrenHead = 0;
+ Result->Flags = 0;
+ ui_WidgetSetFlag(Result, UIWidgetFlag_ExpandsToFitChildren);
+
+ return Result;
+}
+
+//
+// Interaction
+//
+
+internal b32
+ui_MouseClickedRect(ui_interface Interface, rect2 Rect)
+{
+ b32 Result = MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState);
+ Result &= PointIsInRect(Rect, Interface.Mouse.Pos);
+ return Result;
+}
+
+// Layout
+
+static rect2
+ui_ReserveBounds(ui_interface* Interface, ui_widget* Widget, bool Inset)
+{
+ Assert(Widget->ColumnsCount > 0);
+ rect2 Bounds = {0};
+ u32 ColumnIndex = Widget->ChildCount % Widget->ColumnsCount;
+
+ ui_column Column = Widget->Columns[ColumnIndex];
+ Bounds.Min.x = Column.XMin;
+ Bounds.Min.y = Widget->RowYAt;
+ Bounds.Max.x = Column.XMax;
+ Bounds.Max.y = Bounds.Min.y + Widget->RowHeight;
+
+ if (Inset)
+ {
+ Bounds.Min.x += Widget->Margin.x;
+ Bounds.Min.y += Widget->Margin.y;
+ Bounds.Max.x -= Widget->Margin.x;
+ Bounds.Max.y -= Widget->Margin.y;
+ }
+
+ if (Widget->ChildCount == 0)
+ {
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
+ if (State)
+ {
+ Bounds = Rect2Translate(Bounds, State->ChildrenDrawOffset);
+ }
+ }
+
+ return Bounds;
+}
+
+internal void
+ui_CommitBounds(ui_widget* Parent, rect2 Bounds)
+{
+ u32 ColumnIndex = Parent->ChildCount % Parent->ColumnsCount;
+ if (ColumnIndex == 0)
+ {
+ switch (Parent->FillDirection)
+ {
+ case LayoutDirection_BottomUp:
+ {
+ Parent->RowYAt = Bounds.Max.y;
+ if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren))
+ {
+ Parent->Bounds.Max.y = Parent->RowYAt;
+ }
+ }break;
+
+ case LayoutDirection_TopDown:
+ {
+ Parent->RowYAt = Bounds.Min.y - Parent->RowHeight;
+ if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren))
+ {
+ Parent->Bounds.Min.y = Bounds.Min.y;
+ }
+ }break;
+ }
+ }
+}
+
+internal void
+ui_ExpandParentToFit(ui_widget* Widget)
+{
+ ui_widget* Parent = Widget->Parent;
+ switch (Widget->FillDirection)
+ {
+ case LayoutDirection_TopDown:
+ {
+ Parent->Bounds.Min.y = Min(Parent->Bounds.Min.y, Widget->Bounds.Min.y - Parent->Margin.y);
+ }break;
+
+ case LayoutDirection_BottomUp:
+ {
+ Parent->Bounds.Max.y = Max(Parent->Bounds.Max.y, Widget->Bounds.Max.y + Parent->Margin.y);
+ }break;
+
+ InvalidDefaultCase;
+ }
+}
+
+internal void
+ui_WidgetCreateColumns(ui_widget* Widget, u32 ColumnsCount, ui_interface* Interface)
+{
+ Widget->Columns = PushArray(Interface->PerFrameMemory, ui_column, ColumnsCount);
+ InterfaceAssert(Interface->PerFrameMemory);
+ Widget->ColumnsCount = ColumnsCount;
+ Widget->ColumnsFilled = 0;
+}
+
+internal void
+ui_WidgetInitUniformColumns(ui_widget* Widget)
+{
+ r32 CurrentRowWidth = Rect2Width(Widget->Bounds);
+ r32 ColumnWidth = CurrentRowWidth / Widget->ColumnsCount;
+ for (u32 i = 0; i < Widget->ColumnsCount; i++)
+ {
+ ui_column* Column = Widget->Columns + i;
+ Column->XMin = Widget->Bounds.Min.x + (ColumnWidth * i);
+ Column->XMax = Column->XMin + ColumnWidth;
+ }
+}
+
+internal ui_widget*
+ui_CreateLayoutWidget(ui_interface* Interface, rect2 Bounds, gs_string Name, ui_layout_direction FillDir = LayoutDirection_Inherit)
+{
+ ui_widget* Result = ui_CreateWidget(Interface, Name);
+ //ui_WidgetSetFlag(Result, UIWidgetFlag_DrawOutline);
+
+ Result->Bounds = Bounds;
+ Result->Margin = Interface->Style.Margin;
+ Result->RowHeight = Interface->Style.RowHeight;
+
+ // Single Column Layout
+ ui_WidgetCreateColumns(Result, 1, Interface);
+ ui_WidgetInitUniformColumns(Result);
+
+ if (FillDir == LayoutDirection_Inherit && Result->Parent != 0)
+ {
+ Result->FillDirection = Result->Parent->FillDirection;
+ }
+ else
+ {
+ Result->FillDirection = FillDir;
+ }
+
+ switch(Result->FillDirection)
+ {
+ case LayoutDirection_BottomUp:
+ {
+ Result->RowYAt = Bounds.Min.y;
+ }break;
+
+ case LayoutDirection_TopDown:
+ {
+ Result->RowYAt = Bounds.Max.y - Result->RowHeight;
+ }break;
+
+ InvalidDefaultCase;
+ }
+
+ return Result;
+}
+
+static ui_widget*
+ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name)
+{
+ ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir);
+ SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result);
+ Interface->ActiveLayout = Result;
+ return Result;
+}
+
+static gs_string
+ui_PushLayoutCategoryName(ui_interface* Interface, gs_string Category, u32 Value)
+{
+ gs_string Result = PushStringF(Interface->PerFrameMemory,
+ Category.Length + 25,
+ "%S%d",
+ Category.ConstString,
+ Value);
+ return Result;
+}
+
+static ui_widget*
+ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name)
+{
+ ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir);
+
+ if (Interface->DrawOrderRoot)
+ {
+ SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Result);
+ Interface->ActiveLayout->ChildCount++;
+ }
+ else
+ {
+ SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result);
+ }
+
+ Interface->ActiveLayout = Result;
+ return Result;
+}
+static ui_widget*
+ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Category, u32 Value)
+{
+ gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Value);
+ return ui_PushLayout(Interface, Bounds, FillDir, Name);
+}
+
+static ui_widget*
+ui_PushLayout(ui_interface* Interface, gs_string Name, bool Inset = true)
+{
+ rect2 Bounds = {};
+ ui_layout_direction Direction = LayoutDirection_TopDown;
+ if (Interface->ActiveLayout)
+ {
+ Bounds = ui_ReserveBounds(Interface, Interface->ActiveLayout, Inset);
+ Direction = Interface->ActiveLayout->FillDirection;
+ }
+ else
+ {
+ Bounds.Min.x = Interface->WindowBounds.Min.x;
+ Bounds.Min.y = Interface->WindowBounds.Max.y;
+ Bounds.Max.x = Interface->WindowBounds.Max.x;
+ Bounds.Max.y = Interface->WindowBounds.Max.y;
+
+ if (Inset)
+ {
+ Bounds.Min.x += Interface->Style.Margin.x;
+ Bounds.Max.x -= Interface->Style.Margin.x;
+ }
+
+ Direction = LayoutDirection_TopDown;
+ }
+ return ui_PushLayout(Interface, Bounds, Direction, Name);
+}
+
+internal void
+ui_ExpandToFitChildren(ui_widget* Parent)
+{
+ if (!ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) { return; }
+
+ v2 Extents = { Parent->Bounds.Max.y, Parent->Bounds.Min.y };
+ for (ui_widget* Child = Parent->ChildrenRoot; Child != 0; Child = Child->Next)
+ {
+ Extents.x = Min(Extents.x, Child->Bounds.Min.y);
+ Extents.y = Max(Extents.y, Child->Bounds.Max.y);
+ }
+
+ switch(Parent->FillDirection)
+ {
+ case LayoutDirection_BottomUp:
+ {
+ Parent->Bounds.Max.y = Max(Extents.y + Parent->Margin.y, Parent->Bounds.Max.y);
+ }break;
+
+ case LayoutDirection_TopDown:
+ {
+ Parent->Bounds.Min.y = Min(Extents.x - Parent->Margin.y, Parent->Bounds.Min.y);
+ }break;
+
+ InvalidDefaultCase;
+ }
+}
+
+static void
+ui_PopLayout(ui_interface* Interface, gs_string LayoutName)
+{
+ Assert(Interface->ActiveLayout != 0);
+
+ ui_widget* Layout = Interface->ActiveLayout;
+
+ // NOTE(pjs): If this isn't true then a layout was opened without being closed
+ // Go check for ui_PushLayout, ui_BeginDropdown, ui_BeginRow, etc that don't have
+ // a corresponding ui_Pop/ui_End*
+ //
+ // We use StringsEqualUpToLength here becuase its possible that
+ // the current layout used the Category + Identifier method
+ // for generating Layout->String. And if Identifier was a string
+ // that was edited within the scope of this layout, then
+ // Layout->String and LayoutName will no longer match.
+ //
+ // This is a compromise that will at least let us know if
+ // we aren't popping all our layouts correctly.
+ Assert(StringsEqualUpToLength(Layout->String, LayoutName, LayoutName.Length));
+
+ ui_ExpandToFitChildren(Layout);
+
+ Interface->ActiveLayout = Interface->ActiveLayout->Parent;
+
+ // NOTE(pjs): This indicates that the parent layout should
+ // expand to fit the layout that we just popped
+ if (Interface->ActiveLayout != 0 &&
+ Interface->ActiveLayout->ChildrenHead == Layout)
+ {
+ ui_CommitBounds(Interface->ActiveLayout, Layout->Bounds);
+ }
+}
+static void
+ui_PopLayout(ui_interface* Interface, gs_string Category, u32 Value)
+{
+ gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Value);
+ ui_PopLayout(Interface, Name);
+}
+
+static ui_widget*
+ui_BeginRow(ui_interface* Interface, u32 ColumnsMax)
+{
+ ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false);
+ ui_WidgetCreateColumns(Layout, ColumnsMax, Interface);
+ ui_WidgetInitUniformColumns(Layout);
+ return Layout;
+}
+
+enum ui_column_size_rule
+{
+ UIColumnSize_Fixed,
+ UIColumnSize_Percent,
+ UIColumnSize_Fill,
+ UIColumnSize_MaxWidth,
+};
+
+struct ui_column_spec
+{
+ ui_column_size_rule Rule;
+ union
+ {
+ r32 Width;
+ r32 Percent;
+ };
+};
+
+static ui_widget*
+ui_BeginRow(ui_interface* Interface, u32 ColumnsMax, ui_column_spec* ColumnRules)
+{
+ ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false);
+ ui_WidgetCreateColumns(Layout, ColumnsMax, Interface);
+
+ // First Pass, determine widths of each column, and how much space is left to be divided by the fill columns
+ // If a size is specified, it is stored in Column->XMax
+ r32 RowWidth = Rect2Width(Layout->Bounds);
+ r32 RemainingSpace = RowWidth;
+ u32 FillColumnsCount = 0;
+ for (u32 i = 0; i < Layout->ColumnsCount; i++)
+ {
+ ui_column_spec Spec = ColumnRules[i];
+ ui_column* Column = Layout->Columns + i;
+
+ switch (Spec.Rule)
+ {
+ case UIColumnSize_Fixed:
+ {
+ Column->XMax = Spec.Width;
+ RemainingSpace -= Column->XMax;
+ }break;
+
+ case UIColumnSize_Percent:
+ {
+ Column->XMax = Spec.Percent * RowWidth;
+ RemainingSpace -= Column->XMax;
+ }break;
+
+ case UIColumnSize_Fill:
+ {
+ FillColumnsCount += 1;
+ }break;
+
+ case UIColumnSize_MaxWidth:
+ {
+ if (RemainingSpace >= Spec.Width)
+ {
+ Column->XMax = Spec.Width;
+ }
+ else
+ {
+ Column->XMax = RemainingSpace;
+ }
+ RemainingSpace -= Column->XMax;
+ }break;
+
+ InvalidDefaultCase;
+ }
+ }
+
+ r32 FillColumnWidth = RemainingSpace / FillColumnsCount;
+
+ // Second Pass, specify the actual XMin and XMax of each column
+ r32 ColumnStartX = Layout->Bounds.Min.x;
+ for (u32 i = 0; i < Layout->ColumnsCount; i++)
+ {
+ ui_column_spec Spec = ColumnRules[i];
+ ui_column* Column = Layout->Columns + i;
+
+ r32 ColumnWidth = 0;
+ switch (Spec.Rule)
+ {
+ case UIColumnSize_Fixed:
+ case UIColumnSize_Percent:
+ case UIColumnSize_MaxWidth:
+ {
+ ColumnWidth = Column->XMax;
+ }break;
+
+ case UIColumnSize_Fill:
+ {
+ ColumnWidth = FillColumnWidth;
+ }break;
+ }
+
+ Column->XMin = ColumnStartX ;
+ Column->XMax = Column->XMin + Max(0, ColumnWidth);
+ ColumnStartX = Column->XMax;
+ }
+
+ return Layout;
+}
+
+static void
+ui_EndRow(ui_interface* Interface)
+{
+ ui_PopLayout(Interface, MakeString("Row"));
+}
+
+static rect2
+ui_LayoutRemaining(ui_widget Layout)
+{
+ rect2 Result = Layout.Bounds;
+ Result.Max.y = Layout.RowYAt;
+ return Result;
+}
+
+// Widgets
+
+internal ui_eval_result
+ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
+{
+ ui_eval_result Result = {};
+
+ Widget->Bounds = Bounds;
+ SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Widget);
+ Interface->ActiveLayout->ChildCount += 1;
+ ui_CommitBounds(Widget->Parent, Widget->Bounds);
+
+ if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) &&
+ PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
+ {
+ if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
+ {
+ if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id))
+ {
+ Result.Clicked = true;
+ Interface->ActiveWidget = Widget->Id;
+
+ if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable))
+ {
+ Interface->CursorPosition = Widget->String.Length;
+ }
+ }
+ }
+
+ if (Interface->HotWidget.ZIndex == 0 ||
+ Interface->HotWidget.ZIndex <= Widget->Id.ZIndex)
+ {
+ Interface->HotWidget = Widget->Id;
+ Interface->HotWidgetFramesSinceUpdate = 0;
+ }
+ }
+ else
+ {
+ if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) &&
+ MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
+ {
+ Interface->ActiveWidget = {};
+ }
+ }
+
+ if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id))
+ {
+ // click & drag
+ if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState))
+ {
+ Result.Held = true;
+ Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos;
+ }
+
+ if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) &&
+ Interface->TempInputString.Length > 0)
+ {
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
+
+ Interface->CursorPosition = Clamp(0, Interface->CursorPosition, State->EditString.Length);
+ for (u32 i = 0; i < Interface->TempInputString.Length; i++)
+ {
+ if (Interface->TempInputString.Str[i] == '\b')
+ {
+ if (Interface->CursorPosition > 0)
+ {
+ State->EditString.Length -= 1;
+ }
+ }
+ else
+ {
+ OutChar(&State->EditString, Interface->TempInputString.Str[i]);
+ Interface->CursorPosition += 1;
+ }
+ }
+ }
+ }
+
+ Assert(Widget->Parent != 0);
+ return Result;
+}
+
+internal ui_eval_result
+ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget)
+{
+ ui_widget* Layout = Interface->ActiveLayout;
+ rect2 Bounds = ui_ReserveBounds(Interface, Layout, true);
+ return ui_EvaluateWidget(Interface, Widget, Bounds);
+}
+
+//
+// Drawing Functions
+//
+
+static r32
+ui_GetTextLineHeight(ui_interface Interface)
+{
+ r32 Result = Interface.Style.Font->PixelHeight + (2 * Interface.Style.Margin.y);
+ return Result;
+}
+
+static void
+ui_FillRect(ui_interface* Interface, rect2 Bounds, v4 Color)
+{
+ PushRenderQuad2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, Color);
+}
+
+static void
+ui_OutlineRect(ui_interface* Interface, rect2 Bounds, r32 Thickness, v4 Color)
+{
+ PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, Thickness, Color);
+}
+
+internal void
+ui_Label(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left)
+{
+ DEBUG_TRACK_FUNCTION;
+ ui_widget* Widget = ui_CreateWidget(Interface, String);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
+ ui_EvaluateWidget(Interface, Widget, Bounds);
+}
+
+internal void
+ui_Label(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left)
+{
+ DEBUG_TRACK_FUNCTION;
+ ui_widget* Widget = ui_CreateWidget(Interface, String);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
+ ui_EvaluateWidget(Interface, Widget);
+}
+
+internal void
+ui_TextEntrySetFlags(ui_widget* Widget, gs_string EditString)
+{
+ Widget->String = EditString;
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Selectable);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Typable);
+}
+
+internal bool
+ui_TextEntry(ui_interface* Interface, gs_string Identifier, gs_string* Value)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, Identifier);
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
+ if (!State)
+ {
+ State = ui_CreateRetainedState(Interface, Widget);
+ }
+ PrintF(&State->EditString, "%S", *Value);
+
+ ui_TextEntrySetFlags(Widget, State->EditString);
+
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
+ bool StringEdited = !StringsEqual(*Value, State->EditString);
+ PrintF(Value, "%S", State->EditString);
+
+ return StringEdited;
+}
+
+internal u64
+ui_TextEntryU64(ui_interface* Interface, gs_string String, u64 CurrentValue)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, String);
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
+ if (!State)
+ {
+ State = ui_CreateRetainedState(Interface, Widget);
+ PrintF(&State->EditString, "%u", CurrentValue);
+ }
+ ui_TextEntrySetFlags(Widget, State->EditString);
+
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
+ parse_uint_result ParseResult = ValidateAndParseUInt(State->EditString.ConstString);
+ u64 ValueResult = CurrentValue;
+ if (ParseResult.Success)
+ {
+ ValueResult = ParseResult.Value;
+ }
+ return ValueResult;
+}
+
+internal r64
+ui_TextEntryR64(ui_interface* Interface, gs_string String, r64 CurrentValue)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, String);
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
+ if (!State)
+ {
+ State = ui_CreateRetainedState(Interface, Widget);
+ PrintF(&State->EditString, "%f", CurrentValue);
+ }
+ ui_TextEntrySetFlags(Widget, State->EditString);
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
+ parse_float_result ParseResult = ValidateAndParseFloat(State->EditString.ConstString);
+ r64 ValueResult = CurrentValue;
+ if (ParseResult.Success)
+ {
+ ValueResult = ParseResult.Value;
+ }
+ return ValueResult;
+}
+
+internal ui_widget*
+ui_CreateButtonWidget(ui_interface* Interface, gs_string Text)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, Text);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
+ return Widget;
+}
+
+internal b32
+ui_Button(ui_interface* Interface, gs_string Text)
+{
+ ui_widget* Widget = ui_CreateButtonWidget(Interface, Text);
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
+ return Result.Clicked;
+}
+
+internal b32
+ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds)
+{
+ ui_widget* Widget = ui_CreateButtonWidget(Interface, Text);
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
+ return Result.Clicked;
+}
+
+struct list_item_colors
+{
+ v4 Hover;
+ v4 Selected;
+ v4 BGColor;
+};
+
+inline v4
+ui_GetListItemBGColor(interface_config Style, u32 ElementIndex)
+{
+ v4 Result = Style.ListBGColors[ElementIndex % LIST_BG_COLORS_COUNT];
+ return Result;
+}
+
+static list_item_colors
+ui_GetListItemColors(ui_interface* Interface, u32 ListItemIndex)
+{
+ list_item_colors Result = {};
+ Result.Hover = Interface->Style.ListBGHover;
+ Result.Selected = Interface->Style.ListBGSelected;
+ Result.BGColor = ui_GetListItemBGColor(Interface->Style, ListItemIndex);
+ return Result;
+}
+
+static b32
+ui_ListButton(ui_interface* Interface, gs_string Text, rect2 Bounds, u32 ListItemIndex)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, Text);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
+ // TODO(pjs): Reimplement alternating color backgrounds
+ Widget->Bounds = Bounds;
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
+ return Result.Clicked;
+}
+
+static b32
+ui_LayoutListButton(ui_interface* Interface, gs_string Text, u32 ListItemIndex)
+{
+ // TODO(pjs): Reimplement alternating colors
+ return ui_Button(Interface, Text);
+}
+
+internal bool
+ui_EvaluateDropdown(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult)
+{
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
+ if (!State)
+ {
+ State = ui_CreateRetainedState(Interface, Widget);
+ }
+
+ if (EvalResult.Clicked)
+ {
+ State->Value = !State->Value;
+ }
+
+ if (State->Value)
+ {
+ ui_widget ParentLayout = *Interface->ActiveLayout;
+
+ r32 SpaceAbove = Interface->WindowBounds.Max.y - Widget->Bounds.Max.y;
+ r32 SpaceBelow = Widget->Bounds.Min.y - Interface->WindowBounds.Min.y;
+ ui_layout_direction Direction = LayoutDirection_TopDown;
+ rect2 MenuBounds = {};
+
+ if (SpaceAbove > SpaceBelow)
+ {
+ r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y;
+ Direction = LayoutDirection_BottomUp;
+ MenuBounds = rect2{
+ v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, Widget->Bounds.Max.y },
+ v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, ParentLayoutMaxY }
+ };
+ }
+ else
+ {
+ r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y;
+ Direction = LayoutDirection_TopDown;
+ MenuBounds = rect2{
+ v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, ParentLayoutMinY },
+ v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, Widget->Bounds.Min.y }
+ };
+ }
+
+ ui_widget* Layout = ui_PushOverlayLayout(Interface, MenuBounds, Direction, MakeString("DropdownLayout"));
+ Layout->Margin.y = 0;
+ Layout->WidgetReference = Widget->Id;
+ ui_WidgetClearFlag(Layout, UIWidgetFlag_DrawOutline);
+ ui_WidgetSetChildrenPopover(Layout);
+ }
+
+ return State->Value;
+}
+
+internal bool
+ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, Text);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
+
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
+ return ui_EvaluateDropdown(Interface, Widget, Result);
+}
+
+internal bool
+ui_BeginDropdown(ui_interface* Interface, gs_string Text)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, Text);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
+ return ui_EvaluateDropdown(Interface, Widget, Result);
+}
+
+internal void
+ui_EndDropdown(ui_interface* Interface)
+{
+ gs_string LayoutName = MakeString("DropdownLayout");
+ ui_widget* Layout = Interface->ActiveLayout;
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->WidgetReference);
+ if (State)
+ {
+ bool IsOpen = State->Value;
+ ui_widget* Widget = Interface->ActiveLayout;
+ bool IsStillHot = StringsEqualUpToLength(Widget->String, LayoutName, LayoutName.Length);
+ if (IsOpen && IsStillHot)
+ {
+ ui_PopLayout(Interface, LayoutName);
+ } else if (IsOpen && !IsStillHot)
+ {
+ State->Value = false;
+ }
+ }
+}
+
+internal r32
+ui_EvaluateRangeSlider(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult, r32 Value, r32 MinValue, r32 MaxValue)
+{
+ r32 NewValue = Value;
+ ui_widget_retained_state* State = ui_GetOrCreateRetainedState(Interface, Widget);
+
+ if (EvalResult.Clicked)
+ {
+ State->InitialValueR32 = Value;
+ }
+
+ if (EvalResult.Held)
+ {
+ r32 Percent = (Interface->Mouse.Pos.x - Widget->Bounds.Min.x) / Rect2Width(Widget->Bounds);
+ NewValue = LerpR32(Percent, MinValue, MaxValue);
+ }
+
+ NewValue = Clamp(MinValue, NewValue, MaxValue);
+ Widget->FillPercent = RemapR32(NewValue, MinValue, MaxValue, 0, 1);
+ return NewValue;
+}
+
+internal ui_widget*
+ui_CreateRangeSliderWidget(ui_interface* Interface, gs_string Text, r32 Value)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, Text);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
+ Widget->String = PushStringF(Interface->PerFrameMemory, 128, "%f", Value);
+ InterfaceAssert(Interface->PerFrameMemory);
+ return Widget;
+}
+
+internal r32
+ui_RangeSlider(ui_interface* Interface, gs_string Text, r32 Value, r32 ValueMin, r32 ValueMax)
+{
+ ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value);
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
+ return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax);
+
+}
+
+internal r32
+ui_RangeSlider(ui_interface* Interface, gs_string Text, rect2 Bounds, r32 Value, r32 ValueMin, r32 ValueMax)
+{
+ ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value);
+ ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
+ return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax);
+}
+
+internal bool
+ui_Toggle(ui_interface* Interface, gs_string Text, bool Value)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, Text);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
+ ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget);
+
+ bool Result = Eval.Clicked ? !Value : Value;
+ Widget->FillPercent = Result ? 1.0f : 0.0f;
+ return Result;
+}
+
+internal bool
+ui_ToggleText(ui_interface* Interface, gs_string Text, bool Value)
+{
+ ui_widget* Widget = ui_CreateWidget(Interface, Text);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill);
+ ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
+ ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget);
+
+ bool Result = Eval.Clicked ? !Value : Value;
+ Widget->FillPercent = Result ? 1.0f : 0.0f;
+ return Result;
+}
+
+internal void
+ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 ElementCount)
+{
+ if (ElementCount < ViewportRows)
+ {
+ ViewportRows = ElementCount;
+ }
+
+ ui_column_spec ColumnRules[] = {
+ { UIColumnSize_Fixed, 32 },
+ { UIColumnSize_Fill, 0 },
+ };
+ ui_widget* Layout = ui_BeginRow(Interface, 2, ColumnRules);
+ ui_WidgetClearFlag(Layout, UIWidgetFlag_ExpandsToFitChildren);
+
+ ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->Id);
+ if (!State)
+ {
+ State = ui_CreateRetainedState(Interface, Layout);
+ State->InitialValueR32 = 1.0f;
+ }
+
+ r32 LayoutHeight = Layout->RowHeight * ViewportRows;
+ switch (Layout->Parent->FillDirection)
+ {
+ case LayoutDirection_TopDown:
+ {
+ Layout->Bounds.Min.y = Layout->Bounds.Max.y - LayoutHeight;
+ }break;
+
+ case LayoutDirection_BottomUp:
+ {
+ Layout->Bounds.Max.y = Layout->Bounds.Min.y + LayoutHeight;
+ }break;
+
+ InvalidDefaultCase;
+ }
+
+ // Create the scroll bar
+ //
+ // TODO(pjs): Maybe make this a vertical slider widget?
+
+ ui_widget* SliderRegion = ui_CreateWidget(Interface, MakeString("Slider"));
+ ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawOutline);
+ ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawVerticalFill);
+ ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawFillAsHandle);
+
+ ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_Clickable);
+
+ rect2 SliderBounds = ui_ReserveBounds(Interface, Layout, true);
+ SliderBounds.Min.y = Layout->Bounds.Min.y + Layout->Margin.y;
+ SliderBounds.Max.y = Layout->Bounds.Max.y - Layout->Margin.y;
+
+ ui_eval_result SliderEval = ui_EvaluateWidget(Interface, SliderRegion, SliderBounds);
+ if (SliderEval.Clicked || SliderEval.Held)
+ {
+ r32 Percent = (Interface->Mouse.Pos.y - SliderRegion->Bounds.Min.y) / Rect2Height(SliderRegion->Bounds);
+ State->InitialValueR32 = Clamp01(Percent);
+ }
+ SliderRegion->FillPercent = State->InitialValueR32;
+
+ // Create the viewport that offsets list contents (and at render time determines what is visible)
+ //
+ ui_widget* ViewportLayout = ui_PushLayout(Interface, MakeString("Contents"));
+ ui_WidgetSetFlag(ViewportLayout, UIWidgetFlag_DrawOutline);
+ ui_WidgetClearFlag(ViewportLayout, UIWidgetFlag_ExpandsToFitChildren);
+ ViewportLayout->FillDirection = LayoutDirection_TopDown;
+
+ ViewportLayout->Bounds.Min.y = SliderBounds.Min.y;
+ ViewportLayout->Bounds.Max.y = SliderBounds.Max.y;
+
+ s32 ScrollableElements = Max(0, ElementCount - ViewportRows);
+ ui_widget_retained_state* ViewportState = ui_GetOrCreateRetainedState(Interface, ViewportLayout);
+ ViewportState->ChildrenDrawOffset.x = 0;
+ r32 BaseOffset = 0;
+ r32 ScrollPct = 1.0 - State->InitialValueR32;
+ r32 RowsOffset = ScrollPct * ScrollableElements;
+ r32 ScrollOffset = (ViewportLayout->RowHeight - (Interface->Style.Margin.y)) * RowsOffset;
+ ViewportState->ChildrenDrawOffset.y = BaseOffset + ScrollOffset;
+}
+
+internal void
+ui_EndList(ui_interface* Interface)
+{
+ // Pop the Viewport Layout
+ ui_PopLayout(Interface, MakeString("Contents"));
+ // Pop the actual list layout
+ ui_EndRow(Interface);
+}
+
+internal void
+ui_BeginMousePopup(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Text)
+{
+ rect2 FollowMouseBounds = Rect2Translate(Bounds, Interface->Mouse.Pos);
+ ui_widget* Layout = ui_PushOverlayLayout(Interface, FollowMouseBounds, FillDir, MakeString("MousePopup"));
+ ui_WidgetSetFlag(Layout, UIWidgetFlag_DrawBackground);
+}
+
+internal void
+ui_EndMousePopup(ui_interface* Interface)
+{
+ ui_PopLayout(Interface, MakeString("MousePopup"));
+}
+
+//
+internal void
+ui_BeginLabelRow(ui_interface* Interface, gs_string Label, u32 Count = 2)
+{
+ ui_BeginRow(Interface, Count);
+ ui_Label(Interface, Label);
+}
+
+internal bool
+ui_LabeledToggle(ui_interface* Interface, gs_string Label, bool Value)
+{
+ ui_BeginLabelRow(Interface, Label);
+ bool Result = ui_Toggle(Interface, Label, Value);
+ ui_EndRow(Interface);
+ return Result;
+}
+
+internal r32
+ui_LabeledRangeSlider(ui_interface* Interface, gs_string Label, r32 Value, r32 ValueMin, r32 ValueMax)
+{
+ ui_BeginLabelRow(Interface, Label);
+ r32 Result = ui_RangeSlider(Interface, Label, Value, ValueMin, ValueMax);
+ ui_EndRow(Interface);
+ return Result;
+}
+
+internal void
+ui_LabeledTextEntry(ui_interface* Interface, gs_string Label, gs_string* Value)
+{
+ ui_BeginLabelRow(Interface, Label);
+ ui_TextEntry(Interface, Label, Value);
+ ui_EndRow(Interface);
+}
+
+
+internal u64
+ui_LabeledTextEntryU64(ui_interface* Interface, gs_string Label, u32 Value)
+{
+ ui_BeginLabelRow(Interface, Label);
+ u64 Result = ui_TextEntryU64(Interface, Label, Value);
+ ui_EndRow(Interface);
+ return Result;
+}
+
+internal bool
+ui_BeginLabeledDropdown(ui_interface* Interface, gs_string Label, gs_string DropdownValue)
+{
+ ui_BeginLabelRow(Interface, Label);
+ return ui_BeginDropdown(Interface, DropdownValue);
+}
+
+internal void
+ui_EndLabeledDropdown(ui_interface* Interface)
+{
+ ui_EndDropdown(Interface);
+ ui_EndRow(Interface);
+}
+
+internal ui_interface
+ui_InterfaceCreate(context Context, interface_config Style, gs_memory_arena* Permanent)
+{
+ ui_interface Result = {0};
+ Result.Style = Style;
+
+ gs_file FontFile = ReadEntireFile(Context.ThreadContext.FileHandler, ConstString("data/Anonymous Pro.ttf"));
+ Result.Style.Font = PushStruct(Permanent, bitmap_font);
+ *Result.Style.Font = TextFont_Create(FontFile, 512, Style.FontSize, Context, Permanent);
+
+ Result.Style.RowHeight = ui_GetTextLineHeight(Result) + (2 * Result.Style.Margin.y);
+
+ Result.WidgetsCountMax = 4096;
+ Result.Widgets = PushArray(Permanent, ui_widget, Result.WidgetsCountMax);
+ Result.PerFrameMemory = PushStruct(Permanent, gs_memory_arena);
+ *Result.PerFrameMemory = MemoryArenaCreate(KB(32), Bytes(8), Context.ThreadContext.Allocator, 0, 0, "Interface Per Frame Memory Arena");
+ InterfaceAssert(Result.PerFrameMemory);
+
+ Result.Permanent = Permanent;
+
+ return Result;
+}
+
+#define INTERFACE_H
+#endif // INTERFACE_H
\ No newline at end of file
diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h
index 92ee12c..822cf58 100644
--- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h
+++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h
@@ -13,9 +13,18 @@ struct animation_timeline_state
{
frame_range VisibleRange;
handle SelectedBlockHandle;
- u32 SelectedAnimationLayer;
+ animation_handle EditingAnimationHandle;
+ s32 SelectedAnimationLayer;
+
+ animation_handle NextActiveAnim;
};
+internal void
+AnimationTimeline_SelectLayer(animation_timeline_state* TLState, s32 Layer)
+{
+ TLState->SelectedAnimationLayer = Layer;
+}
+
inline u32
GetFrameFromPointInAnimationPanel(v2 Point, rect2 PanelBounds, frame_range VisibleRange)
{
@@ -33,22 +42,13 @@ GetXPositionFromFrameInAnimationPanel (u32 Frame, rect2 PanelBounds, frame_range
return XPositionAtFrame;
}
-internal handle
-AddAnimationBlockAtCurrentTime (animation_pattern_handle AnimationProcHandle, u32 LayerHandle, animation_system* System)
-{
- u32 NewBlockStart = System->CurrentFrame;
- u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System);
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
- handle AnimHandle = Animation_AddBlock(ActiveAnim, NewBlockStart, NewBlockEnd, AnimationProcHandle, LayerHandle);
- return AnimHandle;
-}
-
FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand)
{
animation_timeline_state* PanelState = Panel_GetStateStruct(Panel, animation_timeline_state);
handle SelectedBlockHandle = PanelState->SelectedBlockHandle;
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
+ animation* ActiveAnim = AnimationArray_GetSafe(State->AnimationSystem.Animations, PanelState->EditingAnimationHandle);
+
if(SelectedBlockHandle.Index < ActiveAnim->Blocks_.Count &&
ActiveAnim->Blocks_.Generations[SelectedBlockHandle.Index] == SelectedBlockHandle.Generation)
{
@@ -112,6 +112,7 @@ StartDragTimeMarker(rect2 TimelineBounds, frame_range VisibleFrames, app_state*
OPERATION_STATE_DEF(drag_animation_block_state)
{
rect2 TimelineBounds;
+ animation_handle EditingAnimationHandle;
handle BlockHandle;
frame_range VisibleRange;
frame_range ClipRange;
@@ -133,7 +134,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationBlock)
{
drag_animation_block_state* OpState = (drag_animation_block_state*)Operation.OpStateMemory;
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
+ animation_array Animations = State->AnimationSystem.Animations;
+ animation_handle Handle = OpState->EditingAnimationHandle;
+ animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
r32 ClipInitialStartFrameXPercent = FrameToPercentRange(OpState->ClipRange.Min, OpState->VisibleRange);
u32 ClipInitialStartFrameXPosition = LerpR32(ClipInitialStartFrameXPercent,
@@ -237,7 +240,10 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle
{
TimelineState->SelectedBlockHandle = BlockHandle;
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
+ animation_handle Handle = TimelineState->EditingAnimationHandle;
+ animation_array Animations = State->AnimationSystem.Animations;
+ animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
+
operation_mode* DragAnimationBlockMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationBlockCommands, UpdateDragAnimationBlock);
animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle);
@@ -245,29 +251,45 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle
&State->Modes,
drag_animation_block_state);
OpState->TimelineBounds = TimelineBounds;
+ OpState->EditingAnimationHandle = Handle;
OpState->BlockHandle = BlockHandle;
OpState->VisibleRange = VisibleRange;
OpState->ClipRange = SelectedBlock->Range;
}
// -------------------
-FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand)
+internal void
+AnimationTimeline_AddAnimationBlockCommand(animation_timeline_state* TimelineState, app_state* State, context Context)
{
- animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state);
+ animation_handle Handle = TimelineState->EditingAnimationHandle;
+ animation_array Animations = State->AnimationSystem.Animations;
+ animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
-
- frame_range Range = ActiveAnim->PlayableRange;
- u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, Panel->Bounds, Range);
-
- animation_pattern_handle PatternHandle = Patterns_IndexToHandle(4);
- handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), PatternHandle, TimelineState->SelectedAnimationLayer);
- TimelineState->SelectedBlockHandle = NewBlockHandle;
+ s32 StartFrame = State->AnimationSystem.CurrentFrame;
+ s32 EndFrame = StartFrame + SecondsToFrames(3, State->AnimationSystem);
+ EndFrame = Clamp(0, EndFrame, ActiveAnim->PlayableRange.Max);
+ if ((EndFrame - StartFrame) > 0)
+ {
+ animation_pattern_handle PatternHandle = Patterns_IndexToHandle(0);
+ s32 Layer = TimelineState->SelectedAnimationLayer;
+ if (Layer < 0)
+ {
+ Layer = Animation_AddLayer(ActiveAnim, MakeString("[New Layer]"), BlendMode_Add, &State->AnimationSystem);
+ AnimationTimeline_SelectLayer(TimelineState, Layer);
+ }
+ Assert(Layer >= 0);
+
+ handle NewBlockHandle = Animation_AddBlock(ActiveAnim, StartFrame, EndFrame, PatternHandle, Layer);
+
+ TimelineState->SelectedBlockHandle = NewBlockHandle;
+ } else {
+ // TODO(pjs): we don't want to create a block of zero frames
+ // since you won't be able to delete it. What do we do here??
+ }
}
input_command AnimationTimeline_Commands[] = {
{ KeyCode_X, KeyCode_Invalid, Command_Began, DeleteAnimationBlockCommand },
- { KeyCode_A, KeyCode_Invalid, Command_Began, AddAnimationBlockCommand },
};
s32 AnimationTimeline_CommandsCount = 2;
@@ -276,10 +298,17 @@ GSMetaTag(panel_type_animation_timeline);
internal void
AnimationTimeline_Init(panel* Panel, app_state* State, context Context)
{
+ animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From;
+
// TODO: :FreePanelMemory
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state);
- TimelineState->VisibleRange = ActiveAnim->PlayableRange;
+ TimelineState->EditingAnimationHandle = Handle;
+
+ if (IsValid(Handle)) {
+ animation_array Animations = State->AnimationSystem.Animations;
+ animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
+ TimelineState->VisibleRange = ActiveAnim->PlayableRange;
+ }
Panel->StateMemory = StructToData(TimelineState, animation_timeline_state);
}
@@ -320,7 +349,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames);
r32 FrameX = LerpR32(FramePercent, BarBounds.Min.x, BarBounds.Max.x);
v2 FrameTextPos = v2{FrameX, BarBounds.Min.y + 2};
- DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, FrameTextPos, WhiteV4);
+ DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, FrameTextPos, WhiteV4, -1, GreenV4);
}
// Time Slider
@@ -337,7 +366,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
v2 HeadMin = v2{SliderX - SliderHalfWidth, BarBounds.Min.y};
v2 HeadMax = v2{SliderX + SliderHalfWidth, BarBounds.Max.y};
PushRenderQuad2D(Interface.RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
- DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, HeadMin + v2{6, 4}, WhiteV4);
+ DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, HeadMin + v2{6, 4}, WhiteV4, -1, GreenV4);
}
}
@@ -450,123 +479,10 @@ DrawLayerMenu(animation_system* AnimationSystem, animation ActiveAnim, ui_interf
{
PushRenderBoundingBox2D(Interface.RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4);
}
- DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4);
+ DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4, -1, GreenV4);
}
}
-internal rect2
-DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, frame_range VisibleFrames, rect2 TimelineBounds, render_command_buffer* RenderBuffer)
-{
- rect2 BlockBounds = {};
-
- r32 TimelineWidth = Rect2Width(TimelineBounds);
-
- u32 ClampedBlockStartFrame = ClampFrameToRange(AnimationBlock.Range.Min, VisibleFrames);
- r32 StartFramePercent = FrameToPercentRange(ClampedBlockStartFrame, VisibleFrames);
- r32 StartPosition = TimelineWidth * StartFramePercent;
-
- u32 ClampedBlockEndFrame = ClampFrameToRange(AnimationBlock.Range.Max, VisibleFrames);
- r32 EndFramePercent = FrameToPercentRange(ClampedBlockEndFrame, VisibleFrames);
- r32 EndPosition = TimelineWidth * EndFramePercent;
-
- r32 LayerYOffset = LAYER_HEIGHT * AnimationBlock.Layer;
- BlockBounds.Min = TimelineBounds.Min + v2{StartPosition, LayerYOffset};
- BlockBounds.Max = TimelineBounds.Min + v2{EndPosition, LayerYOffset + LAYER_HEIGHT};
-
- PushRenderQuad2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, BlockColor);
- PushRenderBoundingBox2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, 1, WhiteV4);
-
- // TODO(pjs): If mouse is on one of the border hot spots, render an off colored square to signal the region is hot
-
- return BlockBounds;
-}
-
-internal handle
-DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_state* TimelineState, rect2 PanelBounds, handle SelectedBlockHandle, ui_interface* Interface, app_state* State)
-{
- gs_string Tempgs_string = PushString(State->Transient, 256);
- handle Result = SelectedBlockHandle;
-
- animation CurrAnimation = *AnimationSystem_GetActiveAnimation(AnimationSystem);
-
- rect2 LayerMenuBounds, TimelineBounds;
- RectVSplitAtDistanceFromLeft(PanelBounds, 256, &LayerMenuBounds, &TimelineBounds);
-
- // In Top To Bottom Order
- rect2 TimelineFrameBarBounds;
- rect2 TimelineBlockDisplayBounds;
- rect2 TimelineRangeBarBounds;
- RectHSplitAtDistanceFromTop(TimelineBounds, 32, &TimelineFrameBarBounds, &TimelineBounds);
- RectHSplitAtDistanceFromBottom(TimelineBounds, 24, &TimelineBlockDisplayBounds, &TimelineRangeBarBounds);
-
- DrawLayerMenu(AnimationSystem, CurrAnimation, *Interface, LayerMenuBounds, &TimelineState->SelectedAnimationLayer);
-
- frame_range AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, CurrAnimation, TimelineState, *Interface, TimelineRangeBarBounds);
-
- DrawFrameBar(AnimationSystem, *Interface, AdjustedViewRange, TimelineFrameBarBounds, State);
-
- ui_FillRect(Interface, TimelineBlockDisplayBounds, v4{.25f, .25f, .25f, 1.0f});
-
- // Animation Blocks
- b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState);
- handle DragBlockHandle = {0};
- for (u32 i = 0; i < CurrAnimation.Blocks_.Count; i++)
- {
- animation_block* AnimationBlockAt = CurrAnimation.Blocks_.Values + i;
-
- // If either end is in the range, we should draw it
- b32 RangeIsVisible = (FrameIsInRange(AdjustedViewRange, AnimationBlockAt->Range.Min) ||
- FrameIsInRange(AdjustedViewRange, AnimationBlockAt->Range.Max));
- // If neither end is in the range, but the ends surround the visible range,
- // we should still draw it.
- RangeIsVisible |= (AnimationBlockAt->Range.Min <= AdjustedViewRange.Min &&
- AnimationBlockAt->Range.Max>= AdjustedViewRange.Max);
- if (RangeIsVisible)
- {
- v4 BlockColor = BlackV4;
- if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == CurrAnimation.Blocks_.Generations[i])
- {
- BlockColor = PinkV4;
- }
- rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, AdjustedViewRange, TimelineBounds, Interface->RenderBuffer);
- if (PointIsInRect(BlockBounds, Interface->Mouse.Pos))
- {
- DragBlockHandle.Index = i;
- DragBlockHandle.Generation = CurrAnimation.Blocks_.Generations[i];
- }
- }
- }
-
- if (MouseDownAndNotHandled && Handle_IsValid(DragBlockHandle))
- {
- MouseDownAndNotHandled = false;
- SelectAndBeginDragAnimationBlock(TimelineState, DragBlockHandle, AdjustedViewRange, TimelineBounds, State);
- }
-
- // Time Slider
- if (FrameIsInRange(AdjustedViewRange, AnimationSystem->CurrentFrame))
- {
- r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange);
- r32 SliderX = LerpR32(FrameAtPercentVisibleRange, TimelineBounds.Min.x, TimelineBounds.Max.x);
- rect2 SliderBounds = {
- v2{ SliderX, TimelineBounds.Min.y },
- v2{ SliderX + 1, TimelineBounds.Max.y }
- };
- ui_FillRect(Interface, SliderBounds, TimeSliderColor);
- }
-
- ui_OutlineRect(Interface, TimelineRangeBarBounds, 1.f, RedV4);
- ui_OutlineRect(Interface, TimelineFrameBarBounds, 1.f, RedV4);
- ui_OutlineRect(Interface, TimelineBlockDisplayBounds, 1.f, RedV4);
-
- if (MouseDownAndNotHandled && PointIsInRect(TimelineBounds, Interface->Mouse.Pos))
- {
- TimelineState->SelectedBlockHandle = {0};
- }
-
- return Result;
-}
-
PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback)
{
Assert(ReturningFrom->TypeIndex == PanelType_FileView);
@@ -575,37 +491,19 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback)
if (FileInfo.Path.Length > 0)
{
- gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FileInfo.Path);
-
- gs_string AnimFileString = MakeString((char*)AnimFile.Data.Memory, AnimFile.Data.Size);
- animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, State->Patterns);
- NewAnim.FileInfo = AnimFile.FileInfo;
-
- u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim);
- State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex;
+ animation_handle NewAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem,
+ State->Patterns,
+ Context,
+ FileInfo.Path);
+ State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle;
}
}
-internal void
-DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem, animation_pattern_array Patterns)
-{
- ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("AnimClips Layout"));
- for (u32 i = 0; i < Patterns.Count; i++)
- {
- animation_pattern Pattern = Patterns.Values[i];
- gs_string PatternName = MakeString(Pattern.Name, Pattern.NameLength);
- if (ui_Button(Interface, PatternName))
- {
- animation_pattern_handle PatternHandle = Patterns_IndexToHandle(i);
- AddAnimationBlockAtCurrentTime(PatternHandle, SelectedAnimationLayerHandle, AnimationSystem);
- }
- }
- ui_PopLayout(Interface, MakeString("AnimClips Layout"));
-}
-
internal void
PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
+ DEBUG_TRACK_FUNCTION;
+
animation_system* AnimSystem = &State->AnimationSystem;
ui_interface* Interface = &State->Interface;
ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("PlayBar Layout"));
@@ -634,14 +532,20 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan
}
internal void
-FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
+FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
+ DEBUG_TRACK_FUNCTION;
+
ui_interface* Interface = &State->Interface;
gs_string TempString = PushString(State->Transient, 256);
+
// :FrameRange
// frame_range VisibleFrames = TimelineState->VisibleRange;
- animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
- frame_range VisibleFrames = ActiveAnim.PlayableRange;
+
+ frame_range VisibleFrames = {};
+ if (ActiveAnim) {
+ VisibleFrames = ActiveAnim->PlayableRange;
+ }
s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min;
@@ -657,7 +561,7 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_
r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames);
r32 FrameX = LerpR32(FramePercent, Bounds.Min.x, Bounds.Max.x);
v2 FrameTextPos = v2{FrameX, Bounds.Min.y + 2};
- DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, FrameTextPos, WhiteV4);
+ DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, FrameTextPos, WhiteV4, -1, GreenV4);
}
// Time Slider
@@ -675,7 +579,7 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_
v2 HeadMin = v2{SliderX - SliderHalfWidth, Bounds.Min.y};
v2 HeadMax = v2{SliderX + SliderHalfWidth, Bounds.Max.y};
PushRenderQuad2D(Interface->RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
- DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, HeadMin + v2{6, 4}, WhiteV4);
+ DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, HeadMin + v2{6, 4}, WhiteV4, -1, GreenV4);
}
// Interaction
@@ -686,11 +590,33 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_
}
}
-internal void
-LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
+internal bool
+LayerList_DrawLayerButton (ui_interface* Interface, gs_string Name, rect2 Bounds, bool Selected)
{
+ bool Result = ui_MouseClickedRect(*Interface, Bounds);
+ v2 TextPos = { Bounds.Min.x + 6, Bounds.Max.y - 16};
+
+ v4 BoxColor = WhiteV4;
+ bool DrawBox = Selected;
+ if (PointIsInRect(Bounds, Interface->Mouse.Pos))
+ {
+ DrawBox = true;
+ BoxColor = PinkV4;
+ }
+
+ if (DrawBox)
+ {
+ PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, 1, BoxColor);
+ }
+ DrawString(Interface->RenderBuffer, Name, Interface->Style.Font, TextPos, WhiteV4, -1, GreenV4);
+ return Result;
+}
+
+internal void
+LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
+{
+ DEBUG_TRACK_FUNCTION;
ui_interface* Interface = &State->Interface;
- animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
ui_FillRect(Interface, Bounds, Interface->Style.PanelBG);
@@ -698,38 +624,84 @@ LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* P
rect2 LayerBounds = {0};
LayerBounds.Min = Bounds.Min;
LayerBounds.Max = LayerBounds.Min + LayerDim;
- for (u32 i = 0; i < ActiveAnim.Layers.Count; i++)
+
+ if (ActiveAnim)
{
- anim_layer* Layer = ActiveAnim.Layers.Values + i;
-
- if (ui_MouseClickedRect(*Interface, LayerBounds))
+ v2 LayerTextPos = {};
+ for (s32 i = 0; i < (s32)ActiveAnim->Layers.Count; i++)
{
- TimelineState->SelectedAnimationLayer = i;
+ anim_layer* Layer = ActiveAnim->Layers.Values + i;
+
+ bool Selected = (TimelineState->SelectedAnimationLayer == i);
+ if (LayerList_DrawLayerButton(Interface, Layer->Name, LayerBounds, Selected))
+ {
+ TimelineState->SelectedAnimationLayer = i;
+ }
+ LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y);
}
- v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16};
- if (TimelineState->SelectedAnimationLayer == i)
+ if (LayerList_DrawLayerButton(Interface, MakeString("+ Add Layer"), LayerBounds, false))
{
- PushRenderBoundingBox2D(Interface->RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4);
+ u32 NewLayer = Animation_AddLayer(ActiveAnim, MakeString("[New Layer]"), BlendMode_Add, &State->AnimationSystem);
}
- DrawString(Interface->RenderBuffer, Layer->Name, Interface->Style.Font, LayerTextPos, WhiteV4);
-
- LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y);
}
}
internal void
-TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
+TimeRange_RenderBlock (rect2 BlockBounds, u32 BlockIndex, animation* ActiveAnim, handle SelectedBlockHandle, ui_interface* Interface, render_command_buffer* RenderBuffer)
{
+ v4 BlockColor = BlackV4;
+ if (SelectedBlockHandle.Index == BlockIndex && SelectedBlockHandle.Generation == ActiveAnim->Blocks_.Generations[BlockIndex])
+ {
+ BlockColor = TealV4;
+ }
+
+ PushRenderQuad2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, BlockColor);
+ PushRenderBoundingBox2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, 1, WhiteV4);
+}
+
+struct block_bounds_lut_entry
+{
+ rect2 Bounds;
+ u32 Index;
+};
+
+internal void
+TimeRange_RenderBlockArray(block_bounds_lut_entry* Blocks, u32* LUT, u32 LUTCount, r32 HeightOffset, animation* ActiveAnim, handle SelectedBlockHandle, handle* DragBlockHandle, ui_interface* Interface, render_command_buffer* RenderBuffer)
+{
+ for (u32 i = 0; i < LUTCount; i++)
+ {
+ u32 BlockBoundsIndex = LUT[i];
+ block_bounds_lut_entry Block = Blocks[BlockBoundsIndex];
+ Block.Bounds.Max.y += HeightOffset;
+
+ TimeRange_RenderBlock(Block.Bounds, Block.Index, ActiveAnim, SelectedBlockHandle, Interface, RenderBuffer);
+
+ if (PointIsInRect(Block.Bounds, Interface->Mouse.Pos))
+ {
+ DragBlockHandle->Index = Block.Index;
+ DragBlockHandle->Generation = ActiveAnim->Blocks_.Generations[Block.Index];
+ }
+ }
+}
+
+internal void
+TimeRange_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
+{
+ DEBUG_TRACK_FUNCTION;
+
ui_interface* Interface = &State->Interface;
- animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
// TODO(pjs): setting the timeline to show the entire range
// of the current animation until I reimplement the range
// slider bars
// :FrameRange
// frame_range ViewRange = TimelineState->VisibleRange;
- frame_range ViewRange = ActiveAnim.PlayableRange;
+ frame_range ViewRange = {};
+ if (ActiveAnim)
+ {
+ ViewRange = ActiveAnim->PlayableRange;
+ }
handle SelectedBlockHandle = TimelineState->SelectedBlockHandle;
s32 CurrentFrame = State->AnimationSystem.CurrentFrame;
@@ -737,31 +709,110 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c
// Animation Blocks
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState);
handle DragBlockHandle = {0};
- for (u32 i = 0; i < ActiveAnim.Blocks_.Count; i++)
+ if (ActiveAnim)
{
- animation_block* AnimationBlockAt = ActiveAnim.Blocks_.Values + i;
+ u32 BlocksCountMax = ActiveAnim->Blocks_.Count;
+ u32 BlocksCount = 0;
+ block_bounds_lut_entry* Blocks = PushArray(State->Transient, block_bounds_lut_entry, BlocksCountMax);
- // If either end is in the range, we should draw it
- b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) ||
- FrameIsInRange(ViewRange, AnimationBlockAt->Range.Max));
- // If neither end is in the range, but the ends surround the visible range,
- // we should still draw it.
- RangeIsVisible |= (AnimationBlockAt->Range.Min <= ViewRange.Min &&
- AnimationBlockAt->Range.Max>= ViewRange.Max);
- if (RangeIsVisible)
+ u32 FrontBlocksCount = 0;
+ u32* FrontBlocks = PushArray(State->Transient, u32, BlocksCountMax);
+ u32 BackBlocksCount = 0;
+ u32* BackBlocks = PushArray(State->Transient, u32, BlocksCountMax);
+
+ for (u32 l = 0; l < ActiveAnim->Layers.Count; l++)
{
- v4 BlockColor = BlackV4;
- if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim.Blocks_.Generations[i])
- {
- BlockColor = PinkV4;
- }
- rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, ViewRange, Bounds, Interface->RenderBuffer);
+ BlocksCount = 0;
+ FrontBlocksCount = 0;
+ BackBlocksCount = 0;
- if (PointIsInRect(BlockBounds, Interface->Mouse.Pos))
+ for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++)
{
- DragBlockHandle.Index = i;
- DragBlockHandle.Generation = ActiveAnim.Blocks_.Generations[i];
+ animation_block* AnimationBlockAt = ActiveAnim->Blocks_.Values + i;
+ if (AnimationBlockAt->Layer != l) continue;
+
+ // If either end is in the range, we should draw it
+ b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) ||
+ FrameIsInRange(ViewRange, AnimationBlockAt->Range.Max));
+ // If neither end is in the range, but the ends surround the visible range,
+ // we should still draw it.
+ RangeIsVisible |= (AnimationBlockAt->Range.Min <= ViewRange.Min &&
+ AnimationBlockAt->Range.Max>= ViewRange.Max);
+ if (!RangeIsVisible) continue;
+
+ r32 TimelineWidth = Rect2Width(Bounds);
+
+ frame_range BlockAtRange = AnimationBlockAt->Range;
+ u32 ClampedBlockStartFrame = ClampFrameToRange(BlockAtRange.Min, ViewRange);
+ r32 StartFramePercent = FrameToPercentRange(ClampedBlockStartFrame, ViewRange);
+ r32 StartPosition = TimelineWidth * StartFramePercent;
+
+ u32 ClampedBlockEndFrame = ClampFrameToRange(BlockAtRange.Max, ViewRange);
+ r32 EndFramePercent = FrameToPercentRange(ClampedBlockEndFrame, ViewRange);
+ r32 EndPosition = TimelineWidth * EndFramePercent;
+
+ r32 LayerYOffset = LAYER_HEIGHT * AnimationBlockAt->Layer;
+
+ rect2 NewBlockBounds = {};
+ NewBlockBounds.Min = Bounds.Min + v2{StartPosition, LayerYOffset};
+ NewBlockBounds.Max = Bounds.Min + v2{EndPosition, LayerYOffset + LAYER_HEIGHT};
+
+ block_bounds_lut_entry NewBlock = {};
+ NewBlock.Bounds = NewBlockBounds;
+ NewBlock.Index = i;
+
+ // fast (implementation-wise) insert sort.
+ // TODO(PS): probably not great
+ for (u32 j = 0; j < BlocksCount; j++)
+ {
+ if (Blocks[j].Bounds.Min.x > NewBlock.Bounds.Min.x)
+ {
+ block_bounds_lut_entry Old = Blocks[j];
+ Blocks[j] = NewBlock;
+ NewBlock = Old;
+ }
+ }
+ Blocks[BlocksCount++] = NewBlock;
}
+
+ // BlockBounds are sorted by their render bounds from left to right
+ // This iterates over them to see if any on the same layer overlap, and if
+ // so, shrinks one of them, putting it in a new list
+ for (u32 i = 0; i < BlocksCount; i++)
+ {
+ if (i % 2 == 0)
+ {
+ BackBlocks[BackBlocksCount++] = i;
+ continue;
+ }
+
+ bool ShortCandidate = false;
+ block_bounds_lut_entry Block = Blocks[i];
+ if (i > 0)
+ {
+ block_bounds_lut_entry PrevBlock = Blocks[i - 1];
+ rect2 Union = Rect2Union(PrevBlock.Bounds, Block.Bounds);
+ ShortCandidate |= Rect2Width(Union) > 0;
+ }
+ if (i < BlocksCount - 1)
+ {
+ block_bounds_lut_entry NextBlock = Blocks[i + 1];
+ rect2 Union = Rect2Union(NextBlock.Bounds, Block.Bounds);
+ ShortCandidate |= Rect2Width(Union) > 0;
+ }
+
+ if (ShortCandidate)
+ {
+ FrontBlocks[FrontBlocksCount++] = i;
+ }
+ else
+ {
+ BackBlocks[BackBlocksCount++] = i;
+ }
+ }
+
+ TimeRange_RenderBlockArray(Blocks, BackBlocks, BackBlocksCount, 0, ActiveAnim, SelectedBlockHandle, &DragBlockHandle, Interface, RenderBuffer);
+ TimeRange_RenderBlockArray(Blocks, FrontBlocks, FrontBlocksCount, -15, ActiveAnim, SelectedBlockHandle, &DragBlockHandle, Interface, RenderBuffer);
}
}
@@ -793,24 +844,64 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c
}
internal void
-AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
+AnimInfoView_SaveAnimFile (gs_const_string Path, app_state* State, context Context)
{
- animation_system* AnimSystem = &State->AnimationSystem;
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(AnimSystem);
+ animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From;
+ animation ActiveAnim = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle);
+ gs_string FileText = AnimSerializer_Serialize(ActiveAnim, State->Patterns, State->Transient);
+
+ if (!WriteEntireFile(Context.ThreadContext.FileHandler, Path, StringToData(FileText)))
+ {
+ InvalidCodePath;
+ }
+}
+
+PANEL_MODAL_OVERRIDE_CALLBACK(AnimInfoView_SaveAnimFileCallback)
+{
+ Assert(ReturningFrom->TypeIndex == PanelType_FileView);
+ file_view_state* FileViewState = Panel_GetStateStruct(ReturningFrom, file_view_state);
+ gs_file_info FileInfo = FileViewState->SelectedFile;
+
+ AnimInfoView_SaveAnimFile(FileInfo.Path, State, Context);
+}
+
+internal void
+AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timeline_state* TimelineState)
+{
+ TimelineState->NextActiveAnim = Handle;
+}
+
+internal void
+AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
+{
+ DEBUG_TRACK_FUNCTION;
+ animation_system* AnimSystem = &State->AnimationSystem;
+
+ animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From;
ui_interface* Interface = &State->Interface;
- ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout"));
+ ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout"), ActiveAnimHandle.Index);
ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG);
- if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), ActiveAnim->Name))
+ gs_string AnimName = {};
+ if (ActiveAnim)
+ {
+ AnimName = ActiveAnim->Name;
+ } else {
+ AnimName = MakeString("[None]");
+ }
+
+ if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), AnimName))
{
for (u32 i = 0; i < AnimSystem->Animations.Count; i++)
{
animation Animation = AnimSystem->Animations.Values[i];
if (ui_Button(Interface, Animation.Name))
{
- AnimSystem->ActiveAnimationIndex = i;
+ animation_handle NewHandle = {};
+ NewHandle.Index = i;
+ AnimationTimeline_SetActiveAnimation(NewHandle, TimelineState);
}
}
}
@@ -820,23 +911,31 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel
{
if (ui_Button(Interface, MakeString("New")))
{
- animation NewAnim = {};
- NewAnim.Name = PushString(State->AnimationSystem.Storage, 256);
+ animation_desc Desc = {};
+ Desc.NameSize = 256;
+ Desc.LayersCount = 8;
+ Desc.BlocksCount = 8;
+ Desc.MinFrames = 0;
+ Desc.MaxFrames = SecondsToFrames(15, State->AnimationSystem);
- u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim);
- State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex;
+ animation NewAnim = Animation_Create(Desc, &State->AnimationSystem);
+ animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim);
+ AnimationTimeline_SetActiveAnimation(NewAnimHandle, TimelineState);
}
- if (ui_Button(Interface, MakeString("Save")))
+ if (ActiveAnim && ui_Button(Interface, MakeString("Save")))
{
// Save Animation File
// TODO(pjs): If you created the animation via the "new" button, there won't be a file attached.
// need to use the file browser to create a file
- u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex;
- animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex];
- gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient);
- if (WriteEntireFile(Context.ThreadContext.FileHandler, ActiveAnimation.FileInfo.Path, StringToData(FileText)))
+ animation ActiveAnimation = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle);
+
+ if (!ActiveAnimation.FileInfo.Path.Str)
{
- InvalidCodePath;
+ panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context);
+ FileView_SetMode(FileBrowser, FileViewMode_Save);
+ Panel_PushModalOverride(Panel, FileBrowser, AnimInfoView_SaveAnimFileCallback);
+ } else {
+ AnimInfoView_SaveAnimFile(ActiveAnimation.FileInfo.Path, State, Context);
}
}
if (ui_Button(Interface, MakeString("Load")))
@@ -848,39 +947,75 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel
}
ui_EndRow(Interface);
- ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name);
-
- ui_Label(Interface, MakeString("Frame Range"));
- ui_BeginRow(Interface, 3);
+ if (ActiveAnim)
{
- ActiveAnim->PlayableRange.Min = ui_TextEntryU64(Interface, MakeString("StartFrame"), ActiveAnim->PlayableRange.Min);
- ActiveAnim->PlayableRange.Max = ui_TextEntryU64(Interface, MakeString("EndFrame"), ActiveAnim->PlayableRange.Max);
-
- }
- ui_EndRow(Interface);
-
- animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle);
- if (SelectedBlock)
- {
- animation_pattern BlockPattern = Patterns_GetPattern(State->Patterns, SelectedBlock->AnimationProcHandle);
+ ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name);
+ ui_Label(Interface, MakeString("Frame Range"));
ui_BeginRow(Interface, 3);
- ui_Label(Interface, MakeString("Selected Pattern"));
- //if (ui_BeginLabeledDropdown(Interface, MakeString("Selected Pattern"), MakeString(BlockPattern.Name, BlockPattern.NameLength)))
- if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength)))
{
- for (u32 i = 0; i < State->Patterns.Count; i++)
+ ActiveAnim->PlayableRange.Min = ui_TextEntryU64(Interface, MakeString("StartFrame"), ActiveAnim->PlayableRange.Min);
+ ActiveAnim->PlayableRange.Max = ui_TextEntryU64(Interface, MakeString("EndFrame"), ActiveAnim->PlayableRange.Max);
+
+ }
+ ui_EndRow(Interface);
+
+ ui_Label(Interface, MakeString("Layer"));
+
+ s32 LayerIndex = TimelineState->SelectedAnimationLayer;
+ anim_layer* SelectedLayer = 0;
+ if (LayerIndex >= 0)
+ {
+ SelectedLayer = ActiveAnim->Layers.Values + LayerIndex;
+
+ ui_TextEntry(Interface, MakeString("Layer Name"), &SelectedLayer->Name);
+ gs_string BlendStr = BlendModeStrings[SelectedLayer->BlendMode];
+ if (ui_BeginLabeledDropdown(Interface, MakeString("Blend Mode"), BlendStr))
{
- animation_pattern Pattern = State->Patterns.Values[i];
- if (ui_Button(Interface, MakeString(Pattern.Name, Pattern.NameLength)))
+ for (u32 i = 0; i < BlendMode_Count; i++)
{
- SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i);
+ if (ui_Button(Interface, BlendModeStrings[i]))
+ {
+ SelectedLayer->BlendMode = (blend_mode)i;
+ }
}
}
+ ui_EndLabeledDropdown(Interface);
+ }
+
+ ui_Label(Interface, MakeString("Pattern"));
+
+ animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle);
+ if (SelectedBlock)
+ {
+ animation_pattern BlockPattern = Patterns_GetPattern(State->Patterns, SelectedBlock->AnimationProcHandle);
+
+ ui_BeginRow(Interface, 2);
+ ui_Label(Interface, MakeString("Selected Pattern"));
+ if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength)))
+ {
+ Interface->ActiveLayout->Bounds.Max.x += 128.0f;
+ Interface->ActiveLayout->Columns[0].XMax += 128.0f;
+
+ ui_BeginList(Interface, MakeString("Patterns List"), 5, State->Patterns.Count);
+ for (u32 i = 0; i < State->Patterns.Count; i++)
+ {
+ animation_pattern Pattern = State->Patterns.Values[i];
+ if (ui_Button(Interface, MakeString(Pattern.Name, Pattern.NameLength)))
+ {
+ SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i);
+ }
+ }
+ ui_EndList(Interface);
+ }
+ ui_EndLabeledDropdown(Interface);
+ }
+
+ if (ui_Button(Interface, MakeString("+ Add Block")))
+ {
+ AnimationTimeline_AddAnimationBlockCommand(TimelineState, State, Context);
}
- ui_EndLabeledDropdown(Interface);
}
-
ui_PopLayout(Interface, MakeString("AnimInfo Layout"));
}
@@ -895,8 +1030,20 @@ GSMetaTag(panel_type_animation_timeline);
internal void
AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
+ DEBUG_TRACK_FUNCTION;
+
animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state);
+ animation* ActiveAnim = 0;
+ animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From;
+ TimelineState->NextActiveAnim = Handle;
+ if (IsValid(Handle))
+ {
+ animation_array Animations = State->AnimationSystem.Animations;
+ ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
+ TimelineState->EditingAnimationHandle = Handle;
+ }
+
ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f});
rect2 TimelineBounds, InfoBounds;
@@ -913,10 +1060,18 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer*
RectHSplitAtDistanceFromTop(TimeRangePanelBounds, TitleBarHeight, &FrameCountBounds, &TimeRangeBounds);
PlayBar_Render(TimelineState, PlayBarBounds, Panel, RenderBuffer, State, Context);
- FrameCount_Render(TimelineState, FrameCountBounds, RenderBuffer, State, Context);
- LayerList_Render(TimelineState, LayersBounds, Panel, RenderBuffer, State, Context);
- TimeRange_Render(TimelineState, TimeRangeBounds, RenderBuffer, State, Context);
- AnimInfoView_Render(TimelineState, InfoBounds, Panel, RenderBuffer, State, Context);
+ FrameCount_Render(TimelineState, ActiveAnim, FrameCountBounds, RenderBuffer, State, Context);
+ LayerList_Render(TimelineState, ActiveAnim, LayersBounds, Panel, RenderBuffer, State, Context);
+ TimeRange_Render(TimelineState, ActiveAnim, TimeRangeBounds, RenderBuffer, State, Context);
+ AnimInfoView_Render(TimelineState, ActiveAnim, InfoBounds, Panel, RenderBuffer, State, Context);
+
+ if (!AnimHandlesAreEqual(TimelineState->NextActiveAnim,
+ Handle))
+ {
+ State->AnimationSystem.ActiveFadeGroup.From = TimelineState->NextActiveAnim;
+ TimelineState->EditingAnimationHandle = TimelineState->NextActiveAnim;
+ TimelineState->SelectedAnimationLayer = -1;
+ }
}
#define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H
diff --git a/src/app/editor/panels/foldhaus_panel_assembly_debug.h b/src/app/editor/panels/foldhaus_panel_assembly_debug.h
index 2f4f1a2..4ef3599 100644
--- a/src/app/editor/panels/foldhaus_panel_assembly_debug.h
+++ b/src/app/editor/panels/foldhaus_panel_assembly_debug.h
@@ -43,72 +43,102 @@ AssemblyDebug_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
ui_interface* Interface = &State->Interface;
ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Assembly Debug Layout"));
- InterfaceAssert(Interface->PerFrameMemory);
-
- gs_string OverrideStr = MakeString(OverrideTypeStrings[State->AssemblyDebugState.Override]);
- if (ui_BeginLabeledDropdown(Interface, MakeString("Override"), OverrideStr))
+ ui_BeginRow(Interface, 2);
{
- for (u32 i = 0; i < ADS_Override_Count; i++)
+ if (ui_Button(Interface, MakeString("Assembly")))
{
- if (ui_Button(Interface, MakeString(OverrideTypeStrings[i])))
- {
- State->AssemblyDebugState.Override = (override_type)i;
- }
+ State->ShowingUserSpaceDebug = false;
+ }
+
+ if (ui_Button(Interface, MakeString("User Space")))
+ {
+ State->ShowingUserSpaceDebug = true;
}
}
- ui_EndLabeledDropdown(Interface);
- InterfaceAssert(Interface->PerFrameMemory);
+ ui_EndRow(Interface);
- switch (State->AssemblyDebugState.Override)
+ if (State->ShowingUserSpaceDebug && State->UserSpaceDesc.CustomDebugUI)
{
- case ADS_Override_TagWhite:
- case ADS_Override_TagStripWhite:
+ US_CustomDebugUI(&State->UserSpaceDesc, Panel, PanelBounds, RenderBuffer,
+ State, Context);
+ }
+ else
+ {
+ InterfaceAssert(Interface->PerFrameMemory);
+
+ State->AssemblyDebugState.AllAssemblies = ui_ToggleText(Interface, MakeString("All Assemblies"), State->AssemblyDebugState.AllAssemblies);
+
+ gs_string OverrideStr = MakeString(OverrideTypeStrings[State->AssemblyDebugState.Override]);
+ if (ui_BeginLabeledDropdown(Interface, MakeString("Override"), OverrideStr))
{
- ui_LabeledTextEntry(Interface, MakeString("Tag Name"), &State->AssemblyDebugState.TagName);
- ui_LabeledTextEntry(Interface, MakeString("Tag Value"), &State->AssemblyDebugState.TagValue);
-
- if (State->AssemblyDebugState.Override == ADS_Override_TagStripWhite)
+ for (u32 i = 0; i < ADS_Override_Count; i++)
{
+ if (ui_Button(Interface, MakeString(OverrideTypeStrings[i])))
+ {
+ State->AssemblyDebugState.Override = (override_type)i;
+ }
+ }
+ }
+ ui_EndLabeledDropdown(Interface);
+ InterfaceAssert(Interface->PerFrameMemory);
+
+ switch (State->AssemblyDebugState.Override)
+ {
+ case ADS_Override_TagWhite:
+ case ADS_Override_TagStripWhite:
+ {
+ ui_LabeledTextEntry(Interface, MakeString("Tag Name"), &State->AssemblyDebugState.TagName);
+ ui_LabeledTextEntry(Interface, MakeString("Tag Value"), &State->AssemblyDebugState.TagValue);
+
+ if (State->AssemblyDebugState.Override == ADS_Override_TagStripWhite)
+ {
+ State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly);
+
+ State->AssemblyDebugState.TargetStrip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), State->AssemblyDebugState.TargetStrip);
+ }
+ }break;
+
+ case ADS_Override_ChannelWhite:
+ {
+ u64 Board = 0;
+ u64 Strip = 0;
+ Board = ui_LabeledTextEntryU64(Interface, MakeString("Board"), Board);
+ Strip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), Strip);
+
+ State->AssemblyDebugState.TargetChannel = FSC(Board, Strip);
+ }break;
+
+ case ADS_Override_AllOff:
+ case ADS_Override_AllRed:
+ case ADS_Override_AllGreen:
+ case ADS_Override_AllBlue:
+ case ADS_Override_AllWhite:
+ {
+ State->AssemblyDebugState.Brightness = (u8)ui_LabeledRangeSlider(Interface, MakeString("Brightness"), (r32)State->AssemblyDebugState.Brightness, 0, 255);
+ State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly);
+ }break;
+
+ case ADS_Override_AllHue:
+ {
+ State->AssemblyDebugState.TargetHue = (u32)ui_LabeledRangeSlider(Interface, MakeString("Hue"), (r32)State->AssemblyDebugState.TargetHue, 0, 360);
+ }break;
+
+ default:
+ {
+ InterfaceAssert(Interface->PerFrameMemory);
+
State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly);
+ InterfaceAssert(Interface->PerFrameMemory);
+
State->AssemblyDebugState.TargetStrip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), State->AssemblyDebugState.TargetStrip);
- }
- }break;
-
- case ADS_Override_ChannelWhite:
- {
- u64 Board = 0;
- u64 Strip = 0;
- Board = ui_LabeledTextEntryU64(Interface, MakeString("Board"), Board);
- Strip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), Strip);
-
- State->AssemblyDebugState.TargetChannel = FSC(Board, Strip);
- }break;
-
- case ADS_Override_AllRed:
- case ADS_Override_AllGreen:
- case ADS_Override_AllBlue:
- case ADS_Override_AllWhite:
- {
- State->AssemblyDebugState.Brightness = (u8)ui_LabeledRangeSlider(Interface, MakeString("Brightness"), (r32)State->AssemblyDebugState.Brightness, 0, 255);
- }break;
-
- default:
- {
- InterfaceAssert(Interface->PerFrameMemory);
-
- State->AssemblyDebugState.TargetAssembly = ui_LabeledTextEntryU64(Interface, MakeString("Assembly"), State->AssemblyDebugState.TargetAssembly);
-
- InterfaceAssert(Interface->PerFrameMemory);
-
- State->AssemblyDebugState.TargetStrip = ui_LabeledTextEntryU64(Interface, MakeString("Strip"), State->AssemblyDebugState.TargetStrip);
-
- InterfaceAssert(Interface->PerFrameMemory);
- }break;
+
+ InterfaceAssert(Interface->PerFrameMemory);
+ }break;
+ }
}
- ui_RangeSlider(Interface, MakeString("Test"), .5f, 0, 1);
-
+ State->SendEmptyPackets = ui_LabeledToggle(Interface, MakeString("Send Empty Packets"), State->SendEmptyPackets);
ui_PopLayout(Interface, MakeString("Assembly Debug Layout"));
}
diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h
index eaf5013..64b03d5 100644
--- a/src/app/editor/panels/foldhaus_panel_file_view.h
+++ b/src/app/editor/panels/foldhaus_panel_file_view.h
@@ -7,40 +7,42 @@
enum file_view_mode
{
- FileViewMode_Load,
- FileViewMode_Save,
+ FileViewMode_Load,
+ FileViewMode_Save,
};
struct file_view_state
{
- file_view_mode Mode;
-
- gs_string WorkingDirectory;
- gs_memory_arena FileNamesArena;
- gs_file_info_array FileNames;
-
- gs_file_info SelectedFile;
+ file_view_mode Mode;
+
+ gs_string WorkingDirectory;
+ gs_string DisplayDirectory;
+
+ gs_memory_arena FileNamesArena;
+ gs_file_info_array FileNames;
+
+ gs_file_info SelectedFile;
};
internal void
FileView_SetMode(panel* Panel, file_view_mode Mode)
{
- file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state);
- FileViewState->Mode = Mode;
+ file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state);
+ FileViewState->Mode = Mode;
}
internal void
FileView_Exit_(panel* FileViewPanel, app_state* State, context Context)
{
- // TODO(pjs): Free State->FileNamesArena
-
- Assert(FileViewPanel->IsModalOverrideFor != 0);
- panel* ReturnTo = FileViewPanel->IsModalOverrideFor;
- if (ReturnTo->ModalOverrideCB)
- {
- ReturnTo->ModalOverrideCB(FileViewPanel, State, Context);
- }
- Panel_PopModalOverride(ReturnTo, &State->PanelSystem);
+ // TODO(pjs): Free State->FileNamesArena
+
+ Assert(FileViewPanel->IsModalOverrideFor != 0);
+ panel* ReturnTo = FileViewPanel->IsModalOverrideFor;
+ if (ReturnTo->ModalOverrideCB)
+ {
+ ReturnTo->ModalOverrideCB(FileViewPanel, State, Context);
+ }
+ Panel_PopModalOverride(ReturnTo, &State->PanelSystem);
}
global input_command* FileView_Commands = 0;
@@ -49,31 +51,35 @@ s32 FileView_CommandsCount = 0;
internal void
FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_state* State, context Context)
{
- ClearArena(&State->FileNamesArena);
+ // NOTE(pjs): make sure we're only passing valid directory paths to the
+ // function
+ char LastChar = WorkingDirectory.Str[WorkingDirectory.Length - 1];
+ Assert(LastChar == '\\' || LastChar == '/');
+ MemoryArenaClear(&State->FileNamesArena);
+
+
+ gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2);
+ SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient);
+ if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\')
+ {
+ AppendPrintF(&SanitizedDir, "\\");
+ }
+
+ gs_const_string SanitizedDisplayDir = SanitizedDir.ConstString;
+
+ gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString);
+ if (NewWorkingDir.IsDirectory)
+ {
+ AppendPrintF(&SanitizedDir, "*");
+ NullTerminate(&SanitizedDir);
- gs_const_string SanitizedDirectory = WorkingDirectory;
+ State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, SanitizedDir.ConstString, EnumerateDirectory_IncludeDirectories);
- u32 LastSlashIndex = FindLast(SanitizedDirectory, '\\');
- gs_const_string LastDir = Substring(SanitizedDirectory, LastSlashIndex + 1, SanitizedDirectory.Length);
- if (StringsEqual(LastDir, ConstString("..")))
- {
- u32 SecondLastSlashIndex = FindLast(SanitizedDirectory, LastSlashIndex - 1, '\\');
- SanitizedDirectory = Substring(SanitizedDirectory, 0, SecondLastSlashIndex);
- }
- else if (StringsEqual(LastDir, ConstString(".")) && LastDir.Length > 1)
- {
- SanitizedDirectory = Substring(SanitizedDirectory, 0, LastSlashIndex);
- }
-
- gs_file_info NewWorkingDirectory = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDirectory);
- if (NewWorkingDirectory.IsDirectory)
- {
- // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory
- // in some cases. Shouldn't be a problem but it is unnecessary
- PrintF(&State->WorkingDirectory, "%S", WorkingDirectory);
-
- State->FileNames = EnumerateDirectory(Context.ThreadContext.FileHandler, &State->FileNamesArena, State->WorkingDirectory.ConstString, EnumerateDirectory_IncludeDirectories);
- }
+ // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory
+ // in some cases. Shouldn't be a problem but it is unnecessary
+ PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString);
+ PrintF(&State->DisplayDirectory, "%S", SanitizedDisplayDir);
+ }
}
GSMetaTag(panel_init);
@@ -81,14 +87,16 @@ GSMetaTag(panel_type_file_view);
internal void
FileView_Init(panel* Panel, app_state* State, context Context)
{
- // TODO: :FreePanelMemory
- file_view_state* FileViewState = PushStruct(&State->Permanent, file_view_state);
- Panel->StateMemory = StructToData(FileViewState, file_view_state);
- FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator);
-
- // TODO(pjs): this shouldn't be stored in permanent
- FileViewState->WorkingDirectory = PushString(&State->Permanent, 256);
- FileView_UpdateWorkingDirectory(ConstString("."), FileViewState, Context);
+ // TODO: :FreePanelMemory
+ file_view_state* FileViewState = PushStruct(&State->Permanent, file_view_state);
+ Panel->StateMemory = StructToData(FileViewState, file_view_state);
+ FileViewState->FileNamesArena = MemoryArenaCreate(MB(4), Bytes(8), Context.ThreadContext.Allocator, 0, 0, "File View - File Names Arena");
+
+ // TODO(pjs): this shouldn't be stored in permanent
+ FileViewState->DisplayDirectory = PushString(&State->Permanent, 1024);
+ FileViewState->WorkingDirectory = PushString(&State->Permanent, 1024);
+
+ FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context);
}
GSMetaTag(panel_cleanup);
@@ -96,7 +104,7 @@ GSMetaTag(panel_type_file_view);
internal void
FileView_Cleanup(panel* Panel, app_state* State)
{
-
+
}
GSMetaTag(panel_render);
@@ -104,60 +112,87 @@ GSMetaTag(panel_type_file_view);
internal void
FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
- file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state);
- Assert(FileViewState->Mode == FileViewMode_Save);
-
- ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout"));
+ file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state);
+
+ ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout"));
+ {
+ ui_BeginRow(&State->Interface, 3);
{
- if (ui_Button(&State->Interface, MakeString("Exit")))
+ if (FileViewState->Mode == FileViewMode_Save)
+ {
+ if (ui_Button(&State->Interface, MakeString("Save")))
{
- FileView_Exit_(Panel, State, Context);
+ if (!FileViewState->SelectedFile.Path.Str)
+ {
+ FileViewState->SelectedFile.Path = FileViewState->DisplayDirectory.ConstString;
+ }
+
+ FileView_Exit_(Panel, State, Context);
}
-
- // Header
- if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->WorkingDirectory))
- {
- // if last character is a slash, update pwd, and clear the filter string
- // otherwise update the filter string
- gs_string Pwd = FileViewState->WorkingDirectory;
- char LastChar = Pwd.Str[Pwd.Length - 1];
- if (LastChar == '\\' || LastChar == '/')
- {
- FileView_UpdateWorkingDirectory(Pwd.ConstString, FileViewState, Context);
- }
- else
- {
-
- }
- }
-
- // File Display
- ui_BeginList(&State->Interface, MakeString("Files"), 10, FileViewState->FileNames.Count);
- for (u32 i = 0; i < FileViewState->FileNames.Count; i++)
- {
- gs_file_info File = FileViewState->FileNames.Values[i];
-
- u32 LastSlashIndex = FindLast(File.Path, '\\');
- gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length);
- gs_string PathString = PushString(State->Transient, FileName.Length);
- PrintF(&PathString, "%S", FileName);
-
- if (ui_LayoutListButton(&State->Interface, PathString, i))
- {
- if (File.IsDirectory)
- {
- FileView_UpdateWorkingDirectory(File.Path, FileViewState, Context);
- }
- else
- {
- FileViewState->SelectedFile = File;
- FileView_Exit_(Panel, State, Context);
- }
- }
- }
- ui_EndList(&State->Interface);
+ }
+
+ if (ui_Button(&State->Interface, MakeString("Exit")))
+ {
+ FileView_Exit_(Panel, State, Context);
+ }
}
- ui_PopLayout(&State->Interface, MakeString("FileView Layout"));
+ ui_EndRow(&State->Interface);
+
+ // Header
+ if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->DisplayDirectory))
+ {
+ // if last character is a slash, update pwd, and clear the filter string
+ // otherwise update the filter string
+ gs_string Pwd = FileViewState->DisplayDirectory;
+ char LastChar = Pwd.Str[Pwd.Length - 1];
+ if (LastChar == '\\' || LastChar == '/')
+ {
+ FileView_UpdateWorkingDirectory(Pwd.ConstString, FileViewState, Context);
+ }
+ else
+ {
+
+ }
+ }
+
+ // File Display
+ ui_BeginList(&State->Interface, MakeString("Files"), 10, FileViewState->FileNames.Count);
+ for (u32 i = 0; i < FileViewState->FileNames.Count; i++)
+ {
+ gs_file_info File = FileViewState->FileNames.Values[i];
+
+ u32 LastSlashIndex = FindLast(File.Path, File.Path.Length - 2, '\\');
+ gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length);
+ gs_string PathString = PushString(State->Transient, FileName.Length);
+ PrintF(&PathString, "%S", FileName);
+
+ if (ui_LayoutListButton(&State->Interface, PathString, i))
+ {
+ if (File.IsDirectory)
+ {
+ FileView_UpdateWorkingDirectory(File.Path, FileViewState, Context);
+ }
+ else
+ {
+ FileViewState->SelectedFile = File;
+ switch (FileViewState->Mode)
+ {
+ case FileViewMode_Load:
+ {
+ FileView_Exit_(Panel, State, Context);
+ } break;
+
+ case FileViewMode_Save:
+ {
+
+ } break;
+ }
+ }
+ }
+ }
+ ui_EndList(&State->Interface);
+ }
+ ui_PopLayout(&State->Interface, MakeString("FileView Layout"));
}
diff --git a/src/app/editor/panels/foldhaus_panel_hierarchy.h b/src/app/editor/panels/foldhaus_panel_hierarchy.h
index 13d45fe..8731660 100644
--- a/src/app/editor/panels/foldhaus_panel_hierarchy.h
+++ b/src/app/editor/panels/foldhaus_panel_hierarchy.h
@@ -30,7 +30,7 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAssemblyCallback)
file_view_state* FileViewState = Panel_GetStateStruct(ReturningFrom, file_view_state);
gs_file_info FileInfo = FileViewState->SelectedFile;
- LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, FileInfo.Path, State->GlobalLog);
+ LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, FileInfo.Path, GlobalLogBuffer);
}
GSMetaTag(panel_render);
@@ -69,7 +69,7 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
if (ui_Button(&State->Interface, MakeString("+ Add Assembly")))
{
panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context);
- FileView_SetMode(FileBrowser, FileViewMode_Save);
+ FileView_SetMode(FileBrowser, FileViewMode_Load);
Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback);
}
ui_EndRow(&State->Interface);
diff --git a/src/app/editor/panels/foldhaus_panel_message_log.h b/src/app/editor/panels/foldhaus_panel_message_log.h
new file mode 100644
index 0000000..76d7d59
--- /dev/null
+++ b/src/app/editor/panels/foldhaus_panel_message_log.h
@@ -0,0 +1,46 @@
+/* date = April 12th 2021 4:47 pm */
+
+#ifndef FOLDHAUS_PANEL_MESSAGE_LOG_H
+#define FOLDHAUS_PANEL_MESSAGE_LOG_H
+
+GSMetaTag(panel_init);
+GSMetaTag(panel_type_file_view);
+internal void
+MessageLog_Init(panel* Panel, app_state* State, context Context)
+{
+}
+
+GSMetaTag(panel_cleanup);
+GSMetaTag(panel_type_file_view);
+internal void
+MessageLog_Cleanup(panel* Panel, app_state* State)
+{
+
+}
+
+GSMetaTag(panel_render);
+GSMetaTag(panel_type_file_view);
+internal void
+MessageLog_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
+{
+ ui_interface* Interface = &State->Interface;
+ ui_widget* Layout = ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("Message Log Layout"));
+
+ ui_BeginList(Interface, MakeString("Message Log List"), 10, GlobalLogBuffer->EntriesCount);
+
+ log_buffer_iter Iter = Log_GetIter(GlobalLogBuffer);
+ while (true)
+ {
+ log_entry* At = Iter.At;
+ ui_Label(Interface, At->String);
+ if (!LogIter_CanAdvance(Iter))
+ {
+ break;
+ }
+ LogIter_Advance(&Iter);
+ }
+ ui_EndList(Interface);
+
+ ui_PopLayout(Interface, MakeString("Message Log Layout"));
+}
+#endif //FOLDHAUS_PANEL_MESSAGE_LOG_H
diff --git a/src/app/editor/panels/foldhaus_panel_profiler.h b/src/app/editor/panels/foldhaus_panel_profiler.h
index 405b50f..c0ecd79 100644
--- a/src/app/editor/panels/foldhaus_panel_profiler.h
+++ b/src/app/editor/panels/foldhaus_panel_profiler.h
@@ -13,7 +13,7 @@ GSMetaTag(panel_type_profiler);
internal void
ProfilerView_Init(panel* Panel, app_state* State, context Context)
{
-
+
}
GSMetaTag(panel_cleanup);
@@ -21,146 +21,217 @@ GSMetaTag(panel_type_profiler);
internal void
ProfilerView_Cleanup(panel* Panel, app_state* State)
{
-
+
}
internal void
RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Transient)
{
- rect2 Bounds = ui_LayoutRemaining(*Layout);
- r32 Width = Rect2Width(Bounds);
- r32 DepthHeight = 32;
+ rect2 Bounds = ui_LayoutRemaining(*Layout);
+ r32 Width = Rect2Width(Bounds);
+ r32 DepthHeight = 32;
+
+ s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
+ r32 FrameTotalCycles = (r32)(VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles);
+
+ r32 NextThreadTop = Bounds.Max.y;
+
+ for (s32 t = 0; t < VisibleFrame->ThreadCount; t++)
+ {
+ debug_scope_record_list ThreadCalls = VisibleFrame->ThreadCalls[t];
- s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
- r32 FrameTotalCycles = (r32)(VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles);
+ gs_string String = PushString(Transient, 256);
- r32 NextThreadTop = Bounds.Max.y;
+ r32 ThreadScopeMin = Bounds.Max.y;
- for (s32 t = 0; t < VisibleFrame->ThreadCount; t++)
+ //PrintF(&String, "Thread %d", ThreadCalls.ThreadId);
+ //ui_Label(Interface, String, rect2{ThreadScopeMin);
+
+ r32 Hue = (r32)(t) / (r32)(VisibleFrame->ThreadCount);
+ Hue += (.5f * (t % 2));
+ v4 ThreadHSV = v4{ 360.0f * Hue, .5f, 1.0f, 1.0f };
+ v4 ThreadRGB = HSVToRGB(ThreadHSV);
+
+ for (s32 i = 0; i < ThreadCalls.Count; i++)
{
- debug_scope_record_list ThreadCalls = VisibleFrame->ThreadCalls[t];
-
- gs_string String = PushString(Transient, 256);
-
- r32 ThreadScopeMin = Bounds.Max.y;
-
- //PrintF(&String, "Thread %d", ThreadCalls.ThreadId);
- //ui_Label(Interface, String, rect2{ThreadScopeMin);
-
- r32 Hue = (r32)(t) / (r32)(VisibleFrame->ThreadCount);
- Hue += (.5f * (t % 2));
- v4 ThreadHSV = v4{ 360.0f * Hue, .5f, 1.0f, 1.0f };
- v4 ThreadRGB = HSVToRGB(ThreadHSV);
-
- for (s32 i = 0; i < ThreadCalls.Count; i++)
+ scope_record* Record = ThreadCalls.Calls + i;
+ scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash);
+ s64 OffsetStart = Record->StartCycles - FrameStartCycles;
+ s64 OffsetEnd = Record->EndCycles - FrameStartCycles;
+ r32 PercentStart = (r32)(OffsetStart) / FrameTotalCycles;
+ r32 PercentEnd = (r32)(OffsetEnd) / FrameTotalCycles;
+ r32 PercentWidth = PercentEnd - PercentStart;
+
+ rect2 ScopeBounds = {
+ v2{0, 0},
+ v2{PercentWidth * Width, DepthHeight - 4},
+ };
+ v2 Offset = {
+ Bounds.Min.x + (PercentStart * Width),
+ NextThreadTop - ((Record->CallDepth + 1) * DepthHeight)
+ };
+ ScopeBounds = Rect2Translate(ScopeBounds, Offset);
+ ThreadScopeMin = Min(ScopeBounds.Min.y, ThreadScopeMin);
+
+ if (Rect2Width(ScopeBounds) >= 1)
+ {
+ v4 Color = ThreadRGB;
+ if (PointIsInRect(ScopeBounds, Interface->Mouse.Pos))
{
- scope_record* Record = ThreadCalls.Calls + i;
- scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash);
- s64 OffsetStart = Record->StartCycles - FrameStartCycles;
- s64 OffsetEnd = Record->EndCycles - FrameStartCycles;
- r32 PercentStart = (r32)(OffsetStart) / FrameTotalCycles;
- r32 PercentEnd = (r32)(OffsetEnd) / FrameTotalCycles;
- r32 PercentWidth = PercentEnd - PercentStart;
-
- rect2 ScopeBounds = {
- v2{0, 0},
- v2{PercentWidth * Width, DepthHeight - 4},
- };
- v2 Offset = {
- Bounds.Min.x + (PercentStart * Width),
- NextThreadTop - ((Record->CallDepth + 1) * DepthHeight)
- };
- ScopeBounds = Rect2Translate(ScopeBounds, Offset);
- ThreadScopeMin = Min(ScopeBounds.Min.y, ThreadScopeMin);
-
- if (Rect2Width(ScopeBounds) >= 1)
- {
- v4 Color = ThreadRGB;
- if (PointIsInRect(ScopeBounds, Interface->Mouse.Pos))
- {
- Color = GreenV4;
-
- ui_BeginMousePopup(Interface, rect2{ 25, 25, 300, 57 }, LayoutDirection_TopDown, MakeString("Hover"));
- {
- s64 Cycles = (Record->EndCycles - Record->StartCycles);
- r32 PercentFrame = (r32)(Cycles) / FrameTotalCycles;
- PrintF(&String, "%S : %.2f%% frame | %dcy",
- Name->Name,
- PercentFrame,
- Cycles);
- ui_Label(Interface, String);
- }
- ui_EndMousePopup(Interface);
- }
-
- ui_FillRect(Interface, ScopeBounds, Color);
- ui_OutlineRect(Interface, ScopeBounds, 1, BlackV4);
- }
+ Color = GreenV4;
+
+ ui_BeginMousePopup(Interface, rect2{ 25, 25, 300, 57 }, LayoutDirection_TopDown, MakeString("Hover"));
+ {
+ s64 Cycles = (Record->EndCycles - Record->StartCycles);
+ r32 PercentFrame = (r32)(Cycles) / FrameTotalCycles;
+ PrintF(&String, "%S : %.2f%% frame | %dcy",
+ Name->Name,
+ PercentFrame,
+ Cycles);
+ ui_Label(Interface, String);
+ }
+ ui_EndMousePopup(Interface);
}
- NextThreadTop = ThreadScopeMin;
+ ui_FillRect(Interface, ScopeBounds, Color);
+ ui_OutlineRect(Interface, ScopeBounds, 1, BlackV4);
+ }
}
+
+ NextThreadTop = ThreadScopeMin;
+ }
}
internal void
RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory)
{
- char Backbuffer[256];
- gs_string String = MakeString(Backbuffer, 0, 256);
-
- ui_column_spec ColumnWidths[] = {
- { UIColumnSize_Fixed, 256 },
- { UIColumnSize_Fixed, 128 },
- { UIColumnSize_Fixed, 128 },
- { UIColumnSize_Fixed, 128 },
- { UIColumnSize_Fixed, 128 }};
- ui_BeginRow(Interface, 5, &ColumnWidths[0]);
+ char Backbuffer[256];
+ gs_string String = MakeString(Backbuffer, 0, 256);
+
+ ui_column_spec ColumnWidths[] = {
+ { UIColumnSize_Fixed, 256 },
+ { UIColumnSize_Fixed, 128 },
+ { UIColumnSize_Fixed, 128 },
+ { UIColumnSize_Fixed, 128 },
+ { UIColumnSize_Fixed, 128 }};
+ ui_BeginRow(Interface, 5, &ColumnWidths[0]);
+ {
+ ui_Label(Interface, MakeString("Procedure"));
+ ui_Label(Interface, MakeString("% Frame"));
+ ui_Label(Interface, MakeString("Seconds"));
+ ui_Label(Interface, MakeString("Cycles"));
+ ui_Label(Interface, MakeString("Calls"));
+ }
+ ui_EndRow(Interface);
+
+ s32 CountedScopes = 0;
+ for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
+ {
+ scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
+ if (NameEntry.Hash != 0)
{
- ui_Label(Interface, MakeString("Procedure"));
- ui_Label(Interface, MakeString("% Frame"));
- ui_Label(Interface, MakeString("Seconds"));
- ui_Label(Interface, MakeString("Cycles"));
- ui_Label(Interface, MakeString("Calls"));
+ CountedScopes += 1;
}
- ui_EndRow(Interface);
-
- s32 CountedScopes = 0;
- for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
+ }
+
+ ui_BeginList(Interface, MakeString("Scope List"), 10, CountedScopes);
+ ui_BeginRow(Interface, 5, &ColumnWidths[0]);
+ for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
+ {
+ scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
+ if (NameEntry.Hash != 0)
{
- scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
- if (NameEntry.Hash != 0)
- {
- CountedScopes += 1;
- }
+ collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n;
+
+ PrintF(&String, "%S", NameEntry.Name);
+ ui_Label(Interface, String);
+
+ PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime);
+ ui_Label(Interface, String);
+
+ PrintF(&String, "%fs", CollatedRecord->TotalSeconds);
+ ui_Label(Interface, String);
+
+ PrintF(&String, "%dcy", CollatedRecord->TotalCycles);
+ ui_Label(Interface, String);
+
+ PrintF(&String, "%d", CollatedRecord->CallCount);
+ ui_Label(Interface, String);
}
+ }
+ ui_EndRow(Interface);
+ ui_EndList(Interface);
+}
+
+struct mem_amt
+{
+ u64 OrigSize;
+ r64 Size;
+ char* Units;
+};
+
+internal mem_amt
+GetMemAmt (u64 BytesCount)
+{
+ mem_amt Result = {};
+ Result.OrigSize = BytesCount;
+ Result.Size = (r64)BytesCount;
+ Result.Units = "bytes";
+
+ u32 i = 0;
+ char* UnitList[] = { "kb", "mb", "gb", "tb" };
+ while (Result.Size > 1024) {
+ Result.Size /= 1024.0;
+ Result.Units = UnitList[i++];
+ }
+
+ return Result;
+}
+
+internal void
+RenderProfiler_MemoryView(ui_interface* Interface, ui_widget* Layout, app_state* State, context Context, gs_memory_arena* Memory)
+{
+ gs_debug_allocations_list* DA = Context.ThreadContext.Allocator.DEBUGAllocList;
+
+ gs_string TempString = PushString(State->Transient, 256);
+
+ mem_amt MemFootprint = GetMemAmt(DA->AllocationsSizeTotal);
+ u64 AllocCount = DA->AllocationsCount;
+
+ PrintF(&TempString, "Total Memory Size: %.2f %s | Allocations: %lld", MemFootprint.Size, MemFootprint.Units, AllocCount);
+ ui_Label(Interface, TempString);
+
+ ui_column_spec ColumnWidths[] = {
+ { UIColumnSize_Fill, 0 },
+ { UIColumnSize_Fixed,256 },
+ };
+ ui_BeginRow(Interface, 2, &ColumnWidths[0]);
+ {
+ ui_Label(Interface, MakeString("Location"));
+ ui_Label(Interface, MakeString("Alloc Size"));
+ }
+ ui_EndRow(Interface);
+
+ ui_BeginList(Interface, MakeString("Alloc List"), 10, DA->AllocationsCount);
+ ui_BeginRow(Interface, 2, &ColumnWidths[0]);
+
+ for (gs_debug_memory_allocation* A = DA->Root;
+ A && A->Next != 0;
+ A = A->Next)
+ {
+ gs_const_string Str = ConstString(A->Loc.File);
+ u64 LastSlash = FindLastFromSet(Str, "\\/");
+ gs_const_string JustFileName = Substring(Str, LastSlash + 1, Str.Length);
+ PrintF(&TempString, "%s:%s(%d)", JustFileName.Str, A->Loc.Function, A->Loc.Line);
+ ui_Label(Interface, TempString);
- ui_BeginList(Interface, MakeString("Scope List"), 10, CountedScopes);
- ui_BeginRow(Interface, 5, &ColumnWidths[0]);
- for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
- {
- scope_name NameEntry = VisibleFrame->ScopeNamesHash[n];
- if (NameEntry.Hash != 0)
- {
- collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n;
-
- PrintF(&String, "%S", NameEntry.Name);
- ui_Label(Interface, String);
-
- PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime);
- ui_Label(Interface, String);
-
- PrintF(&String, "%fs", CollatedRecord->TotalSeconds);
- ui_Label(Interface, String);
-
- PrintF(&String, "%dcy", CollatedRecord->TotalCycles);
- ui_Label(Interface, String);
-
- PrintF(&String, "%d", CollatedRecord->CallCount);
- ui_Label(Interface, String);
- }
- }
- ui_EndRow(Interface);
- ui_EndList(Interface);
+ mem_amt Amt = GetMemAmt(A->Size);
+
+ PrintF(&TempString, "%.2f %s", Amt.Size, Amt.Units);
+ ui_Label(Interface, TempString);
+ }
+ ui_EndRow(Interface);
+ ui_EndList(Interface);
}
GSMetaTag(panel_render);
@@ -168,93 +239,120 @@ GSMetaTag(panel_type_profiler);
internal void
ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
- gs_memory_arena* Memory = State->Transient;
- gs_string String = PushString(Memory, 256);
-
- v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 };
-
- r32 FrameListHeight = 64;
- rect2 FrameListBounds, ProcListBounds;
- RectHSplitAtDistanceFromTop(PanelBounds, FrameListHeight, &FrameListBounds, &ProcListBounds);
- rect2 FrameListInner = RectInset(FrameListBounds, 4);
-
- r32 SingleFrameStep = Rect2Width(FrameListInner) / DEBUG_FRAME_COUNT;
+ gs_memory_arena* Memory = State->Transient;
+ gs_string String = PushString(Memory, 256);
+
+ v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 };
+
+ r32 FrameListHeight = 64;
+ rect2 FrameListBounds, ProcListBounds;
+ RectHSplitAtDistanceFromTop(PanelBounds, FrameListHeight, &FrameListBounds, &ProcListBounds);
+ rect2 FrameListInner = RectInset(FrameListBounds, 4);
+
+ s32 FramesToDisplay = DEBUG_FRAME_COUNT;
+ if (FramesToDisplay != 0)
+ {
+ r32 SingleFrameStep = Rect2Width(FrameListInner) / FramesToDisplay;
r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2);
ui_OutlineRect(&State->Interface, FrameListBounds, 2, WhiteV4);
if (MouseButtonHeldDown(Context.Mouse.LeftButtonState))
{
- if (PointIsInRect(FrameListBounds, Context.Mouse.Pos))
+ if (PointIsInRect(FrameListBounds, Context.Mouse.Pos))
+ {
+ v2 LocalMouse = Rect2GetRectLocalPoint(FrameListBounds, Context.Mouse.Pos);
+ s32 ClosestFrameIndex = (LocalMouse.x / SingleFrameStep);
+ if (ClosestFrameIndex >= 0 && ClosestFrameIndex < FramesToDisplay)
{
- v2 LocalMouse = Rect2GetRectLocalPoint(FrameListBounds, Context.Mouse.Pos);
- s32 ClosestFrameIndex = (LocalMouse.x / SingleFrameStep);
- if (ClosestFrameIndex >= 0 && ClosestFrameIndex < DEBUG_FRAME_COUNT)
- {
- GlobalDebugServices->RecordFrames = false;
- GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex;
- }
+ GlobalDebugServices->RecordFrames = false;
+ GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex;
}
+ }
}
rect2 FrameBounds = MakeRect2MinDim(FrameListInner.Min, v2{SingleFrameWidth, Rect2Height(FrameListInner)});
for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++)
{
- rect2 PositionedFrameBounds = Rect2TranslateX(FrameBounds, F * SingleFrameStep);
- s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F);
- if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; }
- v4 Color = FrameColors[Clamp(0, FramesAgo, 3)];
- ui_FillRect(&State->Interface, PositionedFrameBounds, Color);
+ rect2 PositionedFrameBounds = Rect2TranslateX(FrameBounds, F * SingleFrameStep);
+ s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F);
+ if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; }
+ v4 Color = FrameColors[Clamp(0, FramesAgo, 3)];
+ ui_FillRect(&State->Interface, PositionedFrameBounds, Color);
}
-
- debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices);
-
- ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout"));
-
+ }
+
+ ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout"));
+
+ debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices);
+ if (VisibleFrame)
+ {
ui_BeginRow(&State->Interface, 4);
{
- s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
- s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
- u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1;
- PrintF(&String, "Frame %d", CurrentDebugFrame);
- ui_Label(&State->Interface, String);
-
- PrintF(&String, "Total Cycles: %lld", FrameTotalCycles);
- ui_Label(&State->Interface, String);
-
- // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could
- // be removed, or used for something else
- ui_ReserveBounds(&State->Interface, Layout, true);
-
- if (ui_Button(&State->Interface, MakeString("Resume Recording")))
- {
- GlobalDebugServices->RecordFrames = true;
- }
+ s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
+ s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
+ u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1;
+ PrintF(&String, "Frame %d", CurrentDebugFrame);
+ ui_Label(&State->Interface, String);
+
+ PrintF(&String, "Total Cycles: %lld", FrameTotalCycles);
+ ui_Label(&State->Interface, String);
+
+ // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could
+ // be removed, or used for something else
+ ui_ReserveBounds(&State->Interface, Layout, true);
+
+ if (ui_Button(&State->Interface, MakeString("Resume Recording")))
+ {
+ GlobalDebugServices->RecordFrames = true;
+ }
}
ui_EndRow(&State->Interface);
-
- ui_BeginRow(&State->Interface, 8);
+ }
+
+ ui_BeginRow(&State->Interface, 8);
+ {
+ if (ui_Button(&State->Interface, MakeString("Profiler")))
{
- if (ui_Button(&State->Interface, MakeString("Scope View")))
- {
- GlobalDebugServices->Interface.FrameView = FRAME_VIEW_PROFILER;
- }
- if (ui_Button(&State->Interface, MakeString("List View")))
- {
- GlobalDebugServices->Interface.FrameView = FRAME_VIEW_SCOPE_LIST;
- }
+ GlobalDebugServices->Interface.FrameView = DebugUI_Profiler;
}
- ui_EndRow(&State->Interface);
-
- if (GlobalDebugServices->Interface.FrameView == FRAME_VIEW_PROFILER)
+ if (ui_Button(&State->Interface, MakeString("List View")))
{
+ GlobalDebugServices->Interface.FrameView = DebugUI_ScopeList;
+ }
+ if (ui_Button(&State->Interface, MakeString("Memory")))
+ {
+ GlobalDebugServices->Interface.FrameView = DebugUI_MemoryView;
+ }
+ }
+ ui_EndRow(&State->Interface);
+
+ switch (GlobalDebugServices->Interface.FrameView)
+ {
+ case DebugUI_Profiler:
+ {
+ if (VisibleFrame)
+ {
RenderProfiler_ScopeVisualization(&State->Interface, Layout, VisibleFrame, Memory);
- }
- else
- {
- RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory);
- }
+ }
+ }break;
- ui_PopLayout(&State->Interface, MakeString("Profiler Layout"));
+ case DebugUI_ScopeList:
+ {
+ if (VisibleFrame)
+ {
+ RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory);
+ }
+ }break;
+
+ case DebugUI_MemoryView:
+ {
+ RenderProfiler_MemoryView(&State->Interface, Layout, State, Context, Memory);
+ }break;
+
+ InvalidDefaultCase;
+ }
+
+ ui_PopLayout(&State->Interface, MakeString("Profiler Layout"));
}
diff --git a/src/app/editor/panels/foldhaus_panel_sculpture_view.h b/src/app/editor/panels/foldhaus_panel_sculpture_view.h
index 6d4c803..474d5ca 100644
--- a/src/app/editor/panels/foldhaus_panel_sculpture_view.h
+++ b/src/app/editor/panels/foldhaus_panel_sculpture_view.h
@@ -13,49 +13,49 @@
struct sculpture_view_panel_state
{
- camera Camera;
+ camera Camera;
};
// 3D Mouse View
OPERATION_STATE_DEF(mouse_rotate_view_operation_state)
{
- v4 CameraStartPos;
- camera* Camera;
+ v4 CameraStartPos;
+ camera* Camera;
};
OPERATION_RENDER_PROC(Update3DViewMouseRotate)
{
- mouse_rotate_view_operation_state* OpState = (mouse_rotate_view_operation_state*)Operation.OpStateMemory;
-
- v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos;
-
- m44 XRotation = M44RotationX(-TotalDeltaPos.y * PIXEL_TO_WORLD_SCALE);
- m44 YRotation = M44RotationY(TotalDeltaPos.x * PIXEL_TO_WORLD_SCALE);
- m44 Combined = XRotation * YRotation;
-
- OpState->Camera->Position = (Combined * OpState->CameraStartPos).xyz;
+ mouse_rotate_view_operation_state* OpState = (mouse_rotate_view_operation_state*)Operation.OpStateMemory;
+
+ v2 TotalDeltaPos = Mouse.Pos - Mouse.DownPos;
+
+ m44 XRotation = M44RotationX(-TotalDeltaPos.y * PIXEL_TO_WORLD_SCALE);
+ m44 YRotation = M44RotationY(TotalDeltaPos.x * PIXEL_TO_WORLD_SCALE);
+ m44 Combined = XRotation * YRotation;
+
+ OpState->Camera->Position = (Combined * OpState->CameraStartPos).xyz;
}
FOLDHAUS_INPUT_COMMAND_PROC(End3DViewMouseRotate)
{
- DeactivateCurrentOperationMode(&State->Modes);
+ DeactivateCurrentOperationMode(&State->Modes);
}
input_command MouseRotateViewCommands [] = {
- { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, End3DViewMouseRotate},
+ { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, End3DViewMouseRotate},
};
FOLDHAUS_INPUT_COMMAND_PROC(Begin3DViewMouseRotate)
{
- sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state);
-
- operation_mode* RotateViewMode = ActivateOperationModeWithCommands(&State->Modes, MouseRotateViewCommands, Update3DViewMouseRotate);
- mouse_rotate_view_operation_state* OpState = CreateOperationState(RotateViewMode,
- &State->Modes,
- mouse_rotate_view_operation_state);
- OpState->CameraStartPos = ToV4Point(PanelState->Camera.Position);
- OpState->Camera = &PanelState->Camera;
+ sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state);
+
+ operation_mode* RotateViewMode = ActivateOperationModeWithCommands(&State->Modes, MouseRotateViewCommands, Update3DViewMouseRotate);
+ mouse_rotate_view_operation_state* OpState = CreateOperationState(RotateViewMode,
+ &State->Modes,
+ mouse_rotate_view_operation_state);
+ OpState->CameraStartPos = ToV4Point(PanelState->Camera.Position);
+ OpState->Camera = &PanelState->Camera;
}
// ----------------
@@ -63,7 +63,7 @@ FOLDHAUS_INPUT_COMMAND_PROC(Begin3DViewMouseRotate)
GSMetaTag(panel_commands);
GSMetaTag(panel_type_sculpture_view);
global input_command SculptureView_Commands[] = {
- { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, Begin3DViewMouseRotate },
+ { KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, Begin3DViewMouseRotate },
};
global s32 SculptureView_CommandsCount = 1;
@@ -72,16 +72,16 @@ GSMetaTag(panel_type_sculpture_view);
internal void
SculptureView_Init(panel* Panel, app_state* State, context Context)
{
- sculpture_view_panel_state* PanelState = PushStruct(&State->Permanent, sculpture_view_panel_state);
-
- PanelState->Camera.FieldOfView = 45.0f;
- PanelState->Camera.AspectRatio = RectAspectRatio(State->WindowBounds);
- PanelState->Camera.Near = .1f;
- PanelState->Camera.Far = 800.0f;
- PanelState->Camera.Position = v3{0, 0, 400};
- PanelState->Camera.LookAt = v3{0, 0, 0};
-
- Panel->StateMemory = StructToData(PanelState, sculpture_view_panel_state);
+ sculpture_view_panel_state* PanelState = PushStruct(&State->Permanent, sculpture_view_panel_state);
+
+ PanelState->Camera.FieldOfView = 45.0f;
+ PanelState->Camera.AspectRatio = RectAspectRatio(State->WindowBounds);
+ PanelState->Camera.Near = .1f;
+ PanelState->Camera.Far = 800.0f;
+ PanelState->Camera.Position = v3{0, 0, 400};
+ PanelState->Camera.LookAt = v3{0, 0, 0};
+
+ Panel->StateMemory = StructToData(PanelState, sculpture_view_panel_state);
}
GSMetaTag(panel_cleanup);
@@ -89,92 +89,92 @@ GSMetaTag(panel_type_sculpture_view);
internal void
SculptureView_Cleanup(panel* Panel, app_state* State)
{
-
+
}
struct draw_leds_job_data
{
- v4 CameraPosition;
- led_buffer LedBuffer;
- s32 StartIndex;
- s32 OnePastLastIndex;
- render_quad_batch_constructor* Batch;
- quad_batch_constructor_reserved_range BatchReservedRange;
- r32 LEDHalfWidth;
+ v4 CameraPosition;
+ led_buffer LedBuffer;
+ s32 StartIndex;
+ s32 OnePastLastIndex;
+ render_quad_batch_constructor* Batch;
+ quad_batch_constructor_reserved_range BatchReservedRange;
+ r32 LEDHalfWidth;
};
internal void
DrawLedsInBuffer(led_buffer LedBuffer, s32 StartIndex, s32 OnePastLastIndex, render_quad_batch_constructor* Batch, quad_batch_constructor_reserved_range ReservedRange, r32 LedHalfWidth)
{
- s32 TrisUsed = 0;
+ s32 TrisUsed = 0;
+
+ v4 P0_In = v4{-LedHalfWidth, -LedHalfWidth, 0, 1};
+ v4 P1_In = v4{LedHalfWidth, -LedHalfWidth, 0, 1};
+ v4 P2_In = v4{LedHalfWidth, LedHalfWidth, 0, 1};
+ v4 P3_In = v4{-LedHalfWidth, LedHalfWidth, 0, 1};
+
+ v2 UV0 = v2{0, 0};
+ v2 UV1 = v2{1, 0};
+ v2 UV2 = v2{1, 1};
+ v2 UV3 = v2{0, 1};
+
+ Assert(OnePastLastIndex <= (s32)LedBuffer.LedCount);
+ for (s32 LedIndex = StartIndex; LedIndex < OnePastLastIndex; LedIndex++)
+ {
+ pixel PixelColor = LedBuffer.Colors[LedIndex];
+ v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f};
- v4 P0_In = v4{-LedHalfWidth, -LedHalfWidth, 0, 1};
- v4 P1_In = v4{LedHalfWidth, -LedHalfWidth, 0, 1};
- v4 P2_In = v4{LedHalfWidth, LedHalfWidth, 0, 1};
- v4 P3_In = v4{-LedHalfWidth, LedHalfWidth, 0, 1};
+ v4 Position = LedBuffer.Positions[LedIndex];
+ v4 PositionOffset = ToV4Vec(Position.xyz);
+ v4 P0 = P0_In + PositionOffset;
+ v4 P1 = P1_In + PositionOffset;
+ v4 P2 = P2_In + PositionOffset;
+ v4 P3 = P3_In + PositionOffset;
- v2 UV0 = v2{0, 0};
- v2 UV1 = v2{1, 0};
- v2 UV2 = v2{1, 1};
- v2 UV3 = v2{0, 1};
-
- Assert(OnePastLastIndex <= (s32)LedBuffer.LedCount);
- for (s32 LedIndex = StartIndex; LedIndex < OnePastLastIndex; LedIndex++)
- {
- pixel PixelColor = LedBuffer.Colors[LedIndex];
- v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f};
-
- v4 Position = LedBuffer.Positions[LedIndex];
- v4 PositionOffset = ToV4Vec(Position.xyz);
- v4 P0 = P0_In + PositionOffset;
- v4 P1 = P1_In + PositionOffset;
- v4 P2 = P2_In + PositionOffset;
- v4 P3 = P3_In + PositionOffset;
-
- SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P1, P2, UV0, UV1, UV2, Color, Color, Color);
- SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P2, P3, UV0, UV2, UV3, Color, Color, Color);
- }
+ SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P1, P2, UV0, UV1, UV2, Color, Color, Color);
+ SetTri3DInBatch(Batch, ReservedRange.Start + TrisUsed++, P0, P2, P3, UV0, UV2, UV3, Color, Color, Color);
+ }
}
internal void
DrawLEDsInBufferRangeJob (gs_thread_context Context, gs_data JobData)
{
- DEBUG_TRACK_FUNCTION;
- draw_leds_job_data* Data = (draw_leds_job_data*)JobData.Memory;
- DrawLedsInBuffer(Data->LedBuffer, Data->StartIndex, Data->OnePastLastIndex, Data->Batch, Data->BatchReservedRange, Data->LEDHalfWidth);
+ DEBUG_TRACK_FUNCTION;
+ draw_leds_job_data* Data = (draw_leds_job_data*)JobData.Memory;
+ DrawLedsInBuffer(Data->LedBuffer, Data->StartIndex, Data->OnePastLastIndex, Data->Batch, Data->BatchReservedRange, Data->LEDHalfWidth);
}
internal void
DrawQuad(render_command_buffer* RenderBuffer, v4 C, r32 Rad, v4 Color)
{
- v4 P0 = C + v4{-Rad,-Rad,0,0};
- v4 P1 = C + v4{ Rad,-Rad,0,0};
- v4 P2 = C + v4{ Rad,Rad,0,0};
- v4 P3 = C + v4{ -Rad,Rad,0,0};
- PushRenderQuad3D(RenderBuffer, P0, P1, P2, P3, Color);
+ v4 P0 = C + v4{-Rad,-Rad,0,0};
+ v4 P1 = C + v4{ Rad,-Rad,0,0};
+ v4 P2 = C + v4{ Rad,Rad,0,0};
+ v4 P3 = C + v4{ -Rad,Rad,0,0};
+ PushRenderQuad3D(RenderBuffer, P0, P1, P2, P3, Color);
}
internal v2
SculptureView_WorldToScreenPosition(v4 WorldPosition, camera Camera, rect2 PanelBounds)
{
- v2 Result = {0};
-
- r32 PanelW = Rect2Width(PanelBounds);
- r32 PanelH = Rect2Height(PanelBounds);
-
- m44 Matrix = GetCameraPerspectiveProjectionMatrix(Camera) * GetCameraModelViewMatrix(Camera);
- v4 WorldPos = Matrix * WorldPosition;
-
- // this is the Perspective Divide
- v2 ProjectedPos = WorldPos.xy / WorldPos.w;
-
- // Projection gets us in a range [-1, 1], and we want [0, width]
- ProjectedPos.x = ((ProjectedPos.x / 2) * PanelW) + (PanelW / 2);
- ProjectedPos.y = ((ProjectedPos.y / 2) * PanelH) + (PanelH / 2);
-
- Result = ProjectedPos + PanelBounds.Min;
- return Result;
+ v2 Result = {0};
+
+ r32 PanelW = Rect2Width(PanelBounds);
+ r32 PanelH = Rect2Height(PanelBounds);
+
+ m44 Matrix = GetCameraPerspectiveProjectionMatrix(Camera) * GetCameraModelViewMatrix(Camera);
+ v4 WorldPos = Matrix * WorldPosition;
+
+ // this is the Perspective Divide
+ v2 ProjectedPos = WorldPos.xy / WorldPos.w;
+
+ // Projection gets us in a range [-1, 1], and we want [0, width]
+ ProjectedPos.x = ((ProjectedPos.x / 2) * PanelW) + (PanelW / 2);
+ ProjectedPos.y = ((ProjectedPos.y / 2) * PanelH) + (PanelH / 2);
+
+ Result = ProjectedPos + PanelBounds.Min;
+ return Result;
}
GSMetaTag(panel_render);
@@ -182,62 +182,62 @@ GSMetaTag(panel_type_sculpture_view);
internal void
SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
- DEBUG_TRACK_SCOPE(RenderSculpture);
- sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state);
- PanelState->Camera.AspectRatio = RectAspectRatio(PanelBounds);
-
- PushRenderPerspective(RenderBuffer, PanelBounds, PanelState->Camera);
-
- u32 MaxLEDsPerJob = 2048;
- render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->LedSystem.LedsCountTotal);
-
- u32 FocusPixel = 100;
-
- for (u32 BufferIndex = 0; BufferIndex < State->LedSystem.BuffersCount; BufferIndex++)
+ DEBUG_TRACK_SCOPE(RenderSculpture);
+ sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state);
+ PanelState->Camera.AspectRatio = RectAspectRatio(PanelBounds);
+
+ PushRenderPerspective(RenderBuffer, PanelBounds, PanelState->Camera);
+
+ u32 MaxLEDsPerJob = 2048;
+ render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->LedSystem.LedsCountTotal);
+
+ u32 FocusPixel = 100;
+
+ for (u32 BufferIndex = 0; BufferIndex < State->LedSystem.BuffersCount; BufferIndex++)
+ {
+ led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, BufferIndex);
+ u32 JobsNeeded = U32DivideRoundUp(LedBuffer->LedCount, MaxLEDsPerJob);
+ u32 NextLEDIndex = 0;
+ for (u32 Job = 0; Job < JobsNeeded; Job++)
{
- led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, BufferIndex);
- u32 JobsNeeded = U32DivideRoundUp(LedBuffer->LedCount, MaxLEDsPerJob);
- u32 NextLEDIndex = 0;
- for (u32 Job = 0; Job < JobsNeeded; Job++)
- {
- gs_data Data = PushSizeToData(State->Transient, sizeof(draw_leds_job_data));
- draw_leds_job_data* JobData = (draw_leds_job_data*)Data.Memory;
- JobData->LedBuffer = *LedBuffer;
- JobData->StartIndex = NextLEDIndex;
- JobData->OnePastLastIndex = Min(JobData->StartIndex + MaxLEDsPerJob, LedBuffer->LedCount);
- s32 JobLedCount = JobData->OnePastLastIndex - JobData->StartIndex;
- JobData->Batch = &RenderLEDsBatch;
- JobData->BatchReservedRange = ReserveRangeInQuadConstructor(JobData->Batch, JobLedCount * 2);
- JobData->LEDHalfWidth = .5f;
- JobData->CameraPosition = ToV4Point(PanelState->Camera.Position);
+ gs_data Data = PushSize(State->Transient, sizeof(draw_leds_job_data));
+ draw_leds_job_data* JobData = (draw_leds_job_data*)Data.Memory;
+ JobData->LedBuffer = *LedBuffer;
+ JobData->StartIndex = NextLEDIndex;
+ JobData->OnePastLastIndex = Min(JobData->StartIndex + MaxLEDsPerJob, LedBuffer->LedCount);
+ s32 JobLedCount = JobData->OnePastLastIndex - JobData->StartIndex;
+ JobData->Batch = &RenderLEDsBatch;
+ JobData->BatchReservedRange = ReserveRangeInQuadConstructor(JobData->Batch, JobLedCount * 2);
+ JobData->LEDHalfWidth = .5f;
+ JobData->CameraPosition = ToV4Point(PanelState->Camera.Position);
#if 1
- Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, (thread_proc*)DrawLEDsInBufferRangeJob, Data, ConstString("Sculpture Draw LEDS"));
+ Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, (thread_proc*)DrawLEDsInBufferRangeJob, Data, ConstString("Sculpture Draw LEDS"));
#else
- DrawLedsInBuffer(JobData->LedBuffer, JobData->StartIndex, JobData->OnePastLastIndex, JobData->Batch, JobData->BatchReservedRange, JobData->LEDHalfWidth);
+ DrawLedsInBuffer(JobData->LedBuffer, JobData->StartIndex, JobData->OnePastLastIndex, JobData->Batch, JobData->BatchReservedRange, JobData->LEDHalfWidth);
#endif
- NextLEDIndex = JobData->OnePastLastIndex;
- }
+ NextLEDIndex = JobData->OnePastLastIndex;
}
+ }
+
+ // TODO(Peter): I don't like the fact that setting an orthographic view inside a panel render function
+ // needs to relyon the window bounds rather than the panel bounds. Ideally the panel only needs to know where
+ // itself is, and nothing else.
+ PushRenderOrthographic(RenderBuffer, State->WindowBounds);
+ if (State->Assemblies.Count > 0)
+ {
+ assembly Assembly = State->Assemblies.Values[0];
+ led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly.LedBufferIndex);
- // TODO(Peter): I don't like the fact that setting an orthographic view inside a panel render function
- // needs to relyon the window bounds rather than the panel bounds. Ideally the panel only needs to know where
- // itself is, and nothing else.
- PushRenderOrthographic(RenderBuffer, State->WindowBounds);
- if (State->Assemblies.Count > 0)
- {
- assembly Assembly = State->Assemblies.Values[0];
- led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly.LedBufferIndex);
-
- v4 LedPosition = LedBuffer->Positions[FocusPixel];
- v2 LedOnScreenPosition = SculptureView_WorldToScreenPosition(LedPosition, PanelState->Camera, PanelBounds);
-
- gs_string Tempgs_string = PushString(State->Transient, 256);
- PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id,
- State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id);
- DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4);
-
- }
- Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext);
+ v4 LedPosition = LedBuffer->Positions[FocusPixel];
+ v2 LedOnScreenPosition = SculptureView_WorldToScreenPosition(LedPosition, PanelState->Camera, PanelBounds);
+
+ gs_string Tempgs_string = PushString(State->Transient, 256);
+ PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id,
+ State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id);
+ DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4, -1, GreenV4);
+
+ }
+ Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext);
}
#define FOLDHAUS_PANEL_SCULPTURE_VIEW_H
diff --git a/src/app/editor/panels/foldhaus_panel_types.cpp b/src/app/editor/panels/foldhaus_panel_types.cpp
index ea56618..db28c35 100644
--- a/src/app/editor/panels/foldhaus_panel_types.cpp
+++ b/src/app/editor/panels/foldhaus_panel_types.cpp
@@ -4,7 +4,7 @@
// Creation Date: 2020-10-17
//
#ifndef FOLDHAUS_PANEL_TYPES_CPP
-global s32 GlobalPanelDefsCount = 7;
+global s32 GlobalPanelDefsCount = 8;
global panel_definition GlobalPanelDefs[] = {
{ "File View", 9, FileView_Init, FileView_Cleanup, FileView_Render, FileView_Commands, FileView_CommandsCount },
{ "Sculpture View", 14, SculptureView_Init, SculptureView_Cleanup, SculptureView_Render, SculptureView_Commands, SculptureView_CommandsCount },
@@ -13,6 +13,7 @@ global panel_definition GlobalPanelDefs[] = {
{ "Hierarchy", 9, HierarchyView_Init, HierarchyView_Cleanup, HierarchyView_Render, HierarchyView_Commands, HierarchyView_CommandsCount },
{ "Profiler", 8, ProfilerView_Init, ProfilerView_Cleanup, ProfilerView_Render, ProfilerView_Commands, ProfilerView_CommandsCount },
{ "Assembly Debug", 14, AssemblyDebug_Init, AssemblyDebug_Cleanup, AssemblyDebug_Render, 0, 0 },
+ { "Message Log", 11, MessageLog_Init, MessageLog_Cleanup, MessageLog_Render, 0, 0 },
};
#define FOLDHAUS_PANEL_TYPES_CPP
#endif // FOLDHAUS_PANEL_TYPES_CPP
\ No newline at end of file
diff --git a/src/app/editor/panels/foldhaus_panel_types.h b/src/app/editor/panels/foldhaus_panel_types.h
index 9177cdd..559bc43 100644
--- a/src/app/editor/panels/foldhaus_panel_types.h
+++ b/src/app/editor/panels/foldhaus_panel_types.h
@@ -12,6 +12,7 @@ enum panel_type {
PanelType_HierarchyView,
PanelType_ProfilerView,
PanelType_AssemblyDebug,
+ PanelType_MessageLog
};
#define FOLDHAUS_PANEL_TYPES_H
#endif // FOLDHAUS_PANEL_TYPES_H
\ No newline at end of file
diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h
index d93f3f1..f0011f2 100644
--- a/src/app/engine/animation/foldhaus_animation.h
+++ b/src/app/engine/animation/foldhaus_animation.h
@@ -5,18 +5,18 @@
//
#ifndef FOLDHAUS_ANIMATION
-#define ANIMATION_PROC(name) void name(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+#define ANIMATION_PROC(name) void name(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
typedef ANIMATION_PROC(animation_proc);
struct frame_range
{
- s32 Min;
- s32 Max;
+ s32 Min;
+ s32 Max;
};
struct animation_pattern_handle
{
- s32 IndexPlusOne;
+ s32 IndexPlusOne;
};
// NOTE(pjs): An animation block is a time range paired with an
@@ -25,109 +25,157 @@ struct animation_pattern_handle
// will run
struct animation_block
{
- frame_range Range;
- animation_pattern_handle AnimationProcHandle;
- u32 Layer;
+ frame_range Range;
+ animation_pattern_handle AnimationProcHandle;
+ u32 Layer;
};
struct animation_block_array
{
- u32* Generations;
- animation_block* Values;
- u32 Count;
- u32 CountMax;
+ u32* Generations;
+ animation_block* Values;
+ u32 Count;
+ u32 CountMax;
};
enum blend_mode
{
- BlendMode_Overwrite,
- BlendMode_Add,
- BlendMode_Multiply,
- BlendMode_Count,
+ BlendMode_Overwrite,
+ BlendMode_Add,
+ BlendMode_Multiply,
+ BlendMode_Count,
};
-// TODO(pjs): Add Opacity to this
-typedef pixel led_blend_proc(pixel PixelA, pixel PixelB);
-
-global gs_const_string BlendModeStrings[] = {
- ConstString("Overwrite"),
- ConstString("Add"),
- ConstString("Multiply"),
- ConstString("Count"),
+// TODO(pjs): This really doesn't belong here
+global gs_string BlendModeStrings[] = {
+ MakeString("Overwrite"),
+ MakeString("Add"),
+ MakeString("Multiply"),
+ MakeString("Count"),
};
struct anim_layer
{
- gs_string Name;
- blend_mode BlendMode;
+ gs_string Name;
+ blend_mode BlendMode;
};
struct anim_layer_array
{
- anim_layer* Values;
- u32 Count;
- u32 CountMax;
+ anim_layer* Values;
+ u32 Count;
+ u32 CountMax;
};
// NOTE(pjs): An animation is a stack of layers, each of which
// is a timeline of animation blocks.
struct animation
{
- gs_string Name;
-
- anim_layer_array Layers;
- // TODO(pjs): Pretty sure Blocks_ should be obsolete and
- // Layers should contain their own blocks
- animation_block_array Blocks_;
-
- frame_range PlayableRange;
-
- // The information / path to the file where this animation is to be saved / where it is loaded from
- gs_file_info FileInfo;
+ gs_string Name;
+
+ anim_layer_array Layers;
+ animation_block_array Blocks_;
+
+ frame_range PlayableRange;
+
+ // The information / path to the file where this animation is to be saved / where it is loaded from
+ gs_file_info FileInfo;
};
+struct animation_handle
+{
+ s32 Index;
+};
+
+struct animation_handle_array
+{
+ u32 Count;
+ animation_handle* Handles;
+};
+
+internal animation_handle InvalidAnimHandle () { return { -1 }; }
+internal bool IsValid (animation_handle H) { return H.Index >= 0; }
+internal void Clear (animation_handle* H) { H->Index = -1; }
+internal bool AnimHandlesAreEqual (animation_handle A, animation_handle B)
+{
+ return A.Index == B.Index;
+}
+
struct animation_array
{
- animation* Values;
- u32 Count;
- u32 CountMax;
+ animation* Values;
+ u32 Count;
+ u32 CountMax;
};
struct animation_layer_frame
{
- animation_block Hot;
- bool HasHot;
-
- animation_block NextHot;
- bool HasNextHot;
-
- r32 HotOpacity;
+ animation_block Hot;
+ bool HasHot;
+
+ animation_block NextHot;
+ bool HasNextHot;
+
+ r32 NextHotOpacity;
+
+ blend_mode BlendMode;
};
// NOTE(pjs): This is an evaluated frame - across all layers in an
// animation, these are the blocks that need to be run
struct animation_frame
{
- animation_layer_frame* Layers;
- u32 LayersCount;
+ animation_layer_frame* Layers;
+ u32 LayersCount;
+};
+
+enum animation_repeat_mode
+{
+ AnimationRepeat_Single,
+ AnimationRepeat_Loop,
+ AnimationRepeat_Invalid,
+};
+
+global gs_const_string AnimationRepeatModeStrings[] = {
+ ConstString("Repeat Single"),
+ ConstString("Loop"),
+ ConstString("Invalid"),
+};
+
+struct animation_fade_group
+{
+ animation_handle From;
+ animation_handle To;
+ r32 FadeElapsed;
+ r32 FadeDuration;
};
#define ANIMATION_SYSTEM_LAYERS_MAX 128
#define ANIMATION_SYSTEM_BLOCKS_MAX 128
struct animation_system
{
- gs_memory_arena* Storage;
- animation_array Animations;
-
- // NOTE(Peter): The frame currently being displayed/processed. you
- // can see which frame you're on by looking at the time slider on the timeline
- // panel
- u32 ActiveAnimationIndex;
- s32 CurrentFrame;
- s32 LastUpdatedFrame;
- r32 SecondsPerFrame;
- b32 TimelineShouldAdvance;
-
+ gs_memory_arena* Storage;
+ animation_array Animations;
+
+ animation_repeat_mode RepeatMode;
+ animation_handle_array Playlist;
+ u32 PlaylistAt;
+ r32 PlaylistFadeTime;
+
+ // NOTE(Peter): The frame currently being displayed/processed. you
+ // can see which frame you're on by looking at the time slider on the timeline
+ // panel
+ animation_fade_group ActiveFadeGroup;
+
+ r32 SecondsOnCurrentFrame;
+ s32 CurrentFrame;
+ s32 LastUpdatedFrame;
+ r32 SecondsPerFrame;
+ b32 TimelineShouldAdvance;
+ u32 UpdatesThisFrame;
+
+ // Settings
+ bool Multithreaded;
};
// NOTE(pjs): A Pattern is a named procedure which can be used as
@@ -135,69 +183,70 @@ struct animation_system
// and blended via layers to create an animation
struct animation_pattern
{
- char* Name;
- s32 NameLength;
- animation_proc* Proc;
+ char* Name;
+ s32 NameLength;
+ animation_proc* Proc;
+ bool Multithreaded;
};
struct animation_pattern_array
{
- animation_pattern* Values;
- u32 Count;
- u32 CountMax;
+ animation_pattern* Values;
+ u32 Count;
+ u32 CountMax;
};
// Serialization
enum animation_field
{
- AnimField_FileIdent,
- AnimField_AnimName,
- AnimField_LayersCount,
- AnimField_BlocksCount,
-
- AnimField_PlayableRange,
- AnimField_PlayableRangeMin,
- AnimField_PlayableRangeMax,
-
- AnimField_LayersArray,
- AnimField_Layer,
- AnimField_LayerName,
- AnimField_LayerBlendMode,
-
- AnimField_BlocksArray,
- AnimField_Block,
- AnimField_BlockFrameRange,
- AnimField_BlockFrameRangeMin,
- AnimField_BlockFrameRangeMax,
- AnimField_BlockLayerIndex,
- AnimField_BlockAnimName,
-
- AnimField_Count,
+ AnimField_FileIdent,
+ AnimField_AnimName,
+ AnimField_LayersCount,
+ AnimField_BlocksCount,
+
+ AnimField_PlayableRange,
+ AnimField_PlayableRangeMin,
+ AnimField_PlayableRangeMax,
+
+ AnimField_LayersArray,
+ AnimField_Layer,
+ AnimField_LayerName,
+ AnimField_LayerBlendMode,
+
+ AnimField_BlocksArray,
+ AnimField_Block,
+ AnimField_BlockFrameRange,
+ AnimField_BlockFrameRangeMin,
+ AnimField_BlockFrameRangeMax,
+ AnimField_BlockLayerIndex,
+ AnimField_BlockAnimName,
+
+ AnimField_Count,
};
global gs_const_string AnimationFieldStrings[] = {
- ConstString("lumenarium_animation_file"), // AnimField_FileIdent
- ConstString("animation_name"),// AnimField_AnimName
- ConstString("layers_count"),// AnimField_LayersCount
- ConstString("blocks_count"),// AnimField_BlocksCount
-
- ConstString("playable_range"),// AnimField_PlayableRange
- ConstString("min"),// AnimField_PlayableRangeMin
- ConstString("max"),// AnimField_PlayableRangeMax
-
- ConstString("layers"),// AnimField_LayersArray
- ConstString("layer"),// AnimField_Layer
- ConstString("name"),// AnimField_LayerName
- ConstString("blend"),// AnimField_LayerBlendMode
-
- ConstString("blocks"),// AnimField_BlocksArray
- ConstString("block"),// AnimField_Block
- ConstString("frame_range"),// AnimField_BlockFrameRange
- ConstString("min"),// AnimField_BlockFrameRangeMin
- ConstString("max"),// AnimField_BlockFrameRangeMax
- ConstString("layer_index"),// AnimField_BlockLayerIndex
- ConstString("animation_name"),// AnimField_BlockAnimName
+ ConstString("lumenarium_animation_file"), // AnimField_FileIdent
+ ConstString("animation_name"),// AnimField_AnimName
+ ConstString("layers_count"),// AnimField_LayersCount
+ ConstString("blocks_count"),// AnimField_BlocksCount
+
+ ConstString("playable_range"),// AnimField_PlayableRange
+ ConstString("min"),// AnimField_PlayableRangeMin
+ ConstString("max"),// AnimField_PlayableRangeMax
+
+ ConstString("layers"),// AnimField_LayersArray
+ ConstString("layer"),// AnimField_Layer
+ ConstString("name"),// AnimField_LayerName
+ ConstString("blend"),// AnimField_LayerBlendMode
+
+ ConstString("blocks"),// AnimField_BlocksArray
+ ConstString("block"),// AnimField_Block
+ ConstString("frame_range"),// AnimField_BlockFrameRange
+ ConstString("min"),// AnimField_BlockFrameRangeMin
+ ConstString("max"),// AnimField_BlockFrameRangeMax
+ ConstString("layer_index"),// AnimField_BlockLayerIndex
+ ConstString("animation_name"),// AnimField_BlockAnimName
};
@@ -208,45 +257,58 @@ global gs_const_string AnimationFieldStrings[] = {
internal animation_pattern_array
Patterns_Create(gs_memory_arena* Arena, s32 CountMax)
{
- animation_pattern_array Result = {0};
- Result.CountMax = CountMax;
- Result.Values = PushArray(Arena, animation_pattern, Result.CountMax);
- return Result;
+ animation_pattern_array Result = {0};
+ Result.CountMax = CountMax;
+ Result.Values = PushArray(Arena, animation_pattern, Result.CountMax);
+ return Result;
}
-#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)))
+#define PATTERN_MULTITHREADED true
+#define PATTERN_SINGLETHREADED false
+
+#define Patterns_PushPattern(array, proc, multithread) \
+Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1, (multithread))
+
internal void
-Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength)
+Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength, bool Multithreaded)
{
- Assert(Array->Count < Array->CountMax);
-
- animation_pattern Pattern = {0};
- Pattern.Name = Name;
- Pattern.NameLength = NameLength;
- Pattern.Proc = Proc;
-
- Array->Values[Array->Count++] = Pattern;
+ Assert(Array->Count < Array->CountMax);
+
+ animation_pattern Pattern = {0};
+ Pattern.Name = Name;
+ Pattern.NameLength = NameLength;
+ Pattern.Proc = Proc;
+ Pattern.Multithreaded = Multithreaded;
+
+ Array->Values[Array->Count++] = Pattern;
}
internal animation_pattern_handle
Patterns_IndexToHandle(s32 Index)
{
- animation_pattern_handle Result = {};
- Result.IndexPlusOne = Index + 1;
- return Result;
+ animation_pattern_handle Result = {};
+ Result.IndexPlusOne = Index + 1;
+ return Result;
+}
+
+internal bool
+IsValid(animation_pattern_handle Handle)
+{
+ bool Result = Handle.IndexPlusOne > 0;
+ return Result;
}
internal animation_pattern
Patterns_GetPattern(animation_pattern_array Patterns, animation_pattern_handle Handle)
{
- animation_pattern Result = {0};
- if (Handle.IndexPlusOne > 0)
- {
- u32 Index = Handle.IndexPlusOne - 1;
- Assert(Index < Patterns.Count);
- Result = Patterns.Values[Index];
- }
- return Result;
+ animation_pattern Result = {0};
+ if (Handle.IndexPlusOne > 0)
+ {
+ u32 Index = Handle.IndexPlusOne - 1;
+ Assert(Index < Patterns.Count);
+ Result = Patterns.Values[Index];
+ }
+ return Result;
}
//////////////////////////
@@ -256,44 +318,44 @@ Patterns_GetPattern(animation_pattern_array Patterns, animation_pattern_handle H
internal animation_block_array
AnimBlockArray_Create(gs_memory_arena* Storage, u32 CountMax)
{
- animation_block_array Result = {0};
- Result.CountMax = CountMax;
- Result.Values = PushArray(Storage, animation_block, Result.CountMax);
- Result.Generations = PushArray(Storage, u32, Result.CountMax);
- return Result;
+ animation_block_array Result = {0};
+ Result.CountMax = Max(CountMax, 32);
+ Result.Values = PushArray(Storage, animation_block, Result.CountMax);
+ Result.Generations = PushArray(Storage, u32, Result.CountMax);
+ return Result;
}
internal handle
AnimBlockArray_Push(animation_block_array* Array, animation_block Value)
{
- Assert(Array->Count < Array->CountMax);
- handle Result = {0};
- Result.Index = Array->Count++;
- // NOTE(pjs): pre-increment so that generation 0 is always invalid
- Result.Generation = ++Array->Generations[Result.Index];
-
- Array->Values[Result.Index] = Value;
-
- return Result;
+ Assert(Array->Count < Array->CountMax);
+ handle Result = {0};
+ Result.Index = Array->Count++;
+ // NOTE(pjs): pre-increment so that generation 0 is always invalid
+ Result.Generation = ++Array->Generations[Result.Index];
+
+ Array->Values[Result.Index] = Value;
+
+ return Result;
}
internal void
AnimBlockArray_Remove(animation_block_array* Array, handle Handle)
{
- Assert(Handle.Index < Array->Count);
- Assert(Handle_IsValid(Handle));
- Array->Generations[Handle.Index]++;
+ Assert(Handle.Index < Array->Count);
+ Assert(Handle_IsValid(Handle));
+ Array->Generations[Handle.Index]++;
}
internal void
AnimBlockArray_RemoveAt(animation_block_array* Array, u32 Index)
{
- Assert(Index < Array->Count);
-
- handle Handle = {};
- Handle.Index = Index;
- Handle.Generation = Array->Generations[Index];
- AnimBlockArray_Remove(Array, Handle);
+ Assert(Index < Array->Count);
+
+ handle Handle = {};
+ Handle.Index = Index;
+ Handle.Generation = Array->Generations[Index];
+ AnimBlockArray_Remove(Array, Handle);
}
//////////////////////////
@@ -303,29 +365,29 @@ AnimBlockArray_RemoveAt(animation_block_array* Array, u32 Index)
internal anim_layer_array
AnimLayerArray_Create(gs_memory_arena* Storage, u32 CountMax)
{
- anim_layer_array Result = {0};
- Result.CountMax = CountMax;
- Result.Values = PushArray(Storage, anim_layer, Result.CountMax);
- return Result;
+ anim_layer_array Result = {0};
+ Result.CountMax = Max(CountMax, 32);
+ Result.Values = PushArray(Storage, anim_layer, Result.CountMax);
+ return Result;
}
internal u32
AnimLayerArray_Push(anim_layer_array* Array, anim_layer Value)
{
- Assert(Array->Count < Array->CountMax);
- u32 Index = Array->Count++;
- Array->Values[Index] = Value;
- return Index;
+ Assert(Array->Count < Array->CountMax);
+ u32 Index = Array->Count++;
+ Array->Values[Index] = Value;
+ return Index;
}
internal void
AnimLayerArray_Remove(anim_layer_array* Array, u32 Index)
{
- Assert(Index < Array->Count);
- for (u32 i = Index; i < Array->Count - 1; i++)
- {
- Array->Values[i] = Array->Values[i + 1];
- }
+ Assert(Index < Array->Count);
+ for (u32 i = Index; i < Array->Count - 1; i++)
+ {
+ Array->Values[i] = Array->Values[i + 1];
+ }
}
//////////////////////////
@@ -335,92 +397,146 @@ AnimLayerArray_Remove(anim_layer_array* Array, u32 Index)
internal animation_array
AnimationArray_Create(gs_memory_arena* Storage, u32 CountMax)
{
- animation_array Result = {0};
- Result.CountMax = CountMax;
- Result.Values = PushArray(Storage, animation, Result.CountMax);
- return Result;
+ animation_array Result = {0};
+ Result.CountMax = CountMax;
+ Result.Values = PushArray(Storage, animation, Result.CountMax);
+ return Result;
}
-internal u32
+internal animation_handle
AnimationArray_Push(animation_array* Array, animation Value)
{
- Assert(Array->Count < Array->CountMax);
- u32 Index = Array->Count++;
- Array->Values[Index] = Value;
- return Index;
+ Assert(Array->Count < Array->CountMax);
+ animation_handle Result = {0};
+ Result.Index = Array->Count++;
+ Array->Values[Result.Index] = Value;
+ return Result;
+}
+
+internal animation*
+AnimationArray_Get(animation_array Array, animation_handle Handle)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ animation* Result = 0;
+ if (IsValid(Handle) && Handle.Index < (s32)Array.Count)
+ {
+ Result = Array.Values + Handle.Index;
+ }
+ return Result;
+}
+
+internal animation*
+AnimationArray_GetSafe(animation_array Array, animation_handle Handle)
+{
+ Assert(IsValid(Handle));
+ Assert(Handle.Index < (s32)Array.Count);
+ return AnimationArray_Get(Array, Handle);
}
//////////////////////////
//
// Animation
+typedef struct animation_desc
+{
+ u32 NameSize;
+ char* Name;
+
+ u32 LayersCount;
+ u32 BlocksCount;
+
+ u32 MinFrames;
+ u32 MaxFrames;
+} animation_desc;
+
+internal animation
+Animation_Create(animation_desc Desc, animation_system* System)
+{
+ animation Result = {};
+ u32 NameLen = Desc.NameSize;
+ if (Desc.Name)
+ {
+ NameLen = Max(CStringLength(Desc.Name), NameLen);
+ Result.Name = PushStringF(System->Storage, NameLen, "%s", Desc.Name);
+ } else {
+ Result.Name = PushStringF(System->Storage, NameLen, "[New Animation]");
+ }
+
+ Result.Layers = AnimLayerArray_Create(System->Storage, Desc.LayersCount);
+ Result.Blocks_ = AnimBlockArray_Create(System->Storage, Desc.BlocksCount);
+ Result.PlayableRange.Min = Desc.MinFrames;
+ Result.PlayableRange.Max = Desc.MaxFrames;
+ return Result;
+}
+
internal handle
Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, animation_pattern_handle AnimationProcHandle, u32 LayerIndex)
{
- Assert(LayerIndex < Animation->Layers.Count);
-
- animation_block NewBlock = {0};
- NewBlock.Range.Min = StartFrame;
- NewBlock.Range.Max = EndFrame;
- NewBlock.AnimationProcHandle = AnimationProcHandle;
- NewBlock.Layer = LayerIndex;
-
- handle Handle = AnimBlockArray_Push(&Animation->Blocks_, NewBlock);
- return Handle;
+ Assert(LayerIndex < Animation->Layers.Count);
+
+ animation_block NewBlock = {0};
+ NewBlock.Range.Min = StartFrame;
+ NewBlock.Range.Max = EndFrame;
+ NewBlock.AnimationProcHandle = AnimationProcHandle;
+ NewBlock.Layer = LayerIndex;
+
+ handle Handle = AnimBlockArray_Push(&Animation->Blocks_, NewBlock);
+ return Handle;
}
internal void
Animation_RemoveBlock(animation* Animation, handle AnimHandle)
{
- AnimBlockArray_Remove(&Animation->Blocks_, AnimHandle);
+ AnimBlockArray_Remove(&Animation->Blocks_, AnimHandle);
}
internal animation_block*
Animation_GetBlockFromHandle(animation* Animation, handle AnimHandle)
{
- animation_block* Result = 0;
-
- if (AnimHandle.Generation != 0 &&
- Animation->Blocks_.Generations[AnimHandle.Index] == AnimHandle.Generation)
- {
- Result = Animation->Blocks_.Values + AnimHandle.Index;
- }
-
- return Result;
+ animation_block* Result = 0;
+
+ if (AnimHandle.Generation != 0 &&
+ Animation->Blocks_.Generations[AnimHandle.Index] == AnimHandle.Generation)
+ {
+ Result = Animation->Blocks_.Values + AnimHandle.Index;
+ }
+
+ return Result;
}
internal u32
Animation_AddLayer(animation* Animation, anim_layer Layer)
{
- return AnimLayerArray_Push(&Animation->Layers, Layer);
+ return AnimLayerArray_Push(&Animation->Layers, Layer);
}
internal u32
Animation_AddLayer (animation* Animation, gs_string Name, blend_mode BlendMode, animation_system* System)
{
- anim_layer NewLayer = {0};
- NewLayer.Name = PushStringF(System->Storage, 256, "%S", Name);
- NewLayer.BlendMode = BlendMode;
-
- return Animation_AddLayer(Animation, NewLayer);
+ anim_layer NewLayer = {0};
+ NewLayer.Name = PushStringF(System->Storage, 256, "%S", Name);
+ NewLayer.BlendMode = BlendMode;
+
+ return Animation_AddLayer(Animation, NewLayer);
}
internal void
Animation_RemoveLayer (animation* Animation, u32 LayerIndex)
{
- AnimLayerArray_Remove(&Animation->Layers, LayerIndex);
- for (u32 i = Animation->Blocks_.Count - 1; i >= 0; i--)
+ AnimLayerArray_Remove(&Animation->Layers, LayerIndex);
+ for (u32 i = Animation->Blocks_.Count - 1; i >= 0; i--)
+ {
+ animation_block* Block = Animation->Blocks_.Values + i;
+ if (Block->Layer > LayerIndex)
{
- animation_block* Block = Animation->Blocks_.Values + i;
- if (Block->Layer > LayerIndex)
- {
- Block->Layer -= 1;
- }
- else if (Block->Layer == LayerIndex)
- {
- AnimBlockArray_RemoveAt(&Animation->Blocks_, i);
- }
+ Block->Layer -= 1;
}
+ else if (Block->Layer == LayerIndex)
+ {
+ AnimBlockArray_RemoveAt(&Animation->Blocks_, i);
+ }
+ }
}
//////////////////////////
@@ -430,160 +546,297 @@ Animation_RemoveLayer (animation* Animation, u32 LayerIndex)
internal u32
SecondsToFrames(r32 Seconds, animation_system System)
{
- u32 Result = Seconds * (1.0f / System.SecondsPerFrame);
- return Result;
+ u32 Result = Seconds * (1.0f / System.SecondsPerFrame);
+ return Result;
+}
+
+inline frame_range
+FrameRange_Overlap(frame_range A, frame_range B)
+{
+ frame_range Result = {};
+
}
inline bool
FrameIsInRange(frame_range Range, s32 Frame)
{
- bool Result = (Frame >= Range.Min) && (Frame <= Range.Max);
- return Result;
+ bool Result = (Frame >= Range.Min) && (Frame <= Range.Max);
+ return Result;
}
internal u32
GetFrameCount(frame_range Range)
{
- u32 Result = (u32)Max(0, Range.Max - Range.Min);
- return Result;
+ u32 Result = (u32)Max(0, Range.Max - Range.Min);
+ return Result;
}
internal r32
FrameToPercentRange(s32 Frame, frame_range Range)
{
- r32 Result = (r32)(Frame - Range.Min);
- Result = Result / GetFrameCount(Range);
- return Result;
+ r32 Result = (r32)(Frame - Range.Min);
+ Result = Result / GetFrameCount(Range);
+ return Result;
}
internal s32
PercentToFrameInRange(r32 Percent, frame_range Range)
{
- s32 Result = Range.Min + (s32)(Percent * GetFrameCount(Range));
- return Result;
+ s32 Result = Range.Min + (s32)(Percent * GetFrameCount(Range));
+ return Result;
}
internal s32
ClampFrameToRange(s32 Frame, frame_range Range)
{
- s32 Result = Frame;
- if (Result < Range.Min)
- {
- Result = Range.Min;
- }
- else if (Result > Range.Max)
- {
- Result = Range.Max;
- }
- return Result;
+ s32 Result = Frame;
+ if (Result < Range.Min)
+ {
+ Result = Range.Min;
+ }
+ else if (Result > Range.Max)
+ {
+ Result = Range.Max;
+ }
+ return Result;
}
// Blocks
// Layers
+// Fade Group
+
+internal bool
+AnimationFadeGroup_ShouldRender (animation_fade_group FadeGroup)
+{
+ return IsValid(FadeGroup.From);
+}
+
+internal void
+AnimationFadeGroup_Advance(animation_fade_group* Group)
+{
+ Group->From = Group->To;
+ Clear(&Group->To);
+ Group->FadeElapsed = 0;
+ Group->FadeDuration = 0;
+}
+
+internal void
+AnimationFadeGroup_Update(animation_fade_group* Group, r32 DeltaTime)
+{
+ if (IsValid(Group->To))
+ {
+ r32 FadeBefore = Group->FadeElapsed;
+ Group->FadeElapsed += DeltaTime;
+
+ if (Group->FadeElapsed >= Group->FadeDuration)
+ {
+ AnimationFadeGroup_Advance(Group);
+ }
+ }
+}
+
+internal void
+AnimationFadeGroup_FadeTo(animation_fade_group* Group, animation_handle To, r32 Duration)
+{
+ if (IsValid(Group->From))
+ {
+ // complete current fade if there is one in progress
+ if (IsValid(Group->To))
+ {
+ AnimationFadeGroup_Advance(Group);
+ }
+
+ Group->To = To;
+ Group->FadeDuration = Duration;
+ }
+ else
+ {
+ Group->From = To;
+ }
+}
+
// System
struct animation_system_desc
{
- gs_memory_arena* Storage;
- u32 AnimArrayCount;
- r32 SecondsPerFrame;
+ gs_memory_arena* Storage;
+ u32 AnimArrayCount;
+ r32 SecondsPerFrame;
};
internal animation_system
AnimationSystem_Init(animation_system_desc Desc)
{
- animation_system Result = {};
- Result.Storage = Desc.Storage;
- Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount);
- Result.SecondsPerFrame = Desc.SecondsPerFrame;
-
- return Result;
+ animation_system Result = {};
+ Result.Storage = Desc.Storage;
+ Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount);
+ Result.SecondsPerFrame = Desc.SecondsPerFrame;
+
+ Clear(&Result.ActiveFadeGroup.From);
+ Clear(&Result.ActiveFadeGroup.To);
+ Result.ActiveFadeGroup.FadeElapsed = 0;
+
+ // Settings
+ Result.Multithreaded = false;
+
+ return Result;
}
internal animation*
AnimationSystem_GetActiveAnimation(animation_system* System)
{
- // TODO(pjs): need a way to specify the active animation
- return System->Animations.Values + System->ActiveAnimationIndex;
+ return AnimationArray_Get(System->Animations, System->ActiveFadeGroup.From);
}
internal animation_frame
-AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_arena* Arena)
+AnimationSystem_CalculateAnimationFrame(animation_system* System,
+ animation* Animation,
+ gs_memory_arena* Arena)
{
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
+ DEBUG_TRACK_FUNCTION;
+
+ animation_frame Result = {0};
+ Result.LayersCount = Animation->Layers.Count;
+ Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount);
+ ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount);
+
+ for (u32 l = 0; l < Animation->Layers.Count; l++)
+ {
+ animation_layer_frame* Layer = Result.Layers + l;
+ Layer->BlendMode = Animation->Layers.Values[l].BlendMode;
+ }
+
+ for (u32 i = 0; i < Animation->Blocks_.Count; i++)
+ {
+ animation_block Block = Animation->Blocks_.Values[i];
- animation_frame Result = {0};
- Result.LayersCount = ActiveAnim->Layers.Count;
- Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount);
- ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount);
-
- for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++)
+ if (FrameIsInRange(Block.Range, System->CurrentFrame))
{
- animation_block Block = ActiveAnim->Blocks_.Values[i];
+ animation_layer_frame* Layer = Result.Layers + Block.Layer;
+ if (Layer->HasHot)
+ {
+ // NOTE(pjs): With current implementation, we don't allow
+ // animations to hvae more than 2 concurrent blocks in the
+ // timeline
+ Assert(!Layer->HasNextHot);
- if (FrameIsInRange(Block.Range, System->CurrentFrame))
+ // NOTE(pjs): Make sure that Hot comes before NextHot
+ if (Layer->Hot.Range.Min < Block.Range.Min)
{
- animation_layer_frame* Layer = Result.Layers + Block.Layer;
- if (Layer->HasHot)
- {
- // NOTE(pjs): With current implementation, we don't allow
- // animations to hvae more than 2 concurrent blocks in the
- // timeline
- Assert(!Layer->HasNextHot);
-
- // NOTE(pjs): Make sure that Hot comes before NextHot
- if (Layer->Hot.Range.Min < Block.Range.Min)
- {
- Layer->NextHot = Block;
- }
- else
- {
- Layer->NextHot = Layer->Hot;
- Layer->Hot = Block;
- }
- Layer->HasNextHot = true;
-
- frame_range BlendRange = {};
- BlendRange.Min = Layer->NextHot.Range.Min;
- BlendRange.Max = Layer->Hot.Range.Max;
- Layer->HotOpacity = 1.0f - FrameToPercentRange(System->CurrentFrame, BlendRange);
- }
- else
- {
- Layer->Hot = Block;
- Layer->HotOpacity = 1.0f;
- Layer->HasHot = true;
- }
+ Layer->NextHot = Block;
}
+ else
+ {
+ Layer->NextHot = Layer->Hot;
+ Layer->Hot = Block;
+ }
+ Layer->HasNextHot = true;
+
+ frame_range BlendRange = {};
+ BlendRange.Min = Layer->NextHot.Range.Min;
+ BlendRange.Max = Layer->Hot.Range.Max;
+ Layer->NextHotOpacity = FrameToPercentRange(System->CurrentFrame, BlendRange);
+ }
+ else
+ {
+ Layer->Hot = Block;
+ Layer->NextHotOpacity = 0.0f;
+ Layer->HasHot = true;
+ }
}
-
- return Result;
+ }
+
+ return Result;
}
internal void
-AnimationSystem_Update(animation_system* System)
+AnimationSystem_Update(animation_system* System, r32 DeltaTime)
{
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
- if (System->TimelineShouldAdvance) {
- // TODO(Peter): Revisit this. This implies that the framerate of the animation system
- // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure
- System->CurrentFrame += 1;
-
- // Loop back to the beginning
- if (System->CurrentFrame > ActiveAnim->PlayableRange.Max)
- {
- System->CurrentFrame = 0;
- }
+ if (!System->TimelineShouldAdvance) { return; }
+ if (!AnimationFadeGroup_ShouldRender(System->ActiveFadeGroup)) { return; }
+
+ System->UpdatesThisFrame = 0;
+
+ AnimationFadeGroup_Update(&System->ActiveFadeGroup, DeltaTime);
+
+ animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
+ if (ActiveAnim)
+ {
+ System->SecondsOnCurrentFrame += DeltaTime;
+ while (System->SecondsOnCurrentFrame > System->SecondsPerFrame)
+ {
+ System->CurrentFrame += 1;
+ System->SecondsOnCurrentFrame -= System->SecondsPerFrame;
+ System->UpdatesThisFrame += 1;
}
+
+ // Loop back to the beginning
+ if (System->CurrentFrame > ActiveAnim->PlayableRange.Max)
+ {
+ // NOTE(PS): There's no long term reason why this needs to be true
+ // but I don't want to implement dealing with PlayableRanges that
+ // don't start at zero right now becuse there's literally no reason
+ // I can think of where that's useful.
+ Assert(ActiveAnim->PlayableRange.Min == 0);
+
+ s32 FramesPastEnd = System->CurrentFrame;
+ while (FramesPastEnd > ActiveAnim->PlayableRange.Max)
+ {
+ FramesPastEnd -= ActiveAnim->PlayableRange.Max;
+ }
+
+ switch (System->RepeatMode)
+ {
+ case AnimationRepeat_Single:
+ {
+ System->CurrentFrame = 0;
+ }break;
+
+ case AnimationRepeat_Loop:
+ {
+ Assert(System->Playlist.Count > 0);
+ u32 NextIndex = System->PlaylistAt;
+ System->PlaylistAt = (System->PlaylistAt + 1) % System->Playlist.Count;
+ animation_handle Next = System->Playlist.Handles[NextIndex];
+
+ AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup,
+ Next,
+ System->PlaylistFadeTime);
+ System->CurrentFrame = 0;
+ }break;
+
+ InvalidDefaultCase;
+ }
+ }
+ }
+}
+
+internal void
+AnimationSystem_FadeToPlaylist(animation_system* System, animation_handle_array Playlist)
+{
+ System->Playlist = Playlist;
+ System->PlaylistAt = 0;
+
+ if (System->Playlist.Count > 0)
+ {
+ AnimationFadeGroup_FadeTo(&System->ActiveFadeGroup, Playlist.Handles[0], System->PlaylistFadeTime);
+ }
}
inline bool
AnimationSystem_NeedsRender(animation_system System)
{
- bool Result = (System.CurrentFrame != System.LastUpdatedFrame);
- return Result;
+ bool Result = (System.CurrentFrame != System.LastUpdatedFrame);
+ return Result;
+}
+
+inline r32
+AnimationSystem_GetCurrentTime(animation_system System)
+{
+ r32 Result = System.CurrentFrame * System.SecondsPerFrame;
+ return Result;
}
#define FOLDHAUS_ANIMATION
diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp
index 8915d59..6f7d243 100644
--- a/src/app/engine/animation/foldhaus_animation_renderer.cpp
+++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp
@@ -6,89 +6,273 @@
#ifndef FOLDHAUS_ANIMATION_RENDERER_CPP
internal pixel
-LedBlend_Overwrite(pixel PixelA, pixel PixelB)
+LedBlend_Overwrite(pixel PixelA, pixel PixelB, u8* UserData)
{
- return PixelB;
+ r32 MagB = (r32)(PixelB.R + PixelB.G + PixelB.B) / (255 * 3);
+
+ pixel Result = {};
+ Result.R = (u8)LerpR32(MagB, PixelA.R, PixelB.R);
+ Result.G = (u8)LerpR32(MagB, PixelA.G, PixelB.G);
+ Result.B = (u8)LerpR32(MagB, PixelA.B, PixelB.B);
+
+#if 0
+ pixel Result = PixelB;
+ if (PixelB.R == 0 &&
+ PixelB.G == 0 &&
+ PixelB.B == 0)
+ {
+ Result = PixelA;
+ }
+#endif
+ return Result;
}
internal pixel
-LedBlend_Overwrite(pixel PixelA, pixel PixelB, r32 Opacity)
+LedBlend_Lerp(pixel PixelA, pixel PixelB, u8* UserData)
{
- pixel Result = {};
-
- r32 BOpacity = 1.0f - Opacity;
- Result.R = (u8)((PixelA.R * Opacity) + (PixelB.R * BOpacity));
- Result.G = (u8)((PixelA.G * Opacity) + (PixelB.G * BOpacity));
- Result.B = (u8)((PixelA.B * Opacity) + (PixelB.B * BOpacity));
- return Result;
+ r32 BOpacity = *(r32*)UserData;
+
+ pixel Result = {};
+
+ r32 AOpacity = 1.0f - BOpacity;
+ Result.R = (u8)((PixelA.R * AOpacity) + (PixelB.R * BOpacity));
+ Result.G = (u8)((PixelA.G * AOpacity) + (PixelB.G * BOpacity));
+ Result.B = (u8)((PixelA.B * AOpacity) + (PixelB.B * BOpacity));
+ return Result;
}
internal pixel
-LedBlend_Add(pixel PixelA, pixel PixelB)
+LedBlend_Add(pixel PixelA, pixel PixelB, u8* UserData)
{
- pixel Result = {};
-
- u32 R = (u32)PixelA.R + (u32)PixelB.R;
- u32 G = (u32)PixelA.G + (u32)PixelB.G;
- u32 B = (u32)PixelA.B + (u32)PixelB.B;
-
- Result.R = (u8)Min(R, (u32)255);
- Result.G = (u8)Min(G, (u32)255);
- Result.B = (u8)Min(B, (u32)255);
-
- return Result;
+ pixel Result = {};
+
+ u32 R = (u32)PixelA.R + (u32)PixelB.R;
+ u32 G = (u32)PixelA.G + (u32)PixelB.G;
+ u32 B = (u32)PixelA.B + (u32)PixelB.B;
+
+ Result.R = (u8)Min(R, (u32)255);
+ Result.G = (u8)Min(G, (u32)255);
+ Result.B = (u8)Min(B, (u32)255);
+
+ return Result;
}
internal pixel
-LedBlend_Multiply(pixel PixelA, pixel PixelB)
+LedBlend_Multiply(pixel PixelA, pixel PixelB, u8* UserData)
{
- pixel Result = {};
-
- r32 DR = (r32)PixelA.R / 255.f;
- r32 DG = (r32)PixelA.G / 255.f;
- r32 DB = (r32)PixelA.B / 255.f;
-
- r32 SR = (r32)PixelB.R / 255.f;
- r32 SG = (r32)PixelB.G / 255.f;
- r32 SB = (r32)PixelB.B / 255.f;
-
- Result.R = (u8)((DR * SR) * 255.f);
- Result.G = (u8)((DG * SG) * 255.f);
- Result.B = (u8)((DB * SB) * 255.f);
-
- return Result;
+ pixel Result = {};
+
+ r32 DR = (r32)PixelA.R / 255.f;
+ r32 DG = (r32)PixelA.G / 255.f;
+ r32 DB = (r32)PixelA.B / 255.f;
+
+ r32 SR = (r32)PixelB.R / 255.f;
+ r32 SG = (r32)PixelB.G / 255.f;
+ r32 SB = (r32)PixelB.B / 255.f;
+
+ Result.R = (u8)((DR * SR) * 255.f);
+ Result.G = (u8)((DG * SG) * 255.f);
+ Result.B = (u8)((DB * SB) * 255.f);
+
+ return Result;
}
internal pixel
-LedBlend_Overlay(pixel PixelA, pixel PixelB)
+LedBlend_Overlay(pixel PixelA, pixel PixelB, u8* UserData)
{
- pixel Result = {};
- return Result;
+ pixel Result = {};
+ return Result;
}
internal led_blend_proc*
LedBlend_GetProc(blend_mode BlendMode)
{
- led_blend_proc* Result = 0;
- switch (BlendMode)
+ led_blend_proc* Result = 0;
+ switch (BlendMode)
+ {
+ case BlendMode_Overwrite: { Result = LedBlend_Overwrite; }break;
+ case BlendMode_Add: { Result = LedBlend_Add; }break;
+ case BlendMode_Multiply: { Result = LedBlend_Multiply; }break;
+ InvalidDefaultCase;
+ }
+ return Result;
+}
+
+struct pattern_args
+{
+ assembly Assembly;
+ gs_memory_arena* Transient;
+ u8* UserData;
+};
+
+struct render_anim_to_led_buffer_job_data
+{
+ animation_pattern Pattern;
+ led_buffer Buffer;
+ led_buffer_range BufferRange;
+ pattern_args PatternArgs;
+ r32 SecondsIntoBlock;
+};
+
+internal void
+AnimationSystem_RenderAnimationToLedBufferJob(gs_thread_context Context, gs_data Data)
+{
+ render_anim_to_led_buffer_job_data JobData = *(render_anim_to_led_buffer_job_data*)Data.Memory;
+ JobData.Pattern.Proc(&JobData.Buffer,
+ JobData.BufferRange,
+ JobData.PatternArgs.Assembly,
+ JobData.SecondsIntoBlock,
+ JobData.PatternArgs.Transient,
+ JobData.PatternArgs.UserData);
+}
+
+#define MULTITHREAD_PATTERN_RENDERING 1
+
+internal void
+AnimationSystem_BeginRenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, animation_pattern_array Patterns, pattern_args PatternArgs,
+ context Context)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min;
+ r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame;
+
+ animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle);
+ Assert(Pattern.Proc);
+
+ if (System->Multithreaded && Pattern.Multithreaded)
+ {
+ u32 JobsCount = 4;
+ u32 LedsPerJob = Buffer->LedCount / JobsCount;
+
+ for (u32 i = 0; i < JobsCount; i++)
{
- case BlendMode_Overwrite: { Result = LedBlend_Overwrite; }break;
- case BlendMode_Add: { Result = LedBlend_Add; }break;
- case BlendMode_Multiply: { Result = LedBlend_Multiply; }break;
- InvalidDefaultCase;
+ gs_data Data = PushSize(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data));
+ render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory;
+ JobData->Pattern = Pattern;
+ JobData->Buffer = *Buffer;
+ JobData->BufferRange.First = LedsPerJob * i;
+ JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1);
+ JobData->PatternArgs = PatternArgs;
+ JobData->SecondsIntoBlock = SecondsIntoBlock;
+
+ Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue,
+ (thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob,
+ Data,
+ ConstString("Render Pattern To Buffer"));
}
- return Result;
+ }
+ else
+ {
+ led_buffer_range Range = {};
+ Range.First = 0;
+ Range.OnePastLast = Buffer->LedCount;
+
+ Pattern.Proc(Buffer, Range, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData);
+ }
}
internal void
-AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern_array Patterns, gs_memory_arena* Transient,
- u8* UserData)
+AnimationSystem_EndRenderBlockToLedBuffer (animation_system* System, context Context)
{
- u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min;
- r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame;
+ if (System->Multithreaded)
+ {
+ Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext);
+ }
+}
+
+// NOTE(pjs): This mirrors animation_layer_frame to account
+// for overlapping
+struct layer_led_buffer
+{
+ led_buffer HotBuffer;
+ led_buffer NextHotBuffer;
+};
+
+internal led_buffer
+RenderAnimationToLedBuffer (animation_system* System,
+ pattern_args PatternArgs,
+ animation_frame CurrFrame,
+ layer_led_buffer* LayerBuffers,
+ led_buffer* AssemblyLedBuffer,
+ animation_pattern_array Patterns,
+ gs_memory_arena* Transient,
+ context Context)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
+
+ // Create the LayerLEDBuffers
+ for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
+ {
+ layer_led_buffer TempBuffer = {};
- animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle);
- Pattern.Proc(Buffer, Assembly, SecondsIntoBlock, Transient, UserData);
+ if (CurrFrame.Layers[Layer].HasHot)
+ {
+ TempBuffer.HotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
+
+ if (CurrFrame.Layers[Layer].HasNextHot)
+ {
+ TempBuffer.NextHotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
+ }
+ }
+
+ LayerBuffers[Layer] = TempBuffer;
+ }
+
+ // Render Each layer's block to the appropriate temp buffer
+ for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
+ {
+ animation_layer_frame LayerFrame = CurrFrame.Layers[Layer];
+ if (LayerFrame.HasHot)
+ {
+ led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer;
+ animation_block Block = LayerFrame.Hot;
+ AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context);
+ }
+
+ if (LayerFrame.HasNextHot)
+ {
+ led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer;
+ animation_block Block = LayerFrame.NextHot;
+ AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context);
+ }
+
+ AnimationSystem_EndRenderBlockToLedBuffer(System, Context);
+ }
+
+ // Blend together any layers that have a hot and next hot buffer
+ for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
+ {
+ animation_layer_frame LayerFrame = CurrFrame.Layers[Layer];
+ layer_led_buffer LayerBuffer = LayerBuffers[Layer];
+ if (LayerFrame.HasNextHot)
+ {
+ LedBuffer_Blend(LayerBuffer.HotBuffer,
+ LayerBuffer.NextHotBuffer,
+ &LayerBuffer.HotBuffer,
+ LedBlend_Lerp,
+ (u8*)&LayerFrame.NextHotOpacity);
+ }
+ }
+
+ // Consolidate Temp Buffers back into AssemblyLedBuffer
+ // We do this in reverse order so that they go from top to bottom
+ for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
+ {
+ if (CurrFrame.Layers[Layer].HasHot)
+ {
+ led_blend_proc* Blend = LedBlend_GetProc(CurrFrame.Layers[Layer].BlendMode);
+ LedBuffer_Blend(AccBuffer,
+ LayerBuffers[Layer].HotBuffer,
+ &AccBuffer,
+ Blend,
+ 0);
+ }
+ }
+
+ return AccBuffer;
}
internal void
@@ -96,106 +280,95 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse
led_system* LedSystem,
animation_pattern_array Patterns,
gs_memory_arena* Transient,
+ context Context,
u8* UserData)
{
- s32 CurrentFrame = System->CurrentFrame;
- r32 FrameTime = CurrentFrame * System->SecondsPerFrame;
+ DEBUG_TRACK_FUNCTION;
+
+ r32 FrameTime = AnimationSystem_GetCurrentTime(*System);
+
+#if 1
+ animation_array Animations = System->Animations;
+ animation_fade_group FadeGroup = System->ActiveFadeGroup;
+
+ animation* FromAnim = AnimationArray_Get(Animations, FadeGroup.From);
+ animation_frame FromFrame = AnimationSystem_CalculateAnimationFrame(System, FromAnim, Transient);
+ layer_led_buffer* FromLayerBuffers = PushArray(Transient, layer_led_buffer, FromFrame.LayersCount);
+
+ animation* ToAnim = AnimationArray_Get(Animations, FadeGroup.To);
+ animation_frame ToFrame = {0};
+ layer_led_buffer* ToLayerBuffers = 0;
+ if (ToAnim)
+ {
+ ToFrame = AnimationSystem_CalculateAnimationFrame(System, ToAnim, Transient);
+ ToLayerBuffers = PushArray(Transient, layer_led_buffer, ToFrame.LayersCount);
+ }
+
+ for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++)
+ {
+ assembly Assembly = Assemblies.Values[AssemblyIndex];
+ led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
- animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
- animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, Transient);
+ pattern_args PatternArgs = {};
+ PatternArgs.Assembly = Assembly;
+ PatternArgs.Transient = Transient;
+ PatternArgs.UserData = UserData;
- // NOTE(pjs): This mirrors animation_layer_frame to account
- // for overlapping
- struct layer_led_buffer
- {
- led_buffer HotBuffer;
- led_buffer NextHotBuffer;
- };
+ led_buffer FromBuffer = RenderAnimationToLedBuffer(System,
+ PatternArgs,
+ FromFrame,
+ FromLayerBuffers,
+ AssemblyLedBuffer,
+ Patterns,
+ Transient,
+ Context);
+ led_buffer ConsolidatedBuffer = FromBuffer;
- layer_led_buffer* LayerBuffers = PushArray(Transient, layer_led_buffer, CurrFrame.LayersCount);
-
- for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++)
- {
- assembly* Assembly = &Assemblies.Values[AssemblyIndex];
- led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex);
-
- // Create the LayerLEDBuffers
- for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
- {
- layer_led_buffer TempBuffer = {};
- if (CurrFrame.Layers[Layer].HasHot)
- {
- TempBuffer.HotBuffer.LedCount = AssemblyLedBuffer->LedCount;
- TempBuffer.HotBuffer.Positions = AssemblyLedBuffer->Positions;
- TempBuffer.HotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount);
- LedBuffer_ClearToBlack(&TempBuffer.HotBuffer);
- }
-
- if (CurrFrame.Layers[Layer].HasNextHot)
- {
- TempBuffer.NextHotBuffer.LedCount = AssemblyLedBuffer->LedCount;
- TempBuffer.NextHotBuffer.Positions = AssemblyLedBuffer->Positions;
- TempBuffer.NextHotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount);
- LedBuffer_ClearToBlack(&TempBuffer.NextHotBuffer);
- }
-
- LayerBuffers[Layer] = TempBuffer;
- }
-
- // Render Each layer's block to the appropriate temp buffer
- for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
- {
- animation_layer_frame LayerFrame = CurrFrame.Layers[Layer];
- if (LayerFrame.HasHot)
- {
- led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer;
- animation_block Block = LayerFrame.Hot;
- AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData);
- }
-
- if (LayerFrame.HasNextHot)
- {
- led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer;
- animation_block Block = LayerFrame.NextHot;
- AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData);
- }
- }
-
- // Blend together any layers that have a hot and next hot buffer
- for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
- {
- animation_layer_frame LayerFrame = CurrFrame.Layers[Layer];
- layer_led_buffer LayerBuffer = LayerBuffers[Layer];
- if (LayerFrame.HasNextHot)
- {
- for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++)
- {
- pixel A = LayerBuffer.HotBuffer.Colors[LED];
- pixel B = LayerBuffer.NextHotBuffer.Colors[LED];
- LayerBuffer.HotBuffer.Colors[LED] = LedBlend_Overwrite(A, B, LayerFrame.HotOpacity);
- }
- }
- }
-
- // Consolidate Temp Buffers
- // We do this in reverse order so that they go from top to bottom
- for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
- {
- if (CurrFrame.Layers[Layer].HasHot)
- {
- led_blend_proc* Blend = LedBlend_GetProc(ActiveAnim->Layers.Values[Layer].BlendMode);
- Assert(Blend != 0);
- for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++)
- {
- pixel A = AssemblyLedBuffer->Colors[LED];
- pixel B = LayerBuffers[Layer].HotBuffer.Colors[LED];
- AssemblyLedBuffer->Colors[LED] = Blend(A, B);
- }
- }
- }
+ if (ToAnim) {
+ led_buffer ToBuffer = RenderAnimationToLedBuffer(System,
+ PatternArgs,
+ ToFrame,
+ ToLayerBuffers,
+ AssemblyLedBuffer,
+ Patterns,
+ Transient,
+ Context);
+
+ r32 BlendPercent = FadeGroup.FadeElapsed / FadeGroup.FadeDuration;
+ LedBuffer_Blend(FromBuffer, ToBuffer, &ConsolidatedBuffer, LedBlend_Lerp, (u8*)&BlendPercent);
}
- System->LastUpdatedFrame = System->CurrentFrame;
+ LedBuffer_Copy(ConsolidatedBuffer, AssemblyLedBuffer);
+ }
+
+#else
+
+ animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
+ animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, ActiveAnim, Transient);
+
+ for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++)
+ {
+ assembly Assembly = Assemblies.Values[AssemblyIndex];
+ led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
+
+ pattern_args PatternArgs = {};
+ PatternArgs.Assembly = Assembly;
+ PatternArgs.Transient = Transient;
+ PatternArgs.UserData = UserData;
+
+ led_buffer AccBuffer = RenderAnimationToLedBuffer(System,
+ PatternArgs,
+ CurrFrame,
+ LayerBuffers,
+ AssemblyLedBuffer,
+ Patterns,
+ Transient);
+ LedBuffer_Copy(AccBuffer, AssemblyLedBuffer);
+ }
+
+#endif
+
+ System->LastUpdatedFrame = System->CurrentFrame;
}
#define FOLDHAUS_ANIMATION_RENDERER_CPP
diff --git a/src/app/engine/animation/foldhaus_animation_serializer.cpp b/src/app/engine/animation/foldhaus_animation_serializer.cpp
index 7e688f2..55a4422 100644
--- a/src/app/engine/animation/foldhaus_animation_serializer.cpp
+++ b/src/app/engine/animation/foldhaus_animation_serializer.cpp
@@ -32,7 +32,7 @@ AnimSerializer_Serialize(animation Anim, animation_pattern_array Patterns, gs_me
Serializer_OpenStruct(&Serializer, AnimField_Layer);
{
Serializer_WriteStringValue(&Serializer, AnimField_LayerName, LayerAt.Name.ConstString);
- Serializer_WriteStringValue(&Serializer, AnimField_LayerBlendMode, BlendModeStrings[LayerAt.BlendMode]);
+ Serializer_WriteStringValue(&Serializer, AnimField_LayerBlendMode, BlendModeStrings[LayerAt.BlendMode].ConstString);
}
Serializer_CloseStruct(&Serializer);
}
@@ -82,12 +82,10 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array
{
Result.Name = Parser_ReadStringValue(&Parser, AnimField_AnimName);
- Result.Layers.CountMax = Parser_ReadU32Value(&Parser, AnimField_LayersCount);
- Result.Layers.Values = PushArray(Arena, anim_layer, Result.Layers.CountMax);
-
- Result.Blocks_.CountMax = Parser_ReadU32Value(&Parser, AnimField_BlocksCount);
- Result.Blocks_.Generations = PushArray(Arena, u32, Result.Blocks_.CountMax);
- Result.Blocks_.Values = PushArray(Arena, animation_block, Result.Blocks_.CountMax);
+ u32 LayersNeeded = Parser_ReadU32Value(&Parser, AnimField_LayersCount);
+ u32 BlocksNeeded = Parser_ReadU32Value(&Parser, AnimField_BlocksCount);
+ Result.Layers = AnimLayerArray_Create(Arena, LayersNeeded);
+ Result.Blocks_ = AnimBlockArray_Create(Arena, BlocksNeeded);
if (Parser_ReadOpenStruct(&Parser, AnimField_PlayableRange))
{
@@ -116,7 +114,7 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array
gs_string BlendModeName = Parser_ReadStringValue(&Parser, AnimField_LayerBlendMode);
for (u32 i = 0; i < BlendMode_Count; i++)
{
- if (StringsEqual(BlendModeName.ConstString, BlendModeStrings[i]))
+ if (StringsEqual(BlendModeName, BlendModeStrings[i]))
{
Layer.BlendMode = (blend_mode)i;
break;
@@ -169,7 +167,7 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array
break;
}
}
-
+ Assert(IsValid(Block.AnimationProcHandle));
if (Parser_ReadCloseStruct(&Parser))
{
AnimBlockArray_Push(&Result.Blocks_, Block);
@@ -182,9 +180,28 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array
}
}
}
-
return Result;
}
+internal animation
+AnimParser_Parse(gs_data File, gs_memory_arena* Arena, animation_pattern_array AnimPatterns)
+{
+ gs_string FileString = MakeString((char*)File.Memory, File.Size);
+ return AnimParser_Parse(FileString, Arena, AnimPatterns);
+}
+internal animation_handle
+AnimationSystem_LoadAnimationFromFile(animation_system* System, animation_pattern_array AnimPatterns, context Context, gs_const_string FilePath)
+{
+ animation_handle NewAnimHandle = InvalidAnimHandle();
+ gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FilePath);
+ if (AnimFile.Size > 0)
+ {
+ animation NewAnim = AnimParser_Parse(AnimFile.Data, System->Storage, AnimPatterns);
+ NewAnim.FileInfo = AnimFile.FileInfo;
+ NewAnim.FileInfo.Path = PushStringF(System->Storage, AnimFile.FileInfo.Path.Length, "%S", AnimFile.FileInfo.Path).ConstString;
+ NewAnimHandle = AnimationArray_Push(&System->Animations, NewAnim);
+ }
+ return NewAnimHandle;
+}
#define FOLDHAUS_ANIMATION_SERIALIZER_CPP
#endif // FOLDHAUS_ANIMATION_SERIALIZER_CPP
\ No newline at end of file
diff --git a/src/app/engine/assembly/foldhaus_assembly.cpp b/src/app/engine/assembly/foldhaus_assembly.cpp
index 4ae1a6c..0e383f0 100644
--- a/src/app/engine/assembly/foldhaus_assembly.cpp
+++ b/src/app/engine/assembly/foldhaus_assembly.cpp
@@ -14,34 +14,35 @@
internal assembly_array
AssemblyArray_Create(u32 CountMax, gs_memory_arena* Storage)
{
- assembly_array Result = {0};
- Result.CountMax = CountMax;
- Result.Values = PushArray(Storage, assembly, Result.CountMax);
- return Result;
+ assembly_array Result = {0};
+ Result.CountMax = CountMax;
+ Result.Values = PushArray(Storage, assembly, Result.CountMax);
+ return Result;
}
internal u32
AssemblyArray_Push(assembly_array* Array, assembly Assembly)
{
- Assert(Array->Count < Array->CountMax);
- u32 Index = Array->Count++;
- Array->Values[Index] = Assembly;
- return Index;
+ Assert(Array->Count < Array->CountMax);
+ u32 Index = Array->Count++;
+ Array->Values[Index] = Assembly;
+ Array->Values[Index].AssemblyIndex = Index;
+ return Index;
}
internal assembly*
AssemblyArray_Take(assembly_array* Array)
{
- u32 Index = AssemblyArray_Push(Array, {});
- assembly* Result = Array->Values + Index;
- return Result;
+ u32 Index = AssemblyArray_Push(Array, {});
+ assembly* Result = Array->Values + Index;
+ return Result;
}
internal void
AssemblyArray_RemoveAt(assembly_array* Array, u32 Index)
{
- u32 LastAssemblyIndex = --Array->Count;
- Array->Values[Index] = Array->Values[LastAssemblyIndex];
+ u32 LastAssemblyIndex = --Array->Count;
+ Array->Values[Index] = Array->Values[LastAssemblyIndex];
}
typedef bool assembly_array_filter_proc(assembly A);
@@ -51,18 +52,18 @@ bool AssemblyFilter_OutputsViaUART(assembly A) { return A.OutputMode == NetworkP
internal assembly_array
AssemblyArray_Filter(assembly_array Array, assembly_array_filter_proc* Filter, gs_memory_arena* Storage)
{
- assembly_array Result = AssemblyArray_Create(Array.Count, Storage);
-
- for (u32 i = 0; i < Array.Count; i++)
+ assembly_array Result = AssemblyArray_Create(Array.Count, Storage);
+
+ for (u32 i = 0; i < Array.Count; i++)
+ {
+ assembly At = Array.Values[i];
+ if (Filter(At))
{
- assembly At = Array.Values[i];
- if (Filter(At))
- {
- AssemblyArray_Push(&Result, At);
- }
+ AssemblyArray_Push(&Result, At);
}
-
- return Result;
+ }
+
+ return Result;
}
///////////////////////////
@@ -74,173 +75,177 @@ AssemblyArray_Filter(assembly_array Array, assembly_array_filter_proc* Filter, g
internal led_system
LedSystem_Create(gs_allocator PlatformMemory, u32 BuffersMax)
{
- led_system Result = {};
- Result.PlatformMemory = PlatformMemory;
- // TODO(Peter): Since we have access to PlatformMemory, just realloc Buffers when we fill it up
- Result.BuffersCountMax = BuffersMax;
- Result.Buffers = AllocatorAllocArray(PlatformMemory, led_buffer, Result.BuffersCountMax);
- return Result;
+ led_system Result = {};
+ Result.PlatformMemory = PlatformMemory;
+ // TODO(Peter): Since we have access to PlatformMemory, just realloc Buffers when we fill it up
+ Result.BuffersCountMax = BuffersMax;
+ Result.Buffers = AllocArray(PlatformMemory, led_buffer, Result.BuffersCountMax, "led system");
+ return Result;
}
internal u32
LedSystemTakeFreeBuffer(led_system* System, u32 LedCount)
{
- s32 Result = -1;
-
- if (System->BuffersCount < System->BuffersCountMax)
+ s32 Result = -1;
+
+ if (System->BuffersCount < System->BuffersCountMax)
+ {
+ Result = System->BuffersCount++;
+ }
+ else
+ {
+ // NOTE(Peter): Look for a buffer that's flagged as empty
+ for (u32 i = 0; i < System->BuffersCount; i++)
{
- Result = System->BuffersCount++;
+ if (System->Buffers[i].LedCount == 0
+ && System->Buffers[i].Colors == 0
+ && System->Buffers[i].Positions == 0)
+ {
+ Result = i;
+ break;
+ }
}
- else
- {
- // NOTE(Peter): Look for a buffer that's flagged as empty
- for (u32 i = 0; i < System->BuffersCount; i++)
- {
- if (System->Buffers[i].LedCount == 0
- && System->Buffers[i].Colors == 0
- && System->Buffers[i].Positions == 0)
- {
- Result = i;
- break;
- }
- }
- Assert(Result >= 0); // NOTE(Peter): We ran out of room for led buffers
- }
-
- led_buffer* Buffer = &System->Buffers[Result];
- Buffer->LedCount = LedCount;
- Buffer->Colors = AllocatorAllocArray(System->PlatformMemory, pixel, Buffer->LedCount);
- Buffer->Positions = AllocatorAllocArray(System->PlatformMemory, v4, Buffer->LedCount);
-
- System->LedsCountTotal += LedCount;
-
- return (u32)Result;
+ Assert(Result >= 0); // NOTE(Peter): We ran out of room for led buffers
+ }
+
+ led_buffer* Buffer = &System->Buffers[Result];
+ Buffer->A = MemoryArenaCreate(KB(16),Bytes(8),System->PlatformMemory,0,0,"Led Buffer Arena");
+ Buffer->LedCount = LedCount;
+ Buffer->Colors = PushArray(&Buffer->A, pixel, Buffer->LedCount);
+ Buffer->Positions = PushArray(&Buffer->A, v4, Buffer->LedCount);
+
+ System->LedsCountTotal += LedCount;
+
+ return (u32)Result;
}
internal void
LedSystemFreeBuffer(led_system* System, u32 BufferIndex)
{
- Assert(BufferIndex < System->BuffersCountMax);
- led_buffer* Buffer = &System->Buffers[BufferIndex];
- AllocatorFreeArray(System->PlatformMemory, Buffer->Colors, pixel, Buffer->LedCount);
- AllocatorFreeArray(System->PlatformMemory, Buffer->Positions, v4, Buffer->LedCount);
- System->LedsCountTotal -= Buffer->LedCount;
- *Buffer = {};
+ Assert(BufferIndex < System->BuffersCountMax);
+ led_buffer* Buffer = &System->Buffers[BufferIndex];
+ MemoryArenaFree(&Buffer->A);
+ System->LedsCountTotal -= Buffer->LedCount;
+ *Buffer = {};
}
internal void
LedBufferSetLed(led_buffer* Buffer, u32 Led, v4 Position)
{
- Assert(Led < Buffer->LedCount);
- Buffer->Positions[Led] = Position;
+ Assert(Led < Buffer->LedCount);
+ Buffer->Positions[Led] = Position;
}
internal u32
Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* StripAt, strip_gen_data GenData, v4 RootPosition, u32 LedStartIndex, u32 LedLUTStartIndex)
{
- u32 LedsAdded = 0;
-
- switch (GenData.Method)
+ u32 LedsAdded = 0;
+
+ switch (GenData.Method)
+ {
+ case StripGeneration_InterpolatePoints:
{
- case StripGeneration_InterpolatePoints:
- {
- strip_gen_interpolate_points InterpPoints = GenData.InterpolatePoints;
- v4 WS_StripStart = RootPosition + ToV4Point(InterpPoints.StartPosition * Assembly->Scale);
- v4 WS_StripEnd = RootPosition + ToV4Point(InterpPoints.EndPosition * Assembly->Scale);
-
- v4 SingleStep = (WS_StripEnd - WS_StripStart) / (r32)InterpPoints.LedCount;
- for (u32 Step = 0; Step < InterpPoints.LedCount; Step++)
- {
- s32 LedIndex = LedStartIndex + LedsAdded++;
- v4 LedPosition = WS_StripStart + (SingleStep * Step);
- LedBufferSetLed(LedBuffer, LedIndex, LedPosition);
- StripAt->LedLUT[Step + LedLUTStartIndex] = LedIndex;
- }
- }break;
-
- case StripGeneration_Sequence:
- {
- strip_gen_sequence Sequence = GenData.Sequence;
- for (u32 i = 0; i < Sequence.ElementsCount; i++)
- {
- strip_gen_data SegmentGenData = Sequence.Elements[i];
- LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, SegmentGenData, RootPosition, LedStartIndex + LedsAdded, LedsAdded);
- }
- }break;
-
- InvalidDefaultCase;
- }
+ strip_gen_interpolate_points InterpPoints = GenData.InterpolatePoints;
+ v4 WS_StripStart = RootPosition + ToV4Point(InterpPoints.StartPosition * Assembly->Scale);
+ v4 WS_StripEnd = RootPosition + ToV4Point(InterpPoints.EndPosition * Assembly->Scale);
+
+ v4 SingleStep = (WS_StripEnd - WS_StripStart) / (r32)InterpPoints.LedCount;
+ for (u32 Step = 0; Step < InterpPoints.LedCount; Step++)
+ {
+ s32 LedIndex = LedStartIndex + LedsAdded++;
+ v4 LedPosition = WS_StripStart + (SingleStep * Step);
+ LedBufferSetLed(LedBuffer, LedIndex, LedPosition);
+ StripAt->LedLUT[Step + LedLUTStartIndex] = LedIndex;
+ }
+ }break;
- return LedsAdded;
+ case StripGeneration_Sequence:
+ {
+ strip_gen_sequence Sequence = GenData.Sequence;
+ for (u32 i = 0; i < Sequence.ElementsCount; i++)
+ {
+ strip_gen_data SegmentGenData = Sequence.Elements[i];
+ LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, SegmentGenData, RootPosition, LedStartIndex + LedsAdded, LedsAdded);
+ }
+ }break;
+
+ InvalidDefaultCase;
+ }
+
+ return LedsAdded;
}
internal void
ConstructAssemblyFromDefinition (assembly* Assembly, led_system* LedSystem)
{
- Assembly->LedBufferIndex = LedSystemTakeFreeBuffer(LedSystem, Assembly->LedCountTotal);
- led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex);
+ Assembly->LedBufferIndex = LedSystemTakeFreeBuffer(LedSystem, Assembly->LedCountTotal);
+ led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex);
+
+ v4 RootPosition = ToV4Vec(Assembly->Center);
+
+ // Add Leds
+ u32 LedsAdded = 0;
+ for (u32 StripIdx = 0; StripIdx < Assembly->StripCount; StripIdx++)
+ {
+ v2_strip* StripAt = &Assembly->Strips[StripIdx];
+ StripAt->LedLUT = PushArray(&Assembly->Arena, u32, StripAt->LedCount);
- v4 RootPosition = ToV4Vec(Assembly->Center);
-
- // Add Leds
- u32 LedsAdded = 0;
- for (u32 StripIdx = 0; StripIdx < Assembly->StripCount; StripIdx++)
- {
- v2_strip* StripAt = &Assembly->Strips[StripIdx];
- StripAt->LedLUT = PushArray(&Assembly->Arena, u32, StripAt->LedCount);
-
- strip_gen_data GenData = StripAt->GenerationData;
- LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded, 0);
- }
+ strip_gen_data GenData = StripAt->GenerationData;
+ LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded, 0);
+ }
}
-internal void
-LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena* Scratch, context Context, gs_const_string Path, event_log* GlobalLog)
+internal assembly*
+LoadAssembly (assembly_array* Assemblies, led_system* LedSystem, gs_memory_arena* Scratch, context Context, gs_const_string Path, log_buffer* GlobalLog)
{
- gs_file AssemblyFile = ReadEntireFile(Context.ThreadContext.FileHandler, Path);
- if (FileNoError(AssemblyFile))
+ assembly* NewAssembly = 0;
+
+ gs_file AssemblyFile = ReadEntireFile(Context.ThreadContext.FileHandler, Path);
+ if (FileNoError(AssemblyFile))
+ {
+ gs_string AssemblyFileText = MakeString((char*)AssemblyFile.Memory);
+
+ s32 IndexOfLastSlash = FindLast(Path, '\\');
+ gs_const_string FileName = Substring(Path, IndexOfLastSlash + 1, Path.Length);
+
+ NewAssembly = AssemblyArray_Take(Assemblies);
+ NewAssembly->Arena = MemoryArenaCreate(MB(4), Bytes(8), Context.ThreadContext.Allocator, 0, 0, "Assembly Arena");
+
+ parser AssemblyParser = ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch);
+ if (AssemblyParser.Success)
{
- gs_string AssemblyFileText = MakeString((char*)AssemblyFile.Memory);
-
- s32 IndexOfLastSlash = FindLast(Path, '\\');
- gs_const_string FileName = Substring(Path, IndexOfLastSlash + 1, Path.Length);
-
- assembly* NewAssembly = AssemblyArray_Take(Assemblies);
- NewAssembly->Arena = CreateMemoryArena(Context.ThreadContext.Allocator);
-
- parser AssemblyParser = ParseAssemblyFile(NewAssembly, FileName, AssemblyFileText, Scratch);
- if (AssemblyParser.Success)
- {
- ConstructAssemblyFromDefinition(NewAssembly, LedSystem);
- }
- else
- {
- FreeMemoryArena(&NewAssembly->Arena);
- Assemblies->Count -= 1;
- }
-
- for (parser_error* ErrorAt = AssemblyParser.ErrorsRoot;
- ErrorAt != 0;
- ErrorAt = ErrorAt->Next)
- {
- OutputDebugString(ErrorAt->Message.Str);
- }
-
+ ConstructAssemblyFromDefinition(NewAssembly, LedSystem);
}
else
{
- LogError(GlobalLog, "Unable to load assembly file");
+ MemoryArenaFree(&NewAssembly->Arena);
+ Assemblies->Count -= 1;
}
+
+ for (parser_error* ErrorAt = AssemblyParser.ErrorsRoot;
+ ErrorAt != 0;
+ ErrorAt = ErrorAt->Next)
+ {
+ Log_Error(GlobalLogBuffer, ErrorAt->Message.Str);
+ }
+
+ }
+ else
+ {
+ Log_Error(GlobalLog, "Unable to load assembly file");
+ }
+
+ return NewAssembly;
}
internal void
UnloadAssembly (u32 AssemblyIndex, app_state* State, context Context)
{
- Assert(AssemblyIndex < State->Assemblies.Count);
- assembly* Assembly = &State->Assemblies.Values[AssemblyIndex];
- LedSystemFreeBuffer(&State->LedSystem, Assembly->LedBufferIndex);
- FreeMemoryArena(&Assembly->Arena);
- AssemblyArray_RemoveAt(&State->Assemblies, AssemblyIndex);
+ Assert(AssemblyIndex < State->Assemblies.Count);
+ assembly* Assembly = &State->Assemblies.Values[AssemblyIndex];
+ LedSystemFreeBuffer(&State->LedSystem, Assembly->LedBufferIndex);
+ MemoryArenaFree(&Assembly->Arena);
+ AssemblyArray_RemoveAt(&State->Assemblies, AssemblyIndex);
}
// Querying Assemblies
@@ -248,30 +253,30 @@ UnloadAssembly (u32 AssemblyIndex, app_state* State, context Context)
internal led_strip_list
AssemblyStripsGetWithTagValue(assembly Assembly, gs_const_string TagName, gs_const_string TagValue, gs_memory_arena* Storage)
{
- led_strip_list Result = {0};
- // TODO(pjs): @Optimization
- // We can probably come back here and do this allocation procedurally, or in buckets, or with
- // a linked list. But for now, I just want to get this up and running
- Result.CountMax = Assembly.StripCount;
- Result.StripIndices = PushArray(Storage, u32, Result.CountMax);
-
- u64 NameHash = HashDJB2ToU32(StringExpand(TagName));
- u64 ValueHash = 0;
- if (TagValue.Length > 0)
+ led_strip_list Result = {0};
+ // TODO(pjs): @Optimization
+ // We can probably come back here and do this allocation procedurally, or in buckets, or with
+ // a linked list. But for now, I just want to get this up and running
+ Result.CountMax = Assembly.StripCount;
+ Result.StripIndices = PushArray(Storage, u32, Result.CountMax);
+
+ u64 NameHash = HashDJB2ToU32(StringExpand(TagName));
+ u64 ValueHash = 0;
+ if (TagValue.Length > 0)
+ {
+ ValueHash = HashDJB2ToU32(StringExpand(TagValue));
+ }
+
+ for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
+ {
+ v2_strip StripAt = Assembly.Strips[StripIndex];
+ if (AssemblyStrip_HasTagValue(StripAt, NameHash, ValueHash))
{
- ValueHash = HashDJB2ToU32(StringExpand(TagValue));
+ Result.StripIndices[Result.Count++] = StripIndex;
}
-
- for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
- {
- v2_strip StripAt = Assembly.Strips[StripIndex];
- if (AssemblyStrip_HasTagValue(StripAt, NameHash, ValueHash))
- {
- Result.StripIndices[Result.Count++] = StripIndex;
- }
- }
-
- return Result;
+ }
+
+ return Result;
}
#define FOLDHAUS_ASSEMBLY_CPP
diff --git a/src/app/engine/assembly/foldhaus_assembly.h b/src/app/engine/assembly/foldhaus_assembly.h
index 677d4ff..b1b9db9 100644
--- a/src/app/engine/assembly/foldhaus_assembly.h
+++ b/src/app/engine/assembly/foldhaus_assembly.h
@@ -7,230 +7,291 @@
enum network_protocol
{
- NetworkProtocol_SACN,
- NetworkProtocol_ArtNet,
- NetworkProtocol_UART,
-
- NetworkProtocol_Count,
+ NetworkProtocol_SACN,
+ NetworkProtocol_ArtNet,
+ NetworkProtocol_UART,
+
+ NetworkProtocol_Count,
};
union pixel
{
- struct
- {
- u8 R;
- u8 G;
- u8 B;
- };
- u8 Channels[3];
+ struct
+ {
+ u8 R;
+ u8 G;
+ u8 B;
+ };
+ u8 Channels[3];
};
struct led_buffer
{
- u32 LedCount;
- pixel* Colors;
- v4* Positions;
+ // NOTE(PS): This is just a tracking structure,
+ // that enables allocations for a particular buffer
+ // to occur all in contiguous memory
+ // and should not be pushed to after the initial
+ // allocation
+ gs_memory_arena A;
+
+ u32 LedCount;
+ pixel* Colors;
+ v4* Positions;
+};
+
+struct led_buffer_range
+{
+ u32 First;
+ u32 OnePastLast;
};
struct led_system
{
- gs_allocator PlatformMemory;
-
- u32 BuffersCountMax;
- u32 BuffersCount;
- led_buffer* Buffers;
-
- u32 LedsCountTotal;
+ gs_allocator PlatformMemory;
+
+ u32 BuffersCountMax;
+ u32 BuffersCount;
+ led_buffer* Buffers;
+
+ u32 LedsCountTotal;
};
struct v2_tag
{
- u64 NameHash;
- u64 ValueHash;
+ u64 NameHash;
+ u64 ValueHash;
};
struct strip_sacn_addr
{
- s32 StartUniverse;
- s32 StartChannel;
+ s32 StartUniverse;
+ s32 StartChannel;
};
struct strip_uart_addr
{
- u8 Channel;
-
- gs_string ComPort;
- // This may not be used based on the value of the parent
- // assembly's NetworkPortMode field
+ u8 Channel;
+
+ gs_string ComPort;
+ // This may not be used based on the value of the parent
+ // assembly's NetworkPortMode field
};
enum strip_gen_method
{
- StripGeneration_InterpolatePoints,
- StripGeneration_Sequence,
-
- StripGeneration_Count,
+ StripGeneration_InterpolatePoints,
+ StripGeneration_Sequence,
+
+ StripGeneration_Count,
};
typedef struct strip_gen_data strip_gen_data;
struct strip_gen_interpolate_points
{
- v3 StartPosition;
- v3 EndPosition;
- u32 LedCount;
+ v3 StartPosition;
+ v3 EndPosition;
+ u32 LedCount;
};
struct strip_gen_sequence
{
- strip_gen_data* Elements;
- u32 ElementsCount;
+ strip_gen_data* Elements;
+ u32 ElementsCount;
};
struct strip_gen_data
{
- strip_gen_method Method;
-
- strip_gen_interpolate_points InterpolatePoints;
- strip_gen_sequence Sequence;
+ strip_gen_method Method;
+
+ strip_gen_interpolate_points InterpolatePoints;
+ strip_gen_sequence Sequence;
};
struct v2_strip
{
- s32 ControlBoxID; // TODO(Peter): I don't think we need this anymore
-
- strip_sacn_addr SACNAddr;
- strip_uart_addr UARTAddr;
-
- strip_gen_data GenerationData;
-
- u32 LedCount;
- u32* LedLUT;
-
- u32 TagsCount;
- v2_tag* Tags;
+ s32 ControlBoxID; // TODO(Peter): I don't think we need this anymore
+
+ strip_sacn_addr SACNAddr;
+ strip_uart_addr UARTAddr;
+
+ strip_gen_data GenerationData;
+
+ u32 LedCount;
+ u32* LedLUT;
+
+ u32 TagsCount;
+ v2_tag* Tags;
};
struct led_strip_list
{
- u32 Count;
- u32 CountMax;
- u32* StripIndices;
+ u32 Count;
+ u32 CountMax;
+ u32* StripIndices;
};
enum network_port_mode
{
- // This enum defines the scope which contains what network
- // port each address should be sent over.
-
- NetworkPortMode_GlobalPort,
- // GlobalPort means that the port is defined in the assembly structure
-
- NetworkPortMode_PortPerStrip,
- // PortPerStrip means that the address stored in the strip structure
- // should be used, and each strip might have a different port
-
- NetworkPortMode_Count,
+ // This enum defines the scope which contains what network
+ // port each address should be sent over.
+
+ NetworkPortMode_GlobalPort,
+ // GlobalPort means that the port is defined in the assembly structure
+
+ NetworkPortMode_PortPerStrip,
+ // PortPerStrip means that the address stored in the strip structure
+ // should be used, and each strip might have a different port
+
+ NetworkPortMode_Count,
};
struct assembly
{
- gs_memory_arena Arena;
-
- gs_string Name;
- gs_string FilePath;
-
- r32 Scale;
- v3 Center;
- s32 LedCountTotal;
- u32 LedBufferIndex;
-
- u32 StripCount;
- v2_strip* Strips;
-
- network_protocol OutputMode;
- network_port_mode NetPortMode;
- gs_string UARTComPort;
+ gs_memory_arena Arena;
+
+ u32 AssemblyIndex;
+ gs_string Name;
+ gs_string FilePath;
+
+ r32 Scale;
+ v3 Center;
+ v3 MinLedPos, MaxLedPos;
+ s32 LedCountTotal;
+ u32 LedBufferIndex;
+
+ u32 StripCount;
+ v2_strip* Strips;
+
+ network_protocol OutputMode;
+ network_port_mode NetPortMode;
+ gs_string UARTComPort;
};
struct assembly_array
{
- u32 CountMax;
- u32 Count;
- assembly* Values;
+ u32 CountMax;
+ u32 Count;
+ assembly* Values;
};
+typedef pixel led_blend_proc(pixel A, pixel B, u8* UserData);
+
internal led_buffer*
LedSystemGetBuffer(led_system* System, u32 Index)
{
- led_buffer* Result = &System->Buffers[Index];
- return Result;
+ led_buffer* Result = &System->Buffers[Index];
+ return Result;
}
internal void
LedBuffer_ClearToBlack(led_buffer* Buffer)
{
- for (u32 i = 0; i < Buffer->LedCount; i++)
- {
- Buffer->Colors[i].R = 0;
- Buffer->Colors[i].G = 0;
- Buffer->Colors[i].B = 0;
- }
+ for (u32 i = 0; i < Buffer->LedCount; i++)
+ {
+ Buffer->Colors[i].R = 0;
+ Buffer->Colors[i].G = 0;
+ Buffer->Colors[i].B = 0;
+ }
+}
+
+internal void
+LedBuffer_Copy(led_buffer From, led_buffer* To)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ Assert(From.LedCount == To->LedCount);
+ u32 LedCount = To->LedCount;
+ for (u32 i = 0; i < LedCount; i++)
+ {
+ To->Colors[i] = From.Colors[i];
+ }
+}
+
+internal void
+LedBuffer_Blend(led_buffer A, led_buffer B, led_buffer* Dest, led_blend_proc* BlendProc, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ Assert(A.LedCount == B.LedCount);
+ Assert(Dest->LedCount == A.LedCount);
+ Assert(BlendProc);
+
+ u32 LedCount = Dest->LedCount;
+ for (u32 i = 0; i < LedCount; i++)
+ {
+ pixel PA = A.Colors[i];
+ pixel PB = B.Colors[i];
+ Dest->Colors[i] = BlendProc(PA, PB, UserData);
+ }
+}
+
+internal led_buffer
+LedBuffer_CreateCopyCleared (led_buffer Buffer, gs_memory_arena* Arena)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ led_buffer Result = {};
+ Result.LedCount = Buffer.LedCount;
+ Result.Positions = Buffer.Positions;
+ Result.Colors = PushArray(Arena, pixel, Buffer.LedCount);
+ LedBuffer_ClearToBlack(&Result);
+ return Result;
}
internal u32
StripGenData_CountLeds(strip_gen_data Data)
{
- u32 Result = 0;
-
- switch (Data.Method)
+ u32 Result = 0;
+
+ switch (Data.Method)
+ {
+ case StripGeneration_InterpolatePoints:
{
- case StripGeneration_InterpolatePoints:
- {
- Result += Data.InterpolatePoints.LedCount;
- }break;
-
- case StripGeneration_Sequence:
- {
- for (u32 i = 0; i < Data.Sequence.ElementsCount; i++)
- {
- Result += StripGenData_CountLeds(Data.Sequence.Elements[i]);
- }
- }break;
-
- InvalidDefaultCase;
- }
+ Result += Data.InterpolatePoints.LedCount;
+ }break;
- return Result;
+ case StripGeneration_Sequence:
+ {
+ for (u32 i = 0; i < Data.Sequence.ElementsCount; i++)
+ {
+ Result += StripGenData_CountLeds(Data.Sequence.Elements[i]);
+ }
+ }break;
+
+ InvalidDefaultCase;
+ }
+
+ return Result;
}
internal bool
AssemblyStrip_HasTagValue(v2_strip Strip, u64 NameHash, u64 ValueHash)
{
- bool Result = false;
- for (u32 i = 0; i < Strip.TagsCount; i++)
+ bool Result = false;
+ for (u32 i = 0; i < Strip.TagsCount; i++)
+ {
+ v2_tag TagAt = Strip.Tags[i];
+ if (TagAt.NameHash == NameHash)
{
- v2_tag TagAt = Strip.Tags[i];
- if (TagAt.NameHash == NameHash)
- {
- // NOTE(pjs): We can pass an empty string to the Value parameter,
- // and it will match all values of Tag
- if (ValueHash == 0 || ValueHash == TagAt.ValueHash)
- {
- Result = true;
- break;
- }
- }
+ // NOTE(pjs): We can pass an empty string to the Value parameter,
+ // and it will match all values of Tag
+ if (ValueHash == 0 || ValueHash == TagAt.ValueHash)
+ {
+ Result = true;
+ break;
+ }
}
- return Result;
+ }
+ return Result;
}
internal bool
AssemblyStrip_HasTagValueSLOW(v2_strip Strip, char* Name, char* Value)
{
- u64 NameHash = HashDJB2ToU32(Name);
- u64 ValueHash = HashDJB2ToU32(Value);
- return AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash);
+ u64 NameHash = HashDJB2ToU32(Name);
+ u64 ValueHash = HashDJB2ToU32(Value);
+ return AssemblyStrip_HasTagValue(Strip, NameHash, ValueHash);
}
#define FOLDHAUS_ASSEMBLY_H
diff --git a/src/app/engine/assembly/foldhaus_assembly_debug.h b/src/app/engine/assembly/foldhaus_assembly_debug.h
index 07a3685..ffe33ed 100644
--- a/src/app/engine/assembly/foldhaus_assembly_debug.h
+++ b/src/app/engine/assembly/foldhaus_assembly_debug.h
@@ -16,6 +16,7 @@ enum override_type
ADS_Override_AllBlue,
ADS_Override_AllOff,
ADS_Override_AllWhite,
+ ADS_Override_AllHue,
ADS_Override_TagWhite,
ADS_Override_TagStripWhite,
ADS_Override_ChannelWhite,
@@ -32,6 +33,7 @@ global gs_const_string OverrideTypeStrings[] = {
LitString("Override_AllBlue" ),
LitString("ADS_Override_AllOff" ),
LitString("ADS_Override_AllWhite" ),
+ LitString("ADS_Override_AllHue" ),
LitString("ADS_Override_TagWhite" ),
LitString("ADS_Override_TagStripWhite" ),
LitString("ADS_Override_ChannelWhite," ),
@@ -42,12 +44,14 @@ struct assembly_debug_state
{
override_type Override;
+ bool AllAssemblies;
u32 TargetAssembly;
u32 TargetStrip;
gs_string TagName;
gs_string TagValue;
+ u32 TargetHue;
pixel TargetColor;
u32 TargetChannel;
@@ -101,12 +105,9 @@ AssemblyDebug_OverrideTagValueWithColor(assembly Assembly, led_buffer LedBuffer,
}
internal void
-AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assemblies, led_system LedSystem)
+AssemblyDebug_OverrideOutputForAssembly(assembly_debug_state State, led_system LedSystem,
+ assembly Assembly)
{
- if (State.Override == ADS_Override_None) return;
- State.TargetColor = pixel{255,255,255};
-
- assembly Assembly = Assemblies.Values[State.TargetAssembly];
led_buffer LedBuffer = LedSystem.Buffers[Assembly.LedBufferIndex];
u8 V = State.Brightness;
@@ -160,6 +161,14 @@ AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assembli
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{V, V, V});
}break;
+ case ADS_Override_AllHue:
+ {
+ v4 HSV = v4{(r32)State.TargetHue, 1, 1, 1};
+ v4 RGB = HSVToRGB(HSV);
+ pixel P = V4ToRGBPixel(RGB);
+ AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, P);
+ }break;
+
case ADS_Override_TagWhite:
{
AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0});
@@ -201,10 +210,26 @@ AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assembli
InvalidDefaultCase;
}
+}
+
+internal void
+AssemblyDebug_OverrideOutput(assembly_debug_state State, assembly_array Assemblies, led_system LedSystem)
+{
+ if (State.Override == ADS_Override_None) return;
+ State.TargetColor = pixel{255,255,255};
- if (State.Override )
+ if (State.AllAssemblies)
{
-
+ for (u32 i = 0; i < Assemblies.Count; i++)
+ {
+ assembly Assembly = Assemblies.Values[i];
+ AssemblyDebug_OverrideOutputForAssembly(State, LedSystem, Assembly);
+ }
+ }
+ else
+ {
+ assembly Assembly = Assemblies.Values[State.TargetAssembly];
+ AssemblyDebug_OverrideOutputForAssembly(State, LedSystem, Assembly);
}
}
diff --git a/src/app/engine/assembly/foldhaus_assembly_parser.cpp b/src/app/engine/assembly/foldhaus_assembly_parser.cpp
index 5024fb6..dbeb890 100644
--- a/src/app/engine/assembly/foldhaus_assembly_parser.cpp
+++ b/src/app/engine/assembly/foldhaus_assembly_parser.cpp
@@ -276,7 +276,7 @@ ParseAssemblyFile(assembly* Assembly, gs_const_string FileName, gs_string FileTe
Assembly->Name = Parser_ReadStringValue(&Parser, AssemblyField_AssemblyName);
Assembly->Scale = Parser_ReadR32Value(&Parser, AssemblyField_AssemblyScale);
- Assembly->Center = Parser_ReadV3Value(&Parser, AssemblyField_AssemblyCenter);
+ Assembly->Center = Parser_ReadV3Value(&Parser, AssemblyField_AssemblyCenter) * Assembly->Scale;
Assembly->StripCount = Parser_ReadU32Value(&Parser, AssemblyField_LedStripCount);
Assembly->Strips = PushArray(&Assembly->Arena, v2_strip, Assembly->StripCount);
diff --git a/src/app/engine/foldhaus_addressed_data.h b/src/app/engine/foldhaus_addressed_data.h
index c27bd59..e2db91e 100644
--- a/src/app/engine/foldhaus_addressed_data.h
+++ b/src/app/engine/foldhaus_addressed_data.h
@@ -11,97 +11,97 @@
enum data_buffer_address_type
{
- AddressType_NetworkIP,
- AddressType_ComPort,
- AddressType_Invalid,
+ AddressType_NetworkIP,
+ AddressType_ComPort,
+ AddressType_Invalid,
};
struct addressed_data_buffer
{
- union
+ union
+ {
+ struct
{
- struct
- {
- u8* Memory;
- u32 MemorySize;
- };
- gs_data Data;
+ u8* Memory;
+ u32 MemorySize;
};
-
- data_buffer_address_type AddressType;
-
- // IP Address
- platform_socket_handle SendSocket;
- u32 V4SendAddress;
- u32 SendPort;
-
- // COM
- gs_const_string ComPort;
-
- addressed_data_buffer* Next;
+ gs_data Data;
+ };
+
+ data_buffer_address_type AddressType;
+
+ // IP Address
+ platform_socket_handle SendSocket;
+ u32 V4SendAddress;
+ u32 SendPort;
+
+ // COM
+ gs_const_string ComPort;
+
+ addressed_data_buffer* Next;
};
struct addressed_data_buffer_list
{
- gs_memory_arena* Arena;
- addressed_data_buffer* Root;
- addressed_data_buffer* Head;
+ gs_memory_arena* Arena;
+ addressed_data_buffer* Root;
+ addressed_data_buffer* Head;
};
internal void
AddressedDataBufferList_Clear(addressed_data_buffer_list* List)
{
- List->Root = 0;
- List->Head = 0;
- ClearArena(List->Arena);
+ List->Root = 0;
+ List->Head = 0;
+ MemoryArenaClear(List->Arena);
}
internal addressed_data_buffer*
AddressedDataBufferList_PushEmpty(addressed_data_buffer_list* List)
{
- addressed_data_buffer* Result = PushStruct(List->Arena, addressed_data_buffer);
- *Result = {0};
- Result->Next = 0;
- Result->MemorySize = 0;
- Result->Memory = 0;
-
- SLLPushOrInit(List->Root, List->Head, Result);
-
- return Result;
+ addressed_data_buffer* Result = PushStruct(List->Arena, addressed_data_buffer);
+ *Result = {0};
+ Result->Next = 0;
+ Result->MemorySize = 0;
+ Result->Memory = 0;
+
+ SLLPushOrInit(List->Root, List->Head, Result);
+
+ return Result;
}
internal addressed_data_buffer*
AddressedDataBufferList_Push(addressed_data_buffer_list* List, u32 BufferSize)
{
- addressed_data_buffer* Result = AddressedDataBufferList_PushEmpty(List);
- Result->MemorySize = BufferSize;
- Result->Memory = PushArray(List->Arena, u8, Result->MemorySize);
- return Result;
+ addressed_data_buffer* Result = AddressedDataBufferList_PushEmpty(List);
+ Result->MemorySize = BufferSize;
+ Result->Memory = PushArray(List->Arena, u8, Result->MemorySize);
+ return Result;
}
internal void
AddressedDataBuffer_SetNetworkAddress(addressed_data_buffer* Buffer, platform_socket_handle SendSocket, u32 V4SendAddress, u32 SendPort)
{
- Buffer->AddressType = AddressType_NetworkIP;
- Buffer->SendSocket = SendSocket;
- Buffer->V4SendAddress = V4SendAddress;
- Buffer->SendPort = SendPort;
+ Buffer->AddressType = AddressType_NetworkIP;
+ Buffer->SendSocket = SendSocket;
+ Buffer->V4SendAddress = V4SendAddress;
+ Buffer->SendPort = SendPort;
}
internal void
AddressedDataBuffer_SetCOMPort(addressed_data_buffer* Buffer, gs_const_string ComPort)
{
- Buffer->AddressType = AddressType_ComPort;
- Buffer->ComPort = ComPort;
+ Buffer->AddressType = AddressType_ComPort;
+ Buffer->ComPort = ComPort;
}
internal addressed_data_buffer_list
AddressedDataBufferList_Create(gs_thread_context TC)
{
- addressed_data_buffer_list Result = {};
- Result.Arena = AllocatorAllocStruct(TC.Allocator, gs_memory_arena);
- *Result.Arena = CreateMemoryArena(TC.Allocator);
- return Result;
+ addressed_data_buffer_list Result = {};
+ Result.Arena = AllocStruct(TC.Allocator, gs_memory_arena, "Addressed Data");
+ *Result.Arena = MemoryArenaCreate(KB(256), Bytes(8), TC.Allocator, 0, 0, "Addressed Data Buffer List Arena");
+ return Result;
}
#define FOLDHAUS_ADDRESSED_DATA_H
diff --git a/src/app/engine/foldhaus_log.h b/src/app/engine/foldhaus_log.h
new file mode 100644
index 0000000..3d538b2
--- /dev/null
+++ b/src/app/engine/foldhaus_log.h
@@ -0,0 +1,126 @@
+/* date = April 12th 2021 4:25 pm */
+
+#ifndef FOLDHAUS_LOG_H
+#define FOLDHAUS_LOG_H
+
+enum log_entry_type
+{
+ LogEntry_Message,
+ LogEntry_Error,
+};
+
+struct log_entry
+{
+ log_entry_type Type;
+ gs_string String;
+};
+
+struct log_buffer
+{
+ gs_memory_arena* Arena;
+
+ u64 EntriesCount;
+ u64 NextEntry;
+ log_entry* Entries;
+};
+
+struct log_buffer_iter
+{
+ log_buffer* Buffer;
+ u64 Start;
+ u64 IndexAt;
+ log_entry* At;
+};
+
+internal log_buffer
+Log_Init(gs_memory_arena* A, u64 Count)
+{
+ log_buffer Result = {};
+ Result.Arena = A;
+ Result.EntriesCount = Count;
+ Result.Entries = PushArray(A, log_entry, Result.EntriesCount);
+
+ u64 LogStringLength = 512;
+ u64 LogStringBufferSize = LogStringLength * Result.EntriesCount;
+ char* LogStringBuffer = PushArray(A, char, LogStringBufferSize);
+ char* LogStringBufferAt = LogStringBuffer;
+ for (u32 i = 0; i < Result.EntriesCount; i++)
+ {
+ Result.Entries[i].String = MakeString(LogStringBufferAt, 0, LogStringLength);
+ LogStringBufferAt += LogStringLength;
+ }
+
+ return Result;
+}
+
+internal u64
+Log_GetNextIndex(log_buffer Log, u64 At)
+{
+ u64 Result = At + 1;
+ if (Result >= Log.EntriesCount)
+ {
+ Result = 0;
+ }
+ return Result;
+}
+
+internal log_entry*
+Log_TakeNextEntry(log_buffer* Log)
+{
+ log_entry* Result = Log->Entries + Log->NextEntry;
+ Log->NextEntry = Log_GetNextIndex(*Log, Log->NextEntry);
+ return Result;
+}
+
+internal void
+Log_PrintFVarArgs(log_buffer* Log, log_entry_type Type, char* Format, va_list Args)
+{
+ log_entry* NextEntry = Log_TakeNextEntry(Log);
+ NextEntry->String.Length = 0;
+ NextEntry->Type = Type;
+ PrintFArgsList(&NextEntry->String, Format, Args);
+ NullTerminate(&NextEntry->String);
+
+#if DEBUG
+ OutputDebugStringA(NextEntry->String.Str);
+#endif
+}
+
+#define Log_Message(log, fmt, ...) Log_PrintF(log, LogEntry_Message, fmt, __VA_ARGS__)
+#define Log_Error(log, fmt, ...) Log_PrintF(log, LogEntry_Error, fmt, __VA_ARGS__)
+internal void
+Log_PrintF(log_buffer* Log, log_entry_type Type, char* Format, ...)
+{
+ va_list Args;
+ va_start(Args, Format);
+ Log_PrintFVarArgs(Log, Type, Format, Args);
+ va_end(Args);
+}
+
+internal log_buffer_iter
+Log_GetIter(log_buffer* Buffer)
+{
+ log_buffer_iter Result = {};
+ Result.Buffer = Buffer;
+ Result.Start = Buffer->NextEntry;
+ Result.IndexAt = Result.Start;
+ Result.At = Result.Buffer->Entries + Result.IndexAt;
+ return Result;
+}
+
+internal bool
+LogIter_CanAdvance(log_buffer_iter Iter)
+{
+ u64 Next = Log_GetNextIndex(*Iter.Buffer, Iter.IndexAt);
+ bool Result = Next != Iter.Start;
+ return Result;
+}
+
+internal void
+LogIter_Advance(log_buffer_iter* Iter)
+{
+ Iter->IndexAt = Log_GetNextIndex(*Iter->Buffer, Iter->IndexAt);
+ Iter->At = Iter->Buffer->Entries + Iter->IndexAt;
+}
+
+#endif //FOLDHAUS_LOG_H
diff --git a/src/app/engine/foldhaus_network_ordering.h b/src/app/engine/foldhaus_network_ordering.h
index 05e5afd..cc9634d 100644
--- a/src/app/engine/foldhaus_network_ordering.h
+++ b/src/app/engine/foldhaus_network_ordering.h
@@ -10,7 +10,7 @@ inline u8*
PackB1(u8* ptr, u8 val)
{
*ptr = val;
- return ptr + sizeof(val);
+ return ptr + sizeof(val);
}
//Unpacks a u8 from a known big endian buffer
@@ -25,7 +25,7 @@ inline u8*
PackL1(u8* ptr, u8 val)
{
*ptr = val;
- return ptr + sizeof(val);
+ return ptr + sizeof(val);
}
//Unpacks a u8 from a known little endian buffer
@@ -39,19 +39,19 @@ UpackL1(const u8* ptr)
inline u16
PackB2(u16 Value)
{
- u16 Result = 0;
- u8* Array = (u8*)&Result;
- Array[1] = (u8)(Value & 0xFF);
- Array[0] = (u8)((Value & 0xFF00) >> 8);
- return Result;
+ u16 Result = 0;
+ u8* Array = (u8*)&Result;
+ Array[1] = (u8)(Value & 0xFF);
+ Array[0] = (u8)((Value & 0xFF00) >> 8);
+ return Result;
}
inline u8*
PackB2(u8* ptr, u16 val)
{
- ptr[1] = (u8)(val & 0xff);
+ ptr[1] = (u8)(val & 0xff);
ptr[0] = (u8)((val & 0xff00) >> 8);
- return ptr + sizeof(val);
+ return ptr + sizeof(val);
}
//Unpacks a u16 from a known big endian buffer
@@ -66,7 +66,7 @@ inline u8*
PackL2(u8* ptr, u16 val)
{
*((u16*)ptr) = val;
- return ptr + sizeof(val);
+ return ptr + sizeof(val);
}
//Unpacks a u16 from a known little endian buffer
@@ -84,7 +84,7 @@ PackB4(u8* ptr, u32 val)
ptr[2] = (u8)((val & 0xff00) >> 8);
ptr[1] = (u8)((val & 0xff0000) >> 16);
ptr[0] = (u8)((val & 0xff000000) >> 24);
- return ptr + sizeof(val);
+ return ptr + sizeof(val);
}
//Unpacks a u32 from a known big endian buffer
@@ -99,7 +99,7 @@ inline u8*
PackL4(u8* ptr, u32 val)
{
*((u32*)ptr) = val;
- return ptr + sizeof(val);
+ return ptr + sizeof(val);
}
//Unpacks a u32 from a known little endian buffer
@@ -121,7 +121,7 @@ PackB8(u8* ptr, u64 val)
ptr[2] = (u8)((val & 0xff0000000000) >> 40);
ptr[1] = (u8)((val & 0xff000000000000) >> 48);
ptr[0] = (u8)((val & 0xff00000000000000) >> 56);
- return ptr + sizeof(val);
+ return ptr + sizeof(val);
}
//Unpacks a uint64 from a known big endian buffer
@@ -129,9 +129,9 @@ inline u64
UpackB8(const u8* ptr)
{
return ((u64)ptr[7]) | (((u64)ptr[6]) << 8) | (((u64)ptr[5]) << 16) |
- (((u64)ptr[4]) << 24) | (((u64)ptr[3]) << 32) |
- (((u64)ptr[2]) << 40) | (((u64)ptr[1]) << 48) |
- (((u64)ptr[0]) << 56);
+ (((u64)ptr[4]) << 24) | (((u64)ptr[3]) << 32) |
+ (((u64)ptr[2]) << 40) | (((u64)ptr[1]) << 48) |
+ (((u64)ptr[0]) << 56);
}
//Packs a u64 to a known little endian buffer
@@ -139,7 +139,7 @@ inline u8*
PackL8(u8* ptr, u64 val)
{
*((u64*)ptr) = val;
- return ptr + sizeof(val);
+ return ptr + sizeof(val);
}
//Unpacks a u64 from a known little endian buffer
diff --git a/src/app/engine/sacn/foldhaus_sacn.h b/src/app/engine/sacn/foldhaus_sacn.h
index 6004361..ae29913 100644
--- a/src/app/engine/sacn/foldhaus_sacn.h
+++ b/src/app/engine/sacn/foldhaus_sacn.h
@@ -81,14 +81,14 @@ const u8 VHD_D_FLAG = 0x10;
#define CID_Bytes 16
struct cid
{
- u8 Bytes[CID_Bytes];
+ u8 Bytes[CID_Bytes];
};
struct streaming_acn
{
- platform_socket_handle SendSocket;
- cid CID;
- s32 SequenceIterator;
+ platform_socket_handle SendSocket;
+ cid CID;
+ s32 SequenceIterator;
};
///////////////////////////////////////////////
@@ -100,100 +100,100 @@ struct streaming_acn
internal void
SetStreamHeaderSequence_ (u8* Buffer, u8 Sequence, b32 Draft)
{
- DEBUG_TRACK_FUNCTION;
- PackB1(Buffer + SEQ_NUM_ADDR, Sequence);
+ DEBUG_TRACK_FUNCTION;
+ PackB1(Buffer + SEQ_NUM_ADDR, Sequence);
}
internal void
VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData)
{
- u8* Cursor = Buffer;
- u8 NewByte = UpackB1(Cursor) & 0x8f;
-
- if (!InheritVec) { NewByte |= VHD_V_FLAG; }
- if (!InheritHead) { NewByte |= VHD_H_FLAG; }
- if (!InheritData) { NewByte |= VHD_D_FLAG; }
-
- PackB1(Cursor, NewByte);
+ u8* Cursor = Buffer;
+ u8 NewByte = UpackB1(Cursor) & 0x8f;
+
+ if (!InheritVec) { NewByte |= VHD_V_FLAG; }
+ if (!InheritHead) { NewByte |= VHD_H_FLAG; }
+ if (!InheritData) { NewByte |= VHD_D_FLAG; }
+
+ PackB1(Cursor, NewByte);
}
internal u8*
VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength)
{
- u8* Cursor = Buffer;
- u32 AdjustedLength = Length;
- if (IncludeLength)
+ u8* Cursor = Buffer;
+ u32 AdjustedLength = Length;
+ if (IncludeLength)
+ {
+ if (Length + 1 > VHD_MAXMINLENGTH)
{
- if (Length + 1 > VHD_MAXMINLENGTH)
- {
- AdjustedLength += 2;
- }
- else
- {
- AdjustedLength += 1;
- }
- }
-
- // Mask out the length bits to keep flags intact
- u8 NewByte = UpackB1(Cursor) & 0x70;
- if (AdjustedLength > VHD_MAXMINLENGTH)
- {
- NewByte |= VHD_L_FLAG;
- }
-
- u8 PackBuffer[4];
- PackB4(PackBuffer, AdjustedLength);
- if (AdjustedLength <= VHD_MAXMINLENGTH)
- {
- NewByte |= (PackBuffer[2] & 0x0f);
- Cursor = PackB1(Cursor, NewByte);
- Cursor = PackB1(Cursor, PackBuffer[3]);
+ AdjustedLength += 2;
}
else
{
- NewByte |= (PackBuffer[1] & 0x0f);
- Cursor = PackB1(Cursor, PackBuffer[2]);
- Cursor = PackB1(Cursor, PackBuffer[3]);
+ AdjustedLength += 1;
}
-
- return Cursor;
+ }
+
+ // Mask out the length bits to keep flags intact
+ u8 NewByte = UpackB1(Cursor) & 0x70;
+ if (AdjustedLength > VHD_MAXMINLENGTH)
+ {
+ NewByte |= VHD_L_FLAG;
+ }
+
+ u8 PackBuffer[4];
+ PackB4(PackBuffer, AdjustedLength);
+ if (AdjustedLength <= VHD_MAXMINLENGTH)
+ {
+ NewByte |= (PackBuffer[2] & 0x0f);
+ Cursor = PackB1(Cursor, NewByte);
+ Cursor = PackB1(Cursor, PackBuffer[3]);
+ }
+ else
+ {
+ NewByte |= (PackBuffer[1] & 0x0f);
+ Cursor = PackB1(Cursor, PackBuffer[2]);
+ Cursor = PackB1(Cursor, PackBuffer[3]);
+ }
+
+ return Cursor;
}
internal cid
gs_stringToCID_ (const char* gs_string)
{
- cid Result = {};
+ cid Result = {};
+
+ const char* Src = gs_string;
+ u8* Dest = &Result.Bytes[0];
+ b32 FirstNibble = true;
+
+ while(*Src && (Dest - &Result.Bytes[0] < CID_Bytes))
+ {
+ u8 Offset = 0;
+ if ((*Src >= 0x30) && (*Src <= 0x39)){ Offset = 0x30; }
+ else if ((*Src >= 0x41) && (*Src <= 0x46)) { Offset = 0x37; }
+ else if ((*Src >= 0x61) && (*Src <= 0x66)) { Offset = 0x66; }
- const char* Src = gs_string;
- u8* Dest = &Result.Bytes[0];
- b32 FirstNibble = true;
-
- while(*Src && (Dest - &Result.Bytes[0] < CID_Bytes))
+ if (Offset != 0)
{
- u8 Offset = 0;
- if ((*Src >= 0x30) && (*Src <= 0x39)){ Offset = 0x30; }
- else if ((*Src >= 0x41) && (*Src <= 0x46)) { Offset = 0x37; }
- else if ((*Src >= 0x61) && (*Src <= 0x66)) { Offset = 0x66; }
-
- if (Offset != 0)
- {
- if (FirstNibble)
- {
- *Dest = (u8)(*Src - Offset);
- *Dest <<= 4;
- FirstNibble = false;
- }
- else
- {
- *Dest |= (*Src - Offset);
- Dest++;
- FirstNibble = true;
- }
- }
- Src++;
+ if (FirstNibble)
+ {
+ *Dest = (u8)(*Src - Offset);
+ *Dest <<= 4;
+ FirstNibble = false;
+ }
+ else
+ {
+ *Dest |= (*Src - Offset);
+ Dest++;
+ FirstNibble = true;
+ }
}
-
- return Result;
+ Src++;
+ }
+
+ return Result;
}
internal void
@@ -208,85 +208,85 @@ InitStreamHeader (u8* Buffer, s32 BufferSize,
cid CID
)
{
- // TODO(pjs): Replace packing with gs_memory_cursor
-
- u8* Cursor = Buffer;
-
- // Preamble Size
- Cursor = PackB2(Cursor, RLP_PREAMBLE_SIZE);
- Cursor = PackB2(Cursor, RLP_POSTAMBLE_SIZE);
-
- CopyMemoryTo(ACN_IDENTIFIER, Cursor, ACN_IDENTIFIER_SIZE);
- Cursor += ACN_IDENTIFIER_SIZE;
-
- // TODO(Peter): If you never use this anywhere else, go back and remove the parameters
- VHD_PackFlags_(Cursor, false, false, false);
- Cursor = VHD_PackLength_(Cursor,
- STREAM_HEADER_SIZE - RLP_PREAMBLE_SIZE + SlotCount,
- false);
-
- // root vector
- Cursor = PackB4(Cursor, ROOT_VECTOR);
-
- // CID Pack
- for (s32 i = 0; i < CID_Bytes; i++)
- {
- *Cursor++ = CID.Bytes[i];
- }
-
- VHD_PackFlags_(Cursor, false, false, false);
- Cursor = VHD_PackLength_(Cursor,
- STREAM_HEADER_SIZE - FRAMING_FLAGS_AND_LENGTH_ADDR + SlotCount,
- false);
-
- // framing vector
- Cursor = PackB4(Cursor, FRAMING_VECTOR);
-
- // framing source name
- // :Check
- CopyMemoryTo(SourceName, (char*)Cursor, SOURCE_NAME_SIZE);
- Cursor[SOURCE_NAME_SIZE - 1] = '\0';
- Cursor += SOURCE_NAME_SIZE;
-
- // priority
- Cursor = PackB1(Cursor, Priority);
-
- // reserved
- Cursor = PackB2(Cursor, Reserved);
-
- // Sequence # is always set to 0/NONE at the beginning, but it is incremented when sending data
- Cursor = PackB1(Cursor, 0);
-
- // Options
- Cursor = PackB1(Cursor, Options);
-
- // Universe
- Cursor = PackB2(Cursor, Universe);
-
- VHD_PackFlags_(Cursor, false, false, false);
- Cursor = VHD_PackLength_(Cursor,
- STREAM_HEADER_SIZE - DMP_FLAGS_AND_LENGTH_ADDR + SlotCount,
- false);
-
- // DMP Vector
- Cursor = PackB1(Cursor, DMP_VECTOR);
-
- // DMP Address and data type
- Cursor = PackB1(Cursor, ADDRESS_AND_DATA_FORMAT);
-
- // DMP first property address
- Cursor = PackB2(Cursor, 0);
-
- // DMP Address Increment
- Cursor = PackB2(Cursor, ADDRESS_INC);
-
- // Property Value Count -- Includes one byte for start code
- Cursor = PackB2(Cursor, SlotCount + 1);
-
- Cursor = PackB1(Cursor, StartCode);
-
- Assert(Cursor - Buffer == STREAM_HEADER_SIZE);
-
+ // TODO(pjs): Replace packing with gs_memory_cursor
+
+ u8* Cursor = Buffer;
+
+ // Preamble Size
+ Cursor = PackB2(Cursor, RLP_PREAMBLE_SIZE);
+ Cursor = PackB2(Cursor, RLP_POSTAMBLE_SIZE);
+
+ CopyMemoryTo(ACN_IDENTIFIER, Cursor, ACN_IDENTIFIER_SIZE);
+ Cursor += ACN_IDENTIFIER_SIZE;
+
+ // TODO(Peter): If you never use this anywhere else, go back and remove the parameters
+ VHD_PackFlags_(Cursor, false, false, false);
+ Cursor = VHD_PackLength_(Cursor,
+ STREAM_HEADER_SIZE - RLP_PREAMBLE_SIZE + SlotCount,
+ false);
+
+ // root vector
+ Cursor = PackB4(Cursor, ROOT_VECTOR);
+
+ // CID Pack
+ for (s32 i = 0; i < CID_Bytes; i++)
+ {
+ *Cursor++ = CID.Bytes[i];
+ }
+
+ VHD_PackFlags_(Cursor, false, false, false);
+ Cursor = VHD_PackLength_(Cursor,
+ STREAM_HEADER_SIZE - FRAMING_FLAGS_AND_LENGTH_ADDR + SlotCount,
+ false);
+
+ // framing vector
+ Cursor = PackB4(Cursor, FRAMING_VECTOR);
+
+ // framing source name
+ // :Check
+ CopyMemoryTo(SourceName, (char*)Cursor, SOURCE_NAME_SIZE);
+ Cursor[SOURCE_NAME_SIZE - 1] = '\0';
+ Cursor += SOURCE_NAME_SIZE;
+
+ // priority
+ Cursor = PackB1(Cursor, Priority);
+
+ // reserved
+ Cursor = PackB2(Cursor, Reserved);
+
+ // Sequence # is always set to 0/NONE at the beginning, but it is incremented when sending data
+ Cursor = PackB1(Cursor, 0);
+
+ // Options
+ Cursor = PackB1(Cursor, Options);
+
+ // Universe
+ Cursor = PackB2(Cursor, Universe);
+
+ VHD_PackFlags_(Cursor, false, false, false);
+ Cursor = VHD_PackLength_(Cursor,
+ STREAM_HEADER_SIZE - DMP_FLAGS_AND_LENGTH_ADDR + SlotCount,
+ false);
+
+ // DMP Vector
+ Cursor = PackB1(Cursor, DMP_VECTOR);
+
+ // DMP Address and data type
+ Cursor = PackB1(Cursor, ADDRESS_AND_DATA_FORMAT);
+
+ // DMP first property address
+ Cursor = PackB1(Cursor, 0);
+
+ // DMP Address Increment
+ Cursor = PackB1(Cursor, ADDRESS_INC);
+
+ // Property Value Count -- Includes one byte for start code
+ Cursor = PackB2(Cursor, SlotCount + 1);
+
+ Cursor = PackB1(Cursor, StartCode);
+
+ Assert(Cursor - Buffer == STREAM_HEADER_SIZE);
+
}
//
@@ -296,13 +296,13 @@ InitStreamHeader (u8* Buffer, s32 BufferSize,
internal streaming_acn
SACN_Initialize (context Context)
{
- streaming_acn SACN = {};
-
- s32 Multicast_TimeToLive = 20;
- SACN.SendSocket = Context.PlatformGetSocketHandle(Multicast_TimeToLive);
- SACN.CID = gs_stringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}");
-
- return SACN;
+ streaming_acn SACN = {};
+
+ s32 Multicast_TimeToLive = 20;
+ SACN.SendSocket = Context.PlatformGetSocketHandle(Multicast_TimeToLive);
+ SACN.CID = gs_stringToCID_ ("{67F9D986-544E-4abb-8986-D5F79382586C}");
+
+ return SACN;
}
internal void
@@ -313,82 +313,98 @@ SACN_Cleanup(streaming_acn* SACN, context Context)
internal void
SACN_UpdateSequence (streaming_acn* SACN)
{
- // Never use 0 after the first one
- if (++SACN->SequenceIterator == 0)
- {
- ++SACN->SequenceIterator;
- }
+ // Never use 0 after the first one
+ if (++SACN->SequenceIterator == 0)
+ {
+ ++SACN->SequenceIterator;
+ }
}
internal void
SACN_PrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReservedForHeader, streaming_acn SACN)
{
- Assert(SizeReservedForHeader == STREAM_HEADER_SIZE);
- Assert(Buffer && BufferSize > 0);
-
- s32 Priority = 0;
- InitStreamHeader(Buffer, BufferSize, STREAM_BODY_SIZE, STARTCODE_DMX, Universe, Priority, 0, 0, "Lumenarium", SACN.CID);
- SetStreamHeaderSequence_(Buffer, SACN.SequenceIterator, false);
+ Assert(SizeReservedForHeader == STREAM_HEADER_SIZE);
+ Assert(Buffer && BufferSize > 0);
+
+ s32 Priority = 0;
+ InitStreamHeader(Buffer, BufferSize, STREAM_BODY_SIZE, STARTCODE_DMX, Universe, Priority, 0, 0, "Lumenarium", SACN.CID);
+ SetStreamHeaderSequence_(Buffer, SACN.SequenceIterator, false);
}
internal u32
SACN_GetUniverseSendAddress(s32 Universe)
{
- u8 MulticastAddressBuffer[4] = {};
- MulticastAddressBuffer[0] = 239;
- MulticastAddressBuffer[1] = 255;
- MulticastAddressBuffer[2] = (u8)((Universe & 0xff00) >> 8); // high bit
- MulticastAddressBuffer[3] = (u8)((Universe & 0x00ff)); // low bit
-
- u32 V4Address = (u32)UpackB4(MulticastAddressBuffer);
- return V4Address;
+ u8 MulticastAddressBuffer[4] = {};
+ MulticastAddressBuffer[0] = 239;
+ MulticastAddressBuffer[1] = 255;
+ MulticastAddressBuffer[2] = (u8)((Universe & 0xff00) >> 8); // high bit
+ MulticastAddressBuffer[3] = (u8)((Universe & 0x00ff)); // low bit
+
+ u32 V4Address = (u32)UpackB4(MulticastAddressBuffer);
+ return V4Address;
}
-internal void
-SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, led_buffer LedBuffer)
+internal u64
+SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, u64 LedsPlaced, led_buffer LedBuffer)
{
- u8* DestChannel = BufferStart;
- for (u32 i = 0; i < Strip.LedCount; i++)
- {
- u32 LedIndex = Strip.LedLUT[i];
- pixel Color = LedBuffer.Colors[LedIndex];
-
- DestChannel[0] = Color.R;
- DestChannel[1] = Color.G;
- DestChannel[2] = Color.B;
- DestChannel += 3;
- }
+ u8* DestChannel = BufferStart;
+ u64 FirstLed = LedsPlaced;
+ u64 LedsToAdd = Min(Strip.LedCount - LedsPlaced, STREAM_BODY_SIZE / 3);
+ u64 OnePastLastLed = FirstLed + LedsToAdd;
+ for (u32 i = FirstLed; i < OnePastLastLed; i++)
+ {
+ u32 LedIndex = Strip.LedLUT[i];
+ pixel Color = LedBuffer.Colors[LedIndex];
+
+ DestChannel[0] = Color.R;
+ DestChannel[1] = Color.G;
+ DestChannel[2] = Color.B;
+ DestChannel += 3;
+ }
+ return LedsToAdd;
}
internal void
SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem)
{
- SACN_UpdateSequence(SACN);
+ SACN_UpdateSequence(SACN);
+
+ // TODO(pjs): 512 is a magic number - make it a constant?
+ s32 BufferHeaderSize = STREAM_HEADER_SIZE;
+ s32 BufferBodySize = STREAM_BODY_SIZE;
+ s32 BufferSize = BufferHeaderSize + BufferBodySize;
+
+ for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++)
+ {
+ assembly Assembly = Assemblies.Values[AssemblyIdx];
+ led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
- // TODO(pjs): 512 is a magic number - make it a constant?
- s32 BufferHeaderSize = STREAM_HEADER_SIZE;
- s32 BufferBodySize = 512;
- s32 BufferSize = BufferHeaderSize + BufferBodySize;
-
- for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++)
+ for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++)
{
- assembly Assembly = Assemblies.Values[AssemblyIdx];
- led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
+ v2_strip StripAt = Assembly.Strips[StripIdx];
+
+ // NOTE(PS): This isn't actually invalid, we just haven't needed to implement
+ // something more complex than only allowing strips to start at the first
+ // channel of a universe
+ Assert(StripAt.SACNAddr.StartChannel == 1);
+
+ u32 UniverseAt = StripAt.SACNAddr.StartUniverse;
+ u64 LedsPlaced = 0;
+ while (LedsPlaced < StripAt.LedCount)
+ {
+ u32 V4SendAddress = SACN_GetUniverseSendAddress(UniverseAt);
+ u32 SendPort = DEFAULT_STREAMING_ACN_PORT;
- for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++)
- {
- v2_strip StripAt = Assembly.Strips[StripIdx];
-
- u32 V4SendAddress = SACN_GetUniverseSendAddress(StripAt.SACNAddr.StartUniverse);
- u32 SendPort = DEFAULT_STREAMING_ACN_PORT;
-
- addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize);
- AddressedDataBuffer_SetNetworkAddress(Data, SACN->SendSocket, V4SendAddress, SendPort);
-
- SACN_PrepareBufferHeader(StripAt.SACNAddr.StartUniverse, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN);
- SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, *LedBuffer);
- }
+ addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize);
+ AddressedDataBuffer_SetNetworkAddress(Data, SACN->SendSocket, V4SendAddress, SendPort);
+
+ SACN_PrepareBufferHeader(UniverseAt, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN);
+ LedsPlaced += SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, LedsPlaced, *LedBuffer);
+
+ UniverseAt += 1;
+ }
}
+ }
}
#define SACN_H
diff --git a/src/app/engine/uart/foldhaus_uart.cpp b/src/app/engine/uart/foldhaus_uart.cpp
index f18360f..b6ea3fb 100644
--- a/src/app/engine/uart/foldhaus_uart.cpp
+++ b/src/app/engine/uart/foldhaus_uart.cpp
@@ -9,163 +9,163 @@
internal void
UART_SetChannelBuffer_Create(gs_memory_cursor* WriteCursor, uart_channel ChannelSettings, v2_strip Strip, led_buffer LedBuffer)
{
- // NOTE(pjs): This is just here because the information is duplicated and I want to be sure
- // to catch the error where they are different
- Assert(ChannelSettings.PixelsCount == Strip.LedCount);
+ // NOTE(pjs): This is just here because the information is duplicated and I want to be sure
+ // to catch the error where they are different
+ Assert(ChannelSettings.PixelsCount == Strip.LedCount);
+
+ uart_header* Header = MemoryCursorPushStruct(WriteCursor, uart_header);
+ UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812);
+
+ uart_channel* Channel = MemoryCursorPushStruct(WriteCursor, uart_channel);
+ *Channel = ChannelSettings;
+
+ for (u32 i = 0; i < Channel->PixelsCount; i++)
+ {
+ u32 LedIndex = Strip.LedLUT[i];
+ pixel Color = LedBuffer.Colors[LedIndex];
- uart_header* Header = PushStructOnCursor(WriteCursor, uart_header);
- UART_FillHeader(Header, Strip.UARTAddr.Channel, UART_SET_CHANNEL_WS2812);
+ u8* OutputPixel = MemoryCursorPushArray(WriteCursor, u8, 3);
- uart_channel* Channel = PushStructOnCursor(WriteCursor, uart_channel);
- *Channel = ChannelSettings;
-
- for (u32 i = 0; i < Channel->PixelsCount; i++)
- {
- u32 LedIndex = Strip.LedLUT[i];
- pixel Color = LedBuffer.Colors[LedIndex];
-
- u8* OutputPixel = PushArrayOnCursor(WriteCursor, u8, 3);
-
- // TODO(pjs): Use the Output mask
+ // TODO(pjs): Use the Output mask
#if 1
- OutputPixel[0] = Color.R;
- OutputPixel[1] = Color.G;
- OutputPixel[2] = Color.B;
+ OutputPixel[0] = Color.R;
+ OutputPixel[1] = Color.G;
+ OutputPixel[2] = Color.B;
#else
- OutputPixel[0] = 255;
- OutputPixel[1] = 255;
- OutputPixel[2] = 255;
+ OutputPixel[0] = 255;
+ OutputPixel[1] = 255;
+ OutputPixel[2] = 255;
#endif
- if (Channel->ElementsCount == 4)
- {
- // TODO(pjs): Calculate white from the RGB components?
- // Generally we just need a good way to handle the white channel,
- // both in the renderer and in output
-
- //OutputPixel[Channel->WhiteIndex] = Color.W;
- }
+ if (Channel->ElementsCount == 4)
+ {
+ // TODO(pjs): Calculate white from the RGB components?
+ // Generally we just need a good way to handle the white channel,
+ // both in the renderer and in output
+
+ //OutputPixel[Channel->WhiteIndex] = Color.W;
}
-
- uart_footer* Footer = PushStructOnCursor(WriteCursor, uart_footer);
- UART_FillFooter(Footer, (u8*)Header);
+ }
+
+ uart_footer* Footer = MemoryCursorPushStruct(WriteCursor, uart_footer);
+ UART_FillFooter(Footer, (u8*)Header);
}
internal void
UART_DrawAll_Create(gs_memory_cursor* WriteCursor)
{
- uart_header* Header = PushStructOnCursor(WriteCursor, uart_header);
- UART_FillHeader(Header, 1, UART_DRAW_ALL);
-
- uart_footer* Footer = PushStructOnCursor(WriteCursor, uart_footer);
- UART_FillFooter(Footer, (u8*)Header);
+ uart_header* Header = MemoryCursorPushStruct(WriteCursor, uart_header);
+ UART_FillHeader(Header, 1, UART_DRAW_ALL);
+
+ uart_footer* Footer = MemoryCursorPushStruct(WriteCursor, uart_footer);
+ UART_FillFooter(Footer, (u8*)Header);
}
internal void
UART_BuildOutputData(addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem, gs_memory_arena* Transient)
{
- uart_channel ChannelSettings = {0};
- ChannelSettings.ElementsCount = 3;
- ChannelSettings.ColorPackingOrder = 36;
+ uart_channel ChannelSettings = {0};
+ ChannelSettings.ElementsCount = 3;
+ ChannelSettings.ColorPackingOrder = 36;
+
+ // NOTE(pjs): This is the minimum size of every UART message. SetChannelBuffer messages will
+ // be bigger than this, but their size is based on the number of pixels in each channel
+ u32 MessageBaseSize = UART_MESSAGE_MIN_SIZE;
+
+ for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++)
+ {
+ assembly Assembly = Assemblies.Values[AssemblyIdx];
+ led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
- // NOTE(pjs): This is the minimum size of every UART message. SetChannelBuffer messages will
- // be bigger than this, but their size is based on the number of pixels in each channel
- u32 MessageBaseSize = UART_MESSAGE_MIN_SIZE;
-
- for (u32 AssemblyIdx = 0; AssemblyIdx < Assemblies.Count; AssemblyIdx++)
+ struct strips_to_data_buffer
{
- assembly Assembly = Assemblies.Values[AssemblyIdx];
- led_buffer* LedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
-
- struct strips_to_data_buffer
+ gs_const_string ComPort;
+
+ u32* StripIndices;
+ u32 StripIndicesCount;
+ u32 StripIndicesCountMax;
+
+ u64 LedCount;
+
+ u8** ChannelsStart;
+
+ strips_to_data_buffer* Next;
+ };
+
+ u32 BuffersNeededCount = 0;
+ strips_to_data_buffer* BuffersNeededHead = 0;
+ strips_to_data_buffer* BuffersNeededTail = 0;
+
+ for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++)
+ {
+ v2_strip StripAt = Assembly.Strips[StripIdx];
+
+ // If there is a buffer for this com port already created
+ // we use that
+ strips_to_data_buffer* BufferSelected = 0;
+ for (strips_to_data_buffer* At = BuffersNeededHead;
+ At!= 0;
+ At = At->Next)
+ {
+ if (StringsEqual(At->ComPort, StripAt.UARTAddr.ComPort.ConstString))
{
- gs_const_string ComPort;
-
- u32* StripIndices;
- u32 StripIndicesCount;
- u32 StripIndicesCountMax;
-
- u64 LedCount;
-
- u8** ChannelsStart;
-
- strips_to_data_buffer* Next;
- };
-
- u32 BuffersNeededCount = 0;
- strips_to_data_buffer* BuffersNeededHead = 0;
- strips_to_data_buffer* BuffersNeededTail = 0;
-
- for (u32 StripIdx = 0; StripIdx < Assembly.StripCount; StripIdx++)
- {
- v2_strip StripAt = Assembly.Strips[StripIdx];
-
- // If there is a buffer for this com port already created
- // we use that
- strips_to_data_buffer* BufferSelected = 0;
- for (strips_to_data_buffer* At = BuffersNeededHead;
- At!= 0;
- At = At->Next)
- {
- if (StringsEqual(At->ComPort, StripAt.UARTAddr.ComPort.ConstString))
- {
- BufferSelected = At;
- break;
- }
- }
-
- // if no existing buffer for this com port
- // create a new one
- if (!BufferSelected)
- {
- BufferSelected = PushStruct(Transient, strips_to_data_buffer);
- *BufferSelected = {};
- BufferSelected->ComPort = StripAt.UARTAddr.ComPort.ConstString;
- // we don't know at this point how many indices per
- // com port so just make enough room to fit all the strips
- // if necessary
- BufferSelected->StripIndicesCountMax = Assembly.StripCount;
- BufferSelected->StripIndices = PushArray(Transient, u32, BufferSelected->StripIndicesCountMax);
- BufferSelected->LedCount = 0;
- BufferSelected->Next = 0;
-
- SLLPushOrInit(BuffersNeededHead, BuffersNeededTail, BufferSelected);
- BuffersNeededCount += 1;
- }
-
- Assert(BufferSelected->StripIndicesCount < BufferSelected->StripIndicesCountMax);
- u32 Index = BufferSelected->StripIndicesCount++;
- BufferSelected->StripIndices[Index] = StripIdx;
- BufferSelected->LedCount += StripAt.LedCount;
+ BufferSelected = At;
+ break;
}
+ }
+
+ // if no existing buffer for this com port
+ // create a new one
+ if (!BufferSelected)
+ {
+ BufferSelected = PushStruct(Transient, strips_to_data_buffer);
+ *BufferSelected = {};
+ BufferSelected->ComPort = StripAt.UARTAddr.ComPort.ConstString;
+ // we don't know at this point how many indices per
+ // com port so just make enough room to fit all the strips
+ // if necessary
+ BufferSelected->StripIndicesCountMax = Assembly.StripCount;
+ BufferSelected->StripIndices = PushArray(Transient, u32, BufferSelected->StripIndicesCountMax);
+ BufferSelected->LedCount = 0;
+ BufferSelected->Next = 0;
- for (strips_to_data_buffer* At = BuffersNeededHead;
- At!= 0;
- At = At->Next)
- {
- u32 TotalBufferSize = MessageBaseSize * Assembly.StripCount; // SetChannelBuffer messages
- TotalBufferSize += MessageBaseSize; // DrawAll message
- TotalBufferSize += ChannelSettings.ElementsCount * At->LedCount; // pixels * channels per pixel
-
- At->ChannelsStart = PushArray(Transient, u8*, At->StripIndicesCount);
-
- addressed_data_buffer* Buffer = AddressedDataBufferList_Push(Output, TotalBufferSize);
- gs_const_string ComPort = At->ComPort;
- AddressedDataBuffer_SetCOMPort(Buffer, ComPort);
-
- gs_memory_cursor WriteCursor = CreateMemoryCursor(Buffer->Data);
-
- for (u32 i = 0; i < At->StripIndicesCount; i++)
- {
- u32 StripIdx = At->StripIndices[i];
- v2_strip StripAt = Assembly.Strips[StripIdx];
-
- ChannelSettings.PixelsCount = StripAt.LedCount;
- UART_SetChannelBuffer_Create(&WriteCursor, ChannelSettings, StripAt, *LedBuffer);
- }
-
- UART_DrawAll_Create(&WriteCursor);
- }
+ SLLPushOrInit(BuffersNeededHead, BuffersNeededTail, BufferSelected);
+ BuffersNeededCount += 1;
+ }
+
+ Assert(BufferSelected->StripIndicesCount < BufferSelected->StripIndicesCountMax);
+ u32 Index = BufferSelected->StripIndicesCount++;
+ BufferSelected->StripIndices[Index] = StripIdx;
+ BufferSelected->LedCount += StripAt.LedCount;
}
+
+ for (strips_to_data_buffer* At = BuffersNeededHead;
+ At!= 0;
+ At = At->Next)
+ {
+ u32 TotalBufferSize = MessageBaseSize * Assembly.StripCount; // SetChannelBuffer messages
+ TotalBufferSize += MessageBaseSize; // DrawAll message
+ TotalBufferSize += ChannelSettings.ElementsCount * At->LedCount; // pixels * channels per pixel
+
+ At->ChannelsStart = PushArray(Transient, u8*, At->StripIndicesCount);
+
+ addressed_data_buffer* Buffer = AddressedDataBufferList_Push(Output, TotalBufferSize);
+ gs_const_string ComPort = At->ComPort;
+ AddressedDataBuffer_SetCOMPort(Buffer, ComPort);
+
+ gs_memory_cursor WriteCursor = MemoryCursorCreate(Buffer->Memory, Buffer->MemorySize);
+
+ for (u32 i = 0; i < At->StripIndicesCount; i++)
+ {
+ u32 StripIdx = At->StripIndices[i];
+ v2_strip StripAt = Assembly.Strips[StripIdx];
+
+ ChannelSettings.PixelsCount = StripAt.LedCount;
+ UART_SetChannelBuffer_Create(&WriteCursor, ChannelSettings, StripAt, *LedBuffer);
+ }
+
+ UART_DrawAll_Create(&WriteCursor);
+ }
+ }
}
diff --git a/src/app/engine/user_space.cpp b/src/app/engine/user_space.cpp
index 752435a..7e401df 100644
--- a/src/app/engine/user_space.cpp
+++ b/src/app/engine/user_space.cpp
@@ -32,6 +32,16 @@ US_CustomUpdate(user_space_desc* Desc, app_state* State, context* Context)
}
}
+internal void
+US_CustomDebugUI(user_space_desc* Desc, panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer,
+ app_state* State, context Context)
+{
+ if (Desc->CustomDebugUI)
+ {
+ Desc->CustomDebugUI(Desc->UserData, Panel, PanelBounds, RenderBuffer, State, Context);
+ }
+}
+
internal void
US_CustomCleanup(user_space_desc* Desc, app_state* State, context Context)
{
diff --git a/src/app/engine/user_space.h b/src/app/engine/user_space.h
index 8dc5f8a..5373fcb 100644
--- a/src/app/engine/user_space.h
+++ b/src/app/engine/user_space.h
@@ -14,6 +14,9 @@ typedef US_CUSTOM_INIT(us_custom_init_proc);
#define US_CUSTOM_UPDATE(name) void name(gs_data UserData, app_state* State, context* Context)
typedef US_CUSTOM_UPDATE(us_custom_update_proc);
+#define US_CUSTOM_DEBUG_UI(name) void name(gs_data UserData, panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
+typedef US_CUSTOM_DEBUG_UI(us_custom_debug_ui);
+
#define US_CUSTOM_CLEANUP(name) void name(gs_data UserData, app_state* State, context Context)
typedef US_CUSTOM_CLEANUP(us_custom_cleanup_proc);
@@ -22,6 +25,7 @@ typedef struct user_space_desc
us_load_patterns_proc* LoadPatterns;
us_custom_init_proc* CustomInit;
us_custom_update_proc* CustomUpdate;
+ us_custom_debug_ui* CustomDebugUI;
us_custom_cleanup_proc* CustomCleanup;
gs_data UserData;
diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp
index 47590c0..94d8073 100644
--- a/src/app/foldhaus_app.cpp
+++ b/src/app/foldhaus_app.cpp
@@ -10,34 +10,45 @@
RELOAD_STATIC_DATA(ReloadStaticData)
{
+ GlobalDebugServices = DebugServices;
+ GlobalLogBuffer = LogBuffer;
+ if (AppReady)
+ {
app_state* State = (app_state*)Context.MemoryBase;
-
- GlobalDebugServices = DebugServices;
State->PanelSystem.PanelDefs = GlobalPanelDefs;
State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount;
+ gs_data UserData = State->UserSpaceDesc.UserData;
+ State->UserSpaceDesc = BlumenLumen_UserSpaceCreate();
+ if (UserData.Memory && !State->UserSpaceDesc.UserData.Memory)
+ {
+ State->UserSpaceDesc.UserData = UserData;
+ }
US_LoadPatterns(&State->UserSpaceDesc, State, Context);
+ }
}
INITIALIZE_APPLICATION(InitializeApplication)
{
- app_state* State = (app_state*)Context.MemoryBase;
- *State = {};
-
- State->Permanent = CreateMemoryArena(Context.ThreadContext.Allocator);
- State->Transient = Context.ThreadContext.Transient;
- State->Assemblies = AssemblyArray_Create(8, &State->Permanent);
-
- State->GlobalLog = PushStruct(&State->Permanent, event_log);
-
- State->CommandQueue = CommandQueue_Create(&State->Permanent, 32);
-
- animation_system_desc AnimSysDesc = {};
- AnimSysDesc.Storage = &State->Permanent;
- AnimSysDesc.AnimArrayCount = 32;
- AnimSysDesc.SecondsPerFrame = 1.0f / 24.0f;
- State->AnimationSystem = AnimationSystem_Init(AnimSysDesc);
-
+ Context->MemorySize = sizeof(app_state);
+ Context->MemoryBase = Alloc(Context->ThreadContext.Allocator, Context->MemorySize, "Memory Base");
+ app_state* State = (app_state*)Context->MemoryBase;
+ *State = {};
+
+ State->Permanent = MemoryArenaCreate(MB(4), Bytes(8), Context->ThreadContext.Allocator,0, 0, "Permanent");
+ State->Transient = Context->ThreadContext.Transient;
+ State->Assemblies = AssemblyArray_Create(8, &State->Permanent);
+
+ State->CommandQueue = CommandQueue_Create(&State->Permanent, 32);
+
+ animation_system_desc AnimSysDesc = {};
+ AnimSysDesc.Storage = &State->Permanent;
+ AnimSysDesc.AnimArrayCount = 32;
+ AnimSysDesc.SecondsPerFrame = 1.0f / 24.0f;
+ State->AnimationSystem = AnimationSystem_Init(AnimSysDesc);
+
+ if (!Context->Headless)
+ {
interface_config IConfig = {0};
IConfig.FontSize = 14;
IConfig.PanelBG = v4{ .3f, .3f, .3f, 1.f };
@@ -50,92 +61,139 @@ INITIALIZE_APPLICATION(InitializeApplication)
IConfig.ListBGHover = v4{ .22f, .22f, .22f, 1.f };
IConfig.ListBGSelected = v4{ .44f, .44f, .44f, 1.f };
IConfig.Margin = v2{5, 5};
- State->Interface = ui_InterfaceCreate(Context, IConfig, &State->Permanent);
-
- State->SACN = SACN_Initialize(Context);
-
- State->LedSystem = LedSystem_Create(Context.ThreadContext.Allocator, 128);
- State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent);
- State->AssemblyDebugState.Brightness = 255;
- State->AssemblyDebugState.Override = ADS_Override_AllRed;
-
- GlobalDebugServices->Interface.RenderSculpture = true;
+ State->Interface = ui_InterfaceCreate(*Context, IConfig, &State->Permanent);
PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent);
- {
- panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context);
- SplitPanel(RootPanel, .25f, PanelSplit_Horizontal, &State->PanelSystem, State, Context);
-
- panel* AnimPanel = RootPanel->Bottom;
- Panel_SetType(AnimPanel, &State->PanelSystem, PanelType_AnimationTimeline, State, Context);
-
- panel* TopPanel = RootPanel->Top;
- SplitPanel(TopPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, Context);
-
- panel* LeftPanel = TopPanel->Left;
- SplitPanel(LeftPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, Context);
-
- panel* Profiler = LeftPanel->Right;
- Panel_SetType(Profiler, &State->PanelSystem, PanelType_ProfilerView, State, Context);
-
- panel* Hierarchy = LeftPanel->Left;
- Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_HierarchyView, State, Context);
-
- }
- State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext);
+ }
+
+ State->SACN = SACN_Initialize(*Context);
+
+ State->LedSystem = LedSystem_Create(Context->ThreadContext.Allocator, 128);
+ State->AssemblyDebugState = AssemblyDebug_Create(&State->Permanent);
+ State->AssemblyDebugState.Brightness = 255;
+ State->AssemblyDebugState.Override = ADS_Override_None;
+
+ State->Modes = OperationModeSystemInit(&State->Permanent, Context->ThreadContext);
+
+ ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, true);
+ US_CustomInit(&State->UserSpaceDesc, State, *Context);
+
+ if (!Context->Headless)
+ {
+ // NOTE(pjs): This just sets up the default panel layout
+ panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, *Context);
+ SplitPanel(RootPanel, .25f, PanelSplit_Horizontal, &State->PanelSystem, State, *Context);
- State->UserSpaceDesc = BlumenLumen_UserSpaceCreate();
+ panel* AnimPanel = RootPanel->Bottom;
+ Panel_SetType(AnimPanel, &State->PanelSystem, PanelType_AnimationTimeline, State, *Context);
- ReloadStaticData(Context, GlobalDebugServices);
- US_CustomInit(&State->UserSpaceDesc, State, Context);
+ panel* TopPanel = RootPanel->Top;
+ SplitPanel(TopPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context);
+
+ panel* LeftPanel = TopPanel->Left;
+ SplitPanel(LeftPanel, .5f, PanelSplit_Vertical, &State->PanelSystem, State, *Context);
+
+ panel* Profiler = LeftPanel->Right;
+ Panel_SetType(Profiler, &State->PanelSystem, PanelType_MessageLog, State, *Context);
+
+ panel* Hierarchy = LeftPanel->Left;
+ Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, *Context);
+
+ }
+
+ State->RunEditor = !Context->Headless;
+}
+
+internal void
+BuildAssemblyData (app_state* State, context Context, addressed_data_buffer_list* OutputData)
+{
+
+#define SEND_DATA
+#ifdef SEND_DATA
+ // NOTE(pjs): Building data buffers to be sent out to the sculpture
+ // This array is used on the platform side to actually send the information
+ assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient);
+ assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient);
+ SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem);
+ UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem, State->Transient);
+#endif
}
UPDATE_AND_RENDER(UpdateAndRender)
{
- DEBUG_TRACK_FUNCTION;
- app_state* State = (app_state*)Context->MemoryBase;
-
- // NOTE(Peter): We do this at the beginning because all the render commands are stored in Transient,
- // and need to persist beyond the end of the UpdateAndRender call. In the release version, we won't
- // zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically
- // incorrect to clear the arena, and then access the memory later.
- ClearArena(State->Transient);
-
+ DEBUG_TRACK_FUNCTION;
+ app_state* State = (app_state*)Context->MemoryBase;
+
+ // NOTE(Peter): We do this at the beginning because all the render commands are stored in Transient,
+ // and need to persist beyond the end of the UpdateAndRender call. In the release version, we won't
+ // zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically
+ // incorrect to clear the arena, and then access the memory later.
+ MemoryArenaClear(State->Transient);
+ Assert(State->UserSpaceDesc.UserData.Memory != 0);
+
+ if (State->RunEditor)
+ {
Editor_Update(State, Context, InputQueue);
-
- AnimationSystem_Update(&State->AnimationSystem);
- if (AnimationSystem_NeedsRender(State->AnimationSystem))
- {
- AnimationSystem_RenderToLedBuffers(&State->AnimationSystem,
- State->Assemblies,
- &State->LedSystem,
- State->Patterns,
- State->Transient,
- State->UserSpaceDesc.UserData.Memory);
- }
-
- US_CustomUpdate(&State->UserSpaceDesc, State, Context);
-
- AssemblyDebug_OverrideOutput(State->AssemblyDebugState,
- State->Assemblies,
- State->LedSystem);
-
+ }
+
+ AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime);
+ if (AnimationSystem_NeedsRender(State->AnimationSystem))
+ {
+ Assert(State->UserSpaceDesc.UserData.Memory != 0);
+ AnimationSystem_RenderToLedBuffers(&State->AnimationSystem,
+ State->Assemblies,
+ &State->LedSystem,
+ State->Patterns,
+ State->Transient,
+ *Context,
+ State->UserSpaceDesc.UserData.Memory);
+ }
+
+ Assert(State->UserSpaceDesc.UserData.Memory != 0);
+ US_CustomUpdate(&State->UserSpaceDesc, State, Context);
+ Assert(State->UserSpaceDesc.UserData.Memory != 0);
+
+ AssemblyDebug_OverrideOutput(State->AssemblyDebugState,
+ State->Assemblies,
+ State->LedSystem);
+
+ if (State->RunEditor)
+ {
Editor_Render(State, Context, RenderBuffer);
-
- // NOTE(pjs): Building data buffers to be sent out to the sculpture
- // This array is used on the platform side to actually send the information
- assembly_array SACNAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaSACN, State->Transient);
- assembly_array UARTAssemblies = AssemblyArray_Filter(State->Assemblies, AssemblyFilter_OutputsViaUART, State->Transient);
- SACN_BuildOutputData(&State->SACN, OutputData, SACNAssemblies, &State->LedSystem);
- UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem, State->Transient);
+ }
+ ResetWorkQueue(Context->GeneralWorkQueue);
+
+ Assert(State->UserSpaceDesc.UserData.Memory != 0);
+ BuildAssemblyData(State, *Context, OutputData);
+
+ // NOTE(PS): We introduced this in order to test some things on the
+ // blumen lumen circuit boards, to see if they were getting out
+ // of sync
+ if (State->SendEmptyPackets) {
+ for (addressed_data_buffer* At = OutputData->Root;
+ At != 0;
+ At = At->Next)
+ {
+ ZeroMemoryBlock(At->Memory, At->MemorySize);
+ }
+ }
}
CLEANUP_APPLICATION(CleanupApplication)
{
- app_state* State = (app_state*)Context.MemoryBase;
- US_CustomCleanup(&State->UserSpaceDesc, State, Context);
- SACN_Cleanup(&State->SACN, Context);
+ app_state* State = (app_state*)Context.MemoryBase;
+
+ for (u32 i = 0; i < State->Assemblies.Count; i++)
+ {
+ assembly Assembly = State->Assemblies.Values[i];
+ led_buffer LedBuffer = State->LedSystem.Buffers[Assembly.LedBufferIndex];
+ AssemblyDebug_OverrideWithColor(Assembly, LedBuffer, pixel{0, 0, 0});
+ }
+ BuildAssemblyData(State, Context, OutputData);
+
+ US_CustomCleanup(&State->UserSpaceDesc, State, Context);
+ SACN_Cleanup(&State->SACN, Context);
}
#define FOLDHAUS_APP_CPP
diff --git a/src/app/foldhaus_app.h b/src/app/foldhaus_app.h
index 43ded68..5666c95 100644
--- a/src/app/foldhaus_app.h
+++ b/src/app/foldhaus_app.h
@@ -11,13 +11,15 @@
#include "engine/foldhaus_serializer.h"
#include "../gs_libs/gs_font.h"
-#include "foldhaus_log.h"
-#include "interface.h"
+#include "editor/interface.h"
#include "engine/foldhaus_network_ordering.h"
#include "engine/assembly/foldhaus_assembly.h"
+
+#include "ss_blumen_lumen/gfx_math.h"
+
#include "engine/assembly/foldhaus_assembly_parser.cpp"
#include "engine/assembly/foldhaus_assembly_debug.h"
@@ -42,7 +44,8 @@ typedef struct panel panel;
#include "engine/animation/foldhaus_animation_renderer.cpp"
#include "engine/user_space.h"
-#include "blumen_lumen.h"
+#include "ss_blumen_lumen/phrase_hue_map.h"
+#include "ss_blumen_lumen/blumen_lumen.h"
struct app_state
{
@@ -57,7 +60,6 @@ struct app_state
assembly_array Assemblies;
assembly_debug_state AssemblyDebugState;
animation_system AnimationSystem;
- event_log* GlobalLog;
animation_pattern_array Patterns;
// Interface
@@ -72,16 +74,32 @@ struct app_state
panel* HotPanel;
user_space_desc UserSpaceDesc;
+ bool ShowingUserSpaceDebug;
+
+ bool RunEditor;
+ bool SendEmptyPackets;
};
internal void OpenColorPicker(app_state* State, v4* Address);
#include "engine/assembly/foldhaus_assembly.cpp"
+internal assembly*
+LoadAssembly(gs_const_string Path, app_state* State, context Context)
+{
+ return LoadAssembly(&State->Assemblies,
+ &State->LedSystem,
+ State->Transient,
+ Context,
+ Path,
+ GlobalLogBuffer);
+}
+
#include "engine/user_space.cpp"
+#include "ss_blumen_lumen/sdf.h"
#include "patterns/blumen_patterns.h"
-#include "blumen_lumen.cpp"
+#include "ss_blumen_lumen/blumen_lumen.cpp"
internal void
EndCurrentOperationMode(app_state* State)
@@ -98,6 +116,7 @@ EndCurrentOperationMode(app_state* State)
#include "editor/panels/foldhaus_panel_animation_timeline.h"
#include "editor/panels/foldhaus_panel_hierarchy.h"
#include "editor/panels/foldhaus_panel_assembly_debug.h"
+#include "editor/panels/foldhaus_panel_message_log.h"
#include "editor/panels/foldhaus_panel_types.cpp"
diff --git a/src/app/foldhaus_debug.h b/src/app/foldhaus_debug.h
index 7acdfcc..8a66201 100644
--- a/src/app/foldhaus_debug.h
+++ b/src/app/foldhaus_debug.h
@@ -11,70 +11,71 @@
#define SCOPE_NAME_LENGTH 256
struct scope_record
{
- u32 NameHash;
- s64 StartCycles;
- s64 EndCycles;
- s32 CallDepth;
+ u32 NameHash;
+ s64 StartCycles;
+ s64 EndCycles;
+ s32 CallDepth;
};
struct collated_scope_record
{
- u32 NameHash;
- s64 TotalCycles;
- s32 CallCount;
-
- r32 PercentFrameTime;
- r32 TotalSeconds;
-
- r32 AverageSecondsPerCall;
+ u32 NameHash;
+ s64 TotalCycles;
+ s32 CallCount;
+
+ r32 PercentFrameTime;
+ r32 TotalSeconds;
+
+ r32 AverageSecondsPerCall;
};
#define SCOPE_NAME_BUFFER_LENGTH 128
struct scope_name
{
- u32 Hash;
- gs_string Name;
- char Buffer[SCOPE_NAME_BUFFER_LENGTH];
+ u32 Hash;
+ gs_string Name;
+ char Buffer[SCOPE_NAME_BUFFER_LENGTH];
};
struct debug_scope_record_list
{
- s32 ThreadId;
- s32 Max;
- s32 Count;
- scope_record* Calls;
-
- s32 CurrentScopeCallDepth;
+ s32 ThreadId;
+ s32 Max;
+ s32 Count;
+ scope_record* Calls;
+
+ s32 CurrentScopeCallDepth;
};
#define DEBUG_FRAME_GROW_SIZE 8102
struct debug_frame
{
- s64 FrameStartCycles;
- s64 FrameEndCycles;
-
- s32 ScopeNamesMax;
- s32 ScopeNamesCount;
- scope_name* ScopeNamesHash;
-
- s32 ThreadCount;
- debug_scope_record_list* ThreadCalls;
-
- s32 CollatedScopesMax;
- collated_scope_record* CollatedScopes;
+ s64 FrameStartCycles;
+ s64 FrameEndCycles;
+
+ s32 ScopeNamesMax;
+ s32 ScopeNamesCount;
+ scope_name* ScopeNamesHash;
+
+ s32 ThreadCount;
+ debug_scope_record_list* ThreadCalls;
+
+ s32 CollatedScopesMax;
+ collated_scope_record* CollatedScopes;
};
-#define FRAME_VIEW_PROFILER 0
-#define FRAME_VIEW_SCOPE_LIST 1
+enum debug_ui_view
+{
+ DebugUI_Profiler,
+ DebugUI_ScopeList,
+ DebugUI_MemoryView,
+
+ DebugUI_Count,
+};
struct debug_interface
{
- b32 ShowCameraMouse;
- b32 ShowTrackedScopes;
- b32 RenderSculpture;
- b32 SendSACNData;
-
- s32 FrameView;
+ s32 FrameView;
};
typedef s32 debug_get_thread_id();
@@ -85,327 +86,353 @@ typedef u8* debug_realloc(u8* Memory, s32 OldSize, s32 NewSize);
#define HISTOGRAM_DEPTH 10
struct debug_histogram_entry
{
- char ScopeName_[SCOPE_NAME_LENGTH];
- gs_string ScopeName;
-
- u32 PerFrame_Cycles[HISTOGRAM_DEPTH];
- u32 PerFrame_CallCount[HISTOGRAM_DEPTH];
- s32 CurrentFrame;
-
- // NOTE(Peter): Cached Values, recalculated ever frame
- u32 Average_Cycles;
- u32 Average_CallCount;
- u32 Total_Cycles;
- u32 Total_CallCount;
+ char ScopeName_[SCOPE_NAME_LENGTH];
+ gs_string ScopeName;
+
+ u32 PerFrame_Cycles[HISTOGRAM_DEPTH];
+ u32 PerFrame_CallCount[HISTOGRAM_DEPTH];
+ s32 CurrentFrame;
+
+ // NOTE(Peter): Cached Values, recalculated ever frame
+ u32 Average_Cycles;
+ u32 Average_CallCount;
+ u32 Total_Cycles;
+ u32 Total_CallCount;
};
-#define DEBUG_FRAME_COUNT 128
+#if DEBUG
+# define DEBUG_FRAME_COUNT 128
+#else
+# define DEBUG_FRAME_COUNT 0
+#endif
+
struct debug_services
{
- s64 PerformanceCountFrequency;
-
- b32 RecordFrames;
- s32 CurrentDebugFrame;
- debug_frame Frames[DEBUG_FRAME_COUNT];
-
- debug_interface Interface;
-
- debug_get_thread_id* GetThreadId;
- debug_timing_proc* GetWallClock;
- debug_alloc* Alloc;
- debug_realloc* Realloc;
+ s64 PerformanceCountFrequency;
+
+ b32 RecordFrames;
+ s32 CurrentDebugFrame;
+ debug_frame* Frames;
+
+ debug_interface Interface;
+
+ gs_thread_context Ctx;
+ gs_memory_arena A;
+
+ debug_get_thread_id* GetThreadId;
+ debug_timing_proc* GetWallClock;
};
internal void
InitializeDebugFrame (debug_frame* Frame, s32 NameHashMax, s32 ThreadCount, s32 ScopeCallsMax, debug_services* Services)
{
- Frame->ScopeNamesMax = NameHashMax;
- Frame->ScopeNamesHash = (scope_name*)Services->Alloc(sizeof(scope_name), NameHashMax);
-
- // NOTE(Peter): We use the same size as scope names because we're only storing a single instance
- // per scope. If ScopeNamesMax can't hold all the scopes, this will never get filled and
- // we should assert and recompile with a resized NameHashMax
- Frame->CollatedScopesMax = NameHashMax;
- Frame->CollatedScopes = (collated_scope_record*)Services->Alloc(sizeof(collated_scope_record), NameHashMax);
-
- for (s32 i = 0; i < Frame->ScopeNamesMax; i++)
- {
- scope_name* Entry = Frame->ScopeNamesHash + i;
- Entry->Name = MakeString(Entry->Buffer, 0, SCOPE_NAME_BUFFER_LENGTH);
- }
-
- Frame->ThreadCount = ThreadCount;
- Frame->ThreadCalls = (debug_scope_record_list*)Services->Alloc(sizeof(debug_scope_record_list),
- ThreadCount);
-
- for (s32 i = 0; i < ThreadCount; i++)
- {
- Frame->ThreadCalls[i].Max = ScopeCallsMax;
- Frame->ThreadCalls[i].Count = 0;
- Frame->ThreadCalls[i].Calls = (scope_record*)Services->Alloc(sizeof(scope_record), ScopeCallsMax);
- Frame->ThreadCalls[i].CurrentScopeCallDepth = 0;
- Frame->ThreadCalls[i].ThreadId = 0;
- }
-
- for (s32 c = 0; c < Frame->CollatedScopesMax; c++)
- {
- Frame->CollatedScopes[c].NameHash = 0;
- }
+ Frame->ScopeNamesMax = NameHashMax;
+ Frame->ScopeNamesHash = PushArray(&Services->A, scope_name, NameHashMax);
+
+ // NOTE(Peter): We use the same size as scope names because we're only storing a single instance
+ // per scope. If ScopeNamesMax can't hold all the scopes, this will never get filled and
+ // we should assert and recompile with a resized NameHashMax
+ Frame->CollatedScopesMax = NameHashMax;
+ Frame->CollatedScopes = PushArray(&Services->A, collated_scope_record, NameHashMax);
+
+ for (s32 i = 0; i < Frame->ScopeNamesMax; i++)
+ {
+ scope_name* Entry = Frame->ScopeNamesHash + i;
+ Entry->Name = MakeString(Entry->Buffer, 0, SCOPE_NAME_BUFFER_LENGTH);
+ }
+
+ Frame->ThreadCount = ThreadCount;
+ Frame->ThreadCalls = PushArray(&Services->A, debug_scope_record_list, ThreadCount);
+
+ for (s32 i = 0; i < ThreadCount; i++)
+ {
+ Frame->ThreadCalls[i].Max = ScopeCallsMax;
+ Frame->ThreadCalls[i].Count = 0;
+ Frame->ThreadCalls[i].Calls = PushArray(&Services->A, scope_record, ScopeCallsMax);
+ Frame->ThreadCalls[i].CurrentScopeCallDepth = 0;
+ Frame->ThreadCalls[i].ThreadId = 0;
+ }
+
+ for (s32 c = 0; c < Frame->CollatedScopesMax; c++)
+ {
+ Frame->CollatedScopes[c].NameHash = 0;
+ }
}
internal void
StartDebugFrame(debug_frame* Frame, debug_services* Services)
{
- Frame->FrameStartCycles = Services->GetWallClock();
- for (s32 i = 0; i < Frame->ThreadCount; i++)
- {
- Frame->ThreadCalls[i].Count = 0;
- Frame->ThreadCalls[i].CurrentScopeCallDepth = 0;
- }
-
- for (s32 c = 0; c < Frame->CollatedScopesMax; c++)
- {
- s32 Hash = Frame->CollatedScopes[c].NameHash;
- Frame->CollatedScopes[c] = {};
- Frame->CollatedScopes[c].NameHash = Hash;
- }
+ Frame->FrameStartCycles = Services->GetWallClock();
+ for (s32 i = 0; i < Frame->ThreadCount; i++)
+ {
+ Frame->ThreadCalls[i].Count = 0;
+ Frame->ThreadCalls[i].CurrentScopeCallDepth = 0;
+ }
+
+ for (s32 c = 0; c < Frame->CollatedScopesMax; c++)
+ {
+ s32 Hash = Frame->CollatedScopes[c].NameHash;
+ Frame->CollatedScopes[c] = {};
+ Frame->CollatedScopes[c].NameHash = Hash;
+ }
}
internal void
-InitDebugServices (debug_services* Services,
- s64 PerformanceCountFrequency,
- debug_alloc* Alloc,
- debug_realloc* Realloc,
- debug_timing_proc* GetWallClock,
- debug_get_thread_id* GetThreadId,
- s32 ThreadCount)
+InitDebugServices_OffMode (debug_services* Services,
+ s64 PerformanceCountFrequency,
+ debug_timing_proc* GetWallClock,
+ debug_get_thread_id* GetThreadId,
+ gs_thread_context Ctx,
+ s32 ThreadCount)
{
- Services->Alloc = Alloc;
- Services->Realloc = Realloc;
- Services->GetWallClock = GetWallClock;
- Services->GetThreadId = GetThreadId;
-
- Services->RecordFrames = true;
-
- Services->CurrentDebugFrame = 0;
- s32 NameHashMax = 4096;
- s32 ScopeCallsMax = 4096;
- for (s32 i = 0; i < DEBUG_FRAME_COUNT; i++)
- {
- InitializeDebugFrame(&Services->Frames[i], NameHashMax, ThreadCount, ScopeCallsMax, Services);
- }
-
- Services->Interface.RenderSculpture = true;
-
- Services->PerformanceCountFrequency = PerformanceCountFrequency;
-
- Services->Interface.ShowCameraMouse = false;
- Services->Interface.ShowTrackedScopes = false;
- Services->Interface.RenderSculpture = true;
- Services->Interface.SendSACNData = false;
+ *Services = {0};
+
+ Services->Ctx = Ctx;
+ Services->GetWallClock = GetWallClock;
+ Services->GetThreadId = GetThreadId;
+
+ Services->RecordFrames = false;
+ Services->Frames = 0;
+
+ Services->CurrentDebugFrame = 0;
+ Services->PerformanceCountFrequency = PerformanceCountFrequency;
+}
+
+
+internal void
+InitDebugServices_DebugMode (debug_services* Services,
+ s64 PerformanceCountFrequency,
+ debug_timing_proc* GetWallClock,
+ debug_get_thread_id* GetThreadId,
+ gs_thread_context Ctx,
+ s32 ThreadCount)
+{
+ Services->Ctx = Ctx;
+ Services->GetWallClock = GetWallClock;
+ Services->GetThreadId = GetThreadId;
+
+ Services->RecordFrames = true;
+
+ Services->CurrentDebugFrame = 0;
+ Services->A = MemoryArenaCreate(MB(64), Bytes(8), Ctx.Allocator, 0, 0, "Debug Services Allocator");
+
+ s32 NameHashMax = 4096;
+ s32 ScopeCallsMax = 4096;
+ Services->Frames = PushArray(&Services->A, debug_frame, DEBUG_FRAME_COUNT);
+ for (s32 i = 0; i < DEBUG_FRAME_COUNT; i++)
+ {
+ InitializeDebugFrame(&Services->Frames[i], NameHashMax, ThreadCount, ScopeCallsMax, Services);
+ }
+
+ Services->PerformanceCountFrequency = PerformanceCountFrequency;
}
internal debug_frame*
GetCurrentDebugFrame (debug_services* Services)
{
- debug_frame* Result = Services->Frames + Services->CurrentDebugFrame;
- return Result;
+ debug_frame* Result = Services->Frames + Services->CurrentDebugFrame;
+ return Result;
}
internal debug_frame*
GetLastDebugFrame(debug_services* Services)
{
- s32 Index = (Services->CurrentDebugFrame - 1);
- if (Index < 0) { Index += DEBUG_FRAME_COUNT; }
- debug_frame* Result = Services->Frames + Index;
- return Result;
+ if (!Services->Frames) return 0;
+
+ s32 Index = (Services->CurrentDebugFrame - 1);
+ if (Index < 0) { Index += DEBUG_FRAME_COUNT; }
+ debug_frame* Result = Services->Frames + Index;
+ return Result;
}
internal s32
GetIndexForNameHash(debug_frame* Frame, u32 NameHash)
{
- s32 Result = -1;
-
- for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++)
+ s32 Result = -1;
+
+ for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++)
+ {
+ u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax;
+ if (Frame->ScopeNamesHash[Index].Hash == NameHash)
{
- u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax;
- if (Frame->ScopeNamesHash[Index].Hash == NameHash)
- {
- Result = Index;
- break;
- }
+ Result = Index;
+ break;
}
-
- // NOTE(Peter): Its not technically wrong to return a -1 here, just means we didn't find it.
- // At the time of writing however, this function is only being called in contexts where we
- // know there should be an entry in the Name table, so a -1 actually indicates a problem.
- Assert(Result >= 0);
- return Result;
+ }
+
+ // NOTE(Peter): Its not technically wrong to return a -1 here, just means we didn't find it.
+ // At the time of writing however, this function is only being called in contexts where we
+ // know there should be an entry in the Name table, so a -1 actually indicates a problem.
+ Assert(Result >= 0);
+ return Result;
}
internal debug_scope_record_list*
GetScopeListForThreadInFrame(debug_services* Services, debug_frame* Frame)
{
- debug_scope_record_list* List = 0;
-
- s32 CurrentThreadId = Services->GetThreadId();
- for (s32 Offset = 0; Offset < Frame->ThreadCount; Offset++)
+ debug_scope_record_list* List = 0;
+
+ s32 CurrentThreadId = Services->GetThreadId();
+ for (s32 Offset = 0; Offset < Frame->ThreadCount; Offset++)
+ {
+ s32 Index = (CurrentThreadId + Offset) % Frame->ThreadCount;
+ if (Frame->ThreadCalls[Index].ThreadId == CurrentThreadId)
{
- s32 Index = (CurrentThreadId + Offset) % Frame->ThreadCount;
- if (Frame->ThreadCalls[Index].ThreadId == CurrentThreadId)
- {
- List = Frame->ThreadCalls + Index;
- break;
- }
- else if (Frame->ThreadCalls[Index].ThreadId == 0)
- {
- Frame->ThreadCalls[Index].ThreadId = CurrentThreadId;
- List = Frame->ThreadCalls + Index;
- break;
- }
+ List = Frame->ThreadCalls + Index;
+ break;
}
-
- Assert(List);
- return List;
+ else if (Frame->ThreadCalls[Index].ThreadId == 0)
+ {
+ Frame->ThreadCalls[Index].ThreadId = CurrentThreadId;
+ List = Frame->ThreadCalls + Index;
+ break;
+ }
+ }
+
+ Assert(List);
+ return List;
}
internal void
CollateThreadScopeCalls (debug_scope_record_list* ThreadRecords, debug_frame* Frame)
{
- for (s32 i = 0; i < ThreadRecords->Count; i++)
+ for (s32 i = 0; i < ThreadRecords->Count; i++)
+ {
+ scope_record Record = ThreadRecords->Calls[i];
+ s32 Index = GetIndexForNameHash (Frame, Record.NameHash);
+ collated_scope_record* CollatedRecord = Frame->CollatedScopes + Index;
+
+ if (CollatedRecord->NameHash != Record.NameHash)
{
- scope_record Record = ThreadRecords->Calls[i];
- s32 Index = GetIndexForNameHash (Frame, Record.NameHash);
- collated_scope_record* CollatedRecord = Frame->CollatedScopes + Index;
-
- if (CollatedRecord->NameHash != Record.NameHash)
- {
- CollatedRecord->NameHash = Record.NameHash;
- CollatedRecord->TotalCycles = 0;
- CollatedRecord->CallCount = 0;
- }
-
- CollatedRecord->TotalCycles += Record.EndCycles - Record.StartCycles;
- CollatedRecord->CallCount += 1;
+ CollatedRecord->NameHash = Record.NameHash;
+ CollatedRecord->TotalCycles = 0;
+ CollatedRecord->CallCount = 0;
}
+
+ CollatedRecord->TotalCycles += Record.EndCycles - Record.StartCycles;
+ CollatedRecord->CallCount += 1;
+ }
}
internal void
EndDebugFrame (debug_services* Services)
{
- debug_frame* ClosingFrame = GetCurrentDebugFrame(Services);
- ClosingFrame->FrameEndCycles = Services->GetWallClock();
-
- s64 FrameTotalCycles = ClosingFrame->FrameEndCycles - ClosingFrame->FrameStartCycles;
-
- for (s32 t = 0; t < ClosingFrame->ThreadCount; t++)
+ debug_frame* ClosingFrame = GetCurrentDebugFrame(Services);
+ ClosingFrame->FrameEndCycles = Services->GetWallClock();
+
+ s64 FrameTotalCycles = ClosingFrame->FrameEndCycles - ClosingFrame->FrameStartCycles;
+
+ for (s32 t = 0; t < ClosingFrame->ThreadCount; t++)
+ {
+ CollateThreadScopeCalls(ClosingFrame->ThreadCalls + t, ClosingFrame);
+ }
+
+ s32 ScopeNamesCount = 0;
+ for (s32 n = 0; n < ClosingFrame->ScopeNamesMax; n++)
+ {
+ if (ClosingFrame->ScopeNamesHash[n].Hash != 0)
{
- CollateThreadScopeCalls(ClosingFrame->ThreadCalls + t, ClosingFrame);
+ collated_scope_record* CollatedRecord = ClosingFrame->CollatedScopes + n;
+ CollatedRecord->TotalSeconds = (r32)CollatedRecord->TotalCycles / (r32)Services->PerformanceCountFrequency;
+ CollatedRecord->PercentFrameTime = (r32)CollatedRecord->TotalCycles / (r32)FrameTotalCycles;
+ CollatedRecord->AverageSecondsPerCall = CollatedRecord->TotalSeconds / CollatedRecord->CallCount;
+ ScopeNamesCount += 1;
}
-
- s32 ScopeNamesCount = 0;
- for (s32 n = 0; n < ClosingFrame->ScopeNamesMax; n++)
- {
- if (ClosingFrame->ScopeNamesHash[n].Hash != 0)
- {
- collated_scope_record* CollatedRecord = ClosingFrame->CollatedScopes + n;
- CollatedRecord->TotalSeconds = (r32)CollatedRecord->TotalCycles / (r32)Services->PerformanceCountFrequency;
- CollatedRecord->PercentFrameTime = (r32)CollatedRecord->TotalCycles / (r32)FrameTotalCycles;
- CollatedRecord->AverageSecondsPerCall = CollatedRecord->TotalSeconds / CollatedRecord->CallCount;
- ScopeNamesCount += 1;
- }
- }
- ClosingFrame->ScopeNamesCount = ScopeNamesCount;
-
- Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % DEBUG_FRAME_COUNT;
- StartDebugFrame(&Services->Frames[Services->CurrentDebugFrame], Services);
+ }
+ ClosingFrame->ScopeNamesCount = ScopeNamesCount;
+
+ s32 FramesCount = DEBUG_FRAME_COUNT;
+ if (FramesCount > 0)
+ {
+ Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % FramesCount;
+ }
+ StartDebugFrame(&Services->Frames[Services->CurrentDebugFrame], Services);
}
internal u32
HashScopeName (char* ScopeName)
{
- // djb2 hash
- u32 Hash = 5381;
- char* C = ScopeName;
- while(*C)
- {
- Hash = ((Hash << 5) + Hash) + *C;
- C++;
- }
- return Hash;
+ // djb2 hash
+ u32 Hash = 5381;
+ char* C = ScopeName;
+ while(*C)
+ {
+ Hash = ((Hash << 5) + Hash) + *C;
+ C++;
+ }
+ return Hash;
}
internal scope_name*
GetOrAddNameHashEntry(debug_frame* Frame, u32 NameHash)
{
- scope_name* Result = 0;
-
- for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++)
+ scope_name* Result = 0;
+
+ for (s32 Offset = 0; Offset < Frame->ScopeNamesMax; Offset++)
+ {
+ u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax;
+ if ((Frame->ScopeNamesHash[Index].Hash == 0) || (Frame->ScopeNamesHash[Index].Hash == NameHash))
{
- u32 Index = (NameHash + Offset) % Frame->ScopeNamesMax;
- if ((Frame->ScopeNamesHash[Index].Hash == 0) || (Frame->ScopeNamesHash[Index].Hash == NameHash))
- {
- Result = Frame->ScopeNamesHash + Index;
- break;
- }
+ Result = Frame->ScopeNamesHash + Index;
+ break;
}
-
- return Result;
+ }
+
+ return Result;
}
internal u32
BeginTrackingScopeAndGetNameHash (debug_services* Services, char* ScopeName)
{
- debug_frame* CurrentFrame = GetCurrentDebugFrame(Services);
- debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame);
-
- ThreadList->CurrentScopeCallDepth++;
-
- u32 NameHash = HashScopeName(ScopeName);
- scope_name* Entry = GetOrAddNameHashEntry(CurrentFrame, NameHash);
- if (Entry->Hash == 0) // If its new
- {
- Entry->Hash = NameHash;
- // TODO(Peter): need to initialize all entry name gs_strings to point at the buffer
- // This will break eventually. when it does, do this ^^^^ when on startup
- PrintF(&Entry->Name, "%s", ScopeName);
- }
-
- return NameHash;
+ debug_frame* CurrentFrame = GetCurrentDebugFrame(Services);
+ debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame);
+
+ ThreadList->CurrentScopeCallDepth++;
+
+ u32 NameHash = HashScopeName(ScopeName);
+ scope_name* Entry = GetOrAddNameHashEntry(CurrentFrame, NameHash);
+ if (Entry->Hash == 0) // If its new
+ {
+ Entry->Hash = NameHash;
+ // TODO(Peter): need to initialize all entry name gs_strings to point at the buffer
+ // This will break eventually. when it does, do this ^^^^ when on startup
+ PrintF(&Entry->Name, "%s", ScopeName);
+ }
+
+ return NameHash;
}
internal void
PushScopeTimeOnFrame (debug_services* Services, s32 NameHash, u64 StartCycles, u64 EndCycles)
{
- debug_frame* CurrentFrame = GetCurrentDebugFrame(Services);
- debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame);
-
- if (ThreadList->Count >= ThreadList->Max)
- {
- s32 CurrentSize = ThreadList->Max * sizeof(scope_record);
- s32 NewMax = (ThreadList->Max + DEBUG_FRAME_GROW_SIZE);
- s32 NewSize = NewMax * sizeof(scope_record);
- ThreadList->Calls = (scope_record*)Services->Realloc((u8*)ThreadList->Calls, CurrentSize, NewSize);
- ThreadList->Max = NewMax;
- }
-
- Assert(ThreadList->Count < ThreadList->Max);
-
- s32 EntryIndex = ThreadList->Count++;
- scope_record* Record = ThreadList->Calls + EntryIndex;
- Record->NameHash = NameHash;
- Record->StartCycles = StartCycles;
- Record->EndCycles = EndCycles;
- Record->CallDepth = --ThreadList->CurrentScopeCallDepth;
+ debug_frame* CurrentFrame = GetCurrentDebugFrame(Services);
+ debug_scope_record_list* ThreadList = GetScopeListForThreadInFrame(Services, CurrentFrame);
+
+ if (ThreadList->Count >= ThreadList->Max)
+ {
+ s32 NewMax = (ThreadList->Max + DEBUG_FRAME_GROW_SIZE);
+ FreeArray(Services->Ctx.Allocator, ThreadList->Calls, scope_record, ThreadList->Max);
+ ThreadList->Calls = AllocArray(Services->Ctx.Allocator, scope_record, NewMax, "Debug Frame");
+ ThreadList->Max = NewMax;
+ }
+
+ Assert(ThreadList->Count < ThreadList->Max);
+
+ s32 EntryIndex = ThreadList->Count++;
+ scope_record* Record = ThreadList->Calls + EntryIndex;
+ Record->NameHash = NameHash;
+ Record->StartCycles = StartCycles;
+ Record->EndCycles = EndCycles;
+ Record->CallDepth = --ThreadList->CurrentScopeCallDepth;
}
internal r32 DEBUGGetSecondsElapsed (s64 Start, s64 End, r32 PerformanceCountFrequency)
{
- r32 Result = ((r32)(End - Start) / (r32)PerformanceCountFrequency);
- return Result;
+ r32 Result = ((r32)(End - Start) / (r32)PerformanceCountFrequency);
+ return Result;
}
-#ifdef DEBUG
+#if DEBUG
#define DEBUG_TRACK_FUNCTION scope_tracker ScopeTracker ((char*)__func__, GlobalDebugServices)
#define DEBUG_TRACK_SCOPE(name) scope_tracker ScopeTracker_##name (#name, GlobalDebugServices)
#else
@@ -414,32 +441,32 @@ internal r32 DEBUGGetSecondsElapsed (s64 Start, s64 End, r32 PerformanceCountFre
#endif
struct scope_tracker
{
- s64 ScopeStart;
- u32 ScopeNameHash;
- debug_services* DebugServices;
-
- scope_tracker(char* ScopeName, debug_services* DebugServices)
+ s64 ScopeStart;
+ u32 ScopeNameHash;
+ debug_services* DebugServices;
+
+ scope_tracker(char* ScopeName, debug_services* DebugServices)
+ {
+ if (DebugServices->RecordFrames)
{
- if (DebugServices->RecordFrames)
- {
- this->ScopeNameHash = BeginTrackingScopeAndGetNameHash(DebugServices, ScopeName);
- this->ScopeStart = DebugServices->GetWallClock();
- this->DebugServices = DebugServices;
- }
- else
- {
- this->DebugServices = 0;
- }
+ this->ScopeNameHash = BeginTrackingScopeAndGetNameHash(DebugServices, ScopeName);
+ this->ScopeStart = DebugServices->GetWallClock();
+ this->DebugServices = DebugServices;
}
-
- ~scope_tracker()
+ else
{
- if (this->DebugServices) // NOTE(Peter): If DebugServices == 0, then we werent' recording this frame
- {
- s64 ScopeEnd = this->DebugServices->GetWallClock();
- PushScopeTimeOnFrame(this->DebugServices, this->ScopeNameHash, this->ScopeStart, ScopeEnd);
- }
+ this->DebugServices = 0;
}
+ }
+
+ ~scope_tracker()
+ {
+ if (this->DebugServices) // NOTE(Peter): If DebugServices == 0, then we werent' recording this frame
+ {
+ s64 ScopeEnd = this->DebugServices->GetWallClock();
+ PushScopeTimeOnFrame(this->DebugServices, this->ScopeNameHash, this->ScopeStart, ScopeEnd);
+ }
+ }
};
diff --git a/src/app/foldhaus_log.h b/src/app/foldhaus_log.h
index 5d1c936..dea66da 100644
--- a/src/app/foldhaus_log.h
+++ b/src/app/foldhaus_log.h
@@ -42,8 +42,5 @@ PushLogEntry(event_log* Log, gs_string Message, log_entry_type Type)
NewEntry->Type = Type;
}
-
-
-
#define FOLDHAUS_LOG_H
#endif // FOLDHAUS_LOG_H
\ No newline at end of file
diff --git a/src/app/foldhaus_platform.h b/src/app/foldhaus_platform.h
index 7f10ab0..14e1ae5 100644
--- a/src/app/foldhaus_platform.h
+++ b/src/app/foldhaus_platform.h
@@ -11,21 +11,26 @@
#include "..\gs_libs\gs_types.h"
#include "..\gs_libs\gs_types.cpp"
+#include "..\gs_libs\gs_path.h"
struct handle
{
- u32 Generation;
- u32 Index;
+ u32 Generation;
+ u32 Index;
};
inline bool
Handle_IsValid(handle Handle)
{
- bool Result = (Handle.Generation != 0);
- return Result;
+ bool Result = (Handle.Generation != 0);
+ return Result;
}
#include "..\gs_libs\gs_string.h"
+#include "..\gs_libs\gs_csv.h"
+
+#include "engine/foldhaus_log.h"
+global log_buffer* GlobalLogBuffer;
#include "foldhaus_debug.h"
global debug_services* GlobalDebugServices;
@@ -35,9 +40,9 @@ global debug_services* GlobalDebugServices;
struct platform_network_address
{
- s32 Family;
- u16 Port;
- u32 Address;
+ s32 Family;
+ u16 Port;
+ u32 Address;
};
typedef s32 platform_socket_handle;
@@ -54,37 +59,37 @@ typedef struct context context;
typedef void temp_job_req_proc(gs_thread_context* Ctx, u8* Memory);
struct temp_job_req
{
- temp_job_req_proc* Proc;
- u8* Memory;
+ temp_job_req_proc* Proc;
+ u8* Memory;
};
// This isn't necessarily temp but I'm not sure it goes here
#define PACKETS_MAX 32
struct packet_ringbuffer
{
- gs_data Values[PACKETS_MAX];
- u32 ReadHead;
- u32 WriteHead;
+ gs_data Values[PACKETS_MAX];
+ u32 ReadHead;
+ u32 WriteHead;
};
-#define INITIALIZE_APPLICATION(name) void name(context Context)
+#define INITIALIZE_APPLICATION(name) void name(context* Context)
typedef INITIALIZE_APPLICATION(initialize_application);
#define UPDATE_AND_RENDER(name) void name(context* Context, input_queue InputQueue, render_command_buffer* RenderBuffer, addressed_data_buffer_list* OutputData)
typedef UPDATE_AND_RENDER(update_and_render);
-#define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices)
+#define RELOAD_STATIC_DATA(name) void name(context Context, debug_services* DebugServices, log_buffer* LogBuffer, bool AppReady)
typedef RELOAD_STATIC_DATA(reload_static_data);
-#define CLEANUP_APPLICATION(name) void name(context Context)
+#define CLEANUP_APPLICATION(name) void name(context Context, addressed_data_buffer_list* OutputData)
typedef CLEANUP_APPLICATION(cleanup_application);
// Platform Functions
struct window_info
{
- char* Name;
- s32 Width;
- s32 Height;
+ char* Name;
+ s32 Width;
+ s32 Height;
};
typedef struct window window;
@@ -92,38 +97,38 @@ typedef struct window window;
#define PLATFORM_MEMORY_NO_ERROR 0
enum platform_memory_error
{
- PlatformMemory_NoError,
- PlatformMemory_FileNotFound,
-
- PlatformMemory_UnknownError, // You should implement handling this when you see it
+ PlatformMemory_NoError,
+ PlatformMemory_FileNotFound,
+
+ PlatformMemory_UnknownError, // You should implement handling this when you see it
};
struct data
{
- u8* Base;
- u64 Size;
+ u8* Base;
+ u64 Size;
};
struct platform_memory_result
{
- data Data;
- platform_memory_error Error;
+ data Data;
+ platform_memory_error Error;
};
struct system_path
{
- char* Path;
- s32 PathLength;
- s32 IndexOfLastSlash;
+ char* Path;
+ s32 PathLength;
+ s32 IndexOfLastSlash;
};
struct texture_buffer
{
- u8* Memory;
- s32 Width;
- s32 Height;
- s32 Pitch;
- s32 BytesPerPixel;
+ u8* Memory;
+ s32 Width;
+ s32 Height;
+ s32 Pitch;
+ s32 BytesPerPixel;
};
#define PLATFORM_GET_GPU_TEXTURE_HANDLE(name) s32 name(u8* Memory, s32 Width, s32 Height)
@@ -135,25 +140,25 @@ typedef PLATFORM_GET_SOCKET_HANDLE(platform_get_socket_handle);
// Font
struct platform_font_info
{
- s32 PixelHeight;
- s32 Ascent, Descent, Leading;
- s32 MaxCharWidth;
- s32 CodepointStart;
- s32 CodepointOnePastLast;
+ s32 PixelHeight;
+ s32 Ascent, Descent, Leading;
+ s32 MaxCharWidth;
+ s32 CodepointStart;
+ s32 CodepointOnePastLast;
};
enum font_weight
{
- FontWeight_Invalid = 0,
- FontWeight_Thin = 100,
- FontWeight_ExtraLight = 200,
- FontWeight_Light = 300,
- FontWeight_Normal = 400,
- FontWeight_Medium = 500,
- FontWeight_SemiBold = 600,
- FontWeight_Bold = 700,
- FontWeight_ExtraBold = 800,
- FontWeight_Heavy = 900,
+ FontWeight_Invalid = 0,
+ FontWeight_Thin = 100,
+ FontWeight_ExtraLight = 200,
+ FontWeight_Light = 300,
+ FontWeight_Normal = 400,
+ FontWeight_Medium = 500,
+ FontWeight_SemiBold = 600,
+ FontWeight_Bold = 700,
+ FontWeight_ExtraBold = 800,
+ FontWeight_Heavy = 900,
};
#define GET_FONT_INFO(name) platform_font_info name(char* FontName, s32 PixelHeight, font_weight FontWeight, b32 Italic, b32 Underline, b32 Strikeout)
@@ -168,15 +173,15 @@ typedef DRAW_FONT_CODEPOINT(platform_draw_font_codepoint);
RESET_WORK_QUEUE(ResetWorkQueue)
{
- for (u32 i = 0; i < Queue->JobsMax; i++)
- {
- Queue->Jobs[i].Data = {0};
- Queue->Jobs[i].WorkProc = 0;
- }
-
- Queue->JobsCount = 0;
- Queue->NextJobIndex = 0;
- Queue->JobsCompleted = 0;
+ for (u32 i = 0; i < Queue->JobsMax; i++)
+ {
+ Queue->Jobs[i].Data = {0};
+ Queue->Jobs[i].WorkProc = 0;
+ }
+
+ Queue->JobsCount = 0;
+ Queue->NextJobIndex = 0;
+ Queue->JobsCompleted = 0;
}
// Time
@@ -184,39 +189,66 @@ RESET_WORK_QUEUE(ResetWorkQueue)
internal r32
GetSecondsElapsed (s64 Start, s64 End, s64 PerformanceCountFrequency)
{
- r32 Result = ((r32)(End - Start) / (r32) PerformanceCountFrequency);
- return Result;
+ r32 Result = ((r32)(End - Start) / (r32) PerformanceCountFrequency);
+ return Result;
+}
+
+typedef struct system_time
+{
+ u64 NanosSinceEpoch;
+
+ s32 Year;
+ s32 Month;
+ s32 Day;
+ s32 Hour; // [0:23]
+ s32 Minute;
+ s32 Second;
+} system_time;
+
+internal r64
+SecondsElapsed(system_time Start, system_time End)
+{
+ u64 N = End.NanosSinceEpoch - Start.NanosSinceEpoch;
+ r64 S = (r64)N * NanosToSeconds;
+ return S;
}
struct context
{
- gs_thread_context ThreadContext;
-
- u8* MemoryBase;
- u32 MemorySize;
-
- b32 WindowIsVisible;
- rect2 WindowBounds;
- r32 DeltaTime;
- mouse_state Mouse;
-
- // Application Services
- initialize_application* InitializeApplication;
- reload_static_data* ReloadStaticData;
- update_and_render* UpdateAndRender;
- cleanup_application* CleanupApplication;
-
- platform_thread_manager* ThreadManager;
- platform_socket_manager* SocketManager;
-
- // Platform Services
- gs_work_queue* GeneralWorkQueue;
-
- platform_get_gpu_texture_handle* PlatformGetGPUTextureHandle;
- platform_get_font_info* PlatformGetFontInfo;
- platform_draw_font_codepoint* PlatformDrawFontCodepoint;
-
- platform_get_socket_handle* PlatformGetSocketHandle;
+ gs_thread_context ThreadContext;
+
+ u8* MemoryBase;
+ u32 MemorySize;
+
+ b32 WindowIsVisible;
+ rect2 WindowBounds;
+ r64 TotalTime;
+ r32 DeltaTime;
+ mouse_state Mouse;
+
+ // Application Services
+ initialize_application* InitializeApplication;
+ reload_static_data* ReloadStaticData;
+ update_and_render* UpdateAndRender;
+ cleanup_application* CleanupApplication;
+
+ platform_thread_manager* ThreadManager;
+ platform_socket_manager* SocketManager;
+
+ // Platform Services
+ gs_work_queue* GeneralWorkQueue;
+
+ platform_get_gpu_texture_handle* PlatformGetGPUTextureHandle;
+ platform_get_font_info* PlatformGetFontInfo;
+ platform_draw_font_codepoint* PlatformDrawFontCodepoint;
+
+ platform_get_socket_handle* PlatformGetSocketHandle;
+
+ system_time SystemTime_Last;
+ system_time SystemTime_Current;
+
+ //
+ bool Headless;
};
#define FOLDHAUS_PLATFORM_H
diff --git a/src/app/foldhaus_renderer.cpp b/src/app/foldhaus_renderer.cpp
index bfd6522..51c2579 100644
--- a/src/app/foldhaus_renderer.cpp
+++ b/src/app/foldhaus_renderer.cpp
@@ -6,192 +6,192 @@
#ifndef FOLDHAUS_RENDERER_CPP
internal render_command_buffer
-AllocateRenderCommandBuffer (u8* Memory, s32 Size, renderer_realloc* Realloc)
+AllocateRenderCommandBuffer (u8* Memory, s32 Size, gs_thread_context Ctx)
{
- render_command_buffer Result = {};
- Result.CommandMemory = Memory;
- Result.CommandMemorySize = Size;
- Result.CommandMemoryUsed = 0;
- Result.Realloc = Realloc;
- return Result;
+ render_command_buffer Result = {};
+ Result.CommandMemory = Memory;
+ Result.CommandMemorySize = Size;
+ Result.CommandMemoryUsed = 0;
+ Result.Ctx = Ctx;
+ return Result;
}
internal render_command_buffer
AllocateRenderCommandBuffer(u32 MemorySize,
gs_memory_arena* Arena,
- renderer_realloc* Realloc)
+ gs_thread_context Ctx)
{
- u8* Memory = PushSize(Arena, MemorySize);
- return AllocateRenderCommandBuffer(Memory, MemorySize, Realloc);
+ u8* Memory = PushSize(Arena, MemorySize).Memory;
+ return AllocateRenderCommandBuffer(Memory, MemorySize, Ctx);
}
internal void
Render3DQuadBatch (u8* CommandData, s32 TriCount)
{
- DEBUG_TRACK_FUNCTION;
-
- v4* Vertecies = (v4*)(CommandData + BATCH_3D_VERTECIES_OFFSET(TriCount));
- v2* UVs = (v2*)(CommandData + BATCH_3D_UVS_OFFSET(TriCount));
- v4* Colors = (v4*)(CommandData + BATCH_3D_COLORS_OFFSET(TriCount));
-
+ DEBUG_TRACK_FUNCTION;
+
+ v4* Vertecies = (v4*)(CommandData + BATCH_3D_VERTECIES_OFFSET(TriCount));
+ v2* UVs = (v2*)(CommandData + BATCH_3D_UVS_OFFSET(TriCount));
+ v4* Colors = (v4*)(CommandData + BATCH_3D_COLORS_OFFSET(TriCount));
+
#if IMMEDIATE_MODE_RENDERING
+
+ for (s32 Tri = 0; Tri < TriCount; Tri++)
+ {
+ v4 P0 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 0)];
+ v4 P1 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 1)];
+ v4 P2 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 2)];
+ v2 UV0 = UVs[BATCH_3D_UV_INDEX(Tri, 0)];
+ v2 UV1 = UVs[BATCH_3D_UV_INDEX(Tri, 1)];
+ v2 UV2 = UVs[BATCH_3D_UV_INDEX(Tri, 2)];
+ v4 C0 = Colors[BATCH_3D_COLOR_INDEX(Tri, 0)];
+ v4 C1 = Colors[BATCH_3D_COLOR_INDEX(Tri, 1)];
+ v4 C2 = Colors[BATCH_3D_COLOR_INDEX(Tri, 2)];
- for (s32 Tri = 0; Tri < TriCount; Tri++)
- {
- v4 P0 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 0)];
- v4 P1 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 1)];
- v4 P2 = Vertecies[BATCH_3D_VERTEX_INDEX(Tri, 2)];
- v2 UV0 = UVs[BATCH_3D_UV_INDEX(Tri, 0)];
- v2 UV1 = UVs[BATCH_3D_UV_INDEX(Tri, 1)];
- v2 UV2 = UVs[BATCH_3D_UV_INDEX(Tri, 2)];
- v4 C0 = Colors[BATCH_3D_COLOR_INDEX(Tri, 0)];
- v4 C1 = Colors[BATCH_3D_COLOR_INDEX(Tri, 1)];
- v4 C2 = Colors[BATCH_3D_COLOR_INDEX(Tri, 2)];
-
- OpenGLDraw3DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
- }
+ OpenGLDraw3DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
+ }
#else
- OpenGLRenderTriBuffer((u8*)Vertecies, 4, (u8*)UVs, 2, (u8*)Colors, 4, TriCount * 3);
+ OpenGLRenderTriBuffer((u8*)Vertecies, 4, (u8*)UVs, 2, (u8*)Colors, 4, TriCount * 3);
#endif
}
internal void
Render2DQuadBatch (u8* CommandData, s32 QuadCount)
{
- DEBUG_TRACK_FUNCTION;
-
- v2* Vertecies = (v2*)(CommandData + BATCH_2D_VERTECIES_OFFSET(QuadCount));
- v2* UVs = (v2*)(CommandData + BATCH_2D_UVS_OFFSET(QuadCount));
- v4* Colors = (v4*)(CommandData + BATCH_2D_COLORS_OFFSET(QuadCount));
-
+ DEBUG_TRACK_FUNCTION;
+
+ v2* Vertecies = (v2*)(CommandData + BATCH_2D_VERTECIES_OFFSET(QuadCount));
+ v2* UVs = (v2*)(CommandData + BATCH_2D_UVS_OFFSET(QuadCount));
+ v4* Colors = (v4*)(CommandData + BATCH_2D_COLORS_OFFSET(QuadCount));
+
#if IMMEDIATE_MODE_RENDERING
- for (s32 Quad = 0; Quad < QuadCount; Quad++)
+ for (s32 Quad = 0; Quad < QuadCount; Quad++)
+ {
+ for (s32 Tri = 0; Tri < 2; Tri++)
{
- for (s32 Tri = 0; Tri < 2; Tri++)
- {
- v2 P0 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 0)];
- v2 P1 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 1)];
- v2 P2 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 2)];
- v2 UV0 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 0)];
- v2 UV1 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 1)];
- v2 UV2 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 2)];
- v4 C0 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 0)];
- v4 C1 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 1)];
- v4 C2 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 2)];
-
- OpenGLDraw2DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
- }
+ v2 P0 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 0)];
+ v2 P1 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 1)];
+ v2 P2 = Vertecies[BATCH_2D_VERTEX_INDEX(Quad, Tri, 2)];
+ v2 UV0 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 0)];
+ v2 UV1 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 1)];
+ v2 UV2 = UVs[BATCH_2D_UV_INDEX(Quad, Tri, 2)];
+ v4 C0 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 0)];
+ v4 C1 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 1)];
+ v4 C2 = Colors[BATCH_2D_COLOR_INDEX(Quad, Tri, 2)];
+
+ OpenGLDraw2DTri(P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
}
+ }
#else
- OpenGLRenderTriBuffer((u8*)Vertecies, 2, (u8*)UVs, 2, (u8*)Colors, 4, QuadCount * 2 * 3);
+ OpenGLRenderTriBuffer((u8*)Vertecies, 2, (u8*)UVs, 2, (u8*)Colors, 4, QuadCount * 2 * 3);
#endif
}
internal void
RenderCommandBuffer (render_command_buffer CommandBuffer)
{
- DEBUG_TRACK_FUNCTION;
-
- glMatrixMode(GL_TEXTURE_2D);
- glLoadIdentity();
-
- glClearColor(0.1f, 0.1f, 0.1f, 1);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glDisable(GL_TEXTURE_2D);
- b32 GLTextureEnabled = false;
-
- u8* CurrentPosition = CommandBuffer.CommandMemory;
- while(CurrentPosition < CommandBuffer.CommandMemory + CommandBuffer.CommandMemoryUsed)
+ DEBUG_TRACK_FUNCTION;
+
+ glMatrixMode(GL_TEXTURE_2D);
+ glLoadIdentity();
+
+ glClearColor(0.1f, 0.1f, 0.1f, 1);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glDisable(GL_TEXTURE_2D);
+ b32 GLTextureEnabled = false;
+
+ u8* CurrentPosition = CommandBuffer.CommandMemory;
+ while(CurrentPosition < CommandBuffer.CommandMemory + CommandBuffer.CommandMemoryUsed)
+ {
+ render_command_header* CommandHeader = (render_command_header*)CurrentPosition;
+ CurrentPosition += sizeof(render_command_header);
+ switch (CommandHeader->Type)
{
- render_command_header* CommandHeader = (render_command_header*)CurrentPosition;
- CurrentPosition += sizeof(render_command_header);
- switch (CommandHeader->Type)
+ case RenderCommand_render_command_set_render_mode:
+ {
+ DEBUG_TRACK_SCOPE(SetRenderMode);
+
+ render_command_set_render_mode* Command = (render_command_set_render_mode*)(CommandHeader + 1);
+
+ glViewport(Command->ViewOffsetX, Command->ViewOffsetY,
+ Command->ViewWidth, Command->ViewHeight);
+
+ LoadModelView(Command->ModelView.Array);
+ LoadProjection(Command->Projection.Array);
+
+ if (Command->UseDepthBuffer)
{
- case RenderCommand_render_command_set_render_mode:
- {
- DEBUG_TRACK_SCOPE(SetRenderMode);
-
- render_command_set_render_mode* Command = (render_command_set_render_mode*)(CommandHeader + 1);
-
- glViewport(Command->ViewOffsetX, Command->ViewOffsetY,
- Command->ViewWidth, Command->ViewHeight);
-
- LoadModelView(Command->ModelView.Array);
- LoadProjection(Command->Projection.Array);
-
- if (Command->UseDepthBuffer)
- {
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
- }
- else
- {
- glDisable(GL_DEPTH_TEST);
- }
-
- CurrentPosition += sizeof(render_command_set_render_mode);
- }break;
-
- case RenderCommand_render_command_clear_screen:
- {
- DEBUG_TRACK_SCOPE(RendererClearScreen);
-
- render_command_clear_screen* Command = (render_command_clear_screen*)(CommandHeader + 1);
-
- ClearRenderBuffer();
-
- CurrentPosition += sizeof(render_command_clear_screen);
- }break;
-
- case RenderCommand_render_batch_command_quad_2d:
- {
- render_batch_command_quad_2d* Command = (render_batch_command_quad_2d*)(CommandHeader + 1);
-
- if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; }
- u8* CommandData = (u8*)(Command + 1);
- Render2DQuadBatch(CommandData, Command->QuadCount);
-
- CurrentPosition += sizeof(render_batch_command_quad_2d) + Command->DataSize;
- }break;
-
- case RenderCommand_render_batch_command_quad_3d:
- {
- render_batch_command_quad_3d* Command = (render_batch_command_quad_3d*)(CommandHeader + 1);
-
- if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; }
- u8* CommandData = (u8*)(Command + 1);
- Render3DQuadBatch(CommandData, Command->QuadCount * 2);
-
- CurrentPosition += sizeof(render_batch_command_quad_3d) + Command->DataSize;
- }break;
-
- case RenderCommand_render_batch_command_texture_2d:
- {
- render_batch_command_texture_2d* Command = (render_batch_command_texture_2d*)(CommandHeader + 1);
-
- if (!GLTextureEnabled) { glEnable(GL_TEXTURE_2D); GLTextureEnabled = true; }
- Assert(Command->Texture.Handle > 0);
- glBindTexture(GL_TEXTURE_2D, Command->Texture.Handle);
- u8* CommandData = (u8*)(Command + 1);
- Render2DQuadBatch(CommandData, Command->QuadCount);
-
- CurrentPosition += sizeof(render_batch_command_texture_2d) + Command->DataSize;
- }break;
-
- default:
- {
- InvalidCodePath;
- }break;
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
}
+ else
+ {
+ glDisable(GL_DEPTH_TEST);
+ }
+
+ CurrentPosition += sizeof(render_command_set_render_mode);
+ }break;
+
+ case RenderCommand_render_command_clear_screen:
+ {
+ DEBUG_TRACK_SCOPE(RendererClearScreen);
+
+ render_command_clear_screen* Command = (render_command_clear_screen*)(CommandHeader + 1);
+
+ ClearRenderBuffer();
+
+ CurrentPosition += sizeof(render_command_clear_screen);
+ }break;
+
+ case RenderCommand_render_batch_command_quad_2d:
+ {
+ render_batch_command_quad_2d* Command = (render_batch_command_quad_2d*)(CommandHeader + 1);
+
+ if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; }
+ u8* CommandData = (u8*)(Command + 1);
+ Render2DQuadBatch(CommandData, Command->QuadCount);
+
+ CurrentPosition += sizeof(render_batch_command_quad_2d) + Command->DataSize;
+ }break;
+
+ case RenderCommand_render_batch_command_quad_3d:
+ {
+ render_batch_command_quad_3d* Command = (render_batch_command_quad_3d*)(CommandHeader + 1);
+
+ if (GLTextureEnabled) { glDisable(GL_TEXTURE_2D); GLTextureEnabled = false; }
+ u8* CommandData = (u8*)(Command + 1);
+ Render3DQuadBatch(CommandData, Command->QuadCount * 2);
+
+ CurrentPosition += sizeof(render_batch_command_quad_3d) + Command->DataSize;
+ }break;
+
+ case RenderCommand_render_batch_command_texture_2d:
+ {
+ render_batch_command_texture_2d* Command = (render_batch_command_texture_2d*)(CommandHeader + 1);
+
+ if (!GLTextureEnabled) { glEnable(GL_TEXTURE_2D); GLTextureEnabled = true; }
+ Assert(Command->Texture.Handle > 0);
+ glBindTexture(GL_TEXTURE_2D, Command->Texture.Handle);
+ u8* CommandData = (u8*)(Command + 1);
+ Render2DQuadBatch(CommandData, Command->QuadCount);
+
+ CurrentPosition += sizeof(render_batch_command_texture_2d) + Command->DataSize;
+ }break;
+
+ default:
+ {
+ InvalidCodePath;
+ }break;
}
+ }
}
internal void
ClearRenderBuffer (render_command_buffer* Buffer)
{
- Buffer->CommandMemoryUsed = 0;
+ Buffer->CommandMemoryUsed = 0;
}
#define FOLDHAUS_RENDERER_CPP
diff --git a/src/app/foldhaus_renderer.h b/src/app/foldhaus_renderer.h
index a40c0f4..6e48836 100644
--- a/src/app/foldhaus_renderer.h
+++ b/src/app/foldhaus_renderer.h
@@ -9,104 +9,104 @@
struct camera
{
- r32 FieldOfView;
- r32 AspectRatio;
- r32 Near, Far;
- v3 Position;
- v3 LookAt;
+ r32 FieldOfView;
+ r32 AspectRatio;
+ r32 Near, Far;
+ v3 Position;
+ v3 LookAt;
};
inline m44
GetCameraModelViewMatrix (camera Camera)
{
- m44 RotationMatrix = M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt));
- m44 PositionMatrix = M44Translation(ToV4Point(-Camera.Position));
- m44 ModelViewMatrix = RotationMatrix * PositionMatrix;
- return ModelViewMatrix;
+ m44 RotationMatrix = M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt));
+ m44 PositionMatrix = M44Translation(ToV4Point(-Camera.Position));
+ m44 ModelViewMatrix = RotationMatrix * PositionMatrix;
+ return ModelViewMatrix;
}
inline m44
GetCameraPerspectiveProjectionMatrix(camera Camera)
{
- m44 Result = M44ProjectionPerspective(Camera.FieldOfView, Camera.AspectRatio, Camera.Near, Camera.Far);
- return Result;
+ m44 Result = M44ProjectionPerspective(Camera.FieldOfView, Camera.AspectRatio, Camera.Near, Camera.Far);
+ return Result;
}
internal m44
GetCameraMatrix(camera Camera)
{
- m44 ModelView = GetCameraModelViewMatrix(Camera);
- m44 Projection = GetCameraPerspectiveProjectionMatrix(Camera);
- m44 Result = Projection * ModelView;
- return Result;
+ m44 ModelView = GetCameraModelViewMatrix(Camera);
+ m44 Projection = GetCameraPerspectiveProjectionMatrix(Camera);
+ m44 Result = Projection * ModelView;
+ return Result;
}
internal v2
ProjectWorldPointToScreen(v4 WorldSpacePoint, camera Camera, rect2 WindowBounds)
{
- v2 WindowExtents = v2{Rect2Width(WindowBounds), Rect2Height(WindowBounds)};
- v4 ProjectedPosition = GetCameraMatrix(Camera) * WorldSpacePoint;
- ProjectedPosition.xyz /= ProjectedPosition.w;
- v2 ScreenPosition = V2MultiplyPairwise(ProjectedPosition.xy, (WindowExtents / 2)) + (WindowExtents / 2);
-
- return ScreenPosition;
+ v2 WindowExtents = v2{Rect2Width(WindowBounds), Rect2Height(WindowBounds)};
+ v4 ProjectedPosition = GetCameraMatrix(Camera) * WorldSpacePoint;
+ ProjectedPosition.xyz /= ProjectedPosition.w;
+ v2 ScreenPosition = V2MultiplyPairwise(ProjectedPosition.xy, (WindowExtents / 2)) + (WindowExtents / 2);
+
+ return ScreenPosition;
}
internal v4_ray
ProjectScreenPointToWorldRay(v2 ScreenPoint, camera Camera, rect2 WindowBounds)
{
- v4_ray Result = {0};
-
- r32 TanFOVOverTwo = TanR32(DegToRadR32(Camera.FieldOfView / 2.0f));
- r32 Aspect = RectAspectRatio(WindowBounds);
-
- r32 NormalizedX = ScreenPoint.x / Rect2Width(WindowBounds);
- r32 NormalizedY = ScreenPoint.y / Rect2Height(WindowBounds);
-
- r32 CenteredX = (2.0f * NormalizedX) - 1.0f;
- r32 CenteredY = (2.0f * NormalizedY) - 1.0f;
-
- r32 ScaledX = CenteredX * Aspect;
- r32 ScaledY = CenteredY;
-
- r32 CameraX = ScaledX * TanFOVOverTwo;
- r32 CameraY = ScaledY * TanFOVOverTwo;
-
- r32 Near = Camera.Near;
- r32 Far = Camera.Far;
- v3 MousePointOnNearPlane = v3{CameraX, CameraY, -1} * Near;
- v3 MousePointOnFarPlane = v3{CameraX, CameraY, -1} * Far;
-
- v4 MouseRayDirection = ToV4Vec(V3Normalize(MousePointOnFarPlane - MousePointOnNearPlane));
- m44 CameraTransform = M44Transpose(M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt)));
-
- Result.Origin = ToV4Point(Camera.Position);
- Result.Direction = CameraTransform * MouseRayDirection;
-
- return Result;
+ v4_ray Result = {0};
+
+ r32 TanFOVOverTwo = TanR32(DegToRadR32(Camera.FieldOfView / 2.0f));
+ r32 Aspect = RectAspectRatio(WindowBounds);
+
+ r32 NormalizedX = ScreenPoint.x / Rect2Width(WindowBounds);
+ r32 NormalizedY = ScreenPoint.y / Rect2Height(WindowBounds);
+
+ r32 CenteredX = (2.0f * NormalizedX) - 1.0f;
+ r32 CenteredY = (2.0f * NormalizedY) - 1.0f;
+
+ r32 ScaledX = CenteredX * Aspect;
+ r32 ScaledY = CenteredY;
+
+ r32 CameraX = ScaledX * TanFOVOverTwo;
+ r32 CameraY = ScaledY * TanFOVOverTwo;
+
+ r32 Near = Camera.Near;
+ r32 Far = Camera.Far;
+ v3 MousePointOnNearPlane = v3{CameraX, CameraY, -1} * Near;
+ v3 MousePointOnFarPlane = v3{CameraX, CameraY, -1} * Far;
+
+ v4 MouseRayDirection = ToV4Vec(V3Normalize(MousePointOnFarPlane - MousePointOnNearPlane));
+ m44 CameraTransform = M44Transpose(M44LookAt(ToV4Point(Camera.Position), ToV4Point(Camera.LookAt)));
+
+ Result.Origin = ToV4Point(Camera.Position);
+ Result.Direction = CameraTransform * MouseRayDirection;
+
+ return Result;
}
// Render Commands
// Discriminated Union
enum render_command_type
{
- RenderCommand_Invalid,
-
- RenderCommand_render_command_clear_screen,
- RenderCommand_render_command_set_render_mode,
-
- RenderCommand_render_batch_command_quad_2d,
- RenderCommand_render_batch_command_quad_3d,
- RenderCommand_render_batch_command_texture_2d,
-
- RenderCommand_render_command_texture_3d,
-
- RenderCommand_Count
+ RenderCommand_Invalid,
+
+ RenderCommand_render_command_clear_screen,
+ RenderCommand_render_command_set_render_mode,
+
+ RenderCommand_render_batch_command_quad_2d,
+ RenderCommand_render_batch_command_quad_3d,
+ RenderCommand_render_batch_command_texture_2d,
+
+ RenderCommand_render_command_texture_3d,
+
+ RenderCommand_Count
};
struct render_command_header
{
- render_command_type Type;
+ render_command_type Type;
};
// NOTE(Peter): Just to keep with the rest of the system
@@ -114,23 +114,22 @@ struct render_command_clear_screen {};
struct render_quad_2d
{
- v2 Min, Max;
+ v2 Min, Max;
};
struct render_quad_3d
{
- v4 P0, P1, P2, P3;
+ v4 P0, P1, P2, P3;
};
struct render_texture
{
- // TODO(Peter): Is all this necessary?
- u8* Memory;
- s32 Handle;
- s32 Width;
- s32 Height;
- s32 BytesPerPixel;
- s32 Stride;
+ u8* Memory;
+ s32 Handle;
+ s32 Width;
+ s32 Height;
+ s32 BytesPerPixel;
+ s32 Stride;
};
#define BATCH_3D_SIZE(tricount) (((sizeof(v4) + sizeof(v2) + sizeof(v4)) * 3) * tricount)
@@ -151,57 +150,57 @@ struct render_texture
struct render_quad_batch_constructor
{
- s32 Max;
- s32 Count;
-
- v4* Vertecies;
- v2* UVs;
- v4* ColorsV;
+ s32 Max;
+ s32 Count;
+
+ v4* Vertecies;
+ v2* UVs;
+ v4* ColorsV;
};
struct render_batch_command_quad_2d
{
- s32 QuadCount;
- s32 DataSize;
- // NOTE(Peter): The data immediately follows the command in memory
+ s32 QuadCount;
+ s32 DataSize;
+ // NOTE(Peter): The data immediately follows the command in memory
};
struct render_batch_command_quad_3d
{
- s32 QuadCount;
- s32 DataSize;
- // NOTE(Peter): The data immediately follows the command in memory
+ s32 QuadCount;
+ s32 DataSize;
+ // NOTE(Peter): The data immediately follows the command in memory
};
struct render_command_texture_2d
{
- render_quad_2d Quad;
- render_quad_2d UV;
- v4 Color;
- render_texture Texture;
+ render_quad_2d Quad;
+ render_quad_2d UV;
+ v4 Color;
+ render_texture Texture;
};
struct render_batch_command_texture_2d
{
- s32 QuadCount;
- s32 DataSize;
- render_texture Texture;
+ s32 QuadCount;
+ s32 DataSize;
+ render_texture Texture;
};
struct render_command_texture_3d
{
- render_quad_3d Quad;
- v4 Color;
- render_texture Texture;
+ render_quad_3d Quad;
+ v4 Color;
+ render_texture Texture;
};
struct render_command_set_render_mode
{
- m44 ModelView;
- m44 Projection;
- r32 ViewOffsetX, ViewOffsetY;
- r32 ViewWidth, ViewHeight;
- b32 UseDepthBuffer;
+ m44 ModelView;
+ m44 Projection;
+ r32 ViewOffsetX, ViewOffsetY;
+ r32 ViewWidth, ViewHeight;
+ b32 UseDepthBuffer;
};
typedef u8* renderer_realloc(u8* Base, s32 CurrentSize, s32 NewSize);
@@ -210,14 +209,14 @@ typedef u8* renderer_realloc(u8* Base, s32 CurrentSize, s32 NewSize);
struct render_command_buffer
{
- u8* CommandMemory;
- s32 CommandMemoryUsed;
- s32 CommandMemorySize;
-
- renderer_realloc* Realloc;
-
- s32 ViewWidth;
- s32 ViewHeight;
+ u8* CommandMemory;
+ s32 CommandMemoryUsed;
+ s32 CommandMemorySize;
+
+ gs_thread_context Ctx;
+
+ s32 ViewWidth;
+ s32 ViewHeight;
};
///
@@ -227,49 +226,49 @@ struct render_command_buffer
internal u32
PackColorStructU8 (u8 R, u8 G, u8 B, u8 A)
{
- u32 Result = (u32)(A << 24 |
- R << 16 |
- G << 8 |
- B<< 0);
- return Result;
+ u32 Result = (u32)(A << 24 |
+ R << 16 |
+ G << 8 |
+ B<< 0);
+ return Result;
}
internal u32
PackColorStructR32 (r32 In_R, r32 In_G, r32 In_B, r32 In_A)
{
- Assert ((In_R >= 0.0f && In_R <= 1.0f) &&
- (In_G >= 0.0f && In_G <= 1.0f) &&
- (In_B >= 0.0f && In_B <= 1.0f) &&
- (In_A >= 0.0f && In_A <= 1.0f));
-
- u8 R = (u8)(255 * In_R);
- u8 G = (u8)(255 * In_G);
- u8 B = (u8)(255 * In_B);
- u8 A = (u8)(255 * In_A);
-
- u32 Result = (u32)(A << 24 |
- R << 16 |
- G << 8 |
- B<< 0);
- return Result;
+ Assert ((In_R >= 0.0f && In_R <= 1.0f) &&
+ (In_G >= 0.0f && In_G <= 1.0f) &&
+ (In_B >= 0.0f && In_B <= 1.0f) &&
+ (In_A >= 0.0f && In_A <= 1.0f));
+
+ u8 R = (u8)(255 * In_R);
+ u8 G = (u8)(255 * In_G);
+ u8 B = (u8)(255 * In_B);
+ u8 A = (u8)(255 * In_A);
+
+ u32 Result = (u32)(A << 24 |
+ R << 16 |
+ G << 8 |
+ B<< 0);
+ return Result;
}
internal void
ResizeBufferIfNecessary(render_command_buffer* Buffer, s32 DataSize)
{
- if (Buffer->CommandMemoryUsed + DataSize > Buffer->CommandMemorySize)
- {
- // NOTE(Peter): If this becomes a problem just go back to the original solution of
- // NewSize = Buffer->CommandMemorySize + (2 * DataSize);
- s32 SpaceAvailable = Buffer->CommandMemorySize - Buffer->CommandMemoryUsed;
- s32 SpaceNeeded = DataSize - SpaceAvailable; // This is known to be positive at this point
- s32 AdditionSize = Max(SpaceNeeded, COMMAND_BUFFER_MIN_GROW_SIZE);
- s32 NewSize = Buffer->CommandMemorySize + AdditionSize;
- Buffer->CommandMemory = Buffer->Realloc(Buffer->CommandMemory,
- Buffer->CommandMemorySize,
- NewSize);
- Buffer->CommandMemorySize = NewSize;
- }
+ if (Buffer->CommandMemoryUsed + DataSize > Buffer->CommandMemorySize)
+ {
+ // NOTE(Peter): If this becomes a problem just go back to the original solution of
+ // NewSize = Buffer->CommandMemorySize + (2 * DataSize);
+ s32 SpaceAvailable = Buffer->CommandMemorySize - Buffer->CommandMemoryUsed;
+ s32 SpaceNeeded = DataSize - SpaceAvailable; // This is known to be positive at this point
+ s32 AdditionSize = Max(SpaceNeeded, COMMAND_BUFFER_MIN_GROW_SIZE);
+ s32 NewSize = Buffer->CommandMemorySize + AdditionSize;
+
+ Free(Buffer->Ctx.Allocator, Buffer->CommandMemory, Buffer->CommandMemorySize);
+ Buffer->CommandMemory = Alloc(Buffer->Ctx.Allocator, NewSize, "Renderer");
+ Buffer->CommandMemorySize = NewSize;
+ }
}
// Batch
@@ -277,68 +276,68 @@ ResizeBufferIfNecessary(render_command_buffer* Buffer, s32 DataSize)
internal s32
PushQuad3DBatch (render_command_buffer* Buffer, render_quad_batch_constructor* Constructor, u8* MemStart, s32 TriCount, s32 DataSize, b32 UseIntegerColor = false)
{
- Constructor->Max = TriCount;
- Constructor->Count = 0;
-
- Constructor->Vertecies = (v4*)(MemStart + BATCH_3D_VERTECIES_OFFSET(TriCount));
- Constructor->UVs = (v2*)(MemStart + BATCH_3D_UVS_OFFSET(TriCount));
- Constructor->ColorsV = (v4*)(MemStart + BATCH_3D_COLORS_OFFSET(TriCount));
-
- Buffer->CommandMemoryUsed += DataSize;
- return DataSize;
+ Constructor->Max = TriCount;
+ Constructor->Count = 0;
+
+ Constructor->Vertecies = (v4*)(MemStart + BATCH_3D_VERTECIES_OFFSET(TriCount));
+ Constructor->UVs = (v2*)(MemStart + BATCH_3D_UVS_OFFSET(TriCount));
+ Constructor->ColorsV = (v4*)(MemStart + BATCH_3D_COLORS_OFFSET(TriCount));
+
+ Buffer->CommandMemoryUsed += DataSize;
+ return DataSize;
}
internal s32
PushQuad2DBatch (render_command_buffer* Buffer, render_quad_batch_constructor* Constructor, s32 QuadCount, s32 DataSize, u8* MemStart)
{
- ZeroMemoryBlock(MemStart, DataSize);
-
- Constructor->Max = QuadCount;
- Constructor->Count = 0;
-
- Constructor->Vertecies = (v4*)(MemStart + BATCH_2D_VERTECIES_OFFSET(QuadCount));
- Constructor->UVs = (v2*)(MemStart + BATCH_2D_UVS_OFFSET(QuadCount));
- Constructor->ColorsV = (v4*)(MemStart + BATCH_2D_COLORS_OFFSET(QuadCount));
-
- Buffer->CommandMemoryUsed += DataSize;
- return DataSize;
+ ZeroMemoryBlock(MemStart, DataSize);
+
+ Constructor->Max = QuadCount;
+ Constructor->Count = 0;
+
+ Constructor->Vertecies = (v4*)(MemStart + BATCH_2D_VERTECIES_OFFSET(QuadCount));
+ Constructor->UVs = (v2*)(MemStart + BATCH_2D_UVS_OFFSET(QuadCount));
+ Constructor->ColorsV = (v4*)(MemStart + BATCH_2D_COLORS_OFFSET(QuadCount));
+
+ Buffer->CommandMemoryUsed += DataSize;
+ return DataSize;
}
internal s32
ThreadSafeIncrementQuadConstructorCount (render_quad_batch_constructor* Constructor)
{
- s32 Result = InterlockedIncrement((long*)&Constructor->Count);
- // NOTE(Peter): Have to decrement the value by one.
- // Interlocked Increment acts as (++Constructor->Count), not (Constructor->Count++) which
- // is what we wanted;
- // This was causing the first triangle to be garbage data.
- Result -= 1;
- return Result;
+ s32 Result = InterlockedIncrement((long*)&Constructor->Count);
+ // NOTE(Peter): Have to decrement the value by one.
+ // Interlocked Increment acts as (++Constructor->Count), not (Constructor->Count++) which
+ // is what we wanted;
+ // This was causing the first triangle to be garbage data.
+ Result -= 1;
+ return Result;
}
struct quad_batch_constructor_reserved_range
{
- s32 Start;
- s32 OnePastLast;
+ s32 Start;
+ s32 OnePastLast;
};
internal quad_batch_constructor_reserved_range
ReserveRangeInQuadConstructor(render_quad_batch_constructor* Constructor, s32 TrisNeeded)
{
- quad_batch_constructor_reserved_range Result = {};
- Result.OnePastLast = Constructor->Count + TrisNeeded;
- Constructor->Count = Result.OnePastLast;
- Result.Start = Result.OnePastLast - TrisNeeded;
- return Result;
+ quad_batch_constructor_reserved_range Result = {};
+ Result.OnePastLast = Constructor->Count + TrisNeeded;
+ Constructor->Count = Result.OnePastLast;
+ Result.Start = Result.OnePastLast - TrisNeeded;
+ return Result;
}
internal quad_batch_constructor_reserved_range
ThreadSafeReserveRangeInQuadConstructor(render_quad_batch_constructor* Constructor, s32 TrisNeeded)
{
- quad_batch_constructor_reserved_range Result = {};
- Result.OnePastLast = InterlockedAdd((long*)&Constructor->Count, TrisNeeded);
- Result.Start = Result.OnePastLast - TrisNeeded;
- return Result;
+ quad_batch_constructor_reserved_range Result = {};
+ Result.OnePastLast = InterlockedAdd((long*)&Constructor->Count, TrisNeeded);
+ Result.Start = Result.OnePastLast - TrisNeeded;
+ return Result;
}
inline void
@@ -347,22 +346,22 @@ SetTri3DInBatch (render_quad_batch_constructor* Constructor, s32 TriIndex,
v2 UV0, v2 UV1, v2 UV2,
v4 C0, v4 C1, v4 C2)
{
- //Assert(P0.w != 0 && P1.w != 0 && P2.w != 0); // Passing vectors, rather than positions. Will draw wrong
-
- // Vertecies
- Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 0)] = P0;
- Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 1)] = P1;
- Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 2)] = P2;
-
- // UVs
- Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 0)] = UV0;
- Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 1)] = UV1;
- Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 2)] = UV1;
-
- // Color V0
- Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 0)] = C0;
- Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 1)] = C1;
- Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 2)] = C2;
+ //Assert(P0.w != 0 && P1.w != 0 && P2.w != 0); // Passing vectors, rather than positions. Will draw wrong
+
+ // Vertecies
+ Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 0)] = P0;
+ Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 1)] = P1;
+ Constructor->Vertecies[BATCH_3D_VERTEX_INDEX(TriIndex, 2)] = P2;
+
+ // UVs
+ Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 0)] = UV0;
+ Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 1)] = UV1;
+ Constructor->UVs[BATCH_3D_UV_INDEX(TriIndex, 2)] = UV1;
+
+ // Color V0
+ Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 0)] = C0;
+ Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 1)] = C1;
+ Constructor->ColorsV[BATCH_3D_COLOR_INDEX(TriIndex, 2)] = C2;
}
@@ -372,18 +371,18 @@ PushTri3DOnBatch (render_quad_batch_constructor* Constructor,
v2 UV0, v2 UV1, v2 UV2,
v4 C0, v4 C1, v4 C2)
{
- DEBUG_TRACK_FUNCTION;
- // TODO(Peter): I think we avoid doing cross thread filling of a batch so do we need this?
- s32 Tri = ThreadSafeIncrementQuadConstructorCount(Constructor);
- SetTri3DInBatch(Constructor, Tri, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
+ DEBUG_TRACK_FUNCTION;
+ // TODO(Peter): I think we avoid doing cross thread filling of a batch so do we need this?
+ s32 Tri = ThreadSafeIncrementQuadConstructorCount(Constructor);
+ SetTri3DInBatch(Constructor, Tri, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
};
internal void
PushQuad3DOnBatch (render_quad_batch_constructor* Constructor, v4 P0, v4 P1, v4 P2, v4 P3, v2 UVMin, v2 UVMax, v4 Color)
{
- Assert(Constructor->Count + 2 <= Constructor->Max);
- PushTri3DOnBatch(Constructor, P0, P1, P2, UVMin, v2{UVMax.x, UVMin.y}, UVMax, Color, Color, Color);
- PushTri3DOnBatch(Constructor, P0, P2, P3, UVMin, UVMax, v2{UVMin.x, UVMax.y}, Color, Color, Color);
+ Assert(Constructor->Count + 2 <= Constructor->Max);
+ PushTri3DOnBatch(Constructor, P0, P1, P2, UVMin, v2{UVMax.x, UVMin.y}, UVMax, Color, Color, Color);
+ PushTri3DOnBatch(Constructor, P0, P2, P3, UVMin, UVMax, v2{UVMin.x, UVMax.y}, Color, Color, Color);
}
internal void
@@ -392,15 +391,15 @@ PushQuad3DOnBatch (render_quad_batch_constructor* Constructor,
v2 UV0, v2 UV1, v2 UV2, v2 UV3,
v4 C0, v4 C1, v4 C2, v4 C3)
{
- Assert(Constructor->Count <= Constructor->Max);
- PushTri3DOnBatch(Constructor, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
- PushTri3DOnBatch(Constructor, P0, P2, P3, UV0, UV2, UV3, C0, C2, C3);
+ Assert(Constructor->Count <= Constructor->Max);
+ PushTri3DOnBatch(Constructor, P0, P1, P2, UV0, UV1, UV2, C0, C1, C2);
+ PushTri3DOnBatch(Constructor, P0, P2, P3, UV0, UV2, UV3, C0, C2, C3);
}
internal void
PushQuad3DOnBatch (render_quad_batch_constructor* Constructor, v4 P0, v4 P1, v4 P2, v4 P3, v4 Color)
{
- PushQuad3DOnBatch(Constructor, P0, P1, P2, P3, v2{0, 0}, v2{1, 1}, Color);
+ PushQuad3DOnBatch(Constructor, P0, P1, P2, P3, v2{0, 0}, v2{1, 1}, Color);
}
internal void
@@ -409,92 +408,99 @@ PushQuad2DOnBatch (render_quad_batch_constructor* Constructor,
v2 UV0, v2 UV1, v2 UV2, v2 UV3,
v4 C0, v4 C1, v4 C2, v4 C3)
{
- DEBUG_TRACK_FUNCTION;
-
- s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor);
- v2* Vertecies = (v2*)Constructor->Vertecies;
-
- // Tri 1
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0;
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1;
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2;
-
- // Tri 2
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0;
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2;
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3;
-
- // Tri 1 UVs
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UV0;
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = UV1;
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UV2;
- // Tri 2 UVs
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UV0;
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UV2;
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = UV3;
-
- // Tri 1 Colors
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = C0;
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = C1;
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = C2;
- // Tri 2 Colors
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = C0;
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = C2;
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = C3;
+ DEBUG_TRACK_FUNCTION;
+
+ s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor);
+ v2* Vertecies = (v2*)Constructor->Vertecies;
+
+ // Tri 1
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0;
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1;
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2;
+
+ // Tri 2
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0;
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2;
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3;
+
+ // Tri 1 UVs
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UV0;
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = UV1;
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UV2;
+ // Tri 2 UVs
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UV0;
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UV2;
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = UV3;
+
+ // Tri 1 Colors
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = C0;
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = C1;
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = C2;
+ // Tri 2 Colors
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = C0;
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = C2;
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = C3;
}
internal void
PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, v2 P2, v2 P3, v2 UVMin, v2 UVMax, v4 Color)
{
- DEBUG_TRACK_FUNCTION;
-
- s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor);
- v2* Vertecies = (v2*)Constructor->Vertecies;
-
- // Tri 1
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0;
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1;
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2;
-
- // Tri 2
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0;
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2;
- Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3;
-
- // Tri 1 UVs
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UVMin;
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = v2{UVMax.x, UVMin.y};
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UVMax;
- // Tri 2 UVs
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UVMin;
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UVMax;
- Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = v2{UVMin.x, UVMax.y};
-
- // Tri 1 Colors
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = Color;
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = Color;
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = Color;
- // Tri 2 Colors
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = Color;
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = Color;
- Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = Color;
+ DEBUG_TRACK_FUNCTION;
+
+ s32 Quad = ThreadSafeIncrementQuadConstructorCount(Constructor);
+ v2* Vertecies = (v2*)Constructor->Vertecies;
+
+ // Tri 1
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 0)] = P0;
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 1)] = P1;
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 0, 2)] = P2;
+
+ // Tri 2
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 0)] = P0;
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 1)] = P2;
+ Vertecies[BATCH_2D_VERTEX_INDEX(Quad, 1, 2)] = P3;
+
+ // Tri 1 UVs
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 0)] = UVMin;
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 1)] = v2{UVMax.x, UVMin.y};
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 0, 2)] = UVMax;
+ // Tri 2 UVs
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 0)] = UVMin;
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 1)] = UVMax;
+ Constructor->UVs[BATCH_2D_UV_INDEX(Quad, 1, 2)] = v2{UVMin.x, UVMax.y};
+
+ // Tri 1 Colors
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 0)] = Color;
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 1)] = Color;
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 0, 2)] = Color;
+ // Tri 2 Colors
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 0)] = Color;
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 1)] = Color;
+ Constructor->ColorsV[BATCH_2D_COLOR_INDEX(Quad, 1, 2)] = Color;
}
internal void
PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 Min, v2 Max, v4 Color)
{
- PushQuad2DOnBatch(Constructor, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y},
- v2{0, 0}, v2{1, 1}, Color);
+ PushQuad2DOnBatch(Constructor, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y},
+ v2{0, 0}, v2{1, 1}, Color);
+}
+
+internal void
+PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, rect2 Rect, v4 Color)
+{
+ PushQuad2DOnBatch(Constructor, v2{Rect.Min.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Max.y}, v2{Rect.Min.x, Rect.Max.y},
+ v2{0, 0}, v2{1, 1}, Color);
}
internal void
PushLine2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, r32 Thickness, v4 Color)
{
- r32 HalfThickness = Thickness / 2.0f;
- v2 Perpendicular = V2Normalize(V2PerpendicularCCW(P1 - P0)) * HalfThickness;
-
- PushQuad2DOnBatch(Constructor, P0 - Perpendicular, P1 - Perpendicular, P1 + Perpendicular, P0 + Perpendicular,
- v2{0, 0}, v2{1, 1}, Color);
+ r32 HalfThickness = Thickness / 2.0f;
+ v2 Perpendicular = V2Normalize(V2PerpendicularCCW(P1 - P0)) * HalfThickness;
+
+ PushQuad2DOnBatch(Constructor, P0 - Perpendicular, P1 - Perpendicular, P1 + Perpendicular, P0 + Perpendicular,
+ v2{0, 0}, v2{1, 1}, Color);
}
// Commands
@@ -503,174 +509,174 @@ PushLine2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, r32
internal u8*
PushRenderCommand_ (render_command_buffer* CommandBuffer, render_command_type CommandType, s32 CommandSize)
{
- ResizeBufferIfNecessary(CommandBuffer, CommandSize);
- Assert(CommandBuffer->CommandMemoryUsed + CommandSize <= CommandBuffer->CommandMemorySize);
-
- render_command_header* Header = (render_command_header*)(CommandBuffer->CommandMemory + CommandBuffer->CommandMemoryUsed);
- Header->Type = CommandType;
-
- u8* Result = (u8*)(Header + 1);
- CommandBuffer->CommandMemoryUsed += CommandSize;
-
- return Result;
+ ResizeBufferIfNecessary(CommandBuffer, CommandSize);
+ Assert(CommandBuffer->CommandMemoryUsed + CommandSize <= CommandBuffer->CommandMemorySize);
+
+ render_command_header* Header = (render_command_header*)(CommandBuffer->CommandMemory + CommandBuffer->CommandMemoryUsed);
+ Header->Type = CommandType;
+
+ u8* Result = (u8*)(Header + 1);
+ CommandBuffer->CommandMemoryUsed += CommandSize;
+
+ return Result;
}
internal render_command_set_render_mode*
PushRenderPerspective (render_command_buffer* Buffer, s32 OffsetX, s32 OffsetY, s32 ViewWidth, s32 ViewHeight, camera Camera)
{
- render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode);
-
- Command->ModelView = M44Transpose(GetCameraModelViewMatrix(Camera));
- Command->Projection = M44Transpose(GetCameraPerspectiveProjectionMatrix(Camera));
-
- Command->ViewOffsetX = (r32)OffsetX;
- Command->ViewOffsetY = (r32)OffsetY;
- Command->ViewWidth = (r32)ViewWidth;
- Command->ViewHeight = (r32)ViewHeight;
-
- Command->UseDepthBuffer = true;
-
- return Command;
+ render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode);
+
+ Command->ModelView = M44Transpose(GetCameraModelViewMatrix(Camera));
+ Command->Projection = M44Transpose(GetCameraPerspectiveProjectionMatrix(Camera));
+
+ Command->ViewOffsetX = (r32)OffsetX;
+ Command->ViewOffsetY = (r32)OffsetY;
+ Command->ViewWidth = (r32)ViewWidth;
+ Command->ViewHeight = (r32)ViewHeight;
+
+ Command->UseDepthBuffer = true;
+
+ return Command;
}
internal void
PushRenderPerspective(render_command_buffer* Buffer, rect2 Viewport, camera Camera)
{
- PushRenderPerspective(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport), Camera);
+ PushRenderPerspective(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport), Camera);
}
internal void
PushRenderOrthographic (render_command_buffer* Buffer, s32 OffsetX, s32 OffsetY, s32 ViewWidth, s32 ViewHeight)
{
- render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode);
- Command->ModelView = M44Identity();
- Command->Projection = M44ProjectionOrtho((r32)ViewWidth, (r32)ViewHeight, 0, 100, ViewWidth, 0, ViewHeight, 0);
-
- Command->ViewOffsetX = (r32)OffsetX;
- Command->ViewOffsetY = (r32)OffsetY;
- Command->ViewWidth = ViewWidth;
- Command->ViewHeight = ViewHeight;
-
- Command->UseDepthBuffer = false;;
+ render_command_set_render_mode* Command = PushRenderCommand(Buffer, render_command_set_render_mode);
+ Command->ModelView = M44Identity();
+ Command->Projection = M44ProjectionOrtho((r32)ViewWidth, (r32)ViewHeight, 0, 100, ViewWidth, 0, ViewHeight, 0);
+
+ Command->ViewOffsetX = (r32)OffsetX;
+ Command->ViewOffsetY = (r32)OffsetY;
+ Command->ViewWidth = ViewWidth;
+ Command->ViewHeight = ViewHeight;
+
+ Command->UseDepthBuffer = false;;
}
internal void
PushRenderOrthographic(render_command_buffer* Buffer, rect2 Viewport)
{
- PushRenderOrthographic(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport));
+ PushRenderOrthographic(Buffer, Viewport.Min.x, Viewport.Min.y, Rect2Width(Viewport), Rect2Height(Viewport));
}
internal void
PushRenderClearScreen (render_command_buffer* Buffer)
{
- render_command_clear_screen* Command = PushRenderCommand(Buffer, render_command_clear_screen);
+ render_command_clear_screen* Command = PushRenderCommand(Buffer, render_command_clear_screen);
}
internal render_quad_batch_constructor
PushRenderQuad2DBatch(render_command_buffer* Buffer, s32 QuadCount)
{
- s32 DataSize = BATCH_2D_SIZE(QuadCount);
- ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_2d));
- Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
-
- render_quad_batch_constructor Result = {};
-
- render_batch_command_quad_2d* Command = PushRenderCommand(Buffer, render_batch_command_quad_2d);
- Command->QuadCount = QuadCount;
- Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, DataSize, (u8*)(Command + 1));
-
- return Result;
+ s32 DataSize = BATCH_2D_SIZE(QuadCount);
+ ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_2d));
+ Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
+
+ render_quad_batch_constructor Result = {};
+
+ render_batch_command_quad_2d* Command = PushRenderCommand(Buffer, render_batch_command_quad_2d);
+ Command->QuadCount = QuadCount;
+ Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, DataSize, (u8*)(Command + 1));
+
+ return Result;
}
internal void
PushRenderQuad2D (render_command_buffer* Buffer, v2 Min, v2 Max, v4 Color)
{
- render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
- PushQuad2DOnBatch(&Batch, Min, Max, Color);
+ render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
+ PushQuad2DOnBatch(&Batch, Min, Max, Color);
}
internal void
PushRenderQuad2D (render_command_buffer* Buffer, rect2 Rect, v4 Color)
{
- render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
- PushQuad2DOnBatch(&Batch, Rect.Min, Rect.Max, Color);
+ render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
+ PushQuad2DOnBatch(&Batch, Rect.Min, Rect.Max, Color);
}
internal void
PushRenderQuad2DClipped (render_command_buffer* Buffer, rect2 Rect, rect2 ClippingBox, v4 Color)
{
- rect2 Clipped = Rect2Union(Rect, ClippingBox);
- render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
- PushQuad2DOnBatch(&Batch, Clipped.Min, Clipped.Max, Color);
+ rect2 Clipped = Rect2Union(Rect, ClippingBox);
+ render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
+ PushQuad2DOnBatch(&Batch, Clipped.Min, Clipped.Max, Color);
}
internal void
PushRenderQuad2D(render_command_buffer* Buffer, v2 P0, v2 P1, v2 P2, v2 P3, v4 Color)
{
- render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
- PushQuad2DOnBatch(&Batch, P0, P1, P2, P3, v2{0,0}, v2{1,1}, Color);
+ render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
+ PushQuad2DOnBatch(&Batch, P0, P1, P2, P3, v2{0,0}, v2{1,1}, Color);
}
internal void
PushRenderLine2D (render_command_buffer* Buffer, v2 P0, v2 P1, r32 Thickness, v4 Color)
{
- render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
- PushLine2DOnBatch(&Batch, P0, P1, Thickness, Color);
+ render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1);
+ PushLine2DOnBatch(&Batch, P0, P1, Thickness, Color);
}
internal render_quad_batch_constructor
PushRenderQuad3DBatch(render_command_buffer* Buffer, s32 QuadCount)
{
- s32 TriCount = QuadCount * 2;
- s32 DataSize = BATCH_3D_SIZE(TriCount);
- ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_3d));
- Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
-
- render_quad_batch_constructor Result = {};
-
- render_batch_command_quad_3d* Command = PushRenderCommand(Buffer, render_batch_command_quad_3d);
- Command->QuadCount = QuadCount;
- Command->DataSize = PushQuad3DBatch(Buffer, &Result, (u8*)(Command + 1), TriCount, DataSize);
-
- return Result;
+ s32 TriCount = QuadCount * 2;
+ s32 DataSize = BATCH_3D_SIZE(TriCount);
+ ResizeBufferIfNecessary(Buffer, DataSize + sizeof(render_batch_command_quad_3d));
+ Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
+
+ render_quad_batch_constructor Result = {};
+
+ render_batch_command_quad_3d* Command = PushRenderCommand(Buffer, render_batch_command_quad_3d);
+ Command->QuadCount = QuadCount;
+ Command->DataSize = PushQuad3DBatch(Buffer, &Result, (u8*)(Command + 1), TriCount, DataSize);
+
+ return Result;
}
internal void
PushRenderQuad3D (render_command_buffer* Buffer, v4 A, v4 B, v4 C, v4 D, v4 Color)
{
- render_quad_batch_constructor Batch = PushRenderQuad3DBatch(Buffer, 1);
- PushQuad3DOnBatch(&Batch, A, B, C, D, Color);
+ render_quad_batch_constructor Batch = PushRenderQuad3DBatch(Buffer, 1);
+ PushQuad3DOnBatch(&Batch, A, B, C, D, Color);
}
internal void
PushRenderCameraFacingQuad (render_command_buffer* Buffer, v4 Center, v2 Dimensions, v4 Color)
{
- // TODO(Peter): Turn this into an actual camera facing quad
- v4 A = v4{Center.x - Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w};
- v4 B = v4{Center.x + Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w};
- v4 C = v4{Center.x + Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w};
- v4 D = v4{Center.x - Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w};
-
- PushRenderQuad3D(Buffer, A, B, C, D, Color);
+ // TODO(Peter): Turn this into an actual camera facing quad
+ v4 A = v4{Center.x - Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w};
+ v4 B = v4{Center.x + Dimensions.x, Center.y - Dimensions.y, Center.z, Center.w};
+ v4 C = v4{Center.x + Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w};
+ v4 D = v4{Center.x - Dimensions.x, Center.y + Dimensions.y, Center.z, Center.w};
+
+ PushRenderQuad3D(Buffer, A, B, C, D, Color);
}
internal render_quad_batch_constructor
PushRenderTexture2DBatch(render_command_buffer* Buffer, s32 QuadCount,
render_texture Texture)
{
- s32 DataSize = BATCH_2D_SIZE(QuadCount);
- ResizeBufferIfNecessary(Buffer, DataSize);
- Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
-
- render_quad_batch_constructor Result = {};
-
- render_batch_command_texture_2d* Command = PushRenderCommand(Buffer, render_batch_command_texture_2d);
- Command->QuadCount = QuadCount;
- Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, DataSize, (u8*)(Command + 1));
- Command->Texture = Texture;
-
- return Result;
+ s32 DataSize = BATCH_2D_SIZE(QuadCount);
+ ResizeBufferIfNecessary(Buffer, DataSize);
+ Assert(Buffer->CommandMemoryUsed + DataSize <= Buffer->CommandMemorySize);
+
+ render_quad_batch_constructor Result = {};
+
+ render_batch_command_texture_2d* Command = PushRenderCommand(Buffer, render_batch_command_texture_2d);
+ Command->QuadCount = QuadCount;
+ Command->DataSize = PushQuad2DBatch(Buffer, &Result, QuadCount, DataSize, (u8*)(Command + 1));
+ Command->Texture = Texture;
+
+ return Result;
}
internal render_quad_batch_constructor
@@ -678,14 +684,14 @@ PushRenderTexture2DBatch (render_command_buffer* Buffer, s32 QuadCount,
u8* TextureMemory, s32 TextureHandle, s32 TextureWidth, s32 TextureHeight,
s32 TextureBytesPerPixel, s32 TextureStride)
{
- render_texture Texture = render_texture{
- TextureMemory,
- TextureHandle,
- TextureWidth,
- TextureHeight,
- TextureBytesPerPixel,
- TextureStride};
- return PushRenderTexture2DBatch(Buffer, QuadCount, Texture);
+ render_texture Texture = render_texture{
+ TextureMemory,
+ TextureHandle,
+ TextureWidth,
+ TextureHeight,
+ TextureBytesPerPixel,
+ TextureStride};
+ return PushRenderTexture2DBatch(Buffer, QuadCount, Texture);
}
internal void
@@ -693,19 +699,19 @@ PushRenderTexture2D (render_command_buffer* Buffer, v2 Min, v2 Max, v4 Color,
v2 UVMin, v2 UVMax,
render_texture* Texture)
{
- render_quad_batch_constructor Batch = PushRenderTexture2DBatch(Buffer, 1, *Texture);
- PushQuad2DOnBatch(&Batch, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y},
- UVMin, UVMax, Color);
+ render_quad_batch_constructor Batch = PushRenderTexture2DBatch(Buffer, 1, *Texture);
+ PushQuad2DOnBatch(&Batch, v2{Min.x, Min.y}, v2{Max.x, Min.y}, v2{Max.x, Max.y}, v2{Min.x, Max.y},
+ UVMin, UVMax, Color);
}
internal void
PushRenderBoundingBox2D (render_command_buffer* Buffer, v2 Min, v2 Max, r32 Thickness, v4 Color)
{
- render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 4);
- PushQuad2DOnBatch(&Batch, Min, v2{Min.x + Thickness, Max.y}, Color);
- PushQuad2DOnBatch(&Batch, v2{Min.x, Max.y - Thickness}, Max, Color);
- PushQuad2DOnBatch(&Batch, v2{Max.x - Thickness, Min.y}, Max, Color);
- PushQuad2DOnBatch(&Batch, Min, v2{Max.x, Min.y + Thickness}, Color);
+ render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 4);
+ PushQuad2DOnBatch(&Batch, Min, v2{Min.x + Thickness, Max.y}, Color);
+ PushQuad2DOnBatch(&Batch, v2{Min.x, Max.y - Thickness}, Max, Color);
+ PushQuad2DOnBatch(&Batch, v2{Max.x - Thickness, Min.y}, Max, Color);
+ PushQuad2DOnBatch(&Batch, Min, v2{Max.x, Min.y + Thickness}, Color);
}
diff --git a/src/app/handmade_math.h b/src/app/handmade_math.h
deleted file mode 100644
index 049dfdb..0000000
--- a/src/app/handmade_math.h
+++ /dev/null
@@ -1,3239 +0,0 @@
-/*
- HandmadeMath.h v1.11.0
-
- This is a single header file with a bunch of useful functions for game and
- graphics math operations.
-
- =============================================================================
-
- You MUST
-
- #define HANDMADE_MATH_IMPLEMENTATION
-
- in EXACTLY one C or C++ file that includes this header, BEFORE the
- include, like this:
-
- #define HANDMADE_MATH_IMPLEMENTATION
- #include "HandmadeMath.h"
-
- All other files should just #include "HandmadeMath.h" without the #define.
-
- =============================================================================
-
- To disable SSE intrinsics, you MUST
-
- #define HANDMADE_MATH_NO_SSE
-
- in EXACTLY one C or C++ file that includes this header, BEFORE the
- include, like this:
-
- #define HANDMADE_MATH_IMPLEMENTATION
- #define HANDMADE_MATH_NO_SSE
- #include "HandmadeMath.h"
-
- =============================================================================
-
- If you would prefer not to use the HMM_ prefix on function names, you can
-
- #define HMM_PREFIX
-
- To use a custom prefix instead, you can
-
- #define HMM_PREFIX(name) YOUR_PREFIX_##name
-
- =============================================================================
-
- To use HandmadeMath without the CRT, you MUST
-
- #define HMM_SINF MySinF
- #define HMM_COSF MyCosF
- #define HMM_TANF MyTanF
- #define HMM_SQRTF MySqrtF
- #define HMM_EXPF MyExpF
- #define HMM_LOGF MyLogF
- #define HMM_ACOSF MyACosF
- #define HMM_ATANF MyATanF
- #define HMM_ATAN2F MYATan2F
-
- Provide your own implementations of SinF, CosF, TanF, ACosF, ATanF, ATan2F,
- ExpF, and LogF in EXACTLY one C or C++ file that includes this header,
- BEFORE the include, like this:
-
- #define HMM_SINF MySinF
- #define HMM_COSF MyCosF
- #define HMM_TANF MyTanF
- #define HMM_SQRTF MySqrtF
- #define HMM_EXPF MyExpF
- #define HMM_LOGF MyLogF
- #define HMM_ACOSF MyACosF
- #define HMM_ATANF MyATanF
- #define HMM_ATAN2F MyATan2F
- #define HANDMADE_MATH_IMPLEMENTATION
- #include "HandmadeMath.h"
-
- If you do not define all of these, HandmadeMath.h will use the
- versions of these functions that are provided by the CRT.
-
- =============================================================================
-
- LICENSE
-
- This software is in the public domain. Where that dedication is not
- recognized, you are granted a perpetual, irrevocable license to copy,
- distribute, and modify this file as you see fit.
-
- CREDITS
-
- Written by Zakary Strange (strangezak@gmail.com && @strangezak)
-
- Functionality:
- Matt Mascarenhas (@miblo_)
- Aleph
- FieryDrake (@fierydrake)
- Gingerbill (@TheGingerBill)
- Ben Visness (@bvisness)
- Trinton Bullard (@Peliex_Dev)
- @AntonDan
-
- Fixes:
- Jeroen van Rijn (@J_vanRijn)
- Kiljacken (@Kiljacken)
- Insofaras (@insofaras)
- Daniel Gibson (@DanielGibson)
-*/
-
-// Dummy macros for when test framework is not present.
-#ifndef COVERAGE
-#define COVERAGE(a, b)
-#endif
-
-#ifndef ASSERT_COVERED
-#define ASSERT_COVERED(a)
-#endif
-
-/* let's figure out if SSE is really available (unless disabled anyway)
- (it isn't on non-x86/x86_64 platforms or even x86 without explicit SSE support)
- => only use "#ifdef HANDMADE_MATH__USE_SSE" to check for SSE support below this block! */
-#ifndef HANDMADE_MATH_NO_SSE
-
-# ifdef _MSC_VER
-/* MSVC supports SSE in amd64 mode or _M_IX86_FP >= 1 (2 means SSE2) */
-# if defined(_M_AMD64) || ( defined(_M_IX86_FP) && _M_IX86_FP >= 1 )
-# define HANDMADE_MATH__USE_SSE 1
-# endif
-# else /* not MSVC, probably GCC, clang, icc or something that doesn't support SSE anyway */
-# ifdef __SSE__ /* they #define __SSE__ if it's supported */
-# define HANDMADE_MATH__USE_SSE 1
-# endif /* __SSE__ */
-# endif /* not _MSC_VER */
-
-#endif /* #ifndef HANDMADE_MATH_NO_SSE */
-
-#ifdef HANDMADE_MATH__USE_SSE
-#include
-#endif
-
-#ifndef HANDMADE_MATH_H
-#define HANDMADE_MATH_H
-
-#ifdef _MSC_VER
-#pragma warning(disable:4201)
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#if defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
-#pragma GCC diagnostic ignored "-Wmissing-braces"
-#endif
-#ifdef __clang__
-#pragma GCC diagnostic ignored "-Wgnu-anonymous-struct"
-#endif
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#define HMM_DEPRECATED(msg) __attribute__((deprecated(msg)))
-#elif defined(_MSC_VER)
-#define HMM_DEPRECATED(msg) __declspec(deprecated(msg))
-#else
-#define HMM_DEPRECATED(msg)
-#endif
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#define HMM_INLINE static inline
-#define HMM_EXTERN extern
-
-#if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \
- !defined(HMM_SQRTF) || !defined(HMM_EXPF) || !defined(HMM_LOGF) || \
- !defined(HMM_ACOSF) || !defined(HMM_ATANF)|| !defined(HMM_ATAN2F)
-#include
-#endif
-
-#ifndef HMM_SINF
-#define HMM_SINF sinf
-#endif
-
-#ifndef HMM_COSF
-#define HMM_COSF cosf
-#endif
-
-#ifndef HMM_TANF
-#define HMM_TANF tanf
-#endif
-
-#ifndef HMM_SQRTF
-#define HMM_SQRTF sqrtf
-#endif
-
-#ifndef HMM_EXPF
-#define HMM_EXPF expf
-#endif
-
-#ifndef HMM_LOGF
-#define HMM_LOGF logf
-#endif
-
-#ifndef HMM_ACOSF
-#define HMM_ACOSF acosf
-#endif
-
-#ifndef HMM_ATANF
-#define HMM_ATANF atanf
-#endif
-
-#ifndef HMM_ATAN2F
-#define HMM_ATAN2F atan2f
-#endif
-
-#define HMM_PI32 3.14159265359f
-#define HMM_PI 3.14159265358979323846
-
-#define HMM_MIN(a, b) (a) > (b) ? (b) : (a)
-#define HMM_MAX(a, b) (a) < (b) ? (b) : (a)
-#define HMM_ABS(a) ((a) > 0 ? (a) : -(a))
-#define HMM_MOD(a, m) ((a) % (m)) >= 0 ? ((a) % (m)) : (((a) % (m)) + (m))
-#define HMM_SQUARE(x) ((x) * (x))
-
-#ifndef HMM_PREFIX
-#define HMM_PREFIX(name) HMM_##name
-#endif
-
- typedef union hmm_vec2
- {
- struct
- {
- float X, Y;
- };
-
- struct
- {
- float U, V;
- };
-
- struct
- {
- float Left, Right;
- };
-
- struct
- {
- float Width, Height;
- };
-
- float Elements[2];
-
-#ifdef __cplusplus
- inline float &operator[](const int &Index)
- {
- return Elements[Index];
- }
-#endif
- } hmm_vec2;
-
- typedef union hmm_vec3
- {
- struct
- {
- float X, Y, Z;
- };
-
- struct
- {
- float U, V, W;
- };
-
- struct
- {
- float R, G, B;
- };
-
- struct
- {
- hmm_vec2 XY;
- float Ignored0_;
- };
-
- struct
- {
- float Ignored1_;
- hmm_vec2 YZ;
- };
-
- struct
- {
- hmm_vec2 UV;
- float Ignored2_;
- };
-
- struct
- {
- float Ignored3_;
- hmm_vec2 VW;
- };
-
- float Elements[3];
-
-#ifdef __cplusplus
- inline float &operator[](const int &Index)
- {
- return Elements[Index];
- }
-#endif
- } hmm_vec3;
-
- typedef union hmm_vec4
- {
- struct
- {
- union
- {
- hmm_vec3 XYZ;
- struct
- {
- float X, Y, Z;
- };
- };
-
- float W;
- };
- struct
- {
- union
- {
- hmm_vec3 RGB;
- struct
- {
- float R, G, B;
- };
- };
-
- float A;
- };
-
- struct
- {
- hmm_vec2 XY;
- float Ignored0_;
- float Ignored1_;
- };
-
- struct
- {
- float Ignored2_;
- hmm_vec2 YZ;
- float Ignored3_;
- };
-
- struct
- {
- float Ignored4_;
- float Ignored5_;
- hmm_vec2 ZW;
- };
-
- float Elements[4];
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 InternalElementsSSE;
-#endif
-
-#ifdef __cplusplus
- inline float &operator[](const int &Index)
- {
- return Elements[Index];
- }
-#endif
- } hmm_vec4;
-
- typedef union hmm_mat4
- {
- float Elements[4][4];
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 Columns[4];
-
- HMM_DEPRECATED("Our matrices are column-major, so this was named incorrectly. Use Columns instead.")
- __m128 Rows[4];
-#endif
-
-#ifdef __cplusplus
- inline hmm_vec4 operator[](const int &Index)
- {
- float* col = Elements[Index];
-
- hmm_vec4 result;
- result.Elements[0] = col[0];
- result.Elements[1] = col[1];
- result.Elements[2] = col[2];
- result.Elements[3] = col[3];
-
- return result;
- }
-#endif
- } hmm_mat4;
-
- typedef union hmm_quaternion
- {
- struct
- {
- union
- {
- hmm_vec3 XYZ;
- struct
- {
- float X, Y, Z;
- };
- };
-
- float W;
- };
-
- float Elements[4];
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 InternalElementsSSE;
-#endif
- } hmm_quaternion;
-
- typedef signed int hmm_bool;
-
- typedef hmm_vec2 hmm_v2;
- typedef hmm_vec3 hmm_v3;
- typedef hmm_vec4 hmm_v4;
- typedef hmm_mat4 hmm_m4;
-
-
- /*
- * Floating-point math functions
- */
-
- COVERAGE(HMM_SinF, 1)
- HMM_INLINE float HMM_PREFIX(SinF)(float Radians)
- {
- ASSERT_COVERED(HMM_SinF);
-
- float Result = HMM_SINF(Radians);
-
- return (Result);
- }
-
- COVERAGE(HMM_CosF, 1)
- HMM_INLINE float HMM_PREFIX(CosF)(float Radians)
- {
- ASSERT_COVERED(HMM_CosF);
-
- float Result = HMM_COSF(Radians);
-
- return (Result);
- }
-
- COVERAGE(HMM_TanF, 1)
- HMM_INLINE float HMM_PREFIX(TanF)(float Radians)
- {
- ASSERT_COVERED(HMM_TanF);
-
- float Result = HMM_TANF(Radians);
-
- return (Result);
- }
-
- COVERAGE(HMM_ACosF, 1)
- HMM_INLINE float HMM_PREFIX(ACosF)(float Radians)
- {
- ASSERT_COVERED(HMM_ACosF);
-
- float Result = HMM_ACOSF(Radians);
-
- return (Result);
- }
-
- COVERAGE(HMM_ATanF, 1)
- HMM_INLINE float HMM_PREFIX(ATanF)(float Radians)
- {
- ASSERT_COVERED(HMM_ATanF);
-
- float Result = HMM_ATANF(Radians);
-
- return (Result);
- }
-
- COVERAGE(HMM_ATan2F, 1)
- HMM_INLINE float HMM_PREFIX(ATan2F)(float Left, float Right)
- {
- ASSERT_COVERED(HMM_ATan2F);
-
- float Result = HMM_ATAN2F(Left, Right);
-
- return (Result);
- }
-
- COVERAGE(HMM_ExpF, 1)
- HMM_INLINE float HMM_PREFIX(ExpF)(float Float)
- {
- ASSERT_COVERED(HMM_ExpF);
-
- float Result = HMM_EXPF(Float);
-
- return (Result);
- }
-
- COVERAGE(HMM_LogF, 1)
- HMM_INLINE float HMM_PREFIX(LogF)(float Float)
- {
- ASSERT_COVERED(HMM_LogF);
-
- float Result = HMM_LOGF(Float);
-
- return (Result);
- }
-
- COVERAGE(HMM_SquareRootF, 1)
- HMM_INLINE float HMM_PREFIX(SquareRootF)(float Float)
- {
- ASSERT_COVERED(HMM_SquareRootF);
-
- float Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 In = _mm_set_ss(Float);
- __m128 Out = _mm_sqrt_ss(In);
- Result = _mm_cvtss_f32(Out);
-#else
- Result = HMM_SQRTF(Float);
-#endif
-
- return(Result);
- }
-
- COVERAGE(HMM_RSquareRootF, 1)
- HMM_INLINE float HMM_PREFIX(RSquareRootF)(float Float)
- {
- ASSERT_COVERED(HMM_RSquareRootF);
-
- float Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 In = _mm_set_ss(Float);
- __m128 Out = _mm_rsqrt_ss(In);
- Result = _mm_cvtss_f32(Out);
-#else
- Result = 1.0f/HMM_PREFIX(SquareRootF)(Float);
-#endif
-
- return(Result);
- }
-
- HMM_EXTERN float HMM_PREFIX(Power)(float Base, int Exponent);
-
- COVERAGE(HMM_PowerF, 1)
- HMM_INLINE float HMM_PREFIX(PowerF)(float Base, float Exponent)
- {
- ASSERT_COVERED(HMM_PowerF);
-
- float Result = HMM_EXPF(Exponent * HMM_LOGF(Base));
-
- return (Result);
- }
-
-
- /*
- * Utility functions
- */
-
- COVERAGE(HMM_ToRadians, 1)
- HMM_INLINE float HMM_PREFIX(ToRadians)(float Degrees)
- {
- ASSERT_COVERED(HMM_ToRadians);
-
- float Result = Degrees * (HMM_PI32 / 180.0f);
-
- return (Result);
- }
-
- COVERAGE(HMM_Lerp, 1)
- HMM_INLINE float HMM_PREFIX(Lerp)(float A, float Time, float B)
- {
- ASSERT_COVERED(HMM_Lerp);
-
- float Result = (1.0f - Time) * A + Time * B;
-
- return (Result);
- }
-
- COVERAGE(HMM_Clamp, 1)
- HMM_INLINE float HMM_PREFIX(Clamp)(float Min, float Value, float Max)
- {
- ASSERT_COVERED(HMM_Clamp);
-
- float Result = Value;
-
- if(Result < Min)
- {
- Result = Min;
- }
- else if(Result > Max)
- {
- Result = Max;
- }
-
- return (Result);
- }
-
-
- /*
- * Vector initialization
- */
-
- COVERAGE(HMM_Vec2, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(Vec2)(float X, float Y)
- {
- ASSERT_COVERED(HMM_Vec2);
-
- hmm_vec2 Result;
-
- Result.X = X;
- Result.Y = Y;
-
- return (Result);
- }
-
- COVERAGE(HMM_Vec2i, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(Vec2i)(int X, int Y)
- {
- ASSERT_COVERED(HMM_Vec2i);
-
- hmm_vec2 Result;
-
- Result.X = (float)X;
- Result.Y = (float)Y;
-
- return (Result);
- }
-
- COVERAGE(HMM_Vec3, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(Vec3)(float X, float Y, float Z)
- {
- ASSERT_COVERED(HMM_Vec3);
-
- hmm_vec3 Result;
-
- Result.X = X;
- Result.Y = Y;
- Result.Z = Z;
-
- return (Result);
- }
-
- COVERAGE(HMM_Vec3i, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(Vec3i)(int X, int Y, int Z)
- {
- ASSERT_COVERED(HMM_Vec3i);
-
- hmm_vec3 Result;
-
- Result.X = (float)X;
- Result.Y = (float)Y;
- Result.Z = (float)Z;
-
- return (Result);
- }
-
- COVERAGE(HMM_Vec4, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(Vec4)(float X, float Y, float Z, float W)
- {
- ASSERT_COVERED(HMM_Vec4);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_setr_ps(X, Y, Z, W);
-#else
- Result.X = X;
- Result.Y = Y;
- Result.Z = Z;
- Result.W = W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_Vec4i, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(Vec4i)(int X, int Y, int Z, int W)
- {
- ASSERT_COVERED(HMM_Vec4i);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_setr_ps((float)X, (float)Y, (float)Z, (float)W);
-#else
- Result.X = (float)X;
- Result.Y = (float)Y;
- Result.Z = (float)Z;
- Result.W = (float)W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_Vec4v, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(Vec4v)(hmm_vec3 Vector, float W)
- {
- ASSERT_COVERED(HMM_Vec4v);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_setr_ps(Vector.X, Vector.Y, Vector.Z, W);
-#else
- Result.XYZ = Vector;
- Result.W = W;
-#endif
-
- return (Result);
- }
-
-
- /*
- * Binary vector operations
- */
-
- COVERAGE(HMM_AddVec2, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(AddVec2)(hmm_vec2 Left, hmm_vec2 Right)
- {
- ASSERT_COVERED(HMM_AddVec2);
-
- hmm_vec2 Result;
-
- Result.X = Left.X + Right.X;
- Result.Y = Left.Y + Right.Y;
-
- return (Result);
- }
-
- COVERAGE(HMM_AddVec3, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(AddVec3)(hmm_vec3 Left, hmm_vec3 Right)
- {
- ASSERT_COVERED(HMM_AddVec3);
-
- hmm_vec3 Result;
-
- Result.X = Left.X + Right.X;
- Result.Y = Left.Y + Right.Y;
- Result.Z = Left.Z + Right.Z;
-
- return (Result);
- }
-
- COVERAGE(HMM_AddVec4, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(AddVec4)(hmm_vec4 Left, hmm_vec4 Right)
- {
- ASSERT_COVERED(HMM_AddVec4);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_add_ps(Left.InternalElementsSSE, Right.InternalElementsSSE);
-#else
- Result.X = Left.X + Right.X;
- Result.Y = Left.Y + Right.Y;
- Result.Z = Left.Z + Right.Z;
- Result.W = Left.W + Right.W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_SubtractVec2, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(SubtractVec2)(hmm_vec2 Left, hmm_vec2 Right)
- {
- ASSERT_COVERED(HMM_SubtractVec2);
-
- hmm_vec2 Result;
-
- Result.X = Left.X - Right.X;
- Result.Y = Left.Y - Right.Y;
-
- return (Result);
- }
-
- COVERAGE(HMM_SubtractVec3, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(SubtractVec3)(hmm_vec3 Left, hmm_vec3 Right)
- {
- ASSERT_COVERED(HMM_SubtractVec3);
-
- hmm_vec3 Result;
-
- Result.X = Left.X - Right.X;
- Result.Y = Left.Y - Right.Y;
- Result.Z = Left.Z - Right.Z;
-
- return (Result);
- }
-
- COVERAGE(HMM_SubtractVec4, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(SubtractVec4)(hmm_vec4 Left, hmm_vec4 Right)
- {
- ASSERT_COVERED(HMM_SubtractVec4);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_sub_ps(Left.InternalElementsSSE, Right.InternalElementsSSE);
-#else
- Result.X = Left.X - Right.X;
- Result.Y = Left.Y - Right.Y;
- Result.Z = Left.Z - Right.Z;
- Result.W = Left.W - Right.W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_MultiplyVec2, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(MultiplyVec2)(hmm_vec2 Left, hmm_vec2 Right)
- {
- ASSERT_COVERED(HMM_MultiplyVec2);
-
- hmm_vec2 Result;
-
- Result.X = Left.X * Right.X;
- Result.Y = Left.Y * Right.Y;
-
- return (Result);
- }
-
- COVERAGE(HMM_MultiplyVec2f, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(MultiplyVec2f)(hmm_vec2 Left, float Right)
- {
- ASSERT_COVERED(HMM_MultiplyVec2f);
-
- hmm_vec2 Result;
-
- Result.X = Left.X * Right;
- Result.Y = Left.Y * Right;
-
- return (Result);
- }
-
- COVERAGE(HMM_MultiplyVec3, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(MultiplyVec3)(hmm_vec3 Left, hmm_vec3 Right)
- {
- ASSERT_COVERED(HMM_MultiplyVec3);
-
- hmm_vec3 Result;
-
- Result.X = Left.X * Right.X;
- Result.Y = Left.Y * Right.Y;
- Result.Z = Left.Z * Right.Z;
-
- return (Result);
- }
-
- COVERAGE(HMM_MultiplyVec3f, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(MultiplyVec3f)(hmm_vec3 Left, float Right)
- {
- ASSERT_COVERED(HMM_MultiplyVec3f);
-
- hmm_vec3 Result;
-
- Result.X = Left.X * Right;
- Result.Y = Left.Y * Right;
- Result.Z = Left.Z * Right;
-
- return (Result);
- }
-
- COVERAGE(HMM_MultiplyVec4, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(MultiplyVec4)(hmm_vec4 Left, hmm_vec4 Right)
- {
- ASSERT_COVERED(HMM_MultiplyVec4);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_mul_ps(Left.InternalElementsSSE, Right.InternalElementsSSE);
-#else
- Result.X = Left.X * Right.X;
- Result.Y = Left.Y * Right.Y;
- Result.Z = Left.Z * Right.Z;
- Result.W = Left.W * Right.W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_MultiplyVec4f, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(MultiplyVec4f)(hmm_vec4 Left, float Right)
- {
- ASSERT_COVERED(HMM_MultiplyVec4f);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 Scalar = _mm_set1_ps(Right);
- Result.InternalElementsSSE = _mm_mul_ps(Left.InternalElementsSSE, Scalar);
-#else
- Result.X = Left.X * Right;
- Result.Y = Left.Y * Right;
- Result.Z = Left.Z * Right;
- Result.W = Left.W * Right;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_DivideVec2, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(DivideVec2)(hmm_vec2 Left, hmm_vec2 Right)
- {
- ASSERT_COVERED(HMM_DivideVec2);
-
- hmm_vec2 Result;
-
- Result.X = Left.X / Right.X;
- Result.Y = Left.Y / Right.Y;
-
- return (Result);
- }
-
- COVERAGE(HMM_DivideVec2f, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(DivideVec2f)(hmm_vec2 Left, float Right)
- {
- ASSERT_COVERED(HMM_DivideVec2f);
-
- hmm_vec2 Result;
-
- Result.X = Left.X / Right;
- Result.Y = Left.Y / Right;
-
- return (Result);
- }
-
- COVERAGE(HMM_DivideVec3, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(DivideVec3)(hmm_vec3 Left, hmm_vec3 Right)
- {
- ASSERT_COVERED(HMM_DivideVec3);
-
- hmm_vec3 Result;
-
- Result.X = Left.X / Right.X;
- Result.Y = Left.Y / Right.Y;
- Result.Z = Left.Z / Right.Z;
-
- return (Result);
- }
-
- COVERAGE(HMM_DivideVec3f, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(DivideVec3f)(hmm_vec3 Left, float Right)
- {
- ASSERT_COVERED(HMM_DivideVec3f);
-
- hmm_vec3 Result;
-
- Result.X = Left.X / Right;
- Result.Y = Left.Y / Right;
- Result.Z = Left.Z / Right;
-
- return (Result);
- }
-
- COVERAGE(HMM_DivideVec4, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(DivideVec4)(hmm_vec4 Left, hmm_vec4 Right)
- {
- ASSERT_COVERED(HMM_DivideVec4);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_div_ps(Left.InternalElementsSSE, Right.InternalElementsSSE);
-#else
- Result.X = Left.X / Right.X;
- Result.Y = Left.Y / Right.Y;
- Result.Z = Left.Z / Right.Z;
- Result.W = Left.W / Right.W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_DivideVec4f, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(DivideVec4f)(hmm_vec4 Left, float Right)
- {
- ASSERT_COVERED(HMM_DivideVec4f);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 Scalar = _mm_set1_ps(Right);
- Result.InternalElementsSSE = _mm_div_ps(Left.InternalElementsSSE, Scalar);
-#else
- Result.X = Left.X / Right;
- Result.Y = Left.Y / Right;
- Result.Z = Left.Z / Right;
- Result.W = Left.W / Right;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_EqualsVec2, 1)
- HMM_INLINE hmm_bool HMM_PREFIX(EqualsVec2)(hmm_vec2 Left, hmm_vec2 Right)
- {
- ASSERT_COVERED(HMM_EqualsVec2);
-
- hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y);
-
- return (Result);
- }
-
- COVERAGE(HMM_EqualsVec3, 1)
- HMM_INLINE hmm_bool HMM_PREFIX(EqualsVec3)(hmm_vec3 Left, hmm_vec3 Right)
- {
- ASSERT_COVERED(HMM_EqualsVec3);
-
- hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z);
-
- return (Result);
- }
-
- COVERAGE(HMM_EqualsVec4, 1)
- HMM_INLINE hmm_bool HMM_PREFIX(EqualsVec4)(hmm_vec4 Left, hmm_vec4 Right)
- {
- ASSERT_COVERED(HMM_EqualsVec4);
-
- hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z && Left.W == Right.W);
-
- return (Result);
- }
-
- COVERAGE(HMM_DotVec2, 1)
- HMM_INLINE float HMM_PREFIX(DotVec2)(hmm_vec2 VecOne, hmm_vec2 VecTwo)
- {
- ASSERT_COVERED(HMM_DotVec2);
-
- float Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y);
-
- return (Result);
- }
-
- COVERAGE(HMM_DotVec3, 1)
- HMM_INLINE float HMM_PREFIX(DotVec3)(hmm_vec3 VecOne, hmm_vec3 VecTwo)
- {
- ASSERT_COVERED(HMM_DotVec3);
-
- float Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z);
-
- return (Result);
- }
-
- COVERAGE(HMM_DotVec4, 1)
- HMM_INLINE float HMM_PREFIX(DotVec4)(hmm_vec4 VecOne, hmm_vec4 VecTwo)
- {
- ASSERT_COVERED(HMM_DotVec4);
-
- float Result;
-
- // NOTE(zak): IN the future if we wanna check what version SSE is support
- // we can use _mm_dp_ps (4.3) but for now we will use the old way.
- // Or a r = _mm_mul_ps(v1, v2), r = _mm_hadd_ps(r, r), r = _mm_hadd_ps(r, r) for SSE3
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 SSEResultOne = _mm_mul_ps(VecOne.InternalElementsSSE, VecTwo.InternalElementsSSE);
- __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1));
- SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
- SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3));
- SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
- _mm_store_ss(&Result, SSEResultOne);
-#else
- Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z) + (VecOne.W * VecTwo.W);
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_Cross, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(Cross)(hmm_vec3 VecOne, hmm_vec3 VecTwo)
- {
- ASSERT_COVERED(HMM_Cross);
-
- hmm_vec3 Result;
-
- Result.X = (VecOne.Y * VecTwo.Z) - (VecOne.Z * VecTwo.Y);
- Result.Y = (VecOne.Z * VecTwo.X) - (VecOne.X * VecTwo.Z);
- Result.Z = (VecOne.X * VecTwo.Y) - (VecOne.Y * VecTwo.X);
-
- return (Result);
- }
-
-
- /*
- * Unary vector operations
- */
-
- COVERAGE(HMM_LengthSquaredVec2, 1)
- HMM_INLINE float HMM_PREFIX(LengthSquaredVec2)(hmm_vec2 A)
- {
- ASSERT_COVERED(HMM_LengthSquaredVec2);
-
- float Result = HMM_PREFIX(DotVec2)(A, A);
-
- return (Result);
- }
-
- COVERAGE(HMM_LengthSquaredVec3, 1)
- HMM_INLINE float HMM_PREFIX(LengthSquaredVec3)(hmm_vec3 A)
- {
- ASSERT_COVERED(HMM_LengthSquaredVec3);
-
- float Result = HMM_PREFIX(DotVec3)(A, A);
-
- return (Result);
- }
-
- COVERAGE(HMM_LengthSquaredVec4, 1)
- HMM_INLINE float HMM_PREFIX(LengthSquaredVec4)(hmm_vec4 A)
- {
- ASSERT_COVERED(HMM_LengthSquaredVec4);
-
- float Result = HMM_PREFIX(DotVec4)(A, A);
-
- return (Result);
- }
-
- COVERAGE(HMM_LengthVec2, 1)
- HMM_INLINE float HMM_PREFIX(LengthVec2)(hmm_vec2 A)
- {
- ASSERT_COVERED(HMM_LengthVec2);
-
- float Result = HMM_PREFIX(SquareRootF)(HMM_PREFIX(LengthSquaredVec2)(A));
-
- return (Result);
- }
-
- COVERAGE(HMM_LengthVec3, 1)
- HMM_INLINE float HMM_PREFIX(LengthVec3)(hmm_vec3 A)
- {
- ASSERT_COVERED(HMM_LengthVec3);
-
- float Result = HMM_PREFIX(SquareRootF)(HMM_PREFIX(LengthSquaredVec3)(A));
-
- return (Result);
- }
-
- COVERAGE(HMM_LengthVec4, 1)
- HMM_INLINE float HMM_PREFIX(LengthVec4)(hmm_vec4 A)
- {
- ASSERT_COVERED(HMM_LengthVec4);
-
- float Result = HMM_PREFIX(SquareRootF)(HMM_PREFIX(LengthSquaredVec4)(A));
-
- return(Result);
- }
-
- COVERAGE(HMM_NormalizeVec2, 2)
- HMM_INLINE hmm_vec2 HMM_PREFIX(NormalizeVec2)(hmm_vec2 A)
- {
- ASSERT_COVERED(HMM_NormalizeVec2);
-
- hmm_vec2 Result = {0};
-
- float VectorLength = HMM_PREFIX(LengthVec2)(A);
-
- /* NOTE(kiljacken): We need a zero check to not divide-by-zero */
- if (VectorLength != 0.0f)
- {
- ASSERT_COVERED(HMM_NormalizeVec2);
-
- Result.X = A.X * (1.0f / VectorLength);
- Result.Y = A.Y * (1.0f / VectorLength);
- }
-
- return (Result);
- }
-
- COVERAGE(HMM_NormalizeVec3, 2)
- HMM_INLINE hmm_vec3 HMM_PREFIX(NormalizeVec3)(hmm_vec3 A)
- {
- ASSERT_COVERED(HMM_NormalizeVec3);
-
- hmm_vec3 Result = {0};
-
- float VectorLength = HMM_PREFIX(LengthVec3)(A);
-
- /* NOTE(kiljacken): We need a zero check to not divide-by-zero */
- if (VectorLength != 0.0f)
- {
- ASSERT_COVERED(HMM_NormalizeVec3);
-
- Result.X = A.X * (1.0f / VectorLength);
- Result.Y = A.Y * (1.0f / VectorLength);
- Result.Z = A.Z * (1.0f / VectorLength);
- }
-
- return (Result);
- }
-
- COVERAGE(HMM_NormalizeVec4, 2)
- HMM_INLINE hmm_vec4 HMM_PREFIX(NormalizeVec4)(hmm_vec4 A)
- {
- ASSERT_COVERED(HMM_NormalizeVec4);
-
- hmm_vec4 Result = {0};
-
- float VectorLength = HMM_PREFIX(LengthVec4)(A);
-
- /* NOTE(kiljacken): We need a zero check to not divide-by-zero */
- if (VectorLength != 0.0f)
- {
- ASSERT_COVERED(HMM_NormalizeVec4);
-
- float Multiplier = 1.0f / VectorLength;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 SSEMultiplier = _mm_set1_ps(Multiplier);
- Result.InternalElementsSSE = _mm_mul_ps(A.InternalElementsSSE, SSEMultiplier);
-#else
- Result.X = A.X * Multiplier;
- Result.Y = A.Y * Multiplier;
- Result.Z = A.Z * Multiplier;
- Result.W = A.W * Multiplier;
-#endif
- }
-
- return (Result);
- }
-
- COVERAGE(HMM_FastNormalizeVec2, 1)
- HMM_INLINE hmm_vec2 HMM_PREFIX(FastNormalizeVec2)(hmm_vec2 A)
- {
- ASSERT_COVERED(HMM_FastNormalizeVec2);
-
- return HMM_PREFIX(MultiplyVec2f)(A, HMM_PREFIX(RSquareRootF)(HMM_PREFIX(DotVec2)(A, A)));
- }
-
- COVERAGE(HMM_FastNormalizeVec3, 1)
- HMM_INLINE hmm_vec3 HMM_PREFIX(FastNormalizeVec3)(hmm_vec3 A)
- {
- ASSERT_COVERED(HMM_FastNormalizeVec3);
-
- return HMM_PREFIX(MultiplyVec3f)(A, HMM_PREFIX(RSquareRootF)(HMM_PREFIX(DotVec3)(A, A)));
- }
-
- COVERAGE(HMM_FastNormalizeVec4, 1)
- HMM_INLINE hmm_vec4 HMM_PREFIX(FastNormalizeVec4)(hmm_vec4 A)
- {
- ASSERT_COVERED(HMM_FastNormalizeVec4);
-
- return HMM_PREFIX(MultiplyVec4f)(A, HMM_PREFIX(RSquareRootF)(HMM_PREFIX(DotVec4)(A, A)));
- }
-
-
- /*
- * SSE stuff
- */
-
-#ifdef HANDMADE_MATH__USE_SSE
- COVERAGE(HMM_LinearCombineSSE, 1)
- HMM_INLINE __m128 HMM_PREFIX(LinearCombineSSE)(__m128 Left, hmm_mat4 Right)
- {
- ASSERT_COVERED(HMM_LinearCombineSSE);
-
- __m128 Result;
- Result = _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x00), Right.Columns[0]);
- Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x55), Right.Columns[1]));
- Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xaa), Right.Columns[2]));
- Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xff), Right.Columns[3]));
-
- return (Result);
- }
-#endif
-
-
- /*
- * Matrix functions
- */
-
- COVERAGE(HMM_Mat4, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(Mat4)(void)
- {
- ASSERT_COVERED(HMM_Mat4);
-
- hmm_mat4 Result = {0};
-
- return (Result);
- }
-
- COVERAGE(HMM_Mat4d, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(Mat4d)(float Diagonal)
- {
- ASSERT_COVERED(HMM_Mat4d);
-
- hmm_mat4 Result = HMM_PREFIX(Mat4)();
-
- Result.Elements[0][0] = Diagonal;
- Result.Elements[1][1] = Diagonal;
- Result.Elements[2][2] = Diagonal;
- Result.Elements[3][3] = Diagonal;
-
- return (Result);
- }
-
-#ifdef HANDMADE_MATH__USE_SSE
- COVERAGE(HMM_Transpose, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix)
- {
- ASSERT_COVERED(HMM_Transpose);
-
- hmm_mat4 Result = Matrix;
-
- _MM_TRANSPOSE4_PS(Result.Columns[0], Result.Columns[1], Result.Columns[2], Result.Columns[3]);
-
- return (Result);
- }
-#else
- HMM_EXTERN hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix);
-#endif
-
-#ifdef HANDMADE_MATH__USE_SSE
- COVERAGE(HMM_AddMat4, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(AddMat4)(hmm_mat4 Left, hmm_mat4 Right)
- {
- ASSERT_COVERED(HMM_AddMat4);
-
- hmm_mat4 Result;
-
- Result.Columns[0] = _mm_add_ps(Left.Columns[0], Right.Columns[0]);
- Result.Columns[1] = _mm_add_ps(Left.Columns[1], Right.Columns[1]);
- Result.Columns[2] = _mm_add_ps(Left.Columns[2], Right.Columns[2]);
- Result.Columns[3] = _mm_add_ps(Left.Columns[3], Right.Columns[3]);
-
- return (Result);
- }
-#else
- HMM_EXTERN hmm_mat4 HMM_PREFIX(AddMat4)(hmm_mat4 Left, hmm_mat4 Right);
-#endif
-
-#ifdef HANDMADE_MATH__USE_SSE
- COVERAGE(HMM_SubtractMat4, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right)
- {
- ASSERT_COVERED(HMM_SubtractMat4);
-
- hmm_mat4 Result;
-
- Result.Columns[0] = _mm_sub_ps(Left.Columns[0], Right.Columns[0]);
- Result.Columns[1] = _mm_sub_ps(Left.Columns[1], Right.Columns[1]);
- Result.Columns[2] = _mm_sub_ps(Left.Columns[2], Right.Columns[2]);
- Result.Columns[3] = _mm_sub_ps(Left.Columns[3], Right.Columns[3]);
-
- return (Result);
- }
-#else
- HMM_EXTERN hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right);
-#endif
-
- HMM_EXTERN hmm_mat4 HMM_PREFIX(MultiplyMat4)(hmm_mat4 Left, hmm_mat4 Right);
-
-#ifdef HANDMADE_MATH__USE_SSE
- COVERAGE(HMM_MultiplyMat4f, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar)
- {
- ASSERT_COVERED(HMM_MultiplyMat4f);
-
- hmm_mat4 Result;
-
- __m128 SSEScalar = _mm_set1_ps(Scalar);
- Result.Columns[0] = _mm_mul_ps(Matrix.Columns[0], SSEScalar);
- Result.Columns[1] = _mm_mul_ps(Matrix.Columns[1], SSEScalar);
- Result.Columns[2] = _mm_mul_ps(Matrix.Columns[2], SSEScalar);
- Result.Columns[3] = _mm_mul_ps(Matrix.Columns[3], SSEScalar);
-
- return (Result);
- }
-#else
- HMM_EXTERN hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar);
-#endif
-
- HMM_EXTERN hmm_vec4 HMM_PREFIX(MultiplyMat4ByVec4)(hmm_mat4 Matrix, hmm_vec4 Vector);
-
-#ifdef HANDMADE_MATH__USE_SSE
- COVERAGE(HMM_DivideMat4f, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar)
- {
- ASSERT_COVERED(HMM_DivideMat4f);
-
- hmm_mat4 Result;
-
- __m128 SSEScalar = _mm_set1_ps(Scalar);
- Result.Columns[0] = _mm_div_ps(Matrix.Columns[0], SSEScalar);
- Result.Columns[1] = _mm_div_ps(Matrix.Columns[1], SSEScalar);
- Result.Columns[2] = _mm_div_ps(Matrix.Columns[2], SSEScalar);
- Result.Columns[3] = _mm_div_ps(Matrix.Columns[3], SSEScalar);
-
- return (Result);
- }
-#else
- HMM_EXTERN hmm_mat4 HMM_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar);
-#endif
-
-
- /*
- * Common graphics transformations
- */
-
- COVERAGE(HMM_Orthographic, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(Orthographic)(float Left, float Right, float Bottom, float Top, float Near, float Far)
- {
- ASSERT_COVERED(HMM_Orthographic);
-
- hmm_mat4 Result = HMM_PREFIX(Mat4)();
-
- Result.Elements[0][0] = 2.0f / (Right - Left);
- Result.Elements[1][1] = 2.0f / (Top - Bottom);
- Result.Elements[2][2] = 2.0f / (Near - Far);
- Result.Elements[3][3] = 1.0f;
-
- Result.Elements[3][0] = (Left + Right) / (Left - Right);
- Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top);
- Result.Elements[3][2] = (Far + Near) / (Near - Far);
-
- return (Result);
- }
-
- COVERAGE(HMM_Perspective, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(Perspective)(float FOV, float AspectRatio, float Near, float Far)
- {
- ASSERT_COVERED(HMM_Perspective);
-
- hmm_mat4 Result = HMM_PREFIX(Mat4)();
-
- // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
-
- float Cotangent = 1.0f / HMM_PREFIX(TanF)(FOV * (HMM_PI32 / 360.0f));
-
- Result.Elements[0][0] = Cotangent / AspectRatio;
- Result.Elements[1][1] = Cotangent;
- Result.Elements[2][3] = -1.0f;
- Result.Elements[2][2] = (Near + Far) / (Near - Far);
- Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far);
- Result.Elements[3][3] = 0.0f;
-
- return (Result);
- }
-
- COVERAGE(HMM_Translate, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(Translate)(hmm_vec3 Translation)
- {
- ASSERT_COVERED(HMM_Translate);
-
- hmm_mat4 Result = HMM_PREFIX(Mat4d)(1.0f);
-
- Result.Elements[3][0] = Translation.X;
- Result.Elements[3][1] = Translation.Y;
- Result.Elements[3][2] = Translation.Z;
-
- return (Result);
- }
-
- HMM_EXTERN hmm_mat4 HMM_PREFIX(Rotate)(float Angle, hmm_vec3 Axis);
-
- COVERAGE(HMM_Scale, 1)
- HMM_INLINE hmm_mat4 HMM_PREFIX(Scale)(hmm_vec3 Scale)
- {
- ASSERT_COVERED(HMM_Scale);
-
- hmm_mat4 Result = HMM_PREFIX(Mat4d)(1.0f);
-
- Result.Elements[0][0] = Scale.X;
- Result.Elements[1][1] = Scale.Y;
- Result.Elements[2][2] = Scale.Z;
-
- return (Result);
- }
-
- HMM_EXTERN hmm_mat4 HMM_PREFIX(LookAt)(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up);
-
-
- /*
- * Quaternion operations
- */
-
- COVERAGE(HMM_Quaternion, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(Quaternion)(float X, float Y, float Z, float W)
- {
- ASSERT_COVERED(HMM_Quaternion);
-
- hmm_quaternion Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_setr_ps(X, Y, Z, W);
-#else
- Result.X = X;
- Result.Y = Y;
- Result.Z = Z;
- Result.W = W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_QuaternionV4, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(QuaternionV4)(hmm_vec4 Vector)
- {
- ASSERT_COVERED(HMM_QuaternionV4);
-
- hmm_quaternion Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = Vector.InternalElementsSSE;
-#else
- Result.X = Vector.X;
- Result.Y = Vector.Y;
- Result.Z = Vector.Z;
- Result.W = Vector.W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_AddQuaternion, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(AddQuaternion)(hmm_quaternion Left, hmm_quaternion Right)
- {
- ASSERT_COVERED(HMM_AddQuaternion);
-
- hmm_quaternion Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_add_ps(Left.InternalElementsSSE, Right.InternalElementsSSE);
-#else
-
- Result.X = Left.X + Right.X;
- Result.Y = Left.Y + Right.Y;
- Result.Z = Left.Z + Right.Z;
- Result.W = Left.W + Right.W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_SubtractQuaternion, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(SubtractQuaternion)(hmm_quaternion Left, hmm_quaternion Right)
- {
- ASSERT_COVERED(HMM_SubtractQuaternion);
-
- hmm_quaternion Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = _mm_sub_ps(Left.InternalElementsSSE, Right.InternalElementsSSE);
-#else
-
- Result.X = Left.X - Right.X;
- Result.Y = Left.Y - Right.Y;
- Result.Z = Left.Z - Right.Z;
- Result.W = Left.W - Right.W;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_MultiplyQuaternion, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(MultiplyQuaternion)(hmm_quaternion Left, hmm_quaternion Right)
- {
- ASSERT_COVERED(HMM_MultiplyQuaternion);
-
- hmm_quaternion Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.InternalElementsSSE, Left.InternalElementsSSE, _MM_SHUFFLE(0, 0, 0, 0)), _mm_setr_ps(0.f, -0.f, 0.f, -0.f));
- __m128 SSEResultTwo = _mm_shuffle_ps(Right.InternalElementsSSE, Right.InternalElementsSSE, _MM_SHUFFLE(0, 1, 2, 3));
- __m128 SSEResultThree = _mm_mul_ps(SSEResultTwo, SSEResultOne);
-
- SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.InternalElementsSSE, Left.InternalElementsSSE, _MM_SHUFFLE(1, 1, 1, 1)) , _mm_setr_ps(0.f, 0.f, -0.f, -0.f));
- SSEResultTwo = _mm_shuffle_ps(Right.InternalElementsSSE, Right.InternalElementsSSE, _MM_SHUFFLE(1, 0, 3, 2));
- SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne));
-
- SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.InternalElementsSSE, Left.InternalElementsSSE, _MM_SHUFFLE(2, 2, 2, 2)), _mm_setr_ps(-0.f, 0.f, 0.f, -0.f));
- SSEResultTwo = _mm_shuffle_ps(Right.InternalElementsSSE, Right.InternalElementsSSE, _MM_SHUFFLE(2, 3, 0, 1));
- SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne));
-
- SSEResultOne = _mm_shuffle_ps(Left.InternalElementsSSE, Left.InternalElementsSSE, _MM_SHUFFLE(3, 3, 3, 3));
- SSEResultTwo = _mm_shuffle_ps(Right.InternalElementsSSE, Right.InternalElementsSSE, _MM_SHUFFLE(3, 2, 1, 0));
- Result.InternalElementsSSE = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne));
-#else
- Result.X = (Left.X * Right.W) + (Left.Y * Right.Z) - (Left.Z * Right.Y) + (Left.W * Right.X);
- Result.Y = (-Left.X * Right.Z) + (Left.Y * Right.W) + (Left.Z * Right.X) + (Left.W * Right.Y);
- Result.Z = (Left.X * Right.Y) - (Left.Y * Right.X) + (Left.Z * Right.W) + (Left.W * Right.Z);
- Result.W = (-Left.X * Right.X) - (Left.Y * Right.Y) - (Left.Z * Right.Z) + (Left.W * Right.W);
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_MultiplyQuaternionF, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(MultiplyQuaternionF)(hmm_quaternion Left, float Multiplicative)
- {
- ASSERT_COVERED(HMM_MultiplyQuaternionF);
-
- hmm_quaternion Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 Scalar = _mm_set1_ps(Multiplicative);
- Result.InternalElementsSSE = _mm_mul_ps(Left.InternalElementsSSE, Scalar);
-#else
- Result.X = Left.X * Multiplicative;
- Result.Y = Left.Y * Multiplicative;
- Result.Z = Left.Z * Multiplicative;
- Result.W = Left.W * Multiplicative;
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_DivideQuaternionF, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(DivideQuaternionF)(hmm_quaternion Left, float Dividend)
- {
- ASSERT_COVERED(HMM_DivideQuaternionF);
-
- hmm_quaternion Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 Scalar = _mm_set1_ps(Dividend);
- Result.InternalElementsSSE = _mm_div_ps(Left.InternalElementsSSE, Scalar);
-#else
- Result.X = Left.X / Dividend;
- Result.Y = Left.Y / Dividend;
- Result.Z = Left.Z / Dividend;
- Result.W = Left.W / Dividend;
-#endif
-
- return (Result);
- }
-
- HMM_EXTERN hmm_quaternion HMM_PREFIX(InverseQuaternion)(hmm_quaternion Left);
-
- COVERAGE(HMM_DotQuaternion, 1)
- HMM_INLINE float HMM_PREFIX(DotQuaternion)(hmm_quaternion Left, hmm_quaternion Right)
- {
- ASSERT_COVERED(HMM_DotQuaternion);
-
- float Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 SSEResultOne = _mm_mul_ps(Left.InternalElementsSSE, Right.InternalElementsSSE);
- __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1));
- SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
- SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3));
- SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
- _mm_store_ss(&Result, SSEResultOne);
-#else
- Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W);
-#endif
-
- return (Result);
- }
-
- COVERAGE(HMM_NormalizeQuaternion, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(NormalizeQuaternion)(hmm_quaternion Left)
- {
- ASSERT_COVERED(HMM_NormalizeQuaternion);
-
- hmm_quaternion Result;
-
- float Length = HMM_PREFIX(SquareRootF)(HMM_PREFIX(DotQuaternion)(Left, Left));
- Result = HMM_PREFIX(DivideQuaternionF)(Left, Length);
-
- return (Result);
- }
-
- COVERAGE(HMM_NLerp, 1)
- HMM_INLINE hmm_quaternion HMM_PREFIX(NLerp)(hmm_quaternion Left, float Time, hmm_quaternion Right)
- {
- ASSERT_COVERED(HMM_NLerp);
-
- hmm_quaternion Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- __m128 ScalarLeft = _mm_set1_ps(1.0f - Time);
- __m128 ScalarRight = _mm_set1_ps(Time);
- __m128 SSEResultOne = _mm_mul_ps(Left.InternalElementsSSE, ScalarLeft);
- __m128 SSEResultTwo = _mm_mul_ps(Right.InternalElementsSSE, ScalarRight);
- Result.InternalElementsSSE = _mm_add_ps(SSEResultOne, SSEResultTwo);
-#else
- Result.X = HMM_PREFIX(Lerp)(Left.X, Time, Right.X);
- Result.Y = HMM_PREFIX(Lerp)(Left.Y, Time, Right.Y);
- Result.Z = HMM_PREFIX(Lerp)(Left.Z, Time, Right.Z);
- Result.W = HMM_PREFIX(Lerp)(Left.W, Time, Right.W);
-#endif
- Result = HMM_PREFIX(NormalizeQuaternion)(Result);
-
- return (Result);
- }
-
- HMM_EXTERN hmm_quaternion HMM_PREFIX(Slerp)(hmm_quaternion Left, float Time, hmm_quaternion Right);
- HMM_EXTERN hmm_mat4 HMM_PREFIX(QuaternionToMat4)(hmm_quaternion Left);
- HMM_EXTERN hmm_quaternion HMM_PREFIX(Mat4ToQuaternion)(hmm_mat4 Left);
- HMM_EXTERN hmm_quaternion HMM_PREFIX(QuaternionFromAxisAngle)(hmm_vec3 Axis, float AngleOfRotation);
-
-#ifdef __cplusplus
-}
-#endif
-
-#ifdef __cplusplus
-
-COVERAGE(HMM_LengthVec2CPP, 1)
-HMM_INLINE float HMM_PREFIX(Length)(hmm_vec2 A)
-{
- ASSERT_COVERED(HMM_LengthVec2CPP);
-
- float Result = HMM_PREFIX(LengthVec2)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_LengthVec3CPP, 1)
-HMM_INLINE float HMM_PREFIX(Length)(hmm_vec3 A)
-{
- ASSERT_COVERED(HMM_LengthVec3CPP);
-
- float Result = HMM_PREFIX(LengthVec3)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_LengthVec4CPP, 1)
-HMM_INLINE float HMM_PREFIX(Length)(hmm_vec4 A)
-{
- ASSERT_COVERED(HMM_LengthVec4CPP);
-
- float Result = HMM_PREFIX(LengthVec4)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_LengthSquaredVec2CPP, 1)
-HMM_INLINE float HMM_PREFIX(LengthSquared)(hmm_vec2 A)
-{
- ASSERT_COVERED(HMM_LengthSquaredVec2CPP);
-
- float Result = HMM_PREFIX(LengthSquaredVec2)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_LengthSquaredVec3CPP, 1)
-HMM_INLINE float HMM_PREFIX(LengthSquared)(hmm_vec3 A)
-{
- ASSERT_COVERED(HMM_LengthSquaredVec3CPP);
-
- float Result = HMM_PREFIX(LengthSquaredVec3)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_LengthSquaredVec4CPP, 1)
-HMM_INLINE float HMM_PREFIX(LengthSquared)(hmm_vec4 A)
-{
- ASSERT_COVERED(HMM_LengthSquaredVec4CPP);
-
- float Result = HMM_PREFIX(LengthSquaredVec4)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_NormalizeVec2CPP, 1)
-HMM_INLINE hmm_vec2 HMM_PREFIX(Normalize)(hmm_vec2 A)
-{
- ASSERT_COVERED(HMM_NormalizeVec2CPP);
-
- hmm_vec2 Result = HMM_PREFIX(NormalizeVec2)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_NormalizeVec3CPP, 1)
-HMM_INLINE hmm_vec3 HMM_PREFIX(Normalize)(hmm_vec3 A)
-{
- ASSERT_COVERED(HMM_NormalizeVec3CPP);
-
- hmm_vec3 Result = HMM_PREFIX(NormalizeVec3)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_NormalizeVec4CPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(Normalize)(hmm_vec4 A)
-{
- ASSERT_COVERED(HMM_NormalizeVec4CPP);
-
- hmm_vec4 Result = HMM_PREFIX(NormalizeVec4)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_FastNormalizeVec2CPP, 1)
-HMM_INLINE hmm_vec2 HMM_PREFIX(FastNormalize)(hmm_vec2 A)
-{
- ASSERT_COVERED(HMM_FastNormalizeVec2CPP);
-
- hmm_vec2 Result = HMM_PREFIX(FastNormalizeVec2)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_FastNormalizeVec3CPP, 1)
-HMM_INLINE hmm_vec3 HMM_PREFIX(FastNormalize)(hmm_vec3 A)
-{
- ASSERT_COVERED(HMM_FastNormalizeVec3CPP);
-
- hmm_vec3 Result = HMM_PREFIX(FastNormalizeVec3)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_FastNormalizeVec4CPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(FastNormalize)(hmm_vec4 A)
-{
- ASSERT_COVERED(HMM_FastNormalizeVec4CPP);
-
- hmm_vec4 Result = HMM_PREFIX(FastNormalizeVec4)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_NormalizeQuaternionCPP, 1)
-HMM_INLINE hmm_quaternion HMM_PREFIX(Normalize)(hmm_quaternion A)
-{
- ASSERT_COVERED(HMM_NormalizeQuaternionCPP);
-
- hmm_quaternion Result = HMM_PREFIX(NormalizeQuaternion)(A);
-
- return (Result);
-}
-
-COVERAGE(HMM_DotVec2CPP, 1)
-HMM_INLINE float HMM_PREFIX(Dot)(hmm_vec2 VecOne, hmm_vec2 VecTwo)
-{
- ASSERT_COVERED(HMM_DotVec2CPP);
-
- float Result = HMM_PREFIX(DotVec2)(VecOne, VecTwo);
-
- return (Result);
-}
-
-COVERAGE(HMM_DotVec3CPP, 1)
-HMM_INLINE float HMM_PREFIX(Dot)(hmm_vec3 VecOne, hmm_vec3 VecTwo)
-{
- ASSERT_COVERED(HMM_DotVec3CPP);
-
- float Result = HMM_PREFIX(DotVec3)(VecOne, VecTwo);
-
- return (Result);
-}
-
-COVERAGE(HMM_DotVec4CPP, 1)
-HMM_INLINE float HMM_PREFIX(Dot)(hmm_vec4 VecOne, hmm_vec4 VecTwo)
-{
- ASSERT_COVERED(HMM_DotVec4CPP);
-
- float Result = HMM_PREFIX(DotVec4)(VecOne, VecTwo);
-
- return (Result);
-}
-
-COVERAGE(HMM_DotQuaternionCPP, 1)
-HMM_INLINE float HMM_PREFIX(Dot)(hmm_quaternion QuatOne, hmm_quaternion QuatTwo)
-{
- ASSERT_COVERED(HMM_DotQuaternionCPP);
-
- float Result = HMM_PREFIX(DotQuaternion)(QuatOne, QuatTwo);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddVec2CPP, 1)
-HMM_INLINE hmm_vec2 HMM_PREFIX(Add)(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_AddVec2CPP);
-
- hmm_vec2 Result = HMM_PREFIX(AddVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddVec3CPP, 1)
-HMM_INLINE hmm_vec3 HMM_PREFIX(Add)(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_AddVec3CPP);
-
- hmm_vec3 Result = HMM_PREFIX(AddVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddVec4CPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(Add)(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_AddVec4CPP);
-
- hmm_vec4 Result = HMM_PREFIX(AddVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddMat4CPP, 1)
-HMM_INLINE hmm_mat4 HMM_PREFIX(Add)(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_AddMat4CPP);
-
- hmm_mat4 Result = HMM_PREFIX(AddMat4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddQuaternionCPP, 1)
-HMM_INLINE hmm_quaternion HMM_PREFIX(Add)(hmm_quaternion Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_AddQuaternionCPP);
-
- hmm_quaternion Result = HMM_PREFIX(AddQuaternion)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractVec2CPP, 1)
-HMM_INLINE hmm_vec2 HMM_PREFIX(Subtract)(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec2CPP);
-
- hmm_vec2 Result = HMM_PREFIX(SubtractVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractVec3CPP, 1)
-HMM_INLINE hmm_vec3 HMM_PREFIX(Subtract)(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec3CPP);
-
- hmm_vec3 Result = HMM_PREFIX(SubtractVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractVec4CPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(Subtract)(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec4CPP);
-
- hmm_vec4 Result = HMM_PREFIX(SubtractVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractMat4CPP, 1)
-HMM_INLINE hmm_mat4 HMM_PREFIX(Subtract)(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_SubtractMat4CPP);
-
- hmm_mat4 Result = HMM_PREFIX(SubtractMat4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractQuaternionCPP, 1)
-HMM_INLINE hmm_quaternion HMM_PREFIX(Subtract)(hmm_quaternion Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_SubtractQuaternionCPP);
-
- hmm_quaternion Result = HMM_PREFIX(SubtractQuaternion)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec2CPP, 1)
-HMM_INLINE hmm_vec2 HMM_PREFIX(Multiply)(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec2CPP);
-
- hmm_vec2 Result = HMM_PREFIX(MultiplyVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec2fCPP, 1)
-HMM_INLINE hmm_vec2 HMM_PREFIX(Multiply)(hmm_vec2 Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec2fCPP);
-
- hmm_vec2 Result = HMM_PREFIX(MultiplyVec2f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec3CPP, 1)
-HMM_INLINE hmm_vec3 HMM_PREFIX(Multiply)(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec3CPP);
-
- hmm_vec3 Result = HMM_PREFIX(MultiplyVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec3fCPP, 1)
-HMM_INLINE hmm_vec3 HMM_PREFIX(Multiply)(hmm_vec3 Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec3fCPP);
-
- hmm_vec3 Result = HMM_PREFIX(MultiplyVec3f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec4CPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(Multiply)(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec4CPP);
-
- hmm_vec4 Result = HMM_PREFIX(MultiplyVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec4fCPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(Multiply)(hmm_vec4 Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec4fCPP);
-
- hmm_vec4 Result = HMM_PREFIX(MultiplyVec4f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyMat4CPP, 1)
-HMM_INLINE hmm_mat4 HMM_PREFIX(Multiply)(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_MultiplyMat4CPP);
-
- hmm_mat4 Result = HMM_PREFIX(MultiplyMat4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyMat4fCPP, 1)
-HMM_INLINE hmm_mat4 HMM_PREFIX(Multiply)(hmm_mat4 Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyMat4fCPP);
-
- hmm_mat4 Result = HMM_PREFIX(MultiplyMat4f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyMat4ByVec4CPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(Multiply)(hmm_mat4 Matrix, hmm_vec4 Vector)
-{
- ASSERT_COVERED(HMM_MultiplyMat4ByVec4CPP);
-
- hmm_vec4 Result = HMM_PREFIX(MultiplyMat4ByVec4)(Matrix, Vector);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyQuaternionCPP, 1)
-HMM_INLINE hmm_quaternion HMM_PREFIX(Multiply)(hmm_quaternion Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_MultiplyQuaternionCPP);
-
- hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternion)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyQuaternionFCPP, 1)
-HMM_INLINE hmm_quaternion HMM_PREFIX(Multiply)(hmm_quaternion Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyQuaternionFCPP);
-
- hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternionF)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec2CPP, 1)
-HMM_INLINE hmm_vec2 HMM_PREFIX(Divide)(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_DivideVec2CPP);
-
- hmm_vec2 Result = HMM_PREFIX(DivideVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec2fCPP, 1)
-HMM_INLINE hmm_vec2 HMM_PREFIX(Divide)(hmm_vec2 Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec2fCPP);
-
- hmm_vec2 Result = HMM_PREFIX(DivideVec2f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec3CPP, 1)
-HMM_INLINE hmm_vec3 HMM_PREFIX(Divide)(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_DivideVec3CPP);
-
- hmm_vec3 Result = HMM_PREFIX(DivideVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec3fCPP, 1)
-HMM_INLINE hmm_vec3 HMM_PREFIX(Divide)(hmm_vec3 Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec3fCPP);
-
- hmm_vec3 Result = HMM_PREFIX(DivideVec3f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec4CPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(Divide)(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_DivideVec4CPP);
-
- hmm_vec4 Result = HMM_PREFIX(DivideVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec4fCPP, 1)
-HMM_INLINE hmm_vec4 HMM_PREFIX(Divide)(hmm_vec4 Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec4fCPP);
-
- hmm_vec4 Result = HMM_PREFIX(DivideVec4f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideMat4fCPP, 1)
-HMM_INLINE hmm_mat4 HMM_PREFIX(Divide)(hmm_mat4 Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideMat4fCPP);
-
- hmm_mat4 Result = HMM_PREFIX(DivideMat4f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideQuaternionFCPP, 1)
-HMM_INLINE hmm_quaternion HMM_PREFIX(Divide)(hmm_quaternion Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideQuaternionFCPP);
-
- hmm_quaternion Result = HMM_PREFIX(DivideQuaternionF)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_EqualsVec2CPP, 1)
-HMM_INLINE hmm_bool HMM_PREFIX(Equals)(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec2CPP);
-
- hmm_bool Result = HMM_PREFIX(EqualsVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_EqualsVec3CPP, 1)
-HMM_INLINE hmm_bool HMM_PREFIX(Equals)(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec3CPP);
-
- hmm_bool Result = HMM_PREFIX(EqualsVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_EqualsVec4CPP, 1)
-HMM_INLINE hmm_bool HMM_PREFIX(Equals)(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec4CPP);
-
- hmm_bool Result = HMM_PREFIX(EqualsVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddVec2Op, 1)
-HMM_INLINE hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_AddVec2Op);
-
- hmm_vec2 Result = HMM_PREFIX(AddVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddVec3Op, 1)
-HMM_INLINE hmm_vec3 operator+(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_AddVec3Op);
-
- hmm_vec3 Result = HMM_PREFIX(AddVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddVec4Op, 1)
-HMM_INLINE hmm_vec4 operator+(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_AddVec4Op);
-
- hmm_vec4 Result = HMM_PREFIX(AddVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddMat4Op, 1)
-HMM_INLINE hmm_mat4 operator+(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_AddMat4Op);
-
- hmm_mat4 Result = HMM_PREFIX(AddMat4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddQuaternionOp, 1)
-HMM_INLINE hmm_quaternion operator+(hmm_quaternion Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_AddQuaternionOp);
-
- hmm_quaternion Result = HMM_PREFIX(AddQuaternion)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractVec2Op, 1)
-HMM_INLINE hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec2Op);
-
- hmm_vec2 Result = HMM_PREFIX(SubtractVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractVec3Op, 1)
-HMM_INLINE hmm_vec3 operator-(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec3Op);
-
- hmm_vec3 Result = HMM_PREFIX(SubtractVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractVec4Op, 1)
-HMM_INLINE hmm_vec4 operator-(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec4Op);
-
- hmm_vec4 Result = HMM_PREFIX(SubtractVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractMat4Op, 1)
-HMM_INLINE hmm_mat4 operator-(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_SubtractMat4Op);
-
- hmm_mat4 Result = HMM_PREFIX(SubtractMat4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_SubtractQuaternionOp, 1)
-HMM_INLINE hmm_quaternion operator-(hmm_quaternion Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_SubtractQuaternionOp);
-
- hmm_quaternion Result = HMM_PREFIX(SubtractQuaternion)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec2Op, 1)
-HMM_INLINE hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec2Op);
-
- hmm_vec2 Result = HMM_PREFIX(MultiplyVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec3Op, 1)
-HMM_INLINE hmm_vec3 operator*(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec3Op);
-
- hmm_vec3 Result = HMM_PREFIX(MultiplyVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec4Op, 1)
-HMM_INLINE hmm_vec4 operator*(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec4Op);
-
- hmm_vec4 Result = HMM_PREFIX(MultiplyVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyMat4Op, 1)
-HMM_INLINE hmm_mat4 operator*(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_MultiplyMat4Op);
-
- hmm_mat4 Result = HMM_PREFIX(MultiplyMat4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyQuaternionOp, 1)
-HMM_INLINE hmm_quaternion operator*(hmm_quaternion Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_MultiplyQuaternionOp);
-
- hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternion)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec2fOp, 1)
-HMM_INLINE hmm_vec2 operator*(hmm_vec2 Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec2fOp);
-
- hmm_vec2 Result = HMM_PREFIX(MultiplyVec2f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec3fOp, 1)
-HMM_INLINE hmm_vec3 operator*(hmm_vec3 Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec3fOp);
-
- hmm_vec3 Result = HMM_PREFIX(MultiplyVec3f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec4fOp, 1)
-HMM_INLINE hmm_vec4 operator*(hmm_vec4 Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec4fOp);
-
- hmm_vec4 Result = HMM_PREFIX(MultiplyVec4f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyMat4fOp, 1)
-HMM_INLINE hmm_mat4 operator*(hmm_mat4 Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyMat4fOp);
-
- hmm_mat4 Result = HMM_PREFIX(MultiplyMat4f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyQuaternionFOp, 1)
-HMM_INLINE hmm_quaternion operator*(hmm_quaternion Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyQuaternionFOp);
-
- hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternionF)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec2fOpLeft, 1)
-HMM_INLINE hmm_vec2 operator*(float Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec2fOpLeft);
-
- hmm_vec2 Result = HMM_PREFIX(MultiplyVec2f)(Right, Left);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec3fOpLeft, 1)
-HMM_INLINE hmm_vec3 operator*(float Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec3fOpLeft);
-
- hmm_vec3 Result = HMM_PREFIX(MultiplyVec3f)(Right, Left);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyVec4fOpLeft, 1)
-HMM_INLINE hmm_vec4 operator*(float Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec4fOpLeft);
-
- hmm_vec4 Result = HMM_PREFIX(MultiplyVec4f)(Right, Left);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyMat4fOpLeft, 1)
-HMM_INLINE hmm_mat4 operator*(float Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_MultiplyMat4fOpLeft);
-
- hmm_mat4 Result = HMM_PREFIX(MultiplyMat4f)(Right, Left);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyQuaternionFOpLeft, 1)
-HMM_INLINE hmm_quaternion operator*(float Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_MultiplyQuaternionFOpLeft);
-
- hmm_quaternion Result = HMM_PREFIX(MultiplyQuaternionF)(Right, Left);
-
- return (Result);
-}
-
-COVERAGE(HMM_MultiplyMat4ByVec4Op, 1)
-HMM_INLINE hmm_vec4 operator*(hmm_mat4 Matrix, hmm_vec4 Vector)
-{
- ASSERT_COVERED(HMM_MultiplyMat4ByVec4Op);
-
- hmm_vec4 Result = HMM_PREFIX(MultiplyMat4ByVec4)(Matrix, Vector);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec2Op, 1)
-HMM_INLINE hmm_vec2 operator/(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_DivideVec2Op);
-
- hmm_vec2 Result = HMM_PREFIX(DivideVec2)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec3Op, 1)
-HMM_INLINE hmm_vec3 operator/(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_DivideVec3Op);
-
- hmm_vec3 Result = HMM_PREFIX(DivideVec3)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec4Op, 1)
-HMM_INLINE hmm_vec4 operator/(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_DivideVec4Op);
-
- hmm_vec4 Result = HMM_PREFIX(DivideVec4)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec2fOp, 1)
-HMM_INLINE hmm_vec2 operator/(hmm_vec2 Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec2fOp);
-
- hmm_vec2 Result = HMM_PREFIX(DivideVec2f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec3fOp, 1)
-HMM_INLINE hmm_vec3 operator/(hmm_vec3 Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec3fOp);
-
- hmm_vec3 Result = HMM_PREFIX(DivideVec3f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideVec4fOp, 1)
-HMM_INLINE hmm_vec4 operator/(hmm_vec4 Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec4fOp);
-
- hmm_vec4 Result = HMM_PREFIX(DivideVec4f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideMat4fOp, 1)
-HMM_INLINE hmm_mat4 operator/(hmm_mat4 Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideMat4fOp);
-
- hmm_mat4 Result = HMM_PREFIX(DivideMat4f)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_DivideQuaternionFOp, 1)
-HMM_INLINE hmm_quaternion operator/(hmm_quaternion Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideQuaternionFOp);
-
- hmm_quaternion Result = HMM_PREFIX(DivideQuaternionF)(Left, Right);
-
- return (Result);
-}
-
-COVERAGE(HMM_AddVec2Assign, 1)
-HMM_INLINE hmm_vec2 &operator+=(hmm_vec2 &Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_AddVec2Assign);
-
- return (Left = Left + Right);
-}
-
-COVERAGE(HMM_AddVec3Assign, 1)
-HMM_INLINE hmm_vec3 &operator+=(hmm_vec3 &Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_AddVec3Assign);
-
- return (Left = Left + Right);
-}
-
-COVERAGE(HMM_AddVec4Assign, 1)
-HMM_INLINE hmm_vec4 &operator+=(hmm_vec4 &Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_AddVec4Assign);
-
- return (Left = Left + Right);
-}
-
-COVERAGE(HMM_AddMat4Assign, 1)
-HMM_INLINE hmm_mat4 &operator+=(hmm_mat4 &Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_AddMat4Assign);
-
- return (Left = Left + Right);
-}
-
-COVERAGE(HMM_AddQuaternionAssign, 1)
-HMM_INLINE hmm_quaternion &operator+=(hmm_quaternion &Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_AddQuaternionAssign);
-
- return (Left = Left + Right);
-}
-
-COVERAGE(HMM_SubtractVec2Assign, 1)
-HMM_INLINE hmm_vec2 &operator-=(hmm_vec2 &Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec2Assign);
-
- return (Left = Left - Right);
-}
-
-COVERAGE(HMM_SubtractVec3Assign, 1)
-HMM_INLINE hmm_vec3 &operator-=(hmm_vec3 &Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec3Assign);
-
- return (Left = Left - Right);
-}
-
-COVERAGE(HMM_SubtractVec4Assign, 1)
-HMM_INLINE hmm_vec4 &operator-=(hmm_vec4 &Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_SubtractVec4Assign);
-
- return (Left = Left - Right);
-}
-
-COVERAGE(HMM_SubtractMat4Assign, 1)
-HMM_INLINE hmm_mat4 &operator-=(hmm_mat4 &Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_SubtractMat4Assign);
-
- return (Left = Left - Right);
-}
-
-COVERAGE(HMM_SubtractQuaternionAssign, 1)
-HMM_INLINE hmm_quaternion &operator-=(hmm_quaternion &Left, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_SubtractQuaternionAssign);
-
- return (Left = Left - Right);
-}
-
-COVERAGE(HMM_MultiplyVec2Assign, 1)
-HMM_INLINE hmm_vec2 &operator*=(hmm_vec2 &Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec2Assign);
-
- return (Left = Left * Right);
-}
-
-COVERAGE(HMM_MultiplyVec3Assign, 1)
-HMM_INLINE hmm_vec3 &operator*=(hmm_vec3 &Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec3Assign);
-
- return (Left = Left * Right);
-}
-
-COVERAGE(HMM_MultiplyVec4Assign, 1)
-HMM_INLINE hmm_vec4 &operator*=(hmm_vec4 &Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec4Assign);
-
- return (Left = Left * Right);
-}
-
-COVERAGE(HMM_MultiplyVec2fAssign, 1)
-HMM_INLINE hmm_vec2 &operator*=(hmm_vec2 &Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec2fAssign);
-
- return (Left = Left * Right);
-}
-
-COVERAGE(HMM_MultiplyVec3fAssign, 1)
-HMM_INLINE hmm_vec3 &operator*=(hmm_vec3 &Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec3fAssign);
-
- return (Left = Left * Right);
-}
-
-COVERAGE(HMM_MultiplyVec4fAssign, 1)
-HMM_INLINE hmm_vec4 &operator*=(hmm_vec4 &Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyVec4fAssign);
-
- return (Left = Left * Right);
-}
-
-COVERAGE(HMM_MultiplyMat4fAssign, 1)
-HMM_INLINE hmm_mat4 &operator*=(hmm_mat4 &Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyMat4fAssign);
-
- return (Left = Left * Right);
-}
-
-COVERAGE(HMM_MultiplyQuaternionFAssign, 1)
-HMM_INLINE hmm_quaternion &operator*=(hmm_quaternion &Left, float Right)
-{
- ASSERT_COVERED(HMM_MultiplyQuaternionFAssign);
-
- return (Left = Left * Right);
-}
-
-COVERAGE(HMM_DivideVec2Assign, 1)
-HMM_INLINE hmm_vec2 &operator/=(hmm_vec2 &Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_DivideVec2Assign);
-
- return (Left = Left / Right);
-}
-
-COVERAGE(HMM_DivideVec3Assign, 1)
-HMM_INLINE hmm_vec3 &operator/=(hmm_vec3 &Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_DivideVec3Assign);
-
- return (Left = Left / Right);
-}
-
-COVERAGE(HMM_DivideVec4Assign, 1)
-HMM_INLINE hmm_vec4 &operator/=(hmm_vec4 &Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_DivideVec4Assign);
-
- return (Left = Left / Right);
-}
-
-COVERAGE(HMM_DivideVec2fAssign, 1)
-HMM_INLINE hmm_vec2 &operator/=(hmm_vec2 &Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec2fAssign);
-
- return (Left = Left / Right);
-}
-
-COVERAGE(HMM_DivideVec3fAssign, 1)
-HMM_INLINE hmm_vec3 &operator/=(hmm_vec3 &Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec3fAssign);
-
- return (Left = Left / Right);
-}
-
-COVERAGE(HMM_DivideVec4fAssign, 1)
-HMM_INLINE hmm_vec4 &operator/=(hmm_vec4 &Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideVec4fAssign);
-
- return (Left = Left / Right);
-}
-
-COVERAGE(HMM_DivideMat4fAssign, 1)
-HMM_INLINE hmm_mat4 &operator/=(hmm_mat4 &Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideMat4fAssign);
-
- return (Left = Left / Right);
-}
-
-COVERAGE(HMM_DivideQuaternionFAssign, 1)
-HMM_INLINE hmm_quaternion &operator/=(hmm_quaternion &Left, float Right)
-{
- ASSERT_COVERED(HMM_DivideQuaternionFAssign);
-
- return (Left = Left / Right);
-}
-
-COVERAGE(HMM_EqualsVec2Op, 1)
-HMM_INLINE hmm_bool operator==(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec2Op);
-
- return HMM_PREFIX(EqualsVec2)(Left, Right);
-}
-
-COVERAGE(HMM_EqualsVec3Op, 1)
-HMM_INLINE hmm_bool operator==(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec3Op);
-
- return HMM_PREFIX(EqualsVec3)(Left, Right);
-}
-
-COVERAGE(HMM_EqualsVec4Op, 1)
-HMM_INLINE hmm_bool operator==(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec4Op);
-
- return HMM_PREFIX(EqualsVec4)(Left, Right);
-}
-
-COVERAGE(HMM_EqualsVec2OpNot, 1)
-HMM_INLINE hmm_bool operator!=(hmm_vec2 Left, hmm_vec2 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec2OpNot);
-
- return !HMM_PREFIX(EqualsVec2)(Left, Right);
-}
-
-COVERAGE(HMM_EqualsVec3OpNot, 1)
-HMM_INLINE hmm_bool operator!=(hmm_vec3 Left, hmm_vec3 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec3OpNot);
-
- return !HMM_PREFIX(EqualsVec3)(Left, Right);
-}
-
-COVERAGE(HMM_EqualsVec4OpNot, 1)
-HMM_INLINE hmm_bool operator!=(hmm_vec4 Left, hmm_vec4 Right)
-{
- ASSERT_COVERED(HMM_EqualsVec4OpNot);
-
- return !HMM_PREFIX(EqualsVec4)(Left, Right);
-}
-
-#endif /* __cplusplus */
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-
-#endif /* HANDMADE_MATH_H */
-
-#ifdef HANDMADE_MATH_IMPLEMENTATION
-
-COVERAGE(HMM_Power, 2)
-float HMM_PREFIX(Power)(float Base, int Exponent)
-{
- ASSERT_COVERED(HMM_Power);
-
- float Result = 1.0f;
- float Mul = Exponent < 0 ? 1.f / Base : Base;
- int X = Exponent < 0 ? -Exponent : Exponent;
- while (X)
- {
- if (X & 1)
- {
- ASSERT_COVERED(HMM_Power);
-
- Result *= Mul;
- }
-
- Mul *= Mul;
- X >>= 1;
- }
-
- return (Result);
-}
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_Transpose, 1)
-hmm_mat4 HMM_PREFIX(Transpose)(hmm_mat4 Matrix)
-{
- ASSERT_COVERED(HMM_Transpose);
-
- hmm_mat4 Result;
-
- int Columns;
- for(Columns = 0; Columns < 4; ++Columns)
- {
- int Rows;
- for(Rows = 0; Rows < 4; ++Rows)
- {
- Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows];
- }
- }
-
- return (Result);
-}
-#endif
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_AddMat4, 1)
-hmm_mat4 HMM_PREFIX(AddMat4)(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_AddMat4);
-
- hmm_mat4 Result;
-
- int Columns;
- for(Columns = 0; Columns < 4; ++Columns)
- {
- int Rows;
- for(Rows = 0; Rows < 4; ++Rows)
- {
- Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows];
- }
- }
-
- return (Result);
-}
-#endif
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_SubtractMat4, 1)
-hmm_mat4 HMM_PREFIX(SubtractMat4)(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_SubtractMat4);
-
- hmm_mat4 Result;
-
- int Columns;
- for(Columns = 0; Columns < 4; ++Columns)
- {
- int Rows;
- for(Rows = 0; Rows < 4; ++Rows)
- {
- Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows];
- }
- }
-
- return (Result);
-}
-#endif
-
-COVERAGE(HMM_MultiplyMat4, 1)
-hmm_mat4 HMM_PREFIX(MultiplyMat4)(hmm_mat4 Left, hmm_mat4 Right)
-{
- ASSERT_COVERED(HMM_MultiplyMat4);
-
- hmm_mat4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.Columns[0] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[0], Left);
- Result.Columns[1] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[1], Left);
- Result.Columns[2] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[2], Left);
- Result.Columns[3] = HMM_PREFIX(LinearCombineSSE)(Right.Columns[3], Left);
-#else
- int Columns;
- for(Columns = 0; Columns < 4; ++Columns)
- {
- int Rows;
- for(Rows = 0; Rows < 4; ++Rows)
- {
- float Sum = 0;
- int CurrentMatrice;
- for(CurrentMatrice = 0; CurrentMatrice < 4; ++CurrentMatrice)
- {
- Sum += Left.Elements[CurrentMatrice][Rows] * Right.Elements[Columns][CurrentMatrice];
- }
-
- Result.Elements[Columns][Rows] = Sum;
- }
- }
-#endif
-
- return (Result);
-}
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_MultiplyMat4f, 1)
-hmm_mat4 HMM_PREFIX(MultiplyMat4f)(hmm_mat4 Matrix, float Scalar)
-{
- ASSERT_COVERED(HMM_MultiplyMat4f);
-
- hmm_mat4 Result;
-
- int Columns;
- for(Columns = 0; Columns < 4; ++Columns)
- {
- int Rows;
- for(Rows = 0; Rows < 4; ++Rows)
- {
- Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar;
- }
- }
-
- return (Result);
-}
-#endif
-
-COVERAGE(HMM_MultiplyMat4ByVec4, 1)
-hmm_vec4 HMM_PREFIX(MultiplyMat4ByVec4)(hmm_mat4 Matrix, hmm_vec4 Vector)
-{
- ASSERT_COVERED(HMM_MultiplyMat4ByVec4);
-
- hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
- Result.InternalElementsSSE = HMM_PREFIX(LinearCombineSSE)(Vector.InternalElementsSSE, Matrix);
-#else
- int Columns, Rows;
- for(Rows = 0; Rows < 4; ++Rows)
- {
- float Sum = 0;
- for(Columns = 0; Columns < 4; ++Columns)
- {
- Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns];
- }
-
- Result.Elements[Rows] = Sum;
- }
-#endif
-
- return (Result);
-}
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_DivideMat4f, 1);
-hmm_mat4 HMM_PREFIX(DivideMat4f)(hmm_mat4 Matrix, float Scalar)
-{
- ASSERT_COVERED(HMM_DivideMat4f);
-
- hmm_mat4 Result;
-
- int Columns;
- for(Columns = 0; Columns < 4; ++Columns)
- {
- int Rows;
- for(Rows = 0; Rows < 4; ++Rows)
- {
- Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar;
- }
- }
-
- return (Result);
-}
-#endif
-
-COVERAGE(HMM_Rotate, 1)
-hmm_mat4 HMM_PREFIX(Rotate)(float Angle, hmm_vec3 Axis)
-{
- ASSERT_COVERED(HMM_Rotate);
-
- hmm_mat4 Result = HMM_PREFIX(Mat4d)(1.0f);
-
- Axis = HMM_PREFIX(NormalizeVec3)(Axis);
-
- float SinTheta = HMM_PREFIX(SinF)(HMM_PREFIX(ToRadians)(Angle));
- float CosTheta = HMM_PREFIX(CosF)(HMM_PREFIX(ToRadians)(Angle));
- float CosValue = 1.0f - CosTheta;
-
- Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta;
- Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta);
- Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta);
-
- Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta);
- Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta;
- Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta);
-
- Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta);
- Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta);
- Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta;
-
- return (Result);
-}
-
-COVERAGE(HMM_LookAt, 1)
-hmm_mat4 HMM_PREFIX(LookAt)(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up)
-{
- ASSERT_COVERED(HMM_LookAt);
-
- hmm_mat4 Result;
-
- hmm_vec3 F = HMM_PREFIX(NormalizeVec3)(HMM_PREFIX(SubtractVec3)(Center, Eye));
- hmm_vec3 S = HMM_PREFIX(NormalizeVec3)(HMM_PREFIX(Cross)(F, Up));
- hmm_vec3 U = HMM_PREFIX(Cross)(S, F);
-
- Result.Elements[0][0] = S.X;
- Result.Elements[0][1] = U.X;
- Result.Elements[0][2] = -F.X;
- Result.Elements[0][3] = 0.0f;
-
- Result.Elements[1][0] = S.Y;
- Result.Elements[1][1] = U.Y;
- Result.Elements[1][2] = -F.Y;
- Result.Elements[1][3] = 0.0f;
-
- Result.Elements[2][0] = S.Z;
- Result.Elements[2][1] = U.Z;
- Result.Elements[2][2] = -F.Z;
- Result.Elements[2][3] = 0.0f;
-
- Result.Elements[3][0] = -HMM_PREFIX(DotVec3)(S, Eye);
- Result.Elements[3][1] = -HMM_PREFIX(DotVec3)(U, Eye);
- Result.Elements[3][2] = HMM_PREFIX(DotVec3)(F, Eye);
- Result.Elements[3][3] = 1.0f;
-
- return (Result);
-}
-
-COVERAGE(HMM_InverseQuaternion, 1)
-hmm_quaternion HMM_PREFIX(InverseQuaternion)(hmm_quaternion Left)
-{
- ASSERT_COVERED(HMM_InverseQuaternion);
-
- hmm_quaternion Conjugate;
- hmm_quaternion Result;
- float Norm = 0;
- float NormSquared = 0;
-
- Conjugate.X = -Left.X;
- Conjugate.Y = -Left.Y;
- Conjugate.Z = -Left.Z;
- Conjugate.W = Left.W;
-
- Norm = HMM_PREFIX(SquareRootF)(HMM_PREFIX(DotQuaternion)(Left, Left));
- NormSquared = Norm * Norm;
-
- Result = HMM_PREFIX(DivideQuaternionF)(Conjugate, NormSquared);
-
- return (Result);
-}
-
-COVERAGE(HMM_Slerp, 1)
-hmm_quaternion HMM_PREFIX(Slerp)(hmm_quaternion Left, float Time, hmm_quaternion Right)
-{
- ASSERT_COVERED(HMM_Slerp);
-
- hmm_quaternion Result;
- hmm_quaternion QuaternionLeft;
- hmm_quaternion QuaternionRight;
-
- float Cos_Theta = HMM_PREFIX(DotQuaternion)(Left, Right);
- float Angle = HMM_PREFIX(ACosF)(Cos_Theta);
-
- float S1 = HMM_PREFIX(SinF)((1.0f - Time) * Angle);
- float S2 = HMM_PREFIX(SinF)(Time * Angle);
- float Is = 1.0f / HMM_PREFIX(SinF)(Angle);
-
- QuaternionLeft = HMM_PREFIX(MultiplyQuaternionF)(Left, S1);
- QuaternionRight = HMM_PREFIX(MultiplyQuaternionF)(Right, S2);
-
- Result = HMM_PREFIX(AddQuaternion)(QuaternionLeft, QuaternionRight);
- Result = HMM_PREFIX(MultiplyQuaternionF)(Result, Is);
-
- return (Result);
-}
-
-COVERAGE(HMM_QuaternionToMat4, 1)
-hmm_mat4 HMM_PREFIX(QuaternionToMat4)(hmm_quaternion Left)
-{
- ASSERT_COVERED(HMM_QuaternionToMat4);
-
- hmm_mat4 Result;
-
- hmm_quaternion NormalizedQuaternion = HMM_PREFIX(NormalizeQuaternion)(Left);
-
- float XX, YY, ZZ,
- XY, XZ, YZ,
- WX, WY, WZ;
-
- XX = NormalizedQuaternion.X * NormalizedQuaternion.X;
- YY = NormalizedQuaternion.Y * NormalizedQuaternion.Y;
- ZZ = NormalizedQuaternion.Z * NormalizedQuaternion.Z;
- XY = NormalizedQuaternion.X * NormalizedQuaternion.Y;
- XZ = NormalizedQuaternion.X * NormalizedQuaternion.Z;
- YZ = NormalizedQuaternion.Y * NormalizedQuaternion.Z;
- WX = NormalizedQuaternion.W * NormalizedQuaternion.X;
- WY = NormalizedQuaternion.W * NormalizedQuaternion.Y;
- WZ = NormalizedQuaternion.W * NormalizedQuaternion.Z;
-
- Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ);
- Result.Elements[0][1] = 2.0f * (XY + WZ);
- Result.Elements[0][2] = 2.0f * (XZ - WY);
- Result.Elements[0][3] = 0.0f;
-
- Result.Elements[1][0] = 2.0f * (XY - WZ);
- Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ);
- Result.Elements[1][2] = 2.0f * (YZ + WX);
- Result.Elements[1][3] = 0.0f;
-
- Result.Elements[2][0] = 2.0f * (XZ + WY);
- Result.Elements[2][1] = 2.0f * (YZ - WX);
- Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY);
- Result.Elements[2][3] = 0.0f;
-
- Result.Elements[3][0] = 0.0f;
- Result.Elements[3][1] = 0.0f;
- Result.Elements[3][2] = 0.0f;
- Result.Elements[3][3] = 1.0f;
-
- return (Result);
-}
-
-// This method taken from Mike Day at Insomniac Games.
-// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf
-//
-// Note that as mentioned at the top of the paper, the paper assumes the matrix
-// would be *post*-multiplied to a vector to rotate it, meaning the matrix is
-// the transpose of what we're dealing with. But, because our matrices are
-// stored in column-major order, the indices *appear* to match the paper.
-//
-// For example, m12 in the paper is row 1, column 2. We need to transpose it to
-// row 2, column 1. But, because the column comes first when referencing
-// elements, it looks like M.Elements[1][2].
-//
-// Don't be confused! Or if you must be confused, at least trust this
-// comment. :)
-COVERAGE(HMM_Mat4ToQuaternion, 4)
-hmm_quaternion HMM_PREFIX(Mat4ToQuaternion)(hmm_mat4 M)
-{
- float T;
- hmm_quaternion Q;
-
- if (M.Elements[2][2] < 0.0f) {
- if (M.Elements[0][0] > M.Elements[1][1]) {
- ASSERT_COVERED(HMM_Mat4ToQuaternion);
-
- T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2];
- Q = HMM_PREFIX(Quaternion)(
- T,
- M.Elements[0][1] + M.Elements[1][0],
- M.Elements[2][0] + M.Elements[0][2],
- M.Elements[1][2] - M.Elements[2][1]
- );
- } else {
- ASSERT_COVERED(HMM_Mat4ToQuaternion);
-
- T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2];
- Q = HMM_PREFIX(Quaternion)(
- M.Elements[0][1] + M.Elements[1][0],
- T,
- M.Elements[1][2] + M.Elements[2][1],
- M.Elements[2][0] - M.Elements[0][2]
- );
- }
- } else {
- if (M.Elements[0][0] < -M.Elements[1][1]) {
- ASSERT_COVERED(HMM_Mat4ToQuaternion);
-
- T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2];
- Q = HMM_PREFIX(Quaternion)(
- M.Elements[2][0] + M.Elements[0][2],
- M.Elements[1][2] + M.Elements[2][1],
- T,
- M.Elements[0][1] - M.Elements[1][0]
- );
- } else {
- ASSERT_COVERED(HMM_Mat4ToQuaternion);
-
- T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2];
- Q = HMM_PREFIX(Quaternion)(
- M.Elements[1][2] - M.Elements[2][1],
- M.Elements[2][0] - M.Elements[0][2],
- M.Elements[0][1] - M.Elements[1][0],
- T
- );
- }
- }
-
- Q = HMM_PREFIX(MultiplyQuaternionF)(Q, 0.5f / HMM_PREFIX(SquareRootF)(T));
-
- return Q;
-}
-
-COVERAGE(HMM_QuaternionFromAxisAngle, 1)
-hmm_quaternion HMM_PREFIX(QuaternionFromAxisAngle)(hmm_vec3 Axis, float AngleOfRotation)
-{
- ASSERT_COVERED(HMM_QuaternionFromAxisAngle);
-
- hmm_quaternion Result;
-
- hmm_vec3 AxisNormalized = HMM_PREFIX(NormalizeVec3)(Axis);
- float SineOfRotation = HMM_PREFIX(SinF)(AngleOfRotation / 2.0f);
-
- Result.XYZ = HMM_PREFIX(MultiplyVec3f)(AxisNormalized, SineOfRotation);
- Result.W = HMM_PREFIX(CosF)(AngleOfRotation / 2.0f);
-
- return (Result);
-}
-
-#endif /* HANDMADE_MATH_IMPLEMENTATION */
\ No newline at end of file
diff --git a/src/app/interface.h b/src/app/interface.h
deleted file mode 100644
index f0942a7..0000000
--- a/src/app/interface.h
+++ /dev/null
@@ -1,1632 +0,0 @@
-//
-// File: interface.h
-// Author: Peter Slattery
-// Creation Date: 2020-01-01
-//
-#ifndef INTERFACE_H
-
-#define InterfaceAssert(IMemPtr) Assert(IMemPtr && (u64)IMemPtr != 0x5 && (u64)IMemPtr != 0xC)
-
-enum gs_string_alignment
-{
- Align_Left,
- Align_Center,
- Align_Right,
-};
-
-internal void
-ClipUVRect(rect2* Bounds, rect2* UVs, rect2 ClippingBox)
-{
- rect2 NewBounds = Rect2Union(*Bounds, ClippingBox);
-
- r32 OldWidth = Rect2Width(*Bounds);
- r32 OldHeight = Rect2Height(*Bounds);
-
- v2 MinInsetPercent = v2{
- (NewBounds.Min.x - Bounds->Min.x) / OldWidth,
- (NewBounds.Min.y - Bounds->Min.y) / OldHeight,
- };
-
- v2 MaxInsetPercent = v2{
- (NewBounds.Max.x - Bounds->Min.x) / OldWidth,
- (NewBounds.Max.y - Bounds->Min.y) / OldHeight,
- };
-
- UVs->Min.x = LerpR32(MinInsetPercent.x, UVs->Min.x, UVs->Max.x);
- UVs->Min.y = LerpR32(MinInsetPercent.y, UVs->Min.y, UVs->Max.y);
- UVs->Max.x = LerpR32(MaxInsetPercent.x, UVs->Min.x, UVs->Max.x);
- UVs->Max.y = LerpR32(MaxInsetPercent.y, UVs->Min.y, UVs->Max.y);
-
- *Bounds = NewBounds;
-}
-
-internal void
-DrawCharacter_ (render_quad_batch_constructor* BatchConstructor, r32 MinX, r32 MinY, codepoint_bitmap CodepointInfo, rect2 ClippingBox, v4 Color)
-{
- rect2 Bounds = {};
- Bounds.Min.x = FloorR32(MinX);
- Bounds.Min.y = FloorR32(MinY);
- Bounds.Max.x = Bounds.Min.x + (CodepointInfo.Width);
- Bounds.Max.y = Bounds.Min.y + (CodepointInfo.Height);
-
- rect2 UVBounds = {};
- UVBounds.Min = CodepointInfo.UVMin;
- UVBounds.Max = CodepointInfo.UVMax;
-
- ClipUVRect(&Bounds, &UVBounds, ClippingBox);
-
- s32 AlignedMinX = (s32)(MinX);
- s32 AlignedMinY = (s32)(MinY);
- s32 AlignedMaxX = AlignedMinX + (CodepointInfo.Width);
- s32 AlignedMaxY = AlignedMinY + (CodepointInfo.Height);
-
- PushQuad2DOnBatch(BatchConstructor,
- Rect2BottomLeft(Bounds), Rect2BottomRight(Bounds),
- Rect2TopRight(Bounds), Rect2TopLeft(Bounds),
- UVBounds.Min, UVBounds.Max,
- Color);
-}
-
-internal v2
-DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, rect2 ClippingBox, v4 Color)
-{
- s32 GlyphDataIndex = GetIndexForCodepoint(Font, C);
- codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex];
-
- // NOTE(Peter):
- r32 MinX = Position.x + CodepointInfo.XOffset;
- r32 MinY = Position.y + CodepointInfo.YOffset;
- DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color);
-
- // NOTE(Peter):
- v2 PointAfterCharacter = v2{Position.x + CodepointInfo.Width, Position.y};
- return PointAfterCharacter;
-}
-
-internal v2
-DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, rect2 ClippingBox, v4 Color)
-{
- s32 GlyphDataIndex = GetIndexForCodepoint(Font, C);
- codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex];
-
- // NOTE(Peter):
- r32 MinX = Position.x - (CodepointInfo.XOffset + CodepointInfo.Width);
- r32 MinY = Position.y + CodepointInfo.YOffset + CodepointInfo.YOffset;
- DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color);
-
- // NOTE(Peter):
- v2 PointAfterCharacter = v2{Position.x-(CodepointInfo.Width + CodepointInfo.XOffset), Position.y};
- return PointAfterCharacter;
-}
-
-internal v2
-DrawStringLeftAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color)
-{
- v2 RegisterPosition = InitialRegisterPosition;
- char* C = gs_string;
- for (s32 i = 0; i < Length; i++)
- {
- v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color);
- RegisterPosition.x = PositionAfterCharacter.x;
- C++;
- }
- return RegisterPosition;
-}
-
-internal v2
-DrawStringRightAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color)
-{
- v2 RegisterPosition = InitialRegisterPosition;
- char* C = gs_string + Length - 1;
- for (s32 i = Length - 1; i >= 0; i--)
- {
- v2 PositionAfterCharacter = DrawCharacterRightAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color);
- RegisterPosition.x = PositionAfterCharacter.x;
- C--;
- }
- return RegisterPosition;
-}
-
-internal v2
-DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* Font, v2 Position, v4 Color, gs_string_alignment Alignment = Align_Left)
-{
- DEBUG_TRACK_FUNCTION;
- v2 LowerRight = Position;
-
- render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length,
- Font->BitmapMemory,
- Font->BitmapTextureHandle,
- Font->BitmapWidth,
- Font->BitmapHeight,
- Font->BitmapBytesPerPixel,
- Font->BitmapStride);
-
- // TODO(pjs): I don't like this solution but it'll do for now and I want to focus on other problems
- // especially since I think this one will go away once I finish the ui overhaul
- rect2 InfiniteClipBox = {};
- InfiniteClipBox.Min.x = -100000;
- InfiniteClipBox.Min.y = -100000;
- InfiniteClipBox.Max.x = 100000;
- InfiniteClipBox.Max.y = 100000;
-
- v2 RegisterPosition = Position;
- if (Alignment == Align_Left)
- {
- RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color);
- }
- else if (Alignment == Align_Right)
- {
- RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color);
- }
- else
- {
- InvalidCodePath;
- }
-
- LowerRight.x = RegisterPosition.x;
-
- return LowerRight;
-}
-
-internal void
-DrawCursor (render_quad_batch_constructor* BatchConstructor, v2 Position, v4 Color, bitmap_font Font)
-{
- v2 Min = Position;
- v2 Max = Position + v2{(r32)Font.MaxCharWidth, (r32)(Font.Ascent + Font.Descent)};
- PushQuad2DOnBatch(BatchConstructor, Min, Max, Color);
-}
-
-#if 0
-internal v2
-DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32 CursorPosition, bitmap_font* Font, v2 Position, v4 Color, v4 CursorColor, gs_string_alignment Alignment = Align_Left)
-{
- DEBUG_TRACK_FUNCTION;
- v2 LowerRight = Position;
-
- // NOTE(Peter): We push this on first so that the cursor will be drawn underneath any character it may overlap with
- render_quad_batch_constructor CursorBatch = PushRenderQuad2DBatch(RenderBuffer, 1);
- render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length,
- Font->BitmapMemory,
- Font->BitmapTextureHandle,
- Font->BitmapWidth,
- Font->BitmapHeight,
- Font->BitmapBytesPerPixel,
- Font->BitmapStride);
-
- v2 RegisterPosition = Position;
- if (Alignment == Align_Left)
- {
- RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, Color);
- DrawCursor(&CursorBatch, RegisterPosition, GreenV4, *Font);
- if (String.Length - CursorPosition > 0)
- {
- RegisterPosition = DrawStringLeftAligned(&BatchConstructor,
- String.Length - CursorPosition,
- String.Str + CursorPosition,
- RegisterPosition, Font, Color);
- }
- }
- else if (Alignment == Align_Right)
- {
- RegisterPosition = DrawStringRightAligned(&BatchConstructor,
- CursorPosition, String.Str,
- RegisterPosition, Font, Color);
- DrawCursor(&CursorBatch, RegisterPosition, GreenV4, *Font);
- if (String.Length - CursorPosition > 0)
- {
- RegisterPosition = DrawStringRightAligned(&BatchConstructor,
- String.Length - CursorPosition,
- String.Str + CursorPosition,
- RegisterPosition, Font, Color);
- }
- }
- else
- {
- InvalidCodePath;
- }
-
- LowerRight.x = RegisterPosition.x;
- return LowerRight;
-}
-#endif
-
-enum ui_widget_flag
-{
- UIWidgetFlag_ExpandsToFitChildren,
-
- UIWidgetFlag_DrawBackground,
- UIWidgetFlag_DrawString,
- UIWidgetFlag_DrawOutline,
- UIWidgetFlag_DrawHorizontalFill,
- UIWidgetFlag_DrawVerticalFill,
- UIWidgetFlag_DrawFillReversed,
- UIWidgetFlag_DrawFillAsHandle,
-
- UIWidgetFlag_Clickable,
- UIWidgetFlag_Selectable,
- UIWidgetFlag_Typable,
-};
-
-struct ui_widget_id
-{
- u64 Id;
- u64 ZIndex;
-};
-
-enum ui_layout_direction
-{
- LayoutDirection_TopDown,
- LayoutDirection_BottomUp,
- LayoutDirection_Inherit,
-};
-
-struct ui_column
-{
- r32 XMin;
- r32 XMax;
-};
-
-struct ui_widget
-{
- ui_widget_id Id;
-
- gs_string String;
- gs_string_alignment Alignment;
-
- rect2 Bounds;
- u64 Flags;
-
- ui_widget* Next;
-
- // Slider
- r32 FillPercent;
-
- // Layout
- ui_widget* Parent;
-
- v2 Margin;
- r32 RowHeight;
- r32 RowYAt;
-
- ui_layout_direction FillDirection;
-
- ui_column* Columns;
- u32 ColumnsCount;
- u32 ColumnsFilled;
-
- // NOTE(pjs): I'm not sure this will stay but
- // its here so that when we end things like a dropdown,
- // we can check the retained state of that dropdown
- ui_widget_id WidgetReference;
-
- u64 ChildZIndexOffset;
-
- ui_widget* ChildrenRoot;
- ui_widget* ChildrenHead;
- u32 ChildCount;
-};
-
-struct ui_eval_result
-{
- bool Clicked;
- bool Held;
- v2 DragDelta;
-};
-
-struct interface_config
-{
- v4 PanelBG;
-
- // TODO(pjs): Turn these into _Default, _Hot, _Active
- v4 ButtonColor_Inactive, ButtonColor_Active, ButtonColor_Selected;
-
- v4 TextColor;
-
-#define LIST_BG_COLORS_COUNT 2
- v4 ListBGColors[LIST_BG_COLORS_COUNT];
- v4 ListBGHover;
- v4 ListBGSelected;
-
- bitmap_font* Font;
- r32 FontSize;
- v2 Margin;
- r32 RowHeight;
-};
-
-struct ui_widget_retained_state
-{
- ui_widget_id Id;
- bool Value;
- r32 InitialValueR32;
- u32 FramesSinceAccess;
-
- // For use in layouts that allow you to scroll / pan
- v2 ChildrenDrawOffset;
-
- gs_string EditString;
-
- // For dropdowns and rows to be able to error check not closing
- // a layout you open
- u32 MaxChildren;
-};
-
-struct ui_interface
-{
- interface_config Style;
-
- mouse_state Mouse;
- rect2 WindowBounds;
-
- // A per-frame string of the characters which have been typed
- gs_const_string TempInputString;
-
- render_command_buffer* RenderBuffer;
-
- ui_widget* Widgets;
- u64 WidgetsCount;
- u64 WidgetsCountMax;
-
- ui_widget* DrawOrderHead;
- ui_widget* DrawOrderRoot;
-
- ui_widget_id HotWidget;
- // This should really never get higher than 1 or 2
- u8 HotWidgetFramesSinceUpdate;
-
- ui_widget_id ActiveWidget;
- ui_widget_id LastActiveWidget;
-
- ui_widget* ActiveLayout;
-
-#define RETAINED_STATE_MAX 128
- ui_widget_retained_state RetainedState[RETAINED_STATE_MAX];
- u64 RetainedStateCount;
-
- gs_memory_arena* PerFrameMemory;
-
- // TODO(pjs): DONT USE THIS
- // Right now you only need this to create EditStrings for ui_widget_retained_state's
- // and even for those, you eventually want a better solution than "create a string and it lives forever"
- // TODO(pjs): Get rid of the need for this vvv
- gs_memory_arena* Permanent;
-};
-
-internal void
-ui_InterfaceReset(ui_interface* Interface)
-{
- Interface->WidgetsCount = 0;
- Interface->DrawOrderHead = 0;
- Interface->DrawOrderRoot = 0;
- ClearArena(Interface->PerFrameMemory);
- InterfaceAssert(Interface->PerFrameMemory);
-
- for (u32 i = 0; i < Interface->RetainedStateCount; i++)
- {
- Interface->RetainedState[i].FramesSinceAccess += 1;
- if (Interface->RetainedState[i].FramesSinceAccess > 1)
- {
- Interface->RetainedState[i] = {0};
- }
- }
-
- Interface->LastActiveWidget = Interface->ActiveWidget;
-}
-
-internal bool
-ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B)
-{
- bool Result = (A.Id == B.Id);// && (A.ParentId == B.ParentId);
- return Result;
-}
-
-internal void
-ui_WidgetSetFlag(ui_widget* Widget, u64 Flag)
-{
- u64 Value = ((u64)1 << Flag);
- Widget->Flags = Widget->Flags | Value;
-}
-
-internal void
-ui_WidgetClearFlag(ui_widget* Widget, u64 Flag)
-{
- u64 Value = ((u64)1 << Flag);
- Widget->Flags = Widget->Flags & ~Value;
-}
-
-internal bool
-ui_WidgetIsFlagSet(ui_widget Widget, u64 Flag)
-{
- u64 Value = ((u64)1 << Flag);
- bool Result = (Widget.Flags & Value);
- return Result;
-}
-
-internal void
-ui_WidgetSetChildrenPopover(ui_widget* Widget)
-{
- Widget->ChildZIndexOffset = 1000;
-}
-
-internal ui_widget*
-ui_WidgetGetWidgetWithId(ui_widget* Parent, ui_widget_id Id)
-{
- ui_widget* Result = 0;
-
- if (ui_WidgetIdsEqual(Parent->Id, Id))
- {
- Result = Parent;
- }
- else if (Parent->ChildrenRoot != 0)
- {
- for (ui_widget* At = Parent->ChildrenRoot; At != 0; At = At->Next)
- {
- Result = ui_WidgetGetWidgetWithId(At, Id);
- if (Result != 0)
- {
- break;
- }
- }
- }
-
- return Result;
-}
-
-internal ui_widget*
-ui_InterfaceGetWidgetWithId(ui_interface* Interface, ui_widget_id Id)
-{
- ui_widget* Result = 0;
-
- for (ui_widget* At = Interface->DrawOrderRoot; At != 0; At = At->Next)
- {
- Result = ui_WidgetGetWidgetWithId(At, Id);
- if (Result != 0)
- {
- break;
- }
- }
-
- return Result;
-}
-
-internal ui_widget_retained_state*
-ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id)
-{
- ui_widget_retained_state* Result = 0;
- for (u64 i = 0; i < Interface->RetainedStateCount; i++)
- {
- if (ui_WidgetIdsEqual(Interface->RetainedState[i].Id, Id))
- {
- Interface->RetainedState[i].FramesSinceAccess = 0;
- Result = Interface->RetainedState + i;
- break;
- }
- }
- return Result;
-}
-
-internal ui_widget_retained_state*
-ui_CreateRetainedState(ui_interface* Interface, ui_widget* Widget)
-{
- u64 Index = Interface->RetainedStateCount++;
- ui_widget_retained_state* Result = Interface->RetainedState + Index;
- Result->Id = Widget->Id;
- Result->EditString = PushString(Interface->Permanent, 256);
- return Result;
-}
-
-internal ui_widget_retained_state*
-ui_GetOrCreateRetainedState(ui_interface* Interface, ui_widget* Widget)
-{
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
- if (!State)
- {
- State = ui_CreateRetainedState(Interface, Widget);
- }
- return State;
-}
-
-internal ui_widget*
-ui_CreateWidget(ui_interface* Interface, gs_string String)
-{
- InterfaceAssert(Interface->PerFrameMemory);
- Assert(Interface->WidgetsCount < Interface->WidgetsCountMax);
- u64 Index = Interface->WidgetsCount++;
- ui_widget* Result = Interface->Widgets + Index;
- ZeroStruct(Result);
-
- Result->Parent = Interface->ActiveLayout;
-
- u64 Id = HashDJB2ToU64(StringExpand(String));
- if (Result->Parent)
- {
- Id = HashAppendDJB2ToU32(Id, Result->Parent->Id.Id);
- Id = HashAppendDJB2ToU32(Id, Result->Parent->ChildCount);
- //Result->Id.ParentId = Result->Parent->Id.Id;
- }
- Result->Id.Id = Id;
-
- u64 ZIndex = Index + 1;
- if (Result->Parent)
- {
- Result->ChildZIndexOffset += Result->Parent->ChildZIndexOffset;
- ZIndex += Result->Parent->ChildZIndexOffset;
- }
- Result->Id.ZIndex = ZIndex;
-
- Result->String = PushStringCopy(Interface->PerFrameMemory, String.ConstString);
- InterfaceAssert(Interface->PerFrameMemory);
- Result->Alignment = Align_Left;
- Result->Next = 0;
- Result->ChildrenRoot = 0;
- Result->ChildrenHead = 0;
- Result->Flags = 0;
- ui_WidgetSetFlag(Result, UIWidgetFlag_ExpandsToFitChildren);
-
- return Result;
-}
-
-//
-// Interaction
-//
-
-internal b32
-ui_MouseClickedRect(ui_interface Interface, rect2 Rect)
-{
- b32 Result = MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState);
- Result &= PointIsInRect(Rect, Interface.Mouse.Pos);
- return Result;
-}
-
-// Layout
-
-static rect2
-ui_ReserveBounds(ui_interface* Interface, ui_widget* Widget, bool Inset)
-{
- Assert(Widget->ColumnsCount > 0);
- rect2 Bounds = {0};
- u32 ColumnIndex = Widget->ChildCount % Widget->ColumnsCount;
-
- ui_column Column = Widget->Columns[ColumnIndex];
- Bounds.Min.x = Column.XMin;
- Bounds.Min.y = Widget->RowYAt;
- Bounds.Max.x = Column.XMax;
- Bounds.Max.y = Bounds.Min.y + Widget->RowHeight;
-
- if (Inset)
- {
- Bounds.Min.x += Widget->Margin.x;
- Bounds.Min.y += Widget->Margin.y;
- Bounds.Max.x -= Widget->Margin.x;
- Bounds.Max.y -= Widget->Margin.y;
- }
-
- if (Widget->ChildCount == 0)
- {
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
- if (State)
- {
- Bounds = Rect2Translate(Bounds, State->ChildrenDrawOffset);
- }
- }
-
- return Bounds;
-}
-
-internal void
-ui_CommitBounds(ui_widget* Parent, rect2 Bounds)
-{
- u32 ColumnIndex = Parent->ChildCount % Parent->ColumnsCount;
- if (ColumnIndex == 0)
- {
- switch (Parent->FillDirection)
- {
- case LayoutDirection_BottomUp:
- {
- Parent->RowYAt = Bounds.Max.y;
- if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren))
- {
- Parent->Bounds.Max.y = Parent->RowYAt;
- }
- }break;
-
- case LayoutDirection_TopDown:
- {
- Parent->RowYAt = Bounds.Min.y - Parent->RowHeight;
- if (ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren))
- {
- Parent->Bounds.Min.y = Bounds.Min.y;
- }
- }break;
- }
- }
-}
-
-internal void
-ui_ExpandParentToFit(ui_widget* Widget)
-{
- ui_widget* Parent = Widget->Parent;
- switch (Widget->FillDirection)
- {
- case LayoutDirection_TopDown:
- {
- Parent->Bounds.Min.y = Min(Parent->Bounds.Min.y, Widget->Bounds.Min.y - Parent->Margin.y);
- }break;
-
- case LayoutDirection_BottomUp:
- {
- Parent->Bounds.Max.y = Max(Parent->Bounds.Max.y, Widget->Bounds.Max.y + Parent->Margin.y);
- }break;
-
- InvalidDefaultCase;
- }
-}
-
-internal void
-ui_WidgetCreateColumns(ui_widget* Widget, u32 ColumnsCount, ui_interface* Interface)
-{
- Widget->Columns = PushArray(Interface->PerFrameMemory, ui_column, ColumnsCount);
- InterfaceAssert(Interface->PerFrameMemory);
- Widget->ColumnsCount = ColumnsCount;
- Widget->ColumnsFilled = 0;
-}
-
-internal void
-ui_WidgetInitUniformColumns(ui_widget* Widget)
-{
- r32 CurrentRowWidth = Rect2Width(Widget->Bounds);
- r32 ColumnWidth = CurrentRowWidth / Widget->ColumnsCount;
- for (u32 i = 0; i < Widget->ColumnsCount; i++)
- {
- ui_column* Column = Widget->Columns + i;
- Column->XMin = Widget->Bounds.Min.x + (ColumnWidth * i);
- Column->XMax = Column->XMin + ColumnWidth;
- }
-}
-
-internal ui_widget*
-ui_CreateLayoutWidget(ui_interface* Interface, rect2 Bounds, gs_string Name, ui_layout_direction FillDir = LayoutDirection_Inherit)
-{
- ui_widget* Result = ui_CreateWidget(Interface, Name);
- //ui_WidgetSetFlag(Result, UIWidgetFlag_DrawOutline);
-
- Result->Bounds = Bounds;
- Result->Margin = Interface->Style.Margin;
- Result->RowHeight = Interface->Style.RowHeight;
-
- // Single Column Layout
- ui_WidgetCreateColumns(Result, 1, Interface);
- ui_WidgetInitUniformColumns(Result);
-
- if (FillDir == LayoutDirection_Inherit && Result->Parent != 0)
- {
- Result->FillDirection = Result->Parent->FillDirection;
- }
- else
- {
- Result->FillDirection = FillDir;
- }
-
- switch(Result->FillDirection)
- {
- case LayoutDirection_BottomUp:
- {
- Result->RowYAt = Bounds.Min.y;
- }break;
-
- case LayoutDirection_TopDown:
- {
- Result->RowYAt = Bounds.Max.y - Result->RowHeight;
- }break;
-
- InvalidDefaultCase;
- }
-
- return Result;
-}
-
-static ui_widget*
-ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name)
-{
- ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir);
- SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result);
- Interface->ActiveLayout = Result;
- return Result;
-}
-
-static ui_widget*
-ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name)
-{
- ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir);
-
- if (Interface->DrawOrderRoot)
- {
- SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Result);
- Interface->ActiveLayout->ChildCount++;
- }
- else
- {
- SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result);
- }
-
- Interface->ActiveLayout = Result;
- return Result;
-}
-
-static ui_widget*
-ui_PushLayout(ui_interface* Interface, gs_string Name, bool Inset = true)
-{
- rect2 Bounds = {};
- ui_layout_direction Direction = LayoutDirection_TopDown;
- if (Interface->ActiveLayout)
- {
- Bounds = ui_ReserveBounds(Interface, Interface->ActiveLayout, Inset);
- Direction = Interface->ActiveLayout->FillDirection;
- }
- else
- {
- Bounds.Min.x = Interface->WindowBounds.Min.x;
- Bounds.Min.y = Interface->WindowBounds.Max.y;
- Bounds.Max.x = Interface->WindowBounds.Max.x;
- Bounds.Max.y = Interface->WindowBounds.Max.y;
-
- if (Inset)
- {
- Bounds.Min.x += Interface->Style.Margin.x;
- Bounds.Max.x -= Interface->Style.Margin.x;
- }
-
- Direction = LayoutDirection_TopDown;
- }
- return ui_PushLayout(Interface, Bounds, Direction, Name);
-}
-
-internal void
-ui_ExpandToFitChildren(ui_widget* Parent)
-{
- if (!ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) { return; }
-
- v2 Extents = { Parent->Bounds.Max.y, Parent->Bounds.Min.y };
- for (ui_widget* Child = Parent->ChildrenRoot; Child != 0; Child = Child->Next)
- {
- Extents.x = Min(Extents.x, Child->Bounds.Min.y);
- Extents.y = Max(Extents.y, Child->Bounds.Max.y);
- }
-
- switch(Parent->FillDirection)
- {
- case LayoutDirection_BottomUp:
- {
- Parent->Bounds.Max.y = Max(Extents.y + Parent->Margin.y, Parent->Bounds.Max.y);
- }break;
-
- case LayoutDirection_TopDown:
- {
- Parent->Bounds.Min.y = Min(Extents.x - Parent->Margin.y, Parent->Bounds.Min.y);
- }break;
-
- InvalidDefaultCase;
- }
-}
-
-static void
-ui_PopLayout(ui_interface* Interface, gs_string LayoutName)
-{
- Assert(Interface->ActiveLayout != 0);
-
- ui_widget* Layout = Interface->ActiveLayout;
-
- // NOTE(pjs): If this isn't true then a layout was opened without being closed
- // Go check for ui_PushLayout, ui_BeginDropdown, ui_BeginRow, etc that don't have
- // a corresponding ui_Pop/ui_End*
- Assert(StringsEqual(Layout->String, LayoutName));
-
- ui_ExpandToFitChildren(Layout);
-
- Interface->ActiveLayout = Interface->ActiveLayout->Parent;
-
- // NOTE(pjs): This indicates that the parent layout should
- // expand to fit the layout that we just popped
- if (Interface->ActiveLayout != 0 &&
- Interface->ActiveLayout->ChildrenHead == Layout)
- {
- ui_CommitBounds(Interface->ActiveLayout, Layout->Bounds);
- }
-}
-
-static ui_widget*
-ui_BeginRow(ui_interface* Interface, u32 ColumnsMax)
-{
- ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false);
- ui_WidgetCreateColumns(Layout, ColumnsMax, Interface);
- ui_WidgetInitUniformColumns(Layout);
- return Layout;
-}
-
-enum ui_column_size_rule
-{
- UIColumnSize_Fixed,
- UIColumnSize_Percent,
- UIColumnSize_Fill,
- UIColumnSize_MaxWidth,
-};
-
-struct ui_column_spec
-{
- ui_column_size_rule Rule;
- union
- {
- r32 Width;
- r32 Percent;
- };
-};
-
-static ui_widget*
-ui_BeginRow(ui_interface* Interface, u32 ColumnsMax, ui_column_spec* ColumnRules)
-{
- ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false);
- ui_WidgetCreateColumns(Layout, ColumnsMax, Interface);
-
- // First Pass, determine widths of each column, and how much space is left to be divided by the fill columns
- // If a size is specified, it is stored in Column->XMax
- r32 RowWidth = Rect2Width(Layout->Bounds);
- r32 RemainingSpace = RowWidth;
- u32 FillColumnsCount = 0;
- for (u32 i = 0; i < Layout->ColumnsCount; i++)
- {
- ui_column_spec Spec = ColumnRules[i];
- ui_column* Column = Layout->Columns + i;
-
- switch (Spec.Rule)
- {
- case UIColumnSize_Fixed:
- {
- Column->XMax = Spec.Width;
- RemainingSpace -= Column->XMax;
- }break;
-
- case UIColumnSize_Percent:
- {
- Column->XMax = Spec.Percent * RowWidth;
- RemainingSpace -= Column->XMax;
- }break;
-
- case UIColumnSize_Fill:
- {
- FillColumnsCount += 1;
- }break;
-
- case UIColumnSize_MaxWidth:
- {
- if (RemainingSpace >= Spec.Width)
- {
- Column->XMax = Spec.Width;
- }
- else
- {
- Column->XMax = RemainingSpace;
- }
- RemainingSpace -= Column->XMax;
- }break;
-
- InvalidDefaultCase;
- }
- }
-
- r32 FillColumnWidth = RemainingSpace / FillColumnsCount;
-
- // Second Pass, specify the actual XMin and XMax of each column
- r32 ColumnStartX = Layout->Bounds.Min.x;
- for (u32 i = 0; i < Layout->ColumnsCount; i++)
- {
- ui_column_spec Spec = ColumnRules[i];
- ui_column* Column = Layout->Columns + i;
-
- r32 ColumnWidth = 0;
- switch (Spec.Rule)
- {
- case UIColumnSize_Fixed:
- case UIColumnSize_Percent:
- case UIColumnSize_MaxWidth:
- {
- ColumnWidth = Column->XMax;
- }break;
-
- case UIColumnSize_Fill:
- {
- ColumnWidth = FillColumnWidth;
- }break;
- }
-
- Column->XMin = ColumnStartX ;
- Column->XMax = Column->XMin + Max(0, ColumnWidth);
- ColumnStartX = Column->XMax;
- }
-
- return Layout;
-}
-
-static void
-ui_EndRow(ui_interface* Interface)
-{
- ui_PopLayout(Interface, MakeString("Row"));
-}
-
-static rect2
-ui_LayoutRemaining(ui_widget Layout)
-{
- rect2 Result = Layout.Bounds;
- Result.Max.y = Layout.RowYAt;
- return Result;
-}
-
-// Widgets
-
-internal ui_eval_result
-ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
-{
- ui_eval_result Result = {};
-
- Widget->Bounds = Bounds;
- SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Widget);
- Interface->ActiveLayout->ChildCount += 1;
- ui_CommitBounds(Widget->Parent, Widget->Bounds);
-
- if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) &&
- PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
- {
- if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
- {
- if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id))
- {
- Result.Clicked = true;
- Interface->ActiveWidget = Widget->Id;
- }
- }
-
- if (Interface->HotWidget.ZIndex == 0 ||
- Interface->HotWidget.ZIndex <= Widget->Id.ZIndex)
- {
- Interface->HotWidget = Widget->Id;
- Interface->HotWidgetFramesSinceUpdate = 0;
- }
- }
- else
- {
- if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) &&
- MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
- {
- Interface->ActiveWidget = {};
- }
- }
-
- if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id))
- {
- // click & drag
- if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState))
- {
- Result.Held = true;
- Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos;
- }
-
- if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) &&
- Interface->TempInputString.Length > 0)
- {
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
-
- for (u32 i = 0; i < Interface->TempInputString.Length; i++)
- {
- if (Interface->TempInputString.Str[i] == '\b' &&
- State->EditString.Length > 0)
- {
- State->EditString.Length -= 1;
- }
- else
- {
- OutChar(&State->EditString, Interface->TempInputString.Str[i]);
- }
- }
- }
- }
-
-#if 0
- // if you can click it
- if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable))
- {
- // updating hot widget, and handling mouse clicks
- if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) &&
- PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
- {
- if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id) && MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
- {
- Result.Clicked = true;
- Interface->ActiveWidget = Widget->Id;
- }
-
- Interface->HotWidget = Widget->Id;
- }
-
- // click and drag
- if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState) &&
- PointIsInRect(Widget->Bounds, Interface->Mouse.DownPos))
- {
- Result.Held = true;
- Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos;
- }
-
- // if this is the active widget (its been clicked)
- if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id))
- {
- // if you can select it
- if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Selectable))
- {
- //
- if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState) &&
- !PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
- {
- Interface->ActiveWidget = {};
- }
-
- if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) &&
- Interface->TempInputString.Length > 0)
- {
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
-
- // TODO(pjs): Backspace?
- for (u32 i = 0; i < Interface->TempInputString.Length; i++)
- {
- if (Interface->TempInputString.Str[i] == '\b')
- {
- State->EditString.Length -= 1;
- }
- else
- {
- OutChar(&State->EditString, Interface->TempInputString.Str[i]);
- }
- }
- }
- }
- else if (MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState))
- {
- Interface->ActiveWidget = {};
- }
- }
- }
-#endif
-
- Assert(Widget->Parent != 0);
- return Result;
-}
-
-internal ui_eval_result
-ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget)
-{
- ui_widget* Layout = Interface->ActiveLayout;
- rect2 Bounds = ui_ReserveBounds(Interface, Layout, true);
- return ui_EvaluateWidget(Interface, Widget, Bounds);
-}
-
-//
-// Drawing Functions
-//
-
-static r32
-ui_GetTextLineHeight(ui_interface Interface)
-{
- r32 Result = Interface.Style.Font->PixelHeight + (2 * Interface.Style.Margin.y);
- return Result;
-}
-
-static void
-ui_FillRect(ui_interface* Interface, rect2 Bounds, v4 Color)
-{
- PushRenderQuad2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, Color);
-}
-
-static void
-ui_OutlineRect(ui_interface* Interface, rect2 Bounds, r32 Thickness, v4 Color)
-{
- PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, Thickness, Color);
-}
-
-internal void
-ui_Label(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left)
-{
- DEBUG_TRACK_FUNCTION;
- ui_widget* Widget = ui_CreateWidget(Interface, String);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
- ui_EvaluateWidget(Interface, Widget, Bounds);
-}
-
-internal void
-ui_Label(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left)
-{
- DEBUG_TRACK_FUNCTION;
- ui_widget* Widget = ui_CreateWidget(Interface, String);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
- ui_EvaluateWidget(Interface, Widget);
-}
-
-internal void
-ui_TextEntrySetFlags(ui_widget* Widget, gs_string EditString)
-{
- Widget->String = EditString;
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Selectable);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Typable);
-}
-
-internal bool
-ui_TextEntry(ui_interface* Interface, gs_string Identifier, gs_string* Value)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, Identifier);
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
- if (!State)
- {
- State = ui_CreateRetainedState(Interface, Widget);
- }
- PrintF(&State->EditString, "%S", *Value);
-
- ui_TextEntrySetFlags(Widget, State->EditString);
-
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
- bool StringEdited = !StringsEqual(*Value, State->EditString);
- PrintF(Value, "%S", State->EditString);
-
- return StringEdited;
-}
-
-internal u64
-ui_TextEntryU64(ui_interface* Interface, gs_string String, u64 CurrentValue)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, String);
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
- if (!State)
- {
- State = ui_CreateRetainedState(Interface, Widget);
- PrintF(&State->EditString, "%u", CurrentValue);
- }
- ui_TextEntrySetFlags(Widget, State->EditString);
-
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
- parse_uint_result ParseResult = ValidateAndParseUInt(State->EditString.ConstString);
- u64 ValueResult = CurrentValue;
- if (ParseResult.Success)
- {
- ValueResult = ParseResult.Value;
- }
- return ValueResult;
-}
-
-internal r64
-ui_TextEntryR64(ui_interface* Interface, gs_string String, r64 CurrentValue)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, String);
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
- if (!State)
- {
- State = ui_CreateRetainedState(Interface, Widget);
- PrintF(&State->EditString, "%f", CurrentValue);
- }
- ui_TextEntrySetFlags(Widget, State->EditString);
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
- parse_float_result ParseResult = ValidateAndParseFloat(State->EditString.ConstString);
- r64 ValueResult = CurrentValue;
- if (ParseResult.Success)
- {
- ValueResult = ParseResult.Value;
- }
- return ValueResult;
-}
-
-internal ui_widget*
-ui_CreateButtonWidget(ui_interface* Interface, gs_string Text)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, Text);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
- return Widget;
-}
-
-internal b32
-ui_Button(ui_interface* Interface, gs_string Text)
-{
- ui_widget* Widget = ui_CreateButtonWidget(Interface, Text);
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
- return Result.Clicked;
-}
-
-internal b32
-ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds)
-{
- ui_widget* Widget = ui_CreateButtonWidget(Interface, Text);
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
- return Result.Clicked;
-}
-
-struct list_item_colors
-{
- v4 Hover;
- v4 Selected;
- v4 BGColor;
-};
-
-inline v4
-ui_GetListItemBGColor(interface_config Style, u32 ElementIndex)
-{
- v4 Result = Style.ListBGColors[ElementIndex % LIST_BG_COLORS_COUNT];
- return Result;
-}
-
-static list_item_colors
-ui_GetListItemColors(ui_interface* Interface, u32 ListItemIndex)
-{
- list_item_colors Result = {};
- Result.Hover = Interface->Style.ListBGHover;
- Result.Selected = Interface->Style.ListBGSelected;
- Result.BGColor = ui_GetListItemBGColor(Interface->Style, ListItemIndex);
- return Result;
-}
-
-static b32
-ui_ListButton(ui_interface* Interface, gs_string Text, rect2 Bounds, u32 ListItemIndex)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, Text);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
- // TODO(pjs): Reimplement alternating color backgrounds
- Widget->Bounds = Bounds;
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
- return Result.Clicked;
-}
-
-static b32
-ui_LayoutListButton(ui_interface* Interface, gs_string Text, u32 ListItemIndex)
-{
- // TODO(pjs): Reimplement alternating colors
- return ui_Button(Interface, Text);
-}
-
-internal bool
-ui_EvaluateDropdown(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult)
-{
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
- if (!State)
- {
- State = ui_CreateRetainedState(Interface, Widget);
- }
-
- if (EvalResult.Clicked)
- {
- State->Value = !State->Value;
- }
-
- if (State->Value)
- {
- ui_widget ParentLayout = *Interface->ActiveLayout;
-
- r32 SpaceAbove = Interface->WindowBounds.Max.y - Widget->Bounds.Max.y;
- r32 SpaceBelow = Widget->Bounds.Min.y - Interface->WindowBounds.Min.y;
- ui_layout_direction Direction = LayoutDirection_TopDown;
- rect2 MenuBounds = {};
-
- if (SpaceAbove > SpaceBelow)
- {
- r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y;
- Direction = LayoutDirection_BottomUp;
- MenuBounds = rect2{
- v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, Widget->Bounds.Max.y },
- v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, ParentLayoutMaxY }
- };
- }
- else
- {
- r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y;
- Direction = LayoutDirection_TopDown;
- MenuBounds = rect2{
- v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, ParentLayoutMinY },
- v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, Widget->Bounds.Min.y }
- };
- }
-
- ui_widget* Layout = ui_PushOverlayLayout(Interface, MenuBounds, Direction, MakeString("DropdownLayout"));
- Layout->Margin.y = 0;
- Layout->WidgetReference = Widget->Id;
- ui_WidgetClearFlag(Layout, UIWidgetFlag_DrawOutline);
- ui_WidgetSetChildrenPopover(Layout);
- }
-
- return State->Value;
-}
-
-internal bool
-ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, Text);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
-
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
- return ui_EvaluateDropdown(Interface, Widget, Result);
-}
-
-internal bool
-ui_BeginDropdown(ui_interface* Interface, gs_string Text)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, Text);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
- return ui_EvaluateDropdown(Interface, Widget, Result);
-}
-
-internal void
-ui_EndDropdown(ui_interface* Interface)
-{
- ui_widget* Layout = Interface->ActiveLayout;
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->WidgetReference);
- if (State)
- {
- if (State->Value)
- {
- ui_PopLayout(Interface, MakeString("DropdownLayout"));
- }
- }
-}
-
-internal r32
-ui_EvaluateRangeSlider(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult, r32 Value, r32 MinValue, r32 MaxValue)
-{
- r32 NewValue = Value;
- ui_widget_retained_state* State = ui_GetOrCreateRetainedState(Interface, Widget);
-
- if (EvalResult.Clicked)
- {
- State->InitialValueR32 = Value;
- }
-
- if (EvalResult.Held)
- {
- r32 Percent = (Interface->Mouse.Pos.x - Widget->Bounds.Min.x) / Rect2Width(Widget->Bounds);
- NewValue = LerpR32(Percent, MinValue, MaxValue);
- }
-
- NewValue = Clamp(MinValue, NewValue, MaxValue);
- Widget->FillPercent = RemapR32(NewValue, MinValue, MaxValue, 0, 1);
- return NewValue;
-}
-
-internal ui_widget*
-ui_CreateRangeSliderWidget(ui_interface* Interface, gs_string Text, r32 Value)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, Text);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
- Widget->String = PushStringF(Interface->PerFrameMemory, 128, "%f", Value);
- InterfaceAssert(Interface->PerFrameMemory);
- return Widget;
-}
-
-internal r32
-ui_RangeSlider(ui_interface* Interface, gs_string Text, r32 Value, r32 ValueMin, r32 ValueMax)
-{
- ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value);
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
- return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax);
-
-}
-
-internal r32
-ui_RangeSlider(ui_interface* Interface, gs_string Text, rect2 Bounds, r32 Value, r32 ValueMin, r32 ValueMax)
-{
- ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value);
- ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
- return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax);
-}
-
-internal bool
-ui_Toggle(ui_interface* Interface, gs_string Text, bool Value)
-{
- ui_widget* Widget = ui_CreateWidget(Interface, Text);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill);
- ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
- ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget);
-
- bool Result = Eval.Clicked ? !Value : Value;
- Widget->FillPercent = Result ? 1.0f : 0.0f;
- return Result;
-}
-
-internal void
-ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 ElementCount)
-{
- if (ElementCount < ViewportRows)
- {
- ViewportRows = ElementCount;
- }
-
- ui_column_spec ColumnRules[] = {
- { UIColumnSize_Fixed, 32 },
- { UIColumnSize_Fill, 0 },
- };
- ui_widget* Layout = ui_BeginRow(Interface, 2, ColumnRules);
- ui_WidgetClearFlag(Layout, UIWidgetFlag_ExpandsToFitChildren);
-
- ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->Id);
- if (!State)
- {
- State = ui_CreateRetainedState(Interface, Layout);
- State->InitialValueR32 = 1.0f;
- }
-
- r32 LayoutHeight = Layout->RowHeight * ViewportRows;
- switch (Layout->Parent->FillDirection)
- {
- case LayoutDirection_TopDown:
- {
- Layout->Bounds.Min.y = Layout->Bounds.Max.y - LayoutHeight;
- }break;
-
- case LayoutDirection_BottomUp:
- {
- Layout->Bounds.Max.y = Layout->Bounds.Min.y + LayoutHeight;
- }break;
-
- InvalidDefaultCase;
- }
-
- // Create the scroll bar
- //
- // TODO(pjs): Maybe make this a vertical slider widget?
-
- ui_widget* SliderRegion = ui_CreateWidget(Interface, MakeString("Slider"));
- ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawOutline);
- ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawVerticalFill);
- ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawFillAsHandle);
-
- ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_Clickable);
-
- rect2 SliderBounds = ui_ReserveBounds(Interface, Layout, true);
- SliderBounds.Min.y = Layout->Bounds.Min.y + Layout->Margin.y;
- SliderBounds.Max.y = Layout->Bounds.Max.y - Layout->Margin.y;
-
- ui_eval_result SliderEval = ui_EvaluateWidget(Interface, SliderRegion, SliderBounds);
- if (SliderEval.Clicked || SliderEval.Held)
- {
- r32 Percent = (Interface->Mouse.Pos.y - SliderRegion->Bounds.Min.y) / Rect2Height(SliderRegion->Bounds);
- State->InitialValueR32 = Clamp01(Percent);
- }
- SliderRegion->FillPercent = State->InitialValueR32;
-
- // Create the viewport that offsets list contents (and at render time determines what is visible)
- //
- ui_widget* ViewportLayout = ui_PushLayout(Interface, MakeString("Contents"));
- ui_WidgetClearFlag(ViewportLayout, UIWidgetFlag_ExpandsToFitChildren);
-
- ViewportLayout->Bounds.Min.y = SliderBounds.Min.y;
- ViewportLayout->Bounds.Max.y = SliderBounds.Max.y;
-
- s32 ScrollableElements = Max(0, ElementCount - ViewportRows);
- ui_widget_retained_state* ViewportState = ui_GetOrCreateRetainedState(Interface, ViewportLayout);
- ViewportState->ChildrenDrawOffset.x = 0;
- ViewportState->ChildrenDrawOffset.y = ((1.0f - State->InitialValueR32) * (r32)(ScrollableElements)) * ViewportLayout->RowHeight;
-}
-
-internal void
-ui_EndList(ui_interface* Interface)
-{
- // Pop the Viewport Layout
- ui_PopLayout(Interface, MakeString("Contents"));
- // Pop the actual list layout
- ui_EndRow(Interface);
-}
-
-internal void
-ui_BeginMousePopup(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Text)
-{
- rect2 FollowMouseBounds = Rect2Translate(Bounds, Interface->Mouse.Pos);
- ui_widget* Layout = ui_PushOverlayLayout(Interface, FollowMouseBounds, FillDir, MakeString("MousePopup"));
- ui_WidgetSetFlag(Layout, UIWidgetFlag_DrawBackground);
-}
-
-internal void
-ui_EndMousePopup(ui_interface* Interface)
-{
- ui_PopLayout(Interface, MakeString("MousePopup"));
-}
-
-//
-internal void
-ui_BeginLabelRow(ui_interface* Interface, gs_string Label, u32 Count = 2)
-{
- ui_BeginRow(Interface, Count);
- ui_Label(Interface, Label);
-}
-
-internal bool
-ui_LabeledToggle(ui_interface* Interface, gs_string Label, bool Value)
-{
- ui_BeginLabelRow(Interface, Label);
- bool Result = ui_Toggle(Interface, Label, Value);
- ui_EndRow(Interface);
- return Result;
-}
-
-internal r32
-ui_LabeledRangeSlider(ui_interface* Interface, gs_string Label, r32 Value, r32 ValueMin, r32 ValueMax)
-{
- ui_BeginLabelRow(Interface, Label);
- r32 Result = ui_RangeSlider(Interface, Label, Value, ValueMin, ValueMax);
- ui_EndRow(Interface);
- return Result;
-}
-
-internal void
-ui_LabeledTextEntry(ui_interface* Interface, gs_string Label, gs_string* Value)
-{
- ui_BeginLabelRow(Interface, Label);
- ui_TextEntry(Interface, Label, Value);
- ui_EndRow(Interface);
-}
-
-
-internal u64
-ui_LabeledTextEntryU64(ui_interface* Interface, gs_string Label, u32 Value)
-{
- ui_BeginLabelRow(Interface, Label);
- u64 Result = ui_TextEntryU64(Interface, Label, Value);
- ui_EndRow(Interface);
- return Result;
-}
-
-internal bool
-ui_BeginLabeledDropdown(ui_interface* Interface, gs_string Label, gs_string DropdownValue)
-{
- ui_BeginLabelRow(Interface, Label);
- return ui_BeginDropdown(Interface, DropdownValue);
-}
-
-internal void
-ui_EndLabeledDropdown(ui_interface* Interface)
-{
- ui_EndDropdown(Interface);
- ui_EndRow(Interface);
-}
-
-internal ui_interface
-ui_InterfaceCreate(context Context, interface_config Style, gs_memory_arena* Permanent)
-{
- ui_interface Result = {0};
- Result.Style = Style;
-
- gs_file FontFile = ReadEntireFile(Context.ThreadContext.FileHandler, ConstString("data/Anonymous Pro.ttf"));
- Result.Style.Font = PushStruct(Permanent, bitmap_font);
- *Result.Style.Font = TextFont_Create(FontFile, 512, Style.FontSize, Context, Permanent);
-
- Result.Style.RowHeight = ui_GetTextLineHeight(Result) + (2 * Result.Style.Margin.y);
-
- Result.WidgetsCountMax = 4096;
- Result.Widgets = PushArray(Permanent, ui_widget, Result.WidgetsCountMax);
- Result.PerFrameMemory = PushStruct(Permanent, gs_memory_arena);
- *Result.PerFrameMemory = CreateMemoryArena(Context.ThreadContext.Allocator);
- InterfaceAssert(Result.PerFrameMemory);
-
- Result.Permanent = Permanent;
-
- return Result;
-}
-
-#define INTERFACE_H
-#endif // INTERFACE_H
\ No newline at end of file
diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h
index 4345524..6530ccd 100644
--- a/src/app/patterns/blumen_patterns.h
+++ b/src/app/patterns/blumen_patterns.h
@@ -7,383 +7,73 @@
#define FLOWER_COLORS_COUNT 12
-pixel FlowerAColors[FLOWER_COLORS_COUNT] = {
- { 232,219,88 },
- { 232,219,88 },
- { 232,219,88 },
- { 147,75,176 },
- { 193,187,197 },
- { 223,190,49 },
- { 198,76,65 },
- { 198,76,65 },
- { 198,76,65 },
- { 226,200,17 },
- { 116,126,39 },
- { 61,62,31 }
-};
-pixel FlowerBColors[FLOWER_COLORS_COUNT] = {
- { 62,56,139 },
- { 93,87,164 },
- { 93,87,164 },
- { 93,87,164 },
- { 155,140,184 },
- { 191,201,204 },
- { 45,31,116 },
- { 201,196,156 },
- { 191,175,109 },
- { 186,176,107 },
- { 89,77,17 },
- { 47,49,18 },
-};
-pixel FlowerCColors[FLOWER_COLORS_COUNT] = {
- { 220,217,210 },
- { 220,217,210 },
- { 220,217,210 },
- { 225,193,110 },
- { 225,193,110 },
- { 227,221,214 },
- { 227,221,214 },
- { 230,218,187 },
- { 230,218,187 },
- { 172,190,211 },
- { 172,190,211 },
- { 172,190,211 },
-};
-
-internal pixel
-PixelMix(r32 T, pixel A, pixel B)
+internal void
+Pattern_None(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
{
- pixel Result = {
- LerpU8(T, A.R, B.R),
- LerpU8(T, A.G, B.G),
- LerpU8(T, A.B, B.B),
- };
- return Result;
-}
-
-internal pixel
-GetColor(pixel* Colors, u32 ColorsCount, r32 Percent)
-{
- Percent = Clamp01(Percent);
-
- u32 LowerIndex = Percent * ColorsCount;
-
- u32 HigherIndex = LowerIndex + 1;
- if (HigherIndex >= ColorsCount) HigherIndex = 0;
-
- r32 LowerPercent = (r32)LowerIndex / (r32)ColorsCount;
- r32 StepPercent = 1.f / (r32)ColorsCount;
- r32 PercentLower = (Percent - LowerPercent) / StepPercent;
-
- pixel Result = PixelMix(PercentLower, Colors[LowerIndex], Colors[HigherIndex]);
-
- return Result;
+ // just here so you can fade in from black
}
internal void
-SolidColorPattern(led_buffer* Leds, pixel Color)
+Pattern_AltBloomMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
{
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
+ v3 SphereCenter = Assembly.Center - v3{0, -150, 0};
+ r32 SphereRadius = Time;
+ r32 SphereBrightness = 1;
+
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
{
- Leds->Colors[LedIndex] = Color;
+ v3 P = Leds->Positions[LedIndex].xyz;
+ r32 Sphere = SDF_SphereNormalized(P, SphereCenter, SphereRadius);
+ Sphere = Clamp01(-Sphere);
+ Leds->Colors[LedIndex] = V4ToRGBPixel(WhiteV4 * Sphere);
}
}
internal void
-Pattern_Blue(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+Pattern_HueShift(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
{
- pixel Blue = pixel{0, 0, 255};
- SolidColorPattern(Leds, Blue);
-}
-
-internal void
-Pattern_Green(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- pixel Green = pixel{0, 255, 0};
- SolidColorPattern(Leds, Green);
-}
-
-internal void
-Pattern_FlowerColors(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- r32 CycleTime = 10;
- r32 CyclePercent = ModR32(Time, CycleTime) / CycleTime;
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
- pixel CA = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, CyclePercent);
- pixel CB = GetColor(FlowerAColors, FLOWER_COLORS_COUNT, 1.0f - CyclePercent);
-
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
- {
- v4 P = Leds->Positions[LedIndex];
- r32 Pct = (Abs(ModR32(P.y, 150) / 150) + CycleTime) * PiR32;
-
- r32 APct = RemapR32(SinR32(Pct), -1, 1, 0, 1);
- Leds->Colors[LedIndex] = PixelMix(APct, CA, CB);
- }
-}
-
-internal void
-TestPatternOne(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- led_strip_list BlumenStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("assembly"), ConstString("Blumen Lumen"), Transient);
- led_strip_list RadiaStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("assembly"), ConstString("Radialumia"), Transient);
-
- for (u32 i = 0; i < BlumenStrips.Count; i++)
- {
- u32 StripIndex = BlumenStrips.StripIndices[i];
- v2_strip StripAt = Assembly.Strips[StripIndex];
-
- for (u32 j = 0; j < StripAt.LedCount; j++)
- {
- u32 LedIndex = StripAt.LedLUT[j];
- Leds->Colors[LedIndex] = { 255, 0, 0 };
-
- }
- }
-
- for (u32 i = 0; i < RadiaStrips.Count; i++)
- {
- u32 StripIndex = RadiaStrips.StripIndices[i];
- v2_strip StripAt = Assembly.Strips[StripIndex];
-
- for (u32 j = 0; j < StripAt.LedCount; j++)
- {
- u32 LedIndex = StripAt.LedLUT[j];
- Leds->Colors[LedIndex] = { 0, 255, 0 };
- }
- }
-}
-
-internal void
-TestPatternTwo(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- r32 PeriodicTime = (Time / PiR32) * 2;
-
- r32 ZeroOneSin = (SinR32(PeriodicTime) * .5f) + .5f;
- r32 ZeroOneCos = (CosR32(PeriodicTime) * .5f) + .5f;
- pixel Color = { (u8)(ZeroOneSin * 255), 0, (u8)(ZeroOneCos * 255) };
-
- v4 Center = v4{0, 0, 0, 1};
- r32 ThetaZ = Time / 2;
- v4 Normal = v4{CosR32(ThetaZ), 0, SinR32(ThetaZ), 0}; // NOTE(Peter): dont' need to normalize. Should always be 1
- v4 Right = V4Cross(Normal, v4{0, 1, 0, 0});
-
- v4 FrontCenter = Center + (Normal * 25);
- v4 BackCenter = Center - (Normal * 25);
-
- r32 OuterRadiusSquared = 1000000;
- r32 InnerRadiusSquared = 0;
-
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
- {
- v4 Position = Leds->Positions[LedIndex];
-
- v4 ToFront = Position + FrontCenter;
- v4 ToBack = Position + BackCenter;
-
- r32 ToFrontDotNormal = V4Dot(ToFront, Normal);
- r32 ToBackDotNormal = V4Dot(ToBack, Normal);
-
- ToFrontDotNormal = Clamp01(ToFrontDotNormal * 1000);
- ToBackDotNormal = Clamp01(ToBackDotNormal * 1000);
-
- r32 SqDistToCenter = V4MagSquared(Position);
- if (SqDistToCenter < OuterRadiusSquared && SqDistToCenter > InnerRadiusSquared)
- {
- if (XOR(ToFrontDotNormal > 0, ToBackDotNormal > 0))
- {
- Leds->Colors[LedIndex] = Color;
- }
- else
- {
- //Leds->Colors[LedIndex] = {};
- }
- }
- else
- {
- //Leds->Colors[LedIndex] = {};
- }
- }
-}
-
-internal void
-TestPatternThree(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- v4 GreenCenter = v4{0, 0, 150, 1};
- r32 GreenRadius = Abs(SinR32(Time)) * 200;
-
- v4 TealCenter = v4{0, 0, 150, 1};
- r32 TealRadius = Abs(SinR32(Time + 1.5)) * 200;
-
- r32 FadeDist = 35;
-
-
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
- {
- v4 LedPosition = Leds->Positions[LedIndex];
- u8 Red = 0;
- u8 Green = 0;
- u8 Blue = 0;
-
- r32 GreenDist = Abs(V4Mag(LedPosition - GreenCenter) - GreenRadius);
- r32 GreenBrightness = Clamp(0.f, FadeDist - Abs(GreenDist), FadeDist);
- Green = (u8)(GreenBrightness * 255);
-
- r32 TealDist = Abs(V4Mag(LedPosition - TealCenter) - TealRadius);
- r32 TealBrightness = Clamp(0.f, FadeDist - Abs(TealDist), FadeDist);
- Red = (u8)(TealBrightness * 255);
- Blue = (u8)(TealBrightness * 255);
-
- Leds->Colors[LedIndex].R = Red;
- Leds->Colors[LedIndex].B = Green;
- Leds->Colors[LedIndex].G = Green;
- }
-}
-
-v4 RGBToHSV(v4 In)
-{
- v4 Result = {};
-
- r32 Min = Min(In.r, Min(In.g, In.b));
- r32 Max = Max(In.r, Max(In.g, In.b));
-
-
- r32 V = Max;
- r32 Delta = Max - Min;
- r32 S = 0;
- r32 H = 0;
- if( Max != 0 )
- {
- S = Delta / Max;
-
- if( In.r == Max )
- {
- H = ( In.g - In.b ) / Delta; // between yellow & magenta
- }
- else if( In.g == Max )
- {
- H = 2 + ( In.b - In.r ) / Delta; // between cyan & yellow
- }
- else
- {
- H = 4 + ( In.r - In.g ) / Delta; // between magenta & cyan
- }
- H *= 60; // degrees
- if( H < 0 )
- H += 360;
- Assert(H);
- //if ( isNaN(h) )
- //H = 0;
- Result = v4{H, S, V, 1};
- }
- else
- {
- // r = g = b = 0
- // s = 0, v is undefined
- S = 0;
- H = -1;
- Result = v4{H, S, 1, 1};
- }
-
- return Result;
-}
-
-v4 HSVToRGB (v4 In)
-{
- float Hue = In.x;
- /*
-while (Hue > 360.0f) { Hue -= 360.0f; }
- while (Hue < 0.0f) { Hue += 360.0f; }
- */
- Hue = ModR32(Hue, 360.0f);
- if (Hue < 0) { Hue += 360.0f; }
- if (Hue == MinR32) { Hue = 0; }
- if (Hue == MaxR32) { Hue = 360; }
- Assert(Hue >= 0 && Hue < 360);
-
- float Sat = In.y;
- float Value = In.z;
-
- float hh, p, q, t, ff;
- long i;
- v4 Result = {};
- Result.a = In.a;
-
- if(Sat <= 0.0f) { // < is bogus, just shuts up warnings
- Result.r = Value;
- Result.g = Value;
- Result.b = Value;
- return Result;
- }
- hh = Hue;
- if(hh >= 360.0f) hh = 0.0f;
- hh /= 60.0f;
- i = (long)hh;
- ff = hh - i;
- p = Value * (1.0f - Sat);
- q = Value * (1.0f - (Sat * ff));
- t = Value * (1.0f - (Sat * (1.0f - ff)));
-
- switch(i) {
- case 0:
- {Result.r = Value;
- Result.g = t;
- Result.b = p;
- }break;
-
- case 1:
- {
- Result.r = q;
- Result.g = Value;
- Result.b = p;
- }break;
-
- case 2:
- {
- Result.r = p;
- Result.g = Value;
- Result.b = t;
- }break;
-
- case 3:
- {
- Result.r = p;
- Result.g = q;
- Result.b = Value;
- }break;
-
- case 4:
- {
- Result.r = t;
- Result.g = p;
- Result.b = Value;
- }break;
-
- case 5:
- default:
- {
- Result.r = Value;
- Result.g = p;
- Result.b = q;
- }break;
- }
-
- return Result;
-}
-
-internal void
-Pattern_HueShift(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
r32 Height = SinR32(Time) * 25;
r32 CycleLength = 5.0f;
r32 CycleProgress = FractR32(Time / CycleLength);
r32 CycleBlend = (SinR32(Time) * .5f) + .5f;
+#if 0
+ phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3];
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
+
+ v4 HSV = {};
+ if (CycleProgress < .25f)
+ {
+ r32 P = CycleProgress * 4;
+ HSV = V4Lerp(C0,
+ }
+ else if (CycleProgress >= .25f && CycleProgress < .5f)
+ {
+
+ }
+ else if (CycleProgress >= .5f && CycleProgress < .75f)
+ {
+
+ }
+ else if (CycleProgress >= .75f)
+ {
+
+ }
+#endif
+
v4 HSV = { CycleProgress * 360, 1, 1, 1 };
v4 RGB = HSVToRGB(HSV);
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
{
v4 Pos = Leds->Positions[LedIndex];
r32 Dist = Pos.y - Height;
@@ -401,26 +91,19 @@ Pattern_HueShift(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena*
}
}
-internal pixel
-V4ToRGBPixel(v4 C)
-{
- pixel Result = {};
- Result.R = (u8)(C.x * 255);
- Result.G = (u8)(C.y * 255);
- Result.B = (u8)(C.z * 255);
- return Result;
-}
-
internal void
-Pattern_HueFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+Pattern_Rainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
{
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
r32 HueBase = ModR32(Time * 50, 360);
r32 CycleLength = 5.0f;
r32 CycleProgress = FractR32(Time / CycleLength);
r32 CycleBlend = (SinR32(Time) * .5f) + .5f;
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
{
v4 Pos = Leds->Positions[LedIndex];
r32 Hue = HueBase + Pos.y + Pos.x;
@@ -432,65 +115,13 @@ Pattern_HueFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena*
}
internal void
-Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+Pattern_RadialRainbow(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
{
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
- {
- u32 I = LedIndex + 1;
- Leds->Colors[LedIndex] = {};
- if (I % 3 == 0)
- {
- Leds->Colors[LedIndex].R = 255;
- }
- else if (I % 3 == 1)
- {
- Leds->Colors[LedIndex].G = 255;
- }
- else if (I % 3 == 2)
- {
- Leds->Colors[LedIndex].B = 255;
- }
-
- }
-}
-
-internal r32
-PatternHash(r32 Seed)
-{
- return FractR32(Seed * 17.0 * FractR32(Seed * 0.3183099));
-}
-
-internal void
-Pattern_Spots(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- pixel ColorA = { 0, 255, 255 };
- pixel ColorB = { 255, 0, 255 };
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
- r32 Speed = .5f;
- Time *= Speed;
- r32 ScaleA = 2 * SinR32(Time / 5);
- r32 ScaleB = 2.4f * CosR32(Time / 2.5f);
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
- {
- v4 P = Leds->Positions[LedIndex];
- r32 V = P.y;
- r32 Noise = .3f * PatternHash(V);
- r32 ThetaY = (Leds->Positions[LedIndex].y / 10) + Time + Noise;
- r32 ThetaX = (Leds->Positions[LedIndex].x / 13) + Time + Noise;
- r32 Fade = (ScaleA * SinR32(ThetaY)) + (ScaleB * CosR32(3 * ThetaX));
- Fade = RemapClampedR32(Fade, -1, 1, 0, 1);
-
- Leds->Colors[LedIndex].R = (u8)LerpR32(Fade, ColorA.R, ColorB.R);
- Leds->Colors[LedIndex].G = (u8)LerpR32(Fade, ColorA.G, ColorB.G);
- Leds->Colors[LedIndex].B = (u8)LerpR32(Fade, ColorA.B, ColorB.B);
- }
-}
-
-internal void
-Pattern_LighthouseRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
v2 RefVector = V2Normalize(v2{ SinR32(Time), CosR32(Time) });
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
{
v2 Vector = v2{
Leds->Positions[LedIndex].x,
@@ -500,218 +131,27 @@ Pattern_LighthouseRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memo
r32 Angle = V2Dot(RefVector, Vector);
- v4 HSV = { (Angle * 30) + (Time * 10) + Leds->Positions[LedIndex].y, 1, 1, 1 };
+ v4 HSV = { (Angle * 30) + (Time * 10), 1, 1, 1 };
v4 RGB = HSVToRGB(HSV);
Leds->Colors[LedIndex] = V4ToRGBPixel(RGB);
}
}
-internal r32
-Smoothstep(r32 T)
-{
- r32 Result = (T * T * (3 - (2 * T)));
- return Result;
-}
-
internal void
-Pattern_SmoothGrowRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+Pattern_BasicFlowers(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
{
- r32 FillCycleTime = ModR32(Time, 7.0f) / 7.0f;
- r32 ColorCycleTime = ModR32(Time, 21.0f) / 21.0f;
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
- v4 HSV = { 0, 1, 1, 1 };
- for (u32 s = 0; s < Assembly.StripCount; s++)
- {
- v2_strip Strip = Assembly.Strips[s];
-
- v4 RGB0 = HSVToRGB(HSV);
- for (u32 l = 0; l < Strip.LedCount; l++)
- {
- u32 LedIndex = Strip.LedLUT[l];
- Leds->Colors[LedIndex] = V4ToRGBPixel(RGB0);
- }
-
- HSV.x += 15;
- }
-}
-
-internal void
-Pattern_GrowAndFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- r32 PercentCycle = ModR32(Time, 10) / 10;
- v4 HSV = { PercentCycle * 360, 1, 1, 1 };
- v4 RGB = HSVToRGB(HSV);
-
- r32 RefHeight = -100 + (Smoothstep(PercentCycle * 1.4f) * 400);
- r32 RefBrightness = 1.0f - Smoothstep(PercentCycle);
-
- for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
- {
- v4 P = Leds->Positions[LedIndex];
-
- v4 RgbFaded = v4{};
- if (P.y < RefHeight)
- {
- RgbFaded = RGB * RefBrightness;
- }
- Leds->Colors[LedIndex] = V4ToRGBPixel(RgbFaded);
- }
-}
-
-internal void
-Pattern_ColorToWhite(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- r32 FadeBottomBase = 50;
- r32 FadeTop = 125;
+ phrase_hue Hue = BLState->AssemblyColors[Assembly.AssemblyIndex % 3];
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
{
v2_strip Strip = Assembly.Strips[StripIndex];
-
- r32 FlowerSpread = .8f;
- r32 FlowerOffset = 0;
- if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center"))
- {
- FlowerOffset = 1;
- }
- else if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "right"))
- {
- FlowerOffset = 2;
- }
- FlowerOffset *= FlowerSpread;
-
- r32 PercentCycle = ModR32(Time + FlowerOffset, 10) / 10;
-
- r32 FadeBottom = FadeBottomBase + RemapR32(SinR32((PercentCycle * 4) * TauR32), -1, 1, -50, 50);
-
- v4 TopRGB = WhiteV4;
- pixel TopColor = V4ToRGBPixel(TopRGB);
-
- for (u32 i = 0; i < Strip.LedCount; i++)
- {
- u32 LedIndex = Strip.LedLUT[i];
- v4 P = Leds->Positions[LedIndex];
-
- pixel FinalColor = {};
- if (P.y > FadeTop)
- {
- FinalColor = TopColor;
- }
- else
- {
- r32 B = RemapR32(SinR32((P.y / 15.f) + (PercentCycle * TauR32)), -1, 1, .5f, 1.f);
- r32 HNoise = RemapR32(SinR32((P.y / 31.f) + (PercentCycle * TauR32)), -1, 1, -32.f, 32.f);
- v4 BottomRGB = HSVToRGB(v4{ (PercentCycle * 360) + HNoise, 1, B, 1 });
-
- if (P.y < FadeBottom)
- {
- FinalColor = V4ToRGBPixel(BottomRGB);
- }
- else if (P.y >= FadeBottom && P.y <= FadeTop)
- {
- r32 FadePct = RemapR32(P.y, FadeBottom, FadeTop, 0, 1);
- v4 MixRGB = V4Lerp(FadePct, BottomRGB, TopRGB);
- FinalColor = V4ToRGBPixel(MixRGB);
- }
- }
-
- Leds->Colors[LedIndex] = FinalColor;
- }
- }
-}
-
-internal void
-Pattern_FlowerColorToWhite(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- r32 FadeBottomBase = 50;
- r32 FadeTop = 125;
-
- for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
- {
- v2_strip Strip = Assembly.Strips[StripIndex];
-
-#if 0
- // All flowers same flower type
- pixel* Colors = &FlowerAColors[0];
- r32 FlowerSpread = .8f;
- r32 FlowerOffset = 0;
- if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center"))
- {
- FlowerOffset = 1;
- }
- else if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "right"))
- {
- FlowerOffset = 2;
- }
- FlowerOffset *= FlowerSpread;
-#else
- // Each flower different
- pixel* Colors = &FlowerAColors[0];
- r32 FlowerOffset = 0;
- if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center"))
- {
- Colors = &FlowerBColors[0];
- }
- else if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "right"))
- {
- Colors = &FlowerCColors[0];
- }
-#endif
- r32 PercentCycle = ModR32(Time + FlowerOffset, 10) / 10;
-
- r32 FadeBottom = FadeBottomBase + RemapR32(SinR32((PercentCycle * 4) * TauR32), -1, 1, -50, 50);
-
- for (u32 i = 0; i < Strip.LedCount; i++)
- {
- u32 LedIndex = Strip.LedLUT[i];
- v4 P = Leds->Positions[LedIndex];
-
- pixel FinalColor = {};
- r32 B = RemapR32(SinR32((P.y / 15.f) + (PercentCycle * TauR32)), -1, 1, .5f, 1.f);
- r32 HNoise = RemapR32(SinR32((P.y / 31.f) + (PercentCycle * TauR32)), -1, 1, 0.f, 1.f);
-
- pixel BottomColor = GetColor(Colors, FLOWER_COLORS_COUNT, (PercentCycle + HNoise) / 2);
-
- FinalColor = BottomColor;
-
- Leds->Colors[LedIndex] = FinalColor;
- }
- }
-}
-
-r32 TLastFrame = 0;
-pixel* FAC = &FlowerAColors[0];
-pixel* FBC = &FlowerBColors[0];
-pixel* FCC = &FlowerCColors[0];
-
-internal void
-Pattern_BasicFlowers(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
-{
- if (TLastFrame > Time)
- {
- pixel* Temp = FAC;
- FAC = FBC;
- FBC = FCC;
- FCC = Temp;
- }
- TLastFrame = Time;
-
- for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
- {
- v2_strip Strip = Assembly.Strips[StripIndex];
-
- // Each flower different
- pixel* Colors = FAC;
- if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "center"))
- {
- Colors = FBC;
- }
- else if (AssemblyStrip_HasTagValueSLOW(Strip, "flower", "right"))
- {
- Colors = FCC;
- }
-
r32 CycleT = ModR32(Time, 10) * 20;
for (u32 i = 0; i < Strip.LedCount; i++)
@@ -722,9 +162,718 @@ Pattern_BasicFlowers(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_ar
r32 T = ModR32(P.y + CycleT, 200) / 200.f;
T = Clamp01(T);
- Leds->Colors[LedIndex] = GetColor(Colors, FLOWER_COLORS_COUNT, T);
+ v4 Color = {};
+ if (T < 0.5f)
+ {
+ Color = V4Lerp(T * 2, C0, C1);
+ }
+ else
+ {
+ Color = V4Lerp((T - 0.5f) * 2, C1, C2);
+ }
+ Leds->Colors[LedIndex] = V4ToRGBPixel(Color);
}
}
}
+
+internal void
+Pattern_WavyOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2)
+{
+ r32 Top = 120 + (SinR32(Time) * 10);
+ r32 Mid = 70 + (CosR32(Time * 2.13) * 20);
+ r32 Bot = 0;
+
+ r32 TopD = Top - Mid;
+ r32 BotD = Mid - Bot;
+ r32 MidD = Min(TopD, BotD);
+
+ //r32 MaxFadeDistance = 10;
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v3 P = Leds->Positions[LedIndex].xyz;
+
+ r32 PercentTop = Clamp01(1.0f - ((Top - P.y) / TopD));
+
+ r32 PercentMid = Clamp01(1.0f - Abs(P.y - Mid) / MidD);
+ r32 N = Noise3D((P / 17) + v3{Time, -Time, 0});
+ N = Clamp01(N) * 2;
+ N = Smoothstep(N);
+ N *= N;
+ N = Smoothstep(N);
+ N *= 1.0f - PowR32(1.0f - PercentMid, 4);
+ PercentMid = Clamp01(PercentMid + N);
+
+ r32 PercentBot = Clamp01(1.0f - ((P.y - Bot) / BotD));
+
+ v4 TopC = (C0 * PercentTop);
+ v4 MidC = (C1 * PercentMid);
+ v4 BotC = (C2 * PercentBot);
+
+ v4 C = {};
+ if (PercentTop > PercentMid && PercentTop > PercentBot)
+ {
+ C = C0;
+ }
+ else if (PercentMid > PercentBot)
+ {
+ C = C1;
+ }
+ else
+ {
+ C = C2;
+ }
+
+ r32 ScaleFactor = PercentTop + PercentMid + PercentBot;
+ C = (TopC + MidC + BotC) / ScaleFactor;
+ Leds->Colors[LedIndex] = V4ToRGBPixel(C);
+ }
+}
+
+internal void
+Pattern_Wavy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
+ phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
+
+ Pattern_WavyOptions(Leds, Range, Assembly, Time, Transient, UserData, 1, C0, C1, C2);
+}
+
+internal void
+Pattern_PatchyOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2)
+{
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+
+ r32 BaseGA = 50.000f * (1 / Granularity);
+ r32 BaseGB = 135.20f * (1 / Granularity);
+ r32 BaseGC = 260.74f * (1 / Granularity);
+
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v4 P = Leds->Positions[LedIndex];
+ r32 LedRange = 300.0f;
+ r32 ScaleFactor = 1.0f / LedRange;
+ v3 Pp = P.xyz + v3{150, 100, 0};
+
+ r32 NoiseA = Noise3D((Pp / BaseGA) + v3{0, 0, Time});
+ NoiseA = PowR32(NoiseA, 3);
+ NoiseA = Smoothstep(NoiseA);
+
+ r32 NoiseB = Noise3D((Pp / BaseGB) + v3{Time * 0.5f, 0, 0});
+ NoiseB = PowR32(NoiseB, 3);
+ NoiseB = Smoothstep(NoiseB);
+
+#if 1
+ r32 NoiseC = Noise3D((Pp / BaseGC) + v3{Time * 0.5f, 0, 0});
+ NoiseC = PowR32(NoiseC, 3);
+ NoiseC = Smoothstep(NoiseC);
+#else
+ r32 NoiseC = 0;
+#endif
+
+ v4 C = (C0 * NoiseA) + (C1 * NoiseB) + (C2 * NoiseC);
+ C /= (NoiseA + NoiseB + NoiseC);
+ Leds->Colors[LedIndex] = V4ToRGBPixel(C);
+ }
+}
+
+internal void
+Pattern_Patchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
+ Time = Time * BLState->PatternSpeed;
+ Pattern_PatchyOptions(Leds, Range, Assembly, Time, Transient, UserData, 5, C0, C1, C2);
+}
+
+internal r32
+Leafy_BandSDF(v3 P, gs_random_series* Random, r32 Time)
+{
+ r32 MinBandThickness = 5;
+ r32 MaxBandThickness = 10;
+ r32 MaxTransitionPeriod = 120.0f;
+
+ r32 BandTransitionPeriod = NextRandomUnilateral(Random) * MaxTransitionPeriod;
+ r32 BandTransitionBias = (1 - Clamp(0, (Time / (MaxTransitionPeriod / 2)), 0.7f)); // approaches 0.5 over time
+ BandTransitionPeriod *= BandTransitionBias;
+
+ r32 BandPercent = ModR32(Time, BandTransitionPeriod) / BandTransitionPeriod;
+ BandPercent = Smoothstep(BandPercent);
+ r32 BandY = -150 + (BandPercent * 290);
+
+ r32 ThickRand = NextRandomUnilateral(Random);
+ // 1 - 4((ThickRand - .5)^2) - distribution curve
+ ThickRand = 1.0f - ((4 * PowR32(ThickRand, 2)) - (4 * ThickRand) + 1);
+ r32 BandThickness = MinBandThickness + (ThickRand * (MaxBandThickness - MinBandThickness));
+
+ // BandBrightness = 1 - ((2x - 1) ^ 8) where x is BandPercent
+ r32 BandBrightness = 1.0f - PowR32((2 * BandPercent) - 1, 8);
+ BandBrightness *= RemapR32(NextRandomUnilateral(Random), 0, 1, .25f, 1);
+ r32 Result = 1 - Clamp01(Abs(P.y - BandY) / BandThickness);
+ Result *= BandBrightness;
+ return Result;
+}
+
+internal void
+Pattern_Leafy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed * .3f;
+
+ phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
+
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v4 P = Leds->Positions[LedIndex];
+
+ v4 C = {};
+ r32 B = 0;
+
+ // NOTE(PS): initializing the Random seed inside the Led Loop
+ // so that the bands are consistently calculated for each led
+ // ie. each time you calculate a band, the random numbers requested
+ // will always be the same
+ gs_random_series Random = InitRandomSeries(24601);
+ u32 BandCount = 25;
+ for (u32 Band = 0; Band < BandCount; Band++)
+ {
+ B += Leafy_BandSDF(P.xyz, &Random, Time);
+ }
+ B = Clamp01(B);
+
+ C = WhiteV4 * B;
+ Leds->Colors[LedIndex] = V4ToRGBPixel(C);
+ }
+}
+
+internal void
+Pattern_LeafyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
+ phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
+
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v4 P = Leds->Positions[LedIndex];
+ v3 Pp = P.xyz + v3{150, 100, 0};
+
+ r32 NoiseA = Fbm3D((Pp / 18), Time * 0.25f);
+ NoiseA = Smoothstep(NoiseA);
+
+ r32 NoiseB = Noise3D((Pp / 35) + v3{0, 0, Time * 0.5f});
+ NoiseB = PowR32(NoiseB, 3);
+ NoiseB = Smoothstep(NoiseB);
+
+ r32 NoiseC = Noise3D((Pp / 25) + v3{0, 0, Time * 4});
+ r32 CPresence = SinR32((P.y / 50) - Time) + (0.8f * SinR32((P.y / 25) - (Time * 5.0f)));
+ CPresence = RemapR32(CPresence, -1.8, 1.8, 0, 1);
+ CPresence = PowR32(CPresence, 4);
+ NoiseC *= CPresence;
+
+ v4 C = (C0 * NoiseA * 0.5f) + (C1 * NoiseB) + (C2 * NoiseC);
+ C *= 1.0f / (NoiseA + NoiseB + NoiseC);
+ Leds->Colors[LedIndex] = V4ToRGBPixel(C);
+ }
+}
+
+internal void
+Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+}
+
+internal void
+Pattern_VerticalLines(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
+ gs_random_series Random = InitRandomSeries(24601);
+
+ r32 LightSpeedMin = 1;
+ r32 LightSpeedMax = 5;
+
+ s32 LightTailLength = 60;
+ for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++)
+ {
+ v2_strip Strip = Assembly.Strips[StripIndex];
+
+ r32 LightStartHeight = NextRandomUnilateral(&Random);
+ r32 LightSpeed = LerpR32(NextRandomUnilateral(&Random),
+ LightSpeedMin,
+ LightSpeedMax);
+ r32 LightCurrentHeight = LightStartHeight + (LightSpeed * Time * 0.1f);
+ s32 StartIndex = (s32)(LightCurrentHeight * (r32)Strip.LedCount) % Strip.LedCount;
+
+ for (s32 i = 0; i < LightTailLength; i++)
+ {
+ s32 StripLedIndex = StartIndex + i;
+ if (StripLedIndex >= (s32)Strip.LedCount) continue;
+
+ u32 LedIndex = Strip.LedLUT[StripLedIndex];
+ r32 PctTail = ((r32)i / (r32)LightTailLength);
+ v4 C = WhiteV4 * PctTail;
+ Leds->Colors[LedIndex] = V4ToRGBPixel(C);
+ }
+ }
+}
+
+internal void
+Pattern_RotaryOptions(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 BGColor, v4 FGColor)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ gs_random_series Random = InitRandomSeries((u32)(24601 * (Assembly.Center.x + 1.032f)));
+
+#define SphereCount 32
+ v3 SphereCenter[SphereCount];
+
+ r32 G = RemapR32(Granularity, 1, 5, .75f, 2);
+ r32 MaxHeightOffset = 250;
+ r32 MaxSpeed = 10;
+ r32 SphereRotationRadius = 3.0f;
+ r32 SphereRadius = 2.0f / G;
+ for (u32 i = 0; i < SphereCount; i++)
+ {
+ r32 SphereSeedA = NextRandomUnilateral(&Random);
+ SphereSeedA = PowR32(SphereSeedA, 2);
+ r32 SphereSeedB = NextRandomBilateral(&Random);
+ r32 SphereSpeed = NextRandomUnilateral(&Random) * MaxSpeed;
+
+ r32 SphereTime = Time + SphereSpeed;
+ r32 HeightOffset = 150 - (SphereSeedA * MaxHeightOffset);
+ r32 RotationOffset = SphereTime + SphereSeedB * TauR32;
+ r32 SphereRotationDir = NextRandomBilateral(&Random) < 0 ? -1 : 1;
+ v3 SpherePosOffset = v3{
+ SinR32(RotationOffset * SphereRotationDir) * (SphereRotationRadius * 2),
+ HeightOffset,
+ CosR32(RotationOffset * SphereRotationDir) * (SphereRotationRadius * 2)
+ };
+ SphereCenter[i] = Assembly.Center + SpherePosOffset;
+ }
+
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v3 P = Leds->Positions[LedIndex].xyz;
+
+ r32 Dist = 10000000;
+ for (u32 i = 0; i < SphereCount; i++)
+ {
+ r32 SphereSDF = Abs(SDF_Sphere(P, SphereCenter[i], SphereRadius));
+ SphereSDF = SphereSDF / SphereRadius;
+ Dist = Min(Dist, SphereSDF);
+ }
+
+ v4 C = BGColor;
+ if (Dist <= 1)
+ {
+ r32 Brightness = Clamp01(SphereRadius - Dist);
+ C = V4Lerp(Brightness, BGColor, FGColor);
+ }
+
+ Leds->Colors[LedIndex] = V4ToRGBPixel(C);
+ }
+}
+
+internal void
+Pattern_Rotary(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+ Pattern_RotaryOptions(Leds, Range, Assembly, Time, Transient, UserData, .25f, BlackV4, WhiteV4);
+}
+
+internal void
+Pattern_AllOnMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ pixel White = V4ToRGBPixel(WhiteV4);
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v3 P = Leds->Positions[LedIndex].xyz;
+ Leds->Colors[LedIndex] = White;
+ }
+}
+
+internal void
+Pattern_BulbMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
+ r32 Top = 141;
+ r32 BulbRange = 50;
+
+ pixel White = V4ToRGBPixel(WhiteV4);
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v3 P = Leds->Positions[LedIndex].xyz;
+
+ r32 BulbSDF = 1 - Clamp01(((Top - P.y) - BulbRange) / BulbRange);
+ r32 N = Noise3D((P / 17) + v3{Time, -Time, 0});
+ N = Clamp01(N) * 2;
+ N = Smoothstep(N);
+ N *= N;
+ N = Smoothstep(N);
+ N *= 1.0f - PowR32(1.0f - BulbSDF, 4);
+ BulbSDF += N;
+ BulbSDF = Clamp01(BulbSDF);
+ v4 C = WhiteV4 * BulbSDF;
+ Leds->Colors[LedIndex] = V4ToRGBPixel(C);
+ }
+}
+
+internal v4
+GenPatchyColor(v3 P, r32 Time, v4 C0, v4 C1, v4 C2)
+{
+ r32 LedRange = 300.0f;
+ r32 ScaleFactor = 1.0f / LedRange;
+ v3 Pp = P + v3{150, 100, 0};
+
+ r32 ScaleA = 1;
+ r32 NoiseA = Noise3D(((Pp / 38) + v3{0, 0, Time}) * ScaleA);
+ NoiseA = PowR32(NoiseA, 3);
+ NoiseA = Smoothstep(NoiseA);
+
+ r32 ScaleBP = 2;
+ r32 ScaleB = 15;
+ r32 NoiseBP = Noise3D(((Pp / 13) + v3{ 0, Time * -0.33f, 0}) * ScaleBP);
+ NoiseBP = PowR32(NoiseBP, 3);
+ r32 NoiseB = Noise3D(((Pp / 75) + v3{Time * 0.5f, 0, 0}) * ScaleB);
+ NoiseB = PowR32(NoiseB, 3);
+ NoiseB = Smoothstep(NoiseB) * NoiseBP;
+
+ r32 ScaleC = 1.5;
+ r32 NoiseCP = Noise3D(((Pp / 132) + v3{Time * -0.33f, 0, 0}) * 0.5f);
+ r32 NoiseC = Noise3D(((Pp / 164) + v3{Time * 0.25f, 0, 0}) * ScaleC);
+ NoiseC = PowR32(NoiseC, 3);
+ NoiseC = Smoothstep(NoiseC) * NoiseCP;
+
+ v4 C = (C0 * NoiseA) + (C1 * NoiseB) + (C2 * NoiseC);
+ C /= (NoiseA + NoiseB + NoiseC);
+ return C;
+}
+
+internal r32
+GenVerticalStrips(v3 P, r32 Time)
+{
+ v2 Right = v2{1, 0};
+ v2 Pa = V2Normalize(v2{P.x, P.z});
+ r32 Angle = .5f + (.5f * V2Dot(Pa, Right));
+
+ r32 HOffset = 70.0f;
+ r32 O = 50.0f;
+ r32 C = 10.0f;
+
+ r32 X = Angle;
+ r32 Y = P.y;
+ r32 I = FloorR32(Y / C) * C;
+ I += (X * HOffset) + (Time * 25);
+
+ r32 V = FractR32(I / O);
+ V = 2.0f * (0.5f - Abs(V - 0.5f));
+ Assert(V >= 0 && V <= 1);
+
+ return V;
+}
+
+internal v4
+GenVerticalLeaves(v3 P, r32 Time, v4 C0, v4 C1, v4 C2)
+{
+ r32 A = GenVerticalStrips(P, Time * .25f);
+ r32 B = GenVerticalStrips(P * .3f, Time);
+ r32 C = GenVerticalStrips(P * .25f, Time * 2);
+
+ v4 R = (C0 * A) + (C1 * B) + (C2 * C);
+ R /= A + B + C;
+ return R;
+}
+
+internal void
+AddIn_WavesPattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData, r32 Granularity, v4 C0, v4 C1, v4 C2)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ v4 C2P = C2 * 255;
+
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v3 P = Leds->Positions[LedIndex].xyz;
+
+ v4 C = v4{
+ (r32)Leds->Colors[LedIndex].R,
+ (r32)Leds->Colors[LedIndex].G,
+ (r32)Leds->Colors[LedIndex].B,
+ 1
+ };
+
+ r32 Offset = -250;
+ r32 Width = 30;
+ r32 Bands = 0;
+ for (u32 i = 1; i <= 1; i++)
+ {
+ P.x = FloorR32(P.x);
+ P.z = FloorR32(P.z);
+
+ v3 P0 = v3{P.x + (23.124f * i), 0, P.z - (-12.34f * i) + Time};
+ r32 S = Fbm3D(P0 * .005f, Time) * 250;
+ S += ModR32((Time * 100) - (150 * i), 400);
+
+ r32 Y = (P.y - Offset);
+ r32 V = (Width - Abs(Y - S)) / Width;
+ V = Clamp01(V);
+
+ Bands += V;
+ }
+
+ C = V4Lerp(Bands, C * .5f, C2P);
+
+ Leds->Colors[LedIndex] = pixel{(u8)C.r, (u8)C.g, (u8)C.b};
+ }
+}
+
+internal r32
+GenDotBands(v3 P, r32 Time)
+{
+ r32 RowHeight = 25;
+ r32 DotRadius = 20;
+
+ r32 Y = P.y + 150;
+ s32 Row = (s32)FloorR32(Y / RowHeight);
+ r32 RowH = Abs(FractR32(Y / RowHeight));
+ r32 DotDistY = Max(0, .5f - RowH) * 2;
+
+ r32 Angle = (V2Dot(V2Normalize(v2{P.x, P.z}), v2{1,0}) * .5f) + .5f;
+ r32 DotDistX = Abs(ModR32(Angle, .2f));
+
+ r32 DotDist = SqrtR32(PowR32(DotDistX, 2) + PowR32(RowH, 2));
+ r32 B = (DotRadius - DotDist) / DotRadius;
+ B = Clamp01(DotDist);
+
+ return DotDistY;
+
+}
+
+internal void
+Pattern_VoicePattern(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
+
+ Time = Time * BLState->PatternSpeed * Hue.Speed;;
+
+ switch (Hue.Pattern)
+ {
+ case HuePattern_Wavy:
+ {
+ Pattern_WavyOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C0);
+ }break;
+
+ default:
+ {
+ Pattern_PatchyOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C2);
+ }break;
+ }
+
+
+}
+
+internal void
+Pattern_VoiceAddIns(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
+
+ Time = Time * BLState->PatternSpeed * Hue.Speed;;
+
+ switch (Hue.AddIn)
+ {
+ case AddIn_Rotary:
+ {
+ Pattern_RotaryOptions(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, BlackV4, C2);
+ }break;
+
+ case AddIn_Waves:
+ {
+ AddIn_WavesPattern(Leds, Range, Assembly, Time, Transient, UserData, Hue.Granularity, C0, C1, C2);
+ }break;
+
+ case AddIn_None:
+ default:
+ {
+ }break;
+ }
+}
+
+internal void
+Pattern_StemSolid(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+
+ pixel WhiteMask = V4ToRGBPixel(WhiteV4);
+
+ led_strip_list Stem = BLState->StemStrips[Assembly.AssemblyIndex];
+ for (u32 s = 0; s < Stem.Count; s++)
+ {
+ u32 StripIndex = Stem.StripIndices[s];
+ v2_strip Strip = Assembly.Strips[StripIndex];
+ for (u32 i = 0; i < Strip.LedCount; i++)
+ {
+ u32 LedIndex = Strip.LedLUT[i];
+ Leds->Colors[LedIndex] = WhiteMask;
+ }
+ }
+}
+
+internal void
+Pattern_PrimaryHue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
+ phrase_hue Hue = BlumenLumen_GetCurrentHue(BLState, Assembly);
+ v4 C0 = RGBFromPhraseHue(Hue.Hue0);
+ v4 C1 = RGBFromPhraseHue(Hue.Hue1);
+ v4 C2 = RGBFromPhraseHue(Hue.Hue2);
+
+ pixel HueOut = V4ToRGBPixel(C0);
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ Leds->Colors[LedIndex] = HueOut;
+ }
+}
+
+internal void
+Pattern_GrowFadeMask(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
+ r32 Period = 10.0f; // seconds
+ r32 ElapsedPct = FractR32(Time / Period);
+
+ r32 ElapsedPctGrow = PowR32(ElapsedPct * 2, 2);
+ r32 ElapsedPctFade = Clamp01((ElapsedPct * 2) - 1);
+
+ r32 Radius = 300 * ElapsedPctGrow;
+
+ v3 Origin = Assembly.Center - v3{0, 150, 0};
+
+ r32 Brightness = Smoothstep(1.0f - ElapsedPctFade);
+
+ pixel COutside = V4ToRGBPixel(BlackV4);
+ pixel CInside = V4ToRGBPixel(WhiteV4 * Brightness);
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v3 P = Leds->Positions[LedIndex].xyz;
+ r32 Dist = V3Mag(P - Origin);
+ if (Dist < Radius)
+ {
+ Leds->Colors[LedIndex] = CInside;
+ }
+ else
+ {
+ Leds->Colors[LedIndex] = COutside;
+ }
+ }
+}
+
+internal void
+Pattern_RainbowLoadingBar(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData;
+ Time = Time * BLState->PatternSpeed;
+
+ // constants
+ r32 Period = 5.0f; // seconds
+ r32 CSpeed = 16.0f;
+ r32 HIncrement = CSpeed * Period;
+ r32 HOffset = CSpeed * Period;
+ r32 MaxSphereRadius = 300;
+
+ // sphere
+ r32 ElapsedPct = FractR32(Time / Period);
+ r32 ElapsedPctGrow = PowR32(ElapsedPct, 2);
+ r32 Radius = MaxSphereRadius * ElapsedPctGrow;
+ v3 Origin = Assembly.Center - v3{0, 150, 0};
+
+ // colors
+ r32 T = Time * CSpeed;
+ r32 TimeStep0 = T;
+ r32 TimeStep1 = T + HOffset;
+ r32 Hue0 = FloorR32(TimeStep0 / HIncrement) * HIncrement;
+ r32 Hue1 = FloorR32(TimeStep1 / HIncrement) * HIncrement;
+ v4 H0 = v4{ModR32(Hue0, 360), 1, 1, 1};
+ v4 H1 = v4{ModR32(Hue1, 360), 1, 1, 1};
+ pixel C0 = V4ToRGBPixel(HSVToRGB(H0));
+ pixel C1 = V4ToRGBPixel(HSVToRGB(H1));
+
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ v3 P = Leds->Positions[LedIndex].xyz;
+ r32 Dist = V3Mag(P - Origin);
+ if (Dist < Radius)
+ {
+ Leds->Colors[LedIndex] = C1;
+ }
+ else
+ {
+ Leds->Colors[LedIndex] = C0;
+ }
+ }
+}
+
+internal void
+Pattern_Blue(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData)
+{
+ pixel Blue = pixel{0, 0, 255};
+ for (u32 LedIndex = Range.First; LedIndex < Range.OnePastLast; LedIndex++)
+ {
+ Leds->Colors[LedIndex] = Blue;
+ }
+}
+
#define BLUMEN_PATTERNS_H
#endif // BLUMEN_PATTERNS_H
\ No newline at end of file
diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp
index df968c8..d281552 100644
--- a/src/app/platform_win32/win32_foldhaus.cpp
+++ b/src/app/platform_win32/win32_foldhaus.cpp
@@ -332,8 +332,7 @@ DebugPrint (char* Format, ...)
gs_string StringBuffer = MakeString(Buffer, 256);
va_list Args;
va_start(Args, Format);
- PrintF(&StringBuffer, Format, Args);
- OutputDebugStringA(Buffer);
+ Log_PrintFVarArgs(GlobalLogBuffer, LogEntry_Message, Format, Args);
va_end(Args);
}
@@ -356,22 +355,6 @@ SetApplicationLinks (context* Context, win32_dll_refresh DLL, gs_work_queue* Wor
}
}
-// TODO(Peter): :Redundant remove
-internal u8*
-DEBUGAlloc(s32 ElementSize, s32 ElementCount)
-{
- return (u8*)Win32Alloc(ElementSize * ElementCount, 0);
-}
-
-// TODO(Peter): :Redundant remove
-internal u8*
-Win32Realloc(u8* Buf, s32 OldSize, s32 NewSize)
-{
- u8* NewMemory = (u8*)Win32Alloc(NewSize, 0);
- CopyMemoryTo(Buf, NewMemory, OldSize);
- return NewMemory;
-}
-
internal void
Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer* BufferAt)
{
@@ -396,7 +379,7 @@ Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer*
{
if (BufferAt->ComPort.Length > 0)
{
- HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1);
+ HANDLE SerialPort = Win32SerialArray_GetOrOpen(BufferAt->ComPort, 2000000, 8, NOPARITY, 1, Context.Transient);
if (SerialPort != INVALID_HANDLE_VALUE)
{
if (Win32SerialPort_Write(SerialPort, BufferAt->Data))
@@ -413,7 +396,8 @@ Win32_SendAddressedDataBuffer(gs_thread_context Context, addressed_data_buffer*
else
{
#if 0
- OutputDebugStringA("Skipping data buffer because its COM Port isn't set");
+ Log_Message(GlobalLogBuffer,
+ "Skipping data buffer because its COM Port isn't set");
#endif
}
}break;
@@ -430,18 +414,19 @@ Win32_SendAddressedDataBuffer_Job(gs_thread_context Context, gs_data Arg)
}
internal bool
-ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQueue, bool ShouldError)
+ReloadAndLinkDLL(win32_dll_refresh* DLL, context* Context, gs_work_queue* WorkQueue, bool ShouldError, bool AppReady)
{
bool Success = false;
if (HotLoadDLL(DLL))
{
SetApplicationLinks(Context, *DLL, WorkQueue);
- Context->ReloadStaticData(*Context, GlobalDebugServices);
+ Context->ReloadStaticData(*Context, GlobalDebugServices, GlobalLogBuffer, AppReady);
Success = true;
+ Log_Message(GlobalLogBuffer, "Reloaded DLL\n");
}
else if(ShouldError)
{
- OutputDebugStringA("Unable to load application DLL at startup.\nAborting\n");
+ Log_Error(GlobalLogBuffer, "Unable to load application DLL at startup.\nAborting\n");
}
return Success;
}
@@ -505,9 +490,9 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext)
if (WorkingDirectory.Length > 0)
{
- OutputDebugStringA("Setting Working Directory\n");
- OutputDebugStringA(WorkingDirectory.Str);
-
+ Log_Message(GlobalLogBuffer,
+ "Setting Working Directory \n%S \n",
+ WorkingDirectory.ConstString);
Result = SetCurrentDirectory(WorkingDirectory.Str);
if (!Result)
{
@@ -517,7 +502,75 @@ SetWorkingDirectory(HINSTANCE HInstance, gs_thread_context ThreadContext)
}
else
{
- OutputDebugStringA("Error, no data folder found\n");
+ Log_Error(GlobalLogBuffer, "Error, no data folder found\n");
+ }
+
+ return Result;
+}
+
+#include "../../gs_libs/gs_path.h"
+
+internal void
+Win32_SendOutputData(gs_thread_context ThreadContext, addressed_data_buffer_list OutputData)
+{
+ bool Multithread = true;
+ if (Multithread)
+ {
+ for (addressed_data_buffer* At = OutputData.Root;
+ At != 0;
+ At = At->Next)
+ {
+ gs_data ProcArg = {};
+ ProcArg.Memory = (u8*)At;
+ ProcArg.Size = sizeof(addressed_data_buffer);
+ Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data"));
+ }
+ }
+ else
+ {
+ for (addressed_data_buffer* At = OutputData.Root;
+ At != 0;
+ At = At->Next)
+ {
+ gs_data ProcArg = {};
+ ProcArg.Memory = (u8*)At;
+ ProcArg.Size = sizeof(addressed_data_buffer);
+ Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg);
+ }
+ }
+
+}
+
+// Time
+internal system_time
+Win32GetSystemTime()
+{
+ system_time Result = {};
+
+ SYSTEMTIME WinLocalTime;
+ GetLocalTime(&WinLocalTime);
+
+ SYSTEMTIME WinSysTime;
+ FILETIME WinSysFileTime;
+ GetSystemTime(&WinSysTime);
+ if (SystemTimeToFileTime((const SYSTEMTIME*)&WinSysTime, &WinSysFileTime))
+ {
+ ULARGE_INTEGER SysTime = {};
+ SysTime.LowPart = WinSysFileTime.dwLowDateTime;
+ SysTime.HighPart = WinSysFileTime.dwHighDateTime;
+
+ Result.NanosSinceEpoch = SysTime.QuadPart;
+ Result.Year = WinLocalTime.wYear;
+ Result.Month = WinLocalTime.wMonth;
+ Result.Day = WinLocalTime.wDay;
+ Result.Hour = WinLocalTime.wHour;
+ Result.Minute = WinLocalTime.wMinute;
+ Result.Second = WinLocalTime.wSecond;
+ }
+ else
+ {
+ u32 Error = GetLastError();
+ InvalidCodePath;
}
return Result;
@@ -533,37 +586,61 @@ WinMain (
{
gs_thread_context ThreadContext = Win32CreateThreadContext();
+ gs_memory_arena PlatformPermanent = MemoryArenaCreate(MB(4),
+ Bytes(8), ThreadContext.Allocator,
+ 0,
+ 0,
+ "Platform Memory");
+
+ GlobalLogBuffer = AllocStruct(ThreadContext.Allocator, log_buffer, "Global Log Buffer");
+ *GlobalLogBuffer = Log_Init(&PlatformPermanent, 32);
+
if (!SetWorkingDirectory(HInstance, ThreadContext)) return 1;
- MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents);
- Win32UpdateWindowDimension(&MainWindow);
-
- win32_opengl_window_info OpenGLWindowInfo = {};
- OpenGLWindowInfo.ColorBits = 32;
- OpenGLWindowInfo.AlphaBits = 8;
- OpenGLWindowInfo.DepthBits = 0;
- CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow);
-
context Context = {};
Context.ThreadContext = ThreadContext;
- Context.MemorySize = MB(64);
- Context.MemoryBase = (u8*)Win32Alloc(Context.MemorySize, 0);
+ Context.SystemTime_Current = Win32GetSystemTime();
- gs_memory_arena PlatformPermanent = CreateMemoryArena(Context.ThreadContext.Allocator);
+ gs_const_string Args = ConstString((char*)CmdLineArgs);
+ if (StringsEqual(Args, ConstString("-headless")))
+ {
+ Log_Message(GlobalLogBuffer, "Running Headless\n");
+ Context.Headless = true;
+ }
+
+ if (!Context.Headless)
+ {
+ MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents);
+ Win32UpdateWindowDimension(&MainWindow);
+
+ win32_opengl_window_info OpenGLWindowInfo = {};
+ OpenGLWindowInfo.ColorBits = 32;
+ OpenGLWindowInfo.AlphaBits = 8;
+ OpenGLWindowInfo.DepthBits = 0;
+ CreateOpenGLWindowContext(OpenGLWindowInfo, &MainWindow);
+ }
s64 PerformanceCountFrequency = GetPerformanceFrequency();
s64 LastFrameEnd = GetWallClock();
- r32 TargetSecondsPerFrame = 1 / 60.0f;
+ r32 TargetSecondsPerFrame = 1 / 30.0f;
r32 LastFrameSecondsElapsed = 0.0f;
GlobalDebugServices = PushStruct(&PlatformPermanent, debug_services);
- InitDebugServices(GlobalDebugServices,
- PerformanceCountFrequency,
- DEBUGAlloc,
- Win32Realloc,
- GetWallClock,
- Win32GetThreadId,
- PLATFORM_THREAD_COUNT + 1);
+#if DEBUG
+ InitDebugServices_DebugMode(GlobalDebugServices,
+ PerformanceCountFrequency,
+ GetWallClock,
+ Win32GetThreadId,
+ Context.ThreadContext,
+ PLATFORM_THREAD_COUNT + 1);
+#else
+ InitDebugServices_OffMode(GlobalDebugServices,
+ PerformanceCountFrequency,
+ GetWallClock,
+ Win32GetThreadId,
+ Context.ThreadContext,
+ PLATFORM_THREAD_COUNT + 1);
+#endif
input_queue InputQueue = InputQueue_Create(&PlatformPermanent, 32);
@@ -583,19 +660,25 @@ WinMain (
*Context.SocketManager = CreatePlatformSocketManager(Win32ConnectSocket, Win32CloseSocket, Win32SocketQueryStatus, Win32SocketPeek, Win32SocketReceive, Win32SocketSend);
win32_dll_refresh DLLRefresh = InitializeDLLHotReloading(DLLName, WorkingDLLName, DLLLockFileName);
- if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true)) { return -1; }
+ if (!ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, true, false)) { return -1; }
Mouse_Init();
Win32SocketSystem_Init(&PlatformPermanent);
- Win32SerialArray_Create(ThreadContext);
+ Win32SerialArray_Create(&PlatformPermanent);
- render_command_buffer RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, Win32Realloc);
+ render_command_buffer RenderBuffer = {};
+ if (!Context.Headless)
+ {
+ RenderBuffer = AllocateRenderCommandBuffer(MB(12), &PlatformPermanent, ThreadContext);
+ }
addressed_data_buffer_list OutputData = AddressedDataBufferList_Create(ThreadContext);
- Context.InitializeApplication(Context);
+ Context.InitializeApplication(&Context);
+
+ system_time StartTime = Win32GetSystemTime();
Running = true;
Context.WindowIsVisible = true;
@@ -605,64 +688,72 @@ WinMain (
{
EndDebugFrame(GlobalDebugServices);
}
+
DEBUG_TRACK_SCOPE(MainLoop);
+ {
+ Context.SystemTime_Last = Context.SystemTime_Current;
+ Context.SystemTime_Current = Win32GetSystemTime();
+
+#define PRINT_SYSTEM_TIME 0
+#if PRINT_SYSTEM_TIME
+ gs_string T = PushStringF(Context.ThreadContext.Transient,
+ 256,
+ "%d %d %d - %lld\n",
+ Context.SystemTime_Current.Hour,
+ Context.SystemTime_Current.Minute,
+ Context.SystemTime_Current.Second,
+ Context.SystemTime_Current.NanosSinceEpoch);
+
+ u64 NanosElapsed = Context.SystemTime_Current.NanosSinceEpoch - StartTime.NanosSinceEpoch;
+ r64 SecondsElapsed = (r64)NanosElapsed * NanosToSeconds;
+
+ Log_Message(GlobalLogBuffer,
+ "%lld %f Seconds \n",
+ NanosElapsed,
+ SecondsElapsed);
+#endif
+ }
+
ResetInputQueue(&InputQueue);
- ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false);
+ ReloadAndLinkDLL(&DLLRefresh, &Context, &Win32WorkQueue.WorkQueue, false, true);
AddressedDataBufferList_Clear(&OutputData);
- Mouse_Update(MainWindow, &Context);
-
- MSG Message;
- while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE))
+ if (!Context.Headless)
{
- DEBUG_TRACK_SCOPE(PeekWindowsMessages);
- HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse);
+ Mouse_Update(MainWindow, &Context);
+ MSG Message;
+ while (PeekMessageA(&Message, MainWindow.Handle, 0, 0, PM_REMOVE))
+ {
+ DEBUG_TRACK_SCOPE(PeekWindowsMessages);
+ HandleWindowMessage(Message, &MainWindow, &InputQueue, &Context.Mouse);
+ }
+
+ Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}};
+ RenderBuffer.ViewWidth = MainWindow.Width;
+ RenderBuffer.ViewHeight = MainWindow.Height;
}
- Context.WindowBounds = rect2{v2{0, 0}, v2{(r32)MainWindow.Width, (r32)MainWindow.Height}};
- RenderBuffer.ViewWidth = MainWindow.Width;
- RenderBuffer.ViewHeight = MainWindow.Height;
Context.DeltaTime = LastFrameSecondsElapsed;
+ Context.TotalTime += (r64)Context.DeltaTime;
Context.UpdateAndRender(&Context, InputQueue, &RenderBuffer, &OutputData);
- bool Multithread = false;
- if (Multithread)
+ Win32_SendOutputData(ThreadContext, OutputData);
+
+ if (!Context.Headless)
{
- for (addressed_data_buffer* At = OutputData.Root;
- At != 0;
- At = At->Next)
- {
- gs_data ProcArg = {};
- ProcArg.Memory = (u8*)At;
- ProcArg.Size = sizeof(addressed_data_buffer);
- Win32PushWorkOnQueue(&Win32WorkQueue.WorkQueue, Win32_SendAddressedDataBuffer_Job, ProcArg, ConstString("Send UART Data"));
- }
+ RenderCommandBuffer(RenderBuffer);
+ ClearRenderBuffer(&RenderBuffer);
+
+ Mouse_Advance(&Context);
+
+ HDC DeviceContext = GetDC(MainWindow.Handle);
+ SwapBuffers(DeviceContext);
+ ReleaseDC(MainWindow.Handle, DeviceContext);
}
- else
- {
- for (addressed_data_buffer* At = OutputData.Root;
- At != 0;
- At = At->Next)
- {
- gs_data ProcArg = {};
- ProcArg.Memory = (u8*)At;
- ProcArg.Size = sizeof(addressed_data_buffer);
- Win32_SendAddressedDataBuffer_Job(ThreadContext, ProcArg);
- }
- }
-
- RenderCommandBuffer(RenderBuffer);
- ClearRenderBuffer(&RenderBuffer);
-
- Mouse_Advance(&Context);
-
- HDC DeviceContext = GetDC(MainWindow.Handle);
- SwapBuffers(DeviceContext);
- ReleaseDC(MainWindow.Handle, DeviceContext);
Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext);
@@ -681,11 +772,11 @@ WinMain (
LastFrameEnd = GetWallClock();
}
- Context.CleanupApplication(Context);
+ Context.CleanupApplication(Context, &OutputData);
+ Win32_SendOutputData(ThreadContext, OutputData);
+ Win32DoQueueWorkUntilDone(&Win32WorkQueue.WorkQueue, Context.ThreadContext);
Win32WorkQueue_Cleanup();
- //Win32_TestCode_SocketReading_Cleanup();
-
Win32SocketSystem_Cleanup();
return 0;
diff --git a/src/app/platform_win32/win32_foldhaus_dll.h b/src/app/platform_win32/win32_foldhaus_dll.h
index 26071e6..396f15e 100644
--- a/src/app/platform_win32/win32_foldhaus_dll.h
+++ b/src/app/platform_win32/win32_foldhaus_dll.h
@@ -11,92 +11,92 @@
// DLL
struct win32_dll_refresh
{
- FILETIME LastWriteTime;
- HMODULE DLL;
-
- bool IsValid;
-
- char SourceDLLPath[MAX_PATH];
- char WorkingDLLPath[MAX_PATH];
- char LockFilePath[MAX_PATH];
+ FILETIME LastWriteTime;
+ HMODULE DLL;
+
+ bool IsValid;
+
+ char SourceDLLPath[MAX_PATH];
+ char WorkingDLLPath[MAX_PATH];
+ char LockFilePath[MAX_PATH];
};
internal int
Win32DLLgs_stringLength(char* gs_string)
{
- char* At = gs_string;
- while (*At) { At++; };
- return At - gs_string;
+ char* At = gs_string;
+ while (*At) { At++; };
+ return At - gs_string;
}
internal int
Win32DLLConcatgs_strings(int ALength, char* A, int BLength, char* B, int DestLength, char* Dest)
{
- char* Dst = Dest;
- char* AAt = A;
- int ALengthToCopy = ALength < DestLength ? ALength : DestLength;
- for (s32 a = 0; a < ALength; a++)
- {
- *Dst++ = *AAt++;
- }
- char* BAt = B;
- int DestLengthRemaining = DestLength - (Dst - Dest);
- int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength;
- for (s32 b = 0; b < BLengthToCopy; b++)
- {
- *Dst++ = *BAt++;
- }
- int DestLengthOut = Dst - Dest;
- int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength;
- Dest[NullTermIndex] = 0;
- return DestLengthOut;
+ char* Dst = Dest;
+ char* AAt = A;
+ int ALengthToCopy = ALength < DestLength ? ALength : DestLength;
+ for (s32 a = 0; a < ALength; a++)
+ {
+ *Dst++ = *AAt++;
+ }
+ char* BAt = B;
+ int DestLengthRemaining = DestLength - (Dst - Dest);
+ int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength;
+ for (s32 b = 0; b < BLengthToCopy; b++)
+ {
+ *Dst++ = *BAt++;
+ }
+ int DestLengthOut = Dst - Dest;
+ int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength;
+ Dest[NullTermIndex] = 0;
+ return DestLengthOut;
}
internal void
GetApplicationPath(system_path* Result)
{
- Assert(Result->Path);
- Result->PathLength = GetModuleFileNameA(0, Result->Path, Result->PathLength);
-
- u32 CharactersScanned = 0;
- u32 IndexOfLastSlash = 0;
- char *Scan = Result->Path;
- while(*Scan)
+ Assert(Result->Path);
+ Result->PathLength = GetModuleFileNameA(0, Result->Path, Result->PathLength);
+
+ u32 CharactersScanned = 0;
+ u32 IndexOfLastSlash = 0;
+ char *Scan = Result->Path;
+ while(*Scan)
+ {
+ if (*Scan == '\\')
{
- if (*Scan == '\\')
- {
- Result->IndexOfLastSlash = CharactersScanned + 1;
- }
- Scan++;
- CharactersScanned++;
+ Result->IndexOfLastSlash = CharactersScanned + 1;
}
+ Scan++;
+ CharactersScanned++;
+ }
}
internal b32
LoadApplicationDLL(char* DLLName, win32_dll_refresh* DLLResult)
{
- b32 Success = false;
- Assert(DLLResult->DLL == 0);
-
- DLLResult->DLL = LoadLibraryA(DLLName);
- if (DLLResult->DLL)
- {
- Success = true;
- DLLResult->IsValid = true;
- }
-
- return Success;
+ b32 Success = false;
+ Assert(DLLResult->DLL == 0);
+
+ DLLResult->DLL = LoadLibraryA(DLLName);
+ if (DLLResult->DLL)
+ {
+ Success = true;
+ DLLResult->IsValid = true;
+ }
+
+ return Success;
}
internal void
UnloadApplicationDLL(win32_dll_refresh* DLL)
{
- if (DLL->DLL)
- {
- FreeLibrary(DLL->DLL);
- }
- DLL->DLL = 0;
- DLL->IsValid = false;
+ if (DLL->DLL)
+ {
+ FreeLibrary(DLL->DLL);
+ }
+ DLL->DLL = 0;
+ DLL->IsValid = false;
}
internal win32_dll_refresh
@@ -104,57 +104,57 @@ InitializeDLLHotReloading(char* SourceDLLName,
char* WorkingDLLFileName,
char* LockFileName)
{
- win32_dll_refresh Result = {};
- Result.IsValid = false;
-
- system_path ExePath = {};
- ExePath.PathLength = MAX_PATH;
- ExePath.Path = (char*)VirtualAlloc(NULL, ExePath.PathLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
- GetApplicationPath(&ExePath);
-
- Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
- Win32DLLgs_stringLength(SourceDLLName), SourceDLLName,
- MAX_PATH, Result.SourceDLLPath);
- Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
- Win32DLLgs_stringLength(WorkingDLLFileName), WorkingDLLFileName,
- MAX_PATH, Result.WorkingDLLPath);
- Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
- Win32DLLgs_stringLength(LockFileName), LockFileName,
- MAX_PATH, Result.LockFilePath);
-
- Win32Free((u8*)ExePath.Path, ExePath.PathLength);
- return Result;
-
+ win32_dll_refresh Result = {};
+ Result.IsValid = false;
+
+ system_path ExePath = {};
+ ExePath.PathLength = MAX_PATH;
+ ExePath.Path = (char*)VirtualAlloc(NULL, ExePath.PathLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ GetApplicationPath(&ExePath);
+
+ Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
+ Win32DLLgs_stringLength(SourceDLLName), SourceDLLName,
+ MAX_PATH, Result.SourceDLLPath);
+ Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
+ Win32DLLgs_stringLength(WorkingDLLFileName), WorkingDLLFileName,
+ MAX_PATH, Result.WorkingDLLPath);
+ Win32DLLConcatgs_strings(ExePath.IndexOfLastSlash, ExePath.Path,
+ Win32DLLgs_stringLength(LockFileName), LockFileName,
+ MAX_PATH, Result.LockFilePath);
+
+ Win32Free((u8*)ExePath.Path, ExePath.PathLength, 0);
+ return Result;
+
}
internal b32
HotLoadDLL(win32_dll_refresh* DLL)
{
- b32 DidReload = false;
-
- FILETIME UpdatedLastWriteTime = {};
- WIN32_FIND_DATA FindData = {};
- HANDLE FileHandle = FindFirstFileA(DLL->SourceDLLPath, &FindData);
- if (FileHandle != INVALID_HANDLE_VALUE)
+ b32 DidReload = false;
+
+ FILETIME UpdatedLastWriteTime = {};
+ WIN32_FIND_DATA FindData = {};
+ HANDLE FileHandle = FindFirstFileA(DLL->SourceDLLPath, &FindData);
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
+ UpdatedLastWriteTime = FindData.ftLastWriteTime;
+ FindClose(FileHandle);
+ }
+
+ if (CompareFileTime(&UpdatedLastWriteTime, &DLL->LastWriteTime))
+ {
+ WIN32_FILE_ATTRIBUTE_DATA Ignored;
+ if (!GetFileAttributesEx(DLL->LockFilePath, GetFileExInfoStandard, &Ignored))
{
- UpdatedLastWriteTime = FindData.ftLastWriteTime;
- FindClose(FileHandle);
+ UnloadApplicationDLL(DLL);
+ CopyFileA(DLL->SourceDLLPath, DLL->WorkingDLLPath, FALSE);
+ LoadApplicationDLL(DLL->WorkingDLLPath, DLL);
+ DLL->LastWriteTime = UpdatedLastWriteTime;
+ DidReload = true;
}
-
- if (CompareFileTime(&UpdatedLastWriteTime, &DLL->LastWriteTime))
- {
- WIN32_FILE_ATTRIBUTE_DATA Ignored;
- if (!GetFileAttributesEx(DLL->LockFilePath, GetFileExInfoStandard, &Ignored))
- {
- UnloadApplicationDLL(DLL);
- CopyFileA(DLL->SourceDLLPath, DLL->WorkingDLLPath, FALSE);
- LoadApplicationDLL(DLL->WorkingDLLPath, DLL);
- DLL->LastWriteTime = UpdatedLastWriteTime;
- DidReload = true;
- }
- }
-
- return DidReload;
+ }
+
+ return DidReload;
}
diff --git a/src/app/platform_win32/win32_foldhaus_fileio.h b/src/app/platform_win32/win32_foldhaus_fileio.h
index 95163ea..906d947 100644
--- a/src/app/platform_win32/win32_foldhaus_fileio.h
+++ b/src/app/platform_win32/win32_foldhaus_fileio.h
@@ -11,258 +11,272 @@
internal u64
Win32HighLowToU64(u32 LowPart, u32 HighPart)
{
- ULARGE_INTEGER Time = {};
- Time.LowPart = LowPart;
- Time.HighPart = HighPart;
- u64 Result = Time.QuadPart;
- return Result;
+ ULARGE_INTEGER Time = {};
+ Time.LowPart = LowPart;
+ Time.HighPart = HighPart;
+ u64 Result = Time.QuadPart;
+ return Result;
}
internal u64
Win32FileTimeToU64(FILETIME FileTime)
{
- u64 Result = Win32HighLowToU64(FileTime.dwLowDateTime, FileTime.dwHighDateTime);
- return Result;
+ u64 Result = Win32HighLowToU64(FileTime.dwLowDateTime, FileTime.dwHighDateTime);
+ return Result;
}
GET_FILE_INFO(Win32GetFileInfo)
{
- Assert(IsNullTerminated(Path));
- gs_file_info Result = {};
- HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (FileHandle != INVALID_HANDLE_VALUE)
+ Assert(IsNullTerminated(Path));
+ gs_file_info Result = {};
+ HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
+ Result.Path = Path;
+ Result.FileSize = (u64)GetFileSize(FileHandle, NULL) + 1;
+ FILETIME CreationTime, LastWriteTime;
+ if (GetFileTime(FileHandle, &CreationTime, (LPFILETIME)0, &LastWriteTime))
{
- Result.Path = Path;
- Result.FileSize = (u64)GetFileSize(FileHandle, NULL) + 1;
- FILETIME CreationTime, LastWriteTime;
- if (GetFileTime(FileHandle, &CreationTime, (LPFILETIME)0, &LastWriteTime))
- {
- Result.CreationTime = Win32FileTimeToU64(CreationTime);
- Result.LastWriteTime = Win32FileTimeToU64(LastWriteTime);
- }
- else
- {
- PrintLastError();
- }
- CloseHandle(FileHandle);
+ Result.CreationTime = Win32FileTimeToU64(CreationTime);
+ Result.LastWriteTime = Win32FileTimeToU64(LastWriteTime);
}
else
{
- DWORD FileAttr = GetFileAttributes(Path.Str);
- if (FileAttr != INVALID_FILE_ATTRIBUTES &&
- (FileAttr & FILE_ATTRIBUTE_DIRECTORY))
- {
- Result.Path = Path;
- Result.IsDirectory = true;
- }
- else
- {
- // Path is not a file or directory
- }
+ PrintLastError();
}
- return Result;
+ CloseHandle(FileHandle);
+ }
+ else
+ {
+ DWORD FileAttr = GetFileAttributes(Path.Str);
+ if (FileAttr != INVALID_FILE_ATTRIBUTES &&
+ (FileAttr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ Result.Path = Path;
+ Result.IsDirectory = true;
+ }
+ else
+ {
+ // Path is not a file or directory
+ }
+ }
+ return Result;
}
READ_ENTIRE_FILE(Win32ReadEntireFile)
{
- Assert(DataIsNonEmpty(Memory));
- Assert(IsNullTerminated(Path));
-
- gs_file Result = {0};
- u32 Error = 0;
- Result.FileInfo.Path = Path;
-
- HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (FileHandle != INVALID_HANDLE_VALUE)
+ Assert(DataIsNonEmpty(Memory));
+ Assert(IsNullTerminated(Path));
+
+ gs_file Result = {0};
+ u32 Error = 0;
+ Result.FileInfo.Path = Path;
+
+ HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
+ DWORD BytesRead = 0;
+ if (ReadFile(FileHandle, (LPVOID)Memory.Memory, Memory.Size - 1, (LPDWORD)(&BytesRead), NULL))
{
- DWORD BytesRead = 0;
- if (ReadFile(FileHandle, (LPVOID)Memory.Memory, Memory.Size - 1, (LPDWORD)(&BytesRead), NULL))
- {
- Memory.Memory[Memory.Size - 1] = 0;
- Result.Data = Memory;
-
- gs_string AbsolutePath = PushString(FileHandler.Transient, 512);
- AbsolutePath.Length = GetFullPathNameA(Path.Str, AbsolutePath.Size, AbsolutePath.Str, NULL);
- if (AbsolutePath.Length == 0)
- {
- Error = GetLastError();
- InvalidCodePath;
- }
- Result.FileInfo.AbsolutePath = AbsolutePath.ConstString;
- }
- else
- {
- // NOTE(Peter): If we get to this error case, it means that the file exists,
- // and was successfully opened, but we can't read from it. I'm choosing to
- // treat this as a legitimate error at this point.
- gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to read file: %S", Path);
- if (MessageBox(NULL, Message.Str, "Error", MB_OK) == IDOK)
- {
- PostQuitMessage(-1);
- }
- }
- CloseHandle(FileHandle);
+ Memory.Memory[Memory.Size - 1] = 0;
+ Result.Data = Memory;
+
+ gs_string AbsolutePath = PushString(FileHandler.Transient, 512);
+ AbsolutePath.Length = GetFullPathNameA(Path.Str, AbsolutePath.Size, AbsolutePath.Str, NULL);
+ if (AbsolutePath.Length == 0)
+ {
+ Error = GetLastError();
+ InvalidCodePath;
+ }
+ Result.FileInfo.AbsolutePath = AbsolutePath.ConstString;
}
else
{
-
+ // NOTE(Peter): If we get to this error case, it means that the file exists,
+ // and was successfully opened, but we can't read from it. I'm choosing to
+ // treat this as a legitimate error at this point.
+ gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to read file: %S", Path);
+ if (MessageBox(NULL, Message.Str, "Error", MB_OK) == IDOK)
+ {
+ PostQuitMessage(-1);
+ }
}
+ CloseHandle(FileHandle);
+ }
+ else
+ {
- return Result;
+ }
+
+ return Result;
}
WRITE_ENTIRE_FILE(Win32WriteEntireFile)
{
- Assert(DataIsNonEmpty(Data));
- Assert(IsNullTerminated(Path));
-
- bool Success = false;
- HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (FileHandle != INVALID_HANDLE_VALUE)
+ Assert(DataIsNonEmpty(Data));
+ Assert(IsNullTerminated(Path));
+
+ bool Success = false;
+ HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
+ DWORD BytesWritten = 0;
+ if (WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL))
{
- DWORD BytesWritten = 0;
- if (WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL))
- {
- Success = (BytesWritten == Data.Size);
- }
- else
- {
- gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to write to file: %S", Path);
- MessageBox(NULL, Message.Str, "Error", MB_OK);
- }
- CloseHandle(FileHandle);
+ Success = (BytesWritten == Data.Size);
}
else
{
-
+ gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to write to file: %S", Path);
+ MessageBox(NULL, Message.Str, "Error", MB_OK);
}
+ CloseHandle(FileHandle);
+ }
+ else
+ {
- return Success;
+ }
+
+ return Success;
}
internal FILETIME
GetFileLastWriteTime(char* Path)
{
- FILETIME Result = {};
-
- WIN32_FIND_DATA FindData = {};
- HANDLE FileHandle = FindFirstFileA(Path, &FindData);
-
- if (FileHandle != INVALID_HANDLE_VALUE)
- {
- Result = FindData.ftLastWriteTime;
- FindClose(FileHandle);
- }
- else
- {
- // TODO(Peter): :ErrorLogging
- }
-
- return Result;
+ FILETIME Result = {};
+
+ WIN32_FIND_DATA FindData = {};
+ HANDLE FileHandle = FindFirstFileA(Path, &FindData);
+
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
+ Result = FindData.ftLastWriteTime;
+ FindClose(FileHandle);
+ }
+ else
+ {
+ // TODO(Peter): :ErrorLogging
+ }
+
+ return Result;
}
struct temp_file_list_entry
{
- gs_file_info Info;
- temp_file_list_entry* Next;
+ gs_file_info Info;
+ temp_file_list_entry* Next;
};
struct temp_file_list
{
- temp_file_list_entry* First;
- temp_file_list_entry* Last;
+ temp_file_list_entry* First;
+ temp_file_list_entry* Last;
};
internal void
Win32SetFileInfoFromFindFileData(gs_file_info* Info, WIN32_FIND_DATA FindFileData, gs_const_string SearchPath, gs_memory_arena* Storage)
{
- u32 FileNameLength = CharArrayLength(FindFileData.cFileName);
-
- // NOTE(Peter): String Storage
- // Storing the string in the final storage means we don't have to copy the string later, and all
- // strings will be continguous in memory at the calling site, though they will be before the array
- gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 1);
- PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName);
- NullTerminate(&FileName);
-
- Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh);
- Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime);
- Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime);
- Info->Path = FileName.ConstString;
- Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY);
+ u32 FileNameLength = CharArrayLength(FindFileData.cFileName);
+
+ Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh);
+ Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime);
+ Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime);
+ Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY);
+
+ // NOTE(Peter): String Storage
+ // Storing the string in the final storage means we don't have to copy the string later, and all
+ // strings will be continguous in memory at the calling site, though they will be before the array
+ gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 2);
+ PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName);
+ if (Info->IsDirectory)
+ {
+ AppendPrintF(&FileName, "\\");
+ }
+ NullTerminate(&FileName);
+
+ Info->Path = FileName.ConstString;
}
internal u32
Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* TempList, gs_const_string Path, gs_memory_arena* Storage, b32 Flags)
{
- u32 FilesCount = 0;
-
- s64 IndexOfLastSlash = FindLastFromSet(Path, "\\/");
- Assert(IndexOfLastSlash >= 0);
- gs_const_string SearchPath = Substring(Path, 0, IndexOfLastSlash + 1);
-
- WIN32_FIND_DATA FindFileData;
- HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData);
- if (SearchHandle != INVALID_HANDLE_VALUE)
+ u32 FilesCount = 0;
+ Assert(Path.Str[Path.Length - 1] != '\\' &&
+ Path.Str[Path.Length - 1] != '/');
+ gs_const_string SearchPath = Path;
+
+ gs_const_string SearchPathDir = SearchPath;
+ s64 LastSlash = FindLastFromSet(SearchPath, "\\/");
+ if (LastSlash >= 0)
+ {
+ SearchPathDir = Substring(SearchPath, 0, LastSlash + 1);
+ }
+
+ WIN32_FIND_DATA FindFileData;
+ HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData);
+ if (SearchHandle != INVALID_HANDLE_VALUE)
+ {
+ do
{
- do
+ b32 AddFile = true;
+
+ if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
+ {
+ gs_const_string SubDirName = ConstString(FindFileData.cFileName);
+ bool IsNav = (StringsEqual(SubDirName, ConstString(".")) ||
+ StringsEqual(SubDirName, ConstString("..")));
+
+ if (HasFlag(Flags, EnumerateDirectory_Recurse))
{
- b32 AddFile = true;
-
- if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
- {
- if (HasFlag(Flags, EnumerateDirectory_Recurse))
- {
- gs_const_string SubDirName = ConstString(FindFileData.cFileName);
- if (!StringsEqual(SubDirName, ConstString(".")) &&
- !StringsEqual(SubDirName, ConstString("..")))
- {
- gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3);
- PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName);
- FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, Storage, Flags);
- }
- }
-
- AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories);
- }
-
- if (AddFile)
- {
- temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry);
- *File = {0};
- Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPath, Storage);
- SLLPushOrInit(TempList->First, TempList->Last, File);
- FilesCount += 1;
- }
- }while(FindNextFile(SearchHandle, &FindFileData));
- }
- else
- {
- PrintLastError();
- }
-
- return FilesCount;
+ if (!IsNav)
+ {
+ gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3);
+ PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName);
+ FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString,
+ Storage, Flags);
+ }
+ }
+
+ AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories);
+ }
+
+ if (AddFile)
+ {
+ temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry);
+ *File = {0};
+ Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPathDir, Storage);
+ SLLPushOrInit(TempList->First, TempList->Last, File);
+ FilesCount += 1;
+ }
+ }while(FindNextFile(SearchHandle, &FindFileData));
+ }
+ else
+ {
+ PrintLastError();
+ }
+
+ return FilesCount;
}
ENUMERATE_DIRECTORY(Win32EnumerateDirectory)
{
- Assert(IsNullTerminated(Path));
- gs_file_info_array Result = {};
-
- temp_file_list TempList = {};
- Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags);
-
- Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount);
- for (temp_file_list_entry* FileAt = TempList.First;
- FileAt != 0;
- FileAt = FileAt->Next)
- {
- // NOTE(Peter): We don't copy the file name here because its already in Storage.
- // See String Storage note above ^^
- Result.Values[Result.Count++] = FileAt->Info;
- }
-
- return Result;
+ Assert(IsNullTerminated(Path));
+ gs_file_info_array Result = {};
+
+ temp_file_list TempList = {};
+ Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags);
+
+ Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount);
+ for (temp_file_list_entry* FileAt = TempList.First;
+ FileAt != 0;
+ FileAt = FileAt->Next)
+ {
+ // NOTE(Peter): We don't copy the file name here because its already in Storage.
+ // See String Storage note above ^^
+ Result.Values[Result.Count++] = FileAt->Info;
+ }
+
+ return Result;
}
#define WIN32_FOLDHAUS_FILEIO_H
diff --git a/src/app/platform_win32/win32_foldhaus_memory.h b/src/app/platform_win32/win32_foldhaus_memory.h
index 100dd1a..6dbce1c 100644
--- a/src/app/platform_win32/win32_foldhaus_memory.h
+++ b/src/app/platform_win32/win32_foldhaus_memory.h
@@ -1,36 +1,26 @@
-//
-// File: win32_foldhaus_memory.h
-// Author: Peter Slattery
-// Creation Date: 2020-02-04
-//
-//
-// NOTE: Relies on having imported foldhaus_platform.h prior to this file
-//
-#ifndef WIN32_FOLDHAUS_MEMORY_H
+/* date = May 10th 2021 11:48 pm */
-ALLOCATOR_ALLOC(Win32Alloc)
+#ifndef GS_MEMORY_WIN32_H
+#define GS_MEMORY_WIN32_H
+
+PLATFORM_ALLOC(Win32Alloc)
{
- u8* Result = (u8*)VirtualAlloc(NULL, Size,
- MEM_COMMIT | MEM_RESERVE,
- PAGE_EXECUTE_READWRITE);
- if (ResultSize != 0)
- {
- *ResultSize = Size;
- }
- return Result;
+ u8* Result = (u8*)VirtualAlloc(NULL, Size,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READWRITE);
+ if (ResultSize) *ResultSize = Size;
+ return Result;
}
-ALLOCATOR_FREE(Win32Free)
+PLATFORM_FREE(Win32Free)
{
- b32 Result = VirtualFree(Ptr, 0, MEM_RELEASE);
- if (!Result)
- {
- s32 Error = GetLastError();
- // TODO(Peter): I'm waiting to see an error actually occur here
- // to know what it could possibly be.
- InvalidCodePath;
- }
+ VirtualFree(Base, 0, MEM_RELEASE);
}
-#define WIN32_FOLDHAUS_MEMORY_H
-#endif // WIN32_FOLDHAUS_MEMORY_H
\ No newline at end of file
+internal gs_allocator
+CreatePlatformAllocator()
+{
+ return AllocatorCreate(Win32Alloc, Win32Free, 0);
+}
+
+#endif //GS_MEMORY_WIN32_H
diff --git a/src/app/platform_win32/win32_foldhaus_serial.h b/src/app/platform_win32/win32_foldhaus_serial.h
index 71b4c99..4b309c8 100644
--- a/src/app/platform_win32/win32_foldhaus_serial.h
+++ b/src/app/platform_win32/win32_foldhaus_serial.h
@@ -13,277 +13,367 @@ global s32* Win32SerialPortFilled;
DCB
Win32SerialPort_GetState(HANDLE ComPortHandle)
{
- DEBUG_TRACK_FUNCTION;
- DCB ControlSettings = {0};
- ZeroStruct(&ControlSettings);
- ControlSettings.DCBlength = sizeof(ControlSettings);
-
- bool Success = GetCommState(ComPortHandle, &ControlSettings);
- Assert(Success);
-
- return ControlSettings;
+ DEBUG_TRACK_FUNCTION;
+ DCB ControlSettings = {0};
+ ZeroStruct(&ControlSettings);
+ ControlSettings.DCBlength = sizeof(ControlSettings);
+
+ bool Success = GetCommState(ComPortHandle, &ControlSettings);
+ Assert(Success);
+
+ return ControlSettings;
}
void
Win32SerialPort_SetState(HANDLE ComPortHandle, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits)
{
- DEBUG_TRACK_FUNCTION;
- DCB ControlSettings = Win32SerialPort_GetState(ComPortHandle);
+ DEBUG_TRACK_FUNCTION;
+ DCB ControlSettings = Win32SerialPort_GetState(ComPortHandle);
+
+ // TODO(pjs): Validate BaudRate - There's only certain rates that are valid right?
+ ControlSettings.BaudRate = BaudRate;
+
+ if (Parity == NOPARITY)
+ {
+ ControlSettings.Parity = Parity;
+ ControlSettings.fParity = 0;
+ }
+ if (Parity == EVENPARITY || Parity == ODDPARITY)
+ {
+ ControlSettings.Parity = Parity;
+ ControlSettings.fParity = 1;
+ }
+
+ ControlSettings.StopBits = StopBits;
+ ControlSettings.ByteSize = ByteSize;
+
+ ControlSettings.fBinary = true;
+
+ ControlSettings.fOutxCtsFlow = false;
+ ControlSettings.fOutxDsrFlow = false;
+ ControlSettings.fDtrControl = DTR_CONTROL_DISABLE;
+ ControlSettings.fDsrSensitivity = 0;
+ ControlSettings.fRtsControl = RTS_CONTROL_DISABLE;
+ ControlSettings.fOutX = false;
+ ControlSettings.fInX = false;
+
+ ControlSettings.fErrorChar = 0;
+ ControlSettings.fNull = false;
+ ControlSettings.fAbortOnError = false;
+ ControlSettings.wReserved = false;
+ ControlSettings.XonLim = 2;
+ ControlSettings.XoffLim = 4;
+ ControlSettings.XonChar = 0x13;
+ ControlSettings.XoffChar = 0x19;
+ ControlSettings.EvtChar = 0;
+
+ bool Success = SetCommState(ComPortHandle, &ControlSettings);
+}
+
+gs_const_string_array
+Win32SerialPorts_List(gs_memory_arena* Arena, gs_memory_arena* Transient)
+{
+ gs_const_string_array Result = {};
+
+ DWORD SizeNeeded0 = 0;
+ DWORD CountReturned0 = 0;
+ EnumPorts(NULL, 1, 0, 0, &SizeNeeded0, &CountReturned0);
+ Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+
+ DWORD SizeNeeded1 = 0;
+ DWORD CountReturned1 = 0;
+ PORT_INFO_1* PortsArray = (PORT_INFO_1*)PushSize(Transient, SizeNeeded0).Memory;
+ if (EnumPorts(NULL,
+ 1,
+ (u8*)PortsArray,
+ SizeNeeded0,
+ &SizeNeeded1,
+ &CountReturned1))
+ {
+ Result.CountMax = (u64)CountReturned1;
+ Result.Strings = PushArray(Arena, gs_const_string, Result.CountMax);
- // TODO(pjs): Validate BaudRate - There's only certain rates that are valid right?
- ControlSettings.BaudRate = BaudRate;
-
- if (Parity == NOPARITY)
+ for (; Result.Count < Result.CountMax; Result.Count++)
{
- ControlSettings.Parity = Parity;
- ControlSettings.fParity = 0;
+ u64 Index = Result.Count;
+ u64 StrLen = CStringLength(PortsArray[Index].pName);
+ gs_string Str = PushString(Arena, StrLen);
+ PrintF(&Str, "%.*s", StrLen, PortsArray[Index].pName);
+ Result.Strings[Result.Count] = Str.ConstString;
}
- if (Parity == EVENPARITY || Parity == ODDPARITY)
+ }
+
+ return Result;
+}
+
+bool
+Win32SerialPort_Exists(char* PortName, gs_memory_arena* Transient)
+{
+ bool Result = false;
+ if (PortName != 0)
+ {
+ gs_const_string PortIdent = ConstString(PortName);
+ u32 IdentBegin = FindLast(PortIdent, '\\') + 1;
+ PortIdent = Substring(PortIdent, IdentBegin, PortIdent.Length);
+
+ gs_const_string_array PortsAvailable = Win32SerialPorts_List(Transient, Transient);
+
+ for (u64 i = 0; i < PortsAvailable.Count; i++)
{
- ControlSettings.Parity = Parity;
- ControlSettings.fParity = 1;
+ gs_const_string AvailablePortName = PortsAvailable.Strings[i];
+ if (StringsEqualUpToLength(AvailablePortName, PortIdent, PortIdent.Length))
+ {
+ Result = true;
+ break;
+ }
}
-
- ControlSettings.StopBits = StopBits;
- ControlSettings.ByteSize = ByteSize;
-
- ControlSettings.fBinary = true;
-
- ControlSettings.fOutxCtsFlow = false;
- ControlSettings.fOutxDsrFlow = false;
- ControlSettings.fDtrControl = DTR_CONTROL_DISABLE;
- ControlSettings.fDsrSensitivity = 0;
- ControlSettings.fRtsControl = RTS_CONTROL_DISABLE;
- ControlSettings.fOutX = false;
- ControlSettings.fInX = false;
-
- ControlSettings.fErrorChar = 0;
- ControlSettings.fNull = false;
- ControlSettings.fAbortOnError = false;
- ControlSettings.wReserved = false;
- ControlSettings.XonLim = 2;
- ControlSettings.XoffLim = 4;
- ControlSettings.XonChar = 0x13;
- ControlSettings.XoffChar = 0x19;
- ControlSettings.EvtChar = 0;
-
- bool Success = SetCommState(ComPortHandle, &ControlSettings);
+ }
+ return Result;
}
HANDLE
-Win32SerialPort_Open(char* PortName)
+Win32SerialPort_Open(char* PortName, gs_memory_arena* Transient)
{
- DEBUG_TRACK_FUNCTION;
- HANDLE ComPortHandle = CreateFile(PortName,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, // Default Security Attr
- OPEN_EXISTING,
- 0, // Not overlapped I/O
- NULL);
+ DEBUG_TRACK_FUNCTION;
+ HANDLE ComPortHandle = INVALID_HANDLE_VALUE;;
+
+ if (Win32SerialPort_Exists(PortName, Transient))
+ {
+
+ ComPortHandle = CreateFile(PortName,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, // Default Security Attr
+ OPEN_EXISTING,
+ 0, // Not overlapped I/O
+ NULL);
+
+ bool HasError = false;
if (ComPortHandle != INVALID_HANDLE_VALUE)
{
- COMMTIMEOUTS Timeouts = { 0 };
- Timeouts.ReadIntervalTimeout = 0; // in milliseconds
- Timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds
- Timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds
- Timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds
- Timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds
-
- if (SetCommTimeouts(ComPortHandle, &Timeouts))
- {
-
- }
- else
- {
- s32 Error = GetLastError();
- // TODO(pjs): Error logging
- }
+ COMMTIMEOUTS Timeouts = { 0 };
+ Timeouts.ReadIntervalTimeout = 0; // in milliseconds
+ Timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds
+ Timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds
+ Timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds
+ Timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds
+
+ HasError = !SetCommTimeouts(ComPortHandle, &Timeouts);
}
else
{
- // Error
- s32 Error = GetLastError();
- // TODO(pjs): Error logging
+ HasError = true;
}
- return ComPortHandle;
+ if (HasError)
+ {
+ // Error
+ s32 Error = GetLastError();
+ switch (Error)
+ {
+ case ERROR_INVALID_FUNCTION:
+ case ERROR_NO_SUCH_DEVICE:
+ case ERROR_FILE_NOT_FOUND:
+ {
+ // NOTE(PS): The outer scope should handle these cases
+ ComPortHandle = INVALID_HANDLE_VALUE;
+ }break;
+
+ InvalidDefaultCase;
+ }
+ }
+ }
+
+ return ComPortHandle;
}
void
Win32SerialPort_Close(HANDLE PortHandle)
{
- CloseHandle(PortHandle);
+ CloseHandle(PortHandle);
}
bool
Win32SerialPort_Write(HANDLE PortHandle, gs_data Buffer)
{
- DEBUG_TRACK_FUNCTION;
- Assert(PortHandle != INVALID_HANDLE_VALUE);
- bool Success = false;
-
- DWORD BytesWritten = 0;
- if (WriteFile(PortHandle, Buffer.Memory, Buffer.Size, &BytesWritten, NULL))
+ DEBUG_TRACK_FUNCTION;
+ Assert(PortHandle != INVALID_HANDLE_VALUE);
+ bool Success = false;
+
+ DWORD BytesWritten = 0;
+ if (WriteFile(PortHandle, Buffer.Memory, Buffer.Size, &BytesWritten, NULL))
+ {
+ Success = (BytesWritten == Buffer.Size);
+ if (!Success)
{
- Success = (BytesWritten == Buffer.Size);
- if (!Success)
- {
- OutputDebugString("Error: Entire buffer not written.\n");
- }
+ Log_Error(GlobalLogBuffer, "Error: Entire buffer not written.\n");
}
- else
+ }
+ else
+ {
+ Log_Error(GlobalLogBuffer, "Error: Unable to write to port\n");
+ s32 Error = GetLastError();
+ switch (Error)
{
- OutputDebugStringA("Error: Unable to write to port\n");
- s32 Error = GetLastError();
- switch (Error)
- {
- case ERROR_OPERATION_ABORTED:
- case ERROR_GEN_FAILURE:
- {
- // NOTE(pjs): Probably means that the serial port became invalid
- // ie. the usb stick was removed
- }break;
-
- case ERROR_INVALID_HANDLE:
- InvalidDefaultCase;
- }
+ case ERROR_OPERATION_ABORTED:
+ case ERROR_GEN_FAILURE:
+ {
+ // NOTE(pjs): Probably means that the serial port became invalid
+ // ie. the usb stick was removed
+ }break;
+
+ case ERROR_ACCESS_DENIED:
+ {
+ // ??
+ }break;
+
+ case ERROR_NO_SUCH_DEVICE:
+ {
+ }break;
+
+ case ERROR_INVALID_HANDLE:
+ InvalidDefaultCase;
}
-
- return Success;
+ }
+
+ return Success;
}
bool
Win32SerialPort_SetRead(HANDLE PortHandle)
{
- bool Status = SetCommMask(PortHandle, EV_RXCHAR);
- return Status;
+ bool Status = SetCommMask(PortHandle, EV_RXCHAR);
+ return Status;
}
u32
Win32SerialPort_ReadMessageWhenReady(HANDLE PortHandle, gs_data Data)
{
- u32 ReadSize = 0;
-
- DWORD EventMask = 0;
- bool Status = WaitCommEvent(PortHandle, &EventMask, NULL);
- if (Status)
+ u32 ReadSize = 0;
+
+ DWORD EventMask = 0;
+ bool Status = WaitCommEvent(PortHandle, &EventMask, NULL);
+ if (Status)
+ {
+ DWORD NoBytesRead = 0;
+ do
{
- DWORD NoBytesRead = 0;
- do
- {
- u8 Byte = 0;
- Status = ReadFile(PortHandle, &Byte, sizeof(char), &NoBytesRead, NULL);
- Data.Memory[ReadSize] = Byte;
- ReadSize++;
- }
- while (NoBytesRead > 0 && ReadSize < Data.Size);
+ u8 Byte = 0;
+ Status = ReadFile(PortHandle, &Byte, sizeof(char), &NoBytesRead, NULL);
+ Data.Memory[ReadSize] = Byte;
+ ReadSize++;
}
- //Read data and store in a buffer
-
- return ReadSize;
+ while (NoBytesRead > 0 && ReadSize < Data.Size);
+ }
+ //Read data and store in a buffer
+
+ return ReadSize;
}
/////////////////////////
// Win32SerialArray
void
-Win32SerialArray_Create(gs_thread_context Context)
+Win32SerialArray_Create(gs_memory_arena* A)
{
- DEBUG_TRACK_FUNCTION;
-
- Win32SerialHandlesCountMax = 32;
-
- Win32SerialHandles = AllocatorAllocArray(Context.Allocator, HANDLE, Win32SerialHandlesCountMax);
- Win32SerialPortNames = AllocatorAllocArray(Context.Allocator, gs_string, Win32SerialHandlesCountMax);
- Win32SerialPortFilled = AllocatorAllocArray(Context.Allocator, s32, Win32SerialHandlesCountMax);
-
- for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
- {
- Win32SerialPortNames[i] = AllocatorAllocString(Context.Allocator, 256);
- Win32SerialPortFilled[i] = 0;
- }
+ DEBUG_TRACK_FUNCTION;
+
+ Win32SerialHandlesCountMax = 32;
+
+ Win32SerialHandles = PushArray(A, HANDLE, Win32SerialHandlesCountMax);
+ Win32SerialPortNames = PushArray(A, gs_string, Win32SerialHandlesCountMax);
+ Win32SerialPortFilled = PushArray(A, s32, Win32SerialHandlesCountMax);
+
+ u64 PortNameSize = 256;
+ u64 PortNameBufferSize = PortNameSize * Win32SerialHandlesCountMax;
+ char* PortNameBuffer = PushArray(A, char, PortNameBufferSize);
+ for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
+ {
+ char* NameBase = PortNameBuffer + (PortNameSize * i);
+ Win32SerialPortNames[i] = MakeString(NameBase, 0, PortNameSize);
+ Win32SerialPortFilled[i] = 0;
+ }
}
void
Win32SerialArray_Push(HANDLE SerialHandle, gs_const_string PortName)
{
- DEBUG_TRACK_FUNCTION;
-
- bool Found = false;
- for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
+ DEBUG_TRACK_FUNCTION;
+
+ bool Found = false;
+ for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
+ {
+ bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + i, 1, 0);
+ if (!WasFilled)
{
- bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + i, 1, 0);
- if (!WasFilled)
- {
- Win32SerialHandles[i] = SerialHandle;
- PrintF(&Win32SerialPortNames[i], "%S", PortName);
- Found = true;
- break;
- }
+ Win32SerialHandles[i] = SerialHandle;
+ PrintF(&Win32SerialPortNames[i], "%S", PortName);
+ Found = true;
+ break;
}
- Assert(Found);
+ }
+ Assert(Found);
}
void
Win32SerialArray_Pop(u32 Index)
{
- bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + Index, 0, 1);
- Assert(WasFilled);
- Win32SerialPortFilled[Index] = false;
- Win32SerialHandles[Index] = INVALID_HANDLE_VALUE;
+ bool WasFilled = InterlockedCompareExchange((LONG volatile*)Win32SerialPortFilled + Index, 0, 1);
+ Assert(WasFilled);
+ Win32SerialPortFilled[Index] = false;
+ Win32SerialHandles[Index] = INVALID_HANDLE_VALUE;
}
HANDLE
Win32SerialArray_Get(gs_const_string PortName)
{
- DEBUG_TRACK_FUNCTION;
-
- HANDLE PortHandle = INVALID_HANDLE_VALUE;
- for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
+ DEBUG_TRACK_FUNCTION;
+
+ HANDLE PortHandle = INVALID_HANDLE_VALUE;
+ for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
+ {
+ if (Win32SerialPortFilled[i] &&
+ StringsEqual(Win32SerialPortNames[i].ConstString, PortName))
{
- if (Win32SerialPortFilled[i] &&
- StringsEqual(Win32SerialPortNames[i].ConstString, PortName))
- {
- PortHandle = Win32SerialHandles[i];
- break;
- }
+ PortHandle = Win32SerialHandles[i];
+ break;
}
- return PortHandle;
+ }
+ return PortHandle;
}
HANDLE
-Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits)
+Win32SerialArray_GetOrOpen(gs_const_string PortName, u32 BaudRate, u8 ByteSize, u8 Parity, u8 StopBits, gs_memory_arena* Transient)
{
- DEBUG_TRACK_FUNCTION;
-
- HANDLE PortHandle = Win32SerialArray_Get(PortName);
- if (PortHandle == INVALID_HANDLE_VALUE)
+ DEBUG_TRACK_FUNCTION;
+
+ HANDLE PortHandle = Win32SerialArray_Get(PortName);
+ if (PortHandle == INVALID_HANDLE_VALUE)
+ {
+ Assert(IsNullTerminated(PortName));
+ PortHandle = Win32SerialPort_Open(PortName.Str, Transient);
+ if (PortHandle != INVALID_HANDLE_VALUE)
{
- Assert(IsNullTerminated(PortName));
- PortHandle = Win32SerialPort_Open(PortName.Str);
- if (PortHandle != INVALID_HANDLE_VALUE)
- {
- Win32SerialPort_SetState(PortHandle, BaudRate, ByteSize, Parity, StopBits);
- Win32SerialArray_Push(PortHandle, PortName);
- }
+ Win32SerialPort_SetState(PortHandle, BaudRate, ByteSize, Parity, StopBits);
+ Win32SerialArray_Push(PortHandle, PortName);
}
- return PortHandle;
+ }
+ return PortHandle;
}
void
Win32SerialArray_Close(gs_const_string PortName)
{
- for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
+ for (u32 i = 0; i < Win32SerialHandlesCountMax; i++)
+ {
+ if (Win32SerialPortFilled[i] && StringsEqual(Win32SerialPortNames[i].ConstString, PortName))
{
- if (Win32SerialPortFilled[i] && StringsEqual(Win32SerialPortNames[i].ConstString, PortName))
- {
- Win32SerialPort_Close(Win32SerialHandles[i]);
- Win32SerialArray_Pop(i);
- break;
- }
+ Win32SerialPort_Close(Win32SerialHandles[i]);
+ Win32SerialArray_Pop(i);
+ break;
}
+ }
}
#define WIN32_SERIAL_H
diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h
index b42c8c9..1f05067 100644
--- a/src/app/platform_win32/win32_foldhaus_socket.h
+++ b/src/app/platform_win32/win32_foldhaus_socket.h
@@ -7,14 +7,14 @@
struct win32_socket
{
- SOCKET Socket;
+ SOCKET Socket;
};
struct win32_socket_array
{
- win32_socket* Values;
- s32 CountMax;
- s32 Count;
+ win32_socket* Values;
+ s32 CountMax;
+ s32 Count;
};
global WSADATA WSAData;
@@ -27,28 +27,28 @@ global win32_socket_array Win32Sockets;
internal win32_socket_array
Win32SocketArray_Create(u32 CountMax, gs_memory_arena* Storage)
{
- win32_socket_array Result = {};
- Result.CountMax = CountMax;
- Result.Values = PushArray(Storage, win32_socket, CountMax);
- return Result;
+ win32_socket_array Result = {};
+ Result.CountMax = CountMax;
+ Result.Values = PushArray(Storage, win32_socket, CountMax);
+ return Result;
}
internal s32
Win32SocketArray_Take(win32_socket_array* Array)
{
- Assert(Array->Count < Array->CountMax);
- s32 Result = Array->Count++;
- win32_socket* Socket = Array->Values + Result;
- *Socket = {0};
- return Result;
+ Assert(Array->Count < Array->CountMax);
+ s32 Result = Array->Count++;
+ win32_socket* Socket = Array->Values + Result;
+ *Socket = {0};
+ return Result;
}
internal win32_socket*
Win32SocketArray_Get(win32_socket_array Array, s32 Index)
{
- Assert(Index < Array.Count);
- win32_socket* Result = Array.Values + Index;
- return Result;
+ Assert(Index < Array.Count);
+ win32_socket* Result = Array.Values + Index;
+ return Result;
}
//////////////////////
@@ -58,409 +58,448 @@ Win32SocketArray_Get(win32_socket_array Array, s32 Index)
internal win32_socket
Win32Socket_Create(s32 AddressFamily, s32 Type, s32 Protocol)
{
- win32_socket Result = {0};
- Result.Socket = socket(AddressFamily, Type, Protocol);
- if (Result.Socket == INVALID_SOCKET)
- {
- s32 Error = WSAGetLastError();
- InvalidCodePath;
- }
- return Result;
+ win32_socket Result = {0};
+ Result.Socket = socket(AddressFamily, Type, Protocol);
+ if (Result.Socket == INVALID_SOCKET)
+ {
+ s32 Error = WSAGetLastError();
+ InvalidCodePath;
+ }
+ return Result;
}
internal void
Win32Socket_Bind(win32_socket* Socket, s32 AddressFamily, char* Address, s32 Port)
{
- sockaddr_in Service = {0};
- Service.sin_family = AddressFamily;
- Service.sin_addr.s_addr = inet_addr(Address);
- Service.sin_port = htons(Port);
-
- s32 Result = bind(Socket->Socket, (SOCKADDR*)&Service, sizeof(Service));
- if (Result == SOCKET_ERROR)
- {
- s32 Error = WSAGetLastError();
- InvalidCodePath;
- }
+ sockaddr_in Service = {0};
+ Service.sin_family = AddressFamily;
+ Service.sin_addr.s_addr = inet_addr(Address);
+ Service.sin_port = htons(Port);
+
+ s32 Result = bind(Socket->Socket, (SOCKADDR*)&Service, sizeof(Service));
+ if (Result == SOCKET_ERROR)
+ {
+ s32 Error = WSAGetLastError();
+ InvalidCodePath;
+ }
}
internal win32_socket
Win32Socket_ConnectToAddress(char* Address, char* DefaultPort)
{
- win32_socket Result = {};
-
- addrinfo Hints = {0};
- Hints.ai_family = AF_UNSPEC;
- Hints.ai_socktype = SOCK_STREAM;
- Hints.ai_protocol = IPPROTO_TCP;
-
- addrinfo* PotentialConnections;
- s32 Error = getaddrinfo(Address, DefaultPort, &Hints, &PotentialConnections);
- if (Error == 0)
- {
- for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next)
- {
- win32_socket Socket = Win32Socket_Create(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol);
- if (Socket.Socket == INVALID_SOCKET)
- {
- Error = WSAGetLastError();
- InvalidCodePath;
- }
-
- Error = connect(Socket.Socket, InfoAt->ai_addr, (int)InfoAt->ai_addrlen);
- if (Error == SOCKET_ERROR)
- {
- closesocket(Socket.Socket);
- continue;
- }
- else
- {
- Result = Socket;
- break;
- }
- }
- }
- else
+ win32_socket Result = {};
+
+ addrinfo Hints = {0};
+ Hints.ai_family = AF_UNSPEC;
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_protocol = IPPROTO_TCP;
+
+ addrinfo* PotentialConnections;
+ s32 Error = getaddrinfo(Address, DefaultPort, &Hints, &PotentialConnections);
+ if (Error == 0)
+ {
+ for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next)
{
+ win32_socket Socket = Win32Socket_Create(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol);
+ if (Socket.Socket == INVALID_SOCKET)
+ {
Error = WSAGetLastError();
InvalidCodePath;
+ }
+
+ Error = connect(Socket.Socket, InfoAt->ai_addr, (int)InfoAt->ai_addrlen);
+ if (Error == SOCKET_ERROR)
+ {
+ closesocket(Socket.Socket);
+ continue;
+ }
+ else
+ {
+ Result = Socket;
+ break;
+ }
}
-
- freeaddrinfo(PotentialConnections);
-
- return Result;
+ }
+ else
+ {
+ Error = WSAGetLastError();
+ InvalidCodePath;
+ }
+
+ freeaddrinfo(PotentialConnections);
+
+ return Result;
}
internal bool
-Win32ConnectSocket(platform_socket* Socket)
+Win32ConnectSocket(platform_socket_manager* Manager, platform_socket* Socket)
{
- bool Result = false;
-
- addrinfo Hints = {0};
- Hints.ai_family = AF_UNSPEC;
- Hints.ai_socktype = SOCK_STREAM;
- Hints.ai_protocol = IPPROTO_TCP;
-
- addrinfo* PotentialConnections;
- s32 Error = getaddrinfo(Socket->Addr, Socket->Port, &Hints, &PotentialConnections);
- if (Error == 0)
- {
- for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next)
- {
- SOCKET SocketHandle = socket(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol);
- if (SocketHandle == INVALID_SOCKET)
- {
- Error = WSAGetLastError();
- InvalidCodePath;
- }
-
- // If iMode == 0, blocking is enabled
- // if iMode != 0, non-blocking mode is enabled
- u_long iMode = 1;
- Error = ioctlsocket(SocketHandle, FIONBIO, &iMode);
- if (Error != NO_ERROR)
- {
- InvalidCodePath;
- }
-
- Error = connect(SocketHandle, InfoAt->ai_addr, (int)InfoAt->ai_addrlen);
- if (Error == SOCKET_ERROR)
- {
- u32 Status = WSAGetLastError();
- if (Status == WSAEWOULDBLOCK)
- {
-#if 0
- TIMEVAL Timeout = { 0, 500 };
- fd_set SocketSet = {};
- FD_ZERO(&SocketSet);
- FD_SET(SocketHandle, &SocketSet);
- Assert(FD_ISSET(SocketHandle, &SocketSet));
- Status = select(0, &SocketSet, 0, 0, (const TIMEVAL*)&Timeout);
- if (Status == SOCKET_ERROR)
- {
-
- }
- else if (Status == 0)
- {
- }
- else
- {
-
- }
-#endif
- }
- else
- {
- closesocket(SocketHandle);
- continue;
- }
- }
-
- Socket->PlatformHandle = (u8*)Win32Alloc(sizeof(SOCKET), 0);
- *(SOCKET*)Socket->PlatformHandle = SocketHandle;
- Result = true;
- break;
- }
- }
- else
+ bool Result = false;
+
+ addrinfo Hints = {0};
+ Hints.ai_family = AF_UNSPEC;
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_protocol = IPPROTO_TCP;
+
+ addrinfo* PotentialConnections;
+ s32 Error = getaddrinfo(Socket->Addr, Socket->Port, &Hints, &PotentialConnections);
+ if (Error == 0)
+ {
+ for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next)
{
+ SOCKET SocketHandle = socket(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol);
+ if (SocketHandle == INVALID_SOCKET)
+ {
Error = WSAGetLastError();
InvalidCodePath;
- }
-
- if (!Result)
- {
- Assert(Socket->PlatformHandle == 0);
- }
-
- freeaddrinfo(PotentialConnections);
- return Result;
-}
-
-internal bool
-Win32CloseSocket(platform_socket* Socket)
-{
- SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
- closesocket(*Win32Sock);
- Win32Free((u8*)Socket->PlatformHandle, sizeof(SOCKET));
- *Socket = {};
- return true;
-}
-
-internal bool
-Win32SocketQueryStatus(platform_socket* Socket)
-{
- SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
- bool Result = (*Win32Sock != INVALID_SOCKET);
- return Result;
-}
-
-internal u32
-Win32SocketPeek(platform_socket* Socket)
-{
- u32 Result = 0;
- s32 Flags = MSG_PEEK;
- SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
- char Temp[4];
- u32 TempSize = 4;
-
- s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags);
- if (BytesQueued != SOCKET_ERROR)
- {
- Result = (u32)BytesQueued;
- }
- else
- {
- // TODO(pjs): Error handling
- s32 Error = WSAGetLastError();
- switch (Error)
- {
- case WSAEWOULDBLOCK:
- case WSAENOTCONN:
- {
- }break;
-
- InvalidDefaultCase;
- }
- }
- return Result;
-}
-
-internal gs_data
-Win32SocketReceive(platform_socket* Socket, gs_memory_arena* Storage)
-{
- // TODO(pjs): Test this first code path when you have data running - it should
- // get the actual size of the data packet being sent
-#if 0
- gs_data Result = {};
- s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket);
- if (BytesQueued > 0)
- {
- Result = PushSizeToData(Storage, BytesQueued);
- s32 Flags = 0;
- s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
- Assert(BytesReceived == BytesQueued);
- }
- return Result;
-#else
- gs_data Result = PushSizeToData(Storage, 1024);
- s32 Flags = 0;
- SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
- s32 BytesReceived = recv(*Win32Sock, (char*)Result.Memory, Result.Size, Flags);
- if (BytesReceived == SOCKET_ERROR)
- {
- // TODO(pjs): Error logging
- s32 Error = WSAGetLastError();
+ }
+
+ // If iMode == 0, blocking is enabled
+ // if iMode != 0, non-blocking mode is enabled
+ u_long iMode = 0;
+ Error = ioctlsocket(SocketHandle, FIONBIO, &iMode);
+ if (Error != NO_ERROR)
+ {
InvalidCodePath;
- }
- Result.Size = BytesReceived;
- return Result;
+ }
+
+ Error = connect(SocketHandle, InfoAt->ai_addr, (int)InfoAt->ai_addrlen);
+ if (Error == SOCKET_ERROR)
+ {
+ u32 Status = WSAGetLastError();
+ if (Status == WSAEWOULDBLOCK)
+ {
+ // Non-blocking sockets
+#if 0
+ TIMEVAL Timeout = { 0, 500 };
+ fd_set SocketSet = {};
+ FD_ZERO(&SocketSet);
+ FD_SET(SocketHandle, &SocketSet);
+ Assert(FD_ISSET(SocketHandle, &SocketSet));
+ Status = select(0, &SocketSet, 0, 0, (const TIMEVAL*)&Timeout);
+ if (Status == SOCKET_ERROR)
+ {
+
+ }
+ else if (Status == 0)
+ {
+ }
+ else
+ {
+
+ }
#endif
-}
-
-internal s32
-Win32SocketSend(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags)
-{
- SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
-
- sockaddr_in SockAddress = {};
- SockAddress.sin_family = AF_INET;
- SockAddress.sin_port = HostToNetU16(Port);
- SockAddress.sin_addr.s_addr = HostToNetU32(Address);
-
- s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in));
-
- if (LengthSent == SOCKET_ERROR)
- {
- s32 Error = WSAGetLastError();
- if (Error == 10051)
- {
- }
- if (Error == 10053)
- {
- // TODO(pjs): WSAECONNABORTED
- InvalidCodePath;
}
else
{
- // TODO(Peter): :ErrorLogging
- InvalidCodePath;
+ closesocket(SocketHandle);
+ continue;
}
+ }
+
+ Socket->PlatformHandle = (u8*)Win32Alloc(sizeof(SOCKET), 0, 0);
+ *(SOCKET*)Socket->PlatformHandle = SocketHandle;
+ Result = true;
+ break;
}
-
- return LengthSent;
+ }
+ else
+ {
+ Error = WSAGetLastError();
+ InvalidCodePath;
+ }
+
+ if (!Result)
+ {
+ Assert(Socket->PlatformHandle == 0);
+ }
+
+ freeaddrinfo(PotentialConnections);
+ return Result;
+}
+
+internal bool
+Win32CloseSocket(platform_socket_manager* Manager, platform_socket* Socket)
+{
+ SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
+ closesocket(*Win32Sock);
+ Win32Free((u8*)Socket->PlatformHandle, sizeof(SOCKET), 0);
+ *Socket = {};
+ return true;
+}
+
+internal bool
+Win32SocketQueryStatus(platform_socket_manager* Manager, platform_socket* Socket)
+{
+ SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
+ bool Result = (*Win32Sock != INVALID_SOCKET);
+ return Result;
+}
+
+internal u32
+Win32SocketPeek(platform_socket_manager* Manager, platform_socket* Socket)
+{
+ u32 Result = 0;
+ s32 Flags = MSG_PEEK;
+ SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
+ char Temp[4];
+ u32 TempSize = 4;
+
+ //Log_Message(GlobalLogBuffer, "Pre Peek...");
+ //s32 BytesQueued = recv(*Win32Sock, Temp, TempSize, Flags);
+ u_long BytesQueued = 0;
+ ioctlsocket(*Win32Sock, FIONREAD, &BytesQueued);
+ //Log_Message(GlobalLogBuffer, "Post Peek\n");
+
+ if (BytesQueued != SOCKET_ERROR)
+ {
+ Result = (u32)BytesQueued;
+ }
+ else
+ {
+ s32 Error = WSAGetLastError();
+ switch (Error)
+ {
+ case WSAEWOULDBLOCK:
+ {
+ // NOTE(PS): This case covers non-blocking sockets
+ // if we peek and there's nothing there, it returns
+ // this error code. MSDN says its a non-fatal error
+ // and the operation should be retried later
+ Result = 0;
+ } break;
+
+ case WSAENOTCONN:
+ case WSAECONNRESET:
+ case WSAECONNABORTED:
+ {
+ CloseSocket(Manager, Socket);
+ }break;
+
+ InvalidDefaultCase;
+ }
+ }
+ return (s32)Result;
+}
+
+internal gs_data
+Win32SocketReceive(platform_socket_manager* Manager, platform_socket* Socket, gs_memory_arena* Storage)
+{
+ // TODO(pjs): Test this first code path when you have data running - it should
+ // get the actual size of the data packet being sent
+#if 0
+ gs_data Result = {};
+ s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket);
+ if (BytesQueued > 0)
+ {
+ Result = PushSizeToData(Storage, BytesQueued);
+ s32 Flags = 0;
+ s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
+ Assert(BytesReceived == BytesQueued);
+ }
+ return Result;
+#else
+ gs_data Result = PushSize(Storage, 1024);
+ s32 Flags = 0;
+ SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
+ s32 BytesReceived = recv(*Win32Sock, (char*)Result.Memory, Result.Size, Flags);
+ if (BytesReceived == SOCKET_ERROR)
+ {
+ // TODO(pjs): Error logging
+ s32 Error = WSAGetLastError();
+ InvalidCodePath;
+ }
+ Result.Size = BytesReceived;
+ return Result;
+#endif
+}
+
+
+internal s32
+Win32SocketSend(platform_socket_manager* Manager, platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags)
+{
+ SOCKET* Win32Sock = (SOCKET*)Socket->PlatformHandle;
+
+ sockaddr_in SockAddress = {};
+ SockAddress.sin_family = AF_INET;
+ SockAddress.sin_port = HostToNetU16(Port);
+ SockAddress.sin_addr.s_addr = HostToNetU32(Address);
+
+ s32 LengthSent = sendto(*Win32Sock, (char*)Data.Memory, Data.Size, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in));
+
+ //Log_Message(GlobalLogBuffer, "Attempting To Send Network Data: ");
+ if (LengthSent == SOCKET_ERROR)
+ {
+ s32 Error = WSAGetLastError();
+ switch (Error)
+ {
+ case WSAEWOULDBLOCK:
+ {
+ // NOTE(PS): This covers non-blocking sockets
+ // In this case the message should be tried again
+ LengthSent = 0;
+ //Log_Message(GlobalLogBuffer, "Not sent, buffered\n");
+ }break;
+
+ case WSAECONNABORTED:
+ case WSAENETUNREACH:
+ case WSAECONNRESET:
+ case WSAENOTCONN:
+ {
+ if (CloseSocket(Manager, Socket))
+ {
+ Log_Error(GlobalLogBuffer, "Error: %d\n", Error);
+ Error = 0;
+ }
+ }break;
+
+ InvalidDefaultCase;
+ }
+ }
+ else
+ {
+ Log_Message(GlobalLogBuffer, "Sent\n");
+ }
+
+ return LengthSent;
}
internal s32
Win32Socket_SetOption(win32_socket* Socket, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength)
{
- int Error = setsockopt(Socket->Socket, Level, Option, OptionValue, OptionLength);
- if (Error == SOCKET_ERROR)
- {
- Error = WSAGetLastError();
- // TODO(Peter): :ErrorLogging
- }
-
- return Error;
+ int Error = setsockopt(Socket->Socket, Level, Option, OptionValue, OptionLength);
+ if (Error == SOCKET_ERROR)
+ {
+ Error = WSAGetLastError();
+ // TODO(Peter): :ErrorLogging
+ }
+
+ return Error;
}
internal s32
Win32Socket_SetOption(platform_socket_handle SocketHandle, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength)
{
- win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle);
- return Win32Socket_SetOption(Socket, Level, Option, OptionValue, OptionLength);
+ win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle);
+ return Win32Socket_SetOption(Socket, Level, Option, OptionValue, OptionLength);
}
internal s32
Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, const char* Buffer, s32 BufferLength, s32 Flags)
{
- win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle);
-
- sockaddr_in SockAddress = {};
- SockAddress.sin_family = AF_INET;
- SockAddress.sin_port = HostToNetU16(Port);
- SockAddress.sin_addr.s_addr = HostToNetU32(Address);
-
- s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in));
-
- if (LengthSent == SOCKET_ERROR)
+ win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, (s32)SocketHandle);
+
+ sockaddr_in SockAddress = {};
+ SockAddress.sin_family = AF_INET;
+ SockAddress.sin_port = HostToNetU16(Port);
+ SockAddress.sin_addr.s_addr = HostToNetU32(Address);
+
+ s32 LengthSent = sendto(Socket->Socket, Buffer, BufferLength, Flags, (sockaddr*)&SockAddress, sizeof(sockaddr_in));
+
+ if (LengthSent == SOCKET_ERROR)
+ {
+ s32 Error = WSAGetLastError();
+ switch (Error)
{
- s32 Error = WSAGetLastError();
- if (Error == 10051)
- {
- }
- else
- {
- // TODO(Peter): :ErrorLogging
- InvalidCodePath;
- }
+ case WSAENETUNREACH:
+ {
+ Log_Message(GlobalLogBuffer,
+ "Non Critical Error: WSAENETUNREACH \n");
+ } break;
+
+ default:
+ {
+ Log_Error(GlobalLogBuffer, "Error: %d \n", Error);
+ InvalidCodePath;
+ } break;
}
-
- return LengthSent;
+ }
+
+ return LengthSent;
}
internal void
Win32Socket_SetListening(win32_socket* Socket)
{
- if (listen(Socket->Socket, SOMAXCONN) == SOCKET_ERROR)
- {
- // TODO(pjs): Error logging
- s32 Error = WSAGetLastError();
- InvalidCodePath;
- }
+ if (listen(Socket->Socket, SOMAXCONN) == SOCKET_ERROR)
+ {
+ // TODO(pjs): Error logging
+ s32 Error = WSAGetLastError();
+ InvalidCodePath;
+ }
}
internal s32
Win32Socket_PeekGetTotalSize(win32_socket* Socket)
{
- s32 Flags = MSG_PEEK;
- char Temp[4];
- s32 BytesQueued = recv(Socket->Socket, Temp, 4, Flags);
- if (BytesQueued == SOCKET_ERROR)
- {
- // TODO(pjs): Error logging
- s32 Error = WSAGetLastError();
- BytesQueued = 0;
- }
- return BytesQueued;
+ s32 Flags = MSG_PEEK;
+ char Temp[4];
+ s32 BytesQueued = recv(Socket->Socket, Temp, 4, Flags);
+ if (BytesQueued == SOCKET_ERROR)
+ {
+ // TODO(pjs): Error logging
+ s32 Error = WSAGetLastError();
+ BytesQueued = 0;
+ }
+ return BytesQueued;
}
internal gs_data
Win32Socket_Receive(win32_socket* Socket, gs_memory_arena* Storage)
{
#if 0
- gs_data Result = {};
- s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket);
- if (BytesQueued > 0)
- {
- Result = PushSizeToData(Storage, BytesQueued);
- s32 Flags = 0;
- s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
- Assert(BytesReceived == BytesQueued);
- }
- return Result;
-#else
- gs_data Result = PushSizeToData(Storage, 1024);
+ gs_data Result = {};
+ s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket);
+ if (BytesQueued > 0)
+ {
+ Result = PushSizeToData(Storage, BytesQueued);
s32 Flags = 0;
s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
- if (BytesReceived == SOCKET_ERROR)
+ Assert(BytesReceived == BytesQueued);
+ }
+ return Result;
+#else
+ gs_data Result = PushSize(Storage, 1024);
+ s32 Flags = 0;
+ s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
+ if (BytesReceived == SOCKET_ERROR)
+ {
+ // TODO(pjs): Error logging
+ s32 Error = WSAGetLastError();
+ switch (Error)
{
- // TODO(pjs): Error logging
- s32 Error = WSAGetLastError();
- switch (Error)
- {
- case WSAECONNABORTED:
- case WSANOTINITIALISED:
- break;
-
- case WSAENOTCONN:
- {
-
- }break;
- InvalidDefaultCase;
- }
+ case WSAECONNABORTED:
+ case WSANOTINITIALISED:
+ break;
+
+ case WSAENOTCONN:
+ {
+
+ }break;
+ InvalidDefaultCase;
}
- Result.Size = BytesReceived;
- return Result;
+ }
+ Result.Size = BytesReceived;
+ return Result;
#endif
}
internal void
Win32Socket_Close(win32_socket* Socket)
{
- closesocket(Socket->Socket);
- Socket->Socket = INVALID_SOCKET;
+ closesocket(Socket->Socket);
+ Socket->Socket = INVALID_SOCKET;
}
internal void
Win32Socket_CloseArray(win32_socket_array Array)
{
- for (s32 i = 0; i < Array.Count; i++)
- {
- win32_socket* Socket = Array.Values + i;
- Win32Socket_Close(Socket);
- }
+ for (s32 i = 0; i < Array.Count; i++)
+ {
+ win32_socket* Socket = Array.Values + i;
+ Win32Socket_Close(Socket);
+ }
}
//////////////////////
@@ -470,30 +509,30 @@ Win32Socket_CloseArray(win32_socket_array Array)
internal void
Win32SocketSystem_Init(gs_memory_arena* Arena)
{
- WSAStartup(MAKEWORD(2, 2), &WSAData);
- Win32Sockets = Win32SocketArray_Create(16, Arena);
+ WSAStartup(MAKEWORD(2, 2), &WSAData);
+ Win32Sockets = Win32SocketArray_Create(16, Arena);
}
internal void
Win32SocketSystem_Cleanup()
{
- Win32Socket_CloseArray(Win32Sockets);
-
- s32 CleanupResult = 0;
- do {
- CleanupResult = WSACleanup();
- }while(CleanupResult == SOCKET_ERROR);
+ Win32Socket_CloseArray(Win32Sockets);
+
+ s32 CleanupResult = 0;
+ do {
+ CleanupResult = WSACleanup();
+ }while(CleanupResult == SOCKET_ERROR);
}
PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle)
{
- s32 Result = Win32SocketArray_Take(&Win32Sockets);
- s32 Error = 0;
- win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, Result);
- *Socket = Win32Socket_Create(AF_INET, SOCK_DGRAM, 0);
- Error = Win32Socket_SetOption(Socket, IPPROTO_IP, IP_MULTICAST_TTL,
- (const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive));
- return (platform_socket_handle)Result;
+ s32 Result = Win32SocketArray_Take(&Win32Sockets);
+ s32 Error = 0;
+ win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, Result);
+ *Socket = Win32Socket_Create(AF_INET, SOCK_DGRAM, 0);
+ Error = Win32Socket_SetOption(Socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ (const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive));
+ return (platform_socket_handle)Result;
}
#define WIN32_FOLDHAUS_SOCKET_H
diff --git a/src/app/platform_win32/win32_foldhaus_timing.h b/src/app/platform_win32/win32_foldhaus_timing.h
index 6d04a31..dee60bf 100644
--- a/src/app/platform_win32/win32_foldhaus_timing.h
+++ b/src/app/platform_win32/win32_foldhaus_timing.h
@@ -11,33 +11,33 @@
internal s64
GetPerformanceFrequency ()
{
- LARGE_INTEGER Frequency;
- if (!QueryPerformanceFrequency(&Frequency))
- {
- s32 Error = GetLastError();
- // TODO(Peter): I'm waiting to see an error actually occur here
- // to know what it could possibly be.
- InvalidCodePath;
- }
- return (s64)Frequency.QuadPart;
+ LARGE_INTEGER Frequency;
+ if (!QueryPerformanceFrequency(&Frequency))
+ {
+ s32 Error = GetLastError();
+ // TODO(Peter): I'm waiting to see an error actually occur here
+ // to know what it could possibly be.
+ InvalidCodePath;
+ }
+ return (s64)Frequency.QuadPart;
}
internal s64
GetWallClock ()
{
#if 0
- s64 Result = __rdtsc();
- return Result;
+ s64 Result = __rdtsc();
+ return Result;
#else
- LARGE_INTEGER Time;
- if (!QueryPerformanceCounter(&Time))
- {
- s32 Error = GetLastError();
- // TODO(Peter): I'm waiting to see an error actually occur here
- // to know what it could possibly be.
- InvalidCodePath;
- }
- return (s64)Time.QuadPart;
+ LARGE_INTEGER Time;
+ if (!QueryPerformanceCounter(&Time))
+ {
+ s32 Error = GetLastError();
+ // TODO(Peter): I'm waiting to see an error actually occur here
+ // to know what it could possibly be.
+ InvalidCodePath;
+ }
+ return (s64)Time.QuadPart;
#endif
}
diff --git a/src/app/platform_win32/win32_foldhaus_utils.h b/src/app/platform_win32/win32_foldhaus_utils.h
index 5392c9f..c1a4579 100644
--- a/src/app/platform_win32/win32_foldhaus_utils.h
+++ b/src/app/platform_win32/win32_foldhaus_utils.h
@@ -43,8 +43,7 @@ Error Code: %d\n
DEBUG_PRINT(Win32DebugPrint)
{
- Assert(IsNullTerminated(Message));
- OutputDebugStringA(Message.Str);
+ Log_Message(GlobalLogBuffer, "%S", Message);
}
#define PrintLastError() PrintLastError_(__FILE__, __LINE__)
@@ -54,8 +53,7 @@ PrintLastError_(char* File, u32 Line)
char DebugStringData[256];
gs_string DebugString = MakeString(DebugStringData, 0, 256);
u32 Error = GetLastError();
- PrintF(&DebugString, "%s Line %d: Win32 Error %d\n\0", File, Line, Error);
- OutputDebugStringA(DebugString.Str);
+ Log_Error(GlobalLogBuffer, "%s Line %d: Win32 Error %d\n\0", File, Line, Error);
}
diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h
index b3a2a36..7c56418 100644
--- a/src/app/platform_win32/win32_foldhaus_work_queue.h
+++ b/src/app/platform_win32/win32_foldhaus_work_queue.h
@@ -7,22 +7,22 @@
struct worker_thread_entry
{
- b32 IsValid;
- u32 Index;
+ b32 IsValid;
+ u32 Index;
};
struct worker_thread_info
{
- gs_thread_context ThreadContext;
- HANDLE Handle;
- gs_work_queue* Queue;
+ gs_thread_context ThreadContext;
+ HANDLE Handle;
+ gs_work_queue* Queue;
};
struct win32_work_queue
{
- u32 ThreadCount;
- worker_thread_info* Threads;
- gs_work_queue WorkQueue;
+ u32 ThreadCount;
+ worker_thread_info* Threads;
+ gs_work_queue WorkQueue;
};
worker_thread_info* WorkerThreads;
@@ -31,221 +31,219 @@ win32_work_queue Win32WorkQueue;
internal s32
Win32GetThreadId()
{
- s32 Result = GetCurrentThreadId();
- return Result;
+ s32 Result = GetCurrentThreadId();
+ return Result;
}
internal gs_thread_context
Win32CreateThreadContext(gs_memory_arena* Transient = 0)
{
- gs_thread_context Result = {0};
- Result.ThreadInfo.ThreadID = Win32GetThreadId();
- Result.Allocator = CreateAllocator(Win32Alloc, Win32Free);
- if (Transient != 0)
- {
- Result.Transient = Transient;
- }
- else
- {
- Result.Transient = (gs_memory_arena*)AllocatorAlloc(Result.Allocator, sizeof(gs_memory_arena)).Memory;
- *Result.Transient = CreateMemoryArena(Result.Allocator);
- }
- Result.FileHandler = CreateFileHandler(Win32GetFileInfo,
- Win32ReadEntireFile,
- Win32WriteEntireFile,
- Win32EnumerateDirectory,
- Result.Transient);
-
- Result.DebugOutput.Print = Win32DebugPrint;
-
- return Result;
+ gs_thread_context Result = {0};
+ Result.ThreadInfo.ThreadID = Win32GetThreadId();
+ Result.Allocator = CreatePlatformAllocator();
+ if (Transient != 0)
+ {
+ Result.Transient = Transient;
+ }
+ else
+ {
+ Result.Transient = AllocStruct(Result.Allocator, gs_memory_arena, "Work Queue");
+ *Result.Transient = MemoryArenaCreate(MB(4), Bytes(8), Result.Allocator, 0, 0, "Tctx Transient");
+ }
+ Result.FileHandler = CreateFileHandler(Win32GetFileInfo,
+ Win32ReadEntireFile,
+ Win32WriteEntireFile,
+ Win32EnumerateDirectory,
+ Result.Transient);
+
+ Result.DebugOutput.Print = Win32DebugPrint;
+
+ return Result;
}
PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue)
{
-#ifdef DEBUG
- // NOTE(Peter): Just prints out the names of all the pending jobs if we end up
- // overflowing the buffer
- if (Queue->JobsCount >= Queue->JobsMax)
+#if DEBUG
+ // NOTE(Peter): Just prints out the names of all the pending jobs if we end up
+ // overflowing the buffer
+ if (Queue->JobsCount >= Queue->JobsMax)
+ {
+ gs_string DebugString = MakeString((char*)malloc(256), 256);
+ for (u32 i = 0; i < Queue->JobsCount; i++)
{
- gs_string DebugString = MakeString((char*)malloc(256), 256);
- for (u32 i = 0; i < Queue->JobsCount; i++)
- {
- PrintF(&DebugString, "%d %s\n", i, Queue->Jobs[i].JobName);
- NullTerminate(&DebugString);
- OutputDebugStringA(DebugString.Str);
- }
+ Log_Message(GlobalLogBuffer, "%d %s \n", i, Queue->Jobs[i].JobName);
}
+ }
#endif
- Assert(Queue->JobsCount < Queue->JobsMax);
-
- gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount;
- Job->WorkProc = WorkProc;
- Job->Data = Data;
-#ifdef DEBUG
- Job->JobName = JobName;
+ Assert(Queue->JobsCount < Queue->JobsMax);
+
+ gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount;
+ Job->WorkProc = WorkProc;
+ Job->Data = Data;
+#if DEBUG
+ Job->JobName = JobName;
#endif
-
- // Complete Past Writes before Future Writes
- _WriteBarrier();
- _mm_sfence();
-
- ++Queue->JobsCount;
- ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0);
+
+ // Complete Past Writes before Future Writes
+ _WriteBarrier();
+ _mm_sfence();
+
+ ++Queue->JobsCount;
+ ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0);
}
internal worker_thread_entry
CompleteAndTakeNextJob(gs_work_queue* Queue, worker_thread_entry Completed, gs_thread_context Context)
{
- if (Completed.IsValid)
+ if (Completed.IsValid)
+ {
+ InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted);
+ }
+
+ worker_thread_entry Result = {};
+ Result.IsValid = false;
+
+ u32 OriginalNextJobIndex = Queue->NextJobIndex;
+ while (OriginalNextJobIndex < Queue->JobsCount)
+ {
+ u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex,
+ OriginalNextJobIndex + 1,
+ OriginalNextJobIndex);
+ if (Index == OriginalNextJobIndex)
{
- InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted);
+ Result.Index = Index;
+ Result.IsValid = true;
+ break;
}
-
- worker_thread_entry Result = {};
- Result.IsValid = false;
-
- u32 OriginalNextJobIndex = Queue->NextJobIndex;
- while (OriginalNextJobIndex < Queue->JobsCount)
- {
- u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex,
- OriginalNextJobIndex + 1,
- OriginalNextJobIndex);
- if (Index == OriginalNextJobIndex)
- {
- Result.Index = Index;
- Result.IsValid = true;
- break;
- }
- OriginalNextJobIndex = Queue->NextJobIndex;
- }
-
- return Result;
+ OriginalNextJobIndex = Queue->NextJobIndex;
+ }
+
+ return Result;
}
COMPLETE_QUEUE_WORK(Win32DoQueueWorkUntilDone)
{
- worker_thread_entry Entry = {};
- Entry.IsValid = false;
- while (Queue->JobsCompleted < Queue->JobsCount)
+ worker_thread_entry Entry = {};
+ Entry.IsValid = false;
+ while (Queue->JobsCompleted < Queue->JobsCount)
+ {
+ Entry = CompleteAndTakeNextJob(Queue, Entry, Context);
+ if (Entry.IsValid)
{
- Entry = CompleteAndTakeNextJob(Queue, Entry, Context);
- if (Entry.IsValid)
- {
- Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data);
- }
+ Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data);
}
+ }
}
DWORD WINAPI
WorkerThreadProc (LPVOID InputThreadInfo)
{
- worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo;
- ThreadInfo->ThreadContext = Win32CreateThreadContext();
-
- worker_thread_entry Entry = {};
- Entry.IsValid = false;
- while (true)
+ worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo;
+ ThreadInfo->ThreadContext = Win32CreateThreadContext();
+
+ worker_thread_entry Entry = {};
+ Entry.IsValid = false;
+ while (true)
+ {
+ MemoryArenaClear(ThreadInfo->ThreadContext.Transient);
+ Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext);
+ if (Entry.IsValid)
{
- ClearArena(ThreadInfo->ThreadContext.Transient);
- Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext);
- if (Entry.IsValid)
- {
- ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext,
- ThreadInfo->Queue->Jobs[Entry.Index].Data);
- }
- else
- {
- WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0);
- }
+ ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext,
+ ThreadInfo->Queue->Jobs[Entry.Index].Data);
}
-
- return 0;
+ else
+ {
+ WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0);
+ }
+ }
+
+ return 0;
}
DWORD WINAPI
Win32ThreadProcWrapper(LPVOID ThreadInfo)
{
- platform_thread* Thread = (platform_thread*)ThreadInfo;
- gs_thread_context Ctx = Win32CreateThreadContext();
- Thread->Proc(&Ctx, Thread->UserData);
-
- // TODO(pjs): Destroy Thread Context
- // TODO(pjs): How do we notify the thread manager this thread belongs to that it is free?
- // Probaby put a pointer to the thread manager in the platform_thread struct
- // so we can update the tracking structure?
-
- return 0;
+ platform_thread* Thread = (platform_thread*)ThreadInfo;
+ gs_thread_context Ctx = Win32CreateThreadContext();
+ Thread->Proc(&Ctx, Thread->UserData);
+
+ // TODO(pjs): Destroy Thread Context
+ // TODO(pjs): How do we notify the thread manager this thread belongs to that it is free?
+ // Probaby put a pointer to the thread manager in the platform_thread struct
+ // so we can update the tracking structure?
+
+ return 0;
}
CREATE_THREAD(Win32CreateThread)
{
- Thread->Proc = Proc;
- Thread->UserData = UserData;
-
- // TODO(pjs): ugh, allocation out in the middle of nowhere
- HANDLE* ThreadHandle = (HANDLE*)Win32Alloc(sizeof(HANDLE), 0);
- *ThreadHandle = CreateThread(0, 0, Win32ThreadProcWrapper, (void*)Thread, 0, 0);
- // TODO(pjs): Error checking on the created thread
-
- Thread->PlatformHandle = (u8*)ThreadHandle;
-
- return true;
+ Thread->Proc = Proc;
+ Thread->UserData = UserData;
+
+ // TODO(pjs): ugh, allocation out in the middle of nowhere
+ HANDLE* ThreadHandle = AllocStruct(Ctx.Allocator, HANDLE, "Create Thread");
+ *ThreadHandle = CreateThread(0, 0, Win32ThreadProcWrapper, (void*)Thread, 0, 0);
+ // TODO(pjs): Error checking on the created thread
+
+ Thread->PlatformHandle = (u8*)ThreadHandle;
+
+ return true;
}
KILL_THREAD(Win32KillThread)
{
- HANDLE* ThreadHandle = (HANDLE*)Thread->PlatformHandle;
- TerminateThread(ThreadHandle, 0);
-
- // TODO(pjs): see allocation out in the middle of nowhere in Win32CreateThread
- Win32Free((void*)Thread->PlatformHandle, sizeof(HANDLE));
-
- // TODO(pjs): Error checking
- return true;
+ HANDLE* ThreadHandle = (HANDLE*)Thread->PlatformHandle;
+ TerminateThread(ThreadHandle, 0);
+
+ // TODO(pjs): see allocation out in the middle of nowhere in Win32CreateThread
+ Win32Free((void*)Thread->PlatformHandle, sizeof(HANDLE), 0);
+
+ // TODO(pjs): Error checking
+ return true;
}
internal void
Win32WorkQueue_Init(gs_memory_arena* Arena, u32 ThreadCount)
{
- if (ThreadCount > 0)
- {
- Win32WorkQueue.ThreadCount = ThreadCount;
- Win32WorkQueue.Threads = PushArray(Arena, worker_thread_info, ThreadCount);
- }
-
- gs_work_queue WQ = {};
- WQ.SemaphoreHandle = CreateSemaphoreEx(0, 0, ThreadCount, 0, 0, SEMAPHORE_ALL_ACCESS);;
- WQ.JobsMax = 512;
- WQ.Jobs = PushArray(Arena, gs_threaded_job, WQ.JobsMax);
- WQ.NextJobIndex = 0;
- WQ.PushWorkOnQueue = Win32PushWorkOnQueue;
- WQ.CompleteQueueWork = Win32DoQueueWorkUntilDone;
-
- Win32WorkQueue.WorkQueue = WQ;
-
- // ID = 0 is reserved for this thread
- for (u32 i = 0; i < ThreadCount; i++)
- {
- worker_thread_info* T = Win32WorkQueue.Threads + i;
- T->Queue = &Win32WorkQueue.WorkQueue;
- T->Handle = CreateThread(0, 0, &WorkerThreadProc, (void*)T, 0, 0);
- }
+ if (ThreadCount > 0)
+ {
+ Win32WorkQueue.ThreadCount = ThreadCount;
+ Win32WorkQueue.Threads = PushArray(Arena, worker_thread_info, ThreadCount);
+ }
+
+ gs_work_queue WQ = {};
+ WQ.SemaphoreHandle = CreateSemaphoreEx(0, 0, ThreadCount, 0, 0, SEMAPHORE_ALL_ACCESS);;
+ WQ.JobsMax = 512;
+ WQ.Jobs = PushArray(Arena, gs_threaded_job, WQ.JobsMax);
+ WQ.NextJobIndex = 0;
+ WQ.PushWorkOnQueue = Win32PushWorkOnQueue;
+ WQ.CompleteQueueWork = Win32DoQueueWorkUntilDone;
+
+ Win32WorkQueue.WorkQueue = WQ;
+
+ // ID = 0 is reserved for this thread
+ for (u32 i = 0; i < ThreadCount; i++)
+ {
+ worker_thread_info* T = Win32WorkQueue.Threads + i;
+ T->Queue = &Win32WorkQueue.WorkQueue;
+ T->Handle = CreateThread(0, 0, &WorkerThreadProc, (void*)T, 0, 0);
+ }
}
internal void
Win32WorkQueue_Cleanup()
{
- u32 Error = 0;
- for (u32 Thread = 0; Thread < Win32WorkQueue.ThreadCount; Thread++)
+ u32 Error = 0;
+ for (u32 Thread = 0; Thread < Win32WorkQueue.ThreadCount; Thread++)
+ {
+ u32 Success = TerminateThread(Win32WorkQueue.Threads[Thread].Handle, 0);
+ if (!Success)
{
- u32 Success = TerminateThread(Win32WorkQueue.Threads[Thread].Handle, 0);
- if (!Success)
- {
- Error = GetLastError();
- InvalidCodePath;
- }
+ Error = GetLastError();
+ InvalidCodePath;
}
+ }
}
#define WIN32_FOLDHAUS_WORK_QUEUE_H
diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp
new file mode 100644
index 0000000..08ac0e0
--- /dev/null
+++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp
@@ -0,0 +1,1155 @@
+//
+// File: blumen_lumen.cpp
+// Author: Peter Slattery
+// Creation Date: 2021-01-23
+//
+#ifndef BLUMEN_LUMEN_CPP
+
+internal animation_handle_array
+LoadAllAnimationsInDir(gs_const_string Path, blumen_lumen_state* BLState, app_state* State, context Context)
+{
+ animation_handle_array Result = {};
+
+ gs_thread_context Ctx = Context.ThreadContext;
+ gs_file_info_array FilesInDir = EnumerateDirectory(Ctx.FileHandler, State->Transient, Path, 0);
+
+ Result.Count = FilesInDir.Count;
+ Result.Handles = PushArray(&State->Permanent, animation_handle, Result.Count);
+
+ for (u32 i = 0; i < FilesInDir.Count; i++)
+ {
+ gs_file_info File = FilesInDir.Values[i];
+ Result.Handles[i] = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem,
+ State->Patterns,
+ Context,
+ File.Path);
+ }
+
+ return Result;
+}
+
+internal s32
+GetCCIndex (assembly Assembly, blumen_lumen_state* BLState)
+{
+ s32 Result = 0;
+
+ u64 AssemblyNameHash = HashDJB2ToU32(StringExpand(Assembly.Name));
+ for (u32 i = 0; i < BLState->AssemblyNameToClearCoreMapCount; i++)
+ {
+ if (AssemblyNameHash == BLState->AssemblyNameToClearCore_Names[i])
+ {
+ Result = (s32)i;
+ break;
+ }
+ }
+
+ return Result;
+}
+
+internal void
+DEBUG_AppendText(gs_string Str, gs_thread_context Ctx)
+{
+ gs_const_string DebugPath = ConstString("data/debug_motor_changes.txt");
+ gs_file DebugFile = ReadEntireFile(Ctx.FileHandler,
+ DebugPath);
+ gs_string NewString = PushString(Ctx.Transient, DebugFile.Size + Str.Size + 16);
+ if (DebugFile.Size > 0)
+ {
+ PrintF(&NewString, "%.*s\nENTRY:\n", DebugFile.Size, (char*)DebugFile.Memory);
+ }
+ AppendPrintF(&NewString, "%S\n", Str.ConstString);
+ NullTerminate(&NewString);
+
+ if (!WriteEntireFile(Ctx.FileHandler, DebugPath, StringToData(NewString)))
+ {
+ InvalidCodePath;
+ }
+}
+
+internal void
+DEBUG_SentMotorCommand(motor_packet Packet, gs_thread_context Ctx)
+{
+ Log_Message(GlobalLogBuffer,
+ "Motor Command Sent\nRequested Positions: %d %d %d\n",
+ Packet.FlowerPositions[0],
+ Packet.FlowerPositions[1],
+ Packet.FlowerPositions[2]);
+}
+
+internal void
+DEBUG_ReceivedMotorPositions(blumen_lumen_state* BLState,
+ motor_status_packet NewPos,
+ gs_thread_context Ctx)
+{
+ motor_packet LastPos = BLState->LastKnownMotorState;
+ bool PosChanged = (LastPos.FlowerPositions[0] != NewPos.Pos.FlowerPositions[0] ||
+ LastPos.FlowerPositions[1] != NewPos.Pos.FlowerPositions[1] ||
+ LastPos.FlowerPositions[2] != NewPos.Pos.FlowerPositions[2]);
+
+ if (PosChanged)
+ {
+ Log_Message(GlobalLogBuffer,
+ "Motor Status Received\nCurrent Positions: %d %d %d\n",
+ NewPos.Pos.FlowerPositions[0],
+ NewPos.Pos.FlowerPositions[1],
+ NewPos.Pos.FlowerPositions[2]);
+ }
+}
+
+internal void
+DEBUG_ReceivedTemperature(temp_packet Temp, gs_thread_context Ctx)
+{
+ Log_Message(GlobalLogBuffer,
+ "\nTemperature: %d\n",
+ Temp.Temperature);
+}
+
+internal void
+BlumenLumen_MicListenJob(gs_thread_context* Ctx, u8* UserData)
+{
+ mic_listen_job_data* Data = (mic_listen_job_data*)UserData;
+
+ gs_data Msg = {};
+
+ u8 WeathermanIPAddr[4] = {};
+ WeathermanIPAddr[0] = 127;
+ WeathermanIPAddr[1] = 0;
+ WeathermanIPAddr[2] = 0;
+ WeathermanIPAddr[3] = 1;
+
+ u32 WeathermanIPV4 = (u32)UpackB4(WeathermanIPAddr);
+ u32 WeathermanPort = 20185;
+
+ platform_socket_handle_ ListenSocket = {0};
+
+ while (*Data->Running)
+ {
+ if (!SocketQueryStatus(Data->SocketManager, ListenSocket))
+ {
+ Data->IsConnected = false;
+ if (SocketHandleIsValid(ListenSocket))
+ {
+ Log_Message(GlobalLogBuffer, "Disconnected from Python Server\n");
+ CloseSocket(Data->SocketManager, ListenSocket);
+ }
+ ListenSocket = CreateSocket(Data->SocketManager, "127.0.0.1", "20185");
+ if (ListenSocket.Index != 0)
+ {
+ Log_Message(GlobalLogBuffer, "Connected to Python Server\n");
+ Data->IsConnected = true;
+ }
+ }
+
+ if (SocketQueryStatus(Data->SocketManager, ListenSocket))
+ {
+ if (SocketPeek(Data->SocketManager, ListenSocket))
+ {
+ Msg = SocketRecieve(Data->SocketManager, ListenSocket, Ctx->Transient);
+ if (Msg.Size > 0)
+ {
+ MessageQueue_Write(Data->IncomingMsgQueue, Msg);
+ }
+ }
+
+ while (MessageQueue_CanRead(*Data->OutgoingMsgQueue))
+ {
+ Msg = MessageQueue_Peek(Data->OutgoingMsgQueue);
+
+ u32 Address = WeathermanIPV4;
+ u32 Port = WeathermanPort;
+ s32 Flags = 0;
+ s32 LengthSent = SocketSend(Data->SocketManager, ListenSocket, Address, Port, Msg, Flags);
+ if (LengthSent != 0)
+ {
+ // if we actually sent the message, THEN we pull it off the
+ // message queue
+ MessageQueue_Read(Data->OutgoingMsgQueue);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ CloseSocket(Data->SocketManager, ListenSocket);
+}
+
+internal void
+BlumenLumen_SetPatternMode(bl_pattern_mode Mode, r32 FadeDuration, animation_system* System, blumen_lumen_state* BLState)
+{
+ BLState->PatternMode = Mode;
+ animation_handle_array Playlist = BLState->ModeAnimations[Mode];
+ System->RepeatMode = AnimationRepeat_Loop;
+ System->PlaylistFadeTime = FadeDuration;
+ AnimationSystem_FadeToPlaylist(System, Playlist);
+}
+
+internal void
+BlumenLumen_LoadPatterns(app_state* State)
+{
+ animation_pattern_array* Patterns = &State->Patterns;
+ if (Patterns->CountMax == 0)
+ {
+ *Patterns = Patterns_Create(&State->Permanent, 32);
+ }
+
+ Patterns->Count = 0;
+ Patterns_PushPattern(Patterns, Pattern_None, PATTERN_SINGLETHREADED);
+ Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_Rainbow, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED);
+ Patterns_PushPattern(Patterns, Pattern_VerticalLines, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_Rotary, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_AllOnMask, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_BulbMask, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_VoicePattern, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_VoiceAddIns, PATTERN_MULTITHREADED);
+
+ Patterns_PushPattern(Patterns, Pattern_StemSolid, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_PrimaryHue, PATTERN_MULTITHREADED);
+
+ Patterns_PushPattern(Patterns, Pattern_GrowFadeMask, PATTERN_MULTITHREADED);
+ Patterns_PushPattern(Patterns, Pattern_RainbowLoadingBar, PATTERN_MULTITHREADED);
+
+ Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED);
+}
+
+internal void
+AppendPrintDate(gs_string* WriteStr, system_time Time)
+{
+ AppendPrintF(WriteStr, "%d-%d-%d : %d:%d:%d\n\n",
+ Time.Year, Time.Month, Time.Day,
+ Time.Hour, Time.Minute, Time.Second);
+}
+
+internal void
+BlumenLumen_AppendBootupLog(app_state* State, blumen_lumen_state* BLState, context Context)
+{
+ gs_thread_context Ctx = Context.ThreadContext;
+ gs_const_string BootupLogPath = ConstString("lumenarium_boot_log.log");
+
+ gs_file BootLog = ReadEntireFile(Ctx.FileHandler, BootupLogPath);
+ gs_string WriteStr = {};
+
+ // we don't want the log file getting huge
+ // if it gets above some threshold, instead of appending,
+ // copy what there is to an _old file, and start this one over
+ //
+ // The thinking is that without the copy operation, when we reached
+ // our threshold, we'd overwrite the whole log. If something went
+ // wrong at that point, we'd have nothing to go on. This way, there is
+ // always some historical data present on the system
+ //
+ if (BootLog.Size < MB(4))
+ {
+ WriteStr = PushString(State->Transient, BootLog.Size + 1024);
+ }
+ else
+ {
+ if (!WriteEntireFile(Ctx.FileHandler, ConstString("lumenarium_boot_log_old.log"),
+ BootLog.Data))
+ {
+ InvalidCodePath;
+ }
+ WriteStr = PushString(State->Transient, 1024);
+ }
+
+
+ // Copy old entries in
+ if (BootLog.Size > 0)
+ {
+ PrintF(&WriteStr, "%.*s", BootLog.Size, BootLog.Memory);
+ }
+
+ // New Entry
+ AppendPrintF(&WriteStr, "Lumenarium Restarted\n");
+ AppendPrintF(&WriteStr, "* Time: ");
+ AppendPrintDate(&WriteStr, Context.SystemTime_Current);
+
+ gs_data Data = StringToData(WriteStr);
+ WriteEntireFile(Ctx.FileHandler, BootupLogPath, Data);
+}
+
+internal void
+BlumenLumen_UpdateLog(app_state* State, blumen_lumen_state* BLState, context Context)
+{
+ if (!BLState->ShouldUpdateLog) return;
+
+ gs_string FileStr = PushString(State->Transient, 1024);
+ AppendPrintF(&FileStr, "Lumenarium Status\n");
+
+ AppendPrintF(&FileStr, "Last Updated At:");
+ AppendPrintDate(&FileStr, Context.SystemTime_Current);
+ AppendPrintF(&FileStr, "\n\n");
+
+ animation* CurrAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
+ AppendPrintF(&FileStr, "Curr Animation: %S\n", CurrAnim->Name);
+
+ bool IsPlaying = State->AnimationSystem.TimelineShouldAdvance;
+ AppendPrintF(&FileStr, "\tIs Playing: %s\n",
+ IsPlaying ? "True" : "False");
+
+ char* Connected = BLState->MicListenJobData.IsConnected ? "Connected" : "Disconnected";
+ AppendPrintF(&FileStr, "Connected to Python: %s\n", Connected);
+
+ u8 MP0 = BLState->LastKnownMotorState.FlowerPositions[0];
+ u8 MP1 = BLState->LastKnownMotorState.FlowerPositions[1];
+ u8 MP2 = BLState->LastKnownMotorState.FlowerPositions[2];
+ AppendPrintF(&FileStr, "Last Known Motor State: %d %d %d\n", MP0, MP1, MP2);
+
+ time_range MotorRange = {};
+ if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current,
+ MotorOpenTimes,
+ MotorOpenTimesCount,
+ &MotorRange))
+ {
+ AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: ( %d:%d - %d:%d)\n",
+ MotorRange.StartHour, MotorRange.StartMinute,
+ MotorRange.EndHour, MotorRange.EndMinute);
+ }
+ else
+ {
+ AppendPrintF(&FileStr, "\tIn Motor-Open Time Range: None\n");
+ }
+
+ char* PatternMode = 0;
+ switch (BLState->PatternMode)
+ {
+ case BlumenPattern_Standard: { PatternMode = "Standard"; } break;
+ case BlumenPattern_VoiceCommand: { PatternMode = "Voice Command"; } break;
+ case BlumenPattern_NoControl: { PatternMode = "No Control: Someone's doing the Awaken sequence!"; } break;
+ }
+ AppendPrintF(&FileStr, "Pattern Mode: %s\n", PatternMode);
+
+ phrase_hue LastHuePhrase = BLState->LastHuePhrase;
+ AppendPrintF(&FileStr, "Last Mic Phrase: %S\n", LastHuePhrase.Phrase);
+
+ AppendPrintF(&FileStr, "Pattern Speed: %f\n", BLState->PatternSpeed);
+
+ AppendPrintF(&FileStr, "Pattern Brightness: %f\n", BLState->BrightnessPercent);
+
+ time_range RangeIn = {};
+ if (SystemTimeIsInTimeRangeList(Context.SystemTime_Current,
+ LedOnTimes,
+ LedOnTimesCount,
+ &RangeIn))
+ {
+ AppendPrintF(&FileStr, "\tIn Leds-On Time Range: ( %d:%d - %d:%d)\n",
+ RangeIn.StartHour, RangeIn.StartMinute,
+ RangeIn.EndHour, RangeIn.EndMinute);
+ }
+ else
+ {
+ AppendPrintF(&FileStr, "\tIn Leds-On Time Range: None\n");
+ }
+
+ AppendPrintF(&FileStr, "\tTemp Dimming: %s\n",
+ Blumen_TempShouldDimLeds(BLState) ? "On" : "Off");
+
+ AppendPrintF(&FileStr, "Last Temp Received: %d\n", BLState->LastTemperatureReceived);
+
+ gs_data LogMem = StringToData(FileStr);
+ if (!WriteEntireFile(Context.ThreadContext.FileHandler, ConstString("lumenarium_status.log"), LogMem))
+ {
+ InvalidCodePath;
+ }
+}
+
+internal gs_data
+BlumenLumen_CustomInit(app_state* State, context Context)
+{
+ // This is memory for any custom data that we want to use
+ // as a part of a particular sculpture.
+ // By returning it from here, it will be sent as an argument to
+ // the sculpture's CustomUpdate function;
+ gs_data Result = {};
+
+ Result = PushSize(&State->Permanent, sizeof(blumen_lumen_state));
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)Result.Memory;
+ BLState->Running = true;
+ BLState->BrightnessPercent = 1;
+ MessageQueue_Init(&BLState->IncomingMsgQueue, &State->Permanent);
+ MessageQueue_Init(&BLState->OutgoingMsgQueue, &State->Permanent);
+
+ BLState->MicListenJobData.Running = &BLState->Running;
+ BLState->MicListenJobData.SocketManager = Context.SocketManager;
+ BLState->MicListenJobData.IncomingMsgQueue = &BLState->IncomingMsgQueue;
+ BLState->MicListenJobData.OutgoingMsgQueue = &BLState->OutgoingMsgQueue;
+
+ BLState->PatternSpeed = GlobalAnimSpeed;
+
+#if 1
+ BLState->MicListenThread = CreateThread(Context.ThreadManager, BlumenLumen_MicListenJob, (u8*)&BLState->MicListenJobData, Context.ThreadContext);
+#endif
+
+ assembly* Flower0 = LoadAssembly(Flower0AssemblyPath, State, Context);
+ assembly* Flower1 = LoadAssembly(Flower1AssemblyPath, State, Context);
+ assembly* Flower2 = LoadAssembly(Flower2AssemblyPath, State, Context);
+
+ for (u32 i = 0; i < BL_FLOWER_COUNT; i++)
+ {
+ assembly Assembly = State->Assemblies.Values[i];
+ BLState->StemStrips[Assembly.AssemblyIndex] = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("stem"), &State->Permanent);
+ }
+
+ BLState->AssemblyNameToClearCoreMapCount = 3;
+ BLState->AssemblyNameToClearCore_Names = PushArray(&State->Permanent,
+ u64,
+ BLState->AssemblyNameToClearCoreMapCount);
+ BLState->AssemblyNameToClearCore_Names[0] = HashDJB2ToU32(StringExpand(Flower2->Name));
+ BLState->AssemblyNameToClearCore_Names[1] = HashDJB2ToU32(StringExpand(Flower1->Name));
+ BLState->AssemblyNameToClearCore_Names[2] = HashDJB2ToU32(StringExpand(Flower0->Name));
+
+ gs_file_handler FileHandler = Context.ThreadContext.FileHandler;
+ gs_file ColorPhraseCSVFile = ReadEntireFile(FileHandler, PhraseMapCSVPath);
+ if (ColorPhraseCSVFile.Memory != 0)
+ {
+ gs_const_string ColorPhraseMapStr = DataToString(ColorPhraseCSVFile.Data);
+ gscsv_sheet ColorPhraseSheet = CSV_Parse(ColorPhraseMapStr,
+ { PhraseMapCSVSeparator },
+ State->Transient);
+
+ BLState->PhraseHueMap = PhraseHueMap_GenFromCSV(ColorPhraseSheet,
+ &State->Permanent);
+ }
+
+#if 0
+ animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem,
+ State->Patterns,
+ Context,
+ ConstString("data/demo_patterns.foldanim"));
+ State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim;
+#else
+
+ BLState->ModeAnimations[BlumenPattern_Standard] = LoadAllAnimationsInDir(AmbientPatternFolder, BLState, State, Context);
+ BLState->ModeAnimations[BlumenPattern_VoiceCommand] = LoadAllAnimationsInDir(VoicePatternFolder, BLState, State, Context);
+ AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, State->Patterns, Context, ConstString("data/blumen_animations/anim_demo.foldanim"));
+
+ BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState);
+
+ BLState->AwakenHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem,
+ State->Patterns,
+ Context,
+ ConstString("data/blumen_animations/awaken.foldanim"));
+ BLState->OffAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem,
+ State->Patterns,
+ Context,
+ ConstString("data/blumen_animations/off_anim.foldanim"));
+
+#endif
+ State->AnimationSystem.TimelineShouldAdvance = true;
+
+ BLState->StandardPatternHues.Granularity = 1;
+ BLState->StandardPatternHues.Speed = 1;
+ BLState->StandardPatternHues.AddIn = AddIn_Rotary;
+ BLState->StandardPatternHues.Pattern = HuePattern_Wavy;
+
+ BLState->DebugHue.Hue0.HSV = v4{0, 1, 1, 1};
+ BLState->DebugHue.Hue1.HSV = v4{0, 1, 1, 1};
+ BLState->DebugHue.Hue2.HSV = v4{0, 1, 1, 1};
+
+ BlumenLumen_AppendBootupLog(State, BLState, Context);
+ return Result;
+}
+
+internal void
+BlumenLumen_UpdateMotorState(blumen_lumen_state* BLState, motor_status_packet Motor, context Context)
+{
+ DEBUG_ReceivedMotorPositions(BLState, Motor, Context.ThreadContext);
+
+ motor_packet LastPos = BLState->LastKnownMotorState;
+ motor_packet CurrPos = Motor.Pos;
+ for (u32 i = 0; i < BL_FLOWER_COUNT; i++)
+ {
+ if (LastPos.FlowerPositions[i] != CurrPos.FlowerPositions[i])
+ {
+ BLState->LastTimeMotorStateChanged[i] = Context.SystemTime_Current.NanosSinceEpoch;
+ }
+ }
+
+ BLState->LastKnownMotorState = Motor.Pos;
+ BLState->ShouldUpdateLog = true;
+}
+
+internal void
+BlumenLumen_ApplyNextHotHue(blumen_lumen_state* BLState, context Context, gs_string* DebugStr, app_state* State)
+{
+ // if we are in standard color mode, shift all flowers to the new color
+ // otherwise, only shift the next flower in the sequence to the new color
+ phrase_hue NewHue = BLState->NextHotHue;
+ Log_Message(GlobalLogBuffer, "Switching To: %S\n", NewHue.Phrase);
+
+
+ if (BLState->PatternMode == BlumenPattern_Standard ||
+ NewHue.OverrideAll)
+ {
+ BlumenLumen_SetNextHue(BLState, 0, NewHue);
+ BlumenLumen_SetNextHue(BLState, 1, NewHue);
+ BlumenLumen_SetNextHue(BLState, 2, NewHue);
+ }
+ else
+ {
+ u32 AssemblyIdx = BLState->LastAssemblyColorSet;
+ BlumenLumen_SetNextHue(BLState, AssemblyIdx, NewHue);
+ BLState->LastAssemblyColorSet = (BLState->LastAssemblyColorSet + 1) % 3;
+ }
+
+ BlumenLumen_SetPatternMode(BlumenPattern_VoiceCommand, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState);
+ BLState->TimeLastSetToVoiceMode = Context.SystemTime_Current;
+ BLState->LastHuePhrase = NewHue;
+ BLState->ShouldUpdateLog = true;
+ BLState->InPhraseReceptionMode = false;
+}
+
+internal void
+BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context)
+{
+ DEBUG_TRACK_FUNCTION;
+
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory;
+ BLState->ShouldUpdateLog = false;
+
+ gs_string DebugStr = PushString(State->Transient, 256);
+
+ while (MessageQueue_CanRead(BLState->IncomingMsgQueue))
+ {
+ gs_data PacketData = MessageQueue_Read(&BLState->IncomingMsgQueue);
+ if (PacketData.Memory == 0) continue;
+
+ blumen_packet Packet = *(blumen_packet*)PacketData.Memory;
+ switch (Packet.Type) {
+ case PacketType_PatternCommand:
+ {
+ microphone_packet Mic = Packet.MicPacket;
+ u64 NameHash = HashDJB2ToU32(Mic.AnimationFileName);
+ u32 NameLen = CStringLength(Mic.AnimationFileName);
+
+ phrase_hue NewHue = PhraseHueMap_Find(BLState->PhraseHueMap, NameHash);
+ if (NewHue.Phrase.Length > 0)
+ {
+ bool IsLonger = (BLState->NextHotHue.Phrase.Length < NewHue.Phrase.Length);
+ bool IsntInPhraseReceptionMode = !BLState->InPhraseReceptionMode;
+ if (IsLonger || IsntInPhraseReceptionMode)
+ {
+ Log_Message(GlobalLogBuffer, "Queueing: %S\n", NewHue.Phrase);
+
+ BLState->NextHotHue = NewHue;
+ if (SecondsElapsed(BLState->TimePhraseReceptionBegan,
+ Context->SystemTime_Current) > PhrasePriorityMessageGroupingTime)
+ {
+ BLState->TimePhraseReceptionBegan = Context->SystemTime_Current;
+ BLState->InPhraseReceptionMode = true;
+ }
+ BLState->TimeLastPhraseReceived = Context->SystemTime_Current;
+ }
+ }
+ }break;
+
+ case PacketType_MotorState:
+ {
+ motor_status_packet Motor = Packet.MotorStatusPacket;
+
+ // NOTE(pjs): Python sends multi-byte integers in little endian
+ // order. Have to unpack
+ u8* T = (u8*)&Motor.Temperature;
+ Motor.Temperature = (T[0] << 8 |
+ T[1] << 0);
+
+ BlumenLumen_UpdateMotorState(BLState, Motor, *Context);
+ }break;
+
+ case PacketType_Temperature:
+ {
+ temp_packet Temp = Packet.TempPacket;
+ BLState->LastTemperatureReceived = Temp.Temperature;
+ DEBUG_ReceivedTemperature(Temp, Context->ThreadContext);
+ BLState->ShouldUpdateLog = true;
+ }break;
+
+ InvalidDefaultCase;
+ }
+ }
+
+
+
+ if (BLState->InPhraseReceptionMode)
+ {
+ r32 SecondsSincePhraseBegan = SecondsElapsed(BLState->TimePhraseReceptionBegan, Context->SystemTime_Current);
+ if (SecondsSincePhraseBegan > PhrasePriorityMessageGroupingTime)
+ {
+ BlumenLumen_ApplyNextHotHue(BLState, *Context, &DebugStr, State);
+ }
+ }
+
+ BlumenLumen_AdvanceHueFade(BLState, *Context);
+
+ // Update next frames Hues
+ if (!BLState->DebugOverrideHue)
+ {
+ r32 AnimTime = AnimationSystem_GetCurrentTime(State->AnimationSystem);
+ AnimTime = (r32)Context->TotalTime;
+ r32 BaseTime = AnimTime * BLState->PatternSpeed;
+
+ r32 ColorSpeed = 1; //.001;
+ r32 ColorOscSpeed = .05 * ColorSpeed;
+ r32 ColorRelOscSpeed = 1 * ColorSpeed;;
+ r32 ColorOscillation = (SinR32(BaseTime * ColorOscSpeed) + 1) / 2;
+ r32 ColorRelationship = 30 + (((1 + SinR32(BaseTime * ColorRelOscSpeed)) / 2) * 300);
+
+ r32 H0 = ModR32(ColorOscillation * 360, 360);
+ r32 H1 = ModR32(BaseTime + ColorRelationship, 360);
+ // TODO(PS): use our new HSV lerp
+ r32 H2 = LerpR32(.3f, H0, H1);
+
+ BLState->StandardPatternHues.Hue0.HSV = v4{ H0, 1, 1, 1 };
+ BLState->StandardPatternHues.Hue1.HSV = v4{ H1, 1, 1, 1 };
+ BLState->StandardPatternHues.Hue2.HSV = v4{ H2, 1, 1, 1 };
+
+ // Transition back to standard mode after some time
+ if (BLState->PatternMode == BlumenPattern_VoiceCommand)
+ {
+ u64 LastChangeClock = BLState->TimeLastSetToVoiceMode.NanosSinceEpoch;
+ u64 NowClocks = Context->SystemTime_Current.NanosSinceEpoch;
+ s64 NanosSinceChange = NowClocks - LastChangeClock;
+ r64 SecondsSinceChange = (r64)NanosSinceChange * NanosToSeconds;
+
+ if (SecondsSinceChange > VoiceCommandSustainDuration)
+ {
+ BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem, BLState);
+ BLState->ShouldUpdateLog = true;
+ }
+ }
+
+ }
+ else
+ {
+ BLState->StandardPatternHues = BLState->DebugHue;
+ AnimationSystem_FadeToPlaylist(&State->AnimationSystem, BLState->ModeAnimations[BlumenPattern_VoiceCommand]);
+
+ }
+
+ // Open / Close the Motor
+ if (MessageQueue_CanWrite(BLState->OutgoingMsgQueue) &&
+ !BLState->IgnoreTimeOfDay_MotorState)
+ {
+ bool SendMotorCommand = false;
+
+ u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch;
+ r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds;
+ bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod;
+
+ bl_motor_state_value NewMotorState = MotorState_Closed;
+ bool SendOpen = false;
+ for (u32 i = 0; i < MotorOpenTimesCount; i++)
+ {
+ time_range Range = MotorOpenTimes[i];
+ bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range);
+ if (CurrTimeInRange) {
+ NewMotorState = MotorState_Open;
+ }
+ }
+
+ if (NewMotorState != BLState->LastSendState)
+ {
+ ShouldSendCurrentState = true;
+ }
+
+ if (ShouldSendCurrentState)
+ {
+ BLState->LastSendTime = Context->SystemTime_Current;
+ BLState->LastSendState = NewMotorState;
+
+ blumen_packet Packet = {};
+ Packet.Type = PacketType_MotorState;
+ Packet.MotorPacket.FlowerPositions[0] = NewMotorState;
+ Packet.MotorPacket.FlowerPositions[1] = NewMotorState;
+ Packet.MotorPacket.FlowerPositions[2] = NewMotorState;
+ gs_data Msg = StructToData(&Packet, blumen_packet);
+ MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg);
+
+ DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext);
+ }
+ }
+
+ // When a motor state changes to being open, wait to turn Upper Leds on
+ // in order to hide the fact that they are turning off
+ motor_packet CurrMotorPos = BLState->LastKnownMotorState;
+ u64 NowNanos = Context->SystemTime_Current.NanosSinceEpoch;
+ for (u32 i = 0; i < BL_FLOWER_COUNT; i++)
+ {
+ // have to map from "assembly load order" to
+ // the order that the clear core is referencing the
+ // motors by
+ assembly Assembly = State->Assemblies.Values[i];
+ u64 AssemblyCCIndex = GetCCIndex(Assembly, BLState);
+ u8 MotorPos = CurrMotorPos.FlowerPositions[AssemblyCCIndex];
+
+ if ((MotorPos == MotorState_Open || MotorPos == MotorState_MostlyOpen) &&
+ !BLState->ShouldDimUpperLeds[i])
+ {
+ u64 ChangedNanos = BLState->LastTimeMotorStateChanged[i];
+ u64 NanosSinceChanged = NowNanos - ChangedNanos;
+ r64 SecondsSinceChanged = (r64)NanosSinceChanged * NanosToSeconds;
+ if (SecondsSinceChanged > TurnUpperLedsOffAfterMotorCloseCommandDelay)
+ {
+ BLState->ShouldDimUpperLeds[i] = true;
+ }
+ else
+ {
+ BLState->ShouldDimUpperLeds[i] = false;
+ }
+ }
+ else if (MotorPos == MotorState_Closed ||
+ MotorPos == MotorState_HalfOpen)
+ {
+ BLState->ShouldDimUpperLeds[i] = false;
+
+ bool SendMotorCommand = false;
+
+ u64 NanosSinceLastSend = Context->SystemTime_Current.NanosSinceEpoch - BLState->LastSendTime.NanosSinceEpoch;
+ r32 SecondsSinceLastSend = (r64)NanosSinceLastSend * NanosToSeconds;
+ bool ShouldSendCurrentState = SecondsSinceLastSend >= MotorResendStatePeriod;
+
+ bl_motor_state_value NewMotorState = MotorState_Closed;
+ bool SendOpen = false;
+ for (u32 j = 0; j < MotorOpenTimesCount; j++)
+ {
+ time_range Range = MotorOpenTimes[j];
+ bool CurrTimeInRange = SystemTimeIsInTimeRange(Context->SystemTime_Current, Range);
+ if (CurrTimeInRange) {
+ NewMotorState = MotorState_Open;
+ }
+ }
+
+
+ if (NewMotorState != BLState->LastSendState)
+ {
+ ShouldSendCurrentState = true;
+ }
+
+ if (ShouldSendCurrentState)
+ {
+ BLState->LastSendTime = Context->SystemTime_Current;
+ BLState->LastSendState = NewMotorState;
+
+ blumen_packet Packet = {};
+ Packet.Type = PacketType_MotorState;
+ Packet.MotorPacket.FlowerPositions[0] = NewMotorState;
+ Packet.MotorPacket.FlowerPositions[1] = NewMotorState;
+ Packet.MotorPacket.FlowerPositions[2] = NewMotorState;
+ gs_data Msg = StructToData(&Packet, blumen_packet);
+ MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg);
+
+ DEBUG_SentMotorCommand(Packet.MotorPacket, Context->ThreadContext);
+ }
+ }
+ }
+
+ // NOTE(PS): If the flowers are mostly open or full open
+ // we mask off the top leds to prevent them from overheating
+ // while telescoped inside the flower
+ for (u32 a = 0; a < BL_FLOWER_COUNT; a++)
+ {
+ assembly Assembly = State->Assemblies.Values[a];
+ if (!BLState->ShouldDimUpperLeds[a]) continue;
+
+ led_buffer Buffer = State->LedSystem.Buffers[Assembly.LedBufferIndex];
+ led_strip_list TopStrips = AssemblyStripsGetWithTagValue(Assembly, ConstString("section"), ConstString("inner_bloom"), State->Transient);
+ for (u32 s = 0; s < TopStrips.Count; s++)
+ {
+ u32 SIndex = TopStrips.StripIndices[s];
+ v2_strip Strip = Assembly.Strips[SIndex];
+ for (u32 l = 0; l < Strip.LedCount; l++)
+ {
+ u32 LIndex = Strip.LedLUT[l];
+ Buffer.Colors[LIndex] = {0};
+ }
+ }
+ }
+
+ if (!BLState->IgnoreTimeOfDay_LedDimming)
+ {
+ bool TimelineShouldAdvance = false;
+ r32 OverrideBrightness = 0.0f;
+
+ time_range RangeIn = {};
+ if (SystemTimeIsInTimeRangeList(Context->SystemTime_Current,
+ LedOnTimes,
+ LedOnTimesCount,
+ &RangeIn) ||
+ DEBUGIgnoreLedOnTimeRange)
+ {
+
+ if (Blumen_TempShouldDimLeds(BLState))
+ {
+ OverrideBrightness = HighTemperatureBrightnessPercent;
+ }
+ else
+ {
+ OverrideBrightness = FullBrightnessPercent;
+ }
+ TimelineShouldAdvance = true;
+ }
+
+ State->AnimationSystem.TimelineShouldAdvance = TimelineShouldAdvance;
+ BLState->BrightnessPercent = OverrideBrightness;
+ }
+
+ // Dim the leds based on temp data
+ b8 ShouldDim = (State->AnimationSystem.UpdatesThisFrame > 0);
+ if (BLState->BrightnessPercent == 0) ShouldDim = true;
+ if (BLState->DEBUG_IgnoreWeatherDimmingLeds) ShouldDim = false;
+ if (ShouldDim)
+ {
+ led_buffer B0 = State->LedSystem.Buffers[0];
+ pixel P0 = B0.Colors[0];
+
+ for (u32 i = 0; i < State->LedSystem.BuffersCount; i++)
+ {
+ led_buffer Buffer = State->LedSystem.Buffers[i];
+ for (u32 j = 0; j < Buffer.LedCount; j++)
+ {
+ pixel* Color = Buffer.Colors + j;
+ Color->R = Color->R * BLState->BrightnessPercent;
+ Color->G = Color->G * BLState->BrightnessPercent;
+ Color->B = Color->B * BLState->BrightnessPercent;
+ }
+ }
+
+ led_buffer B1 = State->LedSystem.Buffers[0];
+ pixel P1 = B1.Colors[0];
+ }
+
+ // Send Status Packet
+ {
+ system_time LastSendTime = BLState->LastStatusUpdateTime;
+ r64 NanosSinceLastSend = ((r64)Context->SystemTime_Current.NanosSinceEpoch - (r64)LastSendTime.NanosSinceEpoch);
+ r64 SecondsSinceLastSend = NanosSinceLastSend * NanosToSeconds;
+ if (SecondsSinceLastSend >= STATUS_PACKET_FREQ_SECONDS)
+ {
+ BLState->LastStatusUpdateTime = Context->SystemTime_Current;
+ Log_Message(GlobalLogBuffer, "Attempting to Send Lumenarium Status\n");
+
+ blumen_packet Packet = {};
+ Packet.Type = PacketType_LumenariumStatus;
+ Packet.StatusPacket.NextMotorEventType = 0;
+ Packet.StatusPacket.NextEventTime = 0;
+
+ animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
+ if (ActiveAnim)
+ {
+ CopyMemoryTo(ActiveAnim->Name.Str, Packet.StatusPacket.AnimFileName,
+ Min(ActiveAnim->Name.Length, 32));
+ Packet.StatusPacket.AnimFileName[ActiveAnim->Name.Length] = 0;
+ }
+
+ gs_data Msg = StructToData(&Packet, blumen_packet);
+ MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg);
+
+ // there's no new information here, but by updating the log here,
+ // we're updating it at some infrequent but regular period that isnt
+ // every single frame
+ BLState->ShouldUpdateLog = true;
+ }
+ }
+
+ BlumenLumen_UpdateLog(State, BLState, *Context);
+}
+
+US_CUSTOM_DEBUG_UI(BlumenLumen_DebugUI)
+{
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory;
+ ui_interface* I = &State->Interface;
+
+ ui_BeginRow(I, BlumenDebug_Count);
+ for (u32 i = 0; i < BlumenDebug_Count; i++)
+ {
+ if (ui_Button(I, MakeString(BlDebugUiModeStrings[i])))
+ {
+ BLState->DebugMode = (bl_debug_ui_mode)i;
+ }
+ }
+ ui_EndRow(I);
+
+ switch (BLState->DebugMode)
+ {
+ case BlumenDebug_Motors:
+ {
+ motor_packet PendingPacket = BLState->DEBUG_PendingMotorPacket;
+
+ BLState->IgnoreTimeOfDay_MotorState = ui_ToggleText(I, MakeString("Motors Ignore Time Limit"), BLState->IgnoreTimeOfDay_MotorState);
+
+ for (u32 MotorIndex = 0; MotorIndex < BL_FLOWER_COUNT; MotorIndex++)
+ {
+ gs_string Label = PushStringF(State->Transient, 32, "Motor %d", MotorIndex);
+ ui_BeginRow(I, 5);
+ {
+ ui_Label(I, Label);
+
+ bool IsClosed = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Closed;
+ if (ui_ToggleText(I, MakeString("Closed (1)"), IsClosed))
+ {
+ PendingPacket.FlowerPositions[MotorIndex] = MotorState_Closed;
+ }
+ bool IsHOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_HalfOpen;
+ if (ui_ToggleText(I, MakeString("Half Open (3)"), IsHOpen))
+ {
+ PendingPacket.FlowerPositions[MotorIndex] = MotorState_HalfOpen;
+ }
+ bool IsMOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_MostlyOpen;
+ if (ui_ToggleText(I, MakeString("Mostly Open (4)"), IsMOpen))
+ {
+ PendingPacket.FlowerPositions[MotorIndex] = MotorState_MostlyOpen;
+ }
+ bool IsOpen = PendingPacket.FlowerPositions[MotorIndex] == MotorState_Open;
+ if (ui_ToggleText(I, MakeString("Open (2)"), IsOpen))
+ {
+ PendingPacket.FlowerPositions[MotorIndex] = MotorState_Open;
+ }
+ }
+ ui_EndRow(I);
+ }
+ BLState->DEBUG_PendingMotorPacket = PendingPacket;
+
+ if (ui_Button(I, MakeString("Send Motor Packet")))
+ {
+ blumen_packet Packet = {};
+ Packet.Type = PacketType_MotorState;
+ Packet.MotorPacket = BLState->DEBUG_PendingMotorPacket;
+ gs_data Msg = StructToData(&Packet, blumen_packet);
+ MessageQueue_Write(&BLState->OutgoingMsgQueue, Msg);
+ DEBUG_SentMotorCommand(Packet.MotorPacket, Context.ThreadContext);
+
+ }
+
+ motor_packet MotorPos = BLState->LastKnownMotorState;
+ ui_Label(I, MakeString("Current Motor Positions"));
+ {
+ for (u32 i = 0; i < BL_FLOWER_COUNT; i++)
+ {
+ ui_BeginRow(I, 2);
+ gs_string MotorStr = PushStringF(State->Transient, 32,
+ "Motor %d",
+ i);
+ ui_Label(I, MotorStr);
+
+ gs_string StateStr = {};
+ switch (MotorPos.FlowerPositions[i])
+ {
+ case MotorState_Closed: {
+ StateStr = MakeString("Closed");
+ } break;
+ case MotorState_HalfOpen: {
+ StateStr = MakeString("Half Open");
+ } break;
+ case MotorState_MostlyOpen: {
+ StateStr = MakeString("Mostly Open");
+ } break;
+ case MotorState_Open: {
+ StateStr = MakeString("Open");
+ } break;
+
+ default:
+ {
+ StateStr = MakeString("Invalid Value");
+ } break;
+ }
+
+ ui_Label(I, StateStr);
+ ui_EndRow(I);
+ }
+ }
+
+ ui_Label(I, MakeString("Set Internal Motor State:"));
+ if (ui_Button(I, MakeString("Closed")))
+ {
+ motor_status_packet Motor = {};
+ Motor.Pos.FlowerPositions[0] = MotorState_Closed;
+ Motor.Pos.FlowerPositions[1] = MotorState_Closed;
+ Motor.Pos.FlowerPositions[2] = MotorState_Closed;
+ Motor.Temperature = 16;
+
+ BlumenLumen_UpdateMotorState(BLState, Motor, Context);
+ }
+ if (ui_Button(I, MakeString("Open")))
+ {
+ motor_status_packet Motor = {};
+ Motor.Pos.FlowerPositions[0] = MotorState_Open;
+ Motor.Pos.FlowerPositions[1] = MotorState_Open;
+ Motor.Pos.FlowerPositions[2] = MotorState_Open;
+ Motor.Temperature = 16;
+
+ BlumenLumen_UpdateMotorState(BLState, Motor, Context);
+ }
+ } break;
+
+ case BlumenDebug_Leds:
+ {
+ BLState->DEBUG_IgnoreWeatherDimmingLeds = ui_LabeledToggle(I, MakeString("Ignore Weather Dimming Leds"), BLState->DEBUG_IgnoreWeatherDimmingLeds);
+
+ BLState->IgnoreTimeOfDay_LedDimming = ui_ToggleText(I, MakeString("Leds Ignore Time Limit"), BLState->IgnoreTimeOfDay_LedDimming);
+
+ if (ui_BeginLabeledDropdown(I, MakeString("Phrase"), MakeString(BLState->PendingPhrase.Phrase)))
+ {
+ u32 ListCount = BLState->PhraseHueMap.Count;
+ ui_BeginList(I, MakeString("Phrase List"), 5, ListCount);
+ for (u32 i = 0; i < ListCount; i++)
+ {
+ gs_string Str = MakeString(BLState->PhraseHueMap.Phrases[i]);
+ if (ui_Button(I, Str))
+ {
+ BLState->PendingPhrase = PhraseHueMap_Get(BLState->PhraseHueMap, i);
+ BLState->DebugHue = BLState->PendingPhrase;
+ }
+ }
+ ui_EndList(I);
+ }
+ ui_EndLabeledDropdown(I);
+ if (ui_Button(I, MakeString("Say Phrase")))
+ {
+ gs_string DebugStr = PushString(State->Transient, 256);
+ BLState->NextHotHue = BLState->PendingPhrase;
+ BlumenLumen_ApplyNextHotHue(BLState, Context, &DebugStr, State);
+ }
+
+ ui_Label(I, MakeString("Phrase Constructor"));
+ BLState->DebugOverrideHue = ui_ToggleText(I, MakeString("Override Hue"), BLState->DebugOverrideHue);
+ if (BLState->DebugOverrideHue)
+ {
+ phrase_hue PHue = BLState->DebugHue;
+ PHue.Hue0.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue0"), (r32)PHue.Hue0.HSV.x, 0, 360);
+ PHue.Hue1.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue1"), (r32)PHue.Hue1.HSV.x, 0, 360);
+ PHue.Hue2.HSV.x = (r64)ui_LabeledRangeSlider(I, MakeString("Hue2"), (r32)PHue.Hue2.HSV.x, 0, 360);
+ PHue.Granularity = (u32)ui_LabeledRangeSlider(I, MakeString("Granularity"), (r32)PHue.Granularity, 0, 5);
+ PHue.Speed = ui_LabeledRangeSlider(I, MakeString("Speed"), PHue.Speed, 0, 4);
+
+ gs_string PatternOptions[HuePattern_Count] = {};
+ PatternOptions[HuePattern_Patchy] = MakeString("patchy");
+ PatternOptions[HuePattern_Wavy] = MakeString("wavy");
+
+ gs_string CPattern = PatternOptions[PHue.Pattern];
+ if (ui_BeginLabeledDropdown(I, MakeString("Pattern"), CPattern))
+ {
+ for (u32 i = 0; i < HuePattern_Count; i++)
+ {
+ if (ui_Button(I, PatternOptions[i]))
+ {
+ PHue.Pattern = i;
+ }
+ }
+ }
+ ui_EndLabeledDropdown(I);
+
+
+ gs_string AddInOptions[AddIn_Count] = {};
+ AddInOptions[AddIn_None] = MakeString("NA");
+ AddInOptions[AddIn_Waves] = MakeString("waves");
+ AddInOptions[AddIn_Rotary] = MakeString("rotary");
+
+ gs_string CAddIn = AddInOptions[PHue.AddIn];
+ if (ui_BeginLabeledDropdown(I, MakeString("Add In"), CAddIn))
+ {
+ for (u32 i = 0; i < AddIn_Count; i++)
+ {
+ if (ui_Button(I, AddInOptions[i]))
+ {
+ PHue.AddIn = i;
+ }
+ }
+ }
+ ui_EndLabeledDropdown(I);
+ BLState->DebugHue = PHue;
+ }
+
+
+ InterfaceAssert(I->PerFrameMemory);
+ }break;
+
+ case BlumenDebug_Awaken:
+ {
+ ui_Label(I, MakeString("Step 1:"));
+ ui_Label(I, MakeString("Leds off, flowers closed"));
+ if (ui_Button(I, MakeString("Prepare")))
+ {
+ // motors closed
+ blumen_packet M = {};
+ M.Type = PacketType_MotorState;
+ M.MotorPacket.FlowerPositions[0] = MotorState_Closed;
+ M.MotorPacket.FlowerPositions[1] = MotorState_Closed;
+ M.MotorPacket.FlowerPositions[2] = MotorState_Closed;
+ gs_data D = StructToData(&M, blumen_packet);
+ MessageQueue_Write(&BLState->OutgoingMsgQueue, D);
+
+ // animation
+ State->AnimationSystem.RepeatMode = AnimationRepeat_Single;
+ AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup,
+ BLState->OffAnimHandle,
+ VoiceCommandFadeDuration);
+
+ BLState->PatternMode = BlumenPattern_NoControl;
+ BLState->IgnoreTimeOfDay_LedDimming = true;
+ BLState->IgnoreTimeOfDay_MotorState = true;
+ }
+
+ ui_Label(I, MakeString("Step 2:"));
+ if (ui_Button(I, MakeString("Begin Light Show")))
+ {
+ AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup,
+ BLState->AwakenHandle,
+ VoiceCommandFadeDuration);
+ }
+
+ ui_Label(I, MakeString("Step 3:"));
+ if (ui_Button(I, MakeString("Open Flowers")))
+ {
+ // motors closed
+ blumen_packet M = {};
+ M.Type = PacketType_MotorState;
+ M.MotorPacket.FlowerPositions[0] = MotorState_Open;
+ M.MotorPacket.FlowerPositions[1] = MotorState_Open;
+ M.MotorPacket.FlowerPositions[2] = MotorState_Open;
+ gs_data D = StructToData(&M, blumen_packet);
+ MessageQueue_Write(&BLState->OutgoingMsgQueue, D);
+ }
+
+ ui_Label(I, MakeString("Step 4:"));
+ ui_Label(I, MakeString("Resets Lumenarium"));
+ if (ui_Button(I, MakeString("Complete")))
+ {
+ BLState->IgnoreTimeOfDay_LedDimming = false;
+ BLState->IgnoreTimeOfDay_MotorState = false;
+ BlumenLumen_SetPatternMode(BlumenPattern_Standard, GlobalAnimTransitionSpeed, &State->AnimationSystem,
+ BLState);
+ }
+ }break;
+
+ InvalidDefaultCase;
+ }
+}
+
+US_CUSTOM_CLEANUP(BlumenLumen_CustomCleanup)
+{
+ blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory;
+ BLState->Running = false;
+}
+
+internal user_space_desc
+BlumenLumen_UserSpaceCreate()
+{
+ user_space_desc Result = {};
+ Result.LoadPatterns = BlumenLumen_LoadPatterns;
+ Result.CustomInit = BlumenLumen_CustomInit;
+ Result.CustomUpdate = BlumenLumen_CustomUpdate;
+ Result.CustomDebugUI = BlumenLumen_DebugUI;
+ Result.CustomCleanup = BlumenLumen_CustomCleanup;
+ return Result;
+}
+
+#define BLUMEN_LUMEN_CPP
+#endif // BLUMEN_LUMEN_CPP
diff --git a/src/app/ss_blumen_lumen/blumen_lumen.h b/src/app/ss_blumen_lumen/blumen_lumen.h
new file mode 100644
index 0000000..fb04238
--- /dev/null
+++ b/src/app/ss_blumen_lumen/blumen_lumen.h
@@ -0,0 +1,393 @@
+//
+// File: blumen_lumen.h
+// Author: Peter Slattery
+// Creation Date: 2021-01-15
+//
+#ifndef BLUMEN_LUMEN_H
+
+#include "message_queue.h"
+
+enum bl_debug_ui_mode
+{
+ BlumenDebug_Motors,
+ BlumenDebug_Leds,
+ BlumenDebug_Awaken,
+
+ BlumenDebug_Count,
+};
+
+char* BlDebugUiModeStrings[] = {
+ "Motors",
+ "Leds",
+ "Awaken",
+};
+
+enum bl_pattern_mode
+{
+ BlumenPattern_Standard,
+ BlumenPattern_VoiceCommand,
+ BlumenPattern_NoControl,
+
+ BlumenPattern_Count,
+};
+
+enum bl_python_packet_type
+{
+ PacketType_Invalid = 0,
+ PacketType_PatternCommand = 1,
+ PacketType_MotorState = 2,
+ PacketType_Temperature = 3,
+ PacketType_LumenariumStatus = 4,
+};
+
+enum bl_motor_state_value
+{
+ MotorState_Invalid = 0,
+
+ MotorState_Closed = 1,
+ MotorState_Open = 2,
+ MotorState_HalfOpen = 3,
+ MotorState_MostlyOpen = 4,
+};
+
+#pragma pack(push, 1)
+typedef struct motor_packet
+{
+ u8 FlowerPositions[3];
+} motor_packet;
+
+typedef struct motor_status_packet
+{
+ motor_packet Pos;
+ u8 MotorStatus[3];
+ u16 Temperature;
+
+} motor_status_packet;
+
+typedef struct microphone_packet
+{
+ b8 ChangeAnimation;
+ char AnimationFileName[32];
+ b8 SetLayer;
+ char LayerName[32];
+ r32 LayerOpacity;
+ b8 SetLayerParamColor;
+ char LayerParamColor[7];
+ r32 OverrideDuration;
+} microphone_packet;
+
+typedef struct temp_packet
+{
+ s8 Temperature;
+} temp_packet;
+
+enum motor_event_type
+{
+ MotorEvent_Close = 0,
+ MotorEvent_Open = 1,
+};
+
+typedef struct status_packet
+{
+ u8 NextMotorEventType;
+ // u16 Padding;
+ u32 NextEventTime;
+
+ char AnimFileName[32];
+} status_packet;
+
+typedef struct blumen_packet
+{
+ u8 Type;
+ union
+ {
+ motor_packet MotorPacket;
+ motor_status_packet MotorStatusPacket;
+ microphone_packet MicPacket;
+ temp_packet TempPacket;
+ status_packet StatusPacket;
+ };
+} blumen_packet;
+
+#pragma pack(pop)
+
+// TODO(pjs): Refactor this -> blumen_network_job_state
+struct mic_listen_job_data
+{
+ bool* Running;
+
+ platform_socket_manager* SocketManager;
+ blumen_network_msg_queue* IncomingMsgQueue;
+
+ blumen_network_msg_queue* OutgoingMsgQueue;
+
+ // Status
+ bool IsConnected;
+};
+
+typedef struct time_range
+{
+ s32 StartHour;
+ s32 StartMinute;
+
+ s32 EndHour;
+ s32 EndMinute;
+} time_range;
+
+internal bool
+SystemTimeIsBeforeTime(system_time SysTime, s32 Hour, s32 Minute)
+{
+ bool Result = false;
+ if (SysTime.Hour == Hour) {
+ Result = SysTime.Minute < Minute;
+ } else {
+ Result = SysTime.Hour < Hour;
+ }
+ return Result;
+}
+
+internal bool
+SystemTimeIsAfterTime(system_time SysTime, s32 Hour, s32 Minute)
+{
+ bool Result = false;
+ if (SysTime.Hour == Hour) {
+ Result = SysTime.Minute >= Minute;
+ } else {
+ Result = SysTime.Hour > Hour;
+ }
+ return Result;
+}
+
+internal bool
+SystemTimeIsInTimeRange(system_time SysTime, time_range Range)
+{
+ bool Result = false;
+
+ bool IsAfterStartTime = SystemTimeIsAfterTime(SysTime, Range.StartHour, Range.StartMinute);
+ bool IsBeforeEndTime = SystemTimeIsBeforeTime(SysTime, Range.EndHour, Range.EndMinute);
+ Result = IsAfterStartTime && IsBeforeEndTime;
+
+ return Result;
+}
+
+internal bool
+SystemTimeIsInTimeRangeList(system_time SysTime, time_range* Ranges, u32 RangesCount, time_range* RangeOut = 0)
+{
+ bool Result = false;
+ for (u32 i = 0; i < RangesCount; i++)
+ {
+ time_range Range = Ranges[i];
+ bool CurrTimeInRange = SystemTimeIsInTimeRange(SysTime, Range);
+ if (CurrTimeInRange)
+ {
+ Result = true;
+ if (RangeOut != 0) {
+ *RangeOut = Range;
+ }
+ break;
+ }
+ }
+ return Result;
+}
+
+#include "blumen_lumen_settings.h"
+
+struct blumen_lumen_state
+{
+ bool Running;
+
+ blumen_network_msg_queue IncomingMsgQueue;
+ blumen_network_msg_queue OutgoingMsgQueue;
+
+ temp_job_req JobReq;
+
+ led_strip_list StemStrips[BL_FLOWER_COUNT];
+
+ platform_thread_handle MicListenThread;
+ mic_listen_job_data MicListenJobData;
+
+ motor_packet LastKnownMotorState;
+ u64 LastTimeMotorStateChanged[BL_FLOWER_COUNT];
+ b8 ShouldDimUpperLeds[BL_FLOWER_COUNT];
+
+ // NOTE(pjs): Based on temperature data from weatherman
+ // dim the leds.
+ r32 BrightnessPercent;
+ s8 LastTemperatureReceived;
+ system_time LastStatusUpdateTime;
+
+ system_time LastSendTime;
+ bl_motor_state_value LastSendState;
+
+ phrase_hue StandardPatternHues;
+ r32 AssemblyColorsTransitionTimeLeft[BL_FLOWER_COUNT];
+ phrase_hue NextAssemblyColors[BL_FLOWER_COUNT];
+ phrase_hue AssemblyColors[BL_FLOWER_COUNT];
+ u32 LastAssemblyColorSet;
+
+ // The indices of this array are the index the clear core uses to
+ // represent a motor.
+ // The values of the array are the names Lumenarium uses to
+ // represent assemblies.
+ //
+ u32 AssemblyNameToClearCoreMapCount;
+ u64* AssemblyNameToClearCore_Names;
+
+ bl_pattern_mode PatternMode;
+ animation_handle_array ModeAnimations[BlumenPattern_Count];
+ animation_handle OffAnimHandle;
+ animation_handle AwakenHandle;
+
+ phrase_hue_map PhraseHueMap;
+
+ bool InPhraseReceptionMode;
+ phrase_hue NextHotHue;
+ system_time TimePhraseReceptionBegan;
+ system_time TimeLastPhraseReceived;
+
+ system_time TimeLastSetToVoiceMode;
+ phrase_hue LastHuePhrase;
+
+ r32 PatternSpeed;
+
+ // Debug
+ bl_debug_ui_mode DebugMode;
+
+ motor_packet DEBUG_PendingMotorPacket;
+ bool DEBUG_IgnoreWeatherDimmingLeds;
+
+ bool ShouldUpdateLog;
+ bool IgnoreTimeOfDay_LedDimming;
+ bool IgnoreTimeOfDay_MotorState;
+
+ phrase_hue PendingPhrase;
+
+ bool DebugOverrideHue;
+ phrase_hue DebugHue;
+};
+
+internal bool
+Blumen_TempShouldDimLeds(blumen_lumen_state* BLState)
+{
+ bool Result = BLState->LastTemperatureReceived > MinHighTemperature;
+ return Result;
+}
+
+#include "message_queue.cpp"
+
+internal void
+BlumenLumen_SetNextHue(blumen_lumen_state* BLState, u32 AssemblyIndex, phrase_hue Hue)
+{
+#if 1
+ BLState->NextAssemblyColors[AssemblyIndex] = Hue;
+ BLState->AssemblyColorsTransitionTimeLeft[AssemblyIndex] = PhraseHueFadeInDuration;
+#else
+ BLState->AssemblyColors[AssemblyIndex] = Hue;
+#endif
+}
+
+internal void
+BlumenLumen_AdvanceHueFade(blumen_lumen_state* BLState, context Context)
+{
+ for (u32 i = 0; i < BL_FLOWER_COUNT; i++)
+ {
+ r32 T = BLState->AssemblyColorsTransitionTimeLeft[i];
+ if (T > 0)
+ {
+ T -= Context.DeltaTime;
+ if (T <= 0)
+ {
+ BLState->AssemblyColors[i] = BLState->NextAssemblyColors[i];
+ }
+ BLState->AssemblyColorsTransitionTimeLeft[i] = T;
+ }
+ }
+}
+
+internal phrase_hue
+BlumenLumen_GetCurrentHue(blumen_lumen_state* BLState, assembly Assembly)
+{
+ phrase_hue Result = {};
+
+ switch (BLState->PatternMode)
+ {
+ case BlumenPattern_NoControl:
+ case BlumenPattern_Standard:
+ {
+ Result = BLState->StandardPatternHues;
+ }break;
+
+ case BlumenPattern_VoiceCommand:
+ {
+ u32 i = Assembly.AssemblyIndex % 3;
+ r32 T = BLState->AssemblyColorsTransitionTimeLeft[i];
+ if (T > 0)
+ {
+ T = Clamp(T / PhraseHueFadeInDuration, 0, 1);
+ Result = LerpPhraseHue(T, BLState->NextAssemblyColors[i], BLState->AssemblyColors[i]);
+ } else {
+ Result = BLState->AssemblyColors[i];
+ }
+ }break;
+ }
+
+ return Result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// If you change anything, exit lumenarium if its running
+// then in this application hit f1 to compile then
+// go to remedybg (the debugger) and hit f5
+
+
+// don't touch this
+u8 LastPosition = 1;
+
+u8 ClosedValue = 1;
+u8 OpenValue = 2;
+
+
+r64 MotorTimeElapsed = 0;
+r64 OpenClosePeriod = 15.0f;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#define BLUMEN_LUMEN_H
+#endif // BLUMEN_LUMEN_H
\ No newline at end of file
diff --git a/src/app/ss_blumen_lumen/blumen_lumen_settings.h b/src/app/ss_blumen_lumen/blumen_lumen_settings.h
new file mode 100644
index 0000000..59d4767
--- /dev/null
+++ b/src/app/ss_blumen_lumen/blumen_lumen_settings.h
@@ -0,0 +1,135 @@
+/* date = March 27th 2021 2:50 pm */
+
+#ifndef BLUMEN_LUMEN_SETTINGS_H
+#define BLUMEN_LUMEN_SETTINGS_H
+
+// Hey you never know, might need to change this some day lololol
+// The number of flowers in the sculpture. Used to size all sorts of
+// arrays. Maybe don't touch this unless you really know what you're doing?
+#define BL_FLOWER_COUNT 3
+
+// The path to the three flower assembly files
+// PS is 90% sure you don't need to touch these ever
+gs_const_string Flower0AssemblyPath = ConstString("data/ss_blumen_one.fold");
+gs_const_string Flower1AssemblyPath = ConstString("data/ss_blumen_two.fold");
+gs_const_string Flower2AssemblyPath = ConstString("data/ss_blumen_three.fold");
+
+// The path to the phrase map CSV. Can be an absolute path, or relative
+// to the app_run_tree folder
+gs_const_string PhraseMapCSVPath = ConstString("C:/projects/flowers-sound/flower_codes.tsv");
+char PhraseMapCSVSeparator = '\t';
+
+// Search Strings for which folders to find ambient animation files and
+// voice animation files in.
+// these search patterns should always end in *.foldanim so they only
+// return valid animation files
+gs_const_string AmbientPatternFolder = ConstString("data/blumen_animations/ambient_patterns/*.foldanim");
+gs_const_string VoicePatternFolder = ConstString("data/blumen_animations/audio_responses/*.foldanim");
+
+// The times of day when the motors should be open.
+//
+// @TimeFormat: documentation follows
+// these are in the format { Start_Hour, Start_Minute, End_Hour, End_Minute }
+// Hours are in the range 0-23 inclusive
+// Minutes are in the range 0-59 inclusive
+//
+// NOTE: There is no need to modify the MotorOpenTimesCount variable -
+// it is a compile time constant that gets calculated automatically
+global time_range MotorOpenTimes[] = {
+ { 8, 00, 12, 00 }, // 8:00am to 12:00pm
+ { 12, 30, 18, 00 }, // 12:30pm to 06:00pm
+ { 18, 30, 22, 00 }, // 6:30pm to 10:00pm
+};
+global u32 MotorOpenTimesCount = CArrayLength(MotorOpenTimes); // do not edit
+
+// Lumenarium repeatedly resends the current motor state to the python
+// server. This variable determines how much time elapses between each
+// message.
+global r32 MotorResendStatePeriod = 90.0f; // seconds
+
+// The times of day when the leds should be on
+// Search for @TimeFormat to find documentation
+global time_range LedOnTimes[] = {
+ { 17, 00, 23, 59 },
+ { 00, 00, 06, 30 },
+};
+global u32 LedOnTimesCount = CArrayLength(LedOnTimes); // do not edit
+global b8 DEBUGIgnoreLedOnTimeRange = false;
+
+// How long it takes to fade from the default pattern to the
+// voice activated pattern
+r32 VoiceCommandFadeDuration = 0.5f; // in seconds
+
+// How long the voice activated pattern will remain active
+// without additional voice commands, before fading back to
+// default behaviour.
+// ie.
+// if this is set to 30 seconds, upon receiving a voice command
+// lumenarium will fade to the requested pattern/color palette
+// and then wait 30 seconds before fading back to the original
+// pattern. If, in that 30 second window, another voice command
+// is issued, lumenarium will reset the 30 second counter.
+r64 VoiceCommandSustainDuration = 120.0; // in seconds
+
+// When we send a Motor Close command, we don't want the upper leds to
+// immediately turn off. Instead, we want to wait until the flower is
+// at least some of the way closed. This variable dictates how long
+// we wait for.
+// For example:
+// 1. We send a 'motor close' command to the clear core
+// 2. the clear core sends back a 'motor closed' state packet
+// 3. We begin a timer
+// 4. When the timer reaches the value set in this variable,
+// we turn the upper leds off.
+//
+// NOTE: This is not a symmetric operation. When we send a 'motor open'
+// command, we want to immediately turn the upper leds on so they appear
+// to have been on the whole time.
+r64 TurnUpperLedsOffAfterMotorCloseCommandDelay = 120.0; // in seconds
+
+
+// NOTE: Temperature & Time of Day Based Led Brightness Settings
+
+// The temperature above which we dim the leds to
+// HighTemperatureBrightnessPercent (below)
+//
+// NOTE: this is an 8bit signed integer so its range is
+// -128 to 127, not that we should need either of those extremes for
+// this, but just a note that you can't just put anything in here.
+s8 MinHighTemperature = 26;
+
+// The percent brightness we set leds to during high temperatures.
+// A value in the range 0:1 inclusive
+// This is multiplied by each pixels R, G, & B channels before being
+// sent. So if it is set to .1f, then the maximum brightness value sent
+// to any channel of any pixel will be 25 (255 * .1 = 25).
+r32 HighTemperatureBrightnessPercent = 0.25f;
+
+// The percent brightness we set leds to when no other conditions apply
+// A value in the range 0:1 inclusive.
+// Probably wants to be something high like 1 but we might want to
+// lower it for heat reasons?
+r32 FullBrightnessPercent = 0.50f;
+
+// A global modifier so Joerg can just slow all the patterns right down
+// XD
+// This is a percent - so 1 is full speed, 0.1f is 1/10 full speed and
+// 2 is 2x as fast.
+r32 GlobalAnimSpeed = 1.0f;
+
+// How long it takes to fade from one animation to the next.
+// This is used both for transitioning between animation files
+// as well as transitioning from Standard pattern mode to voice
+// activated mode
+r32 GlobalAnimTransitionSpeed = 0.0f;
+
+// how long it takes to fade from the old voice hue to the new one
+r32 PhraseHueFadeInDuration = 0.01f; // seconds
+
+r64 PhrasePriorityMessageGroupingTime = 0.1f;
+
+// How often should Lumenarium send its status to the python server?
+//
+#define STATUS_PACKET_FREQ_SECONDS 10 // in seconds
+
+#endif //BLUMEN_LUMEN_SETTINGS_H
diff --git a/src/app/ss_blumen_lumen/gfx_math.h b/src/app/ss_blumen_lumen/gfx_math.h
new file mode 100644
index 0000000..34b667d
--- /dev/null
+++ b/src/app/ss_blumen_lumen/gfx_math.h
@@ -0,0 +1,461 @@
+/* date = March 31st 2021 2:19 am */
+
+#ifndef GFX_MATH_H
+#define GFX_MATH_H
+
+internal r32
+Smoothstep(r32 T)
+{
+ r32 Result = (T * T * (3 - (2 * T)));
+ return Result;
+}
+internal r32
+Smoothstep(r32 T, r32 A, r32 B)
+{
+ return LerpR32(Smoothstep(T), A, B);
+}
+internal v3
+Smoothstep(v3 P)
+{
+ v3 R = {};
+ R.x = Smoothstep(P.x);
+ R.y = Smoothstep(P.y);
+ R.z = Smoothstep(P.z);
+ return R;
+}
+
+internal v3
+AbsV3(v3 P)
+{
+ v3 Result = {};
+ Result.x = Abs(P.x);
+ Result.y = Abs(P.y);
+ Result.z = Abs(P.z);
+ return Result;
+}
+
+internal v2
+FloorV2(v2 P)
+{
+ v2 Result = {};
+ Result.x = FloorR32(P.x);
+ Result.y = FloorR32(P.y);
+ return Result;
+}
+internal v3
+FloorV3(v3 P)
+{
+ v3 Result = {};
+ Result.x = FloorR32(P.x);
+ Result.y = FloorR32(P.y);
+ Result.z = FloorR32(P.z);
+ return Result;
+}
+
+internal v2
+FractV2(v2 P)
+{
+ v2 Result = {};
+ Result.x = FractR32(P.x);
+ Result.y = FractR32(P.y);
+ return Result;
+}
+internal v3
+FractV3(v3 P)
+{
+ v3 Result = {};
+ Result.x = FractR32(P.x);
+ Result.y = FractR32(P.y);
+ Result.z = FractR32(P.z);
+ return Result;
+}
+
+internal v2
+SinV2(v2 P)
+{
+ v2 Result = {};
+ Result.x = SinR32(P.x);
+ Result.y = SinR32(P.y);
+ return Result;
+}
+internal v3
+SinV3(v3 P)
+{
+ v3 Result = {};
+ Result.x = SinR32(P.x);
+ Result.y = SinR32(P.y);
+ Result.y = SinR32(P.z);
+ return Result;
+}
+
+internal r32
+Hash1(v2 P)
+{
+ v2 Result = FractV2( P * 0.3183099f ) * 50.f;
+ return FractR32(P.x * P.y * (P.x + P.y));
+}
+
+internal r32
+Hash1(r32 N)
+{
+ return FractR32(N * 17.0f * FractR32(N * 0.3183099f));
+}
+
+internal v2
+Hash2(r32 N)
+{
+ v2 P = V2MultiplyPairwise(SinV2(v2{N,N+1.0f}), v2{43758.5453123f,22578.1459123f});
+ return FractV2(P);
+}
+
+internal v2
+Hash2(v2 P)
+{
+ v2 K = v2{ 0.3183099f, 0.3678794f };
+ v2 Kp = v2{K.y, K.x};
+ v2 R = V2MultiplyPairwise(P, K) + Kp;
+ return FractV2( K * 16.0f * FractR32( P.x * P.y * (P.x + P.y)));
+}
+
+internal v3
+Hash3(v2 P)
+{
+ v3 Q = v3{};
+ Q.x = V2Dot(P, v2{127.1f, 311.7f});
+ Q.y = V2Dot(P, v2{267.5f, 183.3f});
+ Q.z = V2Dot(P, v2{419.2f, 371.9f});
+ return FractV3(SinV3(Q) * 43758.5453f);
+}
+
+internal r32
+HashV3ToR32(v3 P)
+{
+ v3 Pp = FractV3(P * 0.3183099f + v3{0.1f, 0.1f, 0.1f});
+ Pp *= 17.0f;
+ r32 Result = FractR32(Pp.x * Pp.y * Pp.z * (Pp.x + Pp.y + Pp.z));
+ return Result;
+}
+
+internal r32
+Random(v2 N)
+{
+ v2 V = v2{12.9898f, 4.1414f};
+ return FractR32(SinR32(V2Dot(N, V)) * 43758.5453);
+}
+
+internal r32
+Noise2D(v2 P)
+{
+ v2 IP = FloorV2(P);
+ v2 U = FractV2(P);
+ U = V2MultiplyPairwise(U, U);
+ U = V2MultiplyPairwise(U, ((U * 2.0f) + v2{-3, -3}));
+
+ r32 A = LerpR32(U.x, Random(IP), Random(IP + v2{1.0f, 0}));
+ r32 B = LerpR32(U.x, Random(IP + v2{0, 1}), Random(IP + v2{1, 1}));
+ r32 Res = LerpR32(U.y, A, B);
+
+ return Res * Res;
+}
+
+internal r32
+Noise3D(v3 P)
+{
+ P = AbsV3(P);
+ v3 PFloor = FloorV3(P);
+ v3 PFract = FractV3(P);
+ v3 F = Smoothstep(PFract);
+
+ r32 Result = LerpR32(F.z,
+ LerpR32(F.y,
+ LerpR32(F.x,
+ HashV3ToR32(PFloor + v3{0, 0, 0}),
+ HashV3ToR32(PFloor + v3{1, 0, 0})),
+ LerpR32(F.x,
+ HashV3ToR32(PFloor + v3{0, 1, 0}),
+ HashV3ToR32(PFloor + v3{1, 1, 0}))),
+ LerpR32(F.y,
+ LerpR32(F.x,
+ HashV3ToR32(PFloor + v3{0, 0, 1}),
+ HashV3ToR32(PFloor + v3{1, 0, 1})),
+ LerpR32(F.x,
+ HashV3ToR32(PFloor + v3{0, 1, 1}),
+ HashV3ToR32(PFloor + v3{1, 1, 1}))));
+
+ Assert(Result >= 0 && Result <= 1);
+ return Result;
+}
+
+internal r32
+Noise3D_(v3 Pp)
+{
+ v3 P = FloorV3(Pp);
+ v3 W = FractV3(Pp);
+
+ //v3 U = W * W * W * (W * (W * 6.0f - 15.0f) + 10.0f);
+ v3 U = V3MultiplyPairwise(W, W * 6.0f - v3{15, 15, 15});
+ U = U + v3{10, 10, 10};
+ U = V3MultiplyPairwise(U, W);
+ U = V3MultiplyPairwise(U, W);
+ U = V3MultiplyPairwise(U, W);
+
+ r32 N = P.x + 317.0f * P.y + 157.0f * P.z;
+
+ r32 A = Hash1(N + 0.0f);
+ r32 B = Hash1(N + 1.0f);
+ r32 C = Hash1(N + 317.0f);
+ r32 D = Hash1(N + 317.0f);
+ r32 E = Hash1(N + 157.0f);
+ r32 F = Hash1(N + 158.0f);
+ r32 G = Hash1(N + 474.0f);
+ r32 H = Hash1(N + 475.0f);
+
+ r32 K0 = A;
+ r32 K1 = B - A;
+ r32 K2 = C - A;
+ r32 K3 = E - A;
+ r32 K4 = A - B - C + D;
+ r32 K5 = A - C - E + G;
+ r32 K6 = A - B - E + F;
+ r32 K7 = A + B + C - D + E - F - G + H;
+
+ return -1.0f + 2.0f * (K0 +
+ K1 * U.x +
+ K2 * U.y +
+ K3 * U.z +
+ K4 * U.x * U.y +
+ K5 * U.y + U.z +
+ K6 * U.z * U.x +
+ K7 * U.x * U.y * U.z);
+}
+
+internal r32
+Fbm2D(v2 P)
+{
+ r32 R = 0;
+ r32 Amp = 1.0;
+ r32 Freq = 1.0;
+ for (u32 i = 0; i < 3; i++)
+ {
+ R += Amp * Noise2D(P * Freq);
+ Amp *= 0.5f;
+ Freq *= 1.0f / 0.5f;
+ }
+ return R;
+}
+
+global m44 M3 = m44{
+ 0.00f, 0.80f, 0.60f, 0,
+ -0.80f, 0.36f, -0.48f, 0,
+ -0.60f, -0.48f, 0.64f, 0,
+ 0, 0, 0, 1
+};
+
+internal r32
+Fbm3D(v3 P)
+{
+ v3 X = P;
+ r32 F = 2.0f;
+ r32 S = 0.5f;
+ r32 A = 0.0f;
+ r32 B = 0.5f;
+ for (u32 i = 0; i < 4; i++)
+ {
+ r32 N = Noise3D(X);
+ A += B * N;
+ B *= S;
+ v4 Xp = M3 * ToV4Point(X);
+ X = Xp.xyz * F;
+ }
+
+ return A;
+}
+
+internal r32
+Fbm3D(v3 P, r32 T)
+{
+ v3 Tt = v3{T, T, T};
+ r32 SinT = SinR32(T);
+ v3 Tv = v3{SinT, SinT, SinT};
+ v3 Pp = P;
+ r32 F = 0.0;
+
+ F += 0.500000f * Noise3D(Pp + Tt); Pp = Pp * 2.02;
+ //F += 0.031250f * Noise3D(Pp); Pp = Pp * 2.01;
+ F += 0.300000f * Noise3D(Pp - Tt); Pp = Pp * 2.03;
+ F += 0.125000f * Noise3D(Pp); Pp = Pp * 2.01;
+ F += 0.062500f * Noise3D(Pp + Tt); Pp = Pp * 2.04;
+ //F += 0.015625f * Noise3D(Pp + Tv);
+ r32 D = 0.9875f;
+
+ F = F / D;
+ return F;
+}
+
+internal r32
+Voronoise(v2 P, r32 U, r32 V)
+{
+ r32 K = 1.0f + 63.0f + PowR32(1.0f - V, 6.0f);
+
+ v2 I = FloorV2(P);
+ v2 F = FractV2(P);
+
+ v2 A = v2{0, 0};
+ for (s32 y = -2; y <= 2; y++)
+ {
+ for (s32 x = -2; x <= 2; x++)
+ {
+ v2 G = v2{(r32)x, (r32)y};
+ v3 O = V3MultiplyPairwise(Hash3(I + G), v3{U, U, 1.0f});
+ v2 D = G - F + O.xy;
+ r32 W = PowR32(1.0f - Smoothstep(V2Mag(D), 0.0f, 1.414f), K);
+ A += v2{O.z * W, W};
+ }
+ }
+
+ return A.x / A.y;
+}
+
+
+v4 RGBToHSV(v4 In)
+{
+ v4 Result = {};
+
+ r32 Min = Min(In.r, Min(In.g, In.b));
+ r32 Max = Max(In.r, Max(In.g, In.b));
+
+ r32 V = Max;
+ r32 Delta = Max - Min;
+ r32 S = 0;
+ r32 H = 0;
+ if( Max != 0 )
+ {
+ S = Delta / Max;
+
+ if( In.r == Max )
+ {
+ H = ( In.g - In.b ) / Delta; // between yellow & magenta
+ }
+ else if( In.g == Max )
+ {
+ H = 2 + ( In.b - In.r ) / Delta; // between cyan & yellow
+ }
+ else
+ {
+ H = 4 + ( In.r - In.g ) / Delta; // between magenta & cyan
+ }
+ H *= 60; // degrees
+ if( H < 0 )
+ H += 360;
+ Result = v4{H, S, V, 1};
+ }
+ else
+ {
+ // r = g = b = 0
+ // s = 0, v is undefined
+ S = 0;
+ H = -1;
+ Result = v4{H, S, 1, 1};
+ }
+
+ return Result;
+}
+
+v4 HSVToRGB (v4 In)
+{
+ float Hue = In.x;
+ /*
+while (Hue > 360.0f) { Hue -= 360.0f; }
+ while (Hue < 0.0f) { Hue += 360.0f; }
+ */
+ Hue = ModR32(Hue, 360.0f);
+ if (Hue < 0) { Hue += 360.0f; }
+ if (Hue == MinR32) { Hue = 0; }
+ if (Hue == MaxR32) { Hue = 360; }
+ Assert(Hue >= 0 && Hue < 360);
+
+ float Sat = In.y;
+ float Value = In.z;
+
+ float hh, p, q, t, ff;
+ long i;
+ v4 Result = {};
+ Result.a = In.a;
+
+ if(Sat <= 0.0f) { // < is bogus, just shuts up warnings
+ Result.r = Value;
+ Result.g = Value;
+ Result.b = Value;
+ return Result;
+ }
+ hh = Hue;
+ if(hh >= 360.0f) hh = 0.0f;
+ hh /= 60.0f;
+ i = (long)hh;
+ ff = hh - i;
+ p = Value * (1.0f - Sat);
+ q = Value * (1.0f - (Sat * ff));
+ t = Value * (1.0f - (Sat * (1.0f - ff)));
+
+ switch(i) {
+ case 0:
+ {Result.r = Value;
+ Result.g = t;
+ Result.b = p;
+ }break;
+
+ case 1:
+ {
+ Result.r = q;
+ Result.g = Value;
+ Result.b = p;
+ }break;
+
+ case 2:
+ {
+ Result.r = p;
+ Result.g = Value;
+ Result.b = t;
+ }break;
+
+ case 3:
+ {
+ Result.r = p;
+ Result.g = q;
+ Result.b = Value;
+ }break;
+
+ case 4:
+ {
+ Result.r = t;
+ Result.g = p;
+ Result.b = Value;
+ }break;
+
+ case 5:
+ default:
+ {
+ Result.r = Value;
+ Result.g = p;
+ Result.b = q;
+ }break;
+ }
+
+ return Result;
+}
+
+internal pixel
+V4ToRGBPixel(v4 C)
+{
+ C.x = Clamp01(C.x);
+ C.y = Clamp01(C.y);
+ C.z = Clamp01(C.z);
+
+ pixel Result = {};
+ Result.R = (u8)(C.x * 255);
+ Result.G = (u8)(C.y * 255);
+ Result.B = (u8)(C.z * 255);
+ return Result;
+}
+
+#endif //GFX_MATH_H
diff --git a/src/app/ss_blumen_lumen/message_queue.cpp b/src/app/ss_blumen_lumen/message_queue.cpp
new file mode 100644
index 0000000..4d4211d
--- /dev/null
+++ b/src/app/ss_blumen_lumen/message_queue.cpp
@@ -0,0 +1,83 @@
+
+internal void
+MessageQueue_Init(blumen_network_msg_queue* Queue, gs_memory_arena* Arena)
+{
+ gs_data MessageQueueData = PushSize(Arena, DEFAULT_QUEUE_ENTRY_SIZE * BLUMEN_MESSAGE_QUEUE_COUNT);
+ gs_memory_cursor C = MemoryCursorCreateFromData(MessageQueueData);
+
+ for (u32 i = 0; i < BLUMEN_MESSAGE_QUEUE_COUNT; i++)
+ {
+ Queue->Buffers[i] = MemoryCursorPushSize(&C, DEFAULT_QUEUE_ENTRY_SIZE);
+ }
+}
+
+internal bool
+MessageQueue_CanWrite(blumen_network_msg_queue Queue)
+{
+ bool Result = ((Queue.WriteHead >= Queue.ReadHead) ||
+ (Queue.WriteHead < Queue.ReadHead));
+ return Result;
+}
+
+internal bool
+MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg)
+{
+ Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE);
+
+ u32 Index = Queue->WriteHead;
+ Assert(Index >= 0 &&
+ Index < BLUMEN_MESSAGE_QUEUE_COUNT);
+
+ gs_data* Dest = Queue->Buffers + Index;
+ CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size);
+ Dest->Size = Msg.Size;
+
+ // NOTE(pjs): We increment write head at the end of writing so that
+ // a reader thread doesn't pull the message off before we've finished
+ // filling it out
+ Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT;
+ return true;
+}
+
+internal bool
+MessageQueue_CanRead(blumen_network_msg_queue Queue)
+{
+ bool Result = (Queue.ReadHead != Queue.WriteHead);
+ return Result;
+}
+
+internal gs_data
+MessageQueue_Peek(blumen_network_msg_queue* Queue)
+{
+ gs_data Result = {};
+ u32 ReadIndex = Queue->ReadHead;
+ if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT)
+ {
+ ReadIndex = 0;
+ }
+ Result = Queue->Buffers[ReadIndex];
+ return Result;
+}
+
+internal gs_data
+MessageQueue_Read(blumen_network_msg_queue* Queue)
+{
+ gs_data Result = {};
+ u32 ReadIndex = Queue->ReadHead++;
+ if (ReadIndex >= BLUMEN_MESSAGE_QUEUE_COUNT)
+ {
+ Queue->ReadHead = 0;
+ ReadIndex = 0;
+ }
+ Result = Queue->Buffers[ReadIndex];
+ return Result;
+}
+
+internal void
+MessageQueue_Clear(blumen_network_msg_queue* Queue)
+{
+ while (MessageQueue_CanRead(*Queue))
+ {
+ MessageQueue_Read(Queue);
+ }
+}
diff --git a/src/app/ss_blumen_lumen/message_queue.h b/src/app/ss_blumen_lumen/message_queue.h
new file mode 100644
index 0000000..a450b54
--- /dev/null
+++ b/src/app/ss_blumen_lumen/message_queue.h
@@ -0,0 +1,17 @@
+/* date = March 27th 2021 3:07 pm */
+
+#ifndef MESSAGE_QUEUE_H
+#define MESSAGE_QUEUE_H
+
+#define BLUMEN_MESSAGE_QUEUE_COUNT 32
+typedef struct blumen_network_msg_queue
+{
+ gs_data Buffers[BLUMEN_MESSAGE_QUEUE_COUNT];
+ u32 WriteHead;
+ u32 ReadHead;
+} blumen_network_msg_queue;
+
+// KB(1) is just bigger than any packet we send. Good for now
+#define DEFAULT_QUEUE_ENTRY_SIZE KB(1)
+
+#endif //MESSAGE_QUEUE_H
diff --git a/src/app/ss_blumen_lumen/phrase_hue_map.h b/src/app/ss_blumen_lumen/phrase_hue_map.h
new file mode 100644
index 0000000..3b2af84
--- /dev/null
+++ b/src/app/ss_blumen_lumen/phrase_hue_map.h
@@ -0,0 +1,283 @@
+/* date = March 27th 2021 1:55 pm */
+
+#ifndef PHRASE_HUE_MAP_H
+#define PHRASE_HUE_MAP_H
+
+enum p_hue_flag
+{
+ Hue_Value = 0,
+ Hue_White = 1,
+ Hue_Black = 2,
+};
+
+enum p_hue_pattern
+{
+ HuePattern_Patchy,
+ HuePattern_Wavy,
+
+ HuePattern_Count,
+};
+
+enum p_hue_add_in
+{
+ AddIn_None,
+ AddIn_Waves,
+ AddIn_Rotary,
+
+ AddIn_Count,
+};
+
+typedef struct p_hue
+{
+ v4 HSV;
+} p_hue;
+
+typedef struct phrase_hue_map
+{
+ u64 CountMax;
+ u64 Count;
+ gs_const_string* Phrases;
+ u64* PhraseHashes;
+ p_hue* Hue0;
+ p_hue* Hue1;
+ p_hue* Hue2;
+ u32* Gran; // granularity
+ r32* Speed;
+ u8* Pattern;
+ u8* AddIn;
+ bool* OverrideAll;
+} phrase_hue_map;
+
+typedef struct phrase_hue
+{
+ gs_const_string Phrase;
+ u64 PhraseHash;
+ p_hue Hue0;
+ p_hue Hue1;
+ p_hue Hue2;
+ u32 Granularity;
+ r32 Speed;
+ u8 Pattern;
+ u8 AddIn;
+ bool OverrideAll;
+} phrase_hue;
+
+internal p_hue
+LerpPHue(r32 T, p_hue A, p_hue B)
+{
+ p_hue Result = {};
+
+ if (Abs(A.HSV.x - B.HSV.x) < 180.0f)
+ {
+ Result.HSV.x = LerpR64(T, A.HSV.x, B.HSV.x);
+ }
+ else if (B.HSV.x > A.HSV.x)
+ {
+ Result.HSV.x = LerpR64(T, A.HSV.x, B.HSV.x - 360.0f);
+ }
+ else
+ {
+ Result.HSV.x = LerpR64(T, A.HSV.x - 360.0f, B.HSV.x);
+ }
+ if (Result.HSV.x < 360) Result.HSV.x += 360;
+ if (Result.HSV.x > 360) Result.HSV.x -= 360;
+ Result.HSV.x = Clamp(0, Result.HSV.x, 360);
+ Result.HSV.y = LerpR32(T, A.HSV.y, B.HSV.y);
+ Result.HSV.z = LerpR32(T, A.HSV.z, B.HSV.z);
+ Result.HSV.w = LerpR32(T, A.HSV.w, B.HSV.w);
+
+ return Result;
+}
+
+internal phrase_hue
+LerpPhraseHue(r32 T, phrase_hue A, phrase_hue B)
+{
+ phrase_hue Result = {};
+ Result.Hue0 = LerpPHue(T, A.Hue0, B.Hue0);
+ Result.Hue1 = LerpPHue(T, A.Hue1, B.Hue1);
+ Result.Hue2 = LerpPHue(T, A.Hue2, B.Hue2);
+ Result.Granularity = (u32)LerpR32(T, (r32)A.Granularity, (r32)B.Granularity);
+ Result.Speed = LerpR32(T, A.Speed, B.Speed);
+
+ if (T < .5f) {
+ Result.Phrase = A.Phrase;
+ Result.PhraseHash = A.PhraseHash;
+ Result.Pattern = A.Pattern;
+ Result.AddIn = A.AddIn;
+ }
+ else {
+ Result.Phrase = B.Phrase;
+ Result.PhraseHash = B.PhraseHash;
+ Result.Pattern = B.Pattern;
+ Result.AddIn = B.AddIn;
+ }
+
+ return Result;
+}
+
+internal p_hue
+CreateHueFromString(gs_const_string Str)
+{
+ p_hue Result = {};
+ if (Str.Str[0] == 'b') {
+ Result.HSV = v4{0, 0, 0, 1 };
+ } else if (Str.Str[0] == 'w') {
+ Result.HSV = v4{0, 0, 1, 1 };;
+ } else {
+ parse_float_result Parsed = ValidateAndParseFloat(Str);
+ if (!Parsed.Success)
+ {
+ Log_Error(GlobalLogBuffer, "Failed to Parse CSV Float\n");
+ Parsed.Value = 0.0;
+ }
+ Result.HSV = v4{ (r32)Parsed.Value, 1, 1, 1 };
+
+ }
+ return Result;
+}
+
+internal v4
+RGBFromPhraseHue (p_hue H)
+{
+ v4 Result = H.HSV;
+ Result = HSVToRGB(Result);
+ return Result;
+}
+
+internal phrase_hue_map
+PhraseHueMap_GenFromCSV(gscsv_sheet Sheet, gs_memory_arena* Arena)
+{
+ phrase_hue_map Result = {};
+ if (Sheet.RowCount == 0) return Result;
+
+ Result.CountMax = Sheet.RowCount - 1; // we don't include the header row
+ Result.Phrases = PushArray(Arena, gs_const_string, Result.CountMax);
+ Result.PhraseHashes = PushArray(Arena, u64, Result.CountMax);
+ Result.Hue0 = PushArray(Arena, p_hue, Result.CountMax);
+ Result.Hue1 = PushArray(Arena, p_hue, Result.CountMax);
+ Result.Hue2 = PushArray(Arena, p_hue, Result.CountMax);
+ Result.Gran = PushArray(Arena, u32, Result.CountMax);
+ Result.Pattern = PushArray(Arena, u8, Result.CountMax);
+ Result.Speed = PushArray(Arena, r32, Result.CountMax);
+ Result.AddIn = PushArray(Arena, u8, Result.CountMax);
+ Result.OverrideAll = PushArray(Arena, bool, Result.CountMax);
+
+ // this lets us tightly pack phrase_hues even if there is a
+ // row in the csv that is empty or invalid
+ s32 DestOffset = 0;
+ for (u32 Row = 1; Row < Sheet.RowCount; Row++)
+ {
+ s32 Index = (Row - 1) - DestOffset;
+ Assert(Index >= 0 && Index < Result.CountMax);
+
+ gs_const_string Phrase = CSVSheet_GetCell(Sheet,
+ 0, Row);
+ gs_const_string Hue0Str = CSVSheet_GetCell(Sheet, 1, Row);
+ gs_const_string Hue1Str = CSVSheet_GetCell(Sheet, 2, Row);
+ gs_const_string Hue2Str = CSVSheet_GetCell(Sheet, 3, Row);
+ gs_const_string Homonyms = CSVSheet_GetCell(Sheet, 4, Row);
+ gs_const_string Granularity = CSVSheet_GetCell(Sheet, 5, Row);
+ gs_const_string Speed = CSVSheet_GetCell(Sheet, 6, Row);
+ gs_const_string Pattern = CSVSheet_GetCell(Sheet, 7, Row);
+ gs_const_string AddIn = CSVSheet_GetCell(Sheet, 8, Row);
+ gs_const_string OverrideAll = CSVSheet_GetCell(Sheet, 9, Row);
+
+ // essential parameters
+ if (Phrase.Length == 0 ||
+ Hue0Str.Length == 0 ||
+ Hue1Str.Length == 0 ||
+ Hue2Str.Length == 0)
+ {
+ DestOffset++;
+ continue;
+ }
+
+ Result.Phrases[Index] = PushStringF(Arena, Phrase.Length, "%S", Phrase).ConstString;
+ for (u64 i = 0; i < Result.Phrases[Index].Length; i++)
+ {
+ char C = Result.Phrases[Index].Str[i];
+ if (IsAlpha(C))
+ {
+ Result.Phrases[Index].Str[i] = ToLower(C);
+ }
+ }
+
+ u64 PhraseHash = HashDJB2ToU32(StringExpand(Result.Phrases[Index]));
+
+ Result.PhraseHashes[Index] = PhraseHash;
+ Result.Hue0[Index] = CreateHueFromString(Hue0Str);
+ Result.Hue1[Index] = CreateHueFromString(Hue1Str);
+ Result.Hue2[Index] = CreateHueFromString(Hue2Str);
+
+ parse_float_result ParsedSpeed = ValidateAndParseFloat(Speed);
+ if (!ParsedSpeed.Success)
+ {
+ ParsedSpeed.Value = 1.0;
+ }
+ Result.Speed[Index] = ParsedSpeed.Value;
+
+ if (StringsEqual(Pattern, ConstString("wavy")))
+ {
+ Result.Pattern[Index] = HuePattern_Wavy;
+ } else {
+ Result.Pattern[Index] = HuePattern_Patchy;
+ }
+
+ if (StringsEqual(AddIn, ConstString("waves")))
+ {
+ Result.AddIn[Index] = AddIn_Waves;
+ } else if (StringsEqual(AddIn, ConstString("rotary"))) {
+ Result.AddIn[Index] = AddIn_Rotary;
+ } else {
+ Result.AddIn[Index] = AddIn_None;
+ }
+
+ parse_uint_result ParsedGranularity = ValidateAndParseUInt(Granularity);
+ if (!ParsedGranularity.Success)
+ {
+ ParsedGranularity.Value = 1;
+ }
+ Result.Gran[Index] = ParsedGranularity.Value;
+
+ Result.OverrideAll[Index] = StringsEqualUpToLength(OverrideAll, ConstString("yes"), 3);
+ }
+
+ Result.Count = Result.CountMax + DestOffset;
+
+ return Result;
+}
+
+internal phrase_hue
+PhraseHueMap_Get(phrase_hue_map Map, u32 Index)
+{
+ Assert(Index < Map.Count);
+ phrase_hue Result = {};
+ Result.Phrase = Map.Phrases[Index];
+ Result.PhraseHash = Map.PhraseHashes[Index];
+ Result.Hue0 = Map.Hue0[Index];
+ Result.Hue1 = Map.Hue1[Index];
+ Result.Hue2 = Map.Hue2[Index];
+ Result.Granularity = Map.Gran[Index];
+ Result.Speed = Map.Speed[Index];
+ Result.AddIn = Map.AddIn[Index];
+ Result.Pattern = Map.Pattern[Index];
+ Result.OverrideAll = Map.OverrideAll[Index];
+ return Result;
+}
+
+internal phrase_hue
+PhraseHueMap_Find(phrase_hue_map Map, u64 PhraseHash)
+{
+ phrase_hue Result = {};
+ for (u32 i = 0; i < Map.Count; i++)
+ {
+ if (Map.PhraseHashes[i] == PhraseHash)
+ {
+ Result = PhraseHueMap_Get(Map, i);
+ break;
+ }
+ }
+ return Result;
+}
+
+#endif //PHRASE_HUE_MAP_H
diff --git a/src/app/ss_blumen_lumen/sdf.h b/src/app/ss_blumen_lumen/sdf.h
new file mode 100644
index 0000000..58e40e9
--- /dev/null
+++ b/src/app/ss_blumen_lumen/sdf.h
@@ -0,0 +1,20 @@
+/* date = March 29th 2021 10:41 pm */
+
+#ifndef SDF_H
+#define SDF_H
+
+internal r32
+SDF_Sphere(v3 Point, v3 Center, r32 Radius)
+{
+ r32 Result = V3Mag(Point - Center) - Radius;
+ return Result;
+}
+
+internal r32
+SDF_SphereNormalized(v3 Point, v3 Center, r32 Radius)
+{
+ r32 Result = SDF_Sphere(Point, Center, Radius) / Radius;
+ return Result;
+}
+
+#endif //SDF_H
diff --git a/src/app/sse_mathfun.h b/src/app/sse_mathfun.h
deleted file mode 100644
index 16b22e9..0000000
--- a/src/app/sse_mathfun.h
+++ /dev/null
@@ -1,711 +0,0 @@
-/* SIMD (SSE1+MMX or SSE2) implementation of sin, cos, exp and log
-
- Inspired by Intel Approximate Math library, and based on the
- corresponding algorithms of the cephes math library
-
- The default is to use the SSE1 version. If you define USE_SSE2 the
- the SSE2 intrinsics will be used in place of the MMX intrinsics. Do
- not expect any significant performance improvement with SSE2.
-*/
-
-/* Copyright (C) 2007 Julien Pommier
-
- 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.
-
- (this is the zlib license)
-*/
-
-#include
-
-/* yes I know, the top of this file is quite ugly */
-
-#ifdef _MSC_VER /* visual c++ */
-# define ALIGN16_BEG __declspec(align(16))
-# define ALIGN16_END
-#else /* gcc or icc */
-# define ALIGN16_BEG
-# define ALIGN16_END __attribute__((aligned(16)))
-#endif
-
-/* __m128 is ugly to write */
-typedef __m128 v4sf; // vector of 4 float (sse1)
-
-#ifdef USE_SSE2
-# include
-typedef __m128i v4si; // vector of 4 int (sse2)
-#else
-typedef __m64 v2si; // vector of 2 int (mmx)
-#endif
-
-/* declare some SSE constants -- why can't I figure a better way to do that? */
-#define _PS_CONST(Name, Val) \
-static const ALIGN16_BEG float _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
-#define _PI32_CONST(Name, Val) \
-static const ALIGN16_BEG int _pi32_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
-#define _PS_CONST_TYPE(Name, Type, Val) \
-static const ALIGN16_BEG Type _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
-
-_PS_CONST(1 , 1.0f);
-_PS_CONST(0p5, 0.5f);
-/* the smallest non denormalized float number */
-_PS_CONST_TYPE(min_norm_pos, int, 0x00800000);
-_PS_CONST_TYPE(mant_mask, int, 0x7f800000);
-_PS_CONST_TYPE(inv_mant_mask, int, ~0x7f800000);
-
-_PS_CONST_TYPE(sign_mask, int, (int)0x80000000);
-_PS_CONST_TYPE(inv_sign_mask, int, ~0x80000000);
-
-_PI32_CONST(1, 1);
-_PI32_CONST(inv1, ~1);
-_PI32_CONST(2, 2);
-_PI32_CONST(4, 4);
-_PI32_CONST(0x7f, 0x7f);
-
-_PS_CONST(cephes_SQRTHF, 0.707106781186547524);
-_PS_CONST(cephes_log_p0, 7.0376836292E-2);
-_PS_CONST(cephes_log_p1, - 1.1514610310E-1);
-_PS_CONST(cephes_log_p2, 1.1676998740E-1);
-_PS_CONST(cephes_log_p3, - 1.2420140846E-1);
-_PS_CONST(cephes_log_p4, + 1.4249322787E-1);
-_PS_CONST(cephes_log_p5, - 1.6668057665E-1);
-_PS_CONST(cephes_log_p6, + 2.0000714765E-1);
-_PS_CONST(cephes_log_p7, - 2.4999993993E-1);
-_PS_CONST(cephes_log_p8, + 3.3333331174E-1);
-_PS_CONST(cephes_log_q1, -2.12194440e-4);
-_PS_CONST(cephes_log_q2, 0.693359375);
-
-#ifndef USE_SSE2
-typedef union xmm_mm_union {
- __m128 xmm;
- __m64 mm[2];
-} xmm_mm_union;
-
-#define COPY_XMM_TO_MM(xmm_, mm0_, mm1_) { \
- xmm_mm_union u; u.xmm = xmm_; \
- mm0_ = u.mm[0]; \
- mm1_ = u.mm[1]; \
-}
-
-#define COPY_MM_TO_XMM(mm0_, mm1_, xmm_) { \
- xmm_mm_union u; u.mm[0]=mm0_; u.mm[1]=mm1_; xmm_ = u.xmm; \
-}
-
-#endif // USE_SSE2
-
-/* natural logarithm computed for 4 simultaneous float
- return NaN for x <= 0
-*/
-v4sf log_ps(v4sf x) {
-#ifdef USE_SSE2
- v4si emm0;
-#else
- v2si mm0, mm1;
-#endif
- v4sf one = *(v4sf*)_ps_1;
-
- v4sf invalid_mask = _mm_cmple_ps(x, _mm_setzero_ps());
-
- x = _mm_max_ps(x, *(v4sf*)_ps_min_norm_pos); /* cut off denormalized stuff */
-
-#ifndef USE_SSE2
- /* part 1: x = frexpf(x, &e); */
- COPY_XMM_TO_MM(x, mm0, mm1);
- mm0 = _mm_srli_pi32(mm0, 23);
- mm1 = _mm_srli_pi32(mm1, 23);
-#else
- emm0 = _mm_srli_epi32(_mm_castps_si128(x), 23);
-#endif
- /* keep only the fractional part */
- x = _mm_and_ps(x, *(v4sf*)_ps_inv_mant_mask);
- x = _mm_or_ps(x, *(v4sf*)_ps_0p5);
-
-#ifndef USE_SSE2
- /* now e=mm0:mm1 contain the really base-2 exponent */
- mm0 = _mm_sub_pi32(mm0, *(v2si*)_pi32_0x7f);
- mm1 = _mm_sub_pi32(mm1, *(v2si*)_pi32_0x7f);
- v4sf e = _mm_cvtpi32x2_ps(mm0, mm1);
- _mm_empty(); /* bye bye mmx */
-#else
- emm0 = _mm_sub_epi32(emm0, *(v4si*)_pi32_0x7f);
- v4sf e = _mm_cvtepi32_ps(emm0);
-#endif
-
- e = _mm_add_ps(e, one);
-
- /* part2:
- if( x < SQRTHF ) {
- e -= 1;
- x = x + x - 1.0;
- } else { x = x - 1.0; }
- */
- v4sf mask = _mm_cmplt_ps(x, *(v4sf*)_ps_cephes_SQRTHF);
- v4sf tmp = _mm_and_ps(x, mask);
- x = _mm_sub_ps(x, one);
- e = _mm_sub_ps(e, _mm_and_ps(one, mask));
- x = _mm_add_ps(x, tmp);
-
-
- v4sf z = _mm_mul_ps(x,x);
-
- v4sf y = *(v4sf*)_ps_cephes_log_p0;
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p1);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p2);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p3);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p4);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p5);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p6);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p7);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p8);
- y = _mm_mul_ps(y, x);
-
- y = _mm_mul_ps(y, z);
-
-
- tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q1);
- y = _mm_add_ps(y, tmp);
-
-
- tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
- y = _mm_sub_ps(y, tmp);
-
- tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q2);
- x = _mm_add_ps(x, y);
- x = _mm_add_ps(x, tmp);
- x = _mm_or_ps(x, invalid_mask); // negative arg will be NAN
- return x;
-}
-
-_PS_CONST(exp_hi, 88.3762626647949f);
-_PS_CONST(exp_lo, -88.3762626647949f);
-
-_PS_CONST(cephes_LOG2EF, 1.44269504088896341);
-_PS_CONST(cephes_exp_C1, 0.693359375);
-_PS_CONST(cephes_exp_C2, -2.12194440e-4);
-
-_PS_CONST(cephes_exp_p0, 1.9875691500E-4);
-_PS_CONST(cephes_exp_p1, 1.3981999507E-3);
-_PS_CONST(cephes_exp_p2, 8.3334519073E-3);
-_PS_CONST(cephes_exp_p3, 4.1665795894E-2);
-_PS_CONST(cephes_exp_p4, 1.6666665459E-1);
-_PS_CONST(cephes_exp_p5, 5.0000001201E-1);
-
-v4sf exp_ps(v4sf x) {
- v4sf tmp = _mm_setzero_ps(), fx;
-#ifdef USE_SSE2
- v4si emm0;
-#else
- v2si mm0, mm1;
-#endif
- v4sf one = *(v4sf*)_ps_1;
-
- x = _mm_min_ps(x, *(v4sf*)_ps_exp_hi);
- x = _mm_max_ps(x, *(v4sf*)_ps_exp_lo);
-
- /* express exp(x) as exp(g + n*log(2)) */
- fx = _mm_mul_ps(x, *(v4sf*)_ps_cephes_LOG2EF);
- fx = _mm_add_ps(fx, *(v4sf*)_ps_0p5);
-
- /* how to perform a floorf with SSE: just below */
-#ifndef USE_SSE2
- /* step 1 : cast to int */
- tmp = _mm_movehl_ps(tmp, fx);
- mm0 = _mm_cvttps_pi32(fx);
- mm1 = _mm_cvttps_pi32(tmp);
- /* step 2 : cast back to float */
- tmp = _mm_cvtpi32x2_ps(mm0, mm1);
-#else
- emm0 = _mm_cvttps_epi32(fx);
- tmp = _mm_cvtepi32_ps(emm0);
-#endif
- /* if greater, substract 1 */
- v4sf mask = _mm_cmpgt_ps(tmp, fx);
- mask = _mm_and_ps(mask, one);
- fx = _mm_sub_ps(tmp, mask);
-
- tmp = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C1);
- v4sf z = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C2);
- x = _mm_sub_ps(x, tmp);
- x = _mm_sub_ps(x, z);
-
- z = _mm_mul_ps(x,x);
-
- v4sf y = *(v4sf*)_ps_cephes_exp_p0;
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p1);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p2);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p3);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p4);
- y = _mm_mul_ps(y, x);
- y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p5);
- y = _mm_mul_ps(y, z);
- y = _mm_add_ps(y, x);
- y = _mm_add_ps(y, one);
-
- /* build 2^n */
-#ifndef USE_SSE2
- z = _mm_movehl_ps(z, fx);
- mm0 = _mm_cvttps_pi32(fx);
- mm1 = _mm_cvttps_pi32(z);
- mm0 = _mm_add_pi32(mm0, *(v2si*)_pi32_0x7f);
- mm1 = _mm_add_pi32(mm1, *(v2si*)_pi32_0x7f);
- mm0 = _mm_slli_pi32(mm0, 23);
- mm1 = _mm_slli_pi32(mm1, 23);
-
- v4sf pow2n;
- COPY_MM_TO_XMM(mm0, mm1, pow2n);
- _mm_empty();
-#else
- emm0 = _mm_cvttps_epi32(fx);
- emm0 = _mm_add_epi32(emm0, *(v4si*)_pi32_0x7f);
- emm0 = _mm_slli_epi32(emm0, 23);
- v4sf pow2n = _mm_castsi128_ps(emm0);
-#endif
- y = _mm_mul_ps(y, pow2n);
- return y;
-}
-
-_PS_CONST(minus_cephes_DP1, -0.78515625);
-_PS_CONST(minus_cephes_DP2, -2.4187564849853515625e-4);
-_PS_CONST(minus_cephes_DP3, -3.77489497744594108e-8);
-_PS_CONST(sincof_p0, -1.9515295891E-4);
-_PS_CONST(sincof_p1, 8.3321608736E-3);
-_PS_CONST(sincof_p2, -1.6666654611E-1);
-_PS_CONST(coscof_p0, 2.443315711809948E-005);
-_PS_CONST(coscof_p1, -1.388731625493765E-003);
-_PS_CONST(coscof_p2, 4.166664568298827E-002);
-_PS_CONST(cephes_FOPI, 1.27323954473516); // 4 / M_PI
-
-
-/* evaluation of 4 sines at onces, using only SSE1+MMX intrinsics so
- it runs also on old athlons XPs and the pentium III of your grand
- mother.
-
- The code is the exact rewriting of the cephes sinf function.
- Precision is excellent as long as x < 8192 (I did not bother to
- take into account the special handling they have for greater values
- -- it does not return garbage for arguments over 8192, though, but
- the extra precision is missing).
-
- Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the
- surprising but correct result.
-
- Performance is also surprisingly good, 1.33 times faster than the
- macos vsinf SSE2 function, and 1.5 times faster than the
- __vrs4_sinf of amd's ACML (which is only available in 64 bits). Not
- too bad for an SSE1 function (with no special tuning) !
- However the latter libraries probably have a much better handling of NaN,
- Inf, denormalized and other special arguments..
-
- On my core 1 duo, the execution of this function takes approximately 95 cycles.
-
- From what I have observed on the experiments with Intel AMath lib, switching to an
- SSE2 version would improve the perf by only 10%.
-
- Since it is based on SSE intrinsics, it has to be compiled at -O2 to
- deliver full speed.
-*/
-v4sf sin_ps(v4sf x) { // any x
- v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y;
-
-#ifdef USE_SSE2
- v4si emm0, emm2;
-#else
- v2si mm0, mm1, mm2, mm3;
-#endif
- sign_bit = x;
- /* take the absolute value */
- x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
- /* extract the sign bit (upper one) */
- sign_bit = _mm_and_ps(sign_bit, *(v4sf*)_ps_sign_mask);
-
- /* scale by 4/Pi */
- y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
-
-#ifdef USE_SSE2
- /* store the integer part of y in mm0 */
- emm2 = _mm_cvttps_epi32(y);
- /* j=(j+1) & (~1) (see the cephes sources) */
- emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
- emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
- y = _mm_cvtepi32_ps(emm2);
-
- /* get the swap sign flag */
- emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
- emm0 = _mm_slli_epi32(emm0, 29);
- /* get the polynom selection mask
- there is one polynom for 0 <= x <= Pi/4
- and another one for Pi/4> precision OK for the tan_ps <<-
-
-checking tan on [-0.49*Pi, 0.49*Pi]
-max deviation from tanf(x): 3.8147e-06 at -0.490000009841*Pi, max deviation from cephes_tan(x):
-9.53674e-07
- ->> precision OK for the tan_ps <<-
-
-checking cot on [0.2*Pi, 0.7*Pi]
-max deviation from cotf(x): 1.19209e-07 at 0.204303119606*Pi, max deviation from cephes_cot(x):
-1.19209e-07
- ->> precision OK for the cot_ps <<-
-
-checking cot on [0.01*Pi, 0.99*Pi]
-max deviation from cotf(x): 3.8147e-06 at 0.987876517942*Pi, max deviation from cephes_cot(x):
-9.53674e-07
- ->> precision OK for the cot_ps <<-
-
-With atan_ps and atan2_ps you get pretty good precision, atan_ps max deviation is < 2e-7 and
-atan2_ps max deviation is < 2.5e-7
-*/
-
-/* Copyright (C) 2016 Tolga Mizrak
-
- 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.
-
- (this is the zlib license)
-*/
-
-#pragma once
-
-#ifndef _SSE_MATHFUN_EXTENSION_H_INCLUDED_
-#define _SSE_MATHFUN_EXTENSION_H_INCLUDED_
-
-#ifndef USE_SSE2
-#error sse1 & mmx version not implemented
-#endif
-
-#ifdef _MSC_VER
-#pragma warning( push )
-/* warning C4838: conversion from 'double' to 'const float' requires a narrowing conversion */
-#pragma warning( disable : 4838 )
-/* warning C4305: 'initializing': truncation from 'double' to 'const float' */
-#pragma warning( disable : 4305 )
-#endif
-
-#include "sse_mathfun.h"
-
-_PS_CONST( 0, 0 );
-_PS_CONST( 2, 2 );
-_PI32_CONST( neg1, 1 );
-
-_PS_CONST( tancof_p0, 9.38540185543E-3 );
-_PS_CONST( tancof_p1, 3.11992232697E-3 );
-_PS_CONST( tancof_p2, 2.44301354525E-2 );
-_PS_CONST( tancof_p3, 5.34112807005E-2 );
-_PS_CONST( tancof_p4, 1.33387994085E-1 );
-_PS_CONST( tancof_p5, 3.33331568548E-1 );
-
-_PS_CONST( tancot_eps, 1.0e-4 );
-
-v4sf tancot_ps( v4sf x, int cotFlag )
-{
- v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y;
-
-#ifdef USE_SSE2
- v4si emm2;
-#else
-#endif
- sign_bit = x;
- /* take the absolute value */
- x = _mm_and_ps( x, *(v4sf*)_ps_inv_sign_mask );
- /* extract the sign bit (upper one) */
- sign_bit = _mm_and_ps( sign_bit, *(v4sf*)_ps_sign_mask );
-
- /* scale by 4/Pi */
- y = _mm_mul_ps( x, *(v4sf*)_ps_cephes_FOPI );
-
-#ifdef USE_SSE2
- /* store the integer part of y in mm0 */
- emm2 = _mm_cvttps_epi32( y );
- /* j=(j+1) & (~1) (see the cephes sources) */
- emm2 = _mm_add_epi32( emm2, *(v4si*)_pi32_1 );
- emm2 = _mm_and_si128( emm2, *(v4si*)_pi32_inv1 );
- y = _mm_cvtepi32_ps( emm2 );
-
- emm2 = _mm_and_si128( emm2, *(v4si*)_pi32_2 );
- emm2 = _mm_cmpeq_epi32( emm2, _mm_setzero_si128() );
-
- v4sf poly_mask = _mm_castsi128_ps( emm2 );
-#else
-#endif
- /* The magic pass: "Extended precision modular arithmetic"
- x = ((x - y * DP1) - y * DP2) - y * DP3; */
- xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
- xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
- xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
- xmm1 = _mm_mul_ps( y, xmm1 );
- xmm2 = _mm_mul_ps( y, xmm2 );
- xmm3 = _mm_mul_ps( y, xmm3 );
- v4sf z = _mm_add_ps( x, xmm1 );
- z = _mm_add_ps( z, xmm2 );
- z = _mm_add_ps( z, xmm3 );
-
- v4sf zz = _mm_mul_ps( z, z );
-
- y = *(v4sf*)_ps_tancof_p0;
- y = _mm_mul_ps( y, zz );
- y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p1 );
- y = _mm_mul_ps( y, zz );
- y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p2 );
- y = _mm_mul_ps( y, zz );
- y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p3 );
- y = _mm_mul_ps( y, zz );
- y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p4 );
- y = _mm_mul_ps( y, zz );
- y = _mm_add_ps( y, *(v4sf*)_ps_tancof_p5 );
- y = _mm_mul_ps( y, zz );
- y = _mm_mul_ps( y, z );
- y = _mm_add_ps( y, z );
-
- v4sf y2;
- if( cotFlag ) {
- y2 = _mm_xor_ps( y, *(v4sf*)_ps_sign_mask );
- /* y = _mm_rcp_ps( y ); */
- /* using _mm_rcp_ps here loses on way too much precision, better to do a div */
- y = _mm_div_ps( *(v4sf*)_ps_1, y );
- } else {
- /* y2 = _mm_rcp_ps( y ); */
- /* using _mm_rcp_ps here loses on way too much precision, better to do a div */
- y2 = _mm_div_ps( *(v4sf*)_ps_1, y );
- y2 = _mm_xor_ps( y2, *(v4sf*)_ps_sign_mask );
- }
-
- /* select the correct result from the two polynoms */
- xmm3 = poly_mask;
- y = _mm_and_ps( xmm3, y );
- y2 = _mm_andnot_ps( xmm3, y2 );
- y = _mm_or_ps( y, y2 );
-
- /* update the sign */
- y = _mm_xor_ps( y, sign_bit );
-
- return y;
-}
-
-v4sf tan_ps( v4sf x ) { return tancot_ps( x, 0 ); }
-
-v4sf cot_ps( v4sf x ) { return tancot_ps( x, 1 ); }
-
-_PS_CONST( atanrange_hi, 2.414213562373095 );
-_PS_CONST( atanrange_lo, 0.4142135623730950 );
-const float PIF = 3.141592653589793238;
-const float PIO2F = 1.5707963267948966192;
-_PS_CONST( cephes_PIF, 3.141592653589793238 );
-_PS_CONST( cephes_PIO2F, 1.5707963267948966192 );
-_PS_CONST( cephes_PIO4F, 0.7853981633974483096 );
-
-_PS_CONST( atancof_p0, 8.05374449538e-2 );
-_PS_CONST( atancof_p1, 1.38776856032E-1 );
-_PS_CONST( atancof_p2, 1.99777106478E-1 );
-_PS_CONST( atancof_p3, 3.33329491539E-1 );
-
-v4sf atan_ps( v4sf x )
-{
- v4sf sign_bit, y;
-
- sign_bit = x;
- /* take the absolute value */
- x = _mm_and_ps( x, *(v4sf*)_ps_inv_sign_mask );
- /* extract the sign bit (upper one) */
- sign_bit = _mm_and_ps( sign_bit, *(v4sf*)_ps_sign_mask );
-
- /* range reduction, init x and y depending on range */
-#ifdef USE_SSE2
- /* x > 2.414213562373095 */
- v4sf cmp0 = _mm_cmpgt_ps( x, *(v4sf*)_ps_atanrange_hi );
- /* x > 0.4142135623730950 */
- v4sf cmp1 = _mm_cmpgt_ps( x, *(v4sf*)_ps_atanrange_lo );
-
- /* x > 0.4142135623730950 && !( x > 2.414213562373095 ) */
- v4sf cmp2 = _mm_andnot_ps( cmp0, cmp1 );
-
- /* -( 1.0/x ) */
- v4sf y0 = _mm_and_ps( cmp0, *(v4sf*)_ps_cephes_PIO2F );
- v4sf x0 = _mm_div_ps( *(v4sf*)_ps_1, x );
- x0 = _mm_xor_ps( x0, *(v4sf*)_ps_sign_mask );
-
- v4sf y1 = _mm_and_ps( cmp2, *(v4sf*)_ps_cephes_PIO4F );
- /* (x-1.0)/(x+1.0) */
- v4sf x1_o = _mm_sub_ps( x, *(v4sf*)_ps_1 );
- v4sf x1_u = _mm_add_ps( x, *(v4sf*)_ps_1 );
- v4sf x1 = _mm_div_ps( x1_o, x1_u );
-
- v4sf x2 = _mm_and_ps( cmp2, x1 );
- x0 = _mm_and_ps( cmp0, x0 );
- x2 = _mm_or_ps( x2, x0 );
- cmp1 = _mm_or_ps( cmp0, cmp2 );
- x2 = _mm_and_ps( cmp1, x2 );
- x = _mm_andnot_ps( cmp1, x );
- x = _mm_or_ps( x2, x );
-
- y = _mm_or_ps( y0, y1 );
-#else
-#error sse1 & mmx version not implemented
-#endif
-
- v4sf zz = _mm_mul_ps( x, x );
- v4sf acc = *(v4sf*)_ps_atancof_p0;
- acc = _mm_mul_ps( acc, zz );
- acc = _mm_sub_ps( acc, *(v4sf*)_ps_atancof_p1 );
- acc = _mm_mul_ps( acc, zz );
- acc = _mm_add_ps( acc, *(v4sf*)_ps_atancof_p2 );
- acc = _mm_mul_ps( acc, zz );
- acc = _mm_sub_ps( acc, *(v4sf*)_ps_atancof_p3 );
- acc = _mm_mul_ps( acc, zz );
- acc = _mm_mul_ps( acc, x );
- acc = _mm_add_ps( acc, x );
- y = _mm_add_ps( y, acc );
-
- /* update the sign */
- y = _mm_xor_ps( y, sign_bit );
-
- return y;
-}
-
-v4sf atan2_ps( v4sf y, v4sf x )
-{
- v4sf x_eq_0 = _mm_cmpeq_ps( x, *(v4sf*)_ps_0 );
- v4sf x_gt_0 = _mm_cmpgt_ps( x, *(v4sf*)_ps_0 );
- v4sf x_le_0 = _mm_cmple_ps( x, *(v4sf*)_ps_0 );
- v4sf y_eq_0 = _mm_cmpeq_ps( y, *(v4sf*)_ps_0 );
- v4sf x_lt_0 = _mm_cmplt_ps( x, *(v4sf*)_ps_0 );
- v4sf y_lt_0 = _mm_cmplt_ps( y, *(v4sf*)_ps_0 );
-
- v4sf zero_mask = _mm_and_ps( x_eq_0, y_eq_0 );
- v4sf zero_mask_other_case = _mm_and_ps( y_eq_0, x_gt_0 );
- zero_mask = _mm_or_ps( zero_mask, zero_mask_other_case );
-
- v4sf pio2_mask = _mm_andnot_ps( y_eq_0, x_eq_0 );
- v4sf pio2_mask_sign = _mm_and_ps( y_lt_0, *(v4sf*)_ps_sign_mask );
- v4sf pio2_result = *(v4sf*)_ps_cephes_PIO2F;
- pio2_result = _mm_xor_ps( pio2_result, pio2_mask_sign );
- pio2_result = _mm_and_ps( pio2_mask, pio2_result );
-
- v4sf pi_mask = _mm_and_ps( y_eq_0, x_le_0 );
- v4sf pi = *(v4sf*)_ps_cephes_PIF;
- v4sf pi_result = _mm_and_ps( pi_mask, pi );
-
- v4sf swap_sign_mask_offset = _mm_and_ps( x_lt_0, y_lt_0 );
- swap_sign_mask_offset = _mm_and_ps( swap_sign_mask_offset, *(v4sf*)_ps_sign_mask );
-
- v4sf offset0 = _mm_setzero_ps();
- v4sf offset1 = *(v4sf*)_ps_cephes_PIF;
- offset1 = _mm_xor_ps( offset1, swap_sign_mask_offset );
-
- v4sf offset = _mm_andnot_ps( x_lt_0, offset0 );
- offset = _mm_and_ps( x_lt_0, offset1 );
-
- v4sf arg = _mm_div_ps( y, x );
- v4sf atan_result = atan_ps( arg );
- atan_result = _mm_add_ps( atan_result, offset );
-
- /* select between zero_result, pio2_result and atan_result */
-
- v4sf result = _mm_andnot_ps( zero_mask, pio2_result );
- atan_result = _mm_andnot_ps( pio2_mask, atan_result );
- atan_result = _mm_andnot_ps( pio2_mask, atan_result);
- result = _mm_or_ps( result, atan_result );
- result = _mm_or_ps( result, pi_result );
-
- return result;
-}
-
-/* for convenience of calling simd sqrt */
-float sqrt_ps( float x )
-{
- v4sf sse_value = _mm_set_ps1( x );
- sse_value = _mm_sqrt_ps( sse_value );
- return _mm_cvtss_f32( sse_value );
-}
-float rsqrt_ps( float x )
-{
- v4sf sse_value = _mm_set_ps1( x );
- sse_value = _mm_rsqrt_ps( sse_value );
- return _mm_cvtss_f32( sse_value );
-}
-
-/* atan2 implementation using atan, used as a reference to implement atan2_ps */
-float atan2_ref( float y, float x )
-{
- if( x == 0.0f ) {
- if( y == 0.0f ) {
- return 0.0f;
- }
- float result = _ps_cephes_PIO2F[0];
- if( y < 0.0f ) {
- result = -result;
- }
- return result;
- }
-
- if( y == 0.0f ) {
- if( x > 0.0f ) {
- return 0.0f;
- }
- return PIF;
- }
-
- float offset = 0;
- if( x < 0.0f ) {
- offset = PIF;
- if( y < 0.0f ) {
- offset = -offset;
- }
- }
-
- v4sf val = _mm_set_ps1( y / x );
- val = atan_ps( val );
- return offset + _mm_cvtss_f32( val );
-}
-
-#ifdef _MSC_VER
-#pragma warning( pop )
-#endif
-
-#endif
diff --git a/src/gs_libs/gs_csv.h b/src/gs_libs/gs_csv.h
new file mode 100644
index 0000000..6969d4c
--- /dev/null
+++ b/src/gs_libs/gs_csv.h
@@ -0,0 +1,214 @@
+/* date = March 24th 2021 5:53 pm */
+
+#ifndef GS_CSV_H
+#define GS_CSV_H
+
+struct gscsv_cell
+{
+ gs_const_string Value;
+};
+
+struct gscsv_row
+{
+ u64* CellIndices;
+};
+
+struct gscsv_sheet
+{
+ char SeparatorChar;
+ gscsv_cell* Cells;
+ gscsv_row* Rows;
+ u64 RowCount;
+ u64 ColumnCount;
+};
+
+struct gscsv_sheet_desc
+{
+ char SeparatorChar;
+};
+
+struct gscsv_parser
+{
+ gs_const_string Str;
+ u64 At;
+ u64 Line;
+};
+
+internal void
+CSVParser_Reset(gscsv_parser* Parser)
+{
+ Parser->At = 0;
+}
+
+internal char
+CSVParser_CharAt(gscsv_parser Parser)
+{
+ char Result = Parser.Str.Str[Parser.At];
+ return Result;
+}
+
+internal bool
+CSVParser_CanAdvance(gscsv_parser Parser) {
+ bool Result = Parser.At < Parser.Str.Length;
+ return Result;
+}
+
+internal void
+CSVParser_Advance(gscsv_parser* Parser)
+{
+ if (CSVParser_CanAdvance(*Parser)) {
+ if (IsNewline(CSVParser_CharAt(*Parser)))
+ {
+ Parser->Line += 1;
+ }
+ Parser->At += 1;
+ }
+}
+
+internal void
+CSVParser_AdvancePastChar(gscsv_parser* Parser, char Char)
+{
+ while (CSVParser_CanAdvance(*Parser) && CSVParser_CharAt(*Parser) != Char)
+ {
+ CSVParser_Advance(Parser);
+ }
+
+ if (CSVParser_CanAdvance(*Parser))
+ {
+ Assert(CSVParser_CharAt(*Parser) == Char);
+ CSVParser_Advance(Parser);
+ }
+}
+
+internal u64
+CSVParser_AdvancePastSeparatorOrNewline(gscsv_parser* Parser, char SeparatorChar)
+{
+ u64 PointBeforeSeparator = 0;
+
+ while (CSVParser_CanAdvance(*Parser) &&
+ !(CSVParser_CharAt(*Parser) == SeparatorChar ||
+ IsNewline(CSVParser_CharAt(*Parser))))
+ {
+ CSVParser_Advance(Parser);
+ }
+
+ PointBeforeSeparator = Parser->At;
+
+ if (CSVParser_CanAdvance(*Parser))
+ {
+ while(IsNewline(CSVParser_CharAt(*Parser))) {
+ CSVParser_Advance(Parser);
+ }
+ if (CSVParser_CharAt(*Parser) == SeparatorChar)
+ {
+ CSVParser_Advance(Parser);
+ }
+ }
+
+ return PointBeforeSeparator;
+}
+
+internal gscsv_cell
+CSVCell_Init(gs_const_string File, u64 CellStart, u64 CellEnd)
+{
+ gscsv_cell Result = {};
+ Result.Value = Substring(File, CellStart, CellEnd);
+ return Result;
+}
+
+internal void
+CSVRow_PushCell(gscsv_row* Row, u64 CellIndex, u64 ColumnIndex)
+{
+ Row->CellIndices[ColumnIndex] = CellIndex;
+}
+
+internal gscsv_sheet
+CSV_Parse(gs_const_string File, gscsv_sheet_desc Desc, gs_memory_arena* Arena)
+{
+ gscsv_sheet Result = {};
+
+ gscsv_parser Parser = {};
+ Parser.Str = File;
+ Parser.At = 0;
+ Parser.Line = 0;
+
+ // Count Tabs in first line
+ u64 Columns = 0;
+ while (CSVParser_CanAdvance(Parser) && !IsNewline(CSVParser_CharAt(Parser)))
+ {
+ char At = CSVParser_CharAt(Parser);
+ if (At == Desc.SeparatorChar) {
+ Columns += 1;
+ }
+ CSVParser_Advance(&Parser);
+ }
+ // NOTE(PS): Add one on the end because the last column won't end in a SeparatorChar,
+ // it ends in a newline
+ Columns += 1;
+ CSVParser_Reset(&Parser);
+
+ // Count New Lines
+ u64 Rows = 0;
+ while (CSVParser_CanAdvance(Parser))
+ {
+ char At = CSVParser_CharAt(Parser);
+ if (IsNewline(At))
+ {
+ Rows++;
+ while (CSVParser_CanAdvance(Parser) && IsNewline(CSVParser_CharAt(Parser)))
+ {
+ CSVParser_Advance(&Parser);
+ }
+ } else {
+ CSVParser_Advance(&Parser);
+ }
+ }
+ // NOTE(PS): Adding a row becuase the last row will just end in the EOF
+ Rows += 1;
+ CSVParser_Reset(&Parser);
+
+ // Allocate Result
+ Result.SeparatorChar = Desc.SeparatorChar;
+ Result.RowCount = Rows;
+ Result.ColumnCount = Columns;
+ Result.Cells = PushArray(Arena, gscsv_cell, Result.RowCount * Result.ColumnCount);
+ Result.Rows = PushArray(Arena, gscsv_row, Result.RowCount);
+
+ // for rows, parse row
+ // for cells parse cells
+ for (u64 r = 0; r < Result.RowCount; r++)
+ {
+ u64 RowIndex = r;
+ gscsv_row* Row = Result.Rows + RowIndex;
+ Row->CellIndices = PushArray(Arena, u64, Result.ColumnCount);
+
+ for (u64 c = 0; c < Result.ColumnCount; c++)
+ {
+ u64 CellIndex = (r * Result.ColumnCount) + c;
+ u64 CellStart = Parser.At;
+ u64 CellEnd = CSVParser_AdvancePastSeparatorOrNewline(&Parser, Desc.SeparatorChar);
+
+ Result.Cells[CellIndex] = CSVCell_Init(Parser.Str, CellStart, CellEnd);
+ CSVRow_PushCell(Row, CellIndex, c);
+ }
+ }
+
+ return Result;
+}
+
+internal gs_const_string
+CSVSheet_GetCell(gscsv_sheet Sheet, u64 Column, u64 Row)
+{
+ gs_const_string Result = {};
+
+ if (Row < Sheet.RowCount && Column < Sheet.ColumnCount)
+ {
+ u64 CellIndex = (Row * Sheet.ColumnCount) + Column;
+ Result = Sheet.Cells[CellIndex].Value;
+ }
+
+ return Result;
+}
+
+
+#endif //GS_CSV_H
diff --git a/src/gs_libs/gs_input.h b/src/gs_libs/gs_input.h
index 4a696b3..f0c1c79 100644
--- a/src/gs_libs/gs_input.h
+++ b/src/gs_libs/gs_input.h
@@ -6,86 +6,86 @@
#ifndef GS_INPUT_H
enum key_code
{
- KeyCode_Invalid,
-
- KeyCode_Esc,
-
- KeyCode_Space,
- KeyCode_Tab,
- KeyCode_CapsLock,
- KeyCode_LeftShift, KeyCode_RightShift,
- KeyCode_LeftCtrl, KeyCode_RightCtrl,
- KeyCode_Fn,
- KeyCode_Alt,
- KeyCode_PageUp, KeyCode_PageDown,
- KeyCode_Backspace, KeyCode_Delete,
- KeyCode_Enter,
-
- // Function Keys
- KeyCode_F0, KeyCode_F1, KeyCode_F2, KeyCode_F3, KeyCode_F4, KeyCode_F5, KeyCode_F6, KeyCode_F7,
- KeyCode_F8, KeyCode_F9, KeyCode_F10, KeyCode_F11, KeyCode_F12,
-
- // Letters
- KeyCode_a, KeyCode_b, KeyCode_c, KeyCode_d, KeyCode_e, KeyCode_f, KeyCode_g, KeyCode_h,
- KeyCode_i, KeyCode_j, KeyCode_k, KeyCode_l, KeyCode_m, KeyCode_n, KeyCode_o, KeyCode_p,
- KeyCode_q, KeyCode_r, KeyCode_s, KeyCode_t, KeyCode_u, KeyCode_v, KeyCode_w, KeyCode_x,
- KeyCode_y, KeyCode_z,
-
- KeyCode_A, KeyCode_B, KeyCode_C, KeyCode_D, KeyCode_E, KeyCode_F, KeyCode_G, KeyCode_H,
- KeyCode_I, KeyCode_J, KeyCode_K, KeyCode_L, KeyCode_M, KeyCode_N, KeyCode_O, KeyCode_P,
- KeyCode_Q, KeyCode_R, KeyCode_S, KeyCode_T, KeyCode_U, KeyCode_V, KeyCode_W, KeyCode_X,
- KeyCode_Y, KeyCode_Z,
-
- // Numbers
- KeyCode_0, KeyCode_1, KeyCode_2, KeyCode_3, KeyCode_4, KeyCode_5, KeyCode_6, KeyCode_7,
- KeyCode_8, KeyCode_9,
-
- KeyCode_Num0, KeyCode_Num1, KeyCode_Num2, KeyCode_Num3, KeyCode_Num4, KeyCode_Num5,
- KeyCode_Num6, KeyCode_Num7, KeyCode_Num8, KeyCode_Num9,
-
- // Symbols
- KeyCode_Bang, KeyCode_At, KeyCode_Pound, KeyCode_Dollar, KeyCode_Percent, KeyCode_Carrot,
- KeyCode_Ampersand, KeyCode_Star, KeyCode_LeftParen, KeyCode_RightParen, KeyCode_Minus, KeyCode_Plus,
- KeyCode_Equals, KeyCode_Underscore, KeyCode_LeftBrace, KeyCode_RightBrace, KeyCode_LeftBracket,
- KeyCode_RightBracket, KeyCode_Colon, KeyCode_SemiColon, KeyCode_SingleQuote, KeyCode_DoubleQuote,
- KeyCode_ForwardSlash, KeyCode_Backslash, KeyCode_Pipe, KeyCode_Comma, KeyCode_Period,
- KeyCode_QuestionMark, KeyCode_LessThan, KeyCode_GreaterThan, KeyCode_Tilde, KeyCode_BackQuote,
-
- // Arrows
- KeyCode_UpArrow,
- KeyCode_DownArrow,
- KeyCode_LeftArrow,
- KeyCode_RightArrow,
-
- // Mouse
- // NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions
- KeyCode_MouseLeftButton,
- KeyCode_MouseMiddleButton,
- KeyCode_MouseRightButton,
-
- KeyCode_Count,
+ KeyCode_Invalid,
+
+ KeyCode_Esc,
+
+ KeyCode_Space,
+ KeyCode_Tab,
+ KeyCode_CapsLock,
+ KeyCode_LeftShift, KeyCode_RightShift,
+ KeyCode_LeftCtrl, KeyCode_RightCtrl,
+ KeyCode_Fn,
+ KeyCode_Alt,
+ KeyCode_PageUp, KeyCode_PageDown,
+ KeyCode_Backspace, KeyCode_Delete,
+ KeyCode_Enter,
+
+ // Function Keys
+ KeyCode_F0, KeyCode_F1, KeyCode_F2, KeyCode_F3, KeyCode_F4, KeyCode_F5, KeyCode_F6, KeyCode_F7,
+ KeyCode_F8, KeyCode_F9, KeyCode_F10, KeyCode_F11, KeyCode_F12,
+
+ // Letters
+ KeyCode_a, KeyCode_b, KeyCode_c, KeyCode_d, KeyCode_e, KeyCode_f, KeyCode_g, KeyCode_h,
+ KeyCode_i, KeyCode_j, KeyCode_k, KeyCode_l, KeyCode_m, KeyCode_n, KeyCode_o, KeyCode_p,
+ KeyCode_q, KeyCode_r, KeyCode_s, KeyCode_t, KeyCode_u, KeyCode_v, KeyCode_w, KeyCode_x,
+ KeyCode_y, KeyCode_z,
+
+ KeyCode_A, KeyCode_B, KeyCode_C, KeyCode_D, KeyCode_E, KeyCode_F, KeyCode_G, KeyCode_H,
+ KeyCode_I, KeyCode_J, KeyCode_K, KeyCode_L, KeyCode_M, KeyCode_N, KeyCode_O, KeyCode_P,
+ KeyCode_Q, KeyCode_R, KeyCode_S, KeyCode_T, KeyCode_U, KeyCode_V, KeyCode_W, KeyCode_X,
+ KeyCode_Y, KeyCode_Z,
+
+ // Numbers
+ KeyCode_0, KeyCode_1, KeyCode_2, KeyCode_3, KeyCode_4, KeyCode_5, KeyCode_6, KeyCode_7,
+ KeyCode_8, KeyCode_9,
+
+ KeyCode_Num0, KeyCode_Num1, KeyCode_Num2, KeyCode_Num3, KeyCode_Num4, KeyCode_Num5,
+ KeyCode_Num6, KeyCode_Num7, KeyCode_Num8, KeyCode_Num9,
+
+ // Symbols
+ KeyCode_Bang, KeyCode_At, KeyCode_Pound, KeyCode_Dollar, KeyCode_Percent, KeyCode_Carrot,
+ KeyCode_Ampersand, KeyCode_Star, KeyCode_LeftParen, KeyCode_RightParen, KeyCode_Minus, KeyCode_Plus,
+ KeyCode_Equals, KeyCode_Underscore, KeyCode_LeftBrace, KeyCode_RightBrace, KeyCode_LeftBracket,
+ KeyCode_RightBracket, KeyCode_Colon, KeyCode_SemiColon, KeyCode_SingleQuote, KeyCode_DoubleQuote,
+ KeyCode_ForwardSlash, KeyCode_Backslash, KeyCode_Pipe, KeyCode_Comma, KeyCode_Period,
+ KeyCode_QuestionMark, KeyCode_LessThan, KeyCode_GreaterThan, KeyCode_Tilde, KeyCode_BackQuote,
+
+ // Arrows
+ KeyCode_UpArrow,
+ KeyCode_DownArrow,
+ KeyCode_LeftArrow,
+ KeyCode_RightArrow,
+
+ // Mouse
+ // NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions
+ KeyCode_MouseLeftButton,
+ KeyCode_MouseMiddleButton,
+ KeyCode_MouseRightButton,
+
+ KeyCode_Count,
};
internal char
CharacterFromKeyCode (key_code Code)
{
- char Result = 0;
+ char Result = 0;
+
+ switch (Code)
+ {
+ case KeyCode_Space: { Result = ' '; }break;
+ case KeyCode_Tab: { Result = '\t'; }break;
- switch (Code)
- {
- case KeyCode_Space: { Result = ' '; }break;
- case KeyCode_Tab: { Result = '\t'; }break;
-
- // Letters
- case KeyCode_a: { Result = 'a'; }break;
- case KeyCode_b: { Result = 'b'; }break;
- case KeyCode_c: { Result = 'c'; }break;
- case KeyCode_d: { Result = 'd'; }break;
- case KeyCode_e: { Result = 'e'; }break;
+ // Letters
+ case KeyCode_a: { Result = 'a'; }break;
+ case KeyCode_b: { Result = 'b'; }break;
+ case KeyCode_c: { Result = 'c'; }break;
+ case KeyCode_d: { Result = 'd'; }break;
+ case KeyCode_e: { Result = 'e'; }break;
case KeyCode_f: { Result = 'f'; }break;
case KeyCode_g: { Result = 'g'; }break;
case KeyCode_h: { Result = 'h'; }break;
- case KeyCode_i: { Result = 'i'; }break;
+ case KeyCode_i: { Result = 'i'; }break;
case KeyCode_j: { Result = 'j'; }break;
case KeyCode_k: { Result = 'k'; }break;
case KeyCode_l: { Result = 'l'; }break;
@@ -93,7 +93,7 @@ CharacterFromKeyCode (key_code Code)
case KeyCode_n: { Result = 'n'; }break;
case KeyCode_o: { Result = 'o'; }break;
case KeyCode_p: { Result = 'p'; }break;
- case KeyCode_q: { Result = 'q'; }break;
+ case KeyCode_q: { Result = 'q'; }break;
case KeyCode_r: { Result = 'r'; }break;
case KeyCode_s: { Result = 's'; }break;
case KeyCode_t: { Result = 't'; }break;
@@ -101,10 +101,10 @@ CharacterFromKeyCode (key_code Code)
case KeyCode_v: { Result = 'v'; }break;
case KeyCode_w: { Result = 'w'; }break;
case KeyCode_x: { Result = 'x'; }break;
- case KeyCode_y: { Result = 'y'; }break;
+ case KeyCode_y: { Result = 'y'; }break;
case KeyCode_z: { Result = 'z'; }break;
-
- case KeyCode_A: { Result = 'A'; }break;
+
+ case KeyCode_A: { Result = 'A'; }break;
case KeyCode_B: { Result = 'B'; }break;
case KeyCode_C: { Result = 'C'; }break;
case KeyCode_D: { Result = 'D'; }break;
@@ -112,7 +112,7 @@ CharacterFromKeyCode (key_code Code)
case KeyCode_F: { Result = 'F'; }break;
case KeyCode_G: { Result = 'G'; }break;
case KeyCode_H: { Result = 'H'; }break;
- case KeyCode_I: { Result = 'I'; }break;
+ case KeyCode_I: { Result = 'I'; }break;
case KeyCode_J: { Result = 'J'; }break;
case KeyCode_K: { Result = 'K'; }break;
case KeyCode_L: { Result = 'L'; }break;
@@ -120,7 +120,7 @@ CharacterFromKeyCode (key_code Code)
case KeyCode_N: { Result = 'N'; }break;
case KeyCode_O: { Result = 'O'; }break;
case KeyCode_P: { Result = 'P'; }break;
- case KeyCode_Q: { Result = 'Q'; }break;
+ case KeyCode_Q: { Result = 'Q'; }break;
case KeyCode_R: { Result = 'R'; }break;
case KeyCode_S: { Result = 'S'; }break;
case KeyCode_T: { Result = 'T'; }break;
@@ -128,11 +128,11 @@ CharacterFromKeyCode (key_code Code)
case KeyCode_V: { Result = 'V'; }break;
case KeyCode_W: { Result = 'W'; }break;
case KeyCode_X: { Result = 'X'; }break;
- case KeyCode_Y: { Result = 'Y'; }break;
+ case KeyCode_Y: { Result = 'Y'; }break;
case KeyCode_Z: { Result = 'Z'; }break;
-
- // Numbers
- case KeyCode_0: { Result = '0'; }break;
+
+ // Numbers
+ case KeyCode_0: { Result = '0'; }break;
case KeyCode_1: { Result = '1'; }break;
case KeyCode_2: { Result = '2'; }break;
case KeyCode_3: { Result = '3'; }break;
@@ -140,88 +140,88 @@ CharacterFromKeyCode (key_code Code)
case KeyCode_5: { Result = '5'; }break;
case KeyCode_6: { Result = '6'; }break;
case KeyCode_7: { Result = '7'; }break;
- case KeyCode_8: { Result = '8'; }break;
+ case KeyCode_8: { Result = '8'; }break;
case KeyCode_9: { Result = '9'; }break;
-
- case KeyCode_Num0: { Result = '0'; }break;
+
+ case KeyCode_Num0: { Result = '0'; }break;
case KeyCode_Num1: { Result = '1'; }break;
case KeyCode_Num2: { Result = '2'; }break;
case KeyCode_Num3: { Result = '3'; }break;
case KeyCode_Num4: { Result = '4'; }break;
case KeyCode_Num5: { Result = '5'; }break;
- case KeyCode_Num6: { Result = '6'; }break;
+ case KeyCode_Num6: { Result = '6'; }break;
case KeyCode_Num7: { Result = '7'; }break;
case KeyCode_Num8: { Result = '8'; }break;
case KeyCode_Num9: { Result = '9'; }break;
-
- // Symbols
- case KeyCode_Bang: { Result = '!'; }break;
+
+ // Symbols
+ case KeyCode_Bang: { Result = '!'; }break;
case KeyCode_At: { Result = '@'; }break;
case KeyCode_Pound: { Result = '#'; }break;
case KeyCode_Dollar: { Result = '$'; }break;
case KeyCode_Percent: { Result = '%'; }break;
case KeyCode_Carrot: { Result = '^'; }break;
- case KeyCode_Ampersand: { Result = '&'; }break;
+ case KeyCode_Ampersand: { Result = '&'; }break;
case KeyCode_Star: { Result = '*'; }break;
case KeyCode_LeftParen: { Result = '('; }break;
case KeyCode_RightParen: { Result = ')'; }break;
case KeyCode_Minus: { Result = '-'; }break;
case KeyCode_Plus: { Result = '+'; }break;
- case KeyCode_Equals: { Result = '='; }break;
+ case KeyCode_Equals: { Result = '='; }break;
case KeyCode_Underscore: { Result = '_'; }break;
case KeyCode_LeftBrace: { Result = '['; }break;
case KeyCode_RightBrace: { Result = ']'; }break;
case KeyCode_LeftBracket: { Result = '{'; }break;
- case KeyCode_RightBracket: { Result = '}'; }break;
+ case KeyCode_RightBracket: { Result = '}'; }break;
case KeyCode_Colon: { Result = ':'; }break;
case KeyCode_SemiColon: { Result = ';'; }break;
case KeyCode_SingleQuote: { Result = '\''; }break;
case KeyCode_DoubleQuote: { Result = '"'; }break;
- case KeyCode_ForwardSlash: { Result = '/'; }break;
+ case KeyCode_ForwardSlash: { Result = '/'; }break;
case KeyCode_Backslash: { Result = '\\'; }break;
case KeyCode_Pipe: { Result = '|'; }break;
case KeyCode_Comma: { Result = ','; }break;
case KeyCode_Period: { Result = '.'; }break;
- case KeyCode_QuestionMark: { Result = '?'; }break;
+ case KeyCode_QuestionMark: { Result = '?'; }break;
case KeyCode_LessThan: { Result = '<'; }break;
case KeyCode_GreaterThan: { Result = '>'; }break;
case KeyCode_Tilde: { Result = '~'; }break;
case KeyCode_BackQuote: { Result = '`'; }break;
- }
-
- Assert(Result != 0);
- return Result;
+ }
+
+ Assert(Result != 0);
+ return Result;
}
enum modifier_flags
{
- Modifier_Shift = 1 << 0,
- Modifier_Ctrl = 1 << 1,
- Modifier_Alt = 1 << 2,
- Modifier_Sys = 1 << 3, // NOTE(Peter): this is the windows key
+ Modifier_Shift = 1 << 0,
+ Modifier_Ctrl = 1 << 1,
+ Modifier_Alt = 1 << 2,
+ Modifier_Sys = 1 << 3, // NOTE(Peter): this is the windows key
};
#define INPUT_FRAME_STRING_LENGTH 32
struct input_frame
{
- b32 KeysDown[(int)KeyCode_Count];
- s32 StringInputUsed;
- char StringInput[INPUT_FRAME_STRING_LENGTH];
- s32 MouseX, MouseY, MouseScroll;
+ b32 KeysDown[(int)KeyCode_Count];
+ s32 StringInputUsed;
+ char StringInput[INPUT_FRAME_STRING_LENGTH];
+ s32 MouseX, MouseY, MouseScroll;
};
struct input
{
- input_frame Frames[2];
- input_frame* New;
- input_frame* Old;
- s32 MouseDownX, MouseDownY;
+ input_frame Frames[2];
+ input_frame* New;
+ input_frame* Old;
+ s32 MouseDownX, MouseDownY;
};
enum key_state_flags
{
- KeyState_WasDown = 1 << 0,
- KeyState_IsDown = 1 << 1,
+ KeyState_WasDown = 1 << 0,
+ KeyState_IsDown = 1 << 1,
};
#define KeyWasDown(event) ((event & KeyState_WasDown) > 0)
@@ -234,75 +234,75 @@ enum key_state_flags
struct input_entry
{
- key_code Key;
- b32 State;
- b32 Modifiers;
+ key_code Key;
+ b32 State;
+ b32 Modifiers;
};
struct input_queue
{
- s32 QueueSize;
- s32 QueueUsed;
- input_entry* Entries;
+ s32 QueueSize;
+ s32 QueueUsed;
+ input_entry* Entries;
};
enum cursor_type
{
- CursorType_Arrow,
- CursorType_Pointer,
- CursorType_Loading,
- CursorType_HArrows,
- CursorType_VArrows,
- CursorType_DTopLeftArrows,
- CursorType_DTopRightArrows,
- CursorType_Count,
+ CursorType_Arrow,
+ CursorType_Pointer,
+ CursorType_Loading,
+ CursorType_HArrows,
+ CursorType_VArrows,
+ CursorType_DTopLeftArrows,
+ CursorType_DTopRightArrows,
+ CursorType_Count,
};
struct mouse_state
{
- s32 Scroll;
-
- v2 Pos;
- v2 OldPos;
- v2 DeltaPos;
- v2 DownPos;
-
- b32 LeftButtonState;
- b32 MiddleButtonState;
- b32 RightButtonState;
-
-
-
- cursor_type CursorType;
+ s32 Scroll;
+
+ v2 Pos;
+ v2 OldPos;
+ v2 DeltaPos;
+ v2 DownPos;
+
+ b32 LeftButtonState;
+ b32 MiddleButtonState;
+ b32 RightButtonState;
+
+
+
+ cursor_type CursorType;
};
internal input_queue
InputQueue_Create (u8* Memory, s32 MemorySize)
{
- input_queue Result = {};
- s32 EntriesCount = MemorySize / sizeof(input_entry);
- Result.QueueSize = EntriesCount;
- Result.QueueUsed = 0;
- Result.Entries = (input_entry*)Memory;
- return Result;
+ input_queue Result = {};
+ s32 EntriesCount = MemorySize / sizeof(input_entry);
+ Result.QueueSize = EntriesCount;
+ Result.QueueUsed = 0;
+ Result.Entries = (input_entry*)Memory;
+ return Result;
}
internal input_queue
InputQueue_Create(gs_memory_arena* Arena, u32 CountMax)
{
- input_queue Result = {0};
- if (CountMax > 0)
- {
- s32 Size = sizeof(input_entry) * 32;
- u8* Memory = PushSize(Arena, Size);
- Result = InputQueue_Create(Memory, Size);
- }
- return Result;
+ input_queue Result = {0};
+ if (CountMax > 0)
+ {
+ s32 Size = sizeof(input_entry) * 32;
+ u8* Memory = PushSize(Arena, Size).Memory;
+ Result = InputQueue_Create(Memory, Size);
+ }
+ return Result;
}
internal void
ResetInputQueue (input_queue* Queue)
{
- Queue->QueueUsed = 0;
+ Queue->QueueUsed = 0;
}
internal void InitializeInput (input* Input);
@@ -311,360 +311,360 @@ internal void SwapInputBuffers (input* Input);
internal void
InitializeInput (input* Input)
{
- *(Input) = {};
- Input->New = &Input->Frames[0];
- Input->Old = &Input->Frames[1];
+ *(Input) = {};
+ Input->New = &Input->Frames[0];
+ Input->Old = &Input->Frames[1];
}
internal void
SwapInputBuffers (input* Input)
{
- input_frame* NowOld = Input->New;
- Input->New = Input->Old;
- Input->Old = NowOld;
-
- for (s32 Key = 0; Key < KeyCode_Count; Key++) { Input->New->KeysDown[Key] = Input->Old->KeysDown[Key]; }
- Input->New->StringInputUsed = 0;
+ input_frame* NowOld = Input->New;
+ Input->New = Input->Old;
+ Input->Old = NowOld;
+
+ for (s32 Key = 0; Key < KeyCode_Count; Key++) { Input->New->KeysDown[Key] = Input->Old->KeysDown[Key]; }
+ Input->New->StringInputUsed = 0;
}
internal b32
KeyDown (input Input, key_code Key)
{
- return Input.New->KeysDown[Key];
+ return Input.New->KeysDown[Key];
}
internal b32
KeyTransitionedDown (input Input, key_code Key)
{
- return Input.New->KeysDown[Key] && !Input.Old->KeysDown[Key];
+ return Input.New->KeysDown[Key] && !Input.Old->KeysDown[Key];
}
internal b32
KeyTransitionedUp (input Input, key_code Key)
{
- return !Input.New->KeysDown[Key] && Input.Old->KeysDown[Key];
+ return !Input.New->KeysDown[Key] && Input.Old->KeysDown[Key];
}
internal void
AddInputEventEntry (input_queue* Queue, key_code Key,
b32 WasDown, b32 IsDown, b32 ShiftDown, b32 AltDown, b32 CtrlDown, b32 SysDown)
{
- Assert(Queue->QueueUsed < Queue->QueueSize);
-
- input_entry* Entry = Queue->Entries + Queue->QueueUsed++;
- Entry->Key = Key;
-
- Entry->State = (key_state_flags)0;
- if (WasDown) { Entry->State |= KeyState_WasDown; }
- if (IsDown) { Entry->State |= KeyState_IsDown; }
-
- Entry->Modifiers = 0;
- if (ShiftDown) { Entry->Modifiers |= Modifier_Shift; }
- if (CtrlDown) { Entry->Modifiers |= Modifier_Ctrl; }
- if (AltDown) { Entry->Modifiers |= Modifier_Alt; }
- if (SysDown) { Entry->Modifiers |= Modifier_Sys; }
+ Assert(Queue->QueueUsed < Queue->QueueSize);
+
+ input_entry* Entry = Queue->Entries + Queue->QueueUsed++;
+ Entry->Key = Key;
+
+ Entry->State = (key_state_flags)0;
+ if (WasDown) { Entry->State |= KeyState_WasDown; }
+ if (IsDown) { Entry->State |= KeyState_IsDown; }
+
+ Entry->Modifiers = 0;
+ if (ShiftDown) { Entry->Modifiers |= Modifier_Shift; }
+ if (CtrlDown) { Entry->Modifiers |= Modifier_Ctrl; }
+ if (AltDown) { Entry->Modifiers |= Modifier_Alt; }
+ if (SysDown) { Entry->Modifiers |= Modifier_Sys; }
}
internal b32
KeyTransitionedDown (input_entry Entry)
{
- return (!KeyWasDown(Entry.State) && KeyIsDown(Entry.State));
+ return (!KeyWasDown(Entry.State) && KeyIsDown(Entry.State));
}
internal b32
KeyTransitionedUp (input_entry Entry)
{
- return (KeyWasDown(Entry.State) && !KeyIsDown(Entry.State));
+ return (KeyWasDown(Entry.State) && !KeyIsDown(Entry.State));
}
internal b32
KeyHeldDown (input_entry Entry)
{
- return (KeyWasDown(Entry.State) && KeyIsDown(Entry.State));
+ return (KeyWasDown(Entry.State) && KeyIsDown(Entry.State));
}
internal b32
MouseButtonTransitionedDown (b32 MouseButtonState)
{
- return (!KeyWasDown(MouseButtonState) && KeyIsDown(MouseButtonState));
+ return (!KeyWasDown(MouseButtonState) && KeyIsDown(MouseButtonState));
}
internal b32
MouseButtonTransitionedUp (b32 MouseButtonState)
{
- return (KeyWasDown(MouseButtonState) && !KeyIsDown(MouseButtonState));
+ return (KeyWasDown(MouseButtonState) && !KeyIsDown(MouseButtonState));
}
internal b32
MouseButtonHeldDown (b32 MouseButtonState)
{
- b32 WasDown = KeyWasDown(MouseButtonState);
- b32 IsDown = KeyIsDown(MouseButtonState);
- return (WasDown && IsDown);
+ b32 WasDown = KeyWasDown(MouseButtonState);
+ b32 IsDown = KeyIsDown(MouseButtonState);
+ return (WasDown && IsDown);
}
inline b32
GetMouseButtonStateAdvanced (b32 ButtonState)
{
- b32 Result = ButtonState;
- if (ButtonState & KeyState_WasDown &&
- !((ButtonState & KeyState_IsDown) > 0))
- {
- Result= 0;
- }
- else if (ButtonState & KeyState_IsDown)
- {
- Result |= KeyState_WasDown;
- }
- return Result;
+ b32 Result = ButtonState;
+ if (ButtonState & KeyState_WasDown &&
+ !((ButtonState & KeyState_IsDown) > 0))
+ {
+ Result= 0;
+ }
+ else if (ButtonState & KeyState_IsDown)
+ {
+ Result |= KeyState_WasDown;
+ }
+ return Result;
}
internal char
KeyCodeToChar(key_code Code)
{
- char Result = 0;
+ char Result = 0;
+
+ switch (Code)
+ {
+ case KeyCode_Space: { Result = ' '; } break;
+ case KeyCode_Tab: { Result = '\t'; } break;
+ case KeyCode_Enter: { Result = '\n'; } break;
+ case KeyCode_Backspace: { Result = '\b'; } break;
- switch (Code)
- {
- case KeyCode_Space: { Result = ' '; } break;
- case KeyCode_Tab: { Result = '\t'; } break;
- case KeyCode_Enter: { Result = '\n'; } break;
- case KeyCode_Backspace: { Result = '\b'; } break;
-
- case KeyCode_a: { Result = 'a'; } break;
- case KeyCode_b: { Result = 'b'; } break;
- case KeyCode_c: { Result = 'c'; } break;
- case KeyCode_d: { Result = 'd'; } break;
- case KeyCode_e: { Result = 'e'; } break;
- case KeyCode_f: { Result = 'f'; } break;
- case KeyCode_g: { Result = 'g'; } break;
- case KeyCode_h: { Result = 'h'; } break;
- case KeyCode_i: { Result = 'i'; } break;
- case KeyCode_j: { Result = 'j'; } break;
- case KeyCode_k: { Result = 'k'; } break;
- case KeyCode_l: { Result = 'l'; } break;
- case KeyCode_m: { Result = 'm'; } break;
- case KeyCode_n: { Result = 'n'; } break;
- case KeyCode_o: { Result = 'o'; } break;
- case KeyCode_p: { Result = 'p'; } break;
- case KeyCode_q: { Result = 'q'; } break;
- case KeyCode_r: { Result = 'r'; } break;
- case KeyCode_s: { Result = 's'; } break;
- case KeyCode_t: { Result = 't'; } break;
- case KeyCode_u: { Result = 'u'; } break;
- case KeyCode_v: { Result = 'v'; } break;
- case KeyCode_w: { Result = 'w'; } break;
- case KeyCode_x: { Result = 'x'; } break;
- case KeyCode_y: { Result = 'y'; } break;
- case KeyCode_z: { Result = 'z'; } break;
- case KeyCode_A: { Result = 'A'; } break;
- case KeyCode_B: { Result = 'B'; } break;
- case KeyCode_C: { Result = 'C'; } break;
- case KeyCode_D: { Result = 'D'; } break;
- case KeyCode_E: { Result = 'E'; } break;
- case KeyCode_F: { Result = 'F'; } break;
- case KeyCode_G: { Result = 'G'; } break;
- case KeyCode_H: { Result = 'H'; } break;
- case KeyCode_I: { Result = 'I'; } break;
- case KeyCode_J: { Result = 'J'; } break;
- case KeyCode_K: { Result = 'K'; } break;
- case KeyCode_L: { Result = 'L'; } break;
- case KeyCode_M: { Result = 'M'; } break;
- case KeyCode_N: { Result = 'N'; } break;
- case KeyCode_O: { Result = 'O'; } break;
- case KeyCode_P: { Result = 'P'; } break;
- case KeyCode_Q: { Result = 'Q'; } break;
- case KeyCode_R: { Result = 'R'; } break;
- case KeyCode_S: { Result = 'S'; } break;
- case KeyCode_T: { Result = 'T'; } break;
- case KeyCode_U: { Result = 'U'; } break;
- case KeyCode_V: { Result = 'V'; } break;
- case KeyCode_W: { Result = 'W'; } break;
- case KeyCode_X: { Result = 'X'; } break;
- case KeyCode_Y: { Result = 'Y'; } break;
- case KeyCode_Z: { Result = 'Z'; } break;
-
- case KeyCode_Num0:
- case KeyCode_0: { Result = '0'; } break;
- case KeyCode_Num1:
- case KeyCode_1: { Result = '1'; } break;
- case KeyCode_Num2:
- case KeyCode_2: { Result = '2'; } break;
- case KeyCode_Num3:
- case KeyCode_3: { Result = '3'; } break;
- case KeyCode_Num4:
- case KeyCode_4: { Result = '4'; } break;
- case KeyCode_Num5:
- case KeyCode_5: { Result = '5'; } break;
- case KeyCode_Num6:
- case KeyCode_6: { Result = '6'; } break;
- case KeyCode_Num7:
- case KeyCode_7: { Result = '7'; } break;
- case KeyCode_Num8:
- case KeyCode_8: { Result = '8'; } break;
- case KeyCode_Num9:
- case KeyCode_9: { Result = '9'; } break;
-
- case KeyCode_Bang: { Result = '!'; } break;
- case KeyCode_At: { Result = '@'; } break;
- case KeyCode_Pound: { Result = '#'; } break;
- case KeyCode_Dollar: { Result = '$'; } break;
- case KeyCode_Percent: { Result = '%'; } break;
- case KeyCode_Carrot: { Result = '^'; } break;
- case KeyCode_Ampersand: { Result = '&'; } break;
- case KeyCode_Star: { Result = '*'; } break;
- case KeyCode_LeftParen: { Result = '('; } break;
- case KeyCode_RightParen: { Result = ')'; } break;
- case KeyCode_Minus: { Result = '-'; } break;
- case KeyCode_Plus: { Result = '+'; } break;
- case KeyCode_Equals: { Result = '='; } break;
- case KeyCode_Underscore: { Result = '_'; } break;
- case KeyCode_LeftBrace: { Result = '{'; } break;
- case KeyCode_RightBrace: { Result = '}'; } break;
- case KeyCode_LeftBracket: { Result = '['; } break;
- case KeyCode_RightBracket: { Result = ']'; } break;
- case KeyCode_Colon: { Result = ':'; } break;
- case KeyCode_SemiColon: { Result = ';'; } break;
- case KeyCode_SingleQuote: { Result = '\''; } break;
- case KeyCode_DoubleQuote: { Result = '"'; } break;
- case KeyCode_ForwardSlash: { Result = '/'; } break;
- case KeyCode_Backslash: { Result = '\\'; } break;
- case KeyCode_Pipe: { Result = '|'; } break;
- case KeyCode_Comma: { Result = ','; } break;
- case KeyCode_Period: { Result = '.'; } break;
- case KeyCode_QuestionMark: { Result = '?'; } break;
- case KeyCode_LessThan: { Result = '<'; } break;
- case KeyCode_GreaterThan: { Result = '>'; } break;
- case KeyCode_Tilde: { Result = '~'; } break;
- case KeyCode_BackQuote: { Result = '`'; } break;
-
- default: { Result = 0; } break;
- }
+ case KeyCode_a: { Result = 'a'; } break;
+ case KeyCode_b: { Result = 'b'; } break;
+ case KeyCode_c: { Result = 'c'; } break;
+ case KeyCode_d: { Result = 'd'; } break;
+ case KeyCode_e: { Result = 'e'; } break;
+ case KeyCode_f: { Result = 'f'; } break;
+ case KeyCode_g: { Result = 'g'; } break;
+ case KeyCode_h: { Result = 'h'; } break;
+ case KeyCode_i: { Result = 'i'; } break;
+ case KeyCode_j: { Result = 'j'; } break;
+ case KeyCode_k: { Result = 'k'; } break;
+ case KeyCode_l: { Result = 'l'; } break;
+ case KeyCode_m: { Result = 'm'; } break;
+ case KeyCode_n: { Result = 'n'; } break;
+ case KeyCode_o: { Result = 'o'; } break;
+ case KeyCode_p: { Result = 'p'; } break;
+ case KeyCode_q: { Result = 'q'; } break;
+ case KeyCode_r: { Result = 'r'; } break;
+ case KeyCode_s: { Result = 's'; } break;
+ case KeyCode_t: { Result = 't'; } break;
+ case KeyCode_u: { Result = 'u'; } break;
+ case KeyCode_v: { Result = 'v'; } break;
+ case KeyCode_w: { Result = 'w'; } break;
+ case KeyCode_x: { Result = 'x'; } break;
+ case KeyCode_y: { Result = 'y'; } break;
+ case KeyCode_z: { Result = 'z'; } break;
+ case KeyCode_A: { Result = 'A'; } break;
+ case KeyCode_B: { Result = 'B'; } break;
+ case KeyCode_C: { Result = 'C'; } break;
+ case KeyCode_D: { Result = 'D'; } break;
+ case KeyCode_E: { Result = 'E'; } break;
+ case KeyCode_F: { Result = 'F'; } break;
+ case KeyCode_G: { Result = 'G'; } break;
+ case KeyCode_H: { Result = 'H'; } break;
+ case KeyCode_I: { Result = 'I'; } break;
+ case KeyCode_J: { Result = 'J'; } break;
+ case KeyCode_K: { Result = 'K'; } break;
+ case KeyCode_L: { Result = 'L'; } break;
+ case KeyCode_M: { Result = 'M'; } break;
+ case KeyCode_N: { Result = 'N'; } break;
+ case KeyCode_O: { Result = 'O'; } break;
+ case KeyCode_P: { Result = 'P'; } break;
+ case KeyCode_Q: { Result = 'Q'; } break;
+ case KeyCode_R: { Result = 'R'; } break;
+ case KeyCode_S: { Result = 'S'; } break;
+ case KeyCode_T: { Result = 'T'; } break;
+ case KeyCode_U: { Result = 'U'; } break;
+ case KeyCode_V: { Result = 'V'; } break;
+ case KeyCode_W: { Result = 'W'; } break;
+ case KeyCode_X: { Result = 'X'; } break;
+ case KeyCode_Y: { Result = 'Y'; } break;
+ case KeyCode_Z: { Result = 'Z'; } break;
- return Result;
+ case KeyCode_Num0:
+ case KeyCode_0: { Result = '0'; } break;
+ case KeyCode_Num1:
+ case KeyCode_1: { Result = '1'; } break;
+ case KeyCode_Num2:
+ case KeyCode_2: { Result = '2'; } break;
+ case KeyCode_Num3:
+ case KeyCode_3: { Result = '3'; } break;
+ case KeyCode_Num4:
+ case KeyCode_4: { Result = '4'; } break;
+ case KeyCode_Num5:
+ case KeyCode_5: { Result = '5'; } break;
+ case KeyCode_Num6:
+ case KeyCode_6: { Result = '6'; } break;
+ case KeyCode_Num7:
+ case KeyCode_7: { Result = '7'; } break;
+ case KeyCode_Num8:
+ case KeyCode_8: { Result = '8'; } break;
+ case KeyCode_Num9:
+ case KeyCode_9: { Result = '9'; } break;
+
+ case KeyCode_Bang: { Result = '!'; } break;
+ case KeyCode_At: { Result = '@'; } break;
+ case KeyCode_Pound: { Result = '#'; } break;
+ case KeyCode_Dollar: { Result = '$'; } break;
+ case KeyCode_Percent: { Result = '%'; } break;
+ case KeyCode_Carrot: { Result = '^'; } break;
+ case KeyCode_Ampersand: { Result = '&'; } break;
+ case KeyCode_Star: { Result = '*'; } break;
+ case KeyCode_LeftParen: { Result = '('; } break;
+ case KeyCode_RightParen: { Result = ')'; } break;
+ case KeyCode_Minus: { Result = '-'; } break;
+ case KeyCode_Plus: { Result = '+'; } break;
+ case KeyCode_Equals: { Result = '='; } break;
+ case KeyCode_Underscore: { Result = '_'; } break;
+ case KeyCode_LeftBrace: { Result = '{'; } break;
+ case KeyCode_RightBrace: { Result = '}'; } break;
+ case KeyCode_LeftBracket: { Result = '['; } break;
+ case KeyCode_RightBracket: { Result = ']'; } break;
+ case KeyCode_Colon: { Result = ':'; } break;
+ case KeyCode_SemiColon: { Result = ';'; } break;
+ case KeyCode_SingleQuote: { Result = '\''; } break;
+ case KeyCode_DoubleQuote: { Result = '"'; } break;
+ case KeyCode_ForwardSlash: { Result = '/'; } break;
+ case KeyCode_Backslash: { Result = '\\'; } break;
+ case KeyCode_Pipe: { Result = '|'; } break;
+ case KeyCode_Comma: { Result = ','; } break;
+ case KeyCode_Period: { Result = '.'; } break;
+ case KeyCode_QuestionMark: { Result = '?'; } break;
+ case KeyCode_LessThan: { Result = '<'; } break;
+ case KeyCode_GreaterThan: { Result = '>'; } break;
+ case KeyCode_Tilde: { Result = '~'; } break;
+ case KeyCode_BackQuote: { Result = '`'; } break;
+
+ default: { Result = 0; } break;
+ }
+
+ return Result;
}
internal bool
KeyCodeHasChar(key_code Code)
{
- bool Result = KeyCodeToChar(Code) != 0;
- return Result;
+ bool Result = KeyCodeToChar(Code) != 0;
+ return Result;
}
internal key_code
CharToKeyCode(char C)
{
- key_code Result = KeyCode_Invalid;
+ key_code Result = KeyCode_Invalid;
+
+ switch (C)
+ {
+ case ' ': { Result = KeyCode_Space; } break;
+ case '\t': { Result = KeyCode_Tab; } break;
+ case '\n': { Result = KeyCode_Enter; } break;
+ case '\b': { Result = KeyCode_Backspace; } break;
- switch (C)
- {
- case ' ': { Result = KeyCode_Space; } break;
- case '\t': { Result = KeyCode_Tab; } break;
- case '\n': { Result = KeyCode_Enter; } break;
- case '\b': { Result = KeyCode_Backspace; } break;
-
- case 'a': { Result = KeyCode_a; } break;
- case 'b': { Result = KeyCode_b; } break;
- case 'c': { Result = KeyCode_c; } break;
- case 'd': { Result = KeyCode_d; } break;
- case 'e': { Result = KeyCode_e; } break;
- case 'f': { Result = KeyCode_f; } break;
- case 'g': { Result = KeyCode_g; } break;
- case 'h': { Result = KeyCode_h; } break;
- case 'i': { Result = KeyCode_i; } break;
- case 'j': { Result = KeyCode_j; } break;
- case 'k': { Result = KeyCode_k; } break;
- case 'l': { Result = KeyCode_l; } break;
- case 'm': { Result = KeyCode_m; } break;
- case 'n': { Result = KeyCode_n; } break;
- case 'o': { Result = KeyCode_o; } break;
- case 'p': { Result = KeyCode_p; } break;
- case 'q': { Result = KeyCode_q; } break;
- case 'r': { Result = KeyCode_r; } break;
- case 's': { Result = KeyCode_s; } break;
- case 't': { Result = KeyCode_t; } break;
- case 'u': { Result = KeyCode_u; } break;
- case 'v': { Result = KeyCode_v; } break;
- case 'w': { Result = KeyCode_w; } break;
- case 'x': { Result = KeyCode_x; } break;
- case 'y': { Result = KeyCode_y; } break;
- case 'z': { Result = KeyCode_z; } break;
- case 'A': { Result = KeyCode_A; } break;
- case 'B': { Result = KeyCode_B; } break;
- case 'C': { Result = KeyCode_C; } break;
- case 'D': { Result = KeyCode_D; } break;
- case 'E': { Result = KeyCode_E; } break;
- case 'F': { Result = KeyCode_F; } break;
- case 'G': { Result = KeyCode_G; } break;
- case 'H': { Result = KeyCode_H; } break;
- case 'I': { Result = KeyCode_I; } break;
- case 'J': { Result = KeyCode_J; } break;
- case 'K': { Result = KeyCode_K; } break;
- case 'L': { Result = KeyCode_L; } break;
- case 'M': { Result = KeyCode_M; } break;
- case 'N': { Result = KeyCode_N; } break;
- case 'O': { Result = KeyCode_O; } break;
- case 'P': { Result = KeyCode_P; } break;
- case 'Q': { Result = KeyCode_Q; } break;
- case 'R': { Result = KeyCode_R; } break;
- case 'S': { Result = KeyCode_S; } break;
- case 'T': { Result = KeyCode_T; } break;
- case 'U': { Result = KeyCode_U; } break;
- case 'V': { Result = KeyCode_V; } break;
- case 'W': { Result = KeyCode_W; } break;
- case 'X': { Result = KeyCode_X; } break;
- case 'Y': { Result = KeyCode_Y; } break;
- case 'Z': { Result = KeyCode_Z; } break;
-
- case '0': { Result = KeyCode_0; } break;
- case '1': { Result = KeyCode_1; } break;
- case '2': { Result = KeyCode_2; } break;
- case '3': { Result = KeyCode_3; } break;
- case '4': { Result = KeyCode_4; } break;
- case '5': { Result = KeyCode_5; } break;
- case '6': { Result = KeyCode_6; } break;
- case '7': { Result = KeyCode_7; } break;
- case '8': { Result = KeyCode_8; } break;
- case '9': { Result = KeyCode_9; } break;
-
- case '!': { Result = KeyCode_Bang; } break;
- case '@': { Result = KeyCode_At; } break;
- case '#': { Result = KeyCode_Pound; } break;
- case '$': { Result = KeyCode_Dollar; } break;
- case '%': { Result = KeyCode_Percent; } break;
- case '^': { Result = KeyCode_Carrot; } break;
- case '&': { Result = KeyCode_Ampersand; } break;
- case '*': { Result = KeyCode_Star; } break;
- case '(': { Result = KeyCode_LeftParen; } break;
- case ')': { Result = KeyCode_RightParen; } break;
- case '-': { Result = KeyCode_Minus; } break;
- case '+': { Result = KeyCode_Plus; } break;
- case '=': { Result = KeyCode_Equals; } break;
- case '_': { Result = KeyCode_Underscore; } break;
- case '{': { Result = KeyCode_LeftBrace; } break;
- case '}': { Result = KeyCode_RightBrace; } break;
- case '[': { Result = KeyCode_LeftBracket; } break;
- case ']': { Result = KeyCode_RightBracket; } break;
- case ':': { Result = KeyCode_Colon; } break;
- case ';': { Result = KeyCode_SemiColon; } break;
- case '\'': { Result = KeyCode_SingleQuote; } break;
- case '"': { Result = KeyCode_DoubleQuote; } break;
- case '/': { Result = KeyCode_ForwardSlash; } break;
- case '\\': { Result = KeyCode_Backslash; } break;
- case '|': { Result = KeyCode_Pipe; } break;
- case ',': { Result = KeyCode_Comma; } break;
- case '.': { Result = KeyCode_Period; } break;
- case '?': { Result = KeyCode_QuestionMark; } break;
- case '<': { Result = KeyCode_LessThan; } break;
- case '>': { Result = KeyCode_GreaterThan; } break;
- case '~': { Result = KeyCode_Tilde; } break;
- case '`': { Result = KeyCode_BackQuote; } break;
-
- default: { Result = KeyCode_Invalid; } break;
- }
+ case 'a': { Result = KeyCode_a; } break;
+ case 'b': { Result = KeyCode_b; } break;
+ case 'c': { Result = KeyCode_c; } break;
+ case 'd': { Result = KeyCode_d; } break;
+ case 'e': { Result = KeyCode_e; } break;
+ case 'f': { Result = KeyCode_f; } break;
+ case 'g': { Result = KeyCode_g; } break;
+ case 'h': { Result = KeyCode_h; } break;
+ case 'i': { Result = KeyCode_i; } break;
+ case 'j': { Result = KeyCode_j; } break;
+ case 'k': { Result = KeyCode_k; } break;
+ case 'l': { Result = KeyCode_l; } break;
+ case 'm': { Result = KeyCode_m; } break;
+ case 'n': { Result = KeyCode_n; } break;
+ case 'o': { Result = KeyCode_o; } break;
+ case 'p': { Result = KeyCode_p; } break;
+ case 'q': { Result = KeyCode_q; } break;
+ case 'r': { Result = KeyCode_r; } break;
+ case 's': { Result = KeyCode_s; } break;
+ case 't': { Result = KeyCode_t; } break;
+ case 'u': { Result = KeyCode_u; } break;
+ case 'v': { Result = KeyCode_v; } break;
+ case 'w': { Result = KeyCode_w; } break;
+ case 'x': { Result = KeyCode_x; } break;
+ case 'y': { Result = KeyCode_y; } break;
+ case 'z': { Result = KeyCode_z; } break;
+ case 'A': { Result = KeyCode_A; } break;
+ case 'B': { Result = KeyCode_B; } break;
+ case 'C': { Result = KeyCode_C; } break;
+ case 'D': { Result = KeyCode_D; } break;
+ case 'E': { Result = KeyCode_E; } break;
+ case 'F': { Result = KeyCode_F; } break;
+ case 'G': { Result = KeyCode_G; } break;
+ case 'H': { Result = KeyCode_H; } break;
+ case 'I': { Result = KeyCode_I; } break;
+ case 'J': { Result = KeyCode_J; } break;
+ case 'K': { Result = KeyCode_K; } break;
+ case 'L': { Result = KeyCode_L; } break;
+ case 'M': { Result = KeyCode_M; } break;
+ case 'N': { Result = KeyCode_N; } break;
+ case 'O': { Result = KeyCode_O; } break;
+ case 'P': { Result = KeyCode_P; } break;
+ case 'Q': { Result = KeyCode_Q; } break;
+ case 'R': { Result = KeyCode_R; } break;
+ case 'S': { Result = KeyCode_S; } break;
+ case 'T': { Result = KeyCode_T; } break;
+ case 'U': { Result = KeyCode_U; } break;
+ case 'V': { Result = KeyCode_V; } break;
+ case 'W': { Result = KeyCode_W; } break;
+ case 'X': { Result = KeyCode_X; } break;
+ case 'Y': { Result = KeyCode_Y; } break;
+ case 'Z': { Result = KeyCode_Z; } break;
- return Result;
+ case '0': { Result = KeyCode_0; } break;
+ case '1': { Result = KeyCode_1; } break;
+ case '2': { Result = KeyCode_2; } break;
+ case '3': { Result = KeyCode_3; } break;
+ case '4': { Result = KeyCode_4; } break;
+ case '5': { Result = KeyCode_5; } break;
+ case '6': { Result = KeyCode_6; } break;
+ case '7': { Result = KeyCode_7; } break;
+ case '8': { Result = KeyCode_8; } break;
+ case '9': { Result = KeyCode_9; } break;
+
+ case '!': { Result = KeyCode_Bang; } break;
+ case '@': { Result = KeyCode_At; } break;
+ case '#': { Result = KeyCode_Pound; } break;
+ case '$': { Result = KeyCode_Dollar; } break;
+ case '%': { Result = KeyCode_Percent; } break;
+ case '^': { Result = KeyCode_Carrot; } break;
+ case '&': { Result = KeyCode_Ampersand; } break;
+ case '*': { Result = KeyCode_Star; } break;
+ case '(': { Result = KeyCode_LeftParen; } break;
+ case ')': { Result = KeyCode_RightParen; } break;
+ case '-': { Result = KeyCode_Minus; } break;
+ case '+': { Result = KeyCode_Plus; } break;
+ case '=': { Result = KeyCode_Equals; } break;
+ case '_': { Result = KeyCode_Underscore; } break;
+ case '{': { Result = KeyCode_LeftBrace; } break;
+ case '}': { Result = KeyCode_RightBrace; } break;
+ case '[': { Result = KeyCode_LeftBracket; } break;
+ case ']': { Result = KeyCode_RightBracket; } break;
+ case ':': { Result = KeyCode_Colon; } break;
+ case ';': { Result = KeyCode_SemiColon; } break;
+ case '\'': { Result = KeyCode_SingleQuote; } break;
+ case '"': { Result = KeyCode_DoubleQuote; } break;
+ case '/': { Result = KeyCode_ForwardSlash; } break;
+ case '\\': { Result = KeyCode_Backslash; } break;
+ case '|': { Result = KeyCode_Pipe; } break;
+ case ',': { Result = KeyCode_Comma; } break;
+ case '.': { Result = KeyCode_Period; } break;
+ case '?': { Result = KeyCode_QuestionMark; } break;
+ case '<': { Result = KeyCode_LessThan; } break;
+ case '>': { Result = KeyCode_GreaterThan; } break;
+ case '~': { Result = KeyCode_Tilde; } break;
+ case '`': { Result = KeyCode_BackQuote; } break;
+
+ default: { Result = KeyCode_Invalid; } break;
+ }
+
+ return Result;
}
#define GS_INPUT_H
diff --git a/src/gs_libs/gs_language.h b/src/gs_libs/gs_language.h
index b95b246..3e31391 100644
--- a/src/gs_libs/gs_language.h
+++ b/src/gs_libs/gs_language.h
@@ -85,7 +85,7 @@ typedef double r64;
#endif
-#ifdef DEBUG
+#if DEBUG
static void DebugPrint(char* Format, ...);
@@ -95,8 +95,8 @@ static void DebugPrint(char* Format, ...);
if((expression)) \
{ \
}else{ \
- volatile int* p = 0; \
- *p = 5; \
+volatile int* p = 0; \
+*p = 5; \
}
#endif
@@ -319,9 +319,9 @@ GSIntDivideRoundUpDef(s64);
#define GSRemapDef(type) \
static type GSRemap(type Value, type OldMin, type OldMax, type NewMin, type NewMax) { \
- type Result = (Value - OldMin) / (OldMax - OldMin); \
- Result = (Result * (NewMax - NewMin)) + NewMin; \
- return Result; \
+type Result = (Value - OldMin) / (OldMax - OldMin); \
+Result = (Result * (NewMax - NewMin)) + NewMin; \
+return Result; \
}
GSRemapDef(u8);
GSRemapDef(u16);
diff --git a/src/gs_libs/gs_memory.h b/src/gs_libs/gs_memory.h
new file mode 100644
index 0000000..d72ac56
--- /dev/null
+++ b/src/gs_libs/gs_memory.h
@@ -0,0 +1,528 @@
+/* date = May 10th 2021 10:19 pm */
+
+#ifndef GS_MEMORY_H
+#define GS_MEMORY_H
+
+#if !defined(Assert)
+# define Assert(c)
+#endif
+
+#if !defined(InvalidCodePath)
+# define InvalidCodePath
+#endif
+
+#if !defined(GS_MEMORY_PROFILE_FUNC)
+# define GS_MEMORY_PROFILE_FUNC
+#endif
+
+#define MemoryClearArray(b,t,s) MemoryClear((u8*)(b), sizeof(t) * (s))
+internal void
+MemoryClear(u8* Base, u64 Size)
+{
+ for (u64 i = 0; i < Size; i++) Base[i] = 0;
+}
+
+#ifndef DEBUG_LOC
+
+typedef struct gs_debug_loc
+{
+ char* File;
+ char* Function;
+ u32 Line;
+} gs_debug_loc;
+
+# define DEBUG_LOC gs_debug_loc{ (char*)__FILE__, (char*)__FUNCTION__, __LINE__ }
+
+#endif
+//
+// Debug Info
+//
+
+typedef struct gs_debug_memory_allocation
+{
+ gs_debug_loc Loc;
+ u64 ArenaHash;
+ u64 Size;
+
+ struct gs_debug_memory_allocation* Next;
+} gs_debug_memory_allocation;
+
+typedef struct gs_debug_arena_info
+{
+ char* ArenaName;
+ u64 AllocationsCount;
+ u64 TotalSize;
+} gs_debug_arena_info;
+
+typedef struct gs_debug_allocations_list
+{
+ gs_debug_memory_allocation* Root;
+ gs_debug_memory_allocation* Head;
+ u64 AllocationsSizeTotal;
+ u64 AllocationsCount;
+
+ u64 ArenaHashesCount;
+ u64* ArenaHashes;
+ gs_debug_arena_info* ArenaInfos;
+} gs_debug_allocations_list;
+
+//
+// Allocator
+//
+
+#define PLATFORM_ALLOC(name) void* name(u64 Size, u64* ResultSize, u8* UserData)
+typedef PLATFORM_ALLOC(platform_alloc);
+
+#define PLATFORM_FREE(name) void name(void* Base, u64 Size, u8* UserData)
+typedef PLATFORM_FREE(platform_free);
+
+typedef struct gs_allocator
+{
+ platform_alloc* PAlloc;
+ platform_free* PFree;
+ u8* UserData;
+
+ gs_debug_allocations_list* DEBUGAllocList;
+} gs_allocator;
+
+PLATFORM_ALLOC(AllocNoOp)
+{
+ GS_MEMORY_PROFILE_FUNC;
+ if (ResultSize) *ResultSize = 0;
+ return 0;
+}
+
+PLATFORM_FREE(FreeNoOp)
+{
+ GS_MEMORY_PROFILE_FUNC;
+ return;
+}
+
+internal u64
+GSMemoryHash(char* Str, u64 Len)
+{
+ u64 Hash = 5381;
+ for (u32 i = 0; i < Len; i++)
+ {
+ Hash = ((Hash << 5) + Hash) + Str[i];
+ }
+ return Hash;
+}
+
+#define Alloc(a,s,an) Alloc_((a),(s),DEBUG_LOC, (char*)(an)).Memory
+#define AllocStruct(a,t,an) (t*)Alloc_((a),sizeof(t),DEBUG_LOC, (char*)(an)).Memory
+#define AllocArray(a,t,c,an) (t*)Alloc_((a),sizeof(t)*(c),DEBUG_LOC, (char*)(an)).Memory
+#define AllocData(a,s,an) Alloc_((a),(s),DEBUG_LOC, (char*)(an))
+
+internal gs_data
+Alloc_(gs_allocator A, u64 Size, gs_debug_loc Loc, char* ArenaName)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ gs_data Result = {};
+ Result.Memory = (u8*)A.PAlloc(Size, &Result.Size, A.UserData);
+
+ // Debug
+ {
+ u64 ArenaHash = GSMemoryHash(ArenaName, CStringLength(ArenaName));
+
+ gs_debug_memory_allocation* Allocation = (gs_debug_memory_allocation*)A.PAlloc(sizeof(gs_debug_memory_allocation*), 0, A.UserData);
+ Allocation->Loc = Loc;
+ Allocation->Size = Size;
+ Allocation->ArenaHash = ArenaHash;
+ SLLPushOrInit(A.DEBUGAllocList->Root, A.DEBUGAllocList->Head, Allocation);
+
+ A.DEBUGAllocList->AllocationsSizeTotal += Size;
+ A.DEBUGAllocList->AllocationsCount += 1;
+
+ u64 ArenaStartIndex = ArenaHash % A.DEBUGAllocList->ArenaHashesCount;
+ u64 ArenaIndex = ArenaStartIndex;
+ while (A.DEBUGAllocList->ArenaHashes[ArenaIndex] != 0 &&
+ A.DEBUGAllocList->ArenaHashes[ArenaIndex] != ArenaHash)
+ {
+ ArenaIndex = (ArenaIndex + 1) % A.DEBUGAllocList->ArenaHashesCount;
+
+ // NOTE(PS): this means you've created enough arena's to fill up every
+ // slot in the arena hash table. Go increase the size of of DEBUG_ALLOCATOR_ARENA_MAX
+ Assert(ArenaIndex != ArenaStartIndex);
+ }
+
+ if (A.DEBUGAllocList->ArenaHashes[ArenaIndex] == 0)
+ {
+ A.DEBUGAllocList->ArenaHashes[ArenaIndex] = ArenaHash;
+
+ gs_debug_arena_info AI = {};
+ AI.ArenaName = ArenaName;
+ A.DEBUGAllocList->ArenaInfos[ArenaIndex] = AI;
+ }
+
+ A.DEBUGAllocList->ArenaInfos[ArenaIndex].AllocationsCount += 1;
+ A.DEBUGAllocList->ArenaInfos[ArenaIndex].TotalSize += Size;
+ }
+
+
+
+ return Result;
+}
+
+#define AllocString(a,s,l) AllocString_((a), (s), DEBUG_LOC, (l))
+internal gs_string
+AllocString_(gs_allocator A, u64 Size, gs_debug_loc Loc, char* LocString)
+{
+ gs_string Result = {};
+ Result.Str = (char*)Alloc_(A, sizeof(char)*Size, Loc, LocString).Memory;
+ Result.Length = 0;
+ Result.Size = Size;
+ return Result;
+}
+
+#define Free(a,b,s) Free_((a),(b),(s),DEBUG_LOC)
+#define FreeStruct(a,b,t) Free_((a),(u8*)(b),sizeof(t),DEBUG_LOC)
+#define FreeArray(a,b,t,c) Free_((a),(u8*)(b),sizeof(t)*(c),DEBUG_LOC)
+internal void
+Free_(gs_allocator A, u8* Base, u64 Size, gs_debug_loc Loc)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ A.PFree(Base, Size, A.UserData);
+}
+
+// NOTE(PS): cast function and struct pointers to proper data types
+// for convenience
+#define AllocatorCreate(a,f,u) AllocatorCreate_((platform_alloc*)(a),(platform_free*)(f),(u8*)(u))
+internal gs_allocator
+AllocatorCreate_(platform_alloc* PAlloc, platform_free* PFree, u8* UserData)
+{
+ gs_allocator Result = {};
+ Result.PAlloc = PAlloc;
+ Result.PFree = PFree;
+ Result.UserData = UserData;
+
+ if (!PAlloc) Result.PAlloc = AllocNoOp;
+ if (!PFree) Result.PFree = FreeNoOp;
+
+ // @DEBUG
+#define DEBUG_ALLOCATOR_ARENA_MAX 256
+ Result.DEBUGAllocList = (gs_debug_allocations_list*)PAlloc(sizeof(gs_debug_allocations_list*), 0, UserData);
+
+ Result.DEBUGAllocList->ArenaHashesCount = DEBUG_ALLOCATOR_ARENA_MAX;
+ Result.DEBUGAllocList->ArenaHashes = (u64*)PAlloc(sizeof(u64*) * DEBUG_ALLOCATOR_ARENA_MAX, 0, UserData);
+ Result.DEBUGAllocList->ArenaInfos = (gs_debug_arena_info*)PAlloc(sizeof(gs_debug_arena_info) * DEBUG_ALLOCATOR_ARENA_MAX, 0, UserData);
+ return Result;
+}
+
+//
+// Memory Cursor
+//
+
+typedef struct gs_memory_cursor
+{
+ gs_data Data;
+ u64 Position;
+} gs_memory_cursor;
+
+internal gs_memory_cursor
+MemoryCursorCreate(u8* Base, u64 Size)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ gs_memory_cursor Result = {};
+ Result.Data.Memory = Base;
+ Result.Data.Size = Size;
+ Result.Position = 0;
+
+ return Result;
+}
+internal gs_memory_cursor
+MemoryCursorCreateFromData(gs_data Data)
+{
+ GS_MEMORY_PROFILE_FUNC;
+ return MemoryCursorCreate(Data.Memory, Data.Size);
+}
+
+internal u64
+MemoryCursorRoomLeft(gs_memory_cursor Cursor)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ u64 Result = 0;
+ if (Cursor.Data.Size >= Cursor.Position)
+ {
+ Result = Cursor.Data.Size - Cursor.Position;
+ }
+ return Result;
+}
+
+internal bool
+MemoryCursorHasRoom(gs_memory_cursor Cursor)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ u64 RoomLeft = MemoryCursorRoomLeft(Cursor);
+ bool Result = RoomLeft > 0;
+ return Result;
+}
+
+internal bool
+MemoryCursorCanPush(gs_memory_cursor Cursor, u64 Size)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ u64 RoomLeft = MemoryCursorRoomLeft(Cursor);
+ bool Result = RoomLeft >= Size;
+ return Result;
+}
+
+#define MemoryCursorPushSize(c,s) MemoryCursorPushSize_((c),(s))
+#define MemoryCursorPushStruct(c,s) (s*)MemoryCursorPushSize_((c),sizeof(s)).Memory
+#define MemoryCursorPushArray(c,s,l) (s*)MemoryCursorPushSize_((c),sizeof(s)*(l)).Memory
+internal gs_data
+MemoryCursorPushSize_(gs_memory_cursor* C, u64 Size)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ gs_data Result = {0};
+ if (MemoryCursorCanPush(*C, Size))
+ {
+ Result.Memory = C->Data.Memory + C->Position;
+ Result.Size = Size,
+ C->Position += Size;
+ }
+ return Result;
+}
+
+internal gs_data
+MemoryCursorAlign(gs_memory_cursor* C, u64 Alignment)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ u64 Position = RoundUpTo64(C->Position, Alignment);
+ if (Position > C->Data.Size)
+ {
+ Position = C->Data.Size;
+ }
+ u64 AlignmentDist = Position - C->Position;
+ gs_data Result = MemoryCursorPushSize_(C, AlignmentDist);
+ return Result;
+}
+
+#define MemoryCursorWriteValue(c,t,v) *PushStructOnCursor((c),(t)) = (v)
+internal void
+MemoryCursorWriteBuffer(gs_memory_cursor* C, gs_data Buffer)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ gs_data Dest = MemoryCursorPushSize(C, Buffer.Size);
+ if (Dest.Size == Buffer.Size)
+ {
+ CopyMemoryTo(Buffer.Memory, Dest.Memory, Buffer.Size);
+ }
+}
+
+internal void
+MemoryCursorPopSize(gs_memory_cursor* C, u64 Size)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ u64 SizeToPop = Size;
+ if (SizeToPop > C->Position)
+ {
+ SizeToPop = C->Position;
+ }
+
+ C->Position -= SizeToPop;
+}
+
+internal void
+MemoryCursorReset(gs_memory_cursor* C)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ C->Position = 0;
+}
+
+//
+// Memory Arena
+//
+
+typedef struct gs_memory_cursor_sll
+{
+ gs_memory_cursor Cursor;
+ struct gs_memory_cursor_sll* Next;
+} gs_memory_cursor_sll;
+
+typedef struct gs_memory_arena
+{
+ u64 ChunkSize;
+ u64 Alignment;
+ char* ArenaName;
+
+ gs_memory_cursor_sll* CursorsRoot;
+ gs_memory_cursor_sll* CursorsHead;
+
+ struct gs_memory_arena* Parent;
+ gs_allocator Allocator;
+ u8* UserData;
+ // TODO: some sort of GrowArena function
+} gs_memory_arena;
+
+internal gs_memory_arena
+MemoryArenaCreate(u64 ChunkSize, u64 Alignment, gs_allocator Allocator, gs_memory_arena* Parent, u8* UserData, char* Name)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ gs_memory_arena Result = {};
+ Result.ChunkSize = ChunkSize;
+ Result.Alignment = Alignment;
+ Result.Allocator = Allocator;
+ Result.Parent = Parent;
+ Result.UserData = UserData;
+ Result.ArenaName = Name;
+
+ return Result;
+}
+
+internal gs_data PushSize_(gs_memory_arena* Arena, u64 Size, gs_debug_loc Loc);
+
+internal gs_memory_cursor*
+MemoryArenaPushCursor(gs_memory_arena* Arena, u64 MinSize, gs_debug_loc Loc)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ gs_memory_cursor* Result = 0;
+
+ u64 CursorSize = MinSize;
+ if (CursorSize < Arena->ChunkSize)
+ {
+ CursorSize = Arena->ChunkSize;
+ }
+ u64 AllocSize = CursorSize + sizeof(gs_memory_cursor_sll);
+
+ gs_data CursorMemory = {0};
+ if (Arena->Parent)
+ {
+ CursorMemory = PushSize_(Arena->Parent, AllocSize, Loc);
+ } else if (Arena->UserData) {
+ // TODO(PS): implement custom MemoryArenaAllocCursor functionality
+ InvalidCodePath;
+ } else {
+ Assert(Arena->Allocator.PAlloc);
+ CursorMemory = Alloc_(Arena->Allocator, AllocSize, Loc, Arena->ArenaName);
+ }
+
+ gs_memory_cursor_sll* CursorEntry = (gs_memory_cursor_sll*)CursorMemory.Memory;
+ if (!Arena->CursorsRoot)
+ {
+ Arena->CursorsRoot = CursorEntry;
+ }
+ if (Arena->CursorsHead)
+ {
+ Arena->CursorsHead->Next = CursorEntry;
+ }
+ Arena->CursorsHead = CursorEntry;
+
+ u8* CursorBase = (u8*)(CursorEntry + 1);
+ CursorEntry->Cursor = MemoryCursorCreate(CursorBase, CursorSize);
+ Result = &CursorEntry->Cursor;
+
+ return Result;
+}
+
+#define PushSize(a,s) PushSize_((a), (s), DEBUG_LOC)
+#define PushStruct(a,t) (t*)PushSize_((a), sizeof(t), DEBUG_LOC).Memory
+#define PushArray(a,t,c) (t*)PushSize_((a), sizeof(t) * (c), DEBUG_LOC).Memory
+#define PushString(a,c) gs_string{ PushArray((a),char,(c)), 0, (c) }
+
+internal gs_data
+PushSize_(gs_memory_arena* Arena, u64 Size, gs_debug_loc Loc)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ gs_data Result = {0};
+ if (Size > 0)
+ {
+ gs_memory_cursor* Cursor = 0;
+ for (gs_memory_cursor_sll* C = Arena->CursorsRoot;
+ C != 0;
+ C = C->Next)
+ {
+ if (MemoryCursorCanPush(C->Cursor, Size))
+ {
+ Cursor = &C->Cursor;
+ break;
+ }
+ }
+
+ // NOTE(PS): We didn't find a cursor with enough room
+ // for the allocation being requested
+ if (!Cursor)
+ {
+ Cursor = MemoryArenaPushCursor(Arena, Size, Loc);
+ }
+ Assert(Cursor);
+ Assert(MemoryCursorCanPush(*Cursor, Size));
+
+ Result = MemoryCursorPushSize(Cursor, Size);
+
+ gs_data Alignment = MemoryCursorAlign(Cursor, Arena->Alignment);
+ Result.Size += Alignment.Size;
+ }
+ return Result;
+}
+
+internal void
+MemoryArenaClear(gs_memory_arena* Arena)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ for (gs_memory_cursor_sll* C = Arena->CursorsRoot;
+ C != 0;
+ C = C->Next)
+ {
+ MemoryCursorReset(&C->Cursor);
+ }
+}
+
+internal void
+MemoryArenaFree(gs_memory_arena* Arena)
+{
+ GS_MEMORY_PROFILE_FUNC;
+
+ // NOTE(PS): If this isn't a base arena, we can't
+ // really free it.
+ // TODO(PS): Once we have the User Specified codepaths
+ // in, we can probably provide a way for the user to
+ // let us free a custom allocator
+ Assert(Arena->Allocator.PFree);
+
+ gs_allocator A = Arena->Allocator;
+ gs_memory_cursor_sll* At = Arena->CursorsRoot;
+ while (At)
+ {
+ gs_memory_cursor_sll* NextAt = At->Next;
+
+ u64 Size = At->Cursor.Data.Size + sizeof(gs_memory_cursor_sll);
+ Free(A, (u8*)At, Size);
+
+ At = NextAt;
+ }
+
+ Arena->CursorsRoot = 0;
+ Arena->CursorsHead = 0;
+}
+
+#ifdef GS_PLATFORM_IMPLEMENTATION
+
+internal gs_allocator CreatePlatformAllocator();
+
+# if PLATFORM_WINDOWS
+# include "./gs_memory_win32.h"
+# elif PLATFORM_OSX
+# include "./gs_memory_osx.h"
+# elif PLATFORM_LINUX
+# include "./gs_memory_linux.h"
+# endif
+
+#endif
+
+#endif //GS_MEMORY_H
diff --git a/src/gs_libs/gs_memory_arena.h b/src/gs_libs/gs_memory_arena.h
index 4fbeb3b..e4bad68 100644
--- a/src/gs_libs/gs_memory_arena.h
+++ b/src/gs_libs/gs_memory_arena.h
@@ -153,13 +153,13 @@ typedef unsigned int gs_mem_u32;
typedef unsigned long long int gs_mem_u64;
-#ifdef DEBUG
+#if DEBUG
#if !defined(GSMem_Assert)
#define GSMem_Assert(expression) \
if((expression)) { \
}else{ \
- volatile int* p = 0; \
- *p = 5; \
+volatile int* p = 0; \
+*p = 5; \
}
#endif
diff --git a/src/gs_libs/gs_path.h b/src/gs_libs/gs_path.h
new file mode 100644
index 0000000..ebcbb7b
--- /dev/null
+++ b/src/gs_libs/gs_path.h
@@ -0,0 +1,106 @@
+//
+// File: gs_path.h
+// Author: Peter Slattery
+// Creation Date: 2021-03-06
+//
+#ifndef GS_PATH_H
+
+internal gs_const_string
+ClearString()
+{
+ gs_const_string R = {};
+ R.Str = 0;
+ R.Length = 0;
+ return R;
+}
+
+internal void
+SanitizePath (gs_const_string Path, gs_string* Dest, gs_memory_arena* Scratch)
+{
+ Dest->Length = 0;
+
+ // Put all slashes in the same format
+ s32 SlashCount = 0;
+ for (u64 i = 0; i < Path.Length; i++)
+ {
+ char At = Path.Str[i];
+ if (At == '\\' || At == '/') {
+ SlashCount += 1;
+ }
+ }
+
+ // we add one to slash count in case the last element is a file or
+ // doesn't end in a slash (even if it should)
+ u32 PathEleCountMax = SlashCount + 1;
+ u32 PathEleCount = 0;
+ gs_const_string* PathEle = PushArray(Scratch, gs_const_string, PathEleCountMax);
+
+ u64 OnePastLastEleEnd = 0;
+ for (s64 i = 0; i < (s64)Path.Length; i++)
+ {
+ char At = Path.Str[i];
+ if (At == '\\' || At == '/')
+ {
+ gs_const_string* NewEle = PathEle + PathEleCount++;
+ *NewEle = Substring(Path, OnePastLastEleEnd, i + 1);
+ OnePastLastEleEnd = i + 1;
+ }
+ }
+
+ if (OnePastLastEleEnd != Path.Length)
+ {
+ gs_const_string* NewEle = PathEle + PathEleCount++;
+ *NewEle = Substring(Path, OnePastLastEleEnd, Path.Length);
+ OnePastLastEleEnd = Path.Length;
+ }
+
+ // Remove redundant elements
+ for (u32 i = 0; i < PathEleCount; i++)
+ {
+ gs_const_string* At = PathEle + i;
+ bool ShouldRemove = false;
+ if (i != 0)
+ {
+ if (StringsEqual(*At, ConstString(".\\")) ||
+ StringsEqual(*At, ConstString("./")))
+ {
+ *At = ClearString();
+ }
+ else if (StringsEqual(*At, ConstString("..\\")) ||
+ StringsEqual(*At, ConstString("../")))
+ {
+ PathEle[i - 1] = ClearString();
+ if (i != 1) {
+ PathEle[i] = ClearString();
+ }
+ }
+ }
+ }
+
+ for (u32 i = 0; i < PathEleCount; i++)
+ {
+ if (PathEle[i].Str) {
+ AppendPrintF(Dest, "%S", PathEle[i]);
+ }
+ }
+
+ // Put all slashes in the same format
+ for (u64 i = 0; i < Dest->Length; i++)
+ {
+ if (Dest->Str[i] == '/') {
+ Dest->Str[i] = '\\';
+ }
+ }
+}
+
+internal gs_const_string
+SanitizePath (gs_const_string Path, gs_memory_arena* Scratch)
+{
+ gs_string Result = PushString(Scratch, Path.Length);
+ SanitizePath(Path, &Result, Scratch);
+ return Result.ConstString;
+}
+
+
+#define GS_PATH_H
+#endif // GS_PATH_H
\ No newline at end of file
diff --git a/src/gs_libs/gs_radix_sort.h b/src/gs_libs/gs_radix_sort.h
index 8e62a27..cf4464d 100644
--- a/src/gs_libs/gs_radix_sort.h
+++ b/src/gs_libs/gs_radix_sort.h
@@ -1,12 +1,11 @@
/*
gs_radix_sort.h - An implementation of radix sort for fixed size unsigned 32bit integer buffers
-TODO
*/
#ifndef GS_RADIX_SORT_H
-#ifdef DEBUG
+#if DEBUG
#if !defined(GSRad_Assert)
#define GSRad_Assert(expression) \
if(!(expression)) { \
diff --git a/src/gs_libs/gs_tests.h b/src/gs_libs/gs_tests.h
new file mode 100644
index 0000000..7a38fb5
--- /dev/null
+++ b/src/gs_libs/gs_tests.h
@@ -0,0 +1,72 @@
+//
+// File: gs_tests.h
+// Author: Peter Slattery
+// Creation Date: 2021-03-06
+//
+#ifndef GS_TESTS_H
+
+int CStringLen(char* Str)
+{
+ char* At = Str;
+ while (*At != 0) { At++; }
+ int Result = At - Str;
+ return Result;
+}
+
+struct test_ctx
+{
+ int TestsCount;
+ int TestsPassedCount;
+};
+
+static test_ctx TestCtx = {0};
+
+static void
+BeginTest(char* Name)
+{
+ int Length = CStringLen(Name);
+ int Spaces = 25 - Length;
+ if(Spaces < 0)
+ {
+ Spaces = 0;
+ }
+ printf("\"%s\" %.*s [", Name, Spaces, "------------------------------");
+ TestCtx.TestsCount = 0;
+ TestCtx.TestsPassedCount = 0;
+}
+
+static void
+TestResult(bool Result)
+{
+ TestCtx.TestsCount += 1;
+ if (Result) {
+ TestCtx.TestsPassedCount += 1;
+ }
+ printf(Result ? "." : "X");
+}
+
+static void
+EndTest(void)
+{
+ int Spaces = 10 - TestCtx.TestsCount;
+ if(Spaces < 0) { Spaces = 0; }
+ printf("]%.*s ", Spaces, " ");
+ printf("[%i/%i] %i passed, %i tests, ",
+ TestCtx.TestsPassedCount, TestCtx.TestsCount,
+ TestCtx.TestsPassedCount, TestCtx.TestsCount);
+ if(TestCtx.TestsCount == TestCtx.TestsPassedCount)
+ {
+ printf("SUCCESS ( )");
+ }
+ else
+ {
+ printf("FAILED (X)");
+ }
+ printf("\n");
+}
+
+#define Test(name) for(int _i_ = (BeginTest(name), 0); !_i_; _i_ += 1, EndTest())
+
+
+#define GS_TESTS_H
+#endif // GS_TESTS_H
\ No newline at end of file
diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp
index 197c270..bdf12c4 100644
--- a/src/gs_libs/gs_types.cpp
+++ b/src/gs_libs/gs_types.cpp
@@ -9,18 +9,18 @@
internal gs_data
StructToData_(u8* Memory, u64 Size)
{
- gs_data Result = {0};
- Result.Memory = Memory;
- Result.Size = Size;
- return Result;
+ gs_data Result = {0};
+ Result.Memory = Memory;
+ Result.Size = Size;
+ return Result;
}
internal u32
U32DivideRoundUp (u32 A, u32 B)
{
- r32 Result = (r32)A / (r32)B;
- Result += .99999f;
- return (u32)Result;
+ r32 Result = (r32)A / (r32)B;
+ Result += .99999f;
+ return (u32)Result;
}
inline bool XOR(bool A, bool B) { return (A == !B); }
@@ -31,495 +31,495 @@ inline bool XOR(b64 A, b64 B) { return (A == !B); }
internal u32
RoundUpToMultiple(u32 Value, u32 MultipleOf)
{
- u32 Result = Value;
- if (MultipleOf != 0)
- {
- u32 Remainder = Value % MultipleOf;
- Result = Value + (MultipleOf - Remainder);
- }
- return Result;
+ u32 Result = Value;
+ if (MultipleOf != 0)
+ {
+ u32 Remainder = Value % MultipleOf;
+ Result = Value + (MultipleOf - Remainder);
+ }
+ return Result;
}
internal u32
RoundUpToPow2U32(u32 Value)
{
- u32 Result = Value - 1;
- Result |= Result >> 1;
- Result |= Result >> 2;
- Result |= Result >> 4;
- Result |= Result >> 8;
- Result |= Result >> 16;
- Result++;
- return Result;
+ u32 Result = Value - 1;
+ Result |= Result >> 1;
+ Result |= Result >> 2;
+ Result |= Result >> 4;
+ Result |= Result >> 8;
+ Result |= Result >> 16;
+ Result++;
+ return Result;
}
internal u32
RoundUpToPow2U64(u64 Value)
{
- u64 Result = Value - 1;
- Result |= Result >> 1;
- Result |= Result >> 2;
- Result |= Result >> 4;
- Result |= Result >> 8;
- Result |= Result >> 16;
- Result |= Result >> 32;
- Result++;
- return Result;
+ u64 Result = Value - 1;
+ Result |= Result >> 1;
+ Result |= Result >> 2;
+ Result |= Result >> 4;
+ Result |= Result >> 8;
+ Result |= Result >> 16;
+ Result |= Result >> 32;
+ Result++;
+ return Result;
}
internal u64
RoundUpTo64(u64 Value, u64 Alignment)
{
- Value += Alignment - 1;
- Value -= Value % Alignment;
- return Value;
+ Value += Alignment - 1;
+ Value -= Value % Alignment;
+ return Value;
}
internal u8
PowU8(u8 X, u32 Power)
{
- u8 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ u8 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal u16
PowU16(u16 X, u32 Power)
{
- u16 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ u16 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal u32
PowU32(u32 X, u32 Power)
{
- u32 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ u32 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal u64
PowU64(u64 X, u32 Power)
{
- u64 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ u64 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal s8
PowS8(s8 X, u32 Power)
{
- s8 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ s8 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal s16
PowS16(s16 X, u32 Power)
{
- s16 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ s16 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal s32
PowS32(s32 X, u32 Power)
{
- s32 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ s32 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal s64
PowS64(s64 X, u32 Power)
{
- s64 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ s64 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal r32
PowR32(r32 X, u32 Power)
{
- r32 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ r32 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal r64
PowR64(r64 X, u32 Power)
{
- r64 Result = X;
- for (u32 i = 1; i < Power; i++) { Result *= X; }
- return Result;
+ r64 Result = X;
+ for (u32 i = 1; i < Power; i++) { Result *= X; }
+ return Result;
}
internal u8
LerpU8(r32 T, u8 A, u8 B)
{
- return (u8)((A * (1.0f - T)) + (B * T));
+ return (u8)((A * (1.0f - T)) + (B * T));
}
internal u16
LerpU16(r32 T, u16 A, u16 B)
{
- return (u16)((A * (1.0f - T)) + (B * T));
+ return (u16)((A * (1.0f - T)) + (B * T));
}
internal u32
LerpU32(r32 T, u32 A, u32 B)
{
- return (u32)((A * (1.0f - T)) + (B * T));
+ return (u32)((A * (1.0f - T)) + (B * T));
}
internal u64
LerpU64(r32 T, u64 A, u64 B)
{
- return (u64)((A * (1.0f - T)) + (B * T));
+ return (u64)((A * (1.0f - T)) + (B * T));
}
internal s8
LerpS8(r32 T, s8 A, s8 B)
{
- return (s8)((A * (1.0f - T)) + (B * T));
+ return (s8)((A * (1.0f - T)) + (B * T));
}
internal s16
LerpS16(r32 T, s16 A, s16 B)
{
- return (s16)((A * (1.0f - T)) + (B * T));
+ return (s16)((A * (1.0f - T)) + (B * T));
}
internal s32
LerpS32(r32 T, s32 A, s32 B)
{
- return (s32)((A * (1.0f - T)) + (B * T));
+ return (s32)((A * (1.0f - T)) + (B * T));
}
internal s64
LerpS64(r32 T, s64 A, s64 B)
{
- return (s64)((A * (1.0f - T)) + (B * T));
+ return (s64)((A * (1.0f - T)) + (B * T));
}
internal r32
LerpR32(r32 T, r32 A, r32 B)
{
- return (r32)((A * (1.0f - T)) + (B * T));
+ return (r32)((A * (1.0f - T)) + (B * T));
}
internal r64
LerpR64(r32 T, r64 A, r64 B)
{
- return (r64)((A * (1.0f - T)) + (B * T));
+ return (r64)((A * (1.0f - T)) + (B * T));
}
internal u8
UnlerpU8(u8 Value, u8 Min, u8 Max)
{
- return (u8)((r64)(Value - Min) / (r64)(Max - Min));
+ return (u8)((r64)(Value - Min) / (r64)(Max - Min));
}
internal u16
UnlerpU16(u16 Value, u16 Min, u16 Max)
{
- return (u16)((r64)(Value - Min) / (r64)(Max - Min));
+ return (u16)((r64)(Value - Min) / (r64)(Max - Min));
}
internal u32
UnlerpU32(u32 Value, u32 Min, u32 Max)
{
- return (u32)((r64)(Value - Min) / (r64)(Max - Min));
+ return (u32)((r64)(Value - Min) / (r64)(Max - Min));
}
internal u64
UnlerpU64(u64 Value, u64 Min, u64 Max)
{
- return (u64)((r64)(Value - Min) / (r64)(Max - Min));
+ return (u64)((r64)(Value - Min) / (r64)(Max - Min));
}
internal s8
UnlerpS8(s8 Value, s8 Min, s8 Max)
{
- return (s8)((r64)(Value - Min) / (r64)(Max - Min));
+ return (s8)((r64)(Value - Min) / (r64)(Max - Min));
}
internal s16
UnlerpS16(s16 Value, s16 Min, s16 Max)
{
- return (s16)((r64)(Value - Min) / (r64)(Max - Min));
+ return (s16)((r64)(Value - Min) / (r64)(Max - Min));
}
internal s32
UnlerpS32(s32 Value, s32 Min, s32 Max)
{
- return (s32)((r64)(Value - Min) / (r64)(Max - Min));
+ return (s32)((r64)(Value - Min) / (r64)(Max - Min));
}
internal s64
UnlerpS64(s64 Value, s64 Min, s64 Max)
{
- return (s64)((r64)(Value - Min) / (r64)(Max - Min));
+ return (s64)((r64)(Value - Min) / (r64)(Max - Min));
}
internal r32
UnlerpR32(r32 Value, r32 Min, r32 Max)
{
- return (Value - Min) / (Max - Min);
+ return (Value - Min) / (Max - Min);
}
internal r64
UnlerpR64(r64 Value, r64 Min, r64 Max)
{
- return (Value - Min) / (Max - Min);
+ return (Value - Min) / (Max - Min);
}
internal u8
RemapU8(u8 Value, u8 OldMin, u8 OldMax, u8 NewMin, u8 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- u8 Result = (u8)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ u8 Result = (u8)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal u16
RemapU16(u16 Value, u16 OldMin, u16 OldMax, u16 NewMin, u16 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- u16 Result = (u16)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ u16 Result = (u16)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal u32
RemapU32(u32 Value, u32 OldMin, u32 OldMax, u32 NewMin, u32 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- u32 Result = (u32)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ u32 Result = (u32)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal u64
RemapU64(u64 Value, u64 OldMin, u64 OldMax, u64 NewMin, u64 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- u64 Result = (u64)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ u64 Result = (u64)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal s8
RemapS8(s8 Value, s8 OldMin, s8 OldMax, s8 NewMin, s8 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- s8 Result = (s8)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ s8 Result = (s8)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal s16
RemapS16(s16 Value, s16 OldMin, s16 OldMax, s16 NewMin, s16 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- s16 Result = (s16)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ s16 Result = (s16)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal s32
RemapS32(s32 Value, s32 OldMin, s32 OldMax, s32 NewMin, s32 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- s32 Result = (s32)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ s32 Result = (s32)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal s64
RemapS64(s64 Value, s64 OldMin, s64 OldMax, s64 NewMin, s64 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- s64 Result = (s64)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ s64 Result = (s64)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal r32
RemapR32(r32 Value, r32 OldMin, r32 OldMax, r32 NewMin, r32 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r32 Result = (r32)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r32 Result = (r32)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal r64
RemapR64(r64 Value, r64 OldMin, r64 OldMax, r64 NewMin, r64 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 Result = (r64)((A * (NewMax - NewMin)) + NewMin);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 Result = (r64)((A * (NewMax - NewMin)) + NewMin);
+ return Result;
}
internal u8
RemapClampedU8(u8 Value, u8 OldMin, u8 OldMax, u8 NewMin, u8 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- u8 Result = (u8)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ u8 Result = (u8)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal u16
RemapClampedU16(u16 Value, u16 OldMin, u16 OldMax, u16 NewMin, u16 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- u16 Result = (u16)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ u16 Result = (u16)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal u32
RemapClampedU32(u32 Value, u32 OldMin, u32 OldMax, u32 NewMin, u32 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- u32 Result = (u32)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ u32 Result = (u32)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal u64
RemapClampedU64(u64 Value, u64 OldMin, u64 OldMax, u64 NewMin, u64 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- u64 Result = (u64)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ u64 Result = (u64)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal s8
RemapClampedS8(s8 Value, s8 OldMin, s8 OldMax, s8 NewMin, s8 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- s8 Result = (s8)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ s8 Result = (s8)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal s16
RemapClampedS16(s16 Value, s16 OldMin, s16 OldMax, s16 NewMin, s16 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- s16 Result = (s16)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ s16 Result = (s16)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal s32
RemapClampedS32(s32 Value, s32 OldMin, s32 OldMax, s32 NewMin, s32 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- s32 Result = (s32)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ s32 Result = (s32)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal s64
RemapClampedS64(s64 Value, s64 OldMin, s64 OldMax, s64 NewMin, s64 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- s64 Result = (s64)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ s64 Result = (s64)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal r32
RemapClampedR32(r32 Value, r32 OldMin, r32 OldMax, r32 NewMin, r32 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- r32 Result = (r32)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ r32 Result = (r32)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal r64
RemapClampedR64(r64 Value, r64 OldMin, r64 OldMax, r64 NewMin, r64 NewMax)
{
- r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
- r64 AClamped = Clamp01(A);
- r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
- r64 Result = (r64)Clamp(NewMin, UnclampedResult, NewMax);
- return Result;
+ r64 A = (r64)(Value - OldMin) / (r64)(OldMax - OldMin);
+ r64 AClamped = Clamp01(A);
+ r64 UnclampedResult = ((AClamped * (NewMax - NewMin)) + NewMin);
+ r64 Result = (r64)Clamp(NewMin, UnclampedResult, NewMax);
+ return Result;
}
internal r32
FloorR32(r32 V)
{
- return (r32)((s64)V);
+ return (r32)((s64)V);
}
internal r64
FloorR64(r64 V)
{
- return (r64)((s64)V);
+ return (r64)((s64)V);
}
internal r32
FractR32(r32 V)
{
- return V - FloorR32(V);
+ return V - FloorR32(V);
}
internal r64
FractR64(r64 V)
{
- return V - FloorR64(V);
+ return V - FloorR64(V);
}
internal r32
SqrtR32(r32 V)
{
- return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(V)));
+ return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(V)));
}
internal u32
SqrtU32(u32 V)
{
- return sqrt(V);
+ return sqrt(V);
}
internal r32
ModR32(r32 Value, r32 Int)
{
- r32 Div = Value / Int;
- r32 Fract = Abs(FractR32(Div));
- return Int * Fract;
+ r32 Div = Value / Int;
+ r32 Fract = Abs(FractR32(Div));
+ return Int * Fract;
}
internal r64
ModR64(r64 Value, r64 Int)
{
- r64 Div = Value / Int;
- r64 Fract = Abs(FractR64(Div));
- return Int * Fract;
+ r64 Div = Value / Int;
+ r64 Fract = Abs(FractR64(Div));
+ return Int * Fract;
}
internal r32
SinR32(r32 Rad)
{
- return sinf(Rad);
+ return sinf(Rad);
}
internal r64
SinR64(r64 Rad)
{
- return sin(Rad);
+ return sin(Rad);
}
internal r32
CosR32(r32 Rad)
{
- return cosf(Rad);
+ return cosf(Rad);
}
internal r64
CosR64(r64 Rad)
{
- return cos(Rad);
+ return cos(Rad);
}
internal r32
TanR32(r32 Rad)
{
- return tanf(Rad);
+ return tanf(Rad);
}
internal r64
TanR64(r64 Rad)
{
- return tan(Rad);
+ return tan(Rad);
}
internal r32
ASinR32(r32 Rad)
{
- return asinf(Rad);
+ return asinf(Rad);
}
internal r64
ASinR64(r64 Rad)
{
- return asin(Rad);
+ return asin(Rad);
}
internal r32
ACosR32(r32 Rad)
{
- return acosf(Rad);
+ return acosf(Rad);
}
internal r64
ACosR64(r64 Rad)
{
- return acos(Rad);
+ return acos(Rad);
}
internal r32
ATanR32(r32 Rad)
{
- return atanf(Rad);
+ return atanf(Rad);
}
internal r64
ATanR64(r64 Rad)
{
- return atan(Rad);
+ return atan(Rad);
}
///////////////////////////
@@ -529,34 +529,34 @@ ATanR64(r64 Rad)
internal v2
V2MultiplyPairwise(v2 A, v2 B)
{
- v2 Result = v2{
- A.x * B.x,
- A.y * B.y,
- };
- return Result;
+ v2 Result = v2{
+ A.x * B.x,
+ A.y * B.y,
+ };
+ return Result;
}
internal v3
V3MultiplyPairwise(v3 A, v3 B)
{
- v3 Result = v3{
- A.x * B.x,
- A.y * B.y,
- A.z * B.z,
- };
- return Result;
+ v3 Result = v3{
+ A.x * B.x,
+ A.y * B.y,
+ A.z * B.z,
+ };
+ return Result;
}
internal v4
V4MultiplyPairwise(v4 A, v4 B)
{
- v4 Result = v4{
- A.x * B.x,
- A.y * B.y,
- A.z * B.z,
- A.w * B.w,
- };
- return Result;
+ v4 Result = v4{
+ A.x * B.x,
+ A.y * B.y,
+ A.z * B.z,
+ A.w * B.w,
+ };
+ return Result;
}
@@ -608,7 +608,7 @@ internal v3 ToV3(v2 V, r32 Z = 0) { return v3{V.x, V.y, Z}; }
internal v4 V2ToV4(v2 V, r32 Z = 0, r32 W = 0) { return v4{V.x, V.y, Z, W}; }
internal v4 ToV4_(v3 V, r32 W)
{
- return v4{V.x, V.y, V.z, W};
+ return v4{V.x, V.y, V.z, W};
}
#define ToV4Point(v) ToV4_((v), 1.0f) // all points have a w value of 1
#define ToV4Vec(v) ToV4_((v), 0.0f) // all vectors have a w value of 0 ie. they cannot be translated
@@ -632,20 +632,20 @@ internal r32 V4Distance(v4 A, v4 B) { return V4Mag(A - B); }
internal v2
V2Normalize(v2 A)
{
- r32 Magnitude = V2Mag(A);
- return A / Magnitude;
+ r32 Magnitude = V2Mag(A);
+ return A / Magnitude;
}
internal v3
V3Normalize(v3 A)
{
- r32 Magnitude = V3Mag(A);
- return A / Magnitude;
+ r32 Magnitude = V3Mag(A);
+ return A / Magnitude;
}
internal v4
V4Normalize(v4 A)
{
- r32 Magnitude = V4Mag(A);
- return A / Magnitude;
+ r32 Magnitude = V4Mag(A);
+ return A / Magnitude;
}
internal r32 V2Dot(v2 A, v2 B) { return ((A.x * B.x) + (A.y * B.y)); }
@@ -658,102 +658,102 @@ internal v2 V2PerpendicularCCW(v2 A) { return v2{A.y, A.x}; }
internal r32
V2Cross(v2 A, v2 B)
{
- return ((A.x * B.y) - (A.y * B.x));
+ return ((A.x * B.y) - (A.y * B.x));
}
internal v3
V3Cross(v3 A, v3 B)
{
- v3 Result = {
- (A.y * B.z) - (A.z * B.y),
- (A.z * B.x) - (A.x * B.z),
- (A.x * B.y) - (A.y * B.x)
- };
- return Result;
+ v3 Result = {
+ (A.y * B.z) - (A.z * B.y),
+ (A.z * B.x) - (A.x * B.z),
+ (A.x * B.y) - (A.y * B.x)
+ };
+ return Result;
}
internal v4
V4Cross(v4 A, v4 B)
{
- v4 Result = {
- (A.y * B.z) - (A.z * B.y),
- (A.z * B.x) - (A.x * B.z),
- (A.x * B.y) - (A.y * B.x),
- 0
- };
- return Result;
+ v4 Result = {
+ (A.y * B.z) - (A.z * B.y),
+ (A.z * B.x) - (A.x * B.z),
+ (A.x * B.y) - (A.y * B.x),
+ 0
+ };
+ return Result;
}
internal v2
V2Lerp(r32 T, v2 A, v2 B)
{
- v2 Result = v2{
- LerpR32(T, A.x, B.x),
- LerpR32(T, A.y, B.y),
- };
- return Result;
+ v2 Result = v2{
+ LerpR32(T, A.x, B.x),
+ LerpR32(T, A.y, B.y),
+ };
+ return Result;
}
internal v3
V3Lerp(r32 T, v3 A, v3 B)
{
- v3 Result = v3{
- LerpR32(T, A.x, B.x),
- LerpR32(T, A.y, B.y),
- LerpR32(T, A.z, B.z),
- };
- return Result;
+ v3 Result = v3{
+ LerpR32(T, A.x, B.x),
+ LerpR32(T, A.y, B.y),
+ LerpR32(T, A.z, B.z),
+ };
+ return Result;
}
internal v4
V4Lerp(r32 T, v4 A, v4 B)
{
- v4 Result = v4{
- LerpR32(T, A.x, B.x),
- LerpR32(T, A.y, B.y),
- LerpR32(T, A.z, B.z),
- LerpR32(T, A.w, B.w),
- };
- return Result;
+ v4 Result = v4{
+ LerpR32(T, A.x, B.x),
+ LerpR32(T, A.y, B.y),
+ LerpR32(T, A.z, B.z),
+ LerpR32(T, A.w, B.w),
+ };
+ return Result;
}
internal v2
V2Remap(v2 P, v2 OldMin, v2 OldMax, v2 NewMin, v2 NewMax)
{
- v2 Result = {0};
- Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x);
- Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y);
- return Result;
+ v2 Result = {0};
+ Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x);
+ Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y);
+ return Result;
}
internal v3
V3Remap(v3 P, v3 OldMin, v3 OldMax, v3 NewMin, v3 NewMax)
{
- v3 Result = {0};
- Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x);
- Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y);
- Result.z = RemapR32(P.z, OldMin.z, OldMax.z, NewMin.z, NewMax.z);
- return Result;
+ v3 Result = {0};
+ Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x);
+ Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y);
+ Result.z = RemapR32(P.z, OldMin.z, OldMax.z, NewMin.z, NewMax.z);
+ return Result;
}
internal v4
V4Remap(v4 P, v4 OldMin, v4 OldMax, v4 NewMin, v4 NewMax)
{
- v4 Result = {0};
- Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x);
- Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y);
- Result.z = RemapR32(P.z, OldMin.z, OldMax.z, NewMin.z, NewMax.z);
- Result.w = RemapR32(P.w, OldMin.w, OldMax.w, NewMin.w, NewMax.w);
- return Result;
+ v4 Result = {0};
+ Result.x = RemapR32(P.x, OldMin.x, OldMax.x, NewMin.x, NewMax.x);
+ Result.y = RemapR32(P.y, OldMin.y, OldMax.y, NewMin.y, NewMax.y);
+ Result.z = RemapR32(P.z, OldMin.z, OldMax.z, NewMin.z, NewMax.z);
+ Result.w = RemapR32(P.w, OldMin.w, OldMax.w, NewMin.w, NewMax.w);
+ return Result;
}
internal v4
V4RemapAsV3(v4 P, v4 OldMin, v4 OldMax, v4 NewMin, v4 NewMax)
{
- v4 Result = {0};
- Result.xyz = V3Remap(P.xyz, OldMin.xyz, OldMax.xyz, NewMin.xyz, NewMax.xyz);
- Result.w = P.w;
- return Result;
+ v4 Result = {0};
+ Result.xyz = V3Remap(P.xyz, OldMin.xyz, OldMax.xyz, NewMin.xyz, NewMax.xyz);
+ Result.w = P.w;
+ return Result;
}
///////////////////////////
@@ -762,47 +762,47 @@ V4RemapAsV3(v4 P, v4 OldMin, v4 OldMax, v4 NewMin, v4 NewMax)
internal rect2 MakeRect2MinDim(v2 Min, v2 Dim)
{
- rect2 Result = {0};
- Result.Min = Min;
- Result.Max = Min + Dim;
- return Result;
+ rect2 Result = {0};
+ Result.Min = Min;
+ Result.Max = Min + Dim;
+ return Result;
}
internal rect2 MakeRect2CenterDim(v2 Center, v2 Dim)
{
- v2 HalfDim = Dim / 2;
- rect2 Result = {0};
- Result.Min = Center - HalfDim;
- Result.Max = Center + HalfDim;
- return Result;
+ v2 HalfDim = Dim / 2;
+ rect2 Result = {0};
+ Result.Min = Center - HalfDim;
+ Result.Max = Center + HalfDim;
+ return Result;
}
internal b32 ValueInRangeR32(r32 Min, r32 Max, r32 V)
{
- return ((V >= Min) && (V <= Max));
+ return ((V >= Min) && (V <= Max));
}
internal b32 ValueInRange1(range1 Range, r32 V)
{
- return ValueInRangeR32(Range.Min, Range.Max, V);
+ return ValueInRangeR32(Range.Min, Range.Max, V);
}
internal b32 ValueInRange2(range2 Range, v2 V)
{
- return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) &&
- ValueInRangeR32(Range.Min.y, Range.Max.y, V.y));
+ return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) &&
+ ValueInRangeR32(Range.Min.y, Range.Max.y, V.y));
}
internal b32 ValueInRange3(range3 Range, v3 V)
{
- return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) &&
- ValueInRangeR32(Range.Min.y, Range.Max.y, V.y) &&
- ValueInRangeR32(Range.Min.z, Range.Max.z, V.z));
+ return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) &&
+ ValueInRangeR32(Range.Min.y, Range.Max.y, V.y) &&
+ ValueInRangeR32(Range.Min.z, Range.Max.z, V.z));
}
internal b32 ValueInRange4(range4 Range, v4 V)
{
- return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) &&
- ValueInRangeR32(Range.Min.y, Range.Max.y, V.y) &&
- ValueInRangeR32(Range.Min.z, Range.Max.z, V.z) &&
- ValueInRangeR32(Range.Min.w, Range.Max.w, V.w));
+ return (ValueInRangeR32(Range.Min.x, Range.Max.x, V.x) &&
+ ValueInRangeR32(Range.Min.y, Range.Max.y, V.y) &&
+ ValueInRangeR32(Range.Min.z, Range.Max.z, V.z) &&
+ ValueInRangeR32(Range.Min.w, Range.Max.w, V.w));
}
#define PointIsInRect(range, point) ValueInRange2((range), (point))
@@ -843,19 +843,19 @@ internal range4 Range4Offset(range4 Range, v4 Delta) { return range4{ Range.Min
internal v2 RectTopLeft(rect2 Rect)
{
- return v2{ Rect.Min.x, Rect.Max.y };
+ return v2{ Rect.Min.x, Rect.Max.y };
}
internal v2 RectTopRight(rect2 Rect)
{
- return Rect.Max;
+ return Rect.Max;
}
internal v2 RectBottomLeft(rect2 Rect)
{
- return Rect.Min;
+ return Rect.Min;
}
internal v2 RectBottomRight(rect2 Rect)
{
- return v2{ Rect.Max.x, Rect.Min.y };
+ return v2{ Rect.Max.x, Rect.Min.y };
}
internal r32 AspectRatio(r32 Width, r32 Height) { return Width / Height; }
@@ -864,143 +864,143 @@ internal r32 RectAspectRatio(rect2 Rect) { return Range2SizeX(Rect) / Range2Size
internal void
RectHSplit(rect2 Rect, r32 YValue, rect2* Top, rect2* Bottom)
{
- r32 ClampedYValue = Clamp(Rect.Min.y, YValue, Rect.Max.y);
- Top->Max = Rect.Max;
- Top->Min = { Rect.Min.x, ClampedYValue };
- Bottom->Max = { Rect.Max.x, ClampedYValue };
- Bottom->Min = Rect.Min;
+ r32 ClampedYValue = Clamp(Rect.Min.y, YValue, Rect.Max.y);
+ Top->Max = Rect.Max;
+ Top->Min = { Rect.Min.x, ClampedYValue };
+ Bottom->Max = { Rect.Max.x, ClampedYValue };
+ Bottom->Min = Rect.Min;
}
internal void
RectVSplit(rect2 Rect, r32 XValue, rect2* Left, rect2* Right)
{
- r32 ClampedXValue = Clamp(Rect.Min.x, XValue, Rect.Max.x);
- Left->Max = { ClampedXValue, Rect.Max.y};
- Left->Min = Rect.Min;
- Right->Max = Rect.Max;
- Right->Min = { ClampedXValue, Rect.Min.y };
+ r32 ClampedXValue = Clamp(Rect.Min.x, XValue, Rect.Max.x);
+ Left->Max = { ClampedXValue, Rect.Max.y};
+ Left->Min = Rect.Min;
+ Right->Max = Rect.Max;
+ Right->Min = { ClampedXValue, Rect.Min.y };
}
internal void
RectHSplitAtDistanceFromTop(rect2 Rect, r32 YDist, rect2* Top, rect2* Bottom)
{
- RectHSplit(Rect, Rect.Max.y - YDist, Top, Bottom);
+ RectHSplit(Rect, Rect.Max.y - YDist, Top, Bottom);
}
internal void
RectHSplitAtDistanceFromBottom(rect2 Rect, r32 YDist, rect2* Top, rect2* Bottom)
{
- RectHSplit(Rect, Rect.Min.y + YDist, Top, Bottom);
+ RectHSplit(Rect, Rect.Min.y + YDist, Top, Bottom);
}
internal void
RectVSplitAtDistanceFromRight(rect2 Rect, r32 XDist, rect2* Left, rect2* Right)
{
- RectVSplit(Rect, Rect.Max.x - XDist, Left, Right);
+ RectVSplit(Rect, Rect.Max.x - XDist, Left, Right);
}
internal void
RectVSplitAtDistanceFromLeft(rect2 Rect, r32 XDist, rect2* Left, rect2* Right)
{
- RectVSplit(Rect, Rect.Min.x + XDist, Left, Right);
+ RectVSplit(Rect, Rect.Min.x + XDist, Left, Right);
}
internal void
RectHSplitAtPercent(rect2 Rect, r32 YPercent, rect2* Top, rect2* Bottom)
{
- RectHSplit(Rect, LerpR32(YPercent, Rect.Min.y, Rect.Max.y), Top, Bottom);
+ RectHSplit(Rect, LerpR32(YPercent, Rect.Min.y, Rect.Max.y), Top, Bottom);
}
internal void
RectVSplitAtPercent(rect2 Rect, r32 XPercent, rect2* Left, rect2* Right)
{
- RectVSplit(Rect, LerpR32(XPercent, Rect.Min.x, Rect.Max.x), Left, Right);
+ RectVSplit(Rect, LerpR32(XPercent, Rect.Min.x, Rect.Max.x), Left, Right);
}
internal rect2
RectInset(rect2 Outer, v2 Amount)
{
- rect2 Result = { Outer.Min + Amount, Outer.Max - Amount };
- return Result;
+ rect2 Result = { Outer.Min + Amount, Outer.Max - Amount };
+ return Result;
}
internal rect2
RectInset(rect2 Outer, r32 UniformAmount)
{
- return RectInset(Outer, v2{UniformAmount, UniformAmount});
+ return RectInset(Outer, v2{UniformAmount, UniformAmount});
}
internal range1
Range1Union(range1 A, range1 B)
{
- range1 Result = {};
- Result.Min = Max(A.Min, B.Min);
- Result.Max = Min(A.Max, B.Max);
- return Result;
+ range1 Result = {};
+ Result.Min = Max(A.Min, B.Min);
+ Result.Max = Min(A.Max, B.Max);
+ return Result;
}
#define Rect2Union(a,b) Range2Union((a), (b))
internal range2
Range2Union(range2 A, range2 B)
{
- range2 Result = {};
- Result.Min.x = Max(A.Min.x, B.Min.x);
- Result.Min.y = Max(A.Min.y, B.Min.y);
- Result.Max.x = Min(A.Max.x, B.Max.x);
- Result.Max.y = Min(A.Max.y, B.Max.y);
-
- if (Rect2Width(Result) < 0) { Result.Min.x = Result.Max.x; }
- if (Rect2Height(Result) < 0) { Result.Min.y = Result.Max.y; }
-
- return Result;
+ range2 Result = {};
+ Result.Min.x = Max(A.Min.x, B.Min.x);
+ Result.Min.y = Max(A.Min.y, B.Min.y);
+ Result.Max.x = Min(A.Max.x, B.Max.x);
+ Result.Max.y = Min(A.Max.y, B.Max.y);
+
+ if (Rect2Width(Result) < 0) { Result.Min.x = Result.Max.x; }
+ if (Rect2Height(Result) < 0) { Result.Min.y = Result.Max.y; }
+
+ return Result;
}
internal range3
Range3Union(range3 A, range3 B)
{
- range3 Result = {};
- Result.Min.x = Max(A.Min.x, B.Min.x);
- Result.Min.y = Max(A.Min.y, B.Min.y);
- Result.Min.z = Max(A.Min.z, B.Min.z);
- Result.Max.x = Min(A.Max.x, B.Max.x);
- Result.Max.y = Min(A.Max.y, B.Max.y);
- Result.Max.z = Min(A.Max.z, B.Max.z);
- return Result;
+ range3 Result = {};
+ Result.Min.x = Max(A.Min.x, B.Min.x);
+ Result.Min.y = Max(A.Min.y, B.Min.y);
+ Result.Min.z = Max(A.Min.z, B.Min.z);
+ Result.Max.x = Min(A.Max.x, B.Max.x);
+ Result.Max.y = Min(A.Max.y, B.Max.y);
+ Result.Max.z = Min(A.Max.z, B.Max.z);
+ return Result;
}
internal v2
Rect2GetRectLocalPoint(rect2 Rect, v2 Point)
{
- v2 Result = Point - Rect.Min;
- return Result;
+ v2 Result = Point - Rect.Min;
+ return Result;
}
internal r32
Rect2Area(rect2 Rect)
{
- r32 Result = Rect2Width(Rect) * Rect2Height(Rect);
- return Result;
+ r32 Result = Rect2Width(Rect) * Rect2Height(Rect);
+ return Result;
}
internal v2
Rect2BottomLeft(rect2 Rect)
{
- v2 Result = Rect.Min;
- return Result;
+ v2 Result = Rect.Min;
+ return Result;
}
internal v2
Rect2BottomRight(rect2 Rect)
{
- v2 Result = v2{ Rect.Max.x, Rect.Min.y };
- return Result;
+ v2 Result = v2{ Rect.Max.x, Rect.Min.y };
+ return Result;
}
internal v2
Rect2TopRight(rect2 Rect)
{
- v2 Result = Rect.Max;
- return Result;
+ v2 Result = Rect.Max;
+ return Result;
}
internal v2
Rect2TopLeft(rect2 Rect)
{
- v2 Result = v2{ Rect.Min.x, Rect.Max.y };
- return Result;
+ v2 Result = v2{ Rect.Min.x, Rect.Max.y };
+ return Result;
}
///////////////////////////
@@ -1010,55 +1010,55 @@ Rect2TopLeft(rect2 Rect)
internal v4
RayGetPointAlong(v4 RayOrigin, v4 RayDirection, r32 T)
{
- v4 Result = RayOrigin + (RayDirection * T);
- return Result;
+ v4 Result = RayOrigin + (RayDirection * T);
+ return Result;
}
internal r32
RayPlaneIntersectionDistance(v4 RayOrigin, v4 RayDirection, v4 PlanePoint, v4 PlaneNormal)
{
- r32 T = 0.0f;
- float Denominator = V4Dot(PlaneNormal, RayDirection);
- if (Abs(Denominator) > 0.00001f)
- {
- T = V4Dot(PlanePoint - RayDirection, PlaneNormal) / Denominator;
- }
- return T;
+ r32 T = 0.0f;
+ float Denominator = V4Dot(PlaneNormal, RayDirection);
+ if (Abs(Denominator) > 0.00001f)
+ {
+ T = V4Dot(PlanePoint - RayDirection, PlaneNormal) / Denominator;
+ }
+ return T;
}
internal v4
GetRayPlaneIntersectionPoint(v4 RayOrigin, v4 RayDirection, v4 PlanePoint, v4 PlaneNormal)
{
- v4 Result = {0};
- r32 T = RayPlaneIntersectionDistance(RayOrigin, RayDirection, PlanePoint, PlaneNormal);
- if (T >= 0)
- {
- Result = RayGetPointAlong(RayOrigin, RayDirection, T);
- }
- return Result;
+ v4 Result = {0};
+ r32 T = RayPlaneIntersectionDistance(RayOrigin, RayDirection, PlanePoint, PlaneNormal);
+ if (T >= 0)
+ {
+ Result = RayGetPointAlong(RayOrigin, RayDirection, T);
+ }
+ return Result;
}
internal v4
GetRayPlaneIntersectionPoint(v4_ray Ray, v4 PlanePoint, v4 PlaneNormal)
{
- return GetRayPlaneIntersectionPoint(Ray.Origin, Ray.Direction, PlanePoint, PlaneNormal);
+ return GetRayPlaneIntersectionPoint(Ray.Origin, Ray.Direction, PlanePoint, PlaneNormal);
}
internal bool
RayIntersectsPlane(v4 RayOrigin, v4 RayDirection, v4 PlanePoint, v4 PlaneNormal, v4* OutPoint)
{
- bool Result = false;
- r32 T = RayPlaneIntersectionDistance(RayOrigin, RayDirection, PlanePoint, PlaneNormal);
- if (T >= 0)
- {
- Result = true;
- *OutPoint = RayGetPointAlong(RayOrigin, RayDirection, T);
- }
- return Result;
+ bool Result = false;
+ r32 T = RayPlaneIntersectionDistance(RayOrigin, RayDirection, PlanePoint, PlaneNormal);
+ if (T >= 0)
+ {
+ Result = true;
+ *OutPoint = RayGetPointAlong(RayOrigin, RayDirection, T);
+ }
+ return Result;
}
internal bool
RayIntersectsPlane(v4_ray Ray, v4 PlanePoint, v4 PlaneNormal, v4* OutPoint)
{
- return RayIntersectsPlane(Ray.Origin, Ray.Direction, PlanePoint, PlaneNormal, OutPoint);
+ return RayIntersectsPlane(Ray.Origin, Ray.Direction, PlanePoint, PlaneNormal, OutPoint);
}
///////////////////////////
@@ -1068,286 +1068,286 @@ RayIntersectsPlane(v4_ray Ray, v4 PlanePoint, v4 PlaneNormal, v4* OutPoint)
internal m44
M44Identity()
{
- m44 M = {0};
- M.AXx = 1.0f;
- M.AYy = 1.0f;
- M.AZz = 1.0f;
- M.Tw = 1.0f;
- return M;
+ m44 M = {0};
+ M.AXx = 1.0f;
+ M.AYy = 1.0f;
+ M.AZz = 1.0f;
+ M.Tw = 1.0f;
+ return M;
}
internal m44
M44Transpose(m44 M)
{
- m44 Result = {0};
- for (u32 Y = 0; Y < 4; Y++)
+ m44 Result = {0};
+ for (u32 Y = 0; Y < 4; Y++)
+ {
+ for (u32 X = 0; X < 4; X++)
{
- for (u32 X = 0; X < 4; X++)
- {
- Result.Array[(X * 4) + Y] = M.Array[(Y * 4) + X];
- }
+ Result.Array[(X * 4) + Y] = M.Array[(Y * 4) + X];
}
- return Result;
+ }
+ return Result;
}
// Matrix * Matrix
m44 operator* (m44 L, m44 R)
{
- m44 M = {0};
-
- // ci ic ci ic ci ic i ic
- M.AXx = (L.AXx * R.AXx) + (L.AYx * R.AXy) + (L.AZx * R.AXz) + (L.Tx * R.AXw);
- M.AXy = (L.AXy * R.AXx) + (L.AYy * R.AXy) + (L.AZy * R.AXz) + (L.Ty * R.AXw);
- M.AXz = (L.AXz * R.AXx) + (L.AYz * R.AXy) + (L.AZz * R.AXz) + (L.Tz * R.AXw);
- M.AXw = (L.AXw * R.AXx) + (L.AYw * R.AXy) + (L.AZw * R.AXz) + (L.Tw * R.AXw);
-
- M.AYx = (L.AXx * R.AYx) + (L.AYx * R.AYy) + (L.AZx * R.AYz) + (L.Tx * R.AYw);
- M.AYy = (L.AXy * R.AYx) + (L.AYy * R.AYy) + (L.AZy * R.AYz) + (L.Ty * R.AYw);
- M.AYz = (L.AXz * R.AYx) + (L.AYz * R.AYy) + (L.AZz * R.AYz) + (L.Tz * R.AYw);
- M.AYz = (L.AXw * R.AYx) + (L.AYw * R.AYy) + (L.AZw * R.AYz) + (L.Tw * R.AYw);
-
- M.AZx = (L.AXx * R.AZx) + (L.AYx * R.AZy) + (L.AZx * R.AZz) + (L.Tx * R.AZw);
- M.AZy = (L.AXy * R.AZx) + (L.AYy * R.AZy) + (L.AZy * R.AZz) + (L.Ty * R.AZw);
- M.AZz = (L.AXz * R.AZx) + (L.AYz * R.AZy) + (L.AZz * R.AZz) + (L.Tz * R.AZw);
- M.AZw = (L.AXw * R.AZx) + (L.AYw * R.AZy) + (L.AZw * R.AZz) + (L.Tw * R.AZw);
-
- M.Tx = (L.AXx * R.Tx) + (L.AYx * R.Ty) + (L.AZx * R.Tz) + (L.Tx * R.Tw);
- M.Ty = (L.AXy * R.Tx) + (L.AYy * R.Ty) + (L.AZy * R.Tz) + (L.Ty * R.Tw);
- M.Tz = (L.AXz * R.Tx) + (L.AYz * R.Ty) + (L.AZz * R.Tz) + (L.Tz * R.Tw);
- M.Tw = (L.AXw * R.Tx) + (L.AYw * R.Ty) + (L.AZw * R.Tz) + (L.Tw * R.Tw);
-
- return M;
+ m44 M = {0};
+
+ // ci ic ci ic ci ic i ic
+ M.AXx = (L.AXx * R.AXx) + (L.AYx * R.AXy) + (L.AZx * R.AXz) + (L.Tx * R.AXw);
+ M.AXy = (L.AXy * R.AXx) + (L.AYy * R.AXy) + (L.AZy * R.AXz) + (L.Ty * R.AXw);
+ M.AXz = (L.AXz * R.AXx) + (L.AYz * R.AXy) + (L.AZz * R.AXz) + (L.Tz * R.AXw);
+ M.AXw = (L.AXw * R.AXx) + (L.AYw * R.AXy) + (L.AZw * R.AXz) + (L.Tw * R.AXw);
+
+ M.AYx = (L.AXx * R.AYx) + (L.AYx * R.AYy) + (L.AZx * R.AYz) + (L.Tx * R.AYw);
+ M.AYy = (L.AXy * R.AYx) + (L.AYy * R.AYy) + (L.AZy * R.AYz) + (L.Ty * R.AYw);
+ M.AYz = (L.AXz * R.AYx) + (L.AYz * R.AYy) + (L.AZz * R.AYz) + (L.Tz * R.AYw);
+ M.AYz = (L.AXw * R.AYx) + (L.AYw * R.AYy) + (L.AZw * R.AYz) + (L.Tw * R.AYw);
+
+ M.AZx = (L.AXx * R.AZx) + (L.AYx * R.AZy) + (L.AZx * R.AZz) + (L.Tx * R.AZw);
+ M.AZy = (L.AXy * R.AZx) + (L.AYy * R.AZy) + (L.AZy * R.AZz) + (L.Ty * R.AZw);
+ M.AZz = (L.AXz * R.AZx) + (L.AYz * R.AZy) + (L.AZz * R.AZz) + (L.Tz * R.AZw);
+ M.AZw = (L.AXw * R.AZx) + (L.AYw * R.AZy) + (L.AZw * R.AZz) + (L.Tw * R.AZw);
+
+ M.Tx = (L.AXx * R.Tx) + (L.AYx * R.Ty) + (L.AZx * R.Tz) + (L.Tx * R.Tw);
+ M.Ty = (L.AXy * R.Tx) + (L.AYy * R.Ty) + (L.AZy * R.Tz) + (L.Ty * R.Tw);
+ M.Tz = (L.AXz * R.Tx) + (L.AYz * R.Ty) + (L.AZz * R.Tz) + (L.Tz * R.Tw);
+ M.Tw = (L.AXw * R.Tx) + (L.AYw * R.Ty) + (L.AZw * R.Tz) + (L.Tw * R.Tw);
+
+ return M;
}
// Matrix * Vector
v4 operator* (m44 M, v4 V)
{
- v4 Result = {0};
- Result.x = (V.x * M.AXx) + (V.y * M.AYx) + (V.z * M.AZx) + (V.w * M.Tx);
- Result.y = (V.x * M.AXy) + (V.y * M.AYy) + (V.z * M.AZy) + (V.w * M.Ty);
- Result.z = (V.x * M.AXz) + (V.y * M.AYz) + (V.z * M.AZz) + (V.w * M.Tz);
- Result.w = (V.x * M.AXw) + (V.y * M.AYw) + (V.z * M.AZw) + (V.w * M.Tw);
- return Result;
+ v4 Result = {0};
+ Result.x = (V.x * M.AXx) + (V.y * M.AYx) + (V.z * M.AZx) + (V.w * M.Tx);
+ Result.y = (V.x * M.AXy) + (V.y * M.AYy) + (V.z * M.AZy) + (V.w * M.Ty);
+ Result.z = (V.x * M.AXz) + (V.y * M.AYz) + (V.z * M.AZz) + (V.w * M.Tz);
+ Result.w = (V.x * M.AXw) + (V.y * M.AYw) + (V.z * M.AZw) + (V.w * M.Tw);
+ return Result;
}
internal m44
M44Translation(v4 Offset)
{
- m44 Result = M44Identity();
- Result.Tx = Offset.x;
- Result.Ty = Offset.y;
- Result.Tz = Offset.z;
- return Result;
+ m44 Result = M44Identity();
+ Result.Tx = Offset.x;
+ Result.Ty = Offset.y;
+ Result.Tz = Offset.z;
+ return Result;
}
internal m44
M44RotationX(r32 Radians)
{
- r32 CosRad = CosR32(Radians);
- r32 SinRad = SinR32(Radians);
- m44 Result = M44Identity();
- Result.AYy = CosRad;
- Result.AZy = SinRad;
- Result.AYz = -SinRad;
- Result.AZz = CosRad;
- return Result;
+ r32 CosRad = CosR32(Radians);
+ r32 SinRad = SinR32(Radians);
+ m44 Result = M44Identity();
+ Result.AYy = CosRad;
+ Result.AZy = SinRad;
+ Result.AYz = -SinRad;
+ Result.AZz = CosRad;
+ return Result;
}
internal m44
M44RotationY(r32 Radians)
{
- r32 CosRad = CosR32(Radians);
- r32 SinRad = SinR32(Radians);
- m44 Result = M44Identity();
- Result.AXx = CosRad;
- Result.AZx = SinRad;
- Result.AXz = -SinRad;
- Result.AZz = CosRad;
- return Result;
+ r32 CosRad = CosR32(Radians);
+ r32 SinRad = SinR32(Radians);
+ m44 Result = M44Identity();
+ Result.AXx = CosRad;
+ Result.AZx = SinRad;
+ Result.AXz = -SinRad;
+ Result.AZz = CosRad;
+ return Result;
}
internal m44
M44RotationZ(r32 Radians)
{
- r32 CosRad = CosR32(Radians);
- r32 SinRad = SinR32(Radians);
- m44 Result = M44Identity();
- Result.AXx = CosRad;
- Result.AYx = -SinRad;
- Result.AXy = SinRad;
- Result.AYy = CosRad;
- return Result;
+ r32 CosRad = CosR32(Radians);
+ r32 SinRad = SinR32(Radians);
+ m44 Result = M44Identity();
+ Result.AXx = CosRad;
+ Result.AYx = -SinRad;
+ Result.AXy = SinRad;
+ Result.AYy = CosRad;
+ return Result;
}
internal m44
M44Rotation(v3 Radians)
{
- r32 CosX = CosR32(Radians.x);
- r32 SinX = SinR32(Radians.x);
- r32 CosY = CosR32(Radians.y);
- r32 SinY = SinR32(Radians.y);
- r32 CosZ = CosR32(Radians.z);
- r32 SinZ = SinR32(Radians.z);
-
- m44 Result = {0};
- Result.AXx = CosY * CosZ;
- Result.AXy = -(SinX * SinY * CosZ) + (CosX * SinZ);
- Result.AXz = -(CosX * SinY * CosZ) - (SinX * SinZ);
- Result.AXw = 0;
-
- Result.AYx = -(SinZ * CosY);
- Result.AYy = (SinX * SinY * SinZ) + (CosX * CosZ);
- Result.AYz = (CosX * SinY * SinZ) - (SinX * CosZ);
- Result.AYw = 0;
-
- Result.AZx = SinY;
- Result.AZy = SinX * CosY;
- Result.AZz = CosX * CosY;
- Result.AZw = 0;
-
- Result.Tx = 0;
- Result.Ty = 0;
- Result.Tz = 0;
- Result.Tw = 1;
-
- return Result;
+ r32 CosX = CosR32(Radians.x);
+ r32 SinX = SinR32(Radians.x);
+ r32 CosY = CosR32(Radians.y);
+ r32 SinY = SinR32(Radians.y);
+ r32 CosZ = CosR32(Radians.z);
+ r32 SinZ = SinR32(Radians.z);
+
+ m44 Result = {0};
+ Result.AXx = CosY * CosZ;
+ Result.AXy = -(SinX * SinY * CosZ) + (CosX * SinZ);
+ Result.AXz = -(CosX * SinY * CosZ) - (SinX * SinZ);
+ Result.AXw = 0;
+
+ Result.AYx = -(SinZ * CosY);
+ Result.AYy = (SinX * SinY * SinZ) + (CosX * CosZ);
+ Result.AYz = (CosX * SinY * SinZ) - (SinX * CosZ);
+ Result.AYw = 0;
+
+ Result.AZx = SinY;
+ Result.AZy = SinX * CosY;
+ Result.AZz = CosX * CosY;
+ Result.AZw = 0;
+
+ Result.Tx = 0;
+ Result.Ty = 0;
+ Result.Tz = 0;
+ Result.Tw = 1;
+
+ return Result;
}
internal m44
M44Scale(v3 Scale)
{
- m44 Result = M44Identity();
- Result.AXx = Scale.x;
- Result.AYy = Scale.y;
- Result.AZz = Scale.z;
- return Result;
+ m44 Result = M44Identity();
+ Result.AXx = Scale.x;
+ Result.AYy = Scale.y;
+ Result.AZz = Scale.z;
+ return Result;
}
internal m44
M44ScaleUniform(r32 Scale)
{
- m44 Result = M44Identity();
- Result.AXx = Scale;
- Result.AYy = Scale;
- Result.AZz = Scale;
- return Result;
+ m44 Result = M44Identity();
+ Result.AXx = Scale;
+ Result.AYy = Scale;
+ Result.AZz = Scale;
+ return Result;
}
internal m44
M44CoordinateFrame(v4 Forward, v4 Right, v4 Up)
{
- m44 Result = {0};
- Result.AXx = Right.x;
- Result.AYx = Right.y;
- Result.AZx = Right.z;
- Result.Tx = Right.w;
-
- Result.AXy = Up.x;
- Result.AYy = Up.y;
- Result.AZy = Up.z;
- Result.Ty = Up.w;
-
- Result.AXz = Forward.x;
- Result.AYz = Forward.y;
- Result.AZz = Forward.z;
- Result.Tz = Forward.w;
-
- Result.Tw = 1.0f;
- return Result;
+ m44 Result = {0};
+ Result.AXx = Right.x;
+ Result.AYx = Right.y;
+ Result.AZx = Right.z;
+ Result.Tx = Right.w;
+
+ Result.AXy = Up.x;
+ Result.AYy = Up.y;
+ Result.AZy = Up.z;
+ Result.Ty = Up.w;
+
+ Result.AXz = Forward.x;
+ Result.AYz = Forward.y;
+ Result.AZz = Forward.z;
+ Result.Tz = Forward.w;
+
+ Result.Tw = 1.0f;
+ return Result;
}
internal m44
M44ModelMatrix(v4 Forward, v4 Right, v4 Up, v4 Position)
{
- m44 RotationMatrix = M44CoordinateFrame(Forward, Right, Up);
- m44 PositionMatrix = M44Translation(-Position);
- m44 ModelViewMatrix = PositionMatrix * RotationMatrix;
- return ModelViewMatrix;
+ m44 RotationMatrix = M44CoordinateFrame(Forward, Right, Up);
+ m44 PositionMatrix = M44Translation(-Position);
+ m44 ModelViewMatrix = PositionMatrix * RotationMatrix;
+ return ModelViewMatrix;
}
internal m44
M44ProjectionOrtho(r32 Width, r32 Height, r32 Near, r32 Far, r32 Right, r32 Left, r32 Top, r32 Bottom)
{
- m44 Result = {0};
- Result.AXx = 2.0f / Width;
- Result.AYy = 2.0f / Height;
- Result.AZz = 2.0f / (Near - Far);
- Result.AXw = -(Right + Left) / (Right - Left);
- Result.AYw = -(Top + Bottom) / (Top - Bottom);
- Result.AZw = -(Far + Near) / (Far - Near);
- Result.Tw = 1;
- return Result;
+ m44 Result = {0};
+ Result.AXx = 2.0f / Width;
+ Result.AYy = 2.0f / Height;
+ Result.AZz = 2.0f / (Near - Far);
+ Result.AXw = -(Right + Left) / (Right - Left);
+ Result.AYw = -(Top + Bottom) / (Top - Bottom);
+ Result.AZw = -(Far + Near) / (Far - Near);
+ Result.Tw = 1;
+ return Result;
}
internal m44
M44ProjectionOrtho(r32 Aspect, r32 Scale, r32 Near, r32 Far)
{
- m44 Result = {0};
- r32 Width = Scale * Aspect;
- r32 Height = Scale;
- r32 Right = Width / 2.0f;
- r32 Left = -Right;
- r32 Top = Height / 2.0f;
- r32 Bottom = -Top;
- Result = M44ProjectionOrtho(Width, Height, Near, Far, Right, Left, Top, Bottom);
- return Result;
+ m44 Result = {0};
+ r32 Width = Scale * Aspect;
+ r32 Height = Scale;
+ r32 Right = Width / 2.0f;
+ r32 Left = -Right;
+ r32 Top = Height / 2.0f;
+ r32 Bottom = -Top;
+ Result = M44ProjectionOrtho(Width, Height, Near, Far, Right, Left, Top, Bottom);
+ return Result;
}
internal m44
M44ProjectionInterfaceOrtho(r32 Width, r32 Height, r32 Near, r32 Far)
{
- m44 Result = {0};
- r32 Aspect = Width / Height;
- r32 Right = Width;
- r32 Left = 0;
- r32 Top = Height;
- r32 Bottom = 0;
- Result = M44ProjectionOrtho(Width, Height, Near, Far, Right, Left, Top, Bottom);
- return Result;
+ m44 Result = {0};
+ r32 Aspect = Width / Height;
+ r32 Right = Width;
+ r32 Left = 0;
+ r32 Top = Height;
+ r32 Bottom = 0;
+ Result = M44ProjectionOrtho(Width, Height, Near, Far, Right, Left, Top, Bottom);
+ return Result;
}
internal m44
M44ProjectionPerspective(r32 FieldOfViewDegrees, r32 AspectRatio, r32 Near, r32 Far)
{
- m44 Result = M44Identity();
-
- // The perspective divide step involves dividing x and y by -z
- // Making Tz = -1 will make Tw of the result = -z
- Result.Tw = 0;
- Result.AZw = -1;
-
- // Remap z' from the range [near clip : far clip] to [0 : 1]
- r32 ViewRange = Far - Near;
- Result.AZz = -((Far + Near) / ViewRange);
- Result.Tz = -(2 * Near * Far) / ViewRange;
-
- // Adjust for field of view - adjust the x' and y coordinates based
- // on how
- r32 FovBasedScale = TanR32(DegToRadR32(FieldOfViewDegrees / 2));
- r32 Top = Near * FovBasedScale;
- r32 Bottom = -Top;
- r32 Right = Top * AspectRatio;
- r32 Left = -Right;
- Result.AXx = (2 * Near) / (Right - Left);
- Result.AZx = (Right + Left) / (Right - Left);
- Result.AYy = (2 * Near) / (Top - Bottom);
- Result.AZy = (Top + Bottom) / (Top - Bottom);
-
- return Result;
+ m44 Result = M44Identity();
+
+ // The perspective divide step involves dividing x and y by -z
+ // Making Tz = -1 will make Tw of the result = -z
+ Result.Tw = 0;
+ Result.AZw = -1;
+
+ // Remap z' from the range [near clip : far clip] to [0 : 1]
+ r32 ViewRange = Far - Near;
+ Result.AZz = -((Far + Near) / ViewRange);
+ Result.Tz = -(2 * Near * Far) / ViewRange;
+
+ // Adjust for field of view - adjust the x' and y coordinates based
+ // on how
+ r32 FovBasedScale = TanR32(DegToRadR32(FieldOfViewDegrees / 2));
+ r32 Top = Near * FovBasedScale;
+ r32 Bottom = -Top;
+ r32 Right = Top * AspectRatio;
+ r32 Left = -Right;
+ Result.AXx = (2 * Near) / (Right - Left);
+ Result.AZx = (Right + Left) / (Right - Left);
+ Result.AYy = (2 * Near) / (Top - Bottom);
+ Result.AZy = (Top + Bottom) / (Top - Bottom);
+
+ return Result;
}
internal m44
M44LookAt(v4 Position, v4 Target)
{
- // NOTE(Peter): the camera usually points along the -z axis, hence
- // Forward = a ray that points from the target back towards your position
- v4 Forward = V4Normalize(Position - Target);
- v4 Right = V4Normalize(V4Cross(v4{0, 1, 0, 0}, Forward));
- v4 Up = V4Normalize(V4Cross(Forward, Right));
- m44 Result = M44CoordinateFrame(Forward, Right, Up);
- return Result;
+ // NOTE(Peter): the camera usually points along the -z axis, hence
+ // Forward = a ray that points from the target back towards your position
+ v4 Forward = V4Normalize(Position - Target);
+ v4 Right = V4Normalize(V4Cross(v4{0, 1, 0, 0}, Forward));
+ v4 Up = V4Normalize(V4Cross(Forward, Right));
+ m44 Result = M44CoordinateFrame(Forward, Right, Up);
+ return Result;
}
///////////////////////////
@@ -1358,37 +1358,44 @@ internal gs_const_string ConstString(char* Data, u64 Length) { return gs_const_s
internal gs_const_string ConstString(char* Data) { return gs_const_string{Data, CStringLength(Data)}; }
internal gs_string MakeString(char* Data, u64 Length, u64 Size)
{
- Assert(Length <= Size);
- gs_string Result = {0};
- Result.Str = Data;
- Result.Length = Length;
- Result.Size = Size;
- return Result;
+ Assert(Length <= Size);
+ gs_string Result = {0};
+ Result.Str = Data;
+ Result.Length = Length;
+ Result.Size = Size;
+ return Result;
}
internal gs_string MakeString(char* Data, u64 Length)
{
- return MakeString(Data, Length, Length);
+ return MakeString(Data, Length, Length);
}
internal gs_string MakeString(char* Data)
{
- u64 StringLength = CStringLength(Data);
- return MakeString(Data, StringLength, StringLength);
+ u64 StringLength = CStringLength(Data);
+ return MakeString(Data, StringLength, StringLength);
}
internal gs_string MakeString(gs_const_string ConstString)
{
- return MakeString(ConstString.Str, ConstString.Length);
+ return MakeString(ConstString.Str, ConstString.Length);
}
internal gs_data StringToData(gs_const_string String)
{
- gs_data Result = gs_data{0};
- Result.Memory = (u8*)String.Str;
- Result.Size = String.Length * sizeof(char);
- return Result;
+ gs_data Result = gs_data{0};
+ Result.Memory = (u8*)String.Str;
+ Result.Size = String.Length * sizeof(char);
+ return Result;
}
internal gs_data StringToData(gs_string String)
{
- return StringToData(String.ConstString);
+ return StringToData(String.ConstString);
+}
+internal gs_const_string DataToString(gs_data Data)
+{
+ gs_const_string Result = {};
+ Result.Str = (char*)Data.Memory;
+ Result.Length = Data.Size;
+ return Result;
}
internal bool IsSlash(char C) { return ((C == '/') || (C == '\\')); }
@@ -1405,899 +1412,1002 @@ internal bool IsNumericExtended(char C) { return IsNumericDecimal(C) || (C == 'x
internal bool IsAlpha(char C) { return( (('a' <= C) && (C <= 'z')) || (('A' <= C) && (C <= 'Z')) || C == '_'); }
internal bool IsAlphaNumeric(char C) { return((('a' <= C) && (C <= 'z')) || (('A' <= C) && (C <= 'Z')) || (('0' <= C) && (C <= '9')) || C == '_'); }
internal bool IsOperator(char C) {
- return ((C == '+') || (C == '-') || (C == '*') || (C == '/') ||
- (C == '=') || (C == '%') || (C == '<') || (C == '>'));
+ return ((C == '+') || (C == '-') || (C == '*') || (C == '/') ||
+ (C == '=') || (C == '%') || (C == '<') || (C == '>'));
}
internal char
ToUpper(char C)
{
- if ((C >= 'a') && (C <= 'z'))
- {
- C -= 'a' - 'A';
- }
- return C;
+ if ((C >= 'a') && (C <= 'z'))
+ {
+ C -= 'a' - 'A';
+ }
+ return C;
}
internal char
ToLower(char C)
{
- if ((C >= 'A') && (C <= 'Z'))
- {
- C += 'a' - 'A';
- }
- return C;
+ if ((C >= 'A') && (C <= 'Z'))
+ {
+ C += 'a' - 'A';
+ }
+ return C;
}
internal bool CharsEqualCaseInsensitive(char A, char B) { return ToLower(A) == ToLower(B); }
internal u64
CharArrayLength (char* CS)
{
- char* At = CS;
- while (*At) { At++; }
- return (u64)(At - CS);
+ char* At = CS;
+ while (*At) { At++; }
+ return (u64)(At - CS);
}
internal bool
IsNullTerminated(gs_const_string String)
{
- bool Result = false;
- if (String.Str)
- {
- Result = (String.Str[String.Length] == 0);
- }
- return Result;
+ bool Result = false;
+ if (String.Str)
+ {
+ Result = (String.Str[String.Length] == 0);
+ }
+ return Result;
}
internal bool
IsNullTerminated(gs_string String)
{
- return IsNullTerminated(String.ConstString);
+ return IsNullTerminated(String.ConstString);
}
internal char
GetChar(gs_const_string String, u64 I)
{
- char Result = 0;
- if (I < String.Length)
- {
- Result = String.Str[I];
- }
- return Result;
+ char Result = 0;
+ if (I < String.Length)
+ {
+ Result = String.Str[I];
+ }
+ return Result;
}
internal char
GetChar(gs_string String, u64 I)
{
- char Result = 0;
- if (I < String.Length)
- {
- Result = String.Str[I];
- }
- return Result;
+ char Result = 0;
+ if (I < String.Length)
+ {
+ Result = String.Str[I];
+ }
+ return Result;
}
internal gs_const_string
GetStringPrefix(gs_const_string String, u64 Size)
{
- gs_const_string Result = String;
- Result.Length = Min(Size, String.Length);
- return Result;
+ gs_const_string Result = String;
+ Result.Length = Min(Size, String.Length);
+ return Result;
}
internal gs_const_string
GetStringPostfix(gs_const_string String, u64 Size)
{
- gs_const_string Result = String;
- u64 PostfixSize = Min(Size, String.Length);
- Result.Str += (Result.Length - PostfixSize);
- Result.Length = PostfixSize;
- return Result;
+ gs_const_string Result = String;
+ u64 PostfixSize = Min(Size, String.Length);
+ Result.Str += (Result.Length - PostfixSize);
+ Result.Length = PostfixSize;
+ return Result;
}
internal gs_const_string
GetStringAfter(gs_const_string String, u64 Cut)
{
- gs_const_string Result = String;
- u64 CutSize = Min(Cut, String.Length);
- Result.Str += CutSize;
- Result.Length -= CutSize;
- return Result;
+ gs_const_string Result = String;
+ u64 CutSize = Min(Cut, String.Length);
+ Result.Str += CutSize;
+ Result.Length -= CutSize;
+ return Result;
}
internal gs_string
GetStringAfter(gs_string String, u64 Cut)
{
- gs_string Result = {0};
- Result.ConstString = GetStringAfter(String.ConstString, Cut);
- Result.Size = String.Size - Cut;
- return Result;
+ gs_string Result = {0};
+ Result.ConstString = GetStringAfter(String.ConstString, Cut);
+ Result.Size = String.Size - Cut;
+ return Result;
}
internal gs_const_string
GetStringBefore(gs_const_string String, u64 Cut)
{
- gs_const_string Result = String;
- Result.Length = Min(Cut, String.Length);
- return Result;
+ gs_const_string Result = String;
+ Result.Length = Min(Cut, String.Length);
+ return Result;
}
internal gs_const_string
Substring(gs_const_string String, u64 First, u64 Last)
{
- gs_const_string Result = {0};
- Result.Str = String.Str + Min(First, String.Length);
- Result.Length = Min(Last - First, String.Length);
- return Result;
+ gs_const_string Result = {0};
+ Result.Str = String.Str + Min(First, String.Length);
+ Result.Length = Min(Last - First, String.Length);
+ return Result;
}
-internal u64
+internal gs_const_string
+Substring(gs_string String, u64 First, u64 Last)
+{
+ return Substring(String.ConstString, First, Last);
+}
+
+internal s64
FindFirst(gs_const_string String, u64 StartIndex, char C)
{
- u64 Result = StartIndex;
- for(; Result < String.Length && C != String.Str[Result]; Result++);
- return Result;
+ s64 Result = -1;
+ for(u64 i = StartIndex; i < String.Length; i++)
+ {
+ if (String.Str[i] == C) {
+ Result = (s64)i;
+ break;
+ }
+ }
+ return Result;
}
-internal u64
+internal s64
FindFirst(gs_const_string String, char C)
{
- return FindFirst(String, 0, C);
+ return FindFirst(String, 0, C);
+}
+internal s64
+FindFirst(gs_string String, u64 StartIndex, char C)
+{
+ return FindFirst(String.ConstString, StartIndex, C);
+}
+internal s64
+FindFirst(gs_string String, char C)
+{
+ return FindFirst(String.ConstString, 0, C);
}
-internal u64
+internal s64
+FindLast(char* String, s64 StartIndex, char C)
+{
+ s64 Result = -1;
+ s64 i = 0;
+ while (String[i] != 0 && i < StartIndex)
+ {
+ i++;
+ }
+ while (String[i])
+ {
+ if (String[i] == C)
+ {
+ Result = i;
+ }
+ i++;
+ }
+ return Result;
+}
+internal s64
FindLast(gs_const_string String, u64 StartIndex, char C)
{
- s64 Result = StartIndex;
- for(; Result >= 0 && C != String.Str[Result]; Result--);
- return (u64)Result;
+ s64 Result = -1;
+ for(s64 i= StartIndex; i >= 0; i--)
+ {
+ if (String.Str[i] == C) {
+ Result = i;
+ break;
+ }
+ }
+ return (u64)Result;
}
-
-internal u64
+internal s64
FindLast(gs_const_string String, char C)
{
- return FindLast(String, String.Length - 1, C);
+ return FindLast(String, String.Length - 1, C);
+}
+internal s64
+FindLast(gs_string String, u64 StartIndex, char C)
+{
+ return FindLast(String.ConstString, StartIndex, C);
+}
+internal s64
+FindLast(gs_string String, char C)
+{
+ return FindLast(String.ConstString, String.Length - 1, C);
}
-internal u64
+internal s64
FindFirstFromSet(gs_const_string String, char* SetArray)
{
- gs_const_string Set = ConstString(SetArray);
- u64 Result = String.Length - 1;
- for(u64 At = 0; At < String.Length; At++)
+ gs_const_string Set = ConstString(SetArray);
+ s64 Result = -1;
+
+ s64 CurrMin = String.Length;
+ for (u64 SetAt = 0; SetAt < Set.Length; SetAt++)
+ {
+ s64 Index = FindFirst(String, Set.Str[SetAt]);
+ if (Index >= 0 && Index < CurrMin)
{
- char CharAt = String.Str[At];
- for (u64 SetAt = 0; SetAt < Set.Length; SetAt++)
- {
- if (CharAt == Set.Str[SetAt])
- {
- Result = At;
- // NOTE(Peter): The alternative to this goto is a break in the inner loop
- // followed by an if check in the outer loop, that must be evaluated
- // every character you check. This is more efficient
- goto find_last_from_set_complete;
- }
- }
+ CurrMin = Index;
}
- find_last_from_set_complete:
- return Result;
+ }
+
+ if (CurrMin < (s64)String.Length)
+ {
+ Result = CurrMin;
+ }
+
+ return Result;
}
internal s64
FindLastFromSet(gs_const_string String, char* SetArray)
{
- gs_const_string Set = ConstString(SetArray);
- s64 Result = -1;
- for(s64 At = String.Length - 1; At >= 0; At--)
+ gs_const_string Set = ConstString(SetArray);
+ s64 Result = -1;
+ for(s64 At = String.Length - 1; At >= 0; At--)
+ {
+ char CharAt = String.Str[At];
+ for (u64 SetAt = 0; SetAt < Set.Length; SetAt++)
{
- char CharAt = String.Str[At];
- for (u64 SetAt = 0; SetAt < Set.Length; SetAt++)
- {
- if (CharAt == Set.Str[SetAt])
- {
- Result = (u64)At;
- // NOTE(Peter): The alternative to this goto is a break in the inner loop
- // followed by an if check in the outer loop, that must be evaluated
- // every character you check. This is more efficient
- goto find_first_from_set_complete;
- }
- }
+ if (CharAt == Set.Str[SetAt])
+ {
+ Result = (u64)At;
+ // NOTE(Peter): The alternative to this goto is a break in the inner loop
+ // followed by an if check in the outer loop, that must be evaluated
+ // every character you check. This is more efficient
+ goto find_first_from_set_complete;
+ }
}
- find_first_from_set_complete:
- return Result;
+ }
+ find_first_from_set_complete:
+ return Result;
}
internal bool
StringContains(gs_const_string Str, char C)
{
- bool Result = false;
- for (u32 i = 0; i < Str.Length; i++)
+ bool Result = false;
+ for (u32 i = 0; i < Str.Length; i++)
+ {
+ if (Str.Str[i] == C)
{
- if (Str.Str[i] == C)
- {
- Result = true;
- break;
- }
+ Result = true;
+ break;
}
- return Result;
+ }
+ return Result;
}
internal bool
StringsEqualUpToLength(gs_const_string A, gs_const_string B, u64 Length)
{
- bool Result = false;
- if (A.Length >= Length && B.Length >= Length)
+ bool Result = false;
+ if (A.Length >= Length && B.Length >= Length)
+ {
+ Result = true;
+ Length = Min(Length, A.Length);
+ for (u64 i = 0; i < Length; i++)
{
- Result = true;
- Length = Min(Length, A.Length);
- for (u64 i = 0; i < Length; i++)
- {
- if (A.Str[i] != B.Str[i])
- {
- Result = false;
- break;
- }
- }
+ if (A.Str[i] != B.Str[i])
+ {
+ Result = false;
+ break;
+ }
}
- return Result;
+ }
+ return Result;
}
internal bool
StringsEqual(gs_const_string A, gs_const_string B)
{
- bool Result = false;
- if (A.Length == B.Length)
- {
- Result = StringsEqualUpToLength(A, B, A.Length);
- }
- return Result;
+ bool Result = false;
+ if (A.Length == B.Length)
+ {
+ Result = StringsEqualUpToLength(A, B, A.Length);
+ }
+ return Result;
}
internal bool
StringEqualsCharArray(gs_const_string A, char* B, u64 Length)
{
- gs_const_string BStr = ConstString(B, Length);
- return StringsEqual(A, BStr);
+ gs_const_string BStr = ConstString(B, Length);
+ return StringsEqual(A, BStr);
+}
+internal bool
+StringEqualsCharArray(gs_const_string A, char* B)
+{
+ u64 Length = CStringLength(B);
+ return StringEqualsCharArray(A, B, Length);
}
internal bool
StringsEqualUpToLength(gs_string A, gs_string B, u64 Length)
{
- return StringsEqualUpToLength(A.ConstString, B.ConstString, Length);
+ return StringsEqualUpToLength(A.ConstString, B.ConstString, Length);
}
internal bool
StringsEqual(gs_string A, gs_string B)
{
- return StringsEqual(A.ConstString, B.ConstString);
+ return StringsEqual(A.ConstString, B.ConstString);
}
internal bool
StringEqualsCharArray(gs_string A, char* B, u64 Length)
{
- gs_string BStr = MakeString(B, Length);
- return StringsEqual(A, BStr);
+ return StringEqualsCharArray(A.ConstString, B, Length);
+}
+internal bool
+StringEqualsCharArray(gs_string A, char* B)
+{
+ return StringEqualsCharArray(A.ConstString, B);
}
internal u64
StringSizeLeft(gs_string String)
{
- u64 Result = String.Size - String.Length;
- return Result;
+ u64 Result = String.Size - String.Length;
+ return Result;
}
internal void
ReverseStringInPlace(gs_string* String)
{
- char* Start = String->Str;
- char* End = String->Str + String->Length;
- while (Start < End)
- {
- End--;
- char Temp = End[0];
- End[0] = Start[0];
- Start[0] = Temp;
- Start++;
- }
+ char* Start = String->Str;
+ char* End = String->Str + String->Length;
+ while (Start < End)
+ {
+ End--;
+ char Temp = End[0];
+ End[0] = Start[0];
+ Start[0] = Temp;
+ Start++;
+ }
}
internal gs_const_string
GetCharSetForBase(u64 Base)
{
- gs_const_string Result = {0};
- switch(Base)
- {
- case 8: { Result = Base8Chars; }break;
- case 10: { Result = Base10Chars; }break;
- case 16: { Result = Base16Chars; }break;
- InvalidDefaultCase;
- }
- return Result;
+ gs_const_string Result = {0};
+ switch(Base)
+ {
+ case 8: { Result = Base8Chars; }break;
+ case 10: { Result = Base10Chars; }break;
+ case 16: { Result = Base16Chars; }break;
+ InvalidDefaultCase;
+ }
+ return Result;
}
internal u64
CharToUInt(char C, gs_const_string CharSet)
{
- return (u64)FindFirst(CharSet, C);
+ return (u64)FindFirst(CharSet, C);
}
internal u64
CharToUInt(char C)
{
- return (u64)CharToUInt(C, Base10Chars);
+ return (u64)CharToUInt(C, Base10Chars);
}
internal u64
CharToUInt(char C, u64 Base)
{
- return CharToUInt(C, GetCharSetForBase(Base));
+ return CharToUInt(C, GetCharSetForBase(Base));
}
struct parse_uint_result
{
- b8 Success;
- u64 Value;
- u32 ParsedLength;
+ b8 Success;
+ u64 Value;
+ u32 ParsedLength;
};
internal parse_uint_result
ValidateAndParseUInt(gs_const_string String, u64 Base = 10)
{
- parse_uint_result Result = {0};
-
- gs_const_string CharSet = GetCharSetForBase(Base);
-
- bool StringIsValid = true;
- for (u32 i = 0; i < String.Length; i++)
+ parse_uint_result Result = {0};
+
+ gs_const_string CharSet = GetCharSetForBase(Base);
+
+ bool StringIsValid = true;
+ for (u32 i = 0; i < String.Length; i++)
+ {
+ if (!StringContains(CharSet, String.Str[i]))
{
- if (!StringContains(CharSet, String.Str[i]))
- {
- StringIsValid = false;
- break;
- }
+ StringIsValid = false;
+ break;
+ }
+ }
+
+ if (StringIsValid)
+ {
+ u64 Acc = 0;
+ u64 i = 0;
+ for (; i < String.Length; i++)
+ {
+ u64 CharIndex = FindFirst(CharSet, String.Str[i]);
+ if (CharIndex < CharSet.Length)
+ {
+ Acc = CharToUInt(String.Str[i], CharSet) + (Acc * Base);
+ }
+ else
+ {
+ break;
+ }
}
- if (StringIsValid)
- {
- u64 Acc = 0;
- u64 i = 0;
- for (; i < String.Length; i++)
- {
- u64 CharIndex = FindFirst(CharSet, String.Str[i]);
- if (CharIndex < CharSet.Length)
- {
- Acc = CharToUInt(String.Str[i], CharSet) + (Acc * Base);
- }
- else
- {
- break;
- }
- }
-
- Result.Success = true;
- Result.Value = Acc;
- Result.ParsedLength = i;
- }
-
- return Result;
+ Result.Success = true;
+ Result.Value = Acc;
+ Result.ParsedLength = i;
+ }
+
+ return Result;
}
internal u64
ParseUInt(gs_const_string String, u64 Base = 10, u64* ParsedLength = 0)
{
- parse_uint_result ParseResult = ValidateAndParseUInt(String, Base);
- Assert(ParseResult.Success);
- if (ParsedLength)
- {
- *ParsedLength = ParseResult.ParsedLength;
- }
- return ParseResult.Value;
+ parse_uint_result ParseResult = ValidateAndParseUInt(String, Base);
+ Assert(ParseResult.Success);
+ if (ParsedLength)
+ {
+ *ParsedLength = ParseResult.ParsedLength;
+ }
+ return ParseResult.Value;
}
internal u64
ParseUInt(u64 Length, char* String, u64 Base = 10, u64* ParsedLength = 0)
{
- return ParseUInt(ConstString(String, Length), Base, ParsedLength);
+ return ParseUInt(ConstString(String, Length), Base, ParsedLength);
}
internal u64
ParseUInt(char* String, u64 Base = 10, u64* ParsedLength = 0)
{
- return ParseUInt(LitString(String), Base, ParsedLength);
+ return ParseUInt(LitString(String), Base, ParsedLength);
}
internal s64
ParseInt(gs_const_string String, u64 Base = 10, u64* ParsedLength = 0)
{
- s64 Result = 0;
- u64 TempParsedLength = 0;
- if (String.Str[0] == '-')
- {
- Result = -1 * (s64)ParseUInt(GetStringAfter(String, 1), Base, &TempParsedLength);
- TempParsedLength += 1;
- }
- else
- {
- Result = (s64)ParseUInt(String, Base, &TempParsedLength);
- }
- if (ParsedLength != 0)
- {
- *ParsedLength = TempParsedLength;
- }
- return Result;
+ s64 Result = 0;
+ u64 TempParsedLength = 0;
+ if (String.Str[0] == '-')
+ {
+ Result = -1 * (s64)ParseUInt(GetStringAfter(String, 1), Base, &TempParsedLength);
+ TempParsedLength += 1;
+ }
+ else
+ {
+ Result = (s64)ParseUInt(String, Base, &TempParsedLength);
+ }
+ if (ParsedLength != 0)
+ {
+ *ParsedLength = TempParsedLength;
+ }
+ return Result;
}
internal s64
ParseInt(char* String, u64 Base = 10, u64* ParsedLength = 0)
{
- return ParseInt(LitString(String), Base, ParsedLength);
+ return ParseInt(LitString(String), Base, ParsedLength);
}
struct parse_float_result
{
- b8 Success;
- r64 Value;
- u64 ParsedLength;
+ b8 Success;
+ r64 Value;
+ u64 ParsedLength;
};
internal parse_float_result
ValidateAndParseFloat(gs_const_string String)
{
- parse_float_result Result = {0};
- Result.Success = false;
-
- // Validate
- bool StringIsValid = true;
- for (u64 i = 0; i < String.Length; i++)
+ parse_float_result Result = {0};
+ Result.Success = false;
+
+ // Validate
+ bool StringIsValid = true;
+ for (u64 i = 0; i < String.Length; i++)
+ {
+ if (!IsNumericDecimal(String.Str[i]) && String.Str[i] != '-')
{
- if (!IsNumericDecimal(String.Str[i]) && String.Str[i] != '-')
- {
- StringIsValid = false;
- break;
- }
+ StringIsValid = false;
+ break;
+ }
+ }
+
+ if (StringIsValid)
+ {
+ s64 DecimalIndex = FindFirst(String, '.');
+ u64 TempParsedLength = 0;
+ u64 PlacesAfterPoint = 0;
+
+ gs_const_string IntegerString = GetStringBefore(String, DecimalIndex);
+ gs_const_string DecimalString = {};
+ if (DecimalIndex >= 0)
+ {
+ DecimalString = GetStringAfter(String, DecimalIndex + 1);
}
- if (StringIsValid)
+ r32 Polarity = 1;
+ if (IntegerString.Str[0] == '-')
{
- u64 DecimalIndex = FindFirst(String, '.');
- u64 TempParsedLength = 0;
- u64 PlacesAfterPoint = 0;
-
- gs_const_string IntegerString = GetStringBefore(String, DecimalIndex);
- gs_const_string DecimalString = GetStringAfter(String, DecimalIndex + 1);
-
- r32 Polarity = 1;
- if (IntegerString.Str[0] == '-')
- {
- IntegerString = GetStringAfter(IntegerString, 1);
- Polarity = -1;
- }
-
- Result.Value = (r64)ParseInt(IntegerString, 10, &TempParsedLength);
-
- if (TempParsedLength == IntegerString.Length)
- {
- r64 AfterPoint = (r64)ParseUInt(DecimalString, 10, &PlacesAfterPoint);
- r64 Decimal = (AfterPoint / PowR64(10, PlacesAfterPoint));
- Result.Value = Result.Value + Decimal;
- Result.Value *= Polarity;
- }
-
- Result.ParsedLength = TempParsedLength + PlacesAfterPoint;
- if (DecimalIndex < String.Length) { Result.ParsedLength += 1; }
-
- Result.Success = true;
+ IntegerString = GetStringAfter(IntegerString, 1);
+ Polarity = -1;
}
- return Result;
+ Result.Value = (r64)ParseInt(IntegerString, 10, &TempParsedLength);
+
+ if (TempParsedLength == IntegerString.Length)
+ {
+ r64 AfterPoint = (r64)ParseUInt(DecimalString, 10, &PlacesAfterPoint);
+ r64 Decimal = (AfterPoint / PowR64(10, PlacesAfterPoint));
+ Result.Value = Result.Value + Decimal;
+ Result.Value *= Polarity;
+ }
+
+ Result.ParsedLength = TempParsedLength + PlacesAfterPoint;
+ if (DecimalIndex < (s64)String.Length) { Result.ParsedLength += 1; }
+
+ Result.Success = true;
+ }
+
+ return Result;
}
internal r64
ParseFloat(gs_const_string String, u64* ParsedLength = 0)
{
- parse_float_result Result = ValidateAndParseFloat(String);
- Assert(Result.Success);
- if (ParsedLength != 0)
- {
- *ParsedLength = Result.ParsedLength;
- }
- return Result.Value;
+ parse_float_result Result = ValidateAndParseFloat(String);
+ Assert(Result.Success);
+ if (ParsedLength != 0)
+ {
+ *ParsedLength = Result.ParsedLength;
+ }
+ return Result.Value;
}
internal r64
ParseFloat(char* String, u64* ParsedLength = 0)
{
- return ParseFloat(LitString(String), ParsedLength);
+ return ParseFloat(LitString(String), ParsedLength);
}
internal u64
AppendString(gs_string* Base, gs_const_string Appendix)
{
- u64 StartIndex = Base->Length;
- u64 LengthAvailable = Base->Size - Base->Length;
- u64 Written = 0;
- for (; Written < Min(LengthAvailable, Appendix.Length); Written++)
- {
- Base->Str[StartIndex + Written] = Appendix.Str[Written];
- }
- Base->Length += Written;
- Assert(Base->Length <= Base->Size);
- return Written;
+ u64 StartIndex = Base->Length;
+ u64 LengthAvailable = Base->Size - Base->Length;
+ u64 Written = 0;
+ for (; Written < Min(LengthAvailable, Appendix.Length); Written++)
+ {
+ Base->Str[StartIndex + Written] = Appendix.Str[Written];
+ }
+ Base->Length += Written;
+ Assert(Base->Length <= Base->Size);
+ return Written;
}
internal u64
AppendString(gs_string* Base, gs_string Appendix)
{
- return AppendString(Base, Appendix.ConstString);
+ return AppendString(Base, Appendix.ConstString);
}
+
+internal void
+InsertAt(gs_string* Str, u64 Index, char C)
+{
+ if (Str->Length > Index)
+ {
+ for (u64 i = Str->Length; i > Index; i--)
+ {
+ Str->Str[i] = Str->Str[i - 1];
+ }
+ }
+
+ if (Index < Str->Size)
+ {
+ Str->Str[Index] = C;
+ Str->Length += 1;
+ Assert(Str->Length < Str->Size);
+ }
+}
+
+internal void
+RemoveAt(gs_string* Str, u64 Index)
+{
+ if (Str->Length > 0 && Index < Str->Length)
+ {
+ for (u64 i = Index; i < Str->Length - 1; i++)
+ {
+ Str->Str[i] = Str->Str[i + 1];
+ }
+ Str->Length -= 1;
+ }
+}
+
internal void
NullTerminate(gs_string* String)
{
- if (String->Length < String->Size)
- {
- String->Str[String->Length] = 0;
- }
- else
- {
- String->Str[String->Length - 1] = 0;
- }
+ if (String->Length < String->Size)
+ {
+ String->Str[String->Length] = 0;
+ }
+ else
+ {
+ String->Str[String->Length - 1] = 0;
+ }
}
internal void
OutChar(gs_string* String, char C)
{
- if (String->Length < String->Size)
- {
- String->Str[String->Length++] = C;
- }
+ if (String->Length < String->Size)
+ {
+ String->Str[String->Length++] = C;
+ }
}
internal void
U64ToASCII(gs_string* String, u64 Value, u64 Base, gs_const_string Digits)
{
- u64 ValueRemaining = Value;
- u64 At = 0;
- do {
- u64 Index = ValueRemaining % Base;
- char Digit = Digits.Str[Index];
- OutChar(String, Digit);
- ValueRemaining /= Base;
- }while(ValueRemaining);
- char* End = String->Str + String->Length;
- ReverseStringInPlace(String);
+ u64 ValueRemaining = Value;
+ u64 At = 0;
+ do {
+ u64 Index = ValueRemaining % Base;
+ char Digit = Digits.Str[Index];
+ OutChar(String, Digit);
+ ValueRemaining /= Base;
+ }while(ValueRemaining);
+ char* End = String->Str + String->Length;
+ ReverseStringInPlace(String);
}
internal void
U64ToASCII(gs_string* String, u64 Value, u64 Base)
{
- U64ToASCII(String, Value, Base, GetCharSetForBase(Base));
+ U64ToASCII(String, Value, Base, GetCharSetForBase(Base));
}
internal void
R64ToASCII(gs_string* String, r64 Value, u64 Precision)
{
- if (Value < 0)
+ if (Value < 0)
+ {
+ OutChar(String, '-');
+ Value = Abs(Value);
+ }
+ u64 IntegerPart = (u64)Value;
+ // NOTE(Peter): If we don't use the inner string, when U64ToASCII reverses the characters
+ // it'll put the negative sign at the end.
+ gs_string IntegerString = GetStringAfter(*String, String->Length);
+ U64ToASCII(&IntegerString, IntegerPart, 10);
+ String->Length += IntegerString.Length;
+ Value -= IntegerPart;
+ if (Value > 0)
+ {
+ OutChar(String, '.');
+ for (u64 i = 0; i < Precision; i++)
{
- OutChar(String, '-');
- Value = Abs(Value);
- }
- u64 IntegerPart = (u64)Value;
- // NOTE(Peter): If we don't use the inner string, when U64ToASCII reverses the characters
- // it'll put the negative sign at the end.
- gs_string IntegerString = GetStringAfter(*String, String->Length);
- U64ToASCII(&IntegerString, IntegerPart, 10);
- String->Length += IntegerString.Length;
- Value -= IntegerPart;
- if (Value > 0)
- {
- OutChar(String, '.');
- for (u64 i = 0; i < Precision; i++)
- {
- Value *= 10.0f;
- u64 DecimalPlace = (u64)Value;
- Value -= DecimalPlace;
- OutChar(String, Base10Chars.Str[DecimalPlace]);
- }
+ Value *= 10.0f;
+ u64 DecimalPlace = (u64)Value;
+ Value -= DecimalPlace;
+ OutChar(String, Base10Chars.Str[DecimalPlace]);
}
+ }
}
internal s64
ReadVarArgsSignedInteger (s32 Width, va_list* Args)
{
- s64 Result = 0;
- switch (Width)
- {
- // NOTE(Peter): For Width lower than 4 bytes, the C++ spec specifies
- // that it will get promoted to an int anyways
- case 1: { Result = (s64)va_arg(*Args, s32); } break;
- case 2: { Result = (s64)va_arg(*Args, s32); } break;
- case 4: { Result = (s64)va_arg(*Args, s32); } break;
- case 8: { Result = (s64)va_arg(*Args, s64); } break;
- InvalidDefaultCase;
- }
- return Result;
+ s64 Result = 0;
+ switch (Width)
+ {
+ // NOTE(Peter): For Width lower than 4 bytes, the C++ spec specifies
+ // that it will get promoted to an int anyways
+ case 1: { Result = (s64)va_arg(*Args, s32); } break;
+ case 2: { Result = (s64)va_arg(*Args, s32); } break;
+ case 4: { Result = (s64)va_arg(*Args, s32); } break;
+ case 8: { Result = (s64)va_arg(*Args, s64); } break;
+ InvalidDefaultCase;
+ }
+ return Result;
}
internal r64
ReadVarArgsUnsignedInteger (s32 Width, va_list* Args)
{
- u64 Result = 0;
- switch (Width)
- {
- // NOTE(Peter): For Width lower than 4 bytes, the C++ spec specifies
- // that it will get promoted to an int anyways
- case 1: { Result = (u64)va_arg(*Args, u32); } break;
- case 2: { Result = (u64)va_arg(*Args, u32); } break;
- case 4: { Result = (u64)va_arg(*Args, u32); } break;
- case 8: { Result = (u64)va_arg(*Args, u64); } break;
- InvalidDefaultCase;
- }
- return Result;
+ u64 Result = 0;
+ switch (Width)
+ {
+ // NOTE(Peter): For Width lower than 4 bytes, the C++ spec specifies
+ // that it will get promoted to an int anyways
+ case 1: { Result = (u64)va_arg(*Args, u32); } break;
+ case 2: { Result = (u64)va_arg(*Args, u32); } break;
+ case 4: { Result = (u64)va_arg(*Args, u32); } break;
+ case 8: { Result = (u64)va_arg(*Args, u64); } break;
+ InvalidDefaultCase;
+ }
+ return Result;
}
internal r64
ReadVarArgsFloat (s32 Width, va_list* Args)
{
- r64 Result = 0;
- switch (Width)
- {
- case 4: { Result = (r64)va_arg(*Args, r64); } break;
- case 8: { Result = (r64)va_arg(*Args, r64); } break;
- InvalidDefaultCase;
- }
- return Result;
+ r64 Result = 0;
+ switch (Width)
+ {
+ case 4: { Result = (r64)va_arg(*Args, r64); } break;
+ case 8: { Result = (r64)va_arg(*Args, r64); } break;
+ InvalidDefaultCase;
+ }
+ return Result;
}
internal s32
PrintFArgsList (gs_string* String, char* Format, va_list Args)
{
- char* FormatAt = Format;
- while (*FormatAt)
+ char* FormatAt = Format;
+ while (*FormatAt)
+ {
+ if (FormatAt[0] != '%')
{
- if (FormatAt[0] != '%')
- {
- if (FormatAt[0] == '\\')
- {
- OutChar(String, *FormatAt++);
- }
- else
- {
- OutChar(String, *FormatAt++);
- }
- }
- else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol
- {
- OutChar(String, '%');
- FormatAt += 2;
- }
- else
- {
- FormatAt++;
-
- // Flags
- if (FormatAt[0] == '-')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == '+')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == ' ')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == '#')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == '0')
- {
- FormatAt++;
- }
-
- // Width
- b32 WidthSpecified = false;
- s32 Width = 0;
-
- if (IsBase10(FormatAt[0]))
- {
- WidthSpecified = true;
- u64 Parsed = 0;
- AssertMessage("ParseInt assumes whole string is an integer");
- Width = (s32)ParseInt(FormatAt, 10, &Parsed);
- FormatAt += Parsed;
- }
- else if (FormatAt[0] == '*')
- {
- WidthSpecified = true;
- Width = va_arg(Args, s32);
- Assert(Width >= 0);
- FormatAt++;
- }
-
- // Precision
- b32 PrecisionSpecified = false;
- s32 Precision = 0;
-
- if (FormatAt[0] == '.')
- {
- FormatAt++;
- if (IsBase10(FormatAt[0]))
- {
- PrecisionSpecified = true;
-
- gs_const_string PrecisionStr = {};
- PrecisionStr.Str = FormatAt;
- for (char* C = FormatAt; *FormatAt && IsBase10(*C); C++)
- {
- PrecisionStr.Length++;
- }
- u64 Parsed = 0;
- Precision = (s32)ParseInt(PrecisionStr, 10, &Parsed);
- FormatAt += Parsed;
- }
- else if (FormatAt[0] == '*')
- {
- PrecisionSpecified = true;
- Precision = va_arg(Args, s32);
- Assert(Precision >= 0);
- FormatAt++;
- }
- }
-
- // Length
- b32 LengthSpecified = false;
- s32 Length = 4;
-
- if (FormatAt[0] == 'h' && FormatAt[1] == 'h')
- {
- LengthSpecified = true;
- Length = 1;
- FormatAt += 2;
- }
- else if (FormatAt[0] == 'h')
- {
- LengthSpecified = true;
- Length = 2;
- FormatAt++;
- }
- else if (FormatAt[0] == 'l' && FormatAt[1] == 'l')
- {
- LengthSpecified = true;
- Length = 8;
- FormatAt += 2;
- }
- else if (FormatAt[0] == 'l')
- {
- LengthSpecified = true;
- Length = 4;
- FormatAt++;
- }
- else if (FormatAt[0] == 'j')
- {
- LengthSpecified = true;
- Length = 8;
- FormatAt++;
- }
- else if (FormatAt[0] == 'z')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == 't')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == 'L')
- {
- FormatAt++;
- }
-
- // Format Specifiers
- gs_string StringRemaining = GetStringAfter(*String, String->Length);
- Assert(StringRemaining.Length == 0);
- if (FormatAt[0] == 'd' || FormatAt[0] == 'i')
- {
- s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args);
- if (SignedInt < 0)
- {
- OutChar(&StringRemaining, '-');
- SignedInt *= -1;
- }
- U64ToASCII(&StringRemaining, (u64)SignedInt, 10, Base10Chars);
- }
- else if (FormatAt[0] == 'u')
- {
- u64 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
- U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars);
- }
- else if (FormatAt[0] == 'o')
- {
- u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
- U64ToASCII(&StringRemaining, UnsignedInt, 8, Base8Chars);
- }
- else if (FormatAt[0] == 'x' || FormatAt[0] == 'X')
- {
- u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
- U64ToASCII(&StringRemaining, UnsignedInt, 16, Base16Chars);
- }
- else if (FormatAt[0] == 'f' || FormatAt[0] == 'F')
- {
- r64 Float = ReadVarArgsFloat(Length, &Args);
- s32 AfterPoint = 6;
- if (PrecisionSpecified)
- {
- AfterPoint = Precision;
- }
- R64ToASCII(&StringRemaining, Float, AfterPoint);
- }
- else if (FormatAt[0] == 'c')
- {
- char InsertChar = va_arg(Args, s32);
- OutChar(&StringRemaining, InsertChar);
- }
- else if (FormatAt[0] == 's')
- {
- char* InsertString = va_arg(Args, char*);
-
- s32 InsertStringLength = CStringLength(InsertString);
- if (PrecisionSpecified)
- {
- InsertStringLength = Min(InsertStringLength, Precision);
- }
- InsertStringLength = Min(StringSizeLeft(StringRemaining), InsertStringLength);
-
- for (s32 c = 0; c < InsertStringLength; c++)
- {
- OutChar(&StringRemaining, InsertString[c]);
- }
- }
- else if (FormatAt[0] == 'S')
- {
- gs_const_string InsertString = va_arg(Args, gs_const_string);
-
- for (s32 c = 0; c < InsertString.Length; c++)
- {
- OutChar(&StringRemaining, InsertString.Str[c]);
- }
- }
- else if (FormatAt[0] == 'p')
- {
- // TODO(Peter): Pointer Address
- }
- else
- {
- // NOTE(Peter): Non-specifier character found
- InvalidCodePath;
- }
-
- String->Length += StringRemaining.Length;
- FormatAt++;
- }
+ if (FormatAt[0] == '\\')
+ {
+ OutChar(String, *FormatAt++);
+ }
+ else
+ {
+ OutChar(String, *FormatAt++);
+ }
}
-
- return String->Length;
+ else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol
+ {
+ OutChar(String, '%');
+ FormatAt += 2;
+ }
+ else
+ {
+ FormatAt++;
+
+ // Flags
+ if (FormatAt[0] == '-')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == '+')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == ' ')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == '#')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == '0')
+ {
+ FormatAt++;
+ }
+
+ // Width
+ b32 WidthSpecified = false;
+ s32 Width = 0;
+
+ if (IsBase10(FormatAt[0]))
+ {
+ WidthSpecified = true;
+ u64 Parsed = 0;
+ AssertMessage("ParseInt assumes whole string is an integer");
+ Width = (s32)ParseInt(FormatAt, 10, &Parsed);
+ FormatAt += Parsed;
+ }
+ else if (FormatAt[0] == '*')
+ {
+ WidthSpecified = true;
+ Width = va_arg(Args, s32);
+ Assert(Width >= 0);
+ FormatAt++;
+ }
+
+ // Precision
+ b32 PrecisionSpecified = false;
+ s32 Precision = 0;
+
+ if (FormatAt[0] == '.')
+ {
+ FormatAt++;
+ if (IsBase10(FormatAt[0]))
+ {
+ PrecisionSpecified = true;
+
+ gs_const_string PrecisionStr = {};
+ PrecisionStr.Str = FormatAt;
+ for (char* C = FormatAt; *FormatAt && IsBase10(*C); C++)
+ {
+ PrecisionStr.Length++;
+ }
+ u64 Parsed = 0;
+ Precision = (s32)ParseInt(PrecisionStr, 10, &Parsed);
+ FormatAt += Parsed;
+ }
+ else if (FormatAt[0] == '*')
+ {
+ PrecisionSpecified = true;
+ Precision = va_arg(Args, s32);
+ Assert(Precision >= 0);
+ FormatAt++;
+ }
+ }
+
+ // Length
+ b32 LengthSpecified = false;
+ s32 Length = 4;
+
+ if (FormatAt[0] == 'h' && FormatAt[1] == 'h')
+ {
+ LengthSpecified = true;
+ Length = 1;
+ FormatAt += 2;
+ }
+ else if (FormatAt[0] == 'h')
+ {
+ LengthSpecified = true;
+ Length = 2;
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 'l' && FormatAt[1] == 'l')
+ {
+ LengthSpecified = true;
+ Length = 8;
+ FormatAt += 2;
+ }
+ else if (FormatAt[0] == 'l')
+ {
+ LengthSpecified = true;
+ Length = 4;
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 'j')
+ {
+ LengthSpecified = true;
+ Length = 8;
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 'z')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 't')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 'L')
+ {
+ FormatAt++;
+ }
+
+ // Format Specifiers
+ gs_string StringRemaining = GetStringAfter(*String, String->Length);
+ Assert(StringRemaining.Length == 0);
+ if (FormatAt[0] == 'd' || FormatAt[0] == 'i')
+ {
+ s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args);
+ if (SignedInt < 0)
+ {
+ OutChar(&StringRemaining, '-');
+ SignedInt *= -1;
+ }
+ U64ToASCII(&StringRemaining, (u64)SignedInt, 10, Base10Chars);
+ }
+ else if (FormatAt[0] == 'u')
+ {
+ u64 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
+ U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars);
+ }
+ else if (FormatAt[0] == 'o')
+ {
+ u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
+ U64ToASCII(&StringRemaining, UnsignedInt, 8, Base8Chars);
+ }
+ else if (FormatAt[0] == 'x' || FormatAt[0] == 'X')
+ {
+ u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
+ U64ToASCII(&StringRemaining, UnsignedInt, 16, Base16Chars);
+ }
+ else if (FormatAt[0] == 'f' || FormatAt[0] == 'F')
+ {
+ r64 Float = ReadVarArgsFloat(Length, &Args);
+ s32 AfterPoint = 6;
+ if (PrecisionSpecified)
+ {
+ AfterPoint = Precision;
+ }
+ R64ToASCII(&StringRemaining, Float, AfterPoint);
+ }
+ else if (FormatAt[0] == 'c')
+ {
+ char InsertChar = va_arg(Args, s32);
+ OutChar(&StringRemaining, InsertChar);
+ }
+ else if (FormatAt[0] == 's')
+ {
+ char* InsertString = va_arg(Args, char*);
+
+ s32 InsertStringLength = CStringLength(InsertString);
+ if (PrecisionSpecified)
+ {
+ InsertStringLength = Min(InsertStringLength, Precision);
+ }
+ InsertStringLength = Min(StringSizeLeft(StringRemaining), InsertStringLength);
+
+ for (s32 c = 0; c < InsertStringLength; c++)
+ {
+ OutChar(&StringRemaining, InsertString[c]);
+ }
+ }
+ else if (FormatAt[0] == 'S')
+ {
+ gs_const_string InsertString = va_arg(Args, gs_const_string);
+
+ for (s32 c = 0; c < InsertString.Length; c++)
+ {
+ OutChar(&StringRemaining, InsertString.Str[c]);
+ }
+ }
+ else if (FormatAt[0] == 'p')
+ {
+ // TODO(Peter): Pointer Address
+ }
+ else
+ {
+ // NOTE(Peter): Non-specifier character found
+ InvalidCodePath;
+ }
+
+ String->Length += StringRemaining.Length;
+ FormatAt++;
+ }
+ }
+
+ return String->Length;
}
internal void
PrintF (gs_string* String, char* Format, ...)
{
- va_list Args;
- va_start(Args, Format);
- String->Length = 0;
- PrintFArgsList(String, Format, Args);
- va_end(Args);
+ va_list Args;
+ va_start(Args, Format);
+ String->Length = 0;
+ PrintFArgsList(String, Format, Args);
+ va_end(Args);
}
internal void
PrintF (gs_string* String, const char* Format, ...)
{
- // NOTE(Peter): This variant is here for clang/gcc - C++ spec doesn't allow
- // implicit conversion from a const char* (a static c string) to char*, so this
- // version of the function just provides the conversion so the compiler will be quiet
- // without removing the other implementation, which is more useful
- va_list Args;
- va_start(Args, Format);
- String->Length = 0;
- PrintFArgsList(String, (char*)Format, Args);
- va_end(Args);
+ // NOTE(Peter): This variant is here for clang/gcc - C++ spec doesn't allow
+ // implicit conversion from a const char* (a static c string) to char*, so this
+ // version of the function just provides the conversion so the compiler will be quiet
+ // without removing the other implementation, which is more useful
+ va_list Args;
+ va_start(Args, Format);
+ String->Length = 0;
+ PrintFArgsList(String, (char*)Format, Args);
+ va_end(Args);
}
internal void
AppendPrintF (gs_string* String, char* Format, ...)
{
- va_list Args;
- va_start(Args, Format);
- PrintFArgsList(String, Format, Args);
- va_end(Args);
+ va_list Args;
+ va_start(Args, Format);
+ PrintFArgsList(String, Format, Args);
+ va_end(Args);
}
internal void
AppendPrintF (gs_string* String, const char* Format, ...)
{
- // NOTE(Peter): This variant is here for clang/gcc - C++ spec doesn't allow
- // implicit conversion from a const char* (a static c string) to char*, so this
- // version of the function just provides the conversion so the compiler will be quiet
- // without removing the other implementation, which is more useful
- va_list Args;
- va_start(Args, Format);
- PrintFArgsList(String, (char*)Format, Args);
- va_end(Args);
+ // NOTE(Peter): This variant is here for clang/gcc - C++ spec doesn't allow
+ // implicit conversion from a const char* (a static c string) to char*, so this
+ // version of the function just provides the conversion so the compiler will be quiet
+ // without removing the other implementation, which is more useful
+ va_list Args;
+ va_start(Args, Format);
+ PrintFArgsList(String, (char*)Format, Args);
+ va_end(Args);
}
///////////////////////////
@@ -2307,356 +2417,44 @@ AppendPrintF (gs_string* String, const char* Format, ...)
internal gs_data
CreateData(u8* Memory, u64 Size)
{
- gs_data Result = {Memory, Size};
- return Result;
+ gs_data Result = {Memory, Size};
+ return Result;
}
internal bool
DataIsNonEmpty(gs_data Data)
{
- return ((Data.Size > 0) && (Data.Memory != 0));
+ return ((Data.Size > 0) && (Data.Memory != 0));
}
-internal void* AllocatorAlloc_NoOp(u64 Size, u64* SizeResult) {
- *SizeResult = 0;
- return 0;
-}
-internal void AllocatorFree_NoOp(void* Base, u64 Size) { return; }
-
-internal gs_allocator
-CreateAllocator_(allocator_allocate* Alloc, allocator_free* Free)
-{
- if (Alloc == 0)
- {
- Alloc = AllocatorAlloc_NoOp;
- }
- if (Free == 0)
- {
- Free = AllocatorFree_NoOp;
- }
- gs_allocator Result = {0};
- Result.Alloc = Alloc;
- Result.Free = Free;
- return Result;
-}
-#define CreateAllocator(a, f) CreateAllocator_((allocator_allocate*)(a), (allocator_free*)(f))
-
-internal gs_data
-AllocatorAlloc_(gs_allocator Allocator, u64 Size, char* Location)
-{
- // TODO(Peter): Memory Profiling with Location
- u64 SizeResult = 0;
- void* Memory = Allocator.Alloc(Size, &SizeResult);
- return CreateData((u8*)Memory, SizeResult);
-}
-internal void
-AllocatorFree_(gs_allocator Allocator, void* Base, u64 Size, char* Location)
-{
- // TODO(Peter): Memory Profiling with Location
- if (Base != 0 && Size != 0)
- {
- Allocator.Free(Base, Size);
- }
-}
-
-#define AllocatorAlloc(alloc,size) AllocatorAlloc_((alloc), (size), FileNameAndLineNumberString)
-#define AllocatorAllocStruct(alloc, type) (type*)(AllocatorAlloc((alloc), sizeof(type)).Memory)
-#define AllocatorAllocArray(alloc, type, count) (type*)(AllocatorAlloc((alloc), sizeof(type) * (count)).Memory)
-#define AllocatorAllocString(alloc, size) gs_string{ AllocatorAllocArray((alloc), char, (size)), 0, (size) }
-#define AllocatorFree(alloc,base,size) AllocatorFree_((alloc), (base), (size), FileNameAndLineNumberString)
-#define AllocatorFreeArray(alloc,base,type,count) AllocatorFree_((alloc), (base), sizeof(type) * count, FileNameAndLineNumberString)
-internal gs_memory_cursor
-CreateMemoryCursor(u8* Base, u64 Size)
-{
- gs_memory_cursor Result = {0};
- Result.Data.Memory = Base;
- Result.Data.Size = Size;
- return Result;
-};
-internal gs_memory_cursor
-CreateMemoryCursor(gs_data Data)
-{
- return CreateMemoryCursor(Data.Memory, Data.Size);
-}
-internal gs_memory_cursor
-CreateMemoryCursor(gs_allocator Allocator, u64 Size)
-{
- gs_data Data = AllocatorAlloc(Allocator, Size);
- return CreateMemoryCursor(Data);
-}
-internal bool
-CursorHasRoom(gs_memory_cursor Cursor, u64 Size)
-{
- bool Result = ((Cursor.Position + Size) <= Cursor.Data.Size);
- return Result;
-}
-internal gs_data
-PushSizeOnCursor_(gs_memory_cursor* Cursor, u64 Size, char* Location)
-{
- gs_data Result = {0};
- if (CursorHasRoom(*Cursor, Size))
- {
- Result.Memory = Cursor->Data.Memory + Cursor->Position;
- Result.Size = Size;
- Cursor->Position += Size;
- }
- return Result;
-}
-
-#define PushSizeOnCursor(cursor,size) PushSizeOnCursor_((cursor), (size), FileNameAndLineNumberString)
-#define PushStructOnCursor(cursor,type) (type*)PushSizeOnCursor_((cursor), sizeof(type), FileNameAndLineNumberString).Memory
-#define PushArrayOnCursor(cursor,type,count) (type*)PushSizeOnCursor_((cursor), sizeof(type) * (count), FileNameAndLineNumberString).Memory
-
-#define MemoryCursor_WriteValue(cursor, type, value) *PushStructOnCursor(cursor, type) = value
-#define MemoryCursor_WriteBuffer(cursor, buf, len) CopyMemoryTo((u8*)(buf), PushArrayOnCursor((cursor), u8, (len)), (len))
-
-internal void
-PopSizeOnCursor(gs_memory_cursor* Cursor, u64 Size)
-{
- if (Cursor->Position > Size)
- {
- Cursor->Position -= Size;
- }
- else
- {
- Cursor->Position = 0;
- }
-}
-internal gs_data
-AlignCursor(gs_memory_cursor* Cursor, u64 Alignment)
-{
- u64 Position = RoundUpTo64(Cursor->Position, Alignment);
- Position = Min(Position, Cursor->Data.Size);
- u64 NewSize = Position - Cursor->Position;
- return PushSizeOnCursor(Cursor, NewSize);
-}
-internal void
-ClearCursor(gs_memory_cursor* Cursor)
-{
- Cursor->Position = 0;
-}
-
-internal void
-FreeCursorListEntry(gs_allocator Allocator, gs_memory_cursor_list* CursorEntry)
-{
- AllocatorFree(Allocator, CursorEntry, CursorEntry->Cursor.Data.Size + sizeof(gs_memory_cursor));
-}
-
-internal gs_memory_arena
-CreateMemoryArena_(arena_type ArenaType, gs_allocator Allocator, u64 ChunkSize, u64 Alignment, gs_memory_arena* ParentArena)
-{
- // we only want a parent arena if the type is Arena_SubArena
- Assert(((ArenaType == Arena_BaseArena) && (ParentArena == 0)) ||
- ((ArenaType == Arena_SubArena) && (ParentArena != 0)));
-
- gs_memory_arena Arena = {};
- Arena.Type = ArenaType;
- Arena.Allocator = Allocator;
- Arena.Parent = ParentArena;
- Arena.MemoryChunkSize = ChunkSize;
- Arena.MemoryAlignment = Alignment;
- return Arena;
-}
-
-internal gs_memory_arena
-CreateMemoryArena(gs_allocator Allocator, u64 ChunkSize = KB(32), u64 Alignment = Bytes(8))
-{
- return CreateMemoryArena_(Arena_BaseArena, Allocator, ChunkSize, Alignment, 0);
-}
-internal gs_memory_arena
-CreateMemorySubArena(gs_memory_arena* Parent, u64 ChunkSize = KB(32), u64 Alignment = Bytes(8))
-{
- return CreateMemoryArena_(Arena_SubArena, Parent->Allocator, ChunkSize, Alignment, Parent);
-}
-
-internal gs_data PushSize_(gs_memory_arena* Arena, u64 Size, char* Location);
-
-internal void
-FreeCursorList(gs_memory_cursor_list* List, gs_allocator Allocator)
-{
- gs_memory_cursor_list* CursorAt = List;
- while (CursorAt != 0)
- {
- gs_memory_cursor_list* Prev = CursorAt->Prev;
- FreeCursorListEntry(Allocator, CursorAt);
- CursorAt = Prev;
- }
-}
-
-internal gs_memory_cursor_list*
-MemoryArenaNewCursor(gs_memory_arena* Arena, u64 MinSize, char* Location)
-{
- // Allocate enough spcae for the minimum size needed + sizeo for the cursor list
- u64 AllocSize = Max(MinSize, Arena->MemoryChunkSize) + sizeof(gs_memory_cursor_list);
-
- gs_data Data = {0};
- switch (Arena->Type)
- {
- case Arena_SubArena:
- {
- Data = PushSize_(Arena->Parent, AllocSize, Location);
- }break;
-
- case Arena_BaseArena:
- {
- Data = AllocatorAlloc_(Arena->Allocator, AllocSize, Location);
- }break;
-
- InvalidDefaultCase;
- }
-
- // Fit the memory cursor into the region allocated
- Assert(MinSize + sizeof(gs_memory_cursor_list) <= Data.Size);
- gs_memory_cursor_list* Result = (gs_memory_cursor_list*)Data.Memory;
- u8* CursorMemoryStart = (u8*)(Result + 1);
- u64 CursorMemorySize = Data.Size - sizeof(gs_memory_cursor_list);
- Result->Cursor = CreateMemoryCursor(CursorMemoryStart, CursorMemorySize);
-
- Result->Prev = Arena->CursorList;
- Result->Next = 0;
- if (Arena->CursorList != 0)
- {
- Arena->CursorList->Next = Result;
- }
- Arena->CursorList = Result;
- return Result;
-}
-
-internal gs_data
-PushSize_(gs_memory_arena* Arena, u64 Size, char* Location)
-{
- gs_data Result = {0};
- if (Size > 0)
- {
- gs_memory_cursor_list* CursorEntry = Arena->CursorList;
- if (CursorEntry == 0)
- {
- CursorEntry = MemoryArenaNewCursor(Arena, Size, Location);
- }
- if (!CursorHasRoom(CursorEntry->Cursor, Size))
- {
- while ((CursorEntry != 0) && !CursorHasRoom(CursorEntry->Cursor, Size))
- {
- CursorEntry = CursorEntry->Next;
- }
- if (CursorEntry == 0)
- {
- CursorEntry = MemoryArenaNewCursor(Arena, Size, Location);
- }
- }
- Assert(CursorEntry != 0);
- Result = PushSizeOnCursor_(&CursorEntry->Cursor, Size, Location);
- Assert(Result.Memory != 0);
-
- gs_data Alignment = AlignCursor(&CursorEntry->Cursor, Arena->MemoryAlignment);
- Result.Size += Alignment.Size;
- }
-
- // TODO(Peter): @Cleanup @Efficiency
- // There is a case I want to handle at some point:
- // You have a Cursor that is empty, but the size you want to allocate is bigger
- // than the cursor. So you create a new cursor, of the exact size you need,
- // immediately fill it up, and push it onto the head of the cursor list. Now,
- // the list looks like this:
- // [root] [cursor] ... [empty cursor] [full cursor] [new, empty cursor]
- // and you'll never use the memory in 'empty cursor'
- // What I'd like to do is, when you fill up a cursor, it gets pushed back until
- // the next cursor is more full
- // NOTE: Thought on this tho - you don't want this behavior in a scratch arena
- // where usage across frames could change drastically. This matters way more in
- // a permanent storage (i think)
- return Result;
-}
-
-internal void
-PopSize(gs_memory_arena* Arena, u64 Size)
-{
- gs_allocator Allocator = Arena->Allocator;
- gs_memory_cursor_list* CursorEntry = Arena->CursorList;
- for (gs_memory_cursor_list* Prev = 0;
- CursorEntry != 0 && Size != 0;
- CursorEntry = Prev)
- {
- Prev = CursorEntry->Prev;
- if (Size >= CursorEntry->Cursor.Position)
- {
- Size -= CursorEntry->Cursor.Position;
- FreeCursorListEntry(Allocator, CursorEntry);
- }
- else
- {
- PopSizeOnCursor(&CursorEntry->Cursor, Size);
- break;
- }
- }
- Arena->CursorList = CursorEntry;
-}
-internal void
-FreeMemoryArena(gs_memory_arena* Arena)
-{
- gs_allocator Allocator = Arena->Allocator;
- gs_memory_cursor_list* CursorEntry = Arena->CursorList;
- for (gs_memory_cursor_list* Prev = 0;
- CursorEntry != 0;
- CursorEntry = Prev)
- {
- Prev = CursorEntry->Prev;
- if (CursorEntry != 0)
- {
- FreeCursorListEntry(Allocator, CursorEntry);
- }
- }
-}
-
-#define PushSizeToData(arena, size) PushSize_((arena), (size), FileNameAndLineNumberString)
-#define PushSize(arena, size) PushSize_((arena), (size), FileNameAndLineNumberString).Memory
-#define PushStruct(arena, type) (type*)(PushSize_((arena), sizeof(type), FileNameAndLineNumberString).Memory)
-#define PushArray(arena, type, count) (type*)(PushSize_((arena), sizeof(type) * (count), FileNameAndLineNumberString).Memory)
-#define PushString(arena, length) MakeString(PushArray((arena), char, (length)), 0, (length));
-
+#define PushStringF(a,l,f,...) PushStringF_((a),(l),(f), DEBUG_LOC, __VA_ARGS__)
internal gs_string
-PushStringF(gs_memory_arena* Arena, u32 MaxLength, char* Format, ...)
+PushStringF_(gs_memory_arena* Arena, u32 MaxLength, char* Format, gs_debug_loc Loc, ...)
{
- gs_string Result = PushString(Arena, MaxLength);
-
- va_list Args;
- va_start(Args, Format);
- PrintFArgsList(&Result, Format, Args);
- va_end(Args);
-
- return Result;
+ gs_string Result = gs_string {
+ (char*)PushSize_(Arena, sizeof(char) * MaxLength, Loc).Memory, // Str
+ 0, // Length
+ MaxLength, // Size
+ };
+
+ va_list Args;
+ va_start(Args, Loc);
+ PrintFArgsList(&Result, Format, Args);
+ va_end(Args);
+
+ return Result;
}
internal gs_string
PushStringCopy(gs_memory_arena* Arena, gs_const_string String)
{
- gs_string Result = PushString(Arena, String.Length);
- Result.Size = String.Length;
- Result.Length = String.Length;
- for (u32 i = 0; i < String.Length; i++)
- {
- Result.Str[i] = String.Str[i];
- }
- return Result;
-}
-
-internal void
-ClearArena(gs_memory_arena* Arena)
-{
- gs_memory_cursor_list* First = 0;
- for (gs_memory_cursor_list* CursorEntry = Arena->CursorList;
- CursorEntry != 0;
- CursorEntry = CursorEntry->Prev)
- {
- First = CursorEntry;
- CursorEntry->Cursor.Position = 0;
- }
- Arena->CursorList = First;
-}
-
-internal void
-FreeArena(gs_memory_arena* Arena)
-{
- FreeCursorList(Arena->CursorList, Arena->Allocator);
+ gs_string Result = PushString(Arena, String.Length);
+ Result.Size = String.Length;
+ Result.Length = String.Length;
+ for (u32 i = 0; i < String.Length; i++)
+ {
+ Result.Str[i] = String.Str[i];
+ }
+ return Result;
}
///////////////////////////
@@ -2666,162 +2464,29 @@ FreeArena(gs_memory_arena* Arena)
inline void
DebugPrint(debug_output Output, gs_const_string Message)
{
- Output.Print(Output, Message);
+ Output.Print(Output, Message);
}
inline void
DebugPrint(debug_output Output, char* Message)
{
- gs_const_string String = ConstString(Message);
- Output.Print(Output, String);
+ gs_const_string String = ConstString(Message);
+ Output.Print(Output, String);
}
internal void
DebugPrintF(debug_output Output, char* Format, ...)
{
- gs_string Message = PushString(Output.Storage, 1024);
- va_list Args;
- va_start(Args, Format);
- PrintFArgsList(&Message, Format, Args);
- NullTerminate(&Message);
- Output.Print(Output, Message.ConstString);
-}
-
-///////////////////////////
-//
-// Dynamic Array
-
-internal gs_dynarray
-CreateDynarrayWithStorage(gs_memory_arena Storage, u32 ElementSize, u32 ElementsPerBuffer)
-{
- gs_dynarray Result = {};
- Result.Arena = Storage;
- Result.ElementSize = ElementSize;
- Result.ElementsPerBuffer = ElementsPerBuffer;
- Result.ElementCount = 0;
- return Result;
-}
-
-internal gs_dynarray
-CreateDynarray_(gs_allocator Allocator, u32 ElementSize, u32 ElementsPerBuffer)
-{
- gs_memory_arena Storage = CreateMemoryArena(Allocator, ElementSize * ElementsPerBuffer);
- return CreateDynarrayWithStorage(Storage, ElementSize, ElementsPerBuffer);
-};
-
-internal gs_dynarray
-CreateDynarray_(gs_memory_arena* Arena, u32 ElementSize, u32 ElementsPerBuffer)
-{
- gs_memory_arena Storage = CreateMemorySubArena(Arena, ElementSize * ElementsPerBuffer);
- return CreateDynarrayWithStorage(Storage, ElementSize, ElementsPerBuffer);
-};
-
-internal u64
-CalculateBufferSize(gs_dynarray Array)
-{
- u64 Result = Array.ElementsPerBuffer * Array.ElementSize;
- return Result;
-}
-
-internal void
-GrowDynarray(gs_dynarray* Array)
-{
- gs_dynarray_buffer* OldBufferList = Array->Buffers;
- u64 NewBuffersCount = Array->BuffersCount + 1;
- gs_dynarray_buffer* NewBufferList = AllocatorAllocArray(Array->Arena.Allocator, gs_dynarray_buffer, NewBuffersCount);
- if (OldBufferList)
- {
- CopyArray(OldBufferList, NewBufferList, gs_dynarray_buffer, Array->BuffersCount);
- AllocatorFree(Array->Arena.Allocator, OldBufferList, sizeof(gs_dynarray_buffer) * Array->BuffersCount);
- }
- u64 BufferSize = CalculateBufferSize(*Array);
- NewBufferList[Array->BuffersCount].Memory = PushSize(&Array->Arena, BufferSize);
- Array->Buffers = NewBufferList;
- Array->BuffersCount = NewBuffersCount;
-}
-internal u64
-DynarraySize(gs_dynarray Array)
-{
- u64 Result = Array.BuffersCount * Array.ElementsPerBuffer;
- return Result;
-}
-internal gs_dynarray_handle
-IndexToHandle(gs_dynarray* Array, u64 Index)
-{
- gs_dynarray_handle Result = {0};
- Result.BufferIndex = Index / Array->ElementsPerBuffer;
- Result.IndexInBuffer = Index % Array->ElementsPerBuffer;
- return Result;
-}
-internal u64
-HandleToIndex(gs_dynarray Array, gs_dynarray_handle Handle)
-{
- u64 Result = Handle.IndexInBuffer + (Handle.BufferIndex * Array.ElementsPerBuffer);
- return Result;
-}
-internal gs_dynarray_handle
-TakeFreeElement(gs_dynarray* Array)
-{
- gs_dynarray_handle Result = {0};
- if (Array->ElementCount >= DynarraySize(*Array))
- {
- GrowDynarray(Array);
- }
- Assert(Array->ElementCount < DynarraySize(*Array));
- u64 ElementIndex = Array->ElementCount++;
- Result = IndexToHandle(Array, ElementIndex);
- return Result;
-}
-internal bool
-HandleIsValid(gs_dynarray Array, gs_dynarray_handle Handle)
-{
- bool Result = Handle.IndexInBuffer < Array.ElementsPerBuffer;
- Result &= Handle.BufferIndex < Array.BuffersCount;
- return Result;
-}
-internal bool
-IndexIsValid(gs_dynarray Array, u64 Index)
-{
- bool Result = Index < DynarraySize(Array);
- return Result;
-}
-internal gs_data
-GetElementInList_(gs_dynarray* Array, gs_dynarray_handle Handle, u64 SizeRequested)
-{
- Assert(SizeRequested == Array->ElementSize);
- Assert(HandleIsValid(*Array, Handle));
- gs_dynarray_buffer Buffer = Array->Buffers[Handle.BufferIndex];
-
- gs_data Result = {0};
- Result.Memory = Buffer.Memory + (Handle.IndexInBuffer * Array->ElementSize);
- Result.Size = SizeRequested;
-
- return Result;
-}
-internal gs_data
-GetElementInList_(gs_dynarray* Array, u64 Index, u64 SizeRequested)
-{
- gs_dynarray_handle Handle = IndexToHandle(Array, Index);
- return GetElementInList_(Array, Handle, SizeRequested);
-}
-internal void
-FreeDynarray(gs_dynarray* Array)
-{
- gs_allocator Allocator = Array->Arena.Allocator;
- u64 BufferSize = CalculateBufferSize(*Array);
- for (u64 i = 0; i < Array->BuffersCount; i++)
- {
- AllocatorFree(Allocator, Array->Buffers[i].Memory, BufferSize);
- }
- AllocatorFree(Allocator, Array->Buffers, sizeof(gs_dynarray_buffer) * Array->BuffersCount);
+ gs_string Message = PushString(Output.Storage, 1024);
+ va_list Args;
+ va_start(Args, Format);
+ PrintFArgsList(&Message, Format, Args);
+ NullTerminate(&Message);
+ Output.Print(Output, Message.ConstString);
}
#define HandlesAreEqual(ha, hb) ((ha.IndexInBuffer == hb.IndexInBuffer) && (ha.BufferIndex == hb.BufferIndex))
-#define CreateDynarray(allocator,type,elePerBuf) CreateDynarray_((allocator), sizeof(type), (elePerBuf))
-#define GetElement_(array,type,size,indexOrHandle) (type*)GetElementInList_(array, indexOrHandle, size).Memory
-#define GetElement(array,type,indexOrHandle) GetElement_(array, type, sizeof(type), indexOrHandle)
-
///////////////////////////
//
// String Builder
@@ -2829,19 +2494,19 @@ FreeDynarray(gs_dynarray* Array)
internal void
GrowStringBuilder_(gs_string_builder* StringBuilder)
{
- gs_string_builder_buffer* NewBuffer = PushStruct(StringBuilder->Arena, gs_string_builder_buffer);
- NewBuffer->String = PushString(StringBuilder->Arena, StringBuilder->BufferSize);
- SLLPushOrInit(StringBuilder->Root, StringBuilder->Head, NewBuffer);
+ gs_string_builder_buffer* NewBuffer = PushStruct(StringBuilder->Arena, gs_string_builder_buffer);
+ NewBuffer->String = PushString(StringBuilder->Arena, StringBuilder->BufferSize);
+ SLLPushOrInit(StringBuilder->Root, StringBuilder->Head, NewBuffer);
}
internal void
OutChar(gs_string_builder* Builder, char C)
{
- if (Builder->Head == 0 || Builder->Head->String.Length >= Builder->Head->String.Size)
- {
- GrowStringBuilder_(Builder);
- }
- OutChar(&Builder->Head->String, C);
+ if (Builder->Head == 0 || Builder->Head->String.Length >= Builder->Head->String.Size)
+ {
+ GrowStringBuilder_(Builder);
+ }
+ OutChar(&Builder->Head->String, C);
}
#if 0
@@ -2850,264 +2515,264 @@ OutChar(gs_string_builder* Builder, char C)
internal void
StringBuilderWriteFArgsList(gs_string_builder* Builder, char* Format, va_list Args)
{
- char* FormatAt = Format;
- while (*FormatAt)
+ char* FormatAt = Format;
+ while (*FormatAt)
+ {
+ if (FormatAt[0] != '%')
{
- if (FormatAt[0] != '%')
+ if (FormatAt[0] == '\\')
+ {
+ FormatAt++;
+ Assert(IsBase8(FormatAt[0]) || // Octal Escape Sequences - \0 is in this set
+ FormatAt[0] == '\'' ||
+ FormatAt[0] == '\"' ||
+ FormatAt[0] == '\?' ||
+ FormatAt[0] == '\\' ||
+ FormatAt[0] == 'a' || // Audible Bell
+ FormatAt[0] == 'b' || // Backspace
+ FormatAt[0] == 'f' || // Form Feed - New Page
+ FormatAt[0] == 'n' || // Line Feed - New Line
+ FormatAt[0] == 'r' || // Carriage Return
+ FormatAt[0] == 't' || // Tab
+ FormatAt[0] == 'v'); // Vertical Tab
+
+ // Not Handled (see cpp spec) \nnn \xnn \unnnn \Unnnnnnnn
+ Assert(FormatAt[0] != 'x' || FormatAt[0] != 'u' || FormatAt[0] != 'U');
+
+ if (IsBase8(FormatAt[0]))
{
- if (FormatAt[0] == '\\')
- {
- FormatAt++;
- Assert(IsBase8(FormatAt[0]) || // Octal Escape Sequences - \0 is in this set
- FormatAt[0] == '\'' ||
- FormatAt[0] == '\"' ||
- FormatAt[0] == '\?' ||
- FormatAt[0] == '\\' ||
- FormatAt[0] == 'a' || // Audible Bell
- FormatAt[0] == 'b' || // Backspace
- FormatAt[0] == 'f' || // Form Feed - New Page
- FormatAt[0] == 'n' || // Line Feed - New Line
- FormatAt[0] == 'r' || // Carriage Return
- FormatAt[0] == 't' || // Tab
- FormatAt[0] == 'v'); // Vertical Tab
-
- // Not Handled (see cpp spec) \nnn \xnn \unnnn \Unnnnnnnn
- Assert(FormatAt[0] != 'x' || FormatAt[0] != 'u' || FormatAt[0] != 'U');
-
- if (IsBase8(FormatAt[0]))
- {
- // TODO(Peter): this should keep going until it finds a non-octal character code
- // but the only one we really need is \0 atm so I'm just handling that one
- Assert(FormatAt[0] == '0');
- OutChar(Builder, (char)0);
- FormatAt++;
- }
- else
- {
- OutChar(Builder, *FormatAt++);
- }
- }
- else
- {
- OutChar(Builder, *FormatAt++);
- }
- }
- else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol
- {
- OutChar(Builder, '%');
- FormatAt += 2;
+ // TODO(Peter): this should keep going until it finds a non-octal character code
+ // but the only one we really need is \0 atm so I'm just handling that one
+ Assert(FormatAt[0] == '0');
+ OutChar(Builder, (char)0);
+ FormatAt++;
}
else
{
- FormatAt++;
-
- // Flags
- if (FormatAt[0] == '-')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == '+')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == ' ')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == '#')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == '0')
- {
- FormatAt++;
- }
-
- // Width
- b32 WidthSpecified = false;
- s32 Width = 0;
-
- if (IsBase10(FormatAt[0]))
- {
- WidthSpecified = true;
- u64 Parsed = 0;
- AssertMessage("ParseInt assumes whole string is an integer");
- Width = (s32)ParseInt(FormatAt, 10, &Parsed);
- FormatAt += Parsed;
- }
- else if (FormatAt[0] == '*')
- {
- WidthSpecified = true;
- Width = va_arg(Args, s32);
- Assert(Width >= 0);
- FormatAt++;
- }
-
- // Precision
- b32 PrecisionSpecified = false;
- s32 Precision = 0;
-
- if (FormatAt[0] == '.')
- {
- FormatAt++;
- if (IsBase10(FormatAt[0]))
- {
-
- PrecisionSpecified = true;
- u64 Parsed = 0;
- AssertMessage("ParseInt assumes whole string is an integer");
- Precision = (s32)ParseInt(FormatAt, 10, &Parsed);
- FormatAt += Parsed;
- }
- else if (FormatAt[0] == '*')
- {
- PrecisionSpecified = true;
- Precision = va_arg(Args, s32);
- Assert(Precision >= 0);
- FormatAt++;
- }
- }
-
- // Length
- b32 LengthSpecified = false;
- s32 Length = 4;
-
- if (FormatAt[0] == 'h' && FormatAt[1] == 'h')
- {
- LengthSpecified = true;
- LengthSpecified = 1;
- FormatAt += 2;
- }
- else if (FormatAt[0] == 'h')
- {
- LengthSpecified = true;
- LengthSpecified = 2;
- FormatAt++;
- }
- else if (FormatAt[0] == 'l' && FormatAt[1] == 'l')
- {
- LengthSpecified = true;
- LengthSpecified = 8;
- FormatAt += 2;
- }
- else if (FormatAt[0] == 'l')
- {
- LengthSpecified = true;
- LengthSpecified = 4;
- FormatAt++;
- }
- else if (FormatAt[0] == 'j')
- {
- LengthSpecified = true;
- LengthSpecified = 8;
- FormatAt++;
- }
- else if (FormatAt[0] == 'z')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == 't')
- {
- FormatAt++;
- }
- else if (FormatAt[0] == 'L')
- {
- FormatAt++;
- }
-
- // Format Specifiers
- gs_string StringRemaining = GetStringAfter(*String, String->Length);
- Assert(StringRemaining.Length == 0);
- if (FormatAt[0] == 'd' || FormatAt[0] == 'i')
- {
- s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args);
- if (SignedInt < 0)
- {
- OutChar(&StringRemaining, '-');
- SignedInt *= -1;
- }
- U64ToASCII(&StringRemaining, (u64)SignedInt, 10, Base10Chars);
- }
- else if (FormatAt[0] == 'u')
- {
- u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
- U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars);
- }
- else if (FormatAt[0] == 'o')
- {
- u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
- U64ToASCII(&StringRemaining, UnsignedInt, 8, Base8Chars);
- }
- else if (FormatAt[0] == 'x' || FormatAt[0] == 'X')
- {
- u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
- U64ToASCII(&StringRemaining, UnsignedInt, 16, Base16Chars);
- }
- else if (FormatAt[0] == 'f' || FormatAt[0] == 'F')
- {
- r64 Float = ReadVarArgsFloat(Length, &Args);
- s32 AfterPoint = 6;
- if (PrecisionSpecified)
- {
- AfterPoint = Precision;
- }
- R64ToASCII(&StringRemaining, Float, AfterPoint);
- }
- else if (FormatAt[0] == 'c')
- {
- char InsertChar = va_arg(Args, s32);
- OutChar(&StringRemaining, InsertChar);
- }
- else if (FormatAt[0] == 's')
- {
- char* InsertString = va_arg(Args, char*);
-
- s32 InsertStringLength = CStringLength(InsertString);
- if (PrecisionSpecified)
- {
- InsertStringLength = Min(InsertStringLength, Precision);
- }
- InsertStringLength = Min(StringSizeLeft(StringRemaining), InsertStringLength);
-
- for (s32 c = 0; c < InsertStringLength; c++)
- {
- OutChar(&StringRemaining, InsertString[c]);
- }
- }
- else if (FormatAt[0] == 'S')
- {
- gs_const_string InsertString = va_arg(Args, gs_const_string);
-
- for (s32 c = 0; c < InsertString.Length; c++)
- {
- OutChar(&StringRemaining, InsertString.Str[c]);
- }
- }
- else if (FormatAt[0] == 'p')
- {
- // TODO(Peter): Pointer Address
- }
- else
- {
- // NOTE(Peter): Non-specifier character found
- InvalidCodePath;
- }
-
- String->Length += StringRemaining.Length;
- FormatAt++;
+ OutChar(Builder, *FormatAt++);
}
+ }
+ else
+ {
+ OutChar(Builder, *FormatAt++);
+ }
}
-
- return String->Length;
+ else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol
+ {
+ OutChar(Builder, '%');
+ FormatAt += 2;
+ }
+ else
+ {
+ FormatAt++;
+
+ // Flags
+ if (FormatAt[0] == '-')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == '+')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == ' ')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == '#')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == '0')
+ {
+ FormatAt++;
+ }
+
+ // Width
+ b32 WidthSpecified = false;
+ s32 Width = 0;
+
+ if (IsBase10(FormatAt[0]))
+ {
+ WidthSpecified = true;
+ u64 Parsed = 0;
+ AssertMessage("ParseInt assumes whole string is an integer");
+ Width = (s32)ParseInt(FormatAt, 10, &Parsed);
+ FormatAt += Parsed;
+ }
+ else if (FormatAt[0] == '*')
+ {
+ WidthSpecified = true;
+ Width = va_arg(Args, s32);
+ Assert(Width >= 0);
+ FormatAt++;
+ }
+
+ // Precision
+ b32 PrecisionSpecified = false;
+ s32 Precision = 0;
+
+ if (FormatAt[0] == '.')
+ {
+ FormatAt++;
+ if (IsBase10(FormatAt[0]))
+ {
+
+ PrecisionSpecified = true;
+ u64 Parsed = 0;
+ AssertMessage("ParseInt assumes whole string is an integer");
+ Precision = (s32)ParseInt(FormatAt, 10, &Parsed);
+ FormatAt += Parsed;
+ }
+ else if (FormatAt[0] == '*')
+ {
+ PrecisionSpecified = true;
+ Precision = va_arg(Args, s32);
+ Assert(Precision >= 0);
+ FormatAt++;
+ }
+ }
+
+ // Length
+ b32 LengthSpecified = false;
+ s32 Length = 4;
+
+ if (FormatAt[0] == 'h' && FormatAt[1] == 'h')
+ {
+ LengthSpecified = true;
+ LengthSpecified = 1;
+ FormatAt += 2;
+ }
+ else if (FormatAt[0] == 'h')
+ {
+ LengthSpecified = true;
+ LengthSpecified = 2;
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 'l' && FormatAt[1] == 'l')
+ {
+ LengthSpecified = true;
+ LengthSpecified = 8;
+ FormatAt += 2;
+ }
+ else if (FormatAt[0] == 'l')
+ {
+ LengthSpecified = true;
+ LengthSpecified = 4;
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 'j')
+ {
+ LengthSpecified = true;
+ LengthSpecified = 8;
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 'z')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 't')
+ {
+ FormatAt++;
+ }
+ else if (FormatAt[0] == 'L')
+ {
+ FormatAt++;
+ }
+
+ // Format Specifiers
+ gs_string StringRemaining = GetStringAfter(*String, String->Length);
+ Assert(StringRemaining.Length == 0);
+ if (FormatAt[0] == 'd' || FormatAt[0] == 'i')
+ {
+ s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args);
+ if (SignedInt < 0)
+ {
+ OutChar(&StringRemaining, '-');
+ SignedInt *= -1;
+ }
+ U64ToASCII(&StringRemaining, (u64)SignedInt, 10, Base10Chars);
+ }
+ else if (FormatAt[0] == 'u')
+ {
+ u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
+ U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars);
+ }
+ else if (FormatAt[0] == 'o')
+ {
+ u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
+ U64ToASCII(&StringRemaining, UnsignedInt, 8, Base8Chars);
+ }
+ else if (FormatAt[0] == 'x' || FormatAt[0] == 'X')
+ {
+ u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
+ U64ToASCII(&StringRemaining, UnsignedInt, 16, Base16Chars);
+ }
+ else if (FormatAt[0] == 'f' || FormatAt[0] == 'F')
+ {
+ r64 Float = ReadVarArgsFloat(Length, &Args);
+ s32 AfterPoint = 6;
+ if (PrecisionSpecified)
+ {
+ AfterPoint = Precision;
+ }
+ R64ToASCII(&StringRemaining, Float, AfterPoint);
+ }
+ else if (FormatAt[0] == 'c')
+ {
+ char InsertChar = va_arg(Args, s32);
+ OutChar(&StringRemaining, InsertChar);
+ }
+ else if (FormatAt[0] == 's')
+ {
+ char* InsertString = va_arg(Args, char*);
+
+ s32 InsertStringLength = CStringLength(InsertString);
+ if (PrecisionSpecified)
+ {
+ InsertStringLength = Min(InsertStringLength, Precision);
+ }
+ InsertStringLength = Min(StringSizeLeft(StringRemaining), InsertStringLength);
+
+ for (s32 c = 0; c < InsertStringLength; c++)
+ {
+ OutChar(&StringRemaining, InsertString[c]);
+ }
+ }
+ else if (FormatAt[0] == 'S')
+ {
+ gs_const_string InsertString = va_arg(Args, gs_const_string);
+
+ for (s32 c = 0; c < InsertString.Length; c++)
+ {
+ OutChar(&StringRemaining, InsertString.Str[c]);
+ }
+ }
+ else if (FormatAt[0] == 'p')
+ {
+ // TODO(Peter): Pointer Address
+ }
+ else
+ {
+ // NOTE(Peter): Non-specifier character found
+ InvalidCodePath;
+ }
+
+ String->Length += StringRemaining.Length;
+ FormatAt++;
+ }
+ }
+
+ return String->Length;
}
internal void
StringBuilderWriteF(gs_string_builder* Builder, char* Format, ...)
{
- va_list Args;
- va_start(Args, Format);
- StringBuilderWriteFArgsList(Builder, Format, Args);
- va_end(Args);
+ va_list Args;
+ va_start(Args, Format);
+ StringBuilderWriteFArgsList(Builder, Format, Args);
+ va_end(Args);
}
#endif // String builder
@@ -3119,25 +2784,25 @@ StringBuilderWriteF(gs_string_builder* Builder, char* Format, ...)
internal u64
FileHandlerGetFileInfo_NoOp(gs_file_handler FileHandler, gs_const_string Path)
{
- return 0;
+ return 0;
}
internal gs_file
FileHandlerReadFile_NoOp(gs_const_string Path)
{
- return gs_file{0};
+ return gs_file{0};
}
internal bool
FileHandlerWriteFile_NoOp(gs_const_string Path, gs_data Data)
{
- return false;
+ return false;
}
internal gs_const_string_array
FileHandlerEnumerateDirectory_NoOp(gs_const_string Path, bool Recursive, bool IncludeDirs)
{
- return gs_const_string_array{0};
+ return gs_const_string_array{0};
}
internal gs_file_handler
@@ -3147,110 +2812,110 @@ CreateFileHandler(file_handler_get_file_info* GetFileInfo,
file_handler_enumerate_directory* EnumerateDirectory,
gs_memory_arena* Transient)
{
- if (GetFileInfo == 0)
- {
- GetFileInfo = (file_handler_get_file_info*)FileHandlerGetFileInfo_NoOp;
- }
- if (ReadEntireFile == 0)
- {
- ReadEntireFile = (file_handler_read_entire_file*)FileHandlerReadFile_NoOp;
- }
- if (WriteEntireFile == 0)
- {
- WriteEntireFile = (file_handler_write_entire_file*)FileHandlerWriteFile_NoOp;
- }
- if (EnumerateDirectory == 0)
- {
- EnumerateDirectory = (file_handler_enumerate_directory*)FileHandlerEnumerateDirectory_NoOp;
- }
- gs_file_handler Result = {0};
- Result.GetFileInfo = GetFileInfo;
- Result.ReadEntireFile = ReadEntireFile;
- Result.WriteEntireFile = WriteEntireFile;
- Result.EnumerateDirectory = EnumerateDirectory;
- Result.Transient = Transient;
-
- return Result;
+ if (GetFileInfo == 0)
+ {
+ GetFileInfo = (file_handler_get_file_info*)FileHandlerGetFileInfo_NoOp;
+ }
+ if (ReadEntireFile == 0)
+ {
+ ReadEntireFile = (file_handler_read_entire_file*)FileHandlerReadFile_NoOp;
+ }
+ if (WriteEntireFile == 0)
+ {
+ WriteEntireFile = (file_handler_write_entire_file*)FileHandlerWriteFile_NoOp;
+ }
+ if (EnumerateDirectory == 0)
+ {
+ EnumerateDirectory = (file_handler_enumerate_directory*)FileHandlerEnumerateDirectory_NoOp;
+ }
+ gs_file_handler Result = {0};
+ Result.GetFileInfo = GetFileInfo;
+ Result.ReadEntireFile = ReadEntireFile;
+ Result.WriteEntireFile = WriteEntireFile;
+ Result.EnumerateDirectory = EnumerateDirectory;
+ Result.Transient = Transient;
+
+ return Result;
}
internal gs_const_string
GetNullTerminatedPath(gs_file_handler FileHandler, gs_const_string Path)
{
- gs_const_string Result = {};
- if (!IsNullTerminated(Path))
- {
- gs_string NullTermPath = PushString(FileHandler.Transient, Path.Length + 1);
- PrintF(&NullTermPath, "%S", Path);
- NullTerminate(&NullTermPath);
- Result = NullTermPath.ConstString;
- }
- else
- {
- Result = Path;
- }
- return Result;
+ gs_const_string Result = {};
+ if (!IsNullTerminated(Path))
+ {
+ gs_string NullTermPath = PushString(FileHandler.Transient, Path.Length + 1);
+ PrintF(&NullTermPath, "%S", Path);
+ NullTerminate(&NullTermPath);
+ Result = NullTermPath.ConstString;
+ }
+ else
+ {
+ Result = Path;
+ }
+ return Result;
}
internal gs_file_info
GetFileInfo(gs_file_handler FileHandler, gs_const_string Path)
{
- Assert(FileHandler.GetFileInfo != 0);
-
- Path = GetNullTerminatedPath(FileHandler, Path);
- gs_file_info Result = FileHandler.GetFileInfo(FileHandler, Path);
- return Result;
+ Assert(FileHandler.GetFileInfo != 0);
+
+ Path = GetNullTerminatedPath(FileHandler, Path);
+ gs_file_info Result = FileHandler.GetFileInfo(FileHandler, Path);
+ return Result;
}
internal gs_file
ReadEntireFile(gs_file_handler FileHandler, gs_const_string Path, gs_data Memory)
{
- Assert(FileHandler.ReadEntireFile != 0);
-
- Path = GetNullTerminatedPath(FileHandler, Path);
- gs_file Result = FileHandler.ReadEntireFile(FileHandler, Path, Memory);
- return Result;
+ Assert(FileHandler.ReadEntireFile != 0);
+
+ Path = GetNullTerminatedPath(FileHandler, Path);
+ gs_file Result = FileHandler.ReadEntireFile(FileHandler, Path, Memory);
+ return Result;
}
internal gs_file
ReadEntireFile(gs_file_handler FileHandler, gs_const_string Path)
{
- Assert(FileHandler.GetFileInfo != 0);
- Assert(FileHandler.ReadEntireFile != 0);
-
- Path = GetNullTerminatedPath(FileHandler, Path);
- gs_file Result = {0};
- gs_file_info FileInfo = FileHandler.GetFileInfo(FileHandler, Path);
- if (FileInfo.FileSize > 0)
- {
- gs_data FileMemory = PushSizeToData(FileHandler.Transient, FileInfo.FileSize);
- Result = ReadEntireFile(FileHandler, Path, FileMemory);
- }
- return Result;
+ Assert(FileHandler.GetFileInfo != 0);
+ Assert(FileHandler.ReadEntireFile != 0);
+
+ Path = GetNullTerminatedPath(FileHandler, Path);
+ gs_file Result = {0};
+ gs_file_info FileInfo = FileHandler.GetFileInfo(FileHandler, Path);
+ if (FileInfo.FileSize > 0)
+ {
+ gs_data FileMemory = PushSize(FileHandler.Transient, FileInfo.FileSize);
+ Result = ReadEntireFile(FileHandler, Path, FileMemory);
+ }
+ return Result;
}
internal bool
WriteEntireFile(gs_file_handler FileHandler, gs_const_string Path, gs_data Memory)
{
- Assert(FileHandler.WriteEntireFile != 0);
-
- Path = GetNullTerminatedPath(FileHandler, Path);
- return FileHandler.WriteEntireFile(FileHandler, Path, Memory);
+ Assert(FileHandler.WriteEntireFile != 0);
+
+ Path = GetNullTerminatedPath(FileHandler, Path);
+ return FileHandler.WriteEntireFile(FileHandler, Path, Memory);
}
internal gs_file_info_array
EnumerateDirectory(gs_file_handler FileHandler, gs_memory_arena* Storage, gs_const_string Path, u32 Flags)
{
- Assert(FileHandler.EnumerateDirectory != 0);
-
- Path = GetNullTerminatedPath(FileHandler, Path);
- return FileHandler.EnumerateDirectory(FileHandler, Storage, Path, Flags);
+ Assert(FileHandler.EnumerateDirectory != 0);
+
+ Path = GetNullTerminatedPath(FileHandler, Path);
+ return FileHandler.EnumerateDirectory(FileHandler, Storage, Path, Flags);
}
internal bool
FileNoError(gs_file File)
{
- bool Result = (File.Size > 0);
- return Result;
+ bool Result = (File.Size > 0);
+ return Result;
}
//////////////////////////
@@ -3260,15 +2925,15 @@ FileNoError(gs_file File)
internal s64
TimeHandlerGetWallClock(gs_time_handler TimeHandler)
{
- s64 Result = TimeHandler.GetWallClock();
- return Result;
+ s64 Result = TimeHandler.GetWallClock();
+ return Result;
}
internal s64
TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 EndCycles)
{
- s64 Result = TimeHandler.GetSecondsElapsed(StartCycles, EndCycles);
- return Result;
+ s64 Result = TimeHandler.GetSecondsElapsed(StartCycles, EndCycles);
+ return Result;
}
//////////////////////////
@@ -3277,71 +2942,71 @@ TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 E
CREATE_THREAD(CreateThreadStub)
{
- return {};
+ return {};
}
KILL_THREAD(KillThreadStub)
{
- return false;
+ return false;
}
internal platform_thread_manager
CreatePlatformThreadManager(platform_create_thread* CreateThreadProc,
platform_kill_thread* KillThreadProc)
{
- platform_thread_manager Result = {};
- Result.CreateThreadProc = CreateThreadProc;
- Result.KillThreadProc = KillThreadProc;
-
- if (!CreateThreadProc)
- {
- Result.CreateThreadProc = CreateThreadStub;
- }
- if (!KillThreadProc)
- {
- Result.KillThreadProc = KillThreadStub;
- }
-
- return Result;
+ platform_thread_manager Result = {};
+ Result.CreateThreadProc = CreateThreadProc;
+ Result.KillThreadProc = KillThreadProc;
+
+ if (!CreateThreadProc)
+ {
+ Result.CreateThreadProc = CreateThreadStub;
+ }
+ if (!KillThreadProc)
+ {
+ Result.KillThreadProc = KillThreadStub;
+ }
+
+ return Result;
}
internal platform_thread_handle
-CreateThread(platform_thread_manager* Manager, thread_proc_* Proc, u8* Arg)
+CreateThread(platform_thread_manager* Manager, thread_proc_* Proc, u8* Arg, gs_thread_context Ctx)
{
- platform_thread_handle Result = {};
-
- for (u32 i = 1; i < THREADS_MAX; i++)
+ platform_thread_handle Result = {};
+
+ for (u32 i = 1; i < THREADS_MAX; i++)
+ {
+ if (!Manager->ThreadsUsed[i])
{
- if (!Manager->ThreadsUsed[i])
- {
- Result.Index = i;
- break;
- }
+ Result.Index = i;
+ break;
}
- Assert(Result.Index != 0);
-
- Manager->ThreadsUsed[Result.Index] = true;
- Manager->CreateThreadProc(&Manager->Threads[Result.Index], Proc, Arg);
-
- return Result;
+ }
+ Assert(Result.Index != 0);
+
+ Manager->ThreadsUsed[Result.Index] = true;
+ Manager->CreateThreadProc(&Manager->Threads[Result.Index], Proc, Arg, Ctx);
+
+ return Result;
}
internal bool
KillThread(platform_thread_manager* Manager, platform_thread_handle Handle)
{
- Assert(Manager->ThreadsUsed[Handle.Index]);
-
- platform_thread* Thread = &Manager->Threads[Handle.Index];
- bool Result = Manager->KillThreadProc(Thread);
-
- if (Result)
- {
- Manager->ThreadsUsed[Handle.Index] = false;
- Manager->Threads[Handle.Index] = {};
- }
-
- return Result;
+ Assert(Manager->ThreadsUsed[Handle.Index]);
+
+ platform_thread* Thread = &Manager->Threads[Handle.Index];
+ bool Result = Manager->KillThreadProc(Thread);
+
+ if (Result)
+ {
+ Manager->ThreadsUsed[Handle.Index] = false;
+ Manager->Threads[Handle.Index] = {};
+ }
+
+ return Result;
}
@@ -3351,32 +3016,32 @@ KillThread(platform_thread_manager* Manager, platform_thread_handle Handle)
CONNECT_SOCKET(PlatformConnectSocket_Stub)
{
- return false;
+ return false;
}
CLOSE_SOCKET(PlatformCloseSocket_Stub)
{
- return false;
+ return false;
}
SOCKET_QUERY_STATUS(PlatformSocketQueryStatus_Stub)
{
- return false;
+ return false;
}
SOCKET_PEEK(PlatformSocketPeek_Stub)
{
- return 0;
+ return 0;
}
SOCKET_RECEIVE(PlatformSocketRecieve_Stub)
{
- return {};
+ return {};
}
SOCKET_SEND(PlatformSocketSend_Stub)
{
- return false;
+ return false;
}
internal platform_socket_manager
@@ -3387,159 +3052,194 @@ CreatePlatformSocketManager(platform_connect_socket* ConnectSocketProc,
platform_socket_receive* SocketRecieveProc,
platform_socket_send* SocketSendProc)
{
- platform_socket_manager Result = {};
- Result.ConnectSocketProc = ConnectSocketProc;
- Result.CloseSocketProc = CloseSocketProc;
- Result.SocketQueryStatusProc = SocketQueryStatusProc;
- Result.SocketPeekProc = SocketPeekProc;
- Result.SocketRecieveProc = SocketRecieveProc;
- Result.SocketSendProc = SocketSendProc;
-
- if (!ConnectSocketProc)
- {
- Result.ConnectSocketProc = PlatformConnectSocket_Stub;
- }
- if (!CloseSocketProc)
- {
- Result.CloseSocketProc = PlatformCloseSocket_Stub;
- }
- if (!SocketQueryStatusProc)
- {
- Result.SocketQueryStatusProc = PlatformSocketQueryStatus_Stub;
- }
- if (!SocketPeekProc)
- {
- Result.SocketPeekProc = PlatformSocketPeek_Stub;
- }
- if (!SocketRecieveProc)
- {
- Result.SocketRecieveProc = PlatformSocketRecieve_Stub;
- }
- if (!SocketSendProc)
- {
- Result.SocketSendProc = PlatformSocketSend_Stub;
- }
- return Result;
+ platform_socket_manager Result = {};
+ Result.ConnectSocketProc = ConnectSocketProc;
+ Result.CloseSocketProc = CloseSocketProc;
+ Result.SocketQueryStatusProc = SocketQueryStatusProc;
+ Result.SocketPeekProc = SocketPeekProc;
+ Result.SocketRecieveProc = SocketRecieveProc;
+ Result.SocketSendProc = SocketSendProc;
+
+ if (!ConnectSocketProc)
+ {
+ Result.ConnectSocketProc = PlatformConnectSocket_Stub;
+ }
+ if (!CloseSocketProc)
+ {
+ Result.CloseSocketProc = PlatformCloseSocket_Stub;
+ }
+ if (!SocketQueryStatusProc)
+ {
+ Result.SocketQueryStatusProc = PlatformSocketQueryStatus_Stub;
+ }
+ if (!SocketPeekProc)
+ {
+ Result.SocketPeekProc = PlatformSocketPeek_Stub;
+ }
+ if (!SocketRecieveProc)
+ {
+ Result.SocketRecieveProc = PlatformSocketRecieve_Stub;
+ }
+ if (!SocketSendProc)
+ {
+ Result.SocketSendProc = PlatformSocketSend_Stub;
+ }
+ return Result;
+}
+
+internal bool
+SocketHandleIsValid(platform_socket_handle_ Handle)
+{
+ return Handle.Index != 0;
}
internal platform_socket*
SocketManagerGetSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle)
{
- platform_socket* Result = 0;
- if (Manager->SocketsUsed[Handle.Index])
+ platform_socket* Result = 0;
+ if (Manager->SocketsUsed[Handle.Index])
+ {
+ platform_socket* Socket = &Manager->Sockets[Handle.Index];
+ if (Socket->PlatformHandle != 0)
{
- platform_socket* Socket = &Manager->Sockets[Handle.Index];
- if (Socket->PlatformHandle != 0)
- {
- Result = Socket;
- }
+ Result = Socket;
}
- return Result;
+ }
+ return Result;
}
internal bool
ConnectSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle)
{
- bool Result = false;
- platform_socket* Socket = SocketManagerGetSocket(Manager, Handle);
- if (Socket)
- {
- Result = Manager->ConnectSocketProc(Socket);
- }
- return Result;
+ bool Result = false;
+ platform_socket* Socket = SocketManagerGetSocket(Manager, Handle);
+ if (Socket)
+ {
+ Result = Manager->ConnectSocketProc(Manager, Socket);
+ }
+ return Result;
+}
+
+internal bool
+RemoveSocket (platform_socket_manager* Manager, platform_socket_handle_ Handle)
+{
+ bool Result = Manager->SocketsUsed[Handle.Index];
+ Manager->SocketsUsed[Handle.Index] = false;
+ return Result;
}
internal platform_socket_handle_
CreateSocket(platform_socket_manager* Manager, char* Addr, char* Port)
{
- platform_socket_handle_ Result = {};
- for (u32 i = 1; i < SOCKETS_COUNT_MAX; i++)
+ platform_socket_handle_ Result = {};
+ for (u32 i = 1; i < SOCKETS_COUNT_MAX; i++)
+ {
+ if (!Manager->SocketsUsed[i])
{
- if (!Manager->SocketsUsed[i])
- {
- Result.Index = i;
- Manager->SocketsUsed[i] = true;
- break;
- }
+ Result.Index = i;
+ Manager->SocketsUsed[i] = true;
+ break;
}
-
- Assert(Result.Index != 0);
- platform_socket* Socket = &Manager->Sockets[Result.Index];
- CopyArray(Addr, Socket->Addr, char, CStringLength(Addr) + 1);
- CopyArray(Port, Socket->Port, char, CStringLength(Port) + 1);
-
- bool Success = Manager->ConnectSocketProc(Socket);
- Assert(Success);
-
- return Result;
+ }
+
+ Assert(Result.Index != 0);
+ platform_socket* Socket = &Manager->Sockets[Result.Index];
+ Socket->Handle = Result;
+ CopyArray(Addr, Socket->Addr, char, CStringLength(Addr) + 1);
+ CopyArray(Port, Socket->Port, char, CStringLength(Port) + 1);
+
+ bool Success = Manager->ConnectSocketProc(Manager, Socket);
+ if (!Success)
+ {
+ if (RemoveSocket(Manager, Result))
+ {
+ Result = {};
+ }
+ else
+ {
+ InvalidCodePath;
+ }
+ }
+
+ return Result;
+}
+
+internal bool
+CloseSocket(platform_socket_manager* Manager, platform_socket* Socket)
+{
+ bool Result = false;
+ if (Socket)
+ {
+ if (Manager->CloseSocketProc(Manager, Socket))
+ {
+ RemoveSocket(Manager, Socket->Handle);
+ *Socket = {};
+ Result = true;
+ }
+ else
+ {
+ InvalidCodePath;
+ }
+ }
+ return Result;
}
internal bool
CloseSocket(platform_socket_manager* Manager, platform_socket_handle_ Handle)
{
- bool Result = false;
- platform_socket* Socket = SocketManagerGetSocket(Manager, Handle);
- if (Socket)
- {
- if (Manager->CloseSocketProc(Socket))
- {
- Manager->SocketsUsed[Handle.Index] = false;
- *Socket = {};
- Result = true;
- }
- }
- return Result;
+ bool Result = false;
+ platform_socket* Socket = SocketManagerGetSocket(Manager, Handle);
+ return CloseSocket(Manager, Socket);
}
// NOTE(pjs): returns true if the socket is connected
-// TODO(pjs): make this more descriptive?
internal bool
SocketQueryStatus(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle)
{
- bool Result = false;
- platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle);
- if (Socket)
- {
- Result = Manager->SocketQueryStatusProc(Socket);
- }
- return Result;
+ bool Result = false;
+ platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle);
+ if (Socket)
+ {
+ Result = Manager->SocketQueryStatusProc(Manager, Socket);
+ }
+ return Result;
}
internal u32
SocketPeek(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle)
{
- u32 Result = 0;
- platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle);
- if (Socket)
- {
- Result = Manager->SocketPeekProc(Socket);
- }
- return Result;
+ u32 Result = 0;
+ platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle);
+ if (Socket)
+ {
+ Result = Manager->SocketPeekProc(Manager, Socket);
+ }
+ return Result;
}
internal gs_data
SocketRecieve(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle, gs_memory_arena* Storage)
{
- gs_data Result = {};
- platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle);
- if (Socket)
- {
- Result = Manager->SocketRecieveProc(Socket, Storage);
- }
- return Result;
+ gs_data Result = {};
+ platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle);
+ if (Socket)
+ {
+ Result = Manager->SocketRecieveProc(Manager, Socket, Storage);
+ }
+ return Result;
}
internal bool
SocketSend(platform_socket_manager* Manager, platform_socket_handle_ SocketHandle, u32 Address, u32 Port, gs_data Data, s32 Flags)
{
- bool Result = false;
- platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle);
- if (Socket)
- {
- s32 SizeSent = Manager->SocketSendProc(Socket, Address, Port, Data, Flags);
- Result = (SizeSent == Data.Size);
- }
- return Result;
+ bool Result = false;
+ platform_socket* Socket = SocketManagerGetSocket(Manager, SocketHandle);
+ if (Socket)
+ {
+ s32 SizeSent = Manager->SocketSendProc(Manager, Socket, Address, Port, Data, Flags);
+ Result = (SizeSent == Data.Size);
+ }
+ return Result;
}
///////////////////////////
@@ -3549,63 +3249,83 @@ SocketSend(platform_socket_manager* Manager, platform_socket_handle_ SocketHandl
internal u32
HashAppendDJB2ToU32(u32 Hash, u8 Byte)
{
- u32 Result = Hash;
- if (Result == 0) { Result = 5381; }
- Result = ((Result << 5) + Result) + Byte;
- return Result;
+ u32 Result = Hash;
+ if (Result == 0) { Result = 5381; }
+ Result = ((Result << 5) + Result) + Byte;
+ return Result;
}
internal u64
HashAppendDJB2ToU32(u64 Hash, u8 Byte)
{
- u64 Result = Hash;
- if (Result == 0) { Result = 5381; }
- Result = ((Result << 5) + Result) + Byte;
- return Result;
+ u64 Result = Hash;
+ if (Result == 0) { Result = 5381; }
+ Result = ((Result << 5) + Result) + Byte;
+ return Result;
}
internal u32
HashDJB2ToU32(char* String)
{
- u32 Hash = 5381;
- char* C = String;
- while(*C)
- {
- Hash = ((Hash << 5) + Hash) + *C++;
- }
- return Hash;
+ u32 Hash = 5381;
+ char* C = String;
+ while(*C)
+ {
+ Hash = ((Hash << 5) + Hash) + *C++;
+ }
+ return Hash;
}
internal u32
HashDJB2ToU32(u32 Length, char* String)
{
- u32 Hash = 5381;
- for (u32 i = 0; i < Length; i++)
- {
- Hash = ((Hash << 5) + Hash) + String[i];
- }
- return Hash;
+ u32 Hash = 5381;
+ for (u32 i = 0; i < Length; i++)
+ {
+ Hash = ((Hash << 5) + Hash) + String[i];
+ }
+ return Hash;
+}
+internal u32
+HashDJB2ToU32(gs_const_string Str)
+{
+ return HashDJB2ToU32(StringExpand(Str));
+}
+internal u32
+HashDJB2ToU32(gs_string Str)
+{
+ return HashDJB2ToU32(StringExpand(Str));
}
internal u64
HashDJB2ToU64(char* String)
{
- u64 Hash = 5381;
- char* C = String;
- while(*C)
- {
- Hash = ((Hash << 5) + Hash) + *C++;
- }
- return Hash;
+ u64 Hash = 5381;
+ char* C = String;
+ while(*C)
+ {
+ Hash = ((Hash << 5) + Hash) + *C++;
+ }
+ return Hash;
}
internal u64
HashDJB2ToU64(u32 Length, char* String)
{
- u64 Hash = 5381;
- for (u32 i = 0; i < Length; i++)
- {
- Hash = ((Hash << 5) + Hash) + String[i];
- }
- return Hash;
+ u64 Hash = 5381;
+ for (u32 i = 0; i < Length; i++)
+ {
+ Hash = ((Hash << 5) + Hash) + String[i];
+ }
+ return Hash;
+}
+internal u64
+HashDJB2ToU64(gs_const_string Str)
+{
+ return HashDJB2ToU64(StringExpand(Str));
+}
+internal u64
+HashDJB2ToU64(gs_string Str)
+{
+ return HashDJB2ToU64(StringExpand(Str));
}
///////////////////////////
@@ -3615,36 +3335,36 @@ HashDJB2ToU64(u32 Length, char* String)
internal gs_random_series
InitRandomSeries(u32 Seed)
{
- gs_random_series Result = {0};
- Result.Value = Seed;
- return Result;
+ gs_random_series Result = {0};
+ Result.Value = Seed;
+ return Result;
}
internal u32
NextRandom(gs_random_series* Series)
{
- u32 Result = Series->Value;
- Result ^= Result << 13;
- Result ^= Result >> 17;
- Result ^= Result << 5;
- Series->Value = Result;
- return Result;
+ u32 Result = Series->Value;
+ Result ^= Result << 13;
+ Result ^= Result >> 17;
+ Result ^= Result << 5;
+ Series->Value = Result;
+ return Result;
}
internal r32
NextRandomUnilateral(gs_random_series* Series)
{
- r32 Result = (r32)NextRandom(Series) / (r32)UINT32_MAX;
- return Result;
+ r32 Result = (r32)NextRandom(Series) / (r32)UINT32_MAX;
+ return Result;
}
internal r32
NextRandomBilateral(gs_random_series* Series)
{
- r32 Result = (r32)NextRandom(Series);
- Result = Result / (r32)0xFFFFFFFF;
- Result = (Result * 2.0f) - 1.0f;
- return Result;
+ r32 Result = (r32)NextRandom(Series);
+ Result = Result / (r32)0xFFFFFFFF;
+ Result = (Result * 2.0f) - 1.0f;
+ return Result;
}
@@ -3656,62 +3376,62 @@ NextRandomBilateral(gs_random_series* Series)
static void
RadixSortInPlace_ (gs_radix_list* List, u32 Start, u32 End, u32 Iteration)
{
- u32 Shift = Iteration;
- u32 ZerosBoundary = Start;
- u32 OnesBoundary = End - 1;
-
- for (u32 d = Start; d < End; d++)
+ u32 Shift = Iteration;
+ u32 ZerosBoundary = Start;
+ u32 OnesBoundary = End - 1;
+
+ for (u32 d = Start; d < End; d++)
+ {
+ u64 CurrentIndex = ZerosBoundary;
+ u64 Radix = List->Radixes.Values[CurrentIndex];
+ u64 Place = (Radix >> Shift) & 0x1;
+ if (Place)
{
- u64 CurrentIndex = ZerosBoundary;
- u64 Radix = List->Radixes.Values[CurrentIndex];
- u64 Place = (Radix >> Shift) & 0x1;
- if (Place)
- {
- u64 EvictedIndex = OnesBoundary;
- u64 EvictedRadix = List->Radixes.Values[EvictedIndex];
- u64 EvictedID = List->IDs.Values[EvictedIndex];
-
- List->Radixes.Values[EvictedIndex] = Radix;
- List->IDs.Values[EvictedIndex] = List->IDs.Values[CurrentIndex];
-
- List->Radixes.Values[CurrentIndex] = EvictedRadix;
- List->IDs.Values[CurrentIndex] = EvictedID;
-
- OnesBoundary -= 1;
- }
- else
- {
- ZerosBoundary += 1;
- }
+ u64 EvictedIndex = OnesBoundary;
+ u64 EvictedRadix = List->Radixes.Values[EvictedIndex];
+ u64 EvictedID = List->IDs.Values[EvictedIndex];
+
+ List->Radixes.Values[EvictedIndex] = Radix;
+ List->IDs.Values[EvictedIndex] = List->IDs.Values[CurrentIndex];
+
+ List->Radixes.Values[CurrentIndex] = EvictedRadix;
+ List->IDs.Values[CurrentIndex] = EvictedID;
+
+ OnesBoundary -= 1;
}
-
- if (Iteration > 0)
+ else
{
- RadixSortInPlace_(List, Start, ZerosBoundary, Iteration - 1);
- RadixSortInPlace_(List, ZerosBoundary, End, Iteration - 1);
+ ZerosBoundary += 1;
}
+ }
+
+ if (Iteration > 0)
+ {
+ RadixSortInPlace_(List, Start, ZerosBoundary, Iteration - 1);
+ RadixSortInPlace_(List, ZerosBoundary, End, Iteration - 1);
+ }
}
static void
RadixSortInPlace (gs_radix_list* List)
{
- u32 Highest = 0;
- for (u32 i = 0; i < List->Radixes.Count; i++)
+ u32 Highest = 0;
+ for (u32 i = 0; i < List->Radixes.Count; i++)
+ {
+ if (List->Radixes.Values[i] > Highest)
{
- if (List->Radixes.Values[i] > Highest)
- {
- Highest = List->Radixes.Values[i];
- }
+ Highest = List->Radixes.Values[i];
}
-
- u32 Iterations = 0;
- while (Highest > 1)
- {
- ++Iterations;
- Highest = Highest >> 1;
- }
-
- RadixSortInPlace_(List, 0, List->Radixes.Count, Iterations);
+ }
+
+ u32 Iterations = 0;
+ while (Highest > 1)
+ {
+ ++Iterations;
+ Highest = Highest >> 1;
+ }
+
+ RadixSortInPlace_(List, 0, List->Radixes.Count, Iterations);
}
@@ -3722,105 +3442,105 @@ RadixSortInPlace (gs_radix_list* List)
inline bool
KeyIsMouseButton(gs_key Key)
{
- bool Result = (Key >= gs_Key_MouseLeftButton);
- Result = Result && Key <= gs_Key_MouseRightButton;
- return Result;
+ bool Result = (Key >= gs_Key_MouseLeftButton);
+ Result = Result && Key <= gs_Key_MouseRightButton;
+ return Result;
}
inline u32
GetMouseButtonIndex(gs_key Button)
{
- Assert(KeyIsMouseButton(Button));
- u32 Result = Button - gs_Key_MouseLeftButton;
- return Result;
+ Assert(KeyIsMouseButton(Button));
+ u32 Result = Button - gs_Key_MouseLeftButton;
+ return Result;
}
inline bool
MouseButtonTransitionedDown(gs_mouse_state Mouse, u32 Index)
{
- bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0;
- bool WasDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_WasDownBit)) != 0;
- return IsDown && !WasDown;
+ bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0;
+ bool WasDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_WasDownBit)) != 0;
+ return IsDown && !WasDown;
}
inline bool
MouseButtonTransitionedDown(gs_mouse_state Mouse, gs_key Button)
{
- u32 Index = GetMouseButtonIndex(Button);
- return MouseButtonTransitionedDown(Mouse, Index);
+ u32 Index = GetMouseButtonIndex(Button);
+ return MouseButtonTransitionedDown(Mouse, Index);
}
inline bool
MouseButtonIsDown(gs_mouse_state Mouse, u32 Index)
{
- bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0;
- return IsDown;
+ bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0;
+ return IsDown;
}
inline bool
MouseButtonIsDown(gs_mouse_state Mouse, gs_key Button)
{
- u32 Index = GetMouseButtonIndex(Button);
- return MouseButtonIsDown(Mouse, Index);
+ u32 Index = GetMouseButtonIndex(Button);
+ return MouseButtonIsDown(Mouse, Index);
}
inline bool
MouseButtonTransitionedUp(gs_mouse_state Mouse, u32 Index)
{
- bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0;
- bool WasDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_WasDownBit)) != 0;
- return !IsDown && WasDown;
+ bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0;
+ bool WasDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_WasDownBit)) != 0;
+ return !IsDown && WasDown;
}
inline bool
MouseButtonTransitionedUp(gs_mouse_state Mouse, gs_key Button)
{
- u32 Index = GetMouseButtonIndex(Button);
- return MouseButtonTransitionedUp(Mouse, Index);
+ u32 Index = GetMouseButtonIndex(Button);
+ return MouseButtonTransitionedUp(Mouse, Index);
}
inline bool
MouseButtonIsUp(gs_mouse_state Mouse, u32 Index)
{
- bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0;
- return !IsDown;
+ bool IsDown = (Mouse.ButtonStates[Index] & (1 << MouseButton_IsDownBit)) != 0;
+ return !IsDown;
}
inline bool
MouseButtonIsUp(gs_mouse_state Mouse, gs_key Button)
{
- u32 Index = GetMouseButtonIndex(Button);
- return MouseButtonIsUp(Mouse, Index);
+ u32 Index = GetMouseButtonIndex(Button);
+ return MouseButtonIsUp(Mouse, Index);
}
internal void
SetMouseButtonTransitionedDown(gs_mouse_state* Mouse, gs_key Button)
{
- u32 Index = GetMouseButtonIndex(Button);
-
- Mouse->ButtonStates[Index] = 0;
- Mouse->ButtonStates[Index] |= MouseButton_IsDown << MouseButton_IsDownBit;
- Mouse->ButtonStates[Index] |= MouseButton_WasNotDown << MouseButton_WasDownBit;
+ u32 Index = GetMouseButtonIndex(Button);
+
+ Mouse->ButtonStates[Index] = 0;
+ Mouse->ButtonStates[Index] |= MouseButton_IsDown << MouseButton_IsDownBit;
+ Mouse->ButtonStates[Index] |= MouseButton_WasNotDown << MouseButton_WasDownBit;
}
internal void
SetMouseButtonTransitionedUp(gs_mouse_state* Mouse, gs_key Button)
{
- u32 Index = GetMouseButtonIndex(Button);
-
- Mouse->ButtonStates[Index] = 0;
- Mouse->ButtonStates[Index] |= MouseButton_IsNotDown << MouseButton_IsDownBit;
- Mouse->ButtonStates[Index] |= MouseButton_WasDown << MouseButton_WasDownBit;
+ u32 Index = GetMouseButtonIndex(Button);
+
+ Mouse->ButtonStates[Index] = 0;
+ Mouse->ButtonStates[Index] |= MouseButton_IsNotDown << MouseButton_IsDownBit;
+ Mouse->ButtonStates[Index] |= MouseButton_WasDown << MouseButton_WasDownBit;
}
internal void
AdvanceMouseButtonState(gs_mouse_state* Mouse, gs_key Button)
{
- u32 Index = GetMouseButtonIndex(Button);
-
- if (MouseButtonIsDown(*Mouse, Index))
- {
- Mouse->ButtonStates[Index] |= MouseButton_WasDown << MouseButton_WasDownBit;
- }
- else
- {
- Mouse->ButtonStates[Index] &= MouseButton_WasNotDown << MouseButton_WasDownBit;
- }
+ u32 Index = GetMouseButtonIndex(Button);
+
+ if (MouseButtonIsDown(*Mouse, Index))
+ {
+ Mouse->ButtonStates[Index] |= MouseButton_WasDown << MouseButton_WasDownBit;
+ }
+ else
+ {
+ Mouse->ButtonStates[Index] &= MouseButton_WasNotDown << MouseButton_WasDownBit;
+ }
}
internal void
AdvanceMouseButtonsState(gs_mouse_state* Mouse)
{
- AdvanceMouseButtonState(Mouse, gs_Key_MouseLeftButton);
- AdvanceMouseButtonState(Mouse, gs_Key_MouseMiddleButton);
- AdvanceMouseButtonState(Mouse, gs_Key_MouseRightButton);
+ AdvanceMouseButtonState(Mouse, gs_Key_MouseLeftButton);
+ AdvanceMouseButtonState(Mouse, gs_Key_MouseMiddleButton);
+ AdvanceMouseButtonState(Mouse, gs_Key_MouseRightButton);
}
///////////////////////////
@@ -3831,17 +3551,17 @@ AdvanceMouseButtonsState(gs_mouse_state* Mouse)
static u32
HostToNetU32(u32 In)
{
- unsigned char *s = (unsigned char *)&In;
- u32 Result = (u32)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
- return Result;
+ unsigned char *s = (unsigned char *)&In;
+ u32 Result = (u32)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
+ return Result;
}
static u16
HostToNetU16(u16 In)
{
- unsigned char *s = (unsigned char *)&In;
- u16 Result = (u16)(s[0] << 8 | s[1]);
- return Result;
+ unsigned char *s = (unsigned char *)&In;
+ u16 Result = (u16)(s[0] << 8 | s[1]);
+ return Result;
}
diff --git a/src/gs_libs/gs_types.h b/src/gs_libs/gs_types.h
index e6ab857..2a1051e 100644
--- a/src/gs_libs/gs_types.h
+++ b/src/gs_libs/gs_types.h
@@ -66,46 +66,46 @@ typedef double r64;
enum gs_basic_type
{
- gs_BasicType_char,
- gs_BasicType_b8,
- gs_BasicType_b32,
- gs_BasicType_b64,
- gs_BasicType_u8,
- gs_BasicType_u16,
- gs_BasicType_u32,
- gs_BasicType_u64,
- gs_BasicType_s8,
- gs_BasicType_s16,
- gs_BasicType_s32,
- gs_BasicType_s64,
- gs_BasicType_r32,
- gs_BasicType_r64,
-
- gs_BasicType_Count,
+ gs_BasicType_char,
+ gs_BasicType_b8,
+ gs_BasicType_b32,
+ gs_BasicType_b64,
+ gs_BasicType_u8,
+ gs_BasicType_u16,
+ gs_BasicType_u32,
+ gs_BasicType_u64,
+ gs_BasicType_s8,
+ gs_BasicType_s16,
+ gs_BasicType_s32,
+ gs_BasicType_s64,
+ gs_BasicType_r32,
+ gs_BasicType_r64,
+
+ gs_BasicType_Count,
};
global_const u64 gs_BasicTypeSizes[] =
{
- sizeof(char),
- sizeof(b8),
- sizeof(b32),
- sizeof(b64),
- sizeof(u8),
- sizeof(u16),
- sizeof(u32),
- sizeof(u64),
- sizeof(s8),
- sizeof(s16),
- sizeof(s32),
- sizeof(s64),
- sizeof(r32),
- sizeof(r64),
+ sizeof(char),
+ sizeof(b8),
+ sizeof(b32),
+ sizeof(b64),
+ sizeof(u8),
+ sizeof(u16),
+ sizeof(u32),
+ sizeof(u64),
+ sizeof(s8),
+ sizeof(s16),
+ sizeof(s32),
+ sizeof(s64),
+ sizeof(r32),
+ sizeof(r64),
};
internal u64
BasicTypeSize(gs_basic_type Type)
{
- return gs_BasicTypeSizes[(u32)Type];
+ return gs_BasicTypeSizes[(u32)Type];
}
global_const u8 MaxU8 = 0xFF;
@@ -137,74 +137,75 @@ global_const r64 MinR64 = -MaxR64;
global_const r64 SmallestPositiveR64 = 4.94065645841247e-324;
global_const r64 EpsilonR64 = 1.11022302462515650e-16;
-// TODO: va_start and va_arg replacements
+global_const r64 NanosToSeconds = 1 / 10000000.0;
+global_const r64 SecondsToNanos = 10000000.0;
internal r32
DegToRadR32(r32 Degrees)
{
- return (Degrees * (PiR32 / 180.0f));
+ return (Degrees * (PiR32 / 180.0f));
}
internal r32
RadToDegR32(r32 Radians)
{
- return (Radians * (180.0f / PiR32));
+ return (Radians * (180.0f / PiR32));
}
struct s8_array
{
- s8* Values;
- u32 Count;
- u32 CountMax;
+ s8* Values;
+ u32 Count;
+ u32 CountMax;
};
struct s16_array
{
- s16* Values;
- u32 Count;
- u32 CountMax;
+ s16* Values;
+ u32 Count;
+ u32 CountMax;
};
struct s32_array
{
- s32* Values;
- u32 Count;
- u32 CountMax;
+ s32* Values;
+ u32 Count;
+ u32 CountMax;
};
struct s64_array
{
- s64* Values;
- u32 Count;
- u32 CountMax;
+ s64* Values;
+ u32 Count;
+ u32 CountMax;
};
struct u8_array
{
- u8* Values;
- u32 Count;
- u32 CountMax;
+ u8* Values;
+ u32 Count;
+ u32 CountMax;
};
struct u16_array
{
- u16* Values;
- u32 Count;
- u32 CountMax;
+ u16* Values;
+ u32 Count;
+ u32 CountMax;
};
struct u32_array
{
- u32* Values;
- u32 Count;
- u32 CountMax;
+ u32* Values;
+ u32 Count;
+ u32 CountMax;
};
struct u64_array
{
- u64* Values;
- u32 Count;
- u32 CountMax;
+ u64* Values;
+ u32 Count;
+ u32 CountMax;
};
@@ -232,7 +233,7 @@ struct u64_array
# define InvalidDefaultCase default: { AssertBreak("invalid default case"); } break;
# define StaticAssert(c) \
enum { \
- Glue(gs_AssertFail_, __LINE__) = 1 / (int)(!!(c)), \
+Glue(gs_AssertFail_, __LINE__) = 1 / (int)(!!(c)), \
}
#else
# define Assert(c)
@@ -247,7 +248,7 @@ enum { \
#define DontCompile ImAfraidICantDoThat
#define LineNumberString Stringify(__LINE__)
-#define FileNameAndLineNumberString_ __FILE__ ":" LineNumberString ":"
+#define FileNameAndLineNumberString_ __FILE__ ":" LineNumberString ":" __FUNCTION__
#define FileNameAndLineNumberString (char*)FileNameAndLineNumberString_
//
@@ -273,22 +274,24 @@ enum { \
#define IsPowerOfTwo(x) (((x) & ((x) - 1)) == 0)
#define IsOdd(x) (((x) & 1) != 0)
+#define CArrayLength(arr) (sizeof(arr) / (sizeof(arr[0])))
+
internal void
ZeroMemory_(u8* Memory, u64 Size)
{
- for (u64 i = 0; i < Size; i++)
- {
- Memory[i] = 0;
- }
+ for (u64 i = 0; i < Size; i++)
+ {
+ Memory[i] = 0;
+ }
}
internal void
CopyMemory_(u8* From, u8* To, u64 Size)
{
- for (u64 i = 0; i < Size; i++)
- {
- To[i] = From[i];
- }
+ for (u64 i = 0; i < Size; i++)
+ {
+ To[i] = From[i];
+ }
}
#define StaticArrayLength(arr) sizeof(arr) / sizeof((arr)[0])
@@ -306,8 +309,8 @@ CopyMemory_(u8* From, u8* To, u64 Size)
#define SLLPop_(list_tail) list_tail=list_tail=list_tail->next
#define SLLPop(list_tail) (SLLPop_((list_tail)))
-#define SLLNext(ele_at) ele_at = ele_at->Next;
-#define SLLPrev(ele_at) ele_at = ele_at->Prev;
+#define SLLNext(ele_at) ele_at = ele_at->Next
+#define SLLPrev(ele_at) ele_at = ele_at->Prev
#define SLLInit(head,tail,first_ele) head=first_ele, tail=first_ele;
@@ -319,80 +322,80 @@ else { SLLInit(first,last,new_ele); }
union v2
{
- struct
- {
- r32 x;
- r32 y;
- };
- r32 E[2];
+ struct
+ {
+ r32 x;
+ r32 y;
+ };
+ r32 E[2];
};
union v3
{
- struct
- {
- r32 x;
- r32 y;
- r32 z;
- };
- struct
- {
- r32 r;
- r32 g;
- r32 b;
- };
- struct
- {
- v2 xy;
- r32 _z0;
- };
- struct
- {
- r32 _x0;
- v2 yz;
- };
- r32 E[3];
+ struct
+ {
+ r32 x;
+ r32 y;
+ r32 z;
+ };
+ struct
+ {
+ r32 r;
+ r32 g;
+ r32 b;
+ };
+ struct
+ {
+ v2 xy;
+ r32 _z0;
+ };
+ struct
+ {
+ r32 _x0;
+ v2 yz;
+ };
+ r32 E[3];
};
union v4
{
- struct
- {
- r32 x;
- r32 y;
- r32 z;
- r32 w;
- };
- struct
- {
- r32 r;
- r32 g;
- r32 b;
- r32 a;
- };
- struct
- {
- v2 xy;
- v2 zw;
- };
- struct
- {
- r32 _x0;
- v2 yz;
- r32 _w0;
- };
- struct
- {
- v3 xyz;
- r32 _w1;
- };
- r32 E[4];
+ struct
+ {
+ r32 x;
+ r32 y;
+ r32 z;
+ r32 w;
+ };
+ struct
+ {
+ r32 r;
+ r32 g;
+ r32 b;
+ r32 a;
+ };
+ struct
+ {
+ v2 xy;
+ v2 zw;
+ };
+ struct
+ {
+ r32 _x0;
+ v2 yz;
+ r32 _w0;
+ };
+ struct
+ {
+ v3 xyz;
+ r32 _w1;
+ };
+ r32 E[4];
};
struct v4_ray
{
- v4 Origin;
- v4 Direction;
+ v4 Origin;
+ v4 Direction;
};
#define WhiteV4 v4{1, 1, 1, 1}
@@ -410,76 +413,76 @@ struct v4_ray
struct v2_array
{
- v2* Vectors;
- u32 Count;
- u32 CountMax;
+ v2* Vectors;
+ u32 Count;
+ u32 CountMax;
};
struct v3_array
{
- v3* Vectors;
- u32 Count;
- u32 CountMax;
+ v3* Vectors;
+ u32 Count;
+ u32 CountMax;
};
struct v4_array
{
- v4* Vectors;
- u32 Count;
- u32 CountMax;
+ v4* Vectors;
+ u32 Count;
+ u32 CountMax;
};
struct range1
{
- r32 Min;
- r32 Max;
+ r32 Min;
+ r32 Max;
};
struct range2
{
- v2 Min;
- v2 Max;
+ v2 Min;
+ v2 Max;
};
typedef range2 rect2;
struct range3
{
- v3 Min;
- v3 Max;
+ v3 Min;
+ v3 Max;
};
struct range4
{
- v4 Min;
- v4 Max;
+ v4 Min;
+ v4 Max;
};
struct range1_array
{
- range1* Ranges;
- u32 Count;
- u32 CountMax;
+ range1* Ranges;
+ u32 Count;
+ u32 CountMax;
};
struct range2_array
{
- range2* Ranges;
- u32 Count;
- u32 CountMax;
+ range2* Ranges;
+ u32 Count;
+ u32 CountMax;
};
struct range3_array
{
- range3* Ranges;
- u32 Count;
- u32 CountMax;
+ range3* Ranges;
+ u32 Count;
+ u32 CountMax;
};
struct range4_array
{
- range4* Ranges;
- u32 Count;
- u32 CountMax;
+ range4* Ranges;
+ u32 Count;
+ u32 CountMax;
};
#define Range1Expand(r) (r).Min, (r).Max
@@ -493,39 +496,39 @@ struct range4_array
union m33
{
- float Array[9];
- struct
- {
- r32 AXx; r32 AYx; r32 AZx;
- r32 AXy; r32 AYy; r32 AZy;
- r32 AXz; r32 AYz; r32 AZz;
- };
+ float Array[9];
+ struct
+ {
+ r32 AXx; r32 AYx; r32 AZx;
+ r32 AXy; r32 AYy; r32 AZy;
+ r32 AXz; r32 AYz; r32 AZz;
+ };
};
union m44
{
- float Array[16];
- struct
- {
- r32 AXx; r32 AYx; r32 AZx; r32 Tx;
- r32 AXy; r32 AYy; r32 AZy; r32 Ty;
- r32 AXz; r32 AYz; r32 AZz; r32 Tz;
- r32 AXw; r32 AYw; r32 AZw; r32 Tw;
- };
+ float Array[16];
+ struct
+ {
+ r32 AXx; r32 AYx; r32 AZx; r32 Tx;
+ r32 AXy; r32 AYy; r32 AZy; r32 Ty;
+ r32 AXz; r32 AYz; r32 AZz; r32 Tz;
+ r32 AXw; r32 AYw; r32 AZw; r32 Tw;
+ };
};
struct m33_array
{
- m33* Matrices;
- u32 Count;
- u32 CountMax;
+ m33* Matrices;
+ u32 Count;
+ u32 CountMax;
};
struct m44_array
{
- m44* Matrices;
- u32 Count;
- u32 CountMax;
+ m44* Matrices;
+ u32 Count;
+ u32 CountMax;
};
//////////////////////////
@@ -534,49 +537,70 @@ struct m44_array
struct gs_const_string
{
- union
- {
- char* Str;
- u8* Data;;
- };
- u64 Length;
+ union
+ {
+ char* Str;
+ u8* Data;;
+ };
+ u64 Length;
};
struct gs_string
{
- union
+ union
+ {
+ gs_const_string ConstString;
+ struct
{
- gs_const_string ConstString;
- struct
- {
- char* Str;
- u64 Length;
- };
+ char* Str;
+ u64 Length;
};
- u64 Size;
+ };
+ u64 Size;
};
struct gs_const_string_array
{
- gs_const_string* Strings;
- u64 Count;
- u64 Used;
+ gs_const_string* Strings;
+ u64 Count;
+ u64 CountMax;
};
struct gs_string_array
{
- gs_string* Strings;
- u64 Count;
- u64 CountMax;
+ gs_string* Strings;
+ u64 Count;
+ u64 CountMax;
};
internal u64
CStringLength(char* Str)
{
- char* At = Str;
- while (*At) { At++; }
- u64 Result = PointerDifference(At, Str);
- return Result;
+ char* At = Str;
+ while (*At) { At++; }
+ u64 Result = PointerDifference(At, Str);
+ return Result;
+}
+
+internal bool
+CStringsEqual(char* A, char* B)
+{
+ bool Result = true;
+
+ char* AAt = A;
+ char* BAt = B;
+ while (AAt[0] && BAt[0])
+ {
+ if (AAt[0] != BAt[0])
+ {
+ Result = false;
+ break;
+ }
+ AAt++;
+ BAt++;
+ }
+ if (AAt[0] != 0 || BAt[0] != 0) { Result = false; }
+ return Result;
}
#define StringExpand(str) (int)(str).Length, (str).Str
@@ -602,79 +626,35 @@ typedef struct gs_thread_context gs_thread_context;
struct gs_data
{
- u8* Memory;
- u64 Size;
+ u8* Memory;
+ u64 Size;
};
struct gs_data_array
{
- gs_data* Data;
- u64 Count;
- u64 CountMax;
+ gs_data* Data;
+ u64 Count;
+ u64 CountMax;
};
enum gs_access_flag
{
- gs_AccessFlag_Read = 1 << 0,
- gs_AccessFlag_Write = 1 << 1,
- gs_AccessFlag_Exec = 1 << 2,
+ gs_AccessFlag_Read = 1 << 0,
+ gs_AccessFlag_Write = 1 << 1,
+ gs_AccessFlag_Exec = 1 << 2,
};
typedef s32 gs_scan_direction;
enum
{
- gs_Scan_Backward = -1,
- gs_Scan_Forward = 1,
+ gs_Scan_Backward = -1,
+ gs_Scan_Forward = 1,
};
-#define ALLOCATOR_ALLOC(name) void* name(u64 Size, u64* ResultSize)
-typedef ALLOCATOR_ALLOC(allocator_allocate);
-#define ALLOCATOR_FREE(name) void name(void* Ptr, u64 Size)
-typedef ALLOCATOR_FREE(allocator_free);
+internal u64 RoundUpTo64(u64 Value, u64 Alignment);
-struct gs_allocator
-{
- allocator_allocate* Alloc;
- allocator_free* Free;
-};
-
-struct gs_memory_cursor
-{
- gs_data Data;
- u64 Position;
-};
-
-struct gs_memory_cursor_list
-{
- gs_memory_cursor Cursor;
- gs_memory_cursor_list* Next;
- gs_memory_cursor_list* Prev;
-};
-
-enum arena_type
-{
- Arena_BaseArena,
- Arena_SubArena,
-};
-
-struct gs_memory_arena
-{
- arena_type Type;
- gs_allocator Allocator;
- gs_memory_arena* Parent;
-
- gs_memory_cursor_list* CursorList;
- u64 MemoryChunkSize;
- u64 MemoryAlignment;
-};
-
-struct gs_memory_arena_array
-{
- gs_memory_arena* Arenas;
- u32 Count;
- u32 Size;
-};
+#include "gs_memory.h"
///////////////////////////////
//
@@ -683,16 +663,16 @@ struct gs_memory_arena_array
struct gs_string_builder_buffer
{
- gs_string String;
- gs_string_builder_buffer* Next;
+ gs_string String;
+ gs_string_builder_buffer* Next;
};
struct gs_string_builder
{
- gs_memory_arena* Arena;
- u32 BufferSize;
- gs_string_builder_buffer* Root;
- gs_string_builder_buffer* Head;
+ gs_memory_arena* Arena;
+ u32 BufferSize;
+ gs_string_builder_buffer* Root;
+ gs_string_builder_buffer* Head;
};
///////////////////////////////
@@ -707,8 +687,8 @@ typedef DEBUG_PRINT(debug_print);
struct debug_output
{
- gs_memory_arena* Storage;
- debug_print* Print;
+ gs_memory_arena* Storage;
+ debug_print* Print;
};
///////////////////////////////
@@ -723,34 +703,34 @@ struct debug_output
struct gs_dynarray_buffer
{
- u8* Memory;
+ u8* Memory;
};
struct gs_dynarray
{
- gs_memory_arena Arena;
-
- gs_dynarray_buffer* Buffers;
- u64 BuffersCount;
- u64 ElementCount;
-
- u64 ElementSize;
- u64 ElementsPerBuffer;
+ gs_memory_arena Arena;
+
+ gs_dynarray_buffer* Buffers;
+ u64 BuffersCount;
+ u64 ElementCount;
+
+ u64 ElementSize;
+ u64 ElementsPerBuffer;
};
struct gs_dynarray_handle
{
- u64 BufferIndex;
- u64 IndexInBuffer;
+ u64 BufferIndex;
+ u64 IndexInBuffer;
};
#define INVALID_DYNARRAY_HANDLE gs_dynarray_handle{0, 0}
struct gs_dynarray_handle_list
{
- gs_dynarray_handle* Handles;
- u32 Count;
- u32 Size;
+ gs_dynarray_handle* Handles;
+ u32 Count;
+ u32 Size;
};
///////////////////////////////
@@ -763,39 +743,39 @@ struct gs_dynarray_handle_list
enum enumerate_directory_flag
{
- EnumerateDirectory_Recurse = 1 << 0,
- EnumerateDirectory_IncludeDirectories = 1 << 1,
+ EnumerateDirectory_Recurse = 1 << 0,
+ EnumerateDirectory_IncludeDirectories = 1 << 1,
};
struct gs_file_info
{
- gs_const_string Path;
- gs_const_string AbsolutePath;
- u64 FileSize;
- u64 CreationTime;
- u64 LastWriteTime;
- b32 IsDirectory;
+ gs_const_string Path;
+ gs_const_string AbsolutePath;
+ u64 FileSize;
+ u64 CreationTime;
+ u64 LastWriteTime;
+ b32 IsDirectory;
};
struct gs_file_info_array
{
- gs_file_info* Values;
- u32 Count;
- u32 MaxCount;
+ gs_file_info* Values;
+ u32 Count;
+ u32 MaxCount;
};
struct gs_file
{
- union
+ union
+ {
+ gs_data Data;
+ struct
{
- gs_data Data;
- struct
- {
- u8* Memory;
- u64 Size;
- };
+ u8* Memory;
+ u64 Size;
};
- gs_file_info FileInfo;
+ };
+ gs_file_info FileInfo;
};
typedef struct gs_file_handler gs_file_handler;
@@ -814,11 +794,11 @@ typedef ENUMERATE_DIRECTORY(file_handler_enumerate_directory);
struct gs_file_handler
{
- file_handler_get_file_info* GetFileInfo;
- file_handler_read_entire_file* ReadEntireFile;
- file_handler_write_entire_file* WriteEntireFile;
- file_handler_enumerate_directory* EnumerateDirectory;
- gs_memory_arena* Transient;
+ file_handler_get_file_info* GetFileInfo;
+ file_handler_read_entire_file* ReadEntireFile;
+ file_handler_write_entire_file* WriteEntireFile;
+ file_handler_enumerate_directory* EnumerateDirectory;
+ gs_memory_arena* Transient;
};
@@ -834,8 +814,8 @@ typedef GET_SECONDS_ELAPSED(get_seconds_elapsed);
struct gs_time_handler
{
- get_wall_clock* GetWallClock;
- get_seconds_elapsed* GetSecondsElapsed;
+ get_wall_clock* GetWallClock;
+ get_seconds_elapsed* GetSecondsElapsed;
};
///////////////////////////////
@@ -844,7 +824,7 @@ struct gs_time_handler
struct gs_random_series
{
- u32 Value;
+ u32 Value;
};
///////////////////////////////
@@ -853,8 +833,8 @@ struct gs_random_series
struct gs_radix_list
{
- u64_array Radixes;
- u64_array IDs;
+ u64_array Radixes;
+ u64_array IDs;
};
@@ -864,115 +844,115 @@ struct gs_radix_list
enum gs_event_type
{
- gs_EventType_Unknown,
-
- // Reached end of event stream
- gs_EventType_NoMoreEvents,
- // There was an event but it requires no action from the using program
- gs_EventType_NoEvent,
-
- gs_EventType_KeyPressed,
- gs_EventType_KeyReleased,
-
- gs_EventType_MouseMoved,
- gs_EventType_MouseWheel,
-
- gs_EventType_Count,
+ gs_EventType_Unknown,
+
+ // Reached end of event stream
+ gs_EventType_NoMoreEvents,
+ // There was an event but it requires no action from the using program
+ gs_EventType_NoEvent,
+
+ gs_EventType_KeyPressed,
+ gs_EventType_KeyReleased,
+
+ gs_EventType_MouseMoved,
+ gs_EventType_MouseWheel,
+
+ gs_EventType_Count,
};
enum gs_key
{
- gs_Key_Invalid,
-
- gs_Key_Esc,
-
- gs_Key_Space,
- gs_Key_Tab,
- gs_Key_CapsLock,
- gs_Key_Shift, gs_Key_LeftShift, gs_Key_RightShift,
- gs_Key_Control, gs_Key_LeftCtrl, gs_Key_RightCtrl,
- gs_Key_Fn,
- gs_Key_Alt,
- gs_Key_PageUp, gs_Key_PageDown,
- gs_Key_End, gs_Key_Home, gs_Key_Select,
- gs_Key_Backspace, gs_Key_Delete,
- gs_Key_Enter,
-
- // Function Keys
- gs_Key_F0, gs_Key_F1, gs_Key_F2, gs_Key_F3, gs_Key_F4, gs_Key_F5, gs_Key_F6, gs_Key_F7,
- gs_Key_F8, gs_Key_F9, gs_Key_F10, gs_Key_F11, gs_Key_F12,
-
- // Letters
- gs_Key_a, gs_Key_b, gs_Key_c, gs_Key_d, gs_Key_e, gs_Key_f, gs_Key_g, gs_Key_h,
- gs_Key_i, gs_Key_j, gs_Key_k, gs_Key_l, gs_Key_m, gs_Key_n, gs_Key_o, gs_Key_p,
- gs_Key_q, gs_Key_r, gs_Key_s, gs_Key_t, gs_Key_u, gs_Key_v, gs_Key_w, gs_Key_x,
- gs_Key_y, gs_Key_z,
-
- gs_Key_A, gs_Key_B, gs_Key_C, gs_Key_D, gs_Key_E, gs_Key_F, gs_Key_G, gs_Key_H,
- gs_Key_I, gs_Key_J, gs_Key_K, gs_Key_L, gs_Key_M, gs_Key_N, gs_Key_O, gs_Key_P,
- gs_Key_Q, gs_Key_R, gs_Key_S, gs_Key_T, gs_Key_U, gs_Key_V, gs_Key_W, gs_Key_X,
- gs_Key_Y, gs_Key_Z,
-
- // Numbers
- gs_Key_0, gs_Key_1, gs_Key_2, gs_Key_3, gs_Key_4, gs_Key_5, gs_Key_6, gs_Key_7,
- gs_Key_8, gs_Key_9,
-
- gs_Key_Num0, gs_Key_Num1, gs_Key_Num2, gs_Key_Num3, gs_Key_Num4, gs_Key_Num5,
- gs_Key_Num6, gs_Key_Num7, gs_Key_Num8, gs_Key_Num9,
-
- // Symbols
- gs_Key_Bang, gs_Key_At, gs_Key_Pound, gs_Key_Dollar, gs_Key_Percent, gs_Key_Carrot,
- gs_Key_Ampersand, gs_Key_Star, gs_Key_LeftParen, gs_Key_RightParen, gs_Key_Minus, gs_Key_Plus,
- gs_Key_Equals, gs_Key_Underscore, gs_Key_OpenSquareBracket, gs_Key_CloseSquareBracket, gs_Key_OpenCurlyBracket,
- gs_Key_CloseCurlyBracket, gs_Key_Colon, gs_Key_SemiColon, gs_Key_SingleQuote, gs_Key_DoubleQuote,
- gs_Key_ForwardSlash, gs_Key_Backslash, gs_Key_Pipe, gs_Key_Comma, gs_Key_Period,
- gs_Key_QuestionMark, gs_Key_LessThan, gs_Key_GreaterThan, gs_Key_Tilde, gs_Key_BackQuote,
-
- // Arrows
- gs_Key_UpArrow,
- gs_Key_DownArrow,
- gs_Key_LeftArrow,
- gs_Key_RightArrow,
-
- // Mouse
- // NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions
- gs_Key_MouseLeftButton,
- gs_Key_MouseMiddleButton,
- gs_Key_MouseRightButton,
- gs_Key_MouseX1Button,
- gs_Key_MouseX2Button,
-
- gs_Key_Count,
+ gs_Key_Invalid,
+
+ gs_Key_Esc,
+
+ gs_Key_Space,
+ gs_Key_Tab,
+ gs_Key_CapsLock,
+ gs_Key_Shift, gs_Key_LeftShift, gs_Key_RightShift,
+ gs_Key_Control, gs_Key_LeftCtrl, gs_Key_RightCtrl,
+ gs_Key_Fn,
+ gs_Key_Alt,
+ gs_Key_PageUp, gs_Key_PageDown,
+ gs_Key_End, gs_Key_Home, gs_Key_Select,
+ gs_Key_Backspace, gs_Key_Delete,
+ gs_Key_Enter,
+
+ // Function Keys
+ gs_Key_F0, gs_Key_F1, gs_Key_F2, gs_Key_F3, gs_Key_F4, gs_Key_F5, gs_Key_F6, gs_Key_F7,
+ gs_Key_F8, gs_Key_F9, gs_Key_F10, gs_Key_F11, gs_Key_F12,
+
+ // Letters
+ gs_Key_a, gs_Key_b, gs_Key_c, gs_Key_d, gs_Key_e, gs_Key_f, gs_Key_g, gs_Key_h,
+ gs_Key_i, gs_Key_j, gs_Key_k, gs_Key_l, gs_Key_m, gs_Key_n, gs_Key_o, gs_Key_p,
+ gs_Key_q, gs_Key_r, gs_Key_s, gs_Key_t, gs_Key_u, gs_Key_v, gs_Key_w, gs_Key_x,
+ gs_Key_y, gs_Key_z,
+
+ gs_Key_A, gs_Key_B, gs_Key_C, gs_Key_D, gs_Key_E, gs_Key_F, gs_Key_G, gs_Key_H,
+ gs_Key_I, gs_Key_J, gs_Key_K, gs_Key_L, gs_Key_M, gs_Key_N, gs_Key_O, gs_Key_P,
+ gs_Key_Q, gs_Key_R, gs_Key_S, gs_Key_T, gs_Key_U, gs_Key_V, gs_Key_W, gs_Key_X,
+ gs_Key_Y, gs_Key_Z,
+
+ // Numbers
+ gs_Key_0, gs_Key_1, gs_Key_2, gs_Key_3, gs_Key_4, gs_Key_5, gs_Key_6, gs_Key_7,
+ gs_Key_8, gs_Key_9,
+
+ gs_Key_Num0, gs_Key_Num1, gs_Key_Num2, gs_Key_Num3, gs_Key_Num4, gs_Key_Num5,
+ gs_Key_Num6, gs_Key_Num7, gs_Key_Num8, gs_Key_Num9,
+
+ // Symbols
+ gs_Key_Bang, gs_Key_At, gs_Key_Pound, gs_Key_Dollar, gs_Key_Percent, gs_Key_Carrot,
+ gs_Key_Ampersand, gs_Key_Star, gs_Key_LeftParen, gs_Key_RightParen, gs_Key_Minus, gs_Key_Plus,
+ gs_Key_Equals, gs_Key_Underscore, gs_Key_OpenSquareBracket, gs_Key_CloseSquareBracket, gs_Key_OpenCurlyBracket,
+ gs_Key_CloseCurlyBracket, gs_Key_Colon, gs_Key_SemiColon, gs_Key_SingleQuote, gs_Key_DoubleQuote,
+ gs_Key_ForwardSlash, gs_Key_Backslash, gs_Key_Pipe, gs_Key_Comma, gs_Key_Period,
+ gs_Key_QuestionMark, gs_Key_LessThan, gs_Key_GreaterThan, gs_Key_Tilde, gs_Key_BackQuote,
+
+ // Arrows
+ gs_Key_UpArrow,
+ gs_Key_DownArrow,
+ gs_Key_LeftArrow,
+ gs_Key_RightArrow,
+
+ // Mouse
+ // NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions
+ gs_Key_MouseLeftButton,
+ gs_Key_MouseMiddleButton,
+ gs_Key_MouseRightButton,
+ gs_Key_MouseX1Button,
+ gs_Key_MouseX2Button,
+
+ gs_Key_Count,
};
enum gs_modifier_key_flags
{
- gs_ModifierKeyFlag_Shift = 1 << 0,
- gs_ModifierKeyFlag_Ctrl = 1 << 1,
- gs_ModifierKeyFlag_Alt = 1 << 2,
+ gs_ModifierKeyFlag_Shift = 1 << 0,
+ gs_ModifierKeyFlag_Ctrl = 1 << 1,
+ gs_ModifierKeyFlag_Alt = 1 << 2,
};
struct gs_input_event
{
- gs_event_type Type;
- gs_key Key;
- v2 Position;
- r32 Amount;
- b32 Modifiers;
+ gs_event_type Type;
+ gs_key Key;
+ v2 Position;
+ r32 Amount;
+ b32 Modifiers;
};
struct gs_input_event_buffer
{
- gs_input_event* Values;
- u32 Count;
- u32 MaxCount;
+ gs_input_event* Values;
+ u32 Count;
+ u32 MaxCount;
};
struct gs_mouse_state
{
- v2 Position;
- b32 ButtonStates[3];
- v2 DownPosition;
+ v2 Position;
+ b32 ButtonStates[3];
+ v2 DownPosition;
};
#define MouseButton_IsDownBit 0
@@ -988,29 +968,29 @@ struct gs_mouse_state
struct gs_thread_info
{
- u32 ThreadID;
+ u32 ThreadID;
};
struct gs_thread_context
{
- gs_thread_info ThreadInfo;
-
- // TODO(Peter): Pull these handlers out into just a gs_context struct so
- // they can be shared across threads.
- // specifically the allocator
- gs_allocator Allocator;
- gs_file_handler FileHandler;
- debug_output DebugOutput;
- gs_time_handler TimeHandler;
-
- gs_memory_arena* Transient;
+ gs_thread_info ThreadInfo;
+
+ // TODO(Peter): Pull these handlers out into just a gs_context struct so
+ // they can be shared across threads.
+ // specifically the allocator
+ gs_allocator Allocator;
+ gs_file_handler FileHandler;
+ debug_output DebugOutput;
+ gs_time_handler TimeHandler;
+
+ gs_memory_arena* Transient;
};
// Threads
typedef struct platform_thread_handle
{
- u32 Index;
+ u32 Index;
} platform_thread_handle;
typedef struct platform_thread_manager platform_thread_manager;
@@ -1020,13 +1000,12 @@ typedef THREAD_PROC_(thread_proc_);
typedef struct platform_thread
{
- u8* PlatformHandle;
- thread_proc_* Proc;
- u8* UserData;
- // TODO(pjs): Some kind of platform thread handle
+ u8* PlatformHandle;
+ thread_proc_* Proc;
+ u8* UserData;
} platform_thread;
-#define CREATE_THREAD(name) bool name(platform_thread* Thread, thread_proc_* Proc, u8* UserData)
+#define CREATE_THREAD(name) bool name(platform_thread* Thread, thread_proc_* Proc, u8* UserData, gs_thread_context Ctx)
typedef CREATE_THREAD(platform_create_thread);
#define KILL_THREAD(name) bool name(platform_thread* Thread)
@@ -1035,11 +1014,11 @@ typedef KILL_THREAD(platform_kill_thread);
#define THREADS_MAX 32
typedef struct platform_thread_manager
{
- b8 ThreadsUsed[THREADS_MAX];
- platform_thread Threads[THREADS_MAX];
-
- platform_create_thread* CreateThreadProc;
- platform_kill_thread* KillThreadProc;
+ b8 ThreadsUsed[THREADS_MAX];
+ platform_thread Threads[THREADS_MAX];
+
+ platform_create_thread* CreateThreadProc;
+ platform_kill_thread* KillThreadProc;
} platform_thread_manager;
// Work Queue
@@ -1048,9 +1027,9 @@ typedef struct gs_work_queue gs_work_queue;
typedef struct gs_worker_thread
{
- gs_thread_context Context;
- gs_work_queue* Queue;
- b32 ShouldExit;
+ gs_thread_context Context;
+ gs_work_queue* Queue;
+ b32 ShouldExit;
} gs_worker_thread;
#define THREAD_PROC(name) void name(gs_thread_context Context, gs_data Data)
@@ -1058,9 +1037,9 @@ typedef THREAD_PROC(thread_proc);
struct gs_threaded_job
{
- thread_proc* WorkProc;
- gs_data Data;
- gs_const_string JobName;
+ thread_proc* WorkProc;
+ gs_data Data;
+ gs_const_string JobName;
};
#define PUSH_WORK_ON_QUEUE(name) void name(gs_work_queue* Queue, thread_proc* WorkProc, gs_data Data, gs_const_string JobName)
@@ -1074,67 +1053,70 @@ typedef RESET_WORK_QUEUE(reset_work_queue);
struct gs_work_queue
{
- void* SemaphoreHandle;
-
- u32 JobsMax;
- u32 volatile JobsCount;
- u32 volatile NextJobIndex;
- u32 volatile JobsCompleted;
- gs_threaded_job* Jobs;
-
- // Work Queue
- push_work_on_queue* PushWorkOnQueue;
- complete_queue_work* CompleteQueueWork;
+ void* SemaphoreHandle;
+
+ u32 JobsMax;
+ u32 volatile JobsCount;
+ u32 volatile NextJobIndex;
+ u32 volatile JobsCompleted;
+ gs_threaded_job* Jobs;
+
+ // Work Queue
+ push_work_on_queue* PushWorkOnQueue;
+ complete_queue_work* CompleteQueueWork;
};
// Sockets
typedef struct platform_socket_handle_
{
- u32 Index;
+ u32 Index;
} platform_socket_handle_;
typedef struct platform_socket
{
- char Addr[128];
- char Port[32];
- u8* PlatformHandle;
+ platform_socket_handle_ Handle;
+ char Addr[128];
+ char Port[32];
+ u8* PlatformHandle;
} platform_socket;
-#define CONNECT_SOCKET(name) bool name(platform_socket* Socket)
+typedef struct platform_socket_manager platform_socket_manager;
+
+#define CONNECT_SOCKET(name) bool name(platform_socket_manager* Manager, platform_socket* Socket)
typedef CONNECT_SOCKET(platform_connect_socket);
-#define CLOSE_SOCKET(name) bool name(platform_socket* Socket)
+#define CLOSE_SOCKET(name) bool name(platform_socket_manager* Manager, platform_socket* Socket)
typedef CLOSE_SOCKET(platform_close_socket);
-#define SOCKET_QUERY_STATUS(name) bool name(platform_socket* Socket)
+#define SOCKET_QUERY_STATUS(name) bool name(platform_socket_manager* Manager, platform_socket* Socket)
typedef SOCKET_QUERY_STATUS(platform_socket_query_status);
-#define SOCKET_PEEK(name) u32 name(platform_socket* Socket)
+#define SOCKET_PEEK(name) u32 name(platform_socket_manager* Manager, platform_socket* Socket)
typedef SOCKET_PEEK(platform_socket_peek);
// TODO(pjs): allow for a size parameter that can be zero
// if provided, that is how big the message it expects to be
// if it equals zero, the proc will peek at the message first to determine
// the needed size
-#define SOCKET_RECEIVE(name) gs_data name(platform_socket* Socket, gs_memory_arena* Storage)
+#define SOCKET_RECEIVE(name) gs_data name(platform_socket_manager* Manager, platform_socket* Socket, gs_memory_arena* Storage)
typedef SOCKET_RECEIVE(platform_socket_receive);
-#define SOCKET_SEND(name) s32 name(platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags)
+#define SOCKET_SEND(name) s32 name(platform_socket_manager* Manager, platform_socket* Socket, u32 Address, u32 Port, gs_data Data, s32 Flags)
typedef SOCKET_SEND(platform_socket_send);
#define SOCKETS_COUNT_MAX 32
typedef struct platform_socket_manager
{
- b8 SocketsUsed[SOCKETS_COUNT_MAX];
- platform_socket Sockets[SOCKETS_COUNT_MAX];
-
- platform_connect_socket* ConnectSocketProc;
- platform_close_socket* CloseSocketProc;
- platform_socket_query_status* SocketQueryStatusProc;
- platform_socket_peek* SocketPeekProc;
- platform_socket_receive* SocketRecieveProc;
- platform_socket_send* SocketSendProc;
+ b8 SocketsUsed[SOCKETS_COUNT_MAX];
+ platform_socket Sockets[SOCKETS_COUNT_MAX];
+
+ platform_connect_socket* ConnectSocketProc;
+ platform_close_socket* CloseSocketProc;
+ platform_socket_query_status* SocketQueryStatusProc;
+ platform_socket_peek* SocketPeekProc;
+ platform_socket_receive* SocketRecieveProc;
+ platform_socket_send* SocketSendProc;
} platform_socket_manager;
#define GS_TYPES_H
diff --git a/src/gs_libs/gs_win32.cpp b/src/gs_libs/gs_win32.cpp
index b96ae2e..18074c6 100644
--- a/src/gs_libs/gs_win32.cpp
+++ b/src/gs_libs/gs_win32.cpp
@@ -9,39 +9,39 @@
struct win32_state
{
- b32 Initialized;
- b32 Running;
+ b32 Initialized;
+ b32 Running;
};
struct win32_opengl_window_info
{
- s32 ColorBits;
- s32 AlphaBits;
- s32 DepthBits;
- HGLRC RenderContext;
+ s32 ColorBits;
+ s32 AlphaBits;
+ s32 DepthBits;
+ HGLRC RenderContext;
};
struct window
{
- char* Name;
- char* ClassName;
- s32 Width;
- s32 Height;
- WNDPROC WindowEventHandler;
-
- WNDCLASS Class;
- HWND Handle;
- HDC DeviceContext;
-
- // TODO(peter): Make this a union?
- win32_opengl_window_info OpenGLInfo;
+ char* Name;
+ char* ClassName;
+ s32 Width;
+ s32 Height;
+ WNDPROC WindowEventHandler;
+
+ WNDCLASS Class;
+ HWND Handle;
+ HDC DeviceContext;
+
+ // TODO(peter): Make this a union?
+ win32_opengl_window_info OpenGLInfo;
};
struct handle_window_msg_result
{
- b32 NeedsUpdate;
-#ifdef DEBUG
- char MessageType[128];
+ b32 NeedsUpdate;
+#if DEBUG
+ char MessageType[128];
#endif
};
@@ -54,8 +54,8 @@ internal s32 Win32ConcatStrings(s32 ALen, char* A, s32 BLen, char* B, s32 DestLe
// Windowing & Graphics
struct win32_offscreen_buffer
{
- texture_buffer Buffer;
- BITMAPINFO Info;
+ texture_buffer Buffer;
+ BITMAPINFO Info;
};
LRESULT CALLBACK Win32HandleWindowsEvents (HWND WindowHandle, UINT Msg, WPARAM wParam, LPARAM lParam);
@@ -64,11 +64,6 @@ internal void Win32UpdateWindowDimension(window* Window);
internal void Win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int Width, int Height);
internal void Win32DisplayBufferInWindow(win32_offscreen_buffer* Buffer, window Window);
-// Memory
-
-internal ALLOCATOR_ALLOC(Win32Alloc);
-internal ALLOCATOR_FREE(Win32Free);
-
///
// Utils
///
@@ -76,32 +71,32 @@ internal ALLOCATOR_FREE(Win32Free);
internal s32
Win32StringLength(char* String)
{
- char* At = String;
- while (*At) { At++; };
- return At - String;
+ char* At = String;
+ while (*At) { At++; };
+ return At - String;
}
internal s32
Win32ConcatStrings(s32 ALength, char* A, s32 BLength, char* B, s32 DestLength, char* Dest)
{
- char* Dst = Dest;
- char* AAt = A;
- int ALengthToCopy = ALength < DestLength ? ALength : DestLength;
- for (s32 a = 0; a < ALength; a++)
- {
- *Dst++ = *AAt++;
- }
- char* BAt = B;
- int DestLengthRemaining = DestLength - (Dst - Dest);
- int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength;
- for (s32 b = 0; b < BLengthToCopy; b++)
- {
- *Dst++ = *BAt++;
- }
- int DestLengthOut = Dst - Dest;
- int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength;
- Dest[NullTermIndex] = 0;
- return DestLengthOut;
+ char* Dst = Dest;
+ char* AAt = A;
+ int ALengthToCopy = ALength < DestLength ? ALength : DestLength;
+ for (s32 a = 0; a < ALength; a++)
+ {
+ *Dst++ = *AAt++;
+ }
+ char* BAt = B;
+ int DestLengthRemaining = DestLength - (Dst - Dest);
+ int BLengthToCopy = BLength < DestLengthRemaining ? BLength : DestLength;
+ for (s32 b = 0; b < BLengthToCopy; b++)
+ {
+ *Dst++ = *BAt++;
+ }
+ int DestLengthOut = Dst - Dest;
+ int NullTermIndex = DestLengthOut < DestLength ? DestLengthOut : DestLength;
+ Dest[NullTermIndex] = 0;
+ return DestLengthOut;
}
///
@@ -112,158 +107,158 @@ internal window
Win32CreateWindow (HINSTANCE HInstance, char* WindowName, s32 Width, s32 Height,
WNDPROC WindowEventHandler)
{
- window Result = {};
- Result.Name = WindowName;
- Result.ClassName = WindowName;
- Result.Width = Width;
- Result.Height = Height;
- Result.WindowEventHandler = WindowEventHandler;
-
- Result.Class = {};
- Result.Class.style = CS_HREDRAW | CS_VREDRAW;
- Result.Class.lpfnWndProc = WindowEventHandler;
- Result.Class.hInstance = HInstance;
- Result.Class.lpszClassName = WindowName;
-
- if (RegisterClass(&Result.Class))
- {
- Result.Handle = CreateWindowEx(
- 0,
- Result.Class.lpszClassName,
- WindowName,
- WS_OVERLAPPEDWINDOW | WS_VISIBLE,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- Width,
- Height,
- 0,
- 0,
- HInstance,
- 0);
- Result.DeviceContext = GetDC(Result.Handle);
- }
-
- return Result;
+ window Result = {};
+ Result.Name = WindowName;
+ Result.ClassName = WindowName;
+ Result.Width = Width;
+ Result.Height = Height;
+ Result.WindowEventHandler = WindowEventHandler;
+
+ Result.Class = {};
+ Result.Class.style = CS_HREDRAW | CS_VREDRAW;
+ Result.Class.lpfnWndProc = WindowEventHandler;
+ Result.Class.hInstance = HInstance;
+ Result.Class.lpszClassName = WindowName;
+
+ if (RegisterClass(&Result.Class))
+ {
+ Result.Handle = CreateWindowEx(
+ 0,
+ Result.Class.lpszClassName,
+ WindowName,
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ Width,
+ Height,
+ 0,
+ 0,
+ HInstance,
+ 0);
+ Result.DeviceContext = GetDC(Result.Handle);
+ }
+
+ return Result;
};
internal window
PlatformCreateWindow (char* WindowName, s32 Width, s32 Height)
{
- HINSTANCE HInstance = GetModuleHandle(NULL);
- return Win32CreateWindow(HInstance, WindowName, Width, Height, Win32HandleWindowsEvents);
+ HINSTANCE HInstance = GetModuleHandle(NULL);
+ return Win32CreateWindow(HInstance, WindowName, Width, Height, Win32HandleWindowsEvents);
}
internal void
CreateOpenGLWindowContext (win32_opengl_window_info Info, window* Window)
{
- // Setup pixel format
- {
- PIXELFORMATDESCRIPTOR PixelFormatDesc = { 0 };
- // TODO: Program seems to work perfectly fine without all other params except dwFlags.
- // Can we skip other params for the sake of brevity?
- PixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
- PixelFormatDesc.nVersion = 1;
- PixelFormatDesc.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
- PixelFormatDesc.iPixelType = PFD_TYPE_RGBA;// TODO(Peter): include this in win32_opengl_window_info?
- PixelFormatDesc.cColorBits = Info.ColorBits;
- PixelFormatDesc.cAlphaBits = Info.AlphaBits;
- PixelFormatDesc.cDepthBits = Info.DepthBits;
- PixelFormatDesc.dwLayerMask = PFD_MAIN_PLANE; // TODO(Peter): include this in win32_opengl_window_info?
- //
-
- s32 PixelFormat = ChoosePixelFormat(Window->DeviceContext, &PixelFormatDesc);
- if (!PixelFormat) { InvalidCodePath; } // TODO: Log: Choose pixel format failed
- if (!SetPixelFormat(Window->DeviceContext, PixelFormat, &PixelFormatDesc)) { InvalidCodePath; } // TODO: Log: Set pixel format failed
- }
+ // Setup pixel format
+ {
+ PIXELFORMATDESCRIPTOR PixelFormatDesc = { 0 };
+ // TODO: Program seems to work perfectly fine without all other params except dwFlags.
+ // Can we skip other params for the sake of brevity?
+ PixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ PixelFormatDesc.nVersion = 1;
+ PixelFormatDesc.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+ PixelFormatDesc.iPixelType = PFD_TYPE_RGBA;// TODO(Peter): include this in win32_opengl_window_info?
+ PixelFormatDesc.cColorBits = Info.ColorBits;
+ PixelFormatDesc.cAlphaBits = Info.AlphaBits;
+ PixelFormatDesc.cDepthBits = Info.DepthBits;
+ PixelFormatDesc.dwLayerMask = PFD_MAIN_PLANE; // TODO(Peter): include this in win32_opengl_window_info?
+ //
- // Create rendering context
- {
- // TODO: Create "proper" context?
- // https://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)#Proper_Context_Creation
-
- Info.RenderContext = wglCreateContext(Window->DeviceContext);
- wglMakeCurrent(Window->DeviceContext, Info.RenderContext);
-
- // TODO(Peter): do we want this?
- /*
- glGetIntegerv(GL_MAJOR_VERSION, );
- glGetIntegerv(GL_MINOR_VERSION, );
- (char*)glGetString(GL_VENDOR);
- (char*)glGetString(GL_RENDERER);
- */
- }
+ s32 PixelFormat = ChoosePixelFormat(Window->DeviceContext, &PixelFormatDesc);
+ if (!PixelFormat) { InvalidCodePath; } // TODO: Log: Choose pixel format failed
+ if (!SetPixelFormat(Window->DeviceContext, PixelFormat, &PixelFormatDesc)) { InvalidCodePath; } // TODO: Log: Set pixel format failed
+ }
+
+ // Create rendering context
+ {
+ // TODO: Create "proper" context?
+ // https://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)#Proper_Context_Creation
- Window->OpenGLInfo = Info;
+ Info.RenderContext = wglCreateContext(Window->DeviceContext);
+ wglMakeCurrent(Window->DeviceContext, Info.RenderContext);
+
+ // TODO(Peter): do we want this?
+ /*
+ glGetIntegerv(GL_MAJOR_VERSION, );
+ glGetIntegerv(GL_MINOR_VERSION, );
+ (char*)glGetString(GL_VENDOR);
+ (char*)glGetString(GL_RENDERER);
+ */
+ }
+
+ Window->OpenGLInfo = Info;
}
struct handle_window_event_result
{
- LRESULT Result;
- b32 Handled;
+ LRESULT Result;
+ b32 Handled;
};
internal void
Win32UpdateInputFrameMouseButtonState (input_frame* InputFrame, key_code KeyCode, int Win32VirtualKey)
{
- InputFrame->KeysDown[KeyCode] = (GetKeyState(Win32VirtualKey) & (1 << 15)) != 0;
+ InputFrame->KeysDown[KeyCode] = (GetKeyState(Win32VirtualKey) & (1 << 15)) != 0;
}
internal void
Win32UpdateInputFrameMouseState (input_frame* InputFrame)
{
- Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseLeftButton, VK_LBUTTON);
- Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseMiddleButton, VK_MBUTTON);
- Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseRightButton, VK_RBUTTON);
- // NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are
- // VK_XBUTTON1 and VK_XBUTTON2
+ Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseLeftButton, VK_LBUTTON);
+ Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseMiddleButton, VK_MBUTTON);
+ Win32UpdateInputFrameMouseButtonState(InputFrame, KeyCode_MouseRightButton, VK_RBUTTON);
+ // NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are
+ // VK_XBUTTON1 and VK_XBUTTON2
}
internal void
Win32UpdateInputFrameMouseWheelDelta (input_frame* InputFrame, MSG Message)
{
- int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam);
- InputFrame->MouseScroll = MouseWheel;
+ int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam);
+ InputFrame->MouseScroll = MouseWheel;
}
internal handle_window_event_result
HandleWindowEventUnlessWouldUseDefault (HWND WindowHandle, UINT Msg, WPARAM wParam, LPARAM lParam)
{
- handle_window_event_result Result = {};
- Result.Handled = false;
-
- switch (Msg)
+ handle_window_event_result Result = {};
+ Result.Handled = false;
+
+ switch (Msg)
+ {
+ case WM_SIZE:
{
- case WM_SIZE:
- {
-
- Result.Handled = true;
- }break;
-
- case WM_CLOSE:
- {
- Result.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam);
- Result.Handled = true;
- }break;
-
- case WM_DESTROY:
- {
- GlobalWin32State.Running = false;
- Result.Handled = true;
- }break;
-
- case WM_PAINT:
- {
- PAINTSTRUCT PaintStruct;
- HDC DeviceContext;
- b32 PaintResult;
-
- DeviceContext = BeginPaint(WindowHandle, &PaintStruct);
- PaintResult = EndPaint(WindowHandle, &PaintStruct);
- Result.Handled = true;
- }break;
- }
+
+ Result.Handled = true;
+ }break;
- return Result;
+ case WM_CLOSE:
+ {
+ Result.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam);
+ Result.Handled = true;
+ }break;
+
+ case WM_DESTROY:
+ {
+ GlobalWin32State.Running = false;
+ Result.Handled = true;
+ }break;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT PaintStruct;
+ HDC DeviceContext;
+ b32 PaintResult;
+
+ DeviceContext = BeginPaint(WindowHandle, &PaintStruct);
+ PaintResult = EndPaint(WindowHandle, &PaintStruct);
+ Result.Handled = true;
+ }break;
+ }
+
+ return Result;
}
LRESULT CALLBACK
@@ -274,124 +269,124 @@ Win32HandleWindowsEvents (
LPARAM lParam
)
{
- handle_window_event_result EventResult = HandleWindowEventUnlessWouldUseDefault(
- WindowHandle,
- Msg,
- wParam,
- lParam);
-
- if (!EventResult.Handled)
- {
- EventResult.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam);
- }
-
- return EventResult.Result;
+ handle_window_event_result EventResult = HandleWindowEventUnlessWouldUseDefault(
+ WindowHandle,
+ Msg,
+ wParam,
+ lParam);
+
+ if (!EventResult.Handled)
+ {
+ EventResult.Result = DefWindowProc(WindowHandle, Msg, wParam, lParam);
+ }
+
+ return EventResult.Result;
}
static key_code
Win32GetKeyCode (int Win32VirtualKey, bool NumpadValid, bool TranslateToChar)
{
- key_code Result = KeyCode_Invalid;
+ key_code Result = KeyCode_Invalid;
+
+ if (Win32VirtualKey == VK_ESCAPE) { Result = KeyCode_Esc; }
+
+ if (!TranslateToChar)
+ {
+ if (Win32VirtualKey == VK_SPACE) { Result = KeyCode_Space; }
- if (Win32VirtualKey == VK_ESCAPE) { Result = KeyCode_Esc; }
+ }
+
+ if (Win32VirtualKey == VK_CAPITAL) { Result = KeyCode_CapsLock; }
+ else if (Win32VirtualKey == VK_TAB) { Result = KeyCode_Tab; }
+ else if (Win32VirtualKey == VK_LSHIFT) { Result = KeyCode_LeftShift; }
+ else if (Win32VirtualKey == VK_RSHIFT) { Result = KeyCode_RightShift; }
+ else if (Win32VirtualKey == VK_LCONTROL) { Result = KeyCode_LeftCtrl; }
+ else if (Win32VirtualKey == VK_RCONTROL) { Result = KeyCode_RightCtrl; }
+
+ // TODO(Peter): support the function key?
+ //else if (Win32VirtualKey == VK_) { Result = KeyCode_Fn; }
+
+ else if (Win32VirtualKey == VK_MENU) { Result = KeyCode_Alt; }
+ else if (Win32VirtualKey == VK_PRIOR) { Result = KeyCode_PageUp; }
+ else if (Win32VirtualKey == VK_NEXT) { Result = KeyCode_PageDown; }
+ else if (Win32VirtualKey == VK_BACK) { Result = KeyCode_Backspace; }
+ else if (Win32VirtualKey == VK_DELETE) { Result = KeyCode_Delete; }
+ else if (Win32VirtualKey == VK_RETURN) { Result = KeyCode_Enter; }
+
+ else if (Win32VirtualKey == VK_F1) { Result = KeyCode_F1; }
+ else if (Win32VirtualKey == VK_F2) { Result = KeyCode_F2; }
+ else if (Win32VirtualKey == VK_F3) { Result = KeyCode_F3; }
+ else if (Win32VirtualKey == VK_F4) { Result = KeyCode_F4; }
+ else if (Win32VirtualKey == VK_F5) { Result = KeyCode_F5; }
+ else if (Win32VirtualKey == VK_F6) { Result = KeyCode_F6; }
+ else if (Win32VirtualKey == VK_F7) { Result = KeyCode_F7; }
+ else if (Win32VirtualKey == VK_F8) { Result = KeyCode_F8; }
+ else if (Win32VirtualKey == VK_F9) { Result = KeyCode_F9; }
+ else if (Win32VirtualKey == VK_F10) { Result = KeyCode_F10; }
+ else if (Win32VirtualKey == VK_F11) { Result = KeyCode_F11; }
+ else if (Win32VirtualKey == VK_F12) { Result = KeyCode_F12; }
+
+ if (!TranslateToChar)
+ {
+ if (Win32VirtualKey == 0x30) { Result = KeyCode_0; }
+ else if (Win32VirtualKey == 0x31) { Result = KeyCode_1; }
+ else if (Win32VirtualKey == 0x32) { Result = KeyCode_2; }
+ else if (Win32VirtualKey == 0x33) { Result = KeyCode_3; }
+ else if (Win32VirtualKey == 0x34) { Result = KeyCode_4; }
+ else if (Win32VirtualKey == 0x35) { Result = KeyCode_5; }
+ else if (Win32VirtualKey == 0x36) { Result = KeyCode_6; }
+ else if (Win32VirtualKey == 0x37) { Result = KeyCode_7; }
+ else if (Win32VirtualKey == 0x38) { Result = KeyCode_8; }
+ else if (Win32VirtualKey == 0x39) { Result = KeyCode_9; }
- if (!TranslateToChar)
- {
- if (Win32VirtualKey == VK_SPACE) { Result = KeyCode_Space; }
-
- }
-
- if (Win32VirtualKey == VK_CAPITAL) { Result = KeyCode_CapsLock; }
- else if (Win32VirtualKey == VK_TAB) { Result = KeyCode_Tab; }
- else if (Win32VirtualKey == VK_LSHIFT) { Result = KeyCode_LeftShift; }
- else if (Win32VirtualKey == VK_RSHIFT) { Result = KeyCode_RightShift; }
- else if (Win32VirtualKey == VK_LCONTROL) { Result = KeyCode_LeftCtrl; }
- else if (Win32VirtualKey == VK_RCONTROL) { Result = KeyCode_RightCtrl; }
-
- // TODO(Peter): support the function key?
- //else if (Win32VirtualKey == VK_) { Result = KeyCode_Fn; }
-
- else if (Win32VirtualKey == VK_MENU) { Result = KeyCode_Alt; }
- else if (Win32VirtualKey == VK_PRIOR) { Result = KeyCode_PageUp; }
- else if (Win32VirtualKey == VK_NEXT) { Result = KeyCode_PageDown; }
- else if (Win32VirtualKey == VK_BACK) { Result = KeyCode_Backspace; }
- else if (Win32VirtualKey == VK_DELETE) { Result = KeyCode_Delete; }
- else if (Win32VirtualKey == VK_RETURN) { Result = KeyCode_Enter; }
-
- else if (Win32VirtualKey == VK_F1) { Result = KeyCode_F1; }
- else if (Win32VirtualKey == VK_F2) { Result = KeyCode_F2; }
- else if (Win32VirtualKey == VK_F3) { Result = KeyCode_F3; }
- else if (Win32VirtualKey == VK_F4) { Result = KeyCode_F4; }
- else if (Win32VirtualKey == VK_F5) { Result = KeyCode_F5; }
- else if (Win32VirtualKey == VK_F6) { Result = KeyCode_F6; }
- else if (Win32VirtualKey == VK_F7) { Result = KeyCode_F7; }
- else if (Win32VirtualKey == VK_F8) { Result = KeyCode_F8; }
- else if (Win32VirtualKey == VK_F9) { Result = KeyCode_F9; }
- else if (Win32VirtualKey == VK_F10) { Result = KeyCode_F10; }
- else if (Win32VirtualKey == VK_F11) { Result = KeyCode_F11; }
- else if (Win32VirtualKey == VK_F12) { Result = KeyCode_F12; }
-
- if (!TranslateToChar)
- {
- if (Win32VirtualKey == 0x30) { Result = KeyCode_0; }
- else if (Win32VirtualKey == 0x31) { Result = KeyCode_1; }
- else if (Win32VirtualKey == 0x32) { Result = KeyCode_2; }
- else if (Win32VirtualKey == 0x33) { Result = KeyCode_3; }
- else if (Win32VirtualKey == 0x34) { Result = KeyCode_4; }
- else if (Win32VirtualKey == 0x35) { Result = KeyCode_5; }
- else if (Win32VirtualKey == 0x36) { Result = KeyCode_6; }
- else if (Win32VirtualKey == 0x37) { Result = KeyCode_7; }
- else if (Win32VirtualKey == 0x38) { Result = KeyCode_8; }
- else if (Win32VirtualKey == 0x39) { Result = KeyCode_9; }
-
- else if (Win32VirtualKey == 0x41) { Result = KeyCode_A; }
- else if (Win32VirtualKey == 0x42) { Result = KeyCode_B; }
- else if (Win32VirtualKey == 0x43) { Result = KeyCode_C; }
- else if (Win32VirtualKey == 0x44) { Result = KeyCode_D; }
- else if (Win32VirtualKey == 0x45) { Result = KeyCode_E; }
- else if (Win32VirtualKey == 0x46) { Result = KeyCode_F; }
- else if (Win32VirtualKey == 0x47) { Result = KeyCode_G; }
- else if (Win32VirtualKey == 0x48) { Result = KeyCode_H; }
- else if (Win32VirtualKey == 0x49) { Result = KeyCode_I; }
- else if (Win32VirtualKey == 0x4A) { Result = KeyCode_J; }
- else if (Win32VirtualKey == 0x4B) { Result = KeyCode_K; }
- else if (Win32VirtualKey == 0x4C) { Result = KeyCode_L; }
- else if (Win32VirtualKey == 0x4D) { Result = KeyCode_M; }
- else if (Win32VirtualKey == 0x4E) { Result = KeyCode_N; }
- else if (Win32VirtualKey == 0x4F) { Result = KeyCode_O; }
- else if (Win32VirtualKey == 0x50) { Result = KeyCode_P; }
- else if (Win32VirtualKey == 0x51) { Result = KeyCode_Q; }
- else if (Win32VirtualKey == 0x52) { Result = KeyCode_R; }
- else if (Win32VirtualKey == 0x53) { Result = KeyCode_S; }
- else if (Win32VirtualKey == 0x54) { Result = KeyCode_T; }
- else if (Win32VirtualKey == 0x55) { Result = KeyCode_U; }
- else if (Win32VirtualKey == 0x56) { Result = KeyCode_V; }
- else if (Win32VirtualKey == 0x57) { Result = KeyCode_W; }
- else if (Win32VirtualKey == 0x58) { Result = KeyCode_X; }
- else if (Win32VirtualKey == 0x59) { Result = KeyCode_Y; }
- else if (Win32VirtualKey == 0x5A) { Result = KeyCode_Z; }
- }
-
- if (NumpadValid)
- {
- if (Win32VirtualKey == VK_NUMPAD0) { Result = KeyCode_Num0; }
- else if (Win32VirtualKey == VK_NUMPAD1) { Result = KeyCode_Num1; }
- else if (Win32VirtualKey == VK_NUMPAD2) { Result = KeyCode_Num2; }
- else if (Win32VirtualKey == VK_NUMPAD3) { Result = KeyCode_Num3; }
- else if (Win32VirtualKey == VK_NUMPAD4) { Result = KeyCode_Num4; }
- else if (Win32VirtualKey == VK_NUMPAD5) { Result = KeyCode_Num5; }
- else if (Win32VirtualKey == VK_NUMPAD6) { Result = KeyCode_Num6; }
- else if (Win32VirtualKey == VK_NUMPAD7) { Result = KeyCode_Num7; }
- else if (Win32VirtualKey == VK_NUMPAD8) { Result = KeyCode_Num8; }
- else if (Win32VirtualKey == VK_NUMPAD9) { Result = KeyCode_Num9; }
- }
-
- if (Win32VirtualKey == VK_UP) { Result = KeyCode_UpArrow; }
- else if (Win32VirtualKey == VK_DOWN) { Result = KeyCode_DownArrow; }
- else if (Win32VirtualKey == VK_LEFT) { Result = KeyCode_LeftArrow; }
- else if (Win32VirtualKey == VK_RIGHT) { Result = KeyCode_RightArrow; }
-
- return Result;
+ else if (Win32VirtualKey == 0x41) { Result = KeyCode_A; }
+ else if (Win32VirtualKey == 0x42) { Result = KeyCode_B; }
+ else if (Win32VirtualKey == 0x43) { Result = KeyCode_C; }
+ else if (Win32VirtualKey == 0x44) { Result = KeyCode_D; }
+ else if (Win32VirtualKey == 0x45) { Result = KeyCode_E; }
+ else if (Win32VirtualKey == 0x46) { Result = KeyCode_F; }
+ else if (Win32VirtualKey == 0x47) { Result = KeyCode_G; }
+ else if (Win32VirtualKey == 0x48) { Result = KeyCode_H; }
+ else if (Win32VirtualKey == 0x49) { Result = KeyCode_I; }
+ else if (Win32VirtualKey == 0x4A) { Result = KeyCode_J; }
+ else if (Win32VirtualKey == 0x4B) { Result = KeyCode_K; }
+ else if (Win32VirtualKey == 0x4C) { Result = KeyCode_L; }
+ else if (Win32VirtualKey == 0x4D) { Result = KeyCode_M; }
+ else if (Win32VirtualKey == 0x4E) { Result = KeyCode_N; }
+ else if (Win32VirtualKey == 0x4F) { Result = KeyCode_O; }
+ else if (Win32VirtualKey == 0x50) { Result = KeyCode_P; }
+ else if (Win32VirtualKey == 0x51) { Result = KeyCode_Q; }
+ else if (Win32VirtualKey == 0x52) { Result = KeyCode_R; }
+ else if (Win32VirtualKey == 0x53) { Result = KeyCode_S; }
+ else if (Win32VirtualKey == 0x54) { Result = KeyCode_T; }
+ else if (Win32VirtualKey == 0x55) { Result = KeyCode_U; }
+ else if (Win32VirtualKey == 0x56) { Result = KeyCode_V; }
+ else if (Win32VirtualKey == 0x57) { Result = KeyCode_W; }
+ else if (Win32VirtualKey == 0x58) { Result = KeyCode_X; }
+ else if (Win32VirtualKey == 0x59) { Result = KeyCode_Y; }
+ else if (Win32VirtualKey == 0x5A) { Result = KeyCode_Z; }
+ }
+
+ if (NumpadValid)
+ {
+ if (Win32VirtualKey == VK_NUMPAD0) { Result = KeyCode_Num0; }
+ else if (Win32VirtualKey == VK_NUMPAD1) { Result = KeyCode_Num1; }
+ else if (Win32VirtualKey == VK_NUMPAD2) { Result = KeyCode_Num2; }
+ else if (Win32VirtualKey == VK_NUMPAD3) { Result = KeyCode_Num3; }
+ else if (Win32VirtualKey == VK_NUMPAD4) { Result = KeyCode_Num4; }
+ else if (Win32VirtualKey == VK_NUMPAD5) { Result = KeyCode_Num5; }
+ else if (Win32VirtualKey == VK_NUMPAD6) { Result = KeyCode_Num6; }
+ else if (Win32VirtualKey == VK_NUMPAD7) { Result = KeyCode_Num7; }
+ else if (Win32VirtualKey == VK_NUMPAD8) { Result = KeyCode_Num8; }
+ else if (Win32VirtualKey == VK_NUMPAD9) { Result = KeyCode_Num9; }
+ }
+
+ if (Win32VirtualKey == VK_UP) { Result = KeyCode_UpArrow; }
+ else if (Win32VirtualKey == VK_DOWN) { Result = KeyCode_DownArrow; }
+ else if (Win32VirtualKey == VK_LEFT) { Result = KeyCode_LeftArrow; }
+ else if (Win32VirtualKey == VK_RIGHT) { Result = KeyCode_RightArrow; }
+
+ return Result;
}
internal handle_window_msg_result
@@ -399,173 +394,173 @@ HandleWindowsMessage (
HWND WindowHandle,
MSG Message)
{
- handle_window_msg_result Result = {};
- Result.NeedsUpdate = 0;
-
- switch (Message.message)
+ handle_window_msg_result Result = {};
+ Result.NeedsUpdate = 0;
+
+ switch (Message.message)
+ {
+ case WM_HOTKEY:
{
- case WM_HOTKEY:
- {
- }break;
-
- case WM_MOUSEWHEEL:
- {
- int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam);
- /*
- Input.New->MouseScroll = MouseWheel;
- Result.NeedsUpdate = true;
- */
- }break;
-
- case WM_LBUTTONDOWN:
- case WM_LBUTTONUP:
- case WM_MBUTTONDOWN:
- case WM_MBUTTONUP:
- case WM_RBUTTONDOWN:
- case WM_RBUTTONUP:
- {
- /*
- Input.New->KeyStates[KeyCode_MouseLeftButton] = (GetKeyState(VK_LBUTTON) & (1 << 15)) != 0;
- Input.New->KeyStates[KeyCode_MouseMiddleButton] = (GetKeyState(VK_MBUTTON) & (1 << 15)) != 0;
- Input.New->KeyStates[KeyCode_MouseRightButton] = (GetKeyState(VK_RBUTTON) & (1 << 15)) != 0;
- // NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are
- // VK_XBUTTON1 and VK_XBUTTON2
-
- if (KeyTransitionedDown(KeyCode_MouseLeftButton, Input))
- {
- Input.MouseDownX = Input.New->MouseX;
- Input.MouseDownY = Input.New->MouseY;
- }
- Result.NeedsUpdate = true;*/
- }break;
-
- case WM_MOUSEMOVE:
- {
- POINT MousePos;
- GetCursorPos(&MousePos);
- ScreenToClient(WindowHandle, &MousePos);
-
- /*
- Input.New->MouseX = MousePos.x;
- Input.New->MouseY = App.WindowHeight - MousePos.y;
-
- Result.NeedsUpdate = true;
- */
- }break;
-
- case WM_SYSKEYDOWN:
- case WM_SYSKEYUP:
- case WM_KEYDOWN:
- case WM_KEYUP:
- {
- int VirtualKey = (int)Message.wParam;
- bool KeyDown = (Message.lParam & (1 << 31)) == 0;
- int KeyIndex = Win32GetKeyCode(VirtualKey, true, true);
- /*
- if (KeyIndex >= 0)
- {
- DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent ");
- Input.New->KeyStates[KeyIndex] = KeyDown;
- Result.NeedsUpdate = true;
- }
- else
- {
- if (Input.TranslateInputToCharValues && KeyDown)
- {
- // NOTE(Peter): Took this out b/c we're translating the WM_CHAR messages
- // in the message pump, and if we do it here as well, character producing
- // key messages get put on the message queue twice
- TranslateMessage(&Message);
- DispatchMessage(&Message);
- }
- else
- {
- DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent ");
- // NOTE(Peter): This is so that when you lift up a key that was generating a WM_CHAR,
- // the app still has a chance to respond to it.
- Result.NeedsUpdate = true;
- }
- }
- */
- }break;
-
- case WM_CHAR:
- {
- /*
- char TranslatedChar = (char)Message.wParam;
- int KeyIndex = GetKeyIndexFromChar(TranslatedChar);
-
- if (KeyIndex >= 0)
- {
- // NOTE(Peter): Always setting this to true becuase windows is stupid and doesn't
- // pass the press/release bit through correctly. So now the KEYDOWN/KEYUP Messages above
- // only translate the message to a WM_CHAR message if its a key down. Since we clear all
- // keystates to false at the beginning of an input frame, this will make transitions
- // get registered correctly.
- Input.New->KeyStates[KeyIndex] = true;
- Result.NeedsUpdate = true;
- }
- else
- {
- printf("Translated Char Not Recognized: %c\n", TranslatedChar);
- }
- */
- }break;
-
- default:
- {
- TranslateMessage(&Message);
- DispatchMessage(&Message);
- }break;
- }
+ }break;
- return Result;
+ case WM_MOUSEWHEEL:
+ {
+ int MouseWheel = GET_WHEEL_DELTA_WPARAM(Message.wParam);
+ /*
+ Input.New->MouseScroll = MouseWheel;
+ Result.NeedsUpdate = true;
+ */
+ }break;
+
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ {
+ /*
+ Input.New->KeyStates[KeyCode_MouseLeftButton] = (GetKeyState(VK_LBUTTON) & (1 << 15)) != 0;
+ Input.New->KeyStates[KeyCode_MouseMiddleButton] = (GetKeyState(VK_MBUTTON) & (1 << 15)) != 0;
+ Input.New->KeyStates[KeyCode_MouseRightButton] = (GetKeyState(VK_RBUTTON) & (1 << 15)) != 0;
+ // NOTE(Peter): If you decide to support extra mouse buttons, on windows the key codes are
+ // VK_XBUTTON1 and VK_XBUTTON2
+
+ if (KeyTransitionedDown(KeyCode_MouseLeftButton, Input))
+ {
+ Input.MouseDownX = Input.New->MouseX;
+ Input.MouseDownY = Input.New->MouseY;
+ }
+ Result.NeedsUpdate = true;*/
+ }break;
+
+ case WM_MOUSEMOVE:
+ {
+ POINT MousePos;
+ GetCursorPos(&MousePos);
+ ScreenToClient(WindowHandle, &MousePos);
+
+ /*
+ Input.New->MouseX = MousePos.x;
+ Input.New->MouseY = App.WindowHeight - MousePos.y;
+
+ Result.NeedsUpdate = true;
+ */
+ }break;
+
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ {
+ int VirtualKey = (int)Message.wParam;
+ bool KeyDown = (Message.lParam & (1 << 31)) == 0;
+ int KeyIndex = Win32GetKeyCode(VirtualKey, true, true);
+ /*
+ if (KeyIndex >= 0)
+ {
+ DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent ");
+ Input.New->KeyStates[KeyIndex] = KeyDown;
+ Result.NeedsUpdate = true;
+ }
+ else
+ {
+ if (Input.TranslateInputToCharValues && KeyDown)
+ {
+ // NOTE(Peter): Took this out b/c we're translating the WM_CHAR messages
+ // in the message pump, and if we do it here as well, character producing
+ // key messages get put on the message queue twice
+ TranslateMessage(&Message);
+ DispatchMessage(&Message);
+ }
+ else
+ {
+ DEBUG_GET_MESSAGE_NAME(Result.MessageType, "WM_KEYEvent ");
+ // NOTE(Peter): This is so that when you lift up a key that was generating a WM_CHAR,
+ // the app still has a chance to respond to it.
+ Result.NeedsUpdate = true;
+ }
+ }
+ */
+ }break;
+
+ case WM_CHAR:
+ {
+ /*
+ char TranslatedChar = (char)Message.wParam;
+ int KeyIndex = GetKeyIndexFromChar(TranslatedChar);
+
+ if (KeyIndex >= 0)
+ {
+ // NOTE(Peter): Always setting this to true becuase windows is stupid and doesn't
+ // pass the press/release bit through correctly. So now the KEYDOWN/KEYUP Messages above
+ // only translate the message to a WM_CHAR message if its a key down. Since we clear all
+ // keystates to false at the beginning of an input frame, this will make transitions
+ // get registered correctly.
+ Input.New->KeyStates[KeyIndex] = true;
+ Result.NeedsUpdate = true;
+ }
+ else
+ {
+ printf("Translated Char Not Recognized: %c\n", TranslatedChar);
+ }
+ */
+ }break;
+
+ default:
+ {
+ TranslateMessage(&Message);
+ DispatchMessage(&Message);
+ }break;
+ }
+
+ return Result;
}
internal void
Win32UpdateWindowDimension(window* Window)
{
- RECT ClientRect;
- GetClientRect(Window->Handle, &ClientRect);
- Window->Width = ClientRect.right - ClientRect.left;
- Window->Height = ClientRect.bottom - ClientRect.top;
+ RECT ClientRect;
+ GetClientRect(Window->Handle, &ClientRect);
+ Window->Width = ClientRect.right - ClientRect.left;
+ Window->Height = ClientRect.bottom - ClientRect.top;
}
internal void
Win32ResizeDIBSection(win32_offscreen_buffer *Win32Buffer, int Width, int Height)
{
- if(Win32Buffer->Buffer.Memory)
- {
- VirtualFree(Win32Buffer->Buffer.Memory, 0, MEM_RELEASE);
- }
-
- Win32Buffer->Buffer.Width = Width;
- Win32Buffer->Buffer.Height = Height;
-
- int BytesPerPixel = 4;
- Win32Buffer->Buffer.BytesPerPixel = BytesPerPixel;
-
- Win32Buffer->Info.bmiHeader.biSize = sizeof(Win32Buffer->Info.bmiHeader);
- Win32Buffer->Info.bmiHeader.biWidth = Win32Buffer->Buffer.Width;
- Win32Buffer->Info.bmiHeader.biHeight = -Win32Buffer->Buffer.Height; // Top down, not bottom up
- Win32Buffer->Info.bmiHeader.biPlanes = 1;
- Win32Buffer->Info.bmiHeader.biBitCount = 32;
- Win32Buffer->Info.bmiHeader.biCompression = BI_RGB;
-
- int BitmapMemorySize = (Win32Buffer->Buffer.Width * Win32Buffer->Buffer.Height)*BytesPerPixel;
- Win32Buffer->Buffer.Memory = (u8*)VirtualAlloc(0, BitmapMemorySize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
- Win32Buffer->Buffer.Pitch = Width*BytesPerPixel;
+ if(Win32Buffer->Buffer.Memory)
+ {
+ VirtualFree(Win32Buffer->Buffer.Memory, 0, MEM_RELEASE);
+ }
+
+ Win32Buffer->Buffer.Width = Width;
+ Win32Buffer->Buffer.Height = Height;
+
+ int BytesPerPixel = 4;
+ Win32Buffer->Buffer.BytesPerPixel = BytesPerPixel;
+
+ Win32Buffer->Info.bmiHeader.biSize = sizeof(Win32Buffer->Info.bmiHeader);
+ Win32Buffer->Info.bmiHeader.biWidth = Win32Buffer->Buffer.Width;
+ Win32Buffer->Info.bmiHeader.biHeight = -Win32Buffer->Buffer.Height; // Top down, not bottom up
+ Win32Buffer->Info.bmiHeader.biPlanes = 1;
+ Win32Buffer->Info.bmiHeader.biBitCount = 32;
+ Win32Buffer->Info.bmiHeader.biCompression = BI_RGB;
+
+ int BitmapMemorySize = (Win32Buffer->Buffer.Width * Win32Buffer->Buffer.Height)*BytesPerPixel;
+ Win32Buffer->Buffer.Memory = (u8*)VirtualAlloc(0, BitmapMemorySize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
+ Win32Buffer->Buffer.Pitch = Width*BytesPerPixel;
}
internal void
Win32DisplayBufferInWindow(win32_offscreen_buffer* Win32Buffer, window Window)
{
- StretchDIBits(Window.DeviceContext,
- 0, 0, Win32Buffer->Buffer.Width, Win32Buffer->Buffer.Height,
- 0, 0, Win32Buffer->Buffer.Width, Win32Buffer->Buffer.Height,
- Win32Buffer->Buffer.Memory,
- &Win32Buffer->Info,
- DIB_RGB_COLORS, SRCCOPY);
+ StretchDIBits(Window.DeviceContext,
+ 0, 0, Win32Buffer->Buffer.Width, Win32Buffer->Buffer.Height,
+ 0, 0, Win32Buffer->Buffer.Width, Win32Buffer->Buffer.Height,
+ Win32Buffer->Buffer.Memory,
+ &Win32Buffer->Info,
+ DIB_RGB_COLORS, SRCCOPY);
}
/////////////////////////////////////////
@@ -581,20 +576,20 @@ OpenGLRenderTriBuffer (u8* Vertecies, s32 VertexElements,
u8* Colors, s32 ColorsElements,
s32 TriCount)
{
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(VertexElements, GL_FLOAT, VertexElements * sizeof(r32), Vertecies);
-
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glTexCoordPointer(UVElements, GL_FLOAT, UVElements * sizeof(r32), UVs);
-
- glEnableClientState(GL_COLOR_ARRAY);
- glColorPointer(ColorsElements, GL_FLOAT, ColorsElements * sizeof(r32), Colors);
-
- glDrawArrays(GL_TRIANGLES, 0, TriCount);
-
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glDisableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(VertexElements, GL_FLOAT, VertexElements * sizeof(r32), Vertecies);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(UVElements, GL_FLOAT, UVElements * sizeof(r32), UVs);
+
+ glEnableClientState(GL_COLOR_ARRAY);
+ glColorPointer(ColorsElements, GL_FLOAT, ColorsElements * sizeof(r32), Colors);
+
+ glDrawArrays(GL_TRIANGLES, 0, TriCount);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
}
internal void
@@ -602,21 +597,21 @@ OpenGLDraw3DTri (v4 P0, v4 P1, v4 P2,
v2 UV0, v2 UV1, v2 UV2,
v4 C0, v4 C1, v4 C2)
{
- glBegin(GL_TRIANGLES);
-
- glTexCoord2f(UV0.x, UV0.y);
- glColor4f(C0.r, C0.g, C0.b, C0.a);
- glVertex4f(P0.x, P0.y, P0.z, P0.w);
-
- glTexCoord2f(UV1.x, UV1.y);
- glColor4f(C1.r, C1.g, C1.b, C1.a);
- glVertex4f(P1.x, P1.y, P1.z, P1.w);
-
- glTexCoord2f(UV2.x, UV2.y);
- glColor4f(C2.r, C2.g, C2.b, C2.a);
- glVertex4f(P2.x, P2.y, P2.z, P2.w);
-
- glEnd();
+ glBegin(GL_TRIANGLES);
+
+ glTexCoord2f(UV0.x, UV0.y);
+ glColor4f(C0.r, C0.g, C0.b, C0.a);
+ glVertex4f(P0.x, P0.y, P0.z, P0.w);
+
+ glTexCoord2f(UV1.x, UV1.y);
+ glColor4f(C1.r, C1.g, C1.b, C1.a);
+ glVertex4f(P1.x, P1.y, P1.z, P1.w);
+
+ glTexCoord2f(UV2.x, UV2.y);
+ glColor4f(C2.r, C2.g, C2.b, C2.a);
+ glVertex4f(P2.x, P2.y, P2.z, P2.w);
+
+ glEnd();
}
internal void
@@ -624,72 +619,72 @@ OpenGLDraw2DTri (v2 P0, v2 P1, v2 P2,
v2 UV0, v2 UV1, v2 UV2,
v4 C0, v4 C1, v4 C2)
{
- glBegin(GL_TRIANGLES);
-
- glTexCoord2f(UV0.x, UV0.y);
- glColor4f(C0.r, C0.g, C0.b, C0.a);
- glVertex2f(P0.x, P0.y);
-
- glTexCoord2f(UV1.x, UV1.y);
- glColor4f(C1.r, C1.g, C1.b, C1.a);
- glVertex2f(P1.x, P1.y);
-
- glTexCoord2f(UV2.x, UV2.y);
- glColor4f(C2.r, C2.g, C2.b, C2.a);
- glVertex2f(P2.x, P2.y);
-
- glEnd();
+ glBegin(GL_TRIANGLES);
+
+ glTexCoord2f(UV0.x, UV0.y);
+ glColor4f(C0.r, C0.g, C0.b, C0.a);
+ glVertex2f(P0.x, P0.y);
+
+ glTexCoord2f(UV1.x, UV1.y);
+ glColor4f(C1.r, C1.g, C1.b, C1.a);
+ glVertex2f(P1.x, P1.y);
+
+ glTexCoord2f(UV2.x, UV2.y);
+ glColor4f(C2.r, C2.g, C2.b, C2.a);
+ glVertex2f(P2.x, P2.y);
+
+ glEnd();
}
internal void
LoadModelView (r32 Matrix[16])
{
- glMatrixMode(GL_MODELVIEW);
- glLoadMatrixf(Matrix);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf(Matrix);
}
internal void
LoadProjection (r32 Matrix[16])
{
- glMatrixMode(GL_PROJECTION);
- glLoadMatrixf(Matrix);
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf(Matrix);
}
internal void
ClearRenderBuffer ()
{
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
s32 NextTextureHandle = 1;
internal s32
SubmitTexture (u8* Memory, s32 Width, s32 Height)
{
- s32 TextureHandle = NextTextureHandle++;
- glBindTexture(GL_TEXTURE_2D, TextureHandle);
- glTexImage2D(GL_TEXTURE_2D,
- 0, // mip map level
- GL_RGBA8,
- Width,
- Height,
- 0, // border
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- Memory);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-
- return TextureHandle;
+ s32 TextureHandle = NextTextureHandle++;
+ glBindTexture(GL_TEXTURE_2D, TextureHandle);
+ glTexImage2D(GL_TEXTURE_2D,
+ 0, // mip map level
+ GL_RGBA8,
+ Width,
+ Height,
+ 0, // border
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ Memory);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ return TextureHandle;
}
internal void
BindTexture (s32 TextureHandle)
{
- glBindTexture(GL_TEXTURE_2D, TextureHandle);
+ glBindTexture(GL_TEXTURE_2D, TextureHandle);
}
#define GS_WIN32_CPP
diff --git a/src/sculpture_gen/gen_blumen_lumen.cpp b/src/sculpture_gen/gen_blumen_lumen.cpp
index dce5c4a..9edceb6 100644
--- a/src/sculpture_gen/gen_blumen_lumen.cpp
+++ b/src/sculpture_gen/gen_blumen_lumen.cpp
@@ -10,6 +10,9 @@
#include "../gs_libs/gs_types.h"
#include "../gs_libs/gs_types.cpp"
+#include "../app/engine/foldhaus_log.h"
+global log_buffer* GlobalLogBuffer;
+
#include "../app/platform_win32/win32_foldhaus_utils.h"
#include "../app/platform_win32/win32_foldhaus_memory.h"
#include "../app/platform_win32/win32_foldhaus_fileio.h"
@@ -26,6 +29,11 @@ typedef struct
u32 SubsegmentsCount;
u32 SubsegmentLeds;
+ // SACN
+ u32 SACNUniverseStart;
+ u32 SACNChannelStart;
+
+ // UART
// Only one of these two values is needed.
// If ChannelsArray != 0, then it will be used, and assumed to
// have SegmentsCount values
@@ -58,7 +66,8 @@ BuildLoop(gs_string* OutputBuffer, loop_desc Desc)
Channel = Desc.ChannelStart + i;
}
- WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort);
+ WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort,
+ Desc.SACNUniverseStart, Desc.SACNChannelStart);
WriteSegmentSequenceOpen(OutputBuffer, Desc.SubsegmentsCount);
for (u32 j = 0; j < Desc.SubsegmentsCount; j++)
@@ -94,6 +103,13 @@ typedef struct
v3 Pos;
char* ComPort;
char* FlowerTagValue;
+
+ // SACN
+ u32 SACNStemInnerStartUniverse;
+ u32 SACNStemOuterStartUniverse;
+ u32 SACNFlowerStemStartUniverse;
+
+ // UART
u32* StemChannels;
u32* BloomOuterChannels;
u32* BloomInnerChannels;
@@ -106,12 +122,15 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc)
#if 1
// the bloom stem inner
loop_desc BloomStemInner = {};
- BloomStemInner.CenterStart = v3{0, 1.4f, 0} + Desc.Pos;
- BloomStemInner.CenterEnd = v3{0, .9f, 0} + Desc.Pos;
+ BloomStemInner.CenterStart = v3{0, 1.4f, 0};
+ BloomStemInner.CenterEnd = v3{0, .9f, 0};
BloomStemInner.Radius = .05f;
- BloomStemInner.SegmentsCount = 6;
+ //BloomStemInner.SegmentsCount = 6;
+ BloomStemInner.SegmentsCount = 1;
BloomStemInner.SubsegmentsCount = 3;
BloomStemInner.SubsegmentLeds = 35;
+ BloomStemInner.SACNUniverseStart = Desc.SACNStemInnerStartUniverse;
+ BloomStemInner.SACNChannelStart = 1;
BloomStemInner.ChannelsArray = Desc.BloomInnerChannels;
BloomStemInner.ComPort = Desc.ComPort;
BloomStemInner.SectionTagValue = "inner_bloom";
@@ -120,12 +139,15 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc)
// the bloom stem outer
loop_desc BloomStemOuter = {};
- BloomStemOuter.CenterStart = v3{0, .5f, 0} + Desc.Pos;
- BloomStemOuter.CenterEnd = v3{0, .9f, 0} + Desc.Pos;
+ BloomStemOuter.CenterStart = v3{0, .5f, 0};
+ BloomStemOuter.CenterEnd = v3{0, .9f, 0};
BloomStemOuter.Radius = .07f;
- BloomStemOuter.SegmentsCount = 9;
+ //BloomStemOuter.SegmentsCount = 9;
+ BloomStemOuter.SegmentsCount = 1;
BloomStemOuter.SubsegmentsCount = 3;
BloomStemOuter.SubsegmentLeds = 41;
+ BloomStemOuter.SACNUniverseStart = Desc.SACNStemOuterStartUniverse;
+ BloomStemOuter.SACNChannelStart = 1;
BloomStemOuter.ChannelsArray = Desc.BloomOuterChannels;
BloomStemOuter.ComPort = Desc.ComPort;
BloomStemOuter.SectionTagValue = "outer_bloom";
@@ -136,12 +158,15 @@ BuildFlower(gs_string* OutputBuffer, flower_desc Desc)
#if 1
// the flower stem
loop_desc FlowerStem = {};
- FlowerStem.CenterStart = v3{0, -1.5f, 0} + Desc.Pos;
- FlowerStem.CenterEnd = v3{0, .5f, 0} + Desc.Pos;
+ FlowerStem.CenterStart = v3{0, .5f, 0};
+ FlowerStem.CenterEnd = v3{0, -1.5f, 0};
FlowerStem.Radius = .05f;
- FlowerStem.SegmentsCount = 6;
+ //FlowerStem.SegmentsCount = 6;
+ FlowerStem.SegmentsCount = 1;
FlowerStem.SubsegmentsCount = 1;
FlowerStem.SubsegmentLeds = 300;
+ FlowerStem.SACNUniverseStart = Desc.SACNFlowerStemStartUniverse;
+ FlowerStem.SACNChannelStart = 1;
FlowerStem.ChannelsArray = Desc.StemChannels;
FlowerStem.ComPort = Desc.ComPort;
FlowerStem.SectionTagValue = "stem";
@@ -174,51 +199,102 @@ FlowerStripToChannel(u8 Flower, u8 Channel)
int main(int ArgCount, char** Args)
{
gs_thread_context Ctx = Win32CreateThreadContext();
+ GlobalLogBuffer = PushStruct(Ctx.Transient, log_buffer);
+ *GlobalLogBuffer = Log_Init(Ctx.Transient, 32);
- gs_string OutputBuffer = PushString(Ctx.Transient, MB(4));
+ gs_string OutputBuffer0 = PushString(Ctx.Transient, MB(4));
+ gs_string OutputBuffer1 = PushString(Ctx.Transient, MB(4));
+ gs_string OutputBuffer2 = PushString(Ctx.Transient, MB(4));
- WriteAssemblyUARTOpen(&OutputBuffer,
- "Blumen Lumen - Silver Spring",
+ u32 StripCount = 3; // used to be 21
+#if 0
+ WriteAssemblyUARTOpen(&OutputBuffer0,
+ "Blumen Lumen - Silver Spring - 00",
+ 100,
+ v3{-1, 0, 0},
+ StripCount,
+ "");
+ WriteAssemblyUARTOpen(&OutputBuffer1,
+ "Blumen Lumen - Silver Spring - 01",
100,
v3{0, 0, 0},
- 63,
+ StripCount,
"");
+ WriteAssemblyUARTOpen(&OutputBuffer2,
+ "Blumen Lumen - Silver Spring - 02",
+ 100,
+ v3{1, 0, 0},
+ StripCount,
+ "");
+#else
+ WriteAssemblySACNOpen(&OutputBuffer0,
+ "Blumen Lumen - Silver Spring - 00",
+ 100,
+ v3{-1, 0, 0},
+ StripCount);
+ WriteAssemblySACNOpen(&OutputBuffer1,
+ "Blumen Lumen - Silver Spring - 01",
+ 100,
+ v3{0, 0, 0},
+ StripCount);
+ WriteAssemblySACNOpen(&OutputBuffer2,
+ "Blumen Lumen - Silver Spring - 02",
+ 100,
+ v3{1, 0, 0},
+ StripCount);
+#endif
- u32 StripCount = 0;
+ u32 StripCountOut = 0;
u32 StemChannels[] = { FSC(2, 1), FSC(2, 2), FSC(2, 3), FSC(2, 4), FSC(2, 5), FSC(2, 6) };
u32 BloomOuterChannels[] = { FSC(1, 0), FSC(1, 1), FSC(1, 2), FSC(1, 3), FSC(1, 4), FSC(1, 5), FSC(1, 6), FSC(1, 7), FSC(2, 0) };
u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) };
flower_desc F0 = {};
- F0.Pos = v3{-1, 0, 0};
- F0.ComPort = "\\\\.\\COM4";
+ F0.Pos = v3{0, 0, 0};
+ F0.ComPort = "\\\\.\\COM11";
F0.FlowerTagValue = "left";
+ F0.SACNStemInnerStartUniverse = 4;
+ F0.SACNStemOuterStartUniverse = 3;
+ F0.SACNFlowerStemStartUniverse = 1;
F0.StemChannels = StemChannels;
F0.BloomOuterChannels = BloomOuterChannels;
F0.BloomInnerChannels = BloomInnerChannels;
- StripCount += BuildFlower(&OutputBuffer, F0);
+ StripCountOut += BuildFlower(&OutputBuffer0, F0);
flower_desc F1 = {};
F1.Pos = v3{0, 0, 0};
- F1.ComPort = "\\\\.\\COM5";
+ F1.ComPort = "\\\\.\\COM12";
F1.FlowerTagValue = "center";
+ F1.SACNStemInnerStartUniverse = 9;
+ F1.SACNStemOuterStartUniverse = 8;
+ F1.SACNFlowerStemStartUniverse = 6;
F1.StemChannels = StemChannels;
F1.BloomInnerChannels = BloomInnerChannels;
F1.BloomOuterChannels = BloomOuterChannels;
- StripCount += BuildFlower(&OutputBuffer, F1);
+ StripCountOut += BuildFlower(&OutputBuffer1, F1);
flower_desc F2 = {};
- F2.Pos = v3{1, 0, 0};
+ F2.Pos = v3{0, 0, 0};
F2.ComPort = "\\\\.\\COM6";
F2.FlowerTagValue = "right";
+ F2.SACNStemInnerStartUniverse = 14;
+ F2.SACNStemOuterStartUniverse = 13;
+ F2.SACNFlowerStemStartUniverse = 11;
F2.StemChannels = StemChannels;
F2.BloomInnerChannels = BloomInnerChannels;
F2.BloomOuterChannels = BloomOuterChannels;
- StripCount += BuildFlower(&OutputBuffer, F2);
+ StripCountOut += BuildFlower(&OutputBuffer2, F2);
- printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str);
+ WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_one.fold"), StringToData(OutputBuffer0));
+ WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_two.fold"), StringToData(OutputBuffer1));
+ WriteEntireFile(Ctx.FileHandler, ConstString("data/ss_blumen_three.fold"), StringToData(OutputBuffer2));
+
+ //printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str);
//printf("%d\n", StripCount);
+
+
+
return 0;
}
diff --git a/src/sculpture_gen/sculpture_gen.h b/src/sculpture_gen/sculpture_gen.h
index bbba18c..6336f46 100644
--- a/src/sculpture_gen/sculpture_gen.h
+++ b/src/sculpture_gen/sculpture_gen.h
@@ -8,87 +8,109 @@
internal void
WriteIndented(gs_string* Buffer, u32 Indent, char* Format, ...)
{
- va_list Args;
- va_start(Args, Format);
-
- for (u32 i = 0; i < Indent; i++)
- {
- OutChar(Buffer, '\t');
- }
-
- PrintFArgsList(Buffer, Format, Args);
- va_end(Args);
+ va_list Args;
+ va_start(Args, Format);
+
+ for (u32 i = 0; i < Indent; i++)
+ {
+ OutChar(Buffer, '\t');
+ }
+
+ PrintFArgsList(Buffer, Format, Args);
+ va_end(Args);
+}
+
+internal void
+WriteAssemblyCommonOpen(gs_string* Buffer, char* Name, u32 Scale, v3 Center, u32 StripCount)
+{
+ WriteIndented(Buffer, 0, "assembly_name: \"%s\";\n", Name);
+ WriteIndented(Buffer, 0, "assembly_scale: %d;\n", Scale);
+ WriteIndented(Buffer, 0, "assembly_center: (%f, %f, %f);\n", Center.x, Center.y, Center.z);
+ WriteIndented(Buffer, 0, "led_strip_count: %d;\n", StripCount);
}
internal void
WriteAssemblyUARTOpen(gs_string* Buffer, char* Name, u32 Scale, v3 Center, u32 StripCount, char* ComPort)
{
- WriteIndented(Buffer, 0, "assembly_name: \"%s\";\n", Name);
- WriteIndented(Buffer, 0, "assembly_scale: %d;\n", Scale);
- WriteIndented(Buffer, 0, "assembly_center: (%f, %f, %f);\n", Center.x, Center.y, Center.z);
- WriteIndented(Buffer, 0, "led_strip_count: %d;\n", StripCount);
- WriteIndented(Buffer, 0, "output_mode: \"UART\";\n");
-
- if (ComPort)
- {
- WriteIndented(Buffer, 0, "com_port: \"%s\";\n", ComPort);
- }
+ WriteAssemblyCommonOpen(Buffer, Name, Scale, Center, StripCount);
+ WriteIndented(Buffer, 0, "output_mode: \"UART\";\n");
+
+ if (ComPort)
+ {
+ WriteIndented(Buffer, 0, "com_port: \"%s\";\n", ComPort);
+ }
}
internal void
-WriteLedStripOpen(gs_string* Buffer, u32 Channel, char* ComPort)
+WriteAssemblySACNOpen(gs_string* Buffer, char* Name, u32 Scale, v3 Center, u32 StripCount)
{
- WriteIndented(Buffer, 0, "led_strip:\n{\n");
- WriteIndented(Buffer, 1, "output_uart: {\n");
- WriteIndented(Buffer, 2, "channel: %d;\n", Channel);
- WriteIndented(Buffer, 2, "com_port: \"%s\";\n", ComPort);
- WriteIndented(Buffer, 1, "};\n\n");
+ WriteAssemblyCommonOpen(Buffer, Name, Scale, Center, StripCount);
+ WriteIndented(Buffer, 0, "output_mode: \"SACN\";\n");
+}
+
+internal void
+WriteLedStripOpen(gs_string* Buffer, u32 UARTChannel, char* UARTComPort, u32 SACNStartUniverse, u32 SACNStartChannel)
+{
+ WriteIndented(Buffer, 0, "led_strip:\n{\n");
+
+ // SACN
+ WriteIndented(Buffer, 1, "output_sacn: {\n");
+ WriteIndented(Buffer, 2, "start_universe: %d;\n", SACNStartUniverse);
+ WriteIndented(Buffer, 2, "start_channel: %d;\n", SACNStartChannel);
+ WriteIndented(Buffer, 1, "};\n\n");
+
+ // UART
+ WriteIndented(Buffer, 1, "output_uart: {\n");
+ WriteIndented(Buffer, 2, "channel: %d;\n", UARTChannel);
+ WriteIndented(Buffer, 2, "com_port: \"%s\";\n", UARTComPort);
+ WriteIndented(Buffer, 1, "};\n\n");
+
}
internal void
WriteSegmentSequenceOpen(gs_string* Buffer, u32 SegmentCount)
{
- WriteIndented(Buffer, 1, "segment: {\n");
- WriteIndented(Buffer, 2, "point_placement_type: \"SegmentSequence\";\n");
- WriteIndented(Buffer, 2, "segment_sequence:\n");
- WriteIndented(Buffer, 2, "{\n");
- WriteIndented(Buffer, 3, "segment_count: %d;\n", SegmentCount);
+ WriteIndented(Buffer, 1, "segment: {\n");
+ WriteIndented(Buffer, 2, "point_placement_type: \"SegmentSequence\";\n");
+ WriteIndented(Buffer, 2, "segment_sequence:\n");
+ WriteIndented(Buffer, 2, "{\n");
+ WriteIndented(Buffer, 3, "segment_count: %d;\n", SegmentCount);
}
internal void
WriteSegmentSequenceSegment(gs_string* Buffer, v3 P0, v3 P1, u32 LedCount)
{
- WriteIndented(Buffer, 3, "segment: {\n");
- WriteIndented(Buffer, 4, "point_placement_type: \"InterpolatePoints\";\n");
- WriteIndented(Buffer, 4, "interpolate_points: {\n");
- WriteIndented(Buffer, 5, "start: (%f, %f, %f);\n", P0.x, P0.y, P0.z);
- WriteIndented(Buffer, 5, "end: (%f, %f, %f);\n", P1.x, P1.y, P1.z);
- WriteIndented(Buffer, 5, "led_count: %d;\n", LedCount);
- WriteIndented(Buffer, 4, "};\n");
- WriteIndented(Buffer, 3, "};\n");
+ WriteIndented(Buffer, 3, "segment: {\n");
+ WriteIndented(Buffer, 4, "point_placement_type: \"InterpolatePoints\";\n");
+ WriteIndented(Buffer, 4, "interpolate_points: {\n");
+ WriteIndented(Buffer, 5, "start: (%f, %f, %f);\n", P0.x, P0.y, P0.z);
+ WriteIndented(Buffer, 5, "end: (%f, %f, %f);\n", P1.x, P1.y, P1.z);
+ WriteIndented(Buffer, 5, "led_count: %d;\n", LedCount);
+ WriteIndented(Buffer, 4, "};\n");
+ WriteIndented(Buffer, 3, "};\n");
}
internal void
WriteSegmentSequenceClose(gs_string* Buffer)
{
- WriteIndented(Buffer, 2, "};\n");
- WriteIndented(Buffer, 1, "};\n");
+ WriteIndented(Buffer, 2, "};\n");
+ WriteIndented(Buffer, 1, "};\n");
}
internal void
WriteSegmentTagsOpen(gs_string* Buffer, u32 TagCount)
{
- WriteIndented(Buffer, 1, "tags_count: %d;\n", TagCount);
+ WriteIndented(Buffer, 1, "tags_count: %d;\n", TagCount);
}
internal void
WriteSegmentTag(gs_string* Buffer, char* TagName, char* TagValue)
{
- WriteIndented(Buffer, 1, "tag: {\n");
- WriteIndented(Buffer, 2, "name: \"%s\";\n", TagName);
- WriteIndented(Buffer, 2, "value: \"%s\";\n", TagValue);
- WriteIndented(Buffer, 1, "};\n");
-
+ WriteIndented(Buffer, 1, "tag: {\n");
+ WriteIndented(Buffer, 2, "name: \"%s\";\n", TagName);
+ WriteIndented(Buffer, 2, "value: \"%s\";\n", TagValue);
+ WriteIndented(Buffer, 1, "};\n");
+
}
internal void
@@ -99,7 +121,7 @@ WriteSegmentTagsClose(gs_string* Buffer)
internal void
WriteLedStripClose(gs_string* Buffer)
{
- WriteIndented(Buffer, 0, "};\n");
+ WriteIndented(Buffer, 0, "};\n");
}
#define SCULPTURE_GEN_H
diff --git a/src/serial_monitor/first.cpp b/src/serial_monitor/first.cpp
index 1f0c6d6..a87e7c7 100644
--- a/src/serial_monitor/first.cpp
+++ b/src/serial_monitor/first.cpp
@@ -6,13 +6,16 @@
#ifndef FIRST_CPP
+#include
+#include
+
#include "../gs_libs/gs_types.h"
#include "../gs_libs/gs_types.cpp"
+#include "../app/engine/foldhaus_log.h"
+global log_buffer* GlobalLogBuffer;
#define DEBUG_TRACK_FUNCTION
-#include
-#include
//#include "../app/foldhaus_platform.h"
//#include "../gs_libs/gs_win32.cpp"
@@ -27,193 +30,195 @@
u8*
FindNextHeader(gs_data Data, u8* StartAt)
{
- u8* At = StartAt;
- while (!(At[0] == 'U' &&
- At[1] == 'P' &&
- At[2] == 'X' &&
- At[3] == 'L') &&
- (u32)(At - Data.Memory) < Data.Size)
- {
- At++;
- }
- return At;
+ u8* At = StartAt;
+ while (!(At[0] == 'U' &&
+ At[1] == 'P' &&
+ At[2] == 'X' &&
+ At[3] == 'L') &&
+ (u32)(At - Data.Memory) < Data.Size)
+ {
+ At++;
+ }
+ return At;
}
void
CreateMessage(gs_data* Data, u8 Count)
{
- gs_memory_cursor WriteCursor = CreateMemoryCursor(*Data);
+ gs_memory_cursor WriteCursor = MemoryCursorCreateFromData(*Data);
+
+ u32 Channels[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ //40, 41, 42, 43, 44, 45, 46, 47,
+ };
+
+ u8* FirstHeaderAddr = 0;
+
+ for (u32 j = 0; j < sizeof(Channels) / sizeof(u32); j++)
+ {
+ u32 ChannelIndex = Channels[j];
+ uart_header* Header = MemoryCursorPushStruct(&WriteCursor, uart_header);
+ UART_FillHeader(Header, ChannelIndex, UART_SET_CHANNEL_WS2812);
- u32 Channels[] = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 16, 17, 18, 19, 20, 21, 22, 23,
- //40, 41, 42, 43, 44, 45, 46, 47,
- };
-
- u8* FirstHeaderAddr = 0;
-
- for (u32 j = 0; j < sizeof(Channels) / sizeof(u32); j++)
+ if (FirstHeaderAddr == 0)
{
- u32 ChannelIndex = Channels[j];
- uart_header* Header = PushStructOnCursor(&WriteCursor, uart_header);
- UART_FillHeader(Header, ChannelIndex, UART_SET_CHANNEL_WS2812);
-
- if (FirstHeaderAddr == 0)
- {
- FirstHeaderAddr = (u8*)Header;
- }
-
- uart_channel* Channel = PushStructOnCursor(&WriteCursor, uart_channel);
- Channel->ElementsCount = 3;
- Channel->ColorPackingOrder = 36; // 10010000
- Channel->PixelsCount = 300;
-
- for (u32 i = 0; i < Channel->PixelsCount; i++)
- {
- u8* Pixel = PushArrayOnCursor(&WriteCursor, u8, 3);
- Pixel[0] = Count;
- Pixel[1] = 0;
- Pixel[2] = 255 - Count;
- }
-
- uart_footer* Footer = PushStructOnCursor(&WriteCursor, uart_footer);
- Footer->CRC = UART_CalculateCRC((u8*)Header, (u8*)(Footer));
+ FirstHeaderAddr = (u8*)Header;
}
- uart_header* DrawAllHeader = PushStructOnCursor(&WriteCursor, uart_header);
- UART_FillHeader(DrawAllHeader, 255, UART_DRAW_ALL);
- uart_footer* DrawAllFooter =
- PushStructOnCursor(&WriteCursor, uart_footer);
- DrawAllFooter->CRC = UART_CalculateCRC((u8*)DrawAllHeader, (u8*)(DrawAllFooter));
+ uart_channel* Channel = MemoryCursorPushStruct(&WriteCursor, uart_channel);
+ Channel->ElementsCount = 3;
+ Channel->ColorPackingOrder = 36; // 10010000
+ Channel->PixelsCount = 300;
- Data->Size = ((u8*)DrawAllFooter - (u8*)FirstHeaderAddr) + sizeof(uart_footer);
+ for (u32 i = 0; i < Channel->PixelsCount; i++)
+ {
+ u8* Pixel = MemoryCursorPushArray(&WriteCursor, u8, 3);
+ Pixel[0] = Count;
+ Pixel[1] = 0;
+ Pixel[2] = 255 - Count;
+ }
+
+ uart_footer* Footer = MemoryCursorPushStruct(&WriteCursor, uart_footer);
+ Footer->CRC = UART_CalculateCRC((u8*)Header, (u8*)(Footer));
+ }
+
+ uart_header* DrawAllHeader = MemoryCursorPushStruct(&WriteCursor, uart_header);
+ UART_FillHeader(DrawAllHeader, 255, UART_DRAW_ALL);
+ uart_footer* DrawAllFooter =
+ MemoryCursorPushStruct(&WriteCursor, uart_footer);
+ DrawAllFooter->CRC = UART_CalculateCRC((u8*)DrawAllHeader, (u8*)(DrawAllFooter));
+
+ Data->Size = ((u8*)DrawAllFooter - (u8*)FirstHeaderAddr) + sizeof(uart_footer);
}
int main(int ArgCount, char** Args)
{
- gs_thread_context Ctx = Win32CreateThreadContext();
+ gs_thread_context Ctx = Win32CreateThreadContext();
+ GlobalLogBuffer = PushStruct(Ctx.Transient, log_buffer);
+ *GlobalLogBuffer = Log_Init(Ctx.Transient, 32);
+
+ HANDLE SerialHandle = Win32SerialPort_Open("\\\\.\\COM9", Ctx.Transient);
+ Win32SerialPort_SetState(SerialHandle, 2000000, 8, 0, 1);
+
+ gs_const_string OutFileName = ConstString("./serial_dump.data");
+
+
+ if (false)
+ {
+ Win32SerialPort_SetRead(SerialHandle);
- HANDLE SerialHandle = Win32SerialPort_Open("\\\\.\\COM9");
- Win32SerialPort_SetState(SerialHandle, 2000000, 8, 0, 1);
+ gs_data Data = PushSize(Ctx.Transient, KB(32));
- gs_const_string OutFileName = ConstString("./serial_dump.data");
+ Win32SerialPort_SetRead(SerialHandle);
+ u32 ReadSize = Win32SerialPort_ReadMessageWhenReady(SerialHandle, Data);
-
- if (false)
+ u8* SetChannelHeaderAddr = 0;
+ uart_header* SetChannelHeader = 0;
+ uart_header* DrawAllHeader = 0;
+ u8* ScanAt = Data.Memory;
+ do
{
- Win32SerialPort_SetRead(SerialHandle);
-
- gs_data Data = PushSizeToData(Ctx.Transient, KB(32));
-
- Win32SerialPort_SetRead(SerialHandle);
- u32 ReadSize = Win32SerialPort_ReadMessageWhenReady(SerialHandle, Data);
-
- u8* SetChannelHeaderAddr = 0;
- uart_header* SetChannelHeader = 0;
- uart_header* DrawAllHeader = 0;
- u8* ScanAt = Data.Memory;
- do
+ ScanAt = FindNextHeader(Data, ScanAt);
+ uart_header* Header = (uart_header*)ScanAt;
+
+ if (Header->RecordType == UART_SET_CHANNEL_WS2812)
+ {
+ printf("Set Channel:\n");
+ printf(" Channel: %d\n", Header->Channel);
+ printf(" Pixels: %d\n", ((uart_channel*)(Header + 1))->PixelsCount);
+ if (!SetChannelHeader)
{
- ScanAt = FindNextHeader(Data, ScanAt);
- uart_header* Header = (uart_header*)ScanAt;
-
- if (Header->RecordType == UART_SET_CHANNEL_WS2812)
- {
- printf("Set Channel:\n");
- printf(" Channel: %d\n", Header->Channel);
- printf(" Pixels: %d\n", ((uart_channel*)(Header + 1))->PixelsCount);
- if (!SetChannelHeader)
- {
- SetChannelHeaderAddr = (u8*)Header;
- SetChannelHeader = Header;
- }
- }
-
- if (Header->RecordType == UART_DRAW_ALL)
- {
- printf("Draw All:\n");
- printf(" Channel: %d\n", Header->Channel);
- if (!DrawAllHeader)
- {
- DrawAllHeader= Header;
- }
- }
-
- ScanAt += sizeof(uart_header);
- }while(((u32)(ScanAt - Data.Memory + sizeof(uart_header)) < Data.Size));
-
- uart_channel* Channel = (uart_channel*)(SetChannelHeader + 1);
-
- u8* DataStart = (u8*)(Channel + 1);
-
- uart_footer* Footer = (uart_footer*)(DataStart + (Channel->ElementsCount * Channel->PixelsCount));
-
- u32 TestCRC = UART_CalculateCRC((u8*)SetChannelHeader, (u8*)(Footer));
-
- uart_footer* DrawAllFooter = (uart_footer*)(DrawAllHeader + 1);
- u32 DrawwAllCRC = UART_CalculateCRC((u8*)DrawAllHeader, (u8*)(DrawAllFooter));
-
- HANDLE FileHandle = CreateFileA(OutFileName.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (FileHandle != INVALID_HANDLE_VALUE)
- {
- DWORD BytesWritten = 0;
- if (!WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL))
- {
- InvalidCodePath;
- }
+ SetChannelHeaderAddr = (u8*)Header;
+ SetChannelHeader = Header;
}
- CloseHandle(FileHandle);
- Win32SerialPort_Close(SerialHandle);
-
- }
- else if (true)
- {
- gs_data Data = PushSizeToData(Ctx.Transient, KB(32));
-
- u8 Count = 0;
- while(true)
+ }
+
+ if (Header->RecordType == UART_DRAW_ALL)
+ {
+ printf("Draw All:\n");
+ printf(" Channel: %d\n", Header->Channel);
+ if (!DrawAllHeader)
{
- CreateMessage(&Data, ++Count);
- Win32SerialPort_Write(SerialHandle, Data);
- Sleep(100);
+ DrawAllHeader= Header;
}
- }
- else if (false)
- {
- gs_data Data = PushSizeToData(Ctx.Transient, KB(32));
- gs_file File = Win32ReadEntireFile(Ctx.FileHandler, OutFileName, Data);
-
- gs_data Messages = {0};
- u8* ScanAt = Data.Memory;
- ScanAt = FindNextHeader(Data, ScanAt);
- uart_header* FirstHeader = (uart_header*)ScanAt;
- ScanAt += sizeof(uart_header);
-
- uart_header* LastHeader = 0;
- do
- {
- ScanAt = FindNextHeader(Data, ScanAt);
- uart_header* Header = (uart_header*)ScanAt;
- if (Header->RecordType == UART_DRAW_ALL)
- {
- LastHeader = Header;
- }
- ScanAt += sizeof(uart_header);
- }while((u32)(ScanAt - Data.Memory) < Data.Size);
-
- u8* OnePastLastByte = ((u8*)(LastHeader + 1)) + sizeof(uart_footer);
-
- Messages.Memory = (u8*)FirstHeader;
- Messages.Size = OnePastLastByte - Messages.Memory;
-
- while (true)
- {
- Win32SerialPort_Write(SerialHandle, Messages);
- Sleep(100);
- }
- }
+ }
+
+ ScanAt += sizeof(uart_header);
+ }while(((u32)(ScanAt - Data.Memory + sizeof(uart_header)) < Data.Size));
- return 0;
+ uart_channel* Channel = (uart_channel*)(SetChannelHeader + 1);
+
+ u8* DataStart = (u8*)(Channel + 1);
+
+ uart_footer* Footer = (uart_footer*)(DataStart + (Channel->ElementsCount * Channel->PixelsCount));
+
+ u32 TestCRC = UART_CalculateCRC((u8*)SetChannelHeader, (u8*)(Footer));
+
+ uart_footer* DrawAllFooter = (uart_footer*)(DrawAllHeader + 1);
+ u32 DrawwAllCRC = UART_CalculateCRC((u8*)DrawAllHeader, (u8*)(DrawAllFooter));
+
+ HANDLE FileHandle = CreateFileA(OutFileName.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
+ DWORD BytesWritten = 0;
+ if (!WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL))
+ {
+ InvalidCodePath;
+ }
+ }
+ CloseHandle(FileHandle);
+ Win32SerialPort_Close(SerialHandle);
+
+ }
+ else if (true)
+ {
+ gs_data Data = PushSize(Ctx.Transient, KB(32));
+
+ u8 Count = 0;
+ while(true)
+ {
+ CreateMessage(&Data, ++Count);
+ Win32SerialPort_Write(SerialHandle, Data);
+ Sleep(100);
+ }
+ }
+ else if (false)
+ {
+ gs_data Data = PushSize(Ctx.Transient, KB(32));
+ gs_file File = Win32ReadEntireFile(Ctx.FileHandler, OutFileName, Data);
+
+ gs_data Messages = {0};
+ u8* ScanAt = Data.Memory;
+ ScanAt = FindNextHeader(Data, ScanAt);
+ uart_header* FirstHeader = (uart_header*)ScanAt;
+ ScanAt += sizeof(uart_header);
+
+ uart_header* LastHeader = 0;
+ do
+ {
+ ScanAt = FindNextHeader(Data, ScanAt);
+ uart_header* Header = (uart_header*)ScanAt;
+ if (Header->RecordType == UART_DRAW_ALL)
+ {
+ LastHeader = Header;
+ }
+ ScanAt += sizeof(uart_header);
+ }while((u32)(ScanAt - Data.Memory) < Data.Size);
+
+ u8* OnePastLastByte = ((u8*)(LastHeader + 1)) + sizeof(uart_footer);
+
+ Messages.Memory = (u8*)FirstHeader;
+ Messages.Size = OnePastLastByte - Messages.Memory;
+
+ while (true)
+ {
+ Win32SerialPort_Write(SerialHandle, Messages);
+ Sleep(100);
+ }
+ }
+
+ return 0;
}
diff --git a/src/app/interface_test.cpp b/src/tests/interface_test.cpp
similarity index 100%
rename from src/app/interface_test.cpp
rename to src/tests/interface_test.cpp
diff --git a/src/tests/memory_arena_tests.cpp b/src/tests/memory_arena_tests.cpp
new file mode 100644
index 0000000..82d446f
--- /dev/null
+++ b/src/tests/memory_arena_tests.cpp
@@ -0,0 +1,202 @@
+internal u32
+NextRandom(u32* LastRandomValue)
+{
+ u32 Result = *LastRandomValue;
+ Result ^= Result << 13;
+ Result ^= Result >> 17;
+ Result ^= Result << 5;
+ *LastRandomValue = Result;
+ return Result;
+}
+
+void
+MemoryArenaTests()
+{
+ Test("Allocator")
+ {
+ gs_allocator A = CreatePlatformAllocator();
+
+ u8* Data = AllocArray(A, u8, 4096, "root");
+ for (int i = 0; i < 4096; i++) Data[i] = (i % MaxU8);
+ bool Success = true;
+ for (int i = 0; i < 4096; i++) Success &= Data[i] == (i % MaxU8);
+ TestResult(Success);
+
+ FreeArray(A, Data, u8, 4096);
+ TestResult(true); // TODO(PS): How do we test free?
+ }
+
+ Test("Memory Cursor")
+ {
+ gs_allocator A = CreatePlatformAllocator();
+
+ u64 Size = 4096;
+ gs_data D = AllocData(A, Size, "root");
+ gs_memory_cursor C = MemoryCursorCreate(D.Memory, D.Size);
+
+ u64 RoomLeft = MemoryCursorRoomLeft(C);
+ TestResult(RoomLeft == Size);
+ TestResult(MemoryCursorHasRoom(C));
+
+ TestResult(MemoryCursorCanPush(C, 2048));
+ TestResult(MemoryCursorCanPush(C, Size));
+ TestResult(!MemoryCursorCanPush(C, Size + 1));
+
+ for (u64 i = 0; i < 2048; i++)
+ {
+ u8* Byte = MemoryCursorPushSize(&C, 1).Memory;
+ *Byte = (u8)(i % 256);
+ }
+ RoomLeft = MemoryCursorRoomLeft(C);
+ TestResult(RoomLeft == (Size - 2048));
+
+ MemoryCursorPopSize(&C, 2048);
+ TestResult(C.Position == 0);
+
+ bool Success = true;
+ for (u64 i = 0; i < 2048; i++)
+ {
+ u8* Byte = MemoryCursorPushSize(&C, 1).Memory;
+ Success &= *Byte == (u8)(i % 256);
+ }
+ TestResult(Success);
+ }
+
+ Test("Memory Arena")
+ {
+ gs_allocator Al = CreatePlatformAllocator();
+ gs_memory_arena A = MemoryArenaCreate(128, 4, Al, 0, 0, "Test");
+
+ // NOTE(PS): We loop through this block 3 times
+ // 1. Make sure the arena works out of the box
+ // 2. Make sure the arena works the same way after clearing
+ // 3. Make sure the arena works the same way after freeing
+ for (int i = 0; i < 3; i++)
+ {
+ gs_data D0 = PushSize_(&A, 32, DEBUG_LOC);
+ TestResult(D0.Size == 32);
+
+ // NOTE(PS): This should still result in 32 bytes
+ // because its going to align the Cursor after
+ // it allocates to a multiple of 4 bytes
+ gs_data D1 = PushSize_(&A, 30, DEBUG_LOC);
+ TestResult(D1.Size == 32);
+
+ // NOTE(PS): Allocating bigger than the size remaining
+ // in the current cursor
+ gs_data D2 = PushSize_(&A, 128, DEBUG_LOC);
+ TestResult(D2.Size == 128);
+ TestResult(A.CursorsRoot != A.CursorsHead);
+
+ // NOTE(PS): Because there is still room in cursor
+ // 0, the head of this gs_data should be one byte
+ // past the end of D1
+ gs_data D3 = PushSize_(&A, 32, DEBUG_LOC);
+ TestResult(D3.Memory == D1.Memory + D1.Size);
+
+ if (i == 0)
+ {
+ MemoryArenaClear(&A);
+ } else if (i == 1) {
+ MemoryArenaFree(&A);
+ }
+ }
+ }
+
+ Test("Memory Arena - Push")
+ {
+ gs_allocator Al = CreatePlatformAllocator();
+ gs_memory_arena A = MemoryArenaCreate(128, 8, Al, 0, 0, "Test");
+
+ // NOTE(PS): This makes sure that the Arena is moving its next allocation
+ // pointer forward the appropriate amount after each allocation. If it isnt'
+ // then Array1 should be overlapping with Array0 in the event that the arena
+ // doesn't push the pointer forward enough
+ u32* Array0 = PushArray(&A, u32, 32);
+ u32* Array1 = PushArray(&A, u32, 32);
+
+ for (u32 i = 0; i < 32; i++)
+ {
+ Array0[i] = i;
+ Array1[i] = i * 4;
+ }
+
+ bool Success = true;
+ for (u32 i = 0; i < 32; i++)
+ {
+ Success &= Array0[i] == i && Array1[i] == i * 4;
+ }
+ TestResult(Success);
+
+ }
+
+ int FreeCount = 0;
+ int ClearCount = 0;
+
+ gs_debug_allocations_list* DEBUGAllocations = 0;
+
+ Test("Memory Arena - Stress Test")
+ {
+ // NOTE(PS): We're going to create thousands of allocations
+ // on the allocator of varying sizes. We're also going to clear
+ // and free the arena at random times to make sure it all works.
+
+ gs_allocator Al = CreatePlatformAllocator();
+ gs_memory_arena A = MemoryArenaCreate(4096, 4, Al, 0, 0, "Test");
+
+ // NOTE(PS): This is an array of allocation sizes
+ // As we repeat the loop we will get values out of this array
+ // semi-randomly.
+ // * if the value is 0, we will clear the arena
+ // * if the value is 2, we will free the arena
+ // * otherwise we will push a value sized allocation on the arena
+ u64 RandomSizes[] = { 8, 32, 128, 93, 1256, 4098, 0, 1024, 7, 18, 967, 53, 1, 2 };
+ u32 RandomSizesCount = sizeof(RandomSizes) / sizeof(u64);
+
+ bool Success = true;
+ u32 RandomSeed = 1923;
+ for (u64 i = 0; i < (4096 * 14); i++)
+ {
+ NextRandom(&RandomSeed);
+ u32 SizeIndex = RandomSeed % RandomSizesCount;
+ u64 RandomSize = RandomSizes[SizeIndex];
+
+ if (RandomSize == 0)
+ {
+ MemoryArenaClear(&A);
+ ClearCount++;
+ } else if (RandomSize == 2) {
+ MemoryArenaFree(&A);
+ FreeCount++;
+ } else {
+ gs_data D = PushSize_(&A, RandomSize, DEBUG_LOC);
+ // NOTE(PS): This check has to be >= because the arena
+ // might have adjusted to maintain alignment on this
+ // allocation.
+ Success &= D.Size >= RandomSize;
+ }
+ }
+
+ TestResult(Success);
+
+ DEBUGAllocations = Al.DEBUGAllocList;
+ }
+
+ printf("\tMemory Arena Cleared: %d times\n", ClearCount);
+ printf("\tMemory Arena Freed: %d times\n", FreeCount);
+
+#if 0
+ printf("\n\nAllocations:\n");
+ for (gs_debug_memory_allocation* ARecord = DEBUGAllocations->Root;
+ ARecord != 0;
+ ARecord = ARecord->Next)
+ {
+ printf("\t");
+ printf("%lld\t%s:%d - %s\n",
+ ARecord->Size,
+ ARecord->Loc.File,
+ ARecord->Loc.Line,
+ ARecord->Loc.Function);
+ }
+#endif
+}
\ No newline at end of file
diff --git a/src/tests/sanity_tests.cpp b/src/tests/sanity_tests.cpp
new file mode 100644
index 0000000..440d4e6
--- /dev/null
+++ b/src/tests/sanity_tests.cpp
@@ -0,0 +1,202 @@
+//
+// File: sanity_tests.cpp
+// Author: Peter Slattery
+// Creation Date: 2021-03-06
+//
+#ifndef SANITY_TESTS_CPP
+
+#include
+#include
+#include "../gs_libs/gs_types.h"
+#include "../gs_libs/gs_types.cpp"
+#include "../gs_libs/gs_tests.h"
+
+#include "../gs_libs/gs_path.h"
+#include "../gs_libs/gs_csv.h"
+
+#include "../app/platform_win32/win32_foldhaus_memory.h"
+#include "./memory_arena_tests.cpp"
+
+gs_memory_arena Scratch = {};
+
+bool StringTest (gs_const_string StrA, gs_const_string StrB)
+{
+ return StringsEqual(StrA, StrB);
+}
+bool StringTest (gs_string StrA, gs_string StrB)
+{
+ return StringsEqual(StrA, StrB);
+}
+
+bool PathTest (char* In, char* Out) {
+ return StringsEqual(SanitizePath(ConstString(In), &Scratch), ConstString(Out));
+}
+
+global char* SampleCSV = R"FOO(Flower Primary Hue (0-365) Secondary Hue (0-365) Tertiary Hue (0-365) Homonyms
+Flower A 55 32 128 foo, bar, blah, baz, whatever
+Flower B 123 344 32 foo, bar, blah, baz, whatever
+Flower C 55 32 128 foo, bar, blah, baz, whatever)FOO";
+
+struct test_sll
+{
+ u32 Val;
+ test_sll* Next;
+};
+
+int main (int ArgCount, char** Args)
+{
+ gs_allocator Al = CreatePlatformAllocator();
+ Scratch = MemoryArenaCreate(KB(4), Bytes(8), Al, 0, 0, "Scratch");
+
+ Test("SLL 1")
+ {
+ test_sll* Root = 0;
+ test_sll* Head = 0;
+
+ test_sll* First = PushStruct(&Scratch, test_sll);
+ First->Val = 0;
+ SLLInit(Root, Head, First);
+ TestResult((Root == First) && (Head == First));
+
+ for (u32 i = 1; i < 4; i++)
+ {
+ test_sll* New = PushStruct(&Scratch, test_sll);
+ New->Val = i;
+ SLLPush(Head, New);
+ TestResult((Root == First) && (Head == New));
+ }
+
+ bool Success = true;
+ u32 i = 0;
+ for (test_sll* At = Root;
+ At && At->Next != 0;
+ SLLNext(At))
+ {
+ Success &= (At->Val == i);
+ i += 1;
+ }
+ TestResult(Success);
+ }
+
+ Test("SLL Push Or Init")
+ {
+ test_sll* Root = 0;
+ test_sll* Head = 0;
+
+ test_sll* First = PushStruct(&Scratch, test_sll);
+ First->Val = 0;
+ SLLPushOrInit(Root, Head, First);
+ TestResult((Root == First) && (Head == First));
+
+ for (u32 i = 1; i < 4; i++)
+ {
+ test_sll* New = PushStruct(&Scratch, test_sll);
+ New->Val = i;
+ SLLPushOrInit(Root, Head, New);
+ TestResult((Root == First) && (Head == New));
+ }
+
+ bool Success = true;
+ u32 i = 0;
+ for (test_sll* At = Root;
+ At && At->Next != 0;
+ SLLNext(At))
+ {
+ Success &= (At->Val == i);
+ i += 1;
+ }
+ TestResult(Success);
+ }
+
+ Test("gs_string")
+ {
+ gs_string TestString = PushStringF(&Scratch, 256, "Hello there, Sailor!");
+
+ NullTerminate(&TestString);
+ TestResult(IsNullTerminated(TestString));
+
+ TestResult(StringTest(GetStringPrefix(TestString.ConstString, 5), ConstString("Hello")));
+ TestResult(StringTest(GetStringPostfix(TestString.ConstString, 5), ConstString("ilor!")));
+ TestResult(StringTest(GetStringAfter(TestString.ConstString, 13), ConstString("Sailor!")));
+ TestResult(StringTest(GetStringBefore(TestString.ConstString, 5), ConstString("Hello")));
+ TestResult(StringTest(Substring(TestString.ConstString, 5, 11), ConstString(" there")));
+
+ TestResult(FindFirst(TestString, 5, 'l') == 16);
+ TestResult(FindFirst(TestString, 0, 'k') == -1);
+ TestResult(FindLast(TestString, 10, 'l') == 3);
+ TestResult(FindLast(TestString, 'k') == -1);
+
+ TestResult(FindFirstFromSet(TestString.ConstString, "re") == 1);
+ TestResult(FindFirstFromSet(TestString.ConstString, "er") == 1);
+ TestResult(FindFirstFromSet(TestString.ConstString, "bk") == -1);
+ TestResult(FindFirstFromSet(TestString.ConstString, "ek") == 1);
+
+ TestResult(FindLastFromSet(TestString.ConstString, "re") == 18);
+ TestResult(FindLastFromSet(TestString.ConstString, "er") == 18);
+ TestResult(FindLastFromSet(TestString.ConstString, "bk") == -1);
+ TestResult(FindLastFromSet(TestString.ConstString, "rk") == 18);
+
+ TestResult(StringContains(TestString.ConstString, ','));
+ TestResult(!StringContains(TestString.ConstString, '@'));
+ TestResult(StringsEqual(TestString, TestString));
+
+ TestResult(StringEqualsCharArray(TestString, "Hello there, Sailor!"));
+ TestResult(!StringEqualsCharArray(TestString, "Hello there, Sailor"));
+ TestResult(!StringEqualsCharArray(TestString, "Foobar"));
+
+ ReverseStringInPlace(&TestString);
+ TestResult(StringTest(TestString, MakeString("!roliaS ,ereht olleH")));
+ ReverseStringInPlace(&TestString);
+
+ TestResult(ParseUInt(ConstString("532")) == 532);
+ TestResult(ParseInt(ConstString("-1234567890")) == -1234567890);
+ TestResult(ParseFloat(ConstString("-12345.6789")) == -12345.6789);
+ TestResult(ParseFloat(ConstString("-1")) == -1);
+ TestResult(ParseFloat(ConstString("-.035")) == -.035);
+
+ TestString.Length = 0;
+ U64ToASCII(&TestString, 53298, 10);
+ TestResult(StringTest(TestString.ConstString, ConstString("53298")));
+
+ TestString.Length = 0;
+ R64ToASCII(&TestString, -145732.321, 2);
+ TestResult(StringTest(TestString.ConstString, ConstString("-145732.32")));
+ }
+
+ Test("gs_path.h")
+ {
+ TestResult(PathTest(".", "."));
+ TestResult(PathTest(".\\", ".\\"));
+ TestResult(PathTest("./", ".\\"));
+ TestResult(PathTest("./../", "..\\"));
+ TestResult(PathTest("C:/users/pslattery\\test.foo", "C:\\users\\pslattery\\test.foo"));
+ TestResult(PathTest("./test/../foo.bar", ".\\foo.bar"));
+ TestResult(PathTest("C:\\hello\\world\\.\\test", "C:\\hello\\world\\test"));
+ }
+
+ Test("gs_csv.h")
+ {
+ gs_const_string TestCSV = ConstString(SampleCSV);
+ gscsv_sheet Sheet = CSV_Parse(TestCSV, { '\t' }, &Scratch);
+
+ gs_const_string Cell = CSVSheet_GetCell(Sheet, 0, 0);
+ TestResult(StringsEqual(Cell, ConstString("Flower")));
+
+ Cell = CSVSheet_GetCell(Sheet, 1, 1);
+ TestResult(StringsEqual(Cell, ConstString("55")));
+
+ Cell = CSVSheet_GetCell(Sheet, 4, 1);
+ TestResult(StringsEqual(Cell, ConstString("foo, bar, blah, baz, whatever")));
+
+ Cell = CSVSheet_GetCell(Sheet, 4, 3);
+ TestResult(StringsEqual(Cell, ConstString("foo, bar, blah, baz, whatever")));
+ }
+
+ MemoryArenaTests();
+
+ return 0;
+}
+
+
+#define SANITY_TESTS_CPP
+#endif // SANITY_TESTS_CPP
\ No newline at end of file
diff --git a/src/app/test_patterns.h b/src/tests/test_patterns.h
similarity index 100%
rename from src/app/test_patterns.h
rename to src/tests/test_patterns.h
diff --git a/src_v2/core/lumenarium_core.h b/src_v2/core/lumenarium_core.h
new file mode 100644
index 0000000..38607a8
--- /dev/null
+++ b/src_v2/core/lumenarium_core.h
@@ -0,0 +1,44 @@
+// NOTE(PS):
+// need to include before this:
+// #include
+// #include
+// #include
+
+#if defined(__clang__)
+# pragma GCC diagnostic ignored "-Wunused-value"
+# pragma GCC diagnostic ignored "-Wvarargs"
+# pragma GCC diagnostic ignored "-Wwritable-strings"
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+#if defined(DEBUG)
+# define USE_ASSERTS 1
+#endif
+
+#include "lumenarium_core_assert.h"
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#define STB_SPRINTF_IMPLEMENTATION
+#define STBTT_assert(x) assert(x)
+#include "../libs/stb_truetype.h"
+#include "../libs/stb_sprintf.h"
+
+#define HANDMADE_MATH_IMPLEMENTATION
+#define HANDMADE_MATH_STATIC
+#include "../libs/HandmadeMath.h"
+typedef hmm_v2 v2;
+typedef hmm_v3 v3;
+typedef hmm_v4 v4;
+typedef hmm_mat4 m44;
+
+#include "lumenarium_core_types.h"
+#include "lumenarium_core_memory.h"
+#include "lumenarium_core_string.h"
+#include "lumenarium_core_hash.h"
+#include "lumenarium_core_random.h"
+
+#include "lumenarium_core_file.h"
+#include "lumenarium_core_window.h"
+#include "lumenarium_core_time.h"
+#include "lumenarium_core_threads.h"
+#include "lumenarium_core_socket.h"
\ No newline at end of file
diff --git a/src_v2/core/lumenarium_core_assert.h b/src_v2/core/lumenarium_core_assert.h
new file mode 100644
index 0000000..ca7896d
--- /dev/null
+++ b/src_v2/core/lumenarium_core_assert.h
@@ -0,0 +1,60 @@
+/* date = March 26th 2022 3:42 pm */
+
+#ifndef LUMENARIUM_CORE_ASSERT_H
+#define LUMENARIUM_CORE_ASSERT_H
+
+#if defined(PRINT_ASSERTS)
+# include
+# define err_write(s,...) err_write_(s,__VA_ARGS__)
+static FILE* file_err;
+void err_write_(char* fmt, ...) {
+ if (!file_err) return;
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(file_err, fmt, args);
+ va_end(args);
+}
+void open_err_file() { file_err = fopen("./err.txt", "wb"); }
+void close_err_file() { fclose(file_err); }
+#else
+# define err_write(s,...)
+void open_err_file() {}
+void close_err_file() {}
+#endif
+
+#if !defined(PLATFORM_wasm)
+
+// this assert works by simply trying to write to an invalid address
+// (in this case, 0x0), which will crash in most debuggers
+# define assert_always (*((volatile int*)0) = 0xFFFF)
+
+#else
+WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned int line);
+# define assert_always wasm_assert_always(__FILE__, sizeof(__FILE__), __LINE__)
+#endif // defined(PLATFORM_WASM)
+
+#ifdef USE_ASSERTS
+# define assert(c) do { \
+if (!(c)) { \
+err_write("Assert Hit: %s:%u\n", __FILE__, (u32)__LINE__); \
+close_err_file(); \
+assert_always; \
+} \
+} while(false)
+
+// useful for catching cases that you aren't sure you'll hit, but
+// want to be alerted when they happen
+# define invalid_code_path assert(0);
+
+// useful for switch statements on enums that might grow. You'll
+// break in the debugger the first time the default case is hit
+// with a new enum value
+# define invalid_default_case default: { assert(0); } break;
+
+#else
+# define assert(c)
+# define invalid_code_path
+# define invalid_default_case default: { } break;
+#endif
+
+#endif //LUMENARIUM_CORE_ASSERT_H
diff --git a/src_v2/core/lumenarium_core_file.h b/src_v2/core/lumenarium_core_file.h
new file mode 100644
index 0000000..6113182
--- /dev/null
+++ b/src_v2/core/lumenarium_core_file.h
@@ -0,0 +1,252 @@
+#if !defined(LUMENARIUM_CORE_FILE_H)
+#define LUMENARIUM_CORE_FILE_H
+
+typedef struct File_Handle File_Handle;
+struct File_Handle
+{
+ u64 value;
+};
+
+typedef u32 File_Access_Flags;
+enum
+{
+ FileAccess_None = 0,
+ FileAccess_Read = 1,
+ FileAccess_Write = 2,
+};
+
+typedef u32 File_Create_Flags;
+enum
+{
+ // these match https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
+ FileCreate_None = 0,
+
+ // Creates a new file, only if it does not already exist.
+ // If the file exists, an error is returned
+ FileCreate_New = 1,
+
+ // Creates a new file, always.
+ // If the specified file exists and is writable, the file is overwritten
+ FileCreate_CreateAlways = 2,
+
+ // Opens a file or device, only if it exists.
+ FileCreate_OpenExisting = 3,
+
+ // Opens a file, always.
+ FileCreate_OpenAlways = 4,
+};
+
+typedef u32 File_Flags;
+enum
+{
+ FileFlag_IsFile = 0,
+ FileFlag_IsDir = 1,
+};
+
+typedef struct File_Info File_Info;
+struct File_Info
+{
+ String path;
+ String path_abs;
+ u64 size;
+ u64 time_created;
+ u64 time_last_write;
+ File_Flags flags;
+};
+
+typedef struct File_Info_List_Ele File_Info_List_Ele;
+struct File_Info_List_Ele
+{
+ File_Info info;
+ File_Info_List_Ele* next;
+};
+
+typedef struct File_Info_List File_Info_List;
+struct File_Info_List
+{
+ File_Info_List_Ele* first;
+ File_Info_List_Ele* last;
+};
+
+// For Cross Platform File Operations use these:
+
+typedef u32 File_Async_Job_Flags;
+enum
+{
+ FileAsyncJob_Invalid = 0,
+ FileAsyncJob_Read = 1 << 0,
+ FileAsyncJob_Write = 1 << 1,
+ FileAsyncJob_InFlight = 1 << 2,
+ FileAsyncJob_Success = 1 << 3,
+ FileAsyncJob_Failed = 1 << 4,
+};
+
+typedef struct File_Async_Job_Args File_Async_Job_Args;
+struct File_Async_Job_Args
+{
+ String path;
+ Data data;
+ File_Async_Job_Flags flags;
+ u32 error;
+};
+
+typedef void File_Async_Cb(File_Async_Job_Args args, u8* user_data);
+
+typedef struct File_Async_Job File_Async_Job;
+struct File_Async_Job
+{
+ Data job_memory;
+ File_Async_Job_Args args;
+ File_Async_Cb* cb;
+};
+
+typedef void File_Async_Job_System_Do_Job(File_Async_Job* job);
+
+typedef struct File_Async_Job_System File_Async_Job_System;
+struct File_Async_Job_System
+{
+ File_Async_Job_System_Do_Job* do_job;
+};
+
+global Allocator* file_jobs_arena = 0;
+#define FILE_ASYNC_MAX_JOBS 32
+global File_Async_Job file_async_jobs[FILE_ASYNC_MAX_JOBS];
+global u32 file_async_jobs_len = 0;
+
+internal File_Async_Job_System file_async_jobs_init(File_Async_Job_System_Do_Job* do_job);
+internal bool file_async_job_add(File_Async_Job job);
+internal File_Async_Job file_async_job_rem(u64 index);
+internal bool file_async_read(String path, File_Async_Cb* cb);
+internal bool file_async_write(String path, Data data, File_Async_Cb* cb);
+internal void file_async_job_complete(File_Async_Job* job, u8* user_data);
+internal void file_async_jobs_do_work(File_Async_Job_System* system, u64 max_jobs, u8* user_data);
+internal void file_async_jobs_do_work(File_Async_Job_System* system, u64 max_jobs, u8* user_data);
+
+typedef u32 Platform_Enum_Dir_Flags;
+enum
+{
+ EnumDir_Recursive = 1,
+ EnumDir_IncludeDirectories = 2,
+};
+
+///////////////////////////////////////
+///////////////////////////////////////
+// IMPLEMENTATION
+///////////////////////////////////////
+///////////////////////////////////////
+
+internal File_Async_Job_System
+file_async_jobs_init(File_Async_Job_System_Do_Job* do_job)
+{
+ file_jobs_arena = paged_allocator_create_reserve(MB(4), 256);
+ File_Async_Job_System result = {};
+ result.do_job = do_job;
+ return result;
+}
+
+internal bool
+file_async_job_add(File_Async_Job job)
+{
+ if (file_async_jobs_len >= FILE_ASYNC_MAX_JOBS) return false;
+
+ // Copy data to job local memory
+ u64 size_needed = job.args.path.len + job.args.data.size + 1;
+ u8* job_mem = allocator_alloc(file_jobs_arena, size_needed);
+ String job_path = string_create(job_mem, 0, job.args.path.len + 1);
+ u64 copied = string_copy_to(&job_path, job.args.path);
+ Data job_data = data_create(job_mem + job_path.cap + 1, size_needed - (job_path.cap + 1));
+ memory_copy(job.args.data.base, job_data.base, job.args.data.size);
+ job.args.path = job_path;
+ job.args.data = job_data;
+ job.job_memory = data_create(job_mem, size_needed);
+
+ file_async_jobs[file_async_jobs_len++] = job;
+ return true;
+}
+
+internal File_Async_Job
+file_async_job_rem(u64 index)
+{
+ assert(index < file_async_jobs_len);
+ File_Async_Job result = file_async_jobs[index];
+
+ file_async_jobs_len -= 1;
+ if (file_async_jobs_len > 0)
+ {
+ u32 last_job = file_async_jobs_len;
+ file_async_jobs[index] = file_async_jobs[last_job];
+ }
+
+ return result;
+}
+
+internal bool
+file_async_read(String path, File_Async_Cb* cb)
+{
+ File_Async_Job job = {};
+ job.args.path = path;
+ job.args.flags = (
+ FileAsyncJob_Read |
+ FileAsyncJob_InFlight
+ );
+ job.cb = cb;
+ bool result = file_async_job_add(job);
+ return result;
+}
+
+internal bool
+file_async_write(String path, Data data, File_Async_Cb* cb)
+{
+ File_Async_Job job = {};
+ job.args.path = path;
+ job.args.data = data;
+ job.args.flags = (
+ FileAsyncJob_Write |
+ FileAsyncJob_InFlight
+ );
+ job.cb = cb;
+ bool result = file_async_job_add(job);
+ return result;
+}
+
+internal void
+file_async_job_complete(File_Async_Job* job, u8* user_data)
+{
+ job->cb(job->args, user_data);
+ allocator_free(file_jobs_arena, job->job_memory.base, job->job_memory.size);
+ if (has_flag(job->args.flags, FileAsyncJob_Write))
+ {
+ allocator_free(file_jobs_arena, job->args.data.base, job->args.data.size);
+ }
+}
+
+internal void
+file_async_jobs_do_work(File_Async_Job_System* system, u64 max_jobs, u8* user_data)
+{
+ assert(system->do_job);
+
+ u64 to_do = max_jobs;
+ if (max_jobs > file_async_jobs_len) to_do = file_async_jobs_len;
+
+ File_Async_Job_Flags completed = (
+ FileAsyncJob_Success |
+ FileAsyncJob_Failed
+ );
+
+ for (u64 i = to_do - 1; i < to_do; i--)
+ {
+ File_Async_Job* job = file_async_jobs + i;
+ system->do_job(job);
+ if (has_flag(job->args.flags, completed))
+ {
+ if (has_flag_exact(job->args.flags, FileAsyncJob_Success))
+ {
+ file_async_job_complete(job, user_data);
+ }
+ file_async_job_rem(i);
+ }
+ }
+}
+
+
+#endif // LUMENARIUM_CORE_FILE_H
\ No newline at end of file
diff --git a/src_v2/core/lumenarium_core_hash.h b/src_v2/core/lumenarium_core_hash.h
new file mode 100644
index 0000000..1598a5a
--- /dev/null
+++ b/src_v2/core/lumenarium_core_hash.h
@@ -0,0 +1,64 @@
+/* date = April 1st 2022 7:29 pm */
+
+#ifndef LUMENARIUM_CORE_HASH_H
+#define LUMENARIUM_CORE_HASH_H
+
+//
+// DJB2
+// Source: http://www.cse.yorku.ca/~oz/hash.html
+
+internal u32
+hash_djb2_append_str_to_u32(u32 base, char* str, u64 len)
+{
+ u32 result = base;
+ for (u64 i = 0; i < len; i++)
+ {
+ result = ((result << 5) + result) + (u8)str[i];
+ }
+ return result;
+}
+
+internal u32
+hash_djb2_str_to_u32(char* str, u64 len)
+{
+ return hash_djb2_append_str_to_u32(5381, str, len);
+}
+
+internal u32
+hash_djb2_cstr_to_u32(char* str)
+{
+ u64 len = c_str_len(str);
+ return hash_djb2_str_to_u32(str, len);
+}
+
+internal u32
+hash_djb2_string_to_u32(String str)
+{
+ return hash_djb2_str_to_u32((char*)str.str, str.len);
+}
+
+internal u64
+hash_djb2_str_to_u64(char* str, u64 len)
+{
+ u64 result = 5381;
+ for (u64 i = 0; i < len; i++)
+ {
+ result = ((result << 5) + result) + (u8)str[i];
+ }
+ return result;
+}
+
+internal u64
+hash_djb2_cstr_to_u64(char* str)
+{
+ u64 len = c_str_len(str);
+ return hash_djb2_str_to_u64(str, len);
+}
+
+internal u64
+hash_djb2_string_to_u64(String str)
+{
+ return hash_djb2_str_to_u64((char*)str.str, str.len);
+}
+
+#endif //LUMENARIUM_HASH_H
diff --git a/src_v2/core/lumenarium_core_memory.h b/src_v2/core/lumenarium_core_memory.h
new file mode 100644
index 0000000..8ab17ca
--- /dev/null
+++ b/src_v2/core/lumenarium_core_memory.h
@@ -0,0 +1,810 @@
+#ifndef LUMENARIUM_CORE_MEMORY_H
+#define LUMENARIUM_CORE_MEMORY_H
+
+#if !defined(OS_MEM_PAGE_SIZE)
+# error "You must define OS_MEM_PAGE_SIZE for core_memory.h to compile properly"
+#endif
+
+//////////////////////////////////////////////
+// Memory Helpers
+
+#define Bytes(x) (x)
+#define KB(x) ((x) << 10)
+#define MB(x) ((x) << 20)
+#define GB(x) (((u64)x) << 30)
+#define TB(x) (((u64)x) << 40)
+
+#define memory_zero_array(b,t,c) memory_zero((u8*)(b), sizeof(t) * (c))
+internal void memory_zero(u8* base, u64 size);
+internal void memory_copy(u8* from, u8* to, u64 size);
+
+//////////////////////////////////////////////
+// Data
+
+#define get_byte(value, byte_index) ((value >> (8 * byte_index)) & 0xFF)
+
+typedef struct Data Data;
+struct Data
+{
+ u8* base;
+ u64 size;
+};
+
+internal Data
+data_create(u8* base, u64 size)
+{
+ Data result = {};
+ result.base = base;
+ result.size = size;
+ return result;
+}
+
+//////////////////////////////////////////////
+// Data Writer
+
+typedef struct Data_Writer Data_Writer;
+struct Data_Writer
+{
+ Data data;
+ u64 at;
+};
+
+// NOTE(PS): functions ending in _b treat data in the Data_Writer as big endian
+// order (network ordering) where functions ending in _l treat data into little
+// endian order
+// It is always assumed that values not in the Data_Writer (ie the other args
+// to the function or the functions return value) are in little endian order
+
+internal u8 dw_get_u8(Data_Writer* w);
+// TODO(PS): get functions
+
+internal void dw_put_u8(Data_Writer* w, u8 b);
+internal void dw_put_u16_b(Data_Writer* w, u16 b);
+internal void dw_put_u16_l(Data_Writer* w, u16 b);
+internal void dw_put_u32_b(Data_Writer* w, u32 b);
+internal void dw_put_u32_l(Data_Writer* w, u32 b);
+internal void dw_put_u64_b(Data_Writer* w, u64 b);
+internal void dw_put_u64_l(Data_Writer* w, u64 b);
+
+//////////////////////////////////////////////
+// Allocator
+
+typedef struct Allocator Allocator;
+
+typedef u8* Allocator_Alloc(Allocator* allocator, u64 size);
+typedef void Allocator_Free(Allocator* allocator, u8* base, u64 size);
+typedef u8* Allocator_Realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size);
+typedef void Allocator_Clear(Allocator* allocator);
+typedef void Allocator_Destroy(Allocator* allocator);
+
+struct Allocator
+{
+ Allocator_Alloc* alloc;
+ Allocator_Free* free;
+ Allocator_Realloc* realloc;
+ Allocator_Clear* clear;
+ Allocator_Destroy* destroy;
+
+ Allocator* parent;
+
+ u8* allocator_data;
+};
+
+#define allocator_alloc(a,s) (a)->alloc((a),(s))
+#define allocator_alloc_struct(a,t) (t*)(a)->alloc((a),sizeof(t))
+#define allocator_alloc_array(a,t,c) (t*)(a)->alloc((a),sizeof(t)*(c))
+
+#define allocator_free(a,b,s) (a)->free((a),(b),(s))
+#define allocator_free_struct(a,b,t) (a)->free((a),(b),sizeof(t))
+#define allocator_free_array(a,b,t,c) (a)->free((a),(b),sizeof(t)*(c))
+
+#define allocator_realloc(a,b,os,ns) (a)->realloc((a),(b),(os),(ns))
+#define allocator_realloc_array(a,b,t,oc,nc) (t*)(a)->realloc((a),(b),sizeof(t)*(oc),sizeof(t)*(nc))
+
+#define allocator_clear(a) (a)->clear(a)
+#define allocator_destroy(a) (a)->destroy(a)
+
+internal Allocator* paged_allocator_create_reserve(u64 reserve_size, u64 page_size);
+
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+// IMPLEMENTATION
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+
+/////////////////////////////////////////
+// Memory Functions
+
+void
+memory_zero_no_simd(u8* base, u64 size)
+{
+ for (u64 i = 0; i < size; i++) base[i] = 0;
+}
+
+void
+memory_copy_no_simd(u8* from, u8* to, u64 size)
+{
+ for (u64 i = 0; i < size; i++) to[i] = from[i];
+}
+
+#if defined(PLATFORM_HAS_SIMD)
+
+// TODO(PS):
+// TODO(PS):
+// TODO(PS):
+
+void
+memory_zero_simd(u8* base, u64 size)
+{
+ memory_zero_no_simd(base, size);
+}
+
+void
+memory_copy_simd(u8* from, u8* to, u64 size)
+{
+ memory_copy_no_simd(from, to, size);
+}
+
+# define memory_zero_(b,s) memory_zero_simd((b),(s))
+# define memory_copy_(f,t,s) memory_copy_simd((f),(t),(s))
+
+#else
+# define memory_zero_(b,s) memory_zero_no_simd((b),(s))
+# define memory_copy_(f,t,s) memory_copy_no_simd((f),(t),(s))
+
+#endif // defined(PLATFORM_HAS_SIMD)
+
+#define zero_struct(s) memory_zero((u8*)(&s), sizeof(s))
+
+internal void memory_zero(u8* base, u64 size) { memory_zero_(base, size); }
+internal void memory_copy(u8* from, u8* to, u64 size) {
+ memory_copy_(from, to, size);
+}
+
+// @Maintenance - if the function only has one argument, it should become
+// round_to_os_page_multiple
+u64
+round_size_to_page_multiple(u64 size, u64 page_size)
+{
+ u64 rem = size % page_size;
+ if (rem != 0 || size < page_size)
+ {
+ u64 grow = page_size - rem;
+ size += grow;
+ }
+ return size;
+}
+
+u64
+round_size_to_os_page_multiple(u64 size)
+{
+ return round_size_to_page_multiple(size, OS_MEM_PAGE_SIZE);
+}
+
+/////////////////////////////////////////
+// Data Writer
+
+internal void
+dw_put_u8(Data_Writer* w, u8 b)
+{
+ if (w->at < w->data.size)
+ {
+ w->data.base[w->at++] = b;
+ }
+}
+
+internal u8
+dw_get_u8(Data_Writer* w)
+{
+ u8 result = 0;
+ if (w->at < w->data.size)
+ {
+ result = w->data.base[w->at];
+ }
+ return result;
+}
+
+internal void
+dw_put_u16_b(Data_Writer* w, u16 b)
+{
+ dw_put_u8(w, get_byte(b, 1));
+ dw_put_u8(w, get_byte(b, 0));
+}
+
+internal void
+dw_put_u16_l(Data_Writer* w, u16 b)
+{
+ dw_put_u8(w, get_byte(b, 0));
+ dw_put_u8(w, get_byte(b, 1));
+}
+
+internal void
+dw_put_u32_b(Data_Writer* w, u32 b)
+{
+ dw_put_u8(w, get_byte(b, 3));
+ dw_put_u8(w, get_byte(b, 2));
+ dw_put_u8(w, get_byte(b, 1));
+ dw_put_u8(w, get_byte(b, 0));
+}
+
+internal void
+dw_put_u32_l(Data_Writer* w, u32 b)
+{
+ dw_put_u8(w, get_byte(b, 0));
+ dw_put_u8(w, get_byte(b, 1));
+ dw_put_u8(w, get_byte(b, 2));
+ dw_put_u8(w, get_byte(b, 3));
+}
+
+internal void
+dw_put_u64_b(Data_Writer* w, u64 b)
+{
+ dw_put_u8(w, get_byte(b, 7));
+ dw_put_u8(w, get_byte(b, 6));
+ dw_put_u8(w, get_byte(b, 5));
+ dw_put_u8(w, get_byte(b, 4));
+ dw_put_u8(w, get_byte(b, 3));
+ dw_put_u8(w, get_byte(b, 2));
+ dw_put_u8(w, get_byte(b, 1));
+ dw_put_u8(w, get_byte(b, 0));
+}
+
+internal void
+dw_put_u64_l(Data_Writer* w, u64 b)
+{
+ dw_put_u8(w, get_byte(b, 0));
+ dw_put_u8(w, get_byte(b, 1));
+ dw_put_u8(w, get_byte(b, 2));
+ dw_put_u8(w, get_byte(b, 3));
+ dw_put_u8(w, get_byte(b, 4));
+ dw_put_u8(w, get_byte(b, 5));
+ dw_put_u8(w, get_byte(b, 6));
+ dw_put_u8(w, get_byte(b, 7));
+}
+
+////////////////////////////////////////
+// Allocator
+//
+// A generic interface for any memory-providing construct
+//
+// To implement a complete allocator, all that is really required
+// is to create its Allocator_Alloc function
+
+internal void
+allocator_destroy_(Allocator* allocator, u64 custom_data_size)
+{
+ zero_struct(*allocator);
+ u64 size = sizeof(Allocator) + custom_data_size;
+ os_mem_decommit((u8*)allocator, size);
+ os_mem_release((u8*)allocator, size);
+}
+
+/////////////////////////////////////////
+// Bump Allocator
+
+typedef struct Allocator_Bump Allocator_Bump;
+struct Allocator_Bump
+{
+ u8* base;
+ u64 at;
+ u64 size_committed;
+ u64 size_reserved;
+ u64 page_size;
+ u64 high_water_mark;
+};
+
+#if defined(DEBUG)
+# define bump_allocator_validate(a) bump_allocator_validate_(a)
+#else
+# define bump_allocator_validate(a)
+#endif
+internal void
+bump_allocator_validate_(Allocator* allocator)
+{
+ Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data;
+ assert(bump != 0);
+ if (bump->size_reserved > 0)
+ {
+ assert(bump->base != 0);
+ }
+ assert(bump->at <= bump->size_committed);
+ assert(bump->size_committed <= bump->size_reserved);
+ assert(bump->page_size > 0);
+ assert(bump->high_water_mark <= bump->size_reserved);
+}
+
+internal u8*
+bump_allocator_alloc_inner(Allocator* allocator, Allocator_Bump* bump, u64 size)
+{
+ bump_allocator_validate(allocator);
+ u64 at_after = bump->at + size;
+ // TODO(PS): align up to 8 bytes
+
+ if (at_after >= bump->size_committed)
+ {
+ // determine new size of the arena
+ u64 new_size = bump->size_committed * 2;
+ if (new_size == 0)
+ {
+ if (bump->page_size == 0) bump->page_size = OS_MEM_PAGE_SIZE;
+ new_size = bump->page_size;
+ }
+ if (new_size < at_after)
+ {
+ new_size = round_size_to_page_multiple(at_after, bump->page_size);
+ }
+
+ if (allocator->parent)
+ {
+ bump->base = allocator_realloc(
+ allocator->parent,
+ bump->base,
+ bump->size_committed,
+ new_size
+ );
+ if (bump->base != 0)
+ {
+ bump->size_reserved = new_size;
+ bump->size_committed = new_size;
+ }
+ }
+ else
+ {
+ if (new_size <= bump->size_reserved)
+ {
+ u64 next_page = round_size_to_os_page_multiple(bump->at);
+ if (bump->at == 0 && bump->size_committed == 0) next_page = 0;
+ u64 commit_amt = new_size - next_page;
+ u8* new_page = os_mem_commit(bump->base + next_page, commit_amt);
+ if (new_page != 0)
+ {
+ bump->size_committed = new_size;
+ }
+ }
+ else
+ {
+ invalid_code_path; // out of reserved memory
+ }
+ }
+ }
+
+ u8* result = bump->base + bump->at;
+ bump->at = at_after;
+ bump->high_water_mark = max(bump->at, bump->high_water_mark);
+
+ bump_allocator_validate(allocator);
+ return result;
+}
+
+internal u8*
+bump_allocator_alloc(Allocator* allocator, u64 size)
+{
+ Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data;
+ u8* result = bump_allocator_alloc_inner(allocator, bump, size);
+ return result;
+}
+
+internal u8*
+bump_allocator_realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size)
+{
+ u8* result = bump_allocator_alloc(allocator, new_size);
+ memory_copy(base, result, old_size);
+ return result;
+}
+
+internal void
+bump_allocator_clear(Allocator* allocator)
+{
+ if (!allocator->allocator_data) return;
+ Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data;
+ bump->at = 0;
+ bump_allocator_validate(allocator);
+}
+
+internal void
+bump_allocator_destroy_(Allocator_Bump* bump)
+{
+ os_mem_decommit(bump->base, bump->size_committed);
+ os_mem_release(bump->base, bump->size_reserved);
+}
+
+internal void
+bump_allocator_destroy(Allocator* allocator)
+{
+ Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data;
+ bump_allocator_destroy_(bump);
+ allocator_destroy_(allocator, sizeof(Allocator_Bump));
+}
+
+internal u64
+bump_allocator_at(Allocator* allocator)
+{
+ Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data;
+ bump_allocator_validate(allocator);
+ return bump->at;
+}
+
+internal void
+bump_allocator_rewind(Allocator* allocator, u64 to_point)
+{
+ Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data;
+ bump_allocator_validate(allocator);
+#if defined(DEBUG)
+ memory_zero(bump->base + to_point, bump->at - to_point);
+#endif
+ bump->at = to_point;
+ bump_allocator_validate(allocator);
+}
+
+internal Allocator*
+bump_allocator_create_(u64 page_size)
+{
+ u64 size_needed = sizeof(Allocator) + sizeof(Allocator_Bump);
+
+ u8* base = os_mem_reserve(size_needed);
+ base = os_mem_commit(base, size_needed);
+
+ Allocator* result = (Allocator*)base;
+ zero_struct(*result);
+
+ Allocator_Bump* bump = (Allocator_Bump*)base + sizeof(Allocator);
+ zero_struct(*bump);
+ bump->page_size = page_size;
+
+ result->alloc = bump_allocator_alloc;
+ result->realloc = bump_allocator_realloc;
+ result->clear = bump_allocator_clear;
+ result->destroy = bump_allocator_destroy;
+ result->allocator_data = (u8*)bump;
+
+ bump_allocator_validate(result);
+ return result;
+}
+
+internal Allocator*
+bump_allocator_create_reserve(u64 reserve_size)
+{
+ Allocator* result = bump_allocator_create_(OS_MEM_PAGE_SIZE);
+ Allocator_Bump* bump = (Allocator_Bump*)result->allocator_data;
+
+ u64 reserve_pages = round_size_to_os_page_multiple(reserve_size);
+ bump->base = os_mem_reserve(reserve_pages);
+ if (bump->base != 0) bump->size_reserved = reserve_pages;
+
+ bump_allocator_validate(result);
+ return result;
+}
+
+internal Allocator*
+bump_allocator_create_child(Allocator* parent, u64 init_size)
+{
+ Allocator* result = bump_allocator_create_(OS_MEM_PAGE_SIZE);
+ result->parent = parent;
+
+ Allocator_Bump* bump = (Allocator_Bump*)result->allocator_data;
+ zero_struct(*bump);
+ bump->base = allocator_alloc(result->parent, init_size);
+ if (bump->base != 0)
+ {
+ bump->size_reserved = init_size;
+ bump->size_committed = init_size;
+ }
+
+ bump_allocator_validate(result);
+ return result;
+}
+
+
+/////////////////////////////////////////
+// Scratch Allocator
+
+typedef struct Allocator_Scratch Allocator_Scratch;
+struct Allocator_Scratch
+{
+ Allocator* a;
+ u64 at_before;
+};
+
+internal Allocator_Scratch
+allocator_scratch_begin(Allocator* allocator)
+{
+ Allocator_Scratch result = {};
+ result.a = allocator;
+ Allocator_Bump* bump = (Allocator_Bump*)result.a->allocator_data;
+ result.at_before = bump->at;
+ return result;
+}
+
+internal void
+allocator_scratch_end(Allocator_Scratch* scratch)
+{
+ bump_allocator_rewind(scratch->a, scratch->at_before);
+ zero_struct(*scratch);
+}
+
+/////////////////////////////////////////
+// Paged Allocator
+
+typedef struct Allocator_Paged_Free_Region Allocator_Paged_Free_Region;
+struct Allocator_Paged_Free_Region
+{
+ u64 pages;
+ Allocator_Paged_Free_Region* prev;
+ Allocator_Paged_Free_Region* next;
+};
+
+typedef struct Allocator_Paged Allocator_Paged;
+struct Allocator_Paged
+{
+ Allocator_Bump bump;
+ Allocator_Paged_Free_Region* free_first;
+};
+
+internal u8*
+paged_allocator_alloc(Allocator* allocator, u64 size)
+{
+ // 1. Find the number of pages we need
+ // 2. Find a run of free pages that we can use
+ // If found,
+ // remove those pages from the run they are in
+ // return those pages of memory
+ // 3. Commit pages on the end
+
+ Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data;
+ if (paged->bump.page_size == 0) paged->bump.page_size = OS_MEM_PAGE_SIZE;
+
+ u64 rounded_size = round_size_to_page_multiple(size, paged->bump.page_size);
+ u64 pages_needed = rounded_size / paged->bump.page_size;
+
+ u8* result = 0;
+
+ // Find free pages
+ if (paged->free_first)
+ {
+ Allocator_Paged_Free_Region* found = 0;
+ for (Allocator_Paged_Free_Region* at = paged->free_first; at != 0; at = at->next)
+ {
+ // NOTE(PS): this set of conditions checks to see if is bigger than what
+ // we need. If it is, we also check to see if this is smaller than any
+ // region we've found before. And we abort the search if this region
+ // perfectly fits the size needed.
+ //
+ // This should make sure that we are always choosing the closest fit we
+ // can. I'm not sure this is the best strategy for dealing with fragmentation
+ // but its a decent first pass
+ if (at->pages >= pages_needed)
+ {
+ if (!found || (found->pages > at->pages))
+ {
+ found = at;
+ if (found->pages == pages_needed) break;
+ }
+ }
+ }
+
+ if (found)
+ {
+ result = (u8*)found;
+ if (found->pages > pages_needed)
+ {
+ Allocator_Paged_Free_Region* region_after = (Allocator_Paged_Free_Region*)(result + rounded_size);
+ if (found->prev != 0) found->prev->next = region_after;
+ region_after = found->next;
+ }
+ else
+ {
+ if (found->prev != 0) found->prev->next = found->next;
+ }
+ }
+ }
+
+ if (!result)
+ {
+ result = bump_allocator_alloc_inner(allocator, &paged->bump, size);
+ }
+
+ return result;
+}
+
+#define region_end(r,page_size) ((u8*)(r) + ((r)->pages * page_size))
+
+internal void
+paged_region_insert(
+ Allocator_Paged_Free_Region* before,
+ Allocator_Paged_Free_Region* new_region,
+ Allocator_Paged_Free_Region* after,
+ u64 page_size
+ ){
+ assert(after == 0 || before < after);
+ assert(before < new_region);
+ assert(after == 0 || new_region < after);
+ assert(new_region->prev == 0 && new_region->next == 0);
+
+ u8* before_end = region_end(before, page_size);
+ u8* new_region_end = region_end(new_region, page_size);
+
+ // Before
+ if (before_end == (u8*)new_region)
+ {
+ // merge the regions
+ before->pages += new_region->pages;
+ new_region = before;
+ assert(new_region_end == region_end(new_region, page_size));
+ }
+ else
+ {
+ assert(before_end < (u8*)new_region);
+ before->next = new_region;
+ new_region->prev = before;
+ }
+
+ // After
+ if (after != 0)
+ {
+ if (new_region_end == (u8*)after)
+ {
+ // merge the regions
+ new_region->pages += after->pages;
+ u8* a = region_end(after, page_size);
+ u8* b = region_end(new_region, page_size);
+ assert(a == b);
+ }
+ else
+ {
+ assert(new_region_end < (u8*)after);
+ new_region->next = after;
+ after->prev = new_region;
+ }
+ }
+}
+
+internal void
+paged_allocator_free(Allocator* allocator, u8* base, u64 size)
+{
+ // Figure out which page base is the base of, assert its the base
+ // figure out how many pages size represents.
+ // create a free range
+ // stick it in between contiguous free ranges
+ // if the ranges before or after meet this new one, merge them all
+
+ Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data;
+
+ u64 page_base_rel = (base - paged->bump.base);
+ assert((page_base_rel % paged->bump.page_size) == 0);
+ u64 page_index = page_base_rel / paged->bump.page_size;
+ u64 size_pages_mult = round_size_to_page_multiple(size, paged->bump.page_size);
+ assert((size_pages_mult % paged->bump.page_size) == 0);
+ u64 page_count = size_pages_mult / paged->bump.page_size;
+
+ Allocator_Paged_Free_Region* region = (Allocator_Paged_Free_Region*)base;
+ zero_struct(*region);
+ region->pages = page_count;
+
+ Allocator_Paged_Free_Region* prev = 0;
+ Allocator_Paged_Free_Region* next = 0;
+ for (Allocator_Paged_Free_Region* at = paged->free_first; at != 0; at = at->next)
+ {
+ if (at < region)
+ {
+ prev = at;
+ next = at->next;
+ if (next != 0)
+ {
+ assert(next > region);
+ assert((u8*)next >= ((u8*)region + size_pages_mult));
+ }
+ }
+ }
+
+ if (prev && next)
+ {
+ // found a region to insert into
+ paged_region_insert(prev, region, next, paged->bump.page_size);
+ }
+ else if (prev)
+ {
+ // got to the end and all were before the free region in memory
+ paged_region_insert(prev, region, 0, paged->bump.page_size);
+ }
+ else
+ {
+ // free list is empty
+ paged->free_first = region;
+ }
+}
+
+internal u8*
+paged_allocator_realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size)
+{
+ // TODO(PS):
+ // Process:
+ // 1. Figure out which page base starts on
+ // 2. Find if there is a free region after base that is big enough to house
+ // the new size
+ // 3. If there is a free region, pull the needed memory out of it
+ // 4. Otherwise, alloc new_size, copy base into it, and free base
+
+ // TODO(PS): you could do a simple version where you just always alloc, copy, free
+ return 0;
+}
+
+internal void
+paged_allocator_clear(Allocator* allocator)
+{
+ if (!allocator->allocator_data) return;
+ Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data;
+ paged->bump.at = 0;
+ paged->free_first = 0;
+}
+
+internal void
+paged_allocator_destroy(Allocator* allocator)
+{
+ Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data;
+ bump_allocator_destroy_(&paged->bump);
+ allocator_destroy_(allocator, sizeof(Allocator_Paged));
+}
+
+internal Allocator*
+paged_allocator_create_()
+{
+ u64 size_needed = sizeof(Allocator) + sizeof(Allocator_Bump);
+
+ u8* base = os_mem_reserve(size_needed);
+ base = os_mem_commit(base, size_needed);
+
+ Allocator* result = (Allocator*)base;
+ zero_struct(*result);
+
+ Allocator_Paged* paged = (Allocator_Paged*)base + sizeof(Allocator);
+ zero_struct(*paged);
+
+ result->alloc = paged_allocator_alloc;
+ result->free = paged_allocator_free;
+ result->realloc = paged_allocator_realloc;
+ result->clear = paged_allocator_clear;
+ result->destroy = paged_allocator_destroy;
+ result->allocator_data = (u8*)paged;
+
+ return result;
+}
+
+internal Allocator*
+paged_allocator_create_reserve(u64 reserve_size, u64 page_size)
+{
+ Allocator* result = paged_allocator_create_();
+ Allocator_Paged* paged = (Allocator_Paged*)result->allocator_data;
+
+ u64 reserve_pages = round_size_to_os_page_multiple(reserve_size);
+ paged->bump.page_size = page_size;
+ paged->bump.base = os_mem_reserve(reserve_pages);
+ if (paged->bump.base != 0) paged->bump.size_reserved = reserve_pages;
+
+ return result;
+}
+
+internal Allocator*
+paged_allocator_create_reserve_os_page_size(u64 reserve_size)
+{
+ u64 page_size = OS_MEM_PAGE_SIZE;
+ return paged_allocator_create_reserve(reserve_size, page_size);
+}
+
+internal Allocator*
+paged_allocator_create_child(Allocator* parent, u64 init_size)
+{
+ Allocator* result = bump_allocator_create_(OS_MEM_PAGE_SIZE);
+ result->parent = parent;
+
+ Allocator_Paged* paged = (Allocator_Paged*)result->allocator_data;
+ paged->bump.base = allocator_alloc(result->parent, init_size);
+ if (paged->bump.base != 0)
+ {
+ paged->bump.size_reserved = init_size;
+ paged->bump.size_committed = init_size;
+ }
+
+ return result;
+}
+
+#endif
\ No newline at end of file
diff --git a/src_v2/core/lumenarium_core_random.h b/src_v2/core/lumenarium_core_random.h
new file mode 100644
index 0000000..09bceac
--- /dev/null
+++ b/src_v2/core/lumenarium_core_random.h
@@ -0,0 +1,72 @@
+/* date = April 11th 2022 6:11 pm */
+
+#ifndef LUMENARIUM_RANDOM_H
+#define LUMENARIUM_RANDOM_H
+
+typedef struct Random_Series Random_Series;
+struct Random_Series
+{
+ u32 last_value;
+};
+
+internal Random_Series
+random_series_create(u32 seed)
+{
+ Random_Series result = {};
+ result.last_value = seed;
+ return result;
+}
+
+internal u32
+random_from_u32(u32 input)
+{
+ u32 result = input;
+ result ^= result << 13;
+ result ^= result >> 17;
+ result ^= result << 5;
+ return result;
+}
+
+internal r32
+random_unilateral_from_u32(u32 input)
+{
+ r32 result = random_from_u32(input) / (r32)(0xFFFFFFFF);
+ return result;
+}
+
+internal u32
+random_series_next(Random_Series* s)
+{
+ u32 result = random_from_u32(s->last_value);
+ s->last_value = result;
+ return result;
+}
+
+internal r32
+random_series_next_unilateral(Random_Series* s)
+{
+ r32 result = random_series_next(s) / (r32)(0xFFFFFFFF);
+ return result;
+}
+
+internal r32
+random_series_next_bilateral(Random_Series* s)
+{
+ r32 result = random_series_next(s) / (r32)(0xFFFFFFFF);
+ result = (result * 2.0f) - 1.0f;
+ return result;
+}
+
+internal v4
+random_series_next_v4(Random_Series* s)
+{
+ v4 result = {
+ .x = random_series_next_unilateral(s),
+ .y = random_series_next_unilateral(s),
+ .z = random_series_next_unilateral(s),
+ .w = 1,
+ };
+ return result;
+}
+
+#endif //LUMENARIUM_RANDOM_H
diff --git a/src_v2/core/lumenarium_core_socket.h b/src_v2/core/lumenarium_core_socket.h
new file mode 100644
index 0000000..7a38b45
--- /dev/null
+++ b/src_v2/core/lumenarium_core_socket.h
@@ -0,0 +1,124 @@
+#if !defined(LUMENARIUM_CORE_SOCKET_H)
+#define LUMENARIUM_CORE_SOCKET_H
+
+typedef struct Socket_Handle Socket_Handle;
+struct Socket_Handle { u64 value; };
+
+typedef s32 Socket_Error;
+enum {
+ SocketError_Invalid,
+ SocketError_NOERROR,
+ SocketError_EAGAIN,
+ SocketError_EBADF,
+ SocketError_ECONNREFUSED,
+ SocketError_EFAULT,
+ SocketError_EINTR,
+ SocketError_EINVAL,
+ SocketError_ENOMEM,
+ SocketError_ENOTCONN,
+ SocketError_ENOTSOCK,
+ SocketError_Count,
+};
+
+global char* socket_error_strings[] = {
+ "Invalid",
+ "SocketError_NOERROR",
+ "SocketError_EAGAIN",
+ "SocketError_EBADF",
+ "SocketError_ECONNREFUSED",
+ "SocketError_EFAULT",
+ "SocketError_EINTR",
+ "SocketError_EINVAL",
+ "SocketError_ENOMEM",
+ "SocketError_ENOTCONN",
+ "SocketError_ENOTSOCK",
+};
+
+u16 endian_swap_u16(u16 v);
+u32 endian_swap_u32(u32 v);
+u64 endian_swap_u64(u64 v);
+
+#define hton_u16(v) endian_swap_u16(v)
+#define hton_u32(v) endian_swap_u32(v)
+#define hton_u64(v) endian_swap_u64(v)
+#define ntoh_s16(v) endian_swap_u16(v)
+#define ntoh_s32(v) endian_swap_u32(v)
+#define ntoh_s64(v) endian_swap_u64(v)
+
+
+//////////////////////////////////////////
+// Implementation
+
+u16
+endian_swap_u16(u16 v)
+{
+ u8* p = (u8*)&v;
+ u16 result = (u16)(
+ (p[0] << 8) |
+ (p[1] << 0)
+ );
+ return result;
+}
+
+u32
+endian_swap_u32(u32 v)
+{
+ u8* p = (u8*)&v;
+ u32 result = (u32)(
+ (p[0] << 24) |
+ (p[1] << 16) |
+ (p[2] << 8) |
+ (p[3] << 0)
+ );
+ return result;
+}
+
+u64
+endian_swap_u64(u64 v)
+{
+ u8* p = (u8*)&v;
+ u64 result = (u64)(
+ ((u64)p[0] << 56) |
+ ((u64)p[1] << 48) |
+ ((u64)p[2] << 40) |
+ ((u64)p[3] << 32) |
+ ((u64)p[4] << 24) |
+ ((u64)p[5] << 16) |
+ ((u64)p[6] << 8) |
+ ((u64)p[7] << 0)
+ );
+ return result;
+}
+
+#if defined(DEBUG)
+
+void
+core_socket_tests()
+{
+ // u16 endian swap
+ u16 a_0 = 0xABCD;
+ u16 a_1 = endian_swap_u16(a_0);
+ u16 a_2 = endian_swap_u16(a_1);
+ assert(a_1 == 0xCDAB);
+ assert(a_2 == a_0);
+
+ // u32 endian swap
+ u32 b_0 = 0x89ABCDEF;
+ u32 b_1 = endian_swap_u32(b_0);
+ u32 b_2 = endian_swap_u32(b_1);
+ assert(b_1 == 0xEFCDAB89);
+ assert(b_2 == b_0);
+
+ // u64 endian swap
+ u64 c_0 = 0x7654321089ABCDEF;
+ u64 c_1 = endian_swap_u64(c_0);
+ u64 c_2 = endian_swap_u64(c_1);
+ assert(c_1 == 0xEFCDAB8910325476);
+ assert(c_2 == c_0);
+}
+
+#else
+# define core_socket_tests()
+#endif
+
+#endif // LUMENARIUM_CORE_SOCKET_H
\ No newline at end of file
diff --git a/src_v2/core/lumenarium_core_string.h b/src_v2/core/lumenarium_core_string.h
new file mode 100644
index 0000000..17fc27e
--- /dev/null
+++ b/src_v2/core/lumenarium_core_string.h
@@ -0,0 +1,319 @@
+//////////////////////////////////////////////
+// String
+
+internal u64 c_str_len(char* s);
+
+// NOTE(PS): even though this has a len and cap, it should always be
+// null terminated
+typedef struct String String;
+struct String
+{
+ u8* str;
+ u64 len;
+ u64 cap;
+};
+
+internal String string_create(u8* str, u64 len, u64 cap);
+internal u64 string_copy_to(String* dest, String src);
+
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+// IMPLEMENTATION
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+
+internal u64
+c_str_len(char* s)
+{
+ u64 result = 0;
+ for (; s[result] != 0; result++) {}
+ return result;
+}
+
+#define str_varg(s) (int)(s).len, (char*)(s).str
+#define str_expand(s) (char*)(s).str, (u64)(s).len
+#define lit_str(s) (String){ (u8*)(s), (u64)sizeof(s)-1, (u64)sizeof(s)-1 }
+
+internal String
+allocator_alloc_string(Allocator* a, u64 cap)
+{
+ String result = {};
+ result.str = allocator_alloc_array(a, u8, cap);
+ result.cap = cap;
+ return result;
+}
+
+/////////////////////////////////////
+// Char Operations
+
+bool
+char_is_space(u8 c)
+{
+ return c == ' ' || c == '\r' || c == '\t' || c == '\f' || c == '\v' || c == '\n';
+}
+
+u8
+char_to_upper(u8 c)
+{
+ return (c >= 'a' && c <= 'z') ? ('A' + (c - 'a')) : c;
+}
+
+u8
+char_to_lower(u8 c)
+{
+ return (c >= 'A' && c <= 'Z') ? ('a' + (c - 'A')) : c;
+}
+
+u8
+char_to_forward_slash(u8 c)
+{
+ return (c == '\\' ? '/' : c);
+}
+
+
+/////////////////////////////////////
+// String Operations
+//
+// Note that these don't actually modify any memory
+// just return structures that let you view it differently
+
+internal String
+string_create(u8* str, u64 len, u64 cap)
+{
+ String result = {};
+ result.str = str;
+ result.len = len;
+ result.cap = cap;
+ return result;
+}
+
+internal String
+string_substring(String s, u64 min, u64 max)
+{
+ if (max > s.len) max = s.len;
+ if (min > s.len) min = s.len;
+ if (min > max) {
+ u64 t = min;
+ min = max;
+ max = t;
+ }
+ String result = {};
+ result.str = s.str + min;
+ result.len = max - min;
+ result.cap = result.len;
+ return result;
+}
+
+internal String
+string_skip(String s, u64 min)
+{
+ return string_substring(s, min, s.len - min);
+}
+
+internal String
+string_chop(String s, u64 nmax)
+{
+ return string_substring(s, 0, s.len - nmax);
+}
+
+internal String
+string_get_prefix(String s, u64 max)
+{
+ return string_substring(s, 0, max);
+}
+
+internal String
+string_get_suffix(String s, u64 nmax)
+{
+ return string_substring(s, s.len - nmax, s.len);
+}
+
+typedef u32 String_Match_Flags;
+enum
+{
+ StringMatch_FindLast = 1,
+ StringMatch_CaseInsensitive = 2,
+ StringMatch_SlashInsensitive = 4,
+};
+
+internal bool
+string_match(String a, String b, String_Match_Flags flags)
+{
+ bool result = false;
+ if (a.len == b.len)
+ {
+ result = true;
+ for (u64 i = 0; i < a.len; i++)
+ {
+ bool match = a.str[i] == b.str[i];
+ if(flags & StringMatch_CaseInsensitive)
+ {
+ match |= (char_to_lower(a.str[i]) == char_to_lower(b.str[i]));
+ }
+ if(flags & StringMatch_SlashInsensitive)
+ {
+ match |= (char_to_forward_slash(a.str[i]) == char_to_forward_slash(b.str[i]));
+ }
+ if(match == 0)
+ {
+ result = 0;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+internal u64
+string_find_substring(String s, String substr, u64 start_pos, String_Match_Flags flags)
+{
+ bool found = false;
+ u64 found_i = s.len;
+ for (u64 i = start_pos; i < s.len; i++)
+ {
+ if (i + substr.len <= s.len)
+ {
+ String at = string_substring(s, i, i + substr.len);
+ if (string_match(at, substr, flags))
+ {
+ found = true;
+ found_i = i;
+ if (!(flags & StringMatch_FindLast)) break;
+ }
+ }
+ }
+ return found_i;
+}
+
+/////////////////////////////////////
+// Path Operations
+
+// good for removing extensions
+internal String
+string_chop_last_period(String s)
+{
+ u64 period_pos = string_find_substring(s, lit_str("."), 0, StringMatch_FindLast);
+ if(period_pos < s.len)
+ {
+ s.len = period_pos;
+ s.cap = s.len;
+ }
+ return s;
+}
+
+// get the filename
+internal String
+string_skip_last_slash(String s)
+{
+ u64 slash_pos = string_find_substring(s, lit_str("/"), 0, StringMatch_FindLast | StringMatch_SlashInsensitive);
+ if(slash_pos < s.len)
+ {
+ s.str += slash_pos + 1;
+ s.len -= slash_pos + 1;
+ s.cap = s.len;
+ }
+ return s;
+}
+
+// get the extension
+internal String
+string_skip_last_period(String s)
+{
+ u64 period_pos = string_find_substring(s, lit_str("."), 0, StringMatch_FindLast);
+ if(period_pos < s.len)
+ {
+ s.str += period_pos + 1;
+ s.len -= period_pos + 1;
+ s.cap = s.len;
+ }
+ return s;
+}
+
+// good for getting the path to a file
+internal String
+string_chop_last_slash(String s)
+{
+ u64 slash_pos = string_find_substring(s, lit_str("/"), 0, StringMatch_FindLast | StringMatch_SlashInsensitive);
+ if(slash_pos < s.len)
+ {
+ s.len = slash_pos;
+ s.cap = s.len;
+ }
+ return s;
+}
+
+
+/////////////////////////////////////
+// String Modifications
+
+internal u64
+string_copy_to(String* dest, String src)
+{
+ u64 len_to_copy = dest->cap < src.len ? dest->cap : src.len;
+ memory_copy(src.str, dest->str, len_to_copy);
+ u64 null_term_index = len_to_copy;
+ if (null_term_index >= dest->cap) null_term_index -= 1;
+ dest->str[null_term_index] = 0;
+ dest->len = null_term_index;
+ return null_term_index;
+}
+
+internal String
+string_copy(String s, Allocator* a)
+{
+ u64 size = s.cap;
+ if (s.str[s.cap] != 0) size += 1;
+ String result = allocator_alloc_string(a, size);
+ string_copy_to(&result, s);
+ return result;
+}
+
+internal String
+string_fv(Allocator* a, char* fmt, va_list args)
+{
+ va_list args1;
+ va_copy(args1, args);
+ s32 needed = stbsp_vsnprintf(0, 0, fmt, args);
+ String result = allocator_alloc_string(a, needed + 1);
+ result.len = stbsp_vsnprintf((char*)result.str, (int)result.cap, fmt, args1);
+ result.str[result.len] = 0;
+ va_end(args1);
+ return result;
+}
+
+internal String
+string_f(Allocator* a, char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ String result = string_fv(a, fmt, args);
+ va_end(args);
+ return result;
+}
+
+internal void
+dw_put_str(Data_Writer* w, String str)
+{
+ for (u64 i = 0; i < str.len; i++)
+ {
+ dw_put_u8(w, str.str[i]);
+ }
+}
+
+internal void
+dw_put_str_min_len(Data_Writer* w, String str, u64 min_len)
+{
+ u64 start = w->at;
+ dw_put_str(w, str);
+ if (str.len < min_len)
+ {
+ w->at = start + min_len;
+ }
+}
+
+internal void
+dw_put_str_min_len_nullterm(Data_Writer* w, String str, u64 min_len)
+{
+ dw_put_str_min_len(w, str, min_len);
+ w->data.base[w->at - 1] = '\0';
+}
\ No newline at end of file
diff --git a/src_v2/core/lumenarium_core_threads.h b/src_v2/core/lumenarium_core_threads.h
new file mode 100644
index 0000000..48848ba
--- /dev/null
+++ b/src_v2/core/lumenarium_core_threads.h
@@ -0,0 +1,27 @@
+#if !defined(LUMENARIUM_CORE_THREADS_H)
+#define LUMENARIUM_CORE_THREADS_H
+
+typedef struct Thread_Handle Thread_Handle;
+struct Thread_Handle { u64 value; };
+
+typedef struct Thread_Result Thread_Result;
+struct Thread_Result { u32 code; };
+
+// TODO(PS): In the interest of time while getting Incenter
+// up and running, you made Thread_Proc take a u8* rather than
+// the whole Thread_Data structure, whcih currently isnt' being
+// used at all
+typedef struct Thread_Data Thread_Data;
+typedef Thread_Result Thread_Proc(u8* data);
+struct Thread_Data
+{
+ Thread_Handle thread_handle;
+ u32 thread_id;
+ Thread_Proc* thread_proc;
+ Allocator* thread_memory;
+ u8* user_data;
+};
+
+
+
+#endif // LUMENARIUM_CORE_THREADS_H
\ No newline at end of file
diff --git a/src_v2/core/lumenarium_core_time.h b/src_v2/core/lumenarium_core_time.h
new file mode 100644
index 0000000..27e57d0
--- /dev/null
+++ b/src_v2/core/lumenarium_core_time.h
@@ -0,0 +1,44 @@
+#if !defined(LUMENARIUM_CORE_TIME_H)
+#define LUMENARIUM_CORE_TIME_H
+
+// Ticks is a container that each os will use differently
+// the value of tick should never be accessed outside of
+// lumenarium_core_time.h or the various os_time implementations.
+// Instead, treat it as an opaque struct and use the values returned
+// from the functions defined below.
+typedef struct { s64 value; } Ticks;
+
+internal Ticks get_ticks_elapsed(Ticks start, Ticks end);
+internal r64 ticks_to_seconds(Ticks t, r64 ticks_per_second);
+internal r64 get_seconds_elapsed(Ticks start, Ticks end, r64 ticks_per_second);
+
+///////////////////////////////////////
+///////////////////////////////////////
+// Implementation
+///////////////////////////////////////
+///////////////////////////////////////
+
+internal Ticks
+get_ticks_elapsed(Ticks start, Ticks end)
+{
+ Ticks result = {};
+ result.value = end.value - start.value;
+ return result;
+}
+
+internal r64
+ticks_to_seconds(Ticks t, r64 ticks_per_second)
+{
+ r64 result = t.value / ticks_per_second;
+ return result;
+}
+
+internal r64
+get_seconds_elapsed(Ticks start, Ticks end, r64 ticks_per_second)
+{
+ Ticks diff = get_ticks_elapsed(start, end);
+ return ticks_to_seconds(diff, ticks_per_second);
+}
+
+
+#endif // LUMENARIUM_CORE_TIME_H
\ No newline at end of file
diff --git a/src_v2/core/lumenarium_core_types.h b/src_v2/core/lumenarium_core_types.h
new file mode 100644
index 0000000..44d60ac
--- /dev/null
+++ b/src_v2/core/lumenarium_core_types.h
@@ -0,0 +1,231 @@
+#ifndef LUMENARIUM_CORE_TYPES_H
+#define LUMENARIUM_CORE_TYPES_H
+
+//////////////////////////////////////////////
+// Explicit Names for static keyword
+#define internal static
+#define local_persist static
+#define global static
+#define local_const static const
+#define global_const static const
+#define external extern "C"
+
+#define STMT(x) do {(x);}while(false)
+
+//////////////////////////////////////////////
+// Integer Sizing
+#if defined(GUESS_INTS)
+
+typedef signed char s8;
+typedef signed short s16;
+typedef signed int s32;
+typedef signed long long s64;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+#else // !defined(GUESS_INTS)
+
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+#endif // defined(GUESS_INTS)
+
+//////////////////////////////////////////////
+// Basic Types
+
+typedef s8 b8;
+typedef s32 b32;
+typedef s64 b64;
+
+typedef float r32;
+typedef double r64;
+
+//////////////////////////////////////////////
+// Basic Type Constants
+
+#define u8_max 0xFF
+#define u16_max 0xFFFF
+#define u32_max 0xFFFFFFFF
+#define u64_max 0xFFFFFFFFFFFFFFFF
+
+#define s8_max 127
+#define s16_max 32767
+#define s32_max 2147483647
+#define s64_max 9223372036854775807
+
+#define s8_min -127 - 1
+#define s16_min -32767 - 1
+#define s32_min -2147483647 - 1
+#define s64_min -9223372036854775807 - 1
+
+#define r32_max 3.402823466e+38f
+#define r32_min -3.402823466e+38f
+#define r32_smallest_positive 1.1754943508e-38f
+#define r32_epsilon 5.96046448e-8f
+#define r32_pi 3.14159265359f
+#define r32_half_pi 1.5707963267f
+#define r32_tau 6.28318530717f
+
+#define r64_max 1.79769313486231e+308
+#define r64_min -1.79769313486231e+308
+#define r64_smallest_positive 4.94065645841247e-324
+#define r64_epsilon 1.11022302462515650e-16
+#define r64_pi 3.14159265359
+#define r64_half_pi 1.5707963267
+#define r64_tau 6.28318530717
+
+#define NanosToSeconds 1 / 10000000.0
+#define SecondsToNanos 10000000.0
+
+//////////////////////////////////////////////
+// Math
+
+#ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef min
+# define min(a,b) ((a) > (b) ? (b) : (a))
+#endif
+
+#define lerp(a,t,b) (a) + ((b - a) * (t))
+#define clamp(r0,v,r1) min((r1),max((r0),(v)))
+#define lerp_clamp(a,t,b) clamp((a),lerp((a),(t),(b)),(b))
+
+internal r32
+remap_r32(r32 v, r32 old_min, r32 old_max, r32 new_min, r32 new_max)
+{
+ r32 result = (v - old_min) / (old_max - old_min);
+ result = (result * (new_max - new_min)) + new_min;
+ return result;
+}
+
+internal r64
+remap_r64(r64 v, r64 old_min, r64 old_max, r64 new_min, r64 new_max)
+{
+ r64 result = (v - old_min) / (old_max - old_min);
+ result = (result * (new_max - new_min)) + new_min;
+ return result;
+}
+
+internal u32
+round_up_to_pow2_u32(u32 v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+
+//////////////////////////////////////////////
+// Flags
+
+#define has_flag(data, flag) (((data) & (flag)) != 0)
+#define has_flag_exact(data, flag) (((data) & (flag)) == (flag))
+#define add_flag(data, flag) STMT((data) |= (flag))
+#define rem_flag(data, flag) STMT((data) &= (~(flag)))
+
+//////////////////////////////////////////////
+// List Helper Macros
+
+#define sll_push(first,last,ele) \
+if (!(first)) { (first) = (ele); }\
+else { (last)->next = (ele); } \
+(last) = (ele); (ele)->next = 0;
+
+// TODO(PS): Stack, Queue, DLL ops
+
+//////////////////////////////////////////////
+// Hash Table
+//
+// Rather than define a data structure, to allow the most flexibility,
+// this is just a set of functions that can be integrated into other
+// routines.
+// In general, they expect you to track a u32* of ids and a u32 capacity
+
+internal void
+hash_table_init(u32* ids, u32 cap)
+{
+ for (u32 i = 0; i < cap; i++) ids[i] = 0;
+}
+
+internal u32
+hash_table_find_(u32* ids, u32 cap, u32 start_id, u32 target_value)
+{
+ u32 index = start_id % cap;
+ u32 start = index;
+ do {
+ if (ids[index] == target_value) break;
+ index = (index + 1) % cap;
+ } while (index != start);
+ return index;
+}
+
+internal u32
+hash_table_register(u32* ids, u32 cap, u32 new_id)
+{
+ u32 index = hash_table_find_(ids, cap, new_id, 0);
+ if (ids[index] != 0) return cap;
+ ids[index] = new_id;
+ return index;
+}
+
+internal u32
+hash_table_find(u32* ids, u32 cap, u32 value)
+{
+ u32 result = hash_table_find_(ids, cap, value, value);
+ if (ids[result] != value) return cap;
+ return result;
+}
+
+//////////////////////////////////////////////
+// Predeclaring Memory Functions
+
+u64 round_size_to_page_multiple(u64 size, u64 page_size);
+u64 round_size_to_os_page_multiple(u64 size);
+
+//////////////////////////////////////////////
+// Vector Extensions
+
+#if defined(HANDMADE_MATH_IMPLEMENTATION)
+
+#define v2_to_v3(xy,z) (v3){(xy).x, (xy).y, (z)}
+#define v2_floor(v) (v2){ floorf(v.x), floorf(v.y) }
+#define v3_floor(v) (v3){ floorf(v.x), floorf(v.y), floorf(v.z) }
+
+internal bool
+rect2_contains(v2 min, v2 max, v2 point)
+{
+ return (
+ min.x <= point.x && min.y <= point.y &&
+ max.x >= point.x && max.y >= point.y
+ );
+}
+
+#endif // defined(HANDMADE_MATH_IMPLEMENTATION)
+
+//////////////////////////////////////////////
+// Color Constants
+
+#define WHITE_V4 (v4){1,1,1,1}
+#define BLACK_V4 (v4){0,0,0,1}
+#define RED_V4 (v4){1,0,0,1}
+#define GREEN_V4 (v4){0,1,0,1}
+#define BLUE_V4 (v4){0,0,1,1}
+#define YELLOW_V4 (v4){1,1,0,1}
+#define TEAL_V4 (v4){0,1,1,1}
+#define PINK_V4 (v4){1,0,1,1}
+
+#endif
\ No newline at end of file
diff --git a/src_v2/core/lumenarium_core_window.h b/src_v2/core/lumenarium_core_window.h
new file mode 100644
index 0000000..ae2e6a7
--- /dev/null
+++ b/src_v2/core/lumenarium_core_window.h
@@ -0,0 +1,244 @@
+#if !defined(LUMENARIUM_CORE_WINDOW_H)
+#define LUMENARIUM_CORE_WINDOW_H
+
+typedef u32 Window_Event_Kind;
+enum
+{
+ WindowEvent_Invalid = 0,
+
+ WindowEvent_MouseScroll,
+ WindowEvent_MouseMoved,
+ WindowEvent_ButtonDown,
+ WindowEvent_ButtonUp,
+ WindowEvent_Char,
+ WindowEvent_WindowClosed,
+
+ WindowEvent_Count,
+};
+
+typedef u32 Key_Code;
+enum
+{
+ KeyCode_Invalid = 0,
+
+ KeyCode_Esc,
+
+ KeyCode_Space,
+ KeyCode_Tab,
+ KeyCode_CapsLock,
+ KeyCode_LeftShift, KeyCode_RightShift,
+ KeyCode_LeftCtrl, KeyCode_RightCtrl,
+ KeyCode_Fn,
+ KeyCode_Alt,
+ KeyCode_PageUp, KeyCode_PageDown,
+ KeyCode_Backspace, KeyCode_Delete,
+ KeyCode_Enter,
+
+ // Function Keys
+ KeyCode_F0, KeyCode_F1, KeyCode_F2, KeyCode_F3, KeyCode_F4, KeyCode_F5, KeyCode_F6, KeyCode_F7,
+ KeyCode_F8, KeyCode_F9, KeyCode_F10, KeyCode_F11, KeyCode_F12,
+
+ // Letters
+ KeyCode_a, KeyCode_b, KeyCode_c, KeyCode_d, KeyCode_e, KeyCode_f, KeyCode_g, KeyCode_h,
+ KeyCode_i, KeyCode_j, KeyCode_k, KeyCode_l, KeyCode_m, KeyCode_n, KeyCode_o, KeyCode_p,
+ KeyCode_q, KeyCode_r, KeyCode_s, KeyCode_t, KeyCode_u, KeyCode_v, KeyCode_w, KeyCode_x,
+ KeyCode_y, KeyCode_z,
+
+ KeyCode_A, KeyCode_B, KeyCode_C, KeyCode_D, KeyCode_E, KeyCode_F, KeyCode_G, KeyCode_H,
+ KeyCode_I, KeyCode_J, KeyCode_K, KeyCode_L, KeyCode_M, KeyCode_N, KeyCode_O, KeyCode_P,
+ KeyCode_Q, KeyCode_R, KeyCode_S, KeyCode_T, KeyCode_U, KeyCode_V, KeyCode_W, KeyCode_X,
+ KeyCode_Y, KeyCode_Z,
+
+ // Numbers
+ KeyCode_0, KeyCode_1, KeyCode_2, KeyCode_3, KeyCode_4, KeyCode_5, KeyCode_6, KeyCode_7,
+ KeyCode_8, KeyCode_9,
+
+ KeyCode_Num0, KeyCode_Num1, KeyCode_Num2, KeyCode_Num3, KeyCode_Num4, KeyCode_Num5,
+ KeyCode_Num6, KeyCode_Num7, KeyCode_Num8, KeyCode_Num9,
+
+ // Symbols
+ KeyCode_Bang, KeyCode_At, KeyCode_Pound, KeyCode_Dollar, KeyCode_Percent, KeyCode_Carrot,
+ KeyCode_Ampersand, KeyCode_Star, KeyCode_LeftParen, KeyCode_RightParen, KeyCode_Minus, KeyCode_Plus,
+ KeyCode_Equals, KeyCode_Underscore, KeyCode_LeftBrace, KeyCode_RightBrace, KeyCode_LeftBracket,
+ KeyCode_RightBracket, KeyCode_Colon, KeyCode_SemiColon, KeyCode_SingleQuote, KeyCode_DoubleQuote,
+ KeyCode_ForwardSlash, KeyCode_Backslash, KeyCode_Pipe, KeyCode_Comma, KeyCode_Period,
+ KeyCode_QuestionMark, KeyCode_LessThan, KeyCode_GreaterThan, KeyCode_Tilde, KeyCode_BackQuote,
+
+ // Arrows
+ KeyCode_UpArrow,
+ KeyCode_DownArrow,
+ KeyCode_LeftArrow,
+ KeyCode_RightArrow,
+
+ // Mouse
+ // NOTE(Peter): Including this here so we can utilize the same KeyDown, KeyUp etc. functions
+ KeyCode_MouseLeftButton,
+ KeyCode_MouseMiddleButton,
+ KeyCode_MouseRightButton,
+
+ KeyCode_Count,
+};
+
+typedef u8 Key_Flags;
+enum
+{
+ KeyFlag_None = 0,
+
+ KeyFlag_Mod_Shift = 1,
+ KeyFlag_Mod_Ctrl = 2,
+ KeyFlag_Mod_Alt = 4,
+ KeyFlag_Mod_Sys = 8,
+
+ KeyFlag_State_WasDown = 16,
+ KeyFlag_State_IsDown = 32,
+};
+
+typedef struct Window_Event Window_Event;
+struct Window_Event
+{
+ Window_Event_Kind kind;
+ Key_Code key_code;
+ Key_Flags key_flags;
+ s32 mouse_x;
+ s32 mouse_y;
+ s32 scroll_amt;
+ char char_value;
+};
+
+typedef u32 Cursor_Kind;
+enum
+{
+ Cursor_Arrow,
+ Cursor_Pointer,
+ Cursor_Loading,
+ Cursor_HArrows,
+ Cursor_VArrows,
+ Cursor_DTopLeftArrows,
+ Cursor_DTopRightArrows,
+ Cursor_Count,
+};
+
+#define INPUT_FRAME_STRING_LENGTH 32
+typedef struct Input_Frame Input_Frame;
+struct Input_Frame
+{
+ Key_Flags key_flags[KeyCode_Count];
+
+ char string_input[INPUT_FRAME_STRING_LENGTH];
+ u32 string_input_len;
+
+ v2 mouse_pos;
+ s32 mouse_scroll;
+};
+
+typedef struct Input_State Input_State;
+struct Input_State
+{
+ Input_Frame frames[2];
+ Input_Frame* frame_hot;
+ Input_Frame* frame_cold;
+
+ // cross frame state tracking
+ v2 mouse_pos_delta;
+ v2 mouse_pos_down;
+};
+
+#define key_was_down(key_flags) has_flag((key_flags), KeyFlag_State_WasDown)
+#define key_is_down(key_flags) has_flag((key_flags), KeyFlag_State_IsDown)
+#define key_was_up(key_flags) (!has_flag((key_flags), KeyFlag_State_WasDown))
+#define key_is_up(key_flags) (!has_flag((key_flags), KeyFlag_State_IsDown))
+
+internal Input_State* input_state_create(Allocator* a);
+internal Key_Flags input_key_advance(Key_Flags flag);
+internal void input_state_swap_frames(Input_State* input_state);
+internal bool input_key_is_down(Input_State* input_state, Key_Code key);
+internal bool input_key_went_down(Input_State* input_state, Key_Code key);
+internal bool input_key_is_up(Input_State* input_state, Key_Code key);
+internal bool input_key_went_up(Input_State* input_state, Key_Code key);
+
+//////////////////////////////////////////
+//////////////////////////////////////////
+// IMPLEMENTATION
+//////////////////////////////////////////
+//////////////////////////////////////////
+
+internal Input_State*
+input_state_create(Allocator* a)
+{
+ Input_State* result = allocator_alloc_struct(a, Input_State);
+ result->frame_hot = result->frames + 0;
+ result->frame_cold = result->frames + 1;
+
+ // Clear the new hot input frame
+ Key_Flags* hot_key_flags = result->frame_hot->key_flags;
+ Key_Flags* cold_key_flags = result->frame_cold->key_flags;
+ for (u32 i = 0; i < KeyCode_Count; i++)
+ {
+ hot_key_flags[i] = 0;
+ cold_key_flags[i] = 0;
+ }
+ return result;
+}
+
+internal Key_Flags
+input_key_advance(Key_Flags flag)
+{
+ Key_Flags result = flag;
+ if (key_is_down(flag))
+ {
+ add_flag(result, KeyFlag_State_WasDown);
+ }
+ if (key_is_up(flag))
+ {
+ rem_flag(result, KeyFlag_State_WasDown);
+ }
+ return result;
+}
+
+internal void
+input_state_swap_frames(Input_State* input_state)
+{
+ Input_Frame* next_hot = input_state->frame_cold;
+ input_state->frame_cold = input_state->frame_hot;
+ input_state->frame_hot = next_hot;
+
+ // Clear the new hot input frame
+ Key_Flags* hot_key_flags = input_state->frame_hot->key_flags;
+ Key_Flags* cold_key_flags = input_state->frame_cold->key_flags;
+ for (u32 i = 0; i < KeyCode_Count; i++)
+ {
+ hot_key_flags[i] = input_key_advance(cold_key_flags[i]);
+ }
+ input_state->frame_hot->string_input_len = 0;
+}
+
+// Key State Queries
+
+internal bool
+input_key_is_down(Input_State* input_state, Key_Code key)
+{
+ Key_Flags flags = input_state->frame_hot->key_flags[key];
+ return key_is_down(flags);
+}
+
+internal bool
+input_key_went_down(Input_State* input_state, Key_Code key)
+{
+ Key_Flags flags = input_state->frame_hot->key_flags[key];
+ return key_is_down(flags) && !key_was_down(flags);
+}
+
+internal bool
+input_key_is_up(Input_State* input_state, Key_Code key)
+{
+ Key_Flags flags = input_state->frame_hot->key_flags[key];
+ return !key_is_down(flags);
+}
+
+internal bool
+input_key_went_up(Input_State* input_state, Key_Code key)
+{
+ Key_Flags flags = input_state->frame_hot->key_flags[key];
+ return !key_is_down(flags) && key_was_down(flags);
+}
+#endif // LUMENARIUM_CORE_WINDOW_H
\ No newline at end of file
diff --git a/src_v2/editor/graphics/lumenarium_editor_graphics.h b/src_v2/editor/graphics/lumenarium_editor_graphics.h
new file mode 100644
index 0000000..a651995
--- /dev/null
+++ b/src_v2/editor/graphics/lumenarium_editor_graphics.h
@@ -0,0 +1,456 @@
+#if !defined(LUMENARIUM_EDITOR_GRAPHICS_H)
+#define LUMENARIUM_EDITOR_GRAPHICS_H
+
+#define os_gl_no_error() os_gl_no_error_((char*)__FILE__, (u32)__LINE__)
+void os_gl_no_error_(char* file, u32 line);
+
+#define GL_NULL (u32)0
+
+#define SHADER_MAX_ATTRS 8
+#define SHADER_ATTR_LAST (u32)(1 << 31)
+typedef struct Shader Shader;
+struct Shader
+{
+ u32 id;
+ u32 attrs[SHADER_MAX_ATTRS];
+ u32 uniforms[SHADER_MAX_ATTRS];
+};
+
+typedef struct XPlatform_Shader_Program_Src XPlatform_Shader_Program_Src;
+struct XPlatform_Shader_Program_Src
+{
+ String win32_vert;
+ String win32_frag;
+
+ String osx_vert;
+ String osx_frag;
+
+ String wasm_vert;
+ String wasm_frag;
+};
+
+String xplatform_shader_program_get_vert(XPlatform_Shader_Program_Src src);
+String xplatform_shader_program_get_frag(XPlatform_Shader_Program_Src src);
+
+typedef struct Geometry_Buffer Geometry_Buffer;
+struct Geometry_Buffer
+{
+ u32 buffer_id_vao;
+ u32 buffer_id_vertices;
+ u32 buffer_id_indices;
+ u32 vertices_len;
+ u32 indices_len;
+};
+
+typedef struct Texture_Desc Texture_Desc;
+struct Texture_Desc
+{
+ u32 w, h, s;
+ u32 mag_filter, min_filter, fmt_internal, fmt_data;
+};
+
+typedef struct Texture Texture;
+struct Texture
+{
+ u32 id;
+ Texture_Desc desc;
+};
+
+typedef struct Graphics_Frame_Desc Graphics_Frame_Desc;
+struct Graphics_Frame_Desc
+{
+ v4 clear_color;
+ v2 viewport_min;
+ v2 viewport_max;
+};
+
+void frame_begin(Graphics_Frame_Desc desc);
+void frame_clear();
+
+// Geometry
+Geometry_Buffer geometry_buffer_create(r32* vertices, u32 vertices_len, u32* indices, u32 indices_len);
+Shader shader_create(String code_vert, String code_frag, String* attribs, u32 attribs_len, String* uniforms, u32 uniforms_len);
+void geometry_buffer_update(Geometry_Buffer* buffer, r32* verts, u32 verts_offset, u32 verts_len, u32* indices, u32 indices_offset, u32 indices_len);
+
+Geometry_Buffer unit_quad_create();
+
+// Shaders
+void geometry_bind(Geometry_Buffer geo);
+void shader_bind(Shader shader);
+void geometry_drawi(Geometry_Buffer geo, u32 indices, u32 offset);
+void geometry_draw(Geometry_Buffer geo);
+void vertex_attrib_pointer(Geometry_Buffer geo, Shader shader, u32 count, u32 attr_index, u32 stride, u32 offset);
+void set_uniform(Shader shader, u32 index, m44 u);
+
+// Textures
+Texture texture_create(Texture_Desc desc, u8* pixels);
+void texture_bind(Texture tex);
+void texture_update(Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride);
+
+//////////////////////////////////////////
+//////////////////////////////////////////
+// IMPLEMENTATION
+//////////////////////////////////////////
+//////////////////////////////////////////
+
+Geometry_Buffer
+geometry_buffer_create(
+ r32* vertices, u32 vertices_len,
+ u32* indices, u32 indices_len
+){
+ Geometry_Buffer result = {};
+
+ gl.glGenVertexArrays(1, &result.buffer_id_vao);
+ os_gl_no_error();
+
+ GLuint buffers[2];
+ gl.glGenBuffers(2, (GLuint*)buffers);
+ os_gl_no_error();
+
+ result.buffer_id_vertices = buffers[0];
+ result.buffer_id_indices = buffers[1];
+
+ // Vertices
+ gl.glBindVertexArray(result.buffer_id_vao);
+ gl.glBindBuffer(GL_ARRAY_BUFFER, result.buffer_id_vertices);
+ os_gl_no_error();
+
+ gl.glBufferData(GL_ARRAY_BUFFER, sizeof(r32) * vertices_len, vertices, GL_STATIC_DRAW);
+ os_gl_no_error();
+ result.vertices_len = vertices_len;
+
+ // Indices
+ gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffer_id_indices);
+ os_gl_no_error();
+
+ gl.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(u32) * indices_len, indices, GL_STATIC_DRAW);
+ os_gl_no_error();
+ result.indices_len = indices_len;
+
+ gl.glBindBuffer(GL_ARRAY_BUFFER, GL_NULL);
+ gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_NULL);
+
+ return result;
+}
+
+void
+geometry_buffer_update(
+ Geometry_Buffer* buffer,
+ r32* verts,
+ u32 verts_offset,
+ u32 verts_len,
+ u32* indices,
+ u32 indices_offset,
+ u32 indices_len
+){
+ gl.glBindVertexArray(buffer->buffer_id_vao);
+ gl.glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_id_vertices);
+ os_gl_no_error();
+
+ if (verts_len > buffer->vertices_len)
+ {
+ // NOTE(PS): this is because we're going to delete the old buffer and
+ // create a new one. In order to do that and not lose data, the update
+ // function needs to have been passed all the buffer's data
+ assert(verts_offset == 0);
+ gl.glBufferData(GL_ARRAY_BUFFER, verts_len * sizeof(r32), (void*)verts, GL_STATIC_DRAW);
+ }
+ else
+ {
+ gl.glBufferSubData(GL_ARRAY_BUFFER, verts_offset * sizeof(r32), verts_len * sizeof(r32), (void*)verts);
+ }
+ os_gl_no_error();
+
+ gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->buffer_id_indices);
+ os_gl_no_error();
+ if (indices_len > buffer->indices_len)
+ {
+ // NOTE(PS): this is because we're going to delete the old buffer and
+ // create a new one. In order to do that and not lose data, the update
+ // function needs to have been passed all the buffer's data
+ assert(indices_offset == 0);
+ gl.glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_len * sizeof(u32), (void*)indices, GL_STATIC_DRAW);
+ }
+ else
+ {
+ gl.glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indices_offset * sizeof(u32), indices_len * sizeof(u32), (void*)indices);
+ }
+ os_gl_no_error();
+}
+
+Geometry_Buffer
+unit_quad_create()
+{
+ r32 z = 0;
+ r32 r = 1;
+ r32 verts[] = {
+ // pos uv
+ -r, -r, z, 0, 0,
+ r, -r, z, 1, 0,
+ r, r, z, 1, 1,
+ -1, r, z, 0, 1,
+ };
+ u32 indices[] = { 0, 1, 2, 0, 2, 3 };
+ return geometry_buffer_create(verts, sizeof(verts) / sizeof(r32), indices, 6);
+}
+
+Shader
+shader_create(String code_vert, String code_frag, String* attrs, u32 attrs_len, String* uniforms, u32 uniforms_len){
+ Shader result = {};
+
+ GLuint shader_vert = gl.glCreateShader(GL_VERTEX_SHADER);
+ s32* code_vert_len = (s32*)&code_vert.len;
+ gl.glShaderSource(shader_vert, 1, (const char**)&code_vert.str, code_vert_len);
+ gl.glCompileShader(shader_vert);
+ { // errors
+ GLint shader_vert_compiled;
+ gl.glGetShaderiv(shader_vert, GL_COMPILE_STATUS, &shader_vert_compiled);
+ if (!shader_vert_compiled)
+ {
+ GLsizei log_length = 0;
+ GLchar message[1024];
+ gl.glGetShaderInfoLog(shader_vert, 1024, &log_length, message);
+ printf("GLSL Error: %s\n", message);
+ invalid_code_path;
+ }
+ }
+
+ GLuint shader_frag = gl.glCreateShader(GL_FRAGMENT_SHADER);
+ s32* code_frag_len = (s32*)&code_frag.len;
+ gl.glShaderSource(shader_frag, 1, (const char**)&code_frag.str, code_frag_len);
+ gl.glCompileShader(shader_frag);
+ { // errors
+ GLint shader_frag_compiled;
+ gl.glGetShaderiv(shader_frag, GL_COMPILE_STATUS, &shader_frag_compiled);
+ if (!shader_frag_compiled)
+ {
+ GLsizei log_length = 0;
+ GLchar message[1024];
+ gl.glGetShaderInfoLog(shader_frag, 1024, &log_length, message);
+ printf("GLSL Error: %s\n", message);
+ printf("%.*s\n", str_varg(code_frag));
+ invalid_code_path;
+ }
+ }
+
+ result.id = (u32)gl.glCreateProgram();
+ gl.glAttachShader(result.id, shader_vert);
+ gl.glAttachShader(result.id, shader_frag);
+ gl.glLinkProgram(result.id);
+
+ GLint program_linked;
+ gl.glGetProgramiv(result.id, GL_LINK_STATUS, &program_linked);
+ if (program_linked != GL_TRUE)
+ {
+ GLsizei log_length = 0;
+ GLchar message[1024];
+ gl.glGetProgramInfoLog(result.id, 1024, &log_length, message);
+ printf("GLSL Error: %s\n", message);
+ invalid_code_path;
+ }
+
+ gl.glUseProgram(result.id);
+
+ // TODO(PS): delete the vert and frag programs
+
+ assert(attrs_len < SHADER_MAX_ATTRS);
+ for (u32 i = 0; i < attrs_len; i++)
+ {
+ result.attrs[i] = gl.glGetAttribLocation(result.id, (char*)attrs[i].str);
+ os_gl_no_error();
+ }
+ result.attrs[attrs_len] = SHADER_ATTR_LAST;
+
+ assert(uniforms_len < SHADER_MAX_ATTRS);
+ for (GLuint i = 0; i < uniforms_len; i++)
+ {
+ s32 len = (s32)uniforms[i].len;
+ result.uniforms[i] = gl.glGetUniformLocation(result.id, (char*)uniforms[i].str);
+ }
+ result.uniforms[uniforms_len] = SHADER_ATTR_LAST;
+
+ return result;
+}
+
+void
+set_uniform(Shader shader, u32 index, m44 u)
+{
+ gl.glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, (r32*)u.Elements);
+}
+
+void
+geometry_bind(Geometry_Buffer geo)
+{
+ gl.glBindVertexArray(geo.buffer_id_vao);
+ os_gl_no_error();
+
+ gl.glBindBuffer(GL_ARRAY_BUFFER, geo.buffer_id_vertices);
+ os_gl_no_error();
+
+ gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geo.buffer_id_indices);
+ os_gl_no_error();
+}
+
+void
+shader_bind(Shader shader)
+{
+ gl.glUseProgram(shader.id);
+ os_gl_no_error();
+ for (u32 i = 0;
+ ((i < SHADER_MAX_ATTRS) && (shader.attrs[i] != SHADER_ATTR_LAST));
+ i++)
+ {
+ gl.glEnableVertexAttribArray(shader.attrs[i]);
+ os_gl_no_error();
+ }
+}
+
+void
+geometry_drawi(Geometry_Buffer geo, u32 indices, u32 offset){
+ glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_INT, (void*)(offset * sizeof(u32)));
+ os_gl_no_error();
+}
+
+void
+geometry_draw(Geometry_Buffer geo){
+ geometry_drawi(geo, geo.indices_len, 0);
+}
+
+void vertex_attrib_pointer(Geometry_Buffer geo, Shader shader, GLuint count, GLuint attr_index, GLuint stride, GLuint offset){
+ geometry_bind(geo);
+ gl.glVertexAttribPointer(shader.attrs[attr_index], count, GL_FLOAT, false, stride * sizeof(float), (void*)(offset * sizeof(float)));
+ os_gl_no_error();
+ gl.glEnableVertexAttribArray(shader.attrs[attr_index]);
+ os_gl_no_error();
+}
+
+Texture_Desc
+texture_desc_default(u32 width, u32 height)
+{
+ return (Texture_Desc){
+ .w = width,
+ .h = height,
+ .s = width,
+ .min_filter = GL_NEAREST,
+ .mag_filter = GL_LINEAR,
+ .fmt_internal = GL_RGBA,
+ .fmt_data = GL_RGBA
+ };
+}
+
+Texture
+texture_create(Texture_Desc desc, u8* pixels)
+{
+ Texture result = {};
+ glGenTextures(1, &result.id);
+ os_gl_no_error();
+
+ glBindTexture(GL_TEXTURE_2D, result.id);
+ os_gl_no_error();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, desc.min_filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, desc.mag_filter);
+ os_gl_no_error();
+
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ desc.fmt_internal,
+ desc.w,
+ desc.h,
+ 0,
+ desc.fmt_data,
+ GL_UNSIGNED_BYTE,
+ pixels
+ );
+ os_gl_no_error();
+
+ result.desc = desc;
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ return result;
+}
+
+void
+texture_update(Texture tex, u8* new_pixels, u32 width, u32 height, u32 stride)
+{
+ // NOTE(PS): this function simply replaces the entire image
+ // we can write a more granular version if we need it
+
+ assert(tex.desc.w == width && tex.desc.h == height && tex.desc.s == stride);
+ texture_bind(tex);
+ glTexSubImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ 0, 0, // offset
+ width, height,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ new_pixels
+ );
+ os_gl_no_error();
+}
+
+void
+texture_bind(Texture tex)
+{
+ glBindTexture(GL_TEXTURE_2D, tex.id);
+ os_gl_no_error();
+}
+
+void
+frame_begin(Graphics_Frame_Desc desc)
+{
+ v4 cc = desc.clear_color;
+ glClearColor(cc.r, cc.g, cc.b, cc.a);
+
+ v2 vmin = desc.viewport_min;
+ v2 vdim = HMM_SubtractVec2(desc.viewport_max, desc.viewport_min);
+ glViewport((GLsizei)vmin.x, (GLsizei)vmin.y, (GLint)vdim.x, (GLint)vdim.y);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glDisable(GL_CULL_FACE);
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+
+ os_gl_no_error();
+}
+
+void
+frame_clear()
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+
+String
+xplatform_shader_program_get_vert(XPlatform_Shader_Program_Src src)
+{
+#if defined(PLATFORM_win32)
+ return src.win32_vert;
+#elif defined(PLATFORM_osx)
+ return src.osx_vert;
+#elif defined(PLATFORM_wasm)
+ return src.wasm_vert;
+#endif
+}
+
+String
+xplatform_shader_program_get_frag(XPlatform_Shader_Program_Src src)
+{
+#if defined(PLATFORM_win32)
+ return src.win32_frag;
+#elif defined(PLATFORM_osx)
+ return src.osx_frag;
+#elif defined(PLATFORM_wasm)
+ return src.wasm_frag;
+#endif
+}
+
+
+#endif // LUMENARIUM_EDITOR_GRAPHICS_H
\ No newline at end of file
diff --git a/src_v2/editor/graphics/lumenarium_editor_opengl.h b/src_v2/editor/graphics/lumenarium_editor_opengl.h
new file mode 100644
index 0000000..094882b
--- /dev/null
+++ b/src_v2/editor/graphics/lumenarium_editor_opengl.h
@@ -0,0 +1,121 @@
+/* date = March 24th 2022 6:05 pm */
+
+#ifndef LUMENARIUM_EDITOR_OPENGL_H
+#define LUMENARIUM_EDITOR_OPENGL_H
+
+// glext.h - https://github.com/KhronosGroup/OpenGL-Registry/blob/main/api/GL/glext.h
+// wglext.h -
+
+void os_gl_no_error();
+
+// type mocking
+// so far, this is only for platforms that won't be using the editor
+// but which still need to compile it.
+#if !defined(GL_GLEXT_VERSION)
+typedef u32 GLsizei;
+typedef u32 GLuint;
+typedef u32 GLenum;
+typedef s32 GLint;
+typedef float GLfloat;
+typedef u8 GLchar;
+typedef bool GLboolean;
+
+#define GL_ARRAY_BUFFER 0
+#define GL_ELEMENT_ARRAY_BUFFER 0
+#define GL_VERTEX_SHADER 0
+#define GL_COMPILE_STATUS 0
+#define GL_FRAGMENT_SHADER 0
+#define GL_LINK_STATUS 0
+#define GL_TRUE true
+#define GL_FALSE false
+#define GL_UNSIGNED_INT 0
+#define GL_FLOAT 0
+#define GL_NEAREST 0
+#define GL_LINEAR 0
+#define GL_RGBA 0
+#define GL_TEXTURE_2D 0
+#define GL_TEXTURE_WRAP_S 0
+#define GL_TEXTURE_WRAP_T 0
+#define GL_TEXTURE_MIN_FILTER 0
+#define GL_TEXTURE_MAG_FILTER 0
+#define GL_UNSIGNED_BYTE 0
+#define GL_SRC_ALPHA 0
+#define GL_ONE_MINUS_SRC_ALPHA 0
+#define GL_CULL_FACE 0
+#define GL_DEPTH_TEST 0
+#define GL_LESS 0
+#define GL_COLOR_BUFFER_BIT 0
+#define GL_DEPTH_BUFFER_BIT 0
+#define GL_STATIC_DRAW 0
+#define GL_TRIANGLES 0
+#define GL_REPEAT 0
+#define GL_BLEND 0
+
+#endif
+
+// OpenGL 3.3+ Context Creation
+#if defined(PLATFORM_win32)
+typedef const char *WINAPI proc_wglGetExtensionsStringARB(HDC hdc);
+typedef BOOL proc_wglChoosePixelFormatARB(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
+typedef HGLRC proc_wglCreateContextAttribsARB(HDC hDC, HGLRC hshareContext, const int *attribList);
+#endif
+
+// OpenGL 3.3+ Extensions
+typedef void proc_glGenVertexArrays(GLsizei n, GLuint *arrays);
+typedef void proc_glBindVertexArray(GLuint array);
+typedef void proc_glGenBuffers (GLsizei n, GLuint *buffers);
+typedef void proc_glBindBuffer(GLenum target, GLuint buffer);
+typedef void proc_glBufferData(GLenum target, size_t size, const void *data, GLenum usage);
+typedef void proc_glBufferSubData(GLenum target, size_t offset, size_t size, const void* data);
+typedef GLuint proc_glCreateShader(GLenum type);
+typedef void proc_glShaderSource(GLuint shader, u32 count, const char* const* string, const GLint *length);
+typedef void proc_glCompileShader(GLuint shader);
+typedef GLuint proc_glCreateProgram(void);
+typedef void proc_glAttachShader(GLuint program, GLuint shader);
+typedef void proc_glLinkProgram(GLuint program);
+typedef void proc_glUseProgram(GLuint program);
+typedef GLuint proc_glGetAttribLocation(GLuint program, const char* name);
+typedef void proc_glVertexAttribPointer(GLuint attr, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
+typedef void proc_glEnableVertexAttribArray(GLuint index);
+typedef void proc_glGetShaderiv(GLuint shader, GLenum ele, GLint* value_out);
+typedef void proc_glGetShaderInfoLog(GLuint shader, GLuint buf_len, GLsizei* len_out, GLchar* buf);
+typedef void proc_glGetProgramiv(GLuint program, GLenum ele, GLint* value_out);
+typedef void proc_glGetProgramInfoLog(GLuint program, GLuint cap, GLsizei* len_out, GLchar* buf);
+typedef GLuint proc_glGetUniformLocation(GLuint program, const char* name);
+typedef void proc_glUniformMatrix4fv(GLuint uniform, GLuint count, GLenum normalize, GLfloat* elements);
+typedef struct OpenGL_Extensions OpenGL_Extensions;
+struct OpenGL_Extensions
+{
+ #if defined(PLATFORM_win32)
+ proc_wglGetExtensionsStringARB* wglGetExtensionsStringARB;
+ proc_wglChoosePixelFormatARB* wglChoosePixelFormatARB;
+ proc_wglCreateContextAttribsARB* wglCreateContextAttribsARB;
+ #endif
+
+ proc_glGenVertexArrays* glGenVertexArrays;
+ proc_glBindVertexArray* glBindVertexArray;
+ proc_glGenBuffers* glGenBuffers;
+ proc_glBindBuffer* glBindBuffer;
+ proc_glBufferData* glBufferData;
+ proc_glBufferSubData* glBufferSubData;
+ proc_glCreateShader* glCreateShader;
+ proc_glShaderSource* glShaderSource;
+ proc_glCompileShader* glCompileShader;
+ proc_glCreateProgram* glCreateProgram;
+ proc_glAttachShader* glAttachShader;
+ proc_glLinkProgram* glLinkProgram;
+ proc_glUseProgram* glUseProgram;
+ proc_glGetAttribLocation* glGetAttribLocation;
+ proc_glVertexAttribPointer* glVertexAttribPointer;
+ proc_glEnableVertexAttribArray* glEnableVertexAttribArray;
+ proc_glGetShaderiv* glGetShaderiv;
+ proc_glGetShaderInfoLog* glGetShaderInfoLog;
+ proc_glGetProgramiv* glGetProgramiv;
+ proc_glGetProgramInfoLog* glGetProgramInfoLog;
+ proc_glGetUniformLocation* glGetUniformLocation;
+ proc_glUniformMatrix4fv* glUniformMatrix4fv;
+};
+
+global OpenGL_Extensions gl = {};
+
+#endif //LUMENARIUM_EDITOR_OPENGL_H
\ No newline at end of file
diff --git a/src_v2/editor/lumenarium_editor.c b/src_v2/editor/lumenarium_editor.c
new file mode 100644
index 0000000..350543d
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor.c
@@ -0,0 +1,334 @@
+internal void
+ed_load_font_cb(File_Async_Job_Args result, u8* user_data)
+{
+ App_State* state = (App_State*)user_data;
+ UI* ui = &state->editor->ui;
+
+ u8* f = result.data.base;
+ stbtt_fontinfo font;
+ if (!stbtt_InitFont(&font, f, stbtt_GetFontOffsetForIndex(f, 0)))
+ {
+ invalid_code_path;
+ }
+
+ r32 scale = stbtt_ScaleForPixelHeight(&font, 16.0f);
+ s32 ascent, descent, line_gap;
+ stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap);
+ ui->font_ascent = (r32)ascent * scale;
+ ui->font_descent = (r32)descent * scale;
+ ui->font_line_gap = (r32)line_gap * scale;
+ if (ui->font_line_gap == 0) ui->font_line_gap = 5;
+
+ ui->font_texture_scale = 2;
+ scale *= ui->font_texture_scale;
+ String c = lit_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+[]{}\\|;:'\",<.>/?`~");
+ for (u64 i = 0; i < c.len; i++)
+ {
+ s32 w, h, xoff, yoff;
+ u32 id = (u32)c.str[i];
+ u8* bp = stbtt_GetCodepointBitmap(&font, 0, scale, (char)c.str[i], &w, &h, &xoff, &yoff);
+ s32 x0, y0, x1, y1;
+ stbtt_GetCodepointBitmapBoxSubpixel(&font, (char)c.str[i], scale, scale, 0, 0, &x0, &y0, &x1, &y1);
+
+ v2 offset = (v2){ 0, (r32)y0 / ui->font_texture_scale };
+ texture_atlas_register(&state->editor->ui.atlas, bp, (u32)w, (u32)h, id, offset, TextureAtlasRegistration_PixelFormat_Alpha);
+ stbtt_FreeBitmap(bp, 0);
+ }
+
+ Texture_Atlas_Sprite m_sprite = texture_atlas_sprite_get(&state->editor->ui.atlas, (u32)'m');
+ ui->font_space_width = (r32)(m_sprite.max_x - m_sprite.min_x);
+
+ texture_update(ui->atlas_texture, ui->atlas.pixels, 1024, 1024, 1024);
+}
+
+internal void
+ed_draw_panel(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area)
+{
+ App_State* state = (App_State*)user_data;
+ UI* ui = &state->editor->ui;
+ scratch_get(scratch);
+
+ UI_Layout title_layout = {};
+ ui_layout_set_row_info(ui, &title_layout);
+ title_layout.bounds_min = (v2){ area.min.x, area.max.y - title_layout.row_height };
+ title_layout.bounds_max = area.max;
+ title_layout.at = title_layout.bounds_min;
+
+ UI_Layout panel_layout = {};
+ ui_layout_set_row_info(ui, &panel_layout);
+ panel_layout.bounds_min = area.min;
+ panel_layout.bounds_max = (v2){ area.max.x, title_layout.bounds_max.y };
+ panel_layout.at = panel_layout.bounds_min;
+
+ ui_layout_push(ui, &panel_layout);
+
+ String title = {};
+ switch (node.user_data)
+ {
+ case 0:
+ {
+ title = lit_str("None");
+ } break;
+
+ case 1:
+ {
+ ed_sculpture_visualizer(state);
+ title = lit_str("Sculpture");
+ } break;
+
+ invalid_default_case;
+ }
+ ui_layout_pop(ui);
+
+ ui_layout_push(ui, &title_layout);
+ UI_Widget_Desc bg = {};
+ bg.style.flags = UIWidgetStyle_Bg;
+ bg.style.color_bg = (v4){.4f,.4f,.4f,1};
+ bg.style.sprite = WHITE_SPRITE_ID;
+ bg.string = string_f(scratch.a, "%.*s_%u_title_bg", str_varg(title), id.value);
+ bg.p_min = title_layout.bounds_min;
+ bg.p_max = title_layout.bounds_max;
+ UI_Widget_Result r = ui_widget_push(ui, bg);
+ ui_layout_row_begin(&title_layout, 4);
+ {
+ ui_textc(ui, BLACK_V4, title);
+ }
+ ui_layout_row_end(&title_layout);
+ ui_widget_pop(ui, r.id);
+ ui_layout_pop(ui);
+
+ scratch_release(scratch);
+}
+
+Geometry_Buffer b;
+Shader shd;
+Texture tex;
+
+internal void
+ed_init(App_State* state, Editor_Desc* desc)
+{
+ lumenarium_env_validate();
+
+ Editor* editor = allocator_alloc_struct(permanent, Editor);
+ zero_struct(*editor);
+ state->editor = editor;
+ editor->window_dim = desc->init_window_dim;
+ editor->content_scale = desc->content_scale;
+ editor->ui = ui_create(4096, 4096, state->input_state, permanent);
+ editor->ui.draw_panel_cb = ed_draw_panel;
+ editor->ui.draw_panel_cb_data = (u8*)state;
+ //bsp_split(&editor->ui.panels, editor->ui.panels.root, 700, BSPSplit_YAxis, 0, 1);
+
+ file_async_read(lit_str("data/font.ttf"), ed_load_font_cb);
+
+ r32 w = 1400;
+ r32 h = 700;
+ r32 z = -1;
+ r32 tri[] = {
+ 0, 0, 0, 0, 0,
+ w, 0, z, 1, 0,
+ w, h, z, 1, 1,
+ 0, h, z, 0, 1,
+ };
+ u32 indices[] = { 0, 1, 2, 0, 2, 3 };
+
+ String shd_v = lit_str(
+ "#version 330 core\n"
+ "layout (location = 0) in vec3 pos;\n"
+ "layout (location = 1) in vec2 auv;\n"
+ "out vec2 uv;\n"
+ "uniform mat4 proj;\n"
+ "void main(void) { \n"
+ " gl_Position = proj * vec4(pos, 1); \n"
+ " uv = auv;\n"
+ "}\n"
+ );
+ String shd_f = lit_str(
+ "#version 330 core\n"
+ "in vec2 uv;\n"
+ "out vec4 FragColor;\n"
+ "uniform sampler2D tex;\n"
+ "void main(void) { FragColor = texture(tex, uv) * vec4(1,1,1,1); }"
+ );
+ String shd_a[] = { lit_str("pos"), lit_str("auv") };
+ String shd_u[] = { lit_str("proj") };
+ b = geometry_buffer_create(
+ tri, sizeof(tri) / sizeof(r32),
+ indices, 6
+ );
+ shd = shader_create(shd_v, shd_f, shd_a, 2, shd_u, 1);
+ vertex_attrib_pointer(b, shd, 3, shd.attrs[0], 5, 0);
+ vertex_attrib_pointer(b, shd, 2, shd.attrs[1], 5, 3);
+
+ u32 tex_pix[] = { 0xFFFFFFFF, 0xFF00FFFF, 0xFFFFFF00, 0xFFFF00FF };
+ tex = texture_create(texture_desc_default(2, 2), (u8*)tex_pix);
+
+ ed_sculpture_visualizer_init(state);
+ lumenarium_env_validate();
+}
+
+internal u8*
+ed_leds_to_texture(App_State* state, Allocator_Scratch* scratch, u32 pixels_dim)
+{
+ u32 at = 0;
+ u32* pixels = allocator_alloc_array(scratch->a, u32, pixels_dim * pixels_dim);
+ for (u32 a = 0; a < state->assemblies.len; a++)
+ {
+ Assembly_Pixel_Buffer leds = state->assemblies.pixel_buffers[a];
+ for (u32 p = 0; p < leds.len; p++)
+ {
+ Assembly_Pixel led = leds.pixels[p];
+ u32 index = at++;
+ pixels[index] = (
+ led.r << 0 |
+ led.g << 8 |
+ led.b << 16 |
+ 0xFF << 24
+ );
+ }
+ }
+ return (u8*)pixels;
+}
+
+internal void
+ed_sculpture_updated(App_State* state, r32 scale, r32 led_size)
+{
+ lumenarium_env_validate();
+ Editor* ed = state->editor;
+ if (!ed) return;
+
+ scratch_get(scratch);
+
+ // NOTE(PS): we need to know the total number of leds in order to give them
+ // texture coordinates
+ u32 leds_count = 0;
+ for (u32 a = 0; a < state->assemblies.len; a++)
+ {
+ Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a];
+ leds_count += pixels.len;
+ }
+
+ // round up to a texture whose sides are powers of two
+ u32 pixels_dim = (u32)floorf(sqrtf((r32)leds_count));
+ pixels_dim = round_up_to_pow2_u32(pixels_dim);
+ u32 pixels_count = pixels_dim * pixels_dim;
+ r32 texel_dim = 1 / (r32)pixels_dim;
+
+ // NOTE(PS): Rebuild the sculpture geometry to point to the new
+ // sculpture.
+ Geo_Vertex_Buffer_Storage storage = (
+ GeoVertexBufferStorage_Position |
+ GeoVertexBufferStorage_TexCoord
+ );
+
+ u32 verts_cap = leds_count * 4;
+ u32 indices_cap = leds_count * 6;
+ Geo_Quad_Buffer_Builder geo = geo_quad_buffer_builder_create(scratch.a, verts_cap, storage, indices_cap);
+ geo_vertex_buffers_validate(&geo.buffer_vertex);
+
+ r32 r = led_size;
+ u32 pixels_created = 0;
+ for (u32 a = 0; a < state->assemblies.len; a++)
+ {
+ Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a];
+ for (u32 p = 0; p < pixels.len; p++)
+ {
+ v3 c = pixels.positions[p].xyz;
+ c = HMM_MultiplyVec3f(c, scale);
+ u32 pixel_count = pixels_created++;
+ u32 pixel_x = pixel_count % pixels_dim;
+ u32 pixel_y = pixel_count / pixels_dim;
+
+ r32 texel_x_min = (r32)pixel_x / (r32)pixels_dim;
+ r32 texel_y_min = (r32)pixel_y / (r32)pixels_dim;
+ r32 texel_x_max = texel_x_min + (texel_dim / 2);
+ r32 texel_y_max = texel_y_min + (texel_dim / 2);
+
+ v2 t0 = (v2){texel_x_max, texel_y_max};
+ v2 t1 = (v2){texel_x_max, texel_y_max};
+ v2 t2 = (v2){texel_x_max, texel_y_max};
+ v2 t3 = (v2){texel_x_max, texel_y_max};
+
+ v3 p0 = HMM_AddVec3(c, (v3){ -r, -r, 0 });
+ v3 p1 = HMM_AddVec3(c, (v3){ r, -r, 0 });
+ v3 p2 = HMM_AddVec3(c, (v3){ r, r, 0 });
+ v3 p3 = HMM_AddVec3(c, (v3){ -r, r, 0 });
+ geo_quad_buffer_builder_push_vt(&geo, p0, p1, p2, p3, t0, t1, t2, t3);
+ }
+ }
+ geo_vertex_buffers_validate(&geo.buffer_vertex);
+
+ if (ed->sculpture_geo.indices_len != 0)
+ {
+ invalid_code_path;
+ // TODO(PS): destroy the old geometry buffer or update it
+ }
+ ed->sculpture_geo = geometry_buffer_create(
+ geo.buffer_vertex.values,
+ geo.buffer_vertex.len * geo.buffer_vertex.stride,
+ geo.buffer_index.values,
+ geo.buffer_index.len
+ );
+ vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 3, ed->sculpture_shd.attrs[0], 5, 0);
+ vertex_attrib_pointer(ed->sculpture_geo, ed->sculpture_shd, 2, ed->sculpture_shd.attrs[1], 5, 3);
+
+ // TODO(PS): make this have enough pixels for the sculpture
+ // TODO(PS): map leds to pixels
+
+ if (ed->sculpture_tex.desc.w != 0)
+ {
+ invalid_code_path;
+ // TODO(PS): destroy the old texture
+ }
+
+ u8* pixels = ed_leds_to_texture(state, &scratch, pixels_dim);
+ Texture_Desc pixel_texture_desc = {
+ .w = pixels_dim,
+ .h = pixels_dim,
+ .s = pixels_dim,
+ .min_filter = GL_NEAREST,
+ .mag_filter = GL_NEAREST,
+ .fmt_internal = GL_RGBA,
+ .fmt_data = GL_RGBA
+ };
+ ed->sculpture_tex = texture_create(pixel_texture_desc, pixels);
+
+ scratch_release(scratch);
+ lumenarium_env_validate();
+}
+
+internal void
+ed_frame_prepare(App_State* state)
+{
+ lumenarium_env_validate();
+ ui_frame_prepare(&state->editor->ui, state->editor->window_dim);
+ lumenarium_env_validate();
+}
+
+internal void
+ed_frame(App_State* state)
+{
+ lumenarium_env_validate();
+ Editor* ed = state->editor;
+ UI* ui = &ed->ui;
+
+ {
+ scratch_get(scratch);
+ u32 w = ed->sculpture_tex.desc.w;
+ u8* pixels = ed_leds_to_texture(state, &scratch, w);
+ texture_update(ed->sculpture_tex, pixels, w, w, w);
+ scratch_release(scratch);
+ }
+
+ edr_render_begin(state);
+ ui_draw(&state->editor->ui);
+ edr_render(state);
+ lumenarium_env_validate();
+}
+
+internal void
+ed_cleanup(App_State* state)
+{
+ lumenarium_env_validate();
+}
+
diff --git a/src_v2/editor/lumenarium_editor.h b/src_v2/editor/lumenarium_editor.h
new file mode 100644
index 0000000..f275a99
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor.h
@@ -0,0 +1,25 @@
+/* date = March 27th 2022 0:50 pm */
+
+#ifndef LUMENARIUM_EDITOR_H
+#define LUMENARIUM_EDITOR_H
+
+typedef struct Editor Editor;
+struct Editor
+{
+ v2 content_scale;
+ v2 window_dim;
+ Editor_Renderer renderer;
+ UI ui;
+
+ v3 camera_pos;
+
+ Geometry_Buffer sculpture_geo;
+ Shader sculpture_shd;
+ Texture sculpture_tex;
+};
+
+// NOTE(PS): call this any time sculpture data is updated if
+// you want to see the sculpture in the visualizer
+internal void ed_sculpture_updated(App_State* state, r32 scale, r32 led_size);
+
+#endif //LUMENARIUM_EDITOR_H
diff --git a/src_v2/editor/lumenarium_editor_renderer.c b/src_v2/editor/lumenarium_editor_renderer.c
new file mode 100644
index 0000000..d0ebf70
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor_renderer.c
@@ -0,0 +1,18 @@
+
+internal void
+edr_render_begin(App_State* state)
+{
+ Graphics_Frame_Desc desc = {};
+ desc.clear_color = (v4){ 0.0f, 0.0f, 0.0f, 1 };
+ desc.viewport_min = (v2){ 0, 0 };
+ v2 wd = state->editor->window_dim;
+ v2 cs = state->editor->content_scale;
+ desc.viewport_max = HMM_MultiplyVec2(wd, cs);
+ frame_begin(desc);
+ frame_clear();
+}
+
+internal void
+edr_render(App_State* state)
+{
+}
\ No newline at end of file
diff --git a/src_v2/editor/lumenarium_editor_renderer.h b/src_v2/editor/lumenarium_editor_renderer.h
new file mode 100644
index 0000000..4129499
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor_renderer.h
@@ -0,0 +1,11 @@
+/* date = March 24th 2022 4:18 pm */
+
+#ifndef LUMENARIUM_EDITOR_RENDERER_H
+#define LUMENARIUM_EDITOR_RENDERER_H
+
+typedef struct Editor_Renderer Editor_Renderer;
+struct Editor_Renderer
+{
+};
+
+#endif //LUMENARIUM_EDITOR_RENDERER_H
diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer.c b/src_v2/editor/lumenarium_editor_sculpture_visualizer.c
new file mode 100644
index 0000000..686aac4
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer.c
@@ -0,0 +1,153 @@
+#include "lumenarium_editor_sculpture_visualizer_shaders.h"
+
+u32 fbo;
+Texture fbo_tex_c;
+u32 fbo_rbo;
+Geometry_Buffer fs_quad;
+Shader fs_shd;
+
+internal void
+ed_sculpture_visualizer_init(App_State* state)
+{
+ Editor* editor = state->editor;
+
+ String vert = xplatform_shader_program_get_vert(sculpture_shd);
+ String frag = xplatform_shader_program_get_frag(sculpture_shd);
+
+ String attrs[] = { lit_str("a_pos"), lit_str("a_uv") };
+ String uniforms[] = { lit_str("proj") };
+ editor->sculpture_shd = shader_create(vert, frag, attrs, 2, uniforms, 1);
+
+ {
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ v2 wds = HMM_MultiplyVec2(editor->window_dim, editor->content_scale);
+ s32 w = (s32)wds.x;
+ s32 h = (s32)wds.y;
+ fbo_tex_c = texture_create(
+ (Texture_Desc){
+ .w = w, .h = h, .s = w,
+ .min_filter = GL_LINEAR,
+ .mag_filter = GL_LINEAR,
+ .fmt_internal = GL_RGBA,
+ .fmt_data = GL_RGBA,
+ },
+ 0
+ );
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_tex_c.id, 0);
+
+ glGenRenderbuffers(1, &fbo_rbo);
+ glBindRenderbuffer(GL_RENDERBUFFER, fbo_rbo);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo_rbo);
+
+ u32 status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ u32 complete = GL_FRAMEBUFFER_COMPLETE;
+ if (status != complete)
+ {
+ #define GL_ENUM_ERROR_CASE(e, msg) case e: { printf("Error: %s - %s\n", #e, msg); } break
+ switch (status) {
+ GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_UNDEFINED, "");
+ GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, "");
+ GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT, "");
+ GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER, "");
+ GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER, "");
+ GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_UNSUPPORTED, "");
+ GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE, "");
+ //GL_ENUM_ERROR_CASE(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS, "");
+ default: {
+ os_gl_no_error();
+ } break;
+ }
+ printf("Error: unable to complete framebuffer\n");
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ String vert = xplatform_shader_program_get_vert(sculpture_comp_shd);
+ String frag = xplatform_shader_program_get_frag(sculpture_comp_shd);
+ String shd_a[] = { lit_str("a_pos"), lit_str("a_uv") };
+ String shd_u[] = { lit_str("proj") };
+ fs_shd = shader_create(vert, frag, shd_a, 2, shd_u, 1);
+
+ fs_quad = unit_quad_create();
+ vertex_attrib_pointer(fs_quad, fs_shd, 3, fs_shd.attrs[0], 5, 0);
+ vertex_attrib_pointer(fs_quad, fs_shd, 2, fs_shd.attrs[1], 5, 3);
+ }
+}
+
+r32 cam_theta = 0;
+u32 offset = 0;
+
+internal void
+ed_sculpture_visualizer(App_State* state)
+{
+ Editor* ed = state->editor;
+
+ incenter_sculpture_visualizer_ui(state, ed);
+
+#define SCULPTURE_VIZ_BLOOM 0
+#if SCULPTURE_VIZ_BLOOM
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ os_gl_no_error();
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_DEPTH_TEST);
+#endif
+
+ // Set the viewport to the current layout's region so that the sculpture
+ // never overlaps any other ui elements
+ UI_Layout l = *ed->ui.layout;
+ v2 view_dim = HMM_SubtractVec2(l.bounds_max, l.bounds_min);
+ v2 view_min = l.bounds_min;
+ v2 view_max = l.bounds_max;
+ v2 view_min_scaled = HMM_MultiplyVec2(view_min, ed->content_scale);
+ v2 view_dim_scaled = HMM_MultiplyVec2(view_dim, ed->content_scale);
+ glViewport(
+ (s32)view_min_scaled.x,
+ (s32)view_min_scaled.y,
+ (u32)view_dim_scaled.x,
+ (u32)view_dim_scaled.y
+ );
+
+ // TODO(PS): TEMPORARY CAMERA CODE
+ cam_theta += 0.005f;
+ r32 cam_r = 50;
+ v3 camera_pos = (v3){ 0, -4.9, -cam_r };
+ camera_pos = (v3){sinf(cam_theta) * cam_r, -4.9f, cosf(cam_theta) * cam_r};
+ r32 aspect = view_dim.x / view_dim.y;
+ m44 proj = HMM_Perspective(72.0, aspect, 0.01f, 500);
+ m44 view = HMM_LookAt(camera_pos, (v3){0,2,0}, (v3){0,1,0});
+
+ shader_bind(ed->sculpture_shd);
+ set_uniform(ed->sculpture_shd, 0, HMM_MultiplyMat4(proj, view));
+ texture_bind(ed->sculpture_tex);
+ geometry_bind(ed->sculpture_geo);
+
+ u32 i = 1008;
+ u32 j = 2868;
+ u32 k = ed->sculpture_geo.indices_len;
+ u32 h = (i * 6) + 3;
+ geometry_drawi(ed->sculpture_geo, k, 0);
+
+ // reset the viewport for all other rendering
+#if SCULPTURE_VIZ_BLOOM
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ v2 wds = HMM_MultiplyVec2(ed->window_dim, ed->content_scale);
+ glViewport(0, 0, (s32)wds.x, (s32)wds.y);
+
+ m44 ortho = HMM_Orthographic(0, ed->window_dim.x, ed->window_dim.y, 0, 0.01f, 200.0f);
+ m44 scale = HMM_Scale((v3){ed->window_dim.x / 2, -ed->window_dim.y / 2, 100});
+ m44 pos = HMM_Translate((v3){ed->window_dim.x / 2, ed->window_dim.y / 2, -99});
+ m44 model = HMM_MultiplyMat4(pos, scale);
+ m44 mvp = HMM_MultiplyMat4(ortho, model);
+
+ shader_bind(fs_shd);
+ set_uniform(fs_shd, 0, mvp);
+ texture_bind(fbo_tex_c);
+ geometry_bind(fs_quad);
+ geometry_drawi(fs_quad, 6, 0);
+#endif // SCULPTURE_VIZ_BLOOM
+}
\ No newline at end of file
diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h b/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h
new file mode 100644
index 0000000..face326
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer_shaders.h
@@ -0,0 +1,135 @@
+
+global XPlatform_Shader_Program_Src sculpture_shd = {
+ .win32_vert = lit_str(
+ "#version 330 core\n"
+ "layout (location = 0) in vec3 a_pos;\n"
+ "layout (location = 1) in vec2 a_uv;\n"
+ "out vec2 uv;\n"
+ "uniform mat4 proj;\n"
+ "void main(void) {\n"
+ " gl_Position = proj * vec4(a_pos, 1.0);\n"
+ " uv = a_uv;\n"
+ "}"
+ ),
+ .win32_frag = lit_str(
+ "#version 330 core\n"
+ "in vec2 uv;\n"
+ "out vec4 FragColor;\n"
+ "uniform sampler2D tex;\n"
+ "void main(void) {\n"
+ " FragColor = texture(tex, uv);\n"
+ "}"
+ ),
+ .osx_vert = lit_str(
+ "#version 330 core\n"
+ "layout (location = 0) in vec3 a_pos;\n"
+ "layout (location = 1) in vec2 a_uv;\n"
+ "out vec2 uv;\n"
+ "uniform mat4 proj;\n"
+ "void main(void) {\n"
+ " gl_Position = proj * vec4(a_pos, 1.0);\n"
+ " uv = a_uv;\n"
+ "}"
+ ),
+ .osx_frag = lit_str(
+ "#version 330 core\n"
+ "in vec2 uv;\n"
+ "out vec4 FragColor;\n"
+ "uniform sampler2D tex;\n"
+ "void main(void) {\n"
+ " FragColor = texture(tex, uv);\n"
+ "}"
+ ),
+
+ .wasm_vert = lit_str(
+ "precision highp float;\n"
+ "attribute vec3 a_pos;\n"
+ "attribute vec2 a_uv;\n"
+ "varying vec2 uv;\n"
+ "uniform mat4 proj;\n"
+ "void main(void) {\n"
+ " gl_Position = proj * vec4(a_pos, 1.0);\n"
+ " uv = a_uv;\n"
+ "}"
+ ),
+ .wasm_frag = lit_str(
+ "precision highp float;\n"
+ "varying vec2 uv;\n"
+ "uniform sampler2D tex;\n"
+ "void main(void) {\n"
+ " //gl_FragColor = texture2D(tex, uv) * color;\n"
+ " gl_FragColor = vec4(uv.x,1,uv.y,1);\n"
+ "}"
+ ),
+};
+
+
+
+global XPlatform_Shader_Program_Src sculpture_comp_shd = {
+ .win32_vert = lit_str(""
+ ),
+ .win32_frag = lit_str(
+ ""
+ ),
+
+ .osx_vert = lit_str(
+ "#version 330 core\n"
+ "layout (location = 0) in vec3 a_pos;\n"
+ "layout (location = 1) in vec2 a_uv;\n"
+ "out vec2 uv;\n"
+ "uniform mat4 proj;\n"
+ "void main(void) {\n"
+ " gl_Position = proj * vec4(a_pos, 1.0);\n"
+ " uv = a_uv;\n"
+ "}"
+ ),
+ .osx_frag = lit_str(
+ "#version 330 core\n"
+ "in vec2 uv;\n"
+ "out vec4 FragColor;\n"
+ "uniform sampler2D tex;\n"
+ "float normpdf(in float x, in float sigma) { return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma; }\n"
+ "void main(void) {\n"
+ " vec4 orig_p = texture(tex, uv);\n"
+ " vec2 tex_size = textureSize(tex, 0);\n"
+ " vec2 tex_offset = 1.0 / tex_size;\n"
+ " "
+ " const int m_size = 15;\n // must be odd\n"
+ " const int k_size = (m_size - 1) / 2;\n"
+ " float kernel[m_size];\n"
+ " float sigma = 7.0;\n"
+ " float z = 0;\n"
+ " for (int i = 0; i <= k_size; i++) {\n"
+ " float v = normpdf(float(i), sigma);\n"
+ " kernel[k_size + i] = v; kernel[k_size - i] = v;\n"
+ " }\n"
+ " for (int i = 0; i < m_size; i++) {\n"
+ " z += kernel[i];\n"
+ " }\n"
+ " vec3 bloom_acc = vec3(0);\n"
+ " for (int i = -k_size; i <= k_size; i++) {\n"
+ " for (int j = -k_size; j <= k_size; j++) {\n"
+ " vec2 uvp = uv + (vec2(float(i), float(j)) / tex_size);\n"
+ " bloom_acc += kernel[k_size + j] * kernel[k_size + i] * texture(tex, uvp).xyz;\n"
+ " }\n"
+ " }\n"
+ " vec3 bloom_color = bloom_acc / (z * z);\n"
+ " vec3 final_color = orig_p.xyz + bloom_color;\n"
+ // tone mapping
+ " float exposure = 1.0;\n"
+ " float gamma = 2.2;\n"
+ " vec3 result = vec3(1.0) - exp(-final_color * exposure);\n"
+ // also gamma correct while we're at it
+ " result = pow(result, vec3(1.0 / gamma));\n"
+ " FragColor = vec4(result, 1.0);\n"
+ "}"
+ ),
+
+ .wasm_vert = lit_str(
+ ""
+ ),
+ .wasm_frag = lit_str(
+ ""
+ ),
+};
+
diff --git a/src_v2/editor/lumenarium_editor_ui.c b/src_v2/editor/lumenarium_editor_ui.c
new file mode 100644
index 0000000..8339f8a
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor_ui.c
@@ -0,0 +1,873 @@
+#define WHITE_SPRITE_ID 511
+
+#include "lumenarium_editor_ui_shaders.h"
+
+internal UI
+ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a)
+{
+ UI result = {};
+ zero_struct(result);
+ result.input = input;
+
+ result.per_frame = bump_allocator_create_reserve(KB(4));
+
+ // Widgets
+ result.widgets.free = allocator_alloc_array(a, UI_Widget, widget_pool_cap);
+ result.widgets.free_cap = widget_pool_cap;
+ result.widgets.states_cap = 3 * widget_pool_cap;
+ result.widgets.states = allocator_alloc_array(a, UI_Widget_State, result.widgets.states_cap);
+ result.widgets.states_hash = allocator_alloc_array(a, u32, result.widgets.states_cap);
+
+ result.panels = bsp_create(a, 32);
+ result.panels.root = bsp_push(&result.panels, (BSP_Node_Id){0}, (BSP_Area){(v2){},(v2){1400, 800}}, 1);
+
+ // Per Frame Vertex Buffer
+ Geo_Vertex_Buffer_Storage storage = (
+ GeoVertexBufferStorage_Position |
+ GeoVertexBufferStorage_TexCoord |
+ GeoVertexBufferStorage_Color
+ );
+ result.geo = geo_quad_buffer_builder_create(a, verts_cap, storage, verts_cap * 2);
+
+ result.per_frame_buffer = geometry_buffer_create(
+ result.geo.buffer_vertex.values,
+ result.geo.buffer_vertex.cap * result.geo.buffer_vertex.stride,
+ result.geo.buffer_index.values,
+ result.geo.buffer_index.cap
+ );
+
+ String vert = xplatform_shader_program_get_vert(ui_shader);
+ String frag = xplatform_shader_program_get_frag(ui_shader);
+
+ String attrs[] = { lit_str("a_pos"), lit_str("a_uv"), lit_str("a_color") };
+ String uniforms[] = { lit_str("proj") };
+ result.shader = shader_create(vert, frag, attrs, 3, uniforms, 1);
+
+ vertex_attrib_pointer(result.per_frame_buffer, result.shader, 3, result.shader.attrs[0], 9, 0);
+ vertex_attrib_pointer(result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 9, 3);
+ vertex_attrib_pointer(result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 9, 5);
+
+ // Texture Atlas
+ result.atlas = texture_atlas_create(1024, 1024, 512, permanent);
+ result.atlas_texture = texture_create(texture_desc_default(1024, 1024), result.atlas.pixels);
+
+ u32 white_sprite[] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ };
+ ui_sprite_register(&result, (u8*)white_sprite, 4, 4, WHITE_SPRITE_ID);
+
+ ui_create_default_style_sheet();
+ return result;
+}
+
+internal void
+ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c)
+{
+ v3 p0 = pmin;
+ v3 p1 = (v3){pmax.x, pmin.y, pmin.z};
+ v3 p2 = pmax;
+ v3 p3 = (v3){pmin.x, pmax.y, pmin.z};
+ v2 t0 = tmin;
+ v2 t1 = (v2){tmax.x,tmin.y};
+ v2 t2 = tmax;
+ v2 t3 = (v2){tmin.x,tmax.y};
+ geo_quad_buffer_builder_push_vtc(&ui->geo, p0, p1, p2, p3, t0, t1, t2, t3, c);
+}
+
+internal void
+ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id)
+{
+ texture_atlas_register(&ui->atlas, pixels, w, h, id, (v2){0,0}, TextureAtlasRegistration_PixelFormat_RGBA);
+ texture_update(ui->atlas_texture, ui->atlas.pixels, ui->atlas.width, ui->atlas.height, ui->atlas.width);
+}
+
+internal void
+ui_sprite_push_color(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color)
+{
+ Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, id);
+ v4 uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite);
+ pmin.XY = HMM_AddVec2(pmin.XY, sprite.draw_offset);
+ pmax.XY = HMM_AddVec2(pmax.XY, sprite.draw_offset);
+ ui_quad_push(ui, pmin, pmax, uv.xy, uv.zw, color);
+}
+
+internal void
+ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id)
+{
+ ui_sprite_push_color(ui, pmin, pmax, id, (v4){1,1,1,1});
+}
+
+typedef struct UI_Char_Draw_Cmd UI_Char_Draw_Cmd;
+struct UI_Char_Draw_Cmd
+{
+ v4 uv;
+ v3 pmin;
+ v3 pmax;
+ v3 baseline_after;
+};
+
+internal UI_Char_Draw_Cmd
+ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint)
+{
+ UI_Char_Draw_Cmd result = {};
+ zero_struct(result);
+
+ Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, codepoint);
+ result.uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite);
+
+ v3 dim = (v3){
+ (r32)(sprite.max_x - sprite.min_x) / ui->font_texture_scale,
+ (r32)(sprite.max_y - sprite.min_y) / ui->font_texture_scale,
+ 0,
+ };
+ result.pmin = at;
+ result.pmin.XY = HMM_AddVec2(result.pmin.XY, sprite.draw_offset);
+ result.pmin.XY = v2_floor(result.pmin.XY);
+ result.pmax = HMM_AddVec3(result.pmin, dim);
+
+ result.baseline_after = (v3){ result.pmax.x + 1.5f, at.y, at.z };
+
+ return result;
+}
+
+internal void
+ui_frame_prepare(UI* ui, v2 window_dim)
+{
+ bump_allocator_clear(ui->per_frame);
+ ui->geo.buffer_vertex.len = 0;
+ ui->geo.buffer_index.len = 0;
+
+ ui->widgets.free_len = 0;
+ ui->widgets.active_parent = 0;
+ ui->widgets.root = ui_widget_pool_push(&ui->widgets, lit_str("root"));
+ ui->widgets.active_parent = ui->widgets.root;
+
+ BSP_Node* panel_root = bsp_get(&ui->panels, ui->panels.root);
+ if (window_dim.x != 0 && window_dim.y != 0 && !HMM_EqualsVec2(window_dim, panel_root->area.max))
+ {
+ BSP_Area area = {};
+ area.min = (v2){0,0};
+ area.max = window_dim;
+ bsp_node_area_update(&ui->panels, ui->panels.root, area);
+ }
+
+ v2 half_d = HMM_MultiplyVec2f(window_dim, 0.5f);
+ ui->proj = HMM_Orthographic(0, window_dim.x, window_dim.y, 0, 0.01f, 100);
+
+ if (ui->widget_next_hot.value != 0)
+ {
+ ui->widget_next_hot_frames += 1;
+ if (ui->widget_next_hot_frames > 1) ui_widget_next_hot_set(ui, 0);
+ }
+ if (ui->widget_hot.value != 0)
+ {
+ ui->widget_hot_frames += 1;
+ if (ui->widget_hot_frames > 1) ui_widget_hot_set(ui, 0);
+ }
+}
+
+global bool show = false;
+
+internal void
+ui_draw_panel(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data)
+{
+ if (node->split.kind != BSPSplit_None) return;
+ UI* ui = (UI*)user_data;
+ BSP_Area area = node->area;
+
+ if (ui->draw_panel_cb) ui->draw_panel_cb(ui->draw_panel_cb_data, id, *node, area);
+
+ r32 z = -1;
+ v3 l0p0 = (v3){ area.min.x, area.min.y, z }; // left side
+ v3 l0p1 = (v3){ area.min.x + 1, area.max.y, z };
+ v3 l1p0 = (v3){ area.max.x - 1, area.min.y, z }; // right side
+ v3 l1p1 = (v3){ area.max.x, area.max.y, z };
+ v3 l2p0 = (v3){ area.min.x, area.min.y , z }; // bottom side
+ v3 l2p1 = (v3){ area.max.x, area.min.y + 1, z };
+ v3 l3p0 = (v3){ area.min.x, area.max.y , z }; // top side
+ v3 l3p1 = (v3){ area.max.x, area.max.y + 1, z };
+ u32 sid = WHITE_SPRITE_ID;
+ v4 c = WHITE_V4;
+ if (rect2_contains(area.min, area.max, ui->input->frame_hot->mouse_pos))
+ {
+ c = PINK_V4;
+ }
+
+ ui_sprite_push_color(ui, l0p0, l0p1, sid, c);
+ ui_sprite_push_color(ui, l1p0, l1p1, sid, c);
+ ui_sprite_push_color(ui, l2p0, l2p1, sid, c);
+ ui_sprite_push_color(ui, l3p0, l3p1, sid, c);
+}
+
+internal void
+ui_draw(UI* ui)
+{
+ bsp_walk_inorder(&ui->panels, ui->panels.root, ui_draw_panel, (u8*)ui);
+
+ u32 widget_count = ui->widgets.free_len;
+ r32 range_min = -10;
+ r32 range_max = -1;
+ r32 range_step = (range_max - range_min) / (r32)(widget_count * 4);
+ ui_widgets_to_geometry_recursive(ui, ui->widgets.root, -10, range_step);
+
+ geometry_buffer_update(
+ &ui->per_frame_buffer,
+ (r32*)ui->geo.buffer_vertex.values,
+ 0,
+ ui->geo.buffer_vertex.len * ui->geo.buffer_vertex.stride,
+ ui->geo.buffer_index.values,
+ 0,
+ ui->geo.buffer_index.len
+ );
+ shader_bind(ui->shader);
+ set_uniform(ui->shader, 0, ui->proj);
+ texture_bind(ui->atlas_texture);
+ geometry_bind(ui->per_frame_buffer);
+ geometry_drawi(ui->per_frame_buffer, ui->geo.buffer_index.len, 0);
+}
+
+////////////////////////////////////////////
+// Widgets
+
+internal UI_Widget_Id
+ui_widget_id_create(String string, u32 index)
+{
+ assert(string.len != 0 && string.str != 0);
+ UI_Widget_Id result = {};
+ zero_struct(result);
+ result.value = hash_djb2_string_to_u32(string);
+ result.index = index;
+ return result;
+}
+
+internal UI_Widget_State*
+ui_widget_pool_state_get(UI_Widget_Pool* pool, UI_Widget_Id id)
+{
+ u32 index = hash_table_find(pool->states_hash, pool->states_cap, id.value);
+ assert(index != pool->states_cap);
+ UI_Widget_State* result = pool->states + index;
+ return result;
+}
+internal UI_Widget_State*
+ui_widget_state_get(UI* ui, UI_Widget_Id id)
+{
+ return ui_widget_pool_state_get(&ui->widgets, id);
+}
+
+internal UI_Widget*
+ui_widget_pool_push(UI_Widget_Pool* pool, String string)
+{
+ assert(pool->free_len < pool->free_cap);
+ UI_Widget* result = pool->free + pool->free_len++;
+
+ result->id = ui_widget_id_create(string, pool->free_len);
+ result->parent = 0;
+ result->next = 0;
+ result->child_first = 0;
+ result->child_last = 0;
+
+ u32 index = hash_table_find(pool->states_hash, pool->states_cap, result->id.value);
+ if (index == pool->states_cap)
+ {
+ index = hash_table_register(pool->states_hash, pool->states_cap, result->id.value);
+ }
+ assert(index != pool->states_cap);
+ UI_Widget_State* state = pool->states + index;
+ zero_struct(*state);
+
+ if (pool->active_parent)
+ {
+ result->parent = pool->active_parent;
+ sll_push(
+ pool->active_parent->child_first,
+ pool->active_parent->child_last,
+ result
+ );
+ }
+ pool->active_parent = result;
+
+ return result;
+}
+
+internal void
+ui_widget_pool_pop(UI_Widget_Pool* pool)
+{
+ if (pool->active_parent->parent)
+ {
+ pool->active_parent = pool->active_parent->parent;
+ }
+}
+
+internal bool
+ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b)
+{
+ return (a.value == b.value);
+}
+
+internal bool
+ui_widget_id_is_valid(UI_Widget_Id h)
+{
+ return h.value != 0;
+}
+
+internal void
+ui_widget_next_hot_set(UI* ui, UI_Widget* w)
+{
+ if (w) {
+ ui->widget_next_hot = w->id;
+ } else {
+ ui->widget_next_hot = (UI_Widget_Id){0};
+ }
+ ui->widget_next_hot_frames = 0;
+}
+
+internal void
+ui_widget_hot_set(UI* ui, UI_Widget* w)
+{
+ if (w) {
+ ui->widget_hot = w->id;
+ } else {
+ ui->widget_hot = (UI_Widget_Id){0};
+ }
+ ui->widget_hot_frames = 0;
+}
+
+internal UI_Widget_Result
+ui_widget_push(UI* ui, UI_Widget_Desc desc)
+{
+ UI_Widget_Result result = {};
+ zero_struct(result);
+ v2 dim = HMM_SubtractVec2(desc.p_max, desc.p_min);
+ if (dim.x == 0 || dim.y == 0) return result;
+
+ UI_Widget* w = ui_widget_pool_push(&ui->widgets, desc.string);
+ w->desc = desc;
+ result.id = w->id;
+
+ UI_Widget_State* state = ui_widget_state_get(ui, w->id);
+ w->desc.fill_pct = state->scroll;
+
+ v2 mouse_p = ui->input->frame_hot->mouse_pos;
+ bool mouse_over = (
+ mouse_p.x >= desc.p_min.x && mouse_p.x <= desc.p_max.x &&
+ mouse_p.y >= desc.p_min.y && mouse_p.y <= desc.p_max.y
+ );
+
+ UI_Widget_Style_Flags flags = desc.style.flags;
+ UI_Widget_Style_Flags mask_drag = (UIWidgetStyle_MouseDragH | UIWidgetStyle_MouseDragV);
+ UI_Widget_Style_Flags mask_hover = (mask_drag | UIWidgetStyle_MouseClick);
+ if (has_flag(flags, mask_hover))
+ {
+ // CASES:
+ // Mouse Over | Mouse Clicked | Is Next Hot | Response
+ // f | f | t | clear next hot
+ // f | f | f | do nothing
+ // f | t | f | do nothing
+ // t | f | f | beome next hot
+ // t | t | f | become next hot
+ // t | t | t | become hot
+
+ if (mouse_over)
+ {
+ if (ui_widget_id_equals(w->id, ui->widget_next_hot))
+ {
+ if (input_key_is_down(ui->input, KeyCode_MouseLeftButton))
+ {
+ ui_widget_hot_set(ui, w);
+ result.flags |= UIWidgetResult_MouseLeft_IsDown;
+ }
+ if (input_key_went_up(ui->input, KeyCode_MouseLeftButton))
+ {
+ result.flags |= UIWidgetResult_MouseLeft_WentUp;
+ ui_widget_hot_set(ui, 0);
+ }
+ }
+
+ if ((w->id.index >= ui->widget_next_hot.index) && ui->widget_hot.value == 0)
+ {
+ ui_widget_next_hot_set(ui, w);
+ }
+ }
+ else
+ {
+ if (ui_widget_id_equals(w->id, ui->widget_next_hot))
+ {
+ ui_widget_next_hot_set(ui, 0);
+ }
+ }
+ }
+
+ if(ui_widget_id_equals(w->id, ui->widget_hot))
+ {
+ if (input_key_is_down(ui->input, KeyCode_MouseLeftButton))
+ {
+ ui_widget_next_hot_set(ui, w);
+ ui_widget_hot_set(ui, w);
+ result.flags |= UIWidgetResult_MouseLeft_IsDown;
+ }
+
+ if (has_flag(flags, mask_drag))
+ {
+ v2 drag_pct_mask = {
+ has_flag(flags, UIWidgetStyle_MouseDragH) ? 1.0f : 0.0f,
+ has_flag(flags, UIWidgetStyle_MouseDragV) ? 1.0f : 0.0f
+ };
+ v2 mp = ui->input->frame_hot->mouse_pos;
+ v2 drag = HMM_SubtractVec2(mp, w->desc.p_min);
+ drag = (v2){ clamp(0, drag.x, w->desc.p_max.x), clamp(0, drag.y, w->desc.p_max.y) };
+ drag = HMM_MultiplyVec2(drag, drag_pct_mask);
+ v2 drag_pct = HMM_DivideVec2(drag, dim);
+ drag_pct = (v2){ clamp(0, drag_pct.x, 1), clamp(0, drag_pct.y, 1) };
+ result.drag = drag_pct;
+
+ state->scroll = drag_pct;
+ }
+ }
+
+ return result;
+}
+
+internal void
+ui_widget_pop(UI* ui, UI_Widget_Id widget_id)
+{
+ if (!ui_widget_id_is_valid(widget_id)) return;
+ assert(ui_widget_id_equals(widget_id, ui->widgets.active_parent->id));
+ ui_widget_pool_pop(&ui->widgets);
+}
+
+internal r32
+ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_step)
+{
+ r32 z_at = z_start;
+ for (UI_Widget* child = widget->child_first; child != 0; child = child->next)
+ {
+ UI_Widget_Desc desc = child->desc;
+ v3 bg_min = v2_to_v3(desc.p_min, z_at);
+ v3 bg_max = v2_to_v3(desc.p_max, z_at);
+
+ v4 color_fg = desc.style.color_fg;
+ v4 color_bg = desc.style.color_bg;
+ if (ui_widget_id_equals(ui->widget_next_hot, child->id))
+ {
+ color_fg = desc.style.color_bg;
+ color_bg = desc.style.color_fg;
+ }
+ if (ui_widget_id_equals(ui->widget_hot, child->id))
+ {
+ //color_fg = desc.style.color_fg;
+ //color_bg = desc.style.color_bg;
+ }
+
+ if (has_flag(child->desc.style.flags, UIWidgetStyle_Outline))
+ {
+ ui_sprite_push_color(ui, bg_min, bg_max, WHITE_SPRITE_ID, color_fg);
+ z_at += z_step;
+ bg_min = HMM_AddVec3(bg_min, (v3){ 1, 1, 0});
+ bg_max = HMM_SubtractVec3(bg_max, (v3){ 1, 1, 0});
+ }
+
+ if (has_flag(child->desc.style.flags, UIWidgetStyle_Bg))
+ {
+ bg_min.z = z_at;
+ bg_max.z = z_at;
+ ui_sprite_push_color(ui, bg_min, bg_max, desc.style.sprite, color_bg);
+ z_at += z_step;
+ }
+
+ if (has_flag(child->desc.style.flags, (UIWidgetStyle_FillH | UIWidgetStyle_FillV)))
+ {
+ v3 fill_min = {};
+ zero_struct(fill_min);
+ v3 fill_max = {};
+ zero_struct(fill_max);
+ if (has_flag(child->desc.style.flags, UIWidgetStyle_FillH))
+ {
+ r32 fill_x = HMM_Lerp(bg_min.x, child->desc.fill_pct.x, bg_max.x);
+
+ if (has_flag(child->desc.style.flags, UIWidgetStyle_LineInsteadOfFill))
+ {
+ fill_min = (v3){ fill_x, bg_min.y, z_at };
+ fill_max = (v3){ fill_x + 1, bg_max.y, z_at };
+ }
+ else
+ {
+ fill_min = bg_min;
+ fill_max = (v3){ fill_x, bg_max.y, z_at };
+ }
+ }
+ else if (has_flag(child->desc.style.flags, UIWidgetStyle_FillV))
+ {
+ r32 fill_y = HMM_Lerp(bg_min.y, child->desc.fill_pct.y, bg_max.y);
+
+ if (has_flag(child->desc.style.flags, UIWidgetStyle_LineInsteadOfFill))
+ {
+ fill_min = (v3){ bg_min.x, fill_y, z_at };
+ fill_max = (v3){ bg_max.x, fill_y + 1, z_at };
+ }
+ else
+ {
+ fill_min = bg_min;
+ fill_max = (v3){ bg_max.x, fill_y, z_at };
+ }
+ }
+
+ ui_sprite_push_color(ui, fill_min, fill_max, WHITE_SPRITE_ID, color_fg);
+ z_at += z_step;
+ }
+
+ if (has_flag(child->desc.style.flags, UIWidgetStyle_TextWrap | UIWidgetStyle_TextClip))
+ {
+ r32 space_width = ui->font_space_width;
+ r32 to_baseline = ui->font_line_gap + ui->font_ascent;
+ v3 line_offset = { 5, 3 + to_baseline, 0 };
+ r32 baseline_x_start = desc.p_min.x + line_offset.x;
+ r32 baseline_y_start = desc.p_min.y + line_offset.y;
+ v3 baseline = { baseline_x_start, baseline_y_start, z_at };
+ for (u64 i = 0; i < child->desc.string.len; i++)
+ {
+ u8 at = child->desc.string.str[i];
+ UI_Char_Draw_Cmd cmd = {};
+ zero_struct(cmd);
+ if (!char_is_space(at))
+ {
+ cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at);
+ }
+ else
+ {
+ cmd.baseline_after = baseline;
+ cmd.baseline_after.x += space_width;
+ }
+
+ if (cmd.baseline_after.x >= desc.p_max.x - 5)
+ {
+ if (has_flag(child->desc.style.flags, UIWidgetStyle_TextClip)) break;
+
+ baseline.x = baseline_x_start;
+ baseline.y += ui->font_ascent + ui->font_descent + ui->font_line_gap;
+ cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at);
+ }
+
+ if (!char_is_space(at))
+ {
+ ui_quad_push(ui, cmd.pmin, cmd.pmax, cmd.uv.xy, cmd.uv.zw, color_fg);
+ }
+ baseline = cmd.baseline_after;
+ }
+ }
+
+ if (child->child_first)
+ {
+ z_at = ui_widgets_to_geometry_recursive(ui, child, z_at + z_step, z_step);
+ }
+
+ z_at += z_step;
+ }
+ return z_at;
+}
+
+///////////////////////////////////////////
+// Layout Manager
+
+internal void
+ui_layout_set_row_info(UI* ui, UI_Layout* l)
+{
+ l->row_height = (ui->font_ascent + ui->font_descent + ui->font_line_gap + 15);
+ l->row_gap = 2;
+ l->col_gap = 2;
+}
+
+internal void
+ui_layout_push(UI* ui, UI_Layout* layout)
+{
+ if (ui->layout)
+ {
+ layout->parent = ui->layout;
+ ui->layout = layout;
+ }
+ else
+ {
+ ui->layout = layout;
+ layout->parent = 0;
+ }
+}
+
+internal void
+ui_layout_pop(UI* ui)
+{
+ if (ui->layout)
+ {
+ ui->layout = ui->layout->parent;
+ }
+}
+
+internal void
+ui_layout_row_begin(UI_Layout* layout, u32 cols)
+{
+ layout->mode = UILayout_Rows;
+ layout->cols = cols;
+}
+internal void
+ui_row_begin(UI* ui, u32 cols) { ui_layout_row_begin(ui->layout, cols); }
+
+internal void
+ui_layout_row_end(UI_Layout* layout)
+{
+ layout->mode = UILayout_Columns;
+}
+internal void
+ui_row_end(UI* ui) { ui_layout_row_end(ui->layout); }
+
+internal UI_Layout_Bounds
+ui_layout_get_next(UI_Layout* layout)
+{
+ UI_Layout_Bounds result = {};
+ zero_struct(result);
+ if (layout->at.x >= layout->bounds_max.x || layout->at.y >= layout->bounds_max.y ||
+ layout->at.y + layout->row_height > layout->bounds_max.y)
+ {
+ return result;
+ }
+
+ switch (layout->mode)
+ {
+ case UILayout_Columns:
+ {
+ result.min = layout->at;
+ result.max = (v2){ layout->bounds_max.x, layout->at.y + layout->row_height };
+ layout->at = (v2){ layout->bounds_min.x, result.max.y + layout->row_gap};
+ } break;
+
+ case UILayout_Rows:
+ {
+ r32 col_width = (layout->bounds_max.x - layout->bounds_min.x) / layout->cols;
+ col_width -= (layout->cols - 1) * layout->col_gap;
+ result.min = layout->at;
+ result.max = (v2){ layout->at.x + col_width, layout->at.y + layout->row_height };
+ layout->at = (v2){ result.max.x + layout->col_gap, layout->at.y };
+ if (layout->at.x >= layout->bounds_max.x)
+ {
+ layout->at = (v2){
+ layout->bounds_min.x,
+ layout->at.y + layout->row_height + layout->row_gap
+ };
+ }
+ } break;
+
+ invalid_default_case;
+ }
+
+ if (result.min.x < layout->bounds_min.x || result.min.y < layout->bounds_min.y ||
+ result.max.x < layout->bounds_min.x || result.max.y < layout->bounds_min.y)
+ {
+ zero_struct(result.min);
+ zero_struct(result.max);
+ }
+
+ return result;
+}
+
+
+///////////////////////////////////////////
+// Specific Widget Implementations
+//
+// These all rely on a layout manager to make calling them simpler
+
+global UI_Style_Sheet ui_default_style_sheet = {};
+
+internal void
+ui_create_default_style_sheet()
+{
+ ui_default_style_sheet.styles[UIWidget_Text] = (UI_Widget_Style){
+ (UIWidgetStyle_TextWrap), (v4){0,0,0,0}, WHITE_V4, WHITE_SPRITE_ID
+ };
+ ui_default_style_sheet.styles[UIWidget_Button] = (UI_Widget_Style){
+ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseClick), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID
+ };
+ ui_default_style_sheet.styles[UIWidget_Toggle] = (UI_Widget_Style){
+ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_MouseClick), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID
+ };
+ ui_default_style_sheet.styles[UIWidget_Menu] = ui_default_style_sheet.styles[UIWidget_Toggle];
+ ui_default_style_sheet.styles[UIWidget_Dropdown] = ui_default_style_sheet.styles[UIWidget_Toggle];
+
+ ui_default_style_sheet.styles[UIWidget_HSlider] = (UI_Widget_Style){
+ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragH | UIWidgetStyle_FillH ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID
+ };
+ ui_default_style_sheet.styles[UIWidget_VSlider] = (UI_Widget_Style){
+ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragV | UIWidgetStyle_FillV ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID
+ };
+ ui_default_style_sheet.styles[UIWidget_HScroll] = (UI_Widget_Style){
+ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragH | UIWidgetStyle_FillH | UIWidgetStyle_LineInsteadOfFill ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID
+ };
+ ui_default_style_sheet.styles[UIWidget_VScroll] = (UI_Widget_Style) {
+ (UIWidgetStyle_TextClip | UIWidgetStyle_Bg | UIWidgetStyle_Outline | UIWidgetStyle_MouseDragV | UIWidgetStyle_FillV | UIWidgetStyle_LineInsteadOfFill ), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID
+ };
+
+ ui_default_style_sheet.styles[UIWidget_Window] = (UI_Widget_Style){
+ (UIWidgetStyle_TextWrap), BLACK_V4, WHITE_V4, WHITE_SPRITE_ID
+ };
+
+};
+
+internal UI_Widget_Style
+ui_get_style(UI* ui, UI_Widget_Kind kind)
+{
+ if (ui->style_sheet) return ui->style_sheet->styles[kind];
+ return ui_default_style_sheet.styles[kind];
+}
+
+internal UI_Widget_Desc
+ui_layout_next_widget(UI* ui, UI_Widget_Kind kind)
+{
+ UI_Layout_Bounds b = ui_layout_get_next(ui->layout);
+ UI_Widget_Desc d = {};
+ zero_struct(d);
+ d.p_min = b.min;
+ d.p_max = b.max;
+ d.style = ui_get_style(ui, kind);
+ return d;
+}
+
+internal void
+ui_textc(UI* ui, v4 color, String string)
+{
+ UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Text);
+ d.string = string;
+ d.style.color_fg = color;
+ UI_Widget_Result r = ui_widget_push(ui, d);
+ ui_widget_pop(ui, r.id);
+}
+
+internal void
+ui_text(UI* ui, v4 color, String string)
+{
+ UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Text);
+ d.string = string_copy(string, ui->per_frame);
+ d.style.color_fg = color;
+ UI_Widget_Result r = ui_widget_push(ui, d);
+ ui_widget_pop(ui, r.id);
+}
+
+internal void
+ui_text_f(UI* ui, v4 color, char* fmt, ...)
+{
+ scratch_get(scratch);
+
+ va_list args;
+ va_start(args, fmt);
+ String string = string_fv(scratch.a, fmt, args);
+ va_end(args);
+
+ ui_text(ui, color, string);
+ scratch_release(scratch);
+}
+
+internal bool
+ui_button(UI* ui, String string)
+{
+ UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Button);
+ d.string = string;
+ UI_Widget_Result r = ui_widget_push(ui, d);
+ ui_widget_pop(ui, r.id);
+ return has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp);
+}
+
+internal bool
+ui_toggle(UI* ui, String string, bool value)
+{
+ UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Button);
+ if (value) {
+ v4 t = d.style.color_fg;
+ d.style.color_fg = d.style.color_bg;
+ d.style.color_bg = t;
+ }
+ d.string = string;
+ UI_Widget_Result r = ui_widget_push(ui, d);
+ ui_widget_pop(ui, r.id);
+ bool result = value;
+ if (has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp)) result = !result;
+ return result;
+}
+
+internal UI_Layout*
+ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 rows)
+{
+ scratch_get(scratch);
+
+ r32 scroll_bar_dim = 15;
+ v2 scroll_bars_area = (v2){0, 0};
+ v2 scroll_area_min = bounds_min;
+ v2 scroll_area_max = HMM_SubtractVec2(bounds_max, scroll_bars_area);
+ v2 scroll_area_dim = HMM_SubtractVec2(scroll_area_max, scroll_area_min);
+
+ v2 scroll_offset = {};
+ zero_struct(scroll_offset);
+ r32 rows_avail = floorf(scroll_area_dim.y / ui->layout->row_height);
+ if (rows > rows_avail)
+ {
+ scroll_bars_area = (v2){ scroll_bar_dim, 0};
+ scroll_area_min = bounds_min;
+ scroll_area_max = HMM_SubtractVec2(bounds_max, scroll_bars_area);
+ scroll_area_dim = HMM_SubtractVec2(scroll_area_max, scroll_area_min);
+
+ UI_Widget_Desc vscroll_d = {};
+ zero_struct(vscroll_d);
+ vscroll_d.p_min = (v2){ bounds_max.x - scroll_bar_dim, bounds_min.y };
+ vscroll_d.p_max = (v2){ bounds_max.x, bounds_max.y };
+ vscroll_d.style = ui_get_style(ui, UIWidget_VScroll);
+ vscroll_d.string = string_f(scratch.a, "%.*s_vscroll", str_varg(string));
+ UI_Widget_Result r = ui_widget_push(ui, vscroll_d);
+ ui_widget_pop(ui, r.id);
+
+ UI_Widget_State* vscroll_state = ui_widget_state_get(ui, r.id);
+ scroll_offset.y = vscroll_state->scroll.y;
+ }
+
+ r32 rows_scroll_to = max(0, rows - (rows_avail - 1));
+ r32 y_scroll_dist = rows_scroll_to * ui->layout->row_height;
+
+ scroll_offset = HMM_MultiplyVec2(scroll_offset, (v2){ 0, y_scroll_dist });
+
+ UI_Layout* layout = allocator_alloc_struct(scratch.a, UI_Layout);
+ layout->mode = UILayout_Columns;
+ layout->bounds_min = scroll_area_min;
+ layout->bounds_max = scroll_area_max;
+ ui_layout_set_row_info(ui, layout);
+ layout->at = HMM_SubtractVec2(bounds_min, scroll_offset);
+ ui_layout_push(ui, layout);
+
+ scratch_release(scratch);
+ return layout;
+}
+
+internal void
+ui_scroll_view_end(UI* ui)
+{
+ ui_layout_pop(ui);
+}
+
+#if 0
+internal bool
+ui_dropdown_begin(UI* ui, String string, bool state)
+{
+ bool result = ui_toggle(ui, string, state);
+ UI_Layout* layout = allocator_alloc_struct(scratch, UI_Layout);
+ zero_struct(*layout);
+ if (result)
+ {
+ ui_scroll_view_begin(ui, layout);
+ }
+ return result;
+}
+
+internal void
+ui_dropdown_end(UI* ui, bool state)
+{
+ if (state)
+ {
+ ui_scroll_view_end(ui);
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h
new file mode 100644
index 0000000..032fd45
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor_ui.h
@@ -0,0 +1,247 @@
+/* date = March 28th 2022 10:52 pm */
+
+#ifndef LUMENARIUM_UI_H
+#define LUMENARIUM_UI_H
+
+/////////////////////////////////////////////////////////////
+// Interface
+
+typedef struct UI_Vertex UI_Vertex;
+struct UI_Vertex
+{
+ v4 pos;
+ v2 uv;
+ v4 color;
+};
+
+#define UI_WIDGET_ID_VALID_BIT 1 << 31
+
+typedef union UI_Widget_Id UI_Widget_Id;
+union UI_Widget_Id
+{
+ // equality of widget id's only relies on the value field
+ // which is a hash of the widget's string
+ u32 value;
+
+ // this struct tracks the index of the widget only to be able
+ // to override next hot in cases where an earlier element became
+ // next hot that is in the same location as the current one
+ u32 index;
+};
+
+typedef u32 UI_Widget_Style_Flags;
+enum
+{
+ UIWidgetStyle_None = 0,
+ UIWidgetStyle_Bg = 1 << 0,
+ UIWidgetStyle_TextClip = 1 << 1,
+ UIWidgetStyle_TextWrap = 1 << 2,
+ UIWidgetStyle_Outline = 1 << 3,
+ UIWidgetStyle_MouseClick = 1 << 4,
+ UIWidgetStyle_MouseDragH = 1 << 5,
+ UIWidgetStyle_MouseDragV = 1 << 6,
+ UIWidgetStyle_FillH = 1 << 7,
+ UIWidgetStyle_FillV = 1 << 8,
+ UIWidgetStyle_LineInsteadOfFill = 1 << 9,
+};
+
+// akin to a css class, could be used to style multiple
+// elements
+typedef struct UI_Widget_Style UI_Widget_Style;
+struct UI_Widget_Style
+{
+ UI_Widget_Style_Flags flags;
+ v4 color_bg;
+ v4 color_fg;
+ u32 sprite;
+};
+
+// combination of style info and per-instance data
+typedef struct UI_Widget_Desc UI_Widget_Desc;
+struct UI_Widget_Desc
+{
+ UI_Widget_Style style;
+ v2 fill_pct;
+ String string;
+ v2 p_min;
+ v2 p_max;
+};
+
+typedef struct UI_Widget UI_Widget;
+struct UI_Widget
+{
+ UI_Widget_Id id;
+ UI_Widget_Desc desc;
+
+ UI_Widget* parent;
+ UI_Widget* next;
+ UI_Widget* child_first;
+ UI_Widget* child_last;
+};
+
+typedef u32 UI_Widget_Result_Flags;
+enum
+{
+ UIWidgetResult_None = 0,
+ UIWidgetResult_MouseLeft_IsDown = 1,
+ UIWidgetResult_MouseLeft_WentUp = 2,
+};
+
+typedef struct UI_Widget_Result UI_Widget_Result;
+struct UI_Widget_Result
+{
+ UI_Widget_Id id;
+ UI_Widget_Result_Flags flags;
+ v2 drag;
+};
+
+typedef u32 UI_Widget_Kind;
+enum
+{
+ UIWidget_Text,
+
+ // Buttons
+ UIWidget_Button,
+ UIWidget_Toggle,
+ UIWidget_Menu,
+ UIWidget_Dropdown,
+
+ // Sliders
+ UIWidget_HSlider,
+ UIWidget_VSlider,
+ UIWidget_HScroll,
+ UIWidget_VScroll,
+
+ // Panels
+ UIWidget_Window,
+
+ UIWidget_Count,
+};
+
+typedef struct UI_Style_Sheet UI_Style_Sheet;
+struct UI_Style_Sheet
+{
+ UI_Widget_Style styles[UIWidget_Count];
+};
+
+typedef struct UI_Widget_State UI_Widget_State;
+struct UI_Widget_State
+{
+ v2 scroll;
+};
+
+typedef struct UI_Widget_Pool UI_Widget_Pool;
+struct UI_Widget_Pool
+{
+ UI_Widget* free;
+ u32 free_cap;
+ u32 free_len;
+
+ UI_Widget* root;
+ UI_Widget* active_parent;
+
+ UI_Widget_State* states;
+ u32* states_hash;
+ u32 states_cap;
+};
+
+typedef u8 UI_Layout_Mode;
+enum
+{
+ // each element takes up a whole row
+ UILayout_Columns,
+
+ // each element takes up one column in the row. If you overflow,
+ // the layout manager overflows to the next row
+ UILayout_Rows,
+};
+
+typedef struct UI_Layout UI_Layout;
+struct UI_Layout
+{
+ UI_Layout_Mode mode;
+ UI_Layout* parent;
+
+ v2 bounds_min;
+ v2 bounds_max;
+ r32 row_height;
+ r32 row_gap;
+ r32 col_gap;
+ v2 at;
+ u32 cols;
+};
+
+typedef struct UI_Layout_Bounds UI_Layout_Bounds;
+struct UI_Layout_Bounds
+{
+ v2 min;
+ v2 max;
+};
+
+typedef void UI_Draw_Panel_Cb(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area);
+
+typedef struct UI UI;
+struct UI
+{
+ Geo_Quad_Buffer_Builder geo;
+
+ Texture_Atlas atlas;
+ r32 font_ascent, font_descent, font_line_gap, font_space_width;
+ r32 font_texture_scale;
+
+ UI_Widget_Pool widgets;
+ UI_Style_Sheet* style_sheet;
+
+ UI_Widget_Id widget_next_hot;
+ UI_Widget_Id widget_hot;
+
+ UI_Layout* layout;
+
+ BSP panels;
+ UI_Draw_Panel_Cb* draw_panel_cb;
+ u8* draw_panel_cb_data;
+
+ // frames since these values were set
+ u16 widget_next_hot_frames;
+ u16 widget_hot_frames;
+
+ Input_State* input;
+
+ m44 proj;
+ Shader shader;
+ Texture atlas_texture;
+ Geometry_Buffer per_frame_buffer;
+
+ Allocator* per_frame;
+};
+
+// Interface
+
+internal UI ui_create();
+internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c);
+internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id);
+internal void ui_sprite_push_color(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color);
+internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id);
+internal v3 ui_sprite_char_push(UI* ui, v2 at, u32 codepoint, v4 color);
+internal void ui_draw(UI* ui);
+
+// Widgets
+
+internal void ui_create_default_style_sheet();
+
+internal UI_Widget_Id ui_widget_id_create(String string, u32 index_in_parent);
+internal bool ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b);
+internal bool ui_widget_id_is_valid(UI_Widget_Id h);
+
+internal void ui_widget_next_hot_set(UI* ui, UI_Widget* w);
+internal void ui_widget_hot_set(UI* ui, UI_Widget* w);
+
+internal UI_Widget* ui_widget_pool_push(UI_Widget_Pool* pool, String string);
+internal void ui_widget_pool_pop(UI_Widget_Pool* pool);
+
+internal UI_Widget_Result ui_widget_push(UI* ui, UI_Widget_Desc desc);
+internal void ui_widget_pop(UI* ui, UI_Widget_Id widget_id);
+
+internal r32 ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_step);
+
+#endif //LUMENARIUM_UI_H
diff --git a/src_v2/editor/lumenarium_editor_ui_shaders.h b/src_v2/editor/lumenarium_editor_ui_shaders.h
new file mode 100644
index 0000000..49e4167
--- /dev/null
+++ b/src_v2/editor/lumenarium_editor_ui_shaders.h
@@ -0,0 +1,76 @@
+global XPlatform_Shader_Program_Src ui_shader = {
+ .win32_vert = lit_str(
+ "#version 330 core\n"
+ "layout (location = 0) in vec3 a_pos;\n"
+ "layout (location = 1) in vec2 a_uv;\n"
+ "layout (location = 2) in vec4 a_color;\n"
+ "out vec2 uv;\n"
+ "out vec4 color;\n"
+ "uniform mat4 proj;\n"
+ "void main(void) {\n"
+ " gl_Position = proj * vec4(a_pos, 1.0);\n"
+ " uv = a_uv;\n"
+ " color = a_color;\n"
+ "}"
+ ),
+ .win32_frag = lit_str(
+ "#version 330 core\n"
+ "in vec2 uv;\n"
+ "in vec4 color;\n"
+ "out vec4 FragColor;\n"
+ "uniform sampler2D tex;\n"
+ "void main(void) {\n"
+ " FragColor = texture(tex, uv) * color;\n"
+ "}"
+ ),
+
+ .osx_vert = lit_str(
+ "#version 330 core\n"
+ "layout (location = 0) in vec3 a_pos;\n"
+ "layout (location = 1) in vec2 a_uv;\n"
+ "layout (location = 2) in vec4 a_color;\n"
+ "out vec2 uv;\n"
+ "out vec4 color;\n"
+ "uniform mat4 proj;\n"
+ "void main(void) {\n"
+ " gl_Position = proj * vec4(a_pos, 1.0);\n"
+ " uv = a_uv;\n"
+ " color = a_color;\n"
+ "}"
+ ),
+ .osx_frag = lit_str(
+ "#version 330 core\n"
+ "in vec2 uv;\n"
+ "in vec4 color;\n"
+ "out vec4 FragColor;\n"
+ "uniform sampler2D tex;\n"
+ "void main(void) {\n"
+ " FragColor = texture(tex, uv) * color;\n"
+ "}"
+ ),
+
+ .wasm_vert = lit_str(
+ "precision highp float;\n"
+ "attribute vec3 a_pos;\n"
+ "attribute vec2 a_uv;\n"
+ "attribute vec4 a_color;\n"
+ "varying vec2 uv;\n"
+ "varying vec4 color;\n"
+ "uniform mat4 proj;\n"
+ "void main(void) {\n"
+ " gl_Position = proj * vec4(a_pos, 1.0);\n"
+ " uv = a_uv;\n"
+ " color = a_color;\n"
+ "}"
+ ),
+ .wasm_frag = lit_str(
+ "precision highp float;\n"
+ "varying vec2 uv;\n"
+ "varying vec4 color;\n"
+ "uniform sampler2D tex;\n"
+ "void main(void) {\n"
+ " gl_FragColor = texture2D(tex, uv) * color;\n"
+ "}"
+ ),
+};
+
diff --git a/src_v2/engine/lumenarium_engine.c b/src_v2/engine/lumenarium_engine.c
new file mode 100644
index 0000000..cf3644e
--- /dev/null
+++ b/src_v2/engine/lumenarium_engine.c
@@ -0,0 +1,104 @@
+
+internal void
+en_init(App_State* state, App_Init_Desc desc)
+{
+ lumenarium_env_validate();
+
+ state->assemblies = assembly_array_create(permanent, desc.assembly_cap);
+
+ Output* o = &state->output;
+ register_output_method(o, OutputData_Invalid, 0, 0, 0);
+ register_output_method(o, OutputData_NetworkSACN, output_network_sacn_update, output_network_sacn_build, output_network_sacn_init());
+ register_output_method(o, OutputData_ComUART, 0, output_network_sacn_build, output_com_uart_init());
+
+ lumenarium_env_validate();
+}
+
+internal void
+en_frame_prepare(App_State* state)
+{
+ lumenarium_env_validate();
+}
+
+global r32 tt = 0;
+
+internal void
+en_frame(App_State* state)
+{
+ lumenarium_env_validate();
+
+ scratch_get(scratch);
+ Assembly_Array assemblies = state->assemblies;
+
+ ///////////////////////////////////////
+ // Output Data
+ Output_Methods methods = state->output.methods;
+
+ // update each method that has an update method
+ for (u32 i = 0; i < OutputData_Count; i++)
+ {
+ if (methods.update_procs[i] == 0) continue;
+ methods.update_procs[i](methods.method_data[i]);
+ }
+
+ Output_Data_Queue output_queue = {};
+ output_queue.a = scratch.a;
+
+ // build output data buffers
+ for (u32 i = 0; i < assemblies.len; i++)
+ {
+ Assembly_Strip_Array strips = assemblies.strip_arrays[i];
+ Assembly_Pixel_Buffer* pixels = assemblies.pixel_buffers + i;
+ for (u32 j = 0; j < strips.len; j++)
+ {
+ Assembly_Strip* strip = strips.strips + j;
+ Build_Output_Data_Buffer* method_proc = methods.procs[strip->output_kind];
+ u8* method_data = methods.method_data[strip->output_kind];
+ if (method_proc == 0) continue;
+ method_proc(state, i, pixels, strip, method_data, &output_queue);
+ }
+ }
+
+ // output the buffers
+ // TODO(PS): we could sort these first if switchig between output
+ // types is time consuming
+
+ Sacn* sacn = (Sacn*)state->output.methods.method_data[OutputData_NetworkSACN];
+ for (Output_Data* at = output_queue.first; at != 0; at = at->next)
+ {
+ // NOTE(PS): we can overload each switch case as more methods come in
+ // ie. OutputData_NetworkSACN and OutputData_NetworkArtNet could use
+ // the same case since at this point they just need to push data out
+ // over a socket
+ switch (at->kind)
+ {
+ case OutputData_NetworkSACN:
+ {
+ os_socket_send_to(
+ sacn->socket,
+ at->network.v4_addr,
+ at->network.port,
+ at->data,
+ 0
+ );
+ } break;
+
+ case OutputData_ComUART:
+ {
+ // TODO(PS): platform com io
+ } break;
+
+ invalid_code_path;
+ }
+ }
+
+ scratch_release(scratch);
+ lumenarium_env_validate();
+}
+
+internal void
+en_cleanup(App_State* state)
+{
+ lumenarium_env_validate();
+}
+
diff --git a/src_v2/engine/lumenarium_engine_assembly.c b/src_v2/engine/lumenarium_engine_assembly.c
new file mode 100644
index 0000000..7d67f56
--- /dev/null
+++ b/src_v2/engine/lumenarium_engine_assembly.c
@@ -0,0 +1,125 @@
+
+Assembly_Array
+assembly_array_create(Allocator* allocator, u32 cap)
+{
+ Assembly_Array result = {
+ .cap = cap,
+ .names = allocator_alloc_array(allocator, String, cap),
+ .pixel_buffers = allocator_alloc_array(allocator, Assembly_Pixel_Buffer, cap),
+ .strip_arrays = allocator_alloc_array(allocator, Assembly_Strip_Array, cap),
+ .allocator = allocator,
+ };
+ return result;
+}
+
+bool
+assembly_handle_is_valid(Assembly_Handle h)
+{
+ return ((h.value & ASSEMBLY_HANDLE_VALID_BIT) != 0);
+}
+
+u32
+assembly_handle_to_index(Assembly_Handle h)
+{
+ assert(assembly_handle_is_valid(h));
+ u32 index = (h.value & ASSEMBLY_HANDLE_INDEX_MASK); // mask off high bit
+ return index;
+}
+
+Assembly_Handle
+assembly_add(Assembly_Array* a, String name, u32 pixels_cap, u32 strips_cap)
+{
+ Assembly_Handle result = {};
+ if (a->len < a->cap)
+ {
+ result.value = a->len++;
+ result.value |= ASSEMBLY_HANDLE_VALID_BIT; // high bit being set means its valid
+ }
+ else
+ {
+ // TODO(PS): find empty index
+ // we can use the name being zero as a signal that the slot is empty
+ }
+
+ // NOTE(PS): not actually a bug, just go increase App_Init_Desc::assemblies_cap
+ // b/c you ran out of room
+ assert(assembly_handle_is_valid(result));
+
+ u32 index = assembly_handle_to_index(result);
+
+ a->names[index] = name;
+
+ Assembly_Pixel_Buffer* pixel_buffer = a->pixel_buffers + index;
+ zero_struct(*pixel_buffer);
+ pixel_buffer->cap = pixels_cap;
+ pixel_buffer->pixels = allocator_alloc_array(a->allocator, Assembly_Pixel, pixels_cap);
+ pixel_buffer->positions = allocator_alloc_array(a->allocator, v4, pixels_cap);
+
+ Assembly_Strip_Array* strip_array = a->strip_arrays + index;
+ zero_struct(*strip_array);
+ strip_array->cap = strips_cap;
+ strip_array->strips = allocator_alloc_array(a->allocator, Assembly_Strip, strips_cap);
+
+ return result;
+}
+
+void
+assembly_rem(Assembly_Array* a, Assembly_Handle h)
+{
+}
+
+Assembly_Strip*
+assembly_add_strip(Assembly_Array* a, Assembly_Handle h, u32 pixels_cap)
+{
+ u32 index = assembly_handle_to_index(h);
+
+ Assembly_Strip_Array* strip_array = a->strip_arrays + index;
+ assert(strip_array->len < strip_array->cap);
+
+ Assembly_Strip* result = strip_array->strips + strip_array->len++;
+ result->pixels_cap = pixels_cap;
+ result->pixels_len = 0;
+ result->pixels = allocator_alloc_array(a->allocator, u32, pixels_cap);
+
+ return result;
+}
+
+void
+assembly_add_led(
+ Assembly_Array* a,
+ Assembly_Handle h,
+ Assembly_Strip* strip,
+ v4 position
+){
+ u32 index = assembly_handle_to_index(h);
+
+ Assembly_Pixel_Buffer* pixel_buffer = a->pixel_buffers + index;
+ assert(pixel_buffer->len < pixel_buffer->cap);
+
+ u32 pixel_index = pixel_buffer->len++;
+ pixel_buffer->pixels[pixel_index] = (Assembly_Pixel){};
+ pixel_buffer->positions[pixel_index] = position;
+
+ assert(strip->pixels_len < strip->pixels_cap);
+ strip->pixels[strip->pixels_len++] = pixel_index;
+}
+
+void
+assembly_strip_append_leds(
+ Assembly_Array* a,
+ Assembly_Handle h,
+ Assembly_Strip* strip,
+ v3 start, v3 end,
+ u32 led_count
+){
+ v3 delta_total = HMM_SubtractVec3(end, start);
+ v3 delta_step = HMM_MultiplyVec3f(delta_total, 1.0f / (r32)led_count);
+
+ for (u32 i = 0; i < led_count; i++)
+ {
+ v4 pos = {0,0,0,1};
+ v3 offset = HMM_MultiplyVec3f(delta_step, (r32)i);
+ pos.XYZ = HMM_AddVec3(start, offset);
+ assembly_add_led(a, h, strip, pos);
+ }
+}
\ No newline at end of file
diff --git a/src_v2/engine/lumenarium_engine_assembly.h b/src_v2/engine/lumenarium_engine_assembly.h
new file mode 100644
index 0000000..8dbed50
--- /dev/null
+++ b/src_v2/engine/lumenarium_engine_assembly.h
@@ -0,0 +1,88 @@
+/* date = March 22nd 2022 6:40 pm */
+
+#ifndef LUMENARIUM_ENGINE_ASSEMBLY_H
+#define LUMENARIUM_ENGINE_ASSEMBLY_H
+
+// Assembly_Handle is valid for any index, including zero. However,
+// valid values must have the high bit set. This way, the handle declared
+// via Assembly_Handle my_handle = {}; is invalid, while still allowing
+// index zero to be used in the array.
+//
+// If memory corruption becomes an issue we can make this a bigger bit
+// field that we check since Lumenarium doesn't ever really expect to have
+// more than 128 sculptures in a scene - but who knows, maybe someday O.o?
+#define ASSEMBLY_HANDLE_VALID_BIT (1 << 31)
+#define ASSEMBLY_HANDLE_INDEX_MASK ~ASSEMBLY_HANDLE_VALID_BIT
+typedef struct Assembly_Handle Assembly_Handle;
+struct Assembly_Handle
+{
+ u32 value;
+};
+
+typedef union Assembly_Pixel Assembly_Pixel;
+union Assembly_Pixel
+{
+ struct {
+ u8 r;
+ u8 g;
+ u8 b;
+ };
+ u8 channels[3];
+};
+
+typedef struct Assembly_Pixel_Buffer Assembly_Pixel_Buffer;
+struct Assembly_Pixel_Buffer
+{
+ u32 cap;
+ u32 len;
+ Assembly_Pixel* pixels;
+ v4* positions;
+};
+
+typedef struct Assembly_Strip Assembly_Strip;
+struct Assembly_Strip
+{
+ u32 pixels_cap;
+ u32 pixels_len;
+ // array of indices into the Assembly_Pixel_Buffer for the same assembly
+ u32* pixels;
+
+ Output_Data_Kind output_kind;
+ u32 sacn_universe;
+};
+
+typedef struct Assembly_Strip_Array Assembly_Strip_Array;
+struct Assembly_Strip_Array
+{
+ u32 cap;
+ u32 len;
+ Assembly_Strip* strips;
+};
+
+typedef struct Assembly_Array Assembly_Array;
+struct Assembly_Array
+{
+ u32 cap;
+ u32 len;
+
+ // assembly names
+ String* names;
+
+ // each assembly gets its own pixel buffer
+ Assembly_Pixel_Buffer* pixel_buffers;
+
+ // each assembly gets its own array of strips which
+ // index into that assemblies pixel_buffer
+ Assembly_Strip_Array* strip_arrays;
+
+ Allocator* allocator;
+};
+
+Assembly_Array assembly_array_create(Allocator* allocator, u32 cap);
+Assembly_Handle assembly_add(Assembly_Array* a, String name, u32 pixels_cap, u32 strips_cap);
+void assembly_rem(Assembly_Array* a, Assembly_Handle h);
+Assembly_Strip* assembly_add_strip(Assembly_Array* a, Assembly_Handle h, u32 pixels_cap);
+void assembly_add_led(Assembly_Array* a, Assembly_Handle h, Assembly_Strip* strip, v4 position);
+
+
+#endif //LUMENARIUM_ENGINE_ASSEMBLY_H
diff --git a/src_v2/engine/lumenarium_engine_output.c b/src_v2/engine/lumenarium_engine_output.c
new file mode 100644
index 0000000..d295d19
--- /dev/null
+++ b/src_v2/engine/lumenarium_engine_output.c
@@ -0,0 +1,26 @@
+
+internal void
+register_output_method(Output* output, Output_Data_Kind kind, Output_Method_Update* update, Build_Output_Data_Buffer* proc, u8* method_data)
+{
+ output->methods.update_procs[kind] = update;
+ output->methods.procs[kind] = proc;
+ output->methods.method_data[kind] = method_data;
+}
+
+internal Output_Data*
+output_data_queue_push(Output_Data_Queue* q, u32 size, Output_Data_Kind kind)
+{
+ Output_Data* d = allocator_alloc_struct(q->a, Output_Data);
+ d->kind = kind;
+ d->data.size = size;
+ d->data.base = allocator_alloc(q->a, size);
+ sll_push(q->first, q->last, d);
+ return d;
+}
+
+internal void
+output_data_set_network_addr(Output_Data* d, u32 send_addr, u32 port)
+{
+ d->network.v4_addr = send_addr;
+ d->network.port = port;
+}
diff --git a/src_v2/engine/lumenarium_engine_output.h b/src_v2/engine/lumenarium_engine_output.h
new file mode 100644
index 0000000..027ce26
--- /dev/null
+++ b/src_v2/engine/lumenarium_engine_output.h
@@ -0,0 +1,75 @@
+/* date = March 30th 2022 9:23 am */
+
+#ifndef LUMENARIUM_ENGINE_OUTPUT_H
+#define LUMENARIUM_ENGINE_OUTPUT_H
+
+typedef u8 Output_Data_Kind;
+enum
+{
+ OutputData_Invalid = 0,
+ OutputData_NetworkSACN,
+ OutputData_ComUART,
+ OutputData_Count,
+};
+
+typedef struct Output_Data_Network Output_Data_Network;
+struct Output_Data_Network
+{
+ // Platform_Socket_Handle socket;
+ u32 v4_addr;
+ u32 port;
+};
+
+typedef struct Output_Data_Com Output_Data_Com;
+struct Output_Data_Com
+{
+ String port;
+};
+
+typedef struct Output_Data Output_Data;
+struct Output_Data
+{
+ Output_Data_Kind kind;
+ union
+ {
+ Output_Data_Network network;
+ Output_Data_Com com;
+ };
+ Data data;
+
+ Output_Data* next;
+};
+
+typedef struct Output_Data_Queue Output_Data_Queue;
+struct Output_Data_Queue
+{
+ Output_Data* first;
+ Output_Data* last;
+ u32 len;
+ Allocator* a;
+};
+
+typedef void Output_Method_Update(u8* method_data);
+typedef void Build_Output_Data_Buffer(App_State* state, u32 assembly_id, Assembly_Pixel_Buffer* pixels, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue);
+
+typedef struct Output_Methods Output_Methods;
+struct Output_Methods
+{
+ Output_Method_Update* update_procs[OutputData_Count];
+ Build_Output_Data_Buffer* procs[OutputData_Count];
+ u8* method_data[OutputData_Count];
+};
+
+typedef struct Output Output;
+struct Output
+{
+ Output_Methods methods;
+};
+
+internal void register_output_method(Output* output, Output_Data_Kind kind, Output_Method_Update* update, Build_Output_Data_Buffer* proc, u8* method_data);
+
+internal Output_Data* output_data_queue_push(Output_Data_Queue* q, u32 size, Output_Data_Kind kind);
+
+internal void output_data_set_network_addr(Output_Data* d, u32 send_addr, u32 port);
+
+#endif //LUMENARIUM_ENGINE_OUTPUT_H
diff --git a/src_v2/engine/output/lumenarium_output_sacn.c b/src_v2/engine/output/lumenarium_output_sacn.c
new file mode 100644
index 0000000..ecd28c4
--- /dev/null
+++ b/src_v2/engine/output/lumenarium_output_sacn.c
@@ -0,0 +1,470 @@
+#define SACN_DEFAULT_PORT 5568
+
+#define SACN_STARTCODE_DMX 0
+
+// a description of the address space being used
+#define SACN_PREAMBLE_SIZE_ADDR 0
+#define SACN_POSTAMBLE_SIZE_ADDR 2
+#define SACN_ACN_IDENTIFIER_ADDR 4
+#define SACN_ROOT_FLAGS_AND_LEN_ADDR 16
+#define SACN_ROOT_VECTOR_ADDR 18
+#define SACN_CID_ADDR 22
+#define SACN_FRAMING_FLAGS_AND_LEN_ADDR 38
+#define SACN_FRAMING_VECTOR_ADDR 40
+#define SACN_SOURCE_NAME_ADDR 44
+#define SACN_PRIORITY_ADDR 108
+#define SACN_RESERVED_ADDR 109
+#define SACN_SEQ_NUM_ADDR 111
+#define SACN_OPTIONS_ADDR 112
+#define SACN_UNIVERSE_ADDR 113
+#define SACN_DMP_FLAGS_AND_LEN_ADDR 115
+#define SACN_DMP_VECTOR_ADDR 117
+#define SACN_DMP_ADDRESS_AND_DATA_ADDR 118
+#define SACN_FIRST_PROPERTY_ADDRESS_ADDR 119
+#define SACN_ADDRESS_INC_ADDR 121
+#define SACN_PROP_COUNT_ADDR 123
+#define SACN_START_CODE_ADDR 125
+#define SACN_PROP_VALUES_ADDR (SACN_START_CODE_ADDR + 1)
+
+// Common Sizes
+#define SACN_BUFFER_HEADER_SIZE 126
+#define SACN_BUFFER_BODY_SIZE 512
+#define SACN_BUFFER_SIZE (SACN_BUFFER_HEADER_SIZE + SACN_BUFFER_BODY_SIZE)
+
+#define SACN_SOURCE_NAME_SIZE 64
+#define SACN_ACN_IDENTIFIER_SIZE 12
+#define SACN_RLP_PREAMBLE_SIZE 16
+#define SACN_RLP_POSTAMBLE_SIZE 0
+
+// Data Definitions
+#define SACN_ACN_IDENTIFIER lit_str("ASC-E1.17\0\0\0")
+#define SACN_ROOT_VECTOR 4
+#define SACN_FRAMING_VECTOR 2
+#define SACN_DMP_VECTOR 2
+#define SACN_ADDRESS_AND_DATA_FORMAT 0xa1
+#define SACN_ADDR_INC 1
+#define SACN_DMP_FIRST_PROPERTY_ADDRESS_FORCE 0
+#define SACN_RESERVED_VALUE 0
+
+#define SACN_VHD_L_FLAG 0x80
+#define SACN_VHD_V_FLAG 0x40
+#define SACN_VHD_H_FLAG 0x20
+#define SACN_VHD_D_FLAG 0x10
+
+#define SACN_VHD_MAXFLAGBYTES 7 //The maximum amount of bytes used to pack the flags, len, and vector
+#define SACN_VHD_MAXLEN 0x0fffff //The maximum packet length is 20 bytes long
+#define SACN_VHD_MAXMINLENGTH 4095 //The highest length that will fit in the "smallest" length pack
+
+
+internal void
+sacn_vhd_pack_flags(Data_Writer* w, b8 inherit_vec, b8 inherit_head, b8 inherit_data)
+{
+ u8 last_byte = dw_get_u8(w) & 0x8F;
+
+ if (!inherit_vec) { last_byte |= SACN_VHD_V_FLAG; }
+ if (!inherit_head) { last_byte |= SACN_VHD_H_FLAG; }
+ if (!inherit_data) { last_byte |= SACN_VHD_D_FLAG; }
+
+ w->data.base[w->at] = last_byte;
+}
+
+internal void
+sacn_vhd_pack_len(Data_Writer* w, u32 len, b8 include_len)
+{
+ u32 len_adjusted = len;
+ if (include_len)
+ {
+ if (len + 1 > SACN_VHD_MAXMINLENGTH)
+ {
+ len_adjusted += 2;
+ }
+ else
+ {
+ len_adjusted += 1;
+ }
+ }
+
+ // Mask out the length bits to keep flags intact
+ u8 last_byte = dw_get_u8(w) & 0x70;
+
+ if (len_adjusted > SACN_VHD_MAXMINLENGTH) last_byte |= SACN_VHD_L_FLAG;
+
+ u8* pack_buffer = (u8*)&len_adjusted;
+ if (len_adjusted <= SACN_VHD_MAXMINLENGTH)
+ {
+ last_byte |= (pack_buffer[1] & 0x0f);
+ dw_put_u8(w, last_byte);
+ dw_put_u8(w, pack_buffer[0]);
+ }
+ else
+ {
+ last_byte |= (pack_buffer[2] & 0x0f);
+ dw_put_u8(w, pack_buffer[1]);
+ dw_put_u8(w, pack_buffer[0]);
+ }
+}
+
+#define CopyMemoryTo(from, to, size) CopyMemory_((u8*)(from), (u8*)(to), (size))
+internal void
+CopyMemory_(u8* From, u8* To, u64 Size)
+{
+ for (u64 i = 0; i < Size; i++)
+ {
+ To[i] = From[i];
+ }
+}
+
+// Packs a u8 to a known big endian buffer
+u8*
+PackB1(u8* ptr, u8 val)
+{
+ *ptr = val;
+ return ptr + sizeof(val);
+}
+
+//Unpacks a u8 from a known big endian buffer
+u8
+UpackB1(const u8* ptr)
+{
+ return *ptr;
+}
+
+//Packs a u8 to a known little endian buffer
+u8*
+PackL1(u8* ptr, u8 val)
+{
+ *ptr = val;
+ return ptr + sizeof(val);
+}
+
+//Unpacks a u8 from a known little endian buffer
+u8
+UpackL1(const u8* ptr)
+{
+ return *ptr;
+}
+
+u8*
+PackB2(u8* ptr, u16 val)
+{
+ ptr[1] = (u8)(val & 0xff);
+ ptr[0] = (u8)((val & 0xff00) >> 8);
+ return ptr + sizeof(val);
+}
+
+//Unpacks a u16 from a known big endian buffer
+u16
+UpackB2(const u8* ptr)
+{
+ return (u16)(ptr[1] | ptr[0] << 8);
+}
+
+//Packs a u32 to a known big endian buffer
+u8*
+PackB4(u8* ptr, u32 val)
+{
+ ptr[3] = (u8) (val & 0xff);
+ ptr[2] = (u8)((val & 0xff00) >> 8);
+ ptr[1] = (u8)((val & 0xff0000) >> 16);
+ ptr[0] = (u8)((val & 0xff000000) >> 24);
+ return ptr + sizeof(val);
+}
+
+internal void
+VHD_PackFlags_(u8* Buffer, b32 InheritVec, b32 InheritHead, b32 InheritData)
+{
+ u8* Cursor = Buffer;
+ u8 NewByte = UpackB1(Cursor) & 0x8f;
+
+ if (!InheritVec) { NewByte |= SACN_VHD_V_FLAG; }
+ if (!InheritHead) { NewByte |= SACN_VHD_H_FLAG; }
+ if (!InheritData) { NewByte |= SACN_VHD_D_FLAG; }
+
+ PackB1(Cursor, NewByte);
+}
+
+internal u8*
+VHD_PackLength_(u8* Buffer, u32 Length, b32 IncludeLength)
+{
+ u8* Cursor = Buffer;
+ u32 AdjustedLength = Length;
+ if (IncludeLength)
+ {
+ if (Length + 1 > SACN_VHD_MAXMINLENGTH)
+ {
+ AdjustedLength += 2;
+ }
+ else
+ {
+ AdjustedLength += 1;
+ }
+ }
+
+ // Mask out the length bits to keep flags intact
+ u8 NewByte = UpackB1(Cursor) & 0x70;
+ if (AdjustedLength > SACN_VHD_MAXMINLENGTH)
+ {
+ NewByte |= SACN_VHD_L_FLAG;
+ }
+
+ u8 PackBuffer[4];
+ PackB4(PackBuffer, AdjustedLength);
+ if (AdjustedLength <= SACN_VHD_MAXMINLENGTH)
+ {
+ NewByte |= (PackBuffer[2] & 0x0f);
+ Cursor = PackB1(Cursor, NewByte);
+ Cursor = PackB1(Cursor, PackBuffer[3]);
+ }
+ else
+ {
+ NewByte |= (PackBuffer[1] & 0x0f);
+ Cursor = PackB1(Cursor, PackBuffer[2]);
+ Cursor = PackB1(Cursor, PackBuffer[3]);
+ }
+
+ return Cursor;
+}
+
+internal void
+InitStreamHeader (u8* Buffer, s32 BufferSize,
+ u16 SlotCount,
+ u8 StartCode,
+ u16 Universe,
+ u8 Priority,
+ u16 Reserved,
+ u8 Options,
+ const char* SourceName,
+ Sacn_Cid CID
+)
+{
+ // TODO(pjs): Replace packing with gs_memory_cursor
+
+ u8* Cursor = Buffer;
+
+ // Preamble Size
+ Cursor = PackB2(Cursor, SACN_RLP_PREAMBLE_SIZE);
+ Cursor = PackB2(Cursor, SACN_RLP_POSTAMBLE_SIZE);
+
+ CopyMemoryTo(SACN_ACN_IDENTIFIER.str, Cursor, SACN_ACN_IDENTIFIER_SIZE);
+ Cursor += SACN_ACN_IDENTIFIER_SIZE;
+
+ // TODO(Peter): If you never use this anywhere else, go back and remove the parameters
+ VHD_PackFlags_(Cursor, false, false, false);
+ Cursor = VHD_PackLength_(Cursor,
+ SACN_BUFFER_HEADER_SIZE - SACN_RLP_PREAMBLE_SIZE + SlotCount,
+ false);
+
+ // root vector
+ Cursor = PackB4(Cursor, SACN_ROOT_VECTOR); // 22
+
+ // CID Pack
+ for (s32 i = 0; i < SACN_CID_BYTES; i++)
+ {
+ *Cursor++ = CID.bytes[i];
+ }// 38
+
+ VHD_PackFlags_(Cursor, false, false, false);
+ Cursor = VHD_PackLength_(Cursor,
+ SACN_BUFFER_HEADER_SIZE - SACN_FRAMING_FLAGS_AND_LEN_ADDR + SlotCount,
+ false);
+ // 40
+ // framing vector
+ Cursor = PackB4(Cursor, SACN_FRAMING_VECTOR);
+
+ // framing source name
+ // :Check
+
+ CopyMemoryTo(SourceName, (char*)Cursor, c_str_len((char*)SourceName));
+ Cursor[SACN_SOURCE_NAME_SIZE - 1] = '\0';
+ Cursor += SACN_SOURCE_NAME_SIZE; // 108
+
+ // priority
+ Cursor = PackB1(Cursor, Priority);
+
+ // reserved
+ Cursor = PackB2(Cursor, Reserved); // 111
+
+ // Sequence # is always set to 0/NONE at the beginning, but it is incremented when sending data
+ Cursor = PackB1(Cursor, 0);
+
+ // Options
+ Cursor = PackB1(Cursor, Options);
+
+ // Universe
+ Cursor = PackB2(Cursor, Universe); // 115
+
+ VHD_PackFlags_(Cursor, false, false, false);
+ Cursor = VHD_PackLength_(Cursor,
+ SACN_BUFFER_HEADER_SIZE - SACN_DMP_FLAGS_AND_LEN_ADDR + SlotCount,
+ false); // 117
+
+ // DMP Vector
+ Cursor = PackB1(Cursor, SACN_DMP_VECTOR);
+
+ // DMP Address and data type
+ Cursor = PackB1(Cursor, SACN_ADDRESS_AND_DATA_FORMAT);
+
+ // DMP first property address
+ Cursor = PackB1(Cursor, 0);
+
+ // DMP Address Increment
+ Cursor = PackB1(Cursor, SACN_ADDR_INC);
+
+ // Property Value Count -- Includes one byte for start code
+ Cursor = PackB2(Cursor, SlotCount + 1);
+
+ Cursor = PackB1(Cursor, StartCode);
+
+ assert(Cursor - Buffer == SACN_BUFFER_HEADER_SIZE);
+
+}
+
+internal void
+sacn_fill_buffer_header(Output_Data* d, u16 universe, Sacn* sacn)
+{
+ assert(d && d->data.size > 0);
+
+ // TODO(PS): these should be passed in?
+ u16 slot_count = SACN_BUFFER_BODY_SIZE;
+ u8 start_code = SACN_STARTCODE_DMX;
+ // universe
+ u8 priority = 0;
+ u16 reserved = 0;
+ u8 options = 0;
+
+ Data_Writer w = {};
+ w.data = d->data;
+
+ dw_put_u16_b(&w, SACN_RLP_PREAMBLE_SIZE);
+ dw_put_u16_b(&w, SACN_RLP_POSTAMBLE_SIZE);
+ dw_put_str_min_len(&w, SACN_ACN_IDENTIFIER, SACN_ACN_IDENTIFIER_SIZE);
+
+ sacn_vhd_pack_flags(&w, false, false, false);
+ sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_RLP_PREAMBLE_SIZE + slot_count, false);
+
+ dw_put_u32_b(&w, SACN_ROOT_VECTOR);
+
+ for (u32 i = 0; i < SACN_CID_BYTES; i++) dw_put_u8(&w, sacn->cid.bytes[i]);
+
+ sacn_vhd_pack_flags(&w, false, false, false);
+ sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_FRAMING_FLAGS_AND_LEN_ADDR + slot_count, false);
+
+ dw_put_u32_b(&w, SACN_FRAMING_VECTOR);
+
+ dw_put_str_min_len_nullterm(&w, sacn->source_name, SACN_SOURCE_NAME_SIZE);
+
+ dw_put_u8(&w, priority);
+ dw_put_u16_b(&w, reserved); // synchronization
+ dw_put_u8(&w, sacn->sequence_iter);
+ dw_put_u8(&w, options);
+ dw_put_u16_b(&w, universe);
+
+ sacn_vhd_pack_flags(&w, false, false, false);
+ sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_DMP_FLAGS_AND_LEN_ADDR + slot_count, false);
+
+ dw_put_u8(&w, SACN_DMP_VECTOR);
+ dw_put_u8(&w, SACN_ADDRESS_AND_DATA_FORMAT);
+ dw_put_u16_b(&w, 0); // dmp first priority addr
+ dw_put_u16_b(&w, SACN_ADDR_INC); // dmp address increment
+ dw_put_u16_b(&w, slot_count + 1);
+ dw_put_u8(&w, start_code);
+
+ assert(w.at == SACN_BUFFER_HEADER_SIZE);
+}
+
+internal void
+sacn_fill_buffer_body(Output_Data* d, Assembly_Pixel_Buffer* pixels, Assembly_Strip* strip, u32* leds_placed)
+{
+ u32 first = *leds_placed;
+ u32 to_add = min(strip->pixels_len - first, SACN_BUFFER_BODY_SIZE / 3);
+ u32 one_past_last = first + to_add;
+ for (u32 i = *leds_placed; i < one_past_last; i++)
+ {
+ u32 led_index = strip->pixels[i];
+ Assembly_Pixel color = pixels->pixels[led_index];
+ d->data.base[SACN_BUFFER_HEADER_SIZE + (i * 3) + 0] = color.r;
+ d->data.base[SACN_BUFFER_HEADER_SIZE + (i * 3) + 1] = color.g;
+ d->data.base[SACN_BUFFER_HEADER_SIZE + (i * 3) + 2] = color.b;
+ }
+ *leds_placed += to_add;
+}
+
+internal Sacn_Cid
+sacn_string_to_cid(String str)
+{
+ return (Sacn_Cid){};
+}
+
+//Unpacks a u32 from a known big endian buffer
+inline u32
+UpackB4(const u8* ptr)
+{
+ return (u32)(ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24));
+}
+
+internal u32
+sacn_universe_to_send_addr(u32 universe)
+{
+ u8 multicast_address_buffer[4] = {};
+ multicast_address_buffer[0] = 239;
+ multicast_address_buffer[1] = 255;
+ multicast_address_buffer[2] = (u8)((universe & 0xff00) >> 8); // high bit
+ multicast_address_buffer[3] = (u8)((universe & 0x00ff)); // low bit
+
+ u32 v4_address = (u32)((multicast_address_buffer[3] ) |
+ (multicast_address_buffer[2] << 8) |
+ (multicast_address_buffer[1] << 16) |
+ (multicast_address_buffer[0] << 24));
+ return v4_address;
+}
+
+internal u8*
+output_network_sacn_init()
+{
+ Sacn* result = allocator_alloc_struct(permanent, Sacn);
+ zero_struct(*result);
+
+ result->source_name = string_f(permanent, "lumenarium::incenter");
+
+ String cid_str = lit_str("{67F9D986-544E-4abb-8986-D5F79382586C}");
+ result->cid = sacn_string_to_cid(cid_str);
+
+ s32 ttl = 20;
+ result->socket = os_socket_create(AF_INET, SOCK_DGRAM, 0);
+ os_socket_set_opt(
+ result->socket,
+ IPPROTO_IP,
+ IP_MULTICAST_TTL,
+ (u8*)&ttl, sizeof(ttl)
+ );
+
+ return (u8*)result;
+}
+
+internal void
+output_network_sacn_update(u8* method_data)
+{
+ Sacn* sacn = (Sacn*)method_data;
+ sacn->sequence_iter += 1;
+}
+
+internal void
+output_network_sacn_build(App_State* state, u32 assembly_id, Assembly_Pixel_Buffer* pixels, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue)
+{
+ Sacn* sacn = (Sacn*)method_data;
+
+ u16 universe = (u16)strip->sacn_universe;
+ u32 send_port = SACN_DEFAULT_PORT;
+ for (u32 leds_placed = 0; leds_placed < strip->pixels_len;)
+ {
+ u32 v4_send_addr = sacn_universe_to_send_addr(universe);
+
+ Output_Data* d = output_data_queue_push(queue, SACN_BUFFER_SIZE, OutputData_NetworkSACN);
+ output_data_set_network_addr(d, v4_send_addr, send_port);
+
+ //InitStreamHeader(d->data.base, d->data.size, SACN_BUFFER_BODY_SIZE, SACN_STARTCODE_DMX, universe, 0, 0, 0, "lumenarium::sacn", sacn->cid);
+ sacn_fill_buffer_header(d, universe, sacn);
+ sacn_fill_buffer_body(d, pixels, strip, &leds_placed);
+ universe += 1;
+ }
+}
diff --git a/src_v2/engine/output/lumenarium_output_sacn.h b/src_v2/engine/output/lumenarium_output_sacn.h
new file mode 100644
index 0000000..09f96ae
--- /dev/null
+++ b/src_v2/engine/output/lumenarium_output_sacn.h
@@ -0,0 +1,26 @@
+/* date = March 30th 2022 9:55 am */
+
+#ifndef LUMENARIUM_OUTPUT_SACN_H
+#define LUMENARIUM_OUTPUT_SACN_H
+
+#define SACN_CID_BYTES 16
+typedef struct Sacn_Cid Sacn_Cid;
+struct Sacn_Cid
+{
+ u8 bytes[SACN_CID_BYTES];
+};
+
+typedef struct Sacn Sacn;
+struct Sacn
+{
+ Sacn_Cid cid;
+ s32 sequence_iter;
+ String source_name;
+ Socket_Handle socket;
+};
+
+internal u8* output_network_sacn_init();
+internal void output_network_sacn_update(u8* method_data);
+internal void output_network_sacn_build(App_State* state, u32 assembly_id, Assembly_Pixel_Buffer* pixels, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue);
+
+#endif //LUMENARIUM_OUTPUT_SACN_H
diff --git a/src_v2/engine/output/lumenarium_output_uart.c b/src_v2/engine/output/lumenarium_output_uart.c
new file mode 100644
index 0000000..cc34f65
--- /dev/null
+++ b/src_v2/engine/output/lumenarium_output_uart.c
@@ -0,0 +1,12 @@
+
+internal u8*
+output_com_uart_init()
+{
+ return 0;
+}
+
+internal void
+output_com_uart_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue)
+{
+
+}
diff --git a/src_v2/engine/output/lumenarium_output_uart.h b/src_v2/engine/output/lumenarium_output_uart.h
new file mode 100644
index 0000000..c23a6aa
--- /dev/null
+++ b/src_v2/engine/output/lumenarium_output_uart.h
@@ -0,0 +1,9 @@
+/* date = March 30th 2022 9:57 am */
+
+#ifndef LUMENARIUM_OUTPUT_UART_H
+#define LUMENARIUM_OUTPUT_UART_H
+
+internal u8* output_com_uart_init();
+internal void output_com_uart_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue);
+
+#endif //LUMENARIUM_OUTPUT_UART_H
diff --git a/src_v2/libs/HandmadeMath.h b/src_v2/libs/HandmadeMath.h
new file mode 100644
index 0000000..63f883b
--- /dev/null
+++ b/src_v2/libs/HandmadeMath.h
@@ -0,0 +1,3116 @@
+/*
+ HandmadeMath.h v1.2.0
+
+ This is a single header file with a bunch of useful functions for
+ basic game math operations.
+
+ =============================================================================
+
+ You MUST
+
+ #define HANDMADE_MATH_IMPLEMENTATION
+
+ in EXACTLY one C or C++ file that includes this header, BEFORE the
+ include, like this:
+
+ #define HANDMADE_MATH_IMPLEMENTATION
+ #include "HandmadeMath.h"
+
+ All other files should just #include "HandmadeMath.h" without the #define.
+
+ =============================================================================
+
+ For overloaded and operator overloaded versions of the base C functions,
+ you MUST
+
+ #define HANDMADE_MATH_CPP_MODE
+
+ in EXACTLY one C or C++ file that includes this header, BEFORE the
+ include, like this:
+
+ #define HANDMADE_MATH_IMPLEMENTATION
+ #define HANDMADE_MATH_CPP_MODE
+ #include "HandmadeMath.h"
+
+ All other files should just #include "HandmadeMath.h" without the #define.
+
+ =============================================================================
+
+ To disable SSE intrinsics, you MUST
+
+ #define HANDMADE_MATH_NO_SSE
+
+ in EXACTLY one C or C++ file that includes this header, BEFORE the
+ include, like this:
+
+ #define HANDMADE_MATH_IMPLEMENTATION
+ #define HANDMADE_MATH_CPP_MODE
+ #define HANDMADE_MATH_NO_SSE
+ #include "HandmadeMath.h"
+
+ or
+
+ #define HANDMADE_MATH_IMPLEMENTATION
+ #define HANDMADE_MATH_NO_SSE
+ #include "HandmadeMath.h"
+
+ =============================================================================
+
+ To disable inlining functions, you MUST
+
+ #define HANDMADE_MATH_NO_INLINE
+
+ in EXACTLY one C or C++ file that includes this header, BEFORE the
+ include, like this:
+
+ #define HANDMADE_MATH_IMPLEMENTATION
+ #define HANDMADE_MATH_CPP_MODE
+ #define HANDMADE_MATH_NO_INLINE
+ #include "HandmadeMath.h"
+
+ All other files should just #include "HandmadeMath.h" without the #define.
+
+ =============================================================================
+
+ To use HandmadeMath without the CRT, you MUST
+
+ #define HMM_SINF MySinF
+ #define HMM_COSF MyCosF
+ #define HMM_TANF MyTanF
+ #define HMM_SQRTF MySqrtF
+ #define HMM_EXPF MyExpF
+ #define HMM_LOGF MyLogF
+ #define HMM_ACOSF MyACosF
+ #define HMM_ATANF MyATanF
+ #define HMM_ATAN2F MYATan2F
+
+ Provide your own implementations of SinF, CosF, TanF, ACosF, ATanF, ATan2F,
+ ExpF, and LogF in EXACTLY one C or C++ file that includes this header,
+ BEFORE the include, like this:
+
+ #define HMM_SINF MySinF
+ #define HMM_COSF MyCosF
+ #define HMM_TANF MyTanF
+ #define HMM_SQRTF MySqrtF
+ #define HMM_EXPF MyExpF
+ #define HMM_LOGF MyLogF
+ #define HMM_ACOSF MyACosF
+ #define HMM_ATANF MyATanF
+ #define HMM_ATAN2F MyATan2F
+ #define HANDMADE_MATH_IMPLEMENTATION
+ #define HANDMADE_MATH_CPP_MODE
+ #include "HandmadeMath.h"
+
+ If you do not define all five of these, HandmadeMath.h will use the
+ versions of these functions that are provided by the CRT.
+
+ =============================================================================
+
+ Version History:
+ 0.2 (*) Updated documentation
+ (*) Better C compliance
+ (*) Prefix all handmade math functions
+ (*) Better operator overloading
+ 0.2a
+ (*) Prefixed Macros
+ 0.2b
+ (*) Disabled warning 4201 on MSVC as it is legal is C11
+ (*) Removed the f at the end of HMM_PI to get 64bit precision
+ 0.3
+ (*) Added +=, -=, *=, /= for hmm_vec2, hmm_vec3, hmm_vec4
+ 0.4
+ (*) SSE Optimized HMM_SqrtF
+ (*) SSE Optimized HMM_RSqrtF
+ (*) Removed CRT
+ 0.5
+ (*) Added scalar multiplication and division for vectors
+ and matrices
+ (*) Added matrix subtraction and += for hmm_mat4
+ (*) Reconciled all headers and implementations
+ (*) Tidied up, and filled in a few missing operators
+ 0.5.1
+ (*) Ensured column-major order for matrices throughout
+ (*) Fixed HMM_Translate producing row-major matrices
+ 0.5.2
+ (*) Fixed SSE code in HMM_SqrtF
+ (*) Fixed SSE code in HMM_RSqrtF
+ 0.6
+ (*) Added Unit testing
+ (*) Made HMM_Power faster
+ (*) Fixed possible efficiency problem with HMM_Normalize
+ (*) RENAMED HMM_LengthSquareRoot to HMM_LengthSquared
+ (*) RENAMED HMM_RSqrtF to HMM_RSquareRootF
+ (*) RENAMED HMM_SqrtF to HMM_SquareRootF
+ (*) REMOVED Inner function (user should use Dot now)
+ (*) REMOVED HMM_FastInverseSquareRoot function declaration
+ 0.7
+ (*) REMOVED HMM_LengthSquared in HANDMADE_MATH_IMPLEMENTATION (should
+ use HMM_LengthSquaredVec3, or HANDMADE_MATH_CPP_MODE for function
+ overloaded version)
+ (*) REMOVED HMM_Length in HANDMADE_MATH_IMPLEMENTATION (should use
+ HMM_LengthVec3, HANDMADE_MATH_CPP_MODE for function
+ overloaded version)
+ (*) REMOVED HMM_Normalize in HANDMADE_MATH_IMPLEMENTATION (should use
+ HMM_NormalizeVec3, or HANDMADE_MATH_CPP_MODE for function
+ overloaded version)
+ (*) Added HMM_LengthSquaredVec2
+ (*) Added HMM_LengthSquaredVec4
+ (*) Addd HMM_LengthVec2
+ (*) Added HMM_LengthVec4
+ (*) Added HMM_NormalizeVec2
+ (*) Added HMM_NormalizeVec4
+ 1.0
+ (*) Lots of testing!
+ 1.1
+ (*) Quaternion support
+ (*) Added type hmm_quaternion
+ (*) Added HMM_Quaternion
+ (*) Added HMM_QuaternionV4
+ (*) Added HMM_AddQuaternion
+ (*) Added HMM_SubtractQuaternion
+ (*) Added HMM_MultiplyQuaternion
+ (*) Added HMM_MultiplyQuaternionF
+ (*) Added HMM_DivideQuaternionF
+ (*) Added HMM_InverseQuaternion
+ (*) Added HMM_DotQuaternion
+ (*) Added HMM_NormalizeQuaternion
+ (*) Added HMM_Slerp
+ (*) Added HMM_QuaternionToMat4
+ (*) Added HMM_QuaternionFromAxisAngle
+ 1.1.1
+ (*) Resolved compiler warnings on gcc and g++
+ 1.1.2
+ (*) Fixed invalid HMMDEF's in the function definitions
+ 1.1.3
+ (*) Fixed compile error in C mode
+ 1.1.4
+ (*) Fixed SSE being included on platforms that don't support it
+ (*) Fixed divide-by-zero errors when normalizing zero vectors.
+ 1.1.5
+ (*) Add Width and Height to HMM_Vec2
+ (*) Made it so you can supply your own SqrtF
+ 1.2.0
+ (*) Added equality functions for HMM_Vec2, HMM_Vec3, and HMM_Vec4.
+ (*) Added HMM_EqualsVec2, HMM_EqualsVec3, and HMM_EqualsVec4
+ (*) Added C++ overloaded HMM_Equals for all three
+ (*) Added C++ == and != operators for all three
+ (*) SSE'd HMM_MultiplyMat4 (this is _WAY_ faster)
+ (*) SSE'd HMM_Transpose
+
+ LICENSE
+
+ This software is in the public domain. Where that dedication is not
+ recognized, you are granted a perpetual, irrevocable license to copy,
+ distribute, and modify this file as you see fit.
+
+ CREDITS
+
+ Written by Zakary Strange (zak@handmade.network && @strangezak)
+
+ Functionality:
+ Matt Mascarenhas (@miblo_)
+ Aleph
+ FieryDrake (@fierydrake)
+ Gingerbill (@TheGingerBill)
+ Ben Visness (@bvisness)
+ Trinton Bullard (@Peliex_Dev)
+
+ Fixes:
+ Jeroen van Rijn (@J_vanRijn)
+ Kiljacken (@Kiljacken)
+ Insofaras (@insofaras)
+ Daniel Gibson (@DanielGibson)
+*/
+
+
+/* let's figure out if SSE is really available (unless disabled anyway)
+ (it isn't on non-x86/x86_64 platforms or even x86 without explicit SSE support)
+ => only use "#ifdef HANDMADE_MATH__USE_SSE" to check for SSE support below this block! */
+#ifndef HANDMADE_MATH_NO_SSE
+
+# ifdef _MSC_VER
+/* MSVC supports SSE in amd64 mode or _M_IX86_FP >= 1 (2 means SSE2) */
+# if defined(_M_AMD64) || ( defined(_M_IX86_FP) && _M_IX86_FP >= 1 )
+# define HANDMADE_MATH__USE_SSE 1
+# endif
+# else /* not MSVC, probably GCC, clang, icc or something that doesn't support SSE anyway */
+# ifdef __SSE__ /* they #define __SSE__ if it's supported */
+# define HANDMADE_MATH__USE_SSE 1
+# endif /* __SSE__ */
+# endif /* not _MSC_VER */
+
+#endif /* #ifndef HANDMADE_MATH_NO_SSE */
+
+#include // This is for types
+
+#ifdef HANDMADE_MATH__USE_SSE
+#include
+#endif
+
+#ifndef HANDMADE_MATH_H
+#define HANDMADE_MATH_H
+
+#ifdef _MSC_VER
+#pragma warning(disable:4201)
+#endif
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef HANDMADE_MATH_STATIC
+#define HMMDEF static
+#else
+#define HMMDEF extern
+#endif
+
+#ifdef HANDMADE_MATH_NO_INLINE
+#define HINLINE
+#elif _MSC_VER && !__INTEL_COMPILER
+#define HINLINE __inline
+#else
+#define HINLINE inline
+#endif
+
+#if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \
+!defined(HMM_SQRTF) || !defined(HMM_EXPF) || !defined(HMM_LOGF) || \
+!defined(HMM_ACOSF) || !defined(HMM_ATANF)|| !defined(HMM_ATAN2F)
+#include
+#endif
+
+#ifndef HMM_SINF
+#define HMM_SINF sinf
+#endif
+
+#ifndef HMM_COSF
+#define HMM_COSF cosf
+#endif
+
+#ifndef HMM_TANF
+#define HMM_TANF tanf
+#endif
+
+#ifndef HMM_SQRTF
+#define HMM_SQRTF sqrtf
+#endif
+
+#ifndef HMM_EXPF
+#define HMM_EXPF expf
+#endif
+
+#ifndef HMM_LOGF
+#define HMM_LOGF logf
+#endif
+
+#ifndef HMM_ACOSF
+#define HMM_ACOSF acosf
+#endif
+
+#ifndef HMM_ATANF
+#define HMM_ATANF atanf
+#endif
+
+#ifndef HMM_ATAN2F
+#define HMM_ATAN2F atan2f
+#endif
+
+#define HMM_PI32 3.14159265359f
+#define HMM_PI 3.14159265358979323846
+
+#define HMM_MIN(a, b) (a) > (b) ? (b) : (a)
+#define HMM_MAX(a, b) (a) < (b) ? (b) : (a)
+#define HMM_ABS(a) ((a) > 0 ? (a) : -(a))
+#define HMM_MOD(a, m) ((a) % (m)) >= 0 ? ((a) % (m)) : (((a) % (m)) + (m))
+#define HMM_SQUARE(x) ((x) * (x))
+
+ typedef union hmm_vec2
+ {
+ struct
+ {
+ float X, Y;
+ };
+ struct
+ {
+ float x, y;
+ };
+ struct
+ {
+ float U, V;
+ };
+
+ struct
+ {
+ float Left, Right;
+ };
+
+ struct
+ {
+ float Width, Height;
+ };
+
+ float Elements[2];
+ } hmm_vec2;
+
+ typedef union hmm_vec3
+ {
+ struct
+ {
+ float X, Y, Z;
+ };
+
+ struct
+ {
+ float x, y, z;
+ };
+
+ struct
+ {
+ float U, V, W;
+ };
+
+ struct
+ {
+ float R, G, B;
+ };
+
+ struct
+ {
+ hmm_vec2 XY;
+ float Ignored0_;
+ };
+
+ struct
+ {
+ float Ignored1_;
+ hmm_vec2 YZ;
+ };
+
+ struct
+ {
+ hmm_vec2 UV;
+ float Ignored2_;
+ };
+
+ struct
+ {
+ float Ignored3_;
+ hmm_vec2 VW;
+ };
+
+ float Elements[3];
+ } hmm_vec3;
+
+ typedef union hmm_vec4
+ {
+ struct
+ {
+ union
+ {
+ hmm_vec3 XYZ;
+ struct
+ {
+ float X, Y, Z;
+ };
+ };
+
+ float W;
+ };
+ struct
+ {
+ union
+ {
+ hmm_vec3 xyz;
+ struct
+ {
+ float x, y, z;
+ };
+ };
+
+ float w;
+ };
+ struct
+ {
+ union
+ {
+ hmm_vec3 RGB;
+ struct
+ {
+ float R, G, B;
+ };
+ struct
+ {
+ float r, g, b;
+ };
+ };
+
+ union
+ {
+ float A;
+ float a;
+ };
+ };
+
+ struct
+ {
+ hmm_vec2 XY;
+ float Ignored0_;
+ float Ignored1_;
+ };
+ struct
+ {
+ hmm_vec2 xy;
+ float Ignored0b_;
+ float Ignored1b_;
+ };
+
+ struct
+ {
+ float Ignored2_;
+ hmm_vec2 YZ;
+ float Ignored3_;
+ };
+ struct
+ {
+ float Ignored2b_;
+ hmm_vec2 yz;
+ float Ignored3b_;
+ };
+
+ struct
+ {
+ float Ignored4_;
+ float Ignored5_;
+ hmm_vec2 ZW;
+ };
+
+ struct
+ {
+ float Ignored4b_;
+ float Ignored5b_;
+ hmm_vec2 zw;
+ };
+
+ float Elements[4];
+ } hmm_vec4;
+
+ typedef union hmm_mat4
+ {
+ float Elements[4][4];
+
+
+#ifdef HANDMADE_MATH__USE_SSE
+ __m128 Rows[4];
+#endif
+ } hmm_mat4;
+
+ typedef union hmm_quaternion
+ {
+ struct
+ {
+ union
+ {
+ hmm_vec3 XYZ;
+ struct
+ {
+ float X, Y, Z;
+ };
+ };
+
+ float W;
+ };
+
+ float Elements[4];
+ } hmm_quaternion;
+
+ typedef struct
+ {
+ hmm_vec2 ValueMin;
+ hmm_vec2 ValueMax;
+ } hmm_vec2r;
+
+ typedef struct
+ {
+ hmm_vec3 ValueMin;
+ hmm_vec3 ValueMax;
+ } hmm_vec3r;
+
+ typedef struct
+ {
+ hmm_vec4 ValueMin;
+ hmm_vec4 ValueMax;
+ } hmm_vec4r;
+
+ typedef int32_t hmm_bool;
+
+ typedef hmm_vec2 hmm_v2;
+ typedef hmm_vec3 hmm_v3;
+ typedef hmm_vec4 hmm_v4;
+ typedef hmm_mat4 hmm_m4;
+ typedef hmm_vec2r hmm_v2r;
+ typedef hmm_vec3r hmm_v3r;
+ typedef hmm_vec4r hmm_v4r;
+
+ HMMDEF float HMM_SinF(float Angle);
+ HMMDEF float HMM_TanF(float Angle);
+ HMMDEF float HMM_ATanF(float Theta);
+ HMMDEF float HMM_ATan2F(float Theta, float Theta2);
+ HMMDEF float HMM_CosF(float Angle);
+ HMMDEF float HMM_ACosF(float Theta);
+ HMMDEF float HMM_ExpF(float Float);
+ HMMDEF float HMM_LogF(float Float);
+
+ HMMDEF float HMM_ToRadians(float Degrees);
+ HMMDEF float HMM_SquareRootF(float Float);
+ HMMDEF float HMM_RSquareRootF(float Float);
+
+ HMMDEF float HMM_LengthSquaredVec2(hmm_vec2 A);
+ HMMDEF float HMM_LengthSquaredVec3(hmm_vec3 A);
+ HMMDEF float HMM_LengthSquaredVec4(hmm_vec4 A);
+
+ HMMDEF float HMM_LengthVec2(hmm_vec2 A);
+ HMMDEF float HMM_LengthVec3(hmm_vec3 A);
+ HMMDEF float HMM_LengthVec4(hmm_vec4 A);
+
+ HMMDEF float HMM_Power(float Base, int Exponent);
+ HMMDEF float HMM_PowerF(float Base, float Exponent);
+ HMMDEF float HMM_Lerp(float A, float Time, float B);
+ HMMDEF float HMM_Clamp(float Min, float Value, float Max);
+
+ HMMDEF hmm_vec2 HMM_NormalizeVec2(hmm_vec2 A);
+ HMMDEF hmm_vec3 HMM_NormalizeVec3(hmm_vec3 A);
+ HMMDEF hmm_vec4 HMM_NormalizeVec4(hmm_vec4 A);
+
+ HMMDEF float HMM_DotVec2(hmm_vec2 VecOne, hmm_vec2 VecTwo);
+ HMMDEF float HMM_DotVec3(hmm_vec3 VecOne, hmm_vec3 VecTwo);
+ HMMDEF float HMM_DotVec4(hmm_vec4 VecOne, hmm_vec4 VecTwo);
+
+ HMMDEF hmm_vec3 HMM_Cross(hmm_vec3 VecOne, hmm_vec3 VecTwo);
+
+ HMMDEF hmm_vec2 HMM_Vec2(float X, float Y);
+ HMMDEF hmm_vec2 HMM_Vec2i(int X, int Y);
+ HMMDEF hmm_vec3 HMM_Vec3(float X, float Y, float Z);
+ HMMDEF hmm_vec3 HMM_Vec3i(int X, int Y, int Z);
+ HMMDEF hmm_vec4 HMM_Vec4(float X, float Y, float Z, float W);
+ HMMDEF hmm_vec4 HMM_Vec4i(int X, int Y, int Z, int W);
+ HMMDEF hmm_vec4 HMM_Vec4v(hmm_vec3 Vector, float W);
+
+ HMMDEF hmm_vec2 HMM_AddVec2(hmm_vec2 Left, hmm_vec2 Right);
+ HMMDEF hmm_vec3 HMM_AddVec3(hmm_vec3 Left, hmm_vec3 Right);
+ HMMDEF hmm_vec4 HMM_AddVec4(hmm_vec4 Left, hmm_vec4 Right);
+
+ HMMDEF hmm_vec2 HMM_SubtractVec2(hmm_vec2 Left, hmm_vec2 Right);
+ HMMDEF hmm_vec3 HMM_SubtractVec3(hmm_vec3 Left, hmm_vec3 Right);
+ HMMDEF hmm_vec4 HMM_SubtractVec4(hmm_vec4 Left, hmm_vec4 Right);
+
+ HMMDEF hmm_vec2 HMM_MultiplyVec2(hmm_vec2 Left, hmm_vec2 Right);
+ HMMDEF hmm_vec2 HMM_MultiplyVec2f(hmm_vec2 Left, float Right);
+ HMMDEF hmm_vec3 HMM_MultiplyVec3(hmm_vec3 Left, hmm_vec3 Right);
+ HMMDEF hmm_vec3 HMM_MultiplyVec3f(hmm_vec3 Left, float Right);
+ HMMDEF hmm_vec4 HMM_MultiplyVec4(hmm_vec4 Left, hmm_vec4 Right);
+ HMMDEF hmm_vec4 HMM_MultiplyVec4f(hmm_vec4 Left, float Right);
+
+ HMMDEF hmm_vec2 HMM_DivideVec2(hmm_vec2 Left, hmm_vec2 Right);
+ HMMDEF hmm_vec2 HMM_DivideVec2f(hmm_vec2 Left, float Right);
+ HMMDEF hmm_vec3 HMM_DivideVec3(hmm_vec3 Left, hmm_vec3 Right);
+ HMMDEF hmm_vec3 HMM_DivideVec3f(hmm_vec3 Left, float Right);
+ HMMDEF hmm_vec4 HMM_DivideVec4(hmm_vec4 Left, hmm_vec4 Right);
+ HMMDEF hmm_vec4 HMM_DivideVec4f(hmm_vec4 Left, float Right);
+
+ HMMDEF hmm_bool HMM_EqualsVec2(hmm_vec2 Left, hmm_vec2 Right);
+ HMMDEF hmm_bool HMM_EqualsVec3(hmm_vec3 Left, hmm_vec3 Right);
+ HMMDEF hmm_bool HMM_EqualsVec4(hmm_vec4 Left, hmm_vec4 Right);
+
+ HMMDEF hmm_mat4 HMM_Mat4(void);
+ HMMDEF hmm_mat4 HMM_Mat4d(float Diagonal);
+ HMMDEF hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right);
+ HMMDEF hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right);
+
+#ifdef HANDMADE_MATH__USE_SSE
+ HMMDEF __m128 HMM_LinearCombineSSE(__m128 Left, hmm_mat4 Right);
+#endif
+
+ HMMDEF hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right);
+ HMMDEF hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar);
+ HMMDEF hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector);
+ HMMDEF hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar);
+
+ HMMDEF hmm_mat4 HMM_Transpose(hmm_mat4 Matrix);
+
+ HMMDEF hmm_mat4 HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far);
+ HMMDEF hmm_mat4 HMM_Perspective(float FOV, float AspectRatio, float Near, float Far);
+
+ HMMDEF hmm_mat4 HMM_Translate(hmm_vec3 Translation);
+ HMMDEF hmm_mat4 HMM_Rotate(float Angle, hmm_vec3 Axis);
+ HMMDEF hmm_mat4 HMM_Scale(hmm_vec3 Scale);
+
+ HMMDEF hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up);
+
+ HMMDEF hmm_quaternion HMM_Quaternion(float X, float Y, float Z, float W);
+ HMMDEF hmm_quaternion HMM_QuaternionV4(hmm_vec4 Vector);
+ HMMDEF hmm_quaternion HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right);
+ HMMDEF hmm_quaternion HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right);
+ HMMDEF hmm_quaternion HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right);
+ HMMDEF hmm_quaternion HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative);
+ HMMDEF hmm_quaternion HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend);
+ HMMDEF hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left);
+ HMMDEF float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right);
+ HMMDEF hmm_quaternion HMM_NormalizeQuaternion(hmm_quaternion Left);
+ HMMDEF hmm_quaternion HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right);
+ HMMDEF hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right);
+ HMMDEF hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left);
+ HMMDEF hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef HANDMADE_MATH_CPP_MODE
+
+HMMDEF float HMM_Length(hmm_vec2 A);
+HMMDEF float HMM_Length(hmm_vec3 A);
+HMMDEF float HMM_Length(hmm_vec4 A);
+
+HMMDEF float HMM_LengthSquared(hmm_vec2 A);
+HMMDEF float HMM_LengthSquared(hmm_vec3 A);
+HMMDEF float HMM_LengthSquared(hmm_vec4 A);
+
+HMMDEF hmm_vec2 HMM_Normalize(hmm_vec2 A);
+HMMDEF hmm_vec3 HMM_Normalize(hmm_vec3 A);
+HMMDEF hmm_vec4 HMM_Normalize(hmm_vec4 A);
+HMMDEF hmm_quaternion HMM_Normalize(hmm_quaternion A);
+
+HMMDEF float HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo);
+HMMDEF float HMM_Dot(hmm_vec3 VecOne, hmm_vec3 VecTwo);
+HMMDEF float HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo);
+HMMDEF float HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo);
+
+HMMDEF hmm_vec2 HMM_Add(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 HMM_Add(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 HMM_Add(hmm_vec4 Left, hmm_vec4 Right);
+HMMDEF hmm_mat4 HMM_Add(hmm_mat4 Left, hmm_mat4 Right);
+HMMDEF hmm_quaternion HMM_Add(hmm_quaternion Left, hmm_quaternion Right);
+
+HMMDEF hmm_vec2 HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 HMM_Subtract(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 HMM_Subtract(hmm_vec4 Left, hmm_vec4 Right);
+HMMDEF hmm_mat4 HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right);
+HMMDEF hmm_quaternion HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right);
+
+HMMDEF hmm_vec2 HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_vec2 HMM_Multiply(hmm_vec2 Left, float Right);
+HMMDEF hmm_vec3 HMM_Multiply(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_vec3 HMM_Multiply(hmm_vec3 Left, float Right);
+HMMDEF hmm_vec4 HMM_Multiply(hmm_vec4 Left, hmm_vec4 Right);
+HMMDEF hmm_vec4 HMM_Multiply(hmm_vec4 Left, float Right);
+HMMDEF hmm_mat4 HMM_Multiply(hmm_mat4 Left, hmm_mat4 Right);
+HMMDEF hmm_mat4 HMM_Multiply(hmm_mat4 Left, float Right);
+HMMDEF hmm_vec4 HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector);
+HMMDEF hmm_quaternion HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right);
+HMMDEF hmm_quaternion HMM_Multiply(hmm_quaternion Left, float Right);
+
+HMMDEF hmm_vec2 HMM_Divide(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_vec2 HMM_Divide(hmm_vec2 Left, float Right);
+HMMDEF hmm_vec3 HMM_Divide(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_vec3 HMM_Divide(hmm_vec3 Left, float Right);
+HMMDEF hmm_vec4 HMM_Divide(hmm_vec4 Left, hmm_vec4 Right);
+HMMDEF hmm_vec4 HMM_Divide(hmm_vec4 Left, float Right);
+HMMDEF hmm_mat4 HMM_Divide(hmm_mat4 Left, float Right);
+HMMDEF hmm_quaternion HMM_Divide(hmm_quaternion Left, hmm_quaternion Right);
+HMMDEF hmm_quaternion HMM_Divide(hmm_quaternion Left, float Right);
+
+HMMDEF hmm_bool HMM_Equals(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_bool HMM_Equals(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_bool HMM_Equals(hmm_vec4 Left, hmm_vec4 Right);
+
+HMMDEF hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 operator+(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 operator+(hmm_vec4 Left, hmm_vec4 Right);
+HMMDEF hmm_mat4 operator+(hmm_mat4 Left, hmm_mat4 Right);
+HMMDEF hmm_quaternion operator+(hmm_quaternion Left, hmm_quaternion Right);
+
+HMMDEF hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 operator-(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 operator-(hmm_vec4 Left, hmm_vec4 Right);
+HMMDEF hmm_mat4 operator-(hmm_mat4 Left, hmm_mat4 Right);
+HMMDEF hmm_quaternion operator-(hmm_quaternion Left, hmm_quaternion Right);
+
+HMMDEF hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 operator*(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 operator*(hmm_vec4 Left, hmm_vec4 Right);
+HMMDEF hmm_mat4 operator*(hmm_mat4 Left, hmm_mat4 Right);
+HMMDEF hmm_quaternion operator*(hmm_quaternion Left, hmm_quaternion Right);
+
+HMMDEF hmm_vec2 operator*(hmm_vec2 Left, float Right);
+HMMDEF hmm_vec3 operator*(hmm_vec3 Left, float Right);
+HMMDEF hmm_vec4 operator*(hmm_vec4 Left, float Right);
+HMMDEF hmm_mat4 operator*(hmm_mat4 Left, float Right);
+HMMDEF hmm_quaternion operator*(hmm_quaternion Left, float Right);
+
+HMMDEF hmm_vec2 operator*(float Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 operator*(float Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 operator*(float Left, hmm_vec4 Right);
+HMMDEF hmm_mat4 operator*(float Left, hmm_mat4 Right);
+HMMDEF hmm_quaternion operator*(float Left, hmm_quaternion Right);
+
+HMMDEF hmm_vec4 operator*(hmm_mat4 Matrix, hmm_vec4 Vector);
+
+HMMDEF hmm_vec2 operator/(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 operator/(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 operator/(hmm_vec4 Left, hmm_vec4 Right);
+
+HMMDEF hmm_vec2 operator/(hmm_vec2 Left, float Right);
+HMMDEF hmm_vec3 operator/(hmm_vec3 Left, float Right);
+HMMDEF hmm_vec4 operator/(hmm_vec4 Left, float Right);
+HMMDEF hmm_mat4 operator/(hmm_mat4 Left, float Right);
+HMMDEF hmm_quaternion operator/(hmm_quaternion Left, float Right);
+
+HMMDEF hmm_vec2 &operator+=(hmm_vec2 &Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 &operator+=(hmm_vec3 &Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 &operator+=(hmm_vec4 &Left, hmm_vec4 Right);
+HMMDEF hmm_mat4 &operator+=(hmm_mat4 &Left, hmm_mat4 Right);
+HMMDEF hmm_quaternion &operator+=(hmm_quaternion &Left, hmm_quaternion Right);
+
+HMMDEF hmm_vec2 &operator-=(hmm_vec2 &Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 &operator-=(hmm_vec3 &Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 &operator-=(hmm_vec4 &Left, hmm_vec4 Right);
+HMMDEF hmm_mat4 &operator-=(hmm_mat4 &Left, hmm_mat4 Right);
+HMMDEF hmm_quaternion &operator-=(hmm_quaternion &Left, hmm_quaternion Right);
+
+HMMDEF hmm_vec2 &operator*=(hmm_vec2 &Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 &operator*=(hmm_vec3 &Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 &operator*=(hmm_vec4 &Left, hmm_vec4 Right);
+
+HMMDEF hmm_vec2 &operator*=(hmm_vec2 &Left, float Right);
+HMMDEF hmm_vec3 &operator*=(hmm_vec3 &Left, float Right);
+HMMDEF hmm_vec4 &operator*=(hmm_vec4 &Left, float Right);
+HMMDEF hmm_mat4 &operator*=(hmm_mat4 &Left, float Right);
+HMMDEF hmm_quaternion &operator*=(hmm_quaternion &Left, float Right);
+
+HMMDEF hmm_vec2 &operator/=(hmm_vec2 &Left, hmm_vec2 Right);
+HMMDEF hmm_vec3 &operator/=(hmm_vec3 &Left, hmm_vec3 Right);
+HMMDEF hmm_vec4 &operator/=(hmm_vec4 &Left, hmm_vec4 Right);
+
+HMMDEF hmm_vec2 &operator/=(hmm_vec2 &Left, float Right);
+HMMDEF hmm_vec3 &operator/=(hmm_vec3 &Left, float Right);
+HMMDEF hmm_vec4 &operator/=(hmm_vec4 &Left, float Right);
+HMMDEF hmm_mat4 &operator/=(hmm_mat4 &Left, float Right);
+HMMDEF hmm_quaternion &operator/=(hmm_quaternion &Left, float Right);
+
+HMMDEF hmm_bool operator==(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_bool operator==(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_bool operator==(hmm_vec4 Left, hmm_vec4 Right);
+
+HMMDEF hmm_bool operator!=(hmm_vec2 Left, hmm_vec2 Right);
+HMMDEF hmm_bool operator!=(hmm_vec3 Left, hmm_vec3 Right);
+HMMDEF hmm_bool operator!=(hmm_vec4 Left, hmm_vec4 Right);
+
+HMMDEF hmm_mat4 HMM_Mat4Identity();
+HMMDEF hmm_vec2 HMM_LerpV2(hmm_vec2 A, float T, hmm_vec2 B);
+HMMDEF hmm_vec3 HMM_LerpV3(hmm_vec3 A, float T, hmm_vec3 B);
+HMMDEF hmm_vec4 HMM_LerpV4(hmm_vec4 A, float T, hmm_vec4 B);
+HMMDEF hmm_vec4 HMM_AverageVec3(hmm_vec4* Arr, unsigned int Len);
+HMMDEF hmm_vec4 HMM_AverageVec4(hmm_vec4* Arr, unsigned int Len);
+HMMDEF bool HMM_SameSide(hmm_vec3 P1, hmm_vec3 P2, hmm_vec3 A, hmm_vec3 B);
+HMMDEF bool HMM_PointInTriangle(hmm_vec3 P, hmm_vec3 T0, hmm_vec3 T1, hmm_vec3 T2);
+
+#endif /* HANDMADE_MATH_CPP */
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif /* HANDMADE_MATH_H */
+
+#ifdef HANDMADE_MATH_IMPLEMENTATION
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-field-initializers"
+#pragma clang diagnostic ignored "-Wmissing-braces"
+#endif
+
+HINLINE float
+HMM_SinF(float Angle)
+{
+ float Result = 0.0f;
+
+ Result = HMM_SINF(Angle);
+ return (Result);
+}
+
+HINLINE float
+HMM_CosF(float Angle)
+{
+ float Result = 0.0f;
+
+ Result = HMM_COSF(Angle);
+ return (Result);
+}
+
+HINLINE float
+HMM_TanF(float Radians)
+{
+ float Result = 0.0f;
+
+ Result = HMM_TANF(Radians);
+ return (Result);
+}
+
+HINLINE float
+HMM_ACosF(float Radians)
+{
+ float Result = 0.0f;
+
+ Result = HMM_ACOSF(Radians);
+ return (Result);
+}
+
+HINLINE float
+HMM_ATanF(float Radians)
+{
+ float Result = 0.0f;
+
+ Result = HMM_ATANF(Radians);
+ return (Result);
+}
+
+HINLINE float
+HMM_Atan2F(float Left, float Right)
+{
+ float Result = 0.0f;
+
+ Result = HMM_ATAN2F(Left, Right);
+ return (Result);
+}
+
+HINLINE float
+HMM_ExpF(float Float)
+{
+ float Result = 0.0f;
+
+ Result = HMM_EXPF(Float);
+ return (Result);
+}
+
+HINLINE float
+HMM_LogF(float Float)
+{
+ float Result = 0.0f;
+
+ Result = HMM_LOGF(Float);
+ return (Result);
+}
+
+HINLINE float
+HMM_ToRadians(float Degrees)
+{
+ float Result = 0.0f;
+
+ Result = Degrees * (HMM_PI32 / 180.0f);
+ return (Result);
+}
+
+HINLINE float
+HMM_SquareRootF(float Value)
+{
+ float Result = 0.0f;
+
+#ifdef HANDMADE_MATH__USE_SSE
+ __m128 In = _mm_set_ss(Value);
+ __m128 Out = _mm_sqrt_ss(In);
+ Result = _mm_cvtss_f32(Out);
+#else
+ Result = HMM_SQRTF(Value);
+#endif
+
+ return(Result);
+}
+
+HINLINE float
+HMM_RSquareRootF(float Value)
+{
+ float Result = 0.0f;
+
+#ifdef HANDMADE_MATH__USE_SSE
+ __m128 In = _mm_set_ss(Value);
+ __m128 Out = _mm_rsqrt_ss(In);
+ Result = _mm_cvtss_f32(Out);
+#else
+ Result = 1.0f/HMM_SquareRootF(Value);
+#endif
+
+ return(Result);
+}
+
+HINLINE float
+HMM_LengthSquaredVec2(hmm_vec2 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_DotVec2(A, A);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_LengthSquaredVec3(hmm_vec3 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_DotVec3(A, A);
+
+ return (Result);
+}
+
+HINLINE float
+HMM_LengthSquaredVec4(hmm_vec4 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_DotVec4(A, A);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_LengthVec2(hmm_vec2 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_SquareRootF(HMM_LengthSquaredVec2(A));
+
+ return(Result);
+}
+
+HINLINE float
+HMM_LengthVec3(hmm_vec3 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_SquareRootF(HMM_LengthSquaredVec3(A));
+
+ return (Result);
+}
+
+HINLINE float
+HMM_LengthVec4(hmm_vec4 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_SquareRootF(HMM_LengthSquaredVec4(A));
+
+ return(Result);
+}
+
+HINLINE float
+HMM_Power(float Base, int Exponent)
+{
+ float Result = 1.0f;
+ float Mul = Exponent < 0 ? 1.f / Base : Base;
+ int X = Exponent < 0 ? -Exponent : Exponent;
+ while (X)
+ {
+ if (X & 1)
+ {
+ Result *= Mul;
+ }
+
+ Mul *= Mul;
+ X >>= 1;
+ }
+
+ return (Result);
+}
+
+HINLINE float
+HMM_PowerF(float Base, float Exponent)
+{
+ return HMM_EXPF(Exponent * HMM_LOGF(Base));
+}
+
+HINLINE float
+HMM_Lerp(float A, float Time, float B)
+{
+ float Result = 0;
+
+ Result = (1.0f - Time) * A + Time * B;
+ return (Result);
+}
+
+HINLINE float
+HMM_Clamp(float Min, float Value, float Max)
+{
+ float Result = Value;
+
+ if(Result < Min)
+ {
+ Result = Min;
+ }
+ else if(Result > Max)
+ {
+ Result = Max;
+ }
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_NormalizeVec2(hmm_vec2 A)
+{
+ hmm_vec2 Result = {0};
+
+ float VectorLength = HMM_LengthVec2(A);
+
+ /* NOTE(kiljacken): We need a zero check to not divide-by-zero */
+ if (VectorLength != 0.0f)
+ {
+ Result.X = A.X * (1.0f / VectorLength);
+ Result.Y = A.Y * (1.0f / VectorLength);
+ }
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_NormalizeVec3(hmm_vec3 A)
+{
+ hmm_vec3 Result = {0};
+
+ float VectorLength = HMM_LengthVec3(A);
+
+ /* NOTE(kiljacken): We need a zero check to not divide-by-zero */
+ if (VectorLength != 0.0f)
+ {
+ Result.X = A.X * (1.0f / VectorLength);
+ Result.Y = A.Y * (1.0f / VectorLength);
+ Result.Z = A.Z * (1.0f / VectorLength);
+ }
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_NormalizeVec4(hmm_vec4 A)
+{
+ hmm_vec4 Result = {0};
+
+ float VectorLength = HMM_LengthVec4(A);
+
+ /* NOTE(kiljacken): We need a zero check to not divide-by-zero */
+ if (VectorLength != 0.0f)
+ {
+ Result.X = A.X * (1.0f / VectorLength);
+ Result.Y = A.Y * (1.0f / VectorLength);
+ Result.Z = A.Z * (1.0f / VectorLength);
+ Result.W = A.W * (1.0f / VectorLength);
+ }
+
+ return (Result);
+}
+
+HINLINE float
+HMM_DotVec2(hmm_vec2 VecOne, hmm_vec2 VecTwo)
+{
+ float Result = 0.0f;
+
+ Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y);
+
+ return (Result);
+}
+
+HINLINE float
+HMM_DotVec3(hmm_vec3 VecOne, hmm_vec3 VecTwo)
+{
+ float Result = 0.0f;
+
+ Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z);
+
+ return (Result);
+}
+
+HINLINE float
+HMM_DotVec4(hmm_vec4 VecOne, hmm_vec4 VecTwo)
+{
+ float Result = 0.0f;
+
+ Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z) + (VecOne.W * VecTwo.W);
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Cross(hmm_vec3 VecOne, hmm_vec3 VecTwo)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = (VecOne.Y * VecTwo.Z) - (VecOne.Z * VecTwo.Y);
+ Result.Y = (VecOne.Z * VecTwo.X) - (VecOne.X * VecTwo.Z);
+ Result.Z = (VecOne.X * VecTwo.Y) - (VecOne.Y * VecTwo.X);
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_Vec2(float X, float Y)
+{
+ hmm_vec2 Result = {0};
+
+ Result.X = X;
+ Result.Y = Y;
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_Vec2i(int X, int Y)
+{
+ hmm_vec2 Result = {0};
+
+ Result.X = (float)X;
+ Result.Y = (float)Y;
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Vec3(float X, float Y, float Z)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = X;
+ Result.Y = Y;
+ Result.Z = Z;
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Vec3i(int X, int Y, int Z)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = (float)X;
+ Result.Y = (float)Y;
+ Result.Z = (float)Z;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Vec4(float X, float Y, float Z, float W)
+{
+ hmm_vec4 Result = {0};
+
+ Result.X = X;
+ Result.Y = Y;
+ Result.Z = Z;
+ Result.W = W;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Vec4i(int X, int Y, int Z, int W)
+{
+ hmm_vec4 Result = {0};
+
+ Result.X = (float)X;
+ Result.Y = (float)Y;
+ Result.Z = (float)Z;
+ Result.W = (float)W;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Vec4v(hmm_vec3 Vector, float W)
+{
+ hmm_vec4 Result = {0};
+
+ Result.XYZ = Vector;
+ Result.W = W;
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_AddVec2(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result.X = Left.X + Right.X;
+ Result.Y = Left.Y + Right.Y;
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_AddVec3(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = Left.X + Right.X;
+ Result.Y = Left.Y + Right.Y;
+ Result.Z = Left.Z + Right.Z;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_AddVec4(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result.X = Left.X + Right.X;
+ Result.Y = Left.Y + Right.Y;
+ Result.Z = Left.Z + Right.Z;
+ Result.W = Left.W + Right.W;
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_SubtractVec2(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result.X = Left.X - Right.X;
+ Result.Y = Left.Y - Right.Y;
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_SubtractVec3(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = Left.X - Right.X;
+ Result.Y = Left.Y - Right.Y;
+ Result.Z = Left.Z - Right.Z;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_SubtractVec4(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result.X = Left.X - Right.X;
+ Result.Y = Left.Y - Right.Y;
+ Result.Z = Left.Z - Right.Z;
+ Result.W = Left.W - Right.W;
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_MultiplyVec2(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result.X = Left.X * Right.X;
+ Result.Y = Left.Y * Right.Y;
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_MultiplyVec2f(hmm_vec2 Left, float Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result.X = Left.X * Right;
+ Result.Y = Left.Y * Right;
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_MultiplyVec3(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = Left.X * Right.X;
+ Result.Y = Left.Y * Right.Y;
+ Result.Z = Left.Z * Right.Z;
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_MultiplyVec3f(hmm_vec3 Left, float Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = Left.X * Right;
+ Result.Y = Left.Y * Right;
+ Result.Z = Left.Z * Right;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_MultiplyVec4(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result.X = Left.X * Right.X;
+ Result.Y = Left.Y * Right.Y;
+ Result.Z = Left.Z * Right.Z;
+ Result.W = Left.W * Right.W;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_MultiplyVec4f(hmm_vec4 Left, float Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result.X = Left.X * Right;
+ Result.Y = Left.Y * Right;
+ Result.Z = Left.Z * Right;
+ Result.W = Left.W * Right;
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_DivideVec2(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result.X = Left.X / Right.X;
+ Result.Y = Left.Y / Right.Y;
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_DivideVec2f(hmm_vec2 Left, float Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result.X = Left.X / Right;
+ Result.Y = Left.Y / Right;
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_DivideVec3(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = Left.X / Right.X;
+ Result.Y = Left.Y / Right.Y;
+ Result.Z = Left.Z / Right.Z;
+
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_DivideVec3f(hmm_vec3 Left, float Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result.X = Left.X / Right;
+ Result.Y = Left.Y / Right;
+ Result.Z = Left.Z / Right;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_DivideVec4(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result.X = Left.X / Right.X;
+ Result.Y = Left.Y / Right.Y;
+ Result.Z = Left.Z / Right.Z;
+ Result.W = Left.W / Right.W;
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_DivideVec4f(hmm_vec4 Left, float Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result.X = Left.X / Right;
+ Result.Y = Left.Y / Right;
+ Result.Z = Left.Z / Right;
+ Result.W = Left.W / Right;
+
+ return (Result);
+}
+
+HINLINE hmm_bool
+HMM_EqualsVec2(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_bool Result = 0;
+
+ Result = (Left.X == Right.X && Left.Y == Right.Y);
+
+ return (Result);
+}
+
+HINLINE hmm_bool
+HMM_EqualsVec3(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_bool Result = 0;
+
+ Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z);
+
+ return (Result);
+}
+
+HINLINE hmm_bool
+HMM_EqualsVec4(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_bool Result = 0;
+
+ Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z && Left.W == Right.W);
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Mat4(void)
+{
+ hmm_mat4 Result = {0};
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Mat4d(float Diagonal)
+{
+ hmm_mat4 Result = HMM_Mat4();
+
+ Result.Elements[0][0] = Diagonal;
+ Result.Elements[1][1] = Diagonal;
+ Result.Elements[2][2] = Diagonal;
+ Result.Elements[3][3] = Diagonal;
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = HMM_Mat4();
+
+ int Columns;
+ for(Columns = 0; Columns < 4; ++Columns)
+ {
+ int Rows;
+ for(Rows = 0; Rows < 4; ++Rows)
+ {
+ Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows];
+ }
+ }
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = HMM_Mat4();
+
+ int Columns;
+ for(Columns = 0; Columns < 4; ++Columns)
+ {
+ int Rows;
+ for(Rows = 0; Rows < 4; ++Rows)
+ {
+ Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows];
+ }
+ }
+
+ return (Result);
+}
+
+#ifdef HANDMADE_MATH__USE_SSE
+HINLINE __m128
+HMM_LinearCombineSSE(__m128 Left, hmm_mat4 Right)
+{
+ __m128 Result = {};
+ Result = _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x00), Right.Rows[0]);
+ Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x55), Right.Rows[1]));
+ Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xaa), Right.Rows[2]));
+ Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xff), Right.Rows[3]));
+
+ return(Result);
+}
+#endif
+
+HINLINE hmm_mat4
+HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = HMM_Mat4();
+
+#ifdef HANDMADE_MATH__USE_SSE
+
+ hmm_mat4 TransposedLeft = HMM_Transpose(Left);
+ hmm_mat4 TransposedRight = HMM_Transpose(Right);
+
+ Result.Rows[0] = HMM_LinearCombineSSE(TransposedLeft.Rows[0], TransposedRight);
+ Result.Rows[1] = HMM_LinearCombineSSE(TransposedLeft.Rows[1], TransposedRight);
+ Result.Rows[2] = HMM_LinearCombineSSE(TransposedLeft.Rows[2], TransposedRight);
+ Result.Rows[3] = HMM_LinearCombineSSE(TransposedLeft.Rows[3], TransposedRight);
+
+ Result = HMM_Transpose(Result);
+
+#else
+ int Columns;
+ for(Columns = 0; Columns < 4; ++Columns)
+ {
+ int Rows;
+ for(Rows = 0; Rows < 4; ++Rows)
+ {
+ float Sum = 0;
+ int CurrentMatrice;
+ for(CurrentMatrice = 0; CurrentMatrice < 4; ++CurrentMatrice)
+ {
+ Sum += Left.Elements[CurrentMatrice][Rows] * Right.Elements[Columns][CurrentMatrice];
+ }
+
+ Result.Elements[Columns][Rows] = Sum;
+ }
+ }
+#endif
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar)
+{
+ hmm_mat4 Result = HMM_Mat4();
+
+ int Columns;
+ for(Columns = 0; Columns < 4; ++Columns)
+ {
+ int Rows;
+ for(Rows = 0; Rows < 4; ++Rows)
+ {
+ Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar;
+ }
+ }
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector)
+{
+ hmm_vec4 Result = {0};
+
+ int Columns, Rows;
+ for(Rows = 0; Rows < 4; ++Rows)
+ {
+ float Sum = 0;
+ for(Columns = 0; Columns < 4; ++Columns)
+ {
+ Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns];
+ }
+
+ Result.Elements[Rows] = Sum;
+ }
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar)
+{
+ hmm_mat4 Result = HMM_Mat4();
+
+ int Columns;
+ for(Columns = 0; Columns < 4; ++Columns)
+ {
+ int Rows;
+ for(Rows = 0; Rows < 4; ++Rows)
+ {
+ Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar;
+ }
+ }
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Transpose(hmm_mat4 Matrix)
+{
+ hmm_mat4 Result = HMM_Mat4();
+
+#ifdef HANDMADE_MATH__USE_SSE
+ Result = Matrix;
+
+ _MM_TRANSPOSE4_PS(Result.Rows[0], Result.Rows[1], Result.Rows[2], Result.Rows[3]);
+#else
+ int Columns;
+ for(Columns = 0; Columns < 4; ++Columns)
+ {
+ int Rows;
+ for(Rows = 0; Rows < 4; ++Rows)
+ {
+ Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows];
+ }
+ }
+#endif
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far)
+{
+ hmm_mat4 Result = HMM_Mat4d(1.0f);
+
+ Result.Elements[0][0] = 2.0f / (Right - Left);
+ Result.Elements[1][1] = 2.0f / (Top - Bottom);
+ Result.Elements[2][2] = 2.0f / (Near - Far);
+
+ Result.Elements[3][0] = (Left + Right) / (Left - Right);
+ Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top);
+ Result.Elements[3][2] = (Far + Near) / (Near - Far);
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Perspective(float FOV, float AspectRatio, float Near, float Far)
+{
+ hmm_mat4 Result = HMM_Mat4d(1.0f);
+
+ float TanThetaOver2 = HMM_TanF(FOV * (HMM_PI32 / 360.0f));
+
+ Result.Elements[0][0] = 1.0f / TanThetaOver2;
+ Result.Elements[1][1] = AspectRatio / TanThetaOver2;
+ Result.Elements[2][3] = -1.0f;
+ Result.Elements[2][2] = (Near + Far) / (Near - Far);
+ Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far);
+ Result.Elements[3][3] = 0.0f;
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Translate(hmm_vec3 Translation)
+{
+ hmm_mat4 Result = HMM_Mat4d(1.0f);
+
+ Result.Elements[3][0] = Translation.X;
+ Result.Elements[3][1] = Translation.Y;
+ Result.Elements[3][2] = Translation.Z;
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Rotate(float Angle, hmm_vec3 Axis)
+{
+ hmm_mat4 Result = HMM_Mat4d(1.0f);
+
+ Axis = HMM_NormalizeVec3(Axis);
+
+ float SinTheta = HMM_SinF(HMM_ToRadians(Angle));
+ float CosTheta = HMM_CosF(HMM_ToRadians(Angle));
+ float CosValue = 1.0f - CosTheta;
+
+ Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta;
+ Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta);
+ Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta);
+
+ Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta);
+ Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta;
+ Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta);
+
+ Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta);
+ Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta);
+ Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta;
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Scale(hmm_vec3 Scale)
+{
+ hmm_mat4 Result = HMM_Mat4d(1.0f);
+
+ Result.Elements[0][0] = Scale.X;
+ Result.Elements[1][1] = Scale.Y;
+ Result.Elements[2][2] = Scale.Z;
+
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up)
+{
+ hmm_mat4 Result = {0};
+
+ hmm_vec3 F = HMM_NormalizeVec3(HMM_SubtractVec3(Center, Eye));
+ hmm_vec3 S = HMM_NormalizeVec3(HMM_Cross(F, Up));
+ hmm_vec3 U = HMM_Cross(S, F);
+
+ Result.Elements[0][0] = S.X;
+ Result.Elements[0][1] = U.X;
+ Result.Elements[0][2] = -F.X;
+
+ Result.Elements[1][0] = S.Y;
+ Result.Elements[1][1] = U.Y;
+ Result.Elements[1][2] = -F.Y;
+
+ Result.Elements[2][0] = S.Z;
+ Result.Elements[2][1] = U.Z;
+ Result.Elements[2][2] = -F.Z;
+
+ Result.Elements[3][0] = -HMM_DotVec3(S, Eye);
+ Result.Elements[3][1] = -HMM_DotVec3(U, Eye);
+ Result.Elements[3][2] = HMM_DotVec3(F, Eye);
+ Result.Elements[3][3] = 1.0f;
+
+ return (Result);
+}
+
+
+HINLINE hmm_quaternion
+HMM_Quaternion(float X, float Y, float Z, float W)
+{
+ hmm_quaternion Result = {0};
+
+ Result.X = X;
+ Result.Y = Y;
+ Result.Z = Z;
+ Result.W = W;
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_QuaternionV4(hmm_vec4 Vector)
+{
+ hmm_quaternion Result = {0};
+
+ Result.X = Vector.X;
+ Result.Y = Vector.Y;
+ Result.Z = Vector.Z;
+ Result.W = Vector.W;
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result.X = Left.X + Right.X;
+ Result.Y = Left.Y + Right.Y;
+ Result.Z = Left.Z + Right.Z;
+ Result.W = Left.W + Right.W;
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result.X = Left.X - Right.X;
+ Result.Y = Left.Y - Right.Y;
+ Result.Z = Left.Z - Right.Z;
+ Result.W = Left.W - Right.W;
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result.X = (Left.X * Right.W) + (Left.Y * Right.Z) - (Left.Z * Right.Y) + (Left.W * Right.X);
+ Result.Y = (-Left.X * Right.Z) + (Left.Y * Right.W) + (Left.Z * Right.X) + (Left.W * Right.Y);
+ Result.Z = (Left.X * Right.Y) - (Left.Y * Right.X) + (Left.Z * Right.W) + (Left.W * Right.Z);
+ Result.W = (-Left.X * Right.X) - (Left.Y * Right.Y) - (Left.Z * Right.Z) + (Left.W * Right.W);
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative)
+{
+ hmm_quaternion Result = {0};
+
+ Result.X = Left.X * Multiplicative;
+ Result.Y = Left.Y * Multiplicative;
+ Result.Z = Left.Z * Multiplicative;
+ Result.W = Left.W * Multiplicative;
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend)
+{
+ hmm_quaternion Result = {0};
+
+ Result.X = Left.X / Dividend;
+ Result.Y = Left.Y / Dividend;
+ Result.Z = Left.Z / Dividend;
+ Result.W = Left.W / Dividend;
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_InverseQuaternion(hmm_quaternion Left)
+{
+ hmm_quaternion Conjugate = {0};
+ hmm_quaternion Result = {0};
+ float Norm = 0;
+ float NormSquared = 0;
+
+ Conjugate.X = -Left.X;
+ Conjugate.Y = -Left.Y;
+ Conjugate.Z = -Left.Z;
+ Conjugate.W = Left.W;
+
+ Norm = HMM_SquareRootF(HMM_DotQuaternion(Left, Left));
+ NormSquared = Norm * Norm;
+
+ Result.X = Conjugate.X / NormSquared;
+ Result.Y = Conjugate.Y / NormSquared;
+ Result.Z = Conjugate.Z / NormSquared;
+ Result.W = Conjugate.W / NormSquared;
+
+ return(Result);
+}
+
+HINLINE float
+HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right)
+{
+ float Result = 0.0f;
+
+ Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W);
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_NormalizeQuaternion(hmm_quaternion Left)
+{
+ hmm_quaternion Result;
+
+ float Length = HMM_SquareRootF(HMM_DotQuaternion(Left, Left));
+ Result = HMM_DivideQuaternionF(Left, Length);
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result.X = HMM_Lerp(Left.X, Time, Right.X);
+ Result.Y = HMM_Lerp(Left.Y, Time, Right.Y);
+ Result.Z = HMM_Lerp(Left.Z, Time, Right.Z);
+ Result.W = HMM_Lerp(Left.W, Time, Right.W);
+
+ Result = HMM_NormalizeQuaternion(Result);
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right)
+{
+ hmm_quaternion Result;
+ hmm_quaternion QuaternionLeft;
+ hmm_quaternion QuaternionRight;
+
+ float Cos_Theta = HMM_DotQuaternion(Left, Right);
+ float Angle = HMM_ACosF(Cos_Theta);
+
+ float S1 = HMM_SinF((1.0f - Time) * Angle);
+ float S2 = HMM_SinF(Time * Angle);
+ float Is = 1.0f / HMM_SinF(Angle);
+
+ QuaternionLeft = HMM_MultiplyQuaternionF(Left, S1);
+ QuaternionRight = HMM_MultiplyQuaternionF(Right, S2);
+
+ Result = HMM_AddQuaternion(QuaternionLeft, QuaternionRight);
+ Result = HMM_MultiplyQuaternionF(Result, Is);
+
+ return(Result);
+}
+
+HINLINE hmm_mat4
+HMM_QuaternionToMat4(hmm_quaternion Left)
+{
+ hmm_mat4 Result;
+ Result = HMM_Mat4d(1);
+
+ hmm_quaternion NormalizedQuaternion = HMM_NormalizeQuaternion(Left);
+
+ float XX, YY, ZZ,
+ XY, XZ, YZ,
+ WX, WY, WZ;
+
+ XX = NormalizedQuaternion.X * NormalizedQuaternion.X;
+ YY = NormalizedQuaternion.Y * NormalizedQuaternion.Y;
+ ZZ = NormalizedQuaternion.Z * NormalizedQuaternion.Z;
+ XY = NormalizedQuaternion.X * NormalizedQuaternion.Y;
+ XZ = NormalizedQuaternion.X * NormalizedQuaternion.Z;
+ YZ = NormalizedQuaternion.Y * NormalizedQuaternion.Z;
+ WX = NormalizedQuaternion.W * NormalizedQuaternion.X;
+ WY = NormalizedQuaternion.W * NormalizedQuaternion.Y;
+ WZ = NormalizedQuaternion.W * NormalizedQuaternion.Z;
+
+ Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ);
+ Result.Elements[0][1] = 2.0f * (XY + WZ);
+ Result.Elements[0][2] = 2.0f * (XZ - WY);
+
+ Result.Elements[1][0] = 2.0f * (XY - WZ);
+ Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ);
+ Result.Elements[1][2] = 2.0f * (YZ + WX);
+
+ Result.Elements[2][0] = 2.0f * (XZ + WY);
+ Result.Elements[2][1] = 2.0f * (YZ - WX);
+ Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY);
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation)
+{
+ hmm_quaternion Result = {0};
+ float AxisNorm = 0;
+ float SineOfRotation = 0;
+ hmm_vec3 RotatedVector;
+
+ AxisNorm = HMM_SquareRootF(HMM_DotVec3(Axis, Axis));
+ SineOfRotation = HMM_SinF(AngleOfRotation / 2.0f);
+ RotatedVector = HMM_MultiplyVec3f(Axis, SineOfRotation);
+
+ Result.W = HMM_CosF(AngleOfRotation / 2.0f);
+ Result.XYZ = HMM_DivideVec3f(RotatedVector, AxisNorm);
+
+ return(Result);
+}
+
+// Extensions
+
+HINLINE hmm_mat4
+HMM_Mat4Identity()
+{
+ hmm_mat4 Result = {};
+
+ Result.Elements[0][0] = 1;
+ Result.Elements[0][1] = 0;
+ Result.Elements[0][2] = 0;
+ Result.Elements[0][3] = 0;
+
+ Result.Elements[1][0] = 0;
+ Result.Elements[1][1] = 1;
+ Result.Elements[1][2] = 0;
+ Result.Elements[1][3] = 0;
+
+ Result.Elements[2][0] = 0;
+ Result.Elements[2][1] = 0;
+ Result.Elements[2][2] = 1;
+ Result.Elements[2][3] = 0;
+
+ Result.Elements[3][0] = 0;
+ Result.Elements[3][1] = 0;
+ Result.Elements[3][2] = 0;
+ Result.Elements[3][3] = 1;
+
+ return Result;
+}
+
+#define HMMToV2(v) (v2){ (v).X, (v).Y }
+#define HMMToV3(v) (v3){ (v).X, (v).Y, (v).Z }
+#define HMMToV4(v) (v4){ (v).X, (v).Y, (v).Z, (v).W }
+
+#define ToHMMV2(v) (hmm_vec2){ (v).x, (v).y }
+#define ToHMMV3(v) (hmm_vec3){ (v).x, (v).y, (v).z }
+#define ToHMMV4(v) (hmm_vec4){ (v).x, (v).y, (v).z, (v).w }
+
+HINLINE hmm_vec2
+HMM_LerpV2(hmm_vec2 A, float T, hmm_vec2 B)
+{
+ hmm_vec2 Result = {};
+ Result.X = HMM_Lerp(A.X, T, B.X);
+ Result.Y = HMM_Lerp(A.Y, T, B.Y);
+ return Result;
+}
+HINLINE hmm_vec3
+HMM_LerpV3(hmm_vec3 A, float T, hmm_vec3 B)
+{
+ hmm_vec3 Result = {};
+ Result.X = HMM_Lerp(A.X, T, B.X);
+ Result.Y = HMM_Lerp(A.Y, T, B.Y);
+ Result.Z = HMM_Lerp(A.Z, T, B.Z);
+ return Result;
+}
+HINLINE hmm_vec4
+HMM_LerpV4(hmm_vec4 A, float T, hmm_vec4 B)
+{
+ hmm_vec4 Result = {};
+ Result.X = HMM_Lerp(A.X, T, B.X);
+ Result.Y = HMM_Lerp(A.Y, T, B.Y);
+ Result.Z = HMM_Lerp(A.Z, T, B.Z);
+ Result.W = HMM_Lerp(A.W, T, B.W);
+ return Result;
+}
+
+// Range
+
+static bool
+HMM_V2RContains(hmm_v2r Range, hmm_v2 P)
+{
+ return (Range.ValueMin.x <= P.x &&
+ Range.ValueMin.y <= P.y &&
+ Range.ValueMax.x >= P.x &&
+ Range.ValueMax.y >= P.y);
+}
+
+static hmm_v2
+HMM_V2RSize(hmm_v2r Range)
+{
+ hmm_v2 Result = {};
+ Result.x = Range.ValueMax.x - Range.ValueMin.x;
+ Result.y = Range.ValueMax.y - Range.ValueMin.y;
+ return Result;
+}
+
+#define HMM_V2RWidth(r) HMM_V2RSize(r).x
+#define HMM_V2RHeight(r) HMM_V2RSize(r).y
+
+static hmm_v2r
+HMM_V2ROffset(hmm_v2r Range, hmm_v2 Offset)
+{
+ hmm_v2r Result = {};
+ Result.ValueMin = HMM_AddVec2(Range.ValueMin, Offset);
+ Result.ValueMax = HMM_AddVec2(Range.ValueMax, Offset);
+ return Result;
+}
+
+// Geometry
+
+static hmm_vec3
+HMM_AverageVec3(hmm_vec3* Arr, int Len)
+{
+ hmm_vec3 Total = {};
+ for (int i = 0; i < Len; i++)
+ {
+ Total = HMM_AddVec3(Total, Arr[i]);
+ }
+
+ hmm_vec3 Result = {};
+ Result.x = Total.x / Len;
+ Result.y = Total.y / Len;
+ Result.z = Total.z / Len;
+
+ return Result;
+}
+
+static hmm_vec4
+HMM_AverageVec4(hmm_vec4* Arr, int Len)
+{
+ hmm_vec4 Total = {};
+ for (int i = 0; i < Len; i++)
+ {
+ Total = HMM_AddVec4(Total, Arr[i]);
+ }
+
+ hmm_vec4 Result = {};
+ Result.x = Total.x / Len;
+ Result.y = Total.y / Len;
+ Result.z = Total.z / Len;
+ Result.w = Total.w / Len;
+ return Result;
+}
+
+// Point In Triangle From: https://blackpawn.com/texts/pointinpoly/default.html
+static bool
+HMM_SameSide(hmm_vec3 P1, hmm_vec3 P2, hmm_vec3 A, hmm_vec3 B)
+{
+ hmm_vec3 BA = HMM_SubtractVec3(B, A);
+ hmm_vec3 P1A = HMM_SubtractVec3(P1, A);
+ hmm_vec3 P2A = HMM_SubtractVec3(P2, A);
+ hmm_vec3 CP1 = HMM_Cross(BA, P1A);
+ hmm_vec3 CP2 = HMM_Cross(BA, P2A);
+ return (HMM_DotVec3(CP1, CP2) >= 0);
+}
+
+static bool
+HMM_PointInTriangle(hmm_vec3 P, hmm_vec3 T0, hmm_vec3 T1, hmm_vec3 T2)
+{
+ return (HMM_SameSide(P, T0, T1, T2) &&
+ HMM_SameSide(P, T1, T0, T2) &&
+ HMM_SameSide(P, T2, T0, T1));
+}
+
+#ifdef HANDMADE_MATH_CPP_MODE
+
+HINLINE float
+HMM_Length(hmm_vec2 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_LengthVec2(A);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_Length(hmm_vec3 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_LengthVec3(A);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_Length(hmm_vec4 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_LengthVec4(A);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_LengthSquared(hmm_vec2 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_LengthSquaredVec2(A);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_LengthSquared(hmm_vec3 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_LengthSquaredVec3(A);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_LengthSquared(hmm_vec4 A)
+{
+ float Result = 0.0f;
+
+ Result = HMM_LengthSquaredVec4(A);
+
+ return(Result);
+}
+
+HINLINE hmm_vec2
+HMM_Normalize(hmm_vec2 A)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_NormalizeVec2(A);
+
+ return(Result);
+}
+
+HINLINE hmm_vec3
+HMM_Normalize(hmm_vec3 A)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_NormalizeVec3(A);
+
+ return(Result);
+}
+
+HINLINE hmm_vec4
+HMM_Normalize(hmm_vec4 A)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_NormalizeVec4(A);
+
+ return(Result);
+}
+
+HINLINE hmm_quaternion
+HMM_Normalize(hmm_quaternion A)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_NormalizeQuaternion(A);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo)
+{
+ float Result = 0;
+
+ Result = HMM_DotVec2(VecOne, VecTwo);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_Dot(hmm_vec3 VecOne, hmm_vec3 VecTwo)
+{
+ float Result = 0;
+
+ Result = HMM_DotVec3(VecOne, VecTwo);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo)
+{
+ float Result = 0;
+
+ Result = HMM_DotVec4(VecOne, VecTwo);
+
+ return(Result);
+}
+
+HINLINE float
+HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo)
+{
+ float Result = 0;
+
+ Result = HMM_DotQuaternion(QuatOne, QuatTwo);
+
+ return(Result);
+}
+
+HINLINE hmm_vec2
+HMM_Add(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_AddVec2(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Add(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_AddVec3(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Add(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_AddVec4(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Add(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_AddMat4(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+HMM_Add(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_AddQuaternion(Left, Right);
+ return(Result);
+}
+
+HINLINE hmm_vec2
+HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_SubtractVec2(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Subtract(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_SubtractVec3(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Subtract(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_SubtractVec4(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_SubtractMat4(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_SubtractQuaternion(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_MultiplyVec2(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_Multiply(hmm_vec2 Left, float Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_MultiplyVec2f(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Multiply(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_MultiplyVec3(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Multiply(hmm_vec3 Left, float Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_MultiplyVec3f(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Multiply(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_MultiplyVec4(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Multiply(hmm_vec4 Left, float Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_MultiplyVec4f(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Multiply(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_MultiplyMat4(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Multiply(hmm_mat4 Left, float Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_MultiplyMat4f(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_MultiplyMat4ByVec4(Matrix, Vector);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_MultiplyQuaternion(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+HMM_Multiply(hmm_quaternion Left, float Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_MultiplyQuaternionF(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+HMM_Multiply(float Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_MultiplyQuaternionF(Right, Left);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_Divide(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_DivideVec2(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+HMM_Divide(hmm_vec2 Left, float Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_DivideVec2f(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Divide(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_DivideVec3(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+HMM_Divide(hmm_vec3 Left, float Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_DivideVec3f(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Divide(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_DivideVec4(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+HMM_Divide(hmm_vec4 Left, float Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_DivideVec4f(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+HMM_Divide(hmm_mat4 Left, float Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_DivideMat4f(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+HMM_Divide(hmm_quaternion Left, float Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_DivideQuaternionF(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_bool
+HMM_Equals(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_bool Result = 0;
+
+ Result = HMM_EqualsVec2(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_bool
+HMM_Equals(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_bool Result = 0;
+
+ Result = HMM_EqualsVec3(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_bool
+HMM_Equals(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_bool Result = 0;
+
+ Result = HMM_EqualsVec4(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+operator+(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_Add(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+operator+(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_Add(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+operator+(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_Add(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+operator+(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_Add(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+operator+(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_Add(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+operator-(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_Subtract(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+operator-(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_Subtract(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+operator-(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_Subtract(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+operator-(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_Subtract(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+operator-(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_Subtract(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+operator*(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+operator*(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+operator*(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = HMM_Multiply(Left, Right);
+
+ return (Result);
+}
+
+HINLINE hmm_vec2
+operator*(hmm_vec2 Left, float Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+operator*(hmm_vec3 Left, float Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+operator*(hmm_vec4 Left, float Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+operator*(hmm_mat4 Left, float Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+operator*(float Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_Multiply(Right, Left);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+operator*(float Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_Multiply(Right, Left);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+operator*(float Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_Multiply(Right, Left);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+operator*(float Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_Multiply(Right, Left);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+operator*(hmm_mat4 Left, hmm_mat4 Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+operator*(hmm_mat4 Matrix, hmm_vec4 Vector)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_Multiply(Matrix, Vector);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+operator*(hmm_quaternion Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+operator*(hmm_quaternion Left, float Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_Multiply(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+operator*(float Left, hmm_quaternion Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_Multiply(Right, Left);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+operator/(hmm_vec2 Left, hmm_vec2 Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_Divide(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+operator/(hmm_vec3 Left, hmm_vec3 Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_Divide(Left, Right);
+
+ return (Result);
+}
+
+HINLINE hmm_vec4
+operator/(hmm_vec4 Left, hmm_vec4 Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_Divide(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2
+operator/(hmm_vec2 Left, float Right)
+{
+ hmm_vec2 Result = {0};
+
+ Result = HMM_Divide(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec3
+operator/(hmm_vec3 Left, float Right)
+{
+ hmm_vec3 Result = {0};
+
+ Result = HMM_Divide(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec4
+operator/(hmm_vec4 Left, float Right)
+{
+ hmm_vec4 Result = {0};
+
+ Result = HMM_Divide(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_mat4
+operator/(hmm_mat4 Left, float Right)
+{
+ hmm_mat4 Result = {0};
+
+ Result = HMM_Divide(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_quaternion
+operator/(hmm_quaternion Left, float Right)
+{
+ hmm_quaternion Result = {0};
+
+ Result = HMM_Divide(Left, Right);
+ return (Result);
+}
+
+HINLINE hmm_vec2 &
+operator+=(hmm_vec2 &Left, hmm_vec2 Right)
+{
+ return (Left = Left + Right);
+}
+
+HINLINE hmm_vec3 &
+operator+=(hmm_vec3 &Left, hmm_vec3 Right)
+{
+ return (Left = Left + Right);
+}
+
+HINLINE hmm_vec4 &
+operator+=(hmm_vec4 &Left, hmm_vec4 Right)
+{
+ return (Left = Left + Right);
+}
+
+HINLINE hmm_mat4 &
+operator+=(hmm_mat4 &Left, hmm_mat4 Right)
+{
+ return (Left = Left + Right);
+}
+
+HINLINE hmm_quaternion &
+operator+=(hmm_quaternion &Left, hmm_quaternion Right)
+{
+ return (Left = Left + Right);
+}
+
+HINLINE hmm_vec2 &
+operator-=(hmm_vec2 &Left, hmm_vec2 Right)
+{
+ return (Left = Left - Right);
+}
+
+HINLINE hmm_vec3 &
+operator-=(hmm_vec3 &Left, hmm_vec3 Right)
+{
+ return (Left = Left - Right);
+}
+
+HINLINE hmm_vec4 &
+operator-=(hmm_vec4 &Left, hmm_vec4 Right)
+{
+ return (Left = Left - Right);
+}
+
+HINLINE hmm_mat4 &
+operator-=(hmm_mat4 &Left, hmm_mat4 Right)
+{
+ return (Left = Left - Right);
+}
+
+HINLINE hmm_quaternion &
+operator-=(hmm_quaternion &Left, hmm_quaternion Right)
+{
+ return (Left = Left - Right);
+}
+
+HINLINE hmm_vec2 &
+operator/=(hmm_vec2 &Left, hmm_vec2 Right)
+{
+ return (Left = Left / Right);
+}
+
+HINLINE hmm_vec3 &
+operator/=(hmm_vec3 &Left, hmm_vec3 Right)
+{
+ return (Left = Left / Right);
+}
+
+HINLINE hmm_vec4 &
+operator/=(hmm_vec4 &Left, hmm_vec4 Right)
+{
+ return (Left = Left / Right);
+}
+
+HINLINE hmm_vec2 &
+operator/=(hmm_vec2 &Left, float Right)
+{
+ return (Left = Left / Right);
+}
+
+HINLINE hmm_vec3 &
+operator/=(hmm_vec3 &Left, float Right)
+{
+ return (Left = Left / Right);
+}
+
+HINLINE hmm_vec4 &
+operator/=(hmm_vec4 &Left, float Right)
+{
+ return (Left = Left / Right);
+}
+
+HINLINE hmm_mat4 &
+operator/=(hmm_mat4 &Left, float Right)
+{
+ return (Left = Left / Right);
+}
+
+HINLINE hmm_quaternion &
+operator/=(hmm_quaternion &Left, float Right)
+{
+ return (Left = Left / Right);
+}
+
+HINLINE hmm_vec2 &
+operator*=(hmm_vec2 &Left, hmm_vec2 Right)
+{
+ return (Left = Left * Right);
+}
+
+HINLINE hmm_vec3 &
+operator*=(hmm_vec3 &Left, hmm_vec3 Right)
+{
+ return (Left = Left * Right);
+}
+
+HINLINE hmm_vec4 &
+operator*=(hmm_vec4 &Left, hmm_vec4 Right)
+{
+ return (Left = Left * Right);
+}
+
+HINLINE hmm_vec2 &
+operator*=(hmm_vec2 &Left, float Right)
+{
+ return (Left = Left * Right);
+}
+
+HINLINE hmm_vec3 &
+operator*=(hmm_vec3 &Left, float Right)
+{
+ return (Left = Left * Right);
+}
+
+HINLINE hmm_vec4 &
+operator*=(hmm_vec4 &Left, float Right)
+{
+ return (Left = Left * Right);
+}
+
+HINLINE hmm_mat4 &
+operator*=(hmm_mat4 &Left, float Right)
+{
+ return (Left = Left * Right);
+}
+
+HINLINE hmm_quaternion &
+operator*=(hmm_quaternion &Left, float Right)
+{
+ return (Left = Left * Right);
+}
+
+HINLINE hmm_bool
+operator==(hmm_vec2 Left, hmm_vec2 Right)
+{
+ return HMM_EqualsVec2(Left, Right);
+}
+
+HINLINE hmm_bool
+operator==(hmm_vec3 Left, hmm_vec3 Right)
+{
+ return HMM_EqualsVec3(Left, Right);
+}
+
+HINLINE hmm_bool
+operator==(hmm_vec4 Left, hmm_vec4 Right)
+{
+ return HMM_EqualsVec4(Left, Right);
+}
+
+
+HINLINE hmm_bool
+operator!=(hmm_vec2 Left, hmm_vec2 Right)
+{
+ return !HMM_EqualsVec2(Left, Right);
+}
+
+HINLINE hmm_bool
+operator!=(hmm_vec3 Left, hmm_vec3 Right)
+{
+ return !HMM_EqualsVec3(Left, Right);
+}
+
+HINLINE hmm_bool
+operator!=(hmm_vec4 Left, hmm_vec4 Right)
+{
+ return !HMM_EqualsVec4(Left, Right);
+}
+
+#endif /* HANDMADE_MATH_CPP_MODE */
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+#endif /* HANDMADE_MATH_IMPLEMENTATION */
diff --git a/src_v2/libs/glfw_osx/include/GLFW/glfw3.h b/src_v2/libs/glfw_osx/include/GLFW/glfw3.h
new file mode 100644
index 0000000..d074de1
--- /dev/null
+++ b/src_v2/libs/glfw_osx/include/GLFW/glfw3.h
@@ -0,0 +1,5913 @@
+/*************************************************************************
+ * GLFW 3.3 - www.glfw.org
+ * A library for OpenGL, window and input
+ *------------------------------------------------------------------------
+ * Copyright (c) 2002-2006 Marcus Geelnard
+ * Copyright (c) 2006-2019 Camilla Löwy
+ *
+ * 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.
+ *
+ *************************************************************************/
+
+#ifndef _glfw3_h_
+#define _glfw3_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************************************************************************
+ * Doxygen documentation
+ *************************************************************************/
+
+/*! @file glfw3.h
+ * @brief The header of the GLFW 3 API.
+ *
+ * This is the header file of the GLFW 3 API. It defines all its types and
+ * declares all its functions.
+ *
+ * For more information about how to use this file, see @ref build_include.
+ */
+/*! @defgroup context Context reference
+ * @brief Functions and types related to OpenGL and OpenGL ES contexts.
+ *
+ * This is the reference documentation for OpenGL and OpenGL ES context related
+ * functions. For more task-oriented information, see the @ref context_guide.
+ */
+/*! @defgroup vulkan Vulkan support reference
+ * @brief Functions and types related to Vulkan.
+ *
+ * This is the reference documentation for Vulkan related functions and types.
+ * For more task-oriented information, see the @ref vulkan_guide.
+ */
+/*! @defgroup init Initialization, version and error reference
+ * @brief Functions and types related to initialization and error handling.
+ *
+ * This is the reference documentation for initialization and termination of
+ * the library, version management and error handling. For more task-oriented
+ * information, see the @ref intro_guide.
+ */
+/*! @defgroup input Input reference
+ * @brief Functions and types related to input handling.
+ *
+ * This is the reference documentation for input related functions and types.
+ * For more task-oriented information, see the @ref input_guide.
+ */
+/*! @defgroup monitor Monitor reference
+ * @brief Functions and types related to monitors.
+ *
+ * This is the reference documentation for monitor related functions and types.
+ * For more task-oriented information, see the @ref monitor_guide.
+ */
+/*! @defgroup window Window reference
+ * @brief Functions and types related to windows.
+ *
+ * This is the reference documentation for window related functions and types,
+ * including creation, deletion and event polling. For more task-oriented
+ * information, see the @ref window_guide.
+ */
+
+
+/*************************************************************************
+ * Compiler- and platform-specific preprocessor work
+ *************************************************************************/
+
+/* If we are we on Windows, we want a single define for it.
+ */
+#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__))
+ #define _WIN32
+#endif /* _WIN32 */
+
+/* Include because most Windows GLU headers need wchar_t and
+ * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h.
+ * Include it unconditionally to avoid surprising side-effects.
+ */
+#include
+
+/* Include because it is needed by Vulkan and related functions.
+ * Include it unconditionally to avoid surprising side-effects.
+ */
+#include
+
+#if defined(GLFW_INCLUDE_VULKAN)
+ #include
+#endif /* Vulkan header */
+
+/* The Vulkan header may have indirectly included windows.h (because of
+ * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it.
+ */
+
+/* It is customary to use APIENTRY for OpenGL function pointer declarations on
+ * all platforms. Additionally, the Windows OpenGL header needs APIENTRY.
+ */
+#if !defined(APIENTRY)
+ #if defined(_WIN32)
+ #define APIENTRY __stdcall
+ #else
+ #define APIENTRY
+ #endif
+ #define GLFW_APIENTRY_DEFINED
+#endif /* APIENTRY */
+
+/* Some Windows OpenGL headers need this.
+ */
+#if !defined(WINGDIAPI) && defined(_WIN32)
+ #define WINGDIAPI __declspec(dllimport)
+ #define GLFW_WINGDIAPI_DEFINED
+#endif /* WINGDIAPI */
+
+/* Some Windows GLU headers need this.
+ */
+#if !defined(CALLBACK) && defined(_WIN32)
+ #define CALLBACK __stdcall
+ #define GLFW_CALLBACK_DEFINED
+#endif /* CALLBACK */
+
+/* Include the chosen OpenGL or OpenGL ES headers.
+ */
+#if defined(GLFW_INCLUDE_ES1)
+
+ #include
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include
+ #endif
+
+#elif defined(GLFW_INCLUDE_ES2)
+
+ #include
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include
+ #endif
+
+#elif defined(GLFW_INCLUDE_ES3)
+
+ #include
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include
+ #endif
+
+#elif defined(GLFW_INCLUDE_ES31)
+
+ #include
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include
+ #endif
+
+#elif defined(GLFW_INCLUDE_ES32)
+
+ #include
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include
+ #endif
+
+#elif defined(GLFW_INCLUDE_GLCOREARB)
+
+ #if defined(__APPLE__)
+
+ #include
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include
+ #endif /*GLFW_INCLUDE_GLEXT*/
+
+ #else /*__APPLE__*/
+
+ #include
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include
+ #endif
+
+ #endif /*__APPLE__*/
+
+#elif defined(GLFW_INCLUDE_GLU)
+
+ #if defined(__APPLE__)
+
+ #if defined(GLFW_INCLUDE_GLU)
+ #include
+ #endif
+
+ #else /*__APPLE__*/
+
+ #if defined(GLFW_INCLUDE_GLU)
+ #include
+ #endif
+
+ #endif /*__APPLE__*/
+
+#elif !defined(GLFW_INCLUDE_NONE) && \
+ !defined(__gl_h_) && \
+ !defined(__gles1_gl_h_) && \
+ !defined(__gles2_gl2_h_) && \
+ !defined(__gles2_gl3_h_) && \
+ !defined(__gles2_gl31_h_) && \
+ !defined(__gles2_gl32_h_) && \
+ !defined(__gl_glcorearb_h_) && \
+ !defined(__gl2_h_) /*legacy*/ && \
+ !defined(__gl3_h_) /*legacy*/ && \
+ !defined(__gl31_h_) /*legacy*/ && \
+ !defined(__gl32_h_) /*legacy*/ && \
+ !defined(__glcorearb_h_) /*legacy*/ && \
+ !defined(__GL_H__) /*non-standard*/ && \
+ !defined(__gltypes_h_) /*non-standard*/ && \
+ !defined(__glee_h_) /*non-standard*/
+
+ #if defined(__APPLE__)
+
+ #if !defined(GLFW_INCLUDE_GLEXT)
+ #define GL_GLEXT_LEGACY
+ #endif
+ #include
+
+ #else /*__APPLE__*/
+
+ #include
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include
+ #endif
+
+ #endif /*__APPLE__*/
+
+#endif /* OpenGL and OpenGL ES headers */
+
+#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL)
+ /* GLFW_DLL must be defined by applications that are linking against the DLL
+ * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW
+ * configuration header when compiling the DLL version of the library.
+ */
+ #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined"
+#endif
+
+/* GLFWAPI is used to declare public API functions for export
+ * from the DLL / shared library / dynamic library.
+ */
+#if defined(_WIN32) && defined(_GLFW_BUILD_DLL)
+ /* We are building GLFW as a Win32 DLL */
+ #define GLFWAPI __declspec(dllexport)
+#elif defined(_WIN32) && defined(GLFW_DLL)
+ /* We are calling GLFW as a Win32 DLL */
+ #define GLFWAPI __declspec(dllimport)
+#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)
+ /* We are building GLFW as a shared / dynamic library */
+ #define GLFWAPI __attribute__((visibility("default")))
+#else
+ /* We are building or calling GLFW as a static library */
+ #define GLFWAPI
+#endif
+
+
+/*************************************************************************
+ * GLFW API tokens
+ *************************************************************************/
+
+/*! @name GLFW version macros
+ * @{ */
+/*! @brief The major version number of the GLFW header.
+ *
+ * The major version number of the GLFW header. This is incremented when the
+ * API is changed in non-compatible ways.
+ * @ingroup init
+ */
+#define GLFW_VERSION_MAJOR 3
+/*! @brief The minor version number of the GLFW header.
+ *
+ * The minor version number of the GLFW header. This is incremented when
+ * features are added to the API but it remains backward-compatible.
+ * @ingroup init
+ */
+#define GLFW_VERSION_MINOR 3
+/*! @brief The revision number of the GLFW header.
+ *
+ * The revision number of the GLFW header. This is incremented when a bug fix
+ * release is made that does not contain any API changes.
+ * @ingroup init
+ */
+#define GLFW_VERSION_REVISION 7
+/*! @} */
+
+/*! @brief One.
+ *
+ * This is only semantic sugar for the number 1. You can instead use `1` or
+ * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal
+ * to one.
+ *
+ * @ingroup init
+ */
+#define GLFW_TRUE 1
+/*! @brief Zero.
+ *
+ * This is only semantic sugar for the number 0. You can instead use `0` or
+ * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is
+ * equal to zero.
+ *
+ * @ingroup init
+ */
+#define GLFW_FALSE 0
+
+/*! @name Key and button actions
+ * @{ */
+/*! @brief The key or mouse button was released.
+ *
+ * The key or mouse button was released.
+ *
+ * @ingroup input
+ */
+#define GLFW_RELEASE 0
+/*! @brief The key or mouse button was pressed.
+ *
+ * The key or mouse button was pressed.
+ *
+ * @ingroup input
+ */
+#define GLFW_PRESS 1
+/*! @brief The key was held down until it repeated.
+ *
+ * The key was held down until it repeated.
+ *
+ * @ingroup input
+ */
+#define GLFW_REPEAT 2
+/*! @} */
+
+/*! @defgroup hat_state Joystick hat states
+ * @brief Joystick hat states.
+ *
+ * See [joystick hat input](@ref joystick_hat) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+#define GLFW_HAT_CENTERED 0
+#define GLFW_HAT_UP 1
+#define GLFW_HAT_RIGHT 2
+#define GLFW_HAT_DOWN 4
+#define GLFW_HAT_LEFT 8
+#define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP)
+#define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN)
+#define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP)
+#define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN)
+/*! @} */
+
+/*! @defgroup keys Keyboard keys
+ * @brief Keyboard key IDs.
+ *
+ * See [key input](@ref input_key) for how these are used.
+ *
+ * These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60),
+ * but re-arranged to map to 7-bit ASCII for printable keys (function keys are
+ * put in the 256+ range).
+ *
+ * The naming of the key codes follow these rules:
+ * - The US keyboard layout is used
+ * - Names of printable alpha-numeric characters are used (e.g. "A", "R",
+ * "3", etc.)
+ * - For non-alphanumeric characters, Unicode:ish names are used (e.g.
+ * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not
+ * correspond to the Unicode standard (usually for brevity)
+ * - Keys that lack a clear US mapping are named "WORLD_x"
+ * - For non-printable keys, custom names are used (e.g. "F4",
+ * "BACKSPACE", etc.)
+ *
+ * @ingroup input
+ * @{
+ */
+
+/* The unknown key */
+#define GLFW_KEY_UNKNOWN -1
+
+/* Printable keys */
+#define GLFW_KEY_SPACE 32
+#define GLFW_KEY_APOSTROPHE 39 /* ' */
+#define GLFW_KEY_COMMA 44 /* , */
+#define GLFW_KEY_MINUS 45 /* - */
+#define GLFW_KEY_PERIOD 46 /* . */
+#define GLFW_KEY_SLASH 47 /* / */
+#define GLFW_KEY_0 48
+#define GLFW_KEY_1 49
+#define GLFW_KEY_2 50
+#define GLFW_KEY_3 51
+#define GLFW_KEY_4 52
+#define GLFW_KEY_5 53
+#define GLFW_KEY_6 54
+#define GLFW_KEY_7 55
+#define GLFW_KEY_8 56
+#define GLFW_KEY_9 57
+#define GLFW_KEY_SEMICOLON 59 /* ; */
+#define GLFW_KEY_EQUAL 61 /* = */
+#define GLFW_KEY_A 65
+#define GLFW_KEY_B 66
+#define GLFW_KEY_C 67
+#define GLFW_KEY_D 68
+#define GLFW_KEY_E 69
+#define GLFW_KEY_F 70
+#define GLFW_KEY_G 71
+#define GLFW_KEY_H 72
+#define GLFW_KEY_I 73
+#define GLFW_KEY_J 74
+#define GLFW_KEY_K 75
+#define GLFW_KEY_L 76
+#define GLFW_KEY_M 77
+#define GLFW_KEY_N 78
+#define GLFW_KEY_O 79
+#define GLFW_KEY_P 80
+#define GLFW_KEY_Q 81
+#define GLFW_KEY_R 82
+#define GLFW_KEY_S 83
+#define GLFW_KEY_T 84
+#define GLFW_KEY_U 85
+#define GLFW_KEY_V 86
+#define GLFW_KEY_W 87
+#define GLFW_KEY_X 88
+#define GLFW_KEY_Y 89
+#define GLFW_KEY_Z 90
+#define GLFW_KEY_LEFT_BRACKET 91 /* [ */
+#define GLFW_KEY_BACKSLASH 92 /* \ */
+#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */
+#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */
+#define GLFW_KEY_WORLD_1 161 /* non-US #1 */
+#define GLFW_KEY_WORLD_2 162 /* non-US #2 */
+
+/* Function keys */
+#define GLFW_KEY_ESCAPE 256
+#define GLFW_KEY_ENTER 257
+#define GLFW_KEY_TAB 258
+#define GLFW_KEY_BACKSPACE 259
+#define GLFW_KEY_INSERT 260
+#define GLFW_KEY_DELETE 261
+#define GLFW_KEY_RIGHT 262
+#define GLFW_KEY_LEFT 263
+#define GLFW_KEY_DOWN 264
+#define GLFW_KEY_UP 265
+#define GLFW_KEY_PAGE_UP 266
+#define GLFW_KEY_PAGE_DOWN 267
+#define GLFW_KEY_HOME 268
+#define GLFW_KEY_END 269
+#define GLFW_KEY_CAPS_LOCK 280
+#define GLFW_KEY_SCROLL_LOCK 281
+#define GLFW_KEY_NUM_LOCK 282
+#define GLFW_KEY_PRINT_SCREEN 283
+#define GLFW_KEY_PAUSE 284
+#define GLFW_KEY_F1 290
+#define GLFW_KEY_F2 291
+#define GLFW_KEY_F3 292
+#define GLFW_KEY_F4 293
+#define GLFW_KEY_F5 294
+#define GLFW_KEY_F6 295
+#define GLFW_KEY_F7 296
+#define GLFW_KEY_F8 297
+#define GLFW_KEY_F9 298
+#define GLFW_KEY_F10 299
+#define GLFW_KEY_F11 300
+#define GLFW_KEY_F12 301
+#define GLFW_KEY_F13 302
+#define GLFW_KEY_F14 303
+#define GLFW_KEY_F15 304
+#define GLFW_KEY_F16 305
+#define GLFW_KEY_F17 306
+#define GLFW_KEY_F18 307
+#define GLFW_KEY_F19 308
+#define GLFW_KEY_F20 309
+#define GLFW_KEY_F21 310
+#define GLFW_KEY_F22 311
+#define GLFW_KEY_F23 312
+#define GLFW_KEY_F24 313
+#define GLFW_KEY_F25 314
+#define GLFW_KEY_KP_0 320
+#define GLFW_KEY_KP_1 321
+#define GLFW_KEY_KP_2 322
+#define GLFW_KEY_KP_3 323
+#define GLFW_KEY_KP_4 324
+#define GLFW_KEY_KP_5 325
+#define GLFW_KEY_KP_6 326
+#define GLFW_KEY_KP_7 327
+#define GLFW_KEY_KP_8 328
+#define GLFW_KEY_KP_9 329
+#define GLFW_KEY_KP_DECIMAL 330
+#define GLFW_KEY_KP_DIVIDE 331
+#define GLFW_KEY_KP_MULTIPLY 332
+#define GLFW_KEY_KP_SUBTRACT 333
+#define GLFW_KEY_KP_ADD 334
+#define GLFW_KEY_KP_ENTER 335
+#define GLFW_KEY_KP_EQUAL 336
+#define GLFW_KEY_LEFT_SHIFT 340
+#define GLFW_KEY_LEFT_CONTROL 341
+#define GLFW_KEY_LEFT_ALT 342
+#define GLFW_KEY_LEFT_SUPER 343
+#define GLFW_KEY_RIGHT_SHIFT 344
+#define GLFW_KEY_RIGHT_CONTROL 345
+#define GLFW_KEY_RIGHT_ALT 346
+#define GLFW_KEY_RIGHT_SUPER 347
+#define GLFW_KEY_MENU 348
+
+#define GLFW_KEY_LAST GLFW_KEY_MENU
+
+/*! @} */
+
+/*! @defgroup mods Modifier key flags
+ * @brief Modifier key flags.
+ *
+ * See [key input](@ref input_key) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+
+/*! @brief If this bit is set one or more Shift keys were held down.
+ *
+ * If this bit is set one or more Shift keys were held down.
+ */
+#define GLFW_MOD_SHIFT 0x0001
+/*! @brief If this bit is set one or more Control keys were held down.
+ *
+ * If this bit is set one or more Control keys were held down.
+ */
+#define GLFW_MOD_CONTROL 0x0002
+/*! @brief If this bit is set one or more Alt keys were held down.
+ *
+ * If this bit is set one or more Alt keys were held down.
+ */
+#define GLFW_MOD_ALT 0x0004
+/*! @brief If this bit is set one or more Super keys were held down.
+ *
+ * If this bit is set one or more Super keys were held down.
+ */
+#define GLFW_MOD_SUPER 0x0008
+/*! @brief If this bit is set the Caps Lock key is enabled.
+ *
+ * If this bit is set the Caps Lock key is enabled and the @ref
+ * GLFW_LOCK_KEY_MODS input mode is set.
+ */
+#define GLFW_MOD_CAPS_LOCK 0x0010
+/*! @brief If this bit is set the Num Lock key is enabled.
+ *
+ * If this bit is set the Num Lock key is enabled and the @ref
+ * GLFW_LOCK_KEY_MODS input mode is set.
+ */
+#define GLFW_MOD_NUM_LOCK 0x0020
+
+/*! @} */
+
+/*! @defgroup buttons Mouse buttons
+ * @brief Mouse button IDs.
+ *
+ * See [mouse button input](@ref input_mouse_button) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+#define GLFW_MOUSE_BUTTON_1 0
+#define GLFW_MOUSE_BUTTON_2 1
+#define GLFW_MOUSE_BUTTON_3 2
+#define GLFW_MOUSE_BUTTON_4 3
+#define GLFW_MOUSE_BUTTON_5 4
+#define GLFW_MOUSE_BUTTON_6 5
+#define GLFW_MOUSE_BUTTON_7 6
+#define GLFW_MOUSE_BUTTON_8 7
+#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8
+#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1
+#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2
+#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3
+/*! @} */
+
+/*! @defgroup joysticks Joysticks
+ * @brief Joystick IDs.
+ *
+ * See [joystick input](@ref joystick) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+#define GLFW_JOYSTICK_1 0
+#define GLFW_JOYSTICK_2 1
+#define GLFW_JOYSTICK_3 2
+#define GLFW_JOYSTICK_4 3
+#define GLFW_JOYSTICK_5 4
+#define GLFW_JOYSTICK_6 5
+#define GLFW_JOYSTICK_7 6
+#define GLFW_JOYSTICK_8 7
+#define GLFW_JOYSTICK_9 8
+#define GLFW_JOYSTICK_10 9
+#define GLFW_JOYSTICK_11 10
+#define GLFW_JOYSTICK_12 11
+#define GLFW_JOYSTICK_13 12
+#define GLFW_JOYSTICK_14 13
+#define GLFW_JOYSTICK_15 14
+#define GLFW_JOYSTICK_16 15
+#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16
+/*! @} */
+
+/*! @defgroup gamepad_buttons Gamepad buttons
+ * @brief Gamepad buttons.
+ *
+ * See @ref gamepad for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+#define GLFW_GAMEPAD_BUTTON_A 0
+#define GLFW_GAMEPAD_BUTTON_B 1
+#define GLFW_GAMEPAD_BUTTON_X 2
+#define GLFW_GAMEPAD_BUTTON_Y 3
+#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4
+#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5
+#define GLFW_GAMEPAD_BUTTON_BACK 6
+#define GLFW_GAMEPAD_BUTTON_START 7
+#define GLFW_GAMEPAD_BUTTON_GUIDE 8
+#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9
+#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10
+#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11
+#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12
+#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13
+#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14
+#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT
+
+#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A
+#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B
+#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X
+#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y
+/*! @} */
+
+/*! @defgroup gamepad_axes Gamepad axes
+ * @brief Gamepad axes.
+ *
+ * See @ref gamepad for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+#define GLFW_GAMEPAD_AXIS_LEFT_X 0
+#define GLFW_GAMEPAD_AXIS_LEFT_Y 1
+#define GLFW_GAMEPAD_AXIS_RIGHT_X 2
+#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3
+#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4
+#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5
+#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER
+/*! @} */
+
+/*! @defgroup errors Error codes
+ * @brief Error codes.
+ *
+ * See [error handling](@ref error_handling) for how these are used.
+ *
+ * @ingroup init
+ * @{ */
+/*! @brief No error has occurred.
+ *
+ * No error has occurred.
+ *
+ * @analysis Yay.
+ */
+#define GLFW_NO_ERROR 0
+/*! @brief GLFW has not been initialized.
+ *
+ * This occurs if a GLFW function was called that must not be called unless the
+ * library is [initialized](@ref intro_init).
+ *
+ * @analysis Application programmer error. Initialize GLFW before calling any
+ * function that requires initialization.
+ */
+#define GLFW_NOT_INITIALIZED 0x00010001
+/*! @brief No context is current for this thread.
+ *
+ * This occurs if a GLFW function was called that needs and operates on the
+ * current OpenGL or OpenGL ES context but no context is current on the calling
+ * thread. One such function is @ref glfwSwapInterval.
+ *
+ * @analysis Application programmer error. Ensure a context is current before
+ * calling functions that require a current context.
+ */
+#define GLFW_NO_CURRENT_CONTEXT 0x00010002
+/*! @brief One of the arguments to the function was an invalid enum value.
+ *
+ * One of the arguments to the function was an invalid enum value, for example
+ * requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib.
+ *
+ * @analysis Application programmer error. Fix the offending call.
+ */
+#define GLFW_INVALID_ENUM 0x00010003
+/*! @brief One of the arguments to the function was an invalid value.
+ *
+ * One of the arguments to the function was an invalid value, for example
+ * requesting a non-existent OpenGL or OpenGL ES version like 2.7.
+ *
+ * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead
+ * result in a @ref GLFW_VERSION_UNAVAILABLE error.
+ *
+ * @analysis Application programmer error. Fix the offending call.
+ */
+#define GLFW_INVALID_VALUE 0x00010004
+/*! @brief A memory allocation failed.
+ *
+ * A memory allocation failed.
+ *
+ * @analysis A bug in GLFW or the underlying operating system. Report the bug
+ * to our [issue tracker](https://github.com/glfw/glfw/issues).
+ */
+#define GLFW_OUT_OF_MEMORY 0x00010005
+/*! @brief GLFW could not find support for the requested API on the system.
+ *
+ * GLFW could not find support for the requested API on the system.
+ *
+ * @analysis The installed graphics driver does not support the requested
+ * API, or does not support it via the chosen context creation backend.
+ * Below are a few examples.
+ *
+ * @par
+ * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only
+ * supports OpenGL ES via EGL, while Nvidia and Intel only support it via
+ * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa
+ * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary
+ * driver. Older graphics drivers do not support Vulkan.
+ */
+#define GLFW_API_UNAVAILABLE 0x00010006
+/*! @brief The requested OpenGL or OpenGL ES version is not available.
+ *
+ * The requested OpenGL or OpenGL ES version (including any requested context
+ * or framebuffer hints) is not available on this machine.
+ *
+ * @analysis The machine does not support your requirements. If your
+ * application is sufficiently flexible, downgrade your requirements and try
+ * again. Otherwise, inform the user that their machine does not match your
+ * requirements.
+ *
+ * @par
+ * Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0
+ * comes out before the 4.x series gets that far, also fail with this error and
+ * not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions
+ * will exist.
+ */
+#define GLFW_VERSION_UNAVAILABLE 0x00010007
+/*! @brief A platform-specific error occurred that does not match any of the
+ * more specific categories.
+ *
+ * A platform-specific error occurred that does not match any of the more
+ * specific categories.
+ *
+ * @analysis A bug or configuration error in GLFW, the underlying operating
+ * system or its drivers, or a lack of required resources. Report the issue to
+ * our [issue tracker](https://github.com/glfw/glfw/issues).
+ */
+#define GLFW_PLATFORM_ERROR 0x00010008
+/*! @brief The requested format is not supported or available.
+ *
+ * If emitted during window creation, the requested pixel format is not
+ * supported.
+ *
+ * If emitted when querying the clipboard, the contents of the clipboard could
+ * not be converted to the requested format.
+ *
+ * @analysis If emitted during window creation, one or more
+ * [hard constraints](@ref window_hints_hard) did not match any of the
+ * available pixel formats. If your application is sufficiently flexible,
+ * downgrade your requirements and try again. Otherwise, inform the user that
+ * their machine does not match your requirements.
+ *
+ * @par
+ * If emitted when querying the clipboard, ignore the error or report it to
+ * the user, as appropriate.
+ */
+#define GLFW_FORMAT_UNAVAILABLE 0x00010009
+/*! @brief The specified window does not have an OpenGL or OpenGL ES context.
+ *
+ * A window that does not have an OpenGL or OpenGL ES context was passed to
+ * a function that requires it to have one.
+ *
+ * @analysis Application programmer error. Fix the offending call.
+ */
+#define GLFW_NO_WINDOW_CONTEXT 0x0001000A
+/*! @} */
+
+/*! @addtogroup window
+ * @{ */
+/*! @brief Input focus window hint and attribute
+ *
+ * Input focus [window hint](@ref GLFW_FOCUSED_hint) or
+ * [window attribute](@ref GLFW_FOCUSED_attrib).
+ */
+#define GLFW_FOCUSED 0x00020001
+/*! @brief Window iconification window attribute
+ *
+ * Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib).
+ */
+#define GLFW_ICONIFIED 0x00020002
+/*! @brief Window resize-ability window hint and attribute
+ *
+ * Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and
+ * [window attribute](@ref GLFW_RESIZABLE_attrib).
+ */
+#define GLFW_RESIZABLE 0x00020003
+/*! @brief Window visibility window hint and attribute
+ *
+ * Window visibility [window hint](@ref GLFW_VISIBLE_hint) and
+ * [window attribute](@ref GLFW_VISIBLE_attrib).
+ */
+#define GLFW_VISIBLE 0x00020004
+/*! @brief Window decoration window hint and attribute
+ *
+ * Window decoration [window hint](@ref GLFW_DECORATED_hint) and
+ * [window attribute](@ref GLFW_DECORATED_attrib).
+ */
+#define GLFW_DECORATED 0x00020005
+/*! @brief Window auto-iconification window hint and attribute
+ *
+ * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and
+ * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib).
+ */
+#define GLFW_AUTO_ICONIFY 0x00020006
+/*! @brief Window decoration window hint and attribute
+ *
+ * Window decoration [window hint](@ref GLFW_FLOATING_hint) and
+ * [window attribute](@ref GLFW_FLOATING_attrib).
+ */
+#define GLFW_FLOATING 0x00020007
+/*! @brief Window maximization window hint and attribute
+ *
+ * Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and
+ * [window attribute](@ref GLFW_MAXIMIZED_attrib).
+ */
+#define GLFW_MAXIMIZED 0x00020008
+/*! @brief Cursor centering window hint
+ *
+ * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint).
+ */
+#define GLFW_CENTER_CURSOR 0x00020009
+/*! @brief Window framebuffer transparency hint and attribute
+ *
+ * Window framebuffer transparency
+ * [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and
+ * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib).
+ */
+#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A
+/*! @brief Mouse cursor hover window attribute.
+ *
+ * Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib).
+ */
+#define GLFW_HOVERED 0x0002000B
+/*! @brief Input focus on calling show window hint and attribute
+ *
+ * Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or
+ * [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib).
+ */
+#define GLFW_FOCUS_ON_SHOW 0x0002000C
+
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_RED_BITS).
+ */
+#define GLFW_RED_BITS 0x00021001
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS).
+ */
+#define GLFW_GREEN_BITS 0x00021002
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS).
+ */
+#define GLFW_BLUE_BITS 0x00021003
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS).
+ */
+#define GLFW_ALPHA_BITS 0x00021004
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS).
+ */
+#define GLFW_DEPTH_BITS 0x00021005
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS).
+ */
+#define GLFW_STENCIL_BITS 0x00021006
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS).
+ */
+#define GLFW_ACCUM_RED_BITS 0x00021007
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS).
+ */
+#define GLFW_ACCUM_GREEN_BITS 0x00021008
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS).
+ */
+#define GLFW_ACCUM_BLUE_BITS 0x00021009
+/*! @brief Framebuffer bit depth hint.
+ *
+ * Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS).
+ */
+#define GLFW_ACCUM_ALPHA_BITS 0x0002100A
+/*! @brief Framebuffer auxiliary buffer hint.
+ *
+ * Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS).
+ */
+#define GLFW_AUX_BUFFERS 0x0002100B
+/*! @brief OpenGL stereoscopic rendering hint.
+ *
+ * OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO).
+ */
+#define GLFW_STEREO 0x0002100C
+/*! @brief Framebuffer MSAA samples hint.
+ *
+ * Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES).
+ */
+#define GLFW_SAMPLES 0x0002100D
+/*! @brief Framebuffer sRGB hint.
+ *
+ * Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE).
+ */
+#define GLFW_SRGB_CAPABLE 0x0002100E
+/*! @brief Monitor refresh rate hint.
+ *
+ * Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE).
+ */
+#define GLFW_REFRESH_RATE 0x0002100F
+/*! @brief Framebuffer double buffering hint.
+ *
+ * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER).
+ */
+#define GLFW_DOUBLEBUFFER 0x00021010
+
+/*! @brief Context client API hint and attribute.
+ *
+ * Context client API [hint](@ref GLFW_CLIENT_API_hint) and
+ * [attribute](@ref GLFW_CLIENT_API_attrib).
+ */
+#define GLFW_CLIENT_API 0x00022001
+/*! @brief Context client API major version hint and attribute.
+ *
+ * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint)
+ * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib).
+ */
+#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002
+/*! @brief Context client API minor version hint and attribute.
+ *
+ * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint)
+ * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib).
+ */
+#define GLFW_CONTEXT_VERSION_MINOR 0x00022003
+/*! @brief Context client API revision number attribute.
+ *
+ * Context client API revision number
+ * [attribute](@ref GLFW_CONTEXT_REVISION_attrib).
+ */
+#define GLFW_CONTEXT_REVISION 0x00022004
+/*! @brief Context robustness hint and attribute.
+ *
+ * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint)
+ * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib).
+ */
+#define GLFW_CONTEXT_ROBUSTNESS 0x00022005
+/*! @brief OpenGL forward-compatibility hint and attribute.
+ *
+ * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint)
+ * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib).
+ */
+#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006
+/*! @brief Debug mode context hint and attribute.
+ *
+ * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and
+ * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib).
+ */
+#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007
+/*! @brief OpenGL profile hint and attribute.
+ *
+ * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and
+ * [attribute](@ref GLFW_OPENGL_PROFILE_attrib).
+ */
+#define GLFW_OPENGL_PROFILE 0x00022008
+/*! @brief Context flush-on-release hint and attribute.
+ *
+ * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and
+ * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib).
+ */
+#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009
+/*! @brief Context error suppression hint and attribute.
+ *
+ * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and
+ * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib).
+ */
+#define GLFW_CONTEXT_NO_ERROR 0x0002200A
+/*! @brief Context creation API hint and attribute.
+ *
+ * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and
+ * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib).
+ */
+#define GLFW_CONTEXT_CREATION_API 0x0002200B
+/*! @brief Window content area scaling window
+ * [window hint](@ref GLFW_SCALE_TO_MONITOR).
+ */
+#define GLFW_SCALE_TO_MONITOR 0x0002200C
+/*! @brief macOS specific
+ * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint).
+ */
+#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
+/*! @brief macOS specific
+ * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint).
+ */
+#define GLFW_COCOA_FRAME_NAME 0x00023002
+/*! @brief macOS specific
+ * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint).
+ */
+#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003
+/*! @brief X11 specific
+ * [window hint](@ref GLFW_X11_CLASS_NAME_hint).
+ */
+#define GLFW_X11_CLASS_NAME 0x00024001
+/*! @brief X11 specific
+ * [window hint](@ref GLFW_X11_CLASS_NAME_hint).
+ */
+#define GLFW_X11_INSTANCE_NAME 0x00024002
+/*! @} */
+
+#define GLFW_NO_API 0
+#define GLFW_OPENGL_API 0x00030001
+#define GLFW_OPENGL_ES_API 0x00030002
+
+#define GLFW_NO_ROBUSTNESS 0
+#define GLFW_NO_RESET_NOTIFICATION 0x00031001
+#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002
+
+#define GLFW_OPENGL_ANY_PROFILE 0
+#define GLFW_OPENGL_CORE_PROFILE 0x00032001
+#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002
+
+#define GLFW_CURSOR 0x00033001
+#define GLFW_STICKY_KEYS 0x00033002
+#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
+#define GLFW_LOCK_KEY_MODS 0x00033004
+#define GLFW_RAW_MOUSE_MOTION 0x00033005
+
+#define GLFW_CURSOR_NORMAL 0x00034001
+#define GLFW_CURSOR_HIDDEN 0x00034002
+#define GLFW_CURSOR_DISABLED 0x00034003
+
+#define GLFW_ANY_RELEASE_BEHAVIOR 0
+#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001
+#define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002
+
+#define GLFW_NATIVE_CONTEXT_API 0x00036001
+#define GLFW_EGL_CONTEXT_API 0x00036002
+#define GLFW_OSMESA_CONTEXT_API 0x00036003
+
+/*! @defgroup shapes Standard cursor shapes
+ * @brief Standard system cursor shapes.
+ *
+ * See [standard cursor creation](@ref cursor_standard) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+
+/*! @brief The regular arrow cursor shape.
+ *
+ * The regular arrow cursor.
+ */
+#define GLFW_ARROW_CURSOR 0x00036001
+/*! @brief The text input I-beam cursor shape.
+ *
+ * The text input I-beam cursor shape.
+ */
+#define GLFW_IBEAM_CURSOR 0x00036002
+/*! @brief The crosshair shape.
+ *
+ * The crosshair shape.
+ */
+#define GLFW_CROSSHAIR_CURSOR 0x00036003
+/*! @brief The hand shape.
+ *
+ * The hand shape.
+ */
+#define GLFW_HAND_CURSOR 0x00036004
+/*! @brief The horizontal resize arrow shape.
+ *
+ * The horizontal resize arrow shape.
+ */
+#define GLFW_HRESIZE_CURSOR 0x00036005
+/*! @brief The vertical resize arrow shape.
+ *
+ * The vertical resize arrow shape.
+ */
+#define GLFW_VRESIZE_CURSOR 0x00036006
+/*! @} */
+
+#define GLFW_CONNECTED 0x00040001
+#define GLFW_DISCONNECTED 0x00040002
+
+/*! @addtogroup init
+ * @{ */
+/*! @brief Joystick hat buttons init hint.
+ *
+ * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS).
+ */
+#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001
+/*! @brief macOS specific init hint.
+ *
+ * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint).
+ */
+#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001
+/*! @brief macOS specific init hint.
+ *
+ * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint).
+ */
+#define GLFW_COCOA_MENUBAR 0x00051002
+/*! @} */
+
+#define GLFW_DONT_CARE -1
+
+
+/*************************************************************************
+ * GLFW API types
+ *************************************************************************/
+
+/*! @brief Client API function pointer type.
+ *
+ * Generic function pointer used for returning client API function pointers
+ * without forcing a cast from a regular pointer.
+ *
+ * @sa @ref context_glext
+ * @sa @ref glfwGetProcAddress
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup context
+ */
+typedef void (*GLFWglproc)(void);
+
+/*! @brief Vulkan API function pointer type.
+ *
+ * Generic function pointer used for returning Vulkan API function pointers
+ * without forcing a cast from a regular pointer.
+ *
+ * @sa @ref vulkan_proc
+ * @sa @ref glfwGetInstanceProcAddress
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+typedef void (*GLFWvkproc)(void);
+
+/*! @brief Opaque monitor object.
+ *
+ * Opaque monitor object.
+ *
+ * @see @ref monitor_object
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+typedef struct GLFWmonitor GLFWmonitor;
+
+/*! @brief Opaque window object.
+ *
+ * Opaque window object.
+ *
+ * @see @ref window_object
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef struct GLFWwindow GLFWwindow;
+
+/*! @brief Opaque cursor object.
+ *
+ * Opaque cursor object.
+ *
+ * @see @ref cursor_object
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+typedef struct GLFWcursor GLFWcursor;
+
+/*! @brief The function pointer type for error callbacks.
+ *
+ * This is the function pointer type for error callbacks. An error callback
+ * function has the following signature:
+ * @code
+ * void callback_name(int error_code, const char* description)
+ * @endcode
+ *
+ * @param[in] error_code An [error code](@ref errors). Future releases may add
+ * more error codes.
+ * @param[in] description A UTF-8 encoded string describing the error.
+ *
+ * @pointer_lifetime The error description string is valid until the callback
+ * function returns.
+ *
+ * @sa @ref error_handling
+ * @sa @ref glfwSetErrorCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup init
+ */
+typedef void (* GLFWerrorfun)(int error_code, const char* description);
+
+/*! @brief The function pointer type for window position callbacks.
+ *
+ * This is the function pointer type for window position callbacks. A window
+ * position callback function has the following signature:
+ * @code
+ * void callback_name(GLFWwindow* window, int xpos, int ypos)
+ * @endcode
+ *
+ * @param[in] window The window that was moved.
+ * @param[in] xpos The new x-coordinate, in screen coordinates, of the
+ * upper-left corner of the content area of the window.
+ * @param[in] ypos The new y-coordinate, in screen coordinates, of the
+ * upper-left corner of the content area of the window.
+ *
+ * @sa @ref window_pos
+ * @sa @ref glfwSetWindowPosCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos);
+
+/*! @brief The function pointer type for window size callbacks.
+ *
+ * This is the function pointer type for window size callbacks. A window size
+ * callback function has the following signature:
+ * @code
+ * void callback_name(GLFWwindow* window, int width, int height)
+ * @endcode
+ *
+ * @param[in] window The window that was resized.
+ * @param[in] width The new width, in screen coordinates, of the window.
+ * @param[in] height The new height, in screen coordinates, of the window.
+ *
+ * @sa @ref window_size
+ * @sa @ref glfwSetWindowSizeCallback
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int width, int height);
+
+/*! @brief The function pointer type for window close callbacks.
+ *
+ * This is the function pointer type for window close callbacks. A window
+ * close callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window)
+ * @endcode
+ *
+ * @param[in] window The window that the user attempted to close.
+ *
+ * @sa @ref window_close
+ * @sa @ref glfwSetWindowCloseCallback
+ *
+ * @since Added in version 2.5.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowclosefun)(GLFWwindow* window);
+
+/*! @brief The function pointer type for window content refresh callbacks.
+ *
+ * This is the function pointer type for window content refresh callbacks.
+ * A window content refresh callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window);
+ * @endcode
+ *
+ * @param[in] window The window whose content needs to be refreshed.
+ *
+ * @sa @ref window_refresh
+ * @sa @ref glfwSetWindowRefreshCallback
+ *
+ * @since Added in version 2.5.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window);
+
+/*! @brief The function pointer type for window focus callbacks.
+ *
+ * This is the function pointer type for window focus callbacks. A window
+ * focus callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, int focused)
+ * @endcode
+ *
+ * @param[in] window The window that gained or lost input focus.
+ * @param[in] focused `GLFW_TRUE` if the window was given input focus, or
+ * `GLFW_FALSE` if it lost it.
+ *
+ * @sa @ref window_focus
+ * @sa @ref glfwSetWindowFocusCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowfocusfun)(GLFWwindow* window, int focused);
+
+/*! @brief The function pointer type for window iconify callbacks.
+ *
+ * This is the function pointer type for window iconify callbacks. A window
+ * iconify callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, int iconified)
+ * @endcode
+ *
+ * @param[in] window The window that was iconified or restored.
+ * @param[in] iconified `GLFW_TRUE` if the window was iconified, or
+ * `GLFW_FALSE` if it was restored.
+ *
+ * @sa @ref window_iconify
+ * @sa @ref glfwSetWindowIconifyCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowiconifyfun)(GLFWwindow* window, int iconified);
+
+/*! @brief The function pointer type for window maximize callbacks.
+ *
+ * This is the function pointer type for window maximize callbacks. A window
+ * maximize callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, int maximized)
+ * @endcode
+ *
+ * @param[in] window The window that was maximized or restored.
+ * @param[in] maximized `GLFW_TRUE` if the window was maximized, or
+ * `GLFW_FALSE` if it was restored.
+ *
+ * @sa @ref window_maximize
+ * @sa glfwSetWindowMaximizeCallback
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowmaximizefun)(GLFWwindow* window, int maximized);
+
+/*! @brief The function pointer type for framebuffer size callbacks.
+ *
+ * This is the function pointer type for framebuffer size callbacks.
+ * A framebuffer size callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, int width, int height)
+ * @endcode
+ *
+ * @param[in] window The window whose framebuffer was resized.
+ * @param[in] width The new width, in pixels, of the framebuffer.
+ * @param[in] height The new height, in pixels, of the framebuffer.
+ *
+ * @sa @ref window_fbsize
+ * @sa @ref glfwSetFramebufferSizeCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height);
+
+/*! @brief The function pointer type for window content scale callbacks.
+ *
+ * This is the function pointer type for window content scale callbacks.
+ * A window content scale callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, float xscale, float yscale)
+ * @endcode
+ *
+ * @param[in] window The window whose content scale changed.
+ * @param[in] xscale The new x-axis content scale of the window.
+ * @param[in] yscale The new y-axis content scale of the window.
+ *
+ * @sa @ref window_scale
+ * @sa @ref glfwSetWindowContentScaleCallback
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale);
+
+/*! @brief The function pointer type for mouse button callbacks.
+ *
+ * This is the function pointer type for mouse button callback functions.
+ * A mouse button callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, int button, int action, int mods)
+ * @endcode
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] button The [mouse button](@ref buttons) that was pressed or
+ * released.
+ * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases
+ * may add more actions.
+ * @param[in] mods Bit field describing which [modifier keys](@ref mods) were
+ * held down.
+ *
+ * @sa @ref input_mouse_button
+ * @sa @ref glfwSetMouseButtonCallback
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle and modifier mask parameters.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWmousebuttonfun)(GLFWwindow* window, int button, int action, int mods);
+
+/*! @brief The function pointer type for cursor position callbacks.
+ *
+ * This is the function pointer type for cursor position callbacks. A cursor
+ * position callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, double xpos, double ypos);
+ * @endcode
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] xpos The new cursor x-coordinate, relative to the left edge of
+ * the content area.
+ * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the
+ * content area.
+ *
+ * @sa @ref cursor_pos
+ * @sa @ref glfwSetCursorPosCallback
+ *
+ * @since Added in version 3.0. Replaces `GLFWmouseposfun`.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWcursorposfun)(GLFWwindow* window, double xpos, double ypos);
+
+/*! @brief The function pointer type for cursor enter/leave callbacks.
+ *
+ * This is the function pointer type for cursor enter/leave callbacks.
+ * A cursor enter/leave callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, int entered)
+ * @endcode
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content
+ * area, or `GLFW_FALSE` if it left it.
+ *
+ * @sa @ref cursor_enter
+ * @sa @ref glfwSetCursorEnterCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWcursorenterfun)(GLFWwindow* window, int entered);
+
+/*! @brief The function pointer type for scroll callbacks.
+ *
+ * This is the function pointer type for scroll callbacks. A scroll callback
+ * function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, double xoffset, double yoffset)
+ * @endcode
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] xoffset The scroll offset along the x-axis.
+ * @param[in] yoffset The scroll offset along the y-axis.
+ *
+ * @sa @ref scrolling
+ * @sa @ref glfwSetScrollCallback
+ *
+ * @since Added in version 3.0. Replaces `GLFWmousewheelfun`.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffset);
+
+/*! @brief The function pointer type for keyboard key callbacks.
+ *
+ * This is the function pointer type for keyboard key callbacks. A keyboard
+ * key callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods)
+ * @endcode
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] key The [keyboard key](@ref keys) that was pressed or released.
+ * @param[in] scancode The system-specific scancode of the key.
+ * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future
+ * releases may add more actions.
+ * @param[in] mods Bit field describing which [modifier keys](@ref mods) were
+ * held down.
+ *
+ * @sa @ref input_key
+ * @sa @ref glfwSetKeyCallback
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle, scancode and modifier mask parameters.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWkeyfun)(GLFWwindow* window, int key, int scancode, int action, int mods);
+
+/*! @brief The function pointer type for Unicode character callbacks.
+ *
+ * This is the function pointer type for Unicode character callbacks.
+ * A Unicode character callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, unsigned int codepoint)
+ * @endcode
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] codepoint The Unicode code point of the character.
+ *
+ * @sa @ref input_char
+ * @sa @ref glfwSetCharCallback
+ *
+ * @since Added in version 2.4.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint);
+
+/*! @brief The function pointer type for Unicode character with modifiers
+ * callbacks.
+ *
+ * This is the function pointer type for Unicode character with modifiers
+ * callbacks. It is called for each input character, regardless of what
+ * modifier keys are held down. A Unicode character with modifiers callback
+ * function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, unsigned int codepoint, int mods)
+ * @endcode
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] codepoint The Unicode code point of the character.
+ * @param[in] mods Bit field describing which [modifier keys](@ref mods) were
+ * held down.
+ *
+ * @sa @ref input_char
+ * @sa @ref glfwSetCharModsCallback
+ *
+ * @deprecated Scheduled for removal in version 4.0.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods);
+
+/*! @brief The function pointer type for path drop callbacks.
+ *
+ * This is the function pointer type for path drop callbacks. A path drop
+ * callback function has the following signature:
+ * @code
+ * void function_name(GLFWwindow* window, int path_count, const char* paths[])
+ * @endcode
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] path_count The number of dropped paths.
+ * @param[in] paths The UTF-8 encoded file and/or directory path names.
+ *
+ * @pointer_lifetime The path array and its strings are valid until the
+ * callback function returns.
+ *
+ * @sa @ref path_drop
+ * @sa @ref glfwSetDropCallback
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]);
+
+/*! @brief The function pointer type for monitor configuration callbacks.
+ *
+ * This is the function pointer type for monitor configuration callbacks.
+ * A monitor callback function has the following signature:
+ * @code
+ * void function_name(GLFWmonitor* monitor, int event)
+ * @endcode
+ *
+ * @param[in] monitor The monitor that was connected or disconnected.
+ * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future
+ * releases may add more events.
+ *
+ * @sa @ref monitor_event
+ * @sa @ref glfwSetMonitorCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event);
+
+/*! @brief The function pointer type for joystick configuration callbacks.
+ *
+ * This is the function pointer type for joystick configuration callbacks.
+ * A joystick configuration callback function has the following signature:
+ * @code
+ * void function_name(int jid, int event)
+ * @endcode
+ *
+ * @param[in] jid The joystick that was connected or disconnected.
+ * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future
+ * releases may add more events.
+ *
+ * @sa @ref joystick_event
+ * @sa @ref glfwSetJoystickCallback
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWjoystickfun)(int jid, int event);
+
+/*! @brief Video mode type.
+ *
+ * This describes a single video mode.
+ *
+ * @sa @ref monitor_modes
+ * @sa @ref glfwGetVideoMode
+ * @sa @ref glfwGetVideoModes
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added refresh rate member.
+ *
+ * @ingroup monitor
+ */
+typedef struct GLFWvidmode
+{
+ /*! The width, in screen coordinates, of the video mode.
+ */
+ int width;
+ /*! The height, in screen coordinates, of the video mode.
+ */
+ int height;
+ /*! The bit depth of the red channel of the video mode.
+ */
+ int redBits;
+ /*! The bit depth of the green channel of the video mode.
+ */
+ int greenBits;
+ /*! The bit depth of the blue channel of the video mode.
+ */
+ int blueBits;
+ /*! The refresh rate, in Hz, of the video mode.
+ */
+ int refreshRate;
+} GLFWvidmode;
+
+/*! @brief Gamma ramp.
+ *
+ * This describes the gamma ramp for a monitor.
+ *
+ * @sa @ref monitor_gamma
+ * @sa @ref glfwGetGammaRamp
+ * @sa @ref glfwSetGammaRamp
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+typedef struct GLFWgammaramp
+{
+ /*! An array of value describing the response of the red channel.
+ */
+ unsigned short* red;
+ /*! An array of value describing the response of the green channel.
+ */
+ unsigned short* green;
+ /*! An array of value describing the response of the blue channel.
+ */
+ unsigned short* blue;
+ /*! The number of elements in each array.
+ */
+ unsigned int size;
+} GLFWgammaramp;
+
+/*! @brief Image data.
+ *
+ * This describes a single 2D image. See the documentation for each related
+ * function what the expected pixel format is.
+ *
+ * @sa @ref cursor_custom
+ * @sa @ref window_icon
+ *
+ * @since Added in version 2.1.
+ * @glfw3 Removed format and bytes-per-pixel members.
+ *
+ * @ingroup window
+ */
+typedef struct GLFWimage
+{
+ /*! The width, in pixels, of this image.
+ */
+ int width;
+ /*! The height, in pixels, of this image.
+ */
+ int height;
+ /*! The pixel data of this image, arranged left-to-right, top-to-bottom.
+ */
+ unsigned char* pixels;
+} GLFWimage;
+
+/*! @brief Gamepad input state
+ *
+ * This describes the input state of a gamepad.
+ *
+ * @sa @ref gamepad
+ * @sa @ref glfwGetGamepadState
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+typedef struct GLFWgamepadstate
+{
+ /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS`
+ * or `GLFW_RELEASE`.
+ */
+ unsigned char buttons[15];
+ /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0
+ * to 1.0 inclusive.
+ */
+ float axes[6];
+} GLFWgamepadstate;
+
+
+/*************************************************************************
+ * GLFW API functions
+ *************************************************************************/
+
+/*! @brief Initializes the GLFW library.
+ *
+ * This function initializes the GLFW library. Before most GLFW functions can
+ * be used, GLFW must be initialized, and before an application terminates GLFW
+ * should be terminated in order to free any resources allocated during or
+ * after initialization.
+ *
+ * If this function fails, it calls @ref glfwTerminate before returning. If it
+ * succeeds, you should call @ref glfwTerminate before the application exits.
+ *
+ * Additional calls to this function after successful initialization but before
+ * termination will return `GLFW_TRUE` immediately.
+ *
+ * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark @macos This function will change the current directory of the
+ * application to the `Contents/Resources` subdirectory of the application's
+ * bundle, if present. This can be disabled with the @ref
+ * GLFW_COCOA_CHDIR_RESOURCES init hint.
+ *
+ * @remark @x11 This function will set the `LC_CTYPE` category of the
+ * application locale according to the current environment if that category is
+ * still "C". This is because the "C" locale breaks Unicode text input.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref intro_init
+ * @sa @ref glfwTerminate
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI int glfwInit(void);
+
+/*! @brief Terminates the GLFW library.
+ *
+ * This function destroys all remaining windows and cursors, restores any
+ * modified gamma ramps and frees any other allocated resources. Once this
+ * function is called, you must again call @ref glfwInit successfully before
+ * you will be able to use most GLFW functions.
+ *
+ * If GLFW has been successfully initialized, this function should be called
+ * before the application exits. If initialization fails, there is no need to
+ * call this function, as it is called by @ref glfwInit before it returns
+ * failure.
+ *
+ * This function has no effect if GLFW is not initialized.
+ *
+ * @errors Possible errors include @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @warning The contexts of any remaining windows must not be current on any
+ * other thread when this function is called.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref intro_init
+ * @sa @ref glfwInit
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI void glfwTerminate(void);
+
+/*! @brief Sets the specified init hint to the desired value.
+ *
+ * This function sets hints for the next initialization of GLFW.
+ *
+ * The values you set hints to are never reset by GLFW, but they only take
+ * effect during initialization. Once GLFW has been initialized, any values
+ * you set will be ignored until the library is terminated and initialized
+ * again.
+ *
+ * Some hints are platform specific. These may be set on any platform but they
+ * will only affect their specific platform. Other platforms will ignore them.
+ * Setting these hints requires no platform specific headers or functions.
+ *
+ * @param[in] hint The [init hint](@ref init_hints) to set.
+ * @param[in] value The new value of the init hint.
+ *
+ * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref
+ * GLFW_INVALID_VALUE.
+ *
+ * @remarks This function may be called before @ref glfwInit.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa init_hints
+ * @sa glfwInit
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup init
+ */
+GLFWAPI void glfwInitHint(int hint, int value);
+
+/*! @brief Retrieves the version of the GLFW library.
+ *
+ * This function retrieves the major, minor and revision numbers of the GLFW
+ * library. It is intended for when you are using GLFW as a shared library and
+ * want to ensure that you are using the minimum required version.
+ *
+ * Any or all of the version arguments may be `NULL`.
+ *
+ * @param[out] major Where to store the major version number, or `NULL`.
+ * @param[out] minor Where to store the minor version number, or `NULL`.
+ * @param[out] rev Where to store the revision number, or `NULL`.
+ *
+ * @errors None.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref intro_version
+ * @sa @ref glfwGetVersionString
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev);
+
+/*! @brief Returns a string describing the compile-time configuration.
+ *
+ * This function returns the compile-time generated
+ * [version string](@ref intro_version_string) of the GLFW library binary. It
+ * describes the version, platform, compiler and any platform-specific
+ * compile-time options. It should not be confused with the OpenGL or OpenGL
+ * ES version string, queried with `glGetString`.
+ *
+ * __Do not use the version string__ to parse the GLFW library version. The
+ * @ref glfwGetVersion function provides the version of the running library
+ * binary in numerical format.
+ *
+ * @return The ASCII encoded GLFW version string.
+ *
+ * @errors None.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @pointer_lifetime The returned string is static and compile-time generated.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref intro_version
+ * @sa @ref glfwGetVersion
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI const char* glfwGetVersionString(void);
+
+/*! @brief Returns and clears the last error for the calling thread.
+ *
+ * This function returns and clears the [error code](@ref errors) of the last
+ * error that occurred on the calling thread, and optionally a UTF-8 encoded
+ * human-readable description of it. If no error has occurred since the last
+ * call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is
+ * set to `NULL`.
+ *
+ * @param[in] description Where to store the error description pointer, or `NULL`.
+ * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR
+ * (zero).
+ *
+ * @errors None.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is guaranteed to be valid only until the
+ * next error occurs or the library is terminated.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref error_handling
+ * @sa @ref glfwSetErrorCallback
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup init
+ */
+GLFWAPI int glfwGetError(const char** description);
+
+/*! @brief Sets the error callback.
+ *
+ * This function sets the error callback, which is called with an error code
+ * and a human-readable description each time a GLFW error occurs.
+ *
+ * The error code is set before the callback is called. Calling @ref
+ * glfwGetError from the error callback will return the same value as the error
+ * code argument.
+ *
+ * The error callback is called on the thread where the error occurred. If you
+ * are using GLFW from multiple threads, your error callback needs to be
+ * written accordingly.
+ *
+ * Because the description string may have been generated specifically for that
+ * error, it is not guaranteed to be valid after the callback has returned. If
+ * you wish to use it after the callback returns, you need to make a copy.
+ *
+ * Once set, the error callback remains set even after the library has been
+ * terminated.
+ *
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set.
+ *
+ * @callback_signature
+ * @code
+ * void callback_name(int error_code, const char* description)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [callback pointer type](@ref GLFWerrorfun).
+ *
+ * @errors None.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref error_handling
+ * @sa @ref glfwGetError
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback);
+
+/*! @brief Returns the currently connected monitors.
+ *
+ * This function returns an array of handles for all currently connected
+ * monitors. The primary monitor is always first in the returned array. If no
+ * monitors were found, this function returns `NULL`.
+ *
+ * @param[out] count Where to store the number of monitors in the returned
+ * array. This is set to zero if an error occurred.
+ * @return An array of monitor handles, or `NULL` if no monitors were found or
+ * if an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is guaranteed to be valid only until the
+ * monitor configuration changes or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_monitors
+ * @sa @ref monitor_event
+ * @sa @ref glfwGetPrimaryMonitor
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI GLFWmonitor** glfwGetMonitors(int* count);
+
+/*! @brief Returns the primary monitor.
+ *
+ * This function returns the primary monitor. This is usually the monitor
+ * where elements like the task bar or global menu bar are located.
+ *
+ * @return The primary monitor, or `NULL` if no monitors were found or if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @remark The primary monitor is always first in the array returned by @ref
+ * glfwGetMonitors.
+ *
+ * @sa @ref monitor_monitors
+ * @sa @ref glfwGetMonitors
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void);
+
+/*! @brief Returns the position of the monitor's viewport on the virtual screen.
+ *
+ * This function returns the position, in screen coordinates, of the upper-left
+ * corner of the specified monitor.
+ *
+ * Any or all of the position arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` position arguments will be set to zero.
+ *
+ * @param[in] monitor The monitor to query.
+ * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`.
+ * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_properties
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos);
+
+/*! @brief Retrieves the work area of the monitor.
+ *
+ * This function returns the position, in screen coordinates, of the upper-left
+ * corner of the work area of the specified monitor along with the work area
+ * size in screen coordinates. The work area is defined as the area of the
+ * monitor not occluded by the operating system task bar where present. If no
+ * task bar exists then the work area is the monitor resolution in screen
+ * coordinates.
+ *
+ * Any or all of the position and size arguments may be `NULL`. If an error
+ * occurs, all non-`NULL` position and size arguments will be set to zero.
+ *
+ * @param[in] monitor The monitor to query.
+ * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`.
+ * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`.
+ * @param[out] width Where to store the monitor width, or `NULL`.
+ * @param[out] height Where to store the monitor height, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_workarea
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height);
+
+/*! @brief Returns the physical size of the monitor.
+ *
+ * This function returns the size, in millimetres, of the display area of the
+ * specified monitor.
+ *
+ * Some systems do not provide accurate monitor size information, either
+ * because the monitor
+ * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data)
+ * data is incorrect or because the driver does not report it accurately.
+ *
+ * Any or all of the size arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` size arguments will be set to zero.
+ *
+ * @param[in] monitor The monitor to query.
+ * @param[out] widthMM Where to store the width, in millimetres, of the
+ * monitor's display area, or `NULL`.
+ * @param[out] heightMM Where to store the height, in millimetres, of the
+ * monitor's display area, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark @win32 On Windows 8 and earlier the physical size is calculated from
+ * the current resolution and system DPI instead of querying the monitor EDID data.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_properties
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM);
+
+/*! @brief Retrieves the content scale for the specified monitor.
+ *
+ * This function retrieves the content scale for the specified monitor. The
+ * content scale is the ratio between the current DPI and the platform's
+ * default DPI. This is especially important for text and any UI elements. If
+ * the pixel dimensions of your UI scaled by this look appropriate on your
+ * machine then it should appear at a reasonable size on other machines
+ * regardless of their DPI and scaling settings. This relies on the system DPI
+ * and scaling settings being somewhat correct.
+ *
+ * The content scale may depend on both the monitor resolution and pixel
+ * density and on user settings. It may be very different from the raw DPI
+ * calculated from the physical size and current resolution.
+ *
+ * @param[in] monitor The monitor to query.
+ * @param[out] xscale Where to store the x-axis content scale, or `NULL`.
+ * @param[out] yscale Where to store the y-axis content scale, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_scale
+ * @sa @ref glfwGetWindowContentScale
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale);
+
+/*! @brief Returns the name of the specified monitor.
+ *
+ * This function returns a human-readable name, encoded as UTF-8, of the
+ * specified monitor. The name typically reflects the make and model of the
+ * monitor and is not guaranteed to be unique among the connected monitors.
+ *
+ * @param[in] monitor The monitor to query.
+ * @return The UTF-8 encoded name of the monitor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified monitor is
+ * disconnected or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_properties
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor);
+
+/*! @brief Sets the user pointer of the specified monitor.
+ *
+ * This function sets the user-defined pointer of the specified monitor. The
+ * current value is retained until the monitor is disconnected. The initial
+ * value is `NULL`.
+ *
+ * This function may be called from the monitor callback, even for a monitor
+ * that is being disconnected.
+ *
+ * @param[in] monitor The monitor whose pointer to set.
+ * @param[in] pointer The new value.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref monitor_userptr
+ * @sa @ref glfwGetMonitorUserPointer
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer);
+
+/*! @brief Returns the user pointer of the specified monitor.
+ *
+ * This function returns the current value of the user-defined pointer of the
+ * specified monitor. The initial value is `NULL`.
+ *
+ * This function may be called from the monitor callback, even for a monitor
+ * that is being disconnected.
+ *
+ * @param[in] monitor The monitor whose pointer to return.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref monitor_userptr
+ * @sa @ref glfwSetMonitorUserPointer
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor);
+
+/*! @brief Sets the monitor configuration callback.
+ *
+ * This function sets the monitor configuration callback, or removes the
+ * currently set callback. This is called when a monitor is connected to or
+ * disconnected from the system.
+ *
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWmonitor* monitor, int event)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWmonitorfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_event
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback);
+
+/*! @brief Returns the available video modes for the specified monitor.
+ *
+ * This function returns an array of all video modes supported by the specified
+ * monitor. The returned array is sorted in ascending order, first by color
+ * bit depth (the sum of all channel depths), then by resolution area (the
+ * product of width and height), then resolution width and finally by refresh
+ * rate.
+ *
+ * @param[in] monitor The monitor to query.
+ * @param[out] count Where to store the number of video modes in the returned
+ * array. This is set to zero if an error occurred.
+ * @return An array of video modes, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified monitor is
+ * disconnected, this function is called again for that monitor or the library
+ * is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_modes
+ * @sa @ref glfwGetVideoMode
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Changed to return an array of modes for a specific monitor.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count);
+
+/*! @brief Returns the current mode of the specified monitor.
+ *
+ * This function returns the current video mode of the specified monitor. If
+ * you have created a full screen window for that monitor, the return value
+ * will depend on whether that window is iconified.
+ *
+ * @param[in] monitor The monitor to query.
+ * @return The current mode of the monitor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified monitor is
+ * disconnected or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_modes
+ * @sa @ref glfwGetVideoModes
+ *
+ * @since Added in version 3.0. Replaces `glfwGetDesktopMode`.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor);
+
+/*! @brief Generates a gamma ramp and sets it for the specified monitor.
+ *
+ * This function generates an appropriately sized gamma ramp from the specified
+ * exponent and then calls @ref glfwSetGammaRamp with it. The value must be
+ * a finite number greater than zero.
+ *
+ * The software controlled gamma ramp is applied _in addition_ to the hardware
+ * gamma correction, which today is usually an approximation of sRGB gamma.
+ * This means that setting a perfectly linear ramp, or gamma 1.0, will produce
+ * the default (usually sRGB-like) behavior.
+ *
+ * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref
+ * GLFW_SRGB_CAPABLE hint.
+ *
+ * @param[in] monitor The monitor whose gamma ramp to set.
+ * @param[in] gamma The desired exponent.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland Gamma handling is a privileged protocol, this function
+ * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_gamma
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma);
+
+/*! @brief Returns the current gamma ramp for the specified monitor.
+ *
+ * This function returns the current gamma ramp of the specified monitor.
+ *
+ * @param[in] monitor The monitor to query.
+ * @return The current gamma ramp, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland Gamma handling is a privileged protocol, this function
+ * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while
+ * returning `NULL`.
+ *
+ * @pointer_lifetime The returned structure and its arrays are allocated and
+ * freed by GLFW. You should not free them yourself. They are valid until the
+ * specified monitor is disconnected, this function is called again for that
+ * monitor or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_gamma
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor);
+
+/*! @brief Sets the current gamma ramp for the specified monitor.
+ *
+ * This function sets the current gamma ramp for the specified monitor. The
+ * original gamma ramp for that monitor is saved by GLFW the first time this
+ * function is called and is restored by @ref glfwTerminate.
+ *
+ * The software controlled gamma ramp is applied _in addition_ to the hardware
+ * gamma correction, which today is usually an approximation of sRGB gamma.
+ * This means that setting a perfectly linear ramp, or gamma 1.0, will produce
+ * the default (usually sRGB-like) behavior.
+ *
+ * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref
+ * GLFW_SRGB_CAPABLE hint.
+ *
+ * @param[in] monitor The monitor whose gamma ramp to set.
+ * @param[in] ramp The gamma ramp to use.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark The size of the specified gamma ramp should match the size of the
+ * current ramp for that monitor.
+ *
+ * @remark @win32 The gamma ramp size must be 256.
+ *
+ * @remark @wayland Gamma handling is a privileged protocol, this function
+ * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The specified gamma ramp is copied before this function
+ * returns.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_gamma
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp);
+
+/*! @brief Resets all window hints to their default values.
+ *
+ * This function resets all window hints to their
+ * [default values](@ref window_hints_values).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hints
+ * @sa @ref glfwWindowHint
+ * @sa @ref glfwWindowHintString
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwDefaultWindowHints(void);
+
+/*! @brief Sets the specified window hint to the desired value.
+ *
+ * This function sets hints for the next call to @ref glfwCreateWindow. The
+ * hints, once set, retain their values until changed by a call to this
+ * function or @ref glfwDefaultWindowHints, or until the library is terminated.
+ *
+ * Only integer value hints can be set with this function. String value hints
+ * are set with @ref glfwWindowHintString.
+ *
+ * This function does not check whether the specified hint values are valid.
+ * If you set hints to invalid values this will instead be reported by the next
+ * call to @ref glfwCreateWindow.
+ *
+ * Some hints are platform specific. These may be set on any platform but they
+ * will only affect their specific platform. Other platforms will ignore them.
+ * Setting these hints requires no platform specific headers or functions.
+ *
+ * @param[in] hint The [window hint](@ref window_hints) to set.
+ * @param[in] value The new value of the window hint.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hints
+ * @sa @ref glfwWindowHintString
+ * @sa @ref glfwDefaultWindowHints
+ *
+ * @since Added in version 3.0. Replaces `glfwOpenWindowHint`.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwWindowHint(int hint, int value);
+
+/*! @brief Sets the specified window hint to the desired value.
+ *
+ * This function sets hints for the next call to @ref glfwCreateWindow. The
+ * hints, once set, retain their values until changed by a call to this
+ * function or @ref glfwDefaultWindowHints, or until the library is terminated.
+ *
+ * Only string type hints can be set with this function. Integer value hints
+ * are set with @ref glfwWindowHint.
+ *
+ * This function does not check whether the specified hint values are valid.
+ * If you set hints to invalid values this will instead be reported by the next
+ * call to @ref glfwCreateWindow.
+ *
+ * Some hints are platform specific. These may be set on any platform but they
+ * will only affect their specific platform. Other platforms will ignore them.
+ * Setting these hints requires no platform specific headers or functions.
+ *
+ * @param[in] hint The [window hint](@ref window_hints) to set.
+ * @param[in] value The new value of the window hint.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @pointer_lifetime The specified string is copied before this function
+ * returns.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hints
+ * @sa @ref glfwWindowHint
+ * @sa @ref glfwDefaultWindowHints
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwWindowHintString(int hint, const char* value);
+
+/*! @brief Creates a window and its associated context.
+ *
+ * This function creates a window and its associated OpenGL or OpenGL ES
+ * context. Most of the options controlling how the window and its context
+ * should be created are specified with [window hints](@ref window_hints).
+ *
+ * Successful creation does not change which context is current. Before you
+ * can use the newly created context, you need to
+ * [make it current](@ref context_current). For information about the `share`
+ * parameter, see @ref context_sharing.
+ *
+ * The created window, framebuffer and context may differ from what you
+ * requested, as not all parameters and hints are
+ * [hard constraints](@ref window_hints_hard). This includes the size of the
+ * window, especially for full screen windows. To query the actual attributes
+ * of the created window, framebuffer and context, see @ref
+ * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize.
+ *
+ * To create a full screen window, you need to specify the monitor the window
+ * will cover. If no monitor is specified, the window will be windowed mode.
+ * Unless you have a way for the user to choose a specific monitor, it is
+ * recommended that you pick the primary monitor. For more information on how
+ * to query connected monitors, see @ref monitor_monitors.
+ *
+ * For full screen windows, the specified size becomes the resolution of the
+ * window's _desired video mode_. As long as a full screen window is not
+ * iconified, the supported video mode most closely matching the desired video
+ * mode is set for the specified monitor. For more information about full
+ * screen windows, including the creation of so called _windowed full screen_
+ * or _borderless full screen_ windows, see @ref window_windowed_full_screen.
+ *
+ * Once you have created the window, you can switch it between windowed and
+ * full screen mode with @ref glfwSetWindowMonitor. This will not affect its
+ * OpenGL or OpenGL ES context.
+ *
+ * By default, newly created windows use the placement recommended by the
+ * window system. To create the window at a specific position, make it
+ * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window
+ * hint, set its [position](@ref window_pos) and then [show](@ref window_hide)
+ * it.
+ *
+ * As long as at least one full screen window is not iconified, the screensaver
+ * is prohibited from starting.
+ *
+ * Window systems put limits on window sizes. Very large or very small window
+ * dimensions may be overridden by the window system on creation. Check the
+ * actual [size](@ref window_size) after creation.
+ *
+ * The [swap interval](@ref buffer_swap) is not set during window creation and
+ * the initial value may vary depending on driver settings and defaults.
+ *
+ * @param[in] width The desired width, in screen coordinates, of the window.
+ * This must be greater than zero.
+ * @param[in] height The desired height, in screen coordinates, of the window.
+ * This must be greater than zero.
+ * @param[in] title The initial, UTF-8 encoded window title.
+ * @param[in] monitor The monitor to use for full screen mode, or `NULL` for
+ * windowed mode.
+ * @param[in] share The window whose context to share resources with, or `NULL`
+ * to not share resources.
+ * @return The handle of the created window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref
+ * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @win32 Window creation will fail if the Microsoft GDI software
+ * OpenGL implementation is the only one available.
+ *
+ * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it
+ * will be set as the initial icon for the window. If no such icon is present,
+ * the `IDI_APPLICATION` icon will be used instead. To set a different icon,
+ * see @ref glfwSetWindowIcon.
+ *
+ * @remark @win32 The context to share resources with must not be current on
+ * any other thread.
+ *
+ * @remark @macos The OS only supports forward-compatible core profile contexts
+ * for OpenGL versions 3.2 and later. Before creating an OpenGL context of
+ * version 3.2 or later you must set the
+ * [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and
+ * [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly.
+ * OpenGL 3.0 and 3.1 contexts are not supported at all on macOS.
+ *
+ * @remark @macos The GLFW window has no icon, as it is not a document
+ * window, but the dock icon will be the same as the application bundle's icon.
+ * For more information on bundles, see the
+ * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
+ * in the Mac Developer Library.
+ *
+ * @remark @macos The first time a window is created the menu bar is created.
+ * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu
+ * bar. Otherwise a minimal menu bar is created manually with common commands
+ * like Hide, Quit and About. The About entry opens a minimal about dialog
+ * with information from the application's bundle. Menu bar creation can be
+ * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint.
+ *
+ * @remark @macos On OS X 10.10 and later the window frame will not be rendered
+ * at full resolution on Retina displays unless the
+ * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint)
+ * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the
+ * application bundle's `Info.plist`. For more information, see
+ * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html)
+ * in the Mac Developer Library. The GLFW test and example programs use
+ * a custom `Info.plist` template for this, which can be found as
+ * `CMake/MacOSXBundleInfo.plist.in` in the source tree.
+ *
+ * @remark @macos When activating frame autosaving with
+ * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified
+ * window size and position may be overridden by previously saved values.
+ *
+ * @remark @x11 Some window managers will not respect the placement of
+ * initially hidden windows.
+ *
+ * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for
+ * a window to reach its requested state. This means you may not be able to
+ * query the final size, position or other attributes directly after window
+ * creation.
+ *
+ * @remark @x11 The class part of the `WM_CLASS` window property will by
+ * default be set to the window title passed to this function. The instance
+ * part will use the contents of the `RESOURCE_NAME` environment variable, if
+ * present and not empty, or fall back to the window title. Set the
+ * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and
+ * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to
+ * override this.
+ *
+ * @remark @wayland Compositors should implement the xdg-decoration protocol
+ * for GLFW to decorate the window properly. If this protocol isn't
+ * supported, or if the compositor prefers client-side decorations, a very
+ * simple fallback frame will be drawn using the wp_viewporter protocol. A
+ * compositor can still emit close, maximize or fullscreen events, using for
+ * instance a keybind mechanism. If neither of these protocols is supported,
+ * the window won't be decorated.
+ *
+ * @remark @wayland A full screen window will not attempt to change the mode,
+ * no matter what the requested size or refresh rate.
+ *
+ * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol
+ * to be implemented in the user's compositor.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_creation
+ * @sa @ref glfwDestroyWindow
+ *
+ * @since Added in version 3.0. Replaces `glfwOpenWindow`.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);
+
+/*! @brief Destroys the specified window and its context.
+ *
+ * This function destroys the specified window and its context. On calling
+ * this function, no further callbacks will be called for that window.
+ *
+ * If the context of the specified window is current on the main thread, it is
+ * detached before being destroyed.
+ *
+ * @param[in] window The window to destroy.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @note The context of the specified window must not be current on any other
+ * thread when this function is called.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_creation
+ * @sa @ref glfwCreateWindow
+ *
+ * @since Added in version 3.0. Replaces `glfwCloseWindow`.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwDestroyWindow(GLFWwindow* window);
+
+/*! @brief Checks the close flag of the specified window.
+ *
+ * This function returns the value of the close flag of the specified window.
+ *
+ * @param[in] window The window to query.
+ * @return The value of the close flag.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref window_close
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI int glfwWindowShouldClose(GLFWwindow* window);
+
+/*! @brief Sets the close flag of the specified window.
+ *
+ * This function sets the value of the close flag of the specified window.
+ * This can be used to override the user's attempt to close the window, or
+ * to signal that it should be closed.
+ *
+ * @param[in] window The window whose flag to change.
+ * @param[in] value The new value.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref window_close
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value);
+
+/*! @brief Sets the title of the specified window.
+ *
+ * This function sets the window title, encoded as UTF-8, of the specified
+ * window.
+ *
+ * @param[in] window The window whose title to change.
+ * @param[in] title The UTF-8 encoded window title.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @macos The window title will not be updated until the next time you
+ * process events.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_title
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);
+
+/*! @brief Sets the icon for the specified window.
+ *
+ * This function sets the icon of the specified window. If passed an array of
+ * candidate images, those of or closest to the sizes desired by the system are
+ * selected. If no images are specified, the window reverts to its default
+ * icon.
+ *
+ * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight
+ * bits per channel with the red channel first. They are arranged canonically
+ * as packed sequential rows, starting from the top-left corner.
+ *
+ * The desired image sizes varies depending on platform and system settings.
+ * The selected images will be rescaled as needed. Good sizes include 16x16,
+ * 32x32 and 48x48.
+ *
+ * @param[in] window The window whose icon to set.
+ * @param[in] count The number of images in the specified array, or zero to
+ * revert to the default window icon.
+ * @param[in] images The images to create the icon from. This is ignored if
+ * count is zero.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The specified image data is copied before this function
+ * returns.
+ *
+ * @remark @macos The GLFW window has no icon, as it is not a document
+ * window, so this function does nothing. The dock icon will be the same as
+ * the application bundle's icon. For more information on bundles, see the
+ * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
+ * in the Mac Developer Library.
+ *
+ * @remark @wayland There is no existing protocol to change an icon, the
+ * window will thus inherit the one defined in the application's desktop file.
+ * This function always emits @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_icon
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images);
+
+/*! @brief Retrieves the position of the content area of the specified window.
+ *
+ * This function retrieves the position, in screen coordinates, of the
+ * upper-left corner of the content area of the specified window.
+ *
+ * Any or all of the position arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` position arguments will be set to zero.
+ *
+ * @param[in] window The window to query.
+ * @param[out] xpos Where to store the x-coordinate of the upper-left corner of
+ * the content area, or `NULL`.
+ * @param[out] ypos Where to store the y-coordinate of the upper-left corner of
+ * the content area, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland There is no way for an application to retrieve the global
+ * position of its windows, this function will always emit @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_pos
+ * @sa @ref glfwSetWindowPos
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos);
+
+/*! @brief Sets the position of the content area of the specified window.
+ *
+ * This function sets the position, in screen coordinates, of the upper-left
+ * corner of the content area of the specified windowed mode window. If the
+ * window is a full screen window, this function does nothing.
+ *
+ * __Do not use this function__ to move an already visible window unless you
+ * have very good reasons for doing so, as it will confuse and annoy the user.
+ *
+ * The window manager may put limits on what positions are allowed. GLFW
+ * cannot and should not override these limits.
+ *
+ * @param[in] window The window to query.
+ * @param[in] xpos The x-coordinate of the upper-left corner of the content area.
+ * @param[in] ypos The y-coordinate of the upper-left corner of the content area.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland There is no way for an application to set the global
+ * position of its windows, this function will always emit @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_pos
+ * @sa @ref glfwGetWindowPos
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos);
+
+/*! @brief Retrieves the size of the content area of the specified window.
+ *
+ * This function retrieves the size, in screen coordinates, of the content area
+ * of the specified window. If you wish to retrieve the size of the
+ * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize.
+ *
+ * Any or all of the size arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` size arguments will be set to zero.
+ *
+ * @param[in] window The window whose size to retrieve.
+ * @param[out] width Where to store the width, in screen coordinates, of the
+ * content area, or `NULL`.
+ * @param[out] height Where to store the height, in screen coordinates, of the
+ * content area, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_size
+ * @sa @ref glfwSetWindowSize
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height);
+
+/*! @brief Sets the size limits of the specified window.
+ *
+ * This function sets the size limits of the content area of the specified
+ * window. If the window is full screen, the size limits only take effect
+ * once it is made windowed. If the window is not resizable, this function
+ * does nothing.
+ *
+ * The size limits are applied immediately to a windowed mode window and may
+ * cause it to be resized.
+ *
+ * The maximum dimensions must be greater than or equal to the minimum
+ * dimensions and all must be greater than or equal to zero.
+ *
+ * @param[in] window The window to set limits for.
+ * @param[in] minwidth The minimum width, in screen coordinates, of the content
+ * area, or `GLFW_DONT_CARE`.
+ * @param[in] minheight The minimum height, in screen coordinates, of the
+ * content area, or `GLFW_DONT_CARE`.
+ * @param[in] maxwidth The maximum width, in screen coordinates, of the content
+ * area, or `GLFW_DONT_CARE`.
+ * @param[in] maxheight The maximum height, in screen coordinates, of the
+ * content area, or `GLFW_DONT_CARE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark If you set size limits and an aspect ratio that conflict, the
+ * results are undefined.
+ *
+ * @remark @wayland The size limits will not be applied until the window is
+ * actually resized, either by the user or by the compositor.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_sizelimits
+ * @sa @ref glfwSetWindowAspectRatio
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight);
+
+/*! @brief Sets the aspect ratio of the specified window.
+ *
+ * This function sets the required aspect ratio of the content area of the
+ * specified window. If the window is full screen, the aspect ratio only takes
+ * effect once it is made windowed. If the window is not resizable, this
+ * function does nothing.
+ *
+ * The aspect ratio is specified as a numerator and a denominator and both
+ * values must be greater than zero. For example, the common 16:9 aspect ratio
+ * is specified as 16 and 9, respectively.
+ *
+ * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect
+ * ratio limit is disabled.
+ *
+ * The aspect ratio is applied immediately to a windowed mode window and may
+ * cause it to be resized.
+ *
+ * @param[in] window The window to set limits for.
+ * @param[in] numer The numerator of the desired aspect ratio, or
+ * `GLFW_DONT_CARE`.
+ * @param[in] denom The denominator of the desired aspect ratio, or
+ * `GLFW_DONT_CARE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark If you set size limits and an aspect ratio that conflict, the
+ * results are undefined.
+ *
+ * @remark @wayland The aspect ratio will not be applied until the window is
+ * actually resized, either by the user or by the compositor.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_sizelimits
+ * @sa @ref glfwSetWindowSizeLimits
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom);
+
+/*! @brief Sets the size of the content area of the specified window.
+ *
+ * This function sets the size, in screen coordinates, of the content area of
+ * the specified window.
+ *
+ * For full screen windows, this function updates the resolution of its desired
+ * video mode and switches to the video mode closest to it, without affecting
+ * the window's context. As the context is unaffected, the bit depths of the
+ * framebuffer remain unchanged.
+ *
+ * If you wish to update the refresh rate of the desired video mode in addition
+ * to its resolution, see @ref glfwSetWindowMonitor.
+ *
+ * The window manager may put limits on what sizes are allowed. GLFW cannot
+ * and should not override these limits.
+ *
+ * @param[in] window The window to resize.
+ * @param[in] width The desired width, in screen coordinates, of the window
+ * content area.
+ * @param[in] height The desired height, in screen coordinates, of the window
+ * content area.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland A full screen window will not attempt to change the mode,
+ * no matter what the requested size.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_size
+ * @sa @ref glfwGetWindowSize
+ * @sa @ref glfwSetWindowMonitor
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height);
+
+/*! @brief Retrieves the size of the framebuffer of the specified window.
+ *
+ * This function retrieves the size, in pixels, of the framebuffer of the
+ * specified window. If you wish to retrieve the size of the window in screen
+ * coordinates, see @ref glfwGetWindowSize.
+ *
+ * Any or all of the size arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` size arguments will be set to zero.
+ *
+ * @param[in] window The window whose framebuffer to query.
+ * @param[out] width Where to store the width, in pixels, of the framebuffer,
+ * or `NULL`.
+ * @param[out] height Where to store the height, in pixels, of the framebuffer,
+ * or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_fbsize
+ * @sa @ref glfwSetFramebufferSizeCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height);
+
+/*! @brief Retrieves the size of the frame of the window.
+ *
+ * This function retrieves the size, in screen coordinates, of each edge of the
+ * frame of the specified window. This size includes the title bar, if the
+ * window has one. The size of the frame may vary depending on the
+ * [window-related hints](@ref window_hints_wnd) used to create it.
+ *
+ * Because this function retrieves the size of each window frame edge and not
+ * the offset along a particular coordinate axis, the retrieved values will
+ * always be zero or positive.
+ *
+ * Any or all of the size arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` size arguments will be set to zero.
+ *
+ * @param[in] window The window whose frame size to query.
+ * @param[out] left Where to store the size, in screen coordinates, of the left
+ * edge of the window frame, or `NULL`.
+ * @param[out] top Where to store the size, in screen coordinates, of the top
+ * edge of the window frame, or `NULL`.
+ * @param[out] right Where to store the size, in screen coordinates, of the
+ * right edge of the window frame, or `NULL`.
+ * @param[out] bottom Where to store the size, in screen coordinates, of the
+ * bottom edge of the window frame, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_size
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom);
+
+/*! @brief Retrieves the content scale for the specified window.
+ *
+ * This function retrieves the content scale for the specified window. The
+ * content scale is the ratio between the current DPI and the platform's
+ * default DPI. This is especially important for text and any UI elements. If
+ * the pixel dimensions of your UI scaled by this look appropriate on your
+ * machine then it should appear at a reasonable size on other machines
+ * regardless of their DPI and scaling settings. This relies on the system DPI
+ * and scaling settings being somewhat correct.
+ *
+ * On systems where each monitors can have its own content scale, the window
+ * content scale will depend on which monitor the system considers the window
+ * to be on.
+ *
+ * @param[in] window The window to query.
+ * @param[out] xscale Where to store the x-axis content scale, or `NULL`.
+ * @param[out] yscale Where to store the y-axis content scale, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_scale
+ * @sa @ref glfwSetWindowContentScaleCallback
+ * @sa @ref glfwGetMonitorContentScale
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale);
+
+/*! @brief Returns the opacity of the whole window.
+ *
+ * This function returns the opacity of the window, including any decorations.
+ *
+ * The opacity (or alpha) value is a positive finite number between zero and
+ * one, where zero is fully transparent and one is fully opaque. If the system
+ * does not support whole window transparency, this function always returns one.
+ *
+ * The initial opacity value for newly created windows is one.
+ *
+ * @param[in] window The window to query.
+ * @return The opacity value of the specified window.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_transparency
+ * @sa @ref glfwSetWindowOpacity
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window);
+
+/*! @brief Sets the opacity of the whole window.
+ *
+ * This function sets the opacity of the window, including any decorations.
+ *
+ * The opacity (or alpha) value is a positive finite number between zero and
+ * one, where zero is fully transparent and one is fully opaque.
+ *
+ * The initial opacity value for newly created windows is one.
+ *
+ * A window created with framebuffer transparency may not use whole window
+ * transparency. The results of doing this are undefined.
+ *
+ * @param[in] window The window to set the opacity for.
+ * @param[in] opacity The desired opacity of the specified window.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_transparency
+ * @sa @ref glfwGetWindowOpacity
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity);
+
+/*! @brief Iconifies the specified window.
+ *
+ * This function iconifies (minimizes) the specified window if it was
+ * previously restored. If the window is already iconified, this function does
+ * nothing.
+ *
+ * If the specified window is a full screen window, the original monitor
+ * resolution is restored until the window is restored.
+ *
+ * @param[in] window The window to iconify.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland There is no concept of iconification in wl_shell, this
+ * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated
+ * protocol.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_iconify
+ * @sa @ref glfwRestoreWindow
+ * @sa @ref glfwMaximizeWindow
+ *
+ * @since Added in version 2.1.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwIconifyWindow(GLFWwindow* window);
+
+/*! @brief Restores the specified window.
+ *
+ * This function restores the specified window if it was previously iconified
+ * (minimized) or maximized. If the window is already restored, this function
+ * does nothing.
+ *
+ * If the specified window is a full screen window, the resolution chosen for
+ * the window is restored on the selected monitor.
+ *
+ * @param[in] window The window to restore.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_iconify
+ * @sa @ref glfwIconifyWindow
+ * @sa @ref glfwMaximizeWindow
+ *
+ * @since Added in version 2.1.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwRestoreWindow(GLFWwindow* window);
+
+/*! @brief Maximizes the specified window.
+ *
+ * This function maximizes the specified window if it was previously not
+ * maximized. If the window is already maximized, this function does nothing.
+ *
+ * If the specified window is a full screen window, this function does nothing.
+ *
+ * @param[in] window The window to maximize.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @par Thread Safety
+ * This function may only be called from the main thread.
+ *
+ * @sa @ref window_iconify
+ * @sa @ref glfwIconifyWindow
+ * @sa @ref glfwRestoreWindow
+ *
+ * @since Added in GLFW 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwMaximizeWindow(GLFWwindow* window);
+
+/*! @brief Makes the specified window visible.
+ *
+ * This function makes the specified window visible if it was previously
+ * hidden. If the window is already visible or is in full screen mode, this
+ * function does nothing.
+ *
+ * By default, windowed mode windows are focused when shown
+ * Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint
+ * to change this behavior for all newly created windows, or change the
+ * behavior for an existing window with @ref glfwSetWindowAttrib.
+ *
+ * @param[in] window The window to make visible.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland Because Wayland wants every frame of the desktop to be
+ * complete, this function does not immediately make the window visible.
+ * Instead it will become visible the next time the window framebuffer is
+ * updated after this call.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hide
+ * @sa @ref glfwHideWindow
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwShowWindow(GLFWwindow* window);
+
+/*! @brief Hides the specified window.
+ *
+ * This function hides the specified window if it was previously visible. If
+ * the window is already hidden or is in full screen mode, this function does
+ * nothing.
+ *
+ * @param[in] window The window to hide.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hide
+ * @sa @ref glfwShowWindow
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwHideWindow(GLFWwindow* window);
+
+/*! @brief Brings the specified window to front and sets input focus.
+ *
+ * This function brings the specified window to front and sets input focus.
+ * The window should already be visible and not iconified.
+ *
+ * By default, both windowed and full screen mode windows are focused when
+ * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to
+ * disable this behavior.
+ *
+ * Also by default, windowed mode windows are focused when shown
+ * with @ref glfwShowWindow. Set the
+ * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior.
+ *
+ * __Do not use this function__ to steal focus from other applications unless
+ * you are certain that is what the user wants. Focus stealing can be
+ * extremely disruptive.
+ *
+ * For a less disruptive way of getting the user's attention, see
+ * [attention requests](@ref window_attention).
+ *
+ * @param[in] window The window to give input focus.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland It is not possible for an application to bring its windows
+ * to front, this function will always emit @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_focus
+ * @sa @ref window_attention
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwFocusWindow(GLFWwindow* window);
+
+/*! @brief Requests user attention to the specified window.
+ *
+ * This function requests user attention to the specified window. On
+ * platforms where this is not supported, attention is requested to the
+ * application as a whole.
+ *
+ * Once the user has given attention, usually by focusing the window or
+ * application, the system will end the request automatically.
+ *
+ * @param[in] window The window to request attention to.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @macos Attention is requested to the application as a whole, not the
+ * specific window.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_attention
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
+
+/*! @brief Returns the monitor that the window uses for full screen mode.
+ *
+ * This function returns the handle of the monitor that the specified window is
+ * in full screen on.
+ *
+ * @param[in] window The window to query.
+ * @return The monitor, or `NULL` if the window is in windowed mode or an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_monitor
+ * @sa @ref glfwSetWindowMonitor
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window);
+
+/*! @brief Sets the mode, monitor, video mode and placement of a window.
+ *
+ * This function sets the monitor that the window uses for full screen mode or,
+ * if the monitor is `NULL`, makes it windowed mode.
+ *
+ * When setting a monitor, this function updates the width, height and refresh
+ * rate of the desired video mode and switches to the video mode closest to it.
+ * The window position is ignored when setting a monitor.
+ *
+ * When the monitor is `NULL`, the position, width and height are used to
+ * place the window content area. The refresh rate is ignored when no monitor
+ * is specified.
+ *
+ * If you only wish to update the resolution of a full screen window or the
+ * size of a windowed mode window, see @ref glfwSetWindowSize.
+ *
+ * When a window transitions from full screen to windowed mode, this function
+ * restores any previous window settings such as whether it is decorated,
+ * floating, resizable, has size or aspect ratio limits, etc.
+ *
+ * @param[in] window The window whose monitor, size or video mode to set.
+ * @param[in] monitor The desired monitor, or `NULL` to set windowed mode.
+ * @param[in] xpos The desired x-coordinate of the upper-left corner of the
+ * content area.
+ * @param[in] ypos The desired y-coordinate of the upper-left corner of the
+ * content area.
+ * @param[in] width The desired with, in screen coordinates, of the content
+ * area or video mode.
+ * @param[in] height The desired height, in screen coordinates, of the content
+ * area or video mode.
+ * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode,
+ * or `GLFW_DONT_CARE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise
+ * affected by any resizing or mode switching, although you may need to update
+ * your viewport if the framebuffer size has changed.
+ *
+ * @remark @wayland The desired window position is ignored, as there is no way
+ * for an application to set this property.
+ *
+ * @remark @wayland Setting the window to full screen will not attempt to
+ * change the mode, no matter what the requested size or refresh rate.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_monitor
+ * @sa @ref window_full_screen
+ * @sa @ref glfwGetWindowMonitor
+ * @sa @ref glfwSetWindowSize
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);
+
+/*! @brief Returns an attribute of the specified window.
+ *
+ * This function returns the value of an attribute of the specified window or
+ * its OpenGL or OpenGL ES context.
+ *
+ * @param[in] window The window to query.
+ * @param[in] attrib The [window attribute](@ref window_attribs) whose value to
+ * return.
+ * @return The value of the attribute, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark Framebuffer related hints are not window attributes. See @ref
+ * window_attribs_fb for more information.
+ *
+ * @remark Zero is a valid value for many window and context related
+ * attributes so you cannot use a return value of zero as an indication of
+ * errors. However, this function should not fail as long as it is passed
+ * valid arguments and the library has been [initialized](@ref intro_init).
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_attribs
+ * @sa @ref glfwSetWindowAttrib
+ *
+ * @since Added in version 3.0. Replaces `glfwGetWindowParam` and
+ * `glfwGetGLVersion`.
+ *
+ * @ingroup window
+ */
+GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
+
+/*! @brief Sets an attribute of the specified window.
+ *
+ * This function sets the value of an attribute of the specified window.
+ *
+ * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),
+ * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib),
+ * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib),
+ * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and
+ * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib).
+ *
+ * Some of these attributes are ignored for full screen windows. The new
+ * value will take effect if the window is later made windowed.
+ *
+ * Some of these attributes are ignored for windowed mode windows. The new
+ * value will take effect if the window is later made full screen.
+ *
+ * @param[in] window The window to set the attribute for.
+ * @param[in] attrib A supported window attribute.
+ * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark Calling @ref glfwGetWindowAttrib will always return the latest
+ * value, even if that value is ignored by the current mode of the window.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_attribs
+ * @sa @ref glfwGetWindowAttrib
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value);
+
+/*! @brief Sets the user pointer of the specified window.
+ *
+ * This function sets the user-defined pointer of the specified window. The
+ * current value is retained until the window is destroyed. The initial value
+ * is `NULL`.
+ *
+ * @param[in] window The window whose pointer to set.
+ * @param[in] pointer The new value.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref window_userptr
+ * @sa @ref glfwGetWindowUserPointer
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer);
+
+/*! @brief Returns the user pointer of the specified window.
+ *
+ * This function returns the current value of the user-defined pointer of the
+ * specified window. The initial value is `NULL`.
+ *
+ * @param[in] window The window whose pointer to return.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref window_userptr
+ * @sa @ref glfwSetWindowUserPointer
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window);
+
+/*! @brief Sets the position callback for the specified window.
+ *
+ * This function sets the position callback of the specified window, which is
+ * called when the window is moved. The callback is provided with the
+ * position, in screen coordinates, of the upper-left corner of the content
+ * area of the window.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int xpos, int ypos)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWwindowposfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark @wayland This callback will never be called, as there is no way for
+ * an application to know its global position.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_pos
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback);
+
+/*! @brief Sets the size callback for the specified window.
+ *
+ * This function sets the size callback of the specified window, which is
+ * called when the window is resized. The callback is provided with the size,
+ * in screen coordinates, of the content area of the window.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int width, int height)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWwindowsizefun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_size
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback);
+
+/*! @brief Sets the close callback for the specified window.
+ *
+ * This function sets the close callback of the specified window, which is
+ * called when the user attempts to close the window, for example by clicking
+ * the close widget in the title bar.
+ *
+ * The close flag is set before this callback is called, but you can modify it
+ * at any time with @ref glfwSetWindowShouldClose.
+ *
+ * The close callback is not triggered by @ref glfwDestroyWindow.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWwindowclosefun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark @macos Selecting Quit from the application menu will trigger the
+ * close callback for all windows.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_close
+ *
+ * @since Added in version 2.5.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback);
+
+/*! @brief Sets the refresh callback for the specified window.
+ *
+ * This function sets the refresh callback of the specified window, which is
+ * called when the content area of the window needs to be redrawn, for example
+ * if the window has been exposed after having been covered by another window.
+ *
+ * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where
+ * the window contents are saved off-screen, this callback may be called only
+ * very infrequently or never at all.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window);
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWwindowrefreshfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_refresh
+ *
+ * @since Added in version 2.5.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback);
+
+/*! @brief Sets the focus callback for the specified window.
+ *
+ * This function sets the focus callback of the specified window, which is
+ * called when the window gains or loses input focus.
+ *
+ * After the focus callback is called for a window that lost input focus,
+ * synthetic key and mouse button release events will be generated for all such
+ * that had been pressed. For more information, see @ref glfwSetKeyCallback
+ * and @ref glfwSetMouseButtonCallback.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int focused)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWwindowfocusfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_focus
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback);
+
+/*! @brief Sets the iconify callback for the specified window.
+ *
+ * This function sets the iconification callback of the specified window, which
+ * is called when the window is iconified or restored.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int iconified)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWwindowiconifyfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark @wayland The wl_shell protocol has no concept of iconification,
+ * this callback will never be called when using this deprecated protocol.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_iconify
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback);
+
+/*! @brief Sets the maximize callback for the specified window.
+ *
+ * This function sets the maximization callback of the specified window, which
+ * is called when the window is maximized or restored.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int maximized)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWwindowmaximizefun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_maximize
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback);
+
+/*! @brief Sets the framebuffer resize callback for the specified window.
+ *
+ * This function sets the framebuffer resize callback of the specified window,
+ * which is called when the framebuffer of the specified window is resized.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int width, int height)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWframebuffersizefun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_fbsize
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback);
+
+/*! @brief Sets the window content scale callback for the specified window.
+ *
+ * This function sets the window content scale callback of the specified window,
+ * which is called when the content scale of the specified window changes.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, float xscale, float yscale)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWwindowcontentscalefun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_scale
+ * @sa @ref glfwGetWindowContentScale
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback);
+
+/*! @brief Processes all pending events.
+ *
+ * This function processes only those events that are already in the event
+ * queue and then returns immediately. Processing events will cause the window
+ * and input callbacks associated with those events to be called.
+ *
+ * On some platforms, a window move, resize or menu operation will cause event
+ * processing to block. This is due to how event processing is designed on
+ * those platforms. You can use the
+ * [window refresh callback](@ref window_refresh) to redraw the contents of
+ * your window when necessary during such operations.
+ *
+ * Do not assume that callbacks you set will _only_ be called in response to
+ * event processing functions like this one. While it is necessary to poll for
+ * events, window systems that require GLFW to register callbacks of its own
+ * can pass events to GLFW in response to many window system function calls.
+ * GLFW will pass those events on to the application callbacks before
+ * returning.
+ *
+ * Event processing is not required for joystick input to work.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref events
+ * @sa @ref glfwWaitEvents
+ * @sa @ref glfwWaitEventsTimeout
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwPollEvents(void);
+
+/*! @brief Waits until events are queued and processes them.
+ *
+ * This function puts the calling thread to sleep until at least one event is
+ * available in the event queue. Once one or more events are available,
+ * it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue
+ * are processed and the function then returns immediately. Processing events
+ * will cause the window and input callbacks associated with those events to be
+ * called.
+ *
+ * Since not all events are associated with callbacks, this function may return
+ * without a callback having been called even if you are monitoring all
+ * callbacks.
+ *
+ * On some platforms, a window move, resize or menu operation will cause event
+ * processing to block. This is due to how event processing is designed on
+ * those platforms. You can use the
+ * [window refresh callback](@ref window_refresh) to redraw the contents of
+ * your window when necessary during such operations.
+ *
+ * Do not assume that callbacks you set will _only_ be called in response to
+ * event processing functions like this one. While it is necessary to poll for
+ * events, window systems that require GLFW to register callbacks of its own
+ * can pass events to GLFW in response to many window system function calls.
+ * GLFW will pass those events on to the application callbacks before
+ * returning.
+ *
+ * Event processing is not required for joystick input to work.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref events
+ * @sa @ref glfwPollEvents
+ * @sa @ref glfwWaitEventsTimeout
+ *
+ * @since Added in version 2.5.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwWaitEvents(void);
+
+/*! @brief Waits with timeout until events are queued and processes them.
+ *
+ * This function puts the calling thread to sleep until at least one event is
+ * available in the event queue, or until the specified timeout is reached. If
+ * one or more events are available, it behaves exactly like @ref
+ * glfwPollEvents, i.e. the events in the queue are processed and the function
+ * then returns immediately. Processing events will cause the window and input
+ * callbacks associated with those events to be called.
+ *
+ * The timeout value must be a positive finite number.
+ *
+ * Since not all events are associated with callbacks, this function may return
+ * without a callback having been called even if you are monitoring all
+ * callbacks.
+ *
+ * On some platforms, a window move, resize or menu operation will cause event
+ * processing to block. This is due to how event processing is designed on
+ * those platforms. You can use the
+ * [window refresh callback](@ref window_refresh) to redraw the contents of
+ * your window when necessary during such operations.
+ *
+ * Do not assume that callbacks you set will _only_ be called in response to
+ * event processing functions like this one. While it is necessary to poll for
+ * events, window systems that require GLFW to register callbacks of its own
+ * can pass events to GLFW in response to many window system function calls.
+ * GLFW will pass those events on to the application callbacks before
+ * returning.
+ *
+ * Event processing is not required for joystick input to work.
+ *
+ * @param[in] timeout The maximum amount of time, in seconds, to wait.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref events
+ * @sa @ref glfwPollEvents
+ * @sa @ref glfwWaitEvents
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwWaitEventsTimeout(double timeout);
+
+/*! @brief Posts an empty event to the event queue.
+ *
+ * This function posts an empty event from the current thread to the event
+ * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref events
+ * @sa @ref glfwWaitEvents
+ * @sa @ref glfwWaitEventsTimeout
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwPostEmptyEvent(void);
+
+/*! @brief Returns the value of an input option for the specified window.
+ *
+ * This function returns the value of an input option for the specified window.
+ * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
+ * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or
+ * @ref GLFW_RAW_MOUSE_MOTION.
+ *
+ * @param[in] window The window to query.
+ * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
+ * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or
+ * `GLFW_RAW_MOUSE_MOTION`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref glfwSetInputMode
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
+
+/*! @brief Sets an input option for the specified window.
+ *
+ * This function sets an input mode option for the specified window. The mode
+ * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
+ * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or
+ * @ref GLFW_RAW_MOUSE_MOTION.
+ *
+ * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor
+ * modes:
+ * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally.
+ * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the
+ * content area of the window but does not restrict the cursor from leaving.
+ * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual
+ * and unlimited cursor movement. This is useful for implementing for
+ * example 3D camera controls.
+ *
+ * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to
+ * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are
+ * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS`
+ * the next time it is called even if the key had been released before the
+ * call. This is useful when you are only interested in whether keys have been
+ * pressed but not when or in which order.
+ *
+ * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either
+ * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it.
+ * If sticky mouse buttons are enabled, a mouse button press will ensure that
+ * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even
+ * if the mouse button had been released before the call. This is useful when
+ * you are only interested in whether mouse buttons have been pressed but not
+ * when or in which order.
+ *
+ * If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `GLFW_TRUE` to
+ * enable lock key modifier bits, or `GLFW_FALSE` to disable them. If enabled,
+ * callbacks that receive modifier bits will also have the @ref
+ * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on,
+ * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on.
+ *
+ * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE`
+ * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is
+ * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported,
+ * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref
+ * glfwRawMouseMotionSupported to check for support.
+ *
+ * @param[in] window The window whose input mode to set.
+ * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
+ * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or
+ * `GLFW_RAW_MOUSE_MOTION`.
+ * @param[in] value The new value of the specified input mode.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref glfwGetInputMode
+ *
+ * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value);
+
+/*! @brief Returns whether raw mouse motion is supported.
+ *
+ * This function returns whether raw mouse motion is supported on the current
+ * system. This status does not change after GLFW has been initialized so you
+ * only need to check this once. If you attempt to enable raw motion on
+ * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted.
+ *
+ * Raw mouse motion is closer to the actual motion of the mouse across
+ * a surface. It is not affected by the scaling and acceleration applied to
+ * the motion of the desktop cursor. That processing is suitable for a cursor
+ * while raw motion is better for controlling for example a 3D camera. Because
+ * of this, raw mouse motion is only provided when the cursor is disabled.
+ *
+ * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine,
+ * or `GLFW_FALSE` otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref raw_mouse_motion
+ * @sa @ref glfwSetInputMode
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwRawMouseMotionSupported(void);
+
+/*! @brief Returns the layout-specific name of the specified printable key.
+ *
+ * This function returns the name of the specified printable key, encoded as
+ * UTF-8. This is typically the character that key would produce without any
+ * modifier keys, intended for displaying key bindings to the user. For dead
+ * keys, it is typically the diacritic it would add to a character.
+ *
+ * __Do not use this function__ for [text input](@ref input_char). You will
+ * break text input for many languages even if it happens to work for yours.
+ *
+ * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key,
+ * otherwise the scancode is ignored. If you specify a non-printable key, or
+ * `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this
+ * function returns `NULL` but does not emit an error.
+ *
+ * This behavior allows you to always pass in the arguments in the
+ * [key callback](@ref input_key) without modification.
+ *
+ * The printable keys are:
+ * - `GLFW_KEY_APOSTROPHE`
+ * - `GLFW_KEY_COMMA`
+ * - `GLFW_KEY_MINUS`
+ * - `GLFW_KEY_PERIOD`
+ * - `GLFW_KEY_SLASH`
+ * - `GLFW_KEY_SEMICOLON`
+ * - `GLFW_KEY_EQUAL`
+ * - `GLFW_KEY_LEFT_BRACKET`
+ * - `GLFW_KEY_RIGHT_BRACKET`
+ * - `GLFW_KEY_BACKSLASH`
+ * - `GLFW_KEY_WORLD_1`
+ * - `GLFW_KEY_WORLD_2`
+ * - `GLFW_KEY_0` to `GLFW_KEY_9`
+ * - `GLFW_KEY_A` to `GLFW_KEY_Z`
+ * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9`
+ * - `GLFW_KEY_KP_DECIMAL`
+ * - `GLFW_KEY_KP_DIVIDE`
+ * - `GLFW_KEY_KP_MULTIPLY`
+ * - `GLFW_KEY_KP_SUBTRACT`
+ * - `GLFW_KEY_KP_ADD`
+ * - `GLFW_KEY_KP_EQUAL`
+ *
+ * Names for printable keys depend on keyboard layout, while names for
+ * non-printable keys are the same across layouts but depend on the application
+ * language and should be localized along with other user interface text.
+ *
+ * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`.
+ * @param[in] scancode The scancode of the key to query.
+ * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark The contents of the returned string may change when a keyboard
+ * layout change event is received.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_key_name
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI const char* glfwGetKeyName(int key, int scancode);
+
+/*! @brief Returns the platform-specific scancode of the specified key.
+ *
+ * This function returns the platform-specific scancode of the specified key.
+ *
+ * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this
+ * method will return `-1`.
+ *
+ * @param[in] key Any [named key](@ref keys).
+ * @return The platform-specific scancode for the key, or `-1` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref input_key
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwGetKeyScancode(int key);
+
+/*! @brief Returns the last reported state of a keyboard key for the specified
+ * window.
+ *
+ * This function returns the last state reported for the specified key to the
+ * specified window. The returned state is one of `GLFW_PRESS` or
+ * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to
+ * the key callback.
+ *
+ * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns
+ * `GLFW_PRESS` the first time you call it for a key that was pressed, even if
+ * that key has already been released.
+ *
+ * The key functions deal with physical keys, with [key tokens](@ref keys)
+ * named after their use on the standard US keyboard layout. If you want to
+ * input text, use the Unicode character callback instead.
+ *
+ * The [modifier key bit masks](@ref mods) are not key tokens and cannot be
+ * used with this function.
+ *
+ * __Do not use this function__ to implement [text input](@ref input_char).
+ *
+ * @param[in] window The desired window.
+ * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is
+ * not a valid key for this function.
+ * @return One of `GLFW_PRESS` or `GLFW_RELEASE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_key
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwGetKey(GLFWwindow* window, int key);
+
+/*! @brief Returns the last reported state of a mouse button for the specified
+ * window.
+ *
+ * This function returns the last state reported for the specified mouse button
+ * to the specified window. The returned state is one of `GLFW_PRESS` or
+ * `GLFW_RELEASE`.
+ *
+ * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function
+ * returns `GLFW_PRESS` the first time you call it for a mouse button that was
+ * pressed, even if that mouse button has already been released.
+ *
+ * @param[in] window The desired window.
+ * @param[in] button The desired [mouse button](@ref buttons).
+ * @return One of `GLFW_PRESS` or `GLFW_RELEASE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_mouse_button
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button);
+
+/*! @brief Retrieves the position of the cursor relative to the content area of
+ * the window.
+ *
+ * This function returns the position of the cursor, in screen coordinates,
+ * relative to the upper-left corner of the content area of the specified
+ * window.
+ *
+ * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor
+ * position is unbounded and limited only by the minimum and maximum values of
+ * a `double`.
+ *
+ * The coordinate can be converted to their integer equivalents with the
+ * `floor` function. Casting directly to an integer type works for positive
+ * coordinates, but fails for negative ones.
+ *
+ * Any or all of the position arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` position arguments will be set to zero.
+ *
+ * @param[in] window The desired window.
+ * @param[out] xpos Where to store the cursor x-coordinate, relative to the
+ * left edge of the content area, or `NULL`.
+ * @param[out] ypos Where to store the cursor y-coordinate, relative to the to
+ * top edge of the content area, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_pos
+ * @sa @ref glfwSetCursorPos
+ *
+ * @since Added in version 3.0. Replaces `glfwGetMousePos`.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos);
+
+/*! @brief Sets the position of the cursor, relative to the content area of the
+ * window.
+ *
+ * This function sets the position, in screen coordinates, of the cursor
+ * relative to the upper-left corner of the content area of the specified
+ * window. The window must have input focus. If the window does not have
+ * input focus when this function is called, it fails silently.
+ *
+ * __Do not use this function__ to implement things like camera controls. GLFW
+ * already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the
+ * cursor, transparently re-centers it and provides unconstrained cursor
+ * motion. See @ref glfwSetInputMode for more information.
+ *
+ * If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is
+ * unconstrained and limited only by the minimum and maximum values of
+ * a `double`.
+ *
+ * @param[in] window The desired window.
+ * @param[in] xpos The desired x-coordinate, relative to the left edge of the
+ * content area.
+ * @param[in] ypos The desired y-coordinate, relative to the top edge of the
+ * content area.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @wayland This function will only work when the cursor mode is
+ * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_pos
+ * @sa @ref glfwGetCursorPos
+ *
+ * @since Added in version 3.0. Replaces `glfwSetMousePos`.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos);
+
+/*! @brief Creates a custom cursor.
+ *
+ * Creates a new custom cursor image that can be set for a window with @ref
+ * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor.
+ * Any remaining cursors are destroyed by @ref glfwTerminate.
+ *
+ * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight
+ * bits per channel with the red channel first. They are arranged canonically
+ * as packed sequential rows, starting from the top-left corner.
+ *
+ * The cursor hotspot is specified in pixels, relative to the upper-left corner
+ * of the cursor image. Like all other coordinate systems in GLFW, the X-axis
+ * points to the right and the Y-axis points down.
+ *
+ * @param[in] image The desired cursor image.
+ * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot.
+ * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot.
+ * @return The handle of the created cursor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The specified image data is copied before this function
+ * returns.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_object
+ * @sa @ref glfwDestroyCursor
+ * @sa @ref glfwCreateStandardCursor
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot);
+
+/*! @brief Creates a cursor with a standard shape.
+ *
+ * Returns a cursor with a [standard shape](@ref shapes), that can be set for
+ * a window with @ref glfwSetCursor.
+ *
+ * @param[in] shape One of the [standard shapes](@ref shapes).
+ * @return A new cursor ready to use or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_object
+ * @sa @ref glfwCreateCursor
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape);
+
+/*! @brief Destroys a cursor.
+ *
+ * This function destroys a cursor previously created with @ref
+ * glfwCreateCursor. Any remaining cursors will be destroyed by @ref
+ * glfwTerminate.
+ *
+ * If the specified cursor is current for any window, that window will be
+ * reverted to the default cursor. This does not affect the cursor mode.
+ *
+ * @param[in] cursor The cursor object to destroy.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_object
+ * @sa @ref glfwCreateCursor
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);
+
+/*! @brief Sets the cursor for the window.
+ *
+ * This function sets the cursor image to be used when the cursor is over the
+ * content area of the specified window. The set cursor will only be visible
+ * when the [cursor mode](@ref cursor_mode) of the window is
+ * `GLFW_CURSOR_NORMAL`.
+ *
+ * On some platforms, the set cursor may not be visible unless the window also
+ * has input focus.
+ *
+ * @param[in] window The window to set the cursor for.
+ * @param[in] cursor The cursor to set, or `NULL` to switch back to the default
+ * arrow cursor.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_object
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
+
+/*! @brief Sets the key callback.
+ *
+ * This function sets the key callback of the specified window, which is called
+ * when a key is pressed, repeated or released.
+ *
+ * The key functions deal with physical keys, with layout independent
+ * [key tokens](@ref keys) named after their values in the standard US keyboard
+ * layout. If you want to input text, use the
+ * [character callback](@ref glfwSetCharCallback) instead.
+ *
+ * When a window loses input focus, it will generate synthetic key release
+ * events for all pressed keys. You can tell these events from user-generated
+ * events by the fact that the synthetic ones are generated after the focus
+ * loss event has been processed, i.e. after the
+ * [window focus callback](@ref glfwSetWindowFocusCallback) has been called.
+ *
+ * The scancode of a key is specific to that platform or sometimes even to that
+ * machine. Scancodes are intended to allow users to bind keys that don't have
+ * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their
+ * state is not saved and so it cannot be queried with @ref glfwGetKey.
+ *
+ * Sometimes GLFW needs to generate synthetic key events, in which case the
+ * scancode may be zero.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new key callback, or `NULL` to remove the currently
+ * set callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWkeyfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_key
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback);
+
+/*! @brief Sets the Unicode character callback.
+ *
+ * This function sets the character callback of the specified window, which is
+ * called when a Unicode character is input.
+ *
+ * The character callback is intended for Unicode text input. As it deals with
+ * characters, it is keyboard layout dependent, whereas the
+ * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1
+ * to physical keys, as a key may produce zero, one or more characters. If you
+ * want to know whether a specific physical key was pressed or released, see
+ * the key callback instead.
+ *
+ * The character callback behaves as system text input normally does and will
+ * not be called if modifier keys are held down that would prevent normal text
+ * input on that platform, for example a Super (Command) key on macOS or Alt key
+ * on Windows.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, unsigned int codepoint)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWcharfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_char
+ *
+ * @since Added in version 2.4.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback);
+
+/*! @brief Sets the Unicode character with modifiers callback.
+ *
+ * This function sets the character with modifiers callback of the specified
+ * window, which is called when a Unicode character is input regardless of what
+ * modifier keys are used.
+ *
+ * The character with modifiers callback is intended for implementing custom
+ * Unicode character input. For regular Unicode text input, see the
+ * [character callback](@ref glfwSetCharCallback). Like the character
+ * callback, the character with modifiers callback deals with characters and is
+ * keyboard layout dependent. Characters do not map 1:1 to physical keys, as
+ * a key may produce zero, one or more characters. If you want to know whether
+ * a specific physical key was pressed or released, see the
+ * [key callback](@ref glfwSetKeyCallback) instead.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or an
+ * [error](@ref error_handling) occurred.
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, unsigned int codepoint, int mods)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWcharmodsfun).
+ *
+ * @deprecated Scheduled for removal in version 4.0.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_char
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback);
+
+/*! @brief Sets the mouse button callback.
+ *
+ * This function sets the mouse button callback of the specified window, which
+ * is called when a mouse button is pressed or released.
+ *
+ * When a window loses input focus, it will generate synthetic mouse button
+ * release events for all pressed mouse buttons. You can tell these events
+ * from user-generated events by the fact that the synthetic ones are generated
+ * after the focus loss event has been processed, i.e. after the
+ * [window focus callback](@ref glfwSetWindowFocusCallback) has been called.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int button, int action, int mods)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWmousebuttonfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_mouse_button
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback);
+
+/*! @brief Sets the cursor position callback.
+ *
+ * This function sets the cursor position callback of the specified window,
+ * which is called when the cursor is moved. The callback is provided with the
+ * position, in screen coordinates, relative to the upper-left corner of the
+ * content area of the window.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, double xpos, double ypos);
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWcursorposfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_pos
+ *
+ * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback);
+
+/*! @brief Sets the cursor enter/leave callback.
+ *
+ * This function sets the cursor boundary crossing callback of the specified
+ * window, which is called when the cursor enters or leaves the content area of
+ * the window.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int entered)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWcursorenterfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_enter
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback);
+
+/*! @brief Sets the scroll callback.
+ *
+ * This function sets the scroll callback of the specified window, which is
+ * called when a scrolling device is used, such as a mouse wheel or scrolling
+ * area of a touchpad.
+ *
+ * The scroll callback receives all scrolling input, like that from a mouse
+ * wheel or a touchpad scrolling area.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new scroll callback, or `NULL` to remove the
+ * currently set callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, double xoffset, double yoffset)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWscrollfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref scrolling
+ *
+ * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback);
+
+/*! @brief Sets the path drop callback.
+ *
+ * This function sets the path drop callback of the specified window, which is
+ * called when one or more dragged paths are dropped on the window.
+ *
+ * Because the path array and its strings may have been generated specifically
+ * for that event, they are not guaranteed to be valid after the callback has
+ * returned. If you wish to use them after the callback returns, you need to
+ * make a deep copy.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] callback The new file drop callback, or `NULL` to remove the
+ * currently set callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(GLFWwindow* window, int path_count, const char* paths[])
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWdropfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark @wayland File drop is currently unimplemented.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref path_drop
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback);
+
+/*! @brief Returns whether the specified joystick is present.
+ *
+ * This function returns whether the specified joystick is present.
+ *
+ * There is no need to call this function before other functions that accept
+ * a joystick ID, as they all check for presence before performing any other
+ * work.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick
+ *
+ * @since Added in version 3.0. Replaces `glfwGetJoystickParam`.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwJoystickPresent(int jid);
+
+/*! @brief Returns the values of all axes of the specified joystick.
+ *
+ * This function returns the values of all axes of the specified joystick.
+ * Each element in the array is a value between -1.0 and 1.0.
+ *
+ * If the specified joystick is not present this function will return `NULL`
+ * but will not generate an error. This can be used instead of first calling
+ * @ref glfwJoystickPresent.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @param[out] count Where to store the number of axis values in the returned
+ * array. This is set to zero if the joystick is not present or an error
+ * occurred.
+ * @return An array of axis values, or `NULL` if the joystick is not present or
+ * an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_axis
+ *
+ * @since Added in version 3.0. Replaces `glfwGetJoystickPos`.
+ *
+ * @ingroup input
+ */
+GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count);
+
+/*! @brief Returns the state of all buttons of the specified joystick.
+ *
+ * This function returns the state of all buttons of the specified joystick.
+ * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`.
+ *
+ * For backward compatibility with earlier versions that did not have @ref
+ * glfwGetJoystickHats, the button array also includes all hats, each
+ * represented as four buttons. The hats are in the same order as returned by
+ * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and
+ * _left_. To disable these extra buttons, set the @ref
+ * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization.
+ *
+ * If the specified joystick is not present this function will return `NULL`
+ * but will not generate an error. This can be used instead of first calling
+ * @ref glfwJoystickPresent.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @param[out] count Where to store the number of button states in the returned
+ * array. This is set to zero if the joystick is not present or an error
+ * occurred.
+ * @return An array of button states, or `NULL` if the joystick is not present
+ * or an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_button
+ *
+ * @since Added in version 2.2.
+ * @glfw3 Changed to return a dynamic array.
+ *
+ * @ingroup input
+ */
+GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count);
+
+/*! @brief Returns the state of all hats of the specified joystick.
+ *
+ * This function returns the state of all hats of the specified joystick.
+ * Each element in the array is one of the following values:
+ *
+ * Name | Value
+ * ---- | -----
+ * `GLFW_HAT_CENTERED` | 0
+ * `GLFW_HAT_UP` | 1
+ * `GLFW_HAT_RIGHT` | 2
+ * `GLFW_HAT_DOWN` | 4
+ * `GLFW_HAT_LEFT` | 8
+ * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP`
+ * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN`
+ * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP`
+ * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN`
+ *
+ * The diagonal directions are bitwise combinations of the primary (up, right,
+ * down and left) directions and you can test for these individually by ANDing
+ * it with the corresponding direction.
+ *
+ * @code
+ * if (hats[2] & GLFW_HAT_RIGHT)
+ * {
+ * // State of hat 2 could be right-up, right or right-down
+ * }
+ * @endcode
+ *
+ * If the specified joystick is not present this function will return `NULL`
+ * but will not generate an error. This can be used instead of first calling
+ * @ref glfwJoystickPresent.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @param[out] count Where to store the number of hat states in the returned
+ * array. This is set to zero if the joystick is not present or an error
+ * occurred.
+ * @return An array of hat states, or `NULL` if the joystick is not present
+ * or an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected, this function is called again for that joystick or the library
+ * is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_hat
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count);
+
+/*! @brief Returns the name of the specified joystick.
+ *
+ * This function returns the name, encoded as UTF-8, of the specified joystick.
+ * The returned string is allocated and freed by GLFW. You should not free it
+ * yourself.
+ *
+ * If the specified joystick is not present this function will return `NULL`
+ * but will not generate an error. This can be used instead of first calling
+ * @ref glfwJoystickPresent.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick
+ * is not present or an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_name
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI const char* glfwGetJoystickName(int jid);
+
+/*! @brief Returns the SDL compatible GUID of the specified joystick.
+ *
+ * This function returns the SDL compatible GUID, as a UTF-8 encoded
+ * hexadecimal string, of the specified joystick. The returned string is
+ * allocated and freed by GLFW. You should not free it yourself.
+ *
+ * The GUID is what connects a joystick to a gamepad mapping. A connected
+ * joystick will always have a GUID even if there is no gamepad mapping
+ * assigned to it.
+ *
+ * If the specified joystick is not present this function will return `NULL`
+ * but will not generate an error. This can be used instead of first calling
+ * @ref glfwJoystickPresent.
+ *
+ * The GUID uses the format introduced in SDL 2.0.5. This GUID tries to
+ * uniquely identify the make and model of a joystick but does not identify
+ * a specific unit, e.g. all wired Xbox 360 controllers will have the same
+ * GUID on that platform. The GUID for a unit may vary between platforms
+ * depending on what hardware information the platform specific APIs provide.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick
+ * is not present or an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref gamepad
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI const char* glfwGetJoystickGUID(int jid);
+
+/*! @brief Sets the user pointer of the specified joystick.
+ *
+ * This function sets the user-defined pointer of the specified joystick. The
+ * current value is retained until the joystick is disconnected. The initial
+ * value is `NULL`.
+ *
+ * This function may be called from the joystick callback, even for a joystick
+ * that is being disconnected.
+ *
+ * @param[in] jid The joystick whose pointer to set.
+ * @param[in] pointer The new value.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref joystick_userptr
+ * @sa @ref glfwGetJoystickUserPointer
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer);
+
+/*! @brief Returns the user pointer of the specified joystick.
+ *
+ * This function returns the current value of the user-defined pointer of the
+ * specified joystick. The initial value is `NULL`.
+ *
+ * This function may be called from the joystick callback, even for a joystick
+ * that is being disconnected.
+ *
+ * @param[in] jid The joystick whose pointer to return.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref joystick_userptr
+ * @sa @ref glfwSetJoystickUserPointer
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI void* glfwGetJoystickUserPointer(int jid);
+
+/*! @brief Returns whether the specified joystick has a gamepad mapping.
+ *
+ * This function returns whether the specified joystick is both present and has
+ * a gamepad mapping.
+ *
+ * If the specified joystick is present but does not have a gamepad mapping
+ * this function will return `GLFW_FALSE` but will not generate an error. Call
+ * @ref glfwJoystickPresent to check if a joystick is present regardless of
+ * whether it has a mapping.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @return `GLFW_TRUE` if a joystick is both present and has a gamepad mapping,
+ * or `GLFW_FALSE` otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref gamepad
+ * @sa @ref glfwGetGamepadState
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwJoystickIsGamepad(int jid);
+
+/*! @brief Sets the joystick configuration callback.
+ *
+ * This function sets the joystick configuration callback, or removes the
+ * currently set callback. This is called when a joystick is connected to or
+ * disconnected from the system.
+ *
+ * For joystick connection and disconnection events to be delivered on all
+ * platforms, you need to call one of the [event processing](@ref events)
+ * functions. Joystick disconnection may also be detected and the callback
+ * called by joystick functions. The function will then return whatever it
+ * returns if the joystick is not present.
+ *
+ * @param[in] callback The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @callback_signature
+ * @code
+ * void function_name(int jid, int event)
+ * @endcode
+ * For more information about the callback parameters, see the
+ * [function pointer type](@ref GLFWjoystickfun).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_event
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback);
+
+/*! @brief Adds the specified SDL_GameControllerDB gamepad mappings.
+ *
+ * This function parses the specified ASCII encoded string and updates the
+ * internal list with any gamepad mappings it finds. This string may
+ * contain either a single gamepad mapping or many mappings separated by
+ * newlines. The parser supports the full format of the `gamecontrollerdb.txt`
+ * source file including empty lines and comments.
+ *
+ * See @ref gamepad_mapping for a description of the format.
+ *
+ * If there is already a gamepad mapping for a given GUID in the internal list,
+ * it will be replaced by the one passed to this function. If the library is
+ * terminated and re-initialized the internal list will revert to the built-in
+ * default.
+ *
+ * @param[in] string The string containing the gamepad mappings.
+ * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_VALUE.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref gamepad
+ * @sa @ref glfwJoystickIsGamepad
+ * @sa @ref glfwGetGamepadName
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwUpdateGamepadMappings(const char* string);
+
+/*! @brief Returns the human-readable gamepad name for the specified joystick.
+ *
+ * This function returns the human-readable name of the gamepad from the
+ * gamepad mapping assigned to the specified joystick.
+ *
+ * If the specified joystick is not present or does not have a gamepad mapping
+ * this function will return `NULL` but will not generate an error. Call
+ * @ref glfwJoystickPresent to check whether it is present regardless of
+ * whether it has a mapping.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @return The UTF-8 encoded name of the gamepad, or `NULL` if the
+ * joystick is not present, does not have a mapping or an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref GLFW_INVALID_ENUM.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected, the gamepad mappings are updated or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref gamepad
+ * @sa @ref glfwJoystickIsGamepad
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI const char* glfwGetGamepadName(int jid);
+
+/*! @brief Retrieves the state of the specified joystick remapped as a gamepad.
+ *
+ * This function retrieves the state of the specified joystick remapped to
+ * an Xbox-like gamepad.
+ *
+ * If the specified joystick is not present or does not have a gamepad mapping
+ * this function will return `GLFW_FALSE` but will not generate an error. Call
+ * @ref glfwJoystickPresent to check whether it is present regardless of
+ * whether it has a mapping.
+ *
+ * The Guide button may not be available for input as it is often hooked by the
+ * system or the Steam client.
+ *
+ * Not all devices have all the buttons or axes provided by @ref
+ * GLFWgamepadstate. Unavailable buttons and axes will always report
+ * `GLFW_RELEASE` and 0.0 respectively.
+ *
+ * @param[in] jid The [joystick](@ref joysticks) to query.
+ * @param[out] state The gamepad input state of the joystick.
+ * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is
+ * connected, it has no gamepad mapping or an [error](@ref error_handling)
+ * occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref gamepad
+ * @sa @ref glfwUpdateGamepadMappings
+ * @sa @ref glfwJoystickIsGamepad
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state);
+
+/*! @brief Sets the clipboard to the specified string.
+ *
+ * This function sets the system clipboard to the specified, UTF-8 encoded
+ * string.
+ *
+ * @param[in] window Deprecated. Any valid window or `NULL`.
+ * @param[in] string A UTF-8 encoded string.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The specified string is copied before this function
+ * returns.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref clipboard
+ * @sa @ref glfwGetClipboardString
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string);
+
+/*! @brief Returns the contents of the clipboard as a string.
+ *
+ * This function returns the contents of the system clipboard, if it contains
+ * or is convertible to a UTF-8 encoded string. If the clipboard is empty or
+ * if its contents cannot be converted, `NULL` is returned and a @ref
+ * GLFW_FORMAT_UNAVAILABLE error is generated.
+ *
+ * @param[in] window Deprecated. Any valid window or `NULL`.
+ * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL`
+ * if an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the next call to @ref
+ * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library
+ * is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref clipboard
+ * @sa @ref glfwSetClipboardString
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window);
+
+/*! @brief Returns the GLFW time.
+ *
+ * This function returns the current GLFW time, in seconds. Unless the time
+ * has been set using @ref glfwSetTime it measures time elapsed since GLFW was
+ * initialized.
+ *
+ * This function and @ref glfwSetTime are helper functions on top of @ref
+ * glfwGetTimerFrequency and @ref glfwGetTimerValue.
+ *
+ * The resolution of the timer is system dependent, but is usually on the order
+ * of a few micro- or nanoseconds. It uses the highest-resolution monotonic
+ * time source on each supported platform.
+ *
+ * @return The current time, in seconds, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Reading and
+ * writing of the internal base time is not atomic, so it needs to be
+ * externally synchronized with calls to @ref glfwSetTime.
+ *
+ * @sa @ref time
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI double glfwGetTime(void);
+
+/*! @brief Sets the GLFW time.
+ *
+ * This function sets the current GLFW time, in seconds. The value must be
+ * a positive finite number less than or equal to 18446744073.0, which is
+ * approximately 584.5 years.
+ *
+ * This function and @ref glfwGetTime are helper functions on top of @ref
+ * glfwGetTimerFrequency and @ref glfwGetTimerValue.
+ *
+ * @param[in] time The new value, in seconds.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_VALUE.
+ *
+ * @remark The upper limit of GLFW time is calculated as
+ * floor((264 - 1) / 109) and is due to implementations
+ * storing nanoseconds in 64 bits. The limit may be increased in the future.
+ *
+ * @thread_safety This function may be called from any thread. Reading and
+ * writing of the internal base time is not atomic, so it needs to be
+ * externally synchronized with calls to @ref glfwGetTime.
+ *
+ * @sa @ref time
+ *
+ * @since Added in version 2.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetTime(double time);
+
+/*! @brief Returns the current value of the raw timer.
+ *
+ * This function returns the current value of the raw timer, measured in
+ * 1 / frequency seconds. To get the frequency, call @ref
+ * glfwGetTimerFrequency.
+ *
+ * @return The value of the timer, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref time
+ * @sa @ref glfwGetTimerFrequency
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI uint64_t glfwGetTimerValue(void);
+
+/*! @brief Returns the frequency, in Hz, of the raw timer.
+ *
+ * This function returns the frequency, in Hz, of the raw timer.
+ *
+ * @return The frequency of the timer, in Hz, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref time
+ * @sa @ref glfwGetTimerValue
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI uint64_t glfwGetTimerFrequency(void);
+
+/*! @brief Makes the context of the specified window current for the calling
+ * thread.
+ *
+ * This function makes the OpenGL or OpenGL ES context of the specified window
+ * current on the calling thread. A context must only be made current on
+ * a single thread at a time and each thread can have only a single current
+ * context at a time.
+ *
+ * When moving a context between threads, you must make it non-current on the
+ * old thread before making it current on the new one.
+ *
+ * By default, making a context non-current implicitly forces a pipeline flush.
+ * On machines that support `GL_KHR_context_flush_control`, you can control
+ * whether a context performs this flush by setting the
+ * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint)
+ * hint.
+ *
+ * The specified window must have an OpenGL or OpenGL ES context. Specifying
+ * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT
+ * error.
+ *
+ * @param[in] window The window whose context to make current, or `NULL` to
+ * detach the current context.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref context_current
+ * @sa @ref glfwGetCurrentContext
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window);
+
+/*! @brief Returns the window whose context is current on the calling thread.
+ *
+ * This function returns the window whose OpenGL or OpenGL ES context is
+ * current on the calling thread.
+ *
+ * @return The window whose context is current, or `NULL` if no window's
+ * context is current.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref context_current
+ * @sa @ref glfwMakeContextCurrent
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI GLFWwindow* glfwGetCurrentContext(void);
+
+/*! @brief Swaps the front and back buffers of the specified window.
+ *
+ * This function swaps the front and back buffers of the specified window when
+ * rendering with OpenGL or OpenGL ES. If the swap interval is greater than
+ * zero, the GPU driver waits the specified number of screen updates before
+ * swapping the buffers.
+ *
+ * The specified window must have an OpenGL or OpenGL ES context. Specifying
+ * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT
+ * error.
+ *
+ * This function does not apply to Vulkan. If you are rendering with Vulkan,
+ * see `vkQueuePresentKHR` instead.
+ *
+ * @param[in] window The window whose buffers to swap.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark __EGL:__ The context of the specified window must be current on the
+ * calling thread.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref buffer_swap
+ * @sa @ref glfwSwapInterval
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSwapBuffers(GLFWwindow* window);
+
+/*! @brief Sets the swap interval for the current context.
+ *
+ * This function sets the swap interval for the current OpenGL or OpenGL ES
+ * context, i.e. the number of screen updates to wait from the time @ref
+ * glfwSwapBuffers was called before swapping the buffers and returning. This
+ * is sometimes called _vertical synchronization_, _vertical retrace
+ * synchronization_ or just _vsync_.
+ *
+ * A context that supports either of the `WGL_EXT_swap_control_tear` and
+ * `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap
+ * intervals, which allows the driver to swap immediately even if a frame
+ * arrives a little bit late. You can check for these extensions with @ref
+ * glfwExtensionSupported.
+ *
+ * A context must be current on the calling thread. Calling this function
+ * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.
+ *
+ * This function does not apply to Vulkan. If you are rendering with Vulkan,
+ * see the present mode of your swapchain instead.
+ *
+ * @param[in] interval The minimum number of screen updates to wait for
+ * until the buffers are swapped by @ref glfwSwapBuffers.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark This function is not called during context creation, leaving the
+ * swap interval set to whatever is the default on that platform. This is done
+ * because some swap interval extensions used by GLFW do not allow the swap
+ * interval to be reset to zero once it has been set to a non-zero value.
+ *
+ * @remark Some GPU drivers do not honor the requested swap interval, either
+ * because of a user setting that overrides the application's request or due to
+ * bugs in the driver.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref buffer_swap
+ * @sa @ref glfwSwapBuffers
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI void glfwSwapInterval(int interval);
+
+/*! @brief Returns whether the specified extension is available.
+ *
+ * This function returns whether the specified
+ * [API extension](@ref context_glext) is supported by the current OpenGL or
+ * OpenGL ES context. It searches both for client API extension and context
+ * creation API extensions.
+ *
+ * A context must be current on the calling thread. Calling this function
+ * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.
+ *
+ * As this functions retrieves and searches one or more extension strings each
+ * call, it is recommended that you cache its results if it is going to be used
+ * frequently. The extension strings will not change during the lifetime of
+ * a context, so there is no danger in doing this.
+ *
+ * This function does not apply to Vulkan. If you are using Vulkan, see @ref
+ * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties`
+ * and `vkEnumerateDeviceExtensionProperties` instead.
+ *
+ * @param[in] extension The ASCII encoded name of the extension.
+ * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE`
+ * otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref context_glext
+ * @sa @ref glfwGetProcAddress
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI int glfwExtensionSupported(const char* extension);
+
+/*! @brief Returns the address of the specified function for the current
+ * context.
+ *
+ * This function returns the address of the specified OpenGL or OpenGL ES
+ * [core or extension function](@ref context_glext), if it is supported
+ * by the current context.
+ *
+ * A context must be current on the calling thread. Calling this function
+ * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.
+ *
+ * This function does not apply to Vulkan. If you are rendering with Vulkan,
+ * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and
+ * `vkGetDeviceProcAddr` instead.
+ *
+ * @param[in] procname The ASCII encoded name of the function.
+ * @return The address of the function, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark The address of a given function is not guaranteed to be the same
+ * between contexts.
+ *
+ * @remark This function may return a non-`NULL` address despite the
+ * associated version or extension not being available. Always check the
+ * context version or extension string first.
+ *
+ * @pointer_lifetime The returned function pointer is valid until the context
+ * is destroyed or the library is terminated.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref context_glext
+ * @sa @ref glfwExtensionSupported
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname);
+
+/*! @brief Returns whether the Vulkan loader and an ICD have been found.
+ *
+ * This function returns whether the Vulkan loader and any minimally functional
+ * ICD have been found.
+ *
+ * The availability of a Vulkan loader and even an ICD does not by itself guarantee that
+ * surface creation or even instance creation is possible. Call @ref
+ * glfwGetRequiredInstanceExtensions to check whether the extensions necessary for Vulkan
+ * surface creation are available and @ref glfwGetPhysicalDevicePresentationSupport to
+ * check whether a queue family of a physical device supports image presentation.
+ *
+ * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE`
+ * otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref vulkan_support
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI int glfwVulkanSupported(void);
+
+/*! @brief Returns the Vulkan instance extensions required by GLFW.
+ *
+ * This function returns an array of names of Vulkan instance extensions required
+ * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the
+ * list will always contain `VK_KHR_surface`, so if you don't require any
+ * additional extensions you can pass this list directly to the
+ * `VkInstanceCreateInfo` struct.
+ *
+ * If Vulkan is not available on the machine, this function returns `NULL` and
+ * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported
+ * to check whether Vulkan is at least minimally available.
+ *
+ * If Vulkan is available but no set of extensions allowing window surface
+ * creation was found, this function returns `NULL`. You may still use Vulkan
+ * for off-screen rendering and compute work.
+ *
+ * @param[out] count Where to store the number of extensions in the returned
+ * array. This is set to zero if an error occurred.
+ * @return An array of ASCII encoded extension names, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_API_UNAVAILABLE.
+ *
+ * @remark Additional extensions may be required by future versions of GLFW.
+ * You should check if any extensions you wish to enable are already in the
+ * returned array, as it is an error to specify an extension more than once in
+ * the `VkInstanceCreateInfo` struct.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is guaranteed to be valid only until the
+ * library is terminated.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref vulkan_ext
+ * @sa @ref glfwCreateWindowSurface
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count);
+
+#if defined(VK_VERSION_1_0)
+
+/*! @brief Returns the address of the specified Vulkan instance function.
+ *
+ * This function returns the address of the specified Vulkan core or extension
+ * function for the specified instance. If instance is set to `NULL` it can
+ * return any function exported from the Vulkan loader, including at least the
+ * following functions:
+ *
+ * - `vkEnumerateInstanceExtensionProperties`
+ * - `vkEnumerateInstanceLayerProperties`
+ * - `vkCreateInstance`
+ * - `vkGetInstanceProcAddr`
+ *
+ * If Vulkan is not available on the machine, this function returns `NULL` and
+ * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported
+ * to check whether Vulkan is at least minimally available.
+ *
+ * This function is equivalent to calling `vkGetInstanceProcAddr` with
+ * a platform-specific query of the Vulkan loader as a fallback.
+ *
+ * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve
+ * functions related to instance creation.
+ * @param[in] procname The ASCII encoded name of the function.
+ * @return The address of the function, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_API_UNAVAILABLE.
+ *
+ * @pointer_lifetime The returned function pointer is valid until the library
+ * is terminated.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref vulkan_proc
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname);
+
+/*! @brief Returns whether the specified queue family can present images.
+ *
+ * This function returns whether the specified queue family of the specified
+ * physical device supports presentation to the platform GLFW was built for.
+ *
+ * If Vulkan or the required window surface creation instance extensions are
+ * not available on the machine, or if the specified instance was not created
+ * with the required extensions, this function returns `GLFW_FALSE` and
+ * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported
+ * to check whether Vulkan is at least minimally available and @ref
+ * glfwGetRequiredInstanceExtensions to check what instance extensions are
+ * required.
+ *
+ * @param[in] instance The instance that the physical device belongs to.
+ * @param[in] device The physical device that the queue family belongs to.
+ * @param[in] queuefamily The index of the queue family to query.
+ * @return `GLFW_TRUE` if the queue family supports presentation, or
+ * `GLFW_FALSE` otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark @macos This function currently always returns `GLFW_TRUE`, as the
+ * `VK_MVK_macos_surface` and `VK_EXT_metal_surface` extensions do not provide
+ * a `vkGetPhysicalDevice*PresentationSupport` type function.
+ *
+ * @thread_safety This function may be called from any thread. For
+ * synchronization details of Vulkan objects, see the Vulkan specification.
+ *
+ * @sa @ref vulkan_present
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily);
+
+/*! @brief Creates a Vulkan surface for the specified window.
+ *
+ * This function creates a Vulkan surface for the specified window.
+ *
+ * If the Vulkan loader or at least one minimally functional ICD were not found,
+ * this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref
+ * GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported to check whether
+ * Vulkan is at least minimally available.
+ *
+ * If the required window surface creation instance extensions are not
+ * available or if the specified instance was not created with these extensions
+ * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and
+ * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref
+ * glfwGetRequiredInstanceExtensions to check what instance extensions are
+ * required.
+ *
+ * The window surface cannot be shared with another API so the window must
+ * have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib)
+ * set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error
+ * and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`.
+ *
+ * The window surface must be destroyed before the specified Vulkan instance.
+ * It is the responsibility of the caller to destroy the window surface. GLFW
+ * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the
+ * surface.
+ *
+ * @param[in] instance The Vulkan instance to create the surface in.
+ * @param[in] window The window to create the surface for.
+ * @param[in] allocator The allocator to use, or `NULL` to use the default
+ * allocator.
+ * @param[out] surface Where to store the handle of the surface. This is set
+ * to `VK_NULL_HANDLE` if an error occurred.
+ * @return `VK_SUCCESS` if successful, or a Vulkan error code if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE
+ *
+ * @remark If an error occurs before the creation call is made, GLFW returns
+ * the Vulkan error code most appropriate for the error. Appropriate use of
+ * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should
+ * eliminate almost all occurrences of these errors.
+ *
+ * @remark @macos GLFW prefers the `VK_EXT_metal_surface` extension, with the
+ * `VK_MVK_macos_surface` extension as a fallback. The name of the selected
+ * extension, if any, is included in the array returned by @ref
+ * glfwGetRequiredInstanceExtensions.
+ *
+ * @remark @macos This function creates and sets a `CAMetalLayer` instance for
+ * the window content view, which is required for MoltenVK to function.
+ *
+ * @thread_safety This function may be called from any thread. For
+ * synchronization details of Vulkan objects, see the Vulkan specification.
+ *
+ * @sa @ref vulkan_surface
+ * @sa @ref glfwGetRequiredInstanceExtensions
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
+
+#endif /*VK_VERSION_1_0*/
+
+
+/*************************************************************************
+ * Global definition cleanup
+ *************************************************************************/
+
+/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */
+
+#ifdef GLFW_WINGDIAPI_DEFINED
+ #undef WINGDIAPI
+ #undef GLFW_WINGDIAPI_DEFINED
+#endif
+
+#ifdef GLFW_CALLBACK_DEFINED
+ #undef CALLBACK
+ #undef GLFW_CALLBACK_DEFINED
+#endif
+
+/* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally
+ * defined by some gl.h variants (OpenBSD) so define it after if needed.
+ */
+#ifndef GLAPIENTRY
+ #define GLAPIENTRY APIENTRY
+#endif
+
+/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _glfw3_h_ */
+
diff --git a/src_v2/libs/glfw_osx/include/GLFW/glfw3native.h b/src_v2/libs/glfw_osx/include/GLFW/glfw3native.h
new file mode 100644
index 0000000..fe74c73
--- /dev/null
+++ b/src_v2/libs/glfw_osx/include/GLFW/glfw3native.h
@@ -0,0 +1,594 @@
+/*************************************************************************
+ * GLFW 3.3 - www.glfw.org
+ * A library for OpenGL, window and input
+ *------------------------------------------------------------------------
+ * Copyright (c) 2002-2006 Marcus Geelnard
+ * Copyright (c) 2006-2018 Camilla Löwy
+ *
+ * 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.
+ *
+ *************************************************************************/
+
+#ifndef _glfw3_native_h_
+#define _glfw3_native_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************************************************************************
+ * Doxygen documentation
+ *************************************************************************/
+
+/*! @file glfw3native.h
+ * @brief The header of the native access functions.
+ *
+ * This is the header file of the native access functions. See @ref native for
+ * more information.
+ */
+/*! @defgroup native Native access
+ * @brief Functions related to accessing native handles.
+ *
+ * **By using the native access functions you assert that you know what you're
+ * doing and how to fix problems caused by using them. If you don't, you
+ * shouldn't be using them.**
+ *
+ * Before the inclusion of @ref glfw3native.h, you may define zero or more
+ * window system API macro and zero or more context creation API macros.
+ *
+ * The chosen backends must match those the library was compiled for. Failure
+ * to do this will cause a link-time error.
+ *
+ * The available window API macros are:
+ * * `GLFW_EXPOSE_NATIVE_WIN32`
+ * * `GLFW_EXPOSE_NATIVE_COCOA`
+ * * `GLFW_EXPOSE_NATIVE_X11`
+ * * `GLFW_EXPOSE_NATIVE_WAYLAND`
+ *
+ * The available context API macros are:
+ * * `GLFW_EXPOSE_NATIVE_WGL`
+ * * `GLFW_EXPOSE_NATIVE_NSGL`
+ * * `GLFW_EXPOSE_NATIVE_GLX`
+ * * `GLFW_EXPOSE_NATIVE_EGL`
+ * * `GLFW_EXPOSE_NATIVE_OSMESA`
+ *
+ * These macros select which of the native access functions that are declared
+ * and which platform-specific headers to include. It is then up your (by
+ * definition platform-specific) code to handle which of these should be
+ * defined.
+ */
+
+
+/*************************************************************************
+ * System headers and types
+ *************************************************************************/
+
+#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
+ // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
+ // example to allow applications to correctly declare a GL_KHR_debug callback)
+ // but windows.h assumes no one will define APIENTRY before it does
+ #if defined(GLFW_APIENTRY_DEFINED)
+ #undef APIENTRY
+ #undef GLFW_APIENTRY_DEFINED
+ #endif
+ #include
+#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
+ #if defined(__OBJC__)
+ #import
+ #else
+ #include
+ typedef void* id;
+ #endif
+#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
+ #include
+ #include
+#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
+ #include
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_WGL)
+ /* WGL is declared by windows.h */
+#endif
+#if defined(GLFW_EXPOSE_NATIVE_NSGL)
+ /* NSGL is declared by Cocoa.h */
+#endif
+#if defined(GLFW_EXPOSE_NATIVE_GLX)
+ #include
+#endif
+#if defined(GLFW_EXPOSE_NATIVE_EGL)
+ #include
+#endif
+#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
+ #include
+#endif
+
+
+/*************************************************************************
+ * Functions
+ *************************************************************************/
+
+#if defined(GLFW_EXPOSE_NATIVE_WIN32)
+/*! @brief Returns the adapter device name of the specified monitor.
+ *
+ * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
+ * of the specified monitor, or `NULL` if an [error](@ref error_handling)
+ * occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
+
+/*! @brief Returns the display device name of the specified monitor.
+ *
+ * @return The UTF-8 encoded display device name (for example
+ * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the `HWND` of the specified window.
+ *
+ * @return The `HWND` of the specified window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark The `HDC` associated with the window can be queried with the
+ * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc)
+ * function.
+ * @code
+ * HDC dc = GetDC(glfwGetWin32Window(window));
+ * @endcode
+ * This DC is private and does not need to be released.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_WGL)
+/*! @brief Returns the `HGLRC` of the specified window.
+ *
+ * @return The `HGLRC` of the specified window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @remark The `HDC` associated with the window can be queried with the
+ * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc)
+ * function.
+ * @code
+ * HDC dc = GetDC(glfwGetWin32Window(window));
+ * @endcode
+ * This DC is private and does not need to be released.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_COCOA)
+/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
+ *
+ * @return The `CGDirectDisplayID` of the specified monitor, or
+ * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the `NSWindow` of the specified window.
+ *
+ * @return The `NSWindow` of the specified window, or `nil` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_NSGL)
+/*! @brief Returns the `NSOpenGLContext` of the specified window.
+ *
+ * @return The `NSOpenGLContext` of the specified window, or `nil` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_X11)
+/*! @brief Returns the `Display` used by GLFW.
+ *
+ * @return The `Display` used by GLFW, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI Display* glfwGetX11Display(void);
+
+/*! @brief Returns the `RRCrtc` of the specified monitor.
+ *
+ * @return The `RRCrtc` of the specified monitor, or `None` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
+
+/*! @brief Returns the `RROutput` of the specified monitor.
+ *
+ * @return The `RROutput` of the specified monitor, or `None` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the `Window` of the specified window.
+ *
+ * @return The `Window` of the specified window, or `None` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
+
+/*! @brief Sets the current primary selection to the specified string.
+ *
+ * @param[in] string A UTF-8 encoded string.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The specified string is copied before this function
+ * returns.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref clipboard
+ * @sa glfwGetX11SelectionString
+ * @sa glfwSetClipboardString
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup native
+ */
+GLFWAPI void glfwSetX11SelectionString(const char* string);
+
+/*! @brief Returns the contents of the current primary selection as a string.
+ *
+ * If the selection is empty or if its contents cannot be converted, `NULL`
+ * is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
+ *
+ * @return The contents of the selection as a UTF-8 encoded string, or `NULL`
+ * if an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the next call to @ref
+ * glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
+ * library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref clipboard
+ * @sa glfwSetX11SelectionString
+ * @sa glfwGetClipboardString
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup native
+ */
+GLFWAPI const char* glfwGetX11SelectionString(void);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_GLX)
+/*! @brief Returns the `GLXContext` of the specified window.
+ *
+ * @return The `GLXContext` of the specified window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
+
+/*! @brief Returns the `GLXWindow` of the specified window.
+ *
+ * @return The `GLXWindow` of the specified window, or `None` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
+/*! @brief Returns the `struct wl_display*` used by GLFW.
+ *
+ * @return The `struct wl_display*` used by GLFW, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
+
+/*! @brief Returns the `struct wl_output*` of the specified monitor.
+ *
+ * @return The `struct wl_output*` of the specified monitor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the main `struct wl_surface*` of the specified window.
+ *
+ * @return The main `struct wl_surface*` of the specified window, or `NULL` if
+ * an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_EGL)
+/*! @brief Returns the `EGLDisplay` used by GLFW.
+ *
+ * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
+
+/*! @brief Returns the `EGLContext` of the specified window.
+ *
+ * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
+
+/*! @brief Returns the `EGLSurface` of the specified window.
+ *
+ * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
+/*! @brief Retrieves the color buffer associated with the specified window.
+ *
+ * @param[in] window The window whose color buffer to retrieve.
+ * @param[out] width Where to store the width of the color buffer, or `NULL`.
+ * @param[out] height Where to store the height of the color buffer, or `NULL`.
+ * @param[out] format Where to store the OSMesa pixel format of the color
+ * buffer, or `NULL`.
+ * @param[out] buffer Where to store the address of the color buffer, or
+ * `NULL`.
+ * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup native
+ */
+GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer);
+
+/*! @brief Retrieves the depth buffer associated with the specified window.
+ *
+ * @param[in] window The window whose depth buffer to retrieve.
+ * @param[out] width Where to store the width of the depth buffer, or `NULL`.
+ * @param[out] height Where to store the height of the depth buffer, or `NULL`.
+ * @param[out] bytesPerValue Where to store the number of bytes per depth
+ * buffer element, or `NULL`.
+ * @param[out] buffer Where to store the address of the depth buffer, or
+ * `NULL`.
+ * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup native
+ */
+GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer);
+
+/*! @brief Returns the `OSMesaContext` of the specified window.
+ *
+ * @return The `OSMesaContext` of the specified window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
+ * GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.3.
+ *
+ * @ingroup native
+ */
+GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _glfw3_native_h_ */
+
diff --git a/src_v2/libs/glfw_osx/lib-arm64/libglfw.3.dylib b/src_v2/libs/glfw_osx/lib-arm64/libglfw.3.dylib
new file mode 100755
index 0000000..5fbb9fb
Binary files /dev/null and b/src_v2/libs/glfw_osx/lib-arm64/libglfw.3.dylib differ
diff --git a/src_v2/libs/glfw_osx/lib-arm64/libglfw3.a b/src_v2/libs/glfw_osx/lib-arm64/libglfw3.a
new file mode 100644
index 0000000..ae5433c
Binary files /dev/null and b/src_v2/libs/glfw_osx/lib-arm64/libglfw3.a differ
diff --git a/src_v2/libs/glfw_osx/lib-universal/libglfw.3.dylib b/src_v2/libs/glfw_osx/lib-universal/libglfw.3.dylib
new file mode 100755
index 0000000..4d12877
Binary files /dev/null and b/src_v2/libs/glfw_osx/lib-universal/libglfw.3.dylib differ
diff --git a/src_v2/libs/glfw_osx/lib-universal/libglfw3.a b/src_v2/libs/glfw_osx/lib-universal/libglfw3.a
new file mode 100644
index 0000000..afc2148
Binary files /dev/null and b/src_v2/libs/glfw_osx/lib-universal/libglfw3.a differ
diff --git a/src_v2/libs/glfw_osx/lib-x86_64/libglfw.3.dylib b/src_v2/libs/glfw_osx/lib-x86_64/libglfw.3.dylib
new file mode 100755
index 0000000..392b8b4
Binary files /dev/null and b/src_v2/libs/glfw_osx/lib-x86_64/libglfw.3.dylib differ
diff --git a/src_v2/libs/glfw_osx/lib-x86_64/libglfw3.a b/src_v2/libs/glfw_osx/lib-x86_64/libglfw3.a
new file mode 100644
index 0000000..0c113d7
Binary files /dev/null and b/src_v2/libs/glfw_osx/lib-x86_64/libglfw3.a differ
diff --git a/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftconfig.h b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftconfig.h
new file mode 100644
index 0000000..f539e9a
--- /dev/null
+++ b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftconfig.h
@@ -0,0 +1,562 @@
+/***************************************************************************/
+/* */
+/* ftconfig.h */
+/* */
+/* ANSI-specific configuration file (specification only). */
+/* */
+/* Copyright 1996-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This header file contains a number of macro definitions that are used */
+ /* by the rest of the engine. Most of the macros here are automatically */
+ /* determined at compile time, and you should not need to change it to */
+ /* port FreeType, except to compile the library with a non-ANSI */
+ /* compiler. */
+ /* */
+ /* Note however that if some specific modifications are needed, we */
+ /* advise you to place a modified copy in your build directory. */
+ /* */
+ /* The build directory is usually `builds/', and contains */
+ /* system-specific files that are always included first when building */
+ /* the library. */
+ /* */
+ /* This ANSI version should stay in `include/config/'. */
+ /* */
+ /*************************************************************************/
+
+#ifndef FTCONFIG_H_
+#define FTCONFIG_H_
+
+#include
+#include FT_CONFIG_OPTIONS_H
+#include FT_CONFIG_STANDARD_LIBRARY_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* PLATFORM-SPECIFIC CONFIGURATION MACROS */
+ /* */
+ /* These macros can be toggled to suit a specific system. The current */
+ /* ones are defaults used to compile FreeType in an ANSI C environment */
+ /* (16bit compilers are also supported). Copy this file to your own */
+ /* `builds/' directory, and edit it to port the engine. */
+ /* */
+ /*************************************************************************/
+
+
+ /* There are systems (like the Texas Instruments 'C54x) where a `char' */
+ /* has 16 bits. ANSI C says that sizeof(char) is always 1. Since an */
+ /* `int' has 16 bits also for this system, sizeof(int) gives 1 which */
+ /* is probably unexpected. */
+ /* */
+ /* `CHAR_BIT' (defined in limits.h) gives the number of bits in a */
+ /* `char' type. */
+
+#ifndef FT_CHAR_BIT
+#define FT_CHAR_BIT CHAR_BIT
+#endif
+
+
+ /* The size of an `int' type. */
+#if FT_UINT_MAX == 0xFFFFUL
+#define FT_SIZEOF_INT ( 16 / FT_CHAR_BIT )
+#elif FT_UINT_MAX == 0xFFFFFFFFUL
+#define FT_SIZEOF_INT ( 32 / FT_CHAR_BIT )
+#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL
+#define FT_SIZEOF_INT ( 64 / FT_CHAR_BIT )
+#else
+#error "Unsupported size of `int' type!"
+#endif
+
+ /* The size of a `long' type. A five-byte `long' (as used e.g. on the */
+ /* DM642) is recognized but avoided. */
+#if FT_ULONG_MAX == 0xFFFFFFFFUL
+#define FT_SIZEOF_LONG ( 32 / FT_CHAR_BIT )
+#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL
+#define FT_SIZEOF_LONG ( 32 / FT_CHAR_BIT )
+#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL
+#define FT_SIZEOF_LONG ( 64 / FT_CHAR_BIT )
+#else
+#error "Unsupported size of `long' type!"
+#endif
+
+
+ /* FT_UNUSED is a macro used to indicate that a given parameter is not */
+ /* used -- this is only used to get rid of unpleasant compiler warnings */
+#ifndef FT_UNUSED
+#define FT_UNUSED( arg ) ( (arg) = (arg) )
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* AUTOMATIC CONFIGURATION MACROS */
+ /* */
+ /* These macros are computed from the ones defined above. Don't touch */
+ /* their definition, unless you know precisely what you are doing. No */
+ /* porter should need to mess with them. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* Mac support */
+ /* */
+ /* This is the only necessary change, so it is defined here instead */
+ /* providing a new configuration file. */
+ /* */
+#if defined( __APPLE__ ) || ( defined( __MWERKS__ ) && defined( macintosh ) )
+ /* no Carbon frameworks for 64bit 10.4.x */
+ /* AvailabilityMacros.h is available since Mac OS X 10.2, */
+ /* so guess the system version by maximum errno before inclusion */
+#include
+#ifdef ECANCELED /* defined since 10.2 */
+#include "AvailabilityMacros.h"
+#endif
+#if defined( __LP64__ ) && \
+ ( MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 )
+#undef FT_MACINTOSH
+#endif
+
+#elif defined( __SC__ ) || defined( __MRC__ )
+ /* Classic MacOS compilers */
+#include "ConditionalMacros.h"
+#if TARGET_OS_MAC
+#define FT_MACINTOSH 1
+#endif
+
+#endif
+
+
+ /* Fix compiler warning with sgi compiler */
+#if defined( __sgi ) && !defined( __GNUC__ )
+#if defined( _COMPILER_VERSION ) && ( _COMPILER_VERSION >= 730 )
+#pragma set woff 3505
+#endif
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* basic_types */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Int16 */
+ /* */
+ /* */
+ /* A typedef for a 16bit signed integer type. */
+ /* */
+ typedef signed short FT_Int16;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_UInt16 */
+ /* */
+ /* */
+ /* A typedef for a 16bit unsigned integer type. */
+ /* */
+ typedef unsigned short FT_UInt16;
+
+ /* */
+
+
+ /* this #if 0 ... #endif clause is for documentation purposes */
+#if 0
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Int32 */
+ /* */
+ /* */
+ /* A typedef for a 32bit signed integer type. The size depends on */
+ /* the configuration. */
+ /* */
+ typedef signed XXX FT_Int32;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_UInt32 */
+ /* */
+ /* A typedef for a 32bit unsigned integer type. The size depends on */
+ /* the configuration. */
+ /* */
+ typedef unsigned XXX FT_UInt32;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Int64 */
+ /* */
+ /* A typedef for a 64bit signed integer type. The size depends on */
+ /* the configuration. Only defined if there is real 64bit support; */
+ /* otherwise, it gets emulated with a structure (if necessary). */
+ /* */
+ typedef signed XXX FT_Int64;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_UInt64 */
+ /* */
+ /* A typedef for a 64bit unsigned integer type. The size depends on */
+ /* the configuration. Only defined if there is real 64bit support; */
+ /* otherwise, it gets emulated with a structure (if necessary). */
+ /* */
+ typedef unsigned XXX FT_UInt64;
+
+ /* */
+
+#endif
+
+#if FT_SIZEOF_INT == ( 32 / FT_CHAR_BIT )
+
+ typedef signed int FT_Int32;
+ typedef unsigned int FT_UInt32;
+
+#elif FT_SIZEOF_LONG == ( 32 / FT_CHAR_BIT )
+
+ typedef signed long FT_Int32;
+ typedef unsigned long FT_UInt32;
+
+#else
+#error "no 32bit type found -- please check your configuration files"
+#endif
+
+
+ /* look up an integer type that is at least 32 bits */
+#if FT_SIZEOF_INT >= ( 32 / FT_CHAR_BIT )
+
+ typedef int FT_Fast;
+ typedef unsigned int FT_UFast;
+
+#elif FT_SIZEOF_LONG >= ( 32 / FT_CHAR_BIT )
+
+ typedef long FT_Fast;
+ typedef unsigned long FT_UFast;
+
+#endif
+
+
+ /* determine whether we have a 64-bit int type for platforms without */
+ /* Autoconf */
+#if FT_SIZEOF_LONG == ( 64 / FT_CHAR_BIT )
+
+ /* FT_LONG64 must be defined if a 64-bit type is available */
+#define FT_LONG64
+#define FT_INT64 long
+#define FT_UINT64 unsigned long
+
+ /*************************************************************************/
+ /* */
+ /* A 64-bit data type may create compilation problems if you compile */
+ /* in strict ANSI mode. To avoid them, we disable other 64-bit data */
+ /* types if __STDC__ is defined. You can however ignore this rule */
+ /* by defining the FT_CONFIG_OPTION_FORCE_INT64 configuration macro. */
+ /* */
+#elif !defined( __STDC__ ) || defined( FT_CONFIG_OPTION_FORCE_INT64 )
+
+#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L
+
+#define FT_LONG64
+#define FT_INT64 long long int
+#define FT_UINT64 unsigned long long int
+
+#elif defined( _MSC_VER ) && _MSC_VER >= 900 /* Visual C++ (and Intel C++) */
+
+ /* this compiler provides the __int64 type */
+#define FT_LONG64
+#define FT_INT64 __int64
+#define FT_UINT64 unsigned __int64
+
+#elif defined( __BORLANDC__ ) /* Borland C++ */
+
+ /* XXXX: We should probably check the value of __BORLANDC__ in order */
+ /* to test the compiler version. */
+
+ /* this compiler provides the __int64 type */
+#define FT_LONG64
+#define FT_INT64 __int64
+#define FT_UINT64 unsigned __int64
+
+#elif defined( __WATCOMC__ ) /* Watcom C++ */
+
+ /* Watcom doesn't provide 64-bit data types */
+
+#elif defined( __MWERKS__ ) /* Metrowerks CodeWarrior */
+
+#define FT_LONG64
+#define FT_INT64 long long int
+#define FT_UINT64 unsigned long long int
+
+#elif defined( __GNUC__ )
+
+ /* GCC provides the `long long' type */
+#define FT_LONG64
+#define FT_INT64 long long int
+#define FT_UINT64 unsigned long long int
+
+#endif /* __STDC_VERSION__ >= 199901L */
+
+#endif /* FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) */
+
+#ifdef FT_LONG64
+ typedef FT_INT64 FT_Int64;
+ typedef FT_UINT64 FT_UInt64;
+#endif
+
+
+#ifdef _WIN64
+ /* only 64bit Windows uses the LLP64 data model, i.e., */
+ /* 32bit integers, 64bit pointers */
+#define FT_UINT_TO_POINTER( x ) (void*)(unsigned __int64)(x)
+#else
+#define FT_UINT_TO_POINTER( x ) (void*)(unsigned long)(x)
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* miscellaneous */
+ /* */
+ /*************************************************************************/
+
+
+#define FT_BEGIN_STMNT do {
+#define FT_END_STMNT } while ( 0 )
+#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT
+
+
+ /* typeof condition taken from gnulib's `intprops.h' header file */
+#if ( ( defined( __GNUC__ ) && __GNUC__ >= 2 ) || \
+ ( defined( __IBMC__ ) && __IBMC__ >= 1210 && \
+ defined( __IBM__TYPEOF__ ) ) || \
+ ( defined( __SUNPRO_C ) && __SUNPRO_C >= 0x5110 && !__STDC__ ) )
+#define FT_TYPEOF( type ) ( __typeof__ ( type ) )
+#else
+#define FT_TYPEOF( type ) /* empty */
+#endif
+
+
+ /* Use FT_LOCAL and FT_LOCAL_DEF to declare and define, respectively, */
+ /* a function that gets used only within the scope of a module. */
+ /* Normally, both the header and source code files for such a */
+ /* function are within a single module directory. */
+ /* */
+ /* Intra-module arrays should be tagged with FT_LOCAL_ARRAY and */
+ /* FT_LOCAL_ARRAY_DEF. */
+ /* */
+#ifdef FT_MAKE_OPTION_SINGLE_OBJECT
+
+#define FT_LOCAL( x ) static x
+#define FT_LOCAL_DEF( x ) static x
+
+#else
+
+#ifdef __cplusplus
+#define FT_LOCAL( x ) extern "C" x
+#define FT_LOCAL_DEF( x ) extern "C" x
+#else
+#define FT_LOCAL( x ) extern x
+#define FT_LOCAL_DEF( x ) x
+#endif
+
+#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */
+
+#define FT_LOCAL_ARRAY( x ) extern const x
+#define FT_LOCAL_ARRAY_DEF( x ) const x
+
+
+ /* Use FT_BASE and FT_BASE_DEF to declare and define, respectively, */
+ /* functions that are used in more than a single module. In the */
+ /* current setup this implies that the declaration is in a header */
+ /* file in the `include/freetype/internal' directory, and the */
+ /* function body is in a file in `src/base'. */
+ /* */
+#ifndef FT_BASE
+
+#ifdef __cplusplus
+#define FT_BASE( x ) extern "C" x
+#else
+#define FT_BASE( x ) extern x
+#endif
+
+#endif /* !FT_BASE */
+
+
+#ifndef FT_BASE_DEF
+
+#ifdef __cplusplus
+#define FT_BASE_DEF( x ) x
+#else
+#define FT_BASE_DEF( x ) x
+#endif
+
+#endif /* !FT_BASE_DEF */
+
+
+ /* When compiling FreeType as a DLL, some systems/compilers need a */
+ /* special attribute in front OR after the return type of function */
+ /* declarations. */
+ /* */
+ /* Two macros are used within the FreeType source code to define */
+ /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */
+ /* */
+ /* FT_EXPORT( return_type ) */
+ /* */
+ /* is used in a function declaration, as in */
+ /* */
+ /* FT_EXPORT( FT_Error ) */
+ /* FT_Init_FreeType( FT_Library* alibrary ); */
+ /* */
+ /* */
+ /* FT_EXPORT_DEF( return_type ) */
+ /* */
+ /* is used in a function definition, as in */
+ /* */
+ /* FT_EXPORT_DEF( FT_Error ) */
+ /* FT_Init_FreeType( FT_Library* alibrary ) */
+ /* { */
+ /* ... some code ... */
+ /* return FT_Err_Ok; */
+ /* } */
+ /* */
+ /* You can provide your own implementation of FT_EXPORT and */
+ /* FT_EXPORT_DEF here if you want. */
+ /* */
+ /* To export a variable, use FT_EXPORT_VAR. */
+ /* */
+#ifndef FT_EXPORT
+
+#ifdef __cplusplus
+#define FT_EXPORT( x ) extern "C" x
+#else
+#define FT_EXPORT( x ) extern x
+#endif
+
+#ifdef _MSC_VER
+#undef FT_EXPORT
+#ifdef _DLL
+#define FT_EXPORT( x ) __declspec( dllexport ) x
+#else
+#define FT_EXPORT( x ) __declspec( dllimport ) x
+#endif
+#endif
+
+#endif /* !FT_EXPORT */
+
+
+#ifndef FT_EXPORT_DEF
+
+#ifdef __cplusplus
+#define FT_EXPORT_DEF( x ) extern "C" x
+#else
+#define FT_EXPORT_DEF( x ) extern x
+#endif
+
+#endif /* !FT_EXPORT_DEF */
+
+
+#ifndef FT_EXPORT_VAR
+
+#ifdef __cplusplus
+#define FT_EXPORT_VAR( x ) extern "C" x
+#else
+#define FT_EXPORT_VAR( x ) extern x
+#endif
+
+#endif /* !FT_EXPORT_VAR */
+
+
+ /* The following macros are needed to compile the library with a */
+ /* C++ compiler and with 16bit compilers. */
+ /* */
+
+ /* This is special. Within C++, you must specify `extern "C"' for */
+ /* functions which are used via function pointers, and you also */
+ /* must do that for structures which contain function pointers to */
+ /* assure C linkage -- it's not possible to have (local) anonymous */
+ /* functions which are accessed by (global) function pointers. */
+ /* */
+ /* */
+ /* FT_CALLBACK_DEF is used to _define_ a callback function, */
+ /* located in the same source code file as the structure that uses */
+ /* it. */
+ /* */
+ /* FT_BASE_CALLBACK and FT_BASE_CALLBACK_DEF are used to declare */
+ /* and define a callback function, respectively, in a similar way */
+ /* as FT_BASE and FT_BASE_DEF work. */
+ /* */
+ /* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */
+ /* contains pointers to callback functions. */
+ /* */
+ /* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable */
+ /* that contains pointers to callback functions. */
+ /* */
+ /* */
+ /* Some 16bit compilers have to redefine these macros to insert */
+ /* the infamous `_cdecl' or `__fastcall' declarations. */
+ /* */
+#ifndef FT_CALLBACK_DEF
+#ifdef __cplusplus
+#define FT_CALLBACK_DEF( x ) extern "C" x
+#else
+#define FT_CALLBACK_DEF( x ) static x
+#endif
+#endif /* FT_CALLBACK_DEF */
+
+#ifndef FT_BASE_CALLBACK
+#ifdef __cplusplus
+#define FT_BASE_CALLBACK( x ) extern "C" x
+#define FT_BASE_CALLBACK_DEF( x ) extern "C" x
+#else
+#define FT_BASE_CALLBACK( x ) extern x
+#define FT_BASE_CALLBACK_DEF( x ) x
+#endif
+#endif /* FT_BASE_CALLBACK */
+
+#ifndef FT_CALLBACK_TABLE
+#ifdef __cplusplus
+#define FT_CALLBACK_TABLE extern "C"
+#define FT_CALLBACK_TABLE_DEF extern "C"
+#else
+#define FT_CALLBACK_TABLE extern
+#define FT_CALLBACK_TABLE_DEF /* nothing */
+#endif
+#endif /* FT_CALLBACK_TABLE */
+
+
+FT_END_HEADER
+
+
+#endif /* FTCONFIG_H_ */
+
+
+/* END */
diff --git a/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftheader.h b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftheader.h
new file mode 100644
index 0000000..702f77c
--- /dev/null
+++ b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftheader.h
@@ -0,0 +1,804 @@
+/***************************************************************************/
+/* */
+/* ftheader.h */
+/* */
+/* Build macros of the FreeType 2 library. */
+/* */
+/* Copyright 1996-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#ifndef FTHEADER_H_
+#define FTHEADER_H_
+
+
+ /*@***********************************************************************/
+ /* */
+ /* */
+ /* FT_BEGIN_HEADER */
+ /* */
+ /* */
+ /* This macro is used in association with @FT_END_HEADER in header */
+ /* files to ensure that the declarations within are properly */
+ /* encapsulated in an `extern "C" { .. }' block when included from a */
+ /* C++ compiler. */
+ /* */
+#ifdef __cplusplus
+#define FT_BEGIN_HEADER extern "C" {
+#else
+#define FT_BEGIN_HEADER /* nothing */
+#endif
+
+
+ /*@***********************************************************************/
+ /* */
+ /* */
+ /* FT_END_HEADER */
+ /* */
+ /* */
+ /* This macro is used in association with @FT_BEGIN_HEADER in header */
+ /* files to ensure that the declarations within are properly */
+ /* encapsulated in an `extern "C" { .. }' block when included from a */
+ /* C++ compiler. */
+ /* */
+#ifdef __cplusplus
+#define FT_END_HEADER }
+#else
+#define FT_END_HEADER /* nothing */
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* Aliases for the FreeType 2 public and configuration files. */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* header_file_macros */
+ /* */
+ /* */
+ /* Header File Macros */
+ /* */
+ /* */
+ /* Macro definitions used to #include specific header files. */
+ /* */
+ /* */
+ /* The following macros are defined to the name of specific */
+ /* FreeType~2 header files. They can be used directly in #include */
+ /* statements as in: */
+ /* */
+ /* { */
+ /* #include FT_FREETYPE_H */
+ /* #include FT_MULTIPLE_MASTERS_H */
+ /* #include FT_GLYPH_H */
+ /* } */
+ /* */
+ /* There are several reasons why we are now using macros to name */
+ /* public header files. The first one is that such macros are not */
+ /* limited to the infamous 8.3~naming rule required by DOS (and */
+ /* `FT_MULTIPLE_MASTERS_H' is a lot more meaningful than `ftmm.h'). */
+ /* */
+ /* The second reason is that it allows for more flexibility in the */
+ /* way FreeType~2 is installed on a given system. */
+ /* */
+ /*************************************************************************/
+
+
+ /* configuration files */
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_CONFIG_CONFIG_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * FreeType~2 configuration data.
+ *
+ */
+#ifndef FT_CONFIG_CONFIG_H
+#define FT_CONFIG_CONFIG_H
+#endif
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_CONFIG_STANDARD_LIBRARY_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * FreeType~2 interface to the standard C library functions.
+ *
+ */
+#ifndef FT_CONFIG_STANDARD_LIBRARY_H
+#define FT_CONFIG_STANDARD_LIBRARY_H
+#endif
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_CONFIG_OPTIONS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * FreeType~2 project-specific configuration options.
+ *
+ */
+#ifndef FT_CONFIG_OPTIONS_H
+#define FT_CONFIG_OPTIONS_H
+#endif
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_CONFIG_MODULES_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * list of FreeType~2 modules that are statically linked to new library
+ * instances in @FT_Init_FreeType.
+ *
+ */
+#ifndef FT_CONFIG_MODULES_H
+#define FT_CONFIG_MODULES_H
+#endif
+
+ /* */
+
+ /* public headers */
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_FREETYPE_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * base FreeType~2 API.
+ *
+ */
+#define FT_FREETYPE_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_ERRORS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * list of FreeType~2 error codes (and messages).
+ *
+ * It is included by @FT_FREETYPE_H.
+ *
+ */
+#define FT_ERRORS_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_MODULE_ERRORS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * list of FreeType~2 module error offsets (and messages).
+ *
+ */
+#define FT_MODULE_ERRORS_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_SYSTEM_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 interface to low-level operations (i.e., memory management
+ * and stream i/o).
+ *
+ * It is included by @FT_FREETYPE_H.
+ *
+ */
+#define FT_SYSTEM_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_IMAGE_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing type
+ * definitions related to glyph images (i.e., bitmaps, outlines,
+ * scan-converter parameters).
+ *
+ * It is included by @FT_FREETYPE_H.
+ *
+ */
+#define FT_IMAGE_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_TYPES_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * basic data types defined by FreeType~2.
+ *
+ * It is included by @FT_FREETYPE_H.
+ *
+ */
+#define FT_TYPES_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_LIST_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * list management API of FreeType~2.
+ *
+ * (Most applications will never need to include this file.)
+ *
+ */
+#define FT_LIST_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_OUTLINE_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * scalable outline management API of FreeType~2.
+ *
+ */
+#define FT_OUTLINE_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_SIZES_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * API which manages multiple @FT_Size objects per face.
+ *
+ */
+#define FT_SIZES_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_MODULE_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * module management API of FreeType~2.
+ *
+ */
+#define FT_MODULE_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_RENDER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * renderer module management API of FreeType~2.
+ *
+ */
+#define FT_RENDER_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_DRIVER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * structures and macros related to the driver modules.
+ *
+ */
+#define FT_DRIVER_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_AUTOHINTER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * structures and macros related to the auto-hinting module.
+ *
+ * Deprecated since version 2.9; use @FT_DRIVER_H instead.
+ *
+ */
+#define FT_AUTOHINTER_H FT_DRIVER_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_CFF_DRIVER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * structures and macros related to the CFF driver module.
+ *
+ * Deprecated since version 2.9; use @FT_DRIVER_H instead.
+ *
+ */
+#define FT_CFF_DRIVER_H FT_DRIVER_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_TRUETYPE_DRIVER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * structures and macros related to the TrueType driver module.
+ *
+ * Deprecated since version 2.9; use @FT_DRIVER_H instead.
+ *
+ */
+#define FT_TRUETYPE_DRIVER_H FT_DRIVER_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_PCF_DRIVER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing
+ * structures and macros related to the PCF driver module.
+ *
+ * Deprecated since version 2.9; use @FT_DRIVER_H instead.
+ *
+ */
+#define FT_PCF_DRIVER_H FT_DRIVER_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_TYPE1_TABLES_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * types and API specific to the Type~1 format.
+ *
+ */
+#define FT_TYPE1_TABLES_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_TRUETYPE_IDS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * enumeration values which identify name strings, languages, encodings,
+ * etc. This file really contains a _large_ set of constant macro
+ * definitions, taken from the TrueType and OpenType specifications.
+ *
+ */
+#define FT_TRUETYPE_IDS_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_TRUETYPE_TABLES_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * types and API specific to the TrueType (as well as OpenType) format.
+ *
+ */
+#define FT_TRUETYPE_TABLES_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_TRUETYPE_TAGS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * definitions of TrueType four-byte `tags' which identify blocks in
+ * SFNT-based font formats (i.e., TrueType and OpenType).
+ *
+ */
+#define FT_TRUETYPE_TAGS_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_BDF_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * definitions of an API which accesses BDF-specific strings from a
+ * face.
+ *
+ */
+#define FT_BDF_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_CID_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * definitions of an API which access CID font information from a
+ * face.
+ *
+ */
+#define FT_CID_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_GZIP_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * definitions of an API which supports gzip-compressed files.
+ *
+ */
+#define FT_GZIP_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_LZW_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * definitions of an API which supports LZW-compressed files.
+ *
+ */
+#define FT_LZW_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_BZIP2_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * definitions of an API which supports bzip2-compressed files.
+ *
+ */
+#define FT_BZIP2_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_WINFONTS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * definitions of an API which supports Windows FNT files.
+ *
+ */
+#define FT_WINFONTS_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_GLYPH_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * API of the optional glyph management component.
+ *
+ */
+#define FT_GLYPH_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_BITMAP_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * API of the optional bitmap conversion component.
+ *
+ */
+#define FT_BITMAP_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_BBOX_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * API of the optional exact bounding box computation routines.
+ *
+ */
+#define FT_BBOX_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_CACHE_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * API of the optional FreeType~2 cache sub-system.
+ *
+ */
+#define FT_CACHE_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_MAC_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * Macintosh-specific FreeType~2 API. The latter is used to access
+ * fonts embedded in resource forks.
+ *
+ * This header file must be explicitly included by client applications
+ * compiled on the Mac (note that the base API still works though).
+ *
+ */
+#define FT_MAC_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_MULTIPLE_MASTERS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * optional multiple-masters management API of FreeType~2.
+ *
+ */
+#define FT_MULTIPLE_MASTERS_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_SFNT_NAMES_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * optional FreeType~2 API which accesses embedded `name' strings in
+ * SFNT-based font formats (i.e., TrueType and OpenType).
+ *
+ */
+#define FT_SFNT_NAMES_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_OPENTYPE_VALIDATE_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * optional FreeType~2 API which validates OpenType tables (BASE, GDEF,
+ * GPOS, GSUB, JSTF).
+ *
+ */
+#define FT_OPENTYPE_VALIDATE_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_GX_VALIDATE_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * optional FreeType~2 API which validates TrueTypeGX/AAT tables (feat,
+ * mort, morx, bsln, just, kern, opbd, trak, prop).
+ *
+ */
+#define FT_GX_VALIDATE_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_PFR_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which accesses PFR-specific data.
+ *
+ */
+#define FT_PFR_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_STROKER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which provides functions to stroke outline paths.
+ */
+#define FT_STROKER_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_SYNTHESIS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which performs artificial obliquing and emboldening.
+ */
+#define FT_SYNTHESIS_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_FONT_FORMATS_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which provides functions specific to font formats.
+ */
+#define FT_FONT_FORMATS_H
+
+ /* deprecated */
+#define FT_XFREE86_H FT_FONT_FORMATS_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_TRIGONOMETRY_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which performs trigonometric computations (e.g.,
+ * cosines and arc tangents).
+ */
+#define FT_TRIGONOMETRY_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_LCD_FILTER_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which performs color filtering for subpixel rendering.
+ */
+#define FT_LCD_FILTER_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_INCREMENTAL_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which performs incremental glyph loading.
+ */
+#define FT_INCREMENTAL_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_GASP_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which returns entries from the TrueType GASP table.
+ */
+#define FT_GASP_H
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_ADVANCES_H
+ *
+ * @description:
+ * A macro used in #include statements to name the file containing the
+ * FreeType~2 API which returns individual and ranged glyph advances.
+ */
+#define FT_ADVANCES_H
+
+
+ /* */
+
+ /* These header files don't need to be included by the user. */
+#define FT_ERROR_DEFINITIONS_H
+#define FT_PARAMETER_TAGS_H
+
+ /* Deprecated macros. */
+#define FT_UNPATENTED_HINTING_H
+#define FT_TRUETYPE_UNPATENTED_H
+
+ /* FT_CACHE_H is the only header file needed for the cache subsystem. */
+#define FT_CACHE_IMAGE_H FT_CACHE_H
+#define FT_CACHE_SMALL_BITMAPS_H FT_CACHE_H
+#define FT_CACHE_CHARMAP_H FT_CACHE_H
+
+ /* The internals of the cache sub-system are no longer exposed. We */
+ /* default to FT_CACHE_H at the moment just in case, but we know of */
+ /* no rogue client that uses them. */
+ /* */
+#define FT_CACHE_MANAGER_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_MRU_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_MANAGER_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_CACHE_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_GLYPH_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_IMAGE_H FT_CACHE_H
+#define FT_CACHE_INTERNAL_SBITS_H FT_CACHE_H
+
+
+ /*
+ * Include internal headers definitions from
+ * only when building the library.
+ */
+#ifdef FT2_BUILD_LIBRARY
+#define FT_INTERNAL_INTERNAL_H
+#include FT_INTERNAL_INTERNAL_H
+#endif /* FT2_BUILD_LIBRARY */
+
+
+#endif /* FTHEADER_H_ */
+
+
+/* END */
diff --git a/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftmodule.h b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftmodule.h
new file mode 100644
index 0000000..76d271a
--- /dev/null
+++ b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftmodule.h
@@ -0,0 +1,32 @@
+/*
+ * This file registers the FreeType modules compiled into the library.
+ *
+ * If you use GNU make, this file IS NOT USED! Instead, it is created in
+ * the objects directory (normally `/objs/') based on information
+ * from `/modules.cfg'.
+ *
+ * Please read `docs/INSTALL.ANY' and `docs/CUSTOMIZE' how to compile
+ * FreeType without GNU make.
+ *
+ */
+
+FT_USE_MODULE( FT_Module_Class, autofit_module_class )
+FT_USE_MODULE( FT_Driver_ClassRec, tt_driver_class )
+FT_USE_MODULE( FT_Driver_ClassRec, t1_driver_class )
+FT_USE_MODULE( FT_Driver_ClassRec, cff_driver_class )
+FT_USE_MODULE( FT_Driver_ClassRec, t1cid_driver_class )
+FT_USE_MODULE( FT_Driver_ClassRec, pfr_driver_class )
+FT_USE_MODULE( FT_Driver_ClassRec, t42_driver_class )
+FT_USE_MODULE( FT_Driver_ClassRec, winfnt_driver_class )
+FT_USE_MODULE( FT_Driver_ClassRec, pcf_driver_class )
+FT_USE_MODULE( FT_Module_Class, psaux_module_class )
+FT_USE_MODULE( FT_Module_Class, psnames_module_class )
+FT_USE_MODULE( FT_Module_Class, pshinter_module_class )
+FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class )
+FT_USE_MODULE( FT_Module_Class, sfnt_module_class )
+FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class )
+FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class )
+FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class )
+FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class )
+
+/* EOF */
diff --git a/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftoption.h b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftoption.h
new file mode 100644
index 0000000..a5cb7ff
--- /dev/null
+++ b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftoption.h
@@ -0,0 +1,973 @@
+/***************************************************************************/
+/* */
+/* ftoption.h */
+/* */
+/* User-selectable configuration macros (specification only). */
+/* */
+/* Copyright 1996-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef FTOPTION_H_
+#define FTOPTION_H_
+
+
+#include
+
+
+FT_BEGIN_HEADER
+
+ /*************************************************************************/
+ /* */
+ /* USER-SELECTABLE CONFIGURATION MACROS */
+ /* */
+ /* This file contains the default configuration macro definitions for */
+ /* a standard build of the FreeType library. There are three ways to */
+ /* use this file to build project-specific versions of the library: */
+ /* */
+ /* - You can modify this file by hand, but this is not recommended in */
+ /* cases where you would like to build several versions of the */
+ /* library from a single source directory. */
+ /* */
+ /* - You can put a copy of this file in your build directory, more */
+ /* precisely in `$BUILD/freetype/config/ftoption.h', where `$BUILD' */
+ /* is the name of a directory that is included _before_ the FreeType */
+ /* include path during compilation. */
+ /* */
+ /* The default FreeType Makefiles and Jamfiles use the build */
+ /* directory `builds/' by default, but you can easily change */
+ /* that for your own projects. */
+ /* */
+ /* - Copy the file to `$BUILD/ft2build.h' and modify it */
+ /* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */
+ /* locate this file during the build. For example, */
+ /* */
+ /* #define FT_CONFIG_OPTIONS_H */
+ /* #include */
+ /* */
+ /* will use `$BUILD/myftoptions.h' instead of this file for macro */
+ /* definitions. */
+ /* */
+ /* Note also that you can similarly pre-define the macro */
+ /* FT_CONFIG_MODULES_H used to locate the file listing of the modules */
+ /* that are statically linked to the library at compile time. By */
+ /* default, this file is . */
+ /* */
+ /* We highly recommend using the third method whenever possible. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*#***********************************************************************/
+ /* */
+ /* If you enable this configuration option, FreeType recognizes an */
+ /* environment variable called `FREETYPE_PROPERTIES', which can be used */
+ /* to control the various font drivers and modules. The controllable */
+ /* properties are listed in the section @properties. */
+ /* */
+ /* `FREETYPE_PROPERTIES' has the following syntax form (broken here into */
+ /* multiple lines for better readability). */
+ /* */
+ /* { */
+ /* */
+ /* ':' */
+ /* '=' */
+ /* */
+ /* ':' */
+ /* '=' */
+ /* ... */
+ /* } */
+ /* */
+ /* Example: */
+ /* */
+ /* FREETYPE_PROPERTIES=truetype:interpreter-version=35 \ */
+ /* cff:no-stem-darkening=1 \ */
+ /* autofitter:warping=1 */
+ /* */
+#define FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+
+
+ /*************************************************************************/
+ /* */
+ /* Uncomment the line below if you want to activate LCD rendering */
+ /* technology similar to ClearType in this build of the library. This */
+ /* technology triples the resolution in the direction color subpixels. */
+ /* To mitigate color fringes inherent to this technology, you also need */
+ /* to explicitly set up LCD filtering. */
+ /* */
+ /* Note that this feature is covered by several Microsoft patents */
+ /* and should not be activated in any default build of the library. */
+ /* When this macro is not defined, FreeType offers alternative LCD */
+ /* rendering technology that produces excellent output without LCD */
+ /* filtering. */
+ /* */
+/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
+
+ /*************************************************************************/
+ /* */
+ /* Many compilers provide a non-ANSI 64-bit data type that can be used */
+ /* by FreeType to speed up some computations. However, this will create */
+ /* some problems when compiling the library in strict ANSI mode. */
+ /* */
+ /* For this reason, the use of 64-bit integers is normally disabled when */
+ /* the __STDC__ macro is defined. You can however disable this by */
+ /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */
+ /* */
+ /* For most compilers, this will only create compilation warnings when */
+ /* building the library. */
+ /* */
+ /* ObNote: The compiler-specific 64-bit integers are detected in the */
+ /* file `ftconfig.h' either statically or through the */
+ /* `configure' script on supported platforms. */
+ /* */
+#undef FT_CONFIG_OPTION_FORCE_INT64
+
+
+ /*************************************************************************/
+ /* */
+ /* If this macro is defined, do not try to use an assembler version of */
+ /* performance-critical functions (e.g. FT_MulFix). You should only do */
+ /* that to verify that the assembler function works properly, or to */
+ /* execute benchmark tests of the various implementations. */
+/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */
+
+
+ /*************************************************************************/
+ /* */
+ /* If this macro is defined, try to use an inlined assembler version of */
+ /* the `FT_MulFix' function, which is a `hotspot' when loading and */
+ /* hinting glyphs, and which should be executed as fast as possible. */
+ /* */
+ /* Note that if your compiler or CPU is not supported, this will default */
+ /* to the standard and portable implementation found in `ftcalc.c'. */
+ /* */
+#define FT_CONFIG_OPTION_INLINE_MULFIX
+
+
+ /*************************************************************************/
+ /* */
+ /* LZW-compressed file support. */
+ /* */
+ /* FreeType now handles font files that have been compressed with the */
+ /* `compress' program. This is mostly used to parse many of the PCF */
+ /* files that come with various X11 distributions. The implementation */
+ /* uses NetBSD's `zopen' to partially uncompress the file on the fly */
+ /* (see src/lzw/ftgzip.c). */
+ /* */
+ /* Define this macro if you want to enable this `feature'. */
+ /* */
+#define FT_CONFIG_OPTION_USE_LZW
+
+
+ /*************************************************************************/
+ /* */
+ /* Gzip-compressed file support. */
+ /* */
+ /* FreeType now handles font files that have been compressed with the */
+ /* `gzip' program. This is mostly used to parse many of the PCF files */
+ /* that come with XFree86. The implementation uses `zlib' to */
+ /* partially uncompress the file on the fly (see src/gzip/ftgzip.c). */
+ /* */
+ /* Define this macro if you want to enable this `feature'. See also */
+ /* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */
+ /* */
+#define FT_CONFIG_OPTION_USE_ZLIB
+
+
+ /*************************************************************************/
+ /* */
+ /* ZLib library selection */
+ /* */
+ /* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */
+ /* It allows FreeType's `ftgzip' component to link to the system's */
+ /* installation of the ZLib library. This is useful on systems like */
+ /* Unix or VMS where it generally is already available. */
+ /* */
+ /* If you let it undefined, the component will use its own copy */
+ /* of the zlib sources instead. These have been modified to be */
+ /* included directly within the component and *not* export external */
+ /* function names. This allows you to link any program with FreeType */
+ /* _and_ ZLib without linking conflicts. */
+ /* */
+ /* Do not #undef this macro here since the build system might define */
+ /* it for certain configurations only. */
+ /* */
+ /* If you use a build system like cmake or the `configure' script, */
+ /* options set by those programs have precendence, overwriting the */
+ /* value here with the configured one. */
+ /* */
+/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */
+
+
+ /*************************************************************************/
+ /* */
+ /* Bzip2-compressed file support. */
+ /* */
+ /* FreeType now handles font files that have been compressed with the */
+ /* `bzip2' program. This is mostly used to parse many of the PCF */
+ /* files that come with XFree86. The implementation uses `libbz2' to */
+ /* partially uncompress the file on the fly (see src/bzip2/ftbzip2.c). */
+ /* Contrary to gzip, bzip2 currently is not included and need to use */
+ /* the system available bzip2 implementation. */
+ /* */
+ /* Define this macro if you want to enable this `feature'. */
+ /* */
+ /* If you use a build system like cmake or the `configure' script, */
+ /* options set by those programs have precendence, overwriting the */
+ /* value here with the configured one. */
+ /* */
+/* #define FT_CONFIG_OPTION_USE_BZIP2 */
+
+
+ /*************************************************************************/
+ /* */
+ /* Define to disable the use of file stream functions and types, FILE, */
+ /* fopen() etc. Enables the use of smaller system libraries on embedded */
+ /* systems that have multiple system libraries, some with or without */
+ /* file stream support, in the cases where file stream support is not */
+ /* necessary such as memory loading of font files. */
+ /* */
+/* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */
+
+
+ /*************************************************************************/
+ /* */
+ /* PNG bitmap support. */
+ /* */
+ /* FreeType now handles loading color bitmap glyphs in the PNG format. */
+ /* This requires help from the external libpng library. Uncompressed */
+ /* color bitmaps do not need any external libraries and will be */
+ /* supported regardless of this configuration. */
+ /* */
+ /* Define this macro if you want to enable this `feature'. */
+ /* */
+ /* If you use a build system like cmake or the `configure' script, */
+ /* options set by those programs have precendence, overwriting the */
+ /* value here with the configured one. */
+ /* */
+/* #define FT_CONFIG_OPTION_USE_PNG */
+
+
+ /*************************************************************************/
+ /* */
+ /* HarfBuzz support. */
+ /* */
+ /* FreeType uses the HarfBuzz library to improve auto-hinting of */
+ /* OpenType fonts. If available, many glyphs not directly addressable */
+ /* by a font's character map will be hinted also. */
+ /* */
+ /* Define this macro if you want to enable this `feature'. */
+ /* */
+ /* If you use a build system like cmake or the `configure' script, */
+ /* options set by those programs have precendence, overwriting the */
+ /* value here with the configured one. */
+ /* */
+/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */
+
+
+ /*************************************************************************/
+ /* */
+ /* Glyph Postscript Names handling */
+ /* */
+ /* By default, FreeType 2 is compiled with the `psnames' module. This */
+ /* module is in charge of converting a glyph name string into a */
+ /* Unicode value, or return a Macintosh standard glyph name for the */
+ /* use with the TrueType `post' table. */
+ /* */
+ /* Undefine this macro if you do not want `psnames' compiled in your */
+ /* build of FreeType. This has the following effects: */
+ /* */
+ /* - The TrueType driver will provide its own set of glyph names, */
+ /* if you build it to support postscript names in the TrueType */
+ /* `post' table, but will not synthesize a missing Unicode charmap. */
+ /* */
+ /* - The Type 1 driver will not be able to synthesize a Unicode */
+ /* charmap out of the glyphs found in the fonts. */
+ /* */
+ /* You would normally undefine this configuration macro when building */
+ /* a version of FreeType that doesn't contain a Type 1 or CFF driver. */
+ /* */
+#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+
+ /*************************************************************************/
+ /* */
+ /* Postscript Names to Unicode Values support */
+ /* */
+ /* By default, FreeType 2 is built with the `PSNames' module compiled */
+ /* in. Among other things, the module is used to convert a glyph name */
+ /* into a Unicode value. This is especially useful in order to */
+ /* synthesize on the fly a Unicode charmap from the CFF/Type 1 driver */
+ /* through a big table named the `Adobe Glyph List' (AGL). */
+ /* */
+ /* Undefine this macro if you do not want the Adobe Glyph List */
+ /* compiled in your `PSNames' module. The Type 1 driver will not be */
+ /* able to synthesize a Unicode charmap out of the glyphs found in the */
+ /* fonts. */
+ /* */
+#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST
+
+
+ /*************************************************************************/
+ /* */
+ /* Support for Mac fonts */
+ /* */
+ /* Define this macro if you want support for outline fonts in Mac */
+ /* format (mac dfont, mac resource, macbinary containing a mac */
+ /* resource) on non-Mac platforms. */
+ /* */
+ /* Note that the `FOND' resource isn't checked. */
+ /* */
+#define FT_CONFIG_OPTION_MAC_FONTS
+
+
+ /*************************************************************************/
+ /* */
+ /* Guessing methods to access embedded resource forks */
+ /* */
+ /* Enable extra Mac fonts support on non-Mac platforms (e.g. */
+ /* GNU/Linux). */
+ /* */
+ /* Resource forks which include fonts data are stored sometimes in */
+ /* locations which users or developers don't expected. In some cases, */
+ /* resource forks start with some offset from the head of a file. In */
+ /* other cases, the actual resource fork is stored in file different */
+ /* from what the user specifies. If this option is activated, */
+ /* FreeType tries to guess whether such offsets or different file */
+ /* names must be used. */
+ /* */
+ /* Note that normal, direct access of resource forks is controlled via */
+ /* the FT_CONFIG_OPTION_MAC_FONTS option. */
+ /* */
+#ifdef FT_CONFIG_OPTION_MAC_FONTS
+#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* Allow the use of FT_Incremental_Interface to load typefaces that */
+ /* contain no glyph data, but supply it via a callback function. */
+ /* This is required by clients supporting document formats which */
+ /* supply font data incrementally as the document is parsed, such */
+ /* as the Ghostscript interpreter for the PostScript language. */
+ /* */
+#define FT_CONFIG_OPTION_INCREMENTAL
+
+
+ /*************************************************************************/
+ /* */
+ /* The size in bytes of the render pool used by the scan-line converter */
+ /* to do all of its work. */
+ /* */
+#define FT_RENDER_POOL_SIZE 16384L
+
+
+ /*************************************************************************/
+ /* */
+ /* FT_MAX_MODULES */
+ /* */
+ /* The maximum number of modules that can be registered in a single */
+ /* FreeType library object. 32 is the default. */
+ /* */
+#define FT_MAX_MODULES 32
+
+
+ /*************************************************************************/
+ /* */
+ /* Debug level */
+ /* */
+ /* FreeType can be compiled in debug or trace mode. In debug mode, */
+ /* errors are reported through the `ftdebug' component. In trace */
+ /* mode, additional messages are sent to the standard output during */
+ /* execution. */
+ /* */
+ /* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */
+ /* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */
+ /* */
+ /* Don't define any of these macros to compile in `release' mode! */
+ /* */
+ /* Do not #undef these macros here since the build system might define */
+ /* them for certain configurations only. */
+ /* */
+/* #define FT_DEBUG_LEVEL_ERROR */
+/* #define FT_DEBUG_LEVEL_TRACE */
+
+
+ /*************************************************************************/
+ /* */
+ /* Autofitter debugging */
+ /* */
+ /* If FT_DEBUG_AUTOFIT is defined, FreeType provides some means to */
+ /* control the autofitter behaviour for debugging purposes with global */
+ /* boolean variables (consequently, you should *never* enable this */
+ /* while compiling in `release' mode): */
+ /* */
+ /* _af_debug_disable_horz_hints */
+ /* _af_debug_disable_vert_hints */
+ /* _af_debug_disable_blue_hints */
+ /* */
+ /* Additionally, the following functions provide dumps of various */
+ /* internal autofit structures to stdout (using `printf'): */
+ /* */
+ /* af_glyph_hints_dump_points */
+ /* af_glyph_hints_dump_segments */
+ /* af_glyph_hints_dump_edges */
+ /* af_glyph_hints_get_num_segments */
+ /* af_glyph_hints_get_segment_offset */
+ /* */
+ /* As an argument, they use another global variable: */
+ /* */
+ /* _af_debug_hints */
+ /* */
+ /* Please have a look at the `ftgrid' demo program to see how those */
+ /* variables and macros should be used. */
+ /* */
+ /* Do not #undef these macros here since the build system might define */
+ /* them for certain configurations only. */
+ /* */
+/* #define FT_DEBUG_AUTOFIT */
+
+
+ /*************************************************************************/
+ /* */
+ /* Memory Debugging */
+ /* */
+ /* FreeType now comes with an integrated memory debugger that is */
+ /* capable of detecting simple errors like memory leaks or double */
+ /* deletes. To compile it within your build of the library, you */
+ /* should define FT_DEBUG_MEMORY here. */
+ /* */
+ /* Note that the memory debugger is only activated at runtime when */
+ /* when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */
+ /* */
+ /* Do not #undef this macro here since the build system might define */
+ /* it for certain configurations only. */
+ /* */
+/* #define FT_DEBUG_MEMORY */
+
+
+ /*************************************************************************/
+ /* */
+ /* Module errors */
+ /* */
+ /* If this macro is set (which is _not_ the default), the higher byte */
+ /* of an error code gives the module in which the error has occurred, */
+ /* while the lower byte is the real error code. */
+ /* */
+ /* Setting this macro makes sense for debugging purposes only, since */
+ /* it would break source compatibility of certain programs that use */
+ /* FreeType 2. */
+ /* */
+ /* More details can be found in the files ftmoderr.h and fterrors.h. */
+ /* */
+#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS
+
+
+ /*************************************************************************/
+ /* */
+ /* Position Independent Code */
+ /* */
+ /* If this macro is set (which is _not_ the default), FreeType2 will */
+ /* avoid creating constants that require address fixups. Instead the */
+ /* constants will be moved into a struct and additional intialization */
+ /* code will be used. */
+ /* */
+ /* Setting this macro is needed for systems that prohibit address */
+ /* fixups, such as BREW. [Note that standard compilers like gcc or */
+ /* clang handle PIC generation automatically; you don't have to set */
+ /* FT_CONFIG_OPTION_PIC, which is only necessary for very special */
+ /* compilers.] */
+ /* */
+ /* Note that FT_CONFIG_OPTION_PIC support is not available for all */
+ /* modules (see `modules.cfg' for a complete list). For building with */
+ /* FT_CONFIG_OPTION_PIC support, do the following. */
+ /* */
+ /* 0. Clone the repository. */
+ /* 1. Define FT_CONFIG_OPTION_PIC. */
+ /* 2. Remove all subdirectories in `src' that don't have */
+ /* FT_CONFIG_OPTION_PIC support. */
+ /* 3. Comment out the corresponding modules in `modules.cfg'. */
+ /* 4. Compile. */
+ /* */
+/* #define FT_CONFIG_OPTION_PIC */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** S F N T D R I V E R C O N F I G U R A T I O N ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */
+ /* embedded bitmaps in all formats using the SFNT module (namely */
+ /* TrueType & OpenType). */
+ /* */
+#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+
+ /*************************************************************************/
+ /* */
+ /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */
+ /* load and enumerate the glyph Postscript names in a TrueType or */
+ /* OpenType file. */
+ /* */
+ /* Note that when you do not compile the `PSNames' module by undefining */
+ /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */
+ /* contain additional code used to read the PS Names table from a font. */
+ /* */
+ /* (By default, the module uses `PSNames' to extract glyph names.) */
+ /* */
+#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+
+ /*************************************************************************/
+ /* */
+ /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */
+ /* access the internal name table in a SFNT-based format like TrueType */
+ /* or OpenType. The name table contains various strings used to */
+ /* describe the font, like family name, copyright, version, etc. It */
+ /* does not contain any glyph name though. */
+ /* */
+ /* Accessing SFNT names is done through the functions declared in */
+ /* `ftsnames.h'. */
+ /* */
+#define TT_CONFIG_OPTION_SFNT_NAMES
+
+
+ /*************************************************************************/
+ /* */
+ /* TrueType CMap support */
+ /* */
+ /* Here you can fine-tune which TrueType CMap table format shall be */
+ /* supported. */
+#define TT_CONFIG_CMAP_FORMAT_0
+#define TT_CONFIG_CMAP_FORMAT_2
+#define TT_CONFIG_CMAP_FORMAT_4
+#define TT_CONFIG_CMAP_FORMAT_6
+#define TT_CONFIG_CMAP_FORMAT_8
+#define TT_CONFIG_CMAP_FORMAT_10
+#define TT_CONFIG_CMAP_FORMAT_12
+#define TT_CONFIG_CMAP_FORMAT_13
+#define TT_CONFIG_CMAP_FORMAT_14
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */
+ /* a bytecode interpreter in the TrueType driver. */
+ /* */
+ /* By undefining this, you will only compile the code necessary to load */
+ /* TrueType glyphs without hinting. */
+ /* */
+ /* Do not #undef this macro here, since the build system might */
+ /* define it for certain configurations only. */
+ /* */
+#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER
+
+
+ /*************************************************************************/
+ /* */
+ /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */
+ /* subpixel hinting support into the TrueType driver. This modifies the */
+ /* TrueType hinting mechanism when anything but FT_RENDER_MODE_MONO is */
+ /* requested. */
+ /* */
+ /* In particular, it modifies the bytecode interpreter to interpret (or */
+ /* not) instructions in a certain way so that all TrueType fonts look */
+ /* like they do in a Windows ClearType (DirectWrite) environment. See */
+ /* [1] for a technical overview on what this means. See `ttinterp.h' */
+ /* for more details on the LEAN option. */
+ /* */
+ /* There are three possible values. */
+ /* */
+ /* Value 1: */
+ /* This value is associated with the `Infinality' moniker, */
+ /* contributed by an individual nicknamed Infinality with the goal of */
+ /* making TrueType fonts render better than on Windows. A high */
+ /* amount of configurability and flexibility, down to rules for */
+ /* single glyphs in fonts, but also very slow. Its experimental and */
+ /* slow nature and the original developer losing interest meant that */
+ /* this option was never enabled in default builds. */
+ /* */
+ /* The corresponding interpreter version is v38. */
+ /* */
+ /* Value 2: */
+ /* The new default mode for the TrueType driver. The Infinality code */
+ /* base was stripped to the bare minimum and all configurability */
+ /* removed in the name of speed and simplicity. The configurability */
+ /* was mainly aimed at legacy fonts like Arial, Times New Roman, or */
+ /* Courier. Legacy fonts are fonts that modify vertical stems to */
+ /* achieve clean black-and-white bitmaps. The new mode focuses on */
+ /* applying a minimal set of rules to all fonts indiscriminately so */
+ /* that modern and web fonts render well while legacy fonts render */
+ /* okay. */
+ /* */
+ /* The corresponding interpreter version is v40. */
+ /* */
+ /* Value 3: */
+ /* Compile both, making both v38 and v40 available (the latter is the */
+ /* default). */
+ /* */
+ /* By undefining these, you get rendering behavior like on Windows */
+ /* without ClearType, i.e., Windows XP without ClearType enabled and */
+ /* Win9x (interpreter version v35). Or not, depending on how much */
+ /* hinting blood and testing tears the font designer put into a given */
+ /* font. If you define one or both subpixel hinting options, you can */
+ /* switch between between v35 and the ones you define (using */
+ /* `FT_Property_Set'). */
+ /* */
+ /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */
+ /* defined. */
+ /* */
+ /* [1] https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */
+ /* */
+/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING 1 */
+#define TT_CONFIG_OPTION_SUBPIXEL_HINTING 2
+/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING ( 1 | 2 ) */
+
+
+ /*************************************************************************/
+ /* */
+ /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */
+ /* TrueType glyph loader to use Apple's definition of how to handle */
+ /* component offsets in composite glyphs. */
+ /* */
+ /* Apple and MS disagree on the default behavior of component offsets */
+ /* in composites. Apple says that they should be scaled by the scaling */
+ /* factors in the transformation matrix (roughly, it's more complex) */
+ /* while MS says they should not. OpenType defines two bits in the */
+ /* composite flags array which can be used to disambiguate, but old */
+ /* fonts will not have them. */
+ /* */
+ /* https://www.microsoft.com/typography/otspec/glyf.htm */
+ /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html */
+ /* */
+#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED
+
+
+ /*************************************************************************/
+ /* */
+ /* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include */
+ /* support for Apple's distortable font technology (fvar, gvar, cvar, */
+ /* and avar tables). This has many similarities to Type 1 Multiple */
+ /* Masters support. */
+ /* */
+#define TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+
+ /*************************************************************************/
+ /* */
+ /* Define TT_CONFIG_OPTION_BDF if you want to include support for */
+ /* an embedded `BDF ' table within SFNT-based bitmap formats. */
+ /* */
+#define TT_CONFIG_OPTION_BDF
+
+
+ /*************************************************************************/
+ /* */
+ /* Option TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES controls the maximum */
+ /* number of bytecode instructions executed for a single run of the */
+ /* bytecode interpreter, needed to prevent infinite loops. You don't */
+ /* want to change this except for very special situations (e.g., making */
+ /* a library fuzzer spend less time to handle broken fonts). */
+ /* */
+ /* It is not expected that this value is ever modified by a configuring */
+ /* script; instead, it gets surrounded with #ifndef ... #endif so that */
+ /* the value can be set as a preprocessor option on the compiler's */
+ /* command line. */
+ /* */
+#ifndef TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES
+#define TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES 1000000L
+#endif
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* T1_MAX_DICT_DEPTH is the maximum depth of nest dictionaries and */
+ /* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */
+ /* required. */
+ /* */
+#define T1_MAX_DICT_DEPTH 5
+
+
+ /*************************************************************************/
+ /* */
+ /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */
+ /* calls during glyph loading. */
+ /* */
+#define T1_MAX_SUBRS_CALLS 16
+
+
+ /*************************************************************************/
+ /* */
+ /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */
+ /* minimum of 16 is required. */
+ /* */
+ /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */
+ /* */
+#define T1_MAX_CHARSTRINGS_OPERANDS 256
+
+
+ /*************************************************************************/
+ /* */
+ /* Define this configuration macro if you want to prevent the */
+ /* compilation of `t1afm', which is in charge of reading Type 1 AFM */
+ /* files into an existing face. Note that if set, the T1 driver will be */
+ /* unable to produce kerning distances. */
+ /* */
+#undef T1_CONFIG_OPTION_NO_AFM
+
+
+ /*************************************************************************/
+ /* */
+ /* Define this configuration macro if you want to prevent the */
+ /* compilation of the Multiple Masters font support in the Type 1 */
+ /* driver. */
+ /* */
+#undef T1_CONFIG_OPTION_NO_MM_SUPPORT
+
+
+ /*************************************************************************/
+ /* */
+ /* T1_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe Type 1 */
+ /* engine gets compiled into FreeType. If defined, it is possible to */
+ /* switch between the two engines using the `hinting-engine' property of */
+ /* the type1 driver module. */
+ /* */
+/* #define T1_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** C F F D R I V E R C O N F I G U R A T I O N ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* Using CFF_CONFIG_OPTION_DARKENING_PARAMETER_{X,Y}{1,2,3,4} it is */
+ /* possible to set up the default values of the four control points that */
+ /* define the stem darkening behaviour of the (new) CFF engine. For */
+ /* more details please read the documentation of the */
+ /* `darkening-parameters' property (file `ftdriver.h'), which allows the */
+ /* control at run-time. */
+ /* */
+ /* Do *not* undefine these macros! */
+ /* */
+#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 500
+#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 400
+
+#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 1000
+#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 275
+
+#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 1667
+#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 275
+
+#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 2333
+#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 0
+
+
+ /*************************************************************************/
+ /* */
+ /* CFF_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe CFF */
+ /* engine gets compiled into FreeType. If defined, it is possible to */
+ /* switch between the two engines using the `hinting-engine' property of */
+ /* the cff driver module. */
+ /* */
+/* #define CFF_CONFIG_OPTION_OLD_ENGINE */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** P C F D R I V E R C O N F I G U R A T I O N ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* There are many PCF fonts just called `Fixed' which look completely */
+ /* different, and which have nothing to do with each other. When */
+ /* selecting `Fixed' in KDE or Gnome one gets results that appear rather */
+ /* random, the style changes often if one changes the size and one */
+ /* cannot select some fonts at all. This option makes the PCF module */
+ /* prepend the foundry name (plus a space) to the family name. */
+ /* */
+ /* We also check whether we have `wide' characters; all put together, we */
+ /* get family names like `Sony Fixed' or `Misc Fixed Wide'. */
+ /* */
+ /* If this option is activated, it can be controlled with the */
+ /* `no-long-family-names' property of the pcf driver module. */
+ /* */
+/* #define PCF_CONFIG_OPTION_LONG_FAMILY_NAMES */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* Compile autofit module with CJK (Chinese, Japanese, Korean) script */
+ /* support. */
+ /* */
+#define AF_CONFIG_OPTION_CJK
+
+ /*************************************************************************/
+ /* */
+ /* Compile autofit module with fallback Indic script support, covering */
+ /* some scripts that the `latin' submodule of the autofit module doesn't */
+ /* (yet) handle. */
+ /* */
+#define AF_CONFIG_OPTION_INDIC
+
+ /*************************************************************************/
+ /* */
+ /* Compile autofit module with warp hinting. The idea of the warping */
+ /* code is to slightly scale and shift a glyph within a single dimension */
+ /* so that as much of its segments are aligned (more or less) on the */
+ /* grid. To find out the optimal scaling and shifting value, various */
+ /* parameter combinations are tried and scored. */
+ /* */
+ /* This experimental option is active only if the rendering mode is */
+ /* FT_RENDER_MODE_LIGHT; you can switch warping on and off with the */
+ /* `warping' property of the auto-hinter (see file `ftdriver.h' for more */
+ /* information; by default it is switched off). */
+ /* */
+#define AF_CONFIG_OPTION_USE_WARPER
+
+ /*************************************************************************/
+ /* */
+ /* Use TrueType-like size metrics for `light' auto-hinting. */
+ /* */
+ /* It is strongly recommended to avoid this option, which exists only to */
+ /* help some legacy applications retain its appearance and behaviour */
+ /* with respect to auto-hinted TrueType fonts. */
+ /* */
+ /* The very reason this option exists at all are GNU/Linux distributions */
+ /* like Fedora that did not un-patch the following change (which was */
+ /* present in FreeType between versions 2.4.6 and 2.7.1, inclusive). */
+ /* */
+ /* 2011-07-16 Steven Chu */
+ /* */
+ /* [truetype] Fix metrics on size request for scalable fonts. */
+ /* */
+ /* This problematic commit is now reverted (more or less). */
+ /* */
+/* #define AF_CONFIG_OPTION_TT_SIZE_METRICS */
+
+ /* */
+
+
+ /*
+ * This macro is obsolete. Support has been removed in FreeType
+ * version 2.5.
+ */
+/* #define FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+ /*
+ * This macro is defined if native TrueType hinting is requested by the
+ * definitions above.
+ */
+#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
+#define TT_USE_BYTECODE_INTERPRETER
+
+#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
+#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 1
+#define TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
+#endif
+
+#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 2
+#define TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+#endif
+#endif
+#endif
+
+
+ /*
+ * Check CFF darkening parameters. The checks are the same as in function
+ * `cff_property_set' in file `cffdrivr.c'.
+ */
+#if CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 < 0 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 < 0 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 < 0 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 < 0 || \
+ \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 < 0 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 < 0 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 < 0 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 < 0 || \
+ \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 > \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 > \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 > \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 || \
+ \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 > 500 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 > 500 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 > 500 || \
+ CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 > 500
+#error "Invalid CFF darkening parameters!"
+#endif
+
+FT_END_HEADER
+
+
+#endif /* FTOPTION_H_ */
+
+
+/* END */
diff --git a/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftstdlib.h b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftstdlib.h
new file mode 100644
index 0000000..42f9a06
--- /dev/null
+++ b/src_v2/libs/sloth/freetype/freetype2/freetype/config/ftstdlib.h
@@ -0,0 +1,175 @@
+/***************************************************************************/
+/* */
+/* ftstdlib.h */
+/* */
+/* ANSI-specific library and header configuration file (specification */
+/* only). */
+/* */
+/* Copyright 2002-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to group all #includes to the ANSI C library that */
+ /* FreeType normally requires. It also defines macros to rename the */
+ /* standard functions within the FreeType source code. */
+ /* */
+ /* Load a file which defines FTSTDLIB_H_ before this one to override it. */
+ /* */
+ /*************************************************************************/
+
+
+#ifndef FTSTDLIB_H_
+#define FTSTDLIB_H_
+
+
+#include
+
+#define ft_ptrdiff_t ptrdiff_t
+
+
+ /**********************************************************************/
+ /* */
+ /* integer limits */
+ /* */
+ /* UINT_MAX and ULONG_MAX are used to automatically compute the size */
+ /* of `int' and `long' in bytes at compile-time. So far, this works */
+ /* for all platforms the library has been tested on. */
+ /* */
+ /* Note that on the extremely rare platforms that do not provide */
+ /* integer types that are _exactly_ 16 and 32 bits wide (e.g. some */
+ /* old Crays where `int' is 36 bits), we do not make any guarantee */
+ /* about the correct behaviour of FT2 with all fonts. */
+ /* */
+ /* In these case, `ftconfig.h' will refuse to compile anyway with a */
+ /* message like `couldn't find 32-bit type' or something similar. */
+ /* */
+ /**********************************************************************/
+
+
+#include
+
+#define FT_CHAR_BIT CHAR_BIT
+#define FT_USHORT_MAX USHRT_MAX
+#define FT_INT_MAX INT_MAX
+#define FT_INT_MIN INT_MIN
+#define FT_UINT_MAX UINT_MAX
+#define FT_LONG_MIN LONG_MIN
+#define FT_LONG_MAX LONG_MAX
+#define FT_ULONG_MAX ULONG_MAX
+
+
+ /**********************************************************************/
+ /* */
+ /* character and string processing */
+ /* */
+ /**********************************************************************/
+
+
+#include
+
+#define ft_memchr memchr
+#define ft_memcmp memcmp
+#define ft_memcpy memcpy
+#define ft_memmove memmove
+#define ft_memset memset
+#define ft_strcat strcat
+#define ft_strcmp strcmp
+#define ft_strcpy strcpy
+#define ft_strlen strlen
+#define ft_strncmp strncmp
+#define ft_strncpy strncpy
+#define ft_strrchr strrchr
+#define ft_strstr strstr
+
+
+ /**********************************************************************/
+ /* */
+ /* file handling */
+ /* */
+ /**********************************************************************/
+
+
+#include
+
+#define FT_FILE FILE
+#define ft_fclose fclose
+#define ft_fopen fopen
+#define ft_fread fread
+#define ft_fseek fseek
+#define ft_ftell ftell
+#define ft_sprintf sprintf
+
+
+ /**********************************************************************/
+ /* */
+ /* sorting */
+ /* */
+ /**********************************************************************/
+
+
+#include
+
+#define ft_qsort qsort
+
+
+ /**********************************************************************/
+ /* */
+ /* memory allocation */
+ /* */
+ /**********************************************************************/
+
+
+#define ft_scalloc calloc
+#define ft_sfree free
+#define ft_smalloc malloc
+#define ft_srealloc realloc
+
+
+ /**********************************************************************/
+ /* */
+ /* miscellaneous */
+ /* */
+ /**********************************************************************/
+
+
+#define ft_strtol strtol
+#define ft_getenv getenv
+
+
+ /**********************************************************************/
+ /* */
+ /* execution control */
+ /* */
+ /**********************************************************************/
+
+
+#include
+
+#define ft_jmp_buf jmp_buf /* note: this cannot be a typedef since */
+ /* jmp_buf is defined as a macro */
+ /* on certain platforms */
+
+#define ft_longjmp longjmp
+#define ft_setjmp( b ) setjmp( *(ft_jmp_buf*) &(b) ) /* same thing here */
+
+
+ /* the following is only used for debugging purposes, i.e., if */
+ /* FT_DEBUG_LEVEL_ERROR or FT_DEBUG_LEVEL_TRACE are defined */
+
+#include
+
+
+#endif /* FTSTDLIB_H_ */
+
+
+/* END */
diff --git a/src_v2/libs/sloth/freetype/freetype2/freetype/freetype.h b/src_v2/libs/sloth/freetype/freetype2/freetype/freetype.h
new file mode 100644
index 0000000..eda95e2
--- /dev/null
+++ b/src_v2/libs/sloth/freetype/freetype2/freetype/freetype.h
@@ -0,0 +1,4652 @@
+/***************************************************************************/
+/* */
+/* freetype.h */
+/* */
+/* FreeType high-level API and common types (specification only). */
+/* */
+/* Copyright 1996-2018 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef FREETYPE_H_
+#define FREETYPE_H_
+
+
+#ifndef FT_FREETYPE_H
+#error "`ft2build.h' hasn't been included yet!"
+#error "Please always use macros to include FreeType header files."
+#error "Example:"
+#error " #include "
+#error " #include FT_FREETYPE_H"
+#endif
+
+
+#include
+#include FT_CONFIG_CONFIG_H
+#include FT_TYPES_H
+#include FT_ERRORS_H
+
+
+FT_BEGIN_HEADER
+
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* header_inclusion */
+ /* */
+ /* */
+ /* FreeType's header inclusion scheme */
+ /* */
+ /* */
+ /* How client applications should include FreeType header files. */
+ /* */
+ /* */
+ /* To be as flexible as possible (and for historical reasons), */
+ /* FreeType uses a very special inclusion scheme to load header */
+ /* files, for example */
+ /* */
+ /* { */
+ /* #include */
+ /* */
+ /* #include FT_FREETYPE_H */
+ /* #include FT_OUTLINE_H */
+ /* } */
+ /* */
+ /* A compiler and its preprocessor only needs an include path to find */
+ /* the file `ft2build.h'; the exact locations and names of the other */
+ /* FreeType header files are hidden by preprocessor macro names, */
+ /* loaded by `ft2build.h'. The API documentation always gives the */
+ /* header macro name needed for a particular function. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* user_allocation */
+ /* */
+ /* */
+ /* User allocation */
+ /* */
+ /* */
+ /* How client applications should allocate FreeType data structures. */
+ /* */
+ /* */
+ /* FreeType assumes that structures allocated by the user and passed */
+ /* as arguments are zeroed out except for the actual data. In other */
+ /* words, it is recommended to use `calloc' (or variants of it) */
+ /* instead of `malloc' for allocation. */
+ /* */
+ /*************************************************************************/
+
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /* */
+ /* B A S I C T Y P E S */
+ /* */
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* base_interface */
+ /* */
+ /* */
+ /* Base Interface */
+ /* */
+ /* */
+ /* The FreeType~2 base font interface. */
+ /* */
+ /* */
+ /* This section describes the most important public high-level API */
+ /* functions of FreeType~2. */
+ /* */
+ /* */
+ /* FT_Library */
+ /* FT_Face */
+ /* FT_Size */
+ /* FT_GlyphSlot */
+ /* FT_CharMap */
+ /* FT_Encoding */
+ /* FT_ENC_TAG */
+ /* */
+ /* FT_FaceRec */
+ /* */
+ /* FT_FACE_FLAG_SCALABLE */
+ /* FT_FACE_FLAG_FIXED_SIZES */
+ /* FT_FACE_FLAG_FIXED_WIDTH */
+ /* FT_FACE_FLAG_HORIZONTAL */
+ /* FT_FACE_FLAG_VERTICAL */
+ /* FT_FACE_FLAG_COLOR */
+ /* FT_FACE_FLAG_SFNT */
+ /* FT_FACE_FLAG_CID_KEYED */
+ /* FT_FACE_FLAG_TRICKY */
+ /* FT_FACE_FLAG_KERNING */
+ /* FT_FACE_FLAG_MULTIPLE_MASTERS */
+ /* FT_FACE_FLAG_VARIATION */
+ /* FT_FACE_FLAG_GLYPH_NAMES */
+ /* FT_FACE_FLAG_EXTERNAL_STREAM */
+ /* FT_FACE_FLAG_HINTER */
+ /* */
+ /* FT_HAS_HORIZONTAL */
+ /* FT_HAS_VERTICAL */
+ /* FT_HAS_KERNING */
+ /* FT_HAS_FIXED_SIZES */
+ /* FT_HAS_GLYPH_NAMES */
+ /* FT_HAS_COLOR */
+ /* FT_HAS_MULTIPLE_MASTERS */
+ /* */
+ /* FT_IS_SFNT */
+ /* FT_IS_SCALABLE */
+ /* FT_IS_FIXED_WIDTH */
+ /* FT_IS_CID_KEYED */
+ /* FT_IS_TRICKY */
+ /* FT_IS_NAMED_INSTANCE */
+ /* FT_IS_VARIATION */
+ /* */
+ /* FT_STYLE_FLAG_BOLD */
+ /* FT_STYLE_FLAG_ITALIC */
+ /* */
+ /* FT_SizeRec */
+ /* FT_Size_Metrics */
+ /* */
+ /* FT_GlyphSlotRec */
+ /* FT_Glyph_Metrics */
+ /* FT_SubGlyph */
+ /* */
+ /* FT_Bitmap_Size */
+ /* */
+ /* FT_Init_FreeType */
+ /* FT_Done_FreeType */
+ /* */
+ /* FT_New_Face */
+ /* FT_Done_Face */
+ /* FT_Reference_Face */
+ /* FT_New_Memory_Face */
+ /* FT_Face_Properties */
+ /* FT_Open_Face */
+ /* FT_Open_Args */
+ /* FT_Parameter */
+ /* FT_Attach_File */
+ /* FT_Attach_Stream */
+ /* */
+ /* FT_Set_Char_Size */
+ /* FT_Set_Pixel_Sizes */
+ /* FT_Request_Size */
+ /* FT_Select_Size */
+ /* FT_Size_Request_Type */
+ /* FT_Size_RequestRec */
+ /* FT_Size_Request */
+ /* FT_Set_Transform */
+ /* FT_Load_Glyph */
+ /* FT_Get_Char_Index */
+ /* FT_Get_First_Char */
+ /* FT_Get_Next_Char */
+ /* FT_Get_Name_Index */
+ /* FT_Load_Char */
+ /* */
+ /* FT_OPEN_MEMORY */
+ /* FT_OPEN_STREAM */
+ /* FT_OPEN_PATHNAME */
+ /* FT_OPEN_DRIVER */
+ /* FT_OPEN_PARAMS */
+ /* */
+ /* FT_LOAD_DEFAULT */
+ /* FT_LOAD_RENDER */
+ /* FT_LOAD_MONOCHROME */
+ /* FT_LOAD_LINEAR_DESIGN */
+ /* FT_LOAD_NO_SCALE */
+ /* FT_LOAD_NO_HINTING */
+ /* FT_LOAD_NO_BITMAP */
+ /* FT_LOAD_NO_AUTOHINT */
+ /* FT_LOAD_COLOR */
+ /* */
+ /* FT_LOAD_VERTICAL_LAYOUT */
+ /* FT_LOAD_IGNORE_TRANSFORM */
+ /* FT_LOAD_FORCE_AUTOHINT */
+ /* FT_LOAD_NO_RECURSE */
+ /* FT_LOAD_PEDANTIC */
+ /* */
+ /* FT_LOAD_TARGET_NORMAL */
+ /* FT_LOAD_TARGET_LIGHT */
+ /* FT_LOAD_TARGET_MONO */
+ /* FT_LOAD_TARGET_LCD */
+ /* FT_LOAD_TARGET_LCD_V */
+ /* */
+ /* FT_LOAD_TARGET_MODE */
+ /* */
+ /* FT_Render_Glyph */
+ /* FT_Render_Mode */
+ /* FT_Get_Kerning */
+ /* FT_Kerning_Mode */
+ /* FT_Get_Track_Kerning */
+ /* FT_Get_Glyph_Name */
+ /* FT_Get_Postscript_Name */
+ /* */
+ /* FT_CharMapRec */
+ /* FT_Select_Charmap */
+ /* FT_Set_Charmap */
+ /* FT_Get_Charmap_Index */
+ /* */
+ /* FT_Get_FSType_Flags */
+ /* FT_Get_SubGlyph_Info */
+ /* */
+ /* FT_Face_Internal */
+ /* FT_Size_Internal */
+ /* FT_Slot_Internal */
+ /* */
+ /* FT_FACE_FLAG_XXX */
+ /* FT_STYLE_FLAG_XXX */
+ /* FT_OPEN_XXX */
+ /* FT_LOAD_XXX */
+ /* FT_LOAD_TARGET_XXX */
+ /* FT_SUBGLYPH_FLAG_XXX */
+ /* FT_FSTYPE_XXX */
+ /* */
+ /* FT_HAS_FAST_GLYPHS */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Glyph_Metrics */
+ /* */
+ /* */
+ /* A structure to model the metrics of a single glyph. The values */
+ /* are expressed in 26.6 fractional pixel format; if the flag */
+ /* @FT_LOAD_NO_SCALE has been used while loading the glyph, values */
+ /* are expressed in font units instead. */
+ /* */
+ /* */
+ /* width :: */
+ /* The glyph's width. */
+ /* */
+ /* height :: */
+ /* The glyph's height. */
+ /* */
+ /* horiBearingX :: */
+ /* Left side bearing for horizontal layout. */
+ /* */
+ /* horiBearingY :: */
+ /* Top side bearing for horizontal layout. */
+ /* */
+ /* horiAdvance :: */
+ /* Advance width for horizontal layout. */
+ /* */
+ /* vertBearingX :: */
+ /* Left side bearing for vertical layout. */
+ /* */
+ /* vertBearingY :: */
+ /* Top side bearing for vertical layout. Larger positive values */
+ /* mean further below the vertical glyph origin. */
+ /* */
+ /* vertAdvance :: */
+ /* Advance height for vertical layout. Positive values mean the */
+ /* glyph has a positive advance downward. */
+ /* */
+ /* */
+ /* If not disabled with @FT_LOAD_NO_HINTING, the values represent */
+ /* dimensions of the hinted glyph (in case hinting is applicable). */
+ /* */
+ /* Stroking a glyph with an outside border does not increase */
+ /* `horiAdvance' or `vertAdvance'; you have to manually adjust these */
+ /* values to account for the added width and height. */
+ /* */
+ /* FreeType doesn't use the `VORG' table data for CFF fonts because */
+ /* it doesn't have an interface to quickly retrieve the glyph height. */
+ /* The y~coordinate of the vertical origin can be simply computed as */
+ /* `vertBearingY + height' after loading a glyph. */
+ /* */
+ typedef struct FT_Glyph_Metrics_
+ {
+ FT_Pos width;
+ FT_Pos height;
+
+ FT_Pos horiBearingX;
+ FT_Pos horiBearingY;
+ FT_Pos horiAdvance;
+
+ FT_Pos vertBearingX;
+ FT_Pos vertBearingY;
+ FT_Pos vertAdvance;
+
+ } FT_Glyph_Metrics;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Bitmap_Size */
+ /* */
+ /* */
+ /* This structure models the metrics of a bitmap strike (i.e., a set */
+ /* of glyphs for a given point size and resolution) in a bitmap font. */
+ /* It is used for the `available_sizes' field of @FT_Face. */
+ /* */
+ /* */
+ /* height :: The vertical distance, in pixels, between two */
+ /* consecutive baselines. It is always positive. */
+ /* */
+ /* width :: The average width, in pixels, of all glyphs in the */
+ /* strike. */
+ /* */
+ /* size :: The nominal size of the strike in 26.6 fractional */
+ /* points. This field is not very useful. */
+ /* */
+ /* x_ppem :: The horizontal ppem (nominal width) in 26.6 fractional */
+ /* pixels. */
+ /* */
+ /* y_ppem :: The vertical ppem (nominal height) in 26.6 fractional */
+ /* pixels. */
+ /* */
+ /* */
+ /* Windows FNT: */
+ /* The nominal size given in a FNT font is not reliable. If the */
+ /* driver finds it incorrect, it sets `size' to some calculated */
+ /* values, and `x_ppem' and `y_ppem' to the pixel width and height */
+ /* given in the font, respectively. */
+ /* */
+ /* TrueType embedded bitmaps: */
+ /* `size', `width', and `height' values are not contained in the */
+ /* bitmap strike itself. They are computed from the global font */
+ /* parameters. */
+ /* */
+ typedef struct FT_Bitmap_Size_
+ {
+ FT_Short height;
+ FT_Short width;
+
+ FT_Pos size;
+
+ FT_Pos x_ppem;
+ FT_Pos y_ppem;
+
+ } FT_Bitmap_Size;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /* */
+ /* O B J E C T C L A S S E S */
+ /* */
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Library */
+ /* */
+ /* */
+ /* A handle to a FreeType library instance. Each `library' is */
+ /* completely independent from the others; it is the `root' of a set */
+ /* of objects like fonts, faces, sizes, etc. */
+ /* */
+ /* It also embeds a memory manager (see @FT_Memory), as well as a */
+ /* scan-line converter object (see @FT_Raster). */
+ /* */
+ /* In multi-threaded applications it is easiest to use one */
+ /* `FT_Library' object per thread. In case this is too cumbersome, */
+ /* a single `FT_Library' object across threads is possible also */
+ /* (since FreeType version 2.5.6), as long as a mutex lock is used */
+ /* around @FT_New_Face and @FT_Done_Face. */
+ /* */
+ /* */
+ /* Library objects are normally created by @FT_Init_FreeType, and */
+ /* destroyed with @FT_Done_FreeType. If you need reference-counting */
+ /* (cf. @FT_Reference_Library), use @FT_New_Library and */
+ /* @FT_Done_Library. */
+ /* */
+ typedef struct FT_LibraryRec_ *FT_Library;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* module_management */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Module */
+ /* */
+ /* */
+ /* A handle to a given FreeType module object. A module can be a */
+ /* font driver, a renderer, or anything else that provides services */
+ /* to the former. */
+ /* */
+ typedef struct FT_ModuleRec_* FT_Module;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Driver */
+ /* */
+ /* */
+ /* A handle to a given FreeType font driver object. A font driver */
+ /* is a module capable of creating faces from font files. */
+ /* */
+ typedef struct FT_DriverRec_* FT_Driver;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Renderer */
+ /* */
+ /* */
+ /* A handle to a given FreeType renderer. A renderer is a module in */
+ /* charge of converting a glyph's outline image to a bitmap. It */
+ /* supports a single glyph image format, and one or more target */
+ /* surface depths. */
+ /* */
+ typedef struct FT_RendererRec_* FT_Renderer;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* base_interface */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Face */
+ /* */
+ /* */
+ /* A handle to a typographic face object. A face object models a */
+ /* given typeface, in a given style. */
+ /* */
+ /* */
+ /* A face object also owns a single @FT_GlyphSlot object, as well */
+ /* as one or more @FT_Size objects. */
+ /* */
+ /* Use @FT_New_Face or @FT_Open_Face to create a new face object from */
+ /* a given filepath or a custom input stream. */
+ /* */
+ /* Use @FT_Done_Face to destroy it (along with its slot and sizes). */
+ /* */
+ /* An `FT_Face' object can only be safely used from one thread at a */
+ /* time. Similarly, creation and destruction of `FT_Face' with the */
+ /* same @FT_Library object can only be done from one thread at a */
+ /* time. On the other hand, functions like @FT_Load_Glyph and its */
+ /* siblings are thread-safe and do not need the lock to be held as */
+ /* long as the same `FT_Face' object is not used from multiple */
+ /* threads at the same time. */
+ /* */
+ /* */
+ /* See @FT_FaceRec for the publicly accessible fields of a given face */
+ /* object. */
+ /* */
+ typedef struct FT_FaceRec_* FT_Face;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Size */
+ /* */
+ /* */
+ /* A handle to an object that models a face scaled to a given */
+ /* character size. */
+ /* */
+ /* */
+ /* An @FT_Face has one _active_ @FT_Size object that is used by */
+ /* functions like @FT_Load_Glyph to determine the scaling */
+ /* transformation that in turn is used to load and hint glyphs and */
+ /* metrics. */
+ /* */
+ /* You can use @FT_Set_Char_Size, @FT_Set_Pixel_Sizes, */
+ /* @FT_Request_Size or even @FT_Select_Size to change the content */
+ /* (i.e., the scaling values) of the active @FT_Size. */
+ /* */
+ /* You can use @FT_New_Size to create additional size objects for a */
+ /* given @FT_Face, but they won't be used by other functions until */
+ /* you activate it through @FT_Activate_Size. Only one size can be */
+ /* activated at any given time per face. */
+ /* */
+ /* */
+ /* See @FT_SizeRec for the publicly accessible fields of a given size */
+ /* object. */
+ /* */
+ typedef struct FT_SizeRec_* FT_Size;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_GlyphSlot */
+ /* */
+ /* */
+ /* A handle to a given `glyph slot'. A slot is a container that can */
+ /* hold any of the glyphs contained in its parent face. */
+ /* */
+ /* In other words, each time you call @FT_Load_Glyph or */
+ /* @FT_Load_Char, the slot's content is erased by the new glyph data, */
+ /* i.e., the glyph's metrics, its image (bitmap or outline), and */
+ /* other control information. */
+ /* */
+ /* */
+ /* See @FT_GlyphSlotRec for the publicly accessible glyph fields. */
+ /* */
+ typedef struct FT_GlyphSlotRec_* FT_GlyphSlot;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_CharMap */
+ /* */
+ /* */
+ /* A handle to a character map (usually abbreviated to `charmap'). A */
+ /* charmap is used to translate character codes in a given encoding */
+ /* into glyph indexes for its parent's face. Some font formats may */
+ /* provide several charmaps per font. */
+ /* */
+ /* Each face object owns zero or more charmaps, but only one of them */
+ /* can be `active', providing the data used by @FT_Get_Char_Index or */
+ /* @FT_Load_Char. */
+ /* */
+ /* The list of available charmaps in a face is available through the */
+ /* `face->num_charmaps' and `face->charmaps' fields of @FT_FaceRec. */
+ /* */
+ /* The currently active charmap is available as `face->charmap'. */
+ /* You should call @FT_Set_Charmap to change it. */
+ /* */
+ /* */
+ /* When a new face is created (either through @FT_New_Face or */
+ /* @FT_Open_Face), the library looks for a Unicode charmap within */
+ /* the list and automatically activates it. If there is no Unicode */
+ /* charmap, FreeType doesn't set an `active' charmap. */
+ /* */
+ /* */
+ /* See @FT_CharMapRec for the publicly accessible fields of a given */
+ /* character map. */
+ /* */
+ typedef struct FT_CharMapRec_* FT_CharMap;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_ENC_TAG */
+ /* */
+ /* */
+ /* This macro converts four-letter tags into an unsigned long. It is */
+ /* used to define `encoding' identifiers (see @FT_Encoding). */
+ /* */
+ /* */
+ /* Since many 16-bit compilers don't like 32-bit enumerations, you */
+ /* should redefine this macro in case of problems to something like */
+ /* this: */
+ /* */
+ /* { */
+ /* #define FT_ENC_TAG( value, a, b, c, d ) value */
+ /* } */
+ /* */
+ /* to get a simple enumeration without assigning special numbers. */
+ /* */
+
+#ifndef FT_ENC_TAG
+#define FT_ENC_TAG( value, a, b, c, d ) \
+ value = ( ( (FT_UInt32)(a) << 24 ) | \
+ ( (FT_UInt32)(b) << 16 ) | \
+ ( (FT_UInt32)(c) << 8 ) | \
+ (FT_UInt32)(d) )
+
+#endif /* FT_ENC_TAG */
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Encoding */
+ /* */
+ /* */
+ /* An enumeration to specify character sets supported by charmaps. */
+ /* Used in the @FT_Select_Charmap API function. */
+ /* */
+ /* */
+ /* Despite the name, this enumeration lists specific character */
+ /* repertories (i.e., charsets), and not text encoding methods (e.g., */
+ /* UTF-8, UTF-16, etc.). */
+ /* */
+ /* Other encodings might be defined in the future. */
+ /* */
+ /* */
+ /* FT_ENCODING_NONE :: */
+ /* The encoding value~0 is reserved. */
+ /* */
+ /* FT_ENCODING_UNICODE :: */
+ /* The Unicode character set. This value covers all versions of */
+ /* the Unicode repertoire, including ASCII and Latin-1. Most fonts */
+ /* include a Unicode charmap, but not all of them. */
+ /* */
+ /* For example, if you want to access Unicode value U+1F028 (and */
+ /* the font contains it), use value 0x1F028 as the input value for */
+ /* @FT_Get_Char_Index. */
+ /* */
+ /* FT_ENCODING_MS_SYMBOL :: */
+ /* Microsoft Symbol encoding, used to encode mathematical symbols */
+ /* and wingdings. For more information, see */
+ /* `https://www.microsoft.com/typography/otspec/recom.htm', */
+ /* `http://www.kostis.net/charsets/symbol.htm', and */
+ /* `http://www.kostis.net/charsets/wingding.htm'. */
+ /* */
+ /* This encoding uses character codes from the PUA (Private Unicode */
+ /* Area) in the range U+F020-U+F0FF. */
+ /* */
+ /* FT_ENCODING_SJIS :: */
+ /* Shift JIS encoding for Japanese. More info at */
+ /* `https://en.wikipedia.org/wiki/Shift_JIS'. See note on */
+ /* multi-byte encodings below. */
+ /* */
+ /* FT_ENCODING_PRC :: */
+ /* Corresponds to encoding systems mainly for Simplified Chinese as */
+ /* used in People's Republic of China (PRC). The encoding layout */
+ /* is based on GB~2312 and its supersets GBK and GB~18030. */
+ /* */
+ /* FT_ENCODING_BIG5 :: */
+ /* Corresponds to an encoding system for Traditional Chinese as */
+ /* used in Taiwan and Hong Kong. */
+ /* */
+ /* FT_ENCODING_WANSUNG :: */
+ /* Corresponds to the Korean encoding system known as Extended */
+ /* Wansung (MS Windows code page 949). */
+ /* For more information see */
+ /* `https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/bestfit949.txt'. */
+ /* */
+ /* FT_ENCODING_JOHAB :: */
+ /* The Korean standard character set (KS~C 5601-1992), which */
+ /* corresponds to MS Windows code page 1361. This character set */
+ /* includes all possible Hangul character combinations. */
+ /* */
+ /* FT_ENCODING_ADOBE_LATIN_1 :: */
+ /* Corresponds to a Latin-1 encoding as defined in a Type~1 */
+ /* PostScript font. It is limited to 256 character codes. */
+ /* */
+ /* FT_ENCODING_ADOBE_STANDARD :: */
+ /* Adobe Standard encoding, as found in Type~1, CFF, and */
+ /* OpenType/CFF fonts. It is limited to 256 character codes. */
+ /* */
+ /* FT_ENCODING_ADOBE_EXPERT :: */
+ /* Adobe Expert encoding, as found in Type~1, CFF, and OpenType/CFF */
+ /* fonts. It is limited to 256 character codes. */
+ /* */
+ /* FT_ENCODING_ADOBE_CUSTOM :: */
+ /* Corresponds to a custom encoding, as found in Type~1, CFF, and */
+ /* OpenType/CFF fonts. It is limited to 256 character codes. */
+ /* */
+ /* FT_ENCODING_APPLE_ROMAN :: */
+ /* Apple roman encoding. Many TrueType and OpenType fonts contain */
+ /* a charmap for this 8-bit encoding, since older versions of Mac */
+ /* OS are able to use it. */
+ /* */
+ /* FT_ENCODING_OLD_LATIN_2 :: */
+ /* This value is deprecated and was neither used nor reported by */
+ /* FreeType. Don't use or test for it. */
+ /* */
+ /* FT_ENCODING_MS_SJIS :: */
+ /* Same as FT_ENCODING_SJIS. Deprecated. */
+ /* */
+ /* FT_ENCODING_MS_GB2312 :: */
+ /* Same as FT_ENCODING_PRC. Deprecated. */
+ /* */
+ /* FT_ENCODING_MS_BIG5 :: */
+ /* Same as FT_ENCODING_BIG5. Deprecated. */
+ /* */
+ /* FT_ENCODING_MS_WANSUNG :: */
+ /* Same as FT_ENCODING_WANSUNG. Deprecated. */
+ /* */
+ /* FT_ENCODING_MS_JOHAB :: */
+ /* Same as FT_ENCODING_JOHAB. Deprecated. */
+ /* */
+ /* */
+ /* By default, FreeType automatically synthesizes a Unicode charmap */
+ /* for PostScript fonts, using their glyph name dictionaries. */
+ /* However, it also reports the encodings defined explicitly in the */
+ /* font file, for the cases when they are needed, with the Adobe */
+ /* values as well. */
+ /* */
+ /* FT_ENCODING_NONE is set by the BDF and PCF drivers if the charmap */
+ /* is neither Unicode nor ISO-8859-1 (otherwise it is set to */
+ /* FT_ENCODING_UNICODE). Use @FT_Get_BDF_Charset_ID to find out */
+ /* which encoding is really present. If, for example, the */
+ /* `cs_registry' field is `KOI8' and the `cs_encoding' field is `R', */
+ /* the font is encoded in KOI8-R. */
+ /* */
+ /* FT_ENCODING_NONE is always set (with a single exception) by the */
+ /* winfonts driver. Use @FT_Get_WinFNT_Header and examine the */
+ /* `charset' field of the @FT_WinFNT_HeaderRec structure to find out */
+ /* which encoding is really present. For example, */
+ /* @FT_WinFNT_ID_CP1251 (204) means Windows code page 1251 (for */
+ /* Russian). */
+ /* */
+ /* FT_ENCODING_NONE is set if `platform_id' is @TT_PLATFORM_MACINTOSH */
+ /* and `encoding_id' is not `TT_MAC_ID_ROMAN' (otherwise it is set to */
+ /* FT_ENCODING_APPLE_ROMAN). */
+ /* */
+ /* If `platform_id' is @TT_PLATFORM_MACINTOSH, use the function */
+ /* @FT_Get_CMap_Language_ID to query the Mac language ID that may */
+ /* be needed to be able to distinguish Apple encoding variants. See */
+ /* */
+ /* https://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/Readme.txt */
+ /* */
+ /* to get an idea how to do that. Basically, if the language ID */
+ /* is~0, don't use it, otherwise subtract 1 from the language ID. */
+ /* Then examine `encoding_id'. If, for example, `encoding_id' is */
+ /* `TT_MAC_ID_ROMAN' and the language ID (minus~1) is */
+ /* `TT_MAC_LANGID_GREEK', it is the Greek encoding, not Roman. */
+ /* `TT_MAC_ID_ARABIC' with `TT_MAC_LANGID_FARSI' means the Farsi */
+ /* variant the Arabic encoding. */
+ /* */
+ typedef enum FT_Encoding_
+ {
+ FT_ENC_TAG( FT_ENCODING_NONE, 0, 0, 0, 0 ),
+
+ FT_ENC_TAG( FT_ENCODING_MS_SYMBOL, 's', 'y', 'm', 'b' ),
+ FT_ENC_TAG( FT_ENCODING_UNICODE, 'u', 'n', 'i', 'c' ),
+
+ FT_ENC_TAG( FT_ENCODING_SJIS, 's', 'j', 'i', 's' ),
+ FT_ENC_TAG( FT_ENCODING_PRC, 'g', 'b', ' ', ' ' ),
+ FT_ENC_TAG( FT_ENCODING_BIG5, 'b', 'i', 'g', '5' ),
+ FT_ENC_TAG( FT_ENCODING_WANSUNG, 'w', 'a', 'n', 's' ),
+ FT_ENC_TAG( FT_ENCODING_JOHAB, 'j', 'o', 'h', 'a' ),
+
+ /* for backward compatibility */
+ FT_ENCODING_GB2312 = FT_ENCODING_PRC,
+ FT_ENCODING_MS_SJIS = FT_ENCODING_SJIS,
+ FT_ENCODING_MS_GB2312 = FT_ENCODING_PRC,
+ FT_ENCODING_MS_BIG5 = FT_ENCODING_BIG5,
+ FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG,
+ FT_ENCODING_MS_JOHAB = FT_ENCODING_JOHAB,
+
+ FT_ENC_TAG( FT_ENCODING_ADOBE_STANDARD, 'A', 'D', 'O', 'B' ),
+ FT_ENC_TAG( FT_ENCODING_ADOBE_EXPERT, 'A', 'D', 'B', 'E' ),
+ FT_ENC_TAG( FT_ENCODING_ADOBE_CUSTOM, 'A', 'D', 'B', 'C' ),
+ FT_ENC_TAG( FT_ENCODING_ADOBE_LATIN_1, 'l', 'a', 't', '1' ),
+
+ FT_ENC_TAG( FT_ENCODING_OLD_LATIN_2, 'l', 'a', 't', '2' ),
+
+ FT_ENC_TAG( FT_ENCODING_APPLE_ROMAN, 'a', 'r', 'm', 'n' )
+
+ } FT_Encoding;
+
+
+ /* these constants are deprecated; use the corresponding `FT_Encoding' */
+ /* values instead */
+#define ft_encoding_none FT_ENCODING_NONE
+#define ft_encoding_unicode FT_ENCODING_UNICODE
+#define ft_encoding_symbol FT_ENCODING_MS_SYMBOL
+#define ft_encoding_latin_1 FT_ENCODING_ADOBE_LATIN_1
+#define ft_encoding_latin_2 FT_ENCODING_OLD_LATIN_2
+#define ft_encoding_sjis FT_ENCODING_SJIS
+#define ft_encoding_gb2312 FT_ENCODING_PRC
+#define ft_encoding_big5 FT_ENCODING_BIG5
+#define ft_encoding_wansung FT_ENCODING_WANSUNG
+#define ft_encoding_johab FT_ENCODING_JOHAB
+
+#define ft_encoding_adobe_standard FT_ENCODING_ADOBE_STANDARD
+#define ft_encoding_adobe_expert FT_ENCODING_ADOBE_EXPERT
+#define ft_encoding_adobe_custom FT_ENCODING_ADOBE_CUSTOM
+#define ft_encoding_apple_roman FT_ENCODING_APPLE_ROMAN
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_CharMapRec */
+ /* */
+ /* */
+ /* The base charmap structure. */
+ /* */
+ /* */
+ /* face :: A handle to the parent face object. */
+ /* */
+ /* encoding :: An @FT_Encoding tag identifying the charmap. Use */
+ /* this with @FT_Select_Charmap. */
+ /* */
+ /* platform_id :: An ID number describing the platform for the */
+ /* following encoding ID. This comes directly from */
+ /* the TrueType specification and gets emulated for */
+ /* other formats. */
+ /* */
+ /* encoding_id :: A platform specific encoding number. This also */
+ /* comes from the TrueType specification and gets */
+ /* emulated similarly. */
+ /* */
+ typedef struct FT_CharMapRec_
+ {
+ FT_Face face;
+ FT_Encoding encoding;
+ FT_UShort platform_id;
+ FT_UShort encoding_id;
+
+ } FT_CharMapRec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /* */
+ /* B A S E O B J E C T C L A S S E S */
+ /* */
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Face_Internal */
+ /* */
+ /* */
+ /* An opaque handle to an `FT_Face_InternalRec' structure that models */
+ /* the private data of a given @FT_Face object. */
+ /* */
+ /* This structure might change between releases of FreeType~2 and is */
+ /* not generally available to client applications. */
+ /* */
+ typedef struct FT_Face_InternalRec_* FT_Face_Internal;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_FaceRec */
+ /* */
+ /* */
+ /* FreeType root face class structure. A face object models a */
+ /* typeface in a font file. */
+ /* */
+ /* */
+ /* num_faces :: The number of faces in the font file. Some */
+ /* font formats can have multiple faces in */
+ /* a single font file. */
+ /* */
+ /* face_index :: This field holds two different values. */
+ /* Bits 0-15 are the index of the face in the */
+ /* font file (starting with value~0). They */
+ /* are set to~0 if there is only one face in */
+ /* the font file. */
+ /* */
+ /* [Since 2.6.1] Bits 16-30 are relevant to GX */
+ /* and OpenType variation fonts only, holding */
+ /* the named instance index for the current */
+ /* face index (starting with value~1; value~0 */
+ /* indicates font access without a named */
+ /* instance). For non-variation fonts, bits */
+ /* 16-30 are ignored. If we have the third */
+ /* named instance of face~4, say, `face_index' */
+ /* is set to 0x00030004. */
+ /* */
+ /* Bit 31 is always zero (this is, */
+ /* `face_index' is always a positive value). */
+ /* */
+ /* [Since 2.9] Changing the design coordinates */
+ /* with @FT_Set_Var_Design_Coordinates or */
+ /* @FT_Set_Var_Blend_Coordinates does not */
+ /* influence the named instance index value */
+ /* (only @FT_Set_Named_Instance does that). */
+ /* */
+ /* face_flags :: A set of bit flags that give important */
+ /* information about the face; see */
+ /* @FT_FACE_FLAG_XXX for the details. */
+ /* */
+ /* style_flags :: The lower 16~bits contain a set of bit */
+ /* flags indicating the style of the face; see */
+ /* @FT_STYLE_FLAG_XXX for the details. */
+ /* */
+ /* [Since 2.6.1] Bits 16-30 hold the number */
+ /* of named instances available for the */
+ /* current face if we have a GX or OpenType */
+ /* variation (sub)font. Bit 31 is always zero */
+ /* (this is, `style_flags' is always a */
+ /* positive value). Note that a variation */
+ /* font has always at least one named */
+ /* instance, namely the default instance. */
+ /* */
+ /* num_glyphs :: The number of glyphs in the face. If the */
+ /* face is scalable and has sbits (see */
+ /* `num_fixed_sizes'), it is set to the number */
+ /* of outline glyphs. */
+ /* */
+ /* For CID-keyed fonts (not in an SFNT */
+ /* wrapper) this value gives the highest CID */
+ /* used in the font. */
+ /* */
+ /* family_name :: The face's family name. This is an ASCII */
+ /* string, usually in English, that describes */
+ /* the typeface's family (like `Times New */
+ /* Roman', `Bodoni', `Garamond', etc). This */
+ /* is a least common denominator used to list */
+ /* fonts. Some formats (TrueType & OpenType) */
+ /* provide localized and Unicode versions of */
+ /* this string. Applications should use the */
+ /* format specific interface to access them. */
+ /* Can be NULL (e.g., in fonts embedded in a */
+ /* PDF file). */
+ /* */
+ /* In case the font doesn't provide a specific */
+ /* family name entry, FreeType tries to */
+ /* synthesize one, deriving it from other name */
+ /* entries. */
+ /* */
+ /* style_name :: The face's style name. This is an ASCII */
+ /* string, usually in English, that describes */
+ /* the typeface's style (like `Italic', */
+ /* `Bold', `Condensed', etc). Not all font */
+ /* formats provide a style name, so this field */
+ /* is optional, and can be set to NULL. As */
+ /* for `family_name', some formats provide */
+ /* localized and Unicode versions of this */
+ /* string. Applications should use the format */
+ /* specific interface to access them. */
+ /* */
+ /* num_fixed_sizes :: The number of bitmap strikes in the face. */
+ /* Even if the face is scalable, there might */
+ /* still be bitmap strikes, which are called */
+ /* `sbits' in that case. */
+ /* */
+ /* available_sizes :: An array of @FT_Bitmap_Size for all bitmap */
+ /* strikes in the face. It is set to NULL if */
+ /* there is no bitmap strike. */
+ /* */
+ /* Note that FreeType tries to sanitize the */
+ /* strike data since they are sometimes sloppy */
+ /* or incorrect, but this can easily fail. */
+ /* */
+ /* num_charmaps :: The number of charmaps in the face. */
+ /* */
+ /* charmaps :: An array of the charmaps of the face. */
+ /* */
+ /* generic :: A field reserved for client uses. See the */
+ /* @FT_Generic type description. */
+ /* */
+ /* bbox :: The font bounding box. Coordinates are */
+ /* expressed in font units (see */
+ /* `units_per_EM'). The box is large enough */
+ /* to contain any glyph from the font. Thus, */
+ /* `bbox.yMax' can be seen as the `maximum */
+ /* ascender', and `bbox.yMin' as the `minimum */
+ /* descender'. Only relevant for scalable */
+ /* formats. */
+ /* */
+ /* Note that the bounding box might be off by */
+ /* (at least) one pixel for hinted fonts. See */
+ /* @FT_Size_Metrics for further discussion. */
+ /* */
+ /* units_per_EM :: The number of font units per EM square for */
+ /* this face. This is typically 2048 for */
+ /* TrueType fonts, and 1000 for Type~1 fonts. */
+ /* Only relevant for scalable formats. */
+ /* */
+ /* ascender :: The typographic ascender of the face, */
+ /* expressed in font units. For font formats */
+ /* not having this information, it is set to */
+ /* `bbox.yMax'. Only relevant for scalable */
+ /* formats. */
+ /* */
+ /* descender :: The typographic descender of the face, */
+ /* expressed in font units. For font formats */
+ /* not having this information, it is set to */
+ /* `bbox.yMin'. Note that this field is */
+ /* negative for values below the baseline. */
+ /* Only relevant for scalable formats. */
+ /* */
+ /* height :: This value is the vertical distance */
+ /* between two consecutive baselines, */
+ /* expressed in font units. It is always */
+ /* positive. Only relevant for scalable */
+ /* formats. */
+ /* */
+ /* If you want the global glyph height, use */
+ /* `ascender - descender'. */
+ /* */
+ /* max_advance_width :: The maximum advance width, in font units, */
+ /* for all glyphs in this face. This can be */
+ /* used to make word wrapping computations */
+ /* faster. Only relevant for scalable */
+ /* formats. */
+ /* */
+ /* max_advance_height :: The maximum advance height, in font units, */
+ /* for all glyphs in this face. This is only */
+ /* relevant for vertical layouts, and is set */
+ /* to `height' for fonts that do not provide */
+ /* vertical metrics. Only relevant for */
+ /* scalable formats. */
+ /* */
+ /* underline_position :: The position, in font units, of the */
+ /* underline line for this face. It is the */
+ /* center of the underlining stem. Only */
+ /* relevant for scalable formats. */
+ /* */
+ /* underline_thickness :: The thickness, in font units, of the */
+ /* underline for this face. Only relevant for */
+ /* scalable formats. */
+ /* */
+ /* glyph :: The face's associated glyph slot(s). */
+ /* */
+ /* size :: The current active size for this face. */
+ /* */
+ /* charmap :: The current active charmap for this face. */
+ /* */
+ /* */
+ /* Fields may be changed after a call to @FT_Attach_File or */
+ /* @FT_Attach_Stream. */
+ /* */
+ /* For an OpenType variation font, the values of the following fields */
+ /* can change after a call to @FT_Set_Var_Design_Coordinates (and */
+ /* friends) if the font contains an `MVAR' table: `ascender', */
+ /* `descender', `height', `underline_position', and */
+ /* `underline_thickness'. */
+ /* */
+ /* Especially for TrueType fonts see also the documentation for */
+ /* @FT_Size_Metrics. */
+ /* */
+ typedef struct FT_FaceRec_
+ {
+ FT_Long num_faces;
+ FT_Long face_index;
+
+ FT_Long face_flags;
+ FT_Long style_flags;
+
+ FT_Long num_glyphs;
+
+ FT_String* family_name;
+ FT_String* style_name;
+
+ FT_Int num_fixed_sizes;
+ FT_Bitmap_Size* available_sizes;
+
+ FT_Int num_charmaps;
+ FT_CharMap* charmaps;
+
+ FT_Generic generic;
+
+ /*# The following member variables (down to `underline_thickness') */
+ /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size */
+ /*# for bitmap fonts. */
+ FT_BBox bbox;
+
+ FT_UShort units_per_EM;
+ FT_Short ascender;
+ FT_Short descender;
+ FT_Short height;
+
+ FT_Short max_advance_width;
+ FT_Short max_advance_height;
+
+ FT_Short underline_position;
+ FT_Short underline_thickness;
+
+ FT_GlyphSlot glyph;
+ FT_Size size;
+ FT_CharMap charmap;
+
+ /*@private begin */
+
+ FT_Driver driver;
+ FT_Memory memory;
+ FT_Stream stream;
+
+ FT_ListRec sizes_list;
+
+ FT_Generic autohint; /* face-specific auto-hinter data */
+ void* extensions; /* unused */
+
+ FT_Face_Internal internal;
+
+ /*@private end */
+
+ } FT_FaceRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_FACE_FLAG_XXX */
+ /* */
+ /* */
+ /* A list of bit flags used in the `face_flags' field of the */
+ /* @FT_FaceRec structure. They inform client applications of */
+ /* properties of the corresponding face. */
+ /* */
+ /* */
+ /* FT_FACE_FLAG_SCALABLE :: */
+ /* The face contains outline glyphs. Note that a face can contain */
+ /* bitmap strikes also, i.e., a face can have both this flag and */
+ /* @FT_FACE_FLAG_FIXED_SIZES set. */
+ /* */
+ /* FT_FACE_FLAG_FIXED_SIZES :: */
+ /* The face contains bitmap strikes. See also the */
+ /* `num_fixed_sizes' and `available_sizes' fields of @FT_FaceRec. */
+ /* */
+ /* FT_FACE_FLAG_FIXED_WIDTH :: */
+ /* The face contains fixed-width characters (like Courier, Lucida, */
+ /* MonoType, etc.). */
+ /* */
+ /* FT_FACE_FLAG_SFNT :: */
+ /* The face uses the SFNT storage scheme. For now, this means */
+ /* TrueType and OpenType. */
+ /* */
+ /* FT_FACE_FLAG_HORIZONTAL :: */
+ /* The face contains horizontal glyph metrics. This should be set */
+ /* for all common formats. */
+ /* */
+ /* FT_FACE_FLAG_VERTICAL :: */
+ /* The face contains vertical glyph metrics. This is only */
+ /* available in some formats, not all of them. */
+ /* */
+ /* FT_FACE_FLAG_KERNING :: */
+ /* The face contains kerning information. If set, the kerning */
+ /* distance can be retrieved using the function @FT_Get_Kerning. */
+ /* Otherwise the function always return the vector (0,0). Note */
+ /* that FreeType doesn't handle kerning data from the SFNT `GPOS' */
+ /* table (as present in many OpenType fonts). */
+ /* */
+ /* FT_FACE_FLAG_FAST_GLYPHS :: */
+ /* THIS FLAG IS DEPRECATED. DO NOT USE OR TEST IT. */
+ /* */
+ /* FT_FACE_FLAG_MULTIPLE_MASTERS :: */
+ /* The face contains multiple masters and is capable of */
+ /* interpolating between them. Supported formats are Adobe MM, */
+ /* TrueType GX, and OpenType variation fonts. */
+ /* */
+ /* See the multiple-masters specific API for details. */
+ /* */
+ /* FT_FACE_FLAG_GLYPH_NAMES :: */
+ /* The face contains glyph names, which can be retrieved using */
+ /* @FT_Get_Glyph_Name. Note that some TrueType fonts contain */
+ /* broken glyph name tables. Use the function */
+ /* @FT_Has_PS_Glyph_Names when needed. */
+ /* */
+ /* FT_FACE_FLAG_EXTERNAL_STREAM :: */
+ /* Used internally by FreeType to indicate that a face's stream was */
+ /* provided by the client application and should not be destroyed */
+ /* when @FT_Done_Face is called. Don't read or test this flag. */
+ /* */
+ /* FT_FACE_FLAG_HINTER :: */
+ /* The font driver has a hinting machine of its own. For example, */
+ /* with TrueType fonts, it makes sense to use data from the SFNT */
+ /* `gasp' table only if the native TrueType hinting engine (with */
+ /* the bytecode interpreter) is available and active. */
+ /* */
+ /* FT_FACE_FLAG_CID_KEYED :: */
+ /* The face is CID-keyed. In that case, the face is not accessed */
+ /* by glyph indices but by CID values. For subsetted CID-keyed */
+ /* fonts this has the consequence that not all index values are a */
+ /* valid argument to @FT_Load_Glyph. Only the CID values for which */
+ /* corresponding glyphs in the subsetted font exist make */
+ /* `FT_Load_Glyph' return successfully; in all other cases you get */
+ /* an `FT_Err_Invalid_Argument' error. */
+ /* */
+ /* Note that CID-keyed fonts that are in an SFNT wrapper (this is, */
+ /* all OpenType/CFF fonts) don't have this flag set since the */
+ /* glyphs are accessed in the normal way (using contiguous */
+ /* indices); the `CID-ness' isn't visible to the application. */
+ /* */
+ /* FT_FACE_FLAG_TRICKY :: */
+ /* The face is `tricky', this is, it always needs the font format's */
+ /* native hinting engine to get a reasonable result. A typical */
+ /* example is the old Chinese font `mingli.ttf' (but not */
+ /* `mingliu.ttc') that uses TrueType bytecode instructions to move */
+ /* and scale all of its subglyphs. */
+ /* */
+ /* It is not possible to auto-hint such fonts using */
+ /* @FT_LOAD_FORCE_AUTOHINT; it will also ignore */
+ /* @FT_LOAD_NO_HINTING. You have to set both @FT_LOAD_NO_HINTING */
+ /* and @FT_LOAD_NO_AUTOHINT to really disable hinting; however, you */
+ /* probably never want this except for demonstration purposes. */
+ /* */
+ /* Currently, there are about a dozen TrueType fonts in the list of */
+ /* tricky fonts; they are hard-coded in file `ttobjs.c'. */
+ /* */
+ /* FT_FACE_FLAG_COLOR :: */
+ /* [Since 2.5.1] The face has color glyph tables. To access color */
+ /* glyphs use @FT_LOAD_COLOR. */
+ /* */
+ /* FT_FACE_FLAG_VARIATION :: */
+ /* [Since 2.9] Set if the current face (or named instance) has been */
+ /* altered with @FT_Set_MM_Design_Coordinates, */
+ /* @FT_Set_Var_Design_Coordinates, or */
+ /* @FT_Set_Var_Blend_Coordinates. This flag is unset by a call to */
+ /* @FT_Set_Named_Instance. */
+ /* */
+#define FT_FACE_FLAG_SCALABLE ( 1L << 0 )
+#define FT_FACE_FLAG_FIXED_SIZES ( 1L << 1 )
+#define FT_FACE_FLAG_FIXED_WIDTH ( 1L << 2 )
+#define FT_FACE_FLAG_SFNT ( 1L << 3 )
+#define FT_FACE_FLAG_HORIZONTAL ( 1L << 4 )
+#define FT_FACE_FLAG_VERTICAL ( 1L << 5 )
+#define FT_FACE_FLAG_KERNING ( 1L << 6 )
+#define FT_FACE_FLAG_FAST_GLYPHS ( 1L << 7 )
+#define FT_FACE_FLAG_MULTIPLE_MASTERS ( 1L << 8 )
+#define FT_FACE_FLAG_GLYPH_NAMES ( 1L << 9 )
+#define FT_FACE_FLAG_EXTERNAL_STREAM ( 1L << 10 )
+#define FT_FACE_FLAG_HINTER ( 1L << 11 )
+#define FT_FACE_FLAG_CID_KEYED ( 1L << 12 )
+#define FT_FACE_FLAG_TRICKY ( 1L << 13 )
+#define FT_FACE_FLAG_COLOR ( 1L << 14 )
+#define FT_FACE_FLAG_VARIATION ( 1L << 15 )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_HAS_HORIZONTAL( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains
+ * horizontal metrics (this is true for all font formats though).
+ *
+ * @also:
+ * @FT_HAS_VERTICAL can be used to check for vertical metrics.
+ *
+ */
+#define FT_HAS_HORIZONTAL( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_HORIZONTAL )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_HAS_VERTICAL( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains real
+ * vertical metrics (and not only synthesized ones).
+ *
+ */
+#define FT_HAS_VERTICAL( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_VERTICAL )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_HAS_KERNING( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains kerning
+ * data that can be accessed with @FT_Get_Kerning.
+ *
+ */
+#define FT_HAS_KERNING( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_KERNING )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_IS_SCALABLE( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains a scalable
+ * font face (true for TrueType, Type~1, Type~42, CID, OpenType/CFF,
+ * and PFR font formats).
+ *
+ */
+#define FT_IS_SCALABLE( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_SCALABLE )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_IS_SFNT( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains a font
+ * whose format is based on the SFNT storage scheme. This usually
+ * means: TrueType fonts, OpenType fonts, as well as SFNT-based embedded
+ * bitmap fonts.
+ *
+ * If this macro is true, all functions defined in @FT_SFNT_NAMES_H and
+ * @FT_TRUETYPE_TABLES_H are available.
+ *
+ */
+#define FT_IS_SFNT( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_SFNT )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_IS_FIXED_WIDTH( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains a font face
+ * that contains fixed-width (or `monospace', `fixed-pitch', etc.)
+ * glyphs.
+ *
+ */
+#define FT_IS_FIXED_WIDTH( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_FIXED_WIDTH )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_HAS_FIXED_SIZES( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains some
+ * embedded bitmaps. See the `available_sizes' field of the
+ * @FT_FaceRec structure.
+ *
+ */
+#define FT_HAS_FIXED_SIZES( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_FIXED_SIZES )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_HAS_FAST_GLYPHS( face )
+ *
+ * @description:
+ * Deprecated.
+ *
+ */
+#define FT_HAS_FAST_GLYPHS( face ) 0
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_HAS_GLYPH_NAMES( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains some glyph
+ * names that can be accessed through @FT_Get_Glyph_Name.
+ *
+ */
+#define FT_HAS_GLYPH_NAMES( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_GLYPH_NAMES )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_HAS_MULTIPLE_MASTERS( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains some
+ * multiple masters. The functions provided by @FT_MULTIPLE_MASTERS_H
+ * are then available to choose the exact design you want.
+ *
+ */
+#define FT_HAS_MULTIPLE_MASTERS( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_IS_NAMED_INSTANCE( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object is a named instance
+ * of a GX or OpenType variation font.
+ *
+ * [Since 2.9] Changing the design coordinates with
+ * @FT_Set_Var_Design_Coordinates or @FT_Set_Var_Blend_Coordinates does
+ * not influence the return value of this macro (only
+ * @FT_Set_Named_Instance does that).
+ *
+ * @since:
+ * 2.7
+ *
+ */
+#define FT_IS_NAMED_INSTANCE( face ) \
+ ( (face)->face_index & 0x7FFF0000L )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_IS_VARIATION( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object has been altered
+ * by @FT_Set_MM_Design_Coordinates, @FT_Set_Var_Design_Coordinates, or
+ * @FT_Set_Var_Blend_Coordinates.
+ *
+ * @since:
+ * 2.9
+ *
+ */
+#define FT_IS_VARIATION( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_VARIATION )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_IS_CID_KEYED( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains a CID-keyed
+ * font. See the discussion of @FT_FACE_FLAG_CID_KEYED for more
+ * details.
+ *
+ * If this macro is true, all functions defined in @FT_CID_H are
+ * available.
+ *
+ */
+#define FT_IS_CID_KEYED( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_CID_KEYED )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_IS_TRICKY( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face represents a `tricky' font.
+ * See the discussion of @FT_FACE_FLAG_TRICKY for more details.
+ *
+ */
+#define FT_IS_TRICKY( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_TRICKY )
+
+
+ /*************************************************************************
+ *
+ * @macro:
+ * FT_HAS_COLOR( face )
+ *
+ * @description:
+ * A macro that returns true whenever a face object contains
+ * tables for color glyphs.
+ *
+ * @since:
+ * 2.5.1
+ *
+ */
+#define FT_HAS_COLOR( face ) \
+ ( (face)->face_flags & FT_FACE_FLAG_COLOR )
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_STYLE_FLAG_XXX */
+ /* */
+ /* */
+ /* A list of bit flags to indicate the style of a given face. These */
+ /* are used in the `style_flags' field of @FT_FaceRec. */
+ /* */
+ /* */
+ /* FT_STYLE_FLAG_ITALIC :: */
+ /* The face style is italic or oblique. */
+ /* */
+ /* FT_STYLE_FLAG_BOLD :: */
+ /* The face is bold. */
+ /* */
+ /* */
+ /* The style information as provided by FreeType is very basic. More */
+ /* details are beyond the scope and should be done on a higher level */
+ /* (for example, by analyzing various fields of the `OS/2' table in */
+ /* SFNT based fonts). */
+ /* */
+#define FT_STYLE_FLAG_ITALIC ( 1 << 0 )
+#define FT_STYLE_FLAG_BOLD ( 1 << 1 )
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Size_Internal */
+ /* */
+ /* */
+ /* An opaque handle to an `FT_Size_InternalRec' structure, used to */
+ /* model private data of a given @FT_Size object. */
+ /* */
+ typedef struct FT_Size_InternalRec_* FT_Size_Internal;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Size_Metrics */
+ /* */
+ /* */
+ /* The size metrics structure gives the metrics of a size object. */
+ /* */
+ /* */
+ /* x_ppem :: The width of the scaled EM square in pixels, hence */
+ /* the term `ppem' (pixels per EM). It is also */
+ /* referred to as `nominal width'. */
+ /* */
+ /* y_ppem :: The height of the scaled EM square in pixels, */
+ /* hence the term `ppem' (pixels per EM). It is also */
+ /* referred to as `nominal height'. */
+ /* */
+ /* x_scale :: A 16.16 fractional scaling value to convert */
+ /* horizontal metrics from font units to 26.6 */
+ /* fractional pixels. Only relevant for scalable */
+ /* font formats. */
+ /* */
+ /* y_scale :: A 16.16 fractional scaling value to convert */
+ /* vertical metrics from font units to 26.6 */
+ /* fractional pixels. Only relevant for scalable */
+ /* font formats. */
+ /* */
+ /* ascender :: The ascender in 26.6 fractional pixels, rounded up */
+ /* to an integer value. See @FT_FaceRec for the */
+ /* details. */
+ /* */
+ /* descender :: The descender in 26.6 fractional pixels, rounded */
+ /* down to an integer value. See @FT_FaceRec for the */
+ /* details. */
+ /* */
+ /* height :: The height in 26.6 fractional pixels, rounded to */
+ /* an integer value. See @FT_FaceRec for the */
+ /* details. */
+ /* */
+ /* max_advance :: The maximum advance width in 26.6 fractional */
+ /* pixels, rounded to an integer value. See */
+ /* @FT_FaceRec for the details. */
+ /* */
+ /* */
+ /* The scaling values, if relevant, are determined first during a */
+ /* size changing operation. The remaining fields are then set by the */
+ /* driver. For scalable formats, they are usually set to scaled */
+ /* values of the corresponding fields in @FT_FaceRec. Some values */
+ /* like ascender or descender are rounded for historical reasons; */
+ /* more precise values (for outline fonts) can be derived by scaling */
+ /* the corresponding @FT_FaceRec values manually, with code similar */
+ /* to the following. */
+ /* */
+ /* { */
+ /* scaled_ascender = FT_MulFix( face->ascender, */
+ /* size_metrics->y_scale ); */
+ /* } */
+ /* */
+ /* Note that due to glyph hinting and the selected rendering mode */
+ /* these values are usually not exact; consequently, they must be */
+ /* treated as unreliable with an error margin of at least one pixel! */
+ /* */
+ /* Indeed, the only way to get the exact metrics is to render _all_ */
+ /* glyphs. As this would be a definite performance hit, it is up to */
+ /* client applications to perform such computations. */
+ /* */
+ /* The `FT_Size_Metrics' structure is valid for bitmap fonts also. */
+ /* */
+ /* */
+ /* *TrueType* *fonts* *with* *native* *bytecode* *hinting* */
+ /* */
+ /* All applications that handle TrueType fonts with native hinting */
+ /* must be aware that TTFs expect different rounding of vertical font */
+ /* dimensions. The application has to cater for this, especially if */
+ /* it wants to rely on a TTF's vertical data (for example, to */
+ /* properly align box characters vertically). */
+ /* */
+ /* Only the application knows _in_ _advance_ that it is going to use */
+ /* native hinting for TTFs! FreeType, on the other hand, selects the */
+ /* hinting mode not at the time of creating an @FT_Size object but */
+ /* much later, namely while calling @FT_Load_Glyph. */
+ /* */
+ /* Here is some pseudo code that illustrates a possible solution. */
+ /* */
+ /* { */
+ /* font_format = FT_Get_Font_Format( face ); */
+ /* */
+ /* if ( !strcmp( font_format, "TrueType" ) && */
+ /* do_native_bytecode_hinting ) */
+ /* { */
+ /* ascender = ROUND( FT_MulFix( face->ascender, */
+ /* size_metrics->y_scale ) ); */
+ /* descender = ROUND( FT_MulFix( face->descender, */
+ /* size_metrics->y_scale ) ); */
+ /* } */
+ /* else */
+ /* { */
+ /* ascender = size_metrics->ascender; */
+ /* descender = size_metrics->descender; */
+ /* } */
+ /* */
+ /* height = size_metrics->height; */
+ /* max_advance = size_metrics->max_advance; */
+ /* } */
+ /* */
+ typedef struct FT_Size_Metrics_
+ {
+ FT_UShort x_ppem; /* horizontal pixels per EM */
+ FT_UShort y_ppem; /* vertical pixels per EM */
+
+ FT_Fixed x_scale; /* scaling values used to convert font */
+ FT_Fixed y_scale; /* units to 26.6 fractional pixels */
+
+ FT_Pos ascender; /* ascender in 26.6 frac. pixels */
+ FT_Pos descender; /* descender in 26.6 frac. pixels */
+ FT_Pos height; /* text height in 26.6 frac. pixels */
+ FT_Pos max_advance; /* max horizontal advance, in 26.6 pixels */
+
+ } FT_Size_Metrics;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_SizeRec */
+ /* */
+ /* */
+ /* FreeType root size class structure. A size object models a face */
+ /* object at a given size. */
+ /* */
+ /* */
+ /* face :: Handle to the parent face object. */
+ /* */
+ /* generic :: A typeless pointer, unused by the FreeType library or */
+ /* any of its drivers. It can be used by client */
+ /* applications to link their own data to each size */
+ /* object. */
+ /* */
+ /* metrics :: Metrics for this size object. This field is read-only. */
+ /* */
+ typedef struct FT_SizeRec_
+ {
+ FT_Face face; /* parent face object */
+ FT_Generic generic; /* generic pointer for client uses */
+ FT_Size_Metrics metrics; /* size metrics */
+ FT_Size_Internal internal;
+
+ } FT_SizeRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_SubGlyph */
+ /* */
+ /* */
+ /* The subglyph structure is an internal object used to describe */
+ /* subglyphs (for example, in the case of composites). */
+ /* */
+ /* */
+ /* The subglyph implementation is not part of the high-level API, */
+ /* hence the forward structure declaration. */
+ /* */
+ /* You can however retrieve subglyph information with */
+ /* @FT_Get_SubGlyph_Info. */
+ /* */
+ typedef struct FT_SubGlyphRec_* FT_SubGlyph;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Slot_Internal */
+ /* */
+ /* */
+ /* An opaque handle to an `FT_Slot_InternalRec' structure, used to */
+ /* model private data of a given @FT_GlyphSlot object. */
+ /* */
+ typedef struct FT_Slot_InternalRec_* FT_Slot_Internal;
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_GlyphSlotRec */
+ /* */
+ /* */
+ /* FreeType root glyph slot class structure. A glyph slot is a */
+ /* container where individual glyphs can be loaded, be they in */
+ /* outline or bitmap format. */
+ /* */
+ /* */
+ /* library :: A handle to the FreeType library instance */
+ /* this slot belongs to. */
+ /* */
+ /* face :: A handle to the parent face object. */
+ /* */
+ /* next :: In some cases (like some font tools), several */
+ /* glyph slots per face object can be a good */
+ /* thing. As this is rare, the glyph slots are */
+ /* listed through a direct, single-linked list */
+ /* using its `next' field. */
+ /* */
+ /* generic :: A typeless pointer unused by the FreeType */
+ /* library or any of its drivers. It can be */
+ /* used by client applications to link their own */
+ /* data to each glyph slot object. */
+ /* */
+ /* metrics :: The metrics of the last loaded glyph in the */
+ /* slot. The returned values depend on the last */
+ /* load flags (see the @FT_Load_Glyph API */
+ /* function) and can be expressed either in 26.6 */
+ /* fractional pixels or font units. */
+ /* */
+ /* Note that even when the glyph image is */
+ /* transformed, the metrics are not. */
+ /* */
+ /* linearHoriAdvance :: The advance width of the unhinted glyph. */
+ /* Its value is expressed in 16.16 fractional */
+ /* pixels, unless @FT_LOAD_LINEAR_DESIGN is set */
+ /* when loading the glyph. This field can be */
+ /* important to perform correct WYSIWYG layout. */
+ /* Only relevant for outline glyphs. */
+ /* */
+ /* linearVertAdvance :: The advance height of the unhinted glyph. */
+ /* Its value is expressed in 16.16 fractional */
+ /* pixels, unless @FT_LOAD_LINEAR_DESIGN is set */
+ /* when loading the glyph. This field can be */
+ /* important to perform correct WYSIWYG layout. */
+ /* Only relevant for outline glyphs. */
+ /* */
+ /* advance :: This shorthand is, depending on */
+ /* @FT_LOAD_IGNORE_TRANSFORM, the transformed */
+ /* (hinted) advance width for the glyph, in 26.6 */
+ /* fractional pixel format. As specified with */
+ /* @FT_LOAD_VERTICAL_LAYOUT, it uses either the */
+ /* `horiAdvance' or the `vertAdvance' value of */
+ /* `metrics' field. */
+ /* */
+ /* format :: This field indicates the format of the image */
+ /* contained in the glyph slot. Typically */
+ /* @FT_GLYPH_FORMAT_BITMAP, */
+ /* @FT_GLYPH_FORMAT_OUTLINE, or */
+ /* @FT_GLYPH_FORMAT_COMPOSITE, but other values */
+ /* are possible. */
+ /* */
+ /* bitmap :: This field is used as a bitmap descriptor. */
+ /* Note that the address and content of the */
+ /* bitmap buffer can change between calls of */
+ /* @FT_Load_Glyph and a few other functions. */
+ /* */
+ /* bitmap_left :: The bitmap's left bearing expressed in */
+ /* integer pixels. */
+ /* */
+ /* bitmap_top :: The bitmap's top bearing expressed in integer */
+ /* pixels. This is the distance from the */
+ /* baseline to the top-most glyph scanline, */
+ /* upwards y~coordinates being *positive*. */
+ /* */
+ /* outline :: The outline descriptor for the current glyph */
+ /* image if its format is */
+ /* @FT_GLYPH_FORMAT_OUTLINE. Once a glyph is */
+ /* loaded, `outline' can be transformed, */
+ /* distorted, emboldened, etc. However, it must */
+ /* not be freed. */
+ /* */
+ /* num_subglyphs :: The number of subglyphs in a composite glyph. */
+ /* This field is only valid for the composite */
+ /* glyph format that should normally only be */
+ /* loaded with the @FT_LOAD_NO_RECURSE flag. */
+ /* */
+ /* subglyphs :: An array of subglyph descriptors for */
+ /* composite glyphs. There are `num_subglyphs' */
+ /* elements in there. Currently internal to */
+ /* FreeType. */
+ /* */
+ /* control_data :: Certain font drivers can also return the */
+ /* control data for a given glyph image (e.g. */
+ /* TrueType bytecode, Type~1 charstrings, etc.). */
+ /* This field is a pointer to such data; it is */
+ /* currently internal to FreeType. */
+ /* */
+ /* control_len :: This is the length in bytes of the control */
+ /* data. Currently internal to FreeType. */
+ /* */
+ /* other :: Reserved. */
+ /* */
+ /* lsb_delta :: The difference between hinted and unhinted */
+ /* left side bearing while auto-hinting is */
+ /* active. Zero otherwise. */
+ /* */
+ /* rsb_delta :: The difference between hinted and unhinted */
+ /* right side bearing while auto-hinting is */
+ /* active. Zero otherwise. */
+ /* */
+ /* */
+ /* If @FT_Load_Glyph is called with default flags (see */
+ /* @FT_LOAD_DEFAULT) the glyph image is loaded in the glyph slot in */
+ /* its native format (e.g., an outline glyph for TrueType and Type~1 */
+ /* formats). [Since 2.9] The prospective bitmap metrics are */
+ /* calculated according to @FT_LOAD_TARGET_XXX and other flags even */
+ /* for the outline glyph, even if @FT_LOAD_RENDER is not set. */
+ /* */
+ /* This image can later be converted into a bitmap by calling */
+ /* @FT_Render_Glyph. This function searches the current renderer for */
+ /* the native image's format, then invokes it. */
+ /* */
+ /* The renderer is in charge of transforming the native image through */
+ /* the slot's face transformation fields, then converting it into a */
+ /* bitmap that is returned in `slot->bitmap'. */
+ /* */
+ /* Note that `slot->bitmap_left' and `slot->bitmap_top' are also used */
+ /* to specify the position of the bitmap relative to the current pen */
+ /* position (e.g., coordinates (0,0) on the baseline). Of course, */
+ /* `slot->format' is also changed to @FT_GLYPH_FORMAT_BITMAP. */
+ /* */
+ /* Here is a small pseudo code fragment that shows how to use */
+ /* `lsb_delta' and `rsb_delta' to do fractional positioning of */
+ /* glyphs: */
+ /* */
+ /* { */
+ /* FT_GlyphSlot slot = face->glyph; */
+ /* FT_Pos origin_x = 0; */
+ /* */
+ /* */
+ /* for all glyphs do */
+ /* */
+ /* */
+ /* FT_Outline_Translate( slot->outline, origin_x & 63, 0 ); */
+ /* */
+ /* */
+ /* */
+ /* */
+ /* */
+ /* origin_x += slot->advance.x; */
+ /* origin_x += slot->rsb_delta - slot->lsb_delta; */
+ /* endfor */
+ /* } */
+ /* */
+ /* Here is another small pseudo code fragment that shows how to use */
+ /* `lsb_delta' and `rsb_delta' to improve integer positioning of */
+ /* glyphs: */
+ /* */
+ /* { */
+ /* FT_GlyphSlot slot = face->glyph; */
+ /* FT_Pos origin_x = 0; */
+ /* FT_Pos prev_rsb_delta = 0; */
+ /* */
+ /* */
+ /* for all glyphs do */
+ /* */
+ /* */
+ /* */
+ /* */
+ /* if ( prev_rsb_delta - slot->lsb_delta > 32 ) */
+ /* origin_x -= 64; */
+ /* else if ( prev_rsb_delta - slot->lsb_delta < -31 ) */
+ /* origin_x += 64; */
+ /* */
+ /* prev_rsb_delta = slot->rsb_delta; */
+ /* */
+ /* */
+ /* */
+ /* origin_x += slot->advance.x; */
+ /* endfor */
+ /* } */
+ /* */
+ /* If you use strong auto-hinting, you *must* apply these delta */
+ /* values! Otherwise you will experience far too large inter-glyph */
+ /* spacing at small rendering sizes in most cases. Note that it */
+ /* doesn't harm to use the above code for other hinting modes also, */
+ /* since the delta values are zero then. */
+ /* */
+ typedef struct FT_GlyphSlotRec_
+ {
+ FT_Library library;
+ FT_Face face;
+ FT_GlyphSlot next;
+ FT_UInt reserved; /* retained for binary compatibility */
+ FT_Generic generic;
+
+ FT_Glyph_Metrics metrics;
+ FT_Fixed linearHoriAdvance;
+ FT_Fixed linearVertAdvance;
+ FT_Vector advance;
+
+ FT_Glyph_Format format;
+
+ FT_Bitmap bitmap;
+ FT_Int bitmap_left;
+ FT_Int bitmap_top;
+
+ FT_Outline outline;
+
+ FT_UInt num_subglyphs;
+ FT_SubGlyph subglyphs;
+
+ void* control_data;
+ long control_len;
+
+ FT_Pos lsb_delta;
+ FT_Pos rsb_delta;
+
+ void* other;
+
+ FT_Slot_Internal internal;
+
+ } FT_GlyphSlotRec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /* */
+ /* F U N C T I O N S */
+ /* */
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* */
+ /* FT_Init_FreeType */
+ /* */
+ /* */
+ /* Initialize a new FreeType library object. The set of modules */
+ /* that are registered by this function is determined at build time. */
+ /* */
+ /*