/// <summary>
        /// Call this method to begin the threaded Octree operations.
        /// </summary>
        /// <param name="otp"<see cref="OctreeThreadParameters"/></param>
        public void ThreadOctreeInit(OctreeThreadParameters otp, bool restrictToMainThread)
        {
            if (restrictToMainThread)
            {
                TrackedObjectStates = evaluateOctree(otp);
                return;
            }

            ThreadRunning = true;

            IsDone = false;
            base.StartOctree(otp);
        }
 protected virtual void ThreadOctree(OctreeThreadParameters otp)
 {
 }
 public virtual void StartOctree(OctreeThreadParameters otp)
 {
     JobThread = new Thread(new ParameterizedThreadStart(runOctree));
     JobThread.Start(otp);
 }
        public void Start()
        {
            usingTriggers = TrackingMode == TrackingMode.UnityTriggers;

            agentMonitors = GameObject.FindObjectsOfType <WorldMonitors>();

            /*
             * Start procedure is O(i*j*k) and a faster solution may exist
             */

            for (int i = 0; i < agentMonitors.Length; i++)
            {
                for (int j = 0; j < agentMonitors[i].TrackedObjects.Count; j++)
                {
                    for (int k = 0; k < agentMonitors[i].TrackedObjects[j].TrackedObjects.Count; k++)
                    {
                        // double threshold size if user wishes for point octree to mimic trigger interaction distances
                        float threshold = (OctreeMimicTriggerInteraction && TrackingMode == TrackingMode.Octree) ? agentMonitors[i].ThresholdSet[j] * 3 / 2 : agentMonitors[i].ThresholdSet[j];

                        GameObject go = agentMonitors[i].TrackedObjects[j].TrackedObjects[k];

                        if (go) // allows user to leave empty gameobject slots in tracked object inspector
                        {
                            int id;
                            if (GameObjectIDReference.TryGetValue(go, out id))
                            {
                                TrackedObjectData TOData;
                                TrackedObjectDataRef.TryGetValue(id, out TOData);
                                TOData.ObjectOwners.Add(agentMonitors[i]);
                                TrackedObjectDataRef[id] = TOData;

                                if (usingTriggers)
                                {
                                    // WorldMonitors will declare ownership in its Start() method

                                    if (!go.GetComponent <TrackedObjectTriggers>())
                                    {
                                        go.AddComponent <TrackedObjectTriggers>();
                                    }

                                    go.GetComponent <TrackedObjectTriggers>().Initialize();
                                }
                            }
                            else
                            {
                                GameObjectIDReference.Add(go, TotalTrackedObjects); //object ID = current number of tracked objects
                                gameObjectReference.Add(TotalTrackedObjects, go);   //using IDs necessary to run aux thread

                                TrackedObjectData TOData = new TrackedObjectData
                                {
                                    Object       = go,
                                    Threshold    = threshold,
                                    ObjectOwners = new List <WorldMonitors>()
                                };

                                TOData.ObjectOwners.Add(agentMonitors[i]);
                                TrackedObjectDataRef.Add(TotalTrackedObjects, TOData);
                                TrackedObjectAffiliations.Add(TotalTrackedObjects, OTIEditorBase._AlphabetAssembler(j));

                                if (usingTriggers)
                                {
                                    if (!go.GetComponent <TrackedObjectTriggers>())
                                    {
                                        go.AddComponent <TrackedObjectTriggers>();
                                    }
                                    go.GetComponent <TrackedObjectTriggers>().Initialize();
                                }

                                TotalTrackedObjects++;
                            }
                        }
                    }
                }
            }

            AllocationSpace = TotalTrackedObjects;
            FreeSpace       = MaximumObjectsAllowed - AllocationSpace >= 0;

            if (usingTriggers || ExhaustiveMethod)
            {
                return; // no more set up required; switching between modes is not allowed at runtime.
            }
            Octree = new Octree();

            //configure tracked object states at start
            for (int i = 0; i < TotalTrackedObjects; i++)
            {
                List <int> locals = new List <int>();

                if (Octree.MasterList.ContainsKey(i))
                {
                    Octree.MasterList[i] = locals;
                }
                else
                {
                    Octree.MasterList.Add(i, locals);
                }
            }

            OctreeThreadParameters otp = new OctreeThreadParameters
            {
                ObjectIDs           = new List <int>(TrackedObjectDataRef.Keys),
                TotalTrackedObjects = TotalTrackedObjects,
                Coordinates         = getUpdatedPositions(new List <int>(TrackedObjectDataRef.Keys)),
                DynamicObjects      = TrackedObjectAffiliations
            };

            //construct initial octree
            Octree.Initialize(InitialWorldSize, WorldOrigin, MinimumObjectSize);

            Octree.IsDone = true; //allows an initial pass into job start
            Octree.ThreadOctreeInit(otp, RestrictToMainThread);

            while (!Octree.UpdateOctree())
            {
            }                                  //wait until conflict states are established

            TrackedObjectStates = Octree.TrackedObjectStates;

            // wipe initial results so conflicts existing before start register
            for (int i = 0; i < Octree.MasterList.Count; i++)
            {
                Octree.MasterList[i] = new List <int>();
            }
        }
        void Update()
        {
            if (ExhaustiveMethod || usingTriggers) // if you are not using these features you can remove this if statement
            {
                if (ExhaustiveMethod)
                {
                    exhaustiveCalculation();
                }
                return;
            }

            if (Octree.UpdateOctree()) // job has concluded
            {
                passedFrames = 0;      // in sync with update

                TrackedObjectStates = Octree.TrackedObjectStates;

                OctreeThreadParameters otp = refreshThreadingParameters();

                int numStates = TrackedObjectStates.ParentIDEnterers.Length;

                for (int tos = 0; tos < numStates; tos++)
                {
                    int parentID = TrackedObjectStates.ParentIDEnterers[tos];

                    TrackedObjectData TOData;
                    if (TrackedObjectDataRef.TryGetValue(parentID, out TOData))
                    {
                        int numConflicts = TrackedObjectStates.EnteringIDs[tos].Length;

                        GameObject[] conflictors            = new GameObject[numConflicts]; // allocate arrays for conflict data
                        string[]     conflictorAffiliations = new string[numConflicts];

                        // fill conflict data
                        for (int i = 0; i < numConflicts; i++)
                        {
                            int m = TrackedObjectStates.EnteringIDs[tos][i];
                            conflictors[i]            = gameObjectReference[m];
                            conflictorAffiliations[i] = TrackedObjectAffiliations[m];
                        }

                        foreach (WorldMonitors wm in TOData.ObjectOwners) //inform the agents monitoring this object}
                        {
                            wm.RaiseConflictEnterers(TOData.Object, conflictors, conflictorAffiliations);
                        }
                    }
                }

                // if user wishes for no end conflict events to be raised, the update has concluded.
                if (ConflictEndMode == ConflictEndMode.NoConflictEndEvents)
                {
                    Octree.ThreadOctreeInit(otp, RestrictToMainThread);
                    return;
                }

                handleEndConflicts(ConflictEndMode, TrackedObjectStates);

                Octree.ThreadOctreeInit(otp, RestrictToMainThread);
            }
            else
            {
                passedFrames++;
            }
        }
        /// <summary>
        /// Runs current tracked object data through Octree.
        /// </summary>
        private TrackedObjectStates evaluateOctree(OctreeThreadParameters otp)
        {
            int alloc = WorldMonitor.Instance.AllocationSpace;

            updatePositions = new Vector3[alloc];  // otp.ObjectIDs.Count
            float[] thresholds = new float[alloc]; // otp.ObjectIDs.Count

            //for (int i = 0; i < otp.ObjectIDs.Count; i++)
            foreach (int id in otp.ObjectIDs)
            {
                KeyValuePair <float, Vector3> pos;

                if (!otp.Coordinates.TryGetValue(id, out pos))
                {
                    Debug.LogError("unknown object position request in octree eval");
                }

                thresholds[id]      = pos.Key;
                updatePositions[id] = pos.Value;

                PointOctree.Remove(id);
                PointOctree.Add(id, pos.Value);
            }

            List <int[]> enteringIDs = new List <int[]>();
            List <int[]> leavingIDs  = new List <int[]>();

            List <int> parentIDLeavers  = new List <int>();
            List <int> parentIDEnterers = new List <int>();

            int ind = 0;

            foreach (int id in otp.ObjectIDs)
            {
                List <int> validConflicts = new List <int>();
                List <int> stayers        = new List <int>();

                int[] areaObjects = PointOctree.GetNearby(updatePositions[id], thresholds[id]);

                for (int j = 0; j < areaObjects.Length; j++)
                {
                    string affiliateObj     = WorldMonitor.Instance.TrackedObjectAffiliations[id];
                    string affiliateCompare = WorldMonitor.Instance.TrackedObjectAffiliations[areaObjects[j]];

                    /*
                     * run conflict validity checks: if not the same object && not an object of the same class type && is a new conflict
                     */

                    if (areaObjects[j] != id && !MasterList[id].Contains(areaObjects[j]) && string.Compare(affiliateObj, affiliateCompare) != 0)
                    {
                        if (!parentIDEnterers.Contains(id))
                        {
                            parentIDEnterers.Add(id);
                        }

                        MasterList[id].Add(areaObjects[j]); // add conflicting object to master list of current conflicts
                        validConflicts.Add(areaObjects[j]); // *new* conflicts
                        stayers.Add(areaObjects[j]);        // use to look for conflicts that have ended
                    }
                    else if (MasterList[id].Contains(areaObjects[j]))
                    {
                        stayers.Add(areaObjects[j]); // this is an object staying in conflict
                    }
                }

                bool       leaverDetected = false;
                List <int> leavers        = new List <int>();

                foreach (int _id in MasterList[id]) // look at master list's record of conflicts for this parent ID - if it isn't in stayers, it has left the conflict area or been destroyed
                {
                    if (!stayers.Contains(_id))
                    {
                        leaverDetected = true;
                        leavers.Add(_id);
                    }

                    switch (WorldMonitor.Instance.ConflictEndMode)
                    {
                    case ConflictEndMode.OnAllConflictsEnded:
                        if (leavers.Count == MasterList[id].Count)
                        {
                            parentIDLeavers.Add(id);
                        }
                        break;

                    case ConflictEndMode.OnIndividualConflictEnded:
                        if (leaverDetected && !parentIDEnterers.Contains(id) && !parentIDLeavers.Contains(id))
                        {
                            parentIDLeavers.Add(id);
                        }
                        break;
                    }
                }

                foreach (int leaver in leavers)
                {
                    MasterList[id].Remove(leaver);
                }

                int numValid = leavers.ToArray().Length;

                if (numValid > 0)
                {
                    leavingIDs.Add(leavers.ToArray());
                }

                numValid = validConflicts.ToArray().Length;

                if (numValid > 0)
                {
                    enteringIDs.Add(validConflicts.ToArray());
                }

                ind++;
            }

            return(new TrackedObjectStates
            {
                ParentIDEnterers = parentIDEnterers.ToArray(), // parent IDs of new or increased conflict states
                ParentIDLeavers = parentIDLeavers.ToArray(),   // parent IDs of expired or reduced conflict states
                LeavingIDs = leavingIDs,                       // child IDs - ended conflict(s)
                EnteringIDs = enteringIDs,                     // child IDs - the conflict(s)
                PriorConflictingIDs = parentIDLeavers          // parent IDs that need informed all conflicts have ceased
            });
        }
 protected override void ThreadOctree(OctreeThreadParameters otp)
 {
     TrackedObjectStates = evaluateOctree(otp);
     IsDone = true; // TrackedObjectStates are defined - - eliminating race conditions
 }