IBB-Helper 0.4.8.dev24__tar.gz → 0.4.8.dev25__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 (25) hide show
  1. {ibb_helper-0.4.8.dev24/src/IBB_Helper.egg-info → ibb_helper-0.4.8.dev25}/PKG-INFO +1 -1
  2. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/pyproject.toml +1 -1
  3. ibb_helper-0.4.8.dev25/src/IBB_Helper/animate.py +214 -0
  4. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25/src/IBB_Helper.egg-info}/PKG-INFO +1 -1
  5. ibb_helper-0.4.8.dev24/src/IBB_Helper/animate.py +0 -176
  6. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/LICENSE +0 -0
  7. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/README.md +0 -0
  8. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/setup.cfg +0 -0
  9. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/setup.py +0 -0
  10. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/__init__.py +0 -0
  11. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/combine_plots.py +0 -0
  12. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/display.py +0 -0
  13. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/display_eigen.py +0 -0
  14. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/display_matrix.py +0 -0
  15. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/extend_plot.py +0 -0
  16. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/minimize.py +0 -0
  17. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/num_int.py +0 -0
  18. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/plot_2d.py +0 -0
  19. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/plot_3d.py +0 -0
  20. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/plot_param_grid.py +0 -0
  21. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper/symbolic_BSpline.py +0 -0
  22. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper.egg-info/SOURCES.txt +0 -0
  23. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper.egg-info/dependency_links.txt +0 -0
  24. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper.egg-info/requires.txt +0 -0
  25. {ibb_helper-0.4.8.dev24 → ibb_helper-0.4.8.dev25}/src/IBB_Helper.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IBB_Helper
3
- Version: 0.4.8.dev24
3
+ Version: 0.4.8.dev25
4
4
  Summary: Helper functions for symbolic math, matrix visualization, and plotting
5
5
  Author-email: "University of Stuttgart, Institute for Structural Mechanics (IBB)" <mvs@ibb.uni-stuttgart.de>
6
6
  License-Expression: BSD-3-Clause
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "IBB_Helper"
7
- version = "0.4.8.dev24"
7
+ version = "0.4.8.dev25"
8
8
  description = "Helper functions for symbolic math, matrix visualization, and plotting"
9
9
  readme = "README.md"
10
10
  license = "BSD-3-Clause"
