fractex 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fractex/3d.py +1585 -0
- fractex/__init__.py +38 -0
- fractex/advanced.py +170 -0
- fractex/cli.py +81 -0
- fractex/core.py +508 -0
- fractex/dynamic_textures_3d.py +1935 -0
- fractex/examples/3d.py +109 -0
- fractex/examples/3d_integration.py +113 -0
- fractex/examples/3d_integration_2d.py +59 -0
- fractex/examples/__init__.py +34 -0
- fractex/examples/_output.py +115 -0
- fractex/examples/architecture_pattern.py +61 -0
- fractex/examples/atmosphere.py +54 -0
- fractex/examples/composite_material.py +63 -0
- fractex/examples/crystal_cave.py +61 -0
- fractex/examples/custom_pattern.py +114 -0
- fractex/examples/game_integration.py +86 -0
- fractex/examples/game_texture.py +178 -0
- fractex/examples/integration.py +102 -0
- fractex/examples/physic_integration.py +70 -0
- fractex/examples/splash.py +159 -0
- fractex/examples/terrain.py +76 -0
- fractex/examples/underwater.py +94 -0
- fractex/examples/underwater_volkano.py +112 -0
- fractex/geometric_patterns_3d.py +2372 -0
- fractex/interactive.py +158 -0
- fractex/simplex_noise.py +1113 -0
- fractex/texture_blending.py +1377 -0
- fractex/volume_scattering.py +1263 -0
- fractex/volume_textures.py +8 -0
- fractex-0.1.0.dist-info/METADATA +100 -0
- fractex-0.1.0.dist-info/RECORD +36 -0
- fractex-0.1.0.dist-info/WHEEL +5 -0
- fractex-0.1.0.dist-info/entry_points.txt +2 -0
- fractex-0.1.0.dist-info/licenses/LICENSE +21 -0
- fractex-0.1.0.dist-info/top_level.txt +1 -0
fractex/__init__.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Fractex public API."""
|
|
2
|
+
|
|
3
|
+
from .core import (
|
|
4
|
+
FractalParams,
|
|
5
|
+
FractalGenerator,
|
|
6
|
+
InfiniteTexture,
|
|
7
|
+
TextureStreamer,
|
|
8
|
+
OptimizedNoise,
|
|
9
|
+
)
|
|
10
|
+
from .interactive import (
|
|
11
|
+
InteractiveConfig,
|
|
12
|
+
add_interactive_args,
|
|
13
|
+
add_preset_arg,
|
|
14
|
+
resolve_preset,
|
|
15
|
+
run_interactive,
|
|
16
|
+
get_screen_size,
|
|
17
|
+
resize_nearest,
|
|
18
|
+
)
|
|
19
|
+
from .examples import list_examples, run_example
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"FractalParams",
|
|
23
|
+
"FractalGenerator",
|
|
24
|
+
"InfiniteTexture",
|
|
25
|
+
"TextureStreamer",
|
|
26
|
+
"OptimizedNoise",
|
|
27
|
+
"InteractiveConfig",
|
|
28
|
+
"add_interactive_args",
|
|
29
|
+
"add_preset_arg",
|
|
30
|
+
"resolve_preset",
|
|
31
|
+
"run_interactive",
|
|
32
|
+
"get_screen_size",
|
|
33
|
+
"resize_nearest",
|
|
34
|
+
"list_examples",
|
|
35
|
+
"run_example",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
__version__ = "0.1.0"
|
fractex/advanced.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# fractex/advanced.py
|
|
2
|
+
"""
|
|
3
|
+
Продвинутые алгоритмы бесконечной детализации
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from numba import jit, prange
|
|
8
|
+
from typing import List, Tuple
|
|
9
|
+
import threading
|
|
10
|
+
from queue import Queue
|
|
11
|
+
|
|
12
|
+
class GPUAcceleratedTexture:
|
|
13
|
+
"""Использование GPU для генерации текстур (через PyOpenCL или CuPy)"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, use_gpu=True):
|
|
16
|
+
self.use_gpu = use_gpu
|
|
17
|
+
self._init_gpu()
|
|
18
|
+
|
|
19
|
+
def _init_gpu(self):
|
|
20
|
+
"""Инициализация GPU контекста"""
|
|
21
|
+
try:
|
|
22
|
+
import pyopencl as cl
|
|
23
|
+
self.ctx = cl.create_some_context()
|
|
24
|
+
self.queue = cl.CommandQueue(self.ctx)
|
|
25
|
+
self.gpu_available = True
|
|
26
|
+
except:
|
|
27
|
+
self.gpu_available = False
|
|
28
|
+
|
|
29
|
+
def generate_on_gpu(self, x_coords, y_coords, params):
|
|
30
|
+
"""Генерация текстуры на GPU"""
|
|
31
|
+
if not self.gpu_available:
|
|
32
|
+
return self.generate_on_cpu(x_coords, y_coords, params)
|
|
33
|
+
|
|
34
|
+
# Реализация OpenCL кернела для фрактального шума
|
|
35
|
+
# ...
|
|
36
|
+
|
|
37
|
+
@jit(nopython=True, parallel=True, nogil=True)
|
|
38
|
+
def fractal_noise_parallel(x, y, params):
|
|
39
|
+
"""
|
|
40
|
+
Параллельная генерация фрактального шума с помощью Numba
|
|
41
|
+
Ускорение в 50-100 раз на многоядерных CPU
|
|
42
|
+
"""
|
|
43
|
+
height, width = x.shape
|
|
44
|
+
result = np.zeros((height, width), dtype=np.float32)
|
|
45
|
+
|
|
46
|
+
for i in prange(height):
|
|
47
|
+
for j in range(width):
|
|
48
|
+
total = 0.0
|
|
49
|
+
frequency = params['base_scale']
|
|
50
|
+
amplitude = 1.0
|
|
51
|
+
max_amplitude = 0.0
|
|
52
|
+
|
|
53
|
+
for octave in range(params['octaves']):
|
|
54
|
+
nx = x[i, j] * frequency
|
|
55
|
+
ny = y[i, j] * frequency
|
|
56
|
+
|
|
57
|
+
# Быстрый симплекс-шум
|
|
58
|
+
noise_val = fast_simplex_2d(nx, ny, octave)
|
|
59
|
+
total += amplitude * noise_val
|
|
60
|
+
max_amplitude += amplitude
|
|
61
|
+
|
|
62
|
+
amplitude *= params['persistence']
|
|
63
|
+
frequency *= params['lacunarity']
|
|
64
|
+
|
|
65
|
+
if amplitude < 0.001:
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
result[i, j] = total / max_amplitude if max_amplitude > 0 else 0
|
|
69
|
+
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
@jit(nopython=True)
|
|
73
|
+
def fast_simplex_2d(x, y, seed):
|
|
74
|
+
"""Оптимизированный 2D симплекс-шум"""
|
|
75
|
+
# Упрощенная реализация для скорости
|
|
76
|
+
F2 = 0.3660254037844386 # 0.5*(sqrt(3)-1)
|
|
77
|
+
G2 = 0.21132486540518713 # (3-sqrt(3))/6
|
|
78
|
+
|
|
79
|
+
s = (x + y) * F2
|
|
80
|
+
i = np.floor(x + s)
|
|
81
|
+
j = np.floor(y + s)
|
|
82
|
+
|
|
83
|
+
t = (i + j) * G2
|
|
84
|
+
X0 = i - t
|
|
85
|
+
Y0 = j - t
|
|
86
|
+
x0 = x - X0
|
|
87
|
+
y0 = y - Y0
|
|
88
|
+
|
|
89
|
+
# Определяем, в каком треугольнике симплекса находимся
|
|
90
|
+
i1, j1 = (1, 0) if x0 > y0 else (0, 1)
|
|
91
|
+
|
|
92
|
+
x1 = x0 - i1 + G2
|
|
93
|
+
y1 = y0 - j1 + G2
|
|
94
|
+
x2 = x0 - 1.0 + 2.0 * G2
|
|
95
|
+
y2 = y0 - 1.0 + 2.0 * G2
|
|
96
|
+
|
|
97
|
+
# Хеширование углов
|
|
98
|
+
ii = int(i) & 255
|
|
99
|
+
jj = int(j) & 255
|
|
100
|
+
|
|
101
|
+
perm = np.arange(512)
|
|
102
|
+
gi0 = perm[ii + perm[jj]] % 12
|
|
103
|
+
gi1 = perm[ii + i1 + perm[jj + j1]] % 12
|
|
104
|
+
gi2 = perm[ii + 1 + perm[jj + 1]] % 12
|
|
105
|
+
|
|
106
|
+
# Градиенты (12 штук)
|
|
107
|
+
grad3 = np.array([
|
|
108
|
+
[1,1,0], [-1,1,0], [1,-1,0], [-1,-1,0],
|
|
109
|
+
[1,0,1], [-1,0,1], [1,0,-1], [-1,0,-1],
|
|
110
|
+
[0,1,1], [0,-1,1], [0,1,-1], [0,-1,-1]
|
|
111
|
+
], dtype=np.float32)
|
|
112
|
+
|
|
113
|
+
# Скалярные произведения
|
|
114
|
+
t0 = 0.5 - x0*x0 - y0*y0
|
|
115
|
+
n0 = 0.0
|
|
116
|
+
if t0 > 0:
|
|
117
|
+
t0 *= t0
|
|
118
|
+
n0 = t0 * t0 * (grad3[gi0, 0]*x0 + grad3[gi0, 1]*y0)
|
|
119
|
+
|
|
120
|
+
t1 = 0.5 - x1*x1 - y1*y1
|
|
121
|
+
n1 = 0.0
|
|
122
|
+
if t1 > 0:
|
|
123
|
+
t1 *= t1
|
|
124
|
+
n1 = t1 * t1 * (grad3[gi1, 0]*x1 + grad3[gi1, 1]*y1)
|
|
125
|
+
|
|
126
|
+
t2 = 0.5 - x2*x2 - y2*y2
|
|
127
|
+
n2 = 0.0
|
|
128
|
+
if t2 > 0:
|
|
129
|
+
t2 *= t2
|
|
130
|
+
n2 = t2 * t2 * (grad3[gi2, 0]*x2 + grad3[gi2, 1]*y2)
|
|
131
|
+
|
|
132
|
+
return 70.0 * (n0 + n1 + n2)
|
|
133
|
+
|
|
134
|
+
class InfiniteDetailTexture:
|
|
135
|
+
"""
|
|
136
|
+
Текстура с истинно бесконечной детализацией
|
|
137
|
+
Использует адаптивные квадродеревья для LOD
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def __init__(self, base_generator, max_depth=20):
|
|
141
|
+
self.generator = base_generator
|
|
142
|
+
self.max_depth = max_depth
|
|
143
|
+
self.quadtree = {}
|
|
144
|
+
self.lod_bias = 1.0
|
|
145
|
+
|
|
146
|
+
def get_pixel(self, world_x, world_y, screen_size, pixel_size):
|
|
147
|
+
"""
|
|
148
|
+
Получение пикселя с учетом экранного пространства
|
|
149
|
+
screen_size: размер экрана в пикселях
|
|
150
|
+
pixel_size: размер пикселя в мировых координатах
|
|
151
|
+
"""
|
|
152
|
+
# Определяем необходимый уровень детализации
|
|
153
|
+
required_detail = -np.log2(pixel_size) * self.lod_bias
|
|
154
|
+
lod = max(0, min(int(required_detail), self.max_depth))
|
|
155
|
+
|
|
156
|
+
# Получаем или создаем нужный тайл
|
|
157
|
+
tile_key = self._get_tile_key(world_x, world_y, lod)
|
|
158
|
+
|
|
159
|
+
if tile_key not in self.quadtree:
|
|
160
|
+
self._generate_tile(tile_key, world_x, world_y, lod)
|
|
161
|
+
|
|
162
|
+
# Билинейная интерполяция внутри тайла
|
|
163
|
+
return self._sample_tile(tile_key, world_x, world_y)
|
|
164
|
+
|
|
165
|
+
def _get_tile_key(self, x, y, lod):
|
|
166
|
+
"""Ключ для тайла в квадродереве"""
|
|
167
|
+
tile_size = 2 ** (-lod) # Размер тайла уменьшается с увеличением lod
|
|
168
|
+
tile_x = int(x // tile_size)
|
|
169
|
+
tile_y = int(y // tile_size)
|
|
170
|
+
return (tile_x, tile_y, lod)
|
fractex/cli.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""CLI for running Fractex interactive examples."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import runpy
|
|
7
|
+
import sys
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _examples() -> List[str]:
|
|
12
|
+
return [
|
|
13
|
+
"splash",
|
|
14
|
+
"custom_pattern",
|
|
15
|
+
"architecture_pattern",
|
|
16
|
+
"composite_material",
|
|
17
|
+
"crystal_cave",
|
|
18
|
+
"integration",
|
|
19
|
+
"terrain",
|
|
20
|
+
"3d_integration_2d",
|
|
21
|
+
"3d_integration",
|
|
22
|
+
"3d",
|
|
23
|
+
"underwater",
|
|
24
|
+
"underwater_volkano",
|
|
25
|
+
"game_texture",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main() -> None:
|
|
30
|
+
parser = argparse.ArgumentParser(description="Fractex CLI")
|
|
31
|
+
parser.add_argument("example", nargs="?", help="Example name")
|
|
32
|
+
parser.add_argument("--list", action="store_true", help="List available examples")
|
|
33
|
+
parser.add_argument("--interactive", action="store_true", help="Run interactive mode")
|
|
34
|
+
parser.add_argument("--no-interactive", action="store_true", help="Disable interactive mode")
|
|
35
|
+
parser.add_argument("--scale", type=float, default=None)
|
|
36
|
+
parser.add_argument("--fps", type=float, default=None)
|
|
37
|
+
parser.add_argument("--width", type=int, default=None)
|
|
38
|
+
parser.add_argument("--height", type=int, default=None)
|
|
39
|
+
parser.add_argument("--preset", type=str, default=None)
|
|
40
|
+
parser.add_argument("args", nargs=argparse.REMAINDER, help="Extra args passed to example")
|
|
41
|
+
args = parser.parse_args()
|
|
42
|
+
|
|
43
|
+
if args.list or not args.example:
|
|
44
|
+
print("Available examples:")
|
|
45
|
+
for name in _examples():
|
|
46
|
+
print(f" - {name}")
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
if args.example not in _examples():
|
|
50
|
+
print(f"Unknown example '{args.example}'. Use --list to see options.")
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
|
|
53
|
+
module = f"fractex.examples.{args.example}"
|
|
54
|
+
forwarded = []
|
|
55
|
+
|
|
56
|
+
interactive = True
|
|
57
|
+
if args.no_interactive:
|
|
58
|
+
interactive = False
|
|
59
|
+
if args.interactive:
|
|
60
|
+
interactive = True
|
|
61
|
+
|
|
62
|
+
if interactive:
|
|
63
|
+
forwarded.append("--interactive")
|
|
64
|
+
|
|
65
|
+
for name in ("scale", "fps", "width", "height", "preset"):
|
|
66
|
+
value = getattr(args, name)
|
|
67
|
+
if value is not None:
|
|
68
|
+
forwarded.extend([f"--{name}", str(value)])
|
|
69
|
+
|
|
70
|
+
if args.args:
|
|
71
|
+
if args.args[0] == "--":
|
|
72
|
+
forwarded.extend(args.args[1:])
|
|
73
|
+
else:
|
|
74
|
+
forwarded.extend(args.args)
|
|
75
|
+
|
|
76
|
+
sys.argv = [module] + forwarded
|
|
77
|
+
runpy.run_module(module, run_name="__main__")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
main()
|