fractex 0.1.1__py3-none-any.whl → 0.2.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/cli.py +1 -0
- fractex/examples/__init__.py +1 -0
- fractex/examples/architecture_pattern.py +1 -0
- fractex/examples/fire_flame.py +141 -0
- fractex/examples/splash.py +5 -1
- fractex/interactive.py +11 -1
- {fractex-0.1.1.dist-info → fractex-0.2.0.dist-info}/METADATA +1 -1
- {fractex-0.1.1.dist-info → fractex-0.2.0.dist-info}/RECORD +12 -11
- {fractex-0.1.1.dist-info → fractex-0.2.0.dist-info}/WHEEL +0 -0
- {fractex-0.1.1.dist-info → fractex-0.2.0.dist-info}/entry_points.txt +0 -0
- {fractex-0.1.1.dist-info → fractex-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {fractex-0.1.1.dist-info → fractex-0.2.0.dist-info}/top_level.txt +0 -0
fractex/cli.py
CHANGED
fractex/examples/__init__.py
CHANGED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import argparse
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
7
|
+
if str(ROOT) not in sys.path:
|
|
8
|
+
sys.path.insert(0, str(ROOT))
|
|
9
|
+
EXAMPLES_DIR = Path(__file__).resolve().parent
|
|
10
|
+
if str(EXAMPLES_DIR) not in sys.path:
|
|
11
|
+
sys.path.insert(0, str(EXAMPLES_DIR))
|
|
12
|
+
|
|
13
|
+
from fractex.simplex_noise import SimplexNoise
|
|
14
|
+
from fractex.interactive import add_interactive_args, InteractiveConfig, run_interactive
|
|
15
|
+
from _output import save_ppm
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _render_flame(t: float, w: int, h: int, noise: SimplexNoise) -> np.ndarray:
|
|
19
|
+
x = np.linspace(-1.0, 1.0, w)
|
|
20
|
+
y = np.linspace(0.0, 2.0, h)
|
|
21
|
+
xx, yy = np.meshgrid(x, y)
|
|
22
|
+
taper = 1.0 - 0.5 * np.clip(yy / 2.0, 0, 1)
|
|
23
|
+
xx_t = xx * taper
|
|
24
|
+
zz = np.ones_like(xx) * (t * 0.15)
|
|
25
|
+
|
|
26
|
+
base = noise.noise_3d(xx_t * 1.5, yy * 1.2, zz)
|
|
27
|
+
detail = noise.noise_3d(xx_t * 5.0, yy * 4.0, zz * 1.8)
|
|
28
|
+
micro = noise.noise_3d(xx_t * 14.0, yy * 10.0, zz * 5.0)
|
|
29
|
+
warp = noise.noise_3d(xx_t * 1.2, yy * 1.0, zz * 0.8)
|
|
30
|
+
turbulence = base * 0.45 + detail * 0.3 + micro * 0.2 + warp * 0.05
|
|
31
|
+
turbulence = (turbulence + 1) * 0.5
|
|
32
|
+
|
|
33
|
+
# Центральное ядро + вытянутая форма вверх (с повышенным контрастом)
|
|
34
|
+
core = np.exp(-((xx_t * 1.8) ** 2)) * np.clip(2.6 - yy, 0, 1)
|
|
35
|
+
# Языки с резкими краями (степень для контраста)
|
|
36
|
+
tongues_raw = np.exp(-((xx_t * 5.0 + warp * 1.6) ** 2)) * np.clip(3.0 - yy, 0, 1)
|
|
37
|
+
tongues = np.clip(tongues_raw * 1.5 - 0.2, 0, 1) ** 0.7
|
|
38
|
+
lobe_raw = np.exp(-((xx_t * 6.0) ** 2)) * np.clip(3.2 - yy, 0, 1) * (0.6 + 0.4 * np.sin(t * 0.7 + yy * 4.0))
|
|
39
|
+
lobe = np.clip(lobe_raw * 1.4 - 0.15, 0, 1) ** 0.8
|
|
40
|
+
jets_raw = np.clip(turbulence - 0.15, 0, 1) * np.exp(-((xx_t * 7.0 + warp * 1.8) ** 2)) * np.clip(3.4 - yy, 0, 1)
|
|
41
|
+
jets = np.clip(jets_raw * 1.6 - 0.1, 0, 1) ** 0.6
|
|
42
|
+
rips = np.clip(turbulence * 1.4 - 0.35, 0, 1) * np.exp(-((xx_t * 4.2) ** 2)) * np.clip(2.2 - yy, 0, 1)
|
|
43
|
+
|
|
44
|
+
# Усиленные языки у основания (резкие)
|
|
45
|
+
base_tongues_raw = np.exp(-((xx_t * 3.5 + warp * 2.0) ** 2)) * np.clip(1.0 - yy * 0.6, 0, 1)
|
|
46
|
+
base_tongues = np.clip(base_tongues_raw * 1.5 - 0.15, 0, 1) ** 0.7
|
|
47
|
+
base_flicker = (0.7 + 0.3 * np.sin(t * 2.5 + xx * 8.0)) * np.clip(0.8 - yy, 0, 1)
|
|
48
|
+
base_texture = np.clip(detail * 0.8 + micro * 0.4, 0, 1) * np.exp(-((xx_t * 2.0) ** 2)) * np.clip(0.6 - yy * 0.5, 0, 1)
|
|
49
|
+
|
|
50
|
+
# Языки пламени веером от центра основания к краям
|
|
51
|
+
yy_safe = np.maximum(yy, 0.05) # избегаем деления на 0
|
|
52
|
+
# Больше языков под меньшими углами (более вертикальные)
|
|
53
|
+
tongue_angles = [-0.2, -0.13, -0.07, 0.0, 0.07, 0.13, 0.2] # 7 языков, узкий веер
|
|
54
|
+
tongues_radial = np.zeros_like(xx)
|
|
55
|
+
for i, ta in enumerate(tongue_angles):
|
|
56
|
+
# Ось языка с динамическим колебанием
|
|
57
|
+
wobble = noise.noise_3d(np.full_like(xx, i * 0.7), yy * 1.5 - t * 0.6, zz) * 0.08
|
|
58
|
+
tongue_axis = yy_safe * (ta + wobble)
|
|
59
|
+
dist_from_axis = np.abs(xx_t - tongue_axis)
|
|
60
|
+
# Ширина: больше у основания, сужается вверх
|
|
61
|
+
width = 0.10 * np.clip(1.0 - yy * 0.35, 0.25, 1.0)
|
|
62
|
+
tongue_shape = np.exp(-((dist_from_axis / width) ** 2))
|
|
63
|
+
# Контрастность: резкие края
|
|
64
|
+
tongue_shape = np.clip(tongue_shape * 1.5 - 0.2, 0, 1) ** 0.7
|
|
65
|
+
# Яркость: сильнее у основания, центральный язык ярче
|
|
66
|
+
center_boost = 1.0 + 0.4 * np.exp(-(ta * 8) ** 2) # центр ярче
|
|
67
|
+
brightness = np.clip(2.0 - yy * 1.0, 0, 1) * (0.8 + 0.2 * np.sin(t * 2.0 + i * 1.0)) * center_boost
|
|
68
|
+
tongues_radial += tongue_shape * brightness
|
|
69
|
+
tongues_radial = np.clip(tongues_radial * 0.6, 0, 1)
|
|
70
|
+
|
|
71
|
+
flame = np.clip(core * (0.2 + 1.0 * turbulence) + tongues * 1.0 + lobe * 0.7 + jets * 0.7 + rips * 0.4 + base_tongues * 0.8 + base_flicker * 0.4 + base_texture * 0.5 + tongues_radial * 0.7, 0, 1)
|
|
72
|
+
flame = np.clip((flame - 0.1) * 1.6, 0, 1)
|
|
73
|
+
|
|
74
|
+
# Подсветка ядра
|
|
75
|
+
core_glow = np.exp(-((xx_t * 3.2) ** 2 + (yy * 1.6) ** 2)) * np.clip(1.6 - yy, 0, 1)
|
|
76
|
+
|
|
77
|
+
# Дым: только в верхней части (выше yy=1.0) - полностью чистый низ
|
|
78
|
+
smoke_vertical = np.clip((yy - 1.0) * 2.0, 0, 1) # маска: 0 внизу, 1 вверху
|
|
79
|
+
smoke = np.clip((yy - 1.5) * 0.7, 0, 1) * (0.2 + 0.5 * turbulence)
|
|
80
|
+
side_mask = np.clip((np.abs(xx) * 1.8 - 0.5), 0, 1)
|
|
81
|
+
smoke = smoke * np.exp(-((xx_t * 1.5 + warp * 0.6) ** 2)) * side_mask
|
|
82
|
+
trail = np.exp(-((xx_t * 0.8 + warp * 0.6) ** 2)) * np.clip(yy - 1.2, 0, 1)
|
|
83
|
+
trail = trail * (0.3 + 0.5 * turbulence) * (0.5 + 0.5 * np.sin(t * 0.3 + yy * 1.5))
|
|
84
|
+
smoke = np.clip((smoke + trail * 0.4) * smoke_vertical, 0, 1)
|
|
85
|
+
|
|
86
|
+
# Искры: у основания без маски, выше - только по краям
|
|
87
|
+
spark_mask = np.clip(micro - 0.7, 0, 1)
|
|
88
|
+
edge = np.clip(np.abs(xx_t) * 2.0, 0, 1)
|
|
89
|
+
bottom_zone = np.clip(1.0 - yy, 0, 1)
|
|
90
|
+
sparks = spark_mask ** 2 * (bottom_zone * 1.2 + (1 - bottom_zone) * edge * 0.5)
|
|
91
|
+
|
|
92
|
+
rgb = np.full((h, w, 3), np.array([0.55, 0.27, 0.20], dtype=np.float32), dtype=np.float32)
|
|
93
|
+
heat = np.clip(flame * 1.2, 0, 1)
|
|
94
|
+
|
|
95
|
+
# Температурный градиент: краснее/оранжевее у основания, белее к ядру
|
|
96
|
+
vertical = np.clip(1.2 - yy, 0, 1)
|
|
97
|
+
temp = np.clip(vertical * 0.6 + heat * 0.7, 0, 1)
|
|
98
|
+
cold_color = np.array([1.0, 0.35, 0.02], dtype=np.float32) # более насыщенный оранжево-красный
|
|
99
|
+
hot_color = np.array([1.0, 0.92, 0.8], dtype=np.float32)
|
|
100
|
+
temp_rgb = cold_color * (1 - temp)[..., None] + hot_color * temp[..., None]
|
|
101
|
+
base_boost = np.clip(1.0 - yy * 0.3, 0.75, 1.0)
|
|
102
|
+
base_heat = np.clip(1.0 - yy * 0.5, 0, 1) * np.exp(-((xx_t * 1.8) ** 2))
|
|
103
|
+
# Дополнительная оранжевая текстура у основания (усилена)
|
|
104
|
+
orange_base = np.clip(detail * 0.5 + 0.5, 0, 1) * np.clip(0.8 - yy * 0.45, 0, 1) * np.exp(-((xx_t * 1.5) ** 2))
|
|
105
|
+
# Красное свечение у самого основания
|
|
106
|
+
red_base = np.clip(0.5 - yy * 0.6, 0, 1) * np.exp(-((xx_t * 1.2) ** 2)) * (0.7 + 0.3 * turbulence)
|
|
107
|
+
|
|
108
|
+
core_boost = 1.0 + 0.5 * core_glow
|
|
109
|
+
core_ripple = 0.85 + 0.15 * np.sin(t * 1.4 + (xx_t + yy) * 3.5) * core_glow
|
|
110
|
+
rgb[..., 0] = np.clip(temp_rgb[..., 0] * heat * core_boost * core_ripple * (1.15 * base_boost) + base_heat * 0.55 + orange_base * 0.7 + red_base * 0.5 + sparks * 1.6 + core_glow * 0.35, 0, 1)
|
|
111
|
+
rgb[..., 1] = np.clip(temp_rgb[..., 1] * heat * core_boost * core_ripple * (1.08 * base_boost) + base_heat * 0.35 + orange_base * 0.35 + red_base * 0.15 + sparks * 0.8 + core_glow * 0.2, 0, 1)
|
|
112
|
+
rgb[..., 2] = np.clip(temp_rgb[..., 2] * heat * (0.9 + 0.3 * core_glow) * core_ripple * (0.95 * base_boost) + base_heat * 0.03 + core_glow * 0.06, 0, 1)
|
|
113
|
+
|
|
114
|
+
# Добавляем дым как серо-синий слой
|
|
115
|
+
rgb[..., 0] = np.clip(rgb[..., 0] * (1 - smoke) + smoke * 0.3, 0, 1)
|
|
116
|
+
rgb[..., 1] = np.clip(rgb[..., 1] * (1 - smoke) + smoke * 0.35, 0, 1)
|
|
117
|
+
rgb[..., 2] = np.clip(rgb[..., 2] * (1 - smoke) + smoke * 0.4, 0, 1)
|
|
118
|
+
return rgb
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def main():
|
|
122
|
+
parser = argparse.ArgumentParser(description="Fire flame example")
|
|
123
|
+
add_interactive_args(parser)
|
|
124
|
+
args = parser.parse_args()
|
|
125
|
+
|
|
126
|
+
noise = SimplexNoise(seed=42)
|
|
127
|
+
|
|
128
|
+
if args.interactive:
|
|
129
|
+
config = InteractiveConfig.from_args(args, title="fractex")
|
|
130
|
+
|
|
131
|
+
def render_frame(t, w, h):
|
|
132
|
+
return _render_flame(t, w, h, noise)
|
|
133
|
+
|
|
134
|
+
run_interactive(render_frame, config)
|
|
135
|
+
else:
|
|
136
|
+
image = _render_flame(0.0, 512, 512, noise)
|
|
137
|
+
save_ppm(image, EXAMPLES_DIR / "output" / "fire_flame.ppm", stretch=True)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
if __name__ == "__main__":
|
|
141
|
+
main()
|
fractex/examples/splash.py
CHANGED
|
@@ -58,7 +58,11 @@ def main():
|
|
|
58
58
|
dpi = fig.get_dpi()
|
|
59
59
|
fig.set_size_inches(screen_width / dpi, screen_height / dpi)
|
|
60
60
|
ax.axis("off")
|
|
61
|
-
ax.set_title(
|
|
61
|
+
ax.set_title("fractex")
|
|
62
|
+
try:
|
|
63
|
+
fig.canvas.manager.set_window_title("fractex")
|
|
64
|
+
except Exception:
|
|
65
|
+
pass
|
|
62
66
|
|
|
63
67
|
texture = textures[presets[0]]
|
|
64
68
|
width = max(64, int(screen_width * max(0.1, args.scale)))
|
fractex/interactive.py
CHANGED
|
@@ -104,6 +104,7 @@ def run_interactive(
|
|
|
104
104
|
try:
|
|
105
105
|
import matplotlib.pyplot as plt
|
|
106
106
|
from matplotlib.animation import FuncAnimation
|
|
107
|
+
import matplotlib as mpl
|
|
107
108
|
except Exception:
|
|
108
109
|
print("matplotlib is not available; cannot display interactive output.")
|
|
109
110
|
return
|
|
@@ -114,12 +115,21 @@ def run_interactive(
|
|
|
114
115
|
width = max(config.min_render, width)
|
|
115
116
|
height = max(config.min_render, height)
|
|
116
117
|
|
|
118
|
+
mpl.rcParams["toolbar"] = "None"
|
|
117
119
|
fig, ax = plt.subplots()
|
|
118
120
|
dpi = fig.get_dpi()
|
|
119
121
|
fig.set_size_inches(width / dpi, height / dpi)
|
|
120
122
|
ax.axis("off")
|
|
121
|
-
ax.set_title(
|
|
123
|
+
ax.set_title("fractex")
|
|
122
124
|
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
|
|
125
|
+
try:
|
|
126
|
+
fig.canvas.manager.toolbar.setVisible(False)
|
|
127
|
+
except Exception:
|
|
128
|
+
pass
|
|
129
|
+
try:
|
|
130
|
+
fig.canvas.manager.set_window_title("fractex")
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
123
133
|
|
|
124
134
|
target_ms = 1000.0 / max(1.0, config.target_fps)
|
|
125
135
|
render_scale = 1.0
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
fractex/3d.py,sha256=m7FmIgtouncb8LVpev3MjhOUNMaKEa6MmE3B8N2lj78,65079
|
|
2
2
|
fractex/__init__.py,sha256=QheT5gsEbyQjvBo_HKy7rgWuhDepJ_UT1io7n_TcpG8,737
|
|
3
3
|
fractex/advanced.py,sha256=UnyLEtH9rZGYSMhL6W4iZzKjTncx1-ToVGtaXbjujrA,5834
|
|
4
|
-
fractex/cli.py,sha256=
|
|
4
|
+
fractex/cli.py,sha256=_54z7rFeJY4sfZm_XwHuOEkF_g-kJtNmGBPNjyjwXY0,2490
|
|
5
5
|
fractex/core.py,sha256=VppjXFtc-15wsKXJ8yXU-fdpIlzvWd-2xLnI1-qP0A0,21546
|
|
6
6
|
fractex/dynamic_textures_3d.py,sha256=ZFkdfbGak1fS-LkPkw_-bXSuPjDaB0RUWdIlo595l-g,83944
|
|
7
7
|
fractex/geometric_patterns_3d.py,sha256=kKGuAMYcNPJLvCUlRO7DzgJIAi5w-b7YkdwmznbKv94,103341
|
|
8
|
-
fractex/interactive.py,sha256=
|
|
8
|
+
fractex/interactive.py,sha256=pVFrXZ3kUXo-3UuKYQHKLvM6oyYIgvmp6K1iY-4jSg4,5558
|
|
9
9
|
fractex/simplex_noise.py,sha256=JuMl0tqo4Qq1XC8fmaxLSiH9GppYVvBhmBI5Zc2ZYpc,41571
|
|
10
10
|
fractex/texture_blending.py,sha256=2bgmqqsw6v6M2qDp5cEhC-SjdbnvPGxmZLJf7Iaa7Ds,58506
|
|
11
11
|
fractex/volume_scattering.py,sha256=T04rrW1N4VwimNskVww7K6XB1rEqo0foBXii6wAvhkk,54040
|
|
@@ -13,24 +13,25 @@ fractex/volume_textures.py,sha256=o-89qqZ1sk7No8w7UHiCTkdFe_m3NmFwyvBEJooL3is,27
|
|
|
13
13
|
fractex/examples/3d.py,sha256=ULpDhMBap5V_CUo2DNPOtsTEF9U7-W2ZWhSVVGsa9P8,3469
|
|
14
14
|
fractex/examples/3d_integration.py,sha256=jiGv_fkirWLSbADmztVOzfO6EfFNs53C6YyesT3RhPk,3781
|
|
15
15
|
fractex/examples/3d_integration_2d.py,sha256=On2LRHV4IZdF1LcUtlJa6poEqD-RNxkrOFix92lCcfo,2193
|
|
16
|
-
fractex/examples/__init__.py,sha256=
|
|
16
|
+
fractex/examples/__init__.py,sha256=w2TZrQjob4nE9eRaWLYEKkaETerHPbdRNCnNgBilCng,857
|
|
17
17
|
fractex/examples/_output.py,sha256=2i8UO6IWYMeUZ2I4f5JW0UJii2nTJbqnKQ8Pqo0MB3U,3367
|
|
18
|
-
fractex/examples/architecture_pattern.py,sha256=
|
|
18
|
+
fractex/examples/architecture_pattern.py,sha256=gREGvvfahNPpoMzHTW1J_oUi8Szquk5v9P7ClaxP8cU,1936
|
|
19
19
|
fractex/examples/atmosphere.py,sha256=9eLAX3aRO6pMz-GrMaZ7QgocG8NBMznO8vuy6OzNkTA,1824
|
|
20
20
|
fractex/examples/composite_material.py,sha256=6Ba7yCcx82MvaEKM2cVYEao6MOQW3pDZrSf1nvlaCow,2048
|
|
21
21
|
fractex/examples/crystal_cave.py,sha256=pXmFTi3FCVSULoTEh11JC_w_0PzEVtbLSExnUw0DQRM,1908
|
|
22
22
|
fractex/examples/custom_pattern.py,sha256=ZqX81-lxUI4e5aQwThNqQPKaJrJ5HldTJgn5fZoV-dc,4310
|
|
23
|
+
fractex/examples/fire_flame.py,sha256=X9T5Mm8PMgTiSQMGsSQG9NfbRwVw8Kmk-e-IMieD6TI,8046
|
|
23
24
|
fractex/examples/game_integration.py,sha256=k85yfez6N46oyR5gXpenbOoxDNMQgRLIDRayL_--6Tw,3194
|
|
24
25
|
fractex/examples/game_texture.py,sha256=4hwtTOpspIQanQODD2g6opke0Zp5XwTriw8kqdtKLak,6841
|
|
25
26
|
fractex/examples/integration.py,sha256=jJwa3ChUmxikGmqJk_tchDnV3rhuYKlNdEATxdbvf_8,3494
|
|
26
27
|
fractex/examples/physic_integration.py,sha256=uh_ik_j-iiEOowPIvd37jwUDi5v9PZtCYUyHgyJBCC4,2551
|
|
27
|
-
fractex/examples/splash.py,sha256=
|
|
28
|
+
fractex/examples/splash.py,sha256=n01HxC0Zhry9NqJf-NmczLKB_OG1WiFaUB-9y-bJa7c,5969
|
|
28
29
|
fractex/examples/terrain.py,sha256=rsduhHchbyI6T-RDIzI-S6D07g2_rCSg6JtA4eXGve0,2611
|
|
29
30
|
fractex/examples/underwater.py,sha256=1_SlTVhd-ONEYqyV_B4sCFhSqScmCHQ7f6eRVI7v5X8,3313
|
|
30
31
|
fractex/examples/underwater_volkano.py,sha256=HobJi3ztK-R_VeHfI8oVRZupONbegWnOL40llj_LnaE,4161
|
|
31
|
-
fractex-0.
|
|
32
|
-
fractex-0.
|
|
33
|
-
fractex-0.
|
|
34
|
-
fractex-0.
|
|
35
|
-
fractex-0.
|
|
36
|
-
fractex-0.
|
|
32
|
+
fractex-0.2.0.dist-info/licenses/LICENSE,sha256=CRrETm3c-JFoqpolJMrRIfFXOxOy5uhnvdSVKFP6sVE,1069
|
|
33
|
+
fractex-0.2.0.dist-info/METADATA,sha256=xNmFDqTLXAzMdz1gzvRbT4oLVx0ssF7t-42JikMoxuY,3603
|
|
34
|
+
fractex-0.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
35
|
+
fractex-0.2.0.dist-info/entry_points.txt,sha256=xgzT1MTUmB69bstzCyxybxJDI7D8nYqsJpBN2UMazL4,45
|
|
36
|
+
fractex-0.2.0.dist-info/top_level.txt,sha256=LFl1_h1YNgANgBDo6d-tPTJzlrO3ny_l5OTItasV9rw,8
|
|
37
|
+
fractex-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|