Convert front line segments to proper class.

Needed so we can add context menus to the lines.
This commit is contained in:
Dan Albert 2021-04-19 21:41:56 -07:00
parent 2a5b37b9ad
commit 2b06d8a096
2 changed files with 109 additions and 54 deletions

View File

@ -4,7 +4,7 @@ import datetime
import logging import logging
import math import math
from functools import singledispatchmethod from functools import singledispatchmethod
from typing import Iterable, Iterator, List, Optional, Set, Tuple from typing import Iterable, Iterator, List, Optional, Tuple
from PySide2 import QtCore, QtWidgets from PySide2 import QtCore, QtWidgets
from PySide2.QtCore import QLineF, QPointF, QRectF, Qt from PySide2.QtCore import QLineF, QPointF, QRectF, Qt
@ -26,8 +26,8 @@ from PySide2.QtWidgets import (
QGraphicsView, QGraphicsView,
) )
from dcs import Point from dcs import Point
from dcs.planes import F_16C_50
from dcs.mapping import point_from_heading from dcs.mapping import point_from_heading
from dcs.planes import F_16C_50
from dcs.unitgroup import Group from dcs.unitgroup import Group
from shapely.geometry import ( from shapely.geometry import (
LineString, LineString,
@ -44,6 +44,7 @@ from game.theater.conflicttheater import FrontLine, ReferencePoint
from game.theater.theatergroundobject import ( from game.theater.theatergroundobject import (
TheaterGroundObject, TheaterGroundObject,
) )
from game.transfers import RoadTransferOrder
from game.utils import Distance, meters, nautical_miles from game.utils import Distance, meters, nautical_miles
from game.weather import TimeOfDay from game.weather import TimeOfDay
from gen import Conflict, Package from gen import Conflict, Package
@ -66,6 +67,7 @@ from qt_ui.widgets.map.QFrontLine import QFrontLine
from qt_ui.widgets.map.QLiberationScene import QLiberationScene from qt_ui.widgets.map.QLiberationScene import QLiberationScene
from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint
from qt_ui.widgets.map.QMapGroundObject import QMapGroundObject from qt_ui.widgets.map.QMapGroundObject import QMapGroundObject
from qt_ui.widgets.map.SupplyRouteSegment import SupplyRouteSegment
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
MAX_SHIP_DISTANCE = nautical_miles(80) MAX_SHIP_DISTANCE = nautical_miles(80)
@ -824,9 +826,8 @@ class QLiberationMap(QGraphicsView):
def draw_bezier_frontline( def draw_bezier_frontline(
self, self,
scene: QGraphicsScene, scene: QGraphicsScene,
pen: QPen,
frontline: FrontLine, frontline: FrontLine,
convoy_size: int = 0, convoys: List[RoadTransferOrder],
) -> None: ) -> None:
""" """
Thanks to Alquimista for sharing a python implementation of the bezier algorithm this is adapted from. Thanks to Alquimista for sharing a python implementation of the bezier algorithm this is adapted from.
@ -841,18 +842,17 @@ class QLiberationMap(QGraphicsView):
for point in bezier_curve_range( for point in bezier_curve_range(
int(len(bezier_fixed_points) * 2), bezier_fixed_points int(len(bezier_fixed_points) * 2), bezier_fixed_points
): ):
line = scene.addLine( scene.addItem(
old_point[0], old_point[1], point[0], point[1], pen=pen SupplyRouteSegment(
) old_point[0],
if convoy_size: old_point[1],
units = "units" if convoy_size > 1 else "unit" point[0],
tooltip = ( point[1],
f"{convoy_size} {units} transferring between " frontline.control_point_a,
f"{frontline.control_point_a} and {frontline.control_point_b}." frontline.control_point_b,
convoys,
) )
else: )
tooltip = "No convoys present on this supply route."
line.setToolTip(tooltip)
old_point = point old_point = point
def draw_supply_routes(self) -> None: def draw_supply_routes(self) -> None:
@ -865,19 +865,21 @@ class QLiberationMap(QGraphicsView):
if DisplayOptions.lines: if DisplayOptions.lines:
self.draw_supply_route_between(cp, connected) self.draw_supply_route_between(cp, connected)
def _count_units_tranferring_between(self, a: ControlPoint, b: ControlPoint) -> int: def _transfers_between(
self, a: ControlPoint, b: ControlPoint
) -> List[RoadTransferOrder]:
# We attempt to short circuit the expensive shortest path computation for the # We attempt to short circuit the expensive shortest path computation for the
# cases where there is never a transfer, but caching might be needed. # cases where there is never a transfer, but caching might be needed.
if a.captured != b.captured: if a.captured != b.captured:
# Cannot transfer to enemy CPs. # Cannot transfer to enemy CPs.
return 0 return []
# This is only called for drawing lines between nodes and have rules out routes # This is only called for drawing lines between nodes and have rules out routes
# to enemy bases, so a and b are guaranteed to be in the same supply route. # to enemy bases, so a and b are guaranteed to be in the same supply route.
supply_route = SupplyRoute.for_control_point(a) supply_route = SupplyRoute.for_control_point(a)
count = 0 transfers = []
points = {a, b} points = {a, b}
for transfer in self.game.transfers: for transfer in self.game.transfers:
# No possible route from our network to this transfer. # No possible route from our network to this transfer.
@ -887,55 +889,32 @@ class QLiberationMap(QGraphicsView):
# Anything left is a transfer within our supply route. # Anything left is a transfer within our supply route.
transfer_points = {transfer.position, transfer.next_stop()} transfer_points = {transfer.position, transfer.next_stop()}
if points == transfer_points: if points == transfer_points:
count += sum(transfer.units.values()) transfers.append(transfer)
return count return transfers
def supply_route_color(self, a: ControlPoint, b: ControlPoint) -> QColor:
if a.front_is_active(b):
return CONST.COLORS["red"]
elif a.captured:
return CONST.COLORS["dark_" + self.game.get_player_color()]
else:
return CONST.COLORS["dark_" + self.game.get_enemy_color()]
def supply_route_style(
self, a: ControlPoint, b: ControlPoint, has_transfer: bool
) -> Qt.PenStyle:
if a.front_is_active(b) or has_transfer:
return Qt.PenStyle.SolidLine
return Qt.PenStyle.DotLine
def supply_route_pen(
self, a: ControlPoint, b: ControlPoint, has_transfer: bool
) -> QPen:
color = self.supply_route_color(a, b)
pen = QPen(brush=color)
pen.setColor(color)
pen.setStyle(self.supply_route_style(a, b, has_transfer))
pen.setWidth(6)
return pen
def draw_supply_route_between(self, a: ControlPoint, b: ControlPoint) -> None: def draw_supply_route_between(self, a: ControlPoint, b: ControlPoint) -> None:
scene = self.scene() scene = self.scene()
convoy_size = self._count_units_tranferring_between(a, b) convoys = self._transfers_between(a, b)
pen = self.supply_route_pen(a, b, convoy_size > 0)
frontline = FrontLine(a, b, self.game.theater) frontline = FrontLine(a, b, self.game.theater)
if a.front_is_active(b): if a.front_is_active(b):
if DisplayOptions.actual_frontline_pos: if DisplayOptions.actual_frontline_pos:
self.draw_actual_frontline(frontline, scene, pen) self.draw_actual_frontline(scene, frontline, convoys)
else: else:
self.draw_frontline_approximation(frontline, scene, pen) self.draw_frontline_approximation(scene, frontline, convoys)
else: else:
self.draw_bezier_frontline(scene, pen, frontline, convoy_size) self.draw_bezier_frontline(scene, frontline, convoys)
def draw_frontline_approximation( def draw_frontline_approximation(
self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen self,
scene: QGraphicsScene,
frontline: FrontLine,
convoys: List[RoadTransferOrder],
) -> None: ) -> None:
posx = frontline.position posx = frontline.position
h = frontline.attack_heading h = frontline.attack_heading
pos2 = self._transform_point(posx) pos2 = self._transform_point(posx)
self.draw_bezier_frontline(scene, pen, frontline) self.draw_bezier_frontline(scene, frontline, convoys)
p1 = point_from_heading(pos2[0], pos2[1], h + 180, 25) p1 = point_from_heading(pos2[0], pos2[1], h + 180, 25)
p2 = point_from_heading(pos2[0], pos2[1], h, 25) p2 = point_from_heading(pos2[0], pos2[1], h, 25)
scene.addItem( scene.addItem(
@ -943,9 +922,12 @@ class QLiberationMap(QGraphicsView):
) )
def draw_actual_frontline( def draw_actual_frontline(
self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen self,
scene: QGraphicsScene,
frontline: FrontLine,
convoys: List[RoadTransferOrder],
) -> None: ) -> None:
self.draw_bezier_frontline(scene, pen, frontline) self.draw_bezier_frontline(scene, frontline, convoys)
vector = Conflict.frontline_vector( vector = Conflict.frontline_vector(
frontline.control_point_a, frontline.control_point_b, self.game.theater frontline.control_point_a, frontline.control_point_b, self.game.theater
) )

View File

@ -0,0 +1,73 @@
from functools import cached_property
from typing import List, Optional
from PySide2.QtCore import Qt
from PySide2.QtGui import QColor, QPen
from PySide2.QtWidgets import QGraphicsItem, QGraphicsLineItem
from game.theater import ControlPoint
from game.transfers import RoadTransferOrder
from qt_ui.uiconstants import COLORS
class SupplyRouteSegment(QGraphicsLineItem):
def __init__(
self,
x0: float,
y0: float,
x1: float,
y1: float,
control_point_a: ControlPoint,
control_point_b: ControlPoint,
convoys: List[RoadTransferOrder],
parent: Optional[QGraphicsItem] = None,
) -> None:
super().__init__(x0, y0, x1, y1, parent)
self.control_point_a = control_point_a
self.control_point_b = control_point_b
self.convoys = convoys
self.setPen(self.make_pen())
self.setToolTip(self.make_tooltip())
@cached_property
def convoy_size(self) -> int:
return sum(sum(c.units.values()) for c in self.convoys)
def make_tooltip(self) -> str:
if not self.convoys:
return "No convoys present on this supply route."
units = "units" if self.convoy_size > 1 else "unit"
return (
f"{self.convoy_size} {units} transferring between {self.control_point_a} "
f"and {self.control_point_b}."
)
@property
def line_color(self) -> QColor:
if self.control_point_a.front_is_active(self.control_point_b):
return COLORS["red"]
elif self.control_point_a.captured:
return COLORS["dark_blue"]
else:
return COLORS["dark_red"]
@property
def line_style(self) -> Qt.PenStyle:
if (
self.control_point_a.front_is_active(self.control_point_b)
or self.has_convoys
):
return Qt.PenStyle.SolidLine
return Qt.PenStyle.DotLine
def make_pen(self) -> QPen:
pen = QPen(brush=self.line_color)
pen.setColor(self.line_color)
pen.setStyle(self.line_style)
pen.setWidth(6)
return pen
@property
def has_convoys(self) -> bool:
return bool(self.convoys)