Created
May 5, 2023 19:12
-
-
Save ja72/8faac41da525c5750ee08c6fd15c04ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class Gdi | |
{ | |
public static Pen Stroke { get; set; } = new Pen(Color.Black, 0); | |
public static SolidBrush Fill { get; set; } = new SolidBrush(Color.Black); | |
public static Font Font { get; set; } = SystemFonts.CaptionFont; | |
/// <summary> | |
/// Adds an arrow to the start of a <see cref="Pen"/> object. | |
/// </summary> | |
/// <remarks>Intended to be used with <see cref="Stroke"/></remarks> | |
/// <param name="pen">The pen to modify.</param> | |
/// <param name="arrowSize">Size of the arrow in pixels.</param> | |
public static void AddStartArrow(this Pen pen, float arrowSize) | |
{ | |
pen.CustomStartCap = new AdjustableArrowCap(arrowSize/2, arrowSize); | |
} | |
/// <summary> | |
/// Removes the start arrow from a <see cref="Pen"/> object. | |
/// </summary> | |
/// <remarks>Intended to be used with <see cref="Stroke"/></remarks> | |
/// <param name="pen">The pen object to modify.</param> | |
public static void RemoveStartArrow(this Pen pen) | |
{ | |
pen.StartCap = LineCap.NoAnchor; | |
} | |
/// <summary> | |
/// Adds an arrow to the end of a <see cref="Pen"/> object. | |
/// </summary> | |
/// <remarks>Intended to be used with <see cref="Stroke"/></remarks> | |
/// <param name="pen">The pen to modify.</param> | |
/// <param name="arrowSize">Size of the arrow in pixels.</param> | |
public static void AddEndArrow(this Pen pen, float arrowSize) | |
{ | |
pen.CustomEndCap = new AdjustableArrowCap(arrowSize/2, arrowSize); | |
} | |
/// <summary> | |
/// Removes the end arrow from a <see cref="Pen"/> object. | |
/// </summary> | |
/// <remarks>Intended to be used with <see cref="Stroke"/></remarks> | |
/// <param name="pen">The pen object to modify.</param> | |
public static void RemoveEndArrow(this Pen pen) | |
{ | |
pen.EndCap = LineCap.NoAnchor; | |
} | |
/// <summary> | |
/// Draws two axis along the x and y directions using arrowed lines and a dot at the origin. | |
/// </summary> | |
/// <param name="graphics">The graphics.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="arrowSize">The size of arrows and dot in pixels.</param> | |
public static void DrawAxes(this Graphics graphics, float scale, float arrowSize=1f) | |
{ | |
float arrowTip = Math.Max(scale/5, arrowSize/2); | |
AddEndArrow(Stroke, arrowTip); | |
DrawLine(graphics, scale, 0, 0, arrowSize, 0d); | |
DrawLine(graphics, scale, 0, 0, 0d, arrowSize); | |
FillPoint(graphics, scale, 0d, 0d, arrowTip); | |
RemoveEndArrow(Stroke); | |
} | |
/// <summary> | |
/// Draws a small circle representing a point on the screen. | |
/// </summary> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="point">The point location.</param> | |
/// <param name="size">The size of the circle.</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void DrawPoint(this Graphics g, float scale, double pointX, double pointY, float size = 6f) | |
{ | |
float x = scale * (float)pointX, y = -scale * (float)pointY; | |
g.DrawEllipse(Stroke, x - size / 2, y - size / 2, size, size); | |
} | |
/// <summary> | |
/// Fills a small circle representing a point on the screen. | |
/// </summary> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="point">The point location.</param> | |
/// <param name="size">The size of the circle.</param> | |
/// <remarks>Uses <see cref="Fill"/> to fill the shape.</remarks> | |
public static void FillPoint(this Graphics g, float scale, double pointX, double pointY, float size = 6f) | |
{ | |
float x = scale * (float)pointX, y = -scale * (float)pointY; | |
g.FillEllipse(Fill, x - size / 2, y - size / 2, size, size); | |
} | |
/// <summary> | |
/// Draws a set of points (nodes) | |
/// </summary> | |
/// <remarks>There must be more than one node to draw anything.</remarks> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="nodes">The array of locations defining the points</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void DrawPoints(this Graphics g, float scale, double[,] nodes, float size = 6) | |
{ | |
PointF[] points = new PointF[nodes.GetLength(0)]; | |
for (int i = 0; i < points.Length; i++) | |
{ | |
DrawPoint(g, scale, nodes[i, 0], nodes[i, 1],size); | |
} | |
} | |
/// <summary> | |
/// Draws a set of points (nodes) | |
/// </summary> | |
/// <remarks>There must be more than one node to draw anything.</remarks> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="nodes">The array of locations defining the points</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void FillPoints(this Graphics g, float scale, double[,] nodes, float size = 6) | |
{ | |
PointF[] points = new PointF[nodes.GetLength(0)]; | |
for (int i = 0; i < points.Length; i++) | |
{ | |
FillPoint(g, scale, nodes[i, 0], nodes[i, 1],size); | |
} | |
} | |
/// <summary> | |
/// Draws consecutive lines defined by their end points (nodes) | |
/// </summary> | |
/// <remarks>There must be more than one node to draw anything.</remarks> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="nodes">The array of locations defining the nodes of the (consecutive) lines</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void DrawLines(this Graphics g, float scale, double[,] nodes) | |
{ | |
if (nodes.GetLength(0) <= 1) return; | |
PointF[] points = new PointF[nodes.GetLength(0)]; | |
for (int i = 0; i < points.Length; i++) | |
{ | |
points[i] = new PointF(scale * (float)nodes[i,0], -scale * (float)nodes[i,1]); | |
} | |
g.DrawLines(Stroke, points); | |
} | |
/// <summary> | |
/// Draws a curve through a set of points (nodes) | |
/// </summary> | |
/// <remarks>There must be more than one node to draw anything.</remarks> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="nodes">The array of locations defining the nodes of the (consecutive) lines</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void DrawCurve(this Graphics g, float scale, double[,] nodes) | |
{ | |
if (nodes.GetLength(0) <= 1) return; | |
PointF[] points = new PointF[nodes.GetLength(0)]; | |
for (int i = 0; i < points.Length; i++) | |
{ | |
points[i] = new PointF(scale * (float)nodes[i,0], -scale * (float)nodes[i,1]); | |
} | |
g.DrawCurve(Stroke, points); | |
} | |
/// <summary> | |
/// Draws a closed shape with lines defined by the end points (nodes). | |
/// </summary> | |
/// <remarks>There must be more than one node to draw anything.</remarks> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="nodes">The array of locations defining the nodes of the (consecutive) lines</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void DrawPolygon(this Graphics g, float scale, double[,] nodes) | |
{ | |
if (nodes.GetLength(0) <= 1) return; | |
PointF[] points = new PointF[nodes.GetLength(0)]; | |
for (int i = 0; i < points.Length; i++) | |
{ | |
points[i] = new PointF(scale * (float)nodes[i,0], -scale * (float)nodes[i,1]); | |
} | |
g.DrawPolygon(Stroke, points); | |
} | |
/// <summary> | |
/// Draws a closed curve though several points (nodes). | |
/// </summary> | |
/// <remarks>There must be more than one node to draw anything.</remarks> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="nodes">The array of locations defining the nodes of the (consecutive) lines</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void DrawClosedCurve(this Graphics g, float scale, double[,] nodes) | |
{ | |
if (nodes.GetLength(0) <= 1) return; | |
PointF[] points = new PointF[nodes.GetLength(0)]; | |
for (int i = 0; i < points.Length; i++) | |
{ | |
points[i] = new PointF(scale * (float)nodes[i,0], -scale * (float)nodes[i,1]); | |
} | |
g.DrawClosedCurve(Stroke, points); | |
} | |
/// <summary> | |
/// Fills a closed shape with lines defined by the end points (nodes). | |
/// </summary> | |
/// <remarks>There must be more than one node to draw anything.</remarks> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="nodes">The array of locations defining the nodes of the (consecutive) lines</param> | |
/// <remarks>Uses <see cref="Fill"/> to fill the shape.</remarks> | |
public static void FillPolygon(this Graphics g, float scale, double[,] nodes) | |
{ | |
if (nodes.GetLength(0) <= 1) return; | |
PointF[] points = new PointF[nodes.GetLength(0)]; | |
for (int i = 0; i < points.Length; i++) | |
{ | |
points[i] = new PointF(scale * (float)nodes[i,0], -scale * (float)nodes[i,1]); | |
} | |
g.FillPolygon(Fill, points); | |
} | |
/// <summary> | |
/// Fills a closed curve though several points (nodes). | |
/// </summary> | |
/// <remarks>There must be more than one node to draw anything.</remarks> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="nodes">The array of locations defining the nodes of the (consecutive) lines</param> | |
/// <remarks>Uses <see cref="Fill"/> to fill the shape.</remarks> | |
public static void FillClosedCurve(this Graphics g, float scale, double[,] nodes) | |
{ | |
if (nodes.GetLength(0) <= 1) return; | |
PointF[] points = new PointF[nodes.GetLength(0)]; | |
for (int i = 0; i < points.Length; i++) | |
{ | |
points[i] = new PointF(scale * (float)nodes[i,0], -scale * (float)nodes[i,1]); | |
} | |
g.FillClosedCurve(Fill, points); | |
} | |
/// <summary> | |
/// Draw a line between two points. | |
/// </summary> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="fromPosition">The start position.</param> | |
/// <param name="toPosition">The end position.</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void DrawLine(this Graphics g, float scale, double fromPositionX, double fromPositionY, double toPositionX, double toPositionY) | |
{ | |
float x1 = scale * (float)fromPositionX, y1 = -scale * (float)fromPositionY; | |
float x2 = scale * (float)toPositionX, y2 = -scale * (float)toPositionY; | |
g.DrawLine(Stroke, x1 ,y1, x2, y2); | |
} | |
/// <summary> | |
/// Draw a line from a point, to a certain offset using pixel coordinates. | |
/// </summary> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="fromPosition">The start position.</param> | |
/// <param name="xPixelOffset">The horizontal offset.</param> | |
/// <param name="yPixelOffset">The vertical offset.</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw the shape.</remarks> | |
public static void DrawLine(this Graphics g, float scale, double fromPositionX, double fromPositionY, float xPixelOffset, int yPixelOffset) | |
{ | |
float x = scale * (float)fromPositionX, y = -scale * (float)fromPositionY; | |
g.DrawLine(Stroke, x, y, xPixelOffset, -yPixelOffset); | |
} | |
/// <summary> | |
/// Draws an ellipse | |
/// </summary> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="center">The center of the ellipse.</param> | |
/// <param name="semiMajor">The semi-major axis.</param> | |
/// <param name="semiMinor">The semi-minor axis.</param> | |
/// <param name="tiltAngleDegrees">The tilt angle degrees CCW from horizontal.</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw lines.</remarks> | |
public static void DrawEllipse(this Graphics g, float scale, double centerX, double centerY, float semiMajor, float semiMinor, float tiltAngleDegrees = 0) | |
{ | |
float x = scale * (float)centerX, y = scale * (float)centerY; | |
float dx = scale * semiMajor, dy = scale * semiMinor; | |
var save = g.Save(); | |
g.RotateTransform(-tiltAngleDegrees); | |
g.DrawEllipse(Stroke, x - dx, y - dy, 2 * dx, 2 * dy); | |
g.Restore(save); | |
} | |
/// <summary> | |
/// Fills an ellipse | |
/// </summary> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="center">The center of the ellipse.</param> | |
/// <param name="semiMajor">The semi-major axis.</param> | |
/// <param name="semiMinor">The semi-minor axis.</param> | |
/// <param name="tiltAngleDegrees">The tilt angle degrees CCW from horizontal.</param> | |
/// <remarks>Uses <see cref="Fill"/> to fill shapes.</remarks> | |
public static void FillEllipse(this Graphics g, float scale, double centerX, double centerY, float semiMajor, float semiMinor, float tiltAngleDegrees = 0) | |
{ | |
float x = scale * (float)centerX, y = scale * (float)centerY; | |
float dx = scale * semiMajor, dy = scale * semiMinor; | |
var save = g.Save(); | |
g.RotateTransform(-tiltAngleDegrees); | |
g.FillEllipse(Fill, x - dx, y - dy, 2 * dx, 2 * dy); | |
g.Restore(save); | |
} | |
/// <summary> | |
/// Draws an arc of an ellipse (partial ellipse) | |
/// </summary> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="center">The center of the ellipse.</param> | |
/// <param name="semiMajor">The semi-major axis.</param> | |
/// <param name="semiMinor">The semi-minor axis.</param> | |
/// <param name="startAngleDegrees">The start angle in degrees CCW from 3 o'clock.</param> | |
/// <param name="sweepAngleDegrees">The sweep angle in degrees CCW.</param> | |
/// <param name="tiltAngleDegrees">The tilt angle degrees CCW from horizontal.</param> | |
/// <remarks>Uses <see cref="Stroke"/> to draw lines.</remarks> | |
public static void DrawEllipseArc(this Graphics g, float scale, double centerX, double centerY, float semiMajor, float semiMinor, float startAngleDegrees, float sweepAngleDegrees, float tiltAngleDegrees = 0) | |
{ | |
float x = scale * (float)centerX, y = scale * (float)centerY; | |
float dx = scale * semiMajor, dy = scale * semiMinor; | |
//float θ = | |
var save = g.Save(); | |
g.RotateTransform(-tiltAngleDegrees); | |
g.DrawArc(Stroke, x - dx, y - dy, 2 * dx, 2 * dy, 360 - startAngleDegrees, -sweepAngleDegrees); | |
g.Restore(save); | |
} | |
/// <summary> | |
/// Draws the text. | |
/// </summary> | |
/// <param name="g">The graphics object to draw on.</param> | |
/// <param name="scale">The drawing scale converting (x, y) to <see cref="PointF"/>.</param> | |
/// <param name="text">The text to draw.</param> | |
/// <param name="anchor">The anchor point.</param> | |
/// <param name="alignment">The alignment of the text relative to the anchor point.</param> | |
/// <param name="padding">The padding in pixels to space from anchor point.</param> | |
/// <exception cref="System.NotSupportedException">For invalid <paramref name="alignment"/>.</exception> | |
/// <remarks>Uses <see cref="Stroke"/> to draw text.</remarks> | |
public static void DrawText(this Graphics g, float scale, string text, double anchorX, double anchorY, ContentAlignment alignment, int padding=4) | |
{ | |
SizeF size = g.MeasureString(text, Font); | |
float x = scale * (float)anchorX, y = -scale * (float)anchorY; | |
switch (alignment) | |
{ | |
case ContentAlignment.TopLeft: | |
x = x - size.Width - padding; | |
y = y - size.Height - padding; | |
break; | |
case ContentAlignment.TopCenter: | |
x = x - size.Width/2; | |
y = y - size.Height - padding; | |
break; | |
case ContentAlignment.TopRight: | |
x = x + padding; | |
y = y - size.Height - padding; | |
break; | |
case ContentAlignment.MiddleLeft: | |
x = x - size.Width - padding; | |
y = y - size.Height/2; | |
break; | |
case ContentAlignment.MiddleCenter: | |
x = x - size.Width/2; | |
y = y - size.Height/2; | |
break; | |
case ContentAlignment.MiddleRight: | |
x = x + padding; | |
y = y - size.Height/2; | |
break; | |
case ContentAlignment.BottomLeft: | |
x = x - size.Width - padding; | |
y = y + padding; | |
break; | |
case ContentAlignment.BottomCenter: | |
x = x - size.Width/2; | |
y = y + padding; | |
break; | |
case ContentAlignment.BottomRight: | |
x = x + padding; | |
y = y + padding; | |
break; | |
default: | |
throw new NotSupportedException(); | |
} | |
g.DrawString(text, Font, Fill, x, y); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment