Compare commits

...

12 Commits

Author SHA1 Message Date
af0a39166c Fix assertion logging failure and rendering pipeline validation errors. 2026-05-19 16:46:43 +02:00
8ea6a0bf3e Remove tiling and LRU caching, add circles basic shape to improve overall performances. 2026-05-18 16:58:30 +02:00
dc708de354 Add cache handling for tiles 2026-05-11 16:48:34 +02:00
beea8a0281 Tiling system for SDF multi resolution 2026-05-07 20:55:44 +02:00
936043340c Start testing the SDF generation 2026-05-06 16:56:48 +02:00
9789e449c3 Rebuild from scratch with a new strategy using compute shaders and SDF. 2026-05-06 16:27:41 +02:00
7e3da1c424 refactor: eliminate globals, add pen tool + edit mode, frustum culling
Remove all file-scope mutable state so the codebase compiles cleanly
as a single translation unit. State moved into structs owned by
userdata_t:

- group_index_ctx_t replaces g_group_by_id/g_group_by_id_cap
- shape_pool_ctx_t replaces g_shape_pool_dirty/g_shape_data_dirty
  and g_shape_groups/g_shape_groups_count
- panel_log_ctx_t wired through all subsystems explicitly
- pipeline_ctx_t owns render pipeline handles
- overlay_upload_state_t (per-buffer flags) replaces single bool

New features piggybacking on the refactor:

- Pen tool: click-to-place anchors, Catmull-Rom preview, finalize
  into Bezier shapes with control points
- Edit mode: anchor/handle hit testing, dragging, pre-drag undo
  snapshots, dedicated GPU buffers for edit overlays
- Frustum culling: spatial-grid-based viewport cull in draw_shapes
  with linear-scan fallback for oversized viewports
- Log dedup: FNV-1a 64-bit hash to skip duplicate messages
- Buffer shrink: halve draw buffers after 60 frames of low usage
- Shape geometry hashing for instanced-draw vertex-buffer grouping
- Group member_indices arrays with O(n) rebuild
- Log ring expanded 64→256 entries, added log_filter

Debug build: added --profiling-funcs and -sASSERTIONS flags.
2026-05-03 00:38:45 +02:00
c4d657043c Add copy/paste, rewrite rendering pipeline with instanced draw calls and add a lot of caching to minimize overhead on huge edits. 2026-05-01 00:10:19 +02:00
e71641c094 Add a geometry pool, shape hierarchy, addittion and deletion. 2026-04-30 17:55:46 +02:00
9ce6e4accd Reorganized the code structure, fix a lot of issues. 2026-04-28 21:05:43 +02:00
81616f8a5d History, spatial grid and optimizations 2026-04-28 19:02:00 +02:00
5881a7dafc Add shapes and basic selective actions 2026-04-27 18:26:02 +02:00
19 changed files with 2028 additions and 886 deletions

View File

@@ -1,5 +1,39 @@
# CLAUDE.md
## Project: Cartograph
A browser-based world map creation tool (like Wonderdraft/Inkarnate). C99 compiled to WebAssembly via Emscripten.
### Stack
- **Graphics:** Sokol (WebGPU backend, `SOKOL_WGPU`) — `lib/sokol/`
- **UI:** Dear ImGui via cimgui — `lib/imgui/`
- **Math:** cglm (types are C arrays: `vec2` = `float[2]`, `mat4` = `float[4][4]` column-major) — `lib/cglm/`
- **Shaders:** WGSL in `src/shaders/`, compiled to C headers via `xxd -i` into `src/generated/`
- **Shapes:** Line-strip based vector shapes (circle, star) with procedural vertex generation
### Build
- `make` (release) / `make debug` — outputs `app.html`
- All includes go through `src/api.h` which defines `SOKOL_IMPL`, `SOKOL_WGPU`, and pulls in every library header
- Include paths: `lib/sokol`, `lib/imgui`, `lib/imgui/imgui`, `lib/util`, `lib/cglm/include`
### Key files
- `src/main.c` — entry point, sokol init, render loop, all input handling, overlay geometry, UI panels, and debug stats
- `src/api.h` — central include hub, backend defines, ALLOC/FREE macros (wired to smemtrack)
- `src/camera.h` — viewport state (zoom/pan), MVP matrix computation, screen↔world coordinate transforms
- `src/render.h` — shape pipeline init/shutdown, per-shape draw calls (shader uniform binding)
- `src/shape.h` — shape geometry types, procedural generation (circle/star), transform building, hit testing, buffer management
- `src/spatial.h` — spatial hash grid for accelerating hit tests and rect-selection queries
- `src/history.h` — undo/redo stack with property-level tracking (position/scale/rotation/color), edit session capture, batch operations
- `src/util.h``vector_t` (dynamic array) and `mem_pool_t` (free-list pool), both stripe-based
- `src/rand.h` — xorshift32 PRNG
### Conventions
- No malloc/free directly — use `ALLOC`/`FREE` macros (wired to smemtrack in main.c)
- Assert is encouraged for invariant checks
- Data structures use stripe-based allocation (byte stride per element, not sizeof)
---
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding

22
README.md Normal file
View File

@@ -0,0 +1,22 @@
# Cartograph
A browser-based world map creation tool inspired by Wonderdraft and Inkarnate. Uses a hierarchical tile-based SDF rendering.
## Build
```sh
# Install dependencies
./fetch_libs.sh
# Release build
make
# Debug build
make debug
```
Output is `app.html`, served by Emscripten's built-in web server or any static server.
## License
MIT

View File

@@ -5,11 +5,12 @@ LIB_DIR="lib"
echo "=== Fetching library dependencies ==="
# 1. Sokol (single-file headers)
mkdir -p "$LIB_DIR/sokol"
mkdir -p "$LIB_DIR/imgui"
mkdir -p "$LIB_DIR/util"
mkdir -p "$LIB_DIR/cglm"
# 1. Sokol (single-file headers)
if [ ! -f "$LIB_DIR/sokol/sokol_gfx.h" ]; then
echo " > Fetching sokol (pinned to emdawnwebgpu-compatible version)..."
git clone --depth 500 https://github.com/floooh/sokol.git "$LIB_DIR/sokol_tmp"
@@ -47,4 +48,25 @@ else
echo " > cimgui already present"
fi
# 4. cglm
if [ ! -f "$LIB_DIR/cglm/include/cglm/cglm.h" ]; then
echo " > Fetching cglm..."
git clone --depth 1 --branch v0.9.6 https://github.com/recp/cglm.git "$LIB_DIR/cglm_tmp"
cp -r "$LIB_DIR/cglm_tmp/include" "$LIB_DIR/cglm/"
cp -r "$LIB_DIR/cglm_tmp/src" "$LIB_DIR/cglm/"
rm -rf "$LIB_DIR/cglm_tmp"
else
echo " > cglm already present"
fi
# 5. stb_ds.h
if [ ! -f "$LIB_DIR/util/stb_ds.h" ]; then
echo " > Fetching STB..."
git clone --depth 1 https://github.com/nothings/stb.git "$LIB_DIR/stb_tmp"
cp -r "$LIB_DIR/stb_tmp/stb_ds.h" "$LIB_DIR/util/stb_ds.h"
rm -rf "$LIB_DIR/stb_tmp"
else
echo " > stb_ds.h already present"
fi
echo "=== Done ==="

View File

