nettracer3d 1.2.7__py3-none-any.whl → 1.3.1__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.

Potentially problematic release.


This version of nettracer3d might be problematic. Click here for more details.

nettracer3d/painting.py CHANGED
@@ -1,32 +1,29 @@
1
1
  from PyQt6.QtWidgets import QApplication, QMainWindow
2
- import matplotlib.pyplot as plt
2
+ from PyQt6.QtCore import Qt
3
+ import pyqtgraph as pg
3
4
  import copy
4
5
  import numpy as np
5
6
 
6
7
 
7
8
  class PaintManager(QMainWindow):
8
- def __init__(self, parent = None):
9
+ def __init__(self, parent=None):
9
10
  super().__init__(parent)
10
11
  self.resume = False
11
12
 
12
13
  # Initialize stroke tracking storage once
13
14
  if parent is not None:
14
15
  if not hasattr(parent, 'completed_paint_strokes'):
15
- parent.completed_paint_strokes = [] # List of individual completed strokes
16
+ parent.completed_paint_strokes = []
16
17
  if not hasattr(parent, 'current_stroke_points'):
17
- parent.current_stroke_points = [] # Current stroke being drawn
18
+ parent.current_stroke_points = []
18
19
  if not hasattr(parent, 'current_stroke_type'):
19
- parent.current_stroke_type = None # 'draw' or 'erase'
20
+ parent.current_stroke_type = None
20
21
 
21
- # Keep the old properties for display purposes
22
- if not hasattr(parent, 'virtual_draw_operations'):
23
- parent.virtual_draw_operations = []
24
- if not hasattr(parent, 'virtual_erase_operations'):
25
- parent.virtual_erase_operations = []
26
- if not hasattr(parent, 'current_operation'):
27
- parent.current_operation = []
28
- if not hasattr(parent, 'current_operation_type'):
29
- parent.current_operation_type = None
22
+ # PyQtGraph visual items
23
+ if not hasattr(parent, 'virtual_paint_items'):
24
+ parent.virtual_paint_items = [] # Store all visual items
25
+ if not hasattr(parent, 'current_paint_items'):
26
+ parent.current_paint_items = [] # Current stroke visuals
30
27
 
31
28
  def get_line_points(self, x0, y0, x1, y1):
32
29
  """Get all points in a line between (x0,y0) and (x1,y1) using Bresenham's algorithm."""
@@ -60,27 +57,19 @@ class PaintManager(QMainWindow):
60
57
  return points
61
58
 
62
59
  def initiate_paint_session(self, channel, current_xlim, current_ylim):
63
- # Create static background (same as selection rectangle)
64
-
65
60
  if self.parent().machine_window is not None:
66
61
  if self.parent().machine_window.segmentation_worker is not None:
67
62
  if not self.parent().machine_window.segmentation_worker._paused:
68
63
  self.resume = True
69
64
  self.parent().machine_window.segmentation_worker.pause()
70
65
 
71
-
72
66
  if not self.parent().channel_visible[channel]:
73
67
  self.parent().channel_visible[channel] = True
74
-
75
- # Capture the background once
76
- self.parent().static_background = self.parent().canvas.copy_from_bbox(self.parent().ax.bbox)
77
68
 
78
69
  if self.resume:
79
70
  self.parent().machine_window.segmentation_worker.resume()
80
71
  self.resume = False
81
72
 
82
-
83
-
84
73
  def start_virtual_paint_session(self, channel, current_xlim, current_ylim):
85
74
  """Start a virtual paint session that doesn't modify arrays until the end."""
86
75
  self.parent().painting = True
@@ -90,7 +79,7 @@ class PaintManager(QMainWindow):
90
79
  if not self.parent().channel_visible[channel]:
91
80
  self.parent().channel_visible[channel] = True
92
81
 
93
- # Initialize stroke tracking storage ONLY if they don't exist
82
+ # Initialize stroke tracking storage
94
83
  if not hasattr(self.parent(), 'completed_paint_strokes'):
95
84
  self.parent().completed_paint_strokes = []
96
85
  if not hasattr(self.parent(), 'current_stroke_points'):
@@ -98,50 +87,53 @@ class PaintManager(QMainWindow):
98
87
  if not hasattr(self.parent(), 'current_stroke_type'):
99
88
  self.parent().current_stroke_type = None
100
89
 
101
- # Initialize display storage ONLY if they don't exist
102
- if not hasattr(self.parent(), 'virtual_draw_operations'):
103
- self.parent().virtual_draw_operations = []
104
- if not hasattr(self.parent(), 'virtual_erase_operations'):
105
- self.parent().virtual_erase_operations = []
106
- if not hasattr(self.parent(), 'current_operation'):
107
- self.parent().current_operation = []
108
- if not hasattr(self.parent(), 'current_operation_type'):
109
- self.parent().current_operation_type = None
90
+ # Initialize PyQtGraph visual storage
91
+ if not hasattr(self.parent(), 'virtual_paint_items'):
92
+ self.parent().virtual_paint_items = []
93
+ if not hasattr(self.parent(), 'current_paint_items'):
94
+ self.parent().current_paint_items = []
110
95
 
111
96
  def reset_all_paint_storage(self):
112
- """Reset all paint storage - call this when you want to start completely fresh."""
97
+ """Reset all paint storage."""
98
+ # Clear visual items from view
99
+ if hasattr(self.parent(), 'virtual_paint_items'):
100
+ for item in self.parent().virtual_paint_items:
101
+ try:
102
+ self.parent().view.removeItem(item)
103
+ except:
104
+ pass
105
+
106
+ if hasattr(self.parent(), 'current_paint_items'):
107
+ for item in self.parent().current_paint_items:
108
+ try:
109
+ self.parent().view.removeItem(item)
110
+ except:
111
+ pass
112
+
113
113
  self.parent().completed_paint_strokes = []
114
114
  self.parent().current_stroke_points = []
115
115
  self.parent().current_stroke_type = None
116
- self.parent().virtual_draw_operations = []
117
- self.parent().virtual_erase_operations = []
118
- self.parent().current_operation = []
119
- self.parent().current_operation_type = None
120
-
121
-
116
+ self.parent().virtual_paint_items = []
117
+ self.parent().current_paint_items = []
122
118
 
123
119
  def add_virtual_paint_point(self, x, y, brush_size, erase=False, foreground=True):
124
- """Add a single paint point to the virtual layer."""
120
+ """Add a single paint point to the virtual layer using PyQtGraph."""
125
121
 
126
122
  # Determine operation type and visual properties
127
123
  if erase:
128
- paint_color = 'black' # Visual indicator for erase
129
- alpha = 0.5
124
+ paint_color = (0, 0, 0) # Black for erase
130
125
  operation_type = 'erase'
131
126
  else:
132
127
  if self.parent().machine_window is not None:
133
128
  if foreground:
134
- paint_color = 'green' # Visual for foreground (value 1)
135
- alpha = 0.7
129
+ paint_color = (0, 255, 0) # Green for foreground (value 1)
136
130
  else:
137
- paint_color = 'red' # Visual for background (value 2)
138
- alpha = 0.7
131
+ paint_color = (255, 0, 0) # Red for background (value 2)
139
132
  else:
140
- paint_color = 'white' # Normal paint
141
- alpha = 0.7
133
+ paint_color = (255, 255, 255) # White for normal paint
142
134
  operation_type = 'draw'
143
-
144
- # Store the operation data (for later conversion to real paint)
135
+
136
+ # Store the operation data
145
137
  operation_data = {
146
138
  'x': x,
147
139
  'y': y,
@@ -153,229 +145,156 @@ class PaintManager(QMainWindow):
153
145
  'threedthresh': getattr(self.parent(), 'threedthresh', 1)
154
146
  }
155
147
 
156
- # Add to stroke tracking (NEW - separate stroke tracking)
148
+ # Add to stroke tracking
157
149
  if self.parent().current_stroke_type != operation_type:
158
- # Finish previous stroke if switching between draw/erase
159
150
  self.finish_current_stroke()
160
151
  self.parent().current_stroke_type = operation_type
161
152
 
162
153
  self.parent().current_stroke_points.append(operation_data)
163
154
 
164
- # Create visual circle for display
165
- circle = plt.Circle((x, y), brush_size/2,
166
- color=paint_color, alpha=alpha, animated=True)
167
-
168
- # Add to display operations (OLD - for visual display)
169
- if self.parent().current_operation_type != operation_type:
170
- # Finish previous operation if switching between draw/erase
171
- self.finish_current_virtual_operation()
172
- self.parent().current_operation_type = operation_type
173
-
174
- self.parent().current_operation.append({
175
- 'circle': circle,
176
- 'data': operation_data
177
- })
178
-
179
- self.parent().ax.add_patch(circle)
155
+ # Create visual circle using ScatterPlotItem
156
+ scatter = pg.ScatterPlotItem(
157
+ [x], [y],
158
+ size=brush_size,
159
+ pen=pg.mkPen(paint_color, width=1),
160
+ brush=pg.mkBrush(*paint_color, 127) # 50% alpha
161
+ )
162
+
163
+ # Add to view
164
+ self.parent().view.addItem(scatter)
165
+ self.parent().current_paint_items.append(scatter)
180
166
 
181
167
  def finish_current_stroke(self):
182
168
  """Finish the current stroke and add it to completed strokes."""
183
169
  if not self.parent().current_stroke_points:
184
170
  return
185
-
186
- # Store the completed stroke with its type
171
+
172
+ # Store the completed stroke with its type AND visual items
187
173
  stroke_data = {
188
174
  'points': self.parent().current_stroke_points.copy(),
189
- 'type': self.parent().current_stroke_type
175
+ 'type': self.parent().current_stroke_type,
176
+ 'visual_items': self.parent().current_paint_items.copy() # Store visual items with stroke
190
177
  }
191
178
 
192
179
  self.parent().completed_paint_strokes.append(stroke_data)
193
180
 
194
- # Clear current stroke
181
+ # Move current visual items to completed
182
+ self.parent().virtual_paint_items.extend(self.parent().current_paint_items)
183
+ self.parent().current_paint_items = []
184
+
185
+ # Clear current stroke data
195
186
  self.parent().current_stroke_points = []
196
187
  self.parent().current_stroke_type = None
197
188
 
189
+ def undo_last_virtual_stroke(self):
190
+ """Undo the most recent virtual paint stroke (not yet converted to data)."""
191
+
192
+ # First try to undo the current stroke in progress
193
+ if hasattr(self.parent(), 'current_stroke_points') and self.parent().current_stroke_points:
194
+ # Remove visual items for current stroke
195
+ if hasattr(self.parent(), 'current_paint_items'):
196
+ for item in self.parent().current_paint_items:
197
+ try:
198
+ self.parent().view.removeItem(item)
199
+ except:
200
+ pass
201
+ self.parent().current_paint_items = []
202
+
203
+ # Clear current stroke data
204
+ self.parent().current_stroke_points = []
205
+ self.parent().current_stroke_type = None
206
+ return True # Successfully undid current stroke
207
+
208
+ # If no current stroke, undo the most recent completed stroke
209
+ if hasattr(self.parent(), 'completed_paint_strokes') and self.parent().completed_paint_strokes:
210
+ # Get the last completed stroke
211
+ last_stroke = self.parent().completed_paint_strokes.pop()
212
+
213
+ # Remove its visual items from the view
214
+ visual_items = last_stroke.get('visual_items', [])
215
+ for item in visual_items:
216
+ try:
217
+ self.parent().view.removeItem(item)
218
+ # Also remove from virtual_paint_items list
219
+ if item in self.parent().virtual_paint_items:
220
+ self.parent().virtual_paint_items.remove(item)
221
+ except:
222
+ pass
223
+
224
+ return True # Successfully undid completed stroke
225
+
226
+ # Nothing to undo
227
+ return False
228
+
198
229
  def add_virtual_paint_stroke(self, x, y, brush_size, erase=False, foreground=True):
199
- """Add a paint stroke - simple visual, interpolation happens during data conversion."""
200
- # Just add the current point for visual display (no interpolation yet)
230
+ """Add a paint stroke."""
201
231
  self.add_virtual_paint_point(x, y, brush_size, erase, foreground)
202
-
203
- # Store the last position for data conversion later
204
232
  self.parent().last_virtual_pos = (x, y)
205
233
 
206
234
  def connect_virtual_paint_points(self):
207
- """Connect points with lines matching the circle size by converting to screen coordinates."""
208
-
209
- if not hasattr(self.parent(), 'current_operation') or len(self.parent().current_operation) < 2:
235
+ """Connect points with lines matching the brush size."""
236
+ if not hasattr(self.parent(), 'current_stroke_points') or len(self.parent().current_stroke_points) < 2:
210
237
  return
211
238
 
212
- # Get existing points but DON'T remove them
213
- existing_points = self.parent().current_operation.copy()
214
- point_data = [item['data'] for item in existing_points if 'data' in item]
239
+ point_data = self.parent().current_stroke_points
215
240
 
216
241
  if len(point_data) < 2:
217
242
  return
218
243
 
219
- # Get visual properties and brush size from first point
244
+ # Get visual properties from first point
220
245
  first_data = point_data[0]
221
- brush_size_data = first_data['brush_size'] # This is in data coordinates
222
-
223
- # Convert brush size from data coordinates to points for linewidth
224
- # Get the transformation from data to display coordinates
225
- ax = self.parent().ax
226
-
227
- # Transform two points to see the scaling
228
- p1_data = [0, 0]
229
- p2_data = [brush_size_data, 0] # One brush_size unit away
230
-
231
- p1_display = ax.transData.transform(p1_data)
232
- p2_display = ax.transData.transform(p2_data)
233
-
234
- # Calculate pixels per data unit
235
- pixels_per_data_unit = abs(p2_display[0] - p1_display[0])
236
-
237
- # Convert to points (matplotlib uses 72 points per inch, figure.dpi pixels per inch)
238
- fig = ax.figure
239
- points_per_pixel = 72.0 / fig.dpi
240
- brush_size_points = pixels_per_data_unit * points_per_pixel
246
+ brush_size = first_data['brush_size']
241
247
 
242
248
  if first_data['erase']:
243
- line_color = 'black'
244
- alpha = 0.5
249
+ line_color = (0, 0, 0) # Black
245
250
  else:
246
251
  if self.parent().machine_window is not None:
247
252
  if first_data['foreground']:
248
- line_color = 'green'
249
- alpha = 0.7
253
+ line_color = (0, 255, 0) # Green
250
254
  else:
251
- line_color = 'red'
252
- alpha = 0.7
255
+ line_color = (255, 0, 0) # Red
253
256
  else:
254
- line_color = 'white'
255
- alpha = 0.7
256
-
257
- # Create line segments for connections using LineCollection
258
- from matplotlib.collections import LineCollection
259
-
260
- segments = []
261
- for i in range(len(point_data) - 1):
262
- x1, y1 = point_data[i]['x'], point_data[i]['y']
263
- x2, y2 = point_data[i+1]['x'], point_data[i+1]['y']
264
- segments.append([(x1, y1), (x2, y2)])
265
-
266
- # Create line collection with converted linewidth
267
- if segments:
268
- lc = LineCollection(segments,
269
- colors=line_color,
270
- alpha=alpha,
271
- linewidths=brush_size_points, # Now in points, matching circles
272
- animated=True)
273
- self.parent().ax.add_collection(lc)
274
-
275
- # Add the line collection as a visual-only element
276
- self.parent().current_operation.append({
277
- 'line_collection': lc,
278
- 'is_connection_visual': True
279
- })
257
+ line_color = (255, 255, 255) # White
258
+
259
+ # Create line segments
260
+ x_coords = [p['x'] for p in point_data]
261
+ y_coords = [p['y'] for p in point_data]
262
+
263
+ # Create connected line
264
+ line = pg.PlotDataItem(
265
+ x_coords, y_coords,
266
+ pen=pg.mkPen(color=line_color, width=brush_size)
267
+ )
268
+ line.setOpacity(0.5)
269
+
270
+ # Add to view
271
+ self.parent().view.addItem(line)
272
+ self.parent().current_paint_items.append(line)
280
273
 
281
274
  def finish_current_virtual_operation(self):
282
- """Finish the current operation (draw or erase) and add it to the appropriate list."""
283
-
284
- if not self.parent().current_operation:
285
- return
286
-
287
- # Filter out connection visuals from the operation before storing
288
- data_items = []
289
- visual_items = []
290
-
291
- for item in self.parent().current_operation:
292
- if item.get('is_connection_visual', False):
293
- visual_items.append(item)
294
- else:
295
- data_items.append(item)
296
-
297
- # Only store the data items for this specific stroke
298
- if data_items:
299
- if self.parent().current_operation_type == 'draw':
300
- self.parent().virtual_draw_operations.append(data_items)
301
- elif self.parent().current_operation_type == 'erase':
302
- self.parent().virtual_erase_operations.append(data_items)
303
-
304
- # Clean up visual items that are connection-only
305
- for item in visual_items:
306
- try:
307
- if 'line_collection' in item:
308
- item['line_collection'].remove()
309
- elif 'line' in item:
310
- item['line'].remove()
311
- except:
312
- pass
313
-
314
- self.parent().current_operation = []
315
- self.parent().current_operation_type = None
316
-
317
- def update_virtual_paint_display(self):
318
- """Update display with virtual paint strokes - handles different object types."""
319
- if not hasattr(self.parent(), 'static_background') or self.parent().static_background is None:
320
- return
321
-
322
- # Restore the clean background
323
- self.parent().canvas.restore_region(self.parent().static_background)
324
-
325
- # Draw all completed operations
326
- for operation_list in [self.parent().virtual_draw_operations, self.parent().virtual_erase_operations]:
327
- for operation in operation_list:
328
- for item in operation:
329
- self._draw_virtual_item(item)
330
-
331
- # Draw current operation being painted
332
- if hasattr(self.parent(), 'current_operation'):
333
- for item in self.parent().current_operation:
334
- self._draw_virtual_item(item)
335
-
336
- # Blit everything at once
337
- self.parent().canvas.blit(self.parent().ax.bbox)
338
-
339
- def _draw_virtual_item(self, item):
340
- """Helper method to draw different types of virtual paint items."""
341
- try:
342
- # Skip items that are marked as visual-only connections
343
- if item.get('is_connection_visual', False):
344
- if 'line' in item:
345
- self.parent().ax.draw_artist(item['line'])
346
- elif 'line_collection' in item:
347
- self.parent().ax.draw_artist(item['line_collection'])
348
- elif 'circle' in item:
349
- self.parent().ax.draw_artist(item['circle'])
350
- elif 'line' in item:
351
- self.parent().ax.draw_artist(item['line'])
352
- elif 'line_collection' in item:
353
- self.parent().ax.draw_artist(item['line_collection'])
354
- except Exception as e:
355
- # Skip items that can't be drawn (might have been removed)
356
- pass
275
+ """Finish the current operation."""
276
+ self.finish_current_stroke()
357
277
 
358
278
  def convert_virtual_strokes_to_data(self):
359
- """Convert each stroke separately to actual array data using ONLY the new stroke tracking system."""
279
+ """Convert each stroke separately to actual array data."""
360
280
 
361
281
  # Finish the current stroke first
362
282
  self.finish_current_stroke()
363
283
 
364
- # Process ONLY the completed_paint_strokes (ignore old display operations)
284
+ # Process completed strokes
365
285
  for stroke in self.parent().completed_paint_strokes:
366
286
  stroke_points = stroke['points']
367
- stroke_type = stroke['type']
368
287
 
369
288
  if len(stroke_points) == 0:
370
289
  continue
371
-
372
- # Apply interpolation within this stroke only
290
+
291
+ # Apply interpolation within this stroke
373
292
  last_pos = None
374
293
  for point_data in stroke_points:
375
294
  current_pos = (point_data['x'], point_data['y'])
376
295
 
377
296
  if last_pos is not None:
378
- # Interpolate between consecutive points in this stroke
297
+ # Interpolate between consecutive points
379
298
  points = self.get_line_points(last_pos[0], last_pos[1], current_pos[0], current_pos[1])
380
299
  for px, py in points:
381
300
  self.paint_at_position_vectorized(
@@ -403,74 +322,25 @@ class PaintManager(QMainWindow):
403
322
 
404
323
  last_pos = current_pos
405
324
 
406
- # Clean up ALL visual elements (both old and new systems)
407
- for operation_list in [self.parent().virtual_draw_operations, self.parent().virtual_erase_operations]:
408
- for operation in operation_list:
409
- for item in operation:
410
- try:
411
- if 'circle' in item:
412
- item['circle'].remove()
413
- elif 'line_collection' in item:
414
- item['line_collection'].remove()
415
- elif 'line' in item:
416
- item['line'].remove()
417
- except:
418
- pass
325
+ # Clean up visual elements
326
+ for item in self.parent().virtual_paint_items:
327
+ try:
328
+ self.parent().view.removeItem(item)
329
+ except:
330
+ pass
419
331
 
420
- if hasattr(self.parent(), 'current_operation'):
421
- for item in self.parent().current_operation:
422
- try:
423
- if 'circle' in item:
424
- item['circle'].remove()
425
- elif 'line_collection' in item:
426
- item['line_collection'].remove()
427
- elif 'line' in item:
428
- item['line'].remove()
429
- except:
430
- pass
332
+ for item in self.parent().current_paint_items:
333
+ try:
334
+ self.parent().view.removeItem(item)
335
+ except:
336
+ pass
431
337
 
432
- # Reset all storage for next paint session
338
+ # Reset storage
433
339
  self.parent().completed_paint_strokes = []
434
340
  self.parent().current_stroke_points = []
435
341
  self.parent().current_stroke_type = None
436
- self.parent().virtual_draw_operations = []
437
- self.parent().virtual_erase_operations = []
438
- self.parent().current_operation = []
439
- self.parent().current_operation_type = None
440
-
441
- def end_virtual_paint_session(self):
442
- """Convert virtual paint to actual array modifications when exiting paint mode."""
443
- if not hasattr(self.parent(), 'virtual_paint_strokes'):
444
- return
445
-
446
- # Now apply all the virtual strokes to the actual arrays
447
- for stroke in self.parent().virtual_paint_strokes:
448
- for circle in stroke:
449
- center = circle.center
450
- radius = circle.radius
451
- is_erase = circle.get_facecolor()[0] == 0 # Black = erase
452
-
453
- # Apply to actual array
454
- self.paint_at_position_vectorized(
455
- int(center[0]), int(center[1]),
456
- erase=is_erase,
457
- channel=self.paint_channel,
458
- brush_size=int(radius * 2)
459
- )
460
-
461
- # Remove the virtual circle
462
- circle.remove()
463
-
464
- # Clean up virtual paint data
465
- self.virtual_paint_strokes = []
466
- self.current_stroke = []
467
-
468
- # Reset background
469
- self.static_background = None
470
- self.painting = False
471
-
472
- # Full refresh to show final result
473
- self.update_display()
342
+ self.parent().virtual_paint_items = []
343
+ self.parent().current_paint_items = []
474
344
 
475
345
  def paint_at_position_vectorized(self, center_x, center_y, erase=False, channel=2,
476
346
  slice_idx=None, brush_size=None, threed=None,
@@ -478,46 +348,37 @@ class PaintManager(QMainWindow):
478
348
  """Vectorized paint operation for better performance."""
479
349
  if self.parent().channel_data[channel] is None:
480
350
  return
481
-
482
- # Use provided parameters or fall back to instance variables
483
351
  slice_idx = slice_idx if slice_idx is not None else self.parent().current_slice
484
352
  brush_size = brush_size if brush_size is not None else getattr(self.parent(), 'brush_size', 5)
485
353
  threed = threed if threed is not None else getattr(self.parent(), 'threed', False)
486
354
  threedthresh = threedthresh if threedthresh is not None else getattr(self.parent(), 'threedthresh', 1)
487
355
 
488
- # Handle 3D painting by recursively calling for each slice
356
+ # Handle 3D painting
489
357
  if threed and threedthresh > 1:
490
358
  half_range = (threedthresh - 1) // 2
491
359
  low = max(0, slice_idx - half_range)
492
360
  high = min(self.parent().channel_data[channel].shape[0] - 1, slice_idx + half_range)
493
361
 
494
-
495
362
  for i in range(low, high + 1):
496
-
497
- # Recursive call for each slice, but with threed=False to avoid infinite recursion
498
363
  self.paint_at_position_vectorized(
499
364
  center_x, center_y,
500
365
  erase=erase,
501
366
  channel=channel,
502
- slice_idx=i, # Paint on slice i
367
+ slice_idx=i,
503
368
  brush_size=brush_size,
504
- threed=False, # Important: turn off 3D for recursive calls
369
+ threed=False,
505
370
  threedthresh=1,
506
371
  foreground=foreground,
507
372
  machine_window=machine_window
508
373
  )
509
-
510
-
511
- return # Exit early, recursive calls handle everything
512
-
513
- # Regular 2D painting (single slice)
374
+ return
514
375
 
515
376
  # Determine paint value
516
377
  if erase:
517
378
  val = 0
518
379
  elif machine_window is None:
519
380
  try:
520
- val = self.parent().min_max[channel][1]
381
+ val = max(1, self.parent().min_max[channel][1])
521
382
  except:
522
383
  val = 255
523
384
  elif foreground:
@@ -535,15 +396,14 @@ class PaintManager(QMainWindow):
535
396
  x_max = min(width, center_x + radius + 1)
536
397
 
537
398
  if y_min >= y_max or x_min >= x_max:
538
- return # No valid region to paint
399
+ return
539
400
 
540
- # Create coordinate grids for the affected region
401
+ # Create coordinate grids
541
402
  y_coords, x_coords = np.mgrid[y_min:y_max, x_min:x_max]
542
403
 
543
- # Calculate distances squared (avoid sqrt for performance)
404
+ # Calculate distances and create mask
544
405
  distances_sq = (x_coords - center_x) ** 2 + (y_coords - center_y) ** 2
545
406
  mask = distances_sq <= radius ** 2
546
407
 
547
- # Paint on this single slice
548
-
408
+ # Paint on this slice
549
409
  self.parent().channel_data[channel][slice_idx][y_min:y_max, x_min:x_max][mask] = val
@@ -222,13 +222,13 @@ def show_identity_network(excel_file_path, node_identities, geometric=False, geo
222
222
  # Node size handling
223
223
  node_dict = {}
224
224
  for node in G.nodes():
225
- try:
225
+ try: #Perhaps remove this
226
226
  if identity_dict[node] == 'Edge':
227
- node_dict[node] = 30
227
+ node_dict[node] = 10
228
228
  else:
229
- node_dict[node] = 100
229
+ node_dict[node] = 10
230
230
  except:
231
- node_dict[node] = 100
231
+ node_dict[node] = 10
232
232
 
233
233
  if geometric:
234
234
  # Handle geometric positioning