Skip to content

Commit

Permalink
Skyline: More improvements towards auto-screenshots for 24.1 tutorials (
Browse files Browse the repository at this point in the history
#3287)

* Skyline: More improvements towards auto-screenshots for 24.1 tutorials
- annotate image in DriftTimePredictorTutorialTest
- added new helpers to ScreenshotProcessingExtensions to support text, arrows, and brackets
- restore shorter SkylineWindow in HiResMetabolomicsTutorial
  • Loading branch information
brendanx67 authored Dec 19, 2024
1 parent 22621f4 commit 14b7021
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 92 deletions.
12 changes: 10 additions & 2 deletions pwiz_tools/Skyline/TestPerf/DriftTimePredictorTutorialTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,19 @@ protected override void DoTest()
const int narrowWidth = 1350;
RunUI(() => SkylineWindow.Width = narrowWidth);
PauseForScreenShot("Yeast chromatograms and RT only - prtsc-paste-edit", 11, null, bmp =>
ClipSkylineWindowShotWithForms(bmp, new DockableForm[]
{
bmp = ClipSkylineWindowShotWithForms(bmp, new DockableForm[]
{
SkylineWindow.GetGraphChrom(YeastName),
SkylineWindow.GraphRetentionTime
}));
});
bmp = bmp.DrawAnnotationTextOnBitmap(new PointF(0.15F, 0.1F), "no\nmonoisotopic\nprecursor");
bmp = bmp.DrawArrowOnBitmap(new PointF(0.29F, 0.22F), new PointF(0.312F, 0.32F),3, 10);
bmp = bmp.Expand(right: 0.15F);
bmp = bmp.DrawAnnotationTextOnBitmap(new PointF(0.875F, 0.488F), "yeast\nbefore\nwater");
bmp = bmp.DrawVerticalBackwardBracket(new PointF(0.855F, 0.488F), 0.12F, 0.008F, 3);
return bmp;
});
const double clickTime1 = 41.06;
const double clickIntensity = 1.62E+6;
if (IsPauseForScreenShots)
Expand Down
5 changes: 4 additions & 1 deletion pwiz_tools/Skyline/TestPerf/HiResMetabolomicsTutorial.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,11 @@ protected override void DoTest()
AssertEx.IsDocumentState(docTargets, null, 1, 4, 7, 14);
Assert.IsFalse(docTargets.MoleculeTransitions.Any(t => !t.Transition.IsPrecursor()));

const int SHORT_HEIGHT = 654;
RunUI(() =>
{
SkylineWindow.ChangeTextSize(TreeViewMS.DEFAULT_TEXT_FACTOR);
SkylineWindow.Size = new Size(957, 654);
SkylineWindow.Size = new Size(957, SHORT_HEIGHT);
SkylineWindow.ExpandPrecursors();
});
RestoreViewOnScreen(5);
Expand Down Expand Up @@ -201,6 +202,8 @@ protected override void DoTest()
menuStrip.Closing -= DenyMenuClosing;
subMenuStrip.Closing -= DenyMenuClosing;
menuStrip.Close();

SkylineWindow.Height = SHORT_HEIGHT;
});
}

Expand Down
38 changes: 20 additions & 18 deletions pwiz_tools/Skyline/TestTutorial/GroupedStudies1TutorialTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,10 @@ private void OpenImportArrange()
{
allChrom = WaitForOpenForm<AllChromatogramsGraph>();

WaitForConditionUI(() => allChrom.ProgressTotalPercent >= 5);
allChrom.SetFreezeProgressPercent(72, @"00:00:06");
WaitForCondition(() => allChrom.IsProgressFrozen());
PauseForScreenShot<AllChromatogramsGraph>("Loading Chromatograms form", _pageNum++);
allChrom.SetFreezeProgressPercent(null, null);
}

RunUI(() =>
Expand Down Expand Up @@ -399,7 +401,14 @@ private void ExploreTopPeptides()

RunUI(() => SkylineWindow.NormalizeAreaGraphTo(NormalizeOption.NONE));

PauseForPeakAreaGraphScreenShot("Peak Areas graph", _pageNum++);
PauseForPeakAreaGraphScreenShot("Peak Areas graph with dotps", _pageNum++);

var areaProps = ShowDialog<AreaChartPropertyDlg>(SkylineWindow.ShowAreaPropertyDlg);
RunUI(() =>
{
areaProps.SetDotpCutoffValue(AreaExpectedValue.library, (0.8).ToString(CultureInfo.CurrentCulture));
});
OkDialog(areaProps, areaProps.OkDialog);

RestoreViewOnScreen(13); // Same layout for chromatogram graphs as before on page 13

Expand Down Expand Up @@ -836,21 +845,15 @@ private void ExploreBottomPeptides()

SelectNode(SrmDocument.Level.Molecules, i);
PauseForRetentionTimeGraphScreenShot("Retention Times graph - misintegrated peaks", _pageNum++, null, bmp =>
{
DrawArrowOnBitmap(bmp, new Point((int)(bmp.Width * 0.85), (int)(bmp.Height * 0.8)),
new Point((int)(bmp.Width * 0.78), (int)(bmp.Height * 0.65)));
return bmp;
});
bmp.DrawArrowOnBitmap(new PointF(0.85F, 0.8F), new PointF(0.78F, 0.65F)));

RestoreViewOnScreen(12); // Same layout for Peak Areas graph as on page 12
SelectNode(SrmDocument.Level.Molecules, i);

PauseForPeakAreaGraphScreenShot("Peak Areas graph - no normalization", _pageNum, null, bmp =>
{
int xPos = (int)(bmp.Width * 0.735);
DrawArrowOnBitmap(bmp, new Point(xPos, (int)(bmp.Height * 0.42)),
new Point(xPos, (int)(bmp.Height * 0.6)));
return bmp;
float xPos = 0.735F;
return bmp.DrawArrowOnBitmap(new PointF(xPos, 0.42F), new PointF(xPos, 0.6F));
});

if (IsFullData)
Expand Down Expand Up @@ -964,13 +967,12 @@ private void ExploreBottomPeptides()
});
PauseForPeakAreaGraphScreenShot("Peak Areas graph - no normalization", _pageNum++, null, bmp =>
{
int xFirst = (int)(bmp.Width * 0.398);
var ptTail = new Point(xFirst, (int)(bmp.Height * 0.3));
var ptHead = new Point(xFirst, (int)(bmp.Height * 0.6));
DrawArrowOnBitmap(bmp, ptTail, ptHead);
ptTail.X = ptHead.X = (int)(bmp.Width * 0.692);
DrawArrowOnBitmap(bmp, ptTail, ptHead);
return bmp;
float xFirst = 0.398F;
var ptTail = new PointF(xFirst, 0.3F);
var ptHead = new PointF(xFirst, 0.6F);
bmp.DrawArrowOnBitmap(ptTail, ptHead);
ptTail.X = ptHead.X = 0.692F;
return bmp.DrawArrowOnBitmap(ptTail, ptHead);
});

RunUI(() =>
Expand Down
202 changes: 202 additions & 0 deletions pwiz_tools/Skyline/TestUtil/ScreenshotProcessingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
using pwiz.Skyline.Controls.Databinding;

namespace pwiz.SkylineTestUtil
Expand Down Expand Up @@ -97,6 +99,206 @@ private static IEnumerable<Point> RectPoints(Rectangle rect)
}
}

private static readonly Color ANNOTATION_COLOR = Color.FromArgb(192, 0, 0);

