Add TileShape and Simplex convex-shape core

Ports geometry/planar/TileShape.java (the border-line-based containment,
area, and centre-of-gravity logic) and Simplex.java (a convex region as
the intersection of directed half-planes). Corners are exact
intersections of consecutive border lines; point containment uses exact
side_of. The remove_redundant_lines normalization — dropping lines that
do not contribute and detecting emptiness — is ported line-for-line.

Supporting additions: Line.compare_to/__lt__ (angular sort order),
Line.fast_equals, Line.side_of_intersection, Line.translate (perpendicular
offset), IntDirection.determinant, and IntBox.to_simplex.

offset is approximate (rounded translated lines, as upstream); enlarge
clips to the enlarged bounding box pending the IntOctagon port.

Since there is no JVM oracle, tests assert invariants: corners lie
exactly on their border lines (exact side_of == 0), IntBox -> Simplex
preserves the region over a sampled grid, intersection is contained in
both operands and a point is in the result iff in both, and get_instance
normalization drops redundant lines and detects empty half-plane pairs.
This commit is contained in:
2026-07-11 18:36:20 -06:00
parent d4aa4c729d
commit 1599d181b5
7 changed files with 790 additions and 2 deletions
+20
View File
@@ -230,6 +230,26 @@ class IntBox:
return [FloatPoint(c.x, c.y) for c in self.corners()]
def to_simplex(self):
"""Return this box as a :class:`~freeroute.geometry.simplex.Simplex`.
Four directed border lines whose common right side is the box interior
(ports ``IntBox.to_Simplex``).
"""
from .direction import IntDirection
from .line import Line
from .simplex import Simplex
if self.is_empty():
return Simplex(())
lines = (
Line.from_point_direction(self.ll, IntDirection(1, 0)), # bottom, +x
Line.from_point_direction(self.ur, IntDirection(0, 1)), # right, +y
Line.from_point_direction(self.ur, IntDirection(-1, 0)), # top, -x
Line.from_point_direction(self.ll, IntDirection(0, -1)), # left, -y
)
return Simplex(lines)
@staticmethod
def empty() -> IntBox:
return IntBox(CRIT_INT, CRIT_INT, -CRIT_INT, -CRIT_INT)