diff options
author | notoraptor <stevenbocco@gmail.com> | 2019-08-14 12:22:22 -0400 |
---|---|---|
committer | Philip Paquette <pcpaquette@gmail.com> | 2019-08-14 12:40:01 -0400 |
commit | 5c3bd9b3802e2001a7e77baf2911386135a03839 (patch) | |
tree | e641744650b05cddc85bc60c2d7e2d6fe2d88b47 /diplomacy/engine/renderer.py | |
parent | 5acb4ff23be4757a49b234f93928f13c436b60c6 (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.py | 399 |
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]) |