Skip to content

Commit

Permalink
fix and improve DrawPlayableAreaHelper
Browse files Browse the repository at this point in the history
  • Loading branch information
MikiraSora committed Sep 18, 2024
1 parent 0d13bfb commit a1ef916
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 41 deletions.
2 changes: 1 addition & 1 deletion Dependences/earcut.net
Submodule earcut.net updated 1 files
+6 −1 src/Earcut.cs
37 changes: 22 additions & 15 deletions OngekiFumenEditor/Kernel/Graphics/ILineDrawing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,28 @@

namespace OngekiFumenEditor.Kernel.Graphics
{
public interface ILineDrawing : IDrawing
{
public struct VertexDash
{
public int DashSize { get; set; }
public int GapSize { get; set; }
public interface ILineDrawing : IDrawing
{
public struct VertexDash
{
public int DashSize { get; set; }
public int GapSize { get; set; }

public static VertexDash Solider { get; } = new VertexDash()
{
GapSize = 0,
DashSize = 100
};
}
public VertexDash() { }
public VertexDash(int dashSize, int gapSize)
{
DashSize = dashSize;
GapSize = gapSize;
}

public record LineVertex(Vector2 Point, Vector4 Color, VertexDash Dash);
void Draw(IDrawingContext target, IEnumerable<LineVertex> points, float lineWidth);
}
public static VertexDash Solider { get; } = new VertexDash()
{
GapSize = 0,
DashSize = 100
};
}

public record LineVertex(Vector2 Point, Vector4 Color, VertexDash Dash);
void Draw(IDrawingContext target, IEnumerable<LineVertex> points, float lineWidth);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@
using OngekiFumenEditor.Utils.ObjectPool;
using EarcutNet;
using System.Drawing;
using Microsoft.CodeAnalysis;
using System.Diagnostics;
using OngekiFumenEditor.Base.OngekiObjects.BulletPalleteEnums;
using Xv2CoreLib.HslColor;

namespace OngekiFumenEditor.Modules.FumenVisualEditor.Graphics.Drawing.Editors
{
public class DrawPlayableAreaHelper
{
private ILineDrawing lineDrawing;
private IPolygonDrawing polygonDrawing;
private ICircleDrawing circleDrawing;

private Vector4 playFieldForegroundColor;
private bool enablePlayFieldDrawing;
Expand All @@ -30,6 +35,7 @@ public DrawPlayableAreaHelper()
{
lineDrawing = IoC.Get<ISimpleLineDrawing>();
polygonDrawing = IoC.Get<IPolygonDrawing>();
circleDrawing = IoC.Get<ICircleDrawing>();

UpdateProps();
Properties.EditorGlobalSetting.Default.PropertyChanged += Default_PropertyChanged;
Expand Down Expand Up @@ -89,6 +95,7 @@ 4. 将左右所有的节点合并成一个多边形,渲染
const long defaultRightX = 24 * XGrid.DEFAULT_RES_X;

var fumen = target.Editor.Fumen;
var currentTGrid = target.Editor.GetCurrentTGrid();

void EnumeratePoints(bool isRight, List<Vector2> result)
{
Expand All @@ -102,15 +109,29 @@ void EnumeratePoints(bool isRight, List<Vector2> result)
var points = ObjectPool<HashSet<float>>.Get();
points.Clear();

var prevPos = new Vector2((float)MathUtils.Random(), (float)MathUtils.Random());
void appendPoint2(List<Vector2> list, float x, float y)
var prevX = (float)Random();
var prevY = (float)Random();

void appendPoint2(List<Vector2> list, float totalXGrid, float totalTGrid)
{
var px = (float)XGridCalculator.ConvertXGridToX(totalXGrid / XGrid.DEFAULT_RES_X, target.Editor);
var py = (float)target.ConvertToY(totalTGrid / TGrid.DEFAULT_RES_T);

appendPoint3(list, px, py, list.Count);
}

void appendPoint3(List<Vector2> list, float px, float py, int insertIdx)
{
var p = new Vector2(x, y);
if (prevPos == p)
return;
list.Add(p);
prevPos = p;
var po = list.ElementAtOrDefault(insertIdx);
if (po.X == px && po.Y == py)
return;

var p = new Vector2(px, py);
list.Insert(insertIdx, p);

//DebugPrintPoint(p, isRight, false, 10);
}

void appendPoint(List<Vector2> list, XGrid xGrid, float y)
{
if (xGrid is null)
Expand Down Expand Up @@ -168,11 +189,13 @@ void appendPoint(List<Vector2> list, XGrid xGrid, float y)
);
}

var sortedPoints = points.OrderBy(x => x).ToList();
var sortedPoints = points.Where(x => minTGrid.TotalGrid < x && x < maxTGrid.TotalGrid).OrderBy(x => x).ToList();
/*
if (sortedPoints.Count == 0 || sortedPoints.FirstOrDefault() > minTGrid.TotalGrid)
sortedPoints.Insert(0, minTGrid.TotalGrid);
if (sortedPoints.LastOrDefault() < maxTGrid.TotalGrid)
sortedPoints.Add(maxTGrid.TotalGrid);
*/
sortedPoints.InsertBySortBy(minTGrid.TotalGrid, x => x);
sortedPoints.InsertBySortBy(maxTGrid.TotalGrid, x => x);

var segments = sortedPoints.SequenceConsecutivelyWrap(2).Select(x => (x.FirstOrDefault(), x.LastOrDefault())).ToArray();

Expand All @@ -190,7 +213,7 @@ void appendPoint(List<Vector2> list, XGrid xGrid, float y)
.ToArray();

//选取轨道,如果存在多个轨道(即轨道交叉冲突了),那么就按左右边规则选取一个
(var midXGrid, var pickLane) = pickables.IsEmpty() ? default : (isRight ? pickables.MaxBy(x => x.Item1) : pickables.MinBy(x => x.Item1));
(_, var pickLane) = pickables.IsEmpty() ? default : (isRight ? pickables.MaxBy(x => x.Item1) : pickables.MinBy(x => x.Item1));
if (pickLane is not null)
{
var fromTGrid = TGrid.FromTotalGrid((int)fromY);
Expand All @@ -205,11 +228,103 @@ void appendPoint(List<Vector2> list, XGrid xGrid, float y)
else
{
//选取不到轨道,表示这个segement是两个轨道之间的空白区域,那么直接填上空白就行
result.Add(new(defaultX, fromY));
result.Add(new(defaultX, toY));
appendPoint2(result, defaultX, fromY);
appendPoint2(result, defaultX, toY);
}
}

//todo 解决变速过快导致的精度丢失问题
Vector2? interpolate(TGrid tGrid, float actualY, out bool isPickLane)
{
isPickLane = false;
var pickables = fumen.Lanes
.GetVisibleStartObjects(tGrid, tGrid)
.Where(x => x.LaneType == type)
.Where(x =>
{
var laneMinY = target.ConvertToY(x.MinTGrid);
var laneMaxY = target.ConvertToY(x.MaxTGrid);

return laneMinY <= actualY && actualY <= laneMaxY;
})
.Select(x => (x.CalulateXGrid(tGrid), x))
.FilterNullBy(x => x.Item1)
.ToArray();

(_, var pickLane) = pickables.IsEmpty() ? default : (isRight ? pickables.MaxBy(x => x.Item1) : pickables.MinBy(x => x.Item1));

if (pickLane is not null)
{
var itor = pickLane.GenAllPath().GetEnumerator();
var prevOpt = default(OpenTK.Mathematics.Vector2?);

while (itor.MoveNext())
{
var cur = itor.Current.pos;

if (cur.Y > tGrid.TotalGrid)
{
// prev ------------ cur
// ^
// tGrid

if (prevOpt is OpenTK.Mathematics.Vector2 prev)
{
var curPx = (float)XGridCalculator.ConvertXGridToX(cur.X / XGrid.DEFAULT_RES_X, target.Editor);
var curPy = (float)target.ConvertToY(cur.Y / TGrid.DEFAULT_RES_T);
var prevPx = (float)XGridCalculator.ConvertXGridToX(prev.X / XGrid.DEFAULT_RES_X, target.Editor);
var prevPy = (float)target.ConvertToY(prev.Y / TGrid.DEFAULT_RES_T);

var nowPy = actualY;
var nowPx = (float)MathUtils.CalculateXFromTwoPointFormFormula(nowPy, prevPx, prevPy, curPx, curPy);
isPickLane = true;
return new(nowPx, nowPy);
}
}

prevOpt = cur;
}
}
else
{
var defaultPx = (float)XGridCalculator.ConvertXGridToX(defaultX / XGrid.DEFAULT_RES_X, target.Editor);
return new(defaultPx, actualY);
}

return default;
}

if (minTGrid <= currentTGrid && currentTGrid <= maxTGrid)
{
var maxY = target.ConvertToY(maxTGrid);
var actualMaxY = target.Rect.TopLeft.Y;

var maxDiff = maxY - actualMaxY;
if (Math.Abs(maxDiff) >= 1)
{
if (interpolate(maxTGrid, (float)Math.Max(actualMaxY, maxY), out var isPickLane) is Vector2 pp)
{
if (!isPickLane)
appendPoint3(result, (float)XGridCalculator.ConvertXGridToX(defaultX / XGrid.DEFAULT_RES_X, target.Editor), result.LastOrDefault().Y, result.Count);
appendPoint3(result, pp.X, pp.Y, result.Count);
}
}

var minY = target.ConvertToY(minTGrid);
var actualMinY = target.Rect.ButtomRight.Y;

var minDiff = minY - actualMinY;
if (Math.Abs(minDiff) >= 1)
{
if (interpolate(minTGrid, (float)Math.Max(actualMinY, minY), out var isPickLane) is Vector2 pp)
{
if (!isPickLane)
appendPoint3(result, (float)XGridCalculator.ConvertXGridToX(defaultX / XGrid.DEFAULT_RES_X, target.Editor), result.FirstOrDefault().Y, 0);
appendPoint3(result, pp.X, pp.Y, 0);
}
}
}

ObjectPool<HashSet<float>>.Return(points);
}

Expand All @@ -218,50 +333,126 @@ void appendPoint(List<Vector2> list, XGrid xGrid, float y)
using var d4 = ObjectPool<List<int>>.GetWithUsingDisposable(out var idxList, out _);
idxList.Clear();

void FillPoints(List<Vector2> ps)
void FillPoints(List<Vector2> ps, bool isRight)
{
foreach (var point in ps)
var s = points.Count / 2;

for (var i = 0; i < ps.Count; i++)
{
var x = (float)XGridCalculator.ConvertXGridToX(point.X / XGrid.DEFAULT_RES_X, target.Editor);
var y = (float)target.ConvertToY(point.Y / TGrid.DEFAULT_RES_T);
var cur = ps[i];

points.Add(x);
points.Add(y);
}
//remove dup
if (points.Count >= 2)
{
var prevY = points[^1];
var prevX = points[^2];

if (prevY == cur.Y && prevX == cur.X)
continue;
}

//optimze prev point if able
if (points.Count >= 4)
{
var prevY = points[^1];
var prevX = points[^2];

if ((prevY == cur.Y && prevY == points[^3]) || (prevX == cur.X && prevX == points[^4]))
{
//remove
points.RemoveAt(points.Count - 1);
points.RemoveAt(points.Count - 1);
}
}

points.Add(cur.X);
points.Add(cur.Y);
}

if (isRight)
{
for (var j = s; j < points.Count / 2; j++)
DebugPrintPoint(new((float)points[2 * j], (float)points[2 * j + 1]), isRight, true, 10);
}
}

using var d = ObjectPool<List<Vector2>>.GetWithUsingDisposable(out var leftPoints, out _);
leftPoints.Clear();
using var d2 = ObjectPool<List<Vector2>>.GetWithUsingDisposable(out var rightPoints, out _);
rightPoints.Clear();
rightPoints.Clear();

BeginDebugPrint(target);

//计算左边墙的点
EnumeratePoints(false, leftPoints);
FillPoints(leftPoints);
FillPoints(leftPoints, false);
//计算右边墙的点,因为要组合成一个多边形,所以右半部分得翻转一下顺序
EnumeratePoints(true, rightPoints);
rightPoints.Reverse();
FillPoints(rightPoints);

FillPoints(rightPoints, true);
//todo 解决左右墙交叉处理问题

EndDebugPrint();

var tessellateList = ObjectPool<List<int>>.Get();
tessellateList.Clear();
Earcut.Tessellate(points, idxList, tessellateList);

var r = string.Join(Environment.NewLine, points.SequenceWrap(2).Select(x => $"{x.FirstOrDefault(),-20}{x.LastOrDefault()}"));

polygonDrawing.Begin(target, OpenTK.Graphics.OpenGL.PrimitiveType.Triangles);

var i = 0;
foreach (var seq in tessellateList.SequenceWrap(3))
{
var normal = i * 1.0f / tessellateList.Count / 2 + 0.5;
var hslColor = new HslColor(0.55, normal, 1);

var rgb = hslColor.ToRgb();
var color = new Vector4((float)rgb.R, (float)rgb.G, (float)rgb.B, 0.25f);

foreach (var idx in seq)
{
var x = (float)points[idx * 2 + 0];
var y = (float)points[idx * 2 + 1];
polygonDrawing.PostPoint(new(x, y), playFieldForegroundColor);
}
i += 3;
}
polygonDrawing.End();
/*
i = 0;
foreach (var seq in tessellateList.SequenceWrap(3))
{
var normal = i * 1.0f / tessellateList.Count / 2 + 0.5;
var hslColor = new HslColor(0.55, normal, 1);
var rgb = hslColor.ToRgb();
var color = new Vector4((float)rgb.R, (float)rgb.G, (float)rgb.B, 1);
lineDrawing.Draw(target, seq.Append(seq.FirstOrDefault()).Select(idx => new LineVertex(new((float)points[idx * 2 + 0], (float)points[idx * 2 + 1]), color, new(2, 2))), 3);
i += 3;
}
*/
ObjectPool<List<int>>.Return(tessellateList);
}

polygonDrawing.End();
[Conditional("DEBUG")]
private void DebugPrintPoint(Vector2 p, bool isRight, bool v1, int v2)
{
var color = isRight ? new Vector4(1, 0, 0, 0.5f) : new Vector4(0, 1, 0, 0.5f);
circleDrawing.Post(p, color, false, v2);
}

[Conditional("DEBUG")]
private void BeginDebugPrint(IFumenEditorDrawingContext target)
{
circleDrawing.Begin(target);
}

[Conditional("DEBUG")]
private void EndDebugPrint()
{
circleDrawing.End();
}
}
}

0 comments on commit a1ef916

Please sign in to comment.