Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b622d9788 | ||
|
|
42087910ab | ||
|
|
c581935fd8 | ||
|
|
632b038561 | ||
|
|
6eb33b8e48 | ||
|
|
3d5f345236 | ||
|
|
4689ea1167 | ||
|
|
15d85096a2 | ||
|
|
f2c2ecf692 | ||
|
|
458215f838 | ||
|
|
7f002e306d | ||
|
|
91a3fef065 | ||
|
|
843b4bd8a8 | ||
|
|
41fdf49df5 | ||
|
|
c9adbed3ff | ||
|
|
3459d85a82 | ||
|
|
9ecdaae7a7 | ||
|
|
43b55b29f3 | ||
|
|
a88cee7fae | ||
|
|
8b3f5476a9 | ||
|
|
233f59e04a | ||
|
|
7b16259c00 | ||
|
|
43baa23dfe | ||
|
|
a9ebea9f26 | ||
|
|
19b729afbf | ||
|
|
47729c225f | ||
|
|
ea6cf5db49 | ||
|
|
794baf8598 | ||
|
|
a551368681 | ||
|
|
c20ca8c937 | ||
|
|
0217cf0da6 | ||
|
|
6bd7251933 | ||
|
|
6dc8d97001 | ||
|
|
9c0565d34f | ||
|
|
0702e3495d | ||
|
|
108c39d22d | ||
|
|
946ebe5cd7 | ||
|
|
fa12281ab9 | ||
|
|
38a52fcb73 | ||
|
|
95a95e55e3 | ||
|
|
fc978a5766 | ||
|
|
9082ee2c47 | ||
|
|
b8ad8431f4 | ||
|
|
1c2b8228fe | ||
|
|
a274fb174f | ||
|
|
3622a5ec58 | ||
|
|
8a5f8a4d74 | ||
|
|
c1d341eecd | ||
|
|
3176e6775d | ||
|
|
34dcd0a235 | ||
|
|
cbda7dfbc9 | ||
|
|
d039e2cfe6 | ||
|
|
c02bd06ec0 | ||
|
|
efa63771e6 | ||
|
|
9f6d27fb3c | ||
|
|
1a61ae6f77 | ||
|
|
83c816fd0e | ||
|
|
adbaa92dd5 | ||
|
|
580df9f233 | ||
|
|
d5d17560f9 | ||
|
|
cd05ab97b5 | ||
|
|
4eecbd692b | ||
|
|
72beed7177 | ||
|
|
e0595de71a | ||
|
|
6687008d1a | ||
|
|
5b9f1b8f51 | ||
|
|
c570de7f41 | ||
|
|
d0138a6c23 | ||
|
|
29aa25e866 | ||
|
|
ef28be93db | ||
|
|
0d7be6a94e | ||
|
|
4fe78c4a63 | ||
|
|
b52edb2746 | ||
|
|
79d5412fe6 | ||
|
|
fcec2cd1dc | ||
|
|
2038ce15a7 | ||
|
|
08557011cb | ||
|
|
3e87bfd6cc | ||
|
|
ef86dd3ecf | ||
|
|
c887bcf7b9 | ||
|
|
709f2459e4 | ||
|
|
cdf8686c64 | ||
|
|
2fdf74f6ee | ||
|
|
e689679aac | ||
|
|
f70f65d1c3 | ||
|
|
d9b316270d | ||
|
|
e2668b330e | ||
|
|
90b5d1430f | ||
|
|
7c47c43655 | ||
|
|
9e45219706 | ||
|
|
d098800c88 | ||
|
|
3a40076958 | ||
|
|
06108df3d4 | ||
|
|
a442cf5a4d | ||
|
|
6dee29d213 | ||
|
|
7711c644a0 | ||
|
|
aab0a56349 | ||
|
|
13245bbc98 | ||
|
|
c25166d35a | ||
|
|
fc09693c93 | ||
|
|
b71c72db8b | ||
|
|
66591e32b5 | ||
|
|
fba05fa0fb | ||
|
|
11357d4fb5 | ||
|
|
674eb237e0 | ||
|
|
939269b060 | ||
|
|
f54200a7dd | ||
|
|
9ae2357493 | ||
|
|
da525cd111 | ||
|
|
c3f07c0ef5 | ||
|
|
2e7643aa2a | ||
|
|
aca9baf585 | ||
|
|
b4371ba3e0 | ||
|
|
4e118dd8e9 | ||
|
|
9279e21b84 | ||
|
|
8d9bb4a2c9 | ||
|
|
1040c61863 | ||
|
|
e86bdf52fe | ||
|
|
53b3f0af9c | ||
|
|
09f48d08b9 | ||
|
|
4eb592b740 | ||
|
|
c603e8f006 | ||
|
|
f334a2ad56 | ||
|
|
a39f287a88 | ||
|
|
758b3e4704 | ||
|
|
aa70dcbdc2 | ||
|
|
3667d53eae | ||
|
|
01df337ccc | ||
|
|
ad182d68ec | ||
|
|
f7dcc8f57c | ||
|
|
f73f738459 | ||
|
|
bf74a3c7d4 | ||
|
|
e8fb50659d | ||
|
|
00df0899fa | ||
|
|
ae5ba67fc8 | ||
|
|
bc929988b2 | ||
|
|
2346040d46 | ||
|
|
2eb6b3e0b4 | ||
|
|
2edcd89780 | ||
|
|
a63e5c5b55 | ||
|
|
af21e10e97 | ||
|
|
1b97527120 | ||
|
|
8074e2a82e | ||
|
|
db1afb6477 | ||
|
|
45311408d6 | ||
|
|
1141fca63a | ||
|
|
7b70def11f | ||
|
|
aac0c3813b | ||
|
|
49786842f0 | ||
|
|
9f9dfe03a6 | ||
|
|
792da2ce4b | ||
|
|
85ee724754 |
17
.cell/cell.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
sdl_video = "main"
|
||||||
|
[dependencies]
|
||||||
|
extramath = "https://gitea.pockle.world/john/extramath@master"
|
||||||
|
[system]
|
||||||
|
ar_timer = 60
|
||||||
|
actor_memory = 0
|
||||||
|
net_service = 0.1
|
||||||
|
reply_timeout = 60
|
||||||
|
actor_max = "10_000"
|
||||||
|
stack_max = 0
|
||||||
|
[actors]
|
||||||
|
[actors.prosperon/sdl_video]
|
||||||
|
main = true
|
||||||
|
[actors.prosperon/prosperon]
|
||||||
|
main = true
|
||||||
|
[actors.prosperon]
|
||||||
|
main = true
|
||||||
6
.cell/lock.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[modules]
|
||||||
|
[modules.extramath]
|
||||||
|
hash = "MCLZT3JABTAENS4WVXKGWJ7JPBLZER4YQ5VN2PE7ZD2Z4WYGTIMA===="
|
||||||
|
url = "https://gitea.pockle.world/john/extramath@master"
|
||||||
|
downloaded = "Monday June 2 12:07:20.42 PM -5 2025 AD"
|
||||||
|
commit = "84d81a19a8455bcf8dc494739e9e6d545df6ff2c"
|
||||||
13
.gitignore
vendored
@@ -6,27 +6,18 @@ build/
|
|||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
*.d
|
*.d
|
||||||
tags
|
|
||||||
Jenkinsfile
|
|
||||||
*~
|
*~
|
||||||
*.log
|
*.log
|
||||||
*.gz
|
*.gz
|
||||||
*.tar
|
*.tar
|
||||||
.nova/
|
.nova/
|
||||||
packer*
|
|
||||||
primum
|
|
||||||
sokol-shdc*
|
|
||||||
source/shaders/*.h
|
source/shaders/*.h
|
||||||
core.cdb
|
|
||||||
primum.exe
|
|
||||||
core.cdb.h
|
|
||||||
jsc
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.html
|
*.html
|
||||||
.vscode
|
.vscode
|
||||||
*.icns
|
*.icns
|
||||||
game.zip
|
|
||||||
icon.ico
|
icon.ico
|
||||||
steam/
|
steam/
|
||||||
subprojects/*/
|
subprojects/*/
|
||||||
build_dbg/
|
build_dbg/
|
||||||
|
modules/
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ This is a game engine developed using a QuickJS fork as its scripting language.
|
|||||||
|
|
||||||
## Coding Practices
|
## Coding Practices
|
||||||
- Use K&R style C
|
- Use K&R style C
|
||||||
- Use as little whitespace as possible
|
|
||||||
- Javascript style prefers objects and prototypical inheritence over ES6 classes, liberal use of closures, and var everywhere
|
- Javascript style prefers objects and prototypical inheritence over ES6 classes, liberal use of closures, and var everywhere
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|||||||
21
CLAUDE.md
@@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
## Build Commands
|
## Build Commands
|
||||||
|
|
||||||
### Build variants
|
### Build variants
|
||||||
- `make debug` - Build debug version (uses meson debug configuration)
|
- `make` - Make and install debug version. Usually all that's needed.
|
||||||
- `make fast` - Build optimized version
|
- `make fast` - Build optimized version
|
||||||
- `make release` - Build release version with LTO and optimizations
|
- `make release` - Build release version with LTO and optimizations
|
||||||
- `make small` - Build minimal size version
|
- `make small` - Build minimal size version
|
||||||
@@ -13,10 +13,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
- `make crosswin` - Cross-compile for Windows using mingw32
|
- `make crosswin` - Cross-compile for Windows using mingw32
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
- `meson test -C build_dbg` - Run all tests in debug build
|
After install with 'make', just run 'cell' and point it at the actor you want to launch. "cell tests/toml" runs the actor "tests/toml.js"
|
||||||
- `meson test -C build_<variant>` - Run tests in specific build variant
|
|
||||||
- `./build_dbg/prosperon tests/<testname>.js` - Run specific test
|
## Scripting language
|
||||||
- Available tests: `spawn_actor`, `empty`, `nota`, `wota`, `portalspawner`, `overling`, `send`, `delay`
|
This is called "cell", but it is is a variant of javascript and extremely similar.
|
||||||
|
|
||||||
### Common development commands
|
### Common development commands
|
||||||
- `meson setup build_<variant>` - Configure build directory
|
- `meson setup build_<variant>` - Configure build directory
|
||||||
@@ -126,7 +126,7 @@ meson test -C build_dbg
|
|||||||
### Debugging
|
### Debugging
|
||||||
- Use debug build: `make debug`
|
- Use debug build: `make debug`
|
||||||
- Tracy profiler support when enabled
|
- Tracy profiler support when enabled
|
||||||
- Console logging available via `console.log()`, `console.error()`, etc.
|
- Console logging available via `log.console()`, `log.error()`, etc.
|
||||||
- Log files written to `.prosperon/log.txt`
|
- Log files written to `.prosperon/log.txt`
|
||||||
|
|
||||||
# Project Structure Notes
|
# Project Structure Notes
|
||||||
@@ -202,6 +202,11 @@ meson test -C build_dbg
|
|||||||
|
|
||||||
### Utility Modules
|
### Utility Modules
|
||||||
- `time` - Time management and delays
|
- `time` - Time management and delays
|
||||||
|
- **Must be imported with `use('time')`**
|
||||||
|
- No `time.now()` function - use:
|
||||||
|
- `time.number()` - Number representation of current time
|
||||||
|
- `time.record()` - Struct representation of current time
|
||||||
|
- `time.text()` - Text representation of current time
|
||||||
- `io` - File I/O operations
|
- `io` - File I/O operations
|
||||||
- `json` - JSON parsing and serialization
|
- `json` - JSON parsing and serialization
|
||||||
- `util` - General utilities
|
- `util` - General utilities
|
||||||
@@ -234,7 +239,7 @@ When sending a message with a callback, respond by sending to the message itself
|
|||||||
```javascript
|
```javascript
|
||||||
// Sender side:
|
// Sender side:
|
||||||
send(actor, {type: 'status'}, response => {
|
send(actor, {type: 'status'}, response => {
|
||||||
console.log(response); // Handle the response
|
log.console(response); // Handle the response
|
||||||
});
|
});
|
||||||
|
|
||||||
// Receiver side:
|
// Receiver side:
|
||||||
@@ -279,7 +284,7 @@ $_.receiver(msg => {
|
|||||||
- Custom formats: Aseprite animations, etc.
|
- Custom formats: Aseprite animations, etc.
|
||||||
|
|
||||||
### Developer Tools
|
### Developer Tools
|
||||||
- Built-in documentation system with `prosperon.DOC`
|
- Built-in documentation system with `cell.DOC`
|
||||||
- Tracy profiler integration for performance monitoring
|
- Tracy profiler integration for performance monitoring
|
||||||
- Imgui debugging tools
|
- Imgui debugging tools
|
||||||
- Console logging with various severity levels
|
- Console logging with various severity levels
|
||||||
|
|||||||
12
Makefile
@@ -1,22 +1,22 @@
|
|||||||
debug: FORCE
|
debug: FORCE
|
||||||
meson setup build_dbg -Dbuildtype=debug
|
meson setup build_dbg -Dbuildtype=debugoptimized
|
||||||
meson compile -C build_dbg
|
meson install --only-changed -C build_dbg
|
||||||
|
|
||||||
fast: FORCE
|
fast: FORCE
|
||||||
meson setup build_fast
|
meson setup build_fast
|
||||||
meson compile -C build_fast
|
meson install -C build_fast
|
||||||
|
|
||||||
release: FORCE
|
release: FORCE
|
||||||
meson setup -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true build_release
|
meson setup -Dbuildtype=release -Db_lto=true -Db_lto_mode=thin -Db_ndebug=true build_release
|
||||||
meson compile -C build_release
|
meson install -C build_release
|
||||||
|
|
||||||
sanitize: FORCE
|
sanitize: FORCE
|
||||||
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani
|
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani
|
||||||
meson compile -C build_sani
|
meson install -C build_sani
|
||||||
|
|
||||||
small: FORCE
|
small: FORCE
|
||||||
meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true build_small
|
meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true build_small
|
||||||
meson compile -C build_small
|
meson install -C build_small
|
||||||
|
|
||||||
web: FORCE
|
web: FORCE
|
||||||
meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web
|
meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web
|
||||||
|
|||||||
43
benchmarks/binarytree.ce
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
function mainThread() {
|
||||||
|
var maxDepth = Math.max(6, Number(arg[0] || 16));
|
||||||
|
|
||||||
|
var stretchDepth = maxDepth + 1;
|
||||||
|
var check = itemCheck(bottomUpTree(stretchDepth));
|
||||||
|
log.console(`stretch tree of depth ${stretchDepth}\t check: ${check}`);
|
||||||
|
|
||||||
|
var longLivedTree = bottomUpTree(maxDepth);
|
||||||
|
|
||||||
|
for (let depth = 4; depth <= maxDepth; depth += 2) {
|
||||||
|
var iterations = 1 << maxDepth - depth + 4;
|
||||||
|
work(iterations, depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console(`long lived tree of depth ${maxDepth}\t check: ${itemCheck(longLivedTree)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function work(iterations, depth) {
|
||||||
|
let check = 0;
|
||||||
|
for (let i = 0; i < iterations; i++)
|
||||||
|
check += itemCheck(bottomUpTree(depth));
|
||||||
|
log.console(`${iterations}\t trees of depth ${depth}\t check: ${check}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TreeNode(left, right) {
|
||||||
|
return {left, right};
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemCheck(node) {
|
||||||
|
if (node.left == null)
|
||||||
|
return 1;
|
||||||
|
return 1 + itemCheck(node.left) + itemCheck(node.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bottomUpTree(depth) {
|
||||||
|
return depth > 0
|
||||||
|
? new TreeNode(bottomUpTree(depth - 1), bottomUpTree(depth - 1))
|
||||||
|
: new TreeNode(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
mainThread()
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
24
benchmarks/eratosthenes.ce
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
var blob = use('blob')
|
||||||
|
|
||||||
|
function eratosthenes (n) {
|
||||||
|
var sieve = new blob(n, true)
|
||||||
|
var sqrtN = Math.trunc(Math.sqrt(n));
|
||||||
|
|
||||||
|
for (i = 2; i <= sqrtN; i++)
|
||||||
|
if (sieve.read_logical(i))
|
||||||
|
for (j = i * i; j <= n; j += i)
|
||||||
|
sieve.write_bit(j, false);
|
||||||
|
|
||||||
|
return sieve;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sieve = eratosthenes(10000000);
|
||||||
|
stone(sieve)
|
||||||
|
|
||||||
|
var c = 0
|
||||||
|
for (var i = 0; i < sieve.length; i++)
|
||||||
|
if (sieve.read_logical(i)) c++
|
||||||
|
|
||||||
|
log.console(c)
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
58
benchmarks/fannkuch.ce
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
function fannkuch(n) {
|
||||||
|
var perm1 = [n]
|
||||||
|
for (let i = 0; i < n; i++) perm1[i] = i
|
||||||
|
var perm = [n]
|
||||||
|
var count = [n]
|
||||||
|
var f = 0, flips = 0, nperm = 0, checksum = 0
|
||||||
|
var i, k, r
|
||||||
|
|
||||||
|
r = n
|
||||||
|
while (r > 0) {
|
||||||
|
i = 0
|
||||||
|
while (r != 1) { count[r-1] = r; r -= 1 }
|
||||||
|
while (i < n) { perm[i] = perm1[i]; i += 1 }
|
||||||
|
|
||||||
|
// Count flips and update max and checksum
|
||||||
|
f = 0
|
||||||
|
k = perm[0]
|
||||||
|
while (k != 0) {
|
||||||
|
i = 0
|
||||||
|
while (2*i < k) {
|
||||||
|
let t = perm[i]; perm[i] = perm[k-i]; perm[k-i] = t
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
k = perm[0]
|
||||||
|
f += 1
|
||||||
|
}
|
||||||
|
if (f > flips) flips = f
|
||||||
|
if ((nperm & 0x1) == 0) checksum += f; else checksum -= f
|
||||||
|
|
||||||
|
// Use incremental change to generate another permutation
|
||||||
|
var more = true
|
||||||
|
while (more) {
|
||||||
|
if (r == n) {
|
||||||
|
log.console( checksum )
|
||||||
|
return flips
|
||||||
|
}
|
||||||
|
let p0 = perm1[0]
|
||||||
|
i = 0
|
||||||
|
while (i < r) {
|
||||||
|
let j = i + 1
|
||||||
|
perm1[i] = perm1[j]
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
perm1[r] = p0
|
||||||
|
|
||||||
|
count[r] -= 1
|
||||||
|
if (count[r] > 0) more = false; else r += 1
|
||||||
|
}
|
||||||
|
nperm += 1
|
||||||
|
}
|
||||||
|
return flips;
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = arg[0] || 10
|
||||||
|
|
||||||
|
log.console(`Pfannkuchen(${n}) = ${fannkuch(n)}`)
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
20
benchmarks/hyperfine_wota_nota_json.sh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Run hyperfine with parameter lists
|
||||||
|
# This will create a cross-product of all libraries × all scenarios
|
||||||
|
hyperfine \
|
||||||
|
--warmup 3 \
|
||||||
|
--runs 20 \
|
||||||
|
-i \
|
||||||
|
--export-csv wota_vs_nota_vs_json.csv \
|
||||||
|
--export-json wota_vs_nota_vs_json.json \
|
||||||
|
--export-markdown wota_vs_nota_vs_json.md \
|
||||||
|
--parameter-list lib wota,nota,json \
|
||||||
|
--parameter-list scen empty,integers,floats,strings,objects,nested,large_array \
|
||||||
|
'cell benchmarks/wota_nota_json {lib} {scen}'
|
||||||
|
|
||||||
|
|
||||||
|
echo "Benchmark complete! Results saved to:"
|
||||||
|
echo " - wota_vs_nota_vs_json.csv"
|
||||||
|
echo " - wota_vs_nota_vs_json.json"
|
||||||
|
echo " - wota_vs_nota_vs_json.md"
|
||||||
395
benchmarks/js_perf.ce
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
var time = use('time')
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// JavaScript Performance Benchmark Suite
|
||||||
|
// Tests core JS operations: property access, function calls, arithmetic, etc.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Test configurations
|
||||||
|
const iterations = {
|
||||||
|
simple: 10000000,
|
||||||
|
medium: 1000000,
|
||||||
|
complex: 100000
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utility: measureTime(fn) => how long fn() takes in seconds
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function measureTime(fn) {
|
||||||
|
var start = time.number();
|
||||||
|
fn();
|
||||||
|
var end = time.number();
|
||||||
|
return (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Benchmark: Property Access
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function benchPropertyAccess() {
|
||||||
|
var obj = {
|
||||||
|
a: 1, b: 2, c: 3, d: 4, e: 5,
|
||||||
|
nested: { x: 10, y: 20, z: 30 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var readTime = measureTime(function() {
|
||||||
|
var sum = 0;
|
||||||
|
for (var i = 0; i < iterations.simple; i++) {
|
||||||
|
sum += obj.a + obj.b + obj.c + obj.d + obj.e;
|
||||||
|
sum += obj.nested.x + obj.nested.y + obj.nested.z;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var writeTime = measureTime(function() {
|
||||||
|
for (var i = 0; i < iterations.simple; i++) {
|
||||||
|
obj.a = i;
|
||||||
|
obj.b = i + 1;
|
||||||
|
obj.c = i + 2;
|
||||||
|
obj.nested.x = i * 2;
|
||||||
|
obj.nested.y = i * 3;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { readTime: readTime, writeTime: writeTime };
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Benchmark: Function Calls
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function benchFunctionCalls() {
|
||||||
|
function add(a, b) { return a + b; }
|
||||||
|
function multiply(a, b) { return a * b; }
|
||||||
|
function complexCalc(a, b, c) { return (a + b) * c / 2; }
|
||||||
|
|
||||||
|
var obj = {
|
||||||
|
method: function(x) { return x * 2; },
|
||||||
|
nested: {
|
||||||
|
deepMethod: function(x, y) { return x + y; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var simpleCallTime = measureTime(function() {
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < iterations.simple; i++) {
|
||||||
|
result = add(i, 1);
|
||||||
|
result = multiply(result, 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var methodCallTime = measureTime(function() {
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < iterations.simple; i++) {
|
||||||
|
result = obj.method(i);
|
||||||
|
result = obj.nested.deepMethod(result, i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var complexCallTime = measureTime(function() {
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
result = complexCalc(i, i + 1, i + 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
simpleCallTime: simpleCallTime,
|
||||||
|
methodCallTime: methodCallTime,
|
||||||
|
complexCallTime: complexCallTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Benchmark: Array Operations
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function benchArrayOps() {
|
||||||
|
var pushTime = measureTime(function() {
|
||||||
|
var arr = [];
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
arr.push(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var arr = [];
|
||||||
|
for (var i = 0; i < 10000; i++) arr.push(i);
|
||||||
|
|
||||||
|
var accessTime = measureTime(function() {
|
||||||
|
var sum = 0;
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
sum += arr[i % 10000];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var iterateTime = measureTime(function() {
|
||||||
|
var sum = 0;
|
||||||
|
for (var j = 0; j < 1000; j++) {
|
||||||
|
for (var i = 0; i < arr.length; i++) {
|
||||||
|
sum += arr[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
pushTime: pushTime,
|
||||||
|
accessTime: accessTime,
|
||||||
|
iterateTime: iterateTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Benchmark: Object Creation
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function benchObjectCreation() {
|
||||||
|
var literalTime = measureTime(function() {
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
var obj = { x: i, y: i * 2, z: i * 3 };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function Point(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructorTime = measureTime(function() {
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
var p = new Point(i, i * 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var protoObj = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
move: function(dx, dy) {
|
||||||
|
this.x += dx;
|
||||||
|
this.y += dy;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var prototypeTime = measureTime(function() {
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
var obj = Object.create(protoObj);
|
||||||
|
obj.x = i;
|
||||||
|
obj.y = i * 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
literalTime: literalTime,
|
||||||
|
constructorTime: constructorTime,
|
||||||
|
prototypeTime: prototypeTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Benchmark: String Operations
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function benchStringOps() {
|
||||||
|
var concatTime = measureTime(function() {
|
||||||
|
var str = "";
|
||||||
|
for (var i = 0; i < iterations.complex; i++) {
|
||||||
|
str = "test" + i + "value";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var strings = [];
|
||||||
|
for (var i = 0; i < 1000; i++) {
|
||||||
|
strings.push("string" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var joinTime = measureTime(function() {
|
||||||
|
for (var i = 0; i < iterations.complex; i++) {
|
||||||
|
var result = strings.join(",");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var splitTime = measureTime(function() {
|
||||||
|
var str = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p";
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
var parts = str.split(",");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
concatTime: concatTime,
|
||||||
|
joinTime: joinTime,
|
||||||
|
splitTime: splitTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Benchmark: Arithmetic Operations
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function benchArithmetic() {
|
||||||
|
var intMathTime = measureTime(function() {
|
||||||
|
var result = 1;
|
||||||
|
for (var i = 0; i < iterations.simple; i++) {
|
||||||
|
result = ((result + i) * 2 - 1) / 3;
|
||||||
|
result = result % 1000 + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var floatMathTime = measureTime(function() {
|
||||||
|
var result = 1.5;
|
||||||
|
for (var i = 0; i < iterations.simple; i++) {
|
||||||
|
result = Math.sin(result) + Math.cos(i * 0.01);
|
||||||
|
result = Math.sqrt(Math.abs(result)) + 0.1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var bitwiseTime = measureTime(function() {
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < iterations.simple; i++) {
|
||||||
|
result = (result ^ i) & 0xFFFF;
|
||||||
|
result = (result << 1) | (result >> 15);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
intMathTime: intMathTime,
|
||||||
|
floatMathTime: floatMathTime,
|
||||||
|
bitwiseTime: bitwiseTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Benchmark: Closure Operations
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function benchClosures() {
|
||||||
|
function makeAdder(x) {
|
||||||
|
return function(y) { return x + y; };
|
||||||
|
}
|
||||||
|
|
||||||
|
var closureCreateTime = measureTime(function() {
|
||||||
|
var funcs = [];
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
funcs.push(makeAdder(i));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var adders = [];
|
||||||
|
for (var i = 0; i < 1000; i++) {
|
||||||
|
adders.push(makeAdder(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
var closureCallTime = measureTime(function() {
|
||||||
|
var sum = 0;
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
sum += adders[i % 1000](i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
closureCreateTime: closureCreateTime,
|
||||||
|
closureCallTime: closureCallTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Main benchmark runner
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
log.console("JavaScript Performance Benchmark");
|
||||||
|
log.console("======================\n");
|
||||||
|
|
||||||
|
// Property Access
|
||||||
|
log.console("BENCHMARK: Property Access");
|
||||||
|
var propResults = benchPropertyAccess();
|
||||||
|
log.console(" Read time: " + propResults.readTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.simple / propResults.readTime).toFixed(1) + " reads/sec [" +
|
||||||
|
(propResults.readTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Write time: " + propResults.writeTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.simple / propResults.writeTime).toFixed(1) + " writes/sec [" +
|
||||||
|
(propResults.writeTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console("");
|
||||||
|
|
||||||
|
// Function Calls
|
||||||
|
log.console("BENCHMARK: Function Calls");
|
||||||
|
var funcResults = benchFunctionCalls();
|
||||||
|
log.console(" Simple calls: " + funcResults.simpleCallTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.simple / funcResults.simpleCallTime).toFixed(1) + " calls/sec [" +
|
||||||
|
(funcResults.simpleCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Method calls: " + funcResults.methodCallTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.simple / funcResults.methodCallTime).toFixed(1) + " calls/sec [" +
|
||||||
|
(funcResults.methodCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Complex calls: " + funcResults.complexCallTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / funcResults.complexCallTime).toFixed(1) + " calls/sec [" +
|
||||||
|
(funcResults.complexCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console("");
|
||||||
|
|
||||||
|
// Array Operations
|
||||||
|
log.console("BENCHMARK: Array Operations");
|
||||||
|
var arrayResults = benchArrayOps();
|
||||||
|
log.console(" Push: " + arrayResults.pushTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / arrayResults.pushTime).toFixed(1) + " pushes/sec [" +
|
||||||
|
(arrayResults.pushTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Access: " + arrayResults.accessTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / arrayResults.accessTime).toFixed(1) + " accesses/sec [" +
|
||||||
|
(arrayResults.accessTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Iterate: " + arrayResults.iterateTime.toFixed(3) + "s => " +
|
||||||
|
(1000 / arrayResults.iterateTime).toFixed(1) + " full iterations/sec");
|
||||||
|
log.console("");
|
||||||
|
|
||||||
|
// Object Creation
|
||||||
|
log.console("BENCHMARK: Object Creation");
|
||||||
|
var objResults = benchObjectCreation();
|
||||||
|
log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" +
|
||||||
|
(objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Constructor: " + objResults.constructorTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / objResults.constructorTime).toFixed(1) + " creates/sec [" +
|
||||||
|
(objResults.constructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" +
|
||||||
|
(objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console("");
|
||||||
|
|
||||||
|
// String Operations
|
||||||
|
log.console("BENCHMARK: String Operations");
|
||||||
|
var strResults = benchStringOps();
|
||||||
|
log.console(" Concat: " + strResults.concatTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.complex / strResults.concatTime).toFixed(1) + " concats/sec [" +
|
||||||
|
(strResults.concatTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Join: " + strResults.joinTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.complex / strResults.joinTime).toFixed(1) + " joins/sec [" +
|
||||||
|
(strResults.joinTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Split: " + strResults.splitTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / strResults.splitTime).toFixed(1) + " splits/sec [" +
|
||||||
|
(strResults.splitTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console("");
|
||||||
|
|
||||||
|
// Arithmetic Operations
|
||||||
|
log.console("BENCHMARK: Arithmetic Operations");
|
||||||
|
var mathResults = benchArithmetic();
|
||||||
|
log.console(" Integer math: " + mathResults.intMathTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.simple / mathResults.intMathTime).toFixed(1) + " ops/sec [" +
|
||||||
|
(mathResults.intMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Float math: " + mathResults.floatMathTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.simple / mathResults.floatMathTime).toFixed(1) + " ops/sec [" +
|
||||||
|
(mathResults.floatMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Bitwise: " + mathResults.bitwiseTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.simple / mathResults.bitwiseTime).toFixed(1) + " ops/sec [" +
|
||||||
|
(mathResults.bitwiseTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console("");
|
||||||
|
|
||||||
|
// Closures
|
||||||
|
log.console("BENCHMARK: Closures");
|
||||||
|
var closureResults = benchClosures();
|
||||||
|
log.console(" Create: " + closureResults.closureCreateTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / closureResults.closureCreateTime).toFixed(1) + " creates/sec [" +
|
||||||
|
(closureResults.closureCreateTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console(" Call: " + closureResults.closureCallTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / closureResults.closureCallTime).toFixed(1) + " calls/sec [" +
|
||||||
|
(closureResults.closureCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
|
||||||
|
log.console("");
|
||||||
|
|
||||||
|
log.console("---------------------------------------------------------");
|
||||||
|
log.console("Benchmark complete.\n");
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
40
benchmarks/mandelbrot.ce
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
var blob = use('blob')
|
||||||
|
|
||||||
|
var iter = 50, limit = 2.0;
|
||||||
|
var zr, zi, cr, ci, tr, ti;
|
||||||
|
|
||||||
|
var h = Number(arg[0]) || 500
|
||||||
|
var w = h
|
||||||
|
|
||||||
|
log.console(`P4\n${w} ${h}`);
|
||||||
|
|
||||||
|
for (let y = 0; y < h; ++y) {
|
||||||
|
// Create a blob for the row - we need w bits
|
||||||
|
var row = new blob(w);
|
||||||
|
|
||||||
|
for (let x = 0; x < w; ++x) {
|
||||||
|
zr = zi = tr = ti = 0;
|
||||||
|
cr = 2 * x / w - 1.5;
|
||||||
|
ci = 2 * y / h - 1;
|
||||||
|
for (let i = 0; i < iter && (tr + ti <= limit * limit); ++i) {
|
||||||
|
zi = 2 * zr * zi + ci;
|
||||||
|
zr = tr - ti + cr;
|
||||||
|
tr = zr * zr;
|
||||||
|
ti = zi * zi;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a 1 bit if inside the set, 0 if outside
|
||||||
|
if (tr + ti <= limit * limit)
|
||||||
|
row.write_bit(1);
|
||||||
|
else
|
||||||
|
row.write_bit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the blob to stone (immutable) to prepare for output
|
||||||
|
stone(row)
|
||||||
|
|
||||||
|
// Output the blob data as raw bytes
|
||||||
|
log.console(text(row, 'b'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
11
benchmarks/montecarlo.ce
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
var N = 1000000;
|
||||||
|
var num = 0;
|
||||||
|
for (var i = 0; i < N; i ++) {
|
||||||
|
var x = 2 * $_.random();
|
||||||
|
var y = $_.random();
|
||||||
|
if (y < Math.sin(x * x))
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
log.console(2 * num / N);
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
161
benchmarks/nbody.ce
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
var PI = Math.PI;
|
||||||
|
var SOLAR_MASS = 4 * PI * PI;
|
||||||
|
var DAYS_PER_YEAR = 365.24;
|
||||||
|
|
||||||
|
function Body(x, y, z, vx, vy, vz, mass) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.vx = vx;
|
||||||
|
this.vy = vy;
|
||||||
|
this.vz = vz;
|
||||||
|
this.mass = mass;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Jupiter() {
|
||||||
|
return new Body(
|
||||||
|
4.84143144246472090e+00,
|
||||||
|
-1.16032004402742839e+00,
|
||||||
|
-1.03622044471123109e-01,
|
||||||
|
1.66007664274403694e-03 * DAYS_PER_YEAR,
|
||||||
|
7.69901118419740425e-03 * DAYS_PER_YEAR,
|
||||||
|
-6.90460016972063023e-05 * DAYS_PER_YEAR,
|
||||||
|
9.54791938424326609e-04 * SOLAR_MASS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Saturn() {
|
||||||
|
return new Body(
|
||||||
|
8.34336671824457987e+00,
|
||||||
|
4.12479856412430479e+00,
|
||||||
|
-4.03523417114321381e-01,
|
||||||
|
-2.76742510726862411e-03 * DAYS_PER_YEAR,
|
||||||
|
4.99852801234917238e-03 * DAYS_PER_YEAR,
|
||||||
|
2.30417297573763929e-05 * DAYS_PER_YEAR,
|
||||||
|
2.85885980666130812e-04 * SOLAR_MASS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Uranus() {
|
||||||
|
return new Body(
|
||||||
|
1.28943695621391310e+01,
|
||||||
|
-1.51111514016986312e+01,
|
||||||
|
-2.23307578892655734e-01,
|
||||||
|
2.96460137564761618e-03 * DAYS_PER_YEAR,
|
||||||
|
2.37847173959480950e-03 * DAYS_PER_YEAR,
|
||||||
|
-2.96589568540237556e-05 * DAYS_PER_YEAR,
|
||||||
|
4.36624404335156298e-05 * SOLAR_MASS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Neptune() {
|
||||||
|
return new Body(
|
||||||
|
1.53796971148509165e+01,
|
||||||
|
-2.59193146099879641e+01,
|
||||||
|
1.79258772950371181e-01,
|
||||||
|
2.68067772490389322e-03 * DAYS_PER_YEAR,
|
||||||
|
1.62824170038242295e-03 * DAYS_PER_YEAR,
|
||||||
|
-9.51592254519715870e-05 * DAYS_PER_YEAR,
|
||||||
|
5.15138902046611451e-05 * SOLAR_MASS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Sun() {
|
||||||
|
return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bodies = Array(Sun(), Jupiter(), Saturn(), Uranus(), Neptune());
|
||||||
|
|
||||||
|
function offsetMomentum() {
|
||||||
|
var px = 0;
|
||||||
|
var py = 0;
|
||||||
|
var pz = 0;
|
||||||
|
var size = bodies.length;
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var body = bodies[i];
|
||||||
|
var mass = body.mass;
|
||||||
|
px += body.vx * mass;
|
||||||
|
py += body.vy * mass;
|
||||||
|
pz += body.vz * mass;
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = bodies[0];
|
||||||
|
body.vx = -px / SOLAR_MASS;
|
||||||
|
body.vy = -py / SOLAR_MASS;
|
||||||
|
body.vz = -pz / SOLAR_MASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function advance(dt) {
|
||||||
|
var size = bodies.length;
|
||||||
|
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var bodyi = bodies[i];
|
||||||
|
var vxi = bodyi.vx;
|
||||||
|
var vyi = bodyi.vy;
|
||||||
|
var vzi = bodyi.vz;
|
||||||
|
for (var j = i + 1; j < size; j++) {
|
||||||
|
var bodyj = bodies[j];
|
||||||
|
var dx = bodyi.x - bodyj.x;
|
||||||
|
var dy = bodyi.y - bodyj.y;
|
||||||
|
var dz = bodyi.z - bodyj.z;
|
||||||
|
|
||||||
|
var d2 = dx * dx + dy * dy + dz * dz;
|
||||||
|
var mag = dt / (d2 * Math.sqrt(d2));
|
||||||
|
|
||||||
|
var massj = bodyj.mass;
|
||||||
|
vxi -= dx * massj * mag;
|
||||||
|
vyi -= dy * massj * mag;
|
||||||
|
vzi -= dz * massj * mag;
|
||||||
|
|
||||||
|
var massi = bodyi.mass;
|
||||||
|
bodyj.vx += dx * massi * mag;
|
||||||
|
bodyj.vy += dy * massi * mag;
|
||||||
|
bodyj.vz += dz * massi * mag;
|
||||||
|
}
|
||||||
|
bodyi.vx = vxi;
|
||||||
|
bodyi.vy = vyi;
|
||||||
|
bodyi.vz = vzi;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var body = bodies[i];
|
||||||
|
body.x += dt * body.vx;
|
||||||
|
body.y += dt * body.vy;
|
||||||
|
body.z += dt * body.vz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function energy() {
|
||||||
|
var e = 0;
|
||||||
|
var size = bodies.length;
|
||||||
|
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var bodyi = bodies[i];
|
||||||
|
|
||||||
|
e += 0.5 * bodyi.mass * ( bodyi.vx * bodyi.vx +
|
||||||
|
bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz );
|
||||||
|
|
||||||
|
for (var j = i + 1; j < size; j++) {
|
||||||
|
var bodyj = bodies[j];
|
||||||
|
var dx = bodyi.x - bodyj.x;
|
||||||
|
var dy = bodyi.y - bodyj.y;
|
||||||
|
var dz = bodyi.z - bodyj.z;
|
||||||
|
|
||||||
|
var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
|
e -= (bodyi.mass * bodyj.mass) / distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = arg[0] || 1000000
|
||||||
|
|
||||||
|
offsetMomentum();
|
||||||
|
|
||||||
|
log.console(`n = ${n}`)
|
||||||
|
log.console(energy().toFixed(9))
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
advance(0.01);
|
||||||
|
log.console(energy().toFixed(9))
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
76
benchmarks/nota.ce
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
var nota = use('nota')
|
||||||
|
var os = use('os')
|
||||||
|
var io = use('io')
|
||||||
|
|
||||||
|
var ll = io.slurp('benchmarks/nota.json')
|
||||||
|
|
||||||
|
var newarr = []
|
||||||
|
var accstr = ""
|
||||||
|
for (var i = 0; i < 10000; i++) {
|
||||||
|
accstr += i;
|
||||||
|
newarr.push(i.toString())
|
||||||
|
}
|
||||||
|
// Arrays to store timing results
|
||||||
|
var jsonDecodeTimes = [];
|
||||||
|
var jsonEncodeTimes = [];
|
||||||
|
var notaEncodeTimes = [];
|
||||||
|
var notaDecodeTimes = [];
|
||||||
|
var notaSizes = [];
|
||||||
|
|
||||||
|
// Run 100 tests
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
// JSON Decode test
|
||||||
|
let start = os.now();
|
||||||
|
var jll = json.decode(ll);
|
||||||
|
jsonDecodeTimes.push((os.now() - start) * 1000);
|
||||||
|
|
||||||
|
// JSON Encode test
|
||||||
|
start = os.now();
|
||||||
|
let jsonStr = JSON.stringify(jll);
|
||||||
|
jsonEncodeTimes.push((os.now() - start) * 1000);
|
||||||
|
|
||||||
|
// NOTA Encode test
|
||||||
|
start = os.now();
|
||||||
|
var nll = nota.encode(jll);
|
||||||
|
notaEncodeTimes.push((os.now() - start) * 1000);
|
||||||
|
|
||||||
|
// NOTA Decode test
|
||||||
|
start = os.now();
|
||||||
|
var oll = nota.decode(nll);
|
||||||
|
notaDecodeTimes.push((os.now() - start) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate statistics
|
||||||
|
function getStats(arr) {
|
||||||
|
def avg = arr.reduce((a, b) => a + b) / arr.length;
|
||||||
|
def min = Math.min(...arr);
|
||||||
|
def max = Math.max(...arr);
|
||||||
|
return { avg, min, max };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty print results
|
||||||
|
log.console("\n== Performance Test Results (100 iterations) ==");
|
||||||
|
log.console("\nJSON Decoding (ms):");
|
||||||
|
def jsonDecStats = getStats(jsonDecodeTimes);
|
||||||
|
log.console(`Average: ${jsonDecStats.avg.toFixed(2)} ms`);
|
||||||
|
log.console(`Min: ${jsonDecStats.min.toFixed(2)} ms`);
|
||||||
|
log.console(`Max: ${jsonDecStats.max.toFixed(2)} ms`);
|
||||||
|
|
||||||
|
log.console("\nJSON Encoding (ms):");
|
||||||
|
def jsonEncStats = getStats(jsonEncodeTimes);
|
||||||
|
log.console(`Average: ${jsonEncStats.avg.toFixed(2)} ms`);
|
||||||
|
log.console(`Min: ${jsonEncStats.min.toFixed(2)} ms`);
|
||||||
|
log.console(`Max: ${jsonEncStats.max.toFixed(2)} ms`);
|
||||||
|
|
||||||
|
log.console("\nNOTA Encoding (ms):");
|
||||||
|
def notaEncStats = getStats(notaEncodeTimes);
|
||||||
|
log.console(`Average: ${notaEncStats.avg.toFixed(2)} ms`);
|
||||||
|
log.console(`Min: ${notaEncStats.min.toFixed(2)} ms`);
|
||||||
|
log.console(`Max: ${notaEncStats.max.toFixed(2)} ms`);
|
||||||
|
|
||||||
|
log.console("\nNOTA Decoding (ms):");
|
||||||
|
def notaDecStats = getStats(notaDecodeTimes);
|
||||||
|
log.console(`Average: ${notaDecStats.avg.toFixed(2)} ms`);
|
||||||
|
log.console(`Min: ${notaDecStats.min.toFixed(2)} ms`);
|
||||||
|
log.console(`Max: ${notaDecStats.max.toFixed(2)} ms`);
|
||||||
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
var nota = use('nota')
|
|
||||||
var os = use('os')
|
|
||||||
var io = use('io')
|
|
||||||
|
|
||||||
var ll = io.slurp('benchmarks/nota.json')
|
|
||||||
|
|
||||||
var newarr = []
|
|
||||||
var accstr = ""
|
|
||||||
for (var i = 0; i < 10000; i++) {
|
|
||||||
accstr += i;
|
|
||||||
newarr.push(i.toString())
|
|
||||||
}
|
|
||||||
// Arrays to store timing results
|
|
||||||
var jsonDecodeTimes = [];
|
|
||||||
var jsonEncodeTimes = [];
|
|
||||||
var notaEncodeTimes = [];
|
|
||||||
var notaDecodeTimes = [];
|
|
||||||
var notaSizes = [];
|
|
||||||
|
|
||||||
// Run 100 tests
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
// JSON Decode test
|
|
||||||
let start = os.now();
|
|
||||||
var jll = json.decode(ll);
|
|
||||||
jsonDecodeTimes.push((os.now() - start) * 1000);
|
|
||||||
|
|
||||||
// JSON Encode test
|
|
||||||
start = os.now();
|
|
||||||
let jsonStr = JSON.stringify(jll);
|
|
||||||
jsonEncodeTimes.push((os.now() - start) * 1000);
|
|
||||||
|
|
||||||
// NOTA Encode test
|
|
||||||
start = os.now();
|
|
||||||
var nll = nota.encode(jll);
|
|
||||||
notaEncodeTimes.push((os.now() - start) * 1000);
|
|
||||||
|
|
||||||
// NOTA Decode test
|
|
||||||
start = os.now();
|
|
||||||
var oll = nota.decode(nll);
|
|
||||||
notaDecodeTimes.push((os.now() - start) * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate statistics
|
|
||||||
function getStats(arr) {
|
|
||||||
const avg = arr.reduce((a, b) => a + b) / arr.length;
|
|
||||||
const min = Math.min(...arr);
|
|
||||||
const max = Math.max(...arr);
|
|
||||||
return { avg, min, max };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pretty print results
|
|
||||||
console.log("\n=== Performance Test Results (100 iterations) ===");
|
|
||||||
console.log("\nJSON Decoding (ms):");
|
|
||||||
const jsonDecStats = getStats(jsonDecodeTimes);
|
|
||||||
console.log(`Average: ${jsonDecStats.avg.toFixed(2)} ms`);
|
|
||||||
console.log(`Min: ${jsonDecStats.min.toFixed(2)} ms`);
|
|
||||||
console.log(`Max: ${jsonDecStats.max.toFixed(2)} ms`);
|
|
||||||
|
|
||||||
console.log("\nJSON Encoding (ms):");
|
|
||||||
const jsonEncStats = getStats(jsonEncodeTimes);
|
|
||||||
console.log(`Average: ${jsonEncStats.avg.toFixed(2)} ms`);
|
|
||||||
console.log(`Min: ${jsonEncStats.min.toFixed(2)} ms`);
|
|
||||||
console.log(`Max: ${jsonEncStats.max.toFixed(2)} ms`);
|
|
||||||
|
|
||||||
console.log("\nNOTA Encoding (ms):");
|
|
||||||
const notaEncStats = getStats(notaEncodeTimes);
|
|
||||||
console.log(`Average: ${notaEncStats.avg.toFixed(2)} ms`);
|
|
||||||
console.log(`Min: ${notaEncStats.min.toFixed(2)} ms`);
|
|
||||||
console.log(`Max: ${notaEncStats.max.toFixed(2)} ms`);
|
|
||||||
|
|
||||||
console.log("\nNOTA Decoding (ms):");
|
|
||||||
const notaDecStats = getStats(notaDecodeTimes);
|
|
||||||
console.log(`Average: ${notaDecStats.avg.toFixed(2)} ms`);
|
|
||||||
console.log(`Min: ${notaDecStats.min.toFixed(2)} ms`);
|
|
||||||
console.log(`Max: ${notaDecStats.max.toFixed(2)} ms`);
|
|
||||||
|
|
||||||
50
benchmarks/spectral-norm.ce
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
function A(i,j) {
|
||||||
|
return 1/((i+j)*(i+j+1)/2+i+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Au(u,v) {
|
||||||
|
for (var i=0; i<u.length; ++i) {
|
||||||
|
var t = 0;
|
||||||
|
for (var j=0; j<u.length; ++j)
|
||||||
|
t += A(i,j) * u[j];
|
||||||
|
|
||||||
|
v[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Atu(u,v) {
|
||||||
|
for (var i=0; i<u.length; ++i) {
|
||||||
|
var t = 0;
|
||||||
|
for (var j=0; j<u.length; ++j)
|
||||||
|
t += A(j,i) * u[j];
|
||||||
|
|
||||||
|
v[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AtAu(u,v,w) {
|
||||||
|
Au(u,w);
|
||||||
|
Atu(w,v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function spectralnorm(n) {
|
||||||
|
var i, u=[], v=[], w=[], vv=0, vBv=0;
|
||||||
|
for (i=0; i<n; ++i)
|
||||||
|
u[i] = 1; v[i] = w[i] = 0;
|
||||||
|
|
||||||
|
for (i=0; i<10; ++i) {
|
||||||
|
AtAu(u,v,w);
|
||||||
|
AtAu(v,u,w);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<n; ++i) {
|
||||||
|
vBv += u[i]*v[i];
|
||||||
|
vv += v[i]*v[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.sqrt(vBv/vv);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console(spectralnorm(arg[0]).toFixed(9));
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
@@ -36,7 +36,7 @@ function roundTripWota(value) {
|
|||||||
// iterations: how many times to loop
|
// iterations: how many times to loop
|
||||||
//
|
//
|
||||||
// You can tweak these as you like for heavier or lighter tests.
|
// You can tweak these as you like for heavier or lighter tests.
|
||||||
const benchmarks = [
|
def benchmarks = [
|
||||||
{
|
{
|
||||||
name: "Small Integers",
|
name: "Small Integers",
|
||||||
data: [0, 42, -1, 2023],
|
data: [0, 42, -1, 2023],
|
||||||
@@ -75,8 +75,8 @@ const benchmarks = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Print a header
|
// Print a header
|
||||||
console.log("Wota Encode/Decode Benchmark");
|
log.console("Wota Encode/Decode Benchmark");
|
||||||
console.log("============================\n");
|
log.console("===================\n");
|
||||||
|
|
||||||
// We'll run each benchmark scenario in turn.
|
// We'll run each benchmark scenario in turn.
|
||||||
for (let bench of benchmarks) {
|
for (let bench of benchmarks) {
|
||||||
@@ -96,11 +96,11 @@ for (let bench of benchmarks) {
|
|||||||
let elapsedSec = measureTime(runAllData, bench.iterations);
|
let elapsedSec = measureTime(runAllData, bench.iterations);
|
||||||
let opsPerSec = (totalIterations / elapsedSec).toFixed(1);
|
let opsPerSec = (totalIterations / elapsedSec).toFixed(1);
|
||||||
|
|
||||||
console.log(`${bench.name}:`);
|
log.console(`${bench.name}:`);
|
||||||
console.log(` Iterations: ${bench.iterations} × ${bench.data.length} data items = ${totalIterations}`);
|
log.console(` Iterations: ${bench.iterations} × ${bench.data.length} data items = ${totalIterations}`);
|
||||||
console.log(` Elapsed: ${elapsedSec.toFixed(3)} s`);
|
log.console(` Elapsed: ${elapsedSec.toFixed(3)} s`);
|
||||||
console.log(` Throughput: ${opsPerSec} encode+decode ops/sec\n`);
|
log.console(` Throughput: ${opsPerSec} encode+decode ops/sec\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All done
|
// All done
|
||||||
console.log("Benchmark completed.\n");
|
log.console("Benchmark completed.\n");
|
||||||
@@ -2,48 +2,64 @@
|
|||||||
// benchmark_wota_nota_json.js
|
// benchmark_wota_nota_json.js
|
||||||
//
|
//
|
||||||
// Usage in QuickJS:
|
// Usage in QuickJS:
|
||||||
// qjs benchmark_wota_nota_json.js
|
// qjs benchmark_wota_nota_json.js <LibraryName> <ScenarioName>
|
||||||
//
|
//
|
||||||
// Ensure wota, nota, json, and os are all available, e.g.:
|
// Ensure wota, nota, json, and os are all available, e.g.:
|
||||||
var wota = use('wota');
|
var wota = use('wota');
|
||||||
var nota = use('nota');
|
var nota = use('nota');
|
||||||
var json = use('json');
|
var json = use('json');
|
||||||
|
var jswota = use('jswota')
|
||||||
var os = use('os');
|
var os = use('os');
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// Parse command line arguments
|
||||||
|
if (arg.length != 2) {
|
||||||
|
log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
|
||||||
|
$_.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
var lib_name = arg[0];
|
||||||
|
var scenario_name = arg[1];
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 1. Setup "libraries" array to easily switch among Wota, Nota, and JSON
|
// 1. Setup "libraries" array to easily switch among wota, nota, and json
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const libraries = [
|
def libraries = [
|
||||||
{
|
{
|
||||||
name: "Wota",
|
name: "wota",
|
||||||
encode: wota.encode,
|
encode: wota.encode,
|
||||||
decode: wota.decode,
|
decode: wota.decode,
|
||||||
// Wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
// wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
||||||
getSize(encoded) {
|
getSize(encoded) {
|
||||||
return encoded.byteLength;
|
return encoded.length;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Nota",
|
name: "nota",
|
||||||
encode: nota.encode,
|
encode: nota.encode,
|
||||||
decode: nota.decode,
|
decode: nota.decode,
|
||||||
// Nota also produces an ArrayBuffer:
|
// nota also produces an ArrayBuffer:
|
||||||
getSize(encoded) {
|
getSize(encoded) {
|
||||||
return encoded.byteLength;
|
return encoded.length;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "JSON",
|
name: "json",
|
||||||
encode: json.encode,
|
encode: json.encode,
|
||||||
decode: json.decode,
|
decode: json.decode,
|
||||||
// JSON produces a JS string. We'll measure its UTF-16 code unit length
|
// json produces a JS string. We'll measure its UTF-16 code unit length
|
||||||
// as a rough "size". Alternatively, you could convert to UTF-8 for
|
// as a rough "size". Alternatively, you could convert to UTF-8 for
|
||||||
// a more accurate byte size. Here we just use `string.length`.
|
// a more accurate byte size. Here we just use `string.length`.
|
||||||
getSize(encodedStr) {
|
getSize(encodedStr) {
|
||||||
return encodedStr.length;
|
return encodedStr.length;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jswota",
|
||||||
|
encode: jswota.encode,
|
||||||
|
decode: jswota.decode,
|
||||||
|
getSize(encoded) { return encoded.length }
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -52,24 +68,29 @@ const libraries = [
|
|||||||
// Each scenario has { name, data, iterations }
|
// Each scenario has { name, data, iterations }
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const benchmarks = [
|
def benchmarks = [
|
||||||
{
|
{
|
||||||
name: "Small Integers",
|
name: "empty",
|
||||||
|
data: [{}, {}, {}, {}],
|
||||||
|
iterations: 10000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "integers",
|
||||||
data: [0, 42, -1, 2023],
|
data: [0, 42, -1, 2023],
|
||||||
iterations: 100000
|
iterations: 100000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Floating point",
|
name: "floats",
|
||||||
data: [0.1, 1e-50, 3.14159265359],
|
data: [0.1, 1e-50, 3.14159265359],
|
||||||
iterations: 100000
|
iterations: 100000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Strings (short, emoji)",
|
name: "strings",
|
||||||
data: ["Hello, Wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
||||||
iterations: 100000
|
iterations: 100000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Small Objects",
|
name: "objects",
|
||||||
data: [
|
data: [
|
||||||
{ a:1, b:2.2, c:"3", d:false },
|
{ a:1, b:2.2, c:"3", d:false },
|
||||||
{ x:42, y:null, z:"test" }
|
{ x:42, y:null, z:"test" }
|
||||||
@@ -77,20 +98,15 @@ const benchmarks = [
|
|||||||
iterations: 50000
|
iterations: 50000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Nested Arrays",
|
name: "nested",
|
||||||
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
|
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
|
||||||
iterations: 50000
|
iterations: 50000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Large Array (1k integers)",
|
name: "large_array",
|
||||||
data: [ Array.from({length:1000}, (_, i) => i) ],
|
data: [ Array.from({length:1000}, (_, i) => i) ],
|
||||||
iterations: 1000
|
iterations: 1000
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Large Binary Blob (256KB)",
|
|
||||||
data: [ new Uint8Array(256 * 1024).buffer ],
|
|
||||||
iterations: 200
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -108,7 +124,7 @@ function measureTime(fn) {
|
|||||||
// 4. For each library, we run each benchmark scenario and measure:
|
// 4. For each library, we run each benchmark scenario and measure:
|
||||||
// - Encoding time (seconds)
|
// - Encoding time (seconds)
|
||||||
// - Decoding time (seconds)
|
// - Decoding time (seconds)
|
||||||
// - Total encoded size (bytes or code units for JSON)
|
// - Total encoded size (bytes or code units for json)
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -125,11 +141,11 @@ function runBenchmarkForLibrary(lib, bench) {
|
|||||||
let encodeTime = measureTime(() => {
|
let encodeTime = measureTime(() => {
|
||||||
for (let i = 0; i < bench.iterations; i++) {
|
for (let i = 0; i < bench.iterations; i++) {
|
||||||
// For each data item, encode it
|
// For each data item, encode it
|
||||||
for (let d of bench.data) {
|
for (let j = 0; j < bench.data.length; j++) {
|
||||||
let e = lib.encode(d);
|
let e = lib.encode(bench.data[j]);
|
||||||
// store only in the very first iteration, so we can decode them later
|
// store only in the very first iteration, so we can decode them later
|
||||||
// but do not store them every iteration or we blow up memory.
|
// but do not store them every iteration or we blow up memory.
|
||||||
if (i === 0) {
|
if (i == 0) {
|
||||||
encodedList.push(e);
|
encodedList.push(e);
|
||||||
totalSize += lib.getSize(e);
|
totalSize += lib.getSize(e);
|
||||||
}
|
}
|
||||||
@@ -152,31 +168,43 @@ function runBenchmarkForLibrary(lib, bench) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// 5. Main driver: run across all benchmarks, for each library.
|
// 5. Main driver: run only the specified library and scenario
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
console.log("Benchmark: Wota vs Nota vs JSON");
|
// Find the requested library and scenario
|
||||||
console.log("================================\n");
|
var lib = libraries.find(l => l.name == lib_name);
|
||||||
|
var bench = benchmarks.find(b => b.name == scenario_name);
|
||||||
|
|
||||||
for (let bench of benchmarks) {
|
if (!lib) {
|
||||||
console.log(`SCENARIO: ${bench.name}`);
|
log.console('Unknown library:', lib_name);
|
||||||
console.log(` Data length: ${bench.data.length} | Iterations: ${bench.iterations}\n`);
|
log.console('Available libraries:', libraries.map(l => l.name).join(', '));
|
||||||
|
$_.stop()
|
||||||
for (let lib of libraries) {
|
|
||||||
let { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
|
|
||||||
|
|
||||||
// We'll compute total operations = bench.iterations * bench.data.length
|
|
||||||
let totalOps = bench.iterations * bench.data.length;
|
|
||||||
let encOpsPerSec = (totalOps / encodeTime).toFixed(1);
|
|
||||||
let decOpsPerSec = (totalOps / decodeTime).toFixed(1);
|
|
||||||
|
|
||||||
console.log(` ${lib.name}:`);
|
|
||||||
console.log(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec`);
|
|
||||||
console.log(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec`);
|
|
||||||
console.log(` Total size: ${totalSize} bytes (or code units for JSON)`);
|
|
||||||
console.log("");
|
|
||||||
}
|
|
||||||
console.log("---------------------------------------------------------\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Benchmark complete.\n");
|
if (!bench) {
|
||||||
|
log.console('Unknown scenario:', scenario_name);
|
||||||
|
log.console('Available scenarios:', benchmarks.map(b => b.name).join(', '));
|
||||||
|
$_.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the benchmark for this library/scenario combination
|
||||||
|
var { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
|
||||||
|
|
||||||
|
// Output json for easy parsing by hyperfine or other tools
|
||||||
|
var totalOps = bench.iterations * bench.data.length;
|
||||||
|
var result = {
|
||||||
|
lib: lib_name,
|
||||||
|
scenario: scenario_name,
|
||||||
|
encodeTime: encodeTime,
|
||||||
|
decodeTime: decodeTime,
|
||||||
|
totalSize: totalSize,
|
||||||
|
totalOps: totalOps,
|
||||||
|
encodeOpsPerSec: totalOps / encodeTime,
|
||||||
|
decodeOpsPerSec: totalOps / decodeTime,
|
||||||
|
encodeNsPerOp: (encodeTime / totalOps) * 1e9,
|
||||||
|
decodeNsPerOp: (decodeTime / totalOps) * 1e9
|
||||||
|
};
|
||||||
|
|
||||||
|
log.console(json.encode(result));
|
||||||
|
|
||||||
|
$_.stop()
|
||||||
164
cell.md
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
JAVASCRIPT VISION
|
||||||
|
|
||||||
|
I see objects as being a sort of combination of a lisp cell and a record: symbols, which are used internally, and are private and non iterable, and record string values, which are iterable, readable, and writable; of course everything becomes locked in when stone.
|
||||||
|
|
||||||
|
CELLSCRIPT
|
||||||
|
|
||||||
|
Javascript to its core. Objects. What does the language need? It can be quite small, I think. The key is, ANYTHING that we want to be fast and JIT'd, must be present. So, record lookups. These are actually quicker in a jit'd language that have them as a feature. Most things should be libraries. Blobs need to be in the runtime.
|
||||||
|
|
||||||
|
## Actors and Objects
|
||||||
|
Actors have a unique memory space and are made up of many objects. Objects are created in the Self style, but with a limitation: only one parent.
|
||||||
|
|
||||||
|
Actors only communicate with messages. Messages are a record of data consisting of a few base types: text, numbers, arrays, records, boolean values. There is no RPC, and it is not recommended to build it into your message passing protocol. Messages are very high level things: "do X", which the actor can then go and carry out.
|
||||||
|
|
||||||
|
Cell provides a fast way to condense an object for sending.
|
||||||
|
|
||||||
|
## How is it different from Javascript?
|
||||||
|
Cell condenses Javascript down into a few core ideas. There are three pillars which cell relies on:
|
||||||
|
|
||||||
|
1. The idea of actors as a method of communication between parts of a program.
|
||||||
|
2. The idea of objects as a way to organize and encapsulate data.
|
||||||
|
3. The idea of the capability model as security.
|
||||||
|
|
||||||
|
Javascript already supplied some of these things; Cell takes the core of Javascript and makes these ideas more explicit, and layers on the actor communication. It removes some goofy suckiness with javascript.
|
||||||
|
|
||||||
|
It acts as something like an operating system at the application level. It allows random code to be ran on your machine without worrying it will break something. This is built into the language.
|
||||||
|
|
||||||
|
It is completly dynamically typed. In comparison with C, in C, you can treat everything as everything: it is almost not typed at all. If you try to use a type as another type, no error is thrown; it might work, but it mightly silently not work. In Cell, data has a hard type, but if you use it "incorrectly", it will throw, and you can correct it. It's a live system.
|
||||||
|
|
||||||
|
Cell is linked very closely with C. It's best to think of cell as a layer for message passing on top of C. It is a way to describe how to translate C tasks from one section of the program to another - or to totally different computers (actors).
|
||||||
|
|
||||||
|
As such, cell's primary duty is marshalling data; so it has been designed for that to be as fast as possible. It has a syntax similar to C to make it easy to translate formulae from cell to C (or the other way, if desired).
|
||||||
|
|
||||||
|
Unlike many actor languages, Cell does not eschew assignment. You must have some assignment. However, when it comes to actor->actor communication, you do not assign. RPC is too direct: one actor should not care all that much what specific functions another actor has available. It should request it to do something, and get a result, or possibly not get a result. It doesn't care what the actor does as long as that gets done.
|
||||||
|
|
||||||
|
But within itself, it will assign; it must. Actors, or cells, are best thought of as computers or nodes within the internet. You request data from a URL by typing it into your browser; that computer you're attempting to reach may not even be on. It very likely has written some other data to disk whenever you contact it. But you're not doing the specific assigning. You just request data with HTTP commands.
|
||||||
|
|
||||||
|
## Objects and actors
|
||||||
|
Objects and actors are both similar ideas: they can hold data and respond to messages. Objects, local to an actor, can be thought of more like an RPC idea: they're invoked and return immediately. However, a failed RPC can crash an object; and in that case, the actor halts. It can be corrected.
|
||||||
|
|
||||||
|
## What does Cell bring you over C?
|
||||||
|
Programs which are built with C; they're built statically; they're built to not crash; they're built doing extremely low level things, like assignment.
|
||||||
|
|
||||||
|
The goal of cell is to thrust your C code into the parallel, actor realm. It lets your code crash and resume it; even rewriting the C code which is butressing your cell code and reloading it live.
|
||||||
|
|
||||||
|
There are two primary sorts of Cell modules you create from C code: data and IO. C code like
|
||||||
|
|
||||||
|
Where there were two similar things in javscript, one has been deleted and one kept. For example, there is only null now, no undefined. There are not four ways to test for equality; there is one.
|
||||||
|
|
||||||
|
The purpose of this is to be a great language for passing messages. So it should be fast at creating records first and foremost, and finding items on them. So it needs first class, jitt'd records.
|
||||||
|
|
||||||
|
Finally, it needs to use less memory. Deleting a bunch of this stuff should make that simpler.
|
||||||
|
|
||||||
|
What is present?
|
||||||
|
Objects, prototypes, numbers, arrays, strings, true, false, null.
|
||||||
|
|
||||||
|
Things to do:
|
||||||
|
|
||||||
|
merge typeof and instanceof. Misty has array? stone? number? etc; it needs to be generic. 5 is number returns true.
|
||||||
|
|
||||||
|
No new operator. It's the same idea though: simply instead of 'var guy = new sprite({x,y})' you would say 'var guy = sprite({x,y})', and sprite would simply be a function written to return a sprite object.
|
||||||
|
|
||||||
|
One number type. Dec64. Numeric stack can be added in later: a bigint library, for example, built inside cell.
|
||||||
|
|
||||||
|
Simplify the property attributes stuff. It is simple: objects have text keys and whatever values. Objects can also have objects as values. These work like symbols. You can share them, if desired. No well known symbols exist to eliminate that much misdirection. Obejcts basically work like private keys. If you serialize an object, objects that are keys are not serialized; only textual keys are. You can do something about it with a json() method that is invoked, if you desire. You cannot retrieve
|
||||||
|
|
||||||
|
var works like let; use var instead of let
|
||||||
|
|
||||||
|
no const
|
||||||
|
|
||||||
|
Function closures and _ => all work the same and close over the 'this' variable
|
||||||
|
|
||||||
|
Totally delete modules, coroutines, generators, proxy .. this deletes a lot of the big switch statement
|
||||||
|
|
||||||
|
Add the 'go' statement for tail calls
|
||||||
|
|
||||||
|
Add the 'do' statement
|
||||||
|
|
||||||
|
Implementation detail: separate out arrays and objects. They are not the same. Objects no longer need to track if they're fast arrays or not. They're not. Arrays are. Always.
|
||||||
|
|
||||||
|
Add the functional proxy idea. Log will be implemented through that.
|
||||||
|
|
||||||
|
Remove ===; it's just == now, and !=.
|
||||||
|
|
||||||
|
Remove 'continue'; now, break handles both. For a do statement, label it, and break to that label; so
|
||||||
|
|
||||||
|
var x = 0
|
||||||
|
do loop {
|
||||||
|
x++
|
||||||
|
if (x < 5) break loop // goes back to loop
|
||||||
|
break // exits loop
|
||||||
|
}
|
||||||
|
|
||||||
|
rename instanceof to 'is'
|
||||||
|
|
||||||
|
remove undefined; all are 'null' now
|
||||||
|
|
||||||
|
remove 'delete'; to remove a field, assign it to null
|
||||||
|
|
||||||
|
remove with
|
||||||
|
|
||||||
|
Remove Object. New records have a prototype of nothing. There are no more 'type prototypes' at all.
|
||||||
|
|
||||||
|
Arrays are their own type
|
||||||
|
|
||||||
|
Remove property descriptors. Properties are always settable, unless the object as a whole is stone. Stone is an object property instead of a shape property.
|
||||||
|
|
||||||
|
Syntax stuff .. would like to invoke functions without (). This can effectively simulate a "getter". Make ? and all other characters usable for names. No reserve words, which are endlessly irritating.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
This will all actually come about gradually. Add a few things at a time, fix up code that did not adhere. For a lot of this, no new functions will even need to be written; it's a matter of not calling certain functions that are no longer relevant, or calling different functions when required.
|
||||||
|
|
||||||
|
|
||||||
|
## Benchmarks to implement
|
||||||
|
### general speed
|
||||||
|
binarytrees
|
||||||
|
coro-prime-sieve
|
||||||
|
edigits
|
||||||
|
fannkuch-redux
|
||||||
|
fasta
|
||||||
|
http-server
|
||||||
|
json serialize/deserialize
|
||||||
|
knucleotide
|
||||||
|
lru
|
||||||
|
mandelbrot
|
||||||
|
merkletrees
|
||||||
|
nbody
|
||||||
|
nsieve
|
||||||
|
pidigits
|
||||||
|
regex-redux
|
||||||
|
secp256k1
|
||||||
|
spectral-norm
|
||||||
|
|
||||||
|
### function calling and recursion stress - test goto
|
||||||
|
naive recursive fibonacci [fib(35) or fib(40)]
|
||||||
|
tak
|
||||||
|
ackermann
|
||||||
|
|
||||||
|
### numeric
|
||||||
|
sieve of eratosthenes [10^7 bits]
|
||||||
|
spectral norm [5500 x 5500 matrix]
|
||||||
|
n-body sim [50 000 - 100 000 steps]
|
||||||
|
mandelbrot [1600x1200 image, max iter = 50]
|
||||||
|
|
||||||
|
### memory & gc torture
|
||||||
|
binary trees [depth 18 (~500 000 nodes)]
|
||||||
|
richards task scheduler
|
||||||
|
fannkuch redux [n=11 or 12]
|
||||||
|
|
||||||
|
### dynamic object & property access
|
||||||
|
deltablue constraint solver
|
||||||
|
splay tree [256k nodes]
|
||||||
|
json, wota, nota decode->encode [use 2MB example]
|
||||||
|
|
||||||
|
### string / regex kernels
|
||||||
|
regex-DNA
|
||||||
|
fasta
|
||||||
|
word-frequency
|
||||||
|
|
||||||
|
### concurrency/message passing
|
||||||
|
ping-pong [two actors exhange a small record N times, 1M messages end to end]
|
||||||
|
chameneos [mating color swap game w/ randezvous]
|
||||||
|
|
||||||
|
For all, track memory and time.
|
||||||
@@ -47,7 +47,7 @@ Certain functions are intrinsic to the program and cannot be overridden. They’
|
|||||||
- **Example**:
|
- **Example**:
|
||||||
```js
|
```js
|
||||||
this.delay(_ => {
|
this.delay(_ => {
|
||||||
console.log("3 seconds later!")
|
log.console("3 seconds later!")
|
||||||
}, 3)
|
}, 3)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
Provides a consistent way to create documentation for prosperon elements. Objects are documented by adding docstrings directly to object-like things (functions, objects, ...), or to an object's own "doc object".
|
Provides a consistent way to create documentation for prosperon elements. Objects are documented by adding docstrings directly to object-like things (functions, objects, ...), or to an object's own "doc object".
|
||||||
|
|
||||||
Docstrings are set to the symbol `prosperon.DOC`
|
Docstrings are set to the symbol `cell.DOC`
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Suppose we have a module that returns a function
|
// Suppose we have a module that returns a function
|
||||||
function greet(name) { console.log("Hello, " + name) }
|
function greet(name) { log.console("Hello, " + name) }
|
||||||
|
|
||||||
// We can attach a docstring
|
// We can attach a docstring
|
||||||
greet.doc = `
|
greet.doc = `
|
||||||
@@ -21,12 +21,12 @@ return greet
|
|||||||
```js
|
```js
|
||||||
// Another way is to add a docstring object to an object
|
// Another way is to add a docstring object to an object
|
||||||
var greet = {
|
var greet = {
|
||||||
hello() { console.log('hello!') }
|
hello() { log.console('hello!') }
|
||||||
}
|
}
|
||||||
|
|
||||||
greet[prosperon.DOC] = {}
|
greet[cell.DOC] = {}
|
||||||
greet[prosperon.DOC][prosperon.DOC] = 'An object full of different greeter functions'
|
greet[cell.DOC][cell.DOC] = 'An object full of different greeter functions'
|
||||||
greet[prosperon.DOC].hello = 'A greeter that says, "hello!"'
|
greet[cell.DOC].hello = 'A greeter that says, "hello!"'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ return {
|
|||||||
This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following:
|
This will cause prosperon to launch a 500x500 window with the title 'Hello World'. In your ```main.js```, write the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
console.log("Hello world")
|
log.console("Hello world")
|
||||||
|
|
||||||
this.delay(_ => {
|
this.delay(_ => {
|
||||||
this.kill();
|
this.kill();
|
||||||
@@ -62,6 +62,6 @@ this.delay(_ => {
|
|||||||
The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section.
|
The global object called `prosperon` has a variety of engine specific settings on it that can be set to influence how the engine behaves. For example, `prosperon.argv` contains a list of the command line arguments given to prosperon; `prosperon.PATH` is an array of paths to resolve resources such as modules and images. `prosperon` is fully documented in the API section.
|
||||||
|
|
||||||
## Getting help
|
## Getting help
|
||||||
The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `prosperon.doc(prosperon)` in your `main.js`.
|
The `prosperon` global has a 'doc' function, which can be invoked on any engine object to see a description of it and its members. For example, to learn about `prosperon`, try printing out `cell.DOC(prosperon)` in your `main.js`.
|
||||||
|
|
||||||
Writing documentation for your own modules and game components will be explored in the chapter on actors & modules.
|
Writing documentation for your own modules and game components will be explored in the chapter on actors & modules.
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
var CELLS = Symbol()
|
|
||||||
|
|
||||||
var key = function key(x,y) { return `${x},${y}` }
|
|
||||||
|
|
||||||
function grid(w, h)
|
|
||||||
{
|
|
||||||
this[CELLS] = new Map()
|
|
||||||
this.width = w;
|
|
||||||
this.height = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
grid.prototype = {
|
|
||||||
cell(x,y) {
|
|
||||||
var k = key(x,y)
|
|
||||||
if (!this[CELLS].has(k)) this[CELLS].set(k,[])
|
|
||||||
return this[CELLS].get(k)
|
|
||||||
},
|
|
||||||
|
|
||||||
add(entity, pos) {
|
|
||||||
this.cell(pos.x, pos.y).push(entity);
|
|
||||||
entity.coord = pos.slice();
|
|
||||||
},
|
|
||||||
|
|
||||||
remove(entity, pos) {
|
|
||||||
var c = this.cell(pos.x, pos.y);
|
|
||||||
c.splice(c.indexOf(entity), 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
at(pos) {
|
|
||||||
return this.cell(pos.x, pos.y);
|
|
||||||
},
|
|
||||||
|
|
||||||
inBounds(pos) {
|
|
||||||
return pos.x >= 0 && pos.x < this.width && pos.y >= 0 && pos.y < this.height;
|
|
||||||
},
|
|
||||||
|
|
||||||
each(fn) {
|
|
||||||
for (var [k, list] of this[CELLS])
|
|
||||||
for (var p of list) fn(p, p.coord);
|
|
||||||
},
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
var out = `grid [${this.width}x${this.height}]
|
|
||||||
`
|
|
||||||
for (var y = 0; y < this.height; y++) {
|
|
||||||
for (var x = 0; x < this.width; x++) {
|
|
||||||
var cell = this.at([x,y]);
|
|
||||||
out += cell.length
|
|
||||||
}
|
|
||||||
if (y !== this.height - 1) out += "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return grid
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/* helper – robust coord access */
|
|
||||||
function cx(c) { return (c.x !== undefined) ? c.x : c[0]; }
|
|
||||||
function cy(c) { return (c.y !== undefined) ? c.y : c[1]; }
|
|
||||||
|
|
||||||
/* simple move-shape checks */
|
|
||||||
var deltas = {
|
|
||||||
pawn: function (pc, dx, dy, grid, to) {
|
|
||||||
var dir = (pc.colour === 'white') ? -1 : 1;
|
|
||||||
var base = (pc.colour === 'white') ? 6 : 1;
|
|
||||||
var one = (dy === dir && dx === 0 && grid.at(to).length === 0);
|
|
||||||
var two = (dy === 2 * dir && dx === 0 && cy(pc.coord) === base &&
|
|
||||||
grid.at({ x: cx(pc.coord), y: cy(pc.coord)+dir }).length === 0 &&
|
|
||||||
grid.at(to).length === 0);
|
|
||||||
var cap = (dy === dir && Math.abs(dx) === 1 && grid.at(to).length);
|
|
||||||
return one || two || cap;
|
|
||||||
},
|
|
||||||
rook : function (pc, dx, dy) { return (dx === 0 || dy === 0); },
|
|
||||||
bishop: function (pc, dx, dy) { return Math.abs(dx) === Math.abs(dy); },
|
|
||||||
queen : function (pc, dx, dy) { return (dx === 0 || dy === 0 || Math.abs(dx) === Math.abs(dy)); },
|
|
||||||
knight: function (pc, dx, dy) { return (Math.abs(dx) === 1 && Math.abs(dy) === 2) ||
|
|
||||||
(Math.abs(dx) === 2 && Math.abs(dy) === 1); },
|
|
||||||
king : function (pc, dx, dy) { return Math.max(Math.abs(dx), Math.abs(dy)) === 1; }
|
|
||||||
};
|
|
||||||
|
|
||||||
function clearLine(from, to, grid) {
|
|
||||||
var dx = Math.sign(cx(to) - cx(from));
|
|
||||||
var dy = Math.sign(cy(to) - cy(from));
|
|
||||||
var x = cx(from) + dx, y = cy(from) + dy;
|
|
||||||
while (x !== cx(to) || y !== cy(to)) {
|
|
||||||
if (grid.at({ x: x, y: y }).length) return false;
|
|
||||||
x += dx; y += dy;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function canMove(piece, from, to, grid) {
|
|
||||||
var dx = cx(to) - cx(from);
|
|
||||||
var dy = cy(to) - cy(from);
|
|
||||||
var f = deltas[piece.kind];
|
|
||||||
if (!f || !f(piece, dx, dy, grid, to)) return false;
|
|
||||||
if (piece.kind === 'knight') return true;
|
|
||||||
return clearLine(from, to, grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { canMove };
|
|
||||||
@@ -18,7 +18,7 @@ var state = {
|
|||||||
|
|
||||||
// Helper to calculate progress percentage
|
// Helper to calculate progress percentage
|
||||||
function get_progress() {
|
function get_progress() {
|
||||||
if (state.total_bytes === 0) {
|
if (state.total_bytes == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return Math.round((state.downloaded_bytes / state.total_bytes) * 100);
|
return Math.round((state.downloaded_bytes / state.total_bytes) * 100);
|
||||||
@@ -101,7 +101,7 @@ $_.receiver(function(msg) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'status':
|
case 'status':
|
||||||
console.log(`got status request. current is ${json.encode(get_status())}`)
|
log.console(`got status request. current is ${json.encode(get_status())}`)
|
||||||
send(msg, {
|
send(msg, {
|
||||||
type: 'status_response',
|
type: 'status_response',
|
||||||
...get_status()
|
...get_status()
|
||||||
@@ -150,7 +150,7 @@ function read_next_chunk() {
|
|||||||
try {
|
try {
|
||||||
var chunk = http.fetch_read_chunk(state.connection);
|
var chunk = http.fetch_read_chunk(state.connection);
|
||||||
|
|
||||||
if (chunk === null) {
|
if (chunk == null) {
|
||||||
// Download complete
|
// Download complete
|
||||||
finish_download();
|
finish_download();
|
||||||
return;
|
return;
|
||||||
@@ -8,22 +8,22 @@ var waiting_client = null;
|
|||||||
var match_id = 0;
|
var match_id = 0;
|
||||||
|
|
||||||
$_.portal(e => {
|
$_.portal(e => {
|
||||||
console.log("NAT server: received connection request");
|
log.console("NAT server: received connection request");
|
||||||
|
|
||||||
if (!is_actor(e.actor))
|
if (!is_actor(e.actor))
|
||||||
send(e, {reason: "Must provide the actor you want to connect."});
|
send(e, {reason: "Must provide the actor you want to connect."});
|
||||||
|
|
||||||
if (waiting_client) {
|
if (waiting_client) {
|
||||||
console.log(`sending out messages! to ${json.encode(e.actor)} and ${json.encode(waiting_client.actor)}`)
|
log.console(`sending out messages! to ${json.encode(e.actor)} and ${json.encode(waiting_client.actor)}`)
|
||||||
send(waiting_client, e.actor)
|
send(waiting_client, e.actor)
|
||||||
send(e, waiting_client.actor)
|
send(e, waiting_client.actor)
|
||||||
|
|
||||||
waiting_client = undefined
|
waiting_client = null
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
waiting_client = e
|
waiting_client = e
|
||||||
|
|
||||||
console.log(`actor ${json.encode(e.actor)} is waiting ...`)
|
log.console(`actor ${json.encode(e.actor)} is waiting ...`)
|
||||||
}, 4000);
|
}, 4000);
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
console.log(`nat client starting`)
|
log.console(`nat client starting`)
|
||||||
|
|
||||||
$_.contact((actor, reason) => {
|
$_.contact((actor, reason) => {
|
||||||
if (actor) {
|
if (actor) {
|
||||||
console.log(`trying to message ${json.encode(actor)}`)
|
log.console(`trying to message ${json.encode(actor)}`)
|
||||||
send(actor, {type:"greet"})
|
send(actor, {type:"greet"})
|
||||||
} else {
|
} else {
|
||||||
console.log(json.encode(reason))
|
log.console(json.encode(reason))
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
address: "108.210.60.32", // NAT server's public IP
|
address: "108.210.60.32", // NAT server's public IP
|
||||||
@@ -16,7 +16,7 @@ $_.contact((actor, reason) => {
|
|||||||
$_.receiver(e => {
|
$_.receiver(e => {
|
||||||
switch(e.type) {
|
switch(e.type) {
|
||||||
case 'greet':
|
case 'greet':
|
||||||
console.log(`hello!`)
|
log.console(`hello!`)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
254
meson.build
@@ -1,4 +1,4 @@
|
|||||||
project('prosperon', ['c', 'cpp'],
|
project('cell', ['c', 'cpp'],
|
||||||
version: '0.9.3',
|
version: '0.9.3',
|
||||||
meson_version: '>=1.4',
|
meson_version: '>=1.4',
|
||||||
default_options : [ 'cpp_std=c++11'])
|
default_options : [ 'cpp_std=c++11'])
|
||||||
@@ -13,21 +13,21 @@ fs = import('fs')
|
|||||||
add_project_arguments('-pedantic', language: ['c'])
|
add_project_arguments('-pedantic', language: ['c'])
|
||||||
|
|
||||||
git_tag_cmd = run_command('git', 'describe', '--tags', '--abbrev=0', check: false)
|
git_tag_cmd = run_command('git', 'describe', '--tags', '--abbrev=0', check: false)
|
||||||
prosperon_version = 'unknown'
|
cell_version = 'unknown'
|
||||||
if git_tag_cmd.returncode() == 0
|
if git_tag_cmd.returncode() == 0
|
||||||
prosperon_version = git_tag_cmd.stdout().strip()
|
cell_version = git_tag_cmd.stdout().strip()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
git_commit_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', check: false)
|
git_commit_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', check: false)
|
||||||
prosperon_commit = 'unknown'
|
cell_commit = 'unknown'
|
||||||
if git_commit_cmd.returncode() == 0
|
if git_commit_cmd.returncode() == 0
|
||||||
prosperon_commit = git_commit_cmd.stdout().strip()
|
cell_commit = git_commit_cmd.stdout().strip()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Important: pass the definitions without double-escaping quotes
|
# Important: pass the definitions without double-escaping quotes
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
'-DPROSPERON_VERSION="' + prosperon_version + '"',
|
'-DCELL_VERSION="' + cell_version + '"',
|
||||||
'-DPROSPERON_COMMIT="' + prosperon_commit + '"',
|
'-DCELL_COMMIT="' + cell_commit + '"',
|
||||||
language : 'c'
|
language : 'c'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -67,21 +67,31 @@ endif
|
|||||||
|
|
||||||
cmake = import('cmake')
|
cmake = import('cmake')
|
||||||
|
|
||||||
mbedtls_opts = cmake.subproject_options()
|
# Try to find system-installed mbedtls first
|
||||||
mbedtls_opts.add_cmake_defines({
|
mbedtls_dep = dependency('mbedtls', static: true, required: false)
|
||||||
'ENABLE_PROGRAMS': 'OFF', # Disable Mbed TLS programs
|
mbedx509_dep = dependency('mbedx509', static: true, required: false)
|
||||||
'ENABLE_TESTING': 'OFF', # Disable Mbed TLS tests
|
mbedcrypto_dep = dependency('mbedcrypto', static: true, required: false)
|
||||||
'CMAKE_BUILD_TYPE': 'Release', # Optimize for release
|
|
||||||
'MBEDTLS_FATAL_WARNINGS': 'ON', # Treat warnings as errors
|
if not mbedtls_dep.found() or not mbedx509_dep.found() or not mbedcrypto_dep.found()
|
||||||
'USE_STATIC_MBEDTLS_LIBRARY': 'ON',# Build static libraries
|
message('⚙ System mbedtls not found, building subproject...')
|
||||||
'USE_SHARED_MBEDTLS_LIBRARY': 'OFF'# Disable shared libraries
|
mbedtls_opts = cmake.subproject_options()
|
||||||
})
|
mbedtls_opts.add_cmake_defines({
|
||||||
mbedtls_proj = cmake.subproject('mbedtls', options: mbedtls_opts)
|
'ENABLE_PROGRAMS': 'OFF', # Disable Mbed TLS programs
|
||||||
deps += [
|
'ENABLE_TESTING': 'OFF', # Disable Mbed TLS tests
|
||||||
mbedtls_proj.dependency('mbedtls'),
|
'CMAKE_BUILD_TYPE': 'Release', # Optimize for release
|
||||||
mbedtls_proj.dependency('mbedx509'),
|
'MBEDTLS_FATAL_WARNINGS': 'ON', # Treat warnings as errors
|
||||||
mbedtls_proj.dependency('mbedcrypto')
|
'USE_STATIC_MBEDTLS_LIBRARY': 'ON',# Build static libraries
|
||||||
]
|
'USE_SHARED_MBEDTLS_LIBRARY': 'OFF'# Disable shared libraries
|
||||||
|
})
|
||||||
|
mbedtls_proj = cmake.subproject('mbedtls', options: mbedtls_opts)
|
||||||
|
deps += [
|
||||||
|
mbedtls_proj.dependency('mbedtls'),
|
||||||
|
mbedtls_proj.dependency('mbedx509'),
|
||||||
|
mbedtls_proj.dependency('mbedcrypto')
|
||||||
|
]
|
||||||
|
else
|
||||||
|
deps += [mbedtls_dep, mbedx509_dep, mbedcrypto_dep]
|
||||||
|
endif
|
||||||
|
|
||||||
sdl3_opts = cmake.subproject_options()
|
sdl3_opts = cmake.subproject_options()
|
||||||
sdl3_opts.add_cmake_defines({
|
sdl3_opts.add_cmake_defines({
|
||||||
@@ -105,11 +115,20 @@ endif
|
|||||||
if host_machine.system() == 'linux'
|
if host_machine.system() == 'linux'
|
||||||
deps += cc.find_library('asound', required:true)
|
deps += cc.find_library('asound', required:true)
|
||||||
deps += [dependency('x11'), dependency('xi'), dependency('xcursor'), dependency('egl'), dependency('gl')]
|
deps += [dependency('x11'), dependency('xi'), dependency('xcursor'), dependency('egl'), dependency('gl')]
|
||||||
|
deps += cc.find_library('blas', required:true)
|
||||||
|
deps += cc.find_library('lapack', required:true)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
deps += cc.find_library('d3d11')
|
deps += cc.find_library('d3d11')
|
||||||
deps += cc.find_library('ws2_32', required:true)
|
deps += cc.find_library('ws2_32', required:true)
|
||||||
|
# For Windows, you may need to install OpenBLAS or Intel MKL
|
||||||
|
# and adjust these library names accordingly
|
||||||
|
deps += cc.find_library('openblas', required:false)
|
||||||
|
if not cc.find_library('openblas', required:false).found()
|
||||||
|
deps += cc.find_library('blas', required:false)
|
||||||
|
deps += cc.find_library('lapack', required:false)
|
||||||
|
endif
|
||||||
deps += cc.find_library('dbghelp')
|
deps += cc.find_library('dbghelp')
|
||||||
deps += cc.find_library('winmm')
|
deps += cc.find_library('winmm')
|
||||||
deps += cc.find_library('setupapi')
|
deps += cc.find_library('setupapi')
|
||||||
@@ -130,34 +149,90 @@ if host_machine.system() == 'emscripten'
|
|||||||
method : 'pkg-config', # or 'cmake' if you prefer
|
method : 'pkg-config', # or 'cmake' if you prefer
|
||||||
required : true)
|
required : true)
|
||||||
else
|
else
|
||||||
sdl3_proj = cmake.subproject('sdl3', options : sdl3_opts)
|
# Try to find system-installed SDL3 first
|
||||||
deps += sdl3_proj.dependency('SDL3-static')
|
sdl3_dep = dependency('sdl3', static: true, required: false)
|
||||||
|
|
||||||
|
if not sdl3_dep.found()
|
||||||
|
message('⚙ System SDL3 not found, building subproject...')
|
||||||
|
sdl3_proj = cmake.subproject('sdl3', options : sdl3_opts)
|
||||||
|
deps += sdl3_proj.dependency('SDL3-static')
|
||||||
|
else
|
||||||
|
deps += sdl3_dep
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
quickjs_opts = []
|
miniz_dep = dependency('miniz', static: true, required: false)
|
||||||
|
if not miniz_dep.found()
|
||||||
|
message('⚙ System miniz not found, building subproject...')
|
||||||
|
deps += dependency('miniz', static:true)
|
||||||
|
else
|
||||||
|
deps += miniz_dep
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Try to find system-installed physfs first
|
||||||
|
physfs_dep = dependency('physfs', static: true, required: false)
|
||||||
|
if not physfs_dep.found()
|
||||||
|
message('⚙ System physfs not found, building subproject...')
|
||||||
|
deps += dependency('physfs', static:true)
|
||||||
|
else
|
||||||
|
deps += physfs_dep
|
||||||
|
endif
|
||||||
|
|
||||||
quickjs_opts += 'default_library=static'
|
|
||||||
deps += dependency('quickjs', static:true, default_options:quickjs_opts)
|
|
||||||
deps += dependency('qjs-layout', static:true)
|
|
||||||
deps += dependency('qjs-miniz', static:true)
|
|
||||||
deps += dependency('physfs', static:true)
|
|
||||||
deps += dependency('threads')
|
deps += dependency('threads')
|
||||||
deps += dependency('chipmunk', static:true)
|
deps += dependency('mimalloc')
|
||||||
|
|
||||||
|
# Try to find system-installed chipmunk first
|
||||||
|
chipmunk_dep = dependency('chipmunk', static: true, required: false)
|
||||||
|
if not chipmunk_dep.found()
|
||||||
|
message('⚙ System chipmunk not found, building subproject...')
|
||||||
|
deps += dependency('chipmunk', static:true)
|
||||||
|
else
|
||||||
|
deps += chipmunk_dep
|
||||||
|
endif
|
||||||
|
|
||||||
if host_machine.system() != 'emscripten'
|
if host_machine.system() != 'emscripten'
|
||||||
deps += dependency('enet', static:true)
|
# Try to find system-installed enet first
|
||||||
|
enet_dep = dependency('enet', static: true, required: false)
|
||||||
|
if not enet_dep.found()
|
||||||
|
message('⚙ System enet not found, building subproject...')
|
||||||
|
deps += dependency('enet', static:true)
|
||||||
|
else
|
||||||
|
deps += enet_dep
|
||||||
|
endif
|
||||||
src += 'qjs_enet.c'
|
src += 'qjs_enet.c'
|
||||||
|
|
||||||
src += 'qjs_tracy.c'
|
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true', 'on_demand=true']
|
||||||
tracy_opts = ['fibers=true', 'on_demand=true']
|
|
||||||
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp'])
|
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp'])
|
||||||
deps += dependency('tracy', static:true, default_options:tracy_opts)
|
|
||||||
|
# Try to find system-installed tracy first
|
||||||
|
tracy_dep = dependency('tracy', static: true, required: false)
|
||||||
|
if not tracy_dep.found()
|
||||||
|
message('⚙ System tracy not found, building subproject...')
|
||||||
|
deps += dependency('tracy', static:true, default_options:tracy_opts)
|
||||||
|
else
|
||||||
|
deps += tracy_dep
|
||||||
|
endif
|
||||||
|
|
||||||
src += 'qjs_dmon.c'
|
src += 'qjs_dmon.c'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
deps += dependency('soloud', static:true)
|
# Try to find system-installed soloud first
|
||||||
deps += dependency('libqrencode', static:true)
|
soloud_dep = dependency('soloud', static: true, required: false)
|
||||||
|
if not soloud_dep.found()
|
||||||
|
message('⚙ System soloud not found, building subproject...')
|
||||||
|
deps += dependency('soloud', static:true)
|
||||||
|
else
|
||||||
|
deps += soloud_dep
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Try to find system-installed qrencode first
|
||||||
|
qr_dep = dependency('qrencode', static: true, required: false)
|
||||||
|
if not qr_dep.found()
|
||||||
|
message('⚙ System qrencode not found, building subproject...')
|
||||||
|
deps += dependency('libqrencode', static:true)
|
||||||
|
else
|
||||||
|
deps += qr_dep
|
||||||
|
endif
|
||||||
|
|
||||||
# Storefront SDK support
|
# Storefront SDK support
|
||||||
storefront = get_option('storefront')
|
storefront = get_option('storefront')
|
||||||
@@ -194,9 +269,9 @@ link_args = link
|
|||||||
sources = []
|
sources = []
|
||||||
src += [
|
src += [
|
||||||
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
||||||
'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c',
|
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
|
||||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_os.c', 'qjs_actor.c',
|
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
|
||||||
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c'
|
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'qjs_num.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
|
||||||
]
|
]
|
||||||
# quirc src
|
# quirc src
|
||||||
src += [
|
src += [
|
||||||
@@ -204,6 +279,9 @@ src += [
|
|||||||
'thirdparty/quirc/identify.c', 'thirdparty/quirc/version_db.c'
|
'thirdparty/quirc/identify.c', 'thirdparty/quirc/version_db.c'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# quickjs src
|
||||||
|
src += ['quickjs.c', 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c']
|
||||||
|
|
||||||
imsrc = [
|
imsrc = [
|
||||||
'GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp',
|
'GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp',
|
||||||
'imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp',
|
'imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp',
|
||||||
@@ -223,99 +301,33 @@ foreach file : src
|
|||||||
sources += files(full_path)
|
sources += files(full_path)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
if get_option('editor')
|
|
||||||
# sources += 'source/qjs_imgui.cpp'
|
|
||||||
# foreach imgui : imsrc
|
|
||||||
# sources += tp / 'imgui' / imgui
|
|
||||||
# endforeach
|
|
||||||
endif
|
|
||||||
|
|
||||||
includers = []
|
includers = []
|
||||||
foreach inc : includes
|
foreach inc : includes
|
||||||
includers += include_directories(inc)
|
includers += include_directories(inc)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
zip_folders = ['scripts', 'fonts', 'icons', 'shaders']
|
|
||||||
zip_paths = []
|
|
||||||
foreach folder: zip_folders
|
|
||||||
zip_paths += meson.project_source_root() / folder
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
# Produce core.zip
|
|
||||||
core = custom_target('core.zip',
|
|
||||||
output : 'core.zip',
|
|
||||||
command : ['sh', '-c',
|
|
||||||
'cd ' + meson.project_source_root() +
|
|
||||||
' && echo "Rebuilding core.zip" && rm -f ' + meson.current_build_dir() + '/core.zip && ' +
|
|
||||||
'zip -r ' + meson.current_build_dir() + '/core.zip scripts fonts icons shaders'
|
|
||||||
],
|
|
||||||
build_always_stale: true,
|
|
||||||
build_by_default: true
|
|
||||||
)
|
|
||||||
|
|
||||||
prosperon_raw = executable('prosperon_raw', sources,
|
|
||||||
dependencies: deps,
|
|
||||||
include_directories: includers,
|
|
||||||
link_args: link,
|
|
||||||
build_rpath: '$ORIGIN',
|
|
||||||
install:false
|
|
||||||
)
|
|
||||||
|
|
||||||
strip_enabled = ['release', 'minsize'].contains(get_option('buildtype'))
|
|
||||||
|
|
||||||
if strip_enabled
|
|
||||||
prosperon_raw_stripped = custom_target('prosperon_raw_stripped',
|
|
||||||
input: prosperon_raw,
|
|
||||||
output: 'prosperon_raw_stripped',
|
|
||||||
command: [
|
|
||||||
'sh', '-c',
|
|
||||||
'strip "$1" && cp "$1" "$2"',
|
|
||||||
'stripper',
|
|
||||||
'@INPUT@',
|
|
||||||
'@OUTPUT@'
|
|
||||||
],
|
|
||||||
build_by_default: true
|
|
||||||
)
|
|
||||||
exe_for_concat = prosperon_raw_stripped
|
|
||||||
else
|
|
||||||
exe_for_concat = prosperon_raw
|
|
||||||
endif
|
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
exe_ext = '.exe'
|
exe_ext = '.exe'
|
||||||
else
|
else
|
||||||
exe_ext = ''
|
exe_ext = ''
|
||||||
endif
|
endif
|
||||||
|
|
||||||
prosperon = custom_target('prosperon',
|
strip_enabled = ['release', 'minsize'].contains(get_option('buildtype'))
|
||||||
output: 'prosperon' + exe_ext,
|
|
||||||
input: [exe_for_concat, core],
|
if strip_enabled
|
||||||
command: [
|
add_project_link_arguments('-s', language: ['c', 'cpp'])
|
||||||
'sh', '-c',
|
endif
|
||||||
'cat "$1" "$2" > "$3" && chmod +x "$3" >/dev/null 2>&1',
|
|
||||||
'concat',
|
cell = executable('cell', sources,
|
||||||
'@INPUT0@',
|
dependencies: deps,
|
||||||
'@INPUT1@',
|
include_directories: includers,
|
||||||
'@OUTPUT@'
|
link_args: link,
|
||||||
],
|
build_rpath: '$ORIGIN',
|
||||||
build_always_stale: true,
|
install: true
|
||||||
build_by_default: true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
prosperon_dep = declare_dependency(
|
cell_dep = declare_dependency(
|
||||||
link_with: prosperon
|
link_with: cell
|
||||||
)
|
|
||||||
|
|
||||||
copy_tests = custom_target(
|
|
||||||
'copy_tests',
|
|
||||||
output: 'tests',
|
|
||||||
command: [
|
|
||||||
'cp', '-rf',
|
|
||||||
join_paths(meson.project_source_root(), 'tests'),
|
|
||||||
meson.project_build_root()
|
|
||||||
],
|
|
||||||
build_always_stale: true,
|
|
||||||
build_by_default: true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
@@ -330,5 +342,5 @@ tests = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
foreach file : tests
|
foreach file : tests
|
||||||
test(file, prosperon_raw, args:['tests/' + file + '.js'], depends:copy_tests)
|
test(file, cell, args:['tests/' + file])
|
||||||
endforeach
|
endforeach
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
var graphics = use('graphics')
|
var graphics = use('graphics')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
var sprite = {
|
var sprite = {
|
||||||
image: undefined,
|
image: null,
|
||||||
set color(x) { this._sprite.color = x; },
|
set color(x) { this._sprite.color = x; },
|
||||||
get color() { return this._sprite.color; },
|
get color() { return this._sprite.color; },
|
||||||
anim_speed: 1,
|
anim_speed: 1,
|
||||||
@@ -11,7 +12,7 @@ var sprite = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof str === 'string') {
|
if (typeof str == 'string') {
|
||||||
if (!this.animset[str]) {
|
if (!this.animset[str]) {
|
||||||
fn?.();
|
fn?.();
|
||||||
return;
|
return;
|
||||||
@@ -25,8 +26,8 @@ var sprite = {
|
|||||||
|
|
||||||
this.del_anim?.();
|
this.del_anim?.();
|
||||||
this.del_anim = () => {
|
this.del_anim = () => {
|
||||||
this.del_anim = undefined;
|
this.del_anim = null;
|
||||||
advance = undefined;
|
advance = null;
|
||||||
stop?.();
|
stop?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,10 +38,10 @@ var sprite = {
|
|||||||
var done = false;
|
var done = false;
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
f = (((f - 1) % playing.frames.length) + playing.frames.length) % playing.frames.length;
|
f = (((f - 1) % playing.frames.length) + playing.frames.length) % playing.frames.length;
|
||||||
if (f === playing.frames.length - 1) done = true;
|
if (f == playing.frames.length - 1) done = true;
|
||||||
} else {
|
} else {
|
||||||
f = (f + 1) % playing.frames.length;
|
f = (f + 1) % playing.frames.length;
|
||||||
if (f === 0) done = true;
|
if (f == 0) done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.image = playing.frames[f];
|
this.image = playing.frames[f];
|
||||||
@@ -65,7 +66,7 @@ var sprite = {
|
|||||||
set path(p) {
|
set path(p) {
|
||||||
var image = graphics.texture(p);
|
var image = graphics.texture(p);
|
||||||
if (!image) {
|
if (!image) {
|
||||||
console.warn(`Could not find image ${p}.`);
|
log.warn(`Could not find image ${p}.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,14 +102,14 @@ var sprite = {
|
|||||||
},
|
},
|
||||||
garbage: function() {
|
garbage: function() {
|
||||||
this.del_anim?.();
|
this.del_anim?.();
|
||||||
this.anim = undefined;
|
this.anim = null;
|
||||||
tree.delete(this._sprite)
|
tree.delete(this._sprite)
|
||||||
this.transform.parent = undefined
|
this.transform.parent = null
|
||||||
for (var t of this.transform.children())
|
for (var t of this.transform.children())
|
||||||
t.parent = undefined
|
t.parent = null
|
||||||
delete this.transform.sprite
|
delete this.transform.sprite
|
||||||
delete this._sprite
|
delete this._sprite
|
||||||
// console.log("CLEARED SPRITE")
|
// log.console("CLEARED SPRITE")
|
||||||
},
|
},
|
||||||
anchor: [0, 0],
|
anchor: [0, 0],
|
||||||
set layer(v) { this._sprite.layer = v; },
|
set layer(v) { this._sprite.layer = v; },
|
||||||
@@ -215,7 +216,7 @@ sprite.to_queue = function(ysort = false)
|
|||||||
};
|
};
|
||||||
var culled = sprite.tree.query(camrect)
|
var culled = sprite.tree.query(camrect)
|
||||||
if (culled.length == 0) return [];
|
if (culled.length == 0) return [];
|
||||||
var cmd = graphics.make_sprite_queue(culled, prosperon.camera, undefined, 1);
|
var cmd = graphics.make_sprite_queue(culled, prosperon.camera, null, 1);
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +224,7 @@ return sprite;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
var Color = use('color')
|
var color = use('color')
|
||||||
var transform = use('transform')
|
var transform = use('transform')
|
||||||
var sprite = use('sprite')
|
var sprite = use('sprite')
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ if (this.overling.transform)
|
|||||||
this.transform.change_hook = $.t_hook;
|
this.transform.change_hook = $.t_hook;
|
||||||
var msp = new sprite
|
var msp = new sprite
|
||||||
this._sprite = msp;
|
this._sprite = msp;
|
||||||
msp.color = Color.white;
|
msp.color = color.white;
|
||||||
this.transform.sprite = this
|
this.transform.sprite = this
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
var cam = {}
|
var cam = {}
|
||||||
|
|
||||||
var os = use('os')
|
|
||||||
var transform = use('transform')
|
var transform = use('transform')
|
||||||
|
|
||||||
var basecam = {}
|
var basecam = {}
|
||||||
@@ -11,18 +11,18 @@ var input = use('input')
|
|||||||
var lay_ctx = layout.make_context();
|
var lay_ctx = layout.make_context();
|
||||||
|
|
||||||
var clay_base = {
|
var clay_base = {
|
||||||
font: undefined,
|
font: null,
|
||||||
background_image: undefined,
|
background_image: null,
|
||||||
slice: 0,
|
slice: 0,
|
||||||
font: 'smalle.16',
|
font: 'smalle.16',
|
||||||
font_size: undefined,
|
font_size: null,
|
||||||
color: [1,1,1,1],
|
color: [1,1,1,1],
|
||||||
spacing:0,
|
spacing:0,
|
||||||
padding:0,
|
padding:0,
|
||||||
margin:0,
|
margin:0,
|
||||||
offset:[0,0],
|
offset:[0,0],
|
||||||
size:undefined,
|
size:null,
|
||||||
background_color: undefined
|
background_color: null
|
||||||
};
|
};
|
||||||
|
|
||||||
var root_item;
|
var root_item;
|
||||||
@@ -125,8 +125,8 @@ function add_item(config)
|
|||||||
// Adjust for child_gap
|
// Adjust for child_gap
|
||||||
if (root_config._childIndex > 0) {
|
if (root_config._childIndex > 0) {
|
||||||
var parentContain = root_config.contain || 0;
|
var parentContain = root_config.contain || 0;
|
||||||
var isVStack = (parentContain & layout.contain.column) !== 0;
|
var isVStack = (parentContain & layout.contain.column) != 0;
|
||||||
var isHStack = (parentContain & layout.contain.row) !== 0;
|
var isHStack = (parentContain & layout.contain.row) != 0;
|
||||||
|
|
||||||
if (isVStack) {
|
if (isVStack) {
|
||||||
margin.t += childGap;
|
margin.t += childGap;
|
||||||
@@ -162,7 +162,7 @@ function add_item(config)
|
|||||||
|
|
||||||
function rectify_configs(config_array)
|
function rectify_configs(config_array)
|
||||||
{
|
{
|
||||||
if (config_array.length === 0)
|
if (config_array.length == 0)
|
||||||
config_array = [{}];
|
config_array = [{}];
|
||||||
|
|
||||||
for (var i = config_array.length-1; i > 0; i--)
|
for (var i = config_array.length-1; i > 0; i--)
|
||||||
@@ -216,8 +216,8 @@ clay.button = function button(str, action, config = {})
|
|||||||
config.action = action;
|
config.action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hovered = undefined;
|
var hovered = null;
|
||||||
clay.newframe = function() { hovered = undefined; }
|
clay.newframe = function() { hovered = null; }
|
||||||
|
|
||||||
// mousepos given in hud coordinates
|
// mousepos given in hud coordinates
|
||||||
clay.draw_commands = function draw_commands(cmds, pos = [0,0], mousepos = prosperon.camera.screen2hud(input.mouse.screenpos()))
|
clay.draw_commands = function draw_commands(cmds, pos = [0,0], mousepos = prosperon.camera.screen2hud(input.mouse.screenpos()))
|
||||||
216
prosperon/color.cm
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
function tohex(n) {
|
||||||
|
var s = Math.floor(n).toString(16);
|
||||||
|
if (s.length == 1) s = "0" + s;
|
||||||
|
return s.toUpperCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
var Color = {
|
||||||
|
white: [1, 1, 1],
|
||||||
|
black: [0, 0, 0],
|
||||||
|
blue: [0, 0, 1],
|
||||||
|
green: [0, 1, 0],
|
||||||
|
yellow: [1, 1, 0],
|
||||||
|
red: [1, 0, 0],
|
||||||
|
gray: [0.71, 0.71, 0.71],
|
||||||
|
cyan: [0, 1, 1],
|
||||||
|
purple: [0.635, 0.365, 0.89],
|
||||||
|
orange: [1, 0.565, 0.251],
|
||||||
|
magenta: [1, 0, 1],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.editor = {};
|
||||||
|
Color.editor.ur = Color.green;
|
||||||
|
|
||||||
|
Color.tohtml = function (v) {
|
||||||
|
var html = v.map(function (n) {
|
||||||
|
return tohex(n * 255);
|
||||||
|
});
|
||||||
|
return "#" + html.join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
var esc = {};
|
||||||
|
esc.reset = "\x1b[0";
|
||||||
|
esc.color = function (v) {
|
||||||
|
var c = v.map(function (n) {
|
||||||
|
return Math.floor(n * 255);
|
||||||
|
});
|
||||||
|
var truecolor = "\x1b[38;2;" + c.join(";") + ";";
|
||||||
|
return truecolor;
|
||||||
|
};
|
||||||
|
|
||||||
|
esc.doc = "Functions and constants for ANSI escape sequences.";
|
||||||
|
|
||||||
|
Color.Arkanoid = {
|
||||||
|
orange: [1, 0.561, 0],
|
||||||
|
teal: [0, 1, 1],
|
||||||
|
green: [0, 1, 0],
|
||||||
|
red: [1, 0, 0],
|
||||||
|
blue: [0, 0.439, 1],
|
||||||
|
purple: [1, 0, 1],
|
||||||
|
yellow: [1, 1, 0],
|
||||||
|
silver: [0.616, 0.616, 0.616],
|
||||||
|
gold: [0.737, 0.682, 0],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Arkanoid.Powerups = {
|
||||||
|
red: [0.682, 0, 0] /* laser */,
|
||||||
|
blue: [0, 0, 0.682] /* enlarge */,
|
||||||
|
green: [0, 0.682, 0] /* catch */,
|
||||||
|
orange: [0.878, 0.561, 0] /* slow */,
|
||||||
|
purple: [0.824, 0, 0.824] /* break */,
|
||||||
|
cyan: [0, 0.682, 1] /* disruption */,
|
||||||
|
gray: [0.561, 0.561, 0.561] /* 1up */,
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Gameboy = {
|
||||||
|
darkest: [0.898, 0.42, 0.102],
|
||||||
|
dark: [0.898, 0.741, 0.102],
|
||||||
|
light: [0.741, 0.898, 0.102],
|
||||||
|
lightest: [0.42, 0.898, 0.102],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Apple = {
|
||||||
|
green: [0.369, 0.741, 0.243],
|
||||||
|
yellow: [1, 0.725, 0],
|
||||||
|
orange: [0.969, 0.51, 0],
|
||||||
|
red: [0.886, 0.22, 0.22],
|
||||||
|
purple: [0.592, 0.224, 0.6],
|
||||||
|
blue: [0, 0.612, 0.875],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Debug = {
|
||||||
|
boundingbox: Color.white,
|
||||||
|
names: [0.329, 0.431, 1],
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.Editor = {
|
||||||
|
grid: [0.388, 1, 0.502],
|
||||||
|
select: [1, 1, 0.216],
|
||||||
|
newgroup: [0.471, 1, 0.039],
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Detects the format of all colors and munges them into a floating point format */
|
||||||
|
Color.normalize = function (c) {
|
||||||
|
var add_a = function (a) {
|
||||||
|
var n = this.slice();
|
||||||
|
n[3] = a;
|
||||||
|
return n;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var p of Object.keys(c)) {
|
||||||
|
if (typeof c[p] != "object") continue;
|
||||||
|
if (!Array.isArray(c[p])) {
|
||||||
|
Color.normalize(c[p]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add alpha channel if not present
|
||||||
|
if (c[p].length == 3) {
|
||||||
|
c[p][3] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any values are > 1 (meaning they're in 0-255 format)
|
||||||
|
var needs_conversion = false;
|
||||||
|
for (var color of c[p]) {
|
||||||
|
if (color > 1) {
|
||||||
|
needs_conversion = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from 0-255 to 0-1 if needed
|
||||||
|
if (needs_conversion) {
|
||||||
|
c[p] = c[p].map(function (x) {
|
||||||
|
return x / 255;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
c[p].alpha = add_a;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.normalize(Color);
|
||||||
|
|
||||||
|
var ColorMap = {};
|
||||||
|
ColorMap.makemap = function (map) {
|
||||||
|
var newmap = Object.create(ColorMap);
|
||||||
|
Object.assign(newmap, map);
|
||||||
|
return newmap;
|
||||||
|
};
|
||||||
|
ColorMap.Jet = ColorMap.makemap({
|
||||||
|
0: [0, 0, 0.514],
|
||||||
|
0.125: [0, 0.235, 0.667],
|
||||||
|
0.375: [0.02, 1, 1],
|
||||||
|
0.625: [1, 1, 0],
|
||||||
|
0.875: [0.98, 0, 0],
|
||||||
|
1: [0.502, 0, 0],
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMap.BlueRed = ColorMap.makemap({
|
||||||
|
0: [0, 0, 1],
|
||||||
|
1: [1, 0, 0],
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMap.Inferno = ColorMap.makemap({
|
||||||
|
0: [0, 0, 0.016],
|
||||||
|
0.13: [0.122, 0.047, 0.282],
|
||||||
|
0.25: [0.333, 0.059, 0.427],
|
||||||
|
0.38: [0.533, 0.133, 0.416],
|
||||||
|
0.5: [0.729, 0.212, 0.333],
|
||||||
|
0.63: [0.89, 0.349, 0.2],
|
||||||
|
0.75: [0.976, 0.549, 0.039],
|
||||||
|
0.88: [0.976, 0.788, 0.196],
|
||||||
|
1: [0.988, 1, 0.643],
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMap.Bathymetry = ColorMap.makemap({
|
||||||
|
0: [0.157, 0.102, 0.173],
|
||||||
|
0.13: [0.233, 0.192, 0.353],
|
||||||
|
0.25: [0.251, 0.298, 0.545],
|
||||||
|
0.38: [0.247, 0.431, 0.592],
|
||||||
|
0.5: [0.282, 0.557, 0.62],
|
||||||
|
0.63: [0.333, 0.682, 0.639],
|
||||||
|
0.75: [0.471, 0.808, 0.639],
|
||||||
|
0.88: [0.733, 0.902, 0.675],
|
||||||
|
1: [0.992, 0.996, 0.8],
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMap.Viridis = ColorMap.makemap({
|
||||||
|
0: [0.267, 0.004, 0.329],
|
||||||
|
0.13: [0.278, 0.173, 0.478],
|
||||||
|
0.25: [0.231, 0.318, 0.545],
|
||||||
|
0.38: [0.173, 0.443, 0.557],
|
||||||
|
0.5: [0.129, 0.565, 0.553],
|
||||||
|
0.63: [0.153, 0.678, 0.506],
|
||||||
|
0.75: [0.361, 0.784, 0.388],
|
||||||
|
0.88: [0.667, 0.863, 0.196],
|
||||||
|
1: [0.992, 0.906, 0.145],
|
||||||
|
});
|
||||||
|
|
||||||
|
Color.normalize(ColorMap);
|
||||||
|
|
||||||
|
ColorMap.sample = function (t, map = this) {
|
||||||
|
if (t < 0) return map[0];
|
||||||
|
if (t > 1) return map[1];
|
||||||
|
|
||||||
|
var lastkey = 0;
|
||||||
|
for (var key of Object.keys(map).sort()) {
|
||||||
|
if (t < key) {
|
||||||
|
var b = map[key];
|
||||||
|
var a = map[lastkey];
|
||||||
|
var tt = (key - lastkey) * t;
|
||||||
|
return a.lerp(b, tt);
|
||||||
|
}
|
||||||
|
lastkey = key;
|
||||||
|
}
|
||||||
|
return map[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
ColorMap.doc = {
|
||||||
|
sample: "Sample a given colormap at the given percentage (0 to 1).",
|
||||||
|
};
|
||||||
|
|
||||||
|
Color.maps = ColorMap
|
||||||
|
Color.utils = esc
|
||||||
|
|
||||||
|
return Color
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
var input = use('input')
|
var input = use('input')
|
||||||
var util = use('util')
|
|
||||||
|
|
||||||
var downkeys = {};
|
var downkeys = {};
|
||||||
|
|
||||||
@@ -108,9 +107,9 @@ input.mouse.normal.doc = "Set the mouse to show again after hiding.";
|
|||||||
|
|
||||||
input.keyboard = {};
|
input.keyboard = {};
|
||||||
input.keyboard.down = function (code) {
|
input.keyboard.down = function (code) {
|
||||||
if (typeof code === "number") return downkeys[code];
|
if (typeof code == "number") return downkeys[code];
|
||||||
if (typeof code === "string") return downkeys[code.toUpperCase().charCodeAt()] || downkeys[code.toLowerCase().charCodeAt()];
|
if (typeof code == "string") return downkeys[code.toUpperCase().charCodeAt()] || downkeys[code.toLowerCase().charCodeAt()];
|
||||||
return undefined;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
input.print_pawn_kbm = function (pawn) {
|
input.print_pawn_kbm = function (pawn) {
|
||||||
@@ -158,7 +157,7 @@ input.print_md_kbm = function print_md_kbm(pawn) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
input.has_bind = function (pawn, bind) {
|
input.has_bind = function (pawn, bind) {
|
||||||
return typeof pawn.inputs?.[bind] === "function";
|
return typeof pawn.inputs?.[bind] == "function";
|
||||||
};
|
};
|
||||||
|
|
||||||
input.action = {
|
input.action = {
|
||||||
@@ -177,17 +176,17 @@ input.tabcomplete = function tabcomplete(val, list) {
|
|||||||
if (!val) return val;
|
if (!val) return val;
|
||||||
list = filter(x => x.startsWith(val))
|
list = filter(x => x.startsWith(val))
|
||||||
|
|
||||||
if (list.length === 1) {
|
if (list.length == 1) {
|
||||||
return list[0];
|
return list[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = undefined;
|
var ret = null;
|
||||||
var i = val.length;
|
var i = val.length;
|
||||||
while (!ret && list.length !== 0) {
|
while (!ret && list.length != 0) {
|
||||||
var char = list[0][i];
|
var char = list[0][i];
|
||||||
if (
|
if (
|
||||||
!list.every(function (x) {
|
!list.every(function (x) {
|
||||||
return x[i] === char;
|
return x[i] == char;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
ret = list[0].slice(0, i);
|
ret = list[0].slice(0, i);
|
||||||
@@ -214,7 +213,7 @@ var Player = {
|
|||||||
|
|
||||||
mouse_input(type, ...args) {
|
mouse_input(type, ...args) {
|
||||||
for (var pawn of [...this.pawns].reverse()) {
|
for (var pawn of [...this.pawns].reverse()) {
|
||||||
if (typeof pawn.inputs?.mouse?.[type] === "function") {
|
if (typeof pawn.inputs?.mouse?.[type] == "function") {
|
||||||
pawn.inputs.mouse[type].call(pawn, ...args);
|
pawn.inputs.mouse[type].call(pawn, ...args);
|
||||||
pawn.inputs.post?.call(pawn);
|
pawn.inputs.post?.call(pawn);
|
||||||
if (!pawn.inputs.fallthru) return;
|
if (!pawn.inputs.fallthru) return;
|
||||||
@@ -224,7 +223,7 @@ var Player = {
|
|||||||
|
|
||||||
char_input(c) {
|
char_input(c) {
|
||||||
for (var pawn of [...this.pawns].reverse()) {
|
for (var pawn of [...this.pawns].reverse()) {
|
||||||
if (typeof pawn.inputs?.char === "function") {
|
if (typeof pawn.inputs?.char == "function") {
|
||||||
pawn.inputs.char.call(pawn, c);
|
pawn.inputs.char.call(pawn, c);
|
||||||
pawn.inputs.post?.call(pawn);
|
pawn.inputs.post?.call(pawn);
|
||||||
if (!pawn.inputs.fallthru) return;
|
if (!pawn.inputs.fallthru) return;
|
||||||
@@ -271,16 +270,16 @@ var Player = {
|
|||||||
fn = inputs[cmd].released;
|
fn = inputs[cmd].released;
|
||||||
break;
|
break;
|
||||||
case "down":
|
case "down":
|
||||||
if (typeof inputs[cmd].down === "function") fn = inputs[cmd].down;
|
if (typeof inputs[cmd].down == "function") fn = inputs[cmd].down;
|
||||||
else if (inputs[cmd].down) fn = inputs[cmd];
|
else if (inputs[cmd].down) fn = inputs[cmd];
|
||||||
}
|
}
|
||||||
|
|
||||||
var consumed = false;
|
var consumed = false;
|
||||||
if (typeof fn === "function") {
|
if (typeof fn == "function") {
|
||||||
fn.call(pawn, ...args);
|
fn.call(pawn, ...args);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
if (state === "released") inputs.release_post?.call(pawn);
|
if (state == "released") inputs.release_post?.call(pawn);
|
||||||
if (inputs.block) return;
|
if (inputs.block) return;
|
||||||
if (consumed) return;
|
if (consumed) return;
|
||||||
}
|
}
|
||||||
@@ -295,7 +294,7 @@ var Player = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
print_pawns() {
|
print_pawns() {
|
||||||
[...this.pawns].reverse().forEach(x => console.log(x))
|
[...this.pawns].reverse().forEach(x => log.console(x))
|
||||||
},
|
},
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
172
prosperon/draw2d.cm
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
var math = use('math')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
|
var draw = {}
|
||||||
|
draw[cell.DOC] = `
|
||||||
|
A collection of 2D drawing functions that create drawing command lists.
|
||||||
|
These are pure functions that return plain JavaScript objects representing
|
||||||
|
drawing operations. No rendering or actor communication happens here.
|
||||||
|
`
|
||||||
|
var current_list = []
|
||||||
|
|
||||||
|
// Clear current list
|
||||||
|
draw.clear = function() {
|
||||||
|
current_list.length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get commands from current list
|
||||||
|
draw.get_commands = function() {
|
||||||
|
return current_list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to add a command
|
||||||
|
function add_command(type, data) {
|
||||||
|
data.cmd = type
|
||||||
|
current_list.push(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default geometry definitions
|
||||||
|
var ellipse_def = {
|
||||||
|
start: 0,
|
||||||
|
end: 1,
|
||||||
|
mode: 'fill',
|
||||||
|
thickness: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
var line_def = {
|
||||||
|
thickness: 1,
|
||||||
|
cap:"butt",
|
||||||
|
}
|
||||||
|
|
||||||
|
var rect_def = {
|
||||||
|
thickness:1,
|
||||||
|
radius: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var slice9_info = {
|
||||||
|
tile_top:true,
|
||||||
|
tile_bottom:true,
|
||||||
|
tile_left:true,
|
||||||
|
tile_right:true,
|
||||||
|
tile_center_x:true,
|
||||||
|
tile_center_right:true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var image_info = {
|
||||||
|
tile_x: false,
|
||||||
|
tile_y: false,
|
||||||
|
flip_x: false,
|
||||||
|
flip_y: false,
|
||||||
|
mode: 'linear'
|
||||||
|
}
|
||||||
|
|
||||||
|
var circle_def = {
|
||||||
|
inner_radius:1, // percentage: 1 means filled circle
|
||||||
|
start:0,
|
||||||
|
end: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawing functions
|
||||||
|
draw.point = function(pos, size, opt = {}, material) {
|
||||||
|
add_command("draw_point", {
|
||||||
|
pos: pos,
|
||||||
|
size: size,
|
||||||
|
opt: opt,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.ellipse = function(pos, radii, defl, material) {
|
||||||
|
var opt = defl ? {...ellipse_def, ...defl} : ellipse_def
|
||||||
|
if (opt.thickness <= 0) opt.thickness = Math.max(radii[0], radii[1])
|
||||||
|
|
||||||
|
add_command("draw_ellipse", {
|
||||||
|
pos: pos,
|
||||||
|
radii: radii,
|
||||||
|
opt: opt,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.line = function(points, defl, material)
|
||||||
|
{
|
||||||
|
var opt = defl ? {...line_def, ...defl} : line_def
|
||||||
|
|
||||||
|
add_command("draw_line", {
|
||||||
|
points: points,
|
||||||
|
opt: opt,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.cross = function render_cross(pos, size, defl, material) {
|
||||||
|
var a = [pos.add([0, size]), pos.add([0, -size])]
|
||||||
|
var b = [pos.add([size, 0]), pos.add([-size, 0])]
|
||||||
|
draw.line(a, defl, material)
|
||||||
|
draw.line(b, defl, material)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, defl, material) {
|
||||||
|
var dir = math.norm(end.sub(start))
|
||||||
|
var wing1 = [math.rotate(dir, wingangle).scale(wingspan).add(end), end]
|
||||||
|
var wing2 = [math.rotate(dir, -wingangle).scale(wingspan).add(end), end]
|
||||||
|
draw.line([start, end], defl, material)
|
||||||
|
draw.line(wing1, defl, material)
|
||||||
|
draw.line(wing2, defl, material)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.rectangle = function render_rectangle(rect, defl, material) {
|
||||||
|
var opt = defl ? {...rect_def, ...defl} : rect_def
|
||||||
|
|
||||||
|
add_command("draw_rect", {
|
||||||
|
rect: rect,
|
||||||
|
opt: opt,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info, material) {
|
||||||
|
if (!image) throw Error('Need an image to render.')
|
||||||
|
|
||||||
|
add_command("draw_slice9", {
|
||||||
|
image: image,
|
||||||
|
rect: rect,
|
||||||
|
slice: slice,
|
||||||
|
info: info,
|
||||||
|
material: material
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.image = function image(image, rect, rotation, anchor, shear, info, material) {
|
||||||
|
if (!rect) throw Error('Need rectangle to render image.')
|
||||||
|
if (!image) throw Error('Need an image to render.')
|
||||||
|
|
||||||
|
if (!('x' in rect && 'y' in rect)) throw Error('Must provide X and Y for image.')
|
||||||
|
|
||||||
|
add_command("draw_image", {
|
||||||
|
image,
|
||||||
|
rect,
|
||||||
|
rotation,
|
||||||
|
anchor,
|
||||||
|
shear,
|
||||||
|
info,
|
||||||
|
material,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.circle = function render_circle(pos, radius, defl, material) {
|
||||||
|
draw.ellipse(pos, [radius,radius], defl, material)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.text = function text(text, pos, font = 'fonts/c64.ttf', size = 8, color = color.white, wrap = 0) {
|
||||||
|
add_command("draw_text", {
|
||||||
|
text,
|
||||||
|
pos,
|
||||||
|
font,
|
||||||
|
size,
|
||||||
|
wrap,
|
||||||
|
material: {color}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return draw
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
var Color = use('color')
|
var color = use('color')
|
||||||
var os = use('os')
|
|
||||||
var graphics = use('graphics')
|
var graphics = use('graphics')
|
||||||
var transform = use('transform')
|
var transform = use('transform')
|
||||||
|
|
||||||
@@ -15,7 +14,7 @@ ex.garbage = function()
|
|||||||
ex.update = function(dt)
|
ex.update = function(dt)
|
||||||
{
|
{
|
||||||
for (var e of ex.emitters)
|
for (var e of ex.emitters)
|
||||||
try { e.step(dt) } catch(e) { console.error(e) }
|
try { e.step(dt) } catch(e) { log.error(e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
ex.step_hook = function(p)
|
ex.step_hook = function(p)
|
||||||
@@ -104,7 +103,7 @@ ex.scale = 1
|
|||||||
ex.grow_for = 0
|
ex.grow_for = 0
|
||||||
ex.spawn_timer = 0
|
ex.spawn_timer = 0
|
||||||
ex.pps = 0
|
ex.pps = 0
|
||||||
ex.color = Color.white
|
ex.color = color.white
|
||||||
|
|
||||||
ex.draw = function()
|
ex.draw = function()
|
||||||
{
|
{
|
||||||
@@ -112,7 +111,7 @@ ex.draw = function()
|
|||||||
if (!diff) throw new Error("emitter does not have a proper diffuse texture")
|
if (!diff) throw new Error("emitter does not have a proper diffuse texture")
|
||||||
|
|
||||||
var mesh = graphics.make_sprite_mesh(this.particles)
|
var mesh = graphics.make_sprite_mesh(this.particles)
|
||||||
if (mesh.num_indices === 0) return
|
if (mesh.num_indices == 0) return
|
||||||
render.queue({
|
render.queue({
|
||||||
type:'geometry',
|
type:'geometry',
|
||||||
mesh,
|
mesh,
|
||||||
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
@@ -5,6 +5,7 @@ var sprite = use('sprite')
|
|||||||
var geom = use('geometry')
|
var geom = use('geometry')
|
||||||
var input = use('controller')
|
var input = use('controller')
|
||||||
var config = use('config')
|
var config = use('config')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
var bunnyTex = graphics.texture("bunny")
|
var bunnyTex = graphics.texture("bunny")
|
||||||
|
|
||||||
@@ -65,5 +66,5 @@ this.hud = function() {
|
|||||||
draw.images(bunnyTex, bunnies)
|
draw.images(bunnyTex, bunnies)
|
||||||
|
|
||||||
var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + bunnies.length
|
var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + bunnies.length
|
||||||
draw.text(msg, {x:0, y:0, width:config.width, height:40}, undefined, 0, Color.white, 0)
|
draw.text(msg, {x:0, y:0, width:config.width, height:40}, null, 0, color.white, 0)
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 390 B |
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 438 B |
|
Before Width: | Height: | Size: 398 B After Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 337 B |
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 390 B |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 379 B |
@@ -1,16 +1,9 @@
|
|||||||
/* main.js – runs the demo with your prototype-based grid */
|
/* main.js – runs the demo with your prototype-based grid */
|
||||||
|
|
||||||
var moth = use('moth', $_.delay)
|
|
||||||
var json = use('json')
|
var json = use('json')
|
||||||
|
var draw2d = use('prosperon/draw2d')
|
||||||
|
|
||||||
var res = 160
|
var blob = use('blob')
|
||||||
var internal_res = 480
|
|
||||||
|
|
||||||
moth.initialize({ width:res, height:res, resolution_x:internal_res, resolution_y:internal_res, mode:'letterbox' });
|
|
||||||
|
|
||||||
var os = use('os');
|
|
||||||
var draw2d = use('draw2d');
|
|
||||||
var gfx = use('graphics');
|
|
||||||
|
|
||||||
/*──── import our pieces + systems ───────────────────────────────────*/
|
/*──── import our pieces + systems ───────────────────────────────────*/
|
||||||
var Grid = use('grid'); // your new ctor
|
var Grid = use('grid'); // your new ctor
|
||||||
@@ -48,14 +41,14 @@ function updateTitle() {
|
|||||||
break;
|
break;
|
||||||
case 'connected':
|
case 'connected':
|
||||||
if (myColor) {
|
if (myColor) {
|
||||||
title += (mover.turn === myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")");
|
title += (mover.turn == myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")");
|
||||||
} else {
|
} else {
|
||||||
title += mover.turn + " turn";
|
title += mover.turn + " turn";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
prosperon.window.title = title
|
log.console(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize title
|
// Initialize title
|
||||||
@@ -70,11 +63,11 @@ var opponentMousePos = null;
|
|||||||
var opponentHoldingPiece = false;
|
var opponentHoldingPiece = false;
|
||||||
var opponentSelectPos = null;
|
var opponentSelectPos = null;
|
||||||
|
|
||||||
prosperon.on('mouse_button_down', function(e) {
|
function handleMouseButtonDown(e) {
|
||||||
if (e.which !== 0) return;
|
if (e.which != 0) return;
|
||||||
|
|
||||||
// Don't allow piece selection unless we have an opponent
|
// Don't allow piece selection unless we have an opponent
|
||||||
if (gameState !== 'connected' || !opponent) return;
|
if (gameState != 'connected' || !opponent) return;
|
||||||
|
|
||||||
var mx = e.mouse.x;
|
var mx = e.mouse.x;
|
||||||
var my = e.mouse.y;
|
var my = e.mouse.y;
|
||||||
@@ -83,7 +76,7 @@ prosperon.on('mouse_button_down', function(e) {
|
|||||||
if (!grid.inBounds(c)) return;
|
if (!grid.inBounds(c)) return;
|
||||||
|
|
||||||
var cell = grid.at(c);
|
var cell = grid.at(c);
|
||||||
if (cell.length && cell[0].colour === mover.turn) {
|
if (cell.length && cell[0].colour == mover.turn) {
|
||||||
selectPos = c;
|
selectPos = c;
|
||||||
holdingPiece = true;
|
holdingPiece = true;
|
||||||
// Send pickup notification to opponent
|
// Send pickup notification to opponent
|
||||||
@@ -96,13 +89,13 @@ prosperon.on('mouse_button_down', function(e) {
|
|||||||
} else {
|
} else {
|
||||||
selectPos = null;
|
selectPos = null;
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
prosperon.on('mouse_button_up', function(e) {
|
function handleMouseButtonUp(e) {
|
||||||
if (e.which !== 0 || !holdingPiece || !selectPos) return;
|
if (e.which != 0 || !holdingPiece || !selectPos) return;
|
||||||
|
|
||||||
// Don't allow moves unless we have an opponent and it's our turn
|
// Don't allow moves unless we have an opponent and it's our turn
|
||||||
if (gameState !== 'connected' || !opponent || !isMyTurn) {
|
if (gameState != 'connected' || !opponent || !isMyTurn) {
|
||||||
holdingPiece = false;
|
holdingPiece = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -117,16 +110,16 @@ prosperon.on('mouse_button_up', function(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mover.tryMove(grid.at(selectPos)[0], c)) {
|
if (mover.tryMove(grid.at(selectPos)[0], c)) {
|
||||||
console.log("Made move from", selectPos, "to", c);
|
log.console("Made move from", selectPos, "to", c);
|
||||||
// Send move to opponent
|
// Send move to opponent
|
||||||
console.log("Sending move to opponent:", opponent);
|
log.console("Sending move to opponent:", opponent);
|
||||||
send(opponent, {
|
send(opponent, {
|
||||||
type: 'move',
|
type: 'move',
|
||||||
from: selectPos,
|
from: selectPos,
|
||||||
to: c
|
to: c
|
||||||
});
|
});
|
||||||
isMyTurn = false; // It's now opponent's turn
|
isMyTurn = false; // It's now opponent's turn
|
||||||
console.log("Move sent, now opponent's turn");
|
log.console("Move sent, now opponent's turn");
|
||||||
selectPos = null;
|
selectPos = null;
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
@@ -139,9 +132,9 @@ prosperon.on('mouse_button_up', function(e) {
|
|||||||
type: 'piece_drop'
|
type: 'piece_drop'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
prosperon.on('mouse_motion', function(e) {
|
function handleMouseMotion(e) {
|
||||||
var mx = e.pos.x;
|
var mx = e.pos.x;
|
||||||
var my = e.pos.y;
|
var my = e.pos.y;
|
||||||
|
|
||||||
@@ -154,7 +147,7 @@ prosperon.on('mouse_motion', function(e) {
|
|||||||
hoverPos = c;
|
hoverPos = c;
|
||||||
|
|
||||||
// Send mouse position to opponent in real-time
|
// Send mouse position to opponent in real-time
|
||||||
if (opponent && gameState === 'connected') {
|
if (opponent && gameState == 'connected') {
|
||||||
send(opponent, {
|
send(opponent, {
|
||||||
type: 'mouse_move',
|
type: 'mouse_move',
|
||||||
pos: c,
|
pos: c,
|
||||||
@@ -162,7 +155,18 @@ prosperon.on('mouse_motion', function(e) {
|
|||||||
selectPos: selectPos
|
selectPos: selectPos
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
function handleKeyDown(e) {
|
||||||
|
// S key - start server
|
||||||
|
if (e.scancode == 22 && gameState == 'waiting') { // S key
|
||||||
|
startServer();
|
||||||
|
}
|
||||||
|
// J key - join server
|
||||||
|
else if (e.scancode == 13 && gameState == 'waiting') { // J key
|
||||||
|
joinServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*──── drawing helpers ───────────────────────────────────────────────*/
|
/*──── drawing helpers ───────────────────────────────────────────────*/
|
||||||
/* ── constants ─────────────────────────────────────────────────── */
|
/* ── constants ─────────────────────────────────────────────────── */
|
||||||
@@ -177,8 +181,8 @@ var opponentMouseColor = [1.0, 0.0, 0.0, 1.0]; // Red for opponent mouse
|
|||||||
function drawBoard() {
|
function drawBoard() {
|
||||||
for (var y = 0; y < 8; ++y)
|
for (var y = 0; y < 8; ++y)
|
||||||
for (var x = 0; x < 8; ++x) {
|
for (var x = 0; x < 8; ++x) {
|
||||||
var isMyHover = hoverPos && hoverPos[0] === x && hoverPos[1] === y;
|
var isMyHover = hoverPos && hoverPos[0] == x && hoverPos[1] == y;
|
||||||
var isOpponentHover = opponentMousePos && opponentMousePos[0] === x && opponentMousePos[1] === y;
|
var isOpponentHover = opponentMousePos && opponentMousePos[0] == x && opponentMousePos[1] == y;
|
||||||
var isValidMove = selectPos && holdingPiece && isValidMoveForTurn(selectPos, [x, y]);
|
var isValidMove = selectPos && holdingPiece && isValidMoveForTurn(selectPos, [x, y]);
|
||||||
|
|
||||||
var color = ((x+y)&1) ? dark : light;
|
var color = ((x+y)&1) ? dark : light;
|
||||||
@@ -193,7 +197,8 @@ function drawBoard() {
|
|||||||
|
|
||||||
draw2d.rectangle(
|
draw2d.rectangle(
|
||||||
{ x: x*S, y: y*S, width: S, height: S },
|
{ x: x*S, y: y*S, width: S, height: S },
|
||||||
{ thickness: 0, color: color }
|
{ thickness: 0 },
|
||||||
|
{ color: color }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,7 +211,7 @@ function isValidMoveForTurn(from, to) {
|
|||||||
|
|
||||||
// Check if the destination has a piece of the same color
|
// Check if the destination has a piece of the same color
|
||||||
var destCell = grid.at(to);
|
var destCell = grid.at(to);
|
||||||
if (destCell.length && destCell[0].colour === piece.colour) {
|
if (destCell.length && destCell[0].colour == piece.colour) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,22 +225,22 @@ function drawPieces() {
|
|||||||
|
|
||||||
// Skip drawing the piece being held (by me or opponent)
|
// Skip drawing the piece being held (by me or opponent)
|
||||||
if (holdingPiece && selectPos &&
|
if (holdingPiece && selectPos &&
|
||||||
piece.coord[0] === selectPos[0] &&
|
piece.coord[0] == selectPos[0] &&
|
||||||
piece.coord[1] === selectPos[1]) {
|
piece.coord[1] == selectPos[1]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip drawing the piece being held by opponent
|
// Skip drawing the piece being held by opponent
|
||||||
if (opponentHoldingPiece && opponentSelectPos &&
|
if (opponentHoldingPiece && opponentSelectPos &&
|
||||||
piece.coord[0] === opponentSelectPos[0] &&
|
piece.coord[0] == opponentSelectPos[0] &&
|
||||||
piece.coord[1] === opponentSelectPos[1]) {
|
piece.coord[1] == opponentSelectPos[1]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var r = { x: piece.coord[0]*S, y: piece.coord[1]*S,
|
var r = { x: piece.coord[0]*S, y: piece.coord[1]*S,
|
||||||
width:S, height:S };
|
width:S, height:S };
|
||||||
|
|
||||||
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
draw2d.image(piece.sprite, r);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Draw the held piece at the mouse position if we're holding one
|
// Draw the held piece at the mouse position if we're holding one
|
||||||
@@ -245,7 +250,7 @@ function drawPieces() {
|
|||||||
var r = { x: hoverPos[0]*S, y: hoverPos[1]*S,
|
var r = { x: hoverPos[0]*S, y: hoverPos[1]*S,
|
||||||
width:S, height:S };
|
width:S, height:S };
|
||||||
|
|
||||||
draw2d.image(piece.sprite, r, 0, [0,0], [0,0], {mode:"nearest"});
|
draw2d.image(piece.sprite, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,29 +262,23 @@ function drawPieces() {
|
|||||||
width:S, height:S };
|
width:S, height:S };
|
||||||
|
|
||||||
// Draw with slight transparency to show it's the opponent's piece
|
// Draw with slight transparency to show it's the opponent's piece
|
||||||
draw2d.image(opponentPiece.sprite, r, 0, [0,0], [0,0], {mode:"nearest", color: [1, 1, 1, 0.7]});
|
draw2d.image(opponentPiece.sprite, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var graphics = use('graphics')
|
function update(dt)
|
||||||
|
{
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
prosperon.on('draw', function() {
|
function draw()
|
||||||
|
{
|
||||||
|
draw2d.clear()
|
||||||
drawBoard()
|
drawBoard()
|
||||||
drawPieces()
|
drawPieces()
|
||||||
draw2d.text("HELL", [100,100])
|
return draw2d.get_commands()
|
||||||
})
|
}
|
||||||
|
|
||||||
prosperon.on('key_down', function(e) {
|
|
||||||
// S key - start server
|
|
||||||
if (e.scancode === 22 && gameState === 'waiting') { // S key
|
|
||||||
startServer();
|
|
||||||
}
|
|
||||||
// J key - join server
|
|
||||||
else if (e.scancode === 13 && gameState === 'waiting') { // J key
|
|
||||||
joinServer();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function startServer() {
|
function startServer() {
|
||||||
gameState = 'server_waiting';
|
gameState = 'server_waiting';
|
||||||
@@ -289,11 +288,11 @@ function startServer() {
|
|||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
$_.portal(e => {
|
$_.portal(e => {
|
||||||
console.log("Portal received contact message");
|
log.console("Portal received contact message");
|
||||||
// Reply with this actor to establish connection
|
// Reply with this actor to establish connection
|
||||||
console.log (json.encode($_))
|
log.console (json.encode($_))
|
||||||
send(e, $_);
|
send(e, $_);
|
||||||
console.log("Portal replied with server actor");
|
log.console("Portal replied with server actor");
|
||||||
}, 5678);
|
}, 5678);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,10 +301,10 @@ function joinServer() {
|
|||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
function contact_fn(actor, reason) {
|
function contact_fn(actor, reason) {
|
||||||
console.log("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason);
|
log.console("CONTACTED!", actor ? "SUCCESS" : "FAILED", reason);
|
||||||
if (actor) {
|
if (actor) {
|
||||||
opponent = actor;
|
opponent = actor;
|
||||||
console.log("Connection established with server, sending join request");
|
log.console("Connection established with server, sending join request");
|
||||||
|
|
||||||
// Send a greet message with our actor object
|
// Send a greet message with our actor object
|
||||||
send(opponent, {
|
send(opponent, {
|
||||||
@@ -313,7 +312,7 @@ function joinServer() {
|
|||||||
client_actor: $_
|
client_actor: $_
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log(`Failed to connect: ${json.encode(reason)}`);
|
log.console(`Failed to connect: ${json.encode(reason)}`);
|
||||||
gameState = 'waiting';
|
gameState = 'waiting';
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
@@ -325,51 +324,38 @@ function joinServer() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var os = use('os')
|
|
||||||
var actor = use('actor')
|
|
||||||
for (var i in actor) console.log(i)
|
|
||||||
|
|
||||||
// Set up IO actor subscription
|
|
||||||
var ioguy = {
|
|
||||||
__ACTORDATA__: {
|
|
||||||
id: actor.ioactor()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
send(ioguy, {
|
|
||||||
type: "subscribe",
|
|
||||||
actor: $_
|
|
||||||
});
|
|
||||||
|
|
||||||
$_.receiver(e => {
|
$_.receiver(e => {
|
||||||
if (e.type === 'game_start' || e.type === 'move' || e.type === 'greet')
|
if (e.kind == 'update')
|
||||||
console.log("Receiver got message:", e.type, e);
|
send(e, update(e.dt))
|
||||||
if (e.type === 'quit') os.exit()
|
else if (e.kind == 'draw')
|
||||||
|
send(e, draw())
|
||||||
|
else if (e.type == 'game_start' || e.type == 'move' || e.type == 'greet')
|
||||||
|
log.console("Receiver got message:", e.type, e);
|
||||||
|
|
||||||
if (e.type === 'greet') {
|
if (e.type == 'greet') {
|
||||||
console.log("Server received greet from client");
|
log.console("Server received greet from client");
|
||||||
// Store the client's actor object for ongoing communication
|
// Store the client's actor object for ongoing communication
|
||||||
opponent = e.client_actor;
|
opponent = e.client_actor;
|
||||||
console.log("Stored client actor:", json.encode(opponent));
|
log.console("Stored client actor:", json.encode(opponent));
|
||||||
gameState = 'connected';
|
gameState = 'connected';
|
||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
// Send game_start to the client
|
// Send game_start to the client
|
||||||
console.log("Sending game_start to client");
|
log.console("Sending game_start to client");
|
||||||
send(opponent, {
|
send(opponent, {
|
||||||
type: 'game_start',
|
type: 'game_start',
|
||||||
your_color: 'black'
|
your_color: 'black'
|
||||||
});
|
});
|
||||||
console.log("game_start message sent to client");
|
log.console("game_start message sent to client");
|
||||||
}
|
}
|
||||||
else if (e.type === 'game_start') {
|
else if (e.type == 'game_start') {
|
||||||
console.log("Game starting, I am:", e.your_color);
|
log.console("Game starting, I am:", e.your_color);
|
||||||
myColor = e.your_color;
|
myColor = e.your_color;
|
||||||
isMyTurn = (myColor === 'white');
|
isMyTurn = (myColor == 'white');
|
||||||
gameState = 'connected';
|
gameState = 'connected';
|
||||||
updateTitle();
|
updateTitle();
|
||||||
} else if (e.type === 'move') {
|
} else if (e.type == 'move') {
|
||||||
console.log("Received move from opponent:", e.from, "to", e.to);
|
log.console("Received move from opponent:", e.from, "to", e.to);
|
||||||
// Apply opponent's move
|
// Apply opponent's move
|
||||||
var fromCell = grid.at(e.from);
|
var fromCell = grid.at(e.from);
|
||||||
if (fromCell.length) {
|
if (fromCell.length) {
|
||||||
@@ -377,27 +363,33 @@ $_.receiver(e => {
|
|||||||
if (mover.tryMove(piece, e.to)) {
|
if (mover.tryMove(piece, e.to)) {
|
||||||
isMyTurn = true; // It's now our turn
|
isMyTurn = true; // It's now our turn
|
||||||
updateTitle();
|
updateTitle();
|
||||||
console.log("Applied opponent move, now my turn");
|
log.console("Applied opponent move, now my turn");
|
||||||
} else {
|
} else {
|
||||||
console.log("Failed to apply opponent move");
|
log.console("Failed to apply opponent move");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("No piece found at from position");
|
log.console("No piece found at from position");
|
||||||
}
|
}
|
||||||
} else if (e.type === 'mouse_move') {
|
} else if (e.type == 'mouse_move') {
|
||||||
// Update opponent's mouse position
|
// Update opponent's mouse position
|
||||||
opponentMousePos = e.pos;
|
opponentMousePos = e.pos;
|
||||||
opponentHoldingPiece = e.holding;
|
opponentHoldingPiece = e.holding;
|
||||||
opponentSelectPos = e.selectPos;
|
opponentSelectPos = e.selectPos;
|
||||||
} else if (e.type === 'piece_pickup') {
|
} else if (e.type == 'piece_pickup') {
|
||||||
// Opponent picked up a piece
|
// Opponent picked up a piece
|
||||||
opponentSelectPos = e.pos;
|
opponentSelectPos = e.pos;
|
||||||
opponentHoldingPiece = true;
|
opponentHoldingPiece = true;
|
||||||
} else if (e.type === 'piece_drop') {
|
} else if (e.type == 'piece_drop') {
|
||||||
// Opponent dropped their piece
|
// Opponent dropped their piece
|
||||||
opponentHoldingPiece = false;
|
opponentHoldingPiece = false;
|
||||||
opponentSelectPos = null;
|
opponentSelectPos = null;
|
||||||
|
} else if (e.type == 'mouse_button_down') {
|
||||||
|
handleMouseButtonDown(e)
|
||||||
|
} else if (e.type == 'mouse_button_up') {
|
||||||
|
handleMouseButtonUp(e)
|
||||||
|
} else if (e.type == 'mouse_motion') {
|
||||||
|
handleMouseMotion(e)
|
||||||
|
} else if (e.type == 'key_down') {
|
||||||
|
handleKeyDown(e)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
prosperon.dispatch(e.type, e)
|
|
||||||
})
|
|
||||||
69
prosperon/examples/chess/grid.cm
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
function grid(w, h) {
|
||||||
|
this.width = w;
|
||||||
|
this.height = h;
|
||||||
|
// create a height×width array of empty lists
|
||||||
|
this.cells = new Array(h);
|
||||||
|
for (let y = 0; y < h; y++) {
|
||||||
|
this.cells[y] = new Array(w);
|
||||||
|
for (let x = 0; x < w; x++) {
|
||||||
|
this.cells[y][x] = []; // each cell holds its own list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grid.prototype = {
|
||||||
|
// return the array at (x,y)
|
||||||
|
cell(x, y) {
|
||||||
|
return this.cells[y][x];
|
||||||
|
},
|
||||||
|
|
||||||
|
// alias for cell
|
||||||
|
at(pos) {
|
||||||
|
return this.cell(pos.x, pos.y);
|
||||||
|
},
|
||||||
|
|
||||||
|
// add an entity into a cell
|
||||||
|
add(entity, pos) {
|
||||||
|
this.cell(pos.x, pos.y).push(entity);
|
||||||
|
entity.coord = pos.slice();
|
||||||
|
},
|
||||||
|
|
||||||
|
// remove an entity from a cell
|
||||||
|
remove(entity, pos) {
|
||||||
|
const c = this.cell(pos.x, pos.y);
|
||||||
|
const i = c.indexOf(entity);
|
||||||
|
if (i !== -1) c.splice(i, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
// bounds check
|
||||||
|
inBounds(pos) {
|
||||||
|
return (
|
||||||
|
pos.x >= 0 && pos.x < this.width &&
|
||||||
|
pos.y >= 0 && pos.y < this.height
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
// call fn(entity, coord) for every entity in every cell
|
||||||
|
each(fn) {
|
||||||
|
for (let y = 0; y < this.height; y++) {
|
||||||
|
for (let x = 0; x < this.width; x++) {
|
||||||
|
const list = this.cells[y][x];
|
||||||
|
for (let entity of list) {
|
||||||
|
fn(entity, entity.coord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// printable representation
|
||||||
|
toString() {
|
||||||
|
let out = `grid [${this.width}×${this.height}]\n`;
|
||||||
|
for (let y = 0; y < this.height; y++) {
|
||||||
|
for (let x = 0; x < this.width; x++) {
|
||||||
|
out += this.cells[y][x].length;
|
||||||
|
}
|
||||||
|
if (y !== this.height - 1) out += "\n";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,17 +5,17 @@ var MovementSystem = function(grid, rules) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MovementSystem.prototype.tryMove = function (piece, to) {
|
MovementSystem.prototype.tryMove = function (piece, to) {
|
||||||
if (piece.colour !== this.turn) return false;
|
if (piece.colour != this.turn) return false;
|
||||||
|
|
||||||
// normalise ‘to’ into our hybrid coord
|
// normalise ‘to’ into our hybrid coord
|
||||||
var dest = [to.x !== undefined ? to.x : to[0],
|
var dest = [to.x ?? t[0],
|
||||||
to.y !== undefined ? to.y : to[1]];
|
to.y ?? to[1]];
|
||||||
|
|
||||||
if (!this.grid.inBounds(dest)) return false;
|
if (!this.grid.inBounds(dest)) return false;
|
||||||
if (!this.rules.canMove(piece, piece.coord, dest, this.grid)) return false;
|
if (!this.rules.canMove(piece, piece.coord, dest, this.grid)) return false;
|
||||||
|
|
||||||
var victims = this.grid.at(dest);
|
var victims = this.grid.at(dest);
|
||||||
if (victims.length && victims[0].colour === piece.colour) return false;
|
if (victims.length && victims[0].colour == piece.colour) return false;
|
||||||
if (victims.length) victims[0].captured = true;
|
if (victims.length) victims[0].captured = true;
|
||||||
|
|
||||||
this.grid.remove(piece, piece.coord);
|
this.grid.remove(piece, piece.coord);
|
||||||
@@ -25,7 +25,7 @@ MovementSystem.prototype.tryMove = function (piece, to) {
|
|||||||
piece.coord.x = dest.x;
|
piece.coord.x = dest.x;
|
||||||
piece.coord.y = dest.y;
|
piece.coord.y = dest.y;
|
||||||
|
|
||||||
this.turn = (this.turn === 'white') ? 'black' : 'white';
|
this.turn = (this.turn == 'white') ? 'black' : 'white';
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
45
prosperon/examples/chess/rules.cm
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/* helper – robust coord access */
|
||||||
|
function cx(c) { return c.x ?? c[0] }
|
||||||
|
function cy(c) { return c.y ?? c[1] }
|
||||||
|
|
||||||
|
/* simple move-shape checks */
|
||||||
|
var deltas = {
|
||||||
|
pawn: function (pc, dx, dy, grid, to) {
|
||||||
|
var dir = (pc.colour == 'white') ? -1 : 1;
|
||||||
|
var base = (pc.colour == 'white') ? 6 : 1;
|
||||||
|
var one = (dy == dir && dx == 0 && grid.at(to).length == 0);
|
||||||
|
var two = (dy == 2 * dir && dx == 0 && cy(pc.coord) == base &&
|
||||||
|
grid.at({ x: cx(pc.coord), y: cy(pc.coord)+dir }).length == 0 &&
|
||||||
|
grid.at(to).length == 0);
|
||||||
|
var cap = (dy == dir && Math.abs(dx) == 1 && grid.at(to).length);
|
||||||
|
return one || two || cap;
|
||||||
|
},
|
||||||
|
rook : function (pc, dx, dy) { return (dx == 0 || dy == 0); },
|
||||||
|
bishop: function (pc, dx, dy) { return Math.abs(dx) == Math.abs(dy); },
|
||||||
|
queen : function (pc, dx, dy) { return (dx == 0 || dy == 0 || Math.abs(dx) == Math.abs(dy)); },
|
||||||
|
knight: function (pc, dx, dy) { return (Math.abs(dx) == 1 && Math.abs(dy) == 2) ||
|
||||||
|
(Math.abs(dx) == 2 && Math.abs(dy) == 1); },
|
||||||
|
king : function (pc, dx, dy) { return Math.max(Math.abs(dx), Math.abs(dy)) == 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
function clearLine(from, to, grid) {
|
||||||
|
var dx = Math.sign(cx(to) - cx(from));
|
||||||
|
var dy = Math.sign(cy(to) - cy(from));
|
||||||
|
var x = cx(from) + dx, y = cy(from) + dy;
|
||||||
|
while (x != cx(to) || y != cy(to)) {
|
||||||
|
if (grid.at({ x: x, y: y }).length) return false;
|
||||||
|
x += dx; y += dy;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canMove(piece, from, to, grid) {
|
||||||
|
var dx = cx(to) - cx(from);
|
||||||
|
var dy = cy(to) - cy(from);
|
||||||
|
var f = deltas[piece.kind];
|
||||||
|
if (!f || !f(piece, dx, dy, grid, to)) return false;
|
||||||
|
if (piece.kind == 'knight') return true;
|
||||||
|
return clearLine(from, to, grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { canMove };
|
||||||
|
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 376 B |
|
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
@@ -2,6 +2,7 @@
|
|||||||
var draw = use('draw2d')
|
var draw = use('draw2d')
|
||||||
var input = use('controller')
|
var input = use('controller')
|
||||||
var config = use('config')
|
var config = use('config')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
prosperon.camera.transform.pos = [0,0]
|
prosperon.camera.transform.pos = [0,0]
|
||||||
|
|
||||||
@@ -73,13 +74,13 @@ this.hud = function() {
|
|||||||
draw.rectangle({x:0, y:0, width:config.width, height:config.height}, [0,0,0,1])
|
draw.rectangle({x:0, y:0, width:config.width, height:config.height}, [0,0,0,1])
|
||||||
|
|
||||||
// Draw paddles
|
// Draw paddles
|
||||||
draw.rectangle({x:p1.x - paddleW*0.5, y:p1.y - paddleH*0.5, width:paddleW, height:paddleH}, Color.white)
|
draw.rectangle({x:p1.x - paddleW*0.5, y:p1.y - paddleH*0.5, width:paddleW, height:paddleH}, color.white)
|
||||||
draw.rectangle({x:p2.x - paddleW*0.5, y:p2.y - paddleH*0.5, width:paddleW, height:paddleH}, Color.white)
|
draw.rectangle({x:p2.x - paddleW*0.5, y:p2.y - paddleH*0.5, width:paddleW, height:paddleH}, color.white)
|
||||||
|
|
||||||
// Draw ball
|
// Draw ball
|
||||||
draw.rectangle({x:ball.x - ball.size*0.5, y:ball.y - ball.size*0.5, width:ball.size, height:ball.size}, Color.white)
|
draw.rectangle({x:ball.x - ball.size*0.5, y:ball.y - ball.size*0.5, width:ball.size, height:ball.size}, color.white)
|
||||||
|
|
||||||
// Simple score display
|
// Simple score display
|
||||||
var msg = score1 + " " + score2
|
var msg = score1 + " " + score2
|
||||||
draw.text(msg, {x:0, y:10, width:config.width, height:40}, undefined, 0, Color.white, 0)
|
draw.text(msg, {x:0, y:10, width:config.width, height:40}, null, 0, color.white, 0)
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ var render = use('render')
|
|||||||
var graphics = use('graphics')
|
var graphics = use('graphics')
|
||||||
var input = use('input')
|
var input = use('input')
|
||||||
var config = use('config')
|
var config = use('config')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
prosperon.camera.transform.pos = [0,0]
|
prosperon.camera.transform.pos = [0,0]
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ function spawnApple() {
|
|||||||
apple = {x:Math.floor(Math.random()*gridW), y:Math.floor(Math.random()*gridH)}
|
apple = {x:Math.floor(Math.random()*gridW), y:Math.floor(Math.random()*gridH)}
|
||||||
// Re-spawn if apple lands on snake
|
// Re-spawn if apple lands on snake
|
||||||
for (var i=0; i<snake.length; i++)
|
for (var i=0; i<snake.length; i++)
|
||||||
if (snake[i].x === apple.x && snake[i].y === apple.y) { spawnApple(); return }
|
if (snake[i].x == apple.x && snake[i].y == apple.y) { spawnApple(); return }
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrap(pos) {
|
function wrap(pos) {
|
||||||
@@ -48,7 +49,7 @@ function wrap(pos) {
|
|||||||
resetGame()
|
resetGame()
|
||||||
|
|
||||||
this.update = function(dt) {
|
this.update = function(dt) {
|
||||||
if (gameState !== "playing") return
|
if (gameState != "playing") return
|
||||||
moveTimer += dt
|
moveTimer += dt
|
||||||
if (moveTimer < moveInterval) return
|
if (moveTimer < moveInterval) return
|
||||||
moveTimer -= moveInterval
|
moveTimer -= moveInterval
|
||||||
@@ -62,7 +63,7 @@ this.update = function(dt) {
|
|||||||
|
|
||||||
// Check collision with body
|
// Check collision with body
|
||||||
for (var i=0; i<snake.length; i++) {
|
for (var i=0; i<snake.length; i++) {
|
||||||
if (snake[i].x === head.x && snake[i].y === head.y) {
|
if (snake[i].x == head.x && snake[i].y == head.y) {
|
||||||
gameState = "gameover"
|
gameState = "gameover"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -72,7 +73,7 @@ this.update = function(dt) {
|
|||||||
snake.unshift(head)
|
snake.unshift(head)
|
||||||
|
|
||||||
// Eat apple?
|
// Eat apple?
|
||||||
if (head.x === apple.x && head.y === apple.y) spawnApple()
|
if (head.x == apple.x && head.y == apple.y) spawnApple()
|
||||||
else snake.pop()
|
else snake.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,15 +84,15 @@ this.hud = function() {
|
|||||||
// Draw snake
|
// Draw snake
|
||||||
for (var i=0; i<snake.length; i++) {
|
for (var i=0; i<snake.length; i++) {
|
||||||
var s = snake[i]
|
var s = snake[i]
|
||||||
draw.rectangle({x:s.x*cellSize, y:s.y*cellSize, width:cellSize, height:cellSize}, Color.green)
|
draw.rectangle({x:s.x*cellSize, y:s.y*cellSize, width:cellSize, height:cellSize}, color.green)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw apple
|
// Draw apple
|
||||||
draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, Color.red)
|
draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, color.red)
|
||||||
|
|
||||||
if (gameState === "gameover") {
|
if (gameState == "gameover") {
|
||||||
var msg = "GAME OVER! Press SPACE to restart."
|
var msg = "GAME OVER! Press SPACE to restart."
|
||||||
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, undefined, 0, Color.white)
|
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, null, 0, color.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,19 +100,19 @@ this.hud = function() {
|
|||||||
// "Up" means y=1, so going physically up on screen
|
// "Up" means y=1, so going physically up on screen
|
||||||
this.inputs = {
|
this.inputs = {
|
||||||
up: function() {
|
up: function() {
|
||||||
if (direction.y !== -1) nextDirection = {x:0,y:1}
|
if (direction.y != -1) nextDirection = {x:0,y:1}
|
||||||
},
|
},
|
||||||
down: function() {
|
down: function() {
|
||||||
if (direction.y !== 1) nextDirection = {x:0,y:-1}
|
if (direction.y != 1) nextDirection = {x:0,y:-1}
|
||||||
},
|
},
|
||||||
left: function() {
|
left: function() {
|
||||||
if (direction.x !== 1) nextDirection = {x:-1,y:0}
|
if (direction.x != 1) nextDirection = {x:-1,y:0}
|
||||||
},
|
},
|
||||||
right: function() {
|
right: function() {
|
||||||
if (direction.x !== -1) nextDirection = {x:1,y:0}
|
if (direction.x != -1) nextDirection = {x:1,y:0}
|
||||||
},
|
},
|
||||||
space: function() {
|
space: function() {
|
||||||
if (gameState==="gameover") resetGame()
|
if (gameState=="gameover") resetGame()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,23 +23,23 @@ var stats_loaded = false;
|
|||||||
// Initialize Steam
|
// Initialize Steam
|
||||||
function init_steam() {
|
function init_steam() {
|
||||||
if (!steam) {
|
if (!steam) {
|
||||||
console.log("Steam module not available");
|
log.console("Steam module not available");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Initializing Steam...");
|
log.console("Initializing Steam...");
|
||||||
steam_available = steam.steam_init();
|
steam_available = steam.steam_init();
|
||||||
|
|
||||||
if (steam_available) {
|
if (steam_available) {
|
||||||
console.log("Steam initialized successfully");
|
log.console("Steam initialized successfully");
|
||||||
|
|
||||||
// Request current stats/achievements
|
// Request current stats/achievements
|
||||||
if (steam.stats.stats_request()) {
|
if (steam.stats.stats_request()) {
|
||||||
console.log("Stats requested");
|
log.console("Stats requested");
|
||||||
stats_loaded = true;
|
stats_loaded = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("Failed to initialize Steam");
|
log.console("Failed to initialize Steam");
|
||||||
}
|
}
|
||||||
|
|
||||||
return steam_available;
|
return steam_available;
|
||||||
@@ -59,13 +59,13 @@ function unlock_achievement(achievement_name) {
|
|||||||
// Check if already unlocked
|
// Check if already unlocked
|
||||||
var unlocked = steam.achievement.achievement_get(achievement_name);
|
var unlocked = steam.achievement.achievement_get(achievement_name);
|
||||||
if (unlocked) {
|
if (unlocked) {
|
||||||
console.log("Achievement already unlocked:", achievement_name);
|
log.console("Achievement already unlocked:", achievement_name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock it
|
// Unlock it
|
||||||
if (steam.achievement.achievement_set(achievement_name)) {
|
if (steam.achievement.achievement_set(achievement_name)) {
|
||||||
console.log("Achievement unlocked:", achievement_name);
|
log.console("Achievement unlocked:", achievement_name);
|
||||||
|
|
||||||
// Store stats to make it permanent
|
// Store stats to make it permanent
|
||||||
steam.stats.stats_store();
|
steam.stats.stats_store();
|
||||||
@@ -87,7 +87,7 @@ function update_stat(stat_name, value, is_float) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log("Stat updated:", stat_name, "=", value);
|
log.console("Stat updated:", stat_name, "=", value);
|
||||||
steam.stats.stats_store();
|
steam.stats.stats_store();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ function start_game() {
|
|||||||
total_score = get_stat(STATS.TOTAL_SCORE, false);
|
total_score = get_stat(STATS.TOTAL_SCORE, false);
|
||||||
current_score = 0;
|
current_score = 0;
|
||||||
|
|
||||||
console.log("Starting game #" + (games_played + 1));
|
log.console("Starting game #" + (games_played + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
function end_game(score) {
|
function end_game(score) {
|
||||||
@@ -128,7 +128,7 @@ function end_game(score) {
|
|||||||
update_stat(STATS.TOTAL_SCORE, total_score, false);
|
update_stat(STATS.TOTAL_SCORE, total_score, false);
|
||||||
|
|
||||||
// Check for achievements
|
// Check for achievements
|
||||||
if (games_played === 1) {
|
if (games_played == 1) {
|
||||||
unlock_achievement(ACHIEVEMENTS.FIRST_WIN);
|
unlock_achievement(ACHIEVEMENTS.FIRST_WIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ function load_from_cloud() {
|
|||||||
function cleanup_steam() {
|
function cleanup_steam() {
|
||||||
if (steam_available) {
|
if (steam_available) {
|
||||||
steam.steam_shutdown();
|
steam.steam_shutdown();
|
||||||
console.log("Steam shut down");
|
log.console("Steam shut down");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
var draw = use('draw2d')
|
var draw = use('draw2d')
|
||||||
var input = use('input')
|
var input = use('input')
|
||||||
var config = use('config')
|
var config = use('config')
|
||||||
|
var color = use('color')
|
||||||
|
|
||||||
prosperon.camera.transform.pos = [0,0]
|
prosperon.camera.transform.pos = [0,0]
|
||||||
|
|
||||||
@@ -127,10 +128,10 @@ function clearLines() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Score
|
// Score
|
||||||
if (lines===1) score += 100
|
if (lines==1) score += 100
|
||||||
else if (lines===2) score += 300
|
else if (lines==2) score += 300
|
||||||
else if (lines===3) score += 500
|
else if (lines==3) score += 500
|
||||||
else if (lines===4) score += 800
|
else if (lines==4) score += 800
|
||||||
linesCleared += lines
|
linesCleared += lines
|
||||||
level = Math.floor(linesCleared/10)
|
level = Math.floor(linesCleared/10)
|
||||||
}
|
}
|
||||||
@@ -152,7 +153,7 @@ spawnPiece()
|
|||||||
this.update = function(dt) {
|
this.update = function(dt) {
|
||||||
if (gameOver) return
|
if (gameOver) return
|
||||||
|
|
||||||
// ========== Horizontal Movement Gate ==========
|
// ======= Horizontal Movement Gate =======
|
||||||
var leftPressed = input.keyboard.down('a')
|
var leftPressed = input.keyboard.down('a')
|
||||||
var rightPressed = input.keyboard.down('d')
|
var rightPressed = input.keyboard.down('d')
|
||||||
var horizontalMove = 0
|
var horizontalMove = 0
|
||||||
@@ -190,7 +191,7 @@ this.update = function(dt) {
|
|||||||
hMoveTimer -= dt
|
hMoveTimer -= dt
|
||||||
prevLeft = leftPressed
|
prevLeft = leftPressed
|
||||||
prevRight = rightPressed
|
prevRight = rightPressed
|
||||||
// ========== End Horizontal Movement Gate ==========
|
// ======= End Horizontal Movement Gate =======
|
||||||
|
|
||||||
// Rotate with W (once per press, no spinning)
|
// Rotate with W (once per press, no spinning)
|
||||||
if (input.keyboard.down('w')) {
|
if (input.keyboard.down('w')) {
|
||||||
@@ -248,7 +249,7 @@ this.hud = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next piece window
|
// Next piece window
|
||||||
draw.text("Next", {x:70, y:5, width:50, height:10}, undefined, 0, Color.white)
|
draw.text("Next", {x:70, y:5, width:50, height:10}, null, 0, color.white)
|
||||||
if (nextPiece) {
|
if (nextPiece) {
|
||||||
for (var i=0; i<nextPiece.blocks.length; i++) {
|
for (var i=0; i<nextPiece.blocks.length; i++) {
|
||||||
var nx = nextPiece.blocks[i][0]
|
var nx = nextPiece.blocks[i][0]
|
||||||
@@ -261,10 +262,10 @@ this.hud = function() {
|
|||||||
|
|
||||||
// Score & Level
|
// Score & Level
|
||||||
var info = "Score: " + score + "\nLines: " + linesCleared + "\nLevel: " + level
|
var info = "Score: " + score + "\nLines: " + linesCleared + "\nLevel: " + level
|
||||||
draw.text(info, {x:70, y:30, width:90, height:50}, undefined, 0, Color.white)
|
draw.text(info, {x:70, y:30, width:90, height:50}, null, 0, color.white)
|
||||||
|
|
||||||
if (gameOver) {
|
if (gameOver) {
|
||||||
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, undefined, 0, Color.red)
|
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, null, 0, color.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
var geometry = this
|
var geometry = this
|
||||||
geometry[prosperon.DOC] = `
|
geometry[cell.DOC] = `
|
||||||
A collection of geometry-related functions for circles, spheres, boxes, polygons,
|
A collection of geometry-related functions for circles, spheres, boxes, polygons,
|
||||||
and rectangle utilities. Some functionality is implemented in C and exposed here.
|
and rectangle utilities. Some functionality is implemented in C and exposed here.
|
||||||
`
|
`
|
||||||
@@ -7,7 +7,7 @@ and rectangle utilities. Some functionality is implemented in C and exposed here
|
|||||||
var math = use('math')
|
var math = use('math')
|
||||||
|
|
||||||
geometry.box = {}
|
geometry.box = {}
|
||||||
geometry.box[prosperon.DOC] = `
|
geometry.box[cell.DOC] = `
|
||||||
An object for box-related operations. Overridden later by a function definition, so
|
An object for box-related operations. Overridden later by a function definition, so
|
||||||
its direct usage is overshadowed. Contains:
|
its direct usage is overshadowed. Contains:
|
||||||
- points(ll, ur): Return an array of four 2D points for a box from ll (lower-left) to ur (upper-right).
|
- points(ll, ur): Return an array of four 2D points for a box from ll (lower-left) to ur (upper-right).
|
||||||
@@ -16,7 +16,7 @@ its direct usage is overshadowed. Contains:
|
|||||||
geometry.box.points = function (ll, ur) {
|
geometry.box.points = function (ll, ur) {
|
||||||
return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])]
|
return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])]
|
||||||
}
|
}
|
||||||
geometry.box.points[prosperon.DOC] = `
|
geometry.box.points[cell.DOC] = `
|
||||||
:param ll: Lower-left coordinate as a 2D vector (x,y).
|
:param ll: Lower-left coordinate as a 2D vector (x,y).
|
||||||
:param ur: Upper-right coordinate as a 2D vector (x,y).
|
:param ur: Upper-right coordinate as a 2D vector (x,y).
|
||||||
:return: An array of four points forming the corners of the box in order [ll, lower-right, ur, upper-left].
|
:return: An array of four points forming the corners of the box in order [ll, lower-right, ur, upper-left].
|
||||||
@@ -24,14 +24,14 @@ Compute the four corners of a box given lower-left and upper-right corners.
|
|||||||
`
|
`
|
||||||
|
|
||||||
geometry.sphere = {}
|
geometry.sphere = {}
|
||||||
geometry.sphere[prosperon.DOC] = `
|
geometry.sphere[cell.DOC] = `
|
||||||
Sphere-related geometry functions:
|
Sphere-related geometry functions:
|
||||||
- volume(r): Return the volume of a sphere with radius r.
|
- volume(r): Return the volume of a sphere with radius r.
|
||||||
- random(r, theta, phi): Return a random point on or inside a sphere.
|
- random(r, theta, phi): Return a random point on or inside a sphere.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.circle = {}
|
geometry.circle = {}
|
||||||
geometry.circle[prosperon.DOC] = `
|
geometry.circle[cell.DOC] = `
|
||||||
Circle-related geometry functions:
|
Circle-related geometry functions:
|
||||||
- area(r): Return the area of a circle with radius r.
|
- area(r): Return the area of a circle with radius r.
|
||||||
- random(r, theta): Return a random 2D point on a circle; uses sphere.random internally and extracts x,z.
|
- random(r, theta): Return a random 2D point on a circle; uses sphere.random internally and extracts x,z.
|
||||||
@@ -40,22 +40,22 @@ Circle-related geometry functions:
|
|||||||
geometry.sphere.volume = function (r) {
|
geometry.sphere.volume = function (r) {
|
||||||
return (Math.pi * r * r * r * 4) / 3
|
return (Math.pi * r * r * r * 4) / 3
|
||||||
}
|
}
|
||||||
geometry.sphere.volume[prosperon.DOC] = `
|
geometry.sphere.volume[cell.DOC] = `
|
||||||
:param r: The sphere radius.
|
:param r: The sphere radius.
|
||||||
:return: The volume of the sphere, calculated as (4/3) * pi * r^3.
|
:return: The volume of the sphere, calculated as (4/3) * pi * r^3.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.sphere.random = function (r, theta = [0, 1], phi = [-0.5, 0.5]) {
|
geometry.sphere.random = function (r, theta = [0, 1], phi = [-0.5, 0.5]) {
|
||||||
if (typeof r === "number") r = [r, r]
|
if (typeof r == "number") r = [r, r]
|
||||||
if (typeof theta === "number") theta = [theta, theta]
|
if (typeof theta == "number") theta = [theta, theta]
|
||||||
if (typeof phi === "number") phi = [phi, phi]
|
if (typeof phi == "number") phi = [phi, phi]
|
||||||
|
|
||||||
var ra = Math.random_range(r[0], r[1])
|
var ra = Math.random_range(r[0], r[1])
|
||||||
var ta = Math.turn2rad(Math.random_range(theta[0], theta[1]))
|
var ta = Math.turn2rad(Math.random_range(theta[0], theta[1]))
|
||||||
var pa = Math.turn2rad(Math.random_range(phi[0], phi[1]))
|
var pa = Math.turn2rad(Math.random_range(phi[0], phi[1]))
|
||||||
return [ra * Math.sin(ta) * Math.cos(pa), ra * Math.sin(ta) * Math.sin(pa), ra * Math.cos(ta)]
|
return [ra * Math.sin(ta) * Math.cos(pa), ra * Math.sin(ta) * Math.sin(pa), ra * Math.cos(ta)]
|
||||||
}
|
}
|
||||||
geometry.sphere.random[prosperon.DOC] = `
|
geometry.sphere.random[cell.DOC] = `
|
||||||
:param r: A single number (radius) or a 2-element array [minRadius, maxRadius].
|
:param r: A single number (radius) or a 2-element array [minRadius, maxRadius].
|
||||||
:param theta: A single number or 2-element array defining the range in turns for the theta angle, default [0,1].
|
:param theta: A single number or 2-element array defining the range in turns for the theta angle, default [0,1].
|
||||||
:param phi: A single number or 2-element array defining the range in turns for the phi angle, default [-0.5,0.5].
|
:param phi: A single number or 2-element array defining the range in turns for the phi angle, default [-0.5,0.5].
|
||||||
@@ -66,7 +66,7 @@ Generate a random point inside a sphere of variable radius, distributing angles
|
|||||||
geometry.circle.area = function (r) {
|
geometry.circle.area = function (r) {
|
||||||
return Math.pi * r * r
|
return Math.pi * r * r
|
||||||
}
|
}
|
||||||
geometry.circle.area[prosperon.DOC] = `
|
geometry.circle.area[cell.DOC] = `
|
||||||
:param r: Radius of the circle.
|
:param r: Radius of the circle.
|
||||||
:return: The area, pi * r^2.
|
:return: The area, pi * r^2.
|
||||||
`
|
`
|
||||||
@@ -74,7 +74,7 @@ geometry.circle.area[prosperon.DOC] = `
|
|||||||
geometry.circle.random = function (r, theta) {
|
geometry.circle.random = function (r, theta) {
|
||||||
return geometry.sphere.random(r, theta).xz
|
return geometry.sphere.random(r, theta).xz
|
||||||
}
|
}
|
||||||
geometry.circle.random[prosperon.DOC] = `
|
geometry.circle.random[cell.DOC] = `
|
||||||
:param r: A radius or [minRadius, maxRadius].
|
:param r: A radius or [minRadius, maxRadius].
|
||||||
:param theta: Angle range in turns (single number or [min,max]).
|
:param theta: Angle range in turns (single number or [min,max]).
|
||||||
:return: A 2D point (x,z) in the circle, using the sphere random generator and ignoring y.
|
:return: A 2D point (x,z) in the circle, using the sphere random generator and ignoring y.
|
||||||
@@ -91,7 +91,7 @@ geometry.box = function (w, h) {
|
|||||||
]
|
]
|
||||||
return points
|
return points
|
||||||
}
|
}
|
||||||
geometry.box[prosperon.DOC] = `
|
geometry.box[cell.DOC] = `
|
||||||
:param w: The width of the box.
|
:param w: The width of the box.
|
||||||
:param h: The height of the box.
|
:param h: The height of the box.
|
||||||
:return: An array of four 2D points representing the corners of a rectangle centered at [0,0].
|
:return: An array of four 2D points representing the corners of a rectangle centered at [0,0].
|
||||||
@@ -101,7 +101,7 @@ Construct a box centered at the origin with the given width and height. This ove
|
|||||||
geometry.ngon = function (radius, n) {
|
geometry.ngon = function (radius, n) {
|
||||||
return geometry.arc(radius, 360, n)
|
return geometry.arc(radius, 360, n)
|
||||||
}
|
}
|
||||||
geometry.ngon[prosperon.DOC] = `
|
geometry.ngon[cell.DOC] = `
|
||||||
:param radius: The radius of the n-gon from center to each vertex.
|
:param radius: The radius of the n-gon from center to each vertex.
|
||||||
:param n: Number of sides/vertices.
|
:param n: Number of sides/vertices.
|
||||||
:return: An array of 2D points forming a regular n-gon.
|
:return: An array of 2D points forming a regular n-gon.
|
||||||
@@ -118,7 +118,7 @@ geometry.arc = function (radius, angle, n, start = 0) {
|
|||||||
for (var i = 0; i < n; i++) points.push(math.rotate([radius, 0], start + arclen * i))
|
for (var i = 0; i < n; i++) points.push(math.rotate([radius, 0], start + arclen * i))
|
||||||
return points
|
return points
|
||||||
}
|
}
|
||||||
geometry.arc[prosperon.DOC] = `
|
geometry.arc[cell.DOC] = `
|
||||||
:param radius: The distance from center to the arc points.
|
:param radius: The distance from center to the arc points.
|
||||||
:param angle: The total angle (in degrees) over which points are generated, capped at 360.
|
:param angle: The total angle (in degrees) over which points are generated, capped at 360.
|
||||||
:param n: Number of segments (if <=1, empty array is returned).
|
:param n: Number of segments (if <=1, empty array is returned).
|
||||||
@@ -131,7 +131,7 @@ geometry.circle.points = function (radius, n) {
|
|||||||
if (n <= 1) return []
|
if (n <= 1) return []
|
||||||
return geometry.arc(radius, 360, n)
|
return geometry.arc(radius, 360, n)
|
||||||
}
|
}
|
||||||
geometry.circle.points[prosperon.DOC] = `
|
geometry.circle.points[cell.DOC] = `
|
||||||
:param radius: The circle's radius.
|
:param radius: The circle's radius.
|
||||||
:param n: Number of points around the circle.
|
:param n: Number of points around the circle.
|
||||||
:return: An array of 2D points equally spaced around a full 360-degree circle.
|
:return: An array of 2D points equally spaced around a full 360-degree circle.
|
||||||
@@ -141,7 +141,7 @@ Shortcut for geometry.arc(radius, 360, n).
|
|||||||
geometry.corners2points = function (ll, ur) {
|
geometry.corners2points = function (ll, ur) {
|
||||||
return [ll, ll.add([ur.x, 0]), ur, ll.add([0, ur.y])]
|
return [ll, ll.add([ur.x, 0]), ur, ll.add([0, ur.y])]
|
||||||
}
|
}
|
||||||
geometry.corners2points[prosperon.DOC] = `
|
geometry.corners2points[cell.DOC] = `
|
||||||
:param ll: Lower-left 2D coordinate.
|
:param ll: Lower-left 2D coordinate.
|
||||||
:param ur: Upper-right 2D coordinate (relative offset in x,y).
|
:param ur: Upper-right 2D coordinate (relative offset in x,y).
|
||||||
:return: A four-point array of corners [ll, lower-right, upper-right, upper-left].
|
:return: A four-point array of corners [ll, lower-right, upper-right, upper-left].
|
||||||
@@ -158,7 +158,7 @@ geometry.sortpointsccw = function (points) {
|
|||||||
})
|
})
|
||||||
return ccw.map(function (x) { return x.add(cm) })
|
return ccw.map(function (x) { return x.add(cm) })
|
||||||
}
|
}
|
||||||
geometry.sortpointsccw[prosperon.DOC] = `
|
geometry.sortpointsccw[cell.DOC] = `
|
||||||
:param points: An array of 2D points.
|
:param points: An array of 2D points.
|
||||||
:return: A new array of the same points, sorted counterclockwise around their centroid.
|
:return: A new array of the same points, sorted counterclockwise around their centroid.
|
||||||
Sort an array of points in CCW order based on their angles from the centroid.
|
Sort an array of points in CCW order based on their angles from the centroid.
|
||||||
@@ -185,61 +185,61 @@ geometry.points2cm = function(points) {
|
|||||||
})
|
})
|
||||||
return [x / n, y / n]
|
return [x / n, y / n]
|
||||||
}
|
}
|
||||||
geometry.points2cm[prosperon.DOC] = `
|
geometry.points2cm[cell.DOC] = `
|
||||||
:param points: An array of 2D points.
|
:param points: An array of 2D points.
|
||||||
:return: The centroid (average x,y) of the given points.
|
:return: The centroid (average x,y) of the given points.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_intersection[prosperon.DOC] = `
|
geometry.rect_intersection[cell.DOC] = `
|
||||||
:param a: The first rectangle as {x, y, w, h}.
|
:param a: The first rectangle as {x, y, w, h}.
|
||||||
:param b: The second rectangle as {x, y, w, h}.
|
:param b: The second rectangle as {x, y, w, h}.
|
||||||
:return: A rectangle that is the intersection of the two. May have zero width/height if no overlap.
|
:return: A rectangle that is the intersection of the two. May have zero width/height if no overlap.
|
||||||
Return the intersection of two rectangles. The result may be empty if no intersection.
|
Return the intersection of two rectangles. The result may be empty if no intersection.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_intersects[prosperon.DOC] = `
|
geometry.rect_intersects[cell.DOC] = `
|
||||||
:param a: Rectangle {x,y,w,h}.
|
:param a: Rectangle {x,y,w,h}.
|
||||||
:param b: Rectangle {x,y,w,h}.
|
:param b: Rectangle {x,y,w,h}.
|
||||||
:return: A boolean indicating whether the two rectangles overlap.
|
:return: A boolean indicating whether the two rectangles overlap.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_expand[prosperon.DOC] = `
|
geometry.rect_expand[cell.DOC] = `
|
||||||
:param a: Rectangle {x,y,w,h}.
|
:param a: Rectangle {x,y,w,h}.
|
||||||
:param b: Rectangle {x,y,w,h}.
|
:param b: Rectangle {x,y,w,h}.
|
||||||
:return: A new rectangle that covers the bounds of both input rectangles.
|
:return: A new rectangle that covers the bounds of both input rectangles.
|
||||||
Merge or combine two rectangles, returning their bounding rectangle.
|
Merge or combine two rectangles, returning their bounding rectangle.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_inside[prosperon.DOC] = `
|
geometry.rect_inside[cell.DOC] = `
|
||||||
:param inner: A rectangle to test.
|
:param inner: A rectangle to test.
|
||||||
:param outer: A rectangle that may contain 'inner'.
|
:param outer: A rectangle that may contain 'inner'.
|
||||||
:return: True if 'inner' is completely inside 'outer', otherwise false.
|
:return: True if 'inner' is completely inside 'outer', otherwise false.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_random[prosperon.DOC] = `
|
geometry.rect_random[cell.DOC] = `
|
||||||
:param rect: A rectangle {x,y,w,h}.
|
:param rect: A rectangle {x,y,w,h}.
|
||||||
:return: A random point within the rectangle (uniform distribution).
|
:return: A random point within the rectangle (uniform distribution).
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.cwh2rect[prosperon.DOC] = `
|
geometry.cwh2rect[cell.DOC] = `
|
||||||
:param center: A 2D point [cx, cy].
|
:param center: A 2D point [cx, cy].
|
||||||
:param wh: A 2D size [width, height].
|
:param wh: A 2D size [width, height].
|
||||||
:return: A rectangle {x, y, w, h} with x,y set to center and w,h set to the given size.
|
:return: A rectangle {x, y, w, h} with x,y set to center and w,h set to the given size.
|
||||||
Helper: convert a center point and width/height vector to a rect object.
|
Helper: convert a center point and width/height vector to a rect object.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_point_inside[prosperon.DOC] = `
|
geometry.rect_point_inside[cell.DOC] = `
|
||||||
:param rect: A rectangle {x,y,w,h}.
|
:param rect: A rectangle {x,y,w,h}.
|
||||||
:param point: A 2D point [px, py].
|
:param point: A 2D point [px, py].
|
||||||
:return: True if the point lies inside the rectangle, otherwise false.
|
:return: True if the point lies inside the rectangle, otherwise false.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_pos[prosperon.DOC] = `
|
geometry.rect_pos[cell.DOC] = `
|
||||||
:param rect: A rectangle {x,y,w,h}.
|
:param rect: A rectangle {x,y,w,h}.
|
||||||
:return: A 2D vector [x,y] giving the rectangle's position.
|
:return: A 2D vector [x,y] giving the rectangle's position.
|
||||||
`
|
`
|
||||||
|
|
||||||
geometry.rect_move[prosperon.DOC] = `
|
geometry.rect_move[cell.DOC] = `
|
||||||
:param rect: A rectangle {x,y,w,h}.
|
:param rect: A rectangle {x,y,w,h}.
|
||||||
:param offset: A 2D vector to add to the rectangle's position.
|
:param offset: A 2D vector to add to the rectangle's position.
|
||||||
:return: A new rectangle with updated x,y offset.
|
:return: A new rectangle with updated x,y offset.
|
||||||
@@ -1,59 +1,99 @@
|
|||||||
var graphics = this
|
var graphics = this
|
||||||
|
|
||||||
graphics[prosperon.DOC] = `
|
graphics[cell.DOC] = `
|
||||||
Provides functionality for loading and managing images, fonts, textures, and sprite meshes.
|
Provides functionality for loading and managing images, fonts, textures, and sprite meshes.
|
||||||
Includes both JavaScript and C-implemented routines for creating geometry buffers, performing
|
Includes both JavaScript and C-implemented routines for creating geometry buffers, performing
|
||||||
rectangle packing, etc.
|
rectangle packing, etc.
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var renderer_actor = arg[0]
|
||||||
|
|
||||||
var io = use('io')
|
var io = use('io')
|
||||||
var os = use('os')
|
var time = use('time')
|
||||||
var res = use('resources')
|
var res = use('resources')
|
||||||
var render = use('render')
|
var json = use('json')
|
||||||
|
|
||||||
var GPU = Symbol()
|
var GPU = Symbol()
|
||||||
var CPU = Symbol()
|
var CPU = Symbol()
|
||||||
var LASTUSE = Symbol()
|
var LASTUSE = Symbol()
|
||||||
|
var LOADING = Symbol()
|
||||||
|
|
||||||
var cache = new Map()
|
var cache = {}
|
||||||
|
|
||||||
// When creating an image, do an Object.create(graphics.Image)
|
// Image constructor function
|
||||||
graphics.Image = {
|
graphics.Image = function(surfaceData) {
|
||||||
get gpu() {
|
// Initialize private properties
|
||||||
this[LASTUSE] = os.now();
|
this[CPU] = surfaceData || null;
|
||||||
if (!this[GPU]) {
|
this[GPU] = null;
|
||||||
this[GPU] = render.load_texture(this[CPU]);
|
this[LOADING] = false;
|
||||||
decorate_rect_px(this);
|
this[LASTUSE] = time.number();
|
||||||
|
this.rect = {x:0, y:0, width:surfaceData.width, height:surfaceData.height};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define getters and methods on the prototype
|
||||||
|
Object.defineProperties(graphics.Image.prototype, {
|
||||||
|
gpu: {
|
||||||
|
get: function() {
|
||||||
|
if (!this[GPU] && !this[LOADING]) {
|
||||||
|
this[LOADING] = true;
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Send message to load texture
|
||||||
|
send(renderer_actor, {
|
||||||
|
kind: "renderer",
|
||||||
|
op: "loadTexture",
|
||||||
|
data: this[CPU]
|
||||||
|
}, function(response) {
|
||||||
|
if (response.error) {
|
||||||
|
log.error("Failed to load texture:")
|
||||||
|
log.error(response.error)
|
||||||
|
self[LOADING] = false;
|
||||||
|
} else {
|
||||||
|
self[GPU] = response;
|
||||||
|
decorate_rect_px(self);
|
||||||
|
self[LOADING] = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this[GPU]
|
||||||
}
|
}
|
||||||
|
|
||||||
return this[GPU]
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get texture() { return this.gpu },
|
texture: {
|
||||||
|
get: function() { return this.gpu }
|
||||||
|
},
|
||||||
|
|
||||||
get cpu() {
|
cpu: {
|
||||||
this[LASTUSE] = os.now();
|
get: function() {
|
||||||
if (!this[CPU]) this[CPU] = render.read_texture(this[GPU])
|
return this[CPU]
|
||||||
return this[CPU]
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get surface() { return this.cpu },
|
surface: {
|
||||||
|
get: function() { return this.cpu }
|
||||||
get width() {
|
|
||||||
return this[GPU].width
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get height() {
|
width: {
|
||||||
return this[GPU].height
|
get: function() {
|
||||||
|
return this[CPU].width
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unload_gpu() {
|
height: {
|
||||||
this[GPU] = undefined
|
get: function() {
|
||||||
},
|
return this[CPU].height
|
||||||
|
}
|
||||||
unload_cpu() {
|
}
|
||||||
this[CPU] = undefined
|
});
|
||||||
},
|
|
||||||
|
// Add methods to prototype
|
||||||
|
graphics.Image.prototype.unload_gpu = function() {
|
||||||
|
this[GPU] = null
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.Image.prototype.unload_cpu = function() {
|
||||||
|
this[CPU] = null
|
||||||
}
|
}
|
||||||
|
|
||||||
function calc_image_size(img) {
|
function calc_image_size(img) {
|
||||||
@@ -79,21 +119,11 @@ function decorate_rect_px(img) {
|
|||||||
|
|
||||||
function make_handle(obj)
|
function make_handle(obj)
|
||||||
{
|
{
|
||||||
var image = Object.create(graphics.Image);
|
return new graphics.Image(obj);
|
||||||
|
|
||||||
if (obj.surface) {
|
|
||||||
im
|
|
||||||
}
|
|
||||||
return Object.assign(Object.create(graphics.Image), {
|
|
||||||
rect:{x:0,y:0,width:1,height:1},
|
|
||||||
[CPU]:obj,
|
|
||||||
[GPU]:undefined,
|
|
||||||
[LASTUSE]:os.now()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapSurface(surf, maybeRect){
|
function wrapSurface(surf, maybeRect){
|
||||||
const h = make_handle(surf);
|
def h = make_handle(surf);
|
||||||
if(maybeRect) h.rect = maybeRect; /* honour frame sub-rect */
|
if(maybeRect) h.rect = maybeRect; /* honour frame sub-rect */
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
@@ -120,12 +150,15 @@ function decode_image(bytes, ext)
|
|||||||
|
|
||||||
function create_image(path){
|
function create_image(path){
|
||||||
try{
|
try{
|
||||||
const bytes = io.slurpbytes(path);
|
def bytes = io.slurpbytes(path);
|
||||||
let raw = decode_image(bytes, path.ext());
|
|
||||||
|
|
||||||
|
let raw = decode_image(bytes, path.ext());
|
||||||
|
|
||||||
/* ── Case A: static image ─────────────────────────────────── */
|
/* ── Case A: static image ─────────────────────────────────── */
|
||||||
if(raw.surface)
|
if(raw.surface) {
|
||||||
return make_handle(raw.surface);
|
var gg = new graphics.Image(raw.surface)
|
||||||
|
return gg
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Case B: GIF helpers returned array [surf, …] ─────────── */
|
/* ── Case B: GIF helpers returned array [surf, …] ─────────── */
|
||||||
if(Array.isArray(raw))
|
if(Array.isArray(raw))
|
||||||
@@ -136,8 +169,8 @@ function create_image(path){
|
|||||||
return makeAnim( wrapFrames(raw.frames), !!raw.loop );
|
return makeAnim( wrapFrames(raw.frames), !!raw.loop );
|
||||||
|
|
||||||
/* ── Case D: ASE helpers returned { animName:{frames,loop}, … } ── */
|
/* ── Case D: ASE helpers returned { animName:{frames,loop}, … } ── */
|
||||||
const anims = {};
|
def anims = {};
|
||||||
for(const [name, anim] of Object.entries(raw)){
|
for(def [name, anim] of Object.entries(raw)){
|
||||||
if(anim && Array.isArray(anim.frames))
|
if(anim && Array.isArray(anim.frames))
|
||||||
anims[name] = makeAnim( wrapFrames(anim.frames), !!anim.loop );
|
anims[name] = makeAnim( wrapFrames(anim.frames), !!anim.loop );
|
||||||
else if(anim && anim.surface) /* ase with flat surface */
|
else if(anim && anim.surface) /* ase with flat surface */
|
||||||
@@ -149,7 +182,7 @@ function create_image(path){
|
|||||||
throw new Error('Unsupported image structure from decoder');
|
throw new Error('Unsupported image structure from decoder');
|
||||||
|
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error(`Error loading image ${path}: ${e.message}`);
|
log.error(`Error loading image ${path}: ${e.message}`);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +191,7 @@ var image = {}
|
|||||||
image.dimensions = function() {
|
image.dimensions = function() {
|
||||||
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]])
|
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]])
|
||||||
}
|
}
|
||||||
image.dimensions[prosperon.DOC] = `
|
image.dimensions[cell.DOC] = `
|
||||||
:return: A 2D array [width, height] that is the scaled size of this image (texture size * rect size).
|
:return: A 2D array [width, height] that is the scaled size of this image (texture size * rect size).
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -179,33 +212,31 @@ function pack_into_sheet(images) {
|
|||||||
graphics.is_image = function(obj) {
|
graphics.is_image = function(obj) {
|
||||||
if (obj.texture && obj.rect) return true
|
if (obj.texture && obj.rect) return true
|
||||||
}
|
}
|
||||||
graphics.is_image[prosperon.DOC] = `
|
graphics.is_image[cell.DOC] = `
|
||||||
:param obj: An object to check.
|
:param obj: An object to check.
|
||||||
:return: True if 'obj' has a .texture and a .rect property, indicating it's an image object.
|
:return: True if 'obj' has a .texture and a .rect property, indicating it's an image object.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.texture_from_data = function(data)
|
graphics.texture_from_data = function(data)
|
||||||
{
|
{
|
||||||
if (!(data instanceof ArrayBuffer)) return undefined
|
if (!(data instanceof ArrayBuffer)) return null
|
||||||
|
|
||||||
var img = {
|
var image = graphics.make_texture(data);
|
||||||
surface: graphics.make_texture(data)
|
var img = make_handle(image)
|
||||||
}
|
|
||||||
render.load_texture(img)
|
img.gpu;
|
||||||
decorate_rect_px(img)
|
|
||||||
|
|
||||||
return img
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics.from_surface = function(id, surf)
|
graphics.from_surface = function(id, surf)
|
||||||
{
|
{
|
||||||
return make_handle(surf)
|
return make_handle(surf)
|
||||||
var img = { surface: surf }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics.from = function(id, data)
|
graphics.from = function(id, data)
|
||||||
{
|
{
|
||||||
if (typeof id !== 'string')
|
if (typeof id != 'string')
|
||||||
throw new Error('Expected a string ID')
|
throw new Error('Expected a string ID')
|
||||||
|
|
||||||
if (data instanceof ArrayBuffer)
|
if (data instanceof ArrayBuffer)
|
||||||
@@ -213,22 +244,23 @@ graphics.from = function(id, data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
graphics.texture = function texture(path) {
|
graphics.texture = function texture(path) {
|
||||||
if (path.__proto__ === graphics.Image) return path
|
if (path instanceof graphics.Image) return path
|
||||||
|
|
||||||
if (typeof path !== 'string')
|
if (typeof path != 'string')
|
||||||
throw new Error('need a string for graphics.texture')
|
throw new Error('need a string for graphics.texture')
|
||||||
|
|
||||||
var id = path.split(':')[0]
|
var id = path //.split(':')[0]
|
||||||
if (cache.has(id)) return cache.get(id)
|
if (cache[id]) return cache[id]
|
||||||
|
|
||||||
var ipath = res.find_image(id)
|
var ipath = res.find_image(id)
|
||||||
if (!ipath) throw new Error(`unknown image ${id}`)
|
if (!ipath)
|
||||||
|
throw new Error(`unknown image ${id}`)
|
||||||
|
|
||||||
var image = create_image(ipath)
|
var image = create_image(ipath)
|
||||||
cache.set(id, image)
|
cache[id] = image
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
graphics.texture[prosperon.DOC] = `
|
graphics.texture[cell.DOC] = `
|
||||||
:param path: A string path to an image file or an already-loaded image object.
|
:param path: A string path to an image file or an already-loaded image object.
|
||||||
:return: An image object with {surface, texture, frames?, etc.} depending on the format.
|
:return: An image object with {surface, texture, frames?, etc.} depending on the format.
|
||||||
Load or retrieve a cached image, converting it into a GPU texture. If 'path' is already an object, it’s returned directly.
|
Load or retrieve a cached image, converting it into a GPU texture. If 'path' is already an object, it’s returned directly.
|
||||||
@@ -242,7 +274,7 @@ graphics.texture.total_size = function() {
|
|||||||
// Not yet implemented, presumably sum of (texture.width * texture.height * 4) for images in RAM
|
// Not yet implemented, presumably sum of (texture.width * texture.height * 4) for images in RAM
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
graphics.texture.total_size[prosperon.DOC] = `
|
graphics.texture.total_size[cell.DOC] = `
|
||||||
:return: The total estimated memory size of all cached textures in RAM, in bytes. (Not yet implemented.)
|
:return: The total estimated memory size of all cached textures in RAM, in bytes. (Not yet implemented.)
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -251,23 +283,23 @@ graphics.texture.total_vram = function() {
|
|||||||
// Not yet implemented, presumably sum of GPU memory usage
|
// Not yet implemented, presumably sum of GPU memory usage
|
||||||
return vram
|
return vram
|
||||||
}
|
}
|
||||||
graphics.texture.total_vram[prosperon.DOC] = `
|
graphics.texture.total_vram[cell.DOC] = `
|
||||||
:return: The total estimated GPU memory usage of all cached textures, in bytes. (Not yet implemented.)
|
:return: The total estimated GPU memory usage of all cached textures, in bytes. (Not yet implemented.)
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.tex_hotreload = function tex_hotreload(file) {
|
graphics.tex_hotreload = function tex_hotreload(file) {
|
||||||
console.log(`hot reloading ${file}`)
|
log.console(`hot reloading ${file}`)
|
||||||
if (!(file in graphics.texture.cache)) return
|
if (!(file in graphics.texture.cache)) return
|
||||||
console.log('really doing it')
|
log.console('really doing it')
|
||||||
|
|
||||||
var img = create_image(file)
|
var img = create_image(file)
|
||||||
var oldimg = graphics.texture.cache[file]
|
var oldimg = graphics.texture.cache[file]
|
||||||
console.log(`new image:${json.encode(img)}`)
|
log.console(`new image:${json.encode(img)}`)
|
||||||
console.log(`old image: ${json.encode(oldimg)}`)
|
log.console(`old image: ${json.encode(oldimg)}`)
|
||||||
|
|
||||||
merge_objects(oldimg, img, ['surface', 'texture', 'loop', 'time'])
|
merge_objects(oldimg, img, ['surface', 'texture', 'loop', 'time'])
|
||||||
}
|
}
|
||||||
graphics.tex_hotreload[prosperon.DOC] = `
|
graphics.tex_hotreload[cell.DOC] = `
|
||||||
:param file: The file path that was changed on disk.
|
:param file: The file path that was changed on disk.
|
||||||
:return: None
|
:return: None
|
||||||
Reload the image for the given file, updating the cached copy in memory and GPU.
|
Reload the image for the given file, updating the cached copy in memory and GPU.
|
||||||
@@ -307,16 +339,25 @@ graphics.get_font = function get_font(path, size) {
|
|||||||
|
|
||||||
var data = io.slurpbytes(fullpath)
|
var data = io.slurpbytes(fullpath)
|
||||||
var font = graphics.make_font(data,size)
|
var font = graphics.make_font(data,size)
|
||||||
font.texture = render.load_texture(font.surface)
|
|
||||||
|
|
||||||
console.log('loaded font texture')
|
// Load font texture via renderer actor (async)
|
||||||
console.log(json.encode(font.texture))
|
send(renderer_actor, {
|
||||||
|
kind: "renderer",
|
||||||
|
op: "loadTexture",
|
||||||
|
data: font.surface
|
||||||
|
}, function(response) {
|
||||||
|
if (response.error) {
|
||||||
|
log.error("Failed to load font texture:", response.error);
|
||||||
|
} else {
|
||||||
|
font.texture = response;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
fontcache[fontstr] = font
|
fontcache[fontstr] = font
|
||||||
|
|
||||||
return font
|
return font
|
||||||
}
|
}
|
||||||
graphics.get_font[prosperon.DOC] = `
|
graphics.get_font[cell.DOC] = `
|
||||||
:param path: A string path to a font file, optionally with ".size" appended.
|
:param path: A string path to a font file, optionally with ".size" appended.
|
||||||
:param size: Pixel size of the font, if not included in 'path'.
|
:param size: Pixel size of the font, if not included in 'path'.
|
||||||
:return: A font object with .surface and .texture for rendering text.
|
:return: A font object with .surface and .texture for rendering text.
|
||||||
@@ -324,8 +365,8 @@ Load a font from file if not cached, or retrieve from cache if already loaded.
|
|||||||
`
|
`
|
||||||
|
|
||||||
graphics.queue_sprite_mesh = function(queue) {
|
graphics.queue_sprite_mesh = function(queue) {
|
||||||
var sprites = queue.filter(x => x.type === 'sprite')
|
var sprites = queue.filter(x => x.type == 'sprite')
|
||||||
if (sprites.length === 0) return []
|
if (sprites.length == 0) return []
|
||||||
var mesh = graphics.make_sprite_mesh(sprites)
|
var mesh = graphics.make_sprite_mesh(sprites)
|
||||||
for (var i = 0; i < sprites.length; i++) {
|
for (var i = 0; i < sprites.length; i++) {
|
||||||
sprites[i].mesh = mesh
|
sprites[i].mesh = mesh
|
||||||
@@ -334,31 +375,14 @@ graphics.queue_sprite_mesh = function(queue) {
|
|||||||
}
|
}
|
||||||
return [mesh.pos, mesh.uv, mesh.color, mesh.indices]
|
return [mesh.pos, mesh.uv, mesh.color, mesh.indices]
|
||||||
}
|
}
|
||||||
graphics.queue_sprite_mesh[prosperon.DOC] = `
|
graphics.queue_sprite_mesh[cell.DOC] = `
|
||||||
:param queue: An array of draw commands, some of which are {type:'sprite'} objects.
|
:param queue: An array of draw commands, some of which are {type:'sprite'} objects.
|
||||||
:return: An array of references to GPU buffers [pos,uv,color,indices].
|
:return: An array of references to GPU buffers [pos,uv,color,indices].
|
||||||
Builds a single geometry mesh for all sprite-type commands in the queue, storing first_index/num_indices
|
Builds a single geometry mesh for all sprite-type commands in the queue, storing first_index/num_indices
|
||||||
so they can be rendered in one draw call.
|
so they can be rendered in one draw call.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_sprite_mesh[prosperon.DOC] = `
|
graphics.make_text_buffer[cell.DOC] = `
|
||||||
:param sprites: An array of sprite objects, each containing .rect (or transform), .src (UV region), .color, etc.
|
|
||||||
:param oldMesh (optional): An existing mesh object to reuse/resize if possible.
|
|
||||||
:return: A GPU mesh object with pos, uv, color, and indices buffers for all sprites.
|
|
||||||
Given an array of sprites, build a single geometry mesh for rendering them.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_sprite_queue[prosperon.DOC] = `
|
|
||||||
:param sprites: An array of sprite objects.
|
|
||||||
:param camera: (unused in the C code example) Typically a camera or transform for sorting?
|
|
||||||
:param pipeline: A pipeline object for rendering.
|
|
||||||
:param sort: An integer or boolean for whether to sort sprites; if truthy, sorts by layer & texture.
|
|
||||||
:return: An array of pipeline commands: geometry with mesh references, grouped by image.
|
|
||||||
Given an array of sprites, optionally sort them, then build a queue of pipeline commands.
|
|
||||||
Each group with a shared image becomes one command.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_text_buffer[prosperon.DOC] = `
|
|
||||||
:param text: The string to render.
|
:param text: The string to render.
|
||||||
:param rect: A rectangle specifying position and possibly wrapping.
|
:param rect: A rectangle specifying position and possibly wrapping.
|
||||||
:param angle: Rotation angle (unused or optional).
|
:param angle: Rotation angle (unused or optional).
|
||||||
@@ -369,7 +393,7 @@ graphics.make_text_buffer[prosperon.DOC] = `
|
|||||||
Generate a GPU buffer mesh of text quads for rendering with a font, etc.
|
Generate a GPU buffer mesh of text quads for rendering with a font, etc.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.rectpack[prosperon.DOC] = `
|
graphics.rectpack[cell.DOC] = `
|
||||||
:param width: The width of the area to pack into.
|
:param width: The width of the area to pack into.
|
||||||
:param height: The height of the area to pack into.
|
:param height: The height of the area to pack into.
|
||||||
:param sizes: An array of [w,h] pairs for the rectangles to pack.
|
:param sizes: An array of [w,h] pairs for the rectangles to pack.
|
||||||
@@ -377,50 +401,39 @@ graphics.rectpack[prosperon.DOC] = `
|
|||||||
Perform a rectangle packing using the stbrp library. Return positions for each rect.
|
Perform a rectangle packing using the stbrp library. Return positions for each rect.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_texture[prosperon.DOC] = `
|
graphics.make_texture[cell.DOC] = `
|
||||||
:param data: Raw image bytes (PNG, JPG, etc.) as an ArrayBuffer.
|
:param data: Raw image bytes (PNG, JPG, etc.) as an ArrayBuffer.
|
||||||
:return: An SDL_Surface object representing the decoded image in RAM, for use with GPU or software rendering.
|
:return: An SDL_Surface object representing the decoded image in RAM, for use with GPU or software rendering.
|
||||||
Convert raw image bytes into an SDL_Surface object.
|
Convert raw image bytes into an SDL_Surface object.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_gif[prosperon.DOC] = `
|
graphics.make_gif[cell.DOC] = `
|
||||||
:param data: An ArrayBuffer containing GIF data.
|
:param data: An ArrayBuffer containing GIF data.
|
||||||
:return: An object with frames[], each frame having its own .surface. Some also have a .texture for GPU use.
|
:return: An object with frames[], each frame having its own .surface. Some also have a .texture for GPU use.
|
||||||
Load a GIF, returning its frames. If it's a single-frame GIF, the result may have .surface only.
|
Load a GIF, returning its frames. If it's a single-frame GIF, the result may have .surface only.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_aseprite[prosperon.DOC] = `
|
graphics.make_aseprite[cell.DOC] = `
|
||||||
:param data: An ArrayBuffer containing Aseprite (ASE) file data.
|
:param data: An ArrayBuffer containing Aseprite (ASE) file data.
|
||||||
:return: An object containing frames or animations, each with .surface. May also have top-level .surface for a single-layer case.
|
:return: An object containing frames or animations, each with .surface. May also have top-level .surface for a single-layer case.
|
||||||
Load an Aseprite/ASE file from an array of bytes, returning frames or animations.
|
Load an Aseprite/ASE file from an array of bytes, returning frames or animations.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.cull_sprites[prosperon.DOC] = `
|
graphics.cull_sprites[cell.DOC] = `
|
||||||
:param sprites: An array of sprite objects (each has rect or transform).
|
:param sprites: An array of sprite objects (each has rect or transform).
|
||||||
:param camera: A camera or bounding rectangle defining the view area.
|
:param camera: A camera or bounding rectangle defining the view area.
|
||||||
:return: A new array of sprites that are visible in the camera's view.
|
:return: A new array of sprites that are visible in the camera's view.
|
||||||
Filter an array of sprites to only those visible in the provided camera’s view.
|
Filter an array of sprites to only those visible in the provided camera’s view.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_surface[prosperon.DOC] = `
|
graphics.make_font[cell.DOC] = `
|
||||||
:param dimensions: The size object {width, height}, or an array [w,h].
|
|
||||||
:return: A blank RGBA surface with the given dimensions, typically for software rendering or icons.
|
|
||||||
Create a blank surface in RAM.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_cursor[prosperon.DOC] = `
|
|
||||||
:param opts: An object with {surface, hotx, hoty} or similar.
|
|
||||||
:return: An SDL_Cursor object referencing the given surface for a custom mouse cursor.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_font[prosperon.DOC] = `
|
|
||||||
:param data: TTF/OTF file data as an ArrayBuffer.
|
:param data: TTF/OTF file data as an ArrayBuffer.
|
||||||
:param size: Pixel size for rendering glyphs.
|
:param size: Pixel size for rendering glyphs.
|
||||||
:return: A font object with surface, texture, and glyph data, for text rendering with make_text_buffer.
|
:return: A font object with surface, texture, and glyph data, for text rendering with make_text_buffer.
|
||||||
Load a font from TTF/OTF data at the given size.
|
Load a font from TTF/OTF data at the given size.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_line_prim[prosperon.DOC] = `
|
graphics.make_line_prim[cell.DOC] = `
|
||||||
:param points: An array of [x,y] points forming the line.
|
:param points: An array of [x,y] points forming the line.
|
||||||
:param thickness: The thickness (width) of the polyline.
|
:param thickness: The thickness (width) of the polyline.
|
||||||
:param startCap: (Unused) Possibly the type of cap for the start.
|
:param startCap: (Unused) Possibly the type of cap for the start.
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 867 B After Width: | Height: | Size: 867 B |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |