public virtual void CopyPoints()
    {
        int i  = 0;
        int ii = 0;

        NucleusPoint[] ps = new NucleusPoint[NucleusSpacing.points.Length];
        i = 0;
        while (i < ps.Length)
        {
            NucleusPoint p = new NucleusPoint();
            p.index     = i;
            p.neighbors = new int[12];
            ii          = 0;
            while (ii < 12)
            {
                p.neighbors[ii] = NucleusSpacing.points[i].neighbors[ii];
                ii++;
            }
            p.pos      = this.flipPositions ? -NucleusSpacing.points[i].pos : NucleusSpacing.points[i].pos;
            p.distRank = NucleusSpacing.points[i].distRank;
            ps[i]      = p;
            i++;
        }
        int maxRank = ps[ps.Length - 1].distRank;

        NucleusRank[] rs         = new NucleusRank[maxRank + 1];
        int[]         rankCounts = new int[rs.Length];
        i = 0;
        while (i < ps.Length)
        {
            rankCounts[ps[i].distRank]++;
            i++;
        }
        int curRank = 0;

        i = 0;
        while (i < rs.Length)
        {
            NucleusRank r = new NucleusRank();
            r.points = new NucleusPoint[rankCounts[i]];
            rs[i]    = r;
            i++;
        }
        rankCounts = new int[rs.Length];
        i          = 0;
        while (i < rs.Length)
        {
            rankCounts[i] = 0;
            i++;
        }
        i = 0;
        while (i < ps.Length)
        {
            rs[ps[i].distRank].points[rankCounts[ps[i].distRank]] = ps[i];
            rankCounts[ps[i].distRank]++;
            i++;
        }
        this.points = ps;
        this.ranks  = rs;
    }
    public virtual void Rebuild()
    {
        //Debug.Log(Time.time);
        UserAtom.p = this.p;
        UserAtom.n = this.n;
        int i  = 0;
        int ii = 0;

        i = 0;
        while (i < this.points.Length)
        {
            this.colliders[i].SetActive(false);
            this.points[i].state = PType.Empty;
            i++;
        }
        int     curRank     = 0;
        int     curP        = this.p;
        int     curN        = this.n;
        Vector3 centerPoint = Vector3.zero;
        int     added       = 0;

        NucleusDisplay.Nucleon[] displayNucleons = new NucleusDisplay.Nucleon[this.p + this.n];
        while ((curP > 0) || (curN > 0))
        {
            bool  nextIsProton    = (curP > 0) && ((curN == 0) || ((((curP + 0.01f) / (curN + 0.01f)) + ((this.randomizeOrder - 0.5f) * 0.2f)) > ((this.p + 0.01f) / (this.n + 0.01f))));
            int   maxNeighbors    = -1;
            float maxRatio        = -1f;
            int   bestFit         = -1;
            int   availableInRank = 0;
            i = 0;
            while (i < this.ranks[curRank].points.Length)
            {
                NucleusPoint point = this.ranks[curRank].points[i];
                if (point.state == PType.Empty)
                {
                    availableInRank++;
                    float prs = 0f;
                    float nus = 0f;
                    ii = 0;
                    while (ii < point.neighbors.Length)
                    {
                        if ((point.neighbors[ii] != -1) && (this.points[point.neighbors[ii]].state == PType.P))
                        {
                            prs++;
                        }
                        if ((point.neighbors[ii] != -1) && (this.points[point.neighbors[ii]].state == PType.N))
                        {
                            nus++;
                        }
                        ii++;
                    }
                    if (maxNeighbors <= (prs + nus))
                    {
                        if (maxNeighbors < (prs + nus))
                        {
                            maxNeighbors = (int)(prs + nus);
                            bestFit      = point.index;
                        }
                        float ratio = nextIsProton ? (nus + 0.01f) / (prs + 0.01f) : (prs + 0.01f) / (nus + 0.01f);
                        if (maxRatio < ratio)
                        {
                            maxRatio = ratio;
                            bestFit  = point.index;
                        }
                    }
                }
                i++;
            }
            this.points[bestFit].state = nextIsProton ? PType.P : PType.N;
            this.colliders[bestFit].SetActive(true);//GameObject
            NucleusDisplay.Nucleon np = new NucleusDisplay.Nucleon();

            np.pos   = this.points[bestFit].pos;
            np.color = nextIsProton ? this.protonColor : this.neutronColor;
            displayNucleons[added] = np;
            added++;
            centerPoint = centerPoint + this.points[bestFit].pos;
            if (nextIsProton)
            {
                curP--;
            }
            else
            {
                curN--;
            }
            if (availableInRank == 1)
            {
                curRank++;
            }
        }
        centerPoint             = centerPoint / (this.p + this.n);
        this.transform.position = -centerPoint * this.overallScale;
        this.display.SetNucleons(displayNucleons);
        this.transform.localScale = Vector3.one * this.overallScale;
    }
    public virtual void Awake()
    {
        if (NucleusSpacing.points != null)
        {
            return;
        }

        // Copy all the points from the mesh to a new list which we will work on
        MeshFilter meshFilter = (MeshFilter)this.GetComponent(typeof(MeshFilter));

        Vector3[] verts = meshFilter.mesh.vertices;
        Dictionary <string, VertexMapping> vertHash = new Dictionary <string, VertexMapping>();
        Dictionary <int, int> vertexIdMapping       = new Dictionary <int, int>();

        for (int vi = 0; vi < verts.Length; vi++)
        {
            string key = Mathf.RoundToInt(verts[vi].x / vertexDictionaryUnit)
                         + "," + Mathf.RoundToInt(verts[vi].y / vertexDictionaryUnit)
                         + "," + Mathf.RoundToInt(verts[vi].z / vertexDictionaryUnit);

            if (!vertHash.ContainsKey(key))
            {
                vertHash[key]     = new VertexMapping();
                vertHash[key].pos = verts[vi];
                vertHash[key].ids = new ArrayList();
            }
            vertHash[key].ids.Add(vi);
        }

        int i  = 0;
        int ii = 0;

        NucleusPoint[] allPoints = new NucleusPoint[vertHash.Count];
        i = 0;
        foreach (KeyValuePair <string, VertexMapping> entry in vertHash)
        {
            NucleusPoint np = new NucleusPoint();
            np.neighbors = new int[this.prefilterNeighborSpaces];
            np.pos       = entry.Value.pos;
            np.dist      = entry.Value.pos.sqrMagnitude;
            for (int j = 0; j < entry.Value.ids.Count; j++)
            {
                vertexIdMapping[(int)entry.Value.ids[j]] = i;
            }
            allPoints[i] = np;
            i++;
        }

        // Copy all neighbor relationships from the triangles to the new list
        int[] tris = meshFilter.mesh.triangles;
        i = 0;
        while (i < tris.Length)
        {
            int          t0 = vertexIdMapping[tris[i]];
            int          t1 = vertexIdMapping[tris[i + 1]];
            int          t2 = vertexIdMapping[tris[i + 2]];
            NucleusPoint p0 = allPoints[t0];
            NucleusPoint p1 = allPoints[t1];
            NucleusPoint p2 = allPoints[t2];
            p0.neighbors[p0.tempIndex]     = t1;
            p0.neighbors[p0.tempIndex + 1] = t2;
            p1.neighbors[p1.tempIndex]     = t0;
            p1.neighbors[p1.tempIndex + 1] = t2;
            p2.neighbors[p2.tempIndex]     = t1;
            p2.neighbors[p2.tempIndex + 1] = t0;
            p0.tempIndex = p0.tempIndex + 2;
            p1.tempIndex = p1.tempIndex + 2;
            p2.tempIndex = p2.tempIndex + 2;
            i            = i + 3;
        }
        // reset tempIndex to -1 so we can later identify neighbor references to unused points
        i = 0;
        while (i < allPoints.Length)
        {
            allPoints[i].tempIndex = -1;
            i++;
        }
        // copy the needed points to the final list, and store the new index in tempIndex
        int   added        = 0;
        float lastDistance = -1f;
        int   distRank     = 0;

        NucleusSpacing.points = new NucleusPoint[this.maxTotalNucleons];
        while (added < this.maxTotalNucleons)
        {
            float nextDistance = 1000f;
            i = 0;
            while (i < allPoints.Length)
            {
                if ((allPoints[i].dist < nextDistance) && (allPoints[i].dist > (lastDistance + this.smallFudge)))
                {
                    nextDistance = allPoints[i].dist;
                }
                i++;
            }
            i = 0;
            while (i < allPoints.Length)
            {
                float delta = allPoints[i].dist - nextDistance;
                if (delta < 0)
                {
                    delta = -delta;
                }
                if ((delta < this.smallFudge) && (added < this.maxTotalNucleons))
                {
                    allPoints[i].dist            = -10;
                    allPoints[i].distRank        = distRank;
                    allPoints[i].tempIndex       = added;
                    NucleusSpacing.points[added] = allPoints[i];
                    added++;
                }
                i++;
            }
            lastDistance = nextDistance;
            distRank++;
        }
        // Remove redundant neighbor relationships and update neighbor relationships to new index. points on the edge will have some neighbor spaces == -1
        i = 0;
        while (i < NucleusSpacing.points.Length)
        {
            int   nni     = 0;
            int   nnAdded = 0;
            int[] nn      = new int[12]; // 12 neighbors in 3d hexagonal closest packing
            ii = 0;
            while (ii < NucleusSpacing.points[i].neighbors.Length)
            {
                if (NucleusSpacing.points[i].neighbors[ii] != 0)
                {
                    bool found = false;
                    nni = 0;
                    while (nni < 12)
                    {
                        if (NucleusSpacing.points[i].neighbors[ii] == nn[nni])
                        {
                            found = true;
                        }
                        nni++;
                    }
                    if (!found)
                    {
                        nn[nnAdded] = NucleusSpacing.points[i].neighbors[ii];
                        nnAdded++;
                    }
                }
                ii++;
            }
            nni = 0;
            while (nni < 12)
            {
                nn[nni] = allPoints[nn[nni]].tempIndex;
                nni++;
            }
            NucleusSpacing.points[i].neighbors = nn;
            i++;
        }
    }