[Render_3D] Added option to auto-crop the resulting PNG
This commit is contained in:
parent
5b19227625
commit
81ce2004d5
|
|
@ -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)
|
- Option to control the *SVG precision* (units scale)
|
||||||
- Render_3D:
|
- Render_3D:
|
||||||
- Option to render only some components (like in PcbDraw)
|
- Option to render only some components (like in PcbDraw)
|
||||||
|
- Option to auto-crop the resulting PNG
|
||||||
- SVG:
|
- SVG:
|
||||||
- Option to control the *SVG precision* (units scale)
|
- Option to control the *SVG precision* (units scale)
|
||||||
|
|
||||||
|
|
|
||||||
20
README.md
20
README.md
|
|
@ -171,6 +171,13 @@ Notes:
|
||||||
- Find commit hash and/or date for `sch_replace`
|
- Find commit hash and/or date for `sch_replace`
|
||||||
- Find commit hash and/or date for `set_text_variables`
|
- Find commit hash and/or date for `set_text_variables`
|
||||||
|
|
||||||
|
[**ImageMagick**](https://imagemagick.org/) [](https://imagemagick.org/) [](https://packages.debian.org/bullseye/imagemagick) 
|
||||||
|
- 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) [](https://gitlab.gnome.org/GNOME/librsvg) [](https://packages.debian.org/bullseye/librsvg2-bin) 
|
[**RSVG tools**](https://gitlab.gnome.org/GNOME/librsvg) [](https://gitlab.gnome.org/GNOME/librsvg) [](https://packages.debian.org/bullseye/librsvg2-bin) 
|
||||||
- Optional to:
|
- Optional to:
|
||||||
- Create outputs preview for `navigate_results`
|
- Create outputs preview for `navigate_results`
|
||||||
|
|
@ -178,12 +185,6 @@ Notes:
|
||||||
- Create PDF, PNG, PS and EPS formats for `pcb_print`
|
- Create PDF, PNG, PS and EPS formats for `pcb_print`
|
||||||
- Create PNG, JPG and BMP images for `pcbdraw`
|
- Create PNG, JPG and BMP images for `pcbdraw`
|
||||||
|
|
||||||
[**ImageMagick**](https://imagemagick.org/) [](https://imagemagick.org/) [](https://packages.debian.org/bullseye/imagemagick) 
|
|
||||||
- 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/) [](https://www.ghostscript.com/) [](https://packages.debian.org/bullseye/ghostscript) 
|
[**Ghostscript**](https://www.ghostscript.com/) [](https://www.ghostscript.com/) [](https://packages.debian.org/bullseye/ghostscript) 
|
||||||
- Optional to:
|
- Optional to:
|
||||||
- Create outputs preview for `navigate_results`
|
- 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.
|
- **`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.
|
- **`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.
|
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.
|
- `background1`: [string='#66667F'] First color for the background gradient.
|
||||||
- `background2`: [string='#CCCCE5'] Second 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.
|
- `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/)
|
- **Chip in assembly_simple.svg**: [oNline Web Fonts](https://www.onlinewebfonts.com/)
|
||||||
- **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa)
|
- **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa)
|
||||||
- **Most icons for the navigate_results output**: The KiCad project
|
- **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)
|
||||||
|
|
|
||||||
|
|
@ -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/)
|
- **Chip in assembly_simple.svg**: [oNline Web Fonts](https://www.onlinewebfonts.com/)
|
||||||
- **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa)
|
- **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa)
|
||||||
- **Most icons for the navigate_results output**: The KiCad project
|
- **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)
|
||||||
|
|
|
||||||
|
|
@ -1829,6 +1829,13 @@ outputs:
|
||||||
type: 'render_3d'
|
type: 'render_3d'
|
||||||
dir: 'Example/render_3d_dir'
|
dir: 'Example/render_3d_dir'
|
||||||
options:
|
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
|
# [string='#66667F'] First color for the background gradient
|
||||||
background1: '#66667F'
|
background1: '#66667F'
|
||||||
# [string='#CCCCE5'] Second color for the background gradient
|
# [string='#CCCCE5'] Second color for the background gradient
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ from .misc import W_UNKOPS
|
||||||
from . import log
|
from . import log
|
||||||
|
|
||||||
logger = log.get_logger()
|
logger = log.get_logger()
|
||||||
|
HEX_DIGIT = '[A-Fa-f0-9]{2}'
|
||||||
|
|
||||||
|
|
||||||
def do_filter(v):
|
def do_filter(v):
|
||||||
|
|
@ -31,8 +32,10 @@ class Optionable(object):
|
||||||
_str_values_re = compile(r"string=.*\] \[([^\]]+)\]")
|
_str_values_re = compile(r"string=.*\] \[([^\]]+)\]")
|
||||||
_num_range_re = compile(r"number=.*\] \[(-?\d+),(-?\d+)\]")
|
_num_range_re = compile(r"number=.*\] \[(-?\d+),(-?\d+)\]")
|
||||||
_default = None
|
_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):
|
def __init__(self):
|
||||||
self._unkown_is_error = False
|
self._unkown_is_error = False
|
||||||
|
|
@ -372,15 +375,36 @@ class Optionable(object):
|
||||||
def get_default(cls):
|
def get_default(cls):
|
||||||
return cls._default
|
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):
|
def validate_color(self, name):
|
||||||
color = getattr(self, name)
|
if not self.validate_color_str(getattr(self, name)):
|
||||||
if not self._color_re.match(color) and not self._color_re_a.match(color):
|
|
||||||
raise KiPlotConfigurationError('Invalid color for `{}` use `#rrggbb` or `#rrggbbaa` with hex digits'.format(name))
|
raise KiPlotConfigurationError('Invalid color for `{}` use `#rrggbb` or `#rrggbbaa` with hex digits'.format(name))
|
||||||
|
|
||||||
def validate_colors(self, names):
|
def validate_colors(self, names):
|
||||||
for color in names:
|
for color in names:
|
||||||
self.validate_color(color)
|
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):
|
class BaseOptions(Optionable):
|
||||||
""" A class to validate and hold output options.
|
""" A class to validate and hold output options.
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,13 @@ Dependencies:
|
||||||
- from: KiAuto
|
- from: KiAuto
|
||||||
role: mandatory
|
role: mandatory
|
||||||
version: 2.0.4
|
version: 2.0.4
|
||||||
|
- from: ImageMagick
|
||||||
|
role: Automatically crop images
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
|
import subprocess
|
||||||
from .misc import (RENDER_3D_ERR, PCB_MAT_COLORS, PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS,
|
from .misc import (RENDER_3D_ERR, PCB_MAT_COLORS, PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS,
|
||||||
KICAD_VERSION_6_0_2, MISSING_TOOL)
|
KICAD_VERSION_6_0_2, MISSING_TOOL)
|
||||||
from .gs import GS
|
from .gs import GS
|
||||||
|
|
@ -24,6 +28,19 @@ from . import log
|
||||||
logger = log.get_logger()
|
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):
|
class Render3DOptions(Base3DOptions):
|
||||||
_colors = {'background1': 'bg_color_1',
|
_colors = {'background1': 'bg_color_1',
|
||||||
'background2': 'bg_color_2',
|
'background2': 'bg_color_2',
|
||||||
|
|
@ -107,6 +124,13 @@ class Render3DOptions(Base3DOptions):
|
||||||
self.show_components = Optionable
|
self.show_components = Optionable
|
||||||
""" *[list(string)|string=all] [none,all] List of components to draw, can be also a string for `none` or `all`.
|
""" *[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` """
|
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__()
|
super().__init__()
|
||||||
self._expand_ext = 'png'
|
self._expand_ext = 'png'
|
||||||
|
|
||||||
|
|
@ -147,7 +171,7 @@ class Render3DOptions(Base3DOptions):
|
||||||
self.copper = "#"+color
|
self.copper = "#"+color
|
||||||
break
|
break
|
||||||
super().config(parent)
|
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)
|
view = self._views.get(self.view, None)
|
||||||
if view is not None:
|
if view is not None:
|
||||||
self.view = view
|
self.view = view
|
||||||
|
|
@ -232,6 +256,9 @@ class Render3DOptions(Base3DOptions):
|
||||||
"Please upgrade KiCad to 6.0.2 or newer")
|
"Please upgrade KiCad to 6.0.2 or newer")
|
||||||
exit(MISSING_TOOL)
|
exit(MISSING_TOOL)
|
||||||
command = self.ensure_tool('KiAuto')
|
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
|
# Base command with overwrite
|
||||||
cmd = [command, '--rec_w', str(self.width+2), '--rec_h', str(self.height+85),
|
cmd = [command, '--rec_w', str(self.width+2), '--rec_h', str(self.height+85),
|
||||||
'3d_view', '--output_name', output]
|
'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')
|
video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_3d_view_screencast.ogv')
|
||||||
if os.path.isfile(video_name):
|
if os.path.isfile(video_name):
|
||||||
os.remove(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
|
@output_class
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ deps = '{\
|
||||||
],\
|
],\
|
||||||
"extra_deb": null,\
|
"extra_deb": null,\
|
||||||
"help_option": "--version",\
|
"help_option": "--version",\
|
||||||
"importance": 3,\
|
"importance": 4,\
|
||||||
"in_debian": true,\
|
"in_debian": true,\
|
||||||
"is_kicad_plugin": false,\
|
"is_kicad_plugin": false,\
|
||||||
"is_python": false,\
|
"is_python": false,\
|
||||||
|
|
@ -190,6 +190,13 @@ deps = '{\
|
||||||
"max_version": null,\
|
"max_version": null,\
|
||||||
"output": "pcbdraw",\
|
"output": "pcbdraw",\
|
||||||
"version": null\
|
"version": null\
|
||||||
|
},\
|
||||||
|
{\
|
||||||
|
"desc": "Automatically crop images",\
|
||||||
|
"mandatory": false,\
|
||||||
|
"max_version": null,\
|
||||||
|
"output": "render_3d",\
|
||||||
|
"version": null\
|
||||||
}\
|
}\
|
||||||
],\
|
],\
|
||||||
"tests": [],\
|
"tests": [],\
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,19 @@
|
||||||
kibot:
|
kibot:
|
||||||
version: 1
|
version: 1
|
||||||
|
|
||||||
|
global:
|
||||||
|
solder_mask_color: blue
|
||||||
|
pcb_finish: ENIG
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
- name: render_list
|
- name: render_list
|
||||||
comment: "Render with only some components"
|
comment: "Render with only some components"
|
||||||
type: render_3d
|
type: render_3d
|
||||||
options:
|
options:
|
||||||
width: 800
|
width: 1280
|
||||||
height: 600
|
height: 960
|
||||||
orthographic: true
|
orthographic: true
|
||||||
zoom: 4
|
zoom: 4
|
||||||
show_components: ["RV1", "RV2", "U1", "U2", "U3"]
|
show_components: ["RV1", "RV2", "U1", "U2", "U3"]
|
||||||
ray_tracing: true
|
ray_tracing: true
|
||||||
background1: "#ff70b7"
|
auto_crop: true
|
||||||
background2: "#ff70b7"
|
|
||||||
# background1: "#ffffff"
|
|
||||||
# background2: "#ffffff"
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue