Editor/RectEditorExport/RectView.cs
namespace Editor.rectedittemplateexporter;
public enum DragState
{
None,
WaitingForMovement,
Dragging,
}
enum GridSnapMode
{
Nearest,
RoundDown,
RoundUp,
}
public class RectView : Widget
{
private readonly Window Session;
private Document Document => Session.Document;
private Rect DrawRect;
private Pixmap SourceImage;
private Pixmap ScaledImage;
private DragState DragState;
private Vector2 DragStartPos;
private Rect NewRect;
private List<Document.Rectangle> RectanglesUnderCursor;
public RectView( Window session ) : base( session )
{
Session = session;
Name = "Rect View";
WindowTitle = "Rect View";
SetWindowIcon( "space_dashboard" );
MouseTracking = true;
FocusMode = FocusMode.Click;
DrawRect = GetDrawRect();
}
private Vector2 SnapUVToGrid( Vector2 uv )
{
var gridCountX = GetGridCountX();
var gridCountY = GetGridCountY();
var x = (int)(gridCountX * uv.x + 0.5f);
var y = (int)(gridCountY * uv.y + 0.5f);
return new Vector2( x / (float)gridCountX, y / (float)gridCountY );
}
private Vector2 PixelToUV_OnGrid( Vector2 vPixel )
{
return SnapUVToGrid( PixelToUV( vPixel ) );
}
private void DragUpdate( Vector2 mousePos )
{
var minStart = PixelToUV_OnGrid( DragStartPos );
var maxStart = PixelToUV_OnGrid( DragStartPos );
var current = PixelToUV_OnGrid( mousePos );
var min = Vector2.Min( current, minStart );
var max = Vector2.Max( current, maxStart );
NewRect = new Rect( min, max - min );
Update();
}
[EditorEvent.Frame]
protected void OnFrame()
{
FindRectanglesUnderCursor( FromScreen( Application.CursorPosition ) );
}
protected override void OnMouseMove( MouseEvent e )
{
base.OnMouseMove( e );
if ( DragState == DragState.WaitingForMovement && (NewRect.Width > 0.0f || NewRect.Height > 0.0f) )
{
DragState = DragState.Dragging;
}
if ( DragState != DragState.None )
{
DragUpdate( e.LocalPosition );
}
}
protected override void OnMousePress( MouseEvent e )
{
base.OnMousePress( e );
if ( e.Button == MouseButtons.Left )
{
DragState = DragState.WaitingForMovement;
DragStartPos = e.LocalPosition;
}
}
protected override void OnMouseReleased( MouseEvent e )
{
base.OnMouseReleased( e );
if ( e.Button == MouseButtons.Left )
{
if ( DragState == DragState.Dragging )
{
if ( Document is not null && NewRect.Width > 0.0f && NewRect.Height > 0.0f )
{
Document.SelectRectangle( Document.AddRectangle( NewRect ), SelectionOperation.Set );
Session.Snapshot( "Create Rectangle" );
}
}
else
{
Document.SelectRectangle( GetFirstRectangleUnderCursor(), SelectionOperation.Set );
Session.Snapshot( "Select Rectangle" );
}
DragState = DragState.None;
NewRect = default;
}
}
public void SetMaterial( Material material )
{
SourceImage = null;
ScaledImage = null;
if ( material is null )
return;
var texture = material.FirstTexture;
if ( texture is null )
return;
SourceImage = Pixmap.FromTexture( texture, false );
if ( SourceImage is null )
return;
UpdateScaledBackgroundImage();
}
private void UpdateScaledBackgroundImage()
{
ScaledImage = SourceImage?.Resize( DrawRect.Size );
}
protected override void OnResize()
{
base.OnResize();
DrawRect = GetDrawRect();
UpdateScaledBackgroundImage();
}
protected override void OnPaint()
{
Paint.ClearPen();
Paint.SetBrush( Theme.ControlBackground );
Paint.DrawRect( LocalRect );
Paint.SetBrush( Color.Gray );
Paint.DrawRect( DrawRect );
if ( ScaledImage is not null )
{
Paint.Draw( DrawRect, ScaledImage );
}
if ( Session.GridEnabled )
{
DrawGrid();
}
DrawRectangleSet( Document?.Rectangles );
if ( DragState == DragState.Dragging )
{
var topLeft = UVToPixel( NewRect.TopLeft );
var bottomRight = UVToPixel( NewRect.BottomRight );
var newRect = new Rect( topLeft, bottomRight - topLeft );
Paint.ClearBrush();
Paint.SetPen( Color.Yellow, 3 );
Paint.DrawRect( newRect );
}
}
private Vector2 UVToPixel( Vector2 uv )
{
return new Vector2( (int)((uv.x * DrawRect.Width) + DrawRect.Left), (int)((uv.y * DrawRect.Height) + DrawRect.Top) );
}
private Vector2 PixelToUV( Vector2 pixel )
{
return new Vector2( (pixel.x - DrawRect.Left) / DrawRect.Width, (pixel.y - DrawRect.Top) / DrawRect.Height );
}
private int GetGridPower()
{
return Session.GridEnabled ? Session.GridPower : 10;
}
private int GetGridCountX()
{
var width = SourceImage is null ? 512 : System.Math.Max( (int)SourceImage.Width, 1 );
var height = SourceImage is null ? 512 : System.Math.Max( (int)SourceImage.Height, 1 );
var gridPower = GetGridPower();
if ( width >= height )
{
return 1 << gridPower;
}
else
{
return (1 << gridPower) * width / height;
}
}
private int GetGridCountY()
{
var width = SourceImage is null ? 512 : System.Math.Max( (int)SourceImage.Width, 1 );
var height = SourceImage is null ? 512 : System.Math.Max( (int)SourceImage.Height, 1 );
var gridPower = GetGridPower();
if ( height >= width )
{
return 1 << gridPower;
}
else
{
return (1 << gridPower) * height / width;
}
}
public Document.Rectangle GetFirstRectangleUnderCursor()
{
return RectanglesUnderCursor?.FirstOrDefault();
}
public void FindRectanglesUnderCursor( Vector2 mousePos )
{
RectanglesUnderCursor = FindRectanglesContainingPoint( PixelToUV( mousePos ) );
Update();
}
public List<Document.Rectangle> FindRectanglesContainingPoint( Vector2 vPoint )
{
return Document.Rectangles
.Where( rectangle => rectangle.IsPointInRectangle( vPoint ) )
.Select( rectangle => new { Rectangle = rectangle, Distance = rectangle.DistanceFromPointToCenter( vPoint ) } )
.OrderBy( item => item.Distance )
.Select( item => item.Rectangle )
.ToList();
}
private void DrawRectangleSet( IEnumerable<Document.Rectangle> rectangles )
{
if ( rectangles is null )
return;
foreach ( var rectangle in rectangles.Where( x => !Document.IsRectangleSelected( x ) ) )
{
Paint.SetBrush( rectangle.Color.WithAlpha( 0.5f ) );
Paint.SetPen( Color.Black.WithAlpha( 192 / 255.0f ), 1 );
DrawRectangle( rectangle );
}
foreach ( var rectangle in Document.SelectedRectangles )
{
Paint.SetBrush( new Color32( 255, 255, 0, 64 ) );
Paint.SetPen( new Color32( 255, 255, 0 ), 1 );
DrawRectangle( rectangle );
}
var rectangleUnderCursor = GetFirstRectangleUnderCursor();
if ( rectangleUnderCursor is not null && !Document.IsRectangleSelected( rectangleUnderCursor ) )
{
Paint.SetBrush( new Color32( 0, 255, 0, 64 ) );
Paint.SetPen( Color.Green );
DrawRectangle( rectangleUnderCursor );
}
}
private void DrawRectangle( Document.Rectangle rectangle, int nMinInset = 0, int nMaxInset = 0 )
{
if ( rectangle is null )
return;
var minPoint = UVToPixel( rectangle.Min );
var maxPoint = UVToPixel( rectangle.Max );
minPoint += new Vector2( nMinInset, nMinInset );
maxPoint -= new Vector2( nMaxInset + 1, nMaxInset + 1 );
Paint.DrawRect( new Rect( minPoint, maxPoint - minPoint ) );
}
private void DrawGrid()
{
const float gridOpacity = 64 / 255.0f;
var gridCountX = GetGridCountX();
var gridCountY = GetGridCountY();
var stepX = 1.0f / gridCountX;
var stepY = 1.0f / gridCountY;
var rect = DrawRect;
Paint.ClearBrush();
for ( int ix = 0; ix <= gridCountX; ++ix )
{
var u = ix * stepX;
var gx = UVToPixel( new Vector2( u, 0 ) ).x;
if ( gx > rect.Left )
{
Paint.SetPen( new Color( 1, 1, 1, gridOpacity ) );
Paint.DrawLine( new Vector2( gx - 1, rect.Top + 1 ), new Vector2( gx - 1, rect.Height + rect.Top - 2 ) );
}
if ( gx < (rect.Left + rect.Width) )
{
Paint.SetPen( new Color( 0, 0, 0, gridOpacity ) );
Paint.DrawLine( new Vector2( gx, rect.Top + 1 ), new Vector2( gx, rect.Height + rect.Top - 2 ) );
}
}
for ( int iy = 0; iy <= gridCountY; ++iy )
{
var v = iy * stepY;
var gy = UVToPixel( new Vector2( 0, v ) ).y;
if ( gy > rect.Top )
{
Paint.SetPen( new Color( 1, 1, 1, gridOpacity ) );
if ( gy == (rect.Top + rect.Height) )
{
Paint.DrawLine( new Vector2( rect.Left, gy - 1 ), new Vector2( rect.Width + rect.Left - 1, gy - 1 ) );
}
else
{
Paint.DrawLine( new Vector2( rect.Left + 1, gy - 1 ), new Vector2( rect.Width + rect.Left - 2, gy - 1 ) );
}
}
if ( gy < (rect.Top + rect.Height) )
{
Paint.SetPen( new Color( 0, 0, 0, gridOpacity ) );
if ( gy == rect.Top )
{
Paint.DrawLine( new Vector2( rect.Left, gy ), new Vector2( rect.Width + rect.Left - 1, gy ) );
}
else
{
Paint.DrawLine( new Vector2( rect.Left + 1, gy ), new Vector2( rect.Width + rect.Left - 2, gy ) );
}
}
}
}
private Rect GetDrawRect()
{
const int marigin = 16;
const int drawSnapSize = 4;
var imageSize = SourceImage is null ? 0 : SourceImage.Size;
var widgetWidth = System.Math.Max( (int)Width - (marigin * 2), 128 );
var widgetHeight = System.Math.Max( (int)Height - (marigin * 2), 128 );
var imageWidth = System.Math.Max( (int)imageSize.x, 1 );
var imageHeight = System.Math.Max( (int)imageSize.y, 1 );
int drawWidth;
int drawHeight;
if ( (imageWidth > 0) && (imageHeight > 0) )
{
var aspect = imageWidth / (float)imageHeight;
var relativeWidth = (int)(widgetWidth / System.MathF.Max( aspect, 1.0f ));
var relativeHeight = (int)(widgetHeight * System.MathF.Min( aspect, 1.0f ));
if ( relativeWidth <= relativeHeight )
{
drawWidth = widgetWidth;
drawHeight = widgetWidth * imageHeight / imageWidth;
}
else
{
drawHeight = widgetHeight;
drawWidth = widgetHeight * imageWidth / imageHeight;
}
}
else
{
var drawSize = System.Math.Min( widgetWidth, widgetHeight );
drawHeight = drawSize;
drawWidth = drawSize;
}
drawWidth = drawWidth / drawSnapSize * drawSnapSize;
drawHeight = drawHeight / drawSnapSize * drawSnapSize;
return new Rect( marigin, marigin, drawWidth, drawHeight );
}
}