@@ -0,0 +1,214 @@
1
+ import numpy as np
2
+ import sympy as sp
3
+ import matplotlib.pyplot as plt
4
+ import matplotlib.animation
5
+ from sympy import latex, Matrix
6
+ from IPython.display import display
7
+
8
+ def animate(exprs, var=None, **kwargs):
9
+
10
+ """
11
+ Animates 2D curves from symbolic expressions or numeric datasets using Matplotlib.
12
+
13
+ Parameters:
14
+ exprs : Expression, tuple, or list of expressions to animate (symbolic, parametric, or numeric data)
15
+ var : Symbol or tuple defining the variable and range, or array mode ('array', x_values)
16
+ labels : Curve labels (strings or SymPy expressions, LaTeX supported) (default=None)
17
+ line_styles : Line styles for each curve (default=None → solid)
18
+ colors : Colors for each curve (default=None → Matplotlib cycle)
19
+ title : Plot title (string or SymPy expression, LaTeX supported) (default="Animation")
20
+ xlabel : X-axis label (string or SymPy expression, LaTeX supported) (default="x")
21
+ ylabel : Y-axis label (string or SymPy expression, LaTeX supported) (default="y")
22
+ xlim : X-axis limits as (min, max) (default=None → auto)
23
+ ylim : Y-axis limits as (min, max) (default=None → auto)
24
+ linewidth : Line width(s) as int or list (default=2)
25
+ animation_time : Total animation duration in milliseconds (default=5000)
26
+ frames : Number of animation frames (default=100)
27
+ resolution : Number of evaluation points for symbolic expressions (default=400)
28
+ show : Whether to display the animation (default=True)
29
+ width : Plot width in pixels (default=800)
30
+ height : Plot height in pixels (default=600)
31
+
32
+ Returns:
33
+ matplotlib.animation.FuncAnimation
34
+ The animation object
35
+ """
36
+
37
+ # Unpack Options (The "Unfolding")
38
+ labels = kwargs.get('labels', None)
39
+ line_styles = kwargs.get('line_styles', None)
40
+ colors = kwargs.get('colors', None)
41
+
42
+ title = kwargs.get('title', "Animation")
43
+ xlabel = kwargs.get('xlabel', "x")
44
+ ylabel = kwargs.get('ylabel', "y")
45
+
46
+ xlim = kwargs.get('xlim', None)
47
+ ylim = kwargs.get('ylim', None)
48
+
49
+ thickness = kwargs.get('thickness', 2)
50
+ scaling = kwargs.get('scaling', None)
51
+
52
+ animation_time = kwargs.get('animation_time', 5000)
53
+ frames = kwargs.get('frames', 100)
54
+ resolution = kwargs.get('resolution', 400)
55
+ show = kwargs.get('show', True)
56
+
57
+ width = kwargs.get('width', 800)
58
+ height = kwargs.get('height', 600)
59
+
60
+ # Smart Labels
61
+ def _smart_label(lbl):
62
+ if isinstance(lbl, str):
63
+ if any(c in lbl for c in ['_', '^', '\\']): return f"${lbl}$"
64
+ return lbl
65
+ elif lbl is None: return None
66
+ return f"${latex(lbl)}$"
67
+
68
+ # Extract Data from Existing Plot
69
+ def _extract_from_axes(ax_input):
70
+ extracted_data, extracted_styles, extracted_colors = [], [], []
71
+ extracted_labels, extracted_widths = [], []
72
+ for line in ax_input.lines:
73
+ extracted_data.append((line.get_xdata(), line.get_ydata()))
74
+ extracted_colors.append(line.get_color())
75
+ extracted_styles.append(line.get_linestyle())
76
+ extracted_labels.append(line.get_label())
77
+ extracted_widths.append(line.get_linewidth())
78
+ return extracted_data, extracted_styles, extracted_colors, extracted_labels, extracted_widths
79
+
80
+ # Handle Parametric Logic
81
+ def _get_parametric_data(expr, x_sym, frames_count):
82
+ if len(expr) == 3: # Custom Range
83
+ x_e, y_e, range_info = expr
84
+ t_sym, t_min, t_max = range_info
85
+ t_vals = np.linspace(float(t_min), float(t_max), frames_count)
86
+ f_x = sp.lambdify(t_sym, x_e, modules='numpy')
87
+ f_y = sp.lambdify(t_sym, y_e, modules='numpy')
88
+ x_res, y_res = f_x(t_vals), f_y(t_vals)
89
+ if not hasattr(x_res, '__len__'): x_res = np.full_like(t_vals, x_res)
90
+ if not hasattr(y_res, '__len__'): y_res = np.full_like(t_vals, y_res)
91
+ return np.array(x_res).flatten(), np.array(y_res).flatten()
92
+ elif len(expr) == 2: # Global Range
93
+ x_data, y_data = expr
94
+ if isinstance(x_data, sp.Basic) or isinstance(y_data, sp.Basic):
95
+ f_x = sp.lambdify(x_sym, x_data, modules='numpy')
96
+ f_y = sp.lambdify(x_sym, y_data, modules='numpy')
97
+ x_data, y_data = f_x(x_vals_sample), f_y(x_vals_sample)
98
+ if hasattr(x_data, '__len__') and not hasattr(y_data, '__len__'):
99
+ y_data = np.full_like(x_data, y_data)
100
+ elif not hasattr(x_data, '__len__') and hasattr(y_data, '__len__'):
101
+ x_data = np.full_like(y_data, x_data)
102
+ return x_data, y_data
103
+ return None, None
104
+
105
+ # Handle Standard y=f(x) Logic
106
+ def _get_standard_data(expr, x_sym, x_vals_sample):
107
+ expr = sp.sympify(expr)
108
+ if not expr.has(x_sym):
109
+ y_vals = np.full_like(x_vals_sample, float(expr))
110
+ else:
111
+ f = sp.lambdify(x_sym, expr, modules='numpy')
112
+ y_vals = np.array(f(x_vals_sample)).flatten()
113
+ return x_vals_sample, y_vals
114
+
115
+ # Main Execution
116
+ dpi = 100
117
+ plt.rcParams["animation.html"] = "jshtml"
118
+ plt.rcParams['figure.dpi'] = dpi
119
+ plt.ioff()
120
+
121
+ plot_data = []
122
+ final_colors, final_styles, final_labels, final_widths = [], [], [], []
123
+
124
+ # Existing Plot
125
+ if isinstance(exprs, plt.Axes):
126
+ plot_data, final_styles, final_colors, final_labels, final_widths = _extract_from_axes(exprs)
127
+ if plot_data: frames = max(len(d[0]) for d in plot_data)
128
+ if title == "Animation": title = exprs.get_title()
129
+ if xlabel == "x": xlabel = exprs.get_xlabel()
130
+ if ylabel == "y": ylabel = exprs.get_ylabel()
131
+
132
+ # List of Expressions
133
+ else:
134
+ if not isinstance(exprs, list): exprs = [exprs]
135
+
136
+ # Parse Variable/Range
137
+ if isinstance(var, tuple):
138
+ if var[0] == 'array':
139
+ x_sym, x_vals_sample = None, var[1]
140
+ frames = len(x_vals_sample)
141
+ else:
142
+ x_sym, x_range = var[0], var[1]
143
+ x_vals_sample = np.linspace(float(x_range[0]), float(x_range[1]), frames)
144
+ else:
145
+ x_sym = var
146
+ x_range = (-1, 1)
147
+ x_vals_sample = np.linspace(float(x_range[0]), float(x_range[1]), frames)
148
+
149
+ # Process Expressions
150
+ for i, expr in enumerate(exprs):
151
+ if isinstance(expr, Matrix): expr = np.array(expr).astype(np.float64).flatten()
152
+
153
+ # Style defaults
154
+ usr_style = line_styles[i] if line_styles and i < len(line_styles) else None
155
+ usr_color = colors[i] if colors and i < len(colors) else None
156
+ usr_label = labels[i] if labels and i < len(labels) else None
157
+ usr_width = thickness[i] if isinstance(thickness, list) and i < len(thickness) else (thickness if isinstance(thickness, (int, float)) else 2)
158
+
159
+ final_styles.append(usr_style)
160
+ final_colors.append(usr_color)
161
+ final_labels.append(usr_label)
162
+ final_widths.append(usr_width)
163
+
164
+ if isinstance(expr, (tuple, list)):
165
+ px, py = _get_parametric_data(expr, x_sym, frames)
166
+ plot_data.append((px, py))
167
+ else:
168
+ sx, sy = _get_standard_data(expr, x_sym, x_vals_sample)
169
+ plot_data.append((sx, sy))
170
+
171
+ # Limits & Figure Setup
172
+ if xlim is None and plot_data:
173
+ all_x = np.concatenate([d[0] for d in plot_data])
174
+ min_x, max_x = np.min(all_x), np.max(all_x)
175
+ pad_x = (max_x - min_x) * 0.1 if max_x != min_x else 0.5
176
+ xlim = (min_x - pad_x, max_x + pad_x)
177
+
178
+ if ylim is None and plot_data:
179
+ all_y = np.concatenate([d[1] for d in plot_data])
180
+ min_y, max_y = np.min(all_y), np.max(all_y)
181
+ pad_y = (max_y - min_y) * 0.1 if max_y != min_y else 0.5
182
+ ylim = (min_y - pad_y, max_y + pad_y)
183
+
184
+ fig, ax = plt.subplots(figsize=(width / dpi, height / dpi))
185
+ if scaling == 'constrained': ax.set_aspect('equal', adjustable='box')
186
+
187
+ def animate_frame(frame):
188
+ ax.clear()
189
+ for i, (x_d, y_d) in enumerate(plot_data):
190
+ total_points = len(x_d)
191
+ idx = int((frame + 1) / frames * total_points)
192
+ lbl = _smart_label(final_labels[i])
193
+ stl = final_styles[i] if final_styles[i] else 'solid'
194
+ clr = final_colors[i]
195
+ wdth = final_widths[i]
196
+ ax.plot(x_d[:idx], y_d[:idx], label=lbl, linestyle=stl, color=clr, linewidth=wdth)
197
+
198
+ ax.set_xlim(xlim)
199
+ ax.set_ylim(ylim)
200
+ ax.set_title(_smart_label(title))
201
+ ax.set_xlabel(_smart_label(xlabel))
202
+ ax.set_ylabel(_smart_label(ylabel))
203
+ if any(final_labels): ax.legend(loc='upper right')
204
+ ax.grid(True, alpha=0.3)
205
+
206
+ anim = matplotlib.animation.FuncAnimation(
207
+ fig, animate_frame, frames=frames, interval=animation_time / frames, repeat=True
208
+ )
209
+
210
+ if show:
211
+ display(anim)
212
+ plt.close()
213
+
214
+ return anim
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IBB_Helper
3
- Version: 0.4.8.dev24
3
+ Version: 0.4.8.dev25
4
4
  Summary: Helper functions for symbolic math, matrix visualization, and plotting
