Example #1
0
        private void OnAddFunction(object e)
        {
            if (CurrentInterface == null)
            {
                return;
            }

            CKlaxScriptInterfaceFunction newFunction = new CKlaxScriptInterfaceFunction();

            newFunction.Name = "NewFunction";
            CInterfaceFunctionViewModel newViewModel = new CInterfaceFunctionViewModel(newFunction, this);

            void Redo()
            {
                CurrentInterface.Functions.Add(newFunction);
                Functions.Add(newViewModel);
            }

            void Undo()
            {
                CurrentInterface.Functions.Remove(newFunction);
                Functions.Remove(newViewModel);
            }

            Redo();
            UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
        }
Example #2
0
        public void LoadGraph(CGraph graph)
        {
            CGraph oldGraph = ScriptGraph;

            ScriptGraph = graph;
            GraphName   = ScriptGraph.Name;

            Dictionary <CNode, CScriptNodeViewmodel> nodeToViewModel = new Dictionary <CNode, CScriptNodeViewmodel>();
            List <CScriptNodeViewmodel> newNodes = new List <CScriptNodeViewmodel>();

            foreach (CNode graphNode in ScriptGraph.m_nodes)
            {
                CScriptNodeViewmodel viewmodel = new CScriptNodeViewmodel(graphNode, this);
                nodeToViewModel.Add(graphNode, viewmodel);
                newNodes.Add(viewmodel);
            }

            m_selectedNodes.Clear();
            Nodes = new ObservableCollection <CScriptNodeViewmodel>(newNodes);
            Connections.Clear();

            ResolveScriptNodeConnections(ScriptGraph.m_nodes, nodeToViewModel);
            UndoRedoUtility.Purge(null);

            OnGraphChanged?.Invoke(this, oldGraph, ScriptGraph);
        }
Example #3
0
        public static void DetachEntityFromAllParents(SEntityId entityId)
        {
            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CEntity target = entityId.GetEntity();
                if (target == null || target.RootComponent == null || target.RootComponent.ParentComponent == null)
                {
                    return;
                }

                SEntityComponentId oldRootParent = new SEntityComponentId(target.RootComponent.ParentComponent);

                void Do()
                {
                    CWorld world   = CEngine.Instance.CurrentWorld;
                    CEntity entity = entityId.GetEntity();

                    if (entity != null)
                    {
                        entity.Detach();
                    }
                }

                void Undo()
                {
                    CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                    {
                        CWorld world              = CEngine.Instance.CurrentWorld;
                        CEntity entity            = entityId.GetEntity();
                        CSceneComponent oldParent = oldRootParent.GetComponent <CSceneComponent>();

                        if (oldParent == null)
                        {
                            LogUtility.Log("[UndoRedo] The old parent is invalid! Undo stack has been corrupted and cleared.");
                            UndoRedoUtility.Purge(null);
                            return;
                        }

                        if (entity != null)
                        {
                            entity.AttachToComponent(oldParent);
                        }
                    });
                }

                void Redo()
                {
                    CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                    {
                        Do();
                    });
                }

                Do();

                CRelayUndoItem item = new CRelayUndoItem(Undo, Redo);
                UndoRedoUtility.Record(item);
            });
        }
