meerk40t 0.9.7030__py2.py3-none-any.whl → 0.9.7040__py2.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.
Files changed (79) hide show
  1. meerk40t/balormk/clone_loader.py +3 -2
  2. meerk40t/balormk/controller.py +28 -11
  3. meerk40t/balormk/cylindermod.py +1 -0
  4. meerk40t/balormk/device.py +13 -9
  5. meerk40t/balormk/driver.py +9 -2
  6. meerk40t/balormk/galvo_commands.py +3 -1
  7. meerk40t/balormk/gui/gui.py +6 -0
  8. meerk40t/balormk/livelightjob.py +338 -321
  9. meerk40t/balormk/mock_connection.py +4 -3
  10. meerk40t/balormk/usb_connection.py +11 -2
  11. meerk40t/camera/camera.py +19 -14
  12. meerk40t/camera/gui/camerapanel.py +6 -0
  13. meerk40t/core/cutplan.py +109 -51
  14. meerk40t/core/elements/element_treeops.py +435 -140
  15. meerk40t/core/elements/elements.py +100 -9
  16. meerk40t/core/elements/shapes.py +259 -39
  17. meerk40t/core/elements/tree_commands.py +10 -5
  18. meerk40t/core/node/elem_ellipse.py +18 -8
  19. meerk40t/core/node/elem_image.py +51 -19
  20. meerk40t/core/node/elem_line.py +18 -8
  21. meerk40t/core/node/elem_path.py +18 -8
  22. meerk40t/core/node/elem_point.py +10 -4
  23. meerk40t/core/node/elem_polyline.py +19 -11
  24. meerk40t/core/node/elem_rect.py +18 -8
  25. meerk40t/core/node/elem_text.py +11 -5
  26. meerk40t/core/node/filenode.py +2 -8
  27. meerk40t/core/node/groupnode.py +11 -11
  28. meerk40t/core/node/image_processed.py +11 -5
  29. meerk40t/core/node/image_raster.py +11 -5
  30. meerk40t/core/node/node.py +64 -16
  31. meerk40t/core/node/refnode.py +2 -1
  32. meerk40t/core/svg_io.py +91 -34
  33. meerk40t/device/dummydevice.py +7 -1
  34. meerk40t/extra/vtracer.py +222 -0
  35. meerk40t/grbl/device.py +81 -8
  36. meerk40t/gui/about.py +20 -0
  37. meerk40t/gui/devicepanel.py +20 -16
  38. meerk40t/gui/gui_mixins.py +4 -0
  39. meerk40t/gui/icons.py +330 -253
  40. meerk40t/gui/laserpanel.py +8 -3
  41. meerk40t/gui/laserrender.py +41 -21
  42. meerk40t/gui/magnetoptions.py +158 -65
  43. meerk40t/gui/materialtest.py +229 -39
  44. meerk40t/gui/navigationpanels.py +229 -24
  45. meerk40t/gui/propertypanels/hatchproperty.py +2 -0
  46. meerk40t/gui/propertypanels/imageproperty.py +160 -106
  47. meerk40t/gui/ribbon.py +6 -1
  48. meerk40t/gui/scenewidgets/gridwidget.py +29 -32
  49. meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
  50. meerk40t/gui/simulation.py +75 -77
  51. meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
  52. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  53. meerk40t/gui/tips.py +15 -1
  54. meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
  55. meerk40t/gui/wxmmain.py +242 -114
  56. meerk40t/gui/wxmscene.py +107 -24
  57. meerk40t/gui/wxmtree.py +4 -2
  58. meerk40t/gui/wxutils.py +60 -15
  59. meerk40t/image/imagetools.py +129 -65
  60. meerk40t/internal_plugins.py +4 -0
  61. meerk40t/kernel/kernel.py +39 -18
  62. meerk40t/kernel/settings.py +28 -9
  63. meerk40t/lihuiyu/device.py +24 -12
  64. meerk40t/main.py +1 -1
  65. meerk40t/moshi/device.py +20 -6
  66. meerk40t/network/console_server.py +22 -6
  67. meerk40t/newly/device.py +10 -3
  68. meerk40t/newly/gui/gui.py +10 -0
  69. meerk40t/ruida/device.py +22 -2
  70. meerk40t/ruida/loader.py +6 -3
  71. meerk40t/tools/geomstr.py +193 -125
  72. meerk40t/tools/rasterplotter.py +179 -93
  73. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/METADATA +1 -1
  74. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/RECORD +79 -78
  75. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/LICENSE +0 -0
  76. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/WHEEL +0 -0
  77. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/entry_points.txt +0 -0
  78. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/top_level.txt +0 -0
  79. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/zip-safe +0 -0
