Improve error handling, add runtime options

This commit is contained in:
jaseg 2023-10-23 14:51:15 +02:00
parent cac262ce37
commit c4f176d3f4
3 changed files with 556 additions and 41 deletions

View file

@ -48,7 +48,7 @@
<property name="minimum_size"></property>
<property name="name">MainDialog</property>
<property name="pos"></property>
<property name="size">632,458</property>
<property name="size">632,580</property>
<property name="style">wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxMINIMIZE_BOX|wxRESIZE_BORDER|wxSTAY_ON_TOP</property>
<property name="subclass">; ; forward_declare</property>
<property name="title">Security Mesh Generator Plugin</property>
@ -62,6 +62,68 @@
<property name="name">bSizer1</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<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="drag_accept_files">0</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">&lt;font color=&quot;red&quot;&gt;&lt;b&gt;Warning: Board outline not found&lt;/b&gt;&lt;/font&gt;</property>
<property name="markup">1</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_warningLabel</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">wxALIGN_CENTER_HORIZONTAL</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="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
@ -1333,6 +1395,358 @@
</object>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<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="checked">1</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="drag_accept_files">0</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">Respect zone keepouts</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_useKeepoutCheckbox</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="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<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="checked">1</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="drag_accept_files">0</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">Respect board outline</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_useOutlineCheckbox</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="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<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="checked">1</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="drag_accept_files">0</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">Save layout visualizations</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_vizCheckbox</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="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<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="drag_accept_files">0</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">Visualization output directory</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_staticText14</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="true">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxTextCtrl" expanded="true">
<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="drag_accept_files">0</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="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="maxlength"></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_vizTextfield</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="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="true">

View file

