DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
SchemaGraphicSegmentedArrow.cpp
Go to the documentation of this file.
1
2#include <QPainter>
3#include <QPen>
4#include <QToolTip>
8#include "dbe/SchemaStyle.hpp"
9
11#include "oks/class.hpp"
12
14#include <cmath>
15
16namespace dbse {
17
19 SchemaGraphicObject * end_item,
20 int connection_count,
21 bool is_inheritance,
22 bool is_composite, QString arrow_name,
23 QString arrow_cardinality, QGraphicsItem * parent )
24 : QGraphicsPathItem ( parent ),
25 m_start_item ( start_item ),
26 m_end_item ( end_item ),
27 m_connection_count ( connection_count),
28 m_inheritance ( is_inheritance ),
29 m_composite ( is_composite ),
30 m_name ( arrow_name ),
31 m_cardinality ( arrow_cardinality ),
32 LastDegree ( 0 ),
33 LastRotation ( 0 )
34{
35 if (!is_inheritance) {
36 setAcceptHoverEvents(true);
37 }
38 //setPen(QPen(Qt::black,2,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
39 setFlag ( ItemIsSelectable, true );
40 m_arrow_size = 20;
41
42}
43
47
48void dbse::SchemaGraphicSegmentedArrow::hoverEnterEvent ( QGraphicsSceneHoverEvent* he) {
49 if (!m_name.isEmpty()) {
50 auto info = m_start_item->GetClass();
51
52 auto rel = info->find_relationship(m_name.toStdString());
53
54 QToolTip::showText( he->screenPos(),
55 QString::fromStdString(rel->get_description()) );
56 }
57 he->ignore();
58}
59void dbse::SchemaGraphicSegmentedArrow::hoverLeaveEvent ( QGraphicsSceneHoverEvent* he) {
60 QToolTip::hideText();
61 he->ignore();
62}
63
65{
66 if ( m_start_item->collidesWithItem ( m_end_item ) )
67 {
68 return QRectF();
69 }
70
71 qreal extra = ( pen().width() + 20 ) / 2.0 + 10;
72 auto br = QRectF ( p1(),
73 QSizeF ( p2().x() - p1().x(),
74 p2().y() - p1().y() ) ).normalized().adjusted (
75 -extra, -extra, extra, extra );
76 br = br.united(m_rel_label_br);
77 br = br.united(m_rel_cardinality_br);
78 return br;
79}
80
82{
83 QPainterPath path = QGraphicsPathItem::shape();
84 path.addRect(m_rel_label_br);
85 path.addRect(m_rel_cardinality_br);
86 path.addPolygon ( m_marker );
87 // path.addText ( p2() + QPoint ( 10, 10 ), QFont ( "Helvetica [Cronyx]", 10 ),
88 // m_cardinality );
89 // path.addText ( p1() + QPoint ( -10, -10 ), QFont ( "Helvetica [Cronyx]", 10 ),
90 // m_cardinality );
91 return path;
92}
93
95{
96 if ( m_start_item->collidesWithItem ( m_end_item ) ) {
97 // this->setPath(QPainterPath());
98 // m_marker = QPolygonF();
99 return;
100 }
101
102 std::vector<QLineF> norms = {
103 {0.,0., 0., 1.}, // up
104 {0.,0., 1., 0.}, // right
105 {0.,0., 0, -1.}, // down
106 {0.,0., -1., 0.}, // left
107 };
108
109 qreal xoffset;
110 qreal yoffset;
111 const qreal xfactor = 23.0;
112 const qreal yfactor = 17.0;
113 if (m_start_item->boundingRect().x() < m_end_item->boundingRect().x()) {
114 xoffset = m_connection_count*xfactor;
115 }
116 else {
117 xoffset = m_connection_count*-xfactor;
118 }
119
120 if (m_start_item->boundingRect().y() > m_end_item->boundingRect().y()) {
121 yoffset = m_connection_count*yfactor;
122 }
123 else {
124 yoffset = m_connection_count*-yfactor;
125 }
126
127 QPointF start_offset(xoffset, yoffset);
128 QPointF end_offset(0, 0);
129
130 QLineF center_line ( m_start_item->mapToScene ( m_start_item->boundingRect().center()+start_offset ),
131 m_end_item->mapToScene ( m_end_item->boundingRect().center()+end_offset ) );
132 QPolygonF start_polygon = QPolygonF ( m_start_item->boundingRect() );
133 QPolygonF end_polygon = QPolygonF ( m_end_item->boundingRect() );
134 QPointF intersect_point_start, intersect_point_end;
135 QLineF intersect_norm_start, intersect_norm_end;
136
137 // Iterate on the sides starting from top-left
138 QPointF p1 = end_polygon.first() + m_end_item->pos(), p2;
139 for ( int i = 1; i < end_polygon.count(); ++i )
140 {
141
142 p2 = end_polygon.at ( i ) + m_end_item->pos();
143 QLineF item_side = QLineF ( p1, p2 );
144 QLineF::IntersectType intersect_type = item_side.intersects ( center_line, &intersect_point_end );
145
146 if ( intersect_type == QLineF::BoundedIntersection ) {
147 intersect_norm_end = norms[i-1];
148 break;
149 }
150
151 p1 = p2;
152 }
153
154 p1 = start_polygon.first() + m_start_item->pos();
155 QPointF start_sp;
156 for ( int i = 1; i < start_polygon.count(); ++i )
157 {
158 p2 = start_polygon.at ( i ) + m_start_item->pos();
159 QLineF item_side = QLineF ( p1, p2 );
160 QLineF::IntersectType intersect_type = item_side.intersects ( center_line,
161 &intersect_point_start );
162 if ( intersect_type == QLineF::BoundedIntersection ) {
163 switch (i) {
164 case 1:
165 start_sp=QPointF {m_start_item->pos().x() + (m_start_item->boundingRect().width() / 2) + xoffset,
166 intersect_point_start.y()};
167 break;
168 case 3:
169 start_sp=QPointF {m_start_item->pos().x() + (m_start_item->boundingRect().width() / 2) - xoffset,
170 intersect_point_start.y()};
171 break;
172
173 case 2:
174 case 4: start_sp={intersect_point_start.x(),
175 m_start_item->pos().y()+m_start_item->boundingRect().height() / 2+yoffset};
176 break;
177 }
178 intersect_norm_start = norms[i-1];
179 break;
180 }
181
182 p1 = p2;
183 }
184
185 QLineF direct_line(start_sp, intersect_point_end);
186 auto font = QFont(SchemaStyle::get_font("line"));
187 auto label_br = QRectF(QFontMetrics ( font ).boundingRect ( m_name ));
188 // Center rectangle on origin
189 label_br.translate(-label_br.width()/2, label_br.height()/2);
190
191
192 auto cardinality_br = QRectF(QFontMetrics ( font ).boundingRect ( m_cardinality ));
193 // Center rectangle on origin
194 cardinality_br.translate(-cardinality_br.width()/2, cardinality_br.height()/2);
195
196
197 qreal label_x_padding = 2;
198 qreal label_y_padding = -2;
199 qreal card_x_padding = 2;
200 qreal card_y_padding = -2;
201
202 QPainterPath path( intersect_point_start );
203
204 if ((intersect_norm_start.dx() == 0) && (intersect_norm_end.dx() == 0)) {
205 // Three segments, starting and ending vertically
206 QPointF wp1 = QPointF(direct_line.x1(),
207 direct_line.center().y()+yoffset);
208 QPointF wp2 = QPointF(direct_line.x2(),
209 direct_line.center().y()+yoffset);
210 path.moveTo(direct_line.x1(), direct_line.y1());
211 path.lineTo(wp1);
212 path.lineTo(wp2);
213
214 // Place the label
215 label_br.translate( wp2
216 + QPointF(
217 (direct_line.dx() < 0 ? 1 : -1) * (label_x_padding+label_br.width()/2),
218 (direct_line.dy() > 0 ? 1 : -1) * (label_br.height()+label_y_padding)
219 )
220 );
221 // Place cardinality
222 cardinality_br.translate( intersect_point_start
223 + QPointF(
224 (direct_line.dx() < 0 ? 1 : -1) * (card_x_padding+cardinality_br.width()/2),
225 (direct_line.dy() > 0 ? 1 : -1) * (cardinality_br.height()+card_y_padding)
226 )
227 );
228
229 } else if ((intersect_norm_start.dy() == 0) && (intersect_norm_end.dy() == 0)) {
230 // Three segments, starting and ending horizontally
231 QPointF wp1 = QPointF(direct_line.center().x()-xoffset,
232 direct_line.y1());
233 QPointF wp2 = QPointF(direct_line.center().x()-xoffset,
234 direct_line.y2());
235
236 path.moveTo(direct_line.x1(), direct_line.y1());
237 path.lineTo(wp1);
238 path.lineTo(wp2);
239
240 // Place the label
241 label_br.translate( wp2
242 + QPointF(
243 (direct_line.dx() > 0 ? 1 : -1) * (label_x_padding+label_br.width()/2),
244 (direct_line.dy() < 0 ? 1 : -1) * (label_br.height()+label_y_padding)
245 )
246 );
247 cardinality_br.translate( intersect_point_start
248 + QPointF(
249 (direct_line.dx() > 0 ? 1 : -1) * (card_x_padding+cardinality_br.width()/2),
250 (direct_line.dy() < 0 ? 1 : -1) * (cardinality_br.height()+card_y_padding)
251 )
252 );
253
254 } else if (intersect_norm_start.dx() == 0) {
255 // Two segments, starting vertically, ending horizontally
256 QPointF wp1 = QPointF(direct_line.x1(), direct_line.y2());
257
258 path.moveTo(direct_line.x1(), direct_line.y1());
259 path.lineTo(wp1);
260
261 label_br.translate( wp1
262 + QPointF(
263 (direct_line.dx() > 0 ? 1 : -1) * (label_x_padding+label_br.width()/2),
264 (direct_line.dy() < 0 ? 1 : -1) * (label_br.height()+label_y_padding)
265 )
266 );
267 cardinality_br.translate( intersect_point_start
268 + QPointF(
269 (direct_line.dx() < 0 ? 1 : -1) * (card_x_padding+cardinality_br.width()/2),
270 (direct_line.dy() > 0 ? 1 : -1) * (cardinality_br.height()+card_y_padding)
271 )
272 );
273
274 } else if (intersect_norm_start.dy() == 0) {
275 // Two segments, starting horizontally, ending vertically
276 QPointF wp1 = QPointF(direct_line.x2(), direct_line.y1() );
277 path.moveTo(direct_line.x1(), direct_line.y1());
278
279 path.lineTo(wp1);
280
281
282 label_br.translate( wp1
283 + QPointF(
284 (direct_line.dx() > 0 ? 1 : -1) * (label_x_padding+label_br.width()/2),
285 (direct_line.dy() < 0 ? 1 : -1) * (label_br.height()+label_y_padding)
286 )
287 );
288 cardinality_br.translate( intersect_point_start
289 + QPointF(
290 (direct_line.dx() < 0 ? 1 : -1) * (card_x_padding+cardinality_br.width()/2),
291 (direct_line.dy() > 0 ? 1 : -1) * (cardinality_br.height()+card_y_padding)
292 )
293 );
294
295 }
296
297 path.lineTo( intersect_point_end );
298 setPath(path);
299
300 m_rel_label_br = label_br;
301 m_rel_cardinality_br = cardinality_br;
302
303 // Why does a function called UpdatePosition add decorations to a line??
304 // =====================================================================
305 if ( m_inheritance )
306 {
307 m_marker = this->make_arrow_head(-intersect_norm_end.angle()/180*M_PI).translated(this->p2());
308 } else {
309 m_marker = this->make_rhombus(-intersect_norm_start.angle()/180*M_PI).translated(this->p1());
310 }
311}
312
317
322
327
329{
330 if ( m_inheritance )
331 {
333 }
334 else
335 {
337 }
338}
339
340QPolygonF SchemaGraphicSegmentedArrow::make_arrow_head(qreal rotation) const {
341
342 float opening_angle = atan2(1, 0.5);
343 qreal size = m_arrow_size;
344 QPointF a1 = QPointF ( sin ( rotation + opening_angle ) * size, cos ( rotation + opening_angle ) * size );
345 QPointF a2 = QPointF ( sin ( rotation + M_PI - opening_angle ) * size, cos ( rotation + M_PI - opening_angle ) * size );
346
347 QPolygonF arrow;
348 arrow << QPointF(0,0) << a1 << a2;
349 return arrow;
350}
351
352QPolygonF SchemaGraphicSegmentedArrow::make_rhombus(qreal rotation) const {
353
354 float opening_angle = M_PI / 3;
355 qreal size = 0.6*m_arrow_size;
356
357 QPointF a1 = QPointF ( sin ( rotation + opening_angle ) * size, cos ( rotation + opening_angle ) * size );
358 QPointF a2 = QPointF ( sin ( rotation + M_PI - opening_angle ) * size, cos ( rotation + M_PI - opening_angle ) * size );
359
360 QPointF m = QPointF ( ( a1.x() + a2.x() ) / 2, ( a1.y() + a2.y() ) / 2 );
361 QPointF a3 = QPointF ( 2 * m.x(), 2 * m.y() );
362
363 QPolygonF rhombus;
364 rhombus << QPointF(0,0) << a1 << a3 << a2;
365 return rhombus;
366}
367
368
369
370void SchemaGraphicSegmentedArrow::paint ( QPainter * painter,
371 const QStyleOptionGraphicsItem * option,
372 QWidget * widget )
373{
374 Q_UNUSED ( option )
375 Q_UNUSED ( widget )
376
377 if ( m_start_item->collidesWithItem ( m_end_item ) )
378 {
379 return;
380 }
381
382 const QPen line_pen = QPen(SchemaStyle::get_color("foreground", "line"), 1);
383 const QPen arrow_pen = QPen(SchemaStyle::get_color("foreground", "line"), 1);
384
385 painter->setFont ( QFont(SchemaStyle::get_font("line")) );
386 painter->setPen ( line_pen );
387 painter->setBrush ( {} );
388 painter->setRenderHint(QPainter::Antialiasing);
389
390
391 painter->drawPath ( this->path());
392
393 painter->setPen ( arrow_pen );
394 if ( !m_inheritance ) {
395 painter->drawText(m_rel_label_br, Qt::AlignTop | Qt::AlignLeft, QString(m_name));
396 painter->drawText(m_rel_cardinality_br, Qt::AlignTop | Qt::AlignLeft, QString(m_cardinality));
397 }
398
399 if ( m_inheritance )
400 {
401 painter->setBrush ( Qt::white );
402 }
403 else if ( m_composite )
404 {
406 painter->setBrush ( SchemaStyle::get_color("foreground", "line") );
407
408
409 } else {
411 painter->setBrush ( Qt::white );
412 }
413 painter->drawPolygon ( m_marker );
414
415}
416
417} // namespace dbse
QRectF boundingRect() const override
SchemaGraphicSegmentedArrow(SchemaGraphicObject *StartItem, SchemaGraphicObject *EndItem, int connection_count, bool IsInheritance, bool IsComposite, QString ArrowName, QString ArrowCardinality, QGraphicsItem *parent=nullptr)
void hoverLeaveEvent(QGraphicsSceneHoverEvent *ev)
void hoverEnterEvent(QGraphicsSceneHoverEvent *ev)
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget=0)
Including QT Headers.
static QColor get_color(const QString &item, const QString &group)
static QFont get_font(const QString &group)