@@ -19,10 +19,7 @@ class GridWidget(Widget):
19
19
 
20
20
  def __init__(self, scene, name=None, suppress_labels=False):
21
21
  Widget.__init__(self, scene, all=True)
22
- if name is None:
23
- self.name = "Standard"
24
- else:
25
- self.name = name
22
+ self.name = "Standard" if name is None else name
26
23
  self.primary_grid_lines = None
27
24
  self.secondary_grid_lines = None
28
25
  self.background = None
@@ -157,28 +154,30 @@ class GridWidget(Widget):
157
154
  # Primary grid
158
155
  # We could be way too high
159
156
  start_x = self.zero_x
160
- while start_x - self.primary_tick_length_x > self.min_x:
161
- start_x -= self.primary_tick_length_x
157
+ dx = abs(self.primary_tick_length_x)
158
+ dy = abs(self.primary_tick_length_y)
159
+ while start_x - dx > self.min_x:
160
+ start_x -= dx
162
161
  start_y = self.zero_y
163
- while start_y - self.primary_tick_length_y > self.min_y:
164
- start_y -= self.primary_tick_length_y
162
+ while start_y - dy > self.min_y:
163
+ start_y -= dy
165
164
  # But we could be way too low, too
166
165
  while start_x < self.min_x:
167
- start_x += self.primary_tick_length_x
166
+ start_x += dx
168
167
  while start_y < self.min_y:
169
- start_y += self.primary_tick_length_y
168
+ start_y += dy
170
169
 
171
170
  x = start_x
172
171
  while x <= self.max_x:
173
172
  starts.append((x, self.min_y))
174
173
  ends.append((x, self.max_y))
175
- x += self.primary_tick_length_x
174
+ x += dx
176
175
 
177
176
  y = start_y
178
177
  while y <= self.max_y:
179
178
  starts.append((self.min_x, y))
180
179
  ends.append((self.max_x, y))
181
- y += self.primary_tick_length_y
180
+ y += dy
182
181
  self.primary_grid_lines = starts, ends
183
182
 
184
183
  def _calc_secondary_grid_lines(self):
@@ -188,28 +187,30 @@ class GridWidget(Widget):
188
187
  # Secondary grid
189
188
  # We could be way too high
190
189
  start_x = self.zero_x
191
- while start_x - self.secondary_tick_length_x > self.min_x:
192
- start_x -= self.secondary_tick_length_x
190
+ dx = abs(self.secondary_tick_length_x)
191
+ dy = abs(self.secondary_tick_length_y)
192
+ while start_x - dx > self.min_x:
193
+ start_x -= dx
193
194
  start_y = self.zero_y
194
- while start_y - self.secondary_tick_length_y > self.min_y:
195
- start_y -= self.secondary_tick_length_y
195
+ while start_y - dy > self.min_y:
196
+ start_y -= dy
196
197
  # But we could be way too low, too
197
198
  while start_x < self.min_x:
198
- start_x += self.secondary_tick_length_x
199
+ start_x += dx
199
200
  while start_y < self.min_y:
200
- start_y += self.secondary_tick_length_y
201
+ start_y += dy
201
202
 
202
203
  x = start_x
203
204
  while x <= self.max_x:
204
205
  starts2.append((x, self.min_y))
