Skip to content

Commit

Permalink
Update files
Browse files Browse the repository at this point in the history
  • Loading branch information
leikareipa committed Oct 19, 2024
1 parent 4c6a41e commit 997b42f
Show file tree
Hide file tree
Showing 5 changed files with 420 additions and 0 deletions.
153 changes: 153 additions & 0 deletions desktop/apps/xmas94/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* 2024 Tarpeeksi Hyvae Soft
*
*/

import {icons} from "./icons.js";
import background from "./bg.texture.js";
import * as fluid from "./fluid.js";

const fluidDomainSize = 150;

export default {
Meta: {
name: "Xmas '94",
version: "1.0",
author: "Tarpeeksi Hyvae Soft",
},
App() {
const width = w95.state(150);
const height = w95.state(150);
const x = w95.state(
~~(0.5 * (w95.shell.display.width - width.now)),
w95.reRenderOnly
);
const y = w95.state(
~~(0.5 * (w95.shell.display.visibleHeight - height.now)),
w95.reRenderOnly
);

const redraw = w95.state(0);
const flakes = w95.state(new Array(100).fill().map(f=>({
x: Math.max(1, Math.min((width.now - 10), ~~(Math.random() * width.now))),
y: ~~(Math.random() * (height.now / 2)),
color: w95.color(200 + ~~(55 * Math.random())),
})));

return {
get x() { return x.now },
get y() { return y.now },
get width() { return width.now },
get height() { return height.now },
Opened() {
setInterval(()=>{
flakes.now.push({
x: Math.max(1, Math.min((width.now - 10), ~~(Math.random() * width.now))),
y: 0,
color: w95.color(200 + ~~(55 * Math.random())),
});
}, 50);
},
Mounted() {
redraw.set(redraw.now+1);

if (fluid.is_initialized()) {
fluid.enforce_boundaries();

skip_flake_position_update:
for (const flake of flakes.now) {
const xm = (fluidDomainSize / width.now);
const ym = (fluidDomainSize / height.now);
const vel = fluid.velocity_at(~~(flake.x*xm), ~~(flake.y*ym));
const gravity = Math.max(0.3, Math.random()/((height.now / flake.y) / 2))/4;
const ru = (-Math.random() + Math.random());
const rv = (-Math.random() + Math.random());
const newX = Math.min((width.now - 10), Math.max(1, flake.x + ru + (vel.u * (1 + Math.random() / 2))));
const newY = Math.min((height.now - 30), Math.max(1, flake.y + rv + gravity + (vel.v * (1 + Math.random() / 2)))) + Math.random();

if (background.pixels[((~~newX + (background.height - 1 - ~~newY) * background.width) * 4) + 3]) {
continue skip_flake_position_update;
}

for (const flake of flakes.now) {
if (~~flake.x === ~~newX && ~~flake.y === ~~newY) {
continue skip_flake_position_update;
}
}

flake.x = newX;
flake.y = newY;
}

fluid.tick();
}
else {
fluid.initialize({
domainSize: fluidDomainSize,
vorticityStrength: 1,
});
}
},
Form() {
return w95.widget.window({
parent: this,
title: this.$app.Meta.name,
icon: icons.app16,
move(deltaX, deltaY) {
x.set(x.now + deltaX);
y.set(y.now + deltaY);
for (let y = 1; y < fluidDomainSize - 1; y++) {
for (let x = 1; x < fluidDomainSize - 1; x++) {
if (Math.random() < 0.25) {
fluid.apply_force({x, y, dU: deltaX*Math.random()*2.25, dV: deltaY*Math.random()*2.25});
}
}
}
},
close() {
w95.windowManager.release_window(this)
},
children: [
w95.widget.frame({
y: 1,
width: "pw",
height: "ph",
shape: w95.frameShape.box,
children: [
w95.widget.renderSurface({
x: 1,
y: 1,
width: "pw - 2",
height: "ph - 2",
backgroundColor: w95.color(0, 0, 0,155),
meshes: [
Rngon.mesh([
Rngon.ngon([
Rngon.vertex(0, 0),
Rngon.vertex(142, 0),
Rngon.vertex(142, 121),
Rngon.vertex(0, 121),
], {
texture: background,
allowAlphaReject: true,
isInScreenSpace: true,
})
]),
Rngon.mesh(flakes.now.map(f=>(
Rngon.ngon([
Rngon.vertex(~~f.x, ~~f.y)
], {
color: f.color,
isInScreenSpace: true,
})
))),
],
}),
],
}),
],
});
},
};
},
};
7 changes: 7 additions & 0 deletions desktop/apps/xmas94/bg.texture.js

