mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
This updates the Addon Inspector with lots of new features and functionality. - Features from Caraxi's fork of UiDebug have been incorporated, such as the Element Selector UI and address search. - Any addon or node can now pop out into its own window. - Revised the visual style of node field/property information. - Color values are now visually displayed. - Any nodes or components that are referenced by fields within the addon will now show that field name in the inspector. - Added editors for nodes, allowing complete control over most of their properties. - Improved texture display for Image nodes (and Image node variant types). The active part of the texture is now highlighted, and the boundaries of other parts can be shown via mouseover. - Highlighting of node bounds onscreen is now more accurate, factoring in rotation (including when using the Element Selector). - Display of animation timelines has been revamped, showing a table of keyframes for each animation. A standalone SamplePlugin-based version is available here: https://github.com/ItsBexy/UiDebug2
170 lines
5.6 KiB
C#
170 lines
5.6 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
|
|
using Dalamud.Interface.Utility;
|
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
|
using ImGuiNET;
|
|
|
|
using static System.Math;
|
|
|
|
namespace Dalamud.Interface.Internal.UiDebug2.Utility;
|
|
|
|
/// <summary>
|
|
/// A struct representing the perimeter of an <see cref="AtkResNode"/>, accounting for all transformations.
|
|
/// </summary>
|
|
public unsafe struct NodeBounds
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="NodeBounds"/> struct.
|
|
/// </summary>
|
|
/// <param name="node">The node to calculate the bounds of.</param>
|
|
internal NodeBounds(AtkResNode* node)
|
|
{
|
|
if (node == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var w = node->Width;
|
|
var h = node->Height;
|
|
this.Points = w == 0 && h == 0 ?
|
|
new() { new(0) } :
|
|
new() { new(0), new(w, 0), new(w, h), new(0, h) };
|
|
|
|
this.TransformPoints(node);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="NodeBounds"/> struct, containing only a single given point.
|
|
/// </summary>
|
|
/// <param name="point">The point onscreen.</param>
|
|
/// <param name="node">The node used to calculate transformations.</param>
|
|
internal NodeBounds(Vector2 point, AtkResNode* node)
|
|
{
|
|
this.Points = [point];
|
|
this.TransformPoints(node);
|
|
}
|
|
|
|
private List<Vector2> Points { get; set; } = [];
|
|
|
|
/// <summary>
|
|
/// Draws the bounds onscreen.
|
|
/// </summary>
|
|
/// <param name="col">The color of line to use.</param>
|
|
/// <param name="thickness">The thickness of line to use.</param>
|
|
/// <remarks>If there is only a single point to draw, it will be indicated with a circle and dot.</remarks>
|
|
internal readonly void Draw(Vector4 col, int thickness = 1)
|
|
{
|
|
if (this.Points == null || this.Points.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this.Points.Count == 1)
|
|
{
|
|
ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], 10, ColorHelpers.RgbaVector4ToUint(col with { W = col.W / 2 }), 12, thickness);
|
|
ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], thickness, ColorHelpers.RgbaVector4ToUint(col), 12, thickness + 1);
|
|
}
|
|
else
|
|
{
|
|
var path = new ImVectorWrapper<Vector2>(this.Points.Count);
|
|
foreach (var p in this.Points)
|
|
{
|
|
path.Add(p);
|
|
}
|
|
|
|
ImGui.GetBackgroundDrawList()
|
|
.AddPolyline(ref path[0], path.Length, ColorHelpers.RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness);
|
|
|
|
path.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws the bounds onscreen, filled in.
|
|
/// </summary>
|
|
/// <param name="col">The fill and border color.</param>
|
|
/// <param name="thickness">The border thickness.</param>
|
|
internal readonly void DrawFilled(Vector4 col, int thickness = 1)
|
|
{
|
|
if (this.Points == null || this.Points.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this.Points.Count == 1)
|
|
{
|
|
ImGui.GetBackgroundDrawList()
|
|
.AddCircleFilled(this.Points[0], 10, ColorHelpers.RgbaVector4ToUint(col with { W = col.W / 2 }), 12);
|
|
ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], 10, ColorHelpers.RgbaVector4ToUint(col), 12, thickness);
|
|
}
|
|
else
|
|
{
|
|
var path = new ImVectorWrapper<Vector2>(this.Points.Count);
|
|
foreach (var p in this.Points)
|
|
{
|
|
path.Add(p);
|
|
}
|
|
|
|
ImGui.GetBackgroundDrawList()
|
|
.AddConvexPolyFilled(ref path[0], path.Length, ColorHelpers.RgbaVector4ToUint(col with { W = col.W / 2 }));
|
|
ImGui.GetBackgroundDrawList()
|
|
.AddPolyline(ref path[0], path.Length, ColorHelpers.RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness);
|
|
|
|
path.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks whether the bounds contain a given point.
|
|
/// </summary>
|
|
/// <param name="p">The point to check.</param>
|
|
/// <returns>True if the point exists within the bounds.</returns>
|
|
internal readonly bool ContainsPoint(Vector2 p)
|
|
{
|
|
var count = this.Points.Count;
|
|
var inside = false;
|
|
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var p1 = this.Points[i];
|
|
var p2 = this.Points[(i + 1) % count];
|
|
|
|
if (p.Y > Min(p1.Y, p2.Y) &&
|
|
p.Y <= Max(p1.Y, p2.Y) &&
|
|
p.X <= Max(p1.X, p2.X) &&
|
|
(p1.X.Equals(p2.X) || p.X <= ((p.Y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y)) + p1.X))
|
|
{
|
|
inside = !inside;
|
|
}
|
|
}
|
|
|
|
return inside;
|
|
}
|
|
|
|
private static Vector2 TransformPoint(Vector2 p, Vector2 o, float r, Vector2 s)
|
|
{
|
|
var cosR = (float)Cos(r);
|
|
var sinR = (float)Sin(r);
|
|
var d = (p - o) * s;
|
|
|
|
return new(o.X + (d.X * cosR) - (d.Y * sinR),
|
|
o.Y + (d.X * sinR) + (d.Y * cosR));
|
|
}
|
|
|
|
private void TransformPoints(AtkResNode* transformNode)
|
|
{
|
|
while (transformNode != null)
|
|
{
|
|
var offset = new Vector2(transformNode->X, transformNode->Y);
|
|
var origin = offset + new Vector2(transformNode->OriginX, transformNode->OriginY);
|
|
var rotation = transformNode->Rotation;
|
|
var scale = new Vector2(transformNode->ScaleX, transformNode->ScaleY);
|
|
|
|
this.Points = this.Points.Select(b => TransformPoint(b + offset, origin, rotation, scale)).ToList();
|
|
|
|
transformNode = transformNode->ParentNode;
|
|
}
|
|
}
|
|
}
|