+30
@@ -0,0 +1,30 @@
|
||||
function DimensionD(width, height) {
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
if (width !== null && height !== null) {
|
||||
this.height = height;
|
||||
this.width = width;
|
||||
}
|
||||
}
|
||||
|
||||
DimensionD.prototype.getWidth = function ()
|
||||
{
|
||||
return this.width;
|
||||
};
|
||||
|
||||
DimensionD.prototype.setWidth = function (width)
|
||||
{
|
||||
this.width = width;
|
||||
};
|
||||
|
||||
DimensionD.prototype.getHeight = function ()
|
||||
{
|
||||
return this.height;
|
||||
};
|
||||
|
||||
DimensionD.prototype.setHeight = function (height)
|
||||
{
|
||||
this.height = height;
|
||||
};
|
||||
|
||||
module.exports = DimensionD;
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
function Emitter(){
|
||||
this.listeners = [];
|
||||
}
|
||||
|
||||
var p = Emitter.prototype;
|
||||
|
||||
p.addListener = function( event, callback ){
|
||||
this.listeners.push({
|
||||
event: event,
|
||||
callback: callback
|
||||
});
|
||||
};
|
||||
|
||||
p.removeListener = function( event, callback ){
|
||||
for( var i = this.listeners.length; i >= 0; i-- ){
|
||||
var l = this.listeners[i];
|
||||
|
||||
if( l.event === event && l.callback === callback ){
|
||||
this.listeners.splice( i, 1 );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
p.emit = function( event, data ){
|
||||
for( var i = 0; i < this.listeners.length; i++ ){
|
||||
var l = this.listeners[i];
|
||||
|
||||
if( event === l.event ){
|
||||
l.callback( data );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Emitter;
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
var UniqueIDGeneretor = require('./UniqueIDGeneretor');
|
||||
|
||||
function HashMap() {
|
||||
this.map = {};
|
||||
this.keys = [];
|
||||
}
|
||||
|
||||
HashMap.prototype.put = function (key, value) {
|
||||
var theId = UniqueIDGeneretor.createID(key);
|
||||
if (!this.contains(theId)) {
|
||||
this.map[theId] = value;
|
||||
this.keys.push(key);
|
||||
}
|
||||
};
|
||||
|
||||
HashMap.prototype.contains = function (key) {
|
||||
var theId = UniqueIDGeneretor.createID(key);
|
||||
return this.map[key] != null;
|
||||
};
|
||||
|
||||
HashMap.prototype.get = function (key) {
|
||||
var theId = UniqueIDGeneretor.createID(key);
|
||||
return this.map[theId];
|
||||
};
|
||||
|
||||
HashMap.prototype.keySet = function () {
|
||||
return this.keys;
|
||||
};
|
||||
|
||||
module.exports = HashMap;
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
var UniqueIDGeneretor = require('./UniqueIDGeneretor');
|
||||
|
||||
function HashSet() {
|
||||
this.set = {};
|
||||
}
|
||||
;
|
||||
|
||||
HashSet.prototype.add = function (obj) {
|
||||
var theId = UniqueIDGeneretor.createID(obj);
|
||||
if (!this.contains(theId))
|
||||
this.set[theId] = obj;
|
||||
};
|
||||
|
||||
HashSet.prototype.remove = function (obj) {
|
||||
delete this.set[UniqueIDGeneretor.createID(obj)];
|
||||
};
|
||||
|
||||
HashSet.prototype.clear = function () {
|
||||
this.set = {};
|
||||
};
|
||||
|
||||
HashSet.prototype.contains = function (obj) {
|
||||
return this.set[UniqueIDGeneretor.createID(obj)] == obj;
|
||||
};
|
||||
|
||||
HashSet.prototype.isEmpty = function () {
|
||||
return this.size() === 0;
|
||||
};
|
||||
|
||||
HashSet.prototype.size = function () {
|
||||
return Object.keys(this.set).length;
|
||||
};
|
||||
|
||||
//concats this.set to the given list
|
||||
HashSet.prototype.addAllTo = function (list) {
|
||||
var keys = Object.keys(this.set);
|
||||
var length = keys.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
list.push(this.set[keys[i]]);
|
||||
}
|
||||
};
|
||||
|
||||
HashSet.prototype.size = function () {
|
||||
return Object.keys(this.set).length;
|
||||
};
|
||||
|
||||
HashSet.prototype.addAll = function (list) {
|
||||
var s = list.length;
|
||||
for (var i = 0; i < s; i++) {
|
||||
var v = list[i];
|
||||
this.add(v);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = HashSet;
|
||||
+567
@@ -0,0 +1,567 @@
|
||||
/**
|
||||
* This class maintains a list of static geometry related utility methods.
|
||||
*
|
||||
*
|
||||
* Copyright: i-Vis Research Group, Bilkent University, 2007 - present
|
||||
*/
|
||||
|
||||
const Point = require('./Point');
|
||||
|
||||
function IGeometry() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calculates *half* the amount in x and y directions of the two
|
||||
* input rectangles needed to separate them keeping their respective
|
||||
* positioning, and returns the result in the input array. An input
|
||||
* separation buffer added to the amount in both directions. We assume that
|
||||
* the two rectangles do intersect.
|
||||
*/
|
||||
IGeometry.calcSeparationAmount = function (rectA, rectB, overlapAmount, separationBuffer)
|
||||
{
|
||||
if (!rectA.intersects(rectB)) {
|
||||
throw "assert failed";
|
||||
}
|
||||
|
||||
let directions = new Array(2);
|
||||
|
||||
this.decideDirectionsForOverlappingNodes(rectA, rectB, directions);
|
||||
|
||||
overlapAmount[0] = Math.min(rectA.getRight(), rectB.getRight()) -
|
||||
Math.max(rectA.x, rectB.x);
|
||||
overlapAmount[1] = Math.min(rectA.getBottom(), rectB.getBottom()) -
|
||||
Math.max(rectA.y, rectB.y);
|
||||
|
||||
// update the overlapping amounts for the following cases:
|
||||
if ((rectA.getX() <= rectB.getX()) && (rectA.getRight() >= rectB.getRight()))
|
||||
{
|
||||
/* Case x.1:
|
||||
*
|
||||
* rectA
|
||||
* | |
|
||||
* | _________ |
|
||||
* | | | |
|
||||
* |________|_______|______|
|
||||
* | |
|
||||
* | |
|
||||
* rectB
|
||||
*/
|
||||
overlapAmount[0] += Math.min((rectB.getX() - rectA.getX()),
|
||||
(rectA.getRight() - rectB.getRight()));
|
||||
}
|
||||
else if ((rectB.getX() <= rectA.getX()) && (rectB.getRight() >= rectA.getRight()))
|
||||
{
|
||||
/* Case x.2:
|
||||
*
|
||||
* rectB
|
||||
* | |
|
||||
* | _________ |
|
||||
* | | | |
|
||||
* |________|_______|______|
|
||||
* | |
|
||||
* | |
|
||||
* rectA
|
||||
*/
|
||||
overlapAmount[0] += Math.min((rectA.getX() - rectB.getX()),
|
||||
(rectB.getRight() - rectA.getRight()));
|
||||
}
|
||||
if ((rectA.getY() <= rectB.getY()) && (rectA.getBottom() >= rectB.getBottom()))
|
||||
{
|
||||
/* Case y.1:
|
||||
* ________ rectA
|
||||
* |
|
||||
* |
|
||||
* ______|____ rectB
|
||||
* | |
|
||||
* | |
|
||||
* ______|____|
|
||||
* |
|
||||
* |
|
||||
* |________
|
||||
*
|
||||
*/
|
||||
overlapAmount[1] += Math.min((rectB.getY() - rectA.getY()),
|
||||
(rectA.getBottom() - rectB.getBottom()));
|
||||
}
|
||||
else if ((rectB.getY() <= rectA.getY()) && (rectB.getBottom() >= rectA.getBottom()))
|
||||
{
|
||||
/* Case y.2:
|
||||
* ________ rectB
|
||||
* |
|
||||
* |
|
||||
* ______|____ rectA
|
||||
* | |
|
||||
* | |
|
||||
* ______|____|
|
||||
* |
|
||||
* |
|
||||
* |________
|
||||
*
|
||||
*/
|
||||
overlapAmount[1] += Math.min((rectA.getY() - rectB.getY()),
|
||||
(rectB.getBottom() - rectA.getBottom()));
|
||||
}
|
||||
|
||||
// find slope of the line passes two centers
|
||||
let slope = Math.abs((rectB.getCenterY() - rectA.getCenterY()) /
|
||||
(rectB.getCenterX() - rectA.getCenterX()));
|
||||
// if centers are overlapped
|
||||
if ((rectB.getCenterY() === rectA.getCenterY()) &&
|
||||
(rectB.getCenterX() === rectA.getCenterX()))
|
||||
{
|
||||
// assume the slope is 1 (45 degree)
|
||||
slope = 1.0;
|
||||
}
|
||||
|
||||
let moveByY = slope * overlapAmount[0];
|
||||
let moveByX = overlapAmount[1] / slope;
|
||||
if (overlapAmount[0] < moveByX)
|
||||
{
|
||||
moveByX = overlapAmount[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
moveByY = overlapAmount[1];
|
||||
}
|
||||
// return half the amount so that if each rectangle is moved by these
|
||||
// amounts in opposite directions, overlap will be resolved
|
||||
overlapAmount[0] = -1 * directions[0] * ((moveByX / 2) + separationBuffer);
|
||||
overlapAmount[1] = -1 * directions[1] * ((moveByY / 2) + separationBuffer);
|
||||
};
|
||||
|
||||
/**
|
||||
* This method decides the separation direction of overlapping nodes
|
||||
*
|
||||
* if directions[0] = -1, then rectA goes left
|
||||
* if directions[0] = 1, then rectA goes right
|
||||
* if directions[1] = -1, then rectA goes up
|
||||
* if directions[1] = 1, then rectA goes down
|
||||
*/
|
||||
IGeometry.decideDirectionsForOverlappingNodes = function (rectA, rectB, directions)
|
||||
{
|
||||
if (rectA.getCenterX() < rectB.getCenterX())
|
||||
{
|
||||
directions[0] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
directions[0] = 1;
|
||||
}
|
||||
|
||||
if (rectA.getCenterY() < rectB.getCenterY())
|
||||
{
|
||||
directions[1] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
directions[1] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method calculates the intersection (clipping) points of the two
|
||||
* input rectangles with line segment defined by the centers of these two
|
||||
* rectangles. The clipping points are saved in the input double array and
|
||||
* whether or not the two rectangles overlap is returned.
|
||||
*/
|
||||
IGeometry.getIntersection2 = function(rectA, rectB, result)
|
||||
{
|
||||
//result[0-1] will contain clipPoint of rectA, result[2-3] will contain clipPoint of rectB
|
||||
let p1x = rectA.getCenterX();
|
||||
let p1y = rectA.getCenterY();
|
||||
let p2x = rectB.getCenterX();
|
||||
let p2y = rectB.getCenterY();
|
||||
|
||||
//if two rectangles intersect, then clipping points are centers
|
||||
if (rectA.intersects(rectB))
|
||||
{
|
||||
result[0] = p1x;
|
||||
result[1] = p1y;
|
||||
result[2] = p2x;
|
||||
result[3] = p2y;
|
||||
return true;
|
||||
}
|
||||
//variables for rectA
|
||||
let topLeftAx = rectA.getX();
|
||||
let topLeftAy = rectA.getY();
|
||||
let topRightAx = rectA.getRight();
|
||||
let bottomLeftAx = rectA.getX();
|
||||
let bottomLeftAy = rectA.getBottom();
|
||||
let bottomRightAx = rectA.getRight();
|
||||
let halfWidthA = rectA.getWidthHalf();
|
||||
let halfHeightA = rectA.getHeightHalf();
|
||||
//variables for rectB
|
||||
let topLeftBx = rectB.getX();
|
||||
let topLeftBy = rectB.getY();
|
||||
let topRightBx = rectB.getRight();
|
||||
let bottomLeftBx = rectB.getX();
|
||||
let bottomLeftBy = rectB.getBottom();
|
||||
let bottomRightBx = rectB.getRight();
|
||||
let halfWidthB = rectB.getWidthHalf();
|
||||
let halfHeightB = rectB.getHeightHalf();
|
||||
|
||||
//flag whether clipping points are found
|
||||
let clipPointAFound = false;
|
||||
let clipPointBFound = false;
|
||||
|
||||
// line is vertical
|
||||
if (p1x === p2x)
|
||||
{
|
||||
if (p1y > p2y)
|
||||
{
|
||||
result[0] = p1x;
|
||||
result[1] = topLeftAy;
|
||||
result[2] = p2x;
|
||||
result[3] = bottomLeftBy;
|
||||
return false;
|
||||
}
|
||||
else if (p1y < p2y)
|
||||
{
|
||||
result[0] = p1x;
|
||||
result[1] = bottomLeftAy;
|
||||
result[2] = p2x;
|
||||
result[3] = topLeftBy;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//not line, return null;
|
||||
}
|
||||
}
|
||||
// line is horizontal
|
||||
else if (p1y === p2y)
|
||||
{
|
||||
if (p1x > p2x)
|
||||
{
|
||||
result[0] = topLeftAx;
|
||||
result[1] = p1y;
|
||||
result[2] = topRightBx;
|
||||
result[3] = p2y;
|
||||
return false;
|
||||
}
|
||||
else if (p1x < p2x)
|
||||
{
|
||||
result[0] = topRightAx;
|
||||
result[1] = p1y;
|
||||
result[2] = topLeftBx;
|
||||
result[3] = p2y;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//not valid line, return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//slopes of rectA's and rectB's diagonals
|
||||
let slopeA = rectA.height / rectA.width;
|
||||
let slopeB = rectB.height / rectB.width;
|
||||
|
||||
//slope of line between center of rectA and center of rectB
|
||||
let slopePrime = (p2y - p1y) / (p2x - p1x);
|
||||
let cardinalDirectionA;
|
||||
let cardinalDirectionB;
|
||||
let tempPointAx;
|
||||
let tempPointAy;
|
||||
let tempPointBx;
|
||||
let tempPointBy;
|
||||
|
||||
//determine whether clipping point is the corner of nodeA
|
||||
if ((-slopeA) === slopePrime)
|
||||
{
|
||||
if (p1x > p2x)
|
||||
{
|
||||
result[0] = bottomLeftAx;
|
||||
result[1] = bottomLeftAy;
|
||||
clipPointAFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[0] = topRightAx;
|
||||
result[1] = topLeftAy;
|
||||
clipPointAFound = true;
|
||||
}
|
||||
}
|
||||
else if (slopeA === slopePrime)
|
||||
{
|
||||
if (p1x > p2x)
|
||||
{
|
||||
result[0] = topLeftAx;
|
||||
result[1] = topLeftAy;
|
||||
clipPointAFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[0] = bottomRightAx;
|
||||
result[1] = bottomLeftAy;
|
||||
clipPointAFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
//determine whether clipping point is the corner of nodeB
|
||||
if ((-slopeB) === slopePrime)
|
||||
{
|
||||
if (p2x > p1x)
|
||||
{
|
||||
result[2] = bottomLeftBx;
|
||||
result[3] = bottomLeftBy;
|
||||
clipPointBFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[2] = topRightBx;
|
||||
result[3] = topLeftBy;
|
||||
clipPointBFound = true;
|
||||
}
|
||||
}
|
||||
else if (slopeB === slopePrime)
|
||||
{
|
||||
if (p2x > p1x)
|
||||
{
|
||||
result[2] = topLeftBx;
|
||||
result[3] = topLeftBy;
|
||||
clipPointBFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[2] = bottomRightBx;
|
||||
result[3] = bottomLeftBy;
|
||||
clipPointBFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
//if both clipping points are corners
|
||||
if (clipPointAFound && clipPointBFound)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//determine Cardinal Direction of rectangles
|
||||
if (p1x > p2x)
|
||||
{
|
||||
if (p1y > p2y)
|
||||
{
|
||||
cardinalDirectionA = this.getCardinalDirection(slopeA, slopePrime, 4);
|
||||
cardinalDirectionB = this.getCardinalDirection(slopeB, slopePrime, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
cardinalDirectionA = this.getCardinalDirection(-slopeA, slopePrime, 3);
|
||||
cardinalDirectionB = this.getCardinalDirection(-slopeB, slopePrime, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p1y > p2y)
|
||||
{
|
||||
cardinalDirectionA = this.getCardinalDirection(-slopeA, slopePrime, 1);
|
||||
cardinalDirectionB = this.getCardinalDirection(-slopeB, slopePrime, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
cardinalDirectionA = this.getCardinalDirection(slopeA, slopePrime, 2);
|
||||
cardinalDirectionB = this.getCardinalDirection(slopeB, slopePrime, 4);
|
||||
}
|
||||
}
|
||||
//calculate clipping Point if it is not found before
|
||||
if (!clipPointAFound)
|
||||
{
|
||||
switch (cardinalDirectionA)
|
||||
{
|
||||
case 1:
|
||||
tempPointAy = topLeftAy;
|
||||
tempPointAx = p1x + (-halfHeightA) / slopePrime;
|
||||
result[0] = tempPointAx;
|
||||
result[1] = tempPointAy;
|
||||
break;
|
||||
case 2:
|
||||
tempPointAx = bottomRightAx;
|
||||
tempPointAy = p1y + halfWidthA * slopePrime;
|
||||
result[0] = tempPointAx;
|
||||
result[1] = tempPointAy;
|
||||
break;
|
||||
case 3:
|
||||
tempPointAy = bottomLeftAy;
|
||||
tempPointAx = p1x + halfHeightA / slopePrime;
|
||||
result[0] = tempPointAx;
|
||||
result[1] = tempPointAy;
|
||||
break;
|
||||
case 4:
|
||||
tempPointAx = bottomLeftAx;
|
||||
tempPointAy = p1y + (-halfWidthA) * slopePrime;
|
||||
result[0] = tempPointAx;
|
||||
result[1] = tempPointAy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!clipPointBFound)
|
||||
{
|
||||
switch (cardinalDirectionB)
|
||||
{
|
||||
case 1:
|
||||
tempPointBy = topLeftBy;
|
||||
tempPointBx = p2x + (-halfHeightB) / slopePrime;
|
||||
result[2] = tempPointBx;
|
||||
result[3] = tempPointBy;
|
||||
break;
|
||||
case 2:
|
||||
tempPointBx = bottomRightBx;
|
||||
tempPointBy = p2y + halfWidthB * slopePrime;
|
||||
result[2] = tempPointBx;
|
||||
result[3] = tempPointBy;
|
||||
break;
|
||||
case 3:
|
||||
tempPointBy = bottomLeftBy;
|
||||
tempPointBx = p2x + halfHeightB / slopePrime;
|
||||
result[2] = tempPointBx;
|
||||
result[3] = tempPointBy;
|
||||
break;
|
||||
case 4:
|
||||
tempPointBx = bottomLeftBx;
|
||||
tempPointBy = p2y + (-halfWidthB) * slopePrime;
|
||||
result[2] = tempPointBx;
|
||||
result[3] = tempPointBy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method returns in which cardinal direction does input point stays
|
||||
* 1: North
|
||||
* 2: East
|
||||
* 3: South
|
||||
* 4: West
|
||||
*/
|
||||
IGeometry.getCardinalDirection = function (slope, slopePrime, line)
|
||||
{
|
||||
if (slope > slopePrime)
|
||||
{
|
||||
return line;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1 + line % 4;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method calculates the intersection of the two lines defined by
|
||||
* point pairs (s1,s2) and (f1,f2).
|
||||
*/
|
||||
IGeometry.getIntersection = function(s1, s2, f1, f2)
|
||||
{
|
||||
if (f2 == null) {
|
||||
return this.getIntersection2(s1, s2, f1);
|
||||
}
|
||||
|
||||
let x1 = s1.x;
|
||||
let y1 = s1.y;
|
||||
let x2 = s2.x;
|
||||
let y2 = s2.y;
|
||||
let x3 = f1.x;
|
||||
let y3 = f1.y;
|
||||
let x4 = f2.x;
|
||||
let y4 = f2.y;
|
||||
let x, y; // intersection point
|
||||
let a1, a2, b1, b2, c1, c2; // coefficients of line eqns.
|
||||
let denom;
|
||||
|
||||
a1 = y2 - y1;
|
||||
b1 = x1 - x2;
|
||||
c1 = x2 * y1 - x1 * y2; // { a1*x + b1*y + c1 = 0 is line 1 }
|
||||
|
||||
a2 = y4 - y3;
|
||||
b2 = x3 - x4;
|
||||
c2 = x4 * y3 - x3 * y4; // { a2*x + b2*y + c2 = 0 is line 2 }
|
||||
|
||||
denom = a1 * b2 - a2 * b1;
|
||||
|
||||
if (denom === 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
x = (b1 * c2 - b2 * c1) / denom;
|
||||
y = (a2 * c1 - a1 * c2) / denom;
|
||||
|
||||
return new Point(x, y);
|
||||
};
|
||||
|
||||
/**
|
||||
* This method finds and returns the angle of the vector from the + x-axis
|
||||
* in clockwise direction (compatible w/ Java coordinate system!).
|
||||
*/
|
||||
IGeometry.angleOfVector = function(Cx, Cy, Nx, Ny)
|
||||
{
|
||||
let C_angle;
|
||||
|
||||
if (Cx !== Nx)
|
||||
{
|
||||
C_angle = Math.atan((Ny - Cy) / (Nx - Cx));
|
||||
|
||||
if (Nx < Cx)
|
||||
{
|
||||
C_angle += Math.PI;
|
||||
}
|
||||
else if (Ny < Cy)
|
||||
{
|
||||
C_angle += this.TWO_PI;
|
||||
}
|
||||
}
|
||||
else if (Ny < Cy)
|
||||
{
|
||||
C_angle = this.ONE_AND_HALF_PI; // 270 degrees
|
||||
}
|
||||
else
|
||||
{
|
||||
C_angle = this.HALF_PI; // 90 degrees
|
||||
}
|
||||
|
||||
return C_angle;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This method checks whether the given two line segments (one with point
|
||||
* p1 and p2, the other with point p3 and p4) intersect at a point other
|
||||
* than these points.
|
||||
*/
|
||||
IGeometry.doIntersect = function(p1, p2, p3, p4){
|
||||
let a = p1.x;
|
||||
let b = p1.y;
|
||||
let c = p2.x;
|
||||
let d = p2.y;
|
||||
let p = p3.x;
|
||||
let q = p3.y;
|
||||
let r = p4.x;
|
||||
let s = p4.y;
|
||||
let det = (c - a) * (s - q) - (r - p) * (d - b);
|
||||
|
||||
if (det === 0) {
|
||||
return false;
|
||||
} else {
|
||||
let lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det;
|
||||
let gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det;
|
||||
return (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Section: Class Constants
|
||||
// -----------------------------------------------------------------------------
|
||||
/**
|
||||
* Some useful pre-calculated constants
|
||||
*/
|
||||
IGeometry.HALF_PI = 0.5 * Math.PI;
|
||||
IGeometry.ONE_AND_HALF_PI = 1.5 * Math.PI;
|
||||
IGeometry.TWO_PI = 2.0 * Math.PI;
|
||||
IGeometry.THREE_PI = 3.0 * Math.PI;
|
||||
|
||||
|
||||
module.exports = IGeometry;
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
function IMath() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the sign of the input value.
|
||||
*/
|
||||
IMath.sign = function (value) {
|
||||
if (value > 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
IMath.floor = function (value) {
|
||||
return value < 0 ? Math.ceil(value) : Math.floor(value);
|
||||
};
|
||||
|
||||
IMath.ceil = function (value) {
|
||||
return value < 0 ? Math.floor(value) : Math.ceil(value);
|
||||
};
|
||||
|
||||
module.exports = IMath;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
function Integer() {
|
||||
}
|
||||
|
||||
Integer.MAX_VALUE = 2147483647;
|
||||
Integer.MIN_VALUE = -2147483648;
|
||||
|
||||
module.exports = Integer;
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
const nodeFrom = value => ({ value, next: null, prev: null });
|
||||
|
||||
const add = ( prev, node, next, list ) => {
|
||||
if( prev !== null ){
|
||||
prev.next = node;
|
||||
} else {
|
||||
list.head = node;
|
||||
}
|
||||
|
||||
if( next !== null ){
|
||||
next.prev = node;
|
||||
} else {
|
||||
list.tail = node;
|
||||
}
|
||||
|
||||
node.prev = prev;
|
||||
node.next = next;
|
||||
|
||||
list.length++;
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
const remove = ( node, list ) => {
|
||||
let { prev, next } = node;
|
||||
|
||||
if( prev !== null ){
|
||||
prev.next = next;
|
||||
} else {
|
||||
list.head = next;
|
||||
}
|
||||
|
||||
if( next !== null ){
|
||||
next.prev = prev;
|
||||
} else {
|
||||
list.tail = prev;
|
||||
}
|
||||
|
||||
node.prev = node.next = null;
|
||||
|
||||
list.length--;
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
class LinkedList {
|
||||
constructor( vals ){
|
||||
this.length = 0;
|
||||
this.head = null;
|
||||
this.tail = null;
|
||||
|
||||
if( vals != null ){
|
||||
vals.forEach( v => this.push(v) );
|
||||
}
|
||||
}
|
||||
|
||||
size(){
|
||||
return this.length;
|
||||
}
|
||||
|
||||
insertBefore( val, otherNode ){
|
||||
return add( otherNode.prev, nodeFrom(val), otherNode, this );
|
||||
}
|
||||
|
||||
insertAfter( val, otherNode ){
|
||||
return add( otherNode, nodeFrom(val), otherNode.next, this );
|
||||
}
|
||||
|
||||
insertNodeBefore( newNode, otherNode ){
|
||||
return add( otherNode.prev, newNode, otherNode, this );
|
||||
}
|
||||
|
||||
insertNodeAfter( newNode, otherNode ){
|
||||
return add( otherNode, newNode, otherNode.next, this );
|
||||
}
|
||||
|
||||
push( val ){
|
||||
return add( this.tail, nodeFrom(val), null, this );
|
||||
}
|
||||
|
||||
unshift( val ){
|
||||
return add( null, nodeFrom(val), this.head, this );
|
||||
}
|
||||
|
||||
remove( node ){
|
||||
return remove( node, this );
|
||||
}
|
||||
|
||||
pop(){
|
||||
return remove( this.tail, this ).value;
|
||||
}
|
||||
|
||||
popNode(){
|
||||
return remove( this.tail, this );
|
||||
}
|
||||
|
||||
shift(){
|
||||
return remove( this.head, this ).value;
|
||||
}
|
||||
|
||||
shiftNode(){
|
||||
return remove( this.head, this );
|
||||
}
|
||||
|
||||
get_object_at( index ){
|
||||
if(index <= this.length()){
|
||||
var i = 1;
|
||||
var current = this.head;
|
||||
while(i < index){
|
||||
current = current.next;
|
||||
i++;
|
||||
}
|
||||
return current.value;
|
||||
}
|
||||
}
|
||||
|
||||
set_object_at( index, value){
|
||||
if(index <= this.length()) {
|
||||
var i = 1;
|
||||
var current = this.head;
|
||||
while (i < index) {
|
||||
current = current.next;
|
||||
i++;
|
||||
}
|
||||
current.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LinkedList;
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
*This class is the javascript implementation of the Point.java class in jdk
|
||||
*/
|
||||
function Point(x, y, p) {
|
||||
this.x = null;
|
||||
this.y = null;
|
||||
if (x == null && y == null && p == null) {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
}
|
||||
else if (typeof x == 'number' && typeof y == 'number' && p == null) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
else if (x.constructor.name == 'Point' && y == null && p == null) {
|
||||
p = x;
|
||||
this.x = p.x;
|
||||
this.y = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
Point.prototype.getX = function () {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
Point.prototype.getY = function () {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
Point.prototype.getLocation = function () {
|
||||
return new Point(this.x, this.y);
|
||||
}
|
||||
|
||||
Point.prototype.setLocation = function (x, y, p) {
|
||||
if (x.constructor.name == 'Point' && y == null && p == null) {
|
||||
p = x;
|
||||
this.setLocation(p.x, p.y);
|
||||
}
|
||||
else if (typeof x == 'number' && typeof y == 'number' && p == null) {
|
||||
//if both parameters are integer just move (x,y) location
|
||||
if (parseInt(x) == x && parseInt(y) == y) {
|
||||
this.move(x, y);
|
||||
}
|
||||
else {
|
||||
this.x = Math.floor(x + 0.5);
|
||||
this.y = Math.floor(y + 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Point.prototype.move = function (x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
Point.prototype.translate = function (dx, dy) {
|
||||
this.x += dx;
|
||||
this.y += dy;
|
||||
}
|
||||
|
||||
Point.prototype.equals = function (obj) {
|
||||
if (obj.constructor.name == "Point") {
|
||||
var pt = obj;
|
||||
return (this.x == pt.x) && (this.y == pt.y);
|
||||
}
|
||||
return this == obj;
|
||||
}
|
||||
|
||||
Point.prototype.toString = function () {
|
||||
return new Point().constructor.name + "[x=" + this.x + ",y=" + this.y + "]";
|
||||
}
|
||||
|
||||
module.exports = Point;
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
function PointD(x, y) {
|
||||
if (x == null && y == null) {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
} else {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
PointD.prototype.getX = function ()
|
||||
{
|
||||
return this.x;
|
||||
};
|
||||
|
||||
PointD.prototype.getY = function ()
|
||||
{
|
||||
return this.y;
|
||||
};
|
||||
|
||||
PointD.prototype.setX = function (x)
|
||||
{
|
||||
this.x = x;
|
||||
};
|
||||
|
||||
PointD.prototype.setY = function (y)
|
||||
{
|
||||
this.y = y;
|
||||
};
|
||||
|
||||
PointD.prototype.getDifference = function (pt)
|
||||
{
|
||||
return new DimensionD(this.x - pt.x, this.y - pt.y);
|
||||
};
|
||||
|
||||
PointD.prototype.getCopy = function ()
|
||||
{
|
||||
return new PointD(this.x, this.y);
|
||||
};
|
||||
|
||||
PointD.prototype.translate = function (dim)
|
||||
{
|
||||
this.x += dim.width;
|
||||
this.y += dim.height;
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = PointD;
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* A classic Quicksort algorithm with Hoare's partition
|
||||
* - Works also on LinkedList objects
|
||||
*
|
||||
* Copyright: i-Vis Research Group, Bilkent University, 2007 - present
|
||||
*/
|
||||
|
||||
const LinkedList = require('./LinkedList.js');
|
||||
|
||||
class Quicksort {
|
||||
constructor(A, compareFunction) {
|
||||
if(compareFunction !== null || compareFunction !== undefined)
|
||||
this.compareFunction = this._defaultCompareFunction;
|
||||
|
||||
let length;
|
||||
if( A instanceof LinkedList )
|
||||
length = A.size();
|
||||
else
|
||||
length = A.length;
|
||||
|
||||
this._quicksort(A, 0, length - 1);
|
||||
}
|
||||
|
||||
_quicksort(A, p, r){
|
||||
if(p < r) {
|
||||
let q = this._partition(A, p, r);
|
||||
this._quicksort(A, p, q);
|
||||
this._quicksort(A, q + 1, r);
|
||||
}
|
||||
}
|
||||
|
||||
_partition(A, p, r){
|
||||
let x = this._get(A, p);
|
||||
let i = p;
|
||||
let j = r;
|
||||
while(true){
|
||||
while (this.compareFunction(x, this._get(A, j)))
|
||||
j--;
|
||||
while (this.compareFunction(this._get(A, i), x))
|
||||
i++;
|
||||
|
||||
if (i < j){
|
||||
this._swap(A, i, j);
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
else
|
||||
return j;
|
||||
}
|
||||
}
|
||||
|
||||
_get(object, index){
|
||||
if( object instanceof LinkedList)
|
||||
return object.get_object_at(index);
|
||||
else
|
||||
return object[index];
|
||||
}
|
||||
|
||||
_set(object, index, value){
|
||||
if( object instanceof LinkedList)
|
||||
object.set_object_at(index, value);
|
||||
else
|
||||
object[index] = value;
|
||||
}
|
||||
|
||||
_swap(A, i, j){
|
||||
let temp = this._get(A, i);
|
||||
this._set(A, i, this._get(A, j));
|
||||
this._set(A, j, temp);
|
||||
}
|
||||
|
||||
_defaultCompareFunction(a, b){
|
||||
return b > a;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Quicksort;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
function RandomSeed() {
|
||||
}
|
||||
// adapted from: https://stackoverflow.com/a/19303725
|
||||
RandomSeed.seed = 1;
|
||||
RandomSeed.x = 0;
|
||||
|
||||
RandomSeed.nextDouble = function () {
|
||||
RandomSeed.x = Math.sin(RandomSeed.seed++) * 10000;
|
||||
return RandomSeed.x - Math.floor(RandomSeed.x);
|
||||
};
|
||||
|
||||
module.exports = RandomSeed;
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
function RectangleD(x, y, width, height) {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
|
||||
if (x != null && y != null && width != null && height != null) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
RectangleD.prototype.getX = function ()
|
||||
{
|
||||
return this.x;
|
||||
};
|
||||
|
||||
RectangleD.prototype.setX = function (x)
|
||||
{
|
||||
this.x = x;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getY = function ()
|
||||
{
|
||||
return this.y;
|
||||
};
|
||||
|
||||
RectangleD.prototype.setY = function (y)
|
||||
{
|
||||
this.y = y;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getWidth = function ()
|
||||
{
|
||||
return this.width;
|
||||
};
|
||||
|
||||
RectangleD.prototype.setWidth = function (width)
|
||||
{
|
||||
this.width = width;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getHeight = function ()
|
||||
{
|
||||
return this.height;
|
||||
};
|
||||
|
||||
RectangleD.prototype.setHeight = function (height)
|
||||
{
|
||||
this.height = height;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getRight = function ()
|
||||
{
|
||||
return this.x + this.width;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getBottom = function ()
|
||||
{
|
||||
return this.y + this.height;
|
||||
};
|
||||
|
||||
RectangleD.prototype.intersects = function (a)
|
||||
{
|
||||
if (this.getRight() < a.x)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.getBottom() < a.y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.getRight() < this.x)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.getBottom() < this.y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getCenterX = function ()
|
||||
{
|
||||
return this.x + this.width / 2;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getMinX = function ()
|
||||
{
|
||||
return this.getX();
|
||||
};
|
||||
|
||||
RectangleD.prototype.getMaxX = function ()
|
||||
{
|
||||
return this.getX() + this.width;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getCenterY = function ()
|
||||
{
|
||||
return this.y + this.height / 2;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getMinY = function ()
|
||||
{
|
||||
return this.getY();
|
||||
};
|
||||
|
||||
RectangleD.prototype.getMaxY = function ()
|
||||
{
|
||||
return this.getY() + this.height;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getWidthHalf = function ()
|
||||
{
|
||||
return this.width / 2;
|
||||
};
|
||||
|
||||
RectangleD.prototype.getHeightHalf = function ()
|
||||
{
|
||||
return this.height / 2;
|
||||
};
|
||||
|
||||
module.exports = RectangleD;
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
var PointD = require('./PointD');
|
||||
|
||||
function Transform(x, y) {
|
||||
this.lworldOrgX = 0.0;
|
||||
this.lworldOrgY = 0.0;
|
||||
this.ldeviceOrgX = 0.0;
|
||||
this.ldeviceOrgY = 0.0;
|
||||
this.lworldExtX = 1.0;
|
||||
this.lworldExtY = 1.0;
|
||||
this.ldeviceExtX = 1.0;
|
||||
this.ldeviceExtY = 1.0;
|
||||
}
|
||||
|
||||
Transform.prototype.getWorldOrgX = function ()
|
||||
{
|
||||
return this.lworldOrgX;
|
||||
}
|
||||
|
||||
Transform.prototype.setWorldOrgX = function (wox)
|
||||
{
|
||||
this.lworldOrgX = wox;
|
||||
}
|
||||
|
||||
Transform.prototype.getWorldOrgY = function ()
|
||||
{
|
||||
return this.lworldOrgY;
|
||||
}
|
||||
|
||||
Transform.prototype.setWorldOrgY = function (woy)
|
||||
{
|
||||
this.lworldOrgY = woy;
|
||||
}
|
||||
|
||||
Transform.prototype.getWorldExtX = function ()
|
||||
{
|
||||
return this.lworldExtX;
|
||||
}
|
||||
|
||||
Transform.prototype.setWorldExtX = function (wex)
|
||||
{
|
||||
this.lworldExtX = wex;
|
||||
}
|
||||
|
||||
Transform.prototype.getWorldExtY = function ()
|
||||
{
|
||||
return this.lworldExtY;
|
||||
}
|
||||
|
||||
Transform.prototype.setWorldExtY = function (wey)
|
||||
{
|
||||
this.lworldExtY = wey;
|
||||
}
|
||||
|
||||
/* Device related */
|
||||
|
||||
Transform.prototype.getDeviceOrgX = function ()
|
||||
{
|
||||
return this.ldeviceOrgX;
|
||||
}
|
||||
|
||||
Transform.prototype.setDeviceOrgX = function (dox)
|
||||
{
|
||||
this.ldeviceOrgX = dox;
|
||||
}
|
||||
|
||||
Transform.prototype.getDeviceOrgY = function ()
|
||||
{
|
||||
return this.ldeviceOrgY;
|
||||
}
|
||||
|
||||
Transform.prototype.setDeviceOrgY = function (doy)
|
||||
{
|
||||
this.ldeviceOrgY = doy;
|
||||
}
|
||||
|
||||
Transform.prototype.getDeviceExtX = function ()
|
||||
{
|
||||
return this.ldeviceExtX;
|
||||
}
|
||||
|
||||
Transform.prototype.setDeviceExtX = function (dex)
|
||||
{
|
||||
this.ldeviceExtX = dex;
|
||||
}
|
||||
|
||||
Transform.prototype.getDeviceExtY = function ()
|
||||
{
|
||||
return this.ldeviceExtY;
|
||||
}
|
||||
|
||||
Transform.prototype.setDeviceExtY = function (dey)
|
||||
{
|
||||
this.ldeviceExtY = dey;
|
||||
}
|
||||
|
||||
Transform.prototype.transformX = function (x)
|
||||
{
|
||||
var xDevice = 0.0;
|
||||
var worldExtX = this.lworldExtX;
|
||||
if (worldExtX != 0.0)
|
||||
{
|
||||
xDevice = this.ldeviceOrgX +
|
||||
((x - this.lworldOrgX) * this.ldeviceExtX / worldExtX);
|
||||
}
|
||||
|
||||
return xDevice;
|
||||
}
|
||||
|
||||
Transform.prototype.transformY = function (y)
|
||||
{
|
||||
var yDevice = 0.0;
|
||||
var worldExtY = this.lworldExtY;
|
||||
if (worldExtY != 0.0)
|
||||
{
|
||||
yDevice = this.ldeviceOrgY +
|
||||
((y - this.lworldOrgY) * this.ldeviceExtY / worldExtY);
|
||||
}
|
||||
|
||||
|
||||
return yDevice;
|
||||
}
|
||||
|
||||
Transform.prototype.inverseTransformX = function (x)
|
||||
{
|
||||
var xWorld = 0.0;
|
||||
var deviceExtX = this.ldeviceExtX;
|
||||
if (deviceExtX != 0.0)
|
||||
{
|
||||
xWorld = this.lworldOrgX +
|
||||
((x - this.ldeviceOrgX) * this.lworldExtX / deviceExtX);
|
||||
}
|
||||
|
||||
|
||||
return xWorld;
|
||||
}
|
||||
|
||||
Transform.prototype.inverseTransformY = function (y)
|
||||
{
|
||||
var yWorld = 0.0;
|
||||
var deviceExtY = this.ldeviceExtY;
|
||||
if (deviceExtY != 0.0)
|
||||
{
|
||||
yWorld = this.lworldOrgY +
|
||||
((y - this.ldeviceOrgY) * this.lworldExtY / deviceExtY);
|
||||
}
|
||||
return yWorld;
|
||||
}
|
||||
|
||||
Transform.prototype.inverseTransformPoint = function (inPoint)
|
||||
{
|
||||
var outPoint =
|
||||
new PointD(this.inverseTransformX(inPoint.x),
|
||||
this.inverseTransformY(inPoint.y));
|
||||
return outPoint;
|
||||
}
|
||||
|
||||
module.exports = Transform;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
function UniqueIDGeneretor() {
|
||||
}
|
||||
|
||||
UniqueIDGeneretor.lastID = 0;
|
||||
|
||||
UniqueIDGeneretor.createID = function (obj) {
|
||||
if (UniqueIDGeneretor.isPrimitive(obj)) {
|
||||
return obj;
|
||||
}
|
||||
if (obj.uniqueID != null) {
|
||||
return obj.uniqueID;
|
||||
}
|
||||
obj.uniqueID = UniqueIDGeneretor.getString();
|
||||
UniqueIDGeneretor.lastID++;
|
||||
return obj.uniqueID;
|
||||
}
|
||||
|
||||
UniqueIDGeneretor.getString = function (id) {
|
||||
if (id == null)
|
||||
id = UniqueIDGeneretor.lastID;
|
||||
return "Object#" + id + "";
|
||||
}
|
||||
|
||||
UniqueIDGeneretor.isPrimitive = function (arg) {
|
||||
var type = typeof arg;
|
||||
return arg == null || (type != "object" && type != "function");
|
||||
}
|
||||
|
||||
module.exports = UniqueIDGeneretor;
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Needleman-Wunsch algorithm is an procedure to compute the optimal global alignment of two string
|
||||
* sequences by S.B.Needleman and C.D.Wunsch (1970).
|
||||
*
|
||||
* Aside from the inputs, you can assign the scores for,
|
||||
* - Match: The two characters at the current index are same.
|
||||
* - Mismatch: The two characters at the current index are different.
|
||||
* - Insertion/Deletion(gaps): The best alignment involves one letter aligning to a gap in the other string.
|
||||
*/
|
||||
|
||||
class NeedlemanWunsch {
|
||||
constructor(sequence1, sequence2, match_score = 1, mismatch_penalty = -1, gap_penalty = -1) {
|
||||
this.sequence1 = sequence1;
|
||||
this.sequence2 = sequence2;
|
||||
this.match_score = match_score;
|
||||
this.mismatch_penalty = mismatch_penalty;
|
||||
this.gap_penalty = gap_penalty;
|
||||
|
||||
// Just the remove redundancy
|
||||
this.iMax = sequence1.length + 1;
|
||||
this.jMax = sequence2.length + 1;
|
||||
|
||||
// Grid matrix of scores
|
||||
this.grid = new Array(this.iMax);
|
||||
for(let i = 0; i < this.iMax; i++){
|
||||
this.grid[i] = new Array(this.jMax );
|
||||
|
||||
for(let j = 0; j < this.jMax ; j++)
|
||||
this.grid[i][j] = 0;
|
||||
}
|
||||
|
||||
// Traceback matrix (2D array, each cell is an array of boolean values for [`Diag`, `Up`, `Left`] positions)
|
||||
this.tracebackGrid = new Array(this.iMax);
|
||||
for(let i = 0; i < this.iMax; i++) {
|
||||
this.tracebackGrid[i] = new Array(this.jMax);
|
||||
|
||||
for(let j = 0; j < this.jMax ; j++)
|
||||
this.tracebackGrid[i][j] = [null, null, null];
|
||||
}
|
||||
|
||||
// The aligned sequences (return multiple possibilities)
|
||||
this.alignments = [];
|
||||
|
||||
// Final alignment score
|
||||
this.score = -1;
|
||||
|
||||
// Calculate scores and tracebacks
|
||||
this.computeGrids();
|
||||
}
|
||||
|
||||
getScore(){
|
||||
return this.score;
|
||||
}
|
||||
|
||||
getAlignments(){
|
||||
return this.alignments;
|
||||
}
|
||||
|
||||
// Main dynamic programming procedure
|
||||
computeGrids(){
|
||||
// Fill in the first row
|
||||
for (let j = 1; j < this.jMax; j++) {
|
||||
this.grid[0][j] = this.grid[0][j-1] + this.gap_penalty;
|
||||
this.tracebackGrid[0][j] = [false, false, true];
|
||||
}
|
||||
|
||||
// Fill in the first column
|
||||
for (let i = 1; i < this.iMax; i++) {
|
||||
this.grid[i][0] = this.grid[i-1][0] + this.gap_penalty;
|
||||
this.tracebackGrid[i][0] = [false, true, false];
|
||||
}
|
||||
|
||||
// Fill the rest of the grid
|
||||
for(let i = 1; i < this.iMax; i++){
|
||||
for(let j = 1; j < this.jMax; j++){
|
||||
// Find the max score(s) among [`Diag`, `Up`, `Left`]
|
||||
let diag;
|
||||
if(this.sequence1[i-1] === this.sequence2[j-1])
|
||||
diag = this.grid[i-1][j-1] + this.match_score;
|
||||
else
|
||||
diag = this.grid[i-1][j-1] + this.mismatch_penalty;
|
||||
|
||||
let up = this.grid[i-1][j] + this.gap_penalty;
|
||||
let left = this.grid[i][j-1] + this.gap_penalty;
|
||||
|
||||
// If there exists multiple max values, capture them for multiple paths
|
||||
let maxOf = [diag,up,left];
|
||||
let indices = this.arrayAllMaxIndexes(maxOf);
|
||||
|
||||
// Update Grids
|
||||
this.grid[i][j] = maxOf[indices[0]];
|
||||
this.tracebackGrid[i][j] = [indices.includes(0), indices.includes(1), indices.includes(2)];
|
||||
}
|
||||
}
|
||||
|
||||
// Update alignment score
|
||||
this.score = this.grid[this.iMax-1][this.jMax-1];
|
||||
}
|
||||
|
||||
// Gets all possible valid sequence combinations
|
||||
alignmentTraceback(){
|
||||
let inProcessAlignments = [];
|
||||
|
||||
inProcessAlignments.push({ pos: [this.sequence1.length, this.sequence2.length],
|
||||
seq1: "",
|
||||
seq2: ""
|
||||
});
|
||||
|
||||
while(inProcessAlignments[0]){
|
||||
let current = inProcessAlignments[0];
|
||||
let directions = this.tracebackGrid[current.pos[0]][current.pos[1]];
|
||||
|
||||
if(directions[0]){
|
||||
inProcessAlignments.push({ pos: [current.pos[0]-1, current.pos[1]-1],
|
||||
seq1: (this.sequence1[current.pos[0]-1] + current.seq1),
|
||||
seq2: (this.sequence2[current.pos[1]-1] + current.seq2)
|
||||
});
|
||||
}
|
||||
if(directions[1]){
|
||||
inProcessAlignments.push({ pos: [current.pos[0]-1, current.pos[1]],
|
||||
seq1: this.sequence1[current.pos[0]-1] + current.seq1,
|
||||
seq2: '-' + current.seq2
|
||||
});
|
||||
}
|
||||
if(directions[2]){
|
||||
inProcessAlignments.push({ pos: [current.pos[0], current.pos[1]-1],
|
||||
seq1:'-' + current.seq1,
|
||||
seq2: this.sequence2[current.pos[1]-1] + current.seq2
|
||||
});
|
||||
}
|
||||
|
||||
if(current.pos[0] === 0 && current.pos[1] === 0)
|
||||
this.alignments.push({sequence1 : current.seq1,
|
||||
sequence2: current.seq2
|
||||
});
|
||||
|
||||
inProcessAlignments.shift();
|
||||
}
|
||||
|
||||
return this.alignments;
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
getAllIndexes(arr, val) {
|
||||
let indexes = [], i = -1;
|
||||
while ((i = arr.indexOf(val, i+1)) !== -1){
|
||||
indexes.push(i);
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
arrayAllMaxIndexes(array){
|
||||
return this.getAllIndexes(array, Math.max.apply(null, array));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NeedlemanWunsch;
|
||||
Reference in New Issue
Block a user