mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
modified shape structures and operations on said shapes
This commit is contained in:
parent
4304ea7f4d
commit
9d86844396
280
src/main/scala/net/psforever/objects/geometry/Closest.scala
Normal file
280
src/main/scala/net/psforever/objects/geometry/Closest.scala
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.geometry
|
||||
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object Closest {
|
||||
object Distance {
|
||||
def apply(point : Vector3, seg : Segment2D) : Float = {
|
||||
val segdx = seg.bx - seg.ax
|
||||
val segdy = seg.by - seg.ay
|
||||
((point.x - seg.ax) * segdx + (point.y - seg.ay) * segdy) /
|
||||
Vector3.MagnitudeSquared(Vector3(segdx, segdy, 0))
|
||||
}
|
||||
|
||||
def apply(line1 : Line2D, line2 : Line2D) : Float = {
|
||||
if (Intersection.Test(line1, line2)) { //intersecting lines
|
||||
0f
|
||||
} else {
|
||||
math.abs(
|
||||
Vector3.DotProduct(
|
||||
Vector3(line2.x - line1.x, line2.y - line1.y, 0),
|
||||
Vector3(-1/line1.d.y, 1/line1.d.x, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def apply(seg1: Segment2D, seg2: Segment2D): Float = {
|
||||
if (Intersection.Test(seg1, seg2)) { //intersecting line segments
|
||||
0f
|
||||
} else {
|
||||
val v1a = Vector3(seg1.ax, seg1.ay, 0)
|
||||
val v2a = Vector3(seg2.ax, seg2.ay, 0)
|
||||
val v1b = Vector3(seg1.bx, seg1.by, 0)
|
||||
val v2b = Vector3(seg2.bx, seg2.by, 0)
|
||||
math.min(
|
||||
apply(v1a, seg2),
|
||||
math.min(
|
||||
apply(v1b, seg2),
|
||||
math.min(
|
||||
apply(v2a, seg1),
|
||||
apply(v2b, seg1)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def apply(c1: Circle, c2 : Circle): Float = {
|
||||
math.max(0, Vector3.Magnitude(Vector3(c1.x - c2.x, c1.y - c2.y, 0)) - c1.radius - c2.radius)
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param line1 na
|
||||
* @param line2 na
|
||||
* @return the shortest distance between the lines;
|
||||
* if parallel, the common perpendicular distance between the lines;
|
||||
* if coincidental, this distance will be 0
|
||||
*/
|
||||
def apply(line1: Line3D, line2: Line3D): Float = {
|
||||
val cross = Vector3.CrossProduct(line1.d, line2.d)
|
||||
if(cross != Vector3.Zero) {
|
||||
math.abs(
|
||||
Vector3.DotProduct(cross, Vector3(line1.x - line2.x, line1.y - line2.y, line1.z - line2.z))
|
||||
) / Vector3.Magnitude(cross)
|
||||
} else {
|
||||
// lines are parallel or coincidental
|
||||
// construct a right triangle with one leg on line1 and the hypotenuse between the line's known points
|
||||
val hypotenuse = Vector3(line2.x - line1.x, line2.y - line1.y, line2.z - line1.z)
|
||||
val legOnLine1 = line1.d * Vector3.DotProduct(hypotenuse, line1.d)
|
||||
Vector3.Magnitude(hypotenuse - legOnLine1)
|
||||
}
|
||||
}
|
||||
|
||||
def apply(seg1: Segment3D, seg2: Segment3D): Float = {
|
||||
//TODO make not as expensive as finding the plotted closest distance segment
|
||||
Segment(seg1, seg2) match {
|
||||
case Some(seg) => seg.length
|
||||
case None => Float.MaxValue
|
||||
}
|
||||
}
|
||||
|
||||
def apply(s1: Sphere, s2 : Sphere): Float = {
|
||||
math.max(0, Vector3.Magnitude(Vector3(s1.x - s2.x, s1.y - s2.y, s1.z - s2.z)) - s1.radius - s2.radius)
|
||||
}
|
||||
}
|
||||
|
||||
object Segment {
|
||||
/**
|
||||
* na
|
||||
* @param c1 na
|
||||
* @param c2 na
|
||||
* @return a line segment that represents the closest distance between the circle's circumferences;
|
||||
* `None`, if the circles have no distance between them (overlapping)
|
||||
*/
|
||||
def apply(c1 : Circle, c2 : Circle): Option[Segment2D] = {
|
||||
val distance = Distance(c1, c2)
|
||||
if (distance > 0) {
|
||||
val c1x = c1.x
|
||||
val c1y = c1.y
|
||||
val v = Vector3.Unit(Vector3(c2.x - c1x, c2.y - c1y, 0f))
|
||||
val c1d = v * c1.radius
|
||||
val c2d = v * c2.radius
|
||||
Some(
|
||||
Segment2D(
|
||||
c1x + c1d.x, c1y + c1d.y,
|
||||
c1x + c2d.x, c1y + c2d.y,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param line1 na
|
||||
* @param line2 na
|
||||
* @return a line segment representing the closest distance between the two not intersecting lines;
|
||||
* in the case of parallel lines, one of infinite closest distances is plotted;
|
||||
* `None`, if the lines intersect with each other
|
||||
*/
|
||||
def apply(line1 : Line3D, line2 : Line3D): Option[Segment3D] = {
|
||||
val p1 = Vector3(line1.x, line1.y, line1.z)
|
||||
val p3 = Vector3(line2.x, line2.y, line2.z)
|
||||
val p13 = p1 - p3 // vector between point on first line and point on second line
|
||||
val p43 = line2.d
|
||||
val p21 = line1.d
|
||||
if (Vector3.MagnitudeSquared(p43) < Float.MinPositiveValue ||
|
||||
Vector3.MagnitudeSquared(p21) < Float.MinPositiveValue) {
|
||||
None
|
||||
} else {
|
||||
val d2121 = Vector3.MagnitudeSquared(p21)
|
||||
val d4343 = Vector3.MagnitudeSquared(p43)
|
||||
val d4321 = Vector3.DotProduct(p43, p21)
|
||||
val denom = d2121 * d4343 - d4321 * d4321 // n where d = (m/n) and a(x,y,z) + d * V<u,v,w> = b(x,y,z) for line1
|
||||
if (math.abs(denom) < Float.MinPositiveValue) {
|
||||
// without a denominator, we have no cross product solution
|
||||
val p13u = Vector3.Unit(p13)
|
||||
if (p21 == p13u || p21 == Vector3.neg(p13u)) { //coincidental lines overlap / intersect
|
||||
None
|
||||
} else { //parallel lines
|
||||
val connecting = Vector3(line2.x - line1.x, line2.y - line1.y, line2.z - line1.z)
|
||||
val legOnLine1 = line1.d * Vector3.DotProduct(connecting, line1.d)
|
||||
val v = connecting - legOnLine1
|
||||
Some(Segment3D(
|
||||
line1.x, line1.y, line1.z,
|
||||
line1.x + v.x, line1.y + v.y, line1.z + v.z
|
||||
))
|
||||
}
|
||||
} else {
|
||||
val d1343 = Vector3.DotProduct(p13, p43)
|
||||
val numer = d1343 * d4321 -d4343 * Vector3.DotProduct(p13, p21) // m where d = (m/n) and ..., etc.
|
||||
val mua = numer / denom
|
||||
val mub = (d1343 + d4321 * mua) / d4343
|
||||
Some(Segment3D(
|
||||
p1.x + mua * p21.x,
|
||||
p1.y + mua * p21.y,
|
||||
p1.z + mua * p21.z,
|
||||
p3.x + mub * p43.x,
|
||||
p3.y + mub * p43.y,
|
||||
p3.z + mub * p43.z
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def apply(line1 : Segment3D, line2 : Segment3D): Option[Segment3D] = {
|
||||
val uline1 = Vector3.Unit(line1.d)
|
||||
val uline2 = Vector3.Unit(line2.d)
|
||||
apply(Line3D(line1.ax, line1.ay, line1.az, uline1), Line3D(line2.ax, line2.ay, line2.az, uline2)) match {
|
||||
case Some(seg: Segment3D) => // common skew lines and parallel lines
|
||||
val sega = Vector3(seg.ax, seg.ay, seg.az)
|
||||
val p1 = Vector3(line1.ax, line1.ay, line1.az)
|
||||
val d1 = sega - p1
|
||||
val out1 = if (!Geometry.equalVectors(Vector3.Unit(d1), uline1)) { //clamp seg.a(xyz) to segment line1's bounds
|
||||
p1
|
||||
} else if (Vector3.MagnitudeSquared(d1) > Vector3.MagnitudeSquared(line1.d)) {
|
||||
Vector3(line1.bx, line1.by, line1.bz)
|
||||
} else {
|
||||
sega
|
||||
}
|
||||
val segb = Vector3(seg.bx, seg.by, seg.bz)
|
||||
val p2 = Vector3(line2.ax, line2.ay, line2.az)
|
||||
val d2 = segb - p2
|
||||
val out2 = if (!Geometry.equalVectors(Vector3.Unit(d2), uline2)) { //clamp seg.b(xyz) to segment line2's bounds
|
||||
p2
|
||||
} else if (Vector3.MagnitudeSquared(d2) > Vector3.MagnitudeSquared(line2.d)) {
|
||||
Vector3(line2.bx, line2.by, line2.bz)
|
||||
} else {
|
||||
segb
|
||||
}
|
||||
Some(Segment3D(
|
||||
out1.x, out1.y, out1.z,
|
||||
out2.x, out2.y, out2.z
|
||||
))
|
||||
case None =>
|
||||
val connectingU = Vector3.Unit(Vector3(line2.ax - line1.ax, line2.ay - line1.ay, line2.az - line1.az))
|
||||
if (uline1 == connectingU || uline1 == Vector3.neg(connectingU)) { // coincidental line segments
|
||||
val line1a = Vector3(line1.ax, line1.ay, line1.az)
|
||||
val line1b = Vector3(line1.bx, line1.by, line1.bz)
|
||||
val line2a = Vector3(line2.ax, line2.ay, line2.az)
|
||||
val line2b = Vector3(line2.bx, line2.by, line2.bz)
|
||||
if (Vector3.Unit(line2a - line1a) != Vector3.Unit(line2b - line1a) ||
|
||||
Vector3.Unit(line2a - line1b) != Vector3.Unit(line2b - line1b) ||
|
||||
Vector3.Unit(line1a - line2a) != Vector3.Unit(line1b - line2a) ||
|
||||
Vector3.Unit(line1a - line2b) != Vector3.Unit(line1b - line2b)) {
|
||||
Some(Segment3D(
|
||||
line1.ax, line1.ay, line1a.z,
|
||||
line1.ax, line1.ay, line1a.z
|
||||
)) // overlap regions
|
||||
}
|
||||
else {
|
||||
val segs = List((line1a, line2a), (line1a, line2b), (line2a, line1b))
|
||||
val (a, b) = segs({
|
||||
//val dist = segs.map { case (_a, _b) => Vector3.DistanceSquared(_a, _b) }
|
||||
//dist.indexOf(dist.min)
|
||||
var index = 0
|
||||
var minDist = Vector3.DistanceSquared(segs.head._1, segs.head._2)
|
||||
(1 to 2).foreach { i =>
|
||||
val dist = Vector3.DistanceSquared(segs(i)._1, segs(i)._2)
|
||||
if (minDist < dist) {
|
||||
index = i
|
||||
minDist = dist
|
||||
}
|
||||
}
|
||||
index
|
||||
})
|
||||
Some(Segment3D(a.x, a.y, a.z, b.x, b.y, b.z)) // connecting across the smallest gap
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param s1 na
|
||||
* @param s2 na
|
||||
* @return a line segment that represents the closest distance between the sphere's surface areas;
|
||||
* `None`, if the spheres have no distance between them (overlapping)
|
||||
*/
|
||||
def apply(s1 : Sphere, s2 : Sphere): Option[Segment3D] = {
|
||||
val distance = Distance(s1, s2)
|
||||
if (distance > 0) {
|
||||
val s1x = s1.x
|
||||
val s1y = s1.y
|
||||
val s1z = s1.z
|
||||
val v = Vector3.Unit(Vector3(s2.x - s1x, s2.y - s1y, s2.z - s1z))
|
||||
val s1d = v * s1.radius
|
||||
val s2d = v * (s1.radius + distance)
|
||||
Some(Segment3D(s1x + s1d.x, s1y + s1d.y, s1y + s1d.y, s1x + s2d.x, s1y + s2d.y, s1y + s2d.y))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def apply(line : Line3D, sphere : Sphere): Option[Segment3D] = {
|
||||
val sphereAsPoint = Vector3(sphere.x, sphere.y, sphere.z)
|
||||
val lineAsPoint = Vector3(line.x, line.y, line.z)
|
||||
val direct = sphereAsPoint - lineAsPoint
|
||||
val projectionOfDirect = line.d * Vector3.DotProduct(direct, line.d)
|
||||
val heightFromProjection = projectionOfDirect - direct
|
||||
val heightFromProjectionDist = Vector3.Magnitude(heightFromProjection)
|
||||
if (heightFromProjectionDist <= sphere.radius) { //intersection
|
||||
None
|
||||
} else {
|
||||
val pointOnLine = lineAsPoint + projectionOfDirect
|
||||
val pointOnSphere = pointOnLine +
|
||||
Vector3.Unit(heightFromProjection) * (heightFromProjectionDist - sphere.radius)
|
||||
Some(Segment3D(
|
||||
pointOnLine.x, pointOnLine.y, pointOnLine.z,
|
||||
pointOnSphere.x, pointOnSphere.y, pointOnSphere.z
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.geometry
|
||||
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object ClosestDistance {
|
||||
object Between {
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, point : Vector3, seg : Segment2D) : Float = {
|
||||
val segdx = seg.bx - seg.ax
|
||||
val segdy = seg.by - seg.ay
|
||||
((point.x + origin1.x - seg.ax + origin2.x) * segdx + (point.y + origin1.y - seg.ay + origin2.y) * segdy) /
|
||||
Vector3.MagnitudeSquared(Vector3(segdx, segdy, 0))
|
||||
}
|
||||
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, line1 : Line2D, line2 : Line2D) : Float = {
|
||||
if (Intersection.Test(origin1, origin2, line1, line2)) { //intersecting lines
|
||||
0f
|
||||
} else {
|
||||
math.abs(
|
||||
Vector3.DotProduct(
|
||||
Vector3(line2.x - line1.x, line2.y - line1.y, 0),
|
||||
Vector3(-1/line1.d.y, 1/line1.d.x, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def apply(origin1: Vector3, origin2: Vector3, seg1: Segment2D, seg2: Segment2D): Float = {
|
||||
if (Intersection.Test(origin1, origin2, seg1, seg2)) { //intersecting line segments
|
||||
0f
|
||||
} else {
|
||||
val v1a = Vector3(seg1.ax, seg1.ay, 0)
|
||||
val v2a = Vector3(seg2.ax, seg2.ay, 0)
|
||||
val v1b = Vector3(seg1.bx, seg1.by, 0)
|
||||
val v2b = Vector3(seg2.bx, seg2.by, 0)
|
||||
math.min(
|
||||
apply(origin1, origin2, v1a, seg2),
|
||||
math.min(
|
||||
apply(origin1, origin2, v1b, seg2),
|
||||
math.min(
|
||||
apply(origin1, origin2, v2a, seg1),
|
||||
apply(origin1, origin2, v2b, seg1)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def apply(origin1: Vector3, origin2: Vector3, line1: Line3D, line2: Line3D): Float = {
|
||||
val cross = Vector3.CrossProduct(line1.d, line2.d)
|
||||
if(cross != Vector3.Zero) {
|
||||
math.abs(
|
||||
Vector3.DotProduct(cross, Vector3(line1.x - line2.x, line1.y - line2.y, line1.z - line2.z))
|
||||
) / Vector3.Magnitude(cross)
|
||||
} else {
|
||||
//lines are parallel
|
||||
Vector3.Magnitude(
|
||||
Vector3.CrossProduct(
|
||||
line1.d,
|
||||
Vector3(line2.x - line1.x, line2.y - line1.y, line2.z - line1.z)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def apply(origin1: Vector3, origin2: Vector3, seg1: Segment3D, seg2: Segment3D): Float = {
|
||||
//TODO make not as expensive as finding the plotted closest distance segment
|
||||
Plotted(origin1, origin2, seg1, seg2) match {
|
||||
case Some(seg) => seg.length
|
||||
case None => Float.MaxValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Plotted {
|
||||
/**
|
||||
* na
|
||||
* This function can only operate normally if a perpendicular line segment between the two lines can be established,
|
||||
* this is, if the cross product of the two lines exists.
|
||||
* As such, for coincidental lines, a segment of zero length from the first line's point is produced.
|
||||
* @param origin1 na
|
||||
* @param origin2 na
|
||||
* @param line1 na
|
||||
* @param line2 na
|
||||
* @return na
|
||||
*/
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, line1 : Line3D, line2 : Line3D): Option[Segment3D] = {
|
||||
val p1 = Vector3(line1.x, line1.y, line1.z)
|
||||
val p2 = p1 + line1.d
|
||||
val p3 = Vector3(line2.x, line2.y, line2.z)
|
||||
val p4 = p3 + line2.d
|
||||
val p13 = p1 - p3 // vector between point on first line and point on second line
|
||||
val p43 = line2.d
|
||||
val p21 = line1.d
|
||||
if (Vector3.MagnitudeSquared(p43) < Float.MinPositiveValue ||
|
||||
Vector3.MagnitudeSquared(p21) < Float.MinPositiveValue) {
|
||||
None
|
||||
} else {
|
||||
val d2121 = Vector3.MagnitudeSquared(p21)
|
||||
val d4343 = Vector3.MagnitudeSquared(p43)
|
||||
val d4321 = Vector3.DotProduct(p43, p21)
|
||||
val denom = d2121 * d4343 - d4321 * d4321 // n where d = (m/n) and a(x,y,z) + d * V<u,v,w> = b(x,y,z) for line1
|
||||
if (math.abs(denom) < Float.MinPositiveValue) {
|
||||
val p13u = Vector3.Unit(p13)
|
||||
if (p21 == p13u || p21 == Vector3.neg(p13u)) { //coincidental lines
|
||||
// can not produce a valid cross product, but a coincidental line does produce an overlap
|
||||
Some(Segment3D(
|
||||
line1.x, line1.y, line1.z,
|
||||
line1.x, line1.y, line1.z
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
val d1343 = Vector3.DotProduct(p13, p43)
|
||||
val numer = d1343 * d4321 -d4343 * Vector3.DotProduct(p13, p21) // m where d = (m/n) and ..., etc.
|
||||
val mua = numer / denom
|
||||
val mub = (d1343 + d4321 * mua) / d4343
|
||||
Some(Segment3D(
|
||||
p1.x + mua * p21.x,
|
||||
p1.y + mua * p21.y,
|
||||
p1.z + mua * p21.z,
|
||||
p3.x + mub * p43.x,
|
||||
p3.y + mub * p43.y,
|
||||
p3.z + mub * p43.z
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, line1 : Segment3D, line2 : Segment3D): Option[Segment3D] = {
|
||||
val uline1 = Vector3.Unit(line1.d)
|
||||
val uline2 = Vector3.Unit(line2.d)
|
||||
apply(
|
||||
origin1,
|
||||
origin2,
|
||||
Line3D(line1.ax, line1.ay, line1.az, uline1),
|
||||
Line3D(line2.ax, line2.ay, line2.az, uline2)
|
||||
) match {
|
||||
case out @ Some(seg: Segment3D)
|
||||
if seg.length == 0 && (uline1 == uline2 || uline1 == Vector3.neg(uline2)) => //coincidental lines
|
||||
out
|
||||
case Some(seg: Segment3D) => //segment of shortest distance when two segments treated as lines
|
||||
val sega = Vector3(seg.ax, seg.ay, seg.az)
|
||||
val p1 = Vector3(line1.ax, line1.ay, line1.az)
|
||||
val d1 = sega - p1
|
||||
val out1 = if (!Geometry.equalVectors(Vector3.Unit(d1), uline1)) { //clamp seg.a(xyz) to segment line1's bounds
|
||||
p1
|
||||
} else if (Vector3.MagnitudeSquared(d1) > Vector3.MagnitudeSquared(line1.d)) {
|
||||
Vector3(line1.bx, line1.by, line1.bz)
|
||||
} else {
|
||||
sega
|
||||
}
|
||||
val segb = Vector3(seg.bx, seg.by, seg.bz)
|
||||
val p2 = Vector3(line2.ax, line2.ay, line2.az)
|
||||
val d2 = segb - p2
|
||||
val out2 = if (!Geometry.equalVectors(Vector3.Unit(d2), uline2)) { //clamp seg.b(xyz) to segment line2's bounds
|
||||
p2
|
||||
} else if (Vector3.MagnitudeSquared(d2) > Vector3.MagnitudeSquared(line2.d)) {
|
||||
Vector3(line2.bx, line2.by, line2.bz)
|
||||
} else {
|
||||
segb
|
||||
}
|
||||
Some(Segment3D(
|
||||
out1.x, out1.y, out1.z,
|
||||
out2.x, out2.y, out2.z
|
||||
))
|
||||
case None =>
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,12 @@ object Segment2D {
|
|||
}
|
||||
}
|
||||
|
||||
final case class Circle(x: Float, y: Float, radius: Float)
|
||||
|
||||
object Circle {
|
||||
def apply(radius: Float): Circle = Circle(0f, 0f, radius)
|
||||
}
|
||||
|
||||
final case class Line3D(x: Float, y: Float, z: Float, d: Vector3) extends Line
|
||||
|
||||
final case class Segment3D(ax: Float, ay: Float, az: Float, bx: Float, by: Float, bz: Float) extends Segment {
|
||||
|
|
@ -52,6 +58,20 @@ object Segment3D {
|
|||
}
|
||||
}
|
||||
|
||||
final case class Sphere(x: Float, y: Float, z: Float, radius: Float)
|
||||
|
||||
final case class Cylinder(circle: Circle, z: Float, height: Float)
|
||||
|
||||
object Cylinder {
|
||||
def apply(x: Float, y: Float, z: Float, radius: Float, height: Float): Cylinder = {
|
||||
Cylinder(Circle(x, y, radius), z, height)
|
||||
}
|
||||
}
|
||||
|
||||
object Sphere {
|
||||
def apply(p: Vector3, radius: Float): Sphere = Sphere(p.x, p.y, p.z, radius)
|
||||
}
|
||||
|
||||
object Geometry {
|
||||
def equalFloats(value1: Float, value2: Float, off: Float = 0.001f): Boolean = {
|
||||
val diff = value1 - value2
|
||||
|
|
|
|||
|
|
@ -8,17 +8,17 @@ object Intersection {
|
|||
/**
|
||||
* Do these two lines intersect?
|
||||
* Lines in 2D space will always intersect unless they are parallel or antiparallel.
|
||||
* In that case, they can still "intersect" if the lines are coincidental.
|
||||
* In that case, however, they can still "intersect" if provided that the lines are coincidental.
|
||||
*/
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, line1 : Line2D, line2 : Line2D): Boolean = {
|
||||
def apply(line1: Line2D, line2: Line2D): Boolean = {
|
||||
line1.d != line2.d || {
|
||||
//parallel or antiparallel?
|
||||
val u = Vector3.Unit(Vector3(line2.x - line1.x, line2.y - line1.y, 0))
|
||||
line1.d == u || line1.d == Vector3.neg(u)
|
||||
u == Vector3.Zero || line1.d == u || line1.d == Vector3.neg(u)
|
||||
}
|
||||
}
|
||||
|
||||
private def pointOnSegment(ax : Float, ay : Float, px : Float, py : Float, bx : Float, by : Float): Boolean = {
|
||||
private def pointOnSegment(ax: Float, ay: Float, px: Float, py: Float, bx: Float, by: Float): Boolean = {
|
||||
px <= math.max(ax, bx) && px >= math.min(ax, bx) && py <= math.max(ay, by) && py >= math.min(ay, by)
|
||||
}
|
||||
|
||||
|
|
@ -41,9 +41,9 @@ object Intersection {
|
|||
* @return the orientation value
|
||||
*/
|
||||
private def orientationOfPoints(
|
||||
ax : Float, ay : Float,
|
||||
px : Float, py : Float,
|
||||
bx : Float, by : Float
|
||||
ax: Float, ay: Float,
|
||||
px: Float, py: Float,
|
||||
bx: Float, by: Float
|
||||
): PointTripleOrientation.Value = {
|
||||
val out = (py - ay) * (bx - px) - (px - ax) * (by - py)
|
||||
if (out == 0) PointTripleOrientation.Colinear
|
||||
|
|
@ -57,16 +57,16 @@ object Intersection {
|
|||
* If a test of multiple ordered triple points reveals that certain triples have different orientations,
|
||||
* then we can safely assume the intersection state of the segments.
|
||||
*/
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, line1 : Segment2D, line2 : Segment2D): Boolean = {
|
||||
def apply(line1: Segment2D, line2: Segment2D): Boolean = {
|
||||
//setup
|
||||
val ln1ax = line1.ax + origin1.x
|
||||
val ln1ay = line1.ay + origin1.y
|
||||
val ln1bx = ln1ax + origin1.x
|
||||
val ln1by = ln1ay + origin1.y
|
||||
val ln2ax = line2.ax + origin2.x
|
||||
val ln2ay = line2.ay + origin2.y
|
||||
val ln2bx = ln2ax + origin2.x
|
||||
val ln2by = ln2ay + origin2.y
|
||||
val ln1ax = line1.ax
|
||||
val ln1ay = line1.ay
|
||||
val ln1bx = line1.bx
|
||||
val ln1by = line1.by
|
||||
val ln2ax = line2.ax
|
||||
val ln2ay = line2.ay
|
||||
val ln2bx = line2.bx
|
||||
val ln2by = line2.by
|
||||
val ln1_ln2a = orientationOfPoints(ln1ax, ln1ay, ln1bx, ln1by, ln2ax, ln2ay)
|
||||
val ln1_ln2b = orientationOfPoints(ln1ax, ln1ay, ln1bx, ln1by, ln2bx, ln2by)
|
||||
val ln2_ln1a = orientationOfPoints(ln2ax, ln2ay, ln2bx, ln2by, ln1ax, ln1ay)
|
||||
|
|
@ -85,11 +85,15 @@ object Intersection {
|
|||
* Actual mathematically-sound intersection between lines and line segments in 3D-space is terribly uncommon.
|
||||
* Instead, check that the closest distance between two line segments is below a threshold value.
|
||||
*/
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, line1 : Line3D, line2 : Line3D): Boolean = {
|
||||
apply(origin1, origin2, line1, line2, 0.15f)
|
||||
def apply(line1: Line3D, line2: Line3D): Boolean = {
|
||||
apply(line1, line2, 0.15f)
|
||||
}
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, line1 : Line3D, line2 : Line3D, threshold: Float): Boolean = {
|
||||
ClosestDistance.Between(origin1, origin2, line1, line2) < threshold
|
||||
def apply(line1: Line3D, line2: Line3D, threshold: Float): Boolean = {
|
||||
Closest.Distance(line1, line2) < threshold
|
||||
}
|
||||
|
||||
def apply(c1: Circle, c2 : Circle): Boolean = {
|
||||
Vector3.Magnitude(Vector3(c1.x - c2.x, c1.y - c2.y, 0)) <= c1.radius + c2.radius
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -97,11 +101,58 @@ object Intersection {
|
|||
* Actual mathematically-sound intersection between lines and line segments in 3D-space is terribly uncommon.
|
||||
* Instead, check that the closest distance between two line segments is below a threshold value.
|
||||
*/
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, seg1 : Segment3D, seg2 : Segment3D): Boolean = {
|
||||
apply(origin1, origin2, seg1, seg2, 0.15f)
|
||||
def apply(seg1: Segment3D, seg2: Segment3D): Boolean = {
|
||||
apply(seg1, seg2, 0.15f)
|
||||
}
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, seg1 : Segment3D, seg2 : Segment3D, threshold: Float): Boolean = {
|
||||
ClosestDistance.Between(origin1, origin2, seg1, seg2) < threshold
|
||||
def apply(seg1: Segment3D, seg2: Segment3D, threshold: Float): Boolean = {
|
||||
Closest.Distance(seg1, seg2) < threshold
|
||||
}
|
||||
|
||||
def apply(s1: Sphere, s2 : Sphere): Boolean = {
|
||||
Vector3.Magnitude(
|
||||
Vector3(
|
||||
s1.x - s2.x,
|
||||
s1.y - s2.y,
|
||||
s1.z - s2.z
|
||||
)
|
||||
) <= s1.radius + s2.radius
|
||||
}
|
||||
|
||||
def apply(c1: Cylinder, c2: Cylinder): Boolean = {
|
||||
apply(c1.circle, c2.circle) &&
|
||||
((c1.height >= c2.z && c1.z <= c2.height) || (c2.height >= c1.z && c2.z <= c1.height))
|
||||
}
|
||||
|
||||
def apply(cylinder: Cylinder, sphere: Sphere): Boolean = {
|
||||
val cylinderCircle = cylinder.circle
|
||||
val cylinderCircleRadius = cylinderCircle.radius
|
||||
val cylinderTop = cylinder.z + cylinder.height
|
||||
val sphereRadius = sphere.radius
|
||||
val sphereBase = sphere.z - sphereRadius
|
||||
val sphereTop = sphere.z + sphereRadius
|
||||
if (apply(cylinderCircle, Circle(sphere.x, sphere.y, sphereRadius)) &&
|
||||
((sphereTop >= cylinder.z && sphereBase <= cylinderTop) ||
|
||||
(cylinderTop >= sphereBase && cylinder.z <= sphereTop))) {
|
||||
// potential intersection ...
|
||||
val sphereAsPoint = Vector3(sphere.x, sphere.y, sphere.z)
|
||||
val cylinderAsPoint = Vector3(cylinderCircle.x, cylinderCircle.y, cylinder.z)
|
||||
val segmentFromCylinderToSphere = sphereAsPoint - cylinderAsPoint
|
||||
val segmentFromCylinderToSphereXY = segmentFromCylinderToSphere.xy
|
||||
if ((cylinder.z <= sphere.z && sphere.z <= cylinderTop) ||
|
||||
Vector3.MagnitudeSquared(segmentFromCylinderToSphereXY) <= cylinderCircleRadius * cylinderCircleRadius) {
|
||||
true // top or bottom of sphere, or widest part of the sphere, must interact with the cylinder
|
||||
} else {
|
||||
// only option left is the curves of the sphere interacting with the cylinder's rim, top or base
|
||||
val directionFromCylinderToSphere = Vector3.Unit(segmentFromCylinderToSphereXY)
|
||||
val pointOnCylinderRimBase = cylinderAsPoint + directionFromCylinderToSphere * cylinderCircleRadius
|
||||
val pointOnCylinderRimTop = pointOnCylinderRimBase + Vector3.z(cylinder.height)
|
||||
val sqSphereRadius = sphereRadius * sphereRadius
|
||||
Vector3.DistanceSquared(sphereAsPoint, pointOnCylinderRimTop) <= sqSphereRadius ||
|
||||
Vector3.DistanceSquared(sphereAsPoint, pointOnCylinderRimBase) <= sqSphereRadius
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,175 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package objects
|
||||
|
||||
import net.psforever.objects.geometry.{Intersection, Line3D, Segment3D}
|
||||
import net.psforever.objects.geometry._
|
||||
import net.psforever.types.Vector3
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
class IntersectionTest extends Specification {
|
||||
"Line2D" should {
|
||||
"detect intersection on target points(s)" in {
|
||||
//these lines intersect at (0, 0)
|
||||
val result = Intersection.Test(
|
||||
Line2D(0,0, 1,0),
|
||||
Line2D(0,0, 0,1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection on a target point" in {
|
||||
//these lines intersect at (0, 0); start of segment 1, middle of segment 2
|
||||
val result = Intersection.Test(
|
||||
Line2D( 0,0, 0,1),
|
||||
Line2D(-1,0, 1,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection anywhere else" in {
|
||||
//these lines intersect at (0.5f, 0.5f)
|
||||
val result = Intersection.Test(
|
||||
Line2D(0,0, 1,1),
|
||||
Line2D(1,0, 0,1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection anywhere else (2)" in {
|
||||
//these lines intersect at (0, 0.5)
|
||||
val result = Intersection.Test(
|
||||
Line2D(0, 0, 1, 0),
|
||||
Line2D(0.5f,1, 0.5f,-1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not detect intersection if the lines are parallel" in {
|
||||
val result = Intersection.Test(
|
||||
Line2D(0,0, 1,1),
|
||||
Line2D(1,0, 2,1)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"detect intersection if the lines overlap" in {
|
||||
//the lines are coincidental
|
||||
val result = Intersection.Test(
|
||||
Line2D(0,0, 1,1),
|
||||
Line2D(1,1, 2,2)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
}
|
||||
|
||||
"Segment2D" should {
|
||||
"detect intersection on target points(s)" in {
|
||||
//these line segments intersect at (0, 0)
|
||||
val result = Intersection.Test(
|
||||
Segment2D(0,0, 1,0),
|
||||
Segment2D(0,0, 0,1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection on a target point" in {
|
||||
//these line segments intersect at (0, 0); start of segment 1, middle of segment 2
|
||||
val result = Intersection.Test(
|
||||
Segment2D( 0,0, 0,1),
|
||||
Segment2D(-1,0, 1,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection anywhere else" in {
|
||||
//these line segments intersect at (0.5f, 0.5f)
|
||||
val result = Intersection.Test(
|
||||
Segment2D(0,0, 1,1),
|
||||
Segment2D(1,0, 0,1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection anywhere else (2)" in {
|
||||
//these line segments intersect at (0, 0.5)
|
||||
val result = Intersection.Test(
|
||||
Segment2D(0, 0, 1, 0),
|
||||
Segment2D(0.5f,1, 0.5f,-1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not detect intersection if the lines are parallel" in {
|
||||
val result = Intersection.Test(
|
||||
Segment2D(0,0, 1,1),
|
||||
Segment2D(1,0, 2,1)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"detect intersection if the lines overlap" in {
|
||||
//the lines are coincidental
|
||||
val result = Intersection.Test(
|
||||
Line2D(0,0, 1,1),
|
||||
Line2D(1,1, 2,2)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
}
|
||||
|
||||
"Circle" should {
|
||||
"intersect when overlapping (coincidental)" in {
|
||||
val result = Intersection.Test(
|
||||
Circle(0,0, 1),
|
||||
Circle(0,0, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"intersect when overlapping (engulfed)" in {
|
||||
val result = Intersection.Test(
|
||||
Circle(0,0, 2),
|
||||
Circle(1,0, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"intersect when overlapping (partial 1)" in {
|
||||
val result = Intersection.Test(
|
||||
Circle(0,0, 2),
|
||||
Circle(2,0, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"intersect when overlapping (partial 2)" in {
|
||||
val result = Intersection.Test(
|
||||
Circle(0, 0, 2),
|
||||
Circle(2.5f,0, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"intersect when the circumferences are touching" in {
|
||||
val result = Intersection.Test(
|
||||
Circle(0,0, 2),
|
||||
Circle(3,0, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not intersect when not touching" in {
|
||||
val result = Intersection.Test(
|
||||
Circle(0,0, 2),
|
||||
Circle(4,0, 1)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
}
|
||||
|
||||
"Line3D" should {
|
||||
"detect intersection on target point(s)" in {
|
||||
//these lines intersect at (0, 0, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Line3D(0,0,0, Vector3(1,0,0)),
|
||||
Line3D(0,0,0, Vector3(0,1,0))
|
||||
)
|
||||
|
|
@ -18,60 +178,42 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"detect intersection on a target point" in {
|
||||
//these lines intersect at (0, 0, 0); start of segment 1, middle of segment 2
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Line3D(0,0,0, Vector3(0,1,0)),
|
||||
Line3D(-1,0,0, Vector3(1,0,0))
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection in the middle(s)" in {
|
||||
"detect intersection anywhere else" in {
|
||||
//these lines intersect at (0.5f, 0.5f, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Line3D(0,0,0, Vector3.Unit(Vector3(1, 1, 0))),
|
||||
Line3D(1,0,0, Vector3(0,1,0))
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection in the middle " in {
|
||||
"detect intersection anywhere else (2)" in {
|
||||
//these lines intersect at (0, 0.5, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Line3D(0,0,0, Vector3(1,0,0)),
|
||||
Line3D(0.5f,1,0, Vector3.Unit(Vector3(0.5f,-1,0)))
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if the point of intersection would be before the start of the segments" in {
|
||||
//these lines would intersect at (0, 0, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Line3D(1,1,0, Vector3.Unit(Vector3(2, 2, 0))),
|
||||
Line3D(1,0,0, Vector3.Unit(Vector3(2,0,0)))
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if the point of intersection would be after the end of the segments" in {
|
||||
//these lines would intersect at (2, 2, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Line3D(0,0,0, Vector3.Unit(Vector3(1,1,0))),
|
||||
Line3D(2,0,0, Vector3.Unit(Vector3(2,1,0)))
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not detect intersection if the line segments are parallel" in {
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
"not detect intersection if the lines are parallel" in {
|
||||
val result = Intersection.Test(
|
||||
Line3D(0,0,0, Vector3.Unit(Vector3(1,1,1))),
|
||||
Line3D(1,1,2, Vector3.Unit(Vector3(1,1,1)))
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"detect overlap" in {
|
||||
"detect intersection if the lines overlap" in {
|
||||
//the sub-segment (1,0,0) to (2,0,0) is an overlap region shared between the two segments
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Line3D(0,0,0, Vector3.Unit(Vector3(2,0,0))),
|
||||
Line3D(1,0,0, Vector3.Unit(Vector3(3,0,0)))
|
||||
)
|
||||
|
|
@ -80,7 +222,7 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"not detect intersection (generic skew)" in {
|
||||
//these segments will not intersect
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(-3,-8,7, Vector3.Unit(Vector3(-3,-9,8))),
|
||||
Segment3D(6,3,0, Vector3.Unit(Vector3(2,0,0)))
|
||||
)
|
||||
|
|
@ -91,7 +233,7 @@ class IntersectionTest extends Specification {
|
|||
"Segment3D" should {
|
||||
"detect intersection of the first point(s)" in {
|
||||
//these segments intersect at (0, 0, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,0, 1,0,0),
|
||||
Segment3D(0,0,0, 0,1,0)
|
||||
)
|
||||
|
|
@ -100,7 +242,7 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"detect intersection of the first point" in {
|
||||
//these segments intersect at (0, 0, 0); start of segment 1, middle of segment 2
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,0, 0,2,0),
|
||||
Segment3D(-1,0,0, 1,0,0)
|
||||
)
|
||||
|
|
@ -109,7 +251,7 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"detect intersection on the farther point(s)" in {
|
||||
//these segments intersect at (0, 1, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,1, 0,1,0),
|
||||
Segment3D(1,0,0, 0,1,0)
|
||||
)
|
||||
|
|
@ -118,7 +260,7 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"detect intersection on the farther point" in {
|
||||
//these segments intersect at (1, 1, 0); end of segment 1, middle of segment 2
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(1,0,0, 1,1,0),
|
||||
Segment3D(2,0,0, 0,2,0)
|
||||
)
|
||||
|
|
@ -127,7 +269,7 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"detect intersection in the middle(s)" in {
|
||||
//these segments intersect at (0.5f, 0.5f, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,0, 1,1,0),
|
||||
Segment3D(1,0,0, 0,1,0)
|
||||
)
|
||||
|
|
@ -136,7 +278,7 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"detect intersection in the middle " in {
|
||||
//these segments intersect at (0, 0.5, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,0, 1,0,0),
|
||||
Segment3D(0.5f,1,0, 0.5f,-1,0)
|
||||
)
|
||||
|
|
@ -145,7 +287,7 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"not detect intersection if the point of intersection would be before the start of the segments" in {
|
||||
//these segments will not intersect as segments; but, as lines, they would intersect at (0, 0, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(1,1,0, 2,2,0),
|
||||
Segment3D(1,0,0, 2,0,0)
|
||||
)
|
||||
|
|
@ -154,7 +296,7 @@ class IntersectionTest extends Specification {
|
|||
|
||||
"not detect intersection if the point of intersection would be after the end of the segments" in {
|
||||
//these segments will not intersect as segments; but, as lines, they would intersect at (2, 2, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,0, 1,1,0),
|
||||
Segment3D(2,0,0, 2,1,0)
|
||||
)
|
||||
|
|
@ -162,33 +304,230 @@ class IntersectionTest extends Specification {
|
|||
}
|
||||
|
||||
"not detect intersection if the line segments are parallel" in {
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,0, 1,1,1),
|
||||
Segment3D(1,1,2, 2,2,3)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"detect overlap" in {
|
||||
"detect intersection with overlapping" in {
|
||||
//the sub-segment (1,0,0) to (2,0,0) is an overlap region shared between the two segments
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,0, 2,0,0),
|
||||
Segment3D(1,0,0, 3,0,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not detect intersection with coincidental, non-overlapping" in {
|
||||
//the sub-segment (1,0,0) to (2,0,0) is an overlap region shared between the two segments
|
||||
val result = Intersection.Test(
|
||||
Segment3D(0,0,0, 1,0,0),
|
||||
Segment3D(2,0,0, 3,0,0)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"not detect intersection (generic skew)" in {
|
||||
//these segments will not intersect
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
val result = Intersection.Test(
|
||||
Segment3D(-3,-8,7, -3,-9,8),
|
||||
Segment3D(6,3,0, 2,0,0)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
}
|
||||
|
||||
"Sphere" should {
|
||||
"intersect when overlapping (coincidental)" in {
|
||||
val result = Intersection.Test(
|
||||
Sphere(Vector3.Zero, 1),
|
||||
Sphere(Vector3.Zero, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"intersect when overlapping (engulfed)" in {
|
||||
val result = Intersection.Test(
|
||||
Sphere(Vector3.Zero, 5),
|
||||
Sphere(Vector3(1,0,0), 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"intersect when overlapping (partial 1)" in {
|
||||
val result = Intersection.Test(
|
||||
Sphere(Vector3.Zero, 2),
|
||||
Sphere(Vector3(2,0,0), 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"intersect when overlapping (partial 2)" in {
|
||||
val result = Intersection.Test(
|
||||
Sphere(Vector3.Zero, 2),
|
||||
Sphere(Vector3(2.5f,0,0), 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"intersect when the circumferences are touching" in {
|
||||
val result = Intersection.Test(
|
||||
Sphere(Vector3.Zero, 2),
|
||||
Sphere(Vector3(3,0,0), 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not intersect when not touching" in {
|
||||
val result = Intersection.Test(
|
||||
Sphere(Vector3.Zero, 2),
|
||||
Sphere(Vector3(4,0,0), 1)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
}
|
||||
|
||||
"Cylinder" should {
|
||||
"detect intersection if overlapping" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 2),
|
||||
Cylinder(0, 0, 0, 1, 2)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if sides clip" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 2),
|
||||
Cylinder(0.5f, 0.5f, 0, 1, 2)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if touching" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 2),
|
||||
Cylinder(1, 0, 0, 1, 2)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if stacked" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(1, 0, 0, 1, 2),
|
||||
Cylinder(1, 0, 2, 1, 2)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if one is sunken into the other" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(1, 0, 0, 1, 2),
|
||||
Cylinder(1, 0, 1, 1, 2)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not detect intersection if not near each other" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 2),
|
||||
Cylinder(2, 2, 0, 1, 2)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"not detect intersection if one is too high / low" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(1, 0, 0, 1, 2),
|
||||
Cylinder(1, 0, 5, 1, 2)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
}
|
||||
|
||||
"Cylinder and Sphere" should {
|
||||
"detect intersection if overlapping" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(1, 0, 0, 1, 1),
|
||||
Sphere(1, 0, 2, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if cylinder top touches sphere base" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(1, 0, 2, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if cylinder base touches sphere top" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(-1, 0, -1, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if cylinder edge touches sphere edge" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(2, 0, 0.5f, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if on cylinder top rim" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(1.75f, 0, 1.25f, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection if on cylinder base rim" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(1.75f, 0, -0.5f, 1)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not detect intersection if too far above" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(0, 0, 3, 1)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"not detect intersection if too far below" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(0, 0, -3, 1)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"not detect intersection if too far out (sideways)" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(2, 2, 0, 1)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"not detect intersection if too far out (skew)" in {
|
||||
val result = Intersection.Test(
|
||||
Cylinder(0, 0, 0, 1, 1),
|
||||
Sphere(1.5f, 1.5f, 1.5f, 1)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object GeometryTest {
|
||||
|
||||
}
|
||||
object GeometryTest { }
|
||||
|
|
|
|||
Loading…
Reference in a new issue