From c65037be0bf3d5a6cafe2bcc35e3218aae04ee79 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Tue, 14 Feb 2023 07:06:49 -0300 Subject: [PATCH] [PCBDraw][KiCad v7] Applied upstream patches --- kibot/PcbDraw/README.md | 5 + kibot/PcbDraw/pcbnew_transition.py | 119 ++- kibot/PcbDraw/plot.py | 42 +- .../7_0_0/batteryPack-top_connector.svg | 723 +++++++++++++++++- .../7_0_0/kibom-variant_3-top-C1.png | Bin 35 -> 5477 bytes tests/reference/7_0_0/kibom-variant_3-top.png | Bin 32 -> 5495 bytes 6 files changed, 876 insertions(+), 13 deletions(-) mode change 120000 => 100644 tests/reference/7_0_0/batteryPack-top_connector.svg mode change 120000 => 100644 tests/reference/7_0_0/kibom-variant_3-top-C1.png mode change 120000 => 100644 tests/reference/7_0_0/kibom-variant_3-top.png diff --git a/kibot/PcbDraw/README.md b/kibot/PcbDraw/README.md index 58940d35..87774718 100644 --- a/kibot/PcbDraw/README.md +++ b/kibot/PcbDraw/README.md @@ -206,3 +206,8 @@ This file comes from KiKit, but it has too much in common with `populate.py`. - Adapted Template._renderBoards to just copy the files (keeping the extensions) - Added listResources, with some changes to copyRelativeTo and _copyResources - The command used for git is now configurable + +## 2023-02-14 Update + +- Changed to transition 0.3.2 (is a tag, detached from main?!) +- Applied v7 compatibility patches from 9c676a7494995c5aeab086e041bc0ca3967f472d to 6e9c0b7077b5cfed58866f13ad745130e8920185 (2023-01-12) diff --git a/kibot/PcbDraw/pcbnew_transition.py b/kibot/PcbDraw/pcbnew_transition.py index b6bdb2c9..d61c7c72 100644 --- a/kibot/PcbDraw/pcbnew_transition.py +++ b/kibot/PcbDraw/pcbnew_transition.py @@ -36,6 +36,51 @@ def setAuxOrigin(self, o): def NewBoard(filename): return pcbnew.BOARD() +def patchRotate(item): + if hasattr(item, "Rotate"): + originalRotate = item.Rotate + if not getattr(originalRotate, "patched", False): + newRotate = lambda self, center, angle: originalRotate(self, center, angle.AsTenthsOfADegree()) + setattr(newRotate, "patched", True) + item.Rotate = newRotate + # We have to ignore PCB_DIMs objects as the orientation has different meaning + if hasattr(item, "SetOrientation") and not hasattr(item, "GetUnitsMode"): + originalSetOrientation = item.SetOrientation + if not getattr(originalSetOrientation, "patched", False): + newSetOrientation = lambda self, angle: originalSetOrientation(self, angle.AsTenthsOfADegree()) + setattr(newSetOrientation, "patched", True) + item.SetOrientation = newSetOrientation + # We have to ignore PCB_DIMs objects as the orientation has different meaning + if hasattr(item, "GetOrientation") and not hasattr(item, "GetUnitsMode"): + originalGetOrientation = item.GetOrientation + if not getattr(originalGetOrientation, "patched", False): + newGetOrientation = lambda self: pcbnew.EDA_ANGLE(originalGetOrientation(self), pcbnew.TENTHS_OF_A_DEGREE_T) + setattr(newGetOrientation, "patched", True) + item.GetOrientation = newGetOrientation + if hasattr(item, "GetDrawRotation"): + originalGetDrawRotation = item.GetDrawRotation + if not getattr(originalGetDrawRotation, "patched", False): + newGetDrawRotation = lambda self: pcbnew.EDA_ANGLE(originalGetDrawRotation(self), pcbnew.TENTHS_OF_A_DEGREE_T) + setattr(newGetDrawRotation, "patched", True) + item.GetDrawRotation = newGetDrawRotation + if hasattr(item, "SetTextAngle"): + originalSetTextAngle = item.SetTextAngle + if not getattr(originalSetTextAngle, "patched", False): + newSetTextAngle = lambda self, angle: originalSetTextAngle(self, angle.AsTenthsOfADegree()) + setattr(newSetTextAngle, "patched", True) + item.SetTextAngle = newSetTextAngle + if hasattr(item, "SetHatchOrientation"): + originalSetHatchOrientation = item.SetHatchOrientation + if not getattr(originalSetHatchOrientation, "patched", False): + newSetHatchOrientation = lambda self, angle: originalSetHatchOrientation(self, angle.AsTenthsOfADegree()) + setattr(newSetHatchOrientation, "patched", True) + item.SetHatchOrientation = newSetHatchOrientation + +def pathGetItemDescription(item): + if hasattr(item, "GetSelectMenuText") and not hasattr(item, "GetItemDescription"): + setattr(item, "GetItemDescription", getattr(item, "GetSelectMenuText")) + + KICAD_VERSION = getVersion() def isV6(version=KICAD_VERSION): @@ -43,7 +88,12 @@ def isV6(version=KICAD_VERSION): return True return version[0] == 6 -if not isV6(KICAD_VERSION): +def isV7(version=KICAD_VERSION): + if version[0] == 6 and version[1] == 99: + return True + return version[0] == 7 + +if not isV6(KICAD_VERSION) and not isV7(KICAD_VERSION): # Introduce new functions pcbnew.NewBoard = NewBoard @@ -89,3 +139,70 @@ if not isV6(KICAD_VERSION): # PCB_TEXT pcbnew.PCB_TEXT.SetTextThickness = pcbnew.PCB_TEXT.SetThickness +#### V7 compatibility section +try: + from pcbnew import EDA_ANGLE as _transition_EDA_ANGLE +except ImportError: + from .eda_angle import EDA_ANGLE_T, EDA_ANGLE + pcbnew.EDA_ANGLE = EDA_ANGLE + pcbnew.DEGREES_T = EDA_ANGLE_T.DEGREES_T + pcbnew.RADIANS_T = EDA_ANGLE_T.RADIANS_T + pcbnew.TENTHS_OF_A_DEGREE_T = EDA_ANGLE_T.TENTHS_OF_A_DEGREE_T + +if not isV7(KICAD_VERSION): + # VECTOR2I & wxPoint + class _transition_VECTOR2I(pcbnew.wxPoint): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + pcbnew.VECTOR2I = _transition_VECTOR2I + + # EDA_RECT and BOX2I + class _transition_BOX2I(pcbnew.EDA_RECT): + def __init__(self, *args): + # We now use this to construct BOX2I and the points are VECTOR2I + if len(args) == 2 and isinstance(args[0], pcbnew.wxPoint) and isinstance(args[1], pcbnew.wxPoint): + super().__init__(pcbnew.wxPoint(*args[0]), pcbnew.wxSize(*args[1])) + else: + super().__init__(*args) + pcbnew.BOX2I = _transition_BOX2I + + # DRILL_MARKS + pcbnew.DRILL_MARKS_NO_DRILL_SHAPE = 0 + pcbnew.DRILL_MARKS_SMALL_DRILL_SHAPE = 1 + pcbnew.DRILL_MARKS_FULL_DRILL_SHAPE = 2 + + # ZONE + pcbnew.ZONE.SetAssignedPriority = pcbnew.ZONE.SetPriority + pcbnew.ZONE.GetAssignedPriority = pcbnew.ZONE.GetPriority + + # Orientation + for x in dir(pcbnew): + patchRotate(getattr(pcbnew, x)) + + originalCalcArcAngles = pcbnew.EDA_SHAPE.CalcArcAngles + if not getattr(originalCalcArcAngles, "patched", False): + def newCalcArcAngles(self, start, end): + start.value = self.GetArcAngleStart() / 10 + if self.GetShape() == pcbnew.SHAPE_T_CIRCLE: + end.value = start.value + 360 + else: + end.value = start.value + self.GetArcAngle() / 10 + setattr(newCalcArcAngles, "patched", True) + pcbnew.EDA_SHAPE.CalcArcAngles = newCalcArcAngles + + # GetSelectMenuText + for x in dir(pcbnew): + pathGetItemDescription(getattr(pcbnew, x)) + + # EDA_TEXT + originalTextSize = pcbnew.EDA_TEXT.SetTextSize + pcbnew.EDA_TEXT.SetTextSize = lambda self, size: originalTextSize(self, pcbnew.wxSize(size[0], size[1])) + + # PAD + originalSetDrillSize = pcbnew.PAD.SetDrillSize + pcbnew.PAD.SetDrillSize = lambda self, size: originalSetDrillSize(self, pcbnew.wxSize(size[0], size[1])) + + originalSetSize = pcbnew.PAD.SetSize + pcbnew.PAD.SetSize = lambda self, size: originalSetSize(self, pcbnew.wxSize(size[0], size[1])) + diff --git a/kibot/PcbDraw/plot.py b/kibot/PcbDraw/plot.py index 6a0606b8..8ca660e6 100644 --- a/kibot/PcbDraw/plot.py +++ b/kibot/PcbDraw/plot.py @@ -17,7 +17,7 @@ from typing import Callable, Dict, List, Optional, Tuple, TypeVar, Union, Any from . import np from .unit import read_resistance from lxml import etree, objectify # type: ignore -from .pcbnew_transition import KICAD_VERSION, isV6, pcbnew # type: ignore +from .pcbnew_transition import KICAD_VERSION, isV6, isV7, pcbnew # type: ignore T = TypeVar("T") Numeric = Union[int, float] @@ -30,6 +30,8 @@ PKG_BASE = os.path.dirname(__file__) etree.register_namespace("xlink", "http://www.w3.org/1999/xlink") +LEGACY_KICAD = not isV6() and not isV7() + default_style = { "copper": "#417e5a", "board": "#4ca06c", @@ -96,7 +98,10 @@ class SvgPathItem: def is_same(p1: Point, p2: Point) -> bool: dx = p1[0] - p2[0] dy = p1[1] - p2[1] - return math.sqrt(dx*dx+dy*dy) < 100 + pseudo_distance = dx*dx + dy*dy + if isV7(): + return pseudo_distance < 0.01 ** 2 + return pseudo_distance < 100 ** 2 def format(self, first: bool) -> str: ret = "" @@ -521,7 +526,7 @@ def remove_inkscape_annotation(tree: etree.Element) -> None: @dataclass class Hole: position: Tuple[int, int] - orientation: int + orientation: pcbnew.EDA_ANGLE drillsize: Tuple[int, int] def get_svg_path_d(self, ki2svg: Callable[[int], float]) -> str: @@ -570,14 +575,14 @@ def collect_holes(board: pcbnew.BOARD) -> List[Hole]: orientation=pad.GetOrientation(), drillsize=(drs.x, drs.y) )) - via_type = 'VIA' if not isV6(KICAD_VERSION) else 'PCB_VIA' + via_type = 'VIA' if LEGACY_KICAD else 'PCB_VIA' for track in board.GetTracks(): if track.GetClass() != via_type: continue pos = track.GetPosition() holes.append(Hole( position=(pos[0], pos[1]), - orientation=0, + orientation=pcbnew.EDA_ANGLE(0, pcbnew.DEGREES_T), drillsize=(track.GetDrillValue(), track.GetDrillValue()) )) return holes @@ -676,7 +681,7 @@ class PlotSubstrate(PlotInterface): el = etree.SubElement(layer, "path") el.attrib["d"] = hole.get_svg_path_d(self._plotter.ki2svg) el.attrib["transform"] = "translate({} {}) rotate({})".format( - position[0], position[1], -hole.orientation / 10) + position[0], position[1], -hole.orientation.AsDegrees()) def _process_baselayer(self, name: str, source_filename: str) -> None: clipPath = self._plotter.get_def_slot(tag_name="clipPath", id="cut-off") @@ -747,7 +752,7 @@ class PlotSubstrate(PlotInterface): el.attrib["stroke-width"] = str(stroke) el.attrib["points"] = points el.attrib["transform"] = "translate({} {}) rotate({})".format( - position[0], position[1], -hole.orientation / 10) + position[0], position[1], -hole.orientation.AsDegrees()) @dataclass class PlacedComponentInfo: @@ -1019,8 +1024,15 @@ class PcbPlotter(): self.yield_warning: Callable[[str, str], None] = lambda tag, msg: None # Handle warnings - self.ki2svg = self._ki2svg_v6 if isV6(KICAD_VERSION) else self._ki2svg_v5 - self.svg2ki = self._svg2ki_v6 if isV6(KICAD_VERSION) else self._svg2ki_v5 + if isV7(): + self.ki2svg = self._ki2svg_v7 + self.svg2ki = self._svg2ki_v7 + elif isV6(): + self.ki2svg = self._ki2svg_v6 + self.svg2ki = self._svg2ki_v6 + else: + self.ki2svg = self._ki2svg_v5 + self.svg2ki = self._svg2ki_v5 @property def svg_precision(self) -> int: @@ -1074,7 +1086,7 @@ class PcbPlotter(): value = footprint.GetValue().strip() ref = footprint.GetReference().strip() center = footprint.GetPosition() - orient = math.radians(footprint.GetOrientation() / 10) + orient = math.radians(footprint.GetOrientation().AsDegrees()) pos = (center.x, center.y, orient) callback(lib, name, ref, value, pos) @@ -1198,8 +1210,10 @@ class PcbPlotter(): # Method does not exist in older versions of KiCad pass popt.SetTextMode(pcbnew.PLOT_TEXT_MODE_STROKE) - if isV6(KICAD_VERSION): + if isV6(): popt.SetSvgPrecision(self.svg_precision, False) + elif isV7(): + popt.SetSvgPrecision(self.svg_precision) for action in to_plot: if len(action.layers) == 0: continue @@ -1238,6 +1252,12 @@ class PcbPlotter(): def _svg2ki_v5(self, x: float) -> int: return dmil2ki(x) + def _svg2ki_v7(self, x: float) -> int: + return int(pcbnew.FromMM(x)) + + def _ki2svg_v7(self, x: int) -> float: + return float(pcbnew.ToMM(x)) + def _shrink_svg(self, svg: etree.ElementTree, margin: tuple, compute_bbox: bool=False) -> None: """ Shrink the SVG canvas to the size of the drawing. Add margin in diff --git a/tests/reference/7_0_0/batteryPack-top_connector.svg b/tests/reference/7_0_0/batteryPack-top_connector.svg deleted file mode 120000 index e4622d83..00000000 --- a/tests/reference/7_0_0/batteryPack-top_connector.svg +++ /dev/null @@ -1 +0,0 @@ -../6_0_8/batteryPack-top_connector.svg \ No newline at end of file diff --git a/tests/reference/7_0_0/batteryPack-top_connector.svg b/tests/reference/7_0_0/batteryPack-top_connector.svg new file mode 100644 index 00000000..7ec1f9b6 --- /dev/null +++ b/tests/reference/7_0_0/batteryPack-top_connector.svg @@ -0,0 +1,722 @@ + + + Picture generated by PcbDraw + Picture generated by PcbDraw + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +MINUS +MINUS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +github.com/RoboticsBrno/ + +github.com/RoboticsBrno/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +RB0002-BatteryPack + +RB0002-BatteryPack + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +MID + +MID + + + + + + + + + + + + + + + + + + + +PLUS +PLUS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/reference/7_0_0/kibom-variant_3-top-C1.png b/tests/reference/7_0_0/kibom-variant_3-top-C1.png deleted file mode 120000 index 2c925fe4..00000000 --- a/tests/reference/7_0_0/kibom-variant_3-top-C1.png +++ /dev/null @@ -1 +0,0 @@ -../6_0_8/kibom-variant_3-top-C1.png \ No newline at end of file diff --git a/tests/reference/7_0_0/kibom-variant_3-top-C1.png b/tests/reference/7_0_0/kibom-variant_3-top-C1.png new file mode 100644 index 0000000000000000000000000000000000000000..d1caa75a84ddbaef36265f474338b49cb9cad98a GIT binary patch literal 5477 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 literal 0 HcmV?d00001 diff --git a/tests/reference/7_0_0/kibom-variant_3-top.png b/tests/reference/7_0_0/kibom-variant_3-top.png deleted file mode 120000 index f0a6eea7..00000000 --- a/tests/reference/7_0_0/kibom-variant_3-top.png +++ /dev/null @@ -1 +0,0 @@ -../6_0_8/kibom-variant_3-top.png \ No newline at end of file diff --git a/tests/reference/7_0_0/kibom-variant_3-top.png b/tests/reference/7_0_0/kibom-variant_3-top.png new file mode 100644 index 0000000000000000000000000000000000000000..18cd03cd2136c2a97d00d7f9ad809609622bd956 GIT binary patch literal 5495 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 GWB&);bLoQs literal 0 HcmV?d00001