Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
94ec4b8feThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* 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 "boardgraphicsscene.h"
#include "../projectcrossprobe.h"
#include "graphicsitems/bgi_airwire.h"
#include "graphicsitems/bgi_device.h"
#include "graphicsitems/bgi_hole.h"
#include "graphicsitems/bgi_netline.h"
#include "graphicsitems/bgi_netpoint.h"
#include "graphicsitems/bgi_pad.h"
#include "graphicsitems/bgi_plane.h"
#include "graphicsitems/bgi_polygon.h"
#include "graphicsitems/bgi_stroketext.h"
#include "graphicsitems/bgi_via.h"
#include "graphicsitems/bgi_zone.h"
#include <librepcb/core/project/board/board.h>
#include <librepcb/core/project/board/items/bi_airwire.h>
#include <librepcb/core/project/board/items/bi_device.h>
#include <librepcb/core/project/board/items/bi_hole.h>
#include <librepcb/core/project/board/items/bi_netline.h>
#include <librepcb/core/project/board/items/bi_netpoint.h>
#include <librepcb/core/project/board/items/bi_netsegment.h>
#include <librepcb/core/project/board/items/bi_pad.h>
#include <librepcb/core/project/board/items/bi_plane.h>
#include <librepcb/core/project/board/items/bi_polygon.h>
#include <librepcb/core/project/board/items/bi_stroketext.h>
#include <librepcb/core/project/board/items/bi_via.h>
#include <librepcb/core/project/board/items/bi_zone.h>
#include <librepcb/core/project/project.h>
#include <librepcb/core/types/layer.h>
#include <QtCore>
#include <QtWidgets>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Class Context
******************************************************************************/
template <typename T>
GraphicsLayer::State BoardGraphicsScene::Context::getLayerState(
bool highlight, const T* obj) const noexcept {
if (highlight) {
// Highlight has always priority.
return GraphicsLayer::State::Highlighted;
} else if (!crossProbe->isActive()) {
// Cross-probing not active -> normal enabled state.
return GraphicsLayer::State::Enabled;
}
const bool probed = crossProbe->isProbed(obj);
if (crossProbe->isCrossProbed(tab)) {
// Cross-probing from other tab -> highlight probed object, disable others.
return probed ? GraphicsLayer::State::Highlighted
: GraphicsLayer::State::Disabled;
} else {
// Self-probing from this tab -> conditional probed state depending on tool.
return probed ? selfProbedState : GraphicsLayer::State::Enabled;
}
}
template GraphicsLayer::State BoardGraphicsScene::Context::getLayerState(
bool highlight, const NetSignal* obj) const noexcept;
template GraphicsLayer::State BoardGraphicsScene::Context::getLayerState(
bool highlight, const ComponentInstance* obj) const noexcept;
template GraphicsLayer::State BoardGraphicsScene::Context::getLayerState(
bool highlight, const ComponentSignalInstance* obj) const noexcept;
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
BoardGraphicsScene::BoardGraphicsScene(Board& board,
const GraphicsLayerList& layers,
std::shared_ptr<Context> context,
QObject* parent) noexcept
: GraphicsScene(parent), mBoard(board), mLayers(layers), mContext(context) {
foreach (BI_Device* obj, mBoard.getDeviceInstances()) {
addDevice(*obj);
}
foreach (BI_NetSegment* obj, mBoard.getNetSegments()) {
addNetSegment(*obj);
}
foreach (BI_Plane* obj, mBoard.getPlanes()) {
addPlane(*obj);
}
foreach (BI_Zone* obj, mBoard.getZones()) {
addZone(*obj);
}
foreach (BI_Polygon* obj, mBoard.getPolygons()) {
addPolygon(*obj);
}
foreach (BI_StrokeText* obj, mBoard.getStrokeTexts()) {
addStrokeText(*obj);
}
foreach (BI_Hole* obj, mBoard.getHoles()) {
addHole(*obj);
}
foreach (BI_AirWire* obj, mBoard.getAirWires()) {
addAirWire(*obj);
}
connect(&mBoard, &Board::deviceAdded, this, &BoardGraphicsScene::addDevice);
connect(&mBoard, &Board::deviceRemoved, this,
&BoardGraphicsScene::removeDevice);
connect(&mBoard, &Board::netSegmentAdded, this,
&BoardGraphicsScene::addNetSegment);
connect(&mBoard, &Board::netSegmentRemoved, this,
&BoardGraphicsScene::removeNetSegment);
connect(&mBoard, &Board::planeAdded, this, &BoardGraphicsScene::addPlane);
connect(&mBoard, &Board::planeRemoved, this,
&BoardGraphicsScene::removePlane);
connect(&mBoard, &Board::zoneAdded, this, &BoardGraphicsScene::addZone);
connect(&mBoard, &Board::zoneRemoved, this, &BoardGraphicsScene::removeZone);
connect(&mBoard, &Board::polygonAdded, this, &BoardGraphicsScene::addPolygon);
connect(&mBoard, &Board::polygonRemoved, this,
&BoardGraphicsScene::removePolygon);
connect(&mBoard, &Board::strokeTextAdded, this,
&BoardGraphicsScene::addStrokeText);
connect(&mBoard, &Board::strokeTextRemoved, this,
&BoardGraphicsScene::removeStrokeText);
connect(&mBoard, &Board::holeAdded, this, &BoardGraphicsScene::addHole);
connect(&mBoard, &Board::holeRemoved, this, &BoardGraphicsScene::removeHole);
connect(&mBoard, &Board::airWireAdded, this, &BoardGraphicsScene::addAirWire);
connect(&mBoard, &Board::airWireRemoved, this,
&BoardGraphicsScene::removeAirWire);
connect(mContext->crossProbe.get(), &ProjectCrossProbe::modified, this,
&BoardGraphicsScene::updateContext, Qt::QueuedConnection);
}
BoardGraphicsScene::~BoardGraphicsScene() noexcept {
// Need to remove all graphics items from scene in case some shared pointers
// are still hold outside of this class.
foreach (BI_Device* obj, mDevices.keys()) {
removeDevice(*obj);
}
foreach (BI_Pad* obj, mPads.keys()) {
removePad(*obj);
}
foreach (BI_Via* obj, mVias.keys()) {
removeVia(*obj);
}
foreach (BI_NetLine* obj, mNetLines.keys()) {
removeNetLine(*obj);
}
foreach (BI_NetPoint* obj, mNetPoints.keys()) {
removeNetPoint(*obj);
}
foreach (BI_Plane* obj, mPlanes.keys()) {
removePlane(*obj);
}
foreach (BI_Zone* obj, mZones.keys()) {
removeZone(*obj);
}
foreach (BI_Polygon* obj, mPolygons.keys()) {
removePolygon(*obj);
}
foreach (BI_StrokeText* obj, mStrokeTexts.keys()) {
removeStrokeText(*obj);
}
foreach (BI_Hole* obj, mHoles.keys()) {
removeHole(*obj);
}
foreach (BI_AirWire* obj, mAirWires.keys()) {
removeAirWire(*obj);
}
}
/*******************************************************************************
* General Methods
******************************************************************************/
void BoardGraphicsScene::setSelfProbedState(
GraphicsLayer::State state) noexcept {
mContext->selfProbedState = state;
}
void BoardGraphicsScene::setFlipped(bool flip) noexcept {
if (flip != mContext->flipView) {
mContext->flipView = flip;
updateContext();
}
}
void BoardGraphicsScene::selectAll() noexcept {
foreach (auto item, mDevices) {
item->setSelected(true);
}
foreach (auto item, mPads) {
item->setSelected(true);
}
foreach (auto item, mNetPoints) {
item->setSelected(true);
}
foreach (auto item, mNetLines) {
item->setSelected(true);
}
foreach (auto item, mVias) {
item->setSelected(true);
}
foreach (auto item, mPlanes) {
item->setSelected(true);
}
foreach (auto item, mZones) {
item->setSelected(true);
}
foreach (auto item, mPolygons) {
item->setSelected(true);
}
foreach (auto item, mStrokeTexts) {
item->setSelected(true);
}
foreach (auto item, mHoles) {
item->setSelected(true);
}
}
void BoardGraphicsScene::selectItemsInRect(const Point& p1,
const Point& p2) noexcept {
GraphicsScene::setSelectionRect(p1, p2);
const QRectF rectPx = QRectF(p1.toPxQPointF(), p2.toPxQPointF()).normalized();
// For now we select the shole device if one of its pads is within the
// selection rect, see https://github.com/LibrePCB/LibrePCB/pull/1533.
// In case this turns out to be problematic in some cases, we should
// reconsider this.
QSet<std::shared_ptr<BGI_Device>> selectedDevices;
foreach (auto item, mPads) {
if (auto device = item->getDeviceGraphicsItem().lock()) {
if ((!selectedDevices.contains(device)) &&
item->mapToScene(item->shape()).intersects(rectPx)) {
selectedDevices.insert(device);
}
} else {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
}
foreach (auto item, mDevices) {
item->setSelected(selectedDevices.contains(item) ||
item->mapToScene(item->shape()).intersects(rectPx));
}
foreach (auto item, mVias) {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
foreach (auto item, mNetPoints) {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
foreach (auto item, mNetLines) {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
foreach (auto item, mPlanes) {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
foreach (auto item, mZones) {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
foreach (auto item, mPolygons) {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
foreach (auto item, mStrokeTexts) {
if (auto device = item->getDeviceGraphicsItem().lock()) {
item->setSelected(device->isSelected());
} else {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
}
foreach (auto item, mHoles) {
item->setSelected(item->mapToScene(item->shape()).intersects(rectPx));
}
}
void BoardGraphicsScene::selectNetSegment(BI_NetSegment& netSegment) noexcept {
foreach (BI_Via* obj, netSegment.getVias()) {
if (auto item = mVias.value(obj)) {
item->setSelected(true);
}
}
// Not sure if we should also select the pads. For example, when invoking the
// "remove whole trace" in the board editor, I'd expect only traces will be
// deleted but the pads should be kept.
// foreach (BI_Pad* obj, netSegment.getPads()) {
// if (auto item = mPads.value(obj)) {
// item->setSelected(true);
// }
// }
foreach (BI_NetPoint* obj, netSegment.getNetPoints()) {
if (auto item = mNetPoints.value(obj)) {
item->setSelected(true);
}
}
foreach (BI_NetLine* obj, netSegment.getNetLines()) {
if (auto item = mNetLines.value(obj)) {
item->setSelected(true);
}
}
}
void BoardGraphicsScene::clearSelection() noexcept {
foreach (auto item, mDevices) {
item->setSelected(false);
}
foreach (auto item, mPads) {
item->setSelected(false);
}
foreach (auto item, mNetPoints) {
item->setSelected(false);
}
foreach (auto item, mNetLines) {
item->setSelected(false);
}
foreach (auto item, mVias) {
item->setSelected(false);
}
foreach (auto item, mPlanes) {
item->setSelected(false);
}
foreach (auto item, mZones) {
item->setSelected(false);
}
foreach (auto item, mPolygons) {
item->setSelected(false);
}
foreach (auto item, mStrokeTexts) {
item->setSelected(false);
}
foreach (auto item, mHoles) {
item->setSelected(false);
}
}
qreal BoardGraphicsScene::getZValueOfCopperLayer(const Layer& layer,
bool flip) noexcept {
if (layer.isTop()) {
return getFlippedZValue(ZValue_CopperTop, flip);
} else if (layer.isBottom()) {
return getFlippedZValue(ZValue_CopperBottom, flip);
} else if (layer.isInner()) {
// 0.0 => TOP
// 1.0 => BOTTOM
const qreal delta = static_cast<qreal>(layer.getCopperNumber()) / 100.0;
return flip ? (static_cast<int>(ZValue_InnerBottom) + delta)
: (static_cast<int>(ZValue_InnerTop) - delta);
} else {
return ZValue_Default;
}
}
qreal BoardGraphicsScene::getFlippedZValue(ItemZValue value,
bool flip) noexcept {
if (flip && (value >= ZValue_Bottom) && (value <= ZValue_Top)) {
return static_cast<qreal>(ZValue_Top + ZValue_Bottom - value);
} else {
return static_cast<qreal>(value);
}
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void BoardGraphicsScene::updateContext() noexcept {
foreach (auto item, mDevices) {
item->updateContext();
}
foreach (auto item, mPads) {
item->updateContext();
}
foreach (auto item, mVias) {
item->updateContext();
}
foreach (auto item, mPads) {
item->updateContext();
}
foreach (auto item, mNetPoints) {
item->updateContext();
}
foreach (auto item, mNetLines) {
item->updateContext();
}
foreach (auto item, mPlanes) {
item->updateContext();
}
foreach (auto item, mZones) {
item->updateContext();
}
foreach (auto item, mAirWires) {
item->updateContext();
}
foreach (auto item, mPolygons) {
item->updateContext();
}
foreach (auto item, mStrokeTexts) {
item->updateContext();
}
}
void BoardGraphicsScene::addDevice(BI_Device& device) noexcept {
Q_ASSERT(!mDevices.contains(&device));
std::shared_ptr<BGI_Device> item =
std::make_shared<BGI_Device>(device, mLayers, mContext);
addItem(*item);
mDevices.insert(&device, item);
foreach (BI_Pad* obj, device.getPads()) {
addPad(*obj, item);
}
foreach (BI_StrokeText* obj, device.getStrokeTexts()) {
addStrokeText(*obj);
}
connect(&device, &BI_Device::strokeTextAdded, this,
&BoardGraphicsScene::addStrokeText);
connect(&device, &BI_Device::strokeTextRemoved, this,
&BoardGraphicsScene::removeStrokeText);
}
void BoardGraphicsScene::removeDevice(BI_Device& device) noexcept {
disconnect(&device, &BI_Device::strokeTextAdded, this,
&BoardGraphicsScene::addStrokeText);
disconnect(&device, &BI_Device::strokeTextRemoved, this,
&BoardGraphicsScene::removeStrokeText);
foreach (BI_StrokeText* obj, device.getStrokeTexts()) {
removeStrokeText(*obj);
}
foreach (BI_Pad* obj, device.getPads()) {
removePad(*obj);
}
if (std::shared_ptr<BGI_Device> item = mDevices.take(&device)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addPad(BI_Pad& pad,
std::weak_ptr<BGI_Device> device) noexcept {
Q_ASSERT(!mPads.contains(&pad));
std::shared_ptr<BGI_Pad> item =
std::make_shared<BGI_Pad>(pad, device, mLayers, mContext);
addItem(*item);
mPads.insert(&pad, item);
}
void BoardGraphicsScene::removePad(BI_Pad& pad) noexcept {
if (std::shared_ptr<BGI_Pad> item = mPads.take(&pad)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addNetSegment(BI_NetSegment& netSegment) noexcept {
foreach (BI_Pad* obj, netSegment.getPads()) {
addPad(*obj, std::weak_ptr<BGI_Device>());
}
foreach (BI_Via* obj, netSegment.getVias()) {
addVia(*obj);
}
foreach (BI_NetPoint* obj, netSegment.getNetPoints()) {
addNetPoint(*obj);
}
foreach (BI_NetLine* obj, netSegment.getNetLines()) {
addNetLine(*obj);
}
connect(&netSegment, &BI_NetSegment::elementsAdded, this,
&BoardGraphicsScene::addNetSegmentElements);
connect(&netSegment, &BI_NetSegment::elementsRemoved, this,
&BoardGraphicsScene::removeNetSegmentElements);
}
void BoardGraphicsScene::removeNetSegment(BI_NetSegment& netSegment) noexcept {
disconnect(&netSegment, &BI_NetSegment::elementsAdded, this,
&BoardGraphicsScene::addNetSegmentElements);
disconnect(&netSegment, &BI_NetSegment::elementsRemoved, this,
&BoardGraphicsScene::removeNetSegmentElements);
foreach (BI_NetLine* obj, netSegment.getNetLines()) {
removeNetLine(*obj);
}
foreach (BI_NetPoint* obj, netSegment.getNetPoints()) {
removeNetPoint(*obj);
}
foreach (BI_Via* obj, netSegment.getVias()) {
removeVia(*obj);
}
foreach (BI_Pad* obj, netSegment.getPads()) {
removePad(*obj);
}
}
void BoardGraphicsScene::addNetSegmentElements(
const QList<BI_Pad*>& pads, const QList<BI_Via*>& vias,
const QList<BI_NetPoint*>& netPoints,
const QList<BI_NetLine*>& netLines) noexcept {
foreach (BI_Pad* obj, pads) {
addPad(*obj, std::weak_ptr<BGI_Device>());
}
foreach (BI_Via* obj, vias) {
addVia(*obj);
}
foreach (BI_NetPoint* obj, netPoints) {
addNetPoint(*obj);
}
foreach (BI_NetLine* obj, netLines) {
addNetLine(*obj);
}
}
void BoardGraphicsScene::removeNetSegmentElements(
const QList<BI_Pad*>& pads, const QList<BI_Via*>& vias,
const QList<BI_NetPoint*>& netPoints,
const QList<BI_NetLine*>& netLines) noexcept {
foreach (BI_NetLine* obj, netLines) {
removeNetLine(*obj);
}
foreach (BI_NetPoint* obj, netPoints) {
removeNetPoint(*obj);
}
foreach (BI_Via* obj, vias) {
removeVia(*obj);
}
foreach (BI_Pad* obj, pads) {
removePad(*obj);
}
}
void BoardGraphicsScene::addVia(BI_Via& via) noexcept {
Q_ASSERT(!mVias.contains(&via));
std::shared_ptr<BGI_Via> item =
std::make_shared<BGI_Via>(via, mLayers, mContext);
addItem(*item);
mVias.insert(&via, item);
}
void BoardGraphicsScene::removeVia(BI_Via& via) noexcept {
if (std::shared_ptr<BGI_Via> item = mVias.take(&via)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addNetPoint(BI_NetPoint& netPoint) noexcept {
Q_ASSERT(!mNetPoints.contains(&netPoint));
std::shared_ptr<BGI_NetPoint> item =
std::make_shared<BGI_NetPoint>(netPoint, mLayers, mContext);
addItem(*item);
mNetPoints.insert(&netPoint, item);
}
void BoardGraphicsScene::removeNetPoint(BI_NetPoint& netPoint) noexcept {
if (std::shared_ptr<BGI_NetPoint> item = mNetPoints.take(&netPoint)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addNetLine(BI_NetLine& netLine) noexcept {
Q_ASSERT(!mNetLines.contains(&netLine));
std::shared_ptr<BGI_NetLine> item =
std::make_shared<BGI_NetLine>(netLine, mLayers, mContext);
addItem(*item);
mNetLines.insert(&netLine, item);
}
void BoardGraphicsScene::removeNetLine(BI_NetLine& netLine) noexcept {
if (std::shared_ptr<BGI_NetLine> item = mNetLines.take(&netLine)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addPlane(BI_Plane& plane) noexcept {
Q_ASSERT(!mPlanes.contains(&plane));
std::shared_ptr<BGI_Plane> item =
std::make_shared<BGI_Plane>(plane, mLayers, mContext);
addItem(*item);
mPlanes.insert(&plane, item);
}
void BoardGraphicsScene::removePlane(BI_Plane& plane) noexcept {
if (std::shared_ptr<BGI_Plane> item = mPlanes.take(&plane)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addZone(BI_Zone& zone) noexcept {
Q_ASSERT(!mZones.contains(&zone));
std::shared_ptr<BGI_Zone> item =
std::make_shared<BGI_Zone>(zone, mLayers, mContext);
addItem(*item);
mZones.insert(&zone, item);
}
void BoardGraphicsScene::removeZone(BI_Zone& zone) noexcept {
if (std::shared_ptr<BGI_Zone> item = mZones.take(&zone)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addPolygon(BI_Polygon& polygon) noexcept {
Q_ASSERT(!mPolygons.contains(&polygon));
std::shared_ptr<BGI_Polygon> item =
std::make_shared<BGI_Polygon>(polygon, mLayers, mContext);
addItem(*item);
mPolygons.insert(&polygon, item);
}
void BoardGraphicsScene::removePolygon(BI_Polygon& polygon) noexcept {
if (std::shared_ptr<BGI_Polygon> item = mPolygons.take(&polygon)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addStrokeText(BI_StrokeText& text) noexcept {
Q_ASSERT(!mStrokeTexts.contains(&text));
std::shared_ptr<BGI_StrokeText> item = std::make_shared<BGI_StrokeText>(
text, mDevices.value(text.getDevice()), mLayers, mContext);
addItem(*item);
mStrokeTexts.insert(&text, item);
}
void BoardGraphicsScene::removeStrokeText(BI_StrokeText& text) noexcept {
if (std::shared_ptr<BGI_StrokeText> item = mStrokeTexts.take(&text)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addHole(BI_Hole& hole) noexcept {
Q_ASSERT(!mHoles.contains(&hole));
std::shared_ptr<BGI_Hole> item = std::make_shared<BGI_Hole>(hole, mLayers);
addItem(*item);
mHoles.insert(&hole, item);
}
void BoardGraphicsScene::removeHole(BI_Hole& hole) noexcept {
if (std::shared_ptr<BGI_Hole> item = mHoles.take(&hole)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
void BoardGraphicsScene::addAirWire(BI_AirWire& airWire) noexcept {
Q_ASSERT(!mAirWires.contains(&airWire));
std::shared_ptr<BGI_AirWire> item =
std::make_shared<BGI_AirWire>(airWire, mLayers, mContext);
addItem(*item);
mAirWires.insert(&airWire, item);
}
void BoardGraphicsScene::removeAirWire(BI_AirWire& airWire) noexcept {
if (std::shared_ptr<BGI_AirWire> item = mAirWires.take(&airWire)) {
removeItem(*item);
} else {
Q_ASSERT(false);
}
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb