Sunday, August 7, 2016

Summer Research Week 5 Homework: Code Notes

Continued annotating the code from last year. Progress so far is attached.

We certainly understood the code more, but we are nowhere close to understanding the whole code. We understand the console.log and what require means now. We also have a clue on what's going on in the pCommand and pCommandTimer section.

//ardroneAutonomousControl.js
//image = 640x360
//Blob detection
//Horizontal tracking
//Vertical tracking
//Marks Radius

/* AGENDA

    √ Find blobs
    √ Record edge links
    √ Test bottom camera
    √ Test if edge link detection is done accurately by marking them    NOTE: I'm wondering if links should store an edge? var, if edge finding is asynchronous at all.
    √ Fix glitches with blob detecting
    √   (skipping blobs)
    √   (green on bottom and right borders of the image)
    √ Record radii from center to edge links
    √ Record max-min radial difference
    √ Find blob with largest difference (not average difference)
    √ Get blob line
    √ Find blob line direction
    √ Mark path
    √ Test + fix path marking
    √ Use Ø(droneDirection-blobDirection) to control Yaw angle
    √ Use bottom camera
    • Incorporate second color for junctions, with original functions
    √ Try getting navdata for its orientation to control more accurately it's drift
    √ Figure out how to read navdata (it's not a straight string...)
    √ Use edge pixels when finding junctions and clean up analyzeBlobs()
    √ Incorporate navdata to help hovering
    √ Fix the "max call stack size exceeded" error: don't use recursion for finding blobs anymore.
    √ Fix new errors with findBlobsNoRecursion(): out-of-bounds[√], infinitely-large-blob[√] = problem: pixels that are already links are added as news.
    √ Look up Hough functions that could possibly find lines and replace findBlobsNoRecursion()
    • Fix drone movement:
        try not updating line data if no new line is found [x],
        don't do any command other than HOVER 2x in a row [x],
        allow drone to do same command twice w/ timer [?],
        have path shoulders which help if the drone is lost [ ]
        try sending initial navdata-enabling command to see if altitude and velocity data becomes available [ ]
            > the command is:
            > client.config('general:navdata_demo', 'FALSE');

*/

/* COLOR KEY:
        WHITE:  line marker
        GRAY:   junction marker
        RED:    radius
        BLUE:   center, best path estimation
        YELLOW: path direction head
        GREEN:  edge
 */

var ardrone = require('ar-drone') //the varaible ardrone requires the information from ar-drone
var jimp = require('./jimp-master/index.js')
var math = require('./mathjs-master/index.js')  //Comprehensive math library (used for square root, exponents, absolute value, vector math, etc.)

//Navdata
var orientation = [0.000,0.000,0.000] //direction facing
var origin = [0,0,0] //start location

var client = ardrone.createClient()
var pngImage   //640*360
var markerX = -1
var markerY = -1
var markerR = -1
var pathX = -1
var pathY = -1
var pathA = -1
var erosionFactor = 2
var count = 0
var skipSize = 10
var command = [0,0]     //0,1,2,3,4
var pCommand = [0,0]    //0,1,2,3,4 = HOVER,UP,RIGHT,DOWN,LEFT
var pCommandTimer = [0,0];  //counts how long the drone has been trying the same command
var timeOffCourse = 0;
var color1 = [240,100,100]
var color2 = [240,172,110]

var blobsFound = new BlobLibrary()

client.config("video:video_channel", 1)

var pngStream = client.getPngStream()

pngStream
.on("error", console.log)
.on("data", function(incoming) {
    processImage(incoming)
    })

client.on("navdata", function(navdata) { //regulates the drone's movement?
          getMotionData(navdata)
         
          if (pCommand[0] == command[0]) { //if pCommand[0] is equal to command [0], add
            pCommandTimer[0]++ //adds one to the commandTimer
          }
          else {
            pCommandTimer[0] = 0 //stays the name
          }
          if (pCommandTimer[0] > 50) {
            pCommand[0] = 0
          }
          else {
            pCommand[0] = 0
          }
         
          if (pCommand[1] == command[1]) {
            pCommandTimer[1]++
          }
          else {
            pCommandTimer[1] = 0
          }
          if (pCommandTimer[1] > 45) {
            pCommand[1] = 0
          }
          else {
            pCommand[1] = 0
          }
         
          controlFlight()
          count++
          })

if (count < 30) {
    client.takeoff()
}

//.................................................................... DECLARATION

function getMotionData(navdata) {                   //I wanted to stabilize the drone by countering it's lean
    if (count > 10) {
        if (count < 30) { //origin = beginning
            origin[0] = navdata.demo.rotation.roll
            origin[1] = navdata.demo.rotation.pitch
            origin[2] = navdata.demo.rotation.yaw
        }
        else { //orientation = facing
            orientation[0] = navdata.demo.rotation.roll
            orientation[1] = navdata.demo.rotation.pitch
            orientation[2] = navdata.demo.rotation.yaw
        }
    }
}

function controlFlight() {                         //Control drone based on given path (X,Y,A)
    if (count < 500 && count > 50) {
        if (pathA > -1 && pathX > -1 && pathY > -1) {
            var distance = math.sqrt(math.pow(pathX-(640*0.5),2) + math.pow(pathY-(320*0.5),2))
            var angleV = math.pi * 1.5
            angleV = pathA - angleV
           
            if (distance > 320/3) {                                                                                 //CENTER OVER THE PATH OR MOVE FORWARD
                timeOffCourse++;
                var xMore = false;
               
                var xV = pathX - (640*0.5)
                var yV = pathY - (320*0.5)
               
                if (math.abs(xV) > math.abs(yV)) {
                    xMore = true;
                }
               
                xV /= math.abs(xV)
                yV /= math.abs(yV)
               
                if ((timeOffCourse*0.001) < 0.04) {
                    xV *= 0.05 - (timeOffCourse*0.0005)
                    yV *= 0.05 - (timeOffCourse*0.0005)
                }
                else {
                    xV *= 0.005; //0.01
                    yV *= 0.005;
                }
               
                if (xV > 0.0) {
                    command[0] = 2
                }
                else if (xV < 0.0) {
                    command[0] = 4
                }
                if (yV > 0.0) {
                    command[1] = 3
                }
                else if (yV < 0.0) {
                    command[1] = 1
                }
               
                client.stop()
                if ((pCommand[1] == 0 || pCommand[1] != command[1]) && !xMore) {
                    if (command[1] == 1) {
                        client.front(math.abs(yV))
                        console.log("FRONT") 
                    }
                    else if (command[1] == 3) {
                        client.back(math.abs(yV))
                        console.log("BACK")
                    }
                }
                if ((pCommand[0] == 0 || pCommand[0] != command[0]) && xMore) {
                    if (command[0] == 2) {
                        client.right(math.abs(xV))
                        console.log("RIGHT")
                    }
                    else if (command[0] == 4) {
                        client.left(math.abs(xV*1.5))
                        console.log("LEFT") //if certain conditions are met, then it will display which direction to the move the drone?
                    }
                }
            }
            else {
                timeOffCourse = 0;
               
                if (distance < 320/3 && math.abs(angleV) > 0/*(math.pi*0.1)*/) {     //ROTATE
                    client.stop()
                    if (math.abs(angleV) < (math.pi*0.5)) {
                        if (angleV > 0) {
                            client.clockwise(0.1)
                            console.log("CLOCK")
                        }
                        else if (angleV < 0) {
                            client.counterClockwise(0.1)
                            console.log("COUNTER")
                        }
                    }
                    else {
                        console.log("PATH IS PERPENDICULAR")
                    }
                }
                if (distance < 320/3) {  //HOVER
    //                if (orientation[0] < origin[0]-4) {
    //                    client.right(0.08)
    //                }
    //                else if (orientation[0] > origin[0]+4) {
    //                    client.left(0.08)
    //                }
    //                if (orientation[1] < origin[1]-4) {
    //                    client.back(0.08)
    //                }
    //                else if (orientation[1] >origin[1]+4) {
    //                    client.front(0.08)
    //                }
                    client.stop()
                    client.front(0.02);
                    command = [0,0]
                    console.log("PATH FOUND :)") //found path
                }
            }
        }
        else {                                                                                                      //HOVER
//            if (orientation[0] < origin[0]-4) {
//                client.right(0.08)
//            }
//            else if (orientation[0] > origin[0]+4) {
//                client.left(0.08)
//            }
//            if (orientation[1] < origin[1]-4) {
//                client.back(0.08)
//            }
//            else if (orientation[1] > origin[1]+4) {
//                client.front(0.08)
//            }
            command = [0,0]
            console.log("LOST :(") //can't locate path
        }
    }
    else {
        if ((count > 500 || count == 500) && count < 510) { //if count meets criteria, drone lands
            client.stop()
            client.land()
        }
    }
}

