Loading files
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "packagecheck.h"
#include "../../graphics/graphicslayer.h"
#include "../../utils/toolbox.h"
#include "msg/msgduplicatepadname.h"
#include "msg/msgmissingfootprint.h"
#include "msg/msgmissingfootprintname.h"
#include "msg/msgmissingfootprintvalue.h"
#include "msg/msgpadclearanceviolation.h"
#include "msg/msgpadoverlapswithplacement.h"
#include "msg/msgwrongfootprinttextlayer.h"
#include "package.h"
#include <QtCore>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
PackageCheck::PackageCheck(const Package& package) noexcept
: LibraryElementCheck(package), mPackage(package) {
}
PackageCheck::~PackageCheck() noexcept {
}
/*******************************************************************************
* General Methods
******************************************************************************/
LibraryElementCheckMessageList PackageCheck::runChecks() const {
LibraryElementCheckMessageList msgs = LibraryElementCheck::runChecks();
checkDuplicatePadNames(msgs);
checkMissingFootprint(msgs);
checkMissingTexts(msgs);
checkWrongTextLayers(msgs);
checkPadsClearanceToPads(msgs);
checkPadsClearanceToPlacement(msgs);
return msgs;
}
/*******************************************************************************
* Protected Methods
******************************************************************************/
void PackageCheck::checkDuplicatePadNames(MsgList& msgs) const {
QSet<CircuitIdentifier> padNames;
for (const PackagePad& pad : mPackage.getPads()) {
if (padNames.contains(pad.getName())) {
msgs.append(std::make_shared<MsgDuplicatePadName>(pad));
} else {
padNames.insert(pad.getName());
}
}
}
void PackageCheck::checkMissingFootprint(MsgList& msgs) const {
if (mPackage.getFootprints().isEmpty()) {
msgs.append(std::make_shared<MsgMissingFootprint>());
}
}
void PackageCheck::checkMissingTexts(MsgList& msgs) const {
for (auto itFtp = mPackage.getFootprints().begin();
itFtp != mPackage.getFootprints().end(); ++itFtp) {
QHash<QString, QVector<std::shared_ptr<const StrokeText>>> texts;
for (auto it = (*itFtp).getStrokeTexts().begin();
it != (*itFtp).getStrokeTexts().end(); ++it) {
texts[(*it).getText()].append(it.ptr());
}
if (texts.value("{{NAME}}").isEmpty()) {
msgs.append(std::make_shared<MsgMissingFootprintName>(itFtp.ptr()));
}
if (texts.value("{{VALUE}}").isEmpty()) {
msgs.append(std::make_shared<MsgMissingFootprintValue>(itFtp.ptr()));
}
}
}
void PackageCheck::checkWrongTextLayers(MsgList& msgs) const {
QHash<QString, QString> textLayers = {
std::make_pair("{{NAME}}", QString(GraphicsLayer::sTopNames)),
std::make_pair("{{VALUE}}", QString(GraphicsLayer::sTopValues)),
};
for (auto itFtp = mPackage.getFootprints().begin();
itFtp != mPackage.getFootprints().end(); ++itFtp) {
for (auto it = (*itFtp).getStrokeTexts().begin();
it != (*itFtp).getStrokeTexts().end(); ++it) {
QString expectedLayer = textLayers.value((*it).getText());
if ((!expectedLayer.isEmpty()) &&
((*it).getLayerName() != expectedLayer)) {
msgs.append(std::make_shared<MsgWrongFootprintTextLayer>(
itFtp.ptr(), it.ptr(), expectedLayer));
}
}
}
}
void PackageCheck::checkPadsClearanceToPads(MsgList& msgs) const {
Length clearance(200000); // 200 µm
Length tolerance(10); // 0.01 µm, to avoid rounding issues
// Check all footprints.
for (auto itFtp = mPackage.getFootprints().begin();
itFtp != mPackage.getFootprints().end(); ++itFtp) {
std::shared_ptr<const Footprint> footprint = itFtp.ptr();
// Check all pads.
for (auto itPad1 = (*itFtp).getPads().begin();
itPad1 != (*itFtp).getPads().end(); ++itPad1) {
std::shared_ptr<const FootprintPad> pad1 = itPad1.ptr();
std::shared_ptr<const PackagePad> pkgPad1 =
mPackage.getPads().find(pad1->getPackagePadUuid());
Path pad1Path = pad1->getOutline(clearance - tolerance);
pad1Path.rotate(pad1->getRotation()).translate(pad1->getPosition());
const QPainterPath pad1PathPx = pad1Path.toQPainterPathPx();
// Compare with all pads *after* pad1 to avoid duplicate messages!
// So, don't initialize the iterator with begin() but with pad1 + 1.
auto itPad2 = itPad1;
for (++itPad2; itPad2 != (*itFtp).getPads().end(); ++itPad2) {
std::shared_ptr<const FootprintPad> pad2 = itPad2.ptr();
std::shared_ptr<const PackagePad> pkgPad2 =
mPackage.getPads().find(pad2->getPackagePadUuid());
Path pad2Path = pad2->getOutline();
pad2Path.rotate(pad2->getRotation()).translate(pad2->getPosition());
const QPainterPath pad2PathPx = pad2Path.toQPainterPathPx();
// Only warn if both pads have copper on the same board side.
if ((pad1->getBoardSide() == pad2->getBoardSide()) ||
(pad1->getBoardSide() == FootprintPad::BoardSide::THT) ||
(pad2->getBoardSide() == FootprintPad::BoardSide::THT)) {
// Only warn if both pads have different net signal, or one of them
// is unconnected (an unconnected pad is considered as a different
// net signal).
if (pad1->getPackagePadUuid() != pad2->getPackagePadUuid()) {
// Now check if the clearance is really too small.
if (pad1PathPx.intersects(pad2PathPx)) {
msgs.append(std::make_shared<MsgPadClearanceViolation>(
footprint, pad1, pkgPad1 ? *pkgPad1->getName() : QString(),
pad2, pkgPad2 ? *pkgPad2->getName() : QString(), clearance));
}
}
}
}
}
}
}
void PackageCheck::checkPadsClearanceToPlacement(MsgList& msgs) const {
for (auto itFtp = mPackage.getFootprints().begin();
itFtp != mPackage.getFootprints().end(); ++itFtp) {
std::shared_ptr<const Footprint> footprint = itFtp.ptr();
QPainterPath topPlacement;
QPainterPath botPlacement;
for (const Polygon& polygon : footprint->getPolygons()) {
QPen pen(Qt::NoPen);
if (polygon.getLineWidth() > 0) {
pen.setStyle(Qt::SolidLine);
pen.setWidthF(polygon.getLineWidth()->toPx());
}
QBrush brush(Qt::NoBrush);
if (polygon.isFilled() && polygon.getPath().isClosed()) {
brush.setStyle(Qt::SolidPattern);
}
QPainterPath area = Toolbox::shapeFromPath(
polygon.getPath().toQPainterPathPx(), pen, brush);
if (polygon.getLayerName() == GraphicsLayer::sTopPlacement) {
topPlacement.addPath(area);
} else if (polygon.getLayerName() == GraphicsLayer::sBotPlacement) {
botPlacement.addPath(area);
}
}
for (auto it = (*itFtp).getPads().begin(); it != (*itFtp).getPads().end();
++it) {
std::shared_ptr<const FootprintPad> pad = it.ptr();
std::shared_ptr<const PackagePad> pkgPad =
mPackage.getPads().find(pad->getPackagePadUuid());
Length clearance(150000); // 150 µm
Length tolerance(10); // 0.01 µm, to avoid rounding issues
Path stopMaskPath = pad->getOutline(clearance - tolerance);
stopMaskPath.rotate(pad->getRotation()).translate(pad->getPosition());
QPainterPath stopMask = stopMaskPath.toQPainterPathPx();
if (pad->isOnLayer(GraphicsLayer::sTopCopper) &&
stopMask.intersects(topPlacement)) {
msgs.append(std::make_shared<MsgPadOverlapsWithPlacement>(
footprint, pad, pkgPad ? *pkgPad->getName() : QString(),
clearance));
} else if (pad->isOnLayer(GraphicsLayer::sBotCopper) &&
stopMask.intersects(botPlacement)) {
msgs.append(std::make_shared<MsgPadOverlapsWithPlacement>(
footprint, pad, pkgPad ? *pkgPad->getName() : QString(),
clearance));
}
}
}
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb
c136b73e0This file is pinned to the selected release. Download the original to open it in your design tool.