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 "primitivefootprintpadgraphicsitem.h"
#include "graphicslayerlist.h"
#include "origincrossgraphicsitem.h"
#include "primitivepathgraphicsitem.h"
#include <librepcb/core/application.h>
#include <librepcb/core/font/stroketextpathbuilder.h>
#include <librepcb/core/geometry/padgeometry.h>
#include <librepcb/core/types/alignment.h>
#include <librepcb/core/types/angle.h>
#include <librepcb/core/types/layer.h>
#include <librepcb/core/types/length.h>
#include <librepcb/core/types/point.h>
#include <librepcb/core/types/stroketextspacing.h>
#include <librepcb/core/workspace/colorrole.h>
#include <QtCore>
#include <QtWidgets>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
PrimitiveFootprintPadGraphicsItem::PrimitiveFootprintPadGraphicsItem(
const GraphicsLayerList& layers, bool originCrossVisible, int textMinAlpha,
QGraphicsItem* parent) noexcept
: QGraphicsItemGroup(parent),
mLayers(layers),
mMirror(false),
mState(GraphicsLayer::State::Enabled),
mCopperLayer(nullptr),
mOriginCrossGraphicsItem(new OriginCrossGraphicsItem(this)),
mTextGraphicsItem(new PrimitivePathGraphicsItem(this)),
mPathGraphicsItems(),
mOnLayerEditedSlot(*this, &PrimitiveFootprintPadGraphicsItem::layerEdited) {
setFlag(QGraphicsItem::ItemHasNoContents, true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
// Origin cross properties
// Note: Should be smaller than the smallest pad, otherwise it would be
// annoying due to too large grab area.
mOriginCrossGraphicsItem->setSize(UnsignedLength(250000));
if (originCrossVisible) {
mOriginCrossGraphicsItem->setLayer(
mLayers.get(ColorRole::boardReferencesTop()));
}
mOriginCrossGraphicsItem->setZValue(1000);
// text properties
mTextGraphicsItem->setLineWidth(UnsignedLength(100000));
mTextGraphicsItem->setLighterColorsWithMinAlpha(textMinAlpha); // Contrast!
mTextGraphicsItem->setShapeMode(PrimitivePathGraphicsItem::ShapeMode::None);
mTextGraphicsItem->setZValue(500);
}
PrimitiveFootprintPadGraphicsItem::
~PrimitiveFootprintPadGraphicsItem() noexcept {
}
/*******************************************************************************
* Setters
******************************************************************************/
void PrimitiveFootprintPadGraphicsItem::setPosition(
const Point& position) noexcept {
setPos(position.toPxQPointF());
}
void PrimitiveFootprintPadGraphicsItem::setRotation(
const Angle& rotation) noexcept {
mOriginCrossGraphicsItem->setRotation(rotation);
foreach (auto& item, mPathGraphicsItems) {
item.item->setRotation(rotation);
}
// Keep the text always at 0° for readability.
}
void PrimitiveFootprintPadGraphicsItem::setMirrored(bool mirrored) noexcept {
mMirror = mirrored;
foreach (auto& item, mPathGraphicsItems) {
item.item->setMirrored(mirrored);
}
}
void PrimitiveFootprintPadGraphicsItem::setText(const QString& text) noexcept {
const QVector<Path> paths = StrokeTextPathBuilder::build(
Application::getDefaultStrokeFont(), StrokeTextSpacing(),
StrokeTextSpacing(), PositiveLength(1000000), UnsignedLength(100000),
Alignment(HAlign::center(), VAlign::center()), Angle(0), false, text);
mTextGraphicsItem->setPath(Path::toQPainterPathPx(paths, false));
updateTextHeight();
}
void PrimitiveFootprintPadGraphicsItem::setTextMirrored(
bool mirrored) noexcept {
mTextGraphicsItem->setMirrored(mirrored);
}
void PrimitiveFootprintPadGraphicsItem::setLayer(
const ColorRole& role) noexcept {
auto layer = mLayers.get(role);
if (layer != mCopperLayer) {
mCopperLayer = layer;
mTextGraphicsItem->setLineLayer(mCopperLayer);
updateRegisteredLayers();
updatePathLayers();
}
}
void PrimitiveFootprintPadGraphicsItem::setState(
GraphicsLayer::State state) noexcept {
mState = state;
mOriginCrossGraphicsItem->setState(state);
mTextGraphicsItem->setState(state);
foreach (auto& item, mPathGraphicsItems) {
item.item->setState(state);
}
}
void PrimitiveFootprintPadGraphicsItem::setGeometries(
const QHash<const Layer*, QList<PadGeometry> >& geometries,
const Length& clearance) noexcept {
static const QHash<const ColorRole*, float> zValues = {
{&ColorRole::boardSolderPasteBot(), -300},
{&ColorRole::boardStopMaskBot(), -200},
{&ColorRole::boardCopperBot(), -100},
{&ColorRole::boardPads(), 0},
{&ColorRole::boardCopperTop(), 100},
{&ColorRole::boardStopMaskTop(), 200},
{&ColorRole::boardSolderPasteTop(), 300},
};
mShapes.clear();
mShapesBoundingRect = QRectF();
mPathGraphicsItems.clear();
for (auto it = geometries.begin(); it != geometries.end(); it++) {
if (auto layer = mLayers.get(it.key()->getColorRole())) {
const bool isCopperLayer =
(layer == mCopperLayer) || (it.key()->isCopper());
QPainterPath shape;
shape.setFillRule(Qt::WindingFill);
foreach (const PadGeometry& geometry, it.value()) {
shape |= geometry.toQPainterPathPx();
if (isCopperLayer) {
const QPainterPath p = geometry.toFilledQPainterPathPx();
mShapes[layer] |= p;
mShapesBoundingRect |= p.boundingRect();
}
}
auto item = std::make_shared<PrimitivePathGraphicsItem>(this);
item->setRotation(mOriginCrossGraphicsItem->rotation());
item->setMirrored(mMirror);
item->setState(mState);
item->setPath(shape);
item->setShapeMode(
isCopperLayer ? PrimitivePathGraphicsItem::ShapeMode::FilledOutline
: PrimitivePathGraphicsItem::ShapeMode::None);
if (zValues.contains(&layer->getRole())) {
item->setZValue(zValues.value(&layer->getRole()));
} else {
item->setZValue(static_cast<qreal>(it.key()->getCopperNumber()));
}
mPathGraphicsItems.append(PathItem{layer, isCopperLayer, false, item});
if (isCopperLayer && (clearance > 0)) {
foreach (const PadGeometry& geometry, it.value()) {
auto clrItem = std::make_shared<PrimitivePathGraphicsItem>(this);
clrItem->setRotation(mOriginCrossGraphicsItem->rotation());
clrItem->setPath(
geometry.withOffset(clearance).toFilledQPainterPathPx());
clrItem->setShapeMode(PrimitivePathGraphicsItem::ShapeMode::None);
clrItem->setZValue(item->zValue());
mPathGraphicsItems.append(PathItem{layer, true, true, clrItem});
}
}
}
}
updatePathLayers();
updateTextHeight();
updateRegisteredLayers();
}
/*******************************************************************************
* Inherited from QGraphicsItem
******************************************************************************/
QPainterPath PrimitiveFootprintPadGraphicsItem::shape() const noexcept {
Q_ASSERT(mOriginCrossGraphicsItem);
QPainterPath p = mOriginCrossGraphicsItem->shape();
if (mCopperLayer && mCopperLayer->isVisible()) {
for (auto it = mShapes.begin(); it != mShapes.end(); it++) {
if (it.key()->isVisible()) {
QTransform t;
t.rotate(mOriginCrossGraphicsItem->rotation());
if (mMirror) t.scale(qreal(-1), qreal(1));
p |= t.map(it.value());
}
}
}
return p;
}
QVariant PrimitiveFootprintPadGraphicsItem::itemChange(
GraphicsItemChange change, const QVariant& value) noexcept {
if ((change == ItemSelectedHasChanged) && mOriginCrossGraphicsItem &&
mTextGraphicsItem) {
mOriginCrossGraphicsItem->setSelected(value.toBool());
mTextGraphicsItem->setSelected(value.toBool());
foreach (auto& item, mPathGraphicsItems) {
item.item->setSelected(value.toBool());
}
}
return QGraphicsItem::itemChange(change, value);
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void PrimitiveFootprintPadGraphicsItem::layerEdited(
const GraphicsLayer& layer, GraphicsLayer::Event event) noexcept {
Q_UNUSED(layer);
switch (event) {
case GraphicsLayer::Event::VisibleChanged:
case GraphicsLayer::Event::EnabledChanged:
updatePathLayers();
break;
default:
break;
}
}
void PrimitiveFootprintPadGraphicsItem::updatePathLayers() noexcept {
foreach (auto& item, mPathGraphicsItems) {
std::shared_ptr<const GraphicsLayer> layer =
item.isCopper ? mCopperLayer : item.layer;
if ((!item.isClearance) && item.layer->isVisible()) {
item.item->setFillLayer(layer);
item.item->setLineLayer(nullptr);
} else {
item.item->setLineLayer(layer);
item.item->setFillLayer(nullptr);
}
item.item->setSelected(isSelected());
}
}
void PrimitiveFootprintPadGraphicsItem::updateTextHeight() noexcept {
const qreal size =
std::min(mShapesBoundingRect.width(), mShapesBoundingRect.height());
const QRectF textRect = mTextGraphicsItem->boundingRect();
const qreal heightRatio = textRect.height() / size;
const qreal widthRatio = textRect.width() / size;
const qreal ratio = qMax(heightRatio, widthRatio);
mTextGraphicsItem->setScale(0.9 / ratio);
}
void PrimitiveFootprintPadGraphicsItem::updateRegisteredLayers() noexcept {
mOnLayerEditedSlot.detachAll();
if (mCopperLayer) {
mCopperLayer->onEdited.attach(mOnLayerEditedSlot);
}
foreach (auto& item, mPathGraphicsItems) {
if (item.layer != mCopperLayer) {
item.layer->onEdited.attach(mOnLayerEditedSlot);
}
}
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb
c730ec352This file is pinned to the selected release. Download the original to open it in your design tool.