1/* -*- C++ -*-
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2003 Jason Harris <kstars@30doradus.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kplotobject.h"
9
10#include <QDebug>
11#include <QPainter>
12#include <QtAlgorithms>
13
14#include "kplotpoint.h"
15#include "kplotwidget.h"
16
17class KPlotObject::Private
18{
19public:
20 Private(KPlotObject *qq)
21 : q(qq)
22 {
23 }
24
25 ~Private()
26 {
27 qDeleteAll(c: pList);
28 }
29
30 KPlotObject *q;
31
32 QList<KPlotPoint *> pList;
33 PlotTypes type;
34 PointStyle pointStyle;
35 double size;
36 QPen pen, linePen, barPen, labelPen;
37 QBrush brush, barBrush;
38};
39
40KPlotObject::KPlotObject(const QColor &c, PlotType t, double size, PointStyle ps)
41 : d(new Private(this))
42{
43 // By default, all pens and brushes are set to the given color
44 setBrush(c);
45 setBarBrush(c);
46 setPen(QPen(brush(), 1));
47 setLinePen(pen());
48 setBarPen(pen());
49 setLabelPen(pen());
50
51 d->type |= t;
52 setSize(size);
53 setPointStyle(ps);
54}
55
56KPlotObject::~KPlotObject() = default;
57
58KPlotObject::PlotTypes KPlotObject::plotTypes() const
59{
60 return d->type;
61}
62
63void KPlotObject::setShowPoints(bool b)
64{
65 if (b) {
66 d->type |= KPlotObject::Points;
67 } else {
68 d->type &= ~KPlotObject::Points;
69 }
70}
71
72void KPlotObject::setShowLines(bool b)
73{
74 if (b) {
75 d->type |= KPlotObject::Lines;
76 } else {
77 d->type &= ~KPlotObject::Lines;
78 }
79}
80
81void KPlotObject::setShowBars(bool b)
82{
83 if (b) {
84 d->type |= KPlotObject::Bars;
85 } else {
86 d->type &= ~KPlotObject::Bars;
87 }
88}
89
90double KPlotObject::size() const
91{
92 return d->size;
93}
94
95void KPlotObject::setSize(double s)
96{
97 d->size = s;
98}
99
100KPlotObject::PointStyle KPlotObject::pointStyle() const
101{
102 return d->pointStyle;
103}
104
105void KPlotObject::setPointStyle(PointStyle p)
106{
107 d->pointStyle = p;
108}
109
110const QPen &KPlotObject::pen() const
111{
112 return d->pen;
113}
114
115void KPlotObject::setPen(const QPen &p)
116{
117 d->pen = p;
118}
119
120const QPen &KPlotObject::linePen() const
121{
122 return d->linePen;
123}
124
125void KPlotObject::setLinePen(const QPen &p)
126{
127 d->linePen = p;
128}
129
130const QPen &KPlotObject::barPen() const
131{
132 return d->barPen;
133}
134
135void KPlotObject::setBarPen(const QPen &p)
136{
137 d->barPen = p;
138}
139
140const QPen &KPlotObject::labelPen() const
141{
142 return d->labelPen;
143}
144
145void KPlotObject::setLabelPen(const QPen &p)
146{
147 d->labelPen = p;
148}
149
150const QBrush KPlotObject::brush() const
151{
152 return d->brush;
153}
154
155void KPlotObject::setBrush(const QBrush &b)
156{
157 d->brush = b;
158}
159
160const QBrush KPlotObject::barBrush() const
161{
162 return d->barBrush;
163}
164
165void KPlotObject::setBarBrush(const QBrush &b)
166{
167 d->barBrush = b;
168}
169
170QList<KPlotPoint *> KPlotObject::points() const
171{
172 return d->pList;
173}
174
175void KPlotObject::addPoint(const QPointF &p, const QString &label, double barWidth)
176{
177 addPoint(p: new KPlotPoint(p.x(), p.y(), label, barWidth));
178}
179
180void KPlotObject::addPoint(KPlotPoint *p)
181{
182 if (!p) {
183 return;
184 }
185 d->pList.append(t: p);
186}
187
188void KPlotObject::addPoint(double x, double y, const QString &label, double barWidth)
189{
190 addPoint(p: new KPlotPoint(x, y, label, barWidth));
191}
192
193void KPlotObject::removePoint(int index)
194{
195 if ((index < 0) || (index >= d->pList.count())) {
196 // qWarning() << "KPlotObject::removePoint(): index " << index << " out of range!";
197 return;
198 }
199
200 d->pList.removeAt(i: index);
201}
202
203void KPlotObject::clearPoints()
204{
205 qDeleteAll(c: d->pList);
206 d->pList.clear();
207}
208
209void KPlotObject::draw(QPainter *painter, KPlotWidget *pw)
210{
211 // Order of drawing determines z-distance: Bars in the back, then lines,
212 // then points, then labels.
213
214 if (d->type & Bars) {
215 painter->setPen(barPen());
216 painter->setBrush(barBrush());
217
218 double w = 0;
219 for (int i = 0; i < d->pList.size(); ++i) {
220 if (d->pList[i]->barWidth() == 0.0) {
221 if (i < d->pList.size() - 1) {
222 w = d->pList[i + 1]->x() - d->pList[i]->x();
223 }
224 // For the last bin, we'll just keep the previous width
225
226 } else {
227 w = d->pList[i]->barWidth();
228 }
229
230 QPointF pp = d->pList[i]->position();
231 QPointF p1(pp.x() - 0.5 * w, 0.0);
232 QPointF p2(pp.x() + 0.5 * w, pp.y());
233 QPointF sp1 = pw->mapToWidget(p: p1);
234 QPointF sp2 = pw->mapToWidget(p: p2);
235
236 QRectF barRect = QRectF(sp1.x(), sp1.y(), sp2.x() - sp1.x(), sp2.y() - sp1.y()).normalized();
237 painter->drawRect(rect: barRect);
238 pw->maskRect(r: barRect, value: 0.25);
239 }
240 }
241
242 // Draw lines:
243 if (d->type & Lines) {
244 painter->setPen(linePen());
245
246 QPointF Previous = QPointF(); // Initialize to null
247
248 for (const KPlotPoint *pp : std::as_const(t&: d->pList)) {
249 // q is the position of the point in screen pixel coordinates
250 QPointF q = pw->mapToWidget(p: pp->position());
251
252 if (!Previous.isNull()) {
253 painter->drawLine(p1: Previous, p2: q);
254 pw->maskAlongLine(p1: Previous, p2: q);
255 }
256
257 Previous = q;
258 }
259 }
260
261 // Draw points:
262 if (d->type & Points) {
263 for (const KPlotPoint *pp : std::as_const(t&: d->pList)) {
264 // q is the position of the point in screen pixel coordinates
265 QPointF q = pw->mapToWidget(p: pp->position());
266 if (pw->pixRect().contains(p: q.toPoint(), proper: false)) {
267 double x1 = q.x() - size();
268 double y1 = q.y() - size();
269 QRectF qr = QRectF(x1, y1, 2 * size(), 2 * size());
270
271 // Mask out this rect in the plot for label avoidance
272 pw->maskRect(r: qr, value: 2.0);
273
274 painter->setPen(pen());
275 painter->setBrush(brush());
276
277 switch (pointStyle()) {
278 case Circle:
279 painter->drawEllipse(r: qr);
280 break;
281
282 case Letter:
283 painter->drawText(r: qr, flags: Qt::AlignCenter, text: pp->label().left(n: 1));
284 break;
285
286 case Triangle: {
287 QPolygonF tri;
288 /* clang-format off */
289 tri << QPointF(q.x() - size(), q.y() + size())
290 << QPointF(q.x(), q.y() - size())
291 << QPointF(q.x() + size(), q.y() + size());
292 /* clang-format on */
293 painter->drawPolygon(polygon: tri);
294 break;
295 }
296
297 case Square:
298 painter->drawRect(rect: qr);
299 break;
300
301 case Pentagon: {
302 QPolygonF pent;
303 /* clang-format off */
304 pent << QPointF(q.x(), q.y() - size())
305 << QPointF(q.x() + size(), q.y() - 0.309 * size())
306 << QPointF(q.x() + 0.588 * size(), q.y() + size())
307 << QPointF(q.x() - 0.588 * size(), q.y() + size())
308 << QPointF(q.x() - size(), q.y() - 0.309 * size());
309 /* clang-format on */
310 painter->drawPolygon(polygon: pent);
311 break;
312 }
313
314 case Hexagon: {
315 QPolygonF hex;
316 /* clang-format off */
317 hex << QPointF(q.x(), q.y() + size())
318 << QPointF(q.x() + size(), q.y() + 0.5 * size())
319 << QPointF(q.x() + size(), q.y() - 0.5 * size())
320 << QPointF(q.x(), q.y() - size())
321 << QPointF(q.x() - size(), q.y() + 0.5 * size())
322 << QPointF(q.x() - size(), q.y() - 0.5 * size());
323 /* clang-format on */
324 painter->drawPolygon(polygon: hex);
325 break;
326 }
327
328 case Asterisk:
329 painter->drawLine(p1: q, p2: QPointF(q.x(), q.y() + size()));
330 painter->drawLine(p1: q, p2: QPointF(q.x() + size(), q.y() + 0.5 * size()));
331 painter->drawLine(p1: q, p2: QPointF(q.x() + size(), q.y() - 0.5 * size()));
332 painter->drawLine(p1: q, p2: QPointF(q.x(), q.y() - size()));
333 painter->drawLine(p1: q, p2: QPointF(q.x() - size(), q.y() + 0.5 * size()));
334 painter->drawLine(p1: q, p2: QPointF(q.x() - size(), q.y() - 0.5 * size()));
335 break;
336
337 case Star: {
338 QPolygonF star;
339 /* clang-format off */
340 star << QPointF(q.x(), q.y() - size())
341 << QPointF(q.x() + 0.2245 * size(), q.y() - 0.309 * size())
342 << QPointF(q.x() + size(), q.y() - 0.309 * size()) << QPointF(q.x() + 0.363 * size(), q.y() + 0.118 * size())
343 << QPointF(q.x() + 0.588 * size(), q.y() + size()) << QPointF(q.x(), q.y() + 0.382 * size())
344 << QPointF(q.x() - 0.588 * size(), q.y() + size()) << QPointF(q.x() - 0.363 * size(), q.y() + 0.118 * size())
345 << QPointF(q.x() - size(), q.y() - 0.309 * size()) << QPointF(q.x() - 0.2245 * size(), q.y() - 0.309 * size());
346 /* clang-format on */
347 painter->drawPolygon(polygon: star);
348 break;
349 }
350
351 default:
352 break;
353 }
354 }
355 }
356 }
357
358 // Draw labels
359 painter->setPen(labelPen());
360
361 for (KPlotPoint *pp : std::as_const(t&: d->pList)) {
362 QPoint q = pw->mapToWidget(p: pp->position()).toPoint();
363 if (pw->pixRect().contains(p: q, proper: false) && !pp->label().isEmpty()) {
364 pw->placeLabel(painter, pp);
365 }
366 }
367}
368

source code of kplotting/src/kplotobject.cpp