[Blender export][Added] Support for pcb2blender 2.7

- Solder mask and silk screen color
- PCB finish
- PCB thickness
This commit is contained in:
Salvador E. Tropea 2023-10-11 13:08:10 -03:00
parent 299e06ae3e
commit 3cd644d19d
10 changed files with 186 additions and 45 deletions

View File

@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `forced_name` option to force the name displayed at the top left corner
(#470)
- Blender export:
- Support for pcb2blender v2.6 (Blender 3.5.1)
- Support for pcb2blender v2.6/2.7 (Blender 3.5.1/3.6)
- `auto_camera_z_axis_factor`: used to control the default camera distance
- Options to create simple animations:
- PoV `steps`: to create rotation angle increments

View File

@ -169,7 +169,7 @@ outputs:
type: 'render'
# [string|dict] Options to export the PCB to Blender.
# You can also specify the name of the output that generates the PCB3D file.
# See the `PCB2Blender_2_1` and `PCB2Blender_2_1_haschtl` templates
# See the `PCB2Blender_2_1`, `PCB2Blender_2_7` and `PCB2Blender_2_1_haschtl` templates
pcb3d:
# [string|list(string)='_none'] Name of the filter to mark components as not fitted.
# A short-cut to use for simple cases where a variant is an overkill
@ -211,8 +211,8 @@ outputs:
solder_paste_for_populated: true
# [string=''] Board variant to apply
variant: ''
# [string='2.1'] [2.1,2.1_haschtl] Variant of the format used
version: '2.1'
# [string='2.7'] [2.1,2.1_haschtl,2.7] Variant of the format used
version: '2.7'
# Options to configure how Blender imports the PCB.
# The default values are good for most cases
pcb_import:
@ -2176,12 +2176,14 @@ outputs:
# [list(string)|string=all] [none,all] List of components to include in the pads list,
# can be also a string for `none` or `all`. The default is `all`
show_components: all
# [boolean=false] Create a JSON file containing the board stackup
# [boolean=false] Create a file containing the board stackup
stackup_create: false
# [string='.'] Directory for the stackup file
# [string='.'] Directory for the stackup file. Use 'layers' for 2.7+
stackup_dir: '.'
# [string='board.yaml'] Name for the stackup file
# [string='board.yaml'] Name for the stackup file. Use 'stackup' for 2.7+
stackup_file: 'board.yaml'
# [string='JSON'] [JSON,BIN] Format for the stackup file. Use 'BIN' for 2.7+
stackup_format: 'JSON'
# [string='bounds'] File name for the sub-PCBs bounds
sub_boards_bounds_file: 'bounds'
# [boolean=true] Extract sub-PCBs and their Z axis position

View File

@ -272,7 +272,8 @@ Here is a list of currently defined templates and their outputs / groups:
- `P-Ban_stencil <https://www.p-ban.com/>`__: same as **P-Ban**, but
also generates gerbers for *F.Paste* and *B.Paste* layers.
- `PCB2Blender_2_1 <https://github.com/30350n/pcb2blender>`__
- `PCB2Blender_2_1 <https://github.com/30350n/pcb2blender>`__: Exports the PCB in a format that can be imported by the
pcb2blender tool v2.1 or newer.
- **_PCB2Blender_layers_2_1**: The layers in SVG format. Disabled by default.
- **_PCB2Blender_vrml_2_1**: The VRML for the board. Disabled by default.
@ -280,7 +281,11 @@ Here is a list of currently defined templates and their outputs / groups:
- **_PCB2Blender_2_1**: The PCB3D file. Is enabled and creates the other files.
- **_PCB2Blender_elements_2_1**: **_PCB2Blender_tools_2_1** + **_PCB2Blender_layers_2_1** + **_PCB2Blender_vrml_2_1**
- `PCB2Blender_2_1_haschtl <https://github.com/haschtl/pcb2blender>`__
- `PCB2Blender_2_7 <https://github.com/30350n/pcb2blender>`__: Similar to **PCB2Blender_2_1**, but for v2.7 or newer.
The content is the same, just using *2_1* instead of *2_7*
- `PCB2Blender_2_1_haschtl <https://github.com/haschtl/pcb2blender>`__: Similar to **PCB2Blender_2_1**, but for the
experimental **haschtl** fork.
- Imports **PCB2Blender_2_1** and disables **_PCB2Blender_2_1**
- **_PCB2Blender_tools_2_1_haschtl**: Pads, bounds and stack-up information. Disabled by default.

View File

@ -33,7 +33,7 @@ Parameters:
- **pcb3d** :index:`: <pair: output - blender_export - options; pcb3d>` [string|dict] Options to export the PCB to Blender.
You can also specify the name of the output that generates the PCB3D file.
See the `PCB2Blender_2_1` and `PCB2Blender_2_1_haschtl` templates.
See the `PCB2Blender_2_1`, `PCB2Blender_2_7` and `PCB2Blender_2_1_haschtl` templates.
- Valid keys:
@ -67,7 +67,7 @@ Parameters:
- ``solder_paste_for_populated`` :index:`: <pair: output - blender_export - options - pcb3d; solder_paste_for_populated>` [boolean=true] Add solder paste only for the populated components.
Populated components are the ones listed in `show_components`.
- ``variant`` :index:`: <pair: output - blender_export - options - pcb3d; variant>` [string=''] Board variant to apply.
- ``version`` :index:`: <pair: output - blender_export - options - pcb3d; version>` [string='2.1'] [2.1,2.1_haschtl] Variant of the format used.
- ``version`` :index:`: <pair: output - blender_export - options - pcb3d; version>` [string='2.7'] [2.1,2.1_haschtl,2.7] Variant of the format used.
- **point_of_view** :index:`: <pair: output - blender_export - options; point_of_view>` [dict|list(dict)] How the object is viewed by the camera.

View File

@ -45,9 +45,10 @@ Parameters:
- ``pre_transform`` :index:`: <pair: output - pcb2blender_tools - options; pre_transform>` [string|list(string)='_none'] Name of the filter to transform fields before applying other filters.
A short-cut to use for simple cases where a variant is an overkill.
- ``stackup_create`` :index:`: <pair: output - pcb2blender_tools - options; stackup_create>` [boolean=false] Create a JSON file containing the board stackup.
- ``stackup_dir`` :index:`: <pair: output - pcb2blender_tools - options; stackup_dir>` [string='.'] Directory for the stackup file.
- ``stackup_file`` :index:`: <pair: output - pcb2blender_tools - options; stackup_file>` [string='board.yaml'] Name for the stackup file.
- ``stackup_create`` :index:`: <pair: output - pcb2blender_tools - options; stackup_create>` [boolean=false] Create a file containing the board stackup.
- ``stackup_dir`` :index:`: <pair: output - pcb2blender_tools - options; stackup_dir>` [string='.'] Directory for the stackup file. Use 'layers' for 2.7+.
- ``stackup_file`` :index:`: <pair: output - pcb2blender_tools - options; stackup_file>` [string='board.yaml'] Name for the stackup file. Use 'stackup' for 2.7+.
- ``stackup_format`` :index:`: <pair: output - pcb2blender_tools - options; stackup_format>` [string='JSON'] [JSON,BIN] Format for the stackup file. Use 'BIN' for 2.7+.
- ``sub_boards_bounds_file`` :index:`: <pair: output - pcb2blender_tools - options; sub_boards_bounds_file>` [string='bounds'] File name for the sub-PCBs bounds.
- ``sub_boards_create`` :index:`: <pair: output - pcb2blender_tools - options; sub_boards_create>` [boolean=true] Extract sub-PCBs and their Z axis position.
- ``sub_boards_dir`` :index:`: <pair: output - pcb2blender_tools - options; sub_boards_dir>` [string='boards'] Directory for the boards definitions.

View File

@ -451,12 +451,12 @@ class Optionable(object):
for color in names:
self.validate_color(color)
def parse_one_color(self, color):
def parse_one_color(self, color, scale=1/255.0):
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)
alpha = int(res[3], 16)*scale
return (int(res[0], 16)*scale, int(res[1], 16)*scale, int(res[2], 16)*scale, alpha)
def color_to_rgb(self, color):
index = 4 if len(color) > 4 else 0

View File

@ -248,8 +248,8 @@ class PCB3DExportOptions(Base3DOptionsWithHL):
with document:
self.output = GS.def_global_output
""" Name for the generated PCB3D file (%i='blender_export' %x='pcb3d') """
self.version = '2.1'
""" [2.1,2.1_haschtl] Variant of the format used """
self.version = '2.7'
""" [2.1,2.1_haschtl,2.7] Variant of the format used """
self.solder_paste_for_populated = True
""" Add solder paste only for the populated components.
Populated components are the ones listed in `show_components` """
@ -307,7 +307,7 @@ class Blender_ExportOptions(BaseOptions):
self.pcb3d = PCB3DExportOptions
""" *[string|dict] Options to export the PCB to Blender.
You can also specify the name of the output that generates the PCB3D file.
See the `PCB2Blender_2_1` and `PCB2Blender_2_1_haschtl` templates """
See the `PCB2Blender_2_1`, `PCB2Blender_2_7` and `PCB2Blender_2_1_haschtl` templates """
self.pcb_import = PCB2BlenderOptions
""" Options to configure how Blender imports the PCB.
The default values are good for most cases """
@ -461,11 +461,19 @@ class Blender_ExportOptions(BaseOptions):
configure_and_run(tree, out_dir, ' - Creating SVG for layers ...')
def create_pads(self, dest_dir):
options = {'stackup_create': False}
if self.pcb3d.version == '2.1_haschtl':
options['stackup_create'] = True
elif self.pcb3d.version == '2.7':
options['stackup_create'] = True
options['stackup_file'] = 'stackup'
options['stackup_dir'] = 'layers'
options['stackup_format'] = 'BIN'
tree = {'name': '_temporal_pcb3d_tools',
'type': 'pcb2blender_tools',
'comment': 'Internally created for the PCB3D',
'dir': dest_dir,
'options': {'stackup_create': self.pcb3d.version == '2.1_haschtl'}}
'options': options}
if self.pcb3d.solder_paste_for_populated:
sc = 'all'
if not self.pcb3d._show_all_components:

View File

@ -5,6 +5,7 @@
# Project: KiBot (formerly KiPlot)
# Some code is adapted from: https://github.com/30350n/pcb2blender
from dataclasses import dataclass, field
from enum import IntEnum
import json
import os
import re
@ -37,6 +38,34 @@ class BoardDef:
stacked_boards: List[StackedBoard] = field(default_factory=list)
class KiCadColor(IntEnum):
CUSTOM = 0
GREEN = 1
RED = 2
BLUE = 3
PURPLE = 4
BLACK = 5
WHITE = 6
YELLOW = 7
class SurfaceFinish(IntEnum):
HASL = 0
ENIG = 1
NONE = 2
SURFACE_FINISH_MAP = {
"ENIG": SurfaceFinish.ENIG,
"ENEPIG": SurfaceFinish.ENIG,
"Hard gold": SurfaceFinish.ENIG,
"ImAu": SurfaceFinish.ENIG,
"Immersion Gold": SurfaceFinish.ENIG,
"Immersion Au": SurfaceFinish.ENIG,
"HT_OSP": SurfaceFinish.NONE,
"OSP": SurfaceFinish.NONE}
def sanitized(name):
""" Replace character that aren't alphabetic by _ """
return re.sub(r"[\W]+", "_", name)
@ -59,11 +88,13 @@ class PCB2Blender_ToolsOptions(VariantOptions):
self.pads_info_dir = 'pads'
""" Sub-directory where the pads info files are stored """
self.stackup_create = False
""" Create a JSON file containing the board stackup """
""" Create a file containing the board stackup """
self.stackup_file = 'board.yaml'
""" Name for the stackup file """
""" Name for the stackup file. Use 'stackup' for 2.7+ """
self.stackup_dir = '.'
""" Directory for the stackup file """
""" Directory for the stackup file. Use 'layers' for 2.7+ """
self.stackup_format = 'JSON'
""" [JSON,BIN] Format for the stackup file. Use 'BIN' for 2.7+ """
self.sub_boards_create = True
""" Extract sub-PCBs and their Z axis position """
self.sub_boards_dir = 'boards'
@ -146,30 +177,53 @@ class PCB2Blender_ToolsOptions(VariantOptions):
pad.GetDrillShape(),
*map(GS.to_mm, pad.GetDrillSize())))
def parse_kicad_color(self, string):
if string[0] == "#":
return KiCadColor.CUSTOM, self.parse_one_color(string, scale=1)[:3]
else:
return KiCadColor[string.upper()], (0, 0, 0)
def do_stackup(self, dir_name):
if not self.stackup_create or (not GS.global_pcb_finish and not GS.stackup):
return
dir_name = os.path.join(dir_name, self.stackup_dir)
os.makedirs(dir_name, exist_ok=True)
fname = os.path.join(dir_name, self.stackup_file)
# Create the board_info
board_info = {}
if GS.global_pcb_finish:
board_info['copper_finish'] = GS.global_pcb_finish
if GS.stackup:
layers_parsed = []
for la in GS.stackup:
parsed_layer = {'name': la.name, 'type': la.type}
if la.color is not None:
parsed_layer['color'] = la.color
if la.thickness is not None:
parsed_layer['thickness'] = la.thickness/1000
layers_parsed.append(parsed_layer)
board_info['stackup'] = layers_parsed
data = json.dumps(board_info, indent=3)
logger.debug('Stackup: '+str(data))
with open(fname, 'wt') as f:
f.write(data)
if self.stackup_format == 'JSON':
# This is for the experimental "haschtl" fork
# Create the board_info
board_info = {}
if GS.global_pcb_finish:
board_info['copper_finish'] = GS.global_pcb_finish
if GS.stackup:
layers_parsed = []
for la in GS.stackup:
parsed_layer = {'name': la.name, 'type': la.type}
if la.color is not None:
parsed_layer['color'] = la.color
if la.thickness is not None:
parsed_layer['thickness'] = la.thickness/1000
layers_parsed.append(parsed_layer)
board_info['stackup'] = layers_parsed
data = json.dumps(board_info, indent=3)
logger.debug('Stackup: '+str(data))
with open(fname, 'wt') as f:
f.write(data)
else: # self.stackup_format == 'BIN':
# This is for 2.7+
# Map the surface finish
if GS.global_pcb_finish:
surface_finish = SURFACE_FINISH_MAP.get(GS.global_pcb_finish, SurfaceFinish.HASL)
else:
surface_finish = SurfaceFinish.NONE
ds = GS.board.GetDesignSettings()
thickness_mm = GS.to_mm(ds.GetBoardThickness())
mask_color, mask_color_custom = self.parse_kicad_color(GS.global_solder_mask_color.upper())
silks_color, silks_color_custom = self.parse_kicad_color(GS.global_silk_screen_color.upper())
logger.error(f"{mask_color} {mask_color_custom}")
with open(fname, 'wb') as f:
f.write(struct.pack("!fbBBBbBBBb", thickness_mm, mask_color, *mask_color_custom, silks_color,
*silks_color_custom, surface_finish))
def get_boarddefs(self):
""" Extract the sub-PCBs and their positions using texts.

View File

@ -0,0 +1,71 @@
# PCB2Blender (PCB3D) format for plug-in v2.7
# URL: https://github.com/30350n/pcb2blender
kibot:
version: 1
groups:
- name: _PCB2Blender_elements_2_7
outputs:
- _PCB2Blender_tools_2_7
- _PCB2Blender_layers_2_7
- _PCB2Blender_vrml_2_7
outputs:
- name: _PCB2Blender_tools_2_7
comment: Pads information and board bounds for PCB3D 2.7
type: pcb2blender_tools
dir: '%f%v_pcb3d'
run_by_default: false
options:
stackup_create: true
stackup_file: stackup
stackup_dir: layers
stackup_format: BIN
- name: _PCB2Blender_layers_2_7
comment: SVG files for the layers for PCB3D 2.7
type: svg
dir: '%f%v_pcb3d/layers'
run_by_default: false
options:
output: "%i.%x"
margin: 1
limit_viewbox: true
svg_precision: 6
drill_marks: none
layers:
- F.Cu
- B.Cu
- F.Paste
- B.Paste
- layer: F.SilkS
suffix: F_SilkS
- layer: B.SilkS
suffix: B_SilkS
- F.Mask
- B.Mask
- name: _PCB2Blender_vrml_2_7
comment: VRML model for PCB3D 2.7
type: vrml
dir: '%f%v_pcb3d'
run_by_default: false
options:
output: pcb.wrl
dir_models: components
use_pcb_center_as_ref: false
model_units: meters
- name: _PCB2Blender_2_7
comment: PCB3D model for pcb2blender plug-in
type: compress
options:
output: '%f%v.pcb3d'
format: ZIP
files:
- from_output: _PCB2Blender_tools_2_7
from_output_dir: true
- from_output: _PCB2Blender_layers_2_7
dest: layers
- from_output: _PCB2Blender_vrml_2_7
from_output_dir: true

View File

@ -3,15 +3,15 @@ kibot:
version: 1
import:
- file: PCB2Blender_2_1
- file: PCB2Blender_2_7
outputs:
- name: '3d_export'
comment: "Exports the PCB in blender format"
type: blender_export
disable_run_by_default: _PCB2Blender_2_1
disable_run_by_default: _PCB2Blender_2_7
options:
pcb3d: _PCB2Blender_2_1
pcb3d: _PCB2Blender_2_7
# camera:
# name: MyCamera
# pos_x: 0.3