c295450e7d
A simple tool to visualize holding pattern entries
163 lines
5.7 KiB
JavaScript
163 lines
5.7 KiB
JavaScript
define([
|
|
'jquery', 'knockout', 'text!./Holding.html', 'sprintf', 'kojqui/button', 'kojqui/spinner'
|
|
], function(jquery, ko, htmlString, sprintf) {
|
|
|
|
function ViewModel(params) {
|
|
var self = this;
|
|
|
|
function normDeg(val, min, max) {
|
|
var d = max - min;
|
|
while (val >= max)
|
|
val -= d;
|
|
while (val < min)
|
|
val += d;
|
|
return val;
|
|
}
|
|
|
|
self.standard = ko.observable(true);
|
|
self.nonStandard = ko.pureComputed(function() {
|
|
return false == self.standard();
|
|
});
|
|
|
|
self.inboundTrack = ko.observable(0);
|
|
self.heading = ko.observable(270);
|
|
self.entry = ko.observable("");
|
|
|
|
self.entryClass = function(p) {
|
|
console.log(p, self.entry());
|
|
if (p == self.entry())
|
|
return 'holding-pattern-' + p;
|
|
else
|
|
return 'active-holding-pattern-' + p;
|
|
|
|
}
|
|
|
|
self.holdingTransform = ko.pureComputed(function() {
|
|
return sprintf.sprintf("rotate(%f 50 50)", self.inboundTrack());
|
|
});
|
|
|
|
self.trackTransform = ko.pureComputed(function() {
|
|
return sprintf.sprintf("rotate(%f 50 50)", self.heading());
|
|
});
|
|
|
|
function test() {
|
|
var v = [ -1, 0 ];
|
|
var phi = 0 * Math.PI/180;
|
|
|
|
var cosPhi = Math.cos(phi);
|
|
var sinPhi = Math.sin(phi);
|
|
var m = [ [ cosPhi, sinPhi ], [ -sinPhi, cosPhi ] ];
|
|
|
|
var r = [ m[0][0] * v[0] + m[1][0]*v[1], m[0][1] * v[0] + m[1][1]*v[1]];
|
|
|
|
console.log(v,m,r);
|
|
}
|
|
|
|
function moveOnArc( targetHeading, r, dir ) {
|
|
dir = dir || 1;
|
|
var phi = targetHeading * Math.PI/180;
|
|
var cosPhi = Math.cos(phi);
|
|
var sinPhi = Math.sin(phi);
|
|
var x = dir*r*(1- cosPhi);
|
|
var y = r * sinPhi;
|
|
return [ Number(x.toFixed(1)), Number(-y.toFixed(1)) ];
|
|
}
|
|
|
|
function moveStraight( heading, dist ) {
|
|
var phi = heading * Math.PI/180;
|
|
var cosPhi = Math.cos(phi);
|
|
var sinPhi = Math.sin(phi);
|
|
var x = dist * sinPhi;
|
|
var y = dist * cosPhi;
|
|
return [ Number(x.toFixed(1)), Number(-y.toFixed(1)) ];
|
|
}
|
|
|
|
self.trackDraw = ko.pureComputed(function() {
|
|
function entryProcedure(s, t, h) {
|
|
var d = normDeg(t - h, -180, 180);
|
|
var reply = "";
|
|
|
|
var dir = s ? 1 : -1;
|
|
|
|
if ((s && d >= -110 && d < 70) || (!s && d >= -70 && d < 110)) {
|
|
self.entry("direct");
|
|
// turn to outbound track
|
|
var turn = normDeg(dir*d+180,0,360);
|
|
var p = moveOnArc(turn, 7.5, dir );
|
|
reply += sprintf.sprintf(" a 7.5 7.5, 0, %d, %d, %f %f ", turn>180?1:0, s?1:0, p[0], p[1] );
|
|
|
|
// fly outbound
|
|
p = moveStraight(d+180, 25);
|
|
reply += sprintf.sprintf(" l %f,%f", p[0], p[1] );
|
|
|
|
// turn back to the holding pattern, intercept inbound
|
|
p = moveStraight(d-dir*90, 15 );
|
|
reply += sprintf.sprintf(" a 7.5 7.5, 0, %d, %d, %f %f ", 0, s?1:0, p[0], p[1] );
|
|
|
|
// and to the fix
|
|
reply += " L50,50";
|
|
} else if ((s && d >= -180 && d < -110) || (!s && d >= 110 && d < 180)) {
|
|
self.entry("teardrop");
|
|
|
|
// fly outbount for 1minute
|
|
var p = moveStraight(d+180-dir*30, 30 );
|
|
reply += sprintf.sprintf(" l %f,%f", p[0], p[1] );
|
|
|
|
// turn back to station
|
|
p = moveOnArc(d-dir*30, 7.5, dir );
|
|
reply += sprintf.sprintf(" a 7.5 7.5, 0, %d, %d, %f %f ", 0, s?1:0, p[0], p[1] );
|
|
|
|
reply += " L50,50";
|
|
} else if ((s && d >= 70 && d < 180) || (!s && d >= -180 && d < -70)) {
|
|
self.entry("parallel");
|
|
// turn to outbound track
|
|
var turn = normDeg(dir*d+180,0,360);
|
|
var p = moveOnArc(180-dir*d, 7.5, -dir );
|
|
reply += sprintf.sprintf(" a 7.5 7.5, 0, %d, %d, %f %f ", 0, s?0:1, p[0], p[1] );
|
|
|
|
// fly outbound
|
|
p = moveStraight(d+180, 25);
|
|
reply += sprintf.sprintf(" l %f,%f", p[0], p[1] );
|
|
|
|
// turn back to the holding pattern, intercept inbound
|
|
p = moveStraight(d+dir*90, 15 );
|
|
reply += sprintf.sprintf(" a 7.5 7.5, 0, %d, %d, %f %f ", 0, s?0:1, p[0], p[1] );
|
|
|
|
reply += " L50,50";
|
|
} else {
|
|
self.entry("unknown");
|
|
}
|
|
return reply;
|
|
}
|
|
return "M50,100 v-50 " + entryProcedure(self.standard(), self.inboundTrack(), self.heading());
|
|
});
|
|
|
|
self.setStandard = function(a, b) {
|
|
self.standard(true);
|
|
}
|
|
|
|
self.setNonStandard = function(a, b) {
|
|
self.standard(false);
|
|
}
|
|
|
|
self.inboundTrackSpin = function(event, ui) {
|
|
$(event.target).spinner("value", normDeg(ui.value, 0, 360));
|
|
return false;
|
|
}
|
|
|
|
self.headingSpin = function(event, ui) {
|
|
$(event.target).spinner("value", normDeg(ui.value, 0, 360));
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
// ViewModel.prototype.dispose = function() {
|
|
// }
|
|
|
|
// Return component definition
|
|
return {
|
|
viewModel : ViewModel,
|
|
template : htmlString
|
|
};
|
|
});
|