Raw File
Track.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows;
using System.Windows.Forms;

namespace Puppy
{
    public class Track
    {
        private static double animationSpeed = 1;
        private static double margin = 0.01;

        public int trackType;
        private List<Vector> p = new List<Vector>();
        public Vector size = new Vector(2, 2);
        private double[] l;
        private double[] ls;
        private double tl;
        private Vector[] nor;
        private double[] an;
        private double[] ans;
        private double ta;
        private double[] hd;
        private double edgeFactor;
        private double angleFactor;
        private double human;
        private double puppy;
        private double drawHuman;
        private double drawPuppy;
        public bool captured = false;
        public bool reached = false;
        public bool attract = true;
        private TrackForm tf;
        private Timer timer;
        private Stopwatch stopwatch;
        public List<Vector> stableCrit;
        public List<Vector> unstableCrit;
        public List<Vector> humanDiagram;
        public List<Vector> humanDiagramBase;
        public List<Vector> puppyDiagonal;
        public CritData[,] critData;
        private int diagSign;
        private double diagMinY;
        private double diagMaxY;

        public struct CritData
        {
            public Vector? stable1;
            public Vector? stable2;
            public Vector? unstable1;
            public Vector? unstable2;
            public bool forward;
        }

        public double Human { get => human; set => human = value.Frac(); }
        public double Puppy { get => puppy; set => puppy = value.Frac(); }
        public double DrawHuman { get => drawHuman; set => drawHuman = value.Frac(); }
        public double DrawPuppy { get => drawPuppy; set => drawPuppy = value.Frac(); }

        private void CreateStar(int n = 20, double r1 = 1, double r2 = 0.55)
        {
            for (int i = 0; i < n; i++)
            {
                double a = 2 * Math.PI * i / n;
                double r = (i & 1) == 1 ? r1 : r2;
                p.Add(Vector.Multiply(r, new Vector(Math.Cos(a), Math.Sin(a))));
            }
            ComputeLengths();
            Human = ls[n / 2 - 1];
            Puppy = ls[n / 2 + 1];
        }

        private void CreateChamferedStar(int n = 26, double r1 = 1, double r2 = 0.6, double d = 0.05)
        {
            for (int i = 0; i < n; i++)
            {
                double a = 2 * Math.PI * i / n;
                double r = (i & 1) == 1 ? r1 : r2;
                p.Add(Vector.Multiply(r, new Vector(Math.Cos(a), Math.Sin(a))));
                p.Add(Vector.Multiply(r, new Vector(Math.Cos(a+d), Math.Sin(a+d))));
            }
            ComputeLengths();
            Human = ls[n / 2 - 1];
            Puppy = ls[n / 2 + 1];
        }

        private void CreateDoubleLoop(int n = 10, double r1 = 0.85, double r2 = 0.7)
        {
            for (int i = 0; i < n; i++)
            {
                double a = 2 * Math.PI * (i + 0.5) / n;
                p.Add(Vector.Multiply(r1, new Vector(Math.Cos(a), Math.Sin(a))));
            }
            for (int i = 0; i < n; i++)
            {
                double a = 2 * Math.PI * (i + 0.5) / n;
                p.Add(Vector.Multiply(r2, new Vector(Math.Cos(a), Math.Sin(a))));
            }
            ComputeLengths();
            Human = ls[n / 2];
            Puppy = ls[n + n / 2];
        }

        private void CreateLimacon(int n = 200, double a = 0.2, double b = 1) // r = a + b cos(theta)
        {
            for (int i = 0; i < n; i++)
            {
                double t = 2 * Math.PI * i / n;
                double r = a + b * Math.Cos(t);
                p.Add(Vector.Multiply(r, new Vector(-Math.Cos(t), Math.Sin(t))));
            }
            ComputeLengths();
            Human = 0;
            Puppy = 0.5;
        }

        private void CreateFigureEight(int n = 200, double a = 0.8, double b = 0.5)
        {
            for (int i = 0; i < n; i++)
            {
                double t = 2 * Math.PI * (i + 0.5) / n;
                p.Add(new Vector(a * Math.Cos(t), b * Math.Sin(2 * t)));
            }
            ComputeLengths();
            Human = (ls[n / 2] + ls[n / 2 - 1]) / 2;
            Puppy = (ls[n] + ls[n - 1]) / 2;
        }

        private void CreateDoubleFigureEight(int n = 150, double a = 0.8, double b = 0.5, double d = 0.1)
        {
            for (int i = 0; i < n; i++)
            {
                double t = 2 * Math.PI * i / n;
                p.Add(new Vector(-a * Math.Cos(t - Math.PI / 2) - d * Math.Sin(t / 2) / 2, b * Math.Sin(2 * t - Math.PI) + d * i / n));
            }
            for (int i = 0; i < n; i++)
            {
                double t = 2 * Math.PI * i / n;
                p.Add(new Vector(-a * Math.Cos(t - Math.PI / 2) + d * Math.Sin(t / 2) / 2, b * Math.Sin(2 * t - Math.PI) + d * (n - i) / n));
            }
            ComputeLengths();
            Human = 0.125;
            Puppy = 0.5;
        }

        private void CreateC(int n = 100, double a = 0.9, double b = 0.3, double c = 0.5)
        {
            int m = n / 2;
            for (int i = 0; i <= n; i++)
            {
                double t = Math.PI * i / n;
                p.Add(new Vector(-a * Math.Sin(t), a * Math.Cos(t)));
            }
            for (int i = 0; i <= m; i++)
            {
                double t = Math.PI * i / m;
                p.Add(new Vector(c + (a - b) * Math.Sin(t) / 2, -(a - b) * Math.Cos(t) / 2 - (a + b) / 2));
            }
            for (int i = 0; i <= n; i++)
            {
                double t = Math.PI * i / n;
                p.Add(new Vector(- b * Math.Sin(t), - b * Math.Cos(t)));
            }
            for (int i = 0; i <= m; i++)
            {
                double t = Math.PI * i / m;
                p.Add(new Vector(c + (a - b) * Math.Sin(t) / 2, -(a - b) * Math.Cos(t) / 2 + (a + b) / 2));
            }
            ComputeLengths();
            Human = 0.975;
            Puppy = 0.4;
        }

        private void CreateSpiral(int n = 11, int k = 2)
        {
            double d = 2 * k * Math.PI / n;
            for (int i = 0; i <= n; i++)
            {
                double t = i * d;
                p.Add(new Vector(t * Math.Cos(t), t * Math.Sin(t)));
            }
            for (int i = -n / (2 * k); i < n; i++)
            {
                double t = (n - i) * d;
                p.Add(new Vector(-t * Math.Cos(t), -t * Math.Sin(t)));
            }
            ComputeLengths();
            Human = (ls[n / 2] + ls[n / 2 - 1]) / 2;
            Puppy = (ls[n] + ls[n - 1]) / 2;
        }

