mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
line and line segment intersection code and tests
This commit is contained in:
parent
f557ecc13d
commit
4304ea7f4d
|
|
@ -1433,7 +1433,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
deadState = DeadState.RespawnTime
|
||||
|
||||
session = session.copy(player = new Player(avatar))
|
||||
//xy-coordinates indicate sanctuary spawn bias:
|
||||
//ay-coordinates indicate sanctuary spawn bias:
|
||||
player.Position = math.abs(scala.util.Random.nextInt() % avatar.name.hashCode % 4) match {
|
||||
case 0 => Vector3(8192, 8192, 0) //NE
|
||||
case 1 => Vector3(8192, 0, 0) //SE
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class ExplosiveDeployableControl(mine: ExplosiveDeployable) extends Actor with D
|
|||
case _ => false
|
||||
}
|
||||
} =>
|
||||
// the mine damages itself, which sets it off, which causes an explosion
|
||||
// the trigger damages the mine, which sets it off, which causes an explosion
|
||||
// think of this as an initiator to the proper explosion
|
||||
mine.Destroyed = true
|
||||
ExplosiveDeployableControl.DamageResolution(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
79
src/main/scala/net/psforever/objects/geometry/Geometry.scala
Normal file
79
src/main/scala/net/psforever/objects/geometry/Geometry.scala
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.geometry
|
||||
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
trait Slope {
|
||||
def d: Vector3
|
||||
|
||||
def length: Float
|
||||
}
|
||||
|
||||
trait Line extends Slope {
|
||||
assert({
|
||||
val mag = Vector3.Magnitude(d)
|
||||
mag - 0.05f < 1f && mag + 0.05f > 1f
|
||||
}, "not a unit vector")
|
||||
|
||||
def length: Float = Float.PositiveInfinity
|
||||
}
|
||||
|
||||
trait Segment extends Slope {
|
||||
def length: Float = Vector3.Magnitude(d)
|
||||
}
|
||||
|
||||
final case class Line2D(x: Float, y: Float, d: Vector3) extends Line
|
||||
|
||||
object Line2D {
|
||||
def apply(ax: Float, ay: Float, bx: Float, by: Float): Line2D = {
|
||||
Line2D(ax, ay, Vector3.Unit(Vector3(bx-ax, by-ay, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
final case class Segment2D(ax: Float, ay: Float, bx: Float, by: Float) extends Segment {
|
||||
def d: Vector3 = Vector3(bx - ax, by - ay, 0)
|
||||
}
|
||||
|
||||
object Segment2D {
|
||||
def apply(x: Float, y: Float, z: Float, d: Vector3): Segment2D = {
|
||||
Segment2D(x, y, x + d.x, y + d.y)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
def d: Vector3 = Vector3(bx - ax, by - ay, bz - az)
|
||||
}
|
||||
|
||||
object Segment3D {
|
||||
def apply(x: Float, y: Float, z: Float, d: Vector3): Segment3D = {
|
||||
Segment3D(x, y, z, z+d.x, y+d.y, z+d.z)
|
||||
}
|
||||
}
|
||||
|
||||
object Geometry {
|
||||
def equalFloats(value1: Float, value2: Float, off: Float = 0.001f): Boolean = {
|
||||
val diff = value1 - value2
|
||||
(diff >= 0 && diff <= off) || diff > -off
|
||||
}
|
||||
|
||||
def equalVectors(value1: Vector3, value2: Vector3, off: Float = 0.001f): Boolean = {
|
||||
equalFloats(value1.x, value2.x, off) &&
|
||||
equalFloats(value1.y, value2.y, off) &&
|
||||
equalFloats(value1.z, value2.z, off)
|
||||
}
|
||||
|
||||
def closeToInsignificance(d: Float, epsilon: Float = 10f): Float = {
|
||||
val ulp = math.ulp(epsilon)
|
||||
math.signum(d) match {
|
||||
case -1f =>
|
||||
val n = math.abs(d)
|
||||
val p = math.abs(n - n.toInt)
|
||||
if (p < ulp || d > ulp) d + p else d
|
||||
case _ =>
|
||||
val p = math.abs(d - d.toInt)
|
||||
if (p < ulp || d < ulp) d - p else d
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/main/scala/net/psforever/objects/geometry/Intersection.scala
Normal file
107
src/main/scala/net/psforever/objects/geometry/Intersection.scala
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) 2021 PSForever
|
||||
package net.psforever.objects.geometry
|
||||
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object Intersection {
|
||||
object Test {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
def apply(origin1 : Vector3, origin2 : Vector3, 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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
object PointTripleOrientation extends Enumeration {
|
||||
val Colinear, Clockwise, Counterclockwise = Value
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the orientation of the given three two-dimensional points.
|
||||
* Any triple has one of three orientations:
|
||||
* clockwise - the third point is to the right side of a line plotted by the first two points;
|
||||
* counterclockwise - the third point is to the left side of a line plotted by the first two points;
|
||||
* and, colinear - the third point is reachable along the line plotted by the first two points.
|
||||
* @param ax x-coordinate of the first point
|
||||
* @param ay y-coordinate of the first point
|
||||
* @param px x-coordinate of the second point
|
||||
* @param py y-coordinate of the second point
|
||||
* @param bx x-coordinate of the third point
|
||||
* @param by y-coordinate of the third point
|
||||
* @return the orientation value
|
||||
*/
|
||||
private def orientationOfPoints(
|
||||
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
|
||||
else if (out > 0) PointTripleOrientation.Clockwise
|
||||
else PointTripleOrientation.Counterclockwise
|
||||
}
|
||||
|
||||
/**
|
||||
* Do these two line segments intersect?
|
||||
* Intersection of two two-dimensional line segments can be determined by the orientation of their endpoints.
|
||||
* 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 = {
|
||||
//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 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)
|
||||
val ln2_ln1b = orientationOfPoints(ln2ax, ln2ay, ln2bx, ln2by, ln1bx, ln1by)
|
||||
//results
|
||||
import PointTripleOrientation._
|
||||
(ln1_ln2a != ln1_ln2b && ln2_ln1a != ln2_ln1b) ||
|
||||
(ln1_ln2a == Colinear && pointOnSegment(ln1ax, ln1ay, ln2ax, ln2ay, ln1bx, ln1by)) || // line2 A is on line1
|
||||
(ln1_ln2b == Colinear && pointOnSegment(ln1ax, ln1ay, ln2bx, ln2by, ln1bx, ln1by)) || // line2 B is on line1
|
||||
(ln2_ln1a == Colinear && pointOnSegment(ln2ax, ln2ay, ln1ax, ln1ay, ln2bx, ln2by)) || // line1 A is on line2
|
||||
(ln2_ln1b == Colinear && pointOnSegment(ln2ax, ln2ay, ln1bx, ln1by, ln2bx, ln2by)) // line1 B is on line2
|
||||
}
|
||||
|
||||
/**
|
||||
* Do these two lines intersect?
|
||||
* 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(origin1 : Vector3, origin2 : Vector3, line1 : Line3D, line2 : Line3D, threshold: Float): Boolean = {
|
||||
ClosestDistance.Between(origin1, origin2, line1, line2) < threshold
|
||||
}
|
||||
|
||||
/**
|
||||
* Do these two line segments intersect?
|
||||
* 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(origin1 : Vector3, origin2 : Vector3, seg1 : Segment3D, seg2 : Segment3D, threshold: Float): Boolean = {
|
||||
ClosestDistance.Between(origin1, origin2, seg1, seg2) < threshold
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,6 +117,14 @@ object Vector3 {
|
|||
*/
|
||||
def z(value: Float): Vector3 = Vector3(0, 0, value)
|
||||
|
||||
/**
|
||||
* Calculate the negation of this vector,
|
||||
* the same vector in the antiparallel direction.
|
||||
* @param v the original vector
|
||||
* @return the negation of the original vector
|
||||
*/
|
||||
def neg(v: Vector3): Vector3 = Vector3(-v.x, -v.y, -v.z)
|
||||
|
||||
/**
|
||||
* Calculate the actual distance between two points.
|
||||
* @param pos1 the first point
|
||||
|
|
|
|||
194
src/test/scala/objects/GeometryTest.scala
Normal file
194
src/test/scala/objects/GeometryTest.scala
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package objects
|
||||
|
||||
import net.psforever.objects.geometry.{Intersection, Line3D, Segment3D}
|
||||
import net.psforever.types.Vector3
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
class IntersectionTest extends Specification {
|
||||
"Line3D" should {
|
||||
"detect intersection on target point(s)" in {
|
||||
//these lines intersect at (0, 0, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Line3D(0,0,0, Vector3(1,0,0)),
|
||||
Line3D(0,0,0, Vector3(0,1,0))
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"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,
|
||||
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 {
|
||||
//these lines intersect at (0.5f, 0.5f, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
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 {
|
||||
//these lines intersect at (0, 0.5, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
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,
|
||||
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 {
|
||||
//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,
|
||||
Line3D(0,0,0, Vector3.Unit(Vector3(2,0,0))),
|
||||
Line3D(1,0,0, Vector3.Unit(Vector3(3,0,0)))
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not detect intersection (generic skew)" in {
|
||||
//these segments will not intersect
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Segment3D(-3,-8,7, Vector3.Unit(Vector3(-3,-9,8))),
|
||||
Segment3D(6,3,0, Vector3.Unit(Vector3(2,0,0)))
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
}
|
||||
|
||||
"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,
|
||||
Segment3D(0,0,0, 1,0,0),
|
||||
Segment3D(0,0,0, 0,1,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"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,
|
||||
Segment3D(0,0,0, 0,2,0),
|
||||
Segment3D(-1,0,0, 1,0,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection on the farther point(s)" in {
|
||||
//these segments intersect at (0, 1, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Segment3D(0,0,1, 0,1,0),
|
||||
Segment3D(1,0,0, 0,1,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"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,
|
||||
Segment3D(1,0,0, 1,1,0),
|
||||
Segment3D(2,0,0, 0,2,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection in the middle(s)" in {
|
||||
//these segments intersect at (0.5f, 0.5f, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Segment3D(0,0,0, 1,1,0),
|
||||
Segment3D(1,0,0, 0,1,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"detect intersection in the middle " in {
|
||||
//these segments intersect at (0, 0.5, 0)
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Segment3D(0,0,0, 1,0,0),
|
||||
Segment3D(0.5f,1,0, 0.5f,-1,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"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,
|
||||
Segment3D(1,1,0, 2,2,0),
|
||||
Segment3D(1,0,0, 2,0,0)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"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,
|
||||
Segment3D(0,0,0, 1,1,0),
|
||||
Segment3D(2,0,0, 2,1,0)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"not detect intersection if the line segments are parallel" in {
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Segment3D(0,0,0, 1,1,1),
|
||||
Segment3D(1,1,2, 2,2,3)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
|
||||
"detect 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,
|
||||
Segment3D(0,0,0, 2,0,0),
|
||||
Segment3D(1,0,0, 3,0,0)
|
||||
)
|
||||
result mustEqual true
|
||||
}
|
||||
|
||||
"not detect intersection (generic skew)" in {
|
||||
//these segments will not intersect
|
||||
val result = Intersection.Test(Vector3.Zero, Vector3.Zero,
|
||||
Segment3D(-3,-8,7, -3,-9,8),
|
||||
Segment3D(6,3,0, 2,0,0)
|
||||
)
|
||||
result mustEqual false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object GeometryTest {
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue