<commit_before>﻿using EDDiscovery.DB;
using EDDiscovery2.DB;
using OpenTK;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using OpenTK.Graphics.OpenGL;
using System.Threading;
using System.Windows.Forms;
using EDDiscovery;

namespace EDDiscovery2
{
    public class StarGrid
    {
        public int Id { get; set; }
        public float X { get; set; }
        public float Z { get; set; }
        public int Percentage { get; set; }          // foreground flags
        public float CalculatedDistance { get; set; }  // foreground.. what distance did we calc on..
        public SystemClass.SystemAskType dBAsk { get; set; } // set for an explicit ask for unpopulated systems
        public int Count { get { return array1displayed ? array1vertices : array2vertices; } }
        public int CountJustMade { get { return array1displayed ? array2vertices : array1vertices; } }
        public bool Working = false;

        public int DisplayChecked = 0;            // set to display count when display checked..
        public int DisplayCount = 0;              // increment when display changed each time..

        bool array1displayed;
        private Vector3[] array1;            // the star points
        private uint[] carray1;                // the star colours
        int array1vertices;
        private Vector3[] array2;            // the star points
        private uint[] carray2;                // the star colours
        int array2vertices;

        public float Size { get; set; }
        public Color Color { get; set; }

        protected int VtxVboID;
        protected int VtxColorVboId;
        protected GLControl GLContext;

        Object lockdisplaydata = new Object();

        public StarGrid(int id, float x, float z, Color c, float s)
        {
            Id = id; X = x; Z = z;
            Percentage = 0;
            CalculatedDistance = 10E6F;        // some large value, but not too large so ABS takes us overrange
            Color = c;
            Size = s;
            array1vertices = 0;
            array2vertices = 0;
            array1displayed = true;
        }

        public float DistanceFrom(float x, float z)
        { return (float)Math.Sqrt((x - X) * (x - X) + (z - Z) * (z - Z)); }

        public void FillFromDB()        // does not affect the display object
        {
            if (array1displayed)
                array2vertices = SystemClass.GetSystemVector(Id, ref array2, ref carray2, dBAsk, Percentage);       // MAY return array/carray is null
            else
                array1vertices = SystemClass.GetSystemVector(Id, ref array1, ref carray1, dBAsk, Percentage);
        }

        public void FillFromVS(List<VisitedSystemsClass> cls) // does not affect the display object
        {
            if (array1displayed)
                array2vertices = FillFromVS(ref array2, ref carray2, cls, this.Color);
            else
                array1vertices = FillFromVS(ref array1, ref carray1, cls, this.Color);
        }

        private int FillFromVS( ref Vector3[] array, ref uint[] carray, List<VisitedSystemsClass> cls, Color basecolour)
        {
            carray = new uint[cls.Count];
            array = new Vector3[cls.Count];     // can't have any more than this 
            int total = 0;

            uint cx = BitConverter.ToUInt32(new byte[] { basecolour.R, basecolour.G, basecolour.B, basecolour.A }, 0);

            foreach (VisitedSystemsClass vs in cls)
            {                                                               // all vs stars which are not in edsm and have co-ords.
                if (vs.HasTravelCoordinates)
                {
                    carray[total] = unchecked((uint)vs.MapColour);
                    array[total++] = new Vector3((float)vs.X, (float)vs.Y, (float)vs.Z);
                }
                else if (vs.curSystem != null && vs.curSystem.HasCoordinate)
                {
                    carray[total] = unchecked((uint)vs.MapColour);
                    array[total++] = new Vector3((float)vs.curSystem.x, (float)vs.curSystem.y, (float)vs.curSystem.z);
                    //Console.WriteLine("Added {0} due to not being in star database", vs.Name);
                }
            }

            return total;
        }

        public class TransFormInfo
        {
            public TransFormInfo(Matrix4 r, float z, int dw, int dh, float zm)
            { resmat = r; znear = z; dwidth = dw; dheight = dh; zoom = zm; }

            public TransFormInfo(Matrix4 r, float z, int dw, int dh, float sq, Vector3 cm)
            { resmat = r; znear = z; dwidth = dw; dheight = dh; sqlylimit = sq; campos = cm; }

