diff options
-rw-r--r-- | plugin/main_dialog.fbp | 197 | ||||
-rw-r--r-- | plugin/mesh_dialog.py | 67 | ||||
-rw-r--r-- | plugin/mesh_plugin_dialog.py | 19 |
3 files changed, 278 insertions, 5 deletions
diff --git a/plugin/main_dialog.fbp b/plugin/main_dialog.fbp index 65bd700..2d08714 100644 --- a/plugin/main_dialog.fbp +++ b/plugin/main_dialog.fbp @@ -895,6 +895,203 @@ <property name="gripper">0</property> <property name="hidden">0</property> <property name="id">wxID_ANY</property> + <property name="label">Board edge clearance</property> + <property name="markup">0</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticText261</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass">; ; forward_declare</property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="wxBoxSizer" expanded="1"> + <property name="minimum_size"></property> + <property name="name">bSizer51</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL</property> + <property name="proportion">0</property> + <object class="wxSpinCtrlDouble" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="digits">3</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="inc">0.1</property> + <property name="initial">1.500000</property> + <property name="max">1000</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min">0</property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_edgeClearanceSpin</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style">wxSP_ARROW_KEYS</property> + <property name="subclass">; ; forward_declare</property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="value"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">mm</property> + <property name="markup">0</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_staticText61</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass">; ; forward_declare</property> + <property name="toolbar_pane">0</property> + <property name="tooltip"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + <property name="wrap">-1</property> + </object> + </object> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL</property> + <property name="proportion">0</property> + <object class="wxStaticText" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="default_pane">0</property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> <property name="label">Anchor exit direction</property> <property name="markup">0</property> <property name="max_size"></property> diff --git a/plugin/mesh_dialog.py b/plugin/mesh_dialog.py index b9911b6..fbdbd7a 100644 --- a/plugin/mesh_dialog.py +++ b/plugin/mesh_dialog.py @@ -1,10 +1,12 @@ from collections import defaultdict -from dataclasses import dataclass +import dataclasses from contextlib import contextmanager import textwrap import random import math from itertools import count, islice +import json +from os import path import wx @@ -25,12 +27,14 @@ class GeneratorError(ValueError): class AbortError(SystemError): pass -@dataclass +@dataclasses.dataclass class GeneratorSettings: mesh_angle: float = 0.0 # deg trace_width: float = 0.127 # mm space_width: float = 0.127 # mm + edge_clearance: float = 1.5 # mm anchor_exit: float = 0.0 # deg + anchor: str = None # Footprint designator num_traces: int = 2 offset_x: float = 0.0 # mm offset_y: float = 0.0 # mm @@ -40,6 +44,24 @@ class GeneratorSettings: random_seed: str = None randomness: float = 1.0 + def serialize(self): + d = dataclasses.asdict(self) + d['kimesh_settings_version'] = '1.0.0' + return json.dumps(d).encode() + + @classmethod + def deserialize(cls, data): + d = json.loads(data.decode()) + version = d.pop('kimesh_settings_version') + vtup = tuple(map(int, version.split('.'))) + if vtup > (2, 0, 0): + raise cls.VersionError("Project kimesh settings file is too new for this plugin's version.") + return cls(**d) + + class VersionError(ValueError): + pass + + class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog): def __init__(self, board): mesh_plugin_dialog.MainDialog.__init__(self, None) @@ -67,9 +89,32 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog): self.m_maskLayerChoice.Append(name) if name == 'User.Eco1': self.m_maskLayerChoice.SetSelection(i) - elif name == 'F.Cu': + elif name == 'B.Cu': self.m_layerChoice.SetSelection(i) + if path.isfile(self.settings_fn()): + with open(self.settings_fn(), 'rb') as f: + try: + settings = GeneratorSettings.deserialize(f.read()) + + self.m_angleSpin.Value = settings.mesh_angle + self.m_traceSpin.Value = settings.trace_width + self.m_spaceSpin.Value = settings.space_width + self.m_exitSpin.Value = settings.anchor_exit + self.m_anchorInput.Value = settings.anchor + self.m_traceCountSpin.Value = settings.num_traces + self.m_offsetXSpin.Value = settings.offset_x + self.m_offsetYSpin.Value = settings.offset_y + self.m_chamferSpin.Value = settings.chamfer*100.0 + self.m_layerChoice.SetSelection(settings.target_layer_id) + self.m_maskLayerChoice.SetSelection(settings.mask_layer_id) + self.m_seedInput.Value = settings.random_seed or '' + self.m_randomnessSpin.Value = settings.randomness*100.0 + self.m_edgeClearanceSpin.Value = settings.edge_clearance + + except GeneratorSettings.VersionError as e: + wx.MessageDialog(self, "Cannot load settings: {}.".format(e), "File I/O error").ShowModal() + self.SetMinSize(self.GetSize()) def get_matching_nets(self): @@ -120,13 +165,18 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog): self.board.Remove(track) print(f'Tore up {count} trace segments.') + def settings_fn(self): + return path.join(path.dirname(self.board.GetFileName()), 'last_kimesh_settings.json') + def generate_mesh(self, evt): try: settings = GeneratorSettings( mesh_angle = float(self.m_angleSpin.Value), trace_width = float(self.m_traceSpin.Value), space_width = float(self.m_spaceSpin.Value), + edge_clearance = float(self.m_edgeClearanceSpin.Value), anchor_exit = float(self.m_exitSpin.Value), + anchor = str(self.m_anchorInput.Value), num_traces = int(self.m_traceCountSpin.Value), offset_x = float(self.m_offsetXSpin.Value), offset_y = float(self.m_offsetYSpin.Value), @@ -138,6 +188,13 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog): except ValueError as e: return wx.MessageDialog(self, "Invalid input value: {}.".format(e), "Invalid input").ShowModal() + try: + with open(self.settings_fn(), 'wb') as f: + f.write(settings.serialize()) + print('Saved settings to', f.name) + except: + wx.MessageDialog(self, "Cannot save settings: {}.".format(e), "File I/O error").ShowModal() + mesh_zones = [] for drawing in self.board.GetDrawings(): if drawing.GetLayer() == settings.mask_layer_id: @@ -151,13 +208,14 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog): self.board.GetBoardPolygonOutlines(outlines, "") board_outlines = list(self.poly_set_to_shapely(outlines)) board_mask = shapely.ops.unary_union(board_outlines) + board_mask = board_mask.buffer(-settings.edge_clearance) zone_outlines = [ outline for zone in mesh_zones for outline in self.poly_set_to_shapely(zone.GetPolyShape()) ] zone_mask = shapely.ops.unary_union(zone_outlines) mask = zone_mask.intersection(board_mask) - anchor = [ mod for mod in self.board.GetModules() if mod.GetReference() == self.m_anchorInput.Value ] + anchor = [ mod for mod in self.board.GetModules() if mod.GetReference() == settings.anchor ] if len(anchor) == 0: return wx.MessageDialog(self, f'Error: Could not find anchor footprint "{self.m_anchorInput.Value}".').ShowModal() if len(anchor) > 1: @@ -546,7 +604,6 @@ def virihex(val, max=1.0, alpha=1.0): @contextmanager def DebugOutput(filename): - from os import path filename = path.join('/tmp', filename) with open(filename, 'w') as f: wrapper = DebugOutputWrapper(f) diff --git a/plugin/mesh_plugin_dialog.py b/plugin/mesh_plugin_dialog.py index c1bdaa9..746030a 100644 --- a/plugin/mesh_plugin_dialog.py +++ b/plugin/mesh_plugin_dialog.py @@ -101,6 +101,25 @@ class MainDialog ( wx.Dialog ): fgSizer1.Add( bSizer6, 1, wx.EXPAND, 5 ) + self.m_staticText261 = wx.StaticText( self, wx.ID_ANY, u"Board edge clearance", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText261.Wrap( -1 ) + + fgSizer1.Add( self.m_staticText261, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) + + bSizer51 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_edgeClearanceSpin = wx.SpinCtrlDouble( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.SP_ARROW_KEYS, 0, 1000, 1.500000, 0.1 ) + self.m_edgeClearanceSpin.SetDigits( 3 ) + bSizer51.Add( self.m_edgeClearanceSpin, 0, wx.ALL, 5 ) + + self.m_staticText61 = wx.StaticText( self, wx.ID_ANY, u"mm", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText61.Wrap( -1 ) + + bSizer51.Add( self.m_staticText61, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + + + fgSizer1.Add( bSizer51, 1, wx.EXPAND, 5 ) + self.m_staticText11 = wx.StaticText( self, wx.ID_ANY, u"Anchor exit direction", wx.DefaultPosition, wx.DefaultSize, 0 ) self.m_staticText11.Wrap( -1 ) |