mirror of
https://github.com/penpot/penpot.git
synced 2025-05-22 13:26:43 +02:00
🎉 Add wasm playground environment
This commit is contained in:
parent
486f036a11
commit
aae81b8a04
5 changed files with 222 additions and 0 deletions
132
frontend/resources/wasm-playground/js/lib.js
Normal file
132
frontend/resources/wasm-playground/js/lib.js
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
let Module = null;
|
||||||
|
|
||||||
|
let scale = 1;
|
||||||
|
let offsetX = 0;
|
||||||
|
let offsetY = 0;
|
||||||
|
|
||||||
|
let isPanning = false;
|
||||||
|
let lastX = 0;
|
||||||
|
let lastY = 0;
|
||||||
|
|
||||||
|
export function init(moduleInstance) {
|
||||||
|
Module = moduleInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assignCanvas(canvas) {
|
||||||
|
const glModule = Module.GL;
|
||||||
|
const context = canvas.getContext("webgl2", {
|
||||||
|
antialias: true,
|
||||||
|
depth: true,
|
||||||
|
alpha: false,
|
||||||
|
stencil: true,
|
||||||
|
preserveDrawingBuffer: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handle = glModule.registerContext(context, { majorVersion: 2 });
|
||||||
|
glModule.makeContextCurrent(handle);
|
||||||
|
context.getExtension("WEBGL_debug_renderer_info");
|
||||||
|
|
||||||
|
Module._init(canvas.width, canvas.height);
|
||||||
|
Module._set_render_options(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hexToU32ARGB(hex, opacity = 1) {
|
||||||
|
const rgb = parseInt(hex.slice(1), 16);
|
||||||
|
const a = Math.floor(opacity * 0xFF);
|
||||||
|
const argb = (a << 24) | rgb;
|
||||||
|
return argb >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRandomInt(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRandomColor() {
|
||||||
|
const r = getRandomInt(0, 256).toString(16).padStart(2, '0');
|
||||||
|
const g = getRandomInt(0, 256).toString(16).padStart(2, '0');
|
||||||
|
const b = getRandomInt(0, 256).toString(16).padStart(2, '0');
|
||||||
|
return `#${r}${g}${b}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRandomFloat(min, max) {
|
||||||
|
return Math.random() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getU32(id) {
|
||||||
|
const hex = id.replace(/-/g, "");
|
||||||
|
const buffer = new Uint32Array(4);
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
buffer[i] = parseInt(hex.slice(i * 8, (i + 1) * 8), 16);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function heapU32SetUUID(id, heap, offset) {
|
||||||
|
const buffer = getU32(id);
|
||||||
|
heap.set(buffer, offset);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ptr8ToPtr32(ptr8) {
|
||||||
|
return ptr8 >>> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function allocBytes(size) {
|
||||||
|
return Module._alloc_bytes(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHeapU32() {
|
||||||
|
return Module.HEAPU32;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setShapeChildren(shapeIds) {
|
||||||
|
const offset = allocBytes(shapeIds.length * 16);
|
||||||
|
const heap = getHeapU32();
|
||||||
|
let currentOffset = offset;
|
||||||
|
for (const id of shapeIds) {
|
||||||
|
heapU32SetUUID(id, heap, ptr8ToPtr32(currentOffset));
|
||||||
|
currentOffset += 16;
|
||||||
|
}
|
||||||
|
return Module._set_children();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useShape(id) {
|
||||||
|
const buffer = getU32(id);
|
||||||
|
Module._use_shape(...buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupInteraction(canvas) {
|
||||||
|
canvas.addEventListener("wheel", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const zoomFactor = e.deltaY < 0 ? 1.1 : 0.9;
|
||||||
|
scale *= zoomFactor;
|
||||||
|
const mouseX = e.offsetX;
|
||||||
|
const mouseY = e.offsetY;
|
||||||
|
offsetX -= (mouseX - offsetX) * (zoomFactor - 1);
|
||||||
|
offsetY -= (mouseY - offsetY) * (zoomFactor - 1);
|
||||||
|
Module._set_view(scale, offsetX, offsetY);
|
||||||
|
Module._render(Date.now());
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.addEventListener("mousedown", (e) => {
|
||||||
|
isPanning = true;
|
||||||
|
lastX = e.offsetX;
|
||||||
|
lastY = e.offsetY;
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.addEventListener("mousemove", (e) => {
|
||||||
|
if (isPanning) {
|
||||||
|
const dx = e.offsetX - lastX;
|
||||||
|
const dy = e.offsetY - lastY;
|
||||||
|
offsetX += dx;
|
||||||
|
offsetY += dy;
|
||||||
|
lastX = e.offsetX;
|
||||||
|
lastY = e.offsetY;
|
||||||
|
Module._set_view(scale, offsetX, offsetY);
|
||||||
|
Module._render(Date.now());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.addEventListener("mouseup", () => { isPanning = false; });
|
||||||
|
canvas.addEventListener("mouseout", () => { isPanning = false; });
|
||||||
|
}
|
69
frontend/resources/wasm-playground/rects.html
Normal file
69
frontend/resources/wasm-playground/rects.html
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>WASM + WebGL2 Canvas</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: #111;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<script type="module">
|
||||||
|
import initWasmModule from '/js/render_wasm.js';
|
||||||
|
import {
|
||||||
|
init, assignCanvas, hexToU32ARGB, getRandomInt, getRandomColor,
|
||||||
|
getRandomFloat, useShape, setShapeChildren, setupInteraction
|
||||||
|
} from './js/lib.js';
|
||||||
|
|
||||||
|
const canvas = document.getElementById("canvas");
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
initWasmModule().then(Module => {
|
||||||
|
init(Module);
|
||||||
|
assignCanvas(canvas);
|
||||||
|
Module._set_canvas_background(hexToU32ARGB("#FABADA", 1));
|
||||||
|
Module._set_view(1, 0, 0);
|
||||||
|
setupInteraction(canvas);
|
||||||
|
|
||||||
|
const children = [];
|
||||||
|
for (let i = 0; i < 1000; i++) {
|
||||||
|
const uuid = crypto.randomUUID();
|
||||||
|
children.push(uuid);
|
||||||
|
|
||||||
|
useShape(uuid);
|
||||||
|
Module._set_parent(0, 0, 0, 0);
|
||||||
|
Module._set_shape_type(3);
|
||||||
|
const x1 = getRandomInt(0, canvas.width);
|
||||||
|
const y1 = getRandomInt(0, canvas.height);
|
||||||
|
const width = getRandomInt(20, 100);
|
||||||
|
const height = getRandomInt(20, 100);
|
||||||
|
Module._set_shape_selrect(x1, y1, x1 + width, y1 + height);
|
||||||
|
|
||||||
|
const color = getRandomColor();
|
||||||
|
const argb = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||||
|
Module._add_shape_solid_fill(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
useShape("00000000-0000-0000-0000-000000000000");
|
||||||
|
setShapeChildren(children);
|
||||||
|
Module._render(Date.now());
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -573,3 +573,16 @@ export async function copyAssets() {
|
||||||
const end = process.hrtime(start);
|
const end = process.hrtime(start);
|
||||||
log.info("done: copy assets", `(${ppt(end)})`);
|
log.info("done: copy assets", `(${ppt(end)})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function copyWasmPlayground() {
|
||||||
|
const start = process.hrtime();
|
||||||
|
log.info("init: copy wasm playground");
|
||||||
|
|
||||||
|
await syncDirs(
|
||||||
|
"resources/wasm-playground/",
|
||||||
|
"resources/public/wasm-playground/",
|
||||||
|
);
|
||||||
|
|
||||||
|
const end = process.hrtime(start);
|
||||||
|
log.info("done: copy wasm playground", `(${ppt(end)})`);
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as h from "./_helpers.js";
|
||||||
|
|
||||||
await h.compileStyles();
|
await h.compileStyles();
|
||||||
await h.copyAssets();
|
await h.copyAssets();
|
||||||
|
await h.copyWasmPlayground();
|
||||||
await h.compileSvgSprites();
|
await h.compileSvgSprites();
|
||||||
await h.compileTemplates();
|
await h.compileTemplates();
|
||||||
await h.compilePolyfills();
|
await h.compilePolyfills();
|
||||||
|
|
|
@ -44,6 +44,7 @@ async function compileSass(path) {
|
||||||
await fs.mkdir("./resources/public/css/", { recursive: true });
|
await fs.mkdir("./resources/public/css/", { recursive: true });
|
||||||
await compileSassAll();
|
await compileSassAll();
|
||||||
await h.copyAssets();
|
await h.copyAssets();
|
||||||
|
await h.copyWasmPlayground();
|
||||||
await h.compileSvgSprites();
|
await h.compileSvgSprites();
|
||||||
await h.compileTemplates();
|
await h.compileTemplates();
|
||||||
await h.compilePolyfills();
|
await h.compilePolyfills();
|
||||||
|
@ -88,4 +89,10 @@ h.watch(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
log.info("watch: wasm playground (~)");
|
||||||
|
h.watch(["resources/wasm-playground"], null, async function (path) {
|
||||||
|
log.info("changed:", path);
|
||||||
|
await h.copyWasmPlayground();
|
||||||
|
});
|
||||||
|
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue