Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
8a92ff80bThis 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 "footprintpainter.h"
#include "../../application.h"
#include "../../export/graphicsexportsettings.h"
#include "../../export/graphicspainter.h"
#include "../../geometry/text.h"
#include "../../utils/transform.h"
#include "../../workspace/colorrole.h"
#include "footprint.h"
#include <QtCore>
#include <QtGui>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
FootprintPainter::FootprintPainter(const Footprint& footprint) noexcept
: mMonospaceFont(Application::getDefaultSansSerifFont()),
mStrokeFont(Application::getDefaultStrokeFont()) {
for (const FootprintPad& pad : footprint.getPads()) {
mPads.append(pad);
}
for (const Polygon& polygon : footprint.getPolygons()) {
mPolygons.append(polygon);
}
for (const Circle& circle : footprint.getCircles()) {
mCircles.append(circle);
}
for (const StrokeText& text : footprint.getStrokeTexts()) {
mStrokeTexts.append(text);
}
for (const Hole& hole : footprint.getHoles()) {
mHoles.append(hole);
}
}
FootprintPainter::~FootprintPainter() noexcept {
}
/*******************************************************************************
* General Methods
******************************************************************************/
void FootprintPainter::paint(
QPainter& painter, const GraphicsExportSettings& settings) const noexcept {
// Determine what to paint on which color layer.
initContentByColor();
// Draw pad circles only if holes are enabled, but pads not.
const bool drawPadHoles =
settings.getPaintOrder().contains(ColorRole::boardHoles().getId()) &&
(!settings.getPaintOrder().contains(ColorRole::boardPads().getId()));
// Draw each layer in reverse order for correct stackup.
GraphicsPainter p(painter);
p.setMinLineWidth(settings.getMinLineWidth());
foreach (const QString& color, settings.getPaintOrder()) {
ColorContent content = mContentByColor.value(color);
// Draw areas.
foreach (const QPainterPath& area, content.areas) {
p.drawPath(area, Length(0), QColor(), settings.getColor(color));
}
// Draw polygons.
foreach (const Polygon& polygon, content.polygons) {
p.drawPolygon(polygon.getPath(), *polygon.getLineWidth(),
settings.getColor(color),
settings.getFillColor(color, polygon.isFilled(),
polygon.isGrabArea()));
}
// Draw circles.
foreach (const Circle& circle, content.circles) {
p.drawCircle(
circle.getCenter(), *circle.getDiameter(), *circle.getLineWidth(),
settings.getColor(color),
settings.getFillColor(color, circle.isFilled(), circle.isGrabArea()));
}
// Draw holes.
foreach (const Hole& hole, content.holes) {
p.drawSlot(*hole.getPath(), hole.getDiameter(), Length(0),
settings.getColor(color), QColor());
}
// Draw pad holes.
if (drawPadHoles) {
foreach (const Hole& hole, content.padHoles) {
p.drawSlot(*hole.getPath(), hole.getDiameter(), Length(0),
settings.getColor(color), QColor());
}
}
// Draw invisible texts to make them selectable and searchable in PDF and
// SVG output.
foreach (const Text& text, content.texts) {
p.drawText(text.getPosition(), text.getRotation(), *text.getHeight(),
text.getAlign(), text.getText(), mMonospaceFont,
Qt::transparent, true, settings.getMirror(), false);
}
}
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void FootprintPainter::initContentByColor() const noexcept {
QMutexLocker lock(&mMutex);
if (mContentByColor.isEmpty()) {
// Footprint polygons.
foreach (Polygon polygon, mPolygons) {
const QString role = polygon.getLayer().getColorRole().getId();
mContentByColor[role].polygons.append(polygon);
}
// Footprint circles.
foreach (Circle circle, mCircles) {
const QString role = circle.getLayer().getColorRole().getId();
mContentByColor[role].circles.append(circle);
}
// Footprint holes.
foreach (Hole hole, mHoles) {
mContentByColor[ColorRole::boardHoles().getId()].holes.append(hole);
}
// Footprint pads.
foreach (FootprintPad pad, mPads) {
const Transform transform(pad.getPosition(), pad.getRotation());
const QPainterPath path =
transform.mapPx(pad.getGeometry().toQPainterPathPx());
const QString role = pad.isTht()
? ColorRole::boardPads().getId()
: pad.getSmtLayer().getColorRole().getId();
mContentByColor[role].areas.append(path);
// Also add the holes for THT pads.
for (const PadHole& hole : pad.getHoles()) {
mContentByColor[ColorRole::boardHoles().getId()].padHoles.append(
Hole(hole.getUuid(), hole.getDiameter(),
transform.map(hole.getPath()), MaskConfig::off()));
}
}
// Texts.
foreach (StrokeText text, mStrokeTexts) {
Transform transform(text);
const QString role = text.getLayer().getColorRole().getId();
foreach (Path path, transform.map(text.generatePaths(mStrokeFont))) {
mContentByColor[role].polygons.append(
Polygon(text.getUuid(), text.getLayer(), text.getStrokeWidth(),
false, false, path));
}
Angle rotation = transform.mapNonMirrorable(Angle::deg0());
Alignment align =
text.getMirrored() ? text.getAlign().mirroredV() : text.getAlign();
Length totalHeight = *text.getHeight() + *text.getStrokeWidth();
totalHeight += totalHeight / 2; // Correction factor from TTF to stroke.
Length baseline = totalHeight / 4; // Baseline correction TTF -> stroke.
Point baselineOffset;
if (align.getV() == VAlign::bottom()) {
baselineOffset.setY(-baseline);
} else if (align.getV() == VAlign::top()) {
baselineOffset.setY(baseline);
}
baselineOffset.rotate(rotation);
mContentByColor[role].texts.append(
Text(text.getUuid(), text.getLayer(), text.getText(),
text.getPosition() + baselineOffset, rotation,
PositiveLength(totalHeight), align, true));
}
}
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb