// Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using System;
#if UNITY_2D_SPRITE
using UnityEditor.U2D.Sprites;
#endif
namespace Animancer.Editor.Tools
{
/// A wrapper around the '2D Sprite' package features for editing Sprite data.
public class SpriteDataEditor
{
/************************************************************************************************************************/
/// Is the '2D Sprite' package currently in the project?
public static bool Is2dPackagePresent;
static SpriteDataEditor()
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.GetName().Name == "Unity.2D.Sprite.Editor")
{
Is2dPackagePresent = true;
break;
}
}
}
/************************************************************************************************************************/
/// The scripting define symbol used to enable the features from the '2D Sprite' package.
public const string SpritePackageDefineSymbol = "UNITY_2D_SPRITE";
/// Is the defined in the current platform?
public static bool IsSpritePackageSymbolDefined
{
get => ScriptingDefineSymbols.IsSymbolDefined(SpritePackageDefineSymbol);
set => ScriptingDefineSymbols.SetSymbolDefined(SpritePackageDefineSymbol, value);
}
/************************************************************************************************************************/
#if UNITY_2D_SPRITE
/************************************************************************************************************************/
private static SpriteDataProviderFactories _Factories;
private static SpriteDataProviderFactories Factories
{
get
{
if (_Factories == null)
{
_Factories = new SpriteDataProviderFactories();
_Factories.Init();
}
return _Factories;
}
}
/************************************************************************************************************************/
private readonly ISpriteEditorDataProvider Provider;
private readonly SpriteRect[] SpriteRects;
/************************************************************************************************************************/
/// The number of sprites in the target data.
public int SpriteCount => SpriteRects.Length;
/// Returns the name of the sprite at the specified `index`.
public string GetName(int index) => SpriteRects[index].name;
/// Sets the name of the sprite at the specified `index`.
public void SetName(int index, string name) => SpriteRects[index].name = name;
/// Returns the rect of the sprite at the specified `index`.
public Rect GetRect(int index) => SpriteRects[index].rect;
/// Sets the rect of the sprite at the specified `index`.
public void SetRect(int index, Rect rect) => SpriteRects[index].rect = rect;
/// Returns the pivot of the sprite at the specified `index`.
public Vector2 GetPivot(int index) => SpriteRects[index].pivot;
/// Sets the pivot of the sprite at the specified `index`.
public void SetPivot(int index, Vector2 pivot) => SpriteRects[index].pivot = pivot;
/// Returns the alignment of the sprite at the specified `index`.
public SpriteAlignment GetAlignment(int index) => SpriteRects[index].alignment;
/// Sets the alignment of the sprite at the specified `index`.
public void SetAlignment(int index, SpriteAlignment alignment) => SpriteRects[index].alignment = alignment;
/// Returns the border of the sprite at the specified `index`.
public Vector4 GetBorder(int index) => SpriteRects[index].border;
/// Sets the border of the sprite at the specified `index`.
public void SetBorder(int index, Vector4 border) => SpriteRects[index].border = border;
/************************************************************************************************************************/
#else
/************************************************************************************************************************/
private readonly SpriteMetaData[] SpriteSheet;
/************************************************************************************************************************/
/// The number of sprites in the target data.
public int SpriteCount => SpriteSheet.Length;
/// Returns the name of the sprite at the specified `index`.
public string GetName(int index) => SpriteSheet[index].name;
/// Sets the name of the sprite at the specified `index`.
public void SetName(int index, string name) => SpriteSheet[index].name = name;
/// Returns the rect of the sprite at the specified `index`.
public Rect GetRect(int index) => SpriteSheet[index].rect;
/// Sets the rect of the sprite at the specified `index`.
public void SetRect(int index, Rect rect) => SpriteSheet[index].rect = rect;
/// Returns the pivot of the sprite at the specified `index`.
public Vector2 GetPivot(int index) => SpriteSheet[index].pivot;
/// Sets the pivot of the sprite at the specified `index`.
public void SetPivot(int index, Vector2 pivot) => SpriteSheet[index].pivot = pivot;
/// Returns the alignment of the sprite at the specified `index`.
public SpriteAlignment GetAlignment(int index) => (SpriteAlignment)SpriteSheet[index].alignment;
/// Sets the alignment of the sprite at the specified `index`.
public void SetAlignment(int index, SpriteAlignment alignment) => SpriteSheet[index].alignment = (int)alignment;
/// Returns the border of the sprite at the specified `index`.
public Vector4 GetBorder(int index) => SpriteSheet[index].border;
/// Sets the border of the sprite at the specified `index`.
public void SetBorder(int index, Vector4 border) => SpriteSheet[index].border = border;
/************************************************************************************************************************/
#endif
/************************************************************************************************************************/
private readonly TextureImporter Importer;
/************************************************************************************************************************/
/// Creates a new .
public SpriteDataEditor(TextureImporter importer)
{
Importer = importer;
#if UNITY_2D_SPRITE
Provider = Factories.GetSpriteEditorDataProviderFromObject(importer);
Provider.InitSpriteEditorDataProvider();
SpriteRects = Provider.GetSpriteRects();
#else
SpriteSheet = importer.spritesheet;
#endif
}
/************************************************************************************************************************/
/// Tries to find the index of the data matching the `sprite`.
///
/// Returns -1 if there is no data matching the .
///
/// Returns -2 if there is more than one data matching the but no
/// match.
///
public int IndexOf(Sprite sprite)
{
var nameMatchIndex = -1;
var count = SpriteCount;
for (int i = 0; i < count; i++)
{
if (GetName(i) == sprite.name)
{
if (GetRect(i) == sprite.rect)
return i;
if (nameMatchIndex == -1)// First name match.
nameMatchIndex = i;
else
nameMatchIndex = -2;// Already found 2 name matches.
}
}
if (nameMatchIndex == -1)
{
Debug.LogError($"No {nameof(SpriteMetaData)} for '{sprite.name}' was found.", sprite);
}
else if (nameMatchIndex == -2)
{
Debug.LogError($"More than one {nameof(SpriteMetaData)} for '{sprite.name}' was found" +
$" but none of them matched the {nameof(Sprite)}.{nameof(Sprite.rect)}." +
$" If the texture's Max Size is smaller than its actual size, increase the Max Size before performing this" +
$" operation so that the {nameof(Rect)}s can be used to identify the correct data.", sprite);
}
return nameMatchIndex;
}
/************************************************************************************************************************/
/// Logs an error and returns false if the data at the specified `index` is out of the texture bounds.
public bool ValidateBounds(int index, Sprite sprite)
{
var rect = GetRect(index);
var widthScale = rect.width / sprite.rect.width;
var heightScale = rect.height / sprite.rect.height;
if (rect.xMin < 0 ||
rect.yMin < 0 ||
rect.xMax > sprite.texture.width * widthScale ||
rect.yMax > sprite.texture.height * heightScale)
{
var path = AssetDatabase.GetAssetPath(sprite);
Debug.LogError($"This modification would have put '{sprite.name}' out of bounds" +
$" so '{path}' was not modified.", sprite);
return false;
}
return true;
}
/************************************************************************************************************************/
/// Applies any modifications to the target asset.
public void Apply()
{
#if UNITY_2D_SPRITE
Provider.SetSpriteRects(SpriteRects);
Provider.Apply();
#else
Importer.spritesheet = SpriteSheet;
EditorUtility.SetDirty(Importer);
#endif
Importer.SaveAndReimport();
}
/************************************************************************************************************************/
}
}
#endif