From 6428ce6aaed05e2feb6abad7b1beb30c0e36fd96 Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Wed, 6 Sep 2023 17:44:40 +0400 Subject: [PATCH 1/9] only edgify --- geks/rivers.py | 8 +------- test/test_rivers.py | 9 +-------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/geks/rivers.py b/geks/rivers.py index cd6c7dc..9493c27 100644 --- a/geks/rivers.py +++ b/geks/rivers.py @@ -77,8 +77,7 @@ def generate_rivers( ocean_alt, source_min_alt, source_min_dist=2, - edgify=True, - curv=None, + curv=None ): """ Generates rivers from the given minimal altitude to the ocean level or the @@ -96,8 +95,6 @@ def generate_rivers( Minimal altitude of river sources source_min_dist : int, default: 2 Minimal distance between river sources - edgify : bool, default: True - Plot rivers through hexagons' edges curv : int or None, default: None If None, edgified river curvature increases as river gets closer to the ocean. Otherwise curvature will be fixed, the higher the value is. @@ -120,9 +117,6 @@ def generate_rivers( pool = [he for he in pool if he not in busy] it += 1 - if not edgify: - return rivers - # Direct rivers through edges of hexagons and handle mergers all_edges = [] for river in rivers: diff --git a/test/test_rivers.py b/test/test_rivers.py index 5f61b7a..8dbe238 100644 --- a/test/test_rivers.py +++ b/test/test_rivers.py @@ -1,14 +1,7 @@ import geks -def test_river_centers(): - hm = geks.RectHexmap(None, (40, 20)) - geks.gen_heightmap(hm) - geks.fill_sinks_cy(hm) - geks.generate_rivers(hm, 5, -1, 150, edgify=False) - - -def test_river_edges(): +def test_river(): hm = geks.RectHexmap(None, (40, 20)) geks.gen_heightmap(hm) geks.fill_sinks_cy(hm) From ba2a16c6a7bac84f43d8e7420fb381e42548ac5c Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Wed, 6 Sep 2023 17:58:15 +0400 Subject: [PATCH 2/9] edge test --- geks/__init__.py | 12 +++++------- test/test_borders.py | 11 +++++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 test/test_borders.py diff --git a/geks/__init__.py b/geks/__init__.py index d97c4eb..745d593 100644 --- a/geks/__init__.py +++ b/geks/__init__.py @@ -1,26 +1,24 @@ from .hexagon import Hex +from .edge import HexEdge from .hexmap import RoundHexmap, RectHexmap from .layout import Layout from .pathfinding import dijkstra_scan, dijkstra_path -from .heightmap import ( - gen_heightmap, - fill_sinks_py, - fill_sinks_cy, - altitude_cdf, -) +from .heightmap import gen_heightmap, fill_sinks_cy, fill_sinks_py, \ + altitude_cdf from .rivers import generate_rivers from .geometry import hex_distance, hex_line, hex_ring, hex_circle, hex_rotate __all__ = [ "Hex", + "HexEdge", "RoundHexmap", "RectHexmap", "Layout", "dijkstra_scan", "dijkstra_path", "gen_heightmap", - "fill_sinks_py", "fill_sinks_cy", + "fill_sinks_py", "altitude_cdf", "generate_rivers", "hex_distance", diff --git a/test/test_borders.py b/test/test_borders.py new file mode 100644 index 0000000..b8897a1 --- /dev/null +++ b/test/test_borders.py @@ -0,0 +1,11 @@ +import geks + + +def test_edge_equal(): + he = geks.Hex((0, 0)) + di = geks.Hex((1, 1)) + edge0 = geks.HexEdge(he, di, 1) + + ne = he + di + edge1 = geks.HexEdge(ne, he - ne, 1) + assert edge0 == edge1 From 4dbb3f43548b33943e487287c47859de9cbf529a Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Wed, 6 Sep 2023 19:08:30 +0400 Subject: [PATCH 3/9] testing example wf --- .github/workflows/development.yaml | 12 ++++++--- examples/building_path.py | 26 +++++++++++-------- examples/heights_and_rivers.py | 10 +++++-- geks/front/front_mpl.py | 6 ----- requirements_build.txt | 2 -- requirements_test.txt => requirements_dev.txt | 1 + test/test_borders.py | 2 +- 7 files changed, 34 insertions(+), 25 deletions(-) delete mode 100644 requirements_build.txt rename requirements_test.txt => requirements_dev.txt (76%) diff --git a/.github/workflows/development.yaml b/.github/workflows/development.yaml index 442515c..7ba535e 100644 --- a/.github/workflows/development.yaml +++ b/.github/workflows/development.yaml @@ -1,7 +1,7 @@ name: Development on: push: - branches: [ main ] + branches: [ main , v0.0.2] pull_request: branches: [ main ] @@ -38,7 +38,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements_test.txt + pip install -r requirements_dev.txt - name: Build extensions run: make build_ext - name: Run tests @@ -51,8 +51,14 @@ jobs: flag-name: run-${{ join(matrix.*, '-') }} git-branch: main parallel: true + - name: Test examples + run: | + make install; + for i in examples/*.py; do + python $i && echo "$i is ok" || echo "$i fails"; + done; - finish: + finish-coverage: name: Finalizing needs: test runs-on: ubuntu-latest diff --git a/examples/building_path.py b/examples/building_path.py index d45e40f..9d0ad47 100644 --- a/examples/building_path.py +++ b/examples/building_path.py @@ -2,10 +2,11 @@ Usage example showing how to map hexagons with different properties, calculate path and rendering result with a debug front end. """ +import matplotlib.pyplot as plt + import geks from geks.front import FrontMPL - # Create rectangular map of unblocked hexagons with pointy top default_value = {"price": 1, "blocked": False} hm = geks.RectHexmap(default_value, dims=(12, 8), flat=True) @@ -22,16 +23,16 @@ # Set rugged terrain by drawing a circle swamps = geks.hex_circle(geks.Hex((3, 0)), 1) for sw in swamps: - if sw in hm.map: - hm.map[sw]["price"] = 5 + if sw in hm: + hm[sw]["price"] = 5 # Draw hexagons according to their values -for he, value in hm.map.items(): +for he, value in hm.items(): mpl.plot_hex( he, fill=value["blocked"] or value["price"] > 1, fillcolor="grey" if value["blocked"] else "lightgreen", - edgecolor="lightgrey", + edgecolor="black", ) # Highlight start and target hexagons @@ -41,15 +42,18 @@ mpl.plot_hex(target, fill=True, fillcolor="orange") # Build path -path, nstep, nit = geks.dijkstra_path( +path, nstep = geks.dijkstra_path( hm, start, target, distance=40, - block_func=lambda y, x: hm.map[x]["blocked"], # walls condition - cost_func=lambda y, x: hm.map[x]["price"], # cost of movement + block_func=lambda y, x: hm[x]["blocked"], # walls condition + cost_func=lambda y, x: hm[x]["price"], # cost of movement ) -mpl.plot_path(path, color="darkblue") +mpl.plot_path(path, width=2, color="darkblue") -# Show results -mpl.show() +# Save resulting image +mpl.fig.set_size_inches(10, 8) +plt.gca().invert_yaxis() +plt.autoscale(enable=True) +test.savefig("example.png", transparent=False) diff --git a/examples/heights_and_rivers.py b/examples/heights_and_rivers.py index 7a43d4a..a5ba27b 100644 --- a/examples/heights_and_rivers.py +++ b/examples/heights_and_rivers.py @@ -1,6 +1,8 @@ +import numpy as np +import matplotlib.pyplot as plt + import geks from geks.front import FrontMPL -import numpy as np # Prepare height map hm = geks.RoundHexmap(0, 32) @@ -26,4 +28,8 @@ for edges in geks.generate_rivers(hm, 10, ocean_alt, source_min_alt): mpl.plot_border(edges, color="#1F3C41", width=1.2) -mpl.show() +# Save resulting image +mpl.fig.set_size_inches(16, 16) +plt.gca().invert_yaxis() +plt.autoscale(enable=True) +plt.savefig("example.png", transparent=True) diff --git a/geks/front/front_mpl.py b/geks/front/front_mpl.py index b04c02a..c596747 100644 --- a/geks/front/front_mpl.py +++ b/geks/front/front_mpl.py @@ -27,12 +27,6 @@ def __init__(self, layout): lambda x: print(self.layout.pixel2hex((x.xdata, x.ydata)).round()), ) - def show(self): - """Shows rendered objects with selected matplotlib backend""" - plt.gca().invert_yaxis() - plt.autoscale(enable=True) - plt.show() - def plot_hex( self, he, fill=False, fillcolor=None, edgecolor=None, alpha=1 ): diff --git a/requirements_build.txt b/requirements_build.txt deleted file mode 100644 index 0adf505..0000000 --- a/requirements_build.txt +++ /dev/null @@ -1,2 +0,0 @@ -Cython>=3.0.2 -numpy>=1.23.5 diff --git a/requirements_test.txt b/requirements_dev.txt similarity index 76% rename from requirements_test.txt rename to requirements_dev.txt index de04b77..812952b 100644 --- a/requirements_test.txt +++ b/requirements_dev.txt @@ -2,3 +2,4 @@ Cython>=3.0.2 numpy>=1.23.5 pytest>=7.4.0 pytest-cov>=4.1.0 +matplotlib>=3.6.3 diff --git a/test/test_borders.py b/test/test_borders.py index b8897a1..13facea 100644 --- a/test/test_borders.py +++ b/test/test_borders.py @@ -6,6 +6,6 @@ def test_edge_equal(): di = geks.Hex((1, 1)) edge0 = geks.HexEdge(he, di, 1) - ne = he + di + ne = he + di edge1 = geks.HexEdge(ne, he - ne, 1) assert edge0 == edge1 From 8afa6eb97ca2ca49b59576c0627d87b68db43c8e Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Wed, 6 Sep 2023 19:10:14 +0400 Subject: [PATCH 4/9] test error --- examples/building_path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/building_path.py b/examples/building_path.py index 9d0ad47..ade184d 100644 --- a/examples/building_path.py +++ b/examples/building_path.py @@ -56,4 +56,4 @@ mpl.fig.set_size_inches(10, 8) plt.gca().invert_yaxis() plt.autoscale(enable=True) -test.savefig("example.png", transparent=False) +plt.savefig("example.png", transparen=False) From 454be6c6dc290efa553a44c869c47793d5a8701a Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Wed, 6 Sep 2023 19:22:19 +0400 Subject: [PATCH 5/9] test examples only on linux --- .github/workflows/development.yaml | 23 +++++++++++++++++++++-- requirements_build.txt | 2 ++ requirements_dev.txt | 5 ----- requirements_test.txt | 2 ++ 4 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 requirements_build.txt delete mode 100644 requirements_dev.txt create mode 100644 requirements_test.txt diff --git a/.github/workflows/development.yaml b/.github/workflows/development.yaml index 7ba535e..671c589 100644 --- a/.github/workflows/development.yaml +++ b/.github/workflows/development.yaml @@ -38,7 +38,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements_dev.txt + pip install -r requirements_build.txt + pip install -r requirements_test.txt - name: Build extensions run: make build_ext - name: Run tests @@ -51,6 +52,24 @@ jobs: flag-name: run-${{ join(matrix.*, '-') }} git-branch: main parallel: true + + examples: + name: Test examples Python ${{ matrix.python-version }} + needs: lint + strategy: + max-parallel: 6 + matrix: + python-version: [ "3.10", "3.11"] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install package + run: make install-mpl - name: Test examples run: | make install; @@ -67,4 +86,4 @@ jobs: uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.github_token }} - parallel-finished: true + parallel-finished: true diff --git a/requirements_build.txt b/requirements_build.txt new file mode 100644 index 0000000..0adf505 --- /dev/null +++ b/requirements_build.txt @@ -0,0 +1,2 @@ +Cython>=3.0.2 +numpy>=1.23.5 diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index 812952b..0000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,5 +0,0 @@ -Cython>=3.0.2 -numpy>=1.23.5 -pytest>=7.4.0 -pytest-cov>=4.1.0 -matplotlib>=3.6.3 diff --git a/requirements_test.txt b/requirements_test.txt new file mode 100644 index 0000000..d14e63a --- /dev/null +++ b/requirements_test.txt @@ -0,0 +1,2 @@ +pytest>=7.4.0 +pytest-cov>=4.1.0 From fedacd9f1333b21e5d8a25ca242a763e44fbcd74 Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Wed, 6 Sep 2023 19:26:15 +0400 Subject: [PATCH 6/9] minor --- .github/workflows/development.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/development.yaml b/.github/workflows/development.yaml index 671c589..fe381c6 100644 --- a/.github/workflows/development.yaml +++ b/.github/workflows/development.yaml @@ -1,7 +1,7 @@ name: Development on: push: - branches: [ main , v0.0.2] + branches: [ main ] pull_request: branches: [ main ] @@ -20,7 +20,7 @@ jobs: run: pip install flake8 && flake8 test: - name: Test ${{ matrix.platform }} Python ${{ matrix.python-version }} + name: Test on ${{ matrix.platform }} Python ${{ matrix.python-version }} needs: lint strategy: max-parallel: 6 @@ -54,7 +54,7 @@ jobs: parallel: true examples: - name: Test examples Python ${{ matrix.python-version }} + name: Run examples on Python ${{ matrix.python-version }} needs: lint strategy: max-parallel: 6 From c32050860ca0c785248a1f458ee9d863565b611e Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Thu, 14 Sep 2023 02:31:13 +0400 Subject: [PATCH 7/9] flat top center fix --- geks/hexmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geks/hexmap.py b/geks/hexmap.py index f4012f8..7f1e60d 100644 --- a/geks/hexmap.py +++ b/geks/hexmap.py @@ -130,7 +130,7 @@ def center(self): """Returns central hexagon.""" center = self.dims // 2 if self.flat: - pass + center[1] -= center[0] // 2 else: center[0] -= center[1] // 2 return Hex(center) From a61836a13f13c98ff1601203ec5a53aba13d9243 Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Fri, 15 Sep 2023 15:27:26 +0400 Subject: [PATCH 8/9] remove frame --- geks/front/front_mpl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/geks/front/front_mpl.py b/geks/front/front_mpl.py index c596747..4cf40ff 100644 --- a/geks/front/front_mpl.py +++ b/geks/front/front_mpl.py @@ -16,6 +16,7 @@ def __init__(self, layout): self.layout = layout self.fig, self.ax = plt.subplots(1) self.ax.set_aspect("equal") + self.ax.set(frame_on=False) self.ax.set_yticklabels([]) self.ax.set_xticklabels([]) From 238b31f0b4be913e2a021bbc8ff4dc32617bb8c3 Mon Sep 17 00:00:00 2001 From: Vasily Negrebetskiy Date: Mon, 18 Sep 2023 16:03:49 +0400 Subject: [PATCH 9/9] use kwargs where possible --- examples/building_path.py | 10 ++++---- examples/heights_and_rivers.py | 2 +- geks/front/front_mpl.py | 42 ++++++++++++++++------------------ 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/examples/building_path.py b/examples/building_path.py index ade184d..68382ae 100644 --- a/examples/building_path.py +++ b/examples/building_path.py @@ -31,15 +31,15 @@ mpl.plot_hex( he, fill=value["blocked"] or value["price"] > 1, - fillcolor="grey" if value["blocked"] else "lightgreen", + facecolor="grey" if value["blocked"] else "lightgreen", edgecolor="black", ) # Highlight start and target hexagons start = geks.Hex((0, 0)) target = geks.Hex((11, 2)) -mpl.plot_hex(start, fill=True, fillcolor="lightblue") -mpl.plot_hex(target, fill=True, fillcolor="orange") +mpl.plot_hex(start, facecolor="lightblue") +mpl.plot_hex(target, facecolor="orange") # Build path path, nstep = geks.dijkstra_path( @@ -50,10 +50,10 @@ block_func=lambda y, x: hm[x]["blocked"], # walls condition cost_func=lambda y, x: hm[x]["price"], # cost of movement ) -mpl.plot_path(path, width=2, color="darkblue") +mpl.plot_path(path, lw=2, color="darkblue") # Save resulting image mpl.fig.set_size_inches(10, 8) plt.gca().invert_yaxis() plt.autoscale(enable=True) -plt.savefig("example.png", transparen=False) +plt.savefig("example.png") diff --git a/examples/heights_and_rivers.py b/examples/heights_and_rivers.py index a5ba27b..53cdeba 100644 --- a/examples/heights_and_rivers.py +++ b/examples/heights_and_rivers.py @@ -26,7 +26,7 @@ # Generate 10 rivers for edges in geks.generate_rivers(hm, 10, ocean_alt, source_min_alt): - mpl.plot_border(edges, color="#1F3C41", width=1.2) + mpl.plot_border(edges, color="#1F3C41", lw=1.2) # Save resulting image mpl.fig.set_size_inches(16, 16) diff --git a/geks/front/front_mpl.py b/geks/front/front_mpl.py index 4cf40ff..84bebab 100644 --- a/geks/front/front_mpl.py +++ b/geks/front/front_mpl.py @@ -28,20 +28,12 @@ def __init__(self, layout): lambda x: print(self.layout.pixel2hex((x.xdata, x.ydata)).round()), ) - def plot_hex( - self, he, fill=False, fillcolor=None, edgecolor=None, alpha=1 - ): + def plot_hex(self, he, **kwargs): """Renders hexagon""" - patch = Polygon( - self.layout.hex_corners(he), - fill=fill, - facecolor=fillcolor, - edgecolor=edgecolor, - alpha=alpha, - ) + patch = Polygon(self.layout.hex_corners(he), **kwargs) self.ax.add_patch(patch) - def plot_hexmap(self, hm, colors, ignore_func=lambda x: False): + def plot_hexmap(self, hm, colors, ignore_func=lambda x: False, **kwargs): """Renders collection of hexagons""" cmap = LinearSegmentedColormap.from_list("terrain", colors) @@ -49,34 +41,40 @@ def plot_hexmap(self, hm, colors, ignore_func=lambda x: False): for he, val in hm.items(): if ignore_func(he): continue - patch = Polygon(self.layout.hex_corners(he), color=cmap(val)) + patch = Polygon( + self.layout.hex_corners(he), + color=cmap(val), + **kwargs + ) patches.append(patch) pc = PatchCollection(patches, match_original=True) self.ax.add_collection(pc) - def plot_path(self, path, width=1, color=None): + def plot_path(self, path, **kwargs): """Renders a trace connecting given hexagons""" if not path or len(path) < 2: return xy = np.array([self.layout.hex2pixel(he) for he in path]) - self.ax.plot(xy[:, 0], xy[:, 1], linewidth=width, color=color) + self.ax.plot(xy[:, 0], xy[:, 1], **kwargs) - def plot_edge(self, he, di, width=1, color=None): + def plot_edge(self, he, di, **kwargs): """Renders hexagon's edge in given direction""" edge = self.layout.hex_edge(he, di) - self.ax.plot(edge[:, 0], edge[:, 1], color=color, linewidth=width) + self.ax.plot(edge[:, 0], edge[:, 1], **kwargs) - def plot_border(self, edges, width=1, color=None): + def plot_border(self, edges, **kwargs): if len(edges) == 0: return xy = np.array([self.layout.hex_edge(e) for e in edges]) xy = xy.reshape(-1, xy.shape[-1]) xy = np.delete(xy, np.arange(1, xy.shape[0] - 1, 2), axis=0) - self.ax.plot(xy[:, 0], xy[:, 1], linewidth=width, color=color) + self.ax.plot(xy[:, 0], xy[:, 1], **kwargs) - def label(self, he, text, color=None): + def label(self, he, text, **kwargs): """Renders text inside hexagon""" pos = self.layout.hex2pixel(he) - self.ax.annotate( - text, pos, ha="center", va="center", color=color - ) + if "ha" not in kwargs: + kwargs["ha"] = "center" + if "va" not in kwargs: + kwargs["va"] = "center" + self.ax.annotate(text, pos, **kwargs)