diff --git a/Assets/Editor/ArchitectureInteractionEditor.cs b/Assets/Editor/ArchitectureInteractionEditor.cs deleted file mode 100644 index 478a74d019..0000000000 --- a/Assets/Editor/ArchitectureInteractionEditor.cs +++ /dev/null @@ -1,72 +0,0 @@ -using SEE.Game; -using SEE.Game.City; -using UnityEditor; -using UnityEngine; - -#if UNITY_EDITOR - -namespace SEEEditor -{ - /// - /// Editor for ArchitectureInteraction. - /// - //[Obsolete("Introduced only for capturing videos.")] - [CustomEditor(typeof(ArchitectureInteraction))] - public class ArchitectureInteractionEditor : Editor - { - public override void OnInspectorGUI() - { - ArchitectureInteraction animator = target as ArchitectureInteraction; - - animator.CodeCity = (SEECity)EditorGUILayout.ObjectField - (label: "Code City", - obj: animator.CodeCity, - objType: typeof(SEECity), - allowSceneObjects: true); - - EditorGUILayout.BeginHorizontal(); - GUILayout.Label("Implementation Edges"); - { - animator.ImplementationEdgesVisible = EditorGUILayout.Toggle(animator.ImplementationEdgesVisible); - animator.ImplementationEdgesStartColor = EditorGUILayout.ColorField(animator.ImplementationEdgesStartColor); - animator.ImplementationEdgesEndColor = EditorGUILayout.ColorField(animator.ImplementationEdgesEndColor); - animator.ImplementationEdgesWidth = EditorGUILayout.FloatField(animator.ImplementationEdgesWidth); - } - EditorGUILayout.EndHorizontal(); - - EditorGUILayout.BeginHorizontal(); - GUILayout.Label("Architecture Edges"); - { - animator.ArchitectureEdgesVisible = EditorGUILayout.Toggle(animator.ArchitectureEdgesVisible); - animator.ArchitectureEdgesStartColor = EditorGUILayout.ColorField(animator.ArchitectureEdgesStartColor); - animator.ArchitectureEdgesEndColor = EditorGUILayout.ColorField(animator.ArchitectureEdgesEndColor); - animator.ArchitectureEdgesWidth = EditorGUILayout.FloatField(animator.ArchitectureEdgesWidth); - } - EditorGUILayout.EndHorizontal(); - - EditorGUILayout.BeginHorizontal(); - GUILayout.Label("Reflexion Edges"); - { - animator.ReflexionEdgesVisible = EditorGUILayout.Toggle(animator.ReflexionEdgesVisible); - animator.ReflexionEdgesWidth = EditorGUILayout.FloatField(animator.ReflexionEdgesWidth); - } - EditorGUILayout.EndHorizontal(); - - EditorGUILayout.BeginHorizontal(); - { - GUILayout.Label("Architecture Nodes"); - animator.ArchitectureNodesVisible = EditorGUILayout.Toggle(animator.ArchitectureNodesVisible); - GUILayout.Label("Implementation Nodes"); - animator.ImplementationNodesVisible = EditorGUILayout.Toggle(animator.ImplementationNodesVisible); - } - EditorGUILayout.EndHorizontal(); - - if (GUILayout.Button("Update")) - { - animator.UpdateCity(); - } - } - } -} - -#endif \ No newline at end of file diff --git a/Assets/Resources/Materials/MirrorMaterial/Mirror Render Texture.renderTexture b/Assets/Resources/Materials/MirrorMaterial/Mirror Render Texture.renderTexture index 1f8372e532..a213549bee 100644 --- a/Assets/Resources/Materials/MirrorMaterial/Mirror Render Texture.renderTexture +++ b/Assets/Resources/Materials/MirrorMaterial/Mirror Render Texture.renderTexture @@ -26,6 +26,7 @@ RenderTexture: m_UseDynamicScale: 0 m_BindMS: 0 m_EnableCompatibleFormat: 1 + m_EnableRandomWrite: 0 m_TextureSettings: serializedVersion: 2 m_FilterMode: 1 diff --git a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs new file mode 100644 index 0000000000..45197890f9 --- /dev/null +++ b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs @@ -0,0 +1,263 @@ +using System.Collections.Generic; +using SEE.DataModel.DG; +using SEE.Tools.ReflexionAnalysis; +using SEE.GO; +using SEE.Utils; +using UnityEngine; +using System; +using SEE.Game.SceneManipulation; +using SEE.Net.Actions; +using SEE.Audio; +using SEE.Game; + +namespace SEE.Controls.Actions +{ + /// + /// Action to solve a divergence (see ) between + /// implementation and architecture by adding the exact edge to the architecture that solves + /// this divergence. + /// + internal class AcceptDivergenceAction : AbstractPlayerAction + { + /// + /// Returns a new instance of . + /// + /// new instance + public static ReversibleAction CreateReversibleAction() + { + return new AcceptDivergenceAction(); + } + + /// + /// Returns a new instance of as a . + /// + /// new instance + public override ReversibleAction NewInstance() + { + return CreateReversibleAction(); + } + + /// + /// The graph that the edge which was hit by the user to be accepted into the graph is + /// in. Set in . + /// + private ReflexionGraph graph; + + /// + /// The information required to (re-)create the edge that solves the divergence. + /// + private struct Memento + { + /// + /// The source of the edge. + /// + public Node from; + /// + /// The target of the edge. + /// + public Node to; + /// + /// The type of the edge. + /// + public string type; + /// + /// Construct a new memento. + /// + /// the source node of the edge in the architecture graph + /// the target node of the edge in the architecture grpah + public Memento(Node source, Node target, string type) + { + this.from = source; + this.to = target; + this.type = type; + } + } + + /// + /// The information required to (re-)create the edge that solves the divergence. + /// + private Memento memento; + + /// + /// The edge created by this action. Can be null if no edge has been created yet or whether + /// an Undo was called. The created edge is stored only to delete it again if Undo is + /// called. All information to create the edge is kept in . + /// + private Edge createdEdge; + + /// + /// Registers itself at to listen for hovering events. + /// + public override void Start() + { + InteractableObject.LocalAnyHoverIn += LocalAnyHoverIn; + InteractableObject.LocalAnyHoverOut += LocalAnyHoverOut; + } + + /// + /// Unregisters itself from . Does no longer listen for + /// hovering events. + /// + public override void Stop() + { + InteractableObject.LocalAnyHoverIn -= LocalAnyHoverIn; + InteractableObject.LocalAnyHoverOut -= LocalAnyHoverOut; + } + + /// + /// See . + /// + /// true if completed + public override bool Update() + { + // indicates whether the divergence has been solved ("true" means "solved") + bool divergenceSolved = false; + + // FIXME: Needs adaptation for VR where no mouse is available. + if (Input.GetMouseButtonDown(0) + && Raycasting.RaycastGraphElement( + out RaycastHit raycastHit, + out GraphElementRef _) != HitGraphElement.None) + { + // find the edge representing the divergence that should be solved. + GameObject selectedDivergenceEdge = raycastHit.collider.gameObject; + + // check whether the object selected is an edge. + if (selectedDivergenceEdge.TryGetEdge(out Edge selectedEdge)) + { + // check if the selected edge represents a divergence + if (selectedEdge.IsInImplementation() && ReflexionGraph.IsDivergent(selectedEdge)) + { + // acquire the containing ReflexionGraph + graph = (ReflexionGraph)selectedEdge.ItsGraph; + + // find that node in the architecture graph, + // which the divergence's source node is + // explicitly or implicitly mapped to + Node source = graph.MapsTo(selectedEdge.Source); + + // find that node in the ArchitectureGraph, + // which the divergence's target is explicitly + // or implicitly mapped to + Node target = graph.MapsTo(selectedEdge.Target); + + // we have both source and target of the edge and use a memento struct + // to remember which edge we have added + memento = new Memento(source, target, Edge.SourceDependency); + + // create the edge + createdEdge = CreateEdge(memento); + + // check whether edge creation was successfull + divergenceSolved = createdEdge != null; + + // add audio cue to the appearance of the new architecture edge + AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.NEW_EDGE_SOUND); + + // update the current state depending on whether the divergence has been solved + // (required in order to register as an undo-able action) + currentState = divergenceSolved ? ReversibleAction.Progress.Completed : ReversibleAction.Progress.NoEffect; + + // the selected object is synced and this action is done + return true; + } + } + else + { + Debug.LogWarning($"Selected Element {selectedDivergenceEdge.name} is not an edge.\n"); + } + } + return false; + } + + /// + /// Undoes this AcceptDivergenceAction. + /// + public override void Undo() + { + base.Undo(); + + // remove the synced edge (its info is saved in memento) + ReflexionGraph graph = (ReflexionGraph)createdEdge.ItsGraph; + + if (graph != null) + { + // find the corresponding GameObject + GameObject createdEdgeGO = GraphElementIDMap.Find(createdEdge.ID); + // remove the edge's GameObject and graph representation locally and on the network + GameEdgeAdder.Remove(createdEdgeGO); + + // propagate the new edge via network + new DeleteNetAction(createdEdge.ID).Execute(); + + // ensure the edge's GameObject gets destroyed properly + Destroyer.Destroy(createdEdgeGO); + } + else + { + throw new Exception($"Edge {createdEdge.ID} to be removed is not contained in a graph."); + } + + // set any edge references back to null + createdEdge = null; + } + + /// + /// Redoes this AcceptDivergenceAction. + /// + public override void Redo() + { + base.Redo(); + // recreate the edge + createdEdge = CreateEdge(memento); + } + + /// + /// Creates a new edge using the given . In case of any error, null will be + /// returned. + /// + /// information needed to create the edge + /// the new edge's GameObject and a reference to itself, or both null + private Edge CreateEdge(Memento memento) + { + Edge newEdge = AcceptDivergence.Accept(memento.from, memento.to, memento.type); + + // propagate the edge (including matching ID) over network + new AcceptDivergenceNetAction(memento.from.ID, memento.to.ID, newEdge.ID, newEdge.Type).Execute(); + + return newEdge; + } + + /// + /// Returns the of this action. + /// + /// + public override ActionStateType GetActionStateType() + { + return ActionStateTypes.AcceptDivergence; + } + + /// + /// Returns all IDs of GameObjects manipulated by this action. + /// + /// all IDs of GameObjects manipulated by this action + public override HashSet GetChangedObjects() + { + if (createdEdge == null) + { + return new HashSet(); + } + else + { + return new HashSet + { + memento.from.ID, + memento.to.ID, + createdEdge.ID + }; + } + } + } +} diff --git a/Assets/SEE/Game/GameNodeScaler.cs.meta b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs.meta similarity index 83% rename from Assets/SEE/Game/GameNodeScaler.cs.meta rename to Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs.meta index a304f1b36a..c69154aa4a 100644 --- a/Assets/SEE/Game/GameNodeScaler.cs.meta +++ b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a7be02c91dc83d3478cd70695ab975ad +guid: 6c0170e2fab3a75bda98c1062700a9d2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/SEE/Controls/Actions/ActionStateType.cs b/Assets/SEE/Controls/Actions/ActionStateType.cs index 20b5d48180..0cb5a51ccf 100644 --- a/Assets/SEE/Controls/Actions/ActionStateType.cs +++ b/Assets/SEE/Controls/Actions/ActionStateType.cs @@ -1,4 +1,4 @@ -using SEE.Utils; +using SEE.Utils; using UnityEngine; namespace SEE.Controls.Actions @@ -6,12 +6,6 @@ namespace SEE.Controls.Actions /// /// The type of a state-based action. - /// Implemented using the "Enumeration" (not enum) or "type safe enum" pattern. - /// The following two pages have been used for reference: - ///
    - ///
  • https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/enumeration-classes-over-enum-types#implement-an-enumeration-base-class
  • - ///
  • https://ardalis.com/enum-alternatives-in-c/
  • - ///
///
public class ActionStateType : AbstractActionStateType { diff --git a/Assets/SEE/Controls/Actions/ActionStateTypes.cs b/Assets/SEE/Controls/Actions/ActionStateTypes.cs index a1c9149dde..3c7b029180 100644 --- a/Assets/SEE/Controls/Actions/ActionStateTypes.cs +++ b/Assets/SEE/Controls/Actions/ActionStateTypes.cs @@ -111,6 +111,11 @@ static ActionStateTypes() Color.magenta.Darker(), "Materials/ModernUIPack/Pencil", DrawAction.CreateReversibleAction); + AcceptDivergence = + new("Accept Divergence", "Accept a diverging edge into the architecture", + Color.grey.Darker(), "Materials/ModernUIPack/Arrow Bold", + AcceptDivergenceAction.CreateReversibleAction); + // Metric Board actions MetricBoard = new("Metric Board", "Manipulate a metric board", @@ -165,6 +170,7 @@ static ActionStateTypes() parent: MetricBoard); } + public readonly static ActionStateType Move; public readonly static ActionStateType Rotate; public readonly static ActionStateType Hide; @@ -175,6 +181,7 @@ static ActionStateTypes() public readonly static ActionStateType Delete; public readonly static ActionStateType ShowCode; public readonly static ActionStateType Draw; + public readonly static ActionStateType AcceptDivergence; public readonly static ActionStateTypeGroup MetricBoard; public readonly static ActionStateType AddBoard; diff --git a/Assets/SEE/Controls/Actions/AddEdgeAction.cs b/Assets/SEE/Controls/Actions/AddEdgeAction.cs index 4bf2a0bf7b..d13e5d0b22 100644 --- a/Assets/SEE/Controls/Actions/AddEdgeAction.cs +++ b/Assets/SEE/Controls/Actions/AddEdgeAction.cs @@ -7,6 +7,8 @@ using System; using UnityEngine; using SEE.Audio; +using SEE.DataModel.DG; +using SEE.Game.SceneManipulation; namespace SEE.Controls.Actions { @@ -121,7 +123,7 @@ public override void Stop() /// /// The default type of an added edge. /// - private const string DefaultEdgeType = "Source_Dependency"; + private const string DefaultEdgeType = Edge.SourceDependency; /// /// . diff --git a/Assets/SEE/Controls/Actions/AddNodeAction.cs b/Assets/SEE/Controls/Actions/AddNodeAction.cs index 3179a77b5c..389b610b10 100644 --- a/Assets/SEE/Controls/Actions/AddNodeAction.cs +++ b/Assets/SEE/Controls/Actions/AddNodeAction.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; -using SEE.Game; using SEE.GO; using SEE.Net.Actions; using SEE.Utils; using UnityEngine; using SEE.Audio; +using SEE.Game.SceneManipulation; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/DeleteAction.cs b/Assets/SEE/Controls/Actions/DeleteAction.cs index 3c583edd99..c3bc01ae11 100644 --- a/Assets/SEE/Controls/Actions/DeleteAction.cs +++ b/Assets/SEE/Controls/Actions/DeleteAction.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; using System.Linq; -using SEE.Game; using SEE.GO; using SEE.Net.Actions; using SEE.Utils; using UnityEngine; using UnityEngine.Assertions; using SEE.Audio; +using SEE.Game.SceneManipulation; namespace SEE.Controls.Actions { diff --git a/Assets/SEE/Controls/Actions/MoveAction.cs b/Assets/SEE/Controls/Actions/MoveAction.cs index 5097cc1942..eb0a624e71 100644 --- a/Assets/SEE/Controls/Actions/MoveAction.cs +++ b/Assets/SEE/Controls/Actions/MoveAction.cs @@ -4,6 +4,7 @@ using SEE.Game; using SEE.Game.City; using SEE.Game.Operator; +using SEE.Game.SceneManipulation; using SEE.Game.UI.Notification; using SEE.GO; using SEE.Net.Actions; diff --git a/Assets/SEE/Controls/Interactables/InteractableObject.cs b/Assets/SEE/Controls/Interactables/InteractableObject.cs index 4503a4731c..49417b07d8 100644 --- a/Assets/SEE/Controls/Interactables/InteractableObject.cs +++ b/Assets/SEE/Controls/Interactables/InteractableObject.cs @@ -9,7 +9,7 @@ #if INCLUDE_STEAM_VR using Valve.VR.InteractionSystem; #endif -using SEE.Game; +using SEE.Game.SceneManipulation; using SEE.Net.Actions; using SEE.Audio; diff --git a/Assets/SEE/DataModel/DG/Edge.cs b/Assets/SEE/DataModel/DG/Edge.cs index 29b393d3c0..81f16b7362 100644 --- a/Assets/SEE/DataModel/DG/Edge.cs +++ b/Assets/SEE/DataModel/DG/Edge.cs @@ -7,6 +7,28 @@ namespace SEE.DataModel.DG /// public sealed class Edge : GraphElement { + /// + /// The most general edge type for all dependencies extracted from the code. + /// This name must correspond to Axivion's nomenclatura. + /// + public const string SourceDependency = "Source_Dependency"; + + /// + /// Edge type for absences (reflexion analysis). + /// This name must correspond to Axivion's nomenclatura. + /// + public const string Absence = "Absence"; + /// + /// Edge type for convergences (reflexion analysis). + /// This name must correspond to Axivion's nomenclatura. + /// + public const string Convergence = "Convergence"; + /// + /// Edge type for divergences (reflexion analysis). + /// This name must correspond to Axivion's nomenclatura. + /// + public const string Divergence = "Divergence"; + // IMPORTANT NOTES: // // If you use Clone() to create a copy of an edge, be aware that the clone diff --git a/Assets/SEE/Game/ArchitectureInteraction.cs b/Assets/SEE/Game/ArchitectureInteraction.cs deleted file mode 100644 index dac81784d5..0000000000 --- a/Assets/SEE/Game/ArchitectureInteraction.cs +++ /dev/null @@ -1,780 +0,0 @@ -using System.Collections.Generic; -using SEE.DataModel.DG; -using SEE.Game.City; -using SEE.GO; -using UnityEngine; - -namespace SEE.Game -{ - /// - /// This component is intended to provide interactions with a - /// code city representing a reflexion model. One can show/hide nodes - /// and edges in the architecture by user interactions. - /// - /// Its only purpose was to create a video. Do not use this class. - /// - [ExecuteAlways] - public class ArchitectureInteraction : MonoBehaviour - { - /// - /// The code city to be manipulated by this component. - /// - [Tooltip("The code city to be manipulated by this component.")] - public SEECity CodeCity; - - //------------------ - // Edge types - //------------------ - - /// - /// Edge types generated by the reflexion analysis. - /// - public static readonly HashSet ReflexionEdgeTypes = new HashSet() - { - "Absence", - "Convergence", - "Divergence", - }; - - /// - /// Edge types representing implementation dependencies. - /// - public static readonly HashSet ArchitectureEdgeTypes = new HashSet() - { - "Source_Dependency", - }; - - public static readonly HashSet AllEdgeTypes = new HashSet(); - - private HashSet implementationEdgeTypes = new HashSet(); - /// - /// All edge types in the graph that are neither reflexion edges - /// nor architecture dependencies. - /// - public HashSet ImplementationEdgeTypes - { - get - { - if (implementationEdgeTypes.Count == 0) - { - implementationEdgeTypes = GetImplementationEdgeTypes(); - foreach (string type in implementationEdgeTypes) - { - Debug.LogFormat("implementation edge type {0}\n", type); - } - } - return implementationEdgeTypes; - } - } - - /// - /// Returns all edge types in the graph that are neither reflexion edges - /// nor architecture dependencies. - /// - /// all implementation edge types - private HashSet GetImplementationEdgeTypes() - { - HashSet result = new HashSet(); - foreach (GameObject go in GetAllEdges()) - { - string type = GetGraphEdge(go).Type; - if (!ReflexionEdgeTypes.Contains(type) && !ArchitectureEdgeTypes.Contains(type)) - { - result.Add(type); - } - } - return result; - } - - // ----------------------- - // Edge property defaults - // ----------------------- - private static readonly Color implementationEdgesColorDefault = Color.black; - private static readonly Color architectureEdgesColorDefault = Color.blue; - private static readonly float implementationEdgeWidthDefault = 0.001f; - private static readonly float architectureEdgeWidthDefault = 0.005f; - private static readonly float reflexionEdgeWidthDefault = 0.005f; - - /// - /// Resets the settings to their defaults. Called in editor mode when the - /// user resets the component. - /// - private void Reset() - { - implementationEdgesVisible = false; - implementationEdgesStartColor = Lighter(implementationEdgesColorDefault); - implementationEdgesEndColor = implementationEdgesColorDefault; - implementationEdgesWidth = implementationEdgeWidthDefault; - - architectureEdgesVisible = false; - architectureEdgesStartColor = Lighter(architectureEdgesColorDefault); - architectureEdgesEndColor = architectureEdgesColorDefault; - architectureEdgesWidth = architectureEdgeWidthDefault; - - reflexionEdgesVisible = false; - reflexionEdgesWidth = reflexionEdgeWidthDefault; - - architectureNodesVisible = true; - implementationNodesVisible = false; - } - - /// - /// Assigns . - /// - private void Awake() - { - if (!gameObject.TryGetComponent(out CodeCity)) - { - Debug.LogError($"Game object {name} does not have a SEECity component.\n"); - enabled = false; - } - else - { - UpdateCity(); - } - } - - // -------------------------------------------- - // Update - // -------------------------------------------- - - private void Update() - { - if (Input.GetKeyDown(KeyCode.F5)) - { - ImplementationEdgesVisible = !ImplementationEdgesVisible; - Debug.Log("F5 pressed.\n"); - } - if (Input.GetKeyDown(KeyCode.F6)) - { - ArchitectureEdgesVisible = !ArchitectureEdgesVisible; - } - if (Input.GetKeyDown(KeyCode.F7)) - { - ReflexionEdgesVisible = !ReflexionEdgesVisible; - } - if (Input.GetKeyDown(KeyCode.F11)) - { - ArchitectureNodesVisible = !ArchitectureNodesVisible; - } - if (Input.GetKeyDown(KeyCode.F12)) - { - ImplementationNodesVisible = !ImplementationNodesVisible; - } - } - - /// - /// Sets the edge references of all edges in - /// and colors the nodes. - /// - public void UpdateCity() - { - if (CodeCity != null) - { - CodeCity.SetNodeEdgeRefs(); - GameObject root = GetCityRootNode(); - if (root != null) - { - Debug.LogFormat("Root of {0} is {1}\n", CodeCity.name, root.name); - SetAllNodes(root); - SetAllEdges(); - } - } - else - { - Debug.LogErrorFormat("Code city is null in {0}.\n", name); - } - } - - /// - /// Sets all attributes of all nodes. - /// - /// root node of the code city - private void SetAllNodes(GameObject root) - { - ColorNodes(root); - SetNodeVisibility(ArchitectureNodes(), architectureNodesVisible); - SetNodeVisibility(ImplementationNodes(), implementationNodesVisible); - } - - /// - /// Sets all attributes of all edges. - /// - private void SetAllEdges() - { - SetArchitectureEdges(); - SetImplementationEdges(); - SetReflexionEdges(); - } - - /// - /// Returns the root node of . - /// - /// root node of - private GameObject GetCityRootNode() - { - return SceneQueries.GetCityRootNode(CodeCity.gameObject); - } - - /// - /// Colors architecture node with increasingly darker colors starting from white - /// and implementation nodes with increasingly darker colors starting from yellow. - /// The actual color used gets darker from nesting level to nesting level. - /// Implementation leaf nodes keep their original color, however. - /// - /// the root of the node tree to be colored - private void ColorNodes(GameObject root) - { - ColorNodes(root, architectureNode: false, Color.yellow); - ColorNodes(root, architectureNode: true, Color.white); - } - - /// - /// Colors all nodes in the node tree rooted by using - /// the given if and only if: - /// (1) if is true, the nodes are architecture nodes - /// or - /// (2) if is false, the nodes are implementation nodes. - /// The color is increasingly darkened from level to level, that is, the deeper a node - /// in the node tree, the darker its color originating from the initial given . - /// - /// Note: The color of implementation leaf nodes is never changed. - /// - /// root of the node tree to be colored - /// whether architecture nodes should be colored - /// the new color of the nodes - private void ColorNodes(GameObject parent, bool architectureNode, Color color) - { - // Is root a node at all? It may as well be an edge, for instance. - if (parent.CompareTag(Tags.Node)) - { - if (parent.TryGetComponent(out NodeRef nodeRef) && nodeRef.Value != null) - { - bool isArchitectureNode = IsArchitectureNode(parent); - Color childColor = color; - - if (!isArchitectureNode && GetGraphNode(parent).IsLeaf()) - { - // We are dealing with an implementation node that is a leaf. - // We will not change the color of implementation nodes that are leaves; - // neither do we need to traverse any children because there are none. - } - else - { - if ((architectureNode && isArchitectureNode) || (!architectureNode && !isArchitectureNode)) - { - parent.SetColor(color); - childColor = Darker(color, 0.35f); - } - foreach (Transform child in parent.transform) - { - ColorNodes(child.gameObject, architectureNode, childColor); - } - } - } - else - { - Debug.LogErrorFormat("Game object {0} has no valid node reference.\n", parent.name); - } - } - } - - //------------------- - // Edges in CodeCity - //------------------- - - /// - /// Returns all edges in the subtree rooted by . - /// - /// all edges in the subtree rooted by - private List GetAllEdges() - { - List edges = new List(); - GameObject root = GetCityRootNode(); - AddAllEdges(root, edges); - return edges; - } - - /// - /// Adds all edges in the subtree rooted by to . - /// - /// root of the subtree to be traversed - /// where to add the found edges - private void AddAllEdges(GameObject root, List edges) - { - foreach (Transform child in root.transform) - { - GameObject childGO = child.gameObject; - if (childGO.CompareTag(Tags.Edge)) - { - if (childGO.TryGetComponent(out EdgeRef edgeRef) && edgeRef.Value != null) - { - edges.Add(childGO); - } - else - { - Debug.LogErrorFormat("Edge {0} has no valid edge reference.\n", childGO.name); - } - } - else if (childGO.CompareTag(Tags.Node)) - { - AddAllEdges(childGO, edges); - } - } - } - - /// - /// Returns the graph edge represented by if there is any. - /// Returns null if does not have a valid graph edge. - /// - /// game object representing an edge - /// graph edge represented or null - private static Edge GetGraphEdge(GameObject gameEdge) - { - if (gameEdge.TryGetComponent(out EdgeRef edgeRef)) - { - return edgeRef.Value; - } - else - { - Debug.LogError($"Edge {gameEdge.name} has no valid edge reference.\n"); - return null; - } - } - - //------------------ - // Nodes in CodeCity - //------------------ - - /// - /// Returns all nodes in the subtree rooted by . - /// - /// all nodes in the subtree rooted by - private List GetAllNodes() - { - GameObject root = GetCityRootNode(); - List nodes = new List() { root }; - AddAllNodes(root, nodes); - return nodes; - } - - /// - /// Adds all nodes in the subtree rooted by to . - /// - /// root of the subtree to be traversed - /// where to add the found nodes - private void AddAllNodes(GameObject root, List nodes) - { - foreach (Transform child in root.transform) - { - GameObject childGO = child.gameObject; - if (childGO.CompareTag(Tags.Node)) - { - if (childGO.TryGetComponent(out NodeRef nodeRef) && nodeRef.Value != null) - { - nodes.Add(childGO); - } - else - { - Debug.LogError($"Node {childGO.name} has no valid node reference.\n"); - } - } - AddAllNodes(childGO, nodes); - } - } - - /// - /// Returns the graph node represented by if there is any. - /// Returns null if does not have a valid graph node. - /// - /// game object representing a node - /// graph node represented or null - private static Node GetGraphNode(GameObject gameNode) - { - if (gameNode.TryGetComponent(out NodeRef nodeRef)) - { - return nodeRef.Value; - } - else - { - Debug.LogErrorFormat("Node {0} has no valid node reference.\n", gameNode.name); - return null; - } - } - - //------------------------------------ - // Implementation edge type properties - //------------------------------------ - - private bool implementationEdgesVisible = false; - /// - /// Whether implementation edges are visible. - /// - public bool ImplementationEdgesVisible - { - get => implementationEdgesVisible; - set - { - if (implementationEdgesVisible != value) - { - implementationEdgesVisible = value; - SetEdgeVisiblity(ImplementationEdgeTypes, implementationEdgesVisible); - } - } - } - - private Color implementationEdgesStartColor = Lighter(implementationEdgesColorDefault); - /// - /// Start color of implementation dependencies. - /// - public Color ImplementationEdgesStartColor - { - get => implementationEdgesStartColor; - set - { - if (implementationEdgesStartColor != value) - { - implementationEdgesStartColor = value; - SetImplementationEdges(); - } - } - } - - private Color implementationEdgesEndColor = implementationEdgesColorDefault; - /// - /// End color of implementation dependencies. - /// - public Color ImplementationEdgesEndColor - { - get => implementationEdgesEndColor; - set - { - if (implementationEdgesEndColor != value) - { - implementationEdgesEndColor = value; - SetImplementationEdges(); - } - } - } - - private float implementationEdgesWidth = implementationEdgeWidthDefault; - /// - /// The width of implementation dependencies. - /// - public float ImplementationEdgesWidth - { - get => implementationEdgesWidth; - set - { - if (implementationEdgesWidth != value) - { - implementationEdgesWidth = value; - SetImplementationEdges(); - } - } - } - - /// - /// Sets all attributes of implementation edges. - /// - private void SetImplementationEdges() - { - SetLine(ImplementationEdgeTypes, implementationEdgesStartColor, implementationEdgesEndColor, implementationEdgesWidth); - SetEdgeVisiblity(ImplementationEdgeTypes, implementationEdgesVisible); - } - - //------------------------------------ - // Reflexion edge type properties - //------------------------------------ - - private bool reflexionEdgesVisible = false; - /// - /// Whether reflexion edges are visible. - /// - public bool ReflexionEdgesVisible - { - get => reflexionEdgesVisible; - set - { - if (reflexionEdgesVisible != value) - { - reflexionEdgesVisible = value; - SetEdgeVisiblity(ReflexionEdgeTypes, reflexionEdgesVisible); - } - } - } - - private float reflexionEdgesWidth = reflexionEdgeWidthDefault; - /// - /// The width of reflexion edges. - /// - public float ReflexionEdgesWidth - { - get => reflexionEdgesWidth; - set - { - if (reflexionEdgesWidth != value) - { - reflexionEdgesWidth = value; - SetReflexionEdges(); - } - } - } - - /// - /// Sets start and end color and width for the lines of all reflexion edges. - /// Absences will be colored yellow, convergences will be colored green, and - /// divergences will be colored red. Sets the visibility of the reflexion edges. - /// - private void SetReflexionEdges() - { - SetLine(new HashSet() { "Absence" }, Color.yellow, Darker(Color.yellow), reflexionEdgesWidth); - SetLine(new HashSet() { "Convergence" }, Color.green, Darker(Color.green), reflexionEdgesWidth); - SetLine(new HashSet() { "Divergence" }, Color.red, Darker(Color.red), reflexionEdgesWidth); - SetEdgeVisiblity(ReflexionEdgeTypes, reflexionEdgesVisible); - } - - //------------------------------------ - // Architecture edge type properties - //------------------------------------ - - private bool architectureEdgesVisible = false; - /// - /// Whether architecture dependencies are visible. - /// - public bool ArchitectureEdgesVisible - { - get => architectureEdgesVisible; - set - { - if (architectureEdgesVisible != value) - { - architectureEdgesVisible = value; - SetEdgeVisiblity(ArchitectureEdgeTypes, architectureEdgesVisible); - } - } - } - - private Color architectureEdgesStartColor = Lighter(Color.blue); - /// - /// Start color of architecture dependencies. - /// - public Color ArchitectureEdgesStartColor - { - get => architectureEdgesStartColor; - set - { - if (architectureEdgesStartColor != value) - { - architectureEdgesStartColor = value; - SetArchitectureEdges(); - } - } - } - - private Color architectureEdgesEndColor = Color.blue; - /// - /// End color of architecture dependencies. - /// - public Color ArchitectureEdgesEndColor - { - get => architectureEdgesEndColor; - set - { - if (architectureEdgesEndColor != value) - { - architectureEdgesEndColor = value; - SetArchitectureEdges(); - } - } - } - - private float architectureEdgesWidth = architectureEdgeWidthDefault; - /// - /// The width of architecture dependencies. - /// - public float ArchitectureEdgesWidth - { - get => architectureEdgesWidth; - set - { - if (architectureEdgesWidth != value) - { - architectureEdgesWidth = value; - SetArchitectureEdges(); - } - } - } - - /// - /// Sets all attributes of all architecture edges. - /// - private void SetArchitectureEdges() - { - SetLine(ArchitectureEdgeTypes, architectureEdgesStartColor, architectureEdgesEndColor, architectureEdgesWidth); - SetEdgeVisiblity(ArchitectureEdgeTypes, architectureEdgesVisible); - } - - /// - /// Set the visibility of all edges in the that have - /// any of the given to . - /// - /// relevant edge types for setting the visibility - /// new visibility - private void SetEdgeVisiblity(HashSet edgeTypes, bool show) - { - foreach (GameObject go in GetAllEdges()) - { - if (edgeTypes == AllEdgeTypes || edgeTypes.Contains(GetGraphEdge(go).Type)) - { - go.SetVisibility(show); - } - } - } - - /// - /// For every edge in having a type included in the given - /// , the given line attributes will be set. - /// - /// relevant edge types - /// starting color of the line - /// ending color of the line - /// width of the line - private void SetLine(HashSet edgeTypes, Color startColor, Color endColor, float width) - { - foreach (GameObject go in GetAllEdges()) - { - if (edgeTypes == AllEdgeTypes || edgeTypes.Contains(GetGraphEdge(go).Type)) - { - LineRenderer renderer = go.GetComponent(); - if (renderer != null) - { - renderer.startColor = startColor; - renderer.endColor = endColor; - renderer.startWidth = width; - renderer.endWidth = width; - } - } - } - } - - //------------------------------------ - // Architecture node type properties - //------------------------------------ - - private bool architectureNodesVisible = true; - /// - /// Whether architecture nodes are visible. - /// - public bool ArchitectureNodesVisible - { - get => architectureNodesVisible; - set - { - if (architectureNodesVisible != value) - { - architectureNodesVisible = value; - SetNodeVisibility(ArchitectureNodes(), architectureNodesVisible); - } - } - } - - private bool implementationNodesVisible = false; - /// - /// Whether implementation nodes are visible. - /// - public bool ImplementationNodesVisible - { - get => implementationNodesVisible; - set - { - if (implementationNodesVisible != value) - { - implementationNodesVisible = value; - SetNodeVisibility(ImplementationNodes(), implementationNodesVisible); - } - } - } - - /// - /// Returns all architecture nodes in . - /// - /// all architecture nodes - private List ArchitectureNodes() - { - List result = new List(); - foreach (GameObject go in GetAllNodes()) - { - if (IsArchitectureNode(go)) - { - result.Add(go); - } - } - return result; - } - - /// - /// Returns true if is an architecture node (has - /// type Cluster). - /// - /// game object representing a node - /// true if architecture node - private static bool IsArchitectureNode(GameObject go) - { - return GetGraphNode(go).Type == "Cluster"; - } - - /// - /// Returns all implementation nodes in the . - /// - /// all implementation nodes - private List ImplementationNodes() - { - List result = new List(); - foreach (GameObject go in GetAllNodes()) - { - if (!IsArchitectureNode(go)) - { - result.Add(go); - } - } - return result; - } - - /// - /// Sets the visibility of all to . - /// - /// game objects whose visibility is to be set - /// the new visibility - private static void SetNodeVisibility(ICollection nodes, bool show) - { - foreach (GameObject go in nodes) - { - go.SetVisibility(show, includingChildren: false); - } - } - - /// - /// Returns given lightened by 50%. - /// - /// base color to be lightened - /// given lightened by 50% - private static Color Lighter(Color color) - { - return Color.Lerp(color, Color.white, 0.5f); // To lighten by 50 % - } - - /// - /// Returns given darkened by . - /// - /// Precondition: 0 <= <= 1 - /// - /// base color to be darkened - /// degree by which to darker the given - /// given darkened by - private static Color Darker(Color color, float degree = 0.5f) - { - return Color.Lerp(color, Color.black, degree); - } - } -} \ No newline at end of file diff --git a/Assets/SEE/Game/EdgeRenderer.cs b/Assets/SEE/Game/EdgeRenderer.cs index 44f41e69b6..9eb8dcfe2f 100644 --- a/Assets/SEE/Game/EdgeRenderer.cs +++ b/Assets/SEE/Game/EdgeRenderer.cs @@ -155,7 +155,7 @@ private GameObject DrawEdge(Edge edge, GameObject from, GameObject to, bool addT Assert.IsNotNull(toLayoutNode, $"Target node {edge.Target.ID} does not have a layout node.\n"); // The single layout edge between source and target. We want the layout only for this edge. ICollection> layoutEdges = new List> - { new LayoutGraphEdge(fromLayoutNode, toLayoutNode, edge) }; + { new(fromLayoutNode, toLayoutNode, edge) }; // Calculate the edge layout (for the single edge only). @@ -216,6 +216,35 @@ public GameObject DrawEdge(GameObject source, GameObject target, string edgeType return DrawEdge(edge, source, target, true); } + /// + /// Draws and returns a new game edge + /// based on the current settings. + /// + /// Note: The default edge layout will be used if no edge layout, + /// i.e., , was chosen in the settings. + /// + /// Precondition: and must have a valid + /// node reference. The corresponding graph nodes must be in the same graph. + /// + /// the edge to be drawn + /// GameObject of source of the new edge + /// GameObject of target of the new edge + /// The new game object representing the given edge. + public GameObject DrawEdge(Edge edge, GameObject sourceNode = null, GameObject targetNode = null) + { + if (sourceNode == null) + { + sourceNode = GraphElementIDMap.Find(edge.Source.ID); + } + + if (targetNode == null) + { + targetNode = GraphElementIDMap.Find(edge.Target.ID); + } + + return DrawEdge(edge, sourceNode, targetNode, true); + } + /// /// Adds and all its transitive parent game objects tagged by /// to . @@ -393,4 +422,4 @@ private static ICollection> ConnectingEdges(ICollection return edges; } } -} \ No newline at end of file +} diff --git a/Assets/SEE/Game/Evolution/EvolutionRenderer.cs b/Assets/SEE/Game/Evolution/EvolutionRenderer.cs index df73a6f44b..2483d4c4bb 100644 --- a/Assets/SEE/Game/Evolution/EvolutionRenderer.cs +++ b/Assets/SEE/Game/Evolution/EvolutionRenderer.cs @@ -932,6 +932,16 @@ public GameObject DrawNode(Node node, GameObject city = null) return Renderer.DrawNode(node, city); } + /// + /// Placeholder to satisfy the compiler. This method is not + /// called anywhere as of yet, but was required in . + /// + public GameObject DrawEdge(Edge edge, GameObject source = null, GameObject target = null) + { + throw new NotImplementedException(); + } + /// /// Returns an edge layout for the given . /// diff --git a/Assets/SEE/Game/GameNodeScaler.cs b/Assets/SEE/Game/GameNodeScaler.cs deleted file mode 100644 index 6fe3e02124..0000000000 --- a/Assets/SEE/Game/GameNodeScaler.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Collections.Generic; -using UnityEditor; -using UnityEngine; - -namespace SEE.Game -{ - /// - /// This GameNodeScaler allows us to scale a game node without modifying the - /// scale of its children. - /// - [ExecuteInEditMode] - public class GameNodeScaler : MonoBehaviour - { - /// - /// The target scale of the game object in world space co-ordinates. - /// - public Vector3 TargetScale; - - /// - /// Sets to the current scale of the game object. - /// - void Start() - { - Init(); - } - - /// - /// Sets to the current scale of the game object. - /// Called when the component is reset in the editor. - /// - void Reset() - { - Init(); - } - - private void Init() - { - TargetScale = transform.lossyScale; - enabled = false; - } - - /// - /// If has changed, the scale of the game object is set - /// to (in world space). The children of the game object - /// are not re-sized. - /// - void Update() - { - if (TargetScale != transform.lossyScale) - { - Debug.LogFormat("{0} original world scale = {1}; target world scale: {2}\n", - name, transform.lossyScale.ToString("F4"), TargetScale.ToString("F4")); - ScaleOnlyRootGameObject(gameObject, TargetScale); - -#if UNITY_EDITOR - EditorUtility.SetDirty(gameObject); - SceneView.RepaintAll(); -#endif - TargetScale = transform.lossyScale; - } - } - - /// - /// Resizes given to given - /// in world space without resizing any of its children. - /// - /// game object to be resized - /// the target scale in world-space co-ordinates - private static void ScaleOnlyRootGameObject(GameObject gameObject, Vector3 targetScale) - { - // The children of gameObject: - List children = new List(); - // Save the children of gameObject. - foreach (Transform child in gameObject.transform) - { - children.Add(child); - } - // Unparent all children of gameObject so that they do not scale along with gameObject. - foreach (Transform child in children) - { - child.parent = null; - } - { - // Resize gameObject and only gameObject in world space. - // The parent of gameObject (may be null): - Transform parent = gameObject.transform.parent; - // Unparent gameObject because targetScale is meant to be in world space. - gameObject.transform.parent = null; - // Resize gameObject. - gameObject.transform.localScale = targetScale; - // Re-parent gameObject. - gameObject.transform.parent = parent; - } - // Re-parent all children of gameObject. - foreach (Transform child in children) - { - child.parent = gameObject.transform; - } - } - } -} \ No newline at end of file diff --git a/Assets/SEE/Game/IGraphRenderer.cs b/Assets/SEE/Game/IGraphRenderer.cs index 5506c8d97f..9b3efe7b0a 100644 --- a/Assets/SEE/Game/IGraphRenderer.cs +++ b/Assets/SEE/Game/IGraphRenderer.cs @@ -34,6 +34,22 @@ public interface IGraphRenderer /// are not contained in any graph or contained in different graphs GameObject DrawEdge(GameObject source, GameObject target, string edgeType); + /// + /// Draws and returns a new game edge + /// based on the current settings. + /// + /// Note: The default edge layout will be used if no edge layout, + /// i.e., , was chosen in the settings. + /// + /// Precondition: and must either have a valid + /// node reference or be null. The corresponding graph nodes must be in the same graph. + /// + /// the edge to be drawn + /// GameObject of source of the new edge + /// GameObject of target of the new edge + /// The new game object representing the given edge. + GameObject DrawEdge(Edge edge, GameObject source = null, GameObject target = null); + /// /// Returns an edge layout for the given . /// @@ -61,4 +77,4 @@ public interface IGraphRenderer /// game object representing given GameObject DrawNode(Node node, GameObject city = null); } -} \ No newline at end of file +} diff --git a/Assets/SEE/Game/InteractionDecorator.cs b/Assets/SEE/Game/InteractionDecorator.cs index 67419b67c6..2ee3d90228 100644 --- a/Assets/SEE/Game/InteractionDecorator.cs +++ b/Assets/SEE/Game/InteractionDecorator.cs @@ -40,7 +40,6 @@ public static void PrepareForInteraction(GameObject gameObject) gameObject.AddComponentIfNecessary(); if (gameObject.HasNodeRef()) { - gameObject.AddComponentIfNecessary(); gameObject.AddComponentIfNecessary(); gameObject.AddComponentIfNecessary(); gameObject.AddComponentIfNecessary(); diff --git a/Assets/SEE/Game/SceneManipulation.meta b/Assets/SEE/Game/SceneManipulation.meta new file mode 100644 index 0000000000..29ac170949 --- /dev/null +++ b/Assets/SEE/Game/SceneManipulation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2f2874a20ebd0a5428e97d6c6f3494d8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SEE/Game/SceneManipulation/AcceptDivergence.cs b/Assets/SEE/Game/SceneManipulation/AcceptDivergence.cs new file mode 100644 index 0000000000..34aec28767 --- /dev/null +++ b/Assets/SEE/Game/SceneManipulation/AcceptDivergence.cs @@ -0,0 +1,83 @@ +using SEE.DataModel.DG; +using SEE.GO; +using SEE.Tools.ReflexionAnalysis; +using UnityEngine; + +namespace SEE.Game.SceneManipulation +{ + /// + /// Adds an edge to a reflexion graph allowing a currently divergent implementation + /// dependency. + /// + public static class AcceptDivergence + { + /// + /// Adds a new edge of given from + /// to to the graph is contained in + /// via . Then the edge is + /// drawn. + /// + /// Preconditions: and both have + /// valid s that are in the same graph and this graph is a + /// . Given must be a unique + /// edge ID. + /// + /// the game object holding the source of the edge + /// the game object holding the target of the edge + /// the type of the edge + /// the unique ID the edge should have + /// the edge that was created and added to the graph + public static Edge Accept(GameObject from, GameObject to, string edgeType, string edgeId) + { + if (from.TryGetNode(out Node fromNode)) + { + if (to.TryGetNode(out Node toNode)) + { + return Accept(fromNode, toNode, edgeType, edgeId); + } + else + { + throw new System.Exception($"Game node {toNode.ID} has not graph node attached.\n"); + } + } + else + { + throw new System.Exception($"Game node {fromNode.ID} has not graph node attached.\n"); + } + } + + /// + /// Adds a new edge of given from + /// to to the graph is contained in + /// via . Then the edge is + /// drawn. + /// + /// Preconditions: and are in the + /// same graph and this graph is a . If + /// is given, it must be a unique edge ID. + /// + /// the source of the edge + /// the target of the edge + /// the type of the edge + /// the unique ID the edge should have; if null or empty + /// a unique ID will be created + /// the edge that was created and added to the graph + public static Edge Accept(Node from, Node to, string edgeType, string edgeId = null) + { + Edge result = new(from, to, edgeType); + + if (!string.IsNullOrEmpty(edgeId)) + { + result.ID = edgeId; + } + + if (from.ItsGraph is ReflexionGraph graph) + { + graph.AddToArchitecture(result); + } + + GameEdgeAdder.Draw(result); + return result; + } + } +} \ No newline at end of file diff --git a/Assets/Editor/ArchitectureInteractionEditor.cs.meta b/Assets/SEE/Game/SceneManipulation/AcceptDivergence.cs.meta similarity index 83% rename from Assets/Editor/ArchitectureInteractionEditor.cs.meta rename to Assets/SEE/Game/SceneManipulation/AcceptDivergence.cs.meta index 4119343cf8..e442d446cf 100644 --- a/Assets/Editor/ArchitectureInteractionEditor.cs.meta +++ b/Assets/SEE/Game/SceneManipulation/AcceptDivergence.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3ef6249495394044fbb78b81be55017b +guid: e48734750aff8b2438f688d5e62c11c8 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/SEE/Game/GameEdgeAdder.cs b/Assets/SEE/Game/SceneManipulation/GameEdgeAdder.cs similarity index 79% rename from Assets/SEE/Game/GameEdgeAdder.cs rename to Assets/SEE/Game/SceneManipulation/GameEdgeAdder.cs index 2b75968745..06614980ff 100644 --- a/Assets/SEE/Game/GameEdgeAdder.cs +++ b/Assets/SEE/Game/SceneManipulation/GameEdgeAdder.cs @@ -4,10 +4,10 @@ using SEE.GO; using UnityEngine; -namespace SEE.Game +namespace SEE.Game.SceneManipulation { /// - /// Creates new game objects representing graph edges or deleting these again, + /// Creates new game objects representing graph edges or deletes these again, /// respectively. /// public class GameEdgeAdder @@ -54,6 +54,29 @@ public static GameObject Add(GameObject source, GameObject target, string edgeTy return result; } + /// + /// Simply (re)draws an edge. + /// + /// the edge that should be (re)drawn + /// the GameObject of the drawn edge + public static GameObject Draw(Edge edge) + { + GameObject source = GraphElementIDMap.Find(edge.Source.ID); + Transform cityObject = SceneQueries.GetCodeCity(source.transform); + + if (!cityObject) + { + throw new Exception($"The code city for the new edge {edge.ToShortString()} cannot be determined.\n"); + } + + if (!cityObject.TryGetComponent(out AbstractSEECity city)) + { + throw new Exception($"Could not determine the code city for the new edge {edge.ToShortString()}.\n"); + } + + return city.Renderer.DrawEdge(edge, source: source); + } + /// /// Inverse operation of . /// Removes the given from the scene and its associated diff --git a/Assets/SEE/Game/GameEdgeAdder.cs.meta b/Assets/SEE/Game/SceneManipulation/GameEdgeAdder.cs.meta similarity index 100% rename from Assets/SEE/Game/GameEdgeAdder.cs.meta rename to Assets/SEE/Game/SceneManipulation/GameEdgeAdder.cs.meta diff --git a/Assets/SEE/Game/GameElementDeleter.cs b/Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs similarity index 99% rename from Assets/SEE/Game/GameElementDeleter.cs rename to Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs index c44bcad5ff..5f7aa824c7 100644 --- a/Assets/SEE/Game/GameElementDeleter.cs +++ b/Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs @@ -6,7 +6,7 @@ using SEE.Tools.ReflexionAnalysis; using UnityEngine; -namespace SEE.Game +namespace SEE.Game.SceneManipulation { /// /// Allows to delete nodes and edges. @@ -106,7 +106,7 @@ private static (GraphElementsMemento, ISet) DeleteEdge(GameObject ga { // The edge memento must be created before the edge is removed; // otherwise ItsGraph would be null. - EdgeMemento edgeMemento = new EdgeMemento(edgeRef.Value); + EdgeMemento edgeMemento = new(edgeRef.Value); edgeRef.Value.ItsGraph.RemoveEdge(edgeRef.Value); GameObjectFader.FadingOut(gameEdge, SetInactive); return (edgeMemento, new HashSet() { gameEdge }); diff --git a/Assets/SEE/Game/GameElementDeleter.cs.meta b/Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs.meta similarity index 100% rename from Assets/SEE/Game/GameElementDeleter.cs.meta rename to Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs.meta diff --git a/Assets/SEE/Game/GameNodeAdder.cs b/Assets/SEE/Game/SceneManipulation/GameNodeAdder.cs similarity index 99% rename from Assets/SEE/Game/GameNodeAdder.cs rename to Assets/SEE/Game/SceneManipulation/GameNodeAdder.cs index 9dd226312f..d4d771aa4b 100644 --- a/Assets/SEE/Game/GameNodeAdder.cs +++ b/Assets/SEE/Game/SceneManipulation/GameNodeAdder.cs @@ -4,7 +4,7 @@ using SEE.GO; using UnityEngine; -namespace SEE.Game +namespace SEE.Game.SceneManipulation { /// /// Creates new game objects representing graph nodes or deleting these again, diff --git a/Assets/SEE/Game/GameNodeAdder.cs.meta b/Assets/SEE/Game/SceneManipulation/GameNodeAdder.cs.meta similarity index 100% rename from Assets/SEE/Game/GameNodeAdder.cs.meta rename to Assets/SEE/Game/SceneManipulation/GameNodeAdder.cs.meta diff --git a/Assets/SEE/Game/GameNodeMover.cs b/Assets/SEE/Game/SceneManipulation/GameNodeMover.cs similarity index 99% rename from Assets/SEE/Game/GameNodeMover.cs rename to Assets/SEE/Game/SceneManipulation/GameNodeMover.cs index 55cc9b706a..51a7134225 100644 --- a/Assets/SEE/Game/GameNodeMover.cs +++ b/Assets/SEE/Game/SceneManipulation/GameNodeMover.cs @@ -3,7 +3,7 @@ using UnityEngine; using UnityEngine.Assertions; -namespace SEE.Game +namespace SEE.Game.SceneManipulation { /// /// Allows to move game nodes (game objects representing a graph node). diff --git a/Assets/SEE/Game/GameNodeMover.cs.meta b/Assets/SEE/Game/SceneManipulation/GameNodeMover.cs.meta similarity index 100% rename from Assets/SEE/Game/GameNodeMover.cs.meta rename to Assets/SEE/Game/SceneManipulation/GameNodeMover.cs.meta diff --git a/Assets/SEE/Game/GameObjectFader.cs b/Assets/SEE/Game/SceneManipulation/GameObjectFader.cs similarity index 99% rename from Assets/SEE/Game/GameObjectFader.cs rename to Assets/SEE/Game/SceneManipulation/GameObjectFader.cs index 5645babf91..2788aa598f 100644 --- a/Assets/SEE/Game/GameObjectFader.cs +++ b/Assets/SEE/Game/SceneManipulation/GameObjectFader.cs @@ -5,7 +5,7 @@ using SEE.Utils; using UnityEngine; -namespace SEE.Game +namespace SEE.Game.SceneManipulation { /// /// Provides fading in and out for game objects. There are static as well as instance methods diff --git a/Assets/SEE/Game/GameObjectFader.cs.meta b/Assets/SEE/Game/SceneManipulation/GameObjectFader.cs.meta similarity index 100% rename from Assets/SEE/Game/GameObjectFader.cs.meta rename to Assets/SEE/Game/SceneManipulation/GameObjectFader.cs.meta diff --git a/Assets/SEE/Game/GameObjectFlasher.cs b/Assets/SEE/Game/SceneManipulation/GameObjectFlasher.cs similarity index 98% rename from Assets/SEE/Game/GameObjectFlasher.cs rename to Assets/SEE/Game/SceneManipulation/GameObjectFlasher.cs index ad7615121b..ca53ce9c17 100644 --- a/Assets/SEE/Game/GameObjectFlasher.cs +++ b/Assets/SEE/Game/SceneManipulation/GameObjectFlasher.cs @@ -2,7 +2,7 @@ using DG.Tweening; using SEE.Utils; -namespace SEE.Game +namespace SEE.Game.SceneManipulation { /// /// Flashes a game object, that is, animates its color pulsating from its diff --git a/Assets/SEE/Game/GameObjectFlasher.cs.meta b/Assets/SEE/Game/SceneManipulation/GameObjectFlasher.cs.meta similarity index 100% rename from Assets/SEE/Game/GameObjectFlasher.cs.meta rename to Assets/SEE/Game/SceneManipulation/GameObjectFlasher.cs.meta diff --git a/Assets/SEE/Game/SceneManipulation/SceneManipulation.cs b/Assets/SEE/Game/SceneManipulation/SceneManipulation.cs new file mode 100644 index 0000000000..7442f512f1 --- /dev/null +++ b/Assets/SEE/Game/SceneManipulation/SceneManipulation.cs @@ -0,0 +1,10 @@ +/// +/// This namespace contains (typically static) classes that manipulate +/// game objects in the scene representing nodes or edges. They will +/// be shared among our Actions and their corresponding NetActions. +/// The intention here is to avoid redundancy among Actions and +/// NetActions as both need to change the scene the same way. +/// +namespace SEE.Game.SceneManipulation +{ +} \ No newline at end of file diff --git a/Assets/SEE/Game/ArchitectureInteraction.cs.meta b/Assets/SEE/Game/SceneManipulation/SceneManipulation.cs.meta similarity index 83% rename from Assets/SEE/Game/ArchitectureInteraction.cs.meta rename to Assets/SEE/Game/SceneManipulation/SceneManipulation.cs.meta index 02d74be976..1df626b538 100644 --- a/Assets/SEE/Game/ArchitectureInteraction.cs.meta +++ b/Assets/SEE/Game/SceneManipulation/SceneManipulation.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4f25e472fae0c304e994d615ed8259db +guid: 2d32acb5594a0914b9461bd09533736f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/SEE/Net/Actions/AcceptDivergenceNetAction.cs b/Assets/SEE/Net/Actions/AcceptDivergenceNetAction.cs new file mode 100644 index 0000000000..e4e26df2dd --- /dev/null +++ b/Assets/SEE/Net/Actions/AcceptDivergenceNetAction.cs @@ -0,0 +1,77 @@ +using SEE.DataModel.DG; +using UnityEngine; +using SEE.GO; +using SEE.Tools.ReflexionAnalysis; +using SEE.Game.SceneManipulation; + +namespace SEE.Net.Actions +{ + /// + /// This class is responsible for adding a specific edge via + /// network from one client to all others and to the server, in + /// order to solve a divergence. + /// + public class AcceptDivergenceNetAction : AbstractNetAction + { + /// + /// The ID of the Node's GameObject from which the edge should be + /// drawn (source node). + /// + public string FromId; + + /// + /// The ID of the Node's GameObject to which the edge should be drawn + /// (target node). + /// + public string ToId; + + /// + /// The ID of the server's edge to ensure they match + /// between server and clients. + /// + public string EdgeId; + + /// + /// The type of the edge that should be created in order to + /// solve the divergence. + /// + public string Type; + + /// + /// Constructor. + /// + /// ID of the source node of the edge + /// ID for target node of the edge + /// ID of the edge to be propagated to the clients + /// the type of the created edge + public AcceptDivergenceNetAction(string fromId, string toId, string edgeId, string type) + : base() + { + FromId = fromId; + ToId = toId; + EdgeId = edgeId; + Type = type; + } + + /// + /// Things to execute on the server (none for this + /// class). Necessary because it is abstract in the + /// superclass. + /// + protected override void ExecuteOnServer() + { + // Intentionally left blank. + } + + /// + /// Creates a new GameObject on each client. + /// + protected override void ExecuteOnClient() + { + if (!IsRequester()) + { + AcceptDivergence.Accept(Find(FromId), Find(ToId), Type, EdgeId); + } + } + } +} diff --git a/Assets/SEE/Net/Actions/AcceptDivergenceNetAction.cs.meta b/Assets/SEE/Net/Actions/AcceptDivergenceNetAction.cs.meta new file mode 100644 index 0000000000..e3ea553c32 --- /dev/null +++ b/Assets/SEE/Net/Actions/AcceptDivergenceNetAction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 724ad0b3aa74ecf5f9afd1bde58e9762 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SEE/Net/Actions/AddEdgeNetAction.cs b/Assets/SEE/Net/Actions/AddEdgeNetAction.cs index 303993f920..1cfa434925 100644 --- a/Assets/SEE/Net/Actions/AddEdgeNetAction.cs +++ b/Assets/SEE/Net/Actions/AddEdgeNetAction.cs @@ -1,5 +1,5 @@ using SEE.Game; -using UnityEngine; +using SEE.Game.SceneManipulation; namespace SEE.Net.Actions { @@ -52,23 +52,7 @@ protected override void ExecuteOnClient() { if (!IsRequester()) { - GameObject fromGO = GraphElementIDMap.Find(FromId); - if (fromGO) - { - GameObject toGO = GraphElementIDMap.Find(ToId); - if (toGO) - { - GameEdgeAdder.Add(fromGO, toGO, EdgeType); - } - else - { - Debug.LogError($"There is no game node named {ToId} for the target of new edge {EdgeType}.\n"); - } - } - else - { - Debug.LogError($"There is no game node named {FromId} for the source of new edge {EdgeType}.\n"); - } + GameEdgeAdder.Add(Find(FromId), GraphElementIDMap.Find(ToId), EdgeType); } } } diff --git a/Assets/SEE/Net/Actions/AddNodeNetAction.cs b/Assets/SEE/Net/Actions/AddNodeNetAction.cs index 8a12c1dda2..53a3421cbc 100644 --- a/Assets/SEE/Net/Actions/AddNodeNetAction.cs +++ b/Assets/SEE/Net/Actions/AddNodeNetAction.cs @@ -1,4 +1,4 @@ -using SEE.Game; +using SEE.Game.SceneManipulation; using UnityEngine; namespace SEE.Net.Actions diff --git a/Assets/SEE/Net/Actions/DeleteNetAction.cs b/Assets/SEE/Net/Actions/DeleteNetAction.cs index 1e39122978..e32324a655 100644 --- a/Assets/SEE/Net/Actions/DeleteNetAction.cs +++ b/Assets/SEE/Net/Actions/DeleteNetAction.cs @@ -1,4 +1,5 @@ using SEE.Game; +using SEE.Game.SceneManipulation; using UnityEngine; namespace SEE.Net.Actions diff --git a/Assets/SEE/Net/Actions/MoveNetAction.cs b/Assets/SEE/Net/Actions/MoveNetAction.cs index a97db27055..8362336813 100644 --- a/Assets/SEE/Net/Actions/MoveNetAction.cs +++ b/Assets/SEE/Net/Actions/MoveNetAction.cs @@ -1,4 +1,5 @@ using SEE.Game; +using SEE.Game.SceneManipulation; using UnityEngine; namespace SEE.Net.Actions diff --git a/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs b/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs index 14744bee5f..b011efebfb 100644 --- a/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs +++ b/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs @@ -1,4 +1,5 @@ using SEE.Game; +using SEE.Game.SceneManipulation; using UnityEngine; namespace SEE.Net.Actions diff --git a/Assets/SEE/Net/Actions/ReviveNetAction.cs b/Assets/SEE/Net/Actions/ReviveNetAction.cs index 5da8003146..c4ada31951 100644 --- a/Assets/SEE/Net/Actions/ReviveNetAction.cs +++ b/Assets/SEE/Net/Actions/ReviveNetAction.cs @@ -1,4 +1,5 @@ using SEE.Game; +using SEE.Game.SceneManipulation; using SEE.Utils; using System.Collections.Generic; using UnityEngine; diff --git a/Assets/SEE/Net/Actions/SetParentNetAction.cs b/Assets/SEE/Net/Actions/SetParentNetAction.cs index d802eb8a47..b50eb99b46 100644 --- a/Assets/SEE/Net/Actions/SetParentNetAction.cs +++ b/Assets/SEE/Net/Actions/SetParentNetAction.cs @@ -1,4 +1,5 @@ using SEE.Game; +using SEE.Game.SceneManipulation; namespace SEE.Net.Actions { diff --git a/Assets/SEE/Net/Actions/VersionNetAction.cs b/Assets/SEE/Net/Actions/VersionNetAction.cs index e3a7906306..9020d5dbf2 100644 --- a/Assets/SEE/Net/Actions/VersionNetAction.cs +++ b/Assets/SEE/Net/Actions/VersionNetAction.cs @@ -1,5 +1,5 @@ using SEE.Game; -using UnityEngine; +using SEE.Game.SceneManipulation; namespace SEE.Net.Actions { diff --git a/Assets/SEE/Tools/ReflexionAnalysis/Incremental.cs b/Assets/SEE/Tools/ReflexionAnalysis/Incremental.cs index 036a5ab3ed..045157c2bb 100644 --- a/Assets/SEE/Tools/ReflexionAnalysis/Incremental.cs +++ b/Assets/SEE/Tools/ReflexionAnalysis/Incremental.cs @@ -21,13 +21,13 @@ public partial class ReflexionGraph /// adjusting the reflexion analysis incrementally. /// This will propagate and lift the new edge, thereby increasing the counter of the matching specified edge /// if it exists. - /// + /// /// Preconditions: ///
    ///
  • is contained in the implementation graph.
  • ///
  • is contained in the implementation graph.
  • ///
- /// + /// /// Postcondition: A new edge from to /// is contained in the implementation graph and the reflexion data is updated; /// all observers are informed of the change by an event. @@ -52,10 +52,10 @@ public Edge AddToImplementation(Node from, Node to, string type) /// /// Adds the given to the implementation graph, /// adjusting the reflexion analysis incrementally. - /// + /// /// This will propagate and lift the new edge, thereby increasing the counter of the matching specified edge /// if it exists. - /// + /// /// Preconditions: ///
    ///
  • .Source is contained in the implementation graph.
  • @@ -67,9 +67,9 @@ public Edge AddToImplementation(Node from, Node to, string type) /// not in the implementation graph public void AddToImplementation(Edge edge) { - AssertOrThrow(ContainsNode(edge.Source) && edge.Source.IsInImplementation(), + AssertOrThrow(ContainsNode(edge.Source) && edge.Source.IsInImplementation(), () => new NotInSubgraphException(Implementation, edge.Source)); - AssertOrThrow(ContainsNode(edge.Target) && edge.Target.IsInImplementation(), + AssertOrThrow(ContainsNode(edge.Target) && edge.Target.IsInImplementation(), () => new NotInSubgraphException(Implementation, edge.Target)); edge.SetInImplementation(); SetState(edge, State.Undefined); @@ -143,7 +143,7 @@ public void RemoveFromImplementation(Edge edge) /// adjusting the reflexion analysis incrementally. /// This edge will be considered as a specified dependency. /// It may not be redundant. - /// + /// /// Preconditions: ///
      ///
    • is contained in the architecture graph.
    • @@ -169,13 +169,14 @@ public Edge AddToArchitecture(Node from, Node to, string type) return edge; } + /// /// Adds the given to the implementation graph, /// adjusting the reflexion analysis incrementally. - /// + /// /// This will propagate and lift the new edge, thereby increasing the counter of the matching specified edge /// if it exists. - /// + /// /// Preconditions: ///
        ///
      • .Source is contained in the implementation graph.
      • @@ -187,9 +188,9 @@ public Edge AddToArchitecture(Node from, Node to, string type) /// not in the implementation graph public void AddToArchitecture(Edge edge) { - AssertOrThrow(ContainsNode(edge.Source) && edge.Source.IsInArchitecture(), + AssertOrThrow(ContainsNode(edge.Source) && edge.Source.IsInArchitecture(), () => new NotInSubgraphException(Architecture, edge.Source)); - AssertOrThrow(ContainsNode(edge.Target) && edge.Target.IsInArchitecture(), + AssertOrThrow(ContainsNode(edge.Target) && edge.Target.IsInArchitecture(), () => new NotInSubgraphException(Architecture, edge.Target)); AssertNotRedundant(edge.Source, edge.Target, edge.Type); edge.SetInArchitecture(); @@ -414,7 +415,7 @@ public void AddToArchitecture(Node node) /// If is true, the children of /// become root nodes. Otherwise they become children of the parent of /// if there is a parent. - /// + /// /// Precondition: must be contained in the architecture graph. /// Postcondition: is no longer contained in the architecture graph and the reflexion /// data is updated; all observers are informed of the change. @@ -460,7 +461,7 @@ public void AddToImplementation(Node node) /// If is true, the children of /// become root nodes. Otherwise they become children of the parent of /// if there is a parent. - /// + /// /// Precondition: must be contained in the implementation graph. /// Postcondition: is no longer contained in the implementation graph and the reflexion /// data is updated; all observers are informed of the change. @@ -548,7 +549,7 @@ public void UnparentInImplementation(Node child) child.Reparent(null); if (formerTarget != null && !IsExplicitlyMapped(child)) { - // If child was implicitly mapped, this was due to parent, which means we now + // If child was implicitly mapped, this was due to parent, which means we now // have to revert that effect on child and its subtree. List subtree = MappedSubtree(child); Unmap(subtree, formerTarget); @@ -597,7 +598,7 @@ public void AddChildInArchitecture(Node child, Node parent) foreach (Edge outgoing in ascendant.Outgoings) { // We needn't check the "supertree", as we are already iterating over ascendants, hence - // we are setting that parameter to empty collections. + // we are setting that parameter to empty collections. // Since the only change in this operation will be the additional subtree rooted by `child`, // we only need to compare this outgoing edge by that subtree. AssertNotRedundant(outgoing.Source, outgoing.Target, outgoing.Type, @@ -608,7 +609,7 @@ public void AddChildInArchitecture(Node child, Node parent) foreach (Edge incoming in ascendant.Incomings) { // We needn't check the "supertree", as we are already iterating over ascendants, hence - // we are setting that parameter to empty collections. + // we are setting that parameter to empty collections. // Since the only change in this operation will be the additional subtree rooted by `child`, // we only need to compare this outgoing edge by that subtree. AssertNotRedundant(incoming.Source, incoming.Target, incoming.Type, @@ -694,7 +695,7 @@ public void UnparentInArchitecture(Node child) child.Reparent(null); } - + #region Helper /// @@ -754,7 +755,7 @@ private static PartitionedDependencies RefsInSubtree(Node root, Predicate return new PartitionedDependencies(oc, ic, i); } - + /// /// Returns all allowed propagated dependencies of the subtree rooted by . /// See for details. @@ -794,7 +795,7 @@ private void RemoveMapsTo(Edge mapsTo) List subtree = MappedSubtree(mapsTo.Source); Unmap(subtree, mapsTo.Target); Node implSourceParent = mapsTo.Source.Parent; - + if (implSourceParent == null) { // If mapsTo.Source has no parent, all nodes in subtree are not mapped at all any longer. @@ -1002,4 +1003,4 @@ private static void AssertOrThrow(bool condition, Func exception) #endregion } -} \ No newline at end of file +} diff --git a/Assets/SEE/Tools/ReflexionAnalysis/ReflexionAnalysis.cs b/Assets/SEE/Tools/ReflexionAnalysis/ReflexionAnalysis.cs index 7ed8019509..99690a112e 100644 --- a/Assets/SEE/Tools/ReflexionAnalysis/ReflexionAnalysis.cs +++ b/Assets/SEE/Tools/ReflexionAnalysis/ReflexionAnalysis.cs @@ -208,6 +208,19 @@ public static bool IsSpecified(Edge edge) return state == State.Specified || state == State.Convergent || state == State.Absent || state == State.AllowedAbsent; } + /// + /// Returns true if is a divergent + /// edge (present in the implementation graph, but missing in + /// the architecture graph). + /// Precondition: must be in the implementation graph. + /// + /// architecture dependency + /// true if edge is a divergent architecture dependency + public static bool IsDivergent(Edge edge) + { + AssertOrThrow(edge.IsInReflexion(), () => new NotInSubgraphException(Implementation, edge)); + return edge.State() == State.Divergent; + } #endregion #region Edge counter attribute @@ -1425,4 +1438,4 @@ public static void DumpTable(Dictionary table) #endregion } -} \ No newline at end of file +} diff --git a/Assets/SEE/Tools/ReflexionAnalysis/ReflexionGraph.cs b/Assets/SEE/Tools/ReflexionAnalysis/ReflexionGraph.cs index 368852b9c9..45eb288c75 100644 --- a/Assets/SEE/Tools/ReflexionAnalysis/ReflexionGraph.cs +++ b/Assets/SEE/Tools/ReflexionAnalysis/ReflexionGraph.cs @@ -72,7 +72,7 @@ public ReflexionGraph(Graph implementation, Graph architecture, Graph mapping, s /// /// This does not really run the reflexion analysis. Use to start the analysis. /// - public ReflexionGraph(Graph fullGraph, bool allowDependenciesToParents = true): base(fullGraph) + public ReflexionGraph(Graph fullGraph, bool allowDependenciesToParents = true) : base(fullGraph) { AllowDependenciesToParents = allowDependenciesToParents; (Graph implementation, Graph architecture, _) = Disassemble(); diff --git a/Assets/SEE/Utils/ActionHistory.cs b/Assets/SEE/Utils/ActionHistory.cs index 9d087821d9..728cadee04 100644 --- a/Assets/SEE/Utils/ActionHistory.cs +++ b/Assets/SEE/Utils/ActionHistory.cs @@ -68,7 +68,7 @@ public class ActionHistory /// /// An entry describing an executed action in the global action history. /// - public struct GlobalHistoryEntry + public readonly struct GlobalHistoryEntry { /// /// Represents an entry in the globalHistory. diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 179e5a56ab..dbf44ff2f8 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -302,7 +302,7 @@ } }, "com.unity.xr.core-utils": { - "version": "2.2.1", + "version": "2.2.2", "depth": 1, "source": "registry", "dependencies": {