Example #4
0
        private void DeleteSelectedNodes(IList <CScriptNodeViewmodel> nodesToDelete, bool bRecordUndo = true)
        {
            List <CScriptNodeViewmodel>     deletedNodes       = new List <CScriptNodeViewmodel>();
            List <CNodeConnectionViewModel> deletedConnections = new List <CNodeConnectionViewModel>();

            foreach (var node in nodesToDelete)
            {
                if (node.ScriptNode.AllowDelete)
                {
                    deletedNodes.Add(node);
                    node.GetConnections(deletedConnections);
                }
            }

            if (deletedNodes.Count <= 0)
            {
                return;
            }

            void Redo()
            {
                foreach (var connection in deletedConnections)
                {
                    connection.Disconnect();
                }

                foreach (var deletedNode in deletedNodes)
                {
                    ScriptGraph.m_nodes.Remove(deletedNode.ScriptNode);
                    Nodes.Remove(deletedNode);
                }

                DeselectAll();
            }

            void Undo()
            {
                foreach (var deletedNode in deletedNodes)
                {
                    ScriptGraph.m_nodes.Add(deletedNode.ScriptNode);
                    Nodes.Add(deletedNode);
                }

                foreach (var connection in deletedConnections)
                {
                    connection.Connect();
                }
            }

            Redo();

            if (bRecordUndo)
            {
                UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
            }
        }
        private void DefaultSetter(CObjectProperty property, object oldValue, object newValue, bool bRecordUndoAction)
        {
            if (!ReferenceEquals(oldValue, null) && oldValue.Equals(newValue))
            {
                return;
            }

            void SetValueInternal(CObjectProperty prop, object value)
            {
                if (DispatchSetter)
                {
                    CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                    {
                        if (property.FieldInfo != null)
                        {
                            property.FieldInfo.SetValue(property.Target, value);
                        }
                        else
                        {
                            property.PropertyInfo.SetValue(property.Target, value);
                        }
                    });
                }
                else
                {
                    if (property.FieldInfo != null)
                    {
                        property.FieldInfo.SetValue(property.Target, value);
                    }
                    else
                    {
                        property.PropertyInfo.SetValue(property.Target, value);
                    }
                }
            }

            void Undo()
            {
                SetValueInternal(property, oldValue);
            }

            void Redo()
            {
                SetValueInternal(property, newValue);
            }

            if (bRecordUndoAction)
            {
                CUndoItem item = new CRelayUndoItem(Undo, Redo);
                UndoRedoUtility.Record(item);
            }

            Redo();
        }
Example #6
0
        public void AttachComponent(SEntityComponentId child, SEntityComponentId parent)
        {
            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CSceneComponent parentObj = parent.GetComponent <CSceneComponent>();
                CSceneComponent childObj  = child.GetComponent <CSceneComponent>();

                if (parentObj != null && childObj != null)
                {
                    CSceneComponent oldParent      = childObj.ParentComponent;
                    SEntityComponentId oldParentid = new SEntityComponentId(oldParent);

                    if (childObj.AttachToComponent(parentObj))
                    {
                        UpdateEntityInformation_EngineThread(m_selectedObject.GetTargetEntityId(), true);

                        void Undo()
                        {
                            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                            {
                                CSceneComponent oldParentInst = oldParentid.GetComponent <CSceneComponent>();
                                CSceneComponent childInst     = child.GetComponent <CSceneComponent>();

                                if (oldParentInst != null && childInst != null)
                                {
                                    childInst.AttachToComponent(oldParentInst);
                                    UpdateEntityInformation_EngineThread(child.EntityId, true);
                                }
                            });
                        }

                        void Redo()
                        {
                            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                            {
                                CSceneComponent newParentInst = parent.GetComponent <CSceneComponent>();
                                CSceneComponent childInst     = child.GetComponent <CSceneComponent>();

                                if (newParentInst != null && childInst != null)
                                {
                                    childInst.AttachToComponent(newParentInst);
                                    UpdateEntityInformation_EngineThread(child.EntityId, true);
                                }
                            });
                        }

                        CRelayUndoItem item = new CRelayUndoItem(Undo, Redo);
                        UndoRedoUtility.Record(item);
                    }
                }
            });
        }
Example #7
0
        public static void DestroyEntity(SEntityId entityId)
        {
            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CEntity entity = entityId.GetEntity();
                if (entity != null)
                {
                    entity.Destroy();
                }

                UndoRedoUtility.Purge(null);
            });
        }
Example #8
0
        private void OnSpawnSphereEntity(object arg)
        {
            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CEntity newEntity = CEngine.Instance.CurrentWorld.SpawnEntity <CEntity>();
                SEntityId id      = new SEntityId(newEntity.Id);

                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                {
                    Instance.SetSelectedObject(new CEditableObject(id));
                }));
                UndoRedoUtility.Purge(null);
            });
        }
Example #9
0
        public void DisconnectWithUndo()
        {
            void Redo()
            {
                Disconnect();
            }

            void Undo()
            {
                Connect();
            }

            Redo();
            UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
        }
Example #10
0
        private void InitUndoRedo()
        {
            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CTransformGizmo.OnTranslationChanged += (tr, oldPos, newPos) =>
                {
                    void undo()
                    {
                        tr.SetWorldPosition(oldPos);
                    }
                    void redo()
                    {
                        tr.SetWorldPosition(newPos);
                    }

                    CRelayUndoItem item = new CRelayUndoItem(undo, redo);
                    UndoRedoUtility.Record(item);
                };
                CTransformGizmo.OnRotationChanged += (tr, oldRot, newRot) =>
                {
                    void undo()
                    {
                        tr.SetWorldRotation(oldRot);
                    }
                    void redo()
                    {
                        tr.SetWorldRotation(newRot);
                    }

                    CRelayUndoItem item = new CRelayUndoItem(undo, redo);
                    UndoRedoUtility.Record(item);
                };
                CTransformGizmo.OnScaleChanged += (tr, oldScale, newScale) =>
                {
                    void undo()
                    {
                        tr.SetWorldScale(oldScale);
                    }
                    void redo()
                    {
                        tr.SetWorldScale(newScale);
                    }

                    CRelayUndoItem item = new CRelayUndoItem(undo, redo);
                    UndoRedoUtility.Record(item);
                };
            });
        }
Example #11
0
        private void ConnectPins(CPinViewModel source, CPinViewModel target)
        {
            CNodeConnectionViewModel newConnection = new CNodeConnectionViewModel(source, target, this);

            void Redo()
            {
                newConnection.Connect();
            }

            void Undo()
            {
                newConnection.Disconnect();
            }

            Redo();
            UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
        }
Example #12
0
        private void OnDelete(object e)
        {
            void Redo()
            {
                m_parentViewModel.CurrentInterface.Functions.Remove(m_interfaceFunction);
                m_parentViewModel.Functions.Remove(this);
            }

            void Undo()
            {
                m_parentViewModel.CurrentInterface.Functions.Add(m_interfaceFunction);
                m_parentViewModel.Functions.Add(this);
            }

            Redo();
            UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
        }
Example #13
0
        private void OnSpawnCubeEntity(object arg)
        {
            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CMeshAsset cubeAsset = CImportManager.Instance.MeshImporter.LoadMeshAsync("TestResources/Cube.fbx");
                CEntity cubeEntity   = CEngine.Instance.CurrentWorld.SpawnEntity <CEntity>();
                cubeEntity.AddComponent <CSceneComponent>(true, true);
                CMeshComponent meshComponent = cubeEntity.AddComponent <CMeshComponent>(true, true);
                meshComponent.LocalPosition  = new Vector3(0, 0, 0.5f);
                meshComponent.Mesh           = cubeAsset;
                SEntityId id = new SEntityId(cubeEntity.Id);

                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                {
                    Instance.SetSelectedObject(new CEditableObject(id));
                }));
                UndoRedoUtility.Purge(null);
            });
        }
Example #14
0
        private void DeleteNode(CScriptNodeViewmodel nodeToDelete, bool bRecordUndo = true)
        {
            if (!nodeToDelete.ScriptNode.AllowDelete)
            {
                return;
            }

            List <CNodeConnectionViewModel> deletedConnections = new List <CNodeConnectionViewModel>();

            nodeToDelete.GetConnections(deletedConnections);

            void Redo()
            {
                foreach (var connection in deletedConnections)
                {
                    connection.Disconnect();
                }

                ScriptGraph.m_nodes.Remove(nodeToDelete.ScriptNode);
                Nodes.Remove(nodeToDelete);

                DeselectAll();
            }

            void Undo()
            {
                ScriptGraph.m_nodes.Add(nodeToDelete.ScriptNode);
                Nodes.Add(nodeToDelete);

                foreach (var connection in deletedConnections)
                {
                    connection.Connect();
                }
            }

            Redo();

            if (bRecordUndo)
            {
                UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
            }
        }
