Added 3 map geometry dragging modes (used when applying Drag Vertices/Linedefs/Sectors modes and Edit Selection mode):

- "Merge Dragged Vertices Only". Only vertex-line intersections will be processed (DB2 mode).
- "Merge Dragged Geometry". Geometry merging will be performed.
- "Replace with Dragged Geometry". Dragged geometry will replace underlaying geometry.
You can switch between these using 3 new actions, top toolbar buttons and Edit menu buttons.
Changed: activating the same 2D mode repeatedly now toggles View modes.
Renamed "Merge Geometry" action/menu item to "Snap to Geometry".
More fixes to vertex/linedef/sector dragging logic.
Updated ZDoom_DECORATE.cfg.
This commit is contained in:
MaxED 2016-06-02 09:55:01 +00:00
parent 528719622b
commit 3bef7dbf72
21 changed files with 546 additions and 131 deletions

View file

@ -82,21 +82,21 @@ keywords
A_ClearLastHeard = "A_ClearLastHeard";
A_ClearSoundTarget = "A_ClearSoundTarget";
A_ClearTarget = "A_ClearTarget";
A_DamageChildren = "A_DamageChildren(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageMaster = "A_DamageMaster(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageSelf = "A_DamageSelf(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageSiblings = "A_DamageSiblings(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageTarget = "A_DamageTarget(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageTracer = "A_DamageTracer(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageChildren = "A_DamageChildren(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageMaster = "A_DamageMaster(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageSelf = "A_DamageSelf(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageSiblings = "A_DamageSiblings(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageTarget = "A_DamageTarget(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_DamageTracer = "A_DamageTracer(int amount[, str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]]])\namount: amount of damage to inflict. Use a negative value to heal.\ndamagetype: the type of damage to inflict.\nflags: DMSS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_Die = "A_Die[(str damagetype = \"none\")]";
A_FaceTarget = "A_FaceTarget[(float angle = 0.0[, float pitch = 270.0])]\nA_FaceTarget([float max_turn = 0.0[, float max_pitch = 270.0[, float ang_offset = 0.0[, float pitch_offset = 0.0[, int flags = 0[, float z_add = 0.0]]]]]])";
A_FaceMaster = "A_FaceMaster[(float angle = 0.0[, float pitch = 270.0])]\nA_FaceMaster([float max_turn = 0.0[, float max_pitch = 270.0[, float ang_offset = 0.0[, float pitch_offset = 0.0[, int flags = 0[, float z_add = 0.0]]]]]])";
A_FastChase = "A_FastChase";
A_KillChildren = "A_KillChildren[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\[, str species = \"None\]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillMaster = "A_KillMaster[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillSiblings = "A_KillSiblings[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillTarget = "A_KillTarget[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillTracer = "A_KillTracer[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillChildren = "A_KillChildren[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillMaster = "A_KillMaster[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillSiblings = "A_KillSiblings[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillTarget = "A_KillTarget[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_KillTracer = "A_KillTracer[(str damagetype = \"None\"[, int flags = 0[, str filter = \"None\"[, str species = \"None\"[, int src = AAPTR_DEFAULT[, int inflictor = AAPTR_DEFAULT]]]]])]\ndamagetype: if the actor dies, the actor will enter a death state based on damagetype if present (or pain state if using NODAMAGE).\nflags: KILS flags.\nfilter: the actor class to damage.\nspecies: the actor species to damage.";
A_Look = "A_Look";
A_Look2 = "A_Look2";
A_LookEx = "A_LookEx(int flags, float minseedist, float maxseedist, float maxheardist, float fov, state seestate)";
@ -433,6 +433,7 @@ keywords
GetSpawnHealth = "int GetSpawnHealth()";
GetZAt = "float GetZAt([float x = 0.0[, float y = 0.0[, float angle = 0.0[, int flags = 0[, int pick_pointer = AAPTR_TARGET]]]]])";
GetGibHealth = "int GetGibHealth()";
GetCrouchFactor = "float GetCrouchFactor(int ptr = AAPTR_PLAYER1)";
}
properties
@ -744,7 +745,14 @@ constants
AAPTR_TARGET;
AAPTR_MASTER;
AAPTR_TRACER;
AAPTR_PLAYER;
AAPTR_PLAYER1;
AAPTR_PLAYER2;
AAPTR_PLAYER3;
AAPTR_PLAYER4;
AAPTR_PLAYER5;
AAPTR_PLAYER6;
AAPTR_PLAYER7;
AAPTR_PLAYER8;
AAPTR_PLAYER_GETTARGET;
AAPTR_PLAYER_GETCONVERSATION;
//A_SpawnItemEx flags

View file

@ -888,6 +888,7 @@
<Compile Include="GZBuilder\Data\SharpCompressHelper.cs" />
<Compile Include="GZBuilder\Data\SkyboxInfo.cs" />
<Compile Include="GZBuilder\Data\ThingCopyData.cs" />
<Compile Include="Map\MergeGeometryMode.cs" />
<Compile Include="Map\SectorBuilder.cs" />
<Compile Include="Rendering\VisualVertexHandle.cs" />
<Compile Include="Geometry\Line3D.cs" />
@ -1168,6 +1169,9 @@
<None Include="Resources\MixedThings.png" />
<None Include="Resources\LinedefColorPresets.png" />
<EmbeddedResource Include="Resources\MissingSky3D.png" />
<None Include="Resources\MergeGeo.png" />
<None Include="Resources\MergeGeoClassic.png" />
<None Include="Resources\MergeGeoRemoveLines.png" />
<Content Include="Resources\Model.png" />
<None Include="Resources\ModelDisabled.png" />
<None Include="Resources\ModelFiltered.png" />

View file

@ -91,6 +91,7 @@ namespace CodeImp.DoomBuilder.Config
private bool locatetexturegroup; //mxd
private bool keeptexturefilterfocused; //mxd
private SplitLineBehavior splitlinebehavior; //mxd
private MergeGeometryMode mergegeomode; //mxd
private bool usehighlight; //mxd
//mxd. Script editor settings
@ -204,6 +205,7 @@ namespace CodeImp.DoomBuilder.Config
public bool LocateTextureGroup { get { return locatetexturegroup; } internal set { locatetexturegroup = value; } } //mxd
public bool KeepTextureFilterFocused { get { return keeptexturefilterfocused; } internal set { keeptexturefilterfocused = value; } } //mxd
public SplitLineBehavior SplitLineBehavior { get { return splitlinebehavior; } set { splitlinebehavior = value; } } //mxd
public MergeGeometryMode MergeGeometryMode { get { return mergegeomode; } internal set { mergegeomode = value; } } //mxd
//mxd. Highlight mode
public bool UseHighlight
@ -349,7 +351,8 @@ namespace CodeImp.DoomBuilder.Config
showtexturesizes = cfg.ReadSetting("showtexturesizes", true);
locatetexturegroup = cfg.ReadSetting("locatetexturegroup", true); //mxd
keeptexturefilterfocused = cfg.ReadSetting("keeptexturefilterfocused", true); //mxd
splitlinebehavior = (SplitLineBehavior)General.Clamp(cfg.ReadSetting("splitlinebehavior", 0), 0, 3); //mxd
splitlinebehavior = (SplitLineBehavior)General.Clamp(cfg.ReadSetting("splitlinebehavior", 0), 0, Enum.GetValues(typeof(SplitLineBehavior)).Length - 1); //mxd
mergegeomode = (MergeGeometryMode)General.Clamp(cfg.ReadSetting("mergegeometrymode", (int)MergeGeometryMode.REPLACE), 0, Enum.GetValues(typeof(MergeGeometryMode)).Length - 1); //mxd
usehighlight = cfg.ReadSetting("usehighlight", true); //mxd
//mxd. Script editor
@ -466,6 +469,7 @@ namespace CodeImp.DoomBuilder.Config
cfg.WriteSetting("locatetexturegroup", locatetexturegroup); //mxd
cfg.WriteSetting("keeptexturefilterfocused", keeptexturefilterfocused); //mxd
cfg.WriteSetting("splitlinebehavior", (int)splitlinebehavior); //mxd
cfg.WriteSetting("mergegeometrymode", (int)mergegeomode); //mxd
cfg.WriteSetting("usehighlight", usehighlight); //mxd
//mxd. Script editor

View file

@ -173,7 +173,10 @@ namespace CodeImp.DoomBuilder
counter += incrementby;
}
public static void ResetCounter() { ResetCounter(string.Empty); }
public static void ResetCounter(string message)
{
if(!string.IsNullOrEmpty(message))
{
if(message.Contains("%"))
message = message.Replace("%", counter.ToString());
@ -181,6 +184,7 @@ namespace CodeImp.DoomBuilder
message = message.TrimEnd() + ": " + counter;
WriteLine(DebugMessageType.SPECIAL, message);
}
counter = 0;
}

View file

@ -633,7 +633,7 @@ namespace CodeImp.DoomBuilder.Editing
}
// This sets the view mode
private static void SetViewMode(ViewMode mode)
internal static void SetViewMode(ViewMode mode)
{
General.Map.CRenderer2D.SetViewMode(mode);
General.MainWindow.UpdateInterface();

View file

@ -17,10 +17,13 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.Plugins;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.VisualModes;
#endregion
@ -160,6 +163,15 @@ namespace CodeImp.DoomBuilder.Editing
// Switch back to last classic mode
General.Editing.ChangeMode(General.Editing.PreviousClassicMode.Name);
}
//mxd. The same mode? Switch view modes instead
else if(General.Editing.Mode is ClassicMode && General.Editing.Mode.GetType().FullName == type.FullName)
{
List<ViewMode> vmodes = new List<ViewMode>(Enum.GetValues(typeof(ViewMode)).Cast<ViewMode>());
int curmode = vmodes.IndexOf(General.Map.Renderer2D.ViewMode);
curmode = (curmode == vmodes.Count - 1 ? 0 : ++curmode);
ClassicMode.SetViewMode(vmodes[curmode]);
}
else
{
// Create instance

View file

@ -1260,8 +1260,8 @@ namespace CodeImp.DoomBuilder.Geometry
// self intersections for which splits were made above.
map.Update(true, false);
map.BeginAddRemove();
MapSet.SplitLinesByVertices(newlines, intersectverts, MapSet.STITCH_DISTANCE, null, false);
MapSet.SplitLinesByVertices(newlines, mergeverts, MapSet.STITCH_DISTANCE, null, false);
MapSet.SplitLinesByVertices(newlines, intersectverts, MapSet.STITCH_DISTANCE, null);
MapSet.SplitLinesByVertices(newlines, mergeverts, MapSet.STITCH_DISTANCE, null);
map.EndAddRemove();
/***************************************************\
@ -1328,7 +1328,7 @@ namespace CodeImp.DoomBuilder.Geometry
// Before this point, the new geometry is not linked with the existing geometry.
// Now perform standard geometry stitching to merge the new geometry with the rest
// of the map. The marked vertices indicate the new geometry.
map.StitchGeometry(false);
map.StitchGeometry();
map.Update(true, false);
// Find our new lines again, because they have been merged with the other geometry

View file

@ -1988,9 +1988,9 @@ namespace CodeImp.DoomBuilder.Map
}
/// <summary>This filters lines by a rectangular area.</summary>
public static List<Linedef> FilterByArea(ICollection<Linedef> lines, ref RectangleF area)
public static HashSet<Linedef> FilterByArea(ICollection<Linedef> lines, ref RectangleF area)
{
List<Linedef> newlines = new List<Linedef>(lines.Count);
HashSet<Linedef> newlines = new HashSet<Linedef>();
// Go for all lines
foreach(Linedef l in lines)
@ -2045,18 +2045,18 @@ namespace CodeImp.DoomBuilder.Map
/// <summary>
/// Stitches marked geometry with non-marked geometry. Returns false when the operation failed.
/// </summary>
public bool StitchGeometry() { return StitchGeometry(false); } //mxd. Compatibility
public bool StitchGeometry(bool correctsectorrefs)
public bool StitchGeometry() { return StitchGeometry(MergeGeometryMode.CLASSIC); } //mxd. Compatibility
public bool StitchGeometry(MergeGeometryMode mergemode)
{
// Find vertices
ICollection<Vertex> movingverts = General.Map.Map.GetMarkedVertices(true);
ICollection<Vertex> fixedverts = General.Map.Map.GetMarkedVertices(false);
HashSet<Vertex> movingverts = new HashSet<Vertex>(General.Map.Map.GetMarkedVertices(true));
HashSet<Vertex> fixedverts = new HashSet<Vertex>(General.Map.Map.GetMarkedVertices(false));
// Find lines that moved during the drag
List<Linedef> movinglines = LinedefsFromMarkedVertices(false, true, true);
HashSet<Linedef> movinglines = new HashSet<Linedef>(LinedefsFromMarkedVertices(false, true, true));
// Find all non-moving lines
List<Linedef> fixedlines = LinedefsFromMarkedVertices(true, false, false);
HashSet<Linedef> fixedlines = new HashSet<Linedef>(LinedefsFromMarkedVertices(true, false, false));
// Determine area in which we are editing
RectangleF editarea = CreateArea(movinglines);
@ -2075,16 +2075,16 @@ namespace CodeImp.DoomBuilder.Map
// Split moving lines with unselected vertices
ICollection<Vertex> nearbyfixedverts = FilterByArea(fixedverts, ref editarea);
if(!SplitLinesByVertices(movinglines, nearbyfixedverts, STITCH_DISTANCE, movinglines, correctsectorrefs))
if(!SplitLinesByVertices(movinglines, nearbyfixedverts, STITCH_DISTANCE, movinglines, mergemode))
return false;
// Split non-moving lines with selected vertices
fixedlines = FilterByArea(fixedlines, ref editarea);
if(!SplitLinesByVertices(fixedlines, movingverts, STITCH_DISTANCE, movinglines, correctsectorrefs))
if(!SplitLinesByVertices(fixedlines, movingverts, STITCH_DISTANCE, movinglines, mergemode))
return false;
//mxd. Split moving lines with fixed lines
if(!SplitLinesByLines(fixedlines, movinglines, correctsectorrefs)) return false;
if(!SplitLinesByLines(fixedlines, movinglines, mergemode)) return false;
// Remove looped linedefs
RemoveLoopedLinedefs(movinglines);
@ -2092,10 +2092,49 @@ namespace CodeImp.DoomBuilder.Map
// Join overlapping lines
if(!JoinOverlappingLines(movinglines)) return false;
//mxd. Remove remaining new verts from dragged shape if possible
if(mergemode == MergeGeometryMode.REPLACE)
{
// Get lines, which belong to dragged sectors
HashSet<Sector> draggedsectors = GetSectorsFromLinedefs(movinglines);
HashSet<Linedef> sectorlines = new HashSet<Linedef>();
foreach(Sector s in draggedsectors)
{
foreach(Sidedef side in s.Sidedefs)
sectorlines.Add(side.Line);
}
HashSet<Vertex> tocheck = new HashSet<Vertex>();
foreach(Linedef l in sectorlines)
{
if(l.IsDisposed) continue;
if(!movingverts.Contains(l.Start)) tocheck.Add(l.Start);
if(!movingverts.Contains(l.End)) tocheck.Add(l.End);
}
// 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();
}
}
}
EndAddRemove();
//mxd. Correct sector references
if(correctsectorrefs)
if(mergemode != MergeGeometryMode.CLASSIC)
{
// Linedefs cache needs to be up to date...
Update(true, false);
@ -2104,6 +2143,11 @@ namespace CodeImp.DoomBuilder.Map
List<Linedef> changedlines = LinedefsFromMarkedVertices(false, true, true);
CorrectSectorReferences(changedlines, true);
CorrectOuterSides(new HashSet<Linedef>(changedlines));
// Mark only fully selected sectors
ClearMarkedSectors(false);
HashSet<Sector> changedsectors = GetSectorsFromLinedefs(changedlines);
foreach(Sector s in changedsectors) s.Marked = true;
}
return true;
@ -2112,6 +2156,9 @@ namespace CodeImp.DoomBuilder.Map
//mxd. Shameless SLADEMap::correctSectors ripoff... Corrects/builds sectors for all lines in [lines]
private static void CorrectSectorReferences(List<Linedef> lines, bool existing_only)
{
//DebugConsole.Clear();
//DebugConsole.WriteLine("CorrectSectorReferences for " + lines.Count + " lines");
// Create a list of sidedefs to perform sector creation with
List<LinedefSide> edges = new List<LinedefSide>();
if(existing_only)
@ -2151,15 +2198,9 @@ namespace CodeImp.DoomBuilder.Map
HashSet<Sector> affectedsectors = new HashSet<Sector>(General.Map.Map.GetSelectedSectors(true));
affectedsectors.UnionWith(General.Map.Map.GetUnselectedSectorsFromLinedefs(lines));
//mxd. Collect their lines
HashSet<Linedef> sectorlines = new HashSet<Linedef>();
foreach(Sector s in affectedsectors)
{
foreach(Sidedef side in s.Sidedefs)
{
if(side.Line != null) sectorlines.Add(side.Line);
}
}
//mxd. Collect their sidedefs
HashSet<Sidedef> sectorsides = new HashSet<Sidedef>();
foreach(Sector s in affectedsectors) sectorsides.UnionWith(s.Sidedefs);
// Build sectors
SectorBuilder builder = new SectorBuilder();
@ -2168,6 +2209,7 @@ namespace CodeImp.DoomBuilder.Map
foreach(LinedefSide ls in edges)
{
// Skip if edge is ignored
//DebugConsole.WriteLine((ls.Ignore ? "Ignoring line " : "Processing line ") + ls.Line.Index);
if(ls.Ignore) continue;
// Run sector builder on current edge
@ -2176,13 +2218,15 @@ namespace CodeImp.DoomBuilder.Map
// Find any subsequent edges that were part of the sector created
bool has_existing_lines = false;
bool has_existing_sides = false;
bool has_zero_sided_lines = false;
bool has_sides_belonging_to_dragged_sectors = false; //mxd
//bool has_zero_sided_lines = false;
bool has_dragged_sides = false; //mxd
List<LinedefSide> edges_in_sector = new List<LinedefSide>();
foreach(LinedefSide edge in builder.SectorEdges)
{
bool line_is_ours = false;
if(sectorlines.Contains(edge.Line)) has_sides_belonging_to_dragged_sectors = true; //mxd
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
foreach(LinedefSide ls2 in edges)
{
@ -2199,14 +2243,13 @@ namespace CodeImp.DoomBuilder.Map
if(line_is_ours)
{
if(edge.Line.Front == null && edge.Line.Back == null)
has_zero_sided_lines = true;
//if(edge.Line.Front == null && edge.Line.Back == null)
//has_zero_sided_lines = true;
}
else
{
has_existing_lines = true;
if(edge.Front ? edge.Line.Front != null : edge.Line.Back != null)
has_existing_sides = true;
has_existing_sides |= side_exists; //mxd
}
}
@ -2217,7 +2260,8 @@ namespace CodeImp.DoomBuilder.Map
// 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.
if(existing_only && has_existing_lines && !has_existing_sides && !has_sides_belonging_to_dragged_sectors) continue;
if(existing_only && has_existing_lines && !has_existing_sides && !has_dragged_sides)
continue;
// Ignore traced edges when trying to create any further sectors
foreach(LinedefSide ls3 in edges_in_sector) ls3.Ignore = true;
@ -2331,7 +2375,7 @@ namespace CodeImp.DoomBuilder.Map
foreach(Sidedef side in newsides)
{
// Clear any unneeded textures
side.RemoveUnneededTextures(side.Other != null);
side.RemoveUnneededTextures(side.Other != null, false, true);
// Set middle texture if needed
if(side.MiddleRequired() && side.MiddleTexture == "-")
@ -2627,8 +2671,8 @@ namespace CodeImp.DoomBuilder.Map
// Go for all the lines
foreach(Linedef l in lines)
{
// Check if referencing the same vertex twice
if(l.Start == l.End)
// 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)
{
// Remove this line
while(lines.Remove(l));
@ -2784,8 +2828,8 @@ namespace CodeImp.DoomBuilder.Map
/// <summary>This splits the given lines with the given vertices. All affected lines
/// will be added to changedlines. Returns false when the operation failed.</summary>
public static bool SplitLinesByVertices(ICollection<Linedef> lines, ICollection<Vertex> verts, float splitdist, ICollection<Linedef> changedlines) { return SplitLinesByVertices(lines, verts, splitdist, changedlines, false); }
public static bool SplitLinesByVertices(ICollection<Linedef> lines, ICollection<Vertex> verts, float splitdist, ICollection<Linedef> changedlines, bool removeinnerlines)
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)
{
if(verts.Count == 0 || lines.Count == 0) return true; //mxd
@ -2802,9 +2846,14 @@ namespace CodeImp.DoomBuilder.Map
BlockEntry[,] bmap = blockmap.Map;
//mxd
//HashSet<Vertex> splitverts = new HashSet<Vertex>();
//HashSet<Sector> changedsectors = (removeinnerlines ? General.Map.Map.GetSectorsFromLinedefs(changedlines) : new HashSet<Sector>());
//HashSet<Linedef> initialchanedlines = new HashSet<Linedef>(changedlines);
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);
}
for(int w = 0; w < bmWidth; w++)
{
@ -2836,7 +2885,7 @@ namespace CodeImp.DoomBuilder.Map
Linedef nl = l.Split(v);
if(nl == null) return false;
v.Marked = true; //mxd
//splitverts.Add(v); //mxd
splitverts.Add(v); //mxd
// Add the new line to the list
lines.Add(nl);
@ -2860,7 +2909,7 @@ namespace CodeImp.DoomBuilder.Map
}
//mxd. Remove lines, which are inside affected sectors
/*if(removeinnerlines)
if(mergemode == MergeGeometryMode.REPLACE && changedsectors.Count > 0)
{
HashSet<Linedef> alllines = new HashSet<Linedef>(lines);
if(changedlines != null) alllines.UnionWith(changedlines);
@ -2870,7 +2919,7 @@ namespace CodeImp.DoomBuilder.Map
foreach(Linedef l in alllines)
{
// Remove line when both it's start and end are inside a changed sector and neither side references it
if(l.Start != null && l.End != null && !initialchanedlines.Contains(l) &&
if(l.Start != null && l.End != null &&
(l.Front == null || !changedsectors.Contains(l.Front.Sector)) &&
(l.Back == null || !changedsectors.Contains(l.Back.Sector)))
{
@ -2878,61 +2927,72 @@ namespace CodeImp.DoomBuilder.Map
{
if(s.Intersect(l.Start.Position) && s.Intersect(l.End.Position))
{
Vertex[] tocheck = new[] { l.Start, l.End };
// Remove from collections and dispose
lines.Remove(l);
if(changedlines != null) changedlines.Remove(l);
Vertex[] tocheck = { l.Start, l.End };
while(lines.Remove(l));
if(changedlines != null) while(changedlines.Remove(l));
l.Dispose();
foreach(Vertex v in tocheck)
{
// If the vertex only has 2 linedefs attached, then merge the linedefs
if(v.Linedefs.Count == 2)
// If the newly created vertex only has 2 linedefs attached, then merge the linedefs
if(!v.IsDisposed && v.Linedefs.Count == 2 && splitverts.Contains(v))
{
Linedef ld1 = General.GetByIndex(v.Linedefs, 0);
Linedef ld2 = General.GetByIndex(v.Linedefs, 1);
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);
// Remove from collections and dispose
lines.Remove(ld2);
if(changedlines != null) changedlines.Remove(ld2);
while(lines.Remove(ld2));
if(changedlines != null) while(changedlines.Remove(ld2));
ld2.Dispose();
// Trash vertex
v.Dispose();
}
}
}
break;
}
}
}
}
}*/
}
return true;
}
/// <summary>Splits lines by lines. Adds new lines to the second collection. Returns false when the operation failed.</summary>
public static bool SplitLinesByLines(IList<Linedef> lines, IList<Linedef> changedlines, bool removeinnerlines) //mxd
public static bool SplitLinesByLines(HashSet<Linedef> lines, HashSet<Linedef> changedlines, MergeGeometryMode mergemode) //mxd
{
if(lines.Count == 0 || changedlines.Count == 0) return true;
if(lines.Count == 0 || changedlines.Count == 0 || mergemode == MergeGeometryMode.CLASSIC) return true;
// Create blockmap
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);
}
RectangleF area = RectangleF.Union(CreateArea(lines), CreateArea(changedlines));
BlockMap<BlockEntry> blockmap = new BlockMap<BlockEntry>(area);
blockmap.AddLinedefsSet(lines);
blockmap.AddLinedefsSet(changedlines);
blockmap.AddVerticesSet(verts); //mxd
int bmWidth = blockmap.Size.Width;
int bmHeight = blockmap.Size.Height;
BlockEntry[,] bmap = blockmap.Map;
//mxd
HashSet<Vertex> splitverts = new HashSet<Vertex>();
HashSet<Sector> changedsectors = (removeinnerlines ? General.Map.Map.GetSectorsFromLinedefs(changedlines) : new HashSet<Sector>());
HashSet<Linedef> initialchanedlines = new HashSet<Linedef>(changedlines);
HashSet<Sector> changedsectors = (mergemode == MergeGeometryMode.REPLACE ? General.Map.Map.GetSectorsFromLinedefs(changedlines) : new HashSet<Sector>());
// Check for intersections
for(int w = 0; w < bmWidth; w++)
@ -2960,8 +3020,25 @@ namespace CodeImp.DoomBuilder.Map
Vector2D intersection = Line2D.GetIntersectionPoint(new Line2D(l1), new Line2D(l2), true);
if(!float.IsNaN(intersection.x))
{
// Create split vertex
Vertex splitvertex = General.Map.Map.CreateVertex(intersection);
//mxd. Round to map format precision
intersection.x = (float)Math.Round(intersection.x, General.Map.FormatInterface.VertexDecimals);
intersection.y = (float)Math.Round(intersection.y, General.Map.FormatInterface.VertexDecimals);
//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);
if(splitvertex == null) return false;
// Split both lines
@ -2971,9 +3048,12 @@ namespace CodeImp.DoomBuilder.Map
Linedef nl2 = l2.Split(splitvertex);
if(nl2 == null) return false;
// Mark split vertex
// Mark split vertex?
if(!existingvert)
{
splitvertex.Marked = true;
splitverts.Add(splitvertex); //mxd
}
// Add to the second collection
changedlines.Add(nl1);
@ -2989,7 +3069,7 @@ namespace CodeImp.DoomBuilder.Map
}
//mxd. Remove lines, which are inside affected sectors
if(removeinnerlines)
if(mergemode == MergeGeometryMode.REPLACE)
{
HashSet<Linedef> alllines = new HashSet<Linedef>(lines);
alllines.UnionWith(changedlines);
@ -2999,21 +3079,26 @@ namespace CodeImp.DoomBuilder.Map
foreach(Linedef l in alllines)
{
// Remove line when both it's start and end are inside a changed sector and neither side references it
if(l.Start != null && l.End != null && !initialchanedlines.Contains(l) &&
(l.Front == null || !changedsectors.Contains(l.Front.Sector)) &&
if(l.Start != null && l.End != null)
{
if(l.Front == null && l.Back == null)
{
l.Dispose();
}
else if((l.Front == null || !changedsectors.Contains(l.Front.Sector)) &&
(l.Back == null || !changedsectors.Contains(l.Back.Sector)))
{
foreach(Sector s in changedsectors)
{
if(s.Intersect(l.Start.Position) && s.Intersect(l.End.Position))
{
Vertex[] tocheck = new[] { l.Start, l.End };
Vertex[] tocheck = { l.Start, l.End };
l.Dispose();
foreach(Vertex v in tocheck)
{
// If the vertex only has 2 linedefs attached, then merge the linedefs
if(!v.IsDisposed && v.Linedefs.Count == 2)
// If the newly created vertex only has 2 linedefs attached, then merge the linedefs
if(!v.IsDisposed && v.Linedefs.Count == 2 && splitverts.Contains(v))
{
Linedef ld1 = General.GetByIndex(v.Linedefs, 0);
Linedef ld2 = General.GetByIndex(v.Linedefs, 1);
@ -3032,6 +3117,7 @@ namespace CodeImp.DoomBuilder.Map
}
}
}
}
return true;
}

View file

@ -0,0 +1,9 @@
namespace CodeImp.DoomBuilder.Map
{
public enum MergeGeometryMode //mxd
{
CLASSIC,
MERGE,
REPLACE,
}
}

View file

@ -64,19 +64,32 @@ namespace CodeImp.DoomBuilder.Map
for(int a = 0; a < 10000; a++)
{
// Trace outline
if(!TraceOutline(line, front)) break;
if(!TraceOutline(line, front))
{
//DebugConsole.WriteLine("TraceSector: find outmost outline failed");
break;
}
// Discard any vertices outside the traced outline
vertex_valid.RemoveWhere(PointOutsideOutline);
//DebugConsole.WriteLine("vertex_valid: " + vertex_valid.Count + " verts after RemoveWhere");
// If it is clockwise, we've found the outmost outline
if(o_clockwise) break;
if(o_clockwise)
{
//DebugConsole.WriteLine("TraceSector: found outmost outline");
break;
}
// Otherwise, find the next edge outside the outline
LinedefSide next = FindOuterEdge();
// If none was found, we're outside the map
if(next == null) return false;
if(next == null)
{
//DebugConsole.WriteLine("TraceSector aborted: no outer edge");
return false;
}
// Repeat with this edge
line = next.Line;
@ -93,17 +106,25 @@ namespace CodeImp.DoomBuilder.Map
LinedefSide edge = FindInnerEdge();
// Check if we're done
if(edge == null) break;
if(edge == null)
{
//DebugConsole.WriteLine("No inner edge found (no edge)");
break;
}
// Trace outline from edge
if(!TraceOutline(edge.Line, edge.Front)) break;
if(!TraceOutline(edge.Line, edge.Front))
{
//DebugConsole.WriteLine("No inner edge found (TraceOutline failed)");
break;
}
// Discard any vertices outside the traced outline
vertex_valid.RemoveWhere(PointOutsideOutline);
//DebugConsole.WriteLine("vertex_valid: " + vertex_valid.Count + " verts after RemoveWhere");
}
//DebugConsole.WriteLine("FindInnerEdge: " + o_edges.Count + " lines");
return true;
}
@ -119,6 +140,7 @@ namespace CodeImp.DoomBuilder.Map
// Init outline
o_edges.Clear();
o_bbox = RectangleF.Empty;
LinedefSide start = new LinedefSide(line, front);
o_edges.Add(start);
int edge_sum = 0;
@ -144,12 +166,14 @@ namespace CodeImp.DoomBuilder.Map
// Get next edge. If no valid next edge was found, go back along the current line
LinedefSide edge_next = (NextEdge(edge, visited_lines) ?? new LinedefSide(edge.Line, !edge.Front));
//DebugConsole.WriteLine("Next line " + edge_next.Line.Index + (edge_next.Front ? " (front)" : " (back)"));
//DebugConsole.WriteLine("Next line for " + edge.Line.Index + (edge.Front ? " (front)" : " (back)") + ": " + edge_next.Line.Index + (edge_next.Front ? " (front)" : " (back)"));
// Discard edge vertices
vertex_valid.Remove(edge_next.Line.Start);
vertex_valid.Remove(edge_next.Line.End);
//DebugConsole.WriteLine("vertex_valid: " + vertex_valid.Count + " verts after Remove (in TraceOutline)");
// Check if we're back to the start
if(edge_next.Line == start.Line && edge_next.Front == start.Front)
break;
@ -166,12 +190,15 @@ namespace CodeImp.DoomBuilder.Map
Math.Max(edge.Line.Start.Position.x, edge.Line.End.Position.x), // right
Math.Max(edge.Line.Start.Position.y, edge.Line.End.Position.y)); // bottom
o_bbox = (o_bbox.IsEmpty ? l_bbox : RectangleF.Union(o_bbox, l_bbox));
//mxd. As it turned out, o_bbox.IsEmpty was not what we needed...
o_bbox = (o_bbox == RectangleF.Empty ? l_bbox : RectangleF.Union(o_bbox, l_bbox));
}
// Check if outline is clockwise
o_clockwise = (edge_sum < 0);
//DebugConsole.WriteLine("TraceOutline for line " + line.Index + " (" + (front ? "front":"back") + ") found " + o_edges.Count + " edges; o_clockwise=" + o_clockwise);
// Add outline edges to sector edge list
sector_edges.AddRange(o_edges);
@ -230,6 +257,8 @@ namespace CodeImp.DoomBuilder.Map
/// <summary>Find the closest edge within the current outline (that isn't part of the current outline)</summary>
private LinedefSide FindInnerEdge()
{
//DebugConsole.WriteLine("FindInnerEdge: processing " + vertex_valid.Count + " verts");
// Find rightmost non-discarded vertex
vertex_right = null;
foreach(Vertex v in vertex_valid)
@ -247,7 +276,11 @@ namespace CodeImp.DoomBuilder.Map
}
// If no vertex was found, we're done
if(vertex_right == null) return null;
if(vertex_right == null)
{
//DebugConsole.WriteLine("FindInnerEdge: no vertex_right");
return null;
}
// Go through vertex's connected lines, to find
// the line with the smallest angle parallel with
@ -280,6 +313,8 @@ namespace CodeImp.DoomBuilder.Map
{
// Discard vertex and try again
vertex_valid.Remove(vertex_right);
//DebugConsole.WriteLine("vertex_valid: " + vertex_valid.Count + " verts after Remove (in FindInnerEdge)");
return FindInnerEdge();
}
@ -384,11 +419,20 @@ namespace CodeImp.DoomBuilder.Map
//mxd. The meaning of 0.0 is also inverted!!!
// Return false if it is on the correct side
if(side > 0 && o_edges[nearest].Front) return false;
if(side <= 0 && !o_edges[nearest].Front) return false;
if(side > 0 && o_edges[nearest].Front)
{
//DebugConsole.WriteLine("Point " + point + " is within outline (infront of line) " + nearest);
return false;
}
if(side <= 0 && !o_edges[nearest].Front)
{
//DebugConsole.WriteLine("Point " + point + " is within outline (at the back of line) " + nearest);
return false;
}
}
// Not within the outline
//DebugConsole.WriteLine("Point " + point + " is outside outline");
return true;
}
@ -491,7 +535,10 @@ namespace CodeImp.DoomBuilder.Map
if(sector_copy != null) sector_copy.CopyPropertiesTo(sector);
}
//DebugConsole.WriteLine(" ");
//DebugConsole.WriteLine("Creating sector " + sector.Index + " from " + sector_edges.Count + " lines");
//DebugConsole.WriteLine("*************************************************************");
//DebugConsole.WriteLine(" ");
// Set sides to new sector
foreach(LinedefSide edge in sector_edges)
@ -501,14 +548,26 @@ namespace CodeImp.DoomBuilder.Map
{
if(target.Sector != sector)
{
bool targetwas2s = (target.Other != null);
target.SetSector(sector); //mxd. Reattach side
target.Marked = true; //mxd. Mark it
//mxd. Mark for texture adjustments if sidedness was changed.
//mxd. Also keep existing mark if the side was already marked.
target.Marked |= ((targetwas2s && target.Other == null) || (!targetwas2s && target.Other != null));
}
}
else
{
target = General.Map.Map.CreateSidedef(edge.Line, edge.Front, sector); //mxd. Create new side
target.Marked = true; //mxd. Mark it
target.Marked = true; //mxd. Mark it for texture adjustments
if(target.Other != null)
{
//mxd. Better than nothing
target.Other.CopyPropertiesTo(target);
//mxd. Other was singlesided. We'll need to adjust it's textures as well
target.Other.Marked = true;
}
}
}
}

