| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- using DragonSkills99.ControlCollection;
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
- namespace Buhnenrennen
- {
- static class GraphicGenerator
- {
- /// <summary>
- /// generates an image from field, hunter and hunted and returns it
- /// </summary>
- /// <param name="field">field to use for calculations</param>
- /// <param name="hunter">hunting dog ( Max )</param>
- /// <param name="hunted">hunted dog ( Minnie )</param>
- public static Image generateField( Field field, Dog hunter, Dog hunted )
- {
- TimedGroyne safePath = field.getFastestSafeRoute( Dog.MAX, Dog.MINNIE, out var hunterMap, out var huntedMap );
-
- float borderWidth = 7.5F;
- int boxRadius = 25;
- Size imagePadding = new Size( 10, 10 );
- Size imageSize = new Size( imagePadding.Width, imagePadding.Height );
- Font font = new Font( "Consolas", 30F );
- Font nameFont = new Font( "Consolas", 50F );
- Brush hunterBrush = new SolidBrush( Color.DarkRed );
- Brush hunterFillBrush = new SolidBrush( Color.Red );
- Brush huntedBrush = new SolidBrush( Color.DarkBlue );
- Brush huntedFillBrush = new SolidBrush( Color.Cyan );
- Brush safePathBrush = new SolidBrush( Color.Gold );
-
- Size boxSpacing = new Size( 700, 50 );
- Size boxSize = new Size( 300, 200 );
- int levels = ( ( int ) field.Groynes.Keys.Max() / 70 ) + 1; // + 1 cause 0 is also a level and hasn't been included yet
-
- imageSize.Width += boxSize.Width * levels;
- imageSize.Width += boxSpacing.Width * ( levels - 1 );
-
- int hunterBoxCount = field.Groynes.Values.Select( groyneList => groyneList.Count ).Max();
- imageSize.Height += boxSize.Height * hunterBoxCount;
- imageSize.Height += boxSpacing.Height * ( hunterBoxCount - 1 );
-
- Image img = new Bitmap( imageSize.Width, imageSize.Height );
- using( Graphics graphics = Graphics.FromImage( img ) )
- {
- Dictionary<Groyne, RectangleF> groynePositions = new Dictionary<Groyne, RectangleF>();
- Dictionary<Groyne, Brush> groyneBorderBrush = new Dictionary<Groyne, Brush>();
- int height = imageSize.Height - imagePadding.Height;
- int yPadding = ( height - 2 * boxSize.Height ) / 3;
- int xOffset = imagePadding.Width / 2;
-
- Rectangle rect = new Rectangle( new Point( xOffset, imagePadding.Height / 2 + yPadding ), boxSize );
- graphics.FillRectangle( hunterFillBrush, rect );
- graphics.DrawRectangle( new Pen( hunterBrush, borderWidth ), rect );
- graphics.DrawStringCenteredTo( "Max", nameFont, hunterBrush, rect );
- Groyne hunterStart = field.Groynes[ 0 ].Find( groyne => groyne.Type == hunter.Start );
- groynePositions.Add( hunterStart, rect );
- groyneBorderBrush.Add( hunterStart, hunterBrush );
- rect = rect.Clone();
-
- rect.Y += rect.Height + yPadding;
- graphics.FillRoundedRectangle( huntedFillBrush, rect, boxRadius );
- graphics.DrawRoundedRectangle( new Pen( huntedBrush, borderWidth ), rect, boxRadius );
- graphics.DrawStringCenteredTo( "Minnie", nameFont, huntedBrush, rect );
- Groyne huntedStart = field.Groynes[ 0 ].Find( groyne => groyne.Type == hunted.Start );
- groynePositions.Add( huntedStart, rect );
- groyneBorderBrush.Add( huntedStart, huntedBrush );
- rect = rect.Clone();
-
- for ( int level = 70; level <= field.Groynes.Keys.Max(); level += 70 )
- {
- int prevLevel = level - 70;
- int numLevel = level / 70;
- List<Groyne> groynes = field.Groynes[ level ];
- groynes = groynes.Select( groyne => groyne ).ToList(); // Clone array so original array won't be touched/sorted
- groynes.OrderBy( groyne => groyne.YCoord ); // Ordered by yCoord ascending
-
- xOffset += boxSize.Width;
- xOffset += boxSpacing.Width;
- yPadding = ( height - groynes.Count() * boxSize.Height ) / ( groynes.Count() + 1 );
- rect.Y = 0;
- rect.X = xOffset;
-
- //int xOffset = numLevel * boxSize.Width;
- //xOffset += numLevel * boxSpacing.Width;
- //xOffset += imagePadding.Width / 2;
-
- foreach( Groyne groyne in groynes )
- {
- rect.Y += yPadding;
- groynePositions.Add( groyne, rect );
- var huntedTimedGroyne = huntedMap[ level ].Find( timedGroyne => timedGroyne.Groyne == groyne );
- var hunterTimedGroyne = hunterMap[ level ].Find( timedGroyne => timedGroyne.Groyne == groyne );
- bool isUnsafe = huntedTimedGroyne.TotalTime > hunterTimedGroyne.TotalTime || groyneBorderBrush[ huntedTimedGroyne.ParentGroyne.Groyne ] == hunterBrush;
- var fillBrush = isUnsafe ? hunterFillBrush : huntedFillBrush;
- var brush = isUnsafe ? hunterBrush : huntedBrush;
- groyneBorderBrush.Add( groyne, brush );
-
- if ( groyne.Type == 'x' )
- {
- graphics.FillRectangle( fillBrush, rect );
- } else
- {
- graphics.FillRoundedRectangle( fillBrush, rect, boxRadius );
- }
-
- graphics.DrawStringCenteredTo( hunterTimedGroyne.TotalTimeConverted, font, hunterBrush, rect, VerticalPosition.Top, 10 );
- graphics.DrawStringCenteredTo( huntedTimedGroyne.TotalTimeConverted, font, huntedBrush, rect, VerticalPosition.Bottom, 10 );
- graphics.DrawStringCenteredTo( hunterTimedGroyne.DistanceConverted, font, hunterBrush, rect, VerticalPosition.Top, boxSize.Height / 4 );
- graphics.DrawStringCenteredTo( huntedTimedGroyne.DistanceConverted, font, huntedBrush, rect, VerticalPosition.Bottom, boxSize.Height / 4 );
- graphics.ConnectRectangles( new Pen( hunterBrush, borderWidth ), groynePositions[ groyne ], groynePositions[ hunterTimedGroyne.ParentGroyne.Groyne ] );
- graphics.ConnectRectangles( new Pen( huntedBrush, borderWidth ), groynePositions[ groyne ], groynePositions[ huntedTimedGroyne.ParentGroyne.Groyne ], borderWidth );
-
- rect = rect.Clone();
- rect.Y += boxSize.Height;
- }
- }
-
- TimedGroyne safePathSub = safePath;
- if ( safePath != null )
- {
- do
- {
- graphics.ConnectRectangles( new Pen( safePathBrush, borderWidth ), groynePositions[ safePathSub.Groyne ], groynePositions[ safePathSub.ParentGroyne.Groyne ], borderWidth );
- safePathSub = safePathSub.ParentGroyne;
- } while ( safePathSub.ParentGroyne != null );
- }
- else ColorMessageBox.Show( "Das gegebene Scenario enthält keine sichere Route für Minnie" );
-
- foreach( var groyne in groyneBorderBrush.Keys )
- {
- var brush = groyneBorderBrush[ groyne ];
- var rectF = groynePositions[ groyne ];
-
- if( groyne.Type == 'x' || groyne.Type == 'X' )
- {
- graphics.DrawRectangle( new Pen( brush, borderWidth ), rectF.Round() );
- } else
- {
- graphics.DrawRoundedRectangle( new Pen( brush, borderWidth ), rectF, boxRadius );
- }
- }
- }
- return img;
- }
-
- /// <summary>
- /// rounds a give RectangeF to a Rectangle and returns it
- /// </summary>
- /// <param name="rectangle">rectangle to round</param>
- public static Rectangle Round( this RectangleF rectangle )
- {
- return new Rectangle( ( int ) Math.Round( rectangle.X ), ( int ) Math.Round( rectangle.Y ), ( int ) Math.Round( rectangle.Width ), ( int ) Math.Round( rectangle.Height ) );
- }
-
- /// <summary>
- /// draws a line between two rectangles
- /// </summary>
- /// <param name="graphics">graphics object to draw to</param>
- /// <param name="pen">pen to draw with</param>
- /// <param name="from">rectangle to start at</param>
- /// <param name="to">rectengles to stop at</param>
- /// <param name="offset">offset of the drawn line ( offset = 0 means perfectly centered )</param>
- public static void ConnectRectangles( this Graphics graphics, Pen pen, RectangleF from, RectangleF to, float offset = 0 )
- {
- if ( from.IntersectsWith( to ) ) return; // do nothing if rectangles are overlapping to prevent errors
- PointF fromPt, toPt;
-
- if ( from.IntersectsVerticallyWith( to ) )
- {
- fromPt = new PointF( from.X + ( from.Width / 2 ) + offset, from.Top > to.Top ? from.Top : from.Bottom );
- toPt = new PointF( to.X + ( to.Width / 2 ) + offset, from.Top < to.Top ? to.Top : to.Bottom );
- } else
- {
- fromPt = new PointF( from.Left > to.Left ? from.Left : from.Right, from.Y + ( from.Height / 2 ) + offset );
- toPt = new PointF( from.Left < to.Left ? to.Left : to.Right, to.Y + ( to.Height / 2 ) + offset );
- }
-
- graphics.DrawLine( pen, fromPt, toPt );
- }
-
- /// <summary>
- /// tests if [me] instersects with [other] vertically
- /// </summary>
- public static bool IntersectsVerticallyWith( this RectangleF me, RectangleF other )
- {
- RectangleF meClone = me.Clone();
- RectangleF otherClone = other.Clone();
- meClone.Y = 0;
- otherClone.Y = 0;
- return meClone.IntersectsWith( otherClone );
- }
-
- /// <summary>
- /// tests if [me] instersects with [other] horizontally
- /// </summary>
- public static bool IntersectsHorizontallyWith( this RectangleF me, RectangleF other )
- {
- RectangleF meClone = me.Clone();
- RectangleF otherClone = other.Clone();
- meClone.X = 0;
- otherClone.X = 0;
- return meClone.IntersectsWith( otherClone );
- }
-
- /// <summary>
- /// draws a string horizontally centered in given rectangle
- /// </summary>
- /// <param name="graphics">graphics object to draw to</param>
- /// <param name="text">text to draw</param>
- /// <param name="font">font to draw the text with</param>
- /// <param name="brush">brush to use for drawing</param>
- /// <param name="centerTo">rectangle, the text should be centered in</param>
- /// <param name="positioning">vertical alignment of the text</param>
- /// <param name="topMiddlePadding">offset of the text from border ( ignored if positioning = centered )</param>
- public static void DrawStringCenteredTo( this Graphics graphics, string text, Font font, Brush brush, RectangleF centerTo, VerticalPosition positioning = VerticalPosition.Middle, float topMiddlePadding = 0 )
- {
- graphics.DrawString( text, font, brush, centerTo.CenterTo( graphics.MeasureString( text, font ), positioning, topMiddlePadding ) );
- }
- /// <summary>
- /// calculates point for text to be centered in rectangle and returns it
- /// </summary>
- /// <param name="rect">rectangle, the text should be centered in</param>
- /// <param name="measured">measured size of the text to draw</param>
- /// <param name="positioning">vertical alignment of the text</param>
- /// <param name="topMiddlePadding">offset of the text from border ( ignored if positioning = centered )</param>
- public static PointF CenterTo( this RectangleF rect, SizeF measured, VerticalPosition positioning = VerticalPosition.Middle, float topMiddlePadding = 0 )
- {
- PointF point = new PointF( rect.X, rect.Y );
-
- point.X += ( rect.Width / 2 ) - ( measured.Width / 2 );
-
- switch( positioning )
- {
- case VerticalPosition.Bottom:
- point.Y += rect.Height - ( measured.Height + topMiddlePadding );
- break;
- case VerticalPosition.Middle:
- point.Y += ( rect.Height / 2 ) - ( measured.Height / 2 );
- break;
- case VerticalPosition.Top:
- point.Y += topMiddlePadding;
- break;
- }
- return point;
- }
-
- public enum VerticalPosition
- {
- Top, Middle, Bottom
- }
- }
- }
|