function processImage(input) {                     //Find path and junction in image
    pngImage = input
    jimp.read(pngImage, function(err, image) {
              if (err) throw err
              image = thresholdImage(image)
              findBlobsNoRecursion(image)
              analyzeBlobs()
              var line = findLines()
//              var marker = findJunctions()
//             
//              if (marker[0] > -1 && marker[1] > -1) {
//                image.setPixelColor(jimp.rgbaToInt(255,0,0,255),marker[0],marker[1])
//                for (var i=0; i<marker[2]; i++) {
//                  if (marker[0] + i + 1 < image.bitmap.width) {
//                    image.setPixelColor(jimp.rgbaToInt(255,0,0,255),marker[0]+i+1,marker[1])
//                  }
//                }
//              }
//              else {
//                //console.log("NO JUNCTIONS")
//              }
             
              if (line[0] > -1 && line[1] > -1 && line[2] > -1) {
                var vectorX = math.cos(line[2]) * 1
                var vectorY = math.sin(line[2]) * 1
             
                for (var i=1; i<20; i++) {
                    image.setPixelColor(jimp.rgbaToInt(0,100,255,255),line[0] + math.round(vectorX*i),line[1] + math.round(vectorY*i))
                }
                image.setPixelColor(jimp.rgbaToInt(255,255,0,255),line[0] + math.round(vectorX*20),line[1] + math.round(vectorY*20))
                pathX = line[0]
                pathY = line[1]
                pathA = line[2]
              }
              else {
                //console.log("NO LINES")
              }
             
              markBlobs(image)
             
              //image.write("./droneControlOutput/img_" + count + ".png")
             
//              markerX = marker[0]
//              markerY = marker[1]
//              markerR = marker[2]
              })
}

function thresholdImage(image) {                    //Color thresholding (looking at image to figure things out, color-wise)
    for (var y = 0; y < image.bitmap.height - skipSize; y += skipSize) {
        for (var x = 0; x < image.bitmap.width - skipSize; x += skipSize) {
            var color = jimp.intToRGBA(image.getPixelColor(x,y))
            if (color.r / color.b > (color1[0]/color1[2]) - 1.5 && color.r / color.b < (color1[0]/color1[2]) + 2.5 && color.r / color.g > (color1[0]/color1[1]) - 1 && color.r / color.g < (color1[0]/color1[1]) + 2.5) {     //~ORANGE
                image.setPixelColor(jimp.rgbaToInt(255,255,255,255),x,y)
            }
            /*else if (color.r / color.b > (color2[0]/color2[2]) - 0.5 && color.r / color.b < (color2[0]/color2[2]) + 0.5 && color.r / color.g > (color2[0]/color2[1]) - 0.5 && color.r / color.g < (color2[0]/color2[1]) + 0.5) {  //GREEN
                image.setPixelColor(jimp.rgbaToInt(100,100,100,255),x,y)
            }*/
            else {
                image.setPixelColor(jimp.rgbaToInt(0,0,0,255),x,y)
            }
        }
    }
   
    return image
}

