private int _yBottomOfPedigree; // used for descendents

        public PedigreeControl()
        {
            InitializeComponent();
            SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
            _lines = new[] { new List <int[]>(), new List <int[]>(), new List <int[]>() };
            NoCreatureSelected();
            listViewCreatures.ListViewItemSorter = new ListViewColumnSorter();
            splitContainer1.Panel2.Paint        += Panel2_Paint;
            _tt = new ToolTip {
                AutoPopDelay = 10000
            };
            _compactGenerations = Properties.Settings.Default.PedigreeCompactViewGenerations;
            switch ((PedigreeViewMode)Properties.Settings.Default.PedigreeViewMode)
            {
            case PedigreeViewMode.Compact: RbViewCompact.Checked = true; break;

            case PedigreeViewMode.HView: RbViewH.Checked = true; break;

            default: RbViewClassic.Checked = true; break;
            }
            TbZoom.Value = (int)(10 * Properties.Settings.Default.PedigreeZoomFactor);
            PedigreeCreatureCompact.SetSizeFactor(Properties.Settings.Default.PedigreeZoomFactor);
            nudGenerations.ValueSave         = _compactGenerations;
            statSelector1.StatIndexSelected += StatSelector1_StatIndexSelected;
        }
        CreateParentsChildCompact(Creature creature, int x, int y, int xOffsetParents, List <int[]>[] lines, List <Control> pcs, ToolTip tt,
                                  bool onlyDrawParents, bool highlightCreature, int highlightStatIndex, bool hView, int hViewRotation,
                                  out Point locationMother, out Point locationFather, int highlightMotherLine = 0, int highlightFatherLine = 0)
        {
            locationMother = Point.Empty;
            locationFather = Point.Empty;
            if (creature == null)
            {
                return((0, 0), (0, 0));
            }

            if (!onlyDrawParents)
            {
                // creature
                var c = new PedigreeCreatureCompact(creature, highlightCreature, highlightStatIndex, tt)
                {
                    Location = new Point(x, y)
                };
                pcs.Add(c);
                if (highlightStatIndex != -1)
                {
                    (highlightMotherLine, highlightFatherLine) = c.PossibleStatInheritance(highlightStatIndex);
                }
            }

            if (creature.Mother == null && creature.Father == null)
            {
                return((0, 0), (0, 0));
            }

            var statInheritanceMother = (0, 0);
            var statInheritanceFather = (0, 0);

            var yParents = y - PedigreeCreatureCompact.ControlHeight * 13 / 12;

            // mother
            if (creature.Mother != null)
            {
                locationMother = hView ? RotateOffset(x, y, -xOffsetParents, 0, hViewRotation) : new Point(x - xOffsetParents, yParents);
                var c = new PedigreeCreatureCompact(creature.Mother, highlightStatIndex: highlightMotherLine != 0 ? highlightStatIndex : -1, tt: tt)
                {
                    Location = locationMother
                };
                pcs.Add(c);
                if (highlightMotherLine != 0 && highlightStatIndex != -1)
                {
                    statInheritanceMother = c.PossibleStatInheritance(highlightStatIndex);
                }
            }
            // father
            if (creature.Father != null)
            {
                locationFather = hView ? RotateOffset(x, y, xOffsetParents, 0, hViewRotation) : new Point(x + xOffsetParents, yParents);
                var c = new PedigreeCreatureCompact(creature.Father, highlightStatIndex: highlightFatherLine != 0 ? highlightStatIndex : -1, tt: tt)
                {
                    Location = locationFather
                };
                pcs.Add(c);
                if (highlightFatherLine != 0 && highlightStatIndex != -1)
                {
                    statInheritanceFather = c.PossibleStatInheritance(highlightStatIndex);
                }
            }

            // lines
            if (hView)
            {
                //  M──O──F

                // keep normal lines black to make them more visible in this mode
                if (highlightMotherLine == 0)
                {
                    highlightMotherLine = 1;
                }
                if (highlightFatherLine == 0)
                {
                    highlightFatherLine = 1;
                }

                var halfControlWidth = PedigreeCreatureCompact.ControlWidth / 2 - 1;
                var xCenter          = x + halfControlWidth;
                var yCenter          = y + PedigreeCreatureCompact.ControlHeight / 2;

                var start = RotateOffset(xCenter, yCenter, -xOffsetParents + halfControlWidth, 0, hViewRotation);
                var end   = RotateOffset(xCenter, yCenter, -halfControlWidth, 0, hViewRotation);
                lines[1].Add(new[] { start.X, start.Y, end.X, end.Y, highlightMotherLine });

                start = RotateOffset(xCenter, yCenter, xOffsetParents - halfControlWidth, 0, hViewRotation);
                end   = RotateOffset(xCenter, yCenter, halfControlWidth, 0, hViewRotation);
                lines[1].Add(new[] { start.X, start.Y, end.X, end.Y, highlightFatherLine });
            }
            else
            {
                //  M──┬──F
                //     O
                var yLineHorizontal  = y - PedigreeCreatureCompact.ControlHeight / 2;
                var xCenterOffspring = x + PedigreeCreatureCompact.ControlWidth / 2;
                lines[2].Add(new[]
                {
                    x - xOffsetParents + PedigreeCreatureCompact.ControlWidth, yLineHorizontal, xCenterOffspring,
                    yLineHorizontal, highlightMotherLine
                });
                lines[2].Add(new[]
                             { x + xOffsetParents, yLineHorizontal, xCenterOffspring, yLineHorizontal, highlightFatherLine });
                lines[1].Add(new[] { xCenterOffspring, yLineHorizontal, xCenterOffspring, y, Math.Max(highlightMotherLine, highlightFatherLine) });
            }

            return(statInheritanceMother, statInheritanceFather);
        }
        private static void DrawKey(PictureBox pb, Species species)
        {
            if (species == null)
            {
                return;
            }

            var w = pb.Width;
            var h = pb.Height;

            Bitmap bmp = new Bitmap(w, h);

            using (Graphics g = Graphics.FromImage(bmp))
                using (var font = new Font("Microsoft Sans Serif", 8.25f))
                    using (var format = new StringFormat {
                        Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center
                    })
                        using (var pen = new Pen(Color.Black))
                            using (var brush = new SolidBrush(Color.Black))
                            {
                                g.SmoothingMode     = SmoothingMode.AntiAlias;
                                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

                                // border
                                g.DrawRectangle(pen, 0, 0, w - 1, h - 1);

                                // stats
                                const int padding           = 4;
                                const int statCircleSize    = PedigreeCreatureCompact.DefaultStatSize * 3 / 2;
                                const int statRadius        = statCircleSize / 2;
                                const int radiusInnerCircle = statRadius / 7;
                                var       statLeftTopCoords = new Point(padding, padding);
                                var       center            = new Point(statLeftTopCoords.X + statRadius, statLeftTopCoords.Y + statRadius);
                                brush.Color = Color.White;
                                g.FillEllipse(brush, statLeftTopCoords.X, statLeftTopCoords.Y, statRadius * 2, statRadius * 2);
                                brush.Color = Color.Black;

                                var usedStats    = Enumerable.Range(0, Values.STATS_COUNT).Where(si => si != (int)StatNames.Torpidity && species.UsesStat(si)).ToArray();
                                var anglePerStat = 360f / usedStats.Length;
                                var i            = 0;
                                foreach (var si in usedStats)
                                {
                                    var angle = PedigreeCreatureCompact.AngleOffset + anglePerStat * i++;
                                    g.DrawPie(pen, statLeftTopCoords.X, statLeftTopCoords.Y, statCircleSize, statCircleSize, angle, anglePerStat);

                                    // text
                                    const int radiusPosition  = statRadius * 7 / 10;
                                    var       anglePosition   = Math.PI * 2 / 360 * (angle + anglePerStat / 2);
                                    const int statTexSizeHalf = 15;
                                    var       x = (int)Math.Round(radiusPosition * Math.Cos(anglePosition) + center.X - statTexSizeHalf);
                                    var       y = (int)Math.Round(radiusPosition * Math.Sin(anglePosition) + center.Y - statTexSizeHalf);
                                    g.DrawString(Utils.StatName(si, true, species.statNames), font, brush,
                                                 new RectangleF(x, y, statTexSizeHalf * 2, statTexSizeHalf * 2),
                                                 format);
                                }
                                brush.Color = Color.Gray;
                                g.FillEllipse(brush, center.X - radiusInnerCircle, center.Y - radiusInnerCircle, 2 * radiusInnerCircle, 2 * radiusInnerCircle);

                                // circles
                                const int textX      = 3 * padding + 6;
                                const int lineHeight = 15;
                                void CircleExplanation(Color circleColor, string text, int y, int circleSize, int circleOffset = 0)
                                {
                                    PedigreeCreatureCompact.DrawFilledCircle(g, brush, pen, circleColor, padding + circleOffset, y + lineHeight / 4 + circleOffset, circleSize);
                                    brush.Color = Color.Black;
                                    g.DrawString(text, font, brush, textX, y);
                                }

                                void RectangleExplanation(Color rectangleColor, string text, int y, int size)
                                {
                                    pen.Color = rectangleColor;
                                    pen.Width = 2;
                                    g.DrawRectangle(pen, padding, y, size, size);
                                    brush.Color = Color.Black;
                                    g.DrawString(text, font, brush, textX, y);
                                }

                                void ArrowExplanation(List <int[]> linesList, int lineStyle, string text, int y, int size)
                                {
                                    var yLine = y + lineHeight / 2;

                                    linesList.Add(new[] { padding, yLine, padding + size, yLine, lineStyle });
                                    brush.Color = Color.Black;
                                    g.DrawString(text, font, brush, textX, y);
                                }

                                int yText = statRadius * 2 + 4 * padding;
                                CircleExplanation(Utils.MutationMarkerColor, "mutation in stat", yText, 6);
                                yText += lineHeight;
                                CircleExplanation(Utils.MutationMarkerPossibleColor, "possible mutation in stat", yText, 6);
                                yText += lineHeight;
                                CircleExplanation(Color.Yellow, "mutation in color", yText, 4, 1);
                                yText += lineHeight;
                                CircleExplanation(Color.GreenYellow, "creature without mutations", yText, 6);
                                yText += lineHeight;
                                CircleExplanation(Utils.MutationColor, "creature mutations < limit", yText, 6);
                                yText += lineHeight;
                                CircleExplanation(Color.DarkRed, "creature mutations ≥ limit", yText, 6);
                                yText += lineHeight;
                                // rectangles
                                RectangleExplanation(Color.DodgerBlue, "selected creature", yText, 10);
                                yText += lineHeight;
                                RectangleExplanation(Utils.MutationMarkerColor, "creature with mutation", yText, 10);
                                yText += lineHeight;
                                // arrows
                                var lines = new[] { null, new List <int[]>(), null };
                                ArrowExplanation(lines[1], 1, "offspring", yText, 10);
                                yText += lineHeight;
                                ArrowExplanation(lines[1], 2, "stat inheritance", yText, 10);
                                yText += lineHeight;
                                ArrowExplanation(lines[1], 3, "stat inheritance with", yText, 10);
                                g.DrawString("possible mutation", font, brush, textX, yText + lineHeight);

                                DrawLines(g, lines);
                            }

            pb.SetImageAndDisposeOld(bmp);
        }