2007-06-13 19:39:38 +00:00
2007-06-14 23:31:57 +00:00
#region = = = = = = = = = = = = = = = = = = Copyright ( c ) 2007 Pascal vd Heiden
2007-06-13 19:39:38 +00:00
/ *
* 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 .
*
* /
2007-06-14 23:31:57 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Namespaces
2007-06-13 19:39:38 +00:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Text ;
2008-12-31 14:08:40 +00:00
using CodeImp.DoomBuilder.Config ;
2007-06-14 12:37:46 +00:00
using CodeImp.DoomBuilder.Geometry ;
2007-06-24 18:56:43 +00:00
using CodeImp.DoomBuilder.Rendering ;
2008-05-11 00:42:34 +00:00
using SlimDX.Direct3D9 ;
2007-12-05 19:39:09 +00:00
using System.Drawing ;
2008-12-06 13:20:47 +00:00
using CodeImp.DoomBuilder.IO ;
2007-06-13 19:39:38 +00:00
2007-06-14 23:31:57 +00:00
#endregion
2007-06-13 19:39:38 +00:00
namespace CodeImp.DoomBuilder.Map
{
2008-10-16 08:45:23 +00:00
public sealed class Linedef : SelectableElement
2007-06-13 19:39:38 +00:00
{
#region = = = = = = = = = = = = = = = = = = Constants
2008-05-01 19:31:49 +00:00
public const float SIDE_POINT_DISTANCE = 0.001f ;
2007-12-26 00:31:32 +00:00
public const int NUM_ARGS = 5 ;
2007-06-24 18:56:43 +00:00
2007-06-13 19:39:38 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
2007-06-14 12:37:46 +00:00
// Map
2007-06-14 13:09:55 +00:00
private MapSet map ;
2007-06-14 12:37:46 +00:00
// List items
private LinkedListNode < Linedef > mainlistitem ;
private LinkedListNode < Linedef > startvertexlistitem ;
private LinkedListNode < Linedef > endvertexlistitem ;
2009-03-30 07:45:39 +00:00
private LinkedListNode < Linedef > selecteditem ;
2007-06-14 12:37:46 +00:00
2007-06-13 19:39:38 +00:00
// Vertices
private Vertex start ;
private Vertex end ;
// Sidedefs
private Sidedef front ;
private Sidedef back ;
2007-06-14 12:37:46 +00:00
// Cache
2007-07-07 09:40:34 +00:00
private bool updateneeded ;
2007-06-14 12:37:46 +00:00
private float lengthsq ;
2008-01-13 21:23:59 +00:00
private float lengthsqinv ;
2007-06-14 12:37:46 +00:00
private float length ;
2007-10-20 13:06:05 +00:00
private float lengthinv ;
2007-10-24 17:25:03 +00:00
private float angle ;
2008-05-05 22:01:27 +00:00
private RectangleF rect ;
2007-12-05 19:39:09 +00:00
2007-06-13 19:39:38 +00:00
// Properties
2008-05-31 19:31:45 +00:00
private Dictionary < string , bool > flags ;
2007-06-13 19:39:38 +00:00
private int action ;
2008-05-31 19:31:45 +00:00
private int activate ;
2007-06-13 19:39:38 +00:00
private int tag ;
2008-05-29 21:09:43 +00:00
private int [ ] args ;
2007-06-13 19:39:38 +00:00
2008-12-06 13:20:47 +00:00
// Clone
private int serializedindex ;
2007-06-13 19:39:38 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
2007-07-07 09:40:34 +00:00
public MapSet Map { get { return map ; } }
2007-06-14 13:09:55 +00:00
public Vertex Start { get { return start ; } }
public Vertex End { get { return end ; } }
2007-06-14 12:37:46 +00:00
public Sidedef Front { get { return front ; } }
public Sidedef Back { get { return back ; } }
2008-05-07 22:46:15 +00:00
public Line2D Line { get { return new Line2D ( start . Position , end . Position ) ; } }
2008-05-31 19:31:45 +00:00
public Dictionary < string , bool > Flags { get { return flags ; } }
2007-12-27 01:24:11 +00:00
public int Action { get { return action ; } set { action = value ; } }
2008-05-31 19:31:45 +00:00
public int Activate { get { return activate ; } set { activate = value ; } }
2009-04-08 19:11:40 +00:00
public int Tag { get { return tag ; } set { tag = value ; if ( ( tag < 0 ) | | ( tag > General . Map . FormatInterface . HighestTag ) ) throw new ArgumentOutOfRangeException ( "Tag" , "Invalid tag number" ) ; } }
2007-10-20 13:06:05 +00:00
public float LengthSq { get { return lengthsq ; } }
public float Length { get { return length ; } }
public float LengthInv { get { return lengthinv ; } }
2007-10-24 17:25:03 +00:00
public float Angle { get { return angle ; } }
public int AngleDeg { get { return ( int ) ( angle * Angle2D . PIDEG ) ; } }
2008-05-05 22:01:27 +00:00
public RectangleF Rect { get { return rect ; } }
2008-05-29 21:09:43 +00:00
public int [ ] Args { get { return args ; } }
2008-12-06 13:20:47 +00:00
internal int SerializedIndex { get { return serializedindex ; } set { serializedindex = value ; } }
2007-06-13 19:39:38 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor
2008-05-10 16:09:45 +00:00
internal Linedef ( MapSet map , LinkedListNode < Linedef > listitem , Vertex start , Vertex end )
2007-06-13 19:39:38 +00:00
{
// Initialize
2007-06-14 12:37:46 +00:00
this . map = map ;
this . mainlistitem = listitem ;
2007-06-13 19:39:38 +00:00
this . start = start ;
this . end = end ;
2007-07-07 09:40:34 +00:00
this . updateneeded = true ;
2008-05-29 21:09:43 +00:00
this . args = new int [ NUM_ARGS ] ;
2008-05-31 19:31:45 +00:00
this . flags = new Dictionary < string , bool > ( ) ;
2007-07-07 09:40:34 +00:00
2007-06-14 12:37:46 +00:00
// Attach to vertices
startvertexlistitem = start . AttachLinedef ( this ) ;
endvertexlistitem = end . AttachLinedef ( this ) ;
2007-06-13 19:39:38 +00:00
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
2008-12-06 13:20:47 +00:00
// Constructor
internal Linedef ( MapSet map , LinkedListNode < Linedef > listitem , Vertex start , Vertex end , IReadWriteStream stream )
{
// Initialize
this . map = map ;
this . mainlistitem = listitem ;
this . start = start ;
this . end = end ;
this . updateneeded = true ;
this . args = new int [ NUM_ARGS ] ;
// Attach to vertices
startvertexlistitem = start . AttachLinedef ( this ) ;
endvertexlistitem = end . AttachLinedef ( this ) ;
ReadWrite ( stream ) ;
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
2008-01-02 21:49:43 +00:00
// Disposer
2008-05-30 08:41:13 +00:00
public override void Dispose ( )
2007-06-13 19:39:38 +00:00
{
// Not already disposed?
if ( ! isdisposed )
{
2007-06-14 12:37:46 +00:00
// Already set isdisposed so that changes can be prohibited
isdisposed = true ;
// Remove from main list
mainlistitem . List . Remove ( mainlistitem ) ;
2007-06-13 19:39:38 +00:00
// Detach from vertices
2007-06-14 12:37:46 +00:00
start . DetachLinedef ( startvertexlistitem ) ;
end . DetachLinedef ( endvertexlistitem ) ;
2008-05-06 05:41:36 +00:00
startvertexlistitem = null ;
endvertexlistitem = null ;
2007-06-13 19:39:38 +00:00
// Dispose sidedefs
2007-06-15 22:38:42 +00:00
if ( front ! = null ) front . Dispose ( ) ;
if ( back ! = null ) back . Dispose ( ) ;
2007-06-13 19:39:38 +00:00
// Clean up
2007-06-14 12:37:46 +00:00
mainlistitem = null ;
2007-06-13 19:39:38 +00:00
start = null ;
end = null ;
front = null ;
back = null ;
2007-06-14 12:37:46 +00:00
map = null ;
2008-05-30 08:41:13 +00:00
// Clean up base
base . Dispose ( ) ;
2007-06-13 19:39:38 +00:00
}
}
#endregion
2007-06-14 12:37:46 +00:00
#region = = = = = = = = = = = = = = = = = = Management
2008-12-06 13:20:47 +00:00
// Serialize / deserialize
internal void ReadWrite ( IReadWriteStream s )
{
base . ReadWrite ( s ) ;
if ( s . IsWriting )
{
s . wInt ( flags . Count ) ;
foreach ( KeyValuePair < string , bool > f in flags )
{
s . wString ( f . Key ) ;
s . wBool ( f . Value ) ;
}
}
else
{
int c ; s . rInt ( out c ) ;
flags = new Dictionary < string , bool > ( c ) ;
for ( int i = 0 ; i < c ; i + + )
{
string t ; s . rString ( out t ) ;
bool b ; s . rBool ( out b ) ;
flags . Add ( t , b ) ;
}
}
s . rwInt ( ref action ) ;
s . rwInt ( ref activate ) ;
s . rwInt ( ref tag ) ;
for ( int i = 0 ; i < NUM_ARGS ; i + + ) s . rwInt ( ref args [ i ] ) ;
}
2009-03-10 17:22:22 +00:00
/// <summary>
/// Returns the index of this linedef. This is a O(n) operation.
/// </summary>
public int GetIndex ( )
{
return map . GetIndexForLinedef ( this ) ;
}
2007-12-01 01:32:56 +00:00
// This sets new start vertex
public void SetStartVertex ( Vertex v )
{
// Change start
2008-05-06 05:41:36 +00:00
if ( startvertexlistitem ! = null ) start . DetachLinedef ( startvertexlistitem ) ;
startvertexlistitem = null ;
2007-12-01 01:32:56 +00:00
start = v ;
startvertexlistitem = start . AttachLinedef ( this ) ;
this . updateneeded = true ;
}
// This sets new end vertex
public void SetEndVertex ( Vertex v )
{
// Change end
2008-05-06 05:41:36 +00:00
if ( endvertexlistitem ! = null ) end . DetachLinedef ( endvertexlistitem ) ;
endvertexlistitem = null ;
2007-12-01 01:32:56 +00:00
end = v ;
endvertexlistitem = end . AttachLinedef ( this ) ;
this . updateneeded = true ;
}
2008-10-16 08:45:23 +00:00
2007-11-12 22:43:01 +00:00
// This copies all properties to another line
2008-10-16 08:45:23 +00:00
new public void CopyPropertiesTo ( Linedef l )
2007-11-12 22:43:01 +00:00
{
// Copy properties
l . action = action ;
2008-05-29 21:09:43 +00:00
l . args = ( int [ ] ) args . Clone ( ) ;
2008-05-31 19:31:45 +00:00
l . flags = new Dictionary < string , bool > ( flags ) ;
2007-11-12 22:43:01 +00:00
l . tag = tag ;
l . updateneeded = true ;
2008-05-31 19:31:45 +00:00
l . activate = activate ;
2008-10-16 08:45:23 +00:00
base . CopyPropertiesTo ( l ) ;
2007-11-12 22:43:01 +00:00
}
2007-06-14 12:37:46 +00:00
// This attaches a sidedef on the front
2007-06-24 18:56:43 +00:00
public void AttachFront ( Sidedef s )
{
// No sidedef here yet?
if ( front = = null )
{
// Attach and recalculate
front = s ;
2007-07-07 09:40:34 +00:00
updateneeded = true ;
2007-06-24 18:56:43 +00:00
}
else throw new Exception ( "Linedef already has a front Sidedef." ) ;
}
2007-06-14 12:37:46 +00:00
// This attaches a sidedef on the back
2007-06-24 18:56:43 +00:00
public void AttachBack ( Sidedef s )
{
// No sidedef here yet?
if ( back = = null )
{
// Attach and recalculate
back = s ;
2007-07-07 09:40:34 +00:00
updateneeded = true ;
2007-06-24 18:56:43 +00:00
}
else throw new Exception ( "Linedef already has a back Sidedef." ) ;
}
2007-06-14 12:37:46 +00:00
// This detaches a sidedef from the front
2007-07-07 09:40:34 +00:00
public void DetachSidedef ( Sidedef s )
{
// Sidedef is on the front?
if ( front = = s )
{
// Remove sidedef reference
front = null ;
updateneeded = true ;
}
// Sidedef is on the back?
else if ( back = = s )
{
// Remove sidedef reference
back = null ;
updateneeded = true ;
}
else throw new Exception ( "Specified Sidedef is not attached to this Linedef." ) ;
}
2007-06-14 12:37:46 +00:00
2007-07-07 09:40:34 +00:00
// This updates the line when changes have been made
2008-01-13 21:23:59 +00:00
public void UpdateCache ( )
2007-06-14 12:37:46 +00:00
{
2007-07-07 09:40:34 +00:00
// Update if needed
if ( updateneeded )
{
// Delta vector
2008-11-30 02:17:19 +00:00
Vector2D delta = end . Position - start . Position ;
2007-07-07 09:40:34 +00:00
// Recalculate values
lengthsq = delta . GetLengthSq ( ) ;
length = ( float ) Math . Sqrt ( lengthsq ) ;
2007-10-20 13:06:05 +00:00
if ( length > 0f ) lengthinv = 1f / length ; else lengthinv = 1f / 0.0000000001f ;
2008-01-13 21:23:59 +00:00
if ( lengthsq > 0f ) lengthsqinv = 1f / lengthsq ; else lengthsqinv = 1f / 0.0000000001f ;
2007-10-24 17:25:03 +00:00
angle = delta . GetAngle ( ) ;
2008-11-30 02:17:19 +00:00
float l = Math . Min ( start . Position . x , end . Position . x ) ;
float t = Math . Min ( start . Position . y , end . Position . y ) ;
float r = Math . Max ( start . Position . x , end . Position . x ) ;
float b = Math . Max ( start . Position . y , end . Position . y ) ;
2008-05-05 22:01:27 +00:00
rect = new RectangleF ( l , t , r - l , b - t ) ;
2007-10-20 13:06:05 +00:00
2007-07-07 09:40:34 +00:00
// Updated
updateneeded = false ;
}
}
2008-01-13 21:23:59 +00:00
// This flags the line needs an update because it moved
2007-07-07 09:40:34 +00:00
public void NeedUpdate ( )
{
2008-01-13 21:23:59 +00:00
// Update this line
2007-07-07 09:40:34 +00:00
updateneeded = true ;
2008-01-13 21:23:59 +00:00
// Update sectors as well
if ( front ! = null ) front . Sector . UpdateNeeded = true ;
if ( back ! = null ) back . Sector . UpdateNeeded = true ;
2007-07-07 09:40:34 +00:00
}
2008-12-06 13:20:47 +00:00
2008-12-31 14:08:40 +00:00
// This translates the flags and activations into UDMF fields
internal void TranslateToUDMF ( )
{
// First make a single integer with all bits from activation and flags
int bits = activate ;
int flagbit = 0 ;
foreach ( KeyValuePair < string , bool > f in flags )
if ( int . TryParse ( f . Key , out flagbit ) & & f . Value ) bits | = flagbit ;
// Now make the new flags
flags . Clear ( ) ;
foreach ( FlagTranslation f in General . Map . Config . LinedefFlagsTranslation )
{
// Flag found in bits?
if ( ( bits & f . Flag ) = = f . Flag )
{
// Add fields and remove bits
bits & = ~ f . Flag ;
for ( int i = 0 ; i < f . Fields . Count ; i + + )
2009-01-24 15:54:50 +00:00
flags [ f . Fields [ i ] ] = f . FieldValues [ i ] ;
2008-12-31 14:08:40 +00:00
}
else
{
// Add fields with inverted value
for ( int i = 0 ; i < f . Fields . Count ; i + + )
2009-01-24 15:54:50 +00:00
flags [ f . Fields [ i ] ] = ! f . FieldValues [ i ] ;
2008-12-31 14:08:40 +00:00
}
}
}
// This translates UDMF fields back into the normal flags and activations
internal void TranslateFromUDMF ( )
{
// Make copy of the flags
Dictionary < string , bool > oldfields = new Dictionary < string , bool > ( flags ) ;
// Make the flags
flags . Clear ( ) ;
foreach ( KeyValuePair < string , string > f in General . Map . Config . LinedefFlags )
{
// Flag must be numeric
int flagbit = 0 ;
if ( int . TryParse ( f . Key , out flagbit ) )
{
foreach ( FlagTranslation ft in General . Map . Config . LinedefFlagsTranslation )
{
if ( ft . Flag = = flagbit )
{
// Only set this flag when the fields match
bool fieldsmatch = true ;
for ( int i = 0 ; i < ft . Fields . Count ; i + + )
{
if ( ! oldfields . ContainsKey ( ft . Fields [ i ] ) | | ( oldfields [ ft . Fields [ i ] ] ! = ft . FieldValues [ i ] ) )
{
fieldsmatch = false ;
break ;
}
}
// Field match? Then add the flag.
if ( fieldsmatch )
{
flags . Add ( f . Key , true ) ;
break ;
}
}
}
}
}
// Make the activation
foreach ( LinedefActivateInfo a in General . Map . Config . LinedefActivates )
{
bool foundactivation = false ;
foreach ( FlagTranslation ft in General . Map . Config . LinedefFlagsTranslation )
{
if ( ft . Flag = = a . Index )
{
// Only set this activation when the fields match
bool fieldsmatch = true ;
for ( int i = 0 ; i < ft . Fields . Count ; i + + )
{
if ( ! oldfields . ContainsKey ( ft . Fields [ i ] ) | | ( oldfields [ ft . Fields [ i ] ] ! = ft . FieldValues [ i ] ) )
{
fieldsmatch = false ;
break ;
}
}
// Field match? Then add the flag.
if ( fieldsmatch )
{
activate = a . Index ;
foundactivation = true ;
break ;
}
}
}
if ( foundactivation ) break ;
}
}
2009-03-30 07:45:39 +00:00
// Selected
protected override void DoSelect ( )
{
base . DoSelect ( ) ;
selecteditem = map . SelectedLinedefs . AddLast ( this ) ;
}
// Deselect
protected override void DoUnselect ( )
{
base . DoUnselect ( ) ;
if ( selecteditem . List ! = null ) selecteditem . List . Remove ( selecteditem ) ;
selecteditem = null ;
}
2008-12-31 14:08:40 +00:00
2007-06-14 12:37:46 +00:00
#endregion
2007-06-24 18:56:43 +00:00
#region = = = = = = = = = = = = = = = = = = Methods
2008-05-06 05:41:36 +00:00
2008-05-31 19:31:45 +00:00
// This checks and returns a flag without creating it
public bool IsFlagSet ( string flagname )
{
if ( flags . ContainsKey ( flagname ) )
return flags [ flagname ] ;
else
return false ;
}
2008-05-05 18:21:13 +00:00
// This flips the linedef's vertex attachments
public void FlipVertices ( )
{
2008-05-06 05:41:36 +00:00
// Flip vertices
2008-05-05 18:21:13 +00:00
Vertex v = start ;
start = end ;
end = v ;
2008-05-06 05:41:36 +00:00
// Flip tickets accordingly
LinkedListNode < Linedef > vn = startvertexlistitem ;
startvertexlistitem = endvertexlistitem ;
endvertexlistitem = vn ;
// Update required (angle changed)
2008-05-05 18:21:13 +00:00
NeedUpdate ( ) ;
2009-01-03 22:18:59 +00:00
General . Map . IsChanged = true ;
2008-05-05 18:21:13 +00:00
}
// This flips the sidedefs
public void FlipSidedefs ( )
{
2008-05-06 05:41:36 +00:00
// Flip sidedefs
2008-05-05 18:21:13 +00:00
Sidedef sd = front ;
front = back ;
back = sd ;
2009-01-03 22:18:59 +00:00
General . Map . IsChanged = true ;
2008-05-05 18:21:13 +00:00
}
2008-05-01 19:31:49 +00:00
// This returns a point for testing on one side
public Vector2D GetSidePoint ( bool front )
{
Vector2D n = new Vector2D ( ) ;
n . x = ( end . Position . x - start . Position . x ) * lengthinv * SIDE_POINT_DISTANCE ;
n . y = ( end . Position . y - start . Position . y ) * lengthinv * SIDE_POINT_DISTANCE ;
if ( front )
{
n . x = - n . x ;
n . y = - n . y ;
}
Vector2D p = new Vector2D ( ) ;
p . x = start . Position . x + ( end . Position . x - start . Position . x ) * 0.5f - n . y ;
p . y = start . Position . y + ( end . Position . y - start . Position . y ) * 0.5f + n . x ;
return p ;
}
2008-05-13 14:24:35 +00:00
// This returns a point in the middle of the line
public Vector2D GetCenterPoint ( )
{
return start . Position + ( end . Position - start . Position ) * 0.5f ;
}
2008-05-01 19:31:49 +00:00
2007-12-26 00:31:32 +00:00
// This applies single/double sided flags
public void ApplySidedFlags ( )
{
// Doublesided?
if ( ( front ! = null ) & & ( back ! = null ) )
{
// Apply or remove flags for doublesided line
2008-05-31 19:31:45 +00:00
flags [ General . Map . Config . SingleSidedFlag ] = false ;
flags [ General . Map . Config . DoubleSidedFlag ] = true ;
2007-12-26 00:31:32 +00:00
}
else
{
// Apply or remove flags for singlesided line
2008-05-31 19:31:45 +00:00
flags [ General . Map . Config . SingleSidedFlag ] = true ;
flags [ General . Map . Config . DoubleSidedFlag ] = false ;
2007-12-26 00:31:32 +00:00
}
2009-01-03 22:18:59 +00:00
General . Map . IsChanged = true ;
2007-12-26 00:31:32 +00:00
}
2007-06-14 12:37:46 +00:00
2008-04-29 14:41:16 +00:00
// This returns all points at which the line intersects with the grid
public List < Vector2D > GetGridIntersections ( )
{
List < Vector2D > coords = new List < Vector2D > ( ) ;
Vector2D v = new Vector2D ( ) ;
float gx , gy , minx , maxx , miny , maxy ;
bool reversex , reversey ;
if ( start . Position . x > end . Position . x )
{
minx = end . Position . x ;
maxx = start . Position . x ;
reversex = true ;
}
else
{
minx = start . Position . x ;
maxx = end . Position . x ;
reversex = false ;
}
if ( start . Position . y > end . Position . y )
{
miny = end . Position . y ;
maxy = start . Position . y ;
reversey = true ;
}
else
{
miny = start . Position . y ;
maxy = end . Position . y ;
reversey = false ;
}
// Go for all vertical grid lines in between line start and end
gx = General . Map . Grid . GetHigher ( minx ) ;
if ( gx < maxx )
{
for ( ; gx < maxx ; gx + = General . Map . Grid . GridSizeF )
{
// Add intersection point at this x coordinate
float u = ( gx - minx ) / ( maxx - minx ) ;
if ( reversex ) u = 1.0f - u ;
v . x = gx ;
v . y = start . Position . y + ( end . Position . y - start . Position . y ) * u ;
coords . Add ( v ) ;
}
}
// Go for all horizontal grid lines in between line start and end
gy = General . Map . Grid . GetHigher ( miny ) ;
if ( gy < maxy )
{
for ( ; gy < maxy ; gy + = General . Map . Grid . GridSizeF )
{
// Add intersection point at this y coordinate
float u = ( gy - miny ) / ( maxy - miny ) ;
if ( reversey ) u = 1.0f - u ;
v . x = start . Position . x + ( end . Position . x - start . Position . x ) * u ;
v . y = gy ;
coords . Add ( v ) ;
}
}
// Profit
return coords ;
}
2008-04-27 12:07:26 +00:00
// This returns the closest coordinates ON the line
public Vector2D NearestOnLine ( Vector2D pos )
{
float u = Line2D . GetNearestOnLine ( start . Position , end . Position , pos ) ;
if ( u < 0f ) u = 0f ; else if ( u > 1f ) u = 1f ;
return Line2D . GetCoordinatesAt ( start . Position , end . Position , u ) ;
}
2007-10-21 22:41:46 +00:00
// This returns the shortest distance from given coordinates to line
public float SafeDistanceToSq ( Vector2D p , bool bounded )
{
Vector2D v1 = start . Position ;
Vector2D v2 = end . Position ;
// Calculate intersection offset
2008-01-13 21:23:59 +00:00
float u = ( ( p . x - v1 . x ) * ( v2 . x - v1 . x ) + ( p . y - v1 . y ) * ( v2 . y - v1 . y ) ) * lengthsqinv ;
2007-10-21 22:41:46 +00:00
// Limit intersection offset to the line
if ( bounded ) if ( u < lengthinv ) u = lengthinv ; else if ( u > ( 1f - lengthinv ) ) u = 1f - lengthinv ;
// Calculate intersection point
Vector2D i = v1 + u * ( v2 - v1 ) ;
// Return distance between intersection and point
// which is the shortest distance to the line
float ldx = p . x - i . x ;
float ldy = p . y - i . y ;
return ldx * ldx + ldy * ldy ;
}
// This returns the shortest distance from given coordinates to line
public float SafeDistanceTo ( Vector2D p , bool bounded )
{
return ( float ) Math . Sqrt ( SafeDistanceToSq ( p , bounded ) ) ;
}
2007-06-14 12:37:46 +00:00
// This returns the shortest distance from given coordinates to line
public float DistanceToSq ( Vector2D p , bool bounded )
{
Vector2D v1 = start . Position ;
Vector2D v2 = end . Position ;
// Calculate intersection offset
2008-01-13 21:23:59 +00:00
float u = ( ( p . x - v1 . x ) * ( v2 . x - v1 . x ) + ( p . y - v1 . y ) * ( v2 . y - v1 . y ) ) * lengthsqinv ;
2007-06-14 12:37:46 +00:00
// Limit intersection offset to the line
if ( bounded ) if ( u < 0f ) u = 0f ; else if ( u > 1f ) u = 1f ;
// Calculate intersection point
Vector2D i = v1 + u * ( v2 - v1 ) ;
// Return distance between intersection and point
// which is the shortest distance to the line
float ldx = p . x - i . x ;
float ldy = p . y - i . y ;
return ldx * ldx + ldy * ldy ;
}
// This returns the shortest distance from given coordinates to line
public float DistanceTo ( Vector2D p , bool bounded )
{
return ( float ) Math . Sqrt ( DistanceToSq ( p , bounded ) ) ;
}
2007-06-13 19:39:38 +00:00
2007-06-14 12:37:46 +00:00
// This tests on which side of the line the given coordinates are
// returns < 0 for front (right) side, > 0 for back (left) side and 0 if on the line
public float SideOfLine ( Vector2D p )
{
Vector2D v1 = start . Position ;
Vector2D v2 = end . Position ;
// Calculate and return side information
return ( p . y - v1 . y ) * ( v2 . x - v1 . x ) - ( p . x - v1 . x ) * ( v2 . y - v1 . y ) ;
}
2007-12-01 01:32:56 +00:00
// This splits this line by vertex v
// Returns the new line resulting from the split
public Linedef Split ( Vertex v )
{
Linedef nl ;
Sidedef nsd ;
// Copy linedef and change vertices
nl = map . CreateLinedef ( v , end ) ;
CopyPropertiesTo ( nl ) ;
SetEndVertex ( v ) ;
2008-05-01 10:59:19 +00:00
nl . selected = this . selected ;
2008-05-01 14:10:38 +00:00
nl . marked = this . marked ;
2007-12-01 01:32:56 +00:00
// Copy front sidedef if exists
if ( front ! = null )
{
nsd = map . CreateSidedef ( nl , true , front . Sector ) ;
front . CopyPropertiesTo ( nsd ) ;
2008-05-01 19:31:49 +00:00
nsd . Marked = front . Marked ;
2009-03-15 16:10:38 +00:00
// Make texture offset adjustments
nsd . OffsetX + = ( int ) Vector2D . Distance ( this . start . Position , this . end . Position ) ;
2007-12-01 01:32:56 +00:00
}
// Copy back sidedef if exists
if ( back ! = null )
{
nsd = map . CreateSidedef ( nl , false , back . Sector ) ;
back . CopyPropertiesTo ( nsd ) ;
2008-05-01 19:31:49 +00:00
nsd . Marked = back . Marked ;
2009-03-15 16:10:38 +00:00
// Make texture offset adjustments
back . OffsetX + = ( int ) Vector2D . Distance ( nl . start . Position , nl . end . Position ) ;
2007-12-01 01:32:56 +00:00
}
// Return result
2009-01-03 22:18:59 +00:00
General . Map . IsChanged = true ;
2007-12-01 01:32:56 +00:00
return nl ;
}
2007-06-14 12:37:46 +00:00
2007-12-01 18:29:58 +00:00
// This joins the line with another line
// This line will be disposed
public void Join ( Linedef other )
{
2007-12-26 00:31:32 +00:00
Sector l1fs , l1bs , l2fs , l2bs ;
2008-05-08 12:04:20 +00:00
bool l1was2s , l2was2s ;
// Check which lines were 2 sided
l1was2s = ( ( other . Front ! = null ) & & ( other . Back ! = null ) ) ;
l2was2s = ( ( this . Front ! = null ) & & ( this . Back ! = null ) ) ;
2007-12-26 00:31:32 +00:00
// Get sector references
if ( other . front ! = null ) l1fs = other . front . Sector ; else l1fs = null ;
if ( other . back ! = null ) l1bs = other . back . Sector ; else l1bs = null ;
if ( this . front ! = null ) l2fs = this . front . Sector ; else l2fs = null ;
if ( this . back ! = null ) l2bs = this . back . Sector ; else l2bs = null ;
2008-05-08 12:04:20 +00:00
// This line has no sidedefs?
if ( ( l2fs = = null ) & & ( l2bs = = null ) )
2007-12-26 00:31:32 +00:00
{
2008-05-08 12:04:20 +00:00
// We have no sidedefs, so we have no influence
// Nothing to change on the other line
2007-12-26 00:31:32 +00:00
}
2008-05-08 12:04:20 +00:00
// Other line has no sidedefs?
else if ( ( l1fs = = null ) & & ( l1bs = = null ) )
2007-12-26 00:31:32 +00:00
{
2008-05-08 12:04:20 +00:00
// The other has no sidedefs, so it has no influence
// Copy my sidedefs to the other
if ( this . Start = = other . Start )
{
JoinChangeSidedefs ( other , true , front ) ;
JoinChangeSidedefs ( other , false , back ) ;
}
else
{
JoinChangeSidedefs ( other , false , front ) ;
JoinChangeSidedefs ( other , true , back ) ;
}
2009-03-15 16:10:38 +00:00
// Copy my properties to the other
this . CopyPropertiesTo ( other ) ;
2007-12-26 00:31:32 +00:00
}
else
{
2008-05-08 12:04:20 +00:00
// Compare front sectors
if ( l1fs = = l2fs )
2007-12-26 00:31:32 +00:00
{
2008-05-08 12:04:20 +00:00
// Copy textures
if ( other . front ! = null ) other . front . AddTexturesTo ( this . back ) ;
if ( this . front ! = null ) this . front . AddTexturesTo ( other . back ) ;
2007-12-26 00:31:32 +00:00
2008-05-08 12:04:20 +00:00
// Change sidedefs
JoinChangeSidedefs ( other , true , back ) ;
}
// Compare back sectors
else if ( l1bs = = l2bs )
{
// Copy textures
if ( other . back ! = null ) other . back . AddTexturesTo ( this . front ) ;
if ( this . back ! = null ) this . back . AddTexturesTo ( other . front ) ;
2007-12-26 00:31:32 +00:00
2008-05-08 12:04:20 +00:00
// Change sidedefs
JoinChangeSidedefs ( other , false , front ) ;
2007-12-26 00:31:32 +00:00
}
2008-05-08 12:04:20 +00:00
// Compare front and back
else if ( l1fs = = l2bs )
2007-12-26 00:31:32 +00:00
{
2008-05-08 12:04:20 +00:00
// Copy textures
if ( other . front ! = null ) other . front . AddTexturesTo ( this . front ) ;
if ( this . back ! = null ) this . back . AddTexturesTo ( other . back ) ;
2007-12-26 00:31:32 +00:00
2008-05-08 12:04:20 +00:00
// Change sidedefs
JoinChangeSidedefs ( other , true , front ) ;
}
// Compare back and front
else if ( l1bs = = l2fs )
{
// Copy textures
if ( other . back ! = null ) other . back . AddTexturesTo ( this . back ) ;
if ( this . front ! = null ) this . front . AddTexturesTo ( other . front ) ;
2007-12-26 00:31:32 +00:00
2008-05-08 12:04:20 +00:00
// Change sidedefs
JoinChangeSidedefs ( other , false , back ) ;
2007-12-26 00:31:32 +00:00
}
else
{
2008-05-08 12:04:20 +00:00
// Other line single sided?
if ( other . back = = null )
2007-12-26 00:31:32 +00:00
{
2008-05-08 12:04:20 +00:00
// This line with its back to the other?
if ( this . start = = other . end )
{
// Copy textures
if ( other . back ! = null ) other . back . AddTexturesTo ( this . front ) ;
if ( this . back ! = null ) this . back . AddTexturesTo ( other . front ) ;
// Change sidedefs
JoinChangeSidedefs ( other , false , front ) ;
}
else
{
// Copy textures
if ( other . back ! = null ) other . back . AddTexturesTo ( this . back ) ;
if ( this . front ! = null ) this . front . AddTexturesTo ( other . front ) ;
// Change sidedefs
JoinChangeSidedefs ( other , false , back ) ;
}
}
// This line single sided?
if ( this . back = = null )
{
// Other line with its back to this?
if ( other . start = = this . end )
{
// Copy textures
if ( other . back ! = null ) other . back . AddTexturesTo ( this . front ) ;
if ( this . back ! = null ) this . back . AddTexturesTo ( other . front ) ;
// Change sidedefs
JoinChangeSidedefs ( other , false , front ) ;
}
else
{
// Copy textures
if ( other . front ! = null ) other . front . AddTexturesTo ( this . front ) ;
if ( this . back ! = null ) this . back . AddTexturesTo ( other . back ) ;
// Change sidedefs
JoinChangeSidedefs ( other , true , front ) ;
}
2007-12-26 00:31:32 +00:00
}
else
{
2008-05-08 12:04:20 +00:00
// This line with its back to the other?
if ( this . start = = other . end )
{
// Copy textures
if ( other . back ! = null ) other . back . AddTexturesTo ( this . front ) ;
if ( this . back ! = null ) this . back . AddTexturesTo ( other . front ) ;
// Change sidedefs
JoinChangeSidedefs ( other , false , front ) ;
}
else
{
// Copy textures
if ( other . back ! = null ) other . back . AddTexturesTo ( this . back ) ;
if ( this . front ! = null ) this . front . AddTexturesTo ( other . front ) ;
// Change sidedefs
JoinChangeSidedefs ( other , false , back ) ;
}
2007-12-26 00:31:32 +00:00
}
}
2009-03-15 16:10:38 +00:00
// Apply single/double sided flags if the double-sided-ness changed
if ( ( ! l1was2s & & ( ( other . Front ! = null ) & & ( other . Back ! = null ) ) ) | |
( l1was2s & & ( ( other . Front = = null ) | | ( other . Back = = null ) ) ) )
other . ApplySidedFlags ( ) ;
// Remove unneeded textures
if ( other . front ! = null ) other . front . RemoveUnneededTextures ( ! ( l1was2s & & l2was2s ) ) ;
if ( other . back ! = null ) other . back . RemoveUnneededTextures ( ! ( l1was2s & & l2was2s ) ) ;
2007-12-26 00:31:32 +00:00
}
2008-05-08 12:04:20 +00:00
2007-12-26 00:31:32 +00:00
// If either of the two lines was selected, keep the other selected
if ( this . selected ) other . selected = true ;
2008-05-01 14:10:38 +00:00
if ( this . marked ) other . marked = true ;
2007-12-26 00:31:32 +00:00
// I got killed by the other.
2007-12-04 19:22:14 +00:00
this . Dispose ( ) ;
2009-01-03 22:18:59 +00:00
General . Map . IsChanged = true ;
2007-12-01 18:29:58 +00:00
}
2007-12-26 00:31:32 +00:00
// This changes sidedefs (used for joining lines)
private void JoinChangeSidedefs ( Linedef other , bool front , Sidedef newside )
{
Sidedef sd ;
// Change sidedefs
if ( front )
{
if ( other . front ! = null ) other . front . Dispose ( ) ;
}
else
{
if ( other . back ! = null ) other . back . Dispose ( ) ;
}
if ( newside ! = null )
{
sd = map . CreateSidedef ( other , front , newside . Sector ) ;
newside . CopyPropertiesTo ( sd ) ;
2008-05-01 19:31:49 +00:00
sd . Marked = newside . Marked ;
2007-12-26 00:31:32 +00:00
}
}
2009-03-11 20:21:50 +00:00
// String representation
public override string ToString ( )
{
return "Linedef " + GetIndex ( ) ;
}
2007-12-26 00:31:32 +00:00
2007-06-13 19:39:38 +00:00
#endregion
2007-06-24 18:56:43 +00:00
#region = = = = = = = = = = = = = = = = = = Changes
// This updates all properties
2008-05-31 19:31:45 +00:00
public void Update ( Dictionary < string , bool > flags , int activate , int tag , int action , int [ ] args )
2007-06-24 18:56:43 +00:00
{
// Apply changes
2008-05-31 19:31:45 +00:00
this . flags = new Dictionary < string , bool > ( flags ) ;
2007-06-24 18:56:43 +00:00
this . tag = tag ;
2008-05-31 19:31:45 +00:00
this . activate = activate ;
2007-06-24 18:56:43 +00:00
this . action = action ;
2008-05-29 21:09:43 +00:00
this . args = new int [ NUM_ARGS ] ;
2007-12-29 15:50:16 +00:00
args . CopyTo ( this . args , 0 ) ;
2007-07-07 09:40:34 +00:00
this . updateneeded = true ;
2007-06-24 18:56:43 +00:00
}
#endregion
2007-06-13 19:39:38 +00:00
}
}