Loading files
2955876e1This 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 "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 {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
BoardGraphicsScene::BoardGraphicsScene(
Board& board, const GraphicsLayerList& layers,
std::shared_ptr<const QSet<const NetSignal*>> highlightedNetSignals,
QObject* parent) noexcept
: GraphicsScene(parent),
mBoard(board),
mLayers(layers),
mHighlightedNetSignals(highlightedNetSignals) {
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);
}
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::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);
}
}
void BoardGraphicsScene::updateHighlightedNetSignals() noexcept {
foreach (auto item, mPads) {
item->updateHighlightedNetSignals();
}
foreach (auto item, mVias) {
item->update();
}
foreach (auto item, mPads) {
item->update();
}
foreach (auto item, mNetLines) {
item->update();
}
foreach (auto item, mPlanes) {
item->update();
}
foreach (auto item, mAirWires) {
item->update();
}
}
qreal BoardGraphicsScene::getZValueOfCopperLayer(const Layer& layer) noexcept {
if (layer.isTop()) {
return ZValue_CopperTop;
} else if (layer.isBottom()) {
return ZValue_CopperBottom;
} else if (layer.isInner()) {
// 0.0 => TOP
// 1.0 => BOTTOM
const qreal delta = static_cast<qreal>(layer.getCopperNumber()) / 100.0;
return (static_cast<int>(ZValue_InnerTop) - delta);
} else {
return ZValue_Default;
}
}
/*******************************************************************************
* Private Methods
******************************************************************************/
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);
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, mHighlightedNetSignals);
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, mHighlightedNetSignals);
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);
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, mHighlightedNetSignals);
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, mHighlightedNetSignals);
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);
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);
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);
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, mHighlightedNetSignals);
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