Pixel.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. const dotProduct = function(U, V) {
  2. return U.x * V.x + U.y * V.y;
  3. };
  4. const isInside = function(vertex, square) {
  5. const AB = { x: square.b.x - square.a.x, y: square.b.y - square.a.y };
  6. const AM = { x: vertex.x - square.a.x, y: vertex.y - square.a.y };
  7. const BC = { x: square.c.x - square.b.x, y: square.c.y - square.b.y };
  8. const BM = { x: vertex.x - square.b.x, y: vertex.y - square.b.y };
  9. return (0 <= dotProduct(AB, AM) && dotProduct(AB, AM) <= dotProduct(AB, AB))
  10. && (0 <= dotProduct(BC, BM) && dotProduct(BC, BM) <= dotProduct(BC, BC));
  11. };
  12. const isInsideSquare = function(vertex, square, isize) {
  13. return isInside(vertex, square)
  14. || isInside({x: vertex.x + isize.width, y: vertex.y}, square)
  15. || isInside({x: vertex.x, y: vertex.y + isize.height}, square)
  16. || isInside({x: vertex.x + isize.width, y: vertex.y + isize.height}, square);
  17. };
  18. const toBinary = function(decimal, padding) {
  19. return decimal.toString(2).padStart(padding, 0);
  20. };
  21. const getId = function (source) {
  22. return /[01]+$/.exec(source)[0];
  23. };
  24. const canvas = {
  25. get boundary() { return document.getElementById("canvas").getBoundingClientRect() },
  26. vertices: {
  27. get a() { return { x: 0, y: 0 }; },
  28. get b() { return { x: 0, y: canvas.boundary.width }; },
  29. get c() { return { x: canvas.boundary.height, y: canvas.boundary.width }; },
  30. get d() { return { x: canvas.boundary.height, y: 0 }; }
  31. },
  32. url: "http://127.0.0.1:5000/image/",
  33. width: 0,
  34. height: 0,
  35. images: [],
  36. level: 0
  37. };
  38. const mouse = {
  39. x: 0,
  40. y: 0,
  41. prevX: 0,
  42. prevY: 0
  43. };
  44. const draw = function(ctx, csize, isize, url, level, images, coords) {
  45. ctx.clearRect(0, 0, csize.width, csize.height);
  46. const seedImage = images[Math.floor((images.length - 1) / 2)] || {image: {src: "0".repeat((level + 1) * 2)}};
  47. const seedCoord = images[Math.floor((images.length - 1) / 2)]
  48. ? {x : seedImage.x + coords.x - coords.prevX, y: seedImage.y + coords.y - coords.prevY} : coords;
  49. return draw_(ctx, isize, url, level, images.map((i) => {
  50. return Object.assign(i, {drawn: false});
  51. }), getId(seedImage.image.src), seedCoord);
  52. }
  53. const draw_ = function(ctx, isize, url, level, images, id, coords) {
  54. if (level != canvas.level) {
  55. return images;
  56. }
  57. const decimal = parseInt(id, 2);
  58. const index = images.findIndex((i) => (url + id) == i.image.src);
  59. if (!isInsideSquare(coords, canvas.vertices, isize)) {
  60. if (index != -1) {
  61. images.splice(index);
  62. }
  63. return images;
  64. } else {
  65. const target = images[index];
  66. if (target) {
  67. if (target.drawn) {
  68. return images;
  69. } else {
  70. if (target.image.naturalWidth) {
  71. ctx.drawImage(target.image, coords.x, coords.y);
  72. } else {
  73. target.image.onload = () => ctx.drawImage(target.image, coords.x, coords.y);
  74. }
  75. images.push({
  76. image: target.image,
  77. x: coords.x,
  78. y: coords.y,
  79. drawn: true
  80. });
  81. images.splice(index);
  82. }
  83. } else {
  84. const image = new Image();
  85. image.src = url + id;
  86. image.onload = () => ctx.drawImage(image, coords.x, coords.y);
  87. images.push({
  88. image: image,
  89. x: coords.x,
  90. y: coords.y,
  91. drawn: true
  92. });
  93. }
  94. switch (decimal % 4) {
  95. case 0:
  96. if (decimal >= 2 ** (level + 1) * 2)
  97. images = draw_(ctx, isize, url, level, images, toBinary(decimal - 2 ** (level + 1) * 2 + 2, id.length), { x: coords.x, y: coords.y - isize.height });
  98. images = draw_(ctx, isize, url, level, images, toBinary(decimal + 1, id.length), { x: coords.x + isize.width, y: coords.y });
  99. images = draw_(ctx, isize, url, level, images, toBinary(decimal + 2, id.length), { x: coords.x, y: coords.y + isize.height });
  100. if (![0, 2].includes(decimal % (2 ** (level + 1) * 2)))
  101. images = draw_(ctx, isize, url, level, images, toBinary(decimal - 3, id.length), { x: coords.x - isize.width, y: coords.y });
  102. break;
  103. case 1:
  104. if (decimal >= 2 ** (level + 1) * 2)
  105. images = draw_(ctx, isize, url, level, images, toBinary(decimal - 2 ** (level + 1) * 2 + 2, id.length), { x: coords.x, y: coords.y - isize.height });
  106. if (![0, 2].includes((decimal + 3) % (2 ** (level + 1) * 2)))
  107. images = draw_(ctx, isize, url, level, images, toBinary(decimal + 3, id.length), { x: coords.x + isize.width, y: coords.y });
  108. images = draw_(ctx, isize, url, level, images, toBinary(decimal + 2, id.length), { x: coords.x, y: coords.y + isize.height });
  109. images = draw_(ctx, isize, url, level, images, toBinary(decimal - 1, id.length), { x: coords.x - isize.width, y: coords.y });
  110. break;
  111. case 2:
  112. images = draw_(ctx, isize, url, level, images, toBinary(decimal - 2, id.length), { x: coords.x, y: coords.y - isize.height });
  113. images = draw_(ctx, isize, url, level, images, toBinary(decimal + 1, id.length), { x: coords.x + isize.width, y: coords.y });
  114. if (decimal < 4 ** (level + 1) - 2 ** (level + 1) * 2)
  115. images = draw_(ctx, isize, url, level, images, toBinary(decimal + 2 ** (level + 1) * 2 - 2, id.length), { x: coords.x, y: coords.y + isize.height });
  116. if (![0, 2].includes(decimal % (2 ** (level + 1) * 2)))
  117. images = draw_(ctx, isize, url, level, images, toBinary(decimal - 3, id.length), { x: coords.x - isize.width, y: coords.y });
  118. break;
  119. case 3:
  120. images = draw_(ctx, isize, url, level, images, toBinary(decimal - 2, id.length), { x: coords.x, y: coords.y - isize.height });
  121. if (![0, 2].includes((decimal + 3) % (2 ** (level + 1) * 2)))
  122. images = draw_(ctx, isize, url, level, images, toBinary(decimal + 3, id.length), { x: coords.x + isize.width, y: coords.y });
  123. if (decimal < 4 ** (level + 1) - 2 ** (level + 1) * 2)
  124. images = draw_(ctx, isize, url, level, images, toBinary(decimal + 2 ** (level + 1) * 2 - 2, id.length), { x: coords.x, y: coords.y + isize.height });
  125. images = draw_(ctx, isize, url, level, images, toBinary(decimal - 1, id.length), { x: coords.x - isize.width, y: coords.y });
  126. break;
  127. }
  128. return images;
  129. }
  130. };
  131. const init = function(c) {
  132. const image = new Image();
  133. image.src = canvas.url + "0".repeat((canvas.level + 1) * 2)
  134. image.onload = () => {
  135. canvas.width = image.width;
  136. canvas.height = image.height;
  137. canvas.images = draw(c.getContext("2d"), canvas.boundary, canvas,
  138. canvas.url, canvas.level, canvas.images, {x: 0, y: 0});
  139. }
  140. }
  141. const main = function() {
  142. const c = document.getElementById("canvas");
  143. c.setAttribute("width", window.getComputedStyle(document.body).getPropertyValue("width"));
  144. c.setAttribute("height", window.getComputedStyle(document.body).getPropertyValue("height"));
  145. document.addEventListener("mousedown", (e) => {
  146. if (c === document.activeElement && e.button === 0) {
  147. mouse.prevX = e.clientX;
  148. mouse.prevY = e.clientY;
  149. }
  150. });
  151. document.addEventListener("mouseup", (e) => {
  152. if (c === document.activeElement && e.button === 0) {
  153. mouse.x = e.clientX;
  154. mouse.y = e.clientY;
  155. canvas.images = draw(c.getContext("2d"), canvas.boundary, canvas,
  156. canvas.url, canvas.level, canvas.images, mouse);
  157. }
  158. });
  159. document.addEventListener("wheel", (e) => {
  160. if (c === document.activeElement) {
  161. if (e.deltaY > 0) {
  162. canvas.level += 1;
  163. } else {
  164. if (canvas.level > 0) {
  165. canvas.level += -1;
  166. }
  167. }
  168. document.getElementById("level").innerText = canvas.level;
  169. init(c);
  170. }
  171. });
  172. init(c);
  173. };