Skip to content

Commit

Permalink
锚点添加与移动
Browse files Browse the repository at this point in the history
  • Loading branch information
LiuYunPlayer committed Jul 4, 2024
1 parent b23b67d commit e087228
Show file tree
Hide file tree
Showing 4 changed files with 459 additions and 37 deletions.
17 changes: 14 additions & 3 deletions TuneLab/Data/IPiecewiseCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ internal interface IPiecewiseCurve : IDataObject<IEnumerable<IReadOnlyCollection
double[] GetValues(IReadOnlyList<double> ticks);
void AddLine(IReadOnlyList<AnchorPoint> points, double extend);
void Clear(double start, double end);
void DeleteAnchors(double start, double end);
void InsertPoint(AnchorPoint point);
void DeletePoints(double start, double end);
void DeletePoints(IReadOnlyList<AnchorPoint> points);
void DeleteAllSelectedAnchors();
void RemoveAnchorGroupAt(int index);
void DeleteAnchorGroupAt(int index);
void ConnectAnchorGroup(int leftIndex);
void MoveSelectedPoints(double offsetPos, double offsetValue);

new List<List<Point>> GetInfo();

Expand Down Expand Up @@ -97,11 +100,19 @@ public static AreaID GetAreaID(this IPiecewiseCurve piecewiseCurve, double pos)
}

if (anchorGroup.IsEmpty())
piecewiseCurve.RemoveAnchorGroupAt(gi);
piecewiseCurve.DeleteAnchorGroupAt(gi);
}
return result;
}

public static void SelectAllAnchors(this IPiecewiseCurve piecewiseCurve)
{
foreach (var anchorGroup in piecewiseCurve.AnchorGroups)
{
anchorGroup.SelectAllItems();
}
}

public static void DeselectAllAnchors(this IPiecewiseCurve piecewiseCurve)
{
foreach (var anchorGroup in piecewiseCurve.AnchorGroups)
Expand Down
296 changes: 293 additions & 3 deletions TuneLab/Data/PiecewiseCurve.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net.Security;
using System.Reflection;
using System.Text;
using TuneLab.Base.Data;
Expand Down Expand Up @@ -166,7 +168,7 @@ public void Clear(double start, double end)
Push(new RedoOnlyCommand(NotifyRangeModified));
}

public void RemoveAnchorGroupAt(int index)
public void DeleteAnchorGroupAt(int index)
{
var anchorGroup = mAnchorGroups[index];
var start = anchorGroup.Start;
Expand All @@ -178,7 +180,7 @@ public void RemoveAnchorGroupAt(int index)
Push(new RedoOnlyCommand(NotifyRangeModified));
}

public void DeleteAnchors(double s, double e)
public void DeletePoints(double s, double e)
{
for (int gi = AnchorGroups.Count - 1; gi >= 0; gi--)
{
Expand Down Expand Up @@ -301,6 +303,294 @@ public void DeletePoints(IReadOnlyList<AnchorPoint> points)
}
}

public void InsertPoint(AnchorPoint point)
{
double start = point.Pos;
double end = point.Pos;
void NotifyRangeModified() => mRangeModified.Invoke(start, end);
Push(new UndoOnlyCommand(NotifyRangeModified));
var areaID = this.GetAreaID(point.Pos);
if (areaID.IsInGroup)
{
var anchorGroup = mAnchorGroups[areaID.Index];

int pi = anchorGroup.Count - 1;
for (; pi >= 0; pi--)
{
var anchor = anchorGroup[pi];
if (anchor.Pos > point.Pos)
continue;

if (anchor.Pos == point.Pos)
anchorGroup.RemoveAt(pi);
else
pi++;

break;
}

anchorGroup.Insert(pi, point);
start = anchorGroup[(pi - 1).Limit(0, anchorGroup.Count - 1)].Pos;
end = anchorGroup[(pi + 1).Limit(0, anchorGroup.Count - 1)].Pos;
}
else
{
var leftHasSelectedAnchor = (uint)areaID.LeftIndex < mAnchorGroups.Count && mAnchorGroups[areaID.LeftIndex].HasSelectedItem();
var rightHasSelectedAnchor = (uint)areaID.RightIndex < mAnchorGroups.Count && mAnchorGroups[areaID.RightIndex].HasSelectedItem();
if (leftHasSelectedAnchor)
{
var anchorGroup = mAnchorGroups[areaID.LeftIndex];
anchorGroup.Add(point);
start = anchorGroup[(anchorGroup.Count - 2).Limit(0, anchorGroup.Count - 1)].Pos;
end = anchorGroup[(anchorGroup.Count - 1).Limit(0, anchorGroup.Count - 1)].Pos;
if (rightHasSelectedAnchor)
{
ConnectAnchorGroup(areaID.LeftIndex);
}
}
else
{
if (rightHasSelectedAnchor)
{
var anchorGroup = mAnchorGroups[areaID.RightIndex];
anchorGroup.Insert(0, point);
start = anchorGroup[0.Limit(0, anchorGroup.Count - 1)].Pos;
end = anchorGroup[1.Limit(0, anchorGroup.Count - 1)].Pos;
}
else
{
var anchorGroup = new T();
anchorGroup.Add(point);
mAnchorGroups.Insert(areaID.RightIndex, anchorGroup);
}
}
}

NotifyRangeModified();
Push(new RedoOnlyCommand(NotifyRangeModified));
}

void InsertPointsToGroup(T anchorGroup, IReadOnlyList<AnchorPoint> points)
{
if (points.IsEmpty())
return;

double start = anchorGroup.IsEmpty() ? double.PositiveInfinity : anchorGroup.End;
double end = anchorGroup.IsEmpty() ? double.NegativeInfinity : anchorGroup.Start;
bool hasDeleteAnchor = false;
void NotifyRangeModified() => mRangeModified.Invoke(start, end);

if (anchorGroup.IsEmpty())
{
start = points.ConstFirst().Pos;
start = points.ConstLast().Pos;
Push(new UndoOnlyCommand(NotifyRangeModified));
anchorGroup.Set(points);
NotifyRangeModified();
Push(new RedoOnlyCommand(NotifyRangeModified));
return;
}

int pi = anchorGroup.Count - 1;
for (int i = points.Count - 1; i >= 0; i--)
{
var anchor = points[i];
while (pi >= 0 && anchor.Pos < anchorGroup[pi].Pos)
{
pi--;
}

void Insert(int index)
{
if (!hasDeleteAnchor)
{
Push(new UndoOnlyCommand(NotifyRangeModified));
hasDeleteAnchor = true;
}

anchorGroup.Insert(index, anchor);
end = Math.Max(end, anchorGroup[(index + 1).Limit(0, anchorGroup.Count - 1)].Pos);
start = Math.Min(start, anchorGroup[(index - 1).Limit(0, anchorGroup.Count - 1)].Pos);
}

if (pi >= 0 && anchorGroup[pi].Pos == anchor.Pos)
{
anchorGroup.RemoveAt(pi);
Insert(pi);
}
else
{
Insert(pi + 1);
}
}
if (hasDeleteAnchor)
{
NotifyRangeModified();
Push(new RedoOnlyCommand(NotifyRangeModified));
}
}

public void ConnectAnchorGroup(int leftIndex)
{
if ((uint)leftIndex >= mAnchorGroups.Count - 1)
return;

var leftAnchorGroup = mAnchorGroups[leftIndex];
var rightAnchorGroup = mAnchorGroups[leftIndex + 1];

double start = leftAnchorGroup[(leftAnchorGroup.Count - 2).Limit(0, leftAnchorGroup.Count - 1)].Pos;
double end = rightAnchorGroup[1.Limit(0, rightAnchorGroup.Count - 1)].Pos;
void NotifyRangeModified() => mRangeModified.Invoke(start, end);
Push(new UndoOnlyCommand(NotifyRangeModified));

for (int i = rightAnchorGroup.Count - 1; i >= 0; i--)
{
var anchor = rightAnchorGroup[0];
rightAnchorGroup.RemoveAt(0);
leftAnchorGroup.Add(anchor);
}
mAnchorGroups.RemoveAt(leftIndex + 1);

NotifyRangeModified();
Push(new RedoOnlyCommand(NotifyRangeModified));
}

public void MoveSelectedPoints(double offsetPos, double offsetValue)
{
void MoveSelectedPointsFromAnchorGroupTo(int gi, double offsetPos, double offsetValue)
{
double leftSide = gi == 0 ? double.NegativeInfinity : mAnchorGroups[gi - 1].End;
double rightSide = gi == mAnchorGroups.Count - 1 ? double.PositiveInfinity : mAnchorGroups[gi + 1].Start;
var anchorGroup = mAnchorGroups[gi];
var selectedAnchors = new System.Collections.Generic.LinkedList<AnchorPoint>();
double start = anchorGroup.End;
double end = anchorGroup.Start;
bool hasSelectedAnchor = false;
void NotifyRangeModified() => mRangeModified.Invoke(start, end);
for (int pi = anchorGroup.Count - 1; pi >= 0; pi--)
{
if (anchorGroup[pi].IsSelected)
{
if (!hasSelectedAnchor)
{
Push(new UndoOnlyCommand(NotifyRangeModified));
hasSelectedAnchor = true;
}

end = Math.Max(end, anchorGroup[(pi + 1).Limit(0, anchorGroup.Count - 1)].Pos);
start = Math.Min(start, anchorGroup[(pi - 1).Limit(0, anchorGroup.Count - 1)].Pos);
selectedAnchors.AddFirst(anchorGroup[pi]);
anchorGroup.RemoveAt(pi);
}
}
if (anchorGroup.IsEmpty())
mAnchorGroups.RemoveAt(gi);

if (hasSelectedAnchor)
{
NotifyRangeModified();
Push(new RedoOnlyCommand(NotifyRangeModified));

var selectionStart = selectedAnchors.First().Pos;
var selectionEnd = selectedAnchors.Last().Pos;
var movedStart = selectionStart + offsetPos;
var movedEnd = selectionEnd + offsetPos;
int startIndex = mAnchorGroups.Count - 1;
int endIndex = 0;
bool hasIntersect = false;
bool Intersect(T ag)
{
var start = ag == anchorGroup ? leftSide : ag.Start;
var end = ag == anchorGroup ? rightSide : ag.End;
return start < movedEnd && end > movedStart;
}
for (int i = 0; i < mAnchorGroups.Count; i++)
{
if (Intersect(mAnchorGroups[i]))
{
hasIntersect = true;
startIndex = Math.Min(startIndex, i);
endIndex = Math.Max(endIndex, i);
}
}
T insertAnchorGroup = null!;
if (hasIntersect)
{
for (int i = startIndex; i < endIndex; i++)
{
ConnectAnchorGroup(startIndex);
}
insertAnchorGroup = mAnchorGroups[startIndex];
}
else
{
int insertIndex = 0;
for (; insertIndex < mAnchorGroups.Count; insertIndex++)
{
if (mAnchorGroups[insertIndex].Start > movedEnd)
break;
}

insertAnchorGroup = new T();
mAnchorGroups.Insert(insertIndex, insertAnchorGroup);
}
InsertPointsToGroup(insertAnchorGroup, selectedAnchors.Select(point => new AnchorPoint(point.Pos + offsetPos, point.Value + offsetValue) { IsSelected = true }).ToList());
}
}

if (offsetPos > 0)
{
var anchorGroups = mAnchorGroups.ToList();
for (int i = anchorGroups.Count - 1; i >= 0; i--)
{
MoveSelectedPointsFromAnchorGroupTo(mAnchorGroups.IndexOf(anchorGroups[i]), offsetPos, offsetValue);
}
}
else if (offsetPos < 0)
{
var anchorGroups = mAnchorGroups.ToList();
for (int i = 0; i < anchorGroups.Count; i++)
{
MoveSelectedPointsFromAnchorGroupTo(mAnchorGroups.IndexOf(anchorGroups[i]), offsetPos, offsetValue);
}
}
else
{
if (offsetValue == 0)
return;

foreach (var anchorGroup in mAnchorGroups)
{
double start = anchorGroup.End;
double end = anchorGroup.Start;
bool hasSelectedAnchor = false;
void NotifyRangeModified() => mRangeModified.Invoke(start, end);
for (int pi = anchorGroup.Count - 1; pi >= 0; pi--)
{
var anchorPoint = anchorGroup[pi];
if (anchorPoint.IsSelected)
{
if (!hasSelectedAnchor)
{
Push(new UndoOnlyCommand(NotifyRangeModified));
hasSelectedAnchor = true;
}

end = Math.Max(end, anchorGroup[(pi + 1).Limit(0, anchorGroup.Count - 1)].Pos);
start = Math.Min(start, anchorGroup[(pi - 1).Limit(0, anchorGroup.Count - 1)].Pos);
anchorGroup[pi] = new AnchorPoint(anchorPoint.Pos, anchorPoint.Value + offsetValue) { IsSelected = true };
}
}

if (hasSelectedAnchor)
{
NotifyRangeModified();
Push(new RedoOnlyCommand(NotifyRangeModified));
}
}
}
}

public List<List<Point>> GetInfo()
{
return mAnchorGroups.GetInfo().Select(anchorGroup => anchorGroup.GetInfo().Select(p => p.ToPoint()).ToList()).ToList();
Expand Down Expand Up @@ -349,7 +639,7 @@ public double[] GetValues(IReadOnlyList<double> ticks)

void IDataObject<IEnumerable<IReadOnlyCollection<Point>>>.SetInfo(IEnumerable<IReadOnlyCollection<Point>> info)
{
IDataObject<IEnumerable<IReadOnlyCollection<Point>>>.SetInfo(mAnchorGroups, info.Where(points => points.Count > 1).Convert(points => { var t = new T(); t.Set(points.Select(point => new AnchorPoint(point))); return t; }).ToArray());
IDataObject<IEnumerable<IReadOnlyCollection<Point>>>.SetInfo(mAnchorGroups, info.Where(points => points.Count > 0).Convert(points => { var t = new T(); t.Set(points.Select(point => new AnchorPoint(point))); return t; }).ToArray());
}

readonly DataObjectList<T> mAnchorGroups = new();
Expand Down
1 change: 1 addition & 0 deletions TuneLab/Views/PianoScrollView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public PianoScrollView(IDependency dependency)
mSelectionOperation = new(this);
mAnchorSelectOperation = new(this);
mAnchorDeleteOperation = new(this);
mAnchorMoveOperation = new(this);

mDependency.PartProvider.ObjectChanged.Subscribe(Update, s);
mDependency.PartProvider.When(p => p.Modified).Subscribe(Update, s);
Expand Down
Loading

0 comments on commit e087228

Please sign in to comment.