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 CHANGED
@@ -11,6 +11,7 @@ from typing import List
11
11
  def _examples() -> List[str]:
12
12
  return [
13
13
  "splash",
14
+ "fire_flame",
14
15
  "custom_pattern",
15
16
  "architecture_pattern",
16
17
  "composite_material",
@@ -10,6 +10,7 @@ from typing import List, Optional
10
10
  def list_examples() -> List[str]:
11
11
  return [
12
12
  "splash",
13
+ "fire_flame",
13
14
  "custom_pattern",
14
15
  "architecture_pattern",
15
16
  "composite_material",
@@ -2,6 +2,7 @@
2
2
  import sys
3
3
  from pathlib import Path
4
4
  import argparse
5
+ import numpy as np
5
6
 
6
7
  ROOT = Path(__file__).resolve().parents[2]
7
8
  if str(ROOT) not in sys.path:
@@ -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()
@@ -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(f"Fractex Splash ({selected or 'auto'})")
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(config.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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fractex
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Fractal texture engine for procedural and infinite-detail textures.
5
5
  Author-email: Timur Isanov <tisanov@yahoo.com>
6
6
  License: MIT License
@@ -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=jQ9bI_rwtXwu_u_J_5HpwoiC8ZIOVH6JXvaXWYvOQV4,2468
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=oilaT3MB6bbSt-ah3zraZtDe1Drt-GFHGH8xtm-oqBs,5295
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=iYCVTXWDYHUJ-_Hje6tSS4vJSxdwh9PlTSP0X106T5M,835
16
+ fractex/examples/__init__.py,sha256=w2TZrQjob4nE9eRaWLYEKkaETerHPbdRNCnNgBilCng,857
17
17
  fractex/examples/_output.py,sha256=2i8UO6IWYMeUZ2I4f5JW0UJii2nTJbqnKQ8Pqo0MB3U,3367
18
- fractex/examples/architecture_pattern.py,sha256=zU2cILsjpx1JbDGbIkLR3sqrB4ZLmCkzNW9DFDbSE_4,1917
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=b18a3UGkxfLcKn9ITCczAeB-ofHeCoRxQi3ToxMtxnU,5901
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.1.1.dist-info/licenses/LICENSE,sha256=CRrETm3c-JFoqpolJMrRIfFXOxOy5uhnvdSVKFP6sVE,1069
32
- fractex-0.1.1.dist-info/METADATA,sha256=V2UWQEXzcqsxjAsY9BXFl5bArmrkWPYJQjMHEkzKb5M,3603
33
- fractex-0.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
34
- fractex-0.1.1.dist-info/entry_points.txt,sha256=xgzT1MTUmB69bstzCyxybxJDI7D8nYqsJpBN2UMazL4,45
35
- fractex-0.1.1.dist-info/top_level.txt,sha256=LFl1_h1YNgANgBDo6d-tPTJzlrO3ny_l5OTItasV9rw,8
36
- fractex-0.1.1.dist-info/RECORD,,
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,,