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
|
|
};
|
|
});
|