2009-04-19 18:07:22 +00:00
#region = = = = = = = = = = = = = = = = = = Copyright ( c ) 2007 Pascal vd Heiden
/ *
* Copyright ( c ) 2007 Pascal vd Heiden , www . codeimp . com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* /
#endregion
#region = = = = = = = = = = = = = = = = = = Namespaces
using System ;
using System.Collections.Generic ;
2015-06-01 09:53:57 +00:00
using System.Drawing ;
2009-04-19 18:07:22 +00:00
using System.Windows.Forms ;
using CodeImp.DoomBuilder.Actions ;
using CodeImp.DoomBuilder.Config ;
2009-08-21 08:38:24 +00:00
using CodeImp.DoomBuilder.Controls ;
2015-06-01 09:53:57 +00:00
using CodeImp.DoomBuilder.Data ;
using CodeImp.DoomBuilder.Editing ;
using CodeImp.DoomBuilder.Geometry ;
using CodeImp.DoomBuilder.Map ;
using CodeImp.DoomBuilder.Rendering ;
using CodeImp.DoomBuilder.Types ;
using CodeImp.DoomBuilder.Windows ;
2009-04-19 18:07:22 +00:00
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
[ EditMode ( DisplayName = "Edit Selection Mode" ,
2011-12-04 10:34:10 +00:00
SwitchAction = "editselectionmode" ,
ButtonImage = "Selection3.png" ,
2014-02-26 14:11:06 +00:00
ButtonOrder = 1 ,
ButtonGroup = "002_modify" ,
2009-04-19 18:07:22 +00:00
Volatile = true ,
UseByDefault = true ,
Optional = false ) ]
public class EditSelectionMode : BaseClassicMode
{
#region = = = = = = = = = = = = = = = = = = Enums
2013-12-20 09:24:43 +00:00
private enum ModifyMode
2009-04-19 18:07:22 +00:00
{
None ,
Dragging ,
Resizing ,
Rotating
}
2013-12-20 09:24:43 +00:00
private enum Grip
2009-04-19 18:07:22 +00:00
{
None ,
Main ,
SizeN ,
SizeS ,
SizeE ,
SizeW ,
RotateLT ,
RotateRT ,
RotateRB ,
RotateLB
}
2016-05-20 15:04:00 +00:00
internal enum HeightAdjustMode
{
NONE ,
ADJUST_FLOORS ,
ADJUST_CEILINGS ,
ADJUST_BOTH ,
}
2009-04-19 18:07:22 +00:00
#endregion
2015-06-01 09:53:57 +00:00
#region = = = = = = = = = = = = = = = = = = Structs ( mxd )
private struct SectorTextureInfo
{
public readonly SurfaceTextureInfo Floor ;
public readonly SurfaceTextureInfo Ceiling ;
public SectorTextureInfo ( Sector s )
{
// Get transform properties
Removed "Paste Properties Options" action.
Added "Paste Properties Special" actions in "Classic" and "Visual" categories. They work the same way as "Paste Special" action.
Added: "Copy Properties", "Paste Properties" and "Paste Properties Special" options are now shown in the Edit menu if current classic mode supports them.
Changed, Paste Properties Special window: only options relevant to current map format are now displayed.
Changed, Paste Properties Special window, UDMF: all UI-managed options are now available.
Fixed: MAPINFO parser was unable to process "include" directives.
Fixed, General interface: selection info was reset to "Nothing selected" after few seconds regardless of current selection.
Fixed, Visual mode: thing bounding boxes were not updated when changing things positions using Randomize mode.
Fixed, Visual mode: event lines were displayed at incorrect height when entering Visual mode for the first time.
Fixed, Texture Browser window: when MixTexturesFlats Game Configuration option is disabled, textures/flats are no longer shown in the Used group when flats/textures with the same names are used in the map.
Fixed(?): probably fixed an exception some users reported when trying to initialize a Classic mode after switching from Visual mode with "Sync cameras" option enabled.
Changed, Game configurations, Thing Categories: a block must have at least one thing category property to be recognized as a thing category.
Changed, Visplane Explorer: the plugin now outputs more info when it fails to initialize vpo.dll.
Cosmetic, Thing Edit window, Doom/Hexen map format: adjusted UI layout so thing flags control no longer displays scrollbars in Hexen map format.
Internal: merged methods from UDMFTools into UniFields, removed UDMFTools.
Updated Inno Setup script (added VC++ 2008 SP1 distributive).
Updated ZDoom_DECORATE.cfg (A_CheckBlock).
Updated documentation (added "System Requirements" page).
2015-10-09 12:38:12 +00:00
Floor . Offset = new Vector2D ( UniFields . GetFloat ( s . Fields , "xpanningfloor" , 0f ) , UniFields . GetFloat ( s . Fields , "ypanningfloor" , 0f ) ) ;
Ceiling . Offset = new Vector2D ( UniFields . GetFloat ( s . Fields , "xpanningceiling" , 0f ) , UniFields . GetFloat ( s . Fields , "ypanningceiling" , 0f ) ) ;
Floor . Scale = new Vector2D ( UniFields . GetFloat ( s . Fields , "xscalefloor" , 1.0f ) , - UniFields . GetFloat ( s . Fields , "yscalefloor" , 1.0f ) ) ;
Ceiling . Scale = new Vector2D ( UniFields . GetFloat ( s . Fields , "xscaleceiling" , 1.0f ) , - UniFields . GetFloat ( s . Fields , "yscaleceiling" , 1.0f ) ) ;
Floor . Rotation = Angle2D . DegToRad ( UniFields . GetFloat ( s . Fields , "rotationfloor" , 0f ) ) ;
Ceiling . Rotation = Angle2D . DegToRad ( UniFields . GetFloat ( s . Fields , "rotationceiling" , 0f ) ) ;
2015-06-01 09:53:57 +00:00
// Get texture sizes
Floor . TextureSize = GetTextureSize ( s . LongFloorTexture ) ;
Ceiling . TextureSize = GetTextureSize ( s . LongCeilTexture ) ;
// Surface name
Floor . Part = "floor" ;
Ceiling . Part = "ceiling" ;
}
private static Size GetTextureSize ( long hash )
{
ImageData texture = General . Map . Data . GetFlatImage ( hash ) ;
if ( ( texture = = null ) | | ( texture = = General . Map . Data . WhiteTexture ) | |
( texture . Width < = 0 ) | | ( texture . Height < = 0 ) | | ! texture . IsImageLoaded )
{
return new Size ( ) ;
}
return new Size ( ( int ) Math . Round ( texture . ScaledWidth ) , ( int ) Math . Round ( texture . ScaledHeight ) ) ;
}
}
private struct SurfaceTextureInfo
{
public Vector2D Offset ;
public Vector2D Scale ;
public Size TextureSize ;
public float Rotation ;
public string Part ;
}
#endregion
2009-04-19 18:07:22 +00:00
#region = = = = = = = = = = = = = = = = = = Constants
private const float GRIP_SIZE = 9.0f ;
private const float ZERO_SIZE_ADDITION = 20.0f ;
private const byte RECTANGLE_ALPHA = 60 ;
private const byte EXTENSION_LINE_ALPHA = 150 ;
private readonly Cursor [ ] RESIZE_CURSORS = { Cursors . SizeNS , Cursors . SizeNWSE , Cursors . SizeWE , Cursors . SizeNESW } ;
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
// Modes
2013-12-20 09:24:43 +00:00
private bool modealreadyswitching ;
2015-09-04 12:44:09 +00:00
private bool clearselection ; //mxd
2013-12-20 09:24:43 +00:00
private bool pasting ;
2016-01-18 20:37:21 +00:00
private bool autodrag ; //mxd
2009-07-09 14:03:47 +00:00
private PasteOptions pasteoptions ;
2016-05-20 15:04:00 +00:00
private HeightAdjustMode heightadjustmode ; //mxd
2009-04-19 18:07:22 +00:00
2009-08-21 08:38:24 +00:00
// Docker
private EditSelectionPanel panel ;
private Docker docker ;
2009-04-19 18:07:22 +00:00
// Highlighted vertex
2009-07-06 08:34:14 +00:00
private MapElement highlighted ;
2009-04-19 18:07:22 +00:00
private Vector2D highlightedpos ;
// Selection
private ICollection < Vertex > selectedvertices ;
private ICollection < Thing > selectedthings ;
2015-06-01 09:53:57 +00:00
private Dictionary < Sector , SectorTextureInfo > selectedsectors ; //mxd
2014-02-20 12:36:09 +00:00
private List < int > fixedrotationthingtypes ; //mxd
2009-04-19 18:07:22 +00:00
private ICollection < Linedef > selectedlines ;
private List < Vector2D > vertexpos ;
private List < Vector2D > thingpos ;
private List < float > thingangle ;
private ICollection < Vertex > unselectedvertices ;
private ICollection < Linedef > unselectedlines ;
2016-04-25 14:48:39 +00:00
private ICollection < Linedef > unstablelines ; //mxd
2020-04-04 20:02:13 +00:00
private Dictionary < Sector , float [ ] > slopeheights ;
2009-04-19 18:07:22 +00:00
// Modification
private float rotation ;
private Vector2D offset ;
private Vector2D size ;
2015-06-01 09:53:57 +00:00
private Vector2D scale = new Vector2D ( 1.0f , 1.0f ) ; //mxd
2009-04-19 18:07:22 +00:00
private Vector2D baseoffset ;
private Vector2D basesize ;
private bool linesflipped ;
2015-06-19 19:19:41 +00:00
private bool usepreciseposition ; //mxd
2015-06-01 09:53:57 +00:00
//mxd. Texture modification
private static bool transformflooroffsets ;
private static bool transformceiloffsets ;
private static bool rotateflooroffsets ;
private static bool rotateceiloffsets ;
private static bool scaleflooroffsets ;
private static bool scaleceiloffsets ;
private Vector2D selectioncenter ;
private Vector2D selectionbasecenter ;
2009-04-19 18:07:22 +00:00
// Modifying Modes
private ModifyMode mode ;
private Vector2D dragoffset ;
private Vector2D resizefilter ;
private Vector2D resizevector ;
private Vector2D edgevector ;
private Line2D resizeaxis ;
private int stickcorner ;
private float rotategripangle ;
private bool autopanning ;
// Rectangle components
private Vector2D [ ] originalcorners ; // lefttop, righttop, rightbottom, leftbottom
private Vector2D [ ] corners ;
private FlatVertex [ ] cornerverts ;
private RectangleF [ ] resizegrips ; // top, right, bottom, left
private RectangleF [ ] rotategrips ; // lefttop, righttop, rightbottom, leftbottom
private Line2D extensionline ;
// Options
private bool snaptogrid ; // SHIFT to toggle
private bool snaptonearest ; // CTRL to enable
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
2009-05-17 14:00:36 +00:00
public override object HighlightedObject { get { return highlighted ; } }
2009-04-19 18:07:22 +00:00
public bool Pasting { get { return pasting ; } set { pasting = value ; } }
2009-07-09 14:03:47 +00:00
public PasteOptions PasteOptions { get { return pasteoptions ; } set { pasteoptions = value . Copy ( ) ; } }
2015-06-19 19:19:41 +00:00
//mxd. Modification
internal bool UsePrecisePosition { get { return usepreciseposition ; } set { usepreciseposition = value ; } }
2015-06-01 09:53:57 +00:00
//mxd. Texture offset properties
internal bool TransformFloorOffsets { get { return transformflooroffsets ; } set { transformflooroffsets = value ; UpdateAllChanges ( ) ; } }
internal bool TransformCeilingOffsets { get { return transformceiloffsets ; } set { transformceiloffsets = value ; UpdateAllChanges ( ) ; } }
internal bool RotateFloorOffsets { get { return rotateflooroffsets ; } set { rotateflooroffsets = value ; UpdateAllChanges ( ) ; } }
internal bool RotateCeilingOffsets { get { return rotateceiloffsets ; } set { rotateceiloffsets = value ; UpdateAllChanges ( ) ; } }
internal bool ScaleFloorOffsets { get { return scaleflooroffsets ; } set { scaleflooroffsets = value ; UpdateAllChanges ( ) ; } }
internal bool ScaleCeilingOffsets { get { return scaleceiloffsets ; } set { scaleceiloffsets = value ; UpdateAllChanges ( ) ; } }
2016-05-20 15:04:00 +00:00
//mxd. Height offset mode
internal HeightAdjustMode SectorHeightAdjustMode { get { return heightadjustmode ; } set { heightadjustmode = value ; } }
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor
public EditSelectionMode ( )
{
// Initialize
mode = ModifyMode . None ;
}
2015-06-01 09:53:57 +00:00
//mxd. Another constructor. Used indirectly from ImportObjAsTerrainMode.OnAccept.
public EditSelectionMode ( bool pasting )
2014-12-03 23:15:26 +00:00
{
2014-01-30 14:52:08 +00:00
// Initialize
this . pasting = pasting ;
this . mode = ModifyMode . None ;
}
2009-04-19 18:07:22 +00:00
// Disposer
public override void Dispose ( )
{
// Not already disposed?
if ( ! isdisposed )
{
// Clean up
// Dispose base
base . Dispose ( ) ;
}
}
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
2009-07-06 08:34:14 +00:00
2009-08-21 08:38:24 +00:00
// The following functions set different properties and update
public void SetAbsPosX ( float posx )
{
offset . x = posx ;
UpdateAllChanges ( ) ;
}
public void SetAbsPosY ( float posy )
{
offset . y = posy ;
UpdateAllChanges ( ) ;
}
public void SetRelPosX ( float posx )
{
offset . x = posx + baseoffset . x ;
UpdateAllChanges ( ) ;
}
public void SetRelPosY ( float posy )
{
offset . y = posy + baseoffset . y ;
UpdateAllChanges ( ) ;
}
public void SetAbsSizeX ( float sizex )
{
size . x = sizex ;
UpdateAllChanges ( ) ;
}
public void SetAbsSizeY ( float sizey )
{
size . y = sizey ;
UpdateAllChanges ( ) ;
}
public void SetRelSizeX ( float sizex )
{
size . x = basesize . x * ( sizex / 100.0f ) ;
UpdateAllChanges ( ) ;
}
public void SetRelSizeY ( float sizey )
{
size . y = basesize . y * ( sizey / 100.0f ) ;
UpdateAllChanges ( ) ;
}
public void SetAbsRotation ( float absrot )
{
rotation = absrot ;
UpdateAllChanges ( ) ;
}
// This updates all after changes were made
private void UpdateAllChanges ( )
{
UpdateGeometry ( ) ;
UpdateRectangleComponents ( ) ;
2015-06-01 09:53:57 +00:00
if ( General . Map . UDMF ) UpdateTextureTransform ( ) ; //mxd
2009-08-21 08:38:24 +00:00
General . Map . Map . Update ( ) ;
General . Interface . RedrawDisplay ( ) ;
}
2009-07-06 08:34:14 +00:00
2009-04-19 18:07:22 +00:00
// This highlights a new vertex
2015-06-01 09:53:57 +00:00
private void Highlight ( MapElement h )
2009-04-19 18:07:22 +00:00
{
2009-07-06 08:34:14 +00:00
// Undraw previous highlight
2016-03-18 12:52:12 +00:00
if ( highlighted ! = null & & ! highlighted . IsDisposed )
2009-04-19 18:07:22 +00:00
{
2009-07-06 08:34:14 +00:00
if ( highlighted is Vertex )
{
if ( renderer . StartPlotter ( false ) )
{
2016-03-18 12:52:12 +00:00
renderer . PlotVertex ( ( Vertex ) highlighted , renderer . DetermineVertexColor ( ( Vertex ) highlighted ) ) ;
2009-07-06 08:34:14 +00:00
renderer . Finish ( ) ;
}
}
else
{
if ( renderer . StartThings ( false ) )
{
2016-04-01 10:49:19 +00:00
renderer . RenderThing ( ( Thing ) highlighted , renderer . DetermineThingColor ( ( Thing ) highlighted ) , General . Settings . ActiveThingsAlpha ) ;
2009-07-06 08:34:14 +00:00
renderer . Finish ( ) ;
}
}
}
// Set new highlight
highlighted = h ;
2009-04-19 18:07:22 +00:00
2009-07-06 08:34:14 +00:00
// Render highlighted item
2016-03-18 12:52:12 +00:00
if ( highlighted ! = null & & ! highlighted . IsDisposed )
2009-07-06 08:34:14 +00:00
{
if ( highlighted is Vertex )
{
if ( renderer . StartPlotter ( false ) )
{
2016-03-18 12:52:12 +00:00
renderer . PlotVertex ( ( Vertex ) highlighted , ColorCollection . HIGHLIGHT ) ;
2009-07-06 08:34:14 +00:00
renderer . Finish ( ) ;
}
}
else
{
if ( renderer . StartThings ( false ) )
{
2016-04-01 10:49:19 +00:00
renderer . RenderThing ( ( Thing ) highlighted , General . Colors . Highlight , General . Settings . ActiveThingsAlpha ) ;
2009-07-06 08:34:14 +00:00
renderer . Finish ( ) ;
}
}
2009-04-19 18:07:22 +00:00
}
2009-07-06 08:34:14 +00:00
// Done
renderer . Present ( ) ;
2009-04-19 18:07:22 +00:00
}
// This updates the selection
private void Update ( )
{
2019-04-20 14:55:20 +00:00
// biwa. This is a fix for autodrag, since it will actually fire OnMouseLeave and would crash when Update is called while the
// mouse is outside the window. This does *not* happen when dragging without autodrag.
if ( ! mouseinside ) return ;
2009-04-19 18:07:22 +00:00
// Not in any modifying mode?
if ( mode = = ModifyMode . None )
{
// Check what grip the mouse is over
// and change cursor accordingly
2016-01-18 20:37:21 +00:00
Grip mousegrip = ( autodrag ? Grip . Main : CheckMouseGrip ( ) ) ; //mxd. We only want to move when starting auto-dragging
2009-04-19 18:07:22 +00:00
switch ( mousegrip )
{
case Grip . Main :
// Find the nearest vertex within highlight range
Vertex v = MapSet . NearestVertex ( selectedvertices , mousemappos ) ;
2009-07-06 08:34:14 +00:00
// Find the nearest thing within range
Thing t = MapSet . NearestThing ( selectedthings , mousemappos ) ;
// Highlight the one that is closer
if ( ( v ! = null ) & & ( t ! = null ) )
{
if ( v . DistanceToSq ( mousemappos ) < t . DistanceToSq ( mousemappos ) )
{
if ( v ! = highlighted ) Highlight ( v ) ;
}
else
{
if ( t ! = highlighted ) Highlight ( t ) ;
}
}
else if ( v ! = null )
{
if ( v ! = highlighted ) Highlight ( v ) ;
}
else
{
if ( t ! = highlighted ) Highlight ( t ) ;
}
2009-04-19 18:07:22 +00:00
General . Interface . SetCursor ( Cursors . Hand ) ;
break ;
case Grip . RotateLB :
case Grip . RotateLT :
case Grip . RotateRB :
case Grip . RotateRT :
Highlight ( null ) ;
General . Interface . SetCursor ( Cursors . Cross ) ;
break ;
case Grip . SizeE :
case Grip . SizeS :
case Grip . SizeW :
case Grip . SizeN :
// Pick the best matching cursor depending on rotation and side
float resizeangle = rotation ;
if ( ( mousegrip = = Grip . SizeE ) | | ( mousegrip = = Grip . SizeW ) ) resizeangle + = Angle2D . PIHALF ;
resizeangle = Angle2D . Normalized ( resizeangle ) ;
if ( resizeangle > Angle2D . PI ) resizeangle - = Angle2D . PI ;
resizeangle = Math . Abs ( resizeangle + Angle2D . PI / 8.000001f ) ;
int cursorindex = ( int ) Math . Floor ( ( resizeangle / Angle2D . PI ) * 4.0f ) % 4 ;
General . Interface . SetCursor ( RESIZE_CURSORS [ cursorindex ] ) ;
Highlight ( null ) ;
break ;
default :
Highlight ( null ) ;
General . Interface . SetCursor ( Cursors . Default ) ;
break ;
}
}
else
{
Vector2D snappedmappos = mousemappos ;
bool dosnaptogrid = snaptogrid ;
// Options
snaptogrid = General . Interface . ShiftState ^ General . Interface . SnapToGrid ;
snaptonearest = General . Interface . CtrlState ^ General . Interface . AutoMerge ;
// Change to crosshair cursor so we can clearly see around the mouse cursor
General . Interface . SetCursor ( Cursors . Cross ) ;
// Check what modifying mode we are in
switch ( mode )
{
// Dragging
case ModifyMode . Dragging :
// Change offset without snapping
offset = mousemappos - dragoffset ;
// Calculate transformed position of highlighted vertex
Vector2D transformedpos = TransformedPoint ( highlightedpos ) ;
// Snap to nearest vertex?
if ( snaptonearest & & ( highlighted ! = null ) )
{
2009-05-20 15:03:08 +00:00
float vrange = BuilderPlug . Me . StitchRange / renderer . Scale ;
2009-04-19 18:07:22 +00:00
// Try the nearest vertex
Vertex nv = MapSet . NearestVertexSquareRange ( unselectedvertices , transformedpos , vrange ) ;
if ( nv ! = null )
{
// Change offset to snap to target
offset + = nv . Position - transformedpos ;
dosnaptogrid = false ;
}
else
{
// Find the nearest unselected line within range
2009-05-20 15:03:08 +00:00
Linedef nl = MapSet . NearestLinedefRange ( unselectedlines , transformedpos , BuilderPlug . Me . StitchRange / renderer . Scale ) ;
2009-04-19 18:07:22 +00:00
if ( nl ! = null )
{
// Snap to grid?
if ( dosnaptogrid )
{
// Get grid intersection coordinates
List < Vector2D > coords = nl . GetGridIntersections ( ) ;
// Find nearest grid intersection
float found_distance = float . MaxValue ;
Vector2D found_pos = new Vector2D ( float . NaN , float . NaN ) ;
foreach ( Vector2D v in coords )
{
Vector2D dist = transformedpos - v ;
if ( dist . GetLengthSq ( ) < found_distance )
{
// Found a better match
found_distance = dist . GetLengthSq ( ) ;
found_pos = v ;
// Do not snap to grid anymore
dosnaptogrid = false ;
}
}
// Found something?
if ( ! float . IsNaN ( found_pos . x ) )
{
// Change offset to snap to target
offset + = found_pos - transformedpos ;
}
}
else
{
// Change offset to snap onto the line
offset + = nl . NearestOnLine ( transformedpos ) - transformedpos ;
}
}
}
}
// Snap to grid?
if ( dosnaptogrid & & ( highlighted ! = null ) )
{
// Change offset to align to grid
offset + = General . Map . Grid . SnappedToGrid ( transformedpos ) - transformedpos ;
}
// Update
UpdateGeometry ( ) ;
UpdateRectangleComponents ( ) ;
General . Interface . RedrawDisplay ( ) ;
break ;
// Resizing
case ModifyMode . Resizing :
// Snap to nearest vertex?
if ( snaptonearest )
{
2009-05-20 15:03:08 +00:00
float vrange = BuilderPlug . Me . StitchRange / renderer . Scale ;
2009-04-19 18:07:22 +00:00
// Try the nearest vertex
Vertex nv = MapSet . NearestVertexSquareRange ( unselectedvertices , snappedmappos , vrange ) ;
if ( nv ! = null )
{
snappedmappos = nv . Position ;
dosnaptogrid = false ;
}
}
// Snap to grid?
if ( dosnaptogrid )
{
// Aligned to grid
snappedmappos = General . Map . Grid . SnappedToGrid ( snappedmappos ) ;
}
// Keep corner position
Vector2D oldcorner = corners [ stickcorner ] ;
// Change size with the scale from the ruler
2015-06-01 09:53:57 +00:00
float newscale = resizeaxis . GetNearestOnLine ( snappedmappos ) ;
size = ( basesize * resizefilter ) * newscale + size * ( 1.0f - resizefilter ) ;
//mxd. Update scale
newscale = 1f / newscale ;
if ( float . IsInfinity ( newscale ) | | float . IsNaN ( newscale ) ) newscale = 99999f ;
scale = ( newscale * resizefilter ) + scale * ( 1.0f - resizefilter ) ;
if ( float . IsInfinity ( scale . x ) | | float . IsNaN ( scale . x ) ) scale . x = 99999f ;
if ( float . IsInfinity ( scale . y ) | | float . IsNaN ( scale . y ) ) scale . y = 99999f ;
2009-04-19 18:07:22 +00:00
// Adjust corner position
Vector2D newcorner = TransformedPoint ( originalcorners [ stickcorner ] ) ;
offset - = newcorner - oldcorner ;
// Show the extension line so that the user knows what it is aligning to
Vector2D sizefiltered = ( size * resizefilter ) ;
float sizelength = sizefiltered . x + sizefiltered . y ;
Line2D edgeline = new Line2D ( resizeaxis . v1 + resizevector * sizelength , resizeaxis . v1 + resizevector * sizelength - edgevector ) ;
float nearestonedge = edgeline . GetNearestOnLine ( snappedmappos ) ;
if ( nearestonedge > 0.5f )
extensionline = new Line2D ( edgeline . v1 , snappedmappos ) ;
else
extensionline = new Line2D ( edgeline . v2 , snappedmappos ) ;
// Update
UpdateGeometry ( ) ;
UpdateRectangleComponents ( ) ;
General . Interface . RedrawDisplay ( ) ;
break ;
// Rotating
case ModifyMode . Rotating :
// Get angle from mouse to center
Vector2D center = offset + size * 0.5f ;
Vector2D delta = snappedmappos - center ;
rotation = delta . GetAngle ( ) - rotategripangle ;
// Snap rotation to grip?
if ( dosnaptogrid )
{
2015-04-15 15:01:40 +00:00
// We make 24 vectors that the rotation can snap to
2009-04-19 18:07:22 +00:00
float founddistance = float . MaxValue ;
float foundrotation = rotation ;
2015-04-15 15:01:40 +00:00
Vector3D rotvec = Vector2D . FromAngle ( rotation ) ;
for ( int i = 0 ; i < 24 ; i + + )
2009-04-19 18:07:22 +00:00
{
// Make the vectors
2015-04-27 14:56:25 +00:00
float angle = i * Angle2D . PI * 0.08333333333f ; //mxd. 15-degree increments
2009-04-19 18:07:22 +00:00
Vector2D gridvec = Vector2D . FromAngle ( angle ) ;
// Check distance
float dist = 2.0f - Vector2D . DotProduct ( gridvec , rotvec ) ;
if ( dist < founddistance )
{
foundrotation = angle ;
founddistance = dist ;
}
}
// Keep rotation
rotation = foundrotation ;
}
// Update
UpdateGeometry ( ) ;
UpdateRectangleComponents ( ) ;
General . Interface . RedrawDisplay ( ) ;
break ;
}
}
}
// This checks and returns the grip the mouse pointer is in
private Grip CheckMouseGrip ( )
{
2015-04-15 15:01:40 +00:00
if ( PointInRectF ( resizegrips [ 0 ] , mousemappos ) ) return Grip . SizeN ;
if ( PointInRectF ( resizegrips [ 2 ] , mousemappos ) ) return Grip . SizeS ;
if ( PointInRectF ( resizegrips [ 1 ] , mousemappos ) ) return Grip . SizeE ;
if ( PointInRectF ( resizegrips [ 3 ] , mousemappos ) ) return Grip . SizeW ;
if ( PointInRectF ( rotategrips [ 0 ] , mousemappos ) ) return Grip . RotateLT ;
if ( PointInRectF ( rotategrips [ 1 ] , mousemappos ) ) return Grip . RotateRT ;
if ( PointInRectF ( rotategrips [ 2 ] , mousemappos ) ) return Grip . RotateRB ;
if ( PointInRectF ( rotategrips [ 3 ] , mousemappos ) ) return Grip . RotateLB ;
if ( Tools . PointInPolygon ( corners , mousemappos ) ) return Grip . Main ;
return Grip . None ;
2009-04-19 18:07:22 +00:00
}
// This applies the current rotation and resize to a point
private Vector2D TransformedPoint ( Vector2D p )
{
// Resize
p = ( p - baseoffset ) * ( size / basesize ) + baseoffset ;
// Rotate
Vector2D center = baseoffset + size * 0.5f ;
Vector2D po = p - center ;
p = po . GetRotated ( rotation ) ;
p + = center ;
// Translate
p + = offset - baseoffset ;
return p ;
}
2010-09-16 17:07:30 +00:00
// This applies the current rotation and resize to a point
private Vector2D TransformedPointNoScale ( Vector2D p )
{
// Rotate
Vector2D center = baseoffset + size * 0.5f ;
Vector2D po = p - center ;
p = po . GetRotated ( rotation ) ;
p + = center ;
// Translate
p + = offset - baseoffset ;
return p ;
}
// This applies the current rotation and resize to a point
private Vector2D TransformedPointNoRotate ( Vector2D p )
{
// Resize
p = ( p - baseoffset ) * ( size / basesize ) + baseoffset ;
// Translate
p + = offset - baseoffset ;
return p ;
}
// This applies the current rotation and resize to a point
private Vector2D TransformedPointNoRotateNoScale ( Vector2D p )
{
// Translate
p + = offset - baseoffset ;
return p ;
}
2009-04-19 18:07:22 +00:00
// This checks if a point is in a rect
2014-05-20 09:09:28 +00:00
private static bool PointInRectF ( RectangleF rect , Vector2D point )
2009-04-19 18:07:22 +00:00
{
2013-12-20 09:24:43 +00:00
return ! ( point . x < rect . Left | | point . x > rect . Right | | point . y < rect . Top | | point . y > rect . Bottom ) ; //mxd
2009-04-19 18:07:22 +00:00
}
2009-08-21 08:38:24 +00:00
// This updates the values in the panel
private void UpdatePanel ( )
{
Vector2D relsize = ( size / basesize ) * 100.0f ;
if ( panel ! = null )
panel . ShowCurrentValues ( offset , offset - baseoffset , size , relsize , rotation ) ;
}
2009-04-19 18:07:22 +00:00
// This moves all things and vertices to match the current transformation
2013-12-20 09:24:43 +00:00
private void UpdateGeometry ( )
2009-04-19 18:07:22 +00:00
{
2011-12-02 20:41:53 +00:00
float [ ] newthingangle = thingangle . ToArray ( ) ;
int index ;
// Flip things horizontally
if ( size . x < 0.0f )
{
for ( index = 0 ; index < newthingangle . Length ; index + + )
{
// Check quadrant
if ( ( newthingangle [ index ] > = 0f ) & & ( newthingangle [ index ] < Angle2D . PIHALF ) )
newthingangle [ index ] = newthingangle [ index ] - ( newthingangle [ index ] * 2 ) ;
else if ( ( newthingangle [ index ] > = Angle2D . PIHALF ) & & ( newthingangle [ index ] < = Angle2D . PI ) )
newthingangle [ index ] = newthingangle [ index ] + ( Angle2D . PI - newthingangle [ index ] ) * 2 ;
else if ( ( newthingangle [ index ] > = Angle2D . PI ) & & ( newthingangle [ index ] < = Angle2D . PI + Angle2D . PIHALF ) )
newthingangle [ index ] = newthingangle [ index ] - ( newthingangle [ index ] - Angle2D . PI ) * 2 ;
else
newthingangle [ index ] = newthingangle [ index ] + ( Angle2D . PI2 - newthingangle [ index ] ) * 2 ;
}
}
// Flip things vertically
if ( size . y < 0.0f )
{
for ( index = 0 ; index < newthingangle . Length ; index + + )
{
// Check quadrant
if ( ( newthingangle [ index ] > = 0f ) & & ( newthingangle [ index ] < Angle2D . PIHALF ) )
newthingangle [ index ] = newthingangle [ index ] + ( Angle2D . PI - newthingangle [ index ] * 2 ) ;
else if ( ( newthingangle [ index ] > = Angle2D . PIHALF ) & & ( newthingangle [ index ] < = Angle2D . PI ) )
newthingangle [ index ] = newthingangle [ index ] - ( newthingangle [ index ] - Angle2D . PIHALF ) * 2 ;
else if ( ( newthingangle [ index ] > = Angle2D . PI ) & & ( newthingangle [ index ] < = Angle2D . PI + Angle2D . PIHALF ) )
newthingangle [ index ] = newthingangle [ index ] + ( Angle2D . PI - ( newthingangle [ index ] - Angle2D . PI ) * 2 ) ;
else
newthingangle [ index ] = newthingangle [ index ] - ( newthingangle [ index ] - ( Angle2D . PI + Angle2D . PIHALF ) ) * 2 ;
}
}
2010-09-16 17:07:30 +00:00
// We use optimized versions of the TransformedPoint depending on what needs to be done.
// This is mainly done because 0.0 rotation and 1.0 scale may still give slight inaccuracies.
bool norotate = Math . Abs ( rotation ) < 0.0001f ;
bool noscale = Math . Abs ( size . x - basesize . x ) + Math . Abs ( size . y - basesize . y ) < 0.0001f ;
if ( norotate & & noscale )
2009-04-19 18:07:22 +00:00
{
2011-12-02 20:41:53 +00:00
index = 0 ;
2010-09-16 17:07:30 +00:00
foreach ( Vertex v in selectedvertices )
{
v . Move ( TransformedPointNoRotateNoScale ( vertexpos [ index + + ] ) ) ;
}
index = 0 ;
foreach ( Thing t in selectedthings )
{
t . Move ( TransformedPointNoRotateNoScale ( thingpos [ index + + ] ) ) ;
}
2009-04-19 18:07:22 +00:00
}
2010-09-16 17:07:30 +00:00
else if ( norotate )
{
2011-12-02 20:41:53 +00:00
index = 0 ;
2010-09-16 17:07:30 +00:00
foreach ( Vertex v in selectedvertices )
{
v . Move ( TransformedPointNoRotate ( vertexpos [ index + + ] ) ) ;
}
index = 0 ;
foreach ( Thing t in selectedthings )
{
t . Move ( TransformedPointNoRotate ( thingpos [ index + + ] ) ) ;
}
}
else if ( noscale )
{
2011-12-02 20:41:53 +00:00
index = 0 ;
2010-09-16 17:07:30 +00:00
foreach ( Vertex v in selectedvertices )
{
v . Move ( TransformedPointNoScale ( vertexpos [ index + + ] ) ) ;
}
index = 0 ;
foreach ( Thing t in selectedthings )
{
2011-12-02 20:41:53 +00:00
newthingangle [ index ] = Angle2D . Normalized ( newthingangle [ index ] + rotation ) ;
2010-09-16 17:07:30 +00:00
t . Move ( TransformedPointNoScale ( thingpos [ index + + ] ) ) ;
}
}
else
2009-04-19 18:07:22 +00:00
{
2011-12-02 20:41:53 +00:00
index = 0 ;
2010-09-16 17:07:30 +00:00
foreach ( Vertex v in selectedvertices )
{
v . Move ( TransformedPoint ( vertexpos [ index + + ] ) ) ;
}
index = 0 ;
foreach ( Thing t in selectedthings )
{
2011-12-02 20:41:53 +00:00
newthingangle [ index ] = Angle2D . Normalized ( newthingangle [ index ] + rotation ) ;
2010-09-16 17:07:30 +00:00
t . Move ( TransformedPoint ( thingpos [ index + + ] ) ) ;
}
2009-04-19 18:07:22 +00:00
}
// This checks if the lines should be flipped
bool shouldbeflipped = ( size . x < 0.0f ) ^ ( size . y < 0.0f ) ;
if ( shouldbeflipped ! = linesflipped ) FlipLinedefs ( ) ;
2011-12-02 20:41:53 +00:00
// Apply new thing rotations
index = 0 ;
foreach ( Thing t in selectedthings )
{
2016-06-15 22:02:51 +00:00
//mxd. Added special Polyobj Anchor handling and Doom angle clamping
if ( ! fixedrotationthingtypes . Contains ( t . Type ) )
{
int newangle = Angle2D . RealToDoom ( Angle2D . Normalized ( newthingangle [ index ] ) ) ;
if ( General . Map . Config . DoomThingRotationAngles ) newangle = newangle / 45 * 45 ;
t . Rotate ( newangle ) ;
}
2015-02-06 21:03:20 +00:00
index + + ;
2011-12-02 20:41:53 +00:00
}
2009-04-19 18:07:22 +00:00
2009-08-21 08:38:24 +00:00
UpdatePanel ( ) ;
2009-04-19 18:07:22 +00:00
General . Map . Map . Update ( true , false ) ;
}
2015-06-01 09:53:57 +00:00
//mxd. This updates texture transforms for all sectors
private void UpdateTextureTransform ( )
{
foreach ( KeyValuePair < Sector , SectorTextureInfo > group in selectedsectors )
{
group . Key . Fields . BeforeFieldsChange ( ) ;
// Apply transforms
UpdateTextureTransform ( group . Key . Fields , group . Value . Ceiling , transformceiloffsets , rotateceiloffsets , scaleceiloffsets ) ;
UpdateTextureTransform ( group . Key . Fields , group . Value . Floor , transformflooroffsets , rotateflooroffsets , scaleflooroffsets ) ;
// Update cache
group . Key . UpdateNeeded = true ;
group . Key . UpdateCache ( ) ;
}
// Map was changed
General . Map . IsChanged = true ;
}
//mxd. This updates texture transforms in given UniFields
private void UpdateTextureTransform ( UniFields fields , SurfaceTextureInfo si , bool transformoffsets , bool rotateoffsets , bool scaleoffsets )
{
// Get offset-ready values
float texrotation = Angle2D . PI2 - rotation ;
// Update texture offsets
2019-09-20 18:49:30 +00:00
if ( transformoffsets )
2015-06-01 09:53:57 +00:00
{
2019-09-20 18:49:30 +00:00
float trotation = rotateoffsets ? ( si . Rotation + texrotation ) : ( si . Rotation ) ;
Vector2D offset = selectioncenter . GetRotated ( trotation ) ;
fields [ "xpanning" + si . Part ] = new UniValue ( UniversalType . Float , ( float ) Math . Round ( - offset . x , General . Map . FormatInterface . VertexDecimals ) ) ;
fields [ "ypanning" + si . Part ] = new UniValue ( UniversalType . Float , ( float ) Math . Round ( offset . y , General . Map . FormatInterface . VertexDecimals ) ) ;
2015-06-01 09:53:57 +00:00
}
// Restore texture offsets
else
{
fields [ "xpanning" + si . Part ] = new UniValue ( UniversalType . Float , si . Offset . x ) ;
fields [ "ypanning" + si . Part ] = new UniValue ( UniversalType . Float , si . Offset . y ) ;
}
// Update rotation
if ( rotateoffsets )
fields [ "rotation" + si . Part ] = new UniValue ( UniversalType . AngleDegreesFloat , General . ClampAngle ( ( float ) Math . Round ( Angle2D . RadToDeg ( si . Rotation + texrotation ) , General . Map . FormatInterface . VertexDecimals ) ) ) ;
// Restore rotation
else
2015-06-18 14:01:31 +00:00
fields [ "rotation" + si . Part ] = new UniValue ( UniversalType . AngleDegreesFloat , Angle2D . RadToDeg ( si . Rotation ) ) ;
2015-06-01 09:53:57 +00:00
// Update scale
if ( scaleoffsets )
{
fields [ "xscale" + si . Part ] = new UniValue ( UniversalType . Float , ( float ) Math . Round ( si . Scale . x * scale . x , General . Map . FormatInterface . VertexDecimals ) ) ;
2016-05-29 00:38:55 +00:00
fields [ "yscale" + si . Part ] = new UniValue ( UniversalType . Float , ( float ) Math . Round ( - si . Scale . y * scale . y , General . Map . FormatInterface . VertexDecimals ) ) ;
2015-06-01 09:53:57 +00:00
}
// Restore scale
else
{
fields [ "xscale" + si . Part ] = new UniValue ( UniversalType . Float , si . Scale . x ) ;
2015-09-04 12:44:09 +00:00
fields [ "yscale" + si . Part ] = new UniValue ( UniversalType . Float , - si . Scale . y ) ;
2015-06-01 09:53:57 +00:00
}
}
//mxd. This restores texture transforms for all sectors
private void RestoreTextureTransform ( )
{
foreach ( KeyValuePair < Sector , SectorTextureInfo > group in selectedsectors )
{
group . Key . Fields . BeforeFieldsChange ( ) ;
// Revert transforms
RestoreTextureTransform ( group . Key . Fields , group . Value . Ceiling ) ;
RestoreTextureTransform ( group . Key . Fields , group . Value . Floor ) ;
// Update cache
group . Key . UpdateNeeded = true ;
group . Key . UpdateCache ( ) ;
}
}
//mxd. This restores texture transforms in given UniFields
private static void RestoreTextureTransform ( UniFields fields , SurfaceTextureInfo si )
{
fields [ "rotation" + si . Part ] = new UniValue ( UniversalType . AngleDegreesFloat , Angle2D . RadToDeg ( si . Rotation ) ) ;
fields [ "xscale" + si . Part ] = new UniValue ( UniversalType . Float , si . Scale . x ) ;
fields [ "yscale" + si . Part ] = new UniValue ( UniversalType . Float , - si . Scale . y ) ;
fields [ "xpanning" + si . Part ] = new UniValue ( UniversalType . Float , si . Offset . x ) ;
fields [ "ypanning" + si . Part ] = new UniValue ( UniversalType . Float , si . Offset . y ) ;
}
2009-04-19 18:07:22 +00:00
// This updates the selection rectangle components
private void UpdateRectangleComponents ( )
{
float gripsize = GRIP_SIZE / renderer . Scale ;
PixelColor rectcolor = General . Colors . Highlight . WithAlpha ( RECTANGLE_ALPHA ) ;
// Original (untransformed) corners
originalcorners = new Vector2D [ 4 ] ;
originalcorners [ 0 ] = new Vector2D ( baseoffset . x , baseoffset . y ) ;
originalcorners [ 1 ] = new Vector2D ( baseoffset . x + basesize . x , baseoffset . y ) ;
originalcorners [ 2 ] = new Vector2D ( baseoffset . x + basesize . x , baseoffset . y + basesize . y ) ;
originalcorners [ 3 ] = new Vector2D ( baseoffset . x , baseoffset . y + basesize . y ) ;
// Corners
corners = new Vector2D [ 4 ] ;
for ( int i = 0 ; i < 4 ; i + + )
corners [ i ] = TransformedPoint ( originalcorners [ i ] ) ;
// Vertices
cornerverts = new FlatVertex [ 6 ] ;
for ( int i = 0 ; i < 6 ; i + + )
{
cornerverts [ i ] = new FlatVertex ( ) ;
cornerverts [ i ] . z = 1.0f ;
cornerverts [ i ] . c = rectcolor . ToInt ( ) ;
}
cornerverts [ 0 ] . x = corners [ 0 ] . x ;
cornerverts [ 0 ] . y = corners [ 0 ] . y ;
cornerverts [ 1 ] . x = corners [ 1 ] . x ;
cornerverts [ 1 ] . y = corners [ 1 ] . y ;
cornerverts [ 2 ] . x = corners [ 2 ] . x ;
cornerverts [ 2 ] . y = corners [ 2 ] . y ;
cornerverts [ 3 ] . x = corners [ 0 ] . x ;
cornerverts [ 3 ] . y = corners [ 0 ] . y ;
cornerverts [ 4 ] . x = corners [ 2 ] . x ;
cornerverts [ 4 ] . y = corners [ 2 ] . y ;
cornerverts [ 5 ] . x = corners [ 3 ] . x ;
cornerverts [ 5 ] . y = corners [ 3 ] . y ;
// Middle points between corners
Vector2D middle01 = corners [ 0 ] + ( corners [ 1 ] - corners [ 0 ] ) * 0.5f ;
Vector2D middle12 = corners [ 1 ] + ( corners [ 2 ] - corners [ 1 ] ) * 0.5f ;
Vector2D middle23 = corners [ 2 ] + ( corners [ 3 ] - corners [ 2 ] ) * 0.5f ;
Vector2D middle30 = corners [ 3 ] + ( corners [ 0 ] - corners [ 3 ] ) * 0.5f ;
// Resize grips
resizegrips = new RectangleF [ 4 ] ;
resizegrips [ 0 ] = new RectangleF ( middle01 . x - gripsize * 0.5f ,
middle01 . y - gripsize * 0.5f ,
gripsize , gripsize ) ;
resizegrips [ 1 ] = new RectangleF ( middle12 . x - gripsize * 0.5f ,
middle12 . y - gripsize * 0.5f ,
gripsize , gripsize ) ;
resizegrips [ 2 ] = new RectangleF ( middle23 . x - gripsize * 0.5f ,
middle23 . y - gripsize * 0.5f ,
gripsize , gripsize ) ;
resizegrips [ 3 ] = new RectangleF ( middle30 . x - gripsize * 0.5f ,
middle30 . y - gripsize * 0.5f ,
gripsize , gripsize ) ;
// Rotate grips
rotategrips = new RectangleF [ 4 ] ;
rotategrips [ 0 ] = new RectangleF ( corners [ 0 ] . x - gripsize * 0.5f ,
corners [ 0 ] . y - gripsize * 0.5f ,
gripsize , gripsize ) ;
rotategrips [ 1 ] = new RectangleF ( corners [ 1 ] . x - gripsize * 0.5f ,
corners [ 1 ] . y - gripsize * 0.5f ,
gripsize , gripsize ) ;
rotategrips [ 2 ] = new RectangleF ( corners [ 2 ] . x - gripsize * 0.5f ,
corners [ 2 ] . y - gripsize * 0.5f ,
gripsize , gripsize ) ;
rotategrips [ 3 ] = new RectangleF ( corners [ 3 ] . x - gripsize * 0.5f ,
corners [ 3 ] . y - gripsize * 0.5f ,
gripsize , gripsize ) ;
2015-06-01 09:53:57 +00:00
//mxd. Update selection center
selectioncenter = new Vector2D ( offset . x + size . x * 0.5f , offset . y + size . y * 0.5f ) ;
2009-04-19 18:07:22 +00:00
}
// This flips all linedefs in the selection (used for mirroring)
private void FlipLinedefs ( )
{
2016-07-07 11:20:28 +00:00
//mxd. Check if we need to flip sidedefs
bool flipsides = false ;
HashSet < Linedef > selectedlineshash = new HashSet < Linedef > ( selectedlines ) ;
foreach ( Vertex v in selectedvertices )
{
foreach ( Linedef l in v . Linedefs )
{
if ( ! selectedlineshash . Contains ( l ) )
{
flipsides = true ;
break ;
}
}
}
2009-04-19 18:07:22 +00:00
// Flip linedefs
2016-07-07 10:59:06 +00:00
foreach ( Linedef ld in selectedlines )
{
ld . FlipVertices ( ) ;
2016-07-07 11:20:28 +00:00
if ( flipsides ) ld . FlipSidedefs ( ) ; //mxd
2016-07-07 10:59:06 +00:00
}
2009-04-19 18:07:22 +00:00
// Done
linesflipped = ! linesflipped ;
}
2020-05-16 10:26:16 +00:00
2009-04-19 18:07:22 +00:00
#endregion
2016-05-20 15:04:00 +00:00
#region = = = = = = = = = = = = = = = = = = Sector height adjust methods ( mxd )
2016-05-29 00:38:55 +00:00
//x = floor height, y = ceiling height
private static Point GetOutsideHeights ( HashSet < Sector > sectors )
2016-05-20 15:04:00 +00:00
{
2016-05-29 00:38:55 +00:00
Sector target = null ;
Point result = new Point { X = int . MinValue , Y = int . MinValue } ;
2016-05-20 15:04:00 +00:00
foreach ( Sector s in sectors )
{
foreach ( Sidedef side in s . Sidedefs )
{
2016-05-29 00:38:55 +00:00
// Don't compare with our own stuff, among other things
if ( side . Other = = null | | side . Other . Sector = = null | | sectors . Contains ( side . Other . Sector ) ) continue ;
if ( target = = null )
{
target = side . Other . Sector ;
result . X = target . FloorHeight ;
result . Y = target . CeilHeight ;
}
else if ( target ! = side . Other . Sector )
{
// Compare heights
if ( target . FloorHeight ! = side . Other . Sector . FloorHeight )
result . X = int . MinValue ;
if ( target . CeilHeight ! = side . Other . Sector . CeilHeight )
result . Y = int . MinValue ;
// We can stop now...
if ( result . X = = int . MinValue & & result . Y = = int . MinValue )
return result ;
}
2016-05-20 15:04:00 +00:00
}
}
return result ;
}
2016-05-29 00:38:55 +00:00
private static void AdjustSectorsHeight ( HashSet < Sector > toadjust , HeightAdjustMode adjustmode , int oldfloorheight , int oldceilheight )
2016-05-20 15:04:00 +00:00
{
// Adjust only when selection is inside a single sector
if ( adjustmode = = HeightAdjustMode . NONE | | oldfloorheight = = int . MinValue | | oldceilheight = = int . MinValue ) return ;
2016-05-29 00:38:55 +00:00
Point outsideheights = GetOutsideHeights ( toadjust ) ;
if ( outsideheights . X = = int . MinValue & & outsideheights . Y = = int . MinValue ) return ;
2016-05-20 15:04:00 +00:00
// Height differences
2016-05-29 00:38:55 +00:00
int floorheightdiff = ( outsideheights . X = = int . MinValue ? int . MinValue : outsideheights . X - oldfloorheight ) ;
int ceilheightdiff = ( outsideheights . Y = = int . MinValue ? int . MinValue : outsideheights . Y - oldceilheight ) ;
2016-05-20 15:04:00 +00:00
switch ( adjustmode )
{
case HeightAdjustMode . ADJUST_FLOORS :
2016-05-29 00:38:55 +00:00
if ( floorheightdiff ! = int . MinValue )
{
foreach ( Sector s in toadjust ) AdjustSectorHeight ( s , floorheightdiff , int . MinValue ) ;
}
2016-05-20 15:04:00 +00:00
break ;
case HeightAdjustMode . ADJUST_CEILINGS :
2016-05-29 00:38:55 +00:00
if ( ceilheightdiff ! = int . MinValue )
{
foreach ( Sector s in toadjust ) AdjustSectorHeight ( s , int . MinValue , ceilheightdiff ) ;
}
2016-05-20 15:04:00 +00:00
break ;
case HeightAdjustMode . ADJUST_BOTH :
foreach ( Sector s in toadjust ) AdjustSectorHeight ( s , floorheightdiff , ceilheightdiff ) ;
break ;
default :
throw new NotImplementedException ( "Unknown HeightAdjustMode: " + adjustmode ) ;
}
}
private static void AdjustSectorHeight ( Sector s , int flooroffset , int ceiloffset )
{
// Adjust floor height
if ( flooroffset ! = int . MinValue )
{
// Adjust regular height
s . FloorHeight + = flooroffset ;
if ( General . Map . UDMF )
{
// Adjust slope height?
if ( s . FloorSlope . GetLengthSq ( ) > 0 & & ! float . IsNaN ( s . FloorSlopeOffset / s . FloorSlope . z ) )
{
s . FloorSlopeOffset - = flooroffset * ( float ) Math . Sin ( s . FloorSlope . GetAngleZ ( ) ) ;
}
// Adjust vertex height?
else if ( s . Sidedefs . Count = = 3 )
{
// Collect verts
HashSet < Vertex > verts = new HashSet < Vertex > ( ) ;
foreach ( Sidedef side in s . Sidedefs )
{
verts . Add ( side . Line . Start ) ;
verts . Add ( side . Line . End ) ;
}
// Offset verts
foreach ( Vertex v in verts )
{
if ( ! float . IsNaN ( v . ZFloor ) ) v . ZFloor + = flooroffset ;
}
}
}
}
// Adjust ceiling height
if ( ceiloffset ! = int . MinValue )
{
// Adjust regular height
s . CeilHeight + = ceiloffset ;
if ( General . Map . UDMF )
{
// Adjust slope height?
if ( s . CeilSlope . GetLengthSq ( ) > 0 & & ! float . IsNaN ( s . CeilSlopeOffset / s . CeilSlope . z ) )
{
s . CeilSlopeOffset - = ceiloffset * ( float ) Math . Sin ( s . CeilSlope . GetAngleZ ( ) ) ;
}
// Adjust vertex height?
else if ( s . Sidedefs . Count = = 3 )
{
// Collect verts
HashSet < Vertex > verts = new HashSet < Vertex > ( ) ;
foreach ( Sidedef side in s . Sidedefs )
{
verts . Add ( side . Line . Start ) ;
verts . Add ( side . Line . End ) ;
}
// Offset verts
foreach ( Vertex v in verts )
{
if ( ! float . IsNaN ( v . ZCeiling ) ) v . ZCeiling + = ceiloffset ;
}
}
}
}
}
#endregion
2009-04-19 18:07:22 +00:00
#region = = = = = = = = = = = = = = = = = = Events
public override void OnHelp ( )
{
General . ShowHelp ( "e_editselection.html" ) ;
}
// Mode engages
public override void OnEngage ( )
{
base . OnEngage ( ) ;
2009-08-21 08:38:24 +00:00
2016-01-18 20:37:21 +00:00
autodrag = ( pasting & & mouseinside & & BuilderPlug . Me . AutoDragOnPaste ) ;
2014-03-06 09:08:21 +00:00
snaptonearest = General . Interface . AutoMerge ; //mxd
2020-04-30 18:22:26 +00:00
selectedsectors = new Dictionary < Sector , SectorTextureInfo > ( ) ; //mxd
2009-04-19 18:07:22 +00:00
// Add toolbar buttons
2016-11-26 00:02:56 +00:00
General . Interface . BeginToolbarUpdate ( ) ; //mxd
2009-04-19 18:07:22 +00:00
General . Interface . AddButton ( BuilderPlug . Me . MenusForm . FlipSelectionH ) ;
General . Interface . AddButton ( BuilderPlug . Me . MenusForm . FlipSelectionV ) ;
2016-11-26 00:02:56 +00:00
General . Interface . EndToolbarUpdate ( ) ; //mxd
2015-06-19 19:19:41 +00:00
//mxd. Get EditPanel-related settings
2016-05-20 15:04:00 +00:00
usepreciseposition = General . Settings . ReadPluginSetting ( "editselectionmode.usepreciseposition" , true ) ;
heightadjustmode = ( HeightAdjustMode ) General . Settings . ReadPluginSetting ( "editselectionmode.heightadjustmode" , ( int ) HeightAdjustMode . NONE ) ;
2009-06-11 21:21:20 +00:00
2009-08-21 08:38:24 +00:00
// Add docker
panel = new EditSelectionPanel ( this ) ;
docker = new Docker ( "editselection" , "Edit Selection" , panel ) ;
2016-04-15 14:24:18 +00:00
General . Interface . AddDocker ( docker , true ) ;
2009-08-21 08:38:24 +00:00
General . Interface . SelectDocker ( docker ) ;
2009-06-11 21:21:20 +00:00
// We don't want to record this for undoing while we move the geometry around.
// This will be set back to normal when we're done.
General . Map . UndoRedo . IgnorePropChanges = true ;
2009-04-19 18:07:22 +00:00
// Convert geometry selection
General . Map . Map . ClearAllMarks ( false ) ;
General . Map . Map . MarkSelectedVertices ( true , true ) ;
General . Map . Map . MarkSelectedThings ( true , true ) ;
General . Map . Map . MarkSelectedLinedefs ( true , true ) ;
General . Map . Map . MarkSelectedSectors ( true , true ) ;
ICollection < Vertex > verts = General . Map . Map . GetVerticesFromLinesMarks ( true ) ;
foreach ( Vertex v in verts ) v . Marked = true ;
2015-06-01 09:53:57 +00:00
ICollection < Sector > sectors = General . Map . Map . GetSelectedSectors ( true ) ; //mxd
2020-04-30 18:22:26 +00:00
2015-06-01 09:53:57 +00:00
foreach ( Sector s in sectors )
2009-04-19 18:07:22 +00:00
{
foreach ( Sidedef sd in s . Sidedefs )
{
sd . Line . Marked = true ;
sd . Line . Start . Marked = true ;
sd . Line . End . Marked = true ;
}
2015-06-01 09:53:57 +00:00
if ( General . Map . UDMF ) selectedsectors . Add ( s , new SectorTextureInfo ( s ) ) ;
2009-04-19 18:07:22 +00:00
}
selectedvertices = General . Map . Map . GetMarkedVertices ( true ) ;
selectedthings = General . Map . Map . GetMarkedThings ( true ) ;
unselectedvertices = General . Map . Map . GetMarkedVertices ( false ) ;
// Make sure everything is selected so that it turns up red
foreach ( Vertex v in selectedvertices ) v . Selected = true ;
ICollection < Linedef > markedlines = General . Map . Map . LinedefsFromMarkedVertices ( false , true , false ) ;
foreach ( Linedef l in markedlines ) l . Selected = true ;
selectedlines = General . Map . Map . LinedefsFromMarkedVertices ( false , true , false ) ;
unselectedlines = General . Map . Map . LinedefsFromMarkedVertices ( true , false , false ) ;
2016-05-29 00:38:55 +00:00
unstablelines = ( pasting ? new List < Linedef > ( ) : General . Map . Map . LinedefsFromMarkedVertices ( false , false , true ) ) ; //mxd
2009-04-19 18:07:22 +00:00
// Array to keep original coordinates
vertexpos = new List < Vector2D > ( selectedvertices . Count ) ;
thingpos = new List < Vector2D > ( selectedthings . Count ) ;
thingangle = new List < float > ( selectedthings . Count ) ;
2014-02-20 12:36:09 +00:00
fixedrotationthingtypes = new List < int > ( ) ; //mxd
2009-04-19 18:07:22 +00:00
// A selection must be made!
if ( ( selectedvertices . Count > 0 ) | | ( selectedthings . Count > 0 ) )
{
// Initialize offset and size
offset . x = float . MaxValue ;
offset . y = float . MaxValue ;
Vector2D right ;
right . x = float . MinValue ;
right . y = float . MinValue ;
foreach ( Vertex v in selectedvertices )
{
// Find left-top and right-bottom
if ( v . Position . x < offset . x ) offset . x = v . Position . x ;
if ( v . Position . y < offset . y ) offset . y = v . Position . y ;
if ( v . Position . x > right . x ) right . x = v . Position . x ;
if ( v . Position . y > right . y ) right . y = v . Position . y ;
// Keep original coordinates
vertexpos . Add ( v . Position ) ;
}
foreach ( Thing t in selectedthings )
{
// Find left-top and right-bottom
if ( ( t . Position . x - t . Size ) < offset . x ) offset . x = t . Position . x - t . Size ;
if ( ( t . Position . y - t . Size ) < offset . y ) offset . y = t . Position . y - t . Size ;
if ( ( t . Position . x + t . Size ) > right . x ) right . x = t . Position . x + t . Size ;
if ( ( t . Position . y + t . Size ) > right . y ) right . y = t . Position . y + t . Size ;
2015-12-28 15:01:53 +00:00
if ( ! fixedrotationthingtypes . Contains ( t . Type ) ) //mxd
2014-12-03 23:15:26 +00:00
{
2014-02-20 12:36:09 +00:00
ThingTypeInfo tti = General . Map . Data . GetThingInfoEx ( t . Type ) ;
2015-12-28 15:01:53 +00:00
if ( tti ! = null & & tti . FixedRotation ) fixedrotationthingtypes . Add ( t . Type ) ;
2014-02-20 12:36:09 +00:00
}
2009-04-19 18:07:22 +00:00
// Keep original coordinates
thingpos . Add ( t . Position ) ;
thingangle . Add ( t . Angle ) ;
}
2020-04-04 20:02:13 +00:00
// Get z heights of floor and ceiling slopes (from the center of the sector). This is used
// to easily compute the new slope after moving and rotating. We can't simply use the sector
// heights, since they might be wrong (as sector heights are technically irrelevant for slopes)
// Floor/ceiling heights are stored if there is no slope, but they won't get used anyway
// Important: this has to be done before the first call to UpdateGeometry, since that will change
// the sector and subsequently the bounding box, but not the slope
slopeheights = new Dictionary < Sector , float [ ] > ( ) ;
foreach ( Sector s in sectors )
{
// Make sure the sector has a valid bounding box
2020-04-19 09:58:55 +00:00
s . UpdateBBox ( ) ;
2020-04-04 20:02:13 +00:00
Vector2D center = new Vector2D ( s . BBox . X + s . BBox . Width / 2 , s . BBox . Y + s . BBox . Height / 2 ) ;
float floorz = s . FloorHeight ;
float ceilingz = s . CeilHeight ;
2020-04-11 08:51:25 +00:00
if ( ! float . IsNaN ( s . FloorSlopeOffset ) & & s . FloorSlope . IsNormalized ( ) )
2020-04-04 20:02:13 +00:00
floorz = new Plane ( s . FloorSlope , s . FloorSlopeOffset ) . GetZ ( center ) ;
2020-04-11 08:51:25 +00:00
if ( ! float . IsNaN ( s . CeilSlopeOffset ) & & s . CeilSlope . IsNormalized ( ) )
2020-04-04 20:02:13 +00:00
ceilingz = new Plane ( s . CeilSlope , s . CeilSlopeOffset ) . GetZ ( center ) ;
slopeheights . Add ( s , new float [ ] { floorz , ceilingz } ) ;
}
2009-04-19 18:07:22 +00:00
// Calculate size
size = right - offset ;
// If the width of a dimension is zero, add a little
if ( Math . Abs ( size . x ) < 1.0f )
{
size . x + = ZERO_SIZE_ADDITION ;
offset . x - = ZERO_SIZE_ADDITION / 2 ;
}
if ( Math . Abs ( size . y ) < 1.0f )
{
size . y + = ZERO_SIZE_ADDITION ;
offset . y - = ZERO_SIZE_ADDITION / 2 ;
}
basesize = size ;
baseoffset = offset ;
2015-06-01 09:53:57 +00:00
selectionbasecenter = new Vector2D ( offset . x + size . x * 0.5f , offset . y + size . y * 0.5f ) ; //mxd
2009-04-19 18:07:22 +00:00
// When pasting, we want to move the geometry so it is visible
if ( pasting )
{
// Mouse in screen?
if ( mouseinside )
{
2013-11-01 11:14:42 +00:00
offset = mousemappos - size / 2 ;
2009-04-19 18:07:22 +00:00
}
else
{
Vector2D viewmappos = new Vector2D ( renderer . OffsetX , renderer . OffsetY ) ;
offset = viewmappos - size / 2 ;
}
2013-11-01 11:14:42 +00:00
if ( General . Interface . SnapToGrid ) //mxd
offset = General . Map . Grid . SnappedToGrid ( offset ) ;
2009-04-19 18:07:22 +00:00
UpdateGeometry ( ) ;
2009-05-13 10:58:13 +00:00
General . Map . Data . UpdateUsedTextures ( ) ;
2009-07-12 11:17:30 +00:00
2015-04-15 15:01:40 +00:00
if ( ! autodrag ) General . Map . Map . Update ( ) ;
2009-04-19 18:07:22 +00:00
}
// Set presentation
if ( selectedthings . Count > 0 )
renderer . SetPresentation ( Presentation . Things ) ;
else
renderer . SetPresentation ( Presentation . Standard ) ;
// Update
2009-08-21 08:38:24 +00:00
panel . ShowOriginalValues ( baseoffset , basesize ) ;
2015-06-01 09:53:57 +00:00
panel . SetTextureTransformSettings ( General . Map . UDMF ) ; //mxd
2016-05-20 21:40:04 +00:00
panel . SetHeightAdjustMode ( heightadjustmode , sectors . Count > 0 ) ; //mxd
2009-04-19 18:07:22 +00:00
UpdateRectangleComponents ( ) ;
2009-08-21 08:38:24 +00:00
UpdatePanel ( ) ;
2009-04-19 18:07:22 +00:00
Update ( ) ;
// When pasting and mouse is in screen, drag selection immediately
2016-01-18 20:37:21 +00:00
if ( autodrag )
{
OnSelectBegin ( ) ;
autodrag = false ; //mxd. Don't need this any more
}
2009-04-19 18:07:22 +00:00
}
else
{
2010-08-15 19:43:00 +00:00
General . Interface . MessageBeep ( MessageBeepType . Default ) ;
2011-12-04 10:34:10 +00:00
General . Interface . DisplayStatus ( StatusType . Info , "A selection is required for this action." ) ;
2009-04-19 18:07:22 +00:00
// Cancel now
General . Editing . CancelMode ( ) ;
}
}
// Cancel mode
public override void OnCancel ( )
{
base . OnCancel ( ) ;
// Paste operation?
if ( pasting )
{
2009-06-11 21:21:20 +00:00
// Resume normal undo/redo recording
General . Map . UndoRedo . IgnorePropChanges = false ;
2013-12-13 09:31:18 +00:00
General . Map . Map . BeginAddRemove ( ) ; //mxd
2009-04-19 18:07:22 +00:00
// Remove the geometry
2013-12-13 09:31:18 +00:00
foreach ( Vertex v in selectedvertices ) v . Dispose ( ) ;
foreach ( Thing t in selectedthings ) t . Dispose ( ) ;
2009-04-19 18:07:22 +00:00
2013-12-13 09:31:18 +00:00
General . Map . Map . EndAddRemove ( ) ; //mxd
2009-07-10 13:56:22 +00:00
// Withdraw the undo
if ( General . Map . UndoRedo . NextUndo ! = null )
General . Map . UndoRedo . WithdrawUndo ( ) ;
2009-04-19 18:07:22 +00:00
}
else
{
// Reset geometry in original position
int index = 0 ;
foreach ( Vertex v in selectedvertices )
v . Move ( vertexpos [ index + + ] ) ;
index = 0 ;
foreach ( Thing t in selectedthings )
{
t . Rotate ( thingangle [ index ] ) ;
t . Move ( thingpos [ index + + ] ) ;
}
2015-06-01 09:53:57 +00:00
//mxd. Reset texture offsets to original values
if ( General . Map . UDMF ) RestoreTextureTransform ( ) ;
2009-06-11 21:21:20 +00:00
// Resume normal undo/redo recording
General . Map . UndoRedo . IgnorePropChanges = false ;
2009-04-19 18:07:22 +00:00
}
General . Map . Map . Update ( true , true ) ;
// Return to previous stable mode
General . Editing . ChangeMode ( General . Editing . PreviousStableMode . Name ) ;
}
// When accepted
public override void OnAccept ( )
{
base . OnAccept ( ) ;
// Anything to do?
if ( ( selectedthings . Count > 0 ) | | ( selectedvertices . Count > 0 ) )
{
2010-08-13 18:32:21 +00:00
Vector2D tl = new Vector2D ( General . Map . Config . RightBoundary , General . Map . Config . BottomBoundary ) ;
Vector2D br = new Vector2D ( General . Map . Config . LeftBoundary , General . Map . Config . RightBoundary ) ;
2015-12-28 15:01:53 +00:00
foreach ( Vertex v in selectedvertices )
2010-08-13 18:32:21 +00:00
{
2015-12-28 15:01:53 +00:00
if ( v . Position . x < tl . x ) tl . x = ( int ) v . Position . x ;
if ( v . Position . x > br . x ) br . x = ( int ) v . Position . x ;
if ( v . Position . y > tl . y ) tl . y = ( int ) v . Position . y ;
if ( v . Position . y < br . y ) br . y = ( int ) v . Position . y ;
2010-08-13 18:32:21 +00:00
}
2015-12-28 15:01:53 +00:00
foreach ( Thing t in selectedthings )
2010-08-13 18:32:21 +00:00
{
2015-12-28 15:01:53 +00:00
if ( t . Position . x < tl . x ) tl . x = ( int ) t . Position . x ;
if ( t . Position . x > br . x ) br . x = ( int ) t . Position . x ;
if ( t . Position . y > tl . y ) tl . y = ( int ) t . Position . y ;
if ( t . Position . y < br . y ) br . y = ( int ) t . Position . y ;
2010-08-13 18:32:21 +00:00
}
// Check if the selection is outside the map boundaries
2015-12-28 15:01:53 +00:00
if ( tl . x < General . Map . Config . LeftBoundary | | br . x > General . Map . Config . RightBoundary | |
2010-08-13 18:32:21 +00:00
tl . y > General . Map . Config . TopBoundary | | br . y < General . Map . Config . BottomBoundary )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Error: selection out of map boundaries." ) ;
// If we're in the process of switching to another mode, reset to selection
// to its old position
2015-12-28 15:01:53 +00:00
if ( modealreadyswitching )
2010-08-13 18:32:21 +00:00
{
// Reset geometry in original position
int index = 0 ;
2015-12-28 15:01:53 +00:00
foreach ( Vertex v in selectedvertices )
2010-08-13 18:32:21 +00:00
v . Move ( vertexpos [ index + + ] ) ;
index = 0 ;
2015-12-28 15:01:53 +00:00
foreach ( Thing t in selectedthings )
2010-08-13 18:32:21 +00:00
{
t . Rotate ( thingangle [ index ] ) ;
t . Move ( thingpos [ index + + ] ) ;
}
2015-06-01 09:53:57 +00:00
//mxd. Reset texture offsets to their original position
if ( General . Map . UDMF ) RestoreTextureTransform ( ) ;
2010-08-13 18:32:21 +00:00
// Resume normal undo/redo recording
General . Map . UndoRedo . IgnorePropChanges = false ;
General . Map . Map . Update ( true , true ) ;
}
return ;
}
2009-04-19 18:07:22 +00:00
Cursor . Current = Cursors . AppStarting ;
if ( ! pasting )
{
// Reset geometry in original position to create an undo
if ( linesflipped ) FlipLinedefs ( ) ; // Flip linedefs back if they were flipped
int index = 0 ;
foreach ( Vertex v in selectedvertices )
v . Move ( vertexpos [ index + + ] ) ;
index = 0 ;
foreach ( Thing t in selectedthings )
{
t . Rotate ( thingangle [ index ] ) ;
t . Move ( thingpos [ index + + ] ) ;
}
2015-06-01 09:53:57 +00:00
//mxd. Reset texture offsets to their original position
if ( General . Map . UDMF ) RestoreTextureTransform ( ) ;
2009-05-03 20:21:52 +00:00
General . Map . Map . Update ( true , true ) ;
2009-04-19 18:07:22 +00:00
// Make undo
General . Map . UndoRedo . CreateUndo ( "Edit selection" ) ;
}
2009-06-11 21:21:20 +00:00
// Resume normal undo/redo recording
General . Map . UndoRedo . IgnorePropChanges = false ;
2020-05-16 10:26:16 +00:00
// Mark selected geometry
General . Map . Map . ClearAllMarks ( false ) ;
General . Map . Map . MarkAllSelectedGeometry ( true , true , true , true , false ) ;
// Move geometry to new position
UpdateGeometry ( ) ;
2015-04-15 15:01:40 +00:00
//mxd. Update sector slopes?
2020-05-16 10:26:16 +00:00
// Do this after UpdateGeometry() because it makes calculating the new slopes much easier
if ( General . Map . UDMF )
2015-04-15 15:01:40 +00:00
{
2020-05-16 10:26:16 +00:00
foreach ( Sector s in selectedsectors . Keys )
2015-04-15 15:01:40 +00:00
{
2020-05-16 10:26:16 +00:00
// Manually update the sector bounding boxes, because they still contain the old values
s . UpdateBBox ( ) ;
2015-04-15 15:01:40 +00:00
// Update floor slope?
2020-05-16 10:26:16 +00:00
if ( s . FloorSlope . GetLengthSq ( ) > 0 & & ! float . IsNaN ( s . FloorSlopeOffset / s . FloorSlope . z ) )
2015-04-15 15:01:40 +00:00
{
2020-05-16 10:26:16 +00:00
float angle = s . FloorSlope . GetAngleXY ( ) + rotation + Angle2D . PIHALF ;
// Not sure what the logic should be for flipping horizintally and/or vertically
//if (size.x < 0.0f) angle += Angle2D.PI;
//if (size.y < 0.0f) angle += Angle2D.PI;
2020-04-04 20:02:13 +00:00
Vector3D center = new Vector3D ( s . BBox . X + s . BBox . Width / 2 , s . BBox . Y + s . BBox . Height / 2 , slopeheights [ s ] [ 0 ] ) ;
2020-05-16 10:26:16 +00:00
Plane p = new Plane ( center , angle , - s . FloorSlope . GetAngleZ ( ) , true ) ;
2020-03-14 19:06:30 +00:00
s . FloorSlope = p . Normal ;
s . FloorSlopeOffset = p . Offset ;
2015-04-15 15:01:40 +00:00
}
// Update ceiling slope?
2020-05-16 10:26:16 +00:00
if ( s . CeilSlope . GetLengthSq ( ) > 0 & & ! float . IsNaN ( s . CeilSlopeOffset / s . CeilSlope . z ) )
2015-04-15 15:01:40 +00:00
{
2020-05-16 10:26:16 +00:00
float angle = s . CeilSlope . GetAngleXY ( ) + rotation + Angle2D . PIHALF ;
// Not sure what the logic should be for flipping horizintally and/or vertically
//if (size.x < 0.0f) angle += Angle2D.PI;
//if (size.y < 0.0f) angle += Angle2D.PI;
2020-04-04 20:02:13 +00:00
Vector3D center = new Vector3D ( s . BBox . X + s . BBox . Width / 2 , s . BBox . Y + s . BBox . Height / 2 , slopeheights [ s ] [ 1 ] ) ;
2020-05-16 10:26:16 +00:00
Plane p = new Plane ( center , angle , - s . CeilSlope . GetAngleZ ( ) , false ) ;
2020-03-14 19:06:30 +00:00
s . CeilSlope = p . Normal ;
s . CeilSlopeOffset = p . Offset ;
2015-04-15 15:01:40 +00:00
}
}
}
2015-06-01 09:53:57 +00:00
//mxd. Update floor/ceiling texture settings
2020-05-16 10:26:16 +00:00
if ( General . Map . UDMF ) UpdateTextureTransform ( ) ;
2015-06-01 09:53:57 +00:00
2009-04-19 18:07:22 +00:00
General . Map . Map . Update ( true , true ) ;
2016-05-20 15:04:00 +00:00
//mxd
int oldoutsidefloorheight = int . MinValue ;
int oldoutsideceilingheight = int . MinValue ;
2009-04-19 18:07:22 +00:00
// When pasting, we want to join with the parent sector
// where the sidedefs are referencing a virtual sector
if ( pasting )
{
Sector parent = null ;
Sector vsector = null ;
General . Settings . FindDefaultDrawSettings ( ) ;
// Go for all sidedes in the new geometry
List < Sidedef > newsides = General . Map . Map . GetMarkedSidedefs ( true ) ;
2016-10-31 18:52:29 +00:00
List < Linedef > oldlines = General . Map . Map . GetMarkedLinedefs ( false ) ; //mxd
//mxd. Let's use a blockmap...
RectangleF area = MapSet . CreateArea ( oldlines ) ;
BlockMap < BlockEntry > blockmap = new BlockMap < BlockEntry > ( area ) ;
blockmap . AddLinedefsSet ( oldlines ) ;
2015-12-28 15:01:53 +00:00
foreach ( Sidedef s in newsides )
2014-12-03 23:15:26 +00:00
{
2009-04-19 18:07:22 +00:00
// Connected to a virtual sector?
if ( s . Marked & & s . Sector . Fields . ContainsKey ( MapSet . VirtualSectorField ) )
{
bool joined = false ;
// Keep reference to virtual sector
vsector = s . Sector ;
// Not virtual on both sides?
// Pascal 3-1-08: I can't remember why I have this check here, but it causes problems when
// pasting a single linedef that refers to the same sector on both sides (the line then
// loses both its sidedefs because it doesn't join any sector)
//if((s.Other != null) && !s.Other.Sector.Fields.ContainsKey(MapSet.VirtualSectorField))
{
// Find out in which sector this was pasted
Vector2D testpoint = s . Line . GetSidePoint ( ! s . IsFront ) ;
2016-10-31 18:52:29 +00:00
Linedef nl = MapSet . NearestLinedef ( blockmap , testpoint ) ; //mxd
2014-12-03 23:15:26 +00:00
if ( nl ! = null )
{
2014-02-21 14:42:12 +00:00
Sidedef joinsidedef = ( nl . SideOfLine ( testpoint ) < = 0 ? nl . Front : nl . Back ) ;
2009-04-19 18:07:22 +00:00
// Join?
if ( joinsidedef ! = null )
{
// Join!
2009-06-11 21:21:20 +00:00
s . SetSector ( joinsidedef . Sector ) ;
2009-04-19 18:07:22 +00:00
s . Marked = false ;
joined = true ;
// If we have no parent sector yet, then this is it!
if ( parent = = null ) parent = joinsidedef . Sector ;
}
}
}
// Not joined any sector?
if ( ! joined )
{
Linedef l = s . Line ;
// Remove the sidedef
s . Dispose ( ) ;
// Correct the linedef
if ( ( l . Front = = null ) & & ( l . Back ! = null ) )
{
l . FlipVertices ( ) ;
l . FlipSidedefs ( ) ;
}
// Correct the sided flags
l . ApplySidedFlags ( ) ;
}
}
}
// Do we have a virtual and parent sector?
if ( ( vsector ! = null ) & & ( parent ! = null ) )
{
2016-05-29 00:38:55 +00:00
//mxd. Store floor/ceiling height
oldoutsidefloorheight = vsector . FloorHeight ;
oldoutsideceilingheight = vsector . CeilHeight ;
2009-04-19 18:07:22 +00:00
}
// Remove any virtual sectors
General . Map . Map . RemoveVirtualSectors ( ) ;
}
2016-05-20 15:04:00 +00:00
else
{
2016-05-29 00:38:55 +00:00
//mxd. Get floor/ceiling height from outside sectors
2016-05-20 15:04:00 +00:00
if ( unstablelines . Count = = 0 & & heightadjustmode ! = HeightAdjustMode . NONE )
{
// Get affected sectors
HashSet < Sector > affectedsectors = new HashSet < Sector > ( General . Map . Map . GetSelectedSectors ( true ) ) ;
2016-05-29 00:38:55 +00:00
Point outsideheights = GetOutsideHeights ( affectedsectors ) ;
oldoutsidefloorheight = outsideheights . X ;
oldoutsideceilingheight = outsideheights . Y ;
2016-05-20 15:04:00 +00:00
}
}
2016-04-25 14:48:39 +00:00
2016-06-02 09:55:01 +00:00
//mxd. We'll need sidedefs marked by StitchGeometry, not all sidedefs from selection...
General . Map . Map . ClearMarkedSidedefs ( false ) ;
2020-05-16 11:22:33 +00:00
// Snap to map format accuracy. We need to do that before stitching geometry because vertices that are very very slightly off the grid (like 0.00001) can
// cause problems with BlockMapGetBlockCoordinates in the 32bit version
General . Map . Map . SnapAllToAccuracy ( General . Map . UDMF & & usepreciseposition ) ;
2016-05-03 13:51:07 +00:00
// Stitch geometry
2016-06-02 09:55:01 +00:00
General . Map . Map . StitchGeometry ( General . Settings . MergeGeometryMode ) ;
2016-05-03 13:51:07 +00:00
2016-05-18 20:31:40 +00:00
// Snap to map format accuracy
General . Map . Map . SnapAllToAccuracy ( General . Map . UDMF & & usepreciseposition ) ;
2016-05-20 15:04:00 +00:00
//mxd. Get new lines from linedef marks...
2016-05-19 21:44:39 +00:00
HashSet < Linedef > newlines = new HashSet < Linedef > ( General . Map . Map . GetMarkedLinedefs ( true ) ) ;
2016-05-18 20:31:40 +00:00
2016-05-20 15:04:00 +00:00
//mxd. Marked lines were created during linedef splitting
2016-05-19 21:44:39 +00:00
HashSet < Linedef > changedlines = new HashSet < Linedef > ( selectedlines ) ;
changedlines . UnionWith ( newlines ) ;
2016-05-18 20:31:40 +00:00
2016-05-29 00:38:55 +00:00
//mxd. Update sector height?
if ( changedlines . Count > 0 & & heightadjustmode ! = HeightAdjustMode . NONE
& & oldoutsidefloorheight ! = int . MinValue & & oldoutsideceilingheight ! = int . MinValue )
2016-05-19 21:44:39 +00:00
{
2016-05-29 00:38:55 +00:00
// Sectors may've been created/removed when applying dragging...
HashSet < Sector > draggedsectors = new HashSet < Sector > ( General . Map . Map . GetMarkedSectors ( true ) ) ;
foreach ( Sector ss in selectedsectors . Keys ) if ( ! ss . IsDisposed ) draggedsectors . Add ( ss ) ;
2016-05-20 15:04:00 +00:00
2016-05-29 00:38:55 +00:00
// Change floor/ceiling height
AdjustSectorsHeight ( draggedsectors , heightadjustmode , oldoutsidefloorheight , oldoutsideceilingheight ) ;
2016-04-25 14:48:39 +00:00
}
2009-04-19 18:07:22 +00:00
// Update cached values
General . Map . Data . UpdateUsedTextures ( ) ;
General . Map . Map . Update ( ) ;
2011-12-02 21:03:05 +00:00
General . Map . ThingsFilter . Update ( ) ;
2009-07-23 11:19:50 +00:00
2015-09-04 12:44:09 +00:00
// Make normal selection?
2009-04-19 18:07:22 +00:00
General . Map . Map . ClearAllSelected ( ) ;
2015-09-04 12:44:09 +00:00
if ( ! clearselection ) //mxd
{
foreach ( Vertex v in selectedvertices ) if ( ! v . IsDisposed ) v . Selected = true ;
foreach ( Linedef l in selectedlines ) { if ( ! l . IsDisposed ) { l . Start . Selected = true ; l . End . Selected = true ; } }
foreach ( Thing t in selectedthings ) if ( ! t . IsDisposed ) t . Selected = true ;
}
2009-07-23 11:19:50 +00:00
General . Map . Map . SelectionType = SelectionType . Vertices | SelectionType . Things ;
2009-04-19 18:07:22 +00:00
// Done
selectedvertices = new List < Vertex > ( ) ;
selectedthings = new List < Thing > ( ) ;
2011-12-02 21:16:45 +00:00
selectedlines = new List < Linedef > ( ) ;
2009-04-19 18:07:22 +00:00
Cursor . Current = Cursors . Default ;
General . Map . IsChanged = true ;
}
if ( ! modealreadyswitching )
{
// Return to previous stable mode
General . Editing . ChangeMode ( General . Editing . PreviousStableMode . Name ) ;
}
}
// Mode disengages
public override void OnDisengage ( )
{
base . OnDisengage ( ) ;
// Remove toolbar buttons
2016-11-26 00:02:56 +00:00
General . Interface . BeginToolbarUpdate ( ) ; //mxd
2009-04-19 18:07:22 +00:00
General . Interface . RemoveButton ( BuilderPlug . Me . MenusForm . FlipSelectionH ) ;
General . Interface . RemoveButton ( BuilderPlug . Me . MenusForm . FlipSelectionV ) ;
2016-11-26 00:02:56 +00:00
General . Interface . EndToolbarUpdate ( ) ; //mxd
2015-06-19 19:19:41 +00:00
//mxd. Save EditPanel-related settings
2016-05-20 15:04:00 +00:00
General . Settings . WritePluginSetting ( "editselectionmode.usepreciseposition" , usepreciseposition ) ;
General . Settings . WritePluginSetting ( "editselectionmode.heightadjustmode" , ( int ) heightadjustmode ) ;
2009-08-21 08:38:24 +00:00
// Remove docker
General . Interface . RemoveDocker ( docker ) ;
panel . Dispose ( ) ;
panel = null ;
2009-04-19 18:07:22 +00:00
// When not cancelled manually, we assume it is accepted
if ( ! cancelled )
{
modealreadyswitching = true ;
2010-08-22 11:36:09 +00:00
//this.OnAccept(); // BAD! Any other plugins won't know this mode was accepted
General . Editing . AcceptMode ( ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-09 17:38:59 +00:00
// Update
General . Map . ThingsFilter . Update ( ) ;
General . Interface . RedrawDisplay ( ) ;
2009-04-19 18:07:22 +00:00
// Hide highlight info
General . Interface . HideInfo ( ) ;
General . Interface . SetCursor ( Cursors . Default ) ;
}
// This redraws the display
public override void OnRedrawDisplay ( )
{
UpdateRectangleComponents ( ) ;
renderer . RedrawSurface ( ) ;
// Render lines
if ( renderer . StartPlotter ( true ) )
{
renderer . PlotLinedefSet ( General . Map . Map . Linedefs ) ;
renderer . PlotVerticesSet ( General . Map . Map . Vertices ) ;
2016-03-18 12:52:12 +00:00
if ( highlighted is Vertex ) renderer . PlotVertex ( ( Vertex ) highlighted , ColorCollection . HIGHLIGHT ) ;
2009-04-19 18:07:22 +00:00
renderer . Finish ( ) ;
}
// Render things
if ( renderer . StartThings ( true ) )
{
2016-03-30 11:29:39 +00:00
renderer . RenderThingSet ( General . Map . ThingsFilter . HiddenThings , General . Settings . HiddenThingsAlpha ) ;
2016-04-01 10:49:19 +00:00
renderer . RenderThingSet ( General . Map . ThingsFilter . VisibleThings , General . Settings . ActiveThingsAlpha ) ;
if ( highlighted is Thing ) renderer . RenderThing ( ( Thing ) highlighted , General . Colors . Highlight , General . Settings . ActiveThingsAlpha ) ;
2009-04-19 18:07:22 +00:00
renderer . Finish ( ) ;
}
// Render selection
if ( renderer . StartOverlay ( true ) )
{
// Rectangle
PixelColor rectcolor = General . Colors . Highlight . WithAlpha ( RECTANGLE_ALPHA ) ;
renderer . RenderGeometry ( cornerverts , null , true ) ;
renderer . RenderLine ( corners [ 0 ] , corners [ 1 ] , 4 , rectcolor , true ) ;
renderer . RenderLine ( corners [ 1 ] , corners [ 2 ] , 4 , rectcolor , true ) ;
renderer . RenderLine ( corners [ 2 ] , corners [ 3 ] , 4 , rectcolor , true ) ;
renderer . RenderLine ( corners [ 3 ] , corners [ 0 ] , 4 , rectcolor , true ) ;
// Extension line
if ( extensionline . GetLengthSq ( ) > 0.0f )
renderer . RenderLine ( extensionline . v1 , extensionline . v2 , 1 , General . Colors . Indication . WithAlpha ( EXTENSION_LINE_ALPHA ) , true ) ;
// Grips
for ( int i = 0 ; i < 4 ; i + + )
{
renderer . RenderRectangleFilled ( resizegrips [ i ] , General . Colors . Background , true ) ;
renderer . RenderRectangle ( resizegrips [ i ] , 2 , General . Colors . Highlight , true ) ;
renderer . RenderRectangleFilled ( rotategrips [ i ] , General . Colors . Background , true ) ;
renderer . RenderRectangle ( rotategrips [ i ] , 2 , General . Colors . Indication , true ) ;
}
2019-09-20 18:49:30 +00:00
2009-04-19 18:07:22 +00:00
renderer . Finish ( ) ;
}
renderer . Present ( ) ;
}
// Mouse moves
public override void OnMouseMove ( MouseEventArgs e )
{
base . OnMouseMove ( e ) ;
2015-06-01 09:53:57 +00:00
if ( panning ) return ; //mxd. Skip all this jazz while panning
2009-04-19 18:07:22 +00:00
Update ( ) ;
}
// Mouse leaves the display
public override void OnMouseLeave ( EventArgs e )
{
base . OnMouseLeave ( e ) ;
// Reset cursor
General . Interface . SetCursor ( Cursors . Default ) ;
}
// When edit button is pressed
protected override void OnEditBegin ( )
{
base . OnEditBegin ( ) ;
OnSelectBegin ( ) ;
}
// When edit button is released
protected override void OnEditEnd ( )
{
base . OnEditEnd ( ) ;
OnSelectEnd ( ) ;
}
// When select button is pressed
protected override void OnSelectBegin ( )
{
base . OnSelectBegin ( ) ;
2009-07-12 11:14:34 +00:00
if ( mode ! = ModifyMode . None ) return ;
2009-04-19 18:07:22 +00:00
// Used in many cases:
Vector2D center = offset + size * 0.5f ;
Vector2D delta ;
// Check what grip the mouse is over
2016-01-18 20:37:21 +00:00
Grip mousegrip = ( autodrag ? Grip . Main : CheckMouseGrip ( ) ) ; //mxd. We only want to move when starting auto-dragging
switch ( mousegrip )
2009-04-19 18:07:22 +00:00
{
// Drag main rectangle
case Grip . Main :
2009-07-06 08:34:14 +00:00
// Find the original position of the highlighted element
if ( highlighted is Vertex )
2009-04-19 18:07:22 +00:00
{
int index = 0 ;
foreach ( Vertex v in selectedvertices )
{
2016-01-18 20:37:21 +00:00
if ( v = = highlighted )
{
highlightedpos = vertexpos [ index ] ;
break ;
}
2009-04-19 18:07:22 +00:00
index + + ;
}
}
2009-07-06 08:34:14 +00:00
else if ( highlighted is Thing )
{
int index = 0 ;
foreach ( Thing t in selectedthings )
{
2016-01-18 20:37:21 +00:00
if ( t = = highlighted )
{
highlightedpos = thingpos [ index ] ;
break ;
}
2009-07-06 08:34:14 +00:00
index + + ;
}
}
2009-04-19 18:07:22 +00:00
dragoffset = mousemappos - offset ;
mode = ModifyMode . Dragging ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Resize
case Grip . SizeN :
// The resize vector is a unit vector in the direction of the resize.
// We multiply this with the sign of the current size, because the
// corners may be reversed when the selection is flipped.
resizevector = corners [ 1 ] - corners [ 2 ] ;
resizevector = resizevector . GetNormal ( ) * Math . Sign ( size . y ) ;
// The edgevector is a vector with length and direction of the edge perpendicular to the resizevector
edgevector = corners [ 1 ] - corners [ 0 ] ;
// Make the resize axis. This is a line with the length and direction
// of basesize used to calculate the resize percentage.
resizeaxis = new Line2D ( corners [ 2 ] , corners [ 2 ] + resizevector * basesize . y ) ;
// Original axis filter
resizefilter = new Vector2D ( 0.0f , 1.0f ) ;
// This is the corner that must stay in the same position
stickcorner = 2 ;
Highlight ( null ) ;
mode = ModifyMode . Resizing ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Resize
case Grip . SizeE :
// See description above
resizevector = corners [ 1 ] - corners [ 0 ] ;
resizevector = resizevector . GetNormal ( ) * Math . Sign ( size . x ) ;
edgevector = corners [ 1 ] - corners [ 2 ] ;
resizeaxis = new Line2D ( corners [ 0 ] , corners [ 0 ] + resizevector * basesize . x ) ;
resizefilter = new Vector2D ( 1.0f , 0.0f ) ;
stickcorner = 0 ;
Highlight ( null ) ;
mode = ModifyMode . Resizing ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Resize
case Grip . SizeS :
// See description above
resizevector = corners [ 2 ] - corners [ 1 ] ;
resizevector = resizevector . GetNormal ( ) * Math . Sign ( size . y ) ;
edgevector = corners [ 2 ] - corners [ 3 ] ;
resizeaxis = new Line2D ( corners [ 1 ] , corners [ 1 ] + resizevector * basesize . y ) ;
resizefilter = new Vector2D ( 0.0f , 1.0f ) ;
stickcorner = 0 ;
Highlight ( null ) ;
mode = ModifyMode . Resizing ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Resize
case Grip . SizeW :
// See description above
resizevector = corners [ 0 ] - corners [ 1 ] ;
resizevector = resizevector . GetNormal ( ) * Math . Sign ( size . x ) ;
edgevector = corners [ 0 ] - corners [ 3 ] ;
resizeaxis = new Line2D ( corners [ 1 ] , corners [ 1 ] + resizevector * basesize . x ) ;
resizefilter = new Vector2D ( 1.0f , 0.0f ) ;
stickcorner = 1 ;
Highlight ( null ) ;
mode = ModifyMode . Resizing ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Rotate
case Grip . RotateLB :
delta = corners [ 3 ] - center ;
rotategripangle = delta . GetAngle ( ) - rotation ;
Highlight ( null ) ;
mode = ModifyMode . Rotating ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Rotate
case Grip . RotateLT :
delta = corners [ 0 ] - center ;
rotategripangle = delta . GetAngle ( ) - rotation ;
Highlight ( null ) ;
mode = ModifyMode . Rotating ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Rotate
case Grip . RotateRB :
delta = corners [ 2 ] - center ;
rotategripangle = delta . GetAngle ( ) - rotation ;
Highlight ( null ) ;
mode = ModifyMode . Rotating ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Rotate
case Grip . RotateRT :
delta = corners [ 1 ] - center ;
rotategripangle = delta . GetAngle ( ) - rotation ;
Highlight ( null ) ;
mode = ModifyMode . Rotating ;
EnableAutoPanning ( ) ;
autopanning = true ;
break ;
// Outside the selection?
default :
// Accept and be done with it
General . Editing . AcceptMode ( ) ;
break ;
}
}
// When selected button is released
protected override void OnSelectEnd ( )
{
base . OnSelectEnd ( ) ;
// Remove extension line
extensionline = new Line2D ( ) ;
if ( autopanning )
{
DisableAutoPanning ( ) ;
autopanning = false ;
}
// No modifying mode
mode = ModifyMode . None ;
2015-06-01 09:53:57 +00:00
//mxd. Update floor/ceiling texture settings
if ( General . Map . UDMF ) UpdateTextureTransform ( ) ;
2009-04-19 18:07:22 +00:00
// Redraw
2009-05-03 20:21:52 +00:00
General . Map . Map . Update ( ) ;
2009-04-19 18:07:22 +00:00
General . Interface . RedrawDisplay ( ) ;
}
// When a key is released
public override void OnKeyUp ( KeyEventArgs e )
{
base . OnKeyUp ( e ) ;
if ( ( snaptogrid ! = ( General . Interface . ShiftState ^ General . Interface . SnapToGrid ) ) | |
( snaptonearest ! = ( General . Interface . CtrlState ^ General . Interface . AutoMerge ) ) ) Update ( ) ;
}
// When a key is pressed
public override void OnKeyDown ( KeyEventArgs e )
{
base . OnKeyDown ( e ) ;
if ( ( snaptogrid ! = ( General . Interface . ShiftState ^ General . Interface . SnapToGrid ) ) | |
( snaptonearest ! = ( General . Interface . CtrlState ^ General . Interface . AutoMerge ) ) ) Update ( ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Actions
// This clears the selection
[BeginAction("clearselection", BaseAction = true)]
public void ClearSelection ( )
{
2015-09-04 12:44:09 +00:00
//mxd. Accept changes
clearselection = true ;
2009-04-19 18:07:22 +00:00
General . Editing . AcceptMode ( ) ;
2015-09-04 12:44:09 +00:00
//mxd. Clear selection info
General . Interface . DisplayStatus ( StatusType . Selection , string . Empty ) ;
2009-04-19 18:07:22 +00:00
}
// Flip vertically
[BeginAction("flipselectionv")]
public void FlipVertically ( )
{
// Flip the selection
offset . y + = size . y ;
size . y = - size . y ;
// Update
UpdateGeometry ( ) ;
UpdateRectangleComponents ( ) ;
2009-05-13 09:35:13 +00:00
General . Map . Map . Update ( ) ;
2009-04-19 18:07:22 +00:00
General . Interface . RedrawDisplay ( ) ;
}
// Flip horizontally
[BeginAction("flipselectionh")]
public void FlipHorizontally ( )
{
// Flip the selection
offset . x + = size . x ;
size . x = - size . x ;
// Update
UpdateGeometry ( ) ;
UpdateRectangleComponents ( ) ;
2009-05-13 09:35:13 +00:00
General . Map . Map . Update ( ) ;
2009-04-19 18:07:22 +00:00
General . Interface . RedrawDisplay ( ) ;
}
#endregion
}
}