aboutsummaryrefslogtreecommitdiff
path: root/diplomacy/engine/renderer.py
diff options
context:
space:
mode:
authornotoraptor <stevenbocco@gmail.com>2019-08-14 12:22:22 -0400
committerPhilip Paquette <pcpaquette@gmail.com>2019-08-14 12:40:01 -0400
commit5c3bd9b3802e2001a7e77baf2911386135a03839 (patch)
treee641744650b05cddc85bc60c2d7e2d6fe2d88b47 /diplomacy/engine/renderer.py
parent5acb4ff23be4757a49b234f93928f13c436b60c6 (diff)
[Web] Integrated new maps on the web interface
- Fixed bug with incorrect dislodged unit on pure map - [python] Make sure dummy powers are registered only for standard maps. - Hardcoded supply centers into SVG files. - Removed supply centers CSS classes. - Update positions for units and dislodged units on all maps. - Converted SVGs to React. - Removed "sym" classes and hardcode related styles into symbol definitions. - Reordered map list (standard at top, then other ones in alphabetical order) - Displayed + button for all maps and disable it for maps without variants. - Minified generated code when converting SVG files to React. - [web] Added ability to hide/display map abbreviations.
Diffstat (limited to 'diplomacy/engine/renderer.py')
-rw-r--r--diplomacy/engine/renderer.py399
1 files changed, 190 insertions, 209 deletions
diff --git a/diplomacy/engine/renderer.py b/diplomacy/engine/renderer.py
index c6369c4..01fe209 100644
--- a/diplomacy/engine/renderer.py
+++ b/diplomacy/engine/renderer.py
@@ -20,10 +20,11 @@
"""
import os
from xml.dom import minidom
+from typing import Tuple
from diplomacy import settings
+from diplomacy.utils.equilateral_triangle import EquilateralTriangle
# Constants
-LAYER_SC = 'SupplyCenterLayer'
LAYER_ORDER = 'OrderLayer'
LAYER_UNIT = 'UnitLayer'
LAYER_DISL = 'DislodgedUnitLayer'
@@ -34,11 +35,6 @@ def _attr(node_element, attr_name):
""" Shorthand method to retrieve an XML attribute """
return node_element.attributes[attr_name].value
-def _offset(str_float, offset):
- """ Shorthand to add a offset to an attribute """
- return str(float(str_float) + offset)
-
-
class Renderer():
""" Renderer object responsible for rendering a game state to svg """
@@ -87,7 +83,6 @@ class Renderer():
# Parsing XML
xml_map = minidom.parseString(self.xml_map)
- scs = self.game.map.scs[:]
# Setting phase and note
nb_centers = [(power.name[:3], len(power.centers))
@@ -98,16 +93,14 @@ class Renderer():
xml_map = self._set_current_phase(xml_map, self.game.get_current_phase())
xml_map = self._set_note(xml_map, nb_centers_per_power, self.game.note)
- # Adding units, supply centers, and influence
+ # Adding units and influence
for power in self.game.powers.values():
for unit in power.units:
xml_map = self._add_unit(xml_map, unit, power.name, is_dislodged=False)
for unit in power.retreats:
xml_map = self._add_unit(xml_map, unit, power.name, is_dislodged=True)
for center in power.centers:
- xml_map = self._add_supply_center(xml_map, center, power.name)
xml_map = self._set_influence(xml_map, center, power.name, has_supply_center=True)
- scs.remove(center)
for loc in power.influence:
xml_map = self._set_influence(xml_map, loc, power.name, has_supply_center=False)
@@ -179,10 +172,6 @@ class Renderer():
else:
raise RuntimeError('Unknown order: {}'.format(order))
- # Adding remaining supply centers
- for center in scs:
- xml_map = self._add_supply_center(xml_map, center, None)
-
# Removing abbrev and mouse layer
svg_node = xml_map.getElementsByTagName('svg')[0]
for child_node in svg_node.childNodes:
@@ -225,15 +214,6 @@ class Renderer():
self.metadata['symbol_size'][_attr(child_node, 'name')] = (_attr(child_node, 'height'),
_attr(child_node, 'width'))
- # Order type
- elif child_node.nodeName.startswith('jdipNS'):
- order_type = child_node.nodeName.replace('jdipNS:', '')
- self.metadata['orders'][order_type] = {}
- for attr_name, attr_value in child_node.attributes.items():
- if ':' in attr_name:
- continue
- self.metadata['orders'][order_type][attr_name] = attr_value
-
# Object coordinates
for province_data in xml_map.getElementsByTagName('jdipNS:PROVINCE_DATA'):
for child_node in province_data.childNodes:
@@ -248,8 +228,6 @@ class Renderer():
self.metadata['coord'][province]['unit'] = (_attr(coord_node, 'x'), _attr(coord_node, 'y'))
elif coord_node.nodeName == 'jdipNS:DISLODGED_UNIT':
self.metadata['coord'][province]['disl'] = (_attr(coord_node, 'x'), _attr(coord_node, 'y'))
- elif coord_node.nodeName == 'jdipNS:SUPPLY_CENTER':
- self.metadata['coord'][province]['sc'] = (_attr(coord_node, 'x'), _attr(coord_node, 'y'))
# Deleting
svg_node = xml_map.getElementsByTagName('svg')[0]
@@ -268,9 +246,10 @@ class Renderer():
"""
unit_type, loc = unit.split()
symbol = FLEET if unit_type == 'F' else ARMY
- loc_x = _offset(self.metadata['coord'][loc][('unit', 'disl')[is_dislodged]][0], -11.5)
- loc_y = _offset(self.metadata['coord'][loc][('unit', 'disl')[is_dislodged]][1], - 10.)
+ loc_x = self.metadata['coord'][loc][('unit', 'disl')[is_dislodged]][0]
+ loc_y = self.metadata['coord'][loc][('unit', 'disl')[is_dislodged]][1]
node = xml_map.createElement('use')
+ node.setAttribute('id', '%sunit_%s' % ('dislodged_' if is_dislodged else '', loc))
node.setAttribute('x', loc_x)
node.setAttribute('y', loc_y)
node.setAttribute('height', self.metadata['symbol_size'][symbol][0])
@@ -286,34 +265,6 @@ class Renderer():
break
return xml_map
- def _add_supply_center(self, xml_map, loc, power_name):
- """ Adds a supply center to the map
- :param xml_map: The xml map being generated
- :param loc: The province where to add the SC (e.g. 'PAR')
- :param power_name: The name of the power owning the SC or None
- :return: Nothing
- """
- symbol = 'SupplyCenter'
- loc_x = _offset(self.metadata['coord'][loc]['sc'][0], -8.5)
- loc_y = _offset(self.metadata['coord'][loc]['sc'][1], -11.)
- node = xml_map.createElement('use')
- node.setAttribute('x', loc_x)
- node.setAttribute('y', loc_y)
- node.setAttribute('height', self.metadata['symbol_size'][symbol][0])
- node.setAttribute('width', self.metadata['symbol_size'][symbol][1])
- node.setAttribute('xlink:href', '#{}'.format(symbol))
- if power_name:
- node.setAttribute('class', 'sc{}'.format(power_name.lower()))
- else:
- node.setAttribute('class', 'scnopower')
-
- # Inserting
- for child_node in xml_map.getElementsByTagName('svg')[0].childNodes:
- if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'SupplyCenterLayer':
- child_node.appendChild(node)
- break
- return xml_map
-
def _set_influence(self, xml_map, loc, power_name, has_supply_center=False):
""" Sets the influence on the map
:param xml_map: The xml map being generated
@@ -325,18 +276,35 @@ class Renderer():
loc = loc.upper()[:3]
if loc in self.game.map.scs and not has_supply_center:
return xml_map
- if self.game.map.area_type(loc) not in ['LAND', 'COAST']:
+ if self.game.map.area_type(loc) == 'WATER':
return xml_map
+ class_name = power_name.lower() if power_name else 'nopower'
+
# Inserting
+ map_layer = None
for child_node in xml_map.getElementsByTagName('svg')[0].childNodes:
if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'MapLayer':
- for map_node in child_node.childNodes:
- if map_node.nodeName == 'path' and _attr(map_node, 'id') == '_{}'.format(loc.lower()):
- if power_name:
- map_node.setAttribute('class', power_name.lower())
- else:
- map_node.setAttribute('class', 'nopower')
+ map_layer = child_node
+ break
+
+ if map_layer:
+ for map_node in map_layer.childNodes:
+ if (map_node.nodeName in ('g', 'path', 'polygon')
+ and map_node.getAttribute('id') == '_{}'.format(loc.lower())):
+
+ # Province is a polygon - Setting influence directly
+ if map_node.nodeName in ('path', 'polygon'):
+ map_node.setAttribute('class', class_name)
+ return xml_map
+
+ # Otherwise, map node is a 'g' node.
+ node_edited = False
+ for sub_node in map_node.childNodes:
+ if sub_node.nodeName in ('path', 'polygon') and sub_node.getAttribute('class') != 'water':
+ node_edited = True
+ sub_node.setAttribute('class', class_name)
+ if node_edited:
return xml_map
# Returning
@@ -349,7 +317,7 @@ class Renderer():
:param current_phase: The current phase (e.g. 'S1901M)
:return: Nothing
"""
- current_phase = 'FINAL' if current_phase[0] == '?' else current_phase
+ current_phase = 'FINAL' if current_phase[0] == '?' or current_phase == 'COMPLETED' else current_phase
for child_node in xml_map.getElementsByTagName('svg')[0].childNodes:
if child_node.nodeName == 'text' and _attr(child_node, 'id') == 'CurrentPhase':
child_node.childNodes[0].nodeValue = current_phase
@@ -380,32 +348,22 @@ class Renderer():
:param power_name: The name of the power owning the unit
:return: Nothing
"""
- # Calculating polygon coord
- polygon_coord = []
- loc_x = _offset(self.metadata['coord'][loc]['unit'][0], 8.5)
- loc_y = _offset(self.metadata['coord'][loc]['unit'][1], 9.5)
- for offset in [(13.8, -33.3), (33.3, -13.8), (33.3, 13.8), (13.8, 33.3), (-13.8, 33.3), (-33.3, 13.8),
- (-33.3, -13.8), (-13.8, -33.3)]:
- polygon_coord += [_offset(loc_x, offset[0]) + ',' + _offset(loc_y, offset[1])]
-
- # Building polygon
- g_node = xml_map.createElement('g')
-
- poly_1 = xml_map.createElement('polygon')
- poly_1.setAttribute('stroke-width', '10')
- poly_1.setAttribute('class', 'varwidthshadow')
- poly_1.setAttribute('points', ' '.join(polygon_coord))
-
- poly_2 = xml_map.createElement('polygon')
- poly_2.setAttribute('stroke-width', '6')
- poly_2.setAttribute('class', 'varwidthorder')
- poly_2.setAttribute('points', ' '.join(polygon_coord))
- poly_2.setAttribute('stroke', self.metadata['color'][power_name])
+ # Symbols
+ symbol = 'HoldUnit'
+ loc_x, loc_y = self._center_symbol_around_unit(loc, False, symbol)
- g_node.appendChild(poly_1)
- g_node.appendChild(poly_2)
+ # Creating nodes
+ g_node = xml_map.createElement('g')
+ g_node.setAttribute('stroke', self.metadata['color'][power_name])
+ symbol_node = xml_map.createElement('use')
+ symbol_node.setAttribute('x', loc_x)
+ symbol_node.setAttribute('y', loc_y)
+ symbol_node.setAttribute('height', self.metadata['symbol_size'][symbol][0])
+ symbol_node.setAttribute('width', self.metadata['symbol_size'][symbol][1])
+ symbol_node.setAttribute('xlink:href', '#{}'.format(symbol))
# Inserting
+ g_node.appendChild(symbol_node)
for child_node in xml_map.getElementsByTagName('svg')[0].childNodes:
if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'OrderLayer':
for layer_node in child_node.childNodes:
@@ -424,58 +382,49 @@ class Renderer():
:param power_name: The power name issuing the move order
:return: Nothing
"""
- loc_x = _offset(self.metadata['coord'][loc]['unit'][0], 10)
- loc_y = _offset(self.metadata['coord'][loc]['unit'][1], 10)
- dest_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 10)
- dest_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 10)
+ # Symbols
+ symbol = 'SupportHoldUnit'
+ symbol_loc_x, symbol_loc_y = self._center_symbol_around_unit(dest_loc, False, symbol)
+ symbol_node = xml_map.createElement('use')
+ symbol_node.setAttribute('x', symbol_loc_x)
+ symbol_node.setAttribute('y', symbol_loc_y)
+ symbol_node.setAttribute('height', self.metadata['symbol_size'][symbol][0])
+ symbol_node.setAttribute('width', self.metadata['symbol_size'][symbol][1])
+ symbol_node.setAttribute('xlink:href', '#{}'.format(symbol))
+
+ loc_x, loc_y = self._get_unit_center(loc, False)
+ dest_loc_x, dest_loc_y = self._get_unit_center(dest_loc, False)
# Adjusting destination
- delta_x = float(dest_loc_x) - float(loc_x)
- delta_y = float(dest_loc_y) - float(loc_y)
- vector_length = (delta_x ** 2. + delta_y ** 2.) ** 0.5
- dest_loc_x = str(round(float(loc_x) + (vector_length - 35.) / vector_length * delta_x, 2))
- dest_loc_y = str(round(float(loc_y) + (vector_length - 35.) / vector_length * delta_y, 2))
-
- # Getting polygon coordinates
- polygon_coord = []
- poly_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 8.5)
- poly_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 9.5)
- for offset in [(15.9, -38.3), (38.3, -15.9), (38.3, 15.9), (15.9, 38.3), (-15.9, 38.3), (-38.3, 15.9),
- (-38.3, -15.9), (-15.9, -38.3)]:
- polygon_coord += [_offset(poly_loc_x, offset[0]) + ',' + _offset(poly_loc_y, offset[1])]
+ delta_x = dest_loc_x - loc_x
+ delta_y = dest_loc_y - loc_y
+ vector_length = (delta_x ** 2 + delta_y ** 2) ** 0.5
+ delta_dec = float(self.metadata['symbol_size'][symbol][1]) / 2
+ dest_loc_x = round(loc_x + (vector_length - delta_dec) / vector_length * delta_x, 2)
+ dest_loc_y = round(loc_y + (vector_length - delta_dec) / vector_length * delta_y, 2)
# Creating nodes
g_node = xml_map.createElement('g')
+ g_node.setAttribute('stroke', self.metadata['color'][power_name])
shadow_line = xml_map.createElement('line')
- shadow_line.setAttribute('x1', loc_x)
- shadow_line.setAttribute('y1', loc_y)
- shadow_line.setAttribute('x2', dest_loc_x)
- shadow_line.setAttribute('y2', dest_loc_y)
+ shadow_line.setAttribute('x1', str(loc_x))
+ shadow_line.setAttribute('y1', str(loc_y))
+ shadow_line.setAttribute('x2', str(dest_loc_x))
+ shadow_line.setAttribute('y2', str(dest_loc_y))
shadow_line.setAttribute('class', 'shadowdash')
support_line = xml_map.createElement('line')
- support_line.setAttribute('x1', loc_x)
- support_line.setAttribute('y1', loc_y)
- support_line.setAttribute('x2', dest_loc_x)
- support_line.setAttribute('y2', dest_loc_y)
+ support_line.setAttribute('x1', str(loc_x))
+ support_line.setAttribute('y1', str(loc_y))
+ support_line.setAttribute('x2', str(dest_loc_x))
+ support_line.setAttribute('y2', str(dest_loc_y))
support_line.setAttribute('class', 'supportorder')
- support_line.setAttribute('stroke', self.metadata['color'][power_name])
-
- shadow_poly = xml_map.createElement('polygon')
- shadow_poly.setAttribute('class', 'shadowdash')
- shadow_poly.setAttribute('points', ' '.join(polygon_coord))
-
- support_poly = xml_map.createElement('polygon')
- support_poly.setAttribute('class', 'supportorder')
- support_poly.setAttribute('points', ' '.join(polygon_coord))
- support_poly.setAttribute('stroke', self.metadata['color'][power_name])
# Inserting
g_node.appendChild(shadow_line)
g_node.appendChild(support_line)
- g_node.appendChild(shadow_poly)
- g_node.appendChild(support_poly)
+ g_node.appendChild(symbol_node)
for child_node in xml_map.getElementsByTagName('svg')[0].childNodes:
if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'OrderLayer':
@@ -495,21 +444,22 @@ class Renderer():
:param power_name: The power name issuing the move order
:return: Nothing
"""
- if self.game.get_current_phase()[-1] == 'R':
- src_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], -2.5)
- src_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], -2.5)
- else:
- src_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], 10)
- src_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], 10)
- dest_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 10)
- dest_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 10)
+ is_dislodged = self.game.get_current_phase()[-1] == 'R'
+ src_loc_x, src_loc_y = self._get_unit_center(src_loc, is_dislodged)
+ dest_loc_x, dest_loc_y = self._get_unit_center(dest_loc, is_dislodged)
# Adjusting destination
- delta_x = float(dest_loc_x) - float(src_loc_x)
- delta_y = float(dest_loc_y) - float(src_loc_y)
- vector_length = (delta_x ** 2. + delta_y ** 2.) ** 0.5
- dest_loc_x = str(round(float(src_loc_x) + (vector_length - 30.) / vector_length * delta_x, 2))
- dest_loc_y = str(round(float(src_loc_y) + (vector_length - 30.) / vector_length * delta_y, 2))
+ delta_x = dest_loc_x - src_loc_x
+ delta_y = dest_loc_y - src_loc_y
+ vector_length = (delta_x ** 2 + delta_y ** 2) ** 0.5
+ delta_dec = float(self.metadata['symbol_size'][ARMY][1]) / 2 + 2 * self._colored_stroke_width()
+ dest_loc_x = str(round(src_loc_x + (vector_length - delta_dec) / vector_length * delta_x, 2))
+ dest_loc_y = str(round(src_loc_y + (vector_length - delta_dec) / vector_length * delta_y, 2))
+
+ src_loc_x = str(src_loc_x)
+ src_loc_y = str(src_loc_y)
+ dest_loc_x = str(dest_loc_x)
+ dest_loc_y = str(dest_loc_y)
# Creating nodes
g_node = xml_map.createElement('g')
@@ -520,7 +470,7 @@ class Renderer():
line_with_shadow.setAttribute('x2', dest_loc_x)
line_with_shadow.setAttribute('y2', dest_loc_y)
line_with_shadow.setAttribute('class', 'varwidthshadow')
- line_with_shadow.setAttribute('stroke-width', '10')
+ line_with_shadow.setAttribute('stroke-width', str(self._plain_stroke_width()))
line_with_arrow = xml_map.createElement('line')
line_with_arrow.setAttribute('x1', src_loc_x)
@@ -529,7 +479,7 @@ class Renderer():
line_with_arrow.setAttribute('y2', dest_loc_y)
line_with_arrow.setAttribute('class', 'varwidthorder')
line_with_arrow.setAttribute('stroke', self.metadata['color'][power_name])
- line_with_arrow.setAttribute('stroke-width', '6')
+ line_with_arrow.setAttribute('stroke-width', str(self._colored_stroke_width()))
line_with_arrow.setAttribute('marker-end', 'url(#arrow)')
# Inserting
@@ -554,19 +504,17 @@ class Renderer():
:param power_name: The power name issuing the move order
:return: Nothing
"""
- loc_x = _offset(self.metadata['coord'][loc]['unit'][0], 10)
- loc_y = _offset(self.metadata['coord'][loc]['unit'][1], 10)
- src_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], 10)
- src_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], 10)
- dest_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 10)
- dest_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 10)
+ loc_x, loc_y = self._get_unit_center(loc, False)
+ src_loc_x, src_loc_y = self._get_unit_center(src_loc, False)
+ dest_loc_x, dest_loc_y = self._get_unit_center(dest_loc, False)
# Adjusting destination
- delta_x = float(dest_loc_x) - float(src_loc_x)
- delta_y = float(dest_loc_y) - float(src_loc_y)
- vector_length = (delta_x ** 2. + delta_y ** 2.) ** 0.5
- dest_loc_x = str(round(float(src_loc_x) + (vector_length - 30.) / vector_length * delta_x, 2))
- dest_loc_y = str(round(float(src_loc_y) + (vector_length - 30.) / vector_length * delta_y, 2))
+ delta_x = dest_loc_x - src_loc_x
+ delta_y = dest_loc_y - src_loc_y
+ vector_length = (delta_x ** 2 + delta_y ** 2) ** 0.5
+ delta_dec = float(self.metadata['symbol_size'][ARMY][1]) / 2 + 2 * self._colored_stroke_width()
+ dest_loc_x = str(round(src_loc_x + (vector_length - delta_dec) / vector_length * delta_x, 2))
+ dest_loc_y = str(round(src_loc_y + (vector_length - delta_dec) / vector_length * delta_y, 2))
# Creating nodes
g_node = xml_map.createElement('g')
@@ -615,46 +563,57 @@ class Renderer():
:param power_name: The power name issuing the convoy order
:return: Nothing
"""
- loc_x = _offset(self.metadata['coord'][loc]['unit'][0], 10)
- loc_y = _offset(self.metadata['coord'][loc]['unit'][1], 10)
- src_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], 10)
- src_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], 10)
- dest_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 10)
- dest_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 10)
+ symbol = 'ConvoyTriangle'
+ symbol_loc_x, symbol_loc_y = self._center_symbol_around_unit(src_loc, False, symbol)
+ symbol_height = float(self.metadata['symbol_size'][symbol][0])
+ symbol_width = float(self.metadata['symbol_size'][symbol][1])
+ triangle = EquilateralTriangle(x_top=float(symbol_loc_x) + symbol_width / 2,
+ y_top=float(symbol_loc_y),
+ x_right=float(symbol_loc_x) + symbol_width,
+ y_right=float(symbol_loc_y) + symbol_height,
+ x_left=float(symbol_loc_x),
+ y_left=float(symbol_loc_y) + symbol_height)
+ symbol_loc_y = str(float(symbol_loc_y) - float(self.metadata['symbol_size'][symbol][0]) / 6)
+
+ loc_x, loc_y = self._get_unit_center(loc, False)
+ src_loc_x, src_loc_y = self._get_unit_center(src_loc, False)
+ dest_loc_x, dest_loc_y = self._get_unit_center(dest_loc, False)
# Adjusting starting arrow (from convoy to start location)
# This is to avoid the end of the arrow conflicting with the convoy triangle
- src_delta_x = float(src_loc_x) - float(loc_x)
- src_delta_y = float(src_loc_y) - float(loc_y)
- src_vector_length = (src_delta_x ** 2. + src_delta_y ** 2.) ** 0.5
- src_loc_x_1 = str(round(float(loc_x) + (src_vector_length - 30.) / src_vector_length * src_delta_x, 2))
- src_loc_y_1 = str(round(float(loc_y) + (src_vector_length - 30.) / src_vector_length * src_delta_y, 2))
+ src_loc_x_1, src_loc_y_1 = triangle.intersection(loc_x, loc_y)
+ src_loc_x_1 = str(src_loc_x_1)
+ src_loc_y_1 = str(src_loc_y_1)
# Adjusting destination arrow (from start location to destination location)
# This is to avoid the start of the arrow conflicting with the convoy triangle
- dest_delta_x = float(src_loc_x) - float(dest_loc_x)
- dest_delta_y = float(src_loc_y) - float(dest_loc_y)
- dest_vector_length = (dest_delta_x ** 2. + dest_delta_y ** 2.) ** 0.5
- src_loc_x_2 = str(round(float(dest_loc_x) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_x, 2))
- src_loc_y_2 = str(round(float(dest_loc_y) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_y, 2))
+ src_loc_x_2, src_loc_y_2 = triangle.intersection(dest_loc_x, dest_loc_y)
+ src_loc_x_2 = str(src_loc_x_2)
+ src_loc_y_2 = str(src_loc_y_2)
# Adjusting destination arrow (from start location to destination location)
# This is to avoid the start of the arrow conflicting with the convoy triangle
- dest_delta_x = float(dest_loc_x) - float(src_loc_x)
- dest_delta_y = float(dest_loc_y) - float(src_loc_y)
- dest_vector_length = (dest_delta_x ** 2. + dest_delta_y ** 2.) ** 0.5
- dest_loc_x = str(round(float(src_loc_x) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_x, 2))
- dest_loc_y = str(round(float(src_loc_y) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_y, 2))
-
- # Getting convoy triangle coordinates
- triangle_coord = []
- triangle_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], 10)
- triangle_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], 10)
- for offset in [(0, -38.3), (33.2, 19.1), (-33.2, 19.1)]:
- triangle_coord += [_offset(triangle_loc_x, offset[0]) + ',' + _offset(triangle_loc_y, offset[1])]
+ dest_delta_x = dest_loc_x - src_loc_x
+ dest_delta_y = dest_loc_y - src_loc_y
+ dest_vector_length = (dest_delta_x ** 2 + dest_delta_y ** 2) ** 0.5
+ delta_dec = float(self.metadata['symbol_size'][ARMY][1]) / 2 + 2 * self._colored_stroke_width()
+ dest_loc_x = str(round(src_loc_x + (dest_vector_length - delta_dec) / dest_vector_length * dest_delta_x, 2))
+ dest_loc_y = str(round(src_loc_y + (dest_vector_length - delta_dec) / dest_vector_length * dest_delta_y, 2))
+
+ loc_x = str(loc_x)
+ loc_y = str(loc_y)
+
+ # Generating convoy triangle node
+ symbol_node = xml_map.createElement('use')
+ symbol_node.setAttribute('x', symbol_loc_x)
+ symbol_node.setAttribute('y', symbol_loc_y)
+ symbol_node.setAttribute('height', self.metadata['symbol_size'][symbol][0])
+ symbol_node.setAttribute('width', self.metadata['symbol_size'][symbol][1])
+ symbol_node.setAttribute('xlink:href', '#{}'.format(symbol))
# Creating nodes
g_node = xml_map.createElement('g')
+ g_node.setAttribute('stroke', self.metadata['color'][power_name])
src_shadow_line = xml_map.createElement('line')
src_shadow_line.setAttribute('x1', loc_x)
@@ -663,20 +622,19 @@ class Renderer():
src_shadow_line.setAttribute('y2', src_loc_y_1)
src_shadow_line.setAttribute('class', 'shadowdash')
- dest_shadow_line = xml_map.createElement('line')
- dest_shadow_line.setAttribute('x1', src_loc_x_2)
- dest_shadow_line.setAttribute('y1', src_loc_y_2)
- dest_shadow_line.setAttribute('x2', dest_loc_x)
- dest_shadow_line.setAttribute('y2', dest_loc_y)
- dest_shadow_line.setAttribute('class', 'shadowdash')
-
src_convoy_line = xml_map.createElement('line')
src_convoy_line.setAttribute('x1', loc_x)
src_convoy_line.setAttribute('y1', loc_y)
src_convoy_line.setAttribute('x2', src_loc_x_1)
src_convoy_line.setAttribute('y2', src_loc_y_1)
src_convoy_line.setAttribute('class', 'convoyorder')
- src_convoy_line.setAttribute('stroke', self.metadata['color'][power_name])
+
+ dest_shadow_line = xml_map.createElement('line')
+ dest_shadow_line.setAttribute('x1', src_loc_x_2)
+ dest_shadow_line.setAttribute('y1', src_loc_y_2)
+ dest_shadow_line.setAttribute('x2', dest_loc_x)
+ dest_shadow_line.setAttribute('y2', dest_loc_y)
+ dest_shadow_line.setAttribute('class', 'shadowdash')
dest_convoy_line = xml_map.createElement('line')
dest_convoy_line.setAttribute('x1', src_loc_x_2)
@@ -684,25 +642,14 @@ class Renderer():
dest_convoy_line.setAttribute('x2', dest_loc_x)
dest_convoy_line.setAttribute('y2', dest_loc_y)
dest_convoy_line.setAttribute('class', 'convoyorder')
- dest_convoy_line.setAttribute('stroke', self.metadata['color'][power_name])
dest_convoy_line.setAttribute('marker-end', 'url(#arrow)')
- shadow_poly = xml_map.createElement('polygon')
- shadow_poly.setAttribute('class', 'shadowdash')
- shadow_poly.setAttribute('points', ' '.join(triangle_coord))
-
- convoy_poly = xml_map.createElement('polygon')
- convoy_poly.setAttribute('class', 'convoyorder')
- convoy_poly.setAttribute('points', ' '.join(triangle_coord))
- convoy_poly.setAttribute('stroke', self.metadata['color'][power_name])
-
# Inserting
g_node.appendChild(src_shadow_line)
g_node.appendChild(dest_shadow_line)
g_node.appendChild(src_convoy_line)
g_node.appendChild(dest_convoy_line)
- g_node.appendChild(shadow_poly)
- g_node.appendChild(convoy_poly)
+ g_node.appendChild(symbol_node)
for child_node in xml_map.getElementsByTagName('svg')[0].childNodes:
if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'OrderLayer':
for layer_node in child_node.childNodes:
@@ -721,15 +668,14 @@ class Renderer():
:param power_name: The name of the power building the unit
:return: Nothing
"""
- loc_x = _offset(self.metadata['coord'][loc]['unit'][0], -11.5)
- loc_y = _offset(self.metadata['coord'][loc]['unit'][1], - 10.)
- build_loc_x = _offset(self.metadata['coord'][loc]['unit'][0], -20.5)
- build_loc_y = _offset(self.metadata['coord'][loc]['unit'][1], -20.5)
-
# Symbols
symbol = ARMY if unit_type == 'A' else FLEET
build_symbol = 'BuildUnit'
+ loc_x = self.metadata['coord'][loc]['unit'][0]
+ loc_y = self.metadata['coord'][loc]['unit'][1]
+ build_loc_x, build_loc_y = self._center_symbol_around_unit(loc, False, build_symbol)
+
# Creating nodes
g_node = xml_map.createElement('g')
@@ -765,15 +711,9 @@ class Renderer():
:param loc: The province where the unit is disbanded (e.g. 'PAR')
:return: Nothing
"""
- if self.game.get_current_phase()[-1] == 'R':
- loc_x = _offset(self.metadata['coord'][loc]['unit'][0], -29.)
- loc_y = _offset(self.metadata['coord'][loc]['unit'][1], -27.5)
- else:
- loc_x = _offset(self.metadata['coord'][loc]['unit'][0], -16.5)
- loc_y = _offset(self.metadata['coord'][loc]['unit'][1], -15.)
-
# Symbols
symbol = 'RemoveUnit'
+ loc_x, loc_y = self._center_symbol_around_unit(loc, self.game.get_current_phase()[-1] == 'R', symbol)
# Creating nodes
g_node = xml_map.createElement('g')
@@ -793,3 +733,44 @@ class Renderer():
# Returning
return xml_map
+
+ def _center_symbol_around_unit(self, loc, is_dislodged, symbol): # type: (str, bool, str) -> Tuple[str, str]
+ """ Compute top-left coordinates of a symbol to be centered around a unit.
+ :param loc: unit location (e.g. 'PAR')
+ :param is_dislodged: boolean to tell if unit is dislodged
+ :param symbol: symbol identifier (e.g. 'HoldUnit')
+ :return: a couple of coordinates (x, y) as string values
+ """
+ key = 'disl' if is_dislodged else 'unit'
+ unit_x, unit_y = self.metadata['coord'][loc][key]
+ unit_height, unit_width = self.metadata['symbol_size'][ARMY]
+ symbol_height, symbol_width = self.metadata['symbol_size'][symbol]
+ return (
+ str(float(unit_x) + float(unit_width) / 2 - float(symbol_width) / 2),
+ str(float(unit_y) + float(unit_height) / 2 - float(symbol_height) / 2)
+ )
+
+ def _get_unit_center(self, loc, is_dislodged): # type: (str, bool) -> Tuple[float, float]
+ """ Compute coordinates of unit center.
+ :param loc: unit location
+ :param is_dislodged: boolean to tell if unit is dislodged
+ :return: a couple of coordinates (x, y) as floating values
+ """
+ unit_x, unit_y = self.metadata['coord'][loc]['disl' if is_dislodged else 'unit']
+ unit_height, unit_width = self.metadata['symbol_size'][ARMY]
+ return (
+ float(unit_x) + float(unit_width) / 2,
+ float(unit_y) + float(unit_height) / 2
+ )
+
+ def _plain_stroke_width(self): # type: () -> float
+ """ Return generic stroke width for plain lines.
+ :return: stroke width as floating value.
+ """
+ return float(self.metadata['symbol_size']['Stroke'][0])
+
+ def _colored_stroke_width(self): # type: () -> float
+ """ Return generic stroke width for colored or textured lines.
+ :return: stroke width as floating value.
+ """
+ return float(self.metadata['symbol_size']['Stroke'][1])