Adapted from strange attractors found at the blog of the Vilnius University Faculty of Physics Institute of Theoretical Physics and Astronomy: rf.mokslasplius.lt/
Not all generated attractors will be strange; some uninteresting or trivial attractors may make it through the filters. If you want to generate random strange attractors, toggle to 'random coefficients', and then arbitrarly move one of the coefficient sliders around to get it to regenerate until you see something interesting. The coefficient values are printed to console if you'd like to input those values yourself and play around with the found configuration (check the inspect element console).
Log in to post a comment.
// Coefficients of the program.
const randomize_inputs = 1; // min=0, max=1, step=1 (Randomize Coefficients, Use Input Coefficients)
const dropped_points = 500; // min=0, max=1000, step=10
const steps = 5000; // min=1000, max=100000, step=100
const a1 = -0.26; // min=-2.0, max=2.0, step=0.001
const a2 = 0.35; // min=-2.0, max=2.0, step=0.001
const a3 = 0.92; // min=-2.0, max=2.0, step=0.001
const a4 = -0.88; // min=-2.0, max=2.0, step=0.001
const a5 = -0.6; // min=-2.0, max=2.0, step=0.001
const a6 = 0.91; // min=-2.0, max=2.0, step=0.001
const b1 = 0.85; // min=-2.0, max=2.0, step=0.01
const b2 = -0.47; // min=-2.0, max=2.0, step=0.01
const b3 = -0.02; // min=-2.0, max=2.0, step=0.01
const b4 = 0.49; // min=-2.0, max=2.0, step=0.01
const b5 = -0.25; // min=-2.0, max=2.0, step=0.01
const b6 = -0.7; // min=-2.0, max=2.0, step=0.01
// Set pen-related draw stuff
const scale = 40; // min=0.1, max=100, step=0.1
const x_offset = 0; // min=-1000, max=1000, step=10
const y_offset = 0; // min=-1000, max=1000, step=10
const opacity=0.5; // min=-1.0, max=1.0, step=0.01
let a = [a1, a2, a3, a4, a5, a6];
let b = [b1, b2, b3, b4, b5, b6];
// Function to hash 2d coordinates. Basically concatenates coordinates in order into string and hashes string (sub-optimal).
function hash_2d(x,y) {
var str=Math.floor(x*1e6)+" "+Math.floor(y*1e6);
var hash = 0;
var chr;
if (str.length == 0) {
return hash;
}
for (var i = 0; i < str.length; i+=1) {
chr = str.charCodeAt(i);
hash = ((hash<<5)-hash)+chr;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
// Compute next x coordinate from current pos
function fx(x,y) {
let res = (a[0]*x+a[1]*y+a[2])*x+(a[3]*y+a[4])*y+a[5];
return res;
}
// Compute next y coordinate from current pos
function fy(x,y) {
let res = (b[0]*x+b[1]*y+b[2])*x+(b[3]*y+b[4])*y+b[5];
return res;
}
// If randomize is enabled, generate brand new points and ignore inputs; regenerate until we have a success.
let attempts = 0; // Track attempts so we don't just infinitely loop here (shouldn't happen? Like statistically I mean..)
let dropped_point_hashtable = new Map();
let attractor_trivial = false;
let xn = 0;
let yn = 0;
do {
attempts += 1;
dropped_point_hashtable.clear();
attractor_trivial = false; // Give it a fresh start
if (randomize_inputs == 0) {
for (let i = 0; i < 6; i++) {
a[i] = 2 * Math.random() - 1;
b[i] = 2 * Math.random() - 1;
}
}
xn = a[5];
yn = b[5];
dropped_point_hashtable.set(hash_2d(xn, yn), 1)
for (let i = 0; i < dropped_points; i++) {
xn_1 = fx(xn, yn);
yn_1 = fy(xn, yn);
// Reject any turtle which blows up or which repeats in the drop window
let next_point_hash = hash_2d(xn_1, yn_1);
if (dropped_point_hashtable.has(next_point_hash)) {
console.log("Trivial Hash Hit Rejection")
dropped_point_hashtable[next_point_hash] += 1;
attractor_trivial = true;
break;
} else if (xn_1 * xn_1 + yn_1 * yn_1 > 1000000) {
attractor_trivial = true;
break;
} else {
dropped_point_hashtable.set(next_point_hash, 1);
xn = xn_1;
yn = yn_1;
}
}
} while (attempts < 1000 && randomize_inputs == 0 && attractor_trivial);
// Now we actually have a valid strange attractor (or not), prepare to draw
Canvas.setpenopacity(opacity);
const turtle = new Turtle();
turtle.penup();
turtle.pendown();
turtle.jump(xn,yn);
if (attractor_trivial) {
// This is how I'm signifying that the coefficient choices were bad. *shrug*
turtle.jump(-2 * Math.sqrt(scale) + x_offset, y_offset);
turtle.circle(4 * scale);
console.log("Attractor is trivial, showing you this pretty circle instead (try again?): " + attempts + " attempt(s)");
} else if (randomize_inputs == 1) {
console.log("Attractor is strange, coefficients follow: " + attempts + " attempt(s)");
// Output for later use, especially if randomizing
console.log(a);
console.log(b);
}
// The walk function will be called until it returns false.
function walk(i) {
let xn_1 = fx(xn, yn);
let yn_1 = fy(xn, yn);
xn = xn_1;
yn = yn_1;
turtle.jump(xn * scale + x_offset, yn * scale + y_offset);
turtle.forward(1);
return !attractor_trivial && i < steps;
}