IBB-Helper 0.4.8.dev7__tar.gz → 0.4.8.dev8__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.dev7/src/IBB_Helper.egg-info → ibb_helper-0.4.8.dev8}/PKG-INFO +1 -1
  2. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/pyproject.toml +1 -1
  3. ibb_helper-0.4.8.dev8/src/IBB_Helper/extend_plot.py +177 -0
  4. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8/src/IBB_Helper.egg-info}/PKG-INFO +1 -1
  5. ibb_helper-0.4.8.dev7/src/IBB_Helper/extend_plot.py +0 -240
  6. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/LICENSE +0 -0
  7. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/README.md +0 -0
  8. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/setup.cfg +0 -0
  9. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/setup.py +0 -0
  10. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/__init__.py +0 -0
  11. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/animate.py +0 -0
  12. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/combine_plots.py +0 -0
  13. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/display.py +0 -0
  14. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/display_eigen.py +0 -0
  15. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/display_matrix.py +0 -0
  16. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/minimize.py +0 -0
  17. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/num_int.py +0 -0
  18. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/plot_2d.py +0 -0
  19. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/plot_3d.py +0 -0
  20. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/plot_param_grid.py +0 -0
  21. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper/symbolic_BSpline.py +0 -0
  22. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper.egg-info/SOURCES.txt +0 -0
  23. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper.egg-info/dependency_links.txt +0 -0
  24. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/src/IBB_Helper.egg-info/requires.txt +0 -0
  25. {ibb_helper-0.4.8.dev7 → ibb_helper-0.4.8.dev8}/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.dev7