5
5
  Author-email: "University of Stuttgart, Institute for Structural Mechanics (IBB)" <mvs@ibb.uni-stuttgart.de>
6
6
  License-Expression: BSD-3-Clause
@@ -1,176 +0,0 @@
1
- import numpy as np
2
- import sympy as sp
3
- import matplotlib.pyplot as plt
4
- import matplotlib.animation
5
- from sympy import latex, Matrix
6
-
7
- def animate(exprs, var, labels=None, line_styles=None, colors=None,
8
- title="Animation", xlabel="x", ylabel="y",
9
- xlim=None, ylim=None, thickness=2,
10
- animation_time=5000, frames=100, resolution=400, show=True):
11
- """
12
- Animates 2D curves from symbolic expressions or numeric datasets using Matplotlib.
13
-
14
- Parameters:
15
- exprs : Expression, tuple, or list of expressions to animate (symbolic, parametric, or numeric data)
16
- var : Symbol or tuple defining the variable and range, or array mode ('array', x_values)
17
- labels : Curve labels (strings or SymPy expressions, LaTeX supported) (default=None)
18
- line_styles : Line styles for each curve (default=None → solid)
19
- colors : Colors for each curve (default=None → Matplotlib cycle)
20
- title : Plot title (string or SymPy expression, LaTeX supported) (default="Animation")
21
- xlabel : X-axis label (string or SymPy expression, LaTeX supported) (default="x")
22
- ylabel : Y-axis label (string or SymPy expression, LaTeX supported) (default="y")
23
- xlim : X-axis limits as (min, max) (default=None → auto)
24
- ylim : Y-axis limits as (min, max) (default=None → auto)
25
- thickness : Line width(s) as int or list (default=2)
26
- animation_time : Total animation duration in milliseconds (default=5000)
27
- frames : Number of animation frames (default=100)
28
- resolution : Number of evaluation points for symbolic expressions (default=400)
29
- show : Whether to display the animation (default=True)
30
-
31
- Returns:
32
- matplotlib.animation.FuncAnimation
33
- The animation object
34
- """
35
-
36
-
37
- # Configure matplotlib for Jupyter notebook compatibility
38
- plt.rcParams["animation.html"] = "jshtml"
39
- plt.rcParams['figure.dpi'] = 150
40
- plt.ioff()
41
-
42
- # Helper function to convert labels to LaTeX
43
- def smart_label(lbl):
44
- if isinstance(lbl, str):
45
- # Check for LaTeX characters
46
- if any(c in lbl for c in ['_', '^', '\\']):
47
- return f"${lbl}$"
48
- else:
49
- return lbl
50
- else:
51
- # Convert SymPy expression to LaTeX
52
- return f"${latex(lbl)}$"
53
-
54
- # Normalize inputs to lists
55
- if not isinstance(exprs, list):
56
- exprs = [exprs]
57
-
58
- # Determine symbol and range
59
- is_sympy = True
60
- if isinstance(var, tuple):
61
- if var[0] == 'array':
62
- is_sympy = False
63
- x_vals_sample = var[1]
64
- frames = len(x_vals_sample)
65
- else:
66
- x_sym = var[0]
67
- x_range = var[1]
68
- x_vals_sample = np.linspace(float(x_range[0]), float(x_range[1]), frames)
69
- else:
70
- x_sym = var
71
- x_range = (-1, 1)
72
- x_vals_sample = np.linspace(float(x_range[0]), float(x_range[1]), frames)
73
-
74
- # Setup thickness
75
- if isinstance(thickness, (int, float)):
76
- thickness = [thickness] * len(exprs)
77
-
78
- # Prepare data for all expressions
79
- plot_data = []
80
- for i, expr in enumerate(exprs):
81
- # Convert SymPy Matrix to NumPy array
82
- if isinstance(expr, Matrix):
83
- expr = np.array(expr).astype(np.float64).flatten()
84
-
85
- # Tuple/list of two items (x, y) - parametric or data
86
- if isinstance(expr, (tuple, list)) and len(expr) == 2:
87
- x_data, y_data = expr
88
-
89
- # If symbolic expressions, lambdify automatically
90
- if isinstance(x_data, sp.Basic) or isinstance(y_data, sp.Basic):
91
- if is_sympy:
92
- f_x = sp.lambdify(x_sym, x_data, modules='numpy')
93
- f_y = sp.lambdify(x_sym, y_data, modules='numpy')
94
- x_data = f_x(x_vals_sample)
95
- y_data = f_y(x_vals_sample)
96
-
97
- # Handle constant parametric plots
98
- is_x_array = hasattr(x_data, '__len__')
99
- is_y_array = hasattr(y_data, '__len__')
100
-
101
- if is_x_array and not is_y_array:
102
- y_data = np.full_like(x_data, y_data)
103
- elif not is_x_array and is_y_array:
104
- x_data = np.full_like(y_data, x_data)
105
-
106
- plot_data.append((np.array(x_data), np.array(y_data)))
107
-
108
- else:
109
- # Single symbolic or numeric expression
110
- expr = sp.sympify(expr)
111
- if not expr.has(x_sym) if is_sympy else False:
112
- y_vals = np.full_like(x_vals_sample, float(expr))
113
- else:
114
- if is_sympy:
115
- f = sp.lambdify(x_sym, expr, modules='numpy')
116
- y_vals = np.array(f(x_vals_sample)).flatten()
117
- else:
118
- y_vals = np.array(expr).flatten()
119
-
120
- plot_data.append((x_vals_sample, y_vals))
121
-
122
- # Auto-calculate axis limits if not provided
123
- if xlim is None:
124
- all_x = np.concatenate([data[0] for data in plot_data])
125
- x_min, x_max = np.min(all_x), np.max(all_x)
126
- x_range = x_max - x_min
127
- xlim = (x_min - 0.05 * x_range, x_max + 0.05 * x_range)
128
-
129
- if ylim is None:
130
- all_y = np.concatenate([data[1] for data in plot_data])
131
- y_min, y_max = np.min(all_y), np.max(all_y)
132
- y_range = y_max - y_min
133
- ylim = (y_min - 0.1 * y_range, y_max + 0.1 * y_range)
134
-
135
- # Create figure
136
- fig, ax = plt.subplots(figsize=(10, 6))
137
-
138
- def animate_frame(frame):
139
- ax.clear()
140
-
141
- # Show data up to current frame
142
- idx = frame + 1
143
-
144
- for i, (x_data, y_data) in enumerate(plot_data):
145
- style = line_styles[i] if line_styles and i < len(line_styles) else 'solid'
146
- color = colors[i] if colors and i < len(colors) else None
147
- raw_label = labels[i] if labels and i < len(labels) else None
148
- label = smart_label(raw_label) if raw_label is not None else None
149
-
150
- ax.plot(x_data[:idx], y_data[:idx],
151
- label=label,
152
- linestyle=style,
153
- color=color,
154
- linewidth=thickness[i])
155
-
156
- ax.set_xlim(xlim)
157
- ax.set_ylim(ylim)
158
- ax.set_title(smart_label(title))
159
- ax.set_xlabel(smart_label(xlabel))
160
- ax.set_ylabel(smart_label(ylabel))
161
-
162
- if labels:
163
- ax.legend()
164
- ax.grid(True, alpha=0.3)
165
-
166
- # Calculate interval from animation_time and frames
167
- interval = animation_time / frames
168
-
169
- anim = matplotlib.animation.FuncAnimation(
170
- fig, animate_frame, frames=frames, interval=interval, repeat=True
171
- )
172
-
173
- if not show:
174
- plt.close()
175
-
176
- return anim