From 929d70542cd0fd456e810a7e46f2527255204aa5 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 23 Apr 2019 04:24:10 +0300 Subject: [PATCH] - Gerber Editor: added two new tools: Add Disc and Add SemiDisc (porting of Circle and Arc from Geometry Editor) --- README.md | 4 + flatcamEditors/FlatCAMGrbEditor.py | 292 +++++++++++++++++++++++++++++ flatcamGUI/FlatCAMGUI.py | 27 +++ share/disc32.png | Bin 0 -> 435 bytes share/semidisc32.png | Bin 0 -> 435 bytes 5 files changed, 323 insertions(+) create mode 100644 share/disc32.png create mode 100644 share/semidisc32.png diff --git a/README.md b/README.md index 456bb339..c4227757 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing. ================================================= +23.04.2019 + +- Gerber Editor: added two new tools: Add Disc and Add SemiDisc (porting of Circle and Arc from Geometry Editor) + 22.04.2019 - added PDF file as type in the Recent File list and capability to load it from there diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index be649a12..ed2c5612 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -1057,6 +1057,286 @@ class FCTrack(FCRegion): return msg +class FCDisc(FCShapeTool): + """ + Resulting type: Polygon + """ + + def __init__(self, draw_app): + DrawTool.__init__(self, draw_app) + self.name = 'disc' + + try: + QtGui.QGuiApplication.restoreOverrideCursor() + except: + pass + self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_circle.png')) + QtGui.QGuiApplication.setOverrideCursor(self.cursor) + + size_ap = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) + self.buf_val = (size_ap / 2) if size_ap > 0 else 0.0000001 + + self.storage_obj = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['solid_geometry'] + + self.start_msg = _("Click on CENTER ...") + self.steps_per_circ = self.draw_app.app.defaults["gerber_circle_steps"] + + def click(self, point): + self.points.append(point) + + if len(self.points) == 1: + self.draw_app.app.inform.emit(_("Click on perimeter point to complete ...")) + return "Click on perimeter to complete ..." + + if len(self.points) == 2: + self.make() + return "Done." + + return "" + + def utility_geometry(self, data=None): + if len(self.points) == 1: + p1 = self.points[0] + p2 = data + radius = sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) + return DrawToolUtilityShape(Point(p1).buffer((radius + self.buf_val / 2), int(self.steps_per_circ / 4))) + + return None + + def make(self): + try: + QtGui.QGuiApplication.restoreOverrideCursor() + except: + pass + + self.draw_app.current_storage = self.storage_obj + + p1 = self.points[0] + p2 = self.points[1] + radius = distance(p1, p2) + self.geometry = DrawToolShape(Point(p1).buffer((radius + self.buf_val / 2), int(self.steps_per_circ / 4))) + self.draw_app.in_action = False + self.complete = True + self.draw_app.app.inform.emit(_("[success] Done.")) + + def clean_up(self): + self.draw_app.selected = [] + self.draw_app.apertures_table.clearSelection() + self.draw_app.plot_all() + + +class FCSemiDisc(FCShapeTool): + def __init__(self, draw_app): + DrawTool.__init__(self, draw_app) + self.name = 'semidisc' + + self.start_msg = _("Click on CENTER ...") + + # Direction of rotation between point 1 and 2. + # 'cw' or 'ccw'. Switch direction by hitting the + # 'o' key. + self.direction = "cw" + + # Mode + # C12 = Center, p1, p2 + # 12C = p1, p2, Center + # 132 = p1, p3, p2 + self.mode = "c12" # Center, p1, p2 + + size_ap = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) + self.buf_val = (size_ap / 2) if size_ap > 0 else 0.0000001 + + self.storage_obj = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['solid_geometry'] + + self.steps_per_circ = self.draw_app.app.defaults["gerber_circle_steps"] + + def click(self, point): + self.points.append(point) + + if len(self.points) == 1: + if self.mode == 'c12': + self.draw_app.app.inform.emit(_("Click on Start point ...")) + elif self.mode == '132': + self.draw_app.app.inform.emit(_("Click on Point3 ...")) + else: + self.draw_app.app.inform.emit(_("Click on Stop point to complete ...")) + return "Click on 1st point ..." + + if len(self.points) == 2: + if self.mode == 'c12': + self.draw_app.app.inform.emit(_("Click on Stop point to complete ...")) + elif self.mode == '132': + self.draw_app.app.inform.emit(_("Click on Point2 ...")) + else: + self.draw_app.app.inform.emit(_("Click on Center point to complete ...")) + return "Click on 2nd point to complete ..." + + if len(self.points) == 3: + self.make() + return "Done." + + return "" + + def on_key(self, key): + if key == 'D' or key == QtCore.Qt.Key_D: + self.direction = 'cw' if self.direction == 'ccw' else 'ccw' + return _('Direction: %s') % self.direction.upper() + + if key == 'M' or key == QtCore.Qt.Key_M: + if self.mode == 'c12': + self.mode = '12c' + return _('Mode: Start -> Stop -> Center. Click on Start point ...') + elif self.mode == '12c': + self.mode = '132' + return _('Mode: Point1 -> Point3 -> Point2. Click on 1st point ...') + else: + self.mode = 'c12' + return _('Mode: Center -> Start -> Stop. Click on Center ...') + + def utility_geometry(self, data=None): + if len(self.points) == 1: # Show the radius + center = self.points[0] + p1 = data + + return DrawToolUtilityShape(LineString([center, p1])) + + if len(self.points) == 2: # Show the arc + + if self.mode == 'c12': + center = self.points[0] + p1 = self.points[1] + p2 = data + + radius = sqrt((center[0] - p1[0]) ** 2 + (center[1] - p1[1]) ** 2) + (self.buf_val / 2) + startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) + stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) + + return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle, + self.direction, self.steps_per_circ)), + Point(center)]) + + elif self.mode == '132': + p1 = array(self.points[0]) + p3 = array(self.points[1]) + p2 = array(data) + + center, radius, t = three_point_circle(p1, p2, p3) + direction = 'cw' if sign(t) > 0 else 'ccw' + radius += (self.buf_val / 2) + + startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) + stopangle = arctan2(p3[1] - center[1], p3[0] - center[0]) + + return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle, + direction, self.steps_per_circ)), + Point(center), Point(p1), Point(p3)]) + + else: # '12c' + p1 = array(self.points[0]) + p2 = array(self.points[1]) + + # Midpoint + a = (p1 + p2) / 2.0 + + # Parallel vector + c = p2 - p1 + + # Perpendicular vector + b = dot(c, array([[0, -1], [1, 0]], dtype=float32)) + b /= norm(b) + + # Distance + t = distance(data, a) + + # Which side? Cross product with c. + # cross(M-A, B-A), where line is AB and M is test point. + side = (data[0] - p1[0]) * c[1] - (data[1] - p1[1]) * c[0] + t *= sign(side) + + # Center = a + bt + center = a + b * t + + radius = norm(center - p1) + (self.buf_val / 2) + startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) + stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) + + return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle, + self.direction, self.steps_per_circ)), + Point(center)]) + + return None + + def make(self): + self.draw_app.current_storage = self.storage_obj + + if self.mode == 'c12': + center = self.points[0] + p1 = self.points[1] + p2 = self.points[2] + + radius = distance(center, p1) + (self.buf_val / 2) + startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) + stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) + self.geometry = DrawToolShape(Polygon(arc(center, radius, startangle, stopangle, + self.direction, self.steps_per_circ))) + + elif self.mode == '132': + p1 = array(self.points[0]) + p3 = array(self.points[1]) + p2 = array(self.points[2]) + + center, radius, t = three_point_circle(p1, p2, p3) + direction = 'cw' if sign(t) > 0 else 'ccw' + radius += (self.buf_val / 2) + + startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) + stopangle = arctan2(p3[1] - center[1], p3[0] - center[0]) + + self.geometry = DrawToolShape(Polygon(arc(center, radius, startangle, stopangle, + direction, self.steps_per_circ))) + + else: # self.mode == '12c' + p1 = array(self.points[0]) + p2 = array(self.points[1]) + pc = array(self.points[2]) + + # Midpoint + a = (p1 + p2) / 2.0 + + # Parallel vector + c = p2 - p1 + + # Perpendicular vector + b = dot(c, array([[0, -1], [1, 0]], dtype=float32)) + b /= norm(b) + + # Distance + t = distance(pc, a) + + # Which side? Cross product with c. + # cross(M-A, B-A), where line is AB and M is test point. + side = (pc[0] - p1[0]) * c[1] - (pc[1] - p1[1]) * c[0] + t *= sign(side) + + # Center = a + bt + center = a + b * t + + radius = norm(center - p1) + (self.buf_val / 2) + startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) + stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) + + self.geometry = DrawToolShape(Polygon(arc(center, radius, startangle, stopangle, + self.direction, self.steps_per_circ))) + self.draw_app.in_action = False + self.complete = True + self.draw_app.app.inform.emit(_("[success] Done.")) + + def clean_up(self): + self.draw_app.selected = [] + self.draw_app.apertures_table.clearSelection() + self.draw_app.plot_all() + + class FCScale(FCShapeTool): def __init__(self, draw_app): FCShapeTool.__init__(self, draw_app) @@ -1763,6 +2043,10 @@ class FlatCAMGrbEditor(QtCore.QObject): "constructor": FCRegion}, "poligonize": {"button": self.app.ui.grb_convert_poly_btn, "constructor": FCPoligonize}, + "semidisc": {"button": self.app.ui.grb_add_semidisc_btn, + "constructor": FCSemiDisc}, + "disc": {"button": self.app.ui.grb_add_disc_btn, + "constructor": FCDisc}, "buffer": {"button": self.app.ui.aperture_buffer_btn, "constructor": FCBuffer}, "scale": {"button": self.app.ui.aperture_scale_btn, @@ -1908,6 +2192,8 @@ class FlatCAMGrbEditor(QtCore.QObject): self.app.ui.grb_add_region_menuitem.triggered.connect(self.on_region_add) self.app.ui.grb_convert_poly_menuitem.triggered.connect(self.on_poligonize) + self.app.ui.grb_add_semidisc_menuitem.triggered.connect(self.on_add_semidisc) + self.app.ui.grb_add_disc_menuitem.triggered.connect(self.on_disc_add) self.app.ui.grb_add_buffer_menuitem.triggered.connect(self.on_buffer) self.app.ui.grb_add_scale_menuitem.triggered.connect(self.on_scale) self.app.ui.grb_transform_menuitem.triggered.connect(self.transform_tool.run) @@ -3350,6 +3636,12 @@ class FlatCAMGrbEditor(QtCore.QObject): def on_poligonize(self): self.select_tool('poligonize') + def on_disc_add(self): + self.select_tool('disc') + + def on_add_semidisc(self): + self.select_tool('semidisc') + def on_buffer(self): buff_value = 0.01 log.debug("FlatCAMGrbEditor.on_buffer()") diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index a0643961..19241777 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -472,6 +472,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.grb_convert_poly_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/poligonize32.png'), _("Poligonize\tALT+N")) + self.grb_add_semidisc_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/semidisc32.png'), + _("Add SemiDisc\tE")) + self.grb_add_disc_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/disc32.png'), + _("Add Disc\tD")) self.grb_add_buffer_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer\tB')) self.grb_add_scale_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/scale32.png'), @@ -695,6 +699,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.grb_convert_poly_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/poligonize32.png'), _("Poligonize")) + + self.grb_add_semidisc_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/semidisc32.png'), _("SemiDisc")) + self.grb_add_disc_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/disc32.png'), _("Disc")) self.grb_edit_toolbar.addSeparator() self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer')) @@ -1448,6 +1455,14 @@ class FlatCAMGUI(QtWidgets.QMainWindow): C  Copy + + D +  Add Disc + + + E +  Add SemiDisc + J  Jump to Location (x, y) @@ -2673,6 +2688,18 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to copy.")) return + # Add Disc Tool + if key == QtCore.Qt.Key_D or key == 'D': + self.app.grb_editor.launched_from_shortcuts = True + self.app.grb_editor.select_tool('disc') + return + + # Add SemiDisc Tool + if key == QtCore.Qt.Key_E or key == 'E': + self.app.grb_editor.launched_from_shortcuts = True + self.app.grb_editor.select_tool('semidisc') + return + # Grid Snap if key == QtCore.Qt.Key_G or key == 'G': self.app.grb_editor.launched_from_shortcuts = True diff --git a/share/disc32.png b/share/disc32.png new file mode 100644 index 0000000000000000000000000000000000000000..da1593d547e86df25ef760bd79a9a785e29f2b7a GIT binary patch literal 435 zcmV;k0ZjghP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Z2(iK~z{r?bk_8 z!!Q`d@tlEi4XlAB&)}AiAqR$PVM-Mvgwn4Ql;8os>ZRu zG>i1i3U+XUV{Bs?gJugCnBphl5gMbhVOhf+zTqzFeBl=Qr>Zi+8{9>5yx;)6a*9v5 z!X;EgwThx&t-sL~>_fHokd<}3!xgIWidD>w7Mv>(;ToOM<5RNQOR4rIlDUMN80j3z zbZs(G9$=%b)J+(|5?a}X8$1M^Bdr|54IYB7)yn8UVDwus`XW?xp1TQ=?vad|2{$Ry z8IsX9IpkKXQ*i^Gxq+J;;RPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Z2(iK~z{r?bb0U z24Nh>@#D>+6fsyODT7iL29rfLc4W1f$n@4*7G~KnDJ5Z27_Cw=nY{yIqEPt$UPL`p>6;HKU9-d|?Q|YQ-TmEO!W22}f|_ za*SYA5FeKf1Ze>7ZyvXXAnhSJE<5mEH>}@YPT{>dSmSaJ9Zs)p!5VkQ;I(sD!_FqW zb_HwLc}Fk2bpdNQP7kOicxfNjXwnM2vCNW_$&1$N1i8=36h2^G zFpo=gqNc|21na_CoS_~6ss{(K2GTrZ3A(EaYXsYPfHf*NSVkATrNcCcNz7vgdJgDi drcu2WMc>e)?T%^c-W&h`002ovPDHLkV1o6axkUf~ literal 0 HcmV?d00001