@ -8,6 +8,7 @@ from itertools import count, islice
import json
import re
from os import path
import os
import wx
@ -30,16 +31,20 @@ class AbortError(SystemError):
@dataclasses.dataclass
class GeneratorSettings:
edge_clearance: float = 1.5 # mm
anchor: str = None # Footprint designator
chamfer: float = 0.0 # unit fraction
mask_layer_id: int = 0 # kicad layer id, populated later
random_seed: str = None
randomness: float = 1.0
edge_clearance: float = 1.5 # mm
anchor: str = None # Footprint designator
chamfer: float = 0.0 # unit fraction
mask_layer_id: int = 0 # kicad layer id, populated later
random_seed: str = None
randomness: float = 1.0
use_keepouts: bool = True
use_outline: bool = True
save_visualization: bool = True
visualization_path: str = 'mesh_visualizations'
def serialize(self):
d = dataclasses.asdict(self)
d['kimesh_settings_version'] = '2.0.0'
d['kimesh_settings_version'] = '2.1.0'
return json.dumps(d).encode()
@classmethod
@ -47,7 +52,7 @@ class GeneratorSettings:
d = json.loads(data.decode())
version = d.pop('kimesh_settings_version')
vtup = tuple(map(int, version.split('.')))
if vtup > (2, 0, 0):
if vtup > (2, 1, 0):
raise cls.VersionError("Project kimesh settings file is too new for this plugin's version.")
return cls(**d)
@ -69,6 +74,7 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog):
self.nets = { str(wxs) for wxs, netinfo in board.GetNetsByName().items() }
self.update_net_label(None)
self.update_outline_warning()
self.Fit()
@ -104,9 +110,64 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog):
self.m_seedInput.Value = settings.random_seed or ''
self.m_randomnessSpin.Value = settings.randomness*100.0
self.m_edgeClearanceSpin.Value = settings.edge_clearance
self.m_useOutlineCheckbox.Value = settings.use_outline
self.m_useKeepoutCheckbox.Value = settings.use_keepouts
self.m_vizTextfield.Value = settings.visualization_path
self.m_vizCheckbox.Value = settings.save_visualization
self.SetMinSize(self.GetSize())
@contextmanager
def viz(self, filename):
if self.m_vizCheckbox.Value:
val = self.m_vizTextfield.Value
project_dir = path.dirname(self.board.GetFileName())
if val:
val = path.join(project_dir, val)
if not os.path.isdir(val):
os.mkdir(val)
filename = path.join(val, filename)
filename = path.join(project_dir, filename)
with open(filename, 'w') as f:
wrapper = DebugOutputWrapper(f)
yield wrapper
wrapper.save()
else:
wrapper = DebugOutputWrapper(None)
yield wrapper
def board_has_outline(self):
# KiCad's API is absolutely insane. As long as the board has an outline, the board outline function works
# alright. Now imagine the Edge.Cuts layer is empty. What would be a sane thing to do? I guess raising an error
# would be the best, with the second best being to return something like the hull of all objects on the other
# layers. Alas, KiCad doesn't do either. Instead, KiCad returns the union of the shapes of all objects on the
# **VISIBLE** layers, so the result of that outline function changes with which layers the user has set to
# visible. Whyyyyy :(
#
# We have to work around this to avoid presenting the user with a foot-gun in case they hide their mesh
# definition layer.
#
edge_cuts = self.board.GetLayerID('Edge.Cuts')
outline_objs = []
for drawing in self.board.GetDrawings():
if drawing.GetLayer() == edge_cuts:
return True
else:
return False
def update_outline_warning(self):
outlines = pcbnew.SHAPE_POLY_SET()
self.board.GetBoardPolygonOutlines(outlines)
board_outlines = list(self.poly_set_to_shapely(outlines))
board_mask = shapely.ops.unary_union(board_outlines)
if not self.board_has_outline() or board_mask.is_empty:
self.m_warningLabel.SetLabelMarkup('<b>Warning: Board outline not found</b>')
else:
self.m_warningLabel.SetLabelMarkup('')
def get_matching_nets(self):
prefix = self.m_net_prefix.Value
return { net for net in self.nets if net.startswith(prefix) }
@ -170,7 +231,11 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog):
chamfer = float(self.m_chamferSpin.Value)/100.0,
mask_layer_id = self.m_maskLayerChoice.GetSelection(),
random_seed = str(self.m_seedInput.Value) or None,
randomness = float(self.m_randomnessSpin.Value)/100.0)
randomness = float(self.m_randomnessSpin.Value)/100.0,
use_outline = self.m_useOutlineCheckbox.Value,
use_keepouts = self.m_useKeepoutCheckbox.Value,
visualization_path = self.m_vizTextfield.Value,
save_visualization = self.m_vizCheckbox.Value)
except ValueError as e:
return wx.MessageDialog(self, "Invalid input value: {}.".format(e), "Invalid input").ShowModal()
@ -198,29 +263,37 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog):
keepouts.append(zone.Outline())
print(f'Found {len(keepouts)} keepout areas.')
outlines = pcbnew.SHAPE_POLY_SET()
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)
print('board outline bounds:', board_mask.bounds)
if board_mask.is_empty:
return wx.MessageDialog(self, "Error: Could not find the board outline, or board edge clearance is set too high.").ShowModal()
if self.board_has_outline() and self.m_useOutlineCheckbox.Value: # Avoid foot-gun due to insane API. See note in the function.
outlines = pcbnew.SHAPE_POLY_SET()
self.board.GetBoardPolygonOutlines(outlines)
board_outlines = list(self.poly_set_to_shapely(outlines))
board_mask = shapely.ops.unary_union(board_outlines)
mask = board_mask.buffer(-settings.edge_clearance)
print('board outline bounds:', mask.bounds)
if mask.is_empty:
return wx.MessageDialog(self, "Error: Board edge clearance is set too high. There is nothing left for the mesh after applying clearance.").ShowModal()
else:
mask = None
zone_outlines = [ outline for zone in mesh_zones for outline in self.poly_set_to_shapely(zone) ]
zone_mask = shapely.ops.unary_union(zone_outlines)
if zone_mask.is_empty:
mask = board_mask
return wx.MessageDialog(self, "Error: Empty mesh outline on mesh outline layer. Make sure the mesh outline is defined with polygon objects only. Other shapes are not supported yet.").ShowModal()
elif mask is None:
mask = zone_mask
else:
mask = zone_mask.intersection(board_mask)
mask = zone_mask.intersection(mask)
print('Mesh mask bounds:', zone_mask.bounds)
keepout_outlines = [ outline for zone in keepouts for outline in self.poly_set_to_shapely(zone) ]
keepout_mask = shapely.ops.unary_union(keepout_outlines)
if not keepout_mask.is_empty:
mask = shapely.difference(mask, keepout_mask)
print('keepout mask bounds:', keepout_mask.bounds)
print('resulting mask bounds:', mask.bounds)
if self.m_useKeepoutCheckbox.Value:
keepout_outlines = [ outline for zone in keepouts for outline in self.poly_set_to_shapely(zone) ]
keepout_mask = shapely.ops.unary_union(keepout_outlines)
if not keepout_mask.is_empty:
mask = shapely.difference(mask, keepout_mask)
print('keepout mask bounds:', keepout_mask.bounds)
print('resulting mask bounds:', mask.bounds)
if mask.is_empty:
return wx.MessageDialog(self, "Error: After applying all keepouts, and intersecting with the board's outline, the mesh outline is empty.")
try:
def warn(msg):
@ -307,7 +380,7 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog):
grid.append(row)
num_valid = 0
with DebugOutput('dbg_grid.svg') as dbg:
with self.viz('mesh_grid.svg') as dbg:
dbg.add(mask, color='#00000020')
for y, row in enumerate(grid, start=grid_y0):
@ -389,10 +462,10 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog):
not_visited = { (x, y) for x in range(grid_x0, grid_x0+grid_cols) for y in range(grid_y0, grid_y0+grid_rows) if is_valid(grid[y-grid_y0][x-grid_x0]) }
num_to_visit = len(not_visited)
track_count = 0
with DebugOutput('dbg_cells.svg') as dbg_cells,\
DebugOutput('dbg_composite.svg') as dbg_composite,\
DebugOutput('dbg_tiles.svg') as dbg_tiles,\
DebugOutput('dbg_traces.svg') as dbg_traces:
with self.viz('mesh_cells.svg') as dbg_cells,\
self.viz('mesh_composite.svg') as dbg_composite,\
self.viz('mesh_tiles.svg') as dbg_tiles,\
self.viz('mesh_traces.svg') as dbg_traces:
dbg_cells.add(mask, color='#00000020')
dbg_composite.add(mask, color='#00000020')
dbg_traces.add(mask, color='#00000020')
@ -425,7 +498,7 @@ class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog):
i = 0
past_tiles = {}
def dump_output(i):
with DebugOutput(f'per-tile/step{i}.svg') as dbg_per_tile:
with self.viz(f'per-tile/step{i}.svg') as dbg_per_tile:
dbg_per_tile.add(mask, color='#00000020')
for foo in anchor_outlines:
dbg_per_tile.add(foo, color='#00000080', stroke_width=0.05, stroke_color='#00000000')
@ -607,13 +680,6 @@ def virihex(val, max=1.0, alpha=1.0):
r, g, b, a = [ int(round(0xff*c)) for c in [r, g, b, alpha] ]
return f'#{r:02x}{g:02x}{b:02x}{a:02x}'
@contextmanager
def DebugOutput(filename):
with open(filename, 'w') as f:
wrapper = DebugOutputWrapper(f)
yield wrapper
wrapper.save()
class DebugOutputWrapper:
def __init__(self, f):
self.f = f

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
###########################################################################
## Python code generated with wxFormBuilder (version 3.10.1-367-gf0e67a69)
## Python code generated with wxFormBuilder (version 3.10.1-380-gf48f2659)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
@ -17,12 +17,18 @@ import wx.xrc
class MainDialog ( wx.Dialog ):
def __init__( self, parent ):
wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Security Mesh Generator Plugin", pos = wx.DefaultPosition, size = wx.Size( 632,458 ), style = wx.CLOSE_BOX|wx.DEFAULT_DIALOG_STYLE|wx.MINIMIZE_BOX|wx.RESIZE_BORDER|wx.STAY_ON_TOP )
wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Security Mesh Generator Plugin", pos = wx.DefaultPosition, size = wx.Size( 632,580 ), style = wx.CLOSE_BOX|wx.DEFAULT_DIALOG_STYLE|wx.MINIMIZE_BOX|wx.RESIZE_BORDER|wx.STAY_ON_TOP )
self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )
bSizer1 = wx.BoxSizer( wx.VERTICAL )
self.m_warningLabel = wx.StaticText( self, wx.ID_ANY, u"<font color=\"red\"><b>Warning: Board outline not found</b></font>", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTER_HORIZONTAL )
self.m_warningLabel.SetLabelMarkup( u"<font color=\"red\"><b>Warning: Board outline not found</b></font>" )
self.m_warningLabel.Wrap( -1 )
bSizer1.Add( self.m_warningLabel, 0, wx.ALL|wx.EXPAND, 5 )
fgSizer1 = wx.FlexGridSizer( 0, 2, 0, 0 )
fgSizer1.AddGrowableCol( 1 )
fgSizer1.SetFlexibleDirection( wx.BOTH )
@ -138,6 +144,35 @@ class MainDialog ( wx.Dialog ):
fgSizer1.Add( bSizer12, 1, wx.EXPAND, 5 )
fgSizer1.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_useKeepoutCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Respect zone keepouts", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_useKeepoutCheckbox.SetValue(True)
fgSizer1.Add( self.m_useKeepoutCheckbox, 0, wx.ALL, 5 )
fgSizer1.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_useOutlineCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Respect board outline", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_useOutlineCheckbox.SetValue(True)
fgSizer1.Add( self.m_useOutlineCheckbox, 0, wx.ALL, 5 )
fgSizer1.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_vizCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Save layout visualizations", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_vizCheckbox.SetValue(True)
fgSizer1.Add( self.m_vizCheckbox, 0, wx.ALL, 5 )
self.m_staticText14 = wx.StaticText( self, wx.ID_ANY, u"Visualization output directory", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText14.Wrap( -1 )
fgSizer1.Add( self.m_staticText14, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 )
self.m_vizTextfield = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
fgSizer1.Add( self.m_vizTextfield, 0, wx.ALL|wx.EXPAND, 5 )
bSizer1.Add( fgSizer1, 1, wx.EXPAND, 5 )
bSizer99 = wx.BoxSizer( wx.HORIZONTAL )