Compare commits
72
Commits
d6a3bf57c8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
625e064285 | ||
|
|
b54e752cfb | ||
|
|
2150e9a0cd | ||
|
|
48f643a45b | ||
|
|
b79bfc78f0 | ||
|
|
c41ae4da75 | ||
|
|
5ed08ed8ef | ||
|
|
c00fd5ac4f | ||
|
|
7a7cf24dad | ||
|
|
f840b25347 | ||
|
|
758d9b234f | ||
|
|
98fd81bd01 | ||
|
|
1b54a93d03 | ||
|
|
c61fec03da | ||
|
|
d8c664b9e0 | ||
|
|
bce09e45c2 | ||
|
|
9c38c61f56 | ||
|
|
007fe255c3 | ||
|
|
ee555c07bf | ||
|
|
f947049144 | ||
|
|
21ea975ccc | ||
|
|
2a84fafca6 | ||
|
|
852b625878 | ||
|
|
a33430a4a8 | ||
|
|
91b6b46d4f | ||
|
|
526cf942c8 | ||
|
|
35b1f42a31 | ||
|
|
d498fceaa9 | ||
|
|
6dda0a2b62 | ||
|
|
8cff5de85e | ||
|
|
3e602844f8 | ||
|
|
c6c3f90f89 | ||
|
|
b0750c3c79 | ||
|
|
9b3ed7e034 | ||
|
|
84057a4434 | ||
|
|
d48352ced2 | ||
|
|
800bd38adb | ||
|
|
9734614396 | ||
|
|
20c71c46ab | ||
|
|
f1cbe1faf4 | ||
|
|
368f4d9ea6 | ||
|
|
3472a9ca77 | ||
|
|
9363bff72c | ||
|
|
6556cfd313 | ||
|
|
92696c31bc | ||
|
|
1125ef2d9e | ||
|
|
e8dd33484a | ||
|
|
06d77cc51b | ||
|
|
8a5ddceef5 | ||
|
|
ce3a225c47 | ||
|
|
f8ffb0475c | ||
|
|
6dc840a3f7 | ||
|
|
70239a83cd | ||
|
|
bdce9ba496 | ||
|
|
08f079b4e2 | ||
|
|
dd4b41b3a7 | ||
|
|
f308864967 | ||
|
|
5e06dcfc15 | ||
|
|
6b73704925 | ||
|
|
d9e0bb7b76 | ||
|
|
bb3496584e | ||
|
|
1fb94729f4 | ||
|
|
03a8ac049c | ||
|
|
1d740f4226 | ||
|
|
03b84351d7 | ||
|
|
12060a97c6 | ||
|
|
971ef3dd1c | ||
|
|
af6076a24c | ||
|
|
cce76ffdc0 | ||
|
|
a06f527398 | ||
|
|
66f839a6b7 | ||
|
|
62463c2f61 |
@@ -6,23 +6,38 @@ on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: /bin/sh -e {0}
|
||||
|
||||
env:
|
||||
PATH: /run/current-system/sw/bin:/run/wrappers/bin:/usr/bin:/bin
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: self-hosted
|
||||
runs-on: homelab
|
||||
steps:
|
||||
- name: Configure git
|
||||
run: git config --global --add safe.directory /servers/site
|
||||
|
||||
- name: Create repo
|
||||
run: |
|
||||
[ ! -d /servers/site ] && git clone https://git.jeremyjanella.com/jjanella/site.git /servers/site || true
|
||||
|
||||
- name: Pull
|
||||
working-directory: /opt/site
|
||||
working-directory: /servers/site
|
||||
run: git pull origin main
|
||||
|
||||
- name: Build
|
||||
working-directory: /opt/site
|
||||
run: cargo build --release
|
||||
working-directory: /servers/site
|
||||
run: |
|
||||
nix develop --command cargo build --release 2>&1
|
||||
|
||||
- name: Restart site
|
||||
working-directory: /opt/site
|
||||
working-directory: /servers/site
|
||||
run: |
|
||||
pkill site || true
|
||||
export RUNNER_TRACKING_ID=self-managed
|
||||
nohup ./target/release/site > server.log 2>&1 &
|
||||
setsid ./target/release/site > server.log 2>&1 &
|
||||
sleep 1
|
||||
pgrep site && echo "Site is running!"
|
||||
|
||||
Generated
+117
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"nodes": {
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1779612045,
|
||||
"narHash": "sha256-+7lfNVnmXJDkiRYHd5NoNwYoyUcc0LcXPaIJqjO7VWM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "d7be747f0a65af378de515fc3cee131bf99a008f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1779508470,
|
||||
"narHash": "sha256-Ap9KJX+5xHIn3bPIpfNgT6MEXdAECECwo4/rmlQD74M=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "29916453413845e54a65b8a1cf996842300cd299",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1785090369,
|
||||
"narHash": "sha256-m0pDuRJG7EDo9ri+4Ksu83VsI+PlxNC9lNBfydejce4=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "624af665418d3c65d544145b4d34ad696439570e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"fenix": "fenix",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1779569060,
|
||||
"narHash": "sha256-NSnk5D+3KEfRdbgPijs33N2RAKSG6A74SwfnynLcouo=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "987ea33645ab1c709b1df6823038abcb2fe8973e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "rust-lang",
|
||||
"ref": "nightly",
|
||||
"repo": "rust-analyzer",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
description = "Rust development environment";
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
fenix = {
|
||||
url = "github:nix-community/fenix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
};
|
||||
outputs = { self, nixpkgs, nixpkgs-unstable, flake-utils, fenix }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
toolchain = fenix.packages.${system}.fromToolchainFile {
|
||||
file = ./rust-toolchain.toml;
|
||||
sha256 = "sha256-A1abGIbOtcBSdrUMhDGrER3pRM1hQP4fp9gh3Y4PKc8=";
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell rec {
|
||||
nativeBuildInputs = [ pkgs.pkg-config ];
|
||||
buildInputs = with pkgs; [
|
||||
toolchain
|
||||
llvmPackages.bintools
|
||||
clang
|
||||
nixpkgs-unstable.legacyPackages.${pkgs.system}.typst
|
||||
];
|
||||
LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
|
||||
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') []);
|
||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (buildInputs ++ nativeBuildInputs);
|
||||
BINDGEN_EXTRA_CLANG_ARGS =
|
||||
(builtins.map (a: ''-I"${a}/include"'') [ pkgs.glibc.dev ])
|
||||
++ [
|
||||
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
||||
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
||||
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
let
|
||||
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
|
||||
libPath = with pkgs; lib.makeLibraryPath [
|
||||
# load external libraries that you need in your rust project here
|
||||
];
|
||||
in
|
||||
pkgs.mkShell rec {
|
||||
nativeBuildInputs = [ pkgs.pkg-config ];
|
||||
buildInputs = with pkgs; [
|
||||
clang
|
||||
# Replace llvmPackages with llvmPackages_X, where X is the latest LLVM version (at the time of writing, 16)
|
||||
llvmPackages.bintools
|
||||
rustup
|
||||
typst
|
||||
];
|
||||
|
||||
RUSTC_VERSION = overrides.toolchain.channel;
|
||||
|
||||
# https://github.com/rust-lang/rust-bindgen#environment-variables
|
||||
LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
|
||||
|
||||
shellHook = ''
|
||||
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
||||
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
|
||||
'';
|
||||
|
||||
# Add precompiled library to rustc search path
|
||||
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
|
||||
# add libraries here (e.g. pkgs.libvmi)
|
||||
]);
|
||||
|
||||
LD_LIBRARY_PATH = libPath;
|
||||
|
||||
# Add glibc, clang, glib, and other headers to bindgen search path
|
||||
BINDGEN_EXTRA_CLANG_ARGS =
|
||||
# Includes normal include path
|
||||
(builtins.map (a: ''-I"${a}/include"'') [
|
||||
# add dev libraries here (e.g. pkgs.libvmi.dev)
|
||||
pkgs.glibc.dev
|
||||
])
|
||||
# Includes with special directory paths
|
||||
++ [
|
||||
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
||||
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
||||
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
||||
];
|
||||
}
|
||||
+3
-2
@@ -3,8 +3,6 @@ use axum::{
|
||||
};
|
||||
use tower_http::services::ServeDir;
|
||||
use askama::Template;
|
||||
// use typst::model;
|
||||
// use typst_html;
|
||||
use std::{collections::HashMap, fs, io::{Error, Write}, process::{Command, Stdio}};
|
||||
use regex::Regex;
|
||||
|
||||
@@ -117,6 +115,9 @@ async fn get_post(
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// unsafe {
|
||||
// std::env::set_var("TYPST_FEATURES", "html");
|
||||
// }
|
||||
|
||||
let home_html = Html(
|
||||
HomeTemplate {
|
||||
|
||||
+74
-11
@@ -1,17 +1,80 @@
|
||||
#import "@preview/treet:1.0.0": *
|
||||
#show list: set text(font: "DejaVu Sans Mono", weight: "bold")
|
||||
|
||||
|
||||
= About me
|
||||
I enjoy competing in capture the flag competitions (cybersecurity and the backyard game), working with robotics, automating tasks and thinking through problems.
|
||||
Computer Science student at the University of Toronto with experience in cybersecurity, firmware, low-level development and software engineering. Interested in deep technical work, solutions architecting. A continuous learner interested in owning problems, researching and implementing solutions.
|
||||
|
||||
I've competed in a few hackathons which are cool, but I prefer spending large time frames incrementally understand solutions to much larger problems, and attempting solutions to those.
|
||||
// === What can I do
|
||||
|
||||
I think automation is one of the most powerful abilities we have. As a species we are on the brink of mass automation, which can either liberate our lives into a post-scarcity utopia, or give a small percentage of us more control over the rest than anyone ever has had.
|
||||
|
||||
An odd and maybe abstract thing I enjoy is design philosophy. Some examples are creating software to building moral structures through logic to methods of learning new skills.
|
||||
|
||||
NixOS has to be my favourite distribution of Linux. My attraction to its design philosophy is that if something works, it will always work anywhere, and can still be worked on long after everything around it has moved on. Its hard to put into words but this feels like debian level stability with arch-like freedom to explore software.
|
||||
|
||||
When I'm not nerding out on math, software, ethics, and optimal ways of learning things, I manage to get outdoors. I like mountain biking through trails, snowboarding, hiking through scenic areas, swimming, and basketball. The next thing I'll be getting into is likely SCUBA diving.
|
||||
|
||||
I wrote this myself. While AI communicates ideas better than I do, I want to ensure that my and only my thoughts are written. That is true for anything on this site.
|
||||
=== Interests & Skills
|
||||
|
||||
#html.elem("div", attrs: (class: "tree"))[
|
||||
#tree-list(
|
||||
indent: "│ ",
|
||||
empty-indent: " "
|
||||
)[
|
||||
- Research
|
||||
- Computer Science
|
||||
- Linux
|
||||
- Nix & NixOS
|
||||
- Perfectly reproducible, time independent environments
|
||||
- IoT / microcomputers & microcontrollers
|
||||
- Hosting servers
|
||||
- Docker
|
||||
- Nix OCI images
|
||||
- Cyber security
|
||||
- Reverse engineering assembly
|
||||
- Binary patching
|
||||
- Anticheat
|
||||
- CTF's
|
||||
- CompTIA PenTest+ certified
|
||||
- AI exploitation
|
||||
- Tricking image classifiers
|
||||
- Prompt injection techniques
|
||||
- Extracting sensitive data with statistics
|
||||
- Side channel attacks
|
||||
- Van Eck Phreaking / TEMPEST
|
||||
- RF Sensing
|
||||
- SPECTRE
|
||||
- OSINT
|
||||
- Machine learning & algorithms
|
||||
- UTAT Satellite Firmware Development
|
||||
- Automation
|
||||
- Robotics
|
||||
- Programming and computing paths & trajectories etc
|
||||
- AI - how much can this actually do, when orchestrated into a full platform?
|
||||
- Langchain
|
||||
- Random conjectures & problems
|
||||
- Collatz Conjecture
|
||||
- Travelling salesman
|
||||
- Graph Reconstruction
|
||||
- Math
|
||||
- Stats
|
||||
- Philosophy
|
||||
- Ethics
|
||||
- Development
|
||||
- Infrastructure
|
||||
- Programming
|
||||
- Rust
|
||||
- C/C++
|
||||
- Python
|
||||
- Bash
|
||||
- Assembly
|
||||
- Functional
|
||||
- Object Oriented
|
||||
- Design patterns
|
||||
- SQL
|
||||
- GDScript
|
||||
- Game development - problem solving outlet
|
||||
- Multiplayer Architecture
|
||||
- Data transfer optimization
|
||||
- Security
|
||||
- High performance code
|
||||
- Touching grass
|
||||
- Mountain biking - especially technical trails
|
||||
- Hiking
|
||||
- Snowboarding
|
||||
- Swimming (Used to lifeguard & teach lessons!)
|
||||
]
|
||||
]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 249 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 157 KiB |
@@ -0,0 +1,126 @@
|
||||
#let post_slug = "dynamic-vision-shader"
|
||||
#let post_preview_image = "image2.png"
|
||||
#let post_summary = "Improvements to Orebit's dynamic vision"
|
||||
#let post_date = "2026-07-28"
|
||||
|
||||
|
||||
#import "@preview/cetz:0.5.2"
|
||||
#import "@preview/cetz-plot:0.1.4"
|
||||
|
||||
|
||||
#let stroke_color = white
|
||||
#let canvas_style = (
|
||||
circle: (fill: stroke_color),
|
||||
stroke: stroke_color,
|
||||
)
|
||||
|
||||
|
||||
= Dynamic Vision
|
||||
|
||||
== The problem
|
||||
In 3d, objects occlude your vision, and you cannot see behind them. This isn't really implemented in most 2D games as it can be disorienting, confusing, and therefore unnecessary.
|
||||
|
||||
For Orebit, disorientation is a mechanic. Careless piloting in unfamiliar territory may cost the player a collision with the terrain or hostiles. Lack of spatial awareness can make it hard to detect your neighbors, which is great for hiding from potential enemies. As much of the players time is underground, the claustrophobia induced by occluding vision is also useful. With this in mind, it adds to the immersion to not know what lies around corners, and dynamic 2D vision is necessary.
|
||||
|
||||
|
||||
== Solution 1
|
||||
Lighting mechanics can simulate dynamic vision, using darkness to represent hidden areas. First, the whole world needs to be dark. Then, emit light where the player should be able to see. Objects with light occluders on cast shadows, which effectively blocks vision behind objects like terrain.
|
||||
|
||||
In Godot, this is implemented by applying a black CanvasModulate node to a scene to create darkness. Objects with collision polygons can reuse their polygon in a light occluder. PointLights using a circular texture can emit light around the character.
|
||||
#image("/static/posts/dynamic-vision/image.png")
|
||||
|
||||
== Solution 1 Flaws
|
||||
The world in Orebit is massive, which becomes a problem when trying to light the entire area. The radius of the circular texture used must be at least as large as the farthest point visible, which can be tens of thousands of pixels away. This means a texture tens of thousands of pixels squared (for the x and y axis) must be used, which severely impacts performance even on high end hardware.
|
||||
|
||||
The solution to this is to scale up a lower resolution light texture, but this has its own issues. The texture is made of discrete pixels, each one black or white if it is occluded or not occluded. As these pixels scale the shadow edges go from smooth, high resolution edges to pixelated, jagged edges. The other problem with scaling light maps is that light clips through objects close to the light source.
|
||||
|
||||
|
||||
== Solution 2
|
||||
Another way to understand the problem is to think of every vision-occluding polygon as a set of vision-occluding line segments, ie $overline(A B)$.
|
||||
|
||||
#set text(fill: stroke_color)
|
||||
#html.frame(
|
||||
align(center,
|
||||
cetz.canvas({
|
||||
import cetz.draw: *
|
||||
set-style(..canvas_style)
|
||||
circle((0, 6), radius: (0.1), name: "C")
|
||||
line((1, 4), "C", (2, 5), stroke: (dash: "dashed"))
|
||||
line((0, 2), (2, 2), (3, 4), (2, 5), (1, 4), (0, 4), (-1, 3), (0, 2))
|
||||
line((2,5), (1, 4), name: "AB", stroke: (paint: blue, thickness: 4pt))
|
||||
content("AB", $ overline(A B) $, anchor: "south-east", padding: .2)
|
||||
})))
|
||||
#set text(fill: black)
|
||||
|
||||
Each line segment occludes a quad (a shape with four vertices) composed of one point on each end of the line segment, and another point at each line end projected away from the character.
|
||||
|
||||
$
|
||||
d & >> 1\
|
||||
p_1 &= A\
|
||||
p_2 &= C + d (A-C)\
|
||||
p_3 &= C + d (B-C)\
|
||||
p_4 &= B\
|
||||
$
|
||||
|
||||
|
||||
#set text(fill: stroke_color)
|
||||
#html.frame(
|
||||
align(center,
|
||||
cetz.canvas({
|
||||
import cetz.draw: *
|
||||
set-style(..canvas_style)
|
||||
|
||||
circle((0, 6), radius: (0.1), name: "C")
|
||||
content("C", $ C $, anchor: "east", padding: .2)
|
||||
|
||||
circle((2, 5), radius: (0.1), name: "A")
|
||||
content("A", $ p_1 $, anchor: "south-west", padding: .2)
|
||||
|
||||
circle((1, 4), radius: (0.1), name: "B")
|
||||
content("B", $ p_4 $, anchor: "north-east", padding: .2)
|
||||
|
||||
circle((6, 3), radius: (0.1), name: "p2")
|
||||
content("p2", $ p_2 $, anchor: "west", padding: .2)
|
||||
|
||||
circle((3, 0), radius: (0.1), name: "p3")
|
||||
content("p3", $ p_3 $, anchor: "north", padding: .2)
|
||||
|
||||
line((0, 2), (2, 2), (3, 4), (2, 5), (1, 4), (0, 4), (-1, 3), (0, 2))
|
||||
// circle((2, 5), radius: (0.08), fill: black, name: "p1")
|
||||
line((2, 5), (6, 3), (3, 0), (1, 4),
|
||||
fill: rgb(50, 50, 255, 100),
|
||||
stroke: 0pt
|
||||
)
|
||||
line("A", "C", "B", stroke: (dash: "dashed"))
|
||||
// content((0, 2), $ p_1 $, anchor: "south", padding: .2)
|
||||
})))
|
||||
#set text(fill: black)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Solution 2 can be implemented in Godot using ImmediateMesh's for each line in the polygon, with two triangles composing each quad for each line in the occluders polygon. Setting vertex color allows the shader to differentiate which vertices to project away from the character.
|
||||
|
||||
To update the mesh each frame, a shader on the ImmediateMesh can take the line segment and live character position. Despite running on thousands of line segments, the ImmediateMesh and gpu-shader exist to efficiently solve problems like this.
|
||||
|
||||
```glsl
|
||||
shader_type canvas_item;
|
||||
# use the global coordinate system
|
||||
render_mode world_vertex_coords;
|
||||
|
||||
# updated each physics frame
|
||||
uniform vec2 character_position;
|
||||
|
||||
void vertex() {
|
||||
if (COLOR.r > 0.5) {
|
||||
VERTEX = VERTEX + (1e6 * (VERTEX - character_position));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
(Solution 1 overlaid on solution 2)
|
||||
#image("/static/posts/dynamic-vision/image2.png")
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
@@ -0,0 +1,71 @@
|
||||
#let post_slug = "pe-p1"
|
||||
#let post_preview_image = "image.png"
|
||||
#let post_summary = "General solution to Project Euler Problem 1"
|
||||
#let post_date = "2026-07-29"
|
||||
|
||||
= Project Euler Problem 1
|
||||
|
||||
== Statement
|
||||
If we list all the natural numbers below $10$ that are multiples of $3$ or $5$, we get $3, 5, 6,$ and $9$. The sum of these multiples is $23$. Find the sum of all the multiples of $3$ or $5$ below $1000$.
|
||||
|
||||
== Solution
|
||||
|
||||
Suppose the base numbers were $2$ and $3$ below $10$ so $9$, they would create duplicates, ie $6$ is counted twice. Therefore take the sum of each multiple to the limit, and one time remove the numbers counted twice:
|
||||
|
||||
#text(fill: white, html.frame(
|
||||
$
|
||||
&2+4+6+8+3+cancel(6)+9 = 32\
|
||||
&sum_(i=1)^(floor(9/(2)))2i + sum_(i=1)^(floor(9/(3))) 3i - sum_(i=1)^(floor(9/(6))) 6i \
|
||||
= 2&sum_(i=1)^(floor(9/(2)))2i + 3sum_(i=1)^(floor(9/(3))) i - 6sum_(i=1)^(floor(9/(6))) i \
|
||||
= 2&sum_(i=1)^4 i + 3sum_(i=1)^3 i - 6sum_(i=1)^(1) i\
|
||||
= 2& (4(5))/2 + 3 (3(4))/2 - 6 (1(2))/2\
|
||||
= 3&2
|
||||
$
|
||||
))
|
||||
|
||||
This revealed a general solution, for base numbers $n, m$ below $L$:
|
||||
|
||||
#text(fill: white, html.frame(
|
||||
$
|
||||
f(n,m,L) =&n sum_(i=1)^floor((L-1)/n) i + m sum_(i=1)^floor((L-1)/m) i- n m sum_(i=1)^floor((L-1)/(n m))i \
|
||||
"Define "&a = floor((L-1)/n), b = floor((L-1)/m), c = floor((L-1)/(n m)) \
|
||||
=> f(n,m,L) =& (n a(a+1) + m b(b+1) - n m (c)(c+1))/2
|
||||
$
|
||||
))
|
||||
|
||||
Testing this as code:
|
||||
```py
|
||||
>>> def f(n,m,L):
|
||||
... a = floor((L-1)/n)
|
||||
... b = floor((L-1)/m)
|
||||
... c = floor((L-1)/(n*m))
|
||||
... return ((n*a*(a+1)) + (m*b*(b+1)) - (n*m*c*(c+1)))/2
|
||||
...
|
||||
>>> f(2,3,10)
|
||||
44.0
|
||||
>>> f(3,5,10)
|
||||
23.0
|
||||
```
|
||||
|
||||
This general solution passes the two tests above.
|
||||
|
||||
```py
|
||||
>>> f(3,5,1000)
|
||||
233168.0
|
||||
```
|
||||
|
||||
Testing against the question, this result is successful. But what about for more than 2 numbers? How can this be approached?
|
||||
|
||||
Given a set $A$ of base numbers, a multiple would be counted once for each of its divisors present in $A$.
|
||||
|
||||
An easy solution is that in code, a hashmap could track the number of times each number is counted, and iteratively remove duplicates.
|
||||
|
||||
The goal is to not add the number if it was already added.
|
||||
|
||||
Mathamatically this can be done piecewise. If a previous element of $A$ divides $A_i k$, skip that number.
|
||||
|
||||
#text(fill: white, html.frame(
|
||||
$
|
||||
f(A, L) = sum_(i)^abs(A)[sum_(k=1)^floor((L-1)/A_i) cases(0 quad & A_j divides A_i k and j < k, A_i k quad & A_j divides.not A_i k and j < k)]
|
||||
$
|
||||
))
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,79 @@
|
||||
#let post_slug = "pe-p2"
|
||||
#let post_preview_image = "image.png"
|
||||
#let post_summary = "Solution to Project Euler Problem 2"
|
||||
#let post_date = "2026-07-29"
|
||||
|
||||
#set math.mat(delim: "[")
|
||||
|
||||
= Project Euler Problem 2
|
||||
|
||||
== Statement
|
||||
Each new term in the Fibonacci sequence is generated by adding the previous two terms.
|
||||
|
||||
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
|
||||
|
||||
== Exploration
|
||||
|
||||
|
||||
The even terms in the sequence are:
|
||||
#text(fill: white, html.frame(
|
||||
$ cal(F) = {0, cancel(1), cancel(1), 2, cancel(3), cancel(5), 8, cancel(13), cancel(21), 34, cancel(55), ...} $
|
||||
))
|
||||
|
||||
This can be computed quickly with code, some Fibonacci sequence equations would be interesting to derive.
|
||||
|
||||
Notice every 3rd number is even, as the previous two numbers are odd, which sums to even. A similar statement can be said about the odd numbers.
|
||||
|
||||
The Fibonacci sequence can be generated with the equation:
|
||||
#text(fill: white, html.frame(
|
||||
$ mat(cal(F)_i; cal(F)_(i+1)) = mat(0, 1; 1, 1) mat(cal(F)_(i-1); cal(F)_i) $
|
||||
))
|
||||
Then every third number can be defined with the equation:
|
||||
#text(fill: white, html.frame(
|
||||
$
|
||||
&mat(cal(F)_(i+2); cal(F)_(i+3)) = mat(0, 1; 1, 1)^3 mat(cal(F)_(i-1); cal(F)_i)\
|
||||
=>&mat(cal(F)_(i+2); cal(F)_(i+3)) = mat(1, 2; 2, 3) mat(cal(F)_(i-1); cal(F)_i)
|
||||
$
|
||||
))
|
||||
Also something interesting I noticed for #text(fill: white, html.frame($cal(F) = {0, 1, 1, 2, 3, 5, 8, ...} (cal(F)_0 = 0, i>= 1)$)) is:
|
||||
#text(fill: white, html.frame(
|
||||
$
|
||||
mat(0, 1; 1, 1)^i = mat(cal(F)_(i-1), cal(F)_(i); cal(F)_(i), cal(F)_(i+1))
|
||||
$
|
||||
))
|
||||
|
||||
Playing with that leads to the similar equation
|
||||
|
||||
#text(fill: white, html.frame(
|
||||
[$cal(F)_(i+j) = cal(F)_(i) cal(F)_(j+1) + cal(F)_(i-1) cal(F)_(j)$,\ thus $cal(F)_(i+3) = 3cal(F)_i + 2cal(F)_(i-1)$]
|
||||
))
|
||||
|
||||
An equation to represent this problem is as follows, however this would need a computer to finish in reasonable time.
|
||||
#text(fill: white, html.frame(
|
||||
$
|
||||
sum_(i=1)^(n: cal(F)_(3n)<4 times 10^6) cal(F)_(3i)
|
||||
$
|
||||
))
|
||||
|
||||
== Solution
|
||||
|
||||
Since every third number is even, take each third element of the sequence until four million. Three step jumps can be taken as defined by the matrix above.
|
||||
|
||||
```py
|
||||
>>> class Pair:
|
||||
... def __init__(self, fi_m1, fi):
|
||||
... self.fi_m1 = fi_m1
|
||||
... self.fi = fi
|
||||
>>> def f_3(pair):
|
||||
... return Pair(
|
||||
... pair.fi_m1 + (2 * pair.fi),
|
||||
... (2 * pair.fi_m1) + (3 * pair.fi)
|
||||
... )
|
||||
>>> x = Pair(1, 2)
|
||||
>>> s = 0
|
||||
>>> while x.fi < 4e6:
|
||||
... s += x.fi
|
||||
... x = f_3(x)
|
||||
>>> print(s)
|
||||
4613732
|
||||
```
|
||||
+35
-11
@@ -12,6 +12,15 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.post a {
|
||||
text-decoration: underline;
|
||||
color:orangered;
|
||||
}
|
||||
|
||||
.post * {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#navbar {
|
||||
display: flex;
|
||||
background: #000;
|
||||
@@ -73,6 +82,21 @@ a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
math * {
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
.multiline-equation {
|
||||
text-align: center;
|
||||
margin-left: 50%;
|
||||
margin-right: 50%;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
|
||||
.hover-underline {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
@@ -114,15 +138,6 @@ a {
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
|
||||
.post a {
|
||||
text-decoration: underline;
|
||||
color:orangered;
|
||||
}
|
||||
|
||||
.post * {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow-x: auto;
|
||||
background-color: #000;
|
||||
@@ -130,7 +145,7 @@ pre {
|
||||
padding: 2pt;
|
||||
}
|
||||
|
||||
code {
|
||||
code, pre code span {
|
||||
font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", "Source Code Pro", monospace;
|
||||
font-variant-ligatures: normal;
|
||||
white-space: pre;
|
||||
@@ -144,12 +159,16 @@ code {
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
#navbar ul li:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
#navbar ul {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@keyframes nameShadow {
|
||||
@@ -180,3 +199,8 @@ body {
|
||||
background-image: linear-gradient(330deg, #222, #040404);
|
||||
}
|
||||
|
||||
.tree {
|
||||
font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", "Source Code Pro", monospace;
|
||||
line-height: 1.05;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
<ul>
|
||||
<li><a class="hover-underline" href="/">Home</a></li>
|
||||
<li><a class="hover-underline" href="/posts">Posts</a></li>
|
||||
<!-- <li><a>Contact</a></li> -->
|
||||
<li><a class="hover-underline" href="https://git.jeremyjanella.com/jjanella/site">Source</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user