function findBlobsNoRecursion(image) {            //Find groups of pixels of the same color (grouping colors)
    blobsFound.blobs = []   //clear blobs from previous image
    var pixNums = [0,0]     //just to keep track of how many pixels were kept vs. how many were not after thresholding
   
    for (var startY = 0; startY < image.bitmap.height - skipSize; startY += skipSize) {     //Loop through all pixels (accounting for skipSize) in the image
        for (var startX = 0; startX < image.bitmap.width - skipSize; startX += skipSize) { 
            var color = jimp.intToRGBA(image.getPixelColor(startX,startY))  //Get color of current pixel (startX,startY)
            var inBlob = false
           
            if (color.b > 0) {                                      //**COMMENT NOT FOR MR LIN** type1 = 255, type2 = 100
                pixNums[0]++
               
                for (var i=0; i<blobsFound.blobs.length; i++) {     //Loop through all blobs found so far to check if current pixel has already been used
                    for (var j=0; j<blobsFound.blobs[i].links.length && inBlob == false; j++) {
                        if (blobsFound.blobs[i].links[j].x == startX && blobsFound.blobs[i].links[j].y == startY) {
                            inBlob = true
                        }
                    }
                }
            }
            else {
                pixNums[1]++
            }
           
            if (inBlob == false && color.b > 0) {   //If pixel is within threshold and not already used, then create a new blob
                var edges = []  //A selection of links that will be used to find blob radii outside of findBlobsNoRecursion()
                var links = []  //Points that will make up the new blob
                var news = []   //Points that haven't been checked yet for new neighboring white pixels
               
                news.push(new Link(startX,startY))  //Add first pixel to news
                var iteration=0     //Just for me to see how long it takes for the program to finish the blob
               
                while (news.length > 0) {   //While there are still pixels whose neighbors are not checked...
                    var len = news.length   //Number of pixels which, as of now, aren't checked
                   
                    for (var i = len-1; i > -1; i--) {  //Loop through current news[] pixels from last to first (won't include pixels added to the array later in the process)
                        var x = news[i].x   //store location of new pixel to be checked
                        var y = news[i].y
                       
                        if (y-skipSize > 0 && y+skipSize < image.bitmap.height && x-skipSize > 0 && x+skipSize < image.bitmap.width) {  //make sure new pixel is not at the edge of the image
                            color = jimp.intToRGBA(image.getPixelColor(x,y-skipSize))   //START: check neighbor above
                            if (color.b == 255) {   //if neighbor is white
                                var used = false
                                for (var j=0; j<news.length && used == false; j++) {    //loop through new pixels
                                    if (news[j].x == x && news[j].y == y-skipSize) {    //check if neighbor is already added
                                        used = true
                                    }
                                }
                               
                                if (used == false) {
                                    for (var j=0; j<links.length && used == false; j++) {    //loop through saved pixels (already in blob)
                                        if (links[j].x == x && links[j].y == y-skipSize) {  //check if neighbor is already used
                                            used = true
                                        }
                                    }
                                    if (used == false) {
                                        news.push(new Link(x,y-skipSize))   //add neighbor to news[]
                                    }
                                }
                            }   //END: check neighbor above
                           
                            color = jimp.intToRGBA(image.getPixelColor(x,y+skipSize))   //START: check neighbor below
                            if (color.b == 255) {
                                var used = false
                                for (var j=0; j<news.length && used == false; j++) {
                                    if (news[j].x == x && news[j].y == y+skipSize) {
                                        used = true
                                    }
                                    if (used) {
                                        break
                                    }
                                }
                               
                                if (used == false) {
                                    for (var j=0; j<links.length && used == false; j++) {
                                        if (links[j].x == x && links[j].y == y+skipSize) {
                                            used = true
                                        }
                                    }
                                    if (used == false) {
                                        news.push(new Link(x,y+skipSize))
                                    }
                                }
                            }   //END: check neighbor below
                           
                            color = jimp.intToRGBA(image.getPixelColor(x-skipSize,y))   //START: check neighbor left
                            if (color.b == 255) {
                                var used = false
                                for (var j=0; j<news.length && used == false; j++) {
                                    if (news[j].x == x-skipSize && news[j].y == y) {
                                        used = true
                                    }
                                }

                                if (used == false) {
                                    for (var j=0; j<links.length && used == false; j++) {
                                        if (links[j].x == x-skipSize && links[j].y == y) {
                                            used = true
                                        }
                                    }
                                    if (used == false) {
                                        news.push(new Link(x-skipSize,y))
                                    }
                                }
                            }   //END: check neighbor left
                           
                            color = jimp.intToRGBA(image.getPixelColor(x+skipSize,y))   //START: check neighbor right
                            if (color.b == 255) {
                                var used = false
                                for (var j=0; j<news.length && used == false; j++) {
                                    if (news[j].x == x+skipSize && news[j].y == y) {
                                        used = true
                                    }
                                }
                               
                                if (used == false) {
                                    for (var j=0; j<links.length && used == false; j++) {
                                        if (links[j].x == x+skipSize && links[j].y == y) {
                                            used = true
                                        }
                                    }
                                    if (used == false) {
                                        news.push(new Link(x+skipSize,y))
                                    }
                                }
                            } //END: check neighbor right
                        }
                       
                        if (isEdge(image,x,y,1)) {  //check if new pixel is an edge
                            edges.push(new Link(x,y))   //add new pixel to edges[] (for calculating blob's radii later)
                        }
                       
                        links.push(news[i]) //add this pixel to the new blob
                        news.splice(i,1)    //remove this pixel from news[], as it's now checked
                    }
                    iteration++
                }
               
                if (links.length > 5) { //only add blob if it's size is somewhat significant
                    //console.log("...BLOB ADDED @ " + startX + "," + startY) //print blob's initial point
                    blobsFound.addBlob(1)   //add an empty blob (constructor is not currently important)
                    blobsFound.blobs[blobsFound.blobs.length-1].links = links   //fill blob's links[] array
                    blobsFound.blobs[blobsFound.blobs.length-1].edges = edges   //fill blob's edges[] array
                }
                else {
                    //console.log("BLOB TOO SMALL")
                }
            }
        }
    }
   
    //console.log("+: " + pixNums[0] + ", -: " + pixNums[1])  //not important
}

function isEdge(image, x, y, type) {        //Edges used for finding the radii of a blob
    var neighbors = 0
    var color
   
    if (x+skipSize < image.bitmap.width && y-skipSize > 0) {
        color = jimp.intToRGBA(image.getPixelColor(x+skipSize,y-skipSize))
        if (color.b == 255) {
            neighbors++
        }
    }
   
    if (x+skipSize < image.bitmap.width) {
        color = jimp.intToRGBA(image.getPixelColor(x+skipSize,y))
        if (color.b == 255) {
            neighbors++
        }
    }
   
    if (x+skipSize < image.bitmap.width && y+skipSize < image.bitmap.height) {
        color = jimp.intToRGBA(image.getPixelColor(x+skipSize,y+skipSize))
        if (color.b == 255) {
            neighbors++
        }
    }
   
    if (y+skipSize < image.bitmap.height) {
        color = jimp.intToRGBA(image.getPixelColor(x,y+skipSize))
        if (color.b == 255) {
            neighbors++
        }
    }
   
    if (x-skipSize > 0 && y+skipSize < image.bitmap.height) {
        color = jimp.intToRGBA(image.getPixelColor(x-skipSize,y+skipSize))
        if (color.b == 255) {
            neighbors++
        }
    }
   
    if (x-skipSize > 0) {
        color = jimp.intToRGBA(image.getPixelColor(x-skipSize,y))
        if (color.b == 255) {
            neighbors++
        }
    }
   
    if (x-skipSize >0 && y-skipSize > 0) {
        color = jimp.intToRGBA(image.getPixelColor(x-skipSize,y-skipSize))
        if (color.b == 255) {
            neighbors++
        }
    }
   
    if (y-skipSize > 0) {
        color = jimp.intToRGBA(image.getPixelColor(x,y-skipSize))
        if (color.b == 255) {
            neighbors++
        }
    }
   
    if (neighbors > 1 && neighbors < 7) {
        return true
    }
    else {
        return false
    }
}

function markBlobs(image) {                 //Show where the program found blobs
    for (var i=0; i<blobsFound.blobs.length; i++) {
        if (blobsFound.blobs[i].links.length > 5) {
            var location = [blobsFound.blobs[i].aspects[0],blobsFound.blobs[i].aspects[1]]
           
            image.setPixelColor(jimp.rgbaToInt(0,100,255,255),math.round(location[0]),math.round(location[1]))
           
            for (var j=0; j<blobsFound.blobs[i].edges.length; j++) {
                location = [blobsFound.blobs[i].edges[j].x,blobsFound.blobs[i].edges[j].y]
                image.setPixelColor(jimp.rgbaToInt(0,255,0,255),location[0],location[1])
            }
        }
    }
}

function analyzeBlobs() {                 //Calculate data of a blob
    for (var i=0; i<blobsFound.blobs.length; i++) {
        blobsFound.blobs[i].calculateCenterRadii()
       
        if (blobsFound.blobs[i].aspects[7] == 1) {
            blobsFound.blobs[i].calculateLinearityDirection()
        }
        else if (blobsFound.blobs[i].aspects[7] == 2) {
            blobsFound.blobs[i].calculateCircularity()
        }
    }
}

