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 ;
2014-07-16 09:45:04 +00:00
using System.Collections ;
2009-04-19 18:07:22 +00:00
using System.Collections.Generic ;
2014-07-16 09:45:04 +00:00
using System.Collections.Specialized ;
2009-04-19 18:07:22 +00:00
using System.Drawing ;
using System.IO ;
2015-07-28 15:04:21 +00:00
using System.Linq ;
2009-07-09 14:03:47 +00:00
using CodeImp.DoomBuilder.Config ;
2014-07-16 09:45:04 +00:00
using CodeImp.DoomBuilder.Geometry ;
using CodeImp.DoomBuilder.IO ;
2016-04-04 22:20:49 +00:00
using CodeImp.DoomBuilder.Rendering ;
2014-07-16 09:45:04 +00:00
using CodeImp.DoomBuilder.Types ;
using CodeImp.DoomBuilder.Windows ;
2014-08-25 11:15:19 +00:00
using CodeImp.DoomBuilder.VisualModes ;
2017-02-20 06:14:07 +00:00
using System.Diagnostics ;
2009-04-19 18:07:22 +00:00
#endregion
namespace CodeImp.DoomBuilder.Map
{
2009-05-19 09:59:19 +00:00
/// <summary>
/// Manages all geometry structures and things in a map. Also provides
/// methods to works with selections and marking elements for any purpose.
/// Note that most methods are of O(n) complexity.
/// </summary>
2009-04-19 18:07:22 +00:00
public sealed class MapSet
{
#region = = = = = = = = = = = = = = = = = = Constants
2009-05-19 09:59:19 +00:00
/// <summary>Stiching distance. This is only to get around inaccuracies. Basically,
/// geometry only stitches when exactly on top of each other.</summary>
2016-06-19 00:09:53 +00:00
public const float STITCH_DISTANCE = 0.005f ; //mxd. 0.001f is not enough when drawing very long lines...
2009-04-19 18:07:22 +00:00
// Virtual sector identification
// This contains a character that is invalid in the UDMF standard, but valid
// in our parser, so that it can only be used by Doom Builder and will never
// conflict with any other valid UDMF field.
2020-11-21 21:38:16 +00:00
public const string VIRTUAL_SECTOR_FIELD = "!virtual_sector" ;
2009-04-19 18:07:22 +00:00
2014-07-16 09:45:04 +00:00
//mxd
private const string SELECTION_GROUPS_PATH = "selectiongroups" ;
2009-07-09 14:03:47 +00:00
// Handler for tag fields
public delegate void TagHandler < T > ( MapElement element , bool actionargument , UniversalType type , ref int value , T obj ) ;
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
// Sector indexing
private List < int > indexholes ;
private int lastsectorindex ;
// Sidedef indexing for (de)serialization
private Sidedef [ ] sidedefindices ;
// Map structures
2009-06-05 19:03:56 +00:00
private Vertex [ ] vertices ;
private Linedef [ ] linedefs ;
private Sidedef [ ] sidedefs ;
private Sector [ ] sectors ;
private Thing [ ] things ;
private int numvertices ;
private int numlinedefs ;
private int numsidedefs ;
private int numsectors ;
private int numthings ;
2009-06-07 10:26:06 +00:00
// Behavior
private int freezearrays ;
private bool autoremove ;
2009-04-19 18:07:22 +00:00
// Selected elements
private LinkedList < Vertex > sel_vertices ;
private LinkedList < Linedef > sel_linedefs ;
private LinkedList < Sector > sel_sectors ;
private LinkedList < Thing > sel_things ;
private SelectionType sel_type ;
2021-10-03 19:04:38 +00:00
// Unknown UDMF data that needs to be preserved
private List < UniversalEntry > unknownudmfdata ;
2009-06-05 19:03:56 +00:00
2009-04-19 18:07:22 +00:00
// Statics
private static long emptylongname ;
private static UniValue virtualsectorvalue ;
2022-01-03 13:33:34 +00:00
// Concurrency
private bool issafetoaccess ;
2009-06-05 19:03:56 +00:00
2009-04-19 18:07:22 +00:00
// Disposing
2014-01-10 15:08:39 +00:00
private bool isdisposed ;
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
2009-07-09 14:03:47 +00:00
/// <summary>Returns the number of selected sectors.</summary>
public int SelectedSectorsCount { get { return sel_sectors . Count ; } }
/// <summary>Returns the number of selected linedefs.</summary>
public int SelectedLinedefsCount { get { return sel_linedefs . Count ; } }
2009-04-19 18:07:22 +00:00
2009-07-09 14:03:47 +00:00
/// <summary>Returns the number of selected vertices.</summary>
public int SelectedVerticessCount { get { return sel_vertices . Count ; } }
/// <summary>Returns the number of selected things.</summary>
public int SelectedThingsCount { get { return sel_things . Count ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns a reference to the list of all vertices.</summary>
2009-06-05 19:03:56 +00:00
public ICollection < Vertex > Vertices { get { if ( freezearrays = = 0 ) return vertices ; else return new MapElementCollection < Vertex > ( ref vertices , numvertices ) ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns a reference to the list of all linedefs.</summary>
2009-06-05 19:03:56 +00:00
public ICollection < Linedef > Linedefs { get { if ( freezearrays = = 0 ) return linedefs ; else return new MapElementCollection < Linedef > ( ref linedefs , numlinedefs ) ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns a reference to the list of all sidedefs.</summary>
2009-06-05 19:03:56 +00:00
public ICollection < Sidedef > Sidedefs { get { if ( freezearrays = = 0 ) return sidedefs ; else return new MapElementCollection < Sidedef > ( ref sidedefs , numsidedefs ) ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns a reference to the list of all sectors.</summary>
2009-06-05 19:03:56 +00:00
public ICollection < Sector > Sectors { get { if ( freezearrays = = 0 ) return sectors ; else return new MapElementCollection < Sector > ( ref sectors , numsectors ) ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns a reference to the list of all things.</summary>
2009-06-05 19:03:56 +00:00
public ICollection < Thing > Things { get { if ( freezearrays = = 0 ) return things ; else return new MapElementCollection < Thing > ( ref things , numthings ) ; } }
2009-04-19 18:07:22 +00:00
2009-05-19 09:59:19 +00:00
/// <summary>Indicates if the map is disposed.</summary>
public bool IsDisposed { get { return isdisposed ; } }
/// <summary>Returns a reference to the list of selected vertices.</summary>
2009-04-19 18:07:22 +00:00
internal LinkedList < Vertex > SelectedVertices { get { return sel_vertices ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns a reference to the list of selected linedefs.</summary>
2009-04-19 18:07:22 +00:00
internal LinkedList < Linedef > SelectedLinedefs { get { return sel_linedefs ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns a reference to the list of selected sectors.</summary>
2009-04-19 18:07:22 +00:00
internal LinkedList < Sector > SelectedSectors { get { return sel_sectors ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns a reference to the list of selected things.</summary>
2009-04-19 18:07:22 +00:00
internal LinkedList < Thing > SelectedThings { get { return sel_things ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns the current type of selection.</summary>
public SelectionType SelectionType { get { return sel_type ; } set { sel_type = value ; } }
/// <summary>Long name to indicate "no texture". This is the long name for a single dash.</summary>
2009-04-19 18:07:22 +00:00
public static long EmptyLongName { get { return emptylongname ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns the name of the custom field that marks virtual sectors in pasted geometry.</summary>
2009-04-19 18:07:22 +00:00
public static string VirtualSectorField { get { return VIRTUAL_SECTOR_FIELD ; } }
2009-05-19 09:59:19 +00:00
/// <summary>Returns the value of the custom field that marks virtual sectors in pasted geometry.</summary>
2009-04-19 18:07:22 +00:00
public static UniValue VirtualSectorValue { get { return virtualsectorvalue ; } }
internal Sidedef [ ] SidedefIndices { get { return sidedefindices ; } }
2009-06-07 10:26:06 +00:00
internal bool AutoRemove { get { return autoremove ; } set { autoremove = value ; } }
2021-10-03 19:04:38 +00:00
internal List < UniversalEntry > UnknownUDMFData { get { return unknownudmfdata ; } set { unknownudmfdata = value ; } }
2022-01-03 13:33:34 +00:00
/// <summary>
/// If it's safe to access (either reading or modifying) the map data. May only be read or set from the UI thread to
/// avoid racing conditions. Code that wants to access the map data on on a timer or in another thread must honor
/// this setting to avoid exceptions
/// </summary>
public bool IsSafeToAccess { get { return issafetoaccess ; } set { issafetoaccess = value ; } }
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor for new empty map
internal MapSet ( )
{
// Initialize
2009-06-05 19:03:56 +00:00
vertices = new Vertex [ 0 ] ;
linedefs = new Linedef [ 0 ] ;
sidedefs = new Sidedef [ 0 ] ;
sectors = new Sector [ 0 ] ;
things = new Thing [ 0 ] ;
2009-04-19 18:07:22 +00:00
sel_vertices = new LinkedList < Vertex > ( ) ;
sel_linedefs = new LinkedList < Linedef > ( ) ;
sel_sectors = new LinkedList < Sector > ( ) ;
sel_things = new LinkedList < Thing > ( ) ;
indexholes = new List < int > ( ) ;
lastsectorindex = 0 ;
2009-06-07 10:26:06 +00:00
autoremove = true ;
2021-10-03 19:04:38 +00:00
unknownudmfdata = new List < UniversalEntry > ( ) ;
2022-01-03 13:33:34 +00:00
issafetoaccess = true ;
2009-04-19 18:07:22 +00:00
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
// Constructor for map to deserialize
internal MapSet ( MemoryStream stream )
{
// Initialize
2009-06-05 19:03:56 +00:00
vertices = new Vertex [ 0 ] ;
linedefs = new Linedef [ 0 ] ;
sidedefs = new Sidedef [ 0 ] ;
sectors = new Sector [ 0 ] ;
things = new Thing [ 0 ] ;
2009-04-19 18:07:22 +00:00
sel_vertices = new LinkedList < Vertex > ( ) ;
sel_linedefs = new LinkedList < Linedef > ( ) ;
sel_sectors = new LinkedList < Sector > ( ) ;
sel_things = new LinkedList < Thing > ( ) ;
indexholes = new List < int > ( ) ;
lastsectorindex = 0 ;
2009-06-07 10:26:06 +00:00
autoremove = true ;
2021-10-03 19:04:38 +00:00
unknownudmfdata = new List < UniversalEntry > ( ) ;
2009-04-19 18:07:22 +00:00
2022-01-03 13:33:34 +00:00
issafetoaccess = true ;
2009-04-19 18:07:22 +00:00
// Deserialize
Deserialize ( stream ) ;
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
// Disposer
internal void Dispose ( )
{
// Not already disposed?
if ( ! isdisposed )
{
// Already set isdisposed so that changes can be prohibited
isdisposed = true ;
2009-06-05 19:03:56 +00:00
BeginAddRemove ( ) ;
2009-04-19 18:07:22 +00:00
// Dispose all things
2009-06-05 19:03:56 +00:00
while ( ( things . Length > 0 ) & & ( things [ 0 ] ! = null ) )
things [ 0 ] . Dispose ( ) ;
2009-04-19 18:07:22 +00:00
// Dispose all sectors
2009-06-05 19:03:56 +00:00
while ( ( sectors . Length > 0 ) & & ( sectors [ 0 ] ! = null ) )
sectors [ 0 ] . Dispose ( ) ;
2009-04-19 18:07:22 +00:00
// Dispose all sidedefs
2009-06-05 19:03:56 +00:00
while ( ( sidedefs . Length > 0 ) & & ( sidedefs [ 0 ] ! = null ) )
sidedefs [ 0 ] . Dispose ( ) ;
2009-04-19 18:07:22 +00:00
// Dispose all linedefs
2009-06-05 19:03:56 +00:00
while ( ( linedefs . Length > 0 ) & & ( linedefs [ 0 ] ! = null ) )
linedefs [ 0 ] . Dispose ( ) ;
2009-04-19 18:07:22 +00:00
// Dispose all vertices
2009-06-05 19:03:56 +00:00
while ( ( vertices . Length > 0 ) & & ( vertices [ 0 ] ! = null ) )
vertices [ 0 ] . Dispose ( ) ;
2009-04-19 18:07:22 +00:00
// Clean up
vertices = null ;
linedefs = null ;
sidedefs = null ;
sectors = null ;
things = null ;
sel_vertices = null ;
sel_linedefs = null ;
sel_sectors = null ;
sel_things = null ;
indexholes = null ;
// Done
isdisposed = true ;
}
}
// Static initializer
internal static void Initialize ( )
{
2014-12-03 09:06:05 +00:00
emptylongname = Lump . MakeLongName ( "-" , false ) ;
2013-08-14 09:02:37 +00:00
virtualsectorvalue = new UniValue ( ( int ) UniversalType . Integer , 0 ) ;
2009-04-19 18:07:22 +00:00
}
#endregion
#region = = = = = = = = = = = = = = = = = = Management
2009-05-19 09:59:19 +00:00
2009-06-05 19:03:56 +00:00
// This begins large add/remove operations
public void BeginAddRemove ( )
{
freezearrays + + ;
}
// This allocates the arrays to a minimum size so that
// a lot of items can be created faster. This function will never
// allocate less than the current number of items.
public void SetCapacity ( int nvertices , int nlinedefs , int nsidedefs , int nsectors , int nthings )
{
if ( freezearrays = = 0 )
throw new Exception ( "You must call BeginAddRemove before setting the reserved capacity." ) ;
if ( numvertices < nvertices )
Array . Resize ( ref vertices , nvertices ) ;
if ( numlinedefs < nlinedefs )
Array . Resize ( ref linedefs , nlinedefs ) ;
if ( numsidedefs < nsidedefs )
Array . Resize ( ref sidedefs , nsidedefs ) ;
if ( numsectors < nsectors )
Array . Resize ( ref sectors , nsectors ) ;
if ( numthings < nthings )
Array . Resize ( ref things , nthings ) ;
}
// This ends add/remove operations and crops the arrays
public void EndAddRemove ( )
{
if ( freezearrays > 0 )
freezearrays - - ;
if ( freezearrays = = 0 )
{
if ( numvertices < vertices . Length )
Array . Resize ( ref vertices , numvertices ) ;
if ( numlinedefs < linedefs . Length )
Array . Resize ( ref linedefs , numlinedefs ) ;
if ( numsidedefs < sidedefs . Length )
Array . Resize ( ref sidedefs , numsidedefs ) ;
if ( numsectors < sectors . Length )
Array . Resize ( ref sectors , numsectors ) ;
if ( numthings < things . Length )
Array . Resize ( ref things , numthings ) ;
}
}
2009-05-19 09:59:19 +00:00
/// <summary>
/// This makes a deep copy and returns the new MapSet.
/// </summary>
2009-04-19 18:07:22 +00:00
public MapSet Clone ( )
{
// Create the map set
MapSet newset = new MapSet ( ) ;
2009-06-05 19:03:56 +00:00
newset . BeginAddRemove ( ) ;
2009-06-12 08:02:47 +00:00
newset . SetCapacity ( numvertices , numlinedefs , numsidedefs , numsectors , numthings ) ;
2009-06-05 19:03:56 +00:00
2009-04-19 18:07:22 +00:00
// Go for all vertices
foreach ( Vertex v in vertices )
{
// Make new vertex
v . Clone = newset . CreateVertex ( v . Position ) ;
v . CopyPropertiesTo ( v . Clone ) ;
}
// Go for all sectors
foreach ( Sector s in sectors )
{
// Make new sector
s . Clone = newset . CreateSector ( ) ;
s . CopyPropertiesTo ( s . Clone ) ;
}
// Go for all linedefs
foreach ( Linedef l in linedefs )
{
// Make new linedef
2015-12-28 15:01:53 +00:00
Linedef nl = newset . CreateLinedef ( l . Start . Clone , l . End . Clone ) ;
2009-04-19 18:07:22 +00:00
l . CopyPropertiesTo ( nl ) ;
// Linedef has a front side?
if ( l . Front ! = null )
{
// Make new sidedef
2015-12-28 15:01:53 +00:00
Sidedef nd = newset . CreateSidedef ( nl , true , l . Front . Sector . Clone ) ;
2009-04-19 18:07:22 +00:00
l . Front . CopyPropertiesTo ( nd ) ;
}
// Linedef has a back side?
if ( l . Back ! = null )
{
// Make new sidedef
2015-12-28 15:01:53 +00:00
Sidedef nd = newset . CreateSidedef ( nl , false , l . Back . Sector . Clone ) ;
2009-04-19 18:07:22 +00:00
l . Back . CopyPropertiesTo ( nd ) ;
}
}
2009-06-05 19:03:56 +00:00
2009-04-19 18:07:22 +00:00
// Go for all things
foreach ( Thing t in things )
{
// Make new thing
Thing nt = newset . CreateThing ( ) ;
t . CopyPropertiesTo ( nt ) ;
}
2009-06-05 19:03:56 +00:00
2009-04-19 18:07:22 +00:00
// Remove clone references
foreach ( Vertex v in vertices ) v . Clone = null ;
foreach ( Sector s in sectors ) s . Clone = null ;
2021-10-03 19:04:38 +00:00
// Copy unknown UDMF data
newset . UnknownUDMFData = new List < UniversalEntry > ( ) ;
foreach ( UniversalEntry e in unknownudmfdata )
{
if ( e . Value is UniversalCollection )
{
// The UniversalEntry value is a collection, so we have to copy all sub-elements
UniversalCollection uc = new UniversalCollection ( ) ;
foreach ( UniversalEntry ie in ( UniversalCollection ) e . Value )
uc . Add ( ie . Key , ie . Value ) ;
newset . UnknownUDMFData . Add ( new UniversalEntry ( e . Key , uc ) ) ;
}
else
{
// Just a normal UniversalEntry
newset . UnknownUDMFData . Add ( new UniversalEntry ( e . Key , e . Value ) ) ;
}
}
2009-04-19 18:07:22 +00:00
// Return the new set
2009-06-05 19:03:56 +00:00
newset . EndAddRemove ( ) ;
2009-04-19 18:07:22 +00:00
return newset ;
}
// This makes a deep copy of the marked geometry and binds missing sectors to a virtual sector
internal MapSet CloneMarked ( )
{
Sector virtualsector = null ;
// Create the map set
MapSet newset = new MapSet ( ) ;
2009-06-05 19:03:56 +00:00
newset . BeginAddRemove ( ) ;
2009-04-19 18:07:22 +00:00
// Get marked geometry
ICollection < Vertex > mvertices = GetMarkedVertices ( true ) ;
ICollection < Linedef > mlinedefs = GetMarkedLinedefs ( true ) ;
ICollection < Sector > msectors = GetMarkedSectors ( true ) ;
ICollection < Thing > mthings = GetMarkedThings ( true ) ;
2009-06-12 08:02:47 +00:00
newset . SetCapacity ( mvertices . Count , mlinedefs . Count , numsidedefs , msectors . Count , mthings . Count ) ;
2009-04-19 18:07:22 +00:00
// Go for all vertices
foreach ( Vertex v in mvertices )
{
// Make new vertex
v . Clone = newset . CreateVertex ( v . Position ) ;
v . CopyPropertiesTo ( v . Clone ) ;
}
// Go for all sectors
foreach ( Sector s in msectors )
{
// Make new sector
s . Clone = newset . CreateSector ( ) ;
s . CopyPropertiesTo ( s . Clone ) ;
}
// Go for all linedefs
foreach ( Linedef l in mlinedefs )
{
// Make new linedef
Linedef nl = newset . CreateLinedef ( l . Start . Clone , l . End . Clone ) ;
l . CopyPropertiesTo ( nl ) ;
// Linedef has a front side?
if ( l . Front ! = null )
{
Sidedef nd ;
// Sector on front side marked?
if ( l . Front . Sector . Marked )
{
// Make new sidedef
nd = newset . CreateSidedef ( nl , true , l . Front . Sector . Clone ) ;
}
else
{
// Make virtual sector if needed
if ( virtualsector = = null )
{
virtualsector = newset . CreateSector ( ) ;
l . Front . Sector . CopyPropertiesTo ( virtualsector ) ;
2009-06-11 21:21:20 +00:00
virtualsector . Fields . BeforeFieldsChange ( ) ;
2009-04-19 18:07:22 +00:00
virtualsector . Fields [ VIRTUAL_SECTOR_FIELD ] = new UniValue ( virtualsectorvalue ) ;
}
// Make new sidedef that links to the virtual sector
nd = newset . CreateSidedef ( nl , true , virtualsector ) ;
}
l . Front . CopyPropertiesTo ( nd ) ;
}
// Linedef has a back side?
if ( l . Back ! = null )
{
Sidedef nd ;
// Sector on front side marked?
if ( l . Back . Sector . Marked )
{
// Make new sidedef
nd = newset . CreateSidedef ( nl , false , l . Back . Sector . Clone ) ;
}
else
{
// Make virtual sector if needed
if ( virtualsector = = null )
{
virtualsector = newset . CreateSector ( ) ;
l . Back . Sector . CopyPropertiesTo ( virtualsector ) ;
2009-06-11 21:21:20 +00:00
virtualsector . Fields . BeforeFieldsChange ( ) ;
2009-04-19 18:07:22 +00:00
virtualsector . Fields [ VIRTUAL_SECTOR_FIELD ] = new UniValue ( virtualsectorvalue ) ;
}
// Make new sidedef that links to the virtual sector
nd = newset . CreateSidedef ( nl , false , virtualsector ) ;
}
l . Back . CopyPropertiesTo ( nd ) ;
}
}
// Go for all things
foreach ( Thing t in mthings )
{
// Make new thing
Thing nt = newset . CreateThing ( ) ;
t . CopyPropertiesTo ( nt ) ;
}
// Remove clone references
foreach ( Vertex v in vertices ) v . Clone = null ;
foreach ( Sector s in sectors ) s . Clone = null ;
// Return the new set
2009-06-05 19:03:56 +00:00
newset . EndAddRemove ( ) ;
2009-04-19 18:07:22 +00:00
return newset ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This creates a new vertex and returns it.</summary>
2009-04-19 18:07:22 +00:00
public Vertex CreateVertex ( Vector2D pos )
{
2010-08-15 19:43:00 +00:00
if ( numvertices = = General . Map . FormatInterface . MaxVertices )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of vertices reached." ) ;
return null ;
}
2009-04-19 18:07:22 +00:00
// Make the vertex
2009-06-05 19:03:56 +00:00
Vertex v = new Vertex ( this , numvertices , pos ) ;
2009-06-07 09:00:14 +00:00
AddItem ( v , ref vertices , numvertices , ref numvertices ) ;
return v ;
}
/// <summary>This creates a new vertex and returns it.</summary>
public Vertex CreateVertex ( int index , Vector2D pos )
{
2010-08-15 19:43:00 +00:00
if ( numvertices = = General . Map . FormatInterface . MaxVertices )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of vertices reached." ) ;
return null ;
}
2009-06-07 09:00:14 +00:00
// Make the vertex
Vertex v = new Vertex ( this , index , pos ) ;
AddItem ( v , ref vertices , index , ref numvertices ) ;
2009-04-19 18:07:22 +00:00
return v ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This creates a new linedef and returns it.</summary>
2009-04-19 18:07:22 +00:00
public Linedef CreateLinedef ( Vertex start , Vertex end )
{
2010-08-15 19:43:00 +00:00
if ( numlinedefs = = General . Map . FormatInterface . MaxLinedefs )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of linedefs reached." ) ;
return null ;
}
2009-04-19 18:07:22 +00:00
// Make the linedef
2009-06-05 19:03:56 +00:00
Linedef l = new Linedef ( this , numlinedefs , start , end ) ;
2009-06-07 09:00:14 +00:00
AddItem ( l , ref linedefs , numlinedefs , ref numlinedefs ) ;
return l ;
}
2009-04-19 18:07:22 +00:00
2009-06-07 09:00:14 +00:00
/// <summary>This creates a new linedef and returns it.</summary>
public Linedef CreateLinedef ( int index , Vertex start , Vertex end )
{
2010-08-15 19:43:00 +00:00
if ( numlinedefs = = General . Map . FormatInterface . MaxLinedefs )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of linedefs reached." ) ;
return null ;
}
2009-06-07 09:00:14 +00:00
// Make the linedef
Linedef l = new Linedef ( this , index , start , end ) ;
AddItem ( l , ref linedefs , index , ref numlinedefs ) ;
2009-04-19 18:07:22 +00:00
return l ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This creates a new sidedef and returns it.</summary>
2009-04-19 18:07:22 +00:00
public Sidedef CreateSidedef ( Linedef l , bool front , Sector s )
{
2010-08-15 19:43:00 +00:00
if ( numsidedefs = = int . MaxValue )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of sidedefs reached." ) ;
return null ;
}
2009-04-19 18:07:22 +00:00
// Make the sidedef
2009-06-05 19:03:56 +00:00
Sidedef sd = new Sidedef ( this , numsidedefs , l , front , s ) ;
2009-06-07 09:00:14 +00:00
AddItem ( sd , ref sidedefs , numsidedefs , ref numsidedefs ) ;
return sd ;
}
2009-04-19 18:07:22 +00:00
2009-06-07 09:00:14 +00:00
/// <summary>This creates a new sidedef and returns it.</summary>
public Sidedef CreateSidedef ( int index , Linedef l , bool front , Sector s )
{
2010-08-15 19:43:00 +00:00
if ( numsidedefs = = int . MaxValue )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of sidedefs reached." ) ;
return null ;
}
2009-06-07 09:00:14 +00:00
// Make the sidedef
Sidedef sd = new Sidedef ( this , index , l , front , s ) ;
AddItem ( sd , ref sidedefs , index , ref numsidedefs ) ;
2009-04-19 18:07:22 +00:00
return sd ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This creates a new sector and returns it.</summary>
2009-04-19 18:07:22 +00:00
public Sector CreateSector ( )
{
2009-06-07 09:00:14 +00:00
// Make the sector
return CreateSector ( numsectors ) ;
}
/// <summary>This creates a new sector and returns it.</summary>
public Sector CreateSector ( int index )
{
int fixedindex ;
2010-08-15 19:43:00 +00:00
if ( numsectors = = General . Map . FormatInterface . MaxSectors )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of sectors reached." ) ;
return null ;
}
2009-04-19 18:07:22 +00:00
// Do we have any index holes we can use?
if ( indexholes . Count > 0 )
{
// Take one of the index holes
2009-06-07 09:00:14 +00:00
fixedindex = indexholes [ indexholes . Count - 1 ] ;
2009-04-19 18:07:22 +00:00
indexholes . RemoveAt ( indexholes . Count - 1 ) ;
}
else
{
// Make a new index
2009-06-07 09:00:14 +00:00
fixedindex = lastsectorindex + + ;
2009-04-19 18:07:22 +00:00
}
// Make the sector
2009-06-07 09:00:14 +00:00
return CreateSectorEx ( fixedindex , index ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
// This creates a new sector with a specific fixed index
2009-06-07 09:00:14 +00:00
private Sector CreateSectorEx ( int fixedindex , int index )
2009-04-19 18:07:22 +00:00
{
2010-08-15 19:43:00 +00:00
if ( numsectors = = General . Map . FormatInterface . MaxSectors )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of sectors reached." ) ;
return null ;
}
2009-04-19 18:07:22 +00:00
// Make the sector
2009-06-07 09:00:14 +00:00
Sector s = new Sector ( this , index , fixedindex ) ;
AddItem ( s , ref sectors , index , ref numsectors ) ;
2009-04-19 18:07:22 +00:00
return s ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This creates a new thing and returns it.</summary>
2009-04-19 18:07:22 +00:00
public Thing CreateThing ( )
{
2013-09-11 09:47:53 +00:00
if ( numthings = = General . Map . FormatInterface . MaxThings )
2010-08-15 19:43:00 +00:00
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of things reached." ) ;
return null ;
}
2009-04-19 18:07:22 +00:00
// Make the thing
2009-06-05 19:03:56 +00:00
Thing t = new Thing ( this , numthings ) ;
2009-06-07 09:00:14 +00:00
AddItem ( t , ref things , numthings , ref numthings ) ;
return t ;
}
2009-04-19 18:07:22 +00:00
2009-06-07 09:00:14 +00:00
/// <summary>This creates a new thing and returns it.</summary>
public Thing CreateThing ( int index )
{
2013-09-11 09:47:53 +00:00
if ( numthings = = General . Map . FormatInterface . MaxThings )
2010-08-15 19:43:00 +00:00
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of things reached." ) ;
return null ;
}
2009-06-07 09:00:14 +00:00
// Make the thing
Thing t = new Thing ( this , index ) ;
AddItem ( t , ref things , index , ref numthings ) ;
2009-04-19 18:07:22 +00:00
return t ;
}
2009-06-07 09:00:14 +00:00
2021-06-03 10:02:51 +00:00
/// <summary>
/// This creates a temporary thing. The difference to normal thing creation is that the creation will not be recorded by the undo manager.
/// This should only be used in very specific cases, like creating a temporary player start for the "test from current position" action
/// </summary>
/// <returns>The new thing</returns>
internal Thing CreateTempThing ( )
{
if ( numthings = = General . Map . FormatInterface . MaxThings )
{
General . Interface . DisplayStatus ( StatusType . Warning , "Failed to complete operation: maximum number of things reached." ) ;
return null ;
}
// Make the thing
Thing t = new Thing ( this , numthings , false ) ;
AddItem ( t , ref things , numthings , ref numthings ) ;
return t ;
}
2009-06-07 09:00:14 +00:00
// This increases the size of the array to add an item
private void AddItem < T > ( T item , ref T [ ] array , int index , ref int counter ) where T : MapElement
{
// Only resize when there are no more free entries
if ( counter = = array . Length )
{
if ( freezearrays = = 0 )
Array . Resize ( ref array , counter + 1 ) ;
else
Array . Resize ( ref array , counter + 10 ) ;
}
// Move item at the given index if the new item is not added at the end
if ( index ! = counter )
{
array [ counter ] = array [ index ] ;
array [ counter ] . Index = counter ;
}
// Add item
array [ index ] = item ;
counter + + ;
}
2009-05-19 09:59:19 +00:00
2009-04-19 18:07:22 +00:00
// This adds a sector index hole
2009-05-19 09:59:19 +00:00
internal void AddSectorIndexHole ( int index )
2009-04-19 18:07:22 +00:00
{
indexholes . Add ( index ) ;
}
2009-06-05 19:03:56 +00:00
private void RemoveItem < T > ( ref T [ ] array , int index , ref int counter ) where T : MapElement
{
if ( index = = ( counter - 1 ) )
{
array [ index ] = null ;
}
else
{
array [ index ] = array [ counter - 1 ] ;
array [ index ] . Index = index ;
array [ counter - 1 ] = null ;
}
counter - - ;
if ( freezearrays = = 0 )
Array . Resize ( ref array , counter ) ;
}
internal void RemoveVertex ( int index )
{
RemoveItem ( ref vertices , index , ref numvertices ) ;
}
internal void RemoveLinedef ( int index )
{
RemoveItem ( ref linedefs , index , ref numlinedefs ) ;
}
internal void RemoveSidedef ( int index )
{
RemoveItem ( ref sidedefs , index , ref numsidedefs ) ;
}
internal void RemoveSector ( int index )
{
RemoveItem ( ref sectors , index , ref numsectors ) ;
}
2009-04-19 18:07:22 +00:00
2009-06-05 19:03:56 +00:00
internal void RemoveThing ( int index )
{
RemoveItem ( ref things , index , ref numthings ) ;
}
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Serialization
// This serializes the MapSet
internal MemoryStream Serialize ( )
{
MemoryStream stream = new MemoryStream ( 20000000 ) ; // Yes that is about 20 MB.
SerializerStream serializer = new SerializerStream ( stream ) ;
// Index the sidedefs
int sidedefindex = 0 ;
foreach ( Sidedef sd in sidedefs )
sd . SerializedIndex = sidedefindex + + ;
serializer . Begin ( ) ;
// Write private data
serializer . wInt ( lastsectorindex ) ;
serializer . wInt ( indexholes . Count ) ;
foreach ( int i in indexholes ) serializer . wInt ( i ) ;
// Write map data
WriteVertices ( serializer ) ;
WriteSectors ( serializer ) ;
WriteLinedefs ( serializer ) ;
WriteSidedefs ( serializer ) ;
WriteThings ( serializer ) ;
serializer . End ( ) ;
// Reallocate to keep only the used memory
stream . Capacity = ( int ) stream . Length ;
return stream ;
}
// This serializes things
private void WriteThings ( SerializerStream stream )
{
2009-06-05 19:03:56 +00:00
stream . wInt ( numthings ) ;
2009-04-19 18:07:22 +00:00
// Go for all things
foreach ( Thing t in things )
{
t . ReadWrite ( stream ) ;
}
}
// This serializes vertices
private void WriteVertices ( SerializerStream stream )
{
2009-06-05 19:03:56 +00:00
stream . wInt ( numvertices ) ;
2009-04-19 18:07:22 +00:00
// Go for all vertices
int index = 0 ;
foreach ( Vertex v in vertices )
{
v . SerializedIndex = index + + ;
v . ReadWrite ( stream ) ;
}
}
// This serializes linedefs
private void WriteLinedefs ( SerializerStream stream )
{
2009-06-05 19:03:56 +00:00
stream . wInt ( numlinedefs ) ;
2009-04-19 18:07:22 +00:00
// Go for all lines
int index = 0 ;
foreach ( Linedef l in linedefs )
{
l . SerializedIndex = index + + ;
stream . wInt ( l . Start . SerializedIndex ) ;
stream . wInt ( l . End . SerializedIndex ) ;
l . ReadWrite ( stream ) ;
}
}
// This serializes sidedefs
private void WriteSidedefs ( SerializerStream stream )
{
2009-06-05 19:03:56 +00:00
stream . wInt ( numsidedefs ) ;
2009-04-19 18:07:22 +00:00
// Go for all sidedefs
foreach ( Sidedef sd in sidedefs )
{
stream . wInt ( sd . Line . SerializedIndex ) ;
stream . wInt ( sd . Sector . SerializedIndex ) ;
stream . wBool ( sd . IsFront ) ;
sd . ReadWrite ( stream ) ;
}
}
// This serializes sectors
private void WriteSectors ( SerializerStream stream )
{
2009-06-05 19:03:56 +00:00
stream . wInt ( numsectors ) ;
2009-04-19 18:07:22 +00:00
// Go for all sectors
int index = 0 ;
foreach ( Sector s in sectors )
{
s . SerializedIndex = index + + ;
s . ReadWrite ( stream ) ;
2015-09-25 13:20:53 +00:00
s . Triangles . ReadWrite ( stream ) ; //mxd
2009-04-19 18:07:22 +00:00
}
}
#endregion
#region = = = = = = = = = = = = = = = = = = Deserialization
2015-09-25 13:20:53 +00:00
// This deserializes the MapSet
internal void Deserialize ( MemoryStream stream )
2009-04-19 18:07:22 +00:00
{
stream . Seek ( 0 , SeekOrigin . Begin ) ;
DeserializerStream deserializer = new DeserializerStream ( stream ) ;
deserializer . Begin ( ) ;
// Read private data
int c ;
deserializer . rInt ( out lastsectorindex ) ;
deserializer . rInt ( out c ) ;
indexholes = new List < int > ( c ) ;
for ( int i = 0 ; i < c ; i + + )
{
int index ; deserializer . rInt ( out index ) ;
indexholes . Add ( index ) ;
}
// Read map data
Vertex [ ] verticesarray = ReadVertices ( deserializer ) ;
Sector [ ] sectorsarray = ReadSectors ( deserializer ) ;
Linedef [ ] linedefsarray = ReadLinedefs ( deserializer , verticesarray ) ;
ReadSidedefs ( deserializer , linedefsarray , sectorsarray ) ;
ReadThings ( deserializer ) ;
deserializer . End ( ) ;
2016-02-01 22:04:00 +00:00
deserializer . Dispose ( ) ; //mxd
2009-04-19 18:07:22 +00:00
// Make table of sidedef indices
2009-06-05 19:03:56 +00:00
sidedefindices = new Sidedef [ numsidedefs ] ;
2009-04-19 18:07:22 +00:00
foreach ( Sidedef sd in sidedefs )
sidedefindices [ sd . SerializedIndex ] = sd ;
// Call PostDeserialize
foreach ( Sector s in sectors )
s . PostDeserialize ( this ) ;
}
// This deserializes things
private void ReadThings ( DeserializerStream stream )
{
int c ; stream . rInt ( out c ) ;
// Go for all things
for ( int i = 0 ; i < c ; i + + )
{
Thing t = CreateThing ( ) ;
t . ReadWrite ( stream ) ;
}
}
// This deserializes vertices
private Vertex [ ] ReadVertices ( DeserializerStream stream )
{
int c ; stream . rInt ( out c ) ;
Vertex [ ] array = new Vertex [ c ] ;
// Go for all vertices
for ( int i = 0 ; i < c ; i + + )
{
2009-06-11 21:21:20 +00:00
array [ i ] = CreateVertex ( new Vector2D ( ) ) ;
array [ i ] . ReadWrite ( stream ) ;
2009-04-19 18:07:22 +00:00
}
return array ;
}
// This deserializes linedefs
private Linedef [ ] ReadLinedefs ( DeserializerStream stream , Vertex [ ] verticesarray )
{
int c ; stream . rInt ( out c ) ;
Linedef [ ] array = new Linedef [ c ] ;
// Go for all lines
for ( int i = 0 ; i < c ; i + + )
{
int start , end ;
stream . rInt ( out start ) ;
stream . rInt ( out end ) ;
2009-06-11 21:21:20 +00:00
array [ i ] = CreateLinedef ( verticesarray [ start ] , verticesarray [ end ] ) ;
array [ i ] . ReadWrite ( stream ) ;
2009-04-19 18:07:22 +00:00
}
return array ;
}
// This deserializes sidedefs
private void ReadSidedefs ( DeserializerStream stream , Linedef [ ] linedefsarray , Sector [ ] sectorsarray )
{
int c ; stream . rInt ( out c ) ;
// Go for all sidedefs
for ( int i = 0 ; i < c ; i + + )
{
int lineindex , sectorindex ;
bool front ;
stream . rInt ( out lineindex ) ;
stream . rInt ( out sectorindex ) ;
stream . rBool ( out front ) ;
2009-06-11 21:21:20 +00:00
Sidedef sd = CreateSidedef ( linedefsarray [ lineindex ] , front , sectorsarray [ sectorindex ] ) ;
sd . ReadWrite ( stream ) ;
2009-04-19 18:07:22 +00:00
}
}
// This deserializes sectors
private Sector [ ] ReadSectors ( DeserializerStream stream )
{
int c ; stream . rInt ( out c ) ;
Sector [ ] array = new Sector [ c ] ;
// Go for all sectors
for ( int i = 0 ; i < c ; i + + )
{
2009-06-11 21:21:20 +00:00
array [ i ] = CreateSector ( ) ;
array [ i ] . ReadWrite ( stream ) ;
2015-09-25 13:20:53 +00:00
array [ i ] . Triangles . ReadWrite ( stream ) ; //mxd
2009-04-19 18:07:22 +00:00
}
return array ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Updating
2009-05-19 09:59:19 +00:00
/// <summary>
/// This updates the cache of all elements where needed. You must call this after making changes to the map.
/// </summary>
2009-04-19 18:07:22 +00:00
public void Update ( )
{
// Update all!
Update ( true , true ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>
/// This updates the cache of all elements where needed. It is not recommended to use this version, please use Update() instead.
/// </summary>
2009-04-19 18:07:22 +00:00
public void Update ( bool dolines , bool dosectors )
{
// Update all linedefs
if ( dolines ) foreach ( Linedef l in linedefs ) l . UpdateCache ( ) ;
2009-06-04 20:21:31 +00:00
2009-04-19 18:07:22 +00:00
// Update all sectors
2009-06-04 20:21:31 +00:00
if ( dosectors )
{
2022-01-03 13:33:34 +00:00
foreach ( Sector s in sectors )
{
s . Triangulate ( ) ;
s . UpdateBBox ( ) ;
}
2009-06-04 20:21:31 +00:00
General . Map . CRenderer2D . Surfaces . AllocateBuffers ( ) ;
2022-01-03 13:33:34 +00:00
foreach ( Sector s in sectors ) s . CreateSurfaces ( ) ;
2009-06-04 20:21:31 +00:00
General . Map . CRenderer2D . Surfaces . UnlockBuffers ( ) ;
}
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>
/// This updates the cache of all elements that is required after a configuration or settings change.
/// </summary>
2009-04-19 18:07:22 +00:00
public void UpdateConfiguration ( )
{
// Update all things
foreach ( Thing t in things ) t . UpdateConfiguration ( ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Selection
// This checks a flag in a selection type
2014-05-20 09:09:28 +00:00
private static bool InSelectionType ( SelectionType value , SelectionType bits )
2009-04-19 18:07:22 +00:00
{
return ( value & bits ) = = bits ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This converts the current selection to a different type of selection as specified.
/// Note that this function uses the markings to convert the selection.</summary>
2009-04-19 18:07:22 +00:00
public void ConvertSelection ( SelectionType target )
{
ConvertSelection ( SelectionType . All , target ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This converts the current selection to a different type of selection as specified.
/// Note that this function uses the markings to convert the selection.</summary>
2009-04-19 18:07:22 +00:00
public void ConvertSelection ( SelectionType source , SelectionType target )
{
ClearAllMarks ( false ) ;
switch ( target )
{
// Convert geometry selection to vertices only
case SelectionType . Vertices :
if ( InSelectionType ( source , SelectionType . Linedefs ) ) MarkSelectedLinedefs ( true , true ) ;
if ( InSelectionType ( source , SelectionType . Sectors ) ) General . Map . Map . MarkSelectedSectors ( true , true ) ;
2015-12-27 21:54:50 +00:00
ICollection < Vertex > verts = General . Map . Map . GetVerticesFromLinesMarks ( true ) ;
2009-04-19 18:07:22 +00:00
foreach ( Vertex v in verts ) v . Selected = true ;
verts = General . Map . Map . GetVerticesFromSectorsMarks ( true ) ;
foreach ( Vertex v in verts ) v . Selected = true ;
General . Map . Map . ClearSelectedSectors ( ) ;
General . Map . Map . ClearSelectedLinedefs ( ) ;
break ;
// Convert geometry selection to linedefs only
case SelectionType . Linedefs :
if ( InSelectionType ( source , SelectionType . Vertices ) ) MarkSelectedVertices ( true , true ) ;
if ( ! InSelectionType ( source , SelectionType . Linedefs ) ) ClearSelectedLinedefs ( ) ;
2015-12-27 21:54:50 +00:00
ICollection < Linedef > lines = General . Map . Map . LinedefsFromMarkedVertices ( false , true , false ) ;
2009-04-19 18:07:22 +00:00
foreach ( Linedef l in lines ) l . Selected = true ;
if ( InSelectionType ( source , SelectionType . Sectors ) )
{
foreach ( Sector s in General . Map . Map . Sectors )
{
if ( s . Selected )
{
foreach ( Sidedef sd in s . Sidedefs )
sd . Line . Selected = true ;
}
}
}
General . Map . Map . ClearSelectedSectors ( ) ;
General . Map . Map . ClearSelectedVertices ( ) ;
break ;
// Convert geometry selection to sectors only
case SelectionType . Sectors :
if ( InSelectionType ( source , SelectionType . Vertices ) ) MarkSelectedVertices ( true , true ) ;
if ( ! InSelectionType ( source , SelectionType . Linedefs ) ) ClearSelectedLinedefs ( ) ;
lines = LinedefsFromMarkedVertices ( false , true , false ) ;
foreach ( Linedef l in lines ) l . Selected = true ;
ClearMarkedSectors ( true ) ;
foreach ( Linedef l in linedefs )
{
if ( ! l . Selected )
{
if ( l . Front ! = null ) l . Front . Sector . Marked = false ;
if ( l . Back ! = null ) l . Back . Sector . Marked = false ;
}
}
ClearSelectedLinedefs ( ) ;
ClearSelectedVertices ( ) ;
if ( InSelectionType ( source , SelectionType . Sectors ) )
{
foreach ( Sector s in General . Map . Map . Sectors )
{
2015-03-09 09:17:46 +00:00
if ( s . Selected | | ( s . Marked & & s . Sidedefs . Count > 0 ) )
2009-04-19 18:07:22 +00:00
{
s . Selected = true ;
foreach ( Sidedef sd in s . Sidedefs )
sd . Line . Selected = true ;
}
}
}
else
{
foreach ( Sector s in General . Map . Map . Sectors )
{
2015-03-09 09:17:46 +00:00
if ( s . Marked & & s . Sidedefs . Count > 0 )
2009-04-19 18:07:22 +00:00
{
s . Selected = true ;
foreach ( Sidedef sd in s . Sidedefs )
sd . Line . Selected = true ;
}
else
{
s . Selected = false ;
}
}
}
break ;
default :
throw new ArgumentException ( "Unsupported selection target conversion" ) ;
}
// New selection type
sel_type = target ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears all selected items</summary>
2009-04-19 18:07:22 +00:00
public void ClearAllSelected ( )
{
ClearSelectedVertices ( ) ;
ClearSelectedThings ( ) ;
ClearSelectedLinedefs ( ) ;
ClearSelectedSectors ( ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears selected vertices.</summary>
2009-04-19 18:07:22 +00:00
public void ClearSelectedVertices ( )
{
sel_vertices . Clear ( ) ;
foreach ( Vertex v in vertices ) v . Selected = false ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears selected things.</summary>
2009-04-19 18:07:22 +00:00
public void ClearSelectedThings ( )
{
sel_things . Clear ( ) ;
foreach ( Thing t in things ) t . Selected = false ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears selected linedefs.</summary>
2009-04-19 18:07:22 +00:00
public void ClearSelectedLinedefs ( )
{
sel_linedefs . Clear ( ) ;
foreach ( Linedef l in linedefs ) l . Selected = false ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears selected sectors.</summary>
2009-04-19 18:07:22 +00:00
public void ClearSelectedSectors ( )
{
sel_sectors . Clear ( ) ;
foreach ( Sector s in sectors ) s . Selected = false ;
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of vertices that match a selected state.</summary>
2009-04-19 18:07:22 +00:00
public ICollection < Vertex > GetSelectedVertices ( bool selected )
{
if ( selected )
{
return new List < Vertex > ( sel_vertices ) ;
}
else
{
2009-06-05 19:03:56 +00:00
List < Vertex > list = new List < Vertex > ( numvertices - sel_vertices . Count ) ;
2009-04-19 18:07:22 +00:00
foreach ( Vertex v in vertices ) if ( ! v . Selected ) list . Add ( v ) ;
return list ;
}
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of things that match a selected state.</summary>
2009-04-19 18:07:22 +00:00
public ICollection < Thing > GetSelectedThings ( bool selected )
{
if ( selected )
{
return new List < Thing > ( sel_things ) ;
}
else
{
2009-06-05 19:03:56 +00:00
List < Thing > list = new List < Thing > ( numthings - sel_things . Count ) ;
2009-04-19 18:07:22 +00:00
foreach ( Thing t in things ) if ( ! t . Selected ) list . Add ( t ) ;
return list ;
}
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of linedefs that match a selected state.</summary>
2009-04-19 18:07:22 +00:00
public ICollection < Linedef > GetSelectedLinedefs ( bool selected )
{
if ( selected )
{
return new List < Linedef > ( sel_linedefs ) ;
}
else
{
2009-06-05 19:03:56 +00:00
List < Linedef > list = new List < Linedef > ( numlinedefs - sel_linedefs . Count ) ;
2009-04-19 18:07:22 +00:00
foreach ( Linedef l in linedefs ) if ( ! l . Selected ) list . Add ( l ) ;
return list ;
}
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of sidedefs that match a selected linedefs state.</summary>
2009-05-09 11:37:55 +00:00
public ICollection < Sidedef > GetSidedefsFromSelectedLinedefs ( bool selected )
{
if ( selected )
{
List < Sidedef > list = new List < Sidedef > ( sel_linedefs . Count ) ;
foreach ( Linedef ld in sel_linedefs )
{
if ( ld . Front ! = null ) list . Add ( ld . Front ) ;
if ( ld . Back ! = null ) list . Add ( ld . Back ) ;
}
return list ;
}
else
{
2009-06-05 19:03:56 +00:00
List < Sidedef > list = new List < Sidedef > ( numlinedefs - sel_linedefs . Count ) ;
2009-05-09 11:37:55 +00:00
foreach ( Linedef ld in linedefs )
{
if ( ! ld . Selected & & ( ld . Front ! = null ) ) list . Add ( ld . Front ) ;
if ( ! ld . Selected & & ( ld . Back ! = null ) ) list . Add ( ld . Back ) ;
}
return list ;
}
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of sectors that match a selected state.</summary>
2009-04-19 18:07:22 +00:00
public ICollection < Sector > GetSelectedSectors ( bool selected )
{
if ( selected )
{
return new List < Sector > ( sel_sectors ) ;
}
else
{
2009-06-05 19:03:56 +00:00
List < Sector > list = new List < Sector > ( numsectors - sel_sectors . Count ) ;
2009-04-19 18:07:22 +00:00
foreach ( Sector s in sectors ) if ( ! s . Selected ) list . Add ( s ) ;
return list ;
}
}
2009-05-19 09:59:19 +00:00
/// <summary>This selects or deselectes geometry based on marked elements.</summary>
2009-04-19 18:07:22 +00:00
public void SelectMarkedGeometry ( bool mark , bool select )
{
SelectMarkedVertices ( mark , select ) ;
SelectMarkedLinedefs ( mark , select ) ;
SelectMarkedSectors ( mark , select ) ;
SelectMarkedThings ( mark , select ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This selects or deselectes geometry based on marked elements.</summary>
2009-04-19 18:07:22 +00:00
public void SelectMarkedVertices ( bool mark , bool select )
{
foreach ( Vertex v in vertices ) if ( v . Marked = = mark ) v . Selected = select ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This selects or deselectes geometry based on marked elements.</summary>
2009-04-19 18:07:22 +00:00
public void SelectMarkedLinedefs ( bool mark , bool select )
{
foreach ( Linedef l in linedefs ) if ( l . Marked = = mark ) l . Selected = select ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This selects or deselectes geometry based on marked elements.</summary>
2009-04-19 18:07:22 +00:00
public void SelectMarkedSectors ( bool mark , bool select )
{
foreach ( Sector s in sectors ) if ( s . Marked = = mark ) s . Selected = select ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This selects or deselectes geometry based on marked elements.</summary>
2009-04-19 18:07:22 +00:00
public void SelectMarkedThings ( bool mark , bool select )
{
foreach ( Thing t in things ) if ( t . Marked = = mark ) t . Selected = select ;
}
2009-05-19 09:59:19 +00:00
2014-07-16 09:45:04 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Selection groups
2009-05-19 09:59:19 +00:00
/// <summary>This selects geometry by selection group index.</summary>
2009-04-19 18:07:22 +00:00
public void SelectVerticesByGroup ( int groupmask )
{
2014-07-16 09:45:04 +00:00
foreach ( Vertex e in vertices ) e . SelectByGroup ( groupmask ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This selects geometry by selection group index.</summary>
2009-04-19 18:07:22 +00:00
public void SelectLinedefsByGroup ( int groupmask )
{
2014-07-16 09:45:04 +00:00
foreach ( Linedef e in linedefs ) e . SelectByGroup ( groupmask ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This selects geometry by selection group index.</summary>
2009-04-19 18:07:22 +00:00
public void SelectSectorsByGroup ( int groupmask )
{
2014-07-16 09:45:04 +00:00
foreach ( Sector e in sectors ) e . SelectByGroup ( groupmask ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This selects geometry by selection group index.</summary>
2009-04-19 18:07:22 +00:00
public void SelectThingsByGroup ( int groupmask )
{
2014-07-16 09:45:04 +00:00
foreach ( Thing e in things ) e . SelectByGroup ( groupmask ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This adds the current selection to the specified selection group.</summary>
2013-04-01 11:06:01 +00:00
//mxd. switched groupmask to groupindex
public void AddSelectionToGroup ( int groupindex )
{
2014-07-16 09:45:04 +00:00
int groupmask = 0x01 < < groupindex ;
foreach ( Vertex e in vertices ) if ( e . Selected ) e . AddToGroup ( groupmask ) ;
foreach ( Linedef e in linedefs ) if ( e . Selected ) e . AddToGroup ( groupmask ) ;
foreach ( Sector e in sectors ) if ( e . Selected ) e . AddToGroup ( groupmask ) ;
foreach ( Thing e in things ) if ( e . Selected ) e . AddToGroup ( groupmask ) ;
}
/// <summary>This clears specified selection group.</summary>
//mxd
public void ClearGroup ( int groupmask )
{
foreach ( Vertex e in vertices ) e . RemoveFromGroup ( groupmask ) ;
foreach ( Linedef e in linedefs ) e . RemoveFromGroup ( groupmask ) ;
foreach ( Sector e in sectors ) e . RemoveFromGroup ( groupmask ) ;
foreach ( Thing e in things ) e . RemoveFromGroup ( groupmask ) ;
}
//mxd
internal GroupInfo GetGroupInfo ( int groupindex )
{
2013-04-01 11:06:01 +00:00
int numSectors = 0 ;
int numLines = 0 ;
int numVerts = 0 ;
int numThings = 0 ;
int groupmask = 0x01 < < groupindex ;
2014-07-16 09:45:04 +00:00
foreach ( Vertex e in vertices ) if ( e . IsInGroup ( groupmask ) ) numVerts + + ; //mxd
foreach ( Linedef e in linedefs ) if ( e . IsInGroup ( groupmask ) ) numLines + + ; //mxd
foreach ( Sector e in sectors ) if ( e . IsInGroup ( groupmask ) ) numSectors + + ; //mxd
foreach ( Thing e in things ) if ( e . IsInGroup ( groupmask ) ) numThings + + ; //mxd
2013-04-01 11:06:01 +00:00
2014-07-16 09:45:04 +00:00
return new GroupInfo ( groupindex + 1 , numSectors , numLines , numVerts , numThings ) ;
}
2013-04-01 11:06:01 +00:00
2014-07-16 09:45:04 +00:00
//mxd
internal void WriteSelectionGroups ( Configuration cfg )
{
// Fill structure
IDictionary groups = new ListDictionary ( ) ;
for ( int i = 0 ; i < 10 ; i + + )
{
IDictionary group = new ListDictionary ( ) ;
int groupmask = 0x01 < < i ;
2013-04-01 11:06:01 +00:00
2014-07-16 09:45:04 +00:00
//store verts
2015-12-28 15:01:53 +00:00
List < string > indices = new List < string > ( ) ;
2014-07-16 09:45:04 +00:00
foreach ( Vertex e in vertices ) if ( e . IsInGroup ( groupmask ) ) indices . Add ( e . Index . ToString ( ) ) ;
if ( indices . Count > 0 ) group . Add ( "vertices" , string . Join ( " " , indices . ToArray ( ) ) ) ;
//store linedefs
indices . Clear ( ) ;
foreach ( Linedef e in linedefs ) if ( e . IsInGroup ( groupmask ) ) indices . Add ( e . Index . ToString ( ) ) ;
if ( indices . Count > 0 ) group . Add ( "linedefs" , string . Join ( " " , indices . ToArray ( ) ) ) ;
//store sectors
indices . Clear ( ) ;
foreach ( Sector e in sectors ) if ( e . IsInGroup ( groupmask ) ) indices . Add ( e . Index . ToString ( ) ) ;
if ( indices . Count > 0 ) group . Add ( "sectors" , string . Join ( " " , indices . ToArray ( ) ) ) ;
//store things
indices . Clear ( ) ;
foreach ( Thing e in things ) if ( e . IsInGroup ( groupmask ) ) indices . Add ( e . Index . ToString ( ) ) ;
if ( indices . Count > 0 ) group . Add ( "things" , string . Join ( " " , indices . ToArray ( ) ) ) ;
//add to main collection
if ( group . Count > 0 ) groups . Add ( i , group ) ;
2013-04-01 11:06:01 +00:00
}
2014-07-16 09:45:04 +00:00
// Write to config
if ( groups . Count > 0 ) cfg . WriteSetting ( SELECTION_GROUPS_PATH , groups ) ;
2013-04-01 11:06:01 +00:00
}
//mxd
2014-07-16 09:45:04 +00:00
internal void ReadSelectionGroups ( Configuration cfg )
{
IDictionary grouplist = cfg . ReadSetting ( SELECTION_GROUPS_PATH , new Hashtable ( ) ) ;
2014-12-03 23:15:26 +00:00
foreach ( DictionaryEntry mp in grouplist )
{
2014-07-16 09:45:04 +00:00
// Item is a structure?
2014-12-03 23:15:26 +00:00
if ( mp . Value is IDictionary )
{
2014-07-16 09:45:04 +00:00
//get group number
int groupnum ;
if ( ! int . TryParse ( mp . Key as string , out groupnum ) ) continue ;
2014-07-17 09:01:56 +00:00
int groupmask = 0x01 < < General . Clamp ( groupnum , 0 , 10 ) ;
2015-12-28 15:01:53 +00:00
IDictionary groupinfo = ( IDictionary ) mp . Value ;
2014-07-16 09:45:04 +00:00
if ( groupinfo . Contains ( "vertices" ) )
{
string s = groupinfo [ "vertices" ] as string ;
2015-12-28 15:01:53 +00:00
if ( ! string . IsNullOrEmpty ( s ) )
2014-07-16 09:45:04 +00:00
{
2016-03-18 12:52:12 +00:00
List < int > indices = GetIndices ( s ) ;
2015-12-28 15:01:53 +00:00
foreach ( int index in indices )
2014-07-16 09:45:04 +00:00
{
if ( index > vertices . Length ) continue ;
vertices [ index ] . AddToGroup ( groupmask ) ;
}
}
}
if ( groupinfo . Contains ( "linedefs" ) )
{
string s = groupinfo [ "linedefs" ] as string ;
if ( ! string . IsNullOrEmpty ( s ) )
{
2016-03-18 12:52:12 +00:00
List < int > indices = GetIndices ( s ) ;
2014-07-16 09:45:04 +00:00
foreach ( int index in indices )
{
if ( index > linedefs . Length ) continue ;
linedefs [ index ] . AddToGroup ( groupmask ) ;
}
}
}
2013-04-01 11:06:01 +00:00
2014-07-16 09:45:04 +00:00
if ( groupinfo . Contains ( "sectors" ) )
{
string s = groupinfo [ "sectors" ] as string ;
if ( ! string . IsNullOrEmpty ( s ) )
{
2016-03-18 12:52:12 +00:00
List < int > indices = GetIndices ( s ) ;
2014-07-16 09:45:04 +00:00
foreach ( int index in indices )
{
if ( index > sectors . Length ) continue ;
sectors [ index ] . AddToGroup ( groupmask ) ;
}
}
}
2013-04-01 11:06:01 +00:00
2014-07-16 09:45:04 +00:00
if ( groupinfo . Contains ( "things" ) )
{
string s = groupinfo [ "things" ] as string ;
if ( ! string . IsNullOrEmpty ( s ) )
{
2016-03-18 12:52:12 +00:00
List < int > indices = GetIndices ( s ) ;
2014-07-16 09:45:04 +00:00
foreach ( int index in indices )
{
if ( index > things . Length ) continue ;
things [ index ] . AddToGroup ( groupmask ) ;
}
}
}
}
}
2013-04-01 11:06:01 +00:00
}
//mxd
2014-12-03 23:15:26 +00:00
private static List < int > GetIndices ( string input )
2014-07-16 09:45:04 +00:00
{
string [ ] parts = input . Split ( new [ ] { ' ' } , StringSplitOptions . RemoveEmptyEntries ) ;
int index ;
List < int > result = new List < int > ( parts . Length ) ;
2015-12-28 15:01:53 +00:00
foreach ( string part in parts ) if ( int . TryParse ( part , out index ) ) result . Add ( index ) ;
2014-07-16 09:45:04 +00:00
return result ;
2009-04-19 18:07:22 +00:00
}
#endregion
#region = = = = = = = = = = = = = = = = = = Marking
2009-05-19 09:59:19 +00:00
/// <summary>This clears all marks on all elements.</summary>
2009-04-19 18:07:22 +00:00
public void ClearAllMarks ( bool mark )
{
ClearMarkedVertices ( mark ) ;
ClearMarkedThings ( mark ) ;
ClearMarkedLinedefs ( mark ) ;
ClearMarkedSectors ( mark ) ;
ClearMarkedSidedefs ( mark ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears all marks on all vertices.</summary>
2009-04-19 18:07:22 +00:00
public void ClearMarkedVertices ( bool mark )
{
foreach ( Vertex v in vertices ) v . Marked = mark ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears all marks on all things.</summary>
2009-04-19 18:07:22 +00:00
public void ClearMarkedThings ( bool mark )
{
foreach ( Thing t in things ) t . Marked = mark ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears all marks on all linedefs.</summary>
2009-04-19 18:07:22 +00:00
public void ClearMarkedLinedefs ( bool mark )
{
foreach ( Linedef l in linedefs ) l . Marked = mark ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears all marks on all sidedefs.</summary>
2009-04-19 18:07:22 +00:00
public void ClearMarkedSidedefs ( bool mark )
{
foreach ( Sidedef s in sidedefs ) s . Marked = mark ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This clears all marks on all sectors.</summary>
2009-04-19 18:07:22 +00:00
public void ClearMarkedSectors ( bool mark )
{
foreach ( Sector s in sectors ) s . Marked = mark ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This inverts all marks on all elements.</summary>
2009-04-19 18:07:22 +00:00
public void InvertAllMarks ( )
{
InvertMarkedVertices ( ) ;
InvertMarkedThings ( ) ;
InvertMarkedLinedefs ( ) ;
InvertMarkedSectors ( ) ;
InvertMarkedSidedefs ( ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This inverts all marks on all vertices.</summary>
2009-04-19 18:07:22 +00:00
public void InvertMarkedVertices ( )
{
foreach ( Vertex v in vertices ) v . Marked = ! v . Marked ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This inverts all marks on all things.</summary>
2009-04-19 18:07:22 +00:00
public void InvertMarkedThings ( )
{
foreach ( Thing t in things ) t . Marked = ! t . Marked ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This inverts all marks on all linedefs.</summary>
2009-04-19 18:07:22 +00:00
public void InvertMarkedLinedefs ( )
{
foreach ( Linedef l in linedefs ) l . Marked = ! l . Marked ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This inverts all marks on all sidedefs.</summary>
2009-04-19 18:07:22 +00:00
public void InvertMarkedSidedefs ( )
{
foreach ( Sidedef s in sidedefs ) s . Marked = ! s . Marked ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This inverts all marks on all sectors.</summary>
2009-04-19 18:07:22 +00:00
public void InvertMarkedSectors ( )
{
foreach ( Sector s in sectors ) s . Marked = ! s . Marked ;
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of vertices that match a marked state.</summary>
2009-04-19 18:07:22 +00:00
public List < Vertex > GetMarkedVertices ( bool mark )
{
2009-06-05 19:03:56 +00:00
List < Vertex > list = new List < Vertex > ( numvertices > > 1 ) ;
2009-04-19 18:07:22 +00:00
foreach ( Vertex v in vertices ) if ( v . Marked = = mark ) list . Add ( v ) ;
return list ;
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of things that match a marked state.</summary>
2009-04-19 18:07:22 +00:00
public List < Thing > GetMarkedThings ( bool mark )
{
2009-06-05 19:03:56 +00:00
List < Thing > list = new List < Thing > ( numthings > > 1 ) ;
2009-04-19 18:07:22 +00:00
foreach ( Thing t in things ) if ( t . Marked = = mark ) list . Add ( t ) ;
return list ;
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of linedefs that match a marked state.</summary>
2009-04-19 18:07:22 +00:00
public List < Linedef > GetMarkedLinedefs ( bool mark )
{
2009-06-05 19:03:56 +00:00
List < Linedef > list = new List < Linedef > ( numlinedefs > > 1 ) ;
2009-04-19 18:07:22 +00:00
foreach ( Linedef l in linedefs ) if ( l . Marked = = mark ) list . Add ( l ) ;
return list ;
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of sidedefs that match a marked state.</summary>
2009-04-19 18:07:22 +00:00
public List < Sidedef > GetMarkedSidedefs ( bool mark )
{
2009-06-05 19:03:56 +00:00
List < Sidedef > list = new List < Sidedef > ( numsidedefs > > 1 ) ;
2009-04-19 18:07:22 +00:00
foreach ( Sidedef s in sidedefs ) if ( s . Marked = = mark ) list . Add ( s ) ;
return list ;
}
2009-05-19 09:59:19 +00:00
/// <summary>Returns a collection of sectors that match a marked state.</summary>
2009-04-19 18:07:22 +00:00
public List < Sector > GetMarkedSectors ( bool mark )
{
2009-06-05 19:03:56 +00:00
List < Sector > list = new List < Sector > ( numsectors > > 1 ) ;
2009-04-19 18:07:22 +00:00
foreach ( Sector s in sectors ) if ( s . Marked = = mark ) list . Add ( s ) ;
return list ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This marks vertices based on selected vertices.</summary>
2009-04-19 18:07:22 +00:00
public void MarkSelectedVertices ( bool selected , bool mark )
{
2009-05-13 09:35:13 +00:00
foreach ( Vertex v in sel_vertices ) v . Marked = mark ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This marks linedefs based on selected linedefs.</summary>
2009-04-19 18:07:22 +00:00
public void MarkSelectedLinedefs ( bool selected , bool mark )
{
2009-05-13 09:35:13 +00:00
foreach ( Linedef l in sel_linedefs ) l . Marked = mark ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This marks sectors based on selected sectors.</summary>
2009-04-19 18:07:22 +00:00
public void MarkSelectedSectors ( bool selected , bool mark )
{
2009-05-13 09:35:13 +00:00
foreach ( Sector s in sel_sectors ) s . Marked = mark ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This marks things based on selected things.</summary>
2009-04-19 18:07:22 +00:00
public void MarkSelectedThings ( bool selected , bool mark )
{
2009-05-13 09:35:13 +00:00
foreach ( Thing t in sel_things ) t . Marked = mark ;
2009-04-19 18:07:22 +00:00
}
/// <summary>
2009-05-19 09:59:19 +00:00
/// This marks the front and back sidedefs on linedefs with the matching mark.
2009-04-19 18:07:22 +00:00
/// </summary>
public void MarkSidedefsFromLinedefs ( bool matchmark , bool setmark )
{
foreach ( Linedef l in linedefs )
{
if ( l . Marked = = matchmark )
{
if ( l . Front ! = null ) l . Front . Marked = setmark ;
if ( l . Back ! = null ) l . Back . Marked = setmark ;
}
}
}
/// <summary>
2009-05-19 09:59:19 +00:00
/// This marks the sidedefs that make up the sectors with the matching mark.
2009-04-19 18:07:22 +00:00
/// </summary>
public void MarkSidedefsFromSectors ( bool matchmark , bool setmark )
{
foreach ( Sidedef sd in sidedefs )
{
if ( sd . Sector . Marked = = matchmark ) sd . Marked = setmark ;
}
}
/// <summary>
2009-05-19 09:59:19 +00:00
/// Returns a collection of vertices that match a marked state on the linedefs.
2009-04-19 18:07:22 +00:00
/// </summary>
public ICollection < Vertex > GetVerticesFromLinesMarks ( bool mark )
{
2009-06-05 19:03:56 +00:00
List < Vertex > list = new List < Vertex > ( numvertices > > 1 ) ;
2009-04-19 18:07:22 +00:00
foreach ( Vertex v in vertices )
{
foreach ( Linedef l in v . Linedefs )
{
if ( l . Marked = = mark )
{
list . Add ( v ) ;
break ;
}
}
}
return list ;
}
/// <summary>
2009-05-19 09:59:19 +00:00
/// Returns a collection of vertices that match a marked state on the linedefs.
2009-04-19 18:07:22 +00:00
/// The difference with GetVerticesFromLinesMarks is that in this method
/// ALL linedefs of a vertex must match the specified marked state.
/// </summary>
public ICollection < Vertex > GetVerticesFromAllLinesMarks ( bool mark )
{
2009-06-05 19:03:56 +00:00
List < Vertex > list = new List < Vertex > ( numvertices > > 1 ) ;
2009-04-19 18:07:22 +00:00
foreach ( Vertex v in vertices )
{
bool qualified = true ;
foreach ( Linedef l in v . Linedefs )
{
if ( l . Marked ! = mark )
{
qualified = false ;
break ;
}
}
if ( qualified ) list . Add ( v ) ;
}
return list ;
}
/// <summary>
2009-05-19 09:59:19 +00:00
/// Returns a collection of vertices that match a marked state on the linedefs.
2009-04-19 18:07:22 +00:00
/// </summary>
public ICollection < Vertex > GetVerticesFromSectorsMarks ( bool mark )
{
2009-06-05 19:03:56 +00:00
List < Vertex > list = new List < Vertex > ( numvertices > > 1 ) ;
2009-04-19 18:07:22 +00:00
foreach ( Vertex v in vertices )
{
foreach ( Linedef l in v . Linedefs )
{
2013-09-03 09:34:28 +00:00
if ( ( ( l . Front ! = null ) & & ( l . Front . Sector ! = null ) & & ( l . Front . Sector . Marked = = mark ) ) | |
( ( l . Back ! = null ) & & ( l . Back . Sector ! = null ) & & ( l . Back . Sector . Marked = = mark ) ) )
2009-04-19 18:07:22 +00:00
{
list . Add ( v ) ;
break ;
}
}
}
return list ;
}
/// <summary>
/// This marks all selected geometry, including sidedefs from sectors.
/// When sidedefsfromsectors is true, then the sidedefs are marked according to the
/// marked sectors. Otherwise the sidedefs are marked according to the marked linedefs.
/// </summary>
public void MarkAllSelectedGeometry ( bool mark , bool linedefsfromvertices , bool verticesfromlinedefs , bool sectorsfromlinedefs , bool sidedefsfromsectors )
{
General . Map . Map . ClearAllMarks ( ! mark ) ;
// Direct vertices
General . Map . Map . MarkSelectedVertices ( true , mark ) ;
// Direct linedefs
General . Map . Map . MarkSelectedLinedefs ( true , mark ) ;
// Linedefs from vertices
// We do this before "vertices from lines" because otherwise we get lines marked that we didn't select
if ( linedefsfromvertices )
{
ICollection < Linedef > lines = General . Map . Map . LinedefsFromMarkedVertices ( ! mark , mark , ! mark ) ;
foreach ( Linedef l in lines ) l . Marked = mark ;
}
// Vertices from linedefs
if ( verticesfromlinedefs )
{
ICollection < Vertex > verts = General . Map . Map . GetVerticesFromLinesMarks ( mark ) ;
foreach ( Vertex v in verts ) v . Marked = mark ;
}
// Mark sectors from linedefs (note: this must be the first to mark
// sectors, because this clears the sector marks!)
if ( sectorsfromlinedefs )
{
General . Map . Map . ClearMarkedSectors ( mark ) ;
foreach ( Linedef l in General . Map . Map . Linedefs )
{
if ( ! l . Selected )
{
if ( l . Front ! = null ) l . Front . Sector . Marked = ! mark ;
if ( l . Back ! = null ) l . Back . Sector . Marked = ! mark ;
}
}
}
// Direct sectors
General . Map . Map . MarkSelectedSectors ( true , mark ) ;
// Direct things
General . Map . Map . MarkSelectedThings ( true , mark ) ;
// Sidedefs from linedefs or sectors
if ( sidedefsfromsectors )
General . Map . Map . MarkSidedefsFromSectors ( true , mark ) ;
else
General . Map . Map . MarkSidedefsFromLinedefs ( true , mark ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Indexing
/// <summary>
2009-06-05 19:03:56 +00:00
/// Returns the vertex at the specified index. Returns null when index is out of range. This is an O(1) operation.
2009-04-19 18:07:22 +00:00
/// </summary>
public Vertex GetVertexByIndex ( int index )
{
2009-06-05 19:03:56 +00:00
return index < numvertices ? vertices [ index ] : null ;
2009-04-19 18:07:22 +00:00
}
/// <summary>
2009-06-05 19:03:56 +00:00
/// Returns the linedef at the specified index. Returns null when index is out of range. This is an O(1) operation.
2009-04-19 18:07:22 +00:00
/// </summary>
public Linedef GetLinedefByIndex ( int index )
{
2009-06-05 19:03:56 +00:00
return index < numlinedefs ? linedefs [ index ] : null ;
2009-04-19 18:07:22 +00:00
}
/// <summary>
2009-06-05 19:03:56 +00:00
/// Returns the sidedef at the specified index. Returns null when index is out of range. This is an O(1) operation.
2009-04-19 18:07:22 +00:00
/// </summary>
public Sidedef GetSidedefByIndex ( int index )
{
2009-06-05 19:03:56 +00:00
return index < numsidedefs ? sidedefs [ index ] : null ;
2009-04-19 18:07:22 +00:00
}
/// <summary>
2009-06-05 19:03:56 +00:00
/// Returns the sector at the specified index. Returns null when index is out of range. This is an O(1) operation.
2009-04-19 18:07:22 +00:00
/// </summary>
public Sector GetSectorByIndex ( int index )
{
2009-06-05 19:03:56 +00:00
return index < numsectors ? sectors [ index ] : null ;
2009-04-19 18:07:22 +00:00
}
/// <summary>
2009-06-05 19:03:56 +00:00
/// Returns the thing at the specified index. Returns null when index is out of range. This is an O(1) operation.
2009-04-19 18:07:22 +00:00
/// </summary>
public Thing GetThingByIndex ( int index )
{
2009-06-05 19:03:56 +00:00
return index < numthings ? things [ index ] : null ;
2009-04-19 18:07:22 +00:00
}
#endregion
#region = = = = = = = = = = = = = = = = = = Areas
2009-05-19 09:59:19 +00:00
/// <summary>This creates an initial, undefined area.</summary>
2009-04-19 18:07:22 +00:00
public static RectangleF CreateEmptyArea ( )
{
return new RectangleF ( float . MaxValue / 2 , float . MaxValue / 2 , - float . MaxValue , - float . MaxValue ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This creates an area from vertices.</summary>
2009-04-19 18:07:22 +00:00
public static RectangleF CreateArea ( ICollection < Vertex > verts )
{
2020-10-04 08:21:13 +00:00
// Do NOT use double here, otherwise the width and height of the RectangleF will become NaN
float l = float . MaxValue ;
float t = float . MaxValue ;
float r = float . MinValue ;
float b = float . MinValue ;
2009-04-19 18:07:22 +00:00
// Go for all vertices
foreach ( Vertex v in verts )
{
// Adjust boundaries by vertices
2020-10-04 08:21:13 +00:00
if ( v . Position . x < l ) l = ( float ) v . Position . x ;
if ( v . Position . x > r ) r = ( float ) v . Position . x ;
if ( v . Position . y < t ) t = ( float ) v . Position . y ;
if ( v . Position . y > b ) b = ( float ) v . Position . y ;
2009-04-19 18:07:22 +00:00
}
// Return a rect
2020-10-04 08:21:13 +00:00
return new RectangleF ( l , t , r - l , b - t ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This increases and existing area with the given vertices.</summary>
2009-04-19 18:07:22 +00:00
public static RectangleF IncreaseArea ( RectangleF area , ICollection < Vertex > verts )
{
2020-10-04 08:21:13 +00:00
// Do NOT use double here, otherwise the width and height of the RectangleF will become NaN
float l = area . Left ;
float t = area . Top ;
float r = area . Right ;
float b = area . Bottom ;
2009-04-19 18:07:22 +00:00
// Go for all vertices
foreach ( Vertex v in verts )
{
// Adjust boundaries by vertices
2020-10-04 08:21:13 +00:00
if ( v . Position . x < l ) l = ( float ) v . Position . x ;
if ( v . Position . x > r ) r = ( float ) v . Position . x ;
if ( v . Position . y < t ) t = ( float ) v . Position . y ;
if ( v . Position . y > b ) b = ( float ) v . Position . y ;
2009-04-19 18:07:22 +00:00
}
// Return a rect
2020-10-04 08:21:13 +00:00
return new RectangleF ( l , t , r - l , b - t ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This increases and existing area with the given things.</summary>
2009-04-19 18:07:22 +00:00
public static RectangleF IncreaseArea ( RectangleF area , ICollection < Thing > things )
{
2020-10-04 08:21:13 +00:00
// Do NOT use double here, otherwise the width and height of the RectangleF will become NaN
float l = area . Left ;
float t = area . Top ;
float r = area . Right ;
float b = area . Bottom ;
2009-04-19 18:07:22 +00:00
// Go for all vertices
foreach ( Thing th in things )
{
// Adjust boundaries by vertices
2020-10-04 08:21:13 +00:00
if ( th . Position . x < l ) l = ( float ) th . Position . x ;
if ( th . Position . x > r ) r = ( float ) th . Position . x ;
if ( th . Position . y < t ) t = ( float ) th . Position . y ;
if ( th . Position . y > b ) b = ( float ) th . Position . y ;
2009-04-19 18:07:22 +00:00
}
// Return a rect
2020-10-04 08:21:13 +00:00
return new RectangleF ( l , t , r - l , b - t ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This increases and existing area with the given vertices.</summary>
2009-04-19 18:07:22 +00:00
public static RectangleF IncreaseArea ( RectangleF area , ICollection < Vector2D > verts )
{
2020-10-04 08:21:13 +00:00
// Do NOT use double here, otherwise the width and height of the RectangleF will become NaN
float l = area . Left ;
float t = area . Top ;
float r = area . Right ;
float b = area . Bottom ;
2009-04-19 18:07:22 +00:00
// Go for all vertices
foreach ( Vector2D v in verts )
{
// Adjust boundaries by vertices
2020-10-04 08:21:13 +00:00
if ( v . x < l ) l = ( float ) v . x ;
if ( v . x > r ) r = ( float ) v . x ;
if ( v . y < t ) t = ( float ) v . y ;
if ( v . y > b ) b = ( float ) v . y ;
2009-04-19 18:07:22 +00:00
}
// Return a rect
2020-10-04 08:21:13 +00:00
return new RectangleF ( l , t , r - l , b - t ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This increases and existing area with the given vertex.</summary>
2009-04-19 18:07:22 +00:00
public static RectangleF IncreaseArea ( RectangleF area , Vector2D vert )
{
2020-10-04 08:21:13 +00:00
// Do NOT use double here, otherwise the width and height of the RectangleF will become NaN
float l = area . Left ;
float t = area . Top ;
float r = area . Right ;
float b = area . Bottom ;
2009-04-19 18:07:22 +00:00
// Adjust boundaries by vertices
2020-10-04 08:21:13 +00:00
if ( vert . x < l ) l = ( float ) vert . x ;
if ( vert . x > r ) r = ( float ) vert . x ;
if ( vert . y < t ) t = ( float ) vert . y ;
if ( vert . y > b ) b = ( float ) vert . y ;
2020-05-18 16:14:54 +00:00
2009-04-19 18:07:22 +00:00
// Return a rect
2020-10-04 08:21:13 +00:00
return new RectangleF ( l , t , r - l , b - t ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This creates an area from linedefs.</summary>
2009-04-19 18:07:22 +00:00
public static RectangleF CreateArea ( ICollection < Linedef > lines )
{
2020-10-04 08:21:13 +00:00
// Do NOT use double here, otherwise the width and height of the RectangleF will become NaN
2020-05-21 12:20:02 +00:00
float l = float . MaxValue ;
float t = float . MaxValue ;
float r = float . MinValue ;
float b = float . MinValue ;
2009-04-19 18:07:22 +00:00
// Go for all linedefs
foreach ( Linedef ld in lines )
{
// Adjust boundaries by vertices
2020-05-21 12:20:02 +00:00
if ( ld . Start . Position . x < l ) l = ( float ) ld . Start . Position . x ;
if ( ld . Start . Position . x > r ) r = ( float ) ld . Start . Position . x ;
if ( ld . Start . Position . y < t ) t = ( float ) ld . Start . Position . y ;
if ( ld . Start . Position . y > b ) b = ( float ) ld . Start . Position . y ;
if ( ld . End . Position . x < l ) l = ( float ) ld . End . Position . x ;
if ( ld . End . Position . x > r ) r = ( float ) ld . End . Position . x ;
if ( ld . End . Position . y < t ) t = ( float ) ld . End . Position . y ;
if ( ld . End . Position . y > b ) b = ( float ) ld . End . Position . y ;
2009-04-19 18:07:22 +00:00
}
// Return a rect
2020-10-04 08:21:13 +00:00
return new RectangleF ( l , t , r - l , b - t ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
2016-10-31 18:52:29 +00:00
/// <summary>This increases and existing area with the given linedefs.</summary>
public static RectangleF IncreaseArea ( RectangleF area , ICollection < Linedef > lines ) //mxd
{
2020-10-04 08:21:13 +00:00
// Do NOT use double here, otherwise the width and height of the RectangleF will become NaN
float l = area . Left ;
float t = area . Top ;
float r = area . Right ;
float b = area . Bottom ;
2016-10-31 18:52:29 +00:00
// Go for all vertices
foreach ( Linedef ld in lines )
{
// Adjust boundaries by vertices
2020-10-04 08:21:13 +00:00
if ( ld . Start . Position . x < l ) l = ( float ) ld . Start . Position . x ;
if ( ld . Start . Position . x > r ) r = ( float ) ld . Start . Position . x ;
if ( ld . Start . Position . y < t ) t = ( float ) ld . Start . Position . y ;
if ( ld . Start . Position . y > b ) b = ( float ) ld . Start . Position . y ;
if ( ld . End . Position . x < l ) l = ( float ) ld . End . Position . x ;
if ( ld . End . Position . x > r ) r = ( float ) ld . End . Position . x ;
if ( ld . End . Position . y < t ) t = ( float ) ld . End . Position . y ;
if ( ld . End . Position . y > b ) b = ( float ) ld . End . Position . y ;
2016-10-31 18:52:29 +00:00
}
// Return a rect
2020-10-04 08:21:13 +00:00
return new RectangleF ( l , t , r - l , b - t ) ;
2016-10-31 18:52:29 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This filters lines by a rectangular area.</summary>
2016-06-02 09:55:01 +00:00
public static HashSet < Linedef > FilterByArea ( ICollection < Linedef > lines , ref RectangleF area )
2009-04-19 18:07:22 +00:00
{
2016-06-02 09:55:01 +00:00
HashSet < Linedef > newlines = new HashSet < Linedef > ( ) ;
2009-04-19 18:07:22 +00:00
// Go for all lines
foreach ( Linedef l in lines )
{
2013-12-11 09:47:35 +00:00
// Check the cs field bits
2015-12-28 15:01:53 +00:00
if ( ( GetCSFieldBits ( l . Start . Position , area ) & GetCSFieldBits ( l . End . Position , area ) ) = = 0 )
2013-12-11 09:47:35 +00:00
{
// The line could be in the area
newlines . Add ( l ) ;
}
2009-04-19 18:07:22 +00:00
}
// Return result
return newlines ;
}
2013-12-17 13:18:44 +00:00
/// <summary> This returns the cohen-sutherland field bits for a vector in a rectangle area</summary>
public static int GetCSFieldBits ( Vector2D v , RectangleF area )
2013-12-11 09:47:35 +00:00
{
int bits = 0 ;
2013-12-17 13:18:44 +00:00
if ( v . y < area . Top ) bits | = 0x01 ;
if ( v . y > area . Bottom ) bits | = 0x02 ;
if ( v . x < area . Left ) bits | = 0x04 ;
if ( v . x > area . Right ) bits | = 0x08 ;
2013-12-11 09:47:35 +00:00
return bits ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This filters vertices by a rectangular area.</summary>
2009-04-19 18:07:22 +00:00
public static ICollection < Vertex > FilterByArea ( ICollection < Vertex > verts , ref RectangleF area )
{
ICollection < Vertex > newverts = new List < Vertex > ( verts . Count ) ;
// Go for all verts
foreach ( Vertex v in verts )
{
// Within rect?
2013-12-11 09:47:35 +00:00
if ( ( v . Position . x < area . Left ) | | ( v . Position . x > area . Right ) | |
( v . Position . y < area . Top ) | | ( v . Position . y > area . Bottom ) ) continue ;
// The vertex is in the area
newverts . Add ( v ) ;
2009-04-19 18:07:22 +00:00
}
// Return result
return newverts ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Stitching
/// <summary>
2010-08-15 19:43:00 +00:00
/// Stitches marked geometry with non-marked geometry. Returns false when the operation failed.
2009-04-19 18:07:22 +00:00
/// </summary>
2016-06-02 09:55:01 +00:00
public bool StitchGeometry ( ) { return StitchGeometry ( MergeGeometryMode . CLASSIC ) ; } //mxd. Compatibility
public bool StitchGeometry ( MergeGeometryMode mergemode )
2009-04-19 18:07:22 +00:00
{
// Find vertices
2016-06-02 09:55:01 +00:00
HashSet < Vertex > movingverts = new HashSet < Vertex > ( General . Map . Map . GetMarkedVertices ( true ) ) ;
HashSet < Vertex > fixedverts = new HashSet < Vertex > ( General . Map . Map . GetMarkedVertices ( false ) ) ;
2009-04-19 18:07:22 +00:00
// Find lines that moved during the drag
2016-06-02 09:55:01 +00:00
HashSet < Linedef > movinglines = new HashSet < Linedef > ( LinedefsFromMarkedVertices ( false , true , true ) ) ;
2009-04-19 18:07:22 +00:00
// Find all non-moving lines
2016-06-02 09:55:01 +00:00
HashSet < Linedef > fixedlines = new HashSet < Linedef > ( LinedefsFromMarkedVertices ( true , false , false ) ) ;
2009-04-19 18:07:22 +00:00
// Determine area in which we are editing
2015-12-27 21:54:50 +00:00
RectangleF editarea = CreateArea ( movinglines ) ;
editarea = IncreaseArea ( editarea , movingverts ) ;
2009-04-19 18:07:22 +00:00
editarea . Inflate ( 1.0f , 1.0f ) ;
// Join nearby vertices
2009-06-05 19:03:56 +00:00
BeginAddRemove ( ) ;
2015-12-27 21:54:50 +00:00
JoinVertices ( fixedverts , movingverts , true , STITCH_DISTANCE ) ;
2009-06-05 19:03:56 +00:00
EndAddRemove ( ) ;
2009-04-19 18:07:22 +00:00
// Update cached values of lines because we need their length/angle
Update ( true , false ) ;
2009-06-05 19:03:56 +00:00
BeginAddRemove ( ) ;
2009-04-19 18:07:22 +00:00
// Split moving lines with unselected vertices
2015-12-27 21:54:50 +00:00
ICollection < Vertex > nearbyfixedverts = FilterByArea ( fixedverts , ref editarea ) ;
2021-07-16 15:56:55 +00:00
if ( ! SplitLinesByVertices ( movinglines , nearbyfixedverts , STITCH_DISTANCE , movinglines , mergemode ) )
{
EndAddRemove ( ) ; // Unfreeze arrays before returning
2010-08-15 19:43:00 +00:00
return false ;
2021-07-16 15:56:55 +00:00
}
2017-02-20 07:13:35 +00:00
// Split non-moving lines with selected vertices
fixedlines = new HashSet < Linedef > ( fixedlines . Where ( fixedline = > ! fixedline . IsDisposed ) ) ;
2021-07-16 15:56:55 +00:00
if ( ! SplitLinesByVertices ( fixedlines , movingverts , STITCH_DISTANCE , movinglines , mergemode ) )
{
EndAddRemove ( ) ; // Unfreeze arrays before returning
2010-08-15 19:43:00 +00:00
return false ;
2021-07-16 15:56:55 +00:00
}
2016-05-29 00:38:55 +00:00
//mxd. Split moving lines with fixed lines
2021-07-16 15:56:55 +00:00
if ( ! SplitLinesByLines ( fixedlines , movinglines , mergemode ) )
{
EndAddRemove ( ) ; // Unfreeze arrays before returning
return false ;
}
2009-04-19 18:07:22 +00:00
// Remove looped linedefs
2015-12-27 21:54:50 +00:00
RemoveLoopedLinedefs ( movinglines ) ;
2021-07-16 15:56:55 +00:00
2009-04-19 18:07:22 +00:00
// Join overlapping lines
2021-07-16 15:56:55 +00:00
if ( ! JoinOverlappingLines ( movinglines ) )
{
EndAddRemove ( ) ; // Unfreeze arrays before returning
return false ;
}
2016-05-29 00:38:55 +00:00
2016-06-02 09:55:01 +00:00
//mxd. Remove remaining new verts from dragged shape if possible
if ( mergemode = = MergeGeometryMode . REPLACE )
{
2016-12-16 21:23:37 +00:00
// Collect verts created by splitting. Can't use GetMarkedVertices here, because we are in the middle of AddRemove
2016-06-02 09:55:01 +00:00
HashSet < Vertex > tocheck = new HashSet < Vertex > ( ) ;
2016-12-16 21:23:37 +00:00
foreach ( Vertex v in vertices )
2016-06-02 09:55:01 +00:00
{
2016-12-16 21:23:37 +00:00
if ( v ! = null & & v . Marked & & ! movingverts . Contains ( v ) ) tocheck . Add ( v ) ;
2016-06-02 09:55:01 +00:00
}
// Remove verts, which are not part of initially dragged verts
foreach ( Vertex v in tocheck )
{
if ( ! v . IsDisposed & & v . Linedefs . Count = = 2 )
{
Linedef ld1 = General . GetByIndex ( v . Linedefs , 0 ) ;
Linedef ld2 = General . GetByIndex ( v . Linedefs , 1 ) ;
Vertex v2 = ( ld2 . Start = = v ) ? ld2 . End : ld2 . Start ;
if ( ld1 . Start = = v ) ld1 . SetStartVertex ( v2 ) ; else ld1 . SetEndVertex ( v2 ) ;
ld2 . Dispose ( ) ;
// Trash vertex
v . Dispose ( ) ;
}
}
}
2009-06-05 19:03:56 +00:00
EndAddRemove ( ) ;
2016-05-29 00:38:55 +00:00
2016-09-10 20:45:41 +00:00
// Collect changed lines... We need those in by-vertex-index order
// (otherwise SectorBuilder logic in some cases will incorrectly assign sector propertes)
List < Vertex > markedverts = GetMarkedVertices ( true ) ;
List < Linedef > changedlines = new List < Linedef > ( markedverts . Count / 2 ) ;
HashSet < Linedef > changedlineshash = new HashSet < Linedef > ( ) ;
foreach ( Vertex v in markedverts )
2016-05-29 00:38:55 +00:00
{
2016-09-10 20:45:41 +00:00
foreach ( Linedef l in v . Linedefs )
2016-08-05 14:30:10 +00:00
{
2016-09-10 20:45:41 +00:00
if ( ! changedlineshash . Contains ( l ) )
2016-08-05 14:30:10 +00:00
{
2016-09-10 20:45:41 +00:00
changedlines . Add ( l ) ;
changedlineshash . Add ( l ) ;
2016-08-05 14:30:10 +00:00
}
}
2016-09-10 20:45:41 +00:00
}
2016-08-05 14:30:10 +00:00
2016-09-10 20:45:41 +00:00
//mxd. Correct sector references
if ( mergemode ! = MergeGeometryMode . CLASSIC )
{
// Linedefs cache needs to be up to date...
Update ( true , false ) ;
2016-05-29 00:38:55 +00:00
// Fix stuff...
CorrectSectorReferences ( changedlines , true ) ;
CorrectOuterSides ( new HashSet < Linedef > ( changedlines ) ) ;
2016-06-02 09:55:01 +00:00
// Mark only fully selected sectors
ClearMarkedSectors ( false ) ;
HashSet < Sector > changedsectors = GetSectorsFromLinedefs ( changedlines ) ;
foreach ( Sector s in changedsectors ) s . Marked = true ;
2016-05-29 00:38:55 +00:00
}
2016-09-10 20:45:41 +00:00
else
{
FlipBackwardLinedefs ( changedlines ) ;
}
2009-06-05 19:03:56 +00:00
2010-08-15 19:43:00 +00:00
return true ;
2009-04-19 18:07:22 +00:00
}
2016-05-29 00:38:55 +00:00
//mxd. Shameless SLADEMap::correctSectors ripoff... Corrects/builds sectors for all lines in [lines]
private static void CorrectSectorReferences ( List < Linedef > lines , bool existing_only )
{
2016-06-02 09:55:01 +00:00
//DebugConsole.Clear();
//DebugConsole.WriteLine("CorrectSectorReferences for " + lines.Count + " lines");
2017-02-08 12:18:01 +00:00
// ano - set a bunch of foreaches to be for()s because they're faster
2016-05-29 00:38:55 +00:00
// Create a list of sidedefs to perform sector creation with
List < LinedefSide > edges = new List < LinedefSide > ( ) ;
if ( existing_only )
{
2017-02-08 12:18:01 +00:00
int lineCount = lines . Count ;
for ( int i = 0 ; i < lineCount ; i + + )
2016-05-29 00:38:55 +00:00
{
2017-02-08 12:18:01 +00:00
Linedef l = lines [ i ] ;
2016-05-29 00:38:55 +00:00
// Add only existing sides as edges (or front side if line has none)
if ( l . Front ! = null | | l . Back = = null )
edges . Add ( new LinedefSide ( l , true ) ) ;
if ( l . Back ! = null )
edges . Add ( new LinedefSide ( l , false ) ) ;
}
}
else
{
2017-02-08 12:18:01 +00:00
int lineCount = lines . Count ;
for ( int i = 0 ; i < lineCount ; i + + )
{
Linedef l = lines [ i ] ;
// Add front side
edges . Add ( new LinedefSide ( l , true ) ) ;
2016-05-29 00:38:55 +00:00
// Add back side if there's a sector
if ( General . Map . Map . GetSectorByCoordinates ( l . GetSidePoint ( false ) ) ! = null )
edges . Add ( new LinedefSide ( l , false ) ) ;
}
}
HashSet < Sidedef > sides_correct = new HashSet < Sidedef > ( ) ;
2017-02-08 12:18:01 +00:00
int edgeCount = edges . Count ;
for ( int i = 0 ; i < edgeCount ; i + + )
{
LinedefSide ls = edges [ i ] ;
if ( ls . Front & & ls . Line . Front ! = null )
2016-05-29 00:38:55 +00:00
sides_correct . Add ( ls . Line . Front ) ;
else if ( ! ls . Front & & ls . Line . Back ! = null )
sides_correct . Add ( ls . Line . Back ) ;
}
//mxd. Get affected sectors
HashSet < Sector > affectedsectors = new HashSet < Sector > ( General . Map . Map . GetSelectedSectors ( true ) ) ;
affectedsectors . UnionWith ( General . Map . Map . GetUnselectedSectorsFromLinedefs ( lines ) ) ;
2016-06-02 09:55:01 +00:00
//mxd. Collect their sidedefs
HashSet < Sidedef > sectorsides = new HashSet < Sidedef > ( ) ;
foreach ( Sector s in affectedsectors ) sectorsides . UnionWith ( s . Sidedefs ) ;
2016-05-29 00:38:55 +00:00
// Build sectors
SectorBuilder builder = new SectorBuilder ( ) ;
List < Sector > sectors_reused = new List < Sector > ( ) ;
2017-02-08 12:18:01 +00:00
for ( int i = 0 ; i < edgeCount ; i + + )
{
LinedefSide ls = edges [ i ] ;
// Skip if edge is ignored
//DebugConsole.WriteLine((ls.Ignore ? "Ignoring line " : "Processing line ") + ls.Line.Index);
if ( ls . Ignore ) continue ;
2016-05-29 00:38:55 +00:00
2017-02-20 06:14:07 +00:00
// Run sector builder on current edge
Stopwatch watch = new Stopwatch ( ) ;
watch . Start ( ) ;
if ( ! builder . TraceSector ( ls . Line , ls . Front ) )
{
2017-02-20 06:21:56 +00:00
//General.ErrorLogger.Add(ErrorType.Warning, string.Format("TraceSector: took {0}ms, failed!", watch.ElapsedMilliseconds));
2017-02-20 06:14:07 +00:00
continue ; // Don't create sector if trace failed
}
2017-02-20 06:21:56 +00:00
//General.ErrorLogger.Add(ErrorType.Warning, string.Format("TraceSector: took {0}ms", watch.ElapsedMilliseconds));
2016-05-29 00:38:55 +00:00
2017-02-20 06:14:07 +00:00
// Find any subsequent edges that were part of the sector created
bool has_existing_lines = false ;
2016-05-29 00:38:55 +00:00
bool has_existing_sides = false ;
2016-06-02 09:55:01 +00:00
//bool has_zero_sided_lines = false;
bool has_dragged_sides = false ; //mxd
2016-05-29 00:38:55 +00:00
List < LinedefSide > edges_in_sector = new List < LinedefSide > ( ) ;
foreach ( LinedefSide edge in builder . SectorEdges )
{
bool line_is_ours = false ;
2016-06-02 09:55:01 +00:00
bool side_exists = ( edge . Front ? edge . Line . Front ! = null : edge . Line . Back ! = null ) ; //mxd
if ( side_exists & & sectorsides . Contains ( edge . Front ? edge . Line . Front : edge . Line . Back ) )
has_dragged_sides = true ; //mxd
2016-05-29 00:38:55 +00:00
2017-02-08 12:18:01 +00:00
for ( int k = 0 ; k < edgeCount ; k + + )
{
LinedefSide ls2 = edges [ k ] ;
if ( ls2 . Line = = edge . Line )
2016-05-29 00:38:55 +00:00
{
line_is_ours = true ;
if ( ls2 . Front = = edge . Front )
{
edges_in_sector . Add ( ls2 ) ;
break ;
}
}
}
2017-02-08 12:18:01 +00:00
// ano - so this inner part was already commented out
// so i just put the /* */ around it
/ * if ( line_is_ours )
2016-05-29 00:38:55 +00:00
{
2016-06-02 09:55:01 +00:00
//if(edge.Line.Front == null && edge.Line.Back == null)
//has_zero_sided_lines = true;
2016-05-29 00:38:55 +00:00
}
2017-02-08 12:18:01 +00:00
else * /
if ( ! line_is_ours )
2016-05-29 00:38:55 +00:00
{
has_existing_lines = true ;
2016-06-02 09:55:01 +00:00
has_existing_sides | = side_exists ; //mxd
2016-05-29 00:38:55 +00:00
}
}
// Pasting or moving a two-sided line into an enclosed void should NOT
// create a new sector out of the entire void.
// Heuristic: if the traced sector includes any edges that are NOT
// "ours", and NONE of those edges already exist, that sector must be
// in an enclosed void, and should not be drawn.
// However, if existing_only is false, the caller expects us to create
// new sides anyway; skip this check.
2016-06-02 09:55:01 +00:00
if ( existing_only & & has_existing_lines & & ! has_existing_sides & & ! has_dragged_sides )
continue ;
2016-05-29 00:38:55 +00:00
// Ignore traced edges when trying to create any further sectors
foreach ( LinedefSide ls3 in edges_in_sector ) ls3 . Ignore = true ;
// Check if sector traced is already valid
if ( builder . IsValidSector ( ) ) continue ;
// Check if we traced over an existing sector (or part of one)
Sector sector = builder . FindExistingSector ( sides_correct ) ;
if ( sector ! = null )
{
// Check if it's already been (re)used
bool reused = false ;
foreach ( Sector s in sectors_reused )
{
if ( s = = sector )
{
reused = true ;
break ;
}
}
// If we can reuse the sector, do so
if ( ! reused )
sectors_reused . Add ( sector ) ;
else
sector = null ;
}
// Create sector
builder . CreateSector ( sector , null ) ;
}
2017-02-08 12:18:01 +00:00
// Remove any sides that weren't part of a sector
for ( int i = 0 ; i < edgeCount ; i + + )
{
LinedefSide ls = edges [ i ] ;
if ( ls . Ignore | | ls . Line = = null ) continue ;
2017-02-20 07:13:35 +00:00
if ( ls . Line . Start = = null | | ls . Line . End = = null )
throw new Exception ( "ls line is null" ) ;
2016-05-29 00:38:55 +00:00
if ( ls . Front )
{
if ( ls . Line . Front ! = null )
{
ls . Line . Front . Dispose ( ) ;
// Update doublesided flag
ls . Line . ApplySidedFlags ( ) ;
}
}
else
{
if ( ls . Line . Back ! = null )
{
ls . Line . Back . Dispose ( ) ;
// Update doublesided flag
ls . Line . ApplySidedFlags ( ) ;
}
}
}
// Check if any lines need to be flipped
FlipBackwardLinedefs ( lines ) ;
// Find an adjacent sector to copy properties from
Sector sector_copy = null ;
foreach ( Linedef l in lines )
{
// Check front sector
Sector sector = ( l . Front ! = null ? l . Front . Sector : null ) ;
if ( sector ! = null & & ! sector . Marked )
{
// Copy this sector if it isn't newly created
sector_copy = sector ;
break ;
}
// Check back sector
sector = ( l . Back ! = null ? l . Back . Sector : null ) ;
if ( sector ! = null & & ! sector . Marked )
{
// Copy this sector if it isn't newly created
sector_copy = sector ;
break ;
}
}
// Go through newly created sectors
List < Sector > newsectors = General . Map . Map . GetMarkedSectors ( true ) ; //mxd
foreach ( Sector s in newsectors )
{
// Skip if sector already has properties
2016-08-05 14:30:10 +00:00
if ( s . CeilTexture ! = "-" | | s . FloorTexture ! = "-"
| | s . FloorHeight ! = General . Settings . DefaultFloorHeight
| | s . CeilHeight ! = General . Settings . DefaultCeilingHeight )
continue ;
2016-05-29 00:38:55 +00:00
// Copy from adjacent sector if any
if ( sector_copy ! = null )
{
sector_copy . CopyPropertiesTo ( s ) ;
continue ;
}
// Otherwise, use defaults from game configuration
s . SetFloorTexture ( General . Map . Options . DefaultFloorTexture ) ;
s . SetCeilTexture ( General . Map . Options . DefaultCeilingTexture ) ;
s . FloorHeight = General . Settings . DefaultFloorHeight ;
s . CeilHeight = General . Settings . DefaultCeilingHeight ;
s . Brightness = General . Settings . DefaultBrightness ;
}
// Update line textures
List < Sidedef > newsides = General . Map . Map . GetMarkedSidedefs ( true ) ;
foreach ( Sidedef side in newsides )
{
// Clear any unneeded textures
2016-06-02 09:55:01 +00:00
side . RemoveUnneededTextures ( side . Other ! = null , false , true ) ;
2016-05-29 00:38:55 +00:00
// Set middle texture if needed
if ( side . MiddleRequired ( ) & & side . MiddleTexture = = "-" )
{
// Find adjacent texture (any)
string tex = GetAdjacentMiddleTexture ( side . Line . Start ) ;
if ( tex = = "-" ) tex = GetAdjacentMiddleTexture ( side . Line . End ) ;
// If no adjacent texture, get default from game configuration
if ( tex = = "-" ) tex = General . Settings . DefaultTexture ;
// Set texture
side . SetTextureMid ( tex ) ;
}
// Update sided flags
side . Line . ApplySidedFlags ( ) ;
}
// Remove any extra sectors
General . Map . Map . RemoveUnusedSectors ( false ) ;
}
//mxd. Try to create outer sidedefs if needed
private static void CorrectOuterSides ( HashSet < Linedef > changedlines )
{
HashSet < Linedef > linesmissingfront = new HashSet < Linedef > ( ) ;
HashSet < Linedef > linesmissingback = new HashSet < Linedef > ( ) ;
// Collect lines without front/back sides
foreach ( Linedef line in changedlines )
{
if ( line . Back = = null ) linesmissingback . Add ( line ) ;
if ( line . Front = = null ) linesmissingfront . Add ( line ) ;
}
2016-10-31 18:52:29 +00:00
// Anything to do?
if ( linesmissingfront . Count = = 0 & & linesmissingback . Count = = 0 ) return ;
// Let's use a blockmap...
RectangleF area = CreateArea ( linesmissingfront ) ;
area = IncreaseArea ( area , linesmissingback ) ;
BlockMap < BlockEntry > blockmap = new BlockMap < BlockEntry > ( area ) ;
blockmap . AddSectorsSet ( General . Map . Map . Sectors ) ;
2016-05-29 00:38:55 +00:00
// Find sectors to join singlesided lines
Dictionary < Linedef , Sector > linefrontsectorref = new Dictionary < Linedef , Sector > ( ) ;
foreach ( Linedef line in linesmissingfront )
{
2016-10-31 18:52:29 +00:00
// Line is now inside a sector?
Sector nearest = FindSectorContaining ( blockmap , line ) ;
2016-05-29 00:38:55 +00:00
// We can reattach our line!
if ( nearest ! = null ) linefrontsectorref [ line ] = nearest ;
}
Dictionary < Linedef , Sector > linebacksectorref = new Dictionary < Linedef , Sector > ( ) ;
foreach ( Linedef line in linesmissingback )
{
2016-10-31 18:52:29 +00:00
// Line is now inside a sector?
Sector nearest = FindSectorContaining ( blockmap , line ) ;
2016-05-29 00:38:55 +00:00
// We can reattach our line!
if ( nearest ! = null ) linebacksectorref [ line ] = nearest ;
}
// Check single-sided lines. Add new sidedefs if necessary
// Key is dragged single-sided line, value is a sector dragged line ended up in.
foreach ( KeyValuePair < Linedef , Sector > group in linefrontsectorref )
{
Linedef line = group . Key ;
// Create new sidedef
Sidedef newside = General . Map . Map . CreateSidedef ( line , true , group . Value ) ;
// Copy props from the other side
Sidedef propssource = ( line . Front ? ? line . Back ) ;
propssource . CopyPropertiesTo ( newside ) ;
// Correct the linedef
if ( ( line . Front = = null ) & & ( line . Back ! = null ) )
{
line . FlipVertices ( ) ;
line . FlipSidedefs ( ) ;
}
2016-05-30 00:18:22 +00:00
// Adjust textures
if ( line . Front ! = null ) line . Front . RemoveUnneededTextures ( line . Back ! = null , false , true ) ;
if ( line . Back ! = null ) line . Back . RemoveUnneededTextures ( line . Front ! = null , false , true ) ;
// Correct the sided flags
line . ApplySidedFlags ( ) ;
2016-05-29 00:38:55 +00:00
}
foreach ( KeyValuePair < Linedef , Sector > group in linebacksectorref )
{
Linedef line = group . Key ;
// Create new sidedef
Sidedef newside = General . Map . Map . CreateSidedef ( line , false , group . Value ) ;
// Copy props from the other side
Sidedef propssource = ( line . Front ? ? line . Back ) ;
propssource . CopyPropertiesTo ( newside ) ;
// Correct the linedef
if ( ( line . Front = = null ) & & ( line . Back ! = null ) )
{
line . FlipVertices ( ) ;
line . FlipSidedefs ( ) ;
}
2016-05-30 00:18:22 +00:00
// Adjust textures
if ( line . Front ! = null ) line . Front . RemoveUnneededTextures ( line . Back ! = null , false , true ) ;
if ( line . Back ! = null ) line . Back . RemoveUnneededTextures ( line . Front ! = null , false , true ) ;
2016-05-29 00:38:55 +00:00
// Correct the sided flags
2016-05-30 00:18:22 +00:00
line . ApplySidedFlags ( ) ;
2016-05-29 00:38:55 +00:00
}
}
2016-10-31 18:52:29 +00:00
//mxd
private static Sector FindSectorContaining ( BlockMap < BlockEntry > sectorsmap , Linedef line )
{
HashSet < BlockEntry > blocks = new HashSet < BlockEntry >
{
sectorsmap . GetBlockAt ( line . Start . Position ) ,
sectorsmap . GetBlockAt ( line . End . Position ) ,
} ;
foreach ( BlockEntry be in blocks )
{
foreach ( Sector sector in be . Sectors )
{
// Check if target line is inside the found sector
if ( sector . Intersect ( line . Start . Position , false ) & & sector . Intersect ( line . End . Position , false ) )
return sector ;
}
}
return null ;
}
2016-05-29 00:38:55 +00:00
//mxd
private static string GetAdjacentMiddleTexture ( Vertex v )
{
// Go through adjacent lines
foreach ( Linedef l in v . Linedefs )
{
if ( l . Front ! = null & & l . Front . MiddleTexture ! = "-" ) return l . Front . MiddleTexture ;
if ( l . Back ! = null & & l . Back . MiddleTexture ! = "-" ) return l . Back . MiddleTexture ;
}
return "-" ;
}
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Geometry Tools
2009-05-19 09:59:19 +00:00
/// <summary>This removes any virtual sectors in the map and returns the number of sectors removed.</summary>
2009-04-19 18:07:22 +00:00
public int RemoveVirtualSectors ( )
{
int count = 0 ;
2009-06-05 19:03:56 +00:00
int index = 0 ;
2009-04-19 18:07:22 +00:00
// Go for all sectors
2009-06-05 19:03:56 +00:00
while ( index < numsectors )
2009-04-19 18:07:22 +00:00
{
// Remove when virtual
2009-06-05 19:03:56 +00:00
if ( sectors [ index ] . Fields . ContainsKey ( VIRTUAL_SECTOR_FIELD ) )
2009-04-19 18:07:22 +00:00
{
2009-06-05 19:03:56 +00:00
sectors [ index ] . Dispose ( ) ;
2009-04-19 18:07:22 +00:00
count + + ;
}
2009-06-05 19:03:56 +00:00
else
{
index + + ;
}
2009-04-19 18:07:22 +00:00
}
2009-06-05 19:03:56 +00:00
2009-04-19 18:07:22 +00:00
return count ;
}
2009-05-17 14:37:34 +00:00
2009-05-19 09:59:19 +00:00
/// <summary>This removes unused sectors and returns the number of removed sectors.</summary>
2009-05-17 14:37:34 +00:00
public int RemoveUnusedSectors ( bool reportwarnings )
{
int count = 0 ;
2009-06-05 19:03:56 +00:00
int index = numsectors - 1 ;
2009-05-17 14:37:34 +00:00
// Go for all sectors
2009-06-05 19:03:56 +00:00
while ( index > = 0 )
2009-05-17 14:37:34 +00:00
{
// Remove when unused
2009-06-05 19:03:56 +00:00
if ( sectors [ index ] . Sidedefs . Count = = 0 )
2009-05-17 14:37:34 +00:00
{
if ( reportwarnings )
General . ErrorLogger . Add ( ErrorType . Warning , "Sector " + index + " was unused and has been removed." ) ;
2009-06-05 19:03:56 +00:00
sectors [ index ] . Dispose ( ) ;
2009-05-17 14:37:34 +00:00
count + + ;
}
2009-06-05 19:03:56 +00:00
index - - ;
2009-05-17 14:37:34 +00:00
}
2009-06-05 19:03:56 +00:00
2009-05-17 14:37:34 +00:00
return count ;
}
2009-05-19 09:59:19 +00:00
2010-08-15 19:43:00 +00:00
/// <summary>This joins overlapping lines together. Returns false when the operation failed.</summary>
public static bool JoinOverlappingLines ( ICollection < Linedef > lines )
2009-04-19 18:07:22 +00:00
{
bool joined ;
do
{
// No joins yet
joined = false ;
// Go for all the lines
foreach ( Linedef l1 in lines )
{
// Check if these vertices have lines that overlap
foreach ( Linedef l2 in l1 . Start . Linedefs )
{
2013-09-03 09:34:28 +00:00
//mxd. The same line?
if ( l1 . Index = = l2 . Index ) continue ;
2009-04-19 18:07:22 +00:00
// Sharing vertices?
2016-05-18 20:31:40 +00:00
if ( l1 . End = = l2 . End | | l1 . End = = l2 . Start )
2009-04-19 18:07:22 +00:00
{
2013-09-03 09:34:28 +00:00
bool oppositedirection = ( l1 . End = = l2 . Start ) ;
bool l2marked = l2 . Marked ;
// Merge these two linedefs
while ( lines . Remove ( l2 ) ) ;
if ( ! l2 . Join ( l1 ) ) return false ;
// If l2 was marked as new geometry, we have to make sure
// that l1's FrontInterior is correct for the drawing procedure
if ( l2marked )
2009-04-19 18:07:22 +00:00
{
2013-09-03 09:34:28 +00:00
l1 . FrontInterior = l2 . FrontInterior ^ oppositedirection ;
}
2016-05-18 20:31:40 +00:00
// If l1 is marked as new geometry, we may need to flip it to preserve
// orientation of the original geometry, and update its FrontInterior
2013-09-03 09:34:28 +00:00
else if ( l1 . Marked )
{
if ( oppositedirection )
2009-05-12 11:36:52 +00:00
{
2013-09-03 09:34:28 +00:00
l1 . FlipVertices ( ) ; // This also flips FrontInterior
l1 . FlipSidedefs ( ) ;
2009-05-06 10:06:45 +00:00
}
2009-04-19 18:07:22 +00:00
}
2013-09-03 09:34:28 +00:00
joined = true ;
break ;
2009-04-19 18:07:22 +00:00
}
}
// Will have to restart when joined
if ( joined ) break ;
// Check if these vertices have lines that overlap
foreach ( Linedef l2 in l1 . End . Linedefs )
{
2013-09-03 09:34:28 +00:00
//mxd. The same line?
if ( l1 . Index = = l2 . Index ) continue ;
2009-04-19 18:07:22 +00:00
// Sharing vertices?
2016-05-18 20:31:40 +00:00
if ( l1 . Start = = l2 . End | | l1 . Start = = l2 . Start )
2009-04-19 18:07:22 +00:00
{
2013-09-03 09:34:28 +00:00
bool oppositedirection = ( l1 . Start = = l2 . End ) ;
bool l2marked = l2 . Marked ;
// Merge these two linedefs
while ( lines . Remove ( l2 ) ) ;
if ( ! l2 . Join ( l1 ) ) return false ;
// If l2 was marked as new geometry, we have to make sure
// that l1's FrontInterior is correct for the drawing procedure
if ( l2marked )
2009-04-19 18:07:22 +00:00
{
2013-09-03 09:34:28 +00:00
l1 . FrontInterior = l2 . FrontInterior ^ oppositedirection ;
}
2016-05-18 20:31:40 +00:00
// If l1 is marked as new geometry, we may need to flip it to preserve
// orientation of the original geometry, and update its FrontInterior
2013-09-03 09:34:28 +00:00
else if ( l1 . Marked )
{
if ( oppositedirection )
2009-05-12 11:36:52 +00:00
{
2013-09-03 09:34:28 +00:00
l1 . FlipVertices ( ) ; // This also flips FrontInterior
l1 . FlipSidedefs ( ) ;
2009-05-06 10:06:45 +00:00
}
2009-04-19 18:07:22 +00:00
}
2013-09-03 09:34:28 +00:00
joined = true ;
break ;
2009-04-19 18:07:22 +00:00
}
}
// Will have to restart when joined
if ( joined ) break ;
}
}
while ( joined ) ;
// Return result
2010-08-15 19:43:00 +00:00
return true ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This removes looped linedefs (linedefs which reference the same vertex for
/// start and end) and returns the number of linedefs removed.</summary>
2009-04-19 18:07:22 +00:00
public static int RemoveLoopedLinedefs ( ICollection < Linedef > lines )
{
int linesremoved = 0 ;
bool removedline ;
do
{
// Nothing removed yet
removedline = false ;
// Go for all the lines
foreach ( Linedef l in lines )
{
2016-06-02 09:55:01 +00:00
// Check if referencing the same vertex twice (mxd. Or if both verts are null)
if ( l . Start = = l . End | | l . Start . Position = = l . End . Position )
2009-04-19 18:07:22 +00:00
{
// Remove this line
while ( lines . Remove ( l ) ) ;
l . Dispose ( ) ;
linesremoved + + ;
removedline = true ;
break ;
}
}
}
while ( removedline ) ;
// Return result
return linesremoved ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This joins nearby vertices from two collections. This does NOT join vertices
/// within the same collection, only if they exist in both collections.
/// The vertex from the second collection is moved to match the first vertex.
/// When keepsecond is true, the vertex in the second collection is kept,
/// otherwise the vertex in the first collection is kept.
/// Returns the number of joins made.</summary>
2009-04-19 18:07:22 +00:00
public static int JoinVertices ( ICollection < Vertex > set1 , ICollection < Vertex > set2 , bool keepsecond , float joindist )
{
float joindist2 = joindist * joindist ;
int joinsdone = 0 ;
bool joined ;
do
{
2016-10-31 18:52:29 +00:00
//mxd. Create blockmap
ICollection < Vertex > biggerset , smallerset ;
bool keepsmaller ;
if ( set1 . Count > set2 . Count )
{
biggerset = set1 ;
smallerset = set2 ;
keepsmaller = ! keepsecond ;
}
else
{
biggerset = set2 ;
smallerset = set1 ;
keepsmaller = keepsecond ;
}
RectangleF area = CreateArea ( biggerset ) ;
BlockMap < BlockEntry > blockmap = new BlockMap < BlockEntry > ( area ) ;
blockmap . AddVerticesSet ( biggerset ) ;
2009-04-19 18:07:22 +00:00
// No joins yet
joined = false ;
2016-10-31 18:52:29 +00:00
// Go for all vertices in the smaller set
foreach ( Vertex v1 in smallerset )
2009-04-19 18:07:22 +00:00
{
2016-10-31 18:52:29 +00:00
HashSet < BlockEntry > blocks = new HashSet < BlockEntry >
2009-04-19 18:07:22 +00:00
{
2016-10-31 18:52:29 +00:00
blockmap . GetBlockAt ( v1 . Position ) ,
blockmap . GetBlockAt ( new Vector2D ( v1 . Position . x + joindist , v1 . Position . y + joindist ) ) ,
blockmap . GetBlockAt ( new Vector2D ( v1 . Position . x + joindist , v1 . Position . y - joindist ) ) ,
blockmap . GetBlockAt ( new Vector2D ( v1 . Position . x - joindist , v1 . Position . y + joindist ) ) ,
blockmap . GetBlockAt ( new Vector2D ( v1 . Position . x - joindist , v1 . Position . y - joindist ) )
} ;
foreach ( BlockEntry be in blocks )
{
if ( be = = null ) continue ;
foreach ( Vertex v2 in be . Vertices )
2009-04-19 18:07:22 +00:00
{
2016-10-31 18:52:29 +00:00
// Check if vertices are close enough
if ( v1 . DistanceToSq ( v2 . Position ) < = joindist2 )
2009-04-19 18:07:22 +00:00
{
2016-10-31 18:52:29 +00:00
// Check if not the same vertex
if ( v1 ! = v2 )
2009-04-19 18:07:22 +00:00
{
2016-10-31 18:52:29 +00:00
// Move the second vertex to match the first
v2 . Move ( v1 . Position ) ;
// Check which one to keep
if ( keepsmaller )
{
// Join the first into the second
// Second is kept, first is removed
v1 . Join ( v2 ) ;
biggerset . Remove ( v1 ) ;
smallerset . Remove ( v1 ) ;
}
else
{
// Join the second into the first
// First is kept, second is removed
v2 . Join ( v1 ) ;
biggerset . Remove ( v2 ) ;
smallerset . Remove ( v2 ) ;
}
// Count the join
joinsdone + + ;
joined = true ;
break ;
2009-04-19 18:07:22 +00:00
}
}
}
}
// Will have to restart when joined
if ( joined ) break ;
}
}
while ( joined ) ;
// Return result
return joinsdone ;
}
2013-12-17 13:18:44 +00:00
/// <summary>This joins nearby vertices in the same collection </summary>
2014-12-03 23:15:26 +00:00
public static int JoinVertices ( List < Vertex > set , float joindist )
{
2013-12-17 13:18:44 +00:00
float joindist2 = joindist * joindist ;
int joinsdone = 0 ;
bool joined ;
2014-12-03 23:15:26 +00:00
do
{
2013-12-17 13:18:44 +00:00
// No joins yet
joined = false ;
// Go for all vertices in the first set
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < set . Count - 1 ; i + + )
{
for ( int c = i + 1 ; c < set . Count ; c + + )
{
2015-12-28 15:01:53 +00:00
Vertex v1 = set [ i ] ;
Vertex v2 = set [ c ] ;
2013-12-17 13:18:44 +00:00
// Check if vertices are close enough
2015-12-28 15:01:53 +00:00
if ( v1 . DistanceToSq ( v2 . Position ) < = joindist2 )
2014-12-03 23:15:26 +00:00
{
2013-12-17 13:18:44 +00:00
// Check if not the same vertex
2015-12-28 15:01:53 +00:00
if ( v1 . Index ! = v2 . Index )
2014-12-03 23:15:26 +00:00
{
2013-12-17 13:18:44 +00:00
// Move the second vertex to match the first
v2 . Move ( v1 . Position ) ;
// Join the second into the first
v2 . Join ( v1 ) ;
set . Remove ( v2 ) ;
// Count the join
joinsdone + + ;
joined = true ;
break ;
}
}
}
}
} while ( joined ) ;
// Return result
return joinsdone ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This corrects lines that have a back sidedef but no front sidedef by flipping them. Returns the number of flips made.</summary>
2009-04-19 18:07:22 +00:00
public static int FlipBackwardLinedefs ( ICollection < Linedef > lines )
{
int flipsdone = 0 ;
// Examine all lines
foreach ( Linedef l in lines )
{
// Back side but no front side?
if ( ( l . Back ! = null ) & & ( l . Front = = null ) )
{
// Flip that linedef!
l . FlipVertices ( ) ;
l . FlipSidedefs ( ) ;
flipsdone + + ;
}
}
// Return result
return flipsdone ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This splits the given lines with the given vertices. All affected lines
2010-08-15 19:43:00 +00:00
/// will be added to changedlines. Returns false when the operation failed.</summary>
2016-06-02 09:55:01 +00:00
public static bool SplitLinesByVertices ( ICollection < Linedef > lines , ICollection < Vertex > verts , float splitdist , ICollection < Linedef > changedlines ) { return SplitLinesByVertices ( lines , verts , splitdist , changedlines , MergeGeometryMode . CLASSIC ) ; }
public static bool SplitLinesByVertices ( ICollection < Linedef > lines , ICollection < Vertex > verts , float splitdist , ICollection < Linedef > changedlines , MergeGeometryMode mergemode )
2009-04-19 18:07:22 +00:00
{
2015-12-28 15:01:53 +00:00
if ( verts . Count = = 0 | | lines . Count = = 0 ) return true ; //mxd
2013-12-24 09:21:18 +00:00
2009-04-19 18:07:22 +00:00
float splitdist2 = splitdist * splitdist ;
2013-12-17 13:18:44 +00:00
//mxd. Create blockmap
2014-01-13 08:06:56 +00:00
RectangleF area = CreateArea ( lines ) ;
IncreaseArea ( area , verts ) ;
2013-12-17 13:18:44 +00:00
BlockMap < BlockEntry > blockmap = new BlockMap < BlockEntry > ( area ) ;
blockmap . AddVerticesSet ( verts ) ;
blockmap . AddLinedefsSet ( lines ) ;
int bmWidth = blockmap . Size . Width ;
int bmHeight = blockmap . Size . Height ;
BlockEntry [ , ] bmap = blockmap . Map ;
2016-05-29 00:38:55 +00:00
//mxd
2016-06-02 09:55:01 +00:00
HashSet < Vertex > splitverts = new HashSet < Vertex > ( ) ;
HashSet < Sector > changedsectors = ( mergemode = = MergeGeometryMode . REPLACE ? General . Map . Map . GetSectorsFromLinedefs ( changedlines ) : new HashSet < Sector > ( ) ) ;
HashSet < Vertex > lineverts = new HashSet < Vertex > ( ) ;
foreach ( Linedef l in lines )
{
lineverts . Add ( l . Start ) ;
lineverts . Add ( l . End ) ;
}
2009-04-19 18:07:22 +00:00
2021-05-13 12:44:15 +00:00
foreach ( Vertex v in verts )
2016-05-29 00:38:55 +00:00
{
2021-05-13 12:44:15 +00:00
HashSet < BlockEntry > blocks ;
// If a vertex is in the very bottom left of it's block, and a linedef passes through the vertex it can
// happen (depending on the linedef's direction) that the vertex and the linedef are not in the same
// block. This will cause the linedef to not be split correctly. The prevent this we'll get all blocks
// surrounding the vertex, since the linedef will definitely be in at least one of them. Otherwise
// just take the block the vertex is in.
if ( ( blockmap . Range . Left - ( int ) v . Position . x ) % blockmap . BlockSize = = 0 & & ( blockmap . Range . Bottom - ( int ) v . Position . y ) % blockmap . BlockSize = = 0 )
{
blocks = blockmap . GetSquareRange ( v . Position . x - 1 , v . Position . y - 1 , 2 , 2 ) ;
}
else
{
blocks = new HashSet < BlockEntry > ( ) { blockmap . GetBlockAt ( v . Position ) } ;
}
foreach ( BlockEntry be in blocks )
2014-12-03 23:15:26 +00:00
{
2021-05-13 12:44:15 +00:00
if ( be = = null ) continue ;
2016-05-29 00:38:55 +00:00
2021-05-13 12:44:15 +00:00
for ( int i = 0 ; i < be . Lines . Count ; i + + )
2014-12-03 23:15:26 +00:00
{
2021-05-13 12:44:15 +00:00
Linedef l = be . Lines [ i ] ;
2016-05-29 00:38:55 +00:00
2021-05-13 12:44:15 +00:00
// Check if v is close enough to l for splitting
if ( l . DistanceToSq ( v . Position , true ) < = splitdist2 )
{
// Line is not already referencing v?
Vector2D deltastart = l . Start . Position - v . Position ;
Vector2D deltaend = l . End . Position - v . Position ;
if ( ( ( Math . Abs ( deltastart . x ) > 0.001 ) | | ( Math . Abs ( deltastart . y ) > 0.001 ) ) & &
( ( Math . Abs ( deltaend . x ) > 0.001 ) | | ( Math . Abs ( deltaend . y ) > 0.001 ) ) )
2016-05-29 00:38:55 +00:00
{
2021-05-13 12:44:15 +00:00
// Split line l with vertex v
Linedef nl = l . Split ( v ) ;
if ( nl = = null ) return false ;
v . Marked = true ; //mxd
splitverts . Add ( v ) ; //mxd
// Add the new line to the list
lines . Add ( nl ) ;
blockmap . AddLinedef ( nl ) ;
// Both lines must be updated because their new length is relevant for next iterations!
l . UpdateCache ( ) ;
nl . UpdateCache ( ) ;
// Add both lines to changedlines
if ( changedlines ! = null )
2016-05-29 00:38:55 +00:00
{
2021-05-13 12:44:15 +00:00
changedlines . Add ( l ) ;
changedlines . Add ( nl ) ;
2016-05-29 00:38:55 +00:00
}
}
}
}
}
}
2013-12-17 13:18:44 +00:00
2016-05-29 00:38:55 +00:00
//mxd. Remove lines, which are inside affected sectors
2016-06-02 09:55:01 +00:00
if ( mergemode = = MergeGeometryMode . REPLACE & & changedsectors . Count > 0 )
2016-05-29 00:38:55 +00:00
{
HashSet < Linedef > alllines = new HashSet < Linedef > ( lines ) ;
if ( changedlines ! = null ) alllines . UnionWith ( changedlines ) ;
foreach ( Linedef l in alllines ) l . UpdateCache ( ) ;
foreach ( Sector s in changedsectors ) s . UpdateBBox ( ) ;
foreach ( Linedef l in alllines )
{
2016-07-02 22:27:20 +00:00
// Remove line when it's start, center and end are inside a changed sector and neither side references it
2016-06-02 09:55:01 +00:00
if ( l . Start ! = null & & l . End ! = null & &
2016-05-29 00:38:55 +00:00
( l . Front = = null | | ! changedsectors . Contains ( l . Front . Sector ) ) & &
( l . Back = = null | | ! changedsectors . Contains ( l . Back . Sector ) ) )
{
foreach ( Sector s in changedsectors )
2014-12-03 23:15:26 +00:00
{
2016-07-02 22:27:20 +00:00
if ( s . Intersect ( l . Start . Position ) & & s . Intersect ( l . End . Position ) & & s . Intersect ( l . GetCenterPoint ( ) ) )
2014-12-03 23:15:26 +00:00
{
2016-06-02 09:55:01 +00:00
Vertex [ ] tocheck = { l . Start , l . End } ;
while ( lines . Remove ( l ) ) ;
if ( changedlines ! = null ) while ( changedlines . Remove ( l ) ) ;
2016-05-29 00:38:55 +00:00
l . Dispose ( ) ;
foreach ( Vertex v in tocheck )
2014-12-03 23:15:26 +00:00
{
2016-06-02 09:55:01 +00:00
// If the newly created vertex only has 2 linedefs attached, then merge the linedefs
if ( ! v . IsDisposed & & v . Linedefs . Count = = 2 & & splitverts . Contains ( v ) )
2014-12-03 23:15:26 +00:00
{
2016-05-29 00:38:55 +00:00
Linedef ld1 = General . GetByIndex ( v . Linedefs , 0 ) ;
Linedef ld2 = General . GetByIndex ( v . Linedefs , 1 ) ;
2016-06-02 09:55:01 +00:00
if ( ! ld1 . Marked & & ! ld2 . Marked )
{
Vertex v2 = ( ld2 . Start = = v ) ? ld2 . End : ld2 . Start ;
if ( ld1 . Start = = v ) ld1 . SetStartVertex ( v2 ) ; else ld1 . SetEndVertex ( v2 ) ;
while ( lines . Remove ( ld2 ) ) ;
if ( changedlines ! = null ) while ( changedlines . Remove ( ld2 ) ) ;
ld2 . Dispose ( ) ;
// Trash vertex
v . Dispose ( ) ;
2017-02-20 07:13:35 +00:00
}
}
2009-04-19 18:07:22 +00:00
}
2016-05-29 00:38:55 +00:00
break ;
2009-04-19 18:07:22 +00:00
}
2016-05-29 00:38:55 +00:00
}
}
}
2016-06-02 09:55:01 +00:00
}
2017-02-20 07:13:35 +00:00
// [ZZ] note: disposing a vertex means also disposing all attached linedefs!
// we need to iterate through our lines collection and make sure no disposed linedefs exist there.
// also, just in case, do it for vertices as well, because vertices can be chain-disposed.
foreach ( Linedef line in lines . Where ( line = > line . IsDisposed ) . ToList ( ) )
while ( lines . Remove ( line ) ) ;
foreach ( Vertex vert in verts . Where ( vert = > vert . IsDisposed ) . ToList ( ) )
while ( verts . Remove ( vert ) ) ;
return true ;
2016-05-29 00:38:55 +00:00
}
2013-12-17 13:18:44 +00:00
2016-05-29 00:38:55 +00:00
/// <summary>Splits lines by lines. Adds new lines to the second collection. Returns false when the operation failed.</summary>
2017-02-20 07:13:35 +00:00
public static bool SplitLinesByLines ( ICollection < Linedef > lines , HashSet < Linedef > changedlines , MergeGeometryMode mergemode ) //mxd
2016-05-29 00:38:55 +00:00
{
2016-06-02 09:55:01 +00:00
if ( lines . Count = = 0 | | changedlines . Count = = 0 | | mergemode = = MergeGeometryMode . CLASSIC ) return true ;
2016-05-29 00:38:55 +00:00
// Create blockmap
2016-06-02 09:55:01 +00:00
HashSet < Vertex > verts = new HashSet < Vertex > ( ) ; //mxd
foreach ( Linedef l in lines )
{
verts . Add ( l . Start ) ;
verts . Add ( l . End ) ;
}
foreach ( Linedef l in changedlines )
{
verts . Add ( l . Start ) ;
verts . Add ( l . End ) ;
}
2016-05-29 00:38:55 +00:00
RectangleF area = RectangleF . Union ( CreateArea ( lines ) , CreateArea ( changedlines ) ) ;
BlockMap < BlockEntry > blockmap = new BlockMap < BlockEntry > ( area ) ;
blockmap . AddLinedefsSet ( lines ) ;
blockmap . AddLinedefsSet ( changedlines ) ;
2016-06-02 09:55:01 +00:00
blockmap . AddVerticesSet ( verts ) ; //mxd
2016-05-29 00:38:55 +00:00
int bmWidth = blockmap . Size . Width ;
int bmHeight = blockmap . Size . Height ;
BlockEntry [ , ] bmap = blockmap . Map ;
//mxd
HashSet < Vertex > splitverts = new HashSet < Vertex > ( ) ;
2016-06-02 09:55:01 +00:00
HashSet < Sector > changedsectors = ( mergemode = = MergeGeometryMode . REPLACE ? General . Map . Map . GetSectorsFromLinedefs ( changedlines ) : new HashSet < Sector > ( ) ) ;
2016-05-29 00:38:55 +00:00
// Check for intersections
for ( int w = 0 ; w < bmWidth ; w + + )
{
for ( int h = 0 ; h < bmHeight ; h + + )
{
BlockEntry block = bmap [ w , h ] ;
if ( block . Lines . Count = = 0 ) continue ;
for ( int i = 0 ; i < block . Lines . Count ; i + + )
{
Linedef l1 = block . Lines [ i ] ;
for ( int c = 0 ; c < block . Lines . Count ; c + + )
{
if ( i = = c ) continue ;
Linedef l2 = block . Lines [ c ] ;
if ( l1 = = l2
| | l1 . Start . Position = = l2 . Start . Position
| | l1 . Start . Position = = l2 . End . Position
| | l1 . End . Position = = l2 . Start . Position
| | l1 . End . Position = = l2 . End . Position ) continue ;
// Check for intersection
Vector2D intersection = Line2D . GetIntersectionPoint ( new Line2D ( l1 ) , new Line2D ( l2 ) , true ) ;
2020-05-18 16:14:54 +00:00
if ( ! double . IsNaN ( intersection . x ) )
2016-05-29 00:38:55 +00:00
{
2016-06-02 09:55:01 +00:00
//mxd. Round to map format precision
2020-05-22 19:39:18 +00:00
intersection . x = Math . Round ( intersection . x , General . Map . FormatInterface . VertexDecimals ) ;
intersection . y = Math . Round ( intersection . y , General . Map . FormatInterface . VertexDecimals ) ;
2016-06-02 09:55:01 +00:00
2016-08-14 16:59:36 +00:00
//mxd. Skip when intersection matches start/end position.
// Otherwise infinite ammount of 0-length lines will be created...
if ( l1 . Start . Position = = intersection | | l1 . End . Position = = intersection | |
l2 . Start . Position = = intersection | | l2 . End . Position = = intersection ) continue ;
2016-06-02 09:55:01 +00:00
//mxd. Do we already have a vertex here?
bool existingvert = false ;
Vertex splitvertex = null ;
foreach ( Vertex v in block . Vertices )
{
if ( v . Position = = intersection )
{
splitvertex = v ;
existingvert = true ;
break ;
}
}
//mxd. Create split vertex?
if ( splitvertex = = null ) splitvertex = General . Map . Map . CreateVertex ( intersection ) ;
2016-05-29 00:38:55 +00:00
if ( splitvertex = = null ) return false ;
// Split both lines
Linedef nl1 = l1 . Split ( splitvertex ) ;
if ( nl1 = = null ) return false ;
Linedef nl2 = l2 . Split ( splitvertex ) ;
if ( nl2 = = null ) return false ;
2016-06-02 09:55:01 +00:00
// Mark split vertex?
if ( ! existingvert )
{
splitvertex . Marked = true ;
splitverts . Add ( splitvertex ) ; //mxd
}
2016-05-29 00:38:55 +00:00
// Add to the second collection
changedlines . Add ( nl1 ) ;
changedlines . Add ( nl2 ) ;
// And to the block entry
blockmap . AddLinedef ( nl1 ) ;
blockmap . AddLinedef ( nl2 ) ;
}
2009-04-19 18:07:22 +00:00
}
}
}
}
2016-05-29 00:38:55 +00:00
//mxd. Remove lines, which are inside affected sectors
2016-06-02 09:55:01 +00:00
if ( mergemode = = MergeGeometryMode . REPLACE )
2016-05-29 00:38:55 +00:00
{
HashSet < Linedef > alllines = new HashSet < Linedef > ( lines ) ;
alllines . UnionWith ( changedlines ) ;
foreach ( Linedef l in alllines ) l . UpdateCache ( ) ;
foreach ( Sector s in changedsectors ) s . UpdateBBox ( ) ;
foreach ( Linedef l in alllines )
{
2016-07-02 22:27:20 +00:00
// Remove line when it's start, center and end are inside a changed sector and neither side references it
2016-07-13 21:53:48 +00:00
if ( l . Start ! = null & & l . End ! = null
& & ( l . Front = = null | | ! changedsectors . Contains ( l . Front . Sector ) )
& & ( l . Back = = null | | ! changedsectors . Contains ( l . Back . Sector ) ) )
2016-05-29 00:38:55 +00:00
{
2016-07-13 21:53:48 +00:00
foreach ( Sector s in changedsectors )
2016-06-02 09:55:01 +00:00
{
2016-07-13 21:53:48 +00:00
if ( s . Intersect ( l . Start . Position ) & & s . Intersect ( l . End . Position ) & & s . Intersect ( l . GetCenterPoint ( ) ) )
2016-05-29 00:38:55 +00:00
{
2016-07-13 21:53:48 +00:00
Vertex [ ] tocheck = { l . Start , l . End } ;
l . Dispose ( ) ;
2016-05-29 00:38:55 +00:00
2016-07-13 21:53:48 +00:00
foreach ( Vertex v in tocheck )
{
// If the newly created vertex only has 2 linedefs attached, then merge the linedefs
if ( ! v . IsDisposed & & v . Linedefs . Count = = 2 & & splitverts . Contains ( v ) )
2016-06-02 09:55:01 +00:00
{
2016-07-13 21:53:48 +00:00
Linedef ld1 = General . GetByIndex ( v . Linedefs , 0 ) ;
Linedef ld2 = General . GetByIndex ( v . Linedefs , 1 ) ;
Vertex v2 = ( ld2 . Start = = v ) ? ld2 . End : ld2 . Start ;
if ( ld1 . Start = = v ) ld1 . SetStartVertex ( v2 ) ; else ld1 . SetEndVertex ( v2 ) ;
ld2 . Dispose ( ) ;
2016-06-02 09:55:01 +00:00
2016-07-13 21:53:48 +00:00
// Trash vertex
v . Dispose ( ) ;
2016-05-29 00:38:55 +00:00
}
2016-06-02 09:55:01 +00:00
}
2016-07-13 21:53:48 +00:00
break ;
2016-05-29 00:38:55 +00:00
}
}
}
}
}
2010-08-15 19:43:00 +00:00
return true ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This finds the side closest to the specified position.</summary>
2009-04-19 18:07:22 +00:00
public static Sidedef NearestSidedef ( ICollection < Sidedef > selection , Vector2D pos )
{
Sidedef closest = null ;
2020-05-21 12:20:02 +00:00
double distance = double . MaxValue ;
2009-04-19 18:07:22 +00:00
// Go for all sidedefs in selection
foreach ( Sidedef sd in selection )
{
// Calculate distance and check if closer than previous find
2020-05-21 12:20:02 +00:00
double d = sd . Line . SafeDistanceToSq ( pos , true ) ;
2009-05-06 07:26:12 +00:00
if ( d = = distance )
{
// Same distance, so only pick the one that is on the right side of the line
2020-05-21 12:20:02 +00:00
double side = sd . Line . SideOfLine ( pos ) ;
2009-05-06 07:26:12 +00:00
if ( ( ( side < = 0.0f ) & & sd . IsFront ) | | ( ( side > 0.0f ) & & ! sd . IsFront ) )
{
closest = sd ;
distance = d ;
}
}
else if ( d < distance )
2009-04-19 18:07:22 +00:00
{
// This one is closer
closest = sd ;
distance = d ;
}
}
// Return result
return closest ;
}
2009-05-19 09:59:19 +00:00
2016-10-31 18:52:29 +00:00
/// <summary>This finds the line closest to the specified position.</summary>
public static Linedef NearestLinedef ( BlockMap < BlockEntry > selectionmap , Vector2D pos ) //mxd
{
Linedef closest = null ;
2020-05-21 12:20:02 +00:00
double distance = double . MaxValue ;
2016-10-31 18:52:29 +00:00
Point p = selectionmap . GetBlockCoordinates ( pos ) ;
int minx = p . X ;
int maxx = p . X ;
int miny = p . Y ;
int maxy = p . Y ;
int step = 0 ;
// Check square block ranges around pos...
while ( true )
{
bool noblocksfound = true ;
for ( int x = minx ; x < maxx + 1 ; x + + )
{
for ( int y = miny ; y < maxy + 1 ; y + + )
{
// Skip inner blocks...
if ( x > minx & & x < maxx & & y > miny & & y < maxy ) continue ;
if ( ! selectionmap . IsInRange ( new Point ( x , y ) ) ) continue ;
// Go for all linedefs in block
BlockEntry be = selectionmap . Map [ x , y ] ;
foreach ( Linedef l in be . Lines )
{
// Calculate distance and check if closer than previous find
2020-05-21 12:20:02 +00:00
double d = l . SafeDistanceToSq ( pos , true ) ;
2016-10-31 18:52:29 +00:00
if ( d < distance )
{
// This one is closer
closest = l ;
distance = d ;
}
}
noblocksfound = false ;
}
}
// Abort if line was found or when outside of blockmap range...
// Check at least 3x3 blocks, because there's a possibility that a line closer to pos exists in a nearby block than in the first block
if ( noblocksfound | | ( closest ! = null & & step > 0 ) ) return closest ;
// Increase search range...
minx - - ;
maxx + + ;
miny - - ;
maxy + + ;
step + + ;
}
}
2009-05-19 09:59:19 +00:00
/// <summary>This finds the line closest to the specified position.</summary>
2009-04-19 18:07:22 +00:00
public static Linedef NearestLinedef ( ICollection < Linedef > selection , Vector2D pos )
{
Linedef closest = null ;
2020-05-21 12:20:02 +00:00
double distance = double . MaxValue ;
2009-04-19 18:07:22 +00:00
// Go for all linedefs in selection
foreach ( Linedef l in selection )
{
// Calculate distance and check if closer than previous find
2020-05-21 12:20:02 +00:00
double d = l . SafeDistanceToSq ( pos , true ) ;
2009-04-19 18:07:22 +00:00
if ( d < distance )
{
// This one is closer
closest = l ;
distance = d ;
}
}
// Return result
return closest ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This finds the line closest to the specified position.</summary>
2020-05-21 12:20:02 +00:00
public static Linedef NearestLinedefRange ( ICollection < Linedef > selection , Vector2D pos , double maxrange )
2009-04-19 18:07:22 +00:00
{
Linedef closest = null ;
2020-05-21 12:20:02 +00:00
double distance = float . MaxValue ;
double maxrangesq = maxrange * maxrange ;
2009-04-19 18:07:22 +00:00
// Go for all linedefs in selection
foreach ( Linedef l in selection )
{
// Calculate distance and check if closer than previous find
2020-05-21 12:20:02 +00:00
double d = l . SafeDistanceToSq ( pos , true ) ;
2013-12-10 12:19:27 +00:00
if ( d < distance & & d < = maxrangesq )
2009-04-19 18:07:22 +00:00
{
// This one is closer
closest = l ;
distance = d ;
}
}
// Return result
return closest ;
}
2016-10-31 18:52:29 +00:00
/// <summary>This finds the line closest to the specified position.</summary>
2020-05-21 12:20:02 +00:00
public static Linedef NearestLinedefRange ( BlockMap < BlockEntry > selectionmap , Vector2D pos , double maxrange ) //mxd
2016-10-31 18:52:29 +00:00
{
2021-02-20 12:11:03 +00:00
if ( selectionmap = = null )
return null ;
2016-10-31 18:52:29 +00:00
Linedef closest = null ;
2020-05-21 12:20:02 +00:00
double distance = double . MaxValue ;
double maxrangesq = maxrange * maxrange ;
2016-10-31 18:52:29 +00:00
HashSet < Linedef > processed = new HashSet < Linedef > ( ) ;
2020-11-15 12:59:04 +00:00
HashSet < BlockEntry > blocks ;
// If the max range is lower or equal to the blockmap's block size we can take a shortcut got the the possible blocks.
// Otherwise get an rectangular range of blocks. This happens when maxrange is computed from low zoom levels
if ( maxrange < = selectionmap . BlockSize )
2016-10-31 18:52:29 +00:00
{
2020-11-15 12:59:04 +00:00
blocks = new HashSet < BlockEntry >
{
selectionmap . GetBlockAt ( pos ) ,
selectionmap . GetBlockAt ( new Vector2D ( pos . x + maxrange , pos . y + maxrange ) ) ,
selectionmap . GetBlockAt ( new Vector2D ( pos . x + maxrange , pos . y - maxrange ) ) ,
selectionmap . GetBlockAt ( new Vector2D ( pos . x - maxrange , pos . y + maxrange ) ) ,
selectionmap . GetBlockAt ( new Vector2D ( pos . x - maxrange , pos . y - maxrange ) )
} ;
}
else
{
blocks = selectionmap . GetSquareRange ( pos . x - maxrange , pos . y - maxrange , maxrange * 2 , maxrange * 2 ) ;
}
2016-10-31 18:52:29 +00:00
foreach ( BlockEntry be in blocks )
{
if ( be = = null ) continue ;
foreach ( Linedef l in be . Lines )
{
if ( processed . Contains ( l ) ) continue ;
2020-05-21 12:20:02 +00:00
2016-10-31 18:52:29 +00:00
// Calculate distance and check if closer than previous find
2020-05-21 12:20:02 +00:00
double d = l . SafeDistanceToSq ( pos , true ) ;
2016-10-31 18:52:29 +00:00
if ( d < distance & & d < = maxrangesq )
{
// This one is closer
closest = l ;
distance = d ;
}
processed . Add ( l ) ;
}
}
// Return result
return closest ;
}
2013-03-18 13:52:27 +00:00
/// <summary>mxd. This finds the line closest to the specified position excluding given list of linedefs.</summary>
2016-04-29 21:38:43 +00:00
public Linedef NearestLinedef ( Vector2D pos , HashSet < Linedef > linesToExclude )
2014-12-03 23:15:26 +00:00
{
2013-03-18 13:52:27 +00:00
Linedef closest = null ;
2020-05-21 12:20:02 +00:00
double distance = double . MaxValue ;
2013-03-18 13:52:27 +00:00
// Go for all linedefs in selection
2014-12-03 23:15:26 +00:00
foreach ( Linedef l in linedefs )
{
2016-04-29 21:38:43 +00:00
if ( linesToExclude . Contains ( l ) ) continue ;
2013-03-18 13:52:27 +00:00
// Calculate distance and check if closer than previous find
2020-05-21 12:20:02 +00:00
double d = l . SafeDistanceToSq ( pos , true ) ;
2014-12-03 23:15:26 +00:00
if ( d < distance )
{
2013-03-18 13:52:27 +00:00
// This one is closer
closest = l ;
distance = d ;
}
}
// Return result
return closest ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This finds the vertex closest to the specified position.</summary>
2009-04-19 18:07:22 +00:00
public static Vertex NearestVertex ( ICollection < Vertex > selection , Vector2D pos )
{
Vertex closest = null ;
2020-05-18 16:14:54 +00:00
double distance = double . MaxValue ;
2015-12-28 15:01:53 +00:00
2009-04-19 18:07:22 +00:00
// Go for all vertices in selection
foreach ( Vertex v in selection )
{
// Calculate distance and check if closer than previous find
2020-05-18 16:14:54 +00:00
double d = v . DistanceToSq ( pos ) ;
2009-04-19 18:07:22 +00:00
if ( d < distance )
{
// This one is closer
closest = v ;
distance = d ;
}
}
// Return result
return closest ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This finds the thing closest to the specified position.</summary>
2009-04-19 18:07:22 +00:00
public static Thing NearestThing ( ICollection < Thing > selection , Vector2D pos )
{
Thing closest = null ;
2020-05-18 16:14:54 +00:00
double distance = double . MaxValue ;
2009-04-19 18:07:22 +00:00
// Go for all things in selection
foreach ( Thing t in selection )
{
// Calculate distance and check if closer than previous find
2020-05-18 16:14:54 +00:00
double d = t . DistanceToSq ( pos ) ;
2009-04-19 18:07:22 +00:00
if ( d < distance )
{
// This one is closer
closest = t ;
distance = d ;
}
}
// Return result
return closest ;
}
2013-03-18 13:52:27 +00:00
/// <summary>mxd. This finds the thing closest to the specified thing.</summary>
2014-12-03 23:15:26 +00:00
public static Thing NearestThing ( ICollection < Thing > selection , Thing thing )
{
2013-03-18 13:52:27 +00:00
Thing closest = null ;
2020-05-18 16:14:54 +00:00
double distance = double . MaxValue ;
2013-03-18 13:52:27 +00:00
// Go for all things in selection
2014-12-03 23:15:26 +00:00
foreach ( Thing t in selection )
{
2013-03-18 13:52:27 +00:00
if ( t = = thing ) continue ;
// Calculate distance and check if closer than previous find
2020-05-18 16:14:54 +00:00
double d = t . DistanceToSq ( thing . Position ) ;
2014-12-03 23:15:26 +00:00
if ( d < distance )
{
2013-03-18 13:52:27 +00:00
// This one is closer
closest = t ;
distance = d ;
}
}
// Return result
return closest ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This finds the vertex closest to the specified position.</summary>
2020-05-18 16:14:54 +00:00
public static Vertex NearestVertexSquareRange ( ICollection < Vertex > selection , Vector2D pos , double maxrange )
2009-04-19 18:07:22 +00:00
{
2020-05-18 16:14:54 +00:00
RectangleF range = RectangleF . FromLTRB ( ( float ) ( pos . x - maxrange ) , ( float ) ( pos . y - maxrange ) , ( float ) ( pos . x + maxrange ) , ( float ) ( pos . y + maxrange ) ) ;
2009-04-19 18:07:22 +00:00
Vertex closest = null ;
2020-05-18 16:14:54 +00:00
double distance = double . MaxValue ;
2009-04-19 18:07:22 +00:00
// Go for all vertices in selection
foreach ( Vertex v in selection )
{
2020-05-18 16:14:54 +00:00
double px = v . Position . x ;
double py = v . Position . y ;
2013-12-10 12:19:27 +00:00
2013-12-11 09:47:35 +00:00
//mxd. Within range?
if ( ( v . Position . x < range . Left ) | | ( v . Position . x > range . Right )
| | ( v . Position . y < range . Top ) | | ( v . Position . y > range . Bottom ) )
continue ;
2013-12-10 12:19:27 +00:00
// Close than previous find?
2020-05-18 16:14:54 +00:00
double d = Math . Abs ( px - pos . x ) + Math . Abs ( py - pos . y ) ;
2013-12-10 12:19:27 +00:00
if ( d < distance )
2009-04-19 18:07:22 +00:00
{
2013-12-10 12:19:27 +00:00
// This one is closer
closest = v ;
distance = d ;
2009-04-19 18:07:22 +00:00
}
}
// Return result
return closest ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This finds the thing closest to the specified position.</summary>
2020-05-21 12:20:02 +00:00
public static Thing NearestThingSquareRange ( ICollection < Thing > selection , Vector2D pos , double maxrange )
2009-04-19 18:07:22 +00:00
{
2020-05-18 16:14:54 +00:00
RectangleF range = RectangleF . FromLTRB ( ( float ) ( pos . x - maxrange ) , ( float ) ( pos . y - maxrange ) , ( float ) ( pos . x + maxrange ) , ( float ) ( pos . y + maxrange ) ) ;
2009-04-19 18:07:22 +00:00
Thing closest = null ;
2020-05-18 16:14:54 +00:00
double distance = double . MaxValue ;
double size = double . MaxValue ; //mxd
2009-04-19 18:07:22 +00:00
2013-12-10 12:19:27 +00:00
// Go for all things in selection
2009-04-19 18:07:22 +00:00
foreach ( Thing t in selection )
{
2020-05-18 16:14:54 +00:00
double px = t . Position . x ;
double py = t . Position . y ;
2016-04-04 22:20:49 +00:00
//mxd. Determine displayed size
2020-05-18 16:14:54 +00:00
double ts ;
2016-04-04 22:20:49 +00:00
if ( t . FixedSize & & General . Map . Renderer2D . Scale > 1.0f )
ts = t . Size / General . Map . Renderer2D . Scale ;
else if ( General . Settings . FixedThingsScale & & t . Size * General . Map . Renderer2D . Scale > Renderer2D . FIXED_THING_SIZE )
ts = Renderer2D . FIXED_THING_SIZE / General . Map . Renderer2D . Scale ;
else
ts = t . Size ;
2013-12-10 12:19:27 +00:00
//mxd. Within range?
2015-09-16 12:10:43 +00:00
if ( px < range . Left - ts | | px > range . Right + ts | | py < range . Top - ts | | py > range . Bottom + ts ) continue ;
2013-12-10 12:19:27 +00:00
2015-08-03 22:02:39 +00:00
// Closer than previous find? mxd. Or smaller when distance is the same?
2020-05-18 16:14:54 +00:00
double d = Math . Abs ( px - pos . x ) + Math . Abs ( py - pos . y ) ;
2015-09-16 12:10:43 +00:00
if ( d < distance | | ( d = = distance & & ts < size ) )
2009-04-19 18:07:22 +00:00
{
2013-12-10 12:19:27 +00:00
// This one is closer
closest = t ;
distance = d ;
2015-09-16 12:10:43 +00:00
size = ts ; //mxd
2009-04-19 18:07:22 +00:00
}
}
// Return result
return closest ;
}
2023-05-01 07:32:09 +00:00
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Tools
2009-05-19 09:59:19 +00:00
/// <summary>This snaps all vertices to the map format accuracy. Call this to ensure the vertices are at valid coordinates.</summary>
2009-04-19 18:07:22 +00:00
public void SnapAllToAccuracy ( )
{
2015-06-19 19:19:41 +00:00
SnapAllToAccuracy ( true ) ;
}
/// <summary>This snaps all vertices to the map format accuracy. Call this to ensure the vertices are at valid coordinates.</summary>
public void SnapAllToAccuracy ( bool usepreciseposition )
{
foreach ( Vertex v in vertices ) v . SnapToAccuracy ( usepreciseposition ) ;
foreach ( Thing t in things ) t . SnapToAccuracy ( usepreciseposition ) ;
2009-04-19 18:07:22 +00:00
}
2009-05-19 09:59:19 +00:00
/// <summary>This returns the next unused tag number.</summary>
2009-04-19 18:07:22 +00:00
public int GetNewTag ( )
{
Dictionary < int , bool > usedtags = new Dictionary < int , bool > ( ) ;
2009-07-09 14:03:47 +00:00
ForAllTags ( NewTagHandler , false , usedtags ) ;
ForAllTags ( NewTagHandler , true , usedtags ) ;
2009-04-19 18:07:22 +00:00
2009-07-09 14:03:47 +00:00
// Now find the first unused index
for ( int i = 1 ; i < = General . Map . FormatInterface . MaxTag ; i + + )
if ( ! usedtags . ContainsKey ( i ) ) return i ;
2009-04-19 18:07:22 +00:00
2009-07-09 14:03:47 +00:00
// All tags used!
return 0 ;
}
2015-07-27 21:35:42 +00:00
//mxd
/// <summary>This returns the next unused tag number.</summary>
public int GetNewTag ( List < int > moreusedtags )
{
Dictionary < int , bool > usedtags = new Dictionary < int , bool > ( ) ;
foreach ( int t in moreusedtags ) if ( ! usedtags . ContainsKey ( t ) ) usedtags . Add ( t , true ) ;
ForAllTags ( NewTagHandler , false , usedtags ) ;
ForAllTags ( NewTagHandler , true , usedtags ) ;
// Now find the first unused index
for ( int i = 1 ; i < = General . Map . FormatInterface . MaxTag ; i + + )
if ( ! usedtags . ContainsKey ( i ) ) return i ;
// All tags used!
return 0 ;
}
2013-08-28 08:29:06 +00:00
//mxd
/// <summary>This returns the tag number, which is not used by any map element of given type. This method doesn't check action arguments!</summary>
public int GetNewTag ( UniversalType elementType )
{
Dictionary < int , bool > usedtags = new Dictionary < int , bool > ( ) ;
2014-12-03 23:15:26 +00:00
switch ( elementType )
{
2013-08-28 08:29:06 +00:00
case UniversalType . ThingTag :
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < things . Length ; i + + )
{
2013-08-28 08:29:06 +00:00
if ( things [ i ] . Tag > 0 & & ! usedtags . ContainsKey ( things [ i ] . Tag ) )
usedtags . Add ( things [ i ] . Tag , false ) ;
}
break ;
case UniversalType . LinedefTag :
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < linedefs . Length ; i + + )
{
2015-12-28 15:01:53 +00:00
foreach ( int tag in linedefs [ i ] . Tags )
2015-07-28 15:04:21 +00:00
{
if ( tag = = 0 ) continue ;
if ( ! usedtags . ContainsKey ( tag ) ) usedtags . Add ( tag , false ) ;
}
2013-08-28 08:29:06 +00:00
}
break ;
case UniversalType . SectorTag :
2014-12-03 23:15:26 +00:00
for ( int i = 0 ; i < sectors . Length ; i + + )
{
2015-07-28 15:04:21 +00:00
foreach ( int tag in sectors [ i ] . Tags )
{
if ( tag = = 0 ) continue ;
if ( ! usedtags . ContainsKey ( tag ) ) usedtags . Add ( tag , false ) ;
}
2013-08-28 08:29:06 +00:00
}
break ;
}
// Now find the first unused index
for ( int i = 1 ; i < = General . Map . FormatInterface . MaxTag ; i + + )
if ( ! usedtags . ContainsKey ( i ) ) return i ;
// All tags used!
return 0 ;
}
2009-07-09 14:03:47 +00:00
/// <summary>This returns the next unused tag number within the marked geometry.</summary>
public int GetNewTag ( bool marked )
{
Dictionary < int , bool > usedtags = new Dictionary < int , bool > ( ) ;
ForAllTags ( NewTagHandler , marked , usedtags ) ;
2009-04-19 18:07:22 +00:00
// Now find the first unused index
for ( int i = 1 ; i < = General . Map . FormatInterface . MaxTag ; i + + )
if ( ! usedtags . ContainsKey ( i ) ) return i ;
2009-07-09 14:03:47 +00:00
// All tags used!
2009-04-19 18:07:22 +00:00
return 0 ;
}
2009-05-19 09:59:19 +00:00
2009-07-09 14:03:47 +00:00
/// <summary>This returns the next unused tag number.</summary>
public List < int > GetMultipleNewTags ( int count )
{
List < int > newtags = new List < int > ( count ) ;
2009-07-10 08:25:04 +00:00
if ( count > 0 )
2009-07-09 14:03:47 +00:00
{
2009-07-10 08:25:04 +00:00
Dictionary < int , bool > usedtags = new Dictionary < int , bool > ( ) ;
ForAllTags ( NewTagHandler , false , usedtags ) ;
ForAllTags ( NewTagHandler , true , usedtags ) ;
// Find unused tags and add them
for ( int i = 1 ; i < = General . Map . FormatInterface . MaxTag ; i + + )
2009-07-09 14:03:47 +00:00
{
2009-07-10 08:25:04 +00:00
if ( ! usedtags . ContainsKey ( i ) )
{
newtags . Add ( i ) ;
if ( newtags . Count = = count ) break ;
}
2009-07-09 14:03:47 +00:00
}
}
return newtags ;
}
/// <summary>This returns the next unused tag number within the marked geometry.</summary>
public List < int > GetMultipleNewTags ( int count , bool marked )
{
List < int > newtags = new List < int > ( count ) ;
2009-07-10 08:25:04 +00:00
if ( count > 0 )
2009-07-09 14:03:47 +00:00
{
2009-07-10 08:25:04 +00:00
Dictionary < int , bool > usedtags = new Dictionary < int , bool > ( ) ;
ForAllTags ( NewTagHandler , marked , usedtags ) ;
// Find unused tags and add them
for ( int i = 1 ; i < = General . Map . FormatInterface . MaxTag ; i + + )
2009-07-09 14:03:47 +00:00
{
2009-07-10 08:25:04 +00:00
if ( ! usedtags . ContainsKey ( i ) )
{
newtags . Add ( i ) ;
if ( newtags . Count = = count ) break ;
}
2009-07-09 14:03:47 +00:00
}
}
2009-07-10 08:25:04 +00:00
2009-07-09 14:03:47 +00:00
return newtags ;
}
// Handler for finding a new tag
2014-05-20 09:09:28 +00:00
private static void NewTagHandler ( MapElement element , bool actionargument , UniversalType type , ref int value , Dictionary < int , bool > usedtags )
2009-07-09 14:03:47 +00:00
{
usedtags [ value ] = true ;
}
/// <summary>This calls a function for all tag fields in the marked or unmarked geometry. The obj parameter can be anything you wish to pass on to your TagHandler function.</summary>
public void ForAllTags < T > ( TagHandler < T > handler , bool marked , T obj )
{
2015-07-28 15:04:21 +00:00
// Call handler on sectors tags
2009-07-09 14:03:47 +00:00
foreach ( Sector s in sectors )
2015-07-28 15:04:21 +00:00
{
2009-07-09 14:03:47 +00:00
if ( s . Marked = = marked )
{
2015-07-28 15:04:21 +00:00
//mxd. Multiple tags support...
bool changed = false ;
2016-07-06 00:15:19 +00:00
// Make a copy of tags, otherwise BeforePropsChange will be triggered after tag changes
List < int > tags = new List < int > ( s . Tags ) ;
for ( int i = 0 ; i < tags . Count ; i + + )
2015-07-28 15:04:21 +00:00
{
2016-07-06 00:15:19 +00:00
int tag = tags [ i ] ;
2015-07-28 15:04:21 +00:00
handler ( s , false , UniversalType . SectorTag , ref tag , obj ) ;
2016-07-06 00:15:19 +00:00
if ( tag ! = tags [ i ] )
2015-07-28 15:04:21 +00:00
{
2016-07-06 00:15:19 +00:00
tags [ i ] = tag ;
2015-07-28 15:04:21 +00:00
changed = true ;
}
}
2016-07-06 00:15:19 +00:00
if ( changed ) s . Tags = tags . Distinct ( ) . ToList ( ) ;
2009-07-09 14:03:47 +00:00
}
2015-07-28 15:04:21 +00:00
}
// Call handler on things tags
2009-07-09 14:03:47 +00:00
if ( General . Map . FormatInterface . HasThingTag )
{
foreach ( Thing t in things )
2015-07-28 15:04:21 +00:00
{
2009-07-09 14:03:47 +00:00
if ( t . Marked = = marked )
{
int tag = t . Tag ;
handler ( t , false , UniversalType . ThingTag , ref tag , obj ) ;
if ( tag ! = t . Tag ) t . Tag = tag ;
}
2015-07-28 15:04:21 +00:00
}
2009-07-09 14:03:47 +00:00
}
2015-07-28 15:04:21 +00:00
// Call handler on things action
if ( General . Map . FormatInterface . HasThingAction & & General . Map . FormatInterface . HasActionArgs )
2009-07-09 14:03:47 +00:00
{
foreach ( Thing t in things )
{
if ( t . Marked = = marked )
{
LinedefActionInfo info = General . Map . Config . GetLinedefActionInfo ( t . Action ) ;
for ( int i = 0 ; i < Thing . NUM_ARGS ; i + + )
2015-07-28 15:04:21 +00:00
{
2009-07-09 14:03:47 +00:00
if ( info . Args [ i ] . Used & & CheckIsTagType ( info . Args [ i ] . Type ) )
{
int tag = t . Args [ i ] ;
handler ( t , true , ( UniversalType ) ( info . Args [ i ] . Type ) , ref tag , obj ) ;
if ( tag ! = t . Args [ i ] ) t . Args [ i ] = tag ;
}
2015-07-28 15:04:21 +00:00
}
2009-07-09 14:03:47 +00:00
}
}
}
2015-07-28 15:04:21 +00:00
// Call handler on linedefs tags
2009-07-09 14:03:47 +00:00
if ( General . Map . FormatInterface . HasLinedefTag )
{
foreach ( Linedef l in linedefs )
2015-07-28 15:04:21 +00:00
{
2009-07-09 14:03:47 +00:00
if ( l . Marked = = marked )
{
2015-07-28 15:04:21 +00:00
//mxd. Multiple tags support...
bool changed = false ;
2016-07-06 00:15:19 +00:00
// Make a copy of tags, otherwise BeforePropsChange will be triggered after tag changes
List < int > tags = new List < int > ( l . Tags ) ;
for ( int i = 0 ; i < tags . Count ; i + + )
2015-07-28 15:04:21 +00:00
{
2016-07-06 00:15:19 +00:00
int tag = tags [ i ] ;
2015-07-28 15:04:21 +00:00
handler ( l , false , UniversalType . LinedefTag , ref tag , obj ) ;
2016-07-06 00:15:19 +00:00
if ( tag ! = tags [ i ] )
2015-07-28 15:04:21 +00:00
{
2016-07-06 00:15:19 +00:00
tags [ i ] = tag ;
2015-07-28 15:04:21 +00:00
changed = true ;
}
}
2016-07-06 00:15:19 +00:00
if ( changed ) l . Tags = tags . Distinct ( ) . ToList ( ) ;
2009-07-09 14:03:47 +00:00
}
2015-07-28 15:04:21 +00:00
}
2009-07-09 14:03:47 +00:00
}
2015-07-28 15:04:21 +00:00
// Call handler on linedefs action
2009-07-09 14:03:47 +00:00
if ( General . Map . FormatInterface . HasActionArgs )
{
foreach ( Linedef l in linedefs )
{
if ( l . Marked = = marked )
{
LinedefActionInfo info = General . Map . Config . GetLinedefActionInfo ( l . Action ) ;
for ( int i = 0 ; i < Linedef . NUM_ARGS ; i + + )
2015-07-28 15:04:21 +00:00
{
2009-07-09 14:03:47 +00:00
if ( info . Args [ i ] . Used & & CheckIsTagType ( info . Args [ i ] . Type ) )
{
int tag = l . Args [ i ] ;
handler ( l , true , ( UniversalType ) ( info . Args [ i ] . Type ) , ref tag , obj ) ;
if ( tag ! = l . Args [ i ] ) l . Args [ i ] = tag ;
}
2015-07-28 15:04:21 +00:00
}
2009-07-09 14:03:47 +00:00
}
}
}
}
// This checks if the given action argument type is a tag type
2014-05-20 09:09:28 +00:00
private static bool CheckIsTagType ( int argtype )
2009-07-09 14:03:47 +00:00
{
return ( argtype = = ( int ) UniversalType . LinedefTag ) | |
( argtype = = ( int ) UniversalType . SectorTag ) | |
( argtype = = ( int ) UniversalType . ThingTag ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This makes a list of lines related to marked vertices.
/// A line is unstable when one vertex is marked and the other isn't.</summary>
2016-05-29 00:38:55 +00:00
public List < Linedef > LinedefsFromMarkedVertices ( bool includeunmarked , bool includestable , bool includeunstable )
2009-04-19 18:07:22 +00:00
{
2009-06-05 19:03:56 +00:00
List < Linedef > list = new List < Linedef > ( ( numlinedefs / 2 ) + 1 ) ;
2009-04-19 18:07:22 +00:00
// Go for all lines
foreach ( Linedef l in linedefs )
{
// Check if this is to be included
if ( ( includestable & & ( l . Start . Marked & & l . End . Marked ) ) | |
( includeunstable & & ( l . Start . Marked ^ l . End . Marked ) ) | |
2016-05-29 00:38:55 +00:00
( includeunmarked & & ( ! l . Start . Marked & & ! l . End . Marked ) ) )
2009-04-19 18:07:22 +00:00
{
// Add to list
list . Add ( l ) ;
}
}
// Return result
return list ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This makes a list of unstable lines from the given vertices.
/// A line is unstable when one vertex is selected and the other isn't.</summary>
2009-04-19 18:07:22 +00:00
public static ICollection < Linedef > UnstableLinedefsFromVertices ( ICollection < Vertex > verts )
{
Dictionary < Linedef , Linedef > lines = new Dictionary < Linedef , Linedef > ( ) ;
// Go for all vertices
foreach ( Vertex v in verts )
{
// Go for all lines
foreach ( Linedef l in v . Linedefs )
{
// If the line exists in the list
if ( lines . ContainsKey ( l ) )
{
// Remove it
lines . Remove ( l ) ;
}
// Otherwise add it
else
{
// Add the line
lines . Add ( l , l ) ;
}
}
}
// Return result
return new List < Linedef > ( lines . Values ) ;
}
2009-05-19 09:59:19 +00:00
2013-09-11 09:47:53 +00:00
//mxd
2014-01-13 12:49:54 +00:00
/// <summary>This returns a sector if given coordinates are inside one.</summary>
2014-08-25 11:15:19 +00:00
public Sector GetSectorByCoordinates ( Vector2D pos )
{
2015-12-28 15:01:53 +00:00
foreach ( Sector s in sectors )
2014-08-25 11:15:19 +00:00
{
2015-12-28 15:01:53 +00:00
if ( s . Intersect ( pos ) ) return s ;
2014-08-25 11:15:19 +00:00
}
return null ;
}
//mxd
/// <summary>This returns a sector if given coordinates are inside one.</summary>
public Sector GetSectorByCoordinates ( Vector2D pos , VisualBlockMap blockmap )
{
2019-12-25 23:39:15 +00:00
return blockmap . GetSectorAt ( pos ) ;
2013-09-11 09:47:53 +00:00
}
2012-06-07 01:06:37 +00:00
2016-04-25 14:48:39 +00:00
//mxd
/// <summary>Gets unselected sectors, which have all their linedefs selected</summary>
public HashSet < Sector > GetUnselectedSectorsFromLinedefs ( IEnumerable < Linedef > lines )
{
HashSet < Sector > result = new HashSet < Sector > ( ) ;
Dictionary < Sector , HashSet < Sidedef > > sectorsbysides = new Dictionary < Sector , HashSet < Sidedef > > ( ) ;
HashSet < Sector > selectedsectors = new HashSet < Sector > ( General . Map . Map . GetSelectedSectors ( true ) ) ;
// Collect unselected sectors, which sidedefs belong to selected lines
foreach ( Linedef line in lines )
{
if ( line . Front ! = null & & line . Front . Sector ! = null & & ! selectedsectors . Contains ( line . Front . Sector ) )
{
if ( ! sectorsbysides . ContainsKey ( line . Front . Sector ) ) sectorsbysides . Add ( line . Front . Sector , new HashSet < Sidedef > ( ) ) ;
sectorsbysides [ line . Front . Sector ] . Add ( line . Front ) ;
}
if ( line . Back ! = null & & line . Back . Sector ! = null & & ! selectedsectors . Contains ( line . Back . Sector ) )
{
if ( ! sectorsbysides . ContainsKey ( line . Back . Sector ) ) sectorsbysides . Add ( line . Back . Sector , new HashSet < Sidedef > ( ) ) ;
sectorsbysides [ line . Back . Sector ] . Add ( line . Back ) ;
}
}
// Add sectors, which have all their lines selected
foreach ( var group in sectorsbysides )
{
if ( group . Key . Sidedefs . Count = = group . Value . Count ) result . Add ( group . Key ) ;
}
return result ;
}
2016-05-29 00:38:55 +00:00
//mxd
/// <summary>Gets sectors, which have all their linedefs selected</summary>
public HashSet < Sector > GetSectorsFromLinedefs ( IEnumerable < Linedef > lines )
{
HashSet < Sector > result = new HashSet < Sector > ( ) ;
Dictionary < Sector , HashSet < Sidedef > > sectorsbysides = new Dictionary < Sector , HashSet < Sidedef > > ( ) ;
// Collect unselected sectors, which sidedefs belong to selected lines
foreach ( Linedef line in lines )
{
2020-11-21 21:38:16 +00:00
if ( line . Front ! = null & & line . Front . Sector ! = null )
2016-05-29 00:38:55 +00:00
{
if ( ! sectorsbysides . ContainsKey ( line . Front . Sector ) ) sectorsbysides . Add ( line . Front . Sector , new HashSet < Sidedef > ( ) ) ;
sectorsbysides [ line . Front . Sector ] . Add ( line . Front ) ;
}
2020-11-21 21:38:16 +00:00
if ( line . Back ! = null & & line . Back . Sector ! = null )
2016-05-29 00:38:55 +00:00
{
if ( ! sectorsbysides . ContainsKey ( line . Back . Sector ) ) sectorsbysides . Add ( line . Back . Sector , new HashSet < Sidedef > ( ) ) ;
sectorsbysides [ line . Back . Sector ] . Add ( line . Back ) ;
}
}
// Add sectors, which have all their lines selected
foreach ( var group in sectorsbysides )
{
if ( group . Key . Sidedefs . Count = = group . Value . Count ) result . Add ( group . Key ) ;
}
return result ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This finds the line closest to the specified position.</summary>
2009-04-19 18:07:22 +00:00
public Linedef NearestLinedef ( Vector2D pos ) { return MapSet . NearestLinedef ( linedefs , pos ) ; }
2009-05-19 09:59:19 +00:00
/// <summary>This finds the line closest to the specified position.</summary>
2020-05-21 12:20:02 +00:00
public Linedef NearestLinedefRange ( Vector2D pos , double maxrange ) { return MapSet . NearestLinedefRange ( linedefs , pos , maxrange ) ; }
2009-04-19 18:07:22 +00:00
2009-05-19 09:59:19 +00:00
/// <summary>This finds the vertex closest to the specified position.</summary>
2009-04-19 18:07:22 +00:00
public Vertex NearestVertex ( Vector2D pos ) { return MapSet . NearestVertex ( vertices , pos ) ; }
2009-05-19 09:59:19 +00:00
/// <summary>This finds the vertex closest to the specified position.</summary>
2020-05-21 12:20:02 +00:00
public Vertex NearestVertexSquareRange ( Vector2D pos , double maxrange ) { return MapSet . NearestVertexSquareRange ( vertices , pos , maxrange ) ; }
2009-04-19 18:07:22 +00:00
2009-05-19 09:59:19 +00:00
/// <summary>This finds the thing closest to the specified position.</summary>
2020-05-21 12:20:02 +00:00
public Thing NearestThingSquareRange ( Vector2D pos , double maxrange ) { return MapSet . NearestThingSquareRange ( things , pos , maxrange ) ; }
2009-04-19 18:07:22 +00:00
2009-05-19 09:59:19 +00:00
/// <summary>This finds the closest unselected linedef that is not connected to the given vertex.</summary>
2020-05-21 12:20:02 +00:00
public Linedef NearestUnselectedUnreferencedLinedef ( Vector2D pos , double maxrange , Vertex v , out double distance )
2009-04-19 18:07:22 +00:00
{
Linedef closest = null ;
2020-05-21 12:20:02 +00:00
distance = double . MaxValue ;
double maxrangesq = maxrange * maxrange ;
2009-04-19 18:07:22 +00:00
// Go for all linedefs in selection
foreach ( Linedef l in linedefs )
{
// Calculate distance and check if closer than previous find
2020-05-21 12:20:02 +00:00
double d = l . SafeDistanceToSq ( pos , true ) ;
2009-04-19 18:07:22 +00:00
if ( ( d < = maxrangesq ) & & ( d < distance ) )
{
// Check if not selected
// Check if linedef is not connected to v
if ( ( l . Start ! = v ) & & ( l . End ! = v ) )
{
// This one is closer
closest = l ;
distance = d ;
}
}
}
// Return result
return closest ;
}
2023-05-01 07:32:09 +00:00
2009-04-19 18:07:22 +00:00
// This performs sidedefs compression
// Note: Only use this for saving, because this messes up the expected data structure horribly.
internal void CompressSidedefs ( )
{
2009-06-05 19:03:56 +00:00
Dictionary < uint , List < Sidedef > > storedsides = new Dictionary < uint , List < Sidedef > > ( numsidedefs ) ;
int originalsidescount = numsidedefs ;
2016-03-14 00:01:21 +00:00
long starttime = Clock . CurrentTime ;
2010-01-09 19:23:54 +00:00
BeginAddRemove ( ) ;
2009-04-19 18:07:22 +00:00
2009-06-05 19:03:56 +00:00
int sn = 0 ;
while ( sn < numsidedefs )
2009-04-19 18:07:22 +00:00
{
Sidedef stored = null ;
2009-06-05 19:03:56 +00:00
Sidedef snsd = sidedefs [ sn ] ;
2010-01-09 19:07:56 +00:00
2016-05-10 23:22:58 +00:00
//mxd. Skip sidedef if it belongs to a linedef with an action or tag?
if ( ! General . Map . Config . SidedefCompressionIgnoresAction & & ( snsd . Line . Action ! = 0 | | snsd . Line . Tag ! = 0 ) )
2016-05-07 20:21:53 +00:00
{
// Next!
sn + + ;
continue ;
}
2009-04-19 18:07:22 +00:00
// Check if checksum is stored
bool samesidedef = false ;
2009-06-05 19:03:56 +00:00
uint checksum = snsd . GetChecksum ( ) ;
2009-04-19 18:07:22 +00:00
bool checksumstored = storedsides . ContainsKey ( checksum ) ;
if ( checksumstored )
{
List < Sidedef > othersides = storedsides [ checksum ] ;
foreach ( Sidedef os in othersides )
{
// They must be in the same sector
2015-12-28 15:01:53 +00:00
if ( snsd . Sector = = os . Sector )
2009-04-19 18:07:22 +00:00
{
// Check if sidedefs are really the same
stored = os ;
MemoryStream sidemem = new MemoryStream ( 1024 ) ;
SerializerStream sidedata = new SerializerStream ( sidemem ) ;
MemoryStream othermem = new MemoryStream ( 1024 ) ;
SerializerStream otherdata = new SerializerStream ( othermem ) ;
2009-06-05 19:03:56 +00:00
snsd . ReadWrite ( sidedata ) ;
2009-04-19 18:07:22 +00:00
os . ReadWrite ( otherdata ) ;
2015-12-28 15:01:53 +00:00
if ( sidemem . Length = = othermem . Length )
2009-04-19 18:07:22 +00:00
{
samesidedef = true ;
sidemem . Seek ( 0 , SeekOrigin . Begin ) ;
othermem . Seek ( 0 , SeekOrigin . Begin ) ;
2015-12-28 15:01:53 +00:00
for ( int i = 0 ; i < sidemem . Length ; i + + )
2009-04-19 18:07:22 +00:00
{
2015-12-28 15:01:53 +00:00
if ( sidemem . ReadByte ( ) ! = othermem . ReadByte ( ) )
2009-04-19 18:07:22 +00:00
{
samesidedef = false ;
break ;
}
}
}
2010-01-09 19:07:56 +00:00
2015-12-28 15:01:53 +00:00
if ( samesidedef ) break ;
2009-04-19 18:07:22 +00:00
}
}
}
// Same sidedef?
if ( samesidedef )
{
// Replace with stored sidedef
2009-06-05 19:03:56 +00:00
bool isfront = snsd . IsFront ;
2010-01-09 19:07:56 +00:00
Linedef ld = snsd . Line ;
2009-06-11 21:21:20 +00:00
snsd . Line . DetachSidedefP ( snsd ) ;
2009-04-19 18:07:22 +00:00
if ( isfront )
2010-01-09 19:07:56 +00:00
ld . AttachFront ( stored ) ;
2009-04-19 18:07:22 +00:00
else
2010-01-09 19:07:56 +00:00
ld . AttachBack ( stored ) ;
2009-04-19 18:07:22 +00:00
// Remove the sidedef
2009-06-11 21:21:20 +00:00
snsd . SetSector ( null ) ;
2009-06-05 19:03:56 +00:00
RemoveSidedef ( sn ) ;
2009-04-19 18:07:22 +00:00
}
else
{
// Store this new one
if ( checksumstored )
{
2009-06-05 19:03:56 +00:00
storedsides [ checksum ] . Add ( snsd ) ;
2009-04-19 18:07:22 +00:00
}
else
{
2013-08-14 09:02:37 +00:00
List < Sidedef > newlist = new List < Sidedef > ( 4 ) { snsd } ;
2009-04-19 18:07:22 +00:00
storedsides . Add ( checksum , newlist ) ;
}
2009-06-05 19:03:56 +00:00
// Next
sn + + ;
2009-04-19 18:07:22 +00:00
}
}
2010-01-09 19:23:54 +00:00
EndAddRemove ( ) ;
2009-04-19 18:07:22 +00:00
// Output info
2016-03-14 00:01:21 +00:00
long endtime = Clock . CurrentTime ;
2012-07-12 22:34:12 +00:00
float deltatimesec = ( endtime - starttime ) / 1000.0f ;
2013-08-14 09:02:37 +00:00
float ratio = 100.0f - ( ( numsidedefs / ( float ) originalsidescount ) * 100.0f ) ;
2009-06-05 19:03:56 +00:00
General . WriteLogLine ( "Sidedefs compressed: " + numsidedefs + " remaining out of " + originalsidescount + " (" + ratio . ToString ( "########0.00" ) + "%) in " + deltatimesec . ToString ( "########0.00" ) + " seconds" ) ;
2009-04-19 18:07:22 +00:00
}
// This converts flags and activations to UDMF fields
2014-12-29 09:19:20 +00:00
internal void TranslateToUDMF ( Type previousmapformatinterfacetype )
2009-04-19 18:07:22 +00:00
{
2014-12-29 09:19:20 +00:00
foreach ( Linedef l in linedefs ) l . TranslateToUDMF ( previousmapformatinterfacetype ) ;
2009-04-19 18:07:22 +00:00
foreach ( Thing t in things ) t . TranslateToUDMF ( ) ;
}
// This converts UDMF fields back into flags and activations
// NOTE: Only converts the marked items
internal void TranslateFromUDMF ( )
{
foreach ( Linedef l in linedefs ) if ( l . Marked ) l . TranslateFromUDMF ( ) ;
2014-09-16 20:26:42 +00:00
foreach ( Sidedef s in sidedefs ) if ( s . Marked ) s . TranslateFromUDMF ( ) ; //mxd
foreach ( Sector s in sectors ) if ( s . Marked ) s . TranslateFromUDMF ( ) ; //mxd
2009-04-19 18:07:22 +00:00
foreach ( Thing t in things ) if ( t . Marked ) t . TranslateFromUDMF ( ) ;
}
2009-05-19 09:59:19 +00:00
/// <summary>This removes unused vertices.</summary>
2009-04-19 18:07:22 +00:00
public void RemoveUnusedVertices ( )
{
// Go for all vertices
2009-06-05 19:03:56 +00:00
int index = numvertices - 1 ;
while ( index > = 0 )
2009-04-19 18:07:22 +00:00
{
2009-06-05 19:03:56 +00:00
if ( ( vertices [ index ] ! = null ) & & ( vertices [ index ] . Linedefs . Count = = 0 ) )
vertices [ index ] . Dispose ( ) ;
else
index - - ;
2009-04-19 18:07:22 +00:00
}
}
2013-03-18 13:52:27 +00:00
2013-09-11 09:47:53 +00:00
//mxd
2014-07-16 09:45:04 +00:00
public void UpdateCustomLinedefColors ( )
{
foreach ( Linedef l in linedefs ) l . UpdateColorPreset ( ) ;
2013-09-11 09:47:53 +00:00
}
2009-04-19 18:07:22 +00:00
#endregion
}
}