Example #15
0
        private void AddInput(CKlaxVariable parameter, bool bIsNewParameter)
        {
            var newViewmodel  = new CEntityVariableViewModel(parameter);
            var functionGraph = m_functionGraph;

            void Redo()
            {
                newViewmodel.PropertyChanged += OnParameterPropertyChanged;
                functionGraph.InputParameters.Add(parameter);
                InputParameters.Add(newViewmodel);
                functionGraph.RebuildFunctionNodes();
            }

            void Undo()
            {
                newViewmodel.PropertyChanged -= OnParameterPropertyChanged;
                InputParameters.Remove(newViewmodel);
                functionGraph.InputParameters.Remove(parameter);
                functionGraph.RebuildFunctionNodes();
            }

            newViewmodel.DeleteCommand = new CRelayCommand(arg =>
            {
                Undo();
                UndoRedoUtility.Record(new CRelayUndoItem(Redo, Undo));
            });

            if (bIsNewParameter)
            {
                Redo();
            }
            else
            {
                newViewmodel.PropertyChanged += OnParameterPropertyChanged;
                InputParameters.Add(newViewmodel);
            }

            if (bIsNewParameter)
            {
                UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
            }
        }
Example #16
0
        private void OnMouseUpNode(object e, Point mousePos)
        {
            if (!m_bIsDraggingNodes)
            {
                NotifySelected(m_clickedNode);
            }
            else
            {
                List <(CScriptNodeViewmodel, Point, Point)> nodeMoves = new List <(CScriptNodeViewmodel, Point, Point)>();
                foreach (var node in m_selectedNodes)
                {
                    Point oldPos = new Point(node.PosXStart, node.PosYStart);
                    Point newPos = new Point(node.PosX, node.PosY);
                    nodeMoves.Add((node, oldPos, newPos));

                    node.StopMove();
                }

                void Redo()
                {
                    foreach (var nodeMove in nodeMoves)
                    {
                        nodeMove.Item1.MoveTo(in nodeMove.Item3);
                    }
                }

                void Undo()
                {
                    foreach (var nodeMove in nodeMoves)
                    {
                        nodeMove.Item1.MoveTo(in nodeMove.Item2);
                    }
                }

                UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
            }

            MouseHook.OnMouseMove -= OnMouseMoveNode;
            MouseHook.OnMouseUp   -= OnMouseUpNode;
            m_bIsDraggingNodes     = false;
        }
Example #17
0
        private void InternalAddComponent(object argument)
        {
            AddComponentMenuOpen = false;

            if ((argument is Type type) && m_selectedObject != null)
            {
                SEntityId selectedEntityId = m_selectedObject.GetTargetEntityId();

                CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                {
                    CEntity entity = selectedEntityId.GetEntity();
                    if (entity != null)
                    {
                        entity.AddComponent(type, true, true);

                        UpdateEntityInformation_EngineThread(selectedEntityId);
                        UndoRedoUtility.Purge(null);
                    }
                });
            }
        }
Example #18
0
        public CScriptNodeViewmodel AddNode(CNode node, bool bRecordUndo = true)
        {
            CScriptNodeViewmodel nodeViewModel = new CScriptNodeViewmodel(node, this);

            void Redo()
            {
                Nodes.Add(nodeViewModel);
                ScriptGraph.m_nodes.Add(nodeViewModel.ScriptNode);
            }

            void Undo()
            {
                DeleteNode(nodeViewModel, false);
            }

            Redo();
            if (bRecordUndo)
            {
                UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
            }
            return(nodeViewModel);
        }
Example #19
0
        private void OnStopGame(object argument)
        {
            IsInPlayMode = false;

            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CWorld world = CEngine.Instance.CurrentWorld;

                world.StopPlayMode();
                world.ChangeLevel(null, m_prePlayModeLevel.GetLevel());

                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                {
                    OpenedLevelAsset = m_prePlayModeLevelReference;
                    m_prePlayModeLevel = null;
                    m_prePlayModeLevelReference = null;
                    UndoRedoUtility.Purge(null);
                    UndoRedoModel.IsRecording = true;

                    GetTool <CViewportViewModel>().FreeMouseCursor();
                }));
            });
        }
Example #20
0
        public void PostWorldLoad()
        {
            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CWorld world             = CEngine.Instance.CurrentWorld;
                world.OnEntityDestroyed += (entity) =>
                {
                    Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)(() =>
                    {
                        SetSelectedObject(null);
                    }));
                };

                world.OnLevelChanged += (asset, level) =>
                {
                    UndoRedoUtility.Purge(null);
                };
            });

            foreach (var tool in Tools)
            {
                tool.PostWorldLoad();
            }
        }
Example #21
0
        public static void DestroyComponent(SEntityComponentId id)
        {
            if (id.OverrideComponent == null)
            {
                CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                {
                    CEntityComponent component = id.GetComponent <CEntityComponent>();
                    if (component != null)
                    {
                        component.Destroy();
                    }
                });
            }
            else
            {
                CEntityComponent component = id.GetComponent <CEntityComponent>();
                if (component != null)
                {
                    component.Destroy();
                }
            }

            UndoRedoUtility.Purge(null);
        }
Example #22
0
        private void AddInput(CKlaxVariable parameter, bool bIsNewParameter)
        {
            var newViewmodel = new CEntityVariableViewModel(parameter);

            void Redo()
            {
                m_interfaceFunction.InputParameters.Add(parameter);
                InputParameters.Add(newViewmodel);
            }

            void Undo()
            {
                InputParameters.Remove(newViewmodel);
                m_interfaceFunction.InputParameters.Remove(parameter);
            }

            newViewmodel.DeleteCommand = new CRelayCommand(arg =>
            {
                Undo();
                UndoRedoUtility.Record(new CRelayUndoItem(Redo, Undo));
            });

            if (bIsNewParameter)
            {
                Redo();
            }
            else
            {
                InputParameters.Add(newViewmodel);
            }

            if (bIsNewParameter)
            {
                UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
            }
        }
        internal void AttachEntities(SEntityId child, SEntityId parent)
        {
            CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
            {
                CEntity childEntity  = child.GetEntity();
                CEntity parentEntity = parent.GetEntity();

                if (childEntity != null && parentEntity != null)
                {
                    CEntity oldParent     = childEntity.Parent;
                    bool bOldParentExists = oldParent != null;
                    SEntityId oldParentId = bOldParentExists ? new SEntityId(oldParent.Id) : SEntityId.Invalid;

                    if (parentEntity.RootComponent != null && childEntity.RootComponent != null)
                    {
                        if (!parentEntity.RootComponent.IsChildOf(childEntity.RootComponent))
                        {
                            childEntity.AttachToEntity(parentEntity);

                            void Undo()
                            {
                                CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                                {
                                    CEntity childInst = child.GetEntity();

                                    if (childInst != null)
                                    {
                                        if (bOldParentExists)
                                        {
                                            CEntity oldParentInst = oldParentId.GetEntity();
                                            if (oldParentInst != null && childInst != null)
                                            {
                                                childInst.AttachToEntity(oldParentInst);
                                            }
                                        }
                                        else
                                        {
                                            childInst.Detach();
                                        }
                                    }
                                });
                            }

                            void Redo()
                            {
                                CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                                {
                                    CEntity newParentInst = parent.GetEntity();
                                    CEntity childInst     = child.GetEntity();

                                    if (newParentInst != null && childInst != null)
                                    {
                                        childInst.AttachToEntity(newParentInst);
                                    }
                                });
                            }

                            CRelayUndoItem item = new CRelayUndoItem(Undo, Redo);
                            UndoRedoUtility.Record(item);
                        }
                    }
                }
            });
        }
Example #24
0
        public static void MakeComponentRoot(SEntityComponentId id, bool bDispatch = true)
        {
            void Command()
            {
                CSceneComponent newRoot = id.GetComponent <CSceneComponent>();

                if (newRoot == null)
                {
                    return;
                }

                CSceneComponent oldRoot = newRoot.Owner.RootComponent;

                if (oldRoot == null)
                {
                    return;
                }

                SEntityComponentId oldId = new SEntityComponentId(oldRoot);

                void Do()
                {
                    CSceneComponent component = id.GetComponent <CSceneComponent>();

                    if (component != null)
                    {
                        CEntity owner = component.Owner;
                        owner.SetRootComponent(component);
                    }
                }

                void Redo()
                {
                    CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                    {
                        Do();

                        Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)(() =>
                        {
                            CWorkspace space = CWorkspace.Instance;
                            space.SetSelectedObject(space.SelectedEditableObject, true);
                        }));
                    });
                }

                void Undo()
                {
                    CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                    {
                        CSceneComponent component = oldId.GetComponent <CSceneComponent>();

                        if (component != null)
                        {
                            CEntity owner = component.Owner;
                            owner.SetRootComponent(component);

                            Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)(() =>
                            {
                                CWorkspace space = CWorkspace.Instance;
                                space.SetSelectedObject(space.SelectedEditableObject, true);
                            }));
                        }
                    });
                }

                Do();

                CUndoItem item = new CRelayUndoItem(Undo, Redo);

                UndoRedoUtility.Record(item);
            }

            if (bDispatch)
            {
                CEngine.Instance.Dispatch(EEngineUpdatePriority.BeginFrame, () =>
                {
                    Command();
                });
            }
            else
            {
                Command();
            }
        }
Example #25
0
        private void AddNodesToGraph(IList <CNode> nodes, Point referencePoint, bool bSelectNewNodes, bool bRecordUndo = true)
        {
            Dictionary <CNode, CScriptNodeViewmodel> nodeToViewModel = new Dictionary <CNode, CScriptNodeViewmodel>();
            List <CScriptNodeViewmodel> addedNodes = new List <CScriptNodeViewmodel>(nodes.Count());
            Point nodesMin = new Point(double.MaxValue, double.MaxValue);
            Point nodesMax = new Point(double.MinValue, double.MinValue);

            foreach (CNode scriptNode in nodes)
            {
                CScriptNodeViewmodel viewmodel = AddNode(scriptNode, false);
                nodeToViewModel.Add(scriptNode, viewmodel);
                addedNodes.Add(viewmodel);

                if (viewmodel.PosX < nodesMin.X)
                {
                    nodesMin.X = viewmodel.PosX;
                }

                if (viewmodel.PosY < nodesMin.Y)
                {
                    nodesMin.Y = viewmodel.PosY;
                }

                if (viewmodel.PosX > nodesMax.X)
                {
                    nodesMax.X = viewmodel.PosX;
                }

                if (viewmodel.PosY > nodesMax.Y)
                {
                    nodesMax.Y = viewmodel.PosY;
                }
            }

            Point nodesMid = new Point((nodesMin.X + nodesMax.X) / 2, (nodesMin.Y + nodesMax.Y) / 2);

            foreach (CScriptNodeViewmodel nodeViewmodel in addedNodes)
            {
                Vector toMid  = new Point(nodeViewmodel.PosX, nodeViewmodel.PosY) - nodesMid;
                Point  newPos = referencePoint + toMid;
                nodeViewmodel.PosX = newPos.X;
                nodeViewmodel.PosY = newPos.Y;
            }

            if (bSelectNewNodes)
            {
                SelectNodes(addedNodes);
            }

            ResolveScriptNodeConnections(nodes, nodeToViewModel);

            if (bRecordUndo)
            {
                void Redo()
                {
                    foreach (CScriptNodeViewmodel nodeViewmodel in addedNodes)
                    {
                        Nodes.Add(nodeViewmodel);
                        ScriptGraph.m_nodes.Add(nodeViewmodel.ScriptNode);

                        Vector toMid  = new Point(nodeViewmodel.PosX, nodeViewmodel.PosY) - nodesMid;
                        Point  newPos = referencePoint + toMid;
                        nodeViewmodel.PosX = newPos.X;
                        nodeViewmodel.PosY = newPos.Y;

                        if (bSelectNewNodes)
                        {
                            SelectNodes(addedNodes);
                        }

                        ResolveScriptNodeConnections(nodes, nodeToViewModel);
                    }
                }

                void Undo()
                {
                    DeleteSelectedNodes(addedNodes, false);
                }

                UndoRedoUtility.Record(new CRelayUndoItem(Undo, Redo));
            }
        }