@@ -19,6 +19,7 @@ IMGUI_SOURCES = $(LIB_DIR)/imgui/imgui/imgui.cpp \
$(LIB_DIR)/imgui/imgui/imgui_tables.cpp \
$(LIB_DIR)/imgui/imgui/imgui_widgets.cpp \
$(LIB_DIR)/imgui/cimgui.cpp
CGLM_SOURCES = $(wildcard $(LIB_DIR)/cglm/src/*.c)
# Dynamic shader processing
SHADER_FILES = $(wildcard $(SHADER_DIR)/*.wgsl)
@@ -29,7 +30,9 @@ EMCC_FLAGS = --use-port=emdawnwebgpu \
-sWASM_BIGINT \
-sALLOW_MEMORY_GROWTH \
-msimd128 \
-sFILESYSTEM=0
-sFILESYSTEM=0 \
-sMALLOC=emmalloc \
-flto
# Shell template
SHELL_FILE = shell.html
@@ -38,15 +41,17 @@ SHELL_FILE = shell.html
all: $(FETCH) $(TARGET)
# Main build target
$(TARGET): $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(SHELL_FILE)
$(CC) $(C_SOURCES) $(IMGUI_SOURCES) \
$(TARGET): $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) $(SHELL_FILE)
$(CC) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) \
-o $(TARGET) \
$(EMCC_FLAGS) \
-O3 \
--closure 1 \
-I$(LIB_DIR)/sokol \
-I$(LIB_DIR)/imgui \
-I$(LIB_DIR)/imgui/imgui \
-I$(LIB_DIR)/util \
-I$(LIB_DIR)/cglm/include \
--shell-file=$(SHELL_FILE)
# Shader header generation
@@ -58,15 +63,17 @@ $(GENERATED_DIR)/%.h: $(SHADER_DIR)/%.wgsl | $(GENERATED_DIR)
$(GENERATED_DIR):
mkdir -p $(GENERATED_DIR)
debug: $(FETCH) $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(SHELL_FILE)
$(CC) $(C_SOURCES) $(IMGUI_SOURCES) \
debug: $(FETCH) $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) $(SHELL_FILE)
$(CC) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) \
-o $(TARGET) \
$(EMCC_FLAGS) \
-g -gsource-map=inline \
-g3 --profiling-funcs -gsource-map \
-sASSERTIONS \
-I$(LIB_DIR)/sokol \
-I$(LIB_DIR)/imgui \
-I$(LIB_DIR)/imgui/imgui \
-I$(LIB_DIR)/util \
-I$(LIB_DIR)/cglm/include \
--shell-file=$(SHELL_FILE)
# Clean build artifacts

View File

@@ -1,30 +1,59 @@
#ifndef API_DEFINITION
#define API_DEFINITION
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
#define SOKOL_IMPL
#define SOKOL_WGPU
#define SOKOL_IMGUI_IMPL
#include "sokol_gfx.h"
#include "sokol_app.h"
#include "sokol_glue.h"
#include "sokol_log.h"
#include "cimgui.h"
#include "sokol_imgui.h"
#include "sokol_memtrack.h"
#include "math.h"
#include "rand.h"
#include "util.h"
#include "sprite.h"
#include "generated/sprite.h"
#include <emscripten.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdarg.h>
#include <emscripten/emmalloc.h>
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
static void log_fn(const char* tag, uint32_t log_level, uint32_t log_item_id, const char* message_or_null, uint32_t line_nr, const char* filename_or_null, void* user_data)
{
fprintf(stderr, "[%s - %s]: (%d) %s \tat %s:%d\r\n", tag, log_level == 0 ? "FATAL" : log_level == 1 ? "ERROR" : log_level == 2 ? "WARNING" : "INFO", log_item_id, message_or_null, filename_or_null, line_nr);
}
#define SOKOL_ASSERT(x) ((void)((x) || (log_fn("ASSERT", 1, 1, "Assertion failed", __LINE__, __FILE__, NULL),0)))
#define SOKOL_IMPL
#define SOKOL_WGPU
#define SOKOL_IMGUI_IMPL
#define STB_DS_IMPLEMENTATION
#define STBDS_NO_SHORT_NAMES
#define STBDS_REALLOC(context,ptr,size) emmalloc_realloc(ptr, size)
#define STBDS_FREE(context,ptr) emmalloc_free(ptr)
#define COMPUTE_VIEWIDX_segments 0
#define COMPUTE_VIEWIDX_shapes 1
#define COMPUTE_VIEWIDX_circles 2
#define COMPUTE_VIEWIDX_tiles 3
#define COMPUTE_VIEWIDX_indices 4
#define COMPUTE_VIEWIDX_SDF 5
#define DISPLAY_VIEWIDX_SDF 0
#define DISPLAY_VIEWIDX_Sampler 1
static float clampf(float x, float a, float b)
{
return x < a ? a : (x > b ? b : x);
}
#include "sokol_gfx.h"
#include "sokol_app.h"
#include "sokol_glue.h"
#include "cimgui.h"
#include "sokol_imgui.h"
#include "stb_ds.h"
#include "shape.h"
//#include "cache.h"
#include "pool.h"
#include "generated/compute.h"
#include "generated/display.h"
#endif

107
src/cache.h Normal file
View File

@@ -0,0 +1,107 @@
#include "api.h"
tile_slot_t* cache_evict(scene_t* s)
{
tile_slot_t* best = NULL;
uint64_t oldest = UINT64_MAX;
for(uint32_t i = 0; i < TILE_LAYERS; i++)
{
tile_slot_t* slot = &s->cache.slots[i];
if(slot->key.lod == UINT32_MAX) continue; // LOD == UINT32_MAX means the slot is free.
//Found a better candidate
if(slot->last_used < oldest)
{
oldest = slot->last_used;
best = slot;
}
}
if(best == NULL)
return best;
stbds_hmdel(s->cache.map, best->key);
s->cache.free_layers[s->cache.free_count] = best->layer;
s->cache.free_count++;
s->cache.slots[best->layer].key.lod = UINT32_MAX; //Mark the slot as free for the evict scan using lod == UINT32_MAX
return best;
}
uint32_t cache_allocate(scene_t* s)
{
assert(s->cache.free_count > 0);
s->cache.free_count--;
uint32_t layer = s->cache.free_layers[s->cache.free_count];
return layer;
}
tile_slot_t* cache_search(scene_t* s, tile_key_t* key, uint64_t frame_count)
{
uint32_t index = stbds_hmgeti(s->cache.map, *key);
if(index >= 0)
{
uint32_t layer = s->cache.map[index].value;
tile_slot_t* slot = &s->cache.slots[layer];
slot->last_used = frame_count;
return slot;
}
// Evict the least recently used (LRU) slot
if(stbds_hmlen(s->cache.map) >= TILE_LAYERS)
{
tile_slot_t* evict = cache_evict(s);
}
// Allocate a free layer
uint32_t layer = cache_allocate(s);
tile_slot_t* slot = &s->cache.slots[layer];
slot->key.lod = key->lod; slot->key.tx = key->tx; slot->key.ty = key->ty;
slot->layer = layer;
slot->state = TILE_STATE_DIRTY;
slot->last_used = frame_count;
stbds_hmput(s->cache.map, *key, layer);
return slot;
}
// Works in 3 steps, first we select every required tiles and store the mising one from the cache
// Then we evict the LRU cache until we have enough space for the missing tiles
// Finally we allocate the missing tiles
static int cache_query(scene_t* s, uint32_t lod, box_t* box, tile_key_t** buffer)
{
uint64_t frame = sapp_frame_count();
tile_key_t tile_key = { lod, 0, 0 };
for(uint32_t ty = box->min_y; ty < box->max_y; ty++)
{
tile_key.ty = ty;
for(uint32_t tx = box->min_x; tx < box->max_x; tx++)
{
tile_key.tx = tx;
stbds_hmget(s->cache.map, tile_key);
}
}
}
static int cache_cull(scene_t* s, uint32_t tile_count)
{
uint32_t count = 0;
for(uint32_t i = 0; i < tile_count; i++)
{
tile_slot_t slot = s->cache.slots[i];
tile_task_t* tile = &s->cache.tiles[i];
tile->bounds.min_x = slot.key.tx * s->LODs[slot.key.lod].texel_size;
tile->bounds.min_y = slot.key.ty * s->LODs[slot.key.lod].texel_size;
tile->bounds.max_x = (slot.key.tx + 1) * s->LODs[slot.key.lod].texel_size;
tile->bounds.max_y = (slot.key.ty + 1) * s->LODs[slot.key.lod].texel_size;
tile->layer = slot.layer;
uint32_t start = count;
for(uint32_t j = 0; j < s->num_shapes; j++)
{
if(BOX_INTERSECTS(tile->bounds, s->shapes[j].aabb)) s->cache.indices[count++] = j;
}
tile->offset = start; tile->count = count - start;
}
}

686
src/generated/compute.h Normal file
View File

@@ -0,0 +1,686 @@
unsigned char src_shaders_compute_wgsl[] = {
0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x53, 0x65, 0x67, 0x6d, 0x65,
0x6e, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x30,
0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x70, 0x31, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x32, 0x3a, 0x20, 0x76, 0x65,
0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x33,
0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a, 0x7d, 0x0d,
0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x53, 0x68, 0x61, 0x70,
0x65, 0x4d, 0x65, 0x74, 0x61, 0x20, 0x7b, 0x20, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x67, 0x6d,
0x65, 0x6e, 0x74, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, 0x20, 0x2f, 0x2f,
0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68,
0x65, 0x20, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x62,
0x75, 0x66, 0x66, 0x65, 0x72, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73,
0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x62, 0x62, 0x6f, 0x78, 0x5f, 0x6d, 0x69, 0x6e, 0x3a, 0x20, 0x76, 0x65,
0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x62,
0x6f, 0x78, 0x5f, 0x6d, 0x61, 0x78, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32,
0x66, 0x2c, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63,
0x74, 0x20, 0x43, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x20, 0x7b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3a, 0x20,
0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x2c,
0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20,
0x54, 0x69, 0x6c, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x77, 0x6f, 0x72, 0x6c, 0x64, 0x5f, 0x6d, 0x69, 0x6e, 0x3a, 0x20, 0x76,
0x65, 0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77,
0x6f, 0x72, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x3a, 0x20, 0x76, 0x65,
0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x66,
0x66, 0x73, 0x65, 0x74, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x5f, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3a, 0x20, 0x75,
0x33, 0x32, 0x2c, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x40,
0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x31, 0x29, 0x20, 0x40, 0x62, 0x69,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x30, 0x29, 0x20, 0x76, 0x61, 0x72,
0x3c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3e, 0x20, 0x73, 0x65,
0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61,
0x79, 0x3c, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x3e, 0x3b, 0x0d,
0x0a, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x31, 0x29, 0x20, 0x40,
0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x31, 0x29, 0x20, 0x76,
0x61, 0x72, 0x3c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3e, 0x20,
0x73, 0x68, 0x61, 0x70, 0x65, 0x73, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61,
0x79, 0x3c, 0x53, 0x68, 0x61, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x3e,
0x3b, 0x0d, 0x0a, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x31, 0x29,
0x20, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x32, 0x29,
0x20, 0x76, 0x61, 0x72, 0x3c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x3e, 0x20, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x73, 0x3a, 0x20, 0x61,
0x72, 0x72, 0x61, 0x79, 0x3c, 0x43, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x3e,
0x3b, 0x0d, 0x0a, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x31, 0x29,
0x20, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x33, 0x29,
0x20, 0x76, 0x61, 0x72, 0x3c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x3e, 0x20, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x3a, 0x20, 0x61, 0x72, 0x72,
0x61, 0x79, 0x3c, 0x54, 0x69, 0x6c, 0x65, 0x3e, 0x3b, 0x0d, 0x0a, 0x40,
0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x31, 0x29, 0x20, 0x40, 0x62, 0x69,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x34, 0x29, 0x20, 0x76, 0x61, 0x72,
0x3c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3e, 0x20, 0x69, 0x6e,
0x64, 0x69, 0x63, 0x65, 0x73, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79,
0x3c, 0x75, 0x33, 0x32, 0x3e, 0x3b, 0x0d, 0x0a, 0x40, 0x67, 0x72, 0x6f,
0x75, 0x70, 0x28, 0x31, 0x29, 0x20, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69,
0x6e, 0x67, 0x28, 0x35, 0x29, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x64,
0x66, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3a, 0x20, 0x74, 0x65,
0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x65, 0x5f, 0x32, 0x64, 0x3c, 0x72, 0x31, 0x36, 0x66, 0x6c, 0x6f, 0x61,
0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x3b, 0x0d, 0x0a,
0x0d, 0x0a, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x20, 0x54,
0x49, 0x4c, 0x45, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x3a, 0x20, 0x75, 0x33,
0x32, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x36, 0x75, 0x3b, 0x0d, 0x0a, 0x0d,
0x0a, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
0x2d, 0x2d, 0x20, 0x43, 0x75, 0x62, 0x69, 0x63, 0x20, 0x42, 0xc3, 0xa9,
0x7a, 0x69, 0x65, 0x72, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x73,
0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0d,
0x0a, 0x66, 0x6e, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x62,
0x69, 0x63, 0x28, 0x74, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x2c, 0x20, 0x70,
0x30, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x20, 0x70, 0x31,
0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x20, 0x70, 0x32, 0x3a,
0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x20, 0x70, 0x33, 0x3a, 0x20,
0x76, 0x65, 0x63, 0x32, 0x66, 0x29, 0x20, 0x2d, 0x3e, 0x20, 0x76, 0x65,
0x63, 0x32, 0x66, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x65, 0x74, 0x20, 0x6d, 0x74, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20,
0x2d, 0x20, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
0x74, 0x20, 0x6d, 0x74, 0x32, 0x20, 0x3d, 0x20, 0x6d, 0x74, 0x20, 0x2a,
0x20, 0x6d, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
0x74, 0x20, 0x74, 0x32, 0x20, 0x3d, 0x20, 0x74, 0x20, 0x2a, 0x20, 0x74,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72,
0x6e, 0x20, 0x6d, 0x74, 0x32, 0x20, 0x2a, 0x20, 0x6d, 0x74, 0x20, 0x2a,
0x20, 0x70, 0x30, 0x20, 0x2b, 0x20, 0x33, 0x2e, 0x30, 0x20, 0x2a, 0x20,
0x6d, 0x74, 0x32, 0x20, 0x2a, 0x20, 0x74, 0x20, 0x2a, 0x20, 0x70, 0x31,
0x20, 0x2b, 0x20, 0x33, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x6d, 0x74, 0x20,
0x2a, 0x20, 0x74, 0x32, 0x20, 0x2a, 0x20, 0x70, 0x32, 0x20, 0x2b, 0x20,
0x74, 0x32, 0x20, 0x2a, 0x20, 0x74, 0x20, 0x2a, 0x20, 0x70, 0x33, 0x3b,
0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x6e, 0x20, 0x65, 0x76,
0x61, 0x6c, 0x5f, 0x63, 0x75, 0x62, 0x69, 0x63, 0x5f, 0x64, 0x65, 0x72,
0x69, 0x76, 0x61, 0x74, 0x69, 0x76, 0x65, 0x28, 0x74, 0x3a, 0x20, 0x66,
0x33, 0x32, 0x2c, 0x20, 0x70, 0x30, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32,
0x66, 0x2c, 0x20, 0x70, 0x31, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66,
0x2c, 0x20, 0x70, 0x32, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c,
0x20, 0x70, 0x33, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x29, 0x20,
0x2d, 0x3e, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x20, 0x7b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x6d, 0x74, 0x20, 0x3d,
0x20, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x74, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x33, 0x2e,
0x30, 0x20, 0x2a, 0x20, 0x6d, 0x74, 0x20, 0x2a, 0x20, 0x6d, 0x74, 0x20,
0x2a, 0x20, 0x28, 0x70, 0x31, 0x20, 0x2d, 0x20, 0x70, 0x30, 0x29, 0x20,
0x2b, 0x20, 0x36, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x6d, 0x74, 0x20, 0x2a,
0x20, 0x74, 0x20, 0x2a, 0x20, 0x28, 0x70, 0x32, 0x20, 0x2d, 0x20, 0x70,
0x31, 0x29, 0x20, 0x2b, 0x20, 0x33, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x74,
0x20, 0x2a, 0x20, 0x74, 0x20, 0x2a, 0x20, 0x28, 0x70, 0x33, 0x20, 0x2d,
0x20, 0x70, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
0x2d, 0x20, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x74,
0x6f, 0x20, 0x61, 0x20, 0x63, 0x75, 0x62, 0x69, 0x63, 0x20, 0x42, 0xc3,
0xa9, 0x7a, 0x69, 0x65, 0x72, 0x20, 0x28, 0x73, 0x61, 0x6d, 0x70, 0x6c,
0x69, 0x6e, 0x67, 0x20, 0x2b, 0x20, 0x4e, 0x65, 0x77, 0x74, 0x6f, 0x6e,
0x20, 0x72, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x29,
0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0d,
0x0a, 0x66, 0x6e, 0x20, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x75, 0x62, 0x69, 0x63, 0x28, 0x70, 0x3a,
0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x20, 0x73, 0x65, 0x67, 0x3a,
0x20, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x2d, 0x3e,
0x20, 0x66, 0x33, 0x32, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x65, 0x74, 0x20, 0x70, 0x30, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x67,
0x2e, 0x70, 0x30, 0x3b, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x70, 0x31, 0x20,
0x3d, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70, 0x31, 0x3b, 0x20, 0x6c, 0x65,
0x74, 0x20, 0x70, 0x32, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70,
0x32, 0x3b, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x70, 0x33, 0x20, 0x3d, 0x20,
0x73, 0x65, 0x67, 0x2e, 0x70, 0x33, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72,
0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x74,
0x6f, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x67, 0x6f, 0x6f,
0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x73, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x31, 0x36, 0x75, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x65,
0x73, 0x74, 0x5f, 0x74, 0x20, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x65, 0x73,
0x74, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x31, 0x65, 0x32,
0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20,
0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x75, 0x3b,
0x20, 0x69, 0x20, 0x3c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73,
0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x74, 0x20,
0x3d, 0x20, 0x66, 0x33, 0x32, 0x28, 0x69, 0x29, 0x20, 0x2f, 0x20, 0x66,
0x33, 0x32, 0x28, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x20, 0x2d,
0x20, 0x31, 0x75, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x71, 0x20, 0x3d, 0x20, 0x65,
0x76, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x62, 0x69, 0x63, 0x28, 0x74, 0x2c,
0x20, 0x70, 0x30, 0x2c, 0x20, 0x70, 0x31, 0x2c, 0x20, 0x70, 0x32, 0x2c,
0x20, 0x70, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x64, 0x20, 0x3d, 0x20, 0x64,
0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x71,
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x69, 0x66, 0x20, 0x64, 0x20, 0x3c, 0x20, 0x62, 0x65, 0x73, 0x74, 0x5f,
0x64, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x65, 0x73, 0x74,
0x5f, 0x64, 0x69, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x64, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x62, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x65, 0x77, 0x74, 0x6f, 0x6e, 0x20, 0x72,
0x65, 0x66, 0x69, 0x6e, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x6e,
0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x64,
0x20, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x28, 0x6d,
0x61, 0x78, 0x20, 0x34, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61,
0x72, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28,
0x62, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c,
0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65,
0x72, 0x20, 0x3d, 0x20, 0x30, 0x75, 0x3b, 0x20, 0x69, 0x74, 0x65, 0x72,
0x20, 0x3c, 0x20, 0x34, 0x75, 0x3b, 0x20, 0x69, 0x74, 0x65, 0x72, 0x2b,
0x2b, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x42, 0x20, 0x3d, 0x20, 0x65, 0x76,
0x61, 0x6c, 0x5f, 0x63, 0x75, 0x62, 0x69, 0x63, 0x28, 0x74, 0x2c, 0x20,
0x70, 0x30, 0x2c, 0x20, 0x70, 0x31, 0x2c, 0x20, 0x70, 0x32, 0x2c, 0x20,
0x70, 0x33, 0x29, 0x20, 0x2d, 0x20, 0x70, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x42, 0x70,
0x20, 0x3d, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x62, 0x69,
0x63, 0x5f, 0x64, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69, 0x76, 0x65,
0x28, 0x74, 0x2c, 0x20, 0x70, 0x30, 0x2c, 0x20, 0x70, 0x31, 0x2c, 0x20,
0x70, 0x32, 0x2c, 0x20, 0x70, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x66, 0x20,
0x3d, 0x20, 0x64, 0x6f, 0x74, 0x28, 0x42, 0x2c, 0x20, 0x42, 0x70, 0x29,
0x3b, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0xc2, 0xbd, 0x20, 0x64, 0x65,
0x72, 0x69, 0x76, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x6f, 0x66, 0x20,
0x7c, 0x42, 0x7c, 0xc2, 0xb2, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x64, 0x66, 0x20, 0x3d, 0x20,
0x64, 0x6f, 0x74, 0x28, 0x42, 0x70, 0x2c, 0x20, 0x42, 0x70, 0x29, 0x20,
0x2b, 0x20, 0x64, 0x6f, 0x74, 0x28, 0x42, 0x2c, 0x20, 0x33, 0x2e, 0x30,
0x20, 0x2a, 0x20, 0x28, 0x70, 0x32, 0x20, 0x2d, 0x20, 0x70, 0x31, 0x20,
0x2b, 0x20, 0x28, 0x70, 0x33, 0x20, 0x2d, 0x20, 0x70, 0x32, 0x20, 0x2d,
0x20, 0x70, 0x32, 0x20, 0x2b, 0x20, 0x70, 0x31, 0x29, 0x20, 0x2a, 0x20,
0x32, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x74, 0x29, 0x29, 0x3b, 0x20, 0x2f,
0x2f, 0x20, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64,
0x20, 0x32, 0x6e, 0x64, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x61,
0x62, 0x73, 0x28, 0x64, 0x66, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x65, 0x2d,
0x31, 0x32, 0x20, 0x7b, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x20,
0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x65, 0x74, 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x20,
0x2f, 0x20, 0x64, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70,
0x28, 0x74, 0x20, 0x2d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x2c, 0x20, 0x30,
0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x61, 0x62,
0x73, 0x28, 0x73, 0x74, 0x65, 0x70, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x65,
0x2d, 0x36, 0x20, 0x7b, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x20,
0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x71, 0x5f, 0x66, 0x69, 0x6e, 0x61,
0x6c, 0x20, 0x3d, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x62,
0x69, 0x63, 0x28, 0x74, 0x2c, 0x20, 0x70, 0x30, 0x2c, 0x20, 0x70, 0x31,
0x2c, 0x20, 0x70, 0x32, 0x2c, 0x20, 0x70, 0x33, 0x29, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64,
0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x71,
0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d,
0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
0x2d, 0x2d, 0x2d, 0x2d, 0x20, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x74, 0x69,
0x63, 0x61, 0x6c, 0x20, 0x63, 0x75, 0x62, 0x69, 0x63, 0x20, 0x72, 0x6f,
0x6f, 0x74, 0x20, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x20, 0x28, 0x66,
0x6f, 0x72, 0x20, 0x79, 0x2d, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x69, 0x6e,
0x67, 0x29, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
0x2d, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e,
0x73, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20,
0x72, 0x65, 0x61, 0x6c, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x20, 0x69,
0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x31, 0x5d, 0x2c, 0x20, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x73,
0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x28, 0x75, 0x70, 0x20, 0x74,
0x6f, 0x20, 0x33, 0x29, 0x2e, 0x0d, 0x0a, 0x66, 0x6e, 0x20, 0x73, 0x6f,
0x6c, 0x76, 0x65, 0x5f, 0x63, 0x75, 0x62, 0x69, 0x63, 0x5f, 0x69, 0x6e,
0x5f, 0x30, 0x31, 0x28, 0x63, 0x33, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x2c,
0x20, 0x63, 0x32, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x2c, 0x20, 0x63, 0x31,
0x3a, 0x20, 0x66, 0x33, 0x32, 0x2c, 0x20, 0x63, 0x30, 0x3a, 0x20, 0x66,
0x33, 0x32, 0x2c, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x3a, 0x20, 0x70,
0x74, 0x72, 0x3c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c,
0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x66, 0x33, 0x32, 0x2c, 0x20,
0x33, 0x3e, 0x3e, 0x29, 0x20, 0x2d, 0x3e, 0x20, 0x75, 0x33, 0x32, 0x20,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x6f,
0x72, 0x6d, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x3a, 0x20, 0x63, 0x33, 0x20,
0x2a, 0x20, 0x74, 0x5e, 0x33, 0x20, 0x2b, 0x20, 0x63, 0x32, 0x20, 0x2a,
0x20, 0x74, 0x5e, 0x32, 0x20, 0x2b, 0x20, 0x63, 0x31, 0x20, 0x2a, 0x20,
0x74, 0x20, 0x2b, 0x20, 0x63, 0x30, 0x20, 0x3d, 0x20, 0x30, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x61, 0x62, 0x73, 0x28, 0x63,
0x33, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x65, 0x2d, 0x39, 0x20, 0x7b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20,
0x51, 0x75, 0x61, 0x64, 0x72, 0x61, 0x74, 0x69, 0x63, 0x20, 0x66, 0x61,
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x61, 0x62, 0x73, 0x28, 0x63,
0x32, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x65, 0x2d, 0x39, 0x20, 0x7b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x69, 0x66, 0x20, 0x61, 0x62, 0x73, 0x28, 0x63, 0x31, 0x29, 0x20,
0x3c, 0x20, 0x31, 0x65, 0x2d, 0x39, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x74,
0x75, 0x72, 0x6e, 0x20, 0x30, 0x75, 0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x65, 0x74, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x2d, 0x63, 0x30, 0x20, 0x2f,
0x20, 0x63, 0x31, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x20, 0x3e,
0x3d, 0x20, 0x30, 0x2e, 0x30, 0x20, 0x26, 0x26, 0x20, 0x74, 0x20, 0x3c,
0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x28, 0x2a, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x29, 0x5b, 0x30, 0x5d,
0x20, 0x3d, 0x20, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72,
0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x75, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x75, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74,
0x20, 0x64, 0x69, 0x73, 0x63, 0x20, 0x3d, 0x20, 0x63, 0x31, 0x20, 0x2a,
0x20, 0x63, 0x31, 0x20, 0x2d, 0x20, 0x34, 0x2e, 0x30, 0x20, 0x2a, 0x20,
0x63, 0x32, 0x20, 0x2a, 0x20, 0x63, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x64, 0x69, 0x73,
0x63, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30, 0x20, 0x7b, 0x20, 0x72, 0x65,
0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x75, 0x3b, 0x20, 0x7d, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20,
0x73, 0x64, 0x20, 0x3d, 0x20, 0x73, 0x71, 0x72, 0x74, 0x28, 0x64, 0x69,
0x73, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x74, 0x30, 0x20, 0x3d, 0x20, 0x28,
0x2d, 0x63, 0x31, 0x20, 0x2b, 0x20, 0x73, 0x64, 0x29, 0x20, 0x2f, 0x20,
0x28, 0x32, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x63, 0x32, 0x29, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74,
0x20, 0x74, 0x31, 0x20, 0x3d, 0x20, 0x28, 0x2d, 0x63, 0x31, 0x20, 0x2d,
0x20, 0x73, 0x64, 0x29, 0x20, 0x2f, 0x20, 0x28, 0x32, 0x2e, 0x30, 0x20,
0x2a, 0x20, 0x63, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x20, 0x3d, 0x20, 0x30, 0x75, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x30, 0x20, 0x3e,
0x3d, 0x20, 0x30, 0x2e, 0x30, 0x20, 0x26, 0x26, 0x20, 0x74, 0x30, 0x20,
0x3c, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x7b, 0x20, 0x28, 0x2a, 0x72,
0x6f, 0x6f, 0x74, 0x73, 0x29, 0x5b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5d,
0x20, 0x3d, 0x20, 0x74, 0x30, 0x3b, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x2b, 0x2b, 0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x31, 0x20, 0x3e, 0x3d, 0x20,
0x30, 0x2e, 0x30, 0x20, 0x26, 0x26, 0x20, 0x74, 0x31, 0x20, 0x3c, 0x3d,
0x20, 0x31, 0x2e, 0x30, 0x20, 0x7b, 0x20, 0x28, 0x2a, 0x72, 0x6f, 0x6f,
0x74, 0x73, 0x29, 0x5b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5d, 0x20, 0x3d,
0x20, 0x74, 0x31, 0x3b, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b,
0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x70, 0x72,
0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x63, 0x75, 0x62, 0x69, 0x63, 0x3a,
0x20, 0x6c, 0x65, 0x74, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x78, 0x20, 0x2d,
0x20, 0x63, 0x32, 0x2f, 0x28, 0x33, 0x2a, 0x63, 0x33, 0x29, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x20, 0x3d, 0x20,
0x63, 0x32, 0x20, 0x2f, 0x20, 0x63, 0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x63, 0x31,
0x20, 0x2f, 0x20, 0x63, 0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x65, 0x74, 0x20, 0x63, 0x20, 0x3d, 0x20, 0x63, 0x30, 0x20, 0x2f,
0x20, 0x63, 0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
0x74, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x62, 0x20, 0x2d, 0x20, 0x61, 0x20,
0x2a, 0x20, 0x61, 0x20, 0x2f, 0x20, 0x33, 0x2e, 0x30, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x71, 0x20, 0x3d, 0x20,
0x32, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x61, 0x20, 0x2a, 0x20, 0x61, 0x20,
0x2a, 0x20, 0x61, 0x20, 0x2f, 0x20, 0x32, 0x37, 0x2e, 0x30, 0x20, 0x2d,
0x20, 0x61, 0x20, 0x2a, 0x20, 0x62, 0x20, 0x2f, 0x20, 0x33, 0x2e, 0x30,
0x20, 0x2b, 0x20, 0x63, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x65, 0x74, 0x20, 0x64, 0x69, 0x73, 0x63, 0x20, 0x3d, 0x20, 0x71, 0x20,
0x2a, 0x20, 0x71, 0x20, 0x2f, 0x20, 0x34, 0x2e, 0x30, 0x20, 0x2b, 0x20,
0x70, 0x20, 0x2a, 0x20, 0x70, 0x20, 0x2a, 0x20, 0x70, 0x20, 0x2f, 0x20,
0x32, 0x37, 0x2e, 0x30, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x3d,
0x20, 0x30, 0x75, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
0x74, 0x20, 0x73, 0x68, 0x69, 0x66, 0x74, 0x20, 0x3d, 0x20, 0x61, 0x20,
0x2f, 0x20, 0x33, 0x2e, 0x30, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x69, 0x66, 0x20, 0x64, 0x69, 0x73, 0x63, 0x20, 0x3e, 0x20,
0x30, 0x2e, 0x30, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x73, 0x64, 0x20, 0x3d, 0x20,
0x73, 0x71, 0x72, 0x74, 0x28, 0x64, 0x69, 0x73, 0x63, 0x29, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74,
0x20, 0x75, 0x20, 0x3d, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x28, 0x2d, 0x71,
0x2f, 0x32, 0x2e, 0x30, 0x20, 0x2b, 0x20, 0x73, 0x64, 0x29, 0x20, 0x2a,
0x20, 0x70, 0x6f, 0x77, 0x28, 0x61, 0x62, 0x73, 0x28, 0x2d, 0x71, 0x2f,
0x32, 0x2e, 0x30, 0x20, 0x2b, 0x20, 0x73, 0x64, 0x29, 0x2c, 0x20, 0x31,
0x2e, 0x30, 0x2f, 0x33, 0x2e, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x76, 0x20,
0x3d, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x28, 0x2d, 0x71, 0x2f, 0x32, 0x2e,
0x30, 0x20, 0x2d, 0x20, 0x73, 0x64, 0x29, 0x20, 0x2a, 0x20, 0x70, 0x6f,
0x77, 0x28, 0x61, 0x62, 0x73, 0x28, 0x2d, 0x71, 0x2f, 0x32, 0x2e, 0x30,
0x20, 0x2d, 0x20, 0x73, 0x64, 0x29, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x2f,
0x33, 0x2e, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x78, 0x31, 0x20, 0x3d, 0x20,
0x75, 0x20, 0x2b, 0x20, 0x76, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x74, 0x20, 0x3d, 0x20,
0x78, 0x31, 0x20, 0x2d, 0x20, 0x73, 0x68, 0x69, 0x66, 0x74, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
0x74, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x20, 0x26, 0x26, 0x20,
0x74, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x7b, 0x20, 0x28,
0x2a, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x29, 0x5b, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x5d, 0x20, 0x3d, 0x20, 0x74, 0x3b, 0x20, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x2b, 0x2b, 0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x64, 0x69,
0x73, 0x63, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x20, 0x7b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74,
0x20, 0x75, 0x20, 0x3d, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x28, 0x2d, 0x71,
0x2f, 0x32, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x70, 0x6f, 0x77, 0x28,
0x61, 0x62, 0x73, 0x28, 0x2d, 0x71, 0x2f, 0x32, 0x2e, 0x30, 0x29, 0x2c,
0x20, 0x31, 0x2e, 0x30, 0x2f, 0x33, 0x2e, 0x30, 0x29, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20,
0x78, 0x31, 0x20, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x75,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x65, 0x74, 0x20, 0x78, 0x32, 0x20, 0x3d, 0x20, 0x2d, 0x75, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74,
0x20, 0x74, 0x31, 0x20, 0x3d, 0x20, 0x78, 0x31, 0x20, 0x2d, 0x20, 0x73,
0x68, 0x69, 0x66, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x74, 0x32, 0x20, 0x3d, 0x20,
0x78, 0x32, 0x20, 0x2d, 0x20, 0x73, 0x68, 0x69, 0x66, 0x74, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
0x74, 0x31, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x20, 0x26, 0x26,
0x20, 0x74, 0x31, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x7b,
0x20, 0x28, 0x2a, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x29, 0x5b, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x5d, 0x20, 0x3d, 0x20, 0x74, 0x31, 0x3b, 0x20, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b, 0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x32,
0x20, 0x3e, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x20, 0x26, 0x26, 0x20, 0x74,
0x32, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x7b, 0x20, 0x28,
0x2a, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x29, 0x5b, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x5d, 0x20, 0x3d, 0x20, 0x74, 0x32, 0x3b, 0x20, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x2b, 0x2b, 0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x70,
0x68, 0x69, 0x20, 0x3d, 0x20, 0x61, 0x63, 0x6f, 0x73, 0x28, 0x20, 0x2d,
0x71, 0x20, 0x2f, 0x20, 0x28, 0x32, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x73,
0x71, 0x72, 0x74, 0x28, 0x2d, 0x70, 0x2a, 0x70, 0x2a, 0x70, 0x2f, 0x32,
0x37, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x72, 0x20,
0x3d, 0x20, 0x32, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x73, 0x71, 0x72, 0x74,
0x28, 0x2d, 0x70, 0x2f, 0x33, 0x2e, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28,
0x76, 0x61, 0x72, 0x20, 0x6b, 0x20, 0x3d, 0x20, 0x30, 0x75, 0x3b, 0x20,
0x6b, 0x20, 0x3c, 0x20, 0x33, 0x75, 0x3b, 0x20, 0x6b, 0x2b, 0x2b, 0x29,
0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x78, 0x20, 0x3d, 0x20,
0x72, 0x20, 0x2a, 0x20, 0x63, 0x6f, 0x73, 0x28, 0x28, 0x70, 0x68, 0x69,
0x20, 0x2b, 0x20, 0x32, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x33, 0x2e, 0x31,
0x34, 0x31, 0x35, 0x39, 0x32, 0x36, 0x35, 0x33, 0x35, 0x20, 0x2a, 0x20,
0x66, 0x33, 0x32, 0x28, 0x6b, 0x29, 0x29, 0x20, 0x2f, 0x20, 0x33, 0x2e,
0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x74, 0x20, 0x3d,
0x20, 0x78, 0x20, 0x2d, 0x20, 0x73, 0x68, 0x69, 0x66, 0x74, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x69, 0x66, 0x20, 0x74, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x2e, 0x30,
0x20, 0x26, 0x26, 0x20, 0x74, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x2e, 0x30,
0x20, 0x7b, 0x20, 0x28, 0x2a, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x29, 0x5b,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5d, 0x20, 0x3d, 0x20, 0x74, 0x3b, 0x20,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b, 0x3b, 0x20, 0x7d, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0d,
0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d,
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x20, 0x57, 0x69, 0x6e, 0x64,
0x69, 0x6e, 0x67, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20,
0x66, 0x72, 0x6f, 0x6d, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x63, 0x75, 0x62,
0x69, 0x63, 0x20, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x2d,
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0d, 0x0a, 0x66,
0x6e, 0x20, 0x72, 0x61, 0x79, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x63, 0x75, 0x62, 0x69,
0x63, 0x28, 0x70, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x20,
0x73, 0x65, 0x67, 0x3a, 0x20, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74,
0x29, 0x20, 0x2d, 0x3e, 0x20, 0x69, 0x33, 0x32, 0x20, 0x7b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x57, 0x65, 0x20, 0x63, 0x61,
0x73, 0x74, 0x20, 0x61, 0x20, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x6f, 0x6e,
0x74, 0x61, 0x6c, 0x20, 0x72, 0x61, 0x79, 0x20, 0x66, 0x72, 0x6f, 0x6d,
0x20, 0x70, 0x20, 0x74, 0x6f, 0x20, 0x2b, 0x78, 0x3b, 0x20, 0x66, 0x69,
0x6e, 0x64, 0x20, 0x74, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x79,
0x28, 0x74, 0x29, 0x20, 0x3d, 0x20, 0x70, 0x2e, 0x79, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x79, 0x30, 0x20, 0x3d, 0x20,
0x73, 0x65, 0x67, 0x2e, 0x70, 0x30, 0x2e, 0x79, 0x20, 0x2d, 0x20, 0x70,
0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74,
0x20, 0x79, 0x31, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70, 0x31,
0x2e, 0x79, 0x20, 0x2d, 0x20, 0x70, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x79, 0x32, 0x20, 0x3d, 0x20,
0x73, 0x65, 0x67, 0x2e, 0x70, 0x32, 0x2e, 0x79, 0x20, 0x2d, 0x20, 0x70,
0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74,
0x20, 0x79, 0x33, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70, 0x33,
0x2e, 0x79, 0x20, 0x2d, 0x20, 0x70, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x79, 0x28, 0x74, 0x29,
0x20, 0x3d, 0x20, 0x28, 0x31, 0x2d, 0x74, 0x29, 0x5e, 0x33, 0x2a, 0x79,
0x30, 0x20, 0x2b, 0x20, 0x33, 0x28, 0x31, 0x2d, 0x74, 0x29, 0x5e, 0x32,
0x20, 0x74, 0x20, 0x2a, 0x20, 0x79, 0x31, 0x20, 0x2b, 0x20, 0x33, 0x28,
0x31, 0x2d, 0x74, 0x29, 0x20, 0x74, 0x5e, 0x32, 0x20, 0x2a, 0x20, 0x79,
0x32, 0x20, 0x2b, 0x20, 0x74, 0x5e, 0x33, 0x20, 0x2a, 0x20, 0x79, 0x33,
0x20, 0x3d, 0x20, 0x30, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f,
0x20, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x6f, 0x6c, 0x79,
0x6e, 0x6f, 0x6d, 0x69, 0x61, 0x6c, 0x3a, 0x20, 0x63, 0x33, 0x20, 0x74,
0x5e, 0x33, 0x20, 0x2b, 0x20, 0x63, 0x32, 0x20, 0x74, 0x5e, 0x32, 0x20,
0x2b, 0x20, 0x63, 0x31, 0x20, 0x74, 0x20, 0x2b, 0x20, 0x63, 0x30, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x63, 0x30, 0x20,
0x3d, 0x20, 0x79, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x65, 0x74, 0x20, 0x63, 0x31, 0x20, 0x3d, 0x20, 0x33, 0x2e, 0x30, 0x20,
0x2a, 0x20, 0x28, 0x79, 0x31, 0x20, 0x2d, 0x20, 0x79, 0x30, 0x29, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x63, 0x32,
0x20, 0x3d, 0x20, 0x33, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x28, 0x79, 0x32,
0x20, 0x2d, 0x20, 0x32, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x79, 0x31, 0x20,
0x2b, 0x20, 0x79, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x65, 0x74, 0x20, 0x63, 0x33, 0x20, 0x3d, 0x20, 0x79, 0x33, 0x20,
0x2d, 0x20, 0x33, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x79, 0x32, 0x20, 0x2b,
0x20, 0x33, 0x2e, 0x30, 0x20, 0x2a, 0x20, 0x79, 0x31, 0x20, 0x2d, 0x20,
0x79, 0x30, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76,
0x61, 0x72, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x3a, 0x20, 0x61, 0x72,
0x72, 0x61, 0x79, 0x3c, 0x66, 0x33, 0x32, 0x2c, 0x20, 0x33, 0x3e, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x6e, 0x75,
0x6d, 0x20, 0x3d, 0x20, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x63, 0x75,
0x62, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x5f, 0x30, 0x31, 0x28, 0x63, 0x33,
0x2c, 0x20, 0x63, 0x32, 0x2c, 0x20, 0x63, 0x31, 0x2c, 0x20, 0x63, 0x30,
0x2c, 0x20, 0x26, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x29, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x77, 0x69, 0x6e, 0x64,
0x20, 0x3d, 0x20, 0x30, 0x69, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d,
0x20, 0x30, 0x75, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d,
0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x74, 0x20,
0x3d, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x5b, 0x69, 0x5d, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74,
0x20, 0x78, 0x20, 0x3d, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x5f, 0x63, 0x75,
0x62, 0x69, 0x63, 0x28, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70,
0x30, 0x2c, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70, 0x31, 0x2c, 0x20, 0x73,
0x65, 0x67, 0x2e, 0x70, 0x32, 0x2c, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70,
0x33, 0x29, 0x2e, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x78, 0x20, 0x3e, 0x20, 0x70, 0x2e,
0x78, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x64, 0x79, 0x20,
0x3d, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x62, 0x69, 0x63,
0x5f, 0x64, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69, 0x76, 0x65, 0x28,
0x74, 0x2c, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70, 0x30, 0x2c, 0x20, 0x73,
0x65, 0x67, 0x2e, 0x70, 0x31, 0x2c, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70,
0x32, 0x2c, 0x20, 0x73, 0x65, 0x67, 0x2e, 0x70, 0x33, 0x29, 0x2e, 0x79,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x64, 0x79, 0x20, 0x3e, 0x20, 0x30,
0x2e, 0x30, 0x20, 0x7b, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x20, 0x2b, 0x3d,
0x20, 0x31, 0x69, 0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65,
0x20, 0x69, 0x66, 0x20, 0x64, 0x79, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30,
0x20, 0x7b, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x20, 0x2d, 0x3d, 0x20, 0x31,
0x69, 0x3b, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77,
0x69, 0x6e, 0x64, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f,
0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
0x20, 0x4d, 0x61, 0x69, 0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74,
0x65, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x2d, 0x2d, 0x2d,
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0d, 0x0a, 0x40, 0x63, 0x6f,
0x6d, 0x70, 0x75, 0x74, 0x65, 0x20, 0x40, 0x77, 0x6f, 0x72, 0x6b, 0x67,
0x72, 0x6f, 0x75, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x38, 0x2c,
0x20, 0x38, 0x29, 0x0d, 0x0a, 0x66, 0x6e, 0x20, 0x6d, 0x61, 0x69, 0x6e,
0x28, 0x40, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x28, 0x77, 0x6f,
0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x29, 0x20,
0x77, 0x67, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x33, 0x3c, 0x75, 0x33, 0x32,
0x3e, 0x2c, 0x20, 0x2f, 0x2f, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x77,
0x6f, 0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x69, 0x73, 0x20,
0x61, 0x20, 0x74, 0x69, 0x6c, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x40, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e,
0x28, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x33, 0x3c, 0x75, 0x33,
0x32, 0x3e, 0x29, 0x20, 0x2f, 0x2f, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x69,
0x78, 0x65, 0x6c, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x65, 0x74, 0x20, 0x74, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x78,
0x20, 0x3d, 0x20, 0x77, 0x67, 0x2e, 0x78, 0x20, 0x2f, 0x20, 0x31, 0x36,
0x75, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20,
0x74, 0x69, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x69, 0x6c, 0x65, 0x73,
0x5b, 0x74, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x78, 0x5d, 0x3b, 0x0d,
0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x70,
0x78, 0x20, 0x3d, 0x20, 0x28, 0x77, 0x67, 0x2e, 0x78, 0x20, 0x25, 0x20,
0x31, 0x36, 0x75, 0x29, 0x20, 0x2a, 0x20, 0x38, 0x75, 0x20, 0x2b, 0x20,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2e, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x70, 0x79, 0x20, 0x3d, 0x20, 0x77,
0x67, 0x2e, 0x79, 0x20, 0x2a, 0x20, 0x38, 0x75, 0x20, 0x2b, 0x20, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x2e, 0x79, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x70, 0x78, 0x20, 0x3e, 0x3d,
0x20, 0x54, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x20, 0x7c,
0x7c, 0x20, 0x70, 0x79, 0x20, 0x3e, 0x3d, 0x20, 0x54, 0x49, 0x4c, 0x45,
0x5f, 0x53, 0x49, 0x5a, 0x45, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x75, 0x76, 0x20, 0x3d,
0x20, 0x28, 0x76, 0x65, 0x63, 0x32, 0x66, 0x28, 0x66, 0x33, 0x32, 0x28,
0x70, 0x78, 0x29, 0x2c, 0x20, 0x66, 0x33, 0x32, 0x28, 0x70, 0x79, 0x29,
0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2f, 0x20, 0x66,
0x33, 0x32, 0x28, 0x54, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x49, 0x5a, 0x45,
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20,
0x77, 0x6f, 0x72, 0x6c, 0x64, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x78, 0x28,
0x74, 0x69, 0x6c, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x5f, 0x6d,
0x69, 0x6e, 0x2c, 0x20, 0x74, 0x69, 0x6c, 0x65, 0x2e, 0x77, 0x6f, 0x72,
0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x2c, 0x20, 0x75, 0x76, 0x29, 0x3b,
0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20,
0x6d, 0x69, 0x6e, 0x44, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x66, 0x33, 0x32,
0x20, 0x3d, 0x20, 0x31, 0x65, 0x32, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x64, 0x66, 0x3a, 0x20, 0x66,
0x33, 0x32, 0x20, 0x3d, 0x20, 0x31, 0x65, 0x32, 0x30, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x77, 0x69, 0x6e, 0x64,
0x69, 0x6e, 0x67, 0x3a, 0x20, 0x69, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x30,
0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72,
0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x75,
0x3b, 0x20, 0x73, 0x20, 0x3c, 0x20, 0x74, 0x69, 0x6c, 0x65, 0x2e, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x20, 0x73, 0x2b, 0x2b, 0x29, 0x20, 0x7b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f,
0x20, 0x55, 0x73, 0x65, 0x20, 0x61, 0x20, 0x70, 0x72, 0x65, 0x2d, 0x63,
0x75, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x20,
0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x70, 0x65, 0x72, 0x20, 0x74, 0x69,
0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65,
0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, 0x72, 0x2d, 0x70, 0x69, 0x78,
0x65, 0x6c, 0x20, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e,
0x67, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65,
0x78, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b,
0x74, 0x69, 0x6c, 0x65, 0x2e, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20,
0x2b, 0x20, 0x73, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x74,
0x68, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x69, 0x73, 0x20,
0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x74,
0x68, 0x65, 0x20, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x20, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x61, 0x6e,
0x20, 0x77, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x63,
0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x69, 0x72, 0x63, 0x6c,
0x65, 0x73, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x69, 0x66, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3c, 0x20, 0x74,
0x69, 0x6c, 0x65, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x5f, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x29, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x63, 0x20,
0x3d, 0x20, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x73, 0x5b, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x64, 0x66, 0x20, 0x3d,
0x20, 0x6d, 0x69, 0x6e, 0x28, 0x73, 0x64, 0x66, 0x2c, 0x20, 0x6c, 0x65,
0x6e, 0x67, 0x74, 0x68, 0x28, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x20, 0x2d,
0x20, 0x63, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x29, 0x20, 0x2d,
0x20, 0x63, 0x2e, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x29, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65,
0x20, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x20,
0x3d, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x73, 0x5b, 0x69, 0x6e, 0x64,
0x65, 0x78, 0x5d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2a, 0x6c, 0x65,
0x74, 0x20, 0x62, 0x62, 0x6f, 0x78, 0x5f, 0x6d, 0x69, 0x6e, 0x20, 0x3d,
0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2e, 0x62, 0x62, 0x6f, 0x78, 0x5f,
0x6d, 0x69, 0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x62, 0x62,
0x6f, 0x78, 0x5f, 0x6d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x73, 0x68, 0x61,
0x70, 0x65, 0x2e, 0x62, 0x62, 0x6f, 0x78, 0x5f, 0x6d, 0x61, 0x78, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x64, 0x78, 0x20, 0x3d, 0x20, 0x6d,
0x61, 0x78, 0x28, 0x62, 0x62, 0x6f, 0x78, 0x5f, 0x6d, 0x69, 0x6e, 0x2e,
0x78, 0x20, 0x2d, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x78, 0x2c,
0x20, 0x6d, 0x61, 0x78, 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x77, 0x6f,
0x72, 0x6c, 0x64, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x62, 0x62, 0x6f, 0x78,
0x5f, 0x6d, 0x61, 0x78, 0x2e, 0x78, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x65, 0x74, 0x20, 0x64, 0x79, 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x78, 0x28,
0x62, 0x62, 0x6f, 0x78, 0x5f, 0x6d, 0x69, 0x6e, 0x2e, 0x79, 0x20, 0x2d,
0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x79, 0x2c, 0x20, 0x6d, 0x61,
0x78, 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0x2e, 0x79, 0x20, 0x2d, 0x20, 0x62, 0x62, 0x6f, 0x78, 0x5f, 0x6d, 0x61,
0x78, 0x2e, 0x79, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20,
0x64, 0x69, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x62, 0x6f, 0x78, 0x20,
0x3d, 0x20, 0x73, 0x71, 0x72, 0x74, 0x28, 0x64, 0x78, 0x20, 0x2a, 0x20,
0x64, 0x78, 0x20, 0x2b, 0x20, 0x64, 0x79, 0x20, 0x2a, 0x20, 0x64, 0x79,
0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x64, 0x69, 0x73, 0x74, 0x5f,
0x74, 0x6f, 0x5f, 0x62, 0x6f, 0x78, 0x20, 0x3e, 0x3d, 0x20, 0x6d, 0x69,
0x6e, 0x44, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x3b, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x7d, 0x2a, 0x2f, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72,
0x6f, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x73, 0x65,
0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
0x65, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72,
0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x73, 0x68,
0x61, 0x70, 0x65, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x65,
0x67, 0x6d, 0x65, 0x6e, 0x74, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x73,
0x68, 0x61, 0x70, 0x65, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73,
0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x2b, 0x20, 0x73, 0x68, 0x61,
0x70, 0x65, 0x2e, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x73, 0x65,
0x67, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73,
0x5b, 0x69, 0x5d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
0x74, 0x20, 0x64, 0x20, 0x3d, 0x20, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e,
0x63, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x75, 0x62, 0x69, 0x63, 0x28,
0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2c, 0x20, 0x73, 0x65, 0x67, 0x29, 0x3b,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x44, 0x69, 0x73,
0x74, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x6d, 0x69, 0x6e, 0x44,
0x69, 0x73, 0x74, 0x2c, 0x20, 0x64, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20,
0x2b, 0x3d, 0x20, 0x72, 0x61, 0x79, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x63, 0x75, 0x62,
0x69, 0x63, 0x28, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2c, 0x20, 0x73, 0x65,
0x67, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c,
0x65, 0x63, 0x74, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x2d, 0x31, 0x2e,
0x30, 0x2c, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x21,
0x3d, 0x20, 0x30, 0x69, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x64, 0x66, 0x20,
0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x73, 0x64, 0x66, 0x2c, 0x20, 0x73,
0x69, 0x67, 0x6e, 0x20, 0x2a, 0x20, 0x6d, 0x69, 0x6e, 0x44, 0x69, 0x73,
0x74, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65,
0x53, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x64, 0x66, 0x5f, 0x62, 0x75,
0x66, 0x66, 0x65, 0x72, 0x2c, 0x20, 0x76, 0x65, 0x63, 0x32, 0x28, 0x75,
0x33, 0x32, 0x28, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x78, 0x29, 0x2c,
0x20, 0x75, 0x33, 0x32, 0x28, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x79,
0x29, 0x29, 0x2c, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x73, 0x64,
0x66, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c,
0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x7d
};
unsigned int src_shaders_compute_wgsl_len = 8194;

47
src/generated/display.h Normal file
View File

@@ -0,0 +1,47 @@
unsigned char src_shaders_display_wgsl[] = {
0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x55, 0x6e, 0x69, 0x66, 0x6f,
0x72, 0x6d, 0x73, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74,
0x69, 0x6d, 0x65, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x75, 0x33,
0x32, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x76, 0x70, 0x3a,
0x20, 0x6d, 0x61, 0x74, 0x34, 0x78, 0x34, 0x66, 0x2c, 0x0d, 0x0a, 0x7d,
0x0d, 0x0a, 0x0d, 0x0a, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x30,
0x29, 0x20, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x30,
0x29, 0x20, 0x76, 0x61, 0x72, 0x3c, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72,
0x6d, 0x3e, 0x20, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x3a,
0x20, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x3b, 0x0d, 0x0a,
0x0d, 0x0a, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x31, 0x29, 0x20,
0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x30, 0x29, 0x20,
0x76, 0x61, 0x72, 0x20, 0x73, 0x64, 0x66, 0x20, 0x3a, 0x20, 0x74, 0x65,
0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x32, 0x64, 0x3c, 0x66, 0x33, 0x32,
0x3e, 0x3b, 0x0d, 0x0a, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x31,
0x29, 0x20, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x31,
0x29, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x64, 0x66, 0x5f, 0x73, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x3a, 0x20, 0x73, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x40, 0x76, 0x65, 0x72,
0x74, 0x65, 0x78, 0x20, 0x66, 0x6e, 0x20, 0x76, 0x73, 0x5f, 0x6d, 0x61,
0x69, 0x6e, 0x28, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x28, 0x30, 0x29, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x29, 0x20, 0x2d, 0x3e, 0x20,
0x40, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x28, 0x70, 0x6f, 0x73,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66,
0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74,
0x75, 0x72, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x70, 0x6f,
0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c,
0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x6e, 0x69, 0x66,
0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6d, 0x76, 0x70, 0x3b, 0x0d, 0x0a, 0x7d,
0x0d, 0x0a, 0x0d, 0x0a, 0x40, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
0x74, 0x20, 0x66, 0x6e, 0x20, 0x66, 0x73, 0x5f, 0x6d, 0x61, 0x69, 0x6e,
0x28, 0x40, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x28, 0x70, 0x6f,
0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x70, 0x6f, 0x73, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x29,
0x20, 0x2d, 0x3e, 0x20, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x28, 0x30, 0x29, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x0d, 0x0a,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72,
0x6e, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x53, 0x61, 0x6d,
0x70, 0x6c, 0x65, 0x28, 0x73, 0x64, 0x66, 0x2c, 0x20, 0x73, 0x64, 0x66,
0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x6f,
0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x79, 0x29, 0x3b, 0x0d,
0x0a, 0x7d
};
unsigned int src_shaders_display_wgsl_len = 518;

View File

@@ -1,145 +0,0 @@
unsigned char src_shaders_sprite_wgsl[] = {
0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x53, 0x70, 0x72, 0x69, 0x74,
0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x74,
0x72, 0x69, 0x78, 0x3a, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x78, 0x34, 0x66,
0x2c, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63,
0x74, 0x20, 0x56, 0x73, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20,
0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x76, 0x70, 0x3a, 0x20,
0x6d, 0x61, 0x74, 0x34, 0x78, 0x34, 0x66, 0x2c, 0x0d, 0x0a, 0x7d, 0x3b,
0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x56, 0x73, 0x49,
0x20, 0x7b, 0x20, 0x2f, 0x2f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x20,
0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74,
0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x40, 0x62, 0x75, 0x69, 0x6c, 0x74,
0x69, 0x6e, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f,
0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61,
0x6e, 0x63, 0x65, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, 0x0d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x28, 0x30, 0x29, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28,
0x31, 0x29, 0x20, 0x75, 0x76, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66,
0x2c, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63,
0x74, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x20, 0x7b, 0x20, 0x2f, 0x2f,
0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65,
0x72, 0x20, 0x74, 0x6f, 0x20, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
0x74, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x40, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x28, 0x70,
0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x70, 0x6f, 0x73,
0x3a, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28,
0x30, 0x29, 0x20, 0x40, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c,
0x61, 0x74, 0x65, 0x28, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x29, 0x20,
0x75, 0x76, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x0d, 0x0a,
0x7d, 0x3b, 0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x46,
0x73, 0x4f, 0x20, 0x7b, 0x20, 0x2f, 0x2f, 0x46, 0x72, 0x61, 0x67, 0x6d,
0x65, 0x6e, 0x74, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x6f,
0x75, 0x74, 0x70, 0x75, 0x74, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x40,
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x30, 0x29, 0x20,
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66,
0x2c, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x40, 0x62, 0x69,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x30, 0x29, 0x20, 0x40, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x28, 0x30, 0x29, 0x20, 0x76, 0x61, 0x72, 0x3c, 0x75,
0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0x20, 0x76, 0x73, 0x5f, 0x75,
0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x3a, 0x20, 0x56, 0x73, 0x55,
0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x3b, 0x0d, 0x0a, 0x40, 0x62, 0x69,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x30, 0x29, 0x20, 0x40, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x28, 0x31, 0x29, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74,
0x65, 0x78, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f,
0x32, 0x64, 0x3c, 0x66, 0x33, 0x32, 0x3e, 0x3b, 0x0d, 0x0a, 0x40, 0x62,
0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x31, 0x29, 0x20, 0x40, 0x67,
0x72, 0x6f, 0x75, 0x70, 0x28, 0x31, 0x29, 0x20, 0x76, 0x61, 0x72, 0x20,
0x73, 0x61, 0x6d, 0x70, 0x3a, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x72, 0x3b, 0x0d, 0x0a, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67,
0x28, 0x32, 0x29, 0x20, 0x40, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x31,
0x29, 0x20, 0x76, 0x61, 0x72, 0x3c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x65, 0x3e, 0x20, 0x73, 0x70, 0x72, 0x69, 0x74, 0x65, 0x73, 0x3a, 0x20,
0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x53, 0x70, 0x72, 0x69, 0x74, 0x65,
0x3e, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x40, 0x76, 0x65, 0x72, 0x74, 0x65,
0x78, 0x20, 0x66, 0x6e, 0x20, 0x76, 0x73, 0x5f, 0x6d, 0x61, 0x69, 0x6e,
0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x56, 0x73, 0x49, 0x29,
0x20, 0x2d, 0x3e, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x20, 0x7b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x3a, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f,
0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20,
0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2e,
0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x2c, 0x20,
0x69, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69,
0x6f, 0x6e, 0x2e, 0x79, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x20,
0x2a, 0x20, 0x73, 0x70, 0x72, 0x69, 0x74, 0x65, 0x73, 0x5b, 0x69, 0x6e,
0x70, 0x75, 0x74, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
0x5d, 0x2e, 0x6d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x20, 0x2a, 0x20, 0x76,
0x73, 0x5f, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6d,
0x76, 0x70, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x2e, 0x75, 0x76, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x70,
0x75, 0x74, 0x2e, 0x75, 0x76, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f,
0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20,
0x33, 0x32, 0x62, 0x69, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x63,
0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x28, 0x68, 0x65, 0x78, 0x20, 0x72, 0x65,
0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x29, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x72,
0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x76, 0x65, 0x63, 0x34,
0x66, 0x0d, 0x0a, 0x2f, 0x2a, 0x66, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x76,
0x65, 0x72, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x69, 0x6e, 0x70,
0x75, 0x74, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x29, 0x20, 0x2d, 0x3e, 0x20,
0x76, 0x65, 0x63, 0x34, 0x66, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x6c, 0x65, 0x74, 0x20, 0x72, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x20,
0x3d, 0x20, 0x66, 0x33, 0x32, 0x28, 0x28, 0x28, 0x69, 0x6e, 0x70, 0x75,
0x74, 0x20, 0x3e, 0x3e, 0x20, 0x20, 0x30, 0x29, 0x20, 0x26, 0x20, 0x30,
0x78, 0x66, 0x66, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x65, 0x74, 0x20, 0x67, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x20, 0x3d,
0x20, 0x66, 0x33, 0x32, 0x28, 0x28, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74,
0x20, 0x3e, 0x3e, 0x20, 0x20, 0x38, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78,
0x66, 0x66, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x65, 0x74, 0x20, 0x62, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x20, 0x3d, 0x20,
0x66, 0x33, 0x32, 0x28, 0x28, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20,
0x3e, 0x3e, 0x20, 0x31, 0x36, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66,
0x66, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65,
0x74, 0x20, 0x61, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x66,
0x33, 0x32, 0x28, 0x28, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x3e,
0x3e, 0x20, 0x32, 0x34, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x66,
0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28,
0x72, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x67, 0x20, 0x2f,
0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x62, 0x20, 0x2f, 0x20, 0x32, 0x35,
0x35, 0x2c, 0x20, 0x61, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b,
0x0d, 0x0a, 0x7d, 0x2a, 0x2f, 0x0d, 0x0a, 0x2f, 0x2f, 0x20, 0x47, 0x65,
0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72,
0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x69, 0x6e, 0x64, 0x65,
0x78, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55,
0x56, 0x0d, 0x0a, 0x2f, 0x2a, 0x66, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x65,
0x78, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x28, 0x75,
0x76, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x20, 0x77, 0x69,
0x64, 0x74, 0x68, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, 0x20, 0x68, 0x65,
0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x29, 0x20, 0x2d,
0x3e, 0x20, 0x75, 0x33, 0x32, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x6c, 0x65, 0x74, 0x20, 0x78, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x20,
0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x66, 0x6c, 0x6f, 0x6f,
0x72, 0x28, 0x75, 0x76, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x66, 0x33, 0x32,
0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x2c, 0x20, 0x30, 0x2c,
0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x79, 0x3a, 0x20, 0x75, 0x33, 0x32,
0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x66, 0x6c, 0x6f,
0x6f, 0x72, 0x28, 0x75, 0x76, 0x2e, 0x79, 0x20, 0x2a, 0x20, 0x66, 0x33,
0x32, 0x28, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x29, 0x2c, 0x20,
0x30, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x3b, 0x0d,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
0x79, 0x20, 0x2a, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x2b, 0x20,
0x78, 0x3b, 0x0d, 0x0a, 0x7d, 0x2a, 0x2f, 0x0d, 0x0a, 0x0d, 0x0a, 0x40,
0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x6e, 0x20,
0x66, 0x73, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x69, 0x6e, 0x70, 0x75,
0x74, 0x3a, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x29, 0x20, 0x2d, 0x3e,
0x20, 0x46, 0x73, 0x4f, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x76, 0x61, 0x72, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3a, 0x20,
0x46, 0x73, 0x4f, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x53, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x28, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61,
0x6d, 0x70, 0x2c, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x75, 0x76,
0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65,
0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b,
0x0d, 0x0a, 0x7d
};
unsigned int src_shaders_sprite_wgsl_len = 1695;

View File

@@ -1,116 +1,322 @@
#include "api.h"
#define ALLOC(arg) smemtrack_alloc(arg, NULL)
#define FREE(arg) smemtrack_free(arg, NULL)
typedef struct display_uniforms {
uint32_t frame;
uint32_t time;
#define GRID_X 1000
#define GRID_Y 1000
typedef struct vs_uniform_t {
mat4x4f mvp;
} uniform_t;
float mvp[16];
} display_uniforms;
typedef struct renderer_t {
sg_pipeline pipeline; //Configured sprite pipeline
sg_pass_action clear_pass; //Render pass - Clear screen
uniform_t uniform; //Uniform data
sg_pipeline pipeline;
sg_pass_action clear_pass;
sg_bindings bindings;
display_uniforms uniforms;
} renderer_t;
typedef struct dragger_t {
bool dragging;
float origin_x, origin_y;
} dragger_t;
typedef struct compute_t {
sg_pipeline pipeline;
sg_bindings bindings;
sg_pass pass;
} compute_t;
typedef struct userdata_t {
int width, height;
vec2f pan;
float zoom;
dragger_t dragger;
scene_t scene;
renderer_t renderer;
manager_t manager;
compute_t compute;
//Viewport related values
float pan_x, pan_y;
float zoom, rotation;
} userdata_t;
const char* format(const char* format, ...)
static void* _malloc(size_t size, void* userdata)
{
char buffer[_SLOG_LINE_LENGTH];
va_list va;
va_start(va, format);
int size = vsnprintf(buffer, _SLOG_LINE_LENGTH, format, va);
va_end(va);
return buffer;
(void) userdata;
return emmalloc_malloc(size);
}
void compute_mvp(userdata_t *userdata)
static void _free(void* ptr, void* userdata)
{
userdata->renderer.uniform.mvp.e[0][0] = (2.0f / userdata->width) * userdata->zoom;
userdata->renderer.uniform.mvp.e[1][1] = (2.0f / userdata->height) * userdata->zoom;
userdata->renderer.uniform.mvp.e[0][3] = (2.0f / userdata->width) * userdata->pan.x;
userdata->renderer.uniform.mvp.e[1][3] = (2.0f / userdata->height) * userdata->pan.y;
(void) userdata;
emmalloc_free(ptr);
}
static void refresh_SDF(userdata_t* ud)
{
//Get visible tiles
float view_world_w = sapp_widthf() * ud->zoom;
float view_world_h = sapp_heightf() * ud->zoom;
box_t viewport = (box_t) {
ud->pan_x - view_world_w * 0.5f,
ud->pan_y - view_world_h * 0.5f,
ud->pan_x + view_world_w * 0.5f,
ud->pan_y + view_world_h * 0.5f,
};
bool dirty = false;
if(ud->scene.segment_dirty)
{
pool_view_grow(ud->compute.bindings.views[COMPUTE_VIEWIDX_segments], sizeof(segment_t), ud->scene.shapes.num_segments, ud->scene.shapes.segments);
ud->scene.segment_dirty = false;
dirty = true;
}
if(ud->scene.shape_dirty)
{
pool_view_grow(ud->compute.bindings.views[COMPUTE_VIEWIDX_shapes], sizeof(shape_meta_t), ud->scene.shapes.num_shapes, ud->scene.shapes.shapes);
ud->scene.shape_dirty = false;
dirty = true;
}
if(ud->scene.circle_dirty)
{
pool_view_grow(ud->compute.bindings.views[COMPUTE_VIEWIDX_circles], sizeof(circle_t), ud->scene.circles.num_circles, ud->scene.circles.circles);
ud->scene.circle_dirty = false;
dirty = true;
}
if(dirty)
{
uint32_t tile_count = scene_process_tiles(&ud->scene, ud->pan_x, ud->pan_y, ud->zoom, &ud->compute.bindings);
if(tile_count > 0)
{
pool_view_grow(ud->compute.bindings.views[COMPUTE_VIEWIDX_tiles], sizeof(tile_task_t), tile_count, ud->scene.cache.tiles);
pool_view_grow(ud->compute.bindings.views[COMPUTE_VIEWIDX_indices], sizeof(uint32_t), ud->scene.cache.num_indices, ud->scene.cache.indices);
sg_begin_pass(&ud->compute.pass);
sg_apply_pipeline(ud->compute.pipeline);
sg_apply_bindings(&ud->compute.bindings);
sg_dispatch(tile_count * 16, 16, 1);
sg_end_pass();
}
dirty = false;
}
}
static sg_pass pass = (sg_pass){
.label = "Render pass",
};
static simgui_frame_desc_t imgui_frame = {0};
static void compute_mvp(userdata_t *ud)
{
const float w = sapp_widthf();
const float h = sapp_heightf();
const float z = ud->zoom;
const float px = ud->pan_x;
const float py = ud->pan_y;
float* mvp = ud->renderer.uniforms.mvp;
mvp[0] = (2.0f / w) * z;
mvp[1] = 0.0f;
mvp[2] = 0.0f;
mvp[3] = (2.0f / w) * px;
mvp[4] = 0.0f;
mvp[5] = (2.0f / h) * z;
mvp[6] = 0.0f;
mvp[7] = (2.0f / h) * py;
mvp[8] = 0.0f;
mvp[9] = 0.0f;
mvp[10] = 0.0f;
mvp[11] = 0.0f;
mvp[12] = 0.0f;
mvp[13] = 0.0f;
mvp[14] = 0.0f;
mvp[15] = 1.0f;
}
static void frame(void* _userdata)
{
userdata_t* userdata = (userdata_t*) _userdata;
userdata_t* ud = (userdata_t*) _userdata;
sg_begin_pass(&(sg_pass){
.action = userdata->renderer.clear_pass,
.swapchain = sglue_swapchain(),
});
ud->renderer.uniforms.frame = sapp_frame_count();
ud->renderer.uniforms.time += (uint32_t) ceil(sapp_frame_duration_unfiltered() / 1000.0f);
const uint32_t length = vector_length(&userdata->manager.textures);
if(length > 0)
{
sg_apply_pipeline(userdata->renderer.pipeline);
refresh_SDF(ud);
for(uint32_t i = 0; i < length; i++)
{
texture_t* texture = (texture_t*) vector_get(&userdata->manager.textures, i);
pass.swapchain = sglue_swapchain();
sg_begin_pass(&pass);
if(texture->dirty)
{
const sg_range range = vector_range(&texture->sprites);
sg_buffer buf = sg_query_view_buffer(texture->binding.views[1]);
sg_update_buffer(buf, &range);
texture->dirty = false;
}
imgui_frame.width = sapp_width(),
imgui_frame.height = sapp_height(),
imgui_frame.delta_time = sapp_frame_duration_unfiltered(),
imgui_frame.dpi_scale = sapp_dpi_scale(),
simgui_new_frame(&imgui_frame);
sg_apply_bindings(&texture->binding);
sg_apply_uniforms(0, &SG_RANGE(userdata->renderer.uniform));
igBegin("Framerate", (bool*) true, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
igText("%.0f FPS", 1 / sapp_frame_duration_unfiltered());
igText("%.3fms", sapp_frame_duration_unfiltered() * 1000);
igEnd();
sg_draw(0, 6, vector_length(&texture->sprites));
}
}
sg_apply_pipeline(ud->renderer.pipeline);
sg_apply_uniforms(0, &SG_RANGE(ud->renderer.uniforms));
sg_apply_bindings(&ud->renderer.bindings);
sg_draw(0, 6, 1);
simgui_render();
sg_end_pass();
sg_commit();
}
static void init(void* _userdata)
{
rand_seed(1);
userdata_t* ud = (userdata_t*) _userdata;
userdata_t* userdata = (userdata_t*) _userdata;
sg_desc sgdesc = {
sg_setup(&(sg_desc){
.environment = sglue_environment(),
.logger.func = slog_func,
};
sg_setup(&sgdesc);
if (!sg_isvalid()) {
fprintf(stderr, "Failed to create Sokol GFX context!\n");
exit(-1);
}
.logger.func = log_fn,
.allocator = {
.alloc_fn = _malloc,
.free_fn = _free,
},
});
simgui_setup(&(simgui_desc_t){
.allocator = {
.alloc_fn = _malloc,
.free_fn = _free,
},
});
const vec2f quad[4] = {
{-2.0f, 2.0f}, // bottom left
{2.0f, 2.0f}, // bottom right
{2.0f, -2.0f}, // top right
{-2.0f, -2.0f}, // top left
ud->scene = (scene_t) {0};
if(!scene_init(&ud->scene, (vec2) { 8192.0f, 8192.0f })) return;
ud->compute = (compute_t) {
.pipeline = sg_make_pipeline(&(sg_pipeline_desc){
.compute = true,
.shader = sg_make_shader(&(sg_shader_desc) {
.compute_func = {
.source = (const char*) src_shaders_compute_wgsl,
.entry = "main",
},
.views = {
[COMPUTE_VIEWIDX_segments] = {
.storage_buffer = {
.stage = SG_SHADERSTAGE_COMPUTE,
.wgsl_group1_binding_n = COMPUTE_VIEWIDX_segments,
.readonly = true,
}
},
[COMPUTE_VIEWIDX_shapes] = {
.storage_buffer = {
.stage = SG_SHADERSTAGE_COMPUTE,
.wgsl_group1_binding_n = COMPUTE_VIEWIDX_shapes,
.readonly = true,
}
},
[COMPUTE_VIEWIDX_circles] = {
.storage_buffer = {
.stage = SG_SHADERSTAGE_COMPUTE,
.wgsl_group1_binding_n = COMPUTE_VIEWIDX_circles,
.readonly = true,
}
},
[COMPUTE_VIEWIDX_tiles] = {
.storage_buffer = {
.stage = SG_SHADERSTAGE_COMPUTE,
.wgsl_group1_binding_n = COMPUTE_VIEWIDX_tiles,
.readonly = true,
}
},
[COMPUTE_VIEWIDX_indices] = {
.storage_buffer = {
.stage = SG_SHADERSTAGE_COMPUTE,
.wgsl_group1_binding_n = COMPUTE_VIEWIDX_indices,
.readonly = true,
}
},
[COMPUTE_VIEWIDX_SDF] = {
.storage_image = {
.stage = SG_SHADERSTAGE_COMPUTE,
.wgsl_group1_binding_n = COMPUTE_VIEWIDX_SDF,
.access_format = SG_PIXELFORMAT_R16F,
.image_type = SG_IMAGETYPE_2D,
.writeonly = true,
}
}
},
.label = "SDF Compute Shader",
}),
.label = "SDF Compute Pipeline",
}),
.bindings = (sg_bindings) {
.views[COMPUTE_VIEWIDX_segments] = sg_make_view(&(sg_view_desc) {
.label = "Segments view",
.storage_buffer = {
.buffer = sg_make_buffer(&(sg_buffer_desc) {
.label = "Segment buffer",
.usage = {
.storage_buffer = true,
.stream_update = true,
},
.size = sizeof(segment_t) * INITIAL_SEGMENTS_SIZE,
}),
},
}),
.views[COMPUTE_VIEWIDX_shapes] = sg_make_view(&(sg_view_desc) {
.label = "Shapes metadata view",
.storage_buffer = {
.buffer = sg_make_buffer(&(sg_buffer_desc) {
.label = "Shapes metadata buffer",
.usage = {
.storage_buffer = true,
.stream_update = true,
},
.size = sizeof(shape_meta_t) * INITIAL_SHAPE_SIZE,
}),
},
}),
.views[COMPUTE_VIEWIDX_circles] = sg_make_view(&(sg_view_desc) {
.label = "Circles view",
.storage_buffer = {
.buffer = sg_make_buffer(&(sg_buffer_desc) {
.label = "Circles buffer",
.usage = {
.storage_buffer = true,
.stream_update = true,
},
.size = sizeof(circle_t) * INITIAL_CIRCLE_SIZE,
}),
},
}),
.views[COMPUTE_VIEWIDX_tiles] = sg_make_view(&(sg_view_desc) {
.label = "Tiles rebuild view",
.storage_buffer = {
.buffer = sg_make_buffer(&(sg_buffer_desc) {
.label = "Tiles rebuild buffer",
.usage = {
.storage_buffer = true,
.stream_update = true,
},
.size = sizeof(tile_task_t) * 256,
}),
},
}),
.views[COMPUTE_VIEWIDX_indices] = sg_make_view(&(sg_view_desc) {
.label = "Culling indices view",
.storage_buffer = {
.buffer = sg_make_buffer(&(sg_buffer_desc) {
.label = "Culling indices buffer",
.usage = {
.storage_buffer = true,
.stream_update = true,
},
.size = sizeof(uint32_t) * INITIAL_INDICES_SIZE,
}),
},
}),
.views[COMPUTE_VIEWIDX_SDF] = ud->scene.texture,
},
.pass = { .compute = true, .label = "Compute Pass" }
};
const vec2f uv[4] = {
const vec2 quad[4] = {
{ 0.0f, 1.0f }, // bottom left
{ 1.0f, 1.0f }, // bottom right
{ 1.0f, 0.0f }, // top right
@@ -120,173 +326,137 @@ static void init(void* _userdata)
0, 1, 2, 0, 2, 3,
};
userdata->width = sapp_width();
userdata->height = sapp_height();
userdata->pan = (vec2f) { 0.0f, 0.0f };
userdata->zoom = 2;
sg_shader sprite_shader = sg_make_shader(&(sg_shader_desc) {
ud->renderer = (renderer_t) {
.pipeline = sg_make_pipeline(&(sg_pipeline_desc) {
.shader = sg_make_shader(&(sg_shader_desc) {
.vertex_func = {
.source = (const char*) src_shaders_sprite_wgsl,
.source = (const char*) src_shaders_display_wgsl,
.entry = "vs_main",
},
.fragment_func = {
.source = (const char*) src_shaders_sprite_wgsl,
.source = (const char*) src_shaders_display_wgsl,
.entry = "fs_main",
},
.views = {
[0] = {
.texture = {
.stage = SG_SHADERSTAGE_FRAGMENT,
.image_type = SG_IMAGETYPE_2D,
.wgsl_group1_binding_n = 0,
.sample_type = SG_IMAGESAMPLETYPE_FLOAT,
},
},
[1] = {
.storage_buffer = {
.stage = SG_SHADERSTAGE_VERTEX,
.readonly = true,
.wgsl_group1_binding_n = 2,
},
},
},
.samplers = {
[0] = {
.stage = SG_SHADERSTAGE_FRAGMENT,
.sampler_type = SG_SAMPLERTYPE_FILTERING,
.wgsl_group1_binding_n = 1,
}
},
.texture_sampler_pairs = {
[0] = {
.stage = SG_SHADERSTAGE_FRAGMENT,
.view_slot = 0,
.sampler_slot = 0,
}
},
.uniform_blocks = {
[0] = {
.size = sizeof(uniform_t),
.stage = SG_SHADERSTAGE_VERTEX,
.wgsl_group0_binding_n = 0,
},
},
.attrs[0] = {
.base_type = SG_SHADERATTRBASETYPE_FLOAT,
},
.label = "Sprite shader",
});
userdata->renderer = (renderer_t) {
.clear_pass = (sg_pass_action) {
.colors[0] = { .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }, .load_action = SG_LOADACTION_CLEAR }
.views = {
[DISPLAY_VIEWIDX_SDF] = {
.texture = {
.image_type = SG_IMAGETYPE_2D,
.stage = SG_SHADERSTAGE_FRAGMENT,
.wgsl_group1_binding_n = DISPLAY_VIEWIDX_SDF,
.sample_type = SG_IMAGESAMPLETYPE_FLOAT,
},
.pipeline = sg_make_pipeline(&(sg_pipeline_desc) {
.shader = sprite_shader,
},
},
.uniform_blocks = {
[0] = {
.size = sizeof(display_uniforms),
.stage = SG_SHADERSTAGE_VERTEX,
.wgsl_group0_binding_n = 0,
},
},
.label = "Display Shader",
.samplers[DISPLAY_VIEWIDX_Sampler] = {
.sampler_type = SG_SAMPLERTYPE_FILTERING,
.stage = SG_SHADERSTAGE_FRAGMENT,
.wgsl_group1_binding_n = DISPLAY_VIEWIDX_Sampler,
},
.texture_sampler_pairs[0] = {
.sampler_slot = DISPLAY_VIEWIDX_Sampler,
.view_slot = DISPLAY_VIEWIDX_SDF,
.stage = SG_SHADERSTAGE_FRAGMENT,
},
}),
.cull_mode = SG_CULLMODE_NONE,
.index_type = SG_INDEXTYPE_UINT16,
.layout.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
[1].format = SG_VERTEXFORMAT_FLOAT2,
},
.label = "Sprite pipeline",
.label = "Render pipeline",
}),
.uniform = (uniform_t) {
.mvp = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
.bindings = {
.index_buffer = sg_make_buffer(&(sg_buffer_desc) {
.label = "Index buffer",
.usage.index_buffer = true,
.size = sizeof(indices),
.data = SG_RANGE(indices),
}),
.vertex_buffers = {
[0] = sg_make_buffer(&(sg_buffer_desc) {
.label = "Vertex buffer",
.usage.vertex_buffer = true,
.size = sizeof(quad),
.data = SG_RANGE(quad),
})
},
.views = {
[DISPLAY_VIEWIDX_SDF] = sg_make_view(&(sg_view_desc) {
.texture.image = sg_query_view_image(ud->scene.texture),
.label = "SDF texture view",
}),
},
.samplers = {
[DISPLAY_VIEWIDX_Sampler] = sg_make_sampler(&(sg_sampler_desc) {
.label = "SDF sampler",
}),
}
},
.uniforms = {
.frame = 0,
.time = 0,
.mvp = { 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 },
},
};
compute_mvp(ud);
pass.action = (sg_pass_action) {
.colors[0] = { .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }, .load_action = SG_LOADACTION_CLEAR }
};
gs_init(&userdata->manager);
compute_mvp(userdata);
add_rectangle(&ud->scene, (vec2) { 200.0f, 100.0f }, (vec2) { 100.0f, 150.0f });
add_circle(&ud->scene, (vec2) { 400.0f, 300.0f }, 125.0f);
}
static void cleanup(void* _userdata)
{
userdata_t* userdata = (userdata_t*) _userdata;
userdata_t* ud = (userdata_t*) _userdata;
vector_free(&userdata->manager.textures);
FREE(userdata);
scene_shutdown(&ud->scene);
emmalloc_free(ud);
simgui_shutdown();
sg_shutdown();
}
static void event(const sapp_event* event, void* _userdata)
{
userdata_t* userdata = (userdata_t*) _userdata;
userdata_t* ud = (userdata_t*) _userdata;
switch(event->type)
{
case SAPP_EVENTTYPE_FILES_DROPPED:
uint32_t files = sapp_get_num_dropped_files();
for(uint32_t i = 0; i < files; i++)
{
const uint32_t size = sapp_html5_get_dropped_file_size(i);
if(size > MAX_FILE_SIZE)
{
//Toast error
continue;
if (event->type == SAPP_EVENTTYPE_RESIZED) {
//handle_resize(ud, event);
}
void* buffer = malloc(size);
sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request) {
.buffer = (sapp_range) { .ptr = buffer, .size = size },
.dropped_file_index = i,
.callback = gs_import_file,
.user_data = userdata
});
if (event->type == SAPP_EVENTTYPE_KEY_DOWN) {
//if (handle_key_down(ud, event)) return;
}
break;
case SAPP_EVENTTYPE_RESIZED:
userdata->width = sapp_width();
userdata->height = sapp_height();
compute_mvp(userdata);
break;
case SAPP_EVENTTYPE_MOUSE_DOWN:
if(event->modifiers & SAPP_MODIFIER_RMB)
{
userdata->dragger.dragging = true;
userdata->dragger.origin_x = event->mouse_x;
userdata->dragger.origin_y = event->mouse_y;
}
if (simgui_handle_event(event)) return;
switch (event->type) {
/* case SAPP_EVENTTYPE_MOUSE_DOWN:
handle_mouse_down(ud, event);
break;
case SAPP_EVENTTYPE_MOUSE_UP:
userdata->dragger.dragging = false;
handle_mouse_up(ud, event);
break;
case SAPP_EVENTTYPE_MOUSE_MOVE:
if(userdata->dragger.dragging)
{
userdata->pan.x += event->mouse_dx;
userdata->pan.y -= event->mouse_dy;
compute_mvp(userdata);
}
handle_mouse_move(ud, event);
break;
case SAPP_EVENTTYPE_MOUSE_SCROLL:
if((userdata->zoom >= 6.0f && event->scroll_y > 0.0f) || (userdata->zoom <= 0.1f && event->scroll_y < 0.0f))
return;
const float diff = expf(event->scroll_y * 0.01f);
userdata->zoom = _sg_clamp(userdata->zoom * diff, 0.1f, 6.0f);
compute_mvp(userdata);
break;
handle_scroll_zoom(ud, event);
break; */
default:
break;
}
@@ -294,7 +464,7 @@ static void event(const sapp_event* event, void* _userdata)
sapp_desc sokol_main(int argc, char* argv[])
{
userdata_t* userdata = (userdata_t*) ALLOC(sizeof(userdata_t));
userdata_t* userdata = (userdata_t*) emmalloc_malloc(sizeof(userdata_t));
(void)argc;
(void)argv;
@@ -305,10 +475,10 @@ sapp_desc sokol_main(int argc, char* argv[])
.cleanup_userdata_cb = cleanup,
.event_userdata_cb = event,
.window_title = "Sokol",
.logger.func = slog_func,
.allocator = {
.alloc_fn = smemtrack_alloc,
.free_fn = smemtrack_free,
.alloc_fn = _malloc,
.free_fn = _free,
},
.logger.func = log_fn,
};
}