public static Bitmap DrawArrowOnBitmap(this Bitmap bmp, PointF startPointF, PointF endPointF, int tailWidth = 6, int arrowHeadWidth = 16, int arrowHeadHeight = 16)
{
using var g = Graphics.FromImage(bmp);

var startPoint = startPointF.ToPoint(bmp.Size);
var endPoint = endPointF.ToPoint(bmp.Size);

// Set high quality for smoother drawing
g.SmoothingMode = SmoothingMode.AntiAlias;

// Define direction vector for the arrow head
double angle = Math.Atan2(endPoint.Y - startPoint.Y, endPoint.X - startPoint.X);
double sinAngle = Math.Sin(angle);
double cosAngle = Math.Cos(angle);

// Create a pen for the arrow tail with specified tail width
using var pen = new Pen(ANNOTATION_COLOR, tailWidth);

pen.StartCap = LineCap.Flat;
pen.EndCap = LineCap.Flat;

// Calculate the point where the tail should end (start of the arrowhead)
var tailEndPoint = new Point(
(int)(endPoint.X - arrowHeadWidth * cosAngle),
(int)(endPoint.Y - arrowHeadWidth * sinAngle)
);

// Draw the tail of the arrow
g.DrawLine(pen, startPoint, tailEndPoint);

// Calculate arrowhead points
using SolidBrush brush = new SolidBrush(ANNOTATION_COLOR);

Point[] arrowHead =
{
endPoint, // Tip of the arrow
new Point(
(int)(endPoint.X - arrowHeadWidth * cosAngle + arrowHeadHeight * sinAngle / 2),
(int)(endPoint.Y - arrowHeadWidth * sinAngle - arrowHeadHeight * cosAngle / 2)
),
new Point(
(int)(endPoint.X - arrowHeadWidth * cosAngle - arrowHeadHeight * sinAngle / 2),
(int)(endPoint.Y - arrowHeadWidth * sinAngle + arrowHeadHeight * cosAngle / 2)
)
};

// Draw the arrow head
g.FillPolygon(brush, arrowHead);

return bmp;
}

public static Bitmap DrawAnnotationTextOnBitmap(this Bitmap bmp, PointF anchorPointF, string text, Color? color = null)
{
var backgroundColor = color ?? Color.White;

using var g = Graphics.FromImage(bmp);
var font = new Font(@"Tahoma", 16);
var size = TextRenderer.MeasureText(g, text, font);

TextRenderer.DrawText(g,
text,
font,
new Rectangle(ToPoint(anchorPointF, bmp.Size), size),
ANNOTATION_COLOR,
backgroundColor,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);

return bmp;
}

/// <summary>
/// Draws a vertical forward bracket:
/// ┌
/// -
/// └
/// and a center line extending horizontally at the midpoint, same length as the arms.
/// </summary>
public static Bitmap DrawVerticalForwardBracket(this Bitmap bmp, PointF locationF, float lengthF, float armLengthF, int lineWidth = 6)
{
return bmp.DrawBracket(Orientation.Vertical, BracketDirection.forward, locationF, lengthF, armLengthF, lineWidth);
}

/// <summary>
/// Draws a vertical backward bracket:
/// ┐
/// -
/// ┘
/// and a center line extending horizontally at the midpoint, same length as the arms.
/// </summary>
public static Bitmap DrawVerticalBackwardBracket(this Bitmap bmp, PointF locationF, float lengthF, float armLengthF, int lineWidth = 6)
{
return bmp.DrawBracket(Orientation.Vertical, BracketDirection.backward, locationF, lengthF, armLengthF, lineWidth);
}

/// <summary>
/// Draws a horizontal forward bracket:
/// ┌─|-┐
///
/// and a center line extending vertically at the midpoint, same length as the arms.
/// </summary>
public static Bitmap DrawHorizontalForwardBracket(this Bitmap bmp, PointF locationF, float lengthF, float armLengthF, int lineWidth = 6)
{
return bmp.DrawBracket(Orientation.Horizontal, BracketDirection.forward, locationF, lengthF, armLengthF, lineWidth);
}

