exploration.display

  • Authors: Peter Mawhorter
  • Consulted:
  • Date: 2022-4-15
  • Purpose: Code for visualizing decision graphs and explorations.

Defines functions for graph layout and drawing for exploration.core.DecisionGraph objects, and for visualizing DiscreteExploration objects as an animation or sequence of DecisionGraph images. Since the focal example is Metroidvania games, we use a square-tiles-based map system as the basis for layout. This will not work well for non-planar graphs.

TODO: Anchor-free localization implementation?

 1"""
 2- Authors: Peter Mawhorter
 3- Consulted:
 4- Date: 2022-4-15
 5- Purpose: Code for visualizing decision graphs and explorations.
 6
 7Defines functions for graph layout and drawing for
 8`exploration.core.DecisionGraph` objects, and for visualizing
 9`DiscreteExploration` objects as an animation or sequence of
10`DecisionGraph` images. Since the focal example is Metroidvania games, we
11use a square-tiles-based map system as the basis for layout. This will
12not work well for non-planar graphs.
13
14TODO: Anchor-free localization implementation?
15"""
16
17from typing import Dict, Tuple
18
19from . import base
20from . import core
21
22BlockPosition = Tuple[int, int, int]
23"""
24A type alias: block positions indicate the x/y coordinates of the
25north-west corner of a node, as well as its side length in grid units
26(all nodes are assumed to be square).
27"""
28
29BlockLayout = Dict[base.DecisionID, BlockPosition]
30"""
31A type alias: block layouts map each decision in a particular graph to a
32block position which indicates both position and size in a unit grid.
33"""
34
35
36def roomSize(connections: int) -> int:
37    """
38    For a room with the given number of connections, returns the side
39    length of the smallest square which can accommodate that many
40    connections. Note that outgoing/incoming reciprocal pairs to/from a
41    single destination should only count as one connection, because they
42    don't need more than one space on the room perimeter. Even with zero
43    connections, we still return 1 as the room size.
44    """
45    if connections == 0:
46        return 1
47    return 1 + (connections - 1) // 4
48
49
50def expandBlocks(layout: BlockLayout) -> None:
51    """
52    Modifies the given block layout by adding extra space between each
53    positioned node: it triples the coordinates of each node, and then
54    shifts them south and east by 1 unit each, by maintaining the nodes
55    at their original sizes, TODO...
56    """
57    # TODO
58
59
60#def blockLayoutFor(region: core.DecisionGraph) -> BlockLayout:
61#    """
62#    Computes a unit-grid position and size for each room in an
63#    `exploration.core.DecisionGraph`, laying out the rooms as
64#    non-overlapping square blocks. In many cases, connections will be
65#    stretched across empty space, but no explicit space is reserved for
66#    connections.
67#    """
68#    # TODO
BlockPosition = typing.Tuple[int, int, int]

A type alias: block positions indicate the x/y coordinates of the north-west corner of a node, as well as its side length in grid units (all nodes are assumed to be square).

BlockLayout = typing.Dict[int, typing.Tuple[int, int, int]]

A type alias: block layouts map each decision in a particular graph to a block position which indicates both position and size in a unit grid.

def roomSize(connections: int) -> int:
37def roomSize(connections: int) -> int:
38    """
39    For a room with the given number of connections, returns the side
40    length of the smallest square which can accommodate that many
41    connections. Note that outgoing/incoming reciprocal pairs to/from a
42    single destination should only count as one connection, because they
43    don't need more than one space on the room perimeter. Even with zero
44    connections, we still return 1 as the room size.
45    """
46    if connections == 0:
47        return 1
48    return 1 + (connections - 1) // 4

For a room with the given number of connections, returns the side length of the smallest square which can accommodate that many connections. Note that outgoing/incoming reciprocal pairs to/from a single destination should only count as one connection, because they don't need more than one space on the room perimeter. Even with zero connections, we still return 1 as the room size.

def expandBlocks(layout: Dict[int, Tuple[int, int, int]]) -> None:
51def expandBlocks(layout: BlockLayout) -> None:
52    """
53    Modifies the given block layout by adding extra space between each
54    positioned node: it triples the coordinates of each node, and then
55    shifts them south and east by 1 unit each, by maintaining the nodes
56    at their original sizes, TODO...
57    """
58    # TODO

Modifies the given block layout by adding extra space between each positioned node: it triples the coordinates of each node, and then shifts them south and east by 1 unit each, by maintaining the nodes at their original sizes, TODO...