File indexing completed on 2024-04-28 03:56:42

0001 /*  -*- C++ -*-
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2003 Jason Harris <kstars@30doradus.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kplotobject.h"
0009 
0010 #include <QDebug>
0011 #include <QPainter>
0012 #include <QtAlgorithms>
0013 
0014 #include "kplotpoint.h"
0015 #include "kplotwidget.h"
0016 
0017 class KPlotObject::Private
0018 {
0019 public:
0020     Private(KPlotObject *qq)
0021         : q(qq)
0022     {
0023     }
0024 
0025     ~Private()
0026     {
0027         qDeleteAll(pList);
0028     }
0029 
0030     KPlotObject *q;
0031 
0032     QList<KPlotPoint *> pList;
0033     PlotTypes type;
0034     PointStyle pointStyle;
0035     double size;
0036     QPen pen, linePen, barPen, labelPen;
0037     QBrush brush, barBrush;
0038 };
0039 
0040 KPlotObject::KPlotObject(const QColor &c, PlotType t, double size, PointStyle ps)
0041     : d(new Private(this))
0042 {
0043     // By default, all pens and brushes are set to the given color
0044     setBrush(c);
0045     setBarBrush(c);
0046     setPen(QPen(brush(), 1));
0047     setLinePen(pen());
0048     setBarPen(pen());
0049     setLabelPen(pen());
0050 
0051     d->type |= t;
0052     setSize(size);
0053     setPointStyle(ps);
0054 }
0055 
0056 KPlotObject::~KPlotObject() = default;
0057 
0058 KPlotObject::PlotTypes KPlotObject::plotTypes() const
0059 {
0060     return d->type;
0061 }
0062 
0063 void KPlotObject::setShowPoints(bool b)
0064 {
0065     if (b) {
0066         d->type |= KPlotObject::Points;
0067     } else {
0068         d->type &= ~KPlotObject::Points;
0069     }
0070 }
0071 
0072 void KPlotObject::setShowLines(bool b)
0073 {
0074     if (b) {
0075         d->type |= KPlotObject::Lines;
0076     } else {
0077         d->type &= ~KPlotObject::Lines;
0078     }
0079 }
0080 
0081 void KPlotObject::setShowBars(bool b)
0082 {
0083     if (b) {
0084         d->type |= KPlotObject::Bars;
0085     } else {
0086         d->type &= ~KPlotObject::Bars;
0087     }
0088 }
0089 
0090 double KPlotObject::size() const
0091 {
0092     return d->size;
0093 }
0094 
0095 void KPlotObject::setSize(double s)
0096 {
0097     d->size = s;
0098 }
0099 
0100 KPlotObject::PointStyle KPlotObject::pointStyle() const
0101 {
0102     return d->pointStyle;
0103 }
0104 
0105 void KPlotObject::setPointStyle(PointStyle p)
0106 {
0107     d->pointStyle = p;
0108 }
0109 
0110 const QPen &KPlotObject::pen() const
0111 {
0112     return d->pen;
0113 }
0114 
0115 void KPlotObject::setPen(const QPen &p)
0116 {
0117     d->pen = p;
0118 }
0119 
0120 const QPen &KPlotObject::linePen() const
0121 {
0122     return d->linePen;
0123 }
0124 
0125 void KPlotObject::setLinePen(const QPen &p)
0126 {
0127     d->linePen = p;
0128 }
0129 
0130 const QPen &KPlotObject::barPen() const
0131 {
0132     return d->barPen;
0133 }
0134 
0135 void KPlotObject::setBarPen(const QPen &p)
0136 {
0137     d->barPen = p;
0138 }
0139 
0140 const QPen &KPlotObject::labelPen() const
0141 {
0142     return d->labelPen;
0143 }
0144 
0145 void KPlotObject::setLabelPen(const QPen &p)
0146 {
0147     d->labelPen = p;
0148 }
0149 
0150 const QBrush KPlotObject::brush() const
0151 {
0152     return d->brush;
0153 }
0154 
0155 void KPlotObject::setBrush(const QBrush &b)
0156 {
0157     d->brush = b;
0158 }
0159 
0160 const QBrush KPlotObject::barBrush() const
0161 {
0162     return d->barBrush;
0163 }
0164 
0165 void KPlotObject::setBarBrush(const QBrush &b)
0166 {
0167     d->barBrush = b;
0168 }
0169 
0170 QList<KPlotPoint *> KPlotObject::points() const
0171 {
0172     return d->pList;
0173 }
0174 
0175 void KPlotObject::addPoint(const QPointF &p, const QString &label, double barWidth)
0176 {
0177     addPoint(new KPlotPoint(p.x(), p.y(), label, barWidth));
0178 }
0179 
0180 void KPlotObject::addPoint(KPlotPoint *p)
0181 {
0182     if (!p) {
0183         return;
0184     }
0185     d->pList.append(p);
0186 }
0187 
0188 void KPlotObject::addPoint(double x, double y, const QString &label, double barWidth)
0189 {
0190     addPoint(new KPlotPoint(x, y, label, barWidth));
0191 }
0192 
0193 void KPlotObject::removePoint(int index)
0194 {
0195     if ((index < 0) || (index >= d->pList.count())) {
0196         // qWarning() << "KPlotObject::removePoint(): index " << index << " out of range!";
0197         return;
0198     }
0199 
0200     d->pList.removeAt(index);
0201 }
0202 
0203 void KPlotObject::clearPoints()
0204 {
0205     qDeleteAll(d->pList);
0206     d->pList.clear();
0207 }
0208 
0209 void KPlotObject::draw(QPainter *painter, KPlotWidget *pw)
0210 {
0211     // Order of drawing determines z-distance: Bars in the back, then lines,
0212     // then points, then labels.
0213 
0214     if (d->type & Bars) {
0215         painter->setPen(barPen());
0216         painter->setBrush(barBrush());
0217 
0218         double w = 0;
0219         for (int i = 0; i < d->pList.size(); ++i) {
0220             if (d->pList[i]->barWidth() == 0.0) {
0221                 if (i < d->pList.size() - 1) {
0222                     w = d->pList[i + 1]->x() - d->pList[i]->x();
0223                 }
0224                 // For the last bin, we'll just keep the previous width
0225 
0226             } else {
0227                 w = d->pList[i]->barWidth();
0228             }
0229 
0230             QPointF pp = d->pList[i]->position();
0231             QPointF p1(pp.x() - 0.5 * w, 0.0);
0232             QPointF p2(pp.x() + 0.5 * w, pp.y());
0233             QPointF sp1 = pw->mapToWidget(p1);
0234             QPointF sp2 = pw->mapToWidget(p2);
0235 
0236             QRectF barRect = QRectF(sp1.x(), sp1.y(), sp2.x() - sp1.x(), sp2.y() - sp1.y()).normalized();
0237             painter->drawRect(barRect);
0238             pw->maskRect(barRect, 0.25);
0239         }
0240     }
0241 
0242     // Draw lines:
0243     if (d->type & Lines) {
0244         painter->setPen(linePen());
0245 
0246         QPointF Previous = QPointF(); // Initialize to null
0247 
0248         for (const KPlotPoint *pp : std::as_const(d->pList)) {
0249             // q is the position of the point in screen pixel coordinates
0250             QPointF q = pw->mapToWidget(pp->position());
0251 
0252             if (!Previous.isNull()) {
0253                 painter->drawLine(Previous, q);
0254                 pw->maskAlongLine(Previous, q);
0255             }
0256 
0257             Previous = q;
0258         }
0259     }
0260 
0261     // Draw points:
0262     if (d->type & Points) {
0263         for (const KPlotPoint *pp : std::as_const(d->pList)) {
0264             // q is the position of the point in screen pixel coordinates
0265             QPointF q = pw->mapToWidget(pp->position());
0266             if (pw->pixRect().contains(q.toPoint(), false)) {
0267                 double x1 = q.x() - size();
0268                 double y1 = q.y() - size();
0269                 QRectF qr = QRectF(x1, y1, 2 * size(), 2 * size());
0270 
0271                 // Mask out this rect in the plot for label avoidance
0272                 pw->maskRect(qr, 2.0);
0273 
0274                 painter->setPen(pen());
0275                 painter->setBrush(brush());
0276 
0277                 switch (pointStyle()) {
0278                 case Circle:
0279                     painter->drawEllipse(qr);
0280                     break;
0281 
0282                 case Letter:
0283                     painter->drawText(qr, Qt::AlignCenter, pp->label().left(1));
0284                     break;
0285 
0286                 case Triangle: {
0287                     QPolygonF tri;
0288                     /* clang-format off */
0289                     tri << QPointF(q.x() - size(), q.y() + size())
0290                         << QPointF(q.x(), q.y() - size())
0291                         << QPointF(q.x() + size(), q.y() + size());
0292                     /* clang-format on */
0293                     painter->drawPolygon(tri);
0294                     break;
0295                 }
0296 
0297                 case Square:
0298                     painter->drawRect(qr);
0299                     break;
0300 
0301                 case Pentagon: {
0302                     QPolygonF pent;
0303                     /* clang-format off */
0304                     pent << QPointF(q.x(), q.y() - size())
0305                          << QPointF(q.x() + size(), q.y() - 0.309 * size())
0306                          << QPointF(q.x() + 0.588 * size(), q.y() + size())
0307                          << QPointF(q.x() - 0.588 * size(), q.y() + size())
0308                          << QPointF(q.x() - size(), q.y() - 0.309 * size());
0309                     /* clang-format on */
0310                     painter->drawPolygon(pent);
0311                     break;
0312                 }
0313 
0314                 case Hexagon: {
0315                     QPolygonF hex;
0316                     /* clang-format off */
0317                     hex << QPointF(q.x(), q.y() + size())
0318                         << QPointF(q.x() + size(), q.y() + 0.5 * size())
0319                         << QPointF(q.x() + size(), q.y() - 0.5 * size())
0320                         << QPointF(q.x(), q.y() - size())
0321                         << QPointF(q.x() - size(), q.y() + 0.5 * size())
0322                         << QPointF(q.x() - size(), q.y() - 0.5 * size());
0323                     /* clang-format on */
0324                     painter->drawPolygon(hex);
0325                     break;
0326                 }
0327 
0328                 case Asterisk:
0329                     painter->drawLine(q, QPointF(q.x(), q.y() + size()));
0330                     painter->drawLine(q, QPointF(q.x() + size(), q.y() + 0.5 * size()));
0331                     painter->drawLine(q, QPointF(q.x() + size(), q.y() - 0.5 * size()));
0332                     painter->drawLine(q, QPointF(q.x(), q.y() - size()));
0333                     painter->drawLine(q, QPointF(q.x() - size(), q.y() + 0.5 * size()));
0334                     painter->drawLine(q, QPointF(q.x() - size(), q.y() - 0.5 * size()));
0335                     break;
0336 
0337                 case Star: {
0338                     QPolygonF star;
0339                     /* clang-format off */
0340                     star << QPointF(q.x(), q.y() - size())
0341                          << QPointF(q.x() + 0.2245 * size(), q.y() - 0.309 * size())
0342                          << QPointF(q.x() + size(), q.y() - 0.309 * size()) << QPointF(q.x() + 0.363 * size(), q.y() + 0.118 * size())
0343                          << QPointF(q.x() + 0.588 * size(), q.y() + size()) << QPointF(q.x(), q.y() + 0.382 * size())
0344                          << QPointF(q.x() - 0.588 * size(), q.y() + size()) << QPointF(q.x() - 0.363 * size(), q.y() + 0.118 * size())
0345                          << QPointF(q.x() - size(), q.y() - 0.309 * size()) << QPointF(q.x() - 0.2245 * size(), q.y() - 0.309 * size());
0346                     /* clang-format on */
0347                     painter->drawPolygon(star);
0348                     break;
0349                 }
0350 
0351                 default:
0352                     break;
0353                 }
0354             }
0355         }
0356     }
0357 
0358     // Draw labels
0359     painter->setPen(labelPen());
0360 
0361     for (KPlotPoint *pp : std::as_const(d->pList)) {
0362         QPoint q = pw->mapToWidget(pp->position()).toPoint();
0363         if (pw->pixRect().contains(q, false) && !pp->label().isEmpty()) {
0364             pw->placeLabel(painter, pp);
0365         }
0366     }
0367 }