Editor/Sprite/SpritesheetImporter/RenderingWidget.cs
using Editor;
using Sandbox;
using System;
using System.Linq;
namespace SpriteTools.SpritesheetImporter;
public class RenderingWidget : SpriteRenderingWidget
{
SpritesheetImporter Importer;
float planeWidth;
float planeHeight;
float startX;
float startY;
float frameWidth;
float frameHeight;
float xSeparation;
float ySeparation;
Vector3 startMovePosition;
bool isCreating = false;
Vector2 startCreatePosition;
Vector2 endCreatePosition;
SpritesheetImporterFrame Selected = null;
RealTimeSince timeSinceLastCornerHover = 0;
public RenderingWidget ( SpritesheetImporter importer, Widget parent ) : base( parent )
{
Importer = importer;
AcceptDrops = false;
IsDraggable = false;
}
protected override void OnMousePress ( MouseEvent e )
{
base.OnMousePress( e );
}
protected override void OnMouseMove ( MouseEvent e )
{
base.OnMouseMove( e );
}
protected override void OnMouseReleased ( MouseEvent e )
{
base.OnMouseReleased( e );
}
void CommitSettings ()
{
}
[EditorEvent.Frame]
public void Frame ()
{
if ( Importer.Settings.NumberOfFrames <= 0 ) Importer.Settings.NumberOfFrames = 1;
if ( Importer.Settings.FramesPerRow <= 0 ) Importer.Settings.FramesPerRow = 1;
if ( Importer.Settings.FramesPerRow > Importer.Settings.NumberOfFrames )
{
Importer.Settings.NumberOfFrames = Importer.Settings.FramesPerRow;
}
UpdateInputs();
if ( timeSinceLastCornerHover > 0.025f )
{
Cursor = CursorShape.Arrow;
}
using ( GizmoInstance.Push() )
{
Gizmo.Draw.Color = Color.White;
Gizmo.Draw.LineThickness = 2f;
planeWidth = 100f * TextureRect.Transform.Scale.y;
planeHeight = 100f * TextureRect.Transform.Scale.x;
startX = Importer.Settings.HorizontalCellOffset * Importer.Settings.FrameWidth + Importer.Settings.HorizontalPixelOffset;
startY = Importer.Settings.VerticalCellOffset * Importer.Settings.FrameHeight + Importer.Settings.VerticalPixelOffset;
startX = ( startX / TextureSize.x * planeWidth ) - ( planeWidth / 2f );
startY = ( startY / TextureSize.y * planeHeight ) - ( planeHeight / 2f );
frameWidth = Importer.Settings.FrameWidth / TextureSize.x * planeWidth;
frameHeight = Importer.Settings.FrameHeight / TextureSize.y * planeHeight;
xSeparation = Importer.Settings.HorizontalSeparation / TextureSize.x * planeWidth;
ySeparation = Importer.Settings.VerticalSeparation / TextureSize.y * planeHeight;
int framesPerRow = Math.Clamp( Importer.Settings.FramesPerRow, 1, (int)TextureSize.x / Importer.Settings.FrameWidth );
var isManualMode = Importer.PageIndex == 1;
if ( !isManualMode )
{
using ( Gizmo.Scope( "import_settings" ) )
{
for ( int i = 0; i < Importer.Settings.NumberOfFrames; i++ )
{
int cellX = i % framesPerRow;
int cellY = i / framesPerRow;
float x = startX + ( cellX ) * ( frameWidth + xSeparation );
float y = startY + ( cellY ) * ( frameHeight + ySeparation );
// Draw Box
Gizmo.Draw.Line( new Vector3( y, x, 0 ), new Vector3( y, x + frameWidth, 0 ) );
Gizmo.Draw.Line( new Vector3( y, x + frameWidth, 0 ), new Vector3( y + frameHeight, x + frameWidth, 0 ) );
Gizmo.Draw.Line( new Vector3( y + frameHeight, x + frameWidth, 0 ), new Vector3( y + frameHeight, x, 0 ) );
Gizmo.Draw.Line( new Vector3( y + frameHeight, x, 0 ), new Vector3( y, x, 0 ) );
}
}
}
if ( isManualMode || Importer.HasModified )
using ( Gizmo.Scope( "committed_frames" ) )
{
Gizmo.Draw.Color = new Color( 0.1f, 0.4f, 1f );
Gizmo.Draw.LineThickness = 3f;
var frames = Importer.Frames.ToList();
foreach ( var frame in frames )
{
FrameControl( frame, isManualMode );
}
}
if ( isManualMode )
{
using ( Gizmo.Scope( "background" ) )
{
var planeBBox = new BBox( new Vector3( -planeHeight / 2f, -planeWidth / 2f, -10 ), new Vector3( planeHeight, planeWidth, 0f ) / 2f );
Gizmo.Hitbox.BBox( planeBBox );
//Gizmo.Draw.Color = Color.Red.WithAlpha( 0.6f );
//Gizmo.Draw.SolidBox( planeBBox );
if ( Gizmo.Pressed.This )
{
var rayPos = Gizmo.CurrentRay.Position;
if ( Gizmo.WasLeftMousePressed )
{
if ( Selected is null )
{
isCreating = true;
startCreatePosition = RayPositionToPixel( rayPos );
}
Selected = null;
}
if ( isCreating )
{
endCreatePosition = RayPositionToPixel( rayPos );
}
}
else
{
if ( isCreating )
{
var start = startCreatePosition;
var end = endCreatePosition;
var size = new Vector2( MathF.Abs( end.y - start.y ), MathF.Abs( end.x - start.x ) );
var topLeft = new Vector2( MathF.Min( start.y, end.y ), MathF.Min( start.x, end.x ) );
if ( size.x > 0 && size.y > 0 )
{
var rect = new Rect( topLeft, size );
var newFrame = new SpritesheetImporterFrame( rect );
Importer.Frames.Add( newFrame );
}
}
isCreating = false;
}
}
if ( isCreating )
{
using ( Gizmo.Scope( "creating" ) )
{
var start = PixelToRayPosition( startCreatePosition );
var end = PixelToRayPosition( endCreatePosition );
var size = ( end - start );
var bbox = BBox.FromPositionAndSize( start + size / 2f, size );
Gizmo.Draw.Color = Color.Yellow;
Gizmo.Draw.LineThickness = 3f;
Gizmo.Draw.LineBBox( bbox );
}
}
}
}
}
void FrameControl ( SpritesheetImporterFrame frame, bool isManualMode )
{
if ( frame is null ) return;
bool isSelected = Selected == frame; ;
using ( Gizmo.Scope( "frame_" + frame.Id ) )
{
var x = startX + frame.Rect.Position.x;
var y = startY + frame.Rect.Position.y;
var width = frame.Rect.Size.x;
var height = frame.Rect.Size.y;
var visualX = x - startX;
var visualY = y - startY;
visualX = ( visualX / TextureSize.x * planeWidth ) - ( planeWidth / 2f );
visualY = ( visualY / TextureSize.y * planeHeight ) - ( planeHeight / 2f );
var visualWidth = width;
var visualHeight = height;
visualWidth = ( visualWidth / TextureSize.x * planeWidth );
visualHeight = ( visualHeight / TextureSize.y * planeHeight );
var bbox = BBox.FromPositionAndSize( new Vector3( visualY + visualHeight / 2f, visualX + visualWidth / 2f, 1f ), new Vector3( visualHeight, visualWidth, 1f ) );
Gizmo.Hitbox.BBox( bbox );
if ( isManualMode )
{
if ( isSelected || Gizmo.Pressed.This )
{
Gizmo.Draw.LineThickness = 4;
Gizmo.Draw.Color = Color.Yellow;
}
var visualRayPos = ( Gizmo.CurrentRay.Position / new Vector3( planeWidth, planeHeight, 1f ) ) * new Vector3( TextureSize.x, TextureSize.y, 1f );
if ( Gizmo.WasLeftMousePressed )
{
startMovePosition = visualRayPos;
}
if ( Gizmo.Pressed.This )
{
Cursor = CursorShape.SizeAll;
timeSinceLastCornerHover = 0f;
var preDelta = startMovePosition - visualRayPos;
//preDelta = ( preDelta * new Vector3( TextureSize.x, TextureSize.y, 1 ) ) / new Vector2( planeWidth, planeHeight );
var deltaf = new Vector2( -preDelta.y, -preDelta.x );
//deltaf = ( deltaf / new Vector2( planeWidth, planeHeight ) );// * new Vector2( TextureSize.x, TextureSize.y );
if ( Math.Abs( deltaf.x ) >= 1f )
{
int xx = (int)deltaf.x;
if ( xx != 0 && CanExpand( frame, xx, 0 ) )
{
startMovePosition += new Vector3( 0, xx );
var rect = frame.Rect;
rect.Position = frame.Rect.Position + new Vector2( xx, 0 );
frame.Rect = rect;
Importer.HasModified = true;
}
}
if ( Math.Abs( deltaf.y ) >= 1f )
{
int yy = (int)deltaf.y;
if ( yy != 0 && CanExpand( frame, 0, yy ) )
{
startMovePosition += new Vector3( yy, 0 );
var rect = frame.Rect;
rect.Position = frame.Rect.Position + new Vector2( 0, yy );
frame.Rect = rect;
Importer.HasModified = true;
}
}
}
if ( Gizmo.IsHovered )
{
Cursor = CursorShape.Finger;
timeSinceLastCornerHover = 0f;
using ( Gizmo.Scope( "hover" ) )
{
Gizmo.Draw.Color = Gizmo.Draw.Color.WithAlpha( 0.5f );
Gizmo.Draw.SolidBox( bbox );
}
if ( Gizmo.WasLeftMousePressed )
{
Selected = frame;
}
else if ( Gizmo.WasRightMousePressed )
{
Importer.Frames.Remove( frame );
Importer.HasModified = true;
}
}
if ( isSelected )
{
using ( Gizmo.Scope( "selected" ) )
{
Gizmo.Draw.Color = Color.Orange;
Gizmo.Draw.LineThickness = 3;
// Draggable Corners
for ( int i = -1; i <= 1; i++ )
{
for ( int j = -1; j <= 1; j++ )
{
if ( i == 0 && j == 0 ) continue;
DraggableCorner( frame, i, j, visualX + visualWidth * ( i + 1 ) / 2f, visualY + visualHeight * ( j + 1 ) / 2f );
}
}
}
}
}
DrawBox( visualX, visualY, visualWidth, visualHeight );
}
}
void DraggableCorner ( SpritesheetImporterFrame frame, int x, int y, float xx, float yy )
{
int currentX = (int)frame.Rect.Position.x;
int currentY = (int)frame.Rect.Position.y;
float xi = currentX + x / 2f;
float yi = currentY + y / 2f;
float width = (int)frame.Rect.Size.x;
float height = (int)frame.Rect.Size.y;
var radius = MathX.Remap( targetZoom, 1, 1000, 0.2f, 4f );
// Can Expand Logic
bool canExpandX = CanExpand( frame, x, 0 );
bool canExpandY = CanExpand( frame, 0, y );
// Can Shrink Logic
bool canShrinkX = !( x != 0 && frame.Rect.Size.x == 1 );
bool canShrinkY = !( y != 0 && frame.Rect.Size.y == 1 );
bool canDrag = ( canExpandX && x != 0 ) || ( canExpandY && y != 0 ) || ( canShrinkX && x != 0 ) || ( canShrinkY && y != 0 );
using ( Gizmo.Scope( $"corner_{x}_{y}" ) )
{
if ( !canDrag )
{
Gizmo.Draw.LineThickness = 1;
Gizmo.Draw.Color = Gizmo.Draw.Color.WithAlpha( 0.2f );
}
if ( canDrag )
{
var bbox = BBox.FromPositionAndSize( new Vector3( yy, xx, 2f ), new Vector3( radius, radius, radius ) * 1.5f );
Gizmo.Hitbox.BBox( bbox );
if ( Gizmo.Pressed.This )
{
Gizmo.Draw.Color = Color.Lerp( Gizmo.Draw.Color, Color.Red, 0.3f );
var preDelta = bbox.Center - Gizmo.CurrentRay.Position;
preDelta = ( preDelta * new Vector3( TextureSize.x, TextureSize.y, 1 ) ) / new Vector2( planeWidth, planeHeight );
var delta = new Vector2( -preDelta.y, -preDelta.x );//Gizmo.Pressed.CursorDelta;
var position = frame.Rect.Position;
var size = frame.Rect.Size;
// Horizontal check
if ( x != 0 )
{
if ( Math.Abs( delta.x ) >= 1f )
{
var am = (int)Math.Abs( delta.x );
// Expanding
if ( Math.Sign( delta.x ) == Math.Sign( x ) )
{
if ( canExpandX )
{
// Expanding Backwards
if ( delta.x < 0 )
{
position -= new Vector2Int( am, 0 );
size += new Vector2Int( am, 0 );
}
else
{
size += new Vector2Int( am, 0 );
}
Importer.HasModified = true;
}
}
// Shinking
else if ( canShrinkX && ( size.x + delta.x ) > 1 )
{
// Shrinking Backwards
if ( delta.x > 0 )
{
size -= new Vector2Int( am, 0 );
position += new Vector2Int( am, 0 );
}
else
{
size -= new Vector2Int( am, 0 );
}
Importer.HasModified = true;
}
}
}
// Vertical check
if ( y != 0 )
{
if ( Math.Abs( delta.y ) >= 1f )
{
var am = (int)Math.Abs( delta.y );
if ( Math.Sign( delta.y ) == Math.Sign( y ) )
{
if ( canExpandY )
{
// Expanding
if ( delta.y < 0 )
{
position -= new Vector2Int( 0, am );
size += new Vector2Int( 0, am );
}
else
{
size += new Vector2Int( 0, am );
}
Importer.HasModified = true;
}
}
else if ( canShrinkY && ( size.y + delta.y ) > 1 )
{
// Shrink
if ( delta.y > 0 )
{
size -= new Vector2Int( 0, am );
position += new Vector2Int( 0, am );
}
else
{
size -= new Vector2Int( 0, am );
}
Importer.HasModified = true;
}
}
}
if ( frame.Rect.Position != position || frame.Rect.Size != size )
{
var rect = frame.Rect;
rect.Position = position;
rect.Size = size;
frame.Rect = rect;
}
}
}
if ( canDrag && Gizmo.IsHovered )
{
Gizmo.Draw.SolidSphere( new Vector3( yy, xx, 10f ), 0.5f, 2, 4 );
Cursor = (x, y) switch
{
(-1, -1 ) => CursorShape.SizeFDiag,
(-1, 0 ) => CursorShape.SizeH,
(-1, 1 ) => CursorShape.SizeBDiag,
(0, -1 ) => CursorShape.SizeV,
(0, 1 ) => CursorShape.SizeV,
(1, -1 ) => CursorShape.SizeBDiag,
(1, 0 ) => CursorShape.SizeH,
(1, 1 ) => CursorShape.SizeFDiag,
_ => CursorShape.Arrow
};
timeSinceLastCornerHover = 0f;
}
else
{
Gizmo.Draw.LineCircle( new Vector3( yy, xx, 10f ), Vector3.Up, radius, 0, 360, 8 );
}
}
}
bool CanExpand ( SpritesheetImporterFrame frame, int x, int y )
{
int currentX = (int)frame.Rect.Position.x;
int currentY = (int)frame.Rect.Position.y;
int width = (int)frame.Rect.Size.x;
int height = (int)frame.Rect.Size.y;
if ( x != 0 )
{
int nextX = currentX + x;
if ( nextX < 0 || ( nextX + width ) >= TextureSize.x ) return false;
}
if ( y != 0 )
{
int nextY = currentY + y;
if ( nextY < 0 || ( nextY + height ) >= TextureSize.y ) return false;
}
return true;
}
void DrawBox ( float x, float y, float width, float height )
{
Gizmo.Draw.Line( new Vector3( y, x, 0 ), new Vector3( y, x + width, 0 ) );
Gizmo.Draw.Line( new Vector3( y, x, 0 ), new Vector3( y + height, x, 0 ) );
Gizmo.Draw.Line( new Vector3( y + height, x, 0 ), new Vector3( y + height, x + width, 0 ) );
Gizmo.Draw.Line( new Vector3( y + height, x + width, 0 ), new Vector3( y, x + width, 0 ) );
}
Vector2 RayPositionToPixel ( Vector3 position )
{
var x = position.y + ( planeWidth / 2f );
var y = position.x + ( planeHeight / 2f );
x = MathF.Floor( ( x / planeWidth ) * TextureSize.x );
y = MathF.Floor( ( y / planeHeight ) * TextureSize.y );
return new Vector2( y, x );
}
Vector3 PixelToRayPosition ( Vector2 position )
{
var x = position.y / TextureSize.x * planeWidth - ( planeWidth / 2f );
var y = position.x / TextureSize.y * planeHeight - ( planeHeight / 2f );
return new Vector3( y, x, 0 );
}
}