• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Pablo1990 / pyVertexModel / 12137699613

03 Dec 2024 10:26AM UTC coverage: 0.773% (-0.002%) from 0.775%
12137699613

push

github

Pablo1990
screenshot available for not vertex models

0 of 1043 branches covered (0.0%)

Branch coverage included in aggregate %.

0 of 67 new or added lines in 3 files covered. (0.0%)

4 existing lines in 3 files now uncovered.

51 of 5553 relevant lines covered (0.92%)

0.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/pyVertexModel/util/utils.py
1
import copy
×
2
import gzip
×
3
import lzma
×
4
import math
×
NEW
5
import os
×
UNCOV
6
import pickle
×
7

NEW
8
import imageio
×
NEW
9
import pyvista as pv
×
10
import numpy as np
×
11
from scipy.optimize import fsolve
×
12

13

NEW
14
def screenshot_(geo, set, t, numStep, temp_dir, selected_cells=None):
×
15
    """
16
    Create a screenshot of the current state of the model.
17
    :param geo:
18
    :param set:
19
    :param t:
20
    :param numStep:
21
    :param temp_dir:
22
    :param selected_cells:
23
    :return:
24
    """
25
    # if exists variable export_images in set
NEW
26
    if hasattr(set, 'export_images'):
×
NEW
27
        if set.export_images is False:
×
NEW
28
            return
×
29

NEW
30
    total_real_cells = len([cell.ID for cell in geo.Cells if cell.AliveStatus is not None])
×
31

32
    # Create a colormap_lim
33
    #if colormap_lim is None:
34
    #    colormap_lim = [0, total_real_cells]
35

36
    # Create a plotter
NEW
37
    if selected_cells is None:
×
NEW
38
        selected_cells = []
×
39

NEW
40
    plotter = pv.Plotter(off_screen=True)
×
41

NEW
42
    for _, cell in enumerate(geo.Cells):
×
NEW
43
        if cell.AliveStatus == 1 and (cell.ID in selected_cells or selected_cells is not []):
×
44
            # Load the VTK file as a pyvista mesh
NEW
45
            mesh = cell.create_pyvista_mesh()
×
46

47
            # Add the mesh to the plotter
48
            # Cmaps that I like: 'tab20b', 'BuPu', 'Blues'
49
            # Cmaps that I don't like: 'prism', 'bone'
NEW
50
            plotter.add_mesh(mesh, name=f'cell_{cell.ID}', scalars='Volume', lighting=True, cmap="pink",
×
51
                             clim=[0.0001, 0.0006], show_edges=True, edge_color='white', edge_opacity=0.3)
52

53

NEW
54
    for _, cell in enumerate(geo.Cells):
×
NEW
55
        if cell.AliveStatus == 1 and (cell.ID in selected_cells or selected_cells is not []):
×
NEW
56
            edge_mesh = cell.create_pyvista_edges()
×
NEW
57
            plotter.add_mesh(edge_mesh, name=f'edge_{cell.ID}', color='black', line_width=3, render_lines_as_tubes=True)
×
58

59
    # Set a fixed camera zoom level
NEW
60
    fixed_zoom_level = 1
×
NEW
61
    plotter.camera.zoom(fixed_zoom_level)
×
62

63
    # Add text to the plotter
NEW
64
    if set.ablation:
×
NEW
65
        timeAfterAblation = float(t) - float(set.TInitAblation)
×
NEW
66
        text_content = f"Ablation time: {timeAfterAblation:.2f}"
×
NEW
67
        plotter.add_text(text_content, position='upper_right', font_size=12, color='black')
×
68
    else:
NEW
69
        text_content = f"Time: {t:.2f}"
×
NEW
70
        plotter.add_text(text_content, position='upper_right', font_size=12, color='black')
×
71

72

73

74
    # Render the scene and capture a screenshot
NEW
75
    img = plotter.screenshot(transparent_background=True, scale=3)
×
76
    # Save the image to a temporary file
NEW
77
    temp_file = os.path.join(temp_dir, f'vModel_perspective_{numStep}.png')
×
NEW
78
    imageio.imwrite(temp_file, img)
×
79

80
    # True 2D
NEW
81
    plotter.enable_parallel_projection()
×
NEW
82
    plotter.enable_image_style()
×
83

84
    # Set the camera to the top view
NEW
85
    plotter.view_xy()
×
86

NEW
87
    img = plotter.screenshot(transparent_background=True, scale=3)
×
NEW
88
    temp_file = os.path.join(temp_dir, f'vModel_top_{numStep}.png')
×
NEW
89
    imageio.imwrite(temp_file, img)
×
90

91
    # Set the camera to the bottom view
NEW
92
    plotter.view_xy(negative=True)
×
93

NEW
94
    img = plotter.screenshot(transparent_background=True, scale=3)
×
NEW
95
    temp_file = os.path.join(temp_dir, f'vModel_bottom_{numStep}.png')
×
NEW
96
    imageio.imwrite(temp_file, img)
×
97

98
    # Set the camera to the front view and adjust the position to be inside the tissue
NEW
99
    plotter.view_xz()
×
NEW
100
    plotter.camera.position = (-0.6836302475532527, 0.49746550619602203, -0.0024260058999061584)
×
NEW
101
    plotter.focal_point = (0.5723095089197159, 0.49746550619602203, -0.0024260058999061584)
×
102
    # Do not show the following cells =
NEW
103
    cells_to_hide = np.array([11, 12, 16, 19, 21, 22, 23, 31, 32, 35, 36, 37, 38, 39, 41, 44, 54, 55, 56, 58, 59, 60, 61, 65, 67, 68, 75, 76, 81, 82, 83, 86, 88, 89, 90, 91, 94, 96, 97, 102, 104, 106, 108, 112, 114, 116, 119, 120, 123, 124, 128, 129, 133, 135, 140, 141, 142, 143, 144, 145, 149])
×
NEW
104
    for cell in cells_to_hide:
×
NEW
105
        plotter.remove_actor(f'cell_{cell}')
×
NEW
106
        plotter.remove_actor(f'edge_{cell}')
×
107

NEW
108
    img = plotter.screenshot(transparent_background=True, scale=3)
×
NEW
109
    temp_file = os.path.join(temp_dir, f'vModel_front_{numStep}.png')
×
NEW
110
    imageio.imwrite(temp_file, img)
×
111

112
    #Add debris cells
NEW
113
    for _, cell in enumerate(geo.Cells):
×
NEW
114
        if cell.AliveStatus == 0:
×
115
            # Load the VTK file as a pyvista mesh
NEW
116
            mesh = cell.create_pyvista_mesh()
×
NEW
117
            plotter.add_mesh(mesh, color='white', lighting=True, opacity=0.5)
×
118

NEW
119
    img = plotter.screenshot(scale=3)
×
NEW
120
    temp_file = os.path.join(temp_dir, f'vModel_front_wound_{numStep}.png')
×
NEW
121
    imageio.imwrite(temp_file, img)
×
122

123
    # Close the plotter
NEW
124
    plotter.close()
×
125

NEW
126
def screenshot(v_model, temp_dir, selected_cells=None):
×
127
    """
128
    Create a screenshot of the current state of the model.
129
    :param v_model:
130
    :param temp_dir:
131
    :param selected_cells:
132
    :return:
133
    """
NEW
134
    screenshot_(v_model.geo, v_model.set, v_model.t, v_model.numStep, temp_dir, selected_cells)
×
135

136
def load_backup_vars(backup_vars):
×
137
    return (backup_vars['Geo_b'].copy(), backup_vars['Geo_n_b'].copy(), backup_vars['Geo_0_b'], backup_vars['tr_b'],
×
138
            backup_vars['Dofs'].copy())
139

140

141
def save_backup_vars(geo, geo_n, geo_0, tr, dofs):
×
142
    backup_vars = {
×
143
        'Geo_b': geo.copy(),
144
        'Geo_n_b': geo_n.copy(),
145
        'Geo_0_b': geo_0.copy(),
146
        'tr_b': tr,
147
        'Dofs': dofs.copy(),
148
    }
149
    return backup_vars
×
150

151

152
def save_state(obj, filename):
×
153
    """
154
    Save state of the different attributes of obj in filename
155
    :param obj:
156
    :param filename:
157
    :return:
158
    """
159
    with gzip.open(filename, 'wb') as f:
×
160
        # Go through all the attributes of obj
161
        for attr in dir(obj):
×
162
            # If the attribute is not a method, save it
163
            if not callable(getattr(obj, attr)) and not attr.startswith("__"):
×
164
                pickle.dump({attr: getattr(obj, attr)}, f)
×
165

166

167
def save_variables(vars, filename):
×
168
    """
169
    Save state of the different variables in filename
170
    :param vars:
171
    :param filename:
172
    :return:
173
    """
174
    with lzma.open(filename, 'wb') as f:
×
175
        # Go through all the attributes of obj
176
        for var in vars:
×
177
            pickle.dump({var: vars[var]}, f)
×
178

179

180
def load_variables(filename):
×
181
    """
182
    Load state of the different variables from filename
183
    :param filename:
184
    :return:
185
    """
186
    vars = {}
×
187
    try:
×
188
        with lzma.open(filename, 'rb') as f:
×
189
            while True:
190
                try:
×
191
                    data = pickle.load(f)
×
192
                    for var, value in data.items():
×
193
                        vars[var] = value
×
194
                except EOFError:
×
195
                    break
×
196
    except gzip.BadGzipFile:
×
197
        with open(filename, 'rb') as f:
×
198
            while True:
199
                try:
×
200
                    data = pickle.load(f)
×
201
                    for var, value in data.items():
×
202
                        vars[var] = value
×
203
                except EOFError:
×
204
                    break
×
205
    return vars
×
206

207

208
def load_state(obj, filename, objs_to_load=None):
×
209
    """
210
    Load state of the different attributes of obj from filename
211
    :param objs_to_load:
212
    :param obj:
213
    :param filename:
214
    :return:
215
    """
216
    try:
×
217
        with gzip.open(filename, 'rb') as f:
×
218
            while True:
219
                try:
×
220
                    data = pickle.load(f)
×
221
                    for attr, value in data.items():
×
222
                        if objs_to_load is None or attr in objs_to_load:
×
223
                            setattr(obj, attr, value)
×
224
                except EOFError:
×
225
                    break
×
226
    except gzip.BadGzipFile:
×
227
        with open(filename, 'rb') as f:
×
228
            while True:
229
                try:
×
230
                    data = pickle.load(f)
×
231
                    for attr, value in data.items():
×
232
                        if objs_to_load is None or attr in objs_to_load:
×
233
                            setattr(obj, attr, value)
×
234
                except EOFError:
×
235
                    break
×
236
                except:
×
237
                    print('Error loading file: ', filename)
×
238
                    break
×
239

240

241
def ismember_rows(a, b):
×
242
    """
243
    Function to mimic MATLAB's ismember function with 'rows' option.
244
    It checks if each row of array 'a' is present in array 'b' and returns a tuple.
245
    The first element is an array of booleans indicating the presence of 'a' row in 'b'.
246
    The second element is an array of indices in 'b' where the rows of 'a' are found.
247

248
    :param a: numpy.ndarray - The array to be checked against 'b'.
249
    :param b: numpy.ndarray - The array to be checked in.
250
    :return: (numpy.ndarray, numpy.ndarray) - Tuple of boolean array and index array.
251
    """
252
    # Checking if they have the 'uint32' dtype
253
    if a.dtype != np.uint32:
×
254
        a = a.astype(np.uint32)
×
255

256
    if b.dtype != np.uint32:
×
257
        b = b.astype(np.uint32)
×
258

259
    # Creating a structured array for efficient comparison
260
    if a.ndim == 1:
×
261
        a = np.sort(a)
×
262
        void_a = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[0])))
×
263
    else:
264
        a = np.sort(a, axis=1)
