예제 #1
0
파일: Moments.cs 프로젝트: jfreax/BAIMP
        public Moments(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Image", typeof(TScan)));

            output.Add(new Compatible("Moments", typeof(TFeatureList<double>)));
        }
예제 #2
0
파일: Haralick.cs 프로젝트: jfreax/BAIMP
        public Haralick(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Matrix", typeof(TMatrix)));

            output.Add(new Compatible("Haralick Features", typeof(TFeatureList<double>)));
        }
예제 #3
0
파일: Galloway.cs 프로젝트: jfreax/BAIMP
        public Galloway(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("GLRL-Matrix", typeof(TMatrix)));

            output.Add(new Compatible("Galloway Features", typeof(TFeatureList<double>)));
        }
예제 #4
0
        public HistogramFeatures(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Histogram", typeof(THistogram)));

            output.Add(new Compatible("Histogram Features", typeof(TFeatureList<double>)));
        }
예제 #5
0
        public Autocorrelation(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Image", typeof(TScan)));

            output.Add(new Compatible("Correlogram", typeof(THistogram)));

            options.Add(new Option("Offest", 0, 1024, 6));
        }
예제 #6
0
파일: LawsEnergy.cs 프로젝트: jfreax/BAIMP
        public LawsEnergy(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Image (Windowed)", typeof(TScan)));

            output.Add(new Compatible("Laws Energy Features", typeof(TFeatureList<double>)));

            options.Add(new OptionBool("Normalize", true));
        }
예제 #7
0
파일: GLRLM.cs 프로젝트: jfreax/BAIMP
        public GLRLM(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Image", typeof(TScan)));

            output.Add(new Compatible("GLRL-Matrix", typeof(TMatrix)));

            options.Add(new Option("Bpp", 2, 32, 8));
        }
예제 #8
0
파일: Tamura.cs 프로젝트: jfreax/BAIMP
        public Tamura(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Image", typeof(TScan)));

            output.Add(new Compatible("Tamura Features", typeof(TFeatureList<double>)));
            output.Add(new Compatible("Directionality Histogram", typeof(THistogram)));

            options.Add(new Option("Directionality Histogram #Bins", 4, int.MaxValue, 64));
        }
예제 #9
0
파일: GLCM.cs 프로젝트: jfreax/BAIMP
        public GLCM(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Image", typeof(TScan)));

            output.Add(new Compatible("Co-occurence matrix", typeof(TMatrix)));

            options.Add(new Option("Bpp", 2, 32, 8));
            options.Add(new Option("X Offest", 0, 10, 1));
            options.Add(new Option("Y Offest", 0, 10, 1));
        }
예제 #10
0
파일: LBP.cs 프로젝트: jfreax/BAIMP
        public LBP(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible("Image", typeof(TScan)));

            output.Add(new Compatible("LBP Feature Vector", typeof(TFeatureList<double>)));
            output.Add(new Compatible("LBP Histogram", typeof(THistogram)));

            options.Add(new Option("Block size 2^x", 2, 32, 3));
            options.Add(new OptionBool("Normalize", true));
            options.Add(new OptionBool("Rotation invariant", true));
            options.Add(new OptionBool("Uniform LBP", true));
        }
예제 #11
0
파일: Windower.cs 프로젝트: jfreax/BAIMP
        public Windower(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            input.Add(new Compatible(
                "Image",
                typeof(TScan)
            ));

            output.Add(new Compatible(
                "ROI",
                typeof(TScan)
            ));

            options.Add(new Option("Width", 1, int.MaxValue, 64));
            options.Add(new Option("Height", 1, int.MaxValue, 64));
        }
예제 #12
0
        public ProjectFiles(PipelineNode parent, ScanCollection scanCollection)
            : base(parent, scanCollection)
        {
            output.Add(new Compatible(
                "Scan",
                typeof(TScan)
            ));

            options.Add(new OptionBool("Masked only", true));

            scanTypeComboBox = new OptionDropDown("Scan type", "Unknown");
            options.Add(scanTypeComboBox);

            request.Add(RequestType.ScanCollection);

            UpdateFiberTypes(scanCollection);
            scanCollection.FilesChanged += (s, e) => UpdateFiberTypes(scanCollection);
        }
