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 "schematicpainter.h"
#include "../../application.h"
#include "../../export/graphicsexportsettings.h"
#include "../../export/graphicspainter.h"
#include "../../library/sym/symbol.h"
#include "../../workspace/colorrole.h"
#include "../circuit/bus.h"
#include "../circuit/netsignal.h"
#include "items/si_busjunction.h"
#include "items/si_buslabel.h"
#include "items/si_busline.h"
#include "items/si_bussegment.h"
#include "items/si_image.h"
#include "items/si_netlabel.h"
#include "items/si_netline.h"
#include "items/si_netpoint.h"
#include "items/si_netsegment.h"
#include "items/si_polygon.h"
#include "items/si_symbol.h"
#include "items/si_symbolpin.h"
#include "items/si_text.h"
#include "schematic.h"
#include <QtCore>
#include <QtGui>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
SchematicPainter::SchematicPainter(const Schematic& schematic,
QStringList* errors, bool thumbnail) noexcept
: mDefaultFont(Application::getDefaultSansSerifFont()),
mNetLabelFont(Application::getDefaultMonospaceFont()) {
mNetLabelFont.setPixelSize(4);
foreach (const SI_Symbol* symbol, schematic.getSymbols()) {
Symbol sym;
sym.transform = Transform(*symbol);
foreach (const SI_SymbolPin* pin, symbol->getPins()) {
sym.pins.append(Pin{
pin->getLibPin().getPosition(),
pin->getLibPin().getRotation(),
pin->getLibPin().getLength(),
pin->getName(),
pin->getNumbersTruncated(),
pin->getLibPin().getNamePosition(),
pin->getLibPin().getNameRotation(),
pin->getLibPin().getNameHeight(),
pin->getLibPin().getNameAlignment(),
pin->getNumbersPosition(),
pin->getNumbersAlignment(),
pin->hasError(),
});
if (pin->isVisibleJunction() && (!thumbnail)) {
mNetJunctions.append(pin->getPosition());
}
}
for (const Polygon& polygon : symbol->getLibSymbol().getPolygons()) {
sym.polygons.append(polygon);
}
for (const Circle& circle : symbol->getLibSymbol().getCircles()) {
sym.circles.append(circle);
}
if (!thumbnail) {
for (const SI_Text* text : symbol->getTexts()) {
Text copy(text->getTextObj());
copy.setText(text->getText()); // Memorize substituted text.
mTexts.append(copy);
}
}
for (const Image& image : symbol->getLibSymbol().getImages()) {
try {
if (!sym.imageFiles.contains(*image.getFileName())) {
const QByteArray content = symbol->getLibSymbol().getDirectory().read(
*image.getFileName()); // can throw
QString error = "Unknown error.";
if (auto pix =
Image::tryLoad(content, image.getFileExtension(), &error)) {
sym.imageFiles.insert(*image.getFileName(), *pix);
} else {
throw RuntimeError(__FILE__, __LINE__,
QString("Failed to load image '%1': %2")
.arg(*image.getFileName(), error));
}
}
sym.images.append(image);
} catch (const Exception& e) {
if (errors) {
errors->append(e.getMsg());
}
}
}
mSymbols.append(sym);
}
foreach (const SI_Polygon* polygon, schematic.getPolygons()) {
mPolygons.append(polygon->getPolygon());
}
if (!thumbnail) {
foreach (const SI_Text* text, schematic.getTexts()) {
Text copy(text->getTextObj());
copy.setText(text->getText()); // Memorize substituted text.
mTexts.append(copy);
}
}
foreach (const SI_Image* image, schematic.getImages()) {
try {
const Image& img = *image->getImage();
if (!mImageFiles.contains(*img.getFileName())) {
const QByteArray content =
schematic.getDirectory().read(*img.getFileName()); // can throw
QString error = "Unknown error.";
if (auto pix =
Image::tryLoad(content, img.getFileExtension(), &error)) {
mImageFiles.insert(*img.getFileName(), *pix);
} else {
throw RuntimeError(__FILE__, __LINE__,
QString("Failed to load image '%1': %2")
.arg(*img.getFileName(), error));
}
}
mImages.append(img);
} catch (const Exception& e) {
if (errors) {
errors->append(e.getMsg());
}
}
}
foreach (const SI_BusSegment* segment, schematic.getBusSegments()) {
if (!thumbnail) {
for (const SI_BusLabel* l : segment->getLabels()) {
mBusLabels.append(Label{
l->getPosition(),
l->getRotation(),
l->getMirrored(),
*l->getBusSegment().getBus().getName(),
});
}
for (const SI_BusJunction* p : segment->getJunctions()) {
if (p->isVisibleJunction()) {
mBusJunctions.append(p->getPosition());
}
}
}
for (const SI_BusLine* l : segment->getLines()) {
mBusLines.append(Line{l->getP1().getPosition(), l->getP2().getPosition(),
l->getWidth()});
}
}
foreach (const SI_NetSegment* segment, schematic.getNetSegments()) {
if (!thumbnail) {
for (const SI_NetLabel* netlabel : segment->getNetLabels()) {
mNetLabels.append(Label{
netlabel->getPosition(),
netlabel->getRotation(),
netlabel->getMirrored(),
*netlabel->getNetSegment().getNetSignal().getName(),
});
}
for (const SI_NetPoint* netpoint : segment->getNetPoints()) {
if (netpoint->isVisibleJunction()) {
mNetJunctions.append(netpoint->getPosition());
}
}
}
for (const SI_NetLine* netline : segment->getNetLines()) {
mNetLines.append(Line{netline->getP1().getPosition(),
netline->getP2().getPosition(),
netline->getWidth()});
}
}
}
SchematicPainter::~SchematicPainter() noexcept {
}
/*******************************************************************************
* General Methods
******************************************************************************/
void SchematicPainter::paint(
QPainter& painter, const GraphicsExportSettings& settings) const noexcept {
GraphicsPainter p(painter);
p.setMinLineWidth(settings.getMinLineWidth());
// Draw Symbols.
foreach (const Symbol& symbol, mSymbols) {
// Helper to draw grab areas first to make them appearing behind every other
// graphics item. Otherwise they might completely cover (hide) other items.
// Images shall be drawn in front of filled polygons/circles, but behind
// non-filled polygons/circles.
enum class ShapeType { GrabArea, Filled, Others };
auto doDraw = [](ShapeType type, bool grabArea, bool filled) {
if (type == ShapeType::GrabArea) {
return grabArea && (!filled);
} else if (type == ShapeType::Filled) {
return filled;
} else {
return (!grabArea) && (!filled);
}
};
auto drawShapes = [&symbol, &settings, &p, &doDraw](ShapeType type) {
// Draw Polygons.
foreach (const Polygon& polygon, symbol.polygons) {
if (doDraw(type, polygon.isGrabArea(), polygon.isFilled())) {
const QString role = polygon.getLayer().getColorRole().getId();
p.drawPolygon(symbol.transform.map(polygon.getPath()),
*polygon.getLineWidth(), settings.getColor(role),
settings.getFillColor(role, polygon.isFilled(),
polygon.isGrabArea()));
}
}
// Draw Circles.
foreach (const Circle& circle, symbol.circles) {
if (doDraw(type, circle.isGrabArea(), circle.isFilled())) {
const QString role = circle.getLayer().getColorRole().getId();
p.drawCircle(symbol.transform.map(circle.getCenter()),
*circle.getDiameter(), *circle.getLineWidth(),
settings.getColor(role),
settings.getFillColor(role, circle.isFilled(),
circle.isGrabArea()));
}
}
};
// Draw grab-area shapes.
drawShapes(ShapeType::GrabArea);
// Draw filled shaped.
drawShapes(ShapeType::Filled);
// Draw images.
// Note that if the symbol is mirrored, we do not mirror the image but
// just fit it into its mirrored bounding box because most images are
// not mirrorable as they might contain text. Most symbol images will
// probably be company logos (or other logos) anyway which are not
// intended to be mirrored.
foreach (const Image& image, symbol.images) {
Point pos = symbol.transform.map(image.getPosition());
Angle rot = symbol.transform.mapNonMirrorable(image.getRotation());
if (symbol.transform.getMirrored()) {
pos +=
Point(-image.getWidth(), 0)
.rotated(-image.getRotation() + symbol.transform.getRotation());
rot += Angle::deg180();
}
p.drawImage(
pos, rot,
settings.convertImageColors(
symbol.imageFiles.value(*image.getFileName())),
image.getWidth(), image.getHeight(), image.getBorderWidth(),
settings.getColor(ColorRole::schematicImageBorders().getId()));
}
// Draw line shapes (no fill, no grab area).
drawShapes(ShapeType::Others);
// Draw Symbol Pins.
foreach (const Pin& pin, symbol.pins) {
p.drawSymbolPin(symbol.transform.map(pin.position),
symbol.transform.mapNonMirrorable(pin.rotation),
pin.hasError ? (*pin.length / 2) : *pin.length,
settings.getColor(ColorRole::schematicPinLines().getId()),
QColor());
Alignment nameAlignment = pin.nameAlignment;
if (symbol.transform.getMirrored()) {
nameAlignment.mirrorV();
}
p.drawText(
symbol.transform.map(pin.position +
pin.namePosition.rotated(pin.rotation)),
symbol.transform.mapNonMirrorable(pin.rotation + pin.nameRotation),
*pin.nameHeight, nameAlignment, pin.name, mDefaultFont,
settings.getColor(ColorRole::schematicPinNames().getId()), true,
false, true);
const Angle numberRot = symbol.transform.mapNonMirrorable(pin.rotation);
p.drawText(symbol.transform.map(
pin.position + pin.numbersPosition.rotated(pin.rotation)),
numberRot, *SymbolPin::getNumbersHeight(),
pin.numbersAlignment, pin.numbers, mDefaultFont,
settings.getColor(ColorRole::schematicPinNumbers().getId()),
true, false, false);
}
}
// Draw Images.
foreach (const Image& image, mImages) {
p.drawImage(
image.getPosition(), image.getRotation(),
settings.convertImageColors(mImageFiles.value(*image.getFileName())),
image.getWidth(), image.getHeight(), image.getBorderWidth(),
settings.getColor(ColorRole::schematicImageBorders().getId()));
}
// Draw Polygons.
foreach (const Polygon& polygon, mPolygons) {
const QString role = polygon.getLayer().getColorRole().getId();
p.drawPolygon(
polygon.getPath(), *polygon.getLineWidth(), settings.getColor(role),
settings.getFillColor(role, polygon.isFilled(), polygon.isGrabArea()));
}
// Draw Texts.
foreach (const Text& text, mTexts) {
const QString role = text.getLayer().getColorRole().getId();
p.drawText(text.getPosition(), text.getRotation(), *text.getHeight(),
text.getAlign(), text.getText(), mDefaultFont,
settings.getColor(role), true, false, false);
}
// Draw Net Lines.
foreach (const Line& netline, mNetLines) {
p.drawLine(netline.startPosition, netline.endPosition, *netline.width,
settings.getColor(ColorRole::schematicWires().getId()));
}
// Draw Net Junctions.
foreach (const Point& pos, mNetJunctions) {
p.drawNetJunction(pos,
settings.getColor(ColorRole::schematicWires().getId()));
}
// Draw Net Labels.
foreach (const Label& netlabel, mNetLabels) {
p.drawNetLabel(netlabel.position, netlabel.rotation, netlabel.mirrored,
netlabel.text, mNetLabelFont,
settings.getColor(ColorRole::schematicNetLabels().getId()));
}
// Draw Bus Lines.
foreach (const Line& netline, mBusLines) {
p.drawLine(netline.startPosition, netline.endPosition, *netline.width,
settings.getColor(ColorRole::schematicBuses().getId()));
}
// Draw Bus Junctions.
foreach (const Point& pos, mBusJunctions) {
p.drawNetJunction(pos,
settings.getColor(ColorRole::schematicBuses().getId()));
}
// Draw Bus Labels.
foreach (const Label& netlabel, mBusLabels) {
p.drawNetLabel(netlabel.position, netlabel.rotation, netlabel.mirrored,
netlabel.text, mNetLabelFont,
settings.getColor(ColorRole::schematicBusLabels().getId()));
}
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb