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
10#include "oks/class.hpp"
11
13#include <cmath>
14
15namespace dbse {
16
18 SchemaGraphicObject * end_item,
19 int connection_count,
20 bool is_inheritance,
21 bool is_composite, QString arrow_name,
22 QString arrow_cardinality, QGraphicsItem * parent )
23 : QGraphicsPathItem ( parent ),
24 m_start_item ( start_item ),
25 m_end_item ( end_item ),
26 m_connection_count ( connection_count),
27 m_inheritance ( is_inheritance ),
28 m_composite ( is_composite ),
29 m_name ( arrow_name ),
30 m_cardinality ( arrow_cardinality ),
31 LastDegree ( 0 ),
32 LastRotation ( 0 )
33{
34 if (!is_inheritance) {
35 setAcceptHoverEvents(true);
36 }
37 //setPen(QPen(Qt::black,2,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
38 setFlag ( ItemIsSelectable, true );
39 m_default_color = QColor ( 0x1e1b18 );
40 m_label_font = QFont( "Helvetica [Cronyx]", 9);
41 m_arrow_size = 20;
42
43}
44
48
49void dbse::SchemaGraphicSegmentedArrow::hoverEnterEvent ( QGraphicsSceneHoverEvent* he) {
50 if (!m_name.isEmpty()) {
51 auto info = m_start_item->GetClass();
52
53 auto rel = info->find_relationship(m_name.toStdString());
54
55 QToolTip::showText( he->screenPos(),
56 QString::fromStdString(rel->get_description()) );
57 }
58 he->ignore();
59}
60void dbse::SchemaGraphicSegmentedArrow::hoverLeaveEvent ( QGraphicsSceneHoverEvent* he) {
61 QToolTip::hideText();
62 he->ignore();
63}
64
66{
67 if ( m_start_item->collidesWithItem ( m_end_item ) )
68 {
69 return QRectF();
70 }
71
72 qreal extra = ( pen().width() + 20 ) / 2.0 + 10;
73 auto br = QRectF ( p1(),
74 QSizeF ( p2().x() - p1().x(),
75 p2().y() - p1().y() ) ).normalized().adjusted (
76 -extra, -extra, extra, extra );
77 br = br.united(m_rel_label_br);
78 br = br.united(m_rel_cardinality_br);
79 return br;
80}
81
83{
84 QPainterPath path = QGraphicsPathItem::shape();
85 path.addRect(m_rel_label_br);
86 path.addRect(m_rel_cardinality_br);
87 path.addPolygon ( m_marker );
88 // path.addText ( p2() + QPoint ( 10, 10 ), QFont ( "Helvetica [Cronyx]", 10 ),
89 // m_cardinality );
90 // path.addText ( p1() + QPoint ( -10, -10 ), QFont ( "Helvetica [Cronyx]", 10 ),
91 // m_cardinality );
92 return path;
93}
94
96{
97 if ( m_start_item->collidesWithItem ( m_end_item ) ) {
98 // this->setPath(QPainterPath());
99 // m_marker = QPolygonF();
100 return;
101 }
102
103 std::vector<QLineF> norms = {
104 {0.,0., 0., 1.}, // up
105 {0.,0., 1., 0.}, // right
106 {0.,0., 0, -1.}, // down
107 {0.,0., -1., 0.}, // left
108 };
109
110 qreal xoffset;
111 qreal yoffset;
112 const qreal xfactor = 23.0;
113 const qreal yfactor = 17.0;
114 if (m_start_item->boundingRect().x() < m_end_item->boundingRect().x()) {
115 xoffset = m_connection_count*xfactor;
116 }
117 else {
118 xoffset = m_connection_count*-xfactor;
119 }
120
121 if (m_start_item->boundingRect().y() > m_end_item->boundingRect().y()) {
122 yoffset = m_connection_count*yfactor;
123 }
124 else {
125 yoffset = m_connection_count*-yfactor;
126 }
127
128 QPointF start_offset(xoffset, yoffset);
129 QPointF end_offset(0, 0);
130
131 QLineF center_line ( m_start_item->mapToScene ( m_start_item->boundingRect().center()+start_offset ),
132 m_end_item->mapToScene ( m_end_item->boundingRect().center()+end_offset ) );
133 QPolygonF start_polygon = QPolygonF ( m_start_item->boundingRect() );
134 QPolygonF end_polygon = QPolygonF ( m_end_item->boundingRect() );
135 QPointF intersect_point_start, intersect_point_end;
136 QLineF intersect_norm_start, intersect_norm_end;
137
138 // Iterate on the sides starting from top-left
139 QPointF p1 = end_polygon.first() + m_end_item->pos(), p2;
140 for ( int i = 1; i < end_polygon.count(); ++i )
141 {
142
143 p2 = end_polygon.at ( i ) + m_end_item->pos();
144 QLineF item_side = QLineF ( p1, p2 );
145 QLineF::IntersectType intersect_type = item_side.intersects ( center_line, &intersect_point_end );
146
147 if ( intersect_type == QLineF::BoundedIntersection ) {
148 intersect_norm_end = norms[i-1];
149 break;
150 }
151
152 p1 = p2;
153 }
154
155 p1 = start_polygon.first() + m_start_item->pos();
156 QPointF start_sp;
157 for ( int i = 1; i < start_polygon.count(); ++i )
158 {
159 p2 = start_polygon.at ( i ) + m_start_item->pos();
160 QLineF item_side = QLineF ( p1, p2 );
161 QLineF::IntersectType intersect_type = item_side.intersects ( center_line,
162 &intersect_point_start );
163 if ( intersect_type == QLineF::BoundedIntersection ) {
164 switch (i) {
165 case 1:
166 start_sp=QPointF {m_start_item->pos().x() + (m_start_item->boundingRect().width() / 2) + xoffset,
167 intersect_point_start.y()};
168 break;
169 case 3:
170 start_sp=QPointF {m_start_item->pos().x() + (m_start_item->boundingRect().width() / 2) - xoffset,
171 intersect_point_start.y()};
172 break;
173
174 case 2:
175 case 4: start_sp={intersect_point_start.x(),
176 m_start_item->pos().y()+m_start_item->boundingRect().height() / 2+yoffset};
177 break;
178 }
179 intersect_norm_start = norms[i-1];
180 break;
181 }
182
183 p1 = p2;
184 }
185
186 QLineF direct_line(start_sp, intersect_point_end);
187 auto label_br = QRectF(QFontMetrics ( m_label_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 ( m_label_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 = 20;
200 qreal card_y_padding = 0;
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(m_default_color, 1);
383 const QPen arrow_pen = QPen(m_default_color, 1);
384
385 painter->setFont ( m_label_font );
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 ( Qt::black );
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.