예제 #13
0
파일: Result.cs 프로젝트: jfreax/BAIMP
        public Result(PipelineNode node, IType data, Result[] input, bool preserve = false, int yieldID = -1)
        {
            this.node = node;
            this.Data = data;
            this.Input = input;
            this.Preserve = preserve;

            if (input != null) {
                completeDistinctName = node.ToString();
                if (yieldID != -1) {
                    completeDistinctName += "#" + yieldID;
                }

                HashSet<string> visitedNames = new HashSet<string>();
                foreach (Result r in input) {
                    if (!visitedNames.Contains(r.completeDistinctName)) {
                        completeDistinctName = string.Format("{0}_{1}", r.completeDistinctName, completeDistinctName);
                        visitedNames.Add(r.completeDistinctName);
                    }
                }
            }
        }
예제 #14
0
        protected override void OnButtonPressed(ButtonEventArgs args)
        {
            SetFocus();

            Point position = args.Position;
            position.X /= scaleFactor;
            position.Y /= scaleFactor;

            popupWindow.Hide();
            initialScrollPosition = Point.Zero;

            PipelineNode node = GetNodeAt(position, true);

            if (node != null) {
                ButtonEventArgs nodeArgs = new ButtonEventArgs();
                nodeArgs.X = position.X - node.bound.Location.X;
                nodeArgs.Y = position.Y - node.bound.Location.Y;
                nodeArgs.Button = args.Button;
                nodeArgs.MultiplePress = args.MultiplePress;
                if (node.OnButtonPressed(nodeArgs)) {
                    return;
                }
            }

            switch (args.Button) {
            case PointerButton.Left:
                if (node != null) { // clicked on node
                    if (args.MultiplePress >= 2) {
                        OpenOptionWindow(node);
                        mouseAction = MouseAction.None;
                        args.Handled = true;
                        break;
                    } else {

                        MarkerNode mNode = node.GetMarkerNodeAt(position);
                        if (mNode != null && !mNode.compatible.IsFinal()) {
                            connectNodesStartMarker = mNode;
                            mouseAction |= MouseAction.AddEdge | MouseAction.AddEdgeNew;
                        } else {
                            if (node.bound.Contains(position)) {
                                nodeToMoveOffset = new Point(
                                    node.bound.Location.X - position.X,
                                    node.bound.Location.Y - position.Y
                                );
                                lastSelectedNode = node;
                                mouseAction |= MouseAction.MoveNode;
                            }
                        }
                    }
                } else {
                    Tuple<MarkerNode, MarkerEdge> edge = GetEdgeAt(position);
                    if (edge != null) { // clicked on edge
                        if (edge.Item2.r >= 0.5) {
                            connectNodesStartMarker = edge.Item1;
                        } else {
                            connectNodesStartMarker = (MarkerNode) edge.Item2.to;
                        }
                        edge.Item1.RemoveEdge(edge.Item2);
                        lastSelectedEdge = edge;
                        mouseAction |= MouseAction.MoveEdge;
                        args.Handled = true;
                    }
                }

                break;

            case PointerButton.Right:
                lastSelectedEdge = GetEdgeAt(position);
                if (lastSelectedEdge != null) {
                    contextMenuEdge.Popup();
                } else {
                    PipelineNode nodeRight = GetNodeAt(position, true);
                    if (nodeRight != null) {
                        lastSelectedNode = nodeRight;
                        if (lastSelectedNode.algorithm.options.Count > 0) {
                            contextMenuNodeOptions.Show();
                        } else {
                            contextMenuNodeOptions.Hide();
                        }
                        contextMenuNode.Popup();
                    }
                }
                break;
            case PointerButton.Middle:
                mouseMover.EnableMouseMover(args.Position);

                if (oldCursor != CursorType.Move) {
                    oldCursor = this.Cursor;
                    this.Cursor = CursorType.Move;
                }

                break;
            }
        }
예제 #15
0
        protected override void OnDragDrop(DragEventArgs args)
        {
            Point position = args.Position;
            position.X /= scaleFactor;
            position.Y /= scaleFactor;

            args.Success = true;
            try {
                string algoType = args.Data.GetValue(TransferDataType.Text).ToString();

                PipelineNode node =
                    new PipelineNode(project, this, algoType, new Rectangle(position, PipelineNode.AbsMinNodeSize));
                node.QueueRedraw += QueueRedraw;

                SetNodePosition(node);
                nodes.Add(node);
                Log.Add(LogLevel.Verbose, this.GetType().Name, "New node added \"" + node + "\".");

                EmitDataChanged();
                this.QueueDraw();

            } catch (Exception e) {
                Console.WriteLine(e.StackTrace);
                Console.WriteLine(e.Message);
                args.Success = false;

                Log.Add(LogLevel.Error, this.GetType().Name, "Failed to add new node.");
            }
        }
예제 #16
0
파일: Census.cs 프로젝트: jfreax/BAIMP
 public Census(PipelineNode parent, ScanCollection scanCollection)
     : base(parent, scanCollection)
 {
     input.Add(new Compatible("Image", typeof(TScan)));
 }
예제 #17
0
파일: Result.cs 프로젝트: jfreax/BAIMP
 /// <summary>
 /// Call when you use this data.
 /// </summary>
 public void Used(PipelineNode by)
 {
     lock (removeLock) {
         if (!usedBy.ContainsKey(by)) {
             usedBy[by] = 0;
         }
         usedBy[by]++;
     }
 }
예제 #18
0
        protected override void OnMouseMoved(MouseMovedEventArgs args)
        {
            Point position = args.Position;
            position.X /= scaleFactor;
            position.Y /= scaleFactor;

            mousePosition = position;

            if (mouseAction.HasFlag(MouseAction.MoveNode)) {
                if (lastSelectedNode != null) {
                    lastSelectedNode.bound.Location = position.Offset(nodeToMoveOffset);
                    lastSelectedNode.OnMove(null);
                    QueueDraw();
                }
            }
            if (mouseAction.HasFlag(MouseAction.AddEdge) || mouseAction.HasFlag(MouseAction.MoveEdge)) {
                mouseAction &= ~MouseAction.AddEdgeNew;
                MarkerNode mNode = GetInOutMarkerAt(position, PipelineNode.NodeInOutSpace);
                if (mNode != null && mNode.Match(connectNodesStartMarker)) {
                    connectNodesEnd = new Point(mNode.IsInput ? mNode.Bounds.Left : mNode.Bounds.Right, mNode.Bounds.Center.Y);
                } else {
                    connectNodesEnd = position;
                }

                QueueDraw();
            }

            if (!mouseAction.HasFlag(MouseAction.MoveNode)) {
                MarkerNode mNode = GetInOutMarkerAt(position);
                if (mNode != null) {
                    TooltipText = mNode.compatible + "\n" + mNode.compatible.Type;
                } else {
                    TooltipText = string.Empty;
                }
            }

            PipelineNode node = GetNodeAt(position, true);
            if (node != null) {
                if (currentHoveredNode != node) {
                    if (currentHoveredNode != null) {
                        currentHoveredNode.OnMouseExited();
                    }
                    currentHoveredNode = node;
                    node.OnMouseEntered();
                }
            } else {
                if (currentHoveredNode != null) {
                    currentHoveredNode.OnMouseExited();
                    currentHoveredNode = null;
                }
            }
        }
