This commit is contained in:
2026-05-06 01:47:40 +07:00
parent eac87bdd03
commit 6869294123
16 changed files with 365 additions and 301 deletions

View 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));
}
}
}
}
}