Source code for landlab.graph.hex.hex

r"""
Examples
--------

::

        * - *
       / \ / \
      * - * - *
     / \ / \ / \
    * - * - * - *
     \ / \ / \ /
      * - * - *
       \ / \ /
        * - *

>>> from landlab.graph import TriGraph
>>> graph = TriGraph((5, 2), node_layout="hex", sort=True)
>>> graph.number_of_nodes
14
>>> graph.x_of_node
array([1. , 2. ,
       0.5, 1.5, 2.5,
       0. , 1. , 2. , 3. ,
       0.5, 1.5, 2.5,
       1. , 2. ])
>>> graph.number_of_links
29
>>> graph.number_of_patches
16

::

    * - * - * - *
     \ / \ / \ / \
      * - * - * - *
     / \ / \ / \ /
    * - * - * - *

>>> from landlab.graph import TriGraph
>>> graph = TriGraph((3, 4), orientation="horizontal", node_layout="rect", sort=True)
>>> graph.number_of_nodes
12
>>> graph.x_of_node.reshape((3, 4))
array([[0. , 1. , 2. , 3. ],
       [0.5, 1.5, 2.5, 3.5],
       [0. , 1. , 2. , 3. ]])
>>> graph.number_of_links
23
>>> graph.number_of_patches
12
"""

from functools import cached_property

import numpy as np

from ...core.utils import as_id_array
from ...grid.linkorientation import LinkOrientation
from ...utils.decorators import cache_result_in_object
from ...utils.decorators import make_return_array_immutable
from ..graph import Graph
from ..voronoi.voronoi import DelaunayGraph


