示例#1
0
        public static Transform FindParentToAssignTo(BrushIntersection intersection)
        {
            if (intersection.brush == null ||
                SelectionUtility.IsPrefab(intersection.brush.gameObject))
            {
                var lastModel = SelectionUtility.LastUsedModel;
                if (lastModel == null ||
                    SelectionUtility.IsPrefab(lastModel.gameObject))
                {
                    return(null);
                }

                return(lastModel.transform);
            }

            var hoverParent = intersection.brush.transform.parent;
            var iterator    = hoverParent;

            while (iterator != null)
            {
                var node = iterator.GetComponent <CSGNode>();
                if (node != null)
                {
                    hoverParent = node.transform;
                }
                iterator = iterator.transform.parent;
            }
            return(hoverParent);
        }
示例#2
0
        void EnableVisualObjects()
        {
            //Debug.Log("EnableVisualObjects");
            if (visualDragGameObject == null ||
                visualDragGameObject.Count != dragGameObjects.Count)
            {
                CreateVisualObjects();
            }
            else
            {
                for (int i = 0; i < dragGameObjects.Count; i++)
                {
                    if (visualDragGameObject[i] == null || !visualDragGameObject[i])
                    {
                        continue;
                    }
                    visualDragGameObject[i].SetActive(dragGameObjects[i].activeSelf);
                }
            }

            int counter = 0;

            foreach (var obj in visualDragGameObject)
            {
                if (obj == null || !obj)
                {
                    continue;
                }
                obj.transform.rotation = hoverRotation;
                obj.transform.position = hoverPosition;
                if (hoverParent != null && !SelectionUtility.IsPrefab(hoverParent.gameObject))
                {
                    obj.transform.SetParent(hoverParent, true);
                }
                obj.transform.SetSiblingIndex(hoverSiblingIndex + counter);
                counter++;
            }

            /*
             * InternalCSGModelManager.CheckTransformChanged();
             * InternalCSGModelManager.OnHierarchyModified();
             * InternalCSGModelManager.UpdateMeshes(forceUpdate: true);
             * MeshInstanceManager.UpdateHelperSurfaceVisibility();
             */
        }
示例#3
0
        void EnableVisualObjects()
        {
            if (visualDragGameObject == null ||
                visualDragGameObject.Count != dragGameObjects.Count)
            {
                CreateVisualObjects();
            }
            else
            {
                for (int i = 0; i < dragGameObjects.Count; i++)
                {
                    if (visualDragGameObject[i] == null || !visualDragGameObject[i])
                    {
                        continue;
                    }
                    visualDragGameObject[i].SetActive(dragGameObjects[i].activeSelf);
                }
            }

            int counter = 0;

            foreach (var obj in visualDragGameObject)
            {
                if (obj == null || !obj)
                {
                    continue;
                }
                obj.transform.rotation = hoverRotation;
                obj.transform.position = hoverPosition;
                if (hoverParent != null && !SelectionUtility.IsPrefab(hoverParent.gameObject))
                {
                    obj.transform.SetParent(hoverParent, true);
                }
                obj.transform.SetSiblingIndex(hoverSiblingIndex + counter);
                counter++;
            }
        }
        public bool UpdateSelection(HashSet <CSGNode> newTargetNodes, HashSet <Transform> newTargetOthers)
        {
            if (newTargetNodes == null)
            {
                newTargetNodes = new HashSet <CSGNode>();
            }
            if (newTargetOthers == null)
            {
                newTargetOthers = new HashSet <Transform>();
            }

            this.RemovedNodes  = null;
            this.RemovedOthers = null;

            this.AddedNodes  = null;
            this.AddedOthers = null;

            var foundRemovedNodes = new List <CSGNode>();

            if (this.NodeTargets != null &&
                this.NodeTargets.Length > 0)
            {
                if (newTargetNodes.Count == 0)
                {
                    foundRemovedNodes.AddRange(this.NodeTargets);
                }
                else
                {
                    for (int i = 0; i < this.NodeTargets.Length; i++)
                    {
                        if (!this.NodeTargets[i] ||
                            !newTargetNodes.Contains(this.NodeTargets[i]))
                        {
                            foundRemovedNodes.Add(this.NodeTargets[i]);
                        }
                    }
                }
            }

            var foundRemovedOthers = new List <Transform>();

            if (this.OtherTargets != null &&
                this.OtherTargets.Length > 0)
            {
                if (newTargetOthers.Count == 0)
                {
                    foundRemovedOthers.AddRange(this.OtherTargets);
                }
                else
                {
                    for (int i = 0; i < this.OtherTargets.Length; i++)
                    {
                        if (!this.OtherTargets[i] ||
                            !newTargetOthers.Contains(this.OtherTargets[i]))
                        {
                            foundRemovedOthers.Add(this.OtherTargets[i]);
                        }
                    }
                }
            }

            var originalTargetNodeCount  = (this.NodeTargets == null) ? 0 : this.NodeTargets.Length;
            var originalTargetOtherCount = (this.OtherTargets == null) ? 0 : this.OtherTargets.Length;

            // If our counts are the same and nothing is removed (and nothing could've been added), nothing has changed.
            if (newTargetNodes.Count == originalTargetNodeCount &&
                newTargetOthers.Count == originalTargetOtherCount &&
                foundRemovedNodes.Count == 0 &&
                foundRemovedOthers.Count == 0)
            {
                //Debug.Log(originalTargetNodeCount + " " + originalTargetOtherCount + " " + newTargetNodes .Count + " " + newTargetOthers .Count);
                return(false);
            }

            Validate();

            foreach (var node in foundRemovedNodes)
            {
                ArrayUtility.Remove(ref this.NodeTargets, node);

                var brush = node as CSGBrush;
                if (brush != null)
                {
                    ArrayUtility.Remove(ref this.BrushTargets, brush);
                    continue;
                }
                var operation = node as CSGOperation;
                if (node is CSGOperation)
                {
                    ArrayUtility.Remove(ref this.OperationTargets, operation);
                    continue;
                }
                var model = node as CSGModel;
                if (node is CSGModel)
                {
                    ArrayUtility.Remove(ref this.ModelTargets, model);
                    continue;
                }
            }

            foreach (var other in foundRemovedOthers)
            {
                ArrayUtility.Remove(ref this.OtherTargets, other);
            }

            var foundAddedNodes = new List <CSGNode>();

            foreach (var node in newTargetNodes)
            {
                if (this.NodeTargets != null &&
                    ArrayUtility.Contains(this.NodeTargets, node))
                {
                    continue;
                }

                if (this.NodeTargets == null)
                {
                    this.NodeTargets = new CSGNode[] { node };
                }
                else
                {
                    ArrayUtility.Add(ref this.NodeTargets, node);
                }

                foundAddedNodes.Add(node);

                if (SelectionUtility.IsPrefab(node.gameObject))
                {
                    continue;
                }

                var brush = node as CSGBrush;
                if (brush != null)
                {
                    if (this.BrushTargets == null)
                    {
                        this.BrushTargets = new CSGBrush[] { brush }
                    }
                    ;
                    else
                    {
                        ArrayUtility.Add(ref this.BrushTargets, brush);
                    }
                    continue;
                }
                var operation = node as CSGOperation;
                if (node is CSGOperation)
                {
                    if (this.OperationTargets == null)
                    {
                        this.OperationTargets = new CSGOperation[] { operation }
                    }
                    ;
                    else
                    {
                        ArrayUtility.Add(ref this.OperationTargets, operation);
                    }
                    continue;
                }
                var model = node as CSGModel;
                if (node is CSGModel)
                {
                    if (this.ModelTargets == null)
                    {
                        this.ModelTargets = new CSGModel[] { model }
                    }
                    ;
                    else
                    {
                        ArrayUtility.Add(ref this.ModelTargets, model);
                    }
                    continue;
                }
            }

            var foundAddedOthers = new List <Transform>();

            foreach (var other in newTargetOthers)
            {
                if (this.OtherTargets != null &&
                    ArrayUtility.Contains(this.OtherTargets, other))
                {
                    continue;
                }

                if (this.OtherTargets == null)
                {
                    this.OtherTargets = new Transform[] { other }
                }
                ;
                else
                {
                    ArrayUtility.Add(ref this.OtherTargets, other);
                }
                foundAddedOthers.Add(other);
            }

            for (int i = foundAddedNodes.Count - 1; i >= 0; i--)
            {
                var node  = foundAddedNodes[i];
                var brush = node as CSGBrush;
                if (brush != null)
                {
                    var brush_cache = InternalCSGModelManager.GetBrushCache(brush);
                    if (brush_cache == null ||
                        brush_cache.childData == null ||
                        brush_cache.childData.Model == null)
                    {
                        continue;
                    }

                    var childModel = brush_cache.childData.Model;
                    if (childModel &&
                        childModel.isActiveAndEnabled)
                    {
                        SelectionUtility.LastUsedModel = childModel;
                    }
                    break;
                }
                var operation = node as CSGOperation;
                if (operation != null)
                {
                    var operation_cache = InternalCSGModelManager.GetOperationCache(operation);
                    if (operation_cache == null ||
                        operation_cache.ChildData == null ||
                        operation_cache.ChildData.Model == null)
                    {
                        continue;
                    }

                    SelectionUtility.LastUsedModel = operation_cache.ChildData.Model;
                    break;
                }
                var model = node as CSGModel;
                if (model &&
                    model.isActiveAndEnabled)
                {
                    SelectionUtility.LastUsedModel = model;
                    break;
                }
            }

            this.RemovedNodes  = foundRemovedNodes.ToArray();
            this.RemovedOthers = foundRemovedOthers.ToArray();

            this.AddedNodes  = foundAddedNodes.ToArray();
            this.AddedOthers = foundAddedOthers.ToArray();

            return(foundAddedNodes.Count > 0 || foundRemovedNodes.Count > 0 || foundAddedOthers.Count > 0 || foundRemovedOthers.Count > 0);
        }
示例#5
0
        static public void OnInspectorGUI(Editor editor, UnityEngine.Object[] targets)
        {
            TooltipUtility.InitToolTip(editor);
            try
            {
                var models = new CSGModel[targets.Length];

                for (int i = targets.Length - 1; i >= 0; i--)
                {
                    models[i] = targets[i] as CSGModel;
                    if (!models[i])
                    {
                        ArrayUtility.RemoveAt(ref models, i);
                    }
                }

                CSG_GUIStyleUtility.InitStyles();
                ShowRealtimeCSGDisabledMessage();

                if (models.Length > 0 && models.Length == targets.Length)
                {
                    CSGModelComponentInspectorGUI.OnInspectorGUI(targets);
                    return;
                }

                var filteredSelection = EditModeManager.FilteredSelection;
                var targetNodes       = filteredSelection.NodeTargets;
                var targetModels      = filteredSelection.ModelTargets;
                var targetBrushes     = filteredSelection.BrushTargets;
                var targetOperations  = filteredSelection.OperationTargets;
                if (targetNodes == null)
                {
                    return;
                }



                bool?isPrefab = false;
                PrefabInstantiateBehaviour?prefabBehaviour            = PrefabInstantiateBehaviour.Reference;
                PrefabSourceAlignment?     prefabSourceAlignment      = PrefabSourceAlignment.AlignedTop;
                PrefabDestinationAlignment?prefabDestinationAlignment = PrefabDestinationAlignment.AlignToSurface;

                if (targetNodes.Length > 0)
                {
                    var gameObject = targetNodes[0].gameObject;
                    isPrefab                   = SelectionUtility.IsPrefab(gameObject);
                    prefabBehaviour            = targetNodes[0].PrefabBehaviour;
                    prefabSourceAlignment      = targetNodes[0].PrefabSourceAlignment;
                    prefabDestinationAlignment = targetNodes[0].PrefabDestinationAlignment;
                    for (int i = 1; i < targetNodes.Length; i++)
                    {
                        gameObject = targetNodes[i].gameObject;
                        var currentIsPrefab                   = PrefabUtility.GetPrefabParent(gameObject) == null && PrefabUtility.GetPrefabObject(gameObject) != null && gameObject.transform.parent == null;
                        var currentPrefabBehaviour            = targetNodes[i].PrefabBehaviour;
                        var currentPrefabSourceAlignment      = targetNodes[i].PrefabSourceAlignment;
                        var currentPrefabDestinationAlignment = targetNodes[i].PrefabDestinationAlignment;
                        if (isPrefab.HasValue && isPrefab.Value != currentIsPrefab)
                        {
                            isPrefab = null;
                        }
                        if (prefabBehaviour.HasValue && prefabBehaviour.Value != currentPrefabBehaviour)
                        {
                            prefabBehaviour = null;
                        }
                        if (prefabSourceAlignment.HasValue && prefabSourceAlignment.Value != currentPrefabSourceAlignment)
                        {
                            prefabSourceAlignment = null;
                        }
                        if (prefabDestinationAlignment.HasValue && prefabDestinationAlignment.Value != currentPrefabDestinationAlignment)
                        {
                            prefabDestinationAlignment = null;
                        }
                    }
                }

                GUILayout.BeginVertical(GUI.skin.box);
                {
                    if (isPrefab.HasValue && isPrefab.Value)
                    {
                        EditorGUILayout.LabelField(PrefabLabelContent);
                    }
                    else
                    {
                        EditorGUILayout.LabelField(RaySnappingLabelContent);
                        TooltipUtility.SetToolTip(RaySnappingBehaviourTooltip);
                    }

                    EditorGUI.indentLevel++;
                    {
                        if (isPrefab.HasValue && isPrefab.Value)
                        {
                            EditorGUI.showMixedValue = !prefabBehaviour.HasValue;
                            var prefabBehavour = prefabBehaviour.HasValue ? prefabBehaviour.Value : PrefabInstantiateBehaviour.Reference;
                            EditorGUI.BeginChangeCheck();
                            {
                                prefabBehavour = (PrefabInstantiateBehaviour)EditorGUILayout.EnumPopup(PrefabInstantiateBehaviourContent, prefabBehavour);
                                TooltipUtility.SetToolTip(PrefabInstantiateBehaviourTooltip);
                            }
                            if (EditorGUI.EndChangeCheck())
                            {
                                for (int i = 0; i < targetNodes.Length; i++)
                                {
                                    targetNodes[i].PrefabBehaviour = prefabBehavour;
                                }
                            }
                            EditorGUI.showMixedValue = false;
                        }


                        EditorGUI.showMixedValue = !prefabDestinationAlignment.HasValue;
                        var destinationAlignment = prefabDestinationAlignment.HasValue ? prefabDestinationAlignment.Value : PrefabDestinationAlignment.AlignToSurface;
                        EditorGUI.BeginChangeCheck();
                        {
                            destinationAlignment = (PrefabDestinationAlignment)EditorGUILayout.EnumPopup(DestinationAlignmentContent, destinationAlignment);
                            TooltipUtility.SetToolTip(DestinationAlignmentTooltip);
                        }
                        if (EditorGUI.EndChangeCheck())
                        {
                            for (int i = 0; i < targetNodes.Length; i++)
                            {
                                targetNodes[i].PrefabDestinationAlignment = destinationAlignment;
                            }
                        }
                        EditorGUI.showMixedValue = false;


                        EditorGUI.showMixedValue = !prefabSourceAlignment.HasValue;
                        var sourceAlignment = prefabSourceAlignment.HasValue ? prefabSourceAlignment.Value : PrefabSourceAlignment.AlignedFront;
                        EditorGUI.BeginChangeCheck();
                        {
                            sourceAlignment = (PrefabSourceAlignment)EditorGUILayout.EnumPopup(SourceAlignmentContent, sourceAlignment);
                            TooltipUtility.SetToolTip(SourceAlignmentTooltip);
                        }
                        if (EditorGUI.EndChangeCheck())
                        {
                            for (int i = 0; i < targetNodes.Length; i++)
                            {
                                targetNodes[i].PrefabSourceAlignment = sourceAlignment;
                            }
                        }
                        EditorGUI.showMixedValue = false;
                    }
                    EditorGUI.indentLevel--;
                }
                GUILayout.EndVertical();
                GUILayout.Space(10);


                if (targetModels.Length == 0)
                {
                    int              invalidOperationType = 999;
                    bool?            handleAsOne          = null;
                    bool             selMixedValues       = false;
                    CSGOperationType operationType        = (CSGOperationType)invalidOperationType;
                    bool             opMixedValues        = false;
                    uint?            targetContentLayer   = null;
                    if (targetBrushes.Length > 0)
                    {
                        operationType      = targetBrushes[0].OperationType;
                        targetContentLayer = targetBrushes[0].ContentLayer;
                    }
                    for (int b = 1; b < targetBrushes.Length; b++)
                    {
                        var brush = targetBrushes[b];
                        if (operationType != brush.OperationType)
                        {
                            opMixedValues = true;
                        }
                        if (targetContentLayer.HasValue && targetContentLayer.Value != brush.ContentLayer)
                        {
                            targetContentLayer = null;
                        }
                    }
                    foreach (var operation in targetOperations)
                    {
                        if (operationType == (CSGOperationType)invalidOperationType)
                        {
                            operationType = operation.OperationType;
                        }
                        else
                        if (operationType != operation.OperationType)
                        {
                            opMixedValues = true;
                        }

                        if (!handleAsOne.HasValue)
                        {
                            handleAsOne = operation.HandleAsOne;
                        }
                        else
                        if (handleAsOne.Value != operation.HandleAsOne)
                        {
                            selMixedValues = true;
                        }
                    }
                    GUILayout.BeginVertical(GUI.skin.box);
                    {
                        bool passThroughValue = false;
                        if (targetBrushes.Length == 0 && targetOperations.Length > 0)                         // only operations
                        {
                            bool?passThrough = targetOperations[0].PassThrough;
                            for (int i = 1; i < targetOperations.Length; i++)
                            {
                                if (passThrough.HasValue && passThrough.Value != targetOperations[i].PassThrough)
                                {
                                    passThrough = null;
                                    break;
                                }
                            }

                            opMixedValues = !passThrough.HasValue || passThrough.Value;

                            var ptMixedValues = !passThrough.HasValue;
                            passThroughValue = passThrough.HasValue ? passThrough.Value : false;
                            if (CSG_EditorGUIUtility.PassThroughButton(passThroughValue, ptMixedValues))
                            {
                                Undo.RecordObjects(targetNodes, "Changed CSG operation of nodes");
                                foreach (var operation in targetOperations)
                                {
                                    operation.PassThrough = true;
                                }
                                InternalCSGModelManager.Refresh();
                                EditorApplication.RepaintHierarchyWindow();
                            }

                            if (passThroughValue)
                            {
                                operationType = (CSGOperationType)255;
                            }
                        }
                        EditorGUI.BeginChangeCheck();
                        {
                            operationType = CSG_EditorGUIUtility.ChooseOperation(operationType, opMixedValues);
                        }
                        if (EditorGUI.EndChangeCheck())
                        {
                            Undo.RecordObjects(targetNodes, "Changed CSG operation of nodes");
                            foreach (var brush in targetBrushes)
                            {
                                brush.OperationType = operationType;
                            }
                            foreach (var operation in targetOperations)
                            {
                                operation.PassThrough   = false;
                                operation.OperationType = operationType;
                            }
                            InternalCSGModelManager.Refresh();
                            EditorApplication.RepaintHierarchyWindow();
                        }
                    }
                    GUILayout.EndVertical();

                    if (targetOperations.Length == 0 && targetModels.Length == 0)
                    {
                        GUILayout.Space(10);

                        /*
                         * GUILayout.BeginVertical(GUI.skin.box);
                         * {
                         *      GUILayout.Label("Experimental");
                         *      EditorGUI.indentLevel++;
                         *      {
                         *              EditorGUI.showMixedValue = !targetContentLayer.HasValue;
                         *              var contentLayer = targetContentLayer.HasValue ? targetContentLayer.Value : 0;
                         *              EditorGUI.BeginChangeCheck();
                         *              {
                         *                      contentLayer = (uint)EditorGUILayout.Popup(ContentLayerContent, (int)contentLayer, ContentLayerTexts);
                         *                      TooltipUtility.SetToolTip(ContentLayerTooltip);
                         *              }
                         *              if (EditorGUI.EndChangeCheck())
                         *              {
                         *                      for (int i = 0; i < targetBrushes.Length; i++)
                         *                      {
                         *                              targetBrushes[i].ContentLayer = contentLayer;
                         *                      }
                         *              }
                         *      }
                         *      EditorGUI.indentLevel--;
                         * }
                         * GUILayout.EndVertical();
                         *
                         * GUILayout.Space(10);
                         * //*/
                        if (targetBrushes.Length == 1)
                        {
                            GUILayout.BeginVertical(GUI.skin.box);
                            {
                                EditorGUI.indentLevel++;
                                OpenSurfaces = EditorGUILayout.Foldout(OpenSurfaces, SurfacesContent);
                                EditorGUI.indentLevel--;
                                if (OpenSurfaces)
                                {
                                    var targetShape     = targetBrushes[0].Shape;
                                    var texGens         = targetShape.TexGens;
                                    var texGenFlagArray = targetShape.TexGenFlags;
                                    for (int t = 0; t < texGens.Length; t++)
                                    {
                                        GUILayout.Space(2);

                                        var texGenFlags = texGenFlagArray[t];
                                        var material    = targetShape.TexGens[t].RenderMaterial;
                                        EditorGUI.BeginChangeCheck();
                                        {
                                            GUILayout.BeginHorizontal();
                                            {
                                                GUILayout.Space(4);
                                                material = CSG_EditorGUIUtility.MaterialImage(material);
                                                GUILayout.Space(2);
                                                GUILayout.BeginVertical();
                                                {
                                                    EditorGUI.BeginDisabledGroup(texGenFlags != TexGenFlags.None);
                                                    {
                                                        material = EditorGUILayout.ObjectField(material, typeof(Material), true) as Material;
                                                    }
                                                    EditorGUI.EndDisabledGroup();

                                                    texGenFlags = EditModeCommonGUI.OnSurfaceFlagButtons(texGenFlags);
                                                }
                                                GUILayout.EndVertical();
                                                GUILayout.Space(4);
                                            }
                                            GUILayout.EndHorizontal();
                                        }
                                        if (EditorGUI.EndChangeCheck())
                                        {
                                            var selectedBrushSurfaces = new []
                                            {
                                                new SelectedBrushSurface(targetBrushes[0], t)
                                            };
                                            using (new UndoGroup(selectedBrushSurfaces, "discarding surface"))
                                            {
                                                texGenFlagArray[t] = texGenFlags;
                                                targetShape.TexGens[t].RenderMaterial = material;
                                            }
                                        }
                                        GUILayout.Space(4);
                                    }
                                }
                            }
                            GUILayout.EndVertical();
                        }
                    }

                    if (handleAsOne.HasValue)
                    {
                        EditorGUI.BeginChangeCheck();
                        {
                            EditorGUI.showMixedValue = selMixedValues;
                            handleAsOne = EditorGUILayout.Toggle(HandleAsOneLabel, handleAsOne.Value);
                        }
                        if (EditorGUI.EndChangeCheck())
                        {
                            Undo.RecordObjects(targetNodes, "Changed CSG operation 'Handle as one object'");
                            foreach (var operation in targetOperations)
                            {
                                operation.HandleAsOne = handleAsOne.Value;
                            }
                            EditorApplication.RepaintHierarchyWindow();
                        }
                    }
                }

        #if false
                if (targetNodes.Length == 1)
                {
                    var node  = targetNodes[0];
                    var brush = node as CSGBrush;
                    if (brush != null)
                    {
                        var brush_cache = CSGSceneManager.GetBrushCache(brush);
                        if (brush_cache == null ||
                            brush_cache.childData == null ||
                            brush_cache.childData.modelTransform == null)
                        {
                            EditorGUILayout.LabelField("brush-cache: null");
                        }
                        else
                        {
                            EditorGUILayout.LabelField("node-id: " + brush.nodeID + " brush-id: " + brush.brushID);
                        }
                    }
                    var operation = node as CSGOperation;
                    if (operation != null)
                    {
                        var operation_cache = CSGSceneManager.GetOperationCache(operation);
                        if (operation_cache == null ||
                            operation_cache.childData == null ||
                            operation_cache.childData.modelTransform == null)
                        {
                            EditorGUILayout.LabelField("operation-cache: null");
                        }
                        else
                        {
                            EditorGUILayout.LabelField("operation-id: " + operation.nodeID + " operation-id: " + operation.operationID);
                        }
                    }
                    var model = node as CSGModel;
                    if (model != null)
                    {
                        var model_cache = CSGSceneManager.GetModelCache(model);
                        if (model_cache == null ||
                            model_cache.meshContainer == null)
                        {
                            EditorGUILayout.LabelField("model-cache: null");
                        }
                        else
                        {
                            EditorGUILayout.LabelField("model-id: " + model.nodeID + " model-id: " + model.modelID);
                        }
                    }
                }
        #endif
            }
            finally
            {
                TooltipUtility.DrawToolTip(getLastRect: true, goUp: true);
            }
        }