From 81ce2004d58b7812b50191a824974b51f1907bb6 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Sun, 30 Oct 2022 14:35:18 -0300 Subject: [PATCH] [Render_3D] Added option to auto-crop the resulting PNG --- CHANGELOG.md | 1 + README.md | 20 +++++++---- docs/README.in | 3 +- docs/samples/generic_plot.kibot.yaml | 7 ++++ kibot/optionable.py | 32 +++++++++++++++--- kibot/out_render_3d.py | 35 +++++++++++++++++++- src/kibot-check | 9 ++++- tests/yaml_samples/render_3d_list.kibot.yaml | 13 ++++---- 8 files changed, 100 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edd8a8a1..8dea3ffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Option to control the *SVG precision* (units scale) - Render_3D: - Option to render only some components (like in PcbDraw) + - Option to auto-crop the resulting PNG - SVG: - Option to control the *SVG precision* (units scale) diff --git a/README.md b/README.md index 407c09f2..d0206bd8 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,13 @@ Notes: - Find commit hash and/or date for `sch_replace` - Find commit hash and/or date for `set_text_variables` +[**ImageMagick**](https://imagemagick.org/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://imagemagick.org/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/imagemagick) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) +- Optional to: + - Create outputs preview for `navigate_results` + - Create monochrome prints and scaled PNG files for `pcb_print` + - Create JPG and BMP images for `pcbdraw` + - Automatically crop images for `render_3d` + [**RSVG tools**](https://gitlab.gnome.org/GNOME/librsvg) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://gitlab.gnome.org/GNOME/librsvg) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/librsvg2-bin) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) - Optional to: - Create outputs preview for `navigate_results` @@ -178,12 +185,6 @@ Notes: - Create PDF, PNG, PS and EPS formats for `pcb_print` - Create PNG, JPG and BMP images for `pcbdraw` -[**ImageMagick**](https://imagemagick.org/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://imagemagick.org/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/imagemagick) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) -- Optional to: - - Create outputs preview for `navigate_results` - - Create monochrome prints and scaled PNG files for `pcb_print` - - Create JPG and BMP images for `pcbdraw` - [**Ghostscript**](https://www.ghostscript.com/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://www.ghostscript.com/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/ghostscript) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) - Optional to: - Create outputs preview for `navigate_results` @@ -3092,6 +3093,10 @@ Notes: - **`view`**: [string='top'] [top,bottom,front,rear,right,left,z,Z,y,Y,x,X] Point of view. - **`zoom`**: [number=0] Zoom steps. Use positive to enlarge, get closer, and negative to reduce. Same result as using the mouse wheel in the 3D viewer. + - `auto_crop`: [boolean=false] When enabled the image will be post-processed to make the background transparent and then remove the + empty space around the image. In this mode the `background1` and `background2` colors are ignored. + - `auto_crop_color`: [string='#ff70b7'] Color used for the chroma key. Adjust it if some regions of the board becomes transparent. + - `auto_crop_fuzz`: [number=15] [0,100] Chroma key tolerance (percent). Bigger values will remove more pixels. - `background1`: [string='#66667F'] First color for the background gradient. - `background2`: [string='#CCCCE5'] Second color for the background gradient. - `board`: [string='#332B16'] Color for the board without copper or solder mask. @@ -4519,4 +4524,5 @@ relative paths. So you can move the new PCB file to any place, as long as the `3 - **Chip in assembly_simple.svg**: [oNline Web Fonts](https://www.onlinewebfonts.com/) - **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa) - **Most icons for the navigate_results output**: The KiCad project - - **PTV09A 3D Model**: Dmitry Levin (https://grabcad.com/dmitry.levin-6) + - **PTV09A 3D Model**: Dmitry Levin ([GrabCad](https://grabcad.com/dmitry.levin-6)) + - **PcbDraw PCB example**: [Arduino Learning Kit Starter](https://github.com/RoboticsBrno/ArduinoLearningKitStarter) diff --git a/docs/README.in b/docs/README.in index eccd7a82..3a735bb4 100644 --- a/docs/README.in +++ b/docs/README.in @@ -1923,4 +1923,5 @@ relative paths. So you can move the new PCB file to any place, as long as the `3 - **Chip in assembly_simple.svg**: [oNline Web Fonts](https://www.onlinewebfonts.com/) - **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa) - **Most icons for the navigate_results output**: The KiCad project - - **PTV09A 3D Model**: Dmitry Levin (https://grabcad.com/dmitry.levin-6) + - **PTV09A 3D Model**: Dmitry Levin ([GrabCad](https://grabcad.com/dmitry.levin-6)) + - **PcbDraw PCB example**: [Arduino Learning Kit Starter](https://github.com/RoboticsBrno/ArduinoLearningKitStarter) diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index dac1313c..147a9a34 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -1829,6 +1829,13 @@ outputs: type: 'render_3d' dir: 'Example/render_3d_dir' options: + # [boolean=false] When enabled the image will be post-processed to make the background transparent and then remove the + # empty space around the image. In this mode the `background1` and `background2` colors are ignored + auto_crop: false + # [string='#ff70b7'] Color used for the chroma key. Adjust it if some regions of the board becomes transparent + auto_crop_color: '#ff70b7' + # [number=15] [0,100] Chroma key tolerance (percent). Bigger values will remove more pixels + auto_crop_fuzz: 15 # [string='#66667F'] First color for the background gradient background1: '#66667F' # [string='#CCCCE5'] Second color for the background gradient diff --git a/kibot/optionable.py b/kibot/optionable.py index df8030d2..974f4f07 100644 --- a/kibot/optionable.py +++ b/kibot/optionable.py @@ -14,6 +14,7 @@ from .misc import W_UNKOPS from . import log logger = log.get_logger() +HEX_DIGIT = '[A-Fa-f0-9]{2}' def do_filter(v): @@ -31,8 +32,10 @@ class Optionable(object): _str_values_re = compile(r"string=.*\] \[([^\]]+)\]") _num_range_re = compile(r"number=.*\] \[(-?\d+),(-?\d+)\]") _default = None - _color_re = re.compile(r"#[A-Fa-f0-9]{6}$") - _color_re_a = re.compile(r"#[A-Fa-f0-9]{8}$") + + _color_re = re.compile(r"#("+HEX_DIGIT+"){3}$") + _color_re_a = re.compile(r"#("+HEX_DIGIT+"){4}$") + _color_re_component = re.compile(HEX_DIGIT) def __init__(self): self._unkown_is_error = False @@ -372,15 +375,36 @@ class Optionable(object): def get_default(cls): return cls._default + def validate_color_str(self, color): + return self._color_re.match(color) or self._color_re_a.match(color) + def validate_color(self, name): - color = getattr(self, name) - if not self._color_re.match(color) and not self._color_re_a.match(color): + if not self.validate_color_str(getattr(self, name)): raise KiPlotConfigurationError('Invalid color for `{}` use `#rrggbb` or `#rrggbbaa` with hex digits'.format(name)) def validate_colors(self, names): for color in names: self.validate_color(color) + def parse_one_color(self, color): + res = self._color_re_component.findall(color) + alpha = 1.0 + if len(res) > 3: + alpha = int(res[3], 16)/255.0 + return (int(res[0], 16)/255.0, int(res[1], 16)/255.0, int(res[2], 16)/255.0, alpha) + + def color_to_rgb(self, color): + index = 4 if len(color) > 4 else 0 + alpha = color[index+3] + if alpha == 1.0: + return "rgb({}, {}, {})".format(round(color[index]*255.0), round(color[index+1]*255.0), + round(color[index+2]*255.0)) + return "rgba({}, {}, {}, {})".format(round(color[index]*255.0), round(color[index+1]*255.0), + round(color[index+2]*255.0), alpha) + + def color_str_to_rgb(self, color): + return self.color_to_rgb(self.parse_one_color(color)) + class BaseOptions(Optionable): """ A class to validate and hold output options. diff --git a/kibot/out_render_3d.py b/kibot/out_render_3d.py index e5d1e898..e7234791 100644 --- a/kibot/out_render_3d.py +++ b/kibot/out_render_3d.py @@ -9,9 +9,13 @@ Dependencies: - from: KiAuto role: mandatory version: 2.0.4 + - from: ImageMagick + role: Automatically crop images """ import os +import shlex from shutil import rmtree +import subprocess from .misc import (RENDER_3D_ERR, PCB_MAT_COLORS, PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS, KICAD_VERSION_6_0_2, MISSING_TOOL) from .gs import GS @@ -24,6 +28,19 @@ from . import log logger = log.get_logger() +def _run_command(cmd): + logger.debug('- Executing: '+shlex.join(cmd)) + try: + cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + logger.error('Failed to run %s, error %d', cmd[0], e.returncode) + if e.output: + logger.debug('Output from command: '+e.output.decode()) + exit(RENDER_3D_ERR) + if cmd_output.strip(): + logger.debug('- Output from command:\n'+cmd_output.decode()) + + class Render3DOptions(Base3DOptions): _colors = {'background1': 'bg_color_1', 'background2': 'bg_color_2', @@ -107,6 +124,13 @@ class Render3DOptions(Base3DOptions): self.show_components = Optionable """ *[list(string)|string=all] [none,all] List of components to draw, can be also a string for `none` or `all`. Unlike the `pcbdraw` output, the default is `all` """ + self.auto_crop = False + """ When enabled the image will be post-processed to make the background transparent and then remove the + empty space around the image. In this mode the `background1` and `background2` colors are ignored """ + self.auto_crop_color = "#ff70b7" + """ Color used for the chroma key. Adjust it if some regions of the board becomes transparent """ + self.auto_crop_fuzz = 15 + """ [0,100] Chroma key tolerance (percent). Bigger values will remove more pixels """ super().__init__() self._expand_ext = 'png' @@ -147,7 +171,7 @@ class Render3DOptions(Base3DOptions): self.copper = "#"+color break super().config(parent) - self.validate_colors(self._colors.keys()) + self.validate_colors(list(self._colors.keys())+['auto_crop_color']) view = self._views.get(self.view, None) if view is not None: self.view = view @@ -232,6 +256,9 @@ class Render3DOptions(Base3DOptions): "Please upgrade KiCad to 6.0.2 or newer") exit(MISSING_TOOL) command = self.ensure_tool('KiAuto') + if self.auto_crop: + self.background1 = self.background2 = self.auto_crop_color + convert_command = self.ensure_tool('ImageMagick') # Base command with overwrite cmd = [command, '--rec_w', str(self.width+2), '--rec_h', str(self.height+85), '3d_view', '--output_name', output] @@ -255,6 +282,12 @@ class Render3DOptions(Base3DOptions): video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_3d_view_screencast.ogv') if os.path.isfile(video_name): os.remove(video_name) + if self.auto_crop: + _run_command([convert_command, output, '-fuzz', str(self.auto_crop_fuzz)+'%', '-transparent', + self.color_str_to_rgb(self.auto_crop_color), output]) + # Don't ask me why I need 2 passes + _run_command([convert_command, output, '-trim', '+repage', output]) + _run_command([convert_command, output, '-trim', '+repage', output]) @output_class diff --git a/src/kibot-check b/src/kibot-check index a16d2964..ba18aee5 100755 --- a/src/kibot-check +++ b/src/kibot-check @@ -159,7 +159,7 @@ deps = '{\ ],\ "extra_deb": null,\ "help_option": "--version",\ - "importance": 3,\ + "importance": 4,\ "in_debian": true,\ "is_kicad_plugin": false,\ "is_python": false,\ @@ -190,6 +190,13 @@ deps = '{\ "max_version": null,\ "output": "pcbdraw",\ "version": null\ + },\ + {\ + "desc": "Automatically crop images",\ + "mandatory": false,\ + "max_version": null,\ + "output": "render_3d",\ + "version": null\ }\ ],\ "tests": [],\ diff --git a/tests/yaml_samples/render_3d_list.kibot.yaml b/tests/yaml_samples/render_3d_list.kibot.yaml index bfbaf8ae..f92815e2 100644 --- a/tests/yaml_samples/render_3d_list.kibot.yaml +++ b/tests/yaml_samples/render_3d_list.kibot.yaml @@ -2,18 +2,19 @@ kibot: version: 1 +global: + solder_mask_color: blue + pcb_finish: ENIG + outputs: - name: render_list comment: "Render with only some components" type: render_3d options: - width: 800 - height: 600 + width: 1280 + height: 960 orthographic: true zoom: 4 show_components: ["RV1", "RV2", "U1", "U2", "U3"] ray_tracing: true - background1: "#ff70b7" - background2: "#ff70b7" - # background1: "#ffffff" - # background2: "#ffffff" + auto_crop: true