예제 #19
0
        /// <summary>
        /// Callback function called when algorithm finished
        /// </summary>
        /// <param name="startNode"></param>
        /// <param name="priority">Current thread priority.</param>
        /// <param name="result">Output of algorithm.</param>
        /// <param name="input">Reference to the data, that was used to compute these results.</param>
        /// <param name="yieldID">Unique identifier for every yielded output from a node.</param>
        void OnFinish(PipelineNode startNode, int priority, IType[] result, Result[] input, int yieldID = -1)
        {
            List<Compatible> compatibleOutput = startNode.algorithm.Output;
            if (result.Length != compatibleOutput.Count) {
                throw new ArgumentOutOfRangeException(); // TODO throw a proper exception
            }

            if (startNode.SaveResult || startNode.HasFinalNode()) {
                startNode.results.Add(new Tuple<IType[], Result[]>(result, input));
            }

            int offsetIndex = startNode.algorithm.Input.Count;
            for (int i = 0; i < result.Length; i++) {
                if (result[i] == null) {
                    Console.WriteLine("null");
                }

                Result resultWrapper = new Result(startNode, result[i], input, startNode.SaveResult, yieldID);
                HashSet<PipelineNode> markedAsUse = new HashSet<PipelineNode>();

                // enqueue new data
                foreach (Edge edge in startNode.MNodes[offsetIndex+i].Edges) {
                    MarkerNode targetNode = edge.to as MarkerNode;
                    if (targetNode == null) {
                        break;
                    }

                    if (!markedAsUse.Contains(targetNode.parent)) {
                        markedAsUse.Add(targetNode.parent);
                        resultWrapper.Used(targetNode.parent);
                    }

                    targetNode.EnqueueInput(resultWrapper);

                    // start next node
                    if (targetNode.parent.IsReady()) {
                        if (cancellationToken.IsCancellationRequested) {
                            return;
                        }
                        this.Start(targetNode.parent, targetNode.parent.DequeueInput(), priority - 1);
                    }
                }

                // dispose data when no one uses them
                if (resultWrapper.InUse <= 0 && !resultWrapper.Preserve) {
                    resultWrapper.Finish(null);
                }
            }
        }
예제 #20
0
파일: Arff.cs 프로젝트: jfreax/BAIMP
        ResultStats GetResultStats(Result[] inputs, string sourceString, string distinctSourceString, PipelineNode node)
        {
            string uncompleteFeatureName = node.ToString(); // + "_" + feature.Key();
            string className = string.Empty;
            string fibername = string.Empty;

            List<Result> currInputs = new List<Result>();
            currInputs.AddRange(inputs);
            while (currInputs != null && currInputs.Count > 0) {
                List<Result> nextInputs = new List<Result>();
                foreach (Result input in currInputs) {
                    if (input.Data != null) {
                        if (input.Node.algorithm.AlgorithmType == AlgorithmType.Input) {
                            if (string.IsNullOrEmpty(fibername)) {
                                fibername = input.Data.ToString();
                            }

                            BaseScan scan = input.Data as BaseScan;
                            if (scan != null) {
                                if (scan.Metadata.ContainsKey("LensMagnification")) {
                                    className = string.Format("{0}_{1}x", scan.FiberType, scan.Metadata["LensMagnification"]);
                                } else {
                                    className = scan.FiberType;
                                }
                            }
                        } else {
                            uncompleteFeatureName = string.Format("{0}_{1}", input.Node, uncompleteFeatureName);
                        }
                    }
                    if (input.Input != null) {
                        nextInputs.AddRange(input.Input);
                    }
                }
                currInputs = nextInputs;
            }

            distinctSourceString = fibername + distinctSourceString;
            fibername += "_" + sourceString;

            return new ResultStats(className, fibername, distinctSourceString, uncompleteFeatureName);
        }
예제 #21
0
파일: Arff.cs 프로젝트: jfreax/BAIMP
        void AddResults(List<IFeature> features, Result[] inputs, string sourceString, string distinctSourceString, PipelineNode node)
        {
            ResultStats rs = GetResultStats(inputs, sourceString, distinctSourceString, node);

            foreach (IFeature feature in features) {
                AddValue(rs, feature);
            }
        }
예제 #22
0
파일: Arff.cs 프로젝트: jfreax/BAIMP
        void AddResult(IFeature feature, Result[] inputs, string sourceString, string distinctSourceString, PipelineNode node)
        {
            ResultStats rs = GetResultStats(inputs, sourceString, distinctSourceString, node);

            AddValue(rs, feature);
        }
