/// <summary> /// Look up the cell at the specified BPM position. /// </summary> /// <returns>The up.</returns> /// <param name="value">Value.</param> /// <param name="useThickness">If set to <c>true</c> use thickness.</param> public CellTreeNode Lookup(double value, bool useThickness = true, double thickness = DrawingView.CellWidth) { CellTreeNode node = Root; thickness /= DrawingView.ScalingFactor; while (node != null) { if (node.Cell.Position + (useThickness ? thickness : 0) >= value && value >= node.Cell.Position) { return(node); } if (node.Cell.Position < value) { node = node.Right; } else { node = node.Left; } } return(null); }
/// <summary> /// Clear this instance. /// </summary> public void Clear() { Root = null; Min = null; Max = null; Count = 0; }
/// <summary> /// Rotate such that if node was the root, node's right child will now be root /// </summary> /// <param name="node">Node.</param> protected void RotateLeft(CellTreeNode node) { var rightLeft = node.Right.Left; if (Root != node) { if (node.Parent.Left == node) { node.Parent.Left = node.Right; } else { node.Parent.Right = node.Right; } } else { Root = node.Right; } node.Right.Parent = node.Parent; node.Right.Left = node; node.Parent = node.Right; node.Right = rightLeft; if (rightLeft != null) { rightLeft.Parent = node; } }
/// <summary> /// Rotate such that if node was root, node's left child will now be root /// </summary> /// <param name="node">Node.</param> protected void RotateRight(CellTreeNode node) { var leftRight = node.Left.Right; if (Root != node) { if (node.Parent.Left == node) { node.Parent.Left = node.Left; } else { node.Parent.Right = node.Left; } } else { Root = node.Left; } node.Left.Parent = node.Parent; node.Left.Right = node; node.Parent = node.Left; node.Left = leftRight; if (leftRight != null) { leftRight.Parent = node; } }
/// <summary> /// Finds the node at bpm position below or equal to value. /// </summary> /// <returns>The below or equal to.</returns> /// <param name="value">Value.</param> public CellTreeNode FindBelowOrEqualTo(double value) { CellTreeNode node = Root; CellTreeNode result = null; if (node == null) { return(null); } while (node != null) { if (node.Cell.Position <= value) { result = node; if (node.Cell.Position == value) { break; } } if (node.Cell.Position > value) { node = node.Left; } else { node = node.Right; } } return(result); }
/// <summary> /// Replace the specified node with the given replacement and remove original node from the tree. /// </summary> /// <returns>The replace.</returns> /// <param name="node">Node.</param> /// <param name="replacement">Replacement.</param> protected void Replace(CellTreeNode node, CellTreeNode replacement) { if (node.Parent == null) { Root = replacement; if (replacement != null) { replacement.Parent = null; } } else { if (node.Parent.Left == node) { node.Parent.Left = replacement; } else { node.Parent.Right = replacement; } if (replacement != null) { replacement.Parent = node.Parent; } } }
public bool TryFind(double value, out Cell cell, bool useThickness = true) { CellTreeNode node = Lookup(value, useThickness); cell = node?.Cell; return(cell != null); }
/// <summary> /// Find all cells inside the given range. /// </summary> /// <returns>The range.</returns> /// <param name="start">Start.</param> /// <param name="end">End.</param> public CellTreeNode[] GetRange(double start, double end) { List <CellTreeNode> cells = new List <CellTreeNode>(); CellTreeNode cell = FindAboveOrEqualTo(start); while (cell != null && cell.Cell.Position <= end) { cells.Add(cell); cell = cell.Next(); } return(cells.ToArray()); }
public IEnumerator GetEnumerator() { CellTreeNode node = Min; if (node == null) { yield break; } while (node != null) { yield return(node.Cell); node = node.Next(); } yield break; }
/// <summary> /// Finds a node with the given cell index. /// </summary> /// <returns>The index.</returns> /// <param name="index">Index.</param> public CellTreeNode LookupIndex(int index) { CellTreeNode node = Root; while (node != null) { if (node.Cell.Index == index) { return(node); } if (node.Cell.Index < index) { node = node.Right; } else { node = node.Left; } } return(null); }
/// <summary> /// Find the first cell above or equal to the given BPM position. /// </summary> /// <returns>The above or equal to.</returns> /// <param name="value">Value.</param> public CellTreeNode FindAboveOrEqualTo(double value, bool useThickness = false) { CellTreeNode node = Root; CellTreeNode result = null; if (node == null) { return(null); } double thickness = useThickness ? DrawingView.CellWidth / DrawingView.ScalingFactor : 0; while (node != null) { if (node.Cell.Position + thickness >= value) { result = node; if (node.Cell.Position + thickness == value) { break; } } if (node.Cell.Position + thickness < value) { node = node.Right; } else { node = node.Left; } } return(result); }
public void CopyTo(CellTreeNode node) { node.Cell = Cell; }
public virtual void Undo() { // if no change, don't do anything if (AfterBeatCode == BeforeBeatCode) { return; } CellTree selectedCells = EditorViewController.Instance.DView.SelectedCells; // get current selection range if it's in this row int selectionStart = -1; int selectionEnd = -1; // get selection indexes if there is a selection in the action's row if (selectedCells.Root != null && selectedCells.Root.Cell.Row == Row) { // find index of first selected cell selectionStart = selectedCells.Min.Cell.Index; selectionEnd = selectedCells.Max.Cell.Index; } bool selectFromBack = selectionEnd > RightIndexBoundOfTransform; if (selectFromBack) { // get index from back of list selectionStart = Row.Cells.Count - selectionStart; selectionEnd = Row.Cells.Count - selectionEnd; } Row.FillFromBeatCode(BeforeBeatCode); if (BeforeOffset != AfterOffset) { Row.OffsetValue = BeforeOffset; Row.Offset = BeatCell.Parse(BeforeOffset); } if (ChangesViewWidth) { double maxDur = DrawingView.Instance.Rows.Max(x => x.Duration); DrawingView.Instance.ResizeFrame(maxDur, false); } else { EditorViewController.Instance.DView.QueueRowToDraw(Row); RedrawReferencers(); } EditorViewController.Instance.DView.ChangesApplied = false; // would be nice to only draw individual rows, but seems to be a problem //DrawingView.Instance.NeedsDisplay = true; if (selectionStart > -1) { if (selectFromBack) { // convert back to forward indexed selectionStart = Row.Cells.Count - selectionStart; selectionEnd = Row.Cells.Count - selectionEnd; } if (selectionStart < 0) { selectionStart = 0; } if (selectionEnd >= Row.Cells.Count) { selectionEnd = Row.Cells.Count - 1; } // make new selection CellTreeNode startNode = Row.Cells.LookupIndex(selectionStart); CellTreeNode endNode = Row.Cells.LookupIndex(selectionEnd); EditorViewController.Instance.DView.SelectCell(startNode.Cell); if (startNode != endNode) { EditorViewController.Instance.DView.SelectCell(endNode.Cell, true); } } }
public CellTree Remove(double value) { CellTreeNode node = Lookup(value, false); return(Remove(node)); }
public CellTree Remove(CellTreeNode node) { CellTreeNode nodeToDeleteAfterwards = null; if (node == Min) { Min = node.Next(); } else if (node == Max) { Max = node.Prev(); } if (node.Left == null && node.Right == null) { if (Root == node) { Root = null; Min = null; Max = null; return(this); } if (node.IsRed) { Replace(node, null); return(this); } // black node with no children, not root // we will delete the node afterwards. it's a 'phantom' leaf. nodeToDeleteAfterwards = node; // do we need to track the actual node, as it may be changed by a rule? } else if (node.Left != null && node.Right != null) { // we find the left-most child of right side and copy it's value to the node being deleted // The node that we copied from (left-most) can then by deleted because it // has at most 1 non-leaf child. var succesor = node.Left; while (succesor.Right != null) { succesor = succesor.Right; } // copy data from succesor node succesor.CopyTo(node); //Swap(node, succesor); return(Remove(succesor)); } if (node.Left != null || node.Right != null) { // one child is a non-leaf var child = node.Left ?? node.Right; if (!node.IsRed && child.IsRed) { // black node, red child Replace(node, child); child.IsRed = false; return(this); } if (node.IsRed && !child.IsRed) { // red node, black child Replace(node, child); return(this); } // black node with black child Replace(node, child); node = child; } while (true) { // node is now a "double black" node if (node == Root) { break; // case 1. terminal case } var sibling = node.GetSibling(); if (sibling.IsRed) { // case 2 // node is black, has black parent, and a red sibling // make sibling take the place of the parent. if (node.Parent.Left == node) { RotateLeft(node.Parent); } else { RotateRight(node.Parent); } node.Parent.IsRed = true; sibling.IsRed = false; sibling = node.GetSibling(); } else if (!node.Parent.IsRed && !sibling.IsRed && (sibling.Left == null || !sibling.Left.IsRed) && (sibling.Right == null || !sibling.Right.IsRed)) { // case 3 // node is black, black parent, black sibling with black children sibling.IsRed = true; node = node.Parent; // go to start with parent as new node. (we pushed the double black status up to the parent) continue; } else if (node.Parent.IsRed && (sibling.Left == null || !sibling.Left.IsRed) && (sibling.Right == null || !sibling.Right.IsRed)) { // case 4, terminal case node.Parent.IsRed = false; sibling.IsRed = true; break; } else if (!sibling.IsRed && node.HasSiblingWithInnerRedChild()) { // case 5 // black node, black parent, black sibling with inner red child if (node.Parent.Left == node) { RotateRight(sibling); sibling.Left.IsRed = false; } else { RotateLeft(sibling); sibling.Right.IsRed = false; } sibling.IsRed = true; sibling = node.GetSibling(); } if (!sibling.IsRed && node.HasSiblingWithOuterRedChild()) { // case 6, terminal case // black node, don't care parent's color, black sibling with outer red child and inner child with either color. if (node.Parent.Left == node) { RotateLeft(node.Parent); sibling.Right.IsRed = false; } else { RotateRight(node.Parent); sibling.Left.IsRed = false; } sibling.IsRed = node.Parent.IsRed; node.Parent.IsRed = false; // node is no longer a bouble black. break; } } if (nodeToDeleteAfterwards != null) { Replace(nodeToDeleteAfterwards, null); } Count--; return(this); }
public bool Insert(CellTreeNode node) { node.IsRed = true; CellTreeNode parent = Root; if (Root == null) { Root = node; Min = node; Max = node; } else { bool isMin = true; bool isMax = true; while (true) { if (parent.Cell.Position > node.Cell.Position) { isMax = false; if (parent.Left == null) { parent.Left = node; break; } parent = parent.Left; } else { isMin = false; if (parent.Cell.Position == node.Cell.Position) { // no overlapping return(false); } if (parent.Right == null) { parent.Right = node; break; } parent = parent.Right; } } if (isMin) { Min = node; } else if (isMax) { Max = node; } node.Parent = parent; } // operate on tree to assert red-black properties while (node != null) { //var parent = node.Parent; // step 1 if (parent == null) { node.IsRed = false; break; } // step 2 if (!parent.IsRed) { break; } var uncle = node.GetUncle(); var gp = node.GetGrandParent(); // step 3 // check if both parent and uncle are red if (uncle != null && uncle.IsRed) { node.Parent.IsRed = uncle.IsRed = false; gp.IsRed = true; node = gp; parent = gp.Parent; continue; // recurse with GP because it's now red } // step 4 if (gp.Left != null && gp.Left.Right == node) { // rotate RotateLeft(parent); // parent is new node, will be target of step 5 node = parent; parent = node.Parent; } else if (gp.Right != null && gp.Right.Left != null && gp.Right.Left == node) { // mirror of above RotateRight(parent); node = parent; parent = node.Parent; } // step 5 // parent becomes GP if (gp.Left != null && gp.Left.Left != null && gp.Left.Left == node) { RotateRight(gp); parent.IsRed = false; gp.IsRed = true; } else { // mirror of above RotateLeft(gp); parent.IsRed = false; gp.IsRed = true; } break; } Count++; return(true); }
public virtual void Redo() { CellTree selectedCells = EditorViewController.Instance.DView.SelectedCells; // get current selection range if it's in this row int selectionStart = -1; int selectionEnd = -1; int rowLengthBefore = Row.Cells.Count; // get selection indexes if there is a selection in the action's row if (selectedCells.Root != null && selectedCells.Root.Cell.Row == Row) { // find index of first selected cell selectionStart = selectedCells.Min.Cell.Index; selectionEnd = selectedCells.Max.Cell.Index; } if (string.IsNullOrEmpty(AfterBeatCode)) { // perform the transform and get the new beat code Transformation(); AfterBeatCode = Row.Stringify(); AfterOffset = Row.OffsetValue; } bool selectFromBack = selectionEnd > RightIndexBoundOfTransform; if (selectFromBack) { // get index from back of list selectionStart = rowLengthBefore - selectionStart; selectionEnd = rowLengthBefore - selectionEnd; } Row.FillFromBeatCode(AfterBeatCode); if (BeforeOffset != AfterOffset) { Row.OffsetValue = AfterOffset; Row.Offset = BeatCell.Parse(AfterOffset); } if (ChangesViewWidth) { double maxDur = DrawingView.Instance.Rows.Max(x => x.Duration); // change the view's width //var curFrame = DrawingView.Instance.Frame; //curFrame.Width = (System.nfloat)(maxDur * DrawingView.ScalingFactor + 550); //DrawingView.Instance.Frame = curFrame; // need to draw the end portion of other rows if (maxDur == Row.Duration) { DrawingView.Instance.ResizeFrame(maxDur); } else { ChangesViewWidth = false; } } DrawingView.Instance.QueueRowToDraw(Row); DrawingView.Instance.ChangesApplied = false; RedrawReferencers(); if (selectionStart > -1) { if (selectFromBack) { // convert back to forward indexed selectionStart = Row.Cells.Count - selectionStart; selectionEnd = Row.Cells.Count - selectionEnd; } if (selectionStart < 0) { selectionStart = 0; } if (selectionEnd >= Row.Cells.Count) { selectionEnd = Row.Cells.Count - 1; } // make new selection CellTreeNode startNode = Row.Cells.LookupIndex(selectionStart); CellTreeNode endNode = Row.Cells.LookupIndex(selectionEnd); if (startNode != null && endNode != null) { EditorViewController.Instance.DView.SelectCell(startNode.Cell); if (startNode != endNode) { EditorViewController.Instance.DView.SelectCell(endNode.Cell, true); } } } }