KiBot/kibot/out_boardview.py

178 lines
5.8 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (c) 2021 Salvador E. Tropea
# Copyright (c) 2021 Instituto Nacional de Tecnología Industrial
# Copyright (c) 2018-2020 @whitequark
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
# Adapted from: https://github.com/whitequark/kicad-boardview
import re
from pcbnew import SHAPE_POLY_SET
from .gs import GS
from .optionable import BaseOptions
from .macros import macros, document, output_class # noqa: F401
from . import log
logger = log.get_logger()
def skip_module(module, tp=False):
refdes = module.GetReference()
if refdes == "REF**":
return True
if tp and not refdes.startswith("TP"):
return True
if not tp and refdes.startswith("TP"):
return True
return False
def coord(nanometers):
milliinches = nanometers * 5 // 127000
return milliinches
def y_coord(obj, maxy, y):
if obj.IsFlipped():
return coord(y)
else:
return coord(maxy - y)
def pad_sort_key(name):
if re.match(r"^\d+$", name):
return (0, int(name))
else:
return (1, name)
def convert(pcb, brd):
# Board outline
outlines = SHAPE_POLY_SET()
if GS.ki5():
pcb.GetBoardPolygonOutlines(outlines, "")
outline = outlines.Outline(0)
outline_points = [outline.Point(n) for n in range(outline.PointCount())]
else:
pcb.GetBoardPolygonOutlines(outlines)
outline = outlines.Outline(0)
outline_points = [outline.GetPoint(n) for n in range(outline.GetPointCount())]
outline_maxx = max(map(lambda p: p.x, outline_points))
outline_maxy = max(map(lambda p: p.y, outline_points))
brd.write("0\n") # unknown
brd.write("BRDOUT: {count} {width} {height}\n"
.format(count=len(outline_points) + outline.IsClosed(),
width=coord(outline_maxx),
height=coord(outline_maxy)))
for point in outline_points:
brd.write("{x} {y}\n"
.format(x=coord(point.x),
y=coord(point.y)))
if outline.IsClosed():
brd.write("{x} {y}\n"
.format(x=coord(outline_points[0].x),
y=coord(outline_points[0].y)))
brd.write("\n")
# Nets
net_info = pcb.GetNetInfo()
net_items = [net_info.GetNetItem(n) for n in range(1, net_info.GetNetCount())]
brd.write("NETS: {count}\n"
.format(count=len(net_items)))
for net_item in net_items:
code = net_item.GetNet() if GS.ki5() else net_item.GetNetCode()
brd.write("{code} {name}\n"
.format(code=code,
name=net_item.GetNetname().replace(" ", u"\u00A0")))
brd.write("\n")
# Parts
module_list = GS.get_modules()
modules = []
for m in module_list:
if not skip_module(m):
modules.append(m)
brd.write("PARTS: {count}\n".format(count=len(modules)))
pin_at = 0
for module in modules:
module_bbox = module.GetBoundingBox()
brd.write("{ref} {x1} {y1} {x2} {y2} {pin} {side}\n"
.format(ref=module.GetReference(),
x1=coord(module_bbox.GetLeft()),
y1=y_coord(module, outline_maxy, module_bbox.GetTop()),
x2=coord(module_bbox.GetRight()),
y2=y_coord(module, outline_maxy, module_bbox.GetBottom()),
pin=pin_at,
side=1 + module.IsFlipped()))
pin_at += module.GetPadCount()
brd.write("\n")
# Pins
module_list = GS.get_modules()
pads = []
for m in module_list:
if not skip_module(m):
pads_list = m.Pads()
for pad in sorted(pads_list, key=lambda pad: pad_sort_key(pad.GetName())):
pads.append(pad)
brd.write("PINS: {count}\n".format(count=len(pads)))
for pad in pads:
pad_pos = pad.GetPosition()
brd.write("{x} {y} {net} {side}\n"
.format(x=coord(pad_pos.x),
y=y_coord(pad, outline_maxy, pad_pos.y),
net=pad.GetNetCode(),
side=1 + pad.IsFlipped()))
brd.write("\n")
# Nails
module_list = GS.get_modules()
testpoints = []
for m in module_list:
if not skip_module(m, tp=True):
pads_list = m.Pads()
for pad in sorted(pads_list, key=lambda pad: pad_sort_key(pad.GetName())):
testpoints.append((m, pad))
brd.write("NAILS: {count}\n".format(count=len(testpoints)))
for module, pad in testpoints:
pad_pos = pad.GetPosition()
brd.write("{probe} {x} {y} {net} {side}\n"
.format(probe=module.GetReference()[2:],
x=coord(pad_pos.x),
y=y_coord(pad, outline_maxy, pad_pos.y),
net=pad.GetNetCode(),
side=1 + pad.IsFlipped()))
brd.write("\n")
class BoardViewOptions(BaseOptions):
def __init__(self):
with document:
self.output = GS.def_global_output
""" Filename for the output (%i=boardview, %x=brd) """
super().__init__()
self._expand_id = 'boardview'
self._expand_ext = 'brd'
def run(self, output):
with open(output, 'wt') as f:
convert(GS.board, f)
@output_class
class BoardView(BaseOutput): # noqa: F821
""" BoardView
Exports the PCB in board view format.
This format allows simple pads and connections navigation, mainly for circuit debug.
The output can be loaded using Open Board View (https://openboardview.org/) """
def __init__(self):
super().__init__()
with document:
self.options = BoardViewOptions
""" [dict] Options for the `boardview` output """