// MIT License - Copyright (c) 2026 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
namespace WallstopStudios.UnityHelpers.Editor.Sprites
{
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Animation;
///
/// JSON-serializable configuration for AnimationCreator settings.
/// Stored alongside sprite source folders as {folderPath}/.animation-creator.json.
///
[Serializable]
public sealed class AnimationCreatorConfig
{
///
/// Configuration file name (without leading path).
///
public const string FileName = ".animation-creator.json";
///
/// Current configuration version for migration support.
///
public const int CurrentVersion = 1;
///
/// Configuration version number for future migration support.
///
public int version = CurrentVersion;
///
/// The regex pattern used to filter sprite names.
///
public string spriteNameRegex = ".*";
///
/// Whether auto-refresh is enabled for the filter.
///
public bool autoRefresh = true;
///
/// Whether grouping should be case-insensitive.
///
public bool groupingCaseInsensitive = true;
///
/// Whether to include the folder name in animation names.
///
public bool includeFolderNameInAnimName;
///
/// Whether to include the full folder path in animation names.
///
public bool includeFullFolderPathInAnimName;
///
/// Prefix to apply to auto-parsed animation names.
///
public string autoParseNamePrefix = string.Empty;
///
/// Suffix to apply to auto-parsed animation names.
///
public string autoParseNameSuffix = string.Empty;
///
/// Whether custom group regex is enabled.
///
public bool useCustomGroupRegex;
///
/// The custom group regex pattern.
///
public string customGroupRegex = string.Empty;
///
/// Whether the custom group regex should ignore case.
///
public bool customGroupRegexIgnoreCase = true;
///
/// Whether to resolve duplicate animation names.
///
public bool resolveDuplicateAnimationNames = true;
///
/// Whether to use strict numeric ordering.
///
public bool strictNumericOrdering;
///
/// The animation data entries.
///
public List animationEntries = new();
///
/// Serializable version of AnimationData for JSON persistence.
/// Does not include transient/UI-only fields like showPreview.
///
[Serializable]
public sealed class AnimationDataEntry
{
///
/// Name of the animation clip.
///
public string animationName = string.Empty;
///
/// Asset paths of the sprite frames (relative to Assets/).
///
public List framePaths = new();
///
/// Constant frames per second.
///
public float framesPerSecond = AnimationData.DefaultFramesPerSecond;
///
/// Whether this animation was created from auto-parse.
///
public bool isCreatedFromAutoParse;
///
/// Whether the animation should loop.
///
public bool loop;
///
/// The framerate mode (Constant or Curve).
///
public FramerateMode framerateMode = FramerateMode.Constant;
///
/// Keyframes for the FPS curve (serialized as time/value pairs).
///
public List curveKeyframes = new();
///
/// Wrap mode for the curve at the start.
///
public WrapMode curvePreWrapMode = WrapMode.Clamp;
///
/// Wrap mode for the curve at the end.
///
public WrapMode curvePostWrapMode = WrapMode.Clamp;
///
/// Starting point in the animation loop (0-1).
///
public float cycleOffset;
}
///
/// Serializable version of AnimationCurve keyframes.
///
[Serializable]
public sealed class CurveKeyframe
{
///
/// The time of the keyframe.
///
public float time;
///
/// The value of the keyframe.
///
public float value;
///
/// The incoming tangent.
///
public float inTangent;
///
/// The outgoing tangent.
///
public float outTangent;
///
/// The incoming weight.
///
public float inWeight;
///
/// The outgoing weight.
///
public float outWeight;
///
/// The weighted mode of the keyframe.
///
public WeightedMode weightedMode;
///
/// Creates a CurveKeyframe from a Unity Keyframe.
///
/// The Unity Keyframe to convert.
/// A serializable CurveKeyframe.
public static CurveKeyframe FromKeyframe(Keyframe keyframe)
{
return new CurveKeyframe
{
time = keyframe.time,
value = keyframe.value,
inTangent = keyframe.inTangent,
outTangent = keyframe.outTangent,
inWeight = keyframe.inWeight,
outWeight = keyframe.outWeight,
weightedMode = keyframe.weightedMode,
};
}
///
/// Converts this CurveKeyframe back to a Unity Keyframe.
///
/// A Unity Keyframe.
public Keyframe ToKeyframe()
{
return new Keyframe(time, value, inTangent, outTangent, inWeight, outWeight)
{
weightedMode = weightedMode,
};
}
}
///
/// Gets the config file path for a given folder path.
///
/// The path to the source folder.
/// The config file path within the folder.
public static string GetConfigPath(string folderPath)
{
if (string.IsNullOrEmpty(folderPath))
{
return string.Empty;
}
string normalized = folderPath.TrimEnd('/', '\\');
return normalized + "/" + FileName;
}
///
/// Migrates a configuration from an older version to the current version.
/// Currently a no-op for version 1, but provides the hook for future migrations.
///
/// The configuration to migrate.
public static void MigrateConfig(AnimationCreatorConfig config)
{
if (config == null || config.version >= CurrentVersion)
{
return;
}
// Future migrations go here
config.version = CurrentVersion;
}
///
/// Converts an AnimationCurve to a list of serializable keyframes.
///
/// The curve to serialize.
/// A list of CurveKeyframes.
public static List SerializeCurve(AnimationCurve curve)
{
List keyframes = new();
if (curve == null)
{
return keyframes;
}
for (int i = 0; i < curve.length; i++)
{
keyframes.Add(CurveKeyframe.FromKeyframe(curve[i]));
}
return keyframes;
}
///
/// Converts a list of serializable keyframes back to an AnimationCurve.
///
/// The keyframes to deserialize.
/// The pre-wrap mode for the curve.
/// The post-wrap mode for the curve.
/// An AnimationCurve.
public static AnimationCurve DeserializeCurve(
List keyframes,
WrapMode preWrapMode,
WrapMode postWrapMode
)
{
if (keyframes == null || keyframes.Count == 0)
{
return AnimationCurve.Constant(0f, 1f, AnimationData.DefaultFramesPerSecond);
}
Keyframe[] unityKeyframes = new Keyframe[keyframes.Count];
for (int i = 0; i < keyframes.Count; i++)
{
unityKeyframes[i] = keyframes[i].ToKeyframe();
}
AnimationCurve curve = new(unityKeyframes)
{
preWrapMode = preWrapMode,
postWrapMode = postWrapMode,
};
return curve;
}
}
#endif
}