205
206
  ends2.append((x, self.max_y))
206
- x += self.secondary_tick_length_x
207
+ x += dx
207
208
 
208
209
  y = start_y
209
210
  while y <= self.max_y:
210
211
  starts2.append((self.min_x, y))
211
212
  ends2.append((self.max_x, y))
212
- y += self.secondary_tick_length_y
213
+ y += dy
213
214
  self.secondary_grid_lines = starts2, ends2
214
215
 
215
216
  def calculate_grid_lines(self):
@@ -346,7 +347,7 @@ class GridWidget(Widget):
346
347
  (self.min_x, self.circular_grid_center_y),
347
348
  (self.max_x, self.circular_grid_center_y),
348
349
  )
349
- for i, pt in enumerate(test_points):
350
+ for pt in test_points:
350
351
  dx = pt[0] - self.circular_grid_center_x
351
352
  dy = pt[1] - self.circular_grid_center_y
352
353
  r = sqrt(dx * dx + dy * dy)
@@ -525,10 +526,7 @@ class GridWidget(Widget):
525
526
  c_angle = r_angle
526
527
  while c_angle > tau:
527
528
  c_angle -= tau
528
- if i % 2 == 0:
529
- r = 0
530
- else:
531
- r = r_fourth
529
+ r = 0 if i % 2 == 0 else r_fourth
532
530
  while r < self.min_radius:
533
531
  r += tick_length
534
532
 
@@ -634,8 +632,8 @@ class GridWidget(Widget):
634
632
  def _draw_grid_primary(self, gc):
635
633
  starts, ends = self.primary_grid_lines
636
634
  gc.SetPen(self.primary_grid_line_pen)
637
- grid_path = gc.CreatePath()
638
635
  if starts and ends:
636
+ grid_path = gc.CreatePath()
639
637
  for i in range(len(starts)):
640
638
  sx = starts[i][0]
641
639
  sy = starts[i][1]
@@ -648,8 +646,8 @@ class GridWidget(Widget):
648
646
  def _draw_grid_secondary(self, gc):
649
647
  starts2, ends2 = self.secondary_grid_lines
650
648
  gc.SetPen(self.secondary_grid_line_pen)
651
- grid_path = gc.CreatePath()
652
649
  if starts2 and ends2:
650
+ grid_path = gc.CreatePath()
653
651
  for i in range(len(starts2)):
654
652
  sx = starts2[i][0]
655
653
  sy = starts2[i][1]
@@ -667,8 +665,8 @@ class GridWidget(Widget):
667
665
  u_height = float(self.scene.context.device.view.unit_height)
668
666
  gc.Clip(0, 0, u_width, u_height)
669
667
  # siz = sqrt(u_width * u_width + u_height * u_height)
670
- sox = self.circular_grid_center_x / u_width
671
- soy = self.circular_grid_center_y / u_height
668
+ # sox = self.circular_grid_center_x / u_width
669
+ # soy = self.circular_grid_center_y / u_height
672
670
  step = self.primary_tick_length_x
673
671
  # factor = max(2 * (1 - sox), 2 * (1 - soy))
674
672
  # Initially I drew a complete circle, which is a waste in most situations,
@@ -699,8 +697,7 @@ class GridWidget(Widget):
699
697
  radials_start = []
700
698
  radials_end = []
701
699
  fsize = 10 / self.scene.widget_root.scene_widget.matrix.value_scale_x()
702
- if fsize < 1.0:
703
- fsize = 1.0 # Mac does not allow values lower than 1.
700
+ fsize = max(fsize, 1.0) # Mac does not allow values lower than 1.
704
701
  try:
705
702
  font = wx.Font(
706
703
  fsize,
@@ -771,7 +768,7 @@ class GridWidget(Widget):
771
768
  )
772
769
  r_angle += tau / segments
773
770
  i += 1
774
- if len(radials_start) > 0:
771
+ if radials_start:
775
772
  gc.StrokeLineSegments(radials_start, radials_end)
