public static Bitmap DrawAutomat(IAutomat A, uint?highlightState = null) { var alreadyDrawn = new IncDictionary <int>(); int lastCurveHeigth = 3 * CURVE_BONUS; float relFactor = 1; var vls = A.VisualizationLines(); if (vls.Length > A.StatesCount * 3) { relFactor *= vls.Length / (A.StatesCount * 3); Utils.DebugMessage($"corrected bmpRelFactor to {relFactor} by transform count", A, Uni.Utils.eDebugLogLevel.Verbose); } else if (A.Alphabet.Length > A.StatesCount * 4) { relFactor *= A.Alphabet.Length / (A.StatesCount * 4); Utils.DebugMessage($"corrected bmpRelFactor to {relFactor} by alphabet count", A, Uni.Utils.eDebugLogLevel.Verbose); } int bmpwidth = Math.Max(500, (int)(IMAGE_SPACE + (A.StatesCount) * (STATE_DIAMETER + IMAGE_SPACE) * relFactor)); var bmp = new Bitmap(bmpwidth, (int)(bmpwidth / 1.5)); Graphics g = Graphics.FromImage(bmp); Font headFont = new Font(SystemFonts.DefaultFont.FontFamily, Math.Max(14, bmpwidth / 60)); int vCenter = (int)(bmp.Height / 2.6); g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height); g.DrawString(A.Name, headFont, Brushes.Black, 20, 20); //Draw full declaration to bottom string FullDesc = A.ToString(); try { FullDesc = FullDesc.Replace(A.Name, "").Trim(); } catch { } const int MARGIN = 10; var FullDescSize = g.MeasureString(FullDesc, CourierNewFont); if (FullDescSize.Width < bmpwidth - MARGIN * 2) { g.DrawString(FullDesc, CourierNewFont, Brushes.Black, 15f, bmp.Height - FullDescSize.Height * 1.5f); } else { int DescPartCount = (int)Math.Ceiling(FullDescSize.Width / (bmpwidth - MARGIN * 2)); int toStringSplitFactor = (int)Math.Ceiling((float)FullDesc.Length / DescPartCount); for (int i = 0; i < DescPartCount; i++) { string rowString = FullDesc.Substring(i * toStringSplitFactor, Math.Min(toStringSplitFactor, FullDesc.Length - i * toStringSplitFactor)); g.DrawString(rowString, CourierNewFont, Brushes.Black, 15, bmp.Height - (DescPartCount - i + 1) * (FullDescSize.Height)); } } // draw States for (uint iq = 0; iq < A.StatesCount; iq++) { var strSize = g.MeasureString(A.States[iq], CourierNewFont); g.DrawString(A.States[iq], CourierNewFont, Brushes.Black, qPos(iq) + STATE_DIAMETER / 2 - strSize.Width / 2, vCenter - strSize.Height / 2); var ellipsePen = PEN; if (A.IsAcceptedState(iq)) { ellipsePen = PEN_DOUBLE; } if (iq == highlightState) { ellipsePen = Pens.Red; } g.DrawEllipse(ellipsePen, qPos(iq), vCenter - STATE_DIAMETER / 2, STATE_DIAMETER, STATE_DIAMETER); } // draw Start Arrow { int lineX = qPos(A.StartState) - IMAGE_SPACE / 2; int lineX2 = qPos(A.StartState); g.DrawLine(PEN_START_ARROW, lineX, vCenter, lineX2, vCenter); } // draw Transform Lines, depends which automat, because of inconsistent transform inheritance and nondeterministic transform foreach (var vtl in vls) { DrawTransformLine(ref g, vtl.Item1, vtl.Item2, vtl.Item3, vCenter, ref lastCurveHeigth, ref alreadyDrawn); } return(bmp); }
static void DrawTransformLine(ref Graphics g, int qStart, int qEnd, string desc, int vCenter, ref int lastCurveHeigth, ref IncDictionary <int> alreadyDrawn) { var descSize = g.MeasureString(desc, CourierNewFont); var penArrow = PEN_ARROW; var alreadyDrawnIndex = alreadyDrawn[qStart, qEnd]; if (qStart == qEnd - 1) //state left to right { int lineX = qPos(qStart) + STATE_DIAMETER; int lineX2 = qPos(qEnd); g.DrawLine(penArrow, lineX, vCenter, lineX2, vCenter); g.DrawString(desc, CourierNewFont, Brushes.Black, (lineX + lineX2) / 2 - descSize.Width / 2 + 10, vCenter - 5 - descSize.Height - alreadyDrawnIndex); } else if (qStart == qEnd + 1) //state right to left { int lineX = qPos(qStart); int lineX2 = qPos(qEnd) + STATE_DIAMETER; g.DrawLine(penArrow, lineX, vCenter, lineX2, vCenter); g.DrawString(desc, CourierNewFont, Brushes.Black, (lineX + lineX2) / 2 - descSize.Width / 2 - 10, vCenter + 5 + alreadyDrawnIndex); } else if (qStart == qEnd) //loop above { int lineX = qPos(qStart) + STATE_DIAMETER / 2 - LOOP_WIDTH / 4; int lineY = vCenter - STATE_DIAMETER / 2 - LOOP_WIDTH; g.DrawEllipse(PEN_ARROW, lineX, lineY, LOOP_WIDTH / 2, LOOP_WIDTH); g.DrawString(desc, CourierNewFont, Brushes.Black, lineX + LOOP_WIDTH / 4 - descSize.Width / 2, lineY - descSize.Height + alreadyDrawnIndex); } else //other connections below { int lineX = qPos(qStart) + STATE_DIAMETER / 2; int lineX2 = qPos(qEnd) + STATE_DIAMETER / 2; var lineM = (lineX + lineX2) / 2; int thisCurveHeigth; if (qStart > qEnd) { penArrow.Color = Color.DarkGray; } if (alreadyDrawnIndex == 0) { thisCurveHeigth = lastCurveHeigth; lastCurveHeigth += CURVE_BONUS; var ps = new Point[] { new Point(lineX, vCenter + STATE_DIAMETER / 2), new Point(lineM, vCenter + STATE_DIAMETER / 2 + thisCurveHeigth), new Point(lineX2, vCenter + STATE_DIAMETER / 2) }; g.DrawCurve(penArrow, ps); g.DrawString(desc, CourierNewFont, Brushes.Black, lineM - descSize.Width / 2, vCenter + STATE_DIAMETER / 2 + thisCurveHeigth); } else { thisCurveHeigth = lastCurveHeigth - CURVE_BONUS; g.DrawString(desc, CourierNewFont, Brushes.Black, lineM - descSize.Width / 2, vCenter + STATE_DIAMETER / 2 + thisCurveHeigth + alreadyDrawnIndex); } } alreadyDrawn.AddOrInc(qStart, qEnd, descSize.Height); }