function findLines() {                  //Use blob data to find most likely path
    var Lnum = 0;
    var bestLine = [2]
    bestLine[0] = 0
    bestLine[1] = 0
   
    for (var i=0; i<blobsFound.blobs.length; i++) {
        if (blobsFound.blobs[i].aspects[7] == 1 && blobsFound.blobs[i].links.length > 10) {
            if (blobsFound.blobs[i].aspects[5] > bestLine[0]) {
                bestLine[0] = blobsFound.blobs[i].aspects[5]
                bestLine[1] = i
            }
            Lnum++
        }
    }
   
    if (blobsFound.blobs.length > 0 && Lnum > 0) {
        var lineHeading = blobsFound.blobs[bestLine[1]].aspects[6]
        var angleDifference = math.abs((math.pi*1.5) - lineHeading)
       
        if (angleDifference > math.pi) {
            angleDifference = (2*math.pi) - angleDifference
        }
        if (angleDifference > 0.5*math.pi) {
            lineHeading += math.pi
        }
       
        if (lineHeading > 2*math.pi) {
            lineHeading -= 2*math.pi
        }
       
        var lineData = [blobsFound.blobs[bestLine[1]].aspects[0],blobsFound.blobs[bestLine[1]].aspects[1],lineHeading]
    }
    else {
        var lineData = [-1,-1,-1]
    }
   
    return lineData
}

function findJunctions() {               //Use blob data to find most likely junction
    var Jnum = 0
   
    var bestCircularity = [2]       //circularity, blob#
    bestCircularity[0] = 20
    bestCircularity[1] = 0
   
    var bestDensity = [2]           //density,     blob#
    bestDensity[0] = 0
    bestDensity[1] = 0
   
    var bestBlob = 0
   
    for (var i=0; i<blobsFound.blobs.length; i++) {
        if (blobsFound.blobs[i].aspects[7] == 2 && blobsFound.blobs[i].links.length > 20) {
            Jnum++
           
            var circularity = blobsFound.blobs[i].aspects[3]
            if (circularity < bestCircularity[0]) {
                bestCircularity[0] = circularity
                bestCircularity[1] = i
                bestBlob = i
            }
           
            var density = blobsFound.blobs[i].aspects[4]    //Not used right now...
        }
    }
   
    if (blobsFound.blobs.length > 0 && Jnum > 0) {
        var junctionData = [blobsFound.blobs[bestBlob].aspects[0],blobsFound.blobs[bestBlob].aspects[1],blobsFound.blobs[bestBlob].aspects[2]]
    }
    else {
        var junctionData =[-1,-1,-1]
    }
   
    return junctionData
}

function BlobLibrary() {
    this.blobs = []
}

BlobLibrary.prototype.addBlob = function(color) {
    this.blobs = this.blobs.concat(new Blob(color))
}

function Blob(color) {
    this.links = []
    this.edges = []
    this.radii = []
    this.aspects = [8]
    this.aspects[0] = 320   //X
    this.aspects[1] = 200   //Y
    this.aspects[2] = 50    //R adius
    this.aspects[3] = 3     //C ircularity
    this.aspects[4] = 5     //D ensity
    this.aspects[5] = 0     //L inearity
    this.aspects[6] = 0     //A ngle
    this.aspects[7] = color //C olor (1=line,2=junction)
}

Blob.prototype.addLink = function(x, y) {
    this.links = this.links.concat(new Link(x, y))
}

Blob.prototype.addEdge = function(x, y) {
    this.edges = this.edges.concat(new Link(x, y))
}

Blob.prototype.calculateCenterRadii = function() {
    var X = 0
    var Y = 0
    var edgeRadii = [this.edges.length]
   
    for (var i=0; i<this.links.length; i++) {
        X += this.links[i].x
        Y += this.links[i].y
    }
   
    X /= this.links.length
    Y /= this.links.length
   
    this.aspects[0] = X
    this.aspects[1] = Y
   
    for (var i=0; i<this.edges.length; i++) {
        var edgeRadius = math.sqrt(math.pow(this.edges[i].x - this.aspects[0],2) + math.pow(this.edges[i].y - this.aspects[1],2))
        edgeRadii[i] = edgeRadius
    }
   
    this.radii = edgeRadii
   
    if (this.radii.length > 0) {
        var avgRadius = 0
       
        for (var i=0; i<this.radii.length; i++) {
            avgRadius += this.radii[i]
        }
        avgRadius /= this.radii.length
       
        this.aspects[2] = avgRadius
    }
}

Blob.prototype.calculateCircularity = function() {
    if (this.radii.length > 0) {
        var avgDifference = 0
       
        for (var i=0; i<this.radii.length; i++) {
            avgDifference += (this.radii[i] - this.aspects[2])
        }
        avgDifference /= this.radii.length
       
        this.aspects[3] = avgDifference
    }
   
    this.aspects[4] = this.links.length / this.aspects[2]
}

Blob.prototype.calculateLinearityDirection = function() {
    var shortest = 700
    var longest = 0
    var arrow = [1,1]
   
    for (var i=0; i<this.radii.length; i++) {
        var edgeRadius = this.radii[i]
       
        if (edgeRadius < shortest) {
            shortest = edgeRadius
        }
        if (edgeRadius > longest) {
            longest = edgeRadius
            arrow[0] = this.edges[i].x - this.aspects[0]
            arrow[1] = this.edges[i].y - this.aspects[1]
        }
    }
   
    var linearity = longest - shortest
   
    this.aspects[5] = linearity
   
    var angle = math.atan2(math.abs(arrow[1]), math.abs(arrow[0]))
   
    if (arrow[0] < 0 && arrow[1] > 0) {
        angle = math.pi - angle
    }
    else if (arrow[0] < 0 && arrow[1] < 0) {
        angle = math.pi + angle
    }
    else if (arrow[0] > 0 && arrow[1] < 0) {
        angle = (2*math.pi) - angle
    }
   
    this.aspects[6] = angle
}

function Link(x, y) {
    this.x = x
    this.y = y
}

Summer Research Week 5 Homework

Video Notes

Object Factory, thenewboston 11


  • to create a new object
    • must give them a function that returns
  • an object that makes other objects
  • each have their own information instead of sharing

In this video, we learned about giving each variable unique characteristics instead of having all the variables share everything. The video was very concise about it and we understood the information. We think that this could be useful in the drone project because every movement in the drone is different.


Core Modules, thenewboston 12
  • there are a lot of core modules 
    • modules that are already in node.js
  • fs
    • file system module
    • read, write, and delete files
      • to make one fs.writeFileSync
        • path = file name
        • data = content
        • options = optional
      • console.log(variable.readileSync(text).toString
        • reads and logs it out
  • whenever you use core module
    • do not include path
      • makes node.js find it easier
    • variable = module to make it simple 
  • path
    • normalizes slashes depending on OS
    • console.log(path.normalize(variable))
    • can also fix errors
      • usage: web servers
    • console.log(path.dirname(variable))
      • gives the directory of the file
    • console.log (path.basename(variable))
      • gives the base name
    • console.log (path.extname(variable))
      • what type of file
  • setInterval 
    • repeats continuously for the set amount of time
    • put consolelog inside to have a message repeat in a certain interval
    • different from timeout
  • consolelog(_dirname)
    • prints out the directory name of whatever file is currently calling the code
  • consolelog(_filename)
    • prints out the entire file name of whatever file is currently calling the code

In this video, there was a lot of information to take in. This part of the node.js tutorial by thenewboston introduced us to Core Modules; this built onto the information we learned from the previous week. There are many useful, built-in modules that help us with our coding experience; for example, the setInterval could be extremely beneficial for us because we can have it record the condition of the drone continuously. 


Creating a Basic Server, thenewboston 13
  • http module already built in
  • http variable.createServer
    • when connect to server, server has to listen to request
      • have a port number
  • function onRequest (request, response)
    • request
      • from the connector
      • can see what they were doing or looking at
    • response
      • tells them the connectivity and such
      • .writeHead
      • .write gives them a writing
      • .end tells the writing ends
    • 200 status code is good
    • 404 means that something did not go well
  • browser has two requests
    • whatever page is looking for
    • also requests for the favicon

In this video, we learned about creating a server. We understood the content in the video, but we did not really see how it connected with our project. This basically taught us how to make a website which could be helpful in life, but we did not see how it would help us.


Simple Web File Server, thenewboston 14
  • require fs module
  • require http
  • GET
    • standard way to connect to server
  • / = homepage
  • text plain = plain string of text
  • text html = html file
  • fs.createReadStream
    • read the file
    • .pipe
      • put out the response object
  • else
    • if something is not right

In this video, we understood more about creating a server. Although this might not directly relate to our project, we thought that these tutorials helped our brains understand coding much more. Since these were topics that we were not familiar with, we had to keep rewinding the video to understand what the code did and why does the code work that way.


Connect, thenewboston 15
  • terminal
    • npm install connect
  • http.createServer
  • goes a certain order by doing another method in the "stack"
    • app.use(variable)
    • doing next will allow us to go to the next one one the list
  • function doFirst (request, response, next)
  • you can organize the path based on the requests received
    • app.use ('/variable,variable)
  • can see what webpage people are going to use and will lead them to it

In this video, we learned about Connect. We found this very helpful because we could organize the code according to the order it will occur. This is useful because it could prioritize more important matters first when working with the drone. We comprehend the material, but we thought that it was still confusing because we had to work with so many different variables. With Connect, we had to make sure everything was aligned properly. 


Express, thenewboston 16
  • file -> new project
    • node.js and NPM
      • express APP
  • template
    • how to write plain HTML templates
    • EJS
  • bin
    • www
      • go to the port inside (3000)
  • idea
    • do not touch
  • in express, www file is the start up script
    • app js is the core
    • node_module
      • for any other modules 
  • anything that users can use is going to public
  • brains of each page is routes
    • index
    • user
  • views
    • html for each page

In this video, we had a lot of content to take in similar to the 12th part of the node.js tutorial. This ws a brand new concept that we had to take in. There was a lot of information thrown at us and we started to get confused, but thenewboston explained it well. He briefly told us what each directory meant and how it affected Express as a whole. We were less confused by the end of the video, but we were curious about each directory. 


Handling User Requests, thenewboston 17
  • app.use
    • tells the system what happens
  • same idea as Connect
  • send the variable to a certain page
    • goes to the brain and it sends out the information

In this video, there were not that many notes to take down because all of the variables were explained briefly. This video basically added onto the last one by breaking down the function of the directories. It also made us recall the information from the fifteenth video. It shows how one directory links with the other ones. 


Understanding app.js, thenewboston 18
  • logger
    • module is used for logging out information in the terminal
  • cookieParser
    • handling cookies
  • bodyParser
    • parse text body or Jason body
  • gives a detailed information of how something is wrong
    • error handler
  • during development
    • want to stack up the errors
  • during actual production
    • hide the errors

In this video, the information was explained more thoroughly and there was nothing much to really take notes on. We learned about why the error handler was broken into two parts and we were fascinated about it because we never knew that web designers did this. 


EJS, thenewboston 19
  • html or the view for the webpage
  • EJS
    • embedded javascript
    • html that javascript can be plugged into
  • equal sign are for variables
  • no equal sign are for plain javascript
  • you can attach variables and extend it into the ejs
    • for example variable "name" can be Bob and if "name" was written in ejs, then Bob will be written

In this video, we learned many new things. HTML was the view for the webpage, EJS is Embedded Javascript and it is basically HTML that JavaScript can be plugged into, and we learned two crucial pieces of information. Firstly, when we type in EJS, we need equal signs for variables and we do not include equal signs for plain JavaScript. Secondly, we can tidy up the code and make everything less intimidating by incorporating variables in EJS and we can use this variable again in different files. This would reduce the wordiness and it makes everything more neat.


Adding More Pages, thenewboston 20
  • add a new directory to views
    • inside new directory, add new file
  • when add a new file, then add new file in route
  • separate using |
  • on homepage, include the navigation 
  • in app.js we need to add all the other pages
  • by separating everything, we don't need to keep adjusting
  • new view, new route, adjust app.js 

In this video, we learned more about organizing a web page. Although this isn't exactly what we are going to do in our Advanced STEM Research project, we found it interesting to learn about. Since we are going on different websites daily, the coding behind it was very intriguing. The material was summed up well and it was presented in a way that was simple to understand like most of thenewboston's video.


Overall, I think that we learned a lot more about coding this week than any other week. Even though most of the information will not be applied in our project, we found it entertaining to learn about because the YouTuber seemed like a pretty cool guy. He presented the material in a simple and education manner that even beginners could comprehend. We were also reviewing the code that was done last year to see if we improved our knowledge.