View File

@@ -1,49 +0,0 @@
#ifndef MATH_IMPL_H
#define MATH_IMPL_H
#include "api.h"
#define PI 3.14159265358979323846
#define PI32 3.14159265359f
typedef struct vec2f {
float x, y;
} vec2f;
typedef struct vec3f {
float x, y, z;
} vec3f;
typedef struct vec4f {
float x, y, z, w;
} vec4f;
typedef struct vec2u {
uint32_t x, y;
} vec2u;
typedef struct vec3u {
uint32_t x, y, z;
} vec3u;
typedef struct vec4u {
uint32_t x, y, z, w;
} vec4u;
typedef struct vec2 {
int32_t x, y;
} vec2;
typedef struct vec3 {
int32_t x, y, z;
} vec3;
typedef struct vec4 {
int32_t x, y, z, w;
} vec4;
typedef struct mat4x4f {
float e[4][4];
} mat4x4f;
typedef struct mat4x4u {
uint32_t e[4][4];
} mat4x4u;
typedef union mat4x4 {
int32_t e[4][4];
} mat4x4;
#endif

36
src/pool.h Normal file
View File

@@ -0,0 +1,36 @@
#include "api.h"
#define MAX_BUFFER_SIZE 1024 * 1024 * 1024 // 1GB
static void pool_buffer_grow(sg_buffer buffer, uint32_t stripe, uint32_t count, void* data)
{
size_t size = sg_query_buffer_size(buffer);
if(size == MAX_BUFFER_SIZE) return sg_update_buffer(buffer, &(sg_range) { data, MAX_BUFFER_SIZE });
if(size < stripe * count)
{
sg_buffer_desc desc = sg_query_buffer_desc(buffer);
while(desc.size < stripe * count) desc.size = fmaxf(MAX_BUFFER_SIZE, size * 2);
sg_uninit_buffer(buffer);
sg_init_buffer(buffer, &desc);
}
sg_update_buffer(buffer, &(sg_range) { data, stripe * count });
}
static void pool_view_grow(sg_view view, uint32_t stripe, uint32_t count, void* data)
{
sg_buffer buffer = sg_query_view_buffer(view);
size_t size = sg_query_buffer_size(buffer);
if(size == MAX_BUFFER_SIZE) return sg_update_buffer(buffer, &(sg_range) { data, MAX_BUFFER_SIZE });
if(size < stripe * count)
{
sg_buffer_desc desc = sg_query_buffer_desc(buffer);
while(desc.size < stripe * count) desc.size = fmaxf(MAX_BUFFER_SIZE, size * 2);
sg_uninit_buffer(buffer);
sg_init_buffer(buffer, &desc);
}
sg_update_buffer(buffer, &(sg_range) { data, stripe * count });
}

View File

@@ -1,66 +0,0 @@
#ifndef RAND_H
#define RAND_H
#include "api.h"
static uint32_t seed;
static uint32_t xorshift32(void);
static void rand_seed(uint32_t _seed);
static uint32_t next_int(void);
static uint32_t next_int_max(uint32_t max);
static uint32_t next_int_minmax(uint32_t min, uint32_t max);
static float next_float(void);
static float next_float_max(float max);
static float next_float_minmax(float min, float max);
static uint32_t xorshift32(void)
{
seed ^= seed<<13;
seed ^= seed>>17;
seed ^= seed<<5;
return seed;
}
static void rand_seed(uint32_t _seed)
{
if(_seed == 0)
return;
seed = _seed;
xorshift32();
}
// PRNG [0-UINT32_MAX]
static uint32_t next_int(void)
{
return xorshift32();
}
// PRNG [0-max]
static uint32_t next_int_max(uint32_t max)
{
return (uint32_t) floorf(xorshift32() / (float) UINT32_MAX * max);
}
// PRNG [min-max]
static uint32_t next_int_minmax(uint32_t min, uint32_t max)
{
const float x = (float) xorshift32() / UINT32_MAX;
//(1.0f - Time) * A + Time * B
return (1.0f - x) * min + x * max;
}
// PRNG [0-1]
static float next_float(void)
{
return (float) xorshift32() / UINT32_MAX;
}
// PRNG [0-max]
static float next_float_max(float max)
{
return (float) xorshift32() / UINT32_MAX * max;
}
// PRNG [min-max]
static float next_float_minmax(float min, float max)
{
const float x = (float) xorshift32() / UINT32_MAX;
return (1.0f - x) * min + x * max;
}
#endif

235
src/shaders/compute.wgsl Normal file
View File

@@ -0,0 +1,235 @@
struct Segment {
p0: vec2f,
p1: vec2f,
p2: vec2f,
p3: vec2f,
}
struct ShapeMeta {
start_segment: u32, //Offset in the segments buffer
segment_count: u32,
bbox_min: vec2f,
bbox_max: vec2f,
}
struct Circle {
center: vec2f,
radius: f32,
}
struct Tile {
world_min: vec2f,
world_max: vec2f,
offset: u32,
circle_count: u32,
count: u32,
};
@group(1) @binding(0) var<storage> segments: array<Segment>;
@group(1) @binding(1) var<storage> shapes: array<ShapeMeta>;
@group(1) @binding(2) var<storage> circles: array<Circle>;
@group(1) @binding(3) var<storage> tiles: array<Tile>;
@group(1) @binding(4) var<storage> indices: array<u32>;
@group(1) @binding(5) var sdf_buffer: texture_storage_2d<r16float, write>;
override TILE_SIZE: u32 = 256u;
// ---------- Cubic Bézier helpers ----------
fn eval_cubic(t: f32, p0: vec2f, p1: vec2f, p2: vec2f, p3: vec2f) -> vec2f {
let mt = 1.0 - t;
let mt2 = mt * mt;
let t2 = t * t;
return mt2 * mt * p0 + 3.0 * mt2 * t * p1 + 3.0 * mt * t2 * p2 + t2 * t * p3;
}
fn eval_cubic_derivative(t: f32, p0: vec2f, p1: vec2f, p2: vec2f, p3: vec2f) -> vec2f {
let mt = 1.0 - t;
return 3.0 * mt * mt * (p1 - p0) + 6.0 * mt * t * (p2 - p1) + 3.0 * t * t * (p3 - p2);
}
// ---------- Distance to a cubic Bézier (sampling + Newton refinement) ----------
fn distance_to_cubic(p: vec2f, seg: Segment) -> f32 {
let p0 = seg.p0; let p1 = seg.p1; let p2 = seg.p2; let p3 = seg.p3;
// Uniform sampling to find a good starting t
let samples = 16u;
var best_t = 0.0;
var best_dist = 1e20;
for (var i = 0u; i < samples; i++) {
let t = f32(i) / f32(samples - 1u);
let q = eval_cubic(t, p0, p1, p2, p3);
let d = distance(p, q);
if d < best_dist {
best_dist = d;
best_t = t;
}
}
// Newton refinement on the squared distance (max 4 iterations)
var t = clamp(best_t, 0.0, 1.0);
for (var iter = 0u; iter < 4u; iter++) {
let B = eval_cubic(t, p0, p1, p2, p3) - p;
let Bp = eval_cubic_derivative(t, p0, p1, p2, p3);
let f = dot(B, Bp); // ½ derivative of |B|²
let df = dot(Bp, Bp) + dot(B, 3.0 * (p2 - p1 + (p3 - p2 - p2 + p1) * 2.0 * t)); // simplified 2nd deriv
if abs(df) < 1e-12 { break; }
let step = f / df;
t = clamp(t - step, 0.0, 1.0);
if abs(step) < 1e-6 { break; }
}
let q_final = eval_cubic(t, p0, p1, p2, p3);
return distance(p, q_final);
}
// ---------- Analytical cubic root solver (for y-crossing) ----------
// Returns number of real roots in [0,1], stored in roots array (up to 3).
fn solve_cubic_in_01(c3: f32, c2: f32, c1: f32, c0: f32, roots: ptr<function, array<f32, 3>>) -> u32 {
// Normalise: c3 * t^3 + c2 * t^2 + c1 * t + c0 = 0
if abs(c3) < 1e-9 {
// Quadratic fallback
if abs(c2) < 1e-9 {
if abs(c1) < 1e-9 { return 0u; }
let t = -c0 / c1;
if t >= 0.0 && t <= 1.0 {
(*roots)[0] = t;
return 1u;
}
return 0u;
}
let disc = c1 * c1 - 4.0 * c2 * c0;
if disc < 0.0 { return 0u; }
let sd = sqrt(disc);
let t0 = (-c1 + sd) / (2.0 * c2);
let t1 = (-c1 - sd) / (2.0 * c2);
var count = 0u;
if t0 >= 0.0 && t0 <= 1.0 { (*roots)[count] = t0; count++; }
if t1 >= 0.0 && t1 <= 1.0 { (*roots)[count] = t1; count++; }
return count;
}
// Depressed cubic: let t = x - c2/(3*c3)
let a = c2 / c3;
let b = c1 / c3;
let c = c0 / c3;
let p = b - a * a / 3.0;
let q = 2.0 * a * a * a / 27.0 - a * b / 3.0 + c;
let disc = q * q / 4.0 + p * p * p / 27.0;
var count = 0u;
let shift = a / 3.0;
if disc > 0.0 {
let sd = sqrt(disc);
let u = sign(-q/2.0 + sd) * pow(abs(-q/2.0 + sd), 1.0/3.0);
let v = sign(-q/2.0 - sd) * pow(abs(-q/2.0 - sd), 1.0/3.0);
let x1 = u + v;
let t = x1 - shift;
if t >= 0.0 && t <= 1.0 { (*roots)[count] = t; count++; }
} else if disc == 0.0 {
let u = sign(-q/2.0) * pow(abs(-q/2.0), 1.0/3.0);
let x1 = 2.0 * u;
let x2 = -u;
let t1 = x1 - shift;
let t2 = x2 - shift;
if t1 >= 0.0 && t1 <= 1.0 { (*roots)[count] = t1; count++; }
if t2 >= 0.0 && t2 <= 1.0 { (*roots)[count] = t2; count++; }
} else {
let phi = acos( -q / (2.0 * sqrt(-p*p*p/27.0)) );
let r = 2.0 * sqrt(-p/3.0);
for (var k = 0u; k < 3u; k++) {
let x = r * cos((phi + 2.0 * 3.1415926535 * f32(k)) / 3.0);
let t = x - shift;
if t >= 0.0 && t <= 1.0 { (*roots)[count] = t; count++; }
}
}
return count;
}
// ---------- Winding number contribution from one cubic segment ----------
fn ray_intersections_cubic(p: vec2f, seg: Segment) -> i32 {
// We cast a horizontal ray from p to +x; find t where y(t) = p.y
let y0 = seg.p0.y - p.y;
let y1 = seg.p1.y - p.y;
let y2 = seg.p2.y - p.y;
let y3 = seg.p3.y - p.y;
// y(t) = (1-t)^3*y0 + 3(1-t)^2 t * y1 + 3(1-t) t^2 * y2 + t^3 * y3 = 0
// Expand polynomial: c3 t^3 + c2 t^2 + c1 t + c0
let c0 = y0;
let c1 = 3.0 * (y1 - y0);
let c2 = 3.0 * (y2 - 2.0 * y1 + y0);
let c3 = y3 - 3.0 * y2 + 3.0 * y1 - y0;
var roots: array<f32, 3>;
let num = solve_cubic_in_01(c3, c2, c1, c0, &roots);
var wind = 0i;
for (var i = 0u; i < num; i++) {
let t = roots[i];
let x = eval_cubic(t, seg.p0, seg.p1, seg.p2, seg.p3).x;
if x > p.x {
let dy = eval_cubic_derivative(t, seg.p0, seg.p1, seg.p2, seg.p3).y;
if dy > 0.0 { wind += 1i; }
else if dy < 0.0 { wind -= 1i; }
}
}
return wind;
}
// ---------- Main compute shader ----------
@compute @workgroup_size(8, 8)
fn main(@builtin(workgroup_id) wg: vec3<u32>, // each workgroup is a tile
@builtin(local_invocation_id) local: vec3<u32>) // each local invocation is a pixel
{
let tile_idx = wg.x / 16u;
let tile = tiles[tile_idx];
let px = (wg.x % 16u) * 8u + local.x;
let py = wg.y * 8u + local.y;
if (px >= TILE_SIZE || py >= TILE_SIZE) {
return;
}
let uv = (vec2f(f32(px), f32(py)) + 0.5) / f32(TILE_SIZE);
let world = mix(tile.world_min, tile.world_max, uv);
var minDist: f32 = 1e20;
var sdf: f32 = 1e20;
var winding: i32 = 0;
for (var s = 0u; s < tile.count; s++) {
// Use a pre-culled shape range per tile to reduce the per-pixel processing time
let index = indices[tile.offset + s];
// If the index is lower than the circle count, it mean we are processing circles
if(index < tile.circle_count)
{
let c = circles[index];
sdf = min(sdf, length(world - c.center) - c.radius);
}
else
{
let shape = shapes[index];
/*let bbox_min = shape.bbox_min;
let bbox_max = shape.bbox_max;
let dx = max(bbox_min.x - world.x, max(0.0, world.x - bbox_max.x));
let dy = max(bbox_min.y - world.y, max(0.0, world.y - bbox_max.y));
let dist_to_box = sqrt(dx * dx + dy * dy);
if dist_to_box >= minDist {
continue;
}*/
// Process all segments of the shape
for (var i = shape.start_segment; i < shape.start_segment + shape.segment_count; i++) {
let seg = segments[i];
let d = distance_to_cubic(world, seg);
minDist = min(minDist, d);
winding += ray_intersections_cubic(world, seg);
}
let sign = select(1.0, -1.0, winding != 0i);
sdf = min(sdf, sign * minDist);
}
}
textureStore(sdf_buffer, vec2(u32(world.x), u32(world.y)), vec4f(sdf, 0.0, 0.0, 1.0));
}

20
src/shaders/display.wgsl Normal file
View File

@@ -0,0 +1,20 @@
struct Uniforms {
time: u32,
frame: u32,
mvp: mat4x4f,
}
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@group(1) @binding(0) var sdf : texture_2d<f32>;
@group(1) @binding(1) var sdf_sampler : sampler;
@vertex fn vs_main(@location(0) position: vec2f) -> @builtin(position) vec4f
{
return vec4f(position, 0.0, 1.0) * uniforms.mvp;
}
@fragment fn fs_main(@builtin(position) position: vec4f) -> @location(0) vec4f
{
return textureSample(sdf, sdf_sampler, position.xy);
}

View File

@@ -1,56 +0,0 @@
struct Sprite {
matrix: mat4x4f,
};
struct VsUniform {
mvp: mat4x4f,
};
struct VsI { //Vertex shader input
@builtin(instance_index) instance: u32,
@location(0) position: vec2f,
@location(1) uv: vec2f,
};
struct Vs2Fs { //Vertex shader to Fragment shader
@builtin(position) pos: vec4f,
@location(0) @interpolate(linear) uv: vec2f,
};
struct FsO { //Fragment shader output
@location(0) color: vec4f,
};
@binding(0) @group(0) var<uniform> vs_uniforms: VsUniform;
@binding(0) @group(1) var tex: texture_2d<f32>;
@binding(1) @group(1) var samp: sampler;
@binding(2) @group(1) var<storage> sprites: array<Sprite>;
@vertex fn vs_main(input: VsI) -> Vs2Fs {
var output: Vs2Fs;
output.pos = vec4f(input.position.x, input.position.y, 0, 0) * sprites[input.instance].matrix * vs_uniforms.mvp;
output.uv = input.uv;
return output;
}
// Convert a 32bit uint color (hex representation) into a normalized vec4f
/*fn convertColor(input: u32) -> vec4f {
let r: f32 = f32(((input >> 0) & 0xff));
let g: f32 = f32(((input >> 8) & 0xff));
let b: f32 = f32(((input >> 16) & 0xff));
let a: f32 = f32(((input >> 24) & 0xff));
return vec4f(r / 255, g / 255, b / 255, a / 255);
}*/
// Get the texture array index from the UV
/*fn indexFromCoord(uv: vec2f, width: u32, height: u32) -> u32 {
let x: u32 = clamp(floor(uv.x * f32(width)), 0, width);
let y: u32 = clamp(floor(uv.y * f32(height)), 0, height);
return y * width + x;
}*/
@fragment fn fs_main(input: Vs2Fs) -> FsO {
var output: FsO;
output.color = textureSample(tex, samp, input.uv);
return output;
}

363
src/shape.h Normal file
View File

@@ -0,0 +1,363 @@
#include "api.h"
typedef struct uvec2 { uint32_t x, y; } uvec2;
typedef struct vec2 { float x, y; } vec2;
typedef struct ubox_t {
uint32_t min_x, min_y, max_x, max_y;
} ubox_t;
typedef struct box_t {
float min_x, min_y, max_x, max_y;
} box_t;
#define BOX_INTERSECTS(a, b) !(b.min_x > a.max_x \
|| b.max_x < a.min_x \
|| b.min_y > a.max_y \
|| b.max_y < a.min_y)
#define CIRCLE_INTERSECTS(box, circle) !(circle.center.x - circle.radius > box.max_x \
|| circle.center.x + circle.radius < box.min_x \
|| circle.center.y - circle.radius > box.max_y \
|| circle.center.y + circle.radius < box.min_y)
typedef struct segment_t {
vec2 p0, p1, p2, p3; // cubic Bézier: start, control1, control2, end
} segment_t;
typedef struct shape_meta_t {
uint32_t start_segment; // index into global segments array
uint32_t segment_count; // number of segments forming this closed loop
box_t aabb;
} shape_meta_t;
typedef struct circle_t {
vec2 center;
float radius;
} circle_t;
#define TILE_SIZE 256 // texels per tile
#define TILE_COUNT(size) (uint32_t)ceilf(size.x / TILE_SIZE) * (uint32_t)ceilf(size.y / TILE_SIZE)
#define INITIAL_SHAPE_SIZE 256
#define INITIAL_SEGMENTS_SIZE 8192
#define INITIAL_CIRCLE_SIZE 1024
#define INITIAL_INDICES_SIZE 16384
// Tile metadata for targetted updates
typedef struct tile_task_t {
box_t bounds;
uint32_t offset;
uint32_t circle_count;
uint32_t count;
} tile_task_t;
typedef struct scene_t {
vec2 world_size;
struct {
uint32_t num_shapes;
uint32_t max_shapes;
shape_meta_t* shapes;
uint32_t num_segments;
uint32_t max_segments;
segment_t* segments;
} shapes;
struct {
uint32_t num_circles;
uint32_t max_circles;
circle_t* circles;
} circles;
struct {
tile_task_t* tiles; // Array of tiles metadata for the compute buffer
bool* tile_dirty; // Array of tiles state, since this info don't needs to be uploaded to the GPU, it is stored separately
uint32_t max_indices;
uint32_t num_indices;
uint32_t* indices; // Array of shapes/circles indices per tile, pre culled on the CPU side
} cache;
sg_view texture; //Layer count depends on the world size so the texture has to be allocate on the scene side
bool shape_dirty, segment_dirty, circle_dirty;
} scene_t;
// Initialise a scene_t with a given capacity for shapes and segments.
static uint32_t scene_init(scene_t* s, vec2 world_size) {
s->shapes.num_shapes = 0;
s->shapes.max_shapes = INITIAL_SHAPE_SIZE;
s->shapes.shapes = (shape_meta_t*) emmalloc_malloc(INITIAL_SHAPE_SIZE * sizeof(shape_meta_t));
s->shapes.num_segments = 0;
s->shapes.max_segments = INITIAL_SEGMENTS_SIZE;
s->shapes.segments = (segment_t*) emmalloc_malloc(INITIAL_SEGMENTS_SIZE * sizeof(segment_t));
s->circles.num_circles = 0;
s->circles.max_circles = INITIAL_SEGMENTS_SIZE;
s->circles.circles = (circle_t*) emmalloc_malloc(INITIAL_CIRCLE_SIZE * sizeof(circle_t));
const uint32_t tile_count_x = (uint32_t)ceilf(world_size.x / TILE_SIZE), tile_count_y = (uint32_t)ceilf(world_size.y / TILE_SIZE);
const uint32_t tile_count = tile_count_x * tile_count_y;
s->cache.tiles = (tile_task_t*) emmalloc_malloc(sizeof(tile_task_t) * tile_count);
s->cache.tile_dirty = (bool*) emmalloc_malloc(sizeof(bool) * tile_count);
uint32_t idx = 0;
for(uint32_t y = 0; y < tile_count_y; y++)
{
for(uint32_t x = 0; x < tile_count_x; x++)
{
s->cache.tile_dirty[idx] = true;
idx++;
}
}
s->cache.max_indices = INITIAL_INDICES_SIZE;
s->cache.num_indices = 0;
s->cache.indices = (uint32_t*) emmalloc_malloc(sizeof(uint32_t) * INITIAL_INDICES_SIZE);
s->world_size = world_size;
s->texture = sg_make_view(&(sg_view_desc) {
.label = "SDF tiles view",
.storage_image = {
.image = sg_make_image(&(sg_image_desc) {
.width = (uint32_t) ceilf(world_size.x),
.height = (uint32_t) ceilf(world_size.y),
.label = "SDF tiles texture",
.pixel_format = SG_PIXELFORMAT_R16F,
.type = SG_IMAGETYPE_2D,
.usage = { .storage_image = true },
}),
},
});
if (!s->shapes.shapes || !s->shapes.segments || !s->circles.circles || !s->cache.indices || !s->cache.tiles || !s->cache.tile_dirty) return 0; // allocation failure
return 1;
}
#define COORD_TO_INDEX(x, y, scene) y * ceilf(s->world_size.x / TILE_SIZE) + x
static void scene_compute_culling(scene_t* s, tile_task_t* task)
{
task->count = 0;
for(uint32_t i = 0; i < s->circles.num_circles; i++)
{
if(CIRCLE_INTERSECTS(task->bounds, s->circles.circles[i]))
{
if(s->cache.num_indices >= s->cache.max_indices)
{
s->cache.max_indices *= 2;
s->cache.indices = emmalloc_realloc(s->cache.indices, s->cache.max_indices * sizeof(uint32_t));
}
s->cache.indices[s->cache.num_indices++] = i;
task->count++;
}
}
task->circle_count = task->count;
for(uint32_t i = 0; i < s->shapes.num_shapes; i++)
{
if(BOX_INTERSECTS(task->bounds, s->shapes.shapes[i].aabb))
{
if(s->cache.num_indices >= s->cache.max_indices)
{
s->cache.max_indices *= 2;
s->cache.indices = emmalloc_realloc(s->cache.indices, s->cache.max_indices * sizeof(uint32_t));
}
s->cache.indices[s->cache.num_indices++] = i;
task->count++;
}
}
}
// Find the proper LOD, the relevant tiles then run the culling process for the dirty tiles and upload data to the GPU for SDF rendering
static uint32_t scene_process_tiles(scene_t* s, float pan_x, float pan_y, float zoom, sg_bindings* bindings)
{
const float width = (float)sapp_width(), height = (float) sapp_height();
const float wpp = fmaxf(s->world_size.y / zoom / height, s->world_size.x / zoom / width); //World point per pixel
float view_w = width * wpp, view_h = height * wpp;
box_t frustum = {
.min_x = ceilf((pan_x - view_w * 0.5) / TILE_SIZE),
.min_y = ceilf((pan_y - view_h * 0.5) / TILE_SIZE),
.max_x = ceilf((pan_x + view_w * 0.5) / TILE_SIZE),
.max_y = ceilf((pan_y + view_h * 0.5) / TILE_SIZE)
};
uint32_t dirty_tile_count = 0, offset = 0;
for(uint32_t y = frustum.min_y; y < frustum.max_y; y++)
{
for(uint32_t x = frustum.min_x; x < frustum.max_x; x++)
{
uint32_t idx = COORD_TO_INDEX(x, y, s);
if(s->cache.tile_dirty[idx])
{
tile_task_t* task = &s->cache.tiles[dirty_tile_count++];
task->bounds.min_x = x * TILE_SIZE;
task->bounds.min_y = y * TILE_SIZE;
task->bounds.max_x = fminf((x + 1) * TILE_SIZE, s->world_size.x);
task->bounds.max_y = fminf((y + 1) * TILE_SIZE, s->world_size.y);
task->offset = offset;
scene_compute_culling(s, task);
offset += task->count;
s->cache.tile_dirty[idx] = false;
}
}
}
return dirty_tile_count;
}
// Append a segment, returns its index. (Reallocs if needed, simplified.)
static uint32_t scene_add_segment(scene_t* s, segment_t seg) {
if (s->shapes.num_segments >= s->shapes.max_segments) {
int new_cap = s->shapes.max_segments * 2;
segment_t* tmp = (segment_t*) realloc(s->shapes.segments, new_cap * sizeof(segment_t));
if (!tmp) return -1;
s->shapes.segments = tmp;
s->shapes.max_segments = new_cap;
}
int idx = s->shapes.num_segments++;
s->shapes.segments[idx] = seg;
return idx;
}
// Append a shape (meta data) and return its index.
static uint32_t scene_add_shape(scene_t* s, shape_meta_t meta) {
if (s->shapes.num_shapes >= s->shapes.max_shapes) {
int new_cap = s->shapes.max_shapes * 2;
shape_meta_t* tmp = (shape_meta_t*) emmalloc_realloc(s->shapes.shapes, new_cap * sizeof(shape_meta_t));
if (!tmp) return -1;
s->shapes.shapes = tmp;
s->shapes.max_shapes = new_cap;
}
int idx = s->shapes.num_shapes++;
s->shapes.shapes[idx] = meta;
return idx;
}
static uint32_t scene_shutdown(scene_t* s) {
emmalloc_free(s->shapes.segments);
emmalloc_free(s->shapes.shapes);
emmalloc_free(s->circles.circles);
emmalloc_free(s->cache.indices);
emmalloc_free(s->cache.tiles);
emmalloc_free(s->cache.tile_dirty);
emmalloc_free(s);
return 1;
}
// Compute axis-aligned bounding box for a set of segments (used after adding a shape).
static void compute_bbox(segment_t* segs, uint32_t start, uint32_t count, float* minx, float* miny, float* maxx, float* maxy) {
float mx = 1e30f, my = 1e30f, Mx = -1e30f, My = -1e30f;
for (int i = 0; i < count; i++) {
segment_t s = segs[start + i];
float xs[4] = {s.p0.x, s.p1.x, s.p2.x, s.p3.x};
float ys[4] = {s.p0.y, s.p1.y, s.p2.y, s.p3.y};
for (int j = 0; j < 4; j++) {
if (xs[j] < mx) mx = xs[j];
if (xs[j] > Mx) Mx = xs[j];
if (ys[j] < my) my = ys[j];
if (ys[j] > My) My = ys[j];
}
}
*minx = mx; *miny = my; *maxx = Mx; *maxy = My;
}
// Add an axisaligned rectangle centred at `center` with given width and height.
// Returns the shape index, or -1 on error.
uint32_t add_rectangle(scene_t* s, vec2 center, vec2 size) {
float hw = size.x * 0.5f, hh = size.y * 0.5f;
vec2 corners[4] = {
{center.x - hw, center.y - hh}, // bottomleft
{center.x + hw, center.y - hh}, // bottomright
{center.x + hw, center.y + hh}, // topright
{center.x - hw, center.y + hh} // topleft
};
// Four straight segments, control points = the corners themselves.
segment_t segs[4];
for (int i = 0; i < 4; i++)
{
int next = (i + 1) % 4;
segs[i].p0 = corners[i];
segs[i].p1 = corners[i]; // start = control1 → straight line
segs[i].p2 = corners[next]; // control2 = end
segs[i].p3 = corners[next];
}
int start = scene_add_segment(s, segs[0]);
if (start < 0) return -1;
for (int i = 1; i < 4; i++)
if (scene_add_segment(s, segs[i]) < 0) return -1;
shape_meta_t meta;
meta.start_segment = start;
meta.segment_count = 4;
meta.aabb = (box_t) { center.x - hw, center.y - hh, center.x + hw, center.y + hh };
return scene_add_shape(s, meta);
}
// Add a circle centred at `center` with given radius.
// approximated by 4 cubic Bézier segments (one per quadrant).
// Returns shape index or -1.
uint32_t add_circle_as_shape(scene_t* s, vec2 center, float radius) {
const float k = 0.552284749831f; // magic constant for 90° arc (4/3 * (sqrt(2)-1))
float rk = radius * k;
// Start at rightmost point (0°), moving counterclockwise.
vec2 p0 = {center.x + radius, center.y};
// Quadrant 1 (0° to 90°): from right to top
segment_t seg1;
seg1.p0 = p0;
seg1.p1 = (vec2) {p0.x, p0.y + rk};
seg1.p2 = (vec2) {center.x + rk, center.y + radius};
seg1.p3 = (vec2) {center.x, center.y + radius};
// Quadrant 2 (90° to 180°): top to left
segment_t seg2;
seg2.p0 = seg1.p3;
seg2.p1 = (vec2) {center.x - rk, center.y + radius};
seg2.p2 = (vec2) {center.x - radius, center.y + rk};
seg2.p3 = (vec2) {center.x - radius, center.y};
// Quadrant 3 (180° to 270°): left to bottom
segment_t seg3;
seg3.p0 = seg2.p3;
seg3.p1 = (vec2) {center.x - radius, center.y - rk};
seg3.p2 = (vec2) {center.x - rk, center.y - radius};
seg3.p3 = (vec2) {center.x, center.y - radius};
// Quadrant 4 (270° to 360°): bottom to right
segment_t seg4;
seg4.p0 = seg3.p3;
seg4.p1 = (vec2) {center.x + rk, center.y - radius};
seg4.p2 = (vec2) {center.x + radius, center.y - rk};
seg4.p3 = p0; // closes back to rightmost point
int start = scene_add_segment(s, seg1);
if (start < 0) return -1;
if (scene_add_segment(s, seg2) < 0) return -1;
if (scene_add_segment(s, seg3) < 0) return -1;
if (scene_add_segment(s, seg4) < 0) return -1;
shape_meta_t meta;
meta.start_segment = start;
meta.segment_count = 4;
compute_bbox(s->shapes.segments, start, 4, &meta.aabb.min_x, &meta.aabb.min_y, &meta.aabb.max_x, &meta.aabb.max_y);
return scene_add_shape(s, meta);
}
uint32_t add_circle(scene_t* s, vec2 center, float radius) {
s->circles.circles[s->circles.num_circles++] = (circle_t) { center, radius };
return s->circles.num_circles - 1;
}