[docs] class HorizontalRectTriGraphCython:
[docs] @staticmethod def xy_of_node(shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0)): from .ext.hex import fill_xy_of_node_rect_horizontal x_of_node = np.empty(shape[0] * shape[1], dtype=float) y_of_node = np.empty(shape[0] * shape[1], dtype=float) fill_xy_of_node_rect_horizontal(shape, x_of_node, y_of_node) x_of_node[:] *= spacing y_of_node[:] *= spacing * np.sin(np.pi / 3.0) x_of_node[:] += xy_of_lower_left[0] y_of_node[:] += xy_of_lower_left[1] return x_of_node, y_of_node
[docs] class VerticalRectTriGraphCython:
[docs] @staticmethod def xy_of_node(shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0)): from .ext.hex import fill_xy_of_node_rect_vertical n_rows, n_cols = shape x_spacing = np.sin(np.pi / 3.0) * spacing y_spacing = spacing x_of_node = np.empty(n_rows * n_cols, dtype=float) y_of_node = np.empty(n_rows * n_cols, dtype=float) fill_xy_of_node_rect_vertical(shape, x_of_node, y_of_node) x_of_node *= x_spacing y_of_node *= y_spacing x_of_node += xy_of_lower_left[0] y_of_node += xy_of_lower_left[1] return x_of_node, y_of_node x_of_node[:, (n_cols + 1) // 2 :] = ( np.arange(n_cols // 2) * x_spacing * 2.0 + x_spacing + xy_of_lower_left[0] ) x_of_node[:, : (n_cols + 1) // 2] = ( np.arange((n_cols + 1) // 2) * x_spacing * 2.0 + xy_of_lower_left[0] ) y_of_node[:, : (n_cols + 1) // 2] = ( np.arange(n_rows) * y_spacing + xy_of_lower_left[1] ).reshape((n_rows, 1)) y_of_node[:, (n_cols + 1) // 2 :] = ( np.arange(n_rows) * y_spacing + xy_of_lower_left[1] + y_spacing * 0.5 ).reshape((n_rows, 1)) return x_of_node.reshape(-1), y_of_node.reshape(-1)
[docs] class HorizontalHexTriGraphCython:
[docs] @staticmethod def xy_of_node(shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0)): from .ext.hex import fill_xy_of_node_hex_horizontal n_rows, n_cols = shape n_nodes = n_rows * n_cols + (n_rows // 2) ** 2 x_of_node = np.empty(n_nodes, dtype=float) y_of_node = np.empty(n_nodes, dtype=float) fill_xy_of_node_hex_horizontal(shape, x_of_node, y_of_node) x_of_node[:] *= spacing y_of_node[:] *= spacing * np.sin(np.pi / 3.0) x_of_node[:] += xy_of_lower_left[0] y_of_node[:] += xy_of_lower_left[1] return x_of_node, y_of_node
[docs] class VerticalHexTriGraphCython:
[docs] @staticmethod def xy_of_node(shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0)): from .ext.hex import fill_xy_of_node_hex_vertical n_rows, n_cols = shape n_nodes = n_cols * n_rows + (n_cols // 2) ** 2 x_of_node = np.empty(n_nodes, dtype=float) y_of_node = np.empty(n_nodes, dtype=float) fill_xy_of_node_hex_vertical(shape, x_of_node, y_of_node) x_of_node[:] *= spacing * np.sin(np.pi / 3.0) y_of_node[:] *= spacing x_of_node[:] += xy_of_lower_left[0] y_of_node[:] += xy_of_lower_left[1] return x_of_node, y_of_node
[docs] class HorizontalRectTriGraph:
[docs] @staticmethod def number_of_nodes(shape): n_rows, n_cols = shape return n_rows * n_cols
[docs] @staticmethod def xy_of_node(shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0)): n_rows, n_cols = shape x_spacing, y_spacing = spacing, spacing * np.sin(np.pi / 3.0) x_of_node, y_of_node = np.meshgrid( np.arange(n_cols) * x_spacing + xy_of_lower_left[0], np.arange(n_rows) * y_spacing + xy_of_lower_left[1], ) x_of_node[1::2] += spacing * 0.5 return x_of_node.reshape(-1), y_of_node.reshape(-1)
[docs] @staticmethod def corner_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import HorizontalRectTriGraph >>> HorizontalRectTriGraph.corner_nodes((3, 4)) (11, 8, 0, 3) >>> HorizontalRectTriGraph.corner_nodes((3, 2)) (5, 4, 0, 1) >>> HorizontalRectTriGraph.corner_nodes((7, 1)) (6, 6, 0, 0) >>> HorizontalRectTriGraph.corner_nodes((1, 3)) (2, 0, 0, 2) """ n_rows, n_cols = shape return (n_rows * n_cols - 1, n_cols * (n_rows - 1), 0, n_cols - 1)
[docs] @staticmethod def number_of_perimeter_nodes(shape): if 1 in shape: return np.prod(shape) return 2 * shape[0] + 2 * (shape[1] - 2)
[docs] @staticmethod def perimeter_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import HorizontalRectTriGraph >>> HorizontalRectTriGraph.perimeter_nodes((3, 2)) array([1, 3, 5, 4, 2, 0]) """ return np.concatenate(HorizontalRectTriGraph.nodes_at_edge(shape))
[docs] @staticmethod def nodes_at_edge(shape): n_rows, n_cols = shape if n_rows == n_cols == 1: return (np.array([0]),) + (np.array([], dtype=int),) * 3 ( northeast, northwest, southwest, southeast, ) = HorizontalRectTriGraph.corner_nodes(shape) if n_rows > 1: south = np.arange(southwest, southeast) else: south = np.array([southwest], dtype=int) if n_cols > 1: west = np.arange(northwest, southwest, -n_cols) else: west = np.array([northwest], dtype=int) return ( np.arange(southeast, northeast, n_cols), np.arange(northeast, northwest, -1), west, south, )
[docs] class VerticalRectTriGraph:
[docs] @staticmethod def number_of_nodes(shape): n_rows, n_cols = shape return n_rows * n_cols
[docs] @staticmethod def xy_of_node(shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0)): n_rows, n_cols = shape x_spacing, y_spacing = spacing * np.sin(np.pi / 3.0), spacing x_of_node = np.empty((n_rows, n_cols), dtype=float) y_of_node = np.empty((n_rows, n_cols), dtype=float) x_of_node[:, (n_cols + 1) // 2 :] = ( np.arange(n_cols // 2) * x_spacing * 2.0 + x_spacing + xy_of_lower_left[0] ) x_of_node[:, : (n_cols + 1) // 2] = ( np.arange((n_cols + 1) // 2) * x_spacing * 2.0 + xy_of_lower_left[0] ) y_of_node[:, : (n_cols + 1) // 2] = ( np.arange(n_rows) * y_spacing + xy_of_lower_left[1] ).reshape((n_rows, 1)) y_of_node[:, (n_cols + 1) // 2 :] = ( np.arange(n_rows) * y_spacing + xy_of_lower_left[1] + y_spacing * 0.5 ).reshape((n_rows, 1)) return x_of_node.reshape(-1), y_of_node.reshape(-1)
[docs] @staticmethod def corner_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import VerticalRectTriGraph >>> VerticalRectTriGraph.corner_nodes((4, 3)) (10, 9, 0, 1) >>> VerticalRectTriGraph.corner_nodes((4, 4)) (15, 12, 0, 3) >>> VerticalRectTriGraph.corner_nodes((3, 2)) (5, 4, 0, 1) >>> VerticalRectTriGraph.corner_nodes((7, 1)) (6, 6, 0, 0) >>> VerticalRectTriGraph.corner_nodes((1, 3)) (1, 0, 0, 1) >>> VerticalRectTriGraph.corner_nodes((2, 3)) (4, 3, 0, 1) """ n_rows, n_cols = shape if n_cols % 2 == 0: return (n_rows * n_cols - 1, n_cols * (n_rows - 1), 0, n_cols - 1) else: return ( n_rows * n_cols - 1 - n_cols // 2, n_cols * (n_rows - 1), 0, n_cols // 2, )
[docs] @staticmethod def number_of_perimeter_nodes(shape): if 1 in shape: return np.prod(shape) return 2 * shape[1] + 2 * (shape[0] - 2)
[docs] @staticmethod def perimeter_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import VerticalRectTriGraph >>> VerticalRectTriGraph.perimeter_nodes((3, 2)) array([1, 3, 5, 4, 2, 0]) >>> VerticalRectTriGraph.perimeter_nodes((2, 3)) array([1, 4, 5, 3, 0, 2]) >>> VerticalRectTriGraph.perimeter_nodes((2, 4)) array([3, 7, 5, 6, 4, 0, 2, 1]) """ return np.concatenate(VerticalRectTriGraph.nodes_at_edge(shape))
[docs] @staticmethod def nodes_at_edge(shape): n_rows, n_cols = shape if n_rows == n_cols == 1: return (np.array([0]),) + (np.array([], dtype=int),) * 3 n_nodes = n_rows * n_cols ( northeast, northwest, southwest, southeast, ) = VerticalRectTriGraph.corner_nodes(shape) if n_cols == 1: southwest = northwest - n_cols north = np.empty(n_cols - 1, dtype=int) north[::2] = n_nodes - n_cols // 2 + np.arange(n_cols // 2) north[1::2] = northwest + np.arange(1, n_cols - n_cols // 2) if n_rows > 1: south = np.empty(n_cols - 1, dtype=int) south[::2] = np.arange(0, n_cols // 2) south[1::2] = (n_cols + 1) // 2 + np.arange(n_cols - n_cols // 2 - 1) else: south = np.array([southwest], dtype=int) return ( np.arange(southeast, northeast, n_cols), north[::-1], np.arange(northwest, southwest, -n_cols), south, )
[docs] class HorizontalHexTriGraph:
[docs] @staticmethod def number_of_nodes(shape): n_rows, n_cols = shape return n_rows * n_cols + (n_rows // 2) ** 2
[docs] @staticmethod def xy_of_node(shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0)): n_rows, n_cols = shape x_spacing, y_spacing = spacing, spacing * np.sin(np.pi / 3.0) length_of_row = np.concatenate( ( np.arange((n_rows + 2) // 2) + n_cols, (n_rows + 2) // 2 + n_cols - 1 - np.arange(1, n_rows - (n_rows + 2) // 2 + 1), ) ) offset_to_row = np.concatenate((np.array([0]), length_of_row)).cumsum() rows = [ slice(start, end) for start, end in zip(offset_to_row[:-1], offset_to_row[1:]) ] y_of_node = np.empty(HorizontalHexTriGraph.number_of_nodes(shape), dtype=float) for row, inds in enumerate(rows): y_of_node[inds] = row * y_spacing + xy_of_lower_left[1] x_of_node = np.empty(HorizontalHexTriGraph.number_of_nodes(shape), dtype=float) x_of_row = ( np.abs((n_rows + 2) // 2 - 1 - np.arange(n_rows)) * x_spacing * 0.5 + xy_of_lower_left[0] ) for row, inds in enumerate(rows): x_of_node[inds] = x_of_row[row] + np.arange(length_of_row[row]) * x_spacing return x_of_node.reshape(-1), y_of_node.reshape(-1)
[docs] @staticmethod def corner_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import HorizontalHexTriGraph >>> HorizontalHexTriGraph.corner_nodes((3, 2)) (4, 6, 5, 2, 0, 1) >>> HorizontalHexTriGraph.corner_nodes((7, 1)) (9, 15, 15, 6, 0, 0) >>> HorizontalHexTriGraph.corner_nodes((6, 1)) (9, 14, 13, 6, 0, 0) >>> HorizontalHexTriGraph.corner_nodes((4, 2)) (8, 11, 9, 5, 0, 1) """ n_rows, n_cols = shape n_nodes_in_middle_row = n_rows // 2 + n_cols n_nodes = n_rows * n_cols + (n_rows // 2) ** 2 east = (n_nodes_in_middle_row + n_cols) * ((n_rows // 2 + 1) // 2) - 1 if (n_rows // 2) % 2 == 0: east += (n_nodes_in_middle_row + n_cols) // 2 return ( east, n_nodes - 1, n_nodes - (n_cols + (n_rows + 1) % 2), east - (n_nodes_in_middle_row - 1), 0, n_cols - 1, )
[docs] @staticmethod def number_of_perimeter_nodes(shape): if shape[0] == 1: return shape[1] return 2 * shape[0] + 2 * (shape[1] - 2) + (shape[0] + 1) % 2
[docs] @staticmethod def perimeter_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import HorizontalHexTriGraph >>> HorizontalHexTriGraph.perimeter_nodes((3, 2)) array([4, 6, 5, 2, 0, 1]) >>> HorizontalHexTriGraph.perimeter_nodes((1, 3)) array([2, 1, 0]) """ return np.concatenate(HorizontalHexTriGraph.nodes_at_edge(shape))
[docs] @staticmethod def nodes_at_edge(shape): """ Examples -------- >>> from landlab.graph.hex.hex import HorizontalHexTriGraph >>> HorizontalHexTriGraph.nodes_at_edge((5, 3)) (array([11, 15]), array([18, 17]), array([16, 12]), array([7, 3]), array([0, 1]), array([2, 6])) >>> HorizontalHexTriGraph.nodes_at_edge((4, 3)) (array([11]), array([15, 14, 13]), array([12]), array([7, 3]), array([0, 1]), array([2, 6])) """ n_rows, n_cols = shape ( east, northeast, northwest, west, southwest, southeast, ) = HorizontalHexTriGraph.corner_nodes(shape) if n_rows == 1: nodes_at_south_edge = np.asarray([southwest], dtype=int) else: nodes_at_south_edge = np.arange(southwest, southeast) return ( as_id_array( northeast - np.arange(northeast - northwest + 1, east - west + 1).cumsum()[::-1] ), np.arange(northeast, northwest, -1), as_id_array( west + np.arange(east - west + 1, northeast - northwest + 1, -1).cumsum()[ ::-1 ] ), as_id_array( southwest + np.arange(n_cols, n_rows // 2 + n_cols).cumsum()[::-1] ), nodes_at_south_edge, as_id_array( east - np.arange(n_cols + n_rows // 2, n_cols, -1).cumsum()[::-1] ), )
[docs] class VerticalHexTriGraph:
[docs] @staticmethod def number_of_nodes(shape): n_rows, n_cols = shape return n_rows * n_cols + (n_cols // 2) ** 2
[docs] @staticmethod def xy_of_node(shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0)): n_rows, n_cols = shape x_spacing, y_spacing = spacing * np.sin(np.pi / 3.0), spacing length_of_middle_rows = np.full(2 * n_rows - 1, n_cols // 2) if n_cols % 2 == 1: length_of_middle_rows[::2] += 1 length_of_row = np.concatenate( ( np.arange(1, n_cols // 2 + 1), length_of_middle_rows, np.arange(n_cols // 2, 0, -1), ) ) offset_to_row = np.concatenate((np.array([0]), length_of_row)).cumsum() rows = [ slice(start, end) for start, end in zip(offset_to_row[:-1], offset_to_row[1:]) ] y_of_node = np.empty(VerticalHexTriGraph.number_of_nodes(shape), dtype=float) for row, inds in enumerate(rows): y_of_node[inds] = row * y_spacing * 0.5 x_of_node = np.empty(VerticalHexTriGraph.number_of_nodes(shape), dtype=float) x_of_middle_rows = np.zeros(2 * n_rows - 1) x_of_middle_rows[1::2] += 1.0 x_of_row = ( np.concatenate( ( np.arange(n_cols // 2, 0, -1), x_of_middle_rows, np.arange(1, n_cols // 2 + 1), ) ) * x_spacing ) for row, inds in enumerate(rows): x_of_node[inds] = ( x_of_row[row] + np.arange(length_of_row[row]) * 2.0 * x_spacing ) x_of_node += xy_of_lower_left[0] y_of_node += xy_of_lower_left[1] return x_of_node.reshape(-1), y_of_node.reshape(-1)
[docs] @staticmethod def corner_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import VerticalHexTriGraph >>> VerticalHexTriGraph.corner_nodes((2, 5)) (10, 13, 8, 3, 0, 5) >>> VerticalHexTriGraph.corner_nodes((2, 3)) (5, 6, 4, 1, 0, 2) >>> VerticalHexTriGraph.corner_nodes((2, 4)) (10, 11, 7, 3, 0, 2) >>> VerticalHexTriGraph.corner_nodes((2, 2)) (4, 4, 3, 1, 0, 0) >>> VerticalHexTriGraph.corner_nodes((3, 1)) (2, 2, 2, 0, 0, 0) >>> VerticalHexTriGraph.corner_nodes((1, 3)) (2, 3, 1, 1, 0, 2) """ n_rows, n_cols = shape n_nodes = n_rows * n_cols + (n_cols // 2) ** 2 n = n_cols // 2 tri_nodes = (n + 1) * (n // 2) if n % 2 == 1: tri_nodes += (n + 1) // 2 if n_cols % 2 == 0: southeast = tri_nodes - 1 southwest = tri_nodes northwest = n_nodes - 1 - (tri_nodes - 1 + n_cols // 2) northeast = n_nodes - 1 - (tri_nodes - n) else: southwest = tri_nodes southeast = tri_nodes + n_cols // 2 northwest = n_nodes - (tri_nodes + (n_cols + 1) // 2) northeast = n_nodes - 1 - tri_nodes south = 0 north = n_nodes - 1 return (northeast, north, northwest, southwest, south, southeast)
[docs] @staticmethod def number_of_perimeter_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import VerticalHexTriGraph >>> VerticalHexTriGraph.number_of_perimeter_nodes((2, 3)) 6 >>> VerticalHexTriGraph.number_of_perimeter_nodes((2, 2)) 5 """ if shape[1] == 1: return shape[0] return 2 * shape[1] + 2 * (shape[0] - 2) + (shape[1] + 1) % 2
[docs] @staticmethod def perimeter_nodes(shape): """ Examples -------- >>> from landlab.graph.hex.hex import VerticalHexTriGraph >>> VerticalHexTriGraph.perimeter_nodes((3, 7)) array([ 9, 16, 23, 26, 28, 29, 27, 24, 20, 13, 6, 3, 1, 0, 2, 5]) >>> VerticalHexTriGraph.perimeter_nodes((2, 3)) array([2, 5, 6, 4, 1, 0]) >>> VerticalHexTriGraph.perimeter_nodes((2, 4)) array([ 2, 6, 10, 11, 9, 7, 3, 1, 0]) >>> VerticalHexTriGraph.perimeter_nodes((2, 2)) array([0, 2, 4, 3, 1]) >>> VerticalHexTriGraph.perimeter_nodes((3, 1)) array([0, 1, 2]) """ return np.concatenate(VerticalHexTriGraph.nodes_at_edge(shape))
[docs] @staticmethod def nodes_at_edge(shape): """ Examples -------- >>> from landlab.graph.hex.hex import VerticalHexTriGraph >>> VerticalHexTriGraph.nodes_at_edge((3, 7)) (array([ 9, 16]), array([23, 26, 28]), array([29, 27, 24]), array([20, 13]), array([6, 3, 1]), array([0, 2, 5])) >>> VerticalHexTriGraph.nodes_at_edge((2, 3)) (array([2]), array([5]), array([6]), array([4]), array([1]), array([0])) >>> VerticalHexTriGraph.nodes_at_edge((2, 4)) (array([2, 6]), array([10]), array([11, 9]), array([7]), array([3, 1]), array([0])) """ n_rows, n_cols = shape ( northeast, north, northwest, southwest, south, southeast, ) = VerticalHexTriGraph.corner_nodes(shape) if shape[1] == 1: southwest = northwest - n_cols return ( np.arange(southeast, northeast, n_cols), as_id_array(north - np.arange(1, (n_cols + 1) // 2).cumsum())[::-1], as_id_array(north - np.arange(1, (n_cols + 2) // 2).cumsum() + 1), np.arange(northwest, southwest, -n_cols), as_id_array(south + np.arange(1, (n_cols + 2) // 2).cumsum())[::-1], as_id_array(south + np.arange(1, (n_cols + 1) // 2).cumsum() - 1), )
[docs] class HexGraphExtras: @property @cache_result_in_object() @make_return_array_immutable def nodes_at_right_edge(self): """Get nodes along the right edge. Examples -------- >>> import numpy as np >>> from landlab.graph import TriGraph >>> graph = TriGraph((3, 4), node_layout="rect") >>> graph.nodes_at_right_edge array([ 3, 7, 11]) """ return np.arange( self.shape[1] - 1, self.shape[0] * self.shape[1], self.shape[1], dtype=int ) @property @cache_result_in_object() @make_return_array_immutable def nodes_at_top_edge(self): """Get nodes along the top edge. Examples -------- >>> import numpy as np >>> from landlab.graph import TriGraph >>> graph = TriGraph((3, 4), node_layout="rect") >>> graph.nodes_at_top_edge array([ 8, 9, 10, 11]) """ return np.arange( self.number_of_nodes - self.shape[1], self.number_of_nodes, dtype=int ) @property @cache_result_in_object() @make_return_array_immutable def nodes_at_left_edge(self): """Get nodes along the left edge. Examples -------- >>> import numpy as np >>> from landlab.graph import TriGraph >>> graph = TriGraph((3, 4), node_layout="rect") >>> graph.nodes_at_left_edge array([0, 4, 8]) """ return np.arange(0, self.shape[0] * self.shape[1], self.shape[1], dtype=int) @property @cache_result_in_object() @make_return_array_immutable def nodes_at_bottom_edge(self): """Get nodes along the bottom edge. Examples -------- >>> import numpy as np >>> from landlab.graph import TriGraph >>> graph = TriGraph((3, 4), node_layout="rect") >>> graph.nodes_at_bottom_edge array([0, 1, 2, 3]) """ return np.arange(self.shape[1], dtype=int) @property @cache_result_in_object() @make_return_array_immutable def length_of_link(self): return np.full(self.number_of_links, self.spacing, dtype=float) def _oriented_links_at_node(self): if self.orientation == "horizontal": orientations = (LinkOrientation.E, LinkOrientation.NNE, LinkOrientation.NNW) else: orientations = (LinkOrientation.ENE, LinkOrientation.N, LinkOrientation.ESE) links_at_node = np.full((self.number_of_nodes, 6), -1, dtype=int) for col, orientation in enumerate(orientations): links = np.where(self.orientation_of_link == orientation)[0] if orientation == LinkOrientation.ESE: links_at_node[self.node_at_link_tail[links], col + 3] = links links_at_node[self.node_at_link_head[links], col] = links else: links_at_node[self.node_at_link_tail[links], col] = links links_at_node[self.node_at_link_head[links], col + 3] = links return links_at_node
[docs] class TriGraph(HexGraphExtras, DelaunayGraph): """Graph of a structured grid of triangles. Examples -------- >>> import numpy as np >>> from landlab.graph import TriGraph >>> graph = TriGraph((3, 2)) >>> graph.number_of_nodes == 6 True >>> np.round(graph.y_of_node * 2.0 / np.sqrt(3)) array([0., 0., 1., 1., 2., 2.]) >>> graph.x_of_node array([0. , 1. , 0.5, 1.5, 0. , 1. ]) """
[docs] def __init__( self, shape, spacing=1.0, xy_of_lower_left=(0.0, 0.0), orientation="horizontal", node_layout="rect", sort=False, ): """Create a structured grid of triangles. Parameters ---------- shape : tuple of int Number of rows and columns of the hex grid. The first value is the number of nodes in the first column and the second the number of nodes in the first column. spacing : float, optional Length of links. xy_of_lower_left : tuple of float, optional Coordinates of lower-left corner of the grid. orientation: {'horizontal', 'vertical'} Specify if triangles should be laid out in rows or columns. node_layout: {'rect', 'hex'} Specify the overall layout of the nodes. Use *rect* for the layout to approximate a rectangle and *hex* for a hexagon. """ if node_layout not in ("rect", "hex"): raise ValueError("node_layout not understood") if orientation not in ("horizontal", "vertical"): raise ValueError("orientation not understood") layouts = { "horizontal_hex": HorizontalHexTriGraph, "vertical_hex": VerticalHexTriGraph, "horizontal_rect": HorizontalRectTriGraph, "vertical_rect": VerticalRectTriGraph, } layout = layouts["_".join([orientation, node_layout])] try: spacing = float(spacing) except TypeError as exc: raise TypeError("spacing must be a float") from exc self._shape = tuple(shape) self._spacing = spacing self._orientation = orientation self._node_layout = node_layout x_of_node, y_of_node = layout.xy_of_node( shape, spacing=spacing, xy_of_lower_left=xy_of_lower_left ) self._perimeter_nodes = layout.perimeter_nodes(shape) perimeter_links = np.empty((len(self._perimeter_nodes), 2), dtype=int) perimeter_links[:, 0] = self._perimeter_nodes perimeter_links[:-1, 1] = self._perimeter_nodes[1:] perimeter_links[-1, 1] = self._perimeter_nodes[0] if 1 in shape: Graph.__init__( self, (y_of_node, x_of_node), links=list( zip(np.arange(len(y_of_node) - 1), np.arange(1, len(y_of_node))) ), sort=False, ) else: DelaunayGraph.__init__( self, (y_of_node, x_of_node), perimeter_links=perimeter_links, sort=False, ) if sort: self.sort()
@property def shape(self): return self._shape @property def spacing(self): return self._spacing @property def orientation(self): return self._orientation @property def node_layout(self): return self._node_layout
[docs] @cached_property @make_return_array_immutable def perimeter_nodes(self): return self._perimeter_nodes