From b5f77eac1be9e2d48b0b540156c6964244db0312 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 5 Aug 2020 11:46:56 +0300 Subject: [PATCH] - Tool Cutout - more work in gaps thickness control feature - Tool Cutout - added some icons to buttons --- CHANGELOG.md | 7 +- appTools/ToolCutOut.py | 246 ++++++++++++------ app_Main.py | 2 +- assets/resources/dark_resources/gaps32.png | Bin 0 -> 512 bytes .../resources/dark_resources/irregular32.png | Bin 0 -> 885 bytes assets/resources/gaps32.png | Bin 0 -> 444 bytes assets/resources/irregular32.png | Bin 0 -> 666 bytes 7 files changed, 171 insertions(+), 84 deletions(-) create mode 100644 assets/resources/dark_resources/gaps32.png create mode 100644 assets/resources/dark_resources/irregular32.png create mode 100644 assets/resources/gaps32.png create mode 100644 assets/resources/irregular32.png diff --git a/CHANGELOG.md b/CHANGELOG.md index c6a88af3..d010c259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,16 @@ CHANGELOG for FlatCAM beta ================================================= +5.08.2020 + +- Tool Cutout - more work in gaps thickness control feature +- Tool Cutout - added some icons to buttons + 4.08.2020 - removed the Toolchange Macro feature (in the future it will be replaced by full preprocessor customization) - modified GUI in Preferences -- Tool Cutout - working in adding gaps suppression feature; added the UI in the Tool +- Tool Cutout - working in adding gaps thickness control feature; added the UI in the Tool 3.08.2020 diff --git a/appTools/ToolCutOut.py b/appTools/ToolCutOut.py index 765e1608..566a4886 100644 --- a/appTools/ToolCutOut.py +++ b/appTools/ToolCutOut.py @@ -320,42 +320,54 @@ class CutOut(AppTool): pass else: if gaps == '8' or gaps == '2LR': - geom = self.subtract_poly_from_geo(geom, - xxmin - gapsize, # botleft_x - py - gapsize + leny / 4, # botleft_y - xxmax + gapsize, # topright_x - py + gapsize + leny / 4) # topright_y - geom = self.subtract_poly_from_geo(geom, - xxmin - gapsize, - py - gapsize - leny / 4, - xxmax + gapsize, - py + gapsize - leny / 4) + points = ( + xxmin - gapsize, # botleft_x + py - gapsize + leny / 4, # botleft_y + xxmax + gapsize, # topright_x + py + gapsize + leny / 4 # topright_y + ) + geom = self.subtract_poly_from_geo(geom, points) + points = ( + xxmin - gapsize, + py - gapsize - leny / 4, + xxmax + gapsize, + py + gapsize - leny / 4 + ) + geom = self.subtract_poly_from_geo(geom, points) if gaps == '8' or gaps == '2TB': - geom = self.subtract_poly_from_geo(geom, - px - gapsize + lenx / 4, - yymin - gapsize, - px + gapsize + lenx / 4, - yymax + gapsize) - geom = self.subtract_poly_from_geo(geom, - px - gapsize - lenx / 4, - yymin - gapsize, - px + gapsize - lenx / 4, - yymax + gapsize) + points = ( + px - gapsize + lenx / 4, + yymin - gapsize, + px + gapsize + lenx / 4, + yymax + gapsize + ) + geom = self.subtract_poly_from_geo(geom, points) + points = ( + px - gapsize - lenx / 4, + yymin - gapsize, + px + gapsize - lenx / 4, + yymax + gapsize + ) + geom = self.subtract_poly_from_geo(geom, points) if gaps == '4' or gaps == 'LR': - geom = self.subtract_poly_from_geo(geom, - xxmin - gapsize, - py - gapsize, - xxmax + gapsize, - py + gapsize) + points = ( + xxmin - gapsize, + py - gapsize, + xxmax + gapsize, + py + gapsize + ) + geom = self.subtract_poly_from_geo(geom, points) if gaps == '4' or gaps == 'TB': - geom = self.subtract_poly_from_geo(geom, - px - gapsize, - yymin - gapsize, - px + gapsize, - yymax + gapsize) + points = ( + px - gapsize, + yymin - gapsize, + px + gapsize, + yymax + gapsize + ) + geom = self.subtract_poly_from_geo(geom, points) try: for g in geom: @@ -520,42 +532,55 @@ class CutOut(AppTool): pass else: if gaps == '8' or gaps == '2LR': - geom = self.subtract_poly_from_geo(geom, - xmin - gapsize, # botleft_x - py - gapsize + leny / 4, # botleft_y - xmax + gapsize, # topright_x - py + gapsize + leny / 4) # topright_y - geom = self.subtract_poly_from_geo(geom, - xmin - gapsize, - py - gapsize - leny / 4, - xmax + gapsize, - py + gapsize - leny / 4) + points = ( + xmin - gapsize, # botleft_x + py - gapsize + leny / 4, # botleft_y + xmax + gapsize, # topright_x + py + gapsize + leny / 4 # topright_y + ) + geom = self.subtract_poly_from_geo(geom, points) + points = ( + xmin - gapsize, + py - gapsize - leny / 4, + xmax + gapsize, + py + gapsize - leny / 4 + ) + geom = self.subtract_poly_from_geo(geom, points) if gaps == '8' or gaps == '2TB': - geom = self.subtract_poly_from_geo(geom, - px - gapsize + lenx / 4, - ymin - gapsize, - px + gapsize + lenx / 4, - ymax + gapsize) - geom = self.subtract_poly_from_geo(geom, - px - gapsize - lenx / 4, - ymin - gapsize, - px + gapsize - lenx / 4, - ymax + gapsize) + points = ( + px - gapsize + lenx / 4, + ymin - gapsize, + px + gapsize + lenx / 4, + ymax + gapsize + ) + geom = self.subtract_poly_from_geo(geom, points) + points = ( + px - gapsize - lenx / 4, + ymin - gapsize, + px + gapsize - lenx / 4, + ymax + gapsize + ) + geom = self.subtract_poly_from_geo(geom, points) if gaps == '4' or gaps == 'LR': - geom = self.subtract_poly_from_geo(geom, - xmin - gapsize, - py - gapsize, - xmax + gapsize, - py + gapsize) + points = ( + xmin - gapsize, + py - gapsize, + xmax + gapsize, + py + gapsize + ) + geom = self.subtract_poly_from_geo(geom, points) if gaps == '4' or gaps == 'TB': - geom = self.subtract_poly_from_geo(geom, - px - gapsize, - ymin - gapsize, - px + gapsize, - ymax + gapsize) + points = ( + px - gapsize, + ymin - gapsize, + px + gapsize, + ymax + gapsize + ) + geom = self.subtract_poly_from_geo(geom, points) + try: for g in geom: proc_geometry.append(g) @@ -1088,20 +1113,29 @@ class CutOut(AppTool): self.draw_utility_geometry(geo=geo) @staticmethod - def subtract_poly_from_geo(solid_geo, x0, y0, x1, y1): + def subtract_poly_from_geo(solid_geo, pts): """ Subtract polygon made from points from the given object. This only operates on the paths in the original geometry, i.e. it converts polygons into paths. - :param x0: x coord for lower left vertex of the polygon. - :param y0: y coord for lower left vertex of the polygon. - :param x1: x coord for upper right vertex of the polygon. - :param y1: y coord for upper right vertex of the polygon. + :param solid_geo: Geometry from which to subtract. + :param pts: a tuple of coordinates in format (x0, y0, x1, y1) + :type pts: tuple + + x0: x coord for lower left vertex of the polygon. + y0: y coord for lower left vertex of the polygon. + x1: x coord for upper right vertex of the polygon. + y1: y coord for upper right vertex of the polygon. - :param solid_geo: Geometry from which to substract. If none, use the solid_geomety property of the object :return: none """ + + x0 = pts[0] + y0 = pts[1] + x1 = pts[2] + y1 = pts[3] + points = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)] # pathonly should be allways True, otherwise polygons are not subtracted @@ -1120,6 +1154,50 @@ class CutOut(AppTool): return unary_union(diffs) + @staticmethod + def intersect_poly_with_geo(solid_geo, pts, margin): + """ + Intersections with a polygon made from points from the given object. + This only operates on the paths in the original geometry, + i.e. it converts polygons into paths. + + :param solid_geo: Geometry from which to get intersections. + :param pts: a tuple of coordinates in format (x0, y0, x1, y1) + :type pts: tuple + :param margin: a distance (buffer) applied to each solid_geo + + x0: x coord for lower left vertex of the polygon. + y0: y coord for lower left vertex of the polygon. + x1: x coord for upper right vertex of the polygon. + y1: y coord for upper right vertex of the polygon. + + :return: none + """ + + x0 = pts[0] + y0 = pts[1] + x1 = pts[2] + y1 = pts[3] + + points = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)] + + # pathonly should be allways True, otherwise polygons are not subtracted + flat_geometry = CutOut.flatten(geometry=solid_geo) + + log.debug("%d paths" % len(flat_geometry)) + + polygon = Polygon(points) + toolgeo = cascaded_union(polygon) + + intersects = [] + for target in flat_geometry: + if type(target) == LineString or type(target) == LinearRing: + intersects.append(target.intersection(toolgeo)) + else: + log.warning("Not implemented.") + + return unary_union(intersects) + @staticmethod def flatten(geometry): """ @@ -1351,29 +1429,29 @@ class CutoutUI: grid0.addWidget(self.mpass_cb, 10, 0) grid0.addWidget(self.maxdepth_entry, 10, 1) - # Suppress gaps - self.suppress_cb = FCCheckBox('%s:' % _("Suppress gaps")) - self.suppress_cb.setToolTip( + # Thin gaps + self.thin_cb = FCCheckBox('%s:' % _("Thin gaps")) + self.thin_cb.setToolTip( _("Active only when multi depth is active.\n" - "If checked, the gaps will not be used until the specified depth.")) + "If checked, the gaps will start at the specified depth.")) - self.suppress_depth_entry = FCDoubleSpinner(callback=self.confirmation_message) - self.suppress_depth_entry.set_precision(self.decimals) - self.suppress_depth_entry.setRange(0, 9999.9999) - self.suppress_depth_entry.setSingleStep(0.1) + self.thin_depth_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.thin_depth_entry.set_precision(self.decimals) + self.thin_depth_entry.setRange(0, 9999.9999) + self.thin_depth_entry.setSingleStep(0.1) - self.suppress_depth_entry.setToolTip( + self.thin_depth_entry.setToolTip( _("Active only when multi depth is active.\n" - "If checked, the gaps will not be used until the specified depth.")) + "If checked, the gaps will start at the specified depth.")) - grid0.addWidget(self.suppress_cb, 12, 0) - grid0.addWidget(self.suppress_depth_entry, 12, 1) + grid0.addWidget(self.thin_cb, 12, 0) + grid0.addWidget(self.thin_depth_entry, 12, 1) self.ois_mpass_geo = OptionalInputSection(self.mpass_cb, [ self.maxdepth_entry, - self.suppress_cb, - self.suppress_depth_entry + self.thin_cb, + self.thin_depth_entry ]) # Margin @@ -1461,7 +1539,8 @@ class CutoutUI: grid0.addWidget(self.gaps, 26, 1) # Buttons - self.ff_cutout_object_btn = FCButton(_("Generate Freeform Geometry")) + self.ff_cutout_object_btn = FCButton(_("Generate Geometry")) + self.ff_cutout_object_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/irregular32.png')) self.ff_cutout_object_btn.setToolTip( _("Cutout the selected object.\n" "The cutout shape can be of any shape.\n" @@ -1475,7 +1554,8 @@ class CutoutUI: """) grid0.addWidget(self.ff_cutout_object_btn, 28, 0, 1, 2) - self.rect_cutout_object_btn = FCButton(_("Generate Rectangular Geometry")) + self.rect_cutout_object_btn = FCButton(_("Generate Geometry")) + self.rect_cutout_object_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/rectangle32.png')) self.rect_cutout_object_btn.setToolTip( _("Cutout the selected object.\n" "The resulting cutout shape is\n" @@ -1517,6 +1597,7 @@ class CutoutUI: # Generate a surrounding Geometry object self.man_geo_creation_btn = FCButton(_("Generate Manual Geometry")) + self.man_geo_creation_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/rectangle32.png')) self.man_geo_creation_btn.setToolTip( _("If the object to be cutout is a Gerber\n" "first create a Geometry that surrounds it,\n" @@ -1548,6 +1629,7 @@ class CutoutUI: grid0.addWidget(self.man_object_combo, 44, 0, 1, 2) self.man_gaps_creation_btn = FCButton(_("Manual Add Bridge Gaps")) + self.man_gaps_creation_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/gaps32.png')) self.man_gaps_creation_btn.setToolTip( _("Use the left mouse button (LMB) click\n" "to create a bridge gap to separate the PCB from\n" diff --git a/app_Main.py b/app_Main.py index 70e7dfd6..50813852 100644 --- a/app_Main.py +++ b/app_Main.py @@ -2436,7 +2436,7 @@ class App(QtCore.QObject): continue else: self.inform.emit('[WARNING_NOTCL] %s' % - _("Select a Gerber, Geometry, Excellon or CNCJobObject to update.")) + _("Select a Gerber, Geometry, Excellon or CNCJob Object to update.")) return elif response == bt_cancel: return diff --git a/assets/resources/dark_resources/gaps32.png b/assets/resources/dark_resources/gaps32.png new file mode 100644 index 0000000000000000000000000000000000000000..609a1fea98127e4fcc462f2b0872cb8f0ed4566f GIT binary patch literal 512 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!ZpAr#1%;2efRFn|NoC) zzJ2`S?d{wD@4tWl_!Wr#_~o0&k3T+o@#g>k{|{cj1}b<6CZ7OhU%q|x@*POa%eO!i z5{;*UEEg>a@(X6*XFa)Yh7ML)4W^?kDh!;y(=(1=TyPHZ%tw$!Ht}KU6#}4@LlJ&+VdyS zZ>{y;m94+-^PT4}>G_l2Sy1|Kwq4EqhY7PH?ekAD9blS!eagzVP#@yWo)QzU|?ln5Y&?R97RKJeoAIqC2kGMH*ZD*HAsSN t2+mI{DNig)WhgH%*UQYyE>2D?NY%?PN}v7CMhd8i!PC{xWt~$(698K7#nJ!( literal 0 HcmV?d00001 diff --git a/assets/resources/dark_resources/irregular32.png b/assets/resources/dark_resources/irregular32.png new file mode 100644 index 0000000000000000000000000000000000000000..1eca0ec887a0524121c8e71ca783f4caa66bcc5b GIT binary patch literal 885 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10;(35ih%1o3|LWDVXYZf9 ze0%%$|HrT1J%0K2(Tg`Q@(ob@!RyzLUqZy+J$m^TDD~{wrw7kKQcs?If=B@cfm|T_ z(W^I4UVzm6|Ns99NEgVct5^R6N$lwH$A=$20HyA~e-AVqX!XOFZ=Zs7J%9cgC;~PX zNCK?`n)dGD^S4i4y?^`)WbES?AXhyGSqI{t`TzeBkOA}p&<#M1Z{I$C4iX3YG1+9p z1|a2K666=mAR%wbUjC_Ot^b`Dl6t>%b{`Ybye~ZE_uJm+gn2(%(q6q|JN`1b`%TxO zgF=GO1Zt~YUFt-S^qiP_CPvF3@1gFVxq(+%11fUo%b3L)KlutYmNCiO-G!lpRn`N@ z;VkfoEM{QfI|9OtQ?>b|fr9KMp1!W^PdHdP6!}Bu2=W4jetEh$hFF~58~mKF$v~nc zP+W_TW%~+?!uNaA@BgpWxw1>cMfmaGUH#URzU`ZL^poWpvqNvfy51x-OfC>lka@rB zHuJ*iNAEmuzwo$jBC|}0AkRu?i$%OK5A;3e^qX+7+`uIT&$y>74o>dF{d(gFjDBF3jyau~^oB+2)^*%s7<)&AU>xv^Hw_ z)rF18k-G7-HaZ@?`OYAnQR`e>_+DlUp*kk^*soXQ&$q?)FK#IZ0z{o(?z(Uu+G{nHd%GlV-&{W&Nz{k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5n0T@z;^_M8K-LVNdpDhOFVsD*`IK*a%d=TZ#%XeDAeWY;uzv_{Oy#Dg3S&*Eaf~s zhyN=U%ZfO%=e6yMVfo|W^Ny{+>Bsv6ox$&zg!FQF|aZ=)iyA&GBEJu)H{WuAvZrIGp!Q028)nsWk3y*ARB`7(@M${ ni&7cN%ggmL^RkPR6AM!H@{7`Ezq647Dq`?-^>bP0l+XkK^U{zi literal 0 HcmV?d00001 diff --git a/assets/resources/irregular32.png b/assets/resources/irregular32.png new file mode 100644 index 0000000000000000000000000000000000000000..da0c2c4b7772cbca5d03c9d85abd61a4ad3bb8bd GIT binary patch literal 666 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5n0T@z;^_M8K-LVNdpDhOFVsD*`IK*aww=*7$Lx&@d^2q<{bdcmL_cevC~6YD#4k=w`^t;klUSJM2AAD<+>{@_#Gg0S#c#v; z+1f|%SBK0nWsO>Ra@uwUwkK-)SS*7TCb^1rFiYv{7>iF@xwz=qt1B@u$gb3l@0wzhd6KX45&v^OqK{x^+7vvawC~<(wl1 zlZx(4Vr%_aztuKEr~iz_2OYL&NjE=f`%KbeV;8FHbi7)>HS)Qx=Z&`Po4%+2F$i=A zhhK1(ECWV^YKdz^NlIc#s#S7PDv)9@GB7gGHL%b%Fby%VurfBXGPcw