        private void CreateDoubleSpiral(int n = 200, int k = 2)
        {
            double d = 2 * k * Math.PI / n;
            double m = (2 * k + 0.5) * Math.PI;
            for (int i = 0; i <= n; i++)
            {
                double t = i * d;
                p.Add(new Vector(t * Math.Cos(t) - m, t * Math.Sin(t)));
                if (i < n / 2) i++;
                if (i < n / 3) i++;
                if (i < n / 4) i++;
            }
            for (int i = n + n / (2 * k) - 1; i >= 1; i--)
            {
                double t = i * d;
                p.Add(new Vector(t * Math.Cos(t) + m, t * Math.Sin(t)));
                if (i < n / 2) i--;
                if (i < n / 3) i--;
                if (i < n / 4) i--;
            }
            for (int i = 0; i <= n; i++)
            {
                double t = i * d;
                p.Add(new Vector(-t * Math.Cos(t) + m, -t * Math.Sin(t)));
                if (i < n / 2) i++;
                if (i < n / 3) i++;
                if (i < n / 4) i++;
            }
            for (int i = n + n / (2 * k) - 1; i >= 1; i--)
            {
                double t = i * d;
                p.Add(new Vector(-t * Math.Cos(t) - m, -t * Math.Sin(t)));
                if (i < n / 2) i--;
                if (i < n / 3) i--;
                if (i < n / 4) i--;
            }
            ComputeLengths();
            Human = (ls[n / 2] + ls[n / 2 - 1]) / 2;
            Puppy = (ls[n] + ls[n - 1]) / 2;
        }

        private void CreateOrtho()
        {
            p.Add(new Vector(-0, 0)); p.Add(new Vector(-0, 2)); p.Add(new Vector(1, 2)); p.Add(new Vector(1, 4)); p.Add(new Vector(-2, 4)); p.Add(new Vector(-2, 8));
            p.Add(new Vector(-0, 8)); p.Add(new Vector(-0, 6)); p.Add(new Vector(2, 6)); p.Add(new Vector(2, 9)); p.Add(new Vector(-1, 9)); p.Add(new Vector(-1, 11));
            p.Add(new Vector(-21, 11)); p.Add(new Vector(-21, 7)); p.Add(new Vector(-20, 7)); p.Add(new Vector(-20, 3)); p.Add(new Vector(-18, 3)); p.Add(new Vector(-18, 0));
            p.Add(new Vector(-13, 0)); p.Add(new Vector(-13, 2)); p.Add(new Vector(-15, 2)); p.Add(new Vector(-15, 6)); p.Add(new Vector(-16, 6)); p.Add(new Vector(-16, 9));
            p.Add(new Vector(-3, 9)); p.Add(new Vector(-3, 7)); p.Add(new Vector(-8, 7)); p.Add(new Vector(-8, 2)); p.Add(new Vector(-11, 2)); p.Add(new Vector(-11, 0));
            p.Add(new Vector(-6, 0)); p.Add(new Vector(-6, 5)); p.Add(new Vector(-4, 5)); p.Add(new Vector(-4, 3)); p.Add(new Vector(-2, 3)); p.Add(new Vector(-2, 0));
            ComputeLengths();
            Human = ls[17];
            Puppy = ls[29];
        }

        /*        private void CreateTrefoil(int n = 100, double a = 0.5, double b = 1)
                {
                    for (int i = 0; i < n; i++)
                    {
                        double t = 2 * Math.PI * i / n;
                        double r = a + b * Math.Cos(t);
                        p.Add(Vector.Multiply(r, new Vector(Math.Cos(t) - (a + b) / 2, Math.Sin(t))));
                    }
                    ComputeLengths();
                    Human = ls[0];
                    Puppy = ls[n / 2];
                }*/

        public Track(TrackForm tf, int type, bool extended)
        {
            trackType = type;
            switch (type)
            {
                case 1: CreateOrtho(); break;
                case 2: CreateStar(24, 1, 0.65); break; // CreateStar(30, 0.8, 0.5);32, 0.6, 0.45
                case 3: CreateSpiral(); break;
                case 4: CreateDoubleSpiral(); break;
                case 5: CreateLimacon(); break;
                case 6: CreateFigureEight(); break;
                case 7: CreateDoubleFigureEight(); break;
                case 8: CreateChamferedStar(); break;
                default: CreateC(); break;
            }
            ComputeCritical(extended);
            this.tf = tf;
            stopwatch = new Stopwatch();
            timer = new Timer { Interval = Program.TIMER_INTERVAL_MILLISECONDS };
            timer.Tick += new EventHandler(Animate);
            DrawHuman = Human;
            DrawPuppy = Puppy;
            MovePuppy(Human, 0, true);
        }

        public Track(TrackForm tf, int type, List<Vector> p, bool extended, double human = 0, double puppy = 0.5)
        {
            trackType = type;
            this.tf = tf;
            this.p = p;
            DrawHuman = Human = human;
            DrawPuppy = Puppy = puppy;
            ComputeLengths();
            ComputeCritical(extended);
            stopwatch = new Stopwatch();
            timer = new Timer { Interval = Program.TIMER_INTERVAL_MILLISECONDS };
            timer.Tick += new EventHandler(Animate);
            MovePuppy(Human, 0, true);
        }

        public Vector GetP(int n) => p[n.Mod(p.Count)];

        public Vector GetN(int n) => nor[n.Mod(nor.Length)];

        public double GetH(int n) => hd[n.Mod(hd.Length)];

        private void ComputeLengths()
        {
            double minX, maxX, minY, maxY;
            minX = maxX = p[0].X;
            minY = maxY = p[0].Y;
            for (int i = 1; i < p.Count; i++)
            {
                minX = Math.Min(p[i].X, minX);
                maxX = Math.Max(p[i].X, maxX);
                minY = Math.Min(p[i].Y, minY);
                maxY = Math.Max(p[i].Y, maxY);
            }
            Vector d = new Vector((minX + maxX) / 2, (minY + maxY) / 2);
            for (int i = 0; i < p.Count; i++)
                p[i] = Vector.Subtract(p[i], d);
            double w = maxX - minX;
            double h = maxY - minY;
            double s;
            if (w > h)
            {
                s = 1 / w;
                size = new Vector(1, h * s);
            }
            else
            {
                s = 1 / h;
                size = new Vector(w * s, 1);
            }
            for (int i = 0; i < p.Count; i++)
                p[i] = Vector.Multiply(p[i], s);
            l = new double[p.Count];
            ls = new double[p.Count + 1];
            nor = new Vector[p.Count];
            ls[0] = 0;
            for (int i = 0; i < l.Length; i++)
            {
                Vector dif = Vector.Subtract(GetP(i + 1), GetP(i));
                l[i] = dif.Length;
                ls[i + 1] = ls[i] + l[i];
                nor[i] = new Vector(-dif.Y / l[i], dif.X / l[i]);
            }
            tl = ls[ls.Length - 1];
            ls[ls.Length - 1] = 1;
            if (tl == 0) return;
            for (int i = 0; i < p.Count; i++) ls[i] /= tl;
        }

        private int BinaryLocate(double w, double[] a)
        {
            w = w.Frac();
            int left = 0;
            int right = a.Length - 2;
            while (left <= right)
            {
                int mid = (left + right) / 2;
                if (w < a[mid]) right = mid - 1;
                else if (w >= a[mid + 1]) left = mid + 1;
                else return mid;
            }
            return 0;
        }

        private int BinaryLocateL(double w) => BinaryLocate(w, ls);

        private (int, bool, Vector, double) BinaryLocateA(double w)
        {
            int i = BinaryLocate(w, hd);
            int j = i / 2;
            bool b = i == 2 * j;
            double u = b ? ls[j] + (w - hd[i]) / edgeFactor : an[j] * (w - hd[i]) / (hd[i + 1] - hd[i]);
            if (b) return (j, b, Locate(u, j).Item1, u);
            return (j, b, GetP(j + 1), u);
        }

        private (Vector, int) Locate(double w)
        {
            w = w.Frac();
            int i = BinaryLocateL(w);
            return (Locate(w, i).Item1, i);
        }

        private (Vector, double) Locate(double w, int i, bool frac = true)
        {
            if (frac) w = w.Frac();
            double l1 = w - ls[i];
            double l2 = ls[i + 1] - ls[i];
            if (l2 != 0) l1 /= l2;
            return (GetP(i).Interpolate(GetP(i + 1), l1), l1);
        }

        private (Vector, double, int) ClosestOnSeg(int i, Vector q, bool angles = false)
        {
            i = i.Mod(p.Count);
            Vector a = GetP(i);
            Vector b = GetP(i + 1);
            Vector ab = Vector.Subtract(b, a);
            Vector aq = Vector.Subtract(q, a);
            double x = Vector.Multiply(ab, aq) / ab.LengthSquared;
            if (x <= 0) return (a, angles ? hd[2 * i] : ls[i], -1);
            if (x >= 1) return (b, angles ? hd[2 * i + 1] : ls[i + 1], 1);
            Vector v = a.Interpolate(b, x);
            double d = angles? hd[2 * i] * (1 - x) + hd[2 * i + 1] * x : ls[i] * (1 - x) + ls[i + 1] * x;
            return (v, d, 0);
        }

        public (double, double) ClosestOnTrack(Vector q, bool angle)
        {
            double d = -1;
            double w = 0;
            int ii = 0;
            for(int i = 0; i < p.Count; i++)
            {
                var (v, u, _) = ClosestOnSeg(i, q);
                double dist = q.DistanceSquared(v);
                if (d == -1 || dist < d)
                {
                    d = dist;
                    w = u;
                    ii = i;
                }
            }
            if (!angle) return (w.Frac(), 0);
            var (_, ww, dir) = ClosestOnSeg(ii, q, true);
            if (dir == 0) return (w.Frac(), ww);
            if (dir == -1)
            {
                ii--;
                if (ii < 0) ii += p.Count;
            }
            double t = hd[2 * ii + 1];
            double tota = an[ii];
            double r = (hd[2 * ii + 2] - t) / tota;
            Vector norm = GetN(ii);
            if (tota > 0) norm.Negate();
            double a = norm.Angle(Vector.Subtract(q, GetP(ii + 1)));

            return (w.Frac(), t + a * r);
        }

        public void PlotColumn(DirectBitmap b, int x, double ofs = 0.5)
        {
            int res = b.Height;
            Vector q = Locate((double)(x + ofs) / res).Item1;
            int y1 = res - (int)(ls[0] * res + ofs);
            for (int i = 0; i < p.Count; i++)
            {
                int y2 = y1;
                y1 = res - (int)(ls[i + 1] * res + ofs);
                var (_, w, j) = ClosestOnSeg(i, q);
                if (j == 1) b.VertLine(x, y1, y2 - 1, Program.FORWARD_CONFIGURATION_COLOR);
                else if (j == -1) b.VertLine(x, y1, y2 - 1, Program.BACKWARD_CONFIGURATION_COLOR);
                else
                {
                    int y3 = res - (int)(w * res + ofs);
                    b.VertLine(x, y1, y3 - 1, Program.BACKWARD_CONFIGURATION_COLOR);
                    b.VertLine(x, y3, y2 - 1, Program.FORWARD_CONFIGURATION_COLOR);
                }
            }
        }

