diff --git a/samples/clock/.gitignore b/samples/clock/.gitignore new file mode 100644 index 0000000..b455bab --- /dev/null +++ b/samples/clock/.gitignore @@ -0,0 +1,3 @@ +Clock +profile.dtrace +profile.spall diff --git a/samples/clock/build.bat b/samples/clock/build.bat new file mode 100644 index 0000000..ec92225 --- /dev/null +++ b/samples/clock/build.bat @@ -0,0 +1,19 @@ +@echo off + +:: compile wasm module +set wasmFlags=--target=wasm32^ + --no-standard-libraries ^ + -fno-builtin ^ + -Wl,--no-entry ^ + -Wl,--export-dynamic ^ + -g ^ + -O2 ^ + -mbulk-memory ^ + -D__ORCA__ ^ + -isystem ..\..\src\libc-shim\include ^ + -I..\..\ext -I ..\..\src + +clang %wasmFlags% -o .\module.wasm ..\..\src\orca.c ..\..\src\libc-shim\src\*.c src\main.c +IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL% + +orca bundle --orca-dir ..\.. --name Clock --icon icon.png --resource-dir data module.wasm diff --git a/samples/clock/build.sh b/samples/clock/build.sh new file mode 100644 index 0000000..d39dbb3 --- /dev/null +++ b/samples/clock/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -euo pipefail + +if [[ -x /usr/local/opt/llvm/bin/clang ]]; then + CLANG=/usr/local/opt/llvm/bin/clang +elif [[ -x /opt/homebrew/opt/llvm/bin/clang ]]; then + CLANG=/opt/homebrew/opt/llvm/bin/clang +else + echo "Could not find Homebrew clang; this script will probably not work." + CLANG=clang +fi + +ORCA_DIR=../.. +STDLIB_DIR=../../src/libc-shim + +wasmFlags="--target=wasm32 \ + --no-standard-libraries \ + -fno-builtin \ + -Wl,--no-entry \ + -Wl,--export-dynamic \ + -g \ + -O2 \ + -mbulk-memory \ + -D__ORCA__ \ + -I $STDLIB_DIR/include \ + -I $ORCA_DIR/ext \ + -I $ORCA_DIR/src" + +$CLANG $wasmFlags -o ./module.wasm ../../src/orca.c $STDLIB_DIR/src/*.c src/main.c + +orca bundle --orca-dir ../.. --name Clock --icon icon.png --resource-dir data module.wasm diff --git a/samples/clock/data/segoeui.ttf b/samples/clock/data/segoeui.ttf new file mode 100644 index 0000000..0f52cbd Binary files /dev/null and b/samples/clock/data/segoeui.ttf differ diff --git a/samples/clock/icon.png b/samples/clock/icon.png new file mode 100644 index 0000000..1091c1b Binary files /dev/null and b/samples/clock/icon.png differ diff --git a/samples/clock/src/main.c b/samples/clock/src/main.c new file mode 100644 index 0000000..c82764b --- /dev/null +++ b/samples/clock/src/main.c @@ -0,0 +1,187 @@ +#include + +#include + +#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0])) + +oc_vec2 frameSize = { 100, 100 }; + +oc_surface surface; +oc_canvas canvas; +oc_font font; + +f64 lastSeconds = 0; + +oc_mat2x3 mat_rotation(f32 radians); +oc_mat2x3 mat_translation(f32 x, f32 y); +oc_mat2x3 mat_transform(f32 x, f32 y, f32 radians); +f32 minf(f32 a, f32 b); + +ORCA_EXPORT void oc_on_init(void) +{ + surface = oc_surface_canvas(); + canvas = oc_canvas_create(); + + { + oc_str8 filename = OC_STR8("/segoeui.ttf"); + oc_file file = oc_file_open(filename, OC_FILE_ACCESS_READ, 0); + if(oc_file_last_error(file) != OC_IO_OK) + { + oc_log_error("Couldn't open file %s\n", oc_str8_to_cstring(oc_scratch(), filename)); + } + u64 size = oc_file_size(file); + char* buffer = oc_arena_push(oc_scratch(), size); + oc_file_read(file, size, buffer); + oc_file_close(file); + + oc_unicode_range ranges[5] = { OC_UNICODE_BASIC_LATIN, + OC_UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT, + OC_UNICODE_LATIN_EXTENDED_A, + OC_UNICODE_LATIN_EXTENDED_B, + OC_UNICODE_SPECIALS }; + font = oc_font_create_from_memory(oc_str8_from_buffer(size, buffer), 5, ranges); + } + + oc_arena_clear(oc_scratch()); +} + +ORCA_EXPORT void oc_on_resize(u32 width, u32 height) +{ + frameSize.x = width; + frameSize.y = height; +} + +ORCA_EXPORT void oc_on_frame_refresh(void) +{ + const oc_str8 clock_number_strings[] = { + OC_STR8("12"), + OC_STR8("1"), + OC_STR8("2"), + OC_STR8("3"), + OC_STR8("4"), + OC_STR8("5"), + OC_STR8("6"), + OC_STR8("7"), + OC_STR8("8"), + OC_STR8("9"), + OC_STR8("10"), + OC_STR8("11"), + }; + + oc_canvas_set_current(canvas); + oc_surface_select(surface); + oc_set_color_rgba(.05, .05, .05, 1); + oc_clear(); + + const f64 timestampSecs = oc_clock_time(OC_CLOCK_DATE); + const f64 secs = fmod(timestampSecs, 60); + const f64 minutes = fmod(timestampSecs, 60 * 60) / 60; + const f64 hours = fmod(timestampSecs, 60 * 60 * 24) / (60 * 60); + const f64 hoursAs12Format = fmod(hours, 12.0); + + if(lastSeconds != floor(secs)) + { + lastSeconds = floor(secs); + oc_log_info("current time: %.0f:%.0f:%.0f", floor(hours), floor(minutes), floor(secs)); + } + + const f32 secondsRotation = (M_PI * 2) * (secs / 60.0) - (M_PI / 2); + const f32 minutesRotation = (M_PI * 2) * (minutes / 60.0) - (M_PI / 2); + const f32 hoursRotation = (M_PI * 2) * (hoursAs12Format / 12.0) - (M_PI / 2); + + const f32 centerX = frameSize.x / 2; + const f32 centerY = frameSize.y / 2; + const f32 clockRadius = minf(frameSize.x, frameSize.y) * 0.5f * 0.85f; + + const f32 DEFAULT_CLOCK_RADIUS = 260; + const f32 uiScale = clockRadius / DEFAULT_CLOCK_RADIUS; + + const f32 fontSize = 26 * uiScale; + oc_set_font(font); + oc_set_font_size(fontSize); + + // clock backing + oc_set_color_rgba(1, 1, 1, 1); + oc_circle_fill(centerX, centerY, clockRadius); + + // clock face + for(int i = 0; i < ARRAYSIZE(clock_number_strings); ++i) + { + const f32 rot = -i * ((M_PI * 2) / 12.0f) + (M_PI / 2); + const f32 sinRot = sinf(rot); + const f32 cosRot = cosf(rot); + + oc_rect textRect = oc_text_bounding_box(font, fontSize, clock_number_strings[i]); + textRect.h -= 10 * uiScale; // oc_text_bounding_box height doesn't seem to be a tight fit around the glyph + + const f32 x = cosRot * clockRadius * 0.8f - (textRect.w / 2) + centerX; + const f32 y = -sinRot * clockRadius * 0.8f + (textRect.h / 2) + centerY; + + oc_set_color_rgba(0.2, 0.2, 0.2, 1); + oc_move_to(x, y); + oc_text_outlines(clock_number_strings[i]); + oc_fill(); + } + + oc_matrix_push(mat_transform(centerX, centerY, hoursRotation)); + { + oc_set_color_rgba(.2, 0.2, 0.2, 1); + oc_rounded_rectangle_fill(0, -7.5 * uiScale, clockRadius * 0.5f, 15 * uiScale, 5 * uiScale); + + oc_matrix_pop(); + } + + oc_matrix_push(mat_transform(centerX, centerY, minutesRotation)); + { + oc_set_color_rgba(.2, 0.2, 0.2, 1); + oc_rounded_rectangle_fill(0, -5 * uiScale, clockRadius * 0.7f, 10 * uiScale, 5 * uiScale); + + oc_matrix_pop(); + } + + oc_matrix_push(mat_transform(centerX, centerY, secondsRotation)); + { + oc_set_color_rgba(1, 0.2, 0.2, 1); + oc_rounded_rectangle_fill(0, -2.5 * uiScale, clockRadius * 0.8f, 5 * uiScale, 5 * uiScale); + + oc_matrix_pop(); + } + + oc_set_color_rgba(.2, 0.2, 0.2, 1); + oc_circle_fill(centerX, centerY, 10 * uiScale); + + oc_render(surface, canvas); + oc_surface_present(surface); +} + +oc_mat2x3 mat_rotation(f32 radians) +{ + const f32 sinRot = sinf(radians); + const f32 cosRot = cosf(radians); + oc_mat2x3 rot = { + cosRot, -sinRot, 0, + sinRot, cosRot, 0 + }; + return rot; +} + +oc_mat2x3 mat_translation(f32 x, f32 y) +{ + oc_mat2x3 translation = { + 1, 0, x, + 0, 1, y + }; + return translation; +} + +oc_mat2x3 mat_transform(f32 x, f32 y, f32 radians) +{ + oc_mat2x3 rotation = mat_rotation(radians); + oc_mat2x3 translation = mat_translation(x, y); + return oc_mat2x3_mul_m(translation, rotation); +} + +f32 minf(f32 a, f32 b) +{ + return (a < b) ? a : b; +}