diff --git a/game/flightplan/joinzonegeometry.py b/game/flightplan/joinzonegeometry.py index 48cff780..02e00fa4 100644 --- a/game/flightplan/joinzonegeometry.py +++ b/game/flightplan/joinzonegeometry.py @@ -11,7 +11,6 @@ from shapely.geometry import ( MultiLineString, ) -from game.theater import ConflictTheater from game.utils import nautical_miles if TYPE_CHECKING: @@ -26,12 +25,7 @@ class JoinZoneGeometry: """ def __init__( - self, - target: Point, - home: Point, - ip: Point, - coalition: Coalition, - theater: ConflictTheater, + self, target: Point, home: Point, ip: Point, coalition: Coalition ) -> None: # Normal join placement is based on the path from home to the IP. If no path is # found it means that the target is on a direct path. In that case we instead @@ -82,14 +76,28 @@ class JoinZoneGeometry: ] ) - permissible_lines = ip_direction_limit_wedge.intersection( + permissible_zones = ip_direction_limit_wedge.difference( + self.excluded_zones + ).difference(self.home_bubble) + if permissible_zones.is_empty: + permissible_zones = MultiPolygon([]) + if not isinstance(permissible_zones, MultiPolygon): + permissible_zones = MultiPolygon([permissible_zones]) + self.permissible_zones = permissible_zones + + preferred_lines = ip_direction_limit_wedge.intersection( self.excluded_zones.boundary ).difference(self.home_bubble) - if not isinstance(permissible_lines, MultiLineString): - permissible_lines = MultiLineString([permissible_lines]) - self.permissible_lines = permissible_lines + if preferred_lines.is_empty: + preferred_lines = MultiLineString([]) + if not isinstance(preferred_lines, MultiLineString): + preferred_lines = MultiLineString([preferred_lines]) + self.preferred_lines = preferred_lines def find_best_join_point(self) -> Point: - join, _ = shapely.ops.nearest_points(self.permissible_lines, self.home) + if self.preferred_lines.is_empty: + join, _ = shapely.ops.nearest_points(self.permissible_zones, self.ip) + else: + join, _ = shapely.ops.nearest_points(self.preferred_lines, self.home) return Point(join.x, join.y) diff --git a/gen/flights/flightplan.py b/gen/flights/flightplan.py index 6c25babd..fde43ee9 100644 --- a/gen/flights/flightplan.py +++ b/gen/flights/flightplan.py @@ -975,7 +975,6 @@ class FlightPlanBuilder: package_airfield.position, ingress_point, self.coalition, - self.theater, ).find_best_join_point() # And the split point based on the best route from the IP. Since that's no diff --git a/qt_ui/widgets/map/mapmodel.py b/qt_ui/widgets/map/mapmodel.py index e8a75298..fce7a5d9 100644 --- a/qt_ui/widgets/map/mapmodel.py +++ b/qt_ui/widgets/map/mapmodel.py @@ -869,7 +869,8 @@ class JoinZonesJs(QObject): targetBubbleChanged = Signal() ipBubbleChanged = Signal() excludedZonesChanged = Signal() - permissibleLinesChanged = Signal() + permissibleZonesChanged = Signal() + preferredLinesChanged = Signal() def __init__( self, @@ -877,14 +878,16 @@ class JoinZonesJs(QObject): target_bubble: LeafletPoly, ip_bubble: LeafletPoly, excluded_zones: list[LeafletPoly], - permissible_lines: list[list[LeafletLatLon]], + permissible_zones: list[LeafletPoly], + preferred_lines: list[list[LeafletLatLon]], ) -> None: super().__init__() self._home_bubble = home_bubble self._target_bubble = target_bubble self._ip_bubble = ip_bubble self._excluded_zones = excluded_zones - self._permissible_lines = permissible_lines + self._permissible_zones = permissible_zones + self._preferred_lines = preferred_lines @Property(list, notify=homeBubbleChanged) def homeBubble(self) -> LeafletPoly: @@ -902,13 +905,17 @@ class JoinZonesJs(QObject): def excludedZones(self) -> list[LeafletPoly]: return self._excluded_zones - @Property(list, notify=permissibleLinesChanged) - def permissibleLines(self) -> list[list[LeafletLatLon]]: - return self._permissible_lines + @Property(list, notify=permissibleZonesChanged) + def permissibleZones(self) -> list[LeafletPoly]: + return self._permissible_zones + + @Property(list, notify=preferredLinesChanged) + def preferredLines(self) -> list[list[LeafletLatLon]]: + return self._preferred_lines @classmethod def empty(cls) -> JoinZonesJs: - return JoinZonesJs([], [], [], [], []) + return JoinZonesJs([], [], [], [], [], []) @classmethod def for_flight(cls, flight: Flight, game: Game) -> JoinZonesJs: @@ -919,15 +926,14 @@ class JoinZonesJs(QObject): if flight.package.waypoints is None: return JoinZonesJs.empty() ip = flight.package.waypoints.ingress - geometry = JoinZoneGeometry( - target.position, home.position, ip, game.blue, game.theater - ) + geometry = JoinZoneGeometry(target.position, home.position, ip, game.blue) return JoinZonesJs( shapely_poly_to_leaflet_points(geometry.home_bubble, game.theater), shapely_poly_to_leaflet_points(geometry.target_bubble, game.theater), shapely_poly_to_leaflet_points(geometry.ip_bubble, game.theater), shapely_to_leaflet_polys(geometry.excluded_zones, game.theater), - shapely_lines_to_leaflet_points(geometry.permissible_lines, game.theater), + shapely_to_leaflet_polys(geometry.permissible_zones, game.theater), + shapely_lines_to_leaflet_points(geometry.preferred_lines, game.theater), ) @@ -937,7 +943,7 @@ class HoldZonesJs(QObject): joinBubbleChanged = Signal() excludedZonesChanged = Signal() permissibleZonesChanged = Signal() - permissibleLinesChanged = Signal() + preferredLinesChanged = Signal() def __init__( self, @@ -946,7 +952,7 @@ class HoldZonesJs(QObject): join_bubble: LeafletPoly, excluded_zones: list[LeafletPoly], permissible_zones: list[LeafletPoly], - permissible_lines: list[list[LeafletLatLon]], + preferred_lines: list[list[LeafletLatLon]], ) -> None: super().__init__() self._home_bubble = home_bubble @@ -954,7 +960,7 @@ class HoldZonesJs(QObject): self._join_bubble = join_bubble self._excluded_zones = excluded_zones self._permissible_zones = permissible_zones - self._permissible_lines = permissible_lines + self._preferred_lines = preferred_lines @Property(list, notify=homeBubbleChanged) def homeBubble(self) -> LeafletPoly: @@ -976,9 +982,9 @@ class HoldZonesJs(QObject): def permissibleZones(self) -> list[LeafletPoly]: return self._permissible_zones - @Property(list, notify=permissibleLinesChanged) - def permissibleLines(self) -> list[list[LeafletLatLon]]: - return self._permissible_lines + @Property(list, notify=preferredLinesChanged) + def preferredLines(self) -> list[list[LeafletLatLon]]: + return self._preferred_lines @classmethod def empty(cls) -> HoldZonesJs: @@ -1003,7 +1009,7 @@ class HoldZonesJs(QObject): shapely_poly_to_leaflet_points(geometry.join_bubble, game.theater), shapely_to_leaflet_polys(geometry.excluded_zones, game.theater), shapely_to_leaflet_polys(geometry.permissible_zones, game.theater), - [], # shapely_to_leaflet_polys(geometry.permissible_lines, game.theater), + shapely_lines_to_leaflet_points(geometry.preferred_lines, game.theater), ) diff --git a/resources/ui/map/map.js b/resources/ui/map/map.js index 831701a9..4e4ff59e 100644 --- a/resources/ui/map/map.js +++ b/resources/ui/map/map.js @@ -1066,7 +1066,14 @@ function drawJoinZones() { }).addTo(joinZones); } - for (const line of game.joinZones.permissibleLines) { + for (const zone of game.joinZones.permissibleZones) { + L.polygon(zone, { + color: Colors.Green, + interactive: false, + }).addTo(joinZones); + } + + for (const line of game.joinZones.preferredLines) { L.polyline(line, { color: Colors.Green, interactive: false, @@ -1114,6 +1121,13 @@ function drawHoldZones() { interactive: false, }).addTo(holdZones); } + + for (const line of game.holdZones.preferredLines) { + L.polyline(line, { + color: Colors.Green, + interactive: false, + }).addTo(holdZones); + } } function drawInitialMap() {