even better copy/pasting

This commit is contained in:
codeimp 2008-09-23 17:46:34 +00:00
parent 5a47093999
commit 8d3d00372b
8 changed files with 250 additions and 88 deletions

View file

@ -83,7 +83,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// This occurs when the user presses Copy. All selected geometry must be marked for copying!
public override bool OnCopyBegin()
{
General.Map.Map.MarkAllSelectedGeometry(true);
General.Map.Map.MarkAllSelectedGeometry(true, false);
// Return true when anything is selected so that the copy continues
// We only have to check vertices for the geometry, because without selected

View file

@ -685,6 +685,10 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Update
UpdateRectangleComponents();
Update();
// When pasting and mouse is in screen, drag selection immediately
if(pasting && mouseinside) OnSelect();
}
else
{
@ -736,11 +740,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Make undo
General.Map.UndoRedo.CreateUndo("Edit selection", UndoGroup.None, 0);
// Mark the selected geometry
// Mark selected geometry
General.Map.Map.ClearAllMarks(false);
General.Map.Map.MarkAllSelectedGeometry(true);
General.Map.Map.MarkAllSelectedGeometry(true, false);
// Move geometry to new position
UpdateGeometry();
General.Map.Map.Update(true, true);
@ -752,21 +756,24 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Map.Map.SnapAllToAccuracy();
// When pasting, we want to join with the parent sector
// where the sidedefs are referencing a virtual sector
if(pasting)
{
General.Settings.FindDefaultDrawSettings();
// Go for all linedefs in the new geometry
ICollection<Linedef> newlines = General.Map.Map.GetMarkedLinedefs(true);
foreach(Linedef l in newlines)
List<Sidedef> newsides = General.Map.Map.GetMarkedSidedefs(true);
for(int i = 0; i < newsides.Count; i++)
{
// Missing sector on the front?
if((l.Front == null) && l.Marked)
Sidedef s = newsides[i];
// Connected to a virtual sector?
if(s.Marked && s.Sector.Fields.ContainsKey(MapSet.VirtualSectorField))
{
bool joined = false;
// Find a way to join a sector here
List<LinedefSide> sectorlines = SectorTools.FindPotentialSectorAt(l, true);
List<LinedefSide> sectorlines = SectorTools.FindPotentialSectorAt(s.Line, s.IsFront);
if(sectorlines != null)
{
// We don't always want to join a sector
@ -774,12 +781,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
Sidedef joinsidedef = null;
foreach(LinedefSide ls in sectorlines)
{
if(ls.Front && (ls.Line.Front != null))
if(ls.Front && (ls.Line.Front != null) && !ls.Line.Front.Marked)
{
joinsidedef = ls.Line.Front;
break;
}
else if(!ls.Front && (ls.Line.Back != null))
else if(!ls.Front && (ls.Line.Back != null) && !ls.Line.Back.Marked)
{
joinsidedef = ls.Line.Back;
break;
@ -793,63 +800,34 @@ namespace CodeImp.DoomBuilder.BuilderModes
Sector newsector = SectorTools.JoinSector(sectorlines, joinsidedef);
joined = true;
// Remove mark from linedefs so that it is not used to join a sector again
foreach(Sidedef sd in newsector.Sidedefs) sd.Line.Marked = false;
// Remove mark from sidedefs so that it is not used to join a sector again
foreach(Sidedef sd in newsector.Sidedefs) sd.Marked = false;
}
}
// Not joined any sector?
if(!joined)
{
// Flip the linedef and correct the sided flags
l.FlipVertices();
l.FlipSidedefs();
Linedef l = s.Line;
// Remove the sidedef
s.Dispose();
// Correct the linedef
if((l.Front == null) && (l.Back != null))
{
l.FlipVertices();
l.FlipSidedefs();
}
// Correct the sided flags
l.ApplySidedFlags();
}
}
// Missing sector on the back?
if((l.Back == null) && l.Marked && l.IsFlagSet(General.Map.Config.DoubleSidedFlag))
{
bool joined = false;
// Find a way to join a sector here
List<LinedefSide> sectorlines = SectorTools.FindPotentialSectorAt(l, false);
if(sectorlines != null)
{
// We don't always want to join a sector
// So first check if any of the surrounding lines originally have sidedefs
Sidedef joinsidedef = null;
foreach(LinedefSide ls in sectorlines)
{
if(ls.Front && (ls.Line.Front != null))
{
joinsidedef = ls.Line.Front;
break;
}
else if(!ls.Front && (ls.Line.Back != null))
{
joinsidedef = ls.Line.Back;
break;
}
}
// Join?
if(joinsidedef != null)
{
// Join the new sector
Sector newsector = SectorTools.JoinSector(sectorlines, joinsidedef);
joined = true;
// Remove mark from linedefs so that it is not used to join a sector again
foreach(Sidedef sd in newsector.Sidedefs) sd.Line.Marked = false;
}
}
// Correct the sided flags if not joined a sector
if(!joined) l.ApplySidedFlags();
}
}
// Remove any virtual sectors
General.Map.Map.RemoveVirtualSectors();
}
// Update cached values

View file

@ -99,18 +99,15 @@ namespace CodeImp.DoomBuilder.Editing
// that need to be copied.
if(General.Map.Mode.OnCopyBegin())
{
// Get all marked elements
ICollection<Vertex> verts = General.Map.Map.GetMarkedVertices(true);
ICollection<Sidedef> sides = General.Map.Map.GetMarkedSidedefs(true);
ICollection<Sector> sectors = General.Map.Map.GetMarkedSectors(true);
ICollection<Linedef> lines = General.Map.Map.GetMarkedLinedefs(true);
ICollection<Thing> things = General.Map.Map.GetMarkedThings(true);
// Copy the marked geometry
// This links sidedefs that are not linked to a marked sector to a virtual sector
MapSet copyset = General.Map.Map.CloneMarked();
// Write data to stream
MemoryStream memstream = new MemoryStream();
UniversalStreamWriter writer = new UniversalStreamWriter();
writer.RememberCustomTypes = false;
writer.Write(verts, lines, sides, sectors, things, memstream, null);
writer.Write(copyset, memstream, null);
// Set on clipboard
Clipboard.SetData(CLIPBOARD_DATA_FORMAT, memstream);
@ -151,6 +148,7 @@ namespace CodeImp.DoomBuilder.Editing
// Read data stream
UniversalStreamReader reader = new UniversalStreamReader();
reader.StrictChecking = false;
reader.Read(General.Map.Map, memstream);
// The new geometry is not marked, so invert the marks to get it marked

View file

@ -503,21 +503,33 @@ namespace CodeImp.DoomBuilder.Geometry
{
if(ls.Front)
{
// Create sidedef is needed and ensure it points to the new sector
// Create sidedef if needed
if(ls.Line.Front == null)
{
General.Map.Map.CreateSidedef(ls.Line, true, original.Sector);
ApplyDefaultsToSidedef(ls.Line.Front, sourceside);
}
// Added 23-9-08, can we do this or will it break things?
else
{
// Link to the new sector
ls.Line.Front.ChangeSector(original.Sector);
}
}
else
{
// Create sidedef is needed and ensure it points to the new sector
// Create sidedef if needed
if(ls.Line.Back == null)
{
General.Map.Map.CreateSidedef(ls.Line, false, original.Sector);
ApplyDefaultsToSidedef(ls.Line.Back, sourceside);
}
// Added 23-9-08, can we do this or will it break things?
else
{
// Link to the new sector
ls.Line.Back.ChangeSector(original.Sector);
}
}
// Update line

View file

@ -65,6 +65,9 @@ namespace CodeImp.DoomBuilder.IO
// Configuration root
private UniversalCollection root = null;
// Settings
private bool strictchecking = true;
#endregion
@ -75,6 +78,7 @@ namespace CodeImp.DoomBuilder.IO
public string ErrorDescription { get { return cpErrorDescription; } }
public int ErrorLine { get { return cpErrorLine; } }
public UniversalCollection Root { get { return root; } }
public bool StrictChecking { get { return strictchecking; } set { strictchecking = value; } }
#endregion
@ -144,15 +148,19 @@ namespace CodeImp.DoomBuilder.IO
}
else
{
// Check if all characters are valid
foreach(char c in key)
// Only when strict checking
if(strictchecking)
{
if(KEY_CHARACTERS.IndexOf(c) == -1)
// Check if all characters are valid
foreach(char c in key)
{
// ERROR: Invalid characters in key name
if(errorline > -1) RaiseError(errorline, ERROR_KEYCHARACTERS);
validateresult = false;
break;
if(KEY_CHARACTERS.IndexOf(c) == -1)
{
// ERROR: Invalid characters in key name
if(errorline > -1) RaiseError(errorline, ERROR_KEYCHARACTERS);
validateresult = false;
break;
}
}
}
}

View file

@ -45,12 +45,14 @@ namespace CodeImp.DoomBuilder.IO
private Configuration config;
private bool setknowncustomtypes;
private bool strictchecking = true;
#endregion
#region ================== Properties
public bool SetKnownCustomTypes { get { return setknowncustomtypes; } set { setknowncustomtypes = value; } }
public bool StrictChecking { get { return strictchecking; } set { strictchecking = value; } }
#endregion
@ -111,7 +113,8 @@ namespace CodeImp.DoomBuilder.IO
Dictionary<int, Vertex> vertexlink;
Dictionary<int, Sector> sectorlink;
UniversalParser textmap = new UniversalParser();
textmap.StrictChecking = strictchecking;
try
{
// Read UDMF from stream

View file

@ -28,6 +28,7 @@ using SlimDX;
using System.Drawing;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Types;
#endregion
@ -43,6 +44,12 @@ namespace CodeImp.DoomBuilder.Map
// Stiching distance
public const float STITCH_DISTANCE = 0.001f;
// 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.
internal const string VIRTUAL_SECTOR_FIELD = "!virtual_sector";
#endregion
#region ================== Variables
@ -58,8 +65,9 @@ namespace CodeImp.DoomBuilder.Map
private LinkedList<Sector> sectors;
private LinkedList<Thing> things;
// Optimization
// Statics
private static long emptylongname;
private static UniValue virtualsectorvalue;
// Disposing
private bool isdisposed = false;
@ -74,7 +82,10 @@ namespace CodeImp.DoomBuilder.Map
public ICollection<Sector> Sectors { get { return sectors; } }
public ICollection<Thing> Things { get { return things; } }
public bool IsDisposed { get { return isdisposed; } }
public static long EmptyLongName { get { return emptylongname; } }
public static string VirtualSectorField { get { return VIRTUAL_SECTOR_FIELD; } }
public static UniValue VirtualSectorValue { get { return virtualsectorvalue; } }
#endregion
@ -144,6 +155,7 @@ namespace CodeImp.DoomBuilder.Map
internal static void Initialize()
{
emptylongname = Lump.MakeLongName("-");
virtualsectorvalue = new UniValue((int)UniversalType.Integer, (int)0);
}
#endregion
@ -214,6 +226,116 @@ namespace CodeImp.DoomBuilder.Map
// Return the new set
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();
// Get marked geometry
ICollection<Vertex> mvertices = GetMarkedVertices(true);
ICollection<Linedef> mlinedefs = GetMarkedLinedefs(true);
ICollection<Sector> msectors = GetMarkedSectors(true);
ICollection<Thing> mthings = GetMarkedThings(true);
// 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);
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);
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
return newset;
}
// This creates a new vertex
public Vertex CreateVertex(Vector2D pos)
@ -713,9 +835,12 @@ namespace CodeImp.DoomBuilder.Map
return list;
}
// This marks all selected geometry, including sidedefs from sectors
// Returns the number of selected elements
public void MarkAllSelectedGeometry(bool mark)
/// <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 sidedefsfromsectors)
{
General.Map.Map.ClearAllMarks(!mark);
@ -725,14 +850,15 @@ namespace CodeImp.DoomBuilder.Map
// 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
ICollection<Linedef> lines = General.Map.Map.LinedefsFromMarkedVertices(!mark, mark, !mark);
foreach(Linedef l in lines) l.Marked = mark;
// Vertices from linedefs
ICollection<Vertex> verts = General.Map.Map.GetVerticesFromLinesMarks(mark);
foreach(Vertex v in verts) v.Marked = mark;
// Linedefs from vertices
ICollection<Linedef> lines = General.Map.Map.LinedefsFromMarkedVertices(!mark, mark, !mark);
foreach(Linedef l in lines) l.Marked = mark;
// Mark sectors from linedefs (note: this must be the first to mark
// sectors, because this clears the sector marks!)
General.Map.Map.ClearMarkedSectors(mark);
@ -751,9 +877,11 @@ namespace CodeImp.DoomBuilder.Map
// Direct things
General.Map.Map.MarkSelectedThings(true, mark);
// Sidedefs from linedefs
//General.Map.Map.MarkSidedefsFromLinedefs(true, mark);
General.Map.Map.MarkSidedefsFromSectors(true, mark);
// Sidedefs from linedefs or sectors
if(sidedefsfromsectors)
General.Map.Map.MarkSidedefsFromSectors(true, mark);
else
General.Map.Map.MarkSidedefsFromLinedefs(true, mark);
}
#endregion
@ -922,6 +1050,31 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Geometry Tools
// This removes any virtual sectors in the map
// Returns the number of sectors removed
public int RemoveVirtualSectors()
{
int count = 0;
LinkedListNode<Sector> n = sectors.First;
// Go for all sectors
while(n != null)
{
LinkedListNode<Sector> nn = n.Next;
// Remove when virtual
if(n.Value.Fields.ContainsKey(VIRTUAL_SECTOR_FIELD))
{
n.Value.Dispose();
count++;
}
n = nn;
}
return count;
}
// This joins overlapping lines together
// Returns the number of joins made
public static int JoinOverlappingLines(ICollection<Linedef> lines)

View file

@ -81,6 +81,16 @@ namespace CodeImp.DoomBuilder.Map
GC.SuppressFinalize(this);
}
// Constructor
public UniValue(UniValue v)
{
this.type = v.type;
this.value = v.value;
// We have no destructor
GC.SuppressFinalize(this);
}
// Constructor
public UniValue()
{