meerk40t 0.9.7030__py2.py3-none-any.whl → 0.9.7050__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.
- meerk40t/balormk/clone_loader.py +3 -2
- meerk40t/balormk/controller.py +38 -13
- meerk40t/balormk/cylindermod.py +1 -0
- meerk40t/balormk/device.py +13 -9
- meerk40t/balormk/driver.py +9 -2
- meerk40t/balormk/galvo_commands.py +3 -1
- meerk40t/balormk/gui/gui.py +6 -0
- meerk40t/balormk/livelightjob.py +338 -321
- meerk40t/balormk/mock_connection.py +4 -3
- meerk40t/balormk/usb_connection.py +11 -2
- meerk40t/camera/camera.py +19 -14
- meerk40t/camera/gui/camerapanel.py +6 -0
- meerk40t/core/cutplan.py +101 -78
- meerk40t/core/elements/element_treeops.py +435 -140
- meerk40t/core/elements/elements.py +100 -9
- meerk40t/core/elements/shapes.py +259 -72
- meerk40t/core/elements/tree_commands.py +10 -5
- meerk40t/core/node/blobnode.py +19 -4
- meerk40t/core/node/elem_ellipse.py +18 -8
- meerk40t/core/node/elem_image.py +51 -19
- meerk40t/core/node/elem_line.py +18 -8
- meerk40t/core/node/elem_path.py +18 -8
- meerk40t/core/node/elem_point.py +10 -4
- meerk40t/core/node/elem_polyline.py +19 -11
- meerk40t/core/node/elem_rect.py +18 -8
- meerk40t/core/node/elem_text.py +11 -5
- meerk40t/core/node/filenode.py +2 -8
- meerk40t/core/node/groupnode.py +11 -11
- meerk40t/core/node/image_processed.py +11 -5
- meerk40t/core/node/image_raster.py +11 -5
- meerk40t/core/node/node.py +64 -16
- meerk40t/core/node/refnode.py +2 -1
- meerk40t/core/planner.py +25 -11
- meerk40t/core/svg_io.py +91 -34
- meerk40t/device/dummydevice.py +7 -1
- meerk40t/extra/vtracer.py +222 -0
- meerk40t/grbl/device.py +96 -9
- meerk40t/grbl/driver.py +15 -5
- meerk40t/gui/about.py +20 -0
- meerk40t/gui/devicepanel.py +20 -16
- meerk40t/gui/gui_mixins.py +4 -0
- meerk40t/gui/icons.py +330 -253
- meerk40t/gui/laserpanel.py +27 -3
- meerk40t/gui/laserrender.py +41 -21
- meerk40t/gui/magnetoptions.py +158 -65
- meerk40t/gui/materialtest.py +569 -310
- meerk40t/gui/navigationpanels.py +229 -24
- meerk40t/gui/propertypanels/hatchproperty.py +2 -0
- meerk40t/gui/propertypanels/imageproperty.py +160 -106
- meerk40t/gui/propertypanels/wobbleproperty.py +6 -2
- meerk40t/gui/ribbon.py +6 -1
- meerk40t/gui/scenewidgets/gridwidget.py +29 -32
- meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
- meerk40t/gui/simulation.py +75 -77
- meerk40t/gui/spoolerpanel.py +27 -7
- meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
- meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
- meerk40t/gui/tips.py +15 -1
- meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
- meerk40t/gui/wxmmain.py +242 -114
- meerk40t/gui/wxmscene.py +107 -24
- meerk40t/gui/wxmtree.py +4 -2
- meerk40t/gui/wxutils.py +286 -15
- meerk40t/image/imagetools.py +129 -65
- meerk40t/internal_plugins.py +4 -0
- meerk40t/kernel/kernel.py +67 -18
- meerk40t/kernel/settings.py +28 -9
- meerk40t/lihuiyu/device.py +24 -12
- meerk40t/main.py +14 -9
- meerk40t/moshi/device.py +20 -6
- meerk40t/network/console_server.py +22 -6
- meerk40t/newly/device.py +10 -3
- meerk40t/newly/gui/gui.py +10 -0
- meerk40t/ruida/device.py +22 -2
- meerk40t/ruida/loader.py +9 -4
- meerk40t/ruida/rdjob.py +48 -8
- meerk40t/tools/geomstr.py +240 -123
- meerk40t/tools/rasterplotter.py +185 -94
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/RECORD +85 -84
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/zip-safe +0 -0
meerk40t/core/elements/shapes.py
CHANGED
@@ -66,7 +66,7 @@ from meerk40t.svgelements import (
|
|
66
66
|
Polygon,
|
67
67
|
Polyline,
|
68
68
|
)
|
69
|
-
from meerk40t.tools.geomstr import Geomstr, stitch_geometries
|
69
|
+
from meerk40t.tools.geomstr import Geomstr, stitch_geometries, stitcheable_nodes
|
70
70
|
|
71
71
|
|
72
72
|
def plugin(kernel, lifecycle=None):
|
@@ -181,12 +181,18 @@ def init_commands(kernel):
|
|
181
181
|
@self.console_argument("x_pos", type=Length, help=_("X-coordinate of center"))
|
182
182
|
@self.console_argument("y_pos", type=Length, help=_("Y-coordinate of center"))
|
183
183
|
@self.console_argument("rx", type=Length, help=_("Primary radius of ellipse"))
|
184
|
-
@self.console_argument(
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
184
|
+
@self.console_argument(
|
185
|
+
"ry",
|
186
|
+
type=Length,
|
187
|
+
help=_("Secondary radius of ellipse (default equal to primary radius=circle)"),
|
188
|
+
)
|
189
|
+
@self.console_argument(
|
190
|
+
"start_angle", type=Angle, help=_("Start angle of arc (default 0°)")
|
189
191
|
)
|
192
|
+
@self.console_argument(
|
193
|
+
"end_angle", type=Angle, help=_("End angle of arc (default 360°)")
|
194
|
+
)
|
195
|
+
@self.console_option("rotation", "r", type=Angle, help=_("Rotation of arc"))
|
190
196
|
@self.console_command(
|
191
197
|
"arc",
|
192
198
|
help=_("arc <cx> <cy> <rx> <ry> <start> <end>"),
|
@@ -195,7 +201,18 @@ def init_commands(kernel):
|
|
195
201
|
all_arguments_required=True,
|
196
202
|
)
|
197
203
|
def element_arc(
|
198
|
-
channel,
|
204
|
+
channel,
|
205
|
+
_,
|
206
|
+
x_pos,
|
207
|
+
y_pos,
|
208
|
+
rx,
|
209
|
+
ry=None,
|
210
|
+
start_angle=None,
|
211
|
+
end_angle=None,
|
212
|
+
rotation=None,
|
213
|
+
data=None,
|
214
|
+
post=None,
|
215
|
+
**kwargs,
|
199
216
|
):
|
200
217
|
if start_angle is None:
|
201
218
|
start_angle = Angle("0deg")
|
@@ -211,7 +228,7 @@ def init_commands(kernel):
|
|
211
228
|
cy = float(y_pos)
|
212
229
|
geom = Geomstr()
|
213
230
|
geom.arc_as_cubics(
|
214
|
-
start_t=start_angle.radians,
|
231
|
+
start_t=start_angle.radians,
|
215
232
|
end_t=end_angle.radians,
|
216
233
|
rx=rx_val,
|
217
234
|
ry=ry_val,
|
@@ -656,7 +673,83 @@ def init_commands(kernel):
|
|
656
673
|
height=500,
|
657
674
|
)
|
658
675
|
except Exception:
|
659
|
-
pass
|
676
|
+
pass # Not relevant...
|
677
|
+
|
678
|
+
@self.console_argument("prop", type=str, help=_("property to get"))
|
679
|
+
@self.console_command(
|
680
|
+
"property-get",
|
681
|
+
help=_("get property value"),
|
682
|
+
input_type=(
|
683
|
+
None,
|
684
|
+
"elements",
|
685
|
+
),
|
686
|
+
output_type="elements",
|
687
|
+
)
|
688
|
+
def element_property_get(command, channel, _, data, post=None, prop=None, **kwargs):
|
689
|
+
def possible_representation(node, prop) -> str:
|
690
|
+
def simple_rep(prop, value):
|
691
|
+
if isinstance(value, (float, int)) and prop in (
|
692
|
+
"x",
|
693
|
+
"y",
|
694
|
+
"cx",
|
695
|
+
"cy",
|
696
|
+
"r",
|
697
|
+
"rx",
|
698
|
+
"ry",
|
699
|
+
):
|
700
|
+
try:
|
701
|
+
s = Length(value).length_mm
|
702
|
+
return s
|
703
|
+
except ValueError:
|
704
|
+
pass
|
705
|
+
elif isinstance(value, Length):
|
706
|
+
return value.length_mm
|
707
|
+
elif isinstance(value, Angle):
|
708
|
+
return value.angle_degrees
|
709
|
+
elif isinstance(value, str):
|
710
|
+
return f"'{value}'"
|
711
|
+
return repr(value)
|
712
|
+
|
713
|
+
value = getattr(node, prop, None)
|
714
|
+
if isinstance(value, (str, float, int)):
|
715
|
+
return simple_rep(prop, value)
|
716
|
+
elif isinstance(value, (tuple, list)):
|
717
|
+
stuff = []
|
718
|
+
for v in value:
|
719
|
+
stuff.append(simple_rep("x", v))
|
720
|
+
return ",".join(stuff)
|
721
|
+
return simple_rep(prop, value)
|
722
|
+
|
723
|
+
if data is None:
|
724
|
+
data = list(self.elems(emphasized=True))
|
725
|
+
if len(data) == 0:
|
726
|
+
channel(_("No selected elements."))
|
727
|
+
return
|
728
|
+
if prop is None or (prop == "?"):
|
729
|
+
channel(_("You need to provide the property to get."))
|
730
|
+
identified = []
|
731
|
+
for op in data:
|
732
|
+
if op.type in identified:
|
733
|
+
continue
|
734
|
+
identified.append(op.type)
|
735
|
+
prop_str = f"{op.type} has the following properties:"
|
736
|
+
first = True
|
737
|
+
for d in op.__dict__:
|
738
|
+
if d.startswith("_"):
|
739
|
+
continue
|
740
|
+
prop_str = f"{prop_str}{'' if first else ','} {d}"
|
741
|
+
first = False
|
742
|
+
channel(prop_str)
|
743
|
+
return
|
744
|
+
for d in data:
|
745
|
+
if not hasattr(d, prop):
|
746
|
+
channel(
|
747
|
+
f"Node: {d.display_label()} (Type: {d.type}) has no property called '{prop}'"
|
748
|
+
)
|
749
|
+
else:
|
750
|
+
channel(
|
751
|
+
f"Node: {d.display_label()} (Type: {d.type}): {prop}={getattr(d, prop, '')} ({possible_representation(d, prop)})"
|
752
|
+
)
|
660
753
|
|
661
754
|
@self.console_argument("prop", type=str, help=_("property to set"))
|
662
755
|
@self.console_argument("new_value", type=str, help=_("new property value"))
|
@@ -681,7 +774,7 @@ def init_commands(kernel):
|
|
681
774
|
if len(data) == 0:
|
682
775
|
channel(_("No selected elements."))
|
683
776
|
return
|
684
|
-
if prop is None or (prop == "?" and new_value=="?"):
|
777
|
+
if prop is None or (prop == "?" and new_value == "?"):
|
685
778
|
channel(_("You need to provide the property to set."))
|
686
779
|
if prop == "?":
|
687
780
|
identified = []
|
@@ -689,13 +782,17 @@ def init_commands(kernel):
|
|
689
782
|
if op.type in identified:
|
690
783
|
continue
|
691
784
|
identified.append(op.type)
|
692
|
-
prop_str = f"{op.type} has the following properties:
|
785
|
+
prop_str = f"{op.type} has the following properties:"
|
786
|
+
first = True
|
693
787
|
for d in op.__dict__:
|
694
788
|
if d.startswith("_"):
|
695
789
|
continue
|
696
|
-
prop_str = f"{prop_str}, {d}"
|
790
|
+
prop_str = f"{prop_str}{'' if first else ','} {d}"
|
791
|
+
first = False
|
697
792
|
channel(prop_str)
|
698
|
-
channel
|
793
|
+
channel(
|
794
|
+
"Be careful what you do - this is a failsafe method to crash MeerK40t, burn down your house or whatever..."
|
795
|
+
)
|
699
796
|
return
|
700
797
|
classify_required = False
|
701
798
|
prop = prop.lower()
|
@@ -769,7 +866,9 @@ def init_commands(kernel):
|
|
769
866
|
if prop in ("x", "y"):
|
770
867
|
if not e.can_move(self.lock_allows_move):
|
771
868
|
channel(
|
772
|
-
_("Element can not be moved: {name}").format(
|
869
|
+
_("Element can not be moved: {name}").format(
|
870
|
+
name=str(e)
|
871
|
+
)
|
773
872
|
)
|
774
873
|
continue
|
775
874
|
# We need to adjust the matrix
|
@@ -795,7 +894,9 @@ def init_commands(kernel):
|
|
795
894
|
continue
|
796
895
|
if hasattr(e, "can_scale") and not e.can_scale:
|
797
896
|
channel(
|
798
|
-
_("Element can not be scaled: {name}").format(
|
897
|
+
_("Element can not be scaled: {name}").format(
|
898
|
+
name=str(e)
|
899
|
+
)
|
799
900
|
)
|
800
901
|
continue
|
801
902
|
if hasattr(e, "matrix") and hasattr(e, "bounds"):
|
@@ -865,7 +966,9 @@ def init_commands(kernel):
|
|
865
966
|
delattr(e, "wxfont")
|
866
967
|
text_elems.append(e)
|
867
968
|
if prop in ("mktext", "mkfont"):
|
868
|
-
for property_op in self.kernel.lookup_all(
|
969
|
+
for property_op in self.kernel.lookup_all(
|
970
|
+
"path_updater/.*"
|
971
|
+
):
|
869
972
|
property_op(self.kernel.root, e)
|
870
973
|
if prop in (
|
871
974
|
"dpi",
|
@@ -935,7 +1038,7 @@ def init_commands(kernel):
|
|
935
1038
|
if not data:
|
936
1039
|
channel(_("No selected operations."))
|
937
1040
|
return
|
938
|
-
if prop is None or (prop == "?" and new_value=="?"):
|
1041
|
+
if prop is None or (prop == "?" and new_value == "?"):
|
939
1042
|
channel(_("You need to provide the property to set."))
|
940
1043
|
if prop == "?":
|
941
1044
|
identified = []
|
@@ -949,7 +1052,9 @@ def init_commands(kernel):
|
|
949
1052
|
continue
|
950
1053
|
prop_str = f"{prop_str}, {d}"
|
951
1054
|
channel(prop_str)
|
952
|
-
channel
|
1055
|
+
channel(
|
1056
|
+
"Be careful what you do - this is a failsafe method to crash MeerK40t, burn down your house or whatever..."
|
1057
|
+
)
|
953
1058
|
return
|
954
1059
|
prop = prop.lower()
|
955
1060
|
if len(new_value) == 0:
|
@@ -982,7 +1087,6 @@ def init_commands(kernel):
|
|
982
1087
|
new_value = testval
|
983
1088
|
prevalidated = True
|
984
1089
|
|
985
|
-
|
986
1090
|
changed = []
|
987
1091
|
# _("Update property")
|
988
1092
|
with self.undoscope("Update property"):
|
@@ -1030,7 +1134,6 @@ def init_commands(kernel):
|
|
1030
1134
|
).format(val=new_value, field=prop, oldval=oldval)
|
1031
1135
|
)
|
1032
1136
|
|
1033
|
-
|
1034
1137
|
else:
|
1035
1138
|
channel(
|
1036
1139
|
_("Operation {name} has no property {field}").format(
|
@@ -1122,7 +1225,7 @@ def init_commands(kernel):
|
|
1122
1225
|
f"Simplified {node.type} ({node.display_label()}), tolerance: {tolerance}={Length(tolerance, digits=4).length_mm})"
|
1123
1226
|
)
|
1124
1227
|
if seg_before:
|
1125
|
-
saving = f"({(seg_before - seg_after)/seg_before*100:.1f}%)"
|
1228
|
+
saving = f"({(seg_before - seg_after) / seg_before * 100:.1f}%)"
|
1126
1229
|
else:
|
1127
1230
|
saving = ""
|
1128
1231
|
channel(f"Subpaths before: {sub_before} to {sub_after}")
|
@@ -1363,7 +1466,9 @@ def init_commands(kernel):
|
|
1363
1466
|
with self.undoscope("Set stroke-width"):
|
1364
1467
|
for e in data:
|
1365
1468
|
if hasattr(e, "lock") and e.lock:
|
1366
|
-
channel(
|
1469
|
+
channel(
|
1470
|
+
_("Can't modify a locked element: {name}").format(name=str(e))
|
1471
|
+
)
|
1367
1472
|
continue
|
1368
1473
|
e.stroke_width = stroke_width
|
1369
1474
|
try:
|
@@ -1396,7 +1501,9 @@ def init_commands(kernel):
|
|
1396
1501
|
with self.undoscope("Update stroke-scale"):
|
1397
1502
|
for e in data:
|
1398
1503
|
if hasattr(e, "lock") and e.lock:
|
1399
|
-
channel(
|
1504
|
+
channel(
|
1505
|
+
_("Can't modify a locked element: {name}").format(name=str(e))
|
1506
|
+
)
|
1400
1507
|
continue
|
1401
1508
|
e.stroke_scaled = command == "enable_stroke_scale"
|
1402
1509
|
e.altered()
|
@@ -1700,7 +1807,9 @@ def init_commands(kernel):
|
|
1700
1807
|
for e in apply:
|
1701
1808
|
if hasattr(e, "lock") and e.lock:
|
1702
1809
|
channel(
|
1703
|
-
_("Can't modify a locked element: {name}").format(
|
1810
|
+
_("Can't modify a locked element: {name}").format(
|
1811
|
+
name=str(e)
|
1812
|
+
)
|
1704
1813
|
)
|
1705
1814
|
continue
|
1706
1815
|
e.stroke = None
|
@@ -1712,7 +1821,9 @@ def init_commands(kernel):
|
|
1712
1821
|
for e in apply:
|
1713
1822
|
if hasattr(e, "lock") and e.lock:
|
1714
1823
|
channel(
|
1715
|
-
_("Can't modify a locked element: {name}").format(
|
1824
|
+
_("Can't modify a locked element: {name}").format(
|
1825
|
+
name=str(e)
|
1826
|
+
)
|
1716
1827
|
)
|
1717
1828
|
continue
|
1718
1829
|
e.stroke = Color(color)
|
@@ -1803,13 +1914,14 @@ def init_commands(kernel):
|
|
1803
1914
|
return "elements", data
|
1804
1915
|
# _("Set fill")
|
1805
1916
|
with self.undoscope("Set fill"):
|
1806
|
-
|
1807
1917
|
if color == "none":
|
1808
1918
|
self.set_start_time("fill")
|
1809
1919
|
for e in apply:
|
1810
1920
|
if hasattr(e, "lock") and e.lock:
|
1811
1921
|
channel(
|
1812
|
-
_("Can't modify a locked element: {name}").format(
|
1922
|
+
_("Can't modify a locked element: {name}").format(
|
1923
|
+
name=str(e)
|
1924
|
+
)
|
1813
1925
|
)
|
1814
1926
|
continue
|
1815
1927
|
e.fill = None
|
@@ -1821,7 +1933,9 @@ def init_commands(kernel):
|
|
1821
1933
|
for e in apply:
|
1822
1934
|
if hasattr(e, "lock") and e.lock:
|
1823
1935
|
channel(
|
1824
|
-
_("Can't modify a locked element: {name}").format(
|
1936
|
+
_("Can't modify a locked element: {name}").format(
|
1937
|
+
name=str(e)
|
1938
|
+
)
|
1825
1939
|
)
|
1826
1940
|
continue
|
1827
1941
|
e.fill = Color(color)
|
@@ -2626,15 +2740,26 @@ def init_commands(kernel):
|
|
2626
2740
|
self.first_emphasized = None
|
2627
2741
|
return "elements", data
|
2628
2742
|
|
2629
|
-
@self.console_argument(
|
2630
|
-
|
2743
|
+
@self.console_argument(
|
2744
|
+
"tolerance", type=str, help=_("Tolerance to stitch paths together")
|
2745
|
+
)
|
2746
|
+
@self.console_option(
|
2747
|
+
"keep",
|
2748
|
+
"k",
|
2749
|
+
type=bool,
|
2750
|
+
action="store_true",
|
2751
|
+
default=False,
|
2752
|
+
help=_("Keep original paths"),
|
2753
|
+
)
|
2631
2754
|
@self.console_command(
|
2632
2755
|
"stitch",
|
2633
2756
|
help=_("stitch selected elements"),
|
2634
2757
|
input_type=(None, "elements"),
|
2635
2758
|
output_type="elements",
|
2636
2759
|
)
|
2637
|
-
def stitched(
|
2760
|
+
def stitched(
|
2761
|
+
command, channel, _, data=None, tolerance=None, keep=None, post=None, **kwargs
|
2762
|
+
):
|
2638
2763
|
def _prepare_stitching_params(channel, data, tolerance, keep):
|
2639
2764
|
if data is None:
|
2640
2765
|
data = list(self.elems(emphasized=True))
|
@@ -2652,38 +2777,10 @@ def init_commands(kernel):
|
|
2652
2777
|
channel(f"Invalid tolerance value: {tolerance}")
|
2653
2778
|
return data, tolerance, keep, False
|
2654
2779
|
return data, tolerance_val, keep, True
|
2655
|
-
|
2656
|
-
|
2657
|
-
|
2658
|
-
|
2659
|
-
# Store all geometries together with an indicator, to which node they belong
|
2660
|
-
for idx, node in enumerate(data):
|
2661
|
-
if not hasattr(node, "as_geometry"):
|
2662
|
-
continue
|
2663
|
-
for g1 in node.as_geometry().as_contiguous():
|
2664
|
-
geoms.append((idx, g1))
|
2665
|
-
for idx1, (nodeidx1, g1) in enumerate(geoms):
|
2666
|
-
for idx2 in range(idx1 + 1, len(geoms)):
|
2667
|
-
nodeidx2 = geoms[idx2][0]
|
2668
|
-
g2 = geoms[idx2][1]
|
2669
|
-
fp1 = g1.first_point
|
2670
|
-
fp2 = g2.first_point
|
2671
|
-
lp1 = g1.last_point
|
2672
|
-
lp2 = g2.last_point
|
2673
|
-
if (
|
2674
|
-
abs(lp1 - lp2) <= tolerance or
|
2675
|
-
abs(lp1 - fp2) <= tolerance or
|
2676
|
-
abs(fp1 - fp2) <= tolerance or
|
2677
|
-
abs(fp1 - lp2) <= tolerance
|
2678
|
-
):
|
2679
|
-
if nodeidx1 not in out:
|
2680
|
-
out.append(nodeidx1)
|
2681
|
-
if nodeidx2 not in out:
|
2682
|
-
out.append(nodeidx2)
|
2683
|
-
|
2684
|
-
return [data[idx] for idx in out]
|
2685
|
-
|
2686
|
-
data, tolerance, keep, valid = _prepare_stitching_params(channel, data, tolerance, keep)
|
2780
|
+
|
2781
|
+
data, tolerance, keep, valid = _prepare_stitching_params(
|
2782
|
+
channel, data, tolerance, keep
|
2783
|
+
)
|
2687
2784
|
if not valid:
|
2688
2785
|
return
|
2689
2786
|
s_data = stitcheable_nodes(data, tolerance)
|
@@ -2700,13 +2797,12 @@ def init_commands(kernel):
|
|
2700
2797
|
default_strokewidth = None
|
2701
2798
|
default_fill = None
|
2702
2799
|
for node in s_data:
|
2703
|
-
|
2704
|
-
|
2705
|
-
|
2706
|
-
|
2707
|
-
|
2708
|
-
|
2709
|
-
default_strokewidth = node.stroke_width
|
2800
|
+
geom: Geomstr = node.as_geometry()
|
2801
|
+
geoms.extend(iter(geom.as_contiguous()))
|
2802
|
+
if default_stroke is None and hasattr(node, "stroke"):
|
2803
|
+
default_stroke = node.stroke
|
2804
|
+
if default_strokewidth is None and hasattr(node, "stroke_width"):
|
2805
|
+
default_strokewidth = node.stroke_width
|
2710
2806
|
to_be_deleted.append(node)
|
2711
2807
|
prev_len = len(geoms)
|
2712
2808
|
if geoms:
|
@@ -2728,10 +2824,101 @@ def init_commands(kernel):
|
|
2728
2824
|
)
|
2729
2825
|
data_out.append(node)
|
2730
2826
|
new_len = len(data_out)
|
2731
|
-
channel(
|
2732
|
-
|
2827
|
+
channel(
|
2828
|
+
f"Sub-Paths before: {prev_len} -> consolidated to {new_len} sub-paths"
|
2829
|
+
)
|
2830
|
+
|
2733
2831
|
post.append(classify_new(data_out))
|
2734
2832
|
self.set_emphasis(data_out)
|
2735
2833
|
return "elements", data_out
|
2736
2834
|
|
2835
|
+
@self.console_argument("xpos", type=Length, help=_("X-Position of cross center"))
|
2836
|
+
@self.console_argument("ypos", type=Length, help=_("Y-Position of cross center"))
|
2837
|
+
@self.console_argument("diameter", type=Length, help=_("Diameter of cross"))
|
2838
|
+
@self.console_option(
|
2839
|
+
"circle",
|
2840
|
+
"c",
|
2841
|
+
type=bool,
|
2842
|
+
action="store_true",
|
2843
|
+
default=False,
|
2844
|
+
help=_("Draw a circle around cross"),
|
2845
|
+
)
|
2846
|
+
@self.console_option(
|
2847
|
+
"diagonal",
|
2848
|
+
"d",
|
2849
|
+
type=bool,
|
2850
|
+
action="store_true",
|
2851
|
+
default=False,
|
2852
|
+
help=_("Draw the cross diagonally"),
|
2853
|
+
)
|
2854
|
+
@self.console_command(
|
2855
|
+
"cross",
|
2856
|
+
help=_("Create a small cross at the given position"),
|
2857
|
+
input_type=None,
|
2858
|
+
output_type="elements",
|
2859
|
+
)
|
2860
|
+
def cross(
|
2861
|
+
command,
|
2862
|
+
channel,
|
2863
|
+
_,
|
2864
|
+
data=None,
|
2865
|
+
xpos=None,
|
2866
|
+
ypos=None,
|
2867
|
+
diameter=None,
|
2868
|
+
circle=None,
|
2869
|
+
diagonal=None,
|
2870
|
+
post=None,
|
2871
|
+
**kwargs,
|
2872
|
+
):
|
2873
|
+
if xpos is None or ypos is None or diameter is None:
|
2874
|
+
channel(_("You need to provide center-point and diameter: cross x y d"))
|
2875
|
+
return
|
2876
|
+
try:
|
2877
|
+
xp = float(xpos)
|
2878
|
+
yp = float(ypos)
|
2879
|
+
dia = float(diameter)
|
2880
|
+
except ValueError:
|
2881
|
+
channel(_("Invalid values given"))
|
2882
|
+
return
|
2883
|
+
if circle is None:
|
2884
|
+
circle = False
|
2885
|
+
if diagonal is None:
|
2886
|
+
diagonal = False
|
2887
|
+
geom = Geomstr()
|
2888
|
+
if diagonal:
|
2889
|
+
sincos45 = dia / 2 * sqrt(2) / 2
|
2890
|
+
geom.line(
|
2891
|
+
complex(xp - sincos45, yp - sincos45),
|
2892
|
+
complex(xp + sincos45, yp + sincos45),
|
2893
|
+
)
|
2894
|
+
geom.line(
|
2895
|
+
complex(xp + sincos45, yp - sincos45),
|
2896
|
+
complex(xp - sincos45, yp + sincos45),
|
2897
|
+
)
|
2898
|
+
else:
|
2899
|
+
geom.line(complex(xp - dia / 2, yp), complex(xp + dia / 2, yp))
|
2900
|
+
geom.line(complex(xp, yp - dia / 2), complex(xp, yp + dia / 2))
|
2901
|
+
if circle:
|
2902
|
+
geom.append(Geomstr.circle(dia / 2, xp, yp))
|
2903
|
+
# _("Create cross") - hint for translator
|
2904
|
+
with self.undoscope("Create cross"):
|
2905
|
+
node = self.elem_branch.add(
|
2906
|
+
label=_("Cross at ({xp}, {yp})").format(
|
2907
|
+
xp=xpos.length_mm, yp=ypos.length_mm
|
2908
|
+
),
|
2909
|
+
geometry=geom,
|
2910
|
+
stroke=self.default_stroke,
|
2911
|
+
stroke_width=self.default_strokewidth,
|
2912
|
+
fill=None,
|
2913
|
+
type="elem path",
|
2914
|
+
)
|
2915
|
+
if data is None:
|
2916
|
+
data = []
|
2917
|
+
data.append(node)
|
2918
|
+
|
2919
|
+
# Newly created! Classification needed?
|
2920
|
+
post.append(classify_new(data))
|
2921
|
+
self.signal("refresh_scene", "Scene")
|
2922
|
+
return "elements", data
|
2923
|
+
|
2737
2924
|
# --------------------------- END COMMANDS ------------------------------
|
@@ -2,7 +2,6 @@
|
|
2
2
|
This is a giant list of console commands that deal with and often implement the elements system in the program.
|
3
3
|
"""
|
4
4
|
|
5
|
-
|
6
5
|
from meerk40t.kernel import CommandSyntaxError
|
7
6
|
|
8
7
|
from .element_types import *
|
@@ -39,9 +38,10 @@ def init_commands(kernel):
|
|
39
38
|
for i, n in enumerate(node.children):
|
40
39
|
p = list(path)
|
41
40
|
p.append(str(i))
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
if hasattr(n, "_bounds"):
|
42
|
+
channel(
|
43
|
+
f"{'.'.join(p).ljust(10)}: {str(n._bounds)} - {str(n._bounds_dirty)} {str(n.type)} - {str(str(n)[:16])}"
|
44
|
+
)
|
45
45
|
b_list(p, n)
|
46
46
|
|
47
47
|
for d in data:
|
@@ -49,7 +49,12 @@ def init_commands(kernel):
|
|
49
49
|
if d.type == "root":
|
50
50
|
channel(_("Tree:"))
|
51
51
|
else:
|
52
|
-
|
52
|
+
if hasattr(d, "_bounds"):
|
53
|
+
channel(
|
54
|
+
f"{str(d)}: {str(d._bounds)} - {str(d._bounds_dirty)} {str(d.type)} - {str(str(d)[:16])}"
|
55
|
+
)
|
56
|
+
else:
|
57
|
+
channel(f"{str(d)}:")
|
53
58
|
b_list([], d)
|
54
59
|
channel("----------")
|
55
60
|
|
meerk40t/core/node/blobnode.py
CHANGED
@@ -41,8 +41,15 @@ class BlobNode(Node):
|
|
41
41
|
return False
|
42
42
|
|
43
43
|
@staticmethod
|
44
|
-
def hex_view(data, data_type):
|
45
|
-
|
44
|
+
def hex_view(data, data_type, info=''):
|
45
|
+
"""
|
46
|
+
Render a hex dump with an optional info header.
|
47
|
+
|
48
|
+
data: bytes to display
|
49
|
+
data_type: mime type
|
50
|
+
info: an arbitrary string to append to the end of first line of header
|
51
|
+
"""
|
52
|
+
header1 = f"Data-Type: {data_type}, Length={len(data)}{info}\n"
|
46
53
|
header2 = "Offset | Hex | Ascii \n"
|
47
54
|
header2 += "-------+-------------------------------------------------+----------------\n"
|
48
55
|
if isinstance(data, str):
|
@@ -70,8 +77,16 @@ class BlobNode(Node):
|
|
70
77
|
return header1 + header2 + "".join(hex_data)
|
71
78
|
|
72
79
|
@staticmethod
|
73
|
-
def ascii_view(data, data_type):
|
74
|
-
|
80
|
+
def ascii_view(data, data_type, info=''):
|
81
|
+
"""
|
82
|
+
Render an ascii view with an optional info header.
|
83
|
+
|
84
|
+
data: bytes to display
|
85
|
+
data_type: mime type
|
86
|
+
info: an arbitrary string to append to the end of first line of header
|
87
|
+
"""
|
88
|
+
|
89
|
+
header1 = f"Data-Type: {data_type}, Length={len(data)}{info}\n"
|
75
90
|
header2 = "Offset | Hex | Ascii \n"
|
76
91
|
header2 += "-------+-------------------------------------------------+----------------\n"
|
77
92
|
if isinstance(data, str):
|
@@ -3,8 +3,8 @@ from math import cos, sin, sqrt, tau
|
|
3
3
|
|
4
4
|
from meerk40t.core.node.mixins import (
|
5
5
|
FunctionalParameter,
|
6
|
-
Stroked,
|
7
6
|
LabelDisplay,
|
7
|
+
Stroked,
|
8
8
|
Suppressable,
|
9
9
|
)
|
10
10
|
from meerk40t.core.node.node import Fillrule, Node
|
@@ -171,12 +171,16 @@ class EllipseNode(Node, Stroked, FunctionalParameter, LabelDisplay, Suppressable
|
|
171
171
|
tablen = self.mktablength
|
172
172
|
numtabs = self.mktabpositions
|
173
173
|
if tablen and numtabs:
|
174
|
-
path = Geomstr.wobble_tab(
|
174
|
+
path = Geomstr.wobble_tab(
|
175
|
+
path, tablen, resolution, numtabs, unit_factor=unit_factor
|
176
|
+
)
|
175
177
|
# Is there a dash/dot pattern to apply?
|
176
178
|
dashlen = self.stroke_dash
|
177
179
|
irrelevant = 50
|
178
180
|
if dashlen:
|
179
|
-
path = Geomstr.wobble_dash(
|
181
|
+
path = Geomstr.wobble_dash(
|
182
|
+
path, dashlen, resolution, irrelevant, unit_factor=unit_factor
|
183
|
+
)
|
180
184
|
|
181
185
|
return path
|
182
186
|
|
@@ -261,19 +265,25 @@ class EllipseNode(Node, Stroked, FunctionalParameter, LabelDisplay, Suppressable
|
|
261
265
|
return default_map
|
262
266
|
|
263
267
|
def can_drop(self, drag_node):
|
268
|
+
if self.is_a_child_of(drag_node):
|
269
|
+
return False
|
264
270
|
# Dragging element into element.
|
265
271
|
return bool(
|
266
|
-
hasattr(drag_node, "as_geometry")
|
267
|
-
hasattr(drag_node, "as_image")
|
268
|
-
(drag_node.type.startswith("op ") and drag_node.type != "op dots")
|
269
|
-
drag_node.type in ("file", "group")
|
272
|
+
hasattr(drag_node, "as_geometry")
|
273
|
+
or hasattr(drag_node, "as_image")
|
274
|
+
or (drag_node.type.startswith("op ") and drag_node.type != "op dots")
|
275
|
+
or drag_node.type in ("file", "group")
|
270
276
|
)
|
271
277
|
|
272
278
|
def drop(self, drag_node, modify=True, flag=False):
|
273
279
|
# Dragging element into element.
|
274
280
|
if not self.can_drop(drag_node):
|
275
281
|
return False
|
276
|
-
if
|
282
|
+
if (
|
283
|
+
hasattr(drag_node, "as_geometry")
|
284
|
+
or hasattr(drag_node, "as_image")
|
285
|
+
or drag_node.type in ("file", "group")
|
286
|
+
):
|
277
287
|
if modify:
|
278
288
|
self.insert_sibling(drag_node)
|
279
289
|
return True
|