2015-12-31 12:21:44 +00:00
#region = = = = = = = = = = = = = = = = = = Namespaces
using System ;
using System.Collections.Generic ;
using System.Drawing ;
using System.Windows.Forms ;
using CodeImp.DoomBuilder.Config ;
using CodeImp.DoomBuilder.Editing ;
using CodeImp.DoomBuilder.Rendering ;
using CodeImp.DoomBuilder.Geometry ;
using CodeImp.DoomBuilder.Actions ;
using CodeImp.DoomBuilder.Windows ;
using CodeImp.DoomBuilder.Map ;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
[ EditMode ( DisplayName = "Draw Rectangle Mode" ,
SwitchAction = "drawrectanglemode" ,
ButtonImage = "DrawRectangleMode.png" , //mxd
ButtonOrder = int . MinValue + 3 , //mxd
ButtonGroup = "000_drawing" , //mxd
AllowCopyPaste = false ,
Volatile = true ,
Optional = false ) ]
public class DrawRectangleMode : DrawGeometryMode
{
#region = = = = = = = = = = = = = = = = = = Variables
protected HintLabel hintlabel ;
2022-11-25 17:14:35 +00:00
protected int bevelwidth ;
protected int currentbevelwidth ;
2015-12-31 12:21:44 +00:00
protected int subdivisions ;
2022-11-25 17:14:35 +00:00
protected int maxsubdivisions ;
protected int minsubdivisions ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
protected string undoname = "Rectangle draw" ;
protected string shapename = "rectangle" ;
2015-12-31 12:21:44 +00:00
protected Vector2D start ;
protected Vector2D end ;
protected int width ;
protected int height ;
2022-11-25 17:14:35 +00:00
protected int minpointscount ;
protected bool alwaysrendershapehints ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
// Interface
2015-12-31 12:21:44 +00:00
private DrawRectangleOptionsPanel panel ;
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
public DrawRectangleMode ( )
{
snaptogrid = true ;
usefourcardinaldirections = true ;
}
public override void Dispose ( )
{
// Not already disposed?
if ( ! isdisposed )
{
// Clean up
if ( hintlabel ! = null ) hintlabel . Dispose ( ) ;
// Done
base . Dispose ( ) ;
}
}
#endregion
#region = = = = = = = = = = = = = = = = = = Settings panel
2022-11-25 17:14:35 +00:00
protected override void SetupInterface ( )
2015-12-31 12:21:44 +00:00
{
2022-11-25 17:14:35 +00:00
maxsubdivisions = 16 ;
minpointscount = 4 ;
// Load stored settings
subdivisions = General . Clamp ( General . Settings . ReadPluginSetting ( "drawrectanglemode.subdivisions" , 0 ) , minsubdivisions , maxsubdivisions ) ;
bevelwidth = General . Settings . ReadPluginSetting ( "drawrectanglemode.bevelwidth" , 0 ) ;
currentbevelwidth = bevelwidth ;
2015-12-31 12:21:44 +00:00
//Add options docker
panel = new DrawRectangleOptionsPanel ( ) ;
2022-11-25 17:14:35 +00:00
panel . MaxSubdivisions = maxsubdivisions ;
panel . MinSubdivisions = minsubdivisions ;
2015-12-31 12:21:44 +00:00
panel . MaxBevelWidth = ( int ) General . Map . FormatInterface . MaxCoordinate ;
panel . MinBevelWidth = ( int ) General . Map . FormatInterface . MinCoordinate ;
2022-11-25 17:14:35 +00:00
panel . BevelWidth = bevelwidth ;
panel . Subdivisions = subdivisions ;
2015-12-31 12:21:44 +00:00
panel . OnValueChanged + = OptionsPanelOnValueChanged ;
2022-11-25 17:14:35 +00:00
panel . OnContinuousDrawingChanged + = OnContinuousDrawingChanged ;
// Needs to be set after adding the OnContinuousDrawingChanged event...
panel . ContinuousDrawing = General . Settings . ReadPluginSetting ( "drawrectanglemode.continuousdrawing" , false ) ;
2015-12-31 12:21:44 +00:00
}
2022-11-25 17:14:35 +00:00
protected override void AddInterface ( )
2015-12-31 12:21:44 +00:00
{
panel . Register ( ) ;
}
2022-11-25 17:14:35 +00:00
protected override void RemoveInterface ( )
2015-12-31 12:21:44 +00:00
{
2022-11-25 17:14:35 +00:00
// Store settings
General . Settings . WritePluginSetting ( "drawrectanglemode.subdivisions" , subdivisions ) ;
General . Settings . WritePluginSetting ( "drawrectanglemode.bevelwidth" , bevelwidth ) ;
General . Settings . WritePluginSetting ( "drawrectanglemode.continuousdrawing" , panel . ContinuousDrawing ) ;
// Remove the buttons
2015-12-31 12:21:44 +00:00
panel . Unregister ( ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
override protected void Update ( )
{
PixelColor stitchcolor = General . Colors . Highlight ;
PixelColor losecolor = General . Colors . Selection ;
snaptocardinaldirection = General . Interface . ShiftState & & General . Interface . AltState ; //mxd
snaptogrid = ( snaptocardinaldirection | | General . Interface . ShiftState ^ General . Interface . SnapToGrid ) ;
snaptonearest = General . Interface . CtrlState ^ General . Interface . AutoMerge ;
DrawnVertex curp = GetCurrentPosition ( ) ;
float vsize = ( renderer . VertexSize + 1.0f ) / renderer . Scale ;
// Render drawing lines
if ( renderer . StartOverlay ( true ) )
{
PixelColor color = snaptonearest ? stitchcolor : losecolor ;
if ( points . Count = = 1 )
{
UpdateReferencePoints ( points [ 0 ] , curp ) ;
Vector2D [ ] shape = GetShape ( start , end ) ;
//render shape
for ( int i = 1 ; i < shape . Length ; i + + )
renderer . RenderLine ( shape [ i - 1 ] , shape [ i ] , LINE_THICKNESS , color , true ) ;
//vertices
for ( int i = 0 ; i < shape . Length ; i + + )
renderer . RenderRectangleFilled ( new RectangleF ( shape [ i ] . x - vsize , shape [ i ] . y - vsize , vsize * 2.0f , vsize * 2.0f ) , color , true ) ;
//and labels
Vector2D [ ] labelCoords = new [ ] { start , new Vector2D ( end . x , start . y ) , end , new Vector2D ( start . x , end . y ) , start } ;
for ( int i = 1 ; i < 5 ; i + + )
{
labels [ i - 1 ] . Move ( labelCoords [ i ] , labelCoords [ i - 1 ] ) ;
renderer . RenderText ( labels [ i - 1 ] . TextLabel ) ;
}
//got beveled corners?
2022-11-25 17:14:35 +00:00
if ( alwaysrendershapehints | | shape . Length > minpointscount + 1 )
2015-12-31 12:21:44 +00:00
{
//render hint
if ( width > 64 * vsize & & height > 16 * vsize )
{
hintlabel . Move ( start , end ) ;
hintlabel . Text = GetHintText ( ) ;
renderer . RenderText ( hintlabel . TextLabel ) ;
}
//and shape corners
for ( int i = 0 ; i < 4 ; i + + )
renderer . RenderRectangleFilled ( new RectangleF ( labelCoords [ i ] . x - vsize , labelCoords [ i ] . y - vsize , vsize * 2.0f , vsize * 2.0f ) , General . Colors . InfoLine , true ) ;
}
}
else
{
// Render vertex at cursor
renderer . RenderRectangleFilled ( new RectangleF ( curp . pos . x - vsize , curp . pos . y - vsize , vsize * 2.0f , vsize * 2.0f ) , color , true ) ;
}
// Done
renderer . Finish ( ) ;
}
// Done
renderer . Present ( ) ;
}
protected virtual Vector2D [ ] GetShape ( Vector2D pStart , Vector2D pEnd )
{
//no shape
if ( pStart = = pEnd )
{
2022-11-25 17:14:35 +00:00
currentbevelwidth = 0 ;
2015-12-31 12:21:44 +00:00
return new Vector2D [ 0 ] ;
}
//line
if ( pEnd . x = = pStart . x | | pEnd . y = = pStart . y )
{
2022-11-25 17:14:35 +00:00
currentbevelwidth = 0 ;
2015-12-31 12:21:44 +00:00
return new [ ] { pStart , pEnd } ;
}
//no corners
2022-11-25 17:14:35 +00:00
if ( bevelwidth = = 0 )
2015-12-31 12:21:44 +00:00
{
2022-11-25 17:14:35 +00:00
currentbevelwidth = 0 ;
2015-12-31 12:21:44 +00:00
return new [ ] { pStart , new Vector2D ( ( int ) pStart . x , ( int ) pEnd . y ) , pEnd , new Vector2D ( ( int ) pEnd . x , ( int ) pStart . y ) , pStart } ;
}
//got corners. TODO: check point order
bool reverse = false ;
2022-11-25 17:14:35 +00:00
currentbevelwidth = Math . Min ( Math . Abs ( bevelwidth ) , Math . Min ( width , height ) / 2 ) ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
if ( bevelwidth < 0 )
2015-12-31 12:21:44 +00:00
{
2022-11-25 17:14:35 +00:00
currentbevelwidth * = - 1 ;
2015-12-31 12:21:44 +00:00
reverse = true ;
}
List < Vector2D > shape = new List < Vector2D > ( ) ;
//top-left corner
2022-11-25 17:14:35 +00:00
shape . AddRange ( GetCornerPoints ( pStart , currentbevelwidth , currentbevelwidth , ! reverse ) ) ;
2015-12-31 12:21:44 +00:00
//top-right corner
2022-11-25 17:14:35 +00:00
shape . AddRange ( GetCornerPoints ( new Vector2D ( pEnd . x , pStart . y ) , - currentbevelwidth , currentbevelwidth , reverse ) ) ;
2015-12-31 12:21:44 +00:00
//bottom-right corner
2022-11-25 17:14:35 +00:00
shape . AddRange ( GetCornerPoints ( pEnd , - currentbevelwidth , - currentbevelwidth , ! reverse ) ) ;
2015-12-31 12:21:44 +00:00
//bottom-left corner
2022-11-25 17:14:35 +00:00
shape . AddRange ( GetCornerPoints ( new Vector2D ( pStart . x , pEnd . y ) , currentbevelwidth , - currentbevelwidth , reverse ) ) ;
2015-12-31 12:21:44 +00:00
//closing point
shape . Add ( shape [ 0 ] ) ;
return shape . ToArray ( ) ;
}
private Vector2D [ ] GetCornerPoints ( Vector2D startPoint , int bevel_width , int bevel_height , bool reverse )
{
Vector2D [ ] points ;
2022-11-25 17:14:35 +00:00
Vector2D center = ( bevelwidth > 0 ? new Vector2D ( startPoint . x + bevel_width , startPoint . y + bevel_height ) : startPoint ) ;
2015-12-31 12:21:44 +00:00
float curAngle = Angle2D . PI ;
int steps = subdivisions + 2 ;
points = new Vector2D [ steps ] ;
float stepAngle = Angle2D . PIHALF / ( subdivisions + 1 ) ;
for ( int i = 0 ; i < steps ; i + + )
{
points [ i ] = new Vector2D ( center . x + ( float ) Math . Sin ( curAngle ) * bevel_width , center . y + ( float ) Math . Cos ( curAngle ) * bevel_height ) ;
curAngle + = stepAngle ;
}
if ( reverse ) Array . Reverse ( points ) ;
return points ;
}
protected virtual string GetHintText ( )
{
2022-11-25 17:14:35 +00:00
return "BVL: " + bevelwidth + "; SUB: " + subdivisions ;
2015-12-31 12:21:44 +00:00
}
2022-11-25 17:14:35 +00:00
// Update top-left and bottom-right points, which define drawing shape
2015-12-31 12:21:44 +00:00
private void UpdateReferencePoints ( DrawnVertex p1 , DrawnVertex p2 )
{
if ( ! p1 . pos . IsFinite ( ) | | ! p2 . pos . IsFinite ( ) ) return ;
2022-11-25 17:14:35 +00:00
// Make sure start always stays at left and up from the end
2015-12-31 12:21:44 +00:00
if ( p1 . pos . x < p2 . pos . x )
{
start . x = p1 . pos . x ;
end . x = p2 . pos . x ;
}
else
{
start . x = p2 . pos . x ;
end . x = p1 . pos . x ;
}
if ( p1 . pos . y < p2 . pos . y )
{
start . y = p1 . pos . y ;
end . y = p2 . pos . y ;
}
else
{
start . y = p2 . pos . y ;
end . y = p1 . pos . y ;
}
2022-11-25 17:14:35 +00:00
// Update size
2015-12-31 12:21:44 +00:00
width = ( int ) ( end . x - start . x ) ;
height = ( int ) ( end . y - start . y ) ;
}
// This draws a point at a specific location
override public bool DrawPointAt ( Vector2D pos , bool stitch , bool stitchline )
{
if ( pos . x < General . Map . Config . LeftBoundary | | pos . x > General . Map . Config . RightBoundary | |
pos . y > General . Map . Config . TopBoundary | | pos . y < General . Map . Config . BottomBoundary )
return false ;
DrawnVertex newpoint = new DrawnVertex ( ) ;
newpoint . pos = pos ;
newpoint . stitch = true ; //stitch
newpoint . stitchline = stitchline ;
points . Add ( newpoint ) ;
if ( points . Count = = 1 ) //add point and labels
{
labels . AddRange ( new [ ] { new LineLengthLabel ( false , true ) , new LineLengthLabel ( false , true ) , new LineLengthLabel ( false , true ) , new LineLengthLabel ( false , true ) } ) ;
hintlabel = new HintLabel ( General . Colors . InfoLine ) ;
Update ( ) ;
}
else if ( points [ 0 ] . pos = = points [ 1 ] . pos ) //nothing is drawn
{
points = new List < DrawnVertex > ( ) ;
FinishDraw ( ) ;
}
else
{
//create vertices for final shape.
UpdateReferencePoints ( points [ 0 ] , newpoint ) ;
points = new List < DrawnVertex > ( ) ; //clear points
Vector2D [ ] shape = GetShape ( start , end ) ;
foreach ( Vector2D t in shape ) base . DrawPointAt ( t , true , true ) ;
FinishDraw ( ) ;
}
return true ;
}
override public void RemovePoint ( )
{
if ( points . Count > 0 ) points . RemoveAt ( points . Count - 1 ) ;
if ( labels . Count > 0 ) labels = new List < LineLengthLabel > ( ) ;
Update ( ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Events
override public void OnAccept ( )
{
Cursor . Current = Cursors . AppStarting ;
General . Settings . FindDefaultDrawSettings ( ) ;
// When we have a rectangle or a line
2022-11-25 17:14:35 +00:00
if ( points . Count > minpointscount | | points . Count = = 2 )
2015-12-31 12:21:44 +00:00
{
// Make undo for the draw
2022-11-25 17:14:35 +00:00
General . Map . UndoRedo . CreateUndo ( undoname ) ;
2015-12-31 12:21:44 +00:00
// Make an analysis and show info
string [ ] adjectives = new [ ] { "gloomy" , "sad" , "unhappy" , "lonely" , "troubled" , "depressed" , "heartsick" , "glum" , "pessimistic" , "bitter" , "downcast" } ; // aaand my english vocabulary ends here :)
string word = adjectives [ new Random ( ) . Next ( adjectives . Length - 1 ) ] ;
string a = ( word [ 0 ] = = 'u' ? "an " : "a " ) ;
2022-11-25 17:14:35 +00:00
General . Interface . DisplayStatus ( StatusType . Action , "Created " + a + word + " " + shapename + "." ) ;
2015-12-31 12:21:44 +00:00
// Make the drawing
2022-11-25 17:14:35 +00:00
if ( Tools . DrawLines ( points , true , BuilderPlug . Me . AutoAlignTextureOffsetsOnCreate ) )
2015-12-31 12:21:44 +00:00
{
2022-11-25 17:14:35 +00:00
// Snap to map format accuracy
General . Map . Map . SnapAllToAccuracy ( ) ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
// Clear selection
General . Map . Map . ClearAllSelected ( ) ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
// Update cached values
General . Map . Map . Update ( ) ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
// Edit new sectors?
List < Sector > newsectors = General . Map . Map . GetMarkedSectors ( true ) ;
if ( BuilderPlug . Me . EditNewSector & & ( newsectors . Count > 0 ) )
General . Interface . ShowEditSectors ( newsectors ) ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
// Update the used textures
General . Map . Data . UpdateUsedTextures ( ) ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
//mxd
General . Map . Renderer2D . UpdateExtraFloorFlag ( ) ;
2015-12-31 12:21:44 +00:00
2022-11-25 17:14:35 +00:00
// Map is changed
General . Map . IsChanged = true ;
}
else
{
// Drawing failed
// NOTE: I have to call this twice, because the first time only cancels this volatile mode
General . Map . UndoRedo . WithdrawUndo ( ) ;
General . Map . UndoRedo . WithdrawUndo ( ) ;
}
2015-12-31 12:21:44 +00:00
}
// Done
Cursor . Current = Cursors . Default ;
2022-11-25 17:14:35 +00:00
if ( continuousdrawing )
{
// Reset settings
points . Clear ( ) ;
labels . Clear ( ) ;
// Redraw display
General . Interface . RedrawDisplay ( ) ;
}
else
{
// Return to original mode
General . Editing . ChangeMode ( General . Editing . PreviousStableMode . Name ) ;
}
2015-12-31 12:21:44 +00:00
}
public override void OnHelp ( )
{
General . ShowHelp ( "/gzdb/features/classic_modes/mode_drawrect.html" ) ;
}
private void OptionsPanelOnValueChanged ( object sender , EventArgs eventArgs )
{
2022-11-25 17:14:35 +00:00
bevelwidth = panel . BevelWidth ;
2015-12-31 12:21:44 +00:00
subdivisions = panel . Subdivisions ;
Update ( ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Actions
[BeginAction("increasesubdivlevel")]
protected virtual void IncreaseSubdivLevel ( )
{
2022-11-25 17:14:35 +00:00
if ( subdivisions < maxsubdivisions )
2015-12-31 12:21:44 +00:00
{
subdivisions + + ;
panel . Subdivisions = subdivisions ;
Update ( ) ;
}
}
[BeginAction("decreasesubdivlevel")]
protected virtual void DecreaseSubdivLevel ( )
{
2022-11-25 17:14:35 +00:00
if ( subdivisions > minsubdivisions )
2015-12-31 12:21:44 +00:00
{
subdivisions - - ;
panel . Subdivisions = subdivisions ;
Update ( ) ;
}
}
[BeginAction("increasebevel")]
protected virtual void IncreaseBevel ( )
{
2022-11-25 17:14:35 +00:00
if ( points . Count < 2 | | currentbevelwidth = = bevelwidth | | bevelwidth < 0 )
2015-12-31 12:21:44 +00:00
{
2022-11-25 17:14:35 +00:00
bevelwidth = Math . Min ( bevelwidth + General . Map . Grid . GridSize , panel . MaxBevelWidth ) ;
panel . BevelWidth = bevelwidth ;
2015-12-31 12:21:44 +00:00
Update ( ) ;
}
}
[BeginAction("decreasebevel")]
protected virtual void DecreaseBevel ( )
{
2022-11-25 17:14:35 +00:00
if ( currentbevelwidth = = bevelwidth | | bevelwidth > 0 )
2015-12-31 12:21:44 +00:00
{
2022-11-25 17:14:35 +00:00
bevelwidth = Math . Max ( bevelwidth - General . Map . Grid . GridSize , panel . MinBevelWidth ) ;
panel . BevelWidth = bevelwidth ;
2015-12-31 12:21:44 +00:00
Update ( ) ;
}
}
#endregion
}
}