Published
Edited
Jul 23, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
html`<div class="world">
${currentBalls.map(
(b) =>
html`<div class="ball" style="left: ${b.x}px; bottom: ${b.y}px; background-color: ${b.color}" ></div>`
)}
</div>`
Insert cell
Insert cell
convex.mutation("golf:createBall")(identifier, color) // start by creating a new ball for this client
Insert cell
remoteBalls = queryGenerator(convex, "golf:getBalls")
Insert cell
function sendThatBall(angleInDegrees, mightiness) {
if (angleInDegrees < -90 || angleInDegrees > 90) {
throw new Error(
"Hey it's in degrees, 90 means straigt left, 0 means straight up"
);
}
if (mightiness < 0 || mightiness > 20) {
throw new Error("You can't hit the ball that hard!");
}

publish(identifier, angleInDegrees, mightiness);
}
Insert cell
convert = (deg) => [
Math.sin((deg * 2 * Math.PI) / 360),
Math.cos((deg * 2 * Math.PI) / 360)
]
Insert cell
Insert cell
currentBalls = {
while (true) {
yield remoteBalls.map(currentPosition);
await new Promise((r) => requestAnimationFrame(r));
//await new Promise((r) => setTimeout(r, 100));
}
}
Insert cell
function currentPosition(ball) {
const now = Date.now();
if (ball.ts > now) {
console.log("in the future, don't touch it!");
return ball;
}

let newBall = ball;
let i = 0;
// infinite loops are annoying
console.log("initial:", ball);
while (i++ < 1000) {
newBall = step(newBall, 10);
//console.log(newBall.y);

// out of bounds, return to start
if (newBall.x < xMin || newBall.x > xMax) {
console.log("out of bounds in x!");
return ball;
}

if (
newBall.y < 1 &&
Math.abs(newBall.dy * newBall.dy + newBall.dx * newBall.dx) < 1
) {
//console.log("stuck on ground after", i, "steps");
return newBall;
}

if (newBall.ts > now) {
console.log("we have reached the present after", i, "steps");
return newBall;
}
}
console.log("ran out of simulation steps");
return newBall;
}
Insert cell
function step(ball, dt) {
let { x, y, dx, dy, ts } = ball;

if (x < xMin || x > xMax) {
console.log("out of bounds:", ball);
// todo when out of bounds, show at start
ball.x = Math.min(xMax, Math.max(xMin, x));
return ball;
}

ts = ts + dt;
dx = 0.99 * dx;
dy = 0.99 * dy - 0.05;
x = x + dx;
y = y + dy;

// bounce
if (y < yMin) {
y = yMin + (yMin - y);
dy = -dy * 0.7;
}

return { ...ball, x, y, dx, dy, ts };
}
Insert cell
xMin = 0
Insert cell
xMax = 1000
Insert cell
yMin = 0
Insert cell
yMax = 300
Insert cell
yMax
Insert cell
function curStroke(strokes) {
let cur;
const now = Date.now();
for (const stroke of strokes) {
if (stroke.ts > now) break;
cur = stroke;
}
return cur;
}
Insert cell
c = import("https://esm.run/convex-dev/react")
Insert cell
origin = "https://guiltless-dog-960.convex.cloud"
Insert cell
convex = new c.ConvexReactClient(origin)
Insert cell
identifier = 'user' + Math.random()
Insert cell
color = `hsl(${Math.floor(Math.random() * 360)}, 100%, 50%)`
Insert cell
publish = convex.mutation("golf:publishStroke")
Insert cell
queryGenerator = (client, query, ...args) => {
const watch = client.watchQuery(query, ...args);
return Generators.observe((notify) =>
watch.onUpdate(() => notify(watch.localQueryResult()))
);
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more