776
773
  gc.ResetClip()
777
774
 
@@ -16,7 +16,7 @@ from meerk40t.gui.scene.scene import (
16
16
  RESPONSE_DROP,
17
17
  )
18
18
  from meerk40t.gui.scene.widget import Widget
19
- from meerk40t.gui.wxutils import get_matrix_scale, get_gc_full_scale
19
+ from meerk40t.gui.wxutils import dip_size, get_gc_full_scale, get_matrix_scale
20
20
  from meerk40t.tools.geomstr import NON_GEOMETRY_TYPES
21
21
 
22
22
 
@@ -80,6 +80,7 @@ class RectSelectWidget(Widget):
80
80
  self.scene.context.setting(bool, "delayed_move", True)
81
81
  self.mode = "select"
82
82
  self.can_drag_move = False
83
+ self.magnification = dip_size(scene.gui, 100, 100)[1] / 100
83
84
 
84
85
  def hit(self):
85
86
  return HITCHAIN_HIT
@@ -93,15 +94,9 @@ class RectSelectWidget(Widget):
93
94
  ex = self.end_location[0]
94
95
  ey = self.end_location[1]
95
96
  if sx <= ex:
96
- if sy <= ey:
97
- return 0
98
- else:
99
- return 1
97
+ return 0 if sy <= ey else 1
100
98
  else:
101
- if sy <= ey:
102
- return 3
103
- else:
104
- return 2
99
+ return 3 if sy <= ey else 2
105
100
 
106
101
  def rect_select(self, elements, sx, sy, ex, ey):
107
102
  sector = self.sector
@@ -190,6 +185,30 @@ class RectSelectWidget(Widget):
190
185
  x = x[0]
191
186
  return box[0] <= x <= box[2] and box[1] <= y <= box[3]
192
187
 
188
+ def shortest_distance(p1, p2, tuplemode):
189
+ """
190
+ Calculates the shortest distance between two arrays of 2-dimensional points.
191
+ """
192
+ try:
193
+ # Calculate the Euclidean distance between each point in p1 and p2
194
+ if tuplemode:
195
+ # For an array of tuples:
196
+ dist = np.sqrt(np.sum((p1[:, np.newaxis] - p2) ** 2, axis=2))
197
+ else:
198
+ # For an array of complex numbers
199
+ dist = np.abs(p1[:, np.newaxis] - p2[np.newaxis, :])
200
+
201
+ # Find the minimum distance and its corresponding indices
202
+ min_dist = np.min(dist)
203
+ if np.isnan(min_dist):
204
+ return None, 0, 0
205
+ min_indices = np.argwhere(dist == min_dist)
206
+
207
+ # Return the coordinates of the two points
208
+ return min_dist, p1[min_indices[0][0]], p2[min_indices[0][1]]
209
+ except Exception: # out of memory eg
210
+ return None, None, None
211
+
193
212
  def move_to(dx, dy):
194
213
  if dx == 0 and dy == 0:
195
214
  return
@@ -217,192 +236,21 @@ class RectSelectWidget(Widget):
217
236
  )
218
237
  self.scene.request_refresh()
219
238
 
220
- if modifiers is not None:
221
- self.modifiers = modifiers
222
-
223
- elements = self.scene.context.elements
224
- if event_type == "leftdown":
239
+ def check_leftdown(space_pos):
225
240
  self.mouse_down_time = perf_counter()
226
241
  self.mode = "unclear"
227
242
  self.start_location = space_pos
228
243
  self.end_location = space_pos
229
244
  if contains(self.scene.context.elements._emphasized_bounds, space_pos):
230
245
  self.can_drag_move = True
231
- # print ("RectSelect consumed leftdown")
232
- return RESPONSE_CONSUME
233
- elif event_type == "leftclick":
246
+
247
+ def check_click(space_pos):
234
248
  # That's too fast
235
249
  # still chaining though
236
250
  self.scene.request_refresh()
237
251
  self.reset()
