Skip to content

Commit

Permalink
Implement percentage for panel position
Browse files Browse the repository at this point in the history
  • Loading branch information
yaqwsx committed Dec 28, 2022
1 parent 95e897d commit ae01451
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 18 deletions.
5 changes: 3 additions & 2 deletions doc/panelizeCli.md
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,9 @@ size from the source board. This feature is not supported on KiCAD 5

- `anchor` - Point of the panel to be placed at given position. Can be one of
`tl`, `tr`, `bl`, `br` (corners), `mt`, `mb`, `ml`, `mr` (middle of sides),
`c` (center). The anchors refer to the panel outline. Default `tl`
- `posx`, `posy` - the position of the panel on the page. Default `15mm`
`c` (center). The anchors refer to the panel outline. Default `mt`
- `posx`, `posy` - the position of the panel on the page. Default `50%` for
`posx` and `20mm` for `posy`.

### Custom

Expand Down
18 changes: 18 additions & 0 deletions kikit/defs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from enum import Enum, IntEnum
from .units import mm, inch

# These classes miss in the exported interface

Expand Down Expand Up @@ -88,3 +89,20 @@ class MODULE_ATTR_T(IntEnum):
PAPER_SIZES = [f"A{size}" for size in range(6)] + ["A", "B", "C", "D", "E"] + \
["USLetter", "USLegal", "USLedger"]
PAPER_SIZES = PAPER_SIZES + [f"{paper}-portrait" for paper in PAPER_SIZES]

PAPER_DIMENSIONS = {
"A5": (210 * mm, 148 * mm),
"A4": (297 * mm, 210 * mm),
"A3": (420 * mm, 297 * mm),
"A2": (594 * mm, 420 * mm),
"A1": (841 * mm, 594 * mm),
"A0": (1198 * mm, 841 * mm),
"A": (11 * inch, 8.5 * inch),
"B": (17 * inch, 11 * inch),
"C": (22 * inch, 17 * inch),
"D": (34 * inch, 22 * inch),
"E": (44 * inch, 34 * inch),
"USLetter": (11 * inch, 8.5 * inch),
"USLegal": (14 * inch, 8.5 * inch),
"USLedger": (17 * inch, 11 * inch)
}
25 changes: 25 additions & 0 deletions kikit/kicadUtil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Tuple
from .common import KiLength
from .units import mm
from .sexpr import SExpr, findNode
from .defs import PAPER_DIMENSIONS

def getPageDimensionsFromAst(ast: SExpr) -> Tuple[KiLength, KiLength]:
paperNode = findNode(ast, "paper")
if paperNode is None:
# KiCAD 5 board use "page" instead of "paper"
paperNode = findNode(ast, "page")
if paperNode is None:
raise RuntimeError("Source document doesn't contain paper size information")
value = paperNode.items[1].value
if value == "User":
size = (paperNode[2].value, paperNode[3].value)
return tuple(int(float(x) * mm) for x in size)
try:
size = PAPER_DIMENSIONS[value]
if len(paperNode.items) >= 3 and paperNode.items[2] == "portrait":
size = (size[1], size[0])
return tuple(int(x) for x in size)
except KeyError:
raise RuntimeError(f"Uknown paper size {value}") from None

30 changes: 28 additions & 2 deletions kikit/panelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@

from kikit import substrate
from kikit import units
from kikit.kicadUtil import getPageDimensionsFromAst
from kikit.substrate import Substrate, linestringToKicad, extractRings
from kikit.defs import STROKE_T, Layer, EDA_TEXT_HJUSTIFY_T, EDA_TEXT_VJUSTIFY_T, PAPER_SIZES
from kikit.defs import PAPER_DIMENSIONS, STROKE_T, Layer, EDA_TEXT_HJUSTIFY_T, EDA_TEXT_VJUSTIFY_T, PAPER_SIZES
from kikit.common import *
from kikit.sexpr import parseSexprF, SExpr, Atom
from kikit.sexpr import parseSexprF, SExpr, Atom, findNode
from kikit.annotations import AnnotationReader, TabAnnotation
from kikit.drc import DrcExclusion, readBoardDrcExclusions, serializeExclusion

Expand Down Expand Up @@ -734,6 +735,14 @@ def inheritPageSize(self, board: Union[pcbnew.BOARD, str]) -> None:
if not isinstance(board, pcbnew.BOARD):
board = pcbnew.LoadBoard(board)
self.board.SetPageSettings(board.GetPageSettings())
self.pageSize = None

# What follows is a hack as KiCAD has no API for page access. Therefore,
# we have to read out the page size from the source board and save it so
# we can recover it.
with open(board.GetFileName(), "r") as f:
tree = parseSexprF(f, limit=10) # Introduce limit to speed up parsing
self._inheritedPageDimensions = getPageDimensionsFromAst(tree)

def setPageSize(self, size: Union[str, Tuple[int, int]] ) -> None:
"""
Expand All @@ -744,6 +753,23 @@ def setPageSize(self, size: Union[str, Tuple[int, int]] ) -> None:
raise RuntimeError(f"Unknown paper size: {size}")
self.pageSize = size

def getPageDimensions(self) -> Tuple[KiLength, KiLength]:
"""
Get page size in KiCAD units for the current panel
"""
if self.pageSize is None:
return self._inheritedPageDimensions
if isinstance(self.pageSize, tuple):
return self.pageSize
if isinstance(self.pageSize, str):
if self.pageSize.endswith("-portrait"):
# Portrait
pageSize = PAPER_DIMENSIONS[self.pageSize.split("-")[0]]
return pageSize[1], pageSize[0]
else:
return PAPER_DIMENSIONS[self.pageSize]
raise RuntimeError("Unknown page dimension - this is probably a bug and you should report it.")

def setProperties(self, properties):
"""
Set text properties cached in the board
Expand Down
4 changes: 2 additions & 2 deletions kikit/panelize_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,8 @@ def doPanelization(input, output, preset, plugins=[]):
ki.buildCopperfill(preset["copperfill"], panel)

ki.setStackup(preset["source"], panel)
ki.positionPanel(preset["page"], panel)
ki.setPageSize(preset["page"], panel, board)
ki.positionPanel(preset["page"], panel)

ki.runUserScript(preset["post"], panel)
useHookPlugins(lambda x: x.finish(panel))
Expand Down Expand Up @@ -348,8 +348,8 @@ def separate(input, output, source, page, debug, keepannotations, preservearcs):
panel.appendBoard(input, destination, sourceArea,
interpretAnnotations=(not keepannotations))
ki.setStackup(preset["source"], panel)
ki.positionPanel(preset["page"], panel)
ki.setPageSize(preset["page"], panel, board)
ki.positionPanel(preset["page"], panel)

panel.save(reconstructArcs=preservearcs)
except Exception as e:
Expand Down
12 changes: 9 additions & 3 deletions kikit/panelize_ui_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from shapely.geometry import box
from kikit.plugin import HookPlugin
from kikit.text import kikitTextVars
from kikit.units import BaseValue
from kikit.units import BaseValue, PercentageValue
from kikit.panelize_ui_sections import *
from kikit.substrate import SubstrateNeighbors
from kikit.common import resolveAnchor
Expand Down Expand Up @@ -33,6 +33,8 @@ def encodePreset(value):
return "none"
if isinstance(value, BaseValue):
return str(value)
if isinstance(value, PercentageValue):
return str(value)
if isinstance(value, EDA_TEXT_HJUSTIFY_T) or isinstance(value, EDA_TEXT_VJUSTIFY_T):
return writeJustify(value)
if isinstance(value, Enum):
Expand Down Expand Up @@ -644,8 +646,12 @@ def positionPanel(preset, panel):
Position the panel on the paper
"""
try:
origin = resolveAnchor(preset["anchor"])(panel.boardSubstrate.boundingBox())
translateVec = (-origin[0] + preset["posx"], -origin[1] + preset["posy"])
bBox = panel.boardSubstrate.boundingBox()
pageSize = panel.getPageDimensions()
posx = preset["posx"] * pageSize[0] if isinstance(preset["posx"], PercentageValue) else preset["posx"]
posy = preset["posy"] * pageSize[1] if isinstance(preset["posy"], PercentageValue) else preset["posy"]
origin = resolveAnchor(preset["anchor"])(bBox)
translateVec = (-origin[0] + posx, -origin[1] + posy)
panel.translate(translateVec)
except KeyError as e:
raise PresetError(f"Missing parameter '{e}' in section 'page'")
Expand Down
20 changes: 15 additions & 5 deletions kikit/panelize_ui_sections.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
from typing import Any, List
from kikit import plugin
from kikit.units import readLength, readAngle
from kikit.units import readLength, readAngle, readPercents
from kikit.defs import Layer, EDA_TEXT_HJUSTIFY_T, EDA_TEXT_VJUSTIFY_T, PAPER_SIZES

class PresetError(RuntimeError):
Expand Down Expand Up @@ -31,6 +31,16 @@ def __init__(self, *args, **kwargs):
def validate(self, x):
return readLength(x)

class SLengthOrPercent(SectionBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def validate(self, x):
x = x.strip()
if x.endswith("%"):
return readPercents(x)
return readLength(x)

class SAngle(SectionBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -648,12 +658,12 @@ def ppPost(section):
ANCHORS,
always(),
"Anchor for positioning the panel on the page"),
"posx": SLength(
"posx": SLengthOrPercent(
always(),
"X position of the panel"),
"posy": SLength(
"X position of the panel. Length or percents of page width."),
"posy": SLengthOrPercent(
always(),
"Y position of the panel"),
"Y position of the panel. Length or percents of page height."),
"width": SLength(
typeIn(["user"]),
"Width of custom paper"),
Expand Down
6 changes: 3 additions & 3 deletions kikit/resources/panelizePresets/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@
},
"page": {
"type": "inherit",
"anchor": "tl",
"posx": "15mm",
"posy": "15mm",
"anchor": "mt",
"posx": "50%",
"posy": "20mm",
"width": "1000mm",
"height": "1000mm"
},
Expand Down
24 changes: 23 additions & 1 deletion kikit/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class UnitError(RuntimeError):
deg = 10
rad = 180 / math.pi * deg

UNIT_SPLIT = re.compile(r"\s*(-?\s*\d+(\.\d*)?)\s*(\w+)$")
UNIT_SPLIT = re.compile(r"\s*(-?\s*\d+(\.\d*)?)\s*(\w+|\%)$")

class BaseValue(int):
"""
Expand All @@ -33,6 +33,24 @@ def __repr__(self):
return f"<BaseValue: {int(self)}, {self.str} >"


class PercentageValue(float):
"""
Value in percents that remembers its original string representation.
Value is stored as floating point number where 1 corresponds to 100 %.
"""
def __new__(cls, value, strRepr):
x = super().__new__(cls, value)
x.str = strRepr
return x

def __str__(self):
return self.str

def __repr__(self):
return f"<BaseValue: {int(self)}, {self.str} >"


def readUnit(unitDir, unitStr):
match = UNIT_SPLIT.match(unitStr)
if not match:
Expand Down Expand Up @@ -70,3 +88,7 @@ def readAngle(unitStr):
if not isinstance(unitStr, str):
raise RuntimeError(f"Got '{unitStr}', an angle with units was expected")
return BaseValue(readUnit(unitDir, unitStr), unitStr)

def readPercents(unitStr):
unitDir = { "%": 0.01 }
return PercentageValue(readUnit(unitDir, unitStr), unitStr)

0 comments on commit ae01451

Please sign in to comment.