Compare commits

..

10 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
21 changed files with 1903 additions and 2857 deletions

View File

@@ -9,6 +9,7 @@ A browser-based world map creation tool (like Wonderdraft/Inkarnate). C99 compil
- **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`
@@ -16,9 +17,13 @@ A browser-based world map creation tool (like Wonderdraft/Inkarnate). C99 compil
- 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, input (zoom/pan/drag)
- `src/api.h` — central include hub, backend defines
- `src/sprite.h`sprite batching, texture manager, file import stubs
- `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

View File

@@ -1,29 +1,6 @@
# Cartograph
A browser-based world map creation tool inspired by Wonderdraft and Inkarnate. Uses shape-based terrain generation with procedural detail.
## Features (planned)
- **Shapes** — the fundamental building block. Each shape has:
- **Height** — additive (raise terrain) or subtractive (lower terrain)
- **Biome** — determines the sampled texture applied to the shape
- **Intensity** — blending weight of the biome texture
- **Roughness** — edge noise amount (voronoi-like jagged edges at maximum)
- **Shape editing** — select, move, rotate, scale individual shapes
- **Groups** — combine shapes into groups for batch operations
- **Shake landmass** — procedurally decompose a shape into a more complex set of shapes
- **Viewport** — zoom and pan across the map canvas
## Tech stack
| Layer | Library |
|---|---|
| Language | C (C99) |
| Compiler | [Emscripten](https://emscripten.org/) (emcc) → WebAssembly |
| Graphics | [Sokol](https://github.com/floooh/sokol) with WebGPU backend |
| UI | [Dear ImGui](https://github.com/ocornut/imgui) via [cimgui](https://github.com/cimgui/cimgui) |
| Math | [cglm](https://github.com/recp/cglm) |
| Shaders | WGSL (compiled to C headers via `xxd`) |
A browser-based world map creation tool inspired by Wonderdraft and Inkarnate. Uses a hierarchical tile-based SDF rendering.
## Build
@@ -40,24 +17,6 @@ make debug
Output is `app.html`, served by Emscripten's built-in web server or any static server.
## Project structure
```
src/
main.c Entry point, sokol init, render loop, input handling
api.h Central include — all library headers and project-wide defines
sprite.h Sprite batching, texture management, file import
util.h Vector (dynamic array) and memory pool data structures
rand.h Xorshift32 PRNG utilities
shaders/ WGSL shader sources
generated/ xxd-generated C headers from shaders
lib/
sokol/ Sokol single-file headers (gfx, app, glue, log)
imgui/ Dear ImGui + cimgui
cglm/ C linear math library
util/ Sokol utility headers (memtrack, imgui integration)
```
## License
MIT

View File

@@ -5,12 +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"
@@ -59,4 +59,14 @@ 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

@@ -31,6 +31,7 @@ EMCC_FLAGS = --use-port=emdawnwebgpu \
-sALLOW_MEMORY_GROWTH \
-msimd128 \
-sFILESYSTEM=0 \
-sMALLOC=emmalloc \
-flto
# Shell template
@@ -45,6 +46,7 @@ $(TARGET): $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES) $(SHE
-o $(TARGET) \
$(EMCC_FLAGS) \
-O3 \
--closure 1 \
-I$(LIB_DIR)/sokol \
-I$(LIB_DIR)/imgui \
-I$(LIB_DIR)/imgui/imgui \
@@ -65,7 +67,8 @@ debug: $(FETCH) $(SHADER_HEADERS) $(C_SOURCES) $(IMGUI_SOURCES) $(CGLM_SOURCES)
$(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 \

View File

@@ -1,41 +1,59 @@
#ifndef API_DEFINITION
#define API_DEFINITION
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
#define SOKOL_IMPL
#define SOKOL_WGPU
#define SOKOL_IMGUI_IMPL
#define SOKOL_VALIDATE_NON_FATAL
#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 "sokol_shape.h"
#define ALLOC(arg) smemtrack_alloc(arg, NULL)
#define FREE(arg) smemtrack_free(arg, NULL)
#include "cglm/cglm.h"
#include "rand.h"
#include "generated/sprite.h"
#include "generated/shape.h"
#include "util.h"
#include "shape.h"
#include "spatial.h"
#include "history.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,95 +0,0 @@
unsigned char src_shaders_shape_wgsl[] = {
0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x56, 0x73, 0x55, 0x6e, 0x69,
0x66, 0x6f, 0x72, 0x6d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d,
0x76, 0x70, 0x3a, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x78, 0x34, 0x66, 0x2c,
0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20,
0x53, 0x68, 0x61, 0x70, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d,
0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x78, 0x34,
0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x73, 0x65, 0x5f,
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66,
0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a,
0x20, 0x75, 0x33, 0x32, 0x2c, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74,
0x72, 0x75, 0x63, 0x74, 0x20, 0x56, 0x73, 0x49, 0x6e, 0x20, 0x7b, 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, 0x0a, 0x7d, 0x3b,
0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x56, 0x73, 0x32,
0x46, 0x73, 0x20, 0x7b, 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, 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, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x2c, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a,
0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x46, 0x73, 0x4f, 0x75, 0x74,
0x20, 0x7b, 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, 0x0a, 0x7d, 0x3b,
0x0a, 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,
0x0a, 0x40, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x31, 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,
0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72,
0x6d, 0x3a, 0x20, 0x53, 0x68, 0x61, 0x70, 0x65, 0x55, 0x6e, 0x69, 0x66,
0x6f, 0x72, 0x6d, 0x3b, 0x0a, 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, 0x6e,
0x29, 0x20, 0x2d, 0x3e, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x20, 0x7b,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x3a, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x3b, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x6c,
0x64, 0x5f, 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, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20,
0x2a, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x66,
0x6f, 0x72, 0x6d, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72,
0x6d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75,
0x74, 0x2e, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x77, 0x6f, 0x72, 0x6c,
0x64, 0x5f, 0x70, 0x6f, 0x73, 0x20, 0x2a, 0x20, 0x76, 0x73, 0x5f, 0x75,
0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6d, 0x76, 0x70, 0x3b,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x68, 0x61,
0x70, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x73,
0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x75, 0x29, 0x20,
0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d,
0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20,
0x30, 0x2e, 0x38, 0x34, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31,
0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65,
0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x68, 0x61, 0x70,
0x65, 0x5f, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x73, 0x74,
0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x75, 0x29, 0x20, 0x7b,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20,
0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f,
0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x62, 0x61, 0x73, 0x65,
0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x2a, 0x20, 0x31, 0x2e, 0x35,
0x2c, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x30, 0x2e, 0x30, 0x29,
0x2c, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x31, 0x2e, 0x30, 0x29,
0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73,
0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
0x20, 0x3d, 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x69,
0x66, 0x6f, 0x72, 0x6d, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
0x6c, 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 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, 0x75, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76,
0x61, 0x72, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x46,
0x73, 0x4f, 0x75, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d,
0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b, 0x0a, 0x7d, 0x0a
};
unsigned int src_shaders_shape_wgsl_len = 1103;

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,254 +0,0 @@
#ifndef HISTORY_H
#define HISTORY_H
#include "api.h"
// Each property kind we can undo/redo independently
typedef enum {
HIST_POSITION,
HIST_SCALE,
HIST_ROTATION,
HIST_COLOR,
} hist_prop_t;
// One property change on one shape (old → new)
typedef struct hist_change_t {
int shape_index;
hist_prop_t prop;
float old_val[4];
float new_val[4];
} hist_change_t;
// A history entry is one or more changes batched together.
// Single-property edits = 1 change. Whole-selection edits = N changes.
typedef struct hist_entry_t {
hist_change_t *changes;
int count;
} hist_entry_t;
#define HIST_MAX 64
typedef struct history_t {
hist_entry_t entries[HIST_MAX];
int count;
int current; // index of last applied entry, -1 = initial state
// Pending edit session (one ImGui widget interaction)
bool capturing;
int pending_shape_idx;
hist_prop_t pending_prop;
float pending_old[4];
} history_t;
// -- internal helpers --
/**
* Read the current value of a single property from a shape.
*
* @param s shape to read from
* @param prop which property (HIST_POSITION, HIST_SCALE, etc.)
* @param out receives the value, zero-padded to 4 floats
*/
static void hist_read_prop(shape_t *s, hist_prop_t prop, float out[4]) {
memset(out, 0, sizeof(float[4]));
switch (prop) {
case HIST_POSITION: out[0] = s->cx; out[1] = s->cy; break;
case HIST_SCALE: out[0] = s->sx; out[1] = s->sy; break;
case HIST_ROTATION: out[0] = s->rotation; break;
case HIST_COLOR: memcpy(out, s->uniform.base_color, sizeof(float[4])); break;
}
}
/**
* Write a value to a single property of a shape. Does NOT regenerate buffers.
*
* @param s shape to modify in-place
* @param prop which property to set
* @param val new value (4 floats, zero-padded for smaller properties)
*/
static void hist_apply_prop(shape_t *s, hist_prop_t prop, const float val[4]) {
switch (prop) {
case HIST_POSITION: s->cx = val[0]; s->cy = val[1]; break;
case HIST_SCALE: s->sx = val[0]; s->sy = val[1]; break;
case HIST_ROTATION: s->rotation = val[0]; break;
case HIST_COLOR: memcpy(s->uniform.base_color, val, sizeof(float[4])); break;
}
}
// -- history API --
/**
* Zero-initialize the history stack. Call once during app init.
*
* @param h history to initialize
*/
static void history_init(history_t *h) {
memset(h, 0, sizeof(*h));
h->current = -1;
}
/**
* Free all heap memory held by the history stack. Call during app shutdown.
*
* @param h history to destroy
*/
static void history_destroy(history_t *h) {
for (int i = 0; i < h->count; i++) {
if (h->entries[i].changes) FREE(h->entries[i].changes);
}
memset(h, 0, sizeof(*h));
h->current = -1;
}
/**
* Push a completed entry onto the stack, discarding any redo branch.
* Takes ownership of entry.changes (must be heap-allocated with ALLOC).
* Used internally by begin_edit/end_edit, or directly for batch edits.
*
* @param h history stack
* @param entry entry to push (changes array is consumed, not copied)
*/
static void history_push_entry(history_t *h, hist_entry_t entry) {
while (h->count > h->current + 1) {
h->count--;
if (h->entries[h->count].changes) {
FREE(h->entries[h->count].changes);
h->entries[h->count].changes = NULL;
}
}
if (h->count >= HIST_MAX) {
if (h->entries[0].changes) FREE(h->entries[0].changes);
memmove(&h->entries[0], &h->entries[1],
(h->count - 1) * sizeof(hist_entry_t));
h->count--;
h->current--;
}
h->entries[h->count] = entry;
h->count++;
h->current = h->count - 1;
}
/**
* Begin capturing an edit session. Snapshots the current value of one property.
* If a prior session is still open (e.g. user switched widgets in the same frame),
* it is finalized and pushed first.
* Call when igIsItemActivated() is true after an ImGui widget.
*
* @param h history stack
* @param shapes the shapes vector (used to read current values)
* @param shape_idx index of the shape being edited
* @param prop which property is about to change
*/
static void history_begin_edit(history_t *h, vector_t *shapes,
int shape_idx, hist_prop_t prop) {
if (h->capturing) {
shape_t *s = (shape_t*) vec_get(shapes, h->pending_shape_idx);
float new_val[4];
hist_read_prop(s, h->pending_prop, new_val);
if (memcmp(h->pending_old, new_val, sizeof(float[4])) != 0) {
hist_change_t change = {
.shape_index = h->pending_shape_idx,
.prop = h->pending_prop,
};
memcpy(change.old_val, h->pending_old, sizeof(float[4]));
memcpy(change.new_val, new_val, sizeof(float[4]));
hist_entry_t entry = { .changes = NULL, .count = 1 };
entry.changes = (hist_change_t*) ALLOC(sizeof(hist_change_t));
*entry.changes = change;
history_push_entry(h, entry);
}
h->capturing = false;
}
h->capturing = true;
h->pending_shape_idx = shape_idx;
h->pending_prop = prop;
shape_t *s = (shape_t*) vec_get(shapes, shape_idx);
hist_read_prop(s, prop, h->pending_old);
}
/**
* End the current edit session and push an entry if the value changed.
* Safe to call when no session is active (no-op).
* Call when igIsAnyItemActive() transitions from true to false.
*
* @param h history stack
* @param shapes the shapes vector (used to read final values)
*/
static void history_end_edit(history_t *h, vector_t *shapes) {
if (!h->capturing) return;
shape_t *s = (shape_t*) vec_get(shapes, h->pending_shape_idx);
float new_val[4];
hist_read_prop(s, h->pending_prop, new_val);
if (memcmp(h->pending_old, new_val, sizeof(float[4])) != 0) {
hist_change_t change = {
.shape_index = h->pending_shape_idx,
.prop = h->pending_prop,
};
memcpy(change.old_val, h->pending_old, sizeof(float[4]));
memcpy(change.new_val, new_val, sizeof(float[4]));
hist_entry_t entry = { .changes = NULL, .count = 1 };
entry.changes = (hist_change_t*) ALLOC(sizeof(hist_change_t));
*entry.changes = change;
history_push_entry(h, entry);
}
h->capturing = false;
}
/**
* Apply every change in an entry to the shapes vector and regenerate buffers.
*
* @param entry the history entry to apply
* @param shapes the shapes vector to modify
* @param forward true to use new_val (redo), false to use old_val (undo)
*/
static void history_apply_entry(hist_entry_t *entry, vector_t *shapes, bool forward) {
for (int i = 0; i < entry->count; i++) {
hist_change_t *c = &entry->changes[i];
if (c->shape_index >= shapes->count) continue;
shape_t *s = (shape_t*) vec_get(shapes, c->shape_index);
hist_apply_prop(s, c->prop, forward ? c->new_val : c->old_val);
shape_regenerate(s);
shape_set_state(s, s->hovered, s->selected);
}
}
/**
* Undo the most recent history entry.
*
* @param h history stack
* @param shapes the shapes vector to revert
* @param selected_count out-parameter for updated selection count (currently passed through)
* @return true if state was changed, false if nothing to undo
*/
static bool history_undo(history_t *h, vector_t *shapes, int *selected_count) {
if (h->current < 0) return false;
history_apply_entry(&h->entries[h->current], shapes, false);
h->current--;
(void)selected_count;
return true;
}
/**
* Redo the next history entry.
*
* @param h history stack
* @param shapes the shapes vector to advance
* @param selected_count out-parameter (currently passed through)
* @return true if state was changed, false if nothing to redo
*/
static bool history_redo(history_t *h, vector_t *shapes, int *selected_count) {
if (h->current + 1 >= h->count) return false;
h->current++;
history_apply_entry(&h->entries[h->current], shapes, true);
(void)selected_count;
return true;
}
#endif

1697
src/main.c

File diff suppressed because it is too large Load Diff

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,107 +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);
/**
* Xorshift32 PRNG core. Advances the global seed and returns the new value.
*
* @return pseudo-random 32-bit integer
*/
static uint32_t xorshift32(void)
{
seed ^= seed<<13;
seed ^= seed>>17;
seed ^= seed<<5;
return seed;
}
/**
* Seed the global PRNG state. Zero is ignored (caller should pass a non-zero
* seed). Runs the generator once after seeding to mix the state.
*
* @param _seed non-zero 32-bit seed value
*/
static void rand_seed(uint32_t _seed)
{
if(_seed == 0)
return;
seed = _seed;
xorshift32();
}
/**
* Return a random integer in [0, UINT32_MAX].
*
* @return pseudo-random 32-bit integer
*/
static uint32_t next_int(void)
{
return xorshift32();
}
/**
* Return a random integer in [0, max].
*
* @param max inclusive upper bound
* @return pseudo-random integer
*/
static uint32_t next_int_max(uint32_t max)
{
return (uint32_t) floorf(xorshift32() / (float) UINT32_MAX * max);
}
/**
* Return a random integer in [min, max].
*
* @param min inclusive lower bound
* @param max inclusive upper bound
* @return pseudo-random integer
*/
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;
}
/**
* Return a random float in [0, 1].
*
* @return pseudo-random float
*/
static float next_float(void)
{
return (float) xorshift32() / UINT32_MAX;
}
/**
* Return a random float in [0, max].
*
* @param max inclusive upper bound
* @return pseudo-random float
*/
static float next_float_max(float max)
{
return (float) xorshift32() / UINT32_MAX * max;
}
/**
* Return a random float in [min, max].
*
* @param min inclusive lower bound
* @param max inclusive upper bound
* @return pseudo-random float
*/
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,45 +0,0 @@
struct VsUniform {
mvp: mat4x4f,
};
struct ShapeUniform {
transform: mat4x4f,
base_color: vec4f,
state: u32,
};
struct VsIn {
@location(0) position: vec2f,
};
struct Vs2Fs {
@builtin(position) pos: vec4f,
@location(0) @interpolate(linear) color: vec4f,
};
struct FsOut {
@location(0) color: vec4f,
};
@binding(0) @group(0) var<uniform> vs_uniforms: VsUniform;
@binding(1) @group(0) var<uniform> shape_uniform: ShapeUniform;
@vertex fn vs_main(input: VsIn) -> Vs2Fs {
var output: Vs2Fs;
let world_pos = vec4f(input.position.x, input.position.y, 0.0, 1.0) * shape_uniform.transform;
output.pos = world_pos * vs_uniforms.mvp;
if (shape_uniform.state == 2u) {
output.color = vec4f(1.0, 0.84, 0.0, 1.0);
} else if (shape_uniform.state == 1u) {
output.color = clamp(shape_uniform.base_color * 1.5, vec4f(0.0), vec4f(1.0));
} else {
output.color = shape_uniform.base_color;
}
return output;
}
@fragment fn fs_main(input: Vs2Fs) -> FsOut {
var output: FsOut;
output.color = input.color;
return output;
}

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;
}

View File

@@ -1,433 +1,363 @@
#ifndef SHAPE_H
#define SHAPE_H
#include "api.h"
typedef struct shape_vertex_t {
float x, y;
} shape_vertex_t;
typedef struct uvec2 { uint32_t x, y; } uvec2;
typedef struct vec2 { float x, y; } vec2;
typedef struct shape_uniform_t {
mat4 transform;
float base_color[4];
uint32_t state;
uint8_t _pad[12];
} shape_uniform_t;
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;
typedef enum shape_kind_t {
SHAPE_CIRCLE,
SHAPE_STAR,
} shape_kind_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)
typedef struct shape_t {
shape_vertex_t *verts;
uint16_t *indices;
#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 num_verts;
sg_buffer vbuf;
sg_buffer ibuf;
shape_uniform_t uniform;
bool hovered;
bool selected;
uint32_t* indices; // Array of shapes/circles indices per tile, pre culled on the CPU side
} cache;
shape_kind_t kind;
float cx, cy;
float sx, sy;
float rotation;
int star_points;
float star_inner_ratio;
int last_update_frame;
} shape_t;
sg_view texture; //Layer count depends on the world size so the texture has to be allocate on the scene side
#define SHAPE_HOVER_PX 6.0f
bool shape_dirty, segment_dirty, circle_dirty;
} scene_t;
static sg_pipeline shape_pipeline;
static sg_pipeline overlay_pipeline;
static sg_shader shape_shader;
static int g_shape_frame_id;
// 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));
static void shape_begin_frame(void)
{
g_shape_frame_id++;
}
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));
/**
* Create the shape shader, shape pipeline (line strip), and overlay pipeline
* (triangles). Call once during app init before drawing any shapes.
*/
static void shape_init_pipeline(void)
{
shape_shader = sg_make_shader(&(sg_shader_desc) {
.vertex_func = {
.source = (const char*) src_shaders_shape_wgsl,
.entry = "vs_main",
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 },
}),
},
.fragment_func = {
.source = (const char*) src_shaders_shape_wgsl,
.entry = "fs_main",
},
.uniform_blocks = {
[0] = {
.size = sizeof(mat4),
.stage = SG_SHADERSTAGE_VERTEX,
.wgsl_group0_binding_n = 0,
},
[1] = {
.size = sizeof(shape_uniform_t),
.stage = SG_SHADERSTAGE_VERTEX,
.wgsl_group0_binding_n = 1,
},
},
.attrs = {
[0] = { .base_type = SG_SHADERATTRBASETYPE_FLOAT },
},
.label = "Shape shader",
});
shape_pipeline = sg_make_pipeline(&(sg_pipeline_desc) {
.shader = shape_shader,
.index_type = SG_INDEXTYPE_UINT16,
.primitive_type = SG_PRIMITIVETYPE_LINE_STRIP,
.layout.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
},
.label = "Shape pipeline",
});
overlay_pipeline = sg_make_pipeline(&(sg_pipeline_desc) {
.shader = shape_shader,
.index_type = SG_INDEXTYPE_UINT16,
.primitive_type = SG_PRIMITIVETYPE_TRIANGLES,
.layout.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
},
.label = "Overlay pipeline",
});
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;
}
/**
* Destroy the shape shader and both pipelines. Call during app shutdown.
*/
static void shape_shutdown_pipeline(void)
{
sg_destroy_pipeline(shape_pipeline);
sg_destroy_pipeline(overlay_pipeline);
sg_destroy_shader(shape_shader);
}
#define COORD_TO_INDEX(x, y, scene) y * ceilf(s->world_size.x / TILE_SIZE) + x
/**
* Return the number of line segments for a circle of the given radius.
* Clamped to [8, 128]; scales roughly with circumference.
*
* @param r circle radius in world units
* @return segment count
*/
static int shape_calc_segments(float r)
static void scene_compute_culling(scene_t* s, tile_task_t* task)
{
int n = (int)(fabsf(r) * 0.5f) + 16;
if (n < 8) n = 8;
if (n > 128) n = 128;
return n;
}
/**
* Set default state for a newly created shape: identity transform, base color,
* not hovered, not selected.
*
* @param s shape to initialize
* @param color RGBA base color (copied)
*/
static void shape_init_common(shape_t *s, const float color[4])
{
s->hovered = false;
s->selected = false;
memcpy(s->uniform.base_color, color, sizeof(float[4]));
s->uniform.state = 0;
memset(s->uniform._pad, 0, sizeof(s->uniform._pad));
}
/**
* Build the per-shape transform matrix from cx, cy, rotation.
* Uses R(-angle) so the shader's row-vector convention matches the existing
* world-space vertex computation.
*/
static void shape_build_transform(shape_t *s)
{
mat4 T, R, S, RS;
glm_translate_make(T, (vec3){s->cx, s->cy, 0.0f});
glm_rotate_make(R, -s->rotation, (vec3){0.0f, 0.0f, 1.0f});
glm_scale_make(S, (vec3){s->sx, s->sy, 1.0f});
glm_mat4_mul(R, S, RS);
glm_mat4_mul(T, RS, s->uniform.transform);
}
/**
* Create GPU vertex and index buffers from the shape's current verts/indices.
*
* @param s shape (must have verts and indices allocated)
*/
static void shape_make_buffers(shape_t *s)
{
s->vbuf = sg_make_buffer(&(sg_buffer_desc) {
.size = s->num_indices * sizeof(shape_vertex_t),
.usage = { .stream_update = true },
.label = "Shape vertices",
});
sg_update_buffer(s->vbuf, &(sg_range){s->verts, s->num_indices * sizeof(shape_vertex_t)});
s->ibuf = sg_make_buffer(&(sg_buffer_desc) {
.usage = { .index_buffer = true },
.data = { s->indices, s->num_indices * sizeof(uint16_t) },
.label = "Shape indices",
});
}
/**
* Destroy GPU buffers and free vertex/index arrays for a single shape.
*
* @param s shape to tear down
*/
static void shape_shutdown(shape_t *s)
{
sg_destroy_buffer(s->vbuf);
sg_destroy_buffer(s->ibuf);
FREE(s->verts);
FREE(s->indices);
}
/**
* Rebuild vertex and index data from the shape's current parameters (position,
* scale, rotation, kind), then recreate GPU buffers. Call after any parameter
* change.
*
* @param s shape to regenerate
*/
static void shape_regenerate(shape_t *s)
{
int n, count;
if (s->kind == SHAPE_CIRCLE) {
int segs = shape_calc_segments(s->sx);
n = segs;
count = segs + 1;
} else {
n = s->star_points * 2;
count = n + 1;
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));
}
bool resized = ((uint32_t)count != s->num_indices);
if (resized) {
sg_destroy_buffer(s->vbuf);
sg_destroy_buffer(s->ibuf);
FREE(s->verts);
FREE(s->indices);
s->verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
s->indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
s->cache.indices[s->cache.num_indices++] = i;
task->count++;
}
if (s->kind == SHAPE_CIRCLE) {
int segs = n;
for (int i = 0; i < segs; i++) {
float a = (float)i / (float)segs * 2.0f * GLM_PIf - GLM_PI_2f;
s->verts[i] = (shape_vertex_t) { cosf(a), sinf(a) };
}
s->verts[segs] = s->verts[0];
} else {
for (int i = 0; i < n; i++) {
float a = (float)i / (float)n * 2.0f * GLM_PIf - GLM_PI_2f;
float r = (i & 1) ? s->star_inner_ratio : 1.0f;
s->verts[i] = (shape_vertex_t) { cosf(a) * r, sinf(a) * r };
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->verts[n] = s->verts[0];
s->cache.indices[s->cache.num_indices++] = i;
task->count++;
}
s->num_indices = (uint32_t)count;
s->num_verts = (uint32_t)n;
for (int i = 0; i <= n; i++) s->indices[i] = (uint16_t)i;
shape_build_transform(s);
if (resized) {
shape_make_buffers(s);
s->last_update_frame = g_shape_frame_id;
} else if (s->last_update_frame != g_shape_frame_id) {
sg_update_buffer(s->vbuf, &(sg_range){s->verts, (size_t)count * sizeof(shape_vertex_t)});
s->last_update_frame = g_shape_frame_id;
}
}
/**
* Update hovered/selected flags and the shader uniform state.
* State is 0=normal, 1=hovered (brightened), 2=selected (green).
*
* @param s shape to update
* @param hovered true if cursor is over the shape
* @param selected true if shape is in the selection set
*/
static void shape_set_state(shape_t *s, bool hovered, bool selected)
// 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)
{
s->hovered = hovered;
s->selected = selected;
s->uniform.state = selected ? 2u : (hovered ? 1u : 0u);
}
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
/**
* Ray-casting point-in-polygon test. Handles arbitrary non-self-intersecting
* polygons.
*
* @param px point X in world space
* @param py point Y in world space
* @param verts polygon vertices
* @param n vertex count
* @return true if the point is inside the polygon
*/
static bool point_in_polygon(float px, float py, shape_vertex_t *verts, uint32_t n)
{
bool inside = false;
for (uint32_t i = 0, j = n - 1; i < n; j = i++) {
float xi = verts[i].x, yi = verts[i].y;
float xj = verts[j].x, yj = verts[j].y;
if ((yi > py) != (yj > py) && px < (xj - xi) * (py - yi) / (yj - yi) + xi)
inside = !inside;
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 inside;
}
/**
* Test whether a world-space point hits this shape. Transforms the query
* to local space (verts are now stored relative to origin), then tests
* polygon containment and edge proximity.
*
* @param s shape to test
* @param wx point X in world space
* @param wy point Y in world space
* @param world_tol hit tolerance in world units
* @return true if the point hits the shape
*/
static bool shape_hit_test(shape_t *s, float wx, float wy, float world_tol)
{
float sc = cosf(s->rotation), ss = sinf(s->rotation);
float dx = wx - s->cx, dy = wy - s->cy;
float lx = (dx * sc + dy * ss) / s->sx;
float ly = (-dx * ss + dy * sc) / s->sy;
float min_scale = fminf(fabsf(s->sx), fabsf(s->sy));
float local_tol = world_tol / (min_scale > 0.0001f ? min_scale : 1.0f);
float tol_sq = local_tol * local_tol;
if (point_in_polygon(lx, ly, s->verts, s->num_verts))
return true;
for (uint32_t i = 0, j = s->num_verts - 1; i < s->num_verts; j = i++) {
float ax = s->verts[i].x, ay = s->verts[i].y;
float bx = s->verts[j].x, by = s->verts[j].y;
float abx = bx - ax, aby = by - ay;
float len_sq = abx * abx + aby * aby;
if (len_sq < 0.0001f) continue;
float t = ((lx - ax) * abx + (ly - ay) * aby) / len_sq;
t = fmaxf(0.0f, fminf(1.0f, t));
float cx = ax + t * abx, cy = ay + t * aby;
float ddx = lx - cx, ddy = ly - cy;
if (ddx * ddx + ddy * ddy <= tol_sq) return true;
}
return false;
}
/**
* Issue a draw call for this shape using the shape line-strip pipeline.
*
* @param s shape to draw
* @param mvp model-view-projection matrix (from compute_mvp)
*/
static void shape_draw(shape_t *s, const mat4 *mvp)
{
sg_apply_uniforms(0, &SG_RANGE(*mvp));
sg_apply_uniforms(1, &SG_RANGE(s->uniform));
sg_apply_bindings(&(sg_bindings) {
.vertex_buffers[0] = s->vbuf,
.index_buffer = s->ibuf,
});
sg_draw(0, s->num_indices, 1);
}
/**
* Create a circle shape (returned by value). Allocates verts/indices and GPU
* buffers. The number of line segments adapts to radius.
*
* @param x center X in world space
* @param y center Y in world space
* @param r radius in world units
* @param color RGBA base color
* @return fully initialized shape_t
*/
static shape_t shape_circle(float x, float y, float r, const float color[4])
{
shape_t s;
s.kind = SHAPE_CIRCLE;
s.cx = x; s.cy = y;
s.sx = r; s.sy = r;
s.rotation = 0.0f;
int segs = shape_calc_segments(r);
int count = segs + 1;
s.verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
s.indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
for (int i = 0; i < segs; i++) {
float a = (float)i / (float)segs * 2.0f * GLM_PIf - GLM_PI_2f;
s.verts[i] = (shape_vertex_t) { cosf(a), sinf(a) };
}
s.verts[segs] = s.verts[0];
for (int i = 0; i <= segs; i++) s.indices[i] = (uint16_t)i;
s.num_indices = (uint32_t)count;
s.num_verts = (uint32_t)segs;
shape_init_common(&s, color);
shape_build_transform(&s);
shape_make_buffers(&s);
return s;
return dirty_tile_count;
}
/**
* Create a star shape (returned by value). Alternates between outer_r and
* inner_r at each vertex, producing a star with the given number of points.
* Allocates verts/indices and GPU buffers.
*
* @param x center X in world space
* @param y center Y in world space
* @param outer_r outer radius in world units
* @param inner_r inner radius in world units
* @param points number of star points
* @param color RGBA base color
* @return fully initialized shape_t
*/
static shape_t shape_star(float x, float y, float outer_r, float inner_r,
int points, const float color[4])
{
shape_t s;
s.kind = SHAPE_STAR;
s.cx = x; s.cy = y;
s.sx = outer_r; s.sy = outer_r;
s.rotation = 0.0f;
s.star_points = points;
s.star_inner_ratio = inner_r / outer_r;
int n = points * 2;
int count = n + 1;
s.verts = (shape_vertex_t*) ALLOC(count * sizeof(shape_vertex_t));
s.indices = (uint16_t*) ALLOC(count * sizeof(uint16_t));
for (int i = 0; i < n; i++) {
float a = (float)i / (float)n * 2.0f * GLM_PIf - GLM_PI_2f;
float r = (i & 1) ? s.star_inner_ratio : 1.0f;
s.verts[i] = (shape_vertex_t) { cosf(a) * r, sinf(a) * r };
// 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;
}
s.verts[n] = s.verts[0];
for (int i = 0; i <= n; i++) s.indices[i] = (uint16_t)i;
s.num_indices = (uint32_t)count;
s.num_verts = (uint32_t)n;
int idx = s->shapes.num_segments++;
s->shapes.segments[idx] = seg;
shape_init_common(&s, color);
shape_build_transform(&s);
shape_make_buffers(&s);
return s;
return idx;
}
#endif
// 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,233 +0,0 @@
#ifndef SPATIAL_H
#define SPATIAL_H
#include "api.h"
// Tunable constants
#define SPATIAL_CELL_SIZE 250.0f
#define SPATIAL_HASH_BITS 8
#define SPATIAL_HASH_SIZE (1 << SPATIAL_HASH_BITS)
#define SPATIAL_QUERY_RANGE 1
typedef struct {
int shape_idx;
float min_x, min_y, max_x, max_y;
} spatial_entry_t;
typedef struct {
bool occupied;
int cx, cy;
spatial_entry_t *entries;
int count;
int capacity;
} spatial_slot_t;
typedef struct {
spatial_slot_t slots[SPATIAL_HASH_SIZE];
bool dirty;
} spatial_grid_t;
static int spatial_hash(int cx, int cy)
{
return (cx * 73856093) ^ (cy * 19349663);
}
static void spatial_compute_aabb(shape_t *s, float *min_x, float *min_y,
float *max_x, float *max_y)
{
float cos_r = cosf(s->rotation);
float sin_r = sinf(s->rotation);
float hx = fabsf(cos_r) * s->sx + fabsf(sin_r) * s->sy;
float hy = fabsf(sin_r) * s->sx + fabsf(cos_r) * s->sy;
*min_x = s->cx - hx;
*min_y = s->cy - hy;
*max_x = s->cx + hx;
*max_y = s->cy + hy;
}
static void spatial_init(spatial_grid_t *grid)
{
memset(grid, 0, sizeof(*grid));
grid->dirty = true;
}
static void spatial_mark_dirty(spatial_grid_t *grid)
{
grid->dirty = true;
}
static void spatial_destroy(spatial_grid_t *grid)
{
for (int i = 0; i < SPATIAL_HASH_SIZE; i++) {
if (grid->slots[i].entries) FREE(grid->slots[i].entries);
}
memset(grid, 0, sizeof(*grid));
}
static void spatial_rebuild(spatial_grid_t *grid, vector_t *shapes)
{
if (!grid->dirty) return;
grid->dirty = false;
int n = shapes->count;
// Phase 0: clear occupied flags
for (int i = 0; i < SPATIAL_HASH_SIZE; i++) {
grid->slots[i].occupied = false;
grid->slots[i].count = 0;
}
if (n == 0) return;
// Phase 1: count shapes per cell
for (int i = 0; i < n; i++) {
shape_t *s = (shape_t*) vec_get(shapes, i);
int ccx = (int) floorf(s->cx / SPATIAL_CELL_SIZE);
int ccy = (int) floorf(s->cy / SPATIAL_CELL_SIZE);
int idx = spatial_hash(ccx, ccy) & (SPATIAL_HASH_SIZE - 1);
while (grid->slots[idx].occupied) {
if (grid->slots[idx].cx == ccx && grid->slots[idx].cy == ccy) break;
idx = (idx + 1) & (SPATIAL_HASH_SIZE - 1);
}
if (!grid->slots[idx].occupied) {
grid->slots[idx].occupied = true;
grid->slots[idx].cx = ccx;
grid->slots[idx].cy = ccy;
}
grid->slots[idx].count++;
}
// Phase 2: allocate entry arrays based on count
for (int i = 0; i < SPATIAL_HASH_SIZE; i++) {
if (!grid->slots[i].occupied) continue;
if (grid->slots[i].count > grid->slots[i].capacity) {
if (grid->slots[i].entries) FREE(grid->slots[i].entries);
grid->slots[i].entries = (spatial_entry_t*) ALLOC(
(size_t) grid->slots[i].count * sizeof(spatial_entry_t));
grid->slots[i].capacity = grid->slots[i].count;
}
grid->slots[i].count = 0; // reset for fill phase
}
// Phase 3: fill entries
for (int i = 0; i < n; i++) {
shape_t *s = (shape_t*) vec_get(shapes, i);
int ccx = (int) floorf(s->cx / SPATIAL_CELL_SIZE);
int ccy = (int) floorf(s->cy / SPATIAL_CELL_SIZE);
int idx = spatial_hash(ccx, ccy) & (SPATIAL_HASH_SIZE - 1);
while (!(grid->slots[idx].occupied &&
grid->slots[idx].cx == ccx && grid->slots[idx].cy == ccy)) {
idx = (idx + 1) & (SPATIAL_HASH_SIZE - 1);
}
spatial_entry_t *e = &grid->slots[idx].entries[grid->slots[idx].count++];
e->shape_idx = i;
spatial_compute_aabb(s, &e->min_x, &e->min_y, &e->max_x, &e->max_y);
}
}
static int spatial_query_point(spatial_grid_t *grid, vector_t *shapes,
float wx, float wy, float world_tol)
{
int ccx = (int) floorf(wx / SPATIAL_CELL_SIZE);
int ccy = (int) floorf(wy / SPATIAL_CELL_SIZE);
for (int dz = -SPATIAL_QUERY_RANGE; dz <= SPATIAL_QUERY_RANGE; dz++) {
for (int dw = -SPATIAL_QUERY_RANGE; dw <= SPATIAL_QUERY_RANGE; dw++) {
int cell_x = ccx + dz;
int cell_y = ccy + dw;
int idx = spatial_hash(cell_x, cell_y) & (SPATIAL_HASH_SIZE - 1);
int probe_start = idx;
do {
if (!grid->slots[idx].occupied) break;
if (grid->slots[idx].cx == cell_x && grid->slots[idx].cy == cell_y) {
for (int e = 0; e < grid->slots[idx].count; e++) {
spatial_entry_t *entry = &grid->slots[idx].entries[e];
if (wx < entry->min_x - world_tol ||
wx > entry->max_x + world_tol ||
wy < entry->min_y - world_tol ||
wy > entry->max_y + world_tol)
continue;
shape_t *s = (shape_t*) vec_get(shapes, entry->shape_idx);
if (shape_hit_test(s, wx, wy, world_tol))
return entry->shape_idx;
}
break;
}
idx = (idx + 1) & (SPATIAL_HASH_SIZE - 1);
} while (idx != probe_start);
}
}
return -1;
}
static int spatial_query_rect_select(spatial_grid_t *grid, vector_t *shapes,
float min_x, float min_y,
float max_x, float max_y)
{
for (int i = 0; i < shapes->count; i++) {
((shape_t*) vec_get(shapes, i))->selected = false;
}
int selected_count = 0;
int min_cx = (int) floorf(min_x / SPATIAL_CELL_SIZE);
int min_cy = (int) floorf(min_y / SPATIAL_CELL_SIZE);
int max_cx = (int) floorf(max_x / SPATIAL_CELL_SIZE);
int max_cy = (int) floorf(max_y / SPATIAL_CELL_SIZE);
for (int cell_x = min_cx; cell_x <= max_cx; cell_x++) {
for (int cell_y = min_cy; cell_y <= max_cy; cell_y++) {
int idx = spatial_hash(cell_x, cell_y) & (SPATIAL_HASH_SIZE - 1);
int probe_start = idx;
do {
if (!grid->slots[idx].occupied) break;
if (grid->slots[idx].cx == cell_x && grid->slots[idx].cy == cell_y) {
for (int e = 0; e < grid->slots[idx].count; e++) {
spatial_entry_t *entry = &grid->slots[idx].entries[e];
if (entry->max_x < min_x || entry->min_x > max_x ||
entry->max_y < min_y || entry->min_y > max_y)
continue;
shape_t *s = (shape_t*) vec_get(shapes, entry->shape_idx);
if (s->selected) continue;
bool hit = (s->cx >= min_x && s->cx <= max_x &&
s->cy >= min_y && s->cy <= max_y);
float sc = cosf(s->rotation), ss = sinf(s->rotation);
for (uint32_t v = 0; !hit && v < s->num_verts; v++) {
float lx = s->verts[v].x * s->sx;
float ly = s->verts[v].y * s->sy;
float wx = s->cx + lx * sc - ly * ss;
float wy = s->cy + lx * ss + ly * sc;
if (wx >= min_x && wx <= max_x &&
wy >= min_y && wy <= max_y)
hit = true;
}
if (hit) {
s->selected = true;
selected_count++;
}
}
break;
}
idx = (idx + 1) & (SPATIAL_HASH_SIZE - 1);
} while (idx != probe_start);
}
}
return selected_count;
}
#endif

View File

@@ -1,104 +0,0 @@
#ifndef UTIL_H
#define UTIL_H
#include <stdint.h>
#include <string.h>
typedef struct vector_t {
uint8_t *data;
int count;
int capacity;
int stride;
} vector_t;
/**
* Zero-initialize a vector with the given element stride.
*
* @param v vector to initialize
* @param stride byte size of each element
*/
static void vec_init(vector_t *v, int stride) {
memset(v, 0, sizeof(*v));
v->stride = stride;
}
/**
* Grow the vector's backing array to at least min_capacity elements.
* Doubles capacity (starting at 8) or uses min_capacity, whichever is larger.
*
* @param v vector to grow
* @param min_capacity minimum element count required
*/
static void vec_grow(vector_t *v, int min_capacity) {
int new_cap = v->capacity ? v->capacity * 2 : 8;
if (new_cap < min_capacity) new_cap = min_capacity;
uint8_t *new_data = (uint8_t*) ALLOC(new_cap * v->stride);
if (v->data) {
memcpy(new_data, v->data, v->count * v->stride);
FREE(v->data);
}
v->data = new_data;
v->capacity = new_cap;
}
/**
* Append an uninitialized element to the end of the vector. Grows if needed.
*
* @param v vector to push into
* @return pointer to the new (uninitialized) element
*/
static void *vec_push(vector_t *v) {
if (v->count >= v->capacity) vec_grow(v, v->count + 1);
return v->data + (v->count++) * v->stride;
}
/**
* Remove the last element from the vector (decrements count, no free).
*
* @param v vector to pop from
*/
static void vec_pop(vector_t *v) {
if (v->count > 0) v->count--;
}
/**
* Remove the element at index by swapping in the last element (O(1)).
* Order is not preserved.
*
* @param v vector to remove from
* @param index index of the element to remove
*/
static void vec_remove(vector_t *v, int index) {
if (index < 0 || index >= v->count) return;
if (index < v->count - 1) {
memcpy(v->data + index * v->stride,
v->data + (v->count - 1) * v->stride,
v->stride);
}
v->count--;
}
/**
* Return a pointer to the element at index (no bounds check).
*
* @param v vector to access
* @param index element index
* @return pointer to the element
*/
static void *vec_get(vector_t *v, int index) {
return v->data + index * v->stride;
}
/**
* Free the backing array and reset the vector to empty.
*
* @param v vector to free
*/
static void vec_free(vector_t *v) {
if (v->data) FREE(v->data);
v->data = NULL;
v->count = 0;
v->capacity = 0;
}
#endif