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 math
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.QtCore import QLineF, QPointF, QRectF, Qt
@ -26,8 +26,8 @@ from PySide2.QtWidgets import (
QGraphicsView,
)
from dcs import Point
from dcs.planes import F_16C_50
from dcs.mapping import point_from_heading
from dcs.planes import F_16C_50
from dcs.unitgroup import Group
from shapely.geometry import (
LineString,
@ -44,6 +44,7 @@ from game.theater.conflicttheater import FrontLine, ReferencePoint
from game.theater.theatergroundobject import (
TheaterGroundObject,
)
from game.transfers import RoadTransferOrder
from game.utils import Distance, meters, nautical_miles
from game.weather import TimeOfDay
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.QMapControlPoint import QMapControlPoint
from qt_ui.widgets.map.QMapGroundObject import QMapGroundObject
from qt_ui.widgets.map.SupplyRouteSegment import SupplyRouteSegment
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
MAX_SHIP_DISTANCE = nautical_miles(80)
@ -824,9 +826,8 @@ class QLiberationMap(QGraphicsView):
def draw_bezier_frontline(
self,
scene: QGraphicsScene,
pen: QPen,
frontline: FrontLine,
convoy_size: int = 0,
convoys: List[RoadTransferOrder],
) -> None:
"""
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(
int(len(bezier_fixed_points) * 2), bezier_fixed_points
):
line = scene.addLine(
old_point[0], old_point[1], point[0], point[1], pen=pen
)
if convoy_size:
units = "units" if convoy_size > 1 else "unit"
tooltip = (
f"{convoy_size} {units} transferring between "
f"{frontline.control_point_a} and {frontline.control_point_b}."
scene.addItem(
SupplyRouteSegment(
old_point[0],
old_point[1],
point[0],
point[1],
frontline.control_point_a,
frontline.control_point_b,
convoys,
)
else:
tooltip = "No convoys present on this supply route."
line.setToolTip(tooltip)
)
old_point = point
def draw_supply_routes(self) -> None:
@ -865,19 +865,21 @@ class QLiberationMap(QGraphicsView):
if DisplayOptions.lines:
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
# cases where there is never a transfer, but caching might be needed.
if a.captured != b.captured:
# Cannot transfer to enemy CPs.
return 0
return []
# 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.
supply_route = SupplyRoute.for_control_point(a)
count = 0
transfers = []
points = {a, b}
for transfer in self.game.transfers:
# 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.
transfer_points = {transfer.position, transfer.next_stop()}
if points == transfer_points:
count += sum(transfer.units.values())
return count
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
transfers.append(transfer)
return transfers
def draw_supply_route_between(self, a: ControlPoint, b: ControlPoint) -> None:
scene = self.scene()
convoy_size = self._count_units_tranferring_between(a, b)
pen = self.supply_route_pen(a, b, convoy_size > 0)
convoys = self._transfers_between(a, b)
frontline = FrontLine(a, b, self.game.theater)
if a.front_is_active(b):
if DisplayOptions.actual_frontline_pos:
self.draw_actual_frontline(frontline, scene, pen)
self.draw_actual_frontline(scene, frontline, convoys)
else:
self.draw_frontline_approximation(frontline, scene, pen)
self.draw_frontline_approximation(scene, frontline, convoys)
else:
self.draw_bezier_frontline(scene, pen, frontline, convoy_size)
self.draw_bezier_frontline(scene, frontline, convoys)
def draw_frontline_approximation(
self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen
self,
scene: QGraphicsScene,
frontline: FrontLine,
convoys: List[RoadTransferOrder],
) -> None:
posx = frontline.position
h = frontline.attack_heading
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)
p2 = point_from_heading(pos2[0], pos2[1], h, 25)
scene.addItem(
@ -943,9 +922,12 @@ class QLiberationMap(QGraphicsView):
)
def draw_actual_frontline(
self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen
self,
scene: QGraphicsScene,
frontline: FrontLine,
convoys: List[RoadTransferOrder],
) -> None:
self.draw_bezier_frontline(scene, pen, frontline)
self.draw_bezier_frontline(scene, frontline, convoys)
vector = Conflict.frontline_vector(
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)