TclCommandPaint.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. from tclCommands.TclCommand import TclCommand
  2. import collections
  3. import logging
  4. import gettext
  5. import appTranslation as fcTranslate
  6. import builtins
  7. fcTranslate.apply_language('strings')
  8. if '_' not in builtins.__dict__:
  9. _ = gettext.gettext
  10. log = logging.getLogger('base')
  11. class TclCommandPaint(TclCommand):
  12. """
  13. Paint the interior of polygons
  14. """
  15. # Array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
  16. aliases = ['paint']
  17. description = '%s %s' % ("--", "Paint polygons in the specified object by covering them with toolpaths.")
  18. # dictionary of types from Tcl command, needs to be ordered
  19. arg_names = collections.OrderedDict([
  20. ('name', str),
  21. ])
  22. # dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
  23. option_types = collections.OrderedDict([
  24. ('tooldia', str),
  25. ('overlap', float),
  26. ('order', str),
  27. ('offset', float),
  28. ('method', str),
  29. ('connect', str),
  30. ('contour', str),
  31. ('all', str),
  32. ('single', str),
  33. ('ref', str),
  34. ('box', str),
  35. ('outname', str),
  36. ])
  37. # array of mandatory options for current Tcl command: required = {'name','outname'}
  38. required = ['name']
  39. # structured help for current command, args needs to be ordered
  40. help = {
  41. 'main': "Paint polygons in the specified object by covering them with toolpaths.\n"
  42. "Can use only one of the parameters: 'all', 'box', 'single'.",
  43. 'args': collections.OrderedDict([
  44. ('name', 'Name of the source Geometry object. String.'),
  45. ('tooldia', 'Diameter of the tools to be used. Can be a comma separated list of diameters.\n'
  46. 'WARNING: No space is allowed between tool diameters. E.g: correct: 0.5,1 / incorrect: 0.5, 1'),
  47. ('overlap', 'Percentage of tool diameter to overlap current pass over previous pass. Float [0, 99.9999]\n'
  48. 'E.g: for a 25% from tool diameter overlap use -overlap 25'),
  49. ('offset', 'Distance from the polygon border where painting starts. Float number.'),
  50. ('order', 'Can have the values: "no", "fwd" and "rev". String.\n'
  51. 'It is useful when there are multiple tools in tooldia parameter.\n'
  52. '"no" -> the order used is the one provided.\n'
  53. '"fwd" -> tools are ordered from smallest to biggest.\n'
  54. '"rev" -> tools are ordered from biggest to smallest.'),
  55. ('method', 'Algorithm for painting. Can be: "standard", "seed", "lines", "laser_lines", "combo".'),
  56. ('connect', 'Draw lines to minimize tool lifts. True (1) or False (0)'),
  57. ('contour', 'Cut around the perimeter of the painting. True (1) or False (0)'),
  58. ('all', 'If used, paint all polygons in the object.'),
  59. ('box', 'name of the object to be used as paint reference. String.'),
  60. ('single', 'Value is in format x,y or (x,y). Example: 2.0,1.1\n'
  61. 'If used will paint a single polygon specified by "x" and "y" values.\n'
  62. 'WARNING: No spaces allowed in the value. Use dot decimals separator.'),
  63. ('outname', 'Name of the resulting Geometry object. String. No spaces.'),
  64. ]),
  65. 'examples': ["paint obj_name -tooldia 0.3 -offset 0.1 -method 'seed' -all",
  66. "paint obj_name -tooldia 0.3 -offset 0.1 -method 'seed' -single 3.3,2.0"]
  67. }
  68. def execute(self, args, unnamed_args):
  69. """
  70. execute current TCL shell command
  71. :param args: array of known named arguments and options
  72. :param unnamed_args: array of other values which were passed into command
  73. without -somename and we do not have them in known arg_names
  74. :return: None or exception
  75. """
  76. name = args['name']
  77. # Get source object.
  78. try:
  79. obj = self.app.collection.get_by_name(str(name))
  80. except Exception as e:
  81. log.debug("TclCommandPaint.execute() --> %s" % str(e))
  82. self.raise_tcl_error("%s: %s" % (_("Could not retrieve object"), name))
  83. return "Could not retrieve object: %s" % name
  84. if 'tooldia' in args:
  85. tooldia = str(args['tooldia'])
  86. else:
  87. tooldia = str(self.app.defaults["tools_paint_tooldia"])
  88. if 'overlap' in args:
  89. overlap = float(args['overlap']) / 100.0
  90. else:
  91. overlap = float(self.app.defaults["tools_paint_overlap"]) / 100.0
  92. if 'order' in args:
  93. order = args['order']
  94. else:
  95. order = str(self.app.defaults["tools_paint_order"])
  96. if 'offset' in args:
  97. offset = float(args['offset'])
  98. else:
  99. offset = float(self.app.defaults["tools_paint_offset"])
  100. if 'method' in args:
  101. method = args['method']
  102. if method == "standard":
  103. method = _("Standard")
  104. elif method == "seed":
  105. method = _("Seed")
  106. elif method == "lines":
  107. method = _("Lines")
  108. elif method == "laser_lines":
  109. method = _("Laser_lines")
  110. else:
  111. method = _("Combo")
  112. else:
  113. method = str(self.app.defaults["tools_paint_method"])
  114. if 'connect' in args:
  115. try:
  116. par = args['connect'].capitalize()
  117. except AttributeError:
  118. par = args['connect']
  119. connect = bool(eval(par))
  120. else:
  121. connect = bool(eval(str(self.app.defaults["tools_paint_connect"])))
  122. if 'contour' in args:
  123. try:
  124. par = args['contour'].capitalize()
  125. except AttributeError:
  126. par = args['contour']
  127. contour = bool(eval(par))
  128. else:
  129. contour = bool(eval(str(self.app.defaults["tools_paint_contour"])))
  130. if 'outname' in args:
  131. outname = args['outname']
  132. else:
  133. outname = name + "_paint"
  134. # used only to have correct information's in the obj.tools[tool]['data'] dict
  135. if "all" in args:
  136. select = _("All")
  137. elif "single" in args:
  138. select = _("Polygon Selection")
  139. else:
  140. select = _("Reference Object")
  141. try:
  142. tools = [float(eval(dia)) for dia in tooldia.split(",") if dia != '']
  143. except AttributeError:
  144. tools = [float(tooldia)]
  145. # store here the default data for Geometry Data
  146. default_data = {}
  147. default_data.update({
  148. "name": outname,
  149. "plot": False,
  150. "cutz": self.app.defaults["geometry_cutz"],
  151. "vtipdia": float(self.app.defaults["tools_paint_tipdia"]),
  152. "vtipangle": float(self.app.defaults["tools_paint_tipangle"]),
  153. "travelz": self.app.defaults["geometry_travelz"],
  154. "feedrate": self.app.defaults["geometry_feedrate"],
  155. "feedrate_z": self.app.defaults["geometry_feedrate_z"],
  156. "feedrate_rapid": self.app.defaults["geometry_feedrate_rapid"],
  157. "dwell": self.app.defaults["geometry_dwell"],
  158. "dwelltime": self.app.defaults["geometry_dwelltime"],
  159. "multidepth": self.app.defaults["geometry_multidepth"],
  160. "ppname_g": self.app.defaults["geometry_ppname_g"],
  161. "depthperpass": self.app.defaults["geometry_depthperpass"],
  162. "extracut": self.app.defaults["geometry_extracut"],
  163. "extracut_length": self.app.defaults["geometry_extracut_length"],
  164. "toolchange": self.app.defaults["geometry_toolchange"],
  165. "toolchangez": self.app.defaults["geometry_toolchangez"],
  166. "endz": self.app.defaults["geometry_endz"],
  167. "endxy": self.app.defaults["geometry_endxy"],
  168. "spindlespeed": self.app.defaults["geometry_spindlespeed"],
  169. "toolchangexy": self.app.defaults["geometry_toolchangexy"],
  170. "startz": self.app.defaults["geometry_startz"],
  171. "area_exclusion": self.app.defaults["geometry_area_exclusion"],
  172. "area_shape": self.app.defaults["geometry_area_shape"],
  173. "area_strategy": self.app.defaults["geometry_area_strategy"],
  174. "area_overz": float(self.app.defaults["geometry_area_overz"]),
  175. "tooldia": tooldia,
  176. "tools_paint_offset": offset,
  177. "tools_paint_method": method,
  178. "tools_paint_selectmethod": select,
  179. "tools_paint_connect": connect,
  180. "tools_paint_contour": contour,
  181. "tools_paint_overlap": overlap
  182. })
  183. paint_tools = {}
  184. tooluid = 0
  185. for tool in tools:
  186. tooluid += 1
  187. paint_tools.update({
  188. int(tooluid): {
  189. 'tooldia': self.app.dec_format(float(tool), self.app.decimals),
  190. 'offset': 'Path',
  191. 'offset_value': 0.0,
  192. 'type': 'Iso',
  193. 'tool_type': 'C1',
  194. 'data': dict(default_data),
  195. 'solid_geometry': []
  196. }
  197. })
  198. paint_tools[int(tooluid)]['data']['tooldia'] = self.app.dec_format(float(tool), self.app.decimals)
  199. if obj is None:
  200. return "Object not found: %s" % name
  201. # Paint all polygons in the painted object
  202. if 'all' in args:
  203. self.app.paint_tool.paint_poly_all(obj=obj,
  204. tooldia=tooldia,
  205. order=order,
  206. method=method,
  207. outname=outname,
  208. tools_storage=paint_tools,
  209. plot=False,
  210. run_threaded=False)
  211. return
  212. # Paint single polygon in the painted object
  213. if 'single' in args:
  214. if not args['single'] or args['single'] == '':
  215. self.raise_tcl_error('%s Got: %s' %
  216. (_("Expected a tuple value like -single 3.2,0.1."), str(args['single'])))
  217. else:
  218. coords_xy = [float(eval(a)) for a in args['single'].split(",") if a != '']
  219. if coords_xy and len(coords_xy) != 2:
  220. self.raise_tcl_error('%s Got: %s' %
  221. (_("Expected a tuple value like -single 3.2,0.1."), str(coords_xy)))
  222. x = coords_xy[0]
  223. y = coords_xy[1]
  224. ret_val = self.app.paint_tool.paint_poly(obj=obj,
  225. inside_pt=[x, y],
  226. tooldia=tooldia,
  227. order=order,
  228. method=method,
  229. outname=outname,
  230. tools_storage=paint_tools,
  231. plot=False,
  232. run_threaded=False)
  233. if ret_val == 'fail':
  234. return "Could not find a Polygon at the specified location."
  235. return
  236. # Paint all polygons found within the box object from the the painted object
  237. if 'box' in args:
  238. box_name = args['box']
  239. if box_name is None:
  240. self.raise_tcl_error('%s' % _("Expected -box <value>."))
  241. # Get box source object.
  242. try:
  243. box_obj = self.app.collection.get_by_name(str(box_name))
  244. except Exception as e:
  245. log.debug("TclCommandPaint.execute() --> %s" % str(e))
  246. self.raise_tcl_error("%s: %s" % (_("Could not retrieve box object"), name))
  247. return "Could not retrieve object: %s" % name
  248. self.app.paint_tool.paint_poly_ref(obj=obj,
  249. sel_obj=box_obj,
  250. tooldia=tooldia,
  251. order=order,
  252. method=method,
  253. outname=outname,
  254. tools_storage=paint_tools,
  255. plot=False,
  256. run_threaded=False)
  257. return
  258. self.raise_tcl_error("%s:" % _("None of the following args: 'box', 'single', 'all' were used.\n"
  259. "Paint failed."))
  260. return "None of the following args: 'box', 'single', 'all' were used. Paint failed."