/// <summary> /// Adds the cell below row. /// </summary> protected void AddCellBelowRow() { Cell cell = new Cell(Row); // get the value string StringBuilder val = new StringBuilder(); int ltmFactor = 1; CellTreeNode c = Row.Cells.Min; while (c != FirstSelected) { if (!string.IsNullOrEmpty(c.Cell.Reference)) { c = c.Next(); continue; } AddCellValueToAccumulator(c.Cell, Row.Cells.Min.Cell, FirstSelected.Cell, val, ref ltmFactor); c = c.Next(); } val.Append('0').Append(BeatCell.MultiplyTerms(BeatCell.Invert(DrawingView.Instance.GridSpacingString), NumIntervals)); cell.Value = BeatCell.Invert(BeatCell.SimplifyValue(val.ToString())); // does having negative positions like this cause problems? cell.Position = FirstSelected.Cell.Position - DrawingView.Instance.GridSpacing * NumIntervals; Row.Cells.Insert(cell); RightIndexBoundOfTransform = -1; Row.OffsetValue = BeatCell.Subtract(Row.OffsetValue, cell.Value); // don't mult group factor this }
/// <summary> /// Adds the cell to row below selection. /// </summary> protected void AddCellToRowBelowSelection() { Cell cell = new Cell(Row); CellTreeNode cellNode = new CellTreeNode(cell); cell.Position = FirstSelected.Cell.Position - DrawingView.Instance.GridSpacing * NumIntervals; if (Row.Cells.Insert(cellNode)) { Cell below = cellNode.Prev().Cell; RightIndexBoundOfTransform = below.Index; // add to applicable groups AddToGroups(cell, below); // see if the cell is being added to a rep group's LTM zone Repeat repWithLtmToMod = null; foreach (Repeat rg in below.RepeatGroups.Where( x => x.ExclusiveCells.Last.Value == below && Position > below.Position + below.Duration)) { repWithLtmToMod = rg; } // get new value string for below StringBuilder val = new StringBuilder(); var sequence = cell.RepeatGroups .Where(x => !FirstSelected.Cell.RepeatGroups.Contains(x) && x.Cells.First.Value != cell) .Reverse() .Select(x => x.Times); int ltmFactor = sequence.Any() ? sequence.Aggregate((x, y) => x * y) : 1; CellTreeNode c = cellNode; while (c != FirstSelected) { if (!string.IsNullOrEmpty(c.Cell.Reference)) { c = c.Next(); continue; } AddCellValueToAccumulator(c.Cell, cell, FirstSelected.Cell, val, ref ltmFactor); c = c.Next(); } val.Append('0').Append(BeatCell.MultiplyTerms(BeatCell.Invert(DrawingView.Instance.GridSpacingString), NumIntervals)); cell.Value = BeatCell.SimplifyValue(cell.GetValueDividedByMultFactors(BeatCell.Invert(val.ToString()))); string newValue = BeatCell.SimplifyValue(val.ToString()); if (repWithLtmToMod == null) { below.Value = below.GetValueDividedByMultFactors(BeatCell.Add(below.Value, newValue));//newValue); below.Value = BeatCell.SimplifyValue(below.Value); } else { repWithLtmToMod.LastTermModifier = repWithLtmToMod.GetValueDividedByMultFactor( BeatCell.Subtract(repWithLtmToMod.LastTermModifier, newValue)); repWithLtmToMod.LastTermModifier = BeatCell.SimplifyValue(repWithLtmToMod.LastTermModifier); } } }
/** * In order to have mult group resizing: * 1) Adding to a mult group from flat within that group, * - the interval will need to be multiplied by the group factor * - use the cell values that have said group's factor applied * * 2) adding from inside a mult group to outside that gorup * * 3) adding from before a mult group to after that group * * 4) adding from outside the group, into the group * - like normal, use the actualValue inside of mult group. Multiply result by group factor to get * base value. */ /** * Add above row works like this: * - if placed above all other cells, and above all rep group's LTMs, * increase the previous last cell's or rep group LTM's duration * * get the BPM value of the increment multiplied by how many ticks from last selected cell * * to the new cell * * Then subtract the accumulated value of all cells including rep groups from the total value. * make new cell duration the increment value * * Add above selection, within row works like this: * - Get the value of increment times # of ticks between last selected cell and new cell position * - Subtract the accumulated values of all cells including rep groups to get the new value * of the preceding cell OR a rep group's LTM if we are placing the cell inside of the LTM * - The cells value is then the preceding cell's previous value minus it's new value. * * Add below row works like this: * - Get the value of increment times # of ticks between first selected cell and new cell position * - subtract the accumulated values of all cells and groups between selected cell and new cell * to get the value of the new cell. * - Subtract new cell value from row's offset to get the new offset * * add below section, within row works like this: * - Get the increment * # of ticks value between the first selected cell and new cell postion * - subtract the accumulated values of all cells and groups between selected cell and new cell * to get the value of the new cell. * - subtract new cell value from preceding cell / group LTM's old value to get value * */ /** * Test Cases: * * 1) Above row * 2) Above row where last cell is in a repeat * 3) Above row and within the duration of the last cell * 4) Above selection and within row * 5) ^ Where selection is in a repeat and new cell is not * 6) ^ Where selection and new cell are in the same repeat * 7) ^ Where new cell is in a repeat group * 8) ^ Selection is in a repeat group that is nested * 9) ^ new cell is in a repeat group that is nested * 10) Below selection and within row * 11) ^ Where selection is in a repeat and new cell is not * 12) ^ Where selection and new cell are in the same repeat * 13) ^ Where new cell is in a repeat group * 14) ^ Selection is in a repeat group that is nested * 15) ^ new cell is in a repeat group that is nested * 16) Below the row, in offset area * 17) ^ selection is in a repeat group * 18) ^ there is a repeat group between the selection and the start */ /// <summary> /// Adds the cell above row. /// </summary> protected void AddCellAboveRow() { Cell cell = new Cell(Row); if (cell != null) { cell.Value = BeatCell.SimplifyValue(DrawingView.Instance.GridSpacingString); cell.Position = LastSelected.Cell.Position + DrawingView.Instance.GridSpacing * NumIntervals; // set new duration of previous cell Cell below = Row.Cells.Max.Cell; // if add above a reference, just drop it in and exit. if (below.IsReference) { Row.Cells.Insert(cell); return; } // find the value string StringBuilder val = new StringBuilder(); int ltmFactor = LastSelected.Cell.RepeatGroups.Any() ? LastSelected.Cell.RepeatGroups .Reverse() .Select(x => x.Times) .Aggregate((x, y) => x * y) : 1; CellTreeNode c = LastSelected; while (c != null) { if (!string.IsNullOrEmpty(c.Cell.Reference)) { c = c.Next(); continue; } AddCellValueToAccumulator(c.Cell, LastSelected.Cell, cell, val, ref ltmFactor); c = c.Next(); } val.Append('0').Append(BeatCell.MultiplyTerms(BeatCell.Invert(DrawingView.Instance.GridSpacingString), NumIntervals)); string valToAdd = BeatCell.Invert(BeatCell.SimplifyValue(val.ToString())); // if last cell is in a rep group, we need to increase the LTM for that group if (below.RepeatGroups.Any()) { var rg = below.RepeatGroups.First.Value; // add to the bottom repeat group's LTM rg.LastTermModifier = BeatCell.SimplifyValue(rg.GetValueDividedByMultFactor(valToAdd)); } else { // add to last cell's duration below.Value = BeatCell.Add(below.GetValueDividedByMultFactors(valToAdd), below.Value); } Row.Cells.Insert(cell); ChangesViewWidth = true; } }
/// <summary> /// Adds the cell to row above selection. /// </summary> protected void AddCellToRowAboveSelection() { Cell cell = new Cell(Row); CellTreeNode node = new CellTreeNode(cell); cell.Position = LastSelected.Cell.Position + NumIntervals * DrawingView.Instance.GridSpacing; if (Row.Cells.Insert(node)) { CellTreeNode belowNode = node.Prev(); Cell below = belowNode.Cell; RightIndexBoundOfTransform = below.Index + 1; // add to applicable groups AddToGroups(cell, below); // is new cell placed in the LTM zone of a rep group? Repeat repWithLtmToMod = null; foreach (Repeat rg in below.RepeatGroups.Where( x => x.ExclusiveCells.Last.Value == below && Position > below.Position + below.ActualDuration)) { repWithLtmToMod = rg; } // determine new value for the below cell StringBuilder val = new StringBuilder(); var sequence = LastSelected.Cell.RepeatGroups .Where(x => !cell.RepeatGroups.Contains(x) && x.Cells.First.Value != LastSelected.Cell) .Reverse() .Select(x => x.Times); int ltmFactor = sequence.Any() ? sequence.Aggregate((x, y) => x * y) : 1; CellTreeNode c = LastSelected; // we subtract all values up to the "whitespace" below the new cell while (c != node) { if (!string.IsNullOrEmpty(c.Cell.Reference)) { c = c.Next(); continue; } AddCellValueToAccumulator(c.Cell, LastSelected.Cell, node.Cell, val, ref ltmFactor); c = c.Next(); } val.Append('0').Append(BeatCell.MultiplyTerms(BeatCell.Invert(DrawingView.Instance.GridSpacingString), NumIntervals)); // get new cells value by subtracting old value of below cell by new value. string newVal = BeatCell.SimplifyValue(val.ToString()); // placing a new cell on the beginning of a LTM is not illegal if (repWithLtmToMod != null && newVal == string.Empty) { newVal = "0"; } // assign the new cell's value cell.Value = BeatCell.SimplifyValue(cell.GetValueDividedByMultFactors(newVal)); if (repWithLtmToMod == null) { // change below cell's value below.Value = below.GetValueDividedByMultFactors( BeatCell.Subtract(below.Value, newVal)); below.Value = BeatCell.SimplifyValue(below.Value); if (below.IsBreak) { below.IsBreak = false; cell.IsBreak = true; } } else { // changing a LTM value repWithLtmToMod.LastTermModifier = BeatCell.SimplifyValue( repWithLtmToMod.GetValueDividedByMultFactor( BeatCell.Subtract(repWithLtmToMod.LastTermModifier, newVal))); } } }
public RemoveCells(CellTree cells) : base(cells.Root.Cell.Row, cells.Count > 1 ? "Remove Cells" : "Remove Cell") { Cells = cells; //Row = cells.Root.Cell.Row; //PreviousCellValue = previousCellValue; //Index = cells[0].Row.Cells.IndexOf(cells[0]); StartNode = Row.Cells.LookupIndex(cells.Min.Cell.Index); EndNode = Row.Cells.LookupIndex(cells.Max.Cell.Index); StringBuilder duration = new StringBuilder(); // find all groups that are encompassed by the selection HashSet <AbstractGroup> touchedGroups = new HashSet <AbstractGroup>(); Repeat groupBeingAppendedTo = null; // a group who's LTM is actively being augmented Queue <Repeat> rgToAppendTo = new Queue <Repeat>(); // RGs that may need to have their LTM added to //LinkedList<AbstractGroup> openedGroups = new LinkedList<AbstractGroup>(); //LinkedList<AbstractGroup> closedGroups = new LinkedList<AbstractGroup>(); foreach (Cell c in Cells) { //if (!string.IsNullOrEmpty(c.Reference)) continue; foreach ((bool begun, AbstractGroup group) in c.GroupActions) { if (begun) { OpenedGroups.AddFirst(group); } else { ClosedGroups.AddFirst(group); } } // add to the LTM of groups with a previous cell in the selection but not this cell if (rgToAppendTo.Any() && !c.RepeatGroups.Contains(rgToAppendTo.Peek())) { groupBeingAppendedTo = rgToAppendTo.Dequeue(); } if (groupBeingAppendedTo != null) { groupBeingAppendedTo.LastTermModifier = BeatCell.Add(groupBeingAppendedTo.LastTermModifier, c.Value); } int times = 1; // times this cell gets repeated // track the times that each RG's LTM gets repeated Dictionary <Repeat, int> lcmTimes = new Dictionary <Repeat, int>(); foreach (Repeat rg in c.RepeatGroups.Reverse()) { // remove cell from group rg.ExclusiveCells.Remove(c); rg.Cells.Remove(c); // remove break cells if (rg.BreakCell == c) { rg.BreakCell = null; } if (touchedGroups.Contains(rg)) { continue; } rgToAppendTo.Enqueue(rg); touchedGroups.Add(rg); if ( (StartNode.Cell == rg.ExclusiveCells.First?.Value || rg.Position > StartNode.Cell.Position) && (EndNode.Cell == rg.ExclusiveCells.Last?.Value || rg.Position + rg.Length < EndNode.Cell.Position)) { RepGroups.Add(rg); bool cellAboveBreak = rg.BreakCell != null && c.Position > rg.BreakCell.Position; times *= rg.Times - (cellAboveBreak ? 1 : 0); // multiply all nested rgs' LTMs by this groups repeat times. foreach (KeyValuePair <Repeat, int> kv in lcmTimes) { bool aboveBreak = rg.BreakCell != null && kv.Key.Position > rg.BreakCell.Position; lcmTimes[kv.Key] *= rg.Times - (aboveBreak ? 1 : 0); } lcmTimes.Add(rg, 1); } } foreach (Multiply mg in c.MultGroups) { // remove cell from group mg.ExclusiveCells.Remove(c); if (touchedGroups.Contains(mg)) { continue; } touchedGroups.Add(mg); if ( (StartNode.Cell == mg.ExclusiveCells.First.Value || mg.Position > StartNode.Cell.Position) && (EndNode.Cell == mg.ExclusiveCells.Last.Value || mg.Position + mg.Length < EndNode.Cell.Position + EndNode.Cell.Duration)) { MultGroups.Add(mg); } } // get the double version of duration Duration += c.Duration * times; // get the string version of duration // add cell's repeat durations if this cell is in the same scope as the first cell. if ((!c.RepeatGroups.Any() && !StartNode.Cell.RepeatGroups.Any()) || c.RepeatGroups.Last?.Value == StartNode.Cell.RepeatGroups.Last?.Value) { duration.Append("+0").Append(BeatCell.MultiplyTerms(c.Value, times)); } // add any LTM's from repeat groups foreach (KeyValuePair <Repeat, int> kv in lcmTimes) { duration.Append("+0").Append(BeatCell.MultiplyTerms(kv.Key.LastTermModifier, kv.Value)); Duration += BeatCell.Parse(kv.Key.LastTermModifier) * kv.Value; } } // Transfer group actions from deleted cells to the 2 cells outside the deleted group //CellTreeNode after = EndNode.Next(); //CellTreeNode before = StartNode.Prev(); // //if (after != null) //{ // foreach (AbstractGroup group in openedGroups) // { // if (group.ExclusiveCells.Count > 0) // { // after.Cell.GroupActions.AddFirst((true, group)); // } // } //} // //if (before != null) //{ // foreach (AbstractGroup group in closedGroups) // { // if (group.ExclusiveCells.Count > 0) // { // before.Cell.GroupActions.AddFirst((false, group)); // } // } //} BeatCodeDuration = BeatCell.SimplifyValue(duration.ToString()); }