fragment-tools 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +101 -0
- package/bin/index.js +19 -0
- package/docs/README.md +18 -0
- package/docs/api/CLI.md +44 -0
- package/docs/api/renderers.md +80 -0
- package/docs/api/sketch.md +216 -0
- package/docs/api/triggers.md +101 -0
- package/docs/guide/about.md +16 -0
- package/docs/guide/exports.md +86 -0
- package/docs/guide/external-dependencies.md +22 -0
- package/docs/guide/getting-started.md +113 -0
- package/docs/guide/hot-shader-reloading.md +20 -0
- package/docs/guide/shortcuts.md +12 -0
- package/docs/guide/triggers.png +0 -0
- package/docs/guide/using-triggers.md +39 -0
- package/examples/cube-three.js +34 -0
- package/examples/ellipse-p5.js +26 -0
- package/examples/icon.fs +96 -0
- package/examples/icon.js +63 -0
- package/examples/icon.png +0 -0
- package/examples/icon.transparent.png +0 -0
- package/examples/package-lock.json +40 -0
- package/examples/package.json +15 -0
- package/examples/shape-2d.js +45 -0
- package/examples/shape-three.js +49 -0
- package/examples/shape-tree.fs +3 -0
- package/package.json +37 -0
- package/screenshot.png +0 -0
- package/src/cli/db.js +17 -0
- package/src/cli/index.js +198 -0
- package/src/cli/log.js +26 -0
- package/src/cli/plugins/check-dependencies.js +77 -0
- package/src/cli/plugins/db.js +12 -0
- package/src/cli/plugins/hot-shader-reload.js +86 -0
- package/src/cli/plugins/hot-sketch-reload.js +39 -0
- package/src/cli/plugins/screenshot.js +31 -0
- package/src/cli/server.js +140 -0
- package/src/cli/templates/2d.js +15 -0
- package/src/cli/templates/fragment.fs +10 -0
- package/src/cli/templates/fragment.js +18 -0
- package/src/cli/templates/index.js +24 -0
- package/src/cli/templates/p5.js +13 -0
- package/src/cli/templates/three-fragment.js +53 -0
- package/src/cli/templates/three-orthographic.js +23 -0
- package/src/cli/templates/three-perspective.js +20 -0
- package/src/cli/ws.js +92 -0
- package/src/client/app/App.svelte +8 -0
- package/src/client/app/client.js +68 -0
- package/src/client/app/components/IconCross.svelte +29 -0
- package/src/client/app/components/Init.svelte +13 -0
- package/src/client/app/components/KeyBinding.svelte +32 -0
- package/src/client/app/inputs/Input.js +15 -0
- package/src/client/app/inputs/Keyboard.js +21 -0
- package/src/client/app/inputs/MIDI.js +144 -0
- package/src/client/app/inputs/Mouse.js +5 -0
- package/src/client/app/inputs/Webcam.js +98 -0
- package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +88 -0
- package/src/client/app/lib/canvas-recorder/FFMPEGRecorder.js +56 -0
- package/src/client/app/lib/canvas-recorder/FrameRecorder.js +40 -0
- package/src/client/app/lib/canvas-recorder/GIFRecorder.js +52 -0
- package/src/client/app/lib/canvas-recorder/MP4Recorder.js +46 -0
- package/src/client/app/lib/canvas-recorder/WebMRecorder.js +30 -0
- package/src/client/app/lib/canvas-recorder/mp4.js +20 -0
- package/src/client/app/lib/canvas-recorder/mp4.wasm +0 -0
- package/src/client/app/lib/canvas-recorder/utils.js +22 -0
- package/src/client/app/lib/gl/Geometry.js +39 -0
- package/src/client/app/lib/gl/Program.js +130 -0
- package/src/client/app/lib/gl/Renderer.js +148 -0
- package/src/client/app/lib/gl/Texture.js +114 -0
- package/src/client/app/lib/gl/index.js +109 -0
- package/src/client/app/lib/gl/utils.js +5 -0
- package/src/client/app/lib/helpers/frameDebounce.js +40 -0
- package/src/client/app/lib/loader/index.js +20 -0
- package/src/client/app/lib/loader/loadImage.js +19 -0
- package/src/client/app/lib/loader/loadScript.js +14 -0
- package/src/client/app/lib/paper-sizes.js +104 -0
- package/src/client/app/lib/presets.js +12 -0
- package/src/client/app/lib/tempo/Analyser.js +165 -0
- package/src/client/app/lib/tempo/Range.js +97 -0
- package/src/client/app/lib/tempo/index.js +138 -0
- package/src/client/app/modules/AudioAnalyser/Range.svelte +93 -0
- package/src/client/app/modules/AudioAnalyser/Spectrum.svelte +31 -0
- package/src/client/app/modules/AudioAnalyser.svelte +70 -0
- package/src/client/app/modules/Console/ConsoleLine.svelte +254 -0
- package/src/client/app/modules/Console.svelte +82 -0
- package/src/client/app/modules/Exports.svelte +105 -0
- package/src/client/app/modules/MidiPanel.svelte +106 -0
- package/src/client/app/modules/Monitor.svelte +62 -0
- package/src/client/app/modules/Params.svelte +112 -0
- package/src/client/app/renderers/2DRenderer.js +5 -0
- package/src/client/app/renderers/FragmentRenderer.js +62 -0
- package/src/client/app/renderers/OGLRenderer.js +0 -0
- package/src/client/app/renderers/P5Renderer.js +39 -0
- package/src/client/app/renderers/THREERenderer.js +128 -0
- package/src/client/app/stores/audioAnalysis.js +10 -0
- package/src/client/app/stores/console.js +76 -0
- package/src/client/app/stores/errors.js +25 -0
- package/src/client/app/stores/exports.js +28 -0
- package/src/client/app/stores/index.js +2 -0
- package/src/client/app/stores/layout.js +187 -0
- package/src/client/app/stores/multisampling.js +16 -0
- package/src/client/app/stores/props.js +44 -0
- package/src/client/app/stores/renderers.js +60 -0
- package/src/client/app/stores/rendering.js +111 -0
- package/src/client/app/stores/sketches.js +40 -0
- package/src/client/app/stores/time.js +27 -0
- package/src/client/app/stores/utils.js +66 -0
- package/src/client/app/transitions/fade.js +17 -0
- package/src/client/app/transitions/index.js +12 -0
- package/src/client/app/transitions/splitX.js +16 -0
- package/src/client/app/transitions/splitY.js +16 -0
- package/src/client/app/triggers/Keyboard.js +95 -0
- package/src/client/app/triggers/MIDI.js +122 -0
- package/src/client/app/triggers/Mouse.js +96 -0
- package/src/client/app/triggers/Trigger.js +71 -0
- package/src/client/app/triggers/index.js +19 -0
- package/src/client/app/triggers/shared.js +37 -0
- package/src/client/app/ui/Build.svelte +96 -0
- package/src/client/app/ui/ErrorOverlay.svelte +130 -0
- package/src/client/app/ui/Field.svelte +262 -0
- package/src/client/app/ui/FieldGroup.svelte +103 -0
- package/src/client/app/ui/FieldSection.svelte +123 -0
- package/src/client/app/ui/FieldSpace.svelte +37 -0
- package/src/client/app/ui/FieldTrigger.svelte +263 -0
- package/src/client/app/ui/FieldTriggers.svelte +58 -0
- package/src/client/app/ui/FloatingParams.svelte +49 -0
- package/src/client/app/ui/Layout.svelte +50 -0
- package/src/client/app/ui/LayoutColumn.svelte +9 -0
- package/src/client/app/ui/LayoutComponent.svelte +279 -0
- package/src/client/app/ui/LayoutResizer.svelte +218 -0
- package/src/client/app/ui/LayoutRoot.svelte +11 -0
- package/src/client/app/ui/LayoutRow.svelte +9 -0
- package/src/client/app/ui/LayoutToolbar.svelte +264 -0
- package/src/client/app/ui/Module.svelte +154 -0
- package/src/client/app/ui/ModuleHeaderAction.svelte +87 -0
- package/src/client/app/ui/ModuleHeaderButton.svelte +21 -0
- package/src/client/app/ui/ModuleHeaderSelect.svelte +50 -0
- package/src/client/app/ui/ModuleRenderer.svelte +38 -0
- package/src/client/app/ui/OutputRenderer.svelte +149 -0
- package/src/client/app/ui/ParamsMultisampling.svelte +109 -0
- package/src/client/app/ui/ParamsOutput.svelte +139 -0
- package/src/client/app/ui/Preview.svelte +15 -0
- package/src/client/app/ui/SelectChevrons.svelte +25 -0
- package/src/client/app/ui/SketchRenderer.svelte +672 -0
- package/src/client/app/ui/SketchSelect.svelte +49 -0
- package/src/client/app/ui/fields/ButtonInput.svelte +54 -0
- package/src/client/app/ui/fields/CheckboxInput.svelte +70 -0
- package/src/client/app/ui/fields/ColorInput.svelte +187 -0
- package/src/client/app/ui/fields/FieldInputRow.svelte +13 -0
- package/src/client/app/ui/fields/ImageInput.svelte +145 -0
- package/src/client/app/ui/fields/Input.svelte +120 -0
- package/src/client/app/ui/fields/ListInput.svelte +106 -0
- package/src/client/app/ui/fields/NumberInput.svelte +114 -0
- package/src/client/app/ui/fields/ProgressInput.svelte +90 -0
- package/src/client/app/ui/fields/Select.svelte +116 -0
- package/src/client/app/ui/fields/TextInput.svelte +18 -0
- package/src/client/app/ui/fields/Vec2Input.svelte +5 -0
- package/src/client/app/ui/fields/Vec3Input.svelte +6 -0
- package/src/client/app/ui/fields/VectorInput.svelte +102 -0
- package/src/client/app/utils/canvas.utils.js +229 -0
- package/src/client/app/utils/color.utils.js +427 -0
- package/src/client/app/utils/file.utils.js +77 -0
- package/src/client/app/utils/glsl.utils.js +14 -0
- package/src/client/app/utils/glslErrors.js +154 -0
- package/src/client/app/utils/index.js +39 -0
- package/src/client/app/utils/math.utils.js +23 -0
- package/src/client/app/utils/props.utils.js +53 -0
- package/src/client/index.html +18 -0
- package/src/client/main.js +9 -0
- package/src/client/public/css/global.css +115 -0
- package/src/client/public/favicon.ico +0 -0
- package/src/client/public/fonts/Inter-Bold.woff2 +0 -0
- package/src/client/public/fonts/Inter-Italic.woff2 +0 -0
- package/src/client/public/fonts/Inter-Regular.woff2 +0 -0
- package/src/client/public/fonts/Inter-SemiBold.woff2 +0 -0
- package/src/client/public/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/src/client/public/icons/chevron-bottom.svg +3 -0
- package/src/client/public/icons/chevron-right.svg +3 -0
- package/src/client/public/icons/chevron-top.svg +3 -0
- package/src/client/public/icons/columns-horizontal.svg +4 -0
- package/src/client/public/icons/columns-vertical.svg +4 -0
- package/src/client/public/icons/folder-plus.svg +6 -0
- package/src/client/public/icons/lock.svg +4 -0
- package/src/client/public/icons/picture-in-picture.svg +4 -0
- package/src/client/public/icons/trash.svg +5 -0
- package/src/client/public/icons/trigger.svg +8 -0
- package/src/client/public/icons/unlock.svg +4 -0
- package/src/client/public/js/ffmpeg.min.js +2 -0
- package/src/client/public/js/ffmpeg.min.js.map +1 -0
- package/src/client/public/js/gif.js +2 -0
- package/src/client/public/js/gif.worker.js +2 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { writable, get } from "svelte/store";
|
|
2
|
+
import { sketches } from "./sketches";
|
|
3
|
+
|
|
4
|
+
export const props = writable({});
|
|
5
|
+
|
|
6
|
+
sketches.subscribe((sketches) => {
|
|
7
|
+
const $props = get(props);
|
|
8
|
+
|
|
9
|
+
Object.keys(sketches).forEach((key) => {
|
|
10
|
+
const sketch = sketches[key];
|
|
11
|
+
|
|
12
|
+
if (sketch) { // sketch can be undefined if failed to load
|
|
13
|
+
$props[key] = reconcile(sketch.props, $props[key]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
props.set($props);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function reconcile(newProps = {}, existingProps = {}) {
|
|
22
|
+
Object.keys(newProps).forEach(propKey => {
|
|
23
|
+
newProps[propKey]._initialValue = newProps[propKey].value;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (existingProps) {
|
|
27
|
+
Object.keys(existingProps).forEach((propKey) => {
|
|
28
|
+
let newProp = newProps[propKey];
|
|
29
|
+
|
|
30
|
+
if (newProp) {
|
|
31
|
+
let prevProp = existingProps[propKey];
|
|
32
|
+
let overrideValue =
|
|
33
|
+
typeof prevProp._initialValue === "number" &&
|
|
34
|
+
prevProp._initialValue === newProp._initialValue;
|
|
35
|
+
|
|
36
|
+
if (overrideValue) {
|
|
37
|
+
newProp.value = prevProp.value;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return newProps;
|
|
44
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { rendering } from "./rendering";
|
|
2
|
+
|
|
3
|
+
export let renderers = {};
|
|
4
|
+
|
|
5
|
+
function loadRenderer(renderingMode = "2d") {
|
|
6
|
+
if (__THREE_RENDERER__ && renderingMode === "three") {
|
|
7
|
+
return import("../renderers/THREERenderer.js");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (__FRAGMENT_RENDERER__ && renderingMode === "fragment") {
|
|
11
|
+
return import("../renderers/FragmentRenderer.js");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (__P5_RENDERER__ && renderingMode === "p5") {
|
|
15
|
+
return import("../renderers/P5Renderer.js");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (__2D_RENDERER__ && renderingMode === "2d") {
|
|
19
|
+
return import("../renderers/2DRenderer.js");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function findRenderer(renderingMode = "2d") {
|
|
24
|
+
if (renderers[renderingMode]) return renderers[renderingMode];
|
|
25
|
+
|
|
26
|
+
// load and save
|
|
27
|
+
renderers[renderingMode] = await loadRenderer(renderingMode);
|
|
28
|
+
|
|
29
|
+
// get
|
|
30
|
+
let renderer = renderers[renderingMode];
|
|
31
|
+
let initialized = false;
|
|
32
|
+
|
|
33
|
+
rendering.subscribe((current) => {
|
|
34
|
+
let r;
|
|
35
|
+
|
|
36
|
+
if (!initialized) {
|
|
37
|
+
if (typeof renderer.init === "function") {
|
|
38
|
+
r = renderer.init({
|
|
39
|
+
canvas: document.createElement('canvas'),
|
|
40
|
+
pixelRatio: current.pixelRatio,
|
|
41
|
+
width: current.width,
|
|
42
|
+
height: current.height,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
initialized = true;
|
|
48
|
+
|
|
49
|
+
if (typeof renderer.resize === "function") {
|
|
50
|
+
renderer.resize({
|
|
51
|
+
width: current.width,
|
|
52
|
+
height: current.height,
|
|
53
|
+
pixelRatio: current.pixelRatio,
|
|
54
|
+
...r,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return renderer;
|
|
60
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { client } from "../client";
|
|
3
|
+
import { createStore } from "./utils";
|
|
4
|
+
import { getDimensionsForPreset } from "../lib/presets";
|
|
5
|
+
|
|
6
|
+
export const SIZES = {
|
|
7
|
+
FIXED: "fixed",
|
|
8
|
+
PRESET: "preset",
|
|
9
|
+
ASPECT_RATIO: "aspect-ratio",
|
|
10
|
+
WINDOW: "window",
|
|
11
|
+
SCALE: "scale",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const rendering = createStore(`rendering`, {
|
|
15
|
+
width: 1024,
|
|
16
|
+
height: 1024,
|
|
17
|
+
pixelRatio: 1,
|
|
18
|
+
resizing: SIZES.FIXED,
|
|
19
|
+
aspectRatio: 1,
|
|
20
|
+
scale: 1,
|
|
21
|
+
preset: 'a4',
|
|
22
|
+
}, {
|
|
23
|
+
persist: !__PRODUCTION__,
|
|
24
|
+
reset: false,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const monitors = createStore('monitors', []);
|
|
28
|
+
export const preview = createStore('preview', null);
|
|
29
|
+
|
|
30
|
+
export const override = (config) => {
|
|
31
|
+
const { canvasSize = SIZES.WINDOW } = config;
|
|
32
|
+
const resizing = canvasSize;
|
|
33
|
+
|
|
34
|
+
const overrides = {
|
|
35
|
+
resizing,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (config.dimensions && config.dimensions.length === 2) {
|
|
39
|
+
const { dimensions } = config;
|
|
40
|
+
overrides.width = dimensions[0];
|
|
41
|
+
overrides.height = dimensions[1];
|
|
42
|
+
|
|
43
|
+
if (!config.canvasSize) {
|
|
44
|
+
overrides.resizing = SIZES.FIXED;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (resizing === SIZES.PRESET) {
|
|
49
|
+
if (config.preset) {
|
|
50
|
+
const [ width, height ] = getDimensionsForPreset(config.preset, { pixelsPerInch: 300 });
|
|
51
|
+
|
|
52
|
+
overrides.width = width;
|
|
53
|
+
overrides.height = height;
|
|
54
|
+
} else {
|
|
55
|
+
overrides.resizing = SIZES.WINDOW;
|
|
56
|
+
console.warn(`Cannot apply canvasSize preset if 'preset' is not specified in config.`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (resizing === SIZES.ASPECT_RATIO) {
|
|
61
|
+
if (isNaN(config.aspectRatio)) {
|
|
62
|
+
overrides.resizing = SIZES.WINDOW;
|
|
63
|
+
console.warn(`Cannot apply canvasSize:"aspectRatio" if 'aspectRatio' is not specified in config.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (resizing === SIZES.SCALE) {
|
|
68
|
+
if (!config.dimensions) {
|
|
69
|
+
console.warn(`Cannot apply canvasSize:"scale" if no dimensions are specified.`);
|
|
70
|
+
overrides.resizing = SIZES.WINDOW;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isNaN(config.scale)) {
|
|
74
|
+
console.warn(`Cannot apply canvasSize:"scale" if 'scale' is not specified in config.`);
|
|
75
|
+
overrides.resizing = SIZES.WINDOW;
|
|
76
|
+
} else {
|
|
77
|
+
overrides.scale = config.scale;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (config.pixelRatio) {
|
|
82
|
+
const { pixelRatio } = config;
|
|
83
|
+
overrides.pixelRatio = typeof pixelRatio === "function" ? pixelRatio() : pixelRatio;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
rendering.update((curr) => ({...curr, ...overrides}));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/* sync across clients */
|
|
90
|
+
let isSynchronized = false;
|
|
91
|
+
|
|
92
|
+
export const sync = writable(isSynchronized);
|
|
93
|
+
|
|
94
|
+
function checkForSync({ clientCount } = {}) {
|
|
95
|
+
let prev = isSynchronized;
|
|
96
|
+
isSynchronized = clientCount > 0;
|
|
97
|
+
|
|
98
|
+
if (prev && !isSynchronized) {
|
|
99
|
+
console.warn("[fragment] Sketch is running at specified framerate.");
|
|
100
|
+
} else if (!prev && isSynchronized) {
|
|
101
|
+
console.warn("[fragment] Multiple instances of Fragment detected. Running sketch(s) at simulated framerate.");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (prev !== isSynchronized) {
|
|
105
|
+
sync.set(isSynchronized);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
client.on('start', checkForSync);
|
|
110
|
+
client.on('client-connect', checkForSync);
|
|
111
|
+
client.on('client-disconnect', checkForSync);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createStore } from "./utils.js";
|
|
2
|
+
import { displayError } from "../stores/errors";
|
|
3
|
+
import { sketches as all, onSketchReload } from "@fragment/sketches";
|
|
4
|
+
|
|
5
|
+
export const sketches = createStore('sketches', {});
|
|
6
|
+
export const sketchesKeys = createStore('sketchesKeys', []);
|
|
7
|
+
export const sketchesCount = createStore('sketchesCount', 0);
|
|
8
|
+
|
|
9
|
+
async function loadSketch(collection, key) {
|
|
10
|
+
try {
|
|
11
|
+
let sketch = await collection[key]();
|
|
12
|
+
|
|
13
|
+
return sketch;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
displayError(error, key);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function loadAll(collection) {
|
|
20
|
+
const keys = [...Object.keys(collection)];
|
|
21
|
+
const loadedSketches = await Promise.all(keys.map((key) => loadSketch(collection, key)));
|
|
22
|
+
|
|
23
|
+
const newSketches = keys.reduce((all, key, index) => {
|
|
24
|
+
if (loadedSketches[index]) {
|
|
25
|
+
all[key] = loadedSketches[index];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return all;
|
|
29
|
+
}, {});
|
|
30
|
+
|
|
31
|
+
sketches.update(() => newSketches);
|
|
32
|
+
sketchesKeys.update(() => keys);
|
|
33
|
+
sketchesCount.update(() => keys.length);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
loadAll(all);
|
|
37
|
+
|
|
38
|
+
onSketchReload(({ sketches }) => {
|
|
39
|
+
loadAll(sketches);
|
|
40
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { readable } from "svelte/store";
|
|
2
|
+
|
|
3
|
+
export const current = readable({ time: 0, deltaTime: 0 }, set => {
|
|
4
|
+
let _raf;
|
|
5
|
+
let lastTime = performance.now();
|
|
6
|
+
let startTime = lastTime - __START_TIME__;
|
|
7
|
+
let time = startTime;
|
|
8
|
+
|
|
9
|
+
function update(dt = 0) {
|
|
10
|
+
lastTime = time;
|
|
11
|
+
|
|
12
|
+
let currentTime = performance.now();
|
|
13
|
+
let deltaTime = currentTime - lastTime;
|
|
14
|
+
|
|
15
|
+
time = currentTime;
|
|
16
|
+
|
|
17
|
+
set({ time, deltaTime, startTime });
|
|
18
|
+
|
|
19
|
+
_raf = requestAnimationFrame(update)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
update();
|
|
23
|
+
|
|
24
|
+
return () => {
|
|
25
|
+
cancelAnimationFrame(_raf);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
|
|
3
|
+
let stores = new Map();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns the value stored in localStorage for key or return defaultValue if it doesn't exist
|
|
7
|
+
* @param {string} key
|
|
8
|
+
* @param {any} defaultValue
|
|
9
|
+
* @param {boolean} override
|
|
10
|
+
* @returns {any} result
|
|
11
|
+
*/
|
|
12
|
+
export function rehydrate(key, defaultValue) {
|
|
13
|
+
const storedValue = localStorage.getItem(`fragment.${key}`);
|
|
14
|
+
|
|
15
|
+
if (storedValue) {
|
|
16
|
+
return typeof storedValue === "string" ? JSON.parse(storedValue) : storedValue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return defaultValue;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Save value in localStorage
|
|
24
|
+
* @param {string} key
|
|
25
|
+
* @param {any} value
|
|
26
|
+
*/
|
|
27
|
+
export function save(key, value) {
|
|
28
|
+
localStorage.setItem(`fragment.${key}`, JSON.stringify(value));
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create store and register it for later usage
|
|
33
|
+
* @param {string} key
|
|
34
|
+
* @param {any} initialValue
|
|
35
|
+
* @param {object} options
|
|
36
|
+
* @returns {object} store
|
|
37
|
+
*/
|
|
38
|
+
export function createStore(key, initialValue, { persist = false, reset = false } = {}) {
|
|
39
|
+
const value = (persist && !reset) ? rehydrate(key, initialValue) : initialValue;
|
|
40
|
+
const store = writable(value);
|
|
41
|
+
|
|
42
|
+
if (persist) {
|
|
43
|
+
store.subscribe((current) => {
|
|
44
|
+
save(key, current);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
stores.set(key, store);
|
|
49
|
+
|
|
50
|
+
return store;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get an existing store from key or create it if it doesn't exist yet
|
|
55
|
+
* @param {string} key
|
|
56
|
+
* @param {any} initialValue
|
|
57
|
+
* @param {object} options
|
|
58
|
+
* @returns {object} store
|
|
59
|
+
*/
|
|
60
|
+
export function getStore(key, initialValue, { persist = false, reset = false } = {}) {
|
|
61
|
+
if (!stores.has(key)) {
|
|
62
|
+
return createStore(key, initialValue, { persist, reset });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return stores.get(key);
|
|
66
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export let name = "Fade";
|
|
2
|
+
|
|
3
|
+
export let props = {};
|
|
4
|
+
|
|
5
|
+
export let fragmentShader = /* glsl */`
|
|
6
|
+
precision highp float;
|
|
7
|
+
|
|
8
|
+
uniform sampler2D uSampler0;
|
|
9
|
+
uniform sampler2D uSampler1;
|
|
10
|
+
uniform float threshold;
|
|
11
|
+
|
|
12
|
+
varying vec2 vUv;
|
|
13
|
+
|
|
14
|
+
void main() {
|
|
15
|
+
gl_FragColor = mix(texture2D(uSampler0, vUv), texture2D(uSampler1, vUv), threshold);
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
// This file is generated by Fragment. Do not edit it.
|
|
3
|
+
|
|
4
|
+
import * as transition0 from "./fade.js";
|
|
5
|
+
import * as transition1 from "./splitX.js";
|
|
6
|
+
import * as transition2 from "./splitY.js";
|
|
7
|
+
|
|
8
|
+
export const transitions = {
|
|
9
|
+
"fade.js": transition0,
|
|
10
|
+
"splitX.js": transition1,
|
|
11
|
+
"splitY.js": transition2,
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export let name = "SplitX";
|
|
2
|
+
export let props = {};
|
|
3
|
+
|
|
4
|
+
export let fragmentShader = /* glsl */`
|
|
5
|
+
precision highp float;
|
|
6
|
+
|
|
7
|
+
uniform float threshold;
|
|
8
|
+
uniform sampler2D uSampler0;
|
|
9
|
+
uniform sampler2D uSampler1;
|
|
10
|
+
|
|
11
|
+
varying vec2 vUv;
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
gl_FragColor = mix(texture2D(uSampler0, vUv), texture2D(uSampler1, vUv), step(1. - vUv.x, threshold));
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export let name = "SplitY";
|
|
2
|
+
export let props = {};
|
|
3
|
+
|
|
4
|
+
export let fragmentShader = /* glsl */`
|
|
5
|
+
precision highp float;
|
|
6
|
+
|
|
7
|
+
uniform float threshold;
|
|
8
|
+
uniform sampler2D uSampler0;
|
|
9
|
+
uniform sampler2D uSampler1;
|
|
10
|
+
|
|
11
|
+
varying vec2 vUv;
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
gl_FragColor = mix(texture2D(uSampler0, vUv), texture2D(uSampler1, vUv), step(1. - vUv.y, threshold));
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Trigger from "./Trigger";
|
|
2
|
+
import Keyboard from "../inputs/Keyboard";
|
|
3
|
+
import { wildcard, getContext } from "./shared";
|
|
4
|
+
import { addToMapArray, removeFromMapArray } from "../utils";
|
|
5
|
+
|
|
6
|
+
const pressedKeys = new Map();
|
|
7
|
+
const upKeys = new Map();
|
|
8
|
+
const downKeys = new Map();
|
|
9
|
+
|
|
10
|
+
export const removeHotListeners = (context) => {
|
|
11
|
+
function removeHotFrom(collection) {
|
|
12
|
+
for (let trigger of collection) {
|
|
13
|
+
const [key, triggers] = trigger;
|
|
14
|
+
|
|
15
|
+
collection.set(key, triggers.filter((trigger) => trigger.context !== context));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
removeHotFrom(pressedKeys);
|
|
20
|
+
removeHotFrom(upKeys);
|
|
21
|
+
removeHotFrom(downKeys);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function createEventListener(collection) {
|
|
25
|
+
return (event) => {
|
|
26
|
+
const { key, target } = event;
|
|
27
|
+
|
|
28
|
+
if (!target.classList.contains('input')) {
|
|
29
|
+
const triggers = [
|
|
30
|
+
...(collection.has(key) ? collection.get(key) : []),
|
|
31
|
+
...(collection.has(wildcard) ? collection.get(wildcard) : []),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
triggers.forEach(trigger => {
|
|
35
|
+
if (!Keyboard.enabled) return;
|
|
36
|
+
|
|
37
|
+
trigger.run(event);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createTrigger(eventName, collection) {
|
|
44
|
+
return (key, fn, options = {}) => {
|
|
45
|
+
if (typeof key === "function") {
|
|
46
|
+
if (typeof fn === "object") {
|
|
47
|
+
options = {
|
|
48
|
+
...options,
|
|
49
|
+
...fn,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn = key;
|
|
54
|
+
key = "*";
|
|
55
|
+
|
|
56
|
+
if (options.key) {
|
|
57
|
+
key = options.key;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { hot, enabled, ...params } = options;
|
|
62
|
+
const context = getContext();
|
|
63
|
+
|
|
64
|
+
const keys = Array.isArray(key) ? key : [key];
|
|
65
|
+
|
|
66
|
+
const trigger = new Trigger({
|
|
67
|
+
inputType: 'Keyboard',
|
|
68
|
+
eventName,
|
|
69
|
+
fn,
|
|
70
|
+
context,
|
|
71
|
+
params: {...params, key: keys },
|
|
72
|
+
hot,
|
|
73
|
+
enabled,
|
|
74
|
+
destroy: () => {
|
|
75
|
+
keys.forEach((key) => {
|
|
76
|
+
removeFromMapArray(collection, key, (item) => item.id === trigger.id);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
keys.forEach(key => {
|
|
82
|
+
addToMapArray(collection, key, trigger);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return trigger;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
window.addEventListener("keypress", createEventListener(pressedKeys));
|
|
90
|
+
window.addEventListener("keyup", createEventListener(upKeys));
|
|
91
|
+
window.addEventListener("keydown", createEventListener(downKeys));
|
|
92
|
+
|
|
93
|
+
export const onKeyPress = createTrigger('onKeyPress', pressedKeys);
|
|
94
|
+
export const onKeyDown = createTrigger('onKeyDown', downKeys);
|
|
95
|
+
export const onKeyUp = createTrigger('onKeyUp', upKeys);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import Trigger from "./Trigger";
|
|
2
|
+
import MIDI from "../inputs/MIDI.js";
|
|
3
|
+
import { addToMapArray, removeFromMapArray } from "../utils";
|
|
4
|
+
import { wildcard, getContext } from "./shared";
|
|
5
|
+
|
|
6
|
+
const noteons = new Map();
|
|
7
|
+
const noteoffs = new Map();
|
|
8
|
+
const numberons = new Map();
|
|
9
|
+
const numberoffs = new Map();
|
|
10
|
+
const controlchanges = new Map();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Remove listeners from a specific context
|
|
14
|
+
* @param {string} context
|
|
15
|
+
*/
|
|
16
|
+
export const removeHotListeners = (context) => {
|
|
17
|
+
function removeHotFrom(collection) {
|
|
18
|
+
for (let trigger of collection) {
|
|
19
|
+
const [key, triggers] = trigger;
|
|
20
|
+
|
|
21
|
+
collection.set(key, triggers.filter((trigger) => trigger.context !== context));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
removeHotFrom(noteons);
|
|
26
|
+
removeHotFrom(noteoffs);
|
|
27
|
+
removeHotFrom(numberons);
|
|
28
|
+
removeHotFrom(numberoffs);
|
|
29
|
+
removeHotFrom(controlchanges);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check all registered listeners for a specific key
|
|
34
|
+
* @param {Map} collection
|
|
35
|
+
* @param {function} getKey
|
|
36
|
+
* @returns {function} listener
|
|
37
|
+
*/
|
|
38
|
+
function createEventListener(collection, getKey = (event) => event.key) {
|
|
39
|
+
return (event) => {
|
|
40
|
+
const key = getKey(event);
|
|
41
|
+
const { id } = event.srcElement;
|
|
42
|
+
|
|
43
|
+
const triggers = [
|
|
44
|
+
...(collection.has(key) ? collection.get(key) : []),
|
|
45
|
+
...(collection.has(wildcard) ? collection.get(wildcard) : []),
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
if (MIDI.enabled && id === MIDI.selectedInputID) {
|
|
49
|
+
triggers.forEach(trigger => {
|
|
50
|
+
trigger.run(event);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a registering function for a specific event
|
|
58
|
+
* @param {string} eventName
|
|
59
|
+
* @param {Map} collection
|
|
60
|
+
* @returns {function} createListener
|
|
61
|
+
*/
|
|
62
|
+
function createTrigger(eventName, collection) {
|
|
63
|
+
return (key, fn, options = {}) => {
|
|
64
|
+
if (typeof key === "function") {
|
|
65
|
+
if (typeof fn === "object") {
|
|
66
|
+
options = {
|
|
67
|
+
...options,
|
|
68
|
+
...fn,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn = key;
|
|
73
|
+
key = "*";
|
|
74
|
+
|
|
75
|
+
if (options.key) {
|
|
76
|
+
key = options.key;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const { hot, enabled, ...params } = options;
|
|
81
|
+
const keys = Array.isArray(key) ? key : [key];
|
|
82
|
+
|
|
83
|
+
if (!MIDI.enabled) {
|
|
84
|
+
MIDI.request();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const context = getContext();
|
|
88
|
+
|
|
89
|
+
const trigger = new Trigger({
|
|
90
|
+
inputType: 'MIDI',
|
|
91
|
+
eventName,
|
|
92
|
+
fn,
|
|
93
|
+
params: {...params, key: keys },
|
|
94
|
+
hot,
|
|
95
|
+
context,
|
|
96
|
+
enabled,
|
|
97
|
+
destroy : () => {
|
|
98
|
+
keys.forEach((key) => {
|
|
99
|
+
removeFromMapArray(collection, key, (item) => item.id === trigger.id);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
keys.forEach(k => {
|
|
105
|
+
addToMapArray(collection, k, trigger);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return trigger;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
MIDI.addEventListener("noteon", createEventListener(noteons, (event) => event.note.name));
|
|
113
|
+
MIDI.addEventListener("noteoff", createEventListener(noteoffs, (event) => event.note.name));
|
|
114
|
+
MIDI.addEventListener("noteon", createEventListener(numberons, (event) => event.note.number));
|
|
115
|
+
MIDI.addEventListener("noteoff", createEventListener(numberoffs, (event) => event.note.number));
|
|
116
|
+
MIDI.addEventListener("controlchange", createEventListener(controlchanges, (event) => event.note.number));
|
|
117
|
+
|
|
118
|
+
export const onNoteOn = createTrigger('onNoteOn', noteons);
|
|
119
|
+
export const onNoteOff = createTrigger('onNoteOff', noteoffs);
|
|
120
|
+
export const onNumberOn = createTrigger('onNumberOn', numberons);
|
|
121
|
+
export const onNumberOff = createTrigger('onNumberOff', numberoffs);
|
|
122
|
+
export const onControlChange = createTrigger('onControlChange', controlchanges);
|