/// <summary>
/// Draws a horizontal backward bracket:
///
/// └─|-┘
/// and a center line extending vertically at the midpoint, same length as the arms.
/// </summary>
public static Bitmap DrawHorizontalBackwardBracket(this Bitmap bmp, PointF locationF, float lengthF, float armLengthF, int lineWidth = 6)
{
return bmp.DrawBracket(Orientation.Horizontal, BracketDirection.forward, locationF, lengthF, armLengthF, lineWidth);
}

enum BracketDirection { forward, backward }

private static Bitmap DrawBracket(this Bitmap bmp, Orientation orientation, BracketDirection bracketDirection,
PointF locationF, float lengthF, float armLengthF, int lineWidth)
{
using var g = Graphics.FromImage(bmp);
var location = locationF.ToPoint(bmp.Size);
int length = (int)(lengthF * bmp.Height);
int armLength = (int)(armLengthF * bmp.Width);
armLength *= bracketDirection == BracketDirection.forward ? 1 : -1;
using var pen = new Pen(ANNOTATION_COLOR, lineWidth);
pen.StartCap = LineCap.Round;
pen.EndCap = LineCap.Round;
pen.LineJoin = LineJoin.Round;

if (orientation == Orientation.Vertical)
{
// Main vertical line
g.DrawLine(pen, location.X, location.Y, location.X, location.Y + length);

// Arms
g.DrawLine(pen, location.X, location.Y, location.X + armLength, location.Y);
g.DrawLine(pen, location.X, location.Y + length, location.X + armLength, location.Y + length);
// Center line
int center = location.Y + length / 2;
g.DrawLine(pen, location.X, center, location.X - armLength, center);
}
else
{
// Main horizontal line
g.DrawLine(pen, location.X, location.Y, location.X + length, location.Y);

// Arms
g.DrawLine(pen, location.X, location.Y, location.X, location.Y + armLength);
g.DrawLine(pen, location.X + length, location.Y, location.X + length, location.Y + armLength);
// Center line
int center = location.X + length / 2;
g.DrawLine(pen, center, location.Y, center, location.Y - armLength);
}

return bmp;
}

public static Bitmap Expand(this Bitmap bmp, float left = 0, float right = 0, float top = 0, float bottom = 0, Color? color = null)
{
var backgroundColor = color ?? Color.White;
int newWidth = (int)(bmp.Width + left * bmp.Width + right * bmp.Width);
int newHeight = (int)(bmp.Height + top * bmp.Height + bottom * bmp.Height);
var newBitmap = new Bitmap(newWidth, newHeight);
using var g = Graphics.FromImage(newBitmap);
g.Clear(backgroundColor);
g.DrawImage(bmp, new PointF(left, top).ToPoint(bmp.Size));
return newBitmap;
}

private static Point ToPoint(this PointF pointF, Size size)
{
return new Point((int)(pointF.X * size.Width), (int)(pointF.Y * size.Height));
}

// Display a placeholder message on a tutorial screenshot. Use when screenshots aren't correct yet and need to be updated.
public static Bitmap DrawPlaceholderTextOnBitmap(this Bitmap bmp)
{
// CONSIDER(ekoneil): support wrapping text on narrow images
using var g = Graphics.FromImage(bmp);
var font = new Font(@"Tahoma", 12);
var text = "Placeholder screenshot, see test for more info";
var size = TextRenderer.MeasureText(g, text, font);

TextRenderer.DrawText(g,
text,
font,
new Rectangle(25, 25, size.Width, size.Height),
Color.Black,
Color.Yellow,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter |
TextFormatFlags.GlyphOverhangPadding);

return bmp;
}

public static void DrawBoxOnColumn(this Graphics g, DocumentGridForm documentGridForm, int column, int rows, Color color, int lineWidth = 3)
{
var rect = documentGridForm.DataGridView.GetCellDisplayRectangle(column, 0, true); // column's top data cell
Expand Down
Loading

0 comments on commit 14b7021

Please sign in to comment.