From 5934d9ed4ba941ab30a9b1555ff4725e625f1cc2 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Wed, 16 May 2018 23:10:50 -0500 Subject: [PATCH 01/22] Initial surface shapes functional test --- functionalTest/SurfaceShapes.html | 59 +++++++++++++++++++++++++++++++ functionalTest/SurfaceShapes.js | 21 +++++++++++ 2 files changed, 80 insertions(+) create mode 100644 functionalTest/SurfaceShapes.html create mode 100644 functionalTest/SurfaceShapes.js diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html new file mode 100644 index 000000000..33b59d019 --- /dev/null +++ b/functionalTest/SurfaceShapes.html @@ -0,0 +1,59 @@ + + + + + + + + + + + Surface Shape Performance Functional Test + + + + +
+
+ + + +
+
+
+
+
+ + Browser Does Not Support HTML5 + +
+
+

+ +

+
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js new file mode 100644 index 000000000..a7c5bf305 --- /dev/null +++ b/functionalTest/SurfaceShapes.js @@ -0,0 +1,21 @@ +requirejs([ + '../src/WorldWind' + ], + function (WorldWind) { + "use strict"; + + var statusDialog = document.getElementById("status-dialog"); + var statusOutput = document.getElementById("output"); + var globeCanvas = document.getElementById("globe"); + + statusDialog.innerText = "Initializing globe..."; + + var wwd = new WorldWind.WorldWindow(globeCanvas); + wwd.globe.elevationModel.removeAllCoverages(); // Don't want delays associated with changing terrain + var bmnglayer = new WorldWind.BMNGOneImageLayer(); + bmnglayer.minActiveAltitude = 0; + wwd.addLayer(bmnglayer); // Don't want any imaging processing delays + + statusDialog.innerText = "Globe Loaded"; + + }); \ No newline at end of file From 80b9d568163b1f731630dda90028c5ef9f9de8c9 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Thu, 17 May 2018 12:55:17 -0500 Subject: [PATCH 02/22] Update layout --- functionalTest/SurfaceShapes.html | 7 ++++--- functionalTest/SurfaceShapes.js | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index 33b59d019..a0f124b42 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -28,9 +28,9 @@
- - - + + +
@@ -44,6 +44,7 @@

+
diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index a7c5bf305..9f7332bda 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -8,14 +8,27 @@ requirejs([ var statusOutput = document.getElementById("output"); var globeCanvas = document.getElementById("globe"); - statusDialog.innerText = "Initializing globe..."; + var updateStatus = function (statusMessage) { + var children = statusDialog.childNodes; + for (var c = 0; c < children.length; c++) { + statusDialog.removeChild(children[c]); + } + + statusDialog.appendChild(document.createTextNode(statusMessage)); + }; + + updateStatus("Initializing globe..."); var wwd = new WorldWind.WorldWindow(globeCanvas); - wwd.globe.elevationModel.removeAllCoverages(); // Don't want delays associated with changing terrain + wwd.globe.elevationModel.removeAllCoverages(); // Don't want delays associated with parsing and changing terrain var bmnglayer = new WorldWind.BMNGOneImageLayer(); bmnglayer.minActiveAltitude = 0; wwd.addLayer(bmnglayer); // Don't want any imaging processing delays - statusDialog.innerText = "Globe Loaded"; + updateStatus("Globe Loaded, ready for testing"); + + var runNavigation = function () { + + }; }); \ No newline at end of file From bc3ff9452c3ecfadf2de57b5954af54d0928b149 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Thu, 17 May 2018 14:54:29 -0500 Subject: [PATCH 03/22] Refactor common utilities to helper class --- functionalTest/SurfaceShapes.html | 2 +- functionalTest/SurfaceShapes.js | 39 +++++++++++---- functionalTest/Util.js | 79 +++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 functionalTest/Util.js diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index a0f124b42..ebec0c9a2 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -28,7 +28,7 @@
- +
diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 9f7332bda..4cfa7d7a2 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -1,12 +1,13 @@ requirejs([ - '../src/WorldWind' + '../src/WorldWind', + './Util' ], - function (WorldWind) { + function (WorldWind, Util) { "use strict"; var statusDialog = document.getElementById("status-dialog"); var statusOutput = document.getElementById("output"); - var globeCanvas = document.getElementById("globe"); + var navigateButton = document.getElementById("navigate-button"); var updateStatus = function (statusMessage) { var children = statusDialog.childNodes; @@ -19,16 +20,34 @@ requirejs([ updateStatus("Initializing globe..."); - var wwd = new WorldWind.WorldWindow(globeCanvas); - wwd.globe.elevationModel.removeAllCoverages(); // Don't want delays associated with parsing and changing terrain - var bmnglayer = new WorldWind.BMNGOneImageLayer(); - bmnglayer.minActiveAltitude = 0; - wwd.addLayer(bmnglayer); // Don't want any imaging processing delays + var wwd = Util.initializeLowResourceWorldWindow("globe"); + var util = new Util(wwd); updateStatus("Globe Loaded, ready for testing"); - var runNavigation = function () { + // second move + var operationsComplete = 0; + var moving = false; + var moveTwo = function () { + // wait until previous moves are complete + operationsComplete++; + if (operationsComplete > 1) { + util.changeHeading(90, 1, 50, function() { + updateStatus("Move Complete"); + moving = false; + operationsComplete = 0; + }); + } + }; + // first move + var moveOne = function () { + if (!moving) { + moving = true; + util.changeRange(1e5, 5e4, 50, moveTwo); + util.changeTilt(50, 0.25, 50, moveTwo); + } }; - }); \ No newline at end of file + navigateButton.addEventListener("click", moveOne); + }); diff --git a/functionalTest/Util.js b/functionalTest/Util.js new file mode 100644 index 000000000..30ba00f97 --- /dev/null +++ b/functionalTest/Util.js @@ -0,0 +1,79 @@ +define([ + + ], + function () { + "use strict"; + + var Util = function (wwd) { + this.wwd = wwd; + }; + + Util.initializeLowResourceWorldWindow = function (canvasId) { + var wwd = new WorldWind.WorldWindow(canvasId); + wwd.globe.elevationModel.removeAllCoverages(); // Don't want delays associated with parsing and changing terrain + var bmnglayer = new WorldWind.BMNGOneImageLayer(); + bmnglayer.minActiveAltitude = 0; + wwd.addLayer(bmnglayer); // Don't want any imaging processing delays + + return wwd; + }; + + Util.prototype.changeRange = function (goal, step, interval, complete) { + var range = this.wwd.navigator.range; + var diff = range - goal; + var self = this; + if (Math.abs(diff) > 1) { + if (diff < 0) { + this.wwd.navigator.range = Math.abs(diff) > step ? range + step : goal; + } else { + this.wwd.navigator.range = Math.abs(diff) > step ? range - step : goal; + } + this.wwd.redraw(); + setTimeout(function () { + self.changeRange(goal, step, interval, complete); + }, interval); + } else { + complete(); + } + }; + + Util.prototype.changeTilt = function (goal, step, interval, complete) { + var tilt = this.wwd.navigator.tilt; + var diff = tilt - goal; + var self = this; + if (Math.abs(diff) > 1) { + if (diff < 0) { + this.wwd.navigator.tilt = Math.abs(diff) > step ? tilt + step : goal; + } else { + this.wwd.navigator.tilt = Math.abs(diff) > step ? tilt - step : goal; + } + this.wwd.redraw(); + setTimeout(function () { + self.changeTilt(goal, step, interval, complete); + }, interval); + } else { + complete(); + } + }; + + Util.prototype.changeHeading = function (goal, step, interval, complete) { + var heading = this.wwd.navigator.heading; + var diff = heading - goal; + var self = this; + if (Math.abs(diff) > 1) { + if (diff < 0) { + this.wwd.navigator.heading = Math.abs(diff) > step ? heading + step : goal; + } else { + this.wwd.navigator.heading = Math.abs(diff) > step ? heading - step : goal; + } + this.wwd.redraw(); + setTimeout(function () { + self.changeHeading(goal, step, interval, complete); + }, interval); + } else { + complete(); + } + }; + + return Util; +}); From 7f28f21a41d1bfcefaa37a66e12923edc6bb69b7 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Thu, 17 May 2018 15:57:37 -0500 Subject: [PATCH 04/22] Refactor navigation to use redraw callbacks instead of timeouts --- functionalTest/SurfaceShapes.js | 4 +- functionalTest/Util.js | 114 +++++++++++++++++++------------- 2 files changed, 71 insertions(+), 47 deletions(-) diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 4cfa7d7a2..654db96ba 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -44,8 +44,8 @@ requirejs([ var moveOne = function () { if (!moving) { moving = true; - util.changeRange(1e5, 5e4, 50, moveTwo); - util.changeTilt(50, 0.25, 50, moveTwo); + util.changeRange(8e5, 5e4, 50, moveTwo); + util.changeTilt(75, 0.25, 50, moveTwo); } }; diff --git a/functionalTest/Util.js b/functionalTest/Util.js index 30ba00f97..cc69d5b7a 100644 --- a/functionalTest/Util.js +++ b/functionalTest/Util.js @@ -1,7 +1,7 @@ define([ - + '../src/WorldWind' ], - function () { + function (WorldWind) { "use strict"; var Util = function (wwd) { @@ -14,65 +14,89 @@ define([ var bmnglayer = new WorldWind.BMNGOneImageLayer(); bmnglayer.minActiveAltitude = 0; wwd.addLayer(bmnglayer); // Don't want any imaging processing delays + // wwd.addLayer(new WorldWind.FrameStatisticsLayer(wwd)); + // wwd.redrawCallbacks.push(function (worldwindow, stage) { + // if (stage === WorldWind.AFTER_REDRAW) { + // worldwindow.redraw(); + // } + // }); return wwd; }; Util.prototype.changeRange = function (goal, step, interval, complete) { - var range = this.wwd.navigator.range; - var diff = range - goal; - var self = this; - if (Math.abs(diff) > 1) { - if (diff < 0) { - this.wwd.navigator.range = Math.abs(diff) > step ? range + step : goal; - } else { - this.wwd.navigator.range = Math.abs(diff) > step ? range - step : goal; + + var movement = function (worldwindow, stage) { + if (stage === WorldWind.AFTER_REDRAW) { + var range = worldwindow.navigator.range; + var diff = range - goal; + var self = this; + if (Math.abs(diff) > 1) { + if (diff < 0) { + worldwindow.navigator.range = Math.abs(diff) > step ? range + step : goal; + } else { + worldwindow.navigator.range = Math.abs(diff) > step ? range - step : goal; + } + worldwindow.redraw(); + } else { + var idx = worldwindow.redrawCallbacks.indexOf(movement); + worldwindow.redrawCallbacks.splice(idx, 1); + complete(); + } } - this.wwd.redraw(); - setTimeout(function () { - self.changeRange(goal, step, interval, complete); - }, interval); - } else { - complete(); - } + }; + + this.wwd.redrawCallbacks.push(movement); + this.wwd.redraw(); + }; Util.prototype.changeTilt = function (goal, step, interval, complete) { - var tilt = this.wwd.navigator.tilt; - var diff = tilt - goal; - var self = this; - if (Math.abs(diff) > 1) { - if (diff < 0) { - this.wwd.navigator.tilt = Math.abs(diff) > step ? tilt + step : goal; + + var movement = function (worldwindow, stage) { + var tilt = worldwindow.navigator.tilt; + var diff = tilt - goal; + var self = this; + if (Math.abs(diff) > 1) { + if (diff < 0) { + worldwindow.navigator.tilt = Math.abs(diff) > step ? tilt + step : goal; + } else { + worldwindow.navigator.tilt = Math.abs(diff) > step ? tilt - step : goal; + } + worldwindow.redraw(); } else { - this.wwd.navigator.tilt = Math.abs(diff) > step ? tilt - step : goal; + var idx = worldwindow.redrawCallbacks.indexOf(movement); + worldwindow.redrawCallbacks.splice(idx, 1); + complete(); } - this.wwd.redraw(); - setTimeout(function () { - self.changeTilt(goal, step, interval, complete); - }, interval); - } else { - complete(); - } + }; + + this.wwd.redrawCallbacks.push(movement); + this.wwd.redraw(); }; Util.prototype.changeHeading = function (goal, step, interval, complete) { - var heading = this.wwd.navigator.heading; - var diff = heading - goal; - var self = this; - if (Math.abs(diff) > 1) { - if (diff < 0) { - this.wwd.navigator.heading = Math.abs(diff) > step ? heading + step : goal; + + var movement = function (worldwindow, stage) { + var heading = worldwindow.navigator.heading; + var diff = heading - goal; + var self = this; + if (Math.abs(diff) > 1) { + if (diff < 0) { + worldwindow.navigator.heading = Math.abs(diff) > step ? heading + step : goal; + } else { + worldwindow.navigator.heading = Math.abs(diff) > step ? heading - step : goal; + } + worldwindow.redraw(); } else { - this.wwd.navigator.heading = Math.abs(diff) > step ? heading - step : goal; + var idx = worldwindow.redrawCallbacks.indexOf(movement); + worldwindow.redrawCallbacks.splice(idx, 1); + complete(); } - this.wwd.redraw(); - setTimeout(function () { - self.changeHeading(goal, step, interval, complete); - }, interval); - } else { - complete(); - } + }; + + this.wwd.redrawCallbacks.push(movement); + this.wwd.redraw(); }; return Util; From b78772e46326a84d77ec28d092492e2ae3baf722 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Thu, 17 May 2018 16:12:11 -0500 Subject: [PATCH 05/22] Add notional frame rate output display --- functionalTest/SurfaceShapes.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 654db96ba..50244c946 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -28,6 +28,7 @@ requirejs([ // second move var operationsComplete = 0; var moving = false; + var navigationComplete = false; var moveTwo = function () { // wait until previous moves are complete operationsComplete++; @@ -36,6 +37,7 @@ requirejs([ updateStatus("Move Complete"); moving = false; operationsComplete = 0; + navigationComplete = true; }); } }; @@ -50,4 +52,31 @@ requirejs([ }; navigateButton.addEventListener("click", moveOne); + + var stats = [], min = Number.MAX_VALUE, max = -Number.MAX_VALUE; + wwd.redrawCallbacks.push(function (worldwindow, stage) { + if (stage === WorldWind.BEFORE_REDRAW) { + if (moving && !navigationComplete) { + var frametime = worldwindow.frameStatistics.frameTime; + stats.push(frametime); + min = Math.min(min, frametime); + max = Math.max(max, frametime); + } + + if (navigationComplete) { + var canvas = document.createElement("canvas"); + canvas.setAttribute("width", Math.ceil(stats.length) + ""); + canvas.setAttribute("height", Math.ceil(max - min) * 50 + ""); + var ctx = canvas.getContext("2d"); + ctx.beginPath(); + ctx.moveTo(0, Math.floor(max - stats[0]) * 50); + for (var i = 1, len = stats.length; i < len; i++) { + ctx.lineTo(i, Math.floor(stats[i]) * 50); + } + ctx.stroke(); + statusOutput.appendChild(canvas); + } + + } + }); }); From 23a1cff1d0bf708fabd89011912717f924b548db Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Thu, 17 May 2018 16:34:24 -0500 Subject: [PATCH 06/22] Refine notional statistics output --- functionalTest/SurfaceShapes.html | 2 +- functionalTest/SurfaceShapes.js | 37 +++++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index ebec0c9a2..50a5ec79b 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -29,7 +29,7 @@
- +
diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 50244c946..323d6cb75 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -8,6 +8,7 @@ requirejs([ var statusDialog = document.getElementById("status-dialog"); var statusOutput = document.getElementById("output"); var navigateButton = document.getElementById("navigate-button"); + var staticShapesButton = document.getElementById("static-button"); var updateStatus = function (statusMessage) { var children = statusDialog.childNodes; @@ -54,8 +55,9 @@ requirejs([ navigateButton.addEventListener("click", moveOne); var stats = [], min = Number.MAX_VALUE, max = -Number.MAX_VALUE; - wwd.redrawCallbacks.push(function (worldwindow, stage) { - if (stage === WorldWind.BEFORE_REDRAW) { + + var metricCapture = function (worldwindow, stage) { + if (stage === WorldWind.AFTER_REDRAW) { if (moving && !navigationComplete) { var frametime = worldwindow.frameStatistics.frameTime; stats.push(frametime); @@ -65,18 +67,39 @@ requirejs([ if (navigationComplete) { var canvas = document.createElement("canvas"); - canvas.setAttribute("width", Math.ceil(stats.length) + ""); - canvas.setAttribute("height", Math.ceil(max - min) * 50 + ""); + canvas.setAttribute("width", 500); + canvas.setAttribute("height", 500); var ctx = canvas.getContext("2d"); ctx.beginPath(); - ctx.moveTo(0, Math.floor(max - stats[0]) * 50); - for (var i = 1, len = stats.length; i < len; i++) { - ctx.lineTo(i, Math.floor(stats[i]) * 50); + var x = Math.floor(1 / len * 500); + var y = Math.floor((max - stats[1]) * 500 / (max - min)); + ctx.moveTo(x, y); + for (var i = 2, len = stats.length; i < len; i++) { + x = Math.floor(i / len * 500); + var y = Math.floor((max - stats[i]) * 500 / (max - min)); + ctx.lineTo(x, y); } ctx.stroke(); statusOutput.appendChild(canvas); + var idx = worldwindow.redrawCallbacks.indexOf(metricCapture); + worldwindow.redrawCallbacks.splice(idx, 1); } } + }; + + wwd.redrawCallbacks.push(metricCapture); + + staticShapesButton.addEventListener("click", function () { + var layer = new WorldWind.RenderableLayer("Test Layer"); + wwd.addLayer(layer); + for (var i = 0; i < 2000; i++) { + var shapeLocation = new WorldWind.Location(Math.random() * 22.5 + 15.0, -80 - Math.random() * 45); + var minorAxis = Math.random() * 100000 + 10000; + var majorAxis = Math.random() * 500000 + 10000; + var heading = Math.random() * 360; + layer.addRenderable(new WorldWind.SurfaceEllipse(shapeLocation, majorAxis, minorAxis, heading)); + } + wwd.redraw(); }); }); From 087f0b4dbdd4fdb6b8216002e9a0fd5f810b79cd Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Thu, 17 May 2018 22:48:49 -0500 Subject: [PATCH 07/22] Update movement utility --- functionalTest/SurfaceShapes.js | 60 +++++++++------ functionalTest/Util.js | 128 ++++++++++++++++++-------------- 2 files changed, 112 insertions(+), 76 deletions(-) diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 323d6cb75..6e384c56a 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -20,39 +20,57 @@ requirejs([ }; updateStatus("Initializing globe..."); - var wwd = Util.initializeLowResourceWorldWindow("globe"); - var util = new Util(wwd); - updateStatus("Globe Loaded, ready for testing"); - // second move - var operationsComplete = 0; + // moving state var moving = false; var navigationComplete = false; - var moveTwo = function () { - // wait until previous moves are complete - operationsComplete++; - if (operationsComplete > 1) { - util.changeHeading(90, 1, 50, function() { - updateStatus("Move Complete"); - moving = false; - operationsComplete = 0; - navigationComplete = true; - }); + + // moves + var moveOne = { + range: { + goal: 8e5, + step: 5e4 + }, + tilt: { + goal: 75, + step: 0.75 + } + }; + + var moveTwo = { + heading: { + goal: 135, + step: 1 } }; - // first move - var moveOne = function () { + var moveThree = { + latitude: { + goal: wwd.navigator.lookAtLocation.latitude, + step: 1 + }, + longitude: { + goal: -70, + step: 0.25 + } + }; + + var onMoveComplete = function () { + moving = false; + navigationComplete = true; + updateStatus("Move Complete"); + }; + + var onClickMove = function () { if (!moving) { moving = true; - util.changeRange(8e5, 5e4, 50, moveTwo); - util.changeTilt(75, 0.25, 50, moveTwo); + Util.move(wwd, [moveOne, moveTwo, moveThree], onMoveComplete); } }; - navigateButton.addEventListener("click", moveOne); + navigateButton.addEventListener("click", onClickMove); var stats = [], min = Number.MAX_VALUE, max = -Number.MAX_VALUE; @@ -94,7 +112,7 @@ requirejs([ var layer = new WorldWind.RenderableLayer("Test Layer"); wwd.addLayer(layer); for (var i = 0; i < 2000; i++) { - var shapeLocation = new WorldWind.Location(Math.random() * 22.5 + 15.0, -80 - Math.random() * 45); + var shapeLocation = new WorldWind.Location(Math.random() * 45 + 15.0, -80 - Math.random() * 90); var minorAxis = Math.random() * 100000 + 10000; var majorAxis = Math.random() * 500000 + 10000; var heading = Math.random() * 360; diff --git a/functionalTest/Util.js b/functionalTest/Util.js index cc69d5b7a..2af14850b 100644 --- a/functionalTest/Util.js +++ b/functionalTest/Util.js @@ -4,8 +4,7 @@ define([ function (WorldWind) { "use strict"; - var Util = function (wwd) { - this.wwd = wwd; + var Util = function () { }; Util.initializeLowResourceWorldWindow = function (canvasId) { @@ -24,79 +23,98 @@ define([ return wwd; }; - Util.prototype.changeRange = function (goal, step, interval, complete) { + Util.move = function (wwd, endStatesArray, complete) { + var idx = 0; - var movement = function (worldwindow, stage) { - if (stage === WorldWind.AFTER_REDRAW) { - var range = worldwindow.navigator.range; - var diff = range - goal; - var self = this; - if (Math.abs(diff) > 1) { - if (diff < 0) { - worldwindow.navigator.range = Math.abs(diff) > step ? range + step : goal; - } else { - worldwindow.navigator.range = Math.abs(diff) > step ? range - step : goal; - } - worldwindow.redraw(); + var nextPosition = function (worldwindow, stage) { + if (stage !== WorldWind.AFTER_REDRAW) { + return; + } + + var range, tilt, heading, latitude, longitude, endStates = endStatesArray[idx]; + + if (endStates.range && !endStates.range.complete) { + range = Util.calculateNextValue(worldwindow.navigator.range, endStates.range.goal, endStates.range.step); + if (typeof range === "number") { + worldwindow.navigator.range = range; } else { - var idx = worldwindow.redrawCallbacks.indexOf(movement); - worldwindow.redrawCallbacks.splice(idx, 1); - complete(); + endStates.range.complete = true; } } - }; - this.wwd.redrawCallbacks.push(movement); - this.wwd.redraw(); + if (endStates.tilt && !endStates.tilt.complete) { + tilt = Util.calculateNextValue(worldwindow.navigator.tilt, endStates.tilt.goal, endStates.tilt.step); + if (typeof tilt === "number") { + worldwindow.navigator.tilt = tilt; + } else { + endStates.tilt.complete = true; + } + } - }; + if (endStates.heading && !endStates.heading.complete) { + heading = Util.calculateNextValue(worldwindow.navigator.heading, endStates.heading.goal, endStates.heading.step); + if (typeof heading === "number") { + worldwindow.navigator.heading = heading; + } else { + endStates.heading.complete = true; + } + } - Util.prototype.changeTilt = function (goal, step, interval, complete) { + if (endStates.latitude && !endStates.latitude.complete) { + latitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.latitude, + endStates.latitude.goal, endStates.latitude.step); + if (typeof latitude === "number") { + worldwindow.navigator.lookAtLocation.latitude = latitude; + } else { + endStates.latitude.complete = true; + } + } - var movement = function (worldwindow, stage) { - var tilt = worldwindow.navigator.tilt; - var diff = tilt - goal; - var self = this; - if (Math.abs(diff) > 1) { - if (diff < 0) { - worldwindow.navigator.tilt = Math.abs(diff) > step ? tilt + step : goal; + if (endStates.longitude && !endStates.longitude.complete) { + longitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.longitude, + endStates.longitude.goal, endStates.longitude.step); + if (typeof longitude === "number") { + worldwindow.navigator.lookAtLocation.longitude = longitude; } else { - worldwindow.navigator.tilt = Math.abs(diff) > step ? tilt - step : goal; + endStates.longitude.complete = true; } + } + + var keys = Object.getOwnPropertyNames(endStates); + for (var i = 0; i < keys.length; i++) { + if (!endStates[keys[i]].complete) { + worldwindow.redraw(); + return; + } + } + + if (idx === endStatesArray.length - 1) { + var callbackIdx = worldwindow.redrawCallbacks.indexOf(nextPosition); + worldwindow.redrawCallbacks.splice(callbackIdx, 1); worldwindow.redraw(); - } else { - var idx = worldwindow.redrawCallbacks.indexOf(movement); - worldwindow.redrawCallbacks.splice(idx, 1); complete(); + } else { + idx++; + worldwindow.redraw(); } }; - this.wwd.redrawCallbacks.push(movement); - this.wwd.redraw(); + wwd.redrawCallbacks.push(nextPosition); + wwd.redraw(); }; - Util.prototype.changeHeading = function (goal, step, interval, complete) { + Util.calculateNextValue = function (currentValue, goal, step) { + var diff = currentValue - goal; - var movement = function (worldwindow, stage) { - var heading = worldwindow.navigator.heading; - var diff = heading - goal; - var self = this; - if (Math.abs(diff) > 1) { - if (diff < 0) { - worldwindow.navigator.heading = Math.abs(diff) > step ? heading + step : goal; - } else { - worldwindow.navigator.heading = Math.abs(diff) > step ? heading - step : goal; - } - worldwindow.redraw(); + if (Math.abs(diff) > 0.1) { + if (diff < 0) { + return Math.abs(diff) > step ? currentValue + step : goal; } else { - var idx = worldwindow.redrawCallbacks.indexOf(movement); - worldwindow.redrawCallbacks.splice(idx, 1); - complete(); + return Math.abs(diff) > step ? currentValue - step : goal; } - }; - - this.wwd.redrawCallbacks.push(movement); - this.wwd.redraw(); + } else { + return null; + } }; return Util; From e3f35a2b2515b96bcdbadf3914fc970e02c35a26 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Fri, 18 May 2018 15:12:57 -0500 Subject: [PATCH 08/22] Refactor function layout --- functionalTest/SurfaceShapes.js | 73 ++++----- functionalTest/Util.js | 282 ++++++++++++++++++++++++-------- 2 files changed, 247 insertions(+), 108 deletions(-) diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 6e384c56a..4d5f5d1d4 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -21,6 +21,7 @@ requirejs([ updateStatus("Initializing globe..."); var wwd = Util.initializeLowResourceWorldWindow("globe"); + var util = new Util(wwd); updateStatus("Globe Loaded, ready for testing"); // moving state @@ -36,6 +37,9 @@ requirejs([ tilt: { goal: 75, step: 0.75 + }, + onComplete: function () { + updateStatus("First move complete..."); } }; @@ -43,6 +47,9 @@ requirejs([ heading: { goal: 135, step: 1 + }, + onComplete: function () { + updateStatus("Second move complete"); } }; @@ -54,26 +61,36 @@ requirejs([ longitude: { goal: -70, step: 0.25 + }, + onComplete: function () { + moving = false; + navigationComplete = true; + updateStatus("Move Complete"); } }; - var onMoveComplete = function () { - moving = false; - navigationComplete = true; - updateStatus("Move Complete"); - }; - var onClickMove = function () { if (!moving) { moving = true; - Util.move(wwd, [moveOne, moveTwo, moveThree], onMoveComplete); + util.move([moveOne, moveTwo, moveThree]); } }; - navigateButton.addEventListener("click", onClickMove); + var onGenerateShapesClick = function () { + var layer = new WorldWind.RenderableLayer("Test Layer"); + wwd.addLayer(layer); + for (var i = 0; i < 2000; i++) { + var shapeLocation = new WorldWind.Location(Math.random() * 45 + 15.0, -80 - Math.random() * 90); + var minorAxis = Math.random() * 100000 + 10000; + var majorAxis = Math.random() * 500000 + 10000; + var heading = Math.random() * 360; + layer.addRenderable(new WorldWind.SurfaceEllipse(shapeLocation, majorAxis, minorAxis, heading)); + } + wwd.redraw(); + }; + // Temporary metric capture framework var stats = [], min = Number.MAX_VALUE, max = -Number.MAX_VALUE; - var metricCapture = function (worldwindow, stage) { if (stage === WorldWind.AFTER_REDRAW) { if (moving && !navigationComplete) { @@ -84,40 +101,16 @@ requirejs([ } if (navigationComplete) { - var canvas = document.createElement("canvas"); - canvas.setAttribute("width", 500); - canvas.setAttribute("height", 500); - var ctx = canvas.getContext("2d"); - ctx.beginPath(); - var x = Math.floor(1 / len * 500); - var y = Math.floor((max - stats[1]) * 500 / (max - min)); - ctx.moveTo(x, y); - for (var i = 2, len = stats.length; i < len; i++) { - x = Math.floor(i / len * 500); - var y = Math.floor((max - stats[i]) * 500 / (max - min)); - ctx.lineTo(x, y); - } - ctx.stroke(); - statusOutput.appendChild(canvas); - var idx = worldwindow.redrawCallbacks.indexOf(metricCapture); - worldwindow.redrawCallbacks.splice(idx, 1); + navigationComplete = false; + // clean up the data + stats.shift(); + stats.pop(); + statusOutput.appendChild(Util.generateResultsSummary(stats, "Frame Times")); } - } }; - wwd.redrawCallbacks.push(metricCapture); - staticShapesButton.addEventListener("click", function () { - var layer = new WorldWind.RenderableLayer("Test Layer"); - wwd.addLayer(layer); - for (var i = 0; i < 2000; i++) { - var shapeLocation = new WorldWind.Location(Math.random() * 45 + 15.0, -80 - Math.random() * 90); - var minorAxis = Math.random() * 100000 + 10000; - var majorAxis = Math.random() * 500000 + 10000; - var heading = Math.random() * 360; - layer.addRenderable(new WorldWind.SurfaceEllipse(shapeLocation, majorAxis, minorAxis, heading)); - } - wwd.redraw(); - }); + navigateButton.addEventListener("click", onClickMove); + staticShapesButton.addEventListener("click", onGenerateShapesClick); }); diff --git a/functionalTest/Util.js b/functionalTest/Util.js index 2af14850b..edb6b1481 100644 --- a/functionalTest/Util.js +++ b/functionalTest/Util.js @@ -1,10 +1,17 @@ define([ - '../src/WorldWind' + '../src/WorldWind', + '../src/Util/WWUtil' ], - function (WorldWind) { + function (WorldWind, WWUtil) { "use strict"; - var Util = function () { + var Util = function (worldwindow) { + + this.wwd = worldwindow; + + this.wwd.redrawCallbacks.push(this.onRedraw()); + + this.moveQueue = []; }; Util.initializeLowResourceWorldWindow = function (canvasId) { @@ -13,96 +20,235 @@ define([ var bmnglayer = new WorldWind.BMNGOneImageLayer(); bmnglayer.minActiveAltitude = 0; wwd.addLayer(bmnglayer); // Don't want any imaging processing delays - // wwd.addLayer(new WorldWind.FrameStatisticsLayer(wwd)); - // wwd.redrawCallbacks.push(function (worldwindow, stage) { - // if (stage === WorldWind.AFTER_REDRAW) { - // worldwindow.redraw(); - // } - // }); return wwd; }; - Util.move = function (wwd, endStatesArray, complete) { - var idx = 0; + Util.prototype.onRedraw = function () { - var nextPosition = function (worldwindow, stage) { - if (stage !== WorldWind.AFTER_REDRAW) { - return; - } + var self = this; - var range, tilt, heading, latitude, longitude, endStates = endStatesArray[idx]; + return function (worldwindow, stage) { - if (endStates.range && !endStates.range.complete) { - range = Util.calculateNextValue(worldwindow.navigator.range, endStates.range.goal, endStates.range.step); - if (typeof range === "number") { - worldwindow.navigator.range = range; - } else { - endStates.range.complete = true; + if (self.moveQueue.length > 0 && stage === WorldWind.AFTER_REDRAW) { + + var range, tilt, heading, latitude, longitude, endStates = self.moveQueue[0]; + + if (endStates.range && !endStates.range.complete) { + range = Util.calculateNextValue(worldwindow.navigator.range, endStates.range.goal, endStates.range.step); + if (typeof range === "number") { + worldwindow.navigator.range = range; + } else { + endStates.range.complete = true; + } } - } - if (endStates.tilt && !endStates.tilt.complete) { - tilt = Util.calculateNextValue(worldwindow.navigator.tilt, endStates.tilt.goal, endStates.tilt.step); - if (typeof tilt === "number") { - worldwindow.navigator.tilt = tilt; - } else { - endStates.tilt.complete = true; + if (endStates.tilt && !endStates.tilt.complete) { + tilt = Util.calculateNextValue(worldwindow.navigator.tilt, endStates.tilt.goal, endStates.tilt.step); + if (typeof tilt === "number") { + worldwindow.navigator.tilt = tilt; + } else { + endStates.tilt.complete = true; + } } - } - if (endStates.heading && !endStates.heading.complete) { - heading = Util.calculateNextValue(worldwindow.navigator.heading, endStates.heading.goal, endStates.heading.step); - if (typeof heading === "number") { - worldwindow.navigator.heading = heading; - } else { - endStates.heading.complete = true; + if (endStates.heading && !endStates.heading.complete) { + heading = Util.calculateNextValue(worldwindow.navigator.heading, endStates.heading.goal, endStates.heading.step); + if (typeof heading === "number") { + worldwindow.navigator.heading = heading; + } else { + endStates.heading.complete = true; + } } - } - if (endStates.latitude && !endStates.latitude.complete) { - latitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.latitude, - endStates.latitude.goal, endStates.latitude.step); - if (typeof latitude === "number") { - worldwindow.navigator.lookAtLocation.latitude = latitude; - } else { - endStates.latitude.complete = true; + if (endStates.latitude && !endStates.latitude.complete) { + latitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.latitude, + endStates.latitude.goal, endStates.latitude.step); + if (typeof latitude === "number") { + worldwindow.navigator.lookAtLocation.latitude = latitude; + } else { + endStates.latitude.complete = true; + } } - } - if (endStates.longitude && !endStates.longitude.complete) { - longitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.longitude, - endStates.longitude.goal, endStates.longitude.step); - if (typeof longitude === "number") { - worldwindow.navigator.lookAtLocation.longitude = longitude; - } else { - endStates.longitude.complete = true; + if (endStates.longitude && !endStates.longitude.complete) { + longitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.longitude, + endStates.longitude.goal, endStates.longitude.step); + if (typeof longitude === "number") { + worldwindow.navigator.lookAtLocation.longitude = longitude; + } else { + endStates.longitude.complete = true; + } } - } - var keys = Object.getOwnPropertyNames(endStates); - for (var i = 0; i < keys.length; i++) { - if (!endStates[keys[i]].complete) { - worldwindow.redraw(); - return; + // Check to see if all the end states of this move have been completed + var keys = Object.getOwnPropertyNames(endStates); + for (var i = 0; i < keys.length; i++) { + + // skip the callback property + if (keys[i] === "onComplete") { + continue; + } + + if (!endStates[keys[i]].complete) { + // not done yet, don't complete the move yet + worldwindow.redraw(); + return; + } } - } - if (idx === endStatesArray.length - 1) { - var callbackIdx = worldwindow.redrawCallbacks.indexOf(nextPosition); - worldwindow.redrawCallbacks.splice(callbackIdx, 1); - worldwindow.redraw(); - complete(); - } else { - idx++; + // Remove the move from the queue and invoke the completion listener if provided + self.moveQueue.shift(); + if (endStates.onComplete) { + endStates.onComplete(); + } worldwindow.redraw(); } }; + }; + + Util.prototype.move = function (endStates) { + + if (Array.isArray(endStates)) { + endStates.forEach(function (endState) { + this.moveQueue.push(endState); + }, this); + } else { + this.moveQueue.push(endStates); + } + + this.wwd.redraw(); + }; + + Util.generateResultsSummary = function (dataArray, title) { + var min = Util.calculateMin(dataArray), max = Util.calculateMax(dataArray), average, stddev, bins; + + // calculate average + average = Util.calculateAverage(dataArray); + + // calculate standard deviation + stddev = Util.calculateStdDev(dataArray); + + // bin the data + bins = Util.binValues(dataArray, 2); + + // create the html elements displaying the data + var encDiv = document.createElement("div"); + var titleEl = document.createElement("h2"); + titleEl.appendChild(document.createTextNode(title)); + encDiv.appendChild(titleEl); + var list = document.createElement("ul"); + encDiv.appendChild(list); + var li = document.createElement("li"); + list.appendChild(li); + li.appendChild(document.createTextNode("Average: " + average)); + li = document.createElement("li"); + list.appendChild(li); + li.appendChild(document.createTextNode("Standard Deviation: " + stddev)); + li = document.createElement("li"); + list.appendChild(li); + li.appendChild(document.createTextNode("Max: " + max)); + li = document.createElement("li"); + list.appendChild(li); + li.appendChild(document.createTextNode("Min: " + min)); + + var canvas = Util.generateSimplePlot(bins, 500, 500, true); + encDiv.appendChild(canvas); + + return encDiv; + }; + + Util.calculateMax = function (values) { + var max = -Number.MAX_VALUE; + + values.forEach(function (value) { + max = Math.max(max, value); + }); + + return max; + }; + + Util.calculateMin = function (values) { + var min = Number.MAX_VALUE; + + values.forEach(function (value) { + min = Math.min(min, value); + }); + + return min; + }; + + Util.calculateAverage = function (values) { + var total = 0; + values.forEach(function (value) { + total += value; + }); + + return total / values.length; + }; + + Util.calculateStdDev = function (values) { + var avg = Util.calculateAverage(values), total = 0, diff; + + values.forEach(function (value) { + diff = value - avg; + total += diff * diff; + }); + + return Math.sqrt(total / values.length); + }; + + Util.binValues = function (values, binSize) { + var i, idx, min = Util.calculateMin(values), max = Util.calculateMax(values), binRange = (max - min), + len = values.length, binCount = Math.ceil(binRange / binSize), bins; + + bins = new Array(binCount + 1); + + WWUtil.fillArray(bins, 0); + for (i = 0; i < len; i++) { + idx = Math.floor((values[i] - min) / binSize); // TODO check if this should be floor or round + bins[idx]++; + } + + return bins; + }; + + Util.generateSimplePlot = function (data, sizeX, sizeY, reverseData) { + var canvas = document.createElement("canvas"), xScale, yScale, i, x, y, ctx, max = Util.calculateMax(data), + min = Util.calculateMin(data), dataPoints = data.length; + + // setup canvas element + canvas.setAttribute("width", sizeX); + canvas.setAttribute("height", sizeY); + ctx = canvas.getContext("2d"); + + // setup scaling values + xScale = sizeX / dataPoints; + yScale = sizeY / (max - min); + + // plot data + x = 0; + y = data[0] * yScale; + if (reverseData) { + y = sizeY - y; + } + ctx.beginPath(); + ctx.moveTo(x, y); + + for (i = 1; i < dataPoints; i++) { + x = xScale * i; + y = data[i] * yScale; + if (reverseData) { + y = sizeY - y; + } + ctx.lineTo(x, y); + } + ctx.stroke(); - wwd.redrawCallbacks.push(nextPosition); - wwd.redraw(); + return canvas; }; + // Internal use only Util.calculateNextValue = function (currentValue, goal, step) { var diff = currentValue - goal; From ec27bb2e253e217ed74107cef2398c41d4c6ea13 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Fri, 18 May 2018 15:16:26 -0500 Subject: [PATCH 09/22] Update layout --- functionalTest/SurfaceShapes.html | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index 50a5ec79b..1e8275b07 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -16,23 +16,12 @@ width: 100%; height: 100vh; } - #buttons { - position: absolute; - top: 30px; - left: 30px; - z-index: 10000; + padding-top: 10px; } -
-
- - - -
-
@@ -41,12 +30,20 @@
-

+
+ + + +
+
+
+

-

-
-
+

+
+
+
From f1554276e2cbced2dbf34e9b22c58d666407e87d Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Fri, 18 May 2018 15:19:31 -0500 Subject: [PATCH 10/22] Update static shape generation region --- functionalTest/SurfaceShapes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 4d5f5d1d4..58eb22b5c 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -79,8 +79,8 @@ requirejs([ var onGenerateShapesClick = function () { var layer = new WorldWind.RenderableLayer("Test Layer"); wwd.addLayer(layer); - for (var i = 0; i < 2000; i++) { - var shapeLocation = new WorldWind.Location(Math.random() * 45 + 15.0, -80 - Math.random() * 90); + for (var i = 0; i < 10000; i++) { + var shapeLocation = new WorldWind.Location(Math.random() * 160 - 80, Math.random() * 340 - 170); var minorAxis = Math.random() * 100000 + 10000; var majorAxis = Math.random() * 500000 + 10000; var heading = Math.random() * 360; From 8ddf26c9dfe235d81db5b06b3fa753af4f22f796 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Fri, 18 May 2018 15:47:36 -0500 Subject: [PATCH 11/22] Add initial copy to clipboard functionality --- functionalTest/SurfaceShapes.html | 4 ++++ functionalTest/Util.js | 37 +++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index 1e8275b07..658d88259 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -19,6 +19,10 @@ #buttons { padding-top: 10px; } + textarea { + width: 100%; + padding: 5px; + } diff --git a/functionalTest/Util.js b/functionalTest/Util.js index edb6b1481..d54920e9e 100644 --- a/functionalTest/Util.js +++ b/functionalTest/Util.js @@ -130,7 +130,7 @@ define([ stddev = Util.calculateStdDev(dataArray); // bin the data - bins = Util.binValues(dataArray, 2); + bins = Util.binValues(dataArray, 5); // create the html elements displaying the data var encDiv = document.createElement("div"); @@ -155,6 +155,8 @@ define([ var canvas = Util.generateSimplePlot(bins, 500, 500, true); encDiv.appendChild(canvas); + encDiv.appendChild(Util.generateTextOutput(dataArray)); + return encDiv; }; @@ -248,6 +250,37 @@ define([ return canvas; }; + Util.generateTextOutput = function (data) { + var textOutput = document.createElement("textarea"); + textOutput.value = data.join(","); + textOutput.setAttribute("id", "text-data-output"); + + var copyToClipboardButton = document.createElement("button"); + copyToClipboardButton.appendChild(document.createTextNode("Copy to Clipboard")); + copyToClipboardButton.addEventListener("click", function () { + textOutput.select(); + + try { + var successful = document.execCommand('copy'); + var msg = successful ? 'successful' : 'unsuccessful'; + console.log('Copying text command was ' + msg); + } catch (err) { + console.log('Oops, unable to copy'); + } + }); + + var outputDiv = document.createElement("div"); + outputDiv.appendChild(textOutput); + var buttonDiv = document.createElement("div"); + buttonDiv.appendChild(copyToClipboardButton); + + var div = document.createElement("div"); + div.appendChild(outputDiv); + div.appendChild(buttonDiv); + + return div; + }; + // Internal use only Util.calculateNextValue = function (currentValue, goal, step) { var diff = currentValue - goal; @@ -262,6 +295,6 @@ define([ return null; } }; - + return Util; }); From f67f896b9985df94522c89295d028d555a3ca8a1 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Sun, 20 May 2018 22:12:55 -0500 Subject: [PATCH 12/22] Refactor status and results output to utility class --- functionalTest/SurfaceShapes.html | 8 ++++---- functionalTest/SurfaceShapes.js | 23 ++++++----------------- functionalTest/Util.js | 27 +++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index 658d88259..f5a50d0b3 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -38,14 +38,14 @@ -
+
-

+
-

+

-
+
diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 58eb22b5c..fc238cf10 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -5,24 +5,13 @@ requirejs([ function (WorldWind, Util) { "use strict"; - var statusDialog = document.getElementById("status-dialog"); - var statusOutput = document.getElementById("output"); var navigateButton = document.getElementById("navigate-button"); var staticShapesButton = document.getElementById("static-button"); - var updateStatus = function (statusMessage) { - var children = statusDialog.childNodes; - for (var c = 0; c < children.length; c++) { - statusDialog.removeChild(children[c]); - } - - statusDialog.appendChild(document.createTextNode(statusMessage)); - }; - - updateStatus("Initializing globe..."); var wwd = Util.initializeLowResourceWorldWindow("globe"); var util = new Util(wwd); - updateStatus("Globe Loaded, ready for testing"); + + util.setStatusMessage("Globe loaded, ready for testing..."); // moving state var moving = false; @@ -39,7 +28,7 @@ requirejs([ step: 0.75 }, onComplete: function () { - updateStatus("First move complete..."); + util.setStatusMessage("First move complete..."); } }; @@ -49,7 +38,7 @@ requirejs([ step: 1 }, onComplete: function () { - updateStatus("Second move complete"); + util.setStatusMessage("Second move complete"); } }; @@ -65,7 +54,7 @@ requirejs([ onComplete: function () { moving = false; navigationComplete = true; - updateStatus("Move Complete"); + util.setStatusMessage("Move Complete"); } }; @@ -105,7 +94,7 @@ requirejs([ // clean up the data stats.shift(); stats.pop(); - statusOutput.appendChild(Util.generateResultsSummary(stats, "Frame Times")); + util.setOutputMessage(Util.generateResultsSummary(stats, "Frame Times")); } } }; diff --git a/functionalTest/Util.js b/functionalTest/Util.js index d54920e9e..4f7e727f9 100644 --- a/functionalTest/Util.js +++ b/functionalTest/Util.js @@ -12,6 +12,10 @@ define([ this.wwd.redrawCallbacks.push(this.onRedraw()); this.moveQueue = []; + + this.statusOutput = document.getElementById("status-output"); + + this.resultsOutput = document.getElementById("results-output"); }; Util.initializeLowResourceWorldWindow = function (canvasId) { @@ -24,6 +28,29 @@ define([ return wwd; }; + Util.prototype.setStatusMessage = function (value) { + Util.setElementValue(this.statusOutput, value); + }; + + Util.prototype.setOutputMessage = function (value) { + Util.setElementValue(this.resultsOutput, value); + }; + + Util.setElementValue = function (element, value) { + var children = element.childNodes; + + // remove existing nodes + for (var c = 0; c < children.length; c++) { + element.removeChild(children[c]); + } + + if (typeof value === "string") { + value = document.createElement("h3").appendChild(document.createTextNode(value)); + } + + element.appendChild(value); + }; + Util.prototype.onRedraw = function () { var self = this; From 5ca89e7281a02db06fbf8730a9b8cf13d22adc85 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Mon, 21 May 2018 15:07:58 -0500 Subject: [PATCH 13/22] Add dynamic shapes --- functionalTest/SurfaceShapes.html | 2 +- functionalTest/SurfaceShapes.js | 236 ++++++++++++++++++++++++------ 2 files changed, 195 insertions(+), 43 deletions(-) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index f5a50d0b3..9e45899cf 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -37,7 +37,7 @@
- +

diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index fc238cf10..ab1f8160f 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -7,17 +7,39 @@ requirejs([ var navigateButton = document.getElementById("navigate-button"); var staticShapesButton = document.getElementById("static-button"); + var dynamicShapesButton = document.getElementById("dynamic-button"); var wwd = Util.initializeLowResourceWorldWindow("globe"); + var testLayer = new WorldWind.RenderableLayer("Test Layer"); + wwd.addLayer(testLayer); var util = new Util(wwd); util.setStatusMessage("Globe loaded, ready for testing..."); - // moving state - var moving = false; - var navigationComplete = false; - // moves + var moveZero = { + latitude: { + goal: wwd.navigator.lookAtLocation.latitude, + step: Number.MAX_VALUE + }, + longitude: { + goal: wwd.navigator.lookAtLocation.longitude, + step: Number.MAX_VALUE + }, + tilt: { + goal: 0, + step: Number.MAX_VALUE + }, + heading: { + goal: 0, + step: Number.MAX_VALUE + }, + range: { + goal: wwd.navigator.range, + step: Number.MAX_VALUE + } + }; + var moveOne = { range: { goal: 8e5, @@ -34,8 +56,8 @@ requirejs([ var moveTwo = { heading: { - goal: 135, - step: 1 + goal: 90, + step: 0.75 }, onComplete: function () { util.setStatusMessage("Second move complete"); @@ -49,57 +71,187 @@ requirejs([ }, longitude: { goal: -70, - step: 0.25 + step: 0.5 }, onComplete: function () { - moving = false; - navigationComplete = true; + util.stopMetricCapture(); util.setStatusMessage("Move Complete"); + + // output metrics + var stats = util.frameStats.map(function (frameStat) { + return frameStat.frameTime; + }); + stats.shift(); + stats.pop(); + util.setOutputMessage(Util.generateResultsSummary(stats, "Frame Time")); + stats = util.frameStats.map(function (frameStat) { + return frameStat.layerRenderingTime; + }); + stats.shift(); + stats.pop(); + util.appendOutputMessage(Util.generateResultsSummary(stats, "Layer Rendering Time")); } }; - var onClickMove = function () { - if (!moving) { - moving = true; - util.move([moveOne, moveTwo, moveThree]); + + + var generateShapeAttributes = function () { + var sa = new WorldWind.ShapeAttributes(); + var r = Math.random(); + + sa.drawInterior = false; + sa.drawOutline = false; + + if (r < 0.3333) { + sa.drawInterior = true; + sa.interiorColor = new WorldWind.Color(Math.random(), Math.random(), Math.random(), Math.random()); + } else if (r < 0.6666) { + sa.drawOutline = true; + sa.outlineColor = new WorldWind.Color(Math.random(), Math.random(), Math.random(), Math.random()); + } else { + sa.drawInterior = true; + sa.interiorColor = new WorldWind.Color(Math.random(), Math.random(), Math.random(), Math.random()); + sa.drawOutline = true; + sa.outlineColor = new WorldWind.Color(Math.random(), Math.random(), Math.random(), Math.random()); } + + return sa; }; - var onGenerateShapesClick = function () { - var layer = new WorldWind.RenderableLayer("Test Layer"); - wwd.addLayer(layer); - for (var i = 0; i < 10000; i++) { - var shapeLocation = new WorldWind.Location(Math.random() * 160 - 80, Math.random() * 340 - 170); - var minorAxis = Math.random() * 100000 + 10000; - var majorAxis = Math.random() * 500000 + 10000; - var heading = Math.random() * 360; - layer.addRenderable(new WorldWind.SurfaceEllipse(shapeLocation, majorAxis, minorAxis, heading)); - } - wwd.redraw(); + var generateLocation = function () { + var lat = 180 * Math.random() - 90; + var lon = 360 * Math.random() - 180; + + return new WorldWind.Location(lat, lon); }; - // Temporary metric capture framework - var stats = [], min = Number.MAX_VALUE, max = -Number.MAX_VALUE; - var metricCapture = function (worldwindow, stage) { - if (stage === WorldWind.AFTER_REDRAW) { - if (moving && !navigationComplete) { - var frametime = worldwindow.frameStatistics.frameTime; - stats.push(frametime); - min = Math.min(min, frametime); - max = Math.max(max, frametime); + var generateShapes = function (count) { + var sa, shape, sector, radius, topLocation, lat, lon, locations, minorAxis, majorAxis, heading, i, j; + + // Surface Circles + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + radius = Math.random() * 1000000; + shape = new WorldWind.SurfaceCircle(generateLocation(), radius, sa); + testLayer.addRenderable(shape); + } + + // Surface Ellipse + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + majorAxis = Math.random() * 1000000; + minorAxis = Math.random() * 500000; + heading = Math.random() * 360; + shape = new WorldWind.SurfaceEllipse(generateLocation(), majorAxis, minorAxis, heading, sa); + testLayer.addRenderable(shape); + } + + // Surface Polygon + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + topLocation = generateLocation(); + locations = []; + locations.push(topLocation); + for (j = 0; j < 2; j++) { + lat = Math.min(90, Math.max(-90, topLocation.latitude - Math.random() * 8)); + lon = Math.min(180, Math.max(-180, topLocation.longitude - Math.random() * 8)); + locations.push(new WorldWind.Location(lat, lon)); } + shape = new WorldWind.SurfacePolygon(locations, sa); + testLayer.addRenderable(shape); + } - if (navigationComplete) { - navigationComplete = false; - // clean up the data - stats.shift(); - stats.pop(); - util.setOutputMessage(Util.generateResultsSummary(stats, "Frame Times")); + // Surface Polyline + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + topLocation = generateLocation(); + locations = []; + locations.push(topLocation); + for (j = 0; j < 2; j++) { + lat = Math.min(90, Math.max(-90, topLocation.latitude - Math.random() * 8)); + lon = Math.min(180, Math.max(-180, topLocation.longitude - Math.random() * 8)); + locations.push(new WorldWind.Location(lat, lon)); } + shape = new WorldWind.SurfacePolyline(locations, sa); + testLayer.addRenderable(shape); + } + + // Surface Rectangle + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + majorAxis = 1000000 * Math.random(); + minorAxis = 500000 * Math.random(); + heading = 360 * Math.random(); + shape = new WorldWind.SurfaceRectangle(generateLocation(), majorAxis, minorAxis, heading, sa); + testLayer.addRenderable(shape); } + + // Surface Sector + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + topLocation = generateLocation(); + majorAxis = 8 * Math.random(); + minorAxis = 4 * Math.random(); + sector = new WorldWind.Sector(topLocation.latitude, topLocation.latitude + majorAxis, + topLocation.longitude, topLocation.longitude + minorAxis); + shape = new WorldWind.SurfaceSector(sector, sa); + } + + wwd.redraw(); + }; + + var onMoveClick = function () { + if (!util.isMoving()) { + util.startMetricCapture(); + util.move([moveZero, moveOne, moveTwo, moveThree]); + } + }; + + var onStaticShapesClick = function () { + generateShapes(1000); + }; + + var onDynamicShapesClick = function () { + generateShapes(100); + + var onRedrawMoveShapes = function (worldwindow, stage) { + if (stage === WorldWind.AFTER_REDRAW) { + var idx = worldwindow.layers.indexOf(testLayer); + + if (idx >= 0) { + var count = worldwindow.layers[idx].renderables.length; + + for (var i = 0; i < count; i++) { + var shape = worldwindow.layers[idx].renderables[i]; + + if (shape instanceof WorldWind.SurfaceCircle || shape instanceof WorldWind.SurfaceEllipse + || shape instanceof WorldWind.SurfaceRectangle) { + var center = shape.center; + center.latitude += Math.random() * 0.5 - 0.25; + center.longitude += Math.random() * 0.5 - 0.25; + shape.center = center; + } else if (shape instanceof WorldWind.SurfacePolygon + || shape instanceof WorldWind.SurfacePolyline) { + var boundaries = shape.boundaries; + boundaries[0].latitude += Math.random() * 0.5 - 0.25; + boundaries[0].longitude += Math.random() * 0.5 - 0.25; + shape.boundaries = boundaries; + } else if (shape instanceof WorldWind.SurfaceSector) { + var sector = shape.sector; + sector.minLongitude += Math.random() * 0.5 - 0.25; + sector.minLatitude += Math.random() * 0.5 - 0.25; + shape.sector = sector; + } + + } + } + } + }; + + wwd.redrawCallbacks.push(onRedrawMoveShapes); }; - wwd.redrawCallbacks.push(metricCapture); - navigateButton.addEventListener("click", onClickMove); - staticShapesButton.addEventListener("click", onGenerateShapesClick); + navigateButton.addEventListener("click", onMoveClick); + staticShapesButton.addEventListener("click", onStaticShapesClick); + dynamicShapesButton.addEventListener("click", onDynamicShapesClick); }); From 6f7e9d77474885834807f1b3c7bc5d419dfd2f91 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Mon, 21 May 2018 15:13:18 -0500 Subject: [PATCH 14/22] Refactor status results output --- functionalTest/Util.js | 60 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/functionalTest/Util.js b/functionalTest/Util.js index 4f7e727f9..fa2334fa2 100644 --- a/functionalTest/Util.js +++ b/functionalTest/Util.js @@ -9,10 +9,16 @@ define([ this.wwd = worldwindow; - this.wwd.redrawCallbacks.push(this.onRedraw()); - this.moveQueue = []; + this.frameStats = []; + + this.captureMetrics = false; + + this.wwd.redrawCallbacks.push(this.onRedrawMove()); + + this.wwd.redrawCallbacks.push(this.onRedrawMetricCapture()); + this.statusOutput = document.getElementById("status-output"); this.resultsOutput = document.getElementById("results-output"); @@ -24,10 +30,15 @@ define([ var bmnglayer = new WorldWind.BMNGOneImageLayer(); bmnglayer.minActiveAltitude = 0; wwd.addLayer(bmnglayer); // Don't want any imaging processing delays + wwd.redraw(); return wwd; }; + Util.prototype.isMoving = function () { + return this.moveQueue.length > 0; + }; + Util.prototype.setStatusMessage = function (value) { Util.setElementValue(this.statusOutput, value); }; @@ -36,6 +47,11 @@ define([ Util.setElementValue(this.resultsOutput, value); }; + Util.prototype.appendOutputMessage = function (value) { + this.resultsOutput.appendChild(document.createElement("hr")); + Util.appendElementValue(this.resultsOutput, value); + }; + Util.setElementValue = function (element, value) { var children = element.childNodes; @@ -44,6 +60,10 @@ define([ element.removeChild(children[c]); } + Util.appendElementValue(element, value); + }; + + Util.appendElementValue = function (element, value) { if (typeof value === "string") { value = document.createElement("h3").appendChild(document.createTextNode(value)); } @@ -51,7 +71,7 @@ define([ element.appendChild(value); }; - Util.prototype.onRedraw = function () { + Util.prototype.onRedrawMove = function () { var self = this; @@ -134,6 +154,31 @@ define([ }; }; + Util.prototype.onRedrawMetricCapture = function () { + + var self = this; + + return function (worldwindow, stage) { + + if (self.captureMetrics && stage === WorldWind.AFTER_REDRAW) { + var frameStatistics = {}; + + frameStatistics.frameTime = worldwindow.frameStatistics.frameTime; + frameStatistics.tesselationTime = worldwindow.frameStatistics.tessellationTime; + frameStatistics.layerRenderingTime = worldwindow.frameStatistics.layerRenderingTime; + frameStatistics.orderedRenderingTime = worldwindow.frameStatistics.orderedRenderingTime; + frameStatistics.terrainTileCount = worldwindow.frameStatistics.terrainTileCount; + frameStatistics.imageTileCount = worldwindow.frameStatistics.imageTileCount; + frameStatistics.renderedTileCount = worldwindow.frameStatistics.renderedTileCount; + frameStatistics.tileUpdateCount = worldwindow.frameStatistics.tileUpdateCount; + frameStatistics.textureLoadCount = worldwindow.frameStatistics.textureLoadCount; + frameStatistics.vboLoadCount = worldwindow.frameStatistics.vboLoadCount; + + self.frameStats.push(frameStatistics); + } + }; + }; + Util.prototype.move = function (endStates) { if (Array.isArray(endStates)) { @@ -147,6 +192,15 @@ define([ this.wwd.redraw(); }; + Util.prototype.startMetricCapture = function () { + this.frameStats = []; + this.captureMetrics = true; + }; + + Util.prototype.stopMetricCapture = function () { + this.captureMetrics = false; + }; + Util.generateResultsSummary = function (dataArray, title) { var min = Util.calculateMin(dataArray), max = Util.calculateMax(dataArray), average, stddev, bins; From c93abf09151f4b42f67e6cfeb93811ae0d18f1ec Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Mon, 21 May 2018 15:31:32 -0500 Subject: [PATCH 15/22] Add comments --- functionalTest/SurfaceShapes.html | 21 +++++++++++++++++---- functionalTest/SurfaceShapes.js | 22 ++++++++++++++++------ functionalTest/Util.js | 4 +++- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index 9e45899cf..7f5eafd04 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -16,9 +16,6 @@ width: 100%; height: 100vh; } - #buttons { - padding-top: 10px; - } textarea { width: 100%; padding: 5px; @@ -28,18 +25,34 @@
+ +
Browser Does Not Support HTML5
+ +
+ + +
+

+ Test Options +

+

+ Reload the page between tests. +

+
- +

+ +
diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index ab1f8160f..440d5fdcc 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -5,10 +5,11 @@ requirejs([ function (WorldWind, Util) { "use strict"; - var navigateButton = document.getElementById("navigate-button"); - var staticShapesButton = document.getElementById("static-button"); - var dynamicShapesButton = document.getElementById("dynamic-button"); + // Define shape counts for testing + var dynamicShapeCount = 100; // of each type of shape (circle, ellipse, etc.) + var staticShapeCount = 1000; // of each type of shape (circle, ellipse, etc.) + // Initialize Globe var wwd = Util.initializeLowResourceWorldWindow("globe"); var testLayer = new WorldWind.RenderableLayer("Test Layer"); wwd.addLayer(testLayer); @@ -16,7 +17,7 @@ requirejs([ util.setStatusMessage("Globe loaded, ready for testing..."); - // moves + // Navigation Moves for Testing var moveZero = { latitude: { goal: wwd.navigator.lookAtLocation.latitude, @@ -93,8 +94,7 @@ requirejs([ } }; - - + // Utility functions var generateShapeAttributes = function () { var sa = new WorldWind.ShapeAttributes(); var r = Math.random(); @@ -200,6 +200,7 @@ requirejs([ wwd.redraw(); }; + // Click Event Callbacks var onMoveClick = function () { if (!util.isMoving()) { util.startMetricCapture(); @@ -209,6 +210,8 @@ requirejs([ var onStaticShapesClick = function () { generateShapes(1000); + wwd.redraw(); + onMoveClick(); }; var onDynamicShapesClick = function () { @@ -249,8 +252,15 @@ requirejs([ }; wwd.redrawCallbacks.push(onRedrawMoveShapes); + wwd.redraw(); + onMoveClick(); }; + // Click Event Assignments + var navigateButton = document.getElementById("navigate-button"); + var staticShapesButton = document.getElementById("static-button"); + var dynamicShapesButton = document.getElementById("dynamic-button"); + navigateButton.addEventListener("click", onMoveClick); staticShapesButton.addEventListener("click", onStaticShapesClick); dynamicShapesButton.addEventListener("click", onDynamicShapesClick); diff --git a/functionalTest/Util.js b/functionalTest/Util.js index fa2334fa2..a3b7ed446 100644 --- a/functionalTest/Util.js +++ b/functionalTest/Util.js @@ -161,6 +161,8 @@ define([ return function (worldwindow, stage) { if (self.captureMetrics && stage === WorldWind.AFTER_REDRAW) { + + // Copy all of the individual frame metrics var frameStatistics = {}; frameStatistics.frameTime = worldwindow.frameStatistics.frameTime; @@ -210,7 +212,7 @@ define([ // calculate standard deviation stddev = Util.calculateStdDev(dataArray); - // bin the data + // bin the data on 5 second bins bins = Util.binValues(dataArray, 5); // create the html elements displaying the data From 75e6ba65bc7f6f17fe0fd5f4ad8e899260a460ea Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Mon, 21 May 2018 16:56:51 -0500 Subject: [PATCH 16/22] Add PRNG and comments --- functionalTest/SurfaceShapes.html | 8 +++- functionalTest/SurfaceShapes.js | 70 ++++++++++++++++++------------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapes.html index 7f5eafd04..09195dc46 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapes.html @@ -39,13 +39,17 @@

- Test Options + Surface Shape Performance Tests

- Reload the page between tests. + Reload the page between tests. Once the test has been completed, a statistical summary of the frame + times and a distribution of the frame times grouped by five second durations is displayed. Below the + frame time distribution plot, a text area with the actual frame times is displayed as well as a + button which automates copying of the data to the clipboard.

+

Available Tests:

diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapes.js index 440d5fdcc..95a6ecf94 100644 --- a/functionalTest/SurfaceShapes.js +++ b/functionalTest/SurfaceShapes.js @@ -84,43 +84,52 @@ requirejs([ }); stats.shift(); stats.pop(); - util.setOutputMessage(Util.generateResultsSummary(stats, "Frame Time")); + util.setOutputMessage(Util.generateResultsSummary(stats, "Frame Times")); stats = util.frameStats.map(function (frameStat) { return frameStat.layerRenderingTime; }); - stats.shift(); - stats.pop(); - util.appendOutputMessage(Util.generateResultsSummary(stats, "Layer Rendering Time")); } }; // Utility functions + + // This is a pseudo random number generator which allows a seed, providing repeatable conditions + var nextFloat = function (seed) { + seed = seed % 2147483647; + return function () { + seed = seed * 16807 % 2147483647; + return (seed - 1) / 2147483646; + }; + }; + // Assign the PRNG to the function which will be used by the testing for content generation + var rand = nextFloat(1234); + var generateShapeAttributes = function () { var sa = new WorldWind.ShapeAttributes(); - var r = Math.random(); + var r = rand(); sa.drawInterior = false; sa.drawOutline = false; if (r < 0.3333) { sa.drawInterior = true; - sa.interiorColor = new WorldWind.Color(Math.random(), Math.random(), Math.random(), Math.random()); + sa.interiorColor = new WorldWind.Color(rand(), rand(), rand(), rand()); } else if (r < 0.6666) { sa.drawOutline = true; - sa.outlineColor = new WorldWind.Color(Math.random(), Math.random(), Math.random(), Math.random()); + sa.outlineColor = new WorldWind.Color(rand(), rand(), rand(), rand()); } else { sa.drawInterior = true; - sa.interiorColor = new WorldWind.Color(Math.random(), Math.random(), Math.random(), Math.random()); + sa.interiorColor = new WorldWind.Color(rand(), rand(), rand(), rand()); sa.drawOutline = true; - sa.outlineColor = new WorldWind.Color(Math.random(), Math.random(), Math.random(), Math.random()); + sa.outlineColor = new WorldWind.Color(rand(), rand(), rand(), rand()); } return sa; }; var generateLocation = function () { - var lat = 180 * Math.random() - 90; - var lon = 360 * Math.random() - 180; + var lat = 180 * rand() - 90; + var lon = 360 * rand() - 180; return new WorldWind.Location(lat, lon); }; @@ -131,7 +140,7 @@ requirejs([ // Surface Circles for (i = 0; i < count; i++) { sa = generateShapeAttributes(); - radius = Math.random() * 1000000; + radius = rand() * 1000000; shape = new WorldWind.SurfaceCircle(generateLocation(), radius, sa); testLayer.addRenderable(shape); } @@ -139,9 +148,9 @@ requirejs([ // Surface Ellipse for (i = 0; i < count; i++) { sa = generateShapeAttributes(); - majorAxis = Math.random() * 1000000; - minorAxis = Math.random() * 500000; - heading = Math.random() * 360; + majorAxis = rand() * 1000000; + minorAxis = rand() * 500000; + heading = rand() * 360; shape = new WorldWind.SurfaceEllipse(generateLocation(), majorAxis, minorAxis, heading, sa); testLayer.addRenderable(shape); } @@ -153,8 +162,8 @@ requirejs([ locations = []; locations.push(topLocation); for (j = 0; j < 2; j++) { - lat = Math.min(90, Math.max(-90, topLocation.latitude - Math.random() * 8)); - lon = Math.min(180, Math.max(-180, topLocation.longitude - Math.random() * 8)); + lat = Math.min(90, Math.max(-90, topLocation.latitude - rand() * 8)); + lon = Math.min(180, Math.max(-180, topLocation.longitude - rand() * 8)); locations.push(new WorldWind.Location(lat, lon)); } shape = new WorldWind.SurfacePolygon(locations, sa); @@ -168,8 +177,8 @@ requirejs([ locations = []; locations.push(topLocation); for (j = 0; j < 2; j++) { - lat = Math.min(90, Math.max(-90, topLocation.latitude - Math.random() * 8)); - lon = Math.min(180, Math.max(-180, topLocation.longitude - Math.random() * 8)); + lat = Math.min(90, Math.max(-90, topLocation.latitude - rand() * 8)); + lon = Math.min(180, Math.max(-180, topLocation.longitude - rand() * 8)); locations.push(new WorldWind.Location(lat, lon)); } shape = new WorldWind.SurfacePolyline(locations, sa); @@ -179,9 +188,9 @@ requirejs([ // Surface Rectangle for (i = 0; i < count; i++) { sa = generateShapeAttributes(); - majorAxis = 1000000 * Math.random(); - minorAxis = 500000 * Math.random(); - heading = 360 * Math.random(); + majorAxis = 1000000 * rand(); + minorAxis = 500000 * rand(); + heading = 360 * rand(); shape = new WorldWind.SurfaceRectangle(generateLocation(), majorAxis, minorAxis, heading, sa); testLayer.addRenderable(shape); } @@ -190,11 +199,12 @@ requirejs([ for (i = 0; i < count; i++) { sa = generateShapeAttributes(); topLocation = generateLocation(); - majorAxis = 8 * Math.random(); - minorAxis = 4 * Math.random(); + majorAxis = 8 * rand(); + minorAxis = 4 * rand(); sector = new WorldWind.Sector(topLocation.latitude, topLocation.latitude + majorAxis, topLocation.longitude, topLocation.longitude + minorAxis); shape = new WorldWind.SurfaceSector(sector, sa); + testLayer.addRenderable(shape); } wwd.redraw(); @@ -230,19 +240,19 @@ requirejs([ if (shape instanceof WorldWind.SurfaceCircle || shape instanceof WorldWind.SurfaceEllipse || shape instanceof WorldWind.SurfaceRectangle) { var center = shape.center; - center.latitude += Math.random() * 0.5 - 0.25; - center.longitude += Math.random() * 0.5 - 0.25; + center.latitude += rand() * 0.5 - 0.25; + center.longitude += rand() * 0.5 - 0.25; shape.center = center; } else if (shape instanceof WorldWind.SurfacePolygon || shape instanceof WorldWind.SurfacePolyline) { var boundaries = shape.boundaries; - boundaries[0].latitude += Math.random() * 0.5 - 0.25; - boundaries[0].longitude += Math.random() * 0.5 - 0.25; + boundaries[0].latitude += rand() * 0.5 - 0.25; + boundaries[0].longitude += rand() * 0.5 - 0.25; shape.boundaries = boundaries; } else if (shape instanceof WorldWind.SurfaceSector) { var sector = shape.sector; - sector.minLongitude += Math.random() * 0.5 - 0.25; - sector.minLatitude += Math.random() * 0.5 - 0.25; + sector.minLongitude += rand() * 0.5 - 0.25; + sector.minLatitude += rand() * 0.5 - 0.25; shape.sector = sector; } From b4434b47ebf4be1cd633f7eddd41820a180f4d94 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Mon, 21 May 2018 23:40:11 -0500 Subject: [PATCH 17/22] Fix path case --- functionalTest/Util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functionalTest/Util.js b/functionalTest/Util.js index a3b7ed446..e5dc9e1b8 100644 --- a/functionalTest/Util.js +++ b/functionalTest/Util.js @@ -1,6 +1,6 @@ define([ '../src/WorldWind', - '../src/Util/WWUtil' + '../src/util/WWUtil' ], function (WorldWind, WWUtil) { "use strict"; From eee43527a59eaa712807776809a599b3687f0d8b Mon Sep 17 00:00:00 2001 From: David Collins Date: Tue, 22 May 2018 17:14:06 -0700 Subject: [PATCH 18/22] Registered the functionalTest folder as a WebStorm test root --- .idea/WebWorldWind.iml | 1 + 1 file changed, 1 insertion(+) diff --git a/.idea/WebWorldWind.iml b/.idea/WebWorldWind.iml index 68a7f7796..afa798668 100644 --- a/.idea/WebWorldWind.iml +++ b/.idea/WebWorldWind.iml @@ -3,6 +3,7 @@ + From 3f7dcf9aad8b7c92170e8fb55aab5ee3cb76ab75 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Wed, 23 May 2018 10:39:36 -0500 Subject: [PATCH 19/22] Change file names --- .../{SurfaceShapes.html => SurfaceShapePerformance.html} | 2 +- functionalTest/{SurfaceShapes.js => SurfaceShapePerformance.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename functionalTest/{SurfaceShapes.html => SurfaceShapePerformance.html} (93%) rename functionalTest/{SurfaceShapes.js => SurfaceShapePerformance.js} (100%) diff --git a/functionalTest/SurfaceShapes.html b/functionalTest/SurfaceShapePerformance.html similarity index 93% rename from functionalTest/SurfaceShapes.html rename to functionalTest/SurfaceShapePerformance.html index 09195dc46..16dc9f1fa 100644 --- a/functionalTest/SurfaceShapes.html +++ b/functionalTest/SurfaceShapePerformance.html @@ -73,6 +73,6 @@

Available Tests:

- + \ No newline at end of file diff --git a/functionalTest/SurfaceShapes.js b/functionalTest/SurfaceShapePerformance.js similarity index 100% rename from functionalTest/SurfaceShapes.js rename to functionalTest/SurfaceShapePerformance.js From 0d432f28d855f5ef78e5addcce90466fe6b93d5b Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Wed, 23 May 2018 11:10:58 -0500 Subject: [PATCH 20/22] Change directory structure to mimic src --- functionalTest/SurfaceShapePerformance.html | 78 ---- functionalTest/SurfaceShapePerformance.js | 277 -------------- functionalTest/Util.js | 383 -------------------- 3 files changed, 738 deletions(-) delete mode 100644 functionalTest/SurfaceShapePerformance.html delete mode 100644 functionalTest/SurfaceShapePerformance.js delete mode 100644 functionalTest/Util.js diff --git a/functionalTest/SurfaceShapePerformance.html b/functionalTest/SurfaceShapePerformance.html deleted file mode 100644 index 16dc9f1fa..000000000 --- a/functionalTest/SurfaceShapePerformance.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - Surface Shape Performance Functional Test - - - - -
-
- - -
- - Browser Does Not Support HTML5 - -
- - -
- - -
-

- Surface Shape Performance Tests -

-

- Reload the page between tests. Once the test has been completed, a statistical summary of the frame - times and a distribution of the frame times grouped by five second durations is displayed. Below the - frame time distribution plot, a text area with the actual frame times is displayed as well as a - button which automates copying of the data to the clipboard. -

-
-
-

Available Tests:

- - - -
-
- - -
-
- -
-
-
- -
-
-
-
-
- - - - - - - \ No newline at end of file diff --git a/functionalTest/SurfaceShapePerformance.js b/functionalTest/SurfaceShapePerformance.js deleted file mode 100644 index 95a6ecf94..000000000 --- a/functionalTest/SurfaceShapePerformance.js +++ /dev/null @@ -1,277 +0,0 @@ -requirejs([ - '../src/WorldWind', - './Util' - ], - function (WorldWind, Util) { - "use strict"; - - // Define shape counts for testing - var dynamicShapeCount = 100; // of each type of shape (circle, ellipse, etc.) - var staticShapeCount = 1000; // of each type of shape (circle, ellipse, etc.) - - // Initialize Globe - var wwd = Util.initializeLowResourceWorldWindow("globe"); - var testLayer = new WorldWind.RenderableLayer("Test Layer"); - wwd.addLayer(testLayer); - var util = new Util(wwd); - - util.setStatusMessage("Globe loaded, ready for testing..."); - - // Navigation Moves for Testing - var moveZero = { - latitude: { - goal: wwd.navigator.lookAtLocation.latitude, - step: Number.MAX_VALUE - }, - longitude: { - goal: wwd.navigator.lookAtLocation.longitude, - step: Number.MAX_VALUE - }, - tilt: { - goal: 0, - step: Number.MAX_VALUE - }, - heading: { - goal: 0, - step: Number.MAX_VALUE - }, - range: { - goal: wwd.navigator.range, - step: Number.MAX_VALUE - } - }; - - var moveOne = { - range: { - goal: 8e5, - step: 5e4 - }, - tilt: { - goal: 75, - step: 0.75 - }, - onComplete: function () { - util.setStatusMessage("First move complete..."); - } - }; - - var moveTwo = { - heading: { - goal: 90, - step: 0.75 - }, - onComplete: function () { - util.setStatusMessage("Second move complete"); - } - }; - - var moveThree = { - latitude: { - goal: wwd.navigator.lookAtLocation.latitude, - step: 1 - }, - longitude: { - goal: -70, - step: 0.5 - }, - onComplete: function () { - util.stopMetricCapture(); - util.setStatusMessage("Move Complete"); - - // output metrics - var stats = util.frameStats.map(function (frameStat) { - return frameStat.frameTime; - }); - stats.shift(); - stats.pop(); - util.setOutputMessage(Util.generateResultsSummary(stats, "Frame Times")); - stats = util.frameStats.map(function (frameStat) { - return frameStat.layerRenderingTime; - }); - } - }; - - // Utility functions - - // This is a pseudo random number generator which allows a seed, providing repeatable conditions - var nextFloat = function (seed) { - seed = seed % 2147483647; - return function () { - seed = seed * 16807 % 2147483647; - return (seed - 1) / 2147483646; - }; - }; - // Assign the PRNG to the function which will be used by the testing for content generation - var rand = nextFloat(1234); - - var generateShapeAttributes = function () { - var sa = new WorldWind.ShapeAttributes(); - var r = rand(); - - sa.drawInterior = false; - sa.drawOutline = false; - - if (r < 0.3333) { - sa.drawInterior = true; - sa.interiorColor = new WorldWind.Color(rand(), rand(), rand(), rand()); - } else if (r < 0.6666) { - sa.drawOutline = true; - sa.outlineColor = new WorldWind.Color(rand(), rand(), rand(), rand()); - } else { - sa.drawInterior = true; - sa.interiorColor = new WorldWind.Color(rand(), rand(), rand(), rand()); - sa.drawOutline = true; - sa.outlineColor = new WorldWind.Color(rand(), rand(), rand(), rand()); - } - - return sa; - }; - - var generateLocation = function () { - var lat = 180 * rand() - 90; - var lon = 360 * rand() - 180; - - return new WorldWind.Location(lat, lon); - }; - - var generateShapes = function (count) { - var sa, shape, sector, radius, topLocation, lat, lon, locations, minorAxis, majorAxis, heading, i, j; - - // Surface Circles - for (i = 0; i < count; i++) { - sa = generateShapeAttributes(); - radius = rand() * 1000000; - shape = new WorldWind.SurfaceCircle(generateLocation(), radius, sa); - testLayer.addRenderable(shape); - } - - // Surface Ellipse - for (i = 0; i < count; i++) { - sa = generateShapeAttributes(); - majorAxis = rand() * 1000000; - minorAxis = rand() * 500000; - heading = rand() * 360; - shape = new WorldWind.SurfaceEllipse(generateLocation(), majorAxis, minorAxis, heading, sa); - testLayer.addRenderable(shape); - } - - // Surface Polygon - for (i = 0; i < count; i++) { - sa = generateShapeAttributes(); - topLocation = generateLocation(); - locations = []; - locations.push(topLocation); - for (j = 0; j < 2; j++) { - lat = Math.min(90, Math.max(-90, topLocation.latitude - rand() * 8)); - lon = Math.min(180, Math.max(-180, topLocation.longitude - rand() * 8)); - locations.push(new WorldWind.Location(lat, lon)); - } - shape = new WorldWind.SurfacePolygon(locations, sa); - testLayer.addRenderable(shape); - } - - // Surface Polyline - for (i = 0; i < count; i++) { - sa = generateShapeAttributes(); - topLocation = generateLocation(); - locations = []; - locations.push(topLocation); - for (j = 0; j < 2; j++) { - lat = Math.min(90, Math.max(-90, topLocation.latitude - rand() * 8)); - lon = Math.min(180, Math.max(-180, topLocation.longitude - rand() * 8)); - locations.push(new WorldWind.Location(lat, lon)); - } - shape = new WorldWind.SurfacePolyline(locations, sa); - testLayer.addRenderable(shape); - } - - // Surface Rectangle - for (i = 0; i < count; i++) { - sa = generateShapeAttributes(); - majorAxis = 1000000 * rand(); - minorAxis = 500000 * rand(); - heading = 360 * rand(); - shape = new WorldWind.SurfaceRectangle(generateLocation(), majorAxis, minorAxis, heading, sa); - testLayer.addRenderable(shape); - } - - // Surface Sector - for (i = 0; i < count; i++) { - sa = generateShapeAttributes(); - topLocation = generateLocation(); - majorAxis = 8 * rand(); - minorAxis = 4 * rand(); - sector = new WorldWind.Sector(topLocation.latitude, topLocation.latitude + majorAxis, - topLocation.longitude, topLocation.longitude + minorAxis); - shape = new WorldWind.SurfaceSector(sector, sa); - testLayer.addRenderable(shape); - } - - wwd.redraw(); - }; - - // Click Event Callbacks - var onMoveClick = function () { - if (!util.isMoving()) { - util.startMetricCapture(); - util.move([moveZero, moveOne, moveTwo, moveThree]); - } - }; - - var onStaticShapesClick = function () { - generateShapes(1000); - wwd.redraw(); - onMoveClick(); - }; - - var onDynamicShapesClick = function () { - generateShapes(100); - - var onRedrawMoveShapes = function (worldwindow, stage) { - if (stage === WorldWind.AFTER_REDRAW) { - var idx = worldwindow.layers.indexOf(testLayer); - - if (idx >= 0) { - var count = worldwindow.layers[idx].renderables.length; - - for (var i = 0; i < count; i++) { - var shape = worldwindow.layers[idx].renderables[i]; - - if (shape instanceof WorldWind.SurfaceCircle || shape instanceof WorldWind.SurfaceEllipse - || shape instanceof WorldWind.SurfaceRectangle) { - var center = shape.center; - center.latitude += rand() * 0.5 - 0.25; - center.longitude += rand() * 0.5 - 0.25; - shape.center = center; - } else if (shape instanceof WorldWind.SurfacePolygon - || shape instanceof WorldWind.SurfacePolyline) { - var boundaries = shape.boundaries; - boundaries[0].latitude += rand() * 0.5 - 0.25; - boundaries[0].longitude += rand() * 0.5 - 0.25; - shape.boundaries = boundaries; - } else if (shape instanceof WorldWind.SurfaceSector) { - var sector = shape.sector; - sector.minLongitude += rand() * 0.5 - 0.25; - sector.minLatitude += rand() * 0.5 - 0.25; - shape.sector = sector; - } - - } - } - } - }; - - wwd.redrawCallbacks.push(onRedrawMoveShapes); - wwd.redraw(); - onMoveClick(); - }; - - // Click Event Assignments - var navigateButton = document.getElementById("navigate-button"); - var staticShapesButton = document.getElementById("static-button"); - var dynamicShapesButton = document.getElementById("dynamic-button"); - - navigateButton.addEventListener("click", onMoveClick); - staticShapesButton.addEventListener("click", onStaticShapesClick); - dynamicShapesButton.addEventListener("click", onDynamicShapesClick); - }); diff --git a/functionalTest/Util.js b/functionalTest/Util.js deleted file mode 100644 index e5dc9e1b8..000000000 --- a/functionalTest/Util.js +++ /dev/null @@ -1,383 +0,0 @@ -define([ - '../src/WorldWind', - '../src/util/WWUtil' - ], - function (WorldWind, WWUtil) { - "use strict"; - - var Util = function (worldwindow) { - - this.wwd = worldwindow; - - this.moveQueue = []; - - this.frameStats = []; - - this.captureMetrics = false; - - this.wwd.redrawCallbacks.push(this.onRedrawMove()); - - this.wwd.redrawCallbacks.push(this.onRedrawMetricCapture()); - - this.statusOutput = document.getElementById("status-output"); - - this.resultsOutput = document.getElementById("results-output"); - }; - - Util.initializeLowResourceWorldWindow = function (canvasId) { - var wwd = new WorldWind.WorldWindow(canvasId); - wwd.globe.elevationModel.removeAllCoverages(); // Don't want delays associated with parsing and changing terrain - var bmnglayer = new WorldWind.BMNGOneImageLayer(); - bmnglayer.minActiveAltitude = 0; - wwd.addLayer(bmnglayer); // Don't want any imaging processing delays - wwd.redraw(); - - return wwd; - }; - - Util.prototype.isMoving = function () { - return this.moveQueue.length > 0; - }; - - Util.prototype.setStatusMessage = function (value) { - Util.setElementValue(this.statusOutput, value); - }; - - Util.prototype.setOutputMessage = function (value) { - Util.setElementValue(this.resultsOutput, value); - }; - - Util.prototype.appendOutputMessage = function (value) { - this.resultsOutput.appendChild(document.createElement("hr")); - Util.appendElementValue(this.resultsOutput, value); - }; - - Util.setElementValue = function (element, value) { - var children = element.childNodes; - - // remove existing nodes - for (var c = 0; c < children.length; c++) { - element.removeChild(children[c]); - } - - Util.appendElementValue(element, value); - }; - - Util.appendElementValue = function (element, value) { - if (typeof value === "string") { - value = document.createElement("h3").appendChild(document.createTextNode(value)); - } - - element.appendChild(value); - }; - - Util.prototype.onRedrawMove = function () { - - var self = this; - - return function (worldwindow, stage) { - - if (self.moveQueue.length > 0 && stage === WorldWind.AFTER_REDRAW) { - - var range, tilt, heading, latitude, longitude, endStates = self.moveQueue[0]; - - if (endStates.range && !endStates.range.complete) { - range = Util.calculateNextValue(worldwindow.navigator.range, endStates.range.goal, endStates.range.step); - if (typeof range === "number") { - worldwindow.navigator.range = range; - } else { - endStates.range.complete = true; - } - } - - if (endStates.tilt && !endStates.tilt.complete) { - tilt = Util.calculateNextValue(worldwindow.navigator.tilt, endStates.tilt.goal, endStates.tilt.step); - if (typeof tilt === "number") { - worldwindow.navigator.tilt = tilt; - } else { - endStates.tilt.complete = true; - } - } - - if (endStates.heading && !endStates.heading.complete) { - heading = Util.calculateNextValue(worldwindow.navigator.heading, endStates.heading.goal, endStates.heading.step); - if (typeof heading === "number") { - worldwindow.navigator.heading = heading; - } else { - endStates.heading.complete = true; - } - } - - if (endStates.latitude && !endStates.latitude.complete) { - latitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.latitude, - endStates.latitude.goal, endStates.latitude.step); - if (typeof latitude === "number") { - worldwindow.navigator.lookAtLocation.latitude = latitude; - } else { - endStates.latitude.complete = true; - } - } - - if (endStates.longitude && !endStates.longitude.complete) { - longitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.longitude, - endStates.longitude.goal, endStates.longitude.step); - if (typeof longitude === "number") { - worldwindow.navigator.lookAtLocation.longitude = longitude; - } else { - endStates.longitude.complete = true; - } - } - - // Check to see if all the end states of this move have been completed - var keys = Object.getOwnPropertyNames(endStates); - for (var i = 0; i < keys.length; i++) { - - // skip the callback property - if (keys[i] === "onComplete") { - continue; - } - - if (!endStates[keys[i]].complete) { - // not done yet, don't complete the move yet - worldwindow.redraw(); - return; - } - } - - // Remove the move from the queue and invoke the completion listener if provided - self.moveQueue.shift(); - if (endStates.onComplete) { - endStates.onComplete(); - } - worldwindow.redraw(); - } - }; - }; - - Util.prototype.onRedrawMetricCapture = function () { - - var self = this; - - return function (worldwindow, stage) { - - if (self.captureMetrics && stage === WorldWind.AFTER_REDRAW) { - - // Copy all of the individual frame metrics - var frameStatistics = {}; - - frameStatistics.frameTime = worldwindow.frameStatistics.frameTime; - frameStatistics.tesselationTime = worldwindow.frameStatistics.tessellationTime; - frameStatistics.layerRenderingTime = worldwindow.frameStatistics.layerRenderingTime; - frameStatistics.orderedRenderingTime = worldwindow.frameStatistics.orderedRenderingTime; - frameStatistics.terrainTileCount = worldwindow.frameStatistics.terrainTileCount; - frameStatistics.imageTileCount = worldwindow.frameStatistics.imageTileCount; - frameStatistics.renderedTileCount = worldwindow.frameStatistics.renderedTileCount; - frameStatistics.tileUpdateCount = worldwindow.frameStatistics.tileUpdateCount; - frameStatistics.textureLoadCount = worldwindow.frameStatistics.textureLoadCount; - frameStatistics.vboLoadCount = worldwindow.frameStatistics.vboLoadCount; - - self.frameStats.push(frameStatistics); - } - }; - }; - - Util.prototype.move = function (endStates) { - - if (Array.isArray(endStates)) { - endStates.forEach(function (endState) { - this.moveQueue.push(endState); - }, this); - } else { - this.moveQueue.push(endStates); - } - - this.wwd.redraw(); - }; - - Util.prototype.startMetricCapture = function () { - this.frameStats = []; - this.captureMetrics = true; - }; - - Util.prototype.stopMetricCapture = function () { - this.captureMetrics = false; - }; - - Util.generateResultsSummary = function (dataArray, title) { - var min = Util.calculateMin(dataArray), max = Util.calculateMax(dataArray), average, stddev, bins; - - // calculate average - average = Util.calculateAverage(dataArray); - - // calculate standard deviation - stddev = Util.calculateStdDev(dataArray); - - // bin the data on 5 second bins - bins = Util.binValues(dataArray, 5); - - // create the html elements displaying the data - var encDiv = document.createElement("div"); - var titleEl = document.createElement("h2"); - titleEl.appendChild(document.createTextNode(title)); - encDiv.appendChild(titleEl); - var list = document.createElement("ul"); - encDiv.appendChild(list); - var li = document.createElement("li"); - list.appendChild(li); - li.appendChild(document.createTextNode("Average: " + average)); - li = document.createElement("li"); - list.appendChild(li); - li.appendChild(document.createTextNode("Standard Deviation: " + stddev)); - li = document.createElement("li"); - list.appendChild(li); - li.appendChild(document.createTextNode("Max: " + max)); - li = document.createElement("li"); - list.appendChild(li); - li.appendChild(document.createTextNode("Min: " + min)); - - var canvas = Util.generateSimplePlot(bins, 500, 500, true); - encDiv.appendChild(canvas); - - encDiv.appendChild(Util.generateTextOutput(dataArray)); - - return encDiv; - }; - - Util.calculateMax = function (values) { - var max = -Number.MAX_VALUE; - - values.forEach(function (value) { - max = Math.max(max, value); - }); - - return max; - }; - - Util.calculateMin = function (values) { - var min = Number.MAX_VALUE; - - values.forEach(function (value) { - min = Math.min(min, value); - }); - - return min; - }; - - Util.calculateAverage = function (values) { - var total = 0; - values.forEach(function (value) { - total += value; - }); - - return total / values.length; - }; - - Util.calculateStdDev = function (values) { - var avg = Util.calculateAverage(values), total = 0, diff; - - values.forEach(function (value) { - diff = value - avg; - total += diff * diff; - }); - - return Math.sqrt(total / values.length); - }; - - Util.binValues = function (values, binSize) { - var i, idx, min = Util.calculateMin(values), max = Util.calculateMax(values), binRange = (max - min), - len = values.length, binCount = Math.ceil(binRange / binSize), bins; - - bins = new Array(binCount + 1); - - WWUtil.fillArray(bins, 0); - for (i = 0; i < len; i++) { - idx = Math.floor((values[i] - min) / binSize); // TODO check if this should be floor or round - bins[idx]++; - } - - return bins; - }; - - Util.generateSimplePlot = function (data, sizeX, sizeY, reverseData) { - var canvas = document.createElement("canvas"), xScale, yScale, i, x, y, ctx, max = Util.calculateMax(data), - min = Util.calculateMin(data), dataPoints = data.length; - - // setup canvas element - canvas.setAttribute("width", sizeX); - canvas.setAttribute("height", sizeY); - ctx = canvas.getContext("2d"); - - // setup scaling values - xScale = sizeX / dataPoints; - yScale = sizeY / (max - min); - - // plot data - x = 0; - y = data[0] * yScale; - if (reverseData) { - y = sizeY - y; - } - ctx.beginPath(); - ctx.moveTo(x, y); - - for (i = 1; i < dataPoints; i++) { - x = xScale * i; - y = data[i] * yScale; - if (reverseData) { - y = sizeY - y; - } - ctx.lineTo(x, y); - } - ctx.stroke(); - - return canvas; - }; - - Util.generateTextOutput = function (data) { - var textOutput = document.createElement("textarea"); - textOutput.value = data.join(","); - textOutput.setAttribute("id", "text-data-output"); - - var copyToClipboardButton = document.createElement("button"); - copyToClipboardButton.appendChild(document.createTextNode("Copy to Clipboard")); - copyToClipboardButton.addEventListener("click", function () { - textOutput.select(); - - try { - var successful = document.execCommand('copy'); - var msg = successful ? 'successful' : 'unsuccessful'; - console.log('Copying text command was ' + msg); - } catch (err) { - console.log('Oops, unable to copy'); - } - }); - - var outputDiv = document.createElement("div"); - outputDiv.appendChild(textOutput); - var buttonDiv = document.createElement("div"); - buttonDiv.appendChild(copyToClipboardButton); - - var div = document.createElement("div"); - div.appendChild(outputDiv); - div.appendChild(buttonDiv); - - return div; - }; - - // Internal use only - Util.calculateNextValue = function (currentValue, goal, step) { - var diff = currentValue - goal; - - if (Math.abs(diff) > 0.1) { - if (diff < 0) { - return Math.abs(diff) > step ? currentValue + step : goal; - } else { - return Math.abs(diff) > step ? currentValue - step : goal; - } - } else { - return null; - } - }; - - return Util; -}); From 928342b70aa9185ab0b455cee7df7d59797369b6 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Wed, 23 May 2018 11:11:16 -0500 Subject: [PATCH 21/22] Change directory structure to mimic src --- .../shapes/SurfaceShapePerformance.html | 78 ++++ .../shapes/SurfaceShapePerformance.js | 280 +++++++++++++ functionalTest/util/Util.js | 383 ++++++++++++++++++ 3 files changed, 741 insertions(+) create mode 100644 functionalTest/shapes/SurfaceShapePerformance.html create mode 100644 functionalTest/shapes/SurfaceShapePerformance.js create mode 100644 functionalTest/util/Util.js diff --git a/functionalTest/shapes/SurfaceShapePerformance.html b/functionalTest/shapes/SurfaceShapePerformance.html new file mode 100644 index 000000000..16dc9f1fa --- /dev/null +++ b/functionalTest/shapes/SurfaceShapePerformance.html @@ -0,0 +1,78 @@ + + + + + + + + + + + Surface Shape Performance Functional Test + + + + +
+
+ + +
+ + Browser Does Not Support HTML5 + +
+ + +
+ + +
+

+ Surface Shape Performance Tests +

+

+ Reload the page between tests. Once the test has been completed, a statistical summary of the frame + times and a distribution of the frame times grouped by five second durations is displayed. Below the + frame time distribution plot, a text area with the actual frame times is displayed as well as a + button which automates copying of the data to the clipboard. +

+
+
+

Available Tests:

+ + + +
+
+ + +
+
+ +
+
+
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/functionalTest/shapes/SurfaceShapePerformance.js b/functionalTest/shapes/SurfaceShapePerformance.js new file mode 100644 index 000000000..aac7526c9 --- /dev/null +++ b/functionalTest/shapes/SurfaceShapePerformance.js @@ -0,0 +1,280 @@ +requirejs([ + '../../src/WorldWind', + '../util/Util' + ], + function (WorldWind, Util) { + "use strict"; + + // Define shape counts for testing + var dynamicShapeCount = 100; // of each type of shape (circle, ellipse, etc.) + var staticShapeCount = 1000; // of each type of shape (circle, ellipse, etc.) + + // Set the base url to use the root images directory + WorldWind.configuration.baseUrl = "../../"; + + // Initialize Globe + var wwd = Util.initializeLowResourceWorldWindow("globe"); + var testLayer = new WorldWind.RenderableLayer("Test Layer"); + wwd.addLayer(testLayer); + var util = new Util(wwd); + + util.setStatusMessage("Globe loaded, ready for testing..."); + + // Navigation Moves for Testing + var moveZero = { + latitude: { + goal: wwd.navigator.lookAtLocation.latitude, + step: Number.MAX_VALUE + }, + longitude: { + goal: wwd.navigator.lookAtLocation.longitude, + step: Number.MAX_VALUE + }, + tilt: { + goal: 0, + step: Number.MAX_VALUE + }, + heading: { + goal: 0, + step: Number.MAX_VALUE + }, + range: { + goal: wwd.navigator.range, + step: Number.MAX_VALUE + } + }; + + var moveOne = { + range: { + goal: 8e5, + step: 5e4 + }, + tilt: { + goal: 75, + step: 0.75 + }, + onComplete: function () { + util.setStatusMessage("First move complete..."); + } + }; + + var moveTwo = { + heading: { + goal: 90, + step: 0.75 + }, + onComplete: function () { + util.setStatusMessage("Second move complete"); + } + }; + + var moveThree = { + latitude: { + goal: wwd.navigator.lookAtLocation.latitude, + step: 1 + }, + longitude: { + goal: -70, + step: 0.5 + }, + onComplete: function () { + util.stopMetricCapture(); + util.setStatusMessage("Move Complete"); + + // output metrics + var stats = util.frameStats.map(function (frameStat) { + return frameStat.frameTime; + }); + stats.shift(); + stats.pop(); + util.setOutputMessage(Util.generateResultsSummary(stats, "Frame Times")); + stats = util.frameStats.map(function (frameStat) { + return frameStat.layerRenderingTime; + }); + } + }; + + // Utility functions + + // This is a pseudo random number generator which allows a seed, providing repeatable conditions + var nextFloat = function (seed) { + seed = seed % 2147483647; + return function () { + seed = seed * 16807 % 2147483647; + return (seed - 1) / 2147483646; + }; + }; + // Assign the PRNG to the function which will be used by the testing for content generation + var rand = nextFloat(1234); + + var generateShapeAttributes = function () { + var sa = new WorldWind.ShapeAttributes(); + var r = rand(); + + sa.drawInterior = false; + sa.drawOutline = false; + + if (r < 0.3333) { + sa.drawInterior = true; + sa.interiorColor = new WorldWind.Color(rand(), rand(), rand(), rand()); + } else if (r < 0.6666) { + sa.drawOutline = true; + sa.outlineColor = new WorldWind.Color(rand(), rand(), rand(), rand()); + } else { + sa.drawInterior = true; + sa.interiorColor = new WorldWind.Color(rand(), rand(), rand(), rand()); + sa.drawOutline = true; + sa.outlineColor = new WorldWind.Color(rand(), rand(), rand(), rand()); + } + + return sa; + }; + + var generateLocation = function () { + var lat = 180 * rand() - 90; + var lon = 360 * rand() - 180; + + return new WorldWind.Location(lat, lon); + }; + + var generateShapes = function (count) { + var sa, shape, sector, radius, topLocation, lat, lon, locations, minorAxis, majorAxis, heading, i, j; + + // Surface Circles + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + radius = rand() * 1000000; + shape = new WorldWind.SurfaceCircle(generateLocation(), radius, sa); + testLayer.addRenderable(shape); + } + + // Surface Ellipse + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + majorAxis = rand() * 1000000; + minorAxis = rand() * 500000; + heading = rand() * 360; + shape = new WorldWind.SurfaceEllipse(generateLocation(), majorAxis, minorAxis, heading, sa); + testLayer.addRenderable(shape); + } + + // Surface Polygon + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + topLocation = generateLocation(); + locations = []; + locations.push(topLocation); + for (j = 0; j < 2; j++) { + lat = Math.min(90, Math.max(-90, topLocation.latitude - rand() * 8)); + lon = Math.min(180, Math.max(-180, topLocation.longitude - rand() * 8)); + locations.push(new WorldWind.Location(lat, lon)); + } + shape = new WorldWind.SurfacePolygon(locations, sa); + testLayer.addRenderable(shape); + } + + // Surface Polyline + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + topLocation = generateLocation(); + locations = []; + locations.push(topLocation); + for (j = 0; j < 2; j++) { + lat = Math.min(90, Math.max(-90, topLocation.latitude - rand() * 8)); + lon = Math.min(180, Math.max(-180, topLocation.longitude - rand() * 8)); + locations.push(new WorldWind.Location(lat, lon)); + } + shape = new WorldWind.SurfacePolyline(locations, sa); + testLayer.addRenderable(shape); + } + + // Surface Rectangle + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + majorAxis = 1000000 * rand(); + minorAxis = 500000 * rand(); + heading = 360 * rand(); + shape = new WorldWind.SurfaceRectangle(generateLocation(), majorAxis, minorAxis, heading, sa); + testLayer.addRenderable(shape); + } + + // Surface Sector + for (i = 0; i < count; i++) { + sa = generateShapeAttributes(); + topLocation = generateLocation(); + majorAxis = 8 * rand(); + minorAxis = 4 * rand(); + sector = new WorldWind.Sector(topLocation.latitude, topLocation.latitude + majorAxis, + topLocation.longitude, topLocation.longitude + minorAxis); + shape = new WorldWind.SurfaceSector(sector, sa); + testLayer.addRenderable(shape); + } + + wwd.redraw(); + }; + + // Click Event Callbacks + var onMoveClick = function () { + if (!util.isMoving()) { + util.startMetricCapture(); + util.move([moveZero, moveOne, moveTwo, moveThree]); + } + }; + + var onStaticShapesClick = function () { + generateShapes(1000); + wwd.redraw(); + onMoveClick(); + }; + + var onDynamicShapesClick = function () { + generateShapes(100); + + var onRedrawMoveShapes = function (worldwindow, stage) { + if (stage === WorldWind.AFTER_REDRAW) { + var idx = worldwindow.layers.indexOf(testLayer); + + if (idx >= 0) { + var count = worldwindow.layers[idx].renderables.length; + + for (var i = 0; i < count; i++) { + var shape = worldwindow.layers[idx].renderables[i]; + + if (shape instanceof WorldWind.SurfaceCircle || shape instanceof WorldWind.SurfaceEllipse + || shape instanceof WorldWind.SurfaceRectangle) { + var center = shape.center; + center.latitude += rand() * 0.5 - 0.25; + center.longitude += rand() * 0.5 - 0.25; + shape.center = center; + } else if (shape instanceof WorldWind.SurfacePolygon + || shape instanceof WorldWind.SurfacePolyline) { + var boundaries = shape.boundaries; + boundaries[0].latitude += rand() * 0.5 - 0.25; + boundaries[0].longitude += rand() * 0.5 - 0.25; + shape.boundaries = boundaries; + } else if (shape instanceof WorldWind.SurfaceSector) { + var sector = shape.sector; + sector.minLongitude += rand() * 0.5 - 0.25; + sector.minLatitude += rand() * 0.5 - 0.25; + shape.sector = sector; + } + + } + } + } + }; + + wwd.redrawCallbacks.push(onRedrawMoveShapes); + wwd.redraw(); + onMoveClick(); + }; + + // Click Event Assignments + var navigateButton = document.getElementById("navigate-button"); + var staticShapesButton = document.getElementById("static-button"); + var dynamicShapesButton = document.getElementById("dynamic-button"); + + navigateButton.addEventListener("click", onMoveClick); + staticShapesButton.addEventListener("click", onStaticShapesClick); + dynamicShapesButton.addEventListener("click", onDynamicShapesClick); + }); diff --git a/functionalTest/util/Util.js b/functionalTest/util/Util.js new file mode 100644 index 000000000..c5cb003fe --- /dev/null +++ b/functionalTest/util/Util.js @@ -0,0 +1,383 @@ +define([ + '../../src/WorldWind', + '../../src/util/WWUtil' + ], + function (WorldWind, WWUtil) { + "use strict"; + + var Util = function (worldwindow) { + + this.wwd = worldwindow; + + this.moveQueue = []; + + this.frameStats = []; + + this.captureMetrics = false; + + this.wwd.redrawCallbacks.push(this.onRedrawMove()); + + this.wwd.redrawCallbacks.push(this.onRedrawMetricCapture()); + + this.statusOutput = document.getElementById("status-output"); + + this.resultsOutput = document.getElementById("results-output"); + }; + + Util.initializeLowResourceWorldWindow = function (canvasId) { + var wwd = new WorldWind.WorldWindow(canvasId); + wwd.globe.elevationModel.removeAllCoverages(); // Don't want delays associated with parsing and changing terrain + var bmnglayer = new WorldWind.BMNGOneImageLayer(); + bmnglayer.minActiveAltitude = 0; + wwd.addLayer(bmnglayer); // Don't want any imaging processing delays + wwd.redraw(); + + return wwd; + }; + + Util.prototype.isMoving = function () { + return this.moveQueue.length > 0; + }; + + Util.prototype.setStatusMessage = function (value) { + Util.setElementValue(this.statusOutput, value); + }; + + Util.prototype.setOutputMessage = function (value) { + Util.setElementValue(this.resultsOutput, value); + }; + + Util.prototype.appendOutputMessage = function (value) { + this.resultsOutput.appendChild(document.createElement("hr")); + Util.appendElementValue(this.resultsOutput, value); + }; + + Util.setElementValue = function (element, value) { + var children = element.childNodes; + + // remove existing nodes + for (var c = 0; c < children.length; c++) { + element.removeChild(children[c]); + } + + Util.appendElementValue(element, value); + }; + + Util.appendElementValue = function (element, value) { + if (typeof value === "string") { + value = document.createElement("h3").appendChild(document.createTextNode(value)); + } + + element.appendChild(value); + }; + + Util.prototype.onRedrawMove = function () { + + var self = this; + + return function (worldwindow, stage) { + + if (self.moveQueue.length > 0 && stage === WorldWind.AFTER_REDRAW) { + + var range, tilt, heading, latitude, longitude, endStates = self.moveQueue[0]; + + if (endStates.range && !endStates.range.complete) { + range = Util.calculateNextValue(worldwindow.navigator.range, endStates.range.goal, endStates.range.step); + if (typeof range === "number") { + worldwindow.navigator.range = range; + } else { + endStates.range.complete = true; + } + } + + if (endStates.tilt && !endStates.tilt.complete) { + tilt = Util.calculateNextValue(worldwindow.navigator.tilt, endStates.tilt.goal, endStates.tilt.step); + if (typeof tilt === "number") { + worldwindow.navigator.tilt = tilt; + } else { + endStates.tilt.complete = true; + } + } + + if (endStates.heading && !endStates.heading.complete) { + heading = Util.calculateNextValue(worldwindow.navigator.heading, endStates.heading.goal, endStates.heading.step); + if (typeof heading === "number") { + worldwindow.navigator.heading = heading; + } else { + endStates.heading.complete = true; + } + } + + if (endStates.latitude && !endStates.latitude.complete) { + latitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.latitude, + endStates.latitude.goal, endStates.latitude.step); + if (typeof latitude === "number") { + worldwindow.navigator.lookAtLocation.latitude = latitude; + } else { + endStates.latitude.complete = true; + } + } + + if (endStates.longitude && !endStates.longitude.complete) { + longitude = Util.calculateNextValue(worldwindow.navigator.lookAtLocation.longitude, + endStates.longitude.goal, endStates.longitude.step); + if (typeof longitude === "number") { + worldwindow.navigator.lookAtLocation.longitude = longitude; + } else { + endStates.longitude.complete = true; + } + } + + // Check to see if all the end states of this move have been completed + var keys = Object.getOwnPropertyNames(endStates); + for (var i = 0; i < keys.length; i++) { + + // skip the callback property + if (keys[i] === "onComplete") { + continue; + } + + if (!endStates[keys[i]].complete) { + // not done yet, don't complete the move yet + worldwindow.redraw(); + return; + } + } + + // Remove the move from the queue and invoke the completion listener if provided + self.moveQueue.shift(); + if (endStates.onComplete) { + endStates.onComplete(); + } + worldwindow.redraw(); + } + }; + }; + + Util.prototype.onRedrawMetricCapture = function () { + + var self = this; + + return function (worldwindow, stage) { + + if (self.captureMetrics && stage === WorldWind.AFTER_REDRAW) { + + // Copy all of the individual frame metrics + var frameStatistics = {}; + + frameStatistics.frameTime = worldwindow.frameStatistics.frameTime; + frameStatistics.tesselationTime = worldwindow.frameStatistics.tessellationTime; + frameStatistics.layerRenderingTime = worldwindow.frameStatistics.layerRenderingTime; + frameStatistics.orderedRenderingTime = worldwindow.frameStatistics.orderedRenderingTime; + frameStatistics.terrainTileCount = worldwindow.frameStatistics.terrainTileCount; + frameStatistics.imageTileCount = worldwindow.frameStatistics.imageTileCount; + frameStatistics.renderedTileCount = worldwindow.frameStatistics.renderedTileCount; + frameStatistics.tileUpdateCount = worldwindow.frameStatistics.tileUpdateCount; + frameStatistics.textureLoadCount = worldwindow.frameStatistics.textureLoadCount; + frameStatistics.vboLoadCount = worldwindow.frameStatistics.vboLoadCount; + + self.frameStats.push(frameStatistics); + } + }; + }; + + Util.prototype.move = function (endStates) { + + if (Array.isArray(endStates)) { + endStates.forEach(function (endState) { + this.moveQueue.push(endState); + }, this); + } else { + this.moveQueue.push(endStates); + } + + this.wwd.redraw(); + }; + + Util.prototype.startMetricCapture = function () { + this.frameStats = []; + this.captureMetrics = true; + }; + + Util.prototype.stopMetricCapture = function () { + this.captureMetrics = false; + }; + + Util.generateResultsSummary = function (dataArray, title) { + var min = Util.calculateMin(dataArray), max = Util.calculateMax(dataArray), average, stddev, bins; + + // calculate average + average = Util.calculateAverage(dataArray); + + // calculate standard deviation + stddev = Util.calculateStdDev(dataArray); + + // bin the data on 5 second bins + bins = Util.binValues(dataArray, 5); + + // create the html elements displaying the data + var encDiv = document.createElement("div"); + var titleEl = document.createElement("h2"); + titleEl.appendChild(document.createTextNode(title)); + encDiv.appendChild(titleEl); + var list = document.createElement("ul"); + encDiv.appendChild(list); + var li = document.createElement("li"); + list.appendChild(li); + li.appendChild(document.createTextNode("Average: " + average)); + li = document.createElement("li"); + list.appendChild(li); + li.appendChild(document.createTextNode("Standard Deviation: " + stddev)); + li = document.createElement("li"); + list.appendChild(li); + li.appendChild(document.createTextNode("Max: " + max)); + li = document.createElement("li"); + list.appendChild(li); + li.appendChild(document.createTextNode("Min: " + min)); + + var canvas = Util.generateSimplePlot(bins, 500, 500, true); + encDiv.appendChild(canvas); + + encDiv.appendChild(Util.generateTextOutput(dataArray)); + + return encDiv; + }; + + Util.calculateMax = function (values) { + var max = -Number.MAX_VALUE; + + values.forEach(function (value) { + max = Math.max(max, value); + }); + + return max; + }; + + Util.calculateMin = function (values) { + var min = Number.MAX_VALUE; + + values.forEach(function (value) { + min = Math.min(min, value); + }); + + return min; + }; + + Util.calculateAverage = function (values) { + var total = 0; + values.forEach(function (value) { + total += value; + }); + + return total / values.length; + }; + + Util.calculateStdDev = function (values) { + var avg = Util.calculateAverage(values), total = 0, diff; + + values.forEach(function (value) { + diff = value - avg; + total += diff * diff; + }); + + return Math.sqrt(total / values.length); + }; + + Util.binValues = function (values, binSize) { + var i, idx, min = Util.calculateMin(values), max = Util.calculateMax(values), binRange = (max - min), + len = values.length, binCount = Math.ceil(binRange / binSize), bins; + + bins = new Array(binCount + 1); + + WWUtil.fillArray(bins, 0); + for (i = 0; i < len; i++) { + idx = Math.floor((values[i] - min) / binSize); // TODO check if this should be floor or round + bins[idx]++; + } + + return bins; + }; + + Util.generateSimplePlot = function (data, sizeX, sizeY, reverseData) { + var canvas = document.createElement("canvas"), xScale, yScale, i, x, y, ctx, max = Util.calculateMax(data), + min = Util.calculateMin(data), dataPoints = data.length; + + // setup canvas element + canvas.setAttribute("width", sizeX); + canvas.setAttribute("height", sizeY); + ctx = canvas.getContext("2d"); + + // setup scaling values + xScale = sizeX / dataPoints; + yScale = sizeY / (max - min); + + // plot data + x = 0; + y = data[0] * yScale; + if (reverseData) { + y = sizeY - y; + } + ctx.beginPath(); + ctx.moveTo(x, y); + + for (i = 1; i < dataPoints; i++) { + x = xScale * i; + y = data[i] * yScale; + if (reverseData) { + y = sizeY - y; + } + ctx.lineTo(x, y); + } + ctx.stroke(); + + return canvas; + }; + + Util.generateTextOutput = function (data) { + var textOutput = document.createElement("textarea"); + textOutput.value = data.join(","); + textOutput.setAttribute("id", "text-data-output"); + + var copyToClipboardButton = document.createElement("button"); + copyToClipboardButton.appendChild(document.createTextNode("Copy to Clipboard")); + copyToClipboardButton.addEventListener("click", function () { + textOutput.select(); + + try { + var successful = document.execCommand('copy'); + var msg = successful ? 'successful' : 'unsuccessful'; + console.log('Copying text command was ' + msg); + } catch (err) { + console.log('Oops, unable to copy'); + } + }); + + var outputDiv = document.createElement("div"); + outputDiv.appendChild(textOutput); + var buttonDiv = document.createElement("div"); + buttonDiv.appendChild(copyToClipboardButton); + + var div = document.createElement("div"); + div.appendChild(outputDiv); + div.appendChild(buttonDiv); + + return div; + }; + + // Internal use only + Util.calculateNextValue = function (currentValue, goal, step) { + var diff = currentValue - goal; + + if (Math.abs(diff) > 0.1) { + if (diff < 0) { + return Math.abs(diff) > step ? currentValue + step : goal; + } else { + return Math.abs(diff) > step ? currentValue - step : goal; + } + } else { + return null; + } + }; + + return Util; +}); From 6c46e5a1d8a03658e4fa84a2ca9b5127a0bb3f47 Mon Sep 17 00:00:00 2001 From: Zach Glueckert Date: Wed, 23 May 2018 11:15:35 -0500 Subject: [PATCH 22/22] Remove frame time distribution plot --- functionalTest/util/Util.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/functionalTest/util/Util.js b/functionalTest/util/Util.js index c5cb003fe..e74a21248 100644 --- a/functionalTest/util/Util.js +++ b/functionalTest/util/Util.js @@ -235,9 +235,6 @@ define([ list.appendChild(li); li.appendChild(document.createTextNode("Min: " + min)); - var canvas = Util.generateSimplePlot(bins, 500, 500, true); - encDiv.appendChild(canvas); - encDiv.appendChild(Util.generateTextOutput(dataArray)); return encDiv;