-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit: Closed loop of TS->Rust->TS
- Loading branch information
0 parents
commit 856bbf4
Showing
14 changed files
with
701 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.sublime* | ||
/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
name = "emgui" | ||
version = "0.1.0" | ||
authors = ["Emil Ernerfeldt <emilernerfeldt@gmail.com>"] | ||
|
||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
|
||
[dependencies] | ||
# rand = { version="0.6", features = ['wasm-bindgen'] } | ||
serde = "1" | ||
serde_derive = "1" | ||
serde_json = "1" | ||
wasm-bindgen = "0.2" | ||
web-sys = { version = "0.3.5", features = ['console', 'Performance', 'Window'] } | ||
|
||
# Optimize for small code size: | ||
[profile.dev] | ||
opt-level = "s" | ||
|
||
[profile.release] | ||
opt-level = "s" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Emgui – An Experimental, Modularized immediate mode Graphical User Interface | ||
|
||
Here are the steps, in chronological order of execution: | ||
|
||
CODE: Input bindings, i.e. gathering GuiInput data from the system (web browser, Mac Window, iPhone App, ...) | ||
DATA: GuiInput: mouse and keyboard state + window size | ||
DATA: GuiSizes: this is a configuration of the ImLayout system, sets sizes of e.g. a slider. | ||
CODE: ImLayout: Immediate mode layout Gui elements. THIS IS WHAT YOUR APP CODE CALLS! | ||
DATA: GuiPaint: High-level commands to render e.g. a checked box with a hover-effect at a certain position. | ||
DATA: GuiStyle: The colors/shading of the gui. | ||
CODE: GuiPainter: Renders GuiPaint + GuiStyle into DrawCommands | ||
DATA: PaintCommands: low-level commands (e.g. "Draw a rectangle with this color here") | ||
CODE: Painter: paints the the PaintCommands to the screen (HTML canvas, OpenGL, ...) | ||
|
||
This is similar to Dear ImGui but separates the layout from the rendering, and adds another step to the rendering. | ||
|
||
# Implementation | ||
|
||
Input is gathered in TypeScript. | ||
PaintCommands rendered to a HTML canvas. | ||
Everything else is written in Rust, compiled to WASM. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#!/bin/bash | ||
set -eu | ||
|
||
# Pre-requisites: | ||
rustup target add wasm32-unknown-unknown | ||
if ! [[ $(wasm-bindgen --version) ]]; then | ||
cargo install wasm-bindgen-cli | ||
fi | ||
|
||
BUILD=debug | ||
# BUILD=release | ||
|
||
# Clear output from old stuff: | ||
rm -rf docs/*.d.ts | ||
rm -rf docs/*.js | ||
rm -rf docs/*.wasm | ||
|
||
echo "Build rust:" | ||
cargo build --target wasm32-unknown-unknown | ||
|
||
echo "Lint and clean up typescript:" | ||
tslint --fix docs/*.ts | ||
|
||
echo "Compile typescript:" | ||
tsc | ||
|
||
echo "Generate JS bindings for wasm:" | ||
|
||
FOLDER_NAME=${PWD##*/} | ||
TARGET_NAME="$FOLDER_NAME.wasm" | ||
wasm-bindgen "target/wasm32-unknown-unknown/$BUILD/$TARGET_NAME" \ | ||
--out-dir docs --no-modules | ||
# --no-modules-global hoboho |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/bin/bash | ||
set -eu | ||
|
||
./build.sh | ||
|
||
open "docs/index.html" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* tslint:disable */ | ||
export function show_gui(arg0: string): string; | ||
|
||
export class Input { | ||
free(): void; | ||
screen_width: number | ||
screen_height: number | ||
mouse_x: number | ||
mouse_y: number | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
(function() { | ||
var wasm; | ||
const __exports = {}; | ||
|
||
|
||
let cachedTextEncoder = new TextEncoder('utf-8'); | ||
|
||
let cachegetUint8Memory = null; | ||
function getUint8Memory() { | ||
if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { | ||
cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); | ||
} | ||
return cachegetUint8Memory; | ||
} | ||
|
||
function passStringToWasm(arg) { | ||
|
||
const buf = cachedTextEncoder.encode(arg); | ||
const ptr = wasm.__wbindgen_malloc(buf.length); | ||
getUint8Memory().set(buf, ptr); | ||
return [ptr, buf.length]; | ||
} | ||
|
||
let cachedTextDecoder = new TextDecoder('utf-8'); | ||
|
||
function getStringFromWasm(ptr, len) { | ||
return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len)); | ||
} | ||
|
||
let cachedGlobalArgumentPtr = null; | ||
function globalArgumentPtr() { | ||
if (cachedGlobalArgumentPtr === null) { | ||
cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr(); | ||
} | ||
return cachedGlobalArgumentPtr; | ||
} | ||
|
||
let cachegetUint32Memory = null; | ||
function getUint32Memory() { | ||
if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) { | ||
cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); | ||
} | ||
return cachegetUint32Memory; | ||
} | ||
/** | ||
* @param {string} arg0 | ||
* @returns {string} | ||
*/ | ||
__exports.show_gui = function(arg0) { | ||
const [ptr0, len0] = passStringToWasm(arg0); | ||
const retptr = globalArgumentPtr(); | ||
try { | ||
wasm.show_gui(retptr, ptr0, len0); | ||
const mem = getUint32Memory(); | ||
const rustptr = mem[retptr / 4]; | ||
const rustlen = mem[retptr / 4 + 1]; | ||
|
||
const realRet = getStringFromWasm(rustptr, rustlen).slice(); | ||
wasm.__wbindgen_free(rustptr, rustlen * 1); | ||
return realRet; | ||
|
||
|
||
} finally { | ||
wasm.__wbindgen_free(ptr0, len0 * 1); | ||
|
||
} | ||
|
||
}; | ||
|
||
function freeInput(ptr) { | ||
|
||
wasm.__wbg_input_free(ptr); | ||
} | ||
/** | ||
*/ | ||
class Input { | ||
|
||
free() { | ||
const ptr = this.ptr; | ||
this.ptr = 0; | ||
freeInput(ptr); | ||
} | ||
|
||
/** | ||
* @returns {number} | ||
*/ | ||
get screen_width() { | ||
return wasm.__wbg_get_input_screen_width(this.ptr); | ||
} | ||
set screen_width(arg0) { | ||
return wasm.__wbg_set_input_screen_width(this.ptr, arg0); | ||
} | ||
/** | ||
* @returns {number} | ||
*/ | ||
get screen_height() { | ||
return wasm.__wbg_get_input_screen_height(this.ptr); | ||
} | ||
set screen_height(arg0) { | ||
return wasm.__wbg_set_input_screen_height(this.ptr, arg0); | ||
} | ||
/** | ||
* @returns {number} | ||
*/ | ||
get mouse_x() { | ||
return wasm.__wbg_get_input_mouse_x(this.ptr); | ||
} | ||
set mouse_x(arg0) { | ||
return wasm.__wbg_set_input_mouse_x(this.ptr, arg0); | ||
} | ||
/** | ||
* @returns {number} | ||
*/ | ||
get mouse_y() { | ||
return wasm.__wbg_get_input_mouse_y(this.ptr); | ||
} | ||
set mouse_y(arg0) { | ||
return wasm.__wbg_set_input_mouse_y(this.ptr, arg0); | ||
} | ||
} | ||
__exports.Input = Input; | ||
|
||
__exports.__wbindgen_throw = function(ptr, len) { | ||
throw new Error(getStringFromWasm(ptr, len)); | ||
}; | ||
|
||
function init(path_or_module) { | ||
let instantiation; | ||
const imports = { './emgui': __exports }; | ||
if (path_or_module instanceof WebAssembly.Module) { | ||
instantiation = WebAssembly.instantiate(path_or_module, imports) | ||
.then(instance => { | ||
return { instance, module: module_or_path } | ||
}); | ||
} else { | ||
const data = fetch(path_or_module); | ||
if (typeof WebAssembly.instantiateStreaming === 'function') { | ||
instantiation = WebAssembly.instantiateStreaming(data, imports); | ||
} else { | ||
instantiation = data | ||
.then(response => response.arrayBuffer()) | ||
.then(buffer => WebAssembly.instantiate(buffer, imports)); | ||
} | ||
} | ||
return instantiation.then(({instance}) => { | ||
wasm = init.wasm = instance.exports; | ||
return; | ||
}); | ||
}; | ||
self.wasm_bindgen = Object.assign(init, __exports); | ||
})(); |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// ---------------------------------------------------------------------------- | ||
// Paint module: | ||
function paintCommand(canvas, cmd) { | ||
var ctx = canvas.getContext("2d"); | ||
switch (cmd.kind) { | ||
case "clear": | ||
ctx.fillStyle = cmd.fill_style; | ||
ctx.clearRect(0, 0, canvas.width, canvas.height); | ||
return; | ||
case "line": | ||
ctx.beginPath(); | ||
ctx.lineWidth = cmd.line_width; | ||
ctx.strokeStyle = cmd.stroke_style; | ||
ctx.moveTo(cmd.from[0], cmd.from[1]); | ||
ctx.lineTo(cmd.to[0], cmd.to[1]); | ||
ctx.stroke(); | ||
return; | ||
case "circle": | ||
ctx.fillStyle = cmd.fill_style; | ||
ctx.beginPath(); | ||
ctx.arc(cmd.center[0], cmd.center[1], cmd.radius, 0, 2 * Math.PI, false); | ||
ctx.fill(); | ||
return; | ||
case "rounded_rect": | ||
ctx.fillStyle = cmd.fill_style; | ||
var x = cmd.pos[0]; | ||
var y = cmd.pos[1]; | ||
var width = cmd.size[0]; | ||
var height = cmd.size[1]; | ||
var radius = cmd.radius; | ||
ctx.beginPath(); | ||
ctx.moveTo(x + radius, y); | ||
ctx.lineTo(x + width - radius, y); | ||
ctx.quadraticCurveTo(x + width, y, x + width, y + radius); | ||
ctx.lineTo(x + width, y + height - radius); | ||
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); | ||
ctx.lineTo(x + radius, y + height); | ||
ctx.quadraticCurveTo(x, y + height, x, y + height - radius); | ||
ctx.lineTo(x, y + radius); | ||
ctx.quadraticCurveTo(x, y, x + radius, y); | ||
ctx.closePath(); | ||
ctx.fill(); | ||
return; | ||
case "text": | ||
ctx.font = cmd.font; | ||
ctx.fillStyle = cmd.fill_style; | ||
ctx.textAlign = cmd.text_align; | ||
ctx.fillText(cmd.text, cmd.pos[0], cmd.pos[1]); | ||
return; | ||
} | ||
} | ||
// we'll defer our execution until the wasm is ready to go | ||
function wasm_loaded() { | ||
console.log("wasm loaded"); | ||
initialize(); | ||
} | ||
// here we tell bindgen the path to the wasm file so it can start | ||
// initialization and return to us a promise when it's done | ||
wasm_bindgen("./emgui_bg.wasm") | ||
.then(wasm_loaded)["catch"](console.error); | ||
function rust_gui(input) { | ||
return JSON.parse(wasm_bindgen.show_gui(JSON.stringify(input))); | ||
} | ||
// ---------------------------------------------------------------------------- | ||
function js_gui(input) { | ||
var commands = []; | ||
commands.push({ | ||
fillStyle: "#111111", | ||
kind: "clear" | ||
}); | ||
commands.push({ | ||
fillStyle: "#ff1111", | ||
kind: "rounded_rect", | ||
pos: [100, 100], | ||
radius: 20, | ||
size: [200, 100] | ||
}); | ||
return commands; | ||
} | ||
function paint_gui(canvas, mouse_pos) { | ||
var input = { | ||
mouse_x: mouse_pos.x, | ||
mouse_y: mouse_pos.y, | ||
screen_height: canvas.height, | ||
screen_width: canvas.width | ||
}; | ||
var commands = rust_gui(input); | ||
for (var _i = 0, commands_1 = commands; _i < commands_1.length; _i++) { | ||
var cmd = commands_1[_i]; | ||
paintCommand(canvas, cmd); | ||
} | ||
} | ||
// ---------------------------------------------------------------------------- | ||
function mouse_pos_from_event(canvas, evt) { | ||
var rect = canvas.getBoundingClientRect(); | ||
return { | ||
x: evt.clientX - rect.left, | ||
y: evt.clientY - rect.top | ||
}; | ||
} | ||
function initialize() { | ||
var canvas = document.getElementById("canvas"); | ||
canvas.addEventListener("mousemove", function (evt) { | ||
var mouse_pos = mouse_pos_from_event(canvas, evt); | ||
paint_gui(canvas, mouse_pos); | ||
}, false); | ||
canvas.addEventListener("mousedown", function (evt) { | ||
var mouse_pos = mouse_pos_from_event(canvas, evt); | ||
paint_gui(canvas, mouse_pos); | ||
}, false); | ||
paint_gui(canvas, { x: 0, y: 0 }); | ||
} |
Oops, something went wrong.