[Rotation Filter][Added] Support for offsets

- In the database
- Specified in the YAML
This commit is contained in:
Salvador E. Tropea 2023-11-15 09:53:14 -03:00
parent 6b040af849
commit 05ca6f72bd
4 changed files with 58 additions and 9 deletions

View File

@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `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.
- `offsets`: a list of pairs containing regex and offset ("x, y")
- `bennymeg_mode`: used to provide compatibility with the
bennymeg/JLC-Plugin-for-KiCad tool.
- 3D outputs:

View File

@ -139,9 +139,13 @@ Supported filters
- ``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.
The optional fields can contain a comma separated x,y position offset.
This concept is from the bennymeg/JLC-Plugin-for-KiCad tool.
- ``offsets`` :index:`: <pair: filter - rot_footprint; offsets>` [list(list(string))] A list of pairs regular expression/offset.
Components matching the regular expression will be moved the specified offset.
The offset must be two numbers separated by a comma. The first is the X offset.
- ``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.

View File

@ -29,6 +29,7 @@ logger = log.get_logger()
# On KiCad this is not the case, diodes follows it, but capacitors don't. So they get 180.
# - There are exceptions, like SOP-18 or SOP-4 which doesn't follow the JLC rules.
# - KiCad mirrors components on the bottom layer, but JLC doesn't. So you need to "un-mirror" them.
# - The JLC mechanism to interpret rotations changed with time
DEFAULT_ROTATIONS = [["^R_Array_Convex_", 90.0],
["^R_Array_Concave_", 90.0],
# *SOT* seems to need 180
@ -88,8 +89,13 @@ DEFAULT_ROTATIONS = [["^R_Array_Convex_", 90.0],
["^Quectel_L80-R", 270.0],
["^SC-74-6", 180.0],
[r"^PinHeader_2x05_P1\.27mm_Vertical", 90.0],
[r"^PinHeader_2x03_P1\.27mm_Vertical", 90.0],
]
DEFAULT_ROT_FIELDS = ['JLCPCB Rotation Offset', 'JLCRotOffset']
DEFAULT_OFFSETS = [["^USB_C_Receptacle_XKB_U262-16XN-4BVC11", (0.0, -1.44)],
[r"^PinHeader_2x05_P1\.27mm_Vertical", (2.54, 0.635)],
[r"^PinHeader_2x03_P1\.27mm_Vertical", (1.27, 0.635)],
]
DEFAULT_OFFSET_FIELDS = ['JLCPCB Position Offset', 'JLCPosOffset']
@ -118,6 +124,10 @@ class Rot_Footprint(BaseFilter): # noqa: F821
self.rotations = Optionable
""" [list(list(string))] A list of pairs regular expression/rotation.
Components matching the regular expression will be rotated the indicated angle """
self.offsets = Optionable
""" [list(list(string))] A list of pairs regular expression/offset.
Components matching the regular expression will be moved the specified offset.
The offset must be two numbers separated by a comma. The first is the X offset """
self.skip_bottom = False
""" Do not rotate components on the bottom """
self.skip_top = False
@ -138,6 +148,7 @@ class Rot_Footprint(BaseFilter): # noqa: F821
def config(self, parent):
super().config(parent)
# List of rotations
self._rot = []
if isinstance(self.rotations, list):
for r in self.rotations:
@ -151,9 +162,25 @@ class Rot_Footprint(BaseFilter): # noqa: F821
raise KiPlotConfigurationError("The second value in the regex/angle pairs must be a number, not {}".
format(r[1]))
self._rot.append([regex, angle])
# List of offsets
self._offset = []
if isinstance(self.offsets, list):
for r in self.offsets:
if len(r) != 2:
raise KiPlotConfigurationError("Each regex/offset pair must contain exactly two values, not {} ({})".
format(len(r), r))
regex = compile(r[0])
try:
offset = (float(r[1].split(",")[0]), float(r[1].split(",")[1]))
except ValueError:
raise KiPlotConfigurationError("The second value in the regex/offset pairs must be two numbers "
f"separated by a comma, not {r[1]}")
self._offset.append([regex, offset])
if self.extend:
for regex_str, angle in DEFAULT_ROTATIONS:
self._rot.append([compile(regex_str), angle])
for regex_str, offset in DEFAULT_OFFSETS:
self._offset.append([compile(regex_str), offset])
if not self._rot:
raise KiPlotConfigurationError("No rotations provided")
self.rot_fields = self.force_list(self.rot_fields, default=DEFAULT_ROT_FIELDS)
@ -165,8 +192,8 @@ class Rot_Footprint(BaseFilter): # noqa: F821
# 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)
# Currently 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
@ -211,6 +238,17 @@ class Rot_Footprint(BaseFilter): # noqa: F821
# No rotation, apply 0 to apply bottom adjusts
self.apply_rotation_angle(comp, 0)
def apply_offset_value(self, comp, angle, pos_offset_x, pos_offset_y):
rotation = radians(angle)
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
# logger.error(f"Ang: {angle} DB offset: {pos_offset_x},{pos_offset_y} "
# f"Offset: {comp.pos_offset_x},{comp.pos_offset_y}")
comp.pos_offset_x = -GS.from_mm(comp.pos_offset_x)
comp.pos_offset_y = GS.from_mm(comp.pos_offset_y)
def apply_field_offset(self, comp):
for f in self.offset_fields:
value = comp.get_field_value(f)
@ -221,11 +259,16 @@ class Rot_Footprint(BaseFilter): # noqa: F821
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
self.apply_offset_value(comp, comp.offset_footprint_rot, pos_offset_x, pos_offset_y)
return
def apply_offset(self, comp):
if self.apply_field_offset(comp):
return
# Try with the regex
for regex, offset in self._offset:
if regex.search(comp.footprint):
self.apply_offset_value(comp, comp.footprint_rot, offset[0], offset[1])
return
def filter(self, comp):
@ -234,4 +277,4 @@ class Rot_Footprint(BaseFilter): # noqa: F821
# Component should be excluded
return
self.apply_rotation(comp)
self.apply_field_offset(comp)
self.apply_offset(comp)

View File

@ -290,6 +290,7 @@ class PositionOptions(VariantOptions):
center_y = center.y
if c.pos_offset_x is not None:
# Offset from the rotation filter
# logger.error(f"{center_x},{center_y} -> {center_x+c.pos_offset_x},{center_y+c.pos_offset_y}")
center_x += c.pos_offset_x
center_y += c.pos_offset_y
if value is None: