/// <summary> /// Adds the cell to the other cell's groups when applicable /// </summary> /// <param name="cell">Cell.</param> /// <param name="below">Below.</param> private static void AddToGroups(Cell cell, Cell below) { foreach (Repeat rg in below.RepeatGroups.Where(x => x.ActualPosition + x.Length > cell.Position)) { //rg.Cells.Last.Value.GroupActions.Remove(); cell.RepeatGroups.AddLast(rg); // transfer actions if below was last cell of group if (rg.ExclusiveCells.Last.Value == below) { below.GroupActions.Remove((false, rg)); cell.GroupActions.AddLast((false, rg)); } } foreach (Multiply mg in below.MultGroups.Where(x => x.ActualPosition + x.Length > cell.Position)) { cell.MultGroups.AddLast(mg); if (UserSettings.GetSettings().DrawMultToScale) { cell.MultFactor = BeatCell.MultiplyTerms(cell.MultFactor, mg.FactorValue); } if (mg.ExclusiveCells.Last.Value == below) { below.GroupActions.Remove((false, mg)); cell.GroupActions.AddLast((false, mg)); } } }
/// <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> /// Gets the ltm with mult factor. /// </summary> /// <returns>The ltm with mult factor.</returns> public string GetLtmWithMultFactor(bool ignoreSettings = false) { if (string.IsNullOrEmpty(LastTermModifier) || (!ignoreSettings && !UserSettings.GetSettings().DrawMultToScale)) { return(LastTermModifier); } if (string.IsNullOrEmpty(MultedLtm)) { MultedLtm = BeatCell.MultiplyTerms(LastTermModifier, MultFactor); } return(MultedLtm); }
/// <summary> /// Add a cell's value to the accumulator the correct number of times /// </summary> /// <param name="target"></param> /// <param name="start"></param> /// <param name="end"></param> /// <param name="accumulator"></param> /// <param name="ltmFactor"></param> /// <param name="recursing"></param> protected void AddCellValueToAccumulator(Cell target, Cell start, Cell end, StringBuilder accumulator, ref int ltmFactor, bool recursing = false) { // subtract each value from the total if (!target.RepeatGroups.Any()) { accumulator.Append(target.GetValueWithMultFactors()).Append('+'); return; } int timesDiff = 1; int ltmTimesDiff = 1; bool isBehind = target.RepeatGroups.First.Value.Position + target.RepeatGroups.First.Value.FullDuration < start.Position; bool contains = !isBehind; int times = 1; foreach (Repeat rg in target.RepeatGroups.TakeWhile(x => !end.RepeatGroups.Contains(x))) // iterate from innermost group { if (recursing) { if (contains && rg.ExclusiveCells.Contains(start)) { // this is the times to subtract because they occur before the starting point. timesDiff = times; } else if (isBehind && rg.Cells.Contains(start)) { // subtract a full cycle if this rep group exists all behind the target ltmTimesDiff = timesDiff = times; ltmTimesDiff /= target.RepeatGroups.First().Times; isBehind = false; } } // break cell(s) may decrease the factor if (rg.BreakCell != null) { times *= rg.Times - (target == rg.BreakCell || target.Position < rg.BreakCell.Position ? 0 : 1); } else { times *= rg.Times; } if (contains && recursing && rg.ExclusiveCells.Contains(start)) { ltmTimesDiff = times; ltmTimesDiff /= target.RepeatGroups.First().Times; } } // handle LTMs foreach ((bool opens, AbstractGroup rg) in target.GroupActions.Where(x => x.Item2 is Repeat && !end.RepeatGroups.Contains(x.Item2))) { if (!opens) { ltmFactor /= ((Repeat)rg).Times; // subtract out the LTM (if group doesn't contain the end point) if (!string.IsNullOrEmpty((rg as Repeat).LastTermModifier)) { accumulator.Append( BeatCell.MultiplyTerms( ((Repeat)rg).GetLtmWithMultFactor(), ltmFactor - (recursing ? ltmTimesDiff : 0))).Append('+'); } } else if (!end.RepeatGroups.Contains(rg)) { ltmFactor *= ((Repeat)rg).Times; } } // account for preceding cells if we are starting mid-way through a rep group if (recursing) { times -= timesDiff; } else if (target == start) { // find outermost rep group that doesn't contain the new cell Repeat rg = target.RepeatGroups.Reverse().SkipWhile(x => end.RepeatGroups.Contains(x)).FirstOrDefault(); if (rg != null) { int ltmFactorR = 1;//rg.Cells.First.Value == target ? 1 : rg.Times; foreach (Cell c in rg.Cells.TakeWhile(x => x.Position < target.Position)) { AddCellValueToAccumulator(c, start, end, accumulator, ref ltmFactorR, true); } } } if (!string.IsNullOrEmpty(target.Value)) { accumulator.Append(BeatCell.MultiplyTerms(target.GetValueWithMultFactors(), times)).Append('+'); } }
/// <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); } } }
/// <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))); } } }
/** * 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; } }
protected override void Transformation() { string value = BeatCell.MultiplyTerms(Increment, Math.Abs(Times)); Cell last = Cells[Cells.Length - 1]; Cell first = Cells[0]; if (Row.Cells.Min.Cell == Cells[0]) { // selection is at start of row, offset will be changed if (ShiftingRight) { // add to offset if (string.IsNullOrEmpty(Row.OffsetValue)) { Row.OffsetValue = "0"; } Row.OffsetValue = BeatCell.Add(Row.OffsetValue, first.GetValueDividedByMultFactors(value)); // subtract from last cell's value if not last cell of row if (last != Row.Cells.Max.Cell) { last.Value = BeatCell.Subtract(last.Value, last.GetValueDividedByMultFactors(value)); } } else { // subtract from offset Row.OffsetValue = BeatCell.Subtract(Row.OffsetValue, first.GetValueDividedByMultFactors(value)); // zero becomes an empty string, make it zero. if (string.IsNullOrEmpty(Row.OffsetValue)) { Row.OffsetValue = "0"; } // add to last cell's value if not last cell of row if (last != Row.Cells.Max.Cell) { last.Value = BeatCell.Add(last.Value, last.GetValueDividedByMultFactors(value)); } } } else { Cell below = Row.Cells.LookupIndex(Cells[0].Index - 1).Cell; // if below is last cell of a repeat group, we instead operate on that group's LTM Repeat leftGroup = below.RepeatGroups.Where(x => x.ExclusiveCells.Last.Value == below).FirstOrDefault(); bool useLeftGroup = leftGroup != default(Repeat); // if last cell in selection is last of a repeat group, operate on it's LTM Repeat rightGroup = last.RepeatGroups.Where(x => x.ExclusiveCells.Last.Value == last).FirstOrDefault(); bool useRightGroup = rightGroup != default(Repeat); if (ShiftingRight) { if (Cells.Last() == Row.Cells.Max.Cell) { ChangesViewWidth = true; } if (useLeftGroup) { // add to LTM leftGroup.LastTermModifier = BeatCell.Add(leftGroup.LastTermModifier, leftGroup.GetValueDividedByMultFactor(value)); } else { // add to below cell's value below.Value = BeatCell.Add(below.Value, below.GetValueDividedByMultFactors(value)); } // subtract from last cell's value if not last of row if (last != Row.Cells.Max.Cell) { if (useRightGroup) { // subtract from LTM rightGroup.LastTermModifier = BeatCell.Subtract(rightGroup.LastTermModifier, rightGroup.GetValueDividedByMultFactor(value)); } else { last.Value = BeatCell.Subtract(last.Value, last.GetValueDividedByMultFactors(value)); } } } else { if (useLeftGroup) { // subtract from LTM leftGroup.LastTermModifier = BeatCell.Subtract(leftGroup.LastTermModifier, leftGroup.GetValueDividedByMultFactor(value)); } else { // subtract from below cell's value below.Value = BeatCell.Subtract(below.Value, below.GetValueDividedByMultFactors(value)); } // add to last cell's value if not last in row if (last != Row.Cells.Max.Cell) { if (useRightGroup) { rightGroup.LastTermModifier = BeatCell.Add(rightGroup.LastTermModifier, rightGroup.GetValueDividedByMultFactor(value)); } else { last.Value = BeatCell.Add(last.Value, last.GetValueDividedByMultFactors(value)); } } } } Cells = null; }
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()); }