        public void PlotColumnExtended(DirectBitmap b, int x, double ofs = 0.5)
        {
            int res = b.Height;
            double xx = (double)(x + ofs) / res;
            var (q, jj) = Locate(xx);
            for (int i = 0; i < p.Count; i++)
            {
                int y1 = res - (int)(hd[2 * i + 1] * res + ofs);
                int y2 = res - (int)(hd[2 * i] * res + ofs);
                var (_, w, j) = ClosestOnSeg(i, q, true);
                if (j == 1) b.VertLine(x, y1, y2 - 1, Program.FORWARD_CONFIGURATION_COLOR);
                else if (j == -1) b.VertLine(x, y1, y2 - 1, Program.BACKWARD_CONFIGURATION_COLOR);
                else
                {
                    int y3 = res - (int)(w * res + ofs);
                    b.VertLine(x, y1, y3 - 1, Program.BACKWARD_CONFIGURATION_COLOR);
                    b.VertLine(x, y3, y2 - 1, Program.FORWARD_CONFIGURATION_COLOR);
                }
            }
            for (int i = 0; i < p.Count; i++)
            {
                double h1 = hd[2 * i + 1];
                double h2 = hd[2 * i + 2];

                int y1 = res - (int)(h2 * res + ofs);
                int y2 = res - (int)(h1 * res + ofs);

                CritData c = critData[jj, i];
                if (c.stable1.HasValue && c.stable1.Value.X <= xx && xx <= c.stable2.Value.X)
                {
                    double d = c.stable2.Value.X - c.stable1.Value.X;
                    double w = d == 0 ? (c.stable1.Value.Y + c.stable2.Value.Y) / 2 : c.stable1.Value.Y + (xx - c.stable1.Value.X) * (c.stable2.Value.Y - c.stable1.Value.Y) / d;
                    int y3 = res - (int)(w * res + ofs);
                    b.VertLine(x, y1, y3 - 1, Program.BACKWARD_CONFIGURATION_COLOR);
                    b.VertLine(x, y3, y2 - 1, Program.FORWARD_CONFIGURATION_COLOR);
                }
                else if (c.unstable1.HasValue && c.unstable1.Value.X <= xx && xx <= c.unstable2.Value.X)
                {
                    double d = c.unstable2.Value.X - c.unstable1.Value.X;
                    double w = d == 0 ? (c.unstable1.Value.Y + c.unstable2.Value.Y) / 2 : c.unstable1.Value.Y + (xx - c.unstable1.Value.X) * (c.unstable2.Value.Y - c.unstable1.Value.Y) / d;
                    int y3 = res - (int)(w * res + ofs);
                    b.VertLine(x, y1, y3 - 1, Program.FORWARD_CONFIGURATION_COLOR);
                    b.VertLine(x, y3, y2 - 1, Program.BACKWARD_CONFIGURATION_COLOR);
                }
                else if (c.stable1.HasValue)
                {
                    double mid = (h1 + h2) / 2;
                    if (c.stable2.Value.X < xx) b.VertLine(x, y1, y2 - 1, c.stable2.Value.Y > mid ? Program.FORWARD_CONFIGURATION_COLOR : Program.BACKWARD_CONFIGURATION_COLOR);
                    else b.VertLine(x, y1, y2 - 1, c.stable1.Value.Y > mid ? Program.FORWARD_CONFIGURATION_COLOR : Program.BACKWARD_CONFIGURATION_COLOR);
                }
                else if (c.unstable1.HasValue)
                {
                    double mid = (h1 + h2) / 2;
                    if (c.unstable2.Value.X < xx) b.VertLine(x, y1, y2 - 1, c.unstable2.Value.Y < mid ? Program.FORWARD_CONFIGURATION_COLOR : Program.BACKWARD_CONFIGURATION_COLOR);
                    else b.VertLine(x, y1, y2 - 1, c.unstable1.Value.Y < mid ? Program.FORWARD_CONFIGURATION_COLOR : Program.BACKWARD_CONFIGURATION_COLOR);
                }
                else b.VertLine(x, y1, y2 - 1, c.forward ? Program.FORWARD_CONFIGURATION_COLOR : Program.BACKWARD_CONFIGURATION_COLOR);
            }
        }

        public int LeftRightTurn(Vector a, Vector b)
        {
            double c = Vector.CrossProduct(a, b);
            if (c < 0) return -1;
            if (c > 0) return 1;
            return 0;
        }

        public int LeftRightTurn(Vector p1, Vector p2, Vector p3) => LeftRightTurn(Vector.Subtract(p2, p1), Vector.Subtract(p3, p1));

        public void HumanDiagramAdd(double x1, double y1, double x2, double y2)
        {
            humanDiagram.Add(new Vector(x1, y1));
            humanDiagram.Add(new Vector(x2, y2));
            if (x1 < margin || x2 < margin)
            {
                humanDiagram.Add(new Vector(x1 + 1, y1));
                humanDiagram.Add(new Vector(x2 + 1, y2));
            }
            if (x1 > 1 - margin || x2 > 1 - margin)
            {
                humanDiagram.Add(new Vector(x1 - 1, y1));
                humanDiagram.Add(new Vector(x2 - 1, y2));
            }
        }

        public void HumanDiagramInterpAdd(Vector norm, Vector u1, Vector u2, double t, double r, bool invert, int n)
        {
            double x1, y1, x2 = 0, y2 = 0;
            for (int i = 0; i <= n; i++)
            {
                x1 = x2;
                y1 = y2;
                Vector u = u1.Interpolate(u2, (double)i / n);
                x2 = t + norm.Angle(u) * r;
                y2 = invert ? -u.Length : u.Length;
                if (i > 0) HumanDiagramAdd(x1, y1, x2, y2);
            }
        }

        public void CriticalSegSeg(int i, int j, bool extended)
        {
            double p1 = ls[i];
            double p2 = ls[i + 1];
            double q1 = ls[j];
            double q2 = ls[j + 1];
            double h1 = hd[2 * j];
            double h2 = hd[2 * j + 1];
            Vector a = Vector.Subtract(GetP(j + 1), GetP(j));
            Vector b1 = Vector.Subtract(GetP(i), GetP(j));
            Vector b2 = Vector.Subtract(GetP(i + 1), GetP(j));
            double s = 1 / (l[j] * tl);
            double w1 = q1 + Vector.Multiply(a, b1) * s;
            double w2 = q1 + Vector.Multiply(a, b2) * s;
            bool inw1 = (q1 <= w1 && w1 <= q2);
            bool inw2 = (q1 <= w2 && w2 <= q2);
            bool added = false;
            double px1 = 0, py1 = 0, px2 = 0, py2 = 0, hx1 = 0, hy1 = 0, hx2 = 0, hy2 = 0;
            if (inw1)
            {
                var (k1, l1) = Locate(w1, j, false);
                double x = h1 + l1 * (h2 - h1);
                px1 = p1;
                py1 = extended ? x : w1;
                hx1 = x;
                hy1 = Vector.Multiply(GetN(j), Vector.Subtract(GetP(i), k1));
                added = true;
            }
            if (inw2) {
                var (k2, l2) = Locate(w2, j, false);
                double x = h1 + l2 * (h2 - h1);
                px2 = p2;
                py2 = extended ? x : w2;
                hx2 = x;
                hy2 = Vector.Multiply(GetN(j), Vector.Subtract(GetP(i + 1), k2));
                added = true;
            }
            if (!inw1 || !inw2)
            {
                if (inw1 || inw2)
                {
                    double u, x;
                    int jj;
                    if ((w1 > w2) == inw1)
                    {
                        u = q1;
                        x = h1;
                        jj = j;
                    }
                    else
                    {
                        u = q2;
                        x = h2;
                        jj = j + 1;
                    }
                    double r = (p2 - p1) / (w2 - w1);
                    double v = inw1 ? p1 + r * (u - w1) : p2 + r * (u - w2);
                    Vector k = Locate(v, i, false).Item1;
                    if (inw1)
                    {
                        px2 = v;
                        py2 = extended ? x : u;
                        hx2 = x;
                        hy2 = Vector.Multiply(GetN(j), Vector.Subtract(k, GetP(jj)));
                    }
                    else
                    {
                        px1 = v;
                        py1 = extended ? x : u;
                        hx1 = x;
                        hy1 = Vector.Multiply(GetN(j), Vector.Subtract(k, GetP(jj)));
                    }
                }
                else if (w1 < q1 && w2 > q2 || w2 < q1 && w1 > q2)
                {
                    double r = (p2 - p1) / (w2 - w1);
                    double v1 = p1 + r * (q1 - w1);
                    double v2 = p1 + r * (q2 - w1);
                    Vector k1 = Locate(v1, i, false).Item1;
                    Vector k2 = Locate(v2, i, false).Item1;
                    px1 = v1;
                    py1 = extended ? h1 : q1;
                    hx1 = h1;
                    hy1 = Vector.Multiply(GetN(j), Vector.Subtract(k1, GetP(j)));
                    px2 = v2;
                    py2 = extended ? h2 : q2;
                    hx2 = h2;
                    hy2 = Vector.Multiply(GetN(j), Vector.Subtract(k2, GetP(j + 1)));
                    added = true;
                }
            }
            if (added)
            {
                stableCrit.Add(new Vector(px1, py1));
                stableCrit.Add(new Vector(px2, py2));
                if (px1 < margin || px2 < margin)
                {
                    stableCrit.Add(new Vector(px1 + 1, py1));
                    stableCrit.Add(new Vector(px2 + 1, py2));
                }
                if (px1 > 1 - margin || px2 > 1 - margin)
                {
                    stableCrit.Add(new Vector(px1 - 1, py1));
                    stableCrit.Add(new Vector(px2 - 1, py2));
                }
                if (py1 < margin || py2 < margin)
                {
                    stableCrit.Add(new Vector(px1, py1 + 1));
                    stableCrit.Add(new Vector(px2, py2 + 1));
                }
                if (py1 > 1 - margin || py2 > 1 - margin)
                {
                    stableCrit.Add(new Vector(px1, py1 - 1));
                    stableCrit.Add(new Vector(px2, py2 - 1));
                }
                humanDiagram.Add(new Vector(hx1, hy1));
                humanDiagram.Add(new Vector(hx2, hy2));
                if (hx1 < margin || hx2 < margin)
                {
                    humanDiagram.Add(new Vector(hx1 + 1, hy1));
                    humanDiagram.Add(new Vector(hx2 + 1, hy2));
                }
                if (hx1 > 1 - margin || hx2 > 1 - margin)
                {
                    humanDiagram.Add(new Vector(hx1 - 1, hy1));
                    humanDiagram.Add(new Vector(hx2 - 1, hy2));
                }
            }
        }

