feat(algorithms): add how-it-works and source links
Add idea, complexity, data structures, GPU use to 10 algorithm pages. Link logic files on Gitea; a spec fails on a missing path. Fix wrong texts: cloth phases, pendulum dt, fractal lighting, typos.
This commit is contained in:
1 parent
0857ce482b
commit
4b19245c6c
20 files changed
+318
-27
No files matched your search
+86
-8
@@ -499,6 +499,14 @@
|
||||
},
|
||||
"ALERT": {
|
||||
"START_END_NODES": "Please select a start and end node before running the algorithm."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "The grid is a two-dimensional array of nodes. Each node stores its distance from the start, its predecessor and, for A*, the heuristic h and the sum f = g + h. Both algorithms run to the end first and return the list of visited nodes and the path. The animation then replays that list node by node with setTimeout.",
|
||||
"ITEM_1": "Dijkstra: all nodes sit in one list. Dijkstra sorts the whole list by distance in every step (O(n² log n)) and takes the next node. A heap would be faster; with at most 22,500 nodes this is enough here. If the next node has distance infinity, the goal cannot be reached.",
|
||||
"ITEM_2": "A*: same structure, but only the open list is sorted, by f = g + h; on equal f the smaller h wins. The heuristic is the Manhattan distance. It is admissible because each step costs 1 and there are only four directions, so the path stays optimal.",
|
||||
"ITEM_3": "The path is read at the end from the predecessor chain, from the goal back to the start.",
|
||||
"ITEM_4": "Compare mode: start, goal and walls are copied into a second grid. A* runs on the first, Dijkstra on the second, both animations run at the same time. The number of visited nodes shows the difference.",
|
||||
"ITEM_5": "Execution time is the search only, without the animation. Memory: one node object per cell, at most 150 × 150 = 22,500 nodes."
|
||||
}
|
||||
},
|
||||
"SORTING": {
|
||||
@@ -523,6 +531,14 @@
|
||||
"DISCLAIMER_2": "Average Case: The statistical norm.",
|
||||
"DISCLAIMER_3": "Worst Case: The data is arranged in the most unfavorable way possible (Quick Sort performs poorly here without optimization, while Heap Sort remains stable).",
|
||||
"DISCLAIMER_4": "Additionally, there is almost always a Time-Space Trade-off: Algorithms that are extremely fast (like Merge Sort) often require a lot of additional working memory. Algorithms that work directly in existing memory (like Heap Sort) save space but are sometimes more complex or slightly slower. Thus, there is no \"one-size-fits-all\" solution."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "Each algorithm runs to the end first and stores a copy of the array (snapshot) in a list at every comparison and every swap. The animation then replays the snapshots at 100 ms per step. Execution time is the run including the copies, not the animation.",
|
||||
"ITEM_1": "Bubble sort and cocktail sort: O(n²) comparisons, in place. Cocktail sort changes direction every pass and stops as soon as a pass swaps nothing.",
|
||||
"ITEM_2": "Quick sort: Lomuto partition with the last element as pivot, recursive. Average O(n log n), worst case O(n²), for example on already sorted data.",
|
||||
"ITEM_3": "Heap sort: builds a max heap inside the array (heapify from the middle to the front) and swaps the root to the end. Always O(n log n), no extra memory.",
|
||||
"ITEM_4": "Tim sort (simplified): insertion sort on fixed blocks of 32 elements, then bottom-up merge with helper arrays. No run detection and no galloping like the original. With 200 bars that is 7 blocks and 3 merge levels.",
|
||||
"ITEM_5": "Memory: the snapshot list is the biggest item, O(steps · n). Bubble sort with 200 bars produces up to about 40,000 snapshots of 200 entries each."
|
||||
}
|
||||
},
|
||||
"GOL": {
|
||||
@@ -545,6 +561,13 @@
|
||||
"DISCLAIMER_2": "A dead cell is “born” (is alive in the next generation) if it has exactly three living neighbors.",
|
||||
"DISCLAIMER_3": "A living cell “dies” (is dead in the next generation) if it has fewer than two (underpopulation) or more than three (overpopulation) living neighbors.",
|
||||
"DISCLAIMER_4": "A dead cell remains dead if it does not have exactly three living neighbors."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "The board is two grids of cells with an alive flag: a read grid and a write grid (double buffering). Each generation reads only from one and writes only to the other, then both are swapped. So a cell that already changed does not affect its neighbors in the same generation.",
|
||||
"ITEM_1": "For each cell the eight neighbors are counted (Moore neighborhood). The border is cut off, not wrapped: border cells have fewer neighbors, the board is not a torus.",
|
||||
"ITEM_2": "Time per generation is O(rows · columns · 8). At 200 × 200 that is 40,000 cells and 320,000 neighbor checks.",
|
||||
"ITEM_3": "The loop is an async loop: compute, draw, then wait until the configured time per generation has passed. If no cell changes any more, the game stops by itself.",
|
||||
"ITEM_4": "The whole grid is drawn onto a canvas: one fill operation for the background plus one per living cell."
|
||||
}
|
||||
},
|
||||
"LABYRINTH": {
|
||||
@@ -560,6 +583,13 @@
|
||||
"DISCLAIMER_2": "Reachability: Since it is a spanning tree, every cell in the grid is guaranteed to be part of the labyrinth; there are no isolated areas.",
|
||||
"DISCLAIMER_3": "Randomness: By weighting the edges with random values, each run produces completely new, unique structures.",
|
||||
"DISCLAIMER_4": "Application: Such labyrinths are the perfect test environment for pathfinding algorithms such as Dijkstra or A*."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "The grid has 101 × 101 cells. Cells with even coordinates are rooms (51 × 51 = 2,601), the cells between them are walls. Both algorithms first compute the order in which walls are opened; the animation then opens them one by one. The result is a spanning tree: every room is reachable and there is exactly one way between two rooms.",
|
||||
"ITEM_1": "Prim (randomized): starts in a random room and keeps a frontier list of walls to rooms not yet visited. Each step picks a random wall (swap with the last element, then pop: O(1)) and opens it if the room behind it is still unvisited. Randomness replaces edge weights.",
|
||||
"ITEM_2": "Kruskal (randomized): all 5,100 walls between two rooms are collected and shuffled with Fisher-Yates. Each room carries a set id. A wall is opened when the rooms have different ids; then all rooms of one set get the id of the other. That is one pass over the grid per union, simpler than union-find and fast enough for 2,601 rooms.",
|
||||
"ITEM_3": "Start and goal sit in two random corners. Dijkstra and A* then run on the same grid as on the pathfinding page.",
|
||||
"ITEM_4": "Memory: one node object per cell (10,201) plus the list of cells to open."
|
||||
}
|
||||
},
|
||||
"FRACTAL": {
|
||||
@@ -580,6 +610,14 @@
|
||||
"DISCLAIMER_3": "Complex Numbers: Calculations don't happen in a standard coordinate system, but in the complex plane using real and imaginary components.",
|
||||
"DISCLAIMER_4": "Computational Load: Since hundreds of calculations are performed for every single pixel, fractals are a classic benchmark for GPU and processor performance.",
|
||||
"DISCLAIMER_BOTTOM": "Graphics cards calculate with 32-bit floating point numbers (float) by default. These only have about 7 digits of accuracy. At very high zoom levels (> 100.000), the difference between two pixels is so tiny that the graphics card can no longer display it. It calculates exactly the same value for 10 pixels next to each other -> you see blocks or stair steps."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "The image is a fragment shader (GLSL) on a full-screen quad in Babylon.js. Each pixel computes its own sequence: for Mandelbrot, Julia and Burning Ship z → z² + c until |z|² > 4 or the maximum iteration count is reached (escape-time algorithm). Newton iterates z → z − (z³ − 1) / (3z²) and colors by the root the pixel converges to.",
|
||||
"ITEM_1": "The CPU sends only uniforms per frame: center, zoom, maximum iterations, algorithm, color scheme and the Julia constant. The GPU computes all pixels in parallel; cost O(pixels · iterations).",
|
||||
"ITEM_2": "The maximum iteration count grows with the zoom: 100 + 200 · log10(zoom), so up to about 1,060 at the maximum zoom of 64,000.",
|
||||
"ITEM_3": "Zooming with the mouse wheel keeps the point under the cursor fixed: its world point is set equal before and after the zoom and the center is moved accordingly.",
|
||||
"ITEM_4": "The color comes from the ratio iterations / maximum; points that never escape get the color for t ≈ 1 (dark red in the Blue-Gold scheme).",
|
||||
"ITEM_5": "Under WebGPU Babylon.js translates the same GLSL shader to WGSL; there is no separate compute path."
|
||||
}
|
||||
},
|
||||
"FRACTAL3D": {
|
||||
@@ -597,7 +635,15 @@
|
||||
"DISCLAIMER_1": "No Polygons: There are no triangles or meshes. The shape is calculated mathematically for every pixel in real-time.",
|
||||
"DISCLAIMER_2": "Distance Estimation: The algorithm 'marches' light rays forward by calculating the safe distance to the nearest object without hitting it immediately.",
|
||||
"DISCLAIMER_3": "Infinite Detail: Since the surface is mathematically defined, it never pixelates when zooming in – new structures always emerge.",
|
||||
"DISCLAIMER_4": "Light & Shadow: To visualize depth, light reflections and shadows (Ambient Occlusion) are simulated based on the curvature of the formula."
|
||||
"DISCLAIMER_4": "Light: depth comes from the surface normal: faces that point at the camera are brighter, plus a fixed ambient term. Shadows and ambient occlusion are not computed."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "There is no geometry. A fragment shader sends one ray per pixel from the camera into the scene and walks it with sphere tracing (raymarching): a distance estimator says how far the surface is at least, and the ray jumps by that amount (times 0.8 as a safety factor), at most 128 steps. Before that, a sphere test (radius 6) limits the ray to the relevant range.",
|
||||
"ITEM_1": "Mandelbulb: 8 iterations in spherical coordinates with power 8 (z → z⁸ + c). The distance is 0.5 · ln(r) · r / dr, where dr is the running derivative.",
|
||||
"ITEM_2": "Mandelbox: 15 iterations of box fold (mirror at ±1), sphere fold (scale by radius) and scale 2.8.",
|
||||
"ITEM_3": "Julia bulb: like Mandelbulb, but with a fixed constant c that moves slightly over time, so the shape moves.",
|
||||
"ITEM_4": "Normals come from four extra distance queries. Lighting: light from the camera direction plus 40 % ambient, no shadows, no ambient occlusion. Color: orbit trap (smallest radius during iteration) mapped to a cosine palette.",
|
||||
"ITEM_5": "Cost per frame: pixels × ray steps × iterations. At full HD with the Mandelbox that is about 4 billion iteration steps in the worst case. It runs as a fragment shader on the GPU only; there is no CPU path."
|
||||
}
|
||||
},
|
||||
"PENDULUM": {
|
||||
@@ -615,12 +661,19 @@
|
||||
"EXPLANATION": {
|
||||
"TITLE": "Chaos Theory: The Double Pendulum",
|
||||
"EXPLANATION": "The double pendulum is one of physics' most famous and fascinating examples of a dynamic system that generates 'deterministic chaos'. It simply consists of a standard pendulum with a second pendulum attached to its lower end. Although the underlying laws of classical mechanics are strictly mathematically defined, the long-term behavior of the double pendulum is absolutely unpredictable. In physics, it is considered the classic showcase object for the so-called butterfly effect.",
|
||||
"DISCLAIMER": "This WebGPU simulation calculates the motion and acceleration equations of the pendulum 60 times per second in real-time. The following characteristics apply:",
|
||||
"DISCLAIMER": "The simulation computes the pendulum's equations of motion once per rendered frame with a fixed time step dt = 0.015, on the GPU (WebGPU) or on the CPU as fallback. The following applies:",
|
||||
"DISCLAIMER_1": "Extreme Sensitivity: The tiniest changes in the initial conditions (e.g., a thousandth of a degree deviation in the starting angle or mass) lead to a completely different, chaotic trajectory after just a short time.",
|
||||
"DISCLAIMER_2": "Deterministic Chaos: The movement may look completely wild and random, but it isn't. If you restart the simulation with the exact same values, the pendulum will follow 100% the same path.",
|
||||
"DISCLAIMER_3": "Numerical Integration: Since computers do not calculate time continuously but in tiny steps (dt), minute mathematical rounding errors occur in every frame. These add up over time and further influence the chaos.",
|
||||
"DISCLAIMER_4": "Energy Conservation & Friction: In a perfect physical system without resistance, the pendulum would swing forever. For a natural look, the algorithm uses an artificial damping factor that simulates air friction and eventually brings the system to a halt.",
|
||||
"DISCLAIMER_BOTTOM": "NOTE: If too many impulses are fed into the system, the simulation becomes unstable. The pendulum will then just hang down and the simulation will have to be restarted."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "The motion follows the equations of motion of the double pendulum (Lagrangian form, formulas as in the Wikipedia article). Each rendered frame computes one step with a fixed time step dt = 0.015: first the angular accelerations a1 and a2, then the angular velocities (times damping, plus impulse), then the angles (semi-implicit Euler).",
|
||||
"ITEM_1": "WebGPU: a compute shader with a single thread computes the physics step; the state (θ1, θ2, v1, v2) stays in a storage buffer on the GPU. A second compute shader (64 threads per workgroup, one per pixel) draws rods, masses and trail directly into a pixel buffer; the trail fades every frame by the trail length factor. A fragment shader displays the buffer.",
|
||||
"ITEM_2": "Without WebGPU: the same physics in TypeScript on the CPU. The trail comes from ping-pong between two render target textures: a GLSL shader reads the old image, fades it and draws the two masses again; a second shader displays trail and rods.",
|
||||
"ITEM_3": "Pixel buffer encoding: intensity 0 to 1 for trail 1, +2 for trail 2, +10 and +20 for the rods. So one float per pixel is enough.",
|
||||
"ITEM_4": "The time step is fixed and not tied to the frame rate: at 120 Hz the pendulum runs twice as fast as at 60 Hz. Euler does not conserve energy exactly; big impulses can make the simulation unstable (see note)."
|
||||
}
|
||||
},
|
||||
"CLOTH": {
|
||||
@@ -640,13 +693,21 @@
|
||||
"DATA_STRUCTURES_EXPLANATION_TITLE": "Data Structures",
|
||||
"CLOTH_SIMULATION_EXPLANATION": "Cloth simulations usually model textiles as a grid of mass points (vertices) held together by invisible connections. The goal is to represent physical influences like gravity, wind, and collisions in real time without the material tearing or stretching unnaturally like rubber.",
|
||||
"XPBD_EXPLANATION": "XPBD (Extended Position-Based Dynamics) is a modern algorithm that manipulates point positions directly to satisfy distance conditions (constraints) instead of calculating accelerations. The 'Extended' means that true physical stiffness is simulated independently of the framerate. Advantage: Absolutely stable, does not explode, and topological changes (like cutting cloth) are trivial. Disadvantage: It is an iterative approximation method and slightly less physically accurate than complex matrix solvers.",
|
||||
"GPU_PARALLELIZATION_EXPLANATION": "To calculate tens of thousands of points in parallel on the graphics card, one must prevent 'race conditions' – i.e., two processing cores shifting the same node at the exact same time. The solution is called 'Independent Sets' (or Graph Coloring): The connections are divided into isolated groups (e.g., 4 phases for a 2D grid) in which not a single point overlaps. This allows the GPU to process each group blindly and at maximum speed.",
|
||||
"GPU_PARALLELIZATION_EXPLANATION": "To calculate tens of thousands of points in parallel on the graphics card, one must prevent 'race conditions' – i.e., two processing cores shifting the same node at the exact same time. The solution is called 'Independent Sets' (or Graph Coloring): The connections are divided into isolated groups (here 8 phases: 2 horizontal, 2 vertical, 4 diagonal) in which not a single point overlaps. This allows the GPU to process each group blindly and at maximum speed.",
|
||||
"DATA_STRUCTURES_EXPLANATION": "For maximum GPU performance, data must be memory-aligned (16-byte alignment). Instead of using many individual variables, information is cleverly packed into blocks of four (vec4). A vertex stores, for example, [X, Y, Z, Inverse_Mass]. If a point has an inverse mass of 0.0, the algorithm ignores it, and it floats motionlessly in the air – an elegant trick for pinning cloth without extra if/then statements.",
|
||||
"DISCLAIMER": "XPBD vs. Mass-Spring Systems: In physical simulations, there are fundamental architectural differences when solving equations:",
|
||||
"DISCLAIMER_1": "Classical Mass-Spring Systems: Here, forces (Hooke's Law) are calculated, leading to accelerations and ultimately new positions. There are two ways to mathematically project these into the future (integration):",
|
||||
"DISCLAIMER_2": "Explicit Solvers (e.g., Forward Euler): These rigidly calculate the next step solely from the current state. They are easy to program but extremely unstable for stiff cloths. Forces can escalate and the simulation 'explodes' unless tiny, very performance-heavy time steps are chosen.",
|
||||
"DISCLAIMER_3": "Implicit Solvers (e.g., Backward Euler): These calculate the next step based on the future state. This is mathematically highly stable but requires solving massive global matrix equation systems in every frame. This is harder to parallelize on the GPU and breaks down if the structure changes (e.g., when the cloth is cut).",
|
||||
"DISCLAIMER_4": "The XPBD Compromise: XPBD completely bypasses this complex matrix problem by acting as a local solver. It combines the absolute stability of an implicit solver with the enormous speed, parallelizability, and dynamic adaptability of an explicit system."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "The cloth is a grid of 100 × 100 = 10,000 particles. Each particle is a vec4 [x, y, z, 1/mass]; the top row has inverse mass 0 and is pinned. 39,402 distance constraints hold the particles together: horizontal, vertical and two diagonals against shearing. Per frame: integration (gravity, wind, position prediction), then 15 substeps in which every constraint is solved once (XPBD), finally the velocity from the position change.",
|
||||
"ITEM_1": "XPBD: per constraint the error C = |pA − pB| − rest length is computed and both particles are moved by λ = −C / (wA + wB + α) along the connection, weighted by inverse mass. α = compliance / dt² makes the stiffness independent of the time step. The elongation slider scales the rest length.",
|
||||
"ITEM_2": "GPU (WebGPU): three compute shaders (integrate, solve, velocity) with 64 threads per workgroup. All data stays in storage buffers on the GPU; the vertex shader reads the positions straight from the buffer. Nothing goes back to the CPU.",
|
||||
"ITEM_3": "Graph coloring: two threads must never write the same particle. So the constraints are sorted into 8 phases (even and odd columns, even and odd rows, two per diagonal). Within one phase no particle has two constraints, so each phase runs without locks. That is 15 · 8 = 120 solver dispatches per frame (plus one each for integrate and velocity).",
|
||||
"ITEM_4": "Without WebGPU: the same three steps in TypeScript on the CPU with the identical memory layout (Float32Array). The positions are copied into the mesh's vertex buffer every frame. 39,402 · 15 = 591,030 constraint solves per frame is a lot for a CPU; the frame rate drops clearly.",
|
||||
"ITEM_5": "Wind: fixed direction (5, 0, 15) plus a sine flutter from position and time. The flutter part also adds a slight lift (y: flutter · 2)."
|
||||
}
|
||||
},
|
||||
"GPU": {
|
||||
@@ -657,7 +718,7 @@
|
||||
"TITLE": "Algorithms",
|
||||
"PATHFINDING": {
|
||||
"TITLE": "Pathfinding",
|
||||
"DESCRIPTION": "Comparing of Dijkstra vs. A*."
|
||||
"DESCRIPTION": "Dijkstra and A* on a grid, alone or side by side."
|
||||
},
|
||||
"SORTING": {
|
||||
"TITLE": "Sorting",
|
||||
@@ -669,7 +730,7 @@
|
||||
},
|
||||
"LABYRINTH": {
|
||||
"TITLE": "Maze Generation",
|
||||
"DESCRIPTION": "Visualizing various maze generation algorithms."
|
||||
"DESCRIPTION": "Maze generation with Prim and Kruskal, then pathfinding inside it."
|
||||
},
|
||||
"FOUR_COLOR": {
|
||||
"TITLE": "Four Color Theorem",
|
||||
@@ -689,15 +750,17 @@
|
||||
},
|
||||
"PENDULUM": {
|
||||
"TITLE": "Double pendulum",
|
||||
"DESCRIPTION": "Visualisation of a chaotic double pendulum simulation with WebGPU."
|
||||
"DESCRIPTION": "Chaotic double pendulum, physics in a WebGPU compute shader (fallback WebGL + CPU)."
|
||||
},
|
||||
"CLOTH": {
|
||||
"TITLE": "Cloth simulation",
|
||||
"DESCRIPTION": "Simulation of cloth with WebGPU."
|
||||
"DESCRIPTION": "Cloth simulation with XPBD on the GPU (WebGPU, fallback WebGL + CPU)."
|
||||
},
|
||||
"NOTE": "Note",
|
||||
"GRID_HEIGHT": "Height",
|
||||
"GRID_WIDTH": "Width"
|
||||
"GRID_WIDTH": "Width",
|
||||
"HOW_IT_WORKS": "How it works",
|
||||
"SOURCE": "Source code"
|
||||
},
|
||||
"FOUR_COLOR": {
|
||||
"TITLE": "Four Color Theorem",
|
||||
@@ -728,6 +791,13 @@
|
||||
},
|
||||
"ALERT": {
|
||||
"NO_SOLUTION": "No solution found (this should not happen for a planar map!)."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "The map is built on a grid: one seed per 30 cells is placed at a random free position. All seeds grow at the same time by breadth-first search (one shared queue) until every cell belongs to a region. Two regions are neighbors when two of their cells share an edge.",
|
||||
"ITEM_1": "Data structure: a list of regions, each with a set of its neighbor ids (adjacency list). The grid stores only region id and color per cell.",
|
||||
"ITEM_2": "Solving: backtracking in region id order. For each region the colors 1 to 4 are tried; if none fits, the solver steps back one region. No heuristic like 'most neighbors first', so on big maps the color attempts are sometimes far above the number of regions.",
|
||||
"ITEM_3": "The default map (25 × 25, 20 regions) solves in under 1 ms. Large maps can take very long, because the backtracking has no heuristic and the worst case is exponential.",
|
||||
"ITEM_4": "Checking: walk all regions and compare each color with its neighbors, O(regions + borders)."
|
||||
}
|
||||
},
|
||||
"FOURIER": {
|
||||
@@ -749,6 +819,14 @@
|
||||
"DISCLAIMER_2": "Each point is a complex number x + iy. A direct discrete Fourier transform gives one circle per frequency.",
|
||||
"DISCLAIMER_3": "The circles are sorted by radius. The slider keeps only the biggest N circles.",
|
||||
"DISCLAIMER_BOTTOM": "Fewer circles give a smoother approximation. With all circles the curve passes exactly through all 256 points."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"INTRO": "The drawn shape is treated as a closed polygon and resampled to 256 points with equal arc-length spacing. Each point is a complex number x + i·y. A direct discrete Fourier transform (two nested loops, O(N²) = 65,536 steps) gives one circle per frequency with radius, start angle and rotation speed. The circles are sorted by radius.",
|
||||
"ITEM_1": "Frequencies run from 0 to N−1; everything above N/2 counts as a negative frequency so circles can also turn backwards.",
|
||||
"ITEM_2": "Each frame adds up the circle centers (O(circles)); the tip of the last circle goes onto the trace. After one full period the trace is cleared.",
|
||||
"ITEM_3": "Drawing uses Canvas 2D with requestAnimationFrame outside the Angular zone. dt is the time between two frames, so the speed does not depend on the frame rate.",
|
||||
"ITEM_4": "Presets: heart and star are formulas, the square is four corner points, the dog is a point list smoothed with three Chaikin passes (corner cutting).",
|
||||
"ITEM_5": "Fewer circles act like a low-pass filter: only the largest radii stay, the shape gets smoother."
|
||||
}
|
||||
},
|
||||
"STOPWATCH": {
|
||||
|
||||
Reference in new issue
Block a user