diff --git a/game/theater/conflicttheater.py b/game/theater/conflicttheater.py index e0aa1154..6281b7d7 100644 --- a/game/theater/conflicttheater.py +++ b/game/theater/conflicttheater.py @@ -447,11 +447,11 @@ class ConflictTheater: if self.is_on_land(point): return False - for exclusion_zone in self.landmap[1]: + for exclusion_zone in self.landmap.exclusion_zones: if poly_contains(point.x, point.y, exclusion_zone): return False - for sea in self.landmap[2]: + for sea in self.landmap.sea_zones: if poly_contains(point.x, point.y, sea): return True @@ -462,14 +462,13 @@ class ConflictTheater: return True is_point_included = False - for inclusion_zone in self.landmap[0]: - if poly_contains(point.x, point.y, inclusion_zone): - is_point_included = True + if poly_contains(point.x, point.y, self.landmap.inclusion_zones): + is_point_included = True if not is_point_included: return False - for exclusion_zone in self.landmap[1]: + for exclusion_zone in self.landmap.exclusion_zones: if poly_contains(point.x, point.y, exclusion_zone): return False @@ -484,7 +483,7 @@ class ConflictTheater: nearest_points = [] if not self.landmap: raise RuntimeError("Landmap not initialized") - for inclusion_zone in self.landmap[0]: + for inclusion_zone in self.landmap.inclusion_zones: nearest_pair = ops.nearest_points(point, inclusion_zone) nearest_points.append(nearest_pair[1]) min_distance = point.distance(nearest_points[0]) # type: geometry.Point diff --git a/game/theater/landmap.py b/game/theater/landmap.py index 6e510087..89dcdcf7 100644 --- a/game/theater/landmap.py +++ b/game/theater/landmap.py @@ -1,11 +1,17 @@ +from dataclasses import dataclass import pickle -from typing import Collection, Optional, Tuple +from typing import Optional, Tuple, Union import logging from shapely import geometry +from shapely.geometry import MultiPolygon, Polygon -Zone = Collection[Tuple[float, float]] -Landmap = Tuple[Collection[geometry.Polygon], Collection[geometry.Polygon], Collection[geometry.Polygon]] + +@dataclass(frozen=True) +class Landmap: + inclusion_zones: MultiPolygon + exclusion_zones: MultiPolygon + sea_zones: MultiPolygon def load_landmap(filename: str) -> Optional[Landmap]: @@ -17,7 +23,7 @@ def load_landmap(filename: str) -> Optional[Landmap]: return None -def poly_contains(x, y, poly:geometry.Polygon): +def poly_contains(x, y, poly: Union[MultiPolygon, Polygon]): return poly.contains(geometry.Point(x, y)) diff --git a/gen/conflictgen.py b/gen/conflictgen.py index 68946ab2..796e6507 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -1,9 +1,9 @@ import logging -import random from typing import Tuple, Optional from dcs.country import Country from dcs.mapping import Point +from shapely.geometry import LineString, Point as ShapelyPoint from game.theater.conflicttheater import ConflictTheater, FrontLine from game.theater.controlpoint import ControlPoint @@ -56,7 +56,7 @@ class Conflict: """ center_position, heading = cls.frontline_position(from_cp, to_cp, theater) left_heading = heading_sum(heading, -90) - right_heading = heading_sum(heading, 90) + right_heading = heading_sum(heading, 90) left_position = cls.extend_ground_position(center_position, int(FRONTLINE_LENGTH / 2), left_heading, theater) right_position = cls.extend_ground_position(center_position, int(FRONTLINE_LENGTH / 2), right_heading, theater) distance = int(left_position.distance_to_point(right_position)) @@ -83,12 +83,21 @@ class Conflict: @classmethod def extend_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: """Finds the first intersection with an exclusion zone in one heading from an initial point up to max_distance""" - pos = initial - for distance in range(0, int(max_distance), 100): - pos = initial.point_from_heading(heading, distance) - if not theater.is_on_land(pos): - return initial.point_from_heading(heading, distance - 100) - return pos + extended = initial.point_from_heading(heading, max_distance) + p0 = ShapelyPoint(initial.x, initial.y) + p1 = ShapelyPoint(extended.x, extended.y) + line = LineString([p0, p1]) + + intersection = line.intersection( + theater.landmap.inclusion_zones.boundary) + if intersection.is_empty: + # Max extent does not intersect with the boundary of the inclusion + # zone, so the full front line is usable. This does assume that the + # front line was centered on a valid location. + return extended + + # Otherwise extend the front line only up to the intersection. + return initial.point_from_heading(heading, p0.distance(intersection)) @classmethod def find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater, coerce=True) -> Optional[Point]: diff --git a/qt_ui/widgets/map/QLiberationMap.py b/qt_ui/widgets/map/QLiberationMap.py index a06b8f44..025230de 100644 --- a/qt_ui/widgets/map/QLiberationMap.py +++ b/qt_ui/widgets/map/QLiberationMap.py @@ -945,7 +945,7 @@ class QLiberationMap(QGraphicsView): # Polygon display mode if self.game.theater.landmap is not None: - for sea_zone in self.game.theater.landmap[2]: + for sea_zone in self.game.theater.landmap.sea_zones: print(sea_zone) poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in sea_zone.exterior.coords]) if self.reference_point_setup_mode: @@ -954,14 +954,14 @@ class QLiberationMap(QGraphicsView): color = "sea_blue" scene.addPolygon(poly, CONST.COLORS[color], CONST.COLORS[color]) - for inclusion_zone in self.game.theater.landmap[0]: + for inclusion_zone in self.game.theater.landmap.inclusion_zones: poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in inclusion_zone.exterior.coords]) if self.reference_point_setup_mode: scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_grey_transparent"]) else: scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"]) - for exclusion_zone in self.game.theater.landmap[1]: + for exclusion_zone in self.game.theater.landmap.exclusion_zones: poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in exclusion_zone.exterior.coords]) if self.reference_point_setup_mode: scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_dark_grey_transparent"]) diff --git a/resources/caulandmap.p b/resources/caulandmap.p index c9f77f43..7ceefc71 100644 Binary files a/resources/caulandmap.p and b/resources/caulandmap.p differ diff --git a/resources/channellandmap.p b/resources/channellandmap.p index 9f5ba1f8..6ee32087 100644 Binary files a/resources/channellandmap.p and b/resources/channellandmap.p differ diff --git a/resources/gulflandmap.p b/resources/gulflandmap.p index 3e2e566e..a521f5e5 100644 Binary files a/resources/gulflandmap.p and b/resources/gulflandmap.p differ diff --git a/resources/nevlandmap.p b/resources/nevlandmap.p index 1b92631a..42e34c37 100644 Binary files a/resources/nevlandmap.p and b/resources/nevlandmap.p differ diff --git a/resources/normandylandmap.p b/resources/normandylandmap.p index 808a2a1e..2d7ade8e 100644 Binary files a/resources/normandylandmap.p and b/resources/normandylandmap.p differ diff --git a/resources/syrialandmap.p b/resources/syrialandmap.p index 1b5cc168..0d11a3c2 100644 Binary files a/resources/syrialandmap.p and b/resources/syrialandmap.p differ diff --git a/resources/tools/generate_landmap.py b/resources/tools/generate_landmap.py index 1812ac4b..7e1a9f4b 100644 --- a/resources/tools/generate_landmap.py +++ b/resources/tools/generate_landmap.py @@ -2,6 +2,9 @@ import pickle from dcs.mission import Mission from shapely import geometry +from shapely.geometry import MultiPolygon + +from game.theater.landmap import Landmap for terrain in ["cau", "nev", "syria", "channel", "normandy", "gulf"]: print("Terrain " + terrain) @@ -29,4 +32,6 @@ for terrain in ["cau", "nev", "syria", "channel", "normandy", "gulf"]: with open("../{}landmap.p".format(terrain), "wb") as f: print(len(inclusion_zones), len(exclusion_zones), len(seas_zones)) - pickle.dump((inclusion_zones, exclusion_zones, seas_zones), f) + pickle.dump(Landmap(MultiPolygon(inclusion_zones), + MultiPolygon(exclusion_zones), + MultiPolygon(seas_zones)), f)