Large diffs are not rendered by default.

227 changes: 227 additions & 0 deletions desktop/apps/xmas94/fluid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* 2022 Tarpeeksi Hyvae Soft
*
* Adapted from Stam: Real-time fluid dynamics for games
* (http://graphics.cs.cmu.edu/nsp/course/15-464/Fall09/papers/StamFluidforGames.pdf).
*
*/

"use strict";

const TICK_RATE = 0.05;
const NUM_PROJECT_LOOPS = 15;
let IS_INITIALIZED = false,
DOMAIN_SIZE = 0,
VORTICITY_STRENGTH = 0,
VEL_GRID_SIZE = 0,
VEL_U = [],
VEL_V = [],
U0 = [],
V0 = [];

function lerp(x, y, interval)
{
return (x + (interval * (y - x)));
}

export function is_initialized()
{
return IS_INITIALIZED;
}

export function velocity_at(x = 0, y = 0)
{
console.assert(IS_INITIALIZED);

const idx = vel_idx(x, y);

return {
u: VEL_U[idx],
v: VEL_V[idx],
};
}

export function enforce_boundaries()
{
for (let i = 0; i < DOMAIN_SIZE; i++)
{
// Top.
VEL_V[vel_idx(i, 0)] = 0;
VEL_V[vel_idx(i, -1)] = 0;

// Bottom.
VEL_V[vel_idx(i, (DOMAIN_SIZE - 0))] = 0;
VEL_V[vel_idx(i, (DOMAIN_SIZE - 1))] = 0;

// Right wall.
VEL_U[vel_idx((DOMAIN_SIZE - 0), i)] = 0;
VEL_U[vel_idx((DOMAIN_SIZE - 1), i)] = 0;

// Left wall.
VEL_U[vel_idx(0, i)] = 0;
VEL_U[vel_idx(-1, i)] = 0;
}
}

export function initialize({domainSize, vorticityStrength})
{
console.assert(!IS_INITIALIZED);

DOMAIN_SIZE = domainSize;
VORTICITY_STRENGTH = vorticityStrength;
VEL_GRID_SIZE = ((DOMAIN_SIZE + 2) ** 2);
VEL_U = new Array(VEL_GRID_SIZE).fill(0);
VEL_V = new Array(VEL_GRID_SIZE).fill(0);
U0 = new Array(VEL_GRID_SIZE).fill(0);
V0 = new Array(VEL_GRID_SIZE).fill(0);
IS_INITIALIZED = true;

return;
}

export function set_force({x = 0, y = 0, dU = 0, dV = 0} = {})
{
console.assert(IS_INITIALIZED);

const idx = vel_idx(x, y);
VEL_U[idx] = dU;
VEL_V[idx] = dV;

return;
}

export function apply_force({x = 0, y = 0, dU = 0, dV = 0} = {})
{
console.assert(IS_INITIALIZED);

const idx = vel_idx(x, y);

VEL_U[idx] = (dU != 0)? lerp(dU, VEL_U[idx], 0.85) : VEL_U[idx];
VEL_V[idx] = (dV != 0)? lerp(dV, VEL_V[idx], 0.85) : VEL_V[idx];

return;
}

export function tick()
{
console.assert(IS_INITIALIZED);

vorticify();
project(VEL_U, VEL_V, U0, V0);
[U0, VEL_U] = [VEL_U, U0];
[V0, VEL_V] = [VEL_V, V0];
advect(VEL_U, U0, U0, V0, TICK_RATE);
advect(VEL_V, V0, U0, V0, TICK_RATE);
project(VEL_U, VEL_V, U0, V0);

return;
}

function vel_idx(x, y)
{
return Math.floor(x + y * (DOMAIN_SIZE + 2));
}

function vorticify()
{
for (let i = 1; i <= DOMAIN_SIZE; ++i)
{
for (let j = 1; j <= DOMAIN_SIZE; ++j)
{
const uy = (VEL_U[vel_idx(i,j+1)] - VEL_U[vel_idx(i,j-1)]) * 0.5;
const vx = (VEL_V[vel_idx(i+1,j)] - VEL_V[vel_idx(i-1,j)]) * 0.5;

U0[vel_idx(i, j)] = Math.abs(uy-vx);
}
}

for (let i = 2; i < DOMAIN_SIZE; ++i)
{
for (let j = 2; j < DOMAIN_SIZE; ++j)
{
let wx = (U0[vel_idx(i+1,j)] - U0[vel_idx(i-1,j)]) * 0.5;
let wy = (U0[vel_idx(i,j+1)] - U0[vel_idx(i,j-1)]) * 0.5;
const len = (1 / (Math.sqrt(wx**2 + wy**2) + 0.000001));

wx *= len;
wy *= len;

const uy = (VEL_U[vel_idx(i,j+1)] - VEL_U[vel_idx(i,j-1)]) * 0.5;
const vx = (VEL_V[vel_idx(i+1,j)] - VEL_V[vel_idx(i-1,j)]) * 0.5;
const t = (uy - vx);

VEL_U[vel_idx(i,j)] += (wy * -t * VORTICITY_STRENGTH);
VEL_V[vel_idx(i,j)] += (wx * t * VORTICITY_STRENGTH);
}
}

return;
}

function project(u, v, p, div)
{
const h = 1.0 / DOMAIN_SIZE;

for (let i = 1; i <= DOMAIN_SIZE; i++)
{
for (let j = 1; j <= DOMAIN_SIZE; j++)
{
div[vel_idx(i, j)] =
-0.5 * h
* (u[vel_idx(i+1,j)] - u[vel_idx(i-1,j)] + v[vel_idx(i,j+1)] - v[vel_idx(i,j-1)]);
p[vel_idx(i,j)] = 0;
}
}

for (let k = 0; k < NUM_PROJECT_LOOPS; k++)
{
for (let i = 1; i <= DOMAIN_SIZE; i++)
{
for (let j = 1; j <= DOMAIN_SIZE; j++)
{
p[vel_idx(i, j)] =
(div[vel_idx(i,j)] + p[vel_idx(i-1,j)]
+ p[vel_idx(i+1,j)] + p[vel_idx(i,j-1)] + p[vel_idx(i,j+1)]) / 4;
}
}
}

for (let i = 1; i <= DOMAIN_SIZE; i++)
{
for (let j = 1; j <= DOMAIN_SIZE; j++)
{
u[vel_idx(i,j)] -= 0.5 * (p[vel_idx(i+1,j)] - p[vel_idx(i-1,j)]) / h;
v[vel_idx(i,j)] -= 0.5 * (p[vel_idx(i,j+1)] - p[vel_idx(i,j-1)]) / h;
}
}

return;
}

function advect(d, d0, u, v, dt)
{
const dt0 = dt * DOMAIN_SIZE;

for (let i = 1; i <= DOMAIN_SIZE; i++)
{
for (let j = 1; j <= DOMAIN_SIZE; j++)
{
const x = Math.max(0.5, Math.min((DOMAIN_SIZE + 0.5), i - dt0 * u[vel_idx(i,j)]));
const y = Math.max(0.5, Math.min((DOMAIN_SIZE + 0.5), j - dt0 * v[vel_idx(i,j)]));
const i0 = Math.floor(x);
const i1 = i0 + 1;
const j0 = Math.floor(y);
const j1 = j0 + 1;
const s1 = x - i0;
const s0 = 1 - s1;
const t1 = y - j0;
const t0 = 1 - t1;

d[vel_idx(i,j)] =
s0 * (t0 * d0[vel_idx(i0,j0)] + t1 * d0[vel_idx(i0,j1)])
+ s1 * (t0 * d0[vel_idx(i1,j0)] + t1 * d0[vel_idx(i1,j1)]);
}
}

return;
}
Loading

0 comments on commit 997b42f

Please sign in to comment.