static void CheckSiblingPosition(CSGBrush brush)
        {
            if (!brush || !brush.gameObject.activeInHierarchy)
            {
                return;
            }

            // NOTE: returns default model when it can't find parent model
            CSGModel     parentModel;
            CSGOperation parentOp;

            FindParentOperationAndModel(brush.transform, out parentOp, out parentModel);
            if (!parentOp)
            {
                return;
            }

            if (brush.ChildData.Parent != parentOp ||
                brush.ChildData.Model != parentModel)
            {
                return;
            }

            ParentNodeDataExtensions.UpdateNodePosition(brush.hierarchyItem, parentOp.ParentData);
        }
        static void CheckBrushHierarchy(CSGBrush brush)
        {
            if (External == null)
            {
                return;
            }

            if (!brush || !brush.gameObject.activeInHierarchy)
            {
                if (!brush && brush.IsRegistered)
                {
                    OnDestroyed(brush);
                }
                return;
            }

            // make sure the node has already been initialized,
            // otherwise ignore it
            if (!brush.IsRegistered)
            {
                return;
            }

            if (RemovedBrushes.Contains(brush.brushNodeID))
            {
                return;
            }

            // NOTE: returns default model when it can't find parent model
            CSGModel     parentModel;
            CSGOperation parentOp;

            FindParentOperationAndModel(brush.transform, out parentOp, out parentModel);

            if (brush.ChildData.Parent == parentOp &&
                brush.ChildData.Model == parentModel)
            {
                if (parentOp)
                {
                    ParentNodeDataExtensions.UpdateNodePosition(brush.hierarchyItem, parentOp.ParentData);
                    return;
                }
            }

            SetCSGBrushHierarchy(brush, parentOp, parentModel);
        }
        public static void OnHierarchyModified()
        {
            if (External == null)
            {
                return;
            }

            HierarchyItem.CurrentLoopCount = (HierarchyItem.CurrentLoopCount + 1);

            UpdateRegistration();

            var startTime = EditorApplication.timeSinceStartup;

            if (OperationTransformChanged.Count > 0)
            {
                foreach (var item in OperationTransformChanged)
                {
                    if (item)
                    {
                        CheckOperationHierarchy(item);
                    }
                }
                OperationTransformChanged.Clear();
            }


            if (BrushTransformChanged.Count > 0)
            {
                foreach (var item in BrushTransformChanged)
                {
                    if (!item)
                    {
                        continue;
                    }

                    CheckBrushHierarchy(item);
                    ValidateBrush(item);                     // to detect material changes when moving between models
                }
                BrushTransformChanged.Clear();
            }
            hierarchyValidateTime = EditorApplication.timeSinceStartup - startTime;


            // remove all nodes that have been scheduled for removal
            if (RemovedBrushes.Count > 0)
            {
                External.DestroyNodes(RemovedBrushes.ToArray()); RemovedBrushes.Clear();
            }
            if (RemovedOperations.Count > 0)
            {
                External.DestroyNodes(RemovedOperations.ToArray()); RemovedOperations.Clear();
            }
            if (RemovedModels.Count > 0)
            {
                External.DestroyNodes(RemovedModels.ToArray()); RemovedModels.Clear();
            }


            for (var i = Brushes.Count - 1; i >= 0; i--)
            {
                var item = Brushes[i];
                if (item && item.brushNodeID != CSGNode.InvalidNodeID)
                {
                    continue;
                }

                UnregisterBrush(item);
            }

            for (var i = Operations.Count - 1; i >= 0; i--)
            {
                var item = Operations[i];
                if (!item || item.operationNodeID == CSGNode.InvalidNodeID)
                {
                    UnregisterOperation(item);
                    continue;
                }

                if (!item.ParentData.Transform)
                {
                    item.ParentData.Init(item, item.operationNodeID);
                }
            }

            for (var i = Models.Length - 1; i >= 0; i--)
            {
                var item = Models[i];
                if (!item || item.modelNodeID == CSGNode.InvalidNodeID)
                {
                    UnregisterModel(item);
                    continue;
                }

                if (!item.parentData.Transform)
                {
                    item.parentData.Init(item, item.modelNodeID);
                }
            }

            startTime = EditorApplication.timeSinceStartup;
            for (var i = Operations.Count - 1; i >= 0; i--)
            {
                var item = Operations[i];
                if (!item || item.operationNodeID == CSGNode.InvalidNodeID)
                {
                    continue;
                }

                var parentData = item.ChildData.OwnerParentData;
                if (parentData == null)
                {
                    continue;
                }

                ParentNodeDataExtensions.UpdateNodePosition(item.ParentData, parentData);
            }

            for (var i = Brushes.Count - 1; i >= 0; i--)
            {
                var item = Brushes[i];
                if (!item || item.brushNodeID == CSGNode.InvalidNodeID)
                {
                    continue;
                }

                var parentData = item.ChildData.OwnerParentData;
                if (parentData == null)
                {
                    continue;
                }

                ParentNodeDataExtensions.UpdateNodePosition(item.hierarchyItem, parentData);
            }

            if (External.SetChildNodes != null)
            {
                for (var i = 0; i < Operations.Count; i++)
                {
                    var item = Operations[i];
                    if (!item || item.operationNodeID == CSGNode.InvalidNodeID)
                    {
                        continue;
                    }

                    if (!item.ParentData.ChildrenModified)
                    {
                        continue;
                    }

                    var childList = UpdateChildList(item.ParentData);

                    External.SetChildNodes(item.operationNodeID, childList.Length, childList);
                    item.ParentData.ChildrenModified = false;
                }

                for (var i = 0; i < Models.Length; i++)
                {
                    var item = Models[i];
                    if (!item || item.modelNodeID == CSGNode.InvalidNodeID)
                    {
                        continue;
                    }

                    if (!item.parentData.ChildrenModified)
                    {
                        continue;
                    }

                    var childList = UpdateChildList(item.parentData);

                    External.SetChildNodes(item.modelNodeID, childList.Length, childList);
                    item.parentData.ChildrenModified = false;
                }
            }

            updateHierarchyTime = EditorApplication.timeSinceStartup - startTime;
        }