|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Point = require('./Point'); |
|
|
|
function IGeometry() { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
if ((rectA.getX() <= rectB.getX()) && (rectA.getRight() >= rectB.getRight())) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
overlapAmount[0] += Math.min((rectB.getX() - rectA.getX()), |
|
(rectA.getRight() - rectB.getRight())); |
|
} |
|
else if ((rectB.getX() <= rectA.getX()) && (rectB.getRight() >= rectA.getRight())) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
overlapAmount[0] += Math.min((rectA.getX() - rectB.getX()), |
|
(rectB.getRight() - rectA.getRight())); |
|
} |
|
if ((rectA.getY() <= rectB.getY()) && (rectA.getBottom() >= rectB.getBottom())) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
overlapAmount[1] += Math.min((rectB.getY() - rectA.getY()), |
|
(rectA.getBottom() - rectB.getBottom())); |
|
} |
|
else if ((rectB.getY() <= rectA.getY()) && (rectB.getBottom() >= rectA.getBottom())) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
overlapAmount[1] += Math.min((rectA.getY() - rectB.getY()), |
|
(rectB.getBottom() - rectA.getBottom())); |
|
} |
|
|
|
|
|
let slope = Math.abs((rectB.getCenterY() - rectA.getCenterY()) / |
|
(rectB.getCenterX() - rectA.getCenterX())); |
|
|
|
if ((rectB.getCenterY() === rectA.getCenterY()) && |
|
(rectB.getCenterX() === rectA.getCenterX())) |
|
{ |
|
|
|
slope = 1.0; |
|
} |
|
|
|
let moveByY = slope * overlapAmount[0]; |
|
let moveByX = overlapAmount[1] / slope; |
|
if (overlapAmount[0] < moveByX) |
|
{ |
|
moveByX = overlapAmount[0]; |
|
} |
|
else |
|
{ |
|
moveByY = overlapAmount[1]; |
|
} |
|
|
|
|
|
overlapAmount[0] = -1 * directions[0] * ((moveByX / 2) + separationBuffer); |
|
overlapAmount[1] = -1 * directions[1] * ((moveByY / 2) + separationBuffer); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IGeometry.getIntersection2 = function(rectA, rectB, result) |
|
{ |
|
|
|
let p1x = rectA.getCenterX(); |
|
let p1y = rectA.getCenterY(); |
|
let p2x = rectB.getCenterX(); |
|
let p2y = rectB.getCenterY(); |
|
|
|
|
|
if (rectA.intersects(rectB)) |
|
{ |
|
result[0] = p1x; |
|
result[1] = p1y; |
|
result[2] = p2x; |
|
result[3] = p2y; |
|
return true; |
|
} |
|
|
|
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(); |
|
|
|
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(); |
|
|
|
|
|
let clipPointAFound = false; |
|
let clipPointBFound = false; |
|
|
|
|
|
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 |
|
{ |
|
|
|
} |
|
} |
|
|
|
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 |
|
{ |
|
|
|
} |
|
} |
|
else |
|
{ |
|
|
|
let slopeA = rectA.height / rectA.width; |
|
let slopeB = rectB.height / rectB.width; |
|
|
|
|
|
let slopePrime = (p2y - p1y) / (p2x - p1x); |
|
let cardinalDirectionA; |
|
let cardinalDirectionB; |
|
let tempPointAx; |
|
let tempPointAy; |
|
let tempPointBx; |
|
let tempPointBy; |
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
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 (clipPointAFound && clipPointBFound) |
|
{ |
|
return false; |
|
} |
|
|
|
|
|
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); |
|
} |
|
} |
|
|
|
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; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IGeometry.getCardinalDirection = function (slope, slopePrime, line) |
|
{ |
|
if (slope > slopePrime) |
|
{ |
|
return line; |
|
} |
|
else |
|
{ |
|
return 1 + line % 4; |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
let a1, a2, b1, b2, c1, c2; |
|
let denom; |
|
|
|
a1 = y2 - y1; |
|
b1 = x1 - x2; |
|
c1 = x2 * y1 - x1 * y2; |
|
|
|
a2 = y4 - y3; |
|
b2 = x3 - x4; |
|
c2 = x4 * y3 - x3 * y4; |
|
|
|
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); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
else |
|
{ |
|
C_angle = this.HALF_PI; |
|
} |
|
|
|
return C_angle; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|