not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
9.9 KiB

  1. define(['activity/data-model', 'webL10n'], function(DataModel, l10n) {
  2. 'use strict';
  3. var canvas = document.querySelector('canvas'),
  4. ctx = canvas.getContext('2d'),
  5. moon = document.querySelector('img#moon');
  6. var _ = l10n.get;
  7. var IMAGE_SIZE, HALF_SIZE;
  8. if (!ctx.ellipse) {
  9. /*
  10. CanvasRenderingContext2D.ellipse() is esperimental at the time of writing
  11. Provide pollyfill
  12. */
  13. ctx.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {
  14. /* for this project, we do not need: rotation, startAngle, endAngle, anticlockwise */
  15. x -= radiusX;
  16. y -= radiusY;
  17. radiusX *= 2;
  18. radiusY *= 2;
  19. var kappa = 0.5522848,
  20. ox = (radiusX / 2) * kappa, // control point offset horizontal
  21. oy = (radiusY / 2) * kappa, // control point offset vertical
  22. xe = x + radiusX, // x-end
  23. ye = y + radiusY, // y-end
  24. xm = x + radiusX / 2, // x-middle
  25. ym = y + radiusY / 2; // y-middle
  26. if (startAngle === Math.PI / 2 && endAngle === 3 * Math.PI / 2) {
  27. ctx.moveTo(xm, ye);
  28. ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); /* 2nd quarter */
  29. ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); /* 3rd quarter */
  30. } else if (startAngle === 3 * Math.PI / 2 && endAngle === Math.PI / 2) {
  31. ctx.moveTo(xm, y);
  32. ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); /* 4th quarter */
  33. ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); /* 1st quarter */
  34. } else if (startAngle === 0 && endAngle === 2 * Math.PI) {
  35. ctx.moveTo(xe, ym);
  36. ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); /* 1st quarter */
  37. ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); /* 2nd quarter */
  38. ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); /* 3rd quarter */
  39. ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); /* 4th quarter */
  40. }
  41. };
  42. }
  43. function drawMoon() {
  44. /*
  45. Draw mask corresponding to either shaded region or lit region
  46. */
  47. var phase_shadow_adjust = null;
  48. var arc_scale = null;
  49. ctx.strokeStyle = 'black';
  50. ctx.fillStyle = 'black';
  51. ctx.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE);
  52. if (DataModel.phase_of_moon < 0.25) {
  53. phase_shadow_adjust = DataModel.phase_of_moon - Math.abs(Math.sin(DataModel.phase_of_moon * Math.PI * 4) / 18.0);
  54. arc_scale = 1 - (4 * phase_shadow_adjust);
  55. ctx.fillStyle = 'white';
  56. ctx.fillRect(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
  57. ctx.fillStyle = 'black';
  58. drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, 3 * Math.PI / 2, Math.PI / 2);
  59. ctx.fill();
  60. } else if (DataModel.phase_of_moon < 0.50) {
  61. phase_shadow_adjust = DataModel.phase_of_moon + Math.abs(Math.sin(DataModel.phase_of_moon * Math.PI * 4) / 18.0);
  62. arc_scale = 4 * (phase_shadow_adjust - 0.25);
  63. ctx.fillStyle = 'white';
  64. ctx.fillRect(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
  65. ctx.fillStyle = 'white';
  66. drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, Math.PI / 2, 3 * Math.PI / 2);
  67. ctx.fill();
  68. } else if (DataModel.phase_of_moon < 0.75) {
  69. phase_shadow_adjust = DataModel.phase_of_moon - Math.abs(Math.sin(DataModel.phase_of_moon * Math.PI * 4) / 18.0);
  70. arc_scale = 1 - (4 * (phase_shadow_adjust - 0.5));
  71. ctx.fillStyle = 'white';
  72. ctx.fillRect(0, 0, HALF_SIZE, IMAGE_SIZE);
  73. ctx.fillStyle = 'white';
  74. drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, 3 * Math.PI / 2, Math.PI / 2);
  75. ctx.fill();
  76. } else {
  77. phase_shadow_adjust = DataModel.phase_of_moon + Math.abs(Math.sin(DataModel.phase_of_moon * Math.PI * 4) / 18.0);
  78. arc_scale = 4 * (phase_shadow_adjust - 0.75);
  79. ctx.fillStyle = 'white';
  80. ctx.fillRect(0, 0, HALF_SIZE, IMAGE_SIZE);
  81. ctx.fillStyle = 'black';
  82. drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, Math.PI / 2, 3 * Math.PI / 2);
  83. ctx.fill();
  84. }
  85. ctx.save();
  86. ctx.globalCompositeOperation = 'multiply';
  87. ctx.drawImage(moon, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
  88. ctx.globalAlpha = 0.5;
  89. ctx.globalCompositeOperation = 'source-over';
  90. ctx.drawImage(moon, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
  91. ctx.restore();
  92. drawEclipse();
  93. }
  94. function drawEclipse() {
  95. if (
  96. (
  97. DataModel.next_lunar_eclipse_sec !== -1 ||
  98. DataModel.last_lunar_eclipse_sec <= 7200
  99. ) && (
  100. DataModel.next_lunar_eclipse_sec <= 7200 ||
  101. DataModel.last_lunar_eclipse_sec !== -1
  102. ) && (
  103. Math.min(DataModel.next_lunar_eclipse_sec, DataModel.last_lunar_eclipse_sec) <= 7200
  104. )
  105. ) {
  106. var eclipse_alpha;
  107. if (DataModel.next_lunar_eclipse_sec == -1) {
  108. eclipse_alpha = DataModel.last_lunar_eclipse_sec / 7200;
  109. }
  110. else if (DataModel.last_lunar_eclipse_sec == -1) {
  111. eclipse_alpha = DataModel.next_lunar_eclipse_sec / 7200;
  112. }
  113. else {
  114. eclipse_alpha = Math.min(DataModel.next_lunar_eclipse_sec, DataModel.last_lunar_eclipse_sec) / 7200;
  115. }
  116. ctx.save();
  117. ctx.globalAlpha = 0.25 * (1 - eclipse_alpha);
  118. ctx.globalCompositeOperation = 'multiply';
  119. ctx.fillStyle = 'red';
  120. ctx.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE);
  121. ctx.restore();
  122. }
  123. }
  124. function drawGrid(compass_text) {
  125. /*
  126. Draw longitudes at 0, +/-30 and +/-60 degrees
  127. Draw latitudes at 0, +/-30 and +/-60 degrees
  128. Draw compass
  129. */
  130. var needleLength = 0.08 * IMAGE_SIZE;
  131. ctx.font = '16px Sans';
  132. ctx.lineWidth = 3;
  133. /* Latitude Labels */
  134. ctx.fillStyle = 'blue';
  135. drawLabel(HALF_SIZE + 1, HALF_SIZE, 26, 22, '0\xB0');
  136. drawLabel(HALF_SIZE + 1, HALF_SIZE * 0.50, 36, 22, '30\xB0');
  137. drawLabel(HALF_SIZE + 1, HALF_SIZE * 1.5, 36, 22, '30\xB0');
  138. drawLabel(HALF_SIZE + 1, HALF_SIZE * 0.15, 36, 22, '60\xB0');
  139. drawLabel(HALF_SIZE + 1, HALF_SIZE * 1.85, 36, 22, '60\xB0');
  140. /* Longitude Labels */
  141. ctx.fillStyle = 'red';
  142. drawLabel((HALF_SIZE * 0.48), HALF_SIZE, 36, 22, '30\xB0');
  143. drawLabel((HALF_SIZE * 1.52), HALF_SIZE, 36, 22, '30\xB0');
  144. drawLabel((HALF_SIZE * 0.15), HALF_SIZE, 36, 22, '60\xB0');
  145. drawLabel((HALF_SIZE * 1.85), HALF_SIZE, 36, 22, '60\xB0');
  146. /* Latitude Lines*/
  147. ctx.strokeStyle = 'blue';
  148. drawLine(0, HALF_SIZE, IMAGE_SIZE, HALF_SIZE);
  149. drawLine(HALF_SIZE * 0.15, HALF_SIZE * 0.5, IMAGE_SIZE - HALF_SIZE * 0.15, HALF_SIZE * 0.5);
  150. drawLine(HALF_SIZE * 0.15, HALF_SIZE * 1.5, IMAGE_SIZE - HALF_SIZE * 0.15, HALF_SIZE * 1.5);
  151. drawLine(HALF_SIZE * 0.5, HALF_SIZE * 0.15, IMAGE_SIZE - HALF_SIZE * 0.5, HALF_SIZE * 0.15);
  152. drawLine(HALF_SIZE * 0.5, HALF_SIZE * 1.85, IMAGE_SIZE - HALF_SIZE * 0.5, HALF_SIZE * 1.85);
  153. /* Longitude Lines*/
  154. ctx.strokeStyle = 'red';
  155. drawLine(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
  156. drawEllipse(HALF_SIZE * 0.15, 0, IMAGE_SIZE - IMAGE_SIZE * 0.15, IMAGE_SIZE, 0, 0, 2 * Math.PI);
  157. drawEllipse(HALF_SIZE * 0.48, 0, IMAGE_SIZE - IMAGE_SIZE * 0.48, IMAGE_SIZE, 0, 0, 2 * Math.PI);
  158. /* Compass */
  159. ctx.fillStyle = 'red';
  160. ctx.fillRect(0 + 16, needleLength, needleLength, 4);
  161. ctx.fillText(compass_text[3], 0, needleLength + 8);
  162. ctx.fillText(compass_text[2], needleLength + 16 + 4, needleLength + 8);
  163. ctx.fillText(_('Longitude'), 0, IMAGE_SIZE - 16);
  164. ctx.fillStyle = 'blue';
  165. ctx.fillRect(0.5 * needleLength + 16, 0.5 * needleLength, 4, needleLength);
  166. ctx.fillText(compass_text[0], 0.5 * needleLength + 16 - 4, 0.5 * needleLength - 8);
  167. ctx.fillText(compass_text[1], 0.5 * needleLength + 16 - 4, 1.5 * needleLength + 16 + 4);
  168. ctx.fillText(_('Latitude'), 0, IMAGE_SIZE - 40);
  169. }
  170. function drawEllipse(x, y, width, height, rotation, startAngle, endAngle, anticlockwise) {
  171. /*
  172. Wrapper for drawing ellipse on canvas
  173. converts bounding-box drawing instructions to center-axes instructions
  174. */
  175. ctx.beginPath();
  176. x += width / 2;
  177. y += height / 2;
  178. var radiusX = width / 2;
  179. var radiusY = height / 2;
  180. ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
  181. ctx.stroke();
  182. }
  183. function drawLine(x1, y1, x2, y2) {
  184. /*
  185. Wrapper for drawing line on canvas
  186. */
  187. ctx.beginPath();
  188. ctx.moveTo(x1, y1);
  189. ctx.lineTo(x2, y2);
  190. ctx.stroke();
  191. }
  192. function drawLabel(x, y, width, height, text) {
  193. /*
  194. Wrapper for placing text on canvas
  195. */
  196. var labelColor = ctx.fillStyle;
  197. ctx.fillRect(x, y, width, height);
  198. ctx.fillStyle = 'white';
  199. ctx.fillText(text, x + 5, y + 18);
  200. ctx.fillStyle = labelColor;
  201. }
  202. function setImageSize(size) {
  203. /*
  204. Update IMAGE_SIZE as window resizes
  205. */
  206. IMAGE_SIZE = size;
  207. HALF_SIZE = 0.5 * IMAGE_SIZE;
  208. }
  209. return {
  210. moon: drawMoon,
  211. eclipse: drawEclipse,
  212. grid: drawGrid,
  213. setImageSize: setImageSize
  214. };
  215. });