ai.platon.pulsar.common.math.geometric.Geometrics.kt Maven / Gradle / Ivy
package ai.platon.pulsar.common.math.geometric
import java.awt.Dimension
import java.awt.Point
import java.awt.Rectangle
import kotlin.math.abs
data class GeoIntPoint(var x: Int, var y: Int): Comparable {
constructor(point: Point) : this(point.x, point.y)
fun toPoint(): Point {
return Point(x, y)
}
fun align(gridWidth: Int = 0, gridHeight: Int = 0): GeoIntPoint {
val x2 = if (gridWidth == 0) x else Math.round(x.toFloat() / gridWidth) * gridWidth
val y2 = if (gridHeight == 0) y else Math.round(y.toFloat() / gridHeight) * gridHeight
return GeoIntPoint(x2, y2)
}
fun align(grid: Dimension): GeoIntPoint {
return align(grid.width, grid.height)
}
override fun compareTo(other: GeoIntPoint): Int {
val r = x - other.x
return if (r == 0) y - other.y else r
}
}
operator fun Point.component1(): Int {
return x
}
operator fun Point.component2(): Int {
return y
}
fun Point.toIntPoint(): GeoIntPoint {
return GeoIntPoint(x, y)
}
val Point.str
get() = "[$x $y]"
val Rectangle.x2: Int
get() {
return x + width
}
val Rectangle.y2: Int
get() {
return y + height
}
operator fun Rectangle.component1(): Int {
return x
}
operator fun Rectangle.component2(): Int {
return y
}
operator fun Rectangle.component3(): Int {
return width
}
operator fun Rectangle.component4(): Int {
return height
}
fun Rectangle.sim(rect: Rectangle): Double {
val overlap = this.intersection(rect)
if (overlap.isEmpty) return 0.0
val a = overlap.area
val b = area.coerceAtLeast(rect.area)
return 1.0 * a / b
}
val Rectangle.area get() = if (isEmpty) 0 else width * height
val Rectangle.str get() = "[$x $y $width $height]"
val Rectangle.str2 get() = "{x:$x y:$y w:$width h:$height}"
/**
* The align type to define linearity
* */
enum class AlignType {
NONE, LEFT, V_CENTER, RIGHT, TOP, H_CENTER, BOTTOM;
val isNone: Boolean
get() = this != NONE
val isVertical: Boolean
get() = this in arrayOf(LEFT, V_CENTER, RIGHT)
val isHorizontal: Boolean
get() = this in arrayOf(TOP, H_CENTER, BOTTOM)
}
/**
* Two rectangles are vertical collinear if they are intersects and have a padding, ignore the height
* TODO: not fully tested
* */
fun Rectangle.testAlignment(r: Rectangle, bias: Double = 0.2): AlignType {
val b = bias.coerceAtMost(1.0)
return when {
abs(x - r.x) <= b * width -> AlignType.LEFT
abs(centerX - r.centerX) <= b * width -> AlignType.V_CENTER
abs(x2 - r.x2) <= b * width -> AlignType.RIGHT
abs(y - r.y) <= b * height -> AlignType.TOP
abs(centerY - r.centerY) <= b * height -> AlignType.H_CENTER
abs(y2 - r.y2) <= b * height -> AlignType.BOTTOM
else -> AlignType.NONE
}
}
data class PointI(
var x: Int,
var y: Int
)
data class DimI(
var width: Int,
var height: Int
)
data class RectI(
var x: Int,
var y: Int,
var with: Int,
var height: Int
)
data class OffsetI(
var x: Int,
var y: Int,
)
data class PointD(
var x: Double,
var y: Double
)
data class DimD(
var width: Double,
var height: Double
)
data class RectD(
var x: Double,
var y: Double,
var width: Double,
var height: Double
)
data class OffsetD(
var x: Double,
var y: Double,
)
object GeometricUtils {
fun findMaxRectangle(rectangles: List): Rectangle? {
// 提取所有 x 和 y 坐标边界
val xCoords = mutableSetOf()
val yCoords = mutableSetOf()
// 遍历矩形,收集 x 和 y 的边界
for (rect in rectangles) {
xCoords.add(rect.x)
xCoords.add(rect.x2)
yCoords.add(rect.y)
yCoords.add(rect.y2)
}
// 排序 x 和 y 的边界
val xSorted = xCoords.sorted()
val ySorted = yCoords.sorted()
var maxArea = 0
var maxRect: Rectangle? = null
// 检查每个由 (x1, x2) 和 (y1, y2) 构成的网格区域
for (i in 0 until xSorted.size - 1) {
for (j in 0 until ySorted.size - 1) {
val x1 = xSorted[i]
val x2 = xSorted[i + 1]
val y1 = ySorted[j]
val y2 = ySorted[j + 1]
// 如果该区域没有与任何矩形相交,计算其面积
if (isEmpty(x1, x2, y1, y2, rectangles)) {
val area = (x2 - x1) * (y2 - y1)
if (area > maxArea) {
maxArea = area
maxRect = Rectangle(x1, x2, y1, y2)
}
}
}
}
return maxRect
}
// 检查区域 (x1, x2, y1, y2) 是否与任何矩形相交
fun isEmpty(x1: Int, x2: Int, y1: Int, y2: Int, rectangles: List): Boolean {
for (rect in rectangles) {
// 如果有相交的矩形,返回 false
if (!(x2 <= rect.x || x1 >= rect.x2 || y2 <= rect.y || y1 >= rect.y2)) {
return false
}
}
return true
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy