diff --git a/README.md b/README.md index b449399..8817be6 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ The purpose of this project is to show the beauty of math with python. It consis + [Reaction diffusion simulation with pyglet and glsl](./src/grayscott). + [Raymarching fractals with pyglet and glsl](./src/fractal3d). + [Raymarching Möbius transformation animations with pyglet and glsl](./src/mobius). ++ [Limit set of rank 4 hyperbolic Coxeter groups](./src/hyperbolic-limit-set) + [Miscellaneous scripts](./src/misc) like E8 root system, Mandelbrot set, Newton's fractal, Lorenz attractor, etc. These topics are chosen largely due to my personal taste: @@ -51,12 +52,6 @@ I'll use only popular python libs and build all math stuff by hand (tools like ` **Note**: Python3.5 is deprecated now because it's a bit tricky to install the latest numba on Ubuntu16.04 for python3.5 (if you are using `anaconda` for package management then you need not worry about this because anaconda will fix it for you). Note `numba` is only used in a few fractal scripts in the `misc` directory and all other projects should also work for python>=2.7. -A few examples: - -
- -
- ## How to use All projects here are implemented in a ready-to-use manner for new comers. You can simply run the examples without tweaking any parameters once you have the dependencies installed correctly. Each subdirectory in `src/` is a single program (except that `glslhelpers` is a helper module for running glsl programs and `misc` is a collection of independent scripts), any file named `main.py`, `run_*.py`, `example_*.py` is an executable script that gives some output. diff --git a/gallery.png b/gallery.png deleted file mode 100644 index 6f00d6e..0000000 Binary files a/gallery.png and /dev/null differ diff --git a/src/glslhelpers/textures/rusty_metal.jpg b/src/glslhelpers/textures/rusty_metal.jpg new file mode 100644 index 0000000..7486252 Binary files /dev/null and b/src/glslhelpers/textures/rusty_metal.jpg differ diff --git a/src/hyperbolic-limit-set/glsl/main.frag b/src/hyperbolic-limit-set/glsl/main.frag new file mode 100644 index 0000000..f2f88ae --- /dev/null +++ b/src/hyperbolic-limit-set/glsl/main.frag @@ -0,0 +1,418 @@ +#version 130 +/* +============================================= + +Limit set of rank 4 hyperbolic Coxeter groups + + by Zhao Liang +============================================= + +This program shows the limit set of rank 4 hyperbolic Coxeter groups. +Dont't be scared by the title, it's a rather simple program, the scene +contains only one sphere and one plane :P. + +Some math stuff: + +Let G be a hyperbolic Coxeter group and x a point inside the hyperbolic +unit ball, the orbit S_x = { gx, g \in G } has accumulation points +(under Euclidean metric) only on the boundary of the space. We call the +accumulation points of S_x the limit set of the group, it can be proved that +this set is independent of the way x is chosen, and it's the smallest +closed subset of the boundary that is invariant under the action of the group. + +The Coxeter-Dynkin diagram of a rank 4 Coxeter group of string type has the form + + A --- B --- C --- D + p q r + +Here A, B, D can be chosen as ususal Euclidean planes, C is a sphere orthongonal +to the unit ball. This is taken from mla's notation, and as far as I know this +has long been used by users on fractalforums. (fragmentarium) + +In this animation these points are colored in "brass metal". + +========== +!important +========== + +The limit set is a closed set with no interior points, to show them we have +to use an approximate procedure: we simply try to reflect a point p on the +boundary to the fundamental domain up to a maximum steps, once failed then we +think p belongs to the limit set. + +**So the number MAX_REFLECTIONS is an important param**, if' its set to a high +threshold then little limit set will be shown, or if it's not high enough then +the boundary of the set will look too coarse, so beware of this. + +As always, you can do whatever you want to this work. + +Update: thanks @mla for helping fix some bugs! +*/ +// ------------------------------------------ + +// want to add some shiny texture effect to the limit set? +#define TEXBUMP 0.007 + +// -------------------------- +// You can try more patterns like +// (3, 7, 3), (4, 6, 3), (4, 4, 5), (5, 4, 4), (7, 3, 4), ..., etc. (5, 4, 4) is now +// my favorite! set PQR below to see the result. +// For large PQRs the limit set will become too small to be visible, you need to adjust +// MAX_REFLECTIONS and tweak with the function chooseColor to get appealling results. + +uniform vec4 iMouse; +uniform float iTime; +uniform vec3 iResolution; +uniform vec3 PQR; +uniform sampler2D iChannel0; + +out vec4 finalColor; + +// -------------------------- +// some global settings + +#define MAX_TRACE_STEPS 100 +#define MIN_TRACE_DIST 0.1 +#define MAX_TRACE_DIST 100.0 +#define PRECISION 0.0001 +#define AA 3 +#define MAX_REFLECTIONS 500 +#define PI 3.141592653 + +// another pattern +#define CHECKER1 vec3(0.196078, 0.33, 0.82) +#define CHECKER2 vec3(0.75, 0.35, 0.196078) + +//#define CHECKER1 vec3(0.82, 0.196078, 0.33) +//#define CHECKER2 vec3(0.196078, 0.35, 0.92) +#define MATERIAL vec3(0.71, 0.65, 0.26) +#define FUNDCOL vec3(0., 0.82, .33) + +// used to highlight the limit set +#define LighteningFactor 8. +// -------------------------- + +vec3 A, B, D; +vec4 C; +float orb; + +// minimal distance to the four mirrors +float distABCD(vec3 p) +{ + float dA = abs(dot(p, A)); + float dB = abs(dot(p, B)); + float dD = abs(dot(p, D)); + float dC = abs(length(p - C.xyz) - C.w); + return min(dA, min(dB, min(dC, dD))); +} + +// try to reflect across a plane with normal n and update the counter +bool try_reflect(inout vec3 p, vec3 n, inout int count) +{ + float k = dot(p, n); + // if we are already inside, do nothing and return true + if (k >= 0.0) + return true; + + p -= 2.0 * k * n; + count += 1; + return false; +} + +// similar with above, instead this is a sphere inversion +bool try_reflect(inout vec3 p, vec4 sphere, inout int count) +{ + vec3 cen = sphere.xyz; + float r = sphere.w; + vec3 q = p - cen; + float d2 = dot(q, q); + if (d2 == 0.0) + return true; + float k = (r * r) / d2; + if (k < 1.0) + return true; + p = k * q + cen; + count += 1; + orb *= k; + return false; +} + +// sdf of the unit sphere at origin +float sdSphere(vec3 p, float radius) { return length(p) - 1.0; } + +// sdf of the plane y=-1 +float sdPlane(vec3 p, float offset) { return p.y + 1.0; } + +// inverse stereo-graphic projection, from a point on plane y=-1 to +// the unit ball centered at the origin +vec3 planeToSphere(vec2 p) +{ + float pp = dot(p, p); + return vec3(2.0 * p, pp - 1.0).xzy / (1.0 + pp); +} + +// iteratively reflect a point on the unit sphere into the fundamental cell +// and update the counter along the way +bool iterateSpherePoint(inout vec3 p, inout int count) +{ + bool inA, inB, inC, inD; + for(int iter=0; iter