            public Matrix4 resmat;
            public float znear;
            public int dwidth;
            public int dheight;

            public float zoom;      // Find Point only
            public float sqlylimit;      //  GetSystemInView only
            public Vector3 campos;//  GetSystemInView only
        }

        public Vector3? FindPoint(int x, int y, ref float cursysdistz, TransFormInfo ti) // UI call .. operate on  display
        {
            Debug.Assert(Application.MessageLoop);

            if (array1displayed)
                return FindPoint(ref array1, array1vertices, x, y, ref cursysdistz, ti);
            else
                return FindPoint(ref array2, array2vertices, x, y, ref cursysdistz, ti);
        }
                                                                                            // operate on  display
        public Vector3? FindPoint(ref Vector3[] vert, int total , int x, int y, ref float cursysdistz, TransFormInfo ti)
        { 
            Vector3? ret = null;
            float w2 = (float)ti.dwidth / 2.0F;
            float h2 = (float)ti.dheight / 2.0F;

            for ( int i = 0; i < total; i++)
            {
                Vector3 v = vert[i];

                Vector4 syspos = new Vector4(v.X, v.Y, v.Z, 1.0F);
                Vector4 sysloc = Vector4.Transform(syspos, ti.resmat);

                if (sysloc.Z > ti.znear)
                {
                    Vector2 syssloc = new Vector2(((sysloc.X / sysloc.W) + 1.0F) * w2 - x, ((sysloc.Y / sysloc.W) + 1.0F) * h2 - y);
                    float sysdistsq = syssloc.X * syssloc.X + syssloc.Y * syssloc.Y;

                    if (sysdistsq < 7.0 * 7.0)
                    {
                        float sysdist = (float)Math.Sqrt(sysdistsq);

                        if ((sysdist + Math.Abs(sysloc.Z * ti.zoom)) < cursysdistz)
                        {
                            cursysdistz = sysdist + (float)Math.Abs(sysloc.Z * ti.zoom);
                            ret = new Vector3(v.X, v.Y, v.Z);
                        }
                    }
                }
            }

            return ret;
        }

        public class InViewInfo
        {
            public InViewInfo(Vector3 pos, uint c) { position = pos; colour = c; }
            public Color AsColor { get { return Color.FromArgb((int)((colour >> 24) & 0xff), (int)(colour & 0xff), (int)((colour >> 8) & 0xff), (int)((colour >> 16) & 0xff)); } }

            public Vector3 position;
            public uint colour;
        }

        // operate on  display - Called in a thread..
        public void GetSystemsInView(ref SortedDictionary<float, InViewInfo> list , TransFormInfo ti, uint forcecol = 0)
        {
            if (array1displayed)
                GetSystemsInView(ref array1, ref carray1, array1vertices, ref list, ti , forcecol);
            else
                GetSystemsInView(ref array2, ref carray2, array2vertices, ref list, ti , forcecol);
        }

        // operate on  display - can be called BY A THREAD
        public void GetSystemsInView(ref Vector3[] vert, ref uint[] cols , int total, ref SortedDictionary<float, InViewInfo> list , TransFormInfo ti , uint forcecol )
        {
            int margin = -150;
            float sqdist = 0F;
            float w2 = (float)ti.dwidth / 2.0F;
            float h2 = (float)ti.dheight / 2.0F;

            lock (lockdisplaydata)                                                  // must lock it..
            {
                for (int i = 0; i < total; i++)
                {
                    Vector3 v = vert[i];

                    Vector4 syspos = new Vector4(v.X, v.Y, v.Z, 1.0F);
                    Vector4 sysloc = Vector4.Transform(syspos, ti.resmat);

                    if (sysloc.Z > ti.znear)
                    {
                        Vector2d syssloc = new Vector2d(((sysloc.X / sysloc.W) + 1.0) * w2, ((sysloc.Y / sysloc.W) + 1.0) * h2);

                        if ((syssloc.X >= margin && syssloc.X <= ti.dwidth - margin) && (syssloc.Y >= margin && syssloc.Y <= ti.dheight - margin))
                        {
                            sqdist = ((float)v.X - ti.campos.X) * ((float)v.X - ti.campos.X) + ((float)v.Y - ti.campos.Y) * ((float)v.Y - ti.campos.Y) + ((float)v.Z - ti.campos.Z) * ((float)v.Z - ti.campos.Z);

                            if (sqdist <= ti.sqlylimit)
                                list.Add(sqdist, new InViewInfo( new Vector3(v.X,v.Y,v.Z),(forcecol!=0) ? forcecol : cols[i]));
                        }
                    }
                }
            }
        }