예제 #23
0
        /// <summary>
        /// Opens the option window.
        /// </summary>
        /// <param name="pNode">Pipeline node for which the option should be shown.</param>
        void OpenOptionWindow(PipelineNode pNode)
        {
            Dialog d = new Dialog();
            d.Title = String.Format("Option for \"{0}\"", pNode.algorithm);
            Table table = new Table();
            VBox contentBox = new VBox();

            int i = 0;
            Widget[] entries = new Widget[pNode.algorithm.Options.Count];
            foreach (BaseOption option in pNode.algorithm.Options) {
                table.Add(new Label(option.Name), 0, i);

                Widget entry = option.ToWidget();
                entries[i] = entry;
                table.Add(entry, 1, i);
                i++;
            }

            TextEntry commentEntry = new TextEntry();
            commentEntry.PlaceholderText = "Comments...";
            commentEntry.MultiLine = true;
            commentEntry.Text = pNode.userComment;

            contentBox.PackStart(table);
            contentBox.PackEnd(commentEntry);
            d.Content = contentBox;

            d.Buttons.Add(new DialogButton(Command.Cancel));
            d.Buttons.Add(new DialogButton(Command.Apply));

            var r = d.Run(this.ParentWindow);

            if (r != null && r.Id == Command.Apply.Id) {
                i = 0;
                foreach (BaseOption option in pNode.algorithm.Options) {
                    object value = option.ExtractValueFrom(entries[i]);
                    if (value != null) {
                        option.Value = value;
                    }

                    i++;
                }
                pNode.userComment = commentEntry.Text;
            }

            d.Dispose();
        }
예제 #24
0
        /// <summary>
        /// Get the node that intersects a given rectangle
        /// </summary>
        /// <returns>The node; or null</returns>
        /// <param name="rectangle">Rectangle to test with.</param>
        /// <param name="ignoreNode">Optional: Ignore this node.</param>
        /// <param name="withExtras">Match not only main body of node, but also in/out marker</param>
        PipelineNode GetNodeAt(Rectangle rectangle, PipelineNode ignoreNode = null, bool withExtras = true)
        {
            foreach (PipelineNode node in nodes) {
                Rectangle nodeBound = node.bound;
                if (withExtras) {
                    nodeBound = node.BoundWithExtras;
                }

                if (node != ignoreNode &&
                    nodeBound.IntersectsWith(rectangle)) {
                    return node;
                }
            }

            return null;
        }
예제 #25
0
        internal BaseAlgorithm(PipelineNode parent, ScanCollection scanCollection)
        {
            this.Parent = parent;

            input = new List<Compatible>();
            output = new List<Compatible>();
            request = new HashSet<RequestType>();
            options = new List<BaseOption>();
        }
예제 #26
0
파일: Result.cs 프로젝트: jfreax/BAIMP
        /// <summary>
        /// Call when finished using these data.
        /// </summary>
        public void Finish(PipelineNode by)
        {
            lock (removeLock) {
                if (by == null) {
                    if (usedBy.Count == 0 && !preserve) {
                        Dispose();
                    }
                } else if (usedBy.ContainsKey(by)) {
                    usedBy[by] = usedBy[by] - 1;
                    if (usedBy[by] <= 0) {
                        usedBy.Remove(by);

                        if (usedBy.Count == 0 && !preserve) {
                            Dispose();
                        }
                    }
                } else if (usedBy.Count == 0 && !preserve) {
                    Dispose();
                }
            }
        }
예제 #27
0
        /// <summary>
        /// Gets the single, yielded, result.
        /// </summary>
        /// <param name="startNode">Start node.</param>
        /// <param name="origInput">Original input.</param>
        /// <param name="priority">Priority.</param>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event args.</param>
        void GetSingleData(PipelineNode startNode, Result[] origInput, int priority, object sender, AlgorithmEventArgs e)
        {
            object yieldKey = e.InputRef == null ? origInput as object : e.InputRef as object;
            if (yieldIds.ContainsKey(yieldKey)) {
                yieldIds[yieldKey]++;
            } else {
                yieldIds[yieldKey] = 1;
            }

            if (e.InputRef != null) {
                Result[] inputResults = new Result[e.InputRef.Length];
                int i = 0;
                foreach (IType input in e.InputRef) {
                    inputResults[i] = new Result(
                        startNode,
                        input,
                        origInput.Length > i ? origInput[i].Input : null,
                        startNode.SaveResult);
                    i++;
                }
                OnFinish(startNode, priority, e.Data, inputResults, yieldIds[yieldKey]);
            } else {
                OnFinish(startNode, priority, e.Data, origInput, yieldIds[yieldKey]);
            }
        }
