def display_culling(self, scene: QGraphicsScene) -> None: """Draws the culling distance rings on the map""" culling_points = self.game_model.game.get_culling_points() culling_zones = self.game_model.game.get_culling_zones() culling_distance = self.game_model.game.settings.perf_culling_distance for point in culling_points: culling_distance_point = Point(point.x + 2500, point.y + 2500) distance_point = self._transform_point(culling_distance_point) transformed = self._transform_point(point) radius = distance_point[0] - transformed[0] scene.addEllipse( transformed[0] - radius, transformed[1] - radius, 2 * radius, 2 * radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"], ) for zone in culling_zones: culling_distance_zone = Point( zone.x + culling_distance * 1000, zone.y + culling_distance * 1000 ) distance_zone = self._transform_point(culling_distance_zone) transformed = self._transform_point(zone) radius = distance_zone[0] - transformed[0] scene.addEllipse( transformed[0] - radius, transformed[1] - radius, 2 * radius, 2 * radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"], )
def projection_test(self): for i in range(100): for j in range(100): x = i * 100.0 y = j * 100.0 original = Point(x, y) proj = self._scene_to_dcs_coords(original) unproj = self._transform_point(proj) converted = Point(*unproj) assert math.isclose(original.x, converted.x, abs_tol=0.00000001) assert math.isclose(original.y, converted.y, abs_tol=0.00000001)
def draw_navmesh_neighbor_line(self, scene: QGraphicsScene, poly: Polygon, begin: ShapelyPoint) -> None: vertex = Point(begin.x, begin.y) centroid = poly.centroid direction = Point(centroid.x, centroid.y) end = vertex.point_from_heading( vertex.heading_between_point(direction), nautical_miles(2).meters) scene.addLine( QLineF(QPointF(*self._transform_point(vertex)), QPointF(*self._transform_point(end))), CONST.COLORS["yellow"])
def keyPressEvent(self, event): modifiers = QtWidgets.QApplication.keyboardModifiers() if not self.reference_point_setup_mode: if modifiers == QtCore.Qt.ShiftModifier and event.key( ) == QtCore.Qt.Key_R: self.reference_point_setup_mode = True self.reload_scene() else: super(QLiberationMap, self).keyPressEvent(event) else: if modifiers == QtCore.Qt.ShiftModifier and event.key( ) == QtCore.Qt.Key_R: self.reference_point_setup_mode = False self.reload_scene() else: distance = 1 modifiers = int(event.modifiers()) if modifiers & QtCore.Qt.ShiftModifier: distance *= 10 elif modifiers & QtCore.Qt.ControlModifier: distance *= 100 if event.key() == QtCore.Qt.Key_Down: self.update_reference_point( self.game.theater.reference_points[0], Point(0, distance)) if event.key() == QtCore.Qt.Key_Up: self.update_reference_point( self.game.theater.reference_points[0], Point(0, -distance)) if event.key() == QtCore.Qt.Key_Left: self.update_reference_point( self.game.theater.reference_points[0], Point(-distance, 0)) if event.key() == QtCore.Qt.Key_Right: self.update_reference_point( self.game.theater.reference_points[0], Point(distance, 0)) if event.key() == QtCore.Qt.Key_S: self.update_reference_point( self.game.theater.reference_points[1], Point(0, distance)) if event.key() == QtCore.Qt.Key_W: self.update_reference_point( self.game.theater.reference_points[1], Point(0, -distance)) if event.key() == QtCore.Qt.Key_A: self.update_reference_point( self.game.theater.reference_points[1], Point(-distance, 0)) if event.key() == QtCore.Qt.Key_D: self.update_reference_point( self.game.theater.reference_points[1], Point(distance, 0)) logging.debug( f"Reference points: {self.game.theater.reference_points}") self.reload_scene()
def set_destination( cp_id: UUID, destination: LeafletPoint = Body(..., title="destination"), game: Game = Depends(GameContext.require), ) -> None: cp = game.theater.find_control_point_by_id(cp_id) if cp is None: raise HTTPException( status.HTTP_404_NOT_FOUND, detail=f"Game has no control point with ID {cp_id}", ) if not cp.moveable: raise HTTPException(status.HTTP_403_FORBIDDEN, detail=f"{cp} is not mobile") if not cp.captured: raise HTTPException(status.HTTP_403_FORBIDDEN, detail=f"{cp} is not owned by the player") point = Point.from_latlng(LatLng(destination.lat, destination.lng), game.theater.terrain) if not cp.destination_in_range(point): raise HTTPException( status.HTTP_400_BAD_REQUEST, detail=f"Cannot move {cp} more than " f"{cp.max_move_distance.nautical_miles}nm.", ) cp.target_position = point EventStream.put_nowait(GameUpdateEvents().update_control_point(cp))
def generate_destroyed_units(self) -> None: """Add destroyed units to the Mission""" if not self.game.settings.perf_destroyed_units: return for d in self.game.get_destroyed_units(): try: type_name = d["type"] if not isinstance(type_name, str): raise TypeError( "Expected the type of the destroyed static to be a string" ) utype = unit_type_from_name(type_name) except KeyError: logging.warning(f"Destroyed unit has no type: {d}") continue pos = Point(cast(float, d["x"]), cast(float, d["z"]), self.mission.terrain) if utype is not None and not self.game.position_culled(pos): self.mission.static_group( country=self.mission.country(self.game.blue.country_name), name="", _type=utype, hidden=True, position=pos, heading=d["orientation"], dead=True, )
def _transform_point(self, world_point: Point) -> Tuple[float, float]: """Transforms world coordinates to image coordinates. World coordinates are transposed. X increases toward the North, Y increases toward the East. The origin point depends on the map. Image coordinates originate from the top left. X increases to the right, Y increases toward the bottom. The two points should be as distant as possible in both latitude and logitude, and tuning the reference points will be simpler if they are in geographically recognizable locations. For example, the Caucasus map is aligned using the first point on Gelendzhik and the second on Batumi. The distances between each point are computed and a scaling factor is determined from that. The given point is then offset from the first point using the scaling factor. X is latitude, increasing northward. Y is longitude, increasing eastward. """ point_a = self.game.theater.reference_points[0] scale = self._scaling_factor() offset = self._transpose_point(point_a.world_coordinates - world_point) scaled = Point(offset.x * scale.x, offset.y * scale.y) transformed = point_a.image_coordinates - scaled return transformed.x, transformed.y
def _scene_to_dcs_coords(self, scene_point: Point) -> Point: point_a = self.game.theater.reference_points[0] scale = self._scaling_factor() offset = point_a.image_coordinates - scene_point scaled = self._transpose_point(Point(offset.x / scale.x, offset.y / scale.y)) return point_a.world_coordinates - scaled
def sceneMousePressEvent(self, event: QGraphicsSceneMouseEvent): if self.state == QLiberationMapState.MOVING_UNIT: if event.buttons() == Qt.RightButton: pass elif event.buttons() == Qt.LeftButton: if self.selected_cp is not None: # Set movement position for the cp pos = event.scenePos() point = Point(int(pos.x()), int(pos.y())) proj = self._scene_to_dcs_coords(point) if self.is_valid_ship_pos(point): self.selected_cp.control_point.target_position = proj else: self.selected_cp.control_point.target_position = None GameUpdateSignal.get_instance().updateGame( self.game_model.game) else: return self.state = QLiberationMapState.NORMAL try: self.scene().removeItem(self.movement_line) except: pass self.selected_cp = None
def generate_routes(self) -> None: """ Generate routes drawing between cps """ seen = set() for cp in self.game.theater.controlpoints: seen.add(cp) for destination, convoy_route in cp.convoy_routes.items(): if destination in seen: continue else: # Determine path color if cp.captured and destination.captured: color = BLUE_PATH_COLOR elif not cp.captured and not destination.captured: color = RED_PATH_COLOR else: color = ACTIVE_PATH_COLOR # Add shape to layer shape = self.player_layer.add_line_segments( cp.position, [Point(0, 0, self.game.theater.terrain)] + [p - cp.position for p in convoy_route] + [destination.position - cp.position], line_thickness=6, color=color, line_style=LineStyle.Solid, ) shape.name = "path from " + cp.name + " to " + destination.name
def draw_flight_plan(self, scene: QGraphicsScene, flight: Flight, selected: bool) -> None: is_player = flight.from_cp.captured pos = self._transform_point(flight.from_cp.position) self.draw_waypoint(scene, pos, is_player, selected) prev_pos = tuple(pos) drew_target = False target_types = ( FlightWaypointType.TARGET_GROUP_LOC, FlightWaypointType.TARGET_POINT, FlightWaypointType.TARGET_SHIP, ) for idx, point in enumerate(flight.flight_plan.waypoints[1:]): if point.waypoint_type == FlightWaypointType.DIVERT: # Don't clutter the map showing divert points. continue new_pos = self._transform_point(Point(point.x, point.y)) self.draw_flight_path(scene, prev_pos, new_pos, is_player, selected) self.draw_waypoint(scene, new_pos, is_player, selected) if selected and DisplayOptions.waypoint_info: if point.waypoint_type in target_types: if drew_target: # Don't draw dozens of targets over each other. continue drew_target = True self.draw_waypoint_info(scene, idx + 1, point, new_pos, flight.flight_plan) prev_pos = tuple(new_pos) if selected and DisplayOptions.barcap_commit_range: self.draw_barcap_commit_range(scene, flight)
def repair_unit(self, unit, price): if self.game.blue.budget > price: self.game.blue.budget -= price unit.alive = True GameUpdateSignal.get_instance().updateGame(self.game) # Remove destroyed units in the vicinity destroyed_units = self.game.get_destroyed_units() for d in destroyed_units: p = Point(d["x"], d["z"], self.game.theater.terrain) if p.distance_to_point(unit.position) < 15: destroyed_units.remove(d) logging.info("Removed destroyed units " + str(d)) logging.info(f"Repaired unit: {unit.unit_name}") self.update_game()
def repair_unit(self, group, unit, price): if self.game.budget > price: self.game.budget -= price group.units_losts = [u for u in group.units_losts if u.id != unit.id] group.units.append(unit) GameUpdateSignal.get_instance().updateGame(self.game) # Remove destroyed units in the vicinity destroyed_units = self.game.get_destroyed_units() for d in destroyed_units: p = Point(d["x"], d["z"]) if p.distance_to_point(unit.position) < 15: destroyed_units.remove(d) logging.info("Removed destroyed units " + str(d)) logging.info("Repaired unit : " + str(unit.id) + " " + str(unit.type)) self.do_refresh_layout()
def coalition(self, obj): bullseye = obj.bullseye if not None else {'x': 0, 'y': 0} bullseye = Point(bullseye['x'], bullseye['y']) return { 'name': obj.name, 'bullseye': self.point(bullseye), 'countries': self.default(obj.countries) }
def poly_to_leaflet(cls, poly: Polygon, theater: ConflictTheater) -> LeafletPoly: if poly.is_empty: return [] return [ cls.latlng_to_leaflet(Point(x, y, theater.terrain).latlng()) for x, y in poly.exterior.coords ]
def draw_shapely_poly(self, scene: QGraphicsScene, poly: Polygon, pen: QPen, brush: QBrush) -> Optional[QPolygonF]: if poly.is_empty: return None points = [] for x, y in poly.exterior.coords: x, y = self._transform_point(Point(x, y)) points.append(QPointF(x, y)) return scene.addPolygon(QPolygonF(points), pen, brush)
def sceneMouseMovedEvent(self, event: QGraphicsSceneMouseEvent): if self.game is None: return mouse_position = Point(event.scenePos().x(), event.scenePos().y()) if self.state == QLiberationMapState.MOVING_UNIT: self.setCursor(Qt.PointingHandCursor) self.movement_line.setLine( QLineF(self.movement_line.line().p1(), event.scenePos()) ) if self.is_valid_ship_pos(mouse_position): self.movement_line.setPen(CONST.COLORS["green"]) else: self.movement_line.setPen(CONST.COLORS["red"]) mouse_world_pos = self._scene_to_dcs_coords(mouse_position) if DisplayOptions.navmeshes.blue_navmesh: self.highlight_mouse_navmesh( self.scene(), self.game.blue_navmesh, self._scene_to_dcs_coords(mouse_position), ) if DisplayOptions.path_debug.shortest_path: self.draw_shortest_path( self.scene(), self.game.blue_navmesh, mouse_world_pos, player=True ) if DisplayOptions.navmeshes.red_navmesh: self.highlight_mouse_navmesh( self.scene(), self.game.red_navmesh, mouse_world_pos ) debug_blue = DisplayOptions.path_debug_faction.blue if DisplayOptions.path_debug.shortest_path: self.draw_shortest_path( self.scene(), self.game.navmesh_for(player=debug_blue), mouse_world_pos, player=False, ) elif not DisplayOptions.path_debug.hide: if DisplayOptions.path_debug.barcap: task = FlightType.BARCAP elif DisplayOptions.path_debug.cas: task = FlightType.CAS elif DisplayOptions.path_debug.sweep: task = FlightType.SWEEP elif DisplayOptions.path_debug.strike: task = FlightType.STRIKE elif DisplayOptions.path_debug.tarcap: task = FlightType.TARCAP else: raise ValueError("Unexpected value for DisplayOptions.path_debug") self.draw_test_flight_plan( self.scene(), task, mouse_world_pos, player=debug_blue )
def _scaling_factor(self) -> Point: point_a = self.game.theater.reference_points[0] point_b = self.game.theater.reference_points[1] world_distance = self._transpose_point(point_b.world_coordinates - point_a.world_coordinates) image_distance = point_b.image_coordinates - point_a.image_coordinates x_scale = image_distance.x / world_distance.x y_scale = image_distance.y / world_distance.y return Point(x_scale, y_scale)
def on_select_wpt_changed(self): super(QCASMissionGenerator, self).on_select_wpt_changed() wpts = self.wpt_selection_box.get_selected_waypoints() if len(wpts) > 0: self.distanceToTargetLabel.setText("~" + str( meter_to_nm( self.flight.from_cp.position.distance_to_point( Point(wpts[0].x, wpts[0].y)))) + " nm") else: self.distanceToTargetLabel.setText("??? nm")
def sceneMouseMovedEvent(self, event: QGraphicsSceneMouseEvent): if self.state == QLiberationMapState.MOVING_UNIT: self.setCursor(Qt.PointingHandCursor) self.movement_line.setLine( QLineF(self.movement_line.line().p1(), event.scenePos())) pos = Point(event.scenePos().x(), event.scenePos().y()) if self.is_valid_ship_pos(pos): self.movement_line.setPen(CONST.COLORS["green"]) else: self.movement_line.setPen(CONST.COLORS["red"])
def addBackground(self): scene = self.scene() if not DisplayOptions.map_poly: bg = QPixmap("./resources/" + self.game.theater.overview_image) scene.addPixmap(bg) # Apply graphical effects to simulate current daytime if self.game.current_turn_time_of_day == TimeOfDay.Day: pass elif self.game.current_turn_time_of_day == TimeOfDay.Night: ov = QPixmap(bg.width(), bg.height()) ov.fill(CONST.COLORS["night_overlay"]) overlay = scene.addPixmap(ov) effect = QGraphicsOpacityEffect() effect.setOpacity(0.7) overlay.setGraphicsEffect(effect) else: ov = QPixmap(bg.width(), bg.height()) ov.fill(CONST.COLORS["dawn_dust_overlay"]) overlay = scene.addPixmap(ov) effect = QGraphicsOpacityEffect() effect.setOpacity(0.3) overlay.setGraphicsEffect(effect) else: # Polygon display mode if self.game.theater.landmap is not None: for sea_zone in self.game.theater.landmap[2]: print(sea_zone) poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in sea_zone]) scene.addPolygon(poly, CONST.COLORS["sea_blue"], CONST.COLORS["sea_blue"]) for inclusion_zone in self.game.theater.landmap[0]: poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in inclusion_zone]) scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"]) for exclusion_zone in self.game.theater.landmap[1]: poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in exclusion_zone]) scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"])
def draw_threat_range(self, scene: QGraphicsScene, group: Group, ground_object: TheaterGroundObject, cp: ControlPoint) -> None: go_pos = self._transform_point(ground_object.position) detection_range = ground_object.detection_range(group) threat_range = ground_object.threat_range(group) if threat_range: threat_pos = self._transform_point( ground_object.position + Point(threat_range.meters, threat_range.meters)) threat_radius = Point(*go_pos).distance_to_point( Point(*threat_pos)) # Add threat range circle scene.addEllipse(go_pos[0] - threat_radius / 2 + 7, go_pos[1] - threat_radius / 2 + 6, threat_radius, threat_radius, self.threat_pen(cp.captured)) if detection_range and DisplayOptions.detection_range: # Add detection range circle detection_pos = self._transform_point( ground_object.position + Point(detection_range.meters, detection_range.meters)) detection_radius = Point(*go_pos).distance_to_point( Point(*detection_pos)) scene.addEllipse(go_pos[0] - detection_radius / 2 + 7, go_pos[1] - detection_radius / 2 + 6, detection_radius, detection_radius, self.detection_pen(cp.captured))
def destination_in_range( cp_id: UUID, lat: float, lng: float, game: Game = Depends(GameContext.require)) -> bool: cp = game.theater.find_control_point_by_id(cp_id) if cp is None: raise HTTPException( status.HTTP_404_NOT_FOUND, detail=f"Game has no control point with ID {cp_id}", ) point = Point.from_latlng(LatLng(lat, lng), game.theater.terrain) return cp.destination_in_range(point)
def display_navmesh(self, scene: QGraphicsScene, player: bool) -> None: for navpoly in self.game.navmesh_for(player).polys: self.draw_shapely_poly(scene, navpoly.poly, CONST.COLORS["black"], CONST.COLORS["transparent"]) position = self._transform_point( Point(navpoly.poly.centroid.x, navpoly.poly.centroid.y)) text = scene.addSimpleText(f"Navmesh {navpoly.ident}", self.waypoint_info_font) text.setBrush(QColor(255, 255, 255)) text.setPen(QColor(255, 255, 255)) text.moveBy(position[0] + 8, position[1]) text.setZValue(2) for border in navpoly.neighbors.values(): self.draw_navmesh_border(border, scene, navpoly.poly)
def generate(self) -> None: if self.cp.captured: country_name = self.game.player_country else: country_name = self.game.enemy_country country = self.m.country(country_name) for i, helipad in enumerate(self.cp.helipads): name = self.cp.name + "_helipad_" + str(i) logging.info("Generating helipad : " + name) pad = SingleHeliPad(name=(name + "_unit")) pad.position = Point(helipad.x, helipad.y) pad.heading = helipad.heading # pad.heliport_frequency = self.radio_registry.alloc_uhf() TODO : alloc radio & callsign sg = unitgroup.StaticGroup(self.m.next_group_id(), name) sg.add_unit(pad) sp = StaticPoint() sp.position = pad.position sg.add_point(sp) country.add_static_group(sg)
def setup_mission_coalitions(self) -> None: self.mission.coalition["blue"] = Coalition( "blue", bullseye=self.game.blue.bullseye.to_pydcs()) self.mission.coalition["red"] = Coalition( "red", bullseye=self.game.red.bullseye.to_pydcs()) self.mission.coalition["neutrals"] = Coalition( "neutrals", bullseye=Bullseye(Point(0, 0, self.mission.terrain)).to_pydcs()) p_country = self.game.blue.country_name e_country = self.game.red.country_name self.mission.coalition["blue"].add_country( country_dict[country_id_from_name(p_country)]()) self.mission.coalition["red"].add_country( country_dict[country_id_from_name(e_country)]()) belligerents = [ country_id_from_name(p_country), country_id_from_name(e_country), ] for country in country_dict.keys(): if country not in belligerents: self.mission.coalition["neutrals"].add_country( country_dict[country]())
def __init__( self, target: Point, home: Point, coalition: Coalition, ) -> None: self._target = target self.threat_zone = coalition.opponent.threat_zone.all self.home = ShapelyPoint(home.x, home.y) max_ip_distance = coalition.doctrine.max_ingress_distance min_ip_distance = coalition.doctrine.min_ingress_distance # The minimum distance between the home location and the IP. min_distance_from_home = nautical_miles(5) # The distance that is expected to be needed between the beginning of the attack # and weapon release. This buffers the threat zone to give a 5nm window between # the edge of the "safe" zone and the actual threat so that "safe" IPs are less # likely to end up with the attacker entering a threatened area. attack_distance_buffer = nautical_miles(5) home_threatened = coalition.opponent.threat_zone.threatened(home) shapely_target = ShapelyPoint(target.x, target.y) home_to_target_distance = meters(home.distance_to_point(target)) self.home_bubble = self.home.buffer(home_to_target_distance.meters).difference( self.home.buffer(min_distance_from_home.meters) ) # If the home zone is not threatened and home is within LAR, constrain the max # range to the home-to-target distance to prevent excessive backtracking. # # If the home zone *is* threatened, we need to back out of the zone to # rendezvous anyway. if not home_threatened and ( min_ip_distance < home_to_target_distance < max_ip_distance ): max_ip_distance = home_to_target_distance max_ip_bubble = shapely_target.buffer(max_ip_distance.meters) min_ip_bubble = shapely_target.buffer(min_ip_distance.meters) self.ip_bubble = max_ip_bubble.difference(min_ip_bubble) # The intersection of the home bubble and IP bubble will be all the points that # are within the valid IP range that are not farther from home than the target # is. However, if the origin airfield is threatened but there are safe # placements for the IP, we should not constrain to the home zone. In this case # we'll either end up with a safe zone outside the home zone and pick the # closest point in to to home (minimizing backtracking), or we'll have no safe # IP anywhere within range of the target, and we'll later pick the IP nearest # the edge of the threat zone. if home_threatened: self.permissible_zone = self.ip_bubble else: self.permissible_zone = self.ip_bubble.intersection(self.home_bubble) if self.permissible_zone.is_empty: # If home is closer to the target than the min range, there will not be an # IP solution that's close enough to home, in which case we need to ignore # the home bubble. self.permissible_zone = self.ip_bubble safe_zones = self.permissible_zone.difference( self.threat_zone.buffer(attack_distance_buffer.meters) ) if not isinstance(safe_zones, MultiPolygon): safe_zones = MultiPolygon([safe_zones]) self.safe_zones = safe_zones
def addBackground(self): scene = self.scene() if not DisplayOptions.map_poly: bg = QPixmap("./resources/" + self.game.theater.overview_image) scene.addPixmap(bg) # Apply graphical effects to simulate current daytime if self.game.current_turn_time_of_day == TimeOfDay.Day: pass elif self.game.current_turn_time_of_day == TimeOfDay.Night: ov = QPixmap(bg.width(), bg.height()) ov.fill(CONST.COLORS["night_overlay"]) overlay = scene.addPixmap(ov) effect = QGraphicsOpacityEffect() effect.setOpacity(0.7) overlay.setGraphicsEffect(effect) else: ov = QPixmap(bg.width(), bg.height()) ov.fill(CONST.COLORS["dawn_dust_overlay"]) overlay = scene.addPixmap(ov) effect = QGraphicsOpacityEffect() effect.setOpacity(0.3) overlay.setGraphicsEffect(effect) if DisplayOptions.map_poly or self.reference_point_setup_mode: # Polygon display mode if self.game.theater.landmap is not None: 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: color = "sea_blue_transparent" else: color = "sea_blue" scene.addPolygon(poly, CONST.COLORS[color], CONST.COLORS[color]) 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.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"]) else: scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"]) # Uncomment to display plan projection test # self.projection_test() self.draw_scale() if self.reference_point_setup_mode: for i, point in enumerate(self.game.theater.reference_points): self.scene().addRect(QRectF(point.image_coordinates.x, point.image_coordinates.y, 25, 25), pen=CONST.COLORS["red"], brush=CONST.COLORS["red"]) text = self.scene().addText( f"P{i} = {point.image_coordinates}", font=QFont("Trebuchet MS", 14, weight=8, italic=False)) text.setDefaultTextColor(CONST.COLORS["red"]) text.setPos(point.image_coordinates.x + 26, point.image_coordinates.y) # Set to True to visually debug _transform_point. draw_transformed = False if draw_transformed: x, y = self._transform_point(point.world_coordinates) self.scene().addRect(QRectF(x, y, 25, 25), pen=CONST.COLORS["red"], brush=CONST.COLORS["red"]) text = self.scene().addText(f"P{i}' = {x}, {y}", font=QFont("Trebuchet MS", 14, weight=8, italic=False)) text.setDefaultTextColor(CONST.COLORS["red"]) text.setPos(x + 26, y)
def distance_to_pixels(self, distance: Distance) -> int: p1 = Point(0, 0) p2 = Point(0, distance.meters) p1a = Point(*self._transform_point(p1)) p2a = Point(*self._transform_point(p2)) return int(p1a.distance_to_point(p2a))
def _transpose_point(p: Point) -> Point: return Point(p.y, p.x)