From d935ce17b71289613b31df4bb15182f9caf167d6 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Fri, 2 Dec 2022 08:41:12 -0300 Subject: [PATCH] [Stencil*] Added PNG preview for the 3D model --- README.md | 6 ++++-- docs/samples/generic_plot.kibot.yaml | 8 ++++++-- kibot/optionable.py | 4 ++++ kibot/out_any_stencil.py | 12 +++++++++++- kibot/out_stencil_3d.py | 8 +++++++- kibot/out_stencil_for_jig.py | 6 +++++- 6 files changed, 37 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 89d8a8dc..2b292b03 100644 --- a/README.md +++ b/README.md @@ -3577,8 +3577,9 @@ Notes: - **`options`**: [dict] Options for the `stencil_3d` output. * Valid keys: - **`output`**: [string='%f-%i%I%v.%x'] Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge', - %x='stl'|'scad'|'dxf'). Affected by global options. + %x='stl'|'scad'|'dxf'|'png'). Affected by global options. - **`thickness`**: [number=0.15] Stencil thickness [mm]. Defines amount of paste dispensed. + - `create_preview`: [boolean=true] Creates a PNG showing the generated 3D model. - `cutout`: [string|list(string)] List of components to add a cutout based on the component courtyard. This is useful when you have already pre-populated board and you want to populate more components. @@ -3631,7 +3632,8 @@ Notes: - **`jigthickness`**: [number=3] Jig thickness [mm]. - **`jigwidth`**: [number=100] Jig frame width [mm]. - **`output`**: [string='%f-%i%I%v.%x'] Filename for the output (%i='stencil_for_jig_top'|'stencil_for_jig_bottom', - %x='stl'|'scad'|'gbp'|'gtp'|'gbrjob'). Affected by global options. + %x='stl'|'scad'|'gbp'|'gtp'|'gbrjob'|'png'). Affected by global options. + - `create_preview`: [boolean=true] Creates a PNG showing the generated 3D model. - `cutout`: [string|list(string)] List of components to add a cutout based on the component courtyard. This is useful when you have already pre-populated board and you want to populate more components. diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index 8c0c7f85..fea0ba82 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -2472,6 +2472,8 @@ outputs: type: 'stencil_3d' dir: 'Example/stencil_3d_dir' options: + # [boolean=true] Creates a PNG showing the generated 3D model + create_preview: true # [string|list(string)] List of components to add a cutout based on the component courtyard. # This is useful when you have already pre-populated board and you want to populate more # components @@ -2492,7 +2494,7 @@ outputs: # Note that this also includes the DXF files include_scad: true # [string='%f-%i%I%v.%x'] Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge', - # %x='stl'|'scad'|'dxf'). Affected by global options + # %x='stl'|'scad'|'dxf'|'png'). Affected by global options output: '%f-%i%I%v.%x' # `pcb_thickness` is an alias for `pcbthickness` # [number=0] PCB thickness [mm]. If 0 we will ask KiCad @@ -2517,6 +2519,8 @@ outputs: type: 'stencil_for_jig' dir: 'Example/stencil_for_jig_dir' options: + # [boolean=true] Creates a PNG showing the generated 3D model + create_preview: true # [string|list(string)] List of components to add a cutout based on the component courtyard. # This is useful when you have already pre-populated board and you want to populate more # components @@ -2536,7 +2540,7 @@ outputs: # [number=100] Jig frame width [mm] jigwidth: 100 # [string='%f-%i%I%v.%x'] Filename for the output (%i='stencil_for_jig_top'|'stencil_for_jig_bottom', - # %x='stl'|'scad'|'gbp'|'gtp'|'gbrjob'). Affected by global options + # %x='stl'|'scad'|'gbp'|'gtp'|'gbrjob'|'png'). Affected by global options output: '%f-%i%I%v.%x' # `pcb_thickness` is an alias for `pcbthickness` # [number=0] PCB thickness [mm]. If 0 we will ask KiCad diff --git a/kibot/optionable.py b/kibot/optionable.py index 59136e69..40870c98 100644 --- a/kibot/optionable.py +++ b/kibot/optionable.py @@ -46,6 +46,8 @@ class Optionable(object): # File/directory pattern expansion self._expand_id = '' self._expand_ext = '' + # Used to indicate we have an output pattern and it must be suitable to generate multiple files + self._output_multiple_files = False super().__init__() for var in ['output', 'variant', 'units', 'hide_excluded']: glb = getattr(GS, 'global_'+var) @@ -217,6 +219,8 @@ class Optionable(object): if self._tree and not self._configured: self._perform_config_mapping() self._configured = True + if self._output_multiple_files and ('%i' not in self.output or '%x' not in self.output): + raise KiPlotConfigurationError('The output pattern must contain %i and %x, otherwise file names will collide') def get_attrs_for(self): """ Returns all attributes """ diff --git a/kibot/out_any_stencil.py b/kibot/out_any_stencil.py index 8951de89..7ca4cd96 100644 --- a/kibot/out_any_stencil.py +++ b/kibot/out_any_stencil.py @@ -33,7 +33,10 @@ class Stencil_Options(VariantOptions): """ PCB thickness [mm]. If 0 we will ask KiCad """ self.pcb_thickness = None """ {pcbthickness} """ + self.create_preview = True + """ Creates a PNG showing the generated 3D model """ super().__init__() + self._output_multiple_files = True def config(self, parent): super().config(parent) @@ -44,6 +47,13 @@ class Stencil_Options(VariantOptions): self._expand_ext = ext return self._parent.expand_filename(out_dir, self.output) + def create_preview_png(self, src_dir, src_file, id): + dst_name = self.expand_name(id, 'png', self._parent.output_dir) + src_name = os.path.join(src_dir, src_file) + if not os.path.isfile(src_name): + raise PlotError('Missing output file {}'.format(src_name)) + run_command([self.cmd_openscad, '-o', dst_name, '--imgsize=1280,720', src_name]) + def move_output(self, src_dir, src_file, id, ext, replacement=None, patch=False, relative=False): dst_name = self.expand_name(id, ext, self._parent.output_dir) src_name = os.path.join(src_dir, src_file) @@ -89,7 +99,7 @@ class Stencil_Options(VariantOptions): def run(self, output): cmd_kikit = self.ensure_tool('KiKit') - self.ensure_tool('OpenSCAD') + self.cmd_openscad = self.ensure_tool('OpenSCAD') super().run(output) # Apply variants and filters filtered = self.filter_pcb_components(GS.board) diff --git a/kibot/out_stencil_3d.py b/kibot/out_stencil_3d.py index c43dc4f4..9d26ade9 100644 --- a/kibot/out_stencil_3d.py +++ b/kibot/out_stencil_3d.py @@ -28,7 +28,7 @@ class Stencil_3D_Options(Stencil_Options): with document: self.output = GS.def_global_output """ *Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge', - %x='stl'|'scad'|'dxf') """ + %x='stl'|'scad'|'dxf'|'png') """ self.thickness = 0.15 """ *Stencil thickness [mm]. Defines amount of paste dispensed """ self.framewidth = 1 @@ -81,6 +81,12 @@ class Stencil_3D_Options(Stencil_Options): def move_outputs(self, tmp, prj_name, do_top, do_bottom): replacements = {} + # Create the preview before we touch anything + if self.create_preview: + if do_top: + self.create_preview_png(tmp, 'topStencil.scad', 'stencil_3d_top') + if do_bottom: + self.create_preview_png(tmp, 'bottomStencil.scad', 'stencil_3d_bottom') # The edge is needed by any of the OpenSCAD files if (do_top or do_bottom) and self.include_scad: self.move_output(tmp, prj_name+'-EdgeCuts.dxf', 'stencil_3d_edge', 'dxf', replacements) diff --git a/kibot/out_stencil_for_jig.py b/kibot/out_stencil_for_jig.py index 8be4ced0..f27e1414 100644 --- a/kibot/out_stencil_for_jig.py +++ b/kibot/out_stencil_for_jig.py @@ -28,7 +28,7 @@ class Stencil_For_Jig_Options(Stencil_Options): with document: self.output = GS.def_global_output """ *Filename for the output (%i='stencil_for_jig_top'|'stencil_for_jig_bottom', - %x='stl'|'scad'|'gbp'|'gtp'|'gbrjob') """ + %x='stl'|'scad'|'gbp'|'gtp'|'gbrjob'|'png') """ self.jigthickness = 3 """ *Jig thickness [mm] """ self.jig_thickness = None @@ -89,6 +89,8 @@ class Stencil_For_Jig_Options(Stencil_Options): if do_top: self.move_output(tmp, 'gerber/stencil-PasteTop.gtp', 'stencil_for_jig_top', 'gtp', replacements, relative=True) self.move_output(tmp, 'topRegister.stl', 'stencil_for_jig_top', 'stl') + if self.create_preview: + self.create_preview_png(tmp, 'topRegister.scad', 'stencil_for_jig_top') if self.include_scad: self.move_output(tmp, 'topRegister.scad', 'stencil_for_jig_top', 'scad') # Bottom side @@ -96,6 +98,8 @@ class Stencil_For_Jig_Options(Stencil_Options): self.move_output(tmp, 'gerber/stencil-PasteBottom.gbp', 'stencil_for_jig_bottom', 'gbp', replacements, relative=True) self.move_output(tmp, 'bottomRegister.stl', 'stencil_for_jig_bottom', 'stl') + if self.create_preview: + self.create_preview_png(tmp, 'bottomRegister.scad', 'stencil_for_jig_top') if self.include_scad: self.move_output(tmp, 'bottomRegister.scad', 'stencil_for_jig_bottom', 'scad') if do_top and do_bottom: