-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from asdawej/dev
Full new version of MapGenerator
- Loading branch information
Showing
15 changed files
with
1,461 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.vscode/ | ||
*.map | ||
build/ | ||
dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from __future__ import annotations | ||
from functools import cached_property | ||
|
||
|
||
class AreaRenderDict: | ||
class Unit: | ||
name: str | ||
value: int | ||
color: str | ||
|
||
def __init__(self, _name: str, _value: int, _color: str) -> None: | ||
self.name, self.value, self.color = _name, _value, _color | ||
|
||
areas: list[Unit] | ||
|
||
@cached_property | ||
def Name2Unit(self) -> dict[str, Unit]: | ||
return {x.name: x for x in self.areas} | ||
|
||
@cached_property | ||
def Value2Unit(self) -> dict[int, Unit]: | ||
return {x.value: x for x in self.areas} | ||
|
||
@cached_property | ||
def Color2Unit(self) -> dict[str, Unit]: | ||
return {x.color: x for x in self.areas} | ||
|
||
@cached_property | ||
def Value2Color(self) -> dict[int, str]: | ||
return {x: self.Value2Unit[x].color for x in self.Value2Unit} | ||
|
||
def __init__(self, _areas: list[list[str, int, str]]) -> None: | ||
self.areas = [AreaRenderDict.Unit(_name, _value, _color) | ||
for _name, _value, _color in _areas] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
from __future__ import annotations | ||
from queue import Queue | ||
|
||
from easygui import choicebox | ||
import matplotlib.pyplot as plt | ||
from matplotlib.figure import Figure | ||
from matplotlib.axes import Axes | ||
from matplotlib.patches import Rectangle | ||
from matplotlib.ticker import MultipleLocator | ||
from matplotlib.backend_bases import MouseEvent, KeyEvent | ||
|
||
from Classes.AreaRenderDict import AreaRenderDict | ||
from Classes.MapStruct import MapStruct | ||
from Classes.RandomCore import RandomCore | ||
|
||
|
||
class MapRenderer: | ||
class RenderUnit: | ||
r: int | ||
c: int | ||
tp: str | ||
|
||
def __init__(self, _r: int, _c: int, _tp: str) -> None: | ||
self.r = _r | ||
self.c = _c | ||
self.tp = _tp | ||
|
||
title: str | ||
map: MapStruct | ||
areaRender: AreaRenderDict | ||
mapf: str | ||
fig: Figure | ||
ax: Axes | ||
rects: list[list[Rectangle]] | ||
queue_render: Queue[RenderUnit] | ||
randomCores: list[RandomCore] | ||
|
||
isCursorLift: int # 0: 未提起; 1: 提起未选定; 2: 提起并选定 | ||
cursorLift: tuple[int] # 提起坐标 | ||
|
||
__curMax: int | ||
__cur: int | ||
|
||
@property | ||
def Cur(self) -> str: | ||
return self.areaRender.areas[self.__cur].color | ||
|
||
@Cur.setter | ||
def Cur(self, _) -> None: | ||
self.__cur += 1 | ||
self.__cur %= self.__curMax | ||
|
||
@property | ||
def Queue_Render(self) -> RenderUnit: | ||
return self.queue_render.get(timeout=0.1) | ||
|
||
@Queue_Render.setter | ||
def Queue_Render(self, value: RenderUnit) -> None: | ||
self.queue_render.put(value, timeout=0.1) | ||
|
||
def __init__(self, _title, _mapStruct: MapStruct, _areas: AreaRenderDict, | ||
_mapf: str, _randoms: list[RandomCore]) -> None: | ||
self.title = _title | ||
self.map = _mapStruct | ||
self.areaRender = _areas | ||
self.mapf = _mapf | ||
self.randomCores = _randoms | ||
self.fig, self.ax = plt.subplots() | ||
self.rects = [[Rectangle((j, i), 1, 1, facecolor=self.areaRender.areas[self.map[i, j]].color) | ||
for j in range(self.map.width)] | ||
for i in range(self.map.height)] | ||
self.queue_render = Queue() | ||
self.isCursorLift = 0 | ||
self.cursorLift = None | ||
self.__curMax = len(self.areaRender.areas) | ||
self.__cur = 0 | ||
|
||
def MainFrame(self) -> None: | ||
self.fig.set_size_inches(self.map.width, self.map.height) | ||
self.ax.set_xlim(0, self.map.width) | ||
self.ax.set_ylim(self.map.height, 0) | ||
self.ax.xaxis.set_major_locator(MultipleLocator(1)) | ||
self.ax.yaxis.set_major_locator(MultipleLocator(1)) | ||
self.ax.set_aspect(1) | ||
for i in range(self.map.height): | ||
for j in range(self.map.width): | ||
self.ax.add_patch(self.rects[i][j]) | ||
plt.hlines(range(self.map.height + 1), 0, self.map.width) | ||
plt.vlines(range(self.map.width + 1), 0, self.map.height) | ||
self.fig.canvas.mpl_connect('button_press_event', self.on_click) | ||
self.fig.canvas.mpl_connect('key_press_event', self.on_press) | ||
plt.show() | ||
|
||
def on_click(self, event: MouseEvent) -> None: | ||
if not event.button: | ||
return | ||
match event.button: | ||
case 1: | ||
r, c = int(event.ydata), int(event.xdata) | ||
match self.isCursorLift: | ||
case 0: | ||
self.map[r, c] = self.areaRender.Color2Unit[self.Cur].value | ||
self.Queue_Render = MapRenderer.RenderUnit(r, c, self.Cur) | ||
self.Render() | ||
case 1: | ||
self.cursorLift = (r, c) | ||
self.isCursorLift = 2 | ||
case 2: | ||
liftr, liftc = self.cursorLift | ||
dir_r, dir_c = (1 if liftr <= r else -1), (1 if liftc <= c else -1) | ||
for i in range(liftr, r + dir_r, dir_r): | ||
for j in range(liftc, c + dir_c, dir_c): | ||
self.map[i, j] = self.areaRender.Color2Unit[self.Cur].value | ||
self.Queue_Render = MapRenderer.RenderUnit(i, j, self.Cur) | ||
self.Render() | ||
self.isCursorLift = 0 | ||
case 3: | ||
self.Cur = 0 | ||
case _: | ||
return | ||
|
||
def on_press(self, event: KeyEvent) -> None: | ||
if not event.key: | ||
return | ||
match event.key: | ||
case 'z': | ||
self.isCursorLift = 1 | ||
case 'c': | ||
self.map.ToFile(self.mapf) | ||
case 'p': | ||
opt = choicebox(msg='Choose random', title=self.title, choices=[x.Name for x in self.randomCores]) | ||
if opt is None: | ||
return | ||
for x in self.randomCores: | ||
if x.Name == opt: | ||
if x.Menu(): | ||
x.Random(self.map) | ||
for r in range(self.map.height): | ||
for c in range(self.map.width): | ||
self.Queue_Render = MapRenderer.RenderUnit( | ||
r, c, self.areaRender.Value2Color[self.map[r, c]]) | ||
self.Render() | ||
plt.show(block=False) | ||
case _: | ||
return | ||
|
||
def Render(self) -> None: | ||
while not self.queue_render.empty(): | ||
cur = self.Queue_Render | ||
self._render(cur.r, cur.c, cur.tp) | ||
|
||
def _render(self, r: int, c: int, tp: str) -> None: | ||
self.rects[r][c].set_color(tp) | ||
self.ax.draw_artist(self.rects[r][c]) | ||
plt.show(block=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from __future__ import annotations | ||
from functools import cached_property | ||
from io import TextIOWrapper | ||
from typing import Literal, overload | ||
from array import array | ||
|
||
|
||
class MapStruct: | ||
__arrAlloc: array[int] | ||
|
||
@cached_property | ||
def width(self) -> int: | ||
return self.__arrAlloc[1] | ||
|
||
@cached_property | ||
def height(self) -> int: | ||
return self.__arrAlloc[0] | ||
|
||
@property | ||
def ArrayView(self) -> list[list[int]]: | ||
return [[self[i, j] for j in range(self.width)] for i in range(self.height)] | ||
|
||
def __getitem__(self, rowcol: tuple[int, int]) -> int: | ||
return self.__arrAlloc[2 + rowcol[1] + rowcol[0] * self.width] | ||
|
||
def __setitem__(self, rowcol: tuple[int, int], val: int) -> None: | ||
self.__arrAlloc[2 + rowcol[1] + rowcol[0] * self.width] = val | ||
|
||
@overload | ||
def __init__(self, dtype: Literal['b', 'B', 'u', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'f', 'd'], | ||
height: int, width: int) -> None: ... | ||
|
||
@overload | ||
def __init__(self, dtype: Literal['b', 'B', 'u', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'f', 'd'], | ||
mapFile: TextIOWrapper) -> None: ... | ||
|
||
def __init__(self, dtype: Literal['b', 'B', 'u', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'f', 'd'], | ||
*args) -> None: | ||
self.__arrAlloc = array(dtype) | ||
if len(args) == 1: | ||
mapFile = args[0] | ||
self.__arrAlloc.fromfile(mapFile, 2) | ||
self.__arrAlloc.fromfile(mapFile, self.width * self.height) | ||
else: | ||
self.__arrAlloc.append(args[0]) | ||
self.__arrAlloc.append(args[1]) | ||
self.__arrAlloc.fromlist( | ||
[0 for _ in range(self.width * self.height)]) | ||
|
||
def ToFile(self, path: str) -> None: | ||
with open(path, 'wb') as fp: | ||
self.__arrAlloc.tofile(fp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from __future__ import annotations | ||
from abc import abstractmethod, abstractproperty | ||
|
||
from Classes.MapStruct import MapStruct | ||
|
||
|
||
class RandomCore: | ||
@abstractproperty | ||
def Name(self) -> str: ... | ||
|
||
@abstractmethod | ||
def Menu(self) -> bool: ... | ||
|
||
@abstractmethod | ||
def Random(self, mp: MapStruct) -> None: ... |
101 changes: 101 additions & 0 deletions
101
dependency/MapGenerator-Python/Classes/RandomCores/PerlinRandomCore.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
from __future__ import annotations | ||
from typing import Any | ||
|
||
from easygui import multenterbox | ||
from perlin_noise import PerlinNoise | ||
|
||
from Classes.MapStruct import MapStruct | ||
from Classes.RandomCore import RandomCore | ||
|
||
dict_place2num = { | ||
'Space': 0, | ||
'Ruin': 1, | ||
'Shadow': 2 | ||
} | ||
|
||
|
||
class PerlinRandomCore(RandomCore): | ||
title: str | ||
octaves: float | ||
seed: int | None | ||
|
||
@property | ||
def Octaves(self) -> float: | ||
return self.octaves | ||
|
||
@Octaves.setter | ||
def Octaves(self, value: float) -> None: | ||
if value > 0: | ||
self.octaves = value | ||
else: | ||
self.octaves = 10 | ||
|
||
@property | ||
def Seed(self) -> int | None: | ||
return self.seed | ||
|
||
@Seed.setter | ||
def Seed(self, value: int | Any) -> None: | ||
if isinstance(value, int) and value > 0: | ||
self.seed = value | ||
else: | ||
self.seed = None | ||
|
||
def __init__(self, title: str, octaves: float = 10) -> None: | ||
self.title = title | ||
self.Octaves = octaves | ||
self.Seed = None | ||
|
||
@property | ||
def Name(self) -> str: | ||
return 'Perlin' | ||
|
||
def Menu(self) -> bool: | ||
try: | ||
(self.Octaves, | ||
self.Seed) = (lambda f1, i2: (float(f1), int(i2) if i2 else i2))(*multenterbox( | ||
msg='Random settings', | ||
title=self.title, | ||
fields=[ | ||
'Octaves', | ||
'Seed' | ||
], | ||
values=[ | ||
self.Octaves, | ||
self.Seed | ||
] | ||
)) | ||
except TypeError: | ||
return False | ||
return True | ||
|
||
def Random(self, mp: MapStruct) -> None: | ||
noise = PerlinNoise(self.Octaves, self.Seed) | ||
arr: list[list[float]] = [] | ||
arrMax: float = float('-inf') | ||
arrMin: float = float('inf') | ||
for i in range(mp.height): | ||
arr.append([]) | ||
for j in range(mp.width): | ||
cur = noise([j / mp.width, i / mp.height]) | ||
arr[i].append(cur) | ||
if cur > arrMax: | ||
arrMax = cur | ||
elif cur < arrMin: | ||
arrMin = cur | ||
arr0, arr1, arr2 = arrMin, (2 * arrMin + arrMax) / 3, (arrMin + 2 * arrMax) / 3 | ||
for i in range(mp.height): | ||
for j in range(mp.width): | ||
if i == 0 or i == mp.height - 1: | ||
mp[i, j] = dict_place2num['Ruin'] | ||
continue | ||
elif j == 0 or j == mp.width - 1: | ||
mp[i, j] = dict_place2num['Ruin'] | ||
continue | ||
cur = arr[i][j] | ||
if arr0 < cur < arr1: | ||
mp[i, j] = dict_place2num['Shadow'] | ||
elif arr1 < cur < arr2: | ||
mp[i, j] = dict_place2num['Space'] | ||
else: | ||
mp[i, j] = dict_place2num['Ruin'] |
Oops, something went wrong.