Raw File
PuppyForm.cs
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows;
using System.Windows.Forms;

namespace Puppy
{
    public partial class PuppyForm : Form
    {
        private TrackForm tf;
        private Track t;
        private DirectBitmap b;
        private DirectBitmap bb;
        private double windowWScale = 1;
        private double windowHScale = 1;
        private bool quality = true;
        private System.Drawing.Point mouseOffset = new System.Drawing.Point(0, 0);
        private bool squareLayout = true;
        public bool extendedDiagram = false;
        private PointF[] ClipPath;
        byte[] ClipBytes;
        public bool mousePressed = false;
        public double mouseX, mouseY;

        public PuppyForm(TrackForm tf)
        {
            this.tf = tf;
            t = tf.t;
            InitializeComponent();
            Recreate(t);
            Location = new System.Drawing.Point(tf.Size.Width, 0);
            Show();
        }

        private int WindowBaseSizeX() => squareLayout ? Program.PUPPY_WINDOW_SIZE_1 : (extendedDiagram ? Program.PUPPY_WINDOW_SIZE_3 : Program.PUPPY_WINDOW_SIZE_2);

        private int WindowBaseSizeY() => squareLayout ? Program.PUPPY_WINDOW_SIZE_1 : (extendedDiagram ? Program.PUPPY_WINDOW_SIZE_1 : Program.PUPPY_WINDOW_SIZE_2);

        private (int, int) ModeScale()
        {
            if (squareLayout) return (ClientSize.Width, ClientSize.Height);
            if (!extendedDiagram) return (ClientSize.Width * Program.PUPPY_WINDOW_SIZE_1 / Program.PUPPY_WINDOW_SIZE_2, ClientSize.Height * Program.PUPPY_WINDOW_SIZE_1 / Program.PUPPY_WINDOW_SIZE_2);
            return (ClientSize.Width * Program.PUPPY_WINDOW_SIZE_1 / Program.PUPPY_WINDOW_SIZE_3, ClientSize.Height);
        }

        public void Recreate(Track t, bool toggleExtended = false)
        {
            this.t = t;
            if (toggleExtended)
            {
                extendedDiagram = !extendedDiagram;
                t.ComputeCritical(extendedDiagram);
            }
            int n = Program.PUPPY_BITMAP_BASE_RESOLUTION;
            if (b != null) b.Dispose();
            b = new DirectBitmap(n, n);
            if (extendedDiagram) for (int x = 0; x < n; x++) t.PlotColumnExtended(b, x);
            else for (int x = 0; x < n; x++) t.PlotColumn(b, x);
            ResizeWindow(windowWScale, windowHScale);
        }

        public void ResizeWindow(double sw, double sh, bool toggleSquare = false)
        {
            if (toggleSquare)
            {
                squareLayout = !squareLayout;
                sw = windowWScale;
                sh = windowHScale;
            }
            int sizeX = (int)(sw * WindowBaseSizeX());
            int sizeY = (int)(sh * WindowBaseSizeY());
            if (sizeX > Screen.FromControl(this).Bounds.Width || sizeY > Screen.FromControl(this).Bounds.Height)
            {
                if (toggleSquare) squareLayout = !squareLayout;
                return;
            }
            int ox = ClientSize.Width;
            int oy = ClientSize.Height;
            ClientSize = new System.Drawing.Size(sizeX, sizeY);
            Location = new System.Drawing.Point(Location.X + (ox - ClientSize.Width) / 2, Location.Y + (oy - ClientSize.Height) / 2);
            windowWScale = sw;
            windowHScale = sh;
            tf.gd.pfScale = Math.Min(sw, sh);
            CreateClip();
            RedrawBackground();
        }

        private void CreateClip()
        {
            var (sw, sh) = ModeScale();
            if (!extendedDiagram) {
                ClipPath = new PointF[4] { new PointF(sw, 0), new PointF(0, sh), new PointF(sw * 0.5f, ClientSize.Height + 0.5f), new PointF(ClientSize.Width + 0.5f, sh * 0.5f) };
                ClipBytes = new byte[4] { 0, 1, 1, 129 };
                return;
            }
            int n = t.puppyDiagonal.Count / 2;
            int m = 2 * n + 2;
            ClipPath = new PointF[m];
            ClipBytes = new byte[m];
            for(int i = 0; i < n; i++)
            {
                PointF q = t.puppyDiagonal[i * 2].ToPointF();
                ClipPath[i] = new PointF(q.X * sw, (1 - q.Y) * sh);
                ClipPath[m - 1 - i] = new PointF((q.X + 1) * sw + 0.5f, (1 - q.Y) * sh);
                ClipBytes[i + 1] = 1;
                ClipBytes[n + i + 1] = 1;
            }
            ClipPath[n] = new PointF(sw, 0);
            ClipPath[n + 1] = new PointF(2 * sw + 0.5f, 0);
            ClipBytes[0] = 0;
            ClipBytes[m - 1] = 129;
        }

        private void RedrawBackground()
        {
            if (b == null) return;
            if (bb != null) bb.Dispose();
            int sw = (int)(windowWScale * Program.PUPPY_WINDOW_SIZE_1);
            int sh = (int)(windowHScale * Program.PUPPY_WINDOW_SIZE_1);
            bb = new DirectBitmap(sw, sh);
            using (var g = Graphics.FromImage(bb.Bitmap))
            {
                g.CompositingMode = CompositingMode.SourceCopy;
                g.CompositingQuality = CompositingQuality.HighSpeed;
                g.PixelOffsetMode = PixelOffsetMode.Half;
                g.InterpolationMode = InterpolationMode.NearestNeighbor;
                g.DrawImage(b.Bitmap, new RectangleF(0, 0, sw, sh));
                g.CompositingMode = CompositingMode.SourceOver;
                if (quality)
                {
                    g.SmoothingMode = SmoothingMode.HighQuality;
                    g.CompositingQuality = CompositingQuality.HighQuality;
                }
                else
                {
                    g.SmoothingMode = SmoothingMode.HighSpeed;
                    g.CompositingQuality = CompositingQuality.HighSpeed;
                }
                tf.gd.DrawPSegmentList(g, t.unstableCrit, sw, sh, Program.UNSTABLE_COLOR);
                tf.gd.DrawPSegmentList(g, t.stableCrit, sw, sh, Program.STABLE_COLOR);
                tf.gd.DrawDiagramDiag(g, t.puppyDiagonal, sw, sh, Program.STABLE_COLOR, squareLayout, extendedDiagram);
            }
            tf.gd.Refresh(false, true, false);
        }

        private void PuppyForm_Paint(object sender, PaintEventArgs e)
        {
            if (bb == null) return;
            tf.gd.refreshingPf = false;
            Graphics g = e.Graphics;
            g.CompositingMode = CompositingMode.SourceCopy;
            g.CompositingQuality = CompositingQuality.HighSpeed;
            g.PixelOffsetMode = PixelOffsetMode.Half;
            g.InterpolationMode = InterpolationMode.NearestNeighbor;
            var (sw, sh) = ModeScale();
            if (!squareLayout)
            {
                g.Clip.Dispose();
                g.Clip = new Region(new GraphicsPath(ClipPath, ClipBytes));
            }
            g.DrawImage(bb.Bitmap, new RectangleF(0, 0, sw, sh));
            if (!squareLayout)
            {
                g.DrawImage(bb.Bitmap, new RectangleF(sw, 0, sw, sh));
                if (!extendedDiagram) g.DrawImage(bb.Bitmap, new RectangleF(0, sh, sw, sh));
            }
            g.CompositingMode = CompositingMode.SourceOver;
            if (quality)
            {
                g.SmoothingMode = SmoothingMode.HighQuality;
                g.CompositingQuality = CompositingQuality.HighQuality;
            }
            else
            {
                g.SmoothingMode = SmoothingMode.HighSpeed;
                g.CompositingQuality = CompositingQuality.HighSpeed;
            }

            if (mousePressed && extendedDiagram)
            {
                double px = (mouseX.Frac() * sw);
                double py = ((1 - mouseY.Frac()) * sh);
                tf.gd.DiagramCircle(g, new Vector(px, py));
                if (!squareLayout) tf.gd.DiagramCircle(g, new Vector(px + sw, py));
            }
            else
            {
                double px = t.DrawHuman;
                double py = t.captured ? t.DrawHuman : t.Puppy;
                //double py = t.reached ? t.DrawPuppy : t.Puppy;
                //double py = t.Human == t.DrawHuman ? t.DrawPuppy : t.Puppy;

                bool diag = px == py;
                bool left = px < py;

                if (extendedDiagram)
                {
                    if (!squareLayout && py == 0) left = true;
                    if (tf.mousePressed) py = tf.mouseAttracted;
                    else py = t.ExtendedConversion(py, px);
                }

                double cx = px;
                double cy = 1 - py;
                if (!squareLayout && left)
                {
                    if (!extendedDiagram && cy < cx) cy += 1;
                    else cx += 1;
                }
                tf.gd.DiagramCircle(g, new Vector(cx * sw, cy * sh));
                if (!squareLayout && diag)
                {
                    tf.gd.DiagramCircle(g, new Vector((cx + 1) * sw, cy * sh));
                    if (!extendedDiagram)
                    {
                        tf.gd.DiagramCircle(g, new Vector(cx * sw, (cy + 1) * sh));
                        tf.gd.DiagramCircle(g, new Vector((cx + 1) * sw, (cy - 1) * sh));
                        tf.gd.DiagramCircle(g, new Vector((cx - 1) * sw, (cy + 1) * sh));
                    }
                }
            }
        }

        private void PuppyForm_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Back: tf.RecreateTrack(t.trackType); break;
                case Keys.D1: tf.RecreateTrack(1); break;
                case Keys.D2: tf.RecreateTrack(2); break;
                case Keys.D3: tf.RecreateTrack(3); break;
                case Keys.D4: tf.RecreateTrack(4); break;
                case Keys.D5: tf.RecreateTrack(5); break;
                case Keys.D6: tf.RecreateTrack(6); break;
                case Keys.D7: tf.RecreateTrack(7); break;
                case Keys.D8: tf.RecreateTrack(8); break;
                case Keys.D9: tf.RecreateTrack(9); break;
                case Keys.D0: tf.RecreateTrack(0); break;
                case Keys.P: Hide(); break;
                case Keys.H: TrackForm.ToggleForm(tf.hf); break;
                case Keys.C: tf.ChamferTrack(); break;
                case Keys.Q: quality = !quality; RedrawBackground(); break;
                case Keys.Enter: ResizeWindow(0, 0, true); break;
                case Keys.Space: Recreate(t, true); break;
                case Keys.PageUp: ResizeWindow(windowWScale * Program.PUPPY_DIAGRAM_RESIZE_FACTOR, windowHScale * Program.PUPPY_DIAGRAM_RESIZE_FACTOR); break;
                case Keys.PageDown: ResizeWindow(windowWScale / Program.PUPPY_DIAGRAM_RESIZE_FACTOR, windowHScale / Program.PUPPY_DIAGRAM_RESIZE_FACTOR); break;
                case Keys.S: ResizeWindow(windowWScale, windowHScale * Program.PUPPY_DIAGRAM_RESIZE_FACTOR); break;
                case Keys.A: ResizeWindow(windowWScale, windowHScale / Program.PUPPY_DIAGRAM_RESIZE_FACTOR); break;
                case Keys.X: ResizeWindow(windowWScale * Program.PUPPY_DIAGRAM_RESIZE_FACTOR, windowHScale); break;
                case Keys.Z: ResizeWindow(windowWScale / Program.PUPPY_DIAGRAM_RESIZE_FACTOR, windowHScale); break;
                case Keys.R: ResizeWindow(1, 1); break;
                case Keys.Up: tf.gd.pfDiagramScale *= Program.FEATURE_RESIZE_FACTOR; RedrawBackground(); break;
                case Keys.Down: tf.gd.pfDiagramScale /= Program.FEATURE_RESIZE_FACTOR; RedrawBackground(); break;
            }
        }

        private void HandleMouse(MouseEventArgs e)
        {
            if (tf.gd == null || t == null) return;
            if ((e.Button & MouseButtons.Left) == MouseButtons.Left) {
                var (sw, sh) = ModeScale();
                mousePressed = true;
                mouseX = (double)e.X / sw;
                mouseY = 1 - (double)e.Y / sh;
                t.RelocateHuman(mouseX, true);
                t.RelocatePuppy(extendedDiagram ? t.ExtendedConversion2(mouseY, mouseX).Item1 : mouseY, 0);
            }
        }

        private void PuppyForm_MouseMove(object sender, MouseEventArgs e)
        {
            HandleMouse(e);
            if ((e.Button & MouseButtons.Right) == MouseButtons.Right)
            {
                System.Drawing.Point mousePos = Control.MousePosition;
                mousePos.Offset(mouseOffset);
                Location = mousePos;
            }
        }

        private void PuppyForm_MouseDown(object sender, MouseEventArgs e)
        {
            HandleMouse(e);
            if ((e.Button & MouseButtons.Right) == MouseButtons.Right)
                mouseOffset = new System.Drawing.Point(-e.X, -e.Y);
        }

        private void PuppyForm_MouseUp(object sender, MouseEventArgs e)
        {
            if (tf.gd == null || t == null) return;
            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                var (sw, sh) = ModeScale();
                mousePressed = false;
                mouseY = 1 - (double)e.Y / sh;
                if (extendedDiagram)
                {
                    mouseX = (double)e.X / sw;
                    var (w, dir) = t.ExtendedConversion2(mouseY, mouseX);
                    t.RelocatePuppy(w, dir, true);
                }
                else t.RelocatePuppy(mouseY, 0, true);
            }
        }
    }
}
back to top