diff --git a/webgui/topics/Tools.js b/webgui/topics/Tools.js index 001accc1f..84bc63f93 100644 --- a/webgui/topics/Tools.js +++ b/webgui/topics/Tools.js @@ -2,6 +2,10 @@ define([ 'knockout', 'text!./Tools.html' ], function(ko, htmlString) { + ko.components.register('Tools/Holding Pattern', { + require : 'topics/Tools/Holding' + }); + ko.components.register('Tools/Stopwatch', { require : 'topics/Tools/Stopwatch' }); diff --git a/webgui/topics/Tools/Holding.html b/webgui/topics/Tools/Holding.html new file mode 100644 index 000000000..87f43bbeb --- /dev/null +++ b/webgui/topics/Tools/Holding.html @@ -0,0 +1,105 @@ +
+ +
+ Holding Pattern + + + + + + + + + + + + + + + + +
Inbound Track +
+ + + + + + + + + + + + + + + + + + + + direct + + + teardrop + + + parallel + +
+
Heading
+ +
+ +
diff --git a/webgui/topics/Tools/Holding.js b/webgui/topics/Tools/Holding.js new file mode 100644 index 000000000..1220fe986 --- /dev/null +++ b/webgui/topics/Tools/Holding.js @@ -0,0 +1,163 @@ +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 + }; +}); diff --git a/webgui/topics/Tools/Stopwatch.js b/webgui/topics/Tools/Stopwatch.js index 055dbdf6e..655077e3c 100644 --- a/webgui/topics/Tools/Stopwatch.js +++ b/webgui/topics/Tools/Stopwatch.js @@ -5,7 +5,7 @@ define([ function ViewModel(params) { var self = this; - self.watches = ko.observableArray([]); + self.watches = ko.observableArray([0]); self.addWatch = function() { self.watches.push(self.watches().length);