        public void Dispose()           // throw away any previous buffer..
        {
            DeleteContext();
            array1 = null;
            carray1 = null;
            array2 = null;
            carray2 = null;
        }

        public void DeleteContext()
        {
            if (GLContext != null)
            {
                if (GLContext.InvokeRequired)
                {
                    GLContext.Invoke(new Action(this.DeleteContext));
                }
                else
                {
                    GL.DeleteBuffer(VtxVboID);
                    GL.DeleteBuffer(VtxColorVboId);
                    GLContext = null;
                    VtxVboID = 0;
                    VtxColorVboId = 0;
                }
            }
        }

        public void Display(GLControl control)          // UI thread..
        {
            Debug.Assert(Application.MessageLoop);

            lock ( lockdisplaydata )                                        // not allowed to swap..
            {
                array1displayed = !array1displayed;

                DeleteContext();

                if (array1displayed)
                {
                    if (array1vertices > 0)
                    {
                        GL.GenBuffers(1, out VtxVboID);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxVboID);
                        GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(array1vertices * BlittableValueType.StrideOf(array1)), array1, BufferUsageHint.StaticDraw);

                        GL.GenBuffers(1, out VtxColorVboId);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxColorVboId);
                        GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(array1vertices * BlittableValueType.StrideOf(carray1)), carray1, BufferUsageHint.StaticDraw);
                        GLContext = control;
                    }

                    carray2 = null;
                    array2 = null;
                    array2vertices = 0;
                }
                else
                {
                    if (array2vertices > 0)
                    {
                        GL.GenBuffers(1, out VtxVboID);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxVboID);

                        GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(array2vertices * BlittableValueType.StrideOf(array2)), array2, BufferUsageHint.StaticDraw);

                        GL.GenBuffers(1, out VtxColorVboId);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxColorVboId);
                        GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(array2vertices * BlittableValueType.StrideOf(carray2)), carray2, BufferUsageHint.StaticDraw);
                        GLContext = control;
                    }

                    carray1 = null;
                    array1 = null;
                    array1vertices = 0;
                }

                DisplayCount++;                                             // another swap
                //Console.WriteLine("Fill grid {0} {1} with {2}", X, Z, Count);
            }
        }

        public void Draw(GLControl control)         // UI thread.
        {
            Debug.Assert(Application.MessageLoop);

            if (GLContext != null)
            {
                Debug.Assert(GLContext == control);
                Debug.Assert(VtxVboID > 0);

                if (control.InvokeRequired)
                {
                    control.Invoke(new Action<GLControl>(this.Draw), control);
                }
                else
                {
                    int numpoints = (array1displayed) ? array1vertices : array2vertices;

                    if (numpoints > 0)
                    {
                        GL.EnableClientState(ArrayCap.VertexArray);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxVboID);
                        GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
                        GL.PointSize(Size);

                        if (Color == Color.Transparent)
                        {
                            GL.EnableClientState(ArrayCap.ColorArray);
                            GL.BindBuffer(BufferTarget.ArrayBuffer, VtxColorVboId);
                            GL.ColorPointer(4, ColorPointerType.UnsignedByte, 0, 0);
                            GL.DrawArrays(PrimitiveType.Points, 0, numpoints);
                            GL.DisableClientState(ArrayCap.ColorArray);
                        }
                        else
                        {
                            GL.Color4(Color);
                            GL.DrawArrays(PrimitiveType.Points, 0, numpoints);
                        }

                        GL.DisableClientState(ArrayCap.VertexArray);
                    }
                }
            }
        }
    }

    public class StarGrids
    {
        #region Vars
        public bool ForceWhite { get; set; } = false;

        private BlockingCollection<StarGrid> computed = new BlockingCollection<StarGrid>();
        private List<StarGrid> grids = new List<StarGrid>();        // unpopulated grid stars
        private StarGrid populatedgrid;
        private StarGrid visitedsystemsgrid;
        private System.Threading.Thread computeThread;
        private bool computeExit = false;
        private EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset);
        private float curx = 0, curz = 0;

        private int midpercentage = 80;
        private float middistance = 20000;
        private int farpercentage = 50;
        private float fardistance = 40000;
        private float MinRecalcDistance = 5000;            // only recalc a grid if we are more than this away from its prev calc pos

        private Color popcolour = Color.Blue;