3
+ Version: 0.4.8.dev8
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.dev7"
7
+ version = "0.4.8.dev8"
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,177 @@
1
+ import matplotlib.pyplot as plt
2
+ import plotly.graph_objects as go
3
+ import copy
4
+ import numpy as np
5
+
6
+ def extend_plot(plot_list, dx=0, colors=None, labels=None, line_styles=None,
7
+ show=True, grid=False, xlim=None, ylim=None, title=None,
8
+ xlabel=None, ylabel=None, width=800, height=700,
9
+ stitch=True): # <--- NEW PARAMETER
10
+ """
11
+ stitched: If True, appends plots horizontally. If False, overlays them using absolute coords.
12
+ """
13
+
14
+ if not plot_list:
15
+ raise ValueError("Plot list cannot be empty")
16
+
17
+ first_plot = plot_list[0]
18
+
19
+ # === MATPLOTLIB AXES HANDLING ===
20
+ if hasattr(first_plot, 'lines') or hasattr(first_plot, 'get_lines'):
21
+ fig, ax = plt.subplots()
22
+ agg_xlim = [float('inf'), float('-inf')]
23
+
24
+ if isinstance(colors, str): colors = [colors] * len(plot_list)
25
+ prev_plot_max_x = None
26
+
27
+ for i, p in enumerate(plot_list):
28
+ if hasattr(p, 'lines'): lines = p.lines
29
+ elif hasattr(p, 'get_lines'): lines = p.get_lines()
30
+ else: lines = []
31
+
32
+ # --- 1. Calculate Shift ---
33
+ if stitch:
34
+ plot_min_x = float('inf')
35
+ plot_max_x = float('-inf')
36
+ for line in lines:
37
+ x_orig, _ = line.get_data()
38
+ if len(x_orig) > 0:
39
+ plot_min_x = min(plot_min_x, np.min(x_orig))
40
+ plot_max_x = max(plot_max_x, np.max(x_orig))
41
+
42
+ if i == 0:
43
+ x_shift = 0
44
+ else:
45
+ x_shift = prev_plot_max_x - plot_min_x + dx
46
+ else:
47
+ # OVERLAY MODE: No shifting
48
+ x_shift = 0
49
+ plot_max_x = 0 # Dummy value
50
+
51
+ # --- 2. Plotting ---
52
+ for line_index, line in enumerate(lines):
53
+ x_orig, y_orig = line.get_data()
54
+ x = x_orig + x_shift # Apply shift (0 if stitch=False)
55
+ y = y_orig
56
+
57
+ plot_color = colors[i] if colors and i < len(colors) else line.get_color()
58
+
59
+ orig_linestyle = line.get_linestyle()
60
+ if orig_linestyle in ['None', '']: orig_linestyle = ''
61
+ plot_style = line_styles[i] if line_styles and i < len(line_styles) else orig_linestyle
62
+
63
+ plot_marker = line.get_marker()
64
+ if plot_marker in ['None', '']: plot_marker = ''
65
+
66
+ plot_label = labels[i] if labels and i < len(labels) and line_index == 0 else None
67
+
68
+ ax.plot(x, y, color=plot_color, linestyle=plot_style, marker=plot_marker,
69
+ markersize=line.get_markersize(), linewidth=line.get_linewidth(), label=plot_label)
70
+
71
+ if len(x) > 0:
72
+ agg_xlim[0] = min(agg_xlim[0], np.min(x))
73
+ agg_xlim[1] = max(agg_xlim[1], np.max(x))
74
+
75
+ if stitch and plot_max_x != float('-inf'):
76
+ prev_plot_max_x = plot_max_x + x_shift
77
+
78
+ # Final Config
79
+ final_xlim = xlim if xlim else agg_xlim
80
+ final_ylim = ylim if ylim is not None else (first_plot.get_ylim() if hasattr(first_plot, 'get_ylim') else None)
81
+
82
+ ax.set_xlim(final_xlim)
83
+ if final_ylim: ax.set_ylim(final_ylim)
84
+
85
+ ax.set_title(title if title else (first_plot.get_title() if hasattr(first_plot, 'get_title') else ''))
86
+ ax.set_xlabel(xlabel if xlabel else (first_plot.get_xlabel() if hasattr(first_plot, 'get_xlabel') else ''))
87
+ ax.set_ylabel(ylabel if ylabel else (first_plot.get_ylabel() if hasattr(first_plot, 'get_ylabel') else ''))
88
+
89
+ if grid: ax.grid(True)
90
+ if labels: ax.legend()
91
+ if show: plt.show()
92
+ else: plt.close()
93
+ return ax
94
+
95
+ # === PLOTLY FIGURE HANDLING (YOUR CASE) ===
96
+ elif hasattr(first_plot, 'data'):
97
+ combined_fig = go.Figure()
98
+ prev_plot_max_x = None
99
+ layout_defaults = first_plot.layout
100
+
101
+ for i, fig in enumerate(plot_list):
102
+
103
+ # --- 1. Calculate Shift ---
104
+ if stitch:
105
+ plot_min_x = float('inf')
106
+ plot_max_x = float('-inf')
107
+
108
+ # Check for Surface (z) or Scatter (x,y)
109
+ for trace in fig.data:
110
+ # Prefer explicitly defined X data
111
+ if hasattr(trace, 'x') and trace.x is not None:
112
+ x_data = np.array(trace.x)
113
+ if x_data.size > 0:
114
+ plot_min_x = min(plot_min_x, np.min(x_data))
115
+ plot_max_x = max(plot_max_x, np.max(x_data))
116
+ # Fallback for Surface plots without explicit X (usually 0 to N)
117
+ elif hasattr(trace, 'z') and trace.z is not None:
118
+ # Assume local coordinates 0..shape[1] if x not provided
119
+ # But usually ibb.plot_3d provides x
120
+ pass
121
+
122
+ if i == 0:
123
+ x_shift = 0
124
+ else:
125
+ x_shift = prev_plot_max_x - plot_min_x + dx
126
+ else:
127
+ # OVERLAY MODE: Disable shift entirely
128
+ x_shift = 0
129
+ plot_max_x = 0 # Dummy
130
+
131
+ # --- 2. Add Traces ---
132
+ for trace in fig.data:
133
+ new_trace = copy.deepcopy(trace)
134
+
135
+ # Apply shift if X exists
136
+ if hasattr(trace, 'x') and trace.x is not None:
137
+ x_data = np.array(trace.x)
138
+ new_trace.x = x_data + x_shift
139
+
140
+ # Style overrides
141
+ if colors and i < len(colors):
142
+ if hasattr(new_trace, 'line'): new_trace.line.color = colors[i]
143
+ elif hasattr(new_trace, 'marker'): new_trace.marker.color = colors[i]
144
+ if line_styles and i < len(line_styles):
145
+ if hasattr(new_trace, 'line'): new_trace.line.dash = line_styles[i]
146
+ if labels and i < len(labels):
147
+ new_trace.name = labels[i]
148
+
149
+ combined_fig.add_trace(new_trace)
150
+
151
+ if stitch and plot_max_x != float('-inf'):
152
+ prev_plot_max_x = plot_max_x + x_shift
153
+
154
+ # Final Layout
155
+ combined_fig.update_layout(
156
+ title=title if title else (layout_defaults.title.text if layout_defaults.title else ''),
157
+ xaxis_title=xlabel if xlabel else (layout_defaults.xaxis.title.text if layout_defaults.xaxis.title else ''),
158
+ yaxis_title=ylabel if ylabel else (layout_defaults.yaxis.title.text if layout_defaults.yaxis.title else '')
159
+ )
160
+ if grid: combined_fig.update_layout(xaxis_showgrid=True, yaxis_showgrid=True)
161
+
162
+ combined_fig.update_layout(
163
+ autosize=False, width=width, height=height,
164
+ scene=dict(
165
+ aspectratio=dict(x=1, y=1, z=1),
166
+ camera=dict(eye=dict(x=1.87, y=0.88, z=1.5)),
167
+ ),
168
+ margin=dict(l=10, r=10, t=50, b=10),
169
+ legend=dict(x=0, y=1),
170
+ )
171
+
172
+ if show: combined_fig.show()
173
+
174
+ return combined_fig
175
+
176
+ else:
177
+ raise TypeError("Unknown plot object type passed to extend_plots.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IBB_Helper
3
- Version: 0.4.8.dev7
3
+ Version: 0.4.8.dev8
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,240 +0,0 @@
1
- import matplotlib.pyplot as plt
2
- import plotly.graph_objects as go
3
- import copy
4
- import numpy as np
5
-
6
- def extend_plot(plot_list, dx=0, colors=None, labels=None, line_styles=None,
7
- show=True, grid=False, xlim=None, ylim=None, title=None,
8
- xlabel=None, ylabel=None, width=800, height=700):
9
- """
10
- Stitches multiple plots end-to-end into a single continuous plot with optional horizontal offset.
11
-
12
- Parameters:
13
- plot_list : List of matplotlib Axes or plotly Figure objects to be stitched together
14
- dx : Horizontal offset between consecutive plots (dx=0 no gap, dx>0 gap, dx<0 overlap) (default=0)
15
- colors : Single color or list of colors for each plot (default=None)
16
- labels : Labels for each stitched segment (default=None)
17
- line_styles: Line styles for each plot segment (default=None)
18
- show : Whether to display the resulting plot (default=True)
19
- grid : Whether to display a grid (default=False)
20
- xlim : X-axis limits as (min, max) (default=None)
21
- ylim : Y-axis limits as (min, max) (default=None)
22
- title : Plot title (default=None)
23
- xlabel : X-axis label (default=None)
24
- ylabel : Y-axis label (default=None)
25
-
26
- Returns:
27
- matplotlib Axes or plotly.graph_objects.Figure
28
- The stitched plot object, matching the input plot type
29
- """
30
-
31
-
32
- if not plot_list:
33
- raise ValueError("Plot list cannot be empty")
34
-
35
- first_plot = plot_list[0]
36
-
37
- # === MATPLOTLIB AXES HANDLING (PRIMARY BRANCH) ===
38
- if hasattr(first_plot, 'lines') or hasattr(first_plot, 'get_lines'):
39
- fig, ax = plt.subplots()
40
-
41
- # Aggregation variables (for defaults if arguments are None)
42
- agg_xlim = [float('inf'), float('-inf')]
43
-
44
- # Use a single color if provided, or prepare for individual colors
45
- if isinstance(colors, str):
46
- colors = [colors] * len(plot_list)
47
-
48
- # Track the ending x position of the previous plot
49
- prev_plot_max_x = None
50
-
51
- for i, p in enumerate(plot_list):
52
-
53
- # Retrieve lines from Axes or Figure
54
- if hasattr(p, 'lines'):
55
- lines = p.lines
56
- elif hasattr(p, 'get_lines'):
57
- lines = p.get_lines()
58
- else:
59
- lines = []
60
-
61
- # First pass: find min and max x for this plot
62
- plot_min_x = float('inf')
63
- plot_max_x = float('-inf')
64
-
65
- for line in lines:
66
- x_orig, y_orig = line.get_data()
67
- if len(x_orig) > 0:
68
- plot_min_x = min(plot_min_x, np.min(x_orig))
69
- plot_max_x = max(plot_max_x, np.max(x_orig))
70
-
71
- # Calculate shift for this plot
72
- if i == 0:
73
- # First plot: no shift
74
- x_shift = 0
75
- else:
76
- # Subsequent plots: align min_x with prev_max_x + dx
77
- x_shift = prev_plot_max_x - plot_min_x + dx
78
-
79
- # --- Plotting ---
80
- for line_index, line in enumerate(lines):
81
- x_orig, y_orig = line.get_data()
82
-
83
- # Apply shift
84
- x = x_orig + x_shift
85
- y = y_orig
86
-
87
- # Default style transfer from source line
88
- plot_color = colors[i] if colors and i < len(colors) else line.get_color()
89
-
90
- # Get linestyle - handle marker-only plots
91
- orig_linestyle = line.get_linestyle()
92
- if orig_linestyle in ['None', '']:
93
- orig_linestyle = ''
94
- plot_style = line_styles[i] if line_styles and i < len(line_styles) else orig_linestyle
95
-
96
- # Get marker - normalize 'None' string to empty
97
- plot_marker = line.get_marker()
98
- if plot_marker in ['None', '']:
99
- plot_marker = ''
100
-
101
- # Label transfer (only the first line of the source plot gets the label)
102
- plot_label = labels[i] if labels and i < len(labels) and line_index == 0 else None
103
-
104
- # Plot the segment
105
- ax.plot(x, y,
106
- color=plot_color,
107
- linestyle=plot_style,
108
- marker=plot_marker,
109
- markersize=line.get_markersize(),
110
- linewidth=line.get_linewidth(),
111
- label=plot_label)
112
-
113
- # Update global xlim
114
- if len(x) > 0:
115
- agg_xlim[0] = min(agg_xlim[0], np.min(x))
116
- agg_xlim[1] = max(agg_xlim[1], np.max(x))
117
-
118
- # Update previous plot's max x for next iteration
119
- if plot_max_x != float('-inf'):
120
- prev_plot_max_x = plot_max_x + x_shift
121
-
122
-
123
- # --- Final Matplotlib Configuration ---
124
-
125
- # Apply limits (prioritize argument, then aggregated data, then source plot limits)
126
- final_xlim = xlim if xlim else agg_xlim
127
-
128
- if ylim is None and hasattr(first_plot, 'get_ylim'):
129
- final_ylim = first_plot.get_ylim()
130
- else:
131
- final_ylim = ylim
132
-
133
- ax.set_xlim(final_xlim)
134
- if final_ylim:
135
- ax.set_ylim(final_ylim)
136
-
137
- # Apply labels/title (prioritize argument, then source plot labels)
138
- ax.set_title(title if title else (first_plot.get_title() if hasattr(first_plot, 'get_title') else ''))
139
- ax.set_xlabel(xlabel if xlabel else (first_plot.get_xlabel() if hasattr(first_plot, 'get_xlabel') else ''))
140
- ax.set_ylabel(ylabel if ylabel else (first_plot.get_ylabel() if hasattr(first_plot, 'get_ylabel') else ''))
141
-
142
- if grid:
143
- ax.grid(True)
144
- if labels: # Only show legend if we managed labels
145
- ax.legend()
146
-
147
- if show:
148
- plt.show()
149
- else:
150
- plt.close()
151
-
152
- return ax
153
-
154
- # === PLOTLY FIGURE HANDLING (SECONDARY BRANCH) ===
155
- elif hasattr(first_plot, 'data'):
156
- # Plotly branch
157
- combined_fig = go.Figure()
158
- prev_plot_max_x = None
159
-
160
- # Retrieve layout defaults from the first figure
161
- layout_defaults = first_plot.layout
162
-
163
- for i, fig in enumerate(plot_list):
164
- # First pass: find min and max x for this plot
165
- plot_min_x = float('inf')
166
- plot_max_x = float('-inf')
167
-
168
- for trace in fig.data:
169
-
170
- if hasattr(trace, 'x') and trace.x is not None:
171
- x_data = np.array(trace.x)
172
- if len(x_data) > 0:
173
- plot_min_x = min(plot_min_x, np.min(x_data))
174
- plot_max_x = max(plot_max_x, np.max(x_data))
175
-
176
- # Calculate shift for this plot
177
- if i==0:
178
- x_shift = 0
179
- else:
180
- x_shift = prev_plot_max_x - plot_min_x + dx
181
-
182
-
183
- # Second pass: apply shift and add traces
184
- for trace in fig.data:
185
- # Use deepcopy for safe cloning
186
- new_trace = copy.deepcopy(trace)
187
-
188
- # Apply shift
189
- if hasattr(trace, 'x') and trace.x is not None:
190
- x_data = np.array(trace.x)
191
- new_trace.x = x_data + x_shift
192
-
193
- # Apply overrides/defaults
194
- if colors and i < len(colors):
195
- if hasattr(new_trace, 'line'):
196
- new_trace.line.color = colors[i]
197
- elif hasattr(new_trace, 'marker'):
198
- new_trace.marker.color = colors[i]
199
-
200
- if line_styles and i < len(line_styles):
201
- if hasattr(new_trace, 'line'):
202
- new_trace.line.dash = line_styles[i]
203
-
204
- if labels and i < len(labels):
205
- new_trace.name = labels[i]
206
-
207
- combined_fig.add_trace(new_trace)
208
-
209
- # Update previous plot's max x for next iteration
210
- if plot_max_x != float('-inf'):
211
- prev_plot_max_x = plot_max_x + x_shift
212
-
213
- # Final Layout Configuration (prioritize arguments over defaults)
214
- combined_fig.update_layout(
215
- title=title if title else (layout_defaults.title.text if layout_defaults.title else ''),
216
- xaxis_title=xlabel if xlabel else (layout_defaults.xaxis.title.text if layout_defaults.xaxis.title else ''),
217
- yaxis_title=ylabel if ylabel else (layout_defaults.yaxis.title.text if layout_defaults.yaxis.title else '')
218
- )
219
- if grid:
220
- combined_fig.update_layout(xaxis_showgrid=True, yaxis_showgrid=True)
221
-
222
- combined_fig.update_layout(
223
- autosize=False,
224
- width=width,
225
- height=height,
226
- scene=dict(
227
- aspectratio=dict(x=1, y=1, z=1),
228
- camera=dict(eye=dict(x=1.87, y=0.88, z=1.5)),
229
- ),
230
- margin=dict(l=10, r=10, t=50, b=10),
231
- legend=dict(x=0, y=1),
232
- )
233
-
234
- if show:
235
- combined_fig.show()
236
-
237
- return combined_fig
238
-
239
- else:
240
- raise TypeError("Unknown plot object type passed to extend_plots.")
File without changes