### Making waves

Ray marching with warped infinite repetition

```// reusing some code from my first ray marching
// https://turtletoy.net/turtle/dbb1b8a695

Canvas.setpenopacity(-0.3);

const t = new Turtle();

const scrZ = 4.0;
const scrR = 1.0;

const eyeZ = 8.0;

const camRotX = -65 * Math.PI/180;
const camRotY = 45 * Math.PI/180;

const step = 0.25;

const Dmax = scrZ + 120;

const EPS = 0.0001;

const MAX_N_STEPS = 256;

function plus(v1, v2) {
return [v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]];
}

function minus(v1, v2) {
return [v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]];
}

function mod(v1, v2) {
return [v1[0] % v2[0], v1[1] % v2[1], v1[2] % v2[2]];
}

function multiply(a, v) {
return [a * v[0], a * v[1], a * v[2]];
}

function abs(v) {
return [Math.abs(v[0]), Math.abs(v[1]), Math.abs(v[2])];
}

function length(v) {
return Math.sqrt(v[0]**2 + v[1]**2 + v[2]**2);
}

function normalize(v) {
return multiply(1 / length(v), v);
}

function rotate(v, phi) {
return [v[0] * Math.cos(phi) - v[1] * Math.sin(phi),
v[0] * Math.sin(phi) + v[1] * Math.cos(phi)];
}

function cast_ray_from_screen(x, y) {
let px = -scrR + 2 * scrR * (x / 200);
let py = -scrR + 2 * scrR * (y / 200);
let pz = scrZ;

[px, py] = rotate([px, py], camRotY);
[py, pz] = rotate([py, pz], camRotX);

let ex = 0, ey = 0, ez = eyeZ;

//[ex, ey] = rotate([ex, ey], camRotY)
[ey, ez] = rotate([ey, ez], camRotX)

let p = [px, py, pz];
let eye = [ex, ey, ez];

let dir = normalize(minus(p, eye));

return [p, dir];
}

function intersect_df(p, dir, df) {
let D = 0;
var n_steps;

for (n_steps = 0; n_steps < MAX_N_STEPS; n_steps++) {
let d = scene_df(p);

if ((d < EPS) || (D > Dmax)) {
break;
}
else {
p = plus(p, multiply(d, dir));

D += d;
}
}

return [p, D, n_steps];
}

function cylinder_df(p, c, h, r) { // axis z
const [px, py, pz] = minus(p, c);

let dxy = Math.sqrt(px**2 + py**2) - r;
let dz = Math.abs(pz) - h/2;

if (dxy > 0) {
if (dz > 0) {
return Math.sqrt(dxy**2 + dz**2);
}
else {
return dxy;
}
}
else {
if (dz > 0) {
return dz;
}
else {
return Math.max(dxy, dz);
}
}
}

function scene_df(p) {
const r = 0.075;
const h = 2.5;

const periods = [3.1 * r, 3.1 * r, 100.0];
const half_periods = multiply(0.5, periods);

let d = 0.75 * r;
let phi = 2 * Math.PI * Math.floor((Math.abs(p[1]) + periods[1]/2) / periods[1]) / 10;
let c = [d * Math.cos(phi), 0, d * Math.sin(phi)];

// based on infinite repeat from https://iquilezles.org/articles/distfunctions/
p = abs(p);
p = minus(mod(plus(p, half_periods), periods), half_periods);

let df = cylinder_df(p, c, h, r);

return df;
}

let x = 0;
let y = 0;

function walk(frame) {
const [p, dir] = cast_ray_from_screen(x, y);

const [p_int, dist_to_int, n_steps] = intersect_df(p, dir, scene_df);

if (dist_to_int < Dmax) {
let s = step * Math.max(0.1, (1 - n_steps / 50));

t.seth(360 * Math.random());

for (let i = 0; i < 4; i++) {
t.jump(x - 100, y - 100);
t.right(90);
t.forward(s);
}
}

x += step;
if (x > 200) {
y += step;
x = 0;
}

return (y < 200);
}```