238
- return RESPONSE_CHAIN
239
- elif event_type == "leftup":
240
- if self.mode == "select":
241
- if self.start_location is None:
242
- return RESPONSE_CHAIN
243
- _ = self.scene.context._
244
- self.update_statusmsg(_("Status"))
245
- elements.validate_selected_area()
246
- sx = min(self.start_location[0], self.end_location[0])
247
- sy = min(self.start_location[1], self.end_location[1])
248
- ex = max(self.start_location[0], self.end_location[0])
249
- ey = max(self.start_location[1], self.end_location[1])
250
- self.rect_select(elements, sx, sy, ex, ey)
251
-
252
- self.scene.request_refresh()
253
- self.scene.context.signal("select_emphasized_tree", 0)
254
- else:
255
-
256
- def shortest_distance(p1, p2, tuplemode):
257
- """
258
- Calculates the shortest distance between two arrays of 2-dimensional points.
259
- """
260
- try:
261
- # Calculate the Euclidean distance between each point in p1 and p2
262
- if tuplemode:
263
- # For an array of tuples:
264
- dist = np.sqrt(np.sum((p1[:, np.newaxis] - p2) ** 2, axis=2))
265
- else:
266
- # For an array of complex numbers
267
- dist = np.abs(p1[:, np.newaxis] - p2[np.newaxis, :])
268
-
269
- # Find the minimum distance and its corresponding indices
270
- min_dist = np.min(dist)
271
- if np.isnan(min_dist):
272
- return None, 0, 0
273
- min_indices = np.argwhere(dist == min_dist)
274
-
275
- # Return the coordinates of the two points
276
- return min_dist, p1[min_indices[0][0]], p2[min_indices[0][1]]
277
- except Exception: # out of memory eg
278
- return None, None, None
279
-
280
- b = self.scene.context.elements._emphasized_bounds
281
- if b is None:
282
- b = self.scene.context.elements.selected_area()
283
- matrix = self.scene.widget_root.scene_widget.matrix
284
- did_snap_to_point = False
285
- if (
286
- self.scene.context.snap_points
287
- and "shift" not in modifiers
288
- and b is not None
289
- ):
290
- gap = self.scene.context.action_attract_len / get_matrix_scale(matrix)
291
- # We gather all points of non-selected elements,
292
- # but only those that lie within the boundaries
293
- # of the selected area
294
- # We compare every point of the selected elements
295
- # with the points of the non-selected elements (provided they
296
- # lie within the selection area plus boundary) and look for
297
- # the closest distance.
298
-
299
- # t1 = perf_counter()
300
- other_points = []
301
- selected_points = []
302
- for e in self.scene.context.elements.elems():
303
- if e.emphasized:
304
- target = selected_points
305
- else:
306
- target = other_points
307
- if not hasattr(e, "as_geometry"):
308
- continue
309
- geom = e.as_geometry()
310
- last = None
311
- for seg in geom.segments[: geom.index]:
312
- start = seg[0]
313
- seg_type = geom._segtype(seg)
314
- end = seg[4]
315
- if seg_type in NON_GEOMETRY_TYPES:
316
- continue
317
- if np.isnan(start) or np.isnan(end):
318
- print (f"Strange, encountered within rectselect a segment with type: {seg_type} and start={start}, end={end} - coming from element type {e.type}\nPlease inform the developers")
319
- continue
320
- if start != last:
321
- xx = start.real
322
- yy = start.imag
323
- ignore = (
324
- xx < b[0] - gap
325
- or xx > b[2] + gap
326
- or yy < b[1] - gap
327
- or yy > b[3] + gap
328
- )
329
- if not ignore:
330
- target.append(start)
331
- xx = end.real
332
- yy = end.imag
333
- ignore = (
334
- xx < b[0] - gap
335
- or xx > b[2] + gap
336
- or yy < b[1] - gap
337
- or yy > b[3] + gap
338
- )
339
- if not ignore:
340
- target.append(end)
341
- last = end
342
- # t2 = perf_counter()
343
- if (
344
- other_points is not None
345
- and selected_points is not None
346
- and len(other_points) > 0
347
- and len(selected_points) > 0
348
- ):
349
- np_other = np.asarray(other_points)
350
- np_selected = np.asarray(selected_points)
351
- dist, pt1, pt2 = shortest_distance(np_other, np_selected, False)
352
-
353
- if dist is not None and dist < gap:
354
- did_snap_to_point = True
355
- dx = pt1.real - pt2.real
356
- dy = pt1.imag - pt2.imag
357
- move_to(dx, dy)
358
- # Get new value
359
- b = self.scene.context.elements._emphasized_bounds
360
- # t3 = perf_counter()
361
- # print (f"Snap, compared {len(selected_points)} pts to {len(other_points)} pts. Total time: {t3-t1:.2f}sec, Generation: {t2-t1:.2f}sec, shortest: {t3-t2:.2f}sec")
362
- if (
363
- self.scene.context.snap_grid
364
- and "shift" not in modifiers
365
- and b is not None
366
- and not did_snap_to_point
367
- ):
368
- # t1 = perf_counter()
369
- gap = self.scene.context.grid_attract_len / get_matrix_scale(matrix)
370
- # Check for corner points + center:
371
- selected_points = (
372
- (b[0], b[1]),
373
- (b[2], b[1]),
374
- (b[0], b[3]),
375
- (b[2], b[3]),
376
- ((b[0] + b[2]) / 2, (b[1] + b[3]) / 2),
377
- )
378
- other_points = self.scene.pane.grid.grid_points
379
- if (
380
- other_points is not None
381
- and selected_points is not None
382
- and len(other_points) > 0
383
- and len(selected_points) > 0
384
- ):
385
- np_other = np.asarray(other_points)
386
- np_selected = np.asarray(selected_points)
387
- dist, pt1, pt2 = shortest_distance(np_other, np_selected, True)
388
- if dist is not None and dist < gap:
389
- # did_snap_to_point = True
390
- dx = pt1[0] - pt2[0]
391
- dy = pt1[1] - pt2[1]
392
- move_to(dx, dy)
393
- # Get new value
394
- b = self.scene.context.elements._emphasized_bounds
395
-
396
- # t2 = perf_counter()
397
- # print (f"Corner-points, compared {len(selected_points)} pts to {len(other_points)} pts. Total time: {t2-t1:.2f}sec")
398
- # Even then magnets win!
399
- dx, dy = self.scene.pane.revised_magnet_bound(b)
400
- move_to(dx, dy)
401
-
402
- self.reset()
403
252
 
404
- return RESPONSE_CONSUME
405
- elif event_type == "move":
253
+ def check_move(space_pos):
406
254
  if self.mode == "unclear":
407
255
  current_time = perf_counter()
408
256
  # print (f"{current_time - self.mouse_down_time:.2f}sec.")
@@ -421,6 +269,155 @@ class RectSelectWidget(Widget):
421
269
  dy = space_pos[5]
422
270
  move_to(dx, dy)
423
271
 
272
+ def check_leftup_select(space_pos):
273
+ _ = self.scene.context._
274
+ self.update_statusmsg(_("Status"))
275
+ elements.validate_selected_area()
276
+ sx = min(self.start_location[0], self.end_location[0])
277
+ sy = min(self.start_location[1], self.end_location[1])
278
+ ex = max(self.start_location[0], self.end_location[0])
279
+ ey = max(self.start_location[1], self.end_location[1])
280
+ self.rect_select(elements, sx, sy, ex, ey)
281
+
282
+ self.scene.request_refresh()
283
+ self.scene.context.signal("select_emphasized_tree", 0)
284
+
285
+ def check_leftup_move(space_pos):
286
+ b = self.scene.context.elements._emphasized_bounds
287
+ if b is None:
288
+ b = self.scene.context.elements.selected_area()
289
+ matrix = self.scene.widget_root.scene_widget.matrix
290
+ did_snap_to_point = False
291
+ if (
292
+ self.scene.context.snap_points
293
+ and "shift" not in modifiers
294
+ and b is not None
295
+ ):
296
+ gap = self.scene.context.action_attract_len / get_matrix_scale(matrix)
297
+ # We gather all points of non-selected elements,
298
+ # but only those that lie within the boundaries
299
+ # of the selected area
300
+ # We compare every point of the selected elements
301
+ # with the points of the non-selected elements (provided they
302
+ # lie within the selection area plus boundary) and look for
303
+ # the closest distance.
304
+
305
+ # t1 = perf_counter()
306
+ other_points = []
307
+ selected_points = []
308
+ for e in self.scene.context.elements.elems():
309
+ target = selected_points if e.emphasized else other_points
310
+ if not hasattr(e, "as_geometry"):
311
+ continue
312
+ geom = e.as_geometry()
313
+ last = None
314
+ for seg in geom.segments[: geom.index]:
315
+ start = seg[0]
316
+ seg_type = geom._segtype(seg)
317
+ end = seg[4]
318
+ if seg_type in NON_GEOMETRY_TYPES:
319
+ continue
320
+ if np.isnan(start) or np.isnan(end):
321
+ print(
322
+ f"Strange, encountered within rectselect a segment with type: {seg_type} and start={start}, end={end} - coming from element type {e.type}\nPlease inform the developers"
323
+ )
324
+ continue
325
+ if start != last:
326
+ xx = start.real
327
+ yy = start.imag
328
+ ignore = (
329
+ xx < b[0] - gap
330
+ or xx > b[2] + gap
331
+ or yy < b[1] - gap
332
+ or yy > b[3] + gap
333
+ )
334
+ if not ignore:
335
+ target.append(start)
336
+ xx = end.real
337
+ yy = end.imag
338
+ ignore = (
339
+ xx < b[0] - gap
340
+ or xx > b[2] + gap
341
+ or yy < b[1] - gap
342
+ or yy > b[3] + gap
343
+ )
344
+ if not ignore:
345
+ target.append(end)
346
+ last = end
347
+ # t2 = perf_counter()
348
+ if other_points and selected_points:
349
+ np_other = np.asarray(other_points)
350
+ np_selected = np.asarray(selected_points)
351
+ dist, pt1, pt2 = shortest_distance(np_other, np_selected, False)
352
+
353
+ if dist is not None and dist < gap:
354
+ did_snap_to_point = True
355
+ dx = pt1.real - pt2.real
356
+ dy = pt1.imag - pt2.imag
357
+ move_to(dx, dy)
358
+ # Get new value
359
+ b = self.scene.context.elements._emphasized_bounds
360
+ # t3 = perf_counter()
361
+ # print (f"Snap, compared {len(selected_points)} pts to {len(other_points)} pts. Total time: {t3-t1:.2f}sec, Generation: {t2-t1:.2f}sec, shortest: {t3-t2:.2f}sec")
362
+ if (
363
+ self.scene.context.snap_grid
364
+ and "shift" not in modifiers
365
+ and b is not None
366
+ and not did_snap_to_point
367
+ ):
368
+ # t1 = perf_counter()
369
+ gap = self.scene.context.grid_attract_len / get_matrix_scale(matrix)
370
+ # Check for corner points + center:
371
+ selected_points = (
372
+ (b[0], b[1]),
373
+ (b[2], b[1]),
374
+ (b[0], b[3]),
375
+ (b[2], b[3]),
376
+ ((b[0] + b[2]) / 2, (b[1] + b[3]) / 2),
377
+ )
378
+ other_points = self.scene.pane.grid.grid_points
379
+ if other_points and selected_points:
380
+ np_other = np.asarray(other_points)
381
+ np_selected = np.asarray(selected_points)
382
+ dist, pt1, pt2 = shortest_distance(np_other, np_selected, True)
383
+ if dist is not None and dist < gap:
384
+ # did_snap_to_point = True
385
+ dx = pt1[0] - pt2[0]
386
+ dy = pt1[1] - pt2[1]
387
+ move_to(dx, dy)
388
+ # Get new value
389
+ b = self.scene.context.elements._emphasized_bounds
390
+
391
+ # t2 = perf_counter()
392
+ # print (f"Corner-points, compared {len(selected_points)} pts to {len(other_points)} pts. Total time: {t2-t1:.2f}sec")
393
+ # Even then magnets win!
394
+ dx, dy = self.scene.pane.revised_magnet_bound(b)
395
+ move_to(dx, dy)
396
+
397
+ if modifiers is not None:
398
+ self.modifiers = modifiers
399
+
400
+ elements = self.scene.context.elements
401
+ if event_type == "leftdown":
402
+ check_leftdown(space_pos)
403
+ # print ("RectSelect consumed leftdown")
404
+ return RESPONSE_CONSUME
405
+ elif event_type == "leftclick":
406
+ check_click(space_pos)
407
+ return RESPONSE_CHAIN
408
+ elif event_type == "leftup":
409
+ if self.mode == "select":
410
+ if self.start_location is None:
411
+ return RESPONSE_CHAIN
412
+ check_leftup_select(space_pos)
413
+ else:
414
+ check_leftup_move(space_pos)
415
+
416
+ self.reset()
417
+
418
+ return RESPONSE_CONSUME
419
+ elif event_type == "move":
420
+ check_move(space_pos)
424
421
  return RESPONSE_CONSUME
