File indexing completed on 2024-04-28 15:29:29
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() 0057 { 0058 delete d; 0059 } 0060 0061 KPlotObject::PlotTypes KPlotObject::plotTypes() const 0062 { 0063 return d->type; 0064 } 0065 0066 void KPlotObject::setShowPoints(bool b) 0067 { 0068 if (b) { 0069 d->type |= KPlotObject::Points; 0070 } else { 0071 d->type &= ~KPlotObject::Points; 0072 } 0073 } 0074 0075 void KPlotObject::setShowLines(bool b) 0076 { 0077 if (b) { 0078 d->type |= KPlotObject::Lines; 0079 } else { 0080 d->type &= ~KPlotObject::Lines; 0081 } 0082 } 0083 0084 void KPlotObject::setShowBars(bool b) 0085 { 0086 if (b) { 0087 d->type |= KPlotObject::Bars; 0088 } else { 0089 d->type &= ~KPlotObject::Bars; 0090 } 0091 } 0092 0093 double KPlotObject::size() const 0094 { 0095 return d->size; 0096 } 0097 0098 void KPlotObject::setSize(double s) 0099 { 0100 d->size = s; 0101 } 0102 0103 KPlotObject::PointStyle KPlotObject::pointStyle() const 0104 { 0105 return d->pointStyle; 0106 } 0107 0108 void KPlotObject::setPointStyle(PointStyle p) 0109 { 0110 d->pointStyle = p; 0111 } 0112 0113 const QPen &KPlotObject::pen() const 0114 { 0115 return d->pen; 0116 } 0117 0118 void KPlotObject::setPen(const QPen &p) 0119 { 0120 d->pen = p; 0121 } 0122 0123 const QPen &KPlotObject::linePen() const 0124 { 0125 return d->linePen; 0126 } 0127 0128 void KPlotObject::setLinePen(const QPen &p) 0129 { 0130 d->linePen = p; 0131 } 0132 0133 const QPen &KPlotObject::barPen() const 0134 { 0135 return d->barPen; 0136 } 0137 0138 void KPlotObject::setBarPen(const QPen &p) 0139 { 0140 d->barPen = p; 0141 } 0142 0143 const QPen &KPlotObject::labelPen() const 0144 { 0145 return d->labelPen; 0146 } 0147 0148 void KPlotObject::setLabelPen(const QPen &p) 0149 { 0150 d->labelPen = p; 0151 } 0152 0153 const QBrush KPlotObject::brush() const 0154 { 0155 return d->brush; 0156 } 0157 0158 void KPlotObject::setBrush(const QBrush &b) 0159 { 0160 d->brush = b; 0161 } 0162 0163 const QBrush KPlotObject::barBrush() const 0164 { 0165 return d->barBrush; 0166 } 0167 0168 void KPlotObject::setBarBrush(const QBrush &b) 0169 { 0170 d->barBrush = b; 0171 } 0172 0173 QList<KPlotPoint *> KPlotObject::points() const 0174 { 0175 return d->pList; 0176 } 0177 0178 void KPlotObject::addPoint(const QPointF &p, const QString &label, double barWidth) 0179 { 0180 addPoint(new KPlotPoint(p.x(), p.y(), label, barWidth)); 0181 } 0182 0183 void KPlotObject::addPoint(KPlotPoint *p) 0184 { 0185 if (!p) { 0186 return; 0187 } 0188 d->pList.append(p); 0189 } 0190 0191 void KPlotObject::addPoint(double x, double y, const QString &label, double barWidth) 0192 { 0193 addPoint(new KPlotPoint(x, y, label, barWidth)); 0194 } 0195 0196 void KPlotObject::removePoint(int index) 0197 { 0198 if ((index < 0) || (index >= d->pList.count())) { 0199 // qWarning() << "KPlotObject::removePoint(): index " << index << " out of range!"; 0200 return; 0201 } 0202 0203 d->pList.removeAt(index); 0204 } 0205 0206 void KPlotObject::clearPoints() 0207 { 0208 qDeleteAll(d->pList); 0209 d->pList.clear(); 0210 } 0211 0212 void KPlotObject::draw(QPainter *painter, KPlotWidget *pw) 0213 { 0214 // Order of drawing determines z-distance: Bars in the back, then lines, 0215 // then points, then labels. 0216 0217 if (d->type & Bars) { 0218 painter->setPen(barPen()); 0219 painter->setBrush(barBrush()); 0220 0221 double w = 0; 0222 for (int i = 0; i < d->pList.size(); ++i) { 0223 if (d->pList[i]->barWidth() == 0.0) { 0224 if (i < d->pList.size() - 1) { 0225 w = d->pList[i + 1]->x() - d->pList[i]->x(); 0226 } 0227 // For the last bin, we'll just keep the previous width 0228 0229 } else { 0230 w = d->pList[i]->barWidth(); 0231 } 0232 0233 QPointF pp = d->pList[i]->position(); 0234 QPointF p1(pp.x() - 0.5 * w, 0.0); 0235 QPointF p2(pp.x() + 0.5 * w, pp.y()); 0236 QPointF sp1 = pw->mapToWidget(p1); 0237 QPointF sp2 = pw->mapToWidget(p2); 0238 0239 QRectF barRect = QRectF(sp1.x(), sp1.y(), sp2.x() - sp1.x(), sp2.y() - sp1.y()).normalized(); 0240 painter->drawRect(barRect); 0241 pw->maskRect(barRect, 0.25); 0242 } 0243 } 0244 0245 // Draw lines: 0246 if (d->type & Lines) { 0247 painter->setPen(linePen()); 0248 0249 QPointF Previous = QPointF(); // Initialize to null 0250 0251 for (const KPlotPoint *pp : std::as_const(d->pList)) { 0252 // q is the position of the point in screen pixel coordinates 0253 QPointF q = pw->mapToWidget(pp->position()); 0254 0255 if (!Previous.isNull()) { 0256 painter->drawLine(Previous, q); 0257 pw->maskAlongLine(Previous, q); 0258 } 0259 0260 Previous = q; 0261 } 0262 } 0263 0264 // Draw points: 0265 if (d->type & Points) { 0266 for (const KPlotPoint *pp : std::as_const(d->pList)) { 0267 // q is the position of the point in screen pixel coordinates 0268 QPointF q = pw->mapToWidget(pp->position()); 0269 if (pw->pixRect().contains(q.toPoint(), false)) { 0270 double x1 = q.x() - size(); 0271 double y1 = q.y() - size(); 0272 QRectF qr = QRectF(x1, y1, 2 * size(), 2 * size()); 0273 0274 // Mask out this rect in the plot for label avoidance 0275 pw->maskRect(qr, 2.0); 0276 0277 painter->setPen(pen()); 0278 painter->setBrush(brush()); 0279 0280 switch (pointStyle()) { 0281 case Circle: 0282 painter->drawEllipse(qr); 0283 break; 0284 0285 case Letter: 0286 painter->drawText(qr, Qt::AlignCenter, pp->label().left(1)); 0287 break; 0288 0289 case Triangle: { 0290 QPolygonF tri; 0291 /* clang-format off */ 0292 tri << QPointF(q.x() - size(), q.y() + size()) 0293 << QPointF(q.x(), q.y() - size()) 0294 << QPointF(q.x() + size(), q.y() + size()); 0295 /* clang-format on */ 0296 painter->drawPolygon(tri); 0297 break; 0298 } 0299 0300 case Square: 0301 painter->drawRect(qr); 0302 break; 0303 0304 case Pentagon: { 0305 QPolygonF pent; 0306 /* clang-format off */ 0307 pent << QPointF(q.x(), q.y() - size()) 0308 << QPointF(q.x() + size(), q.y() - 0.309 * size()) 0309 << QPointF(q.x() + 0.588 * size(), q.y() + size()) 0310 << QPointF(q.x() - 0.588 * size(), q.y() + size()) 0311 << QPointF(q.x() - size(), q.y() - 0.309 * size()); 0312 /* clang-format on */ 0313 painter->drawPolygon(pent); 0314 break; 0315 } 0316 0317 case Hexagon: { 0318 QPolygonF hex; 0319 /* clang-format off */ 0320 hex << 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 << QPointF(q.x(), q.y() - size()) 0324 << QPointF(q.x() - size(), q.y() + 0.5 * size()) 0325 << QPointF(q.x() - size(), q.y() - 0.5 * size()); 0326 /* clang-format on */ 0327 painter->drawPolygon(hex); 0328 break; 0329 } 0330 0331 case Asterisk: 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 painter->drawLine(q, QPointF(q.x(), q.y() - size())); 0336 painter->drawLine(q, QPointF(q.x() - size(), q.y() + 0.5 * size())); 0337 painter->drawLine(q, QPointF(q.x() - size(), q.y() - 0.5 * size())); 0338 break; 0339 0340 case Star: { 0341 QPolygonF star; 0342 /* clang-format off */ 0343 star << QPointF(q.x(), q.y() - size()) 0344 << QPointF(q.x() + 0.2245 * size(), q.y() - 0.309 * size()) 0345 << QPointF(q.x() + size(), q.y() - 0.309 * size()) << QPointF(q.x() + 0.363 * size(), q.y() + 0.118 * size()) 0346 << QPointF(q.x() + 0.588 * size(), q.y() + size()) << QPointF(q.x(), q.y() + 0.382 * size()) 0347 << QPointF(q.x() - 0.588 * size(), q.y() + size()) << QPointF(q.x() - 0.363 * size(), q.y() + 0.118 * size()) 0348 << QPointF(q.x() - size(), q.y() - 0.309 * size()) << QPointF(q.x() - 0.2245 * size(), q.y() - 0.309 * size()); 0349 /* clang-format on */ 0350 painter->drawPolygon(star); 0351 break; 0352 } 0353 0354 default: 0355 break; 0356 } 0357 } 0358 } 0359 } 0360 0361 // Draw labels 0362 painter->setPen(labelPen()); 0363 0364 for (KPlotPoint *pp : std::as_const(d->pList)) { 0365 QPoint q = pw->mapToWidget(pp->position()).toPoint(); 0366 if (pw->pixRect().contains(q, false) && !pp->label().isEmpty()) { 0367 pw->placeLabel(painter, pp); 0368 } 0369 } 0370 }