mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Use shapely projection instead of brute force.
Converts the landmap to use MultiPolygon instead of a collection of polygons, since Shapely has explicit support for this. Because we've done that, we can use a single projection from a line instead of brute forcing the extent of the front line. This makes turn processing ~66% faster (3 seconds to 1.8). There are probably other places this should be used.
This commit is contained in:
parent
2a65916f7c
commit
b9138acbc8
@ -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
|
||||
|
||||
@ -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))
|
||||
|
||||
|
||||
|
||||
@ -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]:
|
||||
|
||||
@ -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"])
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user