meerk40t 0.9.2000__py2.py3-none-any.whl → 0.9.3001__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 (187) hide show
  1. meerk40t/balormk/balor_params.py +1 -43
  2. meerk40t/balormk/controller.py +1 -41
  3. meerk40t/balormk/device.py +16 -22
  4. meerk40t/balormk/driver.py +4 -4
  5. meerk40t/balormk/gui/balorconfig.py +2 -2
  6. meerk40t/balormk/gui/balorcontroller.py +13 -5
  7. meerk40t/balormk/gui/baloroperationproperties.py +0 -46
  8. meerk40t/balormk/gui/gui.py +17 -17
  9. meerk40t/camera/gui/camerapanel.py +18 -11
  10. meerk40t/core/cutcode/rastercut.py +3 -1
  11. meerk40t/core/cutplan.py +145 -14
  12. meerk40t/core/elements/clipboard.py +18 -9
  13. meerk40t/core/elements/element_treeops.py +320 -180
  14. meerk40t/core/elements/element_types.py +7 -2
  15. meerk40t/core/elements/elements.py +53 -27
  16. meerk40t/core/elements/geometry.py +8 -0
  17. meerk40t/core/elements/offset_clpr.py +129 -4
  18. meerk40t/core/elements/offset_mk.py +3 -1
  19. meerk40t/core/elements/shapes.py +28 -25
  20. meerk40t/core/laserjob.py +7 -0
  21. meerk40t/core/node/bootstrap.py +4 -0
  22. meerk40t/core/node/effect_hatch.py +85 -96
  23. meerk40t/core/node/effect_wobble.py +309 -0
  24. meerk40t/core/node/elem_image.py +49 -19
  25. meerk40t/core/node/elem_line.py +60 -0
  26. meerk40t/core/node/elem_rect.py +5 -3
  27. meerk40t/core/node/image_processed.py +766 -0
  28. meerk40t/core/node/image_raster.py +113 -0
  29. meerk40t/core/node/node.py +120 -1
  30. meerk40t/core/node/op_cut.py +2 -8
  31. meerk40t/core/node/op_dots.py +0 -8
  32. meerk40t/core/node/op_engrave.py +2 -18
  33. meerk40t/core/node/op_image.py +22 -35
  34. meerk40t/core/node/op_raster.py +0 -9
  35. meerk40t/core/planner.py +32 -2
  36. meerk40t/core/svg_io.py +699 -461
  37. meerk40t/core/treeop.py +191 -0
  38. meerk40t/core/undos.py +15 -1
  39. meerk40t/core/units.py +14 -4
  40. meerk40t/device/dummydevice.py +3 -2
  41. meerk40t/device/gui/defaultactions.py +43 -55
  42. meerk40t/device/gui/formatterpanel.py +58 -49
  43. meerk40t/device/gui/warningpanel.py +12 -12
  44. meerk40t/device/mixins.py +13 -0
  45. meerk40t/dxf/dxf_io.py +9 -5
  46. meerk40t/extra/ezd.py +28 -26
  47. meerk40t/extra/imageactions.py +300 -308
  48. meerk40t/extra/lbrn.py +19 -2
  49. meerk40t/fill/fills.py +6 -6
  50. meerk40t/fill/patternfill.py +1061 -1061
  51. meerk40t/fill/patterns.py +2 -6
  52. meerk40t/grbl/controller.py +168 -52
  53. meerk40t/grbl/device.py +23 -18
  54. meerk40t/grbl/driver.py +39 -0
  55. meerk40t/grbl/emulator.py +79 -19
  56. meerk40t/grbl/gcodejob.py +10 -0
  57. meerk40t/grbl/gui/grblconfiguration.py +2 -2
  58. meerk40t/grbl/gui/grblcontroller.py +24 -8
  59. meerk40t/grbl/gui/grblhardwareconfig.py +153 -0
  60. meerk40t/grbl/gui/gui.py +17 -14
  61. meerk40t/grbl/mock_connection.py +15 -34
  62. meerk40t/grbl/plugin.py +0 -4
  63. meerk40t/grbl/serial_connection.py +2 -1
  64. meerk40t/gui/about.py +8 -5
  65. meerk40t/gui/alignment.py +10 -6
  66. meerk40t/gui/basicops.py +27 -17
  67. meerk40t/gui/bufferview.py +2 -2
  68. meerk40t/gui/choicepropertypanel.py +101 -13
  69. meerk40t/gui/consolepanel.py +12 -9
  70. meerk40t/gui/devicepanel.py +38 -25
  71. meerk40t/gui/executejob.py +6 -4
  72. meerk40t/gui/help_assets/help_assets.py +13 -10
  73. meerk40t/gui/hersheymanager.py +8 -6
  74. meerk40t/gui/icons.py +1951 -3065
  75. meerk40t/gui/imagesplitter.py +14 -7
  76. meerk40t/gui/keymap.py +3 -3
  77. meerk40t/gui/laserpanel.py +151 -84
  78. meerk40t/gui/laserrender.py +61 -70
  79. meerk40t/gui/lasertoolpanel.py +8 -7
  80. meerk40t/gui/materialtest.py +3 -3
  81. meerk40t/gui/mkdebug.py +254 -1
  82. meerk40t/gui/navigationpanels.py +321 -180
  83. meerk40t/gui/notes.py +3 -3
  84. meerk40t/gui/opassignment.py +12 -12
  85. meerk40t/gui/operation_info.py +13 -13
  86. meerk40t/gui/plugin.py +5 -0
  87. meerk40t/gui/position.py +20 -18
  88. meerk40t/gui/preferences.py +21 -6
  89. meerk40t/gui/propertypanels/attributes.py +70 -22
  90. meerk40t/gui/propertypanels/blobproperty.py +2 -2
  91. meerk40t/gui/propertypanels/consoleproperty.py +2 -2
  92. meerk40t/gui/propertypanels/groupproperties.py +3 -3
  93. meerk40t/gui/propertypanels/hatchproperty.py +11 -18
  94. meerk40t/gui/propertypanels/imageproperty.py +4 -3
  95. meerk40t/gui/propertypanels/opbranchproperties.py +1 -1
  96. meerk40t/gui/propertypanels/pathproperty.py +2 -2
  97. meerk40t/gui/propertypanels/pointproperty.py +2 -2
  98. meerk40t/gui/propertypanels/propertywindow.py +4 -4
  99. meerk40t/gui/propertypanels/textproperty.py +3 -3
  100. meerk40t/gui/propertypanels/wobbleproperty.py +204 -0
  101. meerk40t/gui/ribbon.py +367 -259
  102. meerk40t/gui/scene/scene.py +31 -5
  103. meerk40t/gui/scenewidgets/elementswidget.py +12 -4
  104. meerk40t/gui/scenewidgets/gridwidget.py +2 -2
  105. meerk40t/gui/scenewidgets/laserpathwidget.py +7 -2
  106. meerk40t/gui/scenewidgets/machineoriginwidget.py +6 -2
  107. meerk40t/gui/scenewidgets/relocatewidget.py +1 -1
  108. meerk40t/gui/scenewidgets/reticlewidget.py +9 -0
  109. meerk40t/gui/scenewidgets/selectionwidget.py +12 -7
  110. meerk40t/gui/simpleui.py +95 -8
  111. meerk40t/gui/simulation.py +44 -36
  112. meerk40t/gui/spoolerpanel.py +124 -26
  113. meerk40t/gui/statusbarwidgets/defaultoperations.py +18 -6
  114. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  115. meerk40t/gui/statusbarwidgets/opassignwidget.py +12 -12
  116. meerk40t/gui/statusbarwidgets/shapepropwidget.py +45 -18
  117. meerk40t/gui/statusbarwidgets/statusbar.py +11 -4
  118. meerk40t/gui/themes.py +78 -0
  119. meerk40t/gui/toolwidgets/toolcircle.py +2 -1
  120. meerk40t/gui/toolwidgets/toolellipse.py +2 -1
  121. meerk40t/gui/toolwidgets/toolimagecut.py +132 -0
  122. meerk40t/gui/toolwidgets/toolline.py +144 -0
  123. meerk40t/gui/toolwidgets/toolnodeedit.py +72 -145
  124. meerk40t/gui/toolwidgets/toolpoint.py +1 -1
  125. meerk40t/gui/toolwidgets/toolpolygon.py +8 -55
  126. meerk40t/gui/toolwidgets/toolrect.py +2 -1
  127. meerk40t/gui/usbconnect.py +2 -2
  128. meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +2 -2
  129. meerk40t/gui/utilitywidgets/harmonograph.py +7 -7
  130. meerk40t/gui/utilitywidgets/scalewidget.py +1 -1
  131. meerk40t/gui/wordlisteditor.py +33 -18
  132. meerk40t/gui/wxmeerk40t.py +166 -66
  133. meerk40t/gui/wxmmain.py +236 -157
  134. meerk40t/gui/wxmribbon.py +49 -25
  135. meerk40t/gui/wxmscene.py +49 -38
  136. meerk40t/gui/wxmtree.py +216 -85
  137. meerk40t/gui/wxutils.py +62 -4
  138. meerk40t/image/imagetools.py +443 -15
  139. meerk40t/internal_plugins.py +2 -10
  140. meerk40t/kernel/kernel.py +12 -4
  141. meerk40t/lihuiyu/controller.py +7 -7
  142. meerk40t/lihuiyu/device.py +3 -1
  143. meerk40t/lihuiyu/driver.py +3 -0
  144. meerk40t/lihuiyu/gui/gui.py +8 -8
  145. meerk40t/lihuiyu/gui/lhyaccelgui.py +2 -2
  146. meerk40t/lihuiyu/gui/lhycontrollergui.py +73 -27
  147. meerk40t/lihuiyu/gui/lhydrivergui.py +2 -2
  148. meerk40t/lihuiyu/gui/tcpcontroller.py +22 -9
  149. meerk40t/main.py +6 -1
  150. meerk40t/moshi/controller.py +5 -5
  151. meerk40t/moshi/device.py +5 -2
  152. meerk40t/moshi/driver.py +4 -0
  153. meerk40t/moshi/gui/gui.py +8 -8
  154. meerk40t/moshi/gui/moshicontrollergui.py +24 -8
  155. meerk40t/moshi/gui/moshidrivergui.py +2 -2
  156. meerk40t/newly/controller.py +2 -0
  157. meerk40t/newly/device.py +9 -2
  158. meerk40t/newly/driver.py +4 -0
  159. meerk40t/newly/gui/gui.py +16 -17
  160. meerk40t/newly/gui/newlyconfig.py +2 -2
  161. meerk40t/newly/gui/newlycontroller.py +13 -5
  162. meerk40t/rotary/gui/gui.py +2 -2
  163. meerk40t/rotary/gui/rotarysettings.py +2 -2
  164. meerk40t/ruida/device.py +3 -0
  165. meerk40t/ruida/driver.py +4 -0
  166. meerk40t/ruida/gui/gui.py +6 -6
  167. meerk40t/ruida/gui/ruidaconfig.py +2 -2
  168. meerk40t/ruida/gui/ruidacontroller.py +13 -5
  169. meerk40t/svgelements.py +9 -9
  170. meerk40t/tools/geomstr.py +849 -153
  171. meerk40t/tools/kerftest.py +8 -4
  172. meerk40t/tools/livinghinges.py +15 -8
  173. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/METADATA +21 -16
  174. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/RECORD +185 -177
  175. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/entry_points.txt +0 -1
  176. test/test_core_elements.py +8 -24
  177. test/test_file_svg.py +88 -0
  178. test/test_fill.py +9 -9
  179. test/test_geomstr.py +258 -8
  180. test/test_kernel.py +4 -0
  181. test/test_tools_rasterplotter.py +29 -0
  182. meerk40t/extra/embroider.py +0 -56
  183. meerk40t/extra/pathoptimize.py +0 -249
  184. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/LICENSE +0 -0
  185. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/WHEEL +0 -0
  186. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/top_level.txt +0 -0
  187. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/zip-safe +0 -0
@@ -1,1061 +1,1061 @@
1
- """
2
- Obsolete! No longer needed, just there for reference
3
- """
4
- from copy import copy
5
-
6
- from meerk40t.svgelements import (
7
- Arc,
8
- Close,
9
- Color,
10
- CubicBezier,
11
- Line,
12
- Matrix,
13
- Move,
14
- Path,
15
- Point,
16
- Polygon,
17
- Polyline,
18
- QuadraticBezier,
19
- )
20
- from meerk40t.tools.pathtools import VectorMontonizer
21
-
22
- _FACTOR = 1000
23
-
24
-
25
- class LivingHinges:
26
- """
27
- This class generates a predefined pattern in a *rectangular* area
28
- """
29
-
30
- def __init__(self, xpos, ypos, width, height):
31
- self.pattern = None
32
- self.start_x = xpos
33
- self.start_y = ypos
34
- self.width = width
35
- self.height = height
36
- # We set it off somewhat...
37
- self.gap = 0
38
- self.x0 = width * self.gap
39
- self.y0 = height * self.gap
40
- self.x1 = width * (1 - self.gap)
41
- self.y1 = height * (1 - self.gap)
42
- # Declare all used variables to satisfy codacy
43
- self.param_a = 0
44
- self.param_b = 0
45
- self.cell_height_percentage = 200
46
- self.cell_width_percentage = 200
47
- self.cell_height = height * self.cell_height_percentage / _FACTOR
48
- self.cell_width = width * self.cell_width_percentage / _FACTOR
49
- self.cell_padding_v_percentage = 0
50
- self.cell_padding_h_percentage = 0
51
- self.cell_padding_h = self.cell_width * self.cell_padding_h_percentage / _FACTOR
52
- self.cell_padding_v = (
53
- self.cell_height * self.cell_padding_v_percentage / _FACTOR
54
- )
55
- # Requires recalculation
56
- self.path = None
57
- self.preview_path = None
58
- self.outershape = None
59
- # Specifically for the shape pattern we hold a list of precalculated polygons
60
- self.pattern = []
61
- self._extend_patterns = True
62
- self.set_cell_values(100, 100)
63
- self.set_padding_values(50, 50)
64
- self.set_predefined_pattern(
65
- entry=(
66
- set_line,
67
- False,
68
- "",
69
- "",
70
- (-200, -350, 0, 0),
71
- True,
72
- )
73
- )
74
- self.cutpattern = None
75
-
76
- def set_predefined_pattern(self, entry):
77
- # The pattern needs to be defined within a 0,0 - 1,1 rectangle
78
- #
79
- self.cutpattern = entry
80
-
81
- self._extend_patterns = entry[5]
82
- additional_parameter = entry[1]
83
- info1 = entry[2]
84
- info2 = entry[3]
85
- self.pattern = list(
86
- entry[0](self.param_a, self.param_b, outershape=self.outershape)
87
- )
88
- self.path = None
89
- self.preview_path = None
90
- return additional_parameter, info1, info2
91
-
92
- def make_outline(self, x0, y0, x1, y1):
93
- # Draw a rectangle
94
- pt0 = Point(x0, y0)
95
- pt1 = Point(x1, y0)
96
- pt2 = Point(x1, y1)
97
- pt3 = Point(x0, y1)
98
-
99
- self.path.move(pt0)
100
- self.path.line(pt1)
101
- self.path.line(pt2)
102
- self.path.line(pt3)
103
- self.path.line(pt0)
104
-
105
- def draw_trace(self, offset_x, offset_y, width, height):
106
- # Draw the pattern
107
- # The extents of the cell will be at (offset_x, offset_y)
108
- # in the upper-left corner and (width, height) in the bottom-right corner
109
-
110
- def create_point(x, y):
111
- return Point(x * width + offset_x, y * height + offset_y)
112
-
113
- # self.path.move(offset_x, offset_y)
114
- # print (f"After initial move: {str(self.path)}")
115
- current_x = 0
116
- current_y = 0
117
- s_left = self.start_x
118
- s_right = s_left + self.width
119
- s_top = self.start_y
120
- s_bottom = s_top + self.height
121
- for entry in self.pattern:
122
- old_x = current_x
123
- old_y = current_y
124
- key = entry[0].lower()
125
- if key == "m":
126
- endpoint = create_point(entry[1], entry[2])
127
- self.path.move(endpoint)
128
- current_x = entry[1]
129
- current_y = entry[2]
130
- elif key == "h":
131
- current_x += entry[1]
132
- dx = entry[1]
133
- self.path.horizontal(dx, relative=True)
134
- elif key == "v":
135
- current_y += entry[1]
136
- dy = entry[1]
137
- self.path.vertical(dy, relative=True)
138
- elif key == "l":
139
- # Line to...
140
- current_x = entry[1]
141
- current_y = entry[2]
142
- endpoint = create_point(entry[1], entry[2])
143
- self.path.line(endpoint)
144
- elif key == "a":
145
- current_x = entry[6]
146
- current_y = entry[7]
147
- rx = entry[1]
148
- ry = entry[2]
149
- rotation = entry[3]
150
- arc = entry[4]
151
- sweep = entry[5]
152
- endpoint = create_point(current_x, current_y)
153
- self.path.arc(rx, ry, rotation, arc, sweep, endpoint)
154
- elif key == "c":
155
- current_x = entry[5]
156
- current_y = entry[6]
157
- control1 = create_point(entry[1], entry[2])
158
- control2 = create_point(entry[3], entry[4])
159
- endpoint = create_point(entry[5], entry[6])
160
- self.path.cubic(control1, control2, endpoint)
161
- elif key == "q":
162
- current_x = entry[3]
163
- current_y = entry[4]
164
- control1 = create_point(entry[1], entry[2])
165
- endpoint = create_point(entry[3], entry[4])
166
- self.path.quad(control1, endpoint)
167
-
168
- def set_hinge_shape(self, shapenode):
169
- # reset cache
170
- self.outershape = shapenode
171
-
172
- def set_hinge_area(self, hinge_left, hinge_top, hinge_width, hinge_height):
173
- self.start_x = hinge_left
174
- self.start_y = hinge_top
175
- self.width = hinge_width
176
- self.height = hinge_height
177
- self.x0 = hinge_width * self.gap
178
- self.y0 = hinge_height * self.gap
179
- self.x1 = hinge_width * (1 - self.gap)
180
- self.y1 = hinge_height * (1 - self.gap)
181
- # Requires recalculation
182
- self.path = None
183
- self.preview_path = None
184
-
185
- def set_cell_values(self, percentage_x, percentage_y):
186
- self.cell_width_percentage = percentage_x
187
- self.cell_height_percentage = percentage_y
188
- # Requires recalculation
189
- self.path = None
190
- self.preview_path = None
191
-
192
- def set_padding_values(self, padding_x, padding_y):
193
- self.cell_padding_h_percentage = padding_x
194
- self.cell_padding_v_percentage = padding_y
195
- # Requires recalculation
196
- self.path = None
197
- self.preview_path = None
198
-
199
- def set_additional_parameters(self, param_a, param_b):
200
- self.param_a = param_a
201
- self.param_b = param_b
202
- # Reset cache for shape pattern
203
- # Make sure pattern is updated with additional parameter
204
- self.set_predefined_pattern(self.cutpattern)
205
-
206
- @staticmethod
207
- def outside(bb_to_check, master_bb):
208
- out_x = "inside"
209
- out_y = "inside"
210
- if bb_to_check[0] > master_bb[2] or bb_to_check[2] < master_bb[0]:
211
- # fully out on x
212
- out_x = "outside"
213
- elif bb_to_check[0] < master_bb[0] or bb_to_check[2] > master_bb[2]:
214
- out_x = "cross"
215
- if bb_to_check[1] > master_bb[3] or bb_to_check[3] < master_bb[1]:
216
- out_y = "outside"
217
- elif bb_to_check[1] < master_bb[1] or bb_to_check[3] > master_bb[3]:
218
- out_x = "cross"
219
- return out_x, out_y
220
-
221
- def generate(self, show_outline=False, force=False, final=False, clip_bounds=True):
222
- if final and self.path is not None and not force:
223
- # No need to recalculate...
224
- return
225
- elif not final and self.preview_path is not None and not force:
226
- # No need to recalculate...
227
- return
228
-
229
- self.cell_width = self.width * self.cell_width_percentage / _FACTOR
230
- self.cell_height = self.height * self.cell_height_percentage / _FACTOR
231
- self.cell_padding_h = self.cell_width * self.cell_padding_h_percentage / _FACTOR
232
- self.cell_padding_v = (
233
- self.cell_height * self.cell_padding_v_percentage / _FACTOR
234
- )
235
- self.path = Path(stroke=Color("red"), stroke_width=500)
236
-
237
- if show_outline:
238
- self.make_outline(self.x0, self.y0, self.x1, self.y1)
239
-
240
- # Determine rows and columns of cuts to create
241
- # will round down so add 1 and trim later
242
- # Determine rows and columns of cuts to create
243
- # will round down so add 1 and trim later
244
- if self.cell_width + 2 * self.cell_padding_h == 0:
245
- cols = 1
246
- else:
247
- cols = (
248
- int(
249
- ((self.x1 - self.x0) + self.cell_width)
250
- / (self.cell_width + (2 * self.cell_padding_h))
251
- )
252
- + 1
253
- )
254
- if self.cell_height + 2 * self.cell_padding_v == 0:
255
- rows = 1
256
- else:
257
- rows = (
258
- int(
259
- ((self.y1 - self.y0) + self.cell_height)
260
- / (self.cell_height + (2 * self.cell_padding_v))
261
- )
262
- + 1
263
- )
264
-
265
- if self._extend_patterns:
266
- start_value = -2
267
- end_value = 1
268
- off_x = -1 * (self.cell_width / 2)
269
- else:
270
- cols = max(1, cols - 2)
271
- rows = max(1, rows - 2)
272
- start_value = 0
273
- end_value = 0
274
- off_x = 0
275
- # print (f"Area: {self.width:.1f}, {self.height:.1f}, Cell: {self.cell_width:.1f}, {self.cell_height:.1f}")
276
- # print (f"Rows: {rows}, Cols={cols}")
277
- # print (f"Ratios: {self.cell_width_percentage}, {self.cell_height_percentage}")
278
- # print (f"Padding: {self.cell_padding_h_percentage}, {self.cell_padding_v_percentage}")
279
- for col in range(start_value, cols + end_value, 1):
280
- top_left_x = self.x0 + off_x
281
- x_offset = col * (self.cell_width + (2 * self.cell_padding_h))
282
- x_current = top_left_x + x_offset
283
- for row in range(start_value, rows + end_value, 1):
284
- top_left_y = self.y0
285
- y_offset = row * (self.cell_height + (2 * self.cell_padding_v)) + (
286
- (self.cell_height + (2 * self.cell_padding_v)) / 2
287
- ) * (col % 2)
288
- y_current = top_left_y + y_offset
289
-
290
- if x_current < self.x1 and y_current < self.y1:
291
- # Don't call draw if outside of hinge area
292
- self.draw_trace(
293
- x_current,
294
- y_current,
295
- self.cell_width,
296
- self.cell_height,
297
- )
298
- if show_outline:
299
- self.make_outline(
300
- x_current,
301
- y_current,
302
- x_current + self.cell_width,
303
- y_current + self.cell_height,
304
- )
305
- if not clip_bounds:
306
- return
307
- rectangular = True
308
- if (
309
- self.outershape is not None
310
- and hasattr(self.outershape, "as_path")
311
- and self.outershape.type != "elem rect"
312
- ):
313
- rectangular = False
314
- if final and not rectangular:
315
- self.path.transform *= Matrix.translate(self.start_x, self.start_y)
316
- from time import time
317
-
318
- t0 = time()
319
- vm = VectorMontonizer()
320
- outer_bb = self.outershape.bbox()
321
- outer_path = self.outershape.as_path()
322
- outer_poly = Polygon(
323
- [outer_path.point(i / 1000.0, error=1e4) for i in range(1001)]
324
- )
325
- vm.add_polyline(outer_poly)
326
- path = Path(stroke=Color("red"), stroke_width=500)
327
- deleted = 0
328
- total = 0
329
- # pt_min_x = 1E+30
330
- # pt_min_y = 1E+30
331
- # pt_max_x = -1 * pt_min_x
332
- # pt_max_y = -1 * pt_min_y
333
- # Numpy does not work
334
- # vm.add_polyline(outer_poly)
335
- # path = Path(stroke=Color("red"), stroke_width=500)
336
- # for sub_inner in self.path.as_subpaths():
337
- # sub_inner = Path(sub_inner)
338
- # pts_sub = sub_inner.npoint(linspace(0, 1, 1000))
339
- # good_pts = [p for p in pts_sub if vm.is_point_inside(p[0] + self.start_x, p[1] + self.start_y)]
340
- # path += Path(Polyline(good_pts), stroke=Color("red"), stroke_width=500)
341
- for sub_inner in self.path.as_subpaths():
342
- sub_bbox = sub_inner.bbox()
343
- outx, outy = self.outside(sub_bbox, outer_bb)
344
- if outx == "outside" or outy == "outside":
345
- continue
346
-
347
- sub_inner = Path(sub_inner)
348
- pts_sub = [sub_inner.point(i / 1000.0, error=1e4) for i in range(1001)]
349
- fullyinside = True
350
- for i in range(len(pts_sub) - 1, -1, -1):
351
- total += 1
352
- pt = pts_sub[i]
353
- pt[0] += self.start_x
354
- pt[1] += self.start_y
355
- # pt_min_x = min(pt_min_x, pt[0])
356
- # pt_min_y = min(pt_min_y, pt[1])
357
- # pt_max_x = max(pt_max_x, pt[0])
358
- # pt_max_y = max(pt_max_y, pt[1])
359
- if not vm.is_point_inside(pt[0], pt[1]):
360
- # if we do have points beyond, then we create a seperate path
361
- if i < len(pts_sub) - 1:
362
- goodpts = pts_sub[i + 1 :]
363
- path += Path(
364
- Polyline(goodpts), stroke=Color("red"), stroke_width=500
365
- )
366
- del pts_sub[i:]
367
- deleted += 1
368
- fullyinside = False
369
- if fullyinside:
370
- path += abs(sub_inner)
371
- else:
372
- path += Path(
373
- Polyline(pts_sub), stroke=Color("red"), stroke_width=500
374
- )
375
- self.path = path
376
- else:
377
- # Former method....
378
- # ...is limited to rectangular area but maintains inner cubics,
379
- # quads and arcs while the vectormontonizer is more versatile
380
- # when it comes to the surrounding shape but transforms all
381
- # path elements to lines
382
- self.path = self.clip_path(self.path, 0, 0, self.width, self.height)
383
- self.path.transform *= Matrix.translate(self.start_x, self.start_y)
384
- self.preview_path = copy(self.path)
385
-
386
- def clip_path(self, path, xmin, ymin, xmax, ymax):
387
- """
388
- Clip a path at a rectangular area, will return the clipped path
389
-
390
- Args:
391
- path : The path to clip
392
- xmin : Left side of the rectangular area
393
- ymin : Upper side of the rectangular area
394
- xmax : Right side of the rectangular area
395
- ymax : Lower side of the rectangular area
396
- """
397
-
398
- def approximate_line(part_of_path, current_x, current_y):
399
- # print(f"Check: {type(part_of_path).__name__} {part_of_path.bbox()} {clipbb}")
400
- from numpy import linspace
401
-
402
- added = 0
403
- partial = 0
404
- ignored = 0
405
- subj = part_of_path.npoint(linspace(0, 1, interpolation))
406
- subj.reshape((2, interpolation))
407
- iterated_points = list(map(Point, subj))
408
- for p in iterated_points:
409
- segbb = (
410
- min(current_x, p[0]),
411
- min(current_y, p[1]),
412
- max(current_x, p[0]),
413
- max(current_y, p[1]),
414
- )
415
- sx, sy = self.outside(segbb, clipbb)
416
- # print(f"{segbb} - {clipbb} {sx} - {sy}")
417
- if sx == "outside" or sy == "outside":
418
- # Fully outside, so drop
419
- add_move(newpath, e.end)
420
- ignored += 1
421
- elif statex == "inside" and statey == "inside":
422
- # Fully inside, so append
423
- if current_x != new_cx or current_y != new_cy:
424
- add_move(newpath, Point(current_x, current_y))
425
- newpath.line(p)
426
- added += 1
427
- else:
428
- dx = p[0] - current_x
429
- dy = p[1] - current_y
430
- new_cx = current_x
431
- new_cy = current_y
432
- new_ex = p[0]
433
- new_ey = p[1]
434
- if dx == 0:
435
- # Vertical line needs special treatment
436
- if xmin <= new_cx <= xmax:
437
- new_cy = min(max(new_cy, ymin), ymax)
438
- new_ey = min(max(new_ey, ymin), ymax)
439
- if new_cx != current_x or new_cy != current_y:
440
- # Needs a move
441
- add_move(newpath, Point(new_cx, new_cy))
442
- newpath.line(Point(new_ex, new_ey))
443
- partial += 1
444
- else:
445
- ignored += 1
446
- else:
447
- # regular line, so lets establish x0 x1
448
- # could still be an outward pointing line....
449
- new_cx = min(max(new_cx, xmin), xmax)
450
- new_ex = min(max(new_ex, xmin), xmax)
451
- # corresponding y values...
452
- edx = p[0] - current_x
453
- edy = p[1] - current_y
454
- new_cy = current_y + (new_cx - current_x) / edx * edy
455
- new_ey = current_y + (new_ex - current_x) / edx * edy
456
- # Y can still cross...
457
- new_cx_clipped = new_cx
458
- new_ex_clipped = new_ex
459
- new_cy_clipped = min(max(new_cy, ymin), ymax)
460
- new_ey_clipped = min(max(new_ey, ymin), ymax)
461
- # Adjust x - value
462
- if dy != 0:
463
- new_cx_clipped = new_cx + dx / dy * (
464
- new_cy_clipped - new_cy
465
- )
466
- new_ex_clipped = new_ex + dx / dy * (
467
- new_ey_clipped - new_ey
468
- )
469
-
470
- new_cx = new_cx_clipped
471
- new_cy = new_cy_clipped
472
- new_ex = new_ex_clipped
473
- new_ey = new_ey_clipped
474
- if min(new_cy, new_ey) == ymax and dy != 0:
475
- # Outward...
476
- ignored += 1
477
- elif max(new_cy, new_ey) == ymin and dy != 0:
478
- # Outward...
479
- ignored += 1
480
- else:
481
- if new_cx != current_x or new_cy != current_y:
482
- # Needs a move
483
- add_move(newpath, Point(new_cx, new_cy))
484
- newpath.line(Point(new_ex, new_ey))
485
- partial += 1
486
- current_x = p[0]
487
- current_y = p[1]
488
- if current_x != part_of_path.end[0] or current_y != part_of_path.end[1]:
489
- add_move(newpath, part_of_path.end)
490
- # print (f"From iterated line: added={added}, partial={partial}, ignored={ignored}")
491
-
492
- def add_move(addpath, destination):
493
- # Was the last segment as well a move? Then just update the coords...
494
- if len(addpath) > 0:
495
- if isinstance(addpath[-1], Move):
496
- addpath[-1].end = destination
497
- return
498
- addpath.move(destination)
499
-
500
- interpolation = 50
501
- fully_deleted = 0
502
- partial_deleted = 0
503
- not_deleted = 0
504
- clipbb = (xmin, ymin, xmax, ymax)
505
- current_x = 0
506
- current_y = 0
507
- first_point = path.first_point
508
- if first_point is not None:
509
- current_x = first_point[0]
510
- current_y = first_point[1]
511
- newpath = Path(
512
- stroke=path.stroke, stroke_width=path.stroke_width, transform=path.transform
513
- )
514
- for e in path:
515
- if hasattr(e, "bbox"):
516
- segbb = e.bbox()
517
- elif hasattr(e, "end"):
518
- segbb = (
519
- min(current_x, e.end[0]),
520
- min(current_y, e.end[1]),
521
- max(current_x, e.end[0]),
522
- max(current_y, e.end[1]),
523
- )
524
- else:
525
- segbb = (xmin, ymin, 0, 0)
526
- if isinstance(e, Move):
527
- add_move(newpath, e.end)
528
- current_x = e.end[0]
529
- current_y = e.end[1]
530
- not_deleted += 1
531
- elif isinstance(e, Line):
532
- statex, statey = self.outside(segbb, clipbb)
533
- dx = e.end[0] - current_x
534
- dy = e.end[1] - current_y
535
- if statex == "outside" or statey == "outside":
536
- # Fully outside, so drop
537
- add_move(newpath, e.end)
538
- fully_deleted += 1
539
- elif statex == "inside" and statey == "inside":
540
- # Fully inside, so append
541
- newpath.line(e.end)
542
- not_deleted += 1
543
- else:
544
- # needs dealing, its either for the time being, just ignored...
545
- new_cx = current_x
546
- new_cy = current_y
547
- new_ex = e.end[0]
548
- new_ey = e.end[1]
549
- if dx == 0:
550
- # Vertical line needs special treatment
551
- if xmin <= new_cx <= xmax:
552
- new_cy = min(max(new_cy, ymin), ymax)
553
- new_ey = min(max(new_ey, ymin), ymax)
554
- if new_cx != current_x or new_cy != current_y:
555
- # Needs a move
556
- add_move(newpath, Point(new_cx, new_cy))
557
- newpath.line(Point(new_ex, new_ey))
558
- else:
559
- # regular line, so lets establish x0 x1
560
- # could still be an outward pointing line....
561
- new_cx = min(max(new_cx, xmin), xmax)
562
- new_ex = min(max(new_ex, xmin), xmax)
563
- # corresponding y values...
564
- edx = e.end[0] - current_x
565
- edy = e.end[1] - current_y
566
- new_cy = current_y + (new_cx - current_x) / edx * edy
567
- new_ey = current_y + (new_ex - current_x) / edx * edy
568
- # Y can still cross...
569
- new_cx_clipped = new_cx
570
- new_ex_clipped = new_ex
571
- new_cy_clipped = min(max(new_cy, ymin), ymax)
572
- new_ey_clipped = min(max(new_ey, ymin), ymax)
573
- # Adjust x - value
574
- if dy != 0:
575
- new_cx_clipped = new_cx + dx / dy * (
576
- new_cy_clipped - new_cy
577
- )
578
- new_ex_clipped = new_ex + dx / dy * (
579
- new_ey_clipped - new_ey
580
- )
581
-
582
- new_cx = new_cx_clipped
583
- new_cy = new_cy_clipped
584
- new_ex = new_ex_clipped
585
- new_ey = new_ey_clipped
586
- if min(new_cy, new_ey) == ymax and dy != 0:
587
- # Outward...
588
- pass
589
- elif max(new_cy, new_ey) == ymin and dy != 0:
590
- # Outward...
591
- pass
592
- else:
593
- if new_cx != current_x or new_cy != current_y:
594
- # Needs a move
595
- add_move(newpath, Point(new_cx, new_cy))
596
- newpath.line(Point(new_ex, new_ey))
597
- if current_x != e.end[0] or current_y != e.end[1]:
598
- add_move(newpath, e.end)
599
- partial_deleted += 1
600
- current_x = e.end[0]
601
- current_y = e.end[1]
602
- elif isinstance(e, Close):
603
- newpath.closed()
604
- not_deleted += 1
605
- elif isinstance(e, QuadraticBezier):
606
- statex, statey = self.outside(segbb, clipbb)
607
- if statex == "outside" and statey == "outside":
608
- # Fully outside, so drop
609
- add_move(newpath, e.end)
610
- fully_deleted += 1
611
- elif statex == "inside" and statey == "inside":
612
- # Fully inside, so append
613
- newpath.quad(e.control, e.end)
614
- not_deleted += 1
615
- else:
616
- approximate_line(e, current_x, current_y)
617
- current_x = e.end[0]
618
- current_y = e.end[1]
619
- elif isinstance(e, CubicBezier):
620
- statex, statey = self.outside(segbb, clipbb)
621
- if statex == "outside" and statey == "outside":
622
- # Fully outside, so drop
623
- add_move(newpath, e.end)
624
- fully_deleted += 1
625
- elif statex == "inside" and statey == "inside":
626
- # Fully inside, so append
627
- newpath.cubic(e.control1, e.control2, e.end)
628
- not_deleted += 1
629
- else:
630
- approximate_line(e, current_x, current_y)
631
- partial_deleted += 1
632
- current_x = e.end[0]
633
- current_y = e.end[1]
634
- elif isinstance(e, Arc):
635
- for e_cubic in e.as_cubic_curves():
636
- segbb = e_cubic.bbox()
637
- statex, statey = self.outside(segbb, clipbb)
638
- if statex == "outside" and statey == "outside":
639
- # Fully outside, so drop
640
- add_move(newpath, e.end)
641
- fully_deleted += 1
642
- elif statex == "inside" and statey == "inside":
643
- # Fully inside, so append
644
- newpath.cubic(e_cubic.control1, e_cubic.control2, e_cubic.end)
645
- not_deleted += 1
646
- else:
647
- approximate_line(e_cubic, current_x, current_y)
648
- partial_deleted += 1
649
- current_x = e_cubic.end[0]
650
- current_y = e_cubic.end[1]
651
- current_x = e.end[0]
652
- current_y = e.end[1]
653
-
654
- flag = True
655
- while flag:
656
- flag = False
657
- if len(newpath) > 0 and isinstance(newpath[-1], Move):
658
- # We don't need a move at the end of the path...
659
- del newpath[-1]
660
- flag = True
661
-
662
- # print(
663
- # f"Ready: left untouched: {not_deleted}, fully deleted={fully_deleted}, partial deletion:{partial_deleted}"
664
- # )
665
- return newpath
666
-
667
-
668
- def set_line(*args, **kwargs):
669
- yield "M", 0, 0.5
670
- yield "L", 1, 0.5
671
-
672
-
673
- def set_fishbone(a, b, *args, **kwargs):
674
- dx = a / 5.0 * 0.5
675
- dy = b / 5.0 * 0.5
676
- yield "M", 0 + dx, 1 - dy
677
- yield "L", 0.5, 0
678
- # self.pattern.append(("M", 0.5, 0))
679
- yield "L", 1 - dx, 1 - dy
680
-
681
-
682
- def set_diagonal(a, b, *args, **kwargs):
683
- dx = a / 5.0 * 1.0
684
- dy = b / 5.0 * 1.0
685
- yield "M", 0 + dx, 1 - dy
686
- yield "L", 1 - dx, 0 + dy
687
-
688
-
689
- def set_diamond1(a, b, *args, **kwargs):
690
- yield "M", 0, 0.5
691
- yield "L", 0.5, 0
692
- yield "L", 1, 0.5
693
- yield "L", 0.5, 1
694
- yield "L", 0, 0.5
695
-
696
-
697
- def set_diamond2(a, b, *args, **kwargs):
698
- yield "M", 0, 0
699
- yield "L", 0.5, 0.4
700
- yield "L", 1, 0
701
- yield "M", 0, 1
702
- yield "L", 0.5, 0.6
703
- yield "L", 1, 1
704
-
705
-
706
- def set_cross(a, b, *args, **kwargs):
707
- # Pattern: cross
708
- dx = a / 5.0 * 0.5
709
- dy = b / 5.0 * 0.5
710
- yield "M", 0.0, 0.25 + dy
711
- yield "L", 0.25 + dx, 0.50
712
- yield "L", 0.0, 0.75 - dy
713
- yield "M", 0.25 + dx, 0.50
714
- yield "L", 0.75 - dx, 0.50
715
- yield "M", 1, 0.25 + dy
716
- yield "L", 0.75 - dx, 0.50
717
- yield "L", 1, 0.75 - dy
718
-
719
-
720
- def set_fabric(a, b, *args, **kwargs):
721
- yield "M", 0.25, 0.25
722
- yield "L", 0, 0.25
723
- yield "L", 0, 0
724
- yield "L", 0.5, 0
725
- yield "L", 0.5, 1
726
- yield "L", 1, 1
727
- yield "L", 1, 0.75
728
- yield "L", 0.75, 0.75
729
-
730
- yield "M", 0.75, 0.25
731
- yield "L", 0.75, 0
732
- yield "L", 1, 0
733
- yield "L", 1, 0.5
734
- yield "L", 0, 0.5
735
- yield "L", 0, 1
736
- yield "L", 0.25, 1
737
- yield "L", 0.25, 0.75
738
-
739
-
740
- def set_beehive(a, b, *args, **kwargs):
741
- dx = a / 5.0 * 0.5
742
- dy = b / 5.0 * 0.5
743
- # top
744
- yield "M", 0, 0.5 - dy
745
- yield "L", dx, dy
746
- yield "L", 1 - dx, dy
747
- yield "L", 1, 0.5 - dy
748
- # inner
749
- yield "M", 0, 0.5
750
- yield "L", dx, 2 * dy
751
- yield "L", 1 - dx, 2 * dy
752
- yield "L", 1, 0.5
753
- yield "L", 1 - dx, 1 - 2 * dy
754
- yield "L", dx, 1 - 2 * dy
755
- yield "L", 0, 0.5
756
- # bottom
757
- yield "M", 0, 0.5 + dy
758
- yield "L", dx, 1 - dy
759
- yield "L", 1 - dx, 1 - dy
760
- yield "L", 1, 0.5 + dy
761
-
762
-
763
- def set_bowlingpin(a, b, *args, **kwargs):
764
- yield "M", 0.2, 0.6
765
- ctrl_x = 0.1 + a
766
- ctrl_y = 0.5
767
- yield "Q", ctrl_x, ctrl_y, 0.2, 0.4
768
-
769
- ctrl_x = 0.5
770
- ctrl_y = 0.1 - b
771
- yield "Q", ctrl_x, ctrl_y, 0.8, 0.4
772
-
773
- ctrl_x = 0.9 - a
774
- ctrl_y = 0.5
775
- yield "Q", ctrl_x, ctrl_y, 0.8, 0.6
776
-
777
- ctrl_x = 0.5
778
- ctrl_y = 0.9 + b
779
- yield "Q", ctrl_x, ctrl_y, 0.2, 0.6
780
-
781
-
782
- def set_wave(a, b, *args, **kwargs):
783
- # Pattern: wavy
784
- yield "M", 0.0, 0.25
785
- yield "L", 0.25, 0.25
786
- ctrl_x = 0.5 + a
787
- ctrl_y = 0.25 + b
788
- yield "Q", ctrl_x, ctrl_y, 0.5, 0.5
789
- ctrl_x = 0.5 - a
790
- ctrl_y = 0.75 - b
791
- yield "Q", ctrl_x, ctrl_y, 0.75, 0.75
792
- yield "L", 1, 0.75
793
-
794
-
795
- def set_bezier(a, b, *args, **kwargs):
796
- anchor_tip = a # distance factor from anchor to place control point
797
- anchor_center = b
798
- yield "M", 0, 0
799
- yield "C", 1 * anchor_tip, 0, 1 / 2 - (1 * anchor_center), 1, 1 / 2, 1
800
- yield "C", 1 / 2 + (1 * anchor_center), 1, 1 * (1 - anchor_tip), 0, 1, 0
801
-
802
-
803
- def set_brackets(a, b, *args, **kwargs):
804
- p_a = a
805
- p_b = b
806
- yield "M", 0.0, 0.5
807
- yield "C", 0.0, p_a, 1.0, p_b, 1.0, 0.5
808
- yield "C", 1.0, 1 - p_a, 0.0, 1 - p_b, 0.0, 0.5
809
-
810
-
811
- def set_shape(a, b, *args, outershape=None, **kwargs):
812
- # concentric shapes
813
- polycache = list()
814
-
815
- if len(polycache) != 0:
816
- # We've done our bit already
817
- return
818
- resolution = 200.0
819
- if outershape is None:
820
- shape = Path(Polyline((0, 0), (1, 0), (1, 1), (0, 1), (0, 0)))
821
- else:
822
- shape = outershape.as_path()
823
- bb = shape.bbox()
824
- wd = bb[2] - bb[0]
825
- if wd == 0:
826
- wd = 1
827
- ht = bb[3] - bb[1]
828
- if ht == 0:
829
- ht = 1
830
- tx = 0
831
- ty = 0
832
- tc = int(resolution)
833
- # minx = miny = 1e18
834
- # maxx = maxy = -1e18
835
- # Convert to polygon and bring it to 0 / 1
836
- if wd == 0:
837
- ratiox = 1
838
- else:
839
- ratiox = 1 / wd
840
-
841
- if ht == 0:
842
- ratioy = 1
843
- else:
844
- ratioy = 1 / ht
845
- amount = int(abs(10 * a)) # (1 to 50)
846
- segments = int(abs(10 * b))
847
- seg_break = int(resolution) / (segments + 1)
848
- seg_len = resolution / 40.0
849
- for i in range(int(resolution) + 1):
850
- pt = shape.point(i / resolution, error=1e4)
851
- pt[0] = (pt[0] - bb[0]) * ratiox
852
- pt[1] = (pt[1] - bb[1]) * ratioy
853
- xx = pt[0]
854
- yy = pt[1]
855
- tx += xx
856
- ty += yy
857
- # minx = min(minx, xx)
858
- # miny = min(miny, yy)
859
- # maxx = max(maxx, xx)
860
- # maxy = max(maxy, yy)
861
- polycache.append(pt)
862
- geometric_center_x = tx / tc
863
- geometric_center_y = ty / tc
864
- # print(
865
- # f"geometric center master: {geometric_center_x:.1f}, {geometric_center_y:.1f}"
866
- # )
867
- # print(f"boundaries: {minx:.1f}, {miny:.1f} - {maxx:.1f}, {maxy:.1f}")
868
- dx = 0
869
- dy = 0
870
- regular = False
871
- ratio = 1.0
872
- dx = 1.0 / (amount + 1)
873
-
874
- ratio = 1
875
- for num in range(amount):
876
- ratio -= dx
877
- regular = not regular
878
- current_x = None
879
- current_y = None
880
- if regular:
881
- segcount = int(seg_break * 0.25)
882
- else:
883
- segcount = int(seg_break * 0.5)
884
- # tx = 0
885
- # ty = 0
886
- # minx = miny = 1e18
887
- # maxx = maxy = -1e18
888
- for i in range(int(resolution) + 1):
889
- xx = (polycache[i][0] - geometric_center_x) * ratio + geometric_center_x
890
- yy = (polycache[i][1] - geometric_center_y) * ratio + geometric_center_y
891
- # tx += xx
892
- # ty += yy
893
- # minx = min(minx, xx)
894
- # miny = min(miny, yy)
895
- # maxx = max(maxx, xx)
896
- # maxy = max(maxy, yy)
897
- segcount += 1
898
- if segcount < seg_break:
899
- if current_x is None:
900
- yield "M", xx, yy
901
- else:
902
- yield "L", xx, yy
903
- current_x = xx
904
- current_y = yy
905
- elif segcount >= seg_break + seg_len:
906
- segcount = 0
907
- current_x = None
908
- current_y = None
909
- # geo_x = tx / tc
910
- # geo_y = ty / tc
911
- # print(f"geometric center copy: {geo_x:.1f}, {geo_y:.1f}")
912
- # print(f"boundaries: {minx:.1f}, {miny:.1f} - {maxx:.1f}, {maxy:.1f}")
913
-
914
-
915
- def plugin(kernel, lifecycle):
916
- if lifecycle == "register":
917
- _ = kernel.translation
918
- context = kernel.root
919
- context.register(
920
- "pattern/line",
921
- (
922
- set_line,
923
- False,
924
- "",
925
- "",
926
- (-200, -350, 0, 0),
927
- True,
928
- ),
929
- )
930
- context.register(
931
- "pattern/fishbone",
932
- (
933
- set_fishbone,
934
- True,
935
- "Left/Right Indentation",
936
- "Top/Bottom Indentation",
937
- (100, 100, 0, 0),
938
- True,
939
- ),
940
- )
941
- context.register(
942
- "pattern/diagonal",
943
- (
944
- set_diagonal,
945
- True,
946
- "Left/Right Indentation",
947
- "Top/Bottom Indentation",
948
- (-100, -100, 0, 0),
949
- True,
950
- ),
951
- )
952
- context.register(
953
- "pattern/diamond1",
954
- (
955
- set_diamond1,
956
- False,
957
- "",
958
- "",
959
- (-150, 100, 0, 0),
960
- True,
961
- ),
962
- )
963
- context.register(
964
- "pattern/diamond2",
965
- (
966
- set_diamond2,
967
- False,
968
- "",
969
- "",
970
- (-120, 60, 0, 0),
971
- True,
972
- ),
973
- )
974
- context.register(
975
- "pattern/cross",
976
- (
977
- set_cross,
978
- True,
979
- "Left/Right Indentation",
980
- "Top/Bottom Indentation",
981
- (-150, -40, 0, 0),
982
- True,
983
- ),
984
- )
985
- context.register(
986
- "pattern/bezier",
987
- (
988
- set_bezier,
989
- True,
990
- "",
991
- "",
992
- (-20, -160, 0.4, 0.3),
993
- True,
994
- ),
995
- )
996
- context.register(
997
- "pattern/wave",
998
- (
999
- set_wave,
1000
- True,
1001
- "",
1002
- "",
1003
- (-130, -260, 0, 0),
1004
- True,
1005
- ),
1006
- )
1007
- context.register(
1008
- "pattern/bowlingpin",
1009
- (
1010
- set_bowlingpin,
1011
- True,
1012
- "Left/Right Bowl",
1013
- "Top/Bottom Bowl",
1014
- (-210, -50, -0.3, 0),
1015
- True,
1016
- ),
1017
- )
1018
- context.register(
1019
- "pattern/beehive",
1020
- (
1021
- set_beehive,
1022
- True,
1023
- "Position of left side",
1024
- "Distance of second line",
1025
- (-10, 60, 1.4, 0),
1026
- True,
1027
- ),
1028
- )
1029
- context.register(
1030
- "pattern/fabric",
1031
- (
1032
- set_fabric,
1033
- False,
1034
- "",
1035
- "",
1036
- (-180, 130, 0, 0),
1037
- True,
1038
- ),
1039
- )
1040
- context.register(
1041
- "pattern/brackets",
1042
- (
1043
- set_brackets,
1044
- True,
1045
- "",
1046
- "",
1047
- (-140, -110, 0.7, 0.7),
1048
- True,
1049
- ),
1050
- )
1051
- context.register(
1052
- "pattern/shape",
1053
- (
1054
- set_shape,
1055
- True,
1056
- "Number of copies",
1057
- "Number of segments",
1058
- (0, 0, 1, 0.4),
1059
- False,
1060
- ),
1061
- )
1
+ # """
2
+ # Obsolete! No longer needed, just there for reference
3
+ # """
4
+ # from copy import copy
5
+ #
6
+ # from meerk40t.svgelements import (
7
+ # Arc,
8
+ # Close,
9
+ # Color,
10
+ # CubicBezier,
11
+ # Line,
12
+ # Matrix,
13
+ # Move,
14
+ # Path,
15
+ # Point,
16
+ # Polygon,
17
+ # Polyline,
18
+ # QuadraticBezier,
19
+ # )
20
+ # from meerk40t.tools.pathtools import VectorMontonizer
21
+ #
22
+ # _FACTOR = 1000
23
+ #
24
+ #
25
+ # class LivingHinges:
26
+ # """
27
+ # This class generates a predefined pattern in a *rectangular* area
28
+ # """
29
+ #
30
+ # def __init__(self, xpos, ypos, width, height):
31
+ # self.pattern = None
32
+ # self.start_x = xpos
33
+ # self.start_y = ypos
34
+ # self.width = width
35
+ # self.height = height
36
+ # # We set it off somewhat...
37
+ # self.gap = 0
38
+ # self.x0 = width * self.gap
39
+ # self.y0 = height * self.gap
40
+ # self.x1 = width * (1 - self.gap)
41
+ # self.y1 = height * (1 - self.gap)
42
+ # # Declare all used variables to satisfy codacy
43
+ # self.param_a = 0
44
+ # self.param_b = 0
45
+ # self.cell_height_percentage = 200
46
+ # self.cell_width_percentage = 200
47
+ # self.cell_height = height * self.cell_height_percentage / _FACTOR
48
+ # self.cell_width = width * self.cell_width_percentage / _FACTOR
49
+ # self.cell_padding_v_percentage = 0
50
+ # self.cell_padding_h_percentage = 0
51
+ # self.cell_padding_h = self.cell_width * self.cell_padding_h_percentage / _FACTOR
52
+ # self.cell_padding_v = (
53
+ # self.cell_height * self.cell_padding_v_percentage / _FACTOR
54
+ # )
55
+ # # Requires recalculation
56
+ # self.path = None
57
+ # self.preview_path = None
58
+ # self.outershape = None
59
+ # # Specifically for the shape pattern we hold a list of precalculated polygons
60
+ # self.pattern = []
61
+ # self._extend_patterns = True
62
+ # self.set_cell_values(100, 100)
63
+ # self.set_padding_values(50, 50)
64
+ # self.set_predefined_pattern(
65
+ # entry=(
66
+ # set_line,
67
+ # False,
68
+ # "",
69
+ # "",
70
+ # (-200, -350, 0, 0),
71
+ # True,
72
+ # )
73
+ # )
74
+ # self.cutpattern = None
75
+ #
76
+ # def set_predefined_pattern(self, entry):
77
+ # # The pattern needs to be defined within a 0,0 - 1,1 rectangle
78
+ # #
79
+ # self.cutpattern = entry
80
+ #
81
+ # self._extend_patterns = entry[5]
82
+ # additional_parameter = entry[1]
83
+ # info1 = entry[2]
84
+ # info2 = entry[3]
85
+ # self.pattern = list(
86
+ # entry[0](self.param_a, self.param_b, outershape=self.outershape)
87
+ # )
88
+ # self.path = None
89
+ # self.preview_path = None
90
+ # return additional_parameter, info1, info2
91
+ #
92
+ # def make_outline(self, x0, y0, x1, y1):
93
+ # # Draw a rectangle
94
+ # pt0 = Point(x0, y0)
95
+ # pt1 = Point(x1, y0)
96
+ # pt2 = Point(x1, y1)
97
+ # pt3 = Point(x0, y1)
98
+ #
99
+ # self.path.move(pt0)
100
+ # self.path.line(pt1)
101
+ # self.path.line(pt2)
102
+ # self.path.line(pt3)
103
+ # self.path.line(pt0)
104
+ #
105
+ # def draw_trace(self, offset_x, offset_y, width, height):
106
+ # # Draw the pattern
107
+ # # The extents of the cell will be at (offset_x, offset_y)
108
+ # # in the upper-left corner and (width, height) in the bottom-right corner
109
+ #
110
+ # def create_point(x, y):
111
+ # return Point(x * width + offset_x, y * height + offset_y)
112
+ #
113
+ # # self.path.move(offset_x, offset_y)
114
+ # # print (f"After initial move: {str(self.path)}")
115
+ # current_x = 0
116
+ # current_y = 0
117
+ # s_left = self.start_x
118
+ # s_right = s_left + self.width
119
+ # s_top = self.start_y
120
+ # s_bottom = s_top + self.height
121
+ # for entry in self.pattern:
122
+ # old_x = current_x
123
+ # old_y = current_y
124
+ # key = entry[0].lower()
125
+ # if key == "m":
126
+ # endpoint = create_point(entry[1], entry[2])
127
+ # self.path.move(endpoint)
128
+ # current_x = entry[1]
129
+ # current_y = entry[2]
130
+ # elif key == "h":
131
+ # current_x += entry[1]
132
+ # dx = entry[1]
133
+ # self.path.horizontal(dx, relative=True)
134
+ # elif key == "v":
135
+ # current_y += entry[1]
136
+ # dy = entry[1]
137
+ # self.path.vertical(dy, relative=True)
138
+ # elif key == "l":
139
+ # # Line to...
140
+ # current_x = entry[1]
141
+ # current_y = entry[2]
142
+ # endpoint = create_point(entry[1], entry[2])
143
+ # self.path.line(endpoint)
144
+ # elif key == "a":
145
+ # current_x = entry[6]
146
+ # current_y = entry[7]
147
+ # rx = entry[1]
148
+ # ry = entry[2]
149
+ # rotation = entry[3]
150
+ # arc = entry[4]
151
+ # sweep = entry[5]
152
+ # endpoint = create_point(current_x, current_y)
153
+ # self.path.arc(rx, ry, rotation, arc, sweep, endpoint)
154
+ # elif key == "c":
155
+ # current_x = entry[5]
156
+ # current_y = entry[6]
157
+ # control1 = create_point(entry[1], entry[2])
158
+ # control2 = create_point(entry[3], entry[4])
159
+ # endpoint = create_point(entry[5], entry[6])
160
+ # self.path.cubic(control1, control2, endpoint)
161
+ # elif key == "q":
162
+ # current_x = entry[3]
163
+ # current_y = entry[4]
164
+ # control1 = create_point(entry[1], entry[2])
165
+ # endpoint = create_point(entry[3], entry[4])
166
+ # self.path.quad(control1, endpoint)
167
+ #
168
+ # def set_hinge_shape(self, shapenode):
169
+ # # reset cache
170
+ # self.outershape = shapenode
171
+ #
172
+ # def set_hinge_area(self, hinge_left, hinge_top, hinge_width, hinge_height):
173
+ # self.start_x = hinge_left
174
+ # self.start_y = hinge_top
175
+ # self.width = hinge_width
176
+ # self.height = hinge_height
177
+ # self.x0 = hinge_width * self.gap
178
+ # self.y0 = hinge_height * self.gap
179
+ # self.x1 = hinge_width * (1 - self.gap)
180
+ # self.y1 = hinge_height * (1 - self.gap)
181
+ # # Requires recalculation
182
+ # self.path = None
183
+ # self.preview_path = None
184
+ #
185
+ # def set_cell_values(self, percentage_x, percentage_y):
186
+ # self.cell_width_percentage = percentage_x
187
+ # self.cell_height_percentage = percentage_y
188
+ # # Requires recalculation
189
+ # self.path = None
190
+ # self.preview_path = None
191
+ #
192
+ # def set_padding_values(self, padding_x, padding_y):
193
+ # self.cell_padding_h_percentage = padding_x
194
+ # self.cell_padding_v_percentage = padding_y
195
+ # # Requires recalculation
196
+ # self.path = None
197
+ # self.preview_path = None
198
+ #
199
+ # def set_additional_parameters(self, param_a, param_b):
200
+ # self.param_a = param_a
201
+ # self.param_b = param_b
202
+ # # Reset cache for shape pattern
203
+ # # Make sure pattern is updated with additional parameter
204
+ # self.set_predefined_pattern(self.cutpattern)
205
+ #
206
+ # @staticmethod
207
+ # def outside(bb_to_check, master_bb):
208
+ # out_x = "inside"
209
+ # out_y = "inside"
210
+ # if bb_to_check[0] > master_bb[2] or bb_to_check[2] < master_bb[0]:
211
+ # # fully out on x
212
+ # out_x = "outside"
213
+ # elif bb_to_check[0] < master_bb[0] or bb_to_check[2] > master_bb[2]:
214
+ # out_x = "cross"
215
+ # if bb_to_check[1] > master_bb[3] or bb_to_check[3] < master_bb[1]:
216
+ # out_y = "outside"
217
+ # elif bb_to_check[1] < master_bb[1] or bb_to_check[3] > master_bb[3]:
218
+ # out_x = "cross"
219
+ # return out_x, out_y
220
+ #
221
+ # def generate(self, show_outline=False, force=False, final=False, clip_bounds=True):
222
+ # if final and self.path is not None and not force:
223
+ # # No need to recalculate...
224
+ # return
225
+ # elif not final and self.preview_path is not None and not force:
226
+ # # No need to recalculate...
227
+ # return
228
+ #
229
+ # self.cell_width = self.width * self.cell_width_percentage / _FACTOR
230
+ # self.cell_height = self.height * self.cell_height_percentage / _FACTOR
231
+ # self.cell_padding_h = self.cell_width * self.cell_padding_h_percentage / _FACTOR
232
+ # self.cell_padding_v = (
233
+ # self.cell_height * self.cell_padding_v_percentage / _FACTOR
234
+ # )
235
+ # self.path = Path(stroke=Color("red"), stroke_width=500)
236
+ #
237
+ # if show_outline:
238
+ # self.make_outline(self.x0, self.y0, self.x1, self.y1)
239
+ #
240
+ # # Determine rows and columns of cuts to create
241
+ # # will round down so add 1 and trim later
242
+ # # Determine rows and columns of cuts to create
243
+ # # will round down so add 1 and trim later
244
+ # if self.cell_width + 2 * self.cell_padding_h == 0:
245
+ # cols = 1
246
+ # else:
247
+ # cols = (
248
+ # int(
249
+ # ((self.x1 - self.x0) + self.cell_width)
250
+ # / (self.cell_width + (2 * self.cell_padding_h))
251
+ # )
252
+ # + 1
253
+ # )
254
+ # if self.cell_height + 2 * self.cell_padding_v == 0:
255
+ # rows = 1
256
+ # else:
257
+ # rows = (
258
+ # int(
259
+ # ((self.y1 - self.y0) + self.cell_height)
260
+ # / (self.cell_height + (2 * self.cell_padding_v))
261
+ # )
262
+ # + 1
263
+ # )
264
+ #
265
+ # if self._extend_patterns:
266
+ # start_value = -2
267
+ # end_value = 1
268
+ # off_x = -1 * (self.cell_width / 2)
269
+ # else:
270
+ # cols = max(1, cols - 2)
271
+ # rows = max(1, rows - 2)
272
+ # start_value = 0
273
+ # end_value = 0
274
+ # off_x = 0
275
+ # # print (f"Area: {self.width:.1f}, {self.height:.1f}, Cell: {self.cell_width:.1f}, {self.cell_height:.1f}")
276
+ # # print (f"Rows: {rows}, Cols={cols}")
277
+ # # print (f"Ratios: {self.cell_width_percentage}, {self.cell_height_percentage}")
278
+ # # print (f"Padding: {self.cell_padding_h_percentage}, {self.cell_padding_v_percentage}")
279
+ # for col in range(start_value, cols + end_value, 1):
280
+ # top_left_x = self.x0 + off_x
281
+ # x_offset = col * (self.cell_width + (2 * self.cell_padding_h))
282
+ # x_current = top_left_x + x_offset
283
+ # for row in range(start_value, rows + end_value, 1):
284
+ # top_left_y = self.y0
285
+ # y_offset = row * (self.cell_height + (2 * self.cell_padding_v)) + (
286
+ # (self.cell_height + (2 * self.cell_padding_v)) / 2
287
+ # ) * (col % 2)
288
+ # y_current = top_left_y + y_offset
289
+ #
290
+ # if x_current < self.x1 and y_current < self.y1:
291
+ # # Don't call draw if outside of hinge area
292
+ # self.draw_trace(
293
+ # x_current,
294
+ # y_current,
295
+ # self.cell_width,
296
+ # self.cell_height,
297
+ # )
298
+ # if show_outline:
299
+ # self.make_outline(
300
+ # x_current,
301
+ # y_current,
302
+ # x_current + self.cell_width,
303
+ # y_current + self.cell_height,
304
+ # )
305
+ # if not clip_bounds:
306
+ # return
307
+ # rectangular = True
308
+ # if (
309
+ # self.outershape is not None
310
+ # and hasattr(self.outershape, "as_path")
311
+ # and self.outershape.type != "elem rect"
312
+ # ):
313
+ # rectangular = False
314
+ # if final and not rectangular:
315
+ # self.path.transform *= Matrix.translate(self.start_x, self.start_y)
316
+ # from time import time
317
+ #
318
+ # t0 = time()
319
+ # vm = VectorMontonizer()
320
+ # outer_bb = self.outershape.bbox()
321
+ # outer_path = self.outershape.as_path()
322
+ # outer_poly = Polygon(
323
+ # [outer_path.point(i / 1000.0, error=1e4) for i in range(1001)]
324
+ # )
325
+ # vm.add_polyline(outer_poly)
326
+ # path = Path(stroke=Color("red"), stroke_width=500)
327
+ # deleted = 0
328
+ # total = 0
329
+ # # pt_min_x = 1E+30
330
+ # # pt_min_y = 1E+30
331
+ # # pt_max_x = -1 * pt_min_x
332
+ # # pt_max_y = -1 * pt_min_y
333
+ # # Numpy does not work
334
+ # # vm.add_polyline(outer_poly)
335
+ # # path = Path(stroke=Color("red"), stroke_width=500)
336
+ # # for sub_inner in self.path.as_subpaths():
337
+ # # sub_inner = Path(sub_inner)
338
+ # # pts_sub = sub_inner.npoint(linspace(0, 1, 1000))
339
+ # # good_pts = [p for p in pts_sub if vm.is_point_inside(p[0] + self.start_x, p[1] + self.start_y)]
340
+ # # path += Path(Polyline(good_pts), stroke=Color("red"), stroke_width=500)
341
+ # for sub_inner in self.path.as_subpaths():
342
+ # sub_bbox = sub_inner.bbox()
343
+ # outx, outy = self.outside(sub_bbox, outer_bb)
344
+ # if outx == "outside" or outy == "outside":
345
+ # continue
346
+ #
347
+ # sub_inner = Path(sub_inner)
348
+ # pts_sub = [sub_inner.point(i / 1000.0, error=1e4) for i in range(1001)]
349
+ # fullyinside = True
350
+ # for i in range(len(pts_sub) - 1, -1, -1):
351
+ # total += 1
352
+ # pt = pts_sub[i]
353
+ # pt[0] += self.start_x
354
+ # pt[1] += self.start_y
355
+ # # pt_min_x = min(pt_min_x, pt[0])
356
+ # # pt_min_y = min(pt_min_y, pt[1])
357
+ # # pt_max_x = max(pt_max_x, pt[0])
358
+ # # pt_max_y = max(pt_max_y, pt[1])
359
+ # if not vm.is_point_inside(pt[0], pt[1]):
360
+ # # if we do have points beyond, then we create a seperate path
361
+ # if i < len(pts_sub) - 1:
362
+ # goodpts = pts_sub[i + 1 :]
363
+ # path += Path(
364
+ # Polyline(goodpts), stroke=Color("red"), stroke_width=500
365
+ # )
366
+ # del pts_sub[i:]
367
+ # deleted += 1
368
+ # fullyinside = False
369
+ # if fullyinside:
370
+ # path += abs(sub_inner)
371
+ # else:
372
+ # path += Path(
373
+ # Polyline(pts_sub), stroke=Color("red"), stroke_width=500
374
+ # )
375
+ # self.path = path
376
+ # else:
377
+ # # Former method....
378
+ # # ...is limited to rectangular area but maintains inner cubics,
379
+ # # quads and arcs while the vectormontonizer is more versatile
380
+ # # when it comes to the surrounding shape but transforms all
381
+ # # path elements to lines
382
+ # self.path = self.clip_path(self.path, 0, 0, self.width, self.height)
383
+ # self.path.transform *= Matrix.translate(self.start_x, self.start_y)
384
+ # self.preview_path = copy(self.path)
385
+ #
386
+ # def clip_path(self, path, xmin, ymin, xmax, ymax):
387
+ # """
388
+ # Clip a path at a rectangular area, will return the clipped path
389
+ #
390
+ # Args:
391
+ # path : The path to clip
392
+ # xmin : Left side of the rectangular area
393
+ # ymin : Upper side of the rectangular area
394
+ # xmax : Right side of the rectangular area
395
+ # ymax : Lower side of the rectangular area
396
+ # """
397
+ #
398
+ # def approximate_line(part_of_path, current_x, current_y):
399
+ # # print(f"Check: {type(part_of_path).__name__} {part_of_path.bbox()} {clipbb}")
400
+ # from numpy import linspace
401
+ #
402
+ # added = 0
403
+ # partial = 0
404
+ # ignored = 0
405
+ # subj = part_of_path.npoint(linspace(0, 1, interpolation))
406
+ # subj.reshape((2, interpolation))
407
+ # iterated_points = list(map(Point, subj))
408
+ # for p in iterated_points:
409
+ # segbb = (
410
+ # min(current_x, p[0]),
411
+ # min(current_y, p[1]),
412
+ # max(current_x, p[0]),
413
+ # max(current_y, p[1]),
414
+ # )
415
+ # sx, sy = self.outside(segbb, clipbb)
416
+ # # print(f"{segbb} - {clipbb} {sx} - {sy}")
417
+ # if sx == "outside" or sy == "outside":
418
+ # # Fully outside, so drop
419
+ # add_move(newpath, e.end)
420
+ # ignored += 1
421
+ # elif statex == "inside" and statey == "inside":
422
+ # # Fully inside, so append
423
+ # if current_x != new_cx or current_y != new_cy:
424
+ # add_move(newpath, Point(current_x, current_y))
425
+ # newpath.line(p)
426
+ # added += 1
427
+ # else:
428
+ # dx = p[0] - current_x
429
+ # dy = p[1] - current_y
430
+ # new_cx = current_x
431
+ # new_cy = current_y
432
+ # new_ex = p[0]
433
+ # new_ey = p[1]
434
+ # if dx == 0:
435
+ # # Vertical line needs special treatment
436
+ # if xmin <= new_cx <= xmax:
437
+ # new_cy = min(max(new_cy, ymin), ymax)
438
+ # new_ey = min(max(new_ey, ymin), ymax)
439
+ # if new_cx != current_x or new_cy != current_y:
440
+ # # Needs a move
441
+ # add_move(newpath, Point(new_cx, new_cy))
442
+ # newpath.line(Point(new_ex, new_ey))
443
+ # partial += 1
444
+ # else:
445
+ # ignored += 1
446
+ # else:
447
+ # # regular line, so lets establish x0 x1
448
+ # # could still be an outward pointing line....
449
+ # new_cx = min(max(new_cx, xmin), xmax)
450
+ # new_ex = min(max(new_ex, xmin), xmax)
451
+ # # corresponding y values...
452
+ # edx = p[0] - current_x
453
+ # edy = p[1] - current_y
454
+ # new_cy = current_y + (new_cx - current_x) / edx * edy
455
+ # new_ey = current_y + (new_ex - current_x) / edx * edy
456
+ # # Y can still cross...
457
+ # new_cx_clipped = new_cx
458
+ # new_ex_clipped = new_ex
459
+ # new_cy_clipped = min(max(new_cy, ymin), ymax)
460
+ # new_ey_clipped = min(max(new_ey, ymin), ymax)
461
+ # # Adjust x - value
462
+ # if dy != 0:
463
+ # new_cx_clipped = new_cx + dx / dy * (
464
+ # new_cy_clipped - new_cy
465
+ # )
466
+ # new_ex_clipped = new_ex + dx / dy * (
467
+ # new_ey_clipped - new_ey
468
+ # )
469
+ #
470
+ # new_cx = new_cx_clipped
471
+ # new_cy = new_cy_clipped
472
+ # new_ex = new_ex_clipped
473
+ # new_ey = new_ey_clipped
474
+ # if min(new_cy, new_ey) == ymax and dy != 0:
475
+ # # Outward...
476
+ # ignored += 1
477
+ # elif max(new_cy, new_ey) == ymin and dy != 0:
478
+ # # Outward...
479
+ # ignored += 1
480
+ # else:
481
+ # if new_cx != current_x or new_cy != current_y:
482
+ # # Needs a move
483
+ # add_move(newpath, Point(new_cx, new_cy))
484
+ # newpath.line(Point(new_ex, new_ey))
485
+ # partial += 1
486
+ # current_x = p[0]
487
+ # current_y = p[1]
488
+ # if current_x != part_of_path.end[0] or current_y != part_of_path.end[1]:
489
+ # add_move(newpath, part_of_path.end)
490
+ # # print (f"From iterated line: added={added}, partial={partial}, ignored={ignored}")
491
+ #
492
+ # def add_move(addpath, destination):
493
+ # # Was the last segment as well a move? Then just update the coords...
494
+ # if len(addpath) > 0:
495
+ # if isinstance(addpath[-1], Move):
496
+ # addpath[-1].end = destination
497
+ # return
498
+ # addpath.move(destination)
499
+ #
500
+ # interpolation = 50
501
+ # fully_deleted = 0
502
+ # partial_deleted = 0
503
+ # not_deleted = 0
504
+ # clipbb = (xmin, ymin, xmax, ymax)
505
+ # current_x = 0
506
+ # current_y = 0
507
+ # first_point = path.first_point
508
+ # if first_point is not None:
509
+ # current_x = first_point[0]
510
+ # current_y = first_point[1]
511
+ # newpath = Path(
512
+ # stroke=path.stroke, stroke_width=path.stroke_width, transform=path.transform
513
+ # )
514
+ # for e in path:
515
+ # if hasattr(e, "bbox"):
516
+ # segbb = e.bbox()
517
+ # elif hasattr(e, "end"):
518
+ # segbb = (
519
+ # min(current_x, e.end[0]),
520
+ # min(current_y, e.end[1]),
521
+ # max(current_x, e.end[0]),
522
+ # max(current_y, e.end[1]),
523
+ # )
524
+ # else:
525
+ # segbb = (xmin, ymin, 0, 0)
526
+ # if isinstance(e, Move):
527
+ # add_move(newpath, e.end)
528
+ # current_x = e.end[0]
529
+ # current_y = e.end[1]
530
+ # not_deleted += 1
531
+ # elif isinstance(e, Line):
532
+ # statex, statey = self.outside(segbb, clipbb)
533
+ # dx = e.end[0] - current_x
534
+ # dy = e.end[1] - current_y
535
+ # if statex == "outside" or statey == "outside":
536
+ # # Fully outside, so drop
537
+ # add_move(newpath, e.end)
538
+ # fully_deleted += 1
539
+ # elif statex == "inside" and statey == "inside":
540
+ # # Fully inside, so append
541
+ # newpath.line(e.end)
542
+ # not_deleted += 1
543
+ # else:
544
+ # # needs dealing, its either for the time being, just ignored...
545
+ # new_cx = current_x
546
+ # new_cy = current_y
547
+ # new_ex = e.end[0]
548
+ # new_ey = e.end[1]
549
+ # if dx == 0:
550
+ # # Vertical line needs special treatment
551
+ # if xmin <= new_cx <= xmax:
552
+ # new_cy = min(max(new_cy, ymin), ymax)
553
+ # new_ey = min(max(new_ey, ymin), ymax)
554
+ # if new_cx != current_x or new_cy != current_y:
555
+ # # Needs a move
556
+ # add_move(newpath, Point(new_cx, new_cy))
557
+ # newpath.line(Point(new_ex, new_ey))
558
+ # else:
559
+ # # regular line, so lets establish x0 x1
560
+ # # could still be an outward pointing line....
561
+ # new_cx = min(max(new_cx, xmin), xmax)
562
+ # new_ex = min(max(new_ex, xmin), xmax)
563
+ # # corresponding y values...
564
+ # edx = e.end[0] - current_x
565
+ # edy = e.end[1] - current_y
566
+ # new_cy = current_y + (new_cx - current_x) / edx * edy
567
+ # new_ey = current_y + (new_ex - current_x) / edx * edy
568
+ # # Y can still cross...
569
+ # new_cx_clipped = new_cx
570
+ # new_ex_clipped = new_ex
571
+ # new_cy_clipped = min(max(new_cy, ymin), ymax)
572
+ # new_ey_clipped = min(max(new_ey, ymin), ymax)
573
+ # # Adjust x - value
574
+ # if dy != 0:
575
+ # new_cx_clipped = new_cx + dx / dy * (
576
+ # new_cy_clipped - new_cy
577
+ # )
578
+ # new_ex_clipped = new_ex + dx / dy * (
579
+ # new_ey_clipped - new_ey
580
+ # )
581
+ #
582
+ # new_cx = new_cx_clipped
583
+ # new_cy = new_cy_clipped
584
+ # new_ex = new_ex_clipped
585
+ # new_ey = new_ey_clipped
586
+ # if min(new_cy, new_ey) == ymax and dy != 0:
587
+ # # Outward...
588
+ # pass
589
+ # elif max(new_cy, new_ey) == ymin and dy != 0:
590
+ # # Outward...
591
+ # pass
592
+ # else:
593
+ # if new_cx != current_x or new_cy != current_y:
594
+ # # Needs a move
595
+ # add_move(newpath, Point(new_cx, new_cy))
596
+ # newpath.line(Point(new_ex, new_ey))
597
+ # if current_x != e.end[0] or current_y != e.end[1]:
598
+ # add_move(newpath, e.end)
599
+ # partial_deleted += 1
600
+ # current_x = e.end[0]
601
+ # current_y = e.end[1]
602
+ # elif isinstance(e, Close):
603
+ # newpath.closed()
604
+ # not_deleted += 1
605
+ # elif isinstance(e, QuadraticBezier):
606
+ # statex, statey = self.outside(segbb, clipbb)
607
+ # if statex == "outside" and statey == "outside":
608
+ # # Fully outside, so drop
609
+ # add_move(newpath, e.end)
610
+ # fully_deleted += 1
611
+ # elif statex == "inside" and statey == "inside":
612
+ # # Fully inside, so append
613
+ # newpath.quad(e.control, e.end)
614
+ # not_deleted += 1
615
+ # else:
616
+ # approximate_line(e, current_x, current_y)
617
+ # current_x = e.end[0]
618
+ # current_y = e.end[1]
619
+ # elif isinstance(e, CubicBezier):
620
+ # statex, statey = self.outside(segbb, clipbb)
621
+ # if statex == "outside" and statey == "outside":
622
+ # # Fully outside, so drop
623
+ # add_move(newpath, e.end)
624
+ # fully_deleted += 1
625
+ # elif statex == "inside" and statey == "inside":
626
+ # # Fully inside, so append
627
+ # newpath.cubic(e.control1, e.control2, e.end)
628
+ # not_deleted += 1
629
+ # else:
630
+ # approximate_line(e, current_x, current_y)
631
+ # partial_deleted += 1
632
+ # current_x = e.end[0]
633
+ # current_y = e.end[1]
634
+ # elif isinstance(e, Arc):
635
+ # for e_cubic in e.as_cubic_curves():
636
+ # segbb = e_cubic.bbox()
637
+ # statex, statey = self.outside(segbb, clipbb)
638
+ # if statex == "outside" and statey == "outside":
639
+ # # Fully outside, so drop
640
+ # add_move(newpath, e.end)
641
+ # fully_deleted += 1
642
+ # elif statex == "inside" and statey == "inside":
643
+ # # Fully inside, so append
644
+ # newpath.cubic(e_cubic.control1, e_cubic.control2, e_cubic.end)
645
+ # not_deleted += 1
646
+ # else:
647
+ # approximate_line(e_cubic, current_x, current_y)
648
+ # partial_deleted += 1
649
+ # current_x = e_cubic.end[0]
650
+ # current_y = e_cubic.end[1]
651
+ # current_x = e.end[0]
652
+ # current_y = e.end[1]
653
+ #
654
+ # flag = True
655
+ # while flag:
656
+ # flag = False
657
+ # if len(newpath) > 0 and isinstance(newpath[-1], Move):
658
+ # # We don't need a move at the end of the path...
659
+ # del newpath[-1]
660
+ # flag = True
661
+ #
662
+ # # print(
663
+ # # f"Ready: left untouched: {not_deleted}, fully deleted={fully_deleted}, partial deletion:{partial_deleted}"
664
+ # # )
665
+ # return newpath
666
+ #
667
+ #
668
+ # def set_line(*args, **kwargs):
669
+ # yield "M", 0, 0.5
670
+ # yield "L", 1, 0.5
671
+ #
672
+ #
673
+ # def set_fishbone(a, b, *args, **kwargs):
674
+ # dx = a / 5.0 * 0.5
675
+ # dy = b / 5.0 * 0.5
676
+ # yield "M", 0 + dx, 1 - dy
677
+ # yield "L", 0.5, 0
678
+ # # self.pattern.append(("M", 0.5, 0))
679
+ # yield "L", 1 - dx, 1 - dy
680
+ #
681
+ #
682
+ # def set_diagonal(a, b, *args, **kwargs):
683
+ # dx = a / 5.0 * 1.0
684
+ # dy = b / 5.0 * 1.0
685
+ # yield "M", 0 + dx, 1 - dy
686
+ # yield "L", 1 - dx, 0 + dy
687
+ #
688
+ #
689
+ # def set_diamond1(a, b, *args, **kwargs):
690
+ # yield "M", 0, 0.5
691
+ # yield "L", 0.5, 0
692
+ # yield "L", 1, 0.5
693
+ # yield "L", 0.5, 1
694
+ # yield "L", 0, 0.5
695
+ #
696
+ #
697
+ # def set_diamond2(a, b, *args, **kwargs):
698
+ # yield "M", 0, 0
699
+ # yield "L", 0.5, 0.4
700
+ # yield "L", 1, 0
701
+ # yield "M", 0, 1
702
+ # yield "L", 0.5, 0.6
703
+ # yield "L", 1, 1
704
+ #
705
+ #
706
+ # def set_cross(a, b, *args, **kwargs):
707
+ # # Pattern: cross
708
+ # dx = a / 5.0 * 0.5
709
+ # dy = b / 5.0 * 0.5
710
+ # yield "M", 0.0, 0.25 + dy
711
+ # yield "L", 0.25 + dx, 0.50
712
+ # yield "L", 0.0, 0.75 - dy
713
+ # yield "M", 0.25 + dx, 0.50
714
+ # yield "L", 0.75 - dx, 0.50
715
+ # yield "M", 1, 0.25 + dy
716
+ # yield "L", 0.75 - dx, 0.50
717
+ # yield "L", 1, 0.75 - dy
718
+ #
719
+ #
720
+ # def set_fabric(a, b, *args, **kwargs):
721
+ # yield "M", 0.25, 0.25
722
+ # yield "L", 0, 0.25
723
+ # yield "L", 0, 0
724
+ # yield "L", 0.5, 0
725
+ # yield "L", 0.5, 1
726
+ # yield "L", 1, 1
727
+ # yield "L", 1, 0.75
728
+ # yield "L", 0.75, 0.75
729
+ #
730
+ # yield "M", 0.75, 0.25
731
+ # yield "L", 0.75, 0
732
+ # yield "L", 1, 0
733
+ # yield "L", 1, 0.5
734
+ # yield "L", 0, 0.5
735
+ # yield "L", 0, 1
736
+ # yield "L", 0.25, 1
737
+ # yield "L", 0.25, 0.75
738
+ #
739
+ #
740
+ # def set_beehive(a, b, *args, **kwargs):
741
+ # dx = a / 5.0 * 0.5
742
+ # dy = b / 5.0 * 0.5
743
+ # # top
744
+ # yield "M", 0, 0.5 - dy
745
+ # yield "L", dx, dy
746
+ # yield "L", 1 - dx, dy
747
+ # yield "L", 1, 0.5 - dy
748
+ # # inner
749
+ # yield "M", 0, 0.5
750
+ # yield "L", dx, 2 * dy
751
+ # yield "L", 1 - dx, 2 * dy
752
+ # yield "L", 1, 0.5
753
+ # yield "L", 1 - dx, 1 - 2 * dy
754
+ # yield "L", dx, 1 - 2 * dy
755
+ # yield "L", 0, 0.5
756
+ # # bottom
757
+ # yield "M", 0, 0.5 + dy
758
+ # yield "L", dx, 1 - dy
759
+ # yield "L", 1 - dx, 1 - dy
760
+ # yield "L", 1, 0.5 + dy
761
+ #
762
+ #
763
+ # def set_bowlingpin(a, b, *args, **kwargs):
764
+ # yield "M", 0.2, 0.6
765
+ # ctrl_x = 0.1 + a
766
+ # ctrl_y = 0.5
767
+ # yield "Q", ctrl_x, ctrl_y, 0.2, 0.4
768
+ #
769
+ # ctrl_x = 0.5
770
+ # ctrl_y = 0.1 - b
771
+ # yield "Q", ctrl_x, ctrl_y, 0.8, 0.4
772
+ #
773
+ # ctrl_x = 0.9 - a
774
+ # ctrl_y = 0.5
775
+ # yield "Q", ctrl_x, ctrl_y, 0.8, 0.6
776
+ #
777
+ # ctrl_x = 0.5
778
+ # ctrl_y = 0.9 + b
779
+ # yield "Q", ctrl_x, ctrl_y, 0.2, 0.6
780
+ #
781
+ #
782
+ # def set_wave(a, b, *args, **kwargs):
783
+ # # Pattern: wavy
784
+ # yield "M", 0.0, 0.25
785
+ # yield "L", 0.25, 0.25
786
+ # ctrl_x = 0.5 + a
787
+ # ctrl_y = 0.25 + b
788
+ # yield "Q", ctrl_x, ctrl_y, 0.5, 0.5
789
+ # ctrl_x = 0.5 - a
790
+ # ctrl_y = 0.75 - b
791
+ # yield "Q", ctrl_x, ctrl_y, 0.75, 0.75
792
+ # yield "L", 1, 0.75
793
+ #
794
+ #
795
+ # def set_bezier(a, b, *args, **kwargs):
796
+ # anchor_tip = a # distance factor from anchor to place control point
797
+ # anchor_center = b
798
+ # yield "M", 0, 0
799
+ # yield "C", 1 * anchor_tip, 0, 1 / 2 - (1 * anchor_center), 1, 1 / 2, 1
800
+ # yield "C", 1 / 2 + (1 * anchor_center), 1, 1 * (1 - anchor_tip), 0, 1, 0
801
+ #
802
+ #
803
+ # def set_brackets(a, b, *args, **kwargs):
804
+ # p_a = a
805
+ # p_b = b
806
+ # yield "M", 0.0, 0.5
807
+ # yield "C", 0.0, p_a, 1.0, p_b, 1.0, 0.5
808
+ # yield "C", 1.0, 1 - p_a, 0.0, 1 - p_b, 0.0, 0.5
809
+ #
810
+ #
811
+ # def set_shape(a, b, *args, outershape=None, **kwargs):
812
+ # # concentric shapes
813
+ # polycache = list()
814
+ #
815
+ # if len(polycache) != 0:
816
+ # # We've done our bit already
817
+ # return
818
+ # resolution = 200.0
819
+ # if outershape is None:
820
+ # shape = Path(Polyline((0, 0), (1, 0), (1, 1), (0, 1), (0, 0)))
821
+ # else:
822
+ # shape = outershape.as_path()
823
+ # bb = shape.bbox()
824
+ # wd = bb[2] - bb[0]
825
+ # if wd == 0:
826
+ # wd = 1
827
+ # ht = bb[3] - bb[1]
828
+ # if ht == 0:
829
+ # ht = 1
830
+ # tx = 0
831
+ # ty = 0
832
+ # tc = int(resolution)
833
+ # # minx = miny = 1e18
834
+ # # maxx = maxy = -1e18
835
+ # # Convert to polygon and bring it to 0 / 1
836
+ # if wd == 0:
837
+ # ratiox = 1
838
+ # else:
839
+ # ratiox = 1 / wd
840
+ #
841
+ # if ht == 0:
842
+ # ratioy = 1
843
+ # else:
844
+ # ratioy = 1 / ht
845
+ # amount = int(abs(10 * a)) # (1 to 50)
846
+ # segments = int(abs(10 * b))
847
+ # seg_break = int(resolution) / (segments + 1)
848
+ # seg_len = resolution / 40.0
849
+ # for i in range(int(resolution) + 1):
850
+ # pt = shape.point(i / resolution, error=1e4)
851
+ # pt[0] = (pt[0] - bb[0]) * ratiox
852
+ # pt[1] = (pt[1] - bb[1]) * ratioy
853
+ # xx = pt[0]
854
+ # yy = pt[1]
855
+ # tx += xx
856
+ # ty += yy
857
+ # # minx = min(minx, xx)
858
+ # # miny = min(miny, yy)
859
+ # # maxx = max(maxx, xx)
860
+ # # maxy = max(maxy, yy)
861
+ # polycache.append(pt)
862
+ # geometric_center_x = tx / tc
863
+ # geometric_center_y = ty / tc
864
+ # # print(
865
+ # # f"geometric center master: {geometric_center_x:.1f}, {geometric_center_y:.1f}"
866
+ # # )
867
+ # # print(f"boundaries: {minx:.1f}, {miny:.1f} - {maxx:.1f}, {maxy:.1f}")
868
+ # dx = 0
869
+ # dy = 0
870
+ # regular = False
871
+ # ratio = 1.0
872
+ # dx = 1.0 / (amount + 1)
873
+ #
874
+ # ratio = 1
875
+ # for num in range(amount):
876
+ # ratio -= dx
877
+ # regular = not regular
878
+ # current_x = None
879
+ # current_y = None
880
+ # if regular:
881
+ # segcount = int(seg_break * 0.25)
882
+ # else:
883
+ # segcount = int(seg_break * 0.5)
884
+ # # tx = 0
885
+ # # ty = 0
886
+ # # minx = miny = 1e18
887
+ # # maxx = maxy = -1e18
888
+ # for i in range(int(resolution) + 1):
889
+ # xx = (polycache[i][0] - geometric_center_x) * ratio + geometric_center_x
890
+ # yy = (polycache[i][1] - geometric_center_y) * ratio + geometric_center_y
891
+ # # tx += xx
892
+ # # ty += yy
893
+ # # minx = min(minx, xx)
894
+ # # miny = min(miny, yy)
895
+ # # maxx = max(maxx, xx)
896
+ # # maxy = max(maxy, yy)
897
+ # segcount += 1
898
+ # if segcount < seg_break:
899
+ # if current_x is None:
900
+ # yield "M", xx, yy
901
+ # else:
902
+ # yield "L", xx, yy
903
+ # current_x = xx
904
+ # current_y = yy
905
+ # elif segcount >= seg_break + seg_len:
906
+ # segcount = 0
907
+ # current_x = None
908
+ # current_y = None
909
+ # # geo_x = tx / tc
910
+ # # geo_y = ty / tc
911
+ # # print(f"geometric center copy: {geo_x:.1f}, {geo_y:.1f}")
912
+ # # print(f"boundaries: {minx:.1f}, {miny:.1f} - {maxx:.1f}, {maxy:.1f}")
913
+ #
914
+ #
915
+ # def plugin(kernel, lifecycle):
916
+ # if lifecycle == "register":
917
+ # _ = kernel.translation
918
+ # context = kernel.root
919
+ # context.register(
920
+ # "pattern/line",
921
+ # (
922
+ # set_line,
923
+ # False,
924
+ # "",
925
+ # "",
926
+ # (-200, -350, 0, 0),
927
+ # True,
928
+ # ),
929
+ # )
930
+ # context.register(
931
+ # "pattern/fishbone",
932
+ # (
933
+ # set_fishbone,
934
+ # True,
935
+ # "Left/Right Indentation",
936
+ # "Top/Bottom Indentation",
937
+ # (100, 100, 0, 0),
938
+ # True,
939
+ # ),
940
+ # )
941
+ # context.register(
942
+ # "pattern/diagonal",
943
+ # (
944
+ # set_diagonal,
945
+ # True,
946
+ # "Left/Right Indentation",
947
+ # "Top/Bottom Indentation",
948
+ # (-100, -100, 0, 0),
949
+ # True,
950
+ # ),
951
+ # )
952
+ # context.register(
953
+ # "pattern/diamond1",
954
+ # (
955
+ # set_diamond1,
956
+ # False,
957
+ # "",
958
+ # "",
959
+ # (-150, 100, 0, 0),
960
+ # True,
961
+ # ),
962
+ # )
963
+ # context.register(
964
+ # "pattern/diamond2",
965
+ # (
966
+ # set_diamond2,
967
+ # False,
968
+ # "",
969
+ # "",
970
+ # (-120, 60, 0, 0),
971
+ # True,
972
+ # ),
973
+ # )
974
+ # context.register(
975
+ # "pattern/cross",
976
+ # (
977
+ # set_cross,
978
+ # True,
979
+ # "Left/Right Indentation",
980
+ # "Top/Bottom Indentation",
981
+ # (-150, -40, 0, 0),
982
+ # True,
983
+ # ),
984
+ # )
985
+ # context.register(
986
+ # "pattern/bezier",
987
+ # (
988
+ # set_bezier,
989
+ # True,
990
+ # "",
991
+ # "",
992
+ # (-20, -160, 0.4, 0.3),
993
+ # True,
994
+ # ),
995
+ # )
996
+ # context.register(
997
+ # "pattern/wave",
998
+ # (
999
+ # set_wave,
1000
+ # True,
1001
+ # "",
1002
+ # "",
1003
+ # (-130, -260, 0, 0),
1004
+ # True,
1005
+ # ),
1006
+ # )
1007
+ # context.register(
1008
+ # "pattern/bowlingpin",
1009
+ # (
1010
+ # set_bowlingpin,
1011
+ # True,
1012
+ # "Left/Right Bowl",
1013
+ # "Top/Bottom Bowl",
1014
+ # (-210, -50, -0.3, 0),
1015
+ # True,
1016
+ # ),
1017
+ # )
1018
+ # context.register(
1019
+ # "pattern/beehive",
1020
+ # (
1021
+ # set_beehive,
1022
+ # True,
1023
+ # "Position of left side",
1024
+ # "Distance of second line",
1025
+ # (-10, 60, 1.4, 0),
1026
+ # True,
1027
+ # ),
1028
+ # )
1029
+ # context.register(
1030
+ # "pattern/fabric",
1031
+ # (
1032
+ # set_fabric,
1033
+ # False,
1034
+ # "",
1035
+ # "",
1036
+ # (-180, 130, 0, 0),
1037
+ # True,
1038
+ # ),
1039
+ # )
1040
+ # context.register(
1041
+ # "pattern/brackets",
1042
+ # (
1043
+ # set_brackets,
1044
+ # True,
1045
+ # "",
1046
+ # "",
1047
+ # (-140, -110, 0.7, 0.7),
1048
+ # True,
1049
+ # ),
1050
+ # )
1051
+ # context.register(
1052
+ # "pattern/shape",
1053
+ # (
1054
+ # set_shape,
1055
+ # True,
1056
+ # "Number of copies",
1057
+ # "Number of segments",
1058
+ # (0, 0, 1, 0.4),
1059
+ # False,
1060
+ # ),
1061
+ # )