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 }