Практика сделана
This commit is contained in:
		
						commit
						d225612d2e
					
				
							
								
								
									
										
											BIN
										
									
								
								__pycache__/climate_zones_map.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								__pycache__/climate_zones_map.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								__pycache__/frontend.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								__pycache__/frontend.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								__pycache__/generation_heightmap.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								__pycache__/generation_heightmap.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								__pycache__/generation_landwater.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								__pycache__/generation_landwater.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								__pycache__/map_style_processor.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								__pycache__/map_style_processor.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								__pycache__/map_styler.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								__pycache__/map_styler.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								__pycache__/settings.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								__pycache__/settings.cpython-313.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										49
									
								
								climate_zones_artistic.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								climate_zones_artistic.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					from settings import WorldSettings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ArtisticClimateGenerator:
 | 
				
			||||||
 | 
					    def __init__(self, height_map: np.ndarray, settings: WorldSettings):
 | 
				
			||||||
 | 
					        self.height_map = height_map
 | 
				
			||||||
 | 
					        self.settings = settings
 | 
				
			||||||
 | 
					        random.seed(settings.get_valid_seed())
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Оригинальные цвета из ClimateGenerator
 | 
				
			||||||
 | 
					        self.biome_colors = {
 | 
				
			||||||
 | 
					            'water': (0.2, 0.4, 0.8, 1.0),
 | 
				
			||||||
 | 
					            'ice': (0.95, 0.95, 0.98, 0.8),
 | 
				
			||||||
 | 
					            'tundra': (0.4, 0.6, 0.9, 0.9),
 | 
				
			||||||
 | 
					            'taiga': (0.1, 0.3, 0.15, 1.0),
 | 
				
			||||||
 | 
					            'forest': (0.3, 0.6, 0.3, 1.0),
 | 
				
			||||||
 | 
					            'desert': (0.9, 0.6, 0.2, 1.0),
 | 
				
			||||||
 | 
					            'steppe': (0.8, 0.75, 0.5, 1.0),
 | 
				
			||||||
 | 
					            'swamp': (0.4, 0.35, 0.2, 0.9),
 | 
				
			||||||
 | 
					            'jungle': (0.1, 0.5, 0.1, 1.0),
 | 
				
			||||||
 | 
					            'savanna': (0.8, 0.7, 0.3, 1.0)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_biome_map(self, enabled_biomes=None) -> np.ndarray:
 | 
				
			||||||
 | 
					        """Полностью случайное распределение биомов"""
 | 
				
			||||||
 | 
					        if enabled_biomes is None:
 | 
				
			||||||
 | 
					            enabled_biomes = list(self.biome_colors.keys())
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        height, width = self.height_map.shape
 | 
				
			||||||
 | 
					        biome_map = np.zeros((height, width, 4), dtype=np.float32)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Включаем воду, если она есть в списке
 | 
				
			||||||
 | 
					        water_included = 'water' in enabled_biomes
 | 
				
			||||||
 | 
					        water_level = 0.05
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for y in range(height):
 | 
				
			||||||
 | 
					            for x in range(width):
 | 
				
			||||||
 | 
					                # Если это вода - рисуем воду и переходим к следующей точке
 | 
				
			||||||
 | 
					                if water_included and self.height_map[y,x] < water_level:
 | 
				
			||||||
 | 
					                    biome_map[y,x] = self.biome_colors['water']
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                # Случайный выбор любого биома
 | 
				
			||||||
 | 
					                available_biomes = [b for b in enabled_biomes if b != 'water'] or ['forest']
 | 
				
			||||||
 | 
					                random_biome = random.choice(available_biomes)
 | 
				
			||||||
 | 
					                biome_map[y,x] = self.biome_colors[random_biome]
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					        return biome_map
 | 
				
			||||||
							
								
								
									
										117
									
								
								climate_zones_map.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								climate_zones_map.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					from opensimplex import OpenSimplex
 | 
				
			||||||
 | 
					from settings import WorldSettings
 | 
				
			||||||
 | 
					from numba import njit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ClimateGenerator:
 | 
				
			||||||
 | 
					    def __init__(self, height_map: np.ndarray, settings: WorldSettings):
 | 
				
			||||||
 | 
					        self.height_map = height_map
 | 
				
			||||||
 | 
					        self.settings = settings
 | 
				
			||||||
 | 
					        self.noise = OpenSimplex(seed=settings.get_valid_seed())
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Палитра биомов
 | 
				
			||||||
 | 
					        self.biome_colors = {
 | 
				
			||||||
 | 
					            'water': (0.2, 0.4, 0.8, 1.0),
 | 
				
			||||||
 | 
					            'ice': (0.95, 0.95, 0.98, 0.8),
 | 
				
			||||||
 | 
					            'tundra': (0.4, 0.6, 0.9, 0.9),
 | 
				
			||||||
 | 
					            'taiga': (0.1, 0.3, 0.15, 1.0),
 | 
				
			||||||
 | 
					            'forest': (0.3, 0.6, 0.3, 1.0),
 | 
				
			||||||
 | 
					            'desert': (0.9, 0.6, 0.2, 1.0),
 | 
				
			||||||
 | 
					            'steppe': (0.8, 0.75, 0.5, 1.0),
 | 
				
			||||||
 | 
					            'swamp': (0.4, 0.35, 0.2, 0.9),
 | 
				
			||||||
 | 
					            'jungle': (0.1, 0.5, 0.1, 1.0),
 | 
				
			||||||
 | 
					            'savanna': (0.8, 0.7, 0.3, 1.0)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_biome_map(self, enabled_biomes=None) -> np.ndarray:
 | 
				
			||||||
 | 
					        """Генерация карты биомов с учетом выбранных биомов"""
 | 
				
			||||||
 | 
					        if enabled_biomes is None:
 | 
				
			||||||
 | 
					            enabled_biomes = list(self.biome_colors.keys())
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        height, width = self.height_map.shape
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Генерация шумов
 | 
				
			||||||
 | 
					        temp_noise = self._generate_noise(width, height, 40)
 | 
				
			||||||
 | 
					        moist_noise = self._generate_noise(width, height, 50)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        biome_map = np.zeros((height, width, 4), dtype=np.float32)
 | 
				
			||||||
 | 
					        self._fill_biome_map(
 | 
				
			||||||
 | 
					            biome_map, 
 | 
				
			||||||
 | 
					            self.height_map,
 | 
				
			||||||
 | 
					            temp_noise,
 | 
				
			||||||
 | 
					            moist_noise,
 | 
				
			||||||
 | 
					            enabled_biomes
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return biome_map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _generate_noise(self, width, height, scale):
 | 
				
			||||||
 | 
					        """Генерация многооктавного шума"""
 | 
				
			||||||
 | 
					        noise = np.zeros((height, width))
 | 
				
			||||||
 | 
					        for octave in [1.0, 0.5, 0.25]:
 | 
				
			||||||
 | 
					            for y in range(height):
 | 
				
			||||||
 | 
					                for x in range(width):
 | 
				
			||||||
 | 
					                    nx = x / (scale * octave)
 | 
				
			||||||
 | 
					                    ny = y / (scale * octave)
 | 
				
			||||||
 | 
					                    noise[y, x] += self.noise.noise2(nx, ny) * octave
 | 
				
			||||||
 | 
					        return (noise - noise.min()) / (noise.max() - noise.min())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    @njit
 | 
				
			||||||
 | 
					    def _fill_biome_map(biome_map, height_map, temp_noise, moist_noise, enabled_biomes):
 | 
				
			||||||
 | 
					        height, width = height_map.shape
 | 
				
			||||||
 | 
					        water_level = 0.05
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for y in range(height):
 | 
				
			||||||
 | 
					            latitude = abs(y/height - 0.5) * 2  # 0 на экваторе, 1 на полюсах
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            for x in range(width):
 | 
				
			||||||
 | 
					                h = height_map[y,x]
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                # Вода (всегда включена, без прозрачности)
 | 
				
			||||||
 | 
					                if h < water_level:
 | 
				
			||||||
 | 
					                    biome_map[y,x] = (0.2, 0.4, 0.8, 1.0)
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                # Климатические параметры
 | 
				
			||||||
 | 
					                temp = (0.7 - latitude * 0.5) * (1.0 - h * 0.5) * (0.8 + temp_noise[y,x] * 0.4)
 | 
				
			||||||
 | 
					                moist = moist_noise[y,x]
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                # Расчёт прозрачности: сильнее зависит от высоты
 | 
				
			||||||
 | 
					                alpha = 1.0 - h**2 * 0.7  # h^2 делает прозрачность резче на вершинах
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                # Выбор биома
 | 
				
			||||||
 | 
					                if temp < 0.3:  # Холодные зоны
 | 
				
			||||||
 | 
					                    if h > 0.8 and 'ice' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.95, 0.95, 0.98, alpha * 0.7)  # Лёд прозрачнее
 | 
				
			||||||
 | 
					                    elif moist > 0.65 and 'tundra' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.4, 0.6, 0.9, alpha)
 | 
				
			||||||
 | 
					                    elif 'taiga' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.1, 0.3, 0.15, alpha)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        biome = (0.3, 0.6, 0.3, alpha)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                elif temp < 0.6:  # Умеренные зоны
 | 
				
			||||||
 | 
					                    if moist > 0.7 and 'swamp' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.4, 0.35, 0.2, alpha)
 | 
				
			||||||
 | 
					                    elif moist > 0.5 and 'forest' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.3, 0.6, 0.3, alpha)
 | 
				
			||||||
 | 
					                    elif moist > 0.3 and 'steppe' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.8, 0.75, 0.5, alpha)
 | 
				
			||||||
 | 
					                    elif 'desert' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.9, 0.6, 0.2, alpha)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        biome = (0.3, 0.6, 0.3, alpha)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                else:  # Теплые зоны
 | 
				
			||||||
 | 
					                    if moist < 0.3 and 'desert' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.9, 0.6, 0.2, alpha)
 | 
				
			||||||
 | 
					                    elif moist < 0.5 and 'savanna' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.8, 0.7, 0.3, alpha)
 | 
				
			||||||
 | 
					                    elif moist > 0.7 and 'jungle' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.1, 0.5, 0.1, alpha)
 | 
				
			||||||
 | 
					                    elif 'forest' in enabled_biomes:
 | 
				
			||||||
 | 
					                        biome = (0.3, 0.6, 0.3, alpha)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        biome = (0.8, 0.75, 0.5, alpha)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                biome_map[y,x] = biome
 | 
				
			||||||
							
								
								
									
										391
									
								
								frontend.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								frontend.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,391 @@
 | 
				
			|||||||
 | 
					import tkinter as tk
 | 
				
			||||||
 | 
					from tkinter import ttk
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					import matplotlib.pyplot as plt
 | 
				
			||||||
 | 
					from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
 | 
				
			||||||
 | 
					from generation_landwater import WorldGenerator
 | 
				
			||||||
 | 
					from generation_heightmap import HeightmapGenerator
 | 
				
			||||||
 | 
					from climate_zones_map import ClimateGenerator
 | 
				
			||||||
 | 
					from settings import WorldSettings
 | 
				
			||||||
 | 
					from map_styler import MapStyler
 | 
				
			||||||
 | 
					import threading
 | 
				
			||||||
 | 
					import tempfile
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from PIL import Image, ImageTk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WorldGeneratorApp:
 | 
				
			||||||
 | 
					    def __init__(self, root):
 | 
				
			||||||
 | 
					        self.root = root
 | 
				
			||||||
 | 
					        self.settings = WorldSettings()
 | 
				
			||||||
 | 
					        self._init_variables()
 | 
				
			||||||
 | 
					        self._setup_ui()
 | 
				
			||||||
 | 
					        self.root.protocol("WM_DELETE_WINDOW", self._on_close)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _init_variables(self):
 | 
				
			||||||
 | 
					        """Инициализация переменных"""
 | 
				
			||||||
 | 
					        self.width_var = tk.IntVar(value=self.settings.width)
 | 
				
			||||||
 | 
					        self.height_var = tk.IntVar(value=self.settings.height)
 | 
				
			||||||
 | 
					        self.mode_var = tk.StringVar(value=self.settings.mode)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Параметры рельефа
 | 
				
			||||||
 | 
					        self.terrain_rough_var = tk.DoubleVar(value=self.settings.terrain_roughness)
 | 
				
			||||||
 | 
					        self.continent_size_var = tk.DoubleVar(value=self.settings.continent_size)
 | 
				
			||||||
 | 
					        self.continent_rough_var = tk.DoubleVar(value=self.settings.continent_roughness)
 | 
				
			||||||
 | 
					        self.islands_dens_var = tk.DoubleVar(value=self.settings.islands_density)
 | 
				
			||||||
 | 
					        self.islands_rough_var = tk.DoubleVar(value=self.settings.islands_roughness)
 | 
				
			||||||
 | 
					        self.island_size_var = tk.DoubleVar(value=self.settings.island_size)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Биомы
 | 
				
			||||||
 | 
					        self.biome_vars = {
 | 
				
			||||||
 | 
					            'ice': tk.BooleanVar(value=True),
 | 
				
			||||||
 | 
					            'tundra': tk.BooleanVar(value=True),
 | 
				
			||||||
 | 
					            'taiga': tk.BooleanVar(value=True),
 | 
				
			||||||
 | 
					            'forest': tk.BooleanVar(value=True),
 | 
				
			||||||
 | 
					            'desert': tk.BooleanVar(value=True),
 | 
				
			||||||
 | 
					            'steppe': tk.BooleanVar(value=True),
 | 
				
			||||||
 | 
					            'swamp': tk.BooleanVar(value=True),
 | 
				
			||||||
 | 
					            'jungle': tk.BooleanVar(value=True),
 | 
				
			||||||
 | 
					            'savanna': tk.BooleanVar(value=True)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Данные карт
 | 
				
			||||||
 | 
					        self.height_map = None
 | 
				
			||||||
 | 
					        self.biome_map = None
 | 
				
			||||||
 | 
					        self.map_window = None
 | 
				
			||||||
 | 
					        self.canvas = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _setup_ui(self):
 | 
				
			||||||
 | 
					        """Настройка интерфейса"""
 | 
				
			||||||
 | 
					        self.root.title("Генератор мира")
 | 
				
			||||||
 | 
					        self.root.geometry("900x650")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        main_frame = ttk.Frame(self.root)
 | 
				
			||||||
 | 
					        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Панель управления
 | 
				
			||||||
 | 
					        control_frame = ttk.LabelFrame(main_frame, text="Управление")
 | 
				
			||||||
 | 
					        control_frame.pack(fill=tk.X, pady=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Первый ряд кнопок
 | 
				
			||||||
 | 
					        btn_row1 = ttk.Frame(control_frame)
 | 
				
			||||||
 | 
					        btn_row1.pack(fill=tk.X)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        buttons_row1 = [
 | 
				
			||||||
 | 
					            ("Сгенерировать землю", self.generate_land),
 | 
				
			||||||
 | 
					            ("Сгенерировать высоты", self.generate_height),
 | 
				
			||||||
 | 
					            ("Сгенерировать климат", self.generate_climate),
 | 
				
			||||||
 | 
					            ("Показать карту", self.show_map)
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for i, (text, cmd) in enumerate(buttons_row1):
 | 
				
			||||||
 | 
					            ttk.Button(btn_row1, text=text, command=cmd).grid(
 | 
				
			||||||
 | 
					                row=0, column=i, padx=5, pady=5, sticky="ew")
 | 
				
			||||||
 | 
					            btn_row1.grid_columnconfigure(i, weight=1)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Второй ряд кнопок
 | 
				
			||||||
 | 
					        btn_row2 = ttk.Frame(control_frame)
 | 
				
			||||||
 | 
					        btn_row2.pack(fill=tk.X, pady=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        buttons_row2 = [
 | 
				
			||||||
 | 
					            ("АВТО", self.auto_generate),
 | 
				
			||||||
 | 
					            ("Стилизовать под свиток", self.style_map),
 | 
				
			||||||
 | 
					            ("Настройки", self.open_settings)
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for i, (text, cmd) in enumerate(buttons_row2):
 | 
				
			||||||
 | 
					            ttk.Button(btn_row2, text=text, command=cmd).grid(
 | 
				
			||||||
 | 
					                row=0, column=i, padx=5, pady=5, sticky="ew")
 | 
				
			||||||
 | 
					            btn_row2.grid_columnconfigure(i, weight=1)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Статус бар
 | 
				
			||||||
 | 
					        self.status_var = tk.StringVar(value="Готов к работе")
 | 
				
			||||||
 | 
					        ttk.Label(main_frame, textvariable=self.status_var, 
 | 
				
			||||||
 | 
					                relief=tk.SUNKEN, anchor=tk.W).pack(fill=tk.X, pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _on_close(self):
 | 
				
			||||||
 | 
					        """Корректное закрытие приложения"""
 | 
				
			||||||
 | 
					        if self.map_window:
 | 
				
			||||||
 | 
					            self.map_window.destroy()
 | 
				
			||||||
 | 
					        plt.close('all')
 | 
				
			||||||
 | 
					        self.root.destroy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def auto_generate(self):
 | 
				
			||||||
 | 
					        """Автоматическая генерация без стилизации"""
 | 
				
			||||||
 | 
					        def _generate():
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                self.status_var.set("Автогенерация: создание земли...")
 | 
				
			||||||
 | 
					                self.generate_land()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                self.status_var.set("Автогенерация: создание высот...")
 | 
				
			||||||
 | 
					                self.generate_height()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                self.status_var.set("Автогенерация: создание климата...")
 | 
				
			||||||
 | 
					                self.generate_climate()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                self.status_var.set("Автогенерация завершена!")
 | 
				
			||||||
 | 
					                self.show_map()
 | 
				
			||||||
 | 
					            except Exception as e:
 | 
				
			||||||
 | 
					                self.status_var.set(f"Ошибка: {str(e)}")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        threading.Thread(target=_generate, daemon=True).start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def open_settings(self):
 | 
				
			||||||
 | 
					        settings_win = tk.Toplevel(self.root)
 | 
				
			||||||
 | 
					        settings_win.title("Настройки генерации")
 | 
				
			||||||
 | 
					        settings_win.geometry("600x500")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        notebook = ttk.Notebook(settings_win)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Основные настройки
 | 
				
			||||||
 | 
					        basic_frame = ttk.Frame(notebook)
 | 
				
			||||||
 | 
					        self.create_basic_settings(basic_frame)
 | 
				
			||||||
 | 
					        notebook.add(basic_frame, text="Основные")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Настройки биомов
 | 
				
			||||||
 | 
					        biome_frame = ttk.Frame(notebook)
 | 
				
			||||||
 | 
					        self.create_biome_settings(biome_frame)
 | 
				
			||||||
 | 
					        notebook.add(biome_frame, text="Биомы")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Кнопки управления
 | 
				
			||||||
 | 
					        btn_frame = ttk.Frame(settings_win)
 | 
				
			||||||
 | 
					        ttk.Button(btn_frame, text="Применить",
 | 
				
			||||||
 | 
					                command=lambda: [self.save_settings(), settings_win.destroy()]).pack(side=tk.LEFT, padx=5)
 | 
				
			||||||
 | 
					        ttk.Button(btn_frame, text="Отмена",
 | 
				
			||||||
 | 
					                command=settings_win.destroy).pack(side=tk.LEFT, padx=5)
 | 
				
			||||||
 | 
					        btn_frame.pack(pady=10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def style_map(self):
 | 
				
			||||||
 | 
					        """Стилизация текущей карты под старинный свиток"""
 | 
				
			||||||
 | 
					        if not hasattr(self, 'biome_map') and not hasattr(self, 'height_map'):
 | 
				
			||||||
 | 
					            self.status_var.set("Ошибка: нет данных для стилизации")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self.status_var.set("Стилизация...")
 | 
				
			||||||
 | 
					            self.root.update_idletasks()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Подготовка данных
 | 
				
			||||||
 | 
					            if hasattr(self, 'biome_map') and self.biome_map is not None:
 | 
				
			||||||
 | 
					                img_data = (self.biome_map[:, :, :3] * 255).astype(np.uint8)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                norm_height = (self.height_map - self.height_map.min()) / (self.height_map.max() - self.height_map.min())
 | 
				
			||||||
 | 
					                img_data = (plt.cm.terrain(norm_height)[:, :, :3] * 255).astype(np.uint8)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Создаем PIL Image и применяем стилизацию
 | 
				
			||||||
 | 
					            pil_img = Image.fromarray(img_data, 'RGB')
 | 
				
			||||||
 | 
					            styled_img = MapStyler.apply_parchment_effect(pil_img)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Отображаем результат
 | 
				
			||||||
 | 
					            self.preview_window = tk.Toplevel(self.root)
 | 
				
			||||||
 | 
					            self.preview_window.title("Стилизованная карта")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            img_tk = ImageTk.PhotoImage(styled_img)
 | 
				
			||||||
 | 
					            label = ttk.Label(self.preview_window, image=img_tk)
 | 
				
			||||||
 | 
					            label.image = img_tk
 | 
				
			||||||
 | 
					            label.pack()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.status_var.set("Стилизация завершена!")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            self.status_var.set(f"Ошибка стилизации: {str(e)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_basic_settings(self, frame):
 | 
				
			||||||
 | 
					        """Настройки размера и типа карты"""
 | 
				
			||||||
 | 
					        # Размер карты
 | 
				
			||||||
 | 
					        size_frame = ttk.LabelFrame(frame, text="Размер карты")
 | 
				
			||||||
 | 
					        size_frame.pack(fill=tk.X, padx=5, pady=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        ttk.Label(size_frame, text="Ширина:").grid(row=0, column=0, sticky="e")
 | 
				
			||||||
 | 
					        ttk.Spinbox(size_frame, from_=100, to=1000, textvariable=self.width_var).grid(
 | 
				
			||||||
 | 
					            row=0, column=1, sticky="ew", padx=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        ttk.Label(size_frame, text="Высота:").grid(row=1, column=0, sticky="e")
 | 
				
			||||||
 | 
					        ttk.Spinbox(size_frame, from_=100, to=1000, textvariable=self.height_var).grid(
 | 
				
			||||||
 | 
					            row=1, column=1, sticky="ew", padx=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Тип генерации
 | 
				
			||||||
 | 
					        type_frame = ttk.LabelFrame(frame, text="Тип ландшафта")
 | 
				
			||||||
 | 
					        type_frame.pack(fill=tk.X, padx=5, pady=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        types = ["land_only", "continent", "islands"]
 | 
				
			||||||
 | 
					        texts = ["Только суша", "Материк", "Острова"]
 | 
				
			||||||
 | 
					        for t, text in zip(types, texts):
 | 
				
			||||||
 | 
					            ttk.Radiobutton(type_frame, text=text, variable=self.mode_var, value=t).pack(
 | 
				
			||||||
 | 
					                anchor=tk.W)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Параметры рельефа
 | 
				
			||||||
 | 
					        terrain_frame = ttk.LabelFrame(frame, text="Параметры рельефа")
 | 
				
			||||||
 | 
					        terrain_frame.pack(fill=tk.X, padx=5, pady=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        params = [
 | 
				
			||||||
 | 
					            ("Шероховатость:", self.terrain_rough_var),
 | 
				
			||||||
 | 
					            ("Размер материка:", self.continent_size_var),
 | 
				
			||||||
 | 
					            ("Шероховатость материка:", self.continent_rough_var),
 | 
				
			||||||
 | 
					            ("Плотность островов:", self.islands_dens_var),
 | 
				
			||||||
 | 
					            ("Шероховатость островов:", self.islands_rough_var),
 | 
				
			||||||
 | 
					            ("Размер островов:", self.island_size_var)
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for i, (text, var) in enumerate(params):
 | 
				
			||||||
 | 
					            ttk.Label(terrain_frame, text=text).grid(row=i, column=0, sticky="e", padx=5)
 | 
				
			||||||
 | 
					            ttk.Scale(terrain_frame, from_=0.1, to=1.0, variable=var, 
 | 
				
			||||||
 | 
					                     orient=tk.HORIZONTAL).grid(row=i, column=1, sticky="ew", padx=5)
 | 
				
			||||||
 | 
					            ttk.Label(terrain_frame, textvariable=var).grid(row=i, column=2, padx=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Режим климата
 | 
				
			||||||
 | 
					        climate_frame = ttk.LabelFrame(frame, text="Режим климата")
 | 
				
			||||||
 | 
					        climate_frame.pack(fill=tk.X, padx=5, pady=5)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if not hasattr(self.settings, 'climate_mode'):
 | 
				
			||||||
 | 
					            self.settings.climate_mode = 'realistic'
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        ttk.Radiobutton(climate_frame, text="Реалистичный", 
 | 
				
			||||||
 | 
					                       variable=self.settings.climate_mode, 
 | 
				
			||||||
 | 
					                       value="realistic").pack(anchor=tk.W)
 | 
				
			||||||
 | 
					        ttk.Radiobutton(climate_frame, text="Художественный", 
 | 
				
			||||||
 | 
					                       variable=self.settings.climate_mode, 
 | 
				
			||||||
 | 
					                       value="artistic").pack(anchor=tk.W)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_biome_settings(self, frame):
 | 
				
			||||||
 | 
					        """Настройки биомов через чекбоксы"""
 | 
				
			||||||
 | 
					        biome_frame = ttk.LabelFrame(frame, text="Выбор биомов")
 | 
				
			||||||
 | 
					        biome_frame.pack(fill=tk.BOTH, padx=5, pady=5, expand=True)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for i, (biome, var) in enumerate(self.biome_vars.items()):
 | 
				
			||||||
 | 
					            col = i % 3
 | 
				
			||||||
 | 
					            row = i // 3
 | 
				
			||||||
 | 
					            ttk.Checkbutton(
 | 
				
			||||||
 | 
					                biome_frame, 
 | 
				
			||||||
 | 
					                text=biome.capitalize(),
 | 
				
			||||||
 | 
					                variable=var
 | 
				
			||||||
 | 
					            ).grid(row=row, column=col, sticky="w", padx=5, pady=2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save_settings(self):
 | 
				
			||||||
 | 
					        """Сохраняет все настройки"""
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self.settings.width = self.width_var.get()
 | 
				
			||||||
 | 
					            self.settings.height = self.height_var.get()
 | 
				
			||||||
 | 
					            self.settings.mode = self.mode_var.get()
 | 
				
			||||||
 | 
					            self.settings.terrain_roughness = self.terrain_rough_var.get()
 | 
				
			||||||
 | 
					            self.settings.continent_size = self.continent_size_var.get()
 | 
				
			||||||
 | 
					            self.settings.continent_roughness = self.continent_rough_var.get()
 | 
				
			||||||
 | 
					            self.settings.islands_density = self.islands_dens_var.get()
 | 
				
			||||||
 | 
					            self.settings.islands_roughness = self.islands_rough_var.get()
 | 
				
			||||||
 | 
					            self.settings.island_size = self.island_size_var.get()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.status_var.set("Настройки сохранены")
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            self.status_var.set(f"Ошибка сохранения: {str(e)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_land(self):
 | 
				
			||||||
 | 
					        """Генерация карты земли/воды"""
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self.land_generator = WorldGenerator(
 | 
				
			||||||
 | 
					                width=self.settings.width,
 | 
				
			||||||
 | 
					                height=self.settings.height,
 | 
				
			||||||
 | 
					                seed=self.settings.get_valid_seed()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if self.settings.mode == 'land_only':
 | 
				
			||||||
 | 
					                self.height_map = self.land_generator.generate_land_only()
 | 
				
			||||||
 | 
					            elif self.settings.mode == 'continent':
 | 
				
			||||||
 | 
					                self.height_map = self.land_generator.generate_continent(
 | 
				
			||||||
 | 
					                    size=self.settings.continent_size,
 | 
				
			||||||
 | 
					                    roughness=self.settings.continent_roughness
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.height_map = self.land_generator.generate_islands(
 | 
				
			||||||
 | 
					                    density=self.settings.islands_density,
 | 
				
			||||||
 | 
					                    island_size=self.settings.island_size,
 | 
				
			||||||
 | 
					                    roughness=self.settings.islands_roughness
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.biome_map = None
 | 
				
			||||||
 | 
					            self.styled_map_path = None
 | 
				
			||||||
 | 
					            self.status_var.set("Карта земли сгенерирована")
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            self.status_var.set(f"Ошибка генерации земли: {str(e)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_height(self):
 | 
				
			||||||
 | 
					        """Генерация карты высот"""
 | 
				
			||||||
 | 
					        if not hasattr(self, 'height_map') or self.height_map is None:
 | 
				
			||||||
 | 
					            self.status_var.set("Ошибка: сначала сгенерируйте карту земли")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self.height_generator = HeightmapGenerator(
 | 
				
			||||||
 | 
					                width=self.settings.width,
 | 
				
			||||||
 | 
					                height=self.settings.height,
 | 
				
			||||||
 | 
					                seed=self.settings.get_valid_seed()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.height_map = self.height_generator.generate_heightmap(
 | 
				
			||||||
 | 
					                self.height_map,
 | 
				
			||||||
 | 
					                roughness=self.settings.terrain_roughness
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.biome_map = None
 | 
				
			||||||
 | 
					            self.styled_map_path = None
 | 
				
			||||||
 | 
					            self.status_var.set("Карта высот сгенерирована")
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            self.status_var.set(f"Ошибка генерации высот: {str(e)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_climate(self):
 | 
				
			||||||
 | 
					        if not hasattr(self, 'height_map'):
 | 
				
			||||||
 | 
					            self.status_var.set("Ошибка: сначала сгенерируйте карту высот")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            enabled_biomes = [b for b, var in self.biome_vars.items() if var.get()]
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if self.settings.climate_mode == 'realistic':
 | 
				
			||||||
 | 
					                from climate_zones_map import ClimateGenerator
 | 
				
			||||||
 | 
					                self.climate_gen = ClimateGenerator(self.height_map, self.settings)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                from climate_zones_artistic import ArtisticClimateGenerator
 | 
				
			||||||
 | 
					                self.climate_gen = ArtisticClimateGenerator(self.height_map, self.settings)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            self.biome_map = self.climate_gen.generate_biome_map(['water'] + enabled_biomes)
 | 
				
			||||||
 | 
					            self.status_var.set(f"Карта климата ({self.settings.climate_mode}) сгенерирована")
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            self.status_var.set(f"Ошибка генерации климата: {str(e)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def show_map(self):
 | 
				
			||||||
 | 
					        """Отображение карты во встроенном окне"""
 | 
				
			||||||
 | 
					        if not hasattr(self, 'biome_map') and not hasattr(self, 'height_map'):
 | 
				
			||||||
 | 
					            self.status_var.set("Нет данных для отображения")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.map_window:
 | 
				
			||||||
 | 
					            self.map_window.destroy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.map_window = tk.Toplevel(self.root)
 | 
				
			||||||
 | 
					        self.map_window.title("Просмотр карты")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        fig = plt.Figure(figsize=(8, 6))
 | 
				
			||||||
 | 
					        ax = fig.add_subplot(111)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if hasattr(self, 'biome_map') and self.biome_map is not None:
 | 
				
			||||||
 | 
					            ax.imshow(self.biome_map)
 | 
				
			||||||
 | 
					            ax.set_title("Карта биомов")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            ax.imshow(self.height_map, cmap='terrain')
 | 
				
			||||||
 | 
					            ax.set_title("Карта высот")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        ax.axis('off')
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.canvas = FigureCanvasTkAgg(fig, master=self.map_window)
 | 
				
			||||||
 | 
					        self.canvas.draw()
 | 
				
			||||||
 | 
					        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Кнопка закрытия
 | 
				
			||||||
 | 
					        ttk.Button(self.map_window, text="Закрыть", 
 | 
				
			||||||
 | 
					                 command=self.map_window.destroy).pack(pady=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    root = tk.Tk()
 | 
				
			||||||
 | 
					    app = WorldGeneratorApp(root)
 | 
				
			||||||
 | 
					    root.mainloop()
 | 
				
			||||||
							
								
								
									
										40
									
								
								generation_heightmap.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								generation_heightmap.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					from opensimplex import OpenSimplex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HeightmapGenerator:
 | 
				
			||||||
 | 
					    def __init__(self, width=200, height=200, seed=None):
 | 
				
			||||||
 | 
					        self.width = width
 | 
				
			||||||
 | 
					        self.height = height
 | 
				
			||||||
 | 
					        self.seed = seed if seed is not None else np.random.randint(0, 1000000)
 | 
				
			||||||
 | 
					        self.noise = OpenSimplex(seed=self.seed)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def generate_heightmap(self, land_water_map, roughness=0.5):
 | 
				
			||||||
 | 
					        heightmap = np.zeros((self.height, self.width))
 | 
				
			||||||
 | 
					        scale = 30 / (roughness + 0.1)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        base_noise = np.zeros((self.height, self.width))
 | 
				
			||||||
 | 
					        for y in range(self.height):
 | 
				
			||||||
 | 
					            for x in range(self.width):
 | 
				
			||||||
 | 
					                nx = x / scale
 | 
				
			||||||
 | 
					                ny = y / scale
 | 
				
			||||||
 | 
					                base_noise[y,x] = self.noise.noise2(nx, ny)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        base_noise = (base_noise - base_noise.min()) / (base_noise.max() - base_noise.min())
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        detail_scale = scale * 0.3
 | 
				
			||||||
 | 
					        detail_noise = np.zeros((self.height, self.width))
 | 
				
			||||||
 | 
					        for y in range(self.height):
 | 
				
			||||||
 | 
					            for x in range(self.width):
 | 
				
			||||||
 | 
					                nx = x / detail_scale
 | 
				
			||||||
 | 
					                ny = y / detail_scale
 | 
				
			||||||
 | 
					                detail_noise[y,x] = self.noise.noise2(nx, ny) * 0.3
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					        combined = base_noise + detail_noise
 | 
				
			||||||
 | 
					        combined = np.where(land_water_map == 1, combined, 0)
 | 
				
			||||||
 | 
					        combined = (combined - combined.min()) / (combined.max() - combined.min())
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        heightmap = np.where(land_water_map == 1, 
 | 
				
			||||||
 | 
					                           np.round(combined * 9 + 1) / 10, 
 | 
				
			||||||
 | 
					                           0)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return heightmap
 | 
				
			||||||
							
								
								
									
										79
									
								
								generation_landwater.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								generation_landwater.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					import opensimplex
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WorldGenerator:
 | 
				
			||||||
 | 
					    def __init__(self, width=200, height=200, seed=None):
 | 
				
			||||||
 | 
					        self.width = width
 | 
				
			||||||
 | 
					        self.height = height
 | 
				
			||||||
 | 
					        self.seed = seed if seed is not None else random.randint(0, 1000000)
 | 
				
			||||||
 | 
					        self.noise = opensimplex.OpenSimplex(seed=self.seed)
 | 
				
			||||||
 | 
					        random.seed(self.seed)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def generate_land_only(self):
 | 
				
			||||||
 | 
					        return np.ones((self.height, self.width))
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def generate_continent(self, size=0.7, roughness=0.5):
 | 
				
			||||||
 | 
					        world = np.zeros((self.height, self.width))
 | 
				
			||||||
 | 
					        center_x, center_y = self.width//2, self.height//2
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for y in range(self.height):
 | 
				
			||||||
 | 
					            for x in range(self.width):
 | 
				
			||||||
 | 
					                nx = (x - center_x) / (self.width * 0.8)
 | 
				
			||||||
 | 
					                ny = (y - center_y) / (self.height * 0.8)
 | 
				
			||||||
 | 
					                dist = np.sqrt(nx**2 + ny**2) * (1.0/size)
 | 
				
			||||||
 | 
					                noise_val = self.noise.noise2(x * roughness/10, y * roughness/10)
 | 
				
			||||||
 | 
					                world[y][x] = 1 if (1 - dist) + noise_val * 0.5 > 0.4 else 0
 | 
				
			||||||
 | 
					        return world
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def generate_islands(self, density=0.6, island_size=0.3, roughness=0.5):
 | 
				
			||||||
 | 
					        world = np.zeros((self.height, self.width))
 | 
				
			||||||
 | 
					        scale = 20 / (island_size + 0.1)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        temp_map = np.zeros((self.height, self.width))
 | 
				
			||||||
 | 
					        for y in range(self.height):
 | 
				
			||||||
 | 
					            for x in range(self.width):
 | 
				
			||||||
 | 
					                nx = x / scale * (1 + roughness)
 | 
				
			||||||
 | 
					                ny = y / scale * (1 + roughness)
 | 
				
			||||||
 | 
					                noise_val = self.noise.noise2(nx, ny)
 | 
				
			||||||
 | 
					                threshold = 0.5 - density*0.4
 | 
				
			||||||
 | 
					                if noise_val > threshold:
 | 
				
			||||||
 | 
					                    temp_map[y][x] = 1
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        islands = []
 | 
				
			||||||
 | 
					        visited = np.zeros_like(temp_map)
 | 
				
			||||||
 | 
					        for y in range(self.height):
 | 
				
			||||||
 | 
					            for x in range(self.width):
 | 
				
			||||||
 | 
					                if temp_map[y][x] == 1 and visited[y][x] == 0:
 | 
				
			||||||
 | 
					                    island = []
 | 
				
			||||||
 | 
					                    queue = [(y, x)]
 | 
				
			||||||
 | 
					                    visited[y][x] = 1
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    min_y, max_y = y, y
 | 
				
			||||||
 | 
					                    min_x, max_x = x, x
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    while queue:
 | 
				
			||||||
 | 
					                        cy, cx = queue.pop()
 | 
				
			||||||
 | 
					                        island.append((cy, cx))
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        min_y = min(min_y, cy)
 | 
				
			||||||
 | 
					                        max_y = max(max_y, cy)
 | 
				
			||||||
 | 
					                        min_x = min(min_x, cx)
 | 
				
			||||||
 | 
					                        max_x = max(max_x, cx)
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        for dy, dx in [(-1,0),(1,0),(0,-1),(0,1)]:
 | 
				
			||||||
 | 
					                            ny, nx = cy+dy, cx+dx
 | 
				
			||||||
 | 
					                            if (0 <= ny < self.height and 0 <= nx < self.width and 
 | 
				
			||||||
 | 
					                                temp_map[ny][nx] == 1 and visited[ny][nx] == 0):
 | 
				
			||||||
 | 
					                                visited[ny][nx] = 1
 | 
				
			||||||
 | 
					                                queue.append((ny, nx))
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if (min_y > 0 and max_y < self.height-1 and 
 | 
				
			||||||
 | 
					                        min_x > 0 and max_x < self.width-1):
 | 
				
			||||||
 | 
					                        islands.append(island)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for island in islands:
 | 
				
			||||||
 | 
					            for y, x in island:
 | 
				
			||||||
 | 
					                world[y][x] = 1
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return world
 | 
				
			||||||
							
								
								
									
										10
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					from frontend import WorldGeneratorApp
 | 
				
			||||||
 | 
					import tkinter as tk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    root = tk.Tk()
 | 
				
			||||||
 | 
					    app = WorldGeneratorApp(root)
 | 
				
			||||||
 | 
					    root.mainloop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										82
									
								
								map_styler.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								map_styler.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					from PIL import Image, ImageOps, ImageFilter, ImageDraw, ImageEnhance
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MapStyler:
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def apply_parchment_effect(pil_image, output_path=None):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Применяет эффект старинного свитка к изображению PIL
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            pil_image: PIL Image object
 | 
				
			||||||
 | 
					            output_path: необязательный путь для сохранения
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            PIL Image с эффектом
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # Текстура пергамента
 | 
				
			||||||
 | 
					        width, height = pil_image.size
 | 
				
			||||||
 | 
					        parchment = MapStyler._create_parchment_texture(width, height)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Эффект старения
 | 
				
			||||||
 | 
					        result_img = Image.blend(parchment, pil_image, 0.6)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Эффекты старения
 | 
				
			||||||
 | 
					        result_img = result_img.filter(ImageFilter.SMOOTH_MORE)
 | 
				
			||||||
 | 
					        result_img = ImageEnhance.Contrast(result_img).enhance(1.2)
 | 
				
			||||||
 | 
					        result_img = ImageEnhance.Color(result_img).enhance(0.9)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Декоративные элементы
 | 
				
			||||||
 | 
					        result_img = MapStyler._add_decorations(result_img)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if output_path:
 | 
				
			||||||
 | 
					            result_img.save(output_path, quality=95)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return result_img
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def _create_parchment_texture(width, height):
 | 
				
			||||||
 | 
					        """Создает текстуру пергамента"""
 | 
				
			||||||
 | 
					        # Цвет пергамента
 | 
				
			||||||
 | 
					        base = Image.new('RGB', (width, height), color=(240, 230, 200))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Добавляем шум и неравномерность
 | 
				
			||||||
 | 
					        noise = np.random.normal(0, 0.1, (height, width, 3)) * 255
 | 
				
			||||||
 | 
					        noise = np.clip(noise, -30, 30)
 | 
				
			||||||
 | 
					        noise_img = Image.fromarray(noise.astype(np.uint8), 'RGB')
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return Image.blend(base, noise_img, 0.3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def _add_decorations(img):
 | 
				
			||||||
 | 
					        """Добавляет декоративные элементы"""
 | 
				
			||||||
 | 
					        draw = ImageDraw.Draw(img)
 | 
				
			||||||
 | 
					        width, height = img.size
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Рамка в стиле старинной книги
 | 
				
			||||||
 | 
					        border_color = (139, 69, 19)  # Коричневый
 | 
				
			||||||
 | 
					        border_width = max(width, height) // 100
 | 
				
			||||||
 | 
					        draw.rectangle([(0, 0), (width-1, height-1)], outline=border_color, width=border_width)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Угловые узоры
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        corner_size = min(width, height) // 8
 | 
				
			||||||
 | 
					        corners = [(0, 0, 180, 270),(width-corner_size, 0, 270, 360),(0, height-corner_size, 90, 180),(width-corner_size, height-corner_size, 0, 90)]
 | 
				
			||||||
 | 
					        for x, y, start, end in corners:
 | 
				
			||||||
 | 
					            draw.arc([x, y, x+corner_size, y+corner_size], start, end, fill=border_color, width=2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Эффект потертости по краям
 | 
				
			||||||
 | 
					        mask = Image.new('L', (width, height), 255)
 | 
				
			||||||
 | 
					        edge_width = min(width, height) // 15
 | 
				
			||||||
 | 
					        for i in range(edge_width):
 | 
				
			||||||
 | 
					            alpha = int(255 * (i / edge_width) ** 0.5)
 | 
				
			||||||
 | 
					            for side in ['top', 'bottom', 'left', 'right']:
 | 
				
			||||||
 | 
					                if side in ['top', 'bottom']:
 | 
				
			||||||
 | 
					                    box = [0, i if side == 'top' else height-1-i, 
 | 
				
			||||||
 | 
					                          width, i+1 if side == 'top' else height-i]
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    box = [i if side == 'left' else width-1-i, 0, 
 | 
				
			||||||
 | 
					                          i+1 if side == 'left' else width-i, height]
 | 
				
			||||||
 | 
					                mask_draw = ImageDraw.Draw(mask)
 | 
				
			||||||
 | 
					                mask_draw.rectangle(box, fill=alpha)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return Image.composite(img, Image.new('RGB', img.size, (220, 210, 180)), mask)
 | 
				
			||||||
							
								
								
									
										21
									
								
								settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					from dataclasses import dataclass
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@dataclass
 | 
				
			||||||
 | 
					class WorldSettings:
 | 
				
			||||||
 | 
					    width: int = 400
 | 
				
			||||||
 | 
					    height: int = 300
 | 
				
			||||||
 | 
					    seed: int = None
 | 
				
			||||||
 | 
					    mode: str = 'continent'  # 'land_only', 'continent', 'islands'
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # Параметры рельефа
 | 
				
			||||||
 | 
					    terrain_roughness: float = 0.5
 | 
				
			||||||
 | 
					    continent_size: float = 0.7
 | 
				
			||||||
 | 
					    continent_roughness: float = 0.5
 | 
				
			||||||
 | 
					    islands_density: float = 0.6
 | 
				
			||||||
 | 
					    islands_roughness: float = 0.5
 | 
				
			||||||
 | 
					    island_size: float = 0.5
 | 
				
			||||||
 | 
					    climate_mode: str = 'realistic'  # 'realistic' или 'artistic'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_valid_seed(self):
 | 
				
			||||||
 | 
					        return self.seed if self.seed is not None else np.random.randint(0, 1000000)
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user