summaryrefslogtreecommitdiff
path: root/dvd-logo/js/script.js
blob: 3934c548c480959752ac56320397c88725a38b29 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
const global_state = {
  iterations: 0
};

const ALLOWED_ERROR = 0.01;

const randUpTo = (max) => Math.random() * max

const withinError = (a, b, error=ALLOWED_ERROR) => Math.abs(a - b) < error;

const normalize = (vec) => {
  const magnitude = Math.sqrt(vec.x * vec.x + vec.y * vec.y);
  return {
    x: vec.x / magnitude,
    y: vec.y / magnitude
  };
}

const findNextIntersection = ({x, y, dx, dy}, max_width, max_height) => {
  const slope = dy / dx;
  const y_intercept = y - (slope * x);
  const intersections = [
    {x: 0, y: y_intercept},
    {x: max_width, y: slope * max_width + y_intercept},
    {x: -y_intercept / slope, y: 0},
    {x: (max_height - y_intercept) / slope, y: max_height}
  ];
  return intersections.find((cross) => {
    const cross_dir_hat = normalize({x: cross.x - x, y: cross.y - y});
    const logo_dir_hat = normalize({x: dx, y: dy});
    const in_bounds = cross.x >= 0 && cross.x <= max_width && cross.y >= 0 && cross.y <= max_height;
    return in_bounds && withinError(cross_dir_hat.x, logo_dir_hat.x) && withinError(cross_dir_hat.y, logo_dir_hat.y);
  });
};

const handleCollision = (obj, max_width, max_height) => {
  const collide_y = obj.y <= 0 || obj.y >= max_height;
  const collide_x = obj.x <= 0 || obj.x >= max_width;

  obj.dx *= (collide_x ? -1 : 1);
  obj.dy *= (collide_y ? -1 : 1);

  return {collide_y, collide_x};
}

const updateLogo = (logo, max_width, max_height) => {
  const {collide_x, collide_y} = handleCollision(logo, max_width, max_height);
  if (collide_y || collide_x) {
    logo.dColorWheel = randUpTo(2*Math.PI)
    logo.onIntersection(logo, max_width, max_height);
  }

  logo.x = Math.max(0, Math.min(logo.x + logo.dx, max_width));
  logo.y = Math.max(0, Math.min(logo.y + logo.dy, max_height));
};

const drawLogo = (logo, element) => {
  element.style.left = `${logo.x}px`;
  element.style.top = `${logo.y}px`;
  element.style.filter = `brightness(0.5) sepia(1) saturate(10000%) hue-rotate(${logo.dColorWheel}rad)`;
}

const makeLine = (a, b, color='white') => `<line x1="${a.x}" x2="${b.x}" y1="${a.y}" y2="${b.y}" stroke="${color}" />`;
const makeSvg = (coords) => `
    <svg xmlns='http://www.w3.org/2000/svg' width='${window.innerWidth}px' height='${window.innerHeight}px'>
      ${coords.map((_,i) => {
        if (i == 0) {
          return;
        }
        return makeLine(coords[i-1], coords[i], 'red');
      }).join('')}
    </svg>
  `;

const setBackgroundToSvg = (svg) => {
  document.getElementById("container").style.background = 'url(data:image/svg+xml;base64,'+btoa(svg)+')';
}

const makeListOfFutureCollisionsAndDraw = (logo, max_width, max_height) => {
  let next_int = {x: logo.x, y: logo.y, dx: logo.dx, dy: logo.dy};
  let intersections = [{...next_int}];

  for (let i = 0; i < global_state.iterations; i++) {
    const next = findNextIntersection(next_int, max_width, max_height);
    next_int.x = next.x;
    next_int.y = next.y;
    handleCollision(next_int, max_width, max_height);
    intersections.push(next);
  }

  // Transform each intersection to the center of the logo
  intersections.map((x) => {
    x.x += logo.width/2;
    x.y += logo.height/2;
  });

  setBackgroundToSvg(makeSvg(intersections));
};

let drawNewPaths;
window.onload = () => {
  const dvdLogo = document.getElementById("dvd-logo");
  let logo = {
    x: Math.floor(randUpTo(window.innerWidth-dvdLogo.clientWidth)),
    y: Math.floor(randUpTo(window.innerHeight-dvdLogo.clientHeight)),
    dx: 1.2,
    dy: 1.2,
    width: dvdLogo.clientWidth,
    height: dvdLogo.clientHeight,
    onIntersection: makeListOfFutureCollisionsAndDraw,
    dColorWheel: 0
  };
  setInterval(() => {
    updateLogo(logo, window.innerWidth-logo.width, window.innerHeight-logo.height);
    drawLogo(logo, dvdLogo);
  }, 12);

  drawNewPaths = () => makeListOfFutureCollisionsAndDraw(logo, window.innerWidth-logo.width, window.innerHeight-logo.height);
  drawNewPaths();
  window.onresize = drawNewPaths;

  document.getElementById("iteration-slider").value = global_state.iterations;
  document.getElementById("iteration-count").innerHTML = `${global_state.iterations} iterations`;
}

document.getElementById("iteration-slider").oninput = function() {
  global_state.iterations = parseInt(this.value, 10);
  document.getElementById("iteration-count").innerHTML = `${global_state.iterations} iterations`;
  drawNewPaths();
}