File indexing completed on 2025-01-26 03:34:13

0001 /*
0002     File                 : Plot.h
0003     Project              : LabPlot
0004     Description          : Base class for all plots like scatter plot, box plot, etc.
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2020-2023 Alexander Semke <alexander.semke@web.de>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "Plot.h"
0011 #include "PlotPrivate.h"
0012 #include "backend/lib/commandtemplates.h"
0013 #include "backend/lib/trace.h"
0014 #include "backend/worksheet/Background.h"
0015 
0016 #include <QGraphicsSceneContextMenuEvent>
0017 #include <QMenu>
0018 #include <QPainter>
0019 
0020 /**
0021  * \fn bool Plot::hasData()
0022  * \brief returns \c true if a valid data column is set, returns \c false otherwise.
0023  * Used in CartesianPlot to determine whether the curve needs to be taken into account
0024  * when caclulating the data ranges of the plot area.
0025  */
0026 
0027 Plot::Plot(const QString& name, PlotPrivate* dd, AspectType type)
0028     : WorksheetElement(name, dd, type)
0029     , d_ptr(dd) {
0030 }
0031 
0032 Plot::~Plot() = default;
0033 
0034 // TODO: make this function pure abstract and implement it for all plot types
0035 bool Plot::minMax(const CartesianCoordinateSystem::Dimension, const Range<int>&, Range<double>&, bool) const {
0036     return false;
0037 }
0038 
0039 /*!
0040  * \brief Plot::activatePlot
0041  * Checks if the mousepos distance to the plot is less than @p maxDist
0042  * \p mouseScenePos
0043  * \p maxDist Maximum distance the point lies away from the plot
0044  * \return Returns true if the distance is smaller than maxDist.
0045  */
0046 bool Plot::activatePlot(QPointF mouseScenePos, double maxDist) {
0047     Q_D(Plot);
0048     return d->activatePlot(mouseScenePos, maxDist);
0049 }
0050 
0051 // general
0052 BASIC_SHARED_D_READER_IMPL(Plot, bool, legendVisible, legendVisible)
0053 
0054 STD_SETTER_CMD_IMPL_S(Plot, SetLegendVisible, bool, legendVisible)
0055 void Plot::setLegendVisible(bool visible) {
0056     Q_D(Plot);
0057     if (visible != d->legendVisible)
0058         exec(new PlotSetLegendVisibleCmd(d, visible, ki18n("%1: legend visibility changed")));
0059 }
0060 
0061 // ##############################################################################
0062 // ####################### Private implementation ###############################
0063 // ##############################################################################
0064 PlotPrivate::PlotPrivate(Plot* owner)
0065     : WorksheetElementPrivate(owner)
0066     , q(owner) {
0067 }
0068 
0069 bool PlotPrivate::activatePlot(QPointF mouseScenePos, double /*maxDist*/) {
0070     if (!isVisible())
0071         return false;
0072 
0073     return m_shape.contains(mouseScenePos);
0074 }
0075 
0076 void PlotPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
0077     if (q->activatePlot(event->pos())) {
0078         q->createContextMenu()->exec(event->screenPos());
0079         return;
0080     }
0081     QGraphicsItem::contextMenuEvent(event);
0082 }
0083 
0084 void PlotPrivate::drawFillingPollygon(const QPolygonF& polygon, QPainter* painter, const Background* background) const {
0085     PERFTRACE(name() + QLatin1String(Q_FUNC_INFO));
0086     const QRectF& rect = polygon.boundingRect();
0087 
0088     if (background->type() == Background::Type::Color) {
0089         switch (background->colorStyle()) {
0090         case Background::ColorStyle::SingleColor: {
0091             painter->setBrush(QBrush(background->firstColor()));
0092             break;
0093         }
0094         case Background::ColorStyle::HorizontalLinearGradient: {
0095             QLinearGradient linearGrad(rect.topLeft(), rect.topRight());
0096             linearGrad.setColorAt(0, background->firstColor());
0097             linearGrad.setColorAt(1, background->secondColor());
0098             painter->setBrush(QBrush(linearGrad));
0099             break;
0100         }
0101         case Background::ColorStyle::VerticalLinearGradient: {
0102             QLinearGradient linearGrad(rect.topLeft(), rect.bottomLeft());
0103             linearGrad.setColorAt(0, background->firstColor());
0104             linearGrad.setColorAt(1, background->secondColor());
0105             painter->setBrush(QBrush(linearGrad));
0106             break;
0107         }
0108         case Background::ColorStyle::TopLeftDiagonalLinearGradient: {
0109             QLinearGradient linearGrad(rect.topLeft(), rect.bottomRight());
0110             linearGrad.setColorAt(0, background->firstColor());
0111             linearGrad.setColorAt(1, background->secondColor());
0112             painter->setBrush(QBrush(linearGrad));
0113             break;
0114         }
0115         case Background::ColorStyle::BottomLeftDiagonalLinearGradient: {
0116             QLinearGradient linearGrad(rect.bottomLeft(), rect.topRight());
0117             linearGrad.setColorAt(0, background->firstColor());
0118             linearGrad.setColorAt(1, background->secondColor());
0119             painter->setBrush(QBrush(linearGrad));
0120             break;
0121         }
0122         case Background::ColorStyle::RadialGradient: {
0123             QRadialGradient radialGrad(rect.center(), rect.width() / 2);
0124             radialGrad.setColorAt(0, background->firstColor());
0125             radialGrad.setColorAt(1, background->secondColor());
0126             painter->setBrush(QBrush(radialGrad));
0127             break;
0128         }
0129         }
0130     } else if (background->type() == Background::Type::Image) {
0131         if (!background->fileName().trimmed().isEmpty()) {
0132             QPixmap pix(background->fileName());
0133             switch (background->imageStyle()) {
0134             case Background::ImageStyle::ScaledCropped:
0135                 pix = pix.scaled(rect.size().toSize(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
0136                 painter->setBrush(QBrush(pix));
0137                 painter->setBrushOrigin(pix.size().width() / 2, pix.size().height() / 2);
0138                 break;
0139             case Background::ImageStyle::Scaled:
0140                 pix = pix.scaled(rect.size().toSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0141                 painter->setBrush(QBrush(pix));
0142                 painter->setBrushOrigin(pix.size().width() / 2, pix.size().height() / 2);
0143                 break;
0144             case Background::ImageStyle::ScaledAspectRatio:
0145                 pix = pix.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
0146                 painter->setBrush(QBrush(pix));
0147                 painter->setBrushOrigin(pix.size().width() / 2, pix.size().height() / 2);
0148                 break;
0149             case Background::ImageStyle::Centered: {
0150                 QPixmap backpix(rect.size().toSize());
0151                 backpix.fill();
0152                 QPainter p(&backpix);
0153                 p.drawPixmap(QPointF(0, 0), pix);
0154                 p.end();
0155                 painter->setBrush(QBrush(backpix));
0156                 painter->setBrushOrigin(-pix.size().width() / 2, -pix.size().height() / 2);
0157                 break;
0158             }
0159             case Background::ImageStyle::Tiled:
0160                 painter->setBrush(QBrush(pix));
0161                 break;
0162             case Background::ImageStyle::CenterTiled:
0163                 painter->setBrush(QBrush(pix));
0164                 painter->setBrushOrigin(pix.size().width() / 2, pix.size().height() / 2);
0165             }
0166         }
0167     } else if (background->type() == Background::Type::Pattern)
0168         painter->setBrush(QBrush(background->firstColor(), background->brushStyle()));
0169 
0170     painter->drawPolygon(polygon);
0171 }