        public (Vector?, double, Vector?, double, Vector?, double) ClipSeg(Vector? p1, double w1, Vector? p2, double w2, Vector q) // splits segment p1p2 through line orthogonal to unit vector q
        {
            if (!p1.HasValue || !p2.HasValue) return (null, 0, null, 0, null, 0);
            double u1 = Vector.Multiply(q, p1.Value);
            double u2 = Vector.Multiply(q, p2.Value);
            if (u1 >= 0 && u2 >= 0) return (null, 0, p1, w1, p2, w2); 
            if (u1 <= 0 && u2 <= 0) return (p1, w1, p2, w2, null, 0);
            double d = u1 / (u1 - u2);
            double v = w1 + d * (w2 - w1);
            Vector p3 = p1.Value.Interpolate(p2.Value, d);
            if (u1 < 0) return (p1, w1, p3, v, p2, w2);
            return (p2, w2, p3, v, p1, w1);
        }

        public void CriticalSegVert(int i, int j, bool extended)
        {
            bool added = !extended;
            int k = j == 0 ? p.Count : j;
            Vector q = Vector.Divide(Vector.Subtract(GetP(j), GetP(j - 1)), l[k - 1]);
            Vector? p1 = Vector.Subtract(GetP(i), GetP(j));
            Vector? p2 = Vector.Subtract(GetP(i + 1), GetP(j));
            double w1 = ls[i];
            double w2 = ls[i + 1];
            var (v1, a1, v2, a2, v3, a3) = ClipSeg(p1, w1, p2, w2, q);
            q = Vector.Divide(Vector.Subtract(GetP(j), GetP(j + 1)), l[j]);
            var (u1, b1, u2, b2, u3, b3) = ClipSeg(v2, a2, v3, a3, q);
            if (u3.HasValue) {
                if (!extended)
                {
                    stableCrit.Add(new Vector(b2, ls[j]));
                    stableCrit.Add(new Vector(b3, ls[j]));
                    if (j == 0)
                    {
                        stableCrit.Add(new Vector(b2, 1));
                        stableCrit.Add(new Vector(b3, 1));
                    }
                }
                if (i != j && i != k - 1)
                {
                    double t = hd[2 * k - 1];
                    double tota = an[k - 1];
                    double r = (hd[2 * k] - t) / tota;
                    Vector norm = GetN(k - 1);
                    if (tota > 0) norm.Negate();
                    double an1 = norm.Angle(u2.Value);
                    double an2 = norm.Angle(u3.Value);
                    double x1 = t + an1 * r;
                    double x2 = t + an2 * r;
                    added = true;
                    Vector z1, z2;
                    if (b2 <= b3)
                    {
                        z1 = new Vector(b2, x1);
                        z2 = new Vector(b3, x2);
                    }
                    else
                    {
                        z2 = new Vector(b2, x1);
                        z1 = new Vector(b3, x2);
                    }
                    critData[i, k - 1].stable1 = z1;
                    critData[i, k - 1].stable2 = z2;
                    if (extended)
                    {
                        stableCrit.Add(z1);
                        stableCrit.Add(z2);
                        if (x1 < margin || x2 < margin)
                        {
                            stableCrit.Add(new Vector(b2, x1 + 1));
                            stableCrit.Add(new Vector(b3, x2 + 1));
                        }
                        if (x1 > 1 - margin || x2 > 1 - margin)
                        {
                            stableCrit.Add(new Vector(b2, x1 - 1));
                            stableCrit.Add(new Vector(b3, x2 - 1));
                        }
                        if (b2 < margin || b3 < margin)
                        {
                            stableCrit.Add(new Vector(b2 + 1, x1));
                            stableCrit.Add(new Vector(b3 + 1, x2));
                        }
                        if (b2 > 1 - margin || b3 > 1 - margin)
                        {
                            stableCrit.Add(new Vector(b2 - 1, x1));
                            stableCrit.Add(new Vector(b3 - 1, x2));
                        }
                    }
                    int n = (int)(Math.Abs((norm.Angle(u3.Value) - norm.Angle(u2.Value)) * r) / Program.HUMAN_DIAGRAM_ANGLE_GRAIN) + 1;
                    if (n <= 1) HumanDiagramAdd(x1, tota > 0 ? -u2.Value.Length : u2.Value.Length, x2, tota > 0 ? -u3.Value.Length : u3.Value.Length);
                    else HumanDiagramInterpAdd(norm, u2.Value, u3.Value, t, r, tota > 0, n);
                }
            }
            (u1, b1, u2, b2, u3, b3) = ClipSeg(v1, a1, v2, a2, q);
            if (u1.HasValue)
            {
                if (!extended)
                {
                    unstableCrit.Add(new Vector(b1, ls[j]));
                    unstableCrit.Add(new Vector(b2, ls[j]));
                    if (j == 0)
                    {
                        unstableCrit.Add(new Vector(b1, 1));
                        unstableCrit.Add(new Vector(b2, 1));
                    }
                }
                if (i != j && i != k - 1)
                {
                    double t = hd[2 * k - 1];
                    double tota = an[k - 1];
                    double r = (hd[2 * k] - t) / tota;
                    Vector norm = GetN(k - 1);
                    if (tota < 0) norm.Negate();
                    double an1 = norm.Angle(u1.Value);
                    double an2 = norm.Angle(u2.Value);
                    double x1 = t + an1 * r;
                    double x2 = t + an2 * r;
                    added = true;
                    Vector z1, z2;
                    if (b1 <= b2)
                    {
                        z1 = new Vector(b1, x1);
                        z2 = new Vector(b2, x2);
                    }
                    else
                    {
                        z2 = new Vector(b1, x1);
                        z1 = new Vector(b2, x2);
                    }
                    critData[i, k - 1].unstable1 = z1;
                    critData[i, k - 1].unstable2 = z2;
                    if (extended)
                    {
                        unstableCrit.Add(z1);
                        unstableCrit.Add(z2);
                        if (x1 < margin || x2 < margin)
                        {
                            unstableCrit.Add(new Vector(b1, x1 + 1));
                            unstableCrit.Add(new Vector(b2, x2 + 1));
                        }
                        if (x1 > 1 - margin || x2 > 1 - margin)
                        {
                            unstableCrit.Add(new Vector(b1, x1 - 1));
                            unstableCrit.Add(new Vector(b2, x2 - 1));
                        }
                        if (b1 < margin || b2 < margin)
                        {
                            unstableCrit.Add(new Vector(b1 + 1, x1));
                            unstableCrit.Add(new Vector(b2 + 1, x2));
                        }
                        if (b1 > 1 - margin || b2 > 1 - margin)
                        {
                            unstableCrit.Add(new Vector(b1 - 1, x1));
                            unstableCrit.Add(new Vector(b2 - 1, x2));
                        }
                    }

                    int n = (int)(Math.Abs((norm.Angle(u2.Value) - norm.Angle(u1.Value)) * r) / Program.HUMAN_DIAGRAM_ANGLE_GRAIN) + 1;
                    if (n <= 1) HumanDiagramAdd(x1, tota < 0 ? -u1.Value.Length : u1.Value.Length, x2, tota < 0 ? -u2.Value.Length : u2.Value.Length);
                    else HumanDiagramInterpAdd(norm, u1.Value, u2.Value, t, r, tota < 0, n);
                }
            }
            if ((Math.Abs(an[k - 1]) >= Math.PI * 0.5) && ((i == j) || (i == k - 1)))
            {
                double t = hd[2 * k - 1];
                double tota = an[k - 1];
                double r = (hd[2 * k] - t) / tota;
                Vector norm = GetN(k - 1);
                if (tota < 0) norm.Negate();
                Vector v = (i == j) ? p2.Value : p1.Value;
                double an1 = norm.Angle(v);
                double x = t + an1 * r;
                double y = tota > 0 ? v.Length : -v.Length;
                added = true;
                Vector z1, z2;
                if (w1 <= w2)
                {
                    z1 = new Vector(w1, x);
                    z2 = new Vector(w2, x);
                }
                else
                {
                    z2 = new Vector(w1, x);
                    z1 = new Vector(w2, x);
                }
                critData[i, k - 1].unstable1 = z1;
                critData[i, k - 1].unstable2 = z2;
                if (extended)
                {
                    unstableCrit.Add(z1);
                    unstableCrit.Add(z2);
                    if (w1 < margin)
                    {
                        unstableCrit.Add(new Vector(w1 + 1, x));
                        unstableCrit.Add(new Vector(w2 + 1, x));
                    }
                    if (w2 > 1 - margin)
                    {
                        unstableCrit.Add(new Vector(w1 - 1, x));
                        unstableCrit.Add(new Vector(w2 - 1, x));
                    }
                    if (x < margin)
                    {
                        unstableCrit.Add(new Vector(w1, x + 1));
                        unstableCrit.Add(new Vector(w2, x + 1));
                    }
                    if (x > 1 - margin)
                    {
                        unstableCrit.Add(new Vector(w1, x - 1));
                        unstableCrit.Add(new Vector(w2, x - 1));
                    }
                }
                HumanDiagramAdd(x, 0, x, y);
            }
            if (!added) critData[i, k - 1].forward = v3.HasValue;
        }

        private void ComputeAngles()
        {
            an = new double[p.Count];
            ans = new double[p.Count + 1];
            ans[0] = 0;
            for (int i = 0; i < p.Count; i++)
            {
                an[i] = GetN(i).Angle(GetN(i + 1));
                ans[i + 1] = ans[i] + Math.Abs(an[i]);
            }
            ta = ans[ans.Length - 1];
            ans[ans.Length - 1] = 1;
            if (ta != 0) for (int i = 0; i < p.Count; i++) ans[i] /= ta;
            edgeFactor = Program.HUMAN_DIAGRAM_EDGE_FACTOR * tl;
            angleFactor = Program.HUMAN_DIAGRAM_ANGLE_FACTOR * ta;
            double totFactor = edgeFactor + angleFactor;
            edgeFactor /= totFactor;
            angleFactor /= totFactor;
            hd = new double[2 * p.Count + 1];
            hd[0] = 0;
            for (int i = 0; i < p.Count; i += 1)
            {
                hd[2 * i + 1] = hd[2 * i] + (ls[i + 1] - ls[i]) * edgeFactor;
                hd[2 * i + 2] = hd[2 * i + 1] + (ans[i + 1] - ans[i]) * angleFactor;
            }
            hd[hd.Length - 1] = 1;
        }

        public void ComputeCritical(bool extended)
        {
            ComputeAngles();
            if (stableCrit != null) stableCrit.Clear();
            stableCrit = new List<Vector>();
            if (unstableCrit != null) unstableCrit.Clear();
            unstableCrit = new List<Vector>();
            if (puppyDiagonal != null) puppyDiagonal.Clear();
            puppyDiagonal = new List<Vector>();
            if (humanDiagram != null) humanDiagram.Clear();
            humanDiagram = new List<Vector>();
            if (humanDiagramBase != null) humanDiagramBase.Clear();
            humanDiagramBase = new List<Vector>();
            humanDiagramBase.Add(new Vector(0, 0));
            humanDiagramBase.Add(new Vector(1, 0));
            critData = new CritData[p.Count, p.Count];
            for (int i = 0; i < p.Count; i++)
            {
                for (int j = 0; j < p.Count; j++)
                {
                    if (i != j) CriticalSegSeg(i, j, extended);
                    CriticalSegVert(i, j, extended);
                }
                if (extended)
                {
                    puppyDiagonal.Add(new Vector(ls[i], hd[2 * i]));
                    puppyDiagonal.Add(new Vector(ls[i + 1], hd[2 * i + 1]));
                    puppyDiagonal.Add(new Vector(ls[i + 1], hd[2 * i + 1]));
                    puppyDiagonal.Add(new Vector(ls[i + 1], hd[2 * i + 2]));
                }
            }
            if (!extended)
            {
                puppyDiagonal.Add(new Vector(0, 0));
                puppyDiagonal.Add(new Vector(1, 1));
            }
            diagMinY = 0;
            diagMaxY = 0;
            for (int i = 0; i < humanDiagram.Count; i++)
            {
                diagMinY = Math.Min(humanDiagram[i].Y, diagMinY);
                diagMaxY = Math.Max(humanDiagram[i].Y, diagMaxY);
            }
            diagSign = 1;
            if (diagMaxY + diagMinY < 0)
            {
                diagSign = -1;
                double t = diagMinY;
                diagMinY = -diagMaxY;
                diagMaxY = -t;
            }
            if (diagMaxY == diagMinY) diagMaxY = 1;
            for (int i = 0; i < humanDiagram.Count; i++)
                humanDiagram[i] = new Vector(humanDiagram[i].X, HumanDiagramY(humanDiagram[i].Y));
            for (int i = 0; i < humanDiagramBase.Count; i++)
                humanDiagramBase[i] = new Vector(humanDiagramBase[i].X, HumanDiagramY(humanDiagramBase[i].Y));
        }

        public double HumanDiagramY(double y) => (diagSign * y - diagMinY) / (diagMaxY - diagMinY) - 0.5;

        public (Vector, Vector) HumanDiagramCoordinates(double puppy, double human)
        {
            double x, y;
            var (pv, j) = Locate(puppy);
            var dv = Vector.Subtract(Locate(human).Item1, pv);
            int k = j == 0 ? p.Count : j;
            x = ExtendedConversion(puppy, human);
            if (puppy == ls[j]) y = an[k - 1] < 0 ? dv.Length : -dv.Length;
            else y = Vector.Multiply(nor[j], dv);
            return (new Vector(x, HumanDiagramY(0)), new Vector(x, HumanDiagramY(y)));
        }

        public double ExtendedConversion(double puppy, double human)
        {
            puppy = puppy.Frac();
            human = human.Frac();

            int i = BinaryLocateL(puppy);
            if (puppy != ls[i]) return hd[2 * i] + (hd[2 * i + 1] - hd[2 * i]) * (puppy - ls[i]) / (ls[i + 1] - ls[i]);

            int k = i == 0 ? p.Count : i;
            CritData c = critData[BinaryLocateL(human), k - 1];

            double h1 = hd[2 * k - 1];
            double h2 = hd[2 * k];
            double mid = (h1 + h2) / 2;

            if (c.stable1.HasValue && c.stable1.Value.X <= human && human <= c.stable2.Value.X)
            {
                double d = c.stable2.Value.X - c.stable1.Value.X;
                return d == 0 ? (c.stable1.Value.Y + c.stable2.Value.Y) / 2 : c.stable1.Value.Y + (human - c.stable1.Value.X) * (c.stable2.Value.Y - c.stable1.Value.Y) / d;
            }
            else if (c.unstable1.HasValue && c.unstable1.Value.X <= human && human <= c.unstable2.Value.X)
            {
                double d = c.unstable2.Value.X - c.unstable1.Value.X;
                double w = d == 0 ? (c.unstable1.Value.Y + c.unstable2.Value.Y) / 2 : c.unstable1.Value.Y + (human - c.unstable1.Value.X) * (c.unstable2.Value.Y - c.unstable1.Value.Y) / d;
                return mid < w ? h1 : h2;
            }
            else if (c.stable1.HasValue)
            {
                if (c.stable2.Value.X < human) return c.stable2.Value.Y > mid ? h2 : h1;
                else return c.stable1.Value.Y > mid ? h2 : h1;
            }
            else if (c.unstable1.HasValue)
            {
                if (c.unstable2.Value.X < human) return c.unstable2.Value.Y < mid ? h2 : h1;
                else return c.unstable1.Value.Y < mid ? h2 : h1;
            }
            else return c.forward ? h2 : h1;
        }

        public (double, int) ExtendedConversion2(double puppy, double human)
        {
            puppy = puppy.Frac();
            human = human.Frac();
            var (i, seg, _, dd) = BinaryLocateA(puppy);
            if (seg) return (dd, 0);

            double f;

            int k = i == 0 ? p.Count : i;
            CritData c = critData[BinaryLocateL(human), i];

            double h1 = hd[2 * i + 1];
            double h2 = hd[2 * i + 2];
            double mid = (h1 + h2) / 2;

            if (c.stable1.HasValue && c.stable1.Value.X <= human && human <= c.stable2.Value.X) return (ls[i + 1], 0);
            else if (c.unstable1.HasValue && c.unstable1.Value.X <= human && human <= c.unstable2.Value.X)
            {
                double d = c.unstable2.Value.X - c.unstable1.Value.X;
                f = d == 0 ? (c.unstable1.Value.Y + c.unstable2.Value.Y) / 2 : c.unstable1.Value.Y + (human - c.unstable1.Value.X) * (c.unstable2.Value.Y - c.unstable1.Value.Y) / d;
            }
            else if (c.stable1.HasValue)
            {
                if (c.stable2.Value.X < human) f = c.stable2.Value.Y < mid ? 2 : -2;
                else f = c.stable1.Value.Y < mid ? 2 : -2;
            }
            else if (c.unstable1.HasValue)
            {
                if (c.unstable2.Value.X < human) f = c.unstable2.Value.Y > mid ? 2 : -2;
                else f = c.unstable1.Value.Y > mid ? 2 : -2;
            }
            else f = c.forward ? 2 : -2;

            return (ls[i + 1], puppy < f ? -1 : (puppy > f ? 1 : 0));
        }

        public (Vector, double, bool) Attract(double a, double w, int dir)
        {
            var (p, s) = Locate(a);
            int i = BinaryLocateL(w);
            int j, pj = 0;
            Vector q;
            double d = w;
            while (true)
            {
                if (s == i || (s == (i - 1).Mod(this.p.Count) && ls[i] == d))
                {
                    if (ls[i] != d || dir == 0 || (s == i && dir == 1) || (s == (i - 1).Mod(this.p.Count) && dir == -1)) return (p, a, true);
                    if (dir == -1)
                    {
                        i = (i - 1).Mod(this.p.Count);
                        dir = 1;
                    }
                }
                (q, d, j) = ClosestOnSeg(i, p);
                if (pj == 0 && ls[i] == w && dir != 1)
                {
                    var (q1, d1, j1) = ClosestOnSeg(i - 1, p);
                    if (dir == -1 || p.DistanceSquared(q1) < p.DistanceSquared(q)) {
                        i = (i - 1).Mod(this.p.Count);
                        q = q1;
                        d = d1;
                        j = j1;
                    }
                }
                if (j == 0 || j == -pj) return (q, d, false);
                i = (i + j).Mod(this.p.Count);
                pj = j;
                dir = 0;
            }
        }

        private void CheckStartTimer()
        {
            if (timer.Enabled) return;
            if (DrawHuman == Human && DrawPuppy == Puppy) return;
            stopwatch.Start();
            timer.Start();
        }

        private void CheckStopTimer()
        {
            if (DrawHuman != Human || DrawPuppy != Puppy) return;
            timer.Stop();
            stopwatch.Reset();
        }

        public void RelocateHuman(double w, bool instant = false)
        {
            Human = w;
            if (instant)
            {
                DrawHuman = Human;
                captured = false;
                reached = false;
                stopwatch.Start();
                timer.Start();
            }
            else
            {
                CheckStartTimer();
                Refresh();
            }
        }

        public void RelocatePuppy(double w, int dir, bool attract = false, bool startTimer = false)
        {
            captured = false;
            reached = false;
            if (startTimer)
            {
                stopwatch.Start();
                timer.Start();
            }
            this.attract = attract;
            Puppy = w;
            DrawPuppy = Puppy;
            if (attract) MovePuppy(DrawHuman, dir);
            else Refresh();
        }

        public void MovePuppy(double w, int dir, bool instant = false)
        {
            w = w.Frac();
            if (captured) Puppy = Human;
            else (_, Puppy, captured) = Attract(w, Puppy, dir);
            if (instant) DrawPuppy = Puppy;
            else CheckStartTimer();
            Refresh();
        }

        public void SpeedUp() => animationSpeed *= Program.SPEED_SCALE_FACTOR;

        public void SlowDown() => animationSpeed /= Program.SPEED_SCALE_FACTOR;

        public void Refresh()
        {
            if (!timer.Enabled && tf.gd != null) tf.gd.Refresh(true, true, true);
        }

        private (double, double) Approach(double a, double b, double delta, bool stopAtVertex = false)
        {
            a = a.Frac();
            b = b.Frac();
            double d = b - a;
            if (d > 0.5) d -= 1;
            if (d < -0.5) d += 1;
            double s = d >= 0 ? 1 : -1;
            double dest = (s * d <= delta ? b : a + s * delta).Frac();
            if (stopAtVertex)
            {
                double dd = (dest - a).Frac();
                int i = BinaryLocateL(a);
                if (s > 0)
                {
                    if (dd > ls[i + 1] - a) return (ls[i + 1].Frac(), delta - ls[i + 1] + a);
                }
                else
                {
                    dd = dd - 1;
                    if (a == ls[i])
                    {
                        i--;
                        if (i == -1)
                        {
                            i = p.Count - 1;
                            a = 1;
                        }
                    }
                    if (dd < ls[i] - a) return (ls[i].Frac(), delta - a + ls[i]);
                }
            }
            return (dest, 0);
        }

        public void Animate(object sender, EventArgs e)
        {
            double t = animationSpeed * stopwatch.ElapsedMilliseconds;
            stopwatch.Restart();
            double d = Program.HUMAN_SPEED * t / tl;
            while (d > 0)
            {
                (DrawHuman, d) = Approach(DrawHuman, Human, d, true);
                if (attract) MovePuppy(DrawHuman, 0);
            }

            //DrawPuppy = reached ? DrawHuman : Approach(DrawPuppy, Puppy, Program.PUPPY_SPEED * t / tl).Item1;
            DrawPuppy = reached ? Approach(DrawPuppy, DrawHuman, Program.PUPPY_SPEED * t / tl).Item1 : Approach(DrawPuppy, Puppy, Program.PUPPY_SPEED * t / tl).Item1;

            if (captured)
            {
                double dh = Human - DrawHuman;
                if (dh > 0.5) dh -= 1;
                if (dh < -0.5) dh += 1;
                double dp = Puppy - DrawPuppy;
                if (dp > 0.5) dp -= 1;
                if (dp < -0.5) dp += 1;
                if ((dh <= dp && dp <= 0) || (0 <= dp && dp <= dh)) reached = true;
            }
            CheckStopTimer();
            tf.gd.Refresh(true, true, true);
        }

        public void Draw(Graphics g)
        {
            if (tf.gd == null) return;
            tf.gd.DrawPolygon(g, p);
            Vector q1 = Locate(DrawPuppy).Item1;
            Vector q2 = Locate(DrawHuman).Item1;
            tf.gd.DrawArrowFrame(g, q1, q2, Program.TANGENT_COLOR);
            if (tf.gd.drawNormal)
            {
                var (i, seg, v, d) = BinaryLocateA((tf.gd.normalX - tf.hf.xOffset).Frac());
                if (seg) tf.gd.DrawTNormal(g, v, GetN(i), Program.NORMAL_COLOR);
                else tf.gd.DrawTNormal(g, v, GetN(i).Rotate(d), Program.NORMAL_COLOR);
            }
            tf.gd.DrawPuppy(g, q1);
            tf.gd.DrawHuman(g, q2);
        }

        public List<Vector> Chamfer()
        {
            double epsilon = 1000;
            for(int i= 0; i < p.Count; i++)
            {
                for (int j = 2; j < p.Count; j++)
                {
                    Vector v1 = GetP(i + j);
                    Vector v2 = ClosestOnSeg(i, v1).Item1;
                    epsilon = Math.Min(epsilon, v1.DistanceSquared(v2));
                }
            }
            epsilon = Math.Sqrt(epsilon) * Program.CHAMFERING_FACTOR;

            List<Vector> c = new List<Vector>(2 * p.Count);
            for(int i = 0; i < p.Count; i++)
            {
                Vector p1 = GetP(i);
                Vector p2 = GetP(i + 1);
                double lambda = epsilon / l[i];
                c.Add(p1.Interpolate(p2, lambda));
                c.Add(p2.Interpolate(p1, lambda));
            }
            return c;
        }

        public void Dispose() => timer.Dispose();
    }
}
back to top