commonMain.korlibs.math.geom.MLine.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of korma Show documentation
Show all versions of korma Show documentation
Mathematic library for Multiplatform Kotlin 1.3
package korlibs.math.geom
import korlibs.math.almostEquals
import korlibs.math.annotations.*
import korlibs.memory.*
import korlibs.memory.isAlmostZero
@KormaMutableApi
@Deprecated("Use Line instead")
data class MLine(var a: Point, var b: Point) {
fun clone(): MLine = MLine(a, b)
fun flipped(): MLine = MLine(b, a)
val minX: Double get() = kotlin.math.min(a.xD, b.xD)
val maxX: Double get() = kotlin.math.max(a.xD, b.xD)
val minY: Double get() = kotlin.math.min(a.yD, b.yD)
val maxY: Double get() = kotlin.math.max(a.yD, b.yD)
fun round(): MLine {
a.round()
b.round()
return this
}
fun setTo(a: Point, b: Point): MLine = setTo(a.xD, a.yD, b.xD, b.yD)
fun setTo(a: MPoint, b: MPoint): MLine = setTo(a.x, a.y, b.x, b.y)
fun setTo(x0: Double, y0: Double, x1: Double, y1: Double): MLine {
a = Point(x0, y0)
b = Point(x1, y1)
return this
}
fun setToPolar(x: Double, y: Double, angle: Angle, length: Double = 1.0): MLine {
setTo(x, y, x + angle.cosineD * length, y + angle.sineD * length)
return this
}
fun directionVector(out: MPoint = MPoint()): MPoint {
out.setTo(dx, dy)
return out
}
fun getMinimumDistance(p: Point): Double {
val v = a
val w = b
val l2 = Point.distanceSquared(v, w)
if (l2 == 0.0f) return Point.distanceSquared(p, a).toDouble()
val t = (Point.dot(p - v, w - v) / l2).clamp(0.0f, 1.0f).toDouble()
return Point.distance(p, v + (w - v) * t).toDouble()
}
@KormaExperimental
fun scalePoints(scale: Double): MLine {
a -= delta * scale
b -= delta * scale
//val p1 = getIntersectXY(rect.topLeft, rect.topRight, this.a, this.b)
//val p2 = getIntersectXY(rect.bottomLeft, rect.bottomRight, this.a, this.b)
//val p3 = getIntersectXY(rect.topLeft, rect.bottomLeft, this.a, this.b)
//val p4 = getIntersectXY(rect.topRight, rect.bottomRight, this.a, this.b)
//if (p1 != null) {
// if (Line(this.a, p1!!.mutable).angle.isAlmostEquals(this.angle)) {
// this.setTo(this.a, p1!!.mutable)
// }
// if (Line(p1!!.mutable, this.b).angle.isAlmostEquals(this.angle)) {
// this.setTo(p1!!.mutable, this.b)
// }
//}
//println("p1=$p1, p2=$p2, p3=$p3, p4=$p4")
return this
}
constructor() : this(Point(), Point())
constructor(p0: MPoint, p1: MPoint) : this(p0.point, p1.point)
constructor(x0: Double, y0: Double, x1: Double, y1: Double) : this(MPoint(x0, y0), MPoint(x1, y1))
constructor(x0: Float, y0: Float, x1: Float, y1: Float) : this(MPoint(x0, y0), MPoint(x1, y1))
constructor(x0: Int, y0: Int, x1: Int, y1: Int) : this(MPoint(x0, y0), MPoint(x1, y1))
val x0: Double get() = a.xD
val y0: Double get() = a.yD
val x1: Double get() = b.xD
val y1: Double get() = b.yD
val delta: Point get() = b - a
val dx: Double get() = x1 - x0
val dy: Double get() = y1 - y0
fun containsX(x: Double): Boolean = (x in x0..x1) || (x in x1..x0) || (almostEquals(x, x0)) || (almostEquals(x, x1))
fun containsY(y: Double): Boolean = (y in y0..y1) || (y in y1..y0) || (almostEquals(y, y0)) || (almostEquals(y, y1))
fun containsBoundsXY(x: Double, y: Double): Boolean = containsX(x) && containsY(y)
val angle: Angle get() = Angle.between(a, b)
val length: Double get() = Point.distance(a, b).toDouble()
val lengthSquared: Double get() = Point.distanceSquared(a, b).toDouble()
override fun toString(): String = "Line($a, $b)"
fun getLineIntersectionPoint(line: MLine): Point? {
return getIntersectXY(x0, y0, x1, y1, line.x0, line.y0, line.x1, line.y1)
}
fun getIntersectionPoint(line: MLine): Point? = getSegmentIntersectionPoint(line)
fun getSegmentIntersectionPoint(line: MLine): Point? {
val out = getIntersectXY(x0, y0, x1, y1, line.x0, line.y0, line.x1, line.y1)
if (out != null) {
if (this.containsBoundsXY(out.xD, out.yD) && line.containsBoundsXY(out.xD, out.yD)) {
return out
}
}
return null
}
fun intersectsLine(line: MLine): Boolean = getLineIntersectionPoint(line) != null
fun intersects(line: MLine): Boolean = intersectsSegment(line)
fun intersectsSegment(line: MLine): Boolean {
return getSegmentIntersectionPoint(line) != null
//val line1 = this
//val line2 = line
//val o1 = Point.orientation(line1.a, line1.b, line2.a).sign
//val o2 = Point.orientation(line1.a, line1.b, line2.b).sign
//val o3 = Point.orientation(line2.a, line2.b, line1.a).sign
//val o4 = Point.orientation(line2.a, line2.b, line1.b).sign
//return when {
// o1 != o2 && o3 != o4 -> true
// o1.isAlmostZero() && onSegment(line1.a, line2.a, line1.b) -> true
// o2.isAlmostZero() && onSegment(line1.a, line2.b, line1.b) -> true
// o3.isAlmostZero() && onSegment(line2.a, line1.a, line2.b) -> true
// o4.isAlmostZero() && onSegment(line2.a, line1.b, line2.b) -> true
// else -> false
//}
}
//private fun onSegment(p: Point, q: Point, r: Point): Boolean =
// ((q.x <= max(p.x, r.x)) && (q.x >= min(p.x, r.x)) &&
// (q.y <= max(p.y, r.y)) && (q.y >= min(p.y, r.y)))
companion object {
fun fromPointAndDirection(point: Point, direction: Point, scale: Double = 1.0, out: MLine = MLine()): MLine =
out.setTo(point.xD, point.yD, point.x + direction.x * scale, point.y + direction.y * scale)
fun fromPointAngle(point: Point, angle: Angle, length: Double = 1.0, out: MLine = MLine()): MLine = out.setToPolar(point.xD, point.yD, angle, length)
fun fromPointAndDirection(point: MPoint, direction: MPoint, scale: Double = 1.0, out: MLine = MLine()): MLine = out.setTo(point.x, point.y, point.x + direction.x * scale, point.y + direction.y * scale)
fun fromPointAngle(point: MPoint, angle: Angle, length: Double = 1.0, out: MLine = MLine()): MLine = out.setToPolar(point.x, point.y, angle, length)
fun length(Ax: Double, Ay: Double, Bx: Double, By: Double): Double = kotlin.math.hypot(Bx - Ax, By - Ay)
fun getIntersectXY(Ax: Double, Ay: Double, Bx: Double, By: Double, Cx: Double, Cy: Double, Dx: Double, Dy: Double): Point? {
val a1 = By - Ay
val b1 = Ax - Bx
val c1 = a1 * (Ax) + b1 * (Ay)
val a2 = Dy - Cy
val b2 = Cx - Dx
val c2 = a2 * (Cx) + b2 * (Cy)
val determinant = a1 * b2 - a2 * b1
if (determinant.isAlmostZero()) return null
val x = (b2 * c1 - b1 * c2) / determinant
val y = (a1 * c2 - a2 * c1) / determinant
//if (!x.isFinite() || !y.isFinite()) TODO()
return Point(x, y)
}
fun getIntersectXY(a: Point, b: Point, c: Point, d: Point): Point? =
getIntersectXY(a.xD, a.yD, b.xD, b.yD, c.xD, c.yD, d.xD, d.yD)
}
}
data class LineIntersection(
val line: MLine = MLine(),
var intersection: Point = Point()
) {
val normalVector: MLine = MLine()
fun setFrom(x0: Double, y0: Double, x1: Double, y1: Double, ix: Double, iy: Double, normalLength: Double) {
line.setTo(x0, y0, x1, y1)
intersection = Point(ix, iy)
normalVector.setToPolar(ix, iy, line.angle - 90.degrees, normalLength)
}
override fun toString(): String = "LineIntersection($line, intersection=$intersection)"
}
fun MLine.Companion.projectedPoint(
v1x: Double,
v1y: Double,
v2x: Double,
v2y: Double,
px: Double,
py: Double,
): Point {
// return this.getIntersectionPoint(Line(point, Point.fromPolar(point, this.angle + 90.degrees)))!!
// get dot product of e1, e2
val e1x = v2x - v1x
val e1y = v2y - v1y
val e2x = px - v1x
val e2y = py - v1y
val valDp = MPoint.dot(e1x, e1y, e2x, e2y)
// get length of vectors
val lenLineE1 = kotlin.math.hypot(e1x, e1y)
val lenLineE2 = kotlin.math.hypot(e2x, e2y)
// What happens if lenLineE1 or lenLineE2 are zero?, it would be a division by zero.
// Does that mean that the point is on the line, and we should use it?
if (lenLineE1 == 0.0 || lenLineE2 == 0.0) {
return Point(px, py)
}
val cos = valDp / (lenLineE1 * lenLineE2)
// length of v1P'
val projLenOfLine = cos * lenLineE2
return Point((v1x + (projLenOfLine * e1x) / lenLineE1), (v1y + (projLenOfLine * e1y) / lenLineE1))
}
fun MLine.Companion.projectedPoint(v1: Point, v2: Point, point: Point): Point = projectedPoint(v1.xD, v1.yD, v2.xD, v2.yD, point.xD, point.yD)
fun MLine.Companion.lineIntersectionPoint(l1: MLine, l2: MLine): Point? = l1.getLineIntersectionPoint(l2)
fun MLine.Companion.segmentIntersectionPoint(
l1: MLine,
l2: MLine,
): Point? = l1.getSegmentIntersectionPoint(l2)
// @TODO: Should we create a common interface make projectedPoint part of it? (for ecample to project other kind of shapes)
// https://math.stackexchange.com/questions/62633/orthogonal-projection-of-a-point-onto-a-line
// http://www.sunshine2k.de/coding/java/PointOnLine/PointOnLine.html
fun MLine.projectedPoint(point: Point): Point = MLine.projectedPoint(a, b, point)
© 2015 - 2025 Weber Informatics LLC | Privacy Policy