diff --git a/media/src/form-components/PlayButtons.svelte b/media/src/form-components/PlayButtons.svelte
index 13845593..aab32d82 100644
--- a/media/src/form-components/PlayButtons.svelte
+++ b/media/src/form-components/PlayButtons.svelte
@@ -5,6 +5,8 @@
play = () => {},
pause,
rew,
+ clicker = () => {},
+ playMode = 'loop',
// animate,
} = $props();
@@ -32,6 +34,15 @@
+
diff --git a/media/src/objects/Curve.svelte b/media/src/objects/Curve.svelte
index 914c341f..7c79718a 100644
--- a/media/src/objects/Curve.svelte
+++ b/media/src/objects/Curve.svelte
@@ -44,6 +44,7 @@
camera,
gridStep,
selected = $bindable(false),
+ playMode = 'loop',
} = $props();
title = title || `Curve ${++titleIndex}`;
@@ -163,15 +164,42 @@
let last;
+ function playModeCycle() {
+ if (playMode == 'once') {
+ playMode = 'loop';
+ } else if (playMode == 'loop') {
+ playMode = 'bounce';
+ } else {
+ playMode = 'once';
+ }
+ }
+
+ let modeSign = 1;
+
const update = (dt = 0) => {
- const { a, b } = params;
+ // const { a, b } = params;
if (vizOptions.TNB) {
- tau += dt / arrows.v.speed / (B - A);
+ tau += (modeSign * dt) / arrows.v.speed / (B - A);
} else {
- tau += dt / (B - A);
+ tau += (modeSign * dt) / (B - A);
}
- tau %= 1;
+ if (tau > 1) {
+ if (playMode == 'loop') {
+ tau %= 1;
+ } else if (playMode == 'once') {
+ tau = 1;
+ animation = false;
+ } else {
+ modeSign = -1;
+ tau = 2 - tau;
+ }
+ }
+ if (tau <= 0) {
+ modeSign = 1;
+ tau *= -1;
+ }
+
// const T = A + (B - A) * tau;
// uncomment this if we want aVal to be animated:
@@ -663,6 +691,8 @@
animation = false;
update();
}}
+ {playMode}
+ clicker={playModeCycle}
/>
{#if isDynamic}
diff --git a/media/src/objects/Function.svelte b/media/src/objects/Function.svelte
index f201443c..5411f6e9 100644
--- a/media/src/objects/Function.svelte
+++ b/media/src/objects/Function.svelte
@@ -67,6 +67,7 @@
animate,
camera,
onClose = () => {},
+ playMode = 'loop',
} = $props();
// $inspect(camera);
@@ -577,6 +578,16 @@
*/
let isDynamic = $derived(dependsOn(params, 't'));
+ function playModeCycle() {
+ if (playMode == 'once') {
+ playMode = 'loop';
+ } else if (playMode == 'loop') {
+ playMode = 'bounce';
+ } else {
+ playMode = 'once';
+ }
+ }
+
$effect(() => {
[params.z, params.a, params.b, params.c, params.d];
untrack(updateSurface);
@@ -607,9 +618,25 @@
if (selected) untrack(flash);
});
+ let modeSign = 1;
+
const update = function (dt) {
- tau += dt / (t1 - t0);
- tau %= 1;
+ tau += (modeSign * dt) / (t1 - t0);
+ if (tau > 1) {
+ if (playMode == 'loop') {
+ tau %= 1;
+ } else if (playMode == 'once') {
+ tau = 1;
+ animation = false;
+ } else {
+ modeSign = -1;
+ tau = 2 - tau;
+ }
+ }
+ if (tau <= 0) {
+ modeSign = 1;
+ tau *= -1;
+ }
evolveSurface(tVal);
@@ -799,26 +826,25 @@
boxMesh.add(boxMeshEdges);
const updateBoxes = function () {
- const { a, b, c, d} = params;
+ const { a, b, c, d } = params;
try {
- [
+ [
math.evaluate(a),
math.evaluate(b),
math.evaluate(c),
math.evaluate(d),
];
} catch (e) {
- console.error("Can't show integral boxes on nonconstant bounds",e);
+ console.error("Can't show integral boxes on nonconstant bounds", e);
return;
}
const [A, B, C, D] = [
- math.evaluate(a),
- math.evaluate(b),
- math.evaluate(c),
- math.evaluate(d),
- ];
-
+ math.evaluate(a),
+ math.evaluate(b),
+ math.evaluate(c),
+ math.evaluate(d),
+ ];
// const t = T0 + tau * (T1 - T0);
@@ -1181,6 +1207,8 @@
evolveSurface(tVal);
render();
}}
+ {playMode}
+ clicker={playModeCycle}
/>
{/if}
diff --git a/media/src/objects/Point.svelte b/media/src/objects/Point.svelte
index d68a187e..5d81d0db 100644
--- a/media/src/objects/Point.svelte
+++ b/media/src/objects/Point.svelte
@@ -45,6 +45,7 @@
selectObject,
animate,
onClose = () => {},
+ playMode = 'loop',
} = $props();
let tau = $state(0);
@@ -106,6 +107,16 @@
let isDynamic = $derived(dependsOn(params, 't'));
let isDiscrete = $derived(dependsOn(params, 'n'));
+ function playModeCycle() {
+ if (playMode == 'once') {
+ playMode = 'loop';
+ } else if (playMode == 'loop') {
+ playMode = 'bounce';
+ } else {
+ playMode = 'once';
+ }
+ }
+
$effect(() => {
params;
updatePoint();
@@ -213,9 +224,25 @@
// texString1 = `t = ${Math.round(100 * T) / 100}`;
+ let modeSign = 1;
+
const update = (dt = 0) => {
- tau += dt / (t1 - t0);
- tau %= 1;
+ tau += (modeSign * dt) / (t1 - t0);
+ if (tau > 1) {
+ if (playMode == 'loop') {
+ tau %= 1;
+ } else if (playMode == 'once') {
+ tau = 1;
+ animation = false;
+ } else {
+ modeSign = -1;
+ tau = 2 - tau;
+ }
+ }
+ if (tau <= 0) {
+ modeSign = 1;
+ tau *= -1;
+ }
updatePoint(tVal);
};
// Start animating if animation changes (e.g. animating scene published)
@@ -308,6 +335,8 @@
tau = 0;
update();
}}
+ {playMode}
+ clicker={playModeCycle}
/>
{/if}
diff --git a/media/src/objects/Surface.svelte b/media/src/objects/Surface.svelte
index 56f52132..abd3f928 100644
--- a/media/src/objects/Surface.svelte
+++ b/media/src/objects/Surface.svelte
@@ -68,6 +68,7 @@
selectObject,
selectPoint,
animate = () => {},
+ playMode = 'loop',
} = $props();
let rNum = 10;
@@ -588,9 +589,36 @@
}
};
+ let modeSign = 1;
+
+ function playModeCycle() {
+ if (playMode == 'once') {
+ playMode = 'loop';
+ } else if (playMode == 'loop') {
+ playMode = 'bounce';
+ } else {
+ playMode = 'once';
+ }
+ }
+
const update = function (dt = 0) {
- tau += dt / (t1 - t0);
- tau %= 1;
+ tau += (modeSign * dt) / (t1 - t0);
+
+ if (tau > 1) {
+ if (playMode == 'loop') {
+ tau %= 1;
+ } else if (playMode == 'once') {
+ tau = 1;
+ animation = false;
+ } else {
+ modeSign = -1;
+ tau = 2 - tau;
+ }
+ }
+ if (tau <= 0) {
+ modeSign = 1;
+ tau *= -1;
+ }
if (isDynamic) evolveSurface(tVal);
if (isRhoDynamic) {
@@ -1062,6 +1090,8 @@
tau = 0;
update();
}}
+ {playMode}
+ clicker={playModeCycle}
/>
{/if}
diff --git a/media/src/objects/Vector.svelte b/media/src/objects/Vector.svelte
index 5b38c29b..15787d87 100644
--- a/media/src/objects/Vector.svelte
+++ b/media/src/objects/Vector.svelte
@@ -59,6 +59,7 @@
render,
onClose,
gridStep,
+ playMode = 'loop',
} = $props();
let minimize = $state(false);
@@ -143,6 +144,16 @@
let isDynamic = $derived(dependsOn(params, 't'));
let isDiscrete = $derived(dependsOn(params, 'n'));
+ function playModeCycle() {
+ if (playMode == 'once') {
+ playMode = 'loop';
+ } else if (playMode == 'loop') {
+ playMode = 'bounce';
+ } else {
+ playMode = 'once';
+ }
+ }
+
$effect(updateVector);
// recolor on demand
@@ -250,13 +261,25 @@
return valuation;
};
- const update = (dt = 0) => {
- const { t0, t1 } = params;
- const A = math.parse(t0).evaluate();
- const B = math.parse(t1).evaluate();
+ let modeSign = 1;
- tau += dt / (B - A);
- tau %= 1;
+ const update = (dt = 0) => {
+ tau += (modeSign * dt) / (t1 - t0);
+ if (tau > 1) {
+ if (playMode == 'loop') {
+ tau %= 1;
+ } else if (playMode == 'once') {
+ tau = 1;
+ animation = false;
+ } else {
+ modeSign = -1;
+ tau = 2 - tau;
+ }
+ }
+ if (tau <= 0) {
+ modeSign = 1;
+ tau *= -1;
+ }
updateVector(tVal);
};
@@ -363,6 +386,8 @@
tau = 0;
update();
}}
+ {playMode}
+ clicker={playModeCycle}
/>
{/if}
diff --git a/media/src/stories/FluxIntegral.svelte b/media/src/stories/FluxIntegral.svelte
index e93b29fa..e496573a 100644
--- a/media/src/stories/FluxIntegral.svelte
+++ b/media/src/stories/FluxIntegral.svelte
@@ -46,7 +46,7 @@
// const currentTime = $tickTock;
last = last || $tickTock;
tau += $tickTock - last;
- tau %= 1;
+ if (tau > 1) tau %= 1;
updateTau(tau);
last = $tickTock;
}