From 88eee669c96cff60f7a9be25b8152e8aab75ae1e Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:40:42 +0100 Subject: [PATCH 01/17] Move swissgl files to own directory --- .gitignore => swissgl/.gitignore | 0 CONTRIBUTING.md => swissgl/CONTRIBUTING.md | 0 README.md => swissgl/README.md | 0 audio.js => swissgl/audio.js | 0 {demo => swissgl/demo}/BitField.js | 0 {demo => swissgl/demo}/ColorCube.js | 0 {demo => swissgl/demo}/CubeDeform.js | 0 {demo => swissgl/demo}/DeferredShading.js | 0 {demo => swissgl/demo}/DotCamera.js | 0 {demo => swissgl/demo}/FancyLenia.js | 0 {demo => swissgl/demo}/GameOfLife.js | 0 {demo => swissgl/demo}/MeshGrid.js | 0 {demo => swissgl/demo}/NeuralCA.js | 0 {demo => swissgl/demo}/ParticleLenia.js | 0 {demo => swissgl/demo}/ParticleLife.js | 0 {demo => swissgl/demo}/ParticleLife3d.js | 0 {demo => swissgl/demo}/Physarum.js | 0 {demo => swissgl/demo}/Physarum3d.js | 0 {demo => swissgl/demo}/ReactionDiffusion.js | 0 {demo => swissgl/demo}/Shadowmap.js | 0 {demo => swissgl/demo}/Spectrogram.js | 0 {demo => swissgl/demo}/Springs.js | 0 {demo => swissgl/demo}/SurfaceNormals.js | 0 {demo => swissgl/demo}/TextureSamplers.js | 0 {demo => swissgl/demo}/Torus4d.js | 0 {demo => swissgl/demo}/criticality.html | 0 {demo => swissgl/demo}/dat.gui.min.js | 0 {demo => swissgl/demo}/main.js | 0 {demo => swissgl/demo}/preview/BitField.jpg | Bin {demo => swissgl/demo}/preview/ColorCube.jpg | Bin {demo => swissgl/demo}/preview/CubeDeform.jpg | Bin {demo => swissgl/demo}/preview/DeferredShading.jpg | Bin {demo => swissgl/demo}/preview/DotCamera.jpg | Bin {demo => swissgl/demo}/preview/FancyLenia.jpg | Bin {demo => swissgl/demo}/preview/GameOfLife.jpg | Bin {demo => swissgl/demo}/preview/MeshGrid.jpg | Bin {demo => swissgl/demo}/preview/NeuralCA.jpg | Bin {demo => swissgl/demo}/preview/ParticleLenia.jpg | Bin {demo => swissgl/demo}/preview/ParticleLife.jpg | Bin {demo => swissgl/demo}/preview/ParticleLife3d.jpg | Bin {demo => swissgl/demo}/preview/Physarum.jpg | Bin {demo => swissgl/demo}/preview/Physarum3d.jpg | Bin .../demo}/preview/ReactionDiffusion.jpg | Bin {demo => swissgl/demo}/preview/Shadowmap.jpg | Bin {demo => swissgl/demo}/preview/Spectrogram.jpg | Bin {demo => swissgl/demo}/preview/Springs.jpg | Bin {demo => swissgl/demo}/preview/SurfaceNormals.jpg | Bin {demo => swissgl/demo}/preview/TextureSamplers.jpg | Bin {demo => swissgl/demo}/preview/Torus4d.jpg | Bin {demo => swissgl/demo}/style.css | 0 {docs => swissgl/docs}/API.md | 0 {docs => swissgl/docs}/CHANGELOG.md | 0 {images => swissgl/images}/F.png | Bin {images => swissgl/images}/init_particles.png | Bin {images => swissgl/images}/particle_snake.png | Bin {images => swissgl/images}/quad.png | Bin index.html => swissgl/index.html | 0 package.json => swissgl/package.json | 0 swissgl.js => swissgl/swissgl.js | 0 swissgl.mjs => swissgl/swissgl.mjs | 0 tiny.html => swissgl/tiny.html | 0 61 files changed, 0 insertions(+), 0 deletions(-) rename .gitignore => swissgl/.gitignore (100%) rename CONTRIBUTING.md => swissgl/CONTRIBUTING.md (100%) rename README.md => swissgl/README.md (100%) rename audio.js => swissgl/audio.js (100%) rename {demo => swissgl/demo}/BitField.js (100%) rename {demo => swissgl/demo}/ColorCube.js (100%) rename {demo => swissgl/demo}/CubeDeform.js (100%) rename {demo => swissgl/demo}/DeferredShading.js (100%) rename {demo => swissgl/demo}/DotCamera.js (100%) rename {demo => swissgl/demo}/FancyLenia.js (100%) rename {demo => swissgl/demo}/GameOfLife.js (100%) rename {demo => swissgl/demo}/MeshGrid.js (100%) rename {demo => swissgl/demo}/NeuralCA.js (100%) rename {demo => swissgl/demo}/ParticleLenia.js (100%) rename {demo => swissgl/demo}/ParticleLife.js (100%) rename {demo => swissgl/demo}/ParticleLife3d.js (100%) rename {demo => swissgl/demo}/Physarum.js (100%) rename {demo => swissgl/demo}/Physarum3d.js (100%) rename {demo => swissgl/demo}/ReactionDiffusion.js (100%) rename {demo => swissgl/demo}/Shadowmap.js (100%) rename {demo => swissgl/demo}/Spectrogram.js (100%) rename {demo => swissgl/demo}/Springs.js (100%) rename {demo => swissgl/demo}/SurfaceNormals.js (100%) rename {demo => swissgl/demo}/TextureSamplers.js (100%) rename {demo => swissgl/demo}/Torus4d.js (100%) rename {demo => swissgl/demo}/criticality.html (100%) rename {demo => swissgl/demo}/dat.gui.min.js (100%) rename {demo => swissgl/demo}/main.js (100%) rename {demo => swissgl/demo}/preview/BitField.jpg (100%) rename {demo => swissgl/demo}/preview/ColorCube.jpg (100%) rename {demo => swissgl/demo}/preview/CubeDeform.jpg (100%) rename {demo => swissgl/demo}/preview/DeferredShading.jpg (100%) rename {demo => swissgl/demo}/preview/DotCamera.jpg (100%) rename {demo => swissgl/demo}/preview/FancyLenia.jpg (100%) rename {demo => swissgl/demo}/preview/GameOfLife.jpg (100%) rename {demo => swissgl/demo}/preview/MeshGrid.jpg (100%) rename {demo => swissgl/demo}/preview/NeuralCA.jpg (100%) rename {demo => swissgl/demo}/preview/ParticleLenia.jpg (100%) rename {demo => swissgl/demo}/preview/ParticleLife.jpg (100%) rename {demo => swissgl/demo}/preview/ParticleLife3d.jpg (100%) rename {demo => swissgl/demo}/preview/Physarum.jpg (100%) rename {demo => swissgl/demo}/preview/Physarum3d.jpg (100%) rename {demo => swissgl/demo}/preview/ReactionDiffusion.jpg (100%) rename {demo => swissgl/demo}/preview/Shadowmap.jpg (100%) rename {demo => swissgl/demo}/preview/Spectrogram.jpg (100%) rename {demo => swissgl/demo}/preview/Springs.jpg (100%) rename {demo => swissgl/demo}/preview/SurfaceNormals.jpg (100%) rename {demo => swissgl/demo}/preview/TextureSamplers.jpg (100%) rename {demo => swissgl/demo}/preview/Torus4d.jpg (100%) rename {demo => swissgl/demo}/style.css (100%) rename {docs => swissgl/docs}/API.md (100%) rename {docs => swissgl/docs}/CHANGELOG.md (100%) rename {images => swissgl/images}/F.png (100%) rename {images => swissgl/images}/init_particles.png (100%) rename {images => swissgl/images}/particle_snake.png (100%) rename {images => swissgl/images}/quad.png (100%) rename index.html => swissgl/index.html (100%) rename package.json => swissgl/package.json (100%) rename swissgl.js => swissgl/swissgl.js (100%) rename swissgl.mjs => swissgl/swissgl.mjs (100%) rename tiny.html => swissgl/tiny.html (100%) diff --git a/.gitignore b/swissgl/.gitignore similarity index 100% rename from .gitignore rename to swissgl/.gitignore diff --git a/CONTRIBUTING.md b/swissgl/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to swissgl/CONTRIBUTING.md diff --git a/README.md b/swissgl/README.md similarity index 100% rename from README.md rename to swissgl/README.md diff --git a/audio.js b/swissgl/audio.js similarity index 100% rename from audio.js rename to swissgl/audio.js diff --git a/demo/BitField.js b/swissgl/demo/BitField.js similarity index 100% rename from demo/BitField.js rename to swissgl/demo/BitField.js diff --git a/demo/ColorCube.js b/swissgl/demo/ColorCube.js similarity index 100% rename from demo/ColorCube.js rename to swissgl/demo/ColorCube.js diff --git a/demo/CubeDeform.js b/swissgl/demo/CubeDeform.js similarity index 100% rename from demo/CubeDeform.js rename to swissgl/demo/CubeDeform.js diff --git a/demo/DeferredShading.js b/swissgl/demo/DeferredShading.js similarity index 100% rename from demo/DeferredShading.js rename to swissgl/demo/DeferredShading.js diff --git a/demo/DotCamera.js b/swissgl/demo/DotCamera.js similarity index 100% rename from demo/DotCamera.js rename to swissgl/demo/DotCamera.js diff --git a/demo/FancyLenia.js b/swissgl/demo/FancyLenia.js similarity index 100% rename from demo/FancyLenia.js rename to swissgl/demo/FancyLenia.js diff --git a/demo/GameOfLife.js b/swissgl/demo/GameOfLife.js similarity index 100% rename from demo/GameOfLife.js rename to swissgl/demo/GameOfLife.js diff --git a/demo/MeshGrid.js b/swissgl/demo/MeshGrid.js similarity index 100% rename from demo/MeshGrid.js rename to swissgl/demo/MeshGrid.js diff --git a/demo/NeuralCA.js b/swissgl/demo/NeuralCA.js similarity index 100% rename from demo/NeuralCA.js rename to swissgl/demo/NeuralCA.js diff --git a/demo/ParticleLenia.js b/swissgl/demo/ParticleLenia.js similarity index 100% rename from demo/ParticleLenia.js rename to swissgl/demo/ParticleLenia.js diff --git a/demo/ParticleLife.js b/swissgl/demo/ParticleLife.js similarity index 100% rename from demo/ParticleLife.js rename to swissgl/demo/ParticleLife.js diff --git a/demo/ParticleLife3d.js b/swissgl/demo/ParticleLife3d.js similarity index 100% rename from demo/ParticleLife3d.js rename to swissgl/demo/ParticleLife3d.js diff --git a/demo/Physarum.js b/swissgl/demo/Physarum.js similarity index 100% rename from demo/Physarum.js rename to swissgl/demo/Physarum.js diff --git a/demo/Physarum3d.js b/swissgl/demo/Physarum3d.js similarity index 100% rename from demo/Physarum3d.js rename to swissgl/demo/Physarum3d.js diff --git a/demo/ReactionDiffusion.js b/swissgl/demo/ReactionDiffusion.js similarity index 100% rename from demo/ReactionDiffusion.js rename to swissgl/demo/ReactionDiffusion.js diff --git a/demo/Shadowmap.js b/swissgl/demo/Shadowmap.js similarity index 100% rename from demo/Shadowmap.js rename to swissgl/demo/Shadowmap.js diff --git a/demo/Spectrogram.js b/swissgl/demo/Spectrogram.js similarity index 100% rename from demo/Spectrogram.js rename to swissgl/demo/Spectrogram.js diff --git a/demo/Springs.js b/swissgl/demo/Springs.js similarity index 100% rename from demo/Springs.js rename to swissgl/demo/Springs.js diff --git a/demo/SurfaceNormals.js b/swissgl/demo/SurfaceNormals.js similarity index 100% rename from demo/SurfaceNormals.js rename to swissgl/demo/SurfaceNormals.js diff --git a/demo/TextureSamplers.js b/swissgl/demo/TextureSamplers.js similarity index 100% rename from demo/TextureSamplers.js rename to swissgl/demo/TextureSamplers.js diff --git a/demo/Torus4d.js b/swissgl/demo/Torus4d.js similarity index 100% rename from demo/Torus4d.js rename to swissgl/demo/Torus4d.js diff --git a/demo/criticality.html b/swissgl/demo/criticality.html similarity index 100% rename from demo/criticality.html rename to swissgl/demo/criticality.html diff --git a/demo/dat.gui.min.js b/swissgl/demo/dat.gui.min.js similarity index 100% rename from demo/dat.gui.min.js rename to swissgl/demo/dat.gui.min.js diff --git a/demo/main.js b/swissgl/demo/main.js similarity index 100% rename from demo/main.js rename to swissgl/demo/main.js diff --git a/demo/preview/BitField.jpg b/swissgl/demo/preview/BitField.jpg similarity index 100% rename from demo/preview/BitField.jpg rename to swissgl/demo/preview/BitField.jpg diff --git a/demo/preview/ColorCube.jpg b/swissgl/demo/preview/ColorCube.jpg similarity index 100% rename from demo/preview/ColorCube.jpg rename to swissgl/demo/preview/ColorCube.jpg diff --git a/demo/preview/CubeDeform.jpg b/swissgl/demo/preview/CubeDeform.jpg similarity index 100% rename from demo/preview/CubeDeform.jpg rename to swissgl/demo/preview/CubeDeform.jpg diff --git a/demo/preview/DeferredShading.jpg b/swissgl/demo/preview/DeferredShading.jpg similarity index 100% rename from demo/preview/DeferredShading.jpg rename to swissgl/demo/preview/DeferredShading.jpg diff --git a/demo/preview/DotCamera.jpg b/swissgl/demo/preview/DotCamera.jpg similarity index 100% rename from demo/preview/DotCamera.jpg rename to swissgl/demo/preview/DotCamera.jpg diff --git a/demo/preview/FancyLenia.jpg b/swissgl/demo/preview/FancyLenia.jpg similarity index 100% rename from demo/preview/FancyLenia.jpg rename to swissgl/demo/preview/FancyLenia.jpg diff --git a/demo/preview/GameOfLife.jpg b/swissgl/demo/preview/GameOfLife.jpg similarity index 100% rename from demo/preview/GameOfLife.jpg rename to swissgl/demo/preview/GameOfLife.jpg diff --git a/demo/preview/MeshGrid.jpg b/swissgl/demo/preview/MeshGrid.jpg similarity index 100% rename from demo/preview/MeshGrid.jpg rename to swissgl/demo/preview/MeshGrid.jpg diff --git a/demo/preview/NeuralCA.jpg b/swissgl/demo/preview/NeuralCA.jpg similarity index 100% rename from demo/preview/NeuralCA.jpg rename to swissgl/demo/preview/NeuralCA.jpg diff --git a/demo/preview/ParticleLenia.jpg b/swissgl/demo/preview/ParticleLenia.jpg similarity index 100% rename from demo/preview/ParticleLenia.jpg rename to swissgl/demo/preview/ParticleLenia.jpg diff --git a/demo/preview/ParticleLife.jpg b/swissgl/demo/preview/ParticleLife.jpg similarity index 100% rename from demo/preview/ParticleLife.jpg rename to swissgl/demo/preview/ParticleLife.jpg diff --git a/demo/preview/ParticleLife3d.jpg b/swissgl/demo/preview/ParticleLife3d.jpg similarity index 100% rename from demo/preview/ParticleLife3d.jpg rename to swissgl/demo/preview/ParticleLife3d.jpg diff --git a/demo/preview/Physarum.jpg b/swissgl/demo/preview/Physarum.jpg similarity index 100% rename from demo/preview/Physarum.jpg rename to swissgl/demo/preview/Physarum.jpg diff --git a/demo/preview/Physarum3d.jpg b/swissgl/demo/preview/Physarum3d.jpg similarity index 100% rename from demo/preview/Physarum3d.jpg rename to swissgl/demo/preview/Physarum3d.jpg diff --git a/demo/preview/ReactionDiffusion.jpg b/swissgl/demo/preview/ReactionDiffusion.jpg similarity index 100% rename from demo/preview/ReactionDiffusion.jpg rename to swissgl/demo/preview/ReactionDiffusion.jpg diff --git a/demo/preview/Shadowmap.jpg b/swissgl/demo/preview/Shadowmap.jpg similarity index 100% rename from demo/preview/Shadowmap.jpg rename to swissgl/demo/preview/Shadowmap.jpg diff --git a/demo/preview/Spectrogram.jpg b/swissgl/demo/preview/Spectrogram.jpg similarity index 100% rename from demo/preview/Spectrogram.jpg rename to swissgl/demo/preview/Spectrogram.jpg diff --git a/demo/preview/Springs.jpg b/swissgl/demo/preview/Springs.jpg similarity index 100% rename from demo/preview/Springs.jpg rename to swissgl/demo/preview/Springs.jpg diff --git a/demo/preview/SurfaceNormals.jpg b/swissgl/demo/preview/SurfaceNormals.jpg similarity index 100% rename from demo/preview/SurfaceNormals.jpg rename to swissgl/demo/preview/SurfaceNormals.jpg diff --git a/demo/preview/TextureSamplers.jpg b/swissgl/demo/preview/TextureSamplers.jpg similarity index 100% rename from demo/preview/TextureSamplers.jpg rename to swissgl/demo/preview/TextureSamplers.jpg diff --git a/demo/preview/Torus4d.jpg b/swissgl/demo/preview/Torus4d.jpg similarity index 100% rename from demo/preview/Torus4d.jpg rename to swissgl/demo/preview/Torus4d.jpg diff --git a/demo/style.css b/swissgl/demo/style.css similarity index 100% rename from demo/style.css rename to swissgl/demo/style.css diff --git a/docs/API.md b/swissgl/docs/API.md similarity index 100% rename from docs/API.md rename to swissgl/docs/API.md diff --git a/docs/CHANGELOG.md b/swissgl/docs/CHANGELOG.md similarity index 100% rename from docs/CHANGELOG.md rename to swissgl/docs/CHANGELOG.md diff --git a/images/F.png b/swissgl/images/F.png similarity index 100% rename from images/F.png rename to swissgl/images/F.png diff --git a/images/init_particles.png b/swissgl/images/init_particles.png similarity index 100% rename from images/init_particles.png rename to swissgl/images/init_particles.png diff --git a/images/particle_snake.png b/swissgl/images/particle_snake.png similarity index 100% rename from images/particle_snake.png rename to swissgl/images/particle_snake.png diff --git a/images/quad.png b/swissgl/images/quad.png similarity index 100% rename from images/quad.png rename to swissgl/images/quad.png diff --git a/index.html b/swissgl/index.html similarity index 100% rename from index.html rename to swissgl/index.html diff --git a/package.json b/swissgl/package.json similarity index 100% rename from package.json rename to swissgl/package.json diff --git a/swissgl.js b/swissgl/swissgl.js similarity index 100% rename from swissgl.js rename to swissgl/swissgl.js diff --git a/swissgl.mjs b/swissgl/swissgl.mjs similarity index 100% rename from swissgl.mjs rename to swissgl/swissgl.mjs diff --git a/tiny.html b/swissgl/tiny.html similarity index 100% rename from tiny.html rename to swissgl/tiny.html From efa0512d9f2a5132b079e083a9d80f67ccb87100 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:42:37 +0100 Subject: [PATCH 02/17] pnpm create svelte@latest --- .eslintignore | 13 +++++++++++ .eslintrc.cjs | 30 ++++++++++++++++++++++++ .gitignore | 11 +++++++++ .npmrc | 1 + .prettierignore | 13 +++++++++++ .prettierrc | 9 ++++++++ package.json | 50 ++++++++++++++++++++++++++++++++++++++++ src/app.d.ts | 12 ++++++++++ src/app.html | 12 ++++++++++ src/lib/index.js | 1 + src/routes/+page.svelte | 3 +++ static/favicon.png | Bin 0 -> 1571 bytes svelte.config.js | 18 +++++++++++++++ tsconfig.json | 14 +++++++++++ vite.config.ts | 6 +++++ 15 files changed, 193 insertions(+) create mode 100644 .eslintignore create mode 100644 .eslintrc.cjs create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 package.json create mode 100644 src/app.d.ts create mode 100644 src/app.html create mode 100644 src/lib/index.js create mode 100644 src/routes/+page.svelte create mode 100644 static/favicon.png create mode 100644 svelte.config.js create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..3897265 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..ebc1958 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,30 @@ +module.exports = { + root: true, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:svelte/recommended', + 'prettier' + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + extraFileExtensions: ['.svelte'] + }, + env: { + browser: true, + es2017: true, + node: true + }, + overrides: [ + { + files: ['*.svelte'], + parser: 'svelte-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser' + } + } + ] +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac7211b --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +node_modules +/build +/dist +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..3897265 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a77fdde --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "pluginSearchDirs": ["."], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..471d903 --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "swissgl.svelte", + "version": "0.0.1", + "scripts": { + "dev": "vite dev", + "build": "vite build && npm run package", + "preview": "vite preview", + "package": "svelte-kit sync && svelte-package && publint", + "prepublishOnly": "npm run package", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "prettier --plugin-search-dir . --check . && eslint .", + "format": "prettier --plugin-search-dir . --write ." + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "svelte": "./dist/index.js" + } + }, + "files": [ + "dist", + "!dist/**/*.test.*", + "!dist/**/*.spec.*" + ], + "peerDependencies": { + "svelte": "^4.0.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/kit": "^1.20.4", + "@sveltejs/package": "^2.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.28.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-svelte": "^2.30.0", + "prettier": "^2.8.0", + "prettier-plugin-svelte": "^2.10.1", + "publint": "^0.1.9", + "svelte": "^4.0.5", + "svelte-check": "^3.4.3", + "tslib": "^2.4.1", + "typescript": "^5.0.0", + "vite": "^4.4.2" + }, + "svelte": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module" +} diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..f59b884 --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,12 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..d2fc6b0 --- /dev/null +++ b/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/lib/index.js b/src/lib/index.js new file mode 100644 index 0000000..47d3c46 --- /dev/null +++ b/src/lib/index.js @@ -0,0 +1 @@ +// Reexport your entry components here diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..0a45b69 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,3 @@ +

Welcome to your library project

+

Create your package using @sveltejs/package and preview/showcase your work with SvelteKit

+

Visit kit.svelte.dev to read the documentation

diff --git a/static/favicon.png b/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..825b9e65af7c104cfb07089bb28659393b4f2097 GIT binary patch literal 1571 zcmV+;2Hg3HP)Px)-AP12RCwC$UE6KzI1p6{F2N z1VK2vi|pOpn{~#djwYcWXTI_im_u^TJgMZ4JMOsSj!0ma>B?-(Hr@X&W@|R-$}W@Z zgj#$x=!~7LGqHW?IO8+*oE1MyDp!G=L0#^lUx?;!fXv@l^6SvTnf^ac{5OurzC#ZMYc20lI%HhX816AYVs1T3heS1*WaWH z%;x>)-J}YB5#CLzU@GBR6sXYrD>Vw(Fmt#|JP;+}<#6b63Ike{Fuo!?M{yEffez;| zp!PfsuaC)>h>-AdbnwN13g*1LowNjT5?+lFVd#9$!8Z9HA|$*6dQ8EHLu}U|obW6f z2%uGv?vr=KNq7YYa2Roj;|zooo<)lf=&2yxM@e`kM$CmCR#x>gI>I|*Ubr({5Y^rb zghxQU22N}F51}^yfDSt786oMTc!W&V;d?76)9KXX1 z+6Okem(d}YXmmOiZq$!IPk5t8nnS{%?+vDFz3BevmFNgpIod~R{>@#@5x9zJKEHLHv!gHeK~n)Ld!M8DB|Kfe%~123&Hz1Z(86nU7*G5chmyDe ziV7$pB7pJ=96hpxHv9rCR29%bLOXlKU<_13_M8x)6;P8E1Kz6G<&P?$P^%c!M5`2` zfY2zg;VK5~^>TJGQzc+33-n~gKt{{of8GzUkWmU110IgI0DLxRIM>0US|TsM=L|@F z0Bun8U!cRB7-2apz=y-7*UxOxz@Z0)@QM)9wSGki1AZ38ceG7Q72z5`i;i=J`ILzL z@iUO?SBBG-0cQuo+an4TsLy-g-x;8P4UVwk|D8{W@U1Zi z!M)+jqy@nQ$p?5tsHp-6J304Q={v-B>66$P0IDx&YT(`IcZ~bZfmn11#rXd7<5s}y zBi9eim&zQc0Dk|2>$bs0PnLmDfMP5lcXRY&cvJ=zKxI^f0%-d$tD!`LBf9^jMSYUA zI8U?CWdY@}cRq6{5~y+)#h1!*-HcGW@+gZ4B};0OnC~`xQOyH19z*TA!!BJ%9s0V3F?CAJ{hTd#*tf+ur-W9MOURF-@B77_-OshsY}6 zOXRY=5%C^*26z?l)1=$bz30!so5tfABdSYzO+H=CpV~aaUefmjvfZ3Ttu9W&W3Iu6 zROlh0MFA5h;my}8lB0tAV-Rvc2Zs_CCSJnx@d`**$idgy-iMob4dJWWw|21b4NB=LfsYp0Aeh{Ov)yztQi;eL4y5 zMi>8^SzKqk8~k?UiQK^^-5d8c%bV?$F8%X~czyiaKCI2=UH Date: Fri, 6 Oct 2023 19:44:03 +0100 Subject: [PATCH 03/17] Update dependencies --- package.json | 8 +- pnpm-lock.yaml | 2056 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2060 insertions(+), 4 deletions(-) create mode 100644 pnpm-lock.yaml diff --git a/package.json b/package.json index 471d903..acba066 100644 --- a/package.json +++ b/package.json @@ -33,11 +33,11 @@ "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "eslint": "^8.28.0", - "eslint-config-prettier": "^8.5.0", + "eslint-config-prettier": "^9.0.0", "eslint-plugin-svelte": "^2.30.0", - "prettier": "^2.8.0", - "prettier-plugin-svelte": "^2.10.1", - "publint": "^0.1.9", + "prettier": "^3.0.3", + "prettier-plugin-svelte": "^3.0.3", + "publint": "^0.2.3", "svelte": "^4.0.5", "svelte-check": "^3.4.3", "tslib": "^2.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..4903079 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2056 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@sveltejs/adapter-auto': + specifier: ^2.0.0 + version: 2.1.0(@sveltejs/kit@1.25.1) + '@sveltejs/kit': + specifier: ^1.20.4 + version: 1.25.1(svelte@4.2.1)(vite@4.4.11) + '@sveltejs/package': + specifier: ^2.0.0 + version: 2.2.2(svelte@4.2.1)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.7.4(eslint@8.50.0)(typescript@5.2.2) + eslint: + specifier: ^8.28.0 + version: 8.50.0 + eslint-config-prettier: + specifier: ^9.0.0 + version: 9.0.0(eslint@8.50.0) + eslint-plugin-svelte: + specifier: ^2.30.0 + version: 2.34.0(eslint@8.50.0)(svelte@4.2.1) + prettier: + specifier: ^3.0.3 + version: 3.0.3 + prettier-plugin-svelte: + specifier: ^3.0.3 + version: 3.0.3(prettier@3.0.3)(svelte@4.2.1) + publint: + specifier: ^0.2.3 + version: 0.2.3 + svelte: + specifier: ^4.0.5 + version: 4.2.1 + svelte-check: + specifier: ^3.4.3 + version: 3.5.2(postcss@8.4.31)(svelte@4.2.1) + tslib: + specifier: ^2.4.1 + version: 2.6.2 + typescript: + specifier: ^5.0.0 + version: 5.2.2 + vite: + specifier: ^4.4.2 + version: 4.4.11 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.50.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.9.1: + resolution: {integrity: sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.2: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.23.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.50.0: + resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@fastify/busboy@2.0.0: + resolution: {integrity: sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==} + engines: {node: '>=14'} + dev: true + + /@humanwhocodes/config-array@0.11.11: + resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@polka/url@1.0.0-next.23: + resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==} + dev: true + + /@sveltejs/adapter-auto@2.1.0(@sveltejs/kit@1.25.1): + resolution: {integrity: sha512-o2pZCfATFtA/Gw/BB0Xm7k4EYaekXxaPGER3xGSY3FvzFJGTlJlZjBseaXwYSM94lZ0HniOjTokN3cWaLX6fow==} + peerDependencies: + '@sveltejs/kit': ^1.0.0 + dependencies: + '@sveltejs/kit': 1.25.1(svelte@4.2.1)(vite@4.4.11) + import-meta-resolve: 3.0.0 + dev: true + + /@sveltejs/kit@1.25.1(svelte@4.2.1)(vite@4.4.11): + resolution: {integrity: sha512-pD8XsvNJNgTNkFngNlM60my/X8dXWPKVzN5RghEQr0NjGZmuCjy49AfFu2cGbZjNf5pBcqd2RCNMW912P5fkhA==} + engines: {node: ^16.14 || >=18} + hasBin: true + requiresBuild: true + peerDependencies: + svelte: ^3.54.0 || ^4.0.0-next.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.4.6(svelte@4.2.1)(vite@4.4.11) + '@types/cookie': 0.5.2 + cookie: 0.5.0 + devalue: 4.3.2 + esm-env: 1.0.0 + kleur: 4.1.5 + magic-string: 0.30.4 + mime: 3.0.0 + sade: 1.8.1 + set-cookie-parser: 2.6.0 + sirv: 2.0.3 + svelte: 4.2.1 + tiny-glob: 0.2.9 + undici: 5.25.4 + vite: 4.4.11 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/package@2.2.2(svelte@4.2.1)(typescript@5.2.2): + resolution: {integrity: sha512-rP3sVv6cAntcdcG4r4KspLU6nZYYUrHJBAX3Arrw0KJFdgxtlsi2iDwN0Jwr/vIkgjcU0ZPWM8kkT5kpZDlWAw==} + engines: {node: ^16.14 || >=18} + hasBin: true + peerDependencies: + svelte: ^3.44.0 || ^4.0.0 + dependencies: + chokidar: 3.5.3 + kleur: 4.1.5 + sade: 1.8.1 + semver: 7.5.4 + svelte: 4.2.1 + svelte2tsx: 0.6.22(svelte@4.2.1)(typescript@5.2.2) + transitivePeerDependencies: + - typescript + dev: true + + /@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.4.11): + resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^2.2.0 + svelte: ^3.54.0 || ^4.0.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.4.6(svelte@4.2.1)(vite@4.4.11) + debug: 4.3.4 + svelte: 4.2.1 + vite: 4.4.11 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.4.11): + resolution: {integrity: sha512-zO79p0+DZnXPnF0ltIigWDx/ux7Ni+HRaFOw720Qeivc1azFUrJxTl0OryXVibYNx1hCboGia1NRV3x8RNv4cA==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + svelte: ^3.54.0 || ^4.0.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.4.11) + debug: 4.3.4 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.4 + svelte: 4.2.1 + svelte-hmr: 0.15.3(svelte@4.2.1) + vite: 4.4.11 + vitefu: 0.2.4(vite@4.4.11) + transitivePeerDependencies: + - supports-color + dev: true + + /@types/cookie@0.5.2: + resolution: {integrity: sha512-DBpRoJGKJZn7RY92dPrgoMew8xCWc2P71beqsjyhEI/Ds9mOyVmBwtekyfhpwFIVt1WrxTonFifiOZ62V8CnNA==} + dev: true + + /@types/estree@1.0.2: + resolution: {integrity: sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==} + dev: true + + /@types/json-schema@7.0.13: + resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} + dev: true + + /@types/pug@2.0.7: + resolution: {integrity: sha512-I469DU0UXNC1aHepwirWhu9YKg5fkxohZD95Ey/5A7lovC+Siu+MCLffva87lnfThaOrw9Vb1DUN5t55oULAAw==} + dev: true + + /@types/semver@7.5.3: + resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} + dev: true + + /@typescript-eslint/eslint-plugin@6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2): + resolution: {integrity: sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.9.1 + '@typescript-eslint/parser': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.7.4 + '@typescript-eslint/type-utils': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.4 + debug: 4.3.4 + eslint: 8.50.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.7.4(eslint@8.50.0)(typescript@5.2.2): + resolution: {integrity: sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.7.4 + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.4 + debug: 4.3.4 + eslint: 8.50.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.7.4: + resolution: {integrity: sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/visitor-keys': 6.7.4 + dev: true + + /@typescript-eslint/type-utils@6.7.4(eslint@8.50.0)(typescript@5.2.2): + resolution: {integrity: sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + debug: 4.3.4 + eslint: 8.50.0 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.7.4: + resolution: {integrity: sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.7.4(typescript@5.2.2): + resolution: {integrity: sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/visitor-keys': 6.7.4 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.7.4(eslint@8.50.0)(typescript@5.2.2): + resolution: {integrity: sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@types/json-schema': 7.0.13 + '@types/semver': 7.5.3 + '@typescript-eslint/scope-manager': 6.7.4 + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) + eslint: 8.50.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.7.4: + resolution: {integrity: sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.7.4 + eslint-visitor-keys: 3.4.3 + dev: true + + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /axobject-query@3.2.1: + resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + dependencies: + dequal: 2.0.3 + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /code-red@1.0.4: + resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + '@types/estree': 1.0.2 + acorn: 8.10.0 + estree-walker: 3.0.3 + periscopic: 3.1.0 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.0.2 + dev: true + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /dedent-js@1.0.1: + resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: true + + /detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true + + /devalue@4.3.2: + resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /es6-promise@3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + dev: true + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-prettier@9.0.0(eslint@8.50.0): + resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.50.0 + dev: true + + /eslint-plugin-svelte@2.34.0(eslint@8.50.0)(svelte@4.2.1): + resolution: {integrity: sha512-4RYUgNai7wr0v+T/kljMiYSjC/oqwgq5i+cPppawryAayj4C7WK1ixFlWCGmNmBppnoKCl4iA4ZPzPtlHcb4CA==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0-0 + svelte: ^3.37.0 || ^4.0.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@jridgewell/sourcemap-codec': 1.4.15 + debug: 4.3.4 + eslint: 8.50.0 + esutils: 2.0.3 + known-css-properties: 0.28.0 + postcss: 8.4.31 + postcss-load-config: 3.1.4(postcss@8.4.31) + postcss-safe-parser: 6.0.0(postcss@8.4.31) + postcss-selector-parser: 6.0.13 + semver: 7.5.4 + svelte: 4.2.1 + svelte-eslint-parser: 0.33.1(svelte@4.2.1) + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.50.0: + resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/regexpp': 4.9.1 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.50.0 + '@humanwhocodes/config-array': 0.11.11 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.23.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /esm-env@1.0.0: + resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.2 + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.1 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.9 + keyv: 4.5.3 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: true + + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalyzer@0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /ignore-walk@5.0.1: + resolution: {integrity: sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + minimatch: 5.1.6 + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-meta-resolve@3.0.0: + resolution: {integrity: sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==} + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + dependencies: + '@types/estree': 1.0.2 + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /keyv@4.5.3: + resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true + + /known-css-properties@0.28.0: + resolution: {integrity: sha512-9pSL5XB4J+ifHP0e0jmmC98OGC1nL8/JjS+fi6mnTlIf//yt/MfVLtKg7S6nCtj/8KTcWX7nRlY0XywoYY1ISQ==} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + + /locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.6.2 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /magic-string@0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /magic-string@0.30.4: + resolution: {integrity: sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true + + /mrmime@1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.6.2 + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-bundled@2.0.1: + resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + npm-normalize-package-bin: 2.0.0 + dev: true + + /npm-normalize-package-bin@2.0.0: + resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dev: true + + /npm-packlist@5.1.3: + resolution: {integrity: sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + dependencies: + glob: 8.1.0 + ignore-walk: 5.0.1 + npm-bundled: 2.0.1 + npm-normalize-package-bin: 2.0.0 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /periscopic@3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + dependencies: + '@types/estree': 1.0.2 + estree-walker: 3.0.3 + is-reference: 3.0.2 + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /postcss-load-config@3.1.4(postcss@8.4.31): + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.31 + yaml: 1.10.2 + dev: true + + /postcss-safe-parser@6.0.0(postcss@8.4.31): + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + dependencies: + postcss: 8.4.31 + dev: true + + /postcss-scss@4.0.9(postcss@8.4.31): + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + dependencies: + postcss: 8.4.31 + dev: true + + /postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-plugin-svelte@3.0.3(prettier@3.0.3)(svelte@4.2.1): + resolution: {integrity: sha512-dLhieh4obJEK1hnZ6koxF+tMUrZbV5YGvRpf2+OADyanjya5j0z1Llo8iGwiHmFWZVG/hLEw/AJD5chXd9r3XA==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 + dependencies: + prettier: 3.0.3 + svelte: 4.2.1 + dev: true + + /prettier@3.0.3: + resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /publint@0.2.3: + resolution: {integrity: sha512-Ml/rLotRiRTCbqL8CtWURiWDPzHtjv1SKU2E91R0ZG4mDJS3/rNQXYttM+Wt5t0JZ09MyAXIa/TYOt5OVUlYAQ==} + engines: {node: '>=16'} + hasBin: true + dependencies: + npm-packlist: 5.1.3 + picocolors: 1.0.0 + sade: 1.8.1 + dev: true + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: true + + /sander@0.5.1: + resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} + dependencies: + es6-promise: 3.3.1 + graceful-fs: 4.2.11 + mkdirp: 0.5.6 + rimraf: 2.7.1 + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /sirv@2.0.3: + resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.23 + mrmime: 1.0.1 + totalist: 3.0.1 + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /sorcery@0.11.0: + resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} + hasBin: true + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + buffer-crc32: 0.2.13 + minimist: 1.2.8 + sander: 0.5.1 + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /svelte-check@3.5.2(postcss@8.4.31)(svelte@4.2.1): + resolution: {integrity: sha512-5a/YWbiH4c+AqAUP+0VneiV5bP8YOk9JL3jwvN+k2PEPLgpu85bjQc5eE67+eIZBBwUEJzmO3I92OqKcqbp3fw==} + hasBin: true + peerDependencies: + svelte: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 + dependencies: + '@jridgewell/trace-mapping': 0.3.19 + chokidar: 3.5.3 + fast-glob: 3.3.1 + import-fresh: 3.3.0 + picocolors: 1.0.0 + sade: 1.8.1 + svelte: 4.2.1 + svelte-preprocess: 5.0.4(postcss@8.4.31)(svelte@4.2.1)(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - less + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + dev: true + + /svelte-eslint-parser@0.33.1(svelte@4.2.1): + resolution: {integrity: sha512-vo7xPGTlKBGdLH8T5L64FipvTrqv3OQRx9d2z5X05KKZDlF4rQk8KViZO4flKERY+5BiVdOh7zZ7JGJWo5P0uA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + postcss: 8.4.31 + postcss-scss: 4.0.9(postcss@8.4.31) + svelte: 4.2.1 + dev: true + + /svelte-hmr@0.15.3(svelte@4.2.1): + resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: ^3.19.0 || ^4.0.0 + dependencies: + svelte: 4.2.1 + dev: true + + /svelte-preprocess@5.0.4(postcss@8.4.31)(svelte@4.2.1)(typescript@5.2.2): + resolution: {integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==} + engines: {node: '>= 14.10.0'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 || ^4.0.0 + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.55.0 + sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 + svelte: ^3.23.0 || ^4.0.0-next.0 || ^4.0.0 + typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0' + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.7 + detect-indent: 6.1.0 + magic-string: 0.27.0 + postcss: 8.4.31 + sorcery: 0.11.0 + strip-indent: 3.0.0 + svelte: 4.2.1 + typescript: 5.2.2 + dev: true + + /svelte2tsx@0.6.22(svelte@4.2.1)(typescript@5.2.2): + resolution: {integrity: sha512-eFCfz0juaWeanbwGeQV21kPMwH3LKhfrUYRy1PqRmlieuHvJs8VeK7CaoHJdpBZWCXba2cltHVdywJmwOGhbww==} + peerDependencies: + svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 + typescript: ^4.9.4 || ^5.0.0 + dependencies: + dedent-js: 1.0.1 + pascal-case: 3.1.2 + svelte: 4.2.1 + typescript: 5.2.2 + dev: true + + /svelte@4.2.1: + resolution: {integrity: sha512-LpLqY2Jr7cRxkrTc796/AaaoMLF/1ax7cto8Ot76wrvKQhrPmZ0JgajiWPmg9mTSDqO16SSLiD17r9MsvAPTmw==} + engines: {node: '>=16'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + acorn: 8.10.0 + aria-query: 5.3.0 + axobject-query: 3.2.1 + code-red: 1.0.4 + css-tree: 2.3.1 + estree-walker: 3.0.3 + is-reference: 3.0.2 + locate-character: 3.0.0 + magic-string: 0.30.4 + periscopic: 3.1.0 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /tiny-glob@0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + + /ts-api-utils@1.0.3(typescript@5.2.2): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /undici@5.25.4: + resolution: {integrity: sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==} + engines: {node: '>=14.0'} + dependencies: + '@fastify/busboy': 2.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /vite@4.4.11: + resolution: {integrity: sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.18.20 + postcss: 8.4.31 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitefu@0.2.4(vite@4.4.11): + resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 4.4.11 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true From c298638586534e4cc70efeff85253dee35091a74 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:46:59 +0100 Subject: [PATCH 04/17] Move swissgl lib and demo files --- {swissgl/demo => src/demos}/BitField.js | 0 {swissgl/demo => src/demos}/ColorCube.js | 0 {swissgl/demo => src/demos}/CubeDeform.js | 0 .../demo => src/demos}/DeferredShading.js | 0 {swissgl/demo => src/demos}/DotCamera.js | 0 {swissgl/demo => src/demos}/FancyLenia.js | 0 {swissgl/demo => src/demos}/GameOfLife.js | 0 {swissgl/demo => src/demos}/MeshGrid.js | 0 {swissgl/demo => src/demos}/NeuralCA.js | 0 {swissgl/demo => src/demos}/ParticleLenia.js | 0 {swissgl/demo => src/demos}/ParticleLife.js | 0 {swissgl/demo => src/demos}/ParticleLife3d.js | 0 {swissgl/demo => src/demos}/Physarum.js | 0 {swissgl/demo => src/demos}/Physarum3d.js | 0 .../demo => src/demos}/ReactionDiffusion.js | 0 {swissgl/demo => src/demos}/Shadowmap.js | 0 {swissgl/demo => src/demos}/Spectrogram.js | 0 {swissgl/demo => src/demos}/Springs.js | 0 {swissgl/demo => src/demos}/SurfaceNormals.js | 0 .../demo => src/demos}/TextureSamplers.js | 0 {swissgl/demo => src/demos}/Torus4d.js | 0 {swissgl => src/demos}/audio.js | 0 {swissgl/demo => src/demos}/dat.gui.min.js | 0 {swissgl/demo => src/demos}/main.js | 0 .../demo => src/demos}/preview/BitField.jpg | Bin .../demo => src/demos}/preview/ColorCube.jpg | Bin .../demo => src/demos}/preview/CubeDeform.jpg | Bin .../demos}/preview/DeferredShading.jpg | Bin .../demo => src/demos}/preview/DotCamera.jpg | Bin .../demo => src/demos}/preview/FancyLenia.jpg | Bin .../demo => src/demos}/preview/GameOfLife.jpg | Bin .../demo => src/demos}/preview/MeshGrid.jpg | Bin .../demo => src/demos}/preview/NeuralCA.jpg | Bin .../demos}/preview/ParticleLenia.jpg | Bin .../demos}/preview/ParticleLife.jpg | Bin .../demos}/preview/ParticleLife3d.jpg | Bin .../demo => src/demos}/preview/Physarum.jpg | Bin .../demo => src/demos}/preview/Physarum3d.jpg | Bin .../demos}/preview/ReactionDiffusion.jpg | Bin .../demo => src/demos}/preview/Shadowmap.jpg | Bin .../demos}/preview/Spectrogram.jpg | Bin .../demo => src/demos}/preview/Springs.jpg | Bin .../demos}/preview/SurfaceNormals.jpg | Bin .../demos}/preview/TextureSamplers.jpg | Bin .../demo => src/demos}/preview/Torus4d.jpg | Bin {swissgl/demo => src/demos}/style.css | 0 {swissgl => src/lib}/swissgl.js | 0 swissgl/demo/criticality.html | 85 ------------------ 48 files changed, 85 deletions(-) rename {swissgl/demo => src/demos}/BitField.js (100%) rename {swissgl/demo => src/demos}/ColorCube.js (100%) rename {swissgl/demo => src/demos}/CubeDeform.js (100%) rename {swissgl/demo => src/demos}/DeferredShading.js (100%) rename {swissgl/demo => src/demos}/DotCamera.js (100%) rename {swissgl/demo => src/demos}/FancyLenia.js (100%) rename {swissgl/demo => src/demos}/GameOfLife.js (100%) rename {swissgl/demo => src/demos}/MeshGrid.js (100%) rename {swissgl/demo => src/demos}/NeuralCA.js (100%) rename {swissgl/demo => src/demos}/ParticleLenia.js (100%) rename {swissgl/demo => src/demos}/ParticleLife.js (100%) rename {swissgl/demo => src/demos}/ParticleLife3d.js (100%) rename {swissgl/demo => src/demos}/Physarum.js (100%) rename {swissgl/demo => src/demos}/Physarum3d.js (100%) rename {swissgl/demo => src/demos}/ReactionDiffusion.js (100%) rename {swissgl/demo => src/demos}/Shadowmap.js (100%) rename {swissgl/demo => src/demos}/Spectrogram.js (100%) rename {swissgl/demo => src/demos}/Springs.js (100%) rename {swissgl/demo => src/demos}/SurfaceNormals.js (100%) rename {swissgl/demo => src/demos}/TextureSamplers.js (100%) rename {swissgl/demo => src/demos}/Torus4d.js (100%) rename {swissgl => src/demos}/audio.js (100%) rename {swissgl/demo => src/demos}/dat.gui.min.js (100%) rename {swissgl/demo => src/demos}/main.js (100%) rename {swissgl/demo => src/demos}/preview/BitField.jpg (100%) rename {swissgl/demo => src/demos}/preview/ColorCube.jpg (100%) rename {swissgl/demo => src/demos}/preview/CubeDeform.jpg (100%) rename {swissgl/demo => src/demos}/preview/DeferredShading.jpg (100%) rename {swissgl/demo => src/demos}/preview/DotCamera.jpg (100%) rename {swissgl/demo => src/demos}/preview/FancyLenia.jpg (100%) rename {swissgl/demo => src/demos}/preview/GameOfLife.jpg (100%) rename {swissgl/demo => src/demos}/preview/MeshGrid.jpg (100%) rename {swissgl/demo => src/demos}/preview/NeuralCA.jpg (100%) rename {swissgl/demo => src/demos}/preview/ParticleLenia.jpg (100%) rename {swissgl/demo => src/demos}/preview/ParticleLife.jpg (100%) rename {swissgl/demo => src/demos}/preview/ParticleLife3d.jpg (100%) rename {swissgl/demo => src/demos}/preview/Physarum.jpg (100%) rename {swissgl/demo => src/demos}/preview/Physarum3d.jpg (100%) rename {swissgl/demo => src/demos}/preview/ReactionDiffusion.jpg (100%) rename {swissgl/demo => src/demos}/preview/Shadowmap.jpg (100%) rename {swissgl/demo => src/demos}/preview/Spectrogram.jpg (100%) rename {swissgl/demo => src/demos}/preview/Springs.jpg (100%) rename {swissgl/demo => src/demos}/preview/SurfaceNormals.jpg (100%) rename {swissgl/demo => src/demos}/preview/TextureSamplers.jpg (100%) rename {swissgl/demo => src/demos}/preview/Torus4d.jpg (100%) rename {swissgl/demo => src/demos}/style.css (100%) rename {swissgl => src/lib}/swissgl.js (100%) delete mode 100644 swissgl/demo/criticality.html diff --git a/swissgl/demo/BitField.js b/src/demos/BitField.js similarity index 100% rename from swissgl/demo/BitField.js rename to src/demos/BitField.js diff --git a/swissgl/demo/ColorCube.js b/src/demos/ColorCube.js similarity index 100% rename from swissgl/demo/ColorCube.js rename to src/demos/ColorCube.js diff --git a/swissgl/demo/CubeDeform.js b/src/demos/CubeDeform.js similarity index 100% rename from swissgl/demo/CubeDeform.js rename to src/demos/CubeDeform.js diff --git a/swissgl/demo/DeferredShading.js b/src/demos/DeferredShading.js similarity index 100% rename from swissgl/demo/DeferredShading.js rename to src/demos/DeferredShading.js diff --git a/swissgl/demo/DotCamera.js b/src/demos/DotCamera.js similarity index 100% rename from swissgl/demo/DotCamera.js rename to src/demos/DotCamera.js diff --git a/swissgl/demo/FancyLenia.js b/src/demos/FancyLenia.js similarity index 100% rename from swissgl/demo/FancyLenia.js rename to src/demos/FancyLenia.js diff --git a/swissgl/demo/GameOfLife.js b/src/demos/GameOfLife.js similarity index 100% rename from swissgl/demo/GameOfLife.js rename to src/demos/GameOfLife.js diff --git a/swissgl/demo/MeshGrid.js b/src/demos/MeshGrid.js similarity index 100% rename from swissgl/demo/MeshGrid.js rename to src/demos/MeshGrid.js diff --git a/swissgl/demo/NeuralCA.js b/src/demos/NeuralCA.js similarity index 100% rename from swissgl/demo/NeuralCA.js rename to src/demos/NeuralCA.js diff --git a/swissgl/demo/ParticleLenia.js b/src/demos/ParticleLenia.js similarity index 100% rename from swissgl/demo/ParticleLenia.js rename to src/demos/ParticleLenia.js diff --git a/swissgl/demo/ParticleLife.js b/src/demos/ParticleLife.js similarity index 100% rename from swissgl/demo/ParticleLife.js rename to src/demos/ParticleLife.js diff --git a/swissgl/demo/ParticleLife3d.js b/src/demos/ParticleLife3d.js similarity index 100% rename from swissgl/demo/ParticleLife3d.js rename to src/demos/ParticleLife3d.js diff --git a/swissgl/demo/Physarum.js b/src/demos/Physarum.js similarity index 100% rename from swissgl/demo/Physarum.js rename to src/demos/Physarum.js diff --git a/swissgl/demo/Physarum3d.js b/src/demos/Physarum3d.js similarity index 100% rename from swissgl/demo/Physarum3d.js rename to src/demos/Physarum3d.js diff --git a/swissgl/demo/ReactionDiffusion.js b/src/demos/ReactionDiffusion.js similarity index 100% rename from swissgl/demo/ReactionDiffusion.js rename to src/demos/ReactionDiffusion.js diff --git a/swissgl/demo/Shadowmap.js b/src/demos/Shadowmap.js similarity index 100% rename from swissgl/demo/Shadowmap.js rename to src/demos/Shadowmap.js diff --git a/swissgl/demo/Spectrogram.js b/src/demos/Spectrogram.js similarity index 100% rename from swissgl/demo/Spectrogram.js rename to src/demos/Spectrogram.js diff --git a/swissgl/demo/Springs.js b/src/demos/Springs.js similarity index 100% rename from swissgl/demo/Springs.js rename to src/demos/Springs.js diff --git a/swissgl/demo/SurfaceNormals.js b/src/demos/SurfaceNormals.js similarity index 100% rename from swissgl/demo/SurfaceNormals.js rename to src/demos/SurfaceNormals.js diff --git a/swissgl/demo/TextureSamplers.js b/src/demos/TextureSamplers.js similarity index 100% rename from swissgl/demo/TextureSamplers.js rename to src/demos/TextureSamplers.js diff --git a/swissgl/demo/Torus4d.js b/src/demos/Torus4d.js similarity index 100% rename from swissgl/demo/Torus4d.js rename to src/demos/Torus4d.js diff --git a/swissgl/audio.js b/src/demos/audio.js similarity index 100% rename from swissgl/audio.js rename to src/demos/audio.js diff --git a/swissgl/demo/dat.gui.min.js b/src/demos/dat.gui.min.js similarity index 100% rename from swissgl/demo/dat.gui.min.js rename to src/demos/dat.gui.min.js diff --git a/swissgl/demo/main.js b/src/demos/main.js similarity index 100% rename from swissgl/demo/main.js rename to src/demos/main.js diff --git a/swissgl/demo/preview/BitField.jpg b/src/demos/preview/BitField.jpg similarity index 100% rename from swissgl/demo/preview/BitField.jpg rename to src/demos/preview/BitField.jpg diff --git a/swissgl/demo/preview/ColorCube.jpg b/src/demos/preview/ColorCube.jpg similarity index 100% rename from swissgl/demo/preview/ColorCube.jpg rename to src/demos/preview/ColorCube.jpg diff --git a/swissgl/demo/preview/CubeDeform.jpg b/src/demos/preview/CubeDeform.jpg similarity index 100% rename from swissgl/demo/preview/CubeDeform.jpg rename to src/demos/preview/CubeDeform.jpg diff --git a/swissgl/demo/preview/DeferredShading.jpg b/src/demos/preview/DeferredShading.jpg similarity index 100% rename from swissgl/demo/preview/DeferredShading.jpg rename to src/demos/preview/DeferredShading.jpg diff --git a/swissgl/demo/preview/DotCamera.jpg b/src/demos/preview/DotCamera.jpg similarity index 100% rename from swissgl/demo/preview/DotCamera.jpg rename to src/demos/preview/DotCamera.jpg diff --git a/swissgl/demo/preview/FancyLenia.jpg b/src/demos/preview/FancyLenia.jpg similarity index 100% rename from swissgl/demo/preview/FancyLenia.jpg rename to src/demos/preview/FancyLenia.jpg diff --git a/swissgl/demo/preview/GameOfLife.jpg b/src/demos/preview/GameOfLife.jpg similarity index 100% rename from swissgl/demo/preview/GameOfLife.jpg rename to src/demos/preview/GameOfLife.jpg diff --git a/swissgl/demo/preview/MeshGrid.jpg b/src/demos/preview/MeshGrid.jpg similarity index 100% rename from swissgl/demo/preview/MeshGrid.jpg rename to src/demos/preview/MeshGrid.jpg diff --git a/swissgl/demo/preview/NeuralCA.jpg b/src/demos/preview/NeuralCA.jpg similarity index 100% rename from swissgl/demo/preview/NeuralCA.jpg rename to src/demos/preview/NeuralCA.jpg diff --git a/swissgl/demo/preview/ParticleLenia.jpg b/src/demos/preview/ParticleLenia.jpg similarity index 100% rename from swissgl/demo/preview/ParticleLenia.jpg rename to src/demos/preview/ParticleLenia.jpg diff --git a/swissgl/demo/preview/ParticleLife.jpg b/src/demos/preview/ParticleLife.jpg similarity index 100% rename from swissgl/demo/preview/ParticleLife.jpg rename to src/demos/preview/ParticleLife.jpg diff --git a/swissgl/demo/preview/ParticleLife3d.jpg b/src/demos/preview/ParticleLife3d.jpg similarity index 100% rename from swissgl/demo/preview/ParticleLife3d.jpg rename to src/demos/preview/ParticleLife3d.jpg diff --git a/swissgl/demo/preview/Physarum.jpg b/src/demos/preview/Physarum.jpg similarity index 100% rename from swissgl/demo/preview/Physarum.jpg rename to src/demos/preview/Physarum.jpg diff --git a/swissgl/demo/preview/Physarum3d.jpg b/src/demos/preview/Physarum3d.jpg similarity index 100% rename from swissgl/demo/preview/Physarum3d.jpg rename to src/demos/preview/Physarum3d.jpg diff --git a/swissgl/demo/preview/ReactionDiffusion.jpg b/src/demos/preview/ReactionDiffusion.jpg similarity index 100% rename from swissgl/demo/preview/ReactionDiffusion.jpg rename to src/demos/preview/ReactionDiffusion.jpg diff --git a/swissgl/demo/preview/Shadowmap.jpg b/src/demos/preview/Shadowmap.jpg similarity index 100% rename from swissgl/demo/preview/Shadowmap.jpg rename to src/demos/preview/Shadowmap.jpg diff --git a/swissgl/demo/preview/Spectrogram.jpg b/src/demos/preview/Spectrogram.jpg similarity index 100% rename from swissgl/demo/preview/Spectrogram.jpg rename to src/demos/preview/Spectrogram.jpg diff --git a/swissgl/demo/preview/Springs.jpg b/src/demos/preview/Springs.jpg similarity index 100% rename from swissgl/demo/preview/Springs.jpg rename to src/demos/preview/Springs.jpg diff --git a/swissgl/demo/preview/SurfaceNormals.jpg b/src/demos/preview/SurfaceNormals.jpg similarity index 100% rename from swissgl/demo/preview/SurfaceNormals.jpg rename to src/demos/preview/SurfaceNormals.jpg diff --git a/swissgl/demo/preview/TextureSamplers.jpg b/src/demos/preview/TextureSamplers.jpg similarity index 100% rename from swissgl/demo/preview/TextureSamplers.jpg rename to src/demos/preview/TextureSamplers.jpg diff --git a/swissgl/demo/preview/Torus4d.jpg b/src/demos/preview/Torus4d.jpg similarity index 100% rename from swissgl/demo/preview/Torus4d.jpg rename to src/demos/preview/Torus4d.jpg diff --git a/swissgl/demo/style.css b/src/demos/style.css similarity index 100% rename from swissgl/demo/style.css rename to src/demos/style.css diff --git a/swissgl/swissgl.js b/src/lib/swissgl.js similarity index 100% rename from swissgl/swissgl.js rename to src/lib/swissgl.js diff --git a/swissgl/demo/criticality.html b/swissgl/demo/criticality.html deleted file mode 100644 index 82d2deb..0000000 --- a/swissgl/demo/criticality.html +++ /dev/null @@ -1,85 +0,0 @@ - -Tiny SwissGL example - - - - - - -- \ No newline at end of file From 63d9848687758e85806996a4859d1d3e22207c00 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:48:48 +0100 Subject: [PATCH 05/17] Move swissgl static image files --- {swissgl => static}/images/F.png | Bin {swissgl => static}/images/init_particles.png | Bin {swissgl => static}/images/particle_snake.png | Bin {swissgl => static}/images/quad.png | Bin {src/demos => static}/preview/BitField.jpg | Bin {src/demos => static}/preview/ColorCube.jpg | Bin {src/demos => static}/preview/CubeDeform.jpg | Bin {src/demos => static}/preview/DeferredShading.jpg | Bin {src/demos => static}/preview/DotCamera.jpg | Bin {src/demos => static}/preview/FancyLenia.jpg | Bin {src/demos => static}/preview/GameOfLife.jpg | Bin {src/demos => static}/preview/MeshGrid.jpg | Bin {src/demos => static}/preview/NeuralCA.jpg | Bin {src/demos => static}/preview/ParticleLenia.jpg | Bin {src/demos => static}/preview/ParticleLife.jpg | Bin {src/demos => static}/preview/ParticleLife3d.jpg | Bin {src/demos => static}/preview/Physarum.jpg | Bin {src/demos => static}/preview/Physarum3d.jpg | Bin {src/demos => static}/preview/ReactionDiffusion.jpg | Bin {src/demos => static}/preview/Shadowmap.jpg | Bin {src/demos => static}/preview/Spectrogram.jpg | Bin {src/demos => static}/preview/Springs.jpg | Bin {src/demos => static}/preview/SurfaceNormals.jpg | Bin {src/demos => static}/preview/TextureSamplers.jpg | Bin {src/demos => static}/preview/Torus4d.jpg | Bin 25 files changed, 0 insertions(+), 0 deletions(-) rename {swissgl => static}/images/F.png (100%) rename {swissgl => static}/images/init_particles.png (100%) rename {swissgl => static}/images/particle_snake.png (100%) rename {swissgl => static}/images/quad.png (100%) rename {src/demos => static}/preview/BitField.jpg (100%) rename {src/demos => static}/preview/ColorCube.jpg (100%) rename {src/demos => static}/preview/CubeDeform.jpg (100%) rename {src/demos => static}/preview/DeferredShading.jpg (100%) rename {src/demos => static}/preview/DotCamera.jpg (100%) rename {src/demos => static}/preview/FancyLenia.jpg (100%) rename {src/demos => static}/preview/GameOfLife.jpg (100%) rename {src/demos => static}/preview/MeshGrid.jpg (100%) rename {src/demos => static}/preview/NeuralCA.jpg (100%) rename {src/demos => static}/preview/ParticleLenia.jpg (100%) rename {src/demos => static}/preview/ParticleLife.jpg (100%) rename {src/demos => static}/preview/ParticleLife3d.jpg (100%) rename {src/demos => static}/preview/Physarum.jpg (100%) rename {src/demos => static}/preview/Physarum3d.jpg (100%) rename {src/demos => static}/preview/ReactionDiffusion.jpg (100%) rename {src/demos => static}/preview/Shadowmap.jpg (100%) rename {src/demos => static}/preview/Spectrogram.jpg (100%) rename {src/demos => static}/preview/Springs.jpg (100%) rename {src/demos => static}/preview/SurfaceNormals.jpg (100%) rename {src/demos => static}/preview/TextureSamplers.jpg (100%) rename {src/demos => static}/preview/Torus4d.jpg (100%) diff --git a/swissgl/images/F.png b/static/images/F.png similarity index 100% rename from swissgl/images/F.png rename to static/images/F.png diff --git a/swissgl/images/init_particles.png b/static/images/init_particles.png similarity index 100% rename from swissgl/images/init_particles.png rename to static/images/init_particles.png diff --git a/swissgl/images/particle_snake.png b/static/images/particle_snake.png similarity index 100% rename from swissgl/images/particle_snake.png rename to static/images/particle_snake.png diff --git a/swissgl/images/quad.png b/static/images/quad.png similarity index 100% rename from swissgl/images/quad.png rename to static/images/quad.png diff --git a/src/demos/preview/BitField.jpg b/static/preview/BitField.jpg similarity index 100% rename from src/demos/preview/BitField.jpg rename to static/preview/BitField.jpg diff --git a/src/demos/preview/ColorCube.jpg b/static/preview/ColorCube.jpg similarity index 100% rename from src/demos/preview/ColorCube.jpg rename to static/preview/ColorCube.jpg diff --git a/src/demos/preview/CubeDeform.jpg b/static/preview/CubeDeform.jpg similarity index 100% rename from src/demos/preview/CubeDeform.jpg rename to static/preview/CubeDeform.jpg diff --git a/src/demos/preview/DeferredShading.jpg b/static/preview/DeferredShading.jpg similarity index 100% rename from src/demos/preview/DeferredShading.jpg rename to static/preview/DeferredShading.jpg diff --git a/src/demos/preview/DotCamera.jpg b/static/preview/DotCamera.jpg similarity index 100% rename from src/demos/preview/DotCamera.jpg rename to static/preview/DotCamera.jpg diff --git a/src/demos/preview/FancyLenia.jpg b/static/preview/FancyLenia.jpg similarity index 100% rename from src/demos/preview/FancyLenia.jpg rename to static/preview/FancyLenia.jpg diff --git a/src/demos/preview/GameOfLife.jpg b/static/preview/GameOfLife.jpg similarity index 100% rename from src/demos/preview/GameOfLife.jpg rename to static/preview/GameOfLife.jpg diff --git a/src/demos/preview/MeshGrid.jpg b/static/preview/MeshGrid.jpg similarity index 100% rename from src/demos/preview/MeshGrid.jpg rename to static/preview/MeshGrid.jpg diff --git a/src/demos/preview/NeuralCA.jpg b/static/preview/NeuralCA.jpg similarity index 100% rename from src/demos/preview/NeuralCA.jpg rename to static/preview/NeuralCA.jpg diff --git a/src/demos/preview/ParticleLenia.jpg b/static/preview/ParticleLenia.jpg similarity index 100% rename from src/demos/preview/ParticleLenia.jpg rename to static/preview/ParticleLenia.jpg diff --git a/src/demos/preview/ParticleLife.jpg b/static/preview/ParticleLife.jpg similarity index 100% rename from src/demos/preview/ParticleLife.jpg rename to static/preview/ParticleLife.jpg diff --git a/src/demos/preview/ParticleLife3d.jpg b/static/preview/ParticleLife3d.jpg similarity index 100% rename from src/demos/preview/ParticleLife3d.jpg rename to static/preview/ParticleLife3d.jpg diff --git a/src/demos/preview/Physarum.jpg b/static/preview/Physarum.jpg similarity index 100% rename from src/demos/preview/Physarum.jpg rename to static/preview/Physarum.jpg diff --git a/src/demos/preview/Physarum3d.jpg b/static/preview/Physarum3d.jpg similarity index 100% rename from src/demos/preview/Physarum3d.jpg rename to static/preview/Physarum3d.jpg diff --git a/src/demos/preview/ReactionDiffusion.jpg b/static/preview/ReactionDiffusion.jpg similarity index 100% rename from src/demos/preview/ReactionDiffusion.jpg rename to static/preview/ReactionDiffusion.jpg diff --git a/src/demos/preview/Shadowmap.jpg b/static/preview/Shadowmap.jpg similarity index 100% rename from src/demos/preview/Shadowmap.jpg rename to static/preview/Shadowmap.jpg diff --git a/src/demos/preview/Spectrogram.jpg b/static/preview/Spectrogram.jpg similarity index 100% rename from src/demos/preview/Spectrogram.jpg rename to static/preview/Spectrogram.jpg diff --git a/src/demos/preview/Springs.jpg b/static/preview/Springs.jpg similarity index 100% rename from src/demos/preview/Springs.jpg rename to static/preview/Springs.jpg diff --git a/src/demos/preview/SurfaceNormals.jpg b/static/preview/SurfaceNormals.jpg similarity index 100% rename from src/demos/preview/SurfaceNormals.jpg rename to static/preview/SurfaceNormals.jpg diff --git a/src/demos/preview/TextureSamplers.jpg b/static/preview/TextureSamplers.jpg similarity index 100% rename from src/demos/preview/TextureSamplers.jpg rename to static/preview/TextureSamplers.jpg diff --git a/src/demos/preview/Torus4d.jpg b/static/preview/Torus4d.jpg similarity index 100% rename from src/demos/preview/Torus4d.jpg rename to static/preview/Torus4d.jpg From 15c4fb4a806555a1702456568821262c49bb54bb Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:50:05 +0100 Subject: [PATCH 06/17] Move swissgl docs --- swissgl/README.md => README.md | 0 {swissgl/docs => docs}/API.md | 0 {swissgl/docs => docs}/CHANGELOG.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename swissgl/README.md => README.md (100%) rename {swissgl/docs => docs}/API.md (100%) rename {swissgl/docs => docs}/CHANGELOG.md (100%) diff --git a/swissgl/README.md b/README.md similarity index 100% rename from swissgl/README.md rename to README.md diff --git a/swissgl/docs/API.md b/docs/API.md similarity index 100% rename from swissgl/docs/API.md rename to docs/API.md diff --git a/swissgl/docs/CHANGELOG.md b/docs/CHANGELOG.md similarity index 100% rename from swissgl/docs/CHANGELOG.md rename to docs/CHANGELOG.md From 66e29895ab7624d55ca6c5df8d9bbd49cf37362e Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:52:24 +0100 Subject: [PATCH 07/17] Remove default index +page.svelte --- src/routes/+page.svelte | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/routes/+page.svelte diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte deleted file mode 100644 index 0a45b69..0000000 --- a/src/routes/+page.svelte +++ /dev/null @@ -1,3 +0,0 @@ -

Welcome to your library project

-

Create your package using @sveltejs/package and preview/showcase your work with SvelteKit

-

Visit kit.svelte.dev to read the documentation

From d92149a94acbe9b32a2dec880d2b756ba4cf8556 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:53:53 +0100 Subject: [PATCH 08/17] Move swissgl html files as prototypes for .svelte routes --- swissgl/index.html => src/routes/+page.svelte | 0 src/routes/criticality/+page.svelte | 85 +++++++++++++++++++ .../tiny.html => src/routes/tiny/+page.svelte | 0 3 files changed, 85 insertions(+) rename swissgl/index.html => src/routes/+page.svelte (100%) create mode 100644 src/routes/criticality/+page.svelte rename swissgl/tiny.html => src/routes/tiny/+page.svelte (100%) diff --git a/swissgl/index.html b/src/routes/+page.svelte similarity index 100% rename from swissgl/index.html rename to src/routes/+page.svelte diff --git a/src/routes/criticality/+page.svelte b/src/routes/criticality/+page.svelte new file mode 100644 index 0000000..82d2deb --- /dev/null +++ b/src/routes/criticality/+page.svelte @@ -0,0 +1,85 @@ + +Tiny SwissGL example + + + + + + +- \ No newline at end of file diff --git a/swissgl/tiny.html b/src/routes/tiny/+page.svelte similarity index 100% rename from swissgl/tiny.html rename to src/routes/tiny/+page.svelte From 92db8afdfdb6a964567a00280cdb49835b0a83fa Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:54:25 +0100 Subject: [PATCH 09/17] Remove remaining swissgl files --- swissgl/.gitignore | 1 - swissgl/CONTRIBUTING.md | 33 --------------------------------- swissgl/package.json | 23 ----------------------- swissgl/swissgl.mjs | 5 ----- 4 files changed, 62 deletions(-) delete mode 100644 swissgl/.gitignore delete mode 100644 swissgl/CONTRIBUTING.md delete mode 100644 swissgl/package.json delete mode 100644 swissgl/swissgl.mjs diff --git a/swissgl/.gitignore b/swissgl/.gitignore deleted file mode 100644 index e43b0f9..0000000 --- a/swissgl/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.DS_Store diff --git a/swissgl/CONTRIBUTING.md b/swissgl/CONTRIBUTING.md deleted file mode 100644 index 37ae0a4..0000000 --- a/swissgl/CONTRIBUTING.md +++ /dev/null @@ -1,33 +0,0 @@ -# How to Contribute - -We'd love to accept your patches and contributions to this project. - -## Before you begin - -### Sign our Contributor License Agreement - -Contributions to this project must be accompanied by a -[Contributor License Agreement](https://cla.developers.google.com/about) (CLA). -You (or your employer) retain the copyright to your contribution; this simply -gives us permission to use and redistribute your contributions as part of the -project. - -If you or your current employer have already signed the Google CLA (even if it -was for a different project), you probably don't need to do it again. - -Visit to see your current agreements or to -sign a new one. - -### Review our Community Guidelines - -This project follows -[Google's Open Source Community Guidelines](https://opensource.google/conduct/). - -## Contribution process - -### Code Reviews - -All submissions, including submissions by project members, require review. We -use GitHub pull requests for this purpose. Consult -[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more -information on using pull requests. diff --git a/swissgl/package.json b/swissgl/package.json deleted file mode 100644 index 2e90678..0000000 --- a/swissgl/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "swissgl", - "version": "0.0.1", - "description": "SwissGL is a minimalistic wrapper on top of WebGL2 JS API. It's designed to reduce the amount of boilerplate code required to manage GLSL shaders, textures and framebuffers when making procedural visualizations or simulations.", - "main": "swissgl.js", - "module": "swissgl.mjs", - "directories": { - "doc": "docs" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/google/swissgl.git" - }, - "author": "Alexander Mordvintsev", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/google/swissgl/issues" - }, - "homepage": "https://github.com/google/swissgl#readme" -} diff --git a/swissgl/swissgl.mjs b/swissgl/swissgl.mjs deleted file mode 100644 index b4ef88b..0000000 --- a/swissgl/swissgl.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import './swissgl.js' - -const SwissGL = self._SwissGL; - -export default SwissGL; From c025b123e2a9f67006d47e6953f6e75f03829768 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 19:55:41 +0100 Subject: [PATCH 10/17] Start porting lib to ts --- src/lib/{index.js => index.ts} | 0 src/lib/{swissgl.js => swissgl.ts} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/lib/{index.js => index.ts} (100%) rename src/lib/{swissgl.js => swissgl.ts} (100%) diff --git a/src/lib/index.js b/src/lib/index.ts similarity index 100% rename from src/lib/index.js rename to src/lib/index.ts diff --git a/src/lib/swissgl.js b/src/lib/swissgl.ts similarity index 100% rename from src/lib/swissgl.js rename to src/lib/swissgl.ts From 83c5302ae77eec754dbf4d17c2d20f82251f6793 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 20:01:19 +0100 Subject: [PATCH 11/17] pnpm format --- README.md | 145 ++- docs/API.md | 82 +- docs/CHANGELOG.md | 101 +- src/app.html | 2 +- src/demos/BitField.js | 26 +- src/demos/ColorCube.js | 30 +- src/demos/CubeDeform.js | 29 +- src/demos/DeferredShading.js | 74 +- src/demos/DotCamera.js | 149 ++- src/demos/FancyLenia.js | 206 +-- src/demos/GameOfLife.js | 31 +- src/demos/MeshGrid.js | 24 +- src/demos/NeuralCA.js | 41 +- src/demos/ParticleLenia.js | 151 ++- src/demos/ParticleLife.js | 151 ++- src/demos/ParticleLife3d.js | 127 +- src/demos/Physarum.js | 156 ++- src/demos/Physarum3d.js | 103 +- src/demos/ReactionDiffusion.js | 92 +- src/demos/Shadowmap.js | 120 +- src/demos/Spectrogram.js | 84 +- src/demos/Springs.js | 91 +- src/demos/SurfaceNormals.js | 31 +- src/demos/TextureSamplers.js | 37 +- src/demos/Torus4d.js | 29 +- src/demos/audio.js | 134 +- src/demos/dat.gui.min.js | 2213 +++++++++++++++++++++++++++++++- src/demos/main.js | 524 ++++---- src/demos/style.css | 120 +- src/lib/swissgl.ts | 1251 ++++++++++-------- 30 files changed, 4637 insertions(+), 1717 deletions(-) diff --git a/README.md b/README.md index 5555e6c..fac668b 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,14 @@ As of now, the library API consists of a single function object that does everyt ``` + ![SwissGL quad gradient](images/quad.png) `glsl` function has the following signature: + ```js glsl(params, target); ``` + All it can do is to draw instanced, tessellated plane primitives into the specified (may be created in-place) target buffer using the provided vertex and fragment shaders. This may sound unimpressive, but we'll see that it's possible to do some pretty complex things with such a simple tool! Please refer to the [API reference](docs/API.md) for the detailed explanation `glsl` arguments. Let's now have a look at the more elaborate example of using SwissGL to implement a particle simulation. ## Particle Life Inspired by the [beautiful video](https://youtu.be/p4YirERTVF0?t=481) by Tom Mohr, let's try reproduce the "snake" pattern shown there. Particle Life is made of particles of a few different types. All particles repel when they are closer than some distance $r$, but at around $2r$ the resulting (potentially non-symmetric) force is described by the special force matrix $F_{i,j}$, where $i,j$ are types of two particles. Positive $F$ corresponds to attraction and negative to repulsion. Let's create a texture that stores such a matrix. We can create an array on the JS side and pass it to SwissGL, but it's even easier to populate matrix values right on the GPU: + ```js const K = 6; // number of particle types -const F = glsl({K, FP: - `float(I.x==I.y) + 0.1*float(I.x==(I.y+1)%int(K))`}, - {size:[K,K], format:'r16f', tag:'F'}); +const F = glsl( + { K, FP: `float(I.x==I.y) + 0.1*float(I.x==(I.y+1)%int(K))` }, + { size: [K, K], format: 'r16f', tag: 'F' } +); ``` This creates a single channel float16 texture of size `[width,height]==[6,6]` and populates its values by evaluating the `FP` expression in a newly created fragment shader. `I` is a special variable of type `ivec2` that contains coordinates of the pixel being evaluated. The second (`target`) object must contain the `tag` parameter that is used to store the newly created render target in the `glsl` object for later use. We can easily visualize the resulting texture to make sure everything is ok: + ```js -glsl({F, FP:`F(I/20).x*3.0`}); +glsl({ F, FP: `F(I/20).x*3.0` }); ``` + ![](images/F.png) Uniform textures can be accessed with usual GLSL functions, or with a helper macro that has the same name as the texture uniform. Passing `ivec2` as parameter makes it call `texelFetch()` to get a texel using the integer coordinates, passing `vec2` uses `texture()`, with filtering and wrapping. The next step is to create a list of textures that is going to contain particle positions. Each pixel will contain a single particle position and type. + ```js -const points = glsl({}, {size:[30,10], story:3, format:'rgba32f', tag:'points'}); +const points = glsl({}, { size: [30, 10], story: 3, format: 'rgba32f', tag: 'points' }); ``` -We are going to simulate 30*10=300 particles. Textures will have 4 channels (RGBA) of type float32. The `story:3` argument says that we need to create a cyclic buffer of three textures of the same format, so that we can read two consecutive states of the particle system (for momentum) to produce the third. We don't provide any shader code in the first argument here, but we can initialize these textures later by passing the returned object as a `target`: + +We are going to simulate 30\*10=300 particles. Textures will have 4 channels (RGBA) of type float32. The `story:3` argument says that we need to create a cyclic buffer of three textures of the same format, so that we can read two consecutive states of the particle system (for momentum) to produce the third. We don't provide any shader code in the first argument here, but we can initialize these textures later by passing the returned object as a `target`: + ```js -for (let i=0; i<2; ++i) { - glsl({K, seed:123, FP:` +for (let i = 0; i < 2; ++i) { + glsl( + { + K, + seed: 123, + FP: ` vec2 pos = (hash(ivec3(I, seed)).xy-0.5)*10.0; float color = floor(UV.x*K); - FOut = vec4(pos, 0.0, color);`}, - points); + FOut = vec4(pos, 0.0, color);` + }, + points + ); } ``` + The shader code above uses "multiline" shader `code` format instead of a single expression. The output must be written to a global variable `FOut`. Variable `UV` has type `vec2` and provides `[0,1]`-range normalized coordinates of the current pixel. It is used to assign one of `K` "colors" to each particle. For convenience SwissGL provides a [simple hash](https://github.com/google/swissgl/blob/536c9f43c9f7a7bc59646d6fe1f3cb89bb5862b8/swissgl.js#L157) function `vec3 hash(ivec3)` that can be used as a deterministic random number generator. Note that we are writing the same particle positions two times, which means that particles have zero velocity at initialization. Now `points[0]` and `points[1]` contain the same values, and `points[2]` is uninitialized and is going to be overwritten at the first simulation step. Before we start modeling the particle dynamics it's a good idea to implement visualization. So far we've already seen "expression" and "multiline" shortcut `code` formats. Now we are going to write a `full` vertex-fragment shader pair: + ```js -glsl({K, worldExtent, // uniforms - // reading the last state of 'points' texture - points: points[0], - // render a quad instance for every 'points' texel - Grid: points[0].size, - // preserve the scale of xy-axes by fitting - // [-1..1]x[-1..1] box into the view - Aspect:'fit', - // blend primitives using alpha transparency - Blend: 'd*(1-sa)+s*sa', - // vertex shader that defines where to draw - // the quad primitives - VP:` +glsl({ + K, + worldExtent, // uniforms + // reading the last state of 'points' texture + points: points[0], + // render a quad instance for every 'points' texel + Grid: points[0].size, + // preserve the scale of xy-axes by fitting + // [-1..1]x[-1..1] box into the view + Aspect: 'fit', + // blend primitives using alpha transparency + Blend: 'd*(1-sa)+s*sa', + // vertex shader that defines where to draw + // the quad primitives + VP: ` // fetch the current particle data vec4 d = points(ID.xy); // populate color varying to use in fragment shader varying vec3 color = cos((d.w/K+vec3(0,0.33,0.66))*TAU)*0.5+0.5; // set the clip-space vertex position, 'vec2 XY' contains // coordinates of the quad vertex in -1..1 range - VPos.xy = 2.0*(d.xy+XY/8.0)/worldExtent;`, - // Set the the fragment color and transparency - // depending on the distance from the quad center. - // Interpolated XY values are also available - // in the fragment shader. - FP:`color, smoothstep(1.0, 0.6, length(XY))`}); - // 'target' argument is omitted, so rendering to canvas + VPos.xy = 2.0*(d.xy+XY/8.0)/worldExtent;`, + // Set the the fragment color and transparency + // depending on the distance from the quad center. + // Interpolated XY values are also available + // in the fragment shader. + FP: `color, smoothstep(1.0, 0.6, length(XY))` +}); +// 'target' argument is omitted, so rendering to canvas ``` Running this code in the drawing loop produces the following image: @@ -115,13 +136,21 @@ Running this code in the drawing loop produces the following image: The vertex shader computes WebGL [Clip Space](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_model_view_projection#clip_space) coordinates for each corner of each particle quad. We map particle positions from `[-worldExtent/2, worldExtent/2]` range to `[-1,1]` box. This shader also computes particle color using [cosine palettes trick](https://iquilezles.org/articles/palettes/) and passes it to the fragment shader along with the corner offset vector. The fragment shader calculates pixel opacity using the distance form the particle center. This way we can use low-level GLSL as an expressive, flexible and performant tool to render large numbers of primitives. Now we can set particles in motion by writing the update shader that computes new particle positions each frame. + ```js -glsl({F, worldExtent, repulsion, inertia, dt, // uniforms - // The current state of the system is implicitly - // available to the shader as 'Src' uniform if - // the target has history (is an array of textures). - // Here we explicitly pass the state one step at the past - past:points[1], FP:` +glsl( + { + F, + worldExtent, + repulsion, + inertia, + dt, // uniforms + // The current state of the system is implicitly + // available to the shader as 'Src' uniform if + // the target has history (is an array of textures). + // Here we explicitly pass the state one step at the past + past: points[1], + FP: ` // this function wraps positions and velocities to // [-worldExtent/2, worldExtent/2] range vec3 wrap(vec3 p) { @@ -152,7 +181,10 @@ void fragment() { // update particle position FOut.xyz = wrap(FOut.xyz+vel+0.5*force*(dt*dt)); } -`}, points); // using 'points' as the target +` + }, + points +); // using 'points' as the target ``` Soon randomly scattered particles self-assemble into a nice colorful snake! The simulation is happening on the GPU and is quite fast for the quadratic complexity algorithm (that iterates all particle pairs). Even mobile phones can run hundreds of steps per second. Thanks to SwissGL, orchestrating this computation, managing shaders and framebuffers takes minimal amount of boilerplate code. @@ -162,23 +194,26 @@ Soon randomly scattered particles self-assemble into a nice colorful snake! The ## Links Sources of wisdom: -* [Inigo Quilez](https://iquilezles.org/) -* [Steven Wittens](https://acko.net/) -* [WebGL](https://webglfundamentals.org/) / [WebGL2](https://webgl2fundamentals.org/) fundamentals + +- [Inigo Quilez](https://iquilezles.org/) +- [Steven Wittens](https://acko.net/) +- [WebGL](https://webglfundamentals.org/) / [WebGL2](https://webgl2fundamentals.org/) fundamentals Playgrounds: -* [ShaderToy](https://www.shadertoy.com/) -* [twigl](https://twigl.app/) -* [vertexshaderart](https://www.vertexshaderart.com/) + +- [ShaderToy](https://www.shadertoy.com/) +- [twigl](https://twigl.app/) +- [vertexshaderart](https://www.vertexshaderart.com/) Libraries -* [three.js](https://threejs.org/) -* [Use.GPU](https://usegpu.live/) -* [MathBox](https://github.com/unconed/mathbox) -* [twgljs](https://twgljs.org/) -* [regl](https://github.com/regl-project/regl) -* [gpu-io](https://github.com/amandaghassaei/gpu-io) -* [luma.gl](https://luma.gl/) -* [CindyJS](https://cindyjs.org/) -* [PicoGL.js](https://github.com/tsherif/picogl.js) -* [pex-context](https://github.com/pex-gl/pex-context) + +- [three.js](https://threejs.org/) +- [Use.GPU](https://usegpu.live/) +- [MathBox](https://github.com/unconed/mathbox) +- [twgljs](https://twgljs.org/) +- [regl](https://github.com/regl-project/regl) +- [gpu-io](https://github.com/amandaghassaei/gpu-io) +- [luma.gl](https://luma.gl/) +- [CindyJS](https://cindyjs.org/) +- [PicoGL.js](https://github.com/tsherif/picogl.js) +- [pex-context](https://github.com/pex-gl/pex-context) diff --git a/docs/API.md b/docs/API.md index 9d7e6fa..4f8b515 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,55 +1,58 @@ # SwissGL API `glsl` function has the following signature: + ```js glsl(params, target); ``` -Each call to `glsl` *typically* results in a single WebGL draw call of a number of instanced tessellated planes. -* `params` is a dictionary that specifies GLSL shader programs and uniforms. It may also contain a few other options (see below) that control the WebGL state, like blending or clearing the buffer before the draw call. SwissGL tries to automatically infer uniform types and introduce them to the shader code (it's also possible to override the types by declaring uniforms manually). Shader program is provided through `VP`, `FP` and `Inc` options. SwissGL will try to expand and compile these shaders, and cache the resulting shader program for future use in the `glsl.shaders` dictionary. Then the library will execute a WebGL draw call using this program. The type and the number of drawn primitives is controlled by the `Mesh` and `Grid` options. +Each call to `glsl` _typically_ results in a single WebGL draw call of a number of instanced tessellated planes. + +- `params` is a dictionary that specifies GLSL shader programs and uniforms. It may also contain a few other options (see below) that control the WebGL state, like blending or clearing the buffer before the draw call. SwissGL tries to automatically infer uniform types and introduce them to the shader code (it's also possible to override the types by declaring uniforms manually). Shader program is provided through `VP`, `FP` and `Inc` options. SwissGL will try to expand and compile these shaders, and cache the resulting shader program for future use in the `glsl.shaders` dictionary. Then the library will execute a WebGL draw call using this program. The type and the number of drawn primitives is controlled by the `Mesh` and `Grid` options. -* `target` determines the buffer where the rendering results are written. These are the following possibilities: - * `null` or `undefined` - render to canvas directly. - * `WebGLTexture` object - render to the texture. - * Array of `WebGLTexture`'s - render to the last texture in the array and cyclically shift the array in-place so that it becomes element `0`. This is useful for ping-pong buffers where element `0` corresponds to the state of the system at the current time step, element `1` to the previous step and so on. The original `0`-th texture is provided to the shader for reading as `Src` uniform for convenience. - * *Texture specification* dictionary. That's how we can use SwissGL to create new textures or even arrays of textures. Like shaders, textures are cached in the `glsl.buffers` dictionary. `tag` attribute of the specification is used as the key. +- `target` determines the buffer where the rendering results are written. These are the following possibilities: + - `null` or `undefined` - render to canvas directly. + - `WebGLTexture` object - render to the texture. + - Array of `WebGLTexture`'s - render to the last texture in the array and cyclically shift the array in-place so that it becomes element `0`. This is useful for ping-pong buffers where element `0` corresponds to the state of the system at the current time step, element `1` to the previous step and so on. The original `0`-th texture is provided to the shader for reading as `Src` uniform for convenience. + - _Texture specification_ dictionary. That's how we can use SwissGL to create new textures or even arrays of textures. Like shaders, textures are cached in the `glsl.buffers` dictionary. `tag` attribute of the specification is used as the key. `glsl` function returns a reference to the `target`. If the texture specification was given, the actual created texture(s) is returned. ## Options + In addition to uniforms, SwissGL accepts a number of options in the `params` argument. These options control the shader programs, WebGL state and the number of rendered primitives: -* `VP`: vertex shader string. +- `VP`: vertex shader string. -* `FP`: fragment shader string. +- `FP`: fragment shader string. -* `Inc`: string that is included in both vertex and fragment shaders. This string can be using to define varyings and helper functions. +- `Inc`: string that is included in both vertex and fragment shaders. This string can be using to define varyings and helper functions. -* `Clear`: scalar or `[r,g,b,a]` array. Clears the target buffer with a given color before rendering. Also clears the depth buffer if it's present. +- `Clear`: scalar or `[r,g,b,a]` array. Clears the target buffer with a given color before rendering. Also clears the depth buffer if it's present. -* `Blend`: string. Expression that controls WebGL blending mode (set with `gl.blendFunc` and `gl.blendEquation`). Inputs are: `s` - source color emitted by fragment shader; `d` - destination color already present in the target buffer; `sa` - source alpha; `da` - destination alpha. Examples: `s+d`, `d-s`, `d*(1-sa)+s*sa` (standard transparency), `d*(1-sa)+s` (premultiplied alpha), `max(s,d)`, `min(s,d)`, `d*s`. (TODO formal language definition) +- `Blend`: string. Expression that controls WebGL blending mode (set with `gl.blendFunc` and `gl.blendEquation`). Inputs are: `s` - source color emitted by fragment shader; `d` - destination color already present in the target buffer; `sa` - source alpha; `da` - destination alpha. Examples: `s+d`, `d-s`, `d*(1-sa)+s*sa` (standard transparency), `d*(1-sa)+s` (premultiplied alpha), `max(s,d)`, `min(s,d)`, `d*s`. (TODO formal language definition) -* `View`: array `[w, h]` or `[x, y, w, h]`. Controls WebGL viewport. By default, the viewport is set to cover the whole target. Value is available in shader as `uniform ivec4 View`. `ViewSize` macro also provides `ivec2` view size. +- `View`: array `[w, h]` or `[x, y, w, h]`. Controls WebGL viewport. By default, the viewport is set to cover the whole target. Value is available in shader as `uniform ivec4 View`. `ViewSize` macro also provides `ivec2` view size. -* `Aspect`: string (`fit`, `cover`, `mean`, `x`, `y`). Adjust `xy` coordinates emitted by vertex the program to preserve the scale of viewport axes. +- `Aspect`: string (`fit`, `cover`, `mean`, `x`, `y`). Adjust `xy` coordinates emitted by vertex the program to preserve the scale of viewport axes. -* `Grid`: `[w]`, `[w, h]` or `[w, h, d]`, default `[1,1,1]`. [instantiate](https://webglfundamentals.org/webgl/lessons/webgl-instanced-drawing.html) the rendered primitive `w*h*d` times. Instance ID is available in the vertex shader as `ivec3 ID`. Grid size is available in shader as `uniform ivec3 Grid`. +- `Grid`: `[w]`, `[w, h]` or `[w, h, d]`, default `[1,1,1]`. [instantiate](https://webglfundamentals.org/webgl/lessons/webgl-instanced-drawing.html) the rendered primitive `w*h*d` times. Instance ID is available in the vertex shader as `ivec3 ID`. Grid size is available in shader as `uniform ivec3 Grid`. -* `Mesh`: `[w, h]`, default `[1,1]`. Tessellate the rendered 2d plane primitive. `vec2 UV` and `XY` globals available in both vertex and fragment shaders provide `[0,1]`-range and `[-1,1]`-range vertex coordinates correspondingly. Integer vertex index is also provided as `ivec2 VID` global (VS only). Mesh size is available in the shader as `uniform ivec2 Mesh`. +- `Mesh`: `[w, h]`, default `[1,1]`. Tessellate the rendered 2d plane primitive. `vec2 UV` and `XY` globals available in both vertex and fragment shaders provide `[0,1]`-range and `[-1,1]`-range vertex coordinates correspondingly. Integer vertex index is also provided as `ivec2 VID` global (VS only). Mesh size is available in the shader as `uniform ivec2 Mesh`. -* `DepthTest`: `false`, `true` or `"keep"`, default is `false`. Enables depth testing. Passing `"keep"` sets read-only depth testing mode, when test is being performed, but the depth buffer is not updated with new depth values. +- `DepthTest`: `false`, `true` or `"keep"`, default is `false`. Enables depth testing. Passing `"keep"` sets read-only depth testing mode, when test is being performed, but the depth buffer is not updated with new depth values. -* `Face`: `'front'` or `'back'`. When provided, sets face culling to render corresponding faces only. +- `Face`: `'front'` or `'back'`. When provided, sets face culling to render corresponding faces only. -* `AlphaCoverage`: enable `gl.SAMPLE_ALPHA_TO_COVERAGE` if `true`. See [this article](https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f) for the usage example. +- `AlphaCoverage`: enable `gl.SAMPLE_ALPHA_TO_COVERAGE` if `true`. See [this article](https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f) for the usage example. ## Code formats -`VP` and `FP` options receive code snippets that define vertex and fragment WebGL pipeline stages. In case of *full* format the snippet must contain the function of the form `void vertex() {...; VPos=...}` (`VP` option) or `void fragment() {...; FOut=...}` (`FP` option). In simple cases shortcut syntax can be used: +`VP` and `FP` options receive code snippets that define vertex and fragment WebGL pipeline stages. In case of _full_ format the snippet must contain the function of the form `void vertex() {...; VPos=...}` (`VP` option) or `void fragment() {...; FOut=...}` (`FP` option). In simple cases shortcut syntax can be used: -* *expression*: a string that becomes the correct expression if it's substituted into the `vec4(${code})` template. The result of the expression is implicitly assigned to `VPos`/`FOut`. +- _expression_: a string that becomes the correct expression if it's substituted into the `vec4(${code})` template. The result of the expression is implicitly assigned to `VPos`/`FOut`. -* *multiline*: a function body that can be substituted into `void vertex() {...}` or `void fragment() {...}` function template. The output must be explictly written into to `VPos`/`FOut`. +- _multiline_: a function body that can be substituted into `void vertex() {...}` or `void fragment() {...}` function template. The output must be explictly written into to `VPos`/`FOut`. [MeshGrid](https://google.github.io/swissgl/#MeshGrid) demo provides a simple example of using the shortcut syntax and `XY`, `UV`, `ID`, `Mesh` and `Grid` input variables to render a few tessellated planes: @@ -67,35 +70,36 @@ In addition to uniforms, SwissGL accepts a number of options in the `params` arg The following options control the creation of new textures: -* `size`: `[w,h]` size of the created texture. Also affected by `scale` option. Set to the canvas size by default. Can be modified after the target creation. +- `size`: `[w,h]` size of the created texture. Also affected by `scale` option. Set to the canvas size by default. Can be modified after the target creation. -* `scale`: scalar. Coefficient applied to `size` before creating the texture. For example the following specification `{scale:1/4}` will create a target that is four times smaller than the canvas frame buffer in each dimension, and is automatically resized on canvas size changes. +- `scale`: scalar. Coefficient applied to `size` before creating the texture. For example the following specification `{scale:1/4}` will create a target that is four times smaller than the canvas frame buffer in each dimension, and is automatically resized on canvas size changes. -* `format`: string, `rgba8`, `r8`, `rgba16f`, `r16f`, `rgba32f`, `r32f` and `depth` are currently supported. Adding `+depth` suffix (e.g. `rgba16f+depth`) creates an additional depth texture stored in `.depth` attribute of the created target. +- `format`: string, `rgba8`, `r8`, `rgba16f`, `r16f`, `rgba32f`, `r32f` and `depth` are currently supported. Adding `+depth` suffix (e.g. `rgba16f+depth`) creates an additional depth texture stored in `.depth` attribute of the created target. -* `depth`: Texture target. Allows to attach a previously created depth buffer for the new target. +- `depth`: Texture target. Allows to attach a previously created depth buffer for the new target. -* `layern`: integer. Creates a Texture2DArray with the given number of layers. +- `layern`: integer. Creates a Texture2DArray with the given number of layers. -* `data`: `TypedArray` of size and type matching the texture specification. Allows to set the texture content from JS and update it after the texture creation to stream the new data to GPU. See [Spectrogram demo](../demo/Spectrogram.js) for the example. +- `data`: `TypedArray` of size and type matching the texture specification. Allows to set the texture content from JS and update it after the texture creation to stream the new data to GPU. See [Spectrogram demo](../demo/Spectrogram.js) for the example. -* `tag`: string that is used to cache the created texture in `glsl.buffers`. +- `tag`: string that is used to cache the created texture in `glsl.buffers`. -* `story`: integer. Create an array of textures of the same format instead of a single one. Rendering to such a target rotates the array in place so that the texture last rendered into becomes the element `0`. +- `story`: integer. Create an array of textures of the same format instead of a single one. Rendering to such a target rotates the array in place so that the texture last rendered into becomes the element `0`. -* `filter`: `'nearest'` or `'linear'` - -* `wrap`: `'repeat'`, `'edge'` or `'mirror'` +- `filter`: `'nearest'` or `'linear'` +- `wrap`: `'repeat'`, `'edge'` or `'mirror'` ## Hooks `glsl.hook(fn)` mechanism provides a simple way of extending and modifying SwissGL behavior. This function receives the callback function of the form: + ```js -(glsl, params, target)=>{ - // modify params of target and even make - // multiple calls to 'glsl' if needed - return glsl(params, target); -} +(glsl, params, target) => { + // modify params of target and even make + // multiple calls to 'glsl' if needed + return glsl(params, target); +}; ``` -`glsl.hook` returns a wrapped version of `glsl`, that calls the provided callback on each invocation. The callback may alter the provided `params` and `target` before passing them down the chain. Multiple hooks can be chained by calling `.hook()` of the returned wrapper object. SwissGL Demo application uses this mechanism to inject camera transform functions used by some examples, and even provide VR support by redirecting canvas rendering calls to two eye viewports. \ No newline at end of file + +`glsl.hook` returns a wrapped version of `glsl`, that calls the provided callback on each invocation. The callback may alter the provided `params` and `target` before passing them down the chain. Multiple hooks can be chained by calling `.hook()` of the returned wrapper object. SwissGL Demo application uses this mechanism to inject camera transform functions used by some examples, and even provide VR support by redirecting canvas rendering calls to two eye viewports. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 60e186c..9ee2933 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,92 +1,111 @@ # Changelog ### 2023-09-12 -* Async texture fetch support (see (FancyLenia)[https://google.github.io/swissgl/#FancyLenia] demo) + +- Async texture fetch support (see (FancyLenia)[https://google.github.io/swissgl/#FancyLenia] demo) ### 2023-09-07 -* Samplers support (see [TextureSamplers](https://google.github.io/swissgl/#TextureSamplers) demo) + +- Samplers support (see [TextureSamplers](https://google.github.io/swissgl/#TextureSamplers) demo) ### 2023-08-25 -* `varying`'s can be declared in-place in `VP` arguments -* `glsl.adjustCanvas()` should be explicitly called in `glsl.loop()` callback + +- `varying`'s can be declared in-place in `VP` arguments +- `glsl.adjustCanvas()` should be explicitly called in `glsl.loop()` callback ### 2023-08-11 -* `glsl.adjustCanvas()` helper -* `glsl.loop()` helper -* [`tiny.html`](https://google.github.io/swissgl/tiny.html) minimalistic example + +- `glsl.adjustCanvas()` helper +- `glsl.loop()` helper +- [`tiny.html`](https://google.github.io/swissgl/tiny.html) minimalistic example ### 2023-08-05 -* [Physarum3d](https://google.github.io/swissgl/#Physarum3d) demo +- [Physarum3d](https://google.github.io/swissgl/#Physarum3d) demo ### 2023-07-11 -* **(breaking)** Default texture filtering is now `nearest`. This is a workaround for iOS devices, where `texelFetch` stopped working for float32 textures when filtering is `linear`. + +- **(breaking)** Default texture filtering is now `nearest`. This is a workaround for iOS devices, where `texelFetch` stopped working for float32 textures when filtering is `linear`. ### 2023-06-19 -* **(breaking)** `VOut` -> `VPos` + +- **(breaking)** `VOut` -> `VPos` ### 2023-06-15 -* `readSync` method for fetching textures to CPU +- `readSync` method for fetching textures to CPU ### 2023-06-09 -* `[DotCamera](https://google.github.io/swissgl/#DotCamera) example +- `[DotCamera](https://google.github.io/swissgl/#DotCamera) example ### 2023-05-15 -* `[ReactionDiffusion](https://google.github.io/swissgl/#ReactionDiffusion) example + +- `[ReactionDiffusion](https://google.github.io/swissgl/#ReactionDiffusion) example ### 2023-05-12 -* `[Springs](https://google.github.io/swissgl/#Springs) example + +- `[Springs](https://google.github.io/swissgl/#Springs) example ### 2023-04-30 -* Array uniforms support (https://github.com/google/swissgl/issues/4), see [NeuralCA.js](https://github.com/google/swissgl/blob/main/demo/NeuralCA.js) for the usage example + +- Array uniforms support (https://github.com/google/swissgl/issues/4), see [NeuralCA.js](https://github.com/google/swissgl/blob/main/demo/NeuralCA.js) for the usage example ### 2023-04-28 -* Depth attachments (`depth` texture format and target parameter) -* Texture arrays (`layern` target parameter) -* [DeferredShading](https://google.github.io/swissgl/#DeferredShading) example +- Depth attachments (`depth` texture format and target parameter) +- Texture arrays (`layern` target parameter) +- [DeferredShading](https://google.github.io/swissgl/#DeferredShading) example ### 2023-03-19 -* **(breaking)** removed `code` argument. Shader is passed through `VP`, `FP` and `Inc` parameters. Shortcut syntax can be used in `VP` and `FP` independently. -* **(breaking)** `vertex()` function now returns `void`, output should be written into `vec4 VPos` variable. `fragment()` output now should be stored in `vec4 FOut`. -* **(breaking)** `tag` attribute it now obligatory for newly created render targets. + +- **(breaking)** removed `code` argument. Shader is passed through `VP`, `FP` and `Inc` parameters. Shortcut syntax can be used in `VP` and `FP` independently. +- **(breaking)** `vertex()` function now returns `void`, output should be written into `vec4 VPos` variable. `fragment()` output now should be stored in `vec4 FOut`. +- **(breaking)** `tag` attribute it now obligatory for newly created render targets. ### 2023-03-16 -* WebXR support in the demo + +- WebXR support in the demo ### 2023-03-13 -* [ParticleLife3d](https://google.github.io/swissgl/#ParticleLife3d) example -* replaced `includes` mechanism with hooks + +- [ParticleLife3d](https://google.github.io/swissgl/#ParticleLife3d) example +- replaced `includes` mechanism with hooks ### 2023-03-10 -* `VERT`/`FRAG` defines -* `torus()` glsl function -* `Face` option to control face culling -* [Shadowmap](https://google.github.io/swissgl/#Shadowmap) example + +- `VERT`/`FRAG` defines +- `torus()` glsl function +- `Face` option to control face culling +- [Shadowmap](https://google.github.io/swissgl/#Shadowmap) example ### 2023-03-08 -* `Grid` can be 3D ([ColorCube](https://google.github.io/swissgl/#ColorCube) example) + +- `Grid` can be 3D ([ColorCube](https://google.github.io/swissgl/#ColorCube) example) ### 2023-03-02 -* `depth` texture format -* removed `Perspective` option + +- `depth` texture format +- removed `Perspective` option ### 2023-03-01 -* Mesh rows alternate diagonal direction (see [MeshGrid](https://google.github.io/swissgl/#MeshGrid) example) -* [wireframe()](https://github.com/google/swissgl/blob/8cf8cac20c4ec3352fec639c8d22dc5814d5e674/swissgl.js#L201) helper + +- Mesh rows alternate diagonal direction (see [MeshGrid](https://google.github.io/swissgl/#MeshGrid) example) +- [wireframe()](https://github.com/google/swissgl/blob/8cf8cac20c4ec3352fec639c8d22dc5814d5e674/swissgl.js#L201) helper ### 2023-02-27 -* [CubeDeform](../demo/CubeDeform.js) example -* `SURF(f)` macro to estimate surface normal -* `cubeVert` function to simplify cube creation + +- [CubeDeform](../demo/CubeDeform.js) example +- `SURF(f)` macro to estimate surface normal +- `cubeVert` function to simplify cube creation ### 2023-02-25 -* (breaking) removed `uv` argument from `vertex()` -* (breaking) **removed** `P`, added `UV` and `XY` special variables -* (breaking) `float isoline(float v)` function (available in fragment shaders) -* [MeshGrid](../demo/MeshGrid.js) example + +- (breaking) removed `uv` argument from `vertex()` +- (breaking) **removed** `P`, added `UV` and `XY` special variables +- (breaking) `float isoline(float v)` function (available in fragment shaders) +- [MeshGrid](../demo/MeshGrid.js) example ### 2023-02-22 -* `'mirror'` (`gl.MIRRORED_REPEAT`) texture wrapping mode ([commit](https://github.com/google/swissgl/commit/d690e94fff35766b5a6358d96a4b7d6c59cff166)) \ No newline at end of file + +- `'mirror'` (`gl.MIRRORED_REPEAT`) texture wrapping mode ([commit](https://github.com/google/swissgl/commit/d690e94fff35766b5a6358d96a4b7d6c59cff166)) diff --git a/src/app.html b/src/app.html index d2fc6b0..f22aeaa 100644 --- a/src/app.html +++ b/src/app.html @@ -1,4 +1,4 @@ - + diff --git a/src/demos/BitField.js b/src/demos/BitField.js index 982e9cd..fe2ec89 100644 --- a/src/demos/BitField.js +++ b/src/demos/BitField.js @@ -1,18 +1,18 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ // Bit Field texture, inspired by the tweet thread: // https://twitter.com/aemkei/status/1378106731386040322 class BitField { - static Tags = ['2d']; - constructor(glsl, gui) { - this.k = 9; - gui.add(this, 'k', 2, 50, 1); - } - frame(glsl, {time}) { - const {k} = this; - glsl({t:time, k, FP:`1-((I.x+int(t*40.))/4^(I.y+int(t*20.))/4)%int(k)`}); - } + static Tags = ['2d']; + constructor(glsl, gui) { + this.k = 9; + gui.add(this, 'k', 2, 50, 1); + } + frame(glsl, { time }) { + const { k } = this; + glsl({ t: time, k, FP: `1-((I.x+int(t*40.))/4^(I.y+int(t*20.))/4)%int(k)` }); + } } diff --git a/src/demos/ColorCube.js b/src/demos/ColorCube.js index e5698f6..1eb04db 100644 --- a/src/demos/ColorCube.js +++ b/src/demos/ColorCube.js @@ -1,22 +1,30 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class ColorCube { - static Tags = ['3d']; + static Tags = ['3d']; - frame(glsl, params) { - glsl({...params, Grid:[10,10,10], Clear:[0.2, 0.2, 0.3, 1], - Aspect:'fit', DepthTest:1, AlphaCoverage:1, VP:` + frame(glsl, params) { + glsl({ + ...params, + Grid: [10, 10, 10], + Clear: [0.2, 0.2, 0.3, 1], + Aspect: 'fit', + DepthTest: 1, + AlphaCoverage: 1, + VP: ` vec3 p = color = vec3(ID)/vec3(Grid-1); varying vec3 color = p; vec4 pos = vec4(p-0.5, 1); pos = wld2view(pos); pos.xy += XY*0.03; // offset quad corners in view space - VPos = view2proj(pos);`, FP:` + VPos = view2proj(pos);`, + FP: ` float r = length(XY); float alpha = smoothstep(1.0, 1.0-fwidth(r), r); - FOut = vec4(color, alpha);`}); - } + FOut = vec4(color, alpha);` + }); + } } diff --git a/src/demos/CubeDeform.js b/src/demos/CubeDeform.js index a9a2059..2c272a1 100644 --- a/src/demos/CubeDeform.js +++ b/src/demos/CubeDeform.js @@ -1,14 +1,19 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class CubeDeform { - static Tags = ['3d']; + static Tags = ['3d']; - frame(glsl, params) { - glsl({...params, Grid:[6,1], Mesh:[20, 20], - Aspect:'fit', DepthTest:1, VP:` + frame(glsl, params) { + glsl({ + ...params, + Grid: [6, 1], + Mesh: [20, 20], + Aspect: 'fit', + DepthTest: 1, + VP: ` vec3 surface_f(vec2 xy) { vec3 pos = cubeVert(xy, ID.x); pos += sin(pos*PI+time).zxy*0.2; @@ -22,7 +27,8 @@ class CubeDeform { vec4 v = vec4(SURF(surface_f, XY, normal, 1e-3), 1.0); varying vec3 eyeDir = cameraPos()-v.xyz; VPos = wld2proj(v); - }`, FP:` + }`, + FP: ` vec3 n = normalize(normal); vec3 lightDir = normalize(vec3(0,1,1)); float diffuse = dot(n, lightDir)*0.5+0.5; @@ -30,6 +36,7 @@ class CubeDeform { float spec = smoothstep(0.998, 0.999, dot(halfVec, n)); FOut.rgb = color*diffuse + 0.3*spec; vec2 m = UV*4.0; - FOut.rgb = mix(FOut.rgb, vec3(1.0), (isoline(m.x)+isoline(m.y))*0.25);`}); - } + FOut.rgb = mix(FOut.rgb, vec3(1.0), (isoline(m.x)+isoline(m.y))*0.25);` + }); + } } diff --git a/src/demos/DeferredShading.js b/src/demos/DeferredShading.js index d0e6e6e..d111063 100644 --- a/src/demos/DeferredShading.js +++ b/src/demos/DeferredShading.js @@ -1,13 +1,20 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class DeferredShading { - frame(glsl, params) { - // draw objects - const gbuf = glsl({...params, Mesh:[64,128], Grid:[4,4,3], - Aspect:'fit', DepthTest:1, Clear:0, VP:` + frame(glsl, params) { + // draw objects + const gbuf = glsl( + { + ...params, + Mesh: [64, 128], + Grid: [4, 4, 3], + Aspect: 'fit', + DepthTest: 1, + Clear: 0, + VP: ` vec3 surface_f(vec2 p) { vec2 c = sin(time+p*vec2(ID)*TAU); vec3 pos = torus(p, 1.0, 0.4 + 0.1*c.x + 0.15*c.y)/8.0; @@ -18,26 +25,43 @@ class DeferredShading { void vertex() { varying vec3 normal, wldPos = SURF(surface_f, UV, normal, 1e-3); VPos = wld2proj(wldPos); - }`, FP:` + }`, + FP: ` FOut = vec4(0.7, 0.7, 0.7, 1.0); vec2 m = UV*vec2(Mesh)/4.0; FOut.rgb += (isoline(m.x)+isoline(m.y))*0.2; // color FOut1.xyz = normalize(normal); // normal FOut2 = vec4(wldPos, 1.0); // wldPos - `}, {format:'rgba16f+depth', tag:'gbuf', layern:3}); + ` + }, + { format: 'rgba16f+depth', tag: 'gbuf', layern: 3 } + ); - // common light passes code - const lightArgs = {...params, Face:'front', Grid:[9, 9, 1], - Aspect:'fit', Blend:'s+d', DepthTest:'keep', VP:` + // common light passes code + const lightArgs = { + ...params, + Face: 'front', + Grid: [9, 9, 1], + Aspect: 'fit', + Blend: 's+d', + DepthTest: 'keep', + VP: ` varying vec3 lightPos = vec3(ID-Grid/2)*0.2; lightPos.xy *= rot2(time*0.1+lightPos.z)*(0.5+sin(time*0.7)*0.4); lightPos.z = sin((lightPos.x+lightPos.y)*1.5+time*0.25)*0.75; varying vec3 lightColor = hash(ID+1)*5.0; - VPos = wld2proj(lightPos+uv2sphere(UV)*lightR);`}; + VPos = wld2proj(lightPos+uv2sphere(UV)*lightR);` + }; - // accumulate surface lights - const light = glsl({...lightArgs, lightR:0.3, - Mesh:[32,64], Clear:0, gbuf, FP:` + // accumulate surface lights + const light = glsl( + { + ...lightArgs, + lightR: 0.3, + Mesh: [32, 64], + Clear: 0, + gbuf, + FP: ` vec4 wldPos = gbuf(I,2); if (wldPos.w==0.0) discard; vec3 lightDir = lightPos-wldPos.xyz; @@ -48,12 +72,14 @@ class DeferredShading { float diff = max(dot(normal, lightDir/r), 0.0); float att = 1.5*smoothstep(1.0,0.9,r/lightR) / (1.0+r*r*2e4); FOut = vec4(color.rgb*lightColor*att*diff, 1.0);` - }, {format:'rgba16f', tag:'light', depth:gbuf.depth}); - - // add light source points - glsl({...lightArgs, lightR:0.005, Mesh:[16,8], FP:`lightColor*0.3,1`}, light); + }, + { format: 'rgba16f', tag: 'light', depth: gbuf.depth } + ); - // render to screen - glsl({light, FP:`sqrt(light(UV))`}) - } + // add light source points + glsl({ ...lightArgs, lightR: 0.005, Mesh: [16, 8], FP: `lightColor*0.3,1` }, light); + + // render to screen + glsl({ light, FP: `sqrt(light(UV))` }); + } } diff --git a/src/demos/DotCamera.js b/src/demos/DotCamera.js index 3a0f05b..a635647 100644 --- a/src/demos/DotCamera.js +++ b/src/demos/DotCamera.js @@ -1,53 +1,86 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class DotCamera { - constructor(glsl, gui) { - this.video = document.createElement('video'); - this.dayMode = false; gui.add(this, 'dayMode'); - this.rgbMode = false; gui.add(this, 'rgbMode'); - navigator.mediaDevices.getUserMedia({ video: true }) - .then((stream) => { - this.video.srcObject = stream; - this.video.play(); - }).catch((error) => { - console.log('Error accessing camera:', error); - }); - } + constructor(glsl, gui) { + this.video = document.createElement('video'); + this.dayMode = false; + gui.add(this, 'dayMode'); + this.rgbMode = false; + gui.add(this, 'rgbMode'); + navigator.mediaDevices + .getUserMedia({ video: true }) + .then((stream) => { + this.video.srcObject = stream; + this.video.play(); + }) + .catch((error) => { + console.log('Error accessing camera:', error); + }); + } - frame(glsl, {time, canvasSize}) { - let tex; - if (this.video.videoWidth) { - tex = glsl({}, {data:this.video, tag:'video'}); - } else { - tex = glsl({time, FP:`step(0.0, sin(length(XY)*20.0-time*3.0+atan(XY.x,XY.y)*3.))*0.25`}, - {size:[512, 512], tag:'tmp'}); - } - const blendParams = this.dayMode ? {Clear:1, Blend:'d-s'} : {Clear:0, Blend:'d+s'}; - const rgbMode = this.rgbMode; - const lum = glsl({tex:tex.edge.linear, ...blendParams, rgbMode, - VP:`vec2 r = vec2(ViewSize)/vec2(tex_size()); r /= max(r.x, r.y); VPos.xy = XY/r;`, FP:` + frame(glsl, { time, canvasSize }) { + let tex; + if (this.video.videoWidth) { + tex = glsl({}, { data: this.video, tag: 'video' }); + } else { + tex = glsl( + { time, FP: `step(0.0, sin(length(XY)*20.0-time*3.0+atan(XY.x,XY.y)*3.))*0.25` }, + { size: [512, 512], tag: 'tmp' } + ); + } + const blendParams = this.dayMode ? { Clear: 1, Blend: 'd-s' } : { Clear: 0, Blend: 'd+s' }; + const rgbMode = this.rgbMode; + const lum = glsl( + { + tex: tex.edge.linear, + ...blendParams, + rgbMode, + VP: `vec2 r = vec2(ViewSize)/vec2(tex_size()); r /= max(r.x, r.y); VPos.xy = XY/r;`, + FP: ` FOut = tex(1.0-UV); if (!rgbMode) { FOut.r = dot(FOut.rgb, vec3(0.21,0.72,0.07)); - }`}, - {scale:1/2, tag:'lum'}); - const merged = glsl({T:lum.edge.miplinear, FP:` + }` + }, + { scale: 1 / 2, tag: 'lum' } + ); + const merged = glsl( + { + T: lum.edge.miplinear, + FP: ` for (float lod=0.; lod<8.0; lod+=1.0) {FOut += textureLod(T, UV, lod);} - FOut /= 8.0;`}, {size:lum.size, format:'rgba16f', tag:'merged'}); - const imgForce = glsl({T:merged.edge, FP:` + FOut /= 8.0;` + }, + { size: lum.size, format: 'rgba16f', tag: 'merged' } + ); + const imgForce = glsl( + { + T: merged.edge, + FP: ` vec2 s=T_step(); vec4 a=T(UV-s), b=T(UV+vec2(s.x,-s.y)), c=T(UV+vec2(-s.x,s.y)), d=T(UV+s); FOut = b+d-a-c; FOut1 = c+d-a-b;` - }, {size:lum.size, layern:2, format:'rgba16f', tag:'grad'}); + }, + { size: lum.size, layern: 2, format: 'rgba16f', tag: 'grad' } + ); - const arg = {canvasSize, rgbMode}; - const field = glsl({}, {scale:1/4, format:'rgba16f', layern:3, filter:'linear', tag:'field'}); - let points; - for (let i=0; i<10; ++i) { - points = glsl({...arg, field:field.edge, imgForce:imgForce.edge.linear, seed: Math.random()*124237, FP: ` + const arg = { canvasSize, rgbMode }; + const field = glsl( + {}, + { scale: 1 / 4, format: 'rgba16f', layern: 3, filter: 'linear', tag: 'field' } + ); + let points; + for (let i = 0; i < 10; ++i) { + points = glsl( + { + ...arg, + field: field.edge, + imgForce: imgForce.edge.linear, + seed: Math.random() * 124237, + FP: ` int c = rgbMode ? I.x%3 : 0; vec4 p=Src(I), f=field(p.xy, c); if (p.w == 0.0) { @@ -59,19 +92,39 @@ class DotCamera { vec2 force = f.xy*10.0 + imf.xy*20.0; p.xy = clamp(p.xy + force/canvasSize, vec2(0), vec2(1)); FOut = p; - `}, {scale:(rgbMode?1.7:1)/8, story:2, format:'rgba32f', tag:'points'}); - glsl({...arg, points:points[0], Grid: points[0].size, Blend:'s+d', Clear:0, VP:` + ` + }, + { scale: (rgbMode ? 1.7 : 1) / 8, story: 2, format: 'rgba32f', tag: 'points' } + ); + glsl( + { + ...arg, + points: points[0], + Grid: points[0].size, + Blend: 's+d', + Clear: 0, + VP: ` VPos.xy = (points(ID.xy).xy + XY*15.0/canvasSize)*2.0-1.0; int c = rgbMode ? ID.x%3 : 0; - varying vec3 color = vec3(c==0,c==1,c==2);`,FP:` + varying vec3 color = vec3(c==0,c==1,c==2);`, + FP: ` vec4 v = vec4(vec3(XY,1.)*exp(-dot(XY,XY)*vec3(4,4,8)), 0); - FOut=v*color.r; FOut1=v*color.g; FOut2=v*color.b;`}, field) - } - // draw dots on screen - glsl({...arg, points:points[0], Grid: points[0].size, ...blendParams, VP:` + FOut=v*color.r; FOut1=v*color.g; FOut2=v*color.b;` + }, + field + ); + } + // draw dots on screen + glsl({ + ...arg, + points: points[0], + Grid: points[0].size, + ...blendParams, + VP: ` VPos.xy = (points(ID.xy).xy + XY*4.0/canvasSize)*2.0-1.0; int c = ID.x%3; varying vec3 color = rgbMode ? vec3(c==0,c==1,c==2) : vec3(1);`, - FP:`color*exp(-dot(XY,XY)*3.0),1`}) - } + FP: `color*exp(-dot(XY,XY)*3.0),1` + }); + } } diff --git a/src/demos/FancyLenia.js b/src/demos/FancyLenia.js index ebd31b5..060cdea 100644 --- a/src/demos/FancyLenia.js +++ b/src/demos/FancyLenia.js @@ -1,51 +1,69 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ // Visualization of Particle Lenia fields as 3d landscape class FancyLenia extends ParticleLenia { - static Tags = ['3d', 'simulation', 'audio']; + static Tags = ['3d', 'simulation', 'audio']; - constructor(glsl, gui) { - super(glsl, gui); - this.meanEnergy = 0.0; - gui.add(this, 'meanEnergy', 0.0, 1.0, 0.001).listen(); - this.volume = 0.5; - gui.add(this, 'volume', 0.0, 1.0); - gui.add(this, 'toggleAudio'); - } - reset() { - super.reset(); - this.trails = this.glsl({Clear:0}, {size:[1024, 1024], format:'r8', filter:'linear', tag:'trails'}); - } + constructor(glsl, gui) { + super(glsl, gui); + this.meanEnergy = 0.0; + gui.add(this, 'meanEnergy', 0.0, 1.0, 0.001).listen(); + this.volume = 0.5; + gui.add(this, 'volume', 0.0, 1.0); + gui.add(this, 'toggleAudio'); + } + reset() { + super.reset(); + this.trails = this.glsl( + { Clear: 0 }, + { size: [1024, 1024], format: 'r8', filter: 'linear', tag: 'trails' } + ); + } - step() { - super.step(); - const {glsl, trails} = this; - glsl({Blend:'d-s', FP:`2./255.`}, trails); - this.renderSpots(trails, 0.2); - } + step() { + super.step(); + const { glsl, trails } = this; + glsl({ Blend: 'd-s', FP: `2./255.` }, trails); + this.renderSpots(trails, 0.2); + } - frame(_, cameraParams) { - for (let i=0; i0.1) { vec2 m = 20.0*mat2(1,0.5,0,sin(TAU/6.))*UV; @@ -56,61 +74,97 @@ class FancyLenia extends ParticleLenia { } float G = peak_f(U, mu_g, sigma_g).x; FOut.rgb = mix(FOut.rgb, vec3(0.6, 0.8, 0.3), G); - FOut = mix(FOut, vec4(1), trails(UV).x);`}); + FOut = mix(FOut, vec4(1), trails(UV).x);` + }); - glsl({state:state[0], Grid:state[0].size, Mesh: [32,8], fieldU, ...viewParams, VP:` + glsl({ + state: state[0], + Grid: state[0].size, + Mesh: [32, 8], + fieldU, + ...viewParams, + VP: ` vec4 pos = vec4(state(ID.xy).xy, 0.0, 1.0); pos.xy /= viewR; pos.z = fieldU(pos.xy*0.5+0.5).x*scaleU-0.25; varying vec3 normal = uv2sphere(UV); pos.xyz += normal*0.015; - VPos = wld2proj(pos);`, FP:` + VPos = wld2proj(pos);`, + FP: ` float a = normal.z*0.7+0.3; - FOut = vec4(vec3(1.0-a*a*0.75), 1.0);`}); + FOut = vec4(vec3(1.0-a*a*0.75), 1.0);` + }); - glsl({state:state[0], FP:` + glsl( + { + state: state[0], + FP: ` ivec2 sz = state_size(); float E = 0.0; for (int y=0; ythis.meanEnergy=d[0]); + FOut.x = E / float(sz.x*sz.y);` + }, + { size: [1, 1], format: 'r32f', tag: 'meanE' } + ).read((d) => (this.meanEnergy = d[0])); - if (this.audio) { - glsl({T:this.audio, Grid:this.audio.size, Blend:'s+d', VP:` + if (this.audio) { + glsl({ + T: this.audio, + Grid: this.audio.size, + Blend: 's+d', + VP: ` vec2 p = vec2(float(ID.x)/float(Grid.x-1)*2.-1., T(ID.xy).x*2.+0.5); p = (ViewSize.x>ViewSize.y) ? p : p.yx; VPos.xy = p+XY*0.008; - `, FP:'vec4(0.8,0.1,0.7,0)*exp(-dot(XY,XY)*3.0)'}); - } - } + `, + FP: 'vec4(0.8,0.1,0.7,0)*exp(-dot(XY,XY)*3.0)' + }); + } + } - toggleAudio() { - if (!this.audioStream) { - this.audioStream = new AudioStream(); - this.audioStream.start((...a)=>this.audioFrame(...a)); - } else { - this.audioStream.stop(); - delete this.audioStream - delete this.audio; - } - } - free() { - if (this.audioStream) {this.audioStream.stop();} - } - audioFrame(e, submit) { - const n = e.buf.length/2; - const dt = n/e.sampleRate; - const [s1, s0] = this.state; - this.phase_vol = this.glsl({s0, s1, dt, FP:` + toggleAudio() { + if (!this.audioStream) { + this.audioStream = new AudioStream(); + this.audioStream.start((...a) => this.audioFrame(...a)); + } else { + this.audioStream.stop(); + delete this.audioStream; + delete this.audio; + } + } + free() { + if (this.audioStream) { + this.audioStream.stop(); + } + } + audioFrame(e, submit) { + const n = e.buf.length / 2; + const dt = n / e.sampleRate; + const [s1, s0] = this.state; + this.phase_vol = this.glsl( + { + s0, + s1, + dt, + FP: ` vec4 d0=s0(I), d1=s1(I); FOut.x = fract(Src(I).x + dt*exp2(4.0*d1.w)); FOut.y = length(d1.xyz-d0.xyz); - `}, {size:s0.size, story:2, format:'rg32f', tag:'phase_vol'}); - const [p1, p0] = this.phase_vol; - this.audio = this.glsl({dt, p0, p1, volume:this.volume, FP:` + ` + }, + { size: s0.size, story: 2, format: 'rg32f', tag: 'phase_vol' } + ); + const [p1, p0] = this.phase_vol; + this.audio = this.glsl( + { + dt, + p0, + p1, + volume: this.volume, + FP: ` ivec2 i, sz = p0_size(); float acc = 0.0; for (i.y=0; i.y1.5 && (FOut.x+nhood) > 2.5); FOut = vec4(v,0,0,1); - `}, {scale:1/4, story:2, tag:'state'}); - const fade = glsl({S:state[0], Blend:'d*sa+s', FP:`S(I).xxx,0.9`}, - {size:state[0].size, tag:'fade'}) - glsl({fade, FP:`fade(UV).x`}); - } + ` + }, + { scale: 1 / 4, story: 2, tag: 'state' } + ); + const fade = glsl( + { S: state[0], Blend: 'd*sa+s', FP: `S(I).xxx,0.9` }, + { size: state[0].size, tag: 'fade' } + ); + glsl({ fade, FP: `fade(UV).x` }); + } } diff --git a/src/demos/MeshGrid.js b/src/demos/MeshGrid.js index 2efa25a..28c97ec 100644 --- a/src/demos/MeshGrid.js +++ b/src/demos/MeshGrid.js @@ -1,16 +1,22 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class MeshGrid { - static Tags = ['2d']; - frame(glsl, {time}) { - glsl({time, Grid:[5,5], Mesh:[4,4], Aspect:'fit', VP:` + static Tags = ['2d']; + frame(glsl, { time }) { + glsl({ + time, + Grid: [5, 5], + Mesh: [4, 4], + Aspect: 'fit', + VP: ` varying vec3 color = hash(ID); vec2 pos = vec2(ID) + 0.5 + XY*(0.5-0.5/vec2(Mesh+1)); pos += sin(UV*TAU+time).yx*0.1*(sin(time*0.5)); VPos = vec4(2.0*pos/vec2(Grid)-1.0, 0.0, 1.0);`, - FP:`mix(color, vec3(1.0), wireframe()*0.5),1`}); - } + FP: `mix(color, vec3(1.0), wireframe()*0.5),1` + }); + } } diff --git a/src/demos/NeuralCA.js b/src/demos/NeuralCA.js index 6bc4f89..36165e5 100644 --- a/src/demos/NeuralCA.js +++ b/src/demos/NeuralCA.js @@ -1,20 +1,26 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ // Based on "μNCA: Texture Generation with Ultra-Compact Neural Cellular Automata" // https://arxiv.org/abs/2111.13545 class NeuralCA { - static Tags = ['2d', 'ca']; - constructor() { - this.W = new Float32Array([ - -67,1,2,44,-13,-59,4,30,-1,16,-57,9,-10,-4,-2,-41, 19,-18,-1,8,-4,35,8,0,-4,-4,-1,0,34,31,21,-25, - 4,13,18,-57,-79,-22,-25,71,-12,-11,24,27,-17,-8,-7,6, 11,10,4,0,4,1,2,7,-26,-33,-15,-3,22,27,20,-34]); - this.b = new Float32Array([2,-5,-14,9]); - } - frame(glsl) { - const state = glsl({W:this.W, b:this.b, FP:` + static Tags = ['2d', 'ca']; + constructor() { + this.W = new Float32Array([ + -67, 1, 2, 44, -13, -59, 4, 30, -1, 16, -57, 9, -10, -4, -2, -41, 19, -18, -1, 8, -4, 35, 8, + 0, -4, -4, -1, 0, 34, 31, 21, -25, 4, 13, 18, -57, -79, -22, -25, 71, -12, -11, 24, 27, -17, + -8, -7, 6, 11, 10, 4, 0, 4, 1, 2, 7, -26, -33, -15, -3, 22, 27, 20, -34 + ]); + this.b = new Float32Array([2, -5, -14, 9]); + } + frame(glsl) { + const state = glsl( + { + W: this.W, + b: this.b, + FP: ` uniform mat4 W[4]; uniform vec4 b; vec4 rule(vec4 s, vec4 p) { @@ -37,7 +43,10 @@ class NeuralCA { + R(l,d)*vec4(1,1,-1,-1) + R(x,d)*vec4(2,2,0,-2) + R(r,d)*vec4(1,1,1,-1); vec4 ds = rule(s-0.5, p); // NCA rule application FOut = s+ds; - }`}, {story:2, scale:1/4, tag:'state'}); - glsl({tex:state[0], FP:`tex(UV)*2.-.5`}); - } + }` + }, + { story: 2, scale: 1 / 4, tag: 'state' } + ); + glsl({ tex: state[0], FP: `tex(UV)*2.-.5` }); + } } diff --git a/src/demos/ParticleLenia.js b/src/demos/ParticleLenia.js index a620094..854610c 100644 --- a/src/demos/ParticleLenia.js +++ b/src/demos/ParticleLenia.js @@ -1,59 +1,80 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ // Model description: // https://google-research.github.io/self-organising-systems/particle-lenia/ // https://observablehq.com/@znah/particle-lenia-from-scratch class ParticleLenia { - static Tags = ['2d', 'simulation']; + static Tags = ['2d', 'simulation']; - constructor(glsl, gui) { - this.glsl = glsl.hook((glsl, p, t)=>glsl({...p, Inc:` + constructor(glsl, gui) { + this.glsl = glsl.hook((glsl, p, t) => + glsl( + { + ...p, + Inc: + ` vec2 peak_f(float x, float mu, float sigma) { float t = (x-mu)/sigma; float y = exp(-t*t); return vec2(y, -2.0*t*y/sigma); - }\n`+(p.Inc||'')}, t)); - this.step_n = 5; - this.viewR = 15.0; - const params = this.params = {dt: 0.1, - mu_k: 4.0, sigma_k: 1.0, w_k: 0.022, - mu_g: 0.6, sigma_g: 0.15, c_rep: 1.0}; - gui.add(this, 'step_n', 0, 50, 1); - gui.add(params, 'mu_k', 0.0, 5.0).onChange(()=>this.updateNormCoef()); - gui.add(params, 'sigma_k', 0.1, 2.0).onChange(()=>this.updateNormCoef()); - gui.add(params, 'mu_g', 0.0, 1.5); - gui.add(params, 'sigma_g', 0.1, 1.0); - gui.add(params, 'c_rep', 0.0, 2.0); - gui.add(this, 'reset'); + }\n` + (p.Inc || '') + }, + t + ) + ); + this.step_n = 5; + this.viewR = 15.0; + const params = (this.params = { + dt: 0.1, + mu_k: 4.0, + sigma_k: 1.0, + w_k: 0.022, + mu_g: 0.6, + sigma_g: 0.15, + c_rep: 1.0 + }); + gui.add(this, 'step_n', 0, 50, 1); + gui.add(params, 'mu_k', 0.0, 5.0).onChange(() => this.updateNormCoef()); + gui.add(params, 'sigma_k', 0.1, 2.0).onChange(() => this.updateNormCoef()); + gui.add(params, 'mu_g', 0.0, 1.5); + gui.add(params, 'sigma_g', 0.1, 1.0); + gui.add(params, 'c_rep', 0.0, 2.0); + gui.add(this, 'reset'); - this.reset(); - } + this.reset(); + } - updateNormCoef() { - const {mu_k, sigma_k} = this.params; - const mu = mu_k*sigma_k; - const dr = 0.1*sigma_k, R=sigma_k*3.0; - let acc = 0.0, prev=null; - for (let r = Math.max(mu_k-R, 0.0); r1.0) discard; - emitFragment(color);`}, target); - } + emitFragment(color);` + }, + target + ); + } - frame(_, params) { - this.step(); + frame(_, params) { + this.step(); - const glsl = this.shadowmap.glsl; - const shadowmap = glsl({Clear:0}, {size:[2048, 2048], format:'depth', tag:'shadowmap'}); - this.drawPoints(glsl, {...params, shadowPass:true}, shadowmap); - params = {...params, shadowmap, shadowPass:false, Aspect:'fit'}; - this.drawPoints(glsl, params); + const glsl = this.shadowmap.glsl; + const shadowmap = glsl({ Clear: 0 }, { size: [2048, 2048], format: 'depth', tag: 'shadowmap' }); + this.drawPoints(glsl, { ...params, shadowPass: true }, shadowmap); + params = { ...params, shadowmap, shadowPass: false, Aspect: 'fit' }; + this.drawPoints(glsl, params); - const { points, worldExtent } = this; - const [sx, sy] = points[0].size - const portalmap = glsl({worldExtent, points: points[0], Grid: [sx, sy, 3], - Clear:0, Blend:'max(s,d)', portalR:0.1, VP:` + const { points, worldExtent } = this; + const [sx, sy] = points[0].size; + const portalmap = glsl( + { + worldExtent, + points: points[0], + Grid: [sx, sy, 3], + Clear: 0, + Blend: 'max(s,d)', + portalR: 0.1, + VP: ` vec3 p = 2.0*points(ID.xy).xyz/worldExtent; vec3 proj = ID.z==0 ? p.xyz : (ID.z==1 ? p.yzx : p.zxy); vec2 v = clamp(proj.xy+XY*portalR,-1.0, 1.0); varying vec3 dp = vec3(v-proj.xy, 1.0-abs(proj.z))/portalR; - VPos = vec4((v.x*0.5+0.5+float(ID.z))/3.0*2.0-1.0, v.y, 0,1);`, FP:` - 1.0-length(dp)`}, {size:[256*3, 256], filter:'linear', tag:'portalmap'}); + VPos = vec4((v.x*0.5+0.5+float(ID.z))/3.0*2.0-1.0, v.y, 0,1);`, + FP: ` + 1.0-length(dp)` + }, + { size: [256 * 3, 256], filter: 'linear', tag: 'portalmap' } + ); - for (const face of ['back', 'front']) - glsl({...params, portalmap, Grid:[6,1], Blend:'d*(1-sa)+s', - portalColor: face=='back'?[0.5, 1.0, 1.5]:[1.5, 1.0, 0.5], - Face:face, DepthTest:face=='front'?'keep':1, VP:` + for (const face of ['back', 'front']) + glsl({ + ...params, + portalmap, + Grid: [6, 1], + Blend: 'd*(1-sa)+s', + portalColor: face == 'back' ? [0.5, 1.0, 1.5] : [1.5, 1.0, 0.5], + Face: face, + DepthTest: face == 'front' ? 'keep' : 1, + VP: ` vec3 p = cubeVert(XY, ID.x)*0.5+0.5; Normal = -cubeVert(vec2(0), ID.x); varying vec3 portalPos = vec3( Normal.z!=0. ? p.xy : (Normal.x!=0. ? p.yz : p.zx), abs(Normal.x)+abs(Normal.y)*2.0); - emitVertex((p-0.5)*1.3);`, FP:` + emitVertex((p-0.5)*1.3);`, + FP: ` if (!gl_FrontFacing) { vec2 c = XY; c*=c; c*=c; float ao = 1.0-(c.x+c.y)*0.4; @@ -79,6 +115,7 @@ class ParticleLife3d extends ParticleLife { vec2 dp=portalmap_step()*0.5, p=clamp(portalPos.xy, dp,1.0-dp); p = (p+vec2(portalPos.z,0)) / vec2(3,1); float portal = portalmap(p).r; - FOut = mix(FOut, vec4(portalColor, 1.0), portal);`}); - } -} \ No newline at end of file + FOut = mix(FOut, vec4(portalColor, 1.0), portal);` + }); + } +} diff --git a/src/demos/Physarum.js b/src/demos/Physarum.js index 5bf6505..ebb2f2e 100644 --- a/src/demos/Physarum.js +++ b/src/demos/Physarum.js @@ -1,68 +1,90 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ // Inspired by https://cargocollective.com/sagejenson/physarum class Physarum { - static Tags = ['2d', 'simulation']; + static Tags = ['2d', 'simulation']; - constructor(glsl, gui) { - const U = this.U = {follow:false}; - const controllers = []; - const par = (s, v, ...arg)=>{U[s]||=v;controllers.push(gui.add(U,s,...arg))}; - const presets = { - worms: {senseFlip: false, senseAng:15, senseDist:10,moveAng:7,moveDist:1}, - mesh:{senseFlip:false, senseAng:45, senseDist:15, moveAng:17,moveDist:1}, - flocks: {senseFlip: true, senseAng:40, senseDist:36,moveAng:7,moveDist:1}, - swirl:{senseFlip: true, senseAng:1.28, senseDist: 19, moveAng: 7, moveDist: 2}, - }; - this.preset = 'worms'; - gui.add(this, 'preset', Object.keys(presets)).onChange(name=>{ - updateObject(U, presets[name]); - controllers.forEach(c=>c.updateDisplay()); - }); - par('step_n', 1, 0, 20, 1); - par('dt', 1, 0.3, 1.0); - par('density', 1, 1, 4, 1); - par('senseFlip', false); - par('senseAng', 40, 0, 180 ); - par('senseDist', 36, 1, 50 ); - par('moveAng', 7, 0, 180 ); - par('moveDist', 1, 0, 10 ); - par('zoom', 1, 1, 8); - gui.add(U,'follow'); - updateObject(U, presets[this.preset]); - } + constructor(glsl, gui) { + const U = (this.U = { follow: false }); + const controllers = []; + const par = (s, v, ...arg) => { + U[s] ||= v; + controllers.push(gui.add(U, s, ...arg)); + }; + const presets = { + worms: { senseFlip: false, senseAng: 15, senseDist: 10, moveAng: 7, moveDist: 1 }, + mesh: { senseFlip: false, senseAng: 45, senseDist: 15, moveAng: 17, moveDist: 1 }, + flocks: { senseFlip: true, senseAng: 40, senseDist: 36, moveAng: 7, moveDist: 1 }, + swirl: { senseFlip: true, senseAng: 1.28, senseDist: 19, moveAng: 7, moveDist: 2 } + }; + this.preset = 'worms'; + gui.add(this, 'preset', Object.keys(presets)).onChange((name) => { + updateObject(U, presets[name]); + controllers.forEach((c) => c.updateDisplay()); + }); + par('step_n', 1, 0, 20, 1); + par('dt', 1, 0.3, 1.0); + par('density', 1, 1, 4, 1); + par('senseFlip', false); + par('senseAng', 40, 0, 180); + par('senseDist', 36, 1, 50); + par('moveAng', 7, 0, 180); + par('moveDist', 1, 0, 10); + par('zoom', 1, 1, 8); + gui.add(U, 'follow'); + updateObject(U, presets[this.preset]); + } - frame(glsl) { - for (let i=0; i0) {p.xy += rot2(p.z+radians(senseAng)*float(ID.z-2))[0]*senseDist;} VPos.xy = wld2scr(p.xy+XY*10.0/zoom);`, - FP:`smoothstep(1.0, 0.0, length(XY))*color`}); - } + FP: `smoothstep(1.0, 0.0, length(XY))*color` + }); + } - step(glsl) { - const U=this.U, dt=U.dt; - const field = this.field = glsl({dt, Inc:` + step(glsl) { + const U = this.U, + dt = U.dt; + const field = (this.field = glsl( + { + dt, + Inc: ` float unpack(float x){return exp((x-1.0)*4.0);} - float pack(float x){return log(x)/4.0+1.0;}`, FP:` + float pack(float x){return log(x)/4.0+1.0;}`, + FP: ` vec2 dp = Src_step(); float x=UV.x, y=UV.y; float l=x-dp.x, r=x+dp.x, u=y-dp.y, d=y+dp.y; @@ -70,10 +92,17 @@ class Physarum { float v0 = S(x,y); float v1 = 0.95*(v0+S(l,y)+S(r,y)+S(x,u)+S(x,d)+S(l,u)+S(r,u)+S(l,d)+S(r,d))/9.0; FOut.x = pack(clamp(mix(v0, v1, dt),1e-5,1.)); - `}, {story:2, format:'rgba8', filter:'linear', tag:'field'}); + ` + }, + { story: 2, format: 'rgba8', filter: 'linear', tag: 'field' } + )); - const points = this.points = glsl({field:field[0], ...this.U, - rotAng:(1-U.senseFlip*2)*U.moveAng/180.0*Math.PI, FP:` + const points = (this.points = glsl( + { + field: field[0], + ...this.U, + rotAng: (((1 - U.senseFlip * 2) * U.moveAng) / 180.0) * Math.PI, + FP: ` FOut = Src(I); vec2 wldSize = vec2(field_size()); if (FOut.w == 0.0 || FOut.x>=wldSize.x || FOut.y>=wldSize.y) { @@ -95,10 +124,23 @@ class Physarum { } FOut.xy += dir*moveDist*dt; FOut.xy = mod(FOut.xy, wldSize); - `}, {scale:this.U.density/16, story:2, format:'rgba32f', tag:'points'}); - - glsl({dt, points: points[0], Grid: points[0].size, Blend: 's+d', VP:` - VPos.xy = 2.0 * (points(ID.xy).xy+XY*2.0)/vec2(ViewSize) - 1.0;`, FP:` - smoothstep(1.0, 0.0, length(XY))*dt`}, field[0]); - } + ` + }, + { scale: this.U.density / 16, story: 2, format: 'rgba32f', tag: 'points' } + )); + + glsl( + { + dt, + points: points[0], + Grid: points[0].size, + Blend: 's+d', + VP: ` + VPos.xy = 2.0 * (points(ID.xy).xy+XY*2.0)/vec2(ViewSize) - 1.0;`, + FP: ` + smoothstep(1.0, 0.0, length(XY))*dt` + }, + field[0] + ); + } } diff --git a/src/demos/Physarum3d.js b/src/demos/Physarum3d.js index 2a7ed41..d8747d6 100644 --- a/src/demos/Physarum3d.js +++ b/src/demos/Physarum3d.js @@ -1,34 +1,46 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class Physarum3d { - static Tags = ['3d']; + static Tags = ['3d']; - constructor(glsl, gui) { - this.showVolume = true; - gui.add(this, 'showVolume'); - } + constructor(glsl, gui) { + this.showVolume = true; + gui.add(this, 'showVolume'); + } - frame(glsl, params) { - const D=14, Inc=`const int D=${D}, D2=D*D; + frame(glsl, params) { + const D = 14, + Inc = `const int D=${D}, D2=D*D; ivec2 pos2field(vec3 p) { ivec3 i = ivec3(fract(p)*float(D2)); return i.xy+ivec2(i.z%D, i.z/D)*D2; }`; - // diffusion - const field = glsl({Inc, FP:` + // diffusion + const field = glsl( + { + Inc, + FP: ` ivec3 p = ivec3(I%D2, I.y/D2*D + I.x/D2); ivec3 a=(p-1+D2)%D2, b=(p+1)%D2; #define V(cx,cy,cz) Src(ivec2(cx.x+(cz.z%D)*D2, cy.y+(cz.z/D)*D2)).r FOut.r = Src(I).r*4.0+V(a,p,p)+V(b,p,p)+V(p,a,p)+V(p,b,p)+V(p,p,a)+V(p,p,b); FOut.r *= 0.99/(6.0+4.0); - `}, {size:[D*D*D, D*D*D], story:2, format:'r16f', tag:'field'}); + ` + }, + { size: [D * D * D, D * D * D], story: 2, format: 'r16f', tag: 'field' } + ); - // agent motion - const points = glsl({Inc, field:field[0], seed:Math.random()*12312567,FP:` + // agent motion + const points = glsl( + { + Inc, + field: field[0], + seed: Math.random() * 12312567, + FP: ` vec3 rndunit(ivec3 seed) { return normalize(tan(hash(seed)*2.-1.)); } @@ -50,36 +62,65 @@ class Physarum3d { } FOut.xyz = fract(pos + dir*0.001); FOut1.xyz = dir; - }`}, {size:[256, 256], story:2, format:'rgba32f', layern:2, tag:'points'}); + }` + }, + { size: [256, 256], story: 2, format: 'rgba32f', layern: 2, tag: 'points' } + ); - // deposit - glsl({points:points[0], Grid:points[0].size, Blend:'s+d', Inc, VP:` + // deposit + glsl( + { + points: points[0], + Grid: points[0].size, + Blend: 's+d', + Inc, + VP: ` vec3 pos = points(ID.xy,0).xyz; int l = int(pos.z*float(D2)); VPos.xy = (pos.xy+vec2(l%D, l/D))/float(D) + 0.5*XY/float(D2*D); VPos.xy = VPos.xy*2.0-1.0; - `, FP:`1.0`}, field[0]); + `, + FP: `1.0` + }, + field[0] + ); - // render agents - glsl({...params, Aspect:'fit', DepthTest: 1, - points:points[0], Grid: points[0].size, Mesh:[6,1], VP:` + // render agents + glsl({ + ...params, + Aspect: 'fit', + DepthTest: 1, + points: points[0], + Grid: points[0].size, + Mesh: [6, 1], + VP: ` vec3 pos = points(ID.xy,0).xyz*2.0-1.0; vec3 dir = points(ID.xy,1).xyz; vec3 u=normalize(cross(dir, vec3(1))), v=cross(dir, u); vec2 p = rot2(UV.x*TAU)*vec2(0.5,0); pos += 0.01*mat3(u, v, dir*2.0)*vec3(p*UV.y, 1.0-UV.y); VPos = wld2proj(pos*0.75); - `,FP:`1.0-UV.y*0.7`}); + `, + FP: `1.0-UV.y*0.7` + }); - // fake volume rendering - if (this.showVolume) { - glsl({...params, T:field[0], Grid:[D*D/2,3], Blend:'s+d', Aspect:'fit', - DepthTest:'keep', Inc:Inc+`varying vec3 p;`, VP:` + // fake volume rendering + if (this.showVolume) { + glsl({ + ...params, + T: field[0], + Grid: [(D * D) / 2, 3], + Blend: 's+d', + Aspect: 'fit', + DepthTest: 'keep', + Inc: Inc + `varying vec3 p;`, + VP: ` float l = float(ID.x)/float(Grid.x); p = vec3(UV,l); p = ID.y==0 ? p : (ID.y==1 ? p.xzy : p.zxy); VPos = wld2proj((p*2.0-1.0)*0.75);`, - FP:`T(pos2field(p)).r*vec3(1,0.3,0.1)*0.005,1`}); - } - } + FP: `T(pos2field(p)).r*vec3(1,0.3,0.1)*0.005,1` + }); + } + } } diff --git a/src/demos/ReactionDiffusion.js b/src/demos/ReactionDiffusion.js index 0e3f0b1..4f8db04 100644 --- a/src/demos/ReactionDiffusion.js +++ b/src/demos/ReactionDiffusion.js @@ -1,26 +1,30 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class ReactionDiffusion { - static Tags = ['2d', 'simulation']; + static Tags = ['2d', 'simulation']; - constructor(glsl, gui) { - this.glsl = glsl; - this.step_n = 1; - gui.add(this, 'reset'); - gui.add(this, 'step_n', 0, 20, 1); - this.reset(); - } + constructor(glsl, gui) { + this.glsl = glsl; + this.step_n = 1; + gui.add(this, 'reset'); + gui.add(this, 'step_n', 0, 20, 1); + this.reset(); + } - reset() { - this.state = this.glsl({FP:`1.0, exp(-400.0*dot(XY,XY))*hash(I.xyx).x, 0, 0`}, - {size:[256, 256], format:'rgba16f', filter:'linear', story:2, tag:'state'}); - } + reset() { + this.state = this.glsl( + { FP: `1.0, exp(-400.0*dot(XY,XY))*hash(I.xyx).x, 0, 0` }, + { size: [256, 256], format: 'rgba16f', filter: 'linear', story: 2, tag: 'state' } + ); + } - step() { - this.glsl({FP:` + step() { + this.glsl( + { + FP: ` vec2 v = Src(I).xy; { ivec2 D=Src_size(); @@ -32,30 +36,47 @@ class ReactionDiffusion { const float k=0.05684, f=0.02542; float r = v.x*v.y*v.y; FOut.xy = v + vec2(-r+f*(1.0-v.x), r-(f+k)*v.y); - `}, this.state); - - } + ` + }, + this.state + ); + } - frame(glsl, params) { - const {state} = this; - for (let i=0; iglsl({...p, Inc:` + static Tags = ['3d', 'shadows']; + + constructor(glsl, gui) { + this.glsl = glsl.hook((glsl, p, t) => + glsl( + { + ...p, + Inc: + ` uniform sampler2D shadowmap; uniform bool shadowPass; varying vec4 shadowCoord; @@ -43,22 +48,41 @@ class Shadowmap { FOut.rgb = sqrt(FOut.rgb); // gamma } #endif - `+(p.Inc||'')}, t)); - } + ` + (p.Inc || '') + }, + t + ) + ); + } - drawScene(params) { - const {glsl} = this; - const shadowPass = !params.shadowmap; - const target = shadowPass ? - glsl({}, {size:[1024, 1024], format:'depth', tag:'shadowmap'}) : null; - params = {...params, shadowPass, DepthTest:1}; - // sphere - glsl({...params, Grid:[3], Mesh:[32,32], Clear:[.5,.5,.8,1], VP:` + drawScene(params) { + const { glsl } = this; + const shadowPass = !params.shadowmap; + const target = shadowPass + ? glsl({}, { size: [1024, 1024], format: 'depth', tag: 'shadowmap' }) + : null; + params = { ...params, shadowPass, DepthTest: 1 }; + // sphere + glsl( + { + ...params, + Grid: [3], + Mesh: [32, 32], + Clear: [0.5, 0.5, 0.8, 1], + VP: ` Normal = uv2sphere(UV); - emitVertex(Normal*0.3-vec3(0,0,0.3));`, FP:` - emitFragment(vec3(0.8, 0.2, 0.2));`}, target); - // spirals - glsl({...params, Mesh:[10, 256], VP:` + emitVertex(Normal*0.3-vec3(0,0,0.3));`, + FP: ` + emitFragment(vec3(0.8, 0.2, 0.2));` + }, + target + ); + // spirals + glsl( + { + ...params, + Mesh: [10, 256], + VP: ` vec3 surf(vec2 uv) { float s = uv.y*TAU*8.0; float r1 = 0.7+cos(s)*0.15; @@ -68,26 +92,46 @@ class Shadowmap { } void vertex() { emitVertex(SURF(surf, UV, Normal, 1e-3)); - }`, FP:`emitFragment(vec3(0.3, 0.7, 0.2));`}, target); - // snow - glsl({...params, Grid:[16, 16, 16], VP:` + }`, + FP: `emitFragment(vec3(0.3, 0.7, 0.2));` + }, + target + ); + // snow + glsl( + { + ...params, + Grid: [16, 16, 16], + VP: ` PointSize = 0.005; Normal = vec3(0,0,1); vec3 p = fract(hash(ID)-time*vec3(0.01,0.01,0.1)); - emitVertex((p-0.5)*2.0);`, FP:` + emitVertex((p-0.5)*2.0);`, + FP: ` if (length(XY)>1.0) discard; - emitFragment(vec3(0.9, 0.9, 0.8));`}, target); - // floor - glsl({...params, Face:'front', VP:` + emitFragment(vec3(0.9, 0.9, 0.8));` + }, + target + ); + // floor + glsl( + { + ...params, + Face: 'front', + VP: ` Normal = vec3(0,0,1); - emitVertex(vec3(XY, -0.8));`, FP:` - emitFragment(vec3(0.6));`}, target) - return target; - } + emitVertex(vec3(XY, -0.8));`, + FP: ` + emitFragment(vec3(0.6));` + }, + target + ); + return target; + } - frame(_, params) { - const shadowmap = this.drawScene(params); - this.drawScene({...params, Aspect:'mean', shadowmap}); - //this.glsl({tex:shadowmap, View:[20, 20, 256, 256], FP:`1.0-tex(UV).x`}); - } + frame(_, params) { + const shadowmap = this.drawScene(params); + this.drawScene({ ...params, Aspect: 'mean', shadowmap }); + //this.glsl({tex:shadowmap, View:[20, 20, 256, 256], FP:`1.0-tex(UV).x`}); + } } diff --git a/src/demos/Spectrogram.js b/src/demos/Spectrogram.js index 80264cd..ccb97e5 100644 --- a/src/demos/Spectrogram.js +++ b/src/demos/Spectrogram.js @@ -1,42 +1,58 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ // Example of streaming spectrogram data from WebAudio to WebGL2 class Spectrogram { - static Tags = ['3d', 'data']; - - constructor(glsl, gui) { - navigator.mediaDevices.getUserMedia({audio: true}).then(stream=>{ - this.audioCtx = new AudioContext(); - this.input = this.audioCtx.createMediaStreamSource(stream); - this.analyser = this.audioCtx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.5; - this.input.connect(this.analyser); - this.frequencyArray = new Uint8Array(this.analyser.frequencyBinCount); - console.log('mic activated, frequencyBinCount:', this.analyser.frequencyBinCount); - }).catch(e=>console.error('Error getting microphone:', e)); - } + static Tags = ['3d', 'data']; - frame(glsl, params) { - if (!this.analyser) return; - this.analyser.getByteFrequencyData(this.frequencyArray); - const n = this.frequencyArray.length; - const histLen = 256; - const spectro = glsl({}, {size:[n, 1], format:'r8', data:this.frequencyArray, tag:'spectro'}); - const history = glsl({spectro, FP:'I.y>0 ? Src(I-ivec2(0,1)) : spectro(ivec2(I.x,0))'}, - {size:[n,histLen], story:2, wrap:'edge', tag:'history'}); - glsl({...params, history:history[0], Mesh:[n-1,histLen-1], DepthTest:1, Aspect:'fit', VP:` + constructor(glsl, gui) { + navigator.mediaDevices + .getUserMedia({ audio: true }) + .then((stream) => { + this.audioCtx = new AudioContext(); + this.input = this.audioCtx.createMediaStreamSource(stream); + this.analyser = this.audioCtx.createAnalyser(); + this.analyser.smoothingTimeConstant = 0.5; + this.input.connect(this.analyser); + this.frequencyArray = new Uint8Array(this.analyser.frequencyBinCount); + console.log('mic activated, frequencyBinCount:', this.analyser.frequencyBinCount); + }) + .catch((e) => console.error('Error getting microphone:', e)); + } + + frame(glsl, params) { + if (!this.analyser) return; + this.analyser.getByteFrequencyData(this.frequencyArray); + const n = this.frequencyArray.length; + const histLen = 256; + const spectro = glsl( + {}, + { size: [n, 1], format: 'r8', data: this.frequencyArray, tag: 'spectro' } + ); + const history = glsl( + { spectro, FP: 'I.y>0 ? Src(I-ivec2(0,1)) : spectro(ivec2(I.x,0))' }, + { size: [n, histLen], story: 2, wrap: 'edge', tag: 'history' } + ); + glsl({ + ...params, + history: history[0], + Mesh: [n - 1, histLen - 1], + DepthTest: 1, + Aspect: 'fit', + VP: ` varying float z = history(UV).r; float x = 1.0-log(0.005+UV.x)/log(0.005); - VPos = wld2proj(vec4(-XY.y, (x-0.5)*1.8, z*0.5, 1.0));`, FP:` - mix(vec3(0.0, 0.0, 0.1), vec3(0.9, 0.8, 0.5), z*2.0),1`}); - } + VPos = wld2proj(vec4(-XY.y, (x-0.5)*1.8, z*0.5, 1.0));`, + FP: ` + mix(vec3(0.0, 0.0, 0.1), vec3(0.9, 0.8, 0.5), z*2.0),1` + }); + } - free() { - if (!this.input) return; - this.input.mediaStream.getTracks().forEach(tr=>tr.stop()); - this.audioCtx.close(); - } + free() { + if (!this.input) return; + this.input.mediaStream.getTracks().forEach((tr) => tr.stop()); + this.audioCtx.close(); + } } diff --git a/src/demos/Springs.js b/src/demos/Springs.js index 21bf296..5d19d07 100644 --- a/src/demos/Springs.js +++ b/src/demos/Springs.js @@ -1,31 +1,42 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class Springs { - static Tags = ['2d']; + static Tags = ['2d']; - constructor(glsl, gui) { - this.glsl = glsl; - this.reset(); - this.params = {waveRate:4.0}; - gui.add(this, 'reset'); - gui.add(this.params, 'waveRate', 0.0, 8.0); - } + constructor(glsl, gui) { + this.glsl = glsl; + this.reset(); + this.params = { waveRate: 4.0 }; + gui.add(this, 'reset'); + gui.add(this.params, 'waveRate', 0.0, 8.0); + } - reset() { - const {glsl} = this; - for (let i=0; i<2; ++i) - this.points = glsl({FP:` + reset() { + const { glsl } = this; + for (let i = 0; i < 2; ++i) + this.points = glsl( + { + FP: ` XY*rot2(PI+0.1)*0.6+vec2(0,0.3),0,(UV.x+UV.y)*10.0 - `}, {size:[64, 64], format:'rgba32f', story:2, tag:'points'}); - } + ` + }, + { size: [64, 64], format: 'rgba32f', story: 2, tag: 'points' } + ); + } - frame(glsl, {time}) { - const {points} = this; + frame(glsl, { time }) { + const { points } = this; - const next = glsl({time, ...this.params, points:points[0], prev:points[1], FP:` + const next = glsl( + { + time, + ...this.params, + points: points[0], + prev: points[1], + FP: ` FOut = points(I); if ((I.x%8 == 0 && I.y==0) || I == ivec2(ViewSize.x-1,0)) return; vec3 vel = FOut.xyz-prev(I).xyz; @@ -34,10 +45,16 @@ class Springs { if (FOut.y<-0.8) { FOut.x -= vel.x*0.8; } - `}, {size:points[0].size, format:'rgba32f', story:2, tag:'next'}); + ` + }, + { size: points[0].size, format: 'rgba32f', story: 2, tag: 'next' } + ); - for (let i=0; i<8; ++i) - glsl({time, FP:` + for (let i = 0; i < 8; ++i) + glsl( + { + time, + FP: ` vec4 relax(vec3 p, vec3 p0, float dist) { vec3 dp = p-p0; return vec4(p0+dp/(length(dp)+1e-10)*dist, 1.0); @@ -63,15 +80,23 @@ class Springs { FOut.y = -0.8; FOut.x = mix(FOut.x, p.x, 0.9); } - }`}, next); - glsl({next:next[0], FP:`next(I)`}, points) + }` + }, + next + ); + glsl({ next: next[0], FP: `next(I)` }, points); - glsl({FP:`float(XY.y<-0.8)*0.5`}) + glsl({ FP: `float(XY.y<-0.8)*0.5` }); - const [w, h] = points[0].size; - glsl({points:next[0], Mesh:[w-1,h-1], Aspect:'y', - Blend: 'd*(1-sa)+s*sa',VP:` + const [w, h] = points[0].size; + glsl({ + points: next[0], + Mesh: [w - 1, h - 1], + Aspect: 'y', + Blend: 'd*(1-sa)+s*sa', + VP: ` points(VID.xy).xy,0,1`, - FP:`sqrt(isoline(UV.x*float(Mesh.x))+isoline(UV.y*float(Mesh.y)))`}); - } -} \ No newline at end of file + FP: `sqrt(isoline(UV.x*float(Mesh.x))+isoline(UV.y*float(Mesh.y)))` + }); + } +} diff --git a/src/demos/SurfaceNormals.js b/src/demos/SurfaceNormals.js index 8d94b75..5632ed6 100644 --- a/src/demos/SurfaceNormals.js +++ b/src/demos/SurfaceNormals.js @@ -1,14 +1,19 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class SurfaceNormals { - static Tags = ['3d']; - - frame(glsl, params) { - glsl({...params, Mesh:[64,128], Grid:[5,5], - Aspect:'fit', DepthTest:1, VP:` + static Tags = ['3d']; + + frame(glsl, params) { + glsl({ + ...params, + Mesh: [64, 128], + Grid: [5, 5], + Aspect: 'fit', + DepthTest: 1, + VP: ` vec3 surface_f(vec2 p) { vec2 c = sin(time+p*vec2(ID)*TAU); vec3 pos = torus(p, 1.0, 0.4 + 0.1*c.x + 0.15*c.y)/8.0; @@ -19,12 +24,14 @@ class SurfaceNormals { varying vec3 normal; vec4 pos = vec4(SURF(surface_f, UV, normal, 1e-3), 1.0); VPos = wld2proj(pos); - }`, FP:` + }`, + FP: ` FOut = vec4(normal*0.6, 1); vec2 m = UV*vec2(Mesh)/4.0; FOut.rgb += (isoline(m.x)+isoline(m.y))*0.2; // useful for debugging incorrect face ordering // FOut.r += float(!gl_FrontFacing); - `}); - } + ` + }); + } } diff --git a/src/demos/TextureSamplers.js b/src/demos/TextureSamplers.js index 38613a6..acabde5 100644 --- a/src/demos/TextureSamplers.js +++ b/src/demos/TextureSamplers.js @@ -1,22 +1,33 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ // Use different sampling modes on the same texture class TextureSamplers { - static Tags = ['2d']; - frame(glsl, {time}) { - const T = glsl({time, FP:` + static Tags = ['2d']; + frame(glsl, { time }) { + const T = glsl( + { + time, + FP: ` vec2 p = rot2(sin(time)*8.0*smoothstep(1.03,0.0, length(XY)))*XY; FOut = vec4(smoothstep(-1.,1.,(p/fwidth(p)).y)); - `}, {size:[32, 32], tag:'T'}); + ` + }, + { size: [32, 32], tag: 'T' } + ); - glsl({Aspect:'mean', - A:T.edge, B:T.mirror, - C:T.linear.mirror, D:T.linear.repeat, FP:` + glsl({ + Aspect: 'mean', + A: T.edge, + B: T.mirror, + C: T.linear.mirror, + D: T.linear.repeat, + FP: ` bool x=XY.x<0.0, y=XY.y<0.0; vec2 p = fract(XY)*2.9-0.95; - FOut = y? (x?C(p):D(p)) : (x?A(p):B(p));`}); - } + FOut = y? (x?C(p):D(p)) : (x?A(p):B(p));` + }); + } } diff --git a/src/demos/Torus4d.js b/src/demos/Torus4d.js index 60a3733..77fadac 100644 --- a/src/demos/Torus4d.js +++ b/src/demos/Torus4d.js @@ -1,19 +1,26 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ class Torus4d { - static Tags = ['3d']; - frame(glsl, params) { - glsl({...params, Mesh:[100,100], Aspect:'fit', - AlphaCoverage:1, DepthTest:1, VP:` + static Tags = ['3d']; + frame(glsl, params) { + glsl({ + ...params, + Mesh: [100, 100], + Aspect: 'fit', + AlphaCoverage: 1, + DepthTest: 1, + VP: ` vec4 p = vec4(cos(XY*PI), sin(XY*PI))*0.6; p.xw *= rot2(time*0.4); - VPos = wld2proj(vec4(p.xyz/(1.0-p.w)*0.5, 1));`, FP:` + VPos = wld2proj(vec4(p.xyz/(1.0-p.w)*0.5, 1));`, + FP: ` vec2 v = UV*rot2(PI/4.)*64.0/sqrt(2.); v = smoothstep(0.0, 1.0, (abs(v-round(v))-0.02)/fwidth(v)); float a = 1.0-v.x*v.y; if (a<0.1) discard; - FOut = vec4(gl_FrontFacing?vec3(.9,.9,.6):vec3(.6,.6,.9), a);`}); - } + FOut = vec4(gl_FrontFacing?vec3(.9,.9,.6):vec3(.6,.6,.9), a);` + }); + } } diff --git a/src/demos/audio.js b/src/demos/audio.js index 87c2b63..d421312 100644 --- a/src/demos/audio.js +++ b/src/demos/audio.js @@ -1,66 +1,72 @@ -/** @license - * Copyright 2023 Google LLC. - * SPDX-License-Identifier: Apache-2.0 - */ +/** @license + * Copyright 2023 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ -class AudioWorkletProcessor {}; +class AudioWorkletProcessor {} class AudioStream extends AudioWorkletProcessor { - constructor() { - super(); - this.chunkSize = 1024; - if (typeof registerProcessor === 'undefined') { - return; // main thread - } - this.queue = []; - this.pos = -1; // queue is empty, need fade-in - this.frame = 0; - this.port.onmessage = e=>this.queue.push(e.data); - for (let i=0; i<2; ++i) { - this.queue.push(new Float32Array(this.chunkSize*2)); - this._requestChunk(); - } - } - _requestChunk() { - const buf = this.queue.shift(); - this.port.postMessage({frame:this.frame, sampleRate, buf}, [buf.buffer]); - this.frame += this.chunkSize; - this.pos = this.queue.length ? 0 : -1; - } - process(inputs, outputs, parameters) { - if (!this.queue.length) return true; - const src = this.queue[0]; - const [c0, c1]=outputs[0], n=c0.length; - let v=1.0, dv=0.0; - if (this.pos == -1) { - this.pos = 0; - v = 0.0; dv = 1.0/n; // fade-in - } else if (src.length - this.pos <= n*2 && this.queue.length==1) { - v = 1.0; dv = -1.0/n; // fade-out - } - for (let i=0; i= src.length) { - this._requestChunk(); - } - return true; - } - async start(callback) { - this.audioContext = new AudioContext(); - const audioWorkletJS = AudioStream.toString() - + '\nregisterProcessor("worklet-processor", AudioStream);'; - const workletURL = URL.createObjectURL( - new Blob([audioWorkletJS], {type: 'application/javascript'})); - await this.audioContext.audioWorklet.addModule(workletURL); - this.workletNode = new AudioWorkletNode( - this.audioContext, 'worklet-processor',{outputChannelCount:[2]}); - const submit = buf=>this.workletNode.port.postMessage(buf, [buf.buffer]); - this.workletNode.port.onmessage = msg=>callback(msg.data, submit); - this.workletNode.connect(this.audioContext.destination); - console.log('audio stream started'); - } - stop() { - this.audioContext.close(); - } -}; + constructor() { + super(); + this.chunkSize = 1024; + if (typeof registerProcessor === 'undefined') { + return; // main thread + } + this.queue = []; + this.pos = -1; // queue is empty, need fade-in + this.frame = 0; + this.port.onmessage = (e) => this.queue.push(e.data); + for (let i = 0; i < 2; ++i) { + this.queue.push(new Float32Array(this.chunkSize * 2)); + this._requestChunk(); + } + } + _requestChunk() { + const buf = this.queue.shift(); + this.port.postMessage({ frame: this.frame, sampleRate, buf }, [buf.buffer]); + this.frame += this.chunkSize; + this.pos = this.queue.length ? 0 : -1; + } + process(inputs, outputs, parameters) { + if (!this.queue.length) return true; + const src = this.queue[0]; + const [c0, c1] = outputs[0], + n = c0.length; + let v = 1.0, + dv = 0.0; + if (this.pos == -1) { + this.pos = 0; + v = 0.0; + dv = 1.0 / n; // fade-in + } else if (src.length - this.pos <= n * 2 && this.queue.length == 1) { + v = 1.0; + dv = -1.0 / n; // fade-out + } + for (let i = 0; i < n; ++i, this.pos += 2, v += dv) { + c0[i] = src[this.pos] * v; + c1[i] = src[this.pos + 1] * v; + } + if (this.pos >= src.length) { + this._requestChunk(); + } + return true; + } + async start(callback) { + this.audioContext = new AudioContext(); + const audioWorkletJS = + AudioStream.toString() + '\nregisterProcessor("worklet-processor", AudioStream);'; + const workletURL = URL.createObjectURL( + new Blob([audioWorkletJS], { type: 'application/javascript' }) + ); + await this.audioContext.audioWorklet.addModule(workletURL); + this.workletNode = new AudioWorkletNode(this.audioContext, 'worklet-processor', { + outputChannelCount: [2] + }); + const submit = (buf) => this.workletNode.port.postMessage(buf, [buf.buffer]); + this.workletNode.port.onmessage = (msg) => callback(msg.data, submit); + this.workletNode.connect(this.audioContext.destination); + console.log('audio stream started'); + } + stop() { + this.audioContext.close(); + } +} diff --git a/src/demos/dat.gui.min.js b/src/demos/dat.gui.min.js index dcec6af..5ff73eb 100644 --- a/src/demos/dat.gui.min.js +++ b/src/demos/dat.gui.min.js @@ -10,4 +10,2215 @@ * * http://www.apache.org/licenses/LICENSE-2.0 */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.dat={})}(this,function(e){"use strict";function t(e,t){var n=e.__state.conversionName.toString(),o=Math.round(e.r),i=Math.round(e.g),r=Math.round(e.b),s=e.a,a=Math.round(e.h),l=e.s.toFixed(1),d=e.v.toFixed(1);if(t||"THREE_CHAR_HEX"===n||"SIX_CHAR_HEX"===n){for(var c=e.hex.toString(16);c.length<6;)c="0"+c;return"#"+c}return"CSS_RGB"===n?"rgb("+o+","+i+","+r+")":"CSS_RGBA"===n?"rgba("+o+","+i+","+r+","+s+")":"HEX"===n?"0x"+e.hex.toString(16):"RGB_ARRAY"===n?"["+o+","+i+","+r+"]":"RGBA_ARRAY"===n?"["+o+","+i+","+r+","+s+"]":"RGB_OBJ"===n?"{r:"+o+",g:"+i+",b:"+r+"}":"RGBA_OBJ"===n?"{r:"+o+",g:"+i+",b:"+r+",a:"+s+"}":"HSV_OBJ"===n?"{h:"+a+",s:"+l+",v:"+d+"}":"HSVA_OBJ"===n?"{h:"+a+",s:"+l+",v:"+d+",a:"+s+"}":"unknown format"}function n(e,t,n){Object.defineProperty(e,t,{get:function(){return"RGB"===this.__state.space?this.__state[t]:(I.recalculateRGB(this,t,n),this.__state[t])},set:function(e){"RGB"!==this.__state.space&&(I.recalculateRGB(this,t,n),this.__state.space="RGB"),this.__state[t]=e}})}function o(e,t){Object.defineProperty(e,t,{get:function(){return"HSV"===this.__state.space?this.__state[t]:(I.recalculateHSV(this),this.__state[t])},set:function(e){"HSV"!==this.__state.space&&(I.recalculateHSV(this),this.__state.space="HSV"),this.__state[t]=e}})}function i(e){if("0"===e||S.isUndefined(e))return 0;var t=e.match(U);return S.isNull(t)?0:parseFloat(t[1])}function r(e){var t=e.toString();return t.indexOf(".")>-1?t.length-t.indexOf(".")-1:0}function s(e,t){var n=Math.pow(10,t);return Math.round(e*n)/n}function a(e,t,n,o,i){return o+(e-t)/(n-t)*(i-o)}function l(e,t,n,o){e.style.background="",S.each(ee,function(i){e.style.cssText+="background: "+i+"linear-gradient("+t+", "+n+" 0%, "+o+" 100%); "})}function d(e){e.style.background="",e.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);",e.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}function c(e,t,n){var o=document.createElement("li");return t&&o.appendChild(t),n?e.__ul.insertBefore(o,n):e.__ul.appendChild(o),e.onResize(),o}function u(e){X.unbind(window,"resize",e.__resizeHandler),e.saveToLocalStorageIfPossible&&X.unbind(window,"unload",e.saveToLocalStorageIfPossible)}function _(e,t){var n=e.__preset_select[e.__preset_select.selectedIndex];n.innerHTML=t?n.value+"*":n.value}function h(e,t,n){if(n.__li=t,n.__gui=e,S.extend(n,{options:function(t){if(arguments.length>1){var o=n.__li.nextElementSibling;return n.remove(),f(e,n.object,n.property,{before:o,factoryArgs:[S.toArray(arguments)]})}if(S.isArray(t)||S.isObject(t)){var i=n.__li.nextElementSibling;return n.remove(),f(e,n.object,n.property,{before:i,factoryArgs:[t]})}},name:function(e){return n.__li.firstElementChild.firstElementChild.innerHTML=e,n},listen:function(){return n.__gui.listen(n),n},remove:function(){return n.__gui.remove(n),n}}),n instanceof q){var o=new Q(n.object,n.property,{min:n.__min,max:n.__max,step:n.__step});S.each(["updateDisplay","onChange","onFinishChange","step","min","max"],function(e){var t=n[e],i=o[e];n[e]=o[e]=function(){var e=Array.prototype.slice.call(arguments);return i.apply(o,e),t.apply(n,e)}}),X.addClass(t,"has-slider"),n.domElement.insertBefore(o.domElement,n.domElement.firstElementChild)}else if(n instanceof Q){var i=function(t){if(S.isNumber(n.__min)&&S.isNumber(n.__max)){var o=n.__li.firstElementChild.firstElementChild.innerHTML,i=n.__gui.__listening.indexOf(n)>-1;n.remove();var r=f(e,n.object,n.property,{before:n.__li.nextElementSibling,factoryArgs:[n.__min,n.__max,n.__step]});return r.name(o),i&&r.listen(),r}return t};n.min=S.compose(i,n.min),n.max=S.compose(i,n.max)}else n instanceof K?(X.bind(t,"click",function(){X.fakeEvent(n.__checkbox,"click")}),X.bind(n.__checkbox,"click",function(e){e.stopPropagation()})):n instanceof Z?(X.bind(t,"click",function(){X.fakeEvent(n.__button,"click")}),X.bind(t,"mouseover",function(){X.addClass(n.__button,"hover")}),X.bind(t,"mouseout",function(){X.removeClass(n.__button,"hover")})):n instanceof $&&(X.addClass(t,"color"),n.updateDisplay=S.compose(function(e){return t.style.borderLeftColor=n.__color.toString(),e},n.updateDisplay),n.updateDisplay());n.setValue=S.compose(function(t){return e.getRoot().__preset_select&&n.isModified()&&_(e.getRoot(),!0),t},n.setValue)}function p(e,t){var n=e.getRoot(),o=n.__rememberedObjects.indexOf(t.object);if(-1!==o){var i=n.__rememberedObjectIndecesToControllers[o];if(void 0===i&&(i={},n.__rememberedObjectIndecesToControllers[o]=i),i[t.property]=t,n.load&&n.load.remembered){var r=n.load.remembered,s=void 0;if(r[e.preset])s=r[e.preset];else{if(!r[se])return;s=r[se]}if(s[o]&&void 0!==s[o][t.property]){var a=s[o][t.property];t.initialValue=a,t.setValue(a)}}}}function f(e,t,n,o){if(void 0===t[n])throw new Error('Object "'+t+'" has no property "'+n+'"');var i=void 0;if(o.color)i=new $(t,n);else{var r=[t,n].concat(o.factoryArgs);i=ne.apply(e,r)}o.before instanceof z&&(o.before=o.before.__li),p(e,i),X.addClass(i.domElement,"c");var s=document.createElement("span");X.addClass(s,"property-name"),s.innerHTML=i.property;var a=document.createElement("div");a.appendChild(s),a.appendChild(i.domElement);var l=c(e,a,o.before);return X.addClass(l,he.CLASS_CONTROLLER_ROW),i instanceof $?X.addClass(l,"color"):X.addClass(l,H(i.getValue())),h(e,l,i),e.__controllers.push(i),i}function m(e,t){return document.location.href+"."+t}function g(e,t,n){var o=document.createElement("option");o.innerHTML=t,o.value=t,e.__preset_select.appendChild(o),n&&(e.__preset_select.selectedIndex=e.__preset_select.length-1)}function b(e,t){t.style.display=e.useLocalStorage?"block":"none"}function v(e){var t=e.__save_row=document.createElement("li");X.addClass(e.domElement,"has-save"),e.__ul.insertBefore(t,e.__ul.firstChild),X.addClass(t,"save-row");var n=document.createElement("span");n.innerHTML=" ",X.addClass(n,"button gears");var o=document.createElement("span");o.innerHTML="Save",X.addClass(o,"button"),X.addClass(o,"save");var i=document.createElement("span");i.innerHTML="New",X.addClass(i,"button"),X.addClass(i,"save-as");var r=document.createElement("span");r.innerHTML="Revert",X.addClass(r,"button"),X.addClass(r,"revert");var s=e.__preset_select=document.createElement("select");if(e.load&&e.load.remembered?S.each(e.load.remembered,function(t,n){g(e,n,n===e.preset)}):g(e,se,!1),X.bind(s,"change",function(){for(var t=0;t=0;n--)t=[e[n].apply(this,t)];return t[0]}},each:function(e,t,n){if(e)if(A&&e.forEach&&e.forEach===A)e.forEach(t,n);else if(e.length===e.length+0){var o=void 0,i=void 0;for(o=0,i=e.length;o1?S.toArray(arguments):arguments[0];return S.each(O,function(t){if(t.litmus(e))return S.each(t.conversions,function(t,n){if(T=t.read(e),!1===L&&!1!==T)return L=T,T.conversionName=n,T.conversion=t,S.BREAK}),S.BREAK}),L},B=void 0,N={hsv_to_rgb:function(e,t,n){var o=Math.floor(e/60)%6,i=e/60-Math.floor(e/60),r=n*(1-t),s=n*(1-i*t),a=n*(1-(1-i)*t),l=[[n,a,r],[s,n,r],[r,n,a],[r,s,n],[a,r,n],[n,r,s]][o];return{r:255*l[0],g:255*l[1],b:255*l[2]}},rgb_to_hsv:function(e,t,n){var o=Math.min(e,t,n),i=Math.max(e,t,n),r=i-o,s=void 0,a=void 0;return 0===i?{h:NaN,s:0,v:0}:(a=r/i,s=e===i?(t-n)/r:t===i?2+(n-e)/r:4+(e-t)/r,(s/=6)<0&&(s+=1),{h:360*s,s:a,v:i/255})},rgb_to_hex:function(e,t,n){var o=this.hex_with_component(0,2,e);return o=this.hex_with_component(o,1,t),o=this.hex_with_component(o,0,n)},component_from_hex:function(e,t){return e>>8*t&255},hex_with_component:function(e,t,n){return n<<(B=8*t)|e&~(255<this.__max&&(n=this.__max),void 0!==this.__step&&n%this.__step!=0&&(n=Math.round(n/this.__step)*this.__step),D(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"setValue",this).call(this,n)}},{key:"min",value:function(e){return this.__min=e,this}},{key:"max",value:function(e){return this.__max=e,this}},{key:"step",value:function(e){return this.__step=e,this.__impliedStep=e,this.__precision=r(e),this}}]),t}(),Q=function(e){function t(e,n,o){function i(){l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())}function r(e){var t=d-e.clientY;l.setValue(l.getValue()+t*l.__impliedStep),d=e.clientY}function s(){X.unbind(window,"mousemove",r),X.unbind(window,"mouseup",s),i()}F(this,t);var a=V(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,o));a.__truncationSuspended=!1;var l=a,d=void 0;return a.__input=document.createElement("input"),a.__input.setAttribute("type","text"),X.bind(a.__input,"change",function(){var e=parseFloat(l.__input.value);S.isNaN(e)||l.setValue(e)}),X.bind(a.__input,"blur",function(){i()}),X.bind(a.__input,"mousedown",function(e){X.bind(window,"mousemove",r),X.bind(window,"mouseup",s),d=e.clientY}),X.bind(a.__input,"keydown",function(e){13===e.keyCode&&(l.__truncationSuspended=!0,this.blur(),l.__truncationSuspended=!1,i())}),a.updateDisplay(),a.domElement.appendChild(a.__input),a}return j(t,W),P(t,[{key:"updateDisplay",value:function(){return this.__input.value=this.__truncationSuspended?this.getValue():s(this.getValue(),this.__precision),D(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"updateDisplay",this).call(this)}}]),t}(),q=function(e){function t(e,n,o,i,r){function s(e){e.preventDefault();var t=_.__background.getBoundingClientRect();return _.setValue(a(e.clientX,t.left,t.right,_.__min,_.__max)),!1}function l(){X.unbind(window,"mousemove",s),X.unbind(window,"mouseup",l),_.__onFinishChange&&_.__onFinishChange.call(_,_.getValue())}function d(e){var t=e.touches[0].clientX,n=_.__background.getBoundingClientRect();_.setValue(a(t,n.left,n.right,_.__min,_.__max))}function c(){X.unbind(window,"touchmove",d),X.unbind(window,"touchend",c),_.__onFinishChange&&_.__onFinishChange.call(_,_.getValue())}F(this,t);var u=V(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,{min:o,max:i,step:r})),_=u;return u.__background=document.createElement("div"),u.__foreground=document.createElement("div"),X.bind(u.__background,"mousedown",function(e){document.activeElement.blur(),X.bind(window,"mousemove",s),X.bind(window,"mouseup",l),s(e)}),X.bind(u.__background,"touchstart",function(e){1===e.touches.length&&(X.bind(window,"touchmove",d),X.bind(window,"touchend",c),d(e))}),X.addClass(u.__background,"slider"),X.addClass(u.__foreground,"slider-fg"),u.updateDisplay(),u.__background.appendChild(u.__foreground),u.domElement.appendChild(u.__background),u}return j(t,W),P(t,[{key:"updateDisplay",value:function(){var e=(this.getValue()-this.__min)/(this.__max-this.__min);return this.__foreground.style.width=100*e+"%",D(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"updateDisplay",this).call(this)}}]),t}(),Z=function(e){function t(e,n,o){F(this,t);var i=V(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n)),r=i;return i.__button=document.createElement("div"),i.__button.innerHTML=void 0===o?"Fire":o,X.bind(i.__button,"click",function(e){return e.preventDefault(),r.fire(),!1}),X.addClass(i.__button,"button"),i.domElement.appendChild(i.__button),i}return j(t,z),P(t,[{key:"fire",value:function(){this.__onChange&&this.__onChange.call(this),this.getValue().call(this.object),this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue())}}]),t}(),$=function(e){function t(e,n){function o(e){u(e),X.bind(window,"mousemove",u),X.bind(window,"touchmove",u),X.bind(window,"mouseup",r),X.bind(window,"touchend",r)}function i(e){_(e),X.bind(window,"mousemove",_),X.bind(window,"touchmove",_),X.bind(window,"mouseup",s),X.bind(window,"touchend",s)}function r(){X.unbind(window,"mousemove",u),X.unbind(window,"touchmove",u),X.unbind(window,"mouseup",r),X.unbind(window,"touchend",r),c()}function s(){X.unbind(window,"mousemove",_),X.unbind(window,"touchmove",_),X.unbind(window,"mouseup",s),X.unbind(window,"touchend",s),c()}function a(){var e=R(this.value);!1!==e?(p.__color.__state=e,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function c(){p.__onFinishChange&&p.__onFinishChange.call(p,p.__color.toOriginal())}function u(e){-1===e.type.indexOf("touch")&&e.preventDefault();var t=p.__saturation_field.getBoundingClientRect(),n=e.touches&&e.touches[0]||e,o=n.clientX,i=n.clientY,r=(o-t.left)/(t.right-t.left),s=1-(i-t.top)/(t.bottom-t.top);return s>1?s=1:s<0&&(s=0),r>1?r=1:r<0&&(r=0),p.__color.v=s,p.__color.s=r,p.setValue(p.__color.toOriginal()),!1}function _(e){-1===e.type.indexOf("touch")&&e.preventDefault();var t=p.__hue_field.getBoundingClientRect(),n=1-((e.touches&&e.touches[0]||e).clientY-t.top)/(t.bottom-t.top);return n>1?n=1:n<0&&(n=0),p.__color.h=360*n,p.setValue(p.__color.toOriginal()),!1}F(this,t);var h=V(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n));h.__color=new I(h.getValue()),h.__temp=new I(0);var p=h;h.domElement=document.createElement("div"),X.makeSelectable(h.domElement,!1),h.__selector=document.createElement("div"),h.__selector.className="selector",h.__saturation_field=document.createElement("div"),h.__saturation_field.className="saturation-field",h.__field_knob=document.createElement("div"),h.__field_knob.className="field-knob",h.__field_knob_border="2px solid ",h.__hue_knob=document.createElement("div"),h.__hue_knob.className="hue-knob",h.__hue_field=document.createElement("div"),h.__hue_field.className="hue-field",h.__input=document.createElement("input"),h.__input.type="text",h.__input_textShadow="0 1px 1px ",X.bind(h.__input,"keydown",function(e){13===e.keyCode&&a.call(this)}),X.bind(h.__input,"blur",a),X.bind(h.__selector,"mousedown",function(){X.addClass(this,"drag").bind(window,"mouseup",function(){X.removeClass(p.__selector,"drag")})}),X.bind(h.__selector,"touchstart",function(){X.addClass(this,"drag").bind(window,"touchend",function(){X.removeClass(p.__selector,"drag")})});var f=document.createElement("div");return S.extend(h.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}),S.extend(h.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:h.__field_knob_border+(h.__color.v<.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1}),S.extend(h.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1}),S.extend(h.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"}),S.extend(f.style,{width:"100%",height:"100%",background:"none"}),l(f,"top","rgba(0,0,0,0)","#000"),S.extend(h.__hue_field.style,{width:"15px",height:"100px",border:"1px solid #555",cursor:"ns-resize",position:"absolute",top:"3px",right:"3px"}),d(h.__hue_field),S.extend(h.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:h.__input_textShadow+"rgba(0,0,0,0.7)"}),X.bind(h.__saturation_field,"mousedown",o),X.bind(h.__saturation_field,"touchstart",o),X.bind(h.__field_knob,"mousedown",o),X.bind(h.__field_knob,"touchstart",o),X.bind(h.__hue_field,"mousedown",i),X.bind(h.__hue_field,"touchstart",i),h.__saturation_field.appendChild(f),h.__selector.appendChild(h.__field_knob),h.__selector.appendChild(h.__saturation_field),h.__selector.appendChild(h.__hue_field),h.__hue_field.appendChild(h.__hue_knob),h.domElement.appendChild(h.__input),h.domElement.appendChild(h.__selector),h.updateDisplay(),h}return j(t,z),P(t,[{key:"updateDisplay",value:function(){var e=R(this.getValue());if(!1!==e){var t=!1;S.each(I.COMPONENTS,function(n){if(!S.isUndefined(e[n])&&!S.isUndefined(this.__color.__state[n])&&e[n]!==this.__color.__state[n])return t=!0,{}},this),t&&S.extend(this.__color.__state,e)}S.extend(this.__temp.__state,this.__color.__state),this.__temp.a=1;var n=this.__color.v<.5||this.__color.s>.5?255:0,o=255-n;S.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toHexString(),border:this.__field_knob_border+"rgb("+n+","+n+","+n+")"}),this.__hue_knob.style.marginTop=100*(1-this.__color.h/360)+"px",this.__temp.s=1,this.__temp.v=1,l(this.__saturation_field,"left","#fff",this.__temp.toHexString()),this.__input.value=this.__color.toString(),S.extend(this.__input.style,{backgroundColor:this.__color.toHexString(),color:"rgb("+n+","+n+","+n+")",textShadow:this.__input_textShadow+"rgba("+o+","+o+","+o+",.7)"})}}]),t}(),ee=["-moz-","-o-","-webkit-","-ms-",""],te={load:function(e,t){var n=t||document,o=n.createElement("link");o.type="text/css",o.rel="stylesheet",o.href=e,n.getElementsByTagName("head")[0].appendChild(o)},inject:function(e,t){var n=t||document,o=document.createElement("style");o.type="text/css",o.innerHTML=e;var i=n.getElementsByTagName("head")[0];try{i.appendChild(o)}catch(e){}}},ne=function(e,t){var n=e[t];return S.isArray(arguments[2])||S.isObject(arguments[2])?new Y(e,t,arguments[2]):S.isNumber(n)?S.isNumber(arguments[2])&&S.isNumber(arguments[3])?S.isNumber(arguments[4])?new q(e,t,arguments[2],arguments[3],arguments[4]):new q(e,t,arguments[2],arguments[3]):S.isNumber(arguments[4])?new Q(e,t,{min:arguments[2],max:arguments[3],step:arguments[4]}):new Q(e,t,{min:arguments[2],max:arguments[3]}):S.isString(n)?new J(e,t):S.isFunction(n)?new Z(e,t,""):S.isBoolean(n)?new K(e,t):null},oe=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){setTimeout(e,1e3/60)},ie=function(){function e(){F(this,e),this.backgroundElement=document.createElement("div"),S.extend(this.backgroundElement.style,{backgroundColor:"rgba(0,0,0,0.8)",top:0,left:0,display:"none",zIndex:"1000",opacity:0,WebkitTransition:"opacity 0.2s linear",transition:"opacity 0.2s linear"}),X.makeFullscreen(this.backgroundElement),this.backgroundElement.style.position="fixed",this.domElement=document.createElement("div"),S.extend(this.domElement.style,{position:"fixed",display:"none",zIndex:"1001",opacity:0,WebkitTransition:"-webkit-transform 0.2s ease-out, opacity 0.2s linear",transition:"transform 0.2s ease-out, opacity 0.2s linear"}),document.body.appendChild(this.backgroundElement),document.body.appendChild(this.domElement);var t=this;X.bind(this.backgroundElement,"click",function(){t.hide()})}return P(e,[{key:"show",value:function(){var e=this;this.backgroundElement.style.display="block",this.domElement.style.display="block",this.domElement.style.opacity=0,this.domElement.style.webkitTransform="scale(1.1)",this.layout(),S.defer(function(){e.backgroundElement.style.opacity=1,e.domElement.style.opacity=1,e.domElement.style.webkitTransform="scale(1)"})}},{key:"hide",value:function(){var e=this,t=function t(){e.domElement.style.display="none",e.backgroundElement.style.display="none",X.unbind(e.domElement,"webkitTransitionEnd",t),X.unbind(e.domElement,"transitionend",t),X.unbind(e.domElement,"oTransitionEnd",t)};X.bind(this.domElement,"webkitTransitionEnd",t),X.bind(this.domElement,"transitionend",t),X.bind(this.domElement,"oTransitionEnd",t),this.backgroundElement.style.opacity=0,this.domElement.style.opacity=0,this.domElement.style.webkitTransform="scale(1.1)"}},{key:"layout",value:function(){this.domElement.style.left=window.innerWidth/2-X.getWidth(this.domElement)/2+"px",this.domElement.style.top=window.innerHeight/2-X.getHeight(this.domElement)/2+"px"}}]),e}(),re=function(e){if(e&&"undefined"!=typeof window){var t=document.createElement("style");return t.setAttribute("type","text/css"),t.innerHTML=e,document.head.appendChild(t),e}}(".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear;border:0;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button.close-top{position:relative}.dg.main .close-button.close-bottom{position:absolute}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-y:visible}.dg.a.has-save>ul.close-top{margin-top:0}.dg.a.has-save>ul.close-bottom{margin-top:27px}.dg.a.has-save>ul.closed{margin-top:0}.dg.a .save-row{top:0;z-index:1002}.dg.a .save-row.close-top{position:relative}.dg.a .save-row.close-bottom{position:fixed}.dg li{-webkit-transition:height .1s ease-out;-o-transition:height .1s ease-out;-moz-transition:height .1s ease-out;transition:height .1s ease-out;-webkit-transition:overflow .1s linear;-o-transition:overflow .1s linear;-moz-transition:overflow .1s linear;transition:overflow .1s linear}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li>*{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px;overflow:hidden}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .cr.function .property-name{width:100%}.dg .c{float:left;width:60%;position:relative}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:7px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .cr.color{overflow:visible}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.color{border-left:3px solid}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2FA1D6}.dg .cr.number input[type=text]{color:#2FA1D6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2FA1D6;max-width:100%}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n");te.inject(re);var se="Default",ae=function(){try{return!!window.localStorage}catch(e){return!1}}(),le=void 0,de=!0,ce=void 0,ue=!1,_e=[],he=function e(t){var n=this,o=t||{};this.domElement=document.createElement("div"),this.__ul=document.createElement("ul"),this.domElement.appendChild(this.__ul),X.addClass(this.domElement,"dg"),this.__folders={},this.__controllers=[],this.__rememberedObjects=[],this.__rememberedObjectIndecesToControllers=[],this.__listening=[],o=S.defaults(o,{closeOnTop:!1,autoPlace:!0,width:e.DEFAULT_WIDTH}),o=S.defaults(o,{resizable:o.autoPlace,hideable:o.autoPlace}),S.isUndefined(o.load)?o.load={preset:se}:o.preset&&(o.load.preset=o.preset),S.isUndefined(o.parent)&&o.hideable&&_e.push(this),o.resizable=S.isUndefined(o.parent)&&o.resizable,o.autoPlace&&S.isUndefined(o.scrollable)&&(o.scrollable=!0);var i=ae&&"true"===localStorage.getItem(m(this,"isLocal")),r=void 0,s=void 0;if(Object.defineProperties(this,{parent:{get:function(){return o.parent}},scrollable:{get:function(){return o.scrollable}},autoPlace:{get:function(){return o.autoPlace}},closeOnTop:{get:function(){return o.closeOnTop}},preset:{get:function(){return n.parent?n.getRoot().preset:o.load.preset},set:function(e){n.parent?n.getRoot().preset=e:o.load.preset=e,E(this),n.revert()}},width:{get:function(){return o.width},set:function(e){o.width=e,w(n,e)}},name:{get:function(){return o.name},set:function(e){o.name=e,s&&(s.innerHTML=o.name)}},closed:{get:function(){return o.closed},set:function(t){o.closed=t,o.closed?X.addClass(n.__ul,e.CLASS_CLOSED):X.removeClass(n.__ul,e.CLASS_CLOSED),this.onResize(),n.__closeButton&&(n.__closeButton.innerHTML=t?e.TEXT_OPEN:e.TEXT_CLOSED)}},load:{get:function(){return o.load}},useLocalStorage:{get:function(){return i},set:function(e){ae&&(i=e,e?X.bind(window,"unload",r):X.unbind(window,"unload",r),localStorage.setItem(m(n,"isLocal"),e))}}}),S.isUndefined(o.parent)){if(this.closed=o.closed||!1,X.addClass(this.domElement,e.CLASS_MAIN),X.makeSelectable(this.domElement,!1),ae&&i){n.useLocalStorage=!0;var a=localStorage.getItem(m(this,"gui"));a&&(o.load=JSON.parse(a))}this.__closeButton=document.createElement("div"),this.__closeButton.innerHTML=e.TEXT_CLOSED,X.addClass(this.__closeButton,e.CLASS_CLOSE_BUTTON),o.closeOnTop?(X.addClass(this.__closeButton,e.CLASS_CLOSE_TOP),this.domElement.insertBefore(this.__closeButton,this.domElement.childNodes[0])):(X.addClass(this.__closeButton,e.CLASS_CLOSE_BOTTOM),this.domElement.appendChild(this.__closeButton)),X.bind(this.__closeButton,"click",function(){n.closed=!n.closed})}else{void 0===o.closed&&(o.closed=!0);var l=document.createTextNode(o.name);X.addClass(l,"controller-name"),s=c(n,l);X.addClass(this.__ul,e.CLASS_CLOSED),X.addClass(s,"title"),X.bind(s,"click",function(e){return e.preventDefault(),n.closed=!n.closed,!1}),o.closed||(this.closed=!1)}o.autoPlace&&(S.isUndefined(o.parent)&&(de&&(ce=document.createElement("div"),X.addClass(ce,"dg"),X.addClass(ce,e.CLASS_AUTO_PLACE_CONTAINER),document.body.appendChild(ce),de=!1),ce.appendChild(this.domElement),X.addClass(this.domElement,e.CLASS_AUTO_PLACE)),this.parent||w(n,o.width)),this.__resizeHandler=function(){n.onResizeDebounced()},X.bind(window,"resize",this.__resizeHandler),X.bind(this.__ul,"webkitTransitionEnd",this.__resizeHandler),X.bind(this.__ul,"transitionend",this.__resizeHandler),X.bind(this.__ul,"oTransitionEnd",this.__resizeHandler),this.onResize(),o.resizable&&y(this),r=function(){ae&&"true"===localStorage.getItem(m(n,"isLocal"))&&localStorage.setItem(m(n,"gui"),JSON.stringify(n.getSaveObject()))},this.saveToLocalStorageIfPossible=r,o.parent||function(){var e=n.getRoot();e.width+=1,S.defer(function(){e.width-=1})}()};he.toggleHide=function(){ue=!ue,S.each(_e,function(e){e.domElement.style.display=ue?"none":""})},he.CLASS_AUTO_PLACE="a",he.CLASS_AUTO_PLACE_CONTAINER="ac",he.CLASS_MAIN="main",he.CLASS_CONTROLLER_ROW="cr",he.CLASS_TOO_TALL="taller-than-window",he.CLASS_CLOSED="closed",he.CLASS_CLOSE_BUTTON="close-button",he.CLASS_CLOSE_TOP="close-top",he.CLASS_CLOSE_BOTTOM="close-bottom",he.CLASS_DRAG="drag",he.DEFAULT_WIDTH=245,he.TEXT_CLOSED="Close Controls",he.TEXT_OPEN="Open Controls",he._keydownHandler=function(e){"text"===document.activeElement.type||72!==e.which&&72!==e.keyCode||he.toggleHide()},X.bind(window,"keydown",he._keydownHandler,!1),S.extend(he.prototype,{add:function(e,t){return f(this,e,t,{factoryArgs:Array.prototype.slice.call(arguments,2)})},addColor:function(e,t){return f(this,e,t,{color:!0})},remove:function(e){this.__ul.removeChild(e.__li),this.__controllers.splice(this.__controllers.indexOf(e),1);var t=this;S.defer(function(){t.onResize()})},destroy:function(){if(this.parent)throw new Error("Only the root GUI should be removed with .destroy(). For subfolders, use gui.removeFolder(folder) instead.");this.autoPlace&&ce.removeChild(this.domElement);var e=this;S.each(this.__folders,function(t){e.removeFolder(t)}),X.unbind(window,"keydown",he._keydownHandler,!1),u(this)},addFolder:function(e){if(void 0!==this.__folders[e])throw new Error('You already have a folder in this GUI by the name "'+e+'"');var t={name:e,parent:this};t.autoPlace=this.autoPlace,this.load&&this.load.folders&&this.load.folders[e]&&(t.closed=this.load.folders[e].closed,t.load=this.load.folders[e]);var n=new he(t);this.__folders[e]=n;var o=c(this,n.domElement);return X.addClass(o,"folder"),n},removeFolder:function(e){this.__ul.removeChild(e.domElement.parentElement),delete this.__folders[e.name],this.load&&this.load.folders&&this.load.folders[e.name]&&delete this.load.folders[e.name],u(e);var t=this;S.each(e.__folders,function(t){e.removeFolder(t)}),S.defer(function(){t.onResize()})},open:function(){this.closed=!1},close:function(){this.closed=!0},hide:function(){this.domElement.style.display="none"},show:function(){this.domElement.style.display=""},onResize:function(){var e=this.getRoot();if(e.scrollable){var t=X.getOffset(e.__ul).top,n=0;S.each(e.__ul.childNodes,function(t){e.autoPlace&&t===e.__save_row||(n+=X.getHeight(t))}),window.innerHeight-t-20GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n\n
\n\n
\n\n'),this.parent)throw new Error("You can only call remember on a top level GUI.");var e=this;S.each(Array.prototype.slice.call(arguments),function(t){0===e.__rememberedObjects.length&&v(e),-1===e.__rememberedObjects.indexOf(t)&&e.__rememberedObjects.push(t)}),this.autoPlace&&w(this,this.width)},getRoot:function(){for(var e=this;e.parent;)e=e.parent;return e},getSaveObject:function(){var e=this.load;return e.closed=this.closed,this.__rememberedObjects.length>0&&(e.preset=this.preset,e.remembered||(e.remembered={}),e.remembered[this.preset]=x(this)),e.folders={},S.each(this.__folders,function(t,n){e.folders[n]=t.getSaveObject()}),e},save:function(){this.load.remembered||(this.load.remembered={}),this.load.remembered[this.preset]=x(this),_(this,!1),this.saveToLocalStorageIfPossible()},saveAs:function(e){this.load.remembered||(this.load.remembered={},this.load.remembered[se]=x(this,!0)),this.load.remembered[e]=x(this),this.preset=e,g(this,e,!0),this.saveToLocalStorageIfPossible()},revert:function(e){S.each(this.__controllers,function(t){this.getRoot().load.remembered?p(e||this.getRoot(),t):t.setValue(t.initialValue),t.__onFinishChange&&t.__onFinishChange.call(t,t.getValue())},this),S.each(this.__folders,function(e){e.revert(e)}),e||_(this.getRoot(),!1)},listen:function(e){var t=0===this.__listening.length;this.__listening.push(e),t&&C(this.__listening)},updateDisplay:function(){S.each(this.__controllers,function(e){e.updateDisplay()}),S.each(this.__folders,function(e){e.updateDisplay()})}});var pe={Color:I,math:N,interpret:R},fe={Controller:z,BooleanController:K,OptionController:Y,StringController:J,NumberController:W,NumberControllerBox:Q,NumberControllerSlider:q,FunctionController:Z,ColorController:$},me={dom:X},ge={GUI:he},be=he,ve={color:pe,controllers:fe,dom:me,gui:ge,GUI:be};e.color=pe,e.controllers=fe,e.dom=me,e.gui=ge,e.GUI=be,e.default=ve,Object.defineProperty(e,"__esModule",{value:!0})}); +!(function (e, t) { + 'object' == typeof exports && 'undefined' != typeof module + ? t(exports) + : 'function' == typeof define && define.amd + ? define(['exports'], t) + : t((e.dat = {})); +})(this, function (e) { + 'use strict'; + function t(e, t) { + var n = e.__state.conversionName.toString(), + o = Math.round(e.r), + i = Math.round(e.g), + r = Math.round(e.b), + s = e.a, + a = Math.round(e.h), + l = e.s.toFixed(1), + d = e.v.toFixed(1); + if (t || 'THREE_CHAR_HEX' === n || 'SIX_CHAR_HEX' === n) { + for (var c = e.hex.toString(16); c.length < 6; ) c = '0' + c; + return '#' + c; + } + return 'CSS_RGB' === n + ? 'rgb(' + o + ',' + i + ',' + r + ')' + : 'CSS_RGBA' === n + ? 'rgba(' + o + ',' + i + ',' + r + ',' + s + ')' + : 'HEX' === n + ? '0x' + e.hex.toString(16) + : 'RGB_ARRAY' === n + ? '[' + o + ',' + i + ',' + r + ']' + : 'RGBA_ARRAY' === n + ? '[' + o + ',' + i + ',' + r + ',' + s + ']' + : 'RGB_OBJ' === n + ? '{r:' + o + ',g:' + i + ',b:' + r + '}' + : 'RGBA_OBJ' === n + ? '{r:' + o + ',g:' + i + ',b:' + r + ',a:' + s + '}' + : 'HSV_OBJ' === n + ? '{h:' + a + ',s:' + l + ',v:' + d + '}' + : 'HSVA_OBJ' === n + ? '{h:' + a + ',s:' + l + ',v:' + d + ',a:' + s + '}' + : 'unknown format'; + } + function n(e, t, n) { + Object.defineProperty(e, t, { + get: function () { + return 'RGB' === this.__state.space + ? this.__state[t] + : (I.recalculateRGB(this, t, n), this.__state[t]); + }, + set: function (e) { + 'RGB' !== this.__state.space && + (I.recalculateRGB(this, t, n), (this.__state.space = 'RGB')), + (this.__state[t] = e); + } + }); + } + function o(e, t) { + Object.defineProperty(e, t, { + get: function () { + return 'HSV' === this.__state.space + ? this.__state[t] + : (I.recalculateHSV(this), this.__state[t]); + }, + set: function (e) { + 'HSV' !== this.__state.space && (I.recalculateHSV(this), (this.__state.space = 'HSV')), + (this.__state[t] = e); + } + }); + } + function i(e) { + if ('0' === e || S.isUndefined(e)) return 0; + var t = e.match(U); + return S.isNull(t) ? 0 : parseFloat(t[1]); + } + function r(e) { + var t = e.toString(); + return t.indexOf('.') > -1 ? t.length - t.indexOf('.') - 1 : 0; + } + function s(e, t) { + var n = Math.pow(10, t); + return Math.round(e * n) / n; + } + function a(e, t, n, o, i) { + return o + ((e - t) / (n - t)) * (i - o); + } + function l(e, t, n, o) { + (e.style.background = ''), + S.each(ee, function (i) { + e.style.cssText += + 'background: ' + i + 'linear-gradient(' + t + ', ' + n + ' 0%, ' + o + ' 100%); '; + }); + } + function d(e) { + (e.style.background = ''), + (e.style.cssText += + 'background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);'), + (e.style.cssText += + 'background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'), + (e.style.cssText += + 'background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'), + (e.style.cssText += + 'background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'), + (e.style.cssText += + 'background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'); + } + function c(e, t, n) { + var o = document.createElement('li'); + return ( + t && o.appendChild(t), n ? e.__ul.insertBefore(o, n) : e.__ul.appendChild(o), e.onResize(), o + ); + } + function u(e) { + X.unbind(window, 'resize', e.__resizeHandler), + e.saveToLocalStorageIfPossible && X.unbind(window, 'unload', e.saveToLocalStorageIfPossible); + } + function _(e, t) { + var n = e.__preset_select[e.__preset_select.selectedIndex]; + n.innerHTML = t ? n.value + '*' : n.value; + } + function h(e, t, n) { + if ( + ((n.__li = t), + (n.__gui = e), + S.extend(n, { + options: function (t) { + if (arguments.length > 1) { + var o = n.__li.nextElementSibling; + return ( + n.remove(), + f(e, n.object, n.property, { before: o, factoryArgs: [S.toArray(arguments)] }) + ); + } + if (S.isArray(t) || S.isObject(t)) { + var i = n.__li.nextElementSibling; + return n.remove(), f(e, n.object, n.property, { before: i, factoryArgs: [t] }); + } + }, + name: function (e) { + return (n.__li.firstElementChild.firstElementChild.innerHTML = e), n; + }, + listen: function () { + return n.__gui.listen(n), n; + }, + remove: function () { + return n.__gui.remove(n), n; + } + }), + n instanceof q) + ) { + var o = new Q(n.object, n.property, { min: n.__min, max: n.__max, step: n.__step }); + S.each(['updateDisplay', 'onChange', 'onFinishChange', 'step', 'min', 'max'], function (e) { + var t = n[e], + i = o[e]; + n[e] = o[e] = function () { + var e = Array.prototype.slice.call(arguments); + return i.apply(o, e), t.apply(n, e); + }; + }), + X.addClass(t, 'has-slider'), + n.domElement.insertBefore(o.domElement, n.domElement.firstElementChild); + } else if (n instanceof Q) { + var i = function (t) { + if (S.isNumber(n.__min) && S.isNumber(n.__max)) { + var o = n.__li.firstElementChild.firstElementChild.innerHTML, + i = n.__gui.__listening.indexOf(n) > -1; + n.remove(); + var r = f(e, n.object, n.property, { + before: n.__li.nextElementSibling, + factoryArgs: [n.__min, n.__max, n.__step] + }); + return r.name(o), i && r.listen(), r; + } + return t; + }; + (n.min = S.compose(i, n.min)), (n.max = S.compose(i, n.max)); + } else + n instanceof K + ? (X.bind(t, 'click', function () { + X.fakeEvent(n.__checkbox, 'click'); + }), + X.bind(n.__checkbox, 'click', function (e) { + e.stopPropagation(); + })) + : n instanceof Z + ? (X.bind(t, 'click', function () { + X.fakeEvent(n.__button, 'click'); + }), + X.bind(t, 'mouseover', function () { + X.addClass(n.__button, 'hover'); + }), + X.bind(t, 'mouseout', function () { + X.removeClass(n.__button, 'hover'); + })) + : n instanceof $ && + (X.addClass(t, 'color'), + (n.updateDisplay = S.compose(function (e) { + return (t.style.borderLeftColor = n.__color.toString()), e; + }, n.updateDisplay)), + n.updateDisplay()); + n.setValue = S.compose(function (t) { + return e.getRoot().__preset_select && n.isModified() && _(e.getRoot(), !0), t; + }, n.setValue); + } + function p(e, t) { + var n = e.getRoot(), + o = n.__rememberedObjects.indexOf(t.object); + if (-1 !== o) { + var i = n.__rememberedObjectIndecesToControllers[o]; + if ( + (void 0 === i && ((i = {}), (n.__rememberedObjectIndecesToControllers[o] = i)), + (i[t.property] = t), + n.load && n.load.remembered) + ) { + var r = n.load.remembered, + s = void 0; + if (r[e.preset]) s = r[e.preset]; + else { + if (!r[se]) return; + s = r[se]; + } + if (s[o] && void 0 !== s[o][t.property]) { + var a = s[o][t.property]; + (t.initialValue = a), t.setValue(a); + } + } + } + } + function f(e, t, n, o) { + if (void 0 === t[n]) throw new Error('Object "' + t + '" has no property "' + n + '"'); + var i = void 0; + if (o.color) i = new $(t, n); + else { + var r = [t, n].concat(o.factoryArgs); + i = ne.apply(e, r); + } + o.before instanceof z && (o.before = o.before.__li), p(e, i), X.addClass(i.domElement, 'c'); + var s = document.createElement('span'); + X.addClass(s, 'property-name'), (s.innerHTML = i.property); + var a = document.createElement('div'); + a.appendChild(s), a.appendChild(i.domElement); + var l = c(e, a, o.before); + return ( + X.addClass(l, he.CLASS_CONTROLLER_ROW), + i instanceof $ ? X.addClass(l, 'color') : X.addClass(l, H(i.getValue())), + h(e, l, i), + e.__controllers.push(i), + i + ); + } + function m(e, t) { + return document.location.href + '.' + t; + } + function g(e, t, n) { + var o = document.createElement('option'); + (o.innerHTML = t), + (o.value = t), + e.__preset_select.appendChild(o), + n && (e.__preset_select.selectedIndex = e.__preset_select.length - 1); + } + function b(e, t) { + t.style.display = e.useLocalStorage ? 'block' : 'none'; + } + function v(e) { + var t = (e.__save_row = document.createElement('li')); + X.addClass(e.domElement, 'has-save'), + e.__ul.insertBefore(t, e.__ul.firstChild), + X.addClass(t, 'save-row'); + var n = document.createElement('span'); + (n.innerHTML = ' '), X.addClass(n, 'button gears'); + var o = document.createElement('span'); + (o.innerHTML = 'Save'), X.addClass(o, 'button'), X.addClass(o, 'save'); + var i = document.createElement('span'); + (i.innerHTML = 'New'), X.addClass(i, 'button'), X.addClass(i, 'save-as'); + var r = document.createElement('span'); + (r.innerHTML = 'Revert'), X.addClass(r, 'button'), X.addClass(r, 'revert'); + var s = (e.__preset_select = document.createElement('select')); + if ( + (e.load && e.load.remembered + ? S.each(e.load.remembered, function (t, n) { + g(e, n, n === e.preset); + }) + : g(e, se, !1), + X.bind(s, 'change', function () { + for (var t = 0; t < e.__preset_select.length; t++) + e.__preset_select[t].innerHTML = e.__preset_select[t].value; + e.preset = this.value; + }), + t.appendChild(s), + t.appendChild(n), + t.appendChild(o), + t.appendChild(i), + t.appendChild(r), + ae) + ) { + var a = document.getElementById('dg-local-explain'), + l = document.getElementById('dg-local-storage'); + (document.getElementById('dg-save-locally').style.display = 'block'), + 'true' === localStorage.getItem(m(e, 'isLocal')) && l.setAttribute('checked', 'checked'), + b(e, a), + X.bind(l, 'change', function () { + (e.useLocalStorage = !e.useLocalStorage), b(e, a); + }); + } + var d = document.getElementById('dg-new-constructor'); + X.bind(d, 'keydown', function (e) { + !e.metaKey || (67 !== e.which && 67 !== e.keyCode) || le.hide(); + }), + X.bind(n, 'click', function () { + (d.innerHTML = JSON.stringify(e.getSaveObject(), void 0, 2)), + le.show(), + d.focus(), + d.select(); + }), + X.bind(o, 'click', function () { + e.save(); + }), + X.bind(i, 'click', function () { + var t = prompt('Enter a new preset name.'); + t && e.saveAs(t); + }), + X.bind(r, 'click', function () { + e.revert(); + }); + } + function y(e) { + function t(t) { + return t.preventDefault(), (e.width += i - t.clientX), e.onResize(), (i = t.clientX), !1; + } + function n() { + X.removeClass(e.__closeButton, he.CLASS_DRAG), + X.unbind(window, 'mousemove', t), + X.unbind(window, 'mouseup', n); + } + function o(o) { + return ( + o.preventDefault(), + (i = o.clientX), + X.addClass(e.__closeButton, he.CLASS_DRAG), + X.bind(window, 'mousemove', t), + X.bind(window, 'mouseup', n), + !1 + ); + } + var i = void 0; + (e.__resize_handle = document.createElement('div')), + S.extend(e.__resize_handle.style, { + width: '6px', + marginLeft: '-3px', + height: '200px', + cursor: 'ew-resize', + position: 'absolute' + }), + X.bind(e.__resize_handle, 'mousedown', o), + X.bind(e.__closeButton, 'mousedown', o), + e.domElement.insertBefore(e.__resize_handle, e.domElement.firstElementChild); + } + function w(e, t) { + (e.domElement.style.width = t + 'px'), + e.__save_row && e.autoPlace && (e.__save_row.style.width = t + 'px'), + e.__closeButton && (e.__closeButton.style.width = t + 'px'); + } + function x(e, t) { + var n = {}; + return ( + S.each(e.__rememberedObjects, function (o, i) { + var r = {}, + s = e.__rememberedObjectIndecesToControllers[i]; + S.each(s, function (e, n) { + r[n] = t ? e.initialValue : e.getValue(); + }), + (n[i] = r); + }), + n + ); + } + function E(e) { + for (var t = 0; t < e.__preset_select.length; t++) + e.__preset_select[t].value === e.preset && (e.__preset_select.selectedIndex = t); + } + function C(e) { + 0 !== e.length && + oe.call(window, function () { + C(e); + }), + S.each(e, function (e) { + e.updateDisplay(); + }); + } + var A = Array.prototype.forEach, + k = Array.prototype.slice, + S = { + BREAK: {}, + extend: function (e) { + return ( + this.each( + k.call(arguments, 1), + function (t) { + (this.isObject(t) ? Object.keys(t) : []).forEach( + function (n) { + this.isUndefined(t[n]) || (e[n] = t[n]); + }.bind(this) + ); + }, + this + ), + e + ); + }, + defaults: function (e) { + return ( + this.each( + k.call(arguments, 1), + function (t) { + (this.isObject(t) ? Object.keys(t) : []).forEach( + function (n) { + this.isUndefined(e[n]) && (e[n] = t[n]); + }.bind(this) + ); + }, + this + ), + e + ); + }, + compose: function () { + var e = k.call(arguments); + return function () { + for (var t = k.call(arguments), n = e.length - 1; n >= 0; n--) t = [e[n].apply(this, t)]; + return t[0]; + }; + }, + each: function (e, t, n) { + if (e) + if (A && e.forEach && e.forEach === A) e.forEach(t, n); + else if (e.length === e.length + 0) { + var o = void 0, + i = void 0; + for (o = 0, i = e.length; o < i; o++) + if (o in e && t.call(n, e[o], o) === this.BREAK) return; + } else for (var r in e) if (t.call(n, e[r], r) === this.BREAK) return; + }, + defer: function (e) { + setTimeout(e, 0); + }, + debounce: function (e, t, n) { + var o = void 0; + return function () { + var i = this, + r = arguments, + s = n || !o; + clearTimeout(o), + (o = setTimeout(function () { + (o = null), n || e.apply(i, r); + }, t)), + s && e.apply(i, r); + }; + }, + toArray: function (e) { + return e.toArray ? e.toArray() : k.call(e); + }, + isUndefined: function (e) { + return void 0 === e; + }, + isNull: function (e) { + return null === e; + }, + isNaN: (function (e) { + function t(t) { + return e.apply(this, arguments); + } + return ( + (t.toString = function () { + return e.toString(); + }), + t + ); + })(function (e) { + return isNaN(e); + }), + isArray: + Array.isArray || + function (e) { + return e.constructor === Array; + }, + isObject: function (e) { + return e === Object(e); + }, + isNumber: function (e) { + return e === e + 0; + }, + isString: function (e) { + return e === e + ''; + }, + isBoolean: function (e) { + return !1 === e || !0 === e; + }, + isFunction: function (e) { + return e instanceof Function; + } + }, + O = [ + { + litmus: S.isString, + conversions: { + THREE_CHAR_HEX: { + read: function (e) { + var t = e.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i); + return ( + null !== t && { + space: 'HEX', + hex: parseInt( + '0x' + + t[1].toString() + + t[1].toString() + + t[2].toString() + + t[2].toString() + + t[3].toString() + + t[3].toString(), + 0 + ) + } + ); + }, + write: t + }, + SIX_CHAR_HEX: { + read: function (e) { + var t = e.match(/^#([A-F0-9]{6})$/i); + return null !== t && { space: 'HEX', hex: parseInt('0x' + t[1].toString(), 0) }; + }, + write: t + }, + CSS_RGB: { + read: function (e) { + var t = e.match(/^rgb\(\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*\)/); + return ( + null !== t && { + space: 'RGB', + r: parseFloat(t[1]), + g: parseFloat(t[2]), + b: parseFloat(t[3]) + } + ); + }, + write: t + }, + CSS_RGBA: { + read: function (e) { + var t = e.match(/^rgba\(\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*\)/); + return ( + null !== t && { + space: 'RGB', + r: parseFloat(t[1]), + g: parseFloat(t[2]), + b: parseFloat(t[3]), + a: parseFloat(t[4]) + } + ); + }, + write: t + } + } + }, + { + litmus: S.isNumber, + conversions: { + HEX: { + read: function (e) { + return { space: 'HEX', hex: e, conversionName: 'HEX' }; + }, + write: function (e) { + return e.hex; + } + } + } + }, + { + litmus: S.isArray, + conversions: { + RGB_ARRAY: { + read: function (e) { + return 3 === e.length && { space: 'RGB', r: e[0], g: e[1], b: e[2] }; + }, + write: function (e) { + return [e.r, e.g, e.b]; + } + }, + RGBA_ARRAY: { + read: function (e) { + return 4 === e.length && { space: 'RGB', r: e[0], g: e[1], b: e[2], a: e[3] }; + }, + write: function (e) { + return [e.r, e.g, e.b, e.a]; + } + } + } + }, + { + litmus: S.isObject, + conversions: { + RGBA_OBJ: { + read: function (e) { + return ( + !!(S.isNumber(e.r) && S.isNumber(e.g) && S.isNumber(e.b) && S.isNumber(e.a)) && { + space: 'RGB', + r: e.r, + g: e.g, + b: e.b, + a: e.a + } + ); + }, + write: function (e) { + return { r: e.r, g: e.g, b: e.b, a: e.a }; + } + }, + RGB_OBJ: { + read: function (e) { + return ( + !!(S.isNumber(e.r) && S.isNumber(e.g) && S.isNumber(e.b)) && { + space: 'RGB', + r: e.r, + g: e.g, + b: e.b + } + ); + }, + write: function (e) { + return { r: e.r, g: e.g, b: e.b }; + } + }, + HSVA_OBJ: { + read: function (e) { + return ( + !!(S.isNumber(e.h) && S.isNumber(e.s) && S.isNumber(e.v) && S.isNumber(e.a)) && { + space: 'HSV', + h: e.h, + s: e.s, + v: e.v, + a: e.a + } + ); + }, + write: function (e) { + return { h: e.h, s: e.s, v: e.v, a: e.a }; + } + }, + HSV_OBJ: { + read: function (e) { + return ( + !!(S.isNumber(e.h) && S.isNumber(e.s) && S.isNumber(e.v)) && { + space: 'HSV', + h: e.h, + s: e.s, + v: e.v + } + ); + }, + write: function (e) { + return { h: e.h, s: e.s, v: e.v }; + } + } + } + } + ], + T = void 0, + L = void 0, + R = function () { + L = !1; + var e = arguments.length > 1 ? S.toArray(arguments) : arguments[0]; + return ( + S.each(O, function (t) { + if (t.litmus(e)) + return ( + S.each(t.conversions, function (t, n) { + if (((T = t.read(e)), !1 === L && !1 !== T)) + return (L = T), (T.conversionName = n), (T.conversion = t), S.BREAK; + }), + S.BREAK + ); + }), + L + ); + }, + B = void 0, + N = { + hsv_to_rgb: function (e, t, n) { + var o = Math.floor(e / 60) % 6, + i = e / 60 - Math.floor(e / 60), + r = n * (1 - t), + s = n * (1 - i * t), + a = n * (1 - (1 - i) * t), + l = [ + [n, a, r], + [s, n, r], + [r, n, a], + [r, s, n], + [a, r, n], + [n, r, s] + ][o]; + return { r: 255 * l[0], g: 255 * l[1], b: 255 * l[2] }; + }, + rgb_to_hsv: function (e, t, n) { + var o = Math.min(e, t, n), + i = Math.max(e, t, n), + r = i - o, + s = void 0, + a = void 0; + return 0 === i + ? { h: NaN, s: 0, v: 0 } + : ((a = r / i), + (s = e === i ? (t - n) / r : t === i ? 2 + (n - e) / r : 4 + (e - t) / r), + (s /= 6) < 0 && (s += 1), + { h: 360 * s, s: a, v: i / 255 }); + }, + rgb_to_hex: function (e, t, n) { + var o = this.hex_with_component(0, 2, e); + return (o = this.hex_with_component(o, 1, t)), (o = this.hex_with_component(o, 0, n)); + }, + component_from_hex: function (e, t) { + return (e >> (8 * t)) & 255; + }, + hex_with_component: function (e, t, n) { + return (n << (B = 8 * t)) | (e & ~(255 << B)); + } + }, + H = + 'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator + ? function (e) { + return typeof e; + } + : function (e) { + return e && + 'function' == typeof Symbol && + e.constructor === Symbol && + e !== Symbol.prototype + ? 'symbol' + : typeof e; + }, + F = function (e, t) { + if (!(e instanceof t)) throw new TypeError('Cannot call a class as a function'); + }, + P = (function () { + function e(e, t) { + for (var n = 0; n < t.length; n++) { + var o = t[n]; + (o.enumerable = o.enumerable || !1), + (o.configurable = !0), + 'value' in o && (o.writable = !0), + Object.defineProperty(e, o.key, o); + } + } + return function (t, n, o) { + return n && e(t.prototype, n), o && e(t, o), t; + }; + })(), + D = function e(t, n, o) { + null === t && (t = Function.prototype); + var i = Object.getOwnPropertyDescriptor(t, n); + if (void 0 === i) { + var r = Object.getPrototypeOf(t); + return null === r ? void 0 : e(r, n, o); + } + if ('value' in i) return i.value; + var s = i.get; + if (void 0 !== s) return s.call(o); + }, + j = function (e, t) { + if ('function' != typeof t && null !== t) + throw new TypeError('Super expression must either be null or a function, not ' + typeof t); + (e.prototype = Object.create(t && t.prototype, { + constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } + })), + t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : (e.__proto__ = t)); + }, + V = function (e, t) { + if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + return !t || ('object' != typeof t && 'function' != typeof t) ? e : t; + }, + I = (function () { + function e() { + if ((F(this, e), (this.__state = R.apply(this, arguments)), !1 === this.__state)) + throw new Error('Failed to interpret color arguments'); + this.__state.a = this.__state.a || 1; + } + return ( + P(e, [ + { + key: 'toString', + value: function () { + return t(this); + } + }, + { + key: 'toHexString', + value: function () { + return t(this, !0); + } + }, + { + key: 'toOriginal', + value: function () { + return this.__state.conversion.write(this); + } + } + ]), + e + ); + })(); + (I.recalculateRGB = function (e, t, n) { + if ('HEX' === e.__state.space) e.__state[t] = N.component_from_hex(e.__state.hex, n); + else { + if ('HSV' !== e.__state.space) throw new Error('Corrupted color state'); + S.extend(e.__state, N.hsv_to_rgb(e.__state.h, e.__state.s, e.__state.v)); + } + }), + (I.recalculateHSV = function (e) { + var t = N.rgb_to_hsv(e.r, e.g, e.b); + S.extend(e.__state, { s: t.s, v: t.v }), + S.isNaN(t.h) ? S.isUndefined(e.__state.h) && (e.__state.h = 0) : (e.__state.h = t.h); + }), + (I.COMPONENTS = ['r', 'g', 'b', 'h', 's', 'v', 'hex', 'a']), + n(I.prototype, 'r', 2), + n(I.prototype, 'g', 1), + n(I.prototype, 'b', 0), + o(I.prototype, 'h'), + o(I.prototype, 's'), + o(I.prototype, 'v'), + Object.defineProperty(I.prototype, 'a', { + get: function () { + return this.__state.a; + }, + set: function (e) { + this.__state.a = e; + } + }), + Object.defineProperty(I.prototype, 'hex', { + get: function () { + return ( + 'HEX' !== this.__state.space && + ((this.__state.hex = N.rgb_to_hex(this.r, this.g, this.b)), + (this.__state.space = 'HEX')), + this.__state.hex + ); + }, + set: function (e) { + (this.__state.space = 'HEX'), (this.__state.hex = e); + } + }); + var z = (function () { + function e(t, n) { + F(this, e), + (this.initialValue = t[n]), + (this.domElement = document.createElement('div')), + (this.object = t), + (this.property = n), + (this.__onChange = void 0), + (this.__onFinishChange = void 0); + } + return ( + P(e, [ + { + key: 'onChange', + value: function (e) { + return (this.__onChange = e), this; + } + }, + { + key: 'onFinishChange', + value: function (e) { + return (this.__onFinishChange = e), this; + } + }, + { + key: 'setValue', + value: function (e) { + return ( + (this.object[this.property] = e), + this.__onChange && this.__onChange.call(this, e), + this.updateDisplay(), + this + ); + } + }, + { + key: 'getValue', + value: function () { + return this.object[this.property]; + } + }, + { + key: 'updateDisplay', + value: function () { + return this; + } + }, + { + key: 'isModified', + value: function () { + return this.initialValue !== this.getValue(); + } + } + ]), + e + ); + })(), + M = { + HTMLEvents: ['change'], + MouseEvents: ['click', 'mousemove', 'mousedown', 'mouseup', 'mouseover'], + KeyboardEvents: ['keydown'] + }, + G = {}; + S.each(M, function (e, t) { + S.each(e, function (e) { + G[e] = t; + }); + }); + var U = /(\d+(\.\d+)?)px/, + X = { + makeSelectable: function (e, t) { + void 0 !== e && + void 0 !== e.style && + ((e.onselectstart = t + ? function () { + return !1; + } + : function () {}), + (e.style.MozUserSelect = t ? 'auto' : 'none'), + (e.style.KhtmlUserSelect = t ? 'auto' : 'none'), + (e.unselectable = t ? 'on' : 'off')); + }, + makeFullscreen: function (e, t, n) { + var o = n, + i = t; + S.isUndefined(i) && (i = !0), + S.isUndefined(o) && (o = !0), + (e.style.position = 'absolute'), + i && ((e.style.left = 0), (e.style.right = 0)), + o && ((e.style.top = 0), (e.style.bottom = 0)); + }, + fakeEvent: function (e, t, n, o) { + var i = n || {}, + r = G[t]; + if (!r) throw new Error('Event type ' + t + ' not supported.'); + var s = document.createEvent(r); + switch (r) { + case 'MouseEvents': + var a = i.x || i.clientX || 0, + l = i.y || i.clientY || 0; + s.initMouseEvent( + t, + i.bubbles || !1, + i.cancelable || !0, + window, + i.clickCount || 1, + 0, + 0, + a, + l, + !1, + !1, + !1, + !1, + 0, + null + ); + break; + case 'KeyboardEvents': + var d = s.initKeyboardEvent || s.initKeyEvent; + S.defaults(i, { + cancelable: !0, + ctrlKey: !1, + altKey: !1, + shiftKey: !1, + metaKey: !1, + keyCode: void 0, + charCode: void 0 + }), + d( + t, + i.bubbles || !1, + i.cancelable, + window, + i.ctrlKey, + i.altKey, + i.shiftKey, + i.metaKey, + i.keyCode, + i.charCode + ); + break; + default: + s.initEvent(t, i.bubbles || !1, i.cancelable || !0); + } + S.defaults(s, o), e.dispatchEvent(s); + }, + bind: function (e, t, n, o) { + var i = o || !1; + return ( + e.addEventListener + ? e.addEventListener(t, n, i) + : e.attachEvent && e.attachEvent('on' + t, n), + X + ); + }, + unbind: function (e, t, n, o) { + var i = o || !1; + return ( + e.removeEventListener + ? e.removeEventListener(t, n, i) + : e.detachEvent && e.detachEvent('on' + t, n), + X + ); + }, + addClass: function (e, t) { + if (void 0 === e.className) e.className = t; + else if (e.className !== t) { + var n = e.className.split(/ +/); + -1 === n.indexOf(t) && + (n.push(t), (e.className = n.join(' ').replace(/^\s+/, '').replace(/\s+$/, ''))); + } + return X; + }, + removeClass: function (e, t) { + if (t) + if (e.className === t) e.removeAttribute('class'); + else { + var n = e.className.split(/ +/), + o = n.indexOf(t); + -1 !== o && (n.splice(o, 1), (e.className = n.join(' '))); + } + else e.className = void 0; + return X; + }, + hasClass: function (e, t) { + return new RegExp('(?:^|\\s+)' + t + '(?:\\s+|$)').test(e.className) || !1; + }, + getWidth: function (e) { + var t = getComputedStyle(e); + return ( + i(t['border-left-width']) + + i(t['border-right-width']) + + i(t['padding-left']) + + i(t['padding-right']) + + i(t.width) + ); + }, + getHeight: function (e) { + var t = getComputedStyle(e); + return ( + i(t['border-top-width']) + + i(t['border-bottom-width']) + + i(t['padding-top']) + + i(t['padding-bottom']) + + i(t.height) + ); + }, + getOffset: function (e) { + var t = e, + n = { left: 0, top: 0 }; + if (t.offsetParent) + do { + (n.left += t.offsetLeft), (n.top += t.offsetTop), (t = t.offsetParent); + } while (t); + return n; + }, + isActive: function (e) { + return e === document.activeElement && (e.type || e.href); + } + }, + K = (function (e) { + function t(e, n) { + F(this, t); + var o = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), + i = o; + return ( + (o.__prev = o.getValue()), + (o.__checkbox = document.createElement('input')), + o.__checkbox.setAttribute('type', 'checkbox'), + X.bind( + o.__checkbox, + 'change', + function () { + i.setValue(!i.__prev); + }, + !1 + ), + o.domElement.appendChild(o.__checkbox), + o.updateDisplay(), + o + ); + } + return ( + j(t, z), + P(t, [ + { + key: 'setValue', + value: function (e) { + var n = D( + t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), + 'setValue', + this + ).call(this, e); + return ( + this.__onFinishChange && this.__onFinishChange.call(this, this.getValue()), + (this.__prev = this.getValue()), + n + ); + } + }, + { + key: 'updateDisplay', + value: function () { + return ( + !0 === this.getValue() + ? (this.__checkbox.setAttribute('checked', 'checked'), + (this.__checkbox.checked = !0), + (this.__prev = !0)) + : ((this.__checkbox.checked = !1), (this.__prev = !1)), + D( + t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), + 'updateDisplay', + this + ).call(this) + ); + } + } + ]), + t + ); + })(), + Y = (function (e) { + function t(e, n, o) { + F(this, t); + var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), + r = o, + s = i; + if (((i.__select = document.createElement('select')), S.isArray(r))) { + var a = {}; + S.each(r, function (e) { + a[e] = e; + }), + (r = a); + } + return ( + S.each(r, function (e, t) { + var n = document.createElement('option'); + (n.innerHTML = t), n.setAttribute('value', e), s.__select.appendChild(n); + }), + i.updateDisplay(), + X.bind(i.__select, 'change', function () { + var e = this.options[this.selectedIndex].value; + s.setValue(e); + }), + i.domElement.appendChild(i.__select), + i + ); + } + return ( + j(t, z), + P(t, [ + { + key: 'setValue', + value: function (e) { + var n = D( + t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), + 'setValue', + this + ).call(this, e); + return this.__onFinishChange && this.__onFinishChange.call(this, this.getValue()), n; + } + }, + { + key: 'updateDisplay', + value: function () { + return X.isActive(this.__select) + ? this + : ((this.__select.value = this.getValue()), + D( + t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), + 'updateDisplay', + this + ).call(this)); + } + } + ]), + t + ); + })(), + J = (function (e) { + function t(e, n) { + function o() { + r.setValue(r.__input.value); + } + F(this, t); + var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), + r = i; + return ( + (i.__input = document.createElement('input')), + i.__input.setAttribute('type', 'text'), + X.bind(i.__input, 'keyup', o), + X.bind(i.__input, 'change', o), + X.bind(i.__input, 'blur', function () { + r.__onFinishChange && r.__onFinishChange.call(r, r.getValue()); + }), + X.bind(i.__input, 'keydown', function (e) { + 13 === e.keyCode && this.blur(); + }), + i.updateDisplay(), + i.domElement.appendChild(i.__input), + i + ); + } + return ( + j(t, z), + P(t, [ + { + key: 'updateDisplay', + value: function () { + return ( + X.isActive(this.__input) || (this.__input.value = this.getValue()), + D( + t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), + 'updateDisplay', + this + ).call(this) + ); + } + } + ]), + t + ); + })(), + W = (function (e) { + function t(e, n, o) { + F(this, t); + var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), + s = o || {}; + return ( + (i.__min = s.min), + (i.__max = s.max), + (i.__step = s.step), + S.isUndefined(i.__step) + ? 0 === i.initialValue + ? (i.__impliedStep = 1) + : (i.__impliedStep = + Math.pow(10, Math.floor(Math.log(Math.abs(i.initialValue)) / Math.LN10)) / 10) + : (i.__impliedStep = i.__step), + (i.__precision = r(i.__impliedStep)), + i + ); + } + return ( + j(t, z), + P(t, [ + { + key: 'setValue', + value: function (e) { + var n = e; + return ( + void 0 !== this.__min && n < this.__min + ? (n = this.__min) + : void 0 !== this.__max && n > this.__max && (n = this.__max), + void 0 !== this.__step && + n % this.__step != 0 && + (n = Math.round(n / this.__step) * this.__step), + D( + t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), + 'setValue', + this + ).call(this, n) + ); + } + }, + { + key: 'min', + value: function (e) { + return (this.__min = e), this; + } + }, + { + key: 'max', + value: function (e) { + return (this.__max = e), this; + } + }, + { + key: 'step', + value: function (e) { + return (this.__step = e), (this.__impliedStep = e), (this.__precision = r(e)), this; + } + } + ]), + t + ); + })(), + Q = (function (e) { + function t(e, n, o) { + function i() { + l.__onFinishChange && l.__onFinishChange.call(l, l.getValue()); + } + function r(e) { + var t = d - e.clientY; + l.setValue(l.getValue() + t * l.__impliedStep), (d = e.clientY); + } + function s() { + X.unbind(window, 'mousemove', r), X.unbind(window, 'mouseup', s), i(); + } + F(this, t); + var a = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n, o)); + a.__truncationSuspended = !1; + var l = a, + d = void 0; + return ( + (a.__input = document.createElement('input')), + a.__input.setAttribute('type', 'text'), + X.bind(a.__input, 'change', function () { + var e = parseFloat(l.__input.value); + S.isNaN(e) || l.setValue(e); + }), + X.bind(a.__input, 'blur', function () { + i(); + }), + X.bind(a.__input, 'mousedown', function (e) { + X.bind(window, 'mousemove', r), X.bind(window, 'mouseup', s), (d = e.clientY); + }), + X.bind(a.__input, 'keydown', function (e) { + 13 === e.keyCode && + ((l.__truncationSuspended = !0), this.blur(), (l.__truncationSuspended = !1), i()); + }), + a.updateDisplay(), + a.domElement.appendChild(a.__input), + a + ); + } + return ( + j(t, W), + P(t, [ + { + key: 'updateDisplay', + value: function () { + return ( + (this.__input.value = this.__truncationSuspended + ? this.getValue() + : s(this.getValue(), this.__precision)), + D( + t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), + 'updateDisplay', + this + ).call(this) + ); + } + } + ]), + t + ); + })(), + q = (function (e) { + function t(e, n, o, i, r) { + function s(e) { + e.preventDefault(); + var t = _.__background.getBoundingClientRect(); + return _.setValue(a(e.clientX, t.left, t.right, _.__min, _.__max)), !1; + } + function l() { + X.unbind(window, 'mousemove', s), + X.unbind(window, 'mouseup', l), + _.__onFinishChange && _.__onFinishChange.call(_, _.getValue()); + } + function d(e) { + var t = e.touches[0].clientX, + n = _.__background.getBoundingClientRect(); + _.setValue(a(t, n.left, n.right, _.__min, _.__max)); + } + function c() { + X.unbind(window, 'touchmove', d), + X.unbind(window, 'touchend', c), + _.__onFinishChange && _.__onFinishChange.call(_, _.getValue()); + } + F(this, t); + var u = V( + this, + (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n, { min: o, max: i, step: r }) + ), + _ = u; + return ( + (u.__background = document.createElement('div')), + (u.__foreground = document.createElement('div')), + X.bind(u.__background, 'mousedown', function (e) { + document.activeElement.blur(), + X.bind(window, 'mousemove', s), + X.bind(window, 'mouseup', l), + s(e); + }), + X.bind(u.__background, 'touchstart', function (e) { + 1 === e.touches.length && + (X.bind(window, 'touchmove', d), X.bind(window, 'touchend', c), d(e)); + }), + X.addClass(u.__background, 'slider'), + X.addClass(u.__foreground, 'slider-fg'), + u.updateDisplay(), + u.__background.appendChild(u.__foreground), + u.domElement.appendChild(u.__background), + u + ); + } + return ( + j(t, W), + P(t, [ + { + key: 'updateDisplay', + value: function () { + var e = (this.getValue() - this.__min) / (this.__max - this.__min); + return ( + (this.__foreground.style.width = 100 * e + '%'), + D( + t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), + 'updateDisplay', + this + ).call(this) + ); + } + } + ]), + t + ); + })(), + Z = (function (e) { + function t(e, n, o) { + F(this, t); + var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), + r = i; + return ( + (i.__button = document.createElement('div')), + (i.__button.innerHTML = void 0 === o ? 'Fire' : o), + X.bind(i.__button, 'click', function (e) { + return e.preventDefault(), r.fire(), !1; + }), + X.addClass(i.__button, 'button'), + i.domElement.appendChild(i.__button), + i + ); + } + return ( + j(t, z), + P(t, [ + { + key: 'fire', + value: function () { + this.__onChange && this.__onChange.call(this), + this.getValue().call(this.object), + this.__onFinishChange && this.__onFinishChange.call(this, this.getValue()); + } + } + ]), + t + ); + })(), + $ = (function (e) { + function t(e, n) { + function o(e) { + u(e), + X.bind(window, 'mousemove', u), + X.bind(window, 'touchmove', u), + X.bind(window, 'mouseup', r), + X.bind(window, 'touchend', r); + } + function i(e) { + _(e), + X.bind(window, 'mousemove', _), + X.bind(window, 'touchmove', _), + X.bind(window, 'mouseup', s), + X.bind(window, 'touchend', s); + } + function r() { + X.unbind(window, 'mousemove', u), + X.unbind(window, 'touchmove', u), + X.unbind(window, 'mouseup', r), + X.unbind(window, 'touchend', r), + c(); + } + function s() { + X.unbind(window, 'mousemove', _), + X.unbind(window, 'touchmove', _), + X.unbind(window, 'mouseup', s), + X.unbind(window, 'touchend', s), + c(); + } + function a() { + var e = R(this.value); + !1 !== e + ? ((p.__color.__state = e), p.setValue(p.__color.toOriginal())) + : (this.value = p.__color.toString()); + } + function c() { + p.__onFinishChange && p.__onFinishChange.call(p, p.__color.toOriginal()); + } + function u(e) { + -1 === e.type.indexOf('touch') && e.preventDefault(); + var t = p.__saturation_field.getBoundingClientRect(), + n = (e.touches && e.touches[0]) || e, + o = n.clientX, + i = n.clientY, + r = (o - t.left) / (t.right - t.left), + s = 1 - (i - t.top) / (t.bottom - t.top); + return ( + s > 1 ? (s = 1) : s < 0 && (s = 0), + r > 1 ? (r = 1) : r < 0 && (r = 0), + (p.__color.v = s), + (p.__color.s = r), + p.setValue(p.__color.toOriginal()), + !1 + ); + } + function _(e) { + -1 === e.type.indexOf('touch') && e.preventDefault(); + var t = p.__hue_field.getBoundingClientRect(), + n = 1 - (((e.touches && e.touches[0]) || e).clientY - t.top) / (t.bottom - t.top); + return ( + n > 1 ? (n = 1) : n < 0 && (n = 0), + (p.__color.h = 360 * n), + p.setValue(p.__color.toOriginal()), + !1 + ); + } + F(this, t); + var h = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)); + (h.__color = new I(h.getValue())), (h.__temp = new I(0)); + var p = h; + (h.domElement = document.createElement('div')), + X.makeSelectable(h.domElement, !1), + (h.__selector = document.createElement('div')), + (h.__selector.className = 'selector'), + (h.__saturation_field = document.createElement('div')), + (h.__saturation_field.className = 'saturation-field'), + (h.__field_knob = document.createElement('div')), + (h.__field_knob.className = 'field-knob'), + (h.__field_knob_border = '2px solid '), + (h.__hue_knob = document.createElement('div')), + (h.__hue_knob.className = 'hue-knob'), + (h.__hue_field = document.createElement('div')), + (h.__hue_field.className = 'hue-field'), + (h.__input = document.createElement('input')), + (h.__input.type = 'text'), + (h.__input_textShadow = '0 1px 1px '), + X.bind(h.__input, 'keydown', function (e) { + 13 === e.keyCode && a.call(this); + }), + X.bind(h.__input, 'blur', a), + X.bind(h.__selector, 'mousedown', function () { + X.addClass(this, 'drag').bind(window, 'mouseup', function () { + X.removeClass(p.__selector, 'drag'); + }); + }), + X.bind(h.__selector, 'touchstart', function () { + X.addClass(this, 'drag').bind(window, 'touchend', function () { + X.removeClass(p.__selector, 'drag'); + }); + }); + var f = document.createElement('div'); + return ( + S.extend(h.__selector.style, { + width: '122px', + height: '102px', + padding: '3px', + backgroundColor: '#222', + boxShadow: '0px 1px 3px rgba(0,0,0,0.3)' + }), + S.extend(h.__field_knob.style, { + position: 'absolute', + width: '12px', + height: '12px', + border: h.__field_knob_border + (h.__color.v < 0.5 ? '#fff' : '#000'), + boxShadow: '0px 1px 3px rgba(0,0,0,0.5)', + borderRadius: '12px', + zIndex: 1 + }), + S.extend(h.__hue_knob.style, { + position: 'absolute', + width: '15px', + height: '2px', + borderRight: '4px solid #fff', + zIndex: 1 + }), + S.extend(h.__saturation_field.style, { + width: '100px', + height: '100px', + border: '1px solid #555', + marginRight: '3px', + display: 'inline-block', + cursor: 'pointer' + }), + S.extend(f.style, { width: '100%', height: '100%', background: 'none' }), + l(f, 'top', 'rgba(0,0,0,0)', '#000'), + S.extend(h.__hue_field.style, { + width: '15px', + height: '100px', + border: '1px solid #555', + cursor: 'ns-resize', + position: 'absolute', + top: '3px', + right: '3px' + }), + d(h.__hue_field), + S.extend(h.__input.style, { + outline: 'none', + textAlign: 'center', + color: '#fff', + border: 0, + fontWeight: 'bold', + textShadow: h.__input_textShadow + 'rgba(0,0,0,0.7)' + }), + X.bind(h.__saturation_field, 'mousedown', o), + X.bind(h.__saturation_field, 'touchstart', o), + X.bind(h.__field_knob, 'mousedown', o), + X.bind(h.__field_knob, 'touchstart', o), + X.bind(h.__hue_field, 'mousedown', i), + X.bind(h.__hue_field, 'touchstart', i), + h.__saturation_field.appendChild(f), + h.__selector.appendChild(h.__field_knob), + h.__selector.appendChild(h.__saturation_field), + h.__selector.appendChild(h.__hue_field), + h.__hue_field.appendChild(h.__hue_knob), + h.domElement.appendChild(h.__input), + h.domElement.appendChild(h.__selector), + h.updateDisplay(), + h + ); + } + return ( + j(t, z), + P(t, [ + { + key: 'updateDisplay', + value: function () { + var e = R(this.getValue()); + if (!1 !== e) { + var t = !1; + S.each( + I.COMPONENTS, + function (n) { + if ( + !S.isUndefined(e[n]) && + !S.isUndefined(this.__color.__state[n]) && + e[n] !== this.__color.__state[n] + ) + return (t = !0), {}; + }, + this + ), + t && S.extend(this.__color.__state, e); + } + S.extend(this.__temp.__state, this.__color.__state), (this.__temp.a = 1); + var n = this.__color.v < 0.5 || this.__color.s > 0.5 ? 255 : 0, + o = 255 - n; + S.extend(this.__field_knob.style, { + marginLeft: 100 * this.__color.s - 7 + 'px', + marginTop: 100 * (1 - this.__color.v) - 7 + 'px', + backgroundColor: this.__temp.toHexString(), + border: this.__field_knob_border + 'rgb(' + n + ',' + n + ',' + n + ')' + }), + (this.__hue_knob.style.marginTop = 100 * (1 - this.__color.h / 360) + 'px'), + (this.__temp.s = 1), + (this.__temp.v = 1), + l(this.__saturation_field, 'left', '#fff', this.__temp.toHexString()), + (this.__input.value = this.__color.toString()), + S.extend(this.__input.style, { + backgroundColor: this.__color.toHexString(), + color: 'rgb(' + n + ',' + n + ',' + n + ')', + textShadow: this.__input_textShadow + 'rgba(' + o + ',' + o + ',' + o + ',.7)' + }); + } + } + ]), + t + ); + })(), + ee = ['-moz-', '-o-', '-webkit-', '-ms-', ''], + te = { + load: function (e, t) { + var n = t || document, + o = n.createElement('link'); + (o.type = 'text/css'), + (o.rel = 'stylesheet'), + (o.href = e), + n.getElementsByTagName('head')[0].appendChild(o); + }, + inject: function (e, t) { + var n = t || document, + o = document.createElement('style'); + (o.type = 'text/css'), (o.innerHTML = e); + var i = n.getElementsByTagName('head')[0]; + try { + i.appendChild(o); + } catch (e) {} + } + }, + ne = function (e, t) { + var n = e[t]; + return S.isArray(arguments[2]) || S.isObject(arguments[2]) + ? new Y(e, t, arguments[2]) + : S.isNumber(n) + ? S.isNumber(arguments[2]) && S.isNumber(arguments[3]) + ? S.isNumber(arguments[4]) + ? new q(e, t, arguments[2], arguments[3], arguments[4]) + : new q(e, t, arguments[2], arguments[3]) + : S.isNumber(arguments[4]) + ? new Q(e, t, { min: arguments[2], max: arguments[3], step: arguments[4] }) + : new Q(e, t, { min: arguments[2], max: arguments[3] }) + : S.isString(n) + ? new J(e, t) + : S.isFunction(n) + ? new Z(e, t, '') + : S.isBoolean(n) + ? new K(e, t) + : null; + }, + oe = + window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (e) { + setTimeout(e, 1e3 / 60); + }, + ie = (function () { + function e() { + F(this, e), + (this.backgroundElement = document.createElement('div')), + S.extend(this.backgroundElement.style, { + backgroundColor: 'rgba(0,0,0,0.8)', + top: 0, + left: 0, + display: 'none', + zIndex: '1000', + opacity: 0, + WebkitTransition: 'opacity 0.2s linear', + transition: 'opacity 0.2s linear' + }), + X.makeFullscreen(this.backgroundElement), + (this.backgroundElement.style.position = 'fixed'), + (this.domElement = document.createElement('div')), + S.extend(this.domElement.style, { + position: 'fixed', + display: 'none', + zIndex: '1001', + opacity: 0, + WebkitTransition: '-webkit-transform 0.2s ease-out, opacity 0.2s linear', + transition: 'transform 0.2s ease-out, opacity 0.2s linear' + }), + document.body.appendChild(this.backgroundElement), + document.body.appendChild(this.domElement); + var t = this; + X.bind(this.backgroundElement, 'click', function () { + t.hide(); + }); + } + return ( + P(e, [ + { + key: 'show', + value: function () { + var e = this; + (this.backgroundElement.style.display = 'block'), + (this.domElement.style.display = 'block'), + (this.domElement.style.opacity = 0), + (this.domElement.style.webkitTransform = 'scale(1.1)'), + this.layout(), + S.defer(function () { + (e.backgroundElement.style.opacity = 1), + (e.domElement.style.opacity = 1), + (e.domElement.style.webkitTransform = 'scale(1)'); + }); + } + }, + { + key: 'hide', + value: function () { + var e = this, + t = function t() { + (e.domElement.style.display = 'none'), + (e.backgroundElement.style.display = 'none'), + X.unbind(e.domElement, 'webkitTransitionEnd', t), + X.unbind(e.domElement, 'transitionend', t), + X.unbind(e.domElement, 'oTransitionEnd', t); + }; + X.bind(this.domElement, 'webkitTransitionEnd', t), + X.bind(this.domElement, 'transitionend', t), + X.bind(this.domElement, 'oTransitionEnd', t), + (this.backgroundElement.style.opacity = 0), + (this.domElement.style.opacity = 0), + (this.domElement.style.webkitTransform = 'scale(1.1)'); + } + }, + { + key: 'layout', + value: function () { + (this.domElement.style.left = + window.innerWidth / 2 - X.getWidth(this.domElement) / 2 + 'px'), + (this.domElement.style.top = + window.innerHeight / 2 - X.getHeight(this.domElement) / 2 + 'px'); + } + } + ]), + e + ); + })(), + re = (function (e) { + if (e && 'undefined' != typeof window) { + var t = document.createElement('style'); + return ( + t.setAttribute('type', 'text/css'), (t.innerHTML = e), document.head.appendChild(t), e + ); + } + })( + ".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear;border:0;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button.close-top{position:relative}.dg.main .close-button.close-bottom{position:absolute}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-y:visible}.dg.a.has-save>ul.close-top{margin-top:0}.dg.a.has-save>ul.close-bottom{margin-top:27px}.dg.a.has-save>ul.closed{margin-top:0}.dg.a .save-row{top:0;z-index:1002}.dg.a .save-row.close-top{position:relative}.dg.a .save-row.close-bottom{position:fixed}.dg li{-webkit-transition:height .1s ease-out;-o-transition:height .1s ease-out;-moz-transition:height .1s ease-out;transition:height .1s ease-out;-webkit-transition:overflow .1s linear;-o-transition:overflow .1s linear;-moz-transition:overflow .1s linear;transition:overflow .1s linear}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li>*{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px;overflow:hidden}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .cr.function .property-name{width:100%}.dg .c{float:left;width:60%;position:relative}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:7px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .cr.color{overflow:visible}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.color{border-left:3px solid}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2FA1D6}.dg .cr.number input[type=text]{color:#2FA1D6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2FA1D6;max-width:100%}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n" + ); + te.inject(re); + var se = 'Default', + ae = (function () { + try { + return !!window.localStorage; + } catch (e) { + return !1; + } + })(), + le = void 0, + de = !0, + ce = void 0, + ue = !1, + _e = [], + he = function e(t) { + var n = this, + o = t || {}; + (this.domElement = document.createElement('div')), + (this.__ul = document.createElement('ul')), + this.domElement.appendChild(this.__ul), + X.addClass(this.domElement, 'dg'), + (this.__folders = {}), + (this.__controllers = []), + (this.__rememberedObjects = []), + (this.__rememberedObjectIndecesToControllers = []), + (this.__listening = []), + (o = S.defaults(o, { closeOnTop: !1, autoPlace: !0, width: e.DEFAULT_WIDTH })), + (o = S.defaults(o, { resizable: o.autoPlace, hideable: o.autoPlace })), + S.isUndefined(o.load) ? (o.load = { preset: se }) : o.preset && (o.load.preset = o.preset), + S.isUndefined(o.parent) && o.hideable && _e.push(this), + (o.resizable = S.isUndefined(o.parent) && o.resizable), + o.autoPlace && S.isUndefined(o.scrollable) && (o.scrollable = !0); + var i = ae && 'true' === localStorage.getItem(m(this, 'isLocal')), + r = void 0, + s = void 0; + if ( + (Object.defineProperties(this, { + parent: { + get: function () { + return o.parent; + } + }, + scrollable: { + get: function () { + return o.scrollable; + } + }, + autoPlace: { + get: function () { + return o.autoPlace; + } + }, + closeOnTop: { + get: function () { + return o.closeOnTop; + } + }, + preset: { + get: function () { + return n.parent ? n.getRoot().preset : o.load.preset; + }, + set: function (e) { + n.parent ? (n.getRoot().preset = e) : (o.load.preset = e), E(this), n.revert(); + } + }, + width: { + get: function () { + return o.width; + }, + set: function (e) { + (o.width = e), w(n, e); + } + }, + name: { + get: function () { + return o.name; + }, + set: function (e) { + (o.name = e), s && (s.innerHTML = o.name); + } + }, + closed: { + get: function () { + return o.closed; + }, + set: function (t) { + (o.closed = t), + o.closed + ? X.addClass(n.__ul, e.CLASS_CLOSED) + : X.removeClass(n.__ul, e.CLASS_CLOSED), + this.onResize(), + n.__closeButton && (n.__closeButton.innerHTML = t ? e.TEXT_OPEN : e.TEXT_CLOSED); + } + }, + load: { + get: function () { + return o.load; + } + }, + useLocalStorage: { + get: function () { + return i; + }, + set: function (e) { + ae && + ((i = e), + e ? X.bind(window, 'unload', r) : X.unbind(window, 'unload', r), + localStorage.setItem(m(n, 'isLocal'), e)); + } + } + }), + S.isUndefined(o.parent)) + ) { + if ( + ((this.closed = o.closed || !1), + X.addClass(this.domElement, e.CLASS_MAIN), + X.makeSelectable(this.domElement, !1), + ae && i) + ) { + n.useLocalStorage = !0; + var a = localStorage.getItem(m(this, 'gui')); + a && (o.load = JSON.parse(a)); + } + (this.__closeButton = document.createElement('div')), + (this.__closeButton.innerHTML = e.TEXT_CLOSED), + X.addClass(this.__closeButton, e.CLASS_CLOSE_BUTTON), + o.closeOnTop + ? (X.addClass(this.__closeButton, e.CLASS_CLOSE_TOP), + this.domElement.insertBefore(this.__closeButton, this.domElement.childNodes[0])) + : (X.addClass(this.__closeButton, e.CLASS_CLOSE_BOTTOM), + this.domElement.appendChild(this.__closeButton)), + X.bind(this.__closeButton, 'click', function () { + n.closed = !n.closed; + }); + } else { + void 0 === o.closed && (o.closed = !0); + var l = document.createTextNode(o.name); + X.addClass(l, 'controller-name'), (s = c(n, l)); + X.addClass(this.__ul, e.CLASS_CLOSED), + X.addClass(s, 'title'), + X.bind(s, 'click', function (e) { + return e.preventDefault(), (n.closed = !n.closed), !1; + }), + o.closed || (this.closed = !1); + } + o.autoPlace && + (S.isUndefined(o.parent) && + (de && + ((ce = document.createElement('div')), + X.addClass(ce, 'dg'), + X.addClass(ce, e.CLASS_AUTO_PLACE_CONTAINER), + document.body.appendChild(ce), + (de = !1)), + ce.appendChild(this.domElement), + X.addClass(this.domElement, e.CLASS_AUTO_PLACE)), + this.parent || w(n, o.width)), + (this.__resizeHandler = function () { + n.onResizeDebounced(); + }), + X.bind(window, 'resize', this.__resizeHandler), + X.bind(this.__ul, 'webkitTransitionEnd', this.__resizeHandler), + X.bind(this.__ul, 'transitionend', this.__resizeHandler), + X.bind(this.__ul, 'oTransitionEnd', this.__resizeHandler), + this.onResize(), + o.resizable && y(this), + (r = function () { + ae && + 'true' === localStorage.getItem(m(n, 'isLocal')) && + localStorage.setItem(m(n, 'gui'), JSON.stringify(n.getSaveObject())); + }), + (this.saveToLocalStorageIfPossible = r), + o.parent || + (function () { + var e = n.getRoot(); + (e.width += 1), + S.defer(function () { + e.width -= 1; + }); + })(); + }; + (he.toggleHide = function () { + (ue = !ue), + S.each(_e, function (e) { + e.domElement.style.display = ue ? 'none' : ''; + }); + }), + (he.CLASS_AUTO_PLACE = 'a'), + (he.CLASS_AUTO_PLACE_CONTAINER = 'ac'), + (he.CLASS_MAIN = 'main'), + (he.CLASS_CONTROLLER_ROW = 'cr'), + (he.CLASS_TOO_TALL = 'taller-than-window'), + (he.CLASS_CLOSED = 'closed'), + (he.CLASS_CLOSE_BUTTON = 'close-button'), + (he.CLASS_CLOSE_TOP = 'close-top'), + (he.CLASS_CLOSE_BOTTOM = 'close-bottom'), + (he.CLASS_DRAG = 'drag'), + (he.DEFAULT_WIDTH = 245), + (he.TEXT_CLOSED = 'Close Controls'), + (he.TEXT_OPEN = 'Open Controls'), + (he._keydownHandler = function (e) { + 'text' === document.activeElement.type || + (72 !== e.which && 72 !== e.keyCode) || + he.toggleHide(); + }), + X.bind(window, 'keydown', he._keydownHandler, !1), + S.extend(he.prototype, { + add: function (e, t) { + return f(this, e, t, { factoryArgs: Array.prototype.slice.call(arguments, 2) }); + }, + addColor: function (e, t) { + return f(this, e, t, { color: !0 }); + }, + remove: function (e) { + this.__ul.removeChild(e.__li), this.__controllers.splice(this.__controllers.indexOf(e), 1); + var t = this; + S.defer(function () { + t.onResize(); + }); + }, + destroy: function () { + if (this.parent) + throw new Error( + 'Only the root GUI should be removed with .destroy(). For subfolders, use gui.removeFolder(folder) instead.' + ); + this.autoPlace && ce.removeChild(this.domElement); + var e = this; + S.each(this.__folders, function (t) { + e.removeFolder(t); + }), + X.unbind(window, 'keydown', he._keydownHandler, !1), + u(this); + }, + addFolder: function (e) { + if (void 0 !== this.__folders[e]) + throw new Error('You already have a folder in this GUI by the name "' + e + '"'); + var t = { name: e, parent: this }; + (t.autoPlace = this.autoPlace), + this.load && + this.load.folders && + this.load.folders[e] && + ((t.closed = this.load.folders[e].closed), (t.load = this.load.folders[e])); + var n = new he(t); + this.__folders[e] = n; + var o = c(this, n.domElement); + return X.addClass(o, 'folder'), n; + }, + removeFolder: function (e) { + this.__ul.removeChild(e.domElement.parentElement), + delete this.__folders[e.name], + this.load && + this.load.folders && + this.load.folders[e.name] && + delete this.load.folders[e.name], + u(e); + var t = this; + S.each(e.__folders, function (t) { + e.removeFolder(t); + }), + S.defer(function () { + t.onResize(); + }); + }, + open: function () { + this.closed = !1; + }, + close: function () { + this.closed = !0; + }, + hide: function () { + this.domElement.style.display = 'none'; + }, + show: function () { + this.domElement.style.display = ''; + }, + onResize: function () { + var e = this.getRoot(); + if (e.scrollable) { + var t = X.getOffset(e.__ul).top, + n = 0; + S.each(e.__ul.childNodes, function (t) { + (e.autoPlace && t === e.__save_row) || (n += X.getHeight(t)); + }), + window.innerHeight - t - 20 < n + ? (X.addClass(e.domElement, he.CLASS_TOO_TALL), + (e.__ul.style.height = window.innerHeight - t - 20 + 'px')) + : (X.removeClass(e.domElement, he.CLASS_TOO_TALL), (e.__ul.style.height = 'auto')); + } + e.__resize_handle && + S.defer(function () { + e.__resize_handle.style.height = e.__ul.offsetHeight + 'px'; + }), + e.__closeButton && (e.__closeButton.style.width = e.width + 'px'); + }, + onResizeDebounced: S.debounce(function () { + this.onResize(); + }, 50), + remember: function () { + if ( + (S.isUndefined(le) && + ((le = new ie()).domElement.innerHTML = + '
\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n\n
\n\n
\n\n
'), + this.parent) + ) + throw new Error('You can only call remember on a top level GUI.'); + var e = this; + S.each(Array.prototype.slice.call(arguments), function (t) { + 0 === e.__rememberedObjects.length && v(e), + -1 === e.__rememberedObjects.indexOf(t) && e.__rememberedObjects.push(t); + }), + this.autoPlace && w(this, this.width); + }, + getRoot: function () { + for (var e = this; e.parent; ) e = e.parent; + return e; + }, + getSaveObject: function () { + var e = this.load; + return ( + (e.closed = this.closed), + this.__rememberedObjects.length > 0 && + ((e.preset = this.preset), + e.remembered || (e.remembered = {}), + (e.remembered[this.preset] = x(this))), + (e.folders = {}), + S.each(this.__folders, function (t, n) { + e.folders[n] = t.getSaveObject(); + }), + e + ); + }, + save: function () { + this.load.remembered || (this.load.remembered = {}), + (this.load.remembered[this.preset] = x(this)), + _(this, !1), + this.saveToLocalStorageIfPossible(); + }, + saveAs: function (e) { + this.load.remembered || + ((this.load.remembered = {}), (this.load.remembered[se] = x(this, !0))), + (this.load.remembered[e] = x(this)), + (this.preset = e), + g(this, e, !0), + this.saveToLocalStorageIfPossible(); + }, + revert: function (e) { + S.each( + this.__controllers, + function (t) { + this.getRoot().load.remembered ? p(e || this.getRoot(), t) : t.setValue(t.initialValue), + t.__onFinishChange && t.__onFinishChange.call(t, t.getValue()); + }, + this + ), + S.each(this.__folders, function (e) { + e.revert(e); + }), + e || _(this.getRoot(), !1); + }, + listen: function (e) { + var t = 0 === this.__listening.length; + this.__listening.push(e), t && C(this.__listening); + }, + updateDisplay: function () { + S.each(this.__controllers, function (e) { + e.updateDisplay(); + }), + S.each(this.__folders, function (e) { + e.updateDisplay(); + }); + } + }); + var pe = { Color: I, math: N, interpret: R }, + fe = { + Controller: z, + BooleanController: K, + OptionController: Y, + StringController: J, + NumberController: W, + NumberControllerBox: Q, + NumberControllerSlider: q, + FunctionController: Z, + ColorController: $ + }, + me = { dom: X }, + ge = { GUI: he }, + be = he, + ve = { color: pe, controllers: fe, dom: me, gui: ge, GUI: be }; + (e.color = pe), + (e.controllers = fe), + (e.dom = me), + (e.gui = ge), + (e.GUI = be), + (e.default = ve), + Object.defineProperty(e, '__esModule', { value: !0 }); +}); diff --git a/src/demos/main.js b/src/demos/main.js index 0939850..d4b6fd7 100644 --- a/src/demos/main.js +++ b/src/demos/main.js @@ -1,52 +1,56 @@ 'use strict'; -const $ = s=>document.querySelector(s); -const setDisplay = (el, val)=>{if ($(el)) $(el).style.display = val}; - +const $ = (s) => document.querySelector(s); +const setDisplay = (el, val) => { + if ($(el)) $(el).style.display = val; +}; class DemoApp { - constructor(demos, defaultDemo='ParticleLife3d') { - this.singleMode = demos.length == 1; - if (this.singleMode) { - defaultDemo = demos[0].name; - } - this.demos = Object.fromEntries(demos.map(c=>[c.name, c])); + constructor(demos, defaultDemo = 'ParticleLife3d') { + this.singleMode = demos.length == 1; + if (this.singleMode) { + defaultDemo = demos[0].name; + } + this.demos = Object.fromEntries(demos.map((c) => [c.name, c])); - this.canvas = document.getElementById('c'); - const gl = this.canvas.getContext('webgl2', {alpha:false, antialias:true, - xrCompatible:true}); - this.glsl = SwissGL(gl); - this.demo = null; - this.gui = null; + this.canvas = document.getElementById('c'); + const gl = this.canvas.getContext('webgl2', { + alpha: false, + antialias: true, + xrCompatible: true + }); + this.glsl = SwissGL(gl); + this.demo = null; + this.gui = null; - this.xrDemos = Object.values(this.demos).filter(f=>f.Tags&&f.Tags.includes('3d')); - this.xrSession = null; - this.xrRefSpace = null; - this.xrPose = null; - this.lookUpStartTime = 0; - this.haveVR = this.haveAR = false; - if (navigator.xr) { - navigator.xr.isSessionSupported('immersive-vr').then(supported=>{ - this.haveVR = supported; - this.updateVRButtons(); - }) - navigator.xr.isSessionSupported('immersive-ar').then(supported=>{ - this.haveAR = supported; - this.updateVRButtons(); - }) - } + this.xrDemos = Object.values(this.demos).filter((f) => f.Tags && f.Tags.includes('3d')); + this.xrSession = null; + this.xrRefSpace = null; + this.xrPose = null; + this.lookUpStartTime = 0; + this.haveVR = this.haveAR = false; + if (navigator.xr) { + navigator.xr.isSessionSupported('immersive-vr').then((supported) => { + this.haveVR = supported; + this.updateVRButtons(); + }); + navigator.xr.isSessionSupported('immersive-ar').then((supported) => { + this.haveAR = supported; + this.updateVRButtons(); + }); + } - this.viewParams = { - canvasSize: new Float32Array(2), - pointer: new Float32Array(3), - cameraYPD: new Float32Array(3), - xrRay: new Float32Array(16*2), - xrRayInv: new Float32Array(16*2), - xrButton: new Float32Array(4*2), - }; - this.resetCamera(); + this.viewParams = { + canvasSize: new Float32Array(2), + pointer: new Float32Array(3), + cameraYPD: new Float32Array(3), + xrRay: new Float32Array(16 * 2), + xrRayInv: new Float32Array(16 * 2), + xrButton: new Float32Array(4 * 2) + }; + this.resetCamera(); - this.glsl_include = ` + this.glsl_include = ` uniform bool xrMode; uniform mat4 xrProjectionMatrix, xrViewMatrix; uniform mat4 xrRay[2], xrRayInv[2]; @@ -81,234 +85,256 @@ class DemoApp { return wld2proj(vec4(p,1.0)); } `; - this.withCamera = this.glsl.hook((glsl, params, target)=>{ - params = {...params, Inc:this.glsl_include+(params.Inc||'')}; - if (target || !params.xrMode) { - return glsl(params, target); - } - delete params.Aspect; - let glLayer = this.xrSession.renderState.baseLayer; - target = {bindTarget:(gl)=>{ - gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer); - return [glLayer.framebufferWidth, glLayer.framebufferHeight]; - }} - for (let view of this.xrPose.views) { - const vp = glLayer.getViewport(view); - params.View = [vp.x, vp.y, vp.width, vp.height]; - params.xrProjectionMatrix = view.projectionMatrix; - params.xrViewMatrix = view.transform.inverse.matrix; - let {x,y,z} = view.transform.position; - params.xrPosition = [x, y, z]; - glsl(params, target); - } - }); + this.withCamera = this.glsl.hook((glsl, params, target) => { + params = { ...params, Inc: this.glsl_include + (params.Inc || '') }; + if (target || !params.xrMode) { + return glsl(params, target); + } + delete params.Aspect; + let glLayer = this.xrSession.renderState.baseLayer; + target = { + bindTarget: (gl) => { + gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer); + return [glLayer.framebufferWidth, glLayer.framebufferHeight]; + } + }; + for (let view of this.xrPose.views) { + const vp = glLayer.getViewport(view); + params.View = [vp.x, vp.y, vp.width, vp.height]; + params.xrProjectionMatrix = view.projectionMatrix; + params.xrViewMatrix = view.transform.inverse.matrix; + let { x, y, z } = view.transform.position; + params.xrPosition = [x, y, z]; + glsl(params, target); + } + }); - const setPointer = (e, buttons)=>{ - const [w, h] = this.viewParams.canvasSize; - const [x, y] = [e.offsetX-w/2, h/2-e.offsetY]; - this.viewParams.pointer.set([x, y, buttons]); - return [x, y]; - }; - this.canvas.addEventListener('pointerdown', e=>{ - if (!e.isPrimary) return; - setPointer(e, e.buttons); - }); - this.canvas.addEventListener('pointerout', e=>setPointer(e, 0)); - this.canvas.addEventListener('pointerup', e=>setPointer(e, 0)); - this.canvas.addEventListener('pointermove', e=>{ - const [px, py, _] = this.viewParams.pointer; - const [x, y] = setPointer(e, e.buttons); - if (!e.isPrimary || e.buttons != 1) return; - let [yaw, pitch, dist] = this.viewParams.cameraYPD; - yaw -= (x-px)*0.01; - pitch += (y-py)*0.01; - pitch = Math.min(Math.max(pitch, 0), Math.PI); - this.viewParams.cameraYPD.set([yaw, pitch, dist]); - }); + const setPointer = (e, buttons) => { + const [w, h] = this.viewParams.canvasSize; + const [x, y] = [e.offsetX - w / 2, h / 2 - e.offsetY]; + this.viewParams.pointer.set([x, y, buttons]); + return [x, y]; + }; + this.canvas.addEventListener('pointerdown', (e) => { + if (!e.isPrimary) return; + setPointer(e, e.buttons); + }); + this.canvas.addEventListener('pointerout', (e) => setPointer(e, 0)); + this.canvas.addEventListener('pointerup', (e) => setPointer(e, 0)); + this.canvas.addEventListener('pointermove', (e) => { + const [px, py, _] = this.viewParams.pointer; + const [x, y] = setPointer(e, e.buttons); + if (!e.isPrimary || e.buttons != 1) return; + let [yaw, pitch, dist] = this.viewParams.cameraYPD; + yaw -= (x - px) * 0.01; + pitch += (y - py) * 0.01; + pitch = Math.min(Math.max(pitch, 0), Math.PI); + this.viewParams.cameraYPD.set([yaw, pitch, dist]); + }); - let name = location.hash.slice(1); - if (!(name in this.demos)) { - name = defaultDemo; - } - this.runDemo(name); - this.populatePreviews(); + let name = location.hash.slice(1); + if (!(name in this.demos)) { + name = defaultDemo; + } + this.runDemo(name); + this.populatePreviews(); - requestAnimationFrame(this.frame.bind(this)); - } + requestAnimationFrame(this.frame.bind(this)); + } - resetCamera() { - this.viewParams.cameraYPD.set([Math.PI*3/4, Math.PI/4, 1.8]); - } + resetCamera() { + this.viewParams.cameraYPD.set([(Math.PI * 3) / 4, Math.PI / 4, 1.8]); + } - frame(t) { - requestAnimationFrame(this.frame.bind(this)); - if (this.xrSession) return; // skip canvas frames when XR is running - this.glsl.adjustCanvas(1); // fix devicePixelRatio to 1 - this.viewParams.canvasSize.set([this.canvas.clientWidth, this.canvas.clientHeight]); - - this.demo.frame(this.withCamera, { - time:t/1000.0, xrMode: false, - ...this.viewParams, - }); - } + frame(t) { + requestAnimationFrame(this.frame.bind(this)); + if (this.xrSession) return; // skip canvas frames when XR is running + this.glsl.adjustCanvas(1); // fix devicePixelRatio to 1 + this.viewParams.canvasSize.set([this.canvas.clientWidth, this.canvas.clientHeight]); - xrFrame(t, xrFrame) { - this.xrSession.requestAnimationFrame(this.xrFrame.bind(this)); - this.xrPose = xrFrame.getViewerPose(this.xrRefSpace); - if (!this.xrPose) return; - this.viewParams.xrRay.fill(0.0); - this.viewParams.xrRayInv.fill(0.0); - this.viewParams.xrButton.fill(0.0); - const params = {time:t/1000.0, xrMode: true, ...this.viewParams}; - for (let i=0; i<2; ++i) { - const inputSource = this.xrSession.inputSources[i]; - if (inputSource && inputSource.gamepad && inputSource.gamepad.buttons) { - inputSource.gamepad.buttons.forEach((btn, btnIdx)=>{ - if (btnIdx<4) this.viewParams.xrButton[i*4+btnIdx] = btn.value || btn.pressed; - }); - } - if (!inputSource || !inputSource.targetRaySpace) continue; - const pose = xrFrame.getPose(inputSource.targetRaySpace, this.xrRefSpace); - if (!pose) continue; - this.viewParams.xrRay.set(pose.transform.matrix, i*16); - this.viewParams.xrRayInv.set(pose.transform.inverse.matrix, i*16); - } - - this.demo.frame(this.withCamera, params); - this.withCamera({...params, Mesh: [20,20], Grid:[2], DepthTest:1, VP:` + this.demo.frame(this.withCamera, { + time: t / 1000.0, + xrMode: false, + ...this.viewParams + }); + } + + xrFrame(t, xrFrame) { + this.xrSession.requestAnimationFrame(this.xrFrame.bind(this)); + this.xrPose = xrFrame.getViewerPose(this.xrRefSpace); + if (!this.xrPose) return; + this.viewParams.xrRay.fill(0.0); + this.viewParams.xrRayInv.fill(0.0); + this.viewParams.xrButton.fill(0.0); + const params = { time: t / 1000.0, xrMode: true, ...this.viewParams }; + for (let i = 0; i < 2; ++i) { + const inputSource = this.xrSession.inputSources[i]; + if (inputSource && inputSource.gamepad && inputSource.gamepad.buttons) { + inputSource.gamepad.buttons.forEach((btn, btnIdx) => { + if (btnIdx < 4) this.viewParams.xrButton[i * 4 + btnIdx] = btn.value || btn.pressed; + }); + } + if (!inputSource || !inputSource.targetRaySpace) continue; + const pose = xrFrame.getPose(inputSource.targetRaySpace, this.xrRefSpace); + if (!pose) continue; + this.viewParams.xrRay.set(pose.transform.matrix, i * 16); + this.viewParams.xrRayInv.set(pose.transform.inverse.matrix, i * 16); + } + + this.demo.frame(this.withCamera, params); + this.withCamera({ + ...params, + Mesh: [20, 20], + Grid: [2], + DepthTest: 1, + VP: ` varying vec3 p = uv2sphere(UV); varying vec4 buttons = xrButton[ID.x]; - VPos = wld2proj(xrRay[ID.x]*vec4(p*vec3(0.02, 0.02, 0.1),1));`, FP:` + VPos = wld2proj(xrRay[ID.x]*vec4(p*vec3(0.02, 0.02, 0.1),1));`, + FP: ` vec3 c = p*0.5+0.5; FOut = vec4(c*0.5,1); float b = c.z*4.0; - if (b<4.0 && buttons[int(b)]>fract(b)) FOut += 0.5;`}); + if (b<4.0 && buttons[int(b)]>fract(b)) FOut += 0.5;` + }); - const lookUpCoef = -this.xrPose.transform.matrix[10]; - if (!this.singleMode && (lookUpCoef>0.5)) { - const dt = (t-this.lookUpStartTime) / 1000; - if (dt > 1) { - this.lookUpStartTime = t; - let i = this.xrDemos.indexOf(this.demo.constructor); - i = (i+1)%this.xrDemos.length; - this.runDemo(this.xrDemos[i].name); - } else { - this.withCamera({...params, Mesh: [20,20], dt, DepthTest:1, VP:` + const lookUpCoef = -this.xrPose.transform.matrix[10]; + if (!this.singleMode && lookUpCoef > 0.5) { + const dt = (t - this.lookUpStartTime) / 1000; + if (dt > 1) { + this.lookUpStartTime = t; + let i = this.xrDemos.indexOf(this.demo.constructor); + i = (i + 1) % this.xrDemos.length; + this.runDemo(this.xrDemos[i].name); + } else { + this.withCamera({ + ...params, + Mesh: [20, 20], + dt, + DepthTest: 1, + VP: ` vec3 p = uv2sphere(UV)*0.6*clamp(1.0-dt, 0.0, 0.8) + vec3(-2.0, 0.0, 3.0); - VPos = wld2proj(vec4(p,1));`, FP:`UV,0.5,1`}); - } - } else { - this.lookUpStartTime = t; - } - } + VPos = wld2proj(vec4(p,1));`, + FP: `UV,0.5,1` + }); + } + } else { + this.lookUpStartTime = t; + } + } - toggleXR(xr) { - if (!this.xrSession) { - navigator.xr.requestSession(`immersive-${xr}`).then(session=>{ - this.xrSession = session; - session.addEventListener('end', ()=>{this.xrSession = null;}); - session.updateRenderState({ baseLayer: new XRWebGLLayer(session, this.glsl.gl) }); - session.requestReferenceSpace('local').then((refSpace) => { - this.xrRefSpace = refSpace.getOffsetReferenceSpace( - new XRRigidTransform({x:0,y:-0.25,z:-1.0,w:1}, // position offset - {x:0.5,y:0.5,z:0.5,w:-0.5}) // rotate z up - ); - session.requestAnimationFrame(this.xrFrame.bind(this)); - }); - }); - } else { - this.xrSession.end(); - } - } + toggleXR(xr) { + if (!this.xrSession) { + navigator.xr.requestSession(`immersive-${xr}`).then((session) => { + this.xrSession = session; + session.addEventListener('end', () => { + this.xrSession = null; + }); + session.updateRenderState({ baseLayer: new XRWebGLLayer(session, this.glsl.gl) }); + session.requestReferenceSpace('local').then((refSpace) => { + this.xrRefSpace = refSpace.getOffsetReferenceSpace( + new XRRigidTransform( + { x: 0, y: -0.25, z: -1.0, w: 1 }, // position offset + { x: 0.5, y: 0.5, z: 0.5, w: -0.5 } + ) // rotate z up + ); + session.requestAnimationFrame(this.xrFrame.bind(this)); + }); + }); + } else { + this.xrSession.end(); + } + } - runDemo(name) { - if (this.demo) { - if (this.gui) this.gui.destroy(); - if (this.demo.free) this.demo.free(); - this.glsl.reset(); - this.demo = this.gui = null; - } - if (!this.singleMode) location.hash = name; - if (self.dat) { - this.gui = new dat.GUI(); - this.gui.domElement.id = 'gui' - this.gui.hide(); - } - this.demo = new this.demos[name](this.withCamera, this.gui); - if (this.gui && (this.gui.__controllers.length == 0)) { - this.gui.destroy(); - this.gui = null; - } - setDisplay('#settingButton', this.gui?'block':'none'); - if ($('#sourceLink')) { - $('#sourceLink').href = `https://github.com/google/swissgl/blob/main/demo/${name}.js`; - } - this.updateVRButtons(); - this.resetCamera(); - } + runDemo(name) { + if (this.demo) { + if (this.gui) this.gui.destroy(); + if (this.demo.free) this.demo.free(); + this.glsl.reset(); + this.demo = this.gui = null; + } + if (!this.singleMode) location.hash = name; + if (self.dat) { + this.gui = new dat.GUI(); + this.gui.domElement.id = 'gui'; + this.gui.hide(); + } + this.demo = new this.demos[name](this.withCamera, this.gui); + if (this.gui && this.gui.__controllers.length == 0) { + this.gui.destroy(); + this.gui = null; + } + setDisplay('#settingButton', this.gui ? 'block' : 'none'); + if ($('#sourceLink')) { + $('#sourceLink').href = `https://github.com/google/swissgl/blob/main/demo/${name}.js`; + } + this.updateVRButtons(); + this.resetCamera(); + } - updateVRButtons() { - setDisplay('#vrButton', 'none'); - setDisplay('#arButton', 'none'); - const tags = this.demo && this.demo.constructor.Tags; - if (tags && tags.includes('3d')) { - if (this.haveVR ) setDisplay('#vrButton', 'block'); - if (this.haveAR ) setDisplay('#arButton', 'block'); - } - } + updateVRButtons() { + setDisplay('#vrButton', 'none'); + setDisplay('#arButton', 'none'); + const tags = this.demo && this.demo.constructor.Tags; + if (tags && tags.includes('3d')) { + if (this.haveVR) setDisplay('#vrButton', 'block'); + if (this.haveAR) setDisplay('#arButton', 'block'); + } + } - populatePreviews() { - const panel = document.getElementById('cards'); - if (!panel) return; - Object.keys(this.demos).forEach(name=>{ - const el = document.createElement('div'); - el.classList.add('card'); - el.innerHTML = `${name}`; - el.addEventListener('click', ()=>this.runDemo(name)); - panel.appendChild(el); - }); - } + populatePreviews() { + const panel = document.getElementById('cards'); + if (!panel) return; + Object.keys(this.demos).forEach((name) => { + const el = document.createElement('div'); + el.classList.add('card'); + el.innerHTML = `${name}`; + el.addEventListener('click', () => this.runDemo(name)); + panel.appendChild(el); + }); + } - // helper function to render demo preview images - genPreviews() { - const panel = document.getElementById('cards'); - panel.innerHTML = ''; - const canvas = document.createElement('canvas'); - canvas.width = 400; canvas.height = 300; - const glsl = SwissGL(canvas); - const withCamera = glsl.hook((glsl, p, t)=>glsl( - {...p, Inc:this.glsl_include+(p.Inc||'')}, t)); - Object.keys(this.demos).forEach(name=>{ - if (name == 'Spectrogram') return; - const dummyGui = new dat.GUI(); - const demo = new this.demos[name](withCamera, dummyGui); - dummyGui.destroy(); - this.resetCamera(); - for (let i=0; i<60*5; ++i) { - withCamera({Clear:0}, '') - demo.frame(withCamera, {time:i/60.0, ...this.viewParams}); - } - const el = document.createElement('div') - const data = canvas.toDataURL('image/jpeg', 0.95); - el.innerHTML = ` + // helper function to render demo preview images + genPreviews() { + const panel = document.getElementById('cards'); + panel.innerHTML = ''; + const canvas = document.createElement('canvas'); + canvas.width = 400; + canvas.height = 300; + const glsl = SwissGL(canvas); + const withCamera = glsl.hook((glsl, p, t) => + glsl({ ...p, Inc: this.glsl_include + (p.Inc || '') }, t) + ); + Object.keys(this.demos).forEach((name) => { + if (name == 'Spectrogram') return; + const dummyGui = new dat.GUI(); + const demo = new this.demos[name](withCamera, dummyGui); + dummyGui.destroy(); + this.resetCamera(); + for (let i = 0; i < 60 * 5; ++i) { + withCamera({ Clear: 0 }, ''); + demo.frame(withCamera, { time: i / 60.0, ...this.viewParams }); + } + const el = document.createElement('div'); + const data = canvas.toDataURL('image/jpeg', 0.95); + el.innerHTML = ` ${name}`; - panel.appendChild(el) - if (demo.free) demo.free(); - glsl.reset(); - }) - } - - toggleGui() { - if (!this.gui) return; - const style = this.gui.domElement.style; - style.display = (style.display == 'none')?'':'none' - } + panel.appendChild(el); + if (demo.free) demo.free(); + glsl.reset(); + }); + } - fullscreen() { - const {canvas} = this; - const f = canvas.requestFullscreen || canvas.webkitRequestFullscreen; - if (f) f.apply(canvas); - } + toggleGui() { + if (!this.gui) return; + const style = this.gui.domElement.style; + style.display = style.display == 'none' ? '' : 'none'; + } + fullscreen() { + const { canvas } = this; + const f = canvas.requestFullscreen || canvas.webkitRequestFullscreen; + if (f) f.apply(canvas); + } } diff --git a/src/demos/style.css b/src/demos/style.css index ed0a5df..2789e33 100644 --- a/src/demos/style.css +++ b/src/demos/style.css @@ -1,93 +1,101 @@ body { - box-sizing: border-box; - background:black; margin: 0px; - color: white; - overflow: hidden; - font-family: 'Roboto Mono', monospace; - user-select: none; + box-sizing: border-box; + background: black; + margin: 0px; + color: white; + overflow: hidden; + font-family: 'Roboto Mono', monospace; + user-select: none; } #panel { - width: 200px; - position: fixed; - background: rgba(0, 0, 0, 0.5); + width: 200px; + position: fixed; + background: rgba(0, 0, 0, 0.5); } #panel summary { - padding: 8px; + padding: 8px; } #cards { - overflow: auto; - height: 95vh; + overflow: auto; + height: 95vh; } .card { - padding: 4px; - margin: 8px; - border: 1px solid grey; - border-radius: 5px; - font-size: 14px; + padding: 4px; + margin: 8px; + border: 1px solid grey; + border-radius: 5px; + font-size: 14px; } #panel img { - max-width: 100%; + max-width: 100%; } #demo { - width: 100%; height:100vh; + width: 100%; + height: 100vh; } #c { - width: 100%; height:100%; - background:black; - touch-action: none; + width: 100%; + height: 100%; + background: black; + touch-action: none; } -#gui { - position: fixed; bottom:0px; right:50px +#gui { + position: fixed; + bottom: 0px; + right: 50px; } #buttons { - position:fixed; bottom:10px; right:10px; -} -button{ - appearance: none; - min-width:48px; - height: 40px; - margin: 8px; - font-size: 24px; - background-color: rgba(0, 0, 0, .5); - color: white; - border: none; - text-align: center; - text-decoration: none; - display: block; - cursor: pointer; + position: fixed; + bottom: 10px; + right: 10px; +} +button { + appearance: none; + min-width: 48px; + height: 40px; + margin: 8px; + font-size: 24px; + background-color: rgba(0, 0, 0, 0.5); + color: white; + border: none; + text-align: center; + text-decoration: none; + display: block; + cursor: pointer; } button:hover { - background-color: rgba(80, 80, 80, 0.8); + background-color: rgba(80, 80, 80, 0.8); } #buttons a { - text-decoration: none; - color: white; + text-decoration: none; + color: white; } #aboutButton { - position:fixed; top:10px; right:10px; - + position: fixed; + top: 10px; + right: 10px; } a { - color: aquamarine; + color: aquamarine; } #about { - width: 90%; - max-width: 400px; - background-color: rgba(80, 80, 80, 0.9); - position: absolute; - top: 50%; left: 50%; - transform: translate(-50%, -50%); - padding: 8px; - border-radius: 8px; - user-select: text; -} - \ No newline at end of file + width: 90%; + max-width: 400px; + background-color: rgba(80, 80, 80, 0.9); + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 8px; + border-radius: 8px; + user-select: text; +} diff --git a/src/lib/swissgl.ts b/src/lib/swissgl.ts index 3cfaf36..364267e 100644 --- a/src/lib/swissgl.ts +++ b/src/lib/swissgl.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - // Repeat/Loop? // fbo: // - multiple named render targets (Out...?) @@ -36,136 +35,152 @@ const Type2Setter = {}; const UniformType2TexTarget = {}; const TextureFormats = {}; { - const GL = WebGL2RenderingContext; - for (const t of ['FLOAT', 'INT', 'BOOL']) { - const suf = t=='FLOAT' ? 'f':'i'; - Type2Setter[GL[t]] = 'uniform1'+suf; - for (const i of [2, 3, 4]) { - Type2Setter[GL[`${t}_VEC${i}`]] = `uniform${i}${suf}v`; - if (suf=='f') { - Type2Setter[GL[`${t}_MAT${i}`]] = `uniformMatrix${i}fv`; - } - } - } - UniformType2TexTarget[GL.SAMPLER_2D] = GL.TEXTURE_2D; - UniformType2TexTarget[GL.SAMPLER_2D_ARRAY] = GL.TEXTURE_2D_ARRAY; - - for (const [name, internalFormat, glformat, type, CpuArray, chn] of [ - ['r8', GL.R8, GL.RED, GL.UNSIGNED_BYTE, Uint8Array, 1], - ['rgba8', GL.RGBA8, GL.RGBA, GL.UNSIGNED_BYTE, Uint8Array, 4], - ['r16f', GL.R16F, GL.RED, GL.HALF_FLOAT, Uint16Array, 1], - ['rgba16f', GL.RGBA16F, GL.RGBA, GL.HALF_FLOAT, Uint16Array, 4], - ['r32f', GL.R32F, GL.RED, GL.FLOAT, Float32Array, 1], - ['rg32f', GL.RG32F, GL.RG, GL.FLOAT, Float32Array, 2], - ['rgba32f', GL.RGBA32F, GL.RGBA, GL.FLOAT, Float32Array, 4], - ['depth', GL.DEPTH_COMPONENT24, GL.DEPTH_COMPONENT, GL.UNSIGNED_INT, Uint32Array, 1], - ]) TextureFormats[name] = {internalFormat, glformat, type, CpuArray, chn}; + const GL = WebGL2RenderingContext; + for (const t of ['FLOAT', 'INT', 'BOOL']) { + const suf = t == 'FLOAT' ? 'f' : 'i'; + Type2Setter[GL[t]] = 'uniform1' + suf; + for (const i of [2, 3, 4]) { + Type2Setter[GL[`${t}_VEC${i}`]] = `uniform${i}${suf}v`; + if (suf == 'f') { + Type2Setter[GL[`${t}_MAT${i}`]] = `uniformMatrix${i}fv`; + } + } + } + UniformType2TexTarget[GL.SAMPLER_2D] = GL.TEXTURE_2D; + UniformType2TexTarget[GL.SAMPLER_2D_ARRAY] = GL.TEXTURE_2D_ARRAY; + + for (const [name, internalFormat, glformat, type, CpuArray, chn] of [ + ['r8', GL.R8, GL.RED, GL.UNSIGNED_BYTE, Uint8Array, 1], + ['rgba8', GL.RGBA8, GL.RGBA, GL.UNSIGNED_BYTE, Uint8Array, 4], + ['r16f', GL.R16F, GL.RED, GL.HALF_FLOAT, Uint16Array, 1], + ['rgba16f', GL.RGBA16F, GL.RGBA, GL.HALF_FLOAT, Uint16Array, 4], + ['r32f', GL.R32F, GL.RED, GL.FLOAT, Float32Array, 1], + ['rg32f', GL.RG32F, GL.RG, GL.FLOAT, Float32Array, 2], + ['rgba32f', GL.RGBA32F, GL.RGBA, GL.FLOAT, Float32Array, 4], + ['depth', GL.DEPTH_COMPONENT24, GL.DEPTH_COMPONENT, GL.UNSIGNED_INT, Uint32Array, 1] + ]) + TextureFormats[name] = { internalFormat, glformat, type, CpuArray, chn }; } function memoize(f) { - const cache = {}; - const wrap = k => k in cache ? cache[k] : cache[k]=f(k); - wrap.cache = cache; - return wrap; + const cache = {}; + const wrap = (k) => (k in cache ? cache[k] : (cache[k] = f(k))); + wrap.cache = cache; + return wrap; } function updateObject(o, updates) { - for (const s in updates) { o[s] = updates[s];} - return o; + for (const s in updates) { + o[s] = updates[s]; + } + return o; } // Parse strings like 'min(s,d)', 'max(s,d)', 's*d', 's+d*(1-sa)', // 's*d', 'd*(1-sa) + s*sa', s-d', 'd-s' and so on into // gl.blendFunc/gl.blendEquation arguments. function parseBlend(s0) { - if (!s0) return; - let s = s0.replace(/\s+/g, ''); - if (!s) return null; - const GL = WebGL2RenderingContext; - const func2gl = { - 'min': GL.MIN, 'max': GL.MAX, '+':GL.FUNC_ADD, - 's-d': GL.FUNC_SUBTRACT, 'd-s': GL.FUNC_REVERSE_SUBTRACT - }; - const factor2gl = { - '0': GL.ZERO, '1': GL.ONE, - 's': GL.SRC_COLOR, '(1-s)': GL.ONE_MINUS_SRC_COLOR, - 'd': GL.DST_COLOR, '(1-d)': GL.ONE_MINUS_DST_COLOR, - 'sa': GL.SRC_ALPHA, '(1-sa)': GL.ONE_MINUS_SRC_ALPHA, - 'da': GL.DST_ALPHA, '(1-da)': GL.ONE_MINUS_DST_ALPHA, - 'c': GL.CONSTANT_COLOR, '(1-c)': GL.ONE_MINUS_CONSTANT_COLOR, - 'ca': GL.CONSTANT_ALPHA, '(1-ca)': GL.ONE_MINUS_CONSTANT_ALPHA, - }; - const res = {s:GL.ZERO, d:GL.ZERO, f:null}; - s = s.replace(/(s|d)(?:\*(\w+|\(1-\w+\)))?/g, (_,term,factor)=>{ - factor = factor||'1'; - if (!(factor in factor2gl)) { - throw `Unknown blend factor: "${factor}"`; - } - res[term] = factor2gl[factor]; - return term; - }); - let m; - if (m=s.match(/^(min|max)\((s,d|d,s)\)$/)) { - res.f = func2gl[m[1]]; - } else if (s.match(/^(s|d|s\+d|d\+s)$/)) { - res.f = func2gl['+']; - } else if (s in func2gl) { - res.f = func2gl[s]; - } else { - throw `Unable to parse blend spec: "${s0}"`; - } - return res; + if (!s0) return; + let s = s0.replace(/\s+/g, ''); + if (!s) return null; + const GL = WebGL2RenderingContext; + const func2gl = { + min: GL.MIN, + max: GL.MAX, + '+': GL.FUNC_ADD, + 's-d': GL.FUNC_SUBTRACT, + 'd-s': GL.FUNC_REVERSE_SUBTRACT + }; + const factor2gl = { + '0': GL.ZERO, + '1': GL.ONE, + s: GL.SRC_COLOR, + '(1-s)': GL.ONE_MINUS_SRC_COLOR, + d: GL.DST_COLOR, + '(1-d)': GL.ONE_MINUS_DST_COLOR, + sa: GL.SRC_ALPHA, + '(1-sa)': GL.ONE_MINUS_SRC_ALPHA, + da: GL.DST_ALPHA, + '(1-da)': GL.ONE_MINUS_DST_ALPHA, + c: GL.CONSTANT_COLOR, + '(1-c)': GL.ONE_MINUS_CONSTANT_COLOR, + ca: GL.CONSTANT_ALPHA, + '(1-ca)': GL.ONE_MINUS_CONSTANT_ALPHA + }; + const res = { s: GL.ZERO, d: GL.ZERO, f: null }; + s = s.replace(/(s|d)(?:\*(\w+|\(1-\w+\)))?/g, (_, term, factor) => { + factor = factor || '1'; + if (!(factor in factor2gl)) { + throw `Unknown blend factor: "${factor}"`; + } + res[term] = factor2gl[factor]; + return term; + }); + let m; + if ((m = s.match(/^(min|max)\((s,d|d,s)\)$/))) { + res.f = func2gl[m[1]]; + } else if (s.match(/^(s|d|s\+d|d\+s)$/)) { + res.f = func2gl['+']; + } else if (s in func2gl) { + res.f = func2gl[s]; + } else { + throw `Unable to parse blend spec: "${s0}"`; + } + return res; } parseBlend = memoize(parseBlend); function compileShader(gl, code, type, program) { - code = '#version 300 es\n'+code; - const shader = gl.createShader(type); - gl.shaderSource(shader, code); - gl.compileShader(shader); - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - const withLines = code.split('\n').map( - (s, i)=>`${(i+1+'').padStart(4)}: ${s}`).join('\n') - throw (withLines+'\n'+'--- GLSL COMPILE ERROR ---\n'+ gl.getShaderInfoLog(shader)); - } - gl.attachShader(program, shader); - gl.deleteShader(shader); + code = '#version 300 es\n' + code; + const shader = gl.createShader(type); + gl.shaderSource(shader, code); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + const withLines = code + .split('\n') + .map((s, i) => `${(i + 1 + '').padStart(4)}: ${s}`) + .join('\n'); + throw withLines + '\n' + '--- GLSL COMPILE ERROR ---\n' + gl.getShaderInfoLog(shader); + } + gl.attachShader(program, shader); + gl.deleteShader(shader); } function compileProgram(gl, vs, fs) { - const program = gl.createProgram(); - compileShader(gl, vs, gl.VERTEX_SHADER, program); - compileShader(gl, fs, gl.FRAGMENT_SHADER, program); - gl.linkProgram(program); - if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { - console.error("shader link error:" + gl.getProgramInfoLog(program)); - } - gl.useProgram(program); - program.setters = {}; - let unitCount = 0; - const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); - for (let i = 0; i < numUniforms; ++i) { - const info = gl.getActiveUniform(program, i); - const loc = gl.getUniformLocation(program, info.name); - const name = info.name.match(/^\w+/)[0]; - if (info.type in UniformType2TexTarget) { - const unit = unitCount++; - const target = UniformType2TexTarget[info.type]; - gl.uniform1i(loc, unit); - program.setters[name] = tex=>{ - gl.activeTexture(gl.TEXTURE0+unit); - tex ? tex.bindSampler(unit) : gl.bindTexture(target, null); - } - } else { - const fname = Type2Setter[info.type]; - const setter = fname.startsWith('uniformMatrix') ? - v=>gl[fname](loc, false, v) : v=>gl[fname](loc, v); - program.setters[name] = v=>v!=undefined?setter(v):null; - } - } - gl.useProgram(null); - console.log('created', program); - return program; + const program = gl.createProgram(); + compileShader(gl, vs, gl.VERTEX_SHADER, program); + compileShader(gl, fs, gl.FRAGMENT_SHADER, program); + gl.linkProgram(program); + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + console.error('shader link error:' + gl.getProgramInfoLog(program)); + } + gl.useProgram(program); + program.setters = {}; + let unitCount = 0; + const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + for (let i = 0; i < numUniforms; ++i) { + const info = gl.getActiveUniform(program, i); + const loc = gl.getUniformLocation(program, info.name); + const name = info.name.match(/^\w+/)[0]; + if (info.type in UniformType2TexTarget) { + const unit = unitCount++; + const target = UniformType2TexTarget[info.type]; + gl.uniform1i(loc, unit); + program.setters[name] = (tex) => { + gl.activeTexture(gl.TEXTURE0 + unit); + tex ? tex.bindSampler(unit) : gl.bindTexture(target, null); + }; + } else { + const fname = Type2Setter[info.type]; + const setter = fname.startsWith('uniformMatrix') + ? (v) => gl[fname](loc, false, v) + : (v) => gl[fname](loc, v); + program.setters[name] = (v) => (v != undefined ? setter(v) : null); + } + } + gl.useProgram(null); + console.log('created', program); + return program; } const glsl_template = ` @@ -276,78 +291,82 @@ vec4 _sample(sampler2DArray tex, ivec2 xy, int layer) {return texelFetch(tex, iv `; function guessUniforms(params) { - const uni = []; - const len2type = {1:'float', 2:'vec2', 3:'vec3', 4:'vec4', 9:'mat3', 16:'mat4'}; - for (const name in params) { - const v = params[name]; - let s = null; - if (v instanceof TextureSampler) { - const [type, D] = v.layern?['sampler2DArray', '3']:['sampler2D', '2']; - const lookupMacro = v.layern? - `#define ${name}(p,l) (_sample(${name}, (p), (l)))` : - `#define ${name}(p) (_sample(${name}, (p)))`; - s = `uniform ${type} ${name}; + const uni = []; + const len2type = { 1: 'float', 2: 'vec2', 3: 'vec3', 4: 'vec4', 9: 'mat3', 16: 'mat4' }; + for (const name in params) { + const v = params[name]; + let s = null; + if (v instanceof TextureSampler) { + const [type, D] = v.layern ? ['sampler2DArray', '3'] : ['sampler2D', '2']; + const lookupMacro = v.layern + ? `#define ${name}(p,l) (_sample(${name}, (p), (l)))` + : `#define ${name}(p) (_sample(${name}, (p)))`; + s = `uniform ${type} ${name}; ${lookupMacro} ivec${D} ${name}_size() {return textureSize(${name}, 0);} vec${D} ${name}_step() {return 1.0/vec${D}(${name}_size());}`; - } else if (typeof v === 'number') { - s=`uniform float ${name};` - } else if (typeof v === 'boolean') { - s=`uniform bool ${name};` - } else if (v.length in len2type) { - s=`uniform ${len2type[v.length]} ${name};` - } - if (s) uni.push(s); - } - return uni.join('\n')+'\n'; + } else if (typeof v === 'number') { + s = `uniform float ${name};`; + } else if (typeof v === 'boolean') { + s = `uniform bool ${name};`; + } else if (v.length in len2type) { + s = `uniform ${len2type[v.length]} ${name};`; + } + if (s) uni.push(s); + } + return uni.join('\n') + '\n'; } -const stripComments = code=>code.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g,''); +const stripComments = (code) => code.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, ''); // TODO better parser (use '\b') function definedUniforms(code) { - code = stripComments(code); - const lines = Array.from(code.matchAll(/uniform\s+\w+\s+([^;]+)\s*;/g)); - return new Set(lines.map(m=>m[1].split(/[^\w]+/)).flat()); + code = stripComments(code); + const lines = Array.from(code.matchAll(/uniform\s+\w+\s+([^;]+)\s*;/g)); + return new Set(lines.map((m) => m[1].split(/[^\w]+/)).flat()); } function expandCode(code, mainFunc, outVar) { - const stripped = stripComments(code).trim(); - if (stripped != '' && stripped.indexOf(';') == -1) { - code = `${outVar} = vec4(${stripped});` - } - if (!stripped.match(new RegExp(`\\b${mainFunc}\s*\\(`))) { - code = `void ${mainFunc}() { + const stripped = stripComments(code).trim(); + if (stripped != '' && stripped.indexOf(';') == -1) { + code = `${outVar} = vec4(${stripped});`; + } + if (!stripped.match(new RegExp(`\\b${mainFunc}\s*\\(`))) { + code = `void ${mainFunc}() { ${code}; - }` - } - return code; + }`; + } + return code; } -const expandVP = memoize(code=>expandCode(code, 'vertex', 'VPos')); -const expandFP = memoize(code=>expandCode(code, 'fragment', 'FOut')); +const expandVP = memoize((code) => expandCode(code, 'vertex', 'VPos')); +const expandFP = memoize((code) => expandCode(code, 'fragment', 'FOut')); function extractVaryings(VP) { - return Array.from(stripComments(VP).matchAll(/\bvarying\s+[^;]+;/g)) - .map(m=>m[0]).map(s=>{ - while (s != (s=s.replace(/\([^()]*\)/g, ''))); // remove nested () - return s.replace(/=[^,;]*/g,'') // remove assigned values - }).join('\n'); + return Array.from(stripComments(VP).matchAll(/\bvarying\s+[^;]+;/g)) + .map((m) => m[0]) + .map((s) => { + while (s != (s = s.replace(/\([^()]*\)/g, ''))); // remove nested () + return s.replace(/=[^,;]*/g, ''); // remove assigned values + }) + .join('\n'); } function stripVaryings(VP) { - return VP.replace(/\bvarying\s+\w+/g,''); + return VP.replace(/\bvarying\s+\w+/g, ''); } function linkShader(gl, uniforms, Inc, VP, FP) { - const defined = definedUniforms([glsl_template, Inc, VP, FP].join('\n')); - const undefined = Object.entries(uniforms) - .filter(kv=>kv[0].match(/^\w+$/)) - .filter(kv=>!(defined.has(kv[0]))); - const guessed = guessUniforms(Object.fromEntries(undefined)); - const varyings = extractVaryings(VP); - VP = expandVP(stripVaryings(VP)); - const prefix = `${glsl_template}\n${guessed}\n${varyings}\n${Inc}\n`; - return compileProgram(gl, ` + const defined = definedUniforms([glsl_template, Inc, VP, FP].join('\n')); + const undefined = Object.entries(uniforms) + .filter((kv) => kv[0].match(/^\w+$/)) + .filter((kv) => !defined.has(kv[0])); + const guessed = guessUniforms(Object.fromEntries(undefined)); + const varyings = extractVaryings(VP); + VP = expandVP(stripVaryings(VP)); + const prefix = `${glsl_template}\n${guessed}\n${varyings}\n${Inc}\n`; + return compileProgram( + gl, + ` #define VERT ${prefix}\n${VP} void main() { @@ -365,448 +384,548 @@ function linkShader(gl, uniforms, Inc, VP, FP) { VPos = vec4(XY,0,1); vertex(); VPos.xy *= Aspect; - }`, ` + }`, + ` #define FRAG ${prefix}\n${expandFP(FP)} void main() { I = ivec2(gl_FragCoord.xy); fragment(); - }`); + }` + ); } class TextureSampler { - fork(updates) { - const {gl, handle, gltarget, layern, filter, wrap} = {...this, ...updates}; - return updateObject(new TextureSampler(), {gl, handle, gltarget, layern, filter, wrap}); - } - get linear() {return this.fork({filter:'linear'})} - get nearest() {return this.fork({filter:'nearest'})} - get miplinear() {return this.fork({filter:'miplinear'})} - get edge() {return this.fork({wrap:'edge'})} - get repeat() {return this.fork({wrap:'repeat'})} - get mirror() {return this.fork({wrap:'mirror'})} - - get _sampler() { - const {gl, filter, wrap} = this; - if (!gl._samplers) {gl._samplers = {};} - const id = `${filter}:${wrap}`; - if (!(id in gl._samplers)) { - const glfilter = { 'nearest': gl.NEAREST, 'linear': gl.LINEAR, - 'miplinear':gl.LINEAR_MIPMAP_LINEAR}[filter]; - const glwrap = {'repeat': gl.REPEAT, 'edge': gl.CLAMP_TO_EDGE, - 'mirror': gl.MIRRORED_REPEAT}[wrap]; - const sampler = gl.createSampler(); - const setf = (k, v)=>gl.samplerParameteri(sampler, gl['TEXTURE_'+k], v); - setf('MIN_FILTER', glfilter); - setf('MAG_FILTER', filter=='miplinear' ? gl.LINEAR : glfilter); - setf('WRAP_S', glwrap); - setf('WRAP_T', glwrap); - gl._samplers[id] = sampler; - } - return gl._samplers[id]; - } - bindSampler(unit) { - // assume unit is already active - const {gl, gltarget, handle} = this; - gl.bindTexture(gltarget, handle); - if (this.filter == 'miplinear' && !handle.hasMipmap) { - gl.generateMipmap(gltarget) - handle.hasMipmap = true; - } - gl.bindSampler(unit, this._sampler); - } + fork(updates) { + const { gl, handle, gltarget, layern, filter, wrap } = { ...this, ...updates }; + return updateObject(new TextureSampler(), { gl, handle, gltarget, layern, filter, wrap }); + } + get linear() { + return this.fork({ filter: 'linear' }); + } + get nearest() { + return this.fork({ filter: 'nearest' }); + } + get miplinear() { + return this.fork({ filter: 'miplinear' }); + } + get edge() { + return this.fork({ wrap: 'edge' }); + } + get repeat() { + return this.fork({ wrap: 'repeat' }); + } + get mirror() { + return this.fork({ wrap: 'mirror' }); + } + + get _sampler() { + const { gl, filter, wrap } = this; + if (!gl._samplers) { + gl._samplers = {}; + } + const id = `${filter}:${wrap}`; + if (!(id in gl._samplers)) { + const glfilter = { + nearest: gl.NEAREST, + linear: gl.LINEAR, + miplinear: gl.LINEAR_MIPMAP_LINEAR + }[filter]; + const glwrap = { repeat: gl.REPEAT, edge: gl.CLAMP_TO_EDGE, mirror: gl.MIRRORED_REPEAT }[ + wrap + ]; + const sampler = gl.createSampler(); + const setf = (k, v) => gl.samplerParameteri(sampler, gl['TEXTURE_' + k], v); + setf('MIN_FILTER', glfilter); + setf('MAG_FILTER', filter == 'miplinear' ? gl.LINEAR : glfilter); + setf('WRAP_S', glwrap); + setf('WRAP_T', glwrap); + gl._samplers[id] = sampler; + } + return gl._samplers[id]; + } + bindSampler(unit) { + // assume unit is already active + const { gl, gltarget, handle } = this; + gl.bindTexture(gltarget, handle); + if (this.filter == 'miplinear' && !handle.hasMipmap) { + gl.generateMipmap(gltarget); + handle.hasMipmap = true; + } + gl.bindSampler(unit, this._sampler); + } } class TextureTarget extends TextureSampler { - constructor(gl, params) { - super(); - let {size, tag, format='rgba8', filter='nearest', wrap='repeat', - layern=null, data=null, depth=null} = params; - if (!depth && format.includes('+')) { - const [mainFormat, depthFormat] = format.split('+'); - format = mainFormat; - depth = new TextureTarget(gl, {...params, - tag:tag+'_depth',format:depthFormat, layern:null, depth:null}); - } - this.handle = gl.createTexture(), - this.filter = format=='depth' ? 'nearest' : filter; - this.gltarget = layern ? gl.TEXTURE_2D_ARRAY : gl.TEXTURE_2D; - this.formatInfo = TextureFormats[format]; - updateObject(this, {gl, _tag:tag, format, layern, wrap, depth}); - this.update(size, data); - } - update(size, data) { - const {gl, handle, gltarget, layern} = this; - const {internalFormat, glformat, type} = this.formatInfo; - const [w, h] = size; - gl.bindTexture(gltarget, handle); - if (!layern) { - gl.texImage2D(gltarget, 0/*mip level*/, - internalFormat, w, h, 0/*border*/, - glformat, type, data/*data*/); - } else { - gl.texImage3D(gltarget, 0/*mip level*/, - internalFormat, w, h, layern, 0/*border*/, - glformat, type, data/*data*/); - } - gl.bindTexture(gltarget, null); - this.size = size; - if (this.depth) {this.depth.update(size, data);} - } - attach(gl) { - if (!this.layern) { - const attachment = this.format == 'depth' ? gl.DEPTH_ATTACHMENT : gl.COLOR_ATTACHMENT0; - gl.framebufferTexture2D( - gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, this.handle, 0/*level*/); - } else { - const drawBuffers = []; - for (let i=0; i 6) { - this._deleteAsyncBuf(this.async.queue.pop()); - } - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, gpuBuf); - if (!gpuBuf.length || gpuBuf.length < n) { - const byteN = n * this.formatInfo.CpuArray.BYTES_PER_ELEMENT - gl.bufferData(gl.PIXEL_PACK_BUFFER, byteN, gl.STREAM_READ); - gpuBuf.length = n; - console.log(`created/resized async gpu buffer "${this._tag}":`, gpuBuf); - } - return gpuBuf; - } - _deleteAsyncBuf(gpuBuf) { - delete gpuBuf.length; - this.gl.deleteBuffer(gpuBuf); - this.async.all.delete(gpuBuf); - } - // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#use_non-blocking_async_data_readback - read(callback, optBox, optTarget) { - const {gl} = this; - const {box, n} = this._getBox(optBox); - const gpuBuf = this._bindAsyncBuffer(n); - this._readPixels(box, 0); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); - gl.flush(); - this._asyncFetch(gpuBuf, sync, callback, optTarget); - } - _asyncFetch(gpuBuf, sync, callback, optTarget) { - const {gl} = this; - if (!gpuBuf.length) { // check that gpu buffer is not deleted - gl.deleteSync(sync); return; - } - const res = gl.clientWaitSync(sync, 0, 0); - if (res === gl.TIMEOUT_EXPIRED) { - setTimeout(()=>this._asyncFetch(gpuBuf, sync, callback, optTarget), 1 /*ms*/); return; } - if (res === gl.WAIT_FAILED) { - console.log(`async read of ${this._tag} failed`); - } else { - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, gpuBuf); - const target = optTarget || this._getCPUBuf(gpuBuf.length); - gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0 /*srcOffset*/, - target, 0 /*dstOffset*/, gpuBuf.length /*length*/); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - callback(target); - } - gl.deleteSync(sync); - this.async.queue.push(gpuBuf); - } - free() { - const gl = this.gl; - if (this.depth) this.depth.free(); - if (this.fbo) gl.deleteFramebuffer(this.fbo); - if (this.async) this.async.all.forEach(buf=>this._deleteAsyncBuf(buf)); - gl.deleteTexture(this.handle); - } + constructor(gl, params) { + super(); + let { + size, + tag, + format = 'rgba8', + filter = 'nearest', + wrap = 'repeat', + layern = null, + data = null, + depth = null + } = params; + if (!depth && format.includes('+')) { + const [mainFormat, depthFormat] = format.split('+'); + format = mainFormat; + depth = new TextureTarget(gl, { + ...params, + tag: tag + '_depth', + format: depthFormat, + layern: null, + depth: null + }); + } + (this.handle = gl.createTexture()), (this.filter = format == 'depth' ? 'nearest' : filter); + this.gltarget = layern ? gl.TEXTURE_2D_ARRAY : gl.TEXTURE_2D; + this.formatInfo = TextureFormats[format]; + updateObject(this, { gl, _tag: tag, format, layern, wrap, depth }); + this.update(size, data); + } + update(size, data) { + const { gl, handle, gltarget, layern } = this; + const { internalFormat, glformat, type } = this.formatInfo; + const [w, h] = size; + gl.bindTexture(gltarget, handle); + if (!layern) { + gl.texImage2D( + gltarget, + 0 /*mip level*/, + internalFormat, + w, + h, + 0 /*border*/, + glformat, + type, + data /*data*/ + ); + } else { + gl.texImage3D( + gltarget, + 0 /*mip level*/, + internalFormat, + w, + h, + layern, + 0 /*border*/, + glformat, + type, + data /*data*/ + ); + } + gl.bindTexture(gltarget, null); + this.size = size; + if (this.depth) { + this.depth.update(size, data); + } + } + attach(gl) { + if (!this.layern) { + const attachment = this.format == 'depth' ? gl.DEPTH_ATTACHMENT : gl.COLOR_ATTACHMENT0; + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, this.handle, 0 /*level*/); + } else { + const drawBuffers = []; + for (let i = 0; i < this.layern; ++i) { + const attachment = gl.COLOR_ATTACHMENT0 + i; + drawBuffers.push(attachment); + gl.framebufferTextureLayer(gl.FRAMEBUFFER, attachment, this.handle, 0 /*level*/, i); + } + gl.drawBuffers(drawBuffers); + } + } + bindTarget(gl, readonly = false) { + if (this.fbo) { + gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); + } else { + this.fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); + this.attach(gl); + if (this.depth) this.depth.attach(gl); + } + if (!readonly) { + this.handle.hasMipmap = false; + } + return this.size; + } + _getBox(box) { + box = box && box.length ? box : [0, 0, ...this.size]; + const [x, y, w, h] = box, + n = w * h * this.formatInfo.chn; + return { box, n }; + } + _getCPUBuf(n) { + if (!this.cpu || this.cpu.length < n) { + this.cpu = new this.formatInfo.CpuArray(n); + } + return this.cpu.length == n ? this.cpu : this.cpu.subarray(0, n); + } + _readPixels(box, targetBuf) { + const { glformat, type } = this.formatInfo; + this.bindTarget(this.gl, /*readonly*/ true); + this.gl.readPixels(...box, glformat, type, targetBuf); + } + readSync(...optBox) { + const { box, n } = this._getBox(optBox); + const buf = this._getCPUBuf(n); + this._readPixels(box, buf); + return buf; + } + _bindAsyncBuffer(n) { + const { gl } = this; + const { CpuArray } = this.formatInfo; + if (!this.async) { + this.async = { all: new Set(), queue: [] }; + } + if (this.async.queue.length == 0) { + const gpuBuf = gl.createBuffer(); + this.async.queue.push(gpuBuf); + this.async.all.add(gpuBuf); + } + const gpuBuf = this.async.queue.shift(); + if (this.async.queue.length > 6) { + this._deleteAsyncBuf(this.async.queue.pop()); + } + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, gpuBuf); + if (!gpuBuf.length || gpuBuf.length < n) { + const byteN = n * this.formatInfo.CpuArray.BYTES_PER_ELEMENT; + gl.bufferData(gl.PIXEL_PACK_BUFFER, byteN, gl.STREAM_READ); + gpuBuf.length = n; + console.log(`created/resized async gpu buffer "${this._tag}":`, gpuBuf); + } + return gpuBuf; + } + _deleteAsyncBuf(gpuBuf) { + delete gpuBuf.length; + this.gl.deleteBuffer(gpuBuf); + this.async.all.delete(gpuBuf); + } + // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#use_non-blocking_async_data_readback + read(callback, optBox, optTarget) { + const { gl } = this; + const { box, n } = this._getBox(optBox); + const gpuBuf = this._bindAsyncBuffer(n); + this._readPixels(box, 0); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); + gl.flush(); + this._asyncFetch(gpuBuf, sync, callback, optTarget); + } + _asyncFetch(gpuBuf, sync, callback, optTarget) { + const { gl } = this; + if (!gpuBuf.length) { + // check that gpu buffer is not deleted + gl.deleteSync(sync); + return; + } + const res = gl.clientWaitSync(sync, 0, 0); + if (res === gl.TIMEOUT_EXPIRED) { + setTimeout(() => this._asyncFetch(gpuBuf, sync, callback, optTarget), 1 /*ms*/); + return; + } + if (res === gl.WAIT_FAILED) { + console.log(`async read of ${this._tag} failed`); + } else { + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, gpuBuf); + const target = optTarget || this._getCPUBuf(gpuBuf.length); + gl.getBufferSubData( + gl.PIXEL_PACK_BUFFER, + 0 /*srcOffset*/, + target, + 0 /*dstOffset*/, + gpuBuf.length /*length*/ + ); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + callback(target); + } + gl.deleteSync(sync); + this.async.queue.push(gpuBuf); + } + free() { + const gl = this.gl; + if (this.depth) this.depth.free(); + if (this.fbo) gl.deleteFramebuffer(this.fbo); + if (this.async) this.async.all.forEach((buf) => this._deleteAsyncBuf(buf)); + gl.deleteTexture(this.handle); + } } function calcAspect(aspect, w, h) { - if (!aspect) return [1,1]; - let c; - switch (aspect) { - case 'fit': c = Math.min(w, h); break; - case 'cover': c = Math.max(w, h); break; - case 'x': c = w; break; - case 'y': c = h; break; - case 'mean': c = (w+h)/2; break; - default: throw `Unknown aspect mode "${aspect}"`; - } - return [c/w, c/h]; + if (!aspect) return [1, 1]; + let c; + switch (aspect) { + case 'fit': + c = Math.min(w, h); + break; + case 'cover': + c = Math.max(w, h); + break; + case 'x': + c = w; + break; + case 'y': + c = h; + break; + case 'mean': + c = (w + h) / 2; + break; + default: + throw `Unknown aspect mode "${aspect}"`; + } + return [c / w, c / h]; } function ensureVertexArray(gl, neededSize) { - // gl_VertexID / gl_InstanceID seem to be broken in some configurations - // (e.g. https://crbug.com/1315104), so I had to fallback to using arrays - if (gl._indexVA && neededSize <= gl._indexVA.size) - return; - const size = neededSize*2; - - const va = gl._indexVA || gl.createVertexArray(); - va.size = size; - gl._indexVA = va; - gl.bindVertexArray(va); - - const arr = new Int32Array(size); - arr.forEach((v, i)=>{arr[i] = i}); - - const buf = va.buf || gl.createBuffer(); - va.buf = buf; - gl.bindBuffer(gl.ARRAY_BUFFER, buf); - gl.bufferData(gl.ARRAY_BUFFER, arr, gl.STATIC_DRAW); - - for (let loc=0; loc<2; ++loc) { - gl.enableVertexAttribArray(loc); - gl.vertexAttribIPointer(loc, 1/*size*/, gl.INT, - false/*normalize*/, 0/*stride*/, 0/*offset*/); - } - gl.vertexAttribDivisor(1, 1); - - gl.bindBuffer(gl.ARRAY_BUFFER, null); - gl.bindVertexArray(null); - - console.log('created:', va); + // gl_VertexID / gl_InstanceID seem to be broken in some configurations + // (e.g. https://crbug.com/1315104), so I had to fallback to using arrays + if (gl._indexVA && neededSize <= gl._indexVA.size) return; + const size = neededSize * 2; + + const va = gl._indexVA || gl.createVertexArray(); + va.size = size; + gl._indexVA = va; + gl.bindVertexArray(va); + + const arr = new Int32Array(size); + arr.forEach((v, i) => { + arr[i] = i; + }); + + const buf = va.buf || gl.createBuffer(); + va.buf = buf; + gl.bindBuffer(gl.ARRAY_BUFFER, buf); + gl.bufferData(gl.ARRAY_BUFFER, arr, gl.STATIC_DRAW); + + for (let loc = 0; loc < 2; ++loc) { + gl.enableVertexAttribArray(loc); + gl.vertexAttribIPointer( + loc, + 1 /*size*/, + gl.INT, + false /*normalize*/, + 0 /*stride*/, + 0 /*offset*/ + ); + } + gl.vertexAttribDivisor(1, 1); + + gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.bindVertexArray(null); + + console.log('created:', va); } -function getTargetSize(gl, {size, scale=1, data}) { - if (!size && (data && data.videoWidth && data.videoHeight)) { - size = [data.videoWidth, data.videoHeight]; - } - size = size || [gl.canvas.width, gl.canvas.height]; - return [Math.ceil(size[0]*scale), Math.ceil(size[1]*scale)]; +function getTargetSize(gl, { size, scale = 1, data }) { + if (!size && data && data.videoWidth && data.videoHeight) { + size = [data.videoWidth, data.videoHeight]; + } + size = size || [gl.canvas.width, gl.canvas.height]; + return [Math.ceil(size[0] * scale), Math.ceil(size[1] * scale)]; } function createTarget(gl, params) { - if (!params.story) return new TextureTarget(gl, params); - return Array(params.story).fill(0).map(_=>new TextureTarget(gl, params)); + if (!params.story) return new TextureTarget(gl, params); + return Array(params.story) + .fill(0) + .map((_) => new TextureTarget(gl, params)); } function prepareOwnTarget(self, spec) { - const buffers = self.buffers; - spec.size = getTargetSize(self.gl, spec); - if (!buffers[spec.tag]) { - const target = buffers[spec.tag] = createTarget(self.gl, spec); - console.log('created', target); - } - const target = buffers[spec.tag]; - const tex = Array.isArray(target) ? target[target.length-1] : target; - const needResize = tex.size[0] != spec.size[0] || tex.size[1] != spec.size[1]; - if (needResize || spec.data) { - if (needResize) { - console.log(`resizing "${spec.tag}" (${tex.size})->(${spec.size})`); - } - tex.update(spec.size, spec.data); - } - return buffers[spec.tag]; + const buffers = self.buffers; + spec.size = getTargetSize(self.gl, spec); + if (!buffers[spec.tag]) { + const target = (buffers[spec.tag] = createTarget(self.gl, spec)); + console.log('created', target); + } + const target = buffers[spec.tag]; + const tex = Array.isArray(target) ? target[target.length - 1] : target; + const needResize = tex.size[0] != spec.size[0] || tex.size[1] != spec.size[1]; + if (needResize || spec.data) { + if (needResize) { + console.log(`resizing "${spec.tag}" (${tex.size})->(${spec.size})`); + } + tex.update(spec.size, spec.data); + } + return buffers[spec.tag]; } function bindTarget(gl, target) { - if (!target) { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - return [gl.canvas.width, gl.canvas.height]; - } - if (Array.isArray(target)) { - target.unshift(target = target.pop()); - } - return target.bindTarget(gl) + if (!target) { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + return [gl.canvas.width, gl.canvas.height]; + } + if (Array.isArray(target)) { + target.unshift((target = target.pop())); + } + return target.bindTarget(gl); } const OptNames = new Set([ - 'Inc', 'VP', 'FP', - 'Clear', 'Blend', 'View', 'Grid', 'Mesh', 'Aspect', 'DepthTest', 'AlphaCoverage', 'Face' + 'Inc', + 'VP', + 'FP', + 'Clear', + 'Blend', + 'View', + 'Grid', + 'Mesh', + 'Aspect', + 'DepthTest', + 'AlphaCoverage', + 'Face' ]); function drawQuads(self, params, target) { - const options={}, uniforms={} - for (const p in params) { - (OptNames.has(p)?options:uniforms)[p] = params[p]; - } - const [Inc, VP, FP] = [options.Inc||'', options.VP||'', options.FP||'']; - const noShader = !VP && !FP; - const noDraw = (options.Clear === undefined) && noShader; - - // setup target - if (target && target.tag) { - target = prepareOwnTarget(self, target); - if (noDraw) return target; - } - if (Array.isArray(target)) { - uniforms.Src = uniforms.Src || target[0]; - } - - // bind (and clear) target - const gl = self.gl; - const targetSize = bindTarget(gl, target); - let view = options.View || [0, 0, targetSize[0], targetSize[1]]; - if (view.length == 2) { - view = [0, 0, view[0], view[1]] - } - gl.depthMask(!(options.DepthTest == 'keep')); - if ((typeof options.Clear === 'number') || Array.isArray(options.Clear)) { - let clear = options.Clear; - if (typeof clear === 'number') { - clear = [clear, clear, clear, clear]; - } - gl.clearColor(...clear); - gl.enable(gl.SCISSOR_TEST); - gl.scissor(...view); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.disable(gl.SCISSOR_TEST); - } - - // setup program - if (noShader) { - return target; - } - const shaderID = Inc+VP+FP; - if (!(shaderID in self.shaders)) { - self.shaders[shaderID] = linkShader(gl, uniforms, Inc, VP, FP); - } - const prog = self.shaders[shaderID]; - gl.useProgram(prog); - - // process options - if (options.Blend) { - const blend = parseBlend(options.Blend); - const {s, d, f}=blend; - gl.enable(gl.BLEND); - gl.blendFunc(s, d); - gl.blendEquation(f); - } - if (options.DepthTest) { - gl.enable(gl.DEPTH_TEST); - } - if (options.Face) { - gl.enable(gl.CULL_FACE); - const mode = {'front':gl.BACK, 'back':gl.FRONT}[options.Face]; - gl.cullFace(mode); - } - if (options.AlphaCoverage) { - gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE); - } - - // View, Aspect - gl.viewport(...view) - const width=view[2], height=view[3]; - uniforms.View = view; - uniforms.Aspect = calcAspect(options.Aspect, width, height); - - // Grid, Mesh - const [gx=1, gy=1, gz=1] = options.Grid || []; - uniforms.Grid = [gx, gy, gz]; - uniforms.Mesh = options.Mesh || [1, 1]; // 3d for cube? - const vertN = (uniforms.Mesh[0]*2+3)*uniforms.Mesh[1]-1; - const instN = gx*gy*gz; - ensureVertexArray(gl, Math.max(vertN, instN)); - gl.bindVertexArray(gl._indexVA); - - // setup uniforms and textures - Object.entries(prog.setters).forEach(([name, f])=>f(uniforms[name])); - // draw - gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, vertN, instN); - - // revert gl state - if (options.Blend) gl.disable(gl.BLEND); - if (options.DepthTest) gl.disable(gl.DEPTH_TEST); - if (options.Face) gl.disable(gl.CULL_FACE); - if (options.AlphaCoverage) gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); - gl.bindVertexArray(null); - return target; + const options = {}, + uniforms = {}; + for (const p in params) { + (OptNames.has(p) ? options : uniforms)[p] = params[p]; + } + const [Inc, VP, FP] = [options.Inc || '', options.VP || '', options.FP || '']; + const noShader = !VP && !FP; + const noDraw = options.Clear === undefined && noShader; + + // setup target + if (target && target.tag) { + target = prepareOwnTarget(self, target); + if (noDraw) return target; + } + if (Array.isArray(target)) { + uniforms.Src = uniforms.Src || target[0]; + } + + // bind (and clear) target + const gl = self.gl; + const targetSize = bindTarget(gl, target); + let view = options.View || [0, 0, targetSize[0], targetSize[1]]; + if (view.length == 2) { + view = [0, 0, view[0], view[1]]; + } + gl.depthMask(!(options.DepthTest == 'keep')); + if (typeof options.Clear === 'number' || Array.isArray(options.Clear)) { + let clear = options.Clear; + if (typeof clear === 'number') { + clear = [clear, clear, clear, clear]; + } + gl.clearColor(...clear); + gl.enable(gl.SCISSOR_TEST); + gl.scissor(...view); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.disable(gl.SCISSOR_TEST); + } + + // setup program + if (noShader) { + return target; + } + const shaderID = Inc + VP + FP; + if (!(shaderID in self.shaders)) { + self.shaders[shaderID] = linkShader(gl, uniforms, Inc, VP, FP); + } + const prog = self.shaders[shaderID]; + gl.useProgram(prog); + + // process options + if (options.Blend) { + const blend = parseBlend(options.Blend); + const { s, d, f } = blend; + gl.enable(gl.BLEND); + gl.blendFunc(s, d); + gl.blendEquation(f); + } + if (options.DepthTest) { + gl.enable(gl.DEPTH_TEST); + } + if (options.Face) { + gl.enable(gl.CULL_FACE); + const mode = { front: gl.BACK, back: gl.FRONT }[options.Face]; + gl.cullFace(mode); + } + if (options.AlphaCoverage) { + gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE); + } + + // View, Aspect + gl.viewport(...view); + const width = view[2], + height = view[3]; + uniforms.View = view; + uniforms.Aspect = calcAspect(options.Aspect, width, height); + + // Grid, Mesh + const [gx = 1, gy = 1, gz = 1] = options.Grid || []; + uniforms.Grid = [gx, gy, gz]; + uniforms.Mesh = options.Mesh || [1, 1]; // 3d for cube? + const vertN = (uniforms.Mesh[0] * 2 + 3) * uniforms.Mesh[1] - 1; + const instN = gx * gy * gz; + ensureVertexArray(gl, Math.max(vertN, instN)); + gl.bindVertexArray(gl._indexVA); + + // setup uniforms and textures + Object.entries(prog.setters).forEach(([name, f]) => f(uniforms[name])); + // draw + gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, vertN, instN); + + // revert gl state + if (options.Blend) gl.disable(gl.BLEND); + if (options.DepthTest) gl.disable(gl.DEPTH_TEST); + if (options.Face) gl.disable(gl.CULL_FACE); + if (options.AlphaCoverage) gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); + gl.bindVertexArray(null); + return target; } function wrapSwissGL(hook) { - const glsl = this; - const f = (params, target)=>hook(glsl, params, target); - f.hook = wrapSwissGL; - f.gl = glsl.gl; - return f; + const glsl = this; + const f = (params, target) => hook(glsl, params, target); + f.hook = wrapSwissGL; + f.gl = glsl.gl; + return f; } function SwissGL(canvas_gl) { - const gl = canvas_gl.getContext ? - canvas_gl.getContext('webgl2', {alpha:false, antialias:true}) : canvas_gl; - gl.getExtension("EXT_color_buffer_float"); - gl.getExtension("OES_texture_float_linear"); - gl.pixelStorei(gl.PACK_ALIGNMENT, 1); - gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); - ensureVertexArray(gl, 1024); - const glsl = (params, target)=>drawQuads(glsl, params, target); - glsl.hook = wrapSwissGL; - - glsl.gl = gl; - glsl.shaders = {}; - glsl.buffers = {}; - glsl.reset = ()=>{ - Object.values(glsl.shaders).forEach(prog=>gl.deleteProgram(prog)); - Object.values(glsl.buffers).flat().forEach(target=>target.free()); - glsl.shaders = {}; - glsl.buffers = {}; - }; - glsl.adjustCanvas = dpr=>{ - dpr = dpr || self.devicePixelRatio; - const canvas = gl.canvas; - const w = canvas.clientWidth*dpr, h=canvas.clientHeight*dpr; - if (canvas.width != w || canvas.height != h) { - canvas.width = w; canvas.height = h; - } - } - glsl.loop = callback=>{ - const frameFunc = time=>{ - const res = callback({glsl, time:time/1000.0}); - if (res != 'stop') requestAnimationFrame(frameFunc); - }; - requestAnimationFrame(frameFunc); - }; - return glsl; + const gl = canvas_gl.getContext + ? canvas_gl.getContext('webgl2', { alpha: false, antialias: true }) + : canvas_gl; + gl.getExtension('EXT_color_buffer_float'); + gl.getExtension('OES_texture_float_linear'); + gl.pixelStorei(gl.PACK_ALIGNMENT, 1); + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); + ensureVertexArray(gl, 1024); + const glsl = (params, target) => drawQuads(glsl, params, target); + glsl.hook = wrapSwissGL; + + glsl.gl = gl; + glsl.shaders = {}; + glsl.buffers = {}; + glsl.reset = () => { + Object.values(glsl.shaders).forEach((prog) => gl.deleteProgram(prog)); + Object.values(glsl.buffers) + .flat() + .forEach((target) => target.free()); + glsl.shaders = {}; + glsl.buffers = {}; + }; + glsl.adjustCanvas = (dpr) => { + dpr = dpr || self.devicePixelRatio; + const canvas = gl.canvas; + const w = canvas.clientWidth * dpr, + h = canvas.clientHeight * dpr; + if (canvas.width != w || canvas.height != h) { + canvas.width = w; + canvas.height = h; + } + }; + glsl.loop = (callback) => { + const frameFunc = (time) => { + const res = callback({ glsl, time: time / 1000.0 }); + if (res != 'stop') requestAnimationFrame(frameFunc); + }; + requestAnimationFrame(frameFunc); + }; + return glsl; } self._SwissGL = SwissGL; From bc45da0f57fd4d7b4352e2576f18fd1575e2bad6 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 20:13:52 +0100 Subject: [PATCH 12/17] Rewrite lib in ts --- src/lib/index.ts | 3 +- src/lib/swissgl.ts | 563 +++++++++++++++++++++++++++++++-------------- 2 files changed, 395 insertions(+), 171 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 47d3c46..c572590 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1 +1,2 @@ -// Reexport your entry components here +export * from './swissgl.js'; +export { SwissGL as default } from './swissgl.js'; diff --git a/src/lib/swissgl.ts b/src/lib/swissgl.ts index 364267e..04588ed 100644 --- a/src/lib/swissgl.ts +++ b/src/lib/swissgl.ts @@ -1,4 +1,5 @@ // Copyright 2023 Google LLC +// Copyright 2023 João Paquim // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,18 +32,66 @@ // - tag already exists // - texture/array uniform compatibility -const Type2Setter = {}; -const UniformType2TexTarget = {}; -const TextureFormats = {}; -{ - const GL = WebGL2RenderingContext; - for (const t of ['FLOAT', 'INT', 'BOOL']) { +export const GL = globalThis.WebGL2RenderingContext; +export type GL = WebGL2RenderingContext; + +type S = + | 'BOOL' + | 'BOOL_VEC2' + | 'BOOL_VEC3' + | 'BOOL_VEC4' + | 'INT' + | 'INT_VEC2' + | 'INT_VEC3' + | 'INT_VEC4' + | 'FLOAT' + | 'FLOAT_VEC2' + | 'FLOAT_VEC3' + | 'FLOAT_VEC4' + | 'FLOAT_MAT2' + | 'FLOAT_MAT3' + | 'FLOAT_MAT4'; + +const Type2Setter = {} as Record; +const UniformType2TexTarget = {} as Record< + GL['SAMPLER_2D' | 'SAMPLER_2D_ARRAY'], + GL['TEXTURE_2D' | 'TEXTURE_2D_ARRAY'] +>; + +type CpuArray = Uint8Array | Uint16Array | Float32Array | Uint32Array; +type CpuArrayConstructor = + | Uint8ArrayConstructor + | Uint16ArrayConstructor + | Float32ArrayConstructor + | Uint32ArrayConstructor; + +type TextureFormat = { + internalFormat: GL[ + | 'R8' + | 'RGBA8' + | 'R16F' + | 'RGBA16F' + | 'R32F' + | 'RG32F' + | 'RGBA32F' + | 'DEPTH_COMPONENT24']; + glformat: GL['RED' | 'RGBA' | 'RG' | 'DEPTH_COMPONENT']; + type: GL['UNSIGNED_BYTE' | 'HALF_FLOAT' | 'FLOAT' | 'UNSIGNED_INT']; + CpuArray: CpuArrayConstructor; + chn: 1 | 2 | 4; + // chn: number; +}; +const TextureFormats = {} as Record; + +let inited = false; +function init() { + for (const t of ['FLOAT', 'INT', 'BOOL'] as const) { const suf = t == 'FLOAT' ? 'f' : 'i'; Type2Setter[GL[t]] = 'uniform1' + suf; - for (const i of [2, 3, 4]) { + for (const i of [2, 3, 4] as const) { Type2Setter[GL[`${t}_VEC${i}`]] = `uniform${i}${suf}v`; if (suf == 'f') { - Type2Setter[GL[`${t}_MAT${i}`]] = `uniformMatrix${i}fv`; + Type2Setter[GL[`${t}_MAT${i}` as S]] = `uniformMatrix${i}fv`; } } } @@ -58,32 +107,37 @@ const TextureFormats = {}; ['rg32f', GL.RG32F, GL.RG, GL.FLOAT, Float32Array, 2], ['rgba32f', GL.RGBA32F, GL.RGBA, GL.FLOAT, Float32Array, 4], ['depth', GL.DEPTH_COMPONENT24, GL.DEPTH_COMPONENT, GL.UNSIGNED_INT, Uint32Array, 1] - ]) + ] as const) TextureFormats[name] = { internalFormat, glformat, type, CpuArray, chn }; + inited = true; } -function memoize(f) { - const cache = {}; - const wrap = (k) => (k in cache ? cache[k] : (cache[k] = f(k))); +function memoize(f: (k: string) => T) { + const cache: Record = {}; + const wrap = (k: string) => (k in cache ? cache[k] : (cache[k] = f(k))); wrap.cache = cache; return wrap; } -function updateObject(o, updates) { +export function updateObject>(o: T, updates: Partial) { for (const s in updates) { - o[s] = updates[s]; + o[s] = updates[s]!; } return o; } +type Res = { + s: number; + d: number; + f: number; +}; // Parse strings like 'min(s,d)', 'max(s,d)', 's*d', 's+d*(1-sa)', // 's*d', 'd*(1-sa) + s*sa', s-d', 'd-s' and so on into // gl.blendFunc/gl.blendEquation arguments. -function parseBlend(s0) { +function parseBlendImpl(s0?: string): Res | null | undefined { if (!s0) return; let s = s0.replace(/\s+/g, ''); if (!s) return null; - const GL = WebGL2RenderingContext; const func2gl = { min: GL.MIN, max: GL.MAX, @@ -92,8 +146,8 @@ function parseBlend(s0) { 'd-s': GL.FUNC_REVERSE_SUBTRACT }; const factor2gl = { - '0': GL.ZERO, - '1': GL.ONE, + 0: GL.ZERO, + 1: GL.ONE, s: GL.SRC_COLOR, '(1-s)': GL.ONE_MINUS_SRC_COLOR, d: GL.DST_COLOR, @@ -107,32 +161,31 @@ function parseBlend(s0) { ca: GL.CONSTANT_ALPHA, '(1-ca)': GL.ONE_MINUS_CONSTANT_ALPHA }; - const res = { s: GL.ZERO, d: GL.ZERO, f: null }; - s = s.replace(/(s|d)(?:\*(\w+|\(1-\w+\)))?/g, (_, term, factor) => { - factor = factor || '1'; + const res = { s: GL.ZERO, d: GL.ZERO } as Res; + s = s.replace(/(s|d)(?:\*(\w+|\(1-\w+\)))?/g, (_, term: string, factor = '1') => { if (!(factor in factor2gl)) { throw `Unknown blend factor: "${factor}"`; } - res[term] = factor2gl[factor]; + res[term as keyof Res] = factor2gl[factor as keyof typeof factor2gl]; return term; }); let m; if ((m = s.match(/^(min|max)\((s,d|d,s)\)$/))) { - res.f = func2gl[m[1]]; + res.f = func2gl[m[1] as keyof typeof func2gl]; } else if (s.match(/^(s|d|s\+d|d\+s)$/)) { res.f = func2gl['+']; } else if (s in func2gl) { - res.f = func2gl[s]; + res.f = func2gl[s as keyof typeof func2gl]; } else { throw `Unable to parse blend spec: "${s0}"`; } return res; } -parseBlend = memoize(parseBlend); +const parseBlend = memoize(parseBlendImpl); -function compileShader(gl, code, type, program) { +function compileShader(gl: GL, code: string, type: number, program: WebGLProgram) { code = '#version 300 es\n' + code; - const shader = gl.createShader(type); + const shader = gl.createShader(type)!; gl.shaderSource(shader, code); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { @@ -146,8 +199,10 @@ function compileShader(gl, code, type, program) { gl.deleteShader(shader); } -function compileProgram(gl, vs, fs) { - const program = gl.createProgram(); +type Program = WebGLProgram & { setters: Record void> }; + +function compileProgram(gl: GL, vs: string, fs: string) { + const program = gl.createProgram() as Program; compileShader(gl, vs, gl.VERTEX_SHADER, program); compileShader(gl, fs, gl.FRAGMENT_SHADER, program); gl.linkProgram(program); @@ -159,27 +214,28 @@ function compileProgram(gl, vs, fs) { let unitCount = 0; const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < numUniforms; ++i) { - const info = gl.getActiveUniform(program, i); + const info = gl.getActiveUniform(program, i)!; const loc = gl.getUniformLocation(program, info.name); - const name = info.name.match(/^\w+/)[0]; + const name = info.name.match(/^\w+/)![0]; if (info.type in UniformType2TexTarget) { const unit = unitCount++; - const target = UniformType2TexTarget[info.type]; + const target = UniformType2TexTarget[info.type as keyof typeof UniformType2TexTarget]; gl.uniform1i(loc, unit); program.setters[name] = (tex) => { gl.activeTexture(gl.TEXTURE0 + unit); tex ? tex.bindSampler(unit) : gl.bindTexture(target, null); }; } else { - const fname = Type2Setter[info.type]; + const fname = Type2Setter[info.type as keyof typeof Type2Setter]; const setter = fname.startsWith('uniformMatrix') - ? (v) => gl[fname](loc, false, v) - : (v) => gl[fname](loc, v); + ? (v: Iterable) => + gl[fname as 'uniformMatrix2fv' | 'uniformMatrix3fv' | 'uniformMatrix4fv'](loc, false, v) + : (v: number & Iterable) => gl[fname as 'uniform1f' | 'uniform1i'](loc, v); program.setters[name] = (v) => (v != undefined ? setter(v) : null); } } gl.useProgram(null); - console.log('created', program); + console.debug('created', program); return program; } @@ -290,7 +346,7 @@ vec4 _sample(sampler2DArray tex, ivec2 xy, int layer) {return texelFetch(tex, iv #endif `; -function guessUniforms(params) { +function guessUniforms(params: Record) { const uni = []; const len2type = { 1: 'float', 2: 'vec2', 3: 'vec3', 4: 'vec4', 9: 'mat3', 16: 'mat4' }; for (const name in params) { @@ -310,38 +366,36 @@ function guessUniforms(params) { } else if (typeof v === 'boolean') { s = `uniform bool ${name};`; } else if (v.length in len2type) { - s = `uniform ${len2type[v.length]} ${name};`; + s = `uniform ${len2type[v.length as keyof typeof len2type]} ${name};`; } if (s) uni.push(s); } return uni.join('\n') + '\n'; } -const stripComments = (code) => code.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, ''); +const stripComments = (code: string) => code.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, ''); // TODO better parser (use '\b') -function definedUniforms(code) { +function definedUniforms(code: string) { code = stripComments(code); const lines = Array.from(code.matchAll(/uniform\s+\w+\s+([^;]+)\s*;/g)); return new Set(lines.map((m) => m[1].split(/[^\w]+/)).flat()); } -function expandCode(code, mainFunc, outVar) { +function expandCode(code: string, mainFunc: string, outVar: string) { const stripped = stripComments(code).trim(); if (stripped != '' && stripped.indexOf(';') == -1) { code = `${outVar} = vec4(${stripped});`; } if (!stripped.match(new RegExp(`\\b${mainFunc}\s*\\(`))) { - code = `void ${mainFunc}() { - ${code}; - }`; + code = `void ${mainFunc}() { ${code}; }`; } return code; } const expandVP = memoize((code) => expandCode(code, 'vertex', 'VPos')); const expandFP = memoize((code) => expandCode(code, 'fragment', 'FOut')); -function extractVaryings(VP) { +function extractVaryings(VP: string) { return Array.from(stripComments(VP).matchAll(/\bvarying\s+[^;]+;/g)) .map((m) => m[0]) .map((s) => { @@ -351,11 +405,11 @@ function extractVaryings(VP) { .join('\n'); } -function stripVaryings(VP) { +function stripVaryings(VP: string) { return VP.replace(/\bvarying\s+\w+/g, ''); } -function linkShader(gl, uniforms, Inc, VP, FP) { +function linkShader(gl: GL, uniforms: Record, Inc: string, VP: string, FP: string) { const defined = definedUniforms([glsl_template, Inc, VP, FP].join('\n')); const undefined = Object.entries(uniforms) .filter((kv) => kv[0].match(/^\w+$/)) @@ -367,36 +421,61 @@ function linkShader(gl, uniforms, Inc, VP, FP) { return compileProgram( gl, ` - #define VERT - ${prefix}\n${VP} - void main() { - int rowVertN = Mesh.x*2+3; - int rowI = VertexID/rowVertN; - int rowVertI = min(VertexID%rowVertN, rowVertN-2); - int odd = rowI%2; - if (odd==0) rowVertI = rowVertN-rowVertI-2; - VID = ivec2(rowVertI>>1, rowI + (rowVertI+odd+1)%2); - int ii = InstanceID; - ID.x = ii % Grid.x; ii/=Grid.x; - ID.y = ii % Grid.y; ii/=Grid.y; - ID.z = ii; - UV = vec2(VID) / vec2(Mesh); - VPos = vec4(XY,0,1); - vertex(); - VPos.xy *= Aspect; - }`, +#define VERT +${prefix}\n${VP} +void main() { + int rowVertN = Mesh.x*2+3; + int rowI = VertexID/rowVertN; + int rowVertI = min(VertexID%rowVertN, rowVertN-2); + int odd = rowI%2; + if (odd==0) rowVertI = rowVertN-rowVertI-2; + VID = ivec2(rowVertI>>1, rowI + (rowVertI+odd+1)%2); + int ii = InstanceID; + ID.x = ii % Grid.x; ii/=Grid.x; + ID.y = ii % Grid.y; ii/=Grid.y; + ID.z = ii; + UV = vec2(VID) / vec2(Mesh); + VPos = vec4(XY,0,1); + vertex(); + VPos.xy *= Aspect; +}`, ` - #define FRAG - ${prefix}\n${expandFP(FP)} - void main() { - I = ivec2(gl_FragCoord.xy); - fragment(); - }` +#define FRAG +${prefix}\n${expandFP(FP)} +void main() { + I = ivec2(gl_FragCoord.xy); + fragment(); +}` ); } -class TextureSampler { - fork(updates) { +export type Filter = 'linear' | 'nearest' | 'miplinear'; +export type Wrap = 'edge' | 'repeat' | 'mirror'; + +type TextureSamplerCore = { + gl: GL & { _samplers?: Record }; + handle: WebGLTexture & { hasMipmap?: boolean }; + gltarget: number; + layern: number | null; + filter: Filter; + wrap: Wrap; +}; + +class TextureSampler implements TextureSamplerCore { + // @ts-ignore + gl: GL & { _samplers?: Record }; + // @ts-ignore + handle: WebGLTexture & { hasMipmap?: boolean }; + // @ts-ignore + gltarget: number; + // @ts-ignore + layern: number | null; + // filter: 'linear' | 'nearest' | 'miplinear'; + // @ts-ignore + filter: Filter; + // @ts-ignore + wrap: Wrap; + fork(updates: Partial) { const { gl, handle, gltarget, layern, filter, wrap } = { ...this, ...updates }; return updateObject(new TextureSampler(), { gl, handle, gltarget, layern, filter, wrap }); } @@ -434,8 +513,18 @@ class TextureSampler { const glwrap = { repeat: gl.REPEAT, edge: gl.CLAMP_TO_EDGE, mirror: gl.MIRRORED_REPEAT }[ wrap ]; - const sampler = gl.createSampler(); - const setf = (k, v) => gl.samplerParameteri(sampler, gl['TEXTURE_' + k], v); + const sampler = gl.createSampler()!; + type PName = + | 'COMPARE_FUNC' + | 'COMPARE_MODE' + | 'MAG_FILTER' + | 'MAX_LOD' + | 'MIN_FILTER' + | 'MIN_LOD' + | 'WRAP_R' + | 'WRAP_S' + | 'WRAP_T'; + const setf = (k: PName, v: number) => gl.samplerParameteri(sampler, gl[`TEXTURE_${k}`], v); setf('MIN_FILTER', glfilter); setf('MAG_FILTER', filter == 'miplinear' ? gl.LINEAR : glfilter); setf('WRAP_S', glwrap); @@ -444,7 +533,7 @@ class TextureSampler { } return gl._samplers[id]; } - bindSampler(unit) { + bindSampler(unit: number) { // assume unit is already active const { gl, gltarget, handle } = this; gl.bindTexture(gltarget, handle); @@ -456,8 +545,33 @@ class TextureSampler { } } -class TextureTarget extends TextureSampler { - constructor(gl, params) { +type GpuBuf = WebGLBuffer & { length?: number }; + +type TargetParams = { + size: [number, number]; + tag: string; + format?: string; + filter?: Filter; + wrap?: Wrap; + layern?: number | null; + data?: ArrayBufferView | null; + depth?: TextureTarget | null; +}; + +export class TextureTarget extends TextureSampler { + // @ts-ignore + size: [number, number]; + // @ts-ignore + _tag: string; + // @ts-ignore + format: string; + formatInfo: TextureFormat; + // @ts-ignore + depth: TextureTarget | null; + fbo?: WebGLFramebuffer; + cpu?: CpuArray; + async?: { all: Set; queue: GpuBuf[] }; + constructor(gl: GL, params: TargetParams) { super(); let { size, @@ -480,13 +594,13 @@ class TextureTarget extends TextureSampler { depth: null }); } - (this.handle = gl.createTexture()), (this.filter = format == 'depth' ? 'nearest' : filter); + (this.handle = gl.createTexture()!), (this.filter = format == 'depth' ? 'nearest' : filter); this.gltarget = layern ? gl.TEXTURE_2D_ARRAY : gl.TEXTURE_2D; this.formatInfo = TextureFormats[format]; - updateObject(this, { gl, _tag: tag, format, layern, wrap, depth }); + updateObject(this, { gl, _tag: tag, format, layern, wrap, depth }); this.update(size, data); } - update(size, data) { + update(size: [number, number], data: ArrayBufferView | null) { const { gl, handle, gltarget, layern } = this; const { internalFormat, glformat, type } = this.formatInfo; const [w, h] = size; @@ -523,7 +637,7 @@ class TextureTarget extends TextureSampler { this.depth.update(size, data); } } - attach(gl) { + attach(gl: GL) { if (!this.layern) { const attachment = this.format == 'depth' ? gl.DEPTH_ATTACHMENT : gl.COLOR_ATTACHMENT0; gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, this.handle, 0 /*level*/); @@ -537,11 +651,11 @@ class TextureTarget extends TextureSampler { gl.drawBuffers(drawBuffers); } } - bindTarget(gl, readonly = false) { + bindTarget(gl: GL, readonly = false) { if (this.fbo) { gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); } else { - this.fbo = gl.createFramebuffer(); + this.fbo = gl.createFramebuffer()!; gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); this.attach(gl); if (this.depth) this.depth.attach(gl); @@ -551,70 +665,79 @@ class TextureTarget extends TextureSampler { } return this.size; } - _getBox(box) { + _getBox(box?: [number, number, number, number]) { box = box && box.length ? box : [0, 0, ...this.size]; const [x, y, w, h] = box, n = w * h * this.formatInfo.chn; return { box, n }; } - _getCPUBuf(n) { + _getCPUBuf(n: number): CpuArray { if (!this.cpu || this.cpu.length < n) { this.cpu = new this.formatInfo.CpuArray(n); } return this.cpu.length == n ? this.cpu : this.cpu.subarray(0, n); } - _readPixels(box, targetBuf) { + _readPixels(box: [number, number, number, number], targetBuf: ArrayBufferView | null) { const { glformat, type } = this.formatInfo; this.bindTarget(this.gl, /*readonly*/ true); this.gl.readPixels(...box, glformat, type, targetBuf); } - readSync(...optBox) { + readSync(...optBox: [number, number, number, number]): CpuArray { const { box, n } = this._getBox(optBox); const buf = this._getCPUBuf(n); this._readPixels(box, buf); return buf; } - _bindAsyncBuffer(n) { + _bindAsyncBuffer(n: number) { const { gl } = this; const { CpuArray } = this.formatInfo; if (!this.async) { this.async = { all: new Set(), queue: [] }; } if (this.async.queue.length == 0) { - const gpuBuf = gl.createBuffer(); + const gpuBuf = gl.createBuffer()!; this.async.queue.push(gpuBuf); this.async.all.add(gpuBuf); } - const gpuBuf = this.async.queue.shift(); + const gpuBuf = this.async.queue.shift()!; if (this.async.queue.length > 6) { - this._deleteAsyncBuf(this.async.queue.pop()); + this._deleteAsyncBuf(this.async.queue.pop()!); } gl.bindBuffer(gl.PIXEL_PACK_BUFFER, gpuBuf); if (!gpuBuf.length || gpuBuf.length < n) { const byteN = n * this.formatInfo.CpuArray.BYTES_PER_ELEMENT; gl.bufferData(gl.PIXEL_PACK_BUFFER, byteN, gl.STREAM_READ); gpuBuf.length = n; - console.log(`created/resized async gpu buffer "${this._tag}":`, gpuBuf); + console.debug(`created/resized async gpu buffer "${this._tag}":`, gpuBuf); } return gpuBuf; } - _deleteAsyncBuf(gpuBuf) { + _deleteAsyncBuf(gpuBuf: GpuBuf) { delete gpuBuf.length; this.gl.deleteBuffer(gpuBuf); - this.async.all.delete(gpuBuf); + this.async!.all.delete(gpuBuf); } // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#use_non-blocking_async_data_readback - read(callback, optBox, optTarget) { + read( + callback: (target: ArrayBufferView) => void, + optBox?: [number, number, number, number], + optTarget?: ArrayBufferView + ) { const { gl } = this; const { box, n } = this._getBox(optBox); const gpuBuf = this._bindAsyncBuffer(n); - this._readPixels(box, 0); + this._readPixels(box, null); gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); + const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0)!; gl.flush(); this._asyncFetch(gpuBuf, sync, callback, optTarget); } - _asyncFetch(gpuBuf, sync, callback, optTarget) { + _asyncFetch( + gpuBuf: GpuBuf, + sync: WebGLSync, + callback: (target: ArrayBufferView) => void, + optTarget?: ArrayBufferView + ) { const { gl } = this; if (!gpuBuf.length) { // check that gpu buffer is not deleted @@ -627,7 +750,7 @@ class TextureTarget extends TextureSampler { return; } if (res === gl.WAIT_FAILED) { - console.log(`async read of ${this._tag} failed`); + console.debug(`async read of ${this._tag} failed`); } else { gl.bindBuffer(gl.PIXEL_PACK_BUFFER, gpuBuf); const target = optTarget || this._getCPUBuf(gpuBuf.length); @@ -642,7 +765,7 @@ class TextureTarget extends TextureSampler { callback(target); } gl.deleteSync(sync); - this.async.queue.push(gpuBuf); + this.async!.queue.push(gpuBuf); } free() { const gl = this.gl; @@ -653,7 +776,9 @@ class TextureTarget extends TextureSampler { } } -function calcAspect(aspect, w, h) { +export type Aspect = 'fit' | 'cover' | 'mean' | 'x' | 'y'; + +function calcAspect(aspect: Aspect | null | undefined, w: number, h: number): [number, number] { if (!aspect) return [1, 1]; let c; switch (aspect) { @@ -678,13 +803,15 @@ function calcAspect(aspect, w, h) { return [c / w, c / h]; } -function ensureVertexArray(gl, neededSize) { +type VA = WebGLVertexArrayObject & { size: number; buf?: WebGLBuffer }; + +function ensureVertexArray(gl: GL & { _indexVA?: VA }, neededSize: number) { // gl_VertexID / gl_InstanceID seem to be broken in some configurations // (e.g. https://crbug.com/1315104), so I had to fallback to using arrays if (gl._indexVA && neededSize <= gl._indexVA.size) return; const size = neededSize * 2; - const va = gl._indexVA || gl.createVertexArray(); + const va = gl._indexVA || (gl.createVertexArray() as VA); va.size = size; gl._indexVA = va; gl.bindVertexArray(va); @@ -694,7 +821,7 @@ function ensureVertexArray(gl, neededSize) { arr[i] = i; }); - const buf = va.buf || gl.createBuffer(); + const buf = va.buf || gl.createBuffer()!; va.buf = buf; gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, arr, gl.STATIC_DRAW); @@ -705,7 +832,7 @@ function ensureVertexArray(gl, neededSize) { loc, 1 /*size*/, gl.INT, - false /*normalize*/, + // false /*normalize*/, 0 /*stride*/, 0 /*offset*/ ); @@ -715,49 +842,85 @@ function ensureVertexArray(gl, neededSize) { gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); - console.log('created:', va); + console.debug('created:', va); } -function getTargetSize(gl, { size, scale = 1, data }) { - if (!size && data && data.videoWidth && data.videoHeight) { +function getTargetSize( + gl: GL, + { + size, + scale = 1, + data + }: { + size?: [number, number]; + scale?: number; + data?: Target | null; + } +): [number, number] { + if (!size && data && data instanceof HTMLVideoElement) { size = [data.videoWidth, data.videoHeight]; } size = size || [gl.canvas.width, gl.canvas.height]; return [Math.ceil(size[0] * scale), Math.ceil(size[1] * scale)]; } -function createTarget(gl, params) { +type TargetResult = TextureTarget | TextureTarget[]; + +function createTarget(gl: GL, params: TargetParams & { story?: number }): TargetResult { if (!params.story) return new TextureTarget(gl, params); return Array(params.story) .fill(0) .map((_) => new TextureTarget(gl, params)); } -function prepareOwnTarget(self, spec) { + +export type Buffers = Record; +export type Shaders = Record; + +type Self = { + gl: GL & { _indexVA?: VA }; + buffers: Buffers; + shaders: Shaders; +}; + +export type Spec = { + size: [number, number]; + scale?: number; + format?: string; + depth?: TextureTarget | null; + layern?: number | null; + data: ArrayBufferView | null; + tag: string; + story?: number; + filter?: Filter; + wrap?: Wrap; +}; + +function prepareOwnTarget(self: Self, spec: Spec): TargetResult { const buffers = self.buffers; spec.size = getTargetSize(self.gl, spec); if (!buffers[spec.tag]) { const target = (buffers[spec.tag] = createTarget(self.gl, spec)); - console.log('created', target); + console.debug('created', target); } const target = buffers[spec.tag]; const tex = Array.isArray(target) ? target[target.length - 1] : target; const needResize = tex.size[0] != spec.size[0] || tex.size[1] != spec.size[1]; if (needResize || spec.data) { if (needResize) { - console.log(`resizing "${spec.tag}" (${tex.size})->(${spec.size})`); + console.debug(`resizing "${spec.tag}" (${tex.size})->(${spec.size})`); } - tex.update(spec.size, spec.data); + tex.update(spec.size, spec.data!); } return buffers[spec.tag]; } -function bindTarget(gl, target) { +function bindTarget(gl: GL, target?: TargetResult | null) { if (!target) { gl.bindFramebuffer(gl.FRAMEBUFFER, null); return [gl.canvas.width, gl.canvas.height]; } if (Array.isArray(target)) { - target.unshift((target = target.pop())); + target.unshift((target = target.pop()!)); } return target.bindTarget(gl); } @@ -777,10 +940,38 @@ const OptNames = new Set([ 'Face' ]); -function drawQuads(self, params, target) { - const options = {}, - uniforms = {}; +export type Options = { + Inc: string; + VP: string; + FP: string; + Clear: number | [number, number, number, number]; + Blend: string; + View: [number, number] | [number, number, number, number]; + Grid: [number] | [number, number] | [number, number, number]; + Mesh: [number, number]; + Aspect: Aspect; + DepthTest: 0 | 1 | boolean | 'keep'; + AlphaCoverage: 0 | 1 | boolean; + Face: 'front' | 'back'; +}; + +export type Uniforms = { + Src: WebGLTexture; + View: [number, number, number, number]; + Aspect: [number, number]; + Grid: [number, number, number]; + Mesh: [number, number]; +}; + +export type Params = Partial>; + +export type Target = WebGLTexture | WebGLTexture[] | Spec | HTMLVideoElement; + +function drawQuads(self: Self, params: Params, target?: Target | null): TargetResult { + const options = {} as Options, + uniforms = {} as Uniforms; for (const p in params) { + // @ts-ignore (OptNames.has(p) ? options : uniforms)[p] = params[p]; } const [Inc, VP, FP] = [options.Inc || '', options.VP || '', options.FP || '']; @@ -788,17 +979,18 @@ function drawQuads(self, params, target) { const noDraw = options.Clear === undefined && noShader; // setup target - if (target && target.tag) { - target = prepareOwnTarget(self, target); - if (noDraw) return target; + let targetResult = target as unknown as TargetResult; + if (target && 'tag' in target) { + targetResult = prepareOwnTarget(self, target); + if (noDraw) return targetResult; } - if (Array.isArray(target)) { - uniforms.Src = uniforms.Src || target[0]; + if (Array.isArray(targetResult)) { + uniforms.Src = uniforms.Src || targetResult[0]; } // bind (and clear) target - const gl = self.gl; - const targetSize = bindTarget(gl, target); + const { gl } = self; + const targetSize = bindTarget(gl, targetResult); let view = options.View || [0, 0, targetSize[0], targetSize[1]]; if (view.length == 2) { view = [0, 0, view[0], view[1]]; @@ -818,7 +1010,7 @@ function drawQuads(self, params, target) { // setup program if (noShader) { - return target; + return targetResult; } const shaderID = Inc + VP + FP; if (!(shaderID in self.shaders)) { @@ -829,7 +1021,7 @@ function drawQuads(self, params, target) { // process options if (options.Blend) { - const blend = parseBlend(options.Blend); + const blend = parseBlend(options.Blend)!; const { s, d, f } = blend; gl.enable(gl.BLEND); gl.blendFunc(s, d); @@ -861,10 +1053,10 @@ function drawQuads(self, params, target) { const vertN = (uniforms.Mesh[0] * 2 + 3) * uniforms.Mesh[1] - 1; const instN = gx * gy * gz; ensureVertexArray(gl, Math.max(vertN, instN)); - gl.bindVertexArray(gl._indexVA); + gl.bindVertexArray(gl._indexVA!); // setup uniforms and textures - Object.entries(prog.setters).forEach(([name, f]) => f(uniforms[name])); + Object.entries(prog.setters).forEach(([name, f]) => f(uniforms[name as keyof typeof uniforms])); // draw gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, vertN, instN); @@ -874,58 +1066,89 @@ function drawQuads(self, params, target) { if (options.Face) gl.disable(gl.CULL_FACE); if (options.AlphaCoverage) gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.bindVertexArray(null); - return target; + return targetResult; } -function wrapSwissGL(hook) { +export type Hook = (glsl: SwissGL, params: Params, target?: Target | null) => TargetResult; + +export type WrappedSwissGL = { + (params: Params, target?: Target | null): TargetResult; + hook: (this: SwissGL, hook: Hook) => WrappedSwissGL; + gl: GL; +}; + +function wrapSwissGL(this: SwissGL, hook: Hook): WrappedSwissGL { const glsl = this; - const f = (params, target) => hook(glsl, params, target); - f.hook = wrapSwissGL; - f.gl = glsl.gl; + const f: WrappedSwissGL = Object.assign( + (params: Params, target?: Target | null) => hook(glsl, params, target), + { + hook: wrapSwissGL, + gl: glsl.gl + } + ); return f; } -function SwissGL(canvas_gl) { - const gl = canvas_gl.getContext - ? canvas_gl.getContext('webgl2', { alpha: false, antialias: true }) - : canvas_gl; +export function SwissGL(canvas_gl: HTMLCanvasElement | GL): SwissGL { + !inited && init(); + const gl = + 'getContext' in canvas_gl + ? canvas_gl.getContext('webgl2', { alpha: false, antialias: true })! + : canvas_gl; gl.getExtension('EXT_color_buffer_float'); gl.getExtension('OES_texture_float_linear'); gl.pixelStorei(gl.PACK_ALIGNMENT, 1); gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); ensureVertexArray(gl, 1024); - const glsl = (params, target) => drawQuads(glsl, params, target); - glsl.hook = wrapSwissGL; - - glsl.gl = gl; - glsl.shaders = {}; - glsl.buffers = {}; - glsl.reset = () => { - Object.values(glsl.shaders).forEach((prog) => gl.deleteProgram(prog)); - Object.values(glsl.buffers) - .flat() - .forEach((target) => target.free()); - glsl.shaders = {}; - glsl.buffers = {}; - }; - glsl.adjustCanvas = (dpr) => { - dpr = dpr || self.devicePixelRatio; - const canvas = gl.canvas; - const w = canvas.clientWidth * dpr, - h = canvas.clientHeight * dpr; - if (canvas.width != w || canvas.height != h) { - canvas.width = w; - canvas.height = h; + let raf: ReturnType; + const glsl: SwissGL = Object.assign( + (params: Params, target?: Target | null) => drawQuads(glsl, params, target), + { + hook: wrapSwissGL, + gl, + shaders: {}, + buffers: {}, + reset() { + Object.values(glsl.shaders).forEach((prog) => gl.deleteProgram(prog)); + Object.values(glsl.buffers) + .flat() + .forEach((target) => target.free()); + glsl.shaders = {}; + glsl.buffers = {}; + }, + adjustCanvas(dpr?: number) { + dpr = dpr || self.devicePixelRatio; + const canvas = gl.canvas as HTMLCanvasElement; + const w = canvas.clientWidth * dpr, + h = canvas.clientHeight * dpr; + if (canvas.width != w || canvas.height != h) { + canvas.width = w; + canvas.height = h; + } + }, + loop(callback: (arg: { glsl: SwissGL; time: number }) => any) { + raf = requestAnimationFrame(function frameFunc(time) { + const res = callback({ glsl, time: time / 1000.0 }); + if (res != 'stop') raf = requestAnimationFrame(frameFunc); + }); + }, + stop() { + cancelAnimationFrame(raf); + } } - }; - glsl.loop = (callback) => { - const frameFunc = (time) => { - const res = callback({ glsl, time: time / 1000.0 }); - if (res != 'stop') requestAnimationFrame(frameFunc); - }; - requestAnimationFrame(frameFunc); - }; + ); + return glsl; } -self._SwissGL = SwissGL; +export type SwissGL = { + (params: Params, target?: Target | null): TargetResult; + hook: (this: SwissGL, hook: Hook) => WrappedSwissGL; + gl: GL; + buffers: Buffers; + shaders: Shaders; + reset(): void; + adjustCanvas(dpr?: number): void; + loop(callback: (arg: { glsl: SwissGL; time: number }) => any): void; + stop(): void; +}; From 8df41204b53391f9e0fc74752a6ea827f9b01377 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 23:17:38 +0100 Subject: [PATCH 13/17] Migrate to svelte kit wrapped demo routes --- package.json | 2 + pnpm-lock.yaml | 14 + src/demos/BitField.js | 3 +- src/demos/ColorCube.js | 3 +- src/demos/CubeDeform.js | 3 +- src/demos/DeferredShading.js | 3 +- src/demos/DotCamera.js | 5 +- src/demos/FancyLenia.js | 6 +- src/demos/GameOfLife.js | 3 +- src/demos/MeshGrid.js | 3 +- src/demos/NeuralCA.js | 3 +- src/demos/ParticleLenia.js | 3 +- src/demos/ParticleLife.js | 3 +- src/demos/ParticleLife3d.js | 6 +- src/demos/Physarum.js | 5 +- src/demos/Physarum3d.js | 3 +- src/demos/ReactionDiffusion.js | 3 +- src/demos/Shadowmap.js | 3 +- src/demos/Spectrogram.js | 5 +- src/demos/Springs.js | 3 +- src/demos/SurfaceNormals.js | 3 +- src/demos/TextureSamplers.js | 3 +- src/demos/Torus4d.js | 3 +- src/demos/audio.js | 5 +- src/demos/dat.gui.min.js | 2224 -------------------------------- src/demos/include.glsl | 39 + src/demos/index.ts | 45 + src/demos/main.js | 340 ----- src/demos/style.css | 101 -- src/routes/+page.svelte | 593 +++++++-- 30 files changed, 659 insertions(+), 2779 deletions(-) delete mode 100644 src/demos/dat.gui.min.js create mode 100644 src/demos/include.glsl create mode 100644 src/demos/index.ts delete mode 100644 src/demos/main.js delete mode 100644 src/demos/style.css diff --git a/package.json b/package.json index acba066..1c9027b 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,13 @@ "@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/kit": "^1.20.4", "@sveltejs/package": "^2.0.0", + "@types/webxr": "^0.5.5", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "eslint": "^8.28.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-svelte": "^2.30.0", + "lil-gui": "^0.18.2", "prettier": "^3.0.3", "prettier-plugin-svelte": "^3.0.3", "publint": "^0.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4903079..3d18dfe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ devDependencies: '@sveltejs/package': specifier: ^2.0.0 version: 2.2.2(svelte@4.2.1)(typescript@5.2.2) + '@types/webxr': + specifier: ^0.5.5 + version: 0.5.5 '@typescript-eslint/eslint-plugin': specifier: ^6.0.0 version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2) @@ -29,6 +32,9 @@ devDependencies: eslint-plugin-svelte: specifier: ^2.30.0 version: 2.34.0(eslint@8.50.0)(svelte@4.2.1) + lil-gui: + specifier: ^0.18.2 + version: 0.18.2 prettier: specifier: ^3.0.3 version: 3.0.3 @@ -494,6 +500,10 @@ packages: resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} dev: true + /@types/webxr@0.5.5: + resolution: {integrity: sha512-HVOsSRTQYx3zpVl0c0FBmmmcY/60BkQLzVnpE9M1aG4f2Z0aKlBWfj4XZ2zr++XNBfkQWYcwhGlmuu44RJPDqg==} + dev: true + /@typescript-eslint/eslint-plugin@6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2): resolution: {integrity: sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1307,6 +1317,10 @@ packages: type-check: 0.4.0 dev: true + /lil-gui@0.18.2: + resolution: {integrity: sha512-DgdrLy3/KGC0PiQLKgOcJMPItP4xY4iWgJ9+91Zaxfr8GCTmMps05QS9w9jW7yspILlbscbquwjOwxmWnSx5Uw==} + dev: true + /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} diff --git a/src/demos/BitField.js b/src/demos/BitField.js index fe2ec89..5c3769b 100644 --- a/src/demos/BitField.js +++ b/src/demos/BitField.js @@ -1,11 +1,12 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ // Bit Field texture, inspired by the tweet thread: // https://twitter.com/aemkei/status/1378106731386040322 -class BitField { +export default class BitField { static Tags = ['2d']; constructor(glsl, gui) { this.k = 9; diff --git a/src/demos/ColorCube.js b/src/demos/ColorCube.js index 1eb04db..3f54056 100644 --- a/src/demos/ColorCube.js +++ b/src/demos/ColorCube.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class ColorCube { +export default class ColorCube { static Tags = ['3d']; frame(glsl, params) { diff --git a/src/demos/CubeDeform.js b/src/demos/CubeDeform.js index 2c272a1..f8b2c22 100644 --- a/src/demos/CubeDeform.js +++ b/src/demos/CubeDeform.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class CubeDeform { +export default class CubeDeform { static Tags = ['3d']; frame(glsl, params) { diff --git a/src/demos/DeferredShading.js b/src/demos/DeferredShading.js index d111063..d1ca6e1 100644 --- a/src/demos/DeferredShading.js +++ b/src/demos/DeferredShading.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class DeferredShading { +export default class DeferredShading { frame(glsl, params) { // draw objects const gbuf = glsl( diff --git a/src/demos/DotCamera.js b/src/demos/DotCamera.js index a635647..47e2dfb 100644 --- a/src/demos/DotCamera.js +++ b/src/demos/DotCamera.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class DotCamera { +export default class DotCamera { constructor(glsl, gui) { this.video = document.createElement('video'); this.dayMode = false; @@ -17,7 +18,7 @@ class DotCamera { this.video.play(); }) .catch((error) => { - console.log('Error accessing camera:', error); + console.warn('Error accessing camera:', error); }); } diff --git a/src/demos/FancyLenia.js b/src/demos/FancyLenia.js index 060cdea..6b99d5d 100644 --- a/src/demos/FancyLenia.js +++ b/src/demos/FancyLenia.js @@ -1,10 +1,14 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ +import AudioStream from './audio.js'; +import ParticleLenia from './ParticleLenia.js'; + // Visualization of Particle Lenia fields as 3d landscape -class FancyLenia extends ParticleLenia { +export default class FancyLenia extends ParticleLenia { static Tags = ['3d', 'simulation', 'audio']; constructor(glsl, gui) { diff --git a/src/demos/GameOfLife.js b/src/demos/GameOfLife.js index 31cf49d..f25c175 100644 --- a/src/demos/GameOfLife.js +++ b/src/demos/GameOfLife.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class GameOfLife { +export default class GameOfLife { static Tags = ['2d', 'ca']; frame(glsl) { const state = glsl( diff --git a/src/demos/MeshGrid.js b/src/demos/MeshGrid.js index 28c97ec..ff7dc6e 100644 --- a/src/demos/MeshGrid.js +++ b/src/demos/MeshGrid.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class MeshGrid { +export default class MeshGrid { static Tags = ['2d']; frame(glsl, { time }) { glsl({ diff --git a/src/demos/NeuralCA.js b/src/demos/NeuralCA.js index 36165e5..3b0081e 100644 --- a/src/demos/NeuralCA.js +++ b/src/demos/NeuralCA.js @@ -1,11 +1,12 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ // Based on "μNCA: Texture Generation with Ultra-Compact Neural Cellular Automata" // https://arxiv.org/abs/2111.13545 -class NeuralCA { +export default class NeuralCA { static Tags = ['2d', 'ca']; constructor() { this.W = new Float32Array([ diff --git a/src/demos/ParticleLenia.js b/src/demos/ParticleLenia.js index 854610c..b831fed 100644 --- a/src/demos/ParticleLenia.js +++ b/src/demos/ParticleLenia.js @@ -1,12 +1,13 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ // Model description: // https://google-research.github.io/self-organising-systems/particle-lenia/ // https://observablehq.com/@znah/particle-lenia-from-scratch -class ParticleLenia { +export default class ParticleLenia { static Tags = ['2d', 'simulation']; constructor(glsl, gui) { diff --git a/src/demos/ParticleLife.js b/src/demos/ParticleLife.js index abe9051..286a65d 100644 --- a/src/demos/ParticleLife.js +++ b/src/demos/ParticleLife.js @@ -1,10 +1,11 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ // Inspired by the video https://youtu.be/p4YirERTVF0?t=480 -class ParticleLife { +export default class ParticleLife { static Tags = ['2d', 'simulation']; constructor(glsl, gui) { this.glsl = glsl; diff --git a/src/demos/ParticleLife3d.js b/src/demos/ParticleLife3d.js index fa9d196..ea679fc 100644 --- a/src/demos/ParticleLife3d.js +++ b/src/demos/ParticleLife3d.js @@ -1,10 +1,14 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ +import ParticleLife from './ParticleLife.js'; +import Shadowmap from './Shadowmap.js'; + // Inspired by the video https://youtu.be/p4YirERTVF0?t=480 -class ParticleLife3d extends ParticleLife { +export default class ParticleLife3d extends ParticleLife { static Tags = ['3d', 'simulation', 'shadows']; constructor(glsl, gui) { diff --git a/src/demos/Physarum.js b/src/demos/Physarum.js index ebb2f2e..2692b7b 100644 --- a/src/demos/Physarum.js +++ b/src/demos/Physarum.js @@ -1,10 +1,13 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ +import { updateObject } from '$lib/index.ts'; + // Inspired by https://cargocollective.com/sagejenson/physarum -class Physarum { +export default class Physarum { static Tags = ['2d', 'simulation']; constructor(glsl, gui) { diff --git a/src/demos/Physarum3d.js b/src/demos/Physarum3d.js index d8747d6..57c53f4 100644 --- a/src/demos/Physarum3d.js +++ b/src/demos/Physarum3d.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class Physarum3d { +export default class Physarum3d { static Tags = ['3d']; constructor(glsl, gui) { diff --git a/src/demos/ReactionDiffusion.js b/src/demos/ReactionDiffusion.js index 4f8db04..8c757f4 100644 --- a/src/demos/ReactionDiffusion.js +++ b/src/demos/ReactionDiffusion.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class ReactionDiffusion { +export default class ReactionDiffusion { static Tags = ['2d', 'simulation']; constructor(glsl, gui) { diff --git a/src/demos/Shadowmap.js b/src/demos/Shadowmap.js index 39054e0..53441ca 100644 --- a/src/demos/Shadowmap.js +++ b/src/demos/Shadowmap.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class Shadowmap { +export default class Shadowmap { static Tags = ['3d', 'shadows']; constructor(glsl, gui) { diff --git a/src/demos/Spectrogram.js b/src/demos/Spectrogram.js index ccb97e5..e350a8a 100644 --- a/src/demos/Spectrogram.js +++ b/src/demos/Spectrogram.js @@ -1,10 +1,11 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ // Example of streaming spectrogram data from WebAudio to WebGL2 -class Spectrogram { +export default class Spectrogram { static Tags = ['3d', 'data']; constructor(glsl, gui) { @@ -17,7 +18,7 @@ class Spectrogram { this.analyser.smoothingTimeConstant = 0.5; this.input.connect(this.analyser); this.frequencyArray = new Uint8Array(this.analyser.frequencyBinCount); - console.log('mic activated, frequencyBinCount:', this.analyser.frequencyBinCount); + console.debug('mic activated, frequencyBinCount:', this.analyser.frequencyBinCount); }) .catch((e) => console.error('Error getting microphone:', e)); } diff --git a/src/demos/Springs.js b/src/demos/Springs.js index 5d19d07..6294fc3 100644 --- a/src/demos/Springs.js +++ b/src/demos/Springs.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class Springs { +export default class Springs { static Tags = ['2d']; constructor(glsl, gui) { diff --git a/src/demos/SurfaceNormals.js b/src/demos/SurfaceNormals.js index 5632ed6..5d36dde 100644 --- a/src/demos/SurfaceNormals.js +++ b/src/demos/SurfaceNormals.js @@ -1,9 +1,10 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class SurfaceNormals { +export default class SurfaceNormals { static Tags = ['3d']; frame(glsl, params) { diff --git a/src/demos/TextureSamplers.js b/src/demos/TextureSamplers.js index acabde5..148ad2a 100644 --- a/src/demos/TextureSamplers.js +++ b/src/demos/TextureSamplers.js @@ -1,10 +1,11 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ // Use different sampling modes on the same texture -class TextureSamplers { +export default class TextureSamplers { static Tags = ['2d']; frame(glsl, { time }) { const T = glsl( diff --git a/src/demos/Torus4d.js b/src/demos/Torus4d.js index 77fadac..3710b6a 100644 --- a/src/demos/Torus4d.js +++ b/src/demos/Torus4d.js @@ -1,8 +1,9 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ -class Torus4d { +export default class Torus4d { static Tags = ['3d']; frame(glsl, params) { glsl({ diff --git a/src/demos/audio.js b/src/demos/audio.js index d421312..cce9ddc 100644 --- a/src/demos/audio.js +++ b/src/demos/audio.js @@ -1,10 +1,11 @@ /** @license * Copyright 2023 Google LLC. + * Copyright 2023 João Paquim * SPDX-License-Identifier: Apache-2.0 */ class AudioWorkletProcessor {} -class AudioStream extends AudioWorkletProcessor { +export default class AudioStream extends AudioWorkletProcessor { constructor() { super(); this.chunkSize = 1024; @@ -64,7 +65,7 @@ class AudioStream extends AudioWorkletProcessor { const submit = (buf) => this.workletNode.port.postMessage(buf, [buf.buffer]); this.workletNode.port.onmessage = (msg) => callback(msg.data, submit); this.workletNode.connect(this.audioContext.destination); - console.log('audio stream started'); + console.debug('audio stream started'); } stop() { this.audioContext.close(); diff --git a/src/demos/dat.gui.min.js b/src/demos/dat.gui.min.js deleted file mode 100644 index 5ff73eb..0000000 --- a/src/demos/dat.gui.min.js +++ /dev/null @@ -1,2224 +0,0 @@ -/** - * dat-gui JavaScript Controller Library - * https://github.com/dataarts/dat.gui - * - * Copyright 2011 Data Arts Team, Google Creative Lab - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - */ -!(function (e, t) { - 'object' == typeof exports && 'undefined' != typeof module - ? t(exports) - : 'function' == typeof define && define.amd - ? define(['exports'], t) - : t((e.dat = {})); -})(this, function (e) { - 'use strict'; - function t(e, t) { - var n = e.__state.conversionName.toString(), - o = Math.round(e.r), - i = Math.round(e.g), - r = Math.round(e.b), - s = e.a, - a = Math.round(e.h), - l = e.s.toFixed(1), - d = e.v.toFixed(1); - if (t || 'THREE_CHAR_HEX' === n || 'SIX_CHAR_HEX' === n) { - for (var c = e.hex.toString(16); c.length < 6; ) c = '0' + c; - return '#' + c; - } - return 'CSS_RGB' === n - ? 'rgb(' + o + ',' + i + ',' + r + ')' - : 'CSS_RGBA' === n - ? 'rgba(' + o + ',' + i + ',' + r + ',' + s + ')' - : 'HEX' === n - ? '0x' + e.hex.toString(16) - : 'RGB_ARRAY' === n - ? '[' + o + ',' + i + ',' + r + ']' - : 'RGBA_ARRAY' === n - ? '[' + o + ',' + i + ',' + r + ',' + s + ']' - : 'RGB_OBJ' === n - ? '{r:' + o + ',g:' + i + ',b:' + r + '}' - : 'RGBA_OBJ' === n - ? '{r:' + o + ',g:' + i + ',b:' + r + ',a:' + s + '}' - : 'HSV_OBJ' === n - ? '{h:' + a + ',s:' + l + ',v:' + d + '}' - : 'HSVA_OBJ' === n - ? '{h:' + a + ',s:' + l + ',v:' + d + ',a:' + s + '}' - : 'unknown format'; - } - function n(e, t, n) { - Object.defineProperty(e, t, { - get: function () { - return 'RGB' === this.__state.space - ? this.__state[t] - : (I.recalculateRGB(this, t, n), this.__state[t]); - }, - set: function (e) { - 'RGB' !== this.__state.space && - (I.recalculateRGB(this, t, n), (this.__state.space = 'RGB')), - (this.__state[t] = e); - } - }); - } - function o(e, t) { - Object.defineProperty(e, t, { - get: function () { - return 'HSV' === this.__state.space - ? this.__state[t] - : (I.recalculateHSV(this), this.__state[t]); - }, - set: function (e) { - 'HSV' !== this.__state.space && (I.recalculateHSV(this), (this.__state.space = 'HSV')), - (this.__state[t] = e); - } - }); - } - function i(e) { - if ('0' === e || S.isUndefined(e)) return 0; - var t = e.match(U); - return S.isNull(t) ? 0 : parseFloat(t[1]); - } - function r(e) { - var t = e.toString(); - return t.indexOf('.') > -1 ? t.length - t.indexOf('.') - 1 : 0; - } - function s(e, t) { - var n = Math.pow(10, t); - return Math.round(e * n) / n; - } - function a(e, t, n, o, i) { - return o + ((e - t) / (n - t)) * (i - o); - } - function l(e, t, n, o) { - (e.style.background = ''), - S.each(ee, function (i) { - e.style.cssText += - 'background: ' + i + 'linear-gradient(' + t + ', ' + n + ' 0%, ' + o + ' 100%); '; - }); - } - function d(e) { - (e.style.background = ''), - (e.style.cssText += - 'background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);'), - (e.style.cssText += - 'background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'), - (e.style.cssText += - 'background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'), - (e.style.cssText += - 'background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'), - (e.style.cssText += - 'background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'); - } - function c(e, t, n) { - var o = document.createElement('li'); - return ( - t && o.appendChild(t), n ? e.__ul.insertBefore(o, n) : e.__ul.appendChild(o), e.onResize(), o - ); - } - function u(e) { - X.unbind(window, 'resize', e.__resizeHandler), - e.saveToLocalStorageIfPossible && X.unbind(window, 'unload', e.saveToLocalStorageIfPossible); - } - function _(e, t) { - var n = e.__preset_select[e.__preset_select.selectedIndex]; - n.innerHTML = t ? n.value + '*' : n.value; - } - function h(e, t, n) { - if ( - ((n.__li = t), - (n.__gui = e), - S.extend(n, { - options: function (t) { - if (arguments.length > 1) { - var o = n.__li.nextElementSibling; - return ( - n.remove(), - f(e, n.object, n.property, { before: o, factoryArgs: [S.toArray(arguments)] }) - ); - } - if (S.isArray(t) || S.isObject(t)) { - var i = n.__li.nextElementSibling; - return n.remove(), f(e, n.object, n.property, { before: i, factoryArgs: [t] }); - } - }, - name: function (e) { - return (n.__li.firstElementChild.firstElementChild.innerHTML = e), n; - }, - listen: function () { - return n.__gui.listen(n), n; - }, - remove: function () { - return n.__gui.remove(n), n; - } - }), - n instanceof q) - ) { - var o = new Q(n.object, n.property, { min: n.__min, max: n.__max, step: n.__step }); - S.each(['updateDisplay', 'onChange', 'onFinishChange', 'step', 'min', 'max'], function (e) { - var t = n[e], - i = o[e]; - n[e] = o[e] = function () { - var e = Array.prototype.slice.call(arguments); - return i.apply(o, e), t.apply(n, e); - }; - }), - X.addClass(t, 'has-slider'), - n.domElement.insertBefore(o.domElement, n.domElement.firstElementChild); - } else if (n instanceof Q) { - var i = function (t) { - if (S.isNumber(n.__min) && S.isNumber(n.__max)) { - var o = n.__li.firstElementChild.firstElementChild.innerHTML, - i = n.__gui.__listening.indexOf(n) > -1; - n.remove(); - var r = f(e, n.object, n.property, { - before: n.__li.nextElementSibling, - factoryArgs: [n.__min, n.__max, n.__step] - }); - return r.name(o), i && r.listen(), r; - } - return t; - }; - (n.min = S.compose(i, n.min)), (n.max = S.compose(i, n.max)); - } else - n instanceof K - ? (X.bind(t, 'click', function () { - X.fakeEvent(n.__checkbox, 'click'); - }), - X.bind(n.__checkbox, 'click', function (e) { - e.stopPropagation(); - })) - : n instanceof Z - ? (X.bind(t, 'click', function () { - X.fakeEvent(n.__button, 'click'); - }), - X.bind(t, 'mouseover', function () { - X.addClass(n.__button, 'hover'); - }), - X.bind(t, 'mouseout', function () { - X.removeClass(n.__button, 'hover'); - })) - : n instanceof $ && - (X.addClass(t, 'color'), - (n.updateDisplay = S.compose(function (e) { - return (t.style.borderLeftColor = n.__color.toString()), e; - }, n.updateDisplay)), - n.updateDisplay()); - n.setValue = S.compose(function (t) { - return e.getRoot().__preset_select && n.isModified() && _(e.getRoot(), !0), t; - }, n.setValue); - } - function p(e, t) { - var n = e.getRoot(), - o = n.__rememberedObjects.indexOf(t.object); - if (-1 !== o) { - var i = n.__rememberedObjectIndecesToControllers[o]; - if ( - (void 0 === i && ((i = {}), (n.__rememberedObjectIndecesToControllers[o] = i)), - (i[t.property] = t), - n.load && n.load.remembered) - ) { - var r = n.load.remembered, - s = void 0; - if (r[e.preset]) s = r[e.preset]; - else { - if (!r[se]) return; - s = r[se]; - } - if (s[o] && void 0 !== s[o][t.property]) { - var a = s[o][t.property]; - (t.initialValue = a), t.setValue(a); - } - } - } - } - function f(e, t, n, o) { - if (void 0 === t[n]) throw new Error('Object "' + t + '" has no property "' + n + '"'); - var i = void 0; - if (o.color) i = new $(t, n); - else { - var r = [t, n].concat(o.factoryArgs); - i = ne.apply(e, r); - } - o.before instanceof z && (o.before = o.before.__li), p(e, i), X.addClass(i.domElement, 'c'); - var s = document.createElement('span'); - X.addClass(s, 'property-name'), (s.innerHTML = i.property); - var a = document.createElement('div'); - a.appendChild(s), a.appendChild(i.domElement); - var l = c(e, a, o.before); - return ( - X.addClass(l, he.CLASS_CONTROLLER_ROW), - i instanceof $ ? X.addClass(l, 'color') : X.addClass(l, H(i.getValue())), - h(e, l, i), - e.__controllers.push(i), - i - ); - } - function m(e, t) { - return document.location.href + '.' + t; - } - function g(e, t, n) { - var o = document.createElement('option'); - (o.innerHTML = t), - (o.value = t), - e.__preset_select.appendChild(o), - n && (e.__preset_select.selectedIndex = e.__preset_select.length - 1); - } - function b(e, t) { - t.style.display = e.useLocalStorage ? 'block' : 'none'; - } - function v(e) { - var t = (e.__save_row = document.createElement('li')); - X.addClass(e.domElement, 'has-save'), - e.__ul.insertBefore(t, e.__ul.firstChild), - X.addClass(t, 'save-row'); - var n = document.createElement('span'); - (n.innerHTML = ' '), X.addClass(n, 'button gears'); - var o = document.createElement('span'); - (o.innerHTML = 'Save'), X.addClass(o, 'button'), X.addClass(o, 'save'); - var i = document.createElement('span'); - (i.innerHTML = 'New'), X.addClass(i, 'button'), X.addClass(i, 'save-as'); - var r = document.createElement('span'); - (r.innerHTML = 'Revert'), X.addClass(r, 'button'), X.addClass(r, 'revert'); - var s = (e.__preset_select = document.createElement('select')); - if ( - (e.load && e.load.remembered - ? S.each(e.load.remembered, function (t, n) { - g(e, n, n === e.preset); - }) - : g(e, se, !1), - X.bind(s, 'change', function () { - for (var t = 0; t < e.__preset_select.length; t++) - e.__preset_select[t].innerHTML = e.__preset_select[t].value; - e.preset = this.value; - }), - t.appendChild(s), - t.appendChild(n), - t.appendChild(o), - t.appendChild(i), - t.appendChild(r), - ae) - ) { - var a = document.getElementById('dg-local-explain'), - l = document.getElementById('dg-local-storage'); - (document.getElementById('dg-save-locally').style.display = 'block'), - 'true' === localStorage.getItem(m(e, 'isLocal')) && l.setAttribute('checked', 'checked'), - b(e, a), - X.bind(l, 'change', function () { - (e.useLocalStorage = !e.useLocalStorage), b(e, a); - }); - } - var d = document.getElementById('dg-new-constructor'); - X.bind(d, 'keydown', function (e) { - !e.metaKey || (67 !== e.which && 67 !== e.keyCode) || le.hide(); - }), - X.bind(n, 'click', function () { - (d.innerHTML = JSON.stringify(e.getSaveObject(), void 0, 2)), - le.show(), - d.focus(), - d.select(); - }), - X.bind(o, 'click', function () { - e.save(); - }), - X.bind(i, 'click', function () { - var t = prompt('Enter a new preset name.'); - t && e.saveAs(t); - }), - X.bind(r, 'click', function () { - e.revert(); - }); - } - function y(e) { - function t(t) { - return t.preventDefault(), (e.width += i - t.clientX), e.onResize(), (i = t.clientX), !1; - } - function n() { - X.removeClass(e.__closeButton, he.CLASS_DRAG), - X.unbind(window, 'mousemove', t), - X.unbind(window, 'mouseup', n); - } - function o(o) { - return ( - o.preventDefault(), - (i = o.clientX), - X.addClass(e.__closeButton, he.CLASS_DRAG), - X.bind(window, 'mousemove', t), - X.bind(window, 'mouseup', n), - !1 - ); - } - var i = void 0; - (e.__resize_handle = document.createElement('div')), - S.extend(e.__resize_handle.style, { - width: '6px', - marginLeft: '-3px', - height: '200px', - cursor: 'ew-resize', - position: 'absolute' - }), - X.bind(e.__resize_handle, 'mousedown', o), - X.bind(e.__closeButton, 'mousedown', o), - e.domElement.insertBefore(e.__resize_handle, e.domElement.firstElementChild); - } - function w(e, t) { - (e.domElement.style.width = t + 'px'), - e.__save_row && e.autoPlace && (e.__save_row.style.width = t + 'px'), - e.__closeButton && (e.__closeButton.style.width = t + 'px'); - } - function x(e, t) { - var n = {}; - return ( - S.each(e.__rememberedObjects, function (o, i) { - var r = {}, - s = e.__rememberedObjectIndecesToControllers[i]; - S.each(s, function (e, n) { - r[n] = t ? e.initialValue : e.getValue(); - }), - (n[i] = r); - }), - n - ); - } - function E(e) { - for (var t = 0; t < e.__preset_select.length; t++) - e.__preset_select[t].value === e.preset && (e.__preset_select.selectedIndex = t); - } - function C(e) { - 0 !== e.length && - oe.call(window, function () { - C(e); - }), - S.each(e, function (e) { - e.updateDisplay(); - }); - } - var A = Array.prototype.forEach, - k = Array.prototype.slice, - S = { - BREAK: {}, - extend: function (e) { - return ( - this.each( - k.call(arguments, 1), - function (t) { - (this.isObject(t) ? Object.keys(t) : []).forEach( - function (n) { - this.isUndefined(t[n]) || (e[n] = t[n]); - }.bind(this) - ); - }, - this - ), - e - ); - }, - defaults: function (e) { - return ( - this.each( - k.call(arguments, 1), - function (t) { - (this.isObject(t) ? Object.keys(t) : []).forEach( - function (n) { - this.isUndefined(e[n]) && (e[n] = t[n]); - }.bind(this) - ); - }, - this - ), - e - ); - }, - compose: function () { - var e = k.call(arguments); - return function () { - for (var t = k.call(arguments), n = e.length - 1; n >= 0; n--) t = [e[n].apply(this, t)]; - return t[0]; - }; - }, - each: function (e, t, n) { - if (e) - if (A && e.forEach && e.forEach === A) e.forEach(t, n); - else if (e.length === e.length + 0) { - var o = void 0, - i = void 0; - for (o = 0, i = e.length; o < i; o++) - if (o in e && t.call(n, e[o], o) === this.BREAK) return; - } else for (var r in e) if (t.call(n, e[r], r) === this.BREAK) return; - }, - defer: function (e) { - setTimeout(e, 0); - }, - debounce: function (e, t, n) { - var o = void 0; - return function () { - var i = this, - r = arguments, - s = n || !o; - clearTimeout(o), - (o = setTimeout(function () { - (o = null), n || e.apply(i, r); - }, t)), - s && e.apply(i, r); - }; - }, - toArray: function (e) { - return e.toArray ? e.toArray() : k.call(e); - }, - isUndefined: function (e) { - return void 0 === e; - }, - isNull: function (e) { - return null === e; - }, - isNaN: (function (e) { - function t(t) { - return e.apply(this, arguments); - } - return ( - (t.toString = function () { - return e.toString(); - }), - t - ); - })(function (e) { - return isNaN(e); - }), - isArray: - Array.isArray || - function (e) { - return e.constructor === Array; - }, - isObject: function (e) { - return e === Object(e); - }, - isNumber: function (e) { - return e === e + 0; - }, - isString: function (e) { - return e === e + ''; - }, - isBoolean: function (e) { - return !1 === e || !0 === e; - }, - isFunction: function (e) { - return e instanceof Function; - } - }, - O = [ - { - litmus: S.isString, - conversions: { - THREE_CHAR_HEX: { - read: function (e) { - var t = e.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i); - return ( - null !== t && { - space: 'HEX', - hex: parseInt( - '0x' + - t[1].toString() + - t[1].toString() + - t[2].toString() + - t[2].toString() + - t[3].toString() + - t[3].toString(), - 0 - ) - } - ); - }, - write: t - }, - SIX_CHAR_HEX: { - read: function (e) { - var t = e.match(/^#([A-F0-9]{6})$/i); - return null !== t && { space: 'HEX', hex: parseInt('0x' + t[1].toString(), 0) }; - }, - write: t - }, - CSS_RGB: { - read: function (e) { - var t = e.match(/^rgb\(\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*\)/); - return ( - null !== t && { - space: 'RGB', - r: parseFloat(t[1]), - g: parseFloat(t[2]), - b: parseFloat(t[3]) - } - ); - }, - write: t - }, - CSS_RGBA: { - read: function (e) { - var t = e.match(/^rgba\(\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*\)/); - return ( - null !== t && { - space: 'RGB', - r: parseFloat(t[1]), - g: parseFloat(t[2]), - b: parseFloat(t[3]), - a: parseFloat(t[4]) - } - ); - }, - write: t - } - } - }, - { - litmus: S.isNumber, - conversions: { - HEX: { - read: function (e) { - return { space: 'HEX', hex: e, conversionName: 'HEX' }; - }, - write: function (e) { - return e.hex; - } - } - } - }, - { - litmus: S.isArray, - conversions: { - RGB_ARRAY: { - read: function (e) { - return 3 === e.length && { space: 'RGB', r: e[0], g: e[1], b: e[2] }; - }, - write: function (e) { - return [e.r, e.g, e.b]; - } - }, - RGBA_ARRAY: { - read: function (e) { - return 4 === e.length && { space: 'RGB', r: e[0], g: e[1], b: e[2], a: e[3] }; - }, - write: function (e) { - return [e.r, e.g, e.b, e.a]; - } - } - } - }, - { - litmus: S.isObject, - conversions: { - RGBA_OBJ: { - read: function (e) { - return ( - !!(S.isNumber(e.r) && S.isNumber(e.g) && S.isNumber(e.b) && S.isNumber(e.a)) && { - space: 'RGB', - r: e.r, - g: e.g, - b: e.b, - a: e.a - } - ); - }, - write: function (e) { - return { r: e.r, g: e.g, b: e.b, a: e.a }; - } - }, - RGB_OBJ: { - read: function (e) { - return ( - !!(S.isNumber(e.r) && S.isNumber(e.g) && S.isNumber(e.b)) && { - space: 'RGB', - r: e.r, - g: e.g, - b: e.b - } - ); - }, - write: function (e) { - return { r: e.r, g: e.g, b: e.b }; - } - }, - HSVA_OBJ: { - read: function (e) { - return ( - !!(S.isNumber(e.h) && S.isNumber(e.s) && S.isNumber(e.v) && S.isNumber(e.a)) && { - space: 'HSV', - h: e.h, - s: e.s, - v: e.v, - a: e.a - } - ); - }, - write: function (e) { - return { h: e.h, s: e.s, v: e.v, a: e.a }; - } - }, - HSV_OBJ: { - read: function (e) { - return ( - !!(S.isNumber(e.h) && S.isNumber(e.s) && S.isNumber(e.v)) && { - space: 'HSV', - h: e.h, - s: e.s, - v: e.v - } - ); - }, - write: function (e) { - return { h: e.h, s: e.s, v: e.v }; - } - } - } - } - ], - T = void 0, - L = void 0, - R = function () { - L = !1; - var e = arguments.length > 1 ? S.toArray(arguments) : arguments[0]; - return ( - S.each(O, function (t) { - if (t.litmus(e)) - return ( - S.each(t.conversions, function (t, n) { - if (((T = t.read(e)), !1 === L && !1 !== T)) - return (L = T), (T.conversionName = n), (T.conversion = t), S.BREAK; - }), - S.BREAK - ); - }), - L - ); - }, - B = void 0, - N = { - hsv_to_rgb: function (e, t, n) { - var o = Math.floor(e / 60) % 6, - i = e / 60 - Math.floor(e / 60), - r = n * (1 - t), - s = n * (1 - i * t), - a = n * (1 - (1 - i) * t), - l = [ - [n, a, r], - [s, n, r], - [r, n, a], - [r, s, n], - [a, r, n], - [n, r, s] - ][o]; - return { r: 255 * l[0], g: 255 * l[1], b: 255 * l[2] }; - }, - rgb_to_hsv: function (e, t, n) { - var o = Math.min(e, t, n), - i = Math.max(e, t, n), - r = i - o, - s = void 0, - a = void 0; - return 0 === i - ? { h: NaN, s: 0, v: 0 } - : ((a = r / i), - (s = e === i ? (t - n) / r : t === i ? 2 + (n - e) / r : 4 + (e - t) / r), - (s /= 6) < 0 && (s += 1), - { h: 360 * s, s: a, v: i / 255 }); - }, - rgb_to_hex: function (e, t, n) { - var o = this.hex_with_component(0, 2, e); - return (o = this.hex_with_component(o, 1, t)), (o = this.hex_with_component(o, 0, n)); - }, - component_from_hex: function (e, t) { - return (e >> (8 * t)) & 255; - }, - hex_with_component: function (e, t, n) { - return (n << (B = 8 * t)) | (e & ~(255 << B)); - } - }, - H = - 'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator - ? function (e) { - return typeof e; - } - : function (e) { - return e && - 'function' == typeof Symbol && - e.constructor === Symbol && - e !== Symbol.prototype - ? 'symbol' - : typeof e; - }, - F = function (e, t) { - if (!(e instanceof t)) throw new TypeError('Cannot call a class as a function'); - }, - P = (function () { - function e(e, t) { - for (var n = 0; n < t.length; n++) { - var o = t[n]; - (o.enumerable = o.enumerable || !1), - (o.configurable = !0), - 'value' in o && (o.writable = !0), - Object.defineProperty(e, o.key, o); - } - } - return function (t, n, o) { - return n && e(t.prototype, n), o && e(t, o), t; - }; - })(), - D = function e(t, n, o) { - null === t && (t = Function.prototype); - var i = Object.getOwnPropertyDescriptor(t, n); - if (void 0 === i) { - var r = Object.getPrototypeOf(t); - return null === r ? void 0 : e(r, n, o); - } - if ('value' in i) return i.value; - var s = i.get; - if (void 0 !== s) return s.call(o); - }, - j = function (e, t) { - if ('function' != typeof t && null !== t) - throw new TypeError('Super expression must either be null or a function, not ' + typeof t); - (e.prototype = Object.create(t && t.prototype, { - constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } - })), - t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : (e.__proto__ = t)); - }, - V = function (e, t) { - if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - return !t || ('object' != typeof t && 'function' != typeof t) ? e : t; - }, - I = (function () { - function e() { - if ((F(this, e), (this.__state = R.apply(this, arguments)), !1 === this.__state)) - throw new Error('Failed to interpret color arguments'); - this.__state.a = this.__state.a || 1; - } - return ( - P(e, [ - { - key: 'toString', - value: function () { - return t(this); - } - }, - { - key: 'toHexString', - value: function () { - return t(this, !0); - } - }, - { - key: 'toOriginal', - value: function () { - return this.__state.conversion.write(this); - } - } - ]), - e - ); - })(); - (I.recalculateRGB = function (e, t, n) { - if ('HEX' === e.__state.space) e.__state[t] = N.component_from_hex(e.__state.hex, n); - else { - if ('HSV' !== e.__state.space) throw new Error('Corrupted color state'); - S.extend(e.__state, N.hsv_to_rgb(e.__state.h, e.__state.s, e.__state.v)); - } - }), - (I.recalculateHSV = function (e) { - var t = N.rgb_to_hsv(e.r, e.g, e.b); - S.extend(e.__state, { s: t.s, v: t.v }), - S.isNaN(t.h) ? S.isUndefined(e.__state.h) && (e.__state.h = 0) : (e.__state.h = t.h); - }), - (I.COMPONENTS = ['r', 'g', 'b', 'h', 's', 'v', 'hex', 'a']), - n(I.prototype, 'r', 2), - n(I.prototype, 'g', 1), - n(I.prototype, 'b', 0), - o(I.prototype, 'h'), - o(I.prototype, 's'), - o(I.prototype, 'v'), - Object.defineProperty(I.prototype, 'a', { - get: function () { - return this.__state.a; - }, - set: function (e) { - this.__state.a = e; - } - }), - Object.defineProperty(I.prototype, 'hex', { - get: function () { - return ( - 'HEX' !== this.__state.space && - ((this.__state.hex = N.rgb_to_hex(this.r, this.g, this.b)), - (this.__state.space = 'HEX')), - this.__state.hex - ); - }, - set: function (e) { - (this.__state.space = 'HEX'), (this.__state.hex = e); - } - }); - var z = (function () { - function e(t, n) { - F(this, e), - (this.initialValue = t[n]), - (this.domElement = document.createElement('div')), - (this.object = t), - (this.property = n), - (this.__onChange = void 0), - (this.__onFinishChange = void 0); - } - return ( - P(e, [ - { - key: 'onChange', - value: function (e) { - return (this.__onChange = e), this; - } - }, - { - key: 'onFinishChange', - value: function (e) { - return (this.__onFinishChange = e), this; - } - }, - { - key: 'setValue', - value: function (e) { - return ( - (this.object[this.property] = e), - this.__onChange && this.__onChange.call(this, e), - this.updateDisplay(), - this - ); - } - }, - { - key: 'getValue', - value: function () { - return this.object[this.property]; - } - }, - { - key: 'updateDisplay', - value: function () { - return this; - } - }, - { - key: 'isModified', - value: function () { - return this.initialValue !== this.getValue(); - } - } - ]), - e - ); - })(), - M = { - HTMLEvents: ['change'], - MouseEvents: ['click', 'mousemove', 'mousedown', 'mouseup', 'mouseover'], - KeyboardEvents: ['keydown'] - }, - G = {}; - S.each(M, function (e, t) { - S.each(e, function (e) { - G[e] = t; - }); - }); - var U = /(\d+(\.\d+)?)px/, - X = { - makeSelectable: function (e, t) { - void 0 !== e && - void 0 !== e.style && - ((e.onselectstart = t - ? function () { - return !1; - } - : function () {}), - (e.style.MozUserSelect = t ? 'auto' : 'none'), - (e.style.KhtmlUserSelect = t ? 'auto' : 'none'), - (e.unselectable = t ? 'on' : 'off')); - }, - makeFullscreen: function (e, t, n) { - var o = n, - i = t; - S.isUndefined(i) && (i = !0), - S.isUndefined(o) && (o = !0), - (e.style.position = 'absolute'), - i && ((e.style.left = 0), (e.style.right = 0)), - o && ((e.style.top = 0), (e.style.bottom = 0)); - }, - fakeEvent: function (e, t, n, o) { - var i = n || {}, - r = G[t]; - if (!r) throw new Error('Event type ' + t + ' not supported.'); - var s = document.createEvent(r); - switch (r) { - case 'MouseEvents': - var a = i.x || i.clientX || 0, - l = i.y || i.clientY || 0; - s.initMouseEvent( - t, - i.bubbles || !1, - i.cancelable || !0, - window, - i.clickCount || 1, - 0, - 0, - a, - l, - !1, - !1, - !1, - !1, - 0, - null - ); - break; - case 'KeyboardEvents': - var d = s.initKeyboardEvent || s.initKeyEvent; - S.defaults(i, { - cancelable: !0, - ctrlKey: !1, - altKey: !1, - shiftKey: !1, - metaKey: !1, - keyCode: void 0, - charCode: void 0 - }), - d( - t, - i.bubbles || !1, - i.cancelable, - window, - i.ctrlKey, - i.altKey, - i.shiftKey, - i.metaKey, - i.keyCode, - i.charCode - ); - break; - default: - s.initEvent(t, i.bubbles || !1, i.cancelable || !0); - } - S.defaults(s, o), e.dispatchEvent(s); - }, - bind: function (e, t, n, o) { - var i = o || !1; - return ( - e.addEventListener - ? e.addEventListener(t, n, i) - : e.attachEvent && e.attachEvent('on' + t, n), - X - ); - }, - unbind: function (e, t, n, o) { - var i = o || !1; - return ( - e.removeEventListener - ? e.removeEventListener(t, n, i) - : e.detachEvent && e.detachEvent('on' + t, n), - X - ); - }, - addClass: function (e, t) { - if (void 0 === e.className) e.className = t; - else if (e.className !== t) { - var n = e.className.split(/ +/); - -1 === n.indexOf(t) && - (n.push(t), (e.className = n.join(' ').replace(/^\s+/, '').replace(/\s+$/, ''))); - } - return X; - }, - removeClass: function (e, t) { - if (t) - if (e.className === t) e.removeAttribute('class'); - else { - var n = e.className.split(/ +/), - o = n.indexOf(t); - -1 !== o && (n.splice(o, 1), (e.className = n.join(' '))); - } - else e.className = void 0; - return X; - }, - hasClass: function (e, t) { - return new RegExp('(?:^|\\s+)' + t + '(?:\\s+|$)').test(e.className) || !1; - }, - getWidth: function (e) { - var t = getComputedStyle(e); - return ( - i(t['border-left-width']) + - i(t['border-right-width']) + - i(t['padding-left']) + - i(t['padding-right']) + - i(t.width) - ); - }, - getHeight: function (e) { - var t = getComputedStyle(e); - return ( - i(t['border-top-width']) + - i(t['border-bottom-width']) + - i(t['padding-top']) + - i(t['padding-bottom']) + - i(t.height) - ); - }, - getOffset: function (e) { - var t = e, - n = { left: 0, top: 0 }; - if (t.offsetParent) - do { - (n.left += t.offsetLeft), (n.top += t.offsetTop), (t = t.offsetParent); - } while (t); - return n; - }, - isActive: function (e) { - return e === document.activeElement && (e.type || e.href); - } - }, - K = (function (e) { - function t(e, n) { - F(this, t); - var o = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), - i = o; - return ( - (o.__prev = o.getValue()), - (o.__checkbox = document.createElement('input')), - o.__checkbox.setAttribute('type', 'checkbox'), - X.bind( - o.__checkbox, - 'change', - function () { - i.setValue(!i.__prev); - }, - !1 - ), - o.domElement.appendChild(o.__checkbox), - o.updateDisplay(), - o - ); - } - return ( - j(t, z), - P(t, [ - { - key: 'setValue', - value: function (e) { - var n = D( - t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), - 'setValue', - this - ).call(this, e); - return ( - this.__onFinishChange && this.__onFinishChange.call(this, this.getValue()), - (this.__prev = this.getValue()), - n - ); - } - }, - { - key: 'updateDisplay', - value: function () { - return ( - !0 === this.getValue() - ? (this.__checkbox.setAttribute('checked', 'checked'), - (this.__checkbox.checked = !0), - (this.__prev = !0)) - : ((this.__checkbox.checked = !1), (this.__prev = !1)), - D( - t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), - 'updateDisplay', - this - ).call(this) - ); - } - } - ]), - t - ); - })(), - Y = (function (e) { - function t(e, n, o) { - F(this, t); - var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), - r = o, - s = i; - if (((i.__select = document.createElement('select')), S.isArray(r))) { - var a = {}; - S.each(r, function (e) { - a[e] = e; - }), - (r = a); - } - return ( - S.each(r, function (e, t) { - var n = document.createElement('option'); - (n.innerHTML = t), n.setAttribute('value', e), s.__select.appendChild(n); - }), - i.updateDisplay(), - X.bind(i.__select, 'change', function () { - var e = this.options[this.selectedIndex].value; - s.setValue(e); - }), - i.domElement.appendChild(i.__select), - i - ); - } - return ( - j(t, z), - P(t, [ - { - key: 'setValue', - value: function (e) { - var n = D( - t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), - 'setValue', - this - ).call(this, e); - return this.__onFinishChange && this.__onFinishChange.call(this, this.getValue()), n; - } - }, - { - key: 'updateDisplay', - value: function () { - return X.isActive(this.__select) - ? this - : ((this.__select.value = this.getValue()), - D( - t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), - 'updateDisplay', - this - ).call(this)); - } - } - ]), - t - ); - })(), - J = (function (e) { - function t(e, n) { - function o() { - r.setValue(r.__input.value); - } - F(this, t); - var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), - r = i; - return ( - (i.__input = document.createElement('input')), - i.__input.setAttribute('type', 'text'), - X.bind(i.__input, 'keyup', o), - X.bind(i.__input, 'change', o), - X.bind(i.__input, 'blur', function () { - r.__onFinishChange && r.__onFinishChange.call(r, r.getValue()); - }), - X.bind(i.__input, 'keydown', function (e) { - 13 === e.keyCode && this.blur(); - }), - i.updateDisplay(), - i.domElement.appendChild(i.__input), - i - ); - } - return ( - j(t, z), - P(t, [ - { - key: 'updateDisplay', - value: function () { - return ( - X.isActive(this.__input) || (this.__input.value = this.getValue()), - D( - t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), - 'updateDisplay', - this - ).call(this) - ); - } - } - ]), - t - ); - })(), - W = (function (e) { - function t(e, n, o) { - F(this, t); - var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), - s = o || {}; - return ( - (i.__min = s.min), - (i.__max = s.max), - (i.__step = s.step), - S.isUndefined(i.__step) - ? 0 === i.initialValue - ? (i.__impliedStep = 1) - : (i.__impliedStep = - Math.pow(10, Math.floor(Math.log(Math.abs(i.initialValue)) / Math.LN10)) / 10) - : (i.__impliedStep = i.__step), - (i.__precision = r(i.__impliedStep)), - i - ); - } - return ( - j(t, z), - P(t, [ - { - key: 'setValue', - value: function (e) { - var n = e; - return ( - void 0 !== this.__min && n < this.__min - ? (n = this.__min) - : void 0 !== this.__max && n > this.__max && (n = this.__max), - void 0 !== this.__step && - n % this.__step != 0 && - (n = Math.round(n / this.__step) * this.__step), - D( - t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), - 'setValue', - this - ).call(this, n) - ); - } - }, - { - key: 'min', - value: function (e) { - return (this.__min = e), this; - } - }, - { - key: 'max', - value: function (e) { - return (this.__max = e), this; - } - }, - { - key: 'step', - value: function (e) { - return (this.__step = e), (this.__impliedStep = e), (this.__precision = r(e)), this; - } - } - ]), - t - ); - })(), - Q = (function (e) { - function t(e, n, o) { - function i() { - l.__onFinishChange && l.__onFinishChange.call(l, l.getValue()); - } - function r(e) { - var t = d - e.clientY; - l.setValue(l.getValue() + t * l.__impliedStep), (d = e.clientY); - } - function s() { - X.unbind(window, 'mousemove', r), X.unbind(window, 'mouseup', s), i(); - } - F(this, t); - var a = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n, o)); - a.__truncationSuspended = !1; - var l = a, - d = void 0; - return ( - (a.__input = document.createElement('input')), - a.__input.setAttribute('type', 'text'), - X.bind(a.__input, 'change', function () { - var e = parseFloat(l.__input.value); - S.isNaN(e) || l.setValue(e); - }), - X.bind(a.__input, 'blur', function () { - i(); - }), - X.bind(a.__input, 'mousedown', function (e) { - X.bind(window, 'mousemove', r), X.bind(window, 'mouseup', s), (d = e.clientY); - }), - X.bind(a.__input, 'keydown', function (e) { - 13 === e.keyCode && - ((l.__truncationSuspended = !0), this.blur(), (l.__truncationSuspended = !1), i()); - }), - a.updateDisplay(), - a.domElement.appendChild(a.__input), - a - ); - } - return ( - j(t, W), - P(t, [ - { - key: 'updateDisplay', - value: function () { - return ( - (this.__input.value = this.__truncationSuspended - ? this.getValue() - : s(this.getValue(), this.__precision)), - D( - t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), - 'updateDisplay', - this - ).call(this) - ); - } - } - ]), - t - ); - })(), - q = (function (e) { - function t(e, n, o, i, r) { - function s(e) { - e.preventDefault(); - var t = _.__background.getBoundingClientRect(); - return _.setValue(a(e.clientX, t.left, t.right, _.__min, _.__max)), !1; - } - function l() { - X.unbind(window, 'mousemove', s), - X.unbind(window, 'mouseup', l), - _.__onFinishChange && _.__onFinishChange.call(_, _.getValue()); - } - function d(e) { - var t = e.touches[0].clientX, - n = _.__background.getBoundingClientRect(); - _.setValue(a(t, n.left, n.right, _.__min, _.__max)); - } - function c() { - X.unbind(window, 'touchmove', d), - X.unbind(window, 'touchend', c), - _.__onFinishChange && _.__onFinishChange.call(_, _.getValue()); - } - F(this, t); - var u = V( - this, - (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n, { min: o, max: i, step: r }) - ), - _ = u; - return ( - (u.__background = document.createElement('div')), - (u.__foreground = document.createElement('div')), - X.bind(u.__background, 'mousedown', function (e) { - document.activeElement.blur(), - X.bind(window, 'mousemove', s), - X.bind(window, 'mouseup', l), - s(e); - }), - X.bind(u.__background, 'touchstart', function (e) { - 1 === e.touches.length && - (X.bind(window, 'touchmove', d), X.bind(window, 'touchend', c), d(e)); - }), - X.addClass(u.__background, 'slider'), - X.addClass(u.__foreground, 'slider-fg'), - u.updateDisplay(), - u.__background.appendChild(u.__foreground), - u.domElement.appendChild(u.__background), - u - ); - } - return ( - j(t, W), - P(t, [ - { - key: 'updateDisplay', - value: function () { - var e = (this.getValue() - this.__min) / (this.__max - this.__min); - return ( - (this.__foreground.style.width = 100 * e + '%'), - D( - t.prototype.__proto__ || Object.getPrototypeOf(t.prototype), - 'updateDisplay', - this - ).call(this) - ); - } - } - ]), - t - ); - })(), - Z = (function (e) { - function t(e, n, o) { - F(this, t); - var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)), - r = i; - return ( - (i.__button = document.createElement('div')), - (i.__button.innerHTML = void 0 === o ? 'Fire' : o), - X.bind(i.__button, 'click', function (e) { - return e.preventDefault(), r.fire(), !1; - }), - X.addClass(i.__button, 'button'), - i.domElement.appendChild(i.__button), - i - ); - } - return ( - j(t, z), - P(t, [ - { - key: 'fire', - value: function () { - this.__onChange && this.__onChange.call(this), - this.getValue().call(this.object), - this.__onFinishChange && this.__onFinishChange.call(this, this.getValue()); - } - } - ]), - t - ); - })(), - $ = (function (e) { - function t(e, n) { - function o(e) { - u(e), - X.bind(window, 'mousemove', u), - X.bind(window, 'touchmove', u), - X.bind(window, 'mouseup', r), - X.bind(window, 'touchend', r); - } - function i(e) { - _(e), - X.bind(window, 'mousemove', _), - X.bind(window, 'touchmove', _), - X.bind(window, 'mouseup', s), - X.bind(window, 'touchend', s); - } - function r() { - X.unbind(window, 'mousemove', u), - X.unbind(window, 'touchmove', u), - X.unbind(window, 'mouseup', r), - X.unbind(window, 'touchend', r), - c(); - } - function s() { - X.unbind(window, 'mousemove', _), - X.unbind(window, 'touchmove', _), - X.unbind(window, 'mouseup', s), - X.unbind(window, 'touchend', s), - c(); - } - function a() { - var e = R(this.value); - !1 !== e - ? ((p.__color.__state = e), p.setValue(p.__color.toOriginal())) - : (this.value = p.__color.toString()); - } - function c() { - p.__onFinishChange && p.__onFinishChange.call(p, p.__color.toOriginal()); - } - function u(e) { - -1 === e.type.indexOf('touch') && e.preventDefault(); - var t = p.__saturation_field.getBoundingClientRect(), - n = (e.touches && e.touches[0]) || e, - o = n.clientX, - i = n.clientY, - r = (o - t.left) / (t.right - t.left), - s = 1 - (i - t.top) / (t.bottom - t.top); - return ( - s > 1 ? (s = 1) : s < 0 && (s = 0), - r > 1 ? (r = 1) : r < 0 && (r = 0), - (p.__color.v = s), - (p.__color.s = r), - p.setValue(p.__color.toOriginal()), - !1 - ); - } - function _(e) { - -1 === e.type.indexOf('touch') && e.preventDefault(); - var t = p.__hue_field.getBoundingClientRect(), - n = 1 - (((e.touches && e.touches[0]) || e).clientY - t.top) / (t.bottom - t.top); - return ( - n > 1 ? (n = 1) : n < 0 && (n = 0), - (p.__color.h = 360 * n), - p.setValue(p.__color.toOriginal()), - !1 - ); - } - F(this, t); - var h = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)); - (h.__color = new I(h.getValue())), (h.__temp = new I(0)); - var p = h; - (h.domElement = document.createElement('div')), - X.makeSelectable(h.domElement, !1), - (h.__selector = document.createElement('div')), - (h.__selector.className = 'selector'), - (h.__saturation_field = document.createElement('div')), - (h.__saturation_field.className = 'saturation-field'), - (h.__field_knob = document.createElement('div')), - (h.__field_knob.className = 'field-knob'), - (h.__field_knob_border = '2px solid '), - (h.__hue_knob = document.createElement('div')), - (h.__hue_knob.className = 'hue-knob'), - (h.__hue_field = document.createElement('div')), - (h.__hue_field.className = 'hue-field'), - (h.__input = document.createElement('input')), - (h.__input.type = 'text'), - (h.__input_textShadow = '0 1px 1px '), - X.bind(h.__input, 'keydown', function (e) { - 13 === e.keyCode && a.call(this); - }), - X.bind(h.__input, 'blur', a), - X.bind(h.__selector, 'mousedown', function () { - X.addClass(this, 'drag').bind(window, 'mouseup', function () { - X.removeClass(p.__selector, 'drag'); - }); - }), - X.bind(h.__selector, 'touchstart', function () { - X.addClass(this, 'drag').bind(window, 'touchend', function () { - X.removeClass(p.__selector, 'drag'); - }); - }); - var f = document.createElement('div'); - return ( - S.extend(h.__selector.style, { - width: '122px', - height: '102px', - padding: '3px', - backgroundColor: '#222', - boxShadow: '0px 1px 3px rgba(0,0,0,0.3)' - }), - S.extend(h.__field_knob.style, { - position: 'absolute', - width: '12px', - height: '12px', - border: h.__field_knob_border + (h.__color.v < 0.5 ? '#fff' : '#000'), - boxShadow: '0px 1px 3px rgba(0,0,0,0.5)', - borderRadius: '12px', - zIndex: 1 - }), - S.extend(h.__hue_knob.style, { - position: 'absolute', - width: '15px', - height: '2px', - borderRight: '4px solid #fff', - zIndex: 1 - }), - S.extend(h.__saturation_field.style, { - width: '100px', - height: '100px', - border: '1px solid #555', - marginRight: '3px', - display: 'inline-block', - cursor: 'pointer' - }), - S.extend(f.style, { width: '100%', height: '100%', background: 'none' }), - l(f, 'top', 'rgba(0,0,0,0)', '#000'), - S.extend(h.__hue_field.style, { - width: '15px', - height: '100px', - border: '1px solid #555', - cursor: 'ns-resize', - position: 'absolute', - top: '3px', - right: '3px' - }), - d(h.__hue_field), - S.extend(h.__input.style, { - outline: 'none', - textAlign: 'center', - color: '#fff', - border: 0, - fontWeight: 'bold', - textShadow: h.__input_textShadow + 'rgba(0,0,0,0.7)' - }), - X.bind(h.__saturation_field, 'mousedown', o), - X.bind(h.__saturation_field, 'touchstart', o), - X.bind(h.__field_knob, 'mousedown', o), - X.bind(h.__field_knob, 'touchstart', o), - X.bind(h.__hue_field, 'mousedown', i), - X.bind(h.__hue_field, 'touchstart', i), - h.__saturation_field.appendChild(f), - h.__selector.appendChild(h.__field_knob), - h.__selector.appendChild(h.__saturation_field), - h.__selector.appendChild(h.__hue_field), - h.__hue_field.appendChild(h.__hue_knob), - h.domElement.appendChild(h.__input), - h.domElement.appendChild(h.__selector), - h.updateDisplay(), - h - ); - } - return ( - j(t, z), - P(t, [ - { - key: 'updateDisplay', - value: function () { - var e = R(this.getValue()); - if (!1 !== e) { - var t = !1; - S.each( - I.COMPONENTS, - function (n) { - if ( - !S.isUndefined(e[n]) && - !S.isUndefined(this.__color.__state[n]) && - e[n] !== this.__color.__state[n] - ) - return (t = !0), {}; - }, - this - ), - t && S.extend(this.__color.__state, e); - } - S.extend(this.__temp.__state, this.__color.__state), (this.__temp.a = 1); - var n = this.__color.v < 0.5 || this.__color.s > 0.5 ? 255 : 0, - o = 255 - n; - S.extend(this.__field_knob.style, { - marginLeft: 100 * this.__color.s - 7 + 'px', - marginTop: 100 * (1 - this.__color.v) - 7 + 'px', - backgroundColor: this.__temp.toHexString(), - border: this.__field_knob_border + 'rgb(' + n + ',' + n + ',' + n + ')' - }), - (this.__hue_knob.style.marginTop = 100 * (1 - this.__color.h / 360) + 'px'), - (this.__temp.s = 1), - (this.__temp.v = 1), - l(this.__saturation_field, 'left', '#fff', this.__temp.toHexString()), - (this.__input.value = this.__color.toString()), - S.extend(this.__input.style, { - backgroundColor: this.__color.toHexString(), - color: 'rgb(' + n + ',' + n + ',' + n + ')', - textShadow: this.__input_textShadow + 'rgba(' + o + ',' + o + ',' + o + ',.7)' - }); - } - } - ]), - t - ); - })(), - ee = ['-moz-', '-o-', '-webkit-', '-ms-', ''], - te = { - load: function (e, t) { - var n = t || document, - o = n.createElement('link'); - (o.type = 'text/css'), - (o.rel = 'stylesheet'), - (o.href = e), - n.getElementsByTagName('head')[0].appendChild(o); - }, - inject: function (e, t) { - var n = t || document, - o = document.createElement('style'); - (o.type = 'text/css'), (o.innerHTML = e); - var i = n.getElementsByTagName('head')[0]; - try { - i.appendChild(o); - } catch (e) {} - } - }, - ne = function (e, t) { - var n = e[t]; - return S.isArray(arguments[2]) || S.isObject(arguments[2]) - ? new Y(e, t, arguments[2]) - : S.isNumber(n) - ? S.isNumber(arguments[2]) && S.isNumber(arguments[3]) - ? S.isNumber(arguments[4]) - ? new q(e, t, arguments[2], arguments[3], arguments[4]) - : new q(e, t, arguments[2], arguments[3]) - : S.isNumber(arguments[4]) - ? new Q(e, t, { min: arguments[2], max: arguments[3], step: arguments[4] }) - : new Q(e, t, { min: arguments[2], max: arguments[3] }) - : S.isString(n) - ? new J(e, t) - : S.isFunction(n) - ? new Z(e, t, '') - : S.isBoolean(n) - ? new K(e, t) - : null; - }, - oe = - window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function (e) { - setTimeout(e, 1e3 / 60); - }, - ie = (function () { - function e() { - F(this, e), - (this.backgroundElement = document.createElement('div')), - S.extend(this.backgroundElement.style, { - backgroundColor: 'rgba(0,0,0,0.8)', - top: 0, - left: 0, - display: 'none', - zIndex: '1000', - opacity: 0, - WebkitTransition: 'opacity 0.2s linear', - transition: 'opacity 0.2s linear' - }), - X.makeFullscreen(this.backgroundElement), - (this.backgroundElement.style.position = 'fixed'), - (this.domElement = document.createElement('div')), - S.extend(this.domElement.style, { - position: 'fixed', - display: 'none', - zIndex: '1001', - opacity: 0, - WebkitTransition: '-webkit-transform 0.2s ease-out, opacity 0.2s linear', - transition: 'transform 0.2s ease-out, opacity 0.2s linear' - }), - document.body.appendChild(this.backgroundElement), - document.body.appendChild(this.domElement); - var t = this; - X.bind(this.backgroundElement, 'click', function () { - t.hide(); - }); - } - return ( - P(e, [ - { - key: 'show', - value: function () { - var e = this; - (this.backgroundElement.style.display = 'block'), - (this.domElement.style.display = 'block'), - (this.domElement.style.opacity = 0), - (this.domElement.style.webkitTransform = 'scale(1.1)'), - this.layout(), - S.defer(function () { - (e.backgroundElement.style.opacity = 1), - (e.domElement.style.opacity = 1), - (e.domElement.style.webkitTransform = 'scale(1)'); - }); - } - }, - { - key: 'hide', - value: function () { - var e = this, - t = function t() { - (e.domElement.style.display = 'none'), - (e.backgroundElement.style.display = 'none'), - X.unbind(e.domElement, 'webkitTransitionEnd', t), - X.unbind(e.domElement, 'transitionend', t), - X.unbind(e.domElement, 'oTransitionEnd', t); - }; - X.bind(this.domElement, 'webkitTransitionEnd', t), - X.bind(this.domElement, 'transitionend', t), - X.bind(this.domElement, 'oTransitionEnd', t), - (this.backgroundElement.style.opacity = 0), - (this.domElement.style.opacity = 0), - (this.domElement.style.webkitTransform = 'scale(1.1)'); - } - }, - { - key: 'layout', - value: function () { - (this.domElement.style.left = - window.innerWidth / 2 - X.getWidth(this.domElement) / 2 + 'px'), - (this.domElement.style.top = - window.innerHeight / 2 - X.getHeight(this.domElement) / 2 + 'px'); - } - } - ]), - e - ); - })(), - re = (function (e) { - if (e && 'undefined' != typeof window) { - var t = document.createElement('style'); - return ( - t.setAttribute('type', 'text/css'), (t.innerHTML = e), document.head.appendChild(t), e - ); - } - })( - ".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear;border:0;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button.close-top{position:relative}.dg.main .close-button.close-bottom{position:absolute}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-y:visible}.dg.a.has-save>ul.close-top{margin-top:0}.dg.a.has-save>ul.close-bottom{margin-top:27px}.dg.a.has-save>ul.closed{margin-top:0}.dg.a .save-row{top:0;z-index:1002}.dg.a .save-row.close-top{position:relative}.dg.a .save-row.close-bottom{position:fixed}.dg li{-webkit-transition:height .1s ease-out;-o-transition:height .1s ease-out;-moz-transition:height .1s ease-out;transition:height .1s ease-out;-webkit-transition:overflow .1s linear;-o-transition:overflow .1s linear;-moz-transition:overflow .1s linear;transition:overflow .1s linear}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li>*{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px;overflow:hidden}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .cr.function .property-name{width:100%}.dg .c{float:left;width:60%;position:relative}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:7px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .cr.color{overflow:visible}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.color{border-left:3px solid}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2FA1D6}.dg .cr.number input[type=text]{color:#2FA1D6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2FA1D6;max-width:100%}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n" - ); - te.inject(re); - var se = 'Default', - ae = (function () { - try { - return !!window.localStorage; - } catch (e) { - return !1; - } - })(), - le = void 0, - de = !0, - ce = void 0, - ue = !1, - _e = [], - he = function e(t) { - var n = this, - o = t || {}; - (this.domElement = document.createElement('div')), - (this.__ul = document.createElement('ul')), - this.domElement.appendChild(this.__ul), - X.addClass(this.domElement, 'dg'), - (this.__folders = {}), - (this.__controllers = []), - (this.__rememberedObjects = []), - (this.__rememberedObjectIndecesToControllers = []), - (this.__listening = []), - (o = S.defaults(o, { closeOnTop: !1, autoPlace: !0, width: e.DEFAULT_WIDTH })), - (o = S.defaults(o, { resizable: o.autoPlace, hideable: o.autoPlace })), - S.isUndefined(o.load) ? (o.load = { preset: se }) : o.preset && (o.load.preset = o.preset), - S.isUndefined(o.parent) && o.hideable && _e.push(this), - (o.resizable = S.isUndefined(o.parent) && o.resizable), - o.autoPlace && S.isUndefined(o.scrollable) && (o.scrollable = !0); - var i = ae && 'true' === localStorage.getItem(m(this, 'isLocal')), - r = void 0, - s = void 0; - if ( - (Object.defineProperties(this, { - parent: { - get: function () { - return o.parent; - } - }, - scrollable: { - get: function () { - return o.scrollable; - } - }, - autoPlace: { - get: function () { - return o.autoPlace; - } - }, - closeOnTop: { - get: function () { - return o.closeOnTop; - } - }, - preset: { - get: function () { - return n.parent ? n.getRoot().preset : o.load.preset; - }, - set: function (e) { - n.parent ? (n.getRoot().preset = e) : (o.load.preset = e), E(this), n.revert(); - } - }, - width: { - get: function () { - return o.width; - }, - set: function (e) { - (o.width = e), w(n, e); - } - }, - name: { - get: function () { - return o.name; - }, - set: function (e) { - (o.name = e), s && (s.innerHTML = o.name); - } - }, - closed: { - get: function () { - return o.closed; - }, - set: function (t) { - (o.closed = t), - o.closed - ? X.addClass(n.__ul, e.CLASS_CLOSED) - : X.removeClass(n.__ul, e.CLASS_CLOSED), - this.onResize(), - n.__closeButton && (n.__closeButton.innerHTML = t ? e.TEXT_OPEN : e.TEXT_CLOSED); - } - }, - load: { - get: function () { - return o.load; - } - }, - useLocalStorage: { - get: function () { - return i; - }, - set: function (e) { - ae && - ((i = e), - e ? X.bind(window, 'unload', r) : X.unbind(window, 'unload', r), - localStorage.setItem(m(n, 'isLocal'), e)); - } - } - }), - S.isUndefined(o.parent)) - ) { - if ( - ((this.closed = o.closed || !1), - X.addClass(this.domElement, e.CLASS_MAIN), - X.makeSelectable(this.domElement, !1), - ae && i) - ) { - n.useLocalStorage = !0; - var a = localStorage.getItem(m(this, 'gui')); - a && (o.load = JSON.parse(a)); - } - (this.__closeButton = document.createElement('div')), - (this.__closeButton.innerHTML = e.TEXT_CLOSED), - X.addClass(this.__closeButton, e.CLASS_CLOSE_BUTTON), - o.closeOnTop - ? (X.addClass(this.__closeButton, e.CLASS_CLOSE_TOP), - this.domElement.insertBefore(this.__closeButton, this.domElement.childNodes[0])) - : (X.addClass(this.__closeButton, e.CLASS_CLOSE_BOTTOM), - this.domElement.appendChild(this.__closeButton)), - X.bind(this.__closeButton, 'click', function () { - n.closed = !n.closed; - }); - } else { - void 0 === o.closed && (o.closed = !0); - var l = document.createTextNode(o.name); - X.addClass(l, 'controller-name'), (s = c(n, l)); - X.addClass(this.__ul, e.CLASS_CLOSED), - X.addClass(s, 'title'), - X.bind(s, 'click', function (e) { - return e.preventDefault(), (n.closed = !n.closed), !1; - }), - o.closed || (this.closed = !1); - } - o.autoPlace && - (S.isUndefined(o.parent) && - (de && - ((ce = document.createElement('div')), - X.addClass(ce, 'dg'), - X.addClass(ce, e.CLASS_AUTO_PLACE_CONTAINER), - document.body.appendChild(ce), - (de = !1)), - ce.appendChild(this.domElement), - X.addClass(this.domElement, e.CLASS_AUTO_PLACE)), - this.parent || w(n, o.width)), - (this.__resizeHandler = function () { - n.onResizeDebounced(); - }), - X.bind(window, 'resize', this.__resizeHandler), - X.bind(this.__ul, 'webkitTransitionEnd', this.__resizeHandler), - X.bind(this.__ul, 'transitionend', this.__resizeHandler), - X.bind(this.__ul, 'oTransitionEnd', this.__resizeHandler), - this.onResize(), - o.resizable && y(this), - (r = function () { - ae && - 'true' === localStorage.getItem(m(n, 'isLocal')) && - localStorage.setItem(m(n, 'gui'), JSON.stringify(n.getSaveObject())); - }), - (this.saveToLocalStorageIfPossible = r), - o.parent || - (function () { - var e = n.getRoot(); - (e.width += 1), - S.defer(function () { - e.width -= 1; - }); - })(); - }; - (he.toggleHide = function () { - (ue = !ue), - S.each(_e, function (e) { - e.domElement.style.display = ue ? 'none' : ''; - }); - }), - (he.CLASS_AUTO_PLACE = 'a'), - (he.CLASS_AUTO_PLACE_CONTAINER = 'ac'), - (he.CLASS_MAIN = 'main'), - (he.CLASS_CONTROLLER_ROW = 'cr'), - (he.CLASS_TOO_TALL = 'taller-than-window'), - (he.CLASS_CLOSED = 'closed'), - (he.CLASS_CLOSE_BUTTON = 'close-button'), - (he.CLASS_CLOSE_TOP = 'close-top'), - (he.CLASS_CLOSE_BOTTOM = 'close-bottom'), - (he.CLASS_DRAG = 'drag'), - (he.DEFAULT_WIDTH = 245), - (he.TEXT_CLOSED = 'Close Controls'), - (he.TEXT_OPEN = 'Open Controls'), - (he._keydownHandler = function (e) { - 'text' === document.activeElement.type || - (72 !== e.which && 72 !== e.keyCode) || - he.toggleHide(); - }), - X.bind(window, 'keydown', he._keydownHandler, !1), - S.extend(he.prototype, { - add: function (e, t) { - return f(this, e, t, { factoryArgs: Array.prototype.slice.call(arguments, 2) }); - }, - addColor: function (e, t) { - return f(this, e, t, { color: !0 }); - }, - remove: function (e) { - this.__ul.removeChild(e.__li), this.__controllers.splice(this.__controllers.indexOf(e), 1); - var t = this; - S.defer(function () { - t.onResize(); - }); - }, - destroy: function () { - if (this.parent) - throw new Error( - 'Only the root GUI should be removed with .destroy(). For subfolders, use gui.removeFolder(folder) instead.' - ); - this.autoPlace && ce.removeChild(this.domElement); - var e = this; - S.each(this.__folders, function (t) { - e.removeFolder(t); - }), - X.unbind(window, 'keydown', he._keydownHandler, !1), - u(this); - }, - addFolder: function (e) { - if (void 0 !== this.__folders[e]) - throw new Error('You already have a folder in this GUI by the name "' + e + '"'); - var t = { name: e, parent: this }; - (t.autoPlace = this.autoPlace), - this.load && - this.load.folders && - this.load.folders[e] && - ((t.closed = this.load.folders[e].closed), (t.load = this.load.folders[e])); - var n = new he(t); - this.__folders[e] = n; - var o = c(this, n.domElement); - return X.addClass(o, 'folder'), n; - }, - removeFolder: function (e) { - this.__ul.removeChild(e.domElement.parentElement), - delete this.__folders[e.name], - this.load && - this.load.folders && - this.load.folders[e.name] && - delete this.load.folders[e.name], - u(e); - var t = this; - S.each(e.__folders, function (t) { - e.removeFolder(t); - }), - S.defer(function () { - t.onResize(); - }); - }, - open: function () { - this.closed = !1; - }, - close: function () { - this.closed = !0; - }, - hide: function () { - this.domElement.style.display = 'none'; - }, - show: function () { - this.domElement.style.display = ''; - }, - onResize: function () { - var e = this.getRoot(); - if (e.scrollable) { - var t = X.getOffset(e.__ul).top, - n = 0; - S.each(e.__ul.childNodes, function (t) { - (e.autoPlace && t === e.__save_row) || (n += X.getHeight(t)); - }), - window.innerHeight - t - 20 < n - ? (X.addClass(e.domElement, he.CLASS_TOO_TALL), - (e.__ul.style.height = window.innerHeight - t - 20 + 'px')) - : (X.removeClass(e.domElement, he.CLASS_TOO_TALL), (e.__ul.style.height = 'auto')); - } - e.__resize_handle && - S.defer(function () { - e.__resize_handle.style.height = e.__ul.offsetHeight + 'px'; - }), - e.__closeButton && (e.__closeButton.style.width = e.width + 'px'); - }, - onResizeDebounced: S.debounce(function () { - this.onResize(); - }, 50), - remember: function () { - if ( - (S.isUndefined(le) && - ((le = new ie()).domElement.innerHTML = - '
\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n\n
\n\n
\n\n
'), - this.parent) - ) - throw new Error('You can only call remember on a top level GUI.'); - var e = this; - S.each(Array.prototype.slice.call(arguments), function (t) { - 0 === e.__rememberedObjects.length && v(e), - -1 === e.__rememberedObjects.indexOf(t) && e.__rememberedObjects.push(t); - }), - this.autoPlace && w(this, this.width); - }, - getRoot: function () { - for (var e = this; e.parent; ) e = e.parent; - return e; - }, - getSaveObject: function () { - var e = this.load; - return ( - (e.closed = this.closed), - this.__rememberedObjects.length > 0 && - ((e.preset = this.preset), - e.remembered || (e.remembered = {}), - (e.remembered[this.preset] = x(this))), - (e.folders = {}), - S.each(this.__folders, function (t, n) { - e.folders[n] = t.getSaveObject(); - }), - e - ); - }, - save: function () { - this.load.remembered || (this.load.remembered = {}), - (this.load.remembered[this.preset] = x(this)), - _(this, !1), - this.saveToLocalStorageIfPossible(); - }, - saveAs: function (e) { - this.load.remembered || - ((this.load.remembered = {}), (this.load.remembered[se] = x(this, !0))), - (this.load.remembered[e] = x(this)), - (this.preset = e), - g(this, e, !0), - this.saveToLocalStorageIfPossible(); - }, - revert: function (e) { - S.each( - this.__controllers, - function (t) { - this.getRoot().load.remembered ? p(e || this.getRoot(), t) : t.setValue(t.initialValue), - t.__onFinishChange && t.__onFinishChange.call(t, t.getValue()); - }, - this - ), - S.each(this.__folders, function (e) { - e.revert(e); - }), - e || _(this.getRoot(), !1); - }, - listen: function (e) { - var t = 0 === this.__listening.length; - this.__listening.push(e), t && C(this.__listening); - }, - updateDisplay: function () { - S.each(this.__controllers, function (e) { - e.updateDisplay(); - }), - S.each(this.__folders, function (e) { - e.updateDisplay(); - }); - } - }); - var pe = { Color: I, math: N, interpret: R }, - fe = { - Controller: z, - BooleanController: K, - OptionController: Y, - StringController: J, - NumberController: W, - NumberControllerBox: Q, - NumberControllerSlider: q, - FunctionController: Z, - ColorController: $ - }, - me = { dom: X }, - ge = { GUI: he }, - be = he, - ve = { color: pe, controllers: fe, dom: me, gui: ge, GUI: be }; - (e.color = pe), - (e.controllers = fe), - (e.dom = me), - (e.gui = ge), - (e.GUI = be), - (e.default = ve), - Object.defineProperty(e, '__esModule', { value: !0 }); -}); diff --git a/src/demos/include.glsl b/src/demos/include.glsl new file mode 100644 index 0000000..7c9c384 --- /dev/null +++ b/src/demos/include.glsl @@ -0,0 +1,39 @@ +uniform bool xrMode; +uniform mat4 xrProjectionMatrix, xrViewMatrix; +uniform mat4 xrRay[2], xrRayInv[2]; +uniform vec4 xrButton[2]; +uniform vec3 xrPosition; +uniform vec3 cameraYPD; + +vec3 cameraPos() { + if(xrMode) + return xrPosition; + vec3 p = vec3(0, 0, cameraYPD.z); + p.yz *= rot2(-cameraYPD.y); + p.xy *= rot2(-cameraYPD.x); + return p; +} + +vec4 wld2view(vec4 p) { + if(xrMode) + return xrViewMatrix * p; + p.xy *= rot2(cameraYPD.x); + p.yz *= rot2(cameraYPD.y); + p.z -= cameraYPD.z; + return p; +} + +vec4 view2proj(vec4 p) { + if(xrMode) + return xrProjectionMatrix * p; + const float near = 0.1, far = 10.0, fov = 1.0; + return vec4(p.xy / tan(fov / 2.0), (p.z * (near + far) + 2.0 * near * far) / (near - far), -p.z); +} + +vec4 wld2proj(vec4 p) { + return view2proj(wld2view(p)); +} + +vec4 wld2proj(vec3 p) { + return wld2proj(vec4(p, 1.0)); +} diff --git a/src/demos/index.ts b/src/demos/index.ts new file mode 100644 index 0000000..468c317 --- /dev/null +++ b/src/demos/index.ts @@ -0,0 +1,45 @@ +import NeuralCA from './NeuralCA.js'; +import DotCamera from './DotCamera.js'; +import MeshGrid from './MeshGrid.js'; +import ParticleLife from './ParticleLife.js'; +import ParticleLife3d from './ParticleLife3d.js'; +import BitField from './BitField.js'; +import TextureSamplers from './TextureSamplers.js'; +import GameOfLife from './GameOfLife.js'; +import ParticleLenia from './ParticleLenia.js'; +import FancyLenia from './FancyLenia.js'; +import Spectrogram from './Spectrogram.js'; +import Physarum from './Physarum.js'; +import Physarum3d from './Physarum3d.js'; +import SurfaceNormals from './SurfaceNormals.js'; +import CubeDeform from './CubeDeform.js'; +import ColorCube from './ColorCube.js'; +import Shadowmap from './Shadowmap.js'; +import Torus4d from './Torus4d.js'; +import DeferredShading from './DeferredShading.js'; +import Springs from './Springs.js'; +import ReactionDiffusion from './ReactionDiffusion.js'; + +export default { + NeuralCA, + DotCamera, + MeshGrid, + ParticleLife, + ParticleLife3d, + BitField, + TextureSamplers, + GameOfLife, + ParticleLenia, + FancyLenia, + Spectrogram, + Physarum, + Physarum3d, + SurfaceNormals, + CubeDeform, + ColorCube, + Shadowmap, + Torus4d, + DeferredShading, + Springs, + ReactionDiffusion +}; diff --git a/src/demos/main.js b/src/demos/main.js deleted file mode 100644 index d4b6fd7..0000000 --- a/src/demos/main.js +++ /dev/null @@ -1,340 +0,0 @@ -'use strict'; - -const $ = (s) => document.querySelector(s); -const setDisplay = (el, val) => { - if ($(el)) $(el).style.display = val; -}; - -class DemoApp { - constructor(demos, defaultDemo = 'ParticleLife3d') { - this.singleMode = demos.length == 1; - if (this.singleMode) { - defaultDemo = demos[0].name; - } - this.demos = Object.fromEntries(demos.map((c) => [c.name, c])); - - this.canvas = document.getElementById('c'); - const gl = this.canvas.getContext('webgl2', { - alpha: false, - antialias: true, - xrCompatible: true - }); - this.glsl = SwissGL(gl); - this.demo = null; - this.gui = null; - - this.xrDemos = Object.values(this.demos).filter((f) => f.Tags && f.Tags.includes('3d')); - this.xrSession = null; - this.xrRefSpace = null; - this.xrPose = null; - this.lookUpStartTime = 0; - this.haveVR = this.haveAR = false; - if (navigator.xr) { - navigator.xr.isSessionSupported('immersive-vr').then((supported) => { - this.haveVR = supported; - this.updateVRButtons(); - }); - navigator.xr.isSessionSupported('immersive-ar').then((supported) => { - this.haveAR = supported; - this.updateVRButtons(); - }); - } - - this.viewParams = { - canvasSize: new Float32Array(2), - pointer: new Float32Array(3), - cameraYPD: new Float32Array(3), - xrRay: new Float32Array(16 * 2), - xrRayInv: new Float32Array(16 * 2), - xrButton: new Float32Array(4 * 2) - }; - this.resetCamera(); - - this.glsl_include = ` - uniform bool xrMode; - uniform mat4 xrProjectionMatrix, xrViewMatrix; - uniform mat4 xrRay[2], xrRayInv[2]; - uniform vec4 xrButton[2]; - uniform vec3 xrPosition; - - uniform vec3 cameraYPD; - vec3 cameraPos() { - if (xrMode) return xrPosition; - vec3 p = vec3(0, 0, cameraYPD.z); - p.yz *= rot2(-cameraYPD.y); - p.xy *= rot2(-cameraYPD.x); - return p; - } - vec4 wld2view(vec4 p) { - if (xrMode) return xrViewMatrix * p; - p.xy *= rot2(cameraYPD.x); - p.yz *= rot2(cameraYPD.y); - p.z -= cameraYPD.z; - return p; - } - vec4 view2proj(vec4 p) { - if (xrMode) return xrProjectionMatrix*p; - const float near = 0.1, far = 10.0, fov = 1.0; - return vec4(p.xy/tan(fov/2.0), - (p.z*(near+far)+2.0*near*far)/(near-far), -p.z); - } - vec4 wld2proj(vec4 p) { - return view2proj(wld2view(p)); - } - vec4 wld2proj(vec3 p) { - return wld2proj(vec4(p,1.0)); - } - `; - this.withCamera = this.glsl.hook((glsl, params, target) => { - params = { ...params, Inc: this.glsl_include + (params.Inc || '') }; - if (target || !params.xrMode) { - return glsl(params, target); - } - delete params.Aspect; - let glLayer = this.xrSession.renderState.baseLayer; - target = { - bindTarget: (gl) => { - gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer); - return [glLayer.framebufferWidth, glLayer.framebufferHeight]; - } - }; - for (let view of this.xrPose.views) { - const vp = glLayer.getViewport(view); - params.View = [vp.x, vp.y, vp.width, vp.height]; - params.xrProjectionMatrix = view.projectionMatrix; - params.xrViewMatrix = view.transform.inverse.matrix; - let { x, y, z } = view.transform.position; - params.xrPosition = [x, y, z]; - glsl(params, target); - } - }); - - const setPointer = (e, buttons) => { - const [w, h] = this.viewParams.canvasSize; - const [x, y] = [e.offsetX - w / 2, h / 2 - e.offsetY]; - this.viewParams.pointer.set([x, y, buttons]); - return [x, y]; - }; - this.canvas.addEventListener('pointerdown', (e) => { - if (!e.isPrimary) return; - setPointer(e, e.buttons); - }); - this.canvas.addEventListener('pointerout', (e) => setPointer(e, 0)); - this.canvas.addEventListener('pointerup', (e) => setPointer(e, 0)); - this.canvas.addEventListener('pointermove', (e) => { - const [px, py, _] = this.viewParams.pointer; - const [x, y] = setPointer(e, e.buttons); - if (!e.isPrimary || e.buttons != 1) return; - let [yaw, pitch, dist] = this.viewParams.cameraYPD; - yaw -= (x - px) * 0.01; - pitch += (y - py) * 0.01; - pitch = Math.min(Math.max(pitch, 0), Math.PI); - this.viewParams.cameraYPD.set([yaw, pitch, dist]); - }); - - let name = location.hash.slice(1); - if (!(name in this.demos)) { - name = defaultDemo; - } - this.runDemo(name); - this.populatePreviews(); - - requestAnimationFrame(this.frame.bind(this)); - } - - resetCamera() { - this.viewParams.cameraYPD.set([(Math.PI * 3) / 4, Math.PI / 4, 1.8]); - } - - frame(t) { - requestAnimationFrame(this.frame.bind(this)); - if (this.xrSession) return; // skip canvas frames when XR is running - this.glsl.adjustCanvas(1); // fix devicePixelRatio to 1 - this.viewParams.canvasSize.set([this.canvas.clientWidth, this.canvas.clientHeight]); - - this.demo.frame(this.withCamera, { - time: t / 1000.0, - xrMode: false, - ...this.viewParams - }); - } - - xrFrame(t, xrFrame) { - this.xrSession.requestAnimationFrame(this.xrFrame.bind(this)); - this.xrPose = xrFrame.getViewerPose(this.xrRefSpace); - if (!this.xrPose) return; - this.viewParams.xrRay.fill(0.0); - this.viewParams.xrRayInv.fill(0.0); - this.viewParams.xrButton.fill(0.0); - const params = { time: t / 1000.0, xrMode: true, ...this.viewParams }; - for (let i = 0; i < 2; ++i) { - const inputSource = this.xrSession.inputSources[i]; - if (inputSource && inputSource.gamepad && inputSource.gamepad.buttons) { - inputSource.gamepad.buttons.forEach((btn, btnIdx) => { - if (btnIdx < 4) this.viewParams.xrButton[i * 4 + btnIdx] = btn.value || btn.pressed; - }); - } - if (!inputSource || !inputSource.targetRaySpace) continue; - const pose = xrFrame.getPose(inputSource.targetRaySpace, this.xrRefSpace); - if (!pose) continue; - this.viewParams.xrRay.set(pose.transform.matrix, i * 16); - this.viewParams.xrRayInv.set(pose.transform.inverse.matrix, i * 16); - } - - this.demo.frame(this.withCamera, params); - this.withCamera({ - ...params, - Mesh: [20, 20], - Grid: [2], - DepthTest: 1, - VP: ` - varying vec3 p = uv2sphere(UV); - varying vec4 buttons = xrButton[ID.x]; - VPos = wld2proj(xrRay[ID.x]*vec4(p*vec3(0.02, 0.02, 0.1),1));`, - FP: ` - vec3 c = p*0.5+0.5; - FOut = vec4(c*0.5,1); - float b = c.z*4.0; - if (b<4.0 && buttons[int(b)]>fract(b)) FOut += 0.5;` - }); - - const lookUpCoef = -this.xrPose.transform.matrix[10]; - if (!this.singleMode && lookUpCoef > 0.5) { - const dt = (t - this.lookUpStartTime) / 1000; - if (dt > 1) { - this.lookUpStartTime = t; - let i = this.xrDemos.indexOf(this.demo.constructor); - i = (i + 1) % this.xrDemos.length; - this.runDemo(this.xrDemos[i].name); - } else { - this.withCamera({ - ...params, - Mesh: [20, 20], - dt, - DepthTest: 1, - VP: ` - vec3 p = uv2sphere(UV)*0.6*clamp(1.0-dt, 0.0, 0.8) + vec3(-2.0, 0.0, 3.0); - VPos = wld2proj(vec4(p,1));`, - FP: `UV,0.5,1` - }); - } - } else { - this.lookUpStartTime = t; - } - } - - toggleXR(xr) { - if (!this.xrSession) { - navigator.xr.requestSession(`immersive-${xr}`).then((session) => { - this.xrSession = session; - session.addEventListener('end', () => { - this.xrSession = null; - }); - session.updateRenderState({ baseLayer: new XRWebGLLayer(session, this.glsl.gl) }); - session.requestReferenceSpace('local').then((refSpace) => { - this.xrRefSpace = refSpace.getOffsetReferenceSpace( - new XRRigidTransform( - { x: 0, y: -0.25, z: -1.0, w: 1 }, // position offset - { x: 0.5, y: 0.5, z: 0.5, w: -0.5 } - ) // rotate z up - ); - session.requestAnimationFrame(this.xrFrame.bind(this)); - }); - }); - } else { - this.xrSession.end(); - } - } - - runDemo(name) { - if (this.demo) { - if (this.gui) this.gui.destroy(); - if (this.demo.free) this.demo.free(); - this.glsl.reset(); - this.demo = this.gui = null; - } - if (!this.singleMode) location.hash = name; - if (self.dat) { - this.gui = new dat.GUI(); - this.gui.domElement.id = 'gui'; - this.gui.hide(); - } - this.demo = new this.demos[name](this.withCamera, this.gui); - if (this.gui && this.gui.__controllers.length == 0) { - this.gui.destroy(); - this.gui = null; - } - setDisplay('#settingButton', this.gui ? 'block' : 'none'); - if ($('#sourceLink')) { - $('#sourceLink').href = `https://github.com/google/swissgl/blob/main/demo/${name}.js`; - } - this.updateVRButtons(); - this.resetCamera(); - } - - updateVRButtons() { - setDisplay('#vrButton', 'none'); - setDisplay('#arButton', 'none'); - const tags = this.demo && this.demo.constructor.Tags; - if (tags && tags.includes('3d')) { - if (this.haveVR) setDisplay('#vrButton', 'block'); - if (this.haveAR) setDisplay('#arButton', 'block'); - } - } - - populatePreviews() { - const panel = document.getElementById('cards'); - if (!panel) return; - Object.keys(this.demos).forEach((name) => { - const el = document.createElement('div'); - el.classList.add('card'); - el.innerHTML = `${name}`; - el.addEventListener('click', () => this.runDemo(name)); - panel.appendChild(el); - }); - } - - // helper function to render demo preview images - genPreviews() { - const panel = document.getElementById('cards'); - panel.innerHTML = ''; - const canvas = document.createElement('canvas'); - canvas.width = 400; - canvas.height = 300; - const glsl = SwissGL(canvas); - const withCamera = glsl.hook((glsl, p, t) => - glsl({ ...p, Inc: this.glsl_include + (p.Inc || '') }, t) - ); - Object.keys(this.demos).forEach((name) => { - if (name == 'Spectrogram') return; - const dummyGui = new dat.GUI(); - const demo = new this.demos[name](withCamera, dummyGui); - dummyGui.destroy(); - this.resetCamera(); - for (let i = 0; i < 60 * 5; ++i) { - withCamera({ Clear: 0 }, ''); - demo.frame(withCamera, { time: i / 60.0, ...this.viewParams }); - } - const el = document.createElement('div'); - const data = canvas.toDataURL('image/jpeg', 0.95); - el.innerHTML = ` - - ${name}`; - panel.appendChild(el); - if (demo.free) demo.free(); - glsl.reset(); - }); - } - - toggleGui() { - if (!this.gui) return; - const style = this.gui.domElement.style; - style.display = style.display == 'none' ? '' : 'none'; - } - - fullscreen() { - const { canvas } = this; - const f = canvas.requestFullscreen || canvas.webkitRequestFullscreen; - if (f) f.apply(canvas); - } -} diff --git a/src/demos/style.css b/src/demos/style.css deleted file mode 100644 index 2789e33..0000000 --- a/src/demos/style.css +++ /dev/null @@ -1,101 +0,0 @@ -body { - box-sizing: border-box; - background: black; - margin: 0px; - color: white; - overflow: hidden; - font-family: 'Roboto Mono', monospace; - user-select: none; -} - -#panel { - width: 200px; - position: fixed; - background: rgba(0, 0, 0, 0.5); -} - -#panel summary { - padding: 8px; -} - -#cards { - overflow: auto; - height: 95vh; -} - -.card { - padding: 4px; - margin: 8px; - border: 1px solid grey; - border-radius: 5px; - font-size: 14px; -} - -#panel img { - max-width: 100%; -} - -#demo { - width: 100%; - height: 100vh; -} - -#c { - width: 100%; - height: 100%; - background: black; - touch-action: none; -} - -#gui { - position: fixed; - bottom: 0px; - right: 50px; -} -#buttons { - position: fixed; - bottom: 10px; - right: 10px; -} -button { - appearance: none; - min-width: 48px; - height: 40px; - margin: 8px; - font-size: 24px; - background-color: rgba(0, 0, 0, 0.5); - color: white; - border: none; - text-align: center; - text-decoration: none; - display: block; - cursor: pointer; -} -button:hover { - background-color: rgba(80, 80, 80, 0.8); -} -#buttons a { - text-decoration: none; - color: white; -} -#aboutButton { - position: fixed; - top: 10px; - right: 10px; -} -a { - color: aquamarine; -} - -#about { - width: 90%; - max-width: 400px; - background-color: rgba(80, 80, 80, 0.9); - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - padding: 8px; - border-radius: 8px; - user-select: text; -} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 0e5c67c..3ae4c25 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,92 +1,507 @@ - -SwissGL demos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- SwissGL demos -
+ + +
+ SwissGL demos +
+ {#each Object.keys(demos) as name} +
runDemo(name)}>{name}
+ {/each} +
+ });
-
- -
- - - - - -
+
{ + hideAbout(); + if (window.innerWidth < 500) { + // close menu on small screens + panel.removeAttribute('open'); + } + }} +> + { + if (!e.isPrimary) return; + setPointer(e, e.buttons); + }} + on:pointerout={(e) => setPointer(e, 0)} + on:pointerup={(e) => setPointer(e, 0)} + on:pointermove={(e) => { + const [px, py, _] = viewParams.pointer; + const [x, y] = setPointer(e, e.buttons); + if (!e.isPrimary || e.buttons != 1) return; + let [yaw, pitch, dist] = viewParams.cameraYPD; + yaw -= (x - px) * 0.01; + pitch += (y - py) * 0.01; + pitch = Math.min(Math.max(pitch, 0), Math.PI); + viewParams.cameraYPD.set([yaw, pitch, dist]); + }} + > + +
+ + + + + +
- -
-

- SwissGL - is a prototype of a minimal yet expressive GPU library built on - WebGL2. A single "glsl()" function runs GLSL code - snippets on the GPU and manages the resulting texture buffers. -

-

- This page contains a few demos built using the library. Click - the "<>" button to see the source of the current example. -

- + +
+

+ SwissGL + is a prototype of a minimal yet expressive GPU library built on WebGL2. A single + "glsl()" function runs GLSL code snippets on the GPU and manages the resulting texture + buffers. +

+

+ This page contains a few demos built using the library. Click the "<>" button + to see the source of the current example. +

+
- \ No newline at end of file +
+ + From 1f4a88848488291e91cbc2a943c691bdbc9881ee Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 23:18:32 +0100 Subject: [PATCH 14/17] Migrate other routes to svelte kit --- src/demos/Criticality.svelte | 130 ++++++++++++++++++++++++++++ src/demos/Tiny.svelte | 30 +++++++ src/demos/index.svelte.ts | 19 ++++ src/routes/[name]/+layout.svelte | 33 +++++++ src/routes/[name]/+page.svelte | 8 ++ src/routes/[name]/style.css | 12 +++ src/routes/criticality/+page.svelte | 85 ------------------ src/routes/tiny/+page.svelte | 21 ----- 8 files changed, 232 insertions(+), 106 deletions(-) create mode 100644 src/demos/Criticality.svelte create mode 100644 src/demos/Tiny.svelte create mode 100644 src/demos/index.svelte.ts create mode 100644 src/routes/[name]/+layout.svelte create mode 100644 src/routes/[name]/+page.svelte create mode 100644 src/routes/[name]/style.css delete mode 100644 src/routes/criticality/+page.svelte delete mode 100644 src/routes/tiny/+page.svelte diff --git a/src/demos/Criticality.svelte b/src/demos/Criticality.svelte new file mode 100644 index 0000000..fbf0885 --- /dev/null +++ b/src/demos/Criticality.svelte @@ -0,0 +1,130 @@ + + + (phaseDir *= -1)} /> + + diff --git a/src/demos/Tiny.svelte b/src/demos/Tiny.svelte new file mode 100644 index 0000000..b981ee4 --- /dev/null +++ b/src/demos/Tiny.svelte @@ -0,0 +1,30 @@ + + + + + diff --git a/src/demos/index.svelte.ts b/src/demos/index.svelte.ts new file mode 100644 index 0000000..b4e2b4e --- /dev/null +++ b/src/demos/index.svelte.ts @@ -0,0 +1,19 @@ +import Criticality from './Criticality.svelte'; +import Tiny from './Tiny.svelte'; + +const demos = { + Criticality, + Tiny +}; + +export default demos; + +export const names = Object.keys(demos); + +const camelToKebab = (str: string) => + str + .replace(/[A-Z]/, (letter) => letter.toLowerCase()) + .replace(/[A-Z]/g, (letter: string) => `-${letter.toLowerCase()}`); + +export const nameToPath = Object.fromEntries(names.map((name) => [name, camelToKebab(name)])); +export const pathToName = Object.fromEntries(names.map((name) => [camelToKebab(name), name])); diff --git a/src/routes/[name]/+layout.svelte b/src/routes/[name]/+layout.svelte new file mode 100644 index 0000000..5be7adc --- /dev/null +++ b/src/routes/[name]/+layout.svelte @@ -0,0 +1,33 @@ + + +
    + {#each Object.entries(nameToPath) as [name, path]} +
  • + {name} +
  • + {/each} +
+ + + + diff --git a/src/routes/[name]/+page.svelte b/src/routes/[name]/+page.svelte new file mode 100644 index 0000000..774a37e --- /dev/null +++ b/src/routes/[name]/+page.svelte @@ -0,0 +1,8 @@ + + + diff --git a/src/routes/[name]/style.css b/src/routes/[name]/style.css new file mode 100644 index 0000000..ce6c7c2 --- /dev/null +++ b/src/routes/[name]/style.css @@ -0,0 +1,12 @@ +body, +html { + margin: 0; + padding: 0; + overflow: hidden; + background-color: #000; +} + +body > div { + width: 100vw; + height: 100vh; +} diff --git a/src/routes/criticality/+page.svelte b/src/routes/criticality/+page.svelte deleted file mode 100644 index 82d2deb..0000000 --- a/src/routes/criticality/+page.svelte +++ /dev/null @@ -1,85 +0,0 @@ - -Tiny SwissGL example - - - - - - -- \ No newline at end of file diff --git a/src/routes/tiny/+page.svelte b/src/routes/tiny/+page.svelte deleted file mode 100644 index 8750995..0000000 --- a/src/routes/tiny/+page.svelte +++ /dev/null @@ -1,21 +0,0 @@ - -Tiny SwissGL example - - - - - - From 685752a54559307d86396bf24cbff761e565bfad Mon Sep 17 00:00:00 2001 From: jpaquim Date: Fri, 6 Oct 2023 23:32:03 +0100 Subject: [PATCH 15/17] Add postpackage bundling step using esbuild --- package.json | 3 + pnpm-lock.yaml | 231 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/package.json b/package.json index 1c9027b..5c2fb08 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { "name": "swissgl.svelte", + "license": "Apache-2.0", "version": "0.0.1", "scripts": { "dev": "vite dev", "build": "vite build && npm run package", "preview": "vite preview", "package": "svelte-kit sync && svelte-package && publint", + "postpackage": "esbuild --bundle --minify --platform=neutral dist/swissgl.js --outfile=dist/swissgl.min.js", "prepublishOnly": "npm run package", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", @@ -33,6 +35,7 @@ "@types/webxr": "^0.5.5", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "esbuild": "^0.19.4", "eslint": "^8.28.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-svelte": "^2.30.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d18dfe..85177b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ devDependencies: '@typescript-eslint/parser': specifier: ^6.0.0 version: 6.7.4(eslint@8.50.0)(typescript@5.2.2) + esbuild: + specifier: ^0.19.4 + version: 0.19.4 eslint: specifier: ^8.28.0 version: 8.50.0 @@ -84,6 +87,15 @@ packages: dev: true optional: true + /@esbuild/android-arm64@0.19.4: + resolution: {integrity: sha512-mRsi2vJsk4Bx/AFsNBqOH2fqedxn5L/moT58xgg51DjX1la64Z3Npicut2VbhvDFO26qjWtPMsVxCd80YTFVeg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm@0.18.20: resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} engines: {node: '>=12'} @@ -93,6 +105,15 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.19.4: + resolution: {integrity: sha512-uBIbiYMeSsy2U0XQoOGVVcpIktjLMEKa7ryz2RLr7L/vTnANNEsPVAh4xOv7ondGz6ac1zVb0F8Jx20rQikffQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64@0.18.20: resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} engines: {node: '>=12'} @@ -102,6 +123,15 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.19.4: + resolution: {integrity: sha512-4iPufZ1TMOD3oBlGFqHXBpa3KFT46aLl6Vy7gwed0ZSYgHaZ/mihbYb4t7Z9etjkC9Al3ZYIoOaHrU60gcMy7g==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64@0.18.20: resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} engines: {node: '>=12'} @@ -111,6 +141,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.19.4: + resolution: {integrity: sha512-Lviw8EzxsVQKpbS+rSt6/6zjn9ashUZ7Tbuvc2YENgRl0yZTktGlachZ9KMJUsVjZEGFVu336kl5lBgDN6PmpA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64@0.18.20: resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} engines: {node: '>=12'} @@ -120,6 +159,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.19.4: + resolution: {integrity: sha512-YHbSFlLgDwglFn0lAO3Zsdrife9jcQXQhgRp77YiTDja23FrC2uwnhXMNkAucthsf+Psr7sTwYEryxz6FPAVqw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64@0.18.20: resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} engines: {node: '>=12'} @@ -129,6 +177,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.19.4: + resolution: {integrity: sha512-vz59ijyrTG22Hshaj620e5yhs2dU1WJy723ofc+KUgxVCM6zxQESmWdMuVmUzxtGqtj5heHyB44PjV/HKsEmuQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64@0.18.20: resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} engines: {node: '>=12'} @@ -138,6 +195,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.19.4: + resolution: {integrity: sha512-3sRbQ6W5kAiVQRBWREGJNd1YE7OgzS0AmOGjDmX/qZZecq8NFlQsQH0IfXjjmD0XtUYqr64e0EKNFjMUlPL3Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64@0.18.20: resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} engines: {node: '>=12'} @@ -147,6 +213,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.19.4: + resolution: {integrity: sha512-ZWmWORaPbsPwmyu7eIEATFlaqm0QGt+joRE9sKcnVUG3oBbr/KYdNE2TnkzdQwX6EDRdg/x8Q4EZQTXoClUqqA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm@0.18.20: resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} engines: {node: '>=12'} @@ -156,6 +231,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.19.4: + resolution: {integrity: sha512-z/4ArqOo9EImzTi4b6Vq+pthLnepFzJ92BnofU1jgNlcVb+UqynVFdoXMCFreTK7FdhqAzH0vmdwW5373Hm9pg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32@0.18.20: resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} engines: {node: '>=12'} @@ -165,6 +249,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.19.4: + resolution: {integrity: sha512-EGc4vYM7i1GRUIMqRZNCTzJh25MHePYsnQfKDexD8uPTCm9mK56NIL04LUfX2aaJ+C9vyEp2fJ7jbqFEYgO9lQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64@0.18.20: resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} engines: {node: '>=12'} @@ -174,6 +267,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.19.4: + resolution: {integrity: sha512-WVhIKO26kmm8lPmNrUikxSpXcgd6HDog0cx12BUfA2PkmURHSgx9G6vA19lrlQOMw+UjMZ+l3PpbtzffCxFDRg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el@0.18.20: resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} engines: {node: '>=12'} @@ -183,6 +285,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.19.4: + resolution: {integrity: sha512-keYY+Hlj5w86hNp5JJPuZNbvW4jql7c1eXdBUHIJGTeN/+0QFutU3GrS+c27L+NTmzi73yhtojHk+lr2+502Mw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64@0.18.20: resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} engines: {node: '>=12'} @@ -192,6 +303,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.19.4: + resolution: {integrity: sha512-tQ92n0WMXyEsCH4m32S21fND8VxNiVazUbU4IUGVXQpWiaAxOBvtOtbEt3cXIV3GEBydYsY8pyeRMJx9kn3rvw==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64@0.18.20: resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} engines: {node: '>=12'} @@ -201,6 +321,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.19.4: + resolution: {integrity: sha512-tRRBey6fG9tqGH6V75xH3lFPpj9E8BH+N+zjSUCnFOX93kEzqS0WdyJHkta/mmJHn7MBaa++9P4ARiU4ykjhig==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x@0.18.20: resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} engines: {node: '>=12'} @@ -210,6 +339,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.19.4: + resolution: {integrity: sha512-152aLpQqKZYhThiJ+uAM4PcuLCAOxDsCekIbnGzPKVBRUDlgaaAfaUl5NYkB1hgY6WN4sPkejxKlANgVcGl9Qg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64@0.18.20: resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} engines: {node: '>=12'} @@ -219,6 +357,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.19.4: + resolution: {integrity: sha512-Mi4aNA3rz1BNFtB7aGadMD0MavmzuuXNTaYL6/uiYIs08U7YMPETpgNn5oue3ICr+inKwItOwSsJDYkrE9ekVg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64@0.18.20: resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} engines: {node: '>=12'} @@ -228,6 +375,15 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.19.4: + resolution: {integrity: sha512-9+Wxx1i5N/CYo505CTT7T+ix4lVzEdz0uCoYGxM5JDVlP2YdDC1Bdz+Khv6IbqmisT0Si928eAxbmGkcbiuM/A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64@0.18.20: resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} engines: {node: '>=12'} @@ -237,6 +393,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.19.4: + resolution: {integrity: sha512-MFsHleM5/rWRW9EivFssop+OulYVUoVcqkyOkjiynKBCGBj9Lihl7kh9IzrreDyXa4sNkquei5/DTP4uCk25xw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64@0.18.20: resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} engines: {node: '>=12'} @@ -246,6 +411,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.19.4: + resolution: {integrity: sha512-6Xq8SpK46yLvrGxjp6HftkDwPP49puU4OF0hEL4dTxqCbfx09LyrbUj/D7tmIRMj5D5FCUPksBbxyQhp8tmHzw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64@0.18.20: resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} engines: {node: '>=12'} @@ -255,6 +429,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.19.4: + resolution: {integrity: sha512-PkIl7Jq4mP6ke7QKwyg4fD4Xvn8PXisagV/+HntWoDEdmerB2LTukRZg728Yd1Fj+LuEX75t/hKXE2Ppk8Hh1w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32@0.18.20: resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} engines: {node: '>=12'} @@ -264,6 +447,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.19.4: + resolution: {integrity: sha512-ga676Hnvw7/ycdKB53qPusvsKdwrWzEyJ+AtItHGoARszIqvjffTwaaW3b2L6l90i7MO9i+dlAW415INuRhSGg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64@0.18.20: resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} engines: {node: '>=12'} @@ -273,6 +465,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.19.4: + resolution: {integrity: sha512-HP0GDNla1T3ZL8Ko/SHAS2GgtjOg+VmWnnYLhuTksr++EnduYB0f3Y2LzHsUwb2iQ13JGoY6G3R8h6Du/WG6uA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -900,6 +1101,36 @@ packages: '@esbuild/win32-x64': 0.18.20 dev: true + /esbuild@0.19.4: + resolution: {integrity: sha512-x7jL0tbRRpv4QUyuDMjONtWFciygUxWaUM1kMX2zWxI0X2YWOt7MSA0g4UdeSiHM8fcYVzpQhKYOycZwxTdZkA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.19.4 + '@esbuild/android-arm64': 0.19.4 + '@esbuild/android-x64': 0.19.4 + '@esbuild/darwin-arm64': 0.19.4 + '@esbuild/darwin-x64': 0.19.4 + '@esbuild/freebsd-arm64': 0.19.4 + '@esbuild/freebsd-x64': 0.19.4 + '@esbuild/linux-arm': 0.19.4 + '@esbuild/linux-arm64': 0.19.4 + '@esbuild/linux-ia32': 0.19.4 + '@esbuild/linux-loong64': 0.19.4 + '@esbuild/linux-mips64el': 0.19.4 + '@esbuild/linux-ppc64': 0.19.4 + '@esbuild/linux-riscv64': 0.19.4 + '@esbuild/linux-s390x': 0.19.4 + '@esbuild/linux-x64': 0.19.4 + '@esbuild/netbsd-x64': 0.19.4 + '@esbuild/openbsd-x64': 0.19.4 + '@esbuild/sunos-x64': 0.19.4 + '@esbuild/win32-arm64': 0.19.4 + '@esbuild/win32-ia32': 0.19.4 + '@esbuild/win32-x64': 0.19.4 + dev: true + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} From a48bcc420a8c369c84c1786c4cbffbcd836adfb7 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Sat, 7 Oct 2023 13:12:16 +0100 Subject: [PATCH 16/17] Add svelte wrappers for basic canvas and loop functions --- src/lib/Canvas.svelte | 41 +++++++++++++++++++++++++++++++++++++++++ src/lib/Loop.svelte | 22 ++++++++++++++++++++++ src/lib/index.ts | 3 +++ 3 files changed, 66 insertions(+) create mode 100644 src/lib/Canvas.svelte create mode 100644 src/lib/Loop.svelte diff --git a/src/lib/Canvas.svelte b/src/lib/Canvas.svelte new file mode 100644 index 0000000..0a741ce --- /dev/null +++ b/src/lib/Canvas.svelte @@ -0,0 +1,41 @@ + + + + + diff --git a/src/lib/Loop.svelte b/src/lib/Loop.svelte new file mode 100644 index 0000000..3de325a --- /dev/null +++ b/src/lib/Loop.svelte @@ -0,0 +1,22 @@ + + + { + glsl = e.detail; + glsl.loop(f); + }} + {...$$restProps}> diff --git a/src/lib/index.ts b/src/lib/index.ts index c572590..3b62237 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,2 +1,5 @@ export * from './swissgl.js'; export { SwissGL as default } from './swissgl.js'; + +export { default as Canvas } from './Canvas.svelte'; +export { default as Loop } from './Loop.svelte'; From 854314f32838b768a6d290d432055c9b1e484ce2 Mon Sep 17 00:00:00 2001 From: jpaquim Date: Sat, 7 Oct 2023 13:27:50 +0100 Subject: [PATCH 17/17] Start migrating demo classes to svelte components --- src/demos/BitField.svelte | 13 +++ src/demos/ColorCube.svelte | 28 ++++++ src/demos/CubeDeform.svelte | 41 ++++++++ src/demos/DotCamera.svelte | 152 ++++++++++++++++++++++++++++++ src/demos/GameOfLife.svelte | 34 +++++++ src/demos/Gradient.svelte | 28 ++++++ src/demos/MeshGrid.svelte | 21 +++++ src/demos/NeuralCA.svelte | 74 +++++++++++++++ src/demos/ParticleLenia.svelte | 137 +++++++++++++++++++++++++++ src/demos/ParticleLife.svelte | 84 +++++++++++++++++ src/demos/SurfaceNormals.svelte | 36 +++++++ src/demos/TextureSamplers.svelte | 31 ++++++ src/demos/TinyCanvas.svelte | 24 +++++ src/demos/TinyLoop.svelte | 16 ++++ src/demos/Torus4d.svelte | 27 ++++++ src/demos/index.svelte.ts | 51 +++++++++- src/demos/lib/CameraCanvas.svelte | 62 ++++++++++++ src/demos/{ => lib}/include.glsl | 0 src/routes/+page.svelte | 2 +- 19 files changed, 859 insertions(+), 2 deletions(-) create mode 100644 src/demos/BitField.svelte create mode 100644 src/demos/ColorCube.svelte create mode 100644 src/demos/CubeDeform.svelte create mode 100644 src/demos/DotCamera.svelte create mode 100644 src/demos/GameOfLife.svelte create mode 100644 src/demos/Gradient.svelte create mode 100644 src/demos/MeshGrid.svelte create mode 100644 src/demos/NeuralCA.svelte create mode 100644 src/demos/ParticleLenia.svelte create mode 100644 src/demos/ParticleLife.svelte create mode 100644 src/demos/SurfaceNormals.svelte create mode 100644 src/demos/TextureSamplers.svelte create mode 100644 src/demos/TinyCanvas.svelte create mode 100644 src/demos/TinyLoop.svelte create mode 100644 src/demos/Torus4d.svelte create mode 100644 src/demos/lib/CameraCanvas.svelte rename src/demos/{ => lib}/include.glsl (100%) diff --git a/src/demos/BitField.svelte b/src/demos/BitField.svelte new file mode 100644 index 0000000..d9ab237 --- /dev/null +++ b/src/demos/BitField.svelte @@ -0,0 +1,13 @@ + + + { + glsl.adjustCanvas(1); + + glsl({ t: time, k, FP: `1-((I.x+int(t*40.))/4^(I.y+int(t*20.))/4)%int(k)` }); + }} +> diff --git a/src/demos/ColorCube.svelte b/src/demos/ColorCube.svelte new file mode 100644 index 0000000..4a97876 --- /dev/null +++ b/src/demos/ColorCube.svelte @@ -0,0 +1,28 @@ + + + { + glsl({ + ...v, + Inc, + Grid: [10, 10, 10], + Clear: [0.2, 0.2, 0.3, 1], + Aspect: 'fit', + DepthTest: 1, + AlphaCoverage: 1, + VP: ` +vec3 p = color = vec3(ID)/vec3(Grid-1); +varying vec3 color = p; +vec4 pos = vec4(p-0.5, 1); +pos = wld2view(pos); +pos.xy += XY*0.03; // offset quad corners in view space +VPos = view2proj(pos);`, + FP: ` +float r = length(XY); +float alpha = smoothstep(1.0, 1.0-fwidth(r), r); +FOut = vec4(color, alpha);` + }); + }} +> diff --git a/src/demos/CubeDeform.svelte b/src/demos/CubeDeform.svelte new file mode 100644 index 0000000..8fa9937 --- /dev/null +++ b/src/demos/CubeDeform.svelte @@ -0,0 +1,41 @@ + + + { + glsl({ + ...v, + Inc, + time: t / 1e3, + Grid: [6, 1], + Mesh: [20, 20], + Aspect: 'fit', + DepthTest: 1, + VP: ` +vec3 surface_f(vec2 xy) { + vec3 pos = cubeVert(xy, ID.x); + pos += sin(pos*PI+time).zxy*0.2; + pos = mix(pos, normalize(pos)*1.5, sin(time)*0.8+0.2); + pos.xy *= rot2(PI/4.+time*0.2); + pos.yz *= rot2(PI/3.0); + return pos*0.4; +} +void vertex() { + varying vec3 color = cubeVert(vec2(0), ID.x)*0.5+0.5, normal; + vec4 v = vec4(SURF(surface_f, XY, normal, 1e-3), 1.0); + varying vec3 eyeDir = cameraPos()-v.xyz; + VPos = wld2proj(v); +}`, + FP: ` +vec3 n = normalize(normal); +vec3 lightDir = normalize(vec3(0,1,1)); +float diffuse = dot(n, lightDir)*0.5+0.5; +vec3 halfVec = normalize(lightDir+normalize(eyeDir)); +float spec = smoothstep(0.998, 0.999, dot(halfVec, n)); +FOut.rgb = color*diffuse + 0.3*spec; +vec2 m = UV*4.0; +FOut.rgb = mix(FOut.rgb, vec3(1.0), (isoline(m.x)+isoline(m.y))*0.25);` + }); + }} +> diff --git a/src/demos/DotCamera.svelte b/src/demos/DotCamera.svelte new file mode 100644 index 0000000..d853a0e --- /dev/null +++ b/src/demos/DotCamera.svelte @@ -0,0 +1,152 @@ + + + { + const glsl = e.detail; + raf = requestAnimationFrame(function render(t) { + raf = requestAnimationFrame(render); + glsl.adjustCanvas(1); + canvasSize.set([canvas.clientWidth, canvas.clientHeight]); + + const time = t / 1000; + + let tex; + if (video.videoWidth) { + tex = glsl({}, { data: video, tag: 'video' }); + } else { + tex = glsl( + { time, FP: `step(0.0, sin(length(XY)*20.0-time*3.0+atan(XY.x,XY.y)*3.))*0.25` }, + { size: [512, 512], tag: 'tmp' } + ); + } + const blendParams = dayMode ? { Clear: 1, Blend: 'd-s' } : { Clear: 0, Blend: 'd+s' }; + const lum = glsl( + { + tex: tex.edge.linear, + ...blendParams, + rgbMode, + VP: `vec2 r = vec2(ViewSize)/vec2(tex_size()); r /= max(r.x, r.y); VPos.xy = XY/r;`, + FP: ` +FOut = tex(1.0-UV); +if (!rgbMode) { + FOut.r = dot(FOut.rgb, vec3(0.21,0.72,0.07)); +}` + }, + { scale: 1 / 2, tag: 'lum' } + ); + const merged = glsl( + { + T: lum.edge.miplinear, + FP: ` +for (float lod=0.; lod<8.0; lod+=1.0) {FOut += textureLod(T, UV, lod);} +FOut /= 8.0;` + }, + { size: lum.size, format: 'rgba16f', tag: 'merged' } + ); + const imgForce = glsl( + { + T: merged.edge, + FP: ` +vec2 s=T_step(); +vec4 a=T(UV-s), b=T(UV+vec2(s.x,-s.y)), c=T(UV+vec2(-s.x,s.y)), d=T(UV+s); +FOut = b+d-a-c; FOut1 = c+d-a-b;` + }, + { size: lum.size, layern: 2, format: 'rgba16f', tag: 'grad' } + ); + + const arg = { canvasSize, rgbMode }; + const field = glsl( + {}, + { scale: 1 / 4, format: 'rgba16f', layern: 3, filter: 'linear', tag: 'field' } + ); + let points; + for (let i = 0; i < 10; ++i) { + points = glsl( + { + ...arg, + field: field.edge, + imgForce: imgForce.edge.linear, + seed: Math.random() * 124237, + FP: ` +int c = rgbMode ? I.x%3 : 0; +vec4 p=Src(I), f=field(p.xy, c); +if (p.w == 0.0) { + FOut = vec4(hash(ivec3(I, seed)).xy, 0.0, 1.0); + return; +} +if (f.z>3.0) {p.xy = hash(ivec3(I,seed)).xy;} +vec2 imf = vec2(imgForce(p.xy,0)[c], imgForce(p.xy,1)[c]); +vec2 force = f.xy*10.0 + imf.xy*20.0; +p.xy = clamp(p.xy + force/canvasSize, vec2(0), vec2(1)); +FOut = p;` + }, + { scale: (rgbMode ? 1.7 : 1) / 8, story: 2, format: 'rgba32f', tag: 'points' } + ); + glsl( + { + ...arg, + points: points[0], + Grid: points[0].size, + Blend: 's+d', + Clear: 0, + VP: ` +VPos.xy = (points(ID.xy).xy + XY*15.0/canvasSize)*2.0-1.0; +int c = rgbMode ? ID.x%3 : 0; +varying vec3 color = vec3(c==0,c==1,c==2);`, + FP: ` +vec4 v = vec4(vec3(XY,1.)*exp(-dot(XY,XY)*vec3(4,4,8)), 0); +FOut=v*color.r; FOut1=v*color.g; FOut2=v*color.b;` + }, + field + ); + } + // draw dots on screen + glsl({ + ...arg, + points: points[0], + Grid: points[0].size, + ...blendParams, + VP: ` +VPos.xy = (points(ID.xy).xy + XY*4.0/canvasSize)*2.0-1.0; +int c = ID.x%3; +varying vec3 color = rgbMode ? vec3(c==0,c==1,c==2) : vec3(1);`, + FP: `color*exp(-dot(XY,XY)*3.0),1` + }); + }); + }} +> diff --git a/src/demos/GameOfLife.svelte b/src/demos/GameOfLife.svelte new file mode 100644 index 0000000..9e36bc5 --- /dev/null +++ b/src/demos/GameOfLife.svelte @@ -0,0 +1,34 @@ + + + { + glsl.adjustCanvas(1); + + const state = glsl( + { + FP: ` + FOut = Src(I); + if (FOut.w == 0.0) { + float v = float((I.x^I.y+100)%9==0); + FOut = vec4(v,0,0,1); + return; + } + ivec2 sz = Src_size(); + int x=I.x,l=(x-1+sz.x)%sz.x,r=(x+1)%sz.x; + int y=I.y,d=(y-1+sz.y)%sz.y,u=(y+1)%sz.y; + #define S(u,v) (Src(ivec2(u,v)).x) + float nhood = S(l,y)+S(r,y)+S(x,u)+S(x,d)+S(l,u)+S(l,d)+S(r,u)+S(r,d); + float v = float(nhood<3.5 && nhood>1.5 && (FOut.x+nhood) > 2.5); + FOut = vec4(v,0,0,1);` + }, + { scale: 1 / 4, story: 2, tag: 'state' } + ); + const fade = glsl( + { S: state[0], Blend: 'd*sa+s', FP: `S(I).xxx,0.9` }, + { size: state[0].size, tag: 'fade' } + ); + glsl({ fade, FP: `fade(UV).x` }); + }} +> diff --git a/src/demos/Gradient.svelte b/src/demos/Gradient.svelte new file mode 100644 index 0000000..d9f96a1 --- /dev/null +++ b/src/demos/Gradient.svelte @@ -0,0 +1,28 @@ + + + { + const glsl = e.detail; + raf = requestAnimationFrame(function render(t) { + t /= 1000; // ms to sec + glsl.adjustCanvas(); + glsl({ + t, // pass uniform 't' to GLSL + Mesh: [10, 10], // draw a 10x10 tessellated plane mesh + // Vertex shader expression returns vec4 vertex position in + // WebGL clip space. 'XY' and 'UV' are vec2 input vertex + // coordinates in [-1,1] and [0,1] ranges. + VP: `XY*0.8+sin(t+XY.yx*2.0)*0.2,0,1`, + FP: `UV,0.5,1` + }); + raf = requestAnimationFrame(render); + }); + }} +> diff --git a/src/demos/MeshGrid.svelte b/src/demos/MeshGrid.svelte new file mode 100644 index 0000000..ba4dde7 --- /dev/null +++ b/src/demos/MeshGrid.svelte @@ -0,0 +1,21 @@ + + + { + glsl.adjustCanvas(); + glsl({ + time, + Grid: [5, 5], + Mesh: [4, 4], + Aspect: 'fit', + VP: ` +varying vec3 color = hash(ID); +vec2 pos = vec2(ID) + 0.5 + XY*(0.5-0.5/vec2(Mesh+1)); +pos += sin(UV*TAU+time).yx*0.1*(sin(time*0.5)); +VPos = vec4(2.0*pos/vec2(Grid)-1.0, 0.0, 1.0);`, + FP: `mix(color, vec3(1.0), wireframe()*0.5),1` + }); + }} +> diff --git a/src/demos/NeuralCA.svelte b/src/demos/NeuralCA.svelte new file mode 100644 index 0000000..2ce125a --- /dev/null +++ b/src/demos/NeuralCA.svelte @@ -0,0 +1,74 @@ + + + { + const glsl = e.detail; + raf = requestAnimationFrame(function render(t) { + raf = requestAnimationFrame(render); + + glsl.adjustCanvas(1); + + const state = glsl( + { + W, + b, + FP: ` +uniform mat4 W[4]; +uniform vec4 b; +vec4 rule(vec4 s, vec4 p) { + return 1e-3*(b + W[0]*s + W[1]*p + W[2]*abs(s) + W[3]*abs(p)); +} +void fragment() { + vec4 s = Src(UV); + if (s == vec4(0)) { + ivec2 I = ivec2(gl_FragCoord.xy); + FOut = 0.1+vec4(hash(I.xyy).x)*0.4; + return; + } + vec2 dp = Src_step(); + float x=UV.x, y=UV.y; + float l=x-dp.x, r=x+dp.x, u=y-dp.y, d=y+dp.y; + #define R(x, y) Src(vec2(x, y)) + // perception + vec4 p = R(l,u)*vec4(1,1,-1, 1) + R(x,u)*vec4(2,2,0, 2) + R(r,u)*vec4(1,1,1, 1) + + R(l,y)*vec4(2,2,-2, 0) + s*vec4(-12,-12,0, 0) + R(r,y)*vec4(2,2,2, 0) + + R(l,d)*vec4(1,1,-1,-1) + R(x,d)*vec4(2,2,0,-2) + R(r,d)*vec4(1,1,1,-1); + vec4 ds = rule(s-0.5, p); // NCA rule application + FOut = s+ds; +}` + }, + { story: 2, scale: 1 / 4, tag: 'state' } + ); + + glsl({ tex: state[0], FP: `tex(UV)*2.-.5` }); + }); + }} +> diff --git a/src/demos/ParticleLenia.svelte b/src/demos/ParticleLenia.svelte new file mode 100644 index 0000000..ac9400c --- /dev/null +++ b/src/demos/ParticleLenia.svelte @@ -0,0 +1,137 @@ + + + { + const _glsl = e.detail; + glsl = _glsl.hook((glsl, p, t) => + glsl( + { + ...p, + Inc: + ` +vec2 peak_f(float x, float mu, float sigma) { + float t = (x-mu)/sigma; + float y = exp(-t*t); + return vec2(y, -2.0*t*y/sigma); +}\n` + (p.Inc || '') + }, + t + ) + ); + + // gui.add(this, 'step_n', 0, 50, 1); + // gui.add(params, 'mu_k', 0.0, 5.0).onChange(() => this.updateNormCoef()); + // gui.add(params, 'sigma_k', 0.1, 2.0).onChange(() => this.updateNormCoef()); + // gui.add(params, 'mu_g', 0.0, 1.5); + // gui.add(params, 'sigma_g', 0.1, 1.0); + // gui.add(params, 'c_rep', 0.0, 2.0); + // gui.add(this, 'reset'); + + reset(); + + raf = requestAnimationFrame(function render(t) { + raf = requestAnimationFrame(render); + + _glsl.adjustCanvas(1); + + for (let i = 0; i < step_n; ++i) { + step(); + } + renderSpots(); + }); + }} +> diff --git a/src/demos/ParticleLife.svelte b/src/demos/ParticleLife.svelte new file mode 100644 index 0000000..79f21a2 --- /dev/null +++ b/src/demos/ParticleLife.svelte @@ -0,0 +1,84 @@ + + + { + const glsl = e.detail; + const K = 6; + const F = glsl( + { K, FP: `float(I.x==I.y) + 0.1*float(I.x==(I.y+1)%int(K))` }, + { size: [K, K], format: 'r16f', tag: 'F' } + ); + glsl({ F, FP: `F(I/20).x*3.` }); + const points = glsl({}, { size: [30, 10], story: 3, format: 'rgba32f', tag: 'points' }); + for (let i = 0; i < 2; ++i) { + glsl( + { + K, + seed: 123, + FP: ` +vec2 pos = (hash(ivec3(I, seed)).xy-0.5)*10.; +float color = floor(UV.x*K); +FOut = vec4(pos, 0.0, color);` + }, + points + ); + } + raf = requestAnimationFrame(function render(t) { + glsl( + { + F, + worldExtent: 15, + repulsion: 2, + inertia: 0.1, + dt: 0.1, + past: points[1], + FP: ` +vec3 wrap(vec3 p) { + return (fract(p/worldExtent+0.5)-0.5)*worldExtent; +} +void fragment() { + FOut = Src(I); + vec3 force = vec3(0); + for (int y = 0; y < ViewSize.y; ++y) + for (int x = 0; x < ViewSize.x; ++x) { + vec4 data1 = Src(ivec2(x, y)); + vec3 dpos = wrap(data1.xyz-FOut.xyz); + float r = length(dpos); + if (r>3.) continue; + dpos /= r+1e-8; + float rep = max(1.-r, 0.)*repulsion; + float f = F(ivec2(FOut.w, data1.w)).x; + float inter = f*max(1.-abs(r-2.), 0.); + force += dpos*(inter-rep); + } + vec3 vel = wrap(FOut.xyz-past(I).xyz)*pow(inertia, dt); + FOut.xyz = wrap(FOut.xyz+vel+.5*force*dt*dt); +}` + }, + points + ); + glsl.adjustCanvas(); + glsl({ + K, + worldExtent: 15, + points: points[0], + Grid: points[0].size, + Aspect: 'fit', + Blend: 'd*(1-sa)+s*sa', + VP: ` +vec4 d = points(ID.xy); +varying vec3 color = cos((d.w/K+vec3(0,0.33,0.66))*TAU)*0.5+0.5; +VPos.xy = 2.*(d.xy+XY/8.)/worldExtent;`, + FP: `color,smoothstep(1.,0.6,length(XY))` + }); + raf = requestAnimationFrame(render); + }); + }} +> diff --git a/src/demos/SurfaceNormals.svelte b/src/demos/SurfaceNormals.svelte new file mode 100644 index 0000000..a6ad210 --- /dev/null +++ b/src/demos/SurfaceNormals.svelte @@ -0,0 +1,36 @@ + + + { + glsl({ + ...v, + time: t / 1e3, + Inc, + Mesh: [64, 128], + Grid: [5, 5], + Aspect: 'fit', + DepthTest: 1, + VP: ` +vec3 surface_f(vec2 p) { + vec2 c = sin(time+p*vec2(ID)*TAU); + vec3 pos = torus(p, 1.0, 0.4 + 0.1*c.x + 0.15*c.y)/8.0; + pos.xy += (vec2(ID)-vec2(Grid-1)*0.5)*0.4; + return pos; +} +void vertex() { + varying vec3 normal; + vec4 pos = vec4(SURF(surface_f, UV, normal, 1e-3), 1.0); + VPos = wld2proj(pos); +}`, + FP: ` +FOut = vec4(normal*0.6, 1); +vec2 m = UV*vec2(Mesh)/4.0; +FOut.rgb += (isoline(m.x)+isoline(m.y))*0.2; +// useful for debugging incorrect face ordering +// FOut.r += float(!gl_FrontFacing); +` + }); + }} +> diff --git a/src/demos/TextureSamplers.svelte b/src/demos/TextureSamplers.svelte new file mode 100644 index 0000000..af21d74 --- /dev/null +++ b/src/demos/TextureSamplers.svelte @@ -0,0 +1,31 @@ + + + { + glsl.adjustCanvas(1); + + const T = glsl( + { + time, + FP: ` +vec2 p = rot2(sin(time)*8.0*smoothstep(1.03,0.0, length(XY)))*XY; +FOut = vec4(smoothstep(-1.,1.,(p/fwidth(p)).y));` + }, + { size: [32, 32], tag: 'T' } + ); + + glsl({ + Aspect: 'mean', + A: T.edge, + B: T.mirror, + C: T.linear.mirror, + D: T.linear.repeat, + FP: ` +bool x=XY.x<0.0, y=XY.y<0.0; +vec2 p = fract(XY)*2.9-0.95; +FOut = y? (x?C(p):D(p)) : (x?A(p):B(p));` + }); + }} +> diff --git a/src/demos/TinyCanvas.svelte b/src/demos/TinyCanvas.svelte new file mode 100644 index 0000000..4568795 --- /dev/null +++ b/src/demos/TinyCanvas.svelte @@ -0,0 +1,24 @@ + + + { + glsl = e.detail; + glsl.loop(({ time }) => { + glsl.adjustCanvas(); + glsl({ + time, + Aspect: 'cover', + FP: ` +sin(length(XY)*vec3(30,30.5,31) +-time+atan(XY.x,XY.y)*3.),1` + }); + }); + }} +> diff --git a/src/demos/TinyLoop.svelte b/src/demos/TinyLoop.svelte new file mode 100644 index 0000000..4f42fd4 --- /dev/null +++ b/src/demos/TinyLoop.svelte @@ -0,0 +1,16 @@ + + + { + glsl.adjustCanvas(); + glsl({ + time, + Aspect: 'cover', + FP: ` +sin(length(XY)*vec3(30,30.5,31) +-time+atan(XY.x,XY.y)*3.),1` + }); + }} +> diff --git a/src/demos/Torus4d.svelte b/src/demos/Torus4d.svelte new file mode 100644 index 0000000..f0f249a --- /dev/null +++ b/src/demos/Torus4d.svelte @@ -0,0 +1,27 @@ + + + { + glsl({ + ...v, + Inc, + time: t / 1e3, + Mesh: [100, 100], + Aspect: 'fit', + AlphaCoverage: 1, + DepthTest: 1, + VP: ` +vec4 p = vec4(cos(XY*PI), sin(XY*PI))*0.6; +p.xw *= rot2(time*0.4); +VPos = wld2proj(vec4(p.xyz/(1.0-p.w)*0.5, 1));`, + FP: ` +vec2 v = UV*rot2(PI/4.)*64.0/sqrt(2.); +v = smoothstep(0.0, 1.0, (abs(v-round(v))-0.02)/fwidth(v)); +float a = 1.0-v.x*v.y; +if (a<0.1) discard; +FOut = vec4(gl_FrontFacing?vec3(.9,.9,.6):vec3(.6,.6,.9), a);` + }); + }} +> diff --git a/src/demos/index.svelte.ts b/src/demos/index.svelte.ts index b4e2b4e..154b6fc 100644 --- a/src/demos/index.svelte.ts +++ b/src/demos/index.svelte.ts @@ -1,9 +1,58 @@ +import NeuralCA from './NeuralCA.svelte'; +import DotCamera from './DotCamera.svelte'; +import MeshGrid from './MeshGrid.svelte'; +import ParticleLife from './ParticleLife.svelte'; +// import ParticleLife3d from './ParticleLife3d.svelte'; +import BitField from './BitField.svelte'; +import TextureSamplers from './TextureSamplers.svelte'; +import GameOfLife from './GameOfLife.svelte'; +import ParticleLenia from './ParticleLenia.svelte'; +// import FancyLenia from './FancyLenia.svelte'; +// import Spectrogram from './Spectrogram.svelte'; +// import Physarum from './Physarum.svelte'; +// import Physarum3d from './Physarum3d.svelte'; +import SurfaceNormals from './SurfaceNormals.svelte'; +import CubeDeform from './CubeDeform.svelte'; +import ColorCube from './ColorCube.svelte'; +// import Shadowmap from './Shadowmap.svelte'; +import Torus4d from './Torus4d.svelte'; +// import DeferredShading from './DeferredShading.svelte'; +// import Springs from './Springs.svelte'; +// import ReactionDiffusion from './ReactionDiffusion.svelte'; + import Criticality from './Criticality.svelte'; import Tiny from './Tiny.svelte'; +import TinyCanvas from './TinyCanvas.svelte'; +import TinyLoop from './TinyLoop.svelte'; +import Gradient from './Gradient.svelte'; const demos = { + NeuralCA, + DotCamera, + MeshGrid, + ParticleLife, + // ParticleLife3d, + BitField, + TextureSamplers, + GameOfLife, + ParticleLenia, + // FancyLenia, + // Spectrogram, + // Physarum, + // Physarum3d, + SurfaceNormals, + CubeDeform, + ColorCube, + // Shadowmap, + Torus4d, + // DeferredShading, + // Springs, + // ReactionDiffusion Criticality, - Tiny + Tiny, + TinyCanvas, + TinyLoop, + Gradient }; export default demos; diff --git a/src/demos/lib/CameraCanvas.svelte b/src/demos/lib/CameraCanvas.svelte new file mode 100644 index 0000000..fda5f2d --- /dev/null +++ b/src/demos/lib/CameraCanvas.svelte @@ -0,0 +1,62 @@ + + + { + const glsl = e.detail; + + resetCamera(); + + raf = requestAnimationFrame(function render(t) { + raf = requestAnimationFrame(render); + glsl.adjustCanvas(1); + canvasSize.set([canvas.clientWidth, canvas.clientHeight]); + + r({ Inc: glsl_include, t, v: { canvasSize, pointer, cameraYPD }, glsl }); + }); + }} + on:pointerdown={(e) => { + if (!e.isPrimary) return; + setPointer(e, e.buttons); + }} + on:pointerout={(e) => setPointer(e, 0)} + on:pointerup={(e) => setPointer(e, 0)} + on:pointermove={(e) => { + const [px, py, _] = pointer; + const [x, y] = setPointer(e, e.buttons); + if (!e.isPrimary || e.buttons != 1) return; + let [yaw, pitch, dist] = cameraYPD; + yaw -= (x - px) * 0.01; + pitch += (y - py) * 0.01; + pitch = Math.min(Math.max(pitch, 0), Math.PI); + cameraYPD.set([yaw, pitch, dist]); + }} +> diff --git a/src/demos/include.glsl b/src/demos/lib/include.glsl similarity index 100% rename from src/demos/include.glsl rename to src/demos/lib/include.glsl diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 3ae4c25..0d7ec98 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -2,7 +2,7 @@ import { onDestroy, onMount } from 'svelte'; import SwissGL, { type GL, type WrappedSwissGL } from '$lib/index.js'; import demos from '../demos/index.js'; - import glsl_include from '../demos/include.glsl?raw'; + import glsl_include from '../demos/lib/include.glsl?raw'; export let defaultDemo = 'ParticleLife3d';