Update
This commit is contained in:
133
Assets/Scripts/UI/Components/VectorShapeElement.cs
Normal file
133
Assets/Scripts/UI/Components/VectorShapeElement.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hallucinate.UI.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// An adjustable Vector Element that you can "sketch" and tweak directly in UI Builder (UXML).
|
||||
/// Supports Parametric shapes (Pill, Polygon, Star) and Custom Paths.
|
||||
/// </summary>
|
||||
public class VectorShapeElement : VisualElement
|
||||
{
|
||||
public enum ShapeType { Pill, Polygon, Star, CustomPath }
|
||||
|
||||
public new class UxmlFactory : UxmlFactory<VectorShapeElement, UxmlTraits> { }
|
||||
|
||||
public new class UxmlTraits : VisualElement.UxmlTraits
|
||||
{
|
||||
UxmlEnumAttributeDescription<ShapeType> m_ShapeType = new UxmlEnumAttributeDescription<ShapeType> { name = "shape-type", defaultValue = ShapeType.Pill };
|
||||
UxmlColorAttributeDescription m_FillColor = new UxmlColorAttributeDescription { name = "fill-color", defaultValue = Color.white };
|
||||
UxmlColorAttributeDescription m_StrokeColor = new UxmlColorAttributeDescription { name = "stroke-color", defaultValue = Color.black };
|
||||
UxmlFloatAttributeDescription m_StrokeWidth = new UxmlFloatAttributeDescription { name = "stroke-width", defaultValue = 2f };
|
||||
UxmlFloatAttributeDescription m_CornerRadius = new UxmlFloatAttributeDescription { name = "corner-radius", defaultValue = 10f };
|
||||
UxmlIntAttributeDescription m_Sides = new UxmlIntAttributeDescription { name = "sides", defaultValue = 5 };
|
||||
UxmlFloatAttributeDescription m_Inwardness = new UxmlFloatAttributeDescription { name = "star-inwardness", defaultValue = 0.5f };
|
||||
UxmlStringAttributeDescription m_PathData = new UxmlStringAttributeDescription { name = "path-data", defaultValue = "" };
|
||||
|
||||
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
|
||||
{
|
||||
base.Init(ve, bag, cc);
|
||||
var ate = ve as VectorShapeElement;
|
||||
|
||||
ate.shapeType = m_ShapeType.GetValueFromBag(bag, cc);
|
||||
ate.fillColor = m_FillColor.GetValueFromBag(bag, cc);
|
||||
ate.strokeColor = m_StrokeColor.GetValueFromBag(bag, cc);
|
||||
ate.strokeWidth = m_StrokeWidth.GetValueFromBag(bag, cc);
|
||||
ate.cornerRadius = m_CornerRadius.GetValueFromBag(bag, cc);
|
||||
ate.sides = Mathf.Max(3, m_Sides.GetValueFromBag(bag, cc));
|
||||
ate.inwardness = Mathf.Clamp01(m_Inwardness.GetValueFromBag(bag, cc));
|
||||
ate.pathData = m_PathData.GetValueFromBag(bag, cc);
|
||||
|
||||
ate.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
public ShapeType shapeType { get; set; }
|
||||
public Color fillColor { get; set; }
|
||||
public Color strokeColor { get; set; }
|
||||
public float strokeWidth { get; set; }
|
||||
public float cornerRadius { get; set; }
|
||||
public int sides { get; set; }
|
||||
public float inwardness { get; set; }
|
||||
public string pathData { get; set; }
|
||||
|
||||
public VectorShapeElement()
|
||||
{
|
||||
generateVisualContent += OnGenerateVisualContent;
|
||||
}
|
||||
|
||||
private void OnGenerateVisualContent(MeshGenerationContext mgc)
|
||||
{
|
||||
var paint = mgc.painter2D;
|
||||
var rect = contentRect;
|
||||
|
||||
if (rect.width <= 0 || rect.height <= 0) return;
|
||||
|
||||
paint.BeginPath();
|
||||
paint.fillColor = fillColor;
|
||||
paint.strokeColor = strokeColor;
|
||||
paint.lineWidth = strokeWidth;
|
||||
|
||||
switch (shapeType)
|
||||
{
|
||||
case ShapeType.Pill: DrawPill(paint, rect); break;
|
||||
case ShapeType.Polygon: DrawPolygon(paint, rect, false); break;
|
||||
case ShapeType.Star: DrawPolygon(paint, rect, true); break;
|
||||
case ShapeType.CustomPath: DrawCustomPath(paint); break;
|
||||
}
|
||||
|
||||
paint.ClosePath();
|
||||
paint.Fill();
|
||||
if (strokeWidth > 0) paint.Stroke();
|
||||
}
|
||||
|
||||
private void DrawPill(Painter2D paint, Rect rect)
|
||||
{
|
||||
float r = Mathf.Min(cornerRadius, rect.width / 2f, rect.height / 2f);
|
||||
paint.MoveTo(new Vector2(r, 0));
|
||||
paint.LineTo(new Vector2(rect.width - r, 0));
|
||||
paint.ArcTo(new Vector2(rect.width, 0), new Vector2(rect.width, r), r);
|
||||
paint.LineTo(new Vector2(rect.width, rect.height - r));
|
||||
paint.ArcTo(new Vector2(rect.width, rect.height), new Vector2(rect.width - r, rect.height), r);
|
||||
paint.LineTo(new Vector2(r, rect.height));
|
||||
paint.ArcTo(new Vector2(0, rect.height), new Vector2(0, rect.height - r), r);
|
||||
paint.LineTo(new Vector2(0, r));
|
||||
paint.ArcTo(new Vector2(0, 0), new Vector2(r, 0), r);
|
||||
}
|
||||
|
||||
private void DrawPolygon(Painter2D paint, Rect rect, bool isStar)
|
||||
{
|
||||
Vector2 center = rect.center;
|
||||
float radius = Mathf.Min(rect.width, rect.height) / 2f;
|
||||
int totalPoints = isStar ? sides * 2 : sides;
|
||||
float angleStep = 360f / totalPoints;
|
||||
|
||||
for (int i = 0; i < totalPoints; i++)
|
||||
{
|
||||
float angle = (i * angleStep - 90f) * Mathf.Deg2Rad;
|
||||
float r = radius;
|
||||
if (isStar && i % 2 != 0) r *= inwardness;
|
||||
|
||||
Vector2 pos = center + new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * r;
|
||||
if (i == 0) paint.MoveTo(pos);
|
||||
else paint.LineTo(pos);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCustomPath(Painter2D paint)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pathData)) return;
|
||||
string[] pairs = pathData.Split(' ');
|
||||
for (int i = 0; i < pairs.Length; i++)
|
||||
{
|
||||
string[] coords = pairs[i].Split(',');
|
||||
if (coords.Length == 2 && float.TryParse(coords[0], out float x) && float.TryParse(coords[1], out float y))
|
||||
{
|
||||
if (i == 0) paint.MoveTo(new Vector2(x, y));
|
||||
else paint.LineTo(new Vector2(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user