425
422
  elif event_type == "lost":
426
423
  self.reset()
@@ -476,25 +473,26 @@ class RectSelectWidget(Widget):
476
473
  except TypeError:
477
474
  self.selection_pen.SetWidth(int(linewidth))
478
475
  gc.SetPen(self.selection_pen)
479
- delta_X = 15.0
480
- delta_Y = 15.0
476
+ delta_X = 15.0 * self.magnification
477
+ delta_Y = 15.0 * self.magnification
481
478
  x0 *= sx
482
479
  x1 *= sx
483
480
  y0 *= sx
484
481
  y1 *= sx
485
- if abs(x1 - x0) > delta_X and abs(y1 - y0) > delta_Y: # Don't draw if too tiny
482
+ if abs(x1 - x0) > delta_X and abs(y1 - y0) > delta_Y: # Don't draw if too tiny
486
483
  # Draw tiny '+' in corner of pointer
487
484
  x_signum = +1 * delta_X if x0 < x1 else -1 * delta_X
488
- y_signum = +1 * delta_Y if y0 < y1 else -1 * delta_X
485
+ y_signum = +1 * delta_Y if y0 < y1 else -1 * delta_Y
489
486
  ax1 = x1 - x_signum
490
487
  ay1 = y1 - y_signum
491
488
 
492
489
  gc.SetPen(self.selection_pen)
493
490
  gc.StrokeLine(ax1, y1, ax1, ay1)
494
491
  gc.StrokeLine(ax1, ay1, x1, ay1)
495
- font_size = 10.0
496
- if font_size < 1.0:
497
- font_size = 1.0
492
+ font_size = 10.0 * self.magnification
493
+ font_size = max(
494
+ font_size, 1.0
495
+ ) # Darwin issues a TypeError if font size is smaller than 1
498
496
  try:
499
497
  font = wx.Font(
500
498
  font_size,
@@ -515,7 +513,7 @@ class RectSelectWidget(Widget):
515
513
  symbol, (ax1 + x1) / 2 - t_width / 2, (ay1 + y1) / 2 - t_height / 2
516
514
  )
517
515
  if (
518
- abs(x1 - x0) > 2 * delta_X and abs(y1 - y0) > 2 * delta_Y
516
+ abs(x1 - x0) > 2 * delta_X and abs(y1 - y0) > 2 * delta_Y
519
517
  ): # Don't draw if too tiny
520
518
  # Draw second symbol at origin
521
519
  ax1 = x0 + x_signum