From d613d03163c51c32a9e7f3eda2ed8aa76d793df9 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:29:44 +0100 Subject: [PATCH 01/24] wip --- .github/workflows/release.yml | 69 ++++ .github/workflows/verify.yml | 59 ++++ CMakeLists.txt | 98 ++++++ README.MD | 41 +++ examples/2d/clock.js | 91 ++++++ examples/2d/shapes.js | 41 +++ examples/2d/shapes2.js | 78 +++++ examples/2d/solar2d.js | 48 +++ examples/2d/text.js | 27 ++ examples/assets/AnonymousPro-Regular.ttf | Bin 0 -> 112280 bytes examples/assets/sample.png | Bin 0 -> 39291 bytes examples/assets/sound.wav | Bin 0 -> 97512 bytes examples/info.js | 21 ++ src/api/api_classes.cpp | 263 ++++++++++++++++ src/api/api_context.cpp | 141 +++++++++ src/api/api_enums.cpp | 76 +++++ src/api/hostapi.hpp | 31 ++ src/api/js_context.hpp | 7 + src/api/js_types.hpp | 380 +++++++++++++++++++++++ src/api/js_utils.hpp | 231 ++++++++++++++ src/config.hpp | 18 ++ src/core.cpp | 54 ++++ src/core.hpp | 24 ++ src/js_engine.cpp | 104 +++++++ src/js_engine.hpp | 38 +++ src/main.cpp | 35 +++ src/vectorjs.d.ts | 149 +++++++++ 27 files changed, 2124 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/verify.yml create mode 100644 CMakeLists.txt create mode 100644 examples/2d/clock.js create mode 100644 examples/2d/shapes.js create mode 100644 examples/2d/shapes2.js create mode 100644 examples/2d/solar2d.js create mode 100644 examples/2d/text.js create mode 100644 examples/assets/AnonymousPro-Regular.ttf create mode 100644 examples/assets/sample.png create mode 100644 examples/assets/sound.wav create mode 100644 examples/info.js create mode 100644 src/api/api_classes.cpp create mode 100644 src/api/api_context.cpp create mode 100644 src/api/api_enums.cpp create mode 100644 src/api/hostapi.hpp create mode 100644 src/api/js_context.hpp create mode 100644 src/api/js_types.hpp create mode 100644 src/api/js_utils.hpp create mode 100644 src/config.hpp create mode 100644 src/core.cpp create mode 100644 src/core.hpp create mode 100644 src/js_engine.cpp create mode 100644 src/js_engine.hpp create mode 100644 src/main.cpp create mode 100644 src/vectorjs.d.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ed35756 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,69 @@ +name: Release + +on: + release: + types: [published] + +permissions: + contents: write + +jobs: + release-ubuntu: + name: Release on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Install ubuntu Dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake g++-14 ninja-build libasound2-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxcursor-dev libxinerama-dev + - name: Configure CMake + run: cmake -B ${{ github.workspace }}/build -S . -DCMAKE_BUILD_TYPE=Release + env: + CXX: g++-14 + - name: Build Target + run: cmake --build ${{ github.workspace }}/build --config Release + - name: Package Asset + run: tar -czvf vectorjs-linux.tar.gz -C ${{ github.workspace }}/build vectorjs + - name: Upload Binary to Release + uses: softprops/action-gh-release@v2 + with: + files: vectorjs-linux.tar.gz + + release-windows: + name: Release on Windows + runs-on: windows-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Configure CMake + run: cmake -B ${{ github.workspace }}\build -S . -DCMAKE_BUILD_TYPE=Release + - name: Build Target + run: cmake --build ${{ github.workspace }}\build --config Release + - name: Package Asset + shell: pwsh + run: Compress-Archive -Path "${{ github.workspace }}\build\Release\vectorjs.exe" -DestinationPath "vectorjs-windows.zip" + - name: Upload Binary to Release + uses: softprops/action-gh-release@v2 + with: + files: vectorjs-windows.zip + + release-mac: + name: Release on macOS + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Install Ninja + run: brew install ninja + - name: Configure CMake + run: cmake -B ${{ github.workspace }}/build -S . -DCMAKE_BUILD_TYPE=Release + - name: Build Target + run: cmake --build ${{ github.workspace }}/build --config Release + - name: Package Asset + run: tar -czvf vectorjs-macos.tar.gz -C ${{ github.workspace }}/build vectorjs + - name: Upload Binary to Release + uses: softprops/action-gh-release@v2 + with: + files: vectorjs-macos.tar.gz \ No newline at end of file diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 0000000..609480c --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,59 @@ +name: Verify +concurrency: + cancel-in-progress: false + group: verify + +on: + push: + branches: [ "main", "master" ] + pull_request: + branches: [ "main", "master" ] + +jobs: + verify-ubuntu: + name: Verify on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Install ubuntu Dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake g++-14 ninja-build libasound2-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxcursor-dev libxinerama-dev + - name: Configure CMake + run: cmake -B ${{ github.workspace }}/build -S . -DCMAKE_BUILD_TYPE=Release + env: + CXX: g++-14 + - name: Build Target + run: cmake --build ${{ github.workspace }}/build --config Release + - name: Verify Executable Production + run: ls -l ${{ github.workspace }}/build/vectorjs + + verify-windows: + name: Verify on Windows + runs-on: windows-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Configure CMake + run: cmake -B ${{ github.workspace }}\build -S . -DCMAKE_BUILD_TYPE=Release + - name: Build Target + run: cmake --build ${{ github.workspace }}\build --config Release + - name: Verify Executable Production + shell: pwsh + run: Test-Path "${{ github.workspace }}\build\Release\vectorjs.exe" + + verify-mac: + name: Verify on macOS + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Install Ninja + run: brew install ninja + - name: Configure CMake + run: cmake -B ${{ github.workspace }}/build -S . -DCMAKE_BUILD_TYPE=Release + - name: Build Target + run: cmake --build ${{ github.workspace }}/build --config Release + - name: Verify Executable Production + run: ls -l ${{ github.workspace }}/build/vectorjs \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..642775e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.28) +project(vectorjs VERSION 0.1.0 LANGUAGES CXX) + +# Require C++23 standard +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +include(FetchContent) + +option(ENABLE_MSVC_WIN_MAIN "Hide the console window for MSVC builds" OFF) + +# ----------------------------------------------------------------------------- +# 1. Dependencies +# ----------------------------------------------------------------------------- + +# CLI11 (Header-only CLI parser) +FetchContent_Declare( + cli11 + GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git + GIT_TAG v2.6.2 +) +set(CLI11_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(CLI11_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + +# raylib (Windowing, input, and graphics library) +FetchContent_Declare( + raylib + GIT_REPOSITORY https://github.com/raysan5/raylib.git + GIT_TAG 6.0 +) +set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) +set(BUILD_GAMES OFF CACHE BOOL "" FORCE) + +# qjs +FetchContent_Declare( + qjs + GIT_REPOSITORY https://github.com/quickjs-ng/quickjs + GIT_TAG v0.15.1 +) + +# Bring all dependencies into the build tree automatically +FetchContent_MakeAvailable(cli11 raylib qjs) + +# ----------------------------------------------------------------------------- +# 2. Executable Target +# ----------------------------------------------------------------------------- + +# Define the target executable +add_executable(vectorjs) + +# Add source files to the target +target_sources(vectorjs PRIVATE + "src/main.cpp" + "src/js_engine.cpp" + "src/core.cpp" + "src/api/api_context.cpp" + "src/api/api_classes.cpp" + "src/api/api_enums.cpp" +) + +# Hide console on Windows, keep standard main() entry point +if(MSVC AND ENABLE_MSVC_WIN_MAIN) + target_link_options(vectorjs PRIVATE + "LINKER:/SUBSYSTEM:WINDOWS" + "LINKER:/ENTRY:mainCRTStartup" + ) +endif() + +# Include directories +target_include_directories(vectorjs PRIVATE src) + +# Link dependencies +target_link_libraries(vectorjs PRIVATE + CLI11::CLI11 + raylib + qjs +) + +# ----------------------------------------------------------------------------- +# 3. Options +# ----------------------------------------------------------------------------- + +#function(add_feature_flag OPTION_NAME DESCRIPTION DEFAULT_VAL MACRO_NAME) +# option(${OPTION_NAME} "${DESCRIPTION}" ${DEFAULT_VAL}) +# +# if(${OPTION_NAME}) +# target_compile_definitions(vectorjs PRIVATE ${MACRO_NAME}=1) +# else() +# target_compile_definitions(vectorjs PRIVATE ${MACRO_NAME}=0) +# endif() +#endfunction() +# +#add_feature_flag(ENABLE_2D "Enable 2D graphics systems" ON ENABLE_FEATURE_2D) +#add_feature_flag(ENABLE_3D "Enable 3D graphics systems" ON ENABLE_FEATURE_3D) +#add_feature_flag(ENABLE_FONT "Enable fonts" OFF ENABLE_FEATURE_FONT) +#add_feature_flag(ENABLE_AUDIO "Enable audio engine" ON ENABLE_FEATURE_AUDIO) + diff --git a/README.MD b/README.MD index e69de29..2805ebc 100644 --- a/README.MD +++ b/README.MD @@ -0,0 +1,41 @@ +# VectorJS + +## Dependencies +* Raylib +* Quickjs-ng + +## Example +```js +import {Application, Rectangle, Vector2, Palette } from "vectorjs"; + +const screenWidth = 800; +const screenHeight = 600; +const fpsPos = new Vector2(10, 10); +const rect = new Rectangle(30, 30, 200, 45); +const point1 = new Vector2(400, 150); +const point2 = new Vector2(300, 350); +const point3 = new Vector2(500, 350); + +const app = new Application(screenHeight, screenWidth, "Window"); +app.run({ + + onDraw(render) { + render.withLayer2D((ctx) => { + ctx.drawFPS(fpsPos); + + ctx.shapes.drawRectangle(rect, { + color: Palette.RED + }) + + ctx.shapes.drawCircle(point1, 60, { + color: Palette.GREEN + }) + + ctx.shapes.drawTriangle(point1, point2, point3, { + color: Palette.BLUE + }) + + }); + } +}); +``` \ No newline at end of file diff --git a/examples/2d/clock.js b/examples/2d/clock.js new file mode 100644 index 0000000..b0dea74 --- /dev/null +++ b/examples/2d/clock.js @@ -0,0 +1,91 @@ +import { Application, Vector2, Palette } from "vectorjs"; + +// Window configuration +const screenWidth = 600; +const screenHeight = 600; +const center = new Vector2(screenWidth / 2, screenHeight / 2); +const fpsPos = new Vector2(10, 10); + +// Clock dimensions +const clockRadius = 200; +const hourHandLength = 100; +const minuteHandLength = 140; +const secondHandLength = 160; + +const app = new Application(screenWidth, screenHeight, "Analog Clock"); + +app.run({ + onDraw(render) { + render.withLayer2D((ctx) => { + + // 1. Draw Clock Face Outer Ring & Center + ctx.shapes.drawCircle(center, clockRadius, { + color: Palette.WHITE + }); + ctx.shapes.drawCircle(center, 8, { + color: Palette.BLACK + }); + + // 2. Draw Hour Ticks (12 main markers) + for (let i = 0; i < 12; i++) { + const angle = (i * 30 - 90) * (Math.PI / 180); + const innerRadius = clockRadius - 20; + + const tickStart = new Vector2( + center.x + innerRadius * Math.cos(angle), + center.y + innerRadius * Math.sin(angle) + ); + const tickEnd = new Vector2( + center.x + clockRadius * Math.cos(angle), + center.y + clockRadius * Math.sin(angle) + ); + + ctx.shapes.drawLine(tickStart, tickEnd, { + color: Palette.GRAY + }); + } + + // 3. Get Current Time + const now = new Date(); + const hours = now.getHours() % 12; + const minutes = now.getMinutes(); + const seconds = now.getSeconds(); + const milliseconds = now.getMilliseconds(); + + // Smooth calculations for hand angles (in radians, -90 deg offset to start at 12 o'clock) + const secondAngle = ((seconds + milliseconds / 1000) * 6 - 90) * (Math.PI / 180); + const minuteAngle = ((minutes + seconds / 60) * 6 - 90) * (Math.PI / 180); + const hourAngle = ((hours + minutes / 60) * 30 - 90) * (Math.PI / 180); + + // Calculate hand end positions + const hourHandEnd = new Vector2( + center.x + hourHandLength * Math.cos(hourAngle), + center.y + hourHandLength * Math.sin(hourAngle) + ); + const minuteHandEnd = new Vector2( + center.x + minuteHandLength * Math.cos(minuteAngle), + center.y + minuteHandLength * Math.sin(minuteAngle) + ); + const secondHandEnd = new Vector2( + center.x + secondHandLength * Math.cos(secondAngle), + center.y + secondHandLength * Math.sin(secondAngle) + ); + + // 4. Draw Clock Hands + // Hour Hand + ctx.shapes.drawLine(center, hourHandEnd, { + color: Palette.BLUE + }); + + // Minute Hand + ctx.shapes.drawLine(center, minuteHandEnd, { + color: Palette.GREEN + }); + + // Second Hand + ctx.shapes.drawLine(center, secondHandEnd, { + color: Palette.RED + }); + }); + } +}); \ No newline at end of file diff --git a/examples/2d/shapes.js b/examples/2d/shapes.js new file mode 100644 index 0000000..242f4d4 --- /dev/null +++ b/examples/2d/shapes.js @@ -0,0 +1,41 @@ +import {Application, Rectangle, Vector2, Palette } from "vectorjs"; + +const screenWidth = 600; +const screenHeight = 800; +const fpsPos = new Vector2(10, 10); +const rect = new Rectangle(30, 30, 200, 45); +const point1 = new Vector2(400, 150); +const point2 = new Vector2(300, 350); +const point3 = new Vector2(500, 350); +const point4 = new Vector2(700, 500); + +const app = new Application(screenWidth, screenHeight,"Window"); +app.run({ + + onDraw(render) { + render.withLayer2D((ctx) => { + ctx.drawFPS(fpsPos); + + ctx.shapes.drawRectangle(rect, { + color: Palette.RED + }) + + ctx.shapes.drawCircle(point1, 60, { + color: Palette.GREEN + }) + + ctx.shapes.drawTriangle(point1, point2, point3, { + color: Palette.BLUE + }) + + ctx.shapes.drawEllipse(point4, 75.0, 50.0, { + color: Palette.ORANGE + }) + + ctx.shapes.drawLine(point4, point1, { + color: Palette.BROWN + }); + + }); + } +}); \ No newline at end of file diff --git a/examples/2d/shapes2.js b/examples/2d/shapes2.js new file mode 100644 index 0000000..cadaa27 --- /dev/null +++ b/examples/2d/shapes2.js @@ -0,0 +1,78 @@ +import { Application, Vector2, Palette } from "vectorjs"; + +const screenWidth = 800; +const screenHeight = 600; +const center = new Vector2(screenWidth / 2, screenHeight / 2); +const fpsPos = new Vector2(10, 10); + +const app = new Application(screenHeight, screenWidth, "Animated Shapes"); + +app.run({ + onDraw(render) { + render.withLayer2D((ctx) => { + ctx.drawFPS(fpsPos); + + // Time variable driving all animations + const time = Date.now() * 0.003; + + // 1. Pulsing Center Circle + const pulseRadius = 50 + Math.sin(time * 2) * 20; + ctx.shapes.drawCircle(center, pulseRadius, { + color: Palette.RED + }); + + // 2. Bouncing Ball (Vertical Sinusoidal Motion) + const bounceY = center.y + Math.abs(Math.sin(time * 3)) * -200 + 100; + const bouncePos = new Vector2(150, bounceY); + ctx.shapes.drawCircle(bouncePos, 30, { + color: Palette.ORANGE + }); + + // 3. Orbiting Satellite (Circular Path) + const orbitRadius = 180; + const orbitPos = new Vector2( + center.x + Math.cos(time) * orbitRadius, + center.y + Math.sin(time) * orbitRadius + ); + + // Connection line to center + ctx.shapes.drawLine(center, orbitPos, { + color: Palette.GRAY + }); + + ctx.shapes.drawCircle(orbitPos, 15, { + color: Palette.BLUE + }); + + // 4. Rotating Triangle Vertices + const triangleRadius = 80; + const triangleCenter = new Vector2(650, 200); + + const p1 = new Vector2( + triangleCenter.x + Math.cos(time) * triangleRadius, + triangleCenter.y + Math.sin(time) * triangleRadius + ); + const p2 = new Vector2( + triangleCenter.x + Math.cos(time + (2 * Math.PI / 3)) * triangleRadius, + triangleCenter.y + Math.sin(time + (2 * Math.PI / 3)) * triangleRadius + ); + const p3 = new Vector2( + triangleCenter.x + Math.cos(time + (4 * Math.PI / 3)) * triangleRadius, + triangleCenter.y + Math.sin(time + (4 * Math.PI / 3)) * triangleRadius + ); + + ctx.shapes.drawTriangle(p1, p2, p3, { + color: Palette.GREEN + }); + + // 5. Morphing Ellipse (Width & Height Oscillations) + const ellipsePos = new Vector2(650, 450); + const radiusX = 60 + Math.cos(time * 2) * 30; + const radiusY = 60 + Math.sin(time * 2) * 30; + + ctx.shapes.drawEllipse(ellipsePos, radiusX, radiusY, { + color: Palette.BROWN + }); + }); + } +}); \ No newline at end of file diff --git a/examples/2d/solar2d.js b/examples/2d/solar2d.js new file mode 100644 index 0000000..9a1a275 --- /dev/null +++ b/examples/2d/solar2d.js @@ -0,0 +1,48 @@ +import { Application, Vector2, Palette } from "vectorjs"; + +const screenWidth = 800; +const screenHeight = 800; +const sunPos = new Vector2(screenWidth / 2, screenHeight / 2); +const fpsPos = new Vector2(10, 10); + +// Define planets with varied speeds and orbital radii +const planets = [ + { name: "Mercury", radius: 70, size: 6, speed: 2.5, color: Palette.GRAY }, + { name: "Venus", radius: 110, size: 10, speed: 1.8, color: Palette.ORANGE }, + { name: "Earth", radius: 170, size: 12, speed: 1.2, color: Palette.BLUE }, + { name: "Mars", radius: 240, size: 9, speed: 0.9, color: Palette.RED }, + { name: "Jupiter", radius: 320, size: 22, speed: 0.5, color: Palette.BROWN } +]; + +const app = new Application(screenHeight, screenWidth, "Solar System Simulation"); + +app.run({ + onDraw(render) { + render.withLayer2D((ctx) => { + ctx.drawFPS(fpsPos); + + const time = Date.now() * 0.001; + + // 1. Draw the central Sun + ctx.shapes.drawCircle(sunPos, 35, { + color: Palette.YELLOW + }); + + // 2. Render each planet along its orbital path + planets.forEach((planet) => { + + // Calculate planet position based on its distinct speed + const angle = time * planet.speed; + const planetPos = new Vector2( + sunPos.x + Math.cos(angle) * planet.radius, + sunPos.y + Math.sin(angle) * planet.radius + ); + + // Draw planet + ctx.shapes.drawCircle(planetPos, planet.size, { + color: planet.color + }); + }); + }); + } +}); \ No newline at end of file diff --git a/examples/2d/text.js b/examples/2d/text.js new file mode 100644 index 0000000..bd4eac5 --- /dev/null +++ b/examples/2d/text.js @@ -0,0 +1,27 @@ +import {Application, Rectangle, Vector2, Palette, Font} from "vectorjs"; + +const screenWidth = 600; +const screenHeight = 800; +const fpsPos = new Vector2(10, 10); +const origin = new Vector2(0, 0); + +const app = new Application(screenWidth, screenHeight,"Window"); +app.run({ + + onInit() { + this.customFont = new Font("C:\\workspace\\c\\vectorjs\\examples\\assets\\AnonymousPro-Regular.ttf"); + }, + + onDraw(render) { + render.withLayer2D((ctx) => { + ctx.text.drawText(fpsPos, "this is a sample text", { + font: this.customFont, + color: Palette.RED, + fontSize: 24.0, + spacing: 2.0, + rotation: 0.0, + origin: origin + }) + }); + } +}); \ No newline at end of file diff --git a/examples/assets/AnonymousPro-Regular.ttf b/examples/assets/AnonymousPro-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a98da85573fdb994a11582261fc83e1a2d6963a2 GIT binary patch literal 112280 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
ug~q-O09tp-EHLRv6OF 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 X_N$h!)eGl$H@w}we0lfVP}u(;NDbM+ literal 0 HcmV?d00001 diff --git a/examples/assets/sample.png b/examples/assets/sample.png new file mode 100644 index 0000000000000000000000000000000000000000..12d841c802668edfbb2dff772d3bf9ed24d784fa GIT binary patch literal 39291 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

sXbKo&VRpThznGxwBln# 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

kvOD%h>l?~6+d9Xnl0TdwWshLTOY ziU(NcgKp(g$%Jxwk02+BR+;$RiJ(|w^qO58wEpVUdC;wTnwrK`D^+q@t&=Q9#Apck z(JSJu=siMPWvD4&Tg}zlX@g~LT4{JheO$Y(P}MJ|vwt^S6%wG1xoUE~!Fo#`c%t#A zLSCqngl7&Xk~TQN1I{tkYG+j1lE!pSLL)_%+F;8D=~&ZST1hGLd5tm&{(6w2%p=029vK7 z~Chb=lUPax1?pdSV^r8?yx!UdDIkp3GVROo$q;zH%SmYkE9BW*zPCwb^Ca7UtV&1 z;O#m5eR8nVu*OiNN4J;GY`=O3#Tn~VCX98x`iNl%jk>!jLo%AJ4l9T339~m@o|D$! zGwZD`b^v*8K4&Y}oX-4BHXIB5iMsvzHWS-_ZvIIXw6;+f&>hR)@v@JwgPzT-uK#p) z*$rkJzZgSIVYTFsN#pL-}L8K=uvDAZe({tT|rW8VDZ}HPSr3tVq}%;j7)sa z{`Mf3^2?)V7_U2D+9>@~fCR8)Mcl zC|${-(-0U`ZRlbYxhDxWaYfJU;kBuhFE@7M2ZZC{XW@M4U%30-2wxXU@hO|vjm>W> z1%@?W^$2l$Um*vyWmTx(xS%D0URvAxXTHdycCOnHD%>I)%pSywOIF<0LGuEFirk zEHRtt&CnClWimF1CKaOc`#s16l%GD2$;g0xrZ;4aIVKi?{y=fE=v9 literal 0 HcmV?d00001 diff --git a/examples/assets/sound.wav b/examples/assets/sound.wav new file mode 100644 index 0000000000000000000000000000000000000000..b5d01c9b3a928d211a15d959f62b45a4e80ed457 GIT binary patch literal 97512 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 Y{PQKzN5YHCSLj;JnG$a)_W$4i07a?8Q~&?~ literal 0 HcmV?d00001 diff --git a/examples/info.js b/examples/info.js new file mode 100644 index 0000000..685678d --- /dev/null +++ b/examples/info.js @@ -0,0 +1,21 @@ +import {Application, Vector2, Palette, Font, Info} from "vectorjs"; + +const screenWidth = 600; +const screenHeight = 800; +const line1 = new Vector2(10, 10); +const line2 = new Vector2(10, 30); + +const app = new Application(screenWidth, screenHeight,"Info"); +app.run({ + + onInit() { + this.customFont = new Font("C:\\workspace\\c\\vectorjs\\examples\\assets\\AnonymousPro-Regular.ttf"); + }, + + onDraw(render) { + render.withLayer2D((ctx) => { + ctx.text.drawText(line1, `RAYLIB VERSION: ${Info.RAYLIB_VERSION}`) + ctx.text.drawText(line2, `QUICKJS VERSION: ${Info.QUICKJS_VERSION}`) + }); + } +}); \ No newline at end of file diff --git a/src/api/api_classes.cpp b/src/api/api_classes.cpp new file mode 100644 index 0000000..49d5595 --- /dev/null +++ b/src/api/api_classes.cpp @@ -0,0 +1,263 @@ +#include "hostapi.hpp" +#include "js_types.hpp" +#include "js_utils.hpp" +#include "js_context.hpp" +#include "../config.hpp" + +#define JS_BIND_PROP(ClassType, ClassIDPtr, name, Member) \ + JS_CGETSET_DEF( \ + name, \ + (&HostApi::Utils::js_generic_getter), \ + (&HostApi::Utils::js_generic_setter) \ + ) + +namespace HostApi { + + static void register_application_class(JSContext* ctx, JSModuleDef* m) { + static constexpr JSCFunctionListEntry proto_funcs[] = { + JS_CFUNC_DEF("run", 1, [](JSContext* c, JSValueConst this_val, int argc, JSValueConst* argv) -> JSValue { + const auto* app = Utils::get_opaque(c, this_val, js_application_class_id); + return app ? app->Run(c, this_val, argc, argv) + : JS_ThrowTypeError(c, "Invalid JSApplication instance"); + }) + }; + + Utils::register_js_class(ctx, m, { + .name = "Application", + .class_id = js_application_class_id, + .finalizer = [](auto*, JSValue val) { + delete Utils::get_opaque(val, js_application_class_id); + }, + .constructor = [](auto c, auto new_target, const int argc, auto argv) -> JSValue { + int32_t h = 0, w = 0; + if (argc > 0) JS_ToInt32(c, &h, argv[0]); + if (argc > 1) JS_ToInt32(c, &w, argv[1]); + std::string title = argc > 2 ? Utils::js_to_std_string(c, argv[2]) : "VectorJS Application"; + return Utils::create_js_instance(c, new_target, js_application_class_id, h, w, std::move(title)); + }, + .proto_funcs = proto_funcs + }); + } + + JSApplication::JSApplication(const int h, const int w, const std::string& title) { + InitWindow(w, h, title.c_str()); + SetTargetFPS(60); + } + + JSValue JSApplication::Run(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + if (argc < 1) return JS_ThrowTypeError(ctx, "Expected a user application config object"); + JSValueConst user_app = argv[0]; + + Utils::ScopedJSValue on_init_func(ctx, JS_GetPropertyStr(ctx, user_app, "onInit")); + Utils::ScopedJSValue on_update_func(ctx, JS_GetPropertyStr(ctx, user_app, "onUpdate")); + Utils::ScopedJSValue on_draw_func(ctx, JS_GetPropertyStr(ctx, user_app, "onDraw")); + + // Instantiate rendering and context objects ONCE outside the frame loop + Utils::ScopedJSValue update_obj(ctx, create_update_context_object(ctx)); + Utils::ScopedJSValue render_obj(ctx, create_draw_render_object(ctx)); + + if (JS_IsFunction(ctx, on_init_func)) { + Utils::ScopedJSValue ret(ctx, JS_Call(ctx, on_init_func, user_app, 0, nullptr)); + if (JS_IsException(ret)) return JS_EXCEPTION; + } + + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + + if (JS_IsFunction(ctx, on_update_func)) { + JSValue u = update_obj.get(); + Utils::ScopedJSValue ret(ctx, JS_Call(ctx, on_update_func, user_app, 1, &u)); + if (JS_IsException(ret)) { + EndDrawing(); + break; + } + } + if (JS_IsFunction(ctx, on_draw_func)) { + JSValue r = render_obj.get(); + Utils::ScopedJSValue ret(ctx, JS_Call(ctx, on_draw_func, user_app, 1, &r)); + if (JS_IsException(ret)) { + EndDrawing(); + break; + } + } + EndDrawing(); + } + + if (IsWindowReady()) { + CloseWindow(); + } + + return JS_UNDEFINED; + } + + static void register_color_class(JSContext* ctx, JSModuleDef* m) { + static constexpr JSCFunctionListEntry proto_funcs[] = { + JS_BIND_PROP(JSColor, &js_color_class_id, "r", r), + JS_BIND_PROP(JSColor, &js_color_class_id, "g", g), + JS_BIND_PROP(JSColor, &js_color_class_id, "b", b), + JS_BIND_PROP(JSColor, &js_color_class_id, "a", a), + }; + + Utils::register_js_class(ctx, m, { + .name = "Color", + .class_id = js_color_class_id, + .finalizer = [](auto, JSValue val) { + delete Utils::get_opaque(val, js_color_class_id); + }, + .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { + if (argc >= 4) { + int32_t r = 0, g = 0, b = 0, a = 255; + JS_ToInt32(c, &r, argv[0]); + JS_ToInt32(c, &g, argv[1]); + JS_ToInt32(c, &b, argv[2]); + JS_ToInt32(c, &a, argv[3]); + + return Utils::create_js_instance( + c, new_target, js_color_class_id, + static_cast(r), static_cast(g), + static_cast(b), static_cast(a) + ); + } + return Utils::create_js_instance(c, new_target, js_color_class_id); + }, + .proto_funcs = proto_funcs + }); + } + + static void register_vector2_class(JSContext* ctx, JSModuleDef* m) { + static constexpr JSCFunctionListEntry proto_funcs[] = { + JS_BIND_PROP(JSVector2, &js_vector2_class_id, "x", x), + JS_BIND_PROP(JSVector2, &js_vector2_class_id, "y", y), + }; + + Utils::register_js_class(ctx, m, { + .name = "Vector2", + .class_id = js_vector2_class_id, + .finalizer = [](auto, JSValue val) { + delete Utils::get_opaque(val, js_vector2_class_id); + }, + .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { + int32_t x = 0, y = 0; + if (argc > 0) JS_ToInt32(c, &x, argv[0]); + if (argc > 1) JS_ToInt32(c, &y, argv[1]); + + return Utils::create_js_instance( + c, new_target, js_vector2_class_id, + static_cast(x), static_cast(y) + ); + }, + .proto_funcs = proto_funcs + }); + } + + static void register_rectangle_class(JSContext* ctx, JSModuleDef* m) { + static constexpr JSCFunctionListEntry proto_funcs[] = { + JS_BIND_PROP(JSRectangle, &js_rectangle_class_id, "x", x), + JS_BIND_PROP(JSRectangle, &js_rectangle_class_id, "y", y), + JS_BIND_PROP(JSRectangle, &js_rectangle_class_id, "width", width), + JS_BIND_PROP(JSRectangle, &js_rectangle_class_id, "height", height), + }; + + Utils::register_js_class(ctx, m, { + .name = "Rectangle", + .class_id = js_rectangle_class_id, + .finalizer = [](auto, JSValue val) { + delete Utils::get_opaque(val, js_rectangle_class_id); + }, + .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { + double x = 0, y = 0, w = 0, h = 0; + if (argc > 0) JS_ToFloat64(c, &x, argv[0]); + if (argc > 1) JS_ToFloat64(c, &y, argv[1]); + if (argc > 2) JS_ToFloat64(c, &w, argv[2]); + if (argc > 3) JS_ToFloat64(c, &h, argv[3]); + + return Utils::create_js_instance( + c, new_target, js_rectangle_class_id, + static_cast(x), static_cast(y), + static_cast(w), static_cast(h) + ); + }, + .proto_funcs = proto_funcs + }); + } + + static void register_font_class(JSContext* ctx, JSModuleDef* m) { + Utils::register_js_class(ctx, m, { + .name = "Font", + .class_id = js_font_class_id, + .finalizer = [](auto, JSValue val) { + delete Utils::get_opaque(val, js_font_class_id); + }, + .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { + if (argc < 1) return JS_ThrowTypeError(c, "Font requires at least a path argument"); + + auto font_path = Utils::js_to_std_string(c, argv[0]); + if (font_path.empty()) return JS_EXCEPTION; + + if (argc >= 2) { + int32_t size = 0; + JS_ToInt32(c, &size, argv[1]); + return Utils::create_js_instance(c, new_target, js_font_class_id, font_path, size); + } + + return Utils::create_js_instance(c, new_target, js_font_class_id, font_path); + } + }); + } + + static void register_image_class(JSContext* ctx, JSModuleDef* m) { + static constexpr JSCFunctionListEntry proto_funcs[] = { + JS_CGETSET_DEF( + "width", + ([](JSContext* c, JSValueConst this_val) -> JSValue { + const auto* img = Utils::get_opaque(this_val, js_image_class_id); + return img ? JS_NewInt32(c, img->get_width()) : JS_UNDEFINED; + }), + nullptr + ), + JS_CGETSET_DEF( + "height", + ([](JSContext* c, JSValueConst this_val) -> JSValue { + const auto* img = Utils::get_opaque(this_val, js_image_class_id); + return img ? JS_NewInt32(c, img->get_height()) : JS_UNDEFINED; + }), + nullptr + ) + }; + + Utils::register_js_class(ctx, m, { + .name = "Image", + .class_id = js_image_class_id, + .finalizer = [](auto, JSValue val) { + delete Utils::get_opaque(val, js_image_class_id); + }, + .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { + if (argc < 1) return JS_ThrowTypeError(c, "Image constructor requires a file path argument"); + + auto image_path = Utils::js_to_std_string(c, argv[0]); + if (image_path.empty()) return JS_EXCEPTION; + + return Utils::create_js_instance(c, new_target, js_image_class_id, image_path); + }, + .proto_funcs = proto_funcs + }); + } + + void register_hapi_classes(JSContext* ctx, JSModuleDef* m) { + + register_application_class(ctx, m); + + register_color_class(ctx, m); + register_vector2_class(ctx, m); + register_rectangle_class(ctx, m); + register_image_class(ctx, m); + + if constexpr (config::features::enable_fonts) { + register_font_class(ctx, m); + } + } + +} // namespace HostApi + +#undef JS_BIND_PROP \ No newline at end of file diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp new file mode 100644 index 0000000..96de5ca --- /dev/null +++ b/src/api/api_context.cpp @@ -0,0 +1,141 @@ +#include "hostapi.hpp" +#include "js_types.hpp" +#include "js_utils.hpp" +#include + +namespace HostApi { + + static JSDrawOptions parse_draw_options(JSContext* ctx, JSValueConst optionsObj) { + JSDrawOptions options; + if (JS_IsObject(optionsObj)) { + Utils::try_get_opaque_property(ctx, optionsObj, "color", js_color_class_id, options.color); + } + return options; + } + + static JSTextOptions parse_text_options(JSContext* ctx, JSValueConst optionsObj) { + JSTextOptions options; + if (JS_IsObject(optionsObj)) { + Utils::try_get_opaque_property(ctx, optionsObj, "font", js_font_class_id, options.font); + Utils::try_get_opaque_property(ctx, optionsObj, "color", js_color_class_id, options.color); + Utils::try_get_float_property(ctx, optionsObj, "rotation", options.rotation); + Utils::try_get_float_property(ctx, optionsObj, "fontSize", options.fontSize); + Utils::try_get_float_property(ctx, optionsObj, "spacing", options.spacing); + Utils::try_get_opaque_property(ctx, optionsObj, "origin", js_vector2_class_id, options.origin); + } + return options; + } + + JSValue create_update_context_object(JSContext* ctx) { + return JS_NewObject(ctx); + } + + JSValue create_draw_render_object(JSContext* ctx) { + Utils::ScopedJSValue render2d_obj(ctx, JS_NewObject(ctx)); + + // --- FPS Binding --- + JS_SetPropertyStr(ctx, render2d_obj, "drawFPS", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { + if (argc < 1) return JS_ThrowTypeError(c, "drawFPS requires a Vector2 position argument"); + const auto pos = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); + ::DrawFPS(static_cast(pos.x), static_cast(pos.y)); + return JS_UNDEFINED; + }, "drawFPS", 1)); + + // --- Shapes Sub-Object --- + Utils::ScopedJSValue shape_obj(ctx, JS_NewObject(ctx)); + + JS_SetPropertyStr(ctx, shape_obj, "drawPixel", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { + if (argc < 2) return JS_ThrowTypeError(c, "drawPixel requires position and color arguments"); + const auto pos = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); + const auto col = Utils::get_opaque_or(c, argv[1], js_color_class_id, JSColor(RAYWHITE)); + ::DrawPixelV(pos, col); + return JS_UNDEFINED; + }, "drawPixel", 2)); + + JS_SetPropertyStr(ctx, shape_obj, "drawLine", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { + if (argc < 2) return JS_ThrowTypeError(c, "drawLine requires start and end positions"); + const auto start = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); + const auto end = Utils::get_opaque_or(c, argv[1], js_vector2_class_id, {0, 0}); + JSDrawOptions draw_options = parse_draw_options(c, argc > 2 ? argv[2] : JS_UNDEFINED); + ::DrawLineV(start, end, draw_options.color); + return JS_UNDEFINED; + }, "drawLine", 3)); + + JS_SetPropertyStr(ctx, shape_obj, "drawRectangle", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { + if (argc < 1) return JS_ThrowTypeError(c, "drawRectangle requires a Rectangle argument"); + const auto rect = Utils::get_opaque_or(c, argv[0], js_rectangle_class_id, {0, 0, 0, 0}); + JSDrawOptions draw_options = parse_draw_options(c, argc > 1 ? argv[1] : JS_UNDEFINED); + ::DrawRectanglePro(rect, draw_options.origin, draw_options.rotation, draw_options.color); + return JS_UNDEFINED; + }, "drawRectangle", 2)); + + JS_SetPropertyStr(ctx, shape_obj, "drawCircle", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { + if (argc < 2) return JS_ThrowTypeError(c, "drawCircle requires center and radius arguments"); + const auto center = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); + double rad = 0; + if (JS_ToFloat64(c, &rad, argv[1]) != 0) { + return JS_ThrowTypeError(c, "drawCircle radius must be a number"); + } + JSDrawOptions draw_options = parse_draw_options(c, argc > 2 ? argv[2] : JS_UNDEFINED); + ::DrawCircleV(center, static_cast(rad), draw_options.color); + return JS_UNDEFINED; + }, "drawCircle", 3)); + + JS_SetPropertyStr(ctx, shape_obj, "drawTriangle", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { + if (argc < 3) return JS_ThrowTypeError(c, "drawTriangle requires 3 Vector2 point arguments"); + const auto p1 = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); + const auto p2 = Utils::get_opaque_or(c, argv[1], js_vector2_class_id, {0, 0}); + const auto p3 = Utils::get_opaque_or(c, argv[2], js_vector2_class_id, {0, 0}); + JSDrawOptions draw_options = parse_draw_options(c, argc > 3 ? argv[3] : JS_UNDEFINED); + ::DrawTriangle(p1, p2, p3, draw_options.color); + return JS_UNDEFINED; + }, "drawTriangle", 4)); + + JS_SetPropertyStr(ctx, shape_obj, "drawEllipse", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { + if (argc < 3) return JS_ThrowTypeError(c, "drawEllipse requires center, radiusH, and radiusV arguments"); + const auto center = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); + double radH = 0, radV = 0; + if (JS_ToFloat64(c, &radH, argv[1]) != 0 || JS_ToFloat64(c, &radV, argv[2]) != 0) { + return JS_ThrowTypeError(c, "drawEllipse radii parameters must be numbers"); + } + JSDrawOptions draw_options = parse_draw_options(c, argc > 3 ? argv[3] : JS_UNDEFINED); + ::DrawEllipseV(center, static_cast(radH), static_cast(radV), draw_options.color); + return JS_UNDEFINED; + }, "drawEllipse", 4)); + + JS_SetPropertyStr(ctx, render2d_obj, "shapes", shape_obj.release()); + + // --- Text Sub-Object --- + Utils::ScopedJSValue text_obj(ctx, JS_NewObject(ctx)); + JS_SetPropertyStr(ctx, text_obj, "drawText", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { + if (argc < 2) return JS_ThrowTypeError(c, "drawText requires position and text string arguments"); + const auto pos = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); + + const std::string txt_str = Utils::js_to_std_string(c, argv[1]); + JSTextOptions options = parse_text_options(c, argc > 2 ? argv[2] : JS_UNDEFINED); + Font fontToUse = GetFontDefault(); + if (options.font.font_ptr && options.font.font_ptr->texture.id != 0) { + fontToUse = *options.font.font_ptr; + } + ::DrawTextPro(fontToUse, txt_str.c_str(), pos, options.origin, options.rotation, options.fontSize, options.spacing, options.color); + return JS_UNDEFINED; + }, "drawText", 3)); + + JS_SetPropertyStr(ctx, render2d_obj, "text", text_obj.release()); + + // --- Layer Wrappers --- + const JSValue render_obj = JS_NewObject(ctx); + JSValue r2d_val = render2d_obj.release(); + + JS_SetPropertyStr(ctx, render_obj, "withLayer2D", JS_NewCFunctionData(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv, int, JSValue* magic_argv) -> JSValue { + if (argc > 0 && JS_IsFunction(c, argv[0])) { + Utils::ScopedJSValue res(c, JS_Call(c, argv[0], JS_UNDEFINED, 1, &magic_argv[0])); + } + return JS_UNDEFINED; + }, 1, 0, 1, &r2d_val)); + + JS_FreeValue(ctx, r2d_val); + return render_obj; + } + +} // namespace HostApi \ No newline at end of file diff --git a/src/api/api_enums.cpp b/src/api/api_enums.cpp new file mode 100644 index 0000000..5182806 --- /dev/null +++ b/src/api/api_enums.cpp @@ -0,0 +1,76 @@ +#include "hostapi.hpp" +#include "js_utils.hpp" +#include + +namespace HostApi { + +void register_hapi_enums(JSContext* ctx, JSModuleDef* m) { + + static auto export_object = [](JSContext* js_context, JSModuleDef* js_module_def, const char* obj_name, Pairs&&... pairs) { + const JSValue obj = JS_NewObject(js_context); + Utils::set_object_properties(js_context, obj, std::forward(pairs)...); + JS_SetModuleExport(js_context, js_module_def, obj_name, obj); + }; + + constexpr static auto P = [](const char* name, V&& val) { + return std::pair{name, std::forward(val)}; + }; + + // --- Palette Object --- + export_object(ctx, m, "Palette", + P("LIGHTGRAY", LIGHTGRAY), + P("GRAY", GRAY), + P("DARKGRAY", DARKGRAY), + P("YELLOW", YELLOW), + P("GOLD", GOLD), + P("ORANGE", ORANGE), + P("PINK", PINK), + P("RED", RED), + P("MAROON", MAROON), + P("GREEN", GREEN), + P("LIME", LIME), + P("DARKGREEN", DARKGREEN), + P("SKYBLUE", SKYBLUE), + P("BLUE", BLUE), + P("DARKBLUE", DARKBLUE), + P("PURPLE", PURPLE), + P("VIOLET", VIOLET), + P("DARKPURPLE", DARKPURPLE), + P("BEIGE", BEIGE), + P("BROWN", BROWN), + P("DARKBROWN", DARKBROWN), + P("WHITE", WHITE), + P("BLACK", BLACK), + P("BLANK", BLANK), + P("MAGENTA", MAGENTA), + P("RAYWHITE", RAYWHITE) + ); + + // --- Info Object --- + export_object(ctx, m, "Info", + P("RAYLIB_VERSION", RAYLIB_VERSION_STR), + P("QUICKJS_VERSION", QUICKJS_VERSION_STR) + ); + + // --- ConfigFlags Object --- + export_object(ctx, m, "ConfigFlags", + P("FLAG_VSYNC_HINT", FLAG_VSYNC_HINT), + P("FLAG_FULLSCREEN_MODE", FLAG_FULLSCREEN_MODE), + P("FLAG_WINDOW_RESIZABLE", FLAG_WINDOW_RESIZABLE), + P("FLAG_WINDOW_UNDECORATED", FLAG_WINDOW_UNDECORATED), + P("FLAG_WINDOW_HIDDEN", FLAG_WINDOW_HIDDEN), + P("FLAG_WINDOW_MINIMIZED", FLAG_WINDOW_MINIMIZED), + P("FLAG_WINDOW_MAXIMIZED", FLAG_WINDOW_MAXIMIZED), + P("FLAG_WINDOW_UNFOCUSED", FLAG_WINDOW_UNFOCUSED), + P("FLAG_WINDOW_TOPMOST", FLAG_WINDOW_TOPMOST), + P("FLAG_WINDOW_ALWAYS_RUN", FLAG_WINDOW_ALWAYS_RUN), + P("FLAG_WINDOW_TRANSPARENT", FLAG_WINDOW_TRANSPARENT), + P("FLAG_WINDOW_HIGHDPI", FLAG_WINDOW_HIGHDPI), + P("FLAG_WINDOW_MOUSE_PASSTHROUGH", FLAG_WINDOW_MOUSE_PASSTHROUGH), + P("FLAG_BORDERLESS_WINDOWED_MODE", FLAG_BORDERLESS_WINDOWED_MODE), + P("FLAG_MSAA_4X_HINT", FLAG_MSAA_4X_HINT), + P("FLAG_INTERLACED_HINT", FLAG_INTERLACED_HINT) + ); +} + +} // namespace HostApi \ No newline at end of file diff --git a/src/api/hostapi.hpp b/src/api/hostapi.hpp new file mode 100644 index 0000000..53a89bb --- /dev/null +++ b/src/api/hostapi.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include + +namespace HostApi { + const std::string RAYLIB_VERSION_STR = std::format("{}.{}.{}", RAYLIB_VERSION_MAJOR, RAYLIB_VERSION_MINOR, RAYLIB_VERSION_PATCH); + const std::string QUICKJS_VERSION_STR = std::format("{}.{}.{}", QJS_VERSION_MAJOR, QJS_VERSION_MINOR, QJS_VERSION_PATCH); + + void register_hapi_enums(JSContext* ctx, JSModuleDef* m); + void register_hapi_classes(JSContext* ctx, JSModuleDef* m); + + inline void make_vectorjs_module(JSContext* ctx) { + JSModuleDef* m = JS_NewCModule(ctx, "vectorjs", [](JSContext *c, JSModuleDef *m) -> int { + register_hapi_classes(c, m); + register_hapi_enums(c, m); + return 0; + }); + + if (m) { + JS_AddModuleExport(ctx, m, "Application"); + JS_AddModuleExport(ctx, m, "Color"); + JS_AddModuleExport(ctx, m, "Vector2"); + JS_AddModuleExport(ctx, m, "Rectangle"); + JS_AddModuleExport(ctx, m, "Font"); + JS_AddModuleExport(ctx, m, "Palette"); + JS_AddModuleExport(ctx, m, "Info"); + JS_AddModuleExport(ctx, m, "ConfigFlags"); + } + } +} // namespace HostApi \ No newline at end of file diff --git a/src/api/js_context.hpp b/src/api/js_context.hpp new file mode 100644 index 0000000..1ccaa4d --- /dev/null +++ b/src/api/js_context.hpp @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace HostApi { + inline JSValue create_update_context_object(JSContext* ctx); + inline JSValue create_draw_render_object(JSContext* ctx); +} \ No newline at end of file diff --git a/src/api/js_types.hpp b/src/api/js_types.hpp new file mode 100644 index 0000000..6763cba --- /dev/null +++ b/src/api/js_types.hpp @@ -0,0 +1,380 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace HostApi { + + inline JSClassID js_color_class_id; + inline JSClassID js_vector2_class_id; + inline JSClassID js_rectangle_class_id; + inline JSClassID js_font_class_id; + inline JSClassID js_image_class_id; + inline JSClassID js_application_class_id; + + struct JSApplication { + JSApplication(int h, int w, const std::string& title); + static JSValue Run(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + }; + + struct JSColor { + uint8_t r = 0; + uint8_t g = 0; + uint8_t b = 0; + uint8_t a = 0; + JSColor() = default; + JSColor(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) : r(r), g(g), b(b), a(a) {} + explicit constexpr JSColor(const Color color) : r(color.r), g(color.g), b(color.b), a(color.a) {} + [[nodiscard]] constexpr operator Color() const { return Color{ r, g, b, a }; } + }; + + struct JSVector2 { + float x = 0.0f; + float y = 0.0f; + JSVector2() = default; + JSVector2(const float x, const float y) : x(x), y(y) {} + explicit constexpr JSVector2(const Vector2 v) : x(v.x), y(v.y) {} + [[nodiscard]] constexpr operator Vector2() const { return Vector2 { x, y }; } + }; + + struct JSVector3 { + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + JSVector3() = default; + JSVector3(const float x, const float y, const float z = 0.0f) : x(x), y(y), z(z) {} + explicit constexpr JSVector3(const Vector3 v) : x(v.x), y(v.y), z(v.z) {} + [[nodiscard]] constexpr operator Vector3() const { return Vector3 { x, y, z }; } + }; + + struct JSVector4 { + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + float w = 0.0f; + JSVector4() = default; + JSVector4(const float x, const float y, const float z, const float w) : x(x), y(y), z(z), w(w) {} + explicit constexpr JSVector4(const Vector4 v) : x(v.x), y(v.y), z(v.z), w(v.w) {} + [[nodiscard]] constexpr operator Vector4() const { return Vector4 { x, y, z, w }; } + }; + + struct JSMatrix { + float m0 = 1.0f, m4 = 0.0f, m8 = 0.0f, m12 = 0.0f; + float m1 = 0.0f, m5 = 1.0f, m9 = 0.0f, m13 = 0.0f; + float m2 = 0.0f, m6 = 0.0f, m10 = 1.0f, m14 = 0.0f; + float m3 = 0.0f, m7 = 0.0f, m11 = 0.0f, m15 = 1.0f; + JSMatrix() = default; + explicit constexpr JSMatrix(const Matrix m) : + m0(m.m0), m4(m.m4), m8(m.m8), m12(m.m12), + m1(m.m1), m5(m.m5), m9(m.m9), m13(m.m13), + m2(m.m2), m6(m.m6), m10(m.m10), m14(m.m14), + m3(m.m3), m7(m.m7), m11(m.m11), m15(m.m15) {} + [[nodiscard]] constexpr operator Matrix() const { + return Matrix { + m0, m4, m8, m12, + m1, m5, m9, m13, + m2, m6, m10, m14, + m3, m7, m11, m15 + }; + } + }; + + struct JSRectangle { + float x = 0.0f; + float y = 0.0f; + float width = 0.0f; + float height = 0.0f; + JSRectangle() = default; + JSRectangle(const float x, const float y, const float width, const float height) : x(x), y(y), width(width), height(height) {} + explicit constexpr JSRectangle(const Rectangle r) : x(r.x), y(r.y), width(r.width), height(r.height) {} + [[nodiscard]] constexpr operator Rectangle() const { return Rectangle { x, y, width, height }; } + }; + + struct JSCamera2D { + JSVector2 offset; + JSVector2 target; + float rotation = 0.0f; + float zoom = 1.0f; + JSCamera2D() = default; + JSCamera2D(const JSVector2& offset, const JSVector2& target, float rotation = 0.0f, float zoom = 1.0f) + : offset(offset), target(target), rotation(rotation), zoom(zoom) {} + explicit constexpr JSCamera2D(const Camera2D c) + : offset(c.offset), target(c.target), rotation(c.rotation), zoom(c.zoom) {} + [[nodiscard]] constexpr operator Camera2D() const { return Camera2D { offset, target, rotation, zoom }; } + }; + + struct JSCamera3D { + JSVector3 position; + JSVector3 target; + JSVector3 up{ 0.0f, 1.0f, 0.0f }; + float fovy = 45.0f; + int projection = CAMERA_PERSPECTIVE; + JSCamera3D() = default; + JSCamera3D(const JSVector3& pos, const JSVector3& target, const JSVector3& up = {0.0f, 1.0f, 0.0f}, float fovy = 45.0f, int proj = CAMERA_PERSPECTIVE) + : position(pos), target(target), up(up), fovy(fovy), projection(proj) {} + explicit constexpr JSCamera3D(const Camera3D c) + : position(c.position), target(c.target), up(c.up), fovy(c.fovy), projection(c.projection) {} + [[nodiscard]] constexpr operator Camera3D() const { return Camera3D { position, target, up, fovy, projection }; } + }; + + struct JSRay { + JSVector3 position; + JSVector3 direction; + JSRay() = default; + JSRay(const JSVector3& pos, const JSVector3& dir) : position(pos), direction(dir) {} + explicit constexpr JSRay(const Ray r) : position(r.position), direction(r.direction) {} + [[nodiscard]] constexpr operator Ray() const { return Ray { position, direction }; } + }; + + struct JSRayCollision { + bool hit = false; + float distance = 0.0f; + JSVector3 point; + JSVector3 normal; + JSRayCollision() = default; + explicit constexpr JSRayCollision(const RayCollision rc) + : hit(rc.hit), distance(rc.distance), point(rc.point), normal(rc.normal) {} + [[nodiscard]] constexpr operator RayCollision() const { return RayCollision { hit, distance, point, normal }; } + }; + + struct JSBoundingBox { + JSVector3 min; + JSVector3 max; + JSBoundingBox() = default; + JSBoundingBox(const JSVector3& min, const JSVector3& max) : min(min), max(max) {} + explicit constexpr JSBoundingBox(const BoundingBox bb) : min(bb.min), max(bb.max) {} + [[nodiscard]] constexpr operator BoundingBox() const { return BoundingBox { min, max }; } + }; + + struct JSNPatchInfo { + JSRectangle source; + int left = 0; + int top = 0; + int right = 0; + int bottom = 0; + int layout = NPATCH_NINE_PATCH; + JSNPatchInfo() = default; + explicit constexpr JSNPatchInfo(const NPatchInfo np) + : source(np.source), left(np.left), top(np.top), right(np.right), bottom(np.bottom), layout(np.layout) {} + [[nodiscard]] constexpr operator NPatchInfo() const { return NPatchInfo { source, left, top, right, bottom, layout }; } + }; + + struct JSGlyphInfo { + int value = 0; + int offsetX = 0; + int offsetY = 0; + int advanceX = 0; + Image image{}; + JSGlyphInfo() = default; + explicit JSGlyphInfo(const GlyphInfo g) + : value(g.value), offsetX(g.offsetX), offsetY(g.offsetY), advanceX(g.advanceX), image(g.image) {} + [[nodiscard]] operator GlyphInfo() const { return GlyphInfo { value, offsetX, offsetY, advanceX, image }; } + }; + + struct JSTransform { + JSVector3 translation; + JSVector4 rotation{0, 0, 0, 1}; + JSVector3 scale{1, 1, 1}; + JSTransform() = default; + explicit constexpr JSTransform(const Transform t) + : translation(t.translation), rotation(t.rotation), scale(t.scale) {} + [[nodiscard]] constexpr operator Transform() const { return Transform { translation, rotation, scale }; } + }; + + struct JSImage { + std::shared_ptr texture_ptr; + + JSImage() = default; + + explicit JSImage(const std::string& path) { + Texture2D tex = LoadTexture(path.c_str()); + texture_ptr = std::shared_ptr(new Texture2D(tex), [](const Texture2D* pt) { + if (pt->id != 0) { + UnloadTexture(*pt); + } + delete pt; + }); + } + + [[nodiscard]] Texture2D get_texture() const { + return texture_ptr ? *texture_ptr : Texture2D{ 0 }; + } + + [[nodiscard]] int get_width() const { return texture_ptr ? texture_ptr->width : 0; } + [[nodiscard]] int get_height() const { return texture_ptr ? texture_ptr->height : 0; } + }; + + struct JSTexture { + std::shared_ptr texture_ptr; + JSTexture() = default; + explicit JSTexture(const std::string& path) { + Texture2D tex = LoadTexture(path.c_str()); + texture_ptr = std::shared_ptr(new Texture2D(tex), [](const Texture2D* pt) { + if (pt->id != 0) { + UnloadTexture(*pt); + } + delete pt; + }); + } + }; + + struct JSRenderTexture { + std::shared_ptr target_ptr; + JSRenderTexture() = default; + JSRenderTexture(int width, int height) { + RenderTexture2D target = LoadRenderTexture(width, height); + target_ptr = std::shared_ptr(new RenderTexture2D(target), [](const RenderTexture2D* prt) { + if (prt->id != 0) { + UnloadRenderTexture(*prt); + } + delete prt; + }); + } + }; + + struct JSFont { + std::shared_ptr font_ptr; + JSFont() = default; + explicit JSFont(const std::string& path, const int baseSize = 64) { + const Font f = LoadFontEx(path.c_str(), baseSize, nullptr, 0); + if (f.texture.id != 0) { + SetTextureFilter(f.texture, TEXTURE_FILTER_BILINEAR); + } + font_ptr = std::shared_ptr(new Font(f), [](const Font* pf) { + if (pf->texture.id != 0) { + UnloadFont(*pf); + } + delete pf; + }); + } + }; + + struct JSMesh { + std::shared_ptr mesh_ptr; + JSMesh() = default; + explicit JSMesh(Mesh mesh) { + mesh_ptr = std::shared_ptr(new Mesh(mesh), [](const Mesh* pm) { + UnloadMesh(*pm); + delete pm; + }); + } + }; + + struct JSShader { + std::shared_ptr shader_ptr; + JSShader() = default; + JSShader(const std::string& vsPath, const std::string& fsPath) { + Shader s = LoadShader(vsPath.empty() ? nullptr : vsPath.c_str(), + fsPath.empty() ? nullptr : fsPath.c_str()); + shader_ptr = std::shared_ptr(new Shader(s), [](const Shader* ps) { + if (ps->id != 0) { + UnloadShader(*ps); + } + delete ps; + }); + } + }; + + struct JSMaterial { + std::shared_ptr material_ptr; + JSMaterial() = default; + explicit JSMaterial(Material mat) { + material_ptr = std::shared_ptr(new Material(mat), [](const Material* pm) { + UnloadMaterial(*pm); + delete pm; + }); + } + }; + + struct JSModel { + std::shared_ptr model_ptr; + JSModel() = default; + explicit JSModel(const std::string& path) { + Model m = LoadModel(path.c_str()); + model_ptr = std::shared_ptr(new Model(m), [](const Model* pm) { + UnloadModel(*pm); + delete pm; + }); + } + }; + + struct JSWave { + std::shared_ptr wave_ptr; + JSWave() = default; + explicit JSWave(const std::string& path) { + Wave w = LoadWave(path.c_str()); + wave_ptr = std::shared_ptr(new Wave(w), [](const Wave* pw) { + UnloadWave(*pw); + delete pw; + }); + } + }; + + struct JSSound { + std::shared_ptr sound_ptr; + JSSound() = default; + explicit JSSound(const std::string& path) { + Sound s = LoadSound(path.c_str()); + sound_ptr = std::shared_ptr(new Sound(s), [](const Sound* ps) { + UnloadSound(*ps); + delete ps; + }); + } + }; + + struct JSMusic { + std::shared_ptr music_ptr; + JSMusic() = default; + explicit JSMusic(const std::string& path) { + Music m = LoadMusicStream(path.c_str()); + music_ptr = std::shared_ptr(new Music(m), [](const Music* pm) { + UnloadMusicStream(*pm); + delete pm; + }); + } + }; + + struct JSAudioStream { + std::shared_ptr stream_ptr; + JSAudioStream() = default; + JSAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) { + AudioStream stream = LoadAudioStream(sampleRate, sampleSize, channels); + stream_ptr = std::shared_ptr(new AudioStream(stream), [](const AudioStream* pas) { + UnloadAudioStream(*pas); + delete pas; + }); + } + }; + + struct JSFilePathList { + std::shared_ptr files_ptr; + JSFilePathList() = default; + explicit JSFilePathList(const std::string& dirPath) { + FilePathList f = LoadDirectoryFiles(dirPath.c_str()); + files_ptr = std::shared_ptr(new FilePathList(f), [](const FilePathList* pf) { + UnloadDirectoryFiles(*pf); + delete pf; + }); + } + }; + + struct JSDrawOptions { + JSColor color = JSColor(BLACK); + float rotation = 0.0f; + bool wireframe = false; + JSVector2 origin = JSVector2(0, 0); + }; + + struct JSTextOptions { + JSFont font; + JSColor color = JSColor(BLACK); + float rotation = 0.0f; + float fontSize = 24.0f; + float spacing = 1.0f; + JSVector2 origin = JSVector2(0, 0); + }; + +} \ No newline at end of file diff --git a/src/api/js_utils.hpp b/src/api/js_utils.hpp new file mode 100644 index 0000000..dc94565 --- /dev/null +++ b/src/api/js_utils.hpp @@ -0,0 +1,231 @@ +#pragma once +#include +#include +#include +#include +#include "js_types.hpp" + +namespace HostApi::Utils { + + class ScopedJSValue { + public: + ScopedJSValue(JSContext* ctx, JSValue val) : ctx_(ctx), val_(val) {} + ~ScopedJSValue() { + if (ctx_ && !JS_IsUndefined(val_)) { + JS_FreeValue(ctx_, val_); + } + } + + ScopedJSValue(const ScopedJSValue&) = delete; + ScopedJSValue& operator=(const ScopedJSValue&) = delete; + + ScopedJSValue(ScopedJSValue&& other) noexcept + : ctx_(std::exchange(other.ctx_, nullptr)), val_(std::exchange(other.val_, JS_UNDEFINED)) {} + + ScopedJSValue& operator=(ScopedJSValue&& other) noexcept { + if (this != &other) { + if (ctx_ && !JS_IsUndefined(val_)) JS_FreeValue(ctx_, val_); + ctx_ = std::exchange(other.ctx_, nullptr); + val_ = std::exchange(other.val_, JS_UNDEFINED); + } + return *this; + } + + [[nodiscard]] JSValue get() const { return val_; } + [[nodiscard]] JSValue release() { + JSValue temp = val_; + val_ = JS_UNDEFINED; + return temp; + } + operator JSValue() const { return val_; } + + private: + JSContext* ctx_{nullptr}; + JSValue val_{JS_UNDEFINED}; + }; + + inline std::string js_to_std_string(JSContext* ctx, JSValueConst val, const std::string& fallback = "") { + const char* str = JS_ToCString(ctx, val); + if (!str) return fallback; + std::string result(str); + JS_FreeCString(ctx, str); + return result; + } + + template + T* get_opaque(JSContext* ctx, JSValueConst val, JSClassID class_id) { + if (JS_IsUndefined(val) || JS_IsNull(val)) return nullptr; + return static_cast(JS_GetOpaque2(ctx, val, class_id)); + } + + template + T* get_opaque(JSValueConst val, JSClassID class_id) { + return static_cast(JS_GetOpaque(val, class_id)); + } + + template + T get_opaque_or(JSContext* ctx, JSValueConst val, JSClassID class_id, const T& default_val) { + if (auto* ptr = get_opaque(ctx, val, class_id)) { + return *ptr; + } + return default_val; + } + + template + bool try_get_opaque_property(JSContext* ctx, JSValueConst obj, const char* prop_name, JSClassID class_id, T& out_val) { + if (!JS_IsObject(obj)) return false; + + ScopedJSValue prop(ctx, JS_GetPropertyStr(ctx, obj, prop_name)); + if (!JS_IsUndefined(prop.get()) && !JS_IsNull(prop.get())) { + if (auto* ptr = get_opaque(ctx, prop.get(), class_id)) { + out_val = *ptr; + return true; + } + } + return false; + } + + inline bool try_get_float_property(JSContext* ctx, JSValueConst obj, const char* prop_name, float& out_val) { + if (!JS_IsObject(obj)) return false; + + ScopedJSValue prop(ctx, JS_GetPropertyStr(ctx, obj, prop_name)); + if (!JS_IsUndefined(prop.get()) && JS_IsNumber(prop.get())) { + double temp = 0; + if (JS_ToFloat64(ctx, &temp, prop.get()) == 0) { + out_val = static_cast(temp); + return true; + } + } + return false; + } + + // --- 1. Generic Getters and Setters --- + + template + JSValue js_generic_getter(JSContext* ctx, JSValueConst this_val) { + auto* instance = get_opaque(ctx, this_val, *ClassID); + if (!instance) return JS_EXCEPTION; + + if constexpr (std::is_integral_v) { + return JS_NewInt32(ctx, static_cast(instance->*Member)); + } else if constexpr (std::is_floating_point_v) { + return JS_NewFloat64(ctx, static_cast(instance->*Member)); + } + + return JS_UNDEFINED; + } + + template + JSValue js_generic_setter(JSContext* ctx, JSValueConst this_val, JSValueConst val) { + auto* instance = get_opaque(ctx, this_val, *ClassID); + if (!instance) return JS_EXCEPTION; + + if constexpr (std::is_integral_v) { + int32_t v = 0; + if (JS_ToInt32(ctx, &v, val) != 0) return JS_EXCEPTION; + instance->*Member = static_cast(v); + } else if constexpr (std::is_floating_point_v) { + double v = 0; + if (JS_ToFloat64(ctx, &v, val) != 0) return JS_EXCEPTION; + instance->*Member = static_cast(v); + } + return JS_UNDEFINED; + } + + // --- 2. Exception-Safe Instance Creation --- + + template + JSValue create_js_instance(JSContext* ctx, JSValueConst new_target, JSClassID class_id, Args&&... args) { + ScopedJSValue proto(ctx, JS_GetPropertyStr(ctx, new_target, "prototype")); + if (JS_IsException(proto.get())) return JS_EXCEPTION; + + JSValue obj = JS_NewObjectProtoClass(ctx, proto.get(), class_id); + if (JS_IsException(obj)) return JS_EXCEPTION; + + auto instance = std::make_unique(std::forward(args)...); + JS_SetOpaque(obj, instance.release()); + return obj; + } + + template + JSValue create_class_instance(JSContext* ctx, JSClassID class_id, Args&&... args) { + ScopedJSValue proto(ctx, JS_GetClassProto(ctx, class_id)); + if (JS_IsException(proto.get())) return JS_EXCEPTION; + + JSValue obj = JS_NewObjectProtoClass(ctx, proto.get(), class_id); + if (JS_IsException(obj)) return JS_EXCEPTION; + + auto instance = std::make_unique(std::forward(args)...); + JS_SetOpaque(obj, instance.release()); + return obj; + } + + // Helper forward declaration / overload for JSColor initialization + inline JSValue create_js_color_instance(JSContext* ctx, const ::Color color) { + return create_class_instance(ctx, js_color_class_id, color); + } + + // --- Property & Object Setter Helpers --- + + template + void set_object_property(JSContext* ctx, JSValueConst obj, const char* name, const T& val) { + constexpr int flags = JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE; + + if constexpr (std::is_integral_v) { + JS_DefinePropertyValueStr(ctx, obj, name, JS_NewInt32(ctx, static_cast(val)), flags); + } else if constexpr (std::is_floating_point_v) { + JS_DefinePropertyValueStr(ctx, obj, name, JS_NewFloat64(ctx, static_cast(val)), flags); + } else if constexpr (std::is_convertible_v || std::is_same_v) { + JS_DefinePropertyValueStr(ctx, obj, name, JS_NewString(ctx, std::string(val).c_str()), flags); + } else if constexpr (std::is_same_v) { + JS_DefinePropertyValueStr(ctx, obj, name, create_js_color_instance(ctx, val), flags); + } + } + + template + void set_object_properties(JSContext* ctx, JSValueConst obj, Args&&... entries) { + (set_object_property(ctx, obj, entries.first, entries.second), ...); + } + + // --- Class Registration Helper --- + + struct ClassDefConfig { + std::string_view name; + JSClassID& class_id; + JSClassFinalizer* finalizer = nullptr; + JSCFunction* constructor = nullptr; + std::span proto_funcs{}; + }; + + inline void register_js_class(JSContext* ctx, JSModuleDef* m, const ClassDefConfig& config) { + JSRuntime* const rt = JS_GetRuntime(ctx); + + if (config.class_id == 0) { + JS_NewClassID(rt, &config.class_id); + } + + const JSClassDef class_def{ + .class_name = config.name.data(), + .finalizer = config.finalizer + }; + JS_NewClass(rt, config.class_id, &class_def); + + const ScopedJSValue proto(ctx, JS_NewObject(ctx)); + + if (!config.proto_funcs.empty()) { + JS_SetPropertyFunctionList( + ctx, + proto.get(), + config.proto_funcs.data(), + static_cast(config.proto_funcs.size()) + ); + } + JS_SetClassProto(ctx, config.class_id, proto.get()); + + const JSValue ctor = JS_NewCFunction2(ctx, config.constructor, config.name.data(), 0, JS_CFUNC_constructor, 0); + JS_SetConstructor(ctx, ctor, proto.get()); + + JS_SetModuleExport(ctx, m, config.name.data(), ctor); + } + +} // namespace HostApi::Utils \ No newline at end of file diff --git a/src/config.hpp b/src/config.hpp new file mode 100644 index 0000000..8086c35 --- /dev/null +++ b/src/config.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace config::features { + + // Graphics + constexpr bool enable_2d = true; + constexpr bool enable_3d = true; + + // Extras + constexpr bool enable_fonts = true; + constexpr bool enable_audio = true; + + // Input + constexpr bool enable_keyboard = true; + constexpr bool enable_mouse = true; + constexpr bool enable_joypad = true; + +} \ No newline at end of file diff --git a/src/core.cpp b/src/core.cpp new file mode 100644 index 0000000..a294076 --- /dev/null +++ b/src/core.cpp @@ -0,0 +1,54 @@ +#include +#include "core.hpp" + +#include "api/hostapi.hpp" +#include "js_engine.hpp" + +namespace VectorJS { + + Core::Core() { + HostApi::make_vectorjs_module(js_engine.get_context()); + } + + void Core::eval_script(const std::string& scriptPath) const { + js_engine.eval_file(scriptPath); + } + + constexpr int DEFAULT_FPS = 60; + constexpr int WIN_HEIGHT = 600; + constexpr int WIN_WIDTH = 800; + + void show_welcome() { + InitWindow(WIN_WIDTH, WIN_HEIGHT, "VectorJS"); + SetTargetFPS(DEFAULT_FPS); + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(RAYWHITE); + DrawText("VectorJS", 40, 40, 80, DARKBLUE); + DrawText("Welcome to the VectorJS!", 40, 140, 20, DARKGRAY); + DrawText(std::format("QuickJS Version: {}", HostApi::QUICKJS_VERSION_STR).c_str(), 40, GetScreenHeight() - 80, 20, LIGHTGRAY); + DrawText(std::format("Raylib Version: {}", HostApi::RAYLIB_VERSION_STR).c_str(), 40, GetScreenHeight() - 60, 20, LIGHTGRAY); + DrawText("Press ESC to exit.", 40, GetScreenHeight() - 40, 20, LIGHTGRAY); + EndDrawing(); + } + CloseWindow(); + } + + void show_bsod(const std::string &errStr) { + if (!IsWindowReady()) { + InitWindow(WIN_WIDTH, WIN_HEIGHT, "VectorJS - Fatal Error"); + SetTargetFPS(DEFAULT_FPS); + } + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(BLUE); + DrawText(":(", 40, 40, 80, WHITE); + DrawText("Your VectorJS script ran into a problem and crashed.", 40, 140, 20, WHITE); + DrawText(errStr.c_str(), 40, 190, 20, LIGHTGRAY); + DrawText("Press ESC to exit.", 40, GetScreenHeight() - 40, 20, LIGHTGRAY); + EndDrawing(); + } + CloseWindow(); + } + +} \ No newline at end of file diff --git a/src/core.hpp b/src/core.hpp new file mode 100644 index 0000000..b9242f3 --- /dev/null +++ b/src/core.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "js_engine.hpp" + +namespace VectorJS { + + class Core { + public: + // Lifecycle + explicit Core(); + + ~Core() = default; + + // Execution + void eval_script(const std::string& scriptPath) const; + + private: + JSEngine js_engine; + + }; + + void show_welcome(); + void show_bsod(const std::string &errStr); + +} diff --git a/src/js_engine.cpp b/src/js_engine.cpp new file mode 100644 index 0000000..166afa8 --- /dev/null +++ b/src/js_engine.cpp @@ -0,0 +1,104 @@ +#include "js_engine.hpp" +#include +#include +#include +#include + +namespace VectorJS { + void JSEngine::check_and_throw_exception(JSValue value) const { + if (!JS_IsException(value)) { + return; + } + + const JSValue exception_val = JS_GetException(ctx); + std::string error_msg = "JavaScript Exception"; + + const char* msg = JS_ToCString(ctx, exception_val); + if (msg) { + error_msg = msg; + JS_FreeCString(ctx, msg); + } + + // Attempt to extract stack trace + JSValue stack_val = JS_GetPropertyStr(ctx, exception_val, "stack"); + if (!JS_IsUndefined(stack_val)) { + const char* stack_str = JS_ToCString(ctx, stack_val); + if (stack_str) { + error_msg += "\nStack trace:\n"; + error_msg += stack_str; + JS_FreeCString(ctx, stack_str); + } + } + + JS_FreeValue(ctx, stack_val); + JS_FreeValue(ctx, exception_val); + JS_FreeValue(ctx, value); + + throw std::runtime_error(error_msg); + } + + std::string JSEngine::read_file_contents(const std::string& filepath) { + std::ifstream file(filepath, std::ios::in | std::ios::binary); + if (!file) { + throw std::runtime_error("Failed to open file: " + filepath); + } + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); + } + + JSEngine::JSEngine() { + rt = JS_NewRuntime(); + if (!rt) throw std::runtime_error("Failed to create JSRuntime"); + + ctx = JS_NewContext(rt); + if (!ctx) { + JS_FreeRuntime(rt); + throw std::runtime_error("Failed to create JSContext"); + } + } + + JSEngine::~JSEngine() { + if (ctx) JS_FreeContext(ctx); + if (rt) JS_FreeRuntime(rt); + } + + JSEngine::JSEngine(JSEngine&& other) noexcept : rt(other.rt), ctx(other.ctx) { + other.rt = nullptr; + other.ctx = nullptr; + } + + JSEngine& JSEngine::operator=(JSEngine&& other) noexcept { + if (this != &other) { + if (ctx) JS_FreeContext(ctx); + if (rt) JS_FreeRuntime(rt); + + rt = other.rt; + ctx = other.ctx; + other.rt = nullptr; + other.ctx = nullptr; + } + return *this; + } + + void JSEngine::eval(std::string_view code, std::string_view filename) const { + JSValue result = JS_Eval(ctx, code.data(), code.length(), filename.data(), JS_EVAL_TYPE_MODULE); + check_and_throw_exception(result); + JS_FreeValue(ctx, result); + } + + JSValue JSEngine::eval_value(std::string_view code, std::string_view filename) const { + JSValue result = JS_Eval(ctx, code.data(), code.length(), filename.data(), JS_EVAL_TYPE_MODULE); + check_and_throw_exception(result); + return result; + } + + void JSEngine::eval_file(const std::string& filepath, int eval_flags) const { + std::string code = read_file_contents(filepath); + JSValue result = JS_Eval(ctx, code.c_str(), code.length(), filepath.c_str(), eval_flags); + + // Unified exception handling with stack trace support + check_and_throw_exception(result); + JS_FreeValue(ctx, result); + } +} \ No newline at end of file diff --git a/src/js_engine.hpp b/src/js_engine.hpp new file mode 100644 index 0000000..b80b566 --- /dev/null +++ b/src/js_engine.hpp @@ -0,0 +1,38 @@ +#pragma once + +extern "C" { +#include +} +#include +#include + +namespace VectorJS { + class JSEngine { + JSRuntime* rt{nullptr}; + JSContext* ctx{nullptr}; + + void check_and_throw_exception(JSValue value) const; + static std::string read_file_contents(const std::string& filepath); + + public: + JSEngine(); + ~JSEngine(); + + // Disable copies + JSEngine(const JSEngine&) = delete; + JSEngine& operator=(const JSEngine&) = delete; + + // Enable moves + JSEngine(JSEngine&& other) noexcept; + JSEngine& operator=(JSEngine&& other) noexcept; + + [[nodiscard]] JSContext* get_context() const { return ctx; } + [[nodiscard]] JSRuntime* get_runtime() const { return rt; } + + void eval(std::string_view code, std::string_view filename = "input.js") const; + + [[nodiscard]] JSValue eval_value(std::string_view code, std::string_view filename = "input.js") const; + + void eval_file(const std::string& filepath, int eval_flags = JS_EVAL_TYPE_MODULE) const; + }; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..dd16094 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,35 @@ +#include +#include "core.hpp" + +int main(const int argc, char** argv) { + + CLI::App cli{"VectorJS"}; + + // Run (argument) + std::string scriptInput; + cli.add_option("script", scriptInput, "Path to the JS game script"); + + try { + std::cout << scriptInput << std::endl; + cli.parse(argc, argv); + } catch (const CLI::ParseError &e) { + std::cerr << e.what() << std::endl; + return cli.exit(e); + } + + if (scriptInput.empty()) { + VectorJS::show_welcome(); + return 0; + } + + try { + VectorJS::Core app{}; + app.eval_script(scriptInput); + } catch (const std::exception& e) { + std::cerr << "Fatal Error: " << e.what() << std::endl; + VectorJS::show_bsod(e.what()); + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/src/vectorjs.d.ts b/src/vectorjs.d.ts new file mode 100644 index 0000000..715adc4 --- /dev/null +++ b/src/vectorjs.d.ts @@ -0,0 +1,149 @@ +declare module "vectorjs" { + + /** + * Structure representing an RGBA color. + */ + export class Color { + r: number; + g: number; + b: number; + a: number; + constructor(); + constructor(r: number, g: number, b: number, a: number); + } + + /** + * Structure representing a 2D vector. + */ + export class Vector2 { + x: number; + y: number; + constructor(); + constructor(x: number, y: number); + } + + /** + * Structure representing a 2D rectangle. + */ + export class Rectangle { + x: number; + y: number; + width: number; + height: number; + constructor(); + constructor(x: number, y: number, width: number, height: number); + } + + /** + * Custom font resource loaded from a path. + */ + export class Font { + constructor(path: string); + constructor(path: string, baseSize: number); + } + + /** + * Built-in color palette options matching Raylib defaults. + */ + export const Palette: { + readonly LIGHTGRAY: Color; + readonly GRAY: Color; + readonly DARKGRAY: Color; + readonly YELLOW: Color; + readonly GOLD: Color; + readonly ORANGE: Color; + readonly PINK: Color; + readonly RED: Color; + readonly MAROON: Color; + readonly GREEN: Color; + readonly LIME: Color; + readonly DARKGREEN: Color; + readonly SKYBLUE: Color; + readonly BLUE: Color; + readonly DARKBLUE: Color; + readonly PURPLE: Color; + readonly VIOLET: Color; + readonly DARKPURPLE: Color; + readonly BEIGE: Color; + readonly BROWN: Color; + readonly DARKBROWN: Color; + readonly WHITE: Color; + readonly BLACK: Color; + readonly BLANK: Color; + readonly MAGENTA: Color; + readonly RAYWHITE: Color; + }; + + /** + * Options passed as a raw JS object to shape drawing methods (like drawRectangle). + */ + export interface DrawOptions { + readonly color?: Color; + readonly rotation?: number; + readonly wireframe?: boolean; + readonly origin?: Vector2; + } + + /** + * Custom options configuring text rendering layout and styling. + */ + export interface TextOptions { + readonly font?: Font; + readonly color?: Color; + readonly rotation?: number; + readonly fontSize?: number; + readonly spacing?: number; + readonly origin?: Vector2; + } + + /** + * Exposes individual primitive 2D structural rendering mechanisms. + */ + export interface Render2DShapes { + drawPixel(position: Vector2, options: DrawOptions): void; + drawLine(startPosition: Vector2, endPosition: Vector2, options: DrawOptions): void; + drawRectangle(rect: Rectangle, options: DrawOptions): void; + drawCircle(centre: Vector2, radius: number, options: DrawOptions): void; + drawTriangle(point1: Vector2, point2: Vector2, point3: Vector2, options: DrawOptions): void; + drawEllipse(center: Vector2, radiusH: number, radiusV: number, options: DrawOptions): void; + } + + export interface Render2DText { + drawText(position: Vector2, text: string, options?: TextOptions): void; + } + + /** + * Context parameter exposed to the `onDraw` hook loop. + */ + export interface RenderContext { + drawFPS(position: Vector2): void; + readonly shapes: Render2DShapes; + readonly text: Render2DText; + withLayer2D(callback: (layer: RenderContext) => void): void; + } + + /** + * Context parameter exposed to the `onUpdate` hook loop. + */ + export interface UpdateContext { + // Currently instantated as empty object context inside create_update_context_object + } + + /** + * Hook listener declarations to tie context events explicitly to runtime loops. + */ + export interface UserApplication { + onInit?(this: UserApplication): void; + onUpdate?(this: UserApplication, ctx: UpdateContext): void; + onDraw?(this: UserApplication, render: RenderContext): void; + } + + /** + * Core Application wrapper executing the rendering pipeline. + */ + export class Application { + constructor(width: number, height: number, title: string); + run(userApp: UserApplication): void; + } + +} \ No newline at end of file From 2c618fcffba9ae1b53b174f2911d2e27dfc29dc4 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:40:20 +0100 Subject: [PATCH 02/24] wip --- src/api/api_classes.cpp | 70 --------- src/api/api_context.cpp | 31 ---- src/api/js_types.hpp | 321 ---------------------------------------- src/config.hpp | 18 --- 4 files changed, 440 deletions(-) delete mode 100644 src/config.hpp diff --git a/src/api/api_classes.cpp b/src/api/api_classes.cpp index 49d5595..6e234a3 100644 --- a/src/api/api_classes.cpp +++ b/src/api/api_classes.cpp @@ -2,7 +2,6 @@ #include "js_types.hpp" #include "js_utils.hpp" #include "js_context.hpp" -#include "../config.hpp" #define JS_BIND_PROP(ClassType, ClassIDPtr, name, Member) \ JS_CGETSET_DEF( \ @@ -182,80 +181,11 @@ namespace HostApi { }); } - static void register_font_class(JSContext* ctx, JSModuleDef* m) { - Utils::register_js_class(ctx, m, { - .name = "Font", - .class_id = js_font_class_id, - .finalizer = [](auto, JSValue val) { - delete Utils::get_opaque(val, js_font_class_id); - }, - .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { - if (argc < 1) return JS_ThrowTypeError(c, "Font requires at least a path argument"); - - auto font_path = Utils::js_to_std_string(c, argv[0]); - if (font_path.empty()) return JS_EXCEPTION; - - if (argc >= 2) { - int32_t size = 0; - JS_ToInt32(c, &size, argv[1]); - return Utils::create_js_instance(c, new_target, js_font_class_id, font_path, size); - } - - return Utils::create_js_instance(c, new_target, js_font_class_id, font_path); - } - }); - } - - static void register_image_class(JSContext* ctx, JSModuleDef* m) { - static constexpr JSCFunctionListEntry proto_funcs[] = { - JS_CGETSET_DEF( - "width", - ([](JSContext* c, JSValueConst this_val) -> JSValue { - const auto* img = Utils::get_opaque(this_val, js_image_class_id); - return img ? JS_NewInt32(c, img->get_width()) : JS_UNDEFINED; - }), - nullptr - ), - JS_CGETSET_DEF( - "height", - ([](JSContext* c, JSValueConst this_val) -> JSValue { - const auto* img = Utils::get_opaque(this_val, js_image_class_id); - return img ? JS_NewInt32(c, img->get_height()) : JS_UNDEFINED; - }), - nullptr - ) - }; - - Utils::register_js_class(ctx, m, { - .name = "Image", - .class_id = js_image_class_id, - .finalizer = [](auto, JSValue val) { - delete Utils::get_opaque(val, js_image_class_id); - }, - .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { - if (argc < 1) return JS_ThrowTypeError(c, "Image constructor requires a file path argument"); - - auto image_path = Utils::js_to_std_string(c, argv[0]); - if (image_path.empty()) return JS_EXCEPTION; - - return Utils::create_js_instance(c, new_target, js_image_class_id, image_path); - }, - .proto_funcs = proto_funcs - }); - } - void register_hapi_classes(JSContext* ctx, JSModuleDef* m) { - register_application_class(ctx, m); - register_color_class(ctx, m); register_vector2_class(ctx, m); register_rectangle_class(ctx, m); - register_image_class(ctx, m); - - if constexpr (config::features::enable_fonts) { - register_font_class(ctx, m); - } } } // namespace HostApi diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 96de5ca..dda2b57 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -13,19 +13,6 @@ namespace HostApi { return options; } - static JSTextOptions parse_text_options(JSContext* ctx, JSValueConst optionsObj) { - JSTextOptions options; - if (JS_IsObject(optionsObj)) { - Utils::try_get_opaque_property(ctx, optionsObj, "font", js_font_class_id, options.font); - Utils::try_get_opaque_property(ctx, optionsObj, "color", js_color_class_id, options.color); - Utils::try_get_float_property(ctx, optionsObj, "rotation", options.rotation); - Utils::try_get_float_property(ctx, optionsObj, "fontSize", options.fontSize); - Utils::try_get_float_property(ctx, optionsObj, "spacing", options.spacing); - Utils::try_get_opaque_property(ctx, optionsObj, "origin", js_vector2_class_id, options.origin); - } - return options; - } - JSValue create_update_context_object(JSContext* ctx) { return JS_NewObject(ctx); } @@ -105,24 +92,6 @@ namespace HostApi { JS_SetPropertyStr(ctx, render2d_obj, "shapes", shape_obj.release()); - // --- Text Sub-Object --- - Utils::ScopedJSValue text_obj(ctx, JS_NewObject(ctx)); - JS_SetPropertyStr(ctx, text_obj, "drawText", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { - if (argc < 2) return JS_ThrowTypeError(c, "drawText requires position and text string arguments"); - const auto pos = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); - - const std::string txt_str = Utils::js_to_std_string(c, argv[1]); - JSTextOptions options = parse_text_options(c, argc > 2 ? argv[2] : JS_UNDEFINED); - Font fontToUse = GetFontDefault(); - if (options.font.font_ptr && options.font.font_ptr->texture.id != 0) { - fontToUse = *options.font.font_ptr; - } - ::DrawTextPro(fontToUse, txt_str.c_str(), pos, options.origin, options.rotation, options.fontSize, options.spacing, options.color); - return JS_UNDEFINED; - }, "drawText", 3)); - - JS_SetPropertyStr(ctx, render2d_obj, "text", text_obj.release()); - // --- Layer Wrappers --- const JSValue render_obj = JS_NewObject(ctx); JSValue r2d_val = render2d_obj.release(); diff --git a/src/api/js_types.hpp b/src/api/js_types.hpp index 6763cba..fd1d3b9 100644 --- a/src/api/js_types.hpp +++ b/src/api/js_types.hpp @@ -11,8 +11,6 @@ namespace HostApi { inline JSClassID js_color_class_id; inline JSClassID js_vector2_class_id; inline JSClassID js_rectangle_class_id; - inline JSClassID js_font_class_id; - inline JSClassID js_image_class_id; inline JSClassID js_application_class_id; struct JSApplication { @@ -40,48 +38,6 @@ namespace HostApi { [[nodiscard]] constexpr operator Vector2() const { return Vector2 { x, y }; } }; - struct JSVector3 { - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - JSVector3() = default; - JSVector3(const float x, const float y, const float z = 0.0f) : x(x), y(y), z(z) {} - explicit constexpr JSVector3(const Vector3 v) : x(v.x), y(v.y), z(v.z) {} - [[nodiscard]] constexpr operator Vector3() const { return Vector3 { x, y, z }; } - }; - - struct JSVector4 { - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - float w = 0.0f; - JSVector4() = default; - JSVector4(const float x, const float y, const float z, const float w) : x(x), y(y), z(z), w(w) {} - explicit constexpr JSVector4(const Vector4 v) : x(v.x), y(v.y), z(v.z), w(v.w) {} - [[nodiscard]] constexpr operator Vector4() const { return Vector4 { x, y, z, w }; } - }; - - struct JSMatrix { - float m0 = 1.0f, m4 = 0.0f, m8 = 0.0f, m12 = 0.0f; - float m1 = 0.0f, m5 = 1.0f, m9 = 0.0f, m13 = 0.0f; - float m2 = 0.0f, m6 = 0.0f, m10 = 1.0f, m14 = 0.0f; - float m3 = 0.0f, m7 = 0.0f, m11 = 0.0f, m15 = 1.0f; - JSMatrix() = default; - explicit constexpr JSMatrix(const Matrix m) : - m0(m.m0), m4(m.m4), m8(m.m8), m12(m.m12), - m1(m.m1), m5(m.m5), m9(m.m9), m13(m.m13), - m2(m.m2), m6(m.m6), m10(m.m10), m14(m.m14), - m3(m.m3), m7(m.m7), m11(m.m11), m15(m.m15) {} - [[nodiscard]] constexpr operator Matrix() const { - return Matrix { - m0, m4, m8, m12, - m1, m5, m9, m13, - m2, m6, m10, m14, - m3, m7, m11, m15 - }; - } - }; - struct JSRectangle { float x = 0.0f; float y = 0.0f; @@ -93,274 +49,6 @@ namespace HostApi { [[nodiscard]] constexpr operator Rectangle() const { return Rectangle { x, y, width, height }; } }; - struct JSCamera2D { - JSVector2 offset; - JSVector2 target; - float rotation = 0.0f; - float zoom = 1.0f; - JSCamera2D() = default; - JSCamera2D(const JSVector2& offset, const JSVector2& target, float rotation = 0.0f, float zoom = 1.0f) - : offset(offset), target(target), rotation(rotation), zoom(zoom) {} - explicit constexpr JSCamera2D(const Camera2D c) - : offset(c.offset), target(c.target), rotation(c.rotation), zoom(c.zoom) {} - [[nodiscard]] constexpr operator Camera2D() const { return Camera2D { offset, target, rotation, zoom }; } - }; - - struct JSCamera3D { - JSVector3 position; - JSVector3 target; - JSVector3 up{ 0.0f, 1.0f, 0.0f }; - float fovy = 45.0f; - int projection = CAMERA_PERSPECTIVE; - JSCamera3D() = default; - JSCamera3D(const JSVector3& pos, const JSVector3& target, const JSVector3& up = {0.0f, 1.0f, 0.0f}, float fovy = 45.0f, int proj = CAMERA_PERSPECTIVE) - : position(pos), target(target), up(up), fovy(fovy), projection(proj) {} - explicit constexpr JSCamera3D(const Camera3D c) - : position(c.position), target(c.target), up(c.up), fovy(c.fovy), projection(c.projection) {} - [[nodiscard]] constexpr operator Camera3D() const { return Camera3D { position, target, up, fovy, projection }; } - }; - - struct JSRay { - JSVector3 position; - JSVector3 direction; - JSRay() = default; - JSRay(const JSVector3& pos, const JSVector3& dir) : position(pos), direction(dir) {} - explicit constexpr JSRay(const Ray r) : position(r.position), direction(r.direction) {} - [[nodiscard]] constexpr operator Ray() const { return Ray { position, direction }; } - }; - - struct JSRayCollision { - bool hit = false; - float distance = 0.0f; - JSVector3 point; - JSVector3 normal; - JSRayCollision() = default; - explicit constexpr JSRayCollision(const RayCollision rc) - : hit(rc.hit), distance(rc.distance), point(rc.point), normal(rc.normal) {} - [[nodiscard]] constexpr operator RayCollision() const { return RayCollision { hit, distance, point, normal }; } - }; - - struct JSBoundingBox { - JSVector3 min; - JSVector3 max; - JSBoundingBox() = default; - JSBoundingBox(const JSVector3& min, const JSVector3& max) : min(min), max(max) {} - explicit constexpr JSBoundingBox(const BoundingBox bb) : min(bb.min), max(bb.max) {} - [[nodiscard]] constexpr operator BoundingBox() const { return BoundingBox { min, max }; } - }; - - struct JSNPatchInfo { - JSRectangle source; - int left = 0; - int top = 0; - int right = 0; - int bottom = 0; - int layout = NPATCH_NINE_PATCH; - JSNPatchInfo() = default; - explicit constexpr JSNPatchInfo(const NPatchInfo np) - : source(np.source), left(np.left), top(np.top), right(np.right), bottom(np.bottom), layout(np.layout) {} - [[nodiscard]] constexpr operator NPatchInfo() const { return NPatchInfo { source, left, top, right, bottom, layout }; } - }; - - struct JSGlyphInfo { - int value = 0; - int offsetX = 0; - int offsetY = 0; - int advanceX = 0; - Image image{}; - JSGlyphInfo() = default; - explicit JSGlyphInfo(const GlyphInfo g) - : value(g.value), offsetX(g.offsetX), offsetY(g.offsetY), advanceX(g.advanceX), image(g.image) {} - [[nodiscard]] operator GlyphInfo() const { return GlyphInfo { value, offsetX, offsetY, advanceX, image }; } - }; - - struct JSTransform { - JSVector3 translation; - JSVector4 rotation{0, 0, 0, 1}; - JSVector3 scale{1, 1, 1}; - JSTransform() = default; - explicit constexpr JSTransform(const Transform t) - : translation(t.translation), rotation(t.rotation), scale(t.scale) {} - [[nodiscard]] constexpr operator Transform() const { return Transform { translation, rotation, scale }; } - }; - - struct JSImage { - std::shared_ptr texture_ptr; - - JSImage() = default; - - explicit JSImage(const std::string& path) { - Texture2D tex = LoadTexture(path.c_str()); - texture_ptr = std::shared_ptr(new Texture2D(tex), [](const Texture2D* pt) { - if (pt->id != 0) { - UnloadTexture(*pt); - } - delete pt; - }); - } - - [[nodiscard]] Texture2D get_texture() const { - return texture_ptr ? *texture_ptr : Texture2D{ 0 }; - } - - [[nodiscard]] int get_width() const { return texture_ptr ? texture_ptr->width : 0; } - [[nodiscard]] int get_height() const { return texture_ptr ? texture_ptr->height : 0; } - }; - - struct JSTexture { - std::shared_ptr texture_ptr; - JSTexture() = default; - explicit JSTexture(const std::string& path) { - Texture2D tex = LoadTexture(path.c_str()); - texture_ptr = std::shared_ptr(new Texture2D(tex), [](const Texture2D* pt) { - if (pt->id != 0) { - UnloadTexture(*pt); - } - delete pt; - }); - } - }; - - struct JSRenderTexture { - std::shared_ptr target_ptr; - JSRenderTexture() = default; - JSRenderTexture(int width, int height) { - RenderTexture2D target = LoadRenderTexture(width, height); - target_ptr = std::shared_ptr(new RenderTexture2D(target), [](const RenderTexture2D* prt) { - if (prt->id != 0) { - UnloadRenderTexture(*prt); - } - delete prt; - }); - } - }; - - struct JSFont { - std::shared_ptr font_ptr; - JSFont() = default; - explicit JSFont(const std::string& path, const int baseSize = 64) { - const Font f = LoadFontEx(path.c_str(), baseSize, nullptr, 0); - if (f.texture.id != 0) { - SetTextureFilter(f.texture, TEXTURE_FILTER_BILINEAR); - } - font_ptr = std::shared_ptr(new Font(f), [](const Font* pf) { - if (pf->texture.id != 0) { - UnloadFont(*pf); - } - delete pf; - }); - } - }; - - struct JSMesh { - std::shared_ptr mesh_ptr; - JSMesh() = default; - explicit JSMesh(Mesh mesh) { - mesh_ptr = std::shared_ptr(new Mesh(mesh), [](const Mesh* pm) { - UnloadMesh(*pm); - delete pm; - }); - } - }; - - struct JSShader { - std::shared_ptr shader_ptr; - JSShader() = default; - JSShader(const std::string& vsPath, const std::string& fsPath) { - Shader s = LoadShader(vsPath.empty() ? nullptr : vsPath.c_str(), - fsPath.empty() ? nullptr : fsPath.c_str()); - shader_ptr = std::shared_ptr(new Shader(s), [](const Shader* ps) { - if (ps->id != 0) { - UnloadShader(*ps); - } - delete ps; - }); - } - }; - - struct JSMaterial { - std::shared_ptr material_ptr; - JSMaterial() = default; - explicit JSMaterial(Material mat) { - material_ptr = std::shared_ptr(new Material(mat), [](const Material* pm) { - UnloadMaterial(*pm); - delete pm; - }); - } - }; - - struct JSModel { - std::shared_ptr model_ptr; - JSModel() = default; - explicit JSModel(const std::string& path) { - Model m = LoadModel(path.c_str()); - model_ptr = std::shared_ptr(new Model(m), [](const Model* pm) { - UnloadModel(*pm); - delete pm; - }); - } - }; - - struct JSWave { - std::shared_ptr wave_ptr; - JSWave() = default; - explicit JSWave(const std::string& path) { - Wave w = LoadWave(path.c_str()); - wave_ptr = std::shared_ptr(new Wave(w), [](const Wave* pw) { - UnloadWave(*pw); - delete pw; - }); - } - }; - - struct JSSound { - std::shared_ptr sound_ptr; - JSSound() = default; - explicit JSSound(const std::string& path) { - Sound s = LoadSound(path.c_str()); - sound_ptr = std::shared_ptr(new Sound(s), [](const Sound* ps) { - UnloadSound(*ps); - delete ps; - }); - } - }; - - struct JSMusic { - std::shared_ptr music_ptr; - JSMusic() = default; - explicit JSMusic(const std::string& path) { - Music m = LoadMusicStream(path.c_str()); - music_ptr = std::shared_ptr(new Music(m), [](const Music* pm) { - UnloadMusicStream(*pm); - delete pm; - }); - } - }; - - struct JSAudioStream { - std::shared_ptr stream_ptr; - JSAudioStream() = default; - JSAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) { - AudioStream stream = LoadAudioStream(sampleRate, sampleSize, channels); - stream_ptr = std::shared_ptr(new AudioStream(stream), [](const AudioStream* pas) { - UnloadAudioStream(*pas); - delete pas; - }); - } - }; - - struct JSFilePathList { - std::shared_ptr files_ptr; - JSFilePathList() = default; - explicit JSFilePathList(const std::string& dirPath) { - FilePathList f = LoadDirectoryFiles(dirPath.c_str()); - files_ptr = std::shared_ptr(new FilePathList(f), [](const FilePathList* pf) { - UnloadDirectoryFiles(*pf); - delete pf; - }); - } - }; - struct JSDrawOptions { JSColor color = JSColor(BLACK); float rotation = 0.0f; @@ -368,13 +56,4 @@ namespace HostApi { JSVector2 origin = JSVector2(0, 0); }; - struct JSTextOptions { - JSFont font; - JSColor color = JSColor(BLACK); - float rotation = 0.0f; - float fontSize = 24.0f; - float spacing = 1.0f; - JSVector2 origin = JSVector2(0, 0); - }; - } \ No newline at end of file diff --git a/src/config.hpp b/src/config.hpp deleted file mode 100644 index 8086c35..0000000 --- a/src/config.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -namespace config::features { - - // Graphics - constexpr bool enable_2d = true; - constexpr bool enable_3d = true; - - // Extras - constexpr bool enable_fonts = true; - constexpr bool enable_audio = true; - - // Input - constexpr bool enable_keyboard = true; - constexpr bool enable_mouse = true; - constexpr bool enable_joypad = true; - -} \ No newline at end of file From ab7ae1962e9ef3974b0de39b02913b2f9eff1d25 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:44:16 +0100 Subject: [PATCH 03/24] update d file --- src/vectorjs.d.ts | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/vectorjs.d.ts b/src/vectorjs.d.ts index 715adc4..8e21f9a 100644 --- a/src/vectorjs.d.ts +++ b/src/vectorjs.d.ts @@ -84,18 +84,6 @@ declare module "vectorjs" { readonly origin?: Vector2; } - /** - * Custom options configuring text rendering layout and styling. - */ - export interface TextOptions { - readonly font?: Font; - readonly color?: Color; - readonly rotation?: number; - readonly fontSize?: number; - readonly spacing?: number; - readonly origin?: Vector2; - } - /** * Exposes individual primitive 2D structural rendering mechanisms. */ @@ -108,17 +96,12 @@ declare module "vectorjs" { drawEllipse(center: Vector2, radiusH: number, radiusV: number, options: DrawOptions): void; } - export interface Render2DText { - drawText(position: Vector2, text: string, options?: TextOptions): void; - } - /** * Context parameter exposed to the `onDraw` hook loop. */ export interface RenderContext { drawFPS(position: Vector2): void; readonly shapes: Render2DShapes; - readonly text: Render2DText; withLayer2D(callback: (layer: RenderContext) => void): void; } @@ -142,7 +125,7 @@ declare module "vectorjs" { * Core Application wrapper executing the rendering pipeline. */ export class Application { - constructor(width: number, height: number, title: string); + constructor(height: number, width: number, title: string); run(userApp: UserApplication): void; } From c7989fcb1fb5e73fef8356795c1a21be357b472f Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:45:37 +0100 Subject: [PATCH 04/24] chore: update pull request trigger types in verify.yml --- .github/workflows/verify.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 609480c..dd67487 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -8,6 +8,7 @@ on: branches: [ "main", "master" ] pull_request: branches: [ "main", "master" ] + types: [opened, synchronize, reopened] jobs: verify-ubuntu: From d994de13b906c6c1cce314329313548a1b9fee4c Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:49:50 +0100 Subject: [PATCH 05/24] chore: remove debug output for script input in main.cpp --- src/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index dd16094..25a2a90 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,6 @@ int main(const int argc, char** argv) { cli.add_option("script", scriptInput, "Path to the JS game script"); try { - std::cout << scriptInput << std::endl; cli.parse(argc, argv); } catch (const CLI::ParseError &e) { std::cerr << e.what() << std::endl; From 9d0174f361d2822b9cd1d830ba3bacd60e0ee5b8 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:52:50 +0100 Subject: [PATCH 06/24] include header in core.hpp --- src/core.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core.hpp b/src/core.hpp index b9242f3..b15f034 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include "js_engine.hpp" namespace VectorJS { From b07d9ad782076865a7d7f149583e848c1e60c157 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:53:32 +0100 Subject: [PATCH 07/24] remove unused headers and include quickjs.h in js_types.hpp --- src/api/js_types.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/api/js_types.hpp b/src/api/js_types.hpp index fd1d3b9..806f57c 100644 --- a/src/api/js_types.hpp +++ b/src/api/js_types.hpp @@ -1,10 +1,9 @@ #pragma once #include -#include -#include #include #include +#include "quickjs.h" namespace HostApi { From 3c41e3edf6606a118ec968db9f69245d81ce7a52 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:54:21 +0100 Subject: [PATCH 08/24] fix: change RAYLIB_VERSION_STR and QUICKJS_VERSION_STR to inline variables in hostapi.hpp --- src/api/hostapi.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/hostapi.hpp b/src/api/hostapi.hpp index 53a89bb..109de9b 100644 --- a/src/api/hostapi.hpp +++ b/src/api/hostapi.hpp @@ -4,8 +4,8 @@ #include namespace HostApi { - const std::string RAYLIB_VERSION_STR = std::format("{}.{}.{}", RAYLIB_VERSION_MAJOR, RAYLIB_VERSION_MINOR, RAYLIB_VERSION_PATCH); - const std::string QUICKJS_VERSION_STR = std::format("{}.{}.{}", QJS_VERSION_MAJOR, QJS_VERSION_MINOR, QJS_VERSION_PATCH); + inline const std::string RAYLIB_VERSION_STR = std::format("{}.{}.{}", RAYLIB_VERSION_MAJOR, RAYLIB_VERSION_MINOR, RAYLIB_VERSION_PATCH); + inline const std::string QUICKJS_VERSION_STR = std::format("{}.{}.{}", QJS_VERSION_MAJOR, QJS_VERSION_MINOR, QJS_VERSION_PATCH); void register_hapi_enums(JSContext* ctx, JSModuleDef* m); void register_hapi_classes(JSContext* ctx, JSModuleDef* m); From 0359935ee7377d50f6d40d64fadc52295e734f39 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:55:24 +0100 Subject: [PATCH 09/24] fix: change function declarations to non-inline in js_context.hpp --- src/api/js_context.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/js_context.hpp b/src/api/js_context.hpp index 1ccaa4d..1507a97 100644 --- a/src/api/js_context.hpp +++ b/src/api/js_context.hpp @@ -2,6 +2,6 @@ #include namespace HostApi { - inline JSValue create_update_context_object(JSContext* ctx); - inline JSValue create_draw_render_object(JSContext* ctx); + JSValue create_update_context_object(JSContext* ctx); + JSValue create_draw_render_object(JSContext* ctx); } \ No newline at end of file From 73489d4c6760e20e7f9f61694be4288d6a8ff26c Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:01:11 +0100 Subject: [PATCH 10/24] fix: add error handling for argument conversion in constructors --- src/api/api_classes.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/api/api_classes.cpp b/src/api/api_classes.cpp index 6e234a3..b3d2e5a 100644 --- a/src/api/api_classes.cpp +++ b/src/api/api_classes.cpp @@ -29,8 +29,8 @@ namespace HostApi { }, .constructor = [](auto c, auto new_target, const int argc, auto argv) -> JSValue { int32_t h = 0, w = 0; - if (argc > 0) JS_ToInt32(c, &h, argv[0]); - if (argc > 1) JS_ToInt32(c, &w, argv[1]); + if (argc > 0 && JS_ToInt32(c, &h, argv[0]) < 0) return JS_EXCEPTION; + if (argc > 1 && JS_ToInt32(c, &w, argv[1]) < 0) return JS_EXCEPTION; std::string title = argc > 2 ? Utils::js_to_std_string(c, argv[2]) : "VectorJS Application"; return Utils::create_js_instance(c, new_target, js_application_class_id, h, w, std::move(title)); }, @@ -107,10 +107,10 @@ namespace HostApi { .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { if (argc >= 4) { int32_t r = 0, g = 0, b = 0, a = 255; - JS_ToInt32(c, &r, argv[0]); - JS_ToInt32(c, &g, argv[1]); - JS_ToInt32(c, &b, argv[2]); - JS_ToInt32(c, &a, argv[3]); + if (argc > 0 && JS_ToInt32(c, &r, argv[0]) < 0) return JS_EXCEPTION; + if (argc > 1 && JS_ToInt32(c, &g, argv[1]) < 0) return JS_EXCEPTION; + if (argc > 2 && JS_ToInt32(c, &b, argv[2]) < 0) return JS_EXCEPTION; + if (argc > 2 && JS_ToInt32(c, &a, argv[2]) < 0) return JS_EXCEPTION; return Utils::create_js_instance( c, new_target, js_color_class_id, @@ -138,8 +138,8 @@ namespace HostApi { }, .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { int32_t x = 0, y = 0; - if (argc > 0) JS_ToInt32(c, &x, argv[0]); - if (argc > 1) JS_ToInt32(c, &y, argv[1]); + if (argc > 0 && JS_ToInt32(c, &x, argv[0]) < 0) return JS_EXCEPTION; + if (argc > 1 && JS_ToInt32(c, &y, argv[1]) < 0) return JS_EXCEPTION; return Utils::create_js_instance( c, new_target, js_vector2_class_id, @@ -166,10 +166,10 @@ namespace HostApi { }, .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { double x = 0, y = 0, w = 0, h = 0; - if (argc > 0) JS_ToFloat64(c, &x, argv[0]); - if (argc > 1) JS_ToFloat64(c, &y, argv[1]); - if (argc > 2) JS_ToFloat64(c, &w, argv[2]); - if (argc > 3) JS_ToFloat64(c, &h, argv[3]); + if (argc > 0 && JS_ToFloat64(c, &x, argv[0]) < 0) return JS_EXCEPTION; + if (argc > 1 && JS_ToFloat64(c, &y, argv[1]) < 0) return JS_EXCEPTION; + if (argc > 2 && JS_ToFloat64(c, &w, argv[2]) < 0) return JS_EXCEPTION; + if (argc > 3 && JS_ToFloat64(c, &h, argv[3]) < 0) return JS_EXCEPTION; return Utils::create_js_instance( c, new_target, js_rectangle_class_id, From aa8b69010a67be74e2f9cc68b28a3b99368fd5ab Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:02:45 +0100 Subject: [PATCH 11/24] fix: swap width and height parameters in JSApplication constructor --- src/api/api_classes.cpp | 2 +- src/api/js_types.hpp | 2 +- src/vectorjs.d.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/api_classes.cpp b/src/api/api_classes.cpp index b3d2e5a..35a1d6d 100644 --- a/src/api/api_classes.cpp +++ b/src/api/api_classes.cpp @@ -38,7 +38,7 @@ namespace HostApi { }); } - JSApplication::JSApplication(const int h, const int w, const std::string& title) { + JSApplication::JSApplication(const int w, const int h, const std::string& title) { InitWindow(w, h, title.c_str()); SetTargetFPS(60); } diff --git a/src/api/js_types.hpp b/src/api/js_types.hpp index 806f57c..eea77da 100644 --- a/src/api/js_types.hpp +++ b/src/api/js_types.hpp @@ -13,7 +13,7 @@ namespace HostApi { inline JSClassID js_application_class_id; struct JSApplication { - JSApplication(int h, int w, const std::string& title); + JSApplication(int w, int h, const std::string& title); static JSValue Run(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); }; diff --git a/src/vectorjs.d.ts b/src/vectorjs.d.ts index 8e21f9a..ce89dcf 100644 --- a/src/vectorjs.d.ts +++ b/src/vectorjs.d.ts @@ -125,7 +125,7 @@ declare module "vectorjs" { * Core Application wrapper executing the rendering pipeline. */ export class Application { - constructor(height: number, width: number, title: string); + constructor(width: number, height: number, title: string); run(userApp: UserApplication): void; } From 4f8dc8df6f3f4eae0e5bcaa31cfa10f66c92fadb Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:05:28 +0100 Subject: [PATCH 12/24] fix: add additional properties to JSDrawOptions in api_context.cpp --- src/api/api_context.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index dda2b57..f192d29 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -9,6 +9,9 @@ namespace HostApi { JSDrawOptions options; if (JS_IsObject(optionsObj)) { Utils::try_get_opaque_property(ctx, optionsObj, "color", js_color_class_id, options.color); + Utils::try_get_opaque_property(ctx, optionsObj, "origin", js_color_class_id, options.origin); + Utils::try_get_opaque_property(ctx, optionsObj, "rotation", js_color_class_id, options.rotation); + Utils::try_get_opaque_property(ctx, optionsObj, "wireframe", js_color_class_id, options.wireframe); } return options; } From f336270a1955b542a574c8d3f25b9f50cb7cac20 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:06:56 +0100 Subject: [PATCH 13/24] fix: change draw_options declarations to const in api_context.cpp --- src/api/api_context.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index f192d29..2f18a5c 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -46,7 +46,7 @@ namespace HostApi { if (argc < 2) return JS_ThrowTypeError(c, "drawLine requires start and end positions"); const auto start = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); const auto end = Utils::get_opaque_or(c, argv[1], js_vector2_class_id, {0, 0}); - JSDrawOptions draw_options = parse_draw_options(c, argc > 2 ? argv[2] : JS_UNDEFINED); + const JSDrawOptions draw_options = parse_draw_options(c, argc > 2 ? argv[2] : JS_UNDEFINED); ::DrawLineV(start, end, draw_options.color); return JS_UNDEFINED; }, "drawLine", 3)); @@ -54,7 +54,7 @@ namespace HostApi { JS_SetPropertyStr(ctx, shape_obj, "drawRectangle", JS_NewCFunction(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv) -> JSValue { if (argc < 1) return JS_ThrowTypeError(c, "drawRectangle requires a Rectangle argument"); const auto rect = Utils::get_opaque_or(c, argv[0], js_rectangle_class_id, {0, 0, 0, 0}); - JSDrawOptions draw_options = parse_draw_options(c, argc > 1 ? argv[1] : JS_UNDEFINED); + const JSDrawOptions draw_options = parse_draw_options(c, argc > 1 ? argv[1] : JS_UNDEFINED); ::DrawRectanglePro(rect, draw_options.origin, draw_options.rotation, draw_options.color); return JS_UNDEFINED; }, "drawRectangle", 2)); @@ -66,7 +66,7 @@ namespace HostApi { if (JS_ToFloat64(c, &rad, argv[1]) != 0) { return JS_ThrowTypeError(c, "drawCircle radius must be a number"); } - JSDrawOptions draw_options = parse_draw_options(c, argc > 2 ? argv[2] : JS_UNDEFINED); + const JSDrawOptions draw_options = parse_draw_options(c, argc > 2 ? argv[2] : JS_UNDEFINED); ::DrawCircleV(center, static_cast(rad), draw_options.color); return JS_UNDEFINED; }, "drawCircle", 3)); @@ -76,7 +76,7 @@ namespace HostApi { const auto p1 = Utils::get_opaque_or(c, argv[0], js_vector2_class_id, {0, 0}); const auto p2 = Utils::get_opaque_or(c, argv[1], js_vector2_class_id, {0, 0}); const auto p3 = Utils::get_opaque_or(c, argv[2], js_vector2_class_id, {0, 0}); - JSDrawOptions draw_options = parse_draw_options(c, argc > 3 ? argv[3] : JS_UNDEFINED); + const JSDrawOptions draw_options = parse_draw_options(c, argc > 3 ? argv[3] : JS_UNDEFINED); ::DrawTriangle(p1, p2, p3, draw_options.color); return JS_UNDEFINED; }, "drawTriangle", 4)); @@ -88,7 +88,7 @@ namespace HostApi { if (JS_ToFloat64(c, &radH, argv[1]) != 0 || JS_ToFloat64(c, &radV, argv[2]) != 0) { return JS_ThrowTypeError(c, "drawEllipse radii parameters must be numbers"); } - JSDrawOptions draw_options = parse_draw_options(c, argc > 3 ? argv[3] : JS_UNDEFINED); + const JSDrawOptions draw_options = parse_draw_options(c, argc > 3 ? argv[3] : JS_UNDEFINED); ::DrawEllipseV(center, static_cast(radH), static_cast(radV), draw_options.color); return JS_UNDEFINED; }, "drawEllipse", 4)); @@ -101,7 +101,8 @@ namespace HostApi { JS_SetPropertyStr(ctx, render_obj, "withLayer2D", JS_NewCFunctionData(ctx, [](JSContext* c, JSValueConst, int argc, JSValueConst* argv, int, JSValue* magic_argv) -> JSValue { if (argc > 0 && JS_IsFunction(c, argv[0])) { - Utils::ScopedJSValue res(c, JS_Call(c, argv[0], JS_UNDEFINED, 1, &magic_argv[0])); + const Utils::ScopedJSValue res(c, JS_Call(c, argv[0], JS_UNDEFINED, 1, &magic_argv[0])); + if (JS_IsException(res.get())) return JS_EXCEPTION; } return JS_UNDEFINED; }, 1, 0, 1, &r2d_val)); From 47056bbc2744d7466d13e248e83f6d694ae653db Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:07:32 +0100 Subject: [PATCH 14/24] fix --- examples/info.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/info.js b/examples/info.js index 685678d..b0d28c8 100644 --- a/examples/info.js +++ b/examples/info.js @@ -1,4 +1,4 @@ -import {Application, Vector2, Palette, Font, Info} from "vectorjs"; +import {Application, Vector2, Palette, Info} from "vectorjs"; const screenWidth = 600; const screenHeight = 800; @@ -8,10 +8,6 @@ const line2 = new Vector2(10, 30); const app = new Application(screenWidth, screenHeight,"Info"); app.run({ - onInit() { - this.customFont = new Font("C:\\workspace\\c\\vectorjs\\examples\\assets\\AnonymousPro-Regular.ttf"); - }, - onDraw(render) { render.withLayer2D((ctx) => { ctx.text.drawText(line1, `RAYLIB VERSION: ${Info.RAYLIB_VERSION}`) From be131657cc914ed21ae4bcf87ebfa077a676c84f Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:09:12 +0100 Subject: [PATCH 15/24] fix --- examples/2d/text.js | 27 --------------------------- src/api/js_utils.hpp | 1 + 2 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 examples/2d/text.js diff --git a/examples/2d/text.js b/examples/2d/text.js deleted file mode 100644 index bd4eac5..0000000 --- a/examples/2d/text.js +++ /dev/null @@ -1,27 +0,0 @@ -import {Application, Rectangle, Vector2, Palette, Font} from "vectorjs"; - -const screenWidth = 600; -const screenHeight = 800; -const fpsPos = new Vector2(10, 10); -const origin = new Vector2(0, 0); - -const app = new Application(screenWidth, screenHeight,"Window"); -app.run({ - - onInit() { - this.customFont = new Font("C:\\workspace\\c\\vectorjs\\examples\\assets\\AnonymousPro-Regular.ttf"); - }, - - onDraw(render) { - render.withLayer2D((ctx) => { - ctx.text.drawText(fpsPos, "this is a sample text", { - font: this.customFont, - color: Palette.RED, - fontSize: 24.0, - spacing: 2.0, - rotation: 0.0, - origin: origin - }) - }); - } -}); \ No newline at end of file diff --git a/src/api/js_utils.hpp b/src/api/js_utils.hpp index dc94565..339e3d6 100644 --- a/src/api/js_utils.hpp +++ b/src/api/js_utils.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "js_types.hpp" namespace HostApi::Utils { From fcd30064201534bda5fb568ced854d3364bbeaf4 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:21:50 +0100 Subject: [PATCH 16/24] fix: correct argument index for alpha channel in JSColor constructor --- src/api/api_classes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_classes.cpp b/src/api/api_classes.cpp index 35a1d6d..57bbd69 100644 --- a/src/api/api_classes.cpp +++ b/src/api/api_classes.cpp @@ -110,7 +110,7 @@ namespace HostApi { if (argc > 0 && JS_ToInt32(c, &r, argv[0]) < 0) return JS_EXCEPTION; if (argc > 1 && JS_ToInt32(c, &g, argv[1]) < 0) return JS_EXCEPTION; if (argc > 2 && JS_ToInt32(c, &b, argv[2]) < 0) return JS_EXCEPTION; - if (argc > 2 && JS_ToInt32(c, &a, argv[2]) < 0) return JS_EXCEPTION; + if (argc > 3 && JS_ToInt32(c, &a, argv[3]) < 0) return JS_EXCEPTION; return Utils::create_js_instance( c, new_target, js_color_class_id, From 2401d311d39972b336ca0d426a65d5bf70b8d175 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:22:35 +0100 Subject: [PATCH 17/24] fix: remove Font export from module in hostapi.hpp --- src/api/hostapi.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api/hostapi.hpp b/src/api/hostapi.hpp index 109de9b..f02d65e 100644 --- a/src/api/hostapi.hpp +++ b/src/api/hostapi.hpp @@ -22,7 +22,6 @@ namespace HostApi { JS_AddModuleExport(ctx, m, "Color"); JS_AddModuleExport(ctx, m, "Vector2"); JS_AddModuleExport(ctx, m, "Rectangle"); - JS_AddModuleExport(ctx, m, "Font"); JS_AddModuleExport(ctx, m, "Palette"); JS_AddModuleExport(ctx, m, "Info"); JS_AddModuleExport(ctx, m, "ConfigFlags"); From 280ecce94f17791da60c59d514346ac4de4065eb Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:22:39 +0100 Subject: [PATCH 18/24] fix: include in js_utils.hpp --- src/api/js_utils.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/js_utils.hpp b/src/api/js_utils.hpp index 339e3d6..9d2e292 100644 --- a/src/api/js_utils.hpp +++ b/src/api/js_utils.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "js_types.hpp" namespace HostApi::Utils { From 2dcacab0c4c9a38dd7e5cc80cc3d18448b29f3ff Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:24:14 +0100 Subject: [PATCH 19/24] fix: correct argument order for Application initialization in README --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 2805ebc..616ad9e 100644 --- a/README.MD +++ b/README.MD @@ -16,7 +16,7 @@ const point1 = new Vector2(400, 150); const point2 = new Vector2(300, 350); const point3 = new Vector2(500, 350); -const app = new Application(screenHeight, screenWidth, "Window"); +const app = new Application(screenWidth, screenHeight, "Window"); app.run({ onDraw(render) { From 431a3689e21c0516a4fa111acfe83db35d1dac25 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:24:42 +0100 Subject: [PATCH 20/24] fix: correct argument order for Application initialization in solar2d.js --- examples/2d/solar2d.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/2d/solar2d.js b/examples/2d/solar2d.js index 9a1a275..5d69faa 100644 --- a/examples/2d/solar2d.js +++ b/examples/2d/solar2d.js @@ -14,7 +14,7 @@ const planets = [ { name: "Jupiter", radius: 320, size: 22, speed: 0.5, color: Palette.BROWN } ]; -const app = new Application(screenHeight, screenWidth, "Solar System Simulation"); +const app = new Application(screenWidth, screenHeight, "Solar System Simulation"); app.run({ onDraw(render) { From 6a5c78df279267ffce54ec665ff1abb96d4a43ec Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:25:07 +0100 Subject: [PATCH 21/24] fix: correct argument order for Application initialization in shapes2.js --- examples/2d/shapes2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/2d/shapes2.js b/examples/2d/shapes2.js index cadaa27..91184b6 100644 --- a/examples/2d/shapes2.js +++ b/examples/2d/shapes2.js @@ -5,7 +5,7 @@ const screenHeight = 600; const center = new Vector2(screenWidth / 2, screenHeight / 2); const fpsPos = new Vector2(10, 10); -const app = new Application(screenHeight, screenWidth, "Animated Shapes"); +const app = new Application(screenWidth, screenHeight, "Animated Shapes"); app.run({ onDraw(render) { From ccfe5ffea914c5f988aab66c56951befe8b3d783 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:26:45 +0100 Subject: [PATCH 22/24] refactor: remove Font class and update drawPixel method signature in vectorjs.d.ts --- src/vectorjs.d.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/vectorjs.d.ts b/src/vectorjs.d.ts index ce89dcf..4992900 100644 --- a/src/vectorjs.d.ts +++ b/src/vectorjs.d.ts @@ -34,14 +34,6 @@ declare module "vectorjs" { constructor(x: number, y: number, width: number, height: number); } - /** - * Custom font resource loaded from a path. - */ - export class Font { - constructor(path: string); - constructor(path: string, baseSize: number); - } - /** * Built-in color palette options matching Raylib defaults. */ @@ -88,7 +80,7 @@ declare module "vectorjs" { * Exposes individual primitive 2D structural rendering mechanisms. */ export interface Render2DShapes { - drawPixel(position: Vector2, options: DrawOptions): void; + drawPixel(position: Vector2, color: Color): void; drawLine(startPosition: Vector2, endPosition: Vector2, options: DrawOptions): void; drawRectangle(rect: Rectangle, options: DrawOptions): void; drawCircle(centre: Vector2, radius: number, options: DrawOptions): void; From 815042108370a3f0b5cba73753d5b03f779194b3 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:28:34 +0100 Subject: [PATCH 23/24] fix: resolve exception handling in update and draw function calls in api_classes.cpp --- src/api/api_classes.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api/api_classes.cpp b/src/api/api_classes.cpp index 57bbd69..3797d9e 100644 --- a/src/api/api_classes.cpp +++ b/src/api/api_classes.cpp @@ -67,17 +67,19 @@ namespace HostApi { if (JS_IsFunction(ctx, on_update_func)) { JSValue u = update_obj.get(); Utils::ScopedJSValue ret(ctx, JS_Call(ctx, on_update_func, user_app, 1, &u)); - if (JS_IsException(ret)) { + if (JS_IsException(ret.get())) { EndDrawing(); - break; + if (IsWindowReady()) CloseWindow(); + return JS_EXCEPTION; } } if (JS_IsFunction(ctx, on_draw_func)) { JSValue r = render_obj.get(); Utils::ScopedJSValue ret(ctx, JS_Call(ctx, on_draw_func, user_app, 1, &r)); - if (JS_IsException(ret)) { + if (JS_IsException(ret.get())) { EndDrawing(); - break; + if (IsWindowReady()) CloseWindow(); + return JS_EXCEPTION; } } EndDrawing(); From 17fd2779dd81afddbdbdfeca45240d9d0148c489 Mon Sep 17 00:00:00 2001 From: Peter Richmond <3448039+burdockcascade@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:45:04 +0100 Subject: [PATCH 24/24] fix: update constructor argument types to use double for JSVector2 in api_classes.cpp --- src/api/api_classes.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/api_classes.cpp b/src/api/api_classes.cpp index 3797d9e..a80bee6 100644 --- a/src/api/api_classes.cpp +++ b/src/api/api_classes.cpp @@ -139,9 +139,9 @@ namespace HostApi { delete Utils::get_opaque(val, js_vector2_class_id); }, .constructor = [](auto c, auto new_target, int argc, auto argv) -> JSValue { - int32_t x = 0, y = 0; - if (argc > 0 && JS_ToInt32(c, &x, argv[0]) < 0) return JS_EXCEPTION; - if (argc > 1 && JS_ToInt32(c, &y, argv[1]) < 0) return JS_EXCEPTION; + double x = 0, y = 0; + if (argc > 0 && JS_ToFloat64(c, &x, argv[0]) < 0) return JS_EXCEPTION; + if (argc > 1 && JS_ToFloat64(c, &y, argv[1]) < 0) return JS_EXCEPTION; return Utils::create_js_instance( c, new_target, js_vector2_class_id,