#endregion

#region Initialise

        public void Initialise()
        {
            for (int z = 0; z < GridId.gridzrange; z++)
            {
                for (int x = 0; x < GridId.gridxrange; x++)
                {
                    int id = GridId.IdFromComponents(x, z);
                    float xp = 0, zp = 0;
                    bool ok = GridId.XZ(id, out xp, out zp);
                    Debug.Assert(ok);
                    StarGrid grd = new StarGrid(id, xp, zp, Color.Transparent, 1.0F);           //A=0 means use default colour array
                    if (xp == 0 && zp == 0)                                     // sol grid, unpopulated stars please
                        grd.dBAsk = SystemClass.SystemAskType.UnPopulatedStars;

                    grids.Add(grd);
                }
            }

            visitedsystemsgrid = new StarGrid(-1, 0, 0, Color.Orange, 1.0F);    // grid ID -1 means it won't be filled by the Update task
            grids.Add(visitedsystemsgrid);

            int solid = GridId.Id(0, 0);                                    
            populatedgrid = new StarGrid(solid, 0, 0, Color.Transparent, 1.0F);      // Duplicate grid id but asking for populated stars
            populatedgrid.dBAsk = SystemClass.SystemAskType.PopulatedStars;
            grids.Add(populatedgrid);   // add last so shown last
<commit_msg>Fix a couple of issues with visited system colouring<commit_after>﻿using EDDiscovery.DB;
using EDDiscovery2.DB;
using OpenTK;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using OpenTK.Graphics.OpenGL;
using System.Threading;
using System.Windows.Forms;
using EDDiscovery;

namespace EDDiscovery2
{
    public class StarGrid
    {
        public int Id { get; set; }
        public float X { get; set; }
        public float Z { get; set; }
        public int Percentage { get; set; }          // foreground flags
        public float CalculatedDistance { get; set; }  // foreground.. what distance did we calc on..
        public SystemClass.SystemAskType dBAsk { get; set; } // set for an explicit ask for unpopulated systems
        public int Count { get { return array1displayed ? array1vertices : array2vertices; } }
        public int CountJustMade { get { return array1displayed ? array2vertices : array1vertices; } }
        public bool Working = false;

        public int DisplayChecked = 0;            // set to display count when display checked..
        public int DisplayCount = 0;              // increment when display changed each time..

        bool array1displayed;
        private Vector3[] array1;            // the star points
        private uint[] carray1;                // the star colours
        int array1vertices;
        private Vector3[] array2;            // the star points
        private uint[] carray2;                // the star colours
        int array2vertices;

        public float Size { get; set; }
        public Color Color { get; set; }

        protected int VtxVboID;
        protected int VtxColorVboId;
        protected GLControl GLContext;

        Object lockdisplaydata = new Object();

        public StarGrid(int id, float x, float z, Color c, float s)
        {
            Id = id; X = x; Z = z;
            Percentage = 0;
            CalculatedDistance = 10E6F;        // some large value, but not too large so ABS takes us overrange
            Color = c;
            Size = s;
            array1vertices = 0;
            array2vertices = 0;
            array1displayed = true;
        }

        public float DistanceFrom(float x, float z)
        { return (float)Math.Sqrt((x - X) * (x - X) + (z - Z) * (z - Z)); }

        public void FillFromDB()        // does not affect the display object
        {
            if (array1displayed)
                array2vertices = SystemClass.GetSystemVector(Id, ref array2, ref carray2, dBAsk, Percentage);       // MAY return array/carray is null
            else
                array1vertices = SystemClass.GetSystemVector(Id, ref array1, ref carray1, dBAsk, Percentage);
        }

        public void FillFromVS(List<VisitedSystemsClass> cls) // does not affect the display object
        {
            if (array1displayed)
                array2vertices = FillFromVS(ref array2, ref carray2, cls, this.Color);
            else
                array1vertices = FillFromVS(ref array1, ref carray1, cls, this.Color);
        }

        private int FillFromVS( ref Vector3[] array, ref uint[] carray, List<VisitedSystemsClass> cls, Color basecolour)
        {
            carray = new uint[cls.Count];
            array = new Vector3[cls.Count];     // can't have any more than this 
            int total = 0;

            uint cx = BitConverter.ToUInt32(new byte[] { basecolour.R, basecolour.G, basecolour.B, basecolour.A }, 0);

            foreach (VisitedSystemsClass vs in cls)
            {                                                               // all vs stars which are not in edsm and have co-ords.
                // OpenGL colour byte order is RGBA, while Windows colour byte order is BGRA
                Color colour = Color.FromArgb(vs.MapColour);
                uint vscolour = BitConverter.ToUInt32(new byte[] { colour.R, colour.G, colour.B, colour.A }, 0);
                if (vs.HasTravelCoordinates)
                {
                    carray[total] = vscolour;
                    array[total++] = new Vector3((float)vs.X, (float)vs.Y, (float)vs.Z);
                }
                else if (vs.curSystem != null && vs.curSystem.HasCoordinate)
                {
                    carray[total] = vscolour;
                    array[total++] = new Vector3((float)vs.curSystem.x, (float)vs.curSystem.y, (float)vs.curSystem.z);
                    //Console.WriteLine("Added {0} due to not being in star database", vs.Name);
                }
            }

            return total;
        }

        public class TransFormInfo
        {
            public TransFormInfo(Matrix4 r, float z, int dw, int dh, float zm)
            { resmat = r; znear = z; dwidth = dw; dheight = dh; zoom = zm; }

            public TransFormInfo(Matrix4 r, float z, int dw, int dh, float sq, Vector3 cm)
            { resmat = r; znear = z; dwidth = dw; dheight = dh; sqlylimit = sq; campos = cm; }

            public Matrix4 resmat;
            public float znear;
            public int dwidth;
            public int dheight;

            public float zoom;      // Find Point only
            public float sqlylimit;      //  GetSystemInView only
            public Vector3 campos;//  GetSystemInView only
        }

        public Vector3? FindPoint(int x, int y, ref float cursysdistz, TransFormInfo ti) // UI call .. operate on  display
        {
            Debug.Assert(Application.MessageLoop);

            if (array1displayed)
                return FindPoint(ref array1, array1vertices, x, y, ref cursysdistz, ti);
            else
                return FindPoint(ref array2, array2vertices, x, y, ref cursysdistz, ti);
        }
                                                                                            // operate on  display
        public Vector3? FindPoint(ref Vector3[] vert, int total , int x, int y, ref float cursysdistz, TransFormInfo ti)
        { 
            Vector3? ret = null;
            float w2 = (float)ti.dwidth / 2.0F;
            float h2 = (float)ti.dheight / 2.0F;

            for ( int i = 0; i < total; i++)
            {
                Vector3 v = vert[i];

                Vector4 syspos = new Vector4(v.X, v.Y, v.Z, 1.0F);
                Vector4 sysloc = Vector4.Transform(syspos, ti.resmat);

                if (sysloc.Z > ti.znear)
                {
                    Vector2 syssloc = new Vector2(((sysloc.X / sysloc.W) + 1.0F) * w2 - x, ((sysloc.Y / sysloc.W) + 1.0F) * h2 - y);
                    float sysdistsq = syssloc.X * syssloc.X + syssloc.Y * syssloc.Y;

                    if (sysdistsq < 7.0 * 7.0)
                    {
                        float sysdist = (float)Math.Sqrt(sysdistsq);

                        if ((sysdist + Math.Abs(sysloc.Z * ti.zoom)) < cursysdistz)
                        {
                            cursysdistz = sysdist + (float)Math.Abs(sysloc.Z * ti.zoom);
                            ret = new Vector3(v.X, v.Y, v.Z);
                        }
                    }
                }
            }

            return ret;
        }

        public class InViewInfo
        {
            public InViewInfo(Vector3 pos, uint c) { position = pos; colour = c; }
            public Color AsColor { get { return Color.FromArgb((int)((colour >> 24) & 0xff), (int)(colour & 0xff), (int)((colour >> 8) & 0xff), (int)((colour >> 16) & 0xff)); } }

            public Vector3 position;
            public uint colour;
        }

        // operate on  display - Called in a thread..
        public void GetSystemsInView(ref SortedDictionary<float, InViewInfo> list , TransFormInfo ti, uint forcecol = 0)
        {
            if (array1displayed)
                GetSystemsInView(ref array1, ref carray1, array1vertices, ref list, ti , forcecol);
            else
                GetSystemsInView(ref array2, ref carray2, array2vertices, ref list, ti , forcecol);
        }

        // operate on  display - can be called BY A THREAD
        public void GetSystemsInView(ref Vector3[] vert, ref uint[] cols , int total, ref SortedDictionary<float, InViewInfo> list , TransFormInfo ti , uint forcecol )
        {
            int margin = -150;
            float sqdist = 0F;
            float w2 = (float)ti.dwidth / 2.0F;
            float h2 = (float)ti.dheight / 2.0F;

            lock (lockdisplaydata)                                                  // must lock it..
            {
                for (int i = 0; i < total; i++)
                {
                    Vector3 v = vert[i];

                    Vector4 syspos = new Vector4(v.X, v.Y, v.Z, 1.0F);
                    Vector4 sysloc = Vector4.Transform(syspos, ti.resmat);

                    if (sysloc.Z > ti.znear)
                    {
                        Vector2d syssloc = new Vector2d(((sysloc.X / sysloc.W) + 1.0) * w2, ((sysloc.Y / sysloc.W) + 1.0) * h2);

                        if ((syssloc.X >= margin && syssloc.X <= ti.dwidth - margin) && (syssloc.Y >= margin && syssloc.Y <= ti.dheight - margin))
                        {
                            sqdist = ((float)v.X - ti.campos.X) * ((float)v.X - ti.campos.X) + ((float)v.Y - ti.campos.Y) * ((float)v.Y - ti.campos.Y) + ((float)v.Z - ti.campos.Z) * ((float)v.Z - ti.campos.Z);

                            if (sqdist <= ti.sqlylimit)
                                list.Add(sqdist, new InViewInfo( new Vector3(v.X,v.Y,v.Z),(forcecol!=0) ? forcecol : cols[i]));
                        }
                    }
                }
            }
        }

        public void Dispose()           // throw away any previous buffer..
        {
            DeleteContext();
            array1 = null;
            carray1 = null;
            array2 = null;
            carray2 = null;
        }

        public void DeleteContext()
        {
            if (GLContext != null)
            {
                if (GLContext.InvokeRequired)
                {
                    GLContext.Invoke(new Action(this.DeleteContext));
                }
                else
                {
                    GL.DeleteBuffer(VtxVboID);
                    GL.DeleteBuffer(VtxColorVboId);
                    GLContext = null;
                    VtxVboID = 0;
                    VtxColorVboId = 0;
                }
            }
        }

        public void Display(GLControl control)          // UI thread..
        {
            Debug.Assert(Application.MessageLoop);

            lock ( lockdisplaydata )                                        // not allowed to swap..
            {
                array1displayed = !array1displayed;

                DeleteContext();

                if (array1displayed)
                {
                    if (array1vertices > 0)
                    {
                        GL.GenBuffers(1, out VtxVboID);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxVboID);
                        GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(array1vertices * BlittableValueType.StrideOf(array1)), array1, BufferUsageHint.StaticDraw);

                        GL.GenBuffers(1, out VtxColorVboId);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxColorVboId);
                        GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(array1vertices * BlittableValueType.StrideOf(carray1)), carray1, BufferUsageHint.StaticDraw);
                        GLContext = control;
                    }

                    carray2 = null;
                    array2 = null;
                    array2vertices = 0;
                }
                else
                {
                    if (array2vertices > 0)
                    {
                        GL.GenBuffers(1, out VtxVboID);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxVboID);

                        GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(array2vertices * BlittableValueType.StrideOf(array2)), array2, BufferUsageHint.StaticDraw);

                        GL.GenBuffers(1, out VtxColorVboId);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxColorVboId);
                        GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(array2vertices * BlittableValueType.StrideOf(carray2)), carray2, BufferUsageHint.StaticDraw);
                        GLContext = control;
                    }

                    carray1 = null;
                    array1 = null;
                    array1vertices = 0;
                }

                DisplayCount++;                                             // another swap
                //Console.WriteLine("Fill grid {0} {1} with {2}", X, Z, Count);
            }
        }

        public void Draw(GLControl control)         // UI thread.
        {
            Debug.Assert(Application.MessageLoop);

            if (GLContext != null)
            {
                Debug.Assert(GLContext == control);
                Debug.Assert(VtxVboID > 0);

                if (control.InvokeRequired)
                {
                    control.Invoke(new Action<GLControl>(this.Draw), control);
                }
                else
                {
                    int numpoints = (array1displayed) ? array1vertices : array2vertices;

                    if (numpoints > 0)
                    {
                        GL.EnableClientState(ArrayCap.VertexArray);
                        GL.BindBuffer(BufferTarget.ArrayBuffer, VtxVboID);
                        GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
                        GL.PointSize(Size);

                        if (Color == Color.Transparent)
                        {
                            GL.Color4(Color.White);
                            GL.EnableClientState(ArrayCap.ColorArray);
                            GL.BindBuffer(BufferTarget.ArrayBuffer, VtxColorVboId);
                            GL.ColorPointer(4, ColorPointerType.UnsignedByte, 0, 0);
                            GL.DrawArrays(PrimitiveType.Points, 0, numpoints);
                            GL.DisableClientState(ArrayCap.ColorArray);
                        }
                        else
                        {
                            GL.Color4(Color);
                            GL.DrawArrays(PrimitiveType.Points, 0, numpoints);
                        }

                        GL.DisableClientState(ArrayCap.VertexArray);
                    }
                }
            }
        }
    }

    public class StarGrids
    {
        #region Vars
        public bool ForceWhite { get; set; } = false;

        private BlockingCollection<StarGrid> computed = new BlockingCollection<StarGrid>();
        private List<StarGrid> grids = new List<StarGrid>();        // unpopulated grid stars
        private StarGrid populatedgrid;
        private StarGrid visitedsystemsgrid;
        private System.Threading.Thread computeThread;
        private bool computeExit = false;
        private EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset);
        private float curx = 0, curz = 0;

        private int midpercentage = 80;
        private float middistance = 20000;
        private int farpercentage = 50;
        private float fardistance = 40000;
        private float MinRecalcDistance = 5000;            // only recalc a grid if we are more than this away from its prev calc pos

        private Color popcolour = Color.Blue;

