Pixel.js 6.8 KB

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