Files
aibis-dream/Assets/Scripts/FixSystemNew/BodyModule/CutLine.cs
T
2025-04-29 19:31:24 +08:00

206 lines
6.6 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace AibisDream
{
public class CutLine : MonoBehaviour
{
private LineRenderer _lineRenderer;
private Vector2[] _linePoints;
private float[] _normalizedLinePoints;
private Bounds _bounds;
private List<Vector2> _recordedRanges = new();
#region 初始化
public void Init(List<Vector2> shapePoints, float clickRadius)
{
_lineRenderer = GetComponent<LineRenderer>();
// 按照新点位重绘线条
RedrawLine(shapePoints);
// 生成归一化点
NormalizePoints(shapePoints);
// 生成基本的包围盒
GeneBoundingBox(clickRadius);
ClearRecordedRanges();
}
private void RedrawLine(List<Vector2> shapePoints)
{
var linePoints = shapePoints.Select(item => new Vector3(item.x, item.y, 0)).ToArray();
_lineRenderer.SetPositions(linePoints);
}
private void NormalizePoints(List<Vector2> shapePoints)
{
// 先生成首尾相连的点,默认Loop
shapePoints.Add(shapePoints[0]);
_linePoints = shapePoints.ToArray();
float totalLength = 0f;
var segmentLengths = new List<float>();
// 计算总长度和各段长度
for (var i = 1; i < _linePoints.Length; i++)
{
var segmentLength = Vector2.Distance(_linePoints[i - 1], _linePoints[i]);
segmentLengths.Add(segmentLength);
totalLength += segmentLength;
}
// 计算每个点的归一化位置
var normalizedLinePoints = new float[segmentLengths.Count + 1];
normalizedLinePoints[0] = 0f;
var accumulatedLength = 0f;
for (var i = 0; i < segmentLengths.Count; i++)
{
accumulatedLength += segmentLengths[i];
normalizedLinePoints[i + 1] = accumulatedLength / totalLength;
}
}
private void GeneBoundingBox(float clickRadius)
{
_bounds = new Bounds();
foreach (var point in _linePoints)
{
_bounds.Encapsulate(point);
}
_bounds.Expand(clickRadius * 2);
}
#endregion
#region 切割检测
public void DetectLineClick(Vector2 clickPosition, float clickRadius)
{
// 先进行包围盒检测,不在包围盒直接返回
if (!_bounds.Contains(clickPosition))
{
return;
}
List<Vector2> newRanges = new List<Vector2>();
// 检查每条线段
for (int i = 1; i < _linePoints.Length; i++)
{
Vector2 start = _linePoints[i - 1];
Vector2 end = _linePoints[i];
// 计算点到线段的距离
float distance = DistanceToSegment(clickPosition, start, end);
if (distance <= clickRadius)
{
// 计算点击影响的范围
float normalizedStart = _normalizedLinePoints[i - 1];
float normalizedEnd = _normalizedLinePoints[i];
// 计算实际影响的范围(考虑点击半径)
float segmentLength = Vector2.Distance(start, end);
float radiusRatio = clickRadius / segmentLength;
float rangeStart = Mathf.Clamp01(normalizedStart - radiusRatio);
float rangeEnd = Mathf.Clamp01(normalizedEnd + radiusRatio);
newRanges.Add(new Vector2(rangeStart, rangeEnd));
}
}
// 合并新检测到的区间
foreach (Vector2 range in newRanges)
{
_recordedRanges.Add(range);
}
// 合并所有重叠区间
MergeRanges();
// 打印当前所有区间(调试用)
Debug.Log("Current recorded ranges:");
foreach (Vector2 range in _recordedRanges)
{
Debug.Log($"Range: {range.x} - {range.y}");
}
}
private float DistanceToSegment(Vector2 point, Vector2 segmentStart, Vector2 segmentEnd)
{
Vector2 segment = segmentEnd - segmentStart;
Vector2 pointToStart = point - segmentStart;
float dotProduct = Vector2.Dot(pointToStart, segment);
// 检查点是否在线段的投影范围内
if (dotProduct <= 0)
{
return Vector2.Distance(point, segmentStart);
}
float segmentLengthSquared = segment.sqrMagnitude;
if (dotProduct >= segmentLengthSquared)
{
return Vector2.Distance(point, segmentEnd);
}
// 计算垂直距离
return Mathf.Abs(segmentStart.y * segmentEnd.x - segmentStart.x * segmentEnd.y +
point.x * (segmentStart.y - segmentEnd.y) +
point.y * (segmentEnd.x - segmentStart.x)) /
Mathf.Sqrt(segmentLengthSquared);
}
private void MergeRanges()
{
if (_recordedRanges.Count <= 1) return;
// 先按起始位置排序
_recordedRanges.Sort((a, b) => a.x.CompareTo(b.x));
List<Vector2> mergedRanges = new List<Vector2>();
Vector2 currentRange = _recordedRanges[0];
for (int i = 1; i < _recordedRanges.Count; i++)
{
Vector2 nextRange = _recordedRanges[i];
// 检查是否有重叠或相邻
if (nextRange.x <= currentRange.y)
{
// 合并区间
currentRange.y = Mathf.Max(currentRange.y, nextRange.y);
}
else
{
mergedRanges.Add(currentRange);
currentRange = nextRange;
}
}
mergedRanges.Add(currentRange);
_recordedRanges = mergedRanges;
}
#endregion
// 获取当前所有记录的范围(0-1之间的值)
public List<Vector2> GetRecordedRanges()
{
return new List<Vector2>(_recordedRanges);
}
// 清除所有记录的范围
public void ClearRecordedRanges()
{
_recordedRanges.Clear();
}
}
}