@@ -88,6 +88,9 @@ void QtSLiMGraphView_CustomPlot::freeData(void)
8888 for (std::vector<double > *sizebuffer : size_)
8989 delete sizebuffer;
9090
91+ for (std::vector<double > *anglebuffer : angle_)
92+ delete anglebuffer;
93+
9194 plot_type_.clear ();
9295 x1data_.clear ();
9396 y1data_.clear ();
@@ -101,6 +104,7 @@ void QtSLiMGraphView_CustomPlot::freeData(void)
101104 alpha_.clear ();
102105 line_width_.clear ();
103106 size_.clear ();
107+ angle_.clear ();
104108 xadj_.clear ();
105109 yadj_.clear ();
106110 image_.clear ();
@@ -431,6 +435,7 @@ void QtSLiMGraphView_CustomPlot::addABLineData(double *a_values, double *b_value
431435 alpha_.push_back (alpha);
432436 line_width_.push_back (lwd);
433437 size_.push_back (nullptr ); // unused for abline
438+ angle_.push_back (nullptr ); // unused for abline
434439 xadj_.push_back (-1 ); // unused for abline
435440 yadj_.push_back (-1 ); // unused for abline
436441 image_.push_back (QImage ()); // unused for abline
@@ -456,6 +461,7 @@ void QtSLiMGraphView_CustomPlot::addLineData(double *x_values, double *y_values,
456461 alpha_.push_back (alpha);
457462 line_width_.push_back (lwd);
458463 size_.push_back (nullptr ); // unused for lines
464+ angle_.push_back (nullptr ); // unused for lines
459465 xadj_.push_back (-1 ); // unused for lines
460466 yadj_.push_back (-1 ); // unused for lines
461467 image_.push_back (QImage ()); // unused for lines
@@ -481,6 +487,7 @@ void QtSLiMGraphView_CustomPlot::addRectData(double *x1_values, double *y1_value
481487 alpha_.push_back (alpha);
482488 line_width_.push_back (lwd);
483489 size_.push_back (nullptr ); // unused for rects
490+ angle_.push_back (nullptr ); // unused for rects
484491 xadj_.push_back (-1 ); // unused for rects
485492 yadj_.push_back (-1 ); // unused for rects
486493 image_.push_back (QImage ()); // unused for rects
@@ -506,6 +513,7 @@ void QtSLiMGraphView_CustomPlot::addSegmentData(double *x1_values, double *y1_va
506513 alpha_.push_back (alpha);
507514 line_width_.push_back (lwd);
508515 size_.push_back (nullptr ); // unused for segments
516+ angle_.push_back (nullptr ); // unused for segments
509517 xadj_.push_back (-1 ); // unused for segments
510518 yadj_.push_back (-1 ); // unused for segments
511519 image_.push_back (QImage ()); // unused for segments
@@ -514,6 +522,32 @@ void QtSLiMGraphView_CustomPlot::addSegmentData(double *x1_values, double *y1_va
514522 update ();
515523}
516524
525+ void QtSLiMGraphView_CustomPlot::addMarginTextData (double *x_values, double *y_values, std::vector<QString> *labels, int data_count,
526+ std::vector<QColor> *color, std::vector<double > *alpha,
527+ std::vector<double > *size, double *adj, std::vector<double > *angle)
528+ {
529+ plot_type_.push_back (QtSLiM_CustomPlotType::kMarginText );
530+ x1data_.push_back (x_values);
531+ y1data_.push_back (y_values);
532+ x2data_.push_back (nullptr ); // unused for text
533+ y2data_.push_back (nullptr ); // unused for text
534+ labels_.push_back (labels);
535+ data_count_.push_back (data_count);
536+ symbol_.push_back (nullptr ); // unused for text
537+ color_.push_back (color);
538+ border_.push_back (nullptr ); // unused for text
539+ alpha_.push_back (alpha);
540+ line_width_.push_back (nullptr ); // unused for text
541+ size_.push_back (size);
542+ angle_.push_back (angle); // unused for text
543+ xadj_.push_back (adj[0 ]);
544+ yadj_.push_back (adj[1 ]);
545+ image_.push_back (QImage ()); // unused for text
546+
547+ rescaleAxesForDataRange ();
548+ update ();
549+ }
550+
517551void QtSLiMGraphView_CustomPlot::addPointData (double *x_values, double *y_values, int data_count,
518552 std::vector<int > *symbol, std::vector<QColor> *color, std::vector<QColor> *border,
519553 std::vector<double > *alpha, std::vector<double > *lwd, std::vector<double > *size)
@@ -531,6 +565,7 @@ void QtSLiMGraphView_CustomPlot::addPointData(double *x_values, double *y_values
531565 alpha_.push_back (alpha);
532566 line_width_.push_back (lwd);
533567 size_.push_back (size);
568+ angle_.push_back (nullptr ); // unused for points
534569 xadj_.push_back (-1 ); // unused for points
535570 yadj_.push_back (-1 ); // unused for points
536571 image_.push_back (QImage ()); // unused for points
@@ -541,7 +576,7 @@ void QtSLiMGraphView_CustomPlot::addPointData(double *x_values, double *y_values
541576
542577void QtSLiMGraphView_CustomPlot::addTextData (double *x_values, double *y_values, std::vector<QString> *labels, int data_count,
543578 std::vector<QColor> *color, std::vector<double > *alpha,
544- std::vector<double > *size, double *adj)
579+ std::vector<double > *size, double *adj, std::vector< double > *angle )
545580{
546581 plot_type_.push_back (QtSLiM_CustomPlotType::kText );
547582 x1data_.push_back (x_values);
@@ -556,6 +591,7 @@ void QtSLiMGraphView_CustomPlot::addTextData(double *x_values, double *y_values,
556591 alpha_.push_back (alpha);
557592 line_width_.push_back (nullptr ); // unused for text
558593 size_.push_back (size);
594+ angle_.push_back (angle); // unused for text
559595 xadj_.push_back (adj[0 ]);
560596 yadj_.push_back (adj[1 ]);
561597 image_.push_back (QImage ()); // unused for text
@@ -580,6 +616,7 @@ void QtSLiMGraphView_CustomPlot::addImageData(double *x_values, double *y_values
580616 alpha_.push_back (alpha);
581617 line_width_.push_back (nullptr ); // unused for image
582618 size_.push_back (nullptr ); // unused for image
619+ angle_.push_back (nullptr ); // unused for image
583620 xadj_.push_back (-1 ); // unused for image
584621 yadj_.push_back (-1 ); // unused for image
585622 image_.push_back (image);
@@ -655,6 +692,9 @@ void QtSLiMGraphView_CustomPlot::drawGraph(QPainter &painter, QRect interiorRect
655692 case QtSLiM_CustomPlotType::kRects :
656693 drawRects (painter, interiorRect, i);
657694 break ;
695+ case QtSLiM_CustomPlotType::kMarginText :
696+ drawMarginText (painter, interiorRect, i);
697+ break ;
658698 case QtSLiM_CustomPlotType::kPoints :
659699 drawPoints (painter, interiorRect, i);
660700 break ;
@@ -877,6 +917,117 @@ void QtSLiMGraphView_CustomPlot::drawLines(QPainter &painter, QRect interiorRect
877917 }
878918}
879919
920+ void QtSLiMGraphView_CustomPlot::drawMarginText (QPainter &painter, QRect interiorRect, int dataIndex)
921+ {
922+ double *xdata = x1data_[dataIndex];
923+ double *ydata = y1data_[dataIndex];
924+ std::vector<QString> &labels = *labels_[dataIndex];
925+ int pointCount = data_count_[dataIndex];
926+ std::vector<QColor> &textColors = *color_[dataIndex];
927+ std::vector<double > &textAlphas = *alpha_[dataIndex];
928+ std::vector<double > &textAngles = *angle_[dataIndex];
929+ std::vector<double > &pointSizes = *size_[dataIndex];
930+ double xadj = xadj_[dataIndex];
931+ double yadj = yadj_[dataIndex];
932+
933+ // move clipping area outward to encompass our entire parent widget
934+ QRect bounds = rect ();
935+
936+ painter.save ();
937+ painter.setClipRect (bounds, Qt::ReplaceClip);
938+
939+ // QtSLiMFrameRect(interiorRect, Qt::green, painter);
940+
941+ // set up to get the font and font info
942+ double lastPointSize = -1 ;
943+ QFont labelFont;
944+ double capHeight = 0 ;
945+
946+ for (int pointIndex = 0 ; pointIndex < pointCount; ++pointIndex)
947+ {
948+ double user_x = xdata[pointIndex];
949+ double user_y = ydata[pointIndex];
950+
951+ if (std::isfinite (user_x) && std::isfinite (user_y))
952+ {
953+ // for mtext(), coordinates inside the plot area are in [0,1]
954+ QString &labelText = labels[pointIndex];
955+ double x = user_x * interiorRect.width () + interiorRect.x ();
956+ double y = user_y * interiorRect.height () + interiorRect.y ();
957+
958+ // qDebug() << "labelText ==" << labelText << ", user_x ==" << user_x << ", user_y ==" << user_y << ", x ==" << x << ", y ==" << y;
959+
960+ // translate the painter so (x, y) is at (0, 0)
961+ painter.save ();
962+ painter.translate (x, y);
963+ x = 0 ;
964+ y = 0 ;
965+
966+ double pointSize = pointSizes[pointIndex % pointSizes.size ()];
967+
968+ if (pointSize != lastPointSize)
969+ {
970+ labelFont = QtSLiMGraphView::labelFontOfPointSize (pointSize);
971+ capHeight = QFontMetricsF (labelFont).capHeight ();
972+ painter.setFont (labelFont);
973+
974+ lastPointSize = pointSize;
975+ }
976+
977+ QColor textColor = textColors[pointIndex % textColors.size ()];
978+ double alpha = textAlphas[pointIndex % textAlphas.size ()];
979+
980+ if (alpha != 1.0 )
981+ textColor.setAlphaF (alpha);
982+
983+ painter.setPen (textColor);
984+
985+ QRect labelBoundingRect = painter.boundingRect (QRect (), Qt::TextDontClip | Qt::TextSingleLine, labelText);
986+
987+ // labelBoundingRect is useful for its width, which seems to be calculated correctly; its height, however, is oddly large, and
988+ // is not useful, so we use the capHeight from the font metrics instead. This means that vertically centered (yadj == 0.5) is
989+ // the midpoint between the baseline and the capHeight, which I think is probably the best behavior.
990+ double labelWidth = labelBoundingRect.width ();
991+ double labelHeight = capHeight;
992+ double labelX = x - SLIM_SCREEN_ROUND (labelWidth * xadj);
993+ double labelY = y - SLIM_SCREEN_ROUND (labelHeight * yadj);
994+
995+ // qDebug() << " labelBoundingRect ==" << labelBoundingRect << ", labelWidth ==" << labelWidth << ", labelHeight ==" << labelHeight << ", capHeight ==" << capHeight;
996+ // qDebug() << " labelX ==" << labelX << ", labelY ==" << labelY;
997+
998+ // rotate the coordinate system around (x, y); for example, -10.0 is 10 degrees clockwise
999+ double textAngle = textAngles[pointIndex];
1000+
1001+ if (textAngle != 0.0 )
1002+ painter.rotate (-textAngles[pointIndex]);
1003+
1004+ #if 0
1005+ // draw the axes for the text around the pivot point
1006+ QPainterPath linePath;
1007+
1008+ linePath.moveTo(-50, 0);
1009+ linePath.lineTo(50, 0);
1010+ linePath.moveTo(0, -50);
1011+ linePath.lineTo(0, 50);
1012+ painter.strokePath(linePath, QPen(Qt::green, 1.0));
1013+ #endif
1014+
1015+ // flip vertically so the text is upright, and then use -labelY since we're flipped
1016+ painter.scale (1.0 , -1.0 );
1017+
1018+ painter.drawText (QPointF (labelX, -labelY), labelText);
1019+
1020+ painter.restore ();
1021+ }
1022+ else
1023+ {
1024+ // a NAN or INF value for x or y is not plotted
1025+ }
1026+ }
1027+
1028+ painter.restore ();
1029+ }
1030+
8801031void QtSLiMGraphView_CustomPlot::drawRects (QPainter &painter, QRect interiorRect, int dataIndex)
8811032{
8821033 double *x1data = x1data_[dataIndex];
@@ -1016,6 +1167,7 @@ void QtSLiMGraphView_CustomPlot::drawText(QPainter &painter, QRect interiorRect,
10161167 int pointCount = data_count_[dataIndex];
10171168 std::vector<QColor> &textColors = *color_[dataIndex];
10181169 std::vector<double > &textAlphas = *alpha_[dataIndex];
1170+ std::vector<double > &textAngles = *angle_[dataIndex];
10191171 std::vector<double > &pointSizes = *size_[dataIndex];
10201172 double xadj = xadj_[dataIndex];
10211173 double yadj = yadj_[dataIndex];
@@ -1032,8 +1184,18 @@ void QtSLiMGraphView_CustomPlot::drawText(QPainter &painter, QRect interiorRect,
10321184
10331185 if (std::isfinite (user_x) && std::isfinite (user_y))
10341186 {
1187+ QString &labelText = labels[pointIndex];
10351188 double x = plotToDeviceX (user_x, interiorRect);
10361189 double y = plotToDeviceY (user_y, interiorRect);
1190+
1191+ // qDebug() << "labelText ==" << labelText << ", user_x ==" << user_x << ", user_y ==" << user_y << ", x ==" << x << ", y ==" << y;
1192+
1193+ // translate the painter so (x, y) is at (0, 0)
1194+ painter.save ();
1195+ painter.translate (x, y);
1196+ x = 0 ;
1197+ y = 0 ;
1198+
10371199 double pointSize = pointSizes[pointIndex % pointSizes.size ()];
10381200
10391201 if (pointSize != lastPointSize)
@@ -1053,7 +1215,6 @@ void QtSLiMGraphView_CustomPlot::drawText(QPainter &painter, QRect interiorRect,
10531215
10541216 painter.setPen (textColor);
10551217
1056- QString &labelText = labels[pointIndex];
10571218 QRect labelBoundingRect = painter.boundingRect (QRect (), Qt::TextDontClip | Qt::TextSingleLine, labelText);
10581219
10591220 // labelBoundingRect is useful for its width, which seems to be calculated correctly; its height, however, is oddly large, and
@@ -1064,22 +1225,32 @@ void QtSLiMGraphView_CustomPlot::drawText(QPainter &painter, QRect interiorRect,
10641225 double labelX = x - SLIM_SCREEN_ROUND (labelWidth * xadj);
10651226 double labelY = y - SLIM_SCREEN_ROUND (labelHeight * yadj);
10661227
1067- // qDebug() << "labelText ==" << labelText << ", user_x ==" << user_x << ", user_y ==" << user_y << ", x ==" << x << ", y ==" << y;
10681228 // qDebug() << " labelBoundingRect ==" << labelBoundingRect << ", labelWidth ==" << labelWidth << ", labelHeight ==" << labelHeight << ", capHeight ==" << capHeight;
10691229 // qDebug() << " labelX ==" << labelX << ", labelY ==" << labelY;
10701230
1071- // we need to correct for the fact that the coordinate system is flipped; text would draw upside-down
1072- // we do that by transforming labelY and then turning off the world matrix, to turn off the flipping
1073- // note that labelX transformed is unchanged, since the device coordinate origin is 0 anyway
1074- QPointF transformedPoint = painter.transform ().map (QPointF (labelX, labelY));
1231+ // rotate the coordinate system around (x, y); for example, -10.0 is 10 degrees clockwise
1232+ double textAngle = textAngles[pointIndex];
1233+
1234+ if (textAngle != 0.0 )
1235+ painter.rotate (-textAngles[pointIndex]);
1236+
1237+ #if 0
1238+ // draw the axes for the text around the pivot point
1239+ QPainterPath linePath;
1240+
1241+ linePath.moveTo(-50, 0);
1242+ linePath.lineTo(50, 0);
1243+ linePath.moveTo(0, -50);
1244+ linePath.lineTo(0, 50);
1245+ painter.strokePath(linePath, QPen(Qt::green, 1.0));
1246+ #endif
10751247
1076- labelY = transformedPoint.y ();
1248+ // flip vertically so the text is upright, and then use -labelY since we're flipped
1249+ painter.scale (1.0 , -1.0 );
10771250
1078- // qDebug() << " transformedPoint ==" << transformedPoint << ", labelY ==" << labelY ;
1251+ painter. drawText ( QPointF (labelX, - labelY), labelText) ;
10791252
1080- painter.setWorldMatrixEnabled (false );
1081- painter.drawText (QPointF (labelX, labelY), labelText);
1082- painter.setWorldMatrixEnabled (true );
1253+ painter.restore ();
10831254 }
10841255 else
10851256 {
0 commit comments