fractex 0.1.2__tar.gz → 0.2.1__tar.gz

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.
Files changed (43) hide show
  1. {fractex-0.1.2/fractex.egg-info → fractex-0.2.1}/PKG-INFO +2 -1
  2. {fractex-0.1.2 → fractex-0.2.1}/fractex/3d.py +28 -7
  3. {fractex-0.1.2 → fractex-0.2.1}/fractex/cli.py +1 -0
  4. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/3d.py +2 -2
  5. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/__init__.py +1 -0
  6. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/architecture_pattern.py +1 -0
  7. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/composite_material.py +1 -0
  8. fractex-0.2.1/fractex/examples/fire_flame.py +141 -0
  9. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/splash.py +56 -39
  10. {fractex-0.1.2 → fractex-0.2.1/fractex.egg-info}/PKG-INFO +2 -1
  11. {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/SOURCES.txt +1 -0
  12. {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/requires.txt +1 -0
  13. {fractex-0.1.2 → fractex-0.2.1}/pyproject.toml +2 -2
  14. {fractex-0.1.2 → fractex-0.2.1}/LICENSE +0 -0
  15. {fractex-0.1.2 → fractex-0.2.1}/MANIFEST.in +0 -0
  16. {fractex-0.1.2 → fractex-0.2.1}/README.md +0 -0
  17. {fractex-0.1.2 → fractex-0.2.1}/fractex/__init__.py +0 -0
  18. {fractex-0.1.2 → fractex-0.2.1}/fractex/advanced.py +0 -0
  19. {fractex-0.1.2 → fractex-0.2.1}/fractex/core.py +0 -0
  20. {fractex-0.1.2 → fractex-0.2.1}/fractex/dynamic_textures_3d.py +0 -0
  21. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/3d_integration.py +0 -0
  22. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/3d_integration_2d.py +0 -0
  23. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/_output.py +0 -0
  24. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/atmosphere.py +0 -0
  25. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/crystal_cave.py +0 -0
  26. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/custom_pattern.py +0 -0
  27. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/game_integration.py +0 -0
  28. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/game_texture.py +0 -0
  29. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/integration.py +0 -0
  30. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/physic_integration.py +0 -0
  31. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/terrain.py +0 -0
  32. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/underwater.py +0 -0
  33. {fractex-0.1.2 → fractex-0.2.1}/fractex/examples/underwater_volkano.py +0 -0
  34. {fractex-0.1.2 → fractex-0.2.1}/fractex/geometric_patterns_3d.py +0 -0
  35. {fractex-0.1.2 → fractex-0.2.1}/fractex/interactive.py +0 -0
  36. {fractex-0.1.2 → fractex-0.2.1}/fractex/simplex_noise.py +0 -0
  37. {fractex-0.1.2 → fractex-0.2.1}/fractex/texture_blending.py +0 -0
  38. {fractex-0.1.2 → fractex-0.2.1}/fractex/volume_scattering.py +0 -0
  39. {fractex-0.1.2 → fractex-0.2.1}/fractex/volume_textures.py +0 -0
  40. {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/dependency_links.txt +0 -0
  41. {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/entry_points.txt +0 -0
  42. {fractex-0.1.2 → fractex-0.2.1}/fractex.egg-info/top_level.txt +0 -0
  43. {fractex-0.1.2 → fractex-0.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fractex
3
- Version: 0.1.2
3
+ Version: 0.2.1
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
@@ -43,6 +43,7 @@ Description-Content-Type: text/markdown
43
43
  License-File: LICENSE
44
44
  Requires-Dist: numpy
45
45
  Requires-Dist: numba
46
+ Requires-Dist: matplotlib
46
47
  Dynamic: license-file
47
48
 
48
49
  ## Fractex
@@ -155,6 +155,9 @@ def sample_volume_trilinear(volume: np.ndarray,
155
155
  Интерполированное значение (C,)
156
156
  """
157
157
  depth, height, width, channels = volume.shape
158
+ x = np.float32(x)
159
+ y = np.float32(y)
160
+ z = np.float32(z)
158
161
 
159
162
  # Обрабатываем режим заворачивания
160
163
  if wrap_mode == "repeat":
@@ -162,17 +165,35 @@ def sample_volume_trilinear(volume: np.ndarray,
162
165
  y = y - np.floor(y)
163
166
  z = z - np.floor(z)
164
167
  elif wrap_mode == "clamp":
165
- x = np.clip(x, 0.0, 1.0)
166
- y = np.clip(y, 0.0, 1.0)
167
- z = np.clip(z, 0.0, 1.0)
168
+ if x < 0.0:
169
+ x = 0.0
170
+ elif x > 1.0:
171
+ x = 1.0
172
+ if y < 0.0:
173
+ y = 0.0
174
+ elif y > 1.0:
175
+ y = 1.0
176
+ if z < 0.0:
177
+ z = 0.0
178
+ elif z > 1.0:
179
+ z = 1.0
168
180
  elif wrap_mode == "mirror":
169
181
  x = np.abs(1.0 - np.abs(1.0 - x * 2.0)) / 2.0
170
182
  y = np.abs(1.0 - np.abs(1.0 - y * 2.0)) / 2.0
171
183
  z = np.abs(1.0 - np.abs(1.0 - z * 2.0)) / 2.0
172
184
  else:
173
- x = np.clip(x, 0.0, 1.0)
174
- y = np.clip(y, 0.0, 1.0)
175
- z = np.clip(z, 0.0, 1.0)
185
+ if x < 0.0:
186
+ x = 0.0
187
+ elif x > 1.0:
188
+ x = 1.0
189
+ if y < 0.0:
190
+ y = 0.0
191
+ elif y > 1.0:
192
+ y = 1.0
193
+ if z < 0.0:
194
+ z = 0.0
195
+ elif z > 1.0:
196
+ z = 1.0
176
197
 
177
198
  # Переводим в координаты текстуры
178
199
  fx = x * (width - 1)
@@ -221,7 +242,7 @@ def sample_volume_trilinear(volume: np.ndarray,
221
242
  # Линейная интерполяция по Z
222
243
  result = c0 * (1 - dz) + c1 * dz
223
244
 
224
- return result
245
+ return result.astype(volume.dtype)
225
246
 
226
247
  @jit(nopython=True, parallel=True, cache=True)
227
248
  def sample_volume_trilinear_batch(volume: np.ndarray,
@@ -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",
@@ -34,7 +34,7 @@ def main():
34
34
  args = parser.parse_args()
35
35
  game_time = 0.0
36
36
  player = Player(
37
- position=np.array([0.5, 0.5, 2.0]),
37
+ position=np.array([0.5, 0.5, 0.9]),
38
38
  look_at=np.array([0.5, 0.5, 0.5])
39
39
  )
40
40
 
@@ -73,7 +73,7 @@ def main():
73
73
  # Рендеринг
74
74
  renderer = VolumeTextureRenderer(cave_with_lava)
75
75
  frames = []
76
- for z in [2.2, 2.0, 1.8, 1.6, 1.4, 1.2]:
76
+ for z in [0.95, 0.85, 0.75, 0.65, 0.55, 0.45]:
77
77
  camera_pos = np.array([0.5, 0.5, z])
78
78
  frame = renderer.render_raycast(
79
79
  camera_pos=camera_pos,
@@ -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:
@@ -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()
@@ -80,6 +80,7 @@ def main():
80
80
  ema_ms = target_ms
81
81
  phases = {name: 0.0 for name in presets}
82
82
  last_t_base = 0.0
83
+ phase_wrap = 90.0
83
84
 
84
85
  def _upscale_nearest(image: np.ndarray, out_w: int, out_h: int) -> np.ndarray:
85
86
  in_h, in_w, _ = image.shape
@@ -103,20 +104,47 @@ def main():
103
104
  base_name = presets[(frame // 240) % len(presets)]
104
105
  detail_name = presets[(frame // 240 + 1) % len(presets)]
105
106
 
106
- phases[base_name] += delta_t
107
- base_texture = textures[base_name]
108
- detail_texture = textures[detail_name]
109
- t = phases[base_name]
110
- t2 = phases[detail_name]
111
-
112
- base_zoom = 1.0 + t * 0.02
113
- detail_zoom = 2.5 + t * 0.05
114
-
115
- decay = np.exp(-0.01 * t)
116
- x0 = np.sin(t * 0.12) * 5.0 * decay
117
- y0 = np.cos(t * 0.10) * 5.0 * decay
118
- x1 = np.sin(t2 * 0.22 + 1.2) * 2.0 * decay
119
- y1 = np.cos(t2 * 0.18 + 0.7) * 2.0 * decay
107
+ phases[base_name] = (phases[base_name] + delta_t) % phase_wrap
108
+ phases[detail_name] = (phases[detail_name] + delta_t) % phase_wrap
109
+ def _render_layer(base_preset: str, detail_preset: str) -> np.ndarray:
110
+ base_texture = textures[base_preset]
111
+ detail_texture = textures[detail_preset]
112
+ t = phases[base_preset]
113
+ t2 = phases[detail_preset]
114
+
115
+ base_zoom = 1.0 + 0.15 * np.sin(t * 0.25)
116
+ detail_zoom = 2.4 + 0.25 * np.sin(t2 * 0.35)
117
+
118
+ x0 = np.sin(t * 0.12) * 5.0
119
+ y0 = np.cos(t * 0.10) * 5.0
120
+ x1 = np.sin(t2 * 0.22 + 1.2) * 2.0
121
+ y1 = np.cos(t2 * 0.18 + 0.7) * 2.0
122
+
123
+ base_origin_x = -render_w / (2.0 * base_zoom)
124
+ base_origin_y = -render_h / (2.0 * base_zoom)
125
+ detail_origin_x = -render_w / (2.0 * detail_zoom)
126
+ detail_origin_y = -render_h / (2.0 * detail_zoom)
127
+
128
+ base = base_texture.generate_tile(
129
+ base_origin_x + x0,
130
+ base_origin_y + y0,
131
+ render_w,
132
+ render_h,
133
+ zoom=base_zoom,
134
+ )[..., :3]
135
+ detail = detail_texture.generate_tile(
136
+ detail_origin_x + x1,
137
+ detail_origin_y + y1,
138
+ render_w,
139
+ render_h,
140
+ zoom=detail_zoom,
141
+ )[..., :3]
142
+
143
+ depth = 0.35 + 0.15 * np.sin(t * 0.2)
144
+ rgb = np.clip(base * (1.0 - depth) + detail * depth, 0, 1)
145
+ tint = 0.6 + 0.4 * np.sin(t * 0.2)
146
+ rgb = np.clip(rgb * np.array([1.0, tint, 0.9]), 0, 1)
147
+ return rgb
120
148
 
121
149
  now = time.perf_counter()
122
150
  dt_ms = (now - last_time) * 1000.0
@@ -134,31 +162,20 @@ def main():
134
162
  render_w = max(64, int(width * render_scale))
135
163
  render_h = max(64, int(height * render_scale))
136
164
 
137
- base_origin_x = -render_w / (2.0 * base_zoom)
138
- base_origin_y = -render_h / (2.0 * base_zoom)
139
- detail_origin_x = -render_w / (2.0 * detail_zoom)
140
- detail_origin_y = -render_h / (2.0 * detail_zoom)
141
-
142
- base = base_texture.generate_tile(
143
- base_origin_x + x0,
144
- base_origin_y + y0,
145
- render_w,
146
- render_h,
147
- zoom=base_zoom,
148
- )[..., :3]
149
- detail = detail_texture.generate_tile(
150
- detail_origin_x + x1,
151
- detail_origin_y + y1,
152
- render_w,
153
- render_h,
154
- zoom=detail_zoom,
155
- )[..., :3]
156
-
157
- depth = 0.35 + 0.15 * np.sin(t * 0.2)
158
- rgb_frame = np.clip(base * (1.0 - depth) + detail * depth, 0, 1)
159
-
160
- tint = 0.6 + 0.4 * np.sin(t * 0.2)
161
- rgb_frame = np.clip(rgb_frame * np.array([1.0, tint, 0.9]), 0, 1)
165
+ rgb_frame = _render_layer(base_name, detail_name)
166
+ if not selected:
167
+ cycle_len = 240
168
+ transition_len = 60
169
+ cycle_pos = frame % cycle_len
170
+ if cycle_pos >= cycle_len - transition_len:
171
+ next_base = presets[(frame // cycle_len + 1) % len(presets)]
172
+ next_detail = presets[(frame // cycle_len + 2) % len(presets)]
173
+ phases[next_base] = (phases[next_base] + delta_t) % phase_wrap
174
+ phases[next_detail] = (phases[next_detail] + delta_t) % phase_wrap
175
+ next_rgb = _render_layer(next_base, next_detail)
176
+ w = (cycle_pos - (cycle_len - transition_len)) / transition_len
177
+ w = w * w * (3.0 - 2.0 * w)
178
+ rgb_frame = np.clip(rgb_frame * (1.0 - w) + next_rgb * w, 0, 1)
162
179
  rgb_frame = _upscale_nearest(rgb_frame, width, height)
163
180
  im.set_array(rgb_frame)
164
181
  return (im,)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fractex
3
- Version: 0.1.2
3
+ Version: 0.2.1
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
@@ -43,6 +43,7 @@ Description-Content-Type: text/markdown
43
43
  License-File: LICENSE
44
44
  Requires-Dist: numpy
45
45
  Requires-Dist: numba
46
+ Requires-Dist: matplotlib
46
47
  Dynamic: license-file
47
48
 
48
49
  ## Fractex
@@ -30,6 +30,7 @@ fractex/examples/atmosphere.py
30
30
  fractex/examples/composite_material.py
31
31
  fractex/examples/crystal_cave.py
32
32
  fractex/examples/custom_pattern.py
33
+ fractex/examples/fire_flame.py
33
34
  fractex/examples/game_integration.py
34
35
  fractex/examples/game_texture.py
35
36
  fractex/examples/integration.py
@@ -1,2 +1,3 @@
1
1
  numpy
2
2
  numba
3
+ matplotlib
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fractex"
7
- version = "0.1.2"
7
+ version = "0.2.1"
8
8
  description = "Fractal texture engine for procedural and infinite-detail textures."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
11
  license = { file = "LICENSE" }
12
12
  authors = [{ name = "Timur Isanov", email = "tisanov@yahoo.com" }]
13
- dependencies = ["numpy", "numba"]
13
+ dependencies = ["numpy", "numba", "matplotlib"]
14
14
  keywords = ["fractal", "texture", "procedural", "noise"]
15
15
  classifiers = [
16
16
  "Development Status :: 3 - Alpha",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes