Skip to content

Commit

Permalink
Merge branch 'eesast:dev' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
panxuc authored Nov 18, 2023
2 parents def9fa4 + 5d50304 commit b932be1
Show file tree
Hide file tree
Showing 15 changed files with 1,461 additions and 1 deletion.
4 changes: 4 additions & 0 deletions dependency/MapGenerator-Python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.vscode/
*.map
build/
dist/
34 changes: 34 additions & 0 deletions dependency/MapGenerator-Python/Classes/AreaRenderDict.py
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]
155 changes: 155 additions & 0 deletions dependency/MapGenerator-Python/Classes/MapRenderer.py
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)
52 changes: 52 additions & 0 deletions dependency/MapGenerator-Python/Classes/MapStruct.py
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)
15 changes: 15 additions & 0 deletions dependency/MapGenerator-Python/Classes/RandomCore.py
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 dependency/MapGenerator-Python/Classes/RandomCores/PerlinRandomCore.py
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']
Loading

0 comments on commit b932be1

Please sign in to comment.