[Rotation Filter] Fixes and extensions for JLCPCB
- Fixed the JLCPCB template to apply correct rotations to the bottom components - Added options to specify rotations and offsets using fields - Added an option to mirror the bottom rotation angles - Needs verifications for the offset stuff Related to #510
This commit is contained in:
parent
4c4a06ffb2
commit
08b1e5f7b8
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -16,6 +16,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Internal templates:
|
||||
- 3DRender_top, 3DRender_top_straight, 3DRender_bottom and
|
||||
3DRender_bottom_straight: to generate simple and quick 3D renders.
|
||||
- Filters:
|
||||
- New `_rot_footprint_jlcpcb` internal filter to fix the JLCPCB bottom
|
||||
rotations.
|
||||
- New options for the `rot_footprint` filters: (See #510)
|
||||
- `mirror_bottom`: used to undo the KiCad mirroring of the bottom.
|
||||
- `rot_fields`: list of fields to indicate arbitrary rotations.
|
||||
- `offset_fields`: list of fields to indicate arbitrary offsets.
|
||||
- `bennymeg_mode`: used to provide compatibility with the
|
||||
bennymeg/JLC-Plugin-for-KiCad tool.
|
||||
- 3D outputs:
|
||||
- `download_lcsc` option to disable LCSC 3D model download (See #415)
|
||||
- BoM:
|
||||
|
|
@ -74,6 +83,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Sub PCB separation using annotation method for some edeges and KiCad
|
||||
versions (#496)
|
||||
- Problems when using NET_NAME(n) for a value (#511)
|
||||
- JLCPCB rotations for bottom components
|
||||
|
||||
|
||||
## [1.6.3] - 2023-06-26
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ Built-in filters
|
|||
- **_only_tht** is used to get only THT parts
|
||||
- **_only_virtual** is used to get only virtual parts
|
||||
- **_rot_footprint** is a default ``rot_footprint`` filter
|
||||
- **_rot_footprint_jlcpcb** is a ``rot_footprint`` filter with option specific for JLCPCB
|
||||
- **_value_split** splits the Value field but the field remains and
|
||||
the extra data is not visible
|
||||
- **_value_split_replace** splits the Value field and replaces it
|
||||
|
|
|
|||
|
|
@ -120,17 +120,32 @@ Supported filters
|
|||
- **rot_footprint**: (**Footprint Rotator**)
|
||||
This filter can rotate footprints, used for the positions file generation. |br|
|
||||
Some manufacturers use a different rotation than KiCad. |br|
|
||||
The `JLCPCB Rotation Offset` and `JLCPCB Position Offset` fields can be used to adjust special cases. |br|
|
||||
The internal `_rot_footprint` filter implements the simplest case.
|
||||
|
||||
- Valid keys:
|
||||
|
||||
- ``bennymeg_mode`` :index:`: <pair: filter - rot_footprint; bennymeg_mode>` [boolean=true] Implements the `rot_fields` and `offset_fields` in the same way that the bennymeg/JLC-Plugin-for-KiCad tool.
|
||||
Note that the computation for bottom rotations is wrong, forcing the user to uses arbitrary rotations.
|
||||
The correct computation is `(180 - component rot) + angle` but the plugin does `180 - (component rot + angle)`.
|
||||
This option forces the wrong computation for compatibility.
|
||||
- ``comment`` :index:`: <pair: filter - rot_footprint; comment>` [string=''] A comment for documentation purposes.
|
||||
- ``extend`` :index:`: <pair: filter - rot_footprint; extend>` [boolean=true] Extends the internal list of rotations with the one provided.
|
||||
Otherwise just use the provided list.
|
||||
- ``invert_bottom`` :index:`: <pair: filter - rot_footprint; invert_bottom>` [boolean=false] Rotation for bottom components is negated, resulting in either: `(- component rot - angle)`
|
||||
or when combined with `negative_bottom`, `(angle - component rot)`.
|
||||
- ``mirror_bottom`` :index:`: <pair: filter - rot_footprint; mirror_bottom>` [boolean=false] The original component rotation for components in the bottom is mirrored before applying
|
||||
the adjust so you get `(180 - component rot + angle)`. This is used by JLCPCB.
|
||||
- ``name`` :index:`: <pair: filter - rot_footprint; name>` [string=''] Used to identify this particular filter definition.
|
||||
- ``negative_bottom`` :index:`: <pair: filter - rot_footprint; negative_bottom>` [boolean=true] Rotation for bottom components is computed via subtraction as `(component rot - angle)`.
|
||||
- ``offset_fields`` :index:`: <pair: filter - rot_footprint; offset_fields>` [string|list(string)='JLCPCB Position Offset,JLCPosOffset'] List of fields that can contain a position offset.
|
||||
The optional fields can contain a comma seperated x,y position offset.
|
||||
This concept is from the bennymeg/JLC-Plugin-for-KiCad tool.
|
||||
|
||||
- ``rot_fields`` :index:`: <pair: filter - rot_footprint; rot_fields>` [string|list(string)='JLCPCB Rotation Offset,JLCRotOffset'] List of fields that can contain a rotation offset.
|
||||
The optional fields can contain a counter-clockwise orientation offset in degrees.
|
||||
This concept is from the bennymeg/JLC-Plugin-for-KiCad tool.
|
||||
|
||||
- ``rotations`` :index:`: <pair: filter - rot_footprint; rotations>` [list(list(string))] A list of pairs regular expression/rotation.
|
||||
Components matching the regular expression will be rotated the indicated angle.
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,10 @@ SIMP_FIL = {'_only_smd': {'comment': 'Internal filter for only SMD parts',
|
|||
'exclude_smd': True},
|
||||
'_rot_footprint': {'type': 'rot_footprint',
|
||||
'comment': 'Internal default footprint rotator'},
|
||||
'_rot_footprint_jlcpcb': {'type': 'rot_footprint',
|
||||
'comment': 'Internal footprint rotator for JLCPCB',
|
||||
'negative_bottom': False,
|
||||
'mirror_bottom': True},
|
||||
'_expand_text_vars': {'type': 'expand_text_vars',
|
||||
'comment': 'Internal default text variables expander'},
|
||||
'_datasheet_link': {'type': 'urlify',
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020-2021 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2021 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Copyright (c) 2020-2023 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
|
||||
# License: AGPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
# Description: Implements a filter to rotate footprints.
|
||||
# This is inspired in JLCKicadTools by Matthew Lai.
|
||||
# I latter added more information from bennymeg/JLC-Plugin-for-KiCad
|
||||
from math import sin, cos, radians
|
||||
from re import compile
|
||||
from .gs import GS
|
||||
from .optionable import Optionable
|
||||
from .error import KiPlotConfigurationError
|
||||
from .macros import macros, document, filter_class # noqa: F401
|
||||
from .misc import W_BADANGLE, W_BADOFFSET
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger()
|
||||
|
|
@ -20,29 +23,30 @@ DEFAULT_ROTATIONS = [["^R_Array_Convex_", 90.0],
|
|||
["^R_Array_Concave_", 90.0],
|
||||
["^SOT-223", 180.0],
|
||||
["^SOT-23", 180.0],
|
||||
["^D_SOT-23", 180.0],
|
||||
["^TSOT-23", 180.0],
|
||||
["^SOT-353", 180.0],
|
||||
["^QFN-", 270.0],
|
||||
["^LQFP-", 270.0],
|
||||
["^TQFP-", 270.0],
|
||||
["^SOP-(?!18_)", 270.0],
|
||||
["^MSOP-", 270.0],
|
||||
["^TSSOP-", 270.0],
|
||||
["^DFN-", 270.0],
|
||||
["^SOIC-", 270.0],
|
||||
# ["^SOP-18_", 0],
|
||||
["^VSSOP-10_", 270.0],
|
||||
["^CP_EIA-3216-18_", 180.0],
|
||||
["^CP_EIA-3528-15_AVX-H", 180.0],
|
||||
["^CP_EIA-3528-21_Kemet-B", 180.0],
|
||||
["^CP_Elec_8x10.5", 180.0],
|
||||
["^CP_Elec_6.3x7.7", 180.0],
|
||||
["^CP_Elec_8x6.7", 180.0],
|
||||
["^CP_Elec_8x10", 180.0],
|
||||
["^CP_EIA-", 180.0],
|
||||
["^CP_Elec_", 180.0],
|
||||
["^C_Elec_", 180.0],
|
||||
["^LED_WS2812B_PLCC4", 180.0],
|
||||
["^(.*?_|V)?QFN-(16|20|24|28|40)(-|_|$)", 270.0],
|
||||
["^Bosch_LGA-8_2x2.5mm_P0.65mm_ClockwisePinNumbering", 90.0],
|
||||
["^Bosch_LGA-", 90.0],
|
||||
["^PowerPAK_SO-8_Single", 270.0],
|
||||
["^HTSSOP-28-1EP_4.4x9.7mm*", 270.0],
|
||||
["^HTSSOP-", 270.0],
|
||||
]
|
||||
DEFAULT_ROT_FIELDS = ['JLCPCB Rotation Offset', 'JLCRotOffset']
|
||||
DEFAULT_OFFSET_FIELDS = ['JLCPCB Position Offset', 'JLCPosOffset']
|
||||
|
||||
|
||||
@filter_class
|
||||
|
|
@ -50,6 +54,7 @@ class Rot_Footprint(BaseFilter): # noqa: F821
|
|||
""" Footprint Rotator
|
||||
This filter can rotate footprints, used for the positions file generation.
|
||||
Some manufacturers use a different rotation than KiCad.
|
||||
The `JLCPCB Rotation Offset` and `JLCPCB Position Offset` fields can be used to adjust special cases.
|
||||
The internal `_rot_footprint` filter implements the simplest case """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
|
@ -63,6 +68,9 @@ class Rot_Footprint(BaseFilter): # noqa: F821
|
|||
self.invert_bottom = False
|
||||
""" Rotation for bottom components is negated, resulting in either: `(- component rot - angle)`
|
||||
or when combined with `negative_bottom`, `(angle - component rot)` """
|
||||
self.mirror_bottom = False
|
||||
""" The original component rotation for components in the bottom is mirrored before applying
|
||||
the adjust so you get `(180 - component rot + angle)`. This is used by JLCPCB """
|
||||
self.rotations = Optionable
|
||||
""" [list(list(string))] A list of pairs regular expression/rotation.
|
||||
Components matching the regular expression will be rotated the indicated angle """
|
||||
|
|
@ -70,6 +78,19 @@ class Rot_Footprint(BaseFilter): # noqa: F821
|
|||
""" Do not rotate components on the bottom """
|
||||
self.skip_top = False
|
||||
""" Do not rotate components on the top """
|
||||
self.rot_fields = Optionable
|
||||
""" [string|list(string)='JLCPCB Rotation Offset,JLCRotOffset'] List of fields that can contain a rotation offset.
|
||||
The optional fields can contain a counter-clockwise orientation offset in degrees.
|
||||
This concept is from the bennymeg/JLC-Plugin-for-KiCad tool """
|
||||
self.offset_fields = Optionable
|
||||
""" [string|list(string)='JLCPCB Position Offset,JLCPosOffset'] List of fields that can contain a position offset.
|
||||
The optional fields can contain a comma separated x,y position offset.
|
||||
This concept is from the bennymeg/JLC-Plugin-for-KiCad tool """
|
||||
self.bennymeg_mode = True
|
||||
""" Implements the `rot_fields` and `offset_fields` in the same way that the bennymeg/JLC-Plugin-for-KiCad tool.
|
||||
Note that the computation for bottom rotations is wrong, forcing the user to uses arbitrary rotations.
|
||||
The correct computation is `(180 - component rot) + angle` but the plugin does `180 - (component rot + angle)`.
|
||||
This option forces the wrong computation for compatibility """
|
||||
|
||||
def config(self, parent):
|
||||
super().config(parent)
|
||||
|
|
@ -91,23 +112,83 @@ class Rot_Footprint(BaseFilter): # noqa: F821
|
|||
self._rot.append([compile(regex_str), angle])
|
||||
if not self._rot:
|
||||
raise KiPlotConfigurationError("No rotations provided")
|
||||
self.rot_fields = self.force_list(self.rot_fields, default=DEFAULT_ROT_FIELDS)
|
||||
self.offset_fields = self.force_list(self.offset_fields, default=DEFAULT_OFFSET_FIELDS)
|
||||
|
||||
def filter(self, comp):
|
||||
""" Apply the rotation """
|
||||
if (self.skip_top and not comp.bottom) or (self.skip_bottom and comp.bottom):
|
||||
# Component should be excluded
|
||||
return
|
||||
for regex, angle in self._rot:
|
||||
if regex.search(comp.footprint):
|
||||
old_angle = comp.footprint_rot
|
||||
if self.negative_bottom and comp.bottom:
|
||||
def apply_rotation_angle(self, comp, angle, bennymeg_mode=False):
|
||||
old_footprint_rot = comp.footprint_rot
|
||||
if comp.bottom:
|
||||
# Apply adjusts for bottom components
|
||||
if bennymeg_mode and self.bennymeg_mode:
|
||||
# Compatible with https://github.com/bennymeg/JLC-Plugin-for-KiCad/
|
||||
# Wrong! The real value is (180-comp.footprint_rot)+angle and not
|
||||
# 180-(comp.footprint_rot+angle)
|
||||
comp.footprint_rot = (comp.footprint_rot + angle) % 360.0
|
||||
comp.offset_footprint_rot = old_footprint_rot
|
||||
comp.footprint_rot = (540.0 - comp.footprint_rot) % 360.0
|
||||
else:
|
||||
if self.mirror_bottom:
|
||||
comp.footprint_rot = 180 - comp.footprint_rot
|
||||
if self.negative_bottom:
|
||||
comp.footprint_rot -= angle
|
||||
else:
|
||||
comp.footprint_rot += angle
|
||||
if self.invert_bottom and comp.bottom:
|
||||
if self.invert_bottom:
|
||||
comp.footprint_rot = -comp.footprint_rot
|
||||
comp.footprint_rot = comp.footprint_rot % 360
|
||||
if GS.debug_level > 2:
|
||||
logger.debug('Rotating ref: {} {}: {} -> {}'.
|
||||
format(comp.ref, comp.footprint, old_angle, comp.footprint_rot))
|
||||
comp.offset_footprint_rot = old_footprint_rot
|
||||
else:
|
||||
comp.footprint_rot += angle
|
||||
comp.offset_footprint_rot = old_footprint_rot
|
||||
comp.footprint_rot = comp.footprint_rot % 360
|
||||
if GS.debug_level > 2:
|
||||
logger.debug('Rotating ref: {} {}: {} -> {}'.
|
||||
format(comp.ref, comp.footprint, old_footprint_rot, comp.footprint_rot))
|
||||
|
||||
def apply_field_rotation(self, comp):
|
||||
for f in self.rot_fields:
|
||||
value = comp.get_field_value(f)
|
||||
if value:
|
||||
try:
|
||||
angle = float(value)
|
||||
except ValueError:
|
||||
logger.warning(f'{W_BADANGLE}Wrong angle `{value}` in {f} field of {comp.ref}')
|
||||
angle = 0
|
||||
self.apply_rotation_angle(comp, angle, bennymeg_mode=True)
|
||||
return
|
||||
|
||||
def apply_rotation(self, comp):
|
||||
if self.apply_field_rotation(comp):
|
||||
return
|
||||
# Try with the regex
|
||||
for regex, angle in self._rot:
|
||||
if regex.search(comp.footprint):
|
||||
self.apply_rotation_angle(comp, angle)
|
||||
return
|
||||
# No rotation, apply 0 to apply bottom adjusts
|
||||
self.apply_rotation_angle(comp, 0)
|
||||
|
||||
def apply_field_offset(self, comp):
|
||||
for f in self.offset_fields:
|
||||
value = comp.get_field_value(f)
|
||||
if value:
|
||||
try:
|
||||
pos_offset_x = float(value.split(",")[0])
|
||||
pos_offset_y = float(value.split(",")[1])
|
||||
except ValueError:
|
||||
logger.warning(f'{W_BADOFFSET}Wrong offset `{value}` in {f} field of {comp.ref}')
|
||||
return
|
||||
rotation = radians(comp.offset_footprint_rot)
|
||||
rsin = sin(rotation)
|
||||
rcos = cos(rotation)
|
||||
comp.pos_offset_x = pos_offset_x * rcos - pos_offset_y * rsin
|
||||
comp.pos_offset_y = pos_offset_x * rsin + pos_offset_y * rcos
|
||||
return
|
||||
|
||||
def filter(self, comp):
|
||||
""" Apply the rotation """
|
||||
logger.error(f"{self.invert_bottom} {self.mirror_bottom}")
|
||||
if (self.skip_top and not comp.bottom) or (self.skip_bottom and comp.bottom):
|
||||
# Component should be excluded
|
||||
return
|
||||
self.apply_rotation(comp)
|
||||
self.apply_field_offset(comp)
|
||||
|
|
|
|||
|
|
@ -906,6 +906,8 @@ class SchematicComponent(object):
|
|||
self.has_pcb_info = False
|
||||
self.qty = 1
|
||||
self.annotation_error = False
|
||||
# Position offset i.e. from the rotation filter
|
||||
self.pos_offset_x = self.pos_offset_y = None
|
||||
# KiCad 5 PCB flags (mutually exclusive)
|
||||
self.smd = False
|
||||
self.virtual = False
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@ W_MISSDIR = '(W132) '
|
|||
W_EXTRAINVAL = '(W133) '
|
||||
W_BADANGLE = '(W134) '
|
||||
W_VALMISMATCH = '(W135) '
|
||||
W_BADOFFSET = '(W136) '
|
||||
# Somehow arbitrary, the colors are real, but can be different
|
||||
PCB_MAT_COLORS = {'fr1': "937042", 'fr2': "949d70", 'fr3': "adacb4", 'fr4': "332B16", 'fr5': "6cc290"}
|
||||
PCB_FINISH_COLORS = {'hal': "8b898c", 'hasl': "8b898c", 'imag': "8b898c", 'enig': "cfb96e", 'enepig': "cfb96e",
|
||||
|
|
|
|||
|
|
@ -288,6 +288,10 @@ class PositionOptions(VariantOptions):
|
|||
center = GS.get_center(m)
|
||||
center_x = center.x
|
||||
center_y = center.y
|
||||
if c.pos_offset_x is not None:
|
||||
# Offset from the rotation filter
|
||||
center_x += c.pos_offset_x
|
||||
center_y += c.pos_offset_y
|
||||
if value is None:
|
||||
value = m.GetValue()
|
||||
footprint = str(m.GetFPID().GetLibItemName()) # pcbnew.UTF8 type
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ definitions:
|
|||
_KIBOT_IMPORT_DIR: 'JLCPCB'
|
||||
_KIBOT_MANF_DIR: '@_KIBOT_IMPORT_DIR@'
|
||||
_KIBOT_MANF_DIR_COMP: '@_KIBOT_IMPORT_DIR@'
|
||||
_KIBOT_POS_PRE_TRANSFORM: _rot_footprint
|
||||
_KIBOT_POS_PRE_TRANSFORM: _rot_footprint_jlcpcb
|
||||
_KIBOT_POS_ENABLED: true
|
||||
_KIBOT_BOM_ENABLED: true
|
||||
_KIBOT_GERBER_LAYERS: |
|
||||
|
|
|
|||
Loading…
Reference in New Issue