using System; using UnityEngine; // Shapes © Freya Holmér - https://twitter.com/FreyaHolmer/ // Website & Documentation - https://acegikmo.com/shapes/ namespace Shapes { /// The fill mode to use when drawing textures in a rectangle public enum TextureFillMode { /// Stretches the texture to fit the rectangle, ignoring aspect ratio StretchToFill, /// Scales the texture (preserving aspect ratio) so that it fits within the rectangle ScaleToFit, /// Scales the texture (preserving aspect ratio) so that it covers the rectangle entirely, cropping any overflow ScaleAndCropToFill } /// The size mode to use when drawing textures at a position public enum TextureSizeMode { /// Interprets size as the width of the texture draw region Width, /// Interprets size as the height of the texture draw region Height, /// Interprets size as the longest side of the texture draw region LongestSide, /// Interprets size as the shortest side of the texture draw region ShortestSide, /// Interprets size as pixels per meter, scaling the texture accordingly PixelsPerMeter, /// Interprets size as radius to its corner, scaling the texture accordingly Radius } internal static class TexturePlacement { static readonly Rect fitUvs = new Rect( 0, 0, 1, 1 ); internal static (Rect rect, Rect uvs) Fit( Texture texture, Rect rect, TextureFillMode mode ) { switch( mode ) { case TextureFillMode.StretchToFill: return StretchToFill( rect ); case TextureFillMode.ScaleToFit: return ScaleToFit( texture, rect ); case TextureFillMode.ScaleAndCropToFill: return ScaleAndCropToFill( texture, rect ); default: throw new ArgumentOutOfRangeException( nameof(mode), mode, null ); } } internal static (Rect rect, Rect uvs) Size( Texture texture, Vector2 c, float size, TextureSizeMode mode ) { float aspect = texture.width / (float)texture.height; switch( mode ) { case TextureSizeMode.Width: return FitWidth( c, size, aspect ); case TextureSizeMode.Height: return FitHeight( c, size, aspect ); case TextureSizeMode.LongestSide: return aspect < 1 ? FitHeight( c, size, aspect ) : FitWidth( c, size, aspect ); case TextureSizeMode.ShortestSide: return aspect < 1 ? FitWidth( c, size, aspect ) : FitHeight( c, size, aspect ); case TextureSizeMode.PixelsPerMeter: return TexelSized( texture, c, size ); case TextureSizeMode.Radius: return FitRadius( texture, c, size ); default: throw new ArgumentOutOfRangeException( nameof(mode), mode, null ); } } static (Rect rect, Rect uvs) FitWidth( Vector2 c, float w, float aspect ) => SimpleRect( c, w, w / aspect ); static (Rect rect, Rect uvs) FitHeight( Vector2 c, float h, float aspect ) => SimpleRect( c, h * aspect, h ); static (Rect rect, Rect uvs) FitRadius( Texture tex, Vector2 c, float r ) { Vector2 size = new Vector2( tex.width, tex.height ).normalized * ( r * 2 ); return SimpleRect( c, size.x, size.y ); } static (Rect rect, Rect uvs) SimpleRect( Vector2 c, float w, float h ) => ( RectCnt( c.x, c.y, w, h ), fitUvs ); static Rect RectCnt( float cx, float cy, float w, float h ) => new Rect( cx - w / 2, cy - h / 2, w, h ); static Rect RectCnt( Vector2 c, float w, float h ) => new Rect( c.x - w / 2, c.y - h / 2, w, h ); static (Rect rect, Rect uvs) StretchToFill( Rect rect ) => ( rect, fitUvs ); static (Rect rect, Rect uvs) ScaleToFit( Texture texture, Rect rect ) { float scaleX = rect.width / texture.width; float scaleY = rect.height / texture.height; float scale = Mathf.Min( scaleX, scaleY ); return ( RectCnt( rect.center, texture.width * scale, texture.height * scale ), fitUvs ); } static (Rect rect, Rect uvs) ScaleAndCropToFill( Texture texture, Rect rect ) { float scaleX = rect.width / texture.width; float scaleY = rect.height / texture.height; float scale = Mathf.Max( scaleX, scaleY ); return ( rect, RectCnt( 0.5f, 0.5f, scaleX / scale, scaleY / scale ) ); } static (Rect rect, Rect uvs) TexelSized( Texture texture, Vector2 center, float pixelsPerMeter ) { float scaleX = texture.width / pixelsPerMeter; float scaleY = texture.height / pixelsPerMeter; return SimpleRect( center, scaleX, scaleY ); } } }