Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

waveform spawning + security #11

Merged
merged 2 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [ main ]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ryanhill1.github.io

Welcome to my personal website project. For local development, follow the instructions below.
Welcome to my personal website project. To run locally, follow the instructions below.

## Prerequisites

Expand Down
109 changes: 84 additions & 25 deletions components/waveforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,23 @@ class WaveFunction {
this.growthTargetRadius = 0;
this.growthStartTime = 0;
this.growthDuration = 500;
this.spawnProgress = 0;
this.spawnDuration = 500;
this.spawnStartTime = Date.now();
}

setPosition() {
let position = null;
const initialRadius = this.radius;
for (let i = 1; i <= 5; i++) {
position = WaveFunction.findValidPosition(this.radius);
position = this.findValidPosition();
if (position) break;
this.radius = this.radius / (i + 1);
this.radius = initialRadius / (i + 1);
}
if (position) {
this.x = position.x;
this.y = position.y;
} else {
this.radius = this.radius / 5;
this.x = Math.random() * (canvas.width - 2 * this.radius) + this.radius;
this.y = Math.random() * (canvas.height - 2 * this.radius) + this.radius;
}
Expand All @@ -85,10 +88,27 @@ class WaveFunction {
this.speedY = this.isLink ? normSpeed : normSpeed * 2;
}

static findValidPosition(radius) {
findValidPosition() {
const radius = this.radius;
const width = canvas.width;
const height = canvas.height;
const centerX = width / 2;
const centerY = height / 2;

for (let i = 0; i < 100; i++) {
const x = Math.random() * (canvas.width - 2 * radius) + radius;
const y = Math.random() * (canvas.height - 2 * radius) + radius;
let x, y;

if (this.isLink) {
// Generate coordinates within a circle around the center
const angle = Math.random() * 2 * Math.PI;
const distance = Math.random() * (width / 4 - radius) + radius;
x = centerX + distance * Math.cos(angle);
y = centerY + distance * Math.sin(angle);
} else {
x = Math.random() * (width - 2 * radius) + radius;
y = Math.random() * (height - 2 * radius) + radius;
}

if (
waveFunctions.every(
(wf) => Math.hypot(x - wf.x, y - wf.y) >= radius + wf.radius,
Expand All @@ -103,47 +123,63 @@ class WaveFunction {
draw() {
ctx.save();
ctx.beginPath();
ctx.globalAlpha = this.alpha;
ctx.globalAlpha = this.alpha * (this.spawnProgress / 100);
ctx.fillStyle = this.color;
ctx.shadowColor = this.color;
ctx.shadowBlur = 15 * this.alpha;
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.shadowBlur = 15 * this.alpha * (this.spawnProgress / 100);

const scaleFactor = this.spawnProgress / 100;
const drawRadius = this.radius * scaleFactor;

ctx.arc(this.x, this.y, drawRadius, 0, Math.PI * 2);
ctx.fill();

if (this.isLink) {
this.drawLabel();
this.drawLabel(scaleFactor);
}

ctx.restore();
}

drawLabel() {
drawLabel(scaleFactor) {
ctx.fillStyle = 'white';
ctx.font = 'bold 32px Arial';
ctx.font = `bold ${32 * scaleFactor}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.save();
ctx.translate(this.x, this.y);
ctx.scale(0.5, 0.5);
ctx.scale(0.5 * scaleFactor, 0.5 * scaleFactor);
ctx.rotate(0.01);

const lines = this.label.split('\n');
if (lines.length === 1) {
ctx.fillText(this.label, 0, 0);
} else if (lines.length === 2) {
ctx.fillText(lines[0], 0, -15);
ctx.fillText(lines[1], 0, 15);
} else {
throw new Error('Too many lines in label');
}
ctx.restore();
}

update(deltaTime) {
if (this.spawnProgress < 100) {
const elapsed = Date.now() - this.spawnStartTime;
this.spawnProgress = Math.min(100, (elapsed / this.spawnDuration) * 100);

// Easing function for smooth pop-up effect
const easeOutBack = (t) => {
const c1 = 1.70158;
const c3 = c1 + 1;
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
};

this.spawnProgress = easeOutBack(this.spawnProgress / 100) * 100;
}

if (this.isCollapsing) {
this.updateCollapse();
} else {
this.updatePosition(deltaTime);
this.ensureInsideCanvas();
}
this.updateGrowth();
}
Expand All @@ -162,12 +198,35 @@ class WaveFunction {
this.bounceOffWalls();
}

ensureInsideCanvas() {
const margin = 1;
this.x = Math.max(
this.radius + margin,
Math.min(canvas.width - this.radius - margin, this.x),
);
this.y = Math.max(
this.radius + margin,
Math.min(canvas.height - this.radius - margin, this.y),
);
}

bounceOffWalls() {
if (this.x + this.radius >= canvas.width || this.x - this.radius <= 0) {
this.speedX = -this.speedX;
const margin = 1; // Small margin to ensure the wavefunction stays inside

if (this.x + this.radius >= canvas.width - margin) {
this.x = canvas.width - this.radius - margin;
this.speedX = -Math.abs(this.speedX);
} else if (this.x - this.radius <= margin) {
this.x = this.radius + margin;
this.speedX = Math.abs(this.speedX);
}
if (this.y + this.radius >= canvas.height || this.y - this.radius <= 0) {
this.speedY = -this.speedY;

if (this.y + this.radius >= canvas.height - margin) {
this.y = canvas.height - this.radius - margin;
this.speedY = -Math.abs(this.speedY);
} else if (this.y - this.radius <= margin) {
this.y = this.radius + margin;
this.speedY = Math.abs(this.speedY);
}
}

Expand All @@ -180,8 +239,8 @@ class WaveFunction {

checkCollision(other) {
return (
Math.hypot(this.x - other.x, this.y - other.y) <
this.radius + other.radius
Math.hypot(this.x - other.x, this.y - other.y) <=
this.radius + other.radius - 1
);
}

Expand Down Expand Up @@ -224,8 +283,8 @@ class WaveFunction {
separateOverlap(other, normalX, normalY, distance) {
const overlap = this.radius + other.radius - distance;
if (overlap > 0) {
const separationX = normalX * overlap * 0.5;
const separationY = normalY * overlap * 0.5;
const separationX = normalX * overlap * 0.51;
const separationY = normalY * overlap * 0.51;
this.x -= separationX;
this.y -= separationY;
other.x += separationX;
Expand Down Expand Up @@ -328,7 +387,7 @@ function animate(currentTime) {
waveFunctions = waveFunctions.filter((wf) => !wf.markedForRemoval);
lastTime = currentTime;
}
if (frameCount % 60 === 0) {
if (frameCount % 10 === 0) {
checkAndSeparateOverlaps();
}
frameCount++;
Expand Down
29 changes: 25 additions & 4 deletions start_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@

"""

import os
import signal
import socket
import subprocess
import webbrowser


def find_open_port(min_port: int = 8000, max_port: int = 9000) -> int:
"""Find an open port in port range, fallback to system-assigned if needed."""

# By default, only bind server to localhost IP
ip_address = os.getenv("BIND_IP", "127.0.0.1")

for port in range(min_port, max_port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.bind(("", port))
s.bind((ip_address, port))
return port
except OSError:
continue

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("", 0))
s.bind((ip_address, 0))
return s.getsockname()[1]


Expand All @@ -28,8 +34,23 @@ def main():
port = find_open_port()
print(f"Starting server on port {port}...")

with subprocess.Popen(["python3", "-m", "http.server", str(port)]):
webbrowser.open(f"http://localhost:{port}")
with subprocess.Popen(
["python3", "-m", "http.server", str(port)], start_new_session=True
) as process:
try:
webbrowser.open(f"http://localhost:{port}")

process.wait()
except KeyboardInterrupt:
print("\nTerminating the server...")
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
print("Server didn't terminate gracefully, forcing shutdown...")
os.killpg(os.getpgid(process.pid), signal.SIGKILL)

print("Server stopped.")


if __name__ == "__main__":
Expand Down
16 changes: 8 additions & 8 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ basepython = python3
[testenv:linters]
extras = lint
commands =
black build_app
isort build_app
ruff format build_app
black build_app start_server.py
isort build_app start_server.py
ruff format build_app start_server.py

[testenv:format-check]
extras = lint
commands =
black --check build_app
isort --check-only build_app
pylint build_app
mypy build_app
ruff check build_app
black --check build_app start_server.py
isort --check-only build_app start_server.py
pylint build_app start_server.py
mypy build_app start_server.py
ruff check build_app start_server.py
Loading