×
265
        void_a = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
×
266

267
    if b.ndim == 1:
×
268
        b = np.sort(b)
×
269
        void_b = np.ascontiguousarray(b).view(np.dtype((np.void, b.dtype.itemsize * b.shape[0])))
×
270
    else:
271
        b = np.sort(b, axis=1)
×
272
        void_b = np.ascontiguousarray(b).view(np.dtype((np.void, b.dtype.itemsize * b.shape[1])))
×
273

274
    # Using numpy's in1d method for finding the presence of 'a' rows in 'b'
275
    bool_array = np.in1d(void_a, void_b)
×
276

277
    # Finding the indices where the rows of 'a' are found in 'b'
278
    index_array = np.array([np.where(void_b == row)[0][0] if row in void_b else -1 for row in void_a])
×
279

280
    return bool_array, index_array
×
281

282

283
def copy_non_mutable_attributes(class_to_change, attr_not_to_change, new_cell):
×
284
    """
285
    Copy the non-mutable attributes of class_to_change to new_cell
286
    :param class_to_change:
287
    :param attr_not_to_change:
288
    :param new_cell:
289
    :return:
290
    """
291
    for attr, value in class_to_change.__dict__.items():
×
292
        # check if attr is mutable
293
        if attr == attr_not_to_change:
×
294
            setattr(new_cell, attr, [])
×
295
        elif isinstance(value, list) or isinstance(value, dict):
×
296
            setattr(new_cell, attr, copy.deepcopy(value))
×
297
        elif hasattr(value, 'copy'):
×
298
            setattr(new_cell, attr, value.copy())
×
299
        else:
300
            setattr(new_cell, attr, copy.copy(value))
×
301

302

303
def compute_distance_3d(point1, point2):
×
304
    return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2 + (point2[2] - point1[2]) ** 2)
×
305

306

307
def RegulariseMesh(T, X, Xf=None):
×
308
    if Xf is None:
×
309
        Xf = GetBoundary(T, X)
×
310
    x, dofc, doff = Getx(X, Xf)
×
311
    dJ0 = ComputeJ(T, X)
×
312
    fun = lambda x: gBuild(x, dofc, doff, T, X)
×
313
    x, info, flag, _ = fsolve(fun, x, full_output=True)
×
314
    np_, dim = X.shape
×
315
    xf = X.T.flatten()
×
316
    xf[doff] = x
×
317
    X = xf.reshape(dim, np_).T
×
318
    dJ = ComputeJ(T, X)
×
319
    return X, flag, dJ0, dJ
×
320

321

322
def gBuild(x, dofc, doff, T, X):
×
323
    nele, npe = T.shape
×
324
    np_, dim = X.shape
×
325
    xf = X.T.flatten()
×
326
    xf[doff] = x
×
327
    X = xf.reshape(dim, np_).T
×
328
    xi = np.array([1, 1]) * np.sqrt(3) / 3
×
329
    _, dN = ShapeFunctions(xi)
×
330
    g = np.zeros(np_ * dim)
×
331
    for e in range(nele):
×
332
        Xe = X[T[e, :], :]
×
333
        Je = Xe.T @ dN
×
334
        dJ = np.linalg.det(Je)
×
335
        fact = (dJ - 1) * dJ
×
336
        for i in range(npe):
×
337
            idof = np.arange(T[e, i] * dim, T[e, i] * dim + dim)
×
338
            g[idof] += np.linalg.solve(Je.T, dN[i, :])
×
339
    g = np.delete(g, dofc)
×
340
    return g
×
341

342

343
def ComputeJ(T, X):
×
344
    nele = T.shape[0]
×
345
    dJ = np.zeros(nele)
×
346
    xi = np.array([1, 1]) * np.sqrt(3) / 3
×
347
    _, dN = ShapeFunctions(xi)
×
348
    for e in range(nele):
×
349
        Xe = X[T[e]]
×
350
        Je = Xe.T @ dN
×
351
        dJ[e] = np.linalg.det(Je)
×
352
    return dJ
×
353

354

355
def Getx(X, Xf=None):
×
356
    np_, dim = X.shape
×
357
    x = X.T.flatten()
×
358
    doff = np.arange(np_ * dim)
×
359
    if Xf is not None:
×
360
        nf = len(Xf)
×
361
        dofc = np.kron((Xf - 1) * dim, np.array([1, 1])) + np.kron(np.ones(nf), np.array([0, dim - 1])).astype(int)
×
362
        doff = np.delete(doff, dofc)
×
363
        x = np.delete(x, dofc)
×
364
    return x, dofc, doff
×
365

366

367
def ShapeFunctions(xi):
×
368
    n = len(xi)
×
369
    if n == 2:
×
370
        N = np.array([1 - xi[0] - xi[1], xi[0], xi[1]])
×
371
        dN = np.array([[-1, -1], [1, 0], [0, 1]])
×
372
    return N, dN
×
373

374

375
def GetBoundary(T, X):
×
376
    np_ = X.shape[0]
×
377
    nele = T.shape[0]
×
378
    nodesExt = np.zeros(np_, dtype=int)
×
379
    for e in range(nele):
×
380
        Te = np.append(T[e, :], T[e, 0])
×
381
        Sides = np.zeros(3, dtype=int)
×
382
        for s in range(3):
×
383
            n = Te[s:s + 2]
×
384
            for d in range(nele):
×
385
                if np.sum(np.isin(n, T[d, :])) == 2 and d != e:
×
386
                    Sides[s] = 1
×
387
                    break
×
388
            if Sides[s] == 0:
×
389
                nodesExt[Te[s:s + 2]] = Te[s:s + 2]
×
390
    nodesExt = nodesExt[nodesExt != 0]
×
391
    return nodesExt
×
392

393

394
def laplacian_smoothing(vertices, edges, fixed_indices, iteration_count=10, bounding_box=None):
×
395
    """
396
    Perform Laplacian smoothing on a mesh.
397

398
    Parameters:
399
    - vertices: Nx2 array of vertex positions.
400
    - edges: Mx2 array of indices into vertices forming edges.
401
    - fixed_indices: List of vertex indices that should not be moved.
402
    - iteration_count: Number of smoothing iterations to perform.
403
    - bounding_box: Optional [(min_x, min_y), (max_x, max_y)] bounding box to constrain vertex movement.
404
    """
405
    # Convert fixed_indices to a set for faster lookup
406
    fixed_indices = set(fixed_indices)
×
407

408
    for _ in range(iteration_count):
×
409
        new_positions = vertices.copy()
×
410

411
        for i in range(len(vertices)):
×
412
            if i in fixed_indices:
×
413
                continue
×
414

415
            # Find neighboring vertices
416
            neighbors = np.concatenate((edges[edges[:, 0] == i, 1], edges[edges[:, 1] == i, 0]))
×
417
            if len(neighbors) == 0:
×
418
                continue
×
419

420
            # Calculate the average position of neighboring vertices
421
            neighbor_positions = vertices[neighbors]
×
422
            mean_position = np.mean(neighbor_positions, axis=0)
×
423

424
            # Apply bounding box constraint if specified
425
            if bounding_box is not None:
×
426
                mean_position = np.maximum(mean_position, bounding_box[0])
×
427
                mean_position = np.minimum(mean_position, bounding_box[1])
×
428

429
            new_positions[i] = mean_position
×
430

431
        vertices = new_positions
×
432

433
    return vertices
×
434

435

436
def calculate_polygon_area(points):
×
437
    """
438
    Calculate the area of a polygon using the Shoelace formula.
439

440
    :param points: A list of (x, y) pairs representing the vertices of a polygon.
441
    :return: The area of the polygon.
442
    """
443
    area = 0.0
×
444
    n = len(points)
×
445
    for i in range(n):
×
446
        j = (i + 1) % n
×
447
        area += points[i][0] * points[j][1]
×
448
        area -= points[j][0] * points[i][1]
×
449
    area = abs(area) / 2.0
×
450
    return area
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc