From 70f6469ea80fb9c4ef6b3e1bf3806a49ba102c40 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Thu, 21 Nov 2019 01:44:55 +0200 Subject: [PATCH] - Tool Fiducials - added the GUI and the shortcut key --- FlatCAMApp.py | 5 + README.md | 4 + flatcamGUI/FlatCAMGUI.py | 15 +- flatcamTools/ToolFiducials.py | 332 +++++++++------------------------- share/fiducials_32.png | Bin 0 -> 330 bytes 5 files changed, 109 insertions(+), 247 deletions(-) create mode 100644 share/fiducials_32.png diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 86613dd5..162896a2 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2331,6 +2331,7 @@ class App(QtCore.QObject): self.cal_exc_tool = None self.qrcode_tool = None self.copper_thieving_tool = None + self.fiducial_tool = None # always install tools only after the shell is initialized because the self.inform.emit() depends on shell self.install_tools() @@ -2942,6 +2943,9 @@ class App(QtCore.QObject): self.copper_thieving_tool = ToolCopperThieving(self) self.copper_thieving_tool.install(icon=QtGui.QIcon('share/copperfill32.png'), pos=self.ui.menutool) + self.fiducial_tool = ToolFiducials(self) + self.fiducial_tool.install(icon=QtGui.QIcon('share/fiducials_32.png'), pos=self.ui.menutool) + self.qrcode_tool = QRCode(self) self.qrcode_tool.install(icon=QtGui.QIcon('share/qrcode32.png'), pos=self.ui.menutool) @@ -3070,6 +3074,7 @@ class App(QtCore.QObject): self.ui.transform_btn.triggered.connect(lambda: self.transform_tool.run(toggle=True)) self.ui.qrcode_btn.triggered.connect(lambda: self.qrcode_tool.run(toggle=True)) self.ui.copperfill_btn.triggered.connect(lambda: self.copper_thieving_tool.run(toggle=True)) + self.ui.fiducials_btn.triggered.connect(lambda: self.fiducial_tool.run(toggle=True)) def object2editor(self): """ diff --git a/README.md b/README.md index 9c0c034d..fedbc70b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing. ================================================= +20.11.2019 + +- Tool Fiducials - added the GUI and the shortcut key + 19.11.2019 - removed the f-strings replacing them with the traditional string formatting due of not being supported by older versions of Python 3 diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index d4fd03ba..b2f31413 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -751,6 +751,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.copperfill_btn = self.toolbartools.addAction(QtGui.QIcon('share/copperfill32.png'), _("Copper Thieving Tool")) + self.fiducials_btn = self.toolbartools.addAction(QtGui.QIcon('share/fiducials_32.png'), _("Fiducials Tool")) + # ######################################################################## # ########################## Excellon Editor Toolbar# #################### # ######################################################################## @@ -1319,6 +1321,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): ALT+E  %s + + ALT+J +  %s + ALT+K  %s @@ -1438,7 +1444,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): _("Rotate by 90 degree CCW"), _("Run a Script"), _("Toggle the workspace"), _("Skew on X axis"), _("Skew on Y axis"), # ALT section - _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"), + _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"), _("Fiducials Tool"), _("Solder Paste Dispensing Tool"), _("Film PCB Tool"), _("Non-Copper Clearing Tool"), _("Optimal Tool"), _("Paint Area Tool"), _("QRCode Tool"), _("Rules Check Tool"), @@ -2177,6 +2183,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.copperfill_btn = self.toolbartools.addAction(QtGui.QIcon('share/copperfill32.png'), _("Copper Thieving Tool")) + self.fiducials_btn = self.toolbartools.addAction(QtGui.QIcon('share/fiducials_32.png'), _("Fiducials Tool")) + # ## Excellon Editor Toolbar # ## self.select_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select")) self.add_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/plus16.png'), _('Add Drill Hole')) @@ -2523,6 +2531,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.app.on_toggle_grid_lines() return + # Fiducials Tool + if key == QtCore.Qt.Key_J: + self.app.fiducial_tool.run(toggle=True) + return + # Solder Paste Dispensing Tool if key == QtCore.Qt.Key_K: self.app.paste_tool.run(toggle=True) diff --git a/flatcamTools/ToolFiducials.py b/flatcamTools/ToolFiducials.py index 6188166c..e4da1a52 100644 --- a/flatcamTools/ToolFiducials.py +++ b/flatcamTools/ToolFiducials.py @@ -59,52 +59,32 @@ class ToolFiducials(FlatCAMTool): self.layout.addWidget(title_label) self.layout.addWidget(QtWidgets.QLabel('')) - # ## Grid Layout - i_grid_lay = QtWidgets.QGridLayout() - self.layout.addLayout(i_grid_lay) - i_grid_lay.setColumnStretch(0, 0) - i_grid_lay.setColumnStretch(1, 1) - - self.grb_object_combo = QtWidgets.QComboBox() - self.grb_object_combo.setModel(self.app.collection) - self.grb_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.grb_object_combo.setCurrentIndex(1) - - self.grbobj_label = QtWidgets.QLabel("%s:" % _("GERBER")) - self.grbobj_label.setToolTip( - _("Gerber Object to which will be added a copper thieving.") - ) - - i_grid_lay.addWidget(self.grbobj_label, 0, 0) - i_grid_lay.addWidget(self.grb_object_combo, 0, 1, 1, 2) - i_grid_lay.addWidget(QtWidgets.QLabel(''), 1, 0) - # ## Grid Layout grid_lay = QtWidgets.QGridLayout() self.layout.addLayout(grid_lay) grid_lay.setColumnStretch(0, 0) grid_lay.setColumnStretch(1, 1) - self.copper_fill_label = QtWidgets.QLabel('%s' % _('Parameters')) + self.copper_fill_label = QtWidgets.QLabel('%s:' % _('Parameters')) self.copper_fill_label.setToolTip( _("Parameters used for this tool.") ) grid_lay.addWidget(self.copper_fill_label, 0, 0, 1, 2) - # CLEARANCE # - self.clearance_label = QtWidgets.QLabel('%s:' % _("Clearance")) - self.clearance_label.setToolTip( - _("This set the distance between the copper thieving components\n" - "(the polygon fill may be split in multiple polygons)\n" - "and the copper traces in the Gerber file.") + # DIAMETER # + self.dia_label = QtWidgets.QLabel('%s:' % _("Diameter")) + self.dia_label.setToolTip( + _("This set the fiducial diameter.\n" + "The soldermask opening is double than that.") ) - self.clearance_entry = FCDoubleSpinner() - self.clearance_entry.set_range(0.00001, 9999.9999) - self.clearance_entry.set_precision(self.decimals) - self.clearance_entry.setSingleStep(0.1) + self.dia_entry = FCDoubleSpinner() + self.dia_entry.set_range(1.0000, 3.0000) + self.dia_entry.set_precision(self.decimals) + self.dia_entry.setWrapping(True) + self.dia_entry.setSingleStep(0.1) - grid_lay.addWidget(self.clearance_label, 1, 0) - grid_lay.addWidget(self.clearance_entry, 1, 1) + grid_lay.addWidget(self.dia_label, 1, 0) + grid_lay.addWidget(self.dia_entry, 1, 1) # MARGIN # self.margin_label = QtWidgets.QLabel('%s:' % _("Margin")) @@ -119,221 +99,94 @@ class ToolFiducials(FlatCAMTool): grid_lay.addWidget(self.margin_label, 2, 0) grid_lay.addWidget(self.margin_entry, 2, 1) - # Reference # - self.reference_radio = RadioSet([ - {'label': _('Itself'), 'value': 'itself'}, - {"label": _("Area Selection"), "value": "area"}, - {'label': _("Reference Object"), 'value': 'box'} - ], orientation='vertical', stretch=False) - self.reference_label = QtWidgets.QLabel(_("Reference:")) - self.reference_label.setToolTip( - _("- 'Itself' - the copper thieving extent is based on the object that is copper cleared.\n " - "- 'Area Selection' - left mouse click to start selection of the area to be filled.\n" - "- 'Reference Object' - will do copper thieving within the area specified by another object.") - ) - grid_lay.addWidget(self.reference_label, 3, 0) - grid_lay.addWidget(self.reference_radio, 3, 1) - - self.box_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type")) - self.box_combo_type_label.setToolTip( - _("The type of FlatCAM object to be used as copper thieving reference.\n" - "It can be Gerber, Excellon or Geometry.") - ) - self.box_combo_type = QtWidgets.QComboBox() - self.box_combo_type.addItem(_("Reference Gerber")) - self.box_combo_type.addItem(_("Reference Excellon")) - self.box_combo_type.addItem(_("Reference Geometry")) - - grid_lay.addWidget(self.box_combo_type_label, 4, 0) - grid_lay.addWidget(self.box_combo_type, 4, 1) - - self.box_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object")) - self.box_combo_label.setToolTip( - _("The FlatCAM object to be used as non copper clearing reference.") - ) - self.box_combo = QtWidgets.QComboBox() - self.box_combo.setModel(self.app.collection) - self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.box_combo.setCurrentIndex(1) - - grid_lay.addWidget(self.box_combo_label, 5, 0) - grid_lay.addWidget(self.box_combo, 5, 1) - - self.box_combo.hide() - self.box_combo_label.hide() - self.box_combo_type.hide() - self.box_combo_type_label.hide() - - # Bounding Box Type # - self.bbox_type_radio = RadioSet([ - {'label': _('Rectangular'), 'value': 'rect'}, - {"label": _("Minimal"), "value": "min"} + # Mode # + self.mode_radio = RadioSet([ + {'label': _('Auto'), 'value': 'auto'}, + {"label": _("Manual"), "value": "manual"} ], stretch=False) - self.bbox_type_label = QtWidgets.QLabel(_("Box Type:")) - self.bbox_type_label.setToolTip( - _("- 'Rectangular' - the bounding box will be of rectangular shape.\n " - "- 'Minimal' - the bounding box will be the convex hull shape.") + self.mode_label = QtWidgets.QLabel(_("Mode:")) + self.mode_label.setToolTip( + _("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n " + "- 'Manual' - manual placement of fiducials.") ) - grid_lay.addWidget(self.bbox_type_label, 6, 0) - grid_lay.addWidget(self.bbox_type_radio, 6, 1) - self.bbox_type_label.hide() - self.bbox_type_radio.hide() + grid_lay.addWidget(self.mode_label, 3, 0) + grid_lay.addWidget(self.mode_radio, 3, 1) + + # Position for second fiducial # + self.pos_radio = RadioSet([ + {'label': _('Up'), 'value': 'up'}, + {"label": _("Down"), "value": "down"}, + {"label": _("None"), "value": "no"} + ], stretch=False) + self.pos_label = QtWidgets.QLabel('%s:' % _("Second fiducial")) + self.pos_label.setToolTip( + _("The position for the second fiducial.\n" + "- 'Up' - the order is: bottom-left, top-left, top-right.\n " + "- 'Down' - the order is: bottom-left, bottom-right, top-right.\n" + "- 'None' - there is no second fiducial. The order is: bottom-left, top-right.") + ) + grid_lay.addWidget(self.pos_label, 4, 0) + grid_lay.addWidget(self.pos_radio, 4, 1) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid_lay.addWidget(separator_line, 7, 0, 1, 2) + grid_lay.addWidget(separator_line, 5, 0, 1, 2) - # Fill Type - self.fill_type_radio = RadioSet([ - {'label': _('Solid'), 'value': 'solid'}, - {"label": _("Dots Grid"), "value": "dot"}, - {"label": _("Squares Grid"), "value": "square"}, - {"label": _("Lines Grid"), "value": "line"} - ], orientation='vertical', stretch=False) - self.fill_type_label = QtWidgets.QLabel(_("Fill Type:")) - self.fill_type_label.setToolTip( - _("- 'Solid' - copper thieving will be a solid polygon.\n " - "- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n" - "- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n" - "- 'Lines Grid' - the empty area will be filled with a pattern of lines.") + # Copper Gerber object + self.grb_object_combo = QtWidgets.QComboBox() + self.grb_object_combo.setModel(self.app.collection) + self.grb_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.grb_object_combo.setCurrentIndex(1) + + self.grbobj_label = QtWidgets.QLabel("%s:" % _("Copper Gerber")) + self.grbobj_label.setToolTip( + _("Gerber Object to which will be added a copper thieving.") ) - grid_lay.addWidget(self.fill_type_label, 8, 0) - grid_lay.addWidget(self.fill_type_radio, 8, 1) - # DOTS FRAME - self.dots_frame = QtWidgets.QFrame() - self.dots_frame.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.dots_frame) - dots_grid = QtWidgets.QGridLayout() - dots_grid.setColumnStretch(0, 0) - dots_grid.setColumnStretch(1, 1) - dots_grid.setContentsMargins(0, 0, 0, 0) - self.dots_frame.setLayout(dots_grid) - self.dots_frame.hide() + grid_lay.addWidget(self.grbobj_label, 6, 0, 1, 2) + grid_lay.addWidget(self.grb_object_combo, 7, 0, 1, 2) - self.dots_label = QtWidgets.QLabel('%s:' % _("Dots Grid Parameters")) - dots_grid.addWidget(self.dots_label, 0, 0, 1, 2) - - # Dot diameter # - self.dotdia_label = QtWidgets.QLabel('%s:' % _("Dia")) - self.dotdia_label.setToolTip( - _("Dot diameter in Dots Grid.") + # ## Insert Copper Fiducial + self.add_cfid_button = QtWidgets.QPushButton(_("Add Fiducial")) + self.add_cfid_button.setToolTip( + _("Will add a polygon on the copper layer to serve as fiducial.") ) - self.dot_dia_entry = FCDoubleSpinner() - self.dot_dia_entry.set_range(0.0, 9999.9999) - self.dot_dia_entry.set_precision(self.decimals) - self.dot_dia_entry.setSingleStep(0.1) + grid_lay.addWidget(self.add_cfid_button, 8, 0, 1, 2) - dots_grid.addWidget(self.dotdia_label, 1, 0) - dots_grid.addWidget(self.dot_dia_entry, 1, 1) + separator_line_1 = QtWidgets.QFrame() + separator_line_1.setFrameShape(QtWidgets.QFrame.HLine) + separator_line_1.setFrameShadow(QtWidgets.QFrame.Sunken) + grid_lay.addWidget(separator_line_1, 9, 0, 1, 2) - # Dot spacing # - self.dotspacing_label = QtWidgets.QLabel('%s:' % _("Spacing")) - self.dotspacing_label.setToolTip( - _("Distance between each two dots in Dots Grid.") + # Soldermask Gerber object # + self.sm_object_label = QtWidgets.QLabel('%s:' % _("Soldermask Gerber")) + self.sm_object_label.setToolTip( + _("The Soldermask Gerber object.") ) - self.dot_spacing_entry = FCDoubleSpinner() - self.dot_spacing_entry.set_range(0.0, 9999.9999) - self.dot_spacing_entry.set_precision(self.decimals) - self.dot_spacing_entry.setSingleStep(0.1) + self.sm_object_combo = QtWidgets.QComboBox() + self.sm_object_combo.setModel(self.app.collection) + self.sm_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.sm_object_combo.setCurrentIndex(1) - dots_grid.addWidget(self.dotspacing_label, 2, 0) - dots_grid.addWidget(self.dot_spacing_entry, 2, 1) + grid_lay.addWidget(self.sm_object_label, 10, 0, 1, 2) + grid_lay.addWidget(self.sm_object_combo, 11, 0, 1, 2) - # SQUARES FRAME - self.squares_frame = QtWidgets.QFrame() - self.squares_frame.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.squares_frame) - squares_grid = QtWidgets.QGridLayout() - squares_grid.setColumnStretch(0, 0) - squares_grid.setColumnStretch(1, 1) - squares_grid.setContentsMargins(0, 0, 0, 0) - self.squares_frame.setLayout(squares_grid) - self.squares_frame.hide() - - self.squares_label = QtWidgets.QLabel('%s:' % _("Squares Grid Parameters")) - squares_grid.addWidget(self.squares_label, 0, 0, 1, 2) - - # Square Size # - self.square_size_label = QtWidgets.QLabel('%s:' % _("Size")) - self.square_size_label.setToolTip( - _("Square side size in Squares Grid.") + # ## Insert Soldermask opening for Fiducial + self.add_sm_opening_button = QtWidgets.QPushButton(_("Add SM Opening")) + self.add_sm_opening_button.setToolTip( + _("Will add a polygon on the soldermask layer\n" + "to serve as fiducial opening.\n" + "The diameter is always double of the diameter\n" + "for the copper fiducial.") ) - self.square_size_entry = FCDoubleSpinner() - self.square_size_entry.set_range(0.0, 9999.9999) - self.square_size_entry.set_precision(self.decimals) - self.square_size_entry.setSingleStep(0.1) - - squares_grid.addWidget(self.square_size_label, 1, 0) - squares_grid.addWidget(self.square_size_entry, 1, 1) - - # Squares spacing # - self.squares_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing")) - self.squares_spacing_label.setToolTip( - _("Distance between each two squares in Squares Grid.") - ) - self.squares_spacing_entry = FCDoubleSpinner() - self.squares_spacing_entry.set_range(0.0, 9999.9999) - self.squares_spacing_entry.set_precision(self.decimals) - self.squares_spacing_entry.setSingleStep(0.1) - - squares_grid.addWidget(self.squares_spacing_label, 2, 0) - squares_grid.addWidget(self.squares_spacing_entry, 2, 1) - - # LINES FRAME - self.lines_frame = QtWidgets.QFrame() - self.lines_frame.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.lines_frame) - lines_grid = QtWidgets.QGridLayout() - lines_grid.setColumnStretch(0, 0) - lines_grid.setColumnStretch(1, 1) - lines_grid.setContentsMargins(0, 0, 0, 0) - self.lines_frame.setLayout(lines_grid) - self.lines_frame.hide() - - self.lines_label = QtWidgets.QLabel('%s:' % _("Lines Grid Parameters")) - lines_grid.addWidget(self.lines_label, 0, 0, 1, 2) - - # Square Size # - self.line_size_label = QtWidgets.QLabel('%s:' % _("Size")) - self.line_size_label.setToolTip( - _("Line thickness size in Lines Grid.") - ) - self.line_size_entry = FCDoubleSpinner() - self.line_size_entry.set_range(0.0, 9999.9999) - self.line_size_entry.set_precision(self.decimals) - self.line_size_entry.setSingleStep(0.1) - - lines_grid.addWidget(self.line_size_label, 1, 0) - lines_grid.addWidget(self.line_size_entry, 1, 1) - - # Lines spacing # - self.lines_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing")) - self.lines_spacing_label.setToolTip( - _("Distance between each two lines in Lines Grid.") - ) - self.lines_spacing_entry = FCDoubleSpinner() - self.lines_spacing_entry.set_range(0.0, 9999.9999) - self.lines_spacing_entry.set_precision(self.decimals) - self.lines_spacing_entry.setSingleStep(0.1) - - lines_grid.addWidget(self.lines_spacing_label, 2, 0) - lines_grid.addWidget(self.lines_spacing_entry, 2, 1) - - # ## Insert Copper Thieving - self.fill_button = QtWidgets.QPushButton(_("Insert Copper thieving")) - self.fill_button.setToolTip( - _("Will add a polygon (may be split in multiple parts)\n" - "that will surround the actual Gerber traces at a certain distance.") - ) - self.layout.addWidget(self.fill_button) + grid_lay.addWidget(self.add_sm_opening_button, 12, 0, 1, 2) self.layout.addStretch() # Objects involved in Copper thieving self.grb_object = None - self.ref_obj = None + self.sm_obj = None self.sel_rect = list() # store the flattened geometry here: @@ -356,10 +209,10 @@ class ToolFiducials(FlatCAMTool): self.geo_steps_per_circle = 128 # SIGNALS - self.fill_button.clicked.connect(self.execute) - self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type) - self.reference_radio.group_toggle_fn = self.on_toggle_reference - self.fill_type_radio.activated_custom.connect(self.on_thieving_type) + # self.fill_button.clicked.connect(self.execute) + # self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type) + # self.reference_radio.group_toggle_fn = self.on_toggle_reference + # self.fill_type_radio.activated_custom.connect(self.on_thieving_type) def run(self, toggle=True): self.app.report_usage("ToolFiducials()") @@ -394,22 +247,9 @@ class ToolFiducials(FlatCAMTool): def set_tool_ui(self): self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value() - self.clearance_entry.set_value(float(self.app.defaults["tools_copper_thieving_clearance"])) - self.margin_entry.set_value(float(self.app.defaults["tools_copper_thieving_margin"])) - self.reference_radio.set_value(self.app.defaults["tools_copper_thieving_reference"]) - self.bbox_type_radio.set_value(self.app.defaults["tools_copper_thieving_box_type"]) - self.fill_type_radio.set_value(self.app.defaults["tools_copper_thieving_fill_type"]) - self.geo_steps_per_circle = int(self.app.defaults["tools_copper_thieving_circle_steps"]) - - self.dot_dia_entry.set_value(self.app.defaults["tools_copper_thieving_dots_dia"]) - self.dot_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_dots_spacing"]) - self.square_size_entry.set_value(self.app.defaults["tools_copper_thieving_squares_size"]) - self.squares_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_squares_spacing"]) - self.line_size_entry.set_value(self.app.defaults["tools_copper_thieving_lines_size"]) - self.lines_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_lines_spacing"]) - - # INIT SECTION - self.area_method = False + # self.clearance_entry.set_value(float(self.app.defaults["tools_copper_thieving_clearance"])) + # self.margin_entry.set_value(float(self.app.defaults["tools_copper_thieving_margin"])) + # self.reference_radio.set_value(self.app.defaults["tools_copper_thieving_reference"]) def on_combo_box_type(self): obj_type = self.box_combo_type.currentIndex() diff --git a/share/fiducials_32.png b/share/fiducials_32.png new file mode 100644 index 0000000000000000000000000000000000000000..4498e913b1c5ff32e4c7f7047a06d4b67e4a00d0 GIT binary patch literal 330 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jPK-BC>eK@{oCO|{#S9F5 zM?jcysy3fAP|!QTC&U#W7tG+k@58N0{9%9ppW|eDo~yP(bva|s zS+|LNr!KYk0~Hl}x;TbZ%y~OGlB>Z$!1?&yUGJ;@PmWsS#3ZQy;CEJh`=p;8Gb2UR zGp9*6uG5&ieAP$U*vq@R`R<+k`tS{#go63K>6~mQ&VDcF=GnmA7$*3saCUsAeTDDS zx;niOeSxdoE?=7h_9}$vb6n*XEvn`Hd9hL0`UCrfBd=uxLH_l0^>bP0l+XkKgVbCy literal 0 HcmV?d00001