/// <summary>
        /// Handles addition of new tracked object if user is using Unity triggers.
        /// </summary>
        private void handleTriggerAddition(GameObject trackedObject, WorldMonitors owner, string objectAffiliation, float threshold)
        {
            if (!trackedObject.GetComponent <TrackedObjectTriggers>())
            {
                trackedObject.AddComponent <TrackedObjectTriggers>();
            }

            TrackedObjectTriggers tot = trackedObject.GetComponent <TrackedObjectTriggers>();

            GameObjectIDReference.Add(trackedObject, AllocationSpace);
            gameObjectReference.Add(AllocationSpace, trackedObject);

            TrackedObjectData TOData = new TrackedObjectData
            {
                Object       = trackedObject,
                Threshold    = TriggersMimicOctree ? 0.75f * threshold : threshold,
                ObjectOwners = new List <WorldMonitors>()
            };

            if (owner)
            {
                TOData.ObjectOwners.Add(owner);
                tot.AcceptOwner(owner);
            }

            TrackedObjectDataRef.Add(AllocationSpace, TOData);
            TrackedObjectAffiliations.Add(AllocationSpace, objectAffiliation);

            TotalTrackedObjects++;
            AllocationSpace++;
            tot.Initialize();
        }
        /// <summary>
        /// Runtime objects should be inserted into the tracking system here.
        /// </summary>
        /// <param name="trackedObject">The object to be tracked.</param>
        /// <param name="owner">Provide the WorldMonitors component from the agent tracking this object.</param>
        /// <param name="objectAffiliation">The class of objects this item is in (e.g. "A", "B", etc.)</param>
        /// <param name="threshold">Regardless of class type, this object can be inserted with its own threshold size.</param>
        /// <remarks>Due to the cost associated with this operation, perform minimal additions per frame or run from coroutine</remarks>
        public void InsertNewTrackedObject(GameObject trackedObject, WorldMonitors owner, string objectAffiliation, float threshold)
        {
            if (!FreeSpace)
            {
                return;
            }

            if (usingTriggers)
            {
                handleTriggerAddition(trackedObject, owner, objectAffiliation, threshold);
                FreeSpace = MaximumObjectsAllowed - AllocationSpace > 0;
                return;
            }

            /*
             * Do not need to add directly into Octree
             * Happens in the first update saving time from main thread
             */

            int id;

            if (GameObjectIDReference.TryGetValue(trackedObject, out id))
            {
                // allow for user to add new trackers to one object
                TrackedObjectData TOData;
                TrackedObjectDataRef.TryGetValue(id, out TOData);

                if (owner) // else the user has tried to add a non-owned object more than once
                {
                    TOData.ObjectOwners.Add(owner);
                }
            }
            else
            {
                GameObjectIDReference.Add(trackedObject, AllocationSpace);
                gameObjectReference.Add(AllocationSpace, trackedObject);

                TrackedObjectData TOData = new TrackedObjectData
                {
                    Object       = trackedObject,
                    Threshold    = OctreeMimicTriggerInteraction ? 3 / 2 * threshold : threshold,
                    ObjectOwners = new List <WorldMonitors>()
                };

                if (owner)
                {
                    TOData.ObjectOwners.Add(owner);
                }

                Octree.MasterList.Add(AllocationSpace, new List <int>());

                TrackedObjectDataRef.Add(AllocationSpace, TOData);
                TrackedObjectAffiliations.Add(AllocationSpace, objectAffiliation);

                TotalTrackedObjects++;
                AllocationSpace++;

                Octree.TrackedObjectStates = TrackedObjectStates;
                FreeSpace = MaximumObjectsAllowed - AllocationSpace >= 0;
            }
        }
        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>();
            }
        }