;
/*eslint no-extra-semi:0 */
/**
* @module
*
* @desc
* The svg_ac_inst module returns an object that contains 6 instrument factories:<br/>
* {@link svg_ac_inst:Airspeed} : returns an {@link AirspeedInstance}<br/>
* {@link svg_ac_inst:Altimeter} : returns an {@link AltimeterInstance}<br/>
* {@link svg_ac_inst:Attitude} : returns an {@link AttitudeInstance}<br/>
* {@link svg_ac_inst:Heading} : returns a {@link HeadingInstance}<br/>
* {@link svg_ac_inst:Turn} : returns a {@link TurnInstance}<br/>
* {@link svg_ac_inst:VSI} : returns a {@link VSIInstance}<br/>
*/
var svg_ac_inst = (function (global) {
'use strict';
var ns = 'http://www.w3.org/2000/svg';
// svg element header
var svg_def = {
name: 'svg',
attr: [
['xmlns', 'http://www.w3.org/2000/svg'],
["width", "200px"],
["height", "200px"],
["viewBox", "0 0 100 100"]
]
};
/**
* background rect
*/
var background_def = {
name: 'rect',
attr: [
['x', '0'],
['y', '0'],
['width', '100'],
['height', '100'],
['fill', '#000']
]
};
/**
* dial rect
*/
var dial_def = {
name: 'ellipse',
attr: [
["stroke", '#fff'],
['stroke-width', '0.5'],
['cx', '50'],
['cy', '50'],
['rx', '49'],
['ry', '49']
]
};
// =============================
// needle
// =============================
/**
* needle indicator line
*/
var needle_path_def = {
name: 'path',
attr: [
['d', "M0,0 L0,0.5 L40,1 L45,0 L40,-1 L0,-0.5 L0,0 z"],
['stroke', '#fff'],
['fill', '#fff']
]
};
/**
* needle center
*/
var needle_center_def = {
name: 'ellipse',
attr: [
['cx', '0'],
['cy', '0'],
['rx', '4'],
['ry', '4'],
['fill', '#444']
]
};
/**
* needle tail line
*/
var needle_tail_def = {
name: 'line',
attr: [
['stroke-width', '3.0'],
['stroke', '#444'],
['x1', '-10.0'],
['y1', '0.0'],
['x2', '0.0'],
['y1', '0.0']
]
};
/**
* needle tail circle
*/
var needle_tail2_def = {
name: 'ellipse',
attr: [
['cx', '-10.0'],
['cy', '0'],
['rx', '3'],
['ry', '3'],
['fill', '#444']
]
};
/**
* altimeter drum text
*/
var alt_drum_text_def = {
name: 'text',
attr: [
["stroke", '#000'],
['stroke-width', '0.1'],
["fill", "#fff"],
['x', '50'],
['y', '36'],
["font-family", 'sans-serif'],
['font-size', '10'],
['text-anchor', 'middle']
]
};
/**
* generic group
*/
var needle_group_def = {
name: 'g',
attr: []
};
var standard_group_def = {
name: 'g',
attr: [
["fill", "#fff"],
["stroke", '#fff'],
['stroke-width', '0.5'],
["font-family", 'sans-serif'],
['font-size', '8'],
['text-anchor', 'middle']
]
};
/**
* altimeter pressure setting text
*/
var alt_pressure_text_def = {
name: 'text',
attr: [
["fill", "#fff"],
['x', '50'],
['y', '66'],
["font-family", 'sans-serif'],
['font-size', '6'],
['text-anchor', 'middle']
]
};
var vsi_group6_def = {
name: 'g',
attr: [
["stroke", '#fff'],
['stroke-width', '0.3'],
["fill", "#fff"],
["font-family", 'courier new'],
['font-size', '6'],
['text-anchor', 'middle']
]
};
/**
* create a string from a floating point number with padding and specified factional digits
* @param {number} f value to format
* @param {number} size width of field
* @param {number} fract of fractional digits
* @return {string} padded string
* @inner
*/
function pad(f, size, fract) {
return ('000000000' + f.toFixed(fract)).substr(-size);
}
/**
* create an element with a spedified namespace
* required to svg elements
* @param {string} name name of element
* @param {object} attr array of pairs of attributes
* @param {string} id id of element if any (may be omitted)
* @return {object} constructed element
* @inner
*/
function _createElementSvg(name, attr, id) {
// create the element with the specified name
var e = global.createElementNS(ns, name);
// add an id if specified
if (id) {
e.setAttribute('id', id);
}
// add any attributes specified
attr.forEach(function (v) {
e.setAttribute(v[0], v[1]);
});
return e;
}
function drawLine(x1, y1, x2, y2, stroke_width) {
var e;
e = global.createElementNS(ns, 'line');
if (stroke_width) {
e.setAttribute('stroke-width', stroke_width);
}
e.setAttribute('x1', x1);
e.setAttribute('y1', y1);
e.setAttribute('x2', x2);
e.setAttribute('y2', y2);
return e;
}
function drawText(x, y, text, font_size) {
var e;
e = global.createElementNS(ns, 'text');
e.setAttribute('x', x);
e.setAttribute('y', y);
if (font_size) {
e.setAttribute('font-size', font_size);
}
e.textContent = text;
return e;
}
// x position for altitude numbers
var tx = [50, 71, 85, 85, 70.5, 50, 29, 15, 15, 29];
// y position for altitude numbers
var ty = [16.75, 22.5, 41, 65, 82, 88, 82, 65, 41, 22.75];
/**
* generate the ticks for the altimeter
* @param {object} parent element that ticks will be attached to
* @inner
*/
function altimeterTicks(parent) {
"use strict";
var r3 = 45.0;
var r1 = 40.0;
var r2 = 49.0;
var cx = 50.0;
var cy = 50.0;
var a = 270.0 * (Math.PI / 180.0);
var da = Math.PI / 25;
var x1;
var y1;
var x2;
var y2;
var index;
var alt;
var e;
var g;
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, null);
parent.appendChild(g);
alt = 0;
index = 0;
for (var i = 0; i < 50; ++i) {
if ((i % 5) == 0) {
// bold tick with number
x1 = Math.cos(a) * r1 + cx;
y1 = Math.sin(a) * r1 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2, 1);
g.appendChild(e);
e = drawText(tx[alt].toFixed(1),ty[alt].toFixed(1),alt.toFixed(0));
e.setAttribute('text-anchor', 'middle');
g.appendChild(e);
alt++;
index++;
}
else {
// thin tick
x1 = Math.cos(a) * r3 + cx;
y1 = Math.sin(a) * r3 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2, 0.5);
g.appendChild(e);
}
a += da;
}
}
// airspeed annotation locations
var txc2 = ['52.62', '54.22', '69.75', '82.94', '85.90', '77.18', '59.87', '39.74', '23.26', '15.79', '19.57', '31.92'];
var tyc2 = ['62.69', '25.60', '22.20', '37.26', '58.74', '77.73', '88.42', '88.00', '76.65', '57.45', '36.37', '22.18'];
/**
* generate the ticks for airspeed dial
* @param {object} parent element that ticks will be attached to
* @param {number} dspeed speed increment between ticks (right now only works properly for 20)
* @inner
*/
function airspeedTicks(parent, dspeed) {
"use strict";
var r3 = 45.0;
var r1 = 42.0;
var r2 = 49.0;
var cx = 50.0;
var cy = 50.0;
var a = 270.0 * (Math.PI / 180.0);
var da = Math.PI / 22;
var x1;
var y1;
var x2;
var y2;
var index;
var speed;
var e;
var g;
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, null);
parent.appendChild(g);
speed = dspeed;
index = 2;
for (var i = 0; i < 44; ++i) {
if ((i == 0) || (i == 2)) {
// skip
}
else if ((i % 4) == 0) {
// bold tick with number
x1 = Math.cos(a) * r1 + cx;
y1 = Math.sin(a) * r1 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2, 1.0);
g.appendChild(e);
e = drawText(txc2[index],tyc2[index],speed.toFixed(0));
e.setAttribute('text-anchor', 'middle');
g.appendChild(e);
speed += dspeed;
index++;
}
else if ((i % 2) == 0) {
x1 = Math.cos(a) * r1 + cx;
y1 = Math.sin(a) * r1 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2, 1.0);
g.appendChild(e);
}
else {
// thin tick
x1 = Math.cos(a) * r3 + cx;
y1 = Math.sin(a) * r3 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2, 0.5);
g.appendChild(e);
}
a += da;
}
}
var vsi_y = [18, 29, 52, 78, 88, 78, 52.5, 27];
var vsi_x = [50, 73, 82, 73, 50, 25, 14, 25];
var vsi_t = ['10', '15', '20', '15', '10', '5', '0', '5'];
function vsiTicks(parent) {
"use strict";
var r3 = 45.0;
var r1 = 40.0;
var r2 = 49.0;
var cx = 50.0;
var cy = 50.0;
var a = 270.0 * (Math.PI / 180.0);
var da = Math.PI / 20;
var x1;
var y1;
var x2;
var y2;
var index;
var e;
var g;
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, null);
parent.appendChild(g);
index = 0;
for (var i = 0; i < 40; ++i) {
if ((i == 9) || (i == 11)) {
x1 = Math.cos(a) * r1 + cx;
y1 = Math.sin(a) * r1 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2, 1.0);
g.appendChild(e);
}
else if (i == 10) {
e = drawText( vsi_x[index],vsi_y[index],vsi_t[index]);
e.setAttribute('text-anchor', 'middle');
g.appendChild(e);
index++;
}
else if ((i % 5) == 0) {
x1 = Math.cos(a) * r1 + cx;
y1 = Math.sin(a) * r1 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2, 1.0);
g.appendChild(e);
e = drawText( vsi_x[index],vsi_y[index],vsi_t[index]);
e.setAttribute('text-anchor', 'middle');
g.appendChild(e);
index++;
}
else {
x1 = Math.cos(a) * r3 + cx;
y1 = Math.sin(a) * r3 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2);
g.appendChild(e);
}
a += da;
}
}
function headingDial(parent, id) {
"use strict";
var r3 = 45.0;
var r1 = 43.0;
var r2 = 49.0;
var r4 = 36.0;
var cx = 0.0;
var cy = 0.0;
var a = 270.0 * (Math.PI / 180.0);
var da = Math.PI / 36;
var x1;
var y1;
var x2;
var y2;
var x4;
var y4;
var d;
var e;
var g;
var t;
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, id + '-dial');
g.setAttribute('transform', 'translate(50 50) rotate(0)');
parent.appendChild(g);
d = 0;
for (var i = 0; i < 72; ++i) {
if ((i % 6) == 0) {
x1 = Math.cos(a) * r1 + cx;
y1 = Math.sin(a) * r1 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
x4 = Math.cos(a) * r4 + cx;
y4 = Math.sin(a) * r4 + cy;
// <line stroke-width='1.0' x1='-0.00' y1='-43.00' x2='-0.00' y2='-49.00' />
e = drawLine(x1, y1, x2, y2, 1.0);
g.appendChild(e);
// <text transform='translate(-0.00 -36.00) rotate(0)' >0</text>
t = "translate(";
t += x4.toFixed(2) + " " + y4.toFixed(2);
t += ") ";
t += "rotate(";
t += d.toFixed(0);
t += ")";
e = global.createElementNS(ns, 'text');
e.textContent = (d / 10).toFixed(0);
e.setAttribute('transform', t);
g.appendChild(e);
d += 30.0;
}
else if ((i % 2) == 0) {
x1 = Math.cos(a) * r1 + cx;
y1 = Math.sin(a) * r1 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2, 1.0);
g.appendChild(e);
}
else {
x1 = Math.cos(a) * r3 + cx;
y1 = Math.sin(a) * r3 + cy;
x2 = Math.cos(a) * r2 + cx;
y2 = Math.sin(a) * r2 + cy;
e = drawLine(x1, y1, x2, y2);
g.appendChild(e);
}
a += da;
}
}
function vsiAnnotation(svg) {
var g;
var e;
// <g fill='#fff' stroke='#fff' stroke-width='0.3' font-family='courier new' font-size='6'>
g = _createElementSvg(vsi_group6_def.name, vsi_group6_def.attr, null);
svg.appendChild(g);
// <text text-anchor='middle' x='50' y='37' >VERTICAL</text>
e = drawText(50,37,"VERTICAL");
g.appendChild(e);
// <text text-anchor='middle' x='50' y='43'>SPEED</text>
e = drawText(50,43,'SPEED');
g.appendChild(e);
// <text text-anchor='left' x='14' y='43'>up</text>
e = drawText(14,43,'up');
g.appendChild(e);
// <text text-anchor='left' x='14' y='61'>down</text>
e = drawText(14,61,'down');
g.appendChild(e);
// <text text-anchor='middle' x='50' y='67'>100 ft</text>
e = drawText(50,67,'100 ft');
g.appendChild(e);
// <text text-anchor='middle' x='50' y='72'>per min</text>
e = drawText(50,72,'per min');
g.appendChild(e);
}
/**
* create an arc for the airspeed indicator
* with the given parameters
* @param {number} start_speed speed at beginning of arc
* @param {number} end_speed speed at end of arc
* @param {number} range total range of airspeed dial
* @param {number} radius of arc
* @return {object} contains parameters for drawing the arc
* @inner
*/
function arc(start_speed, end_speed, range, radius) {
var a1 = ((start_speed * (Math.PI * 2)) / range) - (Math.PI / 2.0);
var a2 = ((end_speed * (Math.PI * 2)) / range) - (Math.PI / 2.0);
var x1;
var y1;
var x2;
var y2;
var cx = 50.0;
var cy = 50.0;
x1 = cx + radius * Math.cos(a1);
y1 = cy + radius * Math.sin(a1);
x2 = cx + radius * Math.cos(a2);
y2 = cy + radius * Math.sin(a2);
return {
radius: radius,
x1: x1,
y1: y1,
x2: x2,
y2: y2
};
}
/**
* create the path spec for an arc starting at a specified position
* @param {object} arc parameters describing the arc (from function arc)
* @return {string} 'd' spec for arc element
* @inner
*/
function arc_path(arc) {
var d = "M";
d += arc.x2.toFixed(2) + " ";
d += arc.y2.toFixed(2) + " ";
d += "A ";
d += arc.radius.toFixed(2) + " ";
d += arc.radius.toFixed(2) + " ";
d += "0 0 0 ";
d += arc.x1.toFixed(2) + " ";
d += arc.y1.toFixed(2);
return d;
}
/**
* create the arc specs for all arcs in an airspeed indicator
* @param {number} range
* @param {number} radius
* @param {number} stall_flaps
* @param {number} max_flaps
* @param {number} stall_clean
* @param {number} max_cruise
* @param {number} min_caution
* @param {number} max_caution
* @param {number} never_exceed
* @return {object} specs for all 4 arcs on the airspeed dial
* @inner
*/
function makeLimitArcs(range,
radius,
stall_flaps,
max_flaps,
stall_clean,
max_cruise,
min_caution,
max_caution,
never_exceed) {
var p;
var green;
var yellow;
var red;
var white;
p = arc(stall_clean, max_cruise, range, radius);
green = arc_path(p);
p = arc(min_caution, max_caution, range, radius);
yellow = arc_path(p);
p = arc(never_exceed - 1, never_exceed + 1, range, radius);
red = arc_path(p);
p = arc(stall_flaps, max_flaps, range, radius - 3);
white = arc_path(p);
return {
green: green,
yellow: yellow,
red: red,
white: white
};
}
/**
* generate the airspeed arc parameters
* @param {object} options - to set various airspeed limit arcs
* @param {number} options.range - airspeed range 0..range
* @param {array} options.green - [80,140] green arc from clean stall to max cruise
* @param {array} options.yellow - [140,160] yellow arc from max cruise to never-exceed
* @param {number} options.red - 160 never-exceed speed
* @param {array} options.white - [50,100] white arc for stall-flaps to max-flaps
* @inner
*/
function airspeedArcs(options) {
var radius = 48;
var range = options.range;
var stall_flaps = options.white[0];
var max_flaps = options.white[1];
var stall_clean = options.green[0];
var max_cruise = options.green[1];
var min_caution = options.yellow[0];
var max_caution = options.yellow[1];
var never_exceed = options.red;
var arcs;
arcs = makeLimitArcs(range,
radius,
stall_flaps,
max_flaps,
stall_clean,
max_cruise,
min_caution,
max_caution,
never_exceed
);
return arcs;
}
/** airspeed path element
* <path stroke-width="3" stroke="#0f0" d="M13.72 81.43 A 48.00 48.00 0 0 0 86.28 81.43"/>
*/
var airspeed_arc_def = {
name: 'path',
attr: [
['stroke-width', '3']
]
};
/**
* draw an altimeter
* @param parent reference to parent element, such as a div
* @param id id to attach or reference to existing svg
* @inner
*/
function drawTurn(parent, id) {
var e;
var g;
var svg;
var turn_path_def = {
name: 'path',
attr: [
['d', "M20 65 A 120.00 120.00 0 0 0 80 65"],
['stroke-width', '10'],
['fill', '#000']
]
};
var turn_e1_def = {
name: 'ellipse',
attr: [
["fill", '#000'],
['stroke-width', '0.5'],
['cx', '50'],
['cy', '69'],
['rx', '5.5'],
['ry', '5.5']
]
};
var turn_e2_def = {
name: 'ellipse',
attr: [
['cx', '0'],
['cy', '0'],
['rx', '4'],
['ry', '4']
]
};
// svg element
svg = _createElementSvg(svg_def.name, svg_def.attr, id);
parent.appendChild(svg);
// background rect
e = _createElementSvg(background_def.name, background_def.attr, null);
svg.appendChild(e);
// dial circle
e = _createElementSvg(dial_def.name, dial_def.attr,null);
svg.appendChild(e);
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, null);
g.setAttribute('stroke-width', '2.0');
g.setAttribute('stroke', '#fff');
svg.appendChild(g);
e = drawLine(90.00, 50.00, 99.00, 50.00);
g.appendChild(e);
e = drawLine(88.45, 61.03, 97.10, 63.51);
g.appendChild(e);
e = drawLine(11.55, 61.03, 2.90, 63.51);
g.appendChild(e);
e = drawLine(10.00, 50.00, 1.00, 50.00);
g.appendChild(e);
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, null);
g.setAttribute('stroke-width', '0.5');
g.setAttribute('stroke', '#fff');
g.setAttribute('fill', '#fff');
g.setAttribute('font-family', 'sans-serif');
g.setAttribute('font-size', '6');
g.setAttribute('text-anchor', 'middle');
svg.appendChild(g);
e = drawText(12, 72, 'L');
g.appendChild(e);
e = drawText(88, 72, 'R');
g.appendChild(e);
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, null);
g.setAttribute('stroke-width', '0.1');
g.setAttribute('stroke', '#fff');
g.setAttribute('fill', '#fff');
g.setAttribute('font-family', 'sans-serif');
g.setAttribute('text-anchor', 'middle');
svg.appendChild(g);
e = drawText(50, 60, 'TURN COORDINATOR', 3);
g.appendChild(e);
e = drawText(50, 80, '2 MIN', 4);
g.appendChild(e);
e = drawText(50, 88, 'NO PITCH', 3);
g.appendChild(e);
e = drawText(50, 93, 'INFORMATION', 3);
g.appendChild(e);
e = _createElementSvg(turn_path_def.name, turn_path_def.attr, null);
g.appendChild(e);
e = drawLine(44, 63, 44, 80, 0.5);
e.setAttribute('stroke', '#000');
g.appendChild(e);
e = drawLine(56, 63, 56, 80, 0.5);
e.setAttribute('stroke', '#000');
g.appendChild(e);
e = _createElementSvg(turn_e1_def.name, turn_e1_def.attr, id + '-ball');
svg.appendChild(e);
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, id + '-plane');
g.setAttribute('stroke-width', '1');
g.setAttribute('stroke', '#fff');
g.setAttribute('fill', '#fff');
svg.appendChild(g);
e = _createElementSvg(turn_e2_def.name, turn_e2_def.attr, null);
g.appendChild(e);
e = drawLine(-38, 0, 38, 0, 2);
g.appendChild(e);
e = drawLine(-10, -5, 10, -5);
g.appendChild(e);
e = drawLine(0, -10, 0, 0);
g.appendChild(e);
}
/**
* draw an altimeter
* @param parent reference to parent element, such as a div
* @param id id to attach or reference to existing svg
* @inner
*/
function drawAltimeter(parent, id) {
var e;
var g;
var svg;
// svg element
svg = _createElementSvg(svg_def.name, svg_def.attr, id);
parent.appendChild(svg);
// background rect
e = _createElementSvg(background_def.name, background_def.attr, null);
svg.appendChild(e);
// dial circle
e = _createElementSvg(dial_def.name, dial_def.attr, null);
svg.appendChild(e);
// drum text
e = _createElementSvg(alt_drum_text_def.name, alt_drum_text_def.attr, id + '-drum');
svg.appendChild(e);
// pressure text
e = _createElementSvg(alt_pressure_text_def.name, alt_pressure_text_def.attr, id + '-pwin');
e.textContent = pad(29.92, 5, 2);
svg.appendChild(e);
// needle
g = _createElementSvg(needle_group_def.name, needle_group_def.attr, id + '-needle');
svg.appendChild(g);
e = _createElementSvg(needle_path_def.name, needle_path_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_tail_def.name, needle_tail_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_center_def.name, needle_center_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_tail2_def.name, needle_tail2_def.attr, null);
g.appendChild(e);
altimeterTicks(svg);
}
/**
* draw an airspeed indicator
* @param parent reference to parent element, such as a div
* @param id id to attach or reference to existing svg
* @param {object} options - to set various airspeed limit arcs
* @param {number} options.range - airspeed range 0..range
* @param {array} options.green - [80,140] green arc from clean stall to max cruise
* @param {array} options.yellow - [140,160] yellow arc from max cruise to never-exceed
* @param {number} options.red - 160 red never-exceed
* @param {array} options.white - [50,100] white arc for stall-flaps to max-flaps
* @inner
*/
function drawAirspeed(parent, id, options) {
var e;
var g;
var arcs;
var svg;
// get arc definitions
arcs = airspeedArcs(options);
// svg element
svg = _createElementSvg(svg_def.name, svg_def.attr, id);
parent.appendChild(svg);
// background rect
e = _createElementSvg(background_def.name, background_def.attr, null);
svg.appendChild(e);
// dial circle
e = _createElementSvg(dial_def.name, dial_def.attr, null);
svg.appendChild(e);
// green,yellow and white are behind ticks
e = _createElementSvg(airspeed_arc_def.name, airspeed_arc_def.attr, null);
e.setAttribute('stroke', '#0f0');
e.setAttribute('d', arcs.green);
svg.appendChild(e);
// green,yellow and white are behind ticks
e = _createElementSvg(airspeed_arc_def.name, airspeed_arc_def.attr, null);
e.setAttribute('stroke', '#ff0');
e.setAttribute('d', arcs.yellow);
svg.appendChild(e);
// green,yellow and white are behind ticks
e = _createElementSvg(airspeed_arc_def.name, airspeed_arc_def.attr, null);
e.setAttribute('stroke', '#fff');
e.setAttribute('d', arcs.white);
svg.appendChild(e);
airspeedTicks(svg, 20);
// red is on top of ticks
e = _createElementSvg(airspeed_arc_def.name, airspeed_arc_def.attr, null);
e.setAttribute('stroke', '#f00');
e.setAttribute('d', arcs.red);
svg.appendChild(e);
// needle group
g = _createElementSvg(needle_group_def.name, needle_group_def.attr, id + '-needle');
svg.appendChild(g);
e = _createElementSvg(needle_path_def.name, needle_path_def.attr, null);
g.appendChild(e);
e = _createElementSvg(airspeed_arc_def.name, airspeed_arc_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_tail_def.name, needle_tail_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_center_def.name, needle_center_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_tail2_def.name, needle_tail2_def.attr, null);
g.appendChild(e);
// ===== end of needle
}
/**
* draw a VSI
* @param parent reference to parent element, such as a div
* @param id id to attach or reference to existing svg
* @inner
*/
function drawVSI(parent, id) {
var e;
var g;
var svg;
// svg element
svg = _createElementSvg(svg_def.name, svg_def.attr, id);
parent.appendChild(svg);
// background rect
e = _createElementSvg(background_def.name, background_def.attr, null);
svg.appendChild(e);
// dial circle
e = _createElementSvg(dial_def.name, dial_def.attr, null);
svg.appendChild(e);
// needle
g = _createElementSvg(needle_group_def.name, needle_group_def.attr, id + '-needle');
svg.appendChild(g);
e = _createElementSvg(needle_path_def.name, needle_path_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_tail_def.name, needle_tail_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_center_def.name, needle_center_def.attr, null);
g.appendChild(e);
e = _createElementSvg(needle_tail2_def.name, needle_tail2_def.attr, null);
g.appendChild(e);
// text annotation
vsiAnnotation(svg);
// ticks
vsiTicks(svg);
}
/**
* draw an attitude indicator
* @param parent reference to parent element, such as a div
* @param id id to attach or reference to existing svg
* @inner
*/
function drawAttitude(parent, id) {
var e;
var g;
var svg;
var pos_group = {
name: 'g',
attr: [
['stroke-width', '1'],
['stroke', '#fff'],
['fill', '#fff'],
['transform', 'translate(50,50) rotate(0)'],
['font-family', 'sans-serif'],
['text-anchor', 'middle'],
['font-size', '6']
]
};
var dial_group = {
name: 'g',
attr: [
['stroke-width', '1'],
['stroke', '#fff']
]
};
var rectBlue = {
name: 'rect',
attr: [
['fill', '#29B6F6'],
['x', '-100'],
['y', '-200'],
['width', '200'],
['height', '200']
]
};
var rectBrown = {
name: 'rect',
attr: [
['fill', '#8B4513'],
['x', '-100'],
['y', '0'],
['width', '200'],
['height', '200']
]
};
var dial_path = {
name: 'path',
attr: [
['fill', '#fff'],
['d', 'M-4 -50 L4 -50 L 0 -40 ']
]
};
var pointer_path = {
name: 'path',
attr: [
['stroke-width', '1'],
['stroke', '#ff0'],
['fill', '#ff0'],
['d', 'M46 18 L54 18 L50 11 z']
]
};
var outline_rect = {
name: 'rect',
attr: [
['fill', 'transparent'],
['stroke', '#9e9e9e'],
['stroke-width', '8'],
['x', '0'],
['y', '0'],
['width', '100'],
['height', '100']
]
};
// svg element
svg = _createElementSvg(svg_def.name, svg_def.attr, id);
parent.appendChild(svg);
g = _createElementSvg(pos_group.name, pos_group.attr, id + '-pos');
svg.appendChild(g);
e = _createElementSvg(rectBlue.name, rectBlue.attr, null);
g.appendChild(e);
e = _createElementSvg(rectBrown.name, rectBrown.attr, null);
g.appendChild(e);
// draw pitch angle ticks
var angle = -60.0;
for (var i = 0; i <= 13; i++) {
if (i == 6) {
angle += 10.0;
continue;
}
var a = angle * 1.25;
var dy = -6.25;
var b = a - dy;
e = drawLine(-8, b, 8, b);
g.appendChild(e);
e = drawLine(-16, a, -4, a);
g.appendChild(e);
e = drawText(0, a + 2, Math.abs(angle).toString());
e.setAttribute('stroke-width', '0.1');
g.appendChild(e);
// e = drawLine( 4 ,-75.0 ,16 ,-75.0 );
e = drawLine(4, a, 16, a);
g.appendChild(e);
angle += 10.0;
}
g = _createElementSvg(standard_group_def.name, standard_group_def.attr, null);
g.setAttribute('stroke-width', '2');
g.setAttribute('stroke', '#ff0');
svg.appendChild(g);
e = drawLine(30, 50, 43, 50);
g.appendChild(e);
e = drawLine(42, 50, 42, 53);
g.appendChild(e);
e = drawLine(49, 50, 51, 50);
g.appendChild(e);
e = drawLine(58, 53, 58, 50);
g.appendChild(e);
e = drawLine(57, 50, 70, 50);
g.appendChild(e);
g = _createElementSvg(dial_group.name, dial_group.attr, id + '-dial');
svg.appendChild(g);
e = drawLine(20.00, -34.64, 24.50, -42.44, 2);
g.appendChild(e);
e = drawLine(34.64, -20.00, 42.44, -24.50, 2);
g.appendChild(e);
e = drawLine(40.00, 0.00, 49.00, 0.00, 2);
g.appendChild(e);
e = drawLine(-20.00, -34.64, -24.50, -42.44, 2);
g.appendChild(e);
e = drawLine(-34.64, -20.00, -42.44, -24.50, 2);
g.appendChild(e);
e = drawLine(-40.00, -0.00, -49.00, -0.00, 2);
g.appendChild(e);
e = drawLine(6.95, -39.39, 7.81, -44.32);
g.appendChild(e);
e = drawLine(13.68, -37.59, 15.39, -42.29);
g.appendChild(e);
e = drawLine(28.28, -28.28, 31.82, -31.82);
g.appendChild(e);
e = drawLine(-6.95, -39.39, -7.81, -44.32);
g.appendChild(e);
e = drawLine(-13.68, -37.59, -15.39, -42.29);
g.appendChild(e);
e = drawLine(-28.28, -28.28, -31.82, -31.82);
g.appendChild(e);
e = drawLine(0.00, -40.00, 0.00, -45.00);
g.appendChild(e);
e = _createElementSvg(dial_path.name, dial_path.attr, null);
g.appendChild(e);
e = _createElementSvg(pointer_path.name, pointer_path.attr, null);
svg.appendChild(e);
e = _createElementSvg(outline_rect.name, outline_rect.attr, null);
svg.appendChild(e);
}
/**
* draw a heading indicator
* @param parent reference to parent element, such as a div
* @param id id to attach or reference to existing svg
* @inner
*/
function drawHeading(parent, id) {
var e;
var svg;
var hdg_l1_def = {
name: 'line',
attr: [
['stroke', '#fff'],
['stroke-width', '0.5'],
['x1', '50'],
['y1', '5'],
['x2', '50'],
['y2', '20']
]
};
//<path stroke='#fff' stroke-width='0.5' d="M30,58 L30,50 L46,34 L46,26 L50,16.4 L54,26 L54,34 L70,50 L70,58 L54,50 L54,66 L62,70 L62,76.4 L50,71.6 L38,76.4 L38,70 L46,66 L46,50 L30,58"/>
var hdg_ac_def = {
name: 'path',
attr: [
['stroke', '#fff'],
['stroke-width', '0.5'],
['d', 'M30,58 L30,50 L46,34 L46,26 L50,16.4 L54,26 L54,34 L70,50 L70,58 L54,50 L54,66 L62,70 L62,76.4 L50,71.6 L38,76.4 L38,70 L46,66 L46,50 L30,58']
]
};
// svg element
svg = _createElementSvg(svg_def.name, svg_def.attr, id);
parent.appendChild(svg);
// background rect
e = _createElementSvg(background_def.name, background_def.attr, null);
svg.appendChild(e);
// dial circle
e = _createElementSvg(dial_def.name, dial_def.attr, null);
svg.appendChild(e);
e = _createElementSvg(hdg_l1_def.name, hdg_l1_def.attr, null);
svg.appendChild(e);
e = _createElementSvg(hdg_ac_def.name, hdg_ac_def.attr, null);
svg.appendChild(e);
// draw the dial
headingDial(svg, id);
}
/* ==================================== */
/* CONSTRUCTORS */
/* ==================================== */
/**
* Altimeter
* @param {Element} parent parent element, such as a div
* @param {string} id id of airspeed object
* @param {boolean} draw true to draw on client, false to use static svg with same id
* @return {object} {@link AltimeterInstance}
* @global
* @alias svg_ac_inst:Altimeter
*/
function Altimeter(parent, id, draw) {
var altimeter;
var needle;
var drum;
var pwindow;
if (draw) {
drawAltimeter(parent, id);
}
altimeter = global.querySelector('#' + id);
needle = global.querySelector('#' + id + '-needle');
drum = global.querySelector('#' + id + '-drum');
pwindow = global.querySelector('#' + id + '-pwin');
/**
* @global
* @namespace
*/
var AltimeterInstance = {
/** @param altitude altitude in feet
* @inner
*/
set: function (altitude) {
if (altitude >= 999999) {
altitude = 999999;
}
var d = (altitude % 1000) * (360.0 / 1000.0) - 90;
drum.textContent = pad(altitude, 6, 0);
needle.setAttribute('transform', 'translate(50,50),rotate(' + d + ')');
},
/** @param pressure altimeter setting
* @inner
*/
setPressure: function (pressure) {
pwindow.textContent = pad(pressure, 5, 2);
},
/** @param size new size in feet
* @inner
*/
resize: function (size) {
var s = size.toString() + 'px';
altimeter.setAttribute('width', s);
altimeter.setAttribute('height', s);
}
};
return AltimeterInstance;
}
/**
* Airspeed Indicator
* @param {Element} parent parent element, such as a div
* @param {string} id id of airspeed object
* @param {boolean} draw true to draw on client, false to use static svg with same id
* @param {object} options - to set various airspeed limit arcs
* @param {number} options.range - airspeed range 0..range
* @param {array} options.green - [80,140] green arc from clean stall to max cruise
* @param {array} options.yellow - [140,160] yellow arc from max cruise to never-exceed
* @param {number} options.red - 160 red never-exceed
* @param {array} options.white - [50,100] white arc for stall-flaps to max-flaps
* @return {object} reference to an {@link AirspeedInstance}
* @global
* @alias svg_ac_inst:Airspeed
*/
function Airspeed(parent, id, draw, options) {
var airspeed;
var needle;
if (draw) {
drawAirspeed(parent, id, options);
}
airspeed = global.querySelector('#' + id);
needle = global.querySelector('#' + id + '-needle');
/**
* @global
* @namespace
*/
var AirspeedInstance = {
/**
* @param knots speed in knots
* @inner
*/
set: function (knots) {
if (knots >= 230) {
knots = 230;
}
var d = knots * (360.0 / 230.0) - 90.0;
needle.setAttribute('transform', 'translate(50,50),rotate(' + d + ')');
},
/** @param size new size in pixels (square)
* @inner
*/
resize: function (size) {
var s = size.toString() + 'px';
airspeed.setAttribute('width', s);
airspeed.setAttribute('height', s);
}
};
return AirspeedInstance;
}
/**
* heading Indicator
* @param {Element} parent parent element, such as a div
* @param {string} id id of airspeed object
* @param {boolean} draw true to draw on client, false to use static svg with same id
* @return {object} reference to a {@link HeadingInstance}
* @global
* @alias svg_ac_inst:Heading
*/
function Heading(parent, id, draw) {
if (draw) {
drawHeading(parent, id);
}
var heading = global.querySelector('#' + id);
var dial = global.querySelector('#' + id + '-dial');
/**
* @global
* @namespace
*/
var HeadingInstance = {
/** @param degrees heading in degrees
*/
set: function (degrees) {
degrees *= -1;
dial.setAttribute('transform', 'translate(50 50) rotate(' + degrees.toFixed(0) + ')');
},
/** @param size new size in pixels (square)
* @inner
*/
resize: function (size) {
var s = size.toString() + 'px';
heading.setAttribute('width', s);
heading.setAttribute('height', s);
}
};
return HeadingInstance;
}
/**
* Attitude Indicator
* @param {Element} parent parent element, such as a div
* @param {string} id id of airspeed object
* @param {boolean} draw true to draw on client, false to use static svg with same id
* @return {object} reference to an {@link AttitudeInstance}
* @global
* @alias svg_ac_inst:Attitude
*/
function Attitude(parent, id, draw) {
if (draw) {
drawAttitude(parent, id);
}
var attitude = global.querySelector('#' + id);
var pos = global.querySelector('#' + id + '-pos');
var dial = global.querySelector('#' + id + '-dial');
function setDial(d, roll) {
var x;
var y;
var t;
x = 50;
y = 50;
t = 'translate(' + x + ',' + y + ')' + 'rotate(' + roll + ')';
d.setAttribute('transform', t);
}
/**
* @global
* @namespace
*/
var AttitudeInstance = {
/**
* @param pitch aircraft pitch in degrees (positive up)
* @param roll aircraft roll in degrees (positive right)
* @inner
*/
set: function (pitch, roll) {
var p;
var t;
p = (pitch * 1.25);
t = 'translate(50,50) rotate(' + roll + ') translate(0,' + p + ')';
pos.setAttribute('transform', t);
setDial(dial, roll);
},
/** @param size new size in pixels (square)
* @inner
*/
resize: function (size) {
var s = size.toString() + 'px';
attitude.setAttribute('width', s);
attitude.setAttribute('height', s);
}
};
return AttitudeInstance;
}
/**
* Turn Coordinator
* @param {Element} parent parent element, such as a div
* @param {string} id id of airspeed object
* @param {boolean} draw true to draw on client, false to use static svg with same id
* @return {object} reference to a {@link TurnInstance}
* @global
* @alias svg_ac_inst:Turn
*/
function Turn(parent, id, draw) {
if (draw) {
drawTurn(parent, id);
}
var turn = global.querySelector('#' + id);
var plane = global.querySelector('#' + id + '-plane');
var ball = global.querySelector('#' + id + '-ball');
/**
* @global
* @namespace
*/
var TurnInstance = {
/**
* @param rate turn rate in degrees per second
* @param accel lateral acceleration in units -10 (full left deflection) to +10 (full right deflection)
* @inner
*/
set: function (rate, accel) {
var x;
var y;
var r = 119.0;
var cx = 50;
var cy = -50;
// plane
plane.setAttribute('transform', 'translate(50,50) rotate(' + (rate * 5.3).toFixed(0) + ')');
// ball
accel += 90.0;
x = Math.cos(accel * (Math.PI / 180.0)) * r + cx;
y = Math.sin(accel * (Math.PI / 180.0)) * r + cy;
ball.setAttribute('cx', x);
ball.setAttribute('cy', y);
},
/** @param size new size in pixels (square)
* @inner
*/
resize: function (size) {
var s = size.toString() + 'px';
turn.setAttribute('width', s);
turn.setAttribute('height', s);
}
};
return TurnInstance;
}
/**
* Vertical Speed Indicator
* @param {Element} parent parent element, such as a div
* @param {string} id id of airspeed object
* @param {boolean} draw true to draw on client, false to use static svg with same id
* @return {object} {@link VSIInstance}
* @global
* @alias svg_ac_inst:VSI
*/
function VSI(parent, id, draw) {
if (draw) {
drawVSI(parent, id);
}
var vsi = global.querySelector('#' + id);
var needle = global.querySelector('#' + id + '-needle');
/**
* @global
* @namespace
*/
var VSIInstance = {
/**
* @param vertical_speed vertical speed in feet per minute (positive up)
* @inner
*/
set: function (vertical_speed) {
if (vertical_speed >= 2000) {
vertical_speed = 2000;
}
else if (vertical_speed <= -2000) {
vertical_speed = -2000;
}
var d = vertical_speed * (360.0 / 4000) - 180.0;
needle.setAttribute('transform', 'translate(50,50),rotate(' + d + ')');
},
/** @param size new size in pixels (square)
* @inner
*/
resize: function (size) {
var s = size.toString() + 'px';
vsi.setAttribute('width', s);
vsi.setAttribute('height', s);
}
};
return VSIInstance;
}
return {
Altimeter: Altimeter,
Airspeed: Airspeed,
Heading: Heading,
Attitude: Attitude,
Turn: Turn,
VSI: VSI
};
}(document));