View file

@ -330,7 +330,7 @@ namespace CodeImp.DoomBuilder.Map
public override string ToString()
{
#if DEBUG
return "Vertex (" + pos + (marked ? "; marked" : "") + ")";
return "Vertex " + Index + " (" + pos + (marked ? "; marked" : "") + ")";
#else
return "Vertex (" + pos + ")";
#endif

View file

@ -557,6 +557,20 @@ namespace CodeImp.DoomBuilder.Properties {
}
}
internal static System.Drawing.Bitmap MergeGeo {
get {
object obj = ResourceManager.GetObject("MergeGeo", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
internal static System.Drawing.Bitmap MergeGeoClassic {
get {
object obj = ResourceManager.GetObject("MergeGeoClassic", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
internal static System.Drawing.Bitmap mergegeometry {
get {
object obj = ResourceManager.GetObject("mergegeometry", resourceCulture);
@ -571,6 +585,13 @@ namespace CodeImp.DoomBuilder.Properties {
}
}
internal static System.Drawing.Bitmap MergeGeoRemoveLines {
get {
object obj = ResourceManager.GetObject("MergeGeoRemoveLines", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
internal static System.Drawing.Bitmap MissingTexture {
get {
object obj = ResourceManager.GetObject("MissingTexture", resourceCulture);

View file

@ -586,4 +586,13 @@
<data name="Angle7" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Angle7.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MergeGeo" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MergeGeo.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MergeGeoClassic" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MergeGeoClassic.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MergeGeoRemoveLines" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MergeGeoRemoveLines.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View file

@ -457,6 +457,36 @@ redo
allowscroll = true;
}
geomergeclassic //mxd
{
title = "Merge Dragged Vertices Only";
category = "edit";
description = "Only vertex-line intersections will be processed when applying geometry drag or paste actions.";
allowkeys = true;
allowmouse = true;
allowscroll = false;
}
geomerge //mxd
{
title = "Merge Dragged Geometry";
category = "edit";
description = "Geometry merging will be performed when applying geometry drag or paste actions.";
allowkeys = true;
allowmouse = true;
allowscroll = false;
}
georeplace //mxd
{
title = "Replace with Dragged Geometry";
category = "edit";
description = "Dragged geometry will replace underlaying geometry when applying geometry drag or paste actions.";
allowkeys = true;
allowmouse = true;
allowscroll = false;
}
togglesnap
{
title = "Snap to Grid";
@ -469,9 +499,9 @@ togglesnap
toggleautomerge
{
title = "Merge Geometry";
title = "Snap to Geometry";
category = "edit";
description = "Toggles automatic merging of geometry for vertices and structures that are being dragged.";
description = "Toggles snapping to the nearest vertex or linedef for map elements that are being dragged.";
allowkeys = true;
allowmouse = true;
allowscroll = true;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -97,6 +97,10 @@ namespace CodeImp.DoomBuilder.Windows
this.itemviewfloors = new System.Windows.Forms.ToolStripMenuItem();
this.itemviewceilings = new System.Windows.Forms.ToolStripMenuItem();
this.seperatorviewviews = new System.Windows.Forms.ToolStripSeparator();
this.itemmergegeoclassic = new System.Windows.Forms.ToolStripMenuItem();
this.itemmergegeo = new System.Windows.Forms.ToolStripMenuItem();
this.itemreplacegeo = new System.Windows.Forms.ToolStripMenuItem();
this.separatorgeomerge = new System.Windows.Forms.ToolStripSeparator();
this.itemfullbrightness = new System.Windows.Forms.ToolStripMenuItem();
this.itemtogglegrid = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
@ -180,6 +184,10 @@ namespace CodeImp.DoomBuilder.Windows
this.buttonviewbrightness = new System.Windows.Forms.ToolStripButton();
this.buttonviewfloors = new System.Windows.Forms.ToolStripButton();
this.buttonviewceilings = new System.Windows.Forms.ToolStripButton();
this.separatorgeomergemodes = new System.Windows.Forms.ToolStripSeparator();
this.buttonmergegeoclassic = new System.Windows.Forms.ToolStripButton();
this.buttonmergegeo = new System.Windows.Forms.ToolStripButton();
this.buttonplacegeo = new System.Windows.Forms.ToolStripButton();
this.seperatorviews = new System.Windows.Forms.ToolStripSeparator();
this.buttontogglecomments = new System.Windows.Forms.ToolStripButton();
this.buttontogglefixedthingsscale = new System.Windows.Forms.ToolStripButton();
@ -518,6 +526,10 @@ namespace CodeImp.DoomBuilder.Windows
this.itempaste,
this.itempastespecial,
this.seperatoreditcopypaste,
this.itemmergegeoclassic,
this.itemmergegeo,
this.itemreplacegeo,
this.separatorgeomerge,
this.itemsnaptogrid,
this.itemdynamicgridsize,
this.itemautomerge,
@ -619,7 +631,7 @@ namespace CodeImp.DoomBuilder.Windows
this.itemautomerge.Name = "itemautomerge";
this.itemautomerge.Size = new System.Drawing.Size(219, 22);
this.itemautomerge.Tag = "builder_toggleautomerge";
this.itemautomerge.Text = "&Merge Geometry";
this.itemautomerge.Text = "Snap to &Geometry";
this.itemautomerge.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// itemautoclearsidetextures
@ -819,6 +831,38 @@ namespace CodeImp.DoomBuilder.Windows
this.seperatorviewviews.Name = "seperatorviewviews";
this.seperatorviewviews.Size = new System.Drawing.Size(212, 6);
//
// itemmergegeoclassic
//
this.itemmergegeoclassic.Image = global::CodeImp.DoomBuilder.Properties.Resources.MergeGeoClassic;
this.itemmergegeoclassic.Name = "itemmergegeoclassic";
this.itemmergegeoclassic.Size = new System.Drawing.Size(215, 22);
this.itemmergegeoclassic.Tag = "builder_geomergeclassic";
this.itemmergegeoclassic.Text = "Merge Dragged Vertices Only";
this.itemmergegeoclassic.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// itemmergegeo
//
this.itemmergegeo.Image = global::CodeImp.DoomBuilder.Properties.Resources.MergeGeo;
this.itemmergegeo.Name = "itemmergegeo";
this.itemmergegeo.Size = new System.Drawing.Size(215, 22);
this.itemmergegeo.Tag = "builder_geomerge";
this.itemmergegeo.Text = "Merge Dragged Geometry";
this.itemmergegeo.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// itemreplacegeo
//
this.itemreplacegeo.Image = global::CodeImp.DoomBuilder.Properties.Resources.MergeGeoRemoveLines;
this.itemreplacegeo.Name = "itemreplacegeo";
this.itemreplacegeo.Size = new System.Drawing.Size(215, 22);
this.itemreplacegeo.Tag = "builder_georeplace";
this.itemreplacegeo.Text = "Replace with Dragged Geometry";
this.itemreplacegeo.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// separatorgeomerge
//
this.separatorgeomerge.Name = "separatorgeomerge";
this.separatorgeomerge.Size = new System.Drawing.Size(212, 6);
//
// itemfullbrightness
//
this.itemfullbrightness.Checked = true;
@ -1254,6 +1298,10 @@ namespace CodeImp.DoomBuilder.Windows
this.buttonviewbrightness,
this.buttonviewfloors,
this.buttonviewceilings,
this.separatorgeomergemodes,
this.buttonmergegeoclassic,
this.buttonmergegeo,
this.buttonplacegeo,
this.seperatorviews,
this.buttonsnaptogrid,
this.buttontoggledynamicgrid,
@ -1645,6 +1693,48 @@ namespace CodeImp.DoomBuilder.Windows
this.buttonviewceilings.Text = "View Ceiling Textures";
this.buttonviewceilings.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// separatorgeomergemodes
//
this.separatorgeomergemodes.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
this.separatorgeomergemodes.Name = "separatorgeomergemodes";
this.separatorgeomergemodes.Size = new System.Drawing.Size(6, 25);
//
// buttonmergegeoclassic
//
this.buttonmergegeoclassic.CheckOnClick = true;
this.buttonmergegeoclassic.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.buttonmergegeoclassic.Image = global::CodeImp.DoomBuilder.Properties.Resources.MergeGeoClassic;
this.buttonmergegeoclassic.ImageTransparentColor = System.Drawing.Color.Magenta;
this.buttonmergegeoclassic.Name = "buttonmergegeoclassic";
this.buttonmergegeoclassic.Size = new System.Drawing.Size(23, 22);
this.buttonmergegeoclassic.Tag = "builder_geomergeclassic";
this.buttonmergegeoclassic.Text = "Merge Dragged Vertices Only";
this.buttonmergegeoclassic.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// buttonmergegeoclassic
//
this.buttonmergegeo.CheckOnClick = true;
this.buttonmergegeo.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.buttonmergegeo.Image = global::CodeImp.DoomBuilder.Properties.Resources.MergeGeo;
this.buttonmergegeo.ImageTransparentColor = System.Drawing.Color.Magenta;
this.buttonmergegeo.Name = "buttonmergegeo";
this.buttonmergegeo.Size = new System.Drawing.Size(23, 22);
this.buttonmergegeo.Tag = "builder_geomerge";
this.buttonmergegeo.Text = "Merge Dragged Geometry";
this.buttonmergegeo.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// buttonmergegeoclassic
//
this.buttonplacegeo.CheckOnClick = true;
this.buttonplacegeo.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.buttonplacegeo.Image = global::CodeImp.DoomBuilder.Properties.Resources.MergeGeoRemoveLines;
this.buttonplacegeo.ImageTransparentColor = System.Drawing.Color.Magenta;
this.buttonplacegeo.Name = "buttonmergegeoclassic";
this.buttonplacegeo.Size = new System.Drawing.Size(23, 22);
this.buttonplacegeo.Tag = "builder_georeplace";
this.buttonplacegeo.Text = "Replace with Dragged Geometry";
this.buttonplacegeo.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// seperatorviews
//
this.seperatorviews.Margin = new System.Windows.Forms.Padding(6, 0, 6, 0);
@ -1700,7 +1790,7 @@ namespace CodeImp.DoomBuilder.Windows
this.buttonautomerge.Name = "buttonautomerge";
this.buttonautomerge.Size = new System.Drawing.Size(23, 22);
this.buttonautomerge.Tag = "builder_toggleautomerge";
this.buttonautomerge.Text = "Merge Geometry";
this.buttonautomerge.Text = "Snap to Geometry";
this.buttonautomerge.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// buttonautoclearsidetextures
@ -2607,6 +2697,10 @@ namespace CodeImp.DoomBuilder.Windows
private System.Windows.Forms.ToolStripButton buttonviewbrightness;
private System.Windows.Forms.ToolStripButton buttonviewfloors;
private System.Windows.Forms.ToolStripButton buttonviewceilings;
private System.Windows.Forms.ToolStripSeparator separatorgeomergemodes;
private System.Windows.Forms.ToolStripButton buttonmergegeoclassic;
private System.Windows.Forms.ToolStripButton buttonmergegeo;
private System.Windows.Forms.ToolStripButton buttonplacegeo;
private System.Windows.Forms.ToolStripSeparator seperatortoolsresources;
private System.Windows.Forms.ToolStripButton buttonscripteditor;
private System.Windows.Forms.ToolStripMenuItem menuview;
@ -2617,6 +2711,10 @@ namespace CodeImp.DoomBuilder.Windows
private System.Windows.Forms.ToolStripMenuItem itemviewfloors;
private System.Windows.Forms.ToolStripMenuItem itemviewceilings;
private System.Windows.Forms.ToolStripSeparator seperatorviewzoom;
private System.Windows.Forms.ToolStripMenuItem itemmergegeoclassic;
private System.Windows.Forms.ToolStripMenuItem itemmergegeo;
private System.Windows.Forms.ToolStripMenuItem itemreplacegeo;
private System.Windows.Forms.ToolStripSeparator separatorgeomerge;
private System.Windows.Forms.ToolStripMenuItem itemscripteditor;
private System.Windows.Forms.ToolStripSeparator seperatortoolsconfig;
private System.Windows.Forms.ToolStripMenuItem itemtestmap;

View file

@ -138,6 +138,10 @@ namespace CodeImp.DoomBuilder.Windows
private ToolStripButton[] viewmodesbuttons;
private ToolStripMenuItem[] viewmodesitems;
//mxd. Geometry merge modes
private ToolStripButton[] geomergemodesbuttons;
private ToolStripMenuItem[] geomergemodesitems;
// Edit modes
private List<ToolStripItem> editmodeitems;
@ -248,6 +252,17 @@ namespace CodeImp.DoomBuilder.Windows
viewmodesitems[(int)ViewMode.FloorTextures] = itemviewfloors;
viewmodesitems[(int)ViewMode.CeilingTextures] = itemviewceilings;
//mxd. Make arrays for geometry merge modes
int numgeomodes = Enum.GetValues(typeof(MergeGeometryMode)).Length;
geomergemodesbuttons = new ToolStripButton[numgeomodes];
geomergemodesbuttons[(int)MergeGeometryMode.CLASSIC] = buttonmergegeoclassic;
geomergemodesbuttons[(int)MergeGeometryMode.MERGE] = buttonmergegeo;
geomergemodesbuttons[(int)MergeGeometryMode.REPLACE] = buttonplacegeo;
geomergemodesitems = new ToolStripMenuItem[numgeomodes];
geomergemodesitems[(int)MergeGeometryMode.CLASSIC] = itemmergegeoclassic;
geomergemodesitems[(int)MergeGeometryMode.MERGE] = itemmergegeo;
geomergemodesitems[(int)MergeGeometryMode.REPLACE] = itemreplacegeo;
// Visual Studio IDE doesn't let me set these in the designer :(
buttonzoom.Font = menufile.Font;
buttonzoom.DropDownDirection = ToolStripDropDownDirection.AboveLeft;
@ -312,10 +327,18 @@ namespace CodeImp.DoomBuilder.Windows
}
// View mode only matters in classic editing modes
bool isclassicmode = (General.Editing.Mode is ClassicMode);
for(int i = 0; i < Renderer2D.NUM_VIEW_MODES; i++)
{
viewmodesitems[i].Enabled = (General.Editing.Mode is ClassicMode);
viewmodesbuttons[i].Enabled = (General.Editing.Mode is ClassicMode);
viewmodesitems[i].Enabled = isclassicmode;
viewmodesbuttons[i].Enabled = isclassicmode;
}
//mxd. Merge geometry mode only matters in classic editing modes
for(int i = 0; i < geomergemodesbuttons.Length; i++)
{
geomergemodesbuttons[i].Enabled = isclassicmode;
geomergemodesitems[i].Enabled = isclassicmode;
}
UpdateEditMenu();
@ -2079,6 +2102,10 @@ namespace CodeImp.DoomBuilder.Windows
buttonviewceilings.Visible = General.Settings.ToolbarViewModes && maploaded;
buttonviewfloors.Visible = General.Settings.ToolbarViewModes && maploaded;
buttonviewnormal.Visible = General.Settings.ToolbarViewModes && maploaded;
separatorgeomergemodes.Visible = General.Settings.ToolbarGeometry && maploaded; //mxd
buttonmergegeoclassic.Visible = General.Settings.ToolbarGeometry && maploaded; //mxd
buttonmergegeo.Visible = General.Settings.ToolbarGeometry && maploaded; //mxd
buttonplacegeo.Visible = General.Settings.ToolbarGeometry && maploaded; //mxd
buttonsnaptogrid.Visible = General.Settings.ToolbarGeometry && maploaded;
buttontoggledynamicgrid.Visible = General.Settings.ToolbarGeometry && maploaded; //mxd
buttontoggledynamicgrid.Checked = General.Settings.DynamicGridSize; //mxd
@ -2756,6 +2783,17 @@ namespace CodeImp.DoomBuilder.Windows
buttoncut.Enabled = itemcut.Enabled;
buttoncopy.Enabled = itemcopy.Enabled;
buttonpaste.Enabled = itempaste.Enabled;
//mxd. Geometry merge mode items
if(General.Map != null)
{
for(int i = 0; i < geomergemodesbuttons.Length; i++)
{
// Check the correct item
geomergemodesbuttons[i].Checked = (i == (int)General.Settings.MergeGeometryMode);
geomergemodesitems[i].Checked = (i == (int)General.Settings.MergeGeometryMode);
}
}
}
//mxd
@ -2919,6 +2957,36 @@ namespace CodeImp.DoomBuilder.Windows
f.ShowDialog(this);
}
//mxd
[BeginAction("geomergeclassic")]
private void GeoMergeClassic()
{
General.Settings.MergeGeometryMode = MergeGeometryMode.CLASSIC;
UpdateToolbar();
UpdateEditMenu();
DisplayStatus(StatusType.Action, "\"Merge Dragged Vertices Only\" mode selected");
}
//mxd
[BeginAction("geomerge")]
private void GeoMerge()
{
General.Settings.MergeGeometryMode = MergeGeometryMode.MERGE;
UpdateToolbar();
UpdateEditMenu();
DisplayStatus(StatusType.Action, "\"Merge Dragged Geometry\" mode selected");
}
//mxd
[BeginAction("georeplace")]
private void GeoReplace()
{
General.Settings.MergeGeometryMode = MergeGeometryMode.REPLACE;
UpdateToolbar();
UpdateEditMenu();
DisplayStatus(StatusType.Action, "\"Replace with Dragged Geometry\" mode selected");
}
#endregion
#region ================== View Menu

View file

@ -420,7 +420,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
MoveGeometryRelative(mousemappos - dragstartmappos, snaptogrid, snaptogridincrement, snaptonearest, snaptocardinaldirection);
// Stitch geometry
General.Map.Map.StitchGeometry(true);
General.Map.Map.StitchGeometry(General.Settings.MergeGeometryMode);
// Snap to map format accuracy
General.Map.Map.SnapAllToAccuracy();

View file

@ -1645,8 +1645,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
}
//mxd. We'll need sidedefs marked by StitchGeometry, not all sidedefs from selection...
General.Map.Map.ClearMarkedSidedefs(false);
// Stitch geometry
General.Map.Map.StitchGeometry(true);
General.Map.Map.StitchGeometry(General.Settings.MergeGeometryMode);
// Snap to map format accuracy
General.Map.Map.SnapAllToAccuracy(General.Map.UDMF && usepreciseposition);