View File

@@ -1,79 +0,0 @@
#ifndef SPRITE_H
#define SPRITE_H
#include "api.h"
#define KB (1024u)
#define MB (1024u * KB)
#define GB (1024u * MB)
#define MAX_FILE_SIZE (10u * MB)
typedef struct texture_t {
uint32_t id; //Texture ID
sg_bindings binding; //Texture bindings (texture, sampler and buffer)
vector_t sprites;
bool dirty;
} texture_t;
typedef struct sprite_t {
mat4x4f transform;
} sprite_t;
typedef struct manager_t {
vector_t textures;
} manager_t;
typedef struct loader_t {
uint8_t buffer[MAX_FILE_SIZE];
} loader_t;
typedef void (*ForEachTexture)(texture_t *texture, void *userdata);
typedef void (*ForEachSprite)(sprite_t *sprite, void *userdata);
void gs_init(manager_t *manager);
void gs_shutdown(manager_t *manager);
void gs_import_file(const sapp_html5_fetch_response *response);
void gs_each_textures(manager_t *manager, ForEachTexture callback, void *userdata);
void gs_each_sprites(manager_t *manager, ForEachSprite callback, void *userdata);
void gs_init(manager_t *manager)
{
manager->textures = vector_create(sizeof(texture_t));
}
void gs_shutdown(manager_t *manager)
{
vector_free(&manager->textures);
}
void gs_import_file(const sapp_html5_fetch_response *response)
{
const char* filename = sapp_get_dropped_file_path(response->file_index);
if(response->succeeded)
{
}
else
{
//Toast error
}
}
void gs_each_textures(manager_t *manager, ForEachTexture callback, void *userdata)
{
for(uint32_t i = 0; i < manager->textures.size; i++)
callback((texture_t*) manager->textures.data[i], userdata);
}
void gs_each_sprites(manager_t *manager, ForEachSprite callback, void *userdata)
{
for(uint32_t i = 0; i < manager->textures.size; i++)
{
const texture_t* texture = (texture_t*) manager->textures.data[i];
for(uint32_t j = 0; j < texture->sprites.size; j++)
{
callback((sprite_t*) texture->sprites.data[j], userdata);
}
}
}
#endif

View File

@@ -1,241 +0,0 @@
#ifndef UTIL_H
#define UTIL_H
#include "api.h"
/*typedef struct linked_list_t {
linked_item_t *first, *last;
uint16_t size;
} linked_list_t;
typedef struct linked_item_t {
linked_item_t *next, *prev;
void *data;
} linked_item_t;
typedef void (*linked_list_callback)(void *item);
//Currently, these are the only method required.
//Many more could be implemented but this is unnecessary.
static void l_list_push(linked_list_t *l_list, void *item);
static void l_list_append(linked_list_t *l_list, void *item);
static void* l_list_pop(linked_list_t *l_list);
static void* l_list_unppend(linked_list_t *l_list);
static void l_list_each(linked_list_t *l_list, linked_list_callback callback);
static inline void l_list_push(linked_list_t *l_list, void *item)
{
linked_item_t l_item = (linked_item_t) { .data = item, .prev = l_list->last, .next = nullptr };
if(l_list->first == nullptr)
l_list->first = &l_item;
l_list->last->next = &l_item;
l_list->last = &l_item;
l_list->size++;
}
static inline void l_list_append(linked_list_t *l_list, void *item)
{
linked_item_t l_item = (linked_item_t) { .data = item, .prev = nullptr, .next = l_list->last };
if(l_list->last == nullptr)
l_list->last = &l_item;
l_list->first->prev = &l_item;
l_list->first = &l_item;
l_list->size++;
}
static inline void* l_list_pop(linked_list_t *l_list)
{
if(l_list->last == nullptr)
return;
if(l_list->first == l_list->last)
l_list->first = nullptr;
linked_item_t *item = l_list->last->prev;
l_list->last->prev = nullptr;
l_list->last = item;
l_list->size--;
}
static inline void* l_list_unppend(linked_list_t *l_list)
{
if(l_list->first == nullptr)
return;
if(l_list->last == l_list->first)
l_list->last = nullptr;
linked_item_t *item = l_list->first->next;
l_list->first->next = nullptr;
l_list->first = item;
l_list->size--;
}
static inline void l_list_each(linked_list_t *l_list, linked_list_callback callback)
{
}*/
typedef struct vector_t {
void **data; //Memory pool
uint16_t stripe; //Bit per item
uint32_t size; //Current amount of items
uint32_t capacity; //Max capacity
} vector_t;
#define MAX_BUCKET_SIZE (0xffffffffu)
#define MAX_STRIPE_SIZE (0xffffffu)
#define FIXED_START (0xfffu)
static vector_t vector_create(uint32_t stripe); //Create a new vector with a default size
static void vector_clear(vector_t *vector);
static void vector_free(vector_t *vector);
static uint32_t vector_length(vector_t *vector);
static void* vector_get(vector_t *vector, uint32_t index);
static void vector_set(vector_t *vector, uint32_t index, void *data);
static uint32_t vector_push(vector_t *vector, void *data);
static sg_range vector_range(vector_t *vector);
static vector_t vector_create(uint32_t stripe)
{
assert(stripe >= sizeof(uint32_t));
assert(stripe <= MAX_STRIPE_SIZE);
assert(FIXED_START * stripe <= MAX_BUCKET_SIZE);
return (vector_t) {
.capacity = FIXED_START,
.size = 0,
.stripe = stripe,
.data = (void**) malloc(FIXED_START * stripe),
};
}
static void vector_clear(vector_t *vector)
{
vector->size = 0;
}
static void vector_free(vector_t *vector)
{
free(vector->data);
}
static uint32_t vector_length(vector_t *vector)
{
return vector->size;
}
static void* vector_get(vector_t *vector, uint32_t index)
{
assert(index > 0);
assert(index < vector->size);
return vector->data[index * vector->stripe];
}
static void vector_set(vector_t *vector, uint32_t index, void *data)
{
assert(index > 0);
assert(index < vector->size);
vector->data[index * vector->stripe] = data;
}
static uint32_t vector_push(vector_t *vector, void *data)
{
if(vector->size >= vector->capacity)
{
vector->capacity *= 2;
vector->data = (void**) realloc(vector->data, vector->capacity * vector->stripe);
}
return vector->size++;
}
static sg_range vector_range(vector_t *vector)
{
return (sg_range) {
.ptr = vector->data,
.size = vector->size,
};
}
typedef struct mem_pool_t {
void **data; //Memory pool
uint32_t stripe; //Bit per item
uint32_t size; //Current amount of items
uint32_t capacity; //Max capacity
uint32_t free; //Linked list of available indices
} mem_pool_t;
static mem_pool_t pool_create_default(uint32_t stripe); //Create a new memory pool with a default size
static mem_pool_t pool_create(uint32_t stripe, uint32_t size); //Create a new memory pool with a default size
static void pool_clear(mem_pool_t *pool);
static void pool_free(mem_pool_t *pool);
static const uint32_t pool_add(mem_pool_t *pool); //Request a new free index
static const void* pool_get(mem_pool_t *pool, uint32_t index); //Get the pointer
static void pool_remove(mem_pool_t *pool, uint32_t index); //Flag the given index as free
static mem_pool_t pool_create_default(uint32_t stripe)
{
return pool_create(stripe, FIXED_START);
}
static mem_pool_t pool_create(uint32_t stripe, uint32_t size)
{
assert(stripe >= sizeof(uint32_t));
assert(stripe <= MAX_STRIPE_SIZE);
assert(size * stripe <= MAX_BUCKET_SIZE);
return (mem_pool_t) {
.capacity = size,
.size = 0,
.stripe = stripe,
.free = UINT32_MAX,
.data = (void**) malloc(size * stripe),
};
}
static void pool_clear(mem_pool_t *pool)
{
pool->size = 0;
pool->free = UINT32_MAX;
}
static void pool_free(mem_pool_t *pool)
{
free(pool->data);
}
static const uint32_t pool_add(mem_pool_t *pool)
{
if(pool->free != UINT32_MAX)
{
const uint32_t index = pool->free;
pool->free = (uint32_t) pool->data[index * pool->stripe];
return index;
}
else
{
if(pool->size >= pool->capacity)
{
pool->capacity *= 2;
pool->data = (void**) realloc(pool->data, pool->capacity * pool->stripe);
}
return pool->size++;
}
}
static const void* pool_get(mem_pool_t *pool, uint32_t index)
{
assert(index > 0);
assert(index < pool->capacity);
return pool->data[index * pool->stripe];
}
static void pool_remove(mem_pool_t *pool, uint32_t index)
{
const uint32_t pos = index * pool->stripe;
pool->data[pos] = (void*) pool->free;
pool->free = index;
}
#endif