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 ;
using System.Collections.Generic ;
using System.Windows.Forms ;
using CodeImp.DoomBuilder.Windows ;
using CodeImp.DoomBuilder.Map ;
using CodeImp.DoomBuilder.Rendering ;
using CodeImp.DoomBuilder.Geometry ;
using CodeImp.DoomBuilder.Editing ;
using CodeImp.DoomBuilder.Actions ;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
[ EditMode ( DisplayName = "Vertices Mode" ,
SwitchAction = "verticesmode" , // Action name used to switch to this mode
ButtonImage = "VerticesMode.png" , // Image resource name for the button
ButtonOrder = int . MinValue + 0 , // Position of the button (lower is more to the left)
ButtonGroup = "000_editing" ,
2009-07-09 15:15:49 +00:00
UseByDefault = true ,
SafeStartMode = true ) ]
2009-04-19 18:07:22 +00:00
public class VerticesMode : BaseClassicMode
{
#region = = = = = = = = = = = = = = = = = = Constants
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
// Highlighted item
protected Vertex highlighted ;
2013-04-16 12:52:40 +00:00
private Linedef highlightedLine ;
2009-04-19 18:07:22 +00:00
// Interface
private bool editpressed ;
#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
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
public override void OnHelp ( )
{
General . ShowHelp ( "e_vertices.html" ) ;
}
// Cancel mode
public override void OnCancel ( )
{
base . OnCancel ( ) ;
// Return to this mode
General . Editing . ChangeMode ( new VerticesMode ( ) ) ;
}
// Mode engages
public override void OnEngage ( )
{
base . OnEngage ( ) ;
renderer . SetPresentation ( Presentation . Standard ) ;
2010-08-15 13:45:43 +00:00
// Add toolbar buttons
General . Interface . AddButton ( BuilderPlug . Me . MenusForm . CopyProperties ) ;
General . Interface . AddButton ( BuilderPlug . Me . MenusForm . PasteProperties ) ;
2013-04-11 09:27:16 +00:00
if ( General . Map . UDMF ) General . Interface . AddButton ( BuilderPlug . Me . MenusForm . TextureOffsetLock , ToolbarSection . Geometry ) ; //mxd
2010-08-15 13:45:43 +00:00
2009-04-19 18:07:22 +00:00
// Convert geometry selection to vertices only
General . Map . Map . ConvertSelection ( SelectionType . Vertices ) ;
}
// Mode disengages
public override void OnDisengage ( )
{
base . OnDisengage ( ) ;
2010-08-15 13:45:43 +00:00
// Remove toolbar buttons
General . Interface . RemoveButton ( BuilderPlug . Me . MenusForm . CopyProperties ) ;
General . Interface . RemoveButton ( BuilderPlug . Me . MenusForm . PasteProperties ) ;
2013-04-11 09:27:16 +00:00
if ( General . Map . UDMF ) General . Interface . RemoveButton ( BuilderPlug . Me . MenusForm . TextureOffsetLock ) ; //mxd
2010-08-15 13:45:43 +00:00
2009-04-19 18:07:22 +00:00
// Going to EditSelectionMode?
if ( General . Editing . NewMode is EditSelectionMode )
{
2009-08-19 11:42:20 +00:00
// Not pasting anything?
EditSelectionMode editmode = ( General . Editing . NewMode as EditSelectionMode ) ;
if ( ! editmode . Pasting )
2009-04-19 18:07:22 +00:00
{
2009-08-19 11:42:20 +00:00
// No selection made? But we have a highlight!
if ( ( General . Map . Map . GetSelectedVertices ( true ) . Count = = 0 ) & & ( highlighted ! = null ) )
{
// Make the highlight the selection
highlighted . Selected = true ;
}
2009-04-19 18:07:22 +00:00
}
}
// Hide highlight info
General . Interface . HideInfo ( ) ;
}
// This redraws the display
public override void OnRedrawDisplay ( )
{
renderer . RedrawSurface ( ) ;
// Render lines and vertices
if ( renderer . StartPlotter ( true ) )
{
renderer . PlotLinedefSet ( General . Map . Map . Linedefs ) ;
renderer . PlotVerticesSet ( General . Map . Map . Vertices ) ;
if ( ( highlighted ! = null ) & & ! highlighted . IsDisposed )
renderer . PlotVertex ( highlighted , ColorCollection . HIGHLIGHT ) ;
renderer . Finish ( ) ;
}
// Render things
if ( renderer . StartThings ( true ) )
{
renderer . RenderThingSet ( General . Map . ThingsFilter . HiddenThings , Presentation . THINGS_HIDDEN_ALPHA ) ;
renderer . RenderThingSet ( General . Map . ThingsFilter . VisibleThings , 1.0f ) ;
renderer . Finish ( ) ;
}
// Selecting?
if ( selecting )
{
// Render selection
if ( renderer . StartOverlay ( true ) )
{
RenderMultiSelection ( ) ;
renderer . Finish ( ) ;
}
}
renderer . Present ( ) ;
}
// This highlights a new item
protected void Highlight ( Vertex v )
{
// Update display
if ( renderer . StartPlotter ( false ) )
{
// Undraw previous highlight
2013-05-27 12:20:15 +00:00
if ( highlighted ! = null & & ! highlighted . IsDisposed )
2009-04-19 18:07:22 +00:00
renderer . PlotVertex ( highlighted , renderer . DetermineVertexColor ( highlighted ) ) ;
// Set new highlight
highlighted = v ;
// Render highlighted item
2013-05-27 12:20:15 +00:00
if ( highlighted ! = null & & ! highlighted . IsDisposed )
2009-04-19 18:07:22 +00:00
renderer . PlotVertex ( highlighted , ColorCollection . HIGHLIGHT ) ;
// Done
renderer . Finish ( ) ;
renderer . Present ( ) ;
}
// Show highlight info
if ( ( highlighted ! = null ) & & ! highlighted . IsDisposed )
General . Interface . ShowVertexInfo ( highlighted ) ;
else
General . Interface . HideInfo ( ) ;
}
2013-04-16 12:52:40 +00:00
//mxd
private void HighlightLine ( Linedef l ) {
// Update display
if ( renderer . StartPlotter ( false ) ) {
// Undraw previous highlight
2013-05-20 10:43:34 +00:00
if ( ( highlightedLine ! = null ) & & ! highlightedLine . IsDisposed ) {
2013-04-16 12:52:40 +00:00
renderer . PlotLinedef ( highlightedLine , renderer . DetermineLinedefColor ( highlightedLine ) ) ;
2013-05-27 12:20:15 +00:00
if ( highlighted = = null ) {
renderer . PlotVertex ( highlightedLine . Start , renderer . DetermineVertexColor ( highlightedLine . Start ) ) ;
renderer . PlotVertex ( highlightedLine . End , renderer . DetermineVertexColor ( highlightedLine . End ) ) ;
}
2013-05-20 10:43:34 +00:00
}
2013-04-16 12:52:40 +00:00
// Set new highlight
highlightedLine = l ;
// Render highlighted item
2013-05-20 10:43:34 +00:00
if ( ( highlightedLine ! = null ) & & ! highlightedLine . IsDisposed ) {
2013-04-16 12:52:40 +00:00
renderer . PlotLinedef ( highlightedLine , General . Colors . InfoLine ) ;
2013-05-27 12:20:15 +00:00
if ( highlighted ! = null & & ! highlighted . IsDisposed ) {
renderer . PlotVertex ( highlightedLine . Start , highlightedLine . Start = = highlighted ? ColorCollection . HIGHLIGHT : renderer . DetermineVertexColor ( highlightedLine . Start ) ) ;
renderer . PlotVertex ( highlightedLine . End , highlightedLine . End = = highlighted ? ColorCollection . HIGHLIGHT : renderer . DetermineVertexColor ( highlightedLine . End ) ) ;
} else {
renderer . PlotVertex ( highlightedLine . Start , renderer . DetermineVertexColor ( highlightedLine . Start ) ) ;
renderer . PlotVertex ( highlightedLine . End , renderer . DetermineVertexColor ( highlightedLine . End ) ) ;
}
2013-05-20 10:43:34 +00:00
}
2013-04-16 12:52:40 +00:00
// Done
renderer . Finish ( ) ;
renderer . Present ( ) ;
}
}
2009-04-19 18:07:22 +00:00
// Selection
protected override void OnSelectBegin ( )
{
// Item highlighted?
if ( ( highlighted ! = null ) & & ! highlighted . IsDisposed )
{
// Redraw highlight to show selection
if ( renderer . StartPlotter ( false ) )
{
renderer . PlotVertex ( highlighted , renderer . DetermineVertexColor ( highlighted ) ) ;
renderer . Finish ( ) ;
renderer . Present ( ) ;
}
}
base . OnSelectBegin ( ) ;
}
// End selection
protected override void OnSelectEnd ( )
{
// Not stopping from multiselection?
if ( ! selecting )
{
// Item highlighted?
if ( ( highlighted ! = null ) & & ! highlighted . IsDisposed )
{
2013-03-18 13:52:27 +00:00
//mxd. Flip selection
highlighted . Selected = ! highlighted . Selected ;
2009-04-19 18:07:22 +00:00
// Render highlighted item
if ( renderer . StartPlotter ( false ) )
{
renderer . PlotVertex ( highlighted , ColorCollection . HIGHLIGHT ) ;
renderer . Finish ( ) ;
renderer . Present ( ) ;
}
2013-03-28 11:37:08 +00:00
//mxd
} else if ( BuilderPlug . Me . AutoClearSelection & & General . Map . Map . SelectedVerticessCount > 0 ) {
General . Map . Map . ClearSelectedVertices ( ) ;
General . Interface . RedrawDisplay ( ) ;
2009-04-19 18:07:22 +00:00
}
}
base . OnSelectEnd ( ) ;
}
// Start editing
protected override void OnEditBegin ( )
{
bool snaptogrid = General . Interface . ShiftState ^ General . Interface . SnapToGrid ;
bool snaptonearest = General . Interface . CtrlState ^ General . Interface . AutoMerge ;
// Vertex highlighted?
if ( ( highlighted ! = null ) & & ! highlighted . IsDisposed )
{
// Edit pressed in this mode
editpressed = true ;
// Highlighted item not selected?
2009-07-09 14:03:47 +00:00
if ( ! highlighted . Selected & & ( BuilderPlug . Me . AutoClearSelection | | ( General . Map . Map . SelectedVerticessCount = = 0 ) ) )
2009-04-19 18:07:22 +00:00
{
// Make this the only selection
General . Map . Map . ClearSelectedVertices ( ) ;
highlighted . Selected = true ;
General . Interface . RedrawDisplay ( ) ;
}
// Update display
if ( renderer . StartPlotter ( false ) )
{
// Redraw highlight to show selection
renderer . PlotVertex ( highlighted , renderer . DetermineVertexColor ( highlighted ) ) ;
renderer . Finish ( ) ;
renderer . Present ( ) ;
}
}
2013-03-18 13:52:27 +00:00
else if ( ! selecting ) //mxd. We don't want to do this stuff while multiselecting
2009-04-19 18:07:22 +00:00
{
// Find the nearest linedef within highlight range
2009-05-20 15:03:08 +00:00
Linedef l = General . Map . Map . NearestLinedefRange ( mousemappos , BuilderPlug . Me . SplitLinedefsRange / renderer . Scale ) ;
2009-04-19 18:07:22 +00:00
if ( l ! = null )
{
// Create undo
General . Map . UndoRedo . CreateUndo ( "Split linedef" ) ;
Vector2D insertpos ;
// Snip to grid also?
if ( snaptogrid )
{
// Find all points where the grid intersects the line
List < Vector2D > points = l . GetGridIntersections ( ) ;
insertpos = mousemappos ;
float distance = float . MaxValue ;
foreach ( Vector2D p in points )
{
float pdist = Vector2D . DistanceSq ( p , mousemappos ) ;
if ( pdist < distance )
{
insertpos = p ;
distance = pdist ;
}
}
}
else
{
// Just use the nearest point on line
insertpos = l . NearestOnLine ( mousemappos ) ;
}
// Make the vertex
Vertex v = General . Map . Map . CreateVertex ( insertpos ) ;
2010-08-15 19:43:00 +00:00
if ( v = = null )
{
General . Map . UndoRedo . WithdrawUndo ( ) ;
return ;
}
2009-04-19 18:07:22 +00:00
// Snap to map format accuracy
v . SnapToAccuracy ( ) ;
// Split the line with this vertex
2010-08-15 19:43:00 +00:00
Linedef sld = l . Split ( v ) ;
if ( sld = = null )
{
General . Map . UndoRedo . WithdrawUndo ( ) ;
return ;
}
2012-03-19 21:31:10 +00:00
BuilderPlug . Me . AdjustSplitCoordinates ( l , sld ) ;
2009-04-19 18:07:22 +00:00
// Update
General . Map . Map . Update ( ) ;
2010-08-15 19:43:00 +00:00
2009-04-19 18:07:22 +00:00
// Highlight it
Highlight ( v ) ;
// Redraw display
General . Interface . RedrawDisplay ( ) ;
}
else
{
// Start drawing mode
DrawGeometryMode drawmode = new DrawGeometryMode ( ) ;
DrawnVertex v = DrawGeometryMode . GetCurrentPosition ( mousemappos , snaptonearest , snaptogrid , renderer , new List < DrawnVertex > ( ) ) ;
2010-08-13 18:32:21 +00:00
if ( drawmode . DrawPointAt ( v ) )
General . Editing . ChangeMode ( drawmode ) ;
else
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to draw point: outside of map boundaries." ) ;
2009-04-19 18:07:22 +00:00
}
}
base . OnEditBegin ( ) ;
}
// Done editing
protected override void OnEditEnd ( )
{
// Edit pressed in this mode?
if ( editpressed )
{
// Anything selected?
ICollection < Vertex > selected = General . Map . Map . GetSelectedVertices ( true ) ;
if ( selected . Count > 0 )
{
if ( General . Interface . IsActiveWindow )
{
// Show line edit dialog
General . Interface . ShowEditVertices ( selected ) ;
General . Map . Map . Update ( ) ;
// When a single vertex was selected, deselect it now
if ( selected . Count = = 1 ) General . Map . Map . ClearSelectedVertices ( ) ;
// Update entire display
2013-05-20 10:43:34 +00:00
General . Map . Renderer2D . Update3dFloorTagsList ( ) ; //mxd
2009-04-19 18:07:22 +00:00
General . Interface . RedrawDisplay ( ) ;
}
}
}
editpressed = false ;
base . OnEditEnd ( ) ;
}
// Mouse moves
public override void OnMouseMove ( MouseEventArgs e )
{
base . OnMouseMove ( e ) ;
2013-03-18 13:52:27 +00:00
//mxd
if ( selectpressed & & ! editpressed & & ! selecting ) {
// Check if moved enough pixels for multiselect
Vector2D delta = mousedownpos - mousepos ;
if ( ( Math . Abs ( delta . x ) > MULTISELECT_START_MOVE_PIXELS ) | |
( Math . Abs ( delta . y ) > MULTISELECT_START_MOVE_PIXELS ) ) {
// Start multiselecting
StartMultiSelection ( ) ;
}
}
2013-03-28 12:31:37 +00:00
else if ( paintselectpressed & & ! editpressed & & ! selecting ) //mxd. Drag-select
2013-03-18 13:52:27 +00:00
{
// Find the nearest thing within highlight range
Vertex v = General . Map . Map . NearestVertexSquareRange ( mousemappos , BuilderPlug . Me . HighlightRange / renderer . Scale ) ;
if ( v ! = null ) {
if ( v ! = highlighted ) {
//toggle selected state
if ( General . Interface . ShiftState ^ BuilderPlug . Me . AdditiveSelect )
v . Selected = true ;
else if ( General . Interface . CtrlState )
v . Selected = false ;
else
v . Selected = ! v . Selected ;
highlighted = v ;
// Update entire display
General . Interface . RedrawDisplay ( ) ;
}
} else if ( highlighted ! = null ) {
highlighted = null ;
Highlight ( null ) ;
// Update entire display
General . Interface . RedrawDisplay ( ) ;
}
}
else if ( e . Button = = MouseButtons . None ) // Not holding any buttons?
2009-04-19 18:07:22 +00:00
{
2013-04-16 12:52:40 +00:00
//mxd
// Find the nearest linedef within split linedefs range
Linedef l = General . Map . Map . NearestLinedefRange ( mousemappos , BuilderPlug . Me . SplitLinedefsRange / renderer . Scale ) ;
// Highlight if not the same
if ( l ! = highlightedLine ) HighlightLine ( l ) ;
2013-05-27 12:20:15 +00:00
// Find the nearest vertex within highlight range
Vertex v = General . Map . Map . NearestVertexSquareRange ( mousemappos , BuilderPlug . Me . HighlightRange / renderer . Scale ) ;
// Highlight if not the same
if ( v ! = highlighted ) Highlight ( v ) ;
2013-03-18 13:52:27 +00:00
}
2009-04-19 18:07:22 +00:00
}
// Mouse leaves
public override void OnMouseLeave ( EventArgs e )
{
base . OnMouseLeave ( e ) ;
// Highlight nothing
Highlight ( null ) ;
}
2013-03-18 13:52:27 +00:00
//mxd
protected override void OnPaintSelectBegin ( ) {
highlighted = null ;
base . OnPaintSelectBegin ( ) ;
}
2009-04-19 18:07:22 +00:00
// Mouse wants to drag
protected override void OnDragStart ( MouseEventArgs e )
{
base . OnDragStart ( e ) ;
// Edit button used?
if ( General . Actions . CheckActionActive ( null , "classicedit" ) )
{
// Anything highlighted?
if ( ( highlighted ! = null ) & & ! highlighted . IsDisposed )
{
// Highlighted item not selected?
if ( ! highlighted . Selected )
{
// Select only this vertex for dragging
General . Map . Map . ClearSelectedVertices ( ) ;
highlighted . Selected = true ;
}
// Start dragging the selection
2013-03-18 13:52:27 +00:00
if ( ! BuilderPlug . Me . DontMoveGeometryOutsideMapBoundary | | canDrag ( ) ) //mxd
General . Editing . ChangeMode ( new DragVerticesMode ( highlighted , mousedownmappos ) ) ;
}
}
}
//mxd. Check if any selected vertex is outside of map boundary
private bool canDrag ( ) {
ICollection < Vertex > selectedverts = General . Map . Map . GetSelectedVertices ( true ) ;
int unaffectedCount = 0 ;
foreach ( Vertex v in selectedverts ) {
// Make sure the vertex is inside the map boundary
if ( v . Position . x < General . Map . Config . LeftBoundary | | v . Position . x > General . Map . Config . RightBoundary
| | v . Position . y > General . Map . Config . TopBoundary | | v . Position . y < General . Map . Config . BottomBoundary ) {
v . Selected = false ;
unaffectedCount + + ;
2009-04-19 18:07:22 +00:00
}
}
2013-03-18 13:52:27 +00:00
if ( unaffectedCount = = selectedverts . Count ) {
General . Interface . DisplayStatus ( StatusType . Warning , "Unable to drag selection: " + ( selectedverts . Count = = 1 ? "selected vertex is" : "all of selected vertices are" ) + " outside of map boundary!" ) ;
General . Interface . RedrawDisplay ( ) ;
return false ;
}
if ( unaffectedCount > 0 )
General . Interface . DisplayStatus ( StatusType . Warning , unaffectedCount + " of selected vertices " + ( unaffectedCount = = 1 ? "is" : "are" ) + " outside of map boundary!" ) ;
return true ;
2009-04-19 18:07:22 +00:00
}
// This is called wheh selection ends
protected override void OnEndMultiSelection ( )
{
2009-08-19 11:09:10 +00:00
bool selectionvolume = ( ( Math . Abs ( base . selectionrect . Width ) > 0.1f ) & & ( Math . Abs ( base . selectionrect . Height ) > 0.1f ) ) ;
if ( selectionvolume )
2009-04-19 18:07:22 +00:00
{
2013-03-18 13:52:27 +00:00
//mxd
2013-04-09 10:56:05 +00:00
if ( marqueSelectionMode = = MarqueSelectionMode . SELECT ) {
// Go for all vertices
foreach ( Vertex v in General . Map . Map . Vertices )
v . Selected = selectionrect . Contains ( v . Position . x , v . Position . y ) ;
} else if ( marqueSelectionMode = = MarqueSelectionMode . ADD ) {
// Go for all vertices
foreach ( Vertex v in General . Map . Map . Vertices )
v . Selected | = selectionrect . Contains ( v . Position . x , v . Position . y ) ;
} else if ( marqueSelectionMode = = MarqueSelectionMode . SUBTRACT ) {
2009-07-07 11:29:56 +00:00
// Go for all vertices
foreach ( Vertex v in General . Map . Map . Vertices )
2013-03-18 13:52:27 +00:00
if ( selectionrect . Contains ( v . Position . x , v . Position . y ) ) v . Selected = false ;
2013-04-09 10:56:05 +00:00
} else { //should be Intersect
// Go for all vertices
foreach ( Vertex v in General . Map . Map . Vertices )
if ( ! selectionrect . Contains ( v . Position . x , v . Position . y ) ) v . Selected = false ;
2009-04-19 18:07:22 +00:00
}
}
base . OnEndMultiSelection ( ) ;
// Clear overlay
if ( renderer . StartOverlay ( true ) ) renderer . Finish ( ) ;
// Redraw
General . Interface . RedrawDisplay ( ) ;
}
// This is called when the selection is updated
protected override void OnUpdateMultiSelection ( )
{
base . OnUpdateMultiSelection ( ) ;
// Render selection
if ( renderer . StartOverlay ( true ) )
{
RenderMultiSelection ( ) ;
renderer . Finish ( ) ;
renderer . Present ( ) ;
}
}
// When copying
public override bool OnCopyBegin ( )
{
// No selection made? But we have a highlight!
if ( ( General . Map . Map . GetSelectedVertices ( true ) . Count = = 0 ) & & ( highlighted ! = null ) )
{
// Make the highlight the selection
highlighted . Selected = true ;
}
return base . OnCopyBegin ( ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Actions
2010-08-15 13:45:43 +00:00
// This copies the properties
[BeginAction("classiccopyproperties")]
public void CopyProperties ( )
{
// Determine source vertices
ICollection < Vertex > sel = null ;
if ( General . Map . Map . SelectedVerticessCount > 0 )
sel = General . Map . Map . GetSelectedVertices ( true ) ;
else if ( highlighted ! = null )
{
sel = new List < Vertex > ( ) ;
sel . Add ( highlighted ) ;
}
if ( sel ! = null )
{
// Copy properties from first source vertex
BuilderPlug . Me . CopiedVertexProps = new VertexProperties ( General . GetByIndex ( sel , 0 ) ) ;
General . Interface . DisplayStatus ( StatusType . Action , "Copied vertex properties." ) ;
}
}
// This pastes the properties
[BeginAction("classicpasteproperties")]
public void PasteProperties ( )
{
if ( BuilderPlug . Me . CopiedVertexProps ! = null )
{
// Determine target vertices
ICollection < Vertex > sel = null ;
if ( General . Map . Map . SelectedVerticessCount > 0 )
sel = General . Map . Map . GetSelectedVertices ( true ) ;
else if ( highlighted ! = null )
{
sel = new List < Vertex > ( ) ;
sel . Add ( highlighted ) ;
}
if ( sel ! = null )
{
// Apply properties to selection
General . Map . UndoRedo . CreateUndo ( "Paste vertex properties" ) ;
foreach ( Vertex v in sel )
{
BuilderPlug . Me . CopiedVertexProps . Apply ( v ) ;
}
General . Interface . DisplayStatus ( StatusType . Action , "Pasted vertex properties." ) ;
// Update and redraw
General . Map . IsChanged = true ;
General . Interface . RefreshInfo ( ) ;
General . Interface . RedrawDisplay ( ) ;
}
}
}
2009-04-19 18:07:22 +00:00
// This clears the selection
[BeginAction("clearselection", BaseAction = true)]
public void ClearSelection ( )
{
// Clear selection
General . Map . Map . ClearAllSelected ( ) ;
// Redraw
General . Interface . RedrawDisplay ( ) ;
}
// This creates a new vertex at the mouse position
[BeginAction("insertitem", BaseAction = true)]
public virtual void InsertVertexAction ( ) { VerticesMode . InsertVertex ( mousemappos , renderer . Scale ) ; }
public static void InsertVertex ( Vector2D mousemappos , float rendererscale )
{
bool snaptogrid = General . Interface . ShiftState ^ General . Interface . SnapToGrid ;
bool snaptonearest = General . Interface . CtrlState ^ General . Interface . AutoMerge ;
// Mouse in window?
if ( General . Interface . MouseInDisplay )
{
Vector2D insertpos ;
Linedef l = null ;
// Create undo
General . Map . UndoRedo . CreateUndo ( "Insert vertex" ) ;
// Snap to geometry?
2009-05-20 15:03:08 +00:00
l = General . Map . Map . NearestLinedefRange ( mousemappos , BuilderPlug . Me . SplitLinedefsRange / rendererscale ) ;
2009-04-19 18:07:22 +00:00
if ( snaptonearest & & ( l ! = null ) )
{
// Snip to grid also?
if ( snaptogrid )
{
// Find all points where the grid intersects the line
List < Vector2D > points = l . GetGridIntersections ( ) ;
insertpos = mousemappos ;
float distance = float . MaxValue ;
foreach ( Vector2D p in points )
{
float pdist = Vector2D . DistanceSq ( p , mousemappos ) ;
if ( pdist < distance )
{
insertpos = p ;
distance = pdist ;
}
}
}
else
{
// Just use the nearest point on line
insertpos = l . NearestOnLine ( mousemappos ) ;
}
}
// Snap to grid?
else if ( snaptogrid )
{
// Snap to grid
insertpos = General . Map . Grid . SnappedToGrid ( mousemappos ) ;
}
else
{
// Just insert here, don't snap to anything
insertpos = mousemappos ;
}
2010-08-15 19:43:00 +00:00
2009-04-19 18:07:22 +00:00
// Make the vertex
Vertex v = General . Map . Map . CreateVertex ( insertpos ) ;
2010-08-15 19:43:00 +00:00
if ( v = = null )
{
General . Map . UndoRedo . WithdrawUndo ( ) ;
return ;
}
2009-04-19 18:07:22 +00:00
// Snap to map format accuracy
v . SnapToAccuracy ( ) ;
// Split the line with this vertex
2013-03-18 13:52:27 +00:00
if ( snaptonearest )
2009-04-19 18:07:22 +00:00
{
2013-03-18 13:52:27 +00:00
//mxd. Check if snapped vertex is on top of a linedef
l = General . Map . Map . NearestLinedefRange ( insertpos , 1 / rendererscale ) ;
if ( l ! = null & & l . SideOfLine ( insertpos ) ! = 0 )
l = null ;
if ( l ! = null ) {
General . Interface . DisplayStatus ( StatusType . Action , "Split a linedef." ) ;
Linedef sld = l . Split ( v ) ;
if ( sld = = null ) {
General . Map . UndoRedo . WithdrawUndo ( ) ;
return ;
}
BuilderPlug . Me . AdjustSplitCoordinates ( l , sld ) ;
2012-03-19 21:31:10 +00:00
}
2009-04-19 18:07:22 +00:00
}
else
{
General . Interface . DisplayStatus ( StatusType . Action , "Inserted a vertex." ) ;
}
// Update
General . Map . Map . Update ( ) ;
2010-08-15 19:43:00 +00:00
2009-04-19 18:07:22 +00:00
// Redraw screen
General . Interface . RedrawDisplay ( ) ;
}
}
[BeginAction("deleteitem", BaseAction = true)]
2013-03-25 14:49:03 +00:00
public void DeleteItem ( ) {
2013-03-26 12:31:07 +00:00
deleteItem ( true , new List < Linedef > ( ) ) ; //mxd
2013-03-25 14:49:03 +00:00
}
2013-03-26 12:31:07 +00:00
//mxd. MMMMMMONSTROUS solution!
private void deleteItem ( bool createUndo , List < Linedef > redrawnLines )
2009-04-19 18:07:22 +00:00
{
// Make list of selected vertices
ICollection < Vertex > selected = General . Map . Map . GetSelectedVertices ( true ) ;
if ( ( selected . Count = = 0 ) & & ( highlighted ! = null ) & & ! highlighted . IsDisposed ) selected . Add ( highlighted ) ;
// Anything to do?
if ( selected . Count > 0 )
{
// Make undo
2013-03-25 14:49:03 +00:00
if ( createUndo ) { //mxd
if ( selected . Count > 1 ) {
General . Map . UndoRedo . CreateUndo ( "Delete " + selected . Count + " vertices" ) ;
General . Interface . DisplayStatus ( StatusType . Action , "Deleted " + selected . Count + " vertices." ) ;
} else {
General . Map . UndoRedo . CreateUndo ( "Delete vertex" ) ;
General . Interface . DisplayStatus ( StatusType . Action , "Deleted a vertex." ) ;
}
2009-04-19 18:07:22 +00:00
}
2013-03-25 14:49:03 +00:00
//mxd. Find sectors, affected by vertex removal.
2013-03-22 12:53:34 +00:00
Dictionary < Sector , Vector2D > toMerge = new Dictionary < Sector , Vector2D > ( ) ;
2013-03-25 14:49:03 +00:00
List < Sector > possiblyInvalidSectors = new List < Sector > ( ) ;
List < Sector > affectedSectors = new List < Sector > ( ) ;
2013-03-22 12:53:34 +00:00
2013-03-25 14:49:03 +00:00
//mxd
2013-03-22 12:53:34 +00:00
foreach ( Vertex v in selected ) {
foreach ( Linedef l in v . Linedefs ) {
2013-03-25 14:49:03 +00:00
if ( l . Front ! = null & & l . Front . Sector ! = null ) {
if ( ! affectedSectors . Contains ( l . Front . Sector ) )
affectedSectors . Add ( l . Front . Sector ) ;
2013-03-22 12:53:34 +00:00
2013-03-25 14:49:03 +00:00
foreach ( Sidedef side in l . Front . Sector . Sidedefs ) {
if ( side . Other ! = null & & side . Other . Sector ! = null & & ! affectedSectors . Contains ( side . Other . Sector ) )
affectedSectors . Add ( side . Other . Sector ) ;
}
if ( l . Front . Sector . Sidedefs . Count < 4 & & ! toMerge . ContainsKey ( l . Front . Sector ) )
toMerge . Add ( l . Front . Sector , new Vector2D ( l . Front . Sector . BBox . Location . X + l . Front . Sector . BBox . Width / 2 , l . Front . Sector . BBox . Location . Y + l . Front . Sector . BBox . Height / 2 ) ) ;
}
if ( l . Back ! = null & & l . Back . Sector ! = null ) {
if ( ! affectedSectors . Contains ( l . Back . Sector ) )
affectedSectors . Add ( l . Back . Sector ) ;
foreach ( Sidedef side in l . Back . Sector . Sidedefs ) {
if ( side . Other ! = null & & side . Other . Sector ! = null & & ! affectedSectors . Contains ( side . Other . Sector ) )
affectedSectors . Add ( side . Other . Sector ) ;
}
if ( l . Back . Sector . Sidedefs . Count < 4 & & ! toMerge . ContainsKey ( l . Back . Sector ) )
toMerge . Add ( l . Back . Sector , new Vector2D ( l . Back . Sector . BBox . Location . X + l . Back . Sector . BBox . Width / 2 , l . Back . Sector . BBox . Location . Y + l . Back . Sector . BBox . Height / 2 ) ) ;
}
2013-03-22 12:53:34 +00:00
}
}
2009-04-19 18:07:22 +00:00
// Go for all vertices that need to be removed
foreach ( Vertex v in selected )
{
// Not already removed automatically?
if ( ! v . IsDisposed )
{
// If the vertex only has 2 linedefs attached, then merge the linedefs
2013-03-26 12:31:07 +00:00
if ( v . Linedefs . Count = = 2 ) {
2009-04-19 18:07:22 +00:00
Linedef ld1 = General . GetByIndex ( v . Linedefs , 0 ) ;
Linedef ld2 = General . GetByIndex ( v . Linedefs , 1 ) ;
2013-03-22 12:53:34 +00:00
2013-03-26 12:31:07 +00:00
if ( ! ld1 . IsDisposed & & ! ld2 . IsDisposed ) {
//mxd. We don't want to do this if it will result in sector with 2 sidedefs
if ( ( ld1 . Front ! = null & & ld1 . Front . Sector ! = null & & ld1 . Front . Sector . Sidedefs . Count > 3 ) | |
( ld1 . Back ! = null & & ld1 . Back . Sector ! = null & & ld1 . Back . Sector . Sidedefs . Count > 3 ) | |
( ld2 . Front ! = null & & ld2 . Front . Sector ! = null & & ld2 . Front . Sector . Sidedefs . Count > 3 ) | |
( ld2 . Back ! = null & & ld2 . Back . Sector ! = null & & ld2 . Back . Sector . Sidedefs . Count > 3 ) ) {
2013-03-22 12:53:34 +00:00
2013-03-26 12:31:07 +00:00
Vertex v1 = ( ld1 . Start = = v ) ? ld1 . End : ld1 . Start ;
Vertex v2 = ( ld2 . Start = = v ) ? ld2 . End : ld2 . Start ;
2013-03-22 12:53:34 +00:00
2013-03-26 12:31:07 +00:00
//if both linedefs are on the same line
if ( ld1 . AngleDeg = = ld2 . AngleDeg | | ld1 . AngleDeg = = ld2 . AngleDeg - 180 ) {
if ( ld1 . Start = = v ) ld1 . SetStartVertex ( v2 ) ; else ld1 . SetEndVertex ( v2 ) ;
ld2 . Dispose ( ) ;
} else {
//mxd. Check if removing a vertex will collapse a sector with 3 "outer" sides
bool skip = false ;
if ( ld1 . Front ! = null ) {
foreach ( Sidedef side in ld1 . Front . Sector . Sidedefs ) {
if ( ! possiblyInvalidSectors . Contains ( ld1 . Front . Sector ) & & ( side . Line . Start = = v1 & & side . Line . End = = v2 ) | | side . Line . Start = = v2 & & side . Line . End = = v1 ) {
possiblyInvalidSectors . Add ( ld1 . Front . Sector ) ;
skip = true ;
}
}
}
2013-03-25 14:49:03 +00:00
2013-03-26 12:31:07 +00:00
if ( ld2 . Front ! = null ) {
foreach ( Sidedef side in ld2 . Front . Sector . Sidedefs ) {
if ( ! possiblyInvalidSectors . Contains ( ld2 . Front . Sector ) & & ( side . Line . Start = = v1 & & side . Line . End = = v2 ) | | side . Line . Start = = v2 & & side . Line . End = = v1 ) {
possiblyInvalidSectors . Add ( ld2 . Front . Sector ) ;
skip = true ;
}
}
2013-03-25 14:49:03 +00:00
}
2013-03-22 12:53:34 +00:00
2013-03-26 12:31:07 +00:00
if ( ld1 . Back ! = null ) {
foreach ( Sidedef side in ld1 . Back . Sector . Sidedefs ) {
if ( ! possiblyInvalidSectors . Contains ( ld1 . Back . Sector ) & & ( side . Line . Start = = v1 & & side . Line . End = = v2 ) | | side . Line . Start = = v2 & & side . Line . End = = v1 ) {
possiblyInvalidSectors . Add ( ld1 . Back . Sector ) ;
skip = true ;
}
}
2013-03-25 14:49:03 +00:00
}
2013-03-26 12:31:07 +00:00
if ( ld2 . Back ! = null ) {
foreach ( Sidedef side in ld2 . Back . Sector . Sidedefs ) {
if ( ! possiblyInvalidSectors . Contains ( ld2 . Back . Sector ) & & ( side . Line . Start = = v1 & & side . Line . End = = v2 ) | | side . Line . Start = = v2 & & side . Line . End = = v1 ) {
possiblyInvalidSectors . Add ( ld2 . Back . Sector ) ;
skip = true ;
}
}
2013-03-25 14:49:03 +00:00
}
2013-03-26 12:31:07 +00:00
//mxd
if ( ! skip ) {
DrawnVertex dv1 = new DrawnVertex ( ) ;
DrawnVertex dv2 = new DrawnVertex ( ) ;
dv1 . stitchline = true ;
dv2 . stitchline = true ;
dv1 . stitch = true ;
dv2 . stitch = true ;
if ( ld1 . Start = = v ) {
dv1 . pos = v2 . Position ;
dv2 . pos = v1 . Position ;
} else {
dv1 . pos = v1 . Position ;
dv2 . pos = v2 . Position ;
}
Tools . DrawLines ( new List < DrawnVertex > ( ) { dv1 , dv2 } ) ;
List < Linedef > newLines = General . Map . Map . GetMarkedLinedefs ( true ) ;
if ( newLines . Count > 0 ) redrawnLines . AddRange ( newLines ) ;
General . Map . IsChanged = true ;
General . Map . Map . Update ( ) ;
//if both of the lines are single sided, remove newly created sector
if ( ( ! ld1 . IsDisposed & & ! ld2 . IsDisposed ) & & ( ld1 . Front = = null | | ld1 . Back = = null ) & & ( ld2 . Front = = null | | ld2 . Back = = null ) ) {
int index = ld1 . Front ! = null ? ld1 . Front . Sector . Index : ld1 . Back . Sector . Index ;
General . Map . Map . GetSectorByIndex ( index ) . Dispose ( ) ;
foreach ( Linedef l in newLines ) {
if ( l . Front ! = null & & l . Front . Sector ! = null & & ! affectedSectors . Contains ( l . Front . Sector ) )
affectedSectors . Add ( l . Front . Sector ) ;
if ( l . Back ! = null & & l . Back . Sector ! = null & & ! affectedSectors . Contains ( l . Back . Sector ) )
affectedSectors . Add ( l . Back . Sector ) ;
}
} else {
tryJoinSectors ( ld1 ) ;
tryJoinSectors ( ld2 ) ;
ld1 . Dispose ( ) ;
ld2 . Dispose ( ) ;
}
2013-03-25 14:49:03 +00:00
}
}
}
2013-03-26 12:31:07 +00:00
}
} else { //mxd. More than 2 lines share this vertex
Linedef [ ] lines = new Linedef [ v . Linedefs . Count ] ;
v . Linedefs . CopyTo ( lines , 0 ) ;
2013-03-22 12:53:34 +00:00
2013-03-26 12:31:07 +00:00
List < Linedef > parallel = new List < Linedef > ( ) ;
2013-03-25 14:49:03 +00:00
2013-03-26 12:31:07 +00:00
foreach ( Linedef ld1 in lines ) {
foreach ( Linedef ld2 in lines ) {
if ( ld1 . Index = = ld2 . Index ) continue ;
2013-03-25 14:49:03 +00:00
2013-03-26 12:31:07 +00:00
if ( ld1 . AngleDeg = = ld2 . AngleDeg | | ld1 . AngleDeg = = General . ClampAngle ( ld2 . AngleDeg - 180 ) ) {
if ( ! parallel . Contains ( ld1 ) )
parallel . Add ( ld1 ) ;
if ( ! parallel . Contains ( ld2 ) )
parallel . Add ( ld2 ) ;
}
}
}
2013-03-25 14:49:03 +00:00
2013-03-26 12:31:07 +00:00
if ( parallel . Count = = 2 ) { //if there are 2 parallel lines, join them and remove the rest.
for ( int i = 0 ; i < lines . Length ; i + + ) {
if ( parallel . Contains ( lines [ i ] ) ) continue ;
Linedef ld = lines [ i ] ;
tryJoinSectors ( ld ) ;
ld . Dispose ( ) ;
}
2013-03-25 14:49:03 +00:00
2013-03-26 12:31:07 +00:00
Vertex v2 = ( parallel [ 1 ] . Start = = v ) ? parallel [ 1 ] . End : parallel [ 1 ] . Start ;
if ( parallel [ 0 ] . Start = = v ) parallel [ 0 ] . SetStartVertex ( v2 ) ; else parallel [ 0 ] . SetEndVertex ( v2 ) ;
parallel [ 1 ] . Dispose ( ) ;
} else { //just trash them
for ( int i = 0 ; i < lines . Length ; i + + ) {
Linedef ld = lines [ i ] ;
tryJoinSectors ( ld ) ;
ld . Dispose ( ) ;
2013-03-25 14:49:03 +00:00
}
2013-03-22 12:53:34 +00:00
}
2009-04-19 18:07:22 +00:00
}
// Trash vertex
v . Dispose ( ) ;
}
}
2013-03-22 12:53:34 +00:00
//mxd
Tools . MergeInvalidSectors ( toMerge ) ;
2013-03-25 14:49:03 +00:00
foreach ( Sector s in possiblyInvalidSectors ) {
//still invalid?
if ( ! s . IsDisposed & & s . Sidedefs . Count < 3 ) s . Dispose ( ) ;
}
foreach ( Sector s in affectedSectors ) {
if ( s . IsDisposed ) continue ;
foreach ( Sidedef side in s . Sidedefs ) {
if ( ( side . Line . Front = = null & & side . Line . Back ! = null ) | | ( side . Line . Front ! = null & & side . Line . Back = = null ) ) {
side . Line . ApplySidedFlags ( ) ;
} else if ( side . Line . Front = = null & & side . Line . Back ! = null & & side . Line . Back . Sector ! = null ) {
// Flip linedef
side . Line . FlipVertices ( ) ;
side . Line . FlipSidedefs ( ) ;
side . Line . ApplySidedFlags ( ) ;
}
}
}
2009-04-19 18:07:22 +00:00
// Update cache values
General . Map . IsChanged = true ;
General . Map . Map . Update ( ) ;
2013-03-25 14:49:03 +00:00
//mxd. Because... well... some new vertices might have been created during vertex removal process...
if ( General . Map . Map . GetSelectedVertices ( true ) . Count > 0 ) {
2013-03-26 12:31:07 +00:00
deleteItem ( false , redrawnLines ) ;
2013-03-25 14:49:03 +00:00
return ;
2013-03-26 12:31:07 +00:00
} else if ( redrawnLines . Count > 0 ) {
//try to merge redrawn lines
mergeLines ( redrawnLines ) ;
// Update cache values
General . Map . IsChanged = true ;
General . Map . Map . Update ( ) ;
2013-03-25 14:49:03 +00:00
}
2009-04-19 18:07:22 +00:00
// Invoke a new mousemove so that the highlighted item updates
MouseEventArgs e = new MouseEventArgs ( MouseButtons . None , 0 , ( int ) mousepos . x , ( int ) mousepos . y , 0 ) ;
OnMouseMove ( e ) ;
// Redraw screen
2013-05-20 10:43:34 +00:00
General . Map . Renderer2D . Update3dFloorTagsList ( ) ; //mxd
2009-04-19 18:07:22 +00:00
General . Interface . RedrawDisplay ( ) ;
}
}
2013-03-26 12:31:07 +00:00
//mxd. If there are different sectors on both sides of given linedef, join them
private void tryJoinSectors ( Linedef ld ) {
if ( ld . IsDisposed ) return ;
if ( ld . Front ! = null & & ld . Front . Sector ! = null & & ld . Back ! = null & & ld . Back . Sector ! = null & & ld . Front . Sector . Index ! = ld . Back . Sector . Index ) {
if ( ld . Front . Sector . BBox . Width * ld . Front . Sector . BBox . Height > ld . Back . Sector . BBox . Width * ld . Back . Sector . BBox . Height )
ld . Back . Sector . Join ( ld . Front . Sector ) ;
else
ld . Front . Sector . Join ( ld . Back . Sector ) ;
}
}
private void mergeLines ( List < Linedef > lines ) {
if ( lines . Count < 2 ) return ;
for ( int i = 0 ; i < lines . Count ; i + + ) {
if ( lines [ i ] . IsDisposed ) lines . RemoveAt ( i ) ;
}
for ( int i = 0 ; i < lines . Count ; i + + ) {
for ( int c = 0 ; c < lines . Count ; c + + ) {
if ( i = = c ) continue ;
//lines have the same angle?
if ( lines [ i ] . AngleDeg = = lines [ c ] . AngleDeg | | lines [ i ] . AngleDeg = = General . ClampAngle ( lines [ c ] . AngleDeg - 180 ) ) {
Vertex mid = null ;
//lines share a vertex?
if ( lines [ i ] . End = = lines [ c ] . Start | | lines [ i ] . End = = lines [ c ] . End ) {
mid = lines [ i ] . End ;
} else if ( lines [ i ] . Start = = lines [ c ] . Start | | lines [ i ] . Start = = lines [ c ] . End ) {
mid = lines [ i ] . Start ;
}
if ( mid = = null | | mid . Linedefs . Count > 2 ) continue ;
Vertex end = ( lines [ c ] . Start = = mid ) ? lines [ c ] . End : lines [ c ] . Start ;
//merge lines
if ( lines [ i ] . Start = = mid )
lines [ i ] . SetStartVertex ( end ) ;
else
lines [ i ] . SetEndVertex ( end ) ;
lines [ c ] . Dispose ( ) ;
//trash vertex
mid . Dispose ( ) ;
lines . RemoveAt ( c ) ;
//restart
mergeLines ( lines ) ;
return ;
}
}
}
}
2009-04-19 18:07:22 +00:00
#endregion
}
}