define(['activity/data-model', 'webL10n'], function(DataModel, l10n) { 'use strict'; var canvas = document.querySelector('canvas'), ctx = canvas.getContext('2d'), moon = document.querySelector('img#moon'); var _ = l10n.get; var IMAGE_SIZE, HALF_SIZE; if (!ctx.ellipse) { /* CanvasRenderingContext2D.ellipse() is esperimental at the time of writing Provide pollyfill */ ctx.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) { /* for this project, we do not need: rotation, startAngle, endAngle, anticlockwise */ x -= radiusX; y -= radiusY; radiusX *= 2; radiusY *= 2; var kappa = 0.5522848, ox = (radiusX / 2) * kappa, // control point offset horizontal oy = (radiusY / 2) * kappa, // control point offset vertical xe = x + radiusX, // x-end ye = y + radiusY, // y-end xm = x + radiusX / 2, // x-middle ym = y + radiusY / 2; // y-middle if (startAngle === Math.PI / 2 && endAngle === 3 * Math.PI / 2) { ctx.moveTo(xm, ye); ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); /* 2nd quarter */ ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); /* 3rd quarter */ } else if (startAngle === 3 * Math.PI / 2 && endAngle === Math.PI / 2) { ctx.moveTo(xm, y); ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); /* 4th quarter */ ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); /* 1st quarter */ } else if (startAngle === 0 && endAngle === 2 * Math.PI) { ctx.moveTo(xe, ym); ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); /* 1st quarter */ ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); /* 2nd quarter */ ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); /* 3rd quarter */ ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); /* 4th quarter */ } }; } function drawMoon() { /* Draw mask corresponding to either shaded region or lit region */ var phase_shadow_adjust = null; var arc_scale = null; ctx.strokeStyle = 'black'; ctx.fillStyle = 'black'; ctx.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE); if (DataModel.phase_of_moon < 0.25) { phase_shadow_adjust = DataModel.phase_of_moon - Math.abs(Math.sin(DataModel.phase_of_moon * Math.PI * 4) / 18.0); arc_scale = 1 - (4 * phase_shadow_adjust); ctx.fillStyle = 'white'; ctx.fillRect(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE); ctx.fillStyle = 'black'; drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, 3 * Math.PI / 2, Math.PI / 2); ctx.fill(); } else if (DataModel.phase_of_moon < 0.50) { phase_shadow_adjust = DataModel.phase_of_moon + Math.abs(Math.sin(DataModel.phase_of_moon * Math.PI * 4) / 18.0); arc_scale = 4 * (phase_shadow_adjust - 0.25); ctx.fillStyle = 'white'; ctx.fillRect(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE); ctx.fillStyle = 'white'; drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, Math.PI / 2, 3 * Math.PI / 2); ctx.fill(); } else if (DataModel.phase_of_moon < 0.75) { phase_shadow_adjust = DataModel.phase_of_moon - Math.abs(Math.sin(DataModel.phase_of_moon * Math.PI * 4) / 18.0); arc_scale = 1 - (4 * (phase_shadow_adjust - 0.5)); ctx.fillStyle = 'white'; ctx.fillRect(0, 0, HALF_SIZE, IMAGE_SIZE); ctx.fillStyle = 'white'; drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, 3 * Math.PI / 2, Math.PI / 2); ctx.fill(); } else { phase_shadow_adjust = DataModel.phase_of_moon + Math.abs(Math.sin(DataModel.phase_of_moon * Math.PI * 4) / 18.0); arc_scale = 4 * (phase_shadow_adjust - 0.75); ctx.fillStyle = 'white'; ctx.fillRect(0, 0, HALF_SIZE, IMAGE_SIZE); ctx.fillStyle = 'black'; drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, Math.PI / 2, 3 * Math.PI / 2); ctx.fill(); } ctx.save(); ctx.globalCompositeOperation = 'multiply'; ctx.drawImage(moon, 0, 0, IMAGE_SIZE, IMAGE_SIZE); ctx.globalAlpha = 0.5; ctx.globalCompositeOperation = 'source-over'; ctx.drawImage(moon, 0, 0, IMAGE_SIZE, IMAGE_SIZE); ctx.restore(); drawEclipse(); } function drawEclipse() { if ( ( DataModel.next_lunar_eclipse_sec !== -1 || DataModel.last_lunar_eclipse_sec <= 7200 ) && ( DataModel.next_lunar_eclipse_sec <= 7200 || DataModel.last_lunar_eclipse_sec !== -1 ) && ( Math.min(DataModel.next_lunar_eclipse_sec, DataModel.last_lunar_eclipse_sec) <= 7200 ) ) { var eclipse_alpha; if (DataModel.next_lunar_eclipse_sec == -1) { eclipse_alpha = DataModel.last_lunar_eclipse_sec / 7200; } else if (DataModel.last_lunar_eclipse_sec == -1) { eclipse_alpha = DataModel.next_lunar_eclipse_sec / 7200; } else { eclipse_alpha = Math.min(DataModel.next_lunar_eclipse_sec, DataModel.last_lunar_eclipse_sec) / 7200; } ctx.save(); ctx.globalAlpha = 0.25 * (1 - eclipse_alpha); ctx.globalCompositeOperation = 'multiply'; ctx.fillStyle = 'red'; ctx.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE); ctx.restore(); } } function drawGrid(compass_text) { /* Draw longitudes at 0, +/-30 and +/-60 degrees Draw latitudes at 0, +/-30 and +/-60 degrees Draw compass */ var needleLength = 0.08 * IMAGE_SIZE; ctx.font = '16px Sans'; ctx.lineWidth = 3; /* Latitude Labels */ ctx.fillStyle = 'blue'; drawLabel(HALF_SIZE + 1, HALF_SIZE, 26, 22, '0\xB0'); drawLabel(HALF_SIZE + 1, HALF_SIZE * 0.50, 36, 22, '30\xB0'); drawLabel(HALF_SIZE + 1, HALF_SIZE * 1.5, 36, 22, '30\xB0'); drawLabel(HALF_SIZE + 1, HALF_SIZE * 0.15, 36, 22, '60\xB0'); drawLabel(HALF_SIZE + 1, HALF_SIZE * 1.85, 36, 22, '60\xB0'); /* Longitude Labels */ ctx.fillStyle = 'red'; drawLabel((HALF_SIZE * 0.48), HALF_SIZE, 36, 22, '30\xB0'); drawLabel((HALF_SIZE * 1.52), HALF_SIZE, 36, 22, '30\xB0'); drawLabel((HALF_SIZE * 0.15), HALF_SIZE, 36, 22, '60\xB0'); drawLabel((HALF_SIZE * 1.85), HALF_SIZE, 36, 22, '60\xB0'); /* Latitude Lines*/ ctx.strokeStyle = 'blue'; drawLine(0, HALF_SIZE, IMAGE_SIZE, HALF_SIZE); drawLine(HALF_SIZE * 0.15, HALF_SIZE * 0.5, IMAGE_SIZE - HALF_SIZE * 0.15, HALF_SIZE * 0.5); drawLine(HALF_SIZE * 0.15, HALF_SIZE * 1.5, IMAGE_SIZE - HALF_SIZE * 0.15, HALF_SIZE * 1.5); drawLine(HALF_SIZE * 0.5, HALF_SIZE * 0.15, IMAGE_SIZE - HALF_SIZE * 0.5, HALF_SIZE * 0.15); drawLine(HALF_SIZE * 0.5, HALF_SIZE * 1.85, IMAGE_SIZE - HALF_SIZE * 0.5, HALF_SIZE * 1.85); /* Longitude Lines*/ ctx.strokeStyle = 'red'; drawLine(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE); drawEllipse(HALF_SIZE * 0.15, 0, IMAGE_SIZE - IMAGE_SIZE * 0.15, IMAGE_SIZE, 0, 0, 2 * Math.PI); drawEllipse(HALF_SIZE * 0.48, 0, IMAGE_SIZE - IMAGE_SIZE * 0.48, IMAGE_SIZE, 0, 0, 2 * Math.PI); /* Compass */ ctx.fillStyle = 'red'; ctx.fillRect(0 + 16, needleLength, needleLength, 4); ctx.fillText(compass_text[3], 0, needleLength + 8); ctx.fillText(compass_text[2], needleLength + 16 + 4, needleLength + 8); ctx.fillText(_('Longitude'), 0, IMAGE_SIZE - 16); ctx.fillStyle = 'blue'; ctx.fillRect(0.5 * needleLength + 16, 0.5 * needleLength, 4, needleLength); ctx.fillText(compass_text[0], 0.5 * needleLength + 16 - 4, 0.5 * needleLength - 8); ctx.fillText(compass_text[1], 0.5 * needleLength + 16 - 4, 1.5 * needleLength + 16 + 4); ctx.fillText(_('Latitude'), 0, IMAGE_SIZE - 40); } function drawEllipse(x, y, width, height, rotation, startAngle, endAngle, anticlockwise) { /* Wrapper for drawing ellipse on canvas converts bounding-box drawing instructions to center-axes instructions */ ctx.beginPath(); x += width / 2; y += height / 2; var radiusX = width / 2; var radiusY = height / 2; ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise); ctx.stroke(); } function drawLine(x1, y1, x2, y2) { /* Wrapper for drawing line on canvas */ ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); } function drawLabel(x, y, width, height, text) { /* Wrapper for placing text on canvas */ var labelColor = ctx.fillStyle; ctx.fillRect(x, y, width, height); ctx.fillStyle = 'white'; ctx.fillText(text, x + 5, y + 18); ctx.fillStyle = labelColor; } function setImageSize(size) { /* Update IMAGE_SIZE as window resizes */ IMAGE_SIZE = size; HALF_SIZE = 0.5 * IMAGE_SIZE; } return { moon: drawMoon, eclipse: drawEclipse, grid: drawGrid, setImageSize: setImageSize }; });