diff --git a/HighwayDataExaminer/hdxav-qh.js b/HighwayDataExaminer/hdxav-qh.js
new file mode 100644
index 0000000..1ede449
--- /dev/null
+++ b/HighwayDataExaminer/hdxav-qh.js
@@ -0,0 +1,577 @@
+//
+// HDX Algorithm Visualization Template File
+//
+// METAL Project
+//
+// Primary Authors: Gregory Drapeau
+//
+
+// This global variable refers to the object containaing all the
+// necessary fields, functions, and states for a given AV. This
+// variable must be pushed to the this.avList in the hdxav.js file,
+// and the file of this AV must be included in the index.php file
+const hdxQHAV = {
+ // short name for list of avs, will be used for the av= QS parameter value
+ value: 'qhull',
+ name: "Quickhull",
+ description: "The divide and conquer algorithm for finding the convex hull.",
+
+ // vertices, no edges
+ useV: true,
+ useE: false,
+
+ currentSet: [],
+ vOne: null,
+ vTwo: null,
+ mostNorth: -90,
+ mostSouth: 90,
+ // array of vertices that makes up the hull
+ hullVertices: [],
+ // array of the line segments that makes up the hull
+ hullSegments: [],
+ // dividing line slope
+ slope: 0,
+ // dividing line y-intercept
+ yIntercept: 0,
+ // sets
+ sZero: [],
+ sOne: [],
+ sTwo: [],
+ // max [distance, index, object]
+ max: [],
+ // used for splitLoop due to that being used in multiple places
+ futureAction: "",
+ // temporary vertices for swaping
+ vOneTmp: null,
+ vTwoTmp: null,
+ // polyline object
+ compLine: [],
+
+ // elements index 0 is v1, index 1 is v2, index 2 is lower set
+ recursionStack: [],
+
+ // loop variable that tracks which point is currently being operated upon
+ nextToCheck: -1,
+
+ // obscure irrelevant regions of the map
+ obscureRegion: null,
+ obscure: {
+ color: "black",
+ scale: 4,
+ weight: 0.5,
+ fillOpacity: 0.1,
+ name: "obscure",
+ value: 0
+ },
+
+ // The avActions array defines all of the actions of the AV
+ avActions : [
+ {
+ label: "START",
+ comment: "Initializes fields",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+
+ thisAV.mostNorth = -90;
+ thisAV.mostSouth = 90;
+ for(let i = 0; i < waypoints.length; i++){
+ if(waypoints[i].lat > thisAV.mostNorth){
+ thisAV.mostNorth = waypoints[i].lat;
+ }
+ if(waypoints[i].lat < thisAV.mostSouth){
+ thisAV.mostSouth = waypoints[i].lat;
+ }
+ }
+ thisAV.mostNorth += (thisAV.mostNorth-thisAV.mostSouth)/10;
+ thisAV.mostSouth += -(thisAV.mostNorth-thisAV.mostSouth)/11;
+ const sorter = new HDXWaypointsSorter();
+ thisAV.currentSet = sorter.sortWaypoints();
+ thisAV.vOne = thisAV.currentSet[0];
+ thisAV.vTwo = thisAV.currentSet[thisAV.currentSet.length-1];
+ thisAV.currentSet.splice(0,1);
+ thisAV.currentSet.splice(thisAV.currentSet.length-1, 1);
+ // dividing line
+ thisAV.slope = (thisAV.vTwo.lat-thisAV.vOne.lat)/(thisAV.vTwo.lon-thisAV.vOne.lon);
+ thisAV.yIntercept = (thisAV.slope*(thisAV.vOne.lon*(-1)))+thisAV.vOne.lat;
+ let comLine = [];
+ comLine[0] = [thisAV.vOne.lat, thisAV.vOne.lon];
+ comLine[1] = [thisAV.vTwo.lat, thisAV.vTwo.lon];
+ thisAV.compLine.push(L.polyline(comLine, visualSettings.visiting));
+ thisAV.compLine[thisAV.compLine.length-1].addTo(map);
+ thisAV.hullVertices = [];
+ thisAV.hullSegments = [];
+ thisAV.hullVertices.push(thisAV.vOne);
+ // AVCP updates
+ document.getElementById("hVertsNum").innerText = thisAV.hullVertices.length;
+ let newTableRow = document.createElement("tr");
+ newTableRow.innerHTML = `
`+thisAV.hullVertices[thisAV.hullVertices.length-1].label+`
+ (`+thisAV.hullVertices[thisAV.hullVertices.length-1].lat+`, `+thisAV.hullVertices[thisAV.hullVertices.length-1].lon+`) `;
+ document.getElementById("hullVertexTable").appendChild(newTableRow);
+ thisAV.nextToCheck = -1;
+ thisAV.futureAction = "callOne";
+ hdxAVCP.update("hullv1", "v1 : "+thisAV.vOne.label);
+ hdxAVCP.update("hullv2", "v2 : "+thisAV.vTwo.label);
+ hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+" "+thisAV.vOne.label+"<-->"+thisAV.vTwo.label);
+ updateMarkerAndTable(waypoints.indexOf(thisAV.vOne),
+ visualSettings.averageCoord, 40, false);
+ updateMarkerAndTable(waypoints.indexOf(thisAV.vTwo),
+ visualSettings.averageCoord, 40, false);
+
+ hdxAV.nextAction = "splitLoop";
+ },
+ logMessage: function(thisAV) {
+ return "Doing some setup stuff";
+ }
+ },
+ {
+ label: "splitLoop",
+ comment: "Loop for splitting up of vertices",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+
+ thisAV.nextToCheck++;
+ if(thisAV.nextToCheck < thisAV.currentSet.length){
+ updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]),
+ visualSettings.visiting, 40, false);
+ hdxAV.nextAction = "split";
+ }else{
+ hdxAV.nextAction = thisAV.futureAction;
+ }
+ },
+ logMessage: function(thisAV) {
+ return "Loop iteration: "+thisAV.nextToCheck;
+ }
+ },
+ {
+ label: "split",
+ comment: "Splitting up vertices between set one and two",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+
+ if(thisAV.vOne.lon < thisAV.vTwo.lon){
+ // above hull
+ if(thisAV.currentSet[thisAV.nextToCheck].lat > (thisAV.slope*thisAV.currentSet[thisAV.nextToCheck].lon+thisAV.yIntercept)){
+ thisAV.sOne.push(thisAV.currentSet[thisAV.nextToCheck]);
+ updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]),
+ visualSettings.discovered, 40, false);
+ }else{
+ thisAV.sTwo.push(thisAV.currentSet[thisAV.nextToCheck]);
+ updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]),
+ visualSettings.discarded, 40, false);
+ }
+ }else{
+ // below hull
+ if(thisAV.currentSet[thisAV.nextToCheck].lat > (thisAV.slope*thisAV.currentSet[thisAV.nextToCheck].lon+thisAV.yIntercept)){
+ thisAV.sTwo.push(thisAV.currentSet[thisAV.nextToCheck]);
+ updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]),
+ visualSettings.discarded, 40, false);
+ }else{
+ thisAV.sOne.push(thisAV.currentSet[thisAV.nextToCheck]);
+ updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]),
+ visualSettings.discovered, 40, false);
+ }
+ }
+
+ hdxAV.nextAction = "splitLoop";
+ },
+ logMessage: function(thisAV) {
+ return "Size of set one: "+thisAV.sOne.length+" Size of set two: "+thisAV.sTwo.length;
+ }
+ },
+ {
+ label: "callOne",
+ comment: "Adding to the recursion stack and setting up variables for first call",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+ hdxAV.nextAction = "fnTop";
+
+ if(thisAV.sOne.length > 0){
+ let snapShot
+ thisAV.currentSet = thisAV.sOne;
+ if(thisAV.sTwo.length > 0){
+ snapShot = [thisAV.vTwo, thisAV.vOne, thisAV.sTwo];
+ }else{
+ snapShot = [thisAV.vOne];
+ }
+ thisAV.recursionStack.push(snapShot);
+ }else{
+ thisAV.hullVertices.push(thisAV.vTwo);
+ hdxQHAV.updateHullVertexTable();
+ if(thisAV.sTwo.length > 0){
+ let vZ = thisAV.vOne;
+ thisAV.vOne = thisAV.vTwo;
+ thisAV.vTwo = vZ;
+ thisAV.currentSet = thisAV.sTwo;
+ }else{
+ hdxAV.nextAction = "cleanup";
+ }
+ }
+ for(let i = 0; i < thisAV.sTwo.length; i++){
+ updateMarkerAndTable(waypoints.indexOf(thisAV.sTwo[i]),
+ visualSettings.discovered, 40, false);
+ }
+
+ hdxAV.iterationDone = true;
+ },
+ logMessage: function(thisAV) {
+ return "Starting at "+thisAV.vOne.label;
+ }
+ },
+ {
+ label: "fnTop",
+ comment: "Setting up UI for new call and confirming vars have been reset",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+
+ thisAV.nextToCheck = -1;
+ thisAV.max = [0,-1,null];
+ thisAV.sOne = [];
+ thisAV.sTwo = [];
+ // dividing line
+ thisAV.slope = (thisAV.vTwo.lat-thisAV.vOne.lat)/(thisAV.vTwo.lon-thisAV.vOne.lon);
+ thisAV.yIntercept = (thisAV.slope*(thisAV.vOne.lon*(-1)))+thisAV.vOne.lat;
+ let comLine = [];
+ comLine[0] = [thisAV.vOne.lat, thisAV.vOne.lon];
+ comLine[1] = [thisAV.vTwo.lat, thisAV.vTwo.lon];
+ thisAV.compLine.push(L.polyline(comLine, visualSettings.visiting));
+ thisAV.compLine[thisAV.compLine.length-1].addTo(map);
+
+ // AVCP updates
+ hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+" "+thisAV.vOne.label+"<-->"+thisAV.vTwo.label);
+ hdxAVCP.update("hullv1", "v1 : "+thisAV.vOne.label);
+ hdxAVCP.update("hullv2", "v2 : "+thisAV.vTwo.label);
+ // AVCP recursionStack
+ let recursionTable = "";
+ let trSize = Math.min(4, thisAV.recursionStack.length);
+ for(let i = 0; i < trSize; i++){
+ let recursionIndex;
+ if(i == 3){
+ recursionIndex = 0;
+ recursionTable += "...";
+ }else{
+ recursionIndex = thisAV.recursionStack.length-i-1;
+ }
+ if(thisAV.recursionStack[recursionIndex].length == 1){
+ recursionTable += "#"+recursionIndex+" "+shortLabel(thisAV.recursionStack[recursionIndex][0].label, 8)+" ";
+ }else{
+ recursionTable += "#"+recursionIndex+" "+shortLabel(thisAV.recursionStack[recursionIndex][0].label, 8)+" "+shortLabel(thisAV.recursionStack[recursionIndex][1].label, 8)+" Size: "+thisAV.recursionStack[recursionIndex][2].length+" ";
+ }
+ }
+ recursionTable += " ";
+ document.getElementById("recTable").innerHTML = recursionTable;
+
+ hdxQHAV.updateShading();
+
+ hdxAV.nextAction = "findMaxLoop";
+ },
+ logMessage: function(thisAV) {
+ return "VOne: "+thisAV.vOne.label+" vTwo: "+thisAV.vTwo.label;
+ }
+ },
+ {
+ label: "findMaxLoop",
+ comment: "Loop for finding the max",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+
+ thisAV.nextToCheck++;
+ if(thisAV.nextToCheck thisAV.max[0]){
+ if(thisAV.max[1] > -1){
+ updateMarkerAndTable(waypoints.indexOf(thisAV.max[2]),
+ visualSettings.discovered, 40, false);
+ }
+ thisAV.max = [distance, thisAV.nextToCheck, thisAV.currentSet[thisAV.nextToCheck]];
+ updateMarkerAndTable(waypoints.indexOf(thisAV.max[2]),
+ visualSettings.averageCoord, 40, false);
+ }else{
+ updateMarkerAndTable(waypoints.indexOf(thisAV.currentSet[thisAV.nextToCheck]),
+ visualSettings.discovered, 40, false);
+ }
+
+ hdxAV.nextAction = "findMaxLoop";
+ },
+ logMessage: function(thisAV) {
+ return "max distance is "+thisAV.max[0];
+ }
+ },
+ {
+ label: "firstSplit",
+ comment: "Prepares the variables for the first split of the set",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+
+ if(thisAV.max[2] != null){
+ thisAV.vOneTmp = thisAV.vOne;
+ thisAV.vTwoTmp = thisAV.max[2];
+ // temporary line
+ thisAV.slope = (thisAV.vTwoTmp.lat-thisAV.vOneTmp.lat)/(thisAV.vTwoTmp.lon-thisAV.vOneTmp.lon);
+ thisAV.yIntercept = (thisAV.slope*(thisAV.vOneTmp.lon*(-1)))+thisAV.vOneTmp.lat;
+ let comLine = [];
+ comLine[0] = [thisAV.vOneTmp.lat, thisAV.vOneTmp.lon];
+ comLine[1] = [thisAV.vTwoTmp.lat, thisAV.vTwoTmp.lon];
+ thisAV.compLine.push(L.polyline(comLine, visualSettings.visiting));
+ thisAV.compLine[thisAV.compLine.length-1].addTo(map);
+ thisAV.nextToCheck = -1;
+ thisAV.currentSet.splice(thisAV.max[1],1);
+ thisAV.futureAction = "secondSplit";
+ hdxAV.nextAction = "splitLoop";
+
+ hdxAVCP.update("hullMax", "vmax : "+thisAV.max[2].label);
+ hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+" "+thisAV.vOneTmp.label+"<-->"+thisAV.vTwoTmp.label);
+ }else{
+ hdxAV.nextAction = "postSplits";
+ }
+ },
+ logMessage: function(thisAV) {
+ return "the line is y="+thisAV.slope+"x+"+thisAV.yIntercept;
+ }
+ },
+ {
+ label: "secondSplit",
+ comment: "Prepares the variables for the second split of the set",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+
+ thisAV.vOneTmp = thisAV.max[2];
+ thisAV.vTwoTmp = thisAV.vTwo;
+ // temporary line
+ thisAV.slope = (thisAV.vTwoTmp.lat-thisAV.vOneTmp.lat)/(thisAV.vTwoTmp.lon-thisAV.vOneTmp.lon);
+ thisAV.yIntercept = (thisAV.slope*(thisAV.vOneTmp.lon*(-1)))+thisAV.vOneTmp.lat;
+ let comLine = [];
+ comLine[0] = [thisAV.vOneTmp.lat, thisAV.vOneTmp.lon];
+ comLine[1] = [thisAV.vTwoTmp.lat, thisAV.vTwoTmp.lon];
+ thisAV.compLine.push(L.polyline(comLine, visualSettings.visiting));
+ thisAV.compLine[thisAV.compLine.length-1].addTo(map);
+ thisAV.nextToCheck = -1;
+ thisAV.sZero = thisAV.sOne;
+ thisAV.currentSet = thisAV.sTwo;
+ thisAV.sOne = [];
+ thisAV.sTwo = [];
+ thisAV.futureAction = "postSplits";
+ hdxAV.nextAction = "splitLoop";
+
+ hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+" "+thisAV.vOneTmp.label+"<-->"+thisAV.vTwoTmp.label);
+ },
+ logMessage: function(thisAV) {
+ return "the line is y="+thisAV.slope+"x+"+thisAV.yIntercept;
+ }
+ },
+ {
+ label: "postSplits",
+ comment: "Sets variables for the next iteration",
+ code: function(thisAV) {
+ highlightPseudocode(this.label, visualSettings.visiting);
+
+ thisAV.sTwo = thisAV.sOne;
+ thisAV.sOne = thisAV.sZero;
+ hdxAV.nextAction = "fnTop";
+
+ // checking if set one is empty
+ if(thisAV.sOne.length > 0){
+ let snapShot;
+ // checking if set two is empty
+ if(thisAV.sTwo.length > 0){
+ snapShot = [thisAV.max[2], thisAV.vTwo, thisAV.sTwo];
+ }else{
+ snapShot = [thisAV.vTwo];
+ }
+ thisAV.currentSet = thisAV.sOne;
+ thisAV.vTwo = thisAV.max[2];
+ thisAV.recursionStack.push(snapShot);
+ // checking if set two is empty
+ }else if(thisAV.sTwo.length > 0){
+ thisAV.currentSet = thisAV.sTwo;
+ thisAV.vOne = thisAV.max[2];
+ thisAV.hullVertices.push(thisAV.max[2]);
+ hdxQHAV.updateHullVertexTable();
+ // checking if recursion stack is empty
+ }else if(thisAV.recursionStack.length > 0){
+ thisAV.hullVertices.push(thisAV.max[2]);
+ hdxQHAV.updateHullVertexTable();
+ thisAV.hullVertices.push(thisAV.vTwo);
+ hdxQHAV.updateHullVertexTable();
+ let snapShot = thisAV.recursionStack.pop();
+ // collect any of the single vertices in stack
+ while(snapShot?.length==1){
+ thisAV.hullVertices.push(snapShot[0]);
+ hdxQHAV.updateHullVertexTable();
+ snapShot = thisAV.recursionStack.pop();
+ }
+ // if stack is empty
+ if(snapShot==null){
+ hdxAV.nextAction = "cleanup";
+ }else{
+ thisAV.vOne = snapShot[0];
+ thisAV.vTwo = snapShot[1];
+ thisAV.currentSet = snapShot[2];
+ }
+ // if stack is empty
+ }else{
+ hdxAV.nextAction = "cleanup";
+ if(thisAV.max[2] != null){
+ thisAV.hullVertices.push(thisAV.max[2])
+ hdxQHAV.updateHullVertexTable();
+ }
+ thisAV.hullVertices.push(thisAV.vTwo);
+ hdxQHAV.updateHullVertexTable();
+ }
+ thisAV.sZero = [];
+ thisAV.sOne = [];
+ thisAV.sTwo = [];
+
+ hdxAV.iterationDone = true;
+
+ hdxAVCP.update("checkingLine", "Current line: y="+thisAV.slope+"x+"+thisAV.yIntercept+" "+thisAV.vOne.label+"<-->"+thisAV.vTwo.label);
+ },
+ logMessage: function(thisAV) {
+ return "Preparing for the next iteration";
+ }
+ },
+ {
+ label: "cleanup",
+ comment: "cleanup and updates at the end of the visualization",
+ code: function(thisAV) {
+
+ hdxAVCP.update("hullv1", "");
+ hdxAVCP.update("hullv2", "");
+ hdxAVCP.update("hullMax", "");
+ hdxAVCP.update("checkingLine", "");
+ hdxAVCP.update("recursionStack", "");
+
+ while(thisAV.compLine.length > 0){
+ thisAV.compLine[thisAV.compLine.length-1].remove();
+ thisAV.compLine.pop();
+ }
+ thisAV.obscureRegion.remove();
+ thisAV.obscureRegion = null;
+
+ hdxAV.nextAction = "DONE";
+ hdxAV.iterationDone = true;
+
+
+ },
+ logMessage: function(thisAV) {
+ return "Cleanup and finalize visualization";
+ }
+ }
+ ],
+
+ // prepToStart is a required function which is called when you hit
+ // visualize but before you hit start
+ // sets up pseudocode
+ prepToStart() {
+
+ // Build HTML for the pseudocode, which is an HTML table, with
+ // each state being a different row.
+ this.code = '';
+ this.code += 'sortedPoints[] ← sort(waypoints) westMost ← sortedPoints[0] eastMost ← sortedPoints[this.length-1] slope ← (v2 .y - v1 .y)/(v2 .x - v1 .x) y-intercept ← (slope*v1 .x*(-1))+y-intercept s1 ← split(sortedPoints, slope, y-intercept, +) s2 ← currentSet-s1 ';
+ this.code += pcEntry(0,["qHull(s1 , westMost, eastMost)","qHull(s2 , eastMost, westMost)"],"callOne");
+ this.code += pcEntry(0, ["qHull(currentSet, v1 , v2 )", " if(currentSet.len==0)"," return", " slope ← (v2 .y - v1 .y)/(v2 .x - v1 .x)", " y-intercept ← (slope*v1 .x*(-1))+y-intercept", "max ← [0, point]"], "fnTop");
+ this.code += pcEntry(1, "for (i ← 0 to |currentSet-1|","findMaxLoop");
+ this.code += pcEntry(2, ["if max[0] < distance(vi )", " max ← [distance(vi )], vi "], "findMax")
+ this.code += pcEntry(1, ["slope1 ← (max[1].y - v1 .y)/(max[1].x - v1 .x)", "y-intercept1 ← (slope*v1 .x*(-1))+v1 .y","s1 ← split(currentSet, slope1 , y-intercept1 )"], "firstSplit");
+ this.code += pcEntry(1, ["slope2 ← (v2 .y - max[1].y)/(v2 .x - max[1].x)", "y-intercept2 ← (slope*max[1].x*(-1))+max[1].y","s2 ← split(currentSet, slope2 , y-intercept2 )"], "secondSplit");
+ this.code += pcEntry(1, ["qHull(s1 , v1 , max[1])", "qHull(s2 , max[1]) , v2 "], "postSplits");
+ this.code += pcEntry(0, ["split(set, slope, y-intercept)", " saves ← []", " for (i ← 0 to |set-1|)"], "splitLoop");
+ this.code += pcEntry(2, ["if vi .y > slope*vi .x+y-intercept", " saves+=vi ", " return saves"], "split");
+
+ const hullSeg =`Hull vertices found: 0
+ `;
+ hdxAVCP.update("hullSegments", hullSeg);
+ const recursionTable = 'Recursive Calls ';
+ hdxAVCP.update("recursionStack", recursionTable);
+
+
+ },
+ // setupUI for quickhull av
+ setupUI() {
+
+ let newAO="";
+
+ hdxAV.algOptions.innerHTML = newAO;
+
+ // Setting up the AVCP with the elements that will be used
+ hdxAVCP.add("hullv1", visualSettings.v1);
+ hdxAVCP.add("hullv2", visualSettings.v2);
+ hdxAVCP.add("hullMax", visualSettings.averageCoord);
+ hdxAVCP.add("checkingLine", visualSettings.visiting);
+ hdxAVCP.add("recursionStack", visualSettings.hoverV);
+ hdxAVCP.add("hullSegments", visualSettings.discovered);
+ },
+ // remove any changes made
+ cleanupUI() {
+ for(let i = 0; i < this.hullSegments.length; i++){
+ this.hullSegments[i].remove();
+ }
+ },
+ idOfAction(action) {
+ return action.label;
+ },
+
+ // Calculates the distance from the current vertex to the line to which it is being compared
+ distance(){
+ // finding line that is perpendicular to dividing line
+ let distSlope = ((this.vTwo.lon-this.vOne.lon)/(this.vTwo.lat-this.vOne.lat))*(-1);
+ let distYIntercept = this.currentSet[this.nextToCheck].lon*distSlope*(-1)+this.currentSet[this.nextToCheck].lat;
+ // finding coordinates of point on line
+ let lonValue = (distYIntercept-this.yIntercept)/(this.slope-distSlope);
+ let latValue = distSlope*lonValue+distYIntercept;
+ return exactDistanceInMiles(latValue, lonValue, this.currentSet[this.nextToCheck].lat, this.currentSet[this.nextToCheck].lon)
+ },
+
+ // Adds vertices to the AVCP table and adds line segments to the hull when they are discovered
+ updateHullVertexTable(){
+ // adding to table
+ if(this.hullVertices[0] != this.hullVertices[this.hullVertices.length-1]){
+ document.getElementById("hVertsNum").innerText = this.hullVertices.length;
+ let newTableRow = document.createElement("tr");
+ newTableRow.innerHTML = ``+this.hullVertices[this.hullVertices.length-1].label+`
+ (`+this.hullVertices[this.hullVertices.length-1].lat+`, `+this.hullVertices[this.hullVertices.length-1].lon+`) `;
+ document.getElementById("hullVertexTable").appendChild(newTableRow);
+ }
+ // adding to map hull segments
+ let hullLine = [];
+ hullLine[0] = [this.hullVertices[this.hullVertices.length-2].lat, this.hullVertices[this.hullVertices.length-2].lon];
+ hullLine[1] = [this.hullVertices[this.hullVertices.length-1].lat, this.hullVertices[this.hullVertices.length-1].lon];
+ this.hullSegments.push(L.polyline(hullLine, visualSettings.discovered));
+ this.hullSegments[this.hullSegments.length-1].addTo(map);
+ },
+
+ // Updates shading on map
+ updateShading(){
+ let pollygonObscure;
+ if(this.obscureRegion != null){
+ this.obscureRegion.remove();
+ }
+ thisobscureRegion = [null, null];
+ if(this.vOne.lon < this.vTwo.lon){
+ // Standard
+ pollygonObscure = [[this.vOne.lat, this.vOne.lon], [this.vTwo.lat, this.vTwo.lon], [89, this.vTwo.lon], [89, 179], [-89, 179], [-89, -179], [89, -179], [89, this.vTwo.lon], [this.mostNorth, this.vTwo.lon], [this.mostNorth, this.vOne.lon]];
+ }else{
+ // Underside
+ pollygonObscure = [[this.vOne.lat, this.vOne.lon], [this.vTwo.lat, this.vTwo.lon], [-89, this.vTwo.lon], [-89, -179], [89, -179], [89, 179], [-89, 179], [-89, this.vTwo.lon], [this.mostSouth, this.vTwo.lon], [this.mostSouth, this.vOne.lon]];
+ }
+ this.obscureRegion = L.polygon(pollygonObscure, this.obscure);
+ this.obscureRegion.addTo(map);
+ }
+}
\ No newline at end of file
diff --git a/HighwayDataExaminer/hdxav.js b/HighwayDataExaminer/hdxav.js
index e4a6ed7..5eb778d 100644
--- a/HighwayDataExaminer/hdxav.js
+++ b/HighwayDataExaminer/hdxav.js
@@ -150,6 +150,7 @@ const hdxAV = {
this.avList.push(hdxVertexExtremesSearchAV);
this.avList.push(hdxVertexPairsAV);
this.avList.push(hdxBFConvexHullAV);
+ this.avList.push(hdxQHAV);
this.avList.push(hdxAPClosestPointAV);
this.avList.push(hdxClosestPairsRecAV);
this.avList.push(hdxClickDisAV)
diff --git a/HighwayDataExaminer/index.php b/HighwayDataExaminer/index.php
index e910b0e..efc6426 100644
--- a/HighwayDataExaminer/index.php
+++ b/HighwayDataExaminer/index.php
@@ -93,6 +93,7 @@
+