예제 #28
0
파일: Result.cs 프로젝트: jfreax/BAIMP
 public bool IsUsed(PipelineNode by)
 {
     return usedBy.ContainsKey(by);
 }
예제 #29
0
        /// <summary>
        /// Start to evaluate the pipeline
        /// </summary>
        /// <param name="startNode"></param>
        /// <param name="inputResult"></param>
        /// <param name="priority">Thread priority</param>
        public void Start(PipelineNode startNode, Result[] inputResult, int priority)
        {
            IType[] input = null;
            if (inputResult != null) {
                input = new IType[inputResult.Length];
                int i = 0;
                foreach (Result res in inputResult) {
                    input[i] = res.Data;
                    i++;
                }
            }

            if (input == null) {
                return;
            }

            if (input.Length > 0) {
                bool allNull = true;
                foreach (IType i in input) {
                    if (i != null) {
                        allNull = false;
                    }
                }
                if (allNull) {
                    return;
                }
            }

            Dictionary<RequestType, object> requestedData = new Dictionary<RequestType, object>();
            foreach (RequestType request in startNode.algorithm.Request) {
                switch (request) {
                case RequestType.ScanCollection:
                    requestedData.Add(
                        RequestType.ScanCollection,
                        new ScanCollection(project.scanCollection)
                    );

                    break;
                }
            }

            if (!priorizedScheduler.ContainsKey(priority)) {
                priorizedScheduler[priority] = qts.ActivateNewQueue(priority);
            }

            var inputResult2 = inputResult;
            Task startTask = Task<IType[]>.Factory.StartNew((x) => {
                var inputResult1 = inputResult2;
                EventHandler<AlgorithmEventArgs> yieldFun =
                    (object sender, AlgorithmEventArgs e) => GetSingleData(startNode, inputResult1, priority, sender, e);

                startNode.algorithm.SetProgress(0);
                startNode.algorithm.Yielded += yieldFun;
                IType[] output = null;
                try {
                    startNode.algorithm.cancellationToken = cancellationToken;

                    if (!cancellationToken.IsCancellationRequested) {
                        output = startNode.algorithm.Run(
                            requestedData,
                            startNode.algorithm.options.ToArray(),
                            input
                        );
                    }
                } catch (Exception e) {
                    Console.WriteLine(e.StackTrace);
                    Console.WriteLine(e.Message);
                    if (e.InnerException != null) {
                        Console.WriteLine(e.InnerException.Message);
                    }
                    Log.Add(LogLevel.Error, this.GetType().Name,
                        "Failed to process node \"" + startNode + "\"\n\t" + e.Message);
                }
                startNode.algorithm.Yielded -= yieldFun;
                startNode.algorithm.SetProgress(100);

                return output;
            }, cancellationToken, TaskCreationOptions.AttachedToParent)
                .ContinueWith(fromTask => {

                foreach (Result res in inputResult) {
                    res.Finish(startNode);
                }

                IType[] taskOutput = fromTask.Result;
                if (taskOutput != null) { // null means, there is no more data
                    Result[] thisInput = new Result[taskOutput.Length];
                    int i = 0;
                    foreach (IType to in taskOutput) {
                        thisInput[i] = new Result(startNode, to, inputResult, startNode.SaveResult);
                        i++;
                    }
                    OnFinish(startNode, priority, taskOutput, thisInput);
                }
            });
        }
예제 #30
0
        protected override void OnMouseExited(EventArgs args)
        {
            if (mouseMover.Enabled) {
                mouseMover.DisableMouseMover();
                this.Cursor = oldCursor;
            }

            if (currentHoveredNode != null) {
                currentHoveredNode.OnMouseExited();
                currentHoveredNode = null;
            }
        }