fluxrender 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.
- FluxRender/__init__.py +13 -0
- FluxRender/colors.py +307 -0
- FluxRender/constants.py +105 -0
- FluxRender/core.py +616 -0
- FluxRender/entities.py +1764 -0
- FluxRender/graphics.py +298 -0
- FluxRender/math_engine.py +697 -0
- FluxRender/probes.py +122 -0
- FluxRender/regions.py +495 -0
- FluxRender/ui.py +1144 -0
- FluxRender/validators.py +318 -0
- fluxrender-0.1.0.dist-info/METADATA +104 -0
- fluxrender-0.1.0.dist-info/RECORD +15 -0
- fluxrender-0.1.0.dist-info/WHEEL +5 -0
- fluxrender-0.1.0.dist-info/top_level.txt +1 -0
FluxRender/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .core import Scene, CoordinateSystem
|
|
2
|
+
from .ui import Button, UIStyle, Grid, Axis
|
|
3
|
+
from .entities import VectorField, ParticleSystem
|
|
4
|
+
from .regions import CircularRegion, CursorRegion
|
|
5
|
+
from .math_engine import VectorMathEngine
|
|
6
|
+
from .probes import DataProbe
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from .constants import Align, Property, ArrowStyle, FieldMode, ScaleType
|
|
10
|
+
from .colors import ColorMapper
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
FluxRender/colors.py
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from typing import Sequence, Tuple
|
|
3
|
+
|
|
4
|
+
from .constants import ScaleType
|
|
5
|
+
from .validators import _fatal_error
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def hsl_to_rgba(h: float, s: float, l: float, a: float = 1.0):
|
|
10
|
+
"""
|
|
11
|
+
Converts HSL color space to RGBA color space.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
h (float): Hue component [0, 1].
|
|
15
|
+
s (float): Saturation component [0, 1].
|
|
16
|
+
l (float): Lightness component [0, 1].
|
|
17
|
+
a (float): Alpha component [0, 1].
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
tuple: A tuple representing the RGBA color (r, g, b, a).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
c = (1 - abs(2 * l - 1)) * s
|
|
24
|
+
x = c * (1 - abs((h * 6) % 2 - 1))
|
|
25
|
+
m = l - c / 2
|
|
26
|
+
|
|
27
|
+
r1, g1, b1 = 0, 0, 0
|
|
28
|
+
if 0 <= h < 1/6:
|
|
29
|
+
r1, g1, b1 = c, x, 0
|
|
30
|
+
elif 1/6 <= h < 1/3:
|
|
31
|
+
r1, g1, b1 = x, c, 0
|
|
32
|
+
elif 1/3 <= h < 1/2:
|
|
33
|
+
r1, g1, b1 = 0, c, x
|
|
34
|
+
elif 1/2 <= h < 2/3:
|
|
35
|
+
r1, g1, b1 = 0, x, c
|
|
36
|
+
elif 2/3 <= h < 5/6:
|
|
37
|
+
r1, g1, b1 = x, 0, c
|
|
38
|
+
elif 5/6 <= h < 1:
|
|
39
|
+
r1, g1, b1 = c, 0, x
|
|
40
|
+
|
|
41
|
+
r = r1 + m
|
|
42
|
+
g = g1 + m
|
|
43
|
+
b = b1 + m
|
|
44
|
+
|
|
45
|
+
return (r, g, b, a)
|
|
46
|
+
|
|
47
|
+
def hsl_to_rgb_vectorized(h: np.ndarray, s: np.ndarray, l: np.ndarray, a: np.ndarray = 1.0):
|
|
48
|
+
"""
|
|
49
|
+
Converts HSL color space to RGBA color space for NumPy arrays.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
h (np.ndarray): Hue component array [0, 1].
|
|
53
|
+
s (np.ndarray): Saturation component array [0, 1].
|
|
54
|
+
l (np.ndarray): Lightness component array [0, 1].
|
|
55
|
+
a (np.ndarray): Alpha component array [0, 1].
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
c = (1 - np.abs(2 * l - 1)) * s
|
|
59
|
+
h_prime = h / 60.0
|
|
60
|
+
x = c * (1 - np.abs(h_prime % 2 - 1))
|
|
61
|
+
m = l - c / 2
|
|
62
|
+
|
|
63
|
+
r = np.zeros_like(h)
|
|
64
|
+
g = np.zeros_like(h)
|
|
65
|
+
b = np.zeros_like(h)
|
|
66
|
+
|
|
67
|
+
# Warunki wektorowe (zamiast if/elif)
|
|
68
|
+
# 0 <= H' < 1
|
|
69
|
+
mask = (h_prime >= 0) & (h_prime < 1)
|
|
70
|
+
r[mask], g[mask], b[mask] = c[mask], x[mask], 0
|
|
71
|
+
|
|
72
|
+
# 1 <= H' < 2
|
|
73
|
+
mask = (h_prime >= 1) & (h_prime < 2)
|
|
74
|
+
r[mask], g[mask], b[mask] = x[mask], c[mask], 0
|
|
75
|
+
|
|
76
|
+
# 2 <= H' < 3
|
|
77
|
+
mask = (h_prime >= 2) & (h_prime < 3)
|
|
78
|
+
r[mask], g[mask], b[mask] = 0, c[mask], x[mask]
|
|
79
|
+
|
|
80
|
+
# 3 <= H' < 4
|
|
81
|
+
mask = (h_prime >= 3) & (h_prime < 4)
|
|
82
|
+
r[mask], g[mask], b[mask] = 0, x[mask], c[mask]
|
|
83
|
+
|
|
84
|
+
# 4 <= H' < 5
|
|
85
|
+
mask = (h_prime >= 4) & (h_prime < 5)
|
|
86
|
+
r[mask], g[mask], b[mask] = x[mask], 0, c[mask]
|
|
87
|
+
|
|
88
|
+
# 5 <= H' < 6
|
|
89
|
+
mask = (h_prime >= 5) & (h_prime < 6)
|
|
90
|
+
r[mask], g[mask], b[mask] = c[mask], 0, x[mask]
|
|
91
|
+
|
|
92
|
+
return r + m, g + m, b + m
|
|
93
|
+
|
|
94
|
+
def parse_color(color_input: Sequence[float]) -> Tuple[float, float, float, float]:
|
|
95
|
+
"""
|
|
96
|
+
Validates and standardizes color inputs to a guaranteed RGBA tuple.
|
|
97
|
+
Ensures all elements are numbers within the [0.0, 1.0] range.
|
|
98
|
+
Assumes an alpha of 1.0 if only RGB is provided.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
length = len(color_input)
|
|
103
|
+
except TypeError:
|
|
104
|
+
_fatal_error(f"Color input must be a sequence (e.g., tuple, list). Got {type(color_input).__name__}.", "TypeError")
|
|
105
|
+
|
|
106
|
+
if length not in (3, 4):
|
|
107
|
+
_fatal_error(f"Color must be defined as RGB (3 values) or RGBA (4 values). Got {length} elements.", "ValueError")
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
floats = [float(c) for c in color_input]
|
|
111
|
+
except (ValueError, TypeError):
|
|
112
|
+
_fatal_error(f"All color components must be numbers. Got: {color_input}", "TypeError")
|
|
113
|
+
|
|
114
|
+
if any(c < 0 or c > 1 for c in color_input):
|
|
115
|
+
_fatal_error(f"Color components must be in the range [0, 1]. Got {color_input}.", "ValueError")
|
|
116
|
+
|
|
117
|
+
if any(c < 0.0 or c > 1.0 for c in floats):
|
|
118
|
+
_fatal_error(f"Color components must be in the range [0.0, 1.0]. Got {color_input}.", "ValueError")
|
|
119
|
+
|
|
120
|
+
if length == 3:
|
|
121
|
+
return (floats[0], floats[1], floats[2], 1.0) # Assume alpha = 1.0 if not provided
|
|
122
|
+
|
|
123
|
+
return (floats[0], floats[1], floats[2], floats[3])
|
|
124
|
+
|
|
125
|
+
class ColorSequence:
|
|
126
|
+
def __set_name__(self, owner, name):
|
|
127
|
+
self.private_name = '_' + name
|
|
128
|
+
|
|
129
|
+
def __get__(self, obj, objtype=None):
|
|
130
|
+
return getattr(obj, self.private_name)
|
|
131
|
+
|
|
132
|
+
def __set__(self, obj, value):
|
|
133
|
+
safe_color = parse_color(value)
|
|
134
|
+
setattr(obj, self.private_name, safe_color)
|
|
135
|
+
|
|
136
|
+
if hasattr(obj, '_flag_for_update'):
|
|
137
|
+
obj._flag_for_update(self.private_name[1:])
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ColorMapper:
|
|
142
|
+
"""A dynamic color interpolation engine that translates scalar fields into vibrant visual gradients.
|
|
143
|
+
|
|
144
|
+
The ColorMapper acts as the visual translator for the mathematical engine. It takes raw
|
|
145
|
+
scalar values (such as velocity magnitude, divergence, or custom topological metrics)
|
|
146
|
+
and smoothly maps them to physical RGBA colors.
|
|
147
|
+
|
|
148
|
+
Crucially, all color interpolation is performed natively within the HSL (Hue, Saturation,
|
|
149
|
+
Lightness) color space rather than RGB. This architectural choice guarantees perceptually
|
|
150
|
+
uniform, vivid transitions and completely eliminates the "muddy" or desaturated mid-tones
|
|
151
|
+
commonly seen in standard linear color blending. It features highly flexible normalization,
|
|
152
|
+
allowing bounds to be strictly locked at initialization or dynamically injected frame-by-frame
|
|
153
|
+
by the rendering entities (like VectorField or ParticleSystem).
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
def __init__(self,
|
|
157
|
+
min_hue: int = 240, max_hue: int = 0,
|
|
158
|
+
min_saturation: float = 0.4, max_saturation: float = 1.0,
|
|
159
|
+
min_lightness: float = 0.3, max_lightness: float = 0.65,
|
|
160
|
+
min_alpha: float = 0.8, max_alpha: float = 1.0,
|
|
161
|
+
scale_type: ScaleType = ScaleType.LINEAR,
|
|
162
|
+
scale_function = None,
|
|
163
|
+
min_value: float = None, max_value: float = None
|
|
164
|
+
):
|
|
165
|
+
"""
|
|
166
|
+
Args:
|
|
167
|
+
min_hue (int): Minimum hue in degrees (0-360). Default is 240 (blue).
|
|
168
|
+
max_hue (int): Maximum hue in degrees (0-360). Default is 0 (red).
|
|
169
|
+
min_saturation (float): Minimum saturation (0-1). Default is 0.4.
|
|
170
|
+
max_saturation (float): Maximum saturation (0-1). Default is 1.0.
|
|
171
|
+
min_lightness (float): Minimum lightness (0-1). Default is 0.3.
|
|
172
|
+
max_lightness (float): Maximum lightness (0-1). Default is 0.65.
|
|
173
|
+
min_alpha (float): Minimum alpha (0-1). Default is 0.8.
|
|
174
|
+
max_alpha (float): Maximum alpha (0-1). Default is 1.0.
|
|
175
|
+
scale_type (ScaleType): The type of scaling to apply to the input values. Default is ScaleType.LINEAR.
|
|
176
|
+
scale_function (Callable, optional): A custom single-argument function f(t). Input t is normalized in [0.0, 1.0], and the output must also be within [0.0, 1.0]. Used only if `scale_type` is `ScaleType.CUSTOM`.
|
|
177
|
+
min_value (float, optional): Hardcoded minimum value for normalization. If None, the rendering entity will dynamically calculate and inject the minimum value of the current frame.
|
|
178
|
+
max_value (float, optional): Hardcoded maximum value for normalization. If None, the rendering entity will dynamically calculate and inject the maximum value.
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
Creating a highly saturated thermal gradient (Blue to Red) with a custom logarithmic-like curve:
|
|
182
|
+
```python
|
|
183
|
+
import FluxRender as fr
|
|
184
|
+
|
|
185
|
+
# [Initialize scene and coordinate system here]
|
|
186
|
+
|
|
187
|
+
# This mapper transitions from green to orange, but uses a square-root curve
|
|
188
|
+
mapper = fr.ColorMapper(
|
|
189
|
+
min_hue=150,
|
|
190
|
+
max_hue=20,
|
|
191
|
+
min_saturation=0.8,
|
|
192
|
+
max_saturation=1,
|
|
193
|
+
min_lightness=0.1,
|
|
194
|
+
max_lightness=0.6,
|
|
195
|
+
min_alpha=1,
|
|
196
|
+
scale_type = fr.ScaleType.CUSTOM,
|
|
197
|
+
scale_function = lambda x: x ** 0.5,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Apply the mapper directly to a ParticleSystem
|
|
201
|
+
particles = fr.ParticleSystem(
|
|
202
|
+
vec_function = lambda x, y: (np.sin(x), np.cos(x) * np.sin(y)),
|
|
203
|
+
color_mapper = mapper,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
scene.add(particles)
|
|
207
|
+
```
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
self.min_hue = min_hue
|
|
211
|
+
self.max_hue = max_hue
|
|
212
|
+
self.min_saturation = min_saturation
|
|
213
|
+
self.max_saturation = max_saturation
|
|
214
|
+
self.min_lightness = min_lightness
|
|
215
|
+
self.max_lightness = max_lightness
|
|
216
|
+
self.min_alpha = min_alpha
|
|
217
|
+
self.max_alpha = max_alpha
|
|
218
|
+
|
|
219
|
+
self.min_value = min_value
|
|
220
|
+
self.max_value = max_value
|
|
221
|
+
|
|
222
|
+
self.scale_type = scale_type
|
|
223
|
+
self.scale_function = scale_function
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def calc_color(self, value: float, min_value: float = None, max_value: float = None):
|
|
227
|
+
"""
|
|
228
|
+
Maps a scalar value to an RGBA color using HSL color space.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
value (float): The scalar value to map.
|
|
232
|
+
min_value (float): The minimum value of the range (if self.min_value is not None, it will be used).
|
|
233
|
+
max_value (float): The maximum value of the range (if self.max_value is not None, it will be used).
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
color (tuple): A tuple representing the RGBA color (r, g, b, a).
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
# Use instance min_value and max_value if not provided
|
|
240
|
+
if self.min_value is not None and self.max_value is not None:
|
|
241
|
+
min_value = self.min_value
|
|
242
|
+
max_value = self.max_value
|
|
243
|
+
|
|
244
|
+
# Normalize value to [0, 1]
|
|
245
|
+
t = (value - min_value) / (max_value - min_value)
|
|
246
|
+
t = np.clip(t, 0.0, 1.0)
|
|
247
|
+
|
|
248
|
+
# Interpolate HSL components
|
|
249
|
+
if self.scale_type == ScaleType.LOGARITHMIC:
|
|
250
|
+
t = np.log(1 + 9 * t) / np.log(10)
|
|
251
|
+
elif self.scale_type == ScaleType.EXPONENTIAL:
|
|
252
|
+
t = (10 ** t - 1) / 9
|
|
253
|
+
elif self.scale_type == ScaleType.CUSTOM and self.scale_function is not None:
|
|
254
|
+
t = self.scale_function(t)
|
|
255
|
+
|
|
256
|
+
hue = self.min_hue + t * (self.max_hue - self.min_hue)
|
|
257
|
+
saturation = self.min_saturation + t * (self.max_saturation - self.min_saturation)
|
|
258
|
+
lightness = self.min_lightness + t * (self.max_lightness - self.min_lightness)
|
|
259
|
+
alpha = self.min_alpha + t * (self.max_alpha - self.min_alpha)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
return hsl_to_rgba(hue / 360.0, saturation, lightness, alpha)
|
|
263
|
+
|
|
264
|
+
def map_array(self, values: np.ndarray, min_value: float = None, max_value: float = None):
|
|
265
|
+
"""
|
|
266
|
+
Maps a NumPy array of scalar values to an array of RGBA colors.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
values (np.ndarray): A NumPy array of scalar values.
|
|
270
|
+
min_value (float): The minimum value of the range.
|
|
271
|
+
max_value (float): The maximum value of the range.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
colors (np.ndarray): A NumPy array of RGBA colors corresponding to the input values.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
diff = max_value - min_value
|
|
278
|
+
if diff == 0: diff = 1.0 # Avoid division by zero when all values are the same
|
|
279
|
+
|
|
280
|
+
with np.errstate(divide='ignore', over='ignore', invalid='ignore'):
|
|
281
|
+
# Normalize value to [0, 1]
|
|
282
|
+
t = (values - min_value) / diff
|
|
283
|
+
t = np.clip(t, 0.0, 1.0)
|
|
284
|
+
|
|
285
|
+
if self.scale_type == ScaleType.LOGARITHMIC:
|
|
286
|
+
t = np.log10(1 + 9 * t)
|
|
287
|
+
elif self.scale_type == ScaleType.EXPONENTIAL:
|
|
288
|
+
t = (np.power(10.0, t) - 1) / 9.0
|
|
289
|
+
elif self.scale_type == ScaleType.CUSTOM and self.scale_function is not None:
|
|
290
|
+
try:
|
|
291
|
+
# The function can take list/array inputs and return list/array outputs (vectorized)
|
|
292
|
+
t = self.scale_function(t)
|
|
293
|
+
except Exception:
|
|
294
|
+
# The function is a regular Python function (for each point individually)
|
|
295
|
+
f = np.vectorize(self.scale_function)
|
|
296
|
+
t = f(t)
|
|
297
|
+
|
|
298
|
+
hue = self.min_hue + t * (self.max_hue - self.min_hue)
|
|
299
|
+
saturation = self.min_saturation + t * (self.max_saturation - self.min_saturation)
|
|
300
|
+
lightness = self.min_lightness + t * (self.max_lightness - self.min_lightness)
|
|
301
|
+
alpha = self.min_alpha + t * (self.max_alpha - self.min_alpha)
|
|
302
|
+
|
|
303
|
+
r, g, b = hsl_to_rgb_vectorized(hue, saturation, lightness)
|
|
304
|
+
|
|
305
|
+
return np.column_stack((r, g, b, alpha)).astype(np.float32)
|
|
306
|
+
|
|
307
|
+
|
FluxRender/constants.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from enum import Enum, IntEnum, auto
|
|
2
|
+
|
|
3
|
+
class Align(Enum):
|
|
4
|
+
"""Specifies the anchoring or alignment behavior for UI elements and spatial bounding boxes.
|
|
5
|
+
|
|
6
|
+
Attributes:
|
|
7
|
+
LEFT_TOP: Anchored to the top-left corner.
|
|
8
|
+
RIGHT_TOP: Anchored to the top-right corner.
|
|
9
|
+
LEFT_BOTTOM: Anchored to the bottom-left corner.
|
|
10
|
+
RIGHT_BOTTOM: Anchored to the bottom-right corner.
|
|
11
|
+
CENTER: Centered both horizontally and vertically.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
LEFT_TOP = auto()
|
|
15
|
+
RIGHT_TOP = auto()
|
|
16
|
+
LEFT_BOTTOM = auto()
|
|
17
|
+
RIGHT_BOTTOM = auto()
|
|
18
|
+
CENTER = auto()
|
|
19
|
+
|
|
20
|
+
class Property(Enum):
|
|
21
|
+
"""Defines the mathematical or physical property to be evaluated and visualized.
|
|
22
|
+
|
|
23
|
+
Used primarily for coloring mappings, filtering, or data probe measurements
|
|
24
|
+
within vector fields and particle systems.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
VELOCITY: The magnitude (length) of the vector.
|
|
28
|
+
ANGLE: The angle between the field vector and the base vector (usually equal to [1, 0] by default).
|
|
29
|
+
DIVERGENCE: The rate at which the field acts as a source or sink.
|
|
30
|
+
CURL: The macroscopic rotation or vorticity of the field.
|
|
31
|
+
COMPONENT_X: The horizontal component of the vector.
|
|
32
|
+
COMPONENT_Y: The vertical component of the vector.
|
|
33
|
+
JACOBIAN: The determinant of the Jacobian matrix.
|
|
34
|
+
OKUBO_WEISS: The Okubo-Weiss parameter used for vortex identification.
|
|
35
|
+
CONVECTIVE_ACCELERATION: The convective term of the material derivative.
|
|
36
|
+
CUSTOM: A user-defined scalar function evaluated by the math engine.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
VELOCITY = auto()
|
|
40
|
+
ANGLE = auto()
|
|
41
|
+
DIVERGENCE = auto()
|
|
42
|
+
CURL = auto()
|
|
43
|
+
COMPONENT_X = auto()
|
|
44
|
+
COMPONENT_Y = auto()
|
|
45
|
+
JACOBIAN = auto()
|
|
46
|
+
OKUBO_WEISS = auto()
|
|
47
|
+
CONVECTIVE_ACCELERATION = auto()
|
|
48
|
+
CUSTOM = auto()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ArrowStyle(IntEnum):
|
|
53
|
+
"""Determines the visual shape and rendering style of arrowheads in vector fields.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
OPEN: Simple, unclosed line segments forming a 'V' shape.
|
|
57
|
+
TRIANGLE_TIGHT: A filled triangle with a narrow base.
|
|
58
|
+
TRIANGLE_WIDE: A filled triangle with a broad base.
|
|
59
|
+
HARPOON: A half-arrowhead with only one side drawn.
|
|
60
|
+
CURVED: Arrowhead wings with a slight inner curve.
|
|
61
|
+
BLOCK: A solid, rectangular block style.
|
|
62
|
+
"""
|
|
63
|
+
OPEN = 0
|
|
64
|
+
TRIANGLE_TIGHT = 1
|
|
65
|
+
TRIANGLE_WIDE = 2
|
|
66
|
+
HARPOON = 3
|
|
67
|
+
CURVED = 4
|
|
68
|
+
BLOCK = 5
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ScaleType(Enum):
|
|
72
|
+
"""Dictates the mathematical scaling function applied during data mapping.
|
|
73
|
+
|
|
74
|
+
Controls how scalar values are normalized before being mapped to a color gradient
|
|
75
|
+
or determining physical attributes like particles speed.
|
|
76
|
+
|
|
77
|
+
Attributes:
|
|
78
|
+
LINEAR: Direct proportional scaling.
|
|
79
|
+
LOGARITHMIC: Base-10 logarithmic scaling, useful for wide-ranging data.
|
|
80
|
+
EXPONENTIAL: Exponential scaling, highlighting peak values.
|
|
81
|
+
CUSTOM: A user-provided mapping function.
|
|
82
|
+
"""
|
|
83
|
+
LINEAR = auto()
|
|
84
|
+
LOGARITHMIC = auto()
|
|
85
|
+
EXPONENTIAL = auto()
|
|
86
|
+
CUSTOM = auto()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class FieldMode(IntEnum):
|
|
90
|
+
"""Controls how a vector field scales and positions itself relative to the viewport.
|
|
91
|
+
|
|
92
|
+
Attributes:
|
|
93
|
+
SCREEN_FIXED: Anchored strictly to UI pixel coordinates. Ignores camera movement.
|
|
94
|
+
WORLD_FIXED: Anchored to the mathematical world space. Arrow density is static.
|
|
95
|
+
ZOOM_ADAPTIVE: Arrows change length dynamically based on the camera's zoom level.
|
|
96
|
+
WORLD_DENSITY_ADAPTIVE: Arrow density changes based on the mathematical region size.
|
|
97
|
+
ZOOM_DENSITY_ADAPTIVE: Arrow density automatically recalculates based on camera zoom.
|
|
98
|
+
"""
|
|
99
|
+
SCREEN_FIXED = 0
|
|
100
|
+
WORLD_FIXED = 1
|
|
101
|
+
ZOOM_ADAPTIVE = 2
|
|
102
|
+
WORLD_DENSITY_ADAPTIVE = 3
|
|
103
|
+
ZOOM_DENSITY_ADAPTIVE = 4
|
|
104
|
+
|
|
105
|
+
|