#endregion

#region Initialise

        public void Initialise()
        {
            for (int z = 0; z < GridId.gridzrange; z++)
            {
                for (int x = 0; x < GridId.gridxrange; x++)
                {
                    int id = GridId.IdFromComponents(x, z);
                    float xp = 0, zp = 0;
                    bool ok = GridId.XZ(id, out xp, out zp);
                    Debug.Assert(ok);
                    StarGrid grd = new StarGrid(id, xp, zp, Color.Transparent, 1.0F);           //A=0 means use default colour array
                    if (xp == 0 && zp == 0)                                     // sol grid, unpopulated stars please
                        grd.dBAsk = SystemClass.SystemAskType.UnPopulatedStars;

                    grids.Add(grd);
                }
            }

            visitedsystemsgrid = new StarGrid(-1, 0, 0, Color.Transparent, 1.0F);    // grid ID -1 means it won't be filled by the Update task
            grids.Add(visitedsystemsgrid);

            int solid = GridId.Id(0, 0);                                    
            populatedgrid = new StarGrid(solid, 0, 0, Color.Transparent, 1.0F);      // Duplicate grid id but asking for populated stars
            populatedgrid.dBAsk = SystemClass.SystemAskType.PopulatedStars;
            grids.Add(populatedgrid);   // add last so shown last

{'fraction_non_alphanumeric': 0.09085484111591824, 'fraction_numerical': 0.014142921080113382, 'mean_word_length': 1.3577910657755892, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4865591', 'n_tokens_mistral': 9445, 'n_tokens_neox': 8783, 'n_words': 2366}