From 70b88bd47d5691e94fe0bda8bfad739a8f34bc70 Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Wed, 29 May 2013 14:18:49 +0000
Subject: [PATCH] Visual mode, UDMF: "Auto-align textures" actions now work for
 floors and ceilings. Textures are aligned to a linedef of highlighted
 floor/ceiling, which is closest to 3d-cursor position.

---
 Source/Core/VisualModes/VisualMode.cs         |   4 +-
 .../VisualModes/BaseVisualGeometrySector.cs   | 149 ++++++++++++++++++
 .../VisualModes/BaseVisualMode.cs             |   7 +-
 .../BuilderModes/VisualModes/VisualCeiling.cs |  97 +++---------
 .../BuilderModes/VisualModes/VisualFloor.cs   |  96 +++--------
 5 files changed, 196 insertions(+), 157 deletions(-)

diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs
index 7df490399..61597f135 100644
--- a/Source/Core/VisualModes/VisualMode.cs
+++ b/Source/Core/VisualModes/VisualMode.cs
@@ -427,7 +427,7 @@ namespace CodeImp.DoomBuilder.VisualModes
         //mxd
         [BeginAction("placethingatcursor", BaseAction = true)]
         protected void placeThingAtCursor() {
-            Vector2D hitpos = getHitPosition();
+            Vector2D hitpos = GetHitPosition();
             if (!hitpos.IsFinite()) {
                 General.Interface.DisplayStatus(StatusType.Warning, "Cannot place Thing here");
                 return;
@@ -437,7 +437,7 @@ namespace CodeImp.DoomBuilder.VisualModes
         }
 
         //mxd. 
-        protected Vector2D getHitPosition() {
+        public Vector2D GetHitPosition() {
             Vector3D start = General.Map.VisualCamera.Position;
             Vector3D delta = General.Map.VisualCamera.Target - General.Map.VisualCamera.Position;
             delta = delta.GetFixedLength(General.Settings.ViewDistance * 0.98f);
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
index 082d2e45c..648bebcde 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
@@ -26,6 +26,8 @@ using CodeImp.DoomBuilder.Map;
 using CodeImp.DoomBuilder.Rendering;
 using CodeImp.DoomBuilder.Geometry;
 using CodeImp.DoomBuilder.VisualModes;
+using CodeImp.DoomBuilder.Types;
+using CodeImp.DoomBuilder.GZBuilder.Tools;
 
 #endregion
 
@@ -179,6 +181,153 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		public virtual bool IsSelected() {
 			return selected;
 		}
+
+		//mxd
+		protected void alignTextureToClosestLine(bool alignx, bool aligny) {
+			//find a linedef to align to
+			Vector2D hitpos = mode.GetHitPosition();
+			if(!(mode.HighlightedObject is BaseVisualSector) || !hitpos.IsFinite())	return;
+			bool isFront = false;
+
+			//align to line of highlighted sector, which is closest to hitpos
+			Sector highlightedSector = ((BaseVisualSector)mode.HighlightedObject).Sector;
+			List<Linedef> lines = new List<Linedef>();
+			foreach(Sidedef side in highlightedSector.Sidedefs)	lines.Add(side.Line);
+
+			Linedef targetLine = MapSet.NearestLinedef(lines, hitpos);
+			if(targetLine == null) return;
+
+			foreach(Sidedef side in highlightedSector.Sidedefs) {
+				if(side.Line == targetLine && side.Line.Front != null && side.Line.Front == side) {
+					isFront = true;
+					break;
+				}
+			}
+
+			Sector.Sector.Fields.BeforeFieldsChange();
+
+			//find an angle to rotate texture
+			float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(targetLine.Angle) + 90 : -Angle2D.RadToDeg(targetLine.Angle) - 90), 1);
+			if(!isFront) sourceAngle = General.ClampAngle(sourceAngle + 180);
+			string rotationKey = (geoType == VisualGeometryType.FLOOR ? "rotationfloor" : "rotationceiling");
+
+			//update angle
+			UDMFTools.SetFloat(Sector.Sector.Fields, rotationKey, sourceAngle, 0f, false);
+
+			//update scale. Target should be either floor or ceiling at this point
+			float scaleX = 1.0f;
+			float scaleY = 1.0f;
+
+			if(mode.HighlightedTarget is VisualFloor) {
+				VisualFloor target = mode.HighlightedTarget as VisualFloor;
+				scaleX = target.Sector.Sector.Fields.GetValue("xscalefloor", 1.0f);
+				scaleY = target.Sector.Sector.Fields.GetValue("yscalefloor", 1.0f);
+			} else {
+				VisualCeiling target = mode.HighlightedTarget as VisualCeiling;
+				scaleX = target.Sector.Sector.Fields.GetValue("xscaleceiling", 1.0f);
+				scaleY = target.Sector.Sector.Fields.GetValue("yscaleceiling", 1.0f);
+			}
+
+			string xScaleKey = (geoType == VisualGeometryType.FLOOR ? "xscalefloor" : "xscaleceiling");
+			string yScaleKey = (geoType == VisualGeometryType.FLOOR ? "yscalefloor" : "yscaleceiling");
+
+			//set scale
+			UDMFTools.SetFloat(Sector.Sector.Fields, xScaleKey, scaleX, 1.0f, false);
+			UDMFTools.SetFloat(Sector.Sector.Fields, yScaleKey, scaleY, 1.0f, false);
+
+			//update offset
+			float distToStart = Vector2D.Distance(hitpos, targetLine.Start.Position);
+			float distToEnd = Vector2D.Distance(hitpos, targetLine.End.Position);
+			Vector2D offset = (distToStart < distToEnd ? targetLine.Start.Position : targetLine.End.Position).GetRotated(Angle2D.DegToRad(sourceAngle));
+
+			if(alignx) {
+				if(Texture != null)	offset.x %= Texture.Width / scaleX;
+				UDMFTools.SetFloat(Sector.Sector.Fields, (geoType == VisualGeometryType.FLOOR ? "xpanningfloor" : "xpanningceiling"), (float)Math.Round(-offset.x), 0f, false);
+			}
+
+			if(aligny) {
+				if(Texture != null)	offset.y %= Texture.Height / scaleY;
+				UDMFTools.SetFloat(Sector.Sector.Fields, (geoType == VisualGeometryType.FLOOR ? "ypanningfloor" : "ypanningceiling"), (float)Math.Round(offset.y), 0f, false);
+			}
+
+			//update geometry
+			Sector.UpdateSectorGeometry(false);
+		}
+
+		//mxd
+		protected void alignTextureToSlopeLine(Linedef slopeSource, float slopeAngle, bool isFront, bool alignx, bool aligny) {
+			Vector2D hitpos = mode.GetHitPosition();
+			bool isFloor = (geoType == VisualGeometryType.FLOOR);
+
+			Sector.Sector.Fields.BeforeFieldsChange();
+			
+			float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(slopeSource.Angle) + 90 : -Angle2D.RadToDeg(slopeSource.Angle) - 90), 1);
+
+			if(isFloor) {
+				if((isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight) ||
+				  (!isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight)) {
+					sourceAngle = General.ClampAngle(sourceAngle + 180);
+				}
+			} else {
+				if((isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight) ||
+				  (!isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight)) {
+					sourceAngle = General.ClampAngle(sourceAngle + 180);
+				}
+			}
+
+			//update angle
+			string rotationKey = (isFloor ? "rotationfloor" : "rotationceiling");
+			UDMFTools.SetFloat(Sector.Sector.Fields, rotationKey, sourceAngle, 0f, false);
+
+			//update scaleY
+			string xScaleKey = (isFloor ? "xscalefloor" : "xscaleceiling");
+			string yScaleKey = (isFloor ? "yscalefloor" : "yscaleceiling");
+
+			float scaleX = Sector.Sector.Fields.GetValue(xScaleKey, 1.0f);
+			float scaleY;// = (float)Math.Round(scaleX * (1 / (float)Math.Cos(slopeAngle)), 2);
+
+			//set scale
+			if(aligny) {
+				scaleY = (float)Math.Round(scaleX * (1 / (float)Math.Cos(slopeAngle)), 2);
+				UDMFTools.SetFloat(Sector.Sector.Fields, yScaleKey, scaleY, 1.0f, false);
+			} else {
+				scaleY = Sector.Sector.Fields.GetValue(yScaleKey, 1.0f);
+			}
+
+			//update texture offsets
+			Vector2D offset;
+
+			if(isFloor) {
+				if((isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight) ||
+				  (!isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight)) {
+					offset = slopeSource.End.Position;
+				} else {
+					offset = slopeSource.Start.Position;
+				}
+			} else {
+				if((isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight) ||
+				  (!isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight)) {
+					offset = slopeSource.End.Position;
+				} else {
+					offset = slopeSource.Start.Position;
+				}
+			}
+
+			offset = offset.GetRotated(Angle2D.DegToRad(sourceAngle));
+
+			if(alignx) {
+				if(Texture != null)	offset.x %= Texture.Width / scaleX;
+				UDMFTools.SetFloat(Sector.Sector.Fields, (isFloor ? "xpanningfloor" : "xpanningceiling"), (float)Math.Round(-offset.x), 0f, false);
+			}
+
+			if(aligny) {
+				if(Texture != null)	offset.y %= Texture.Height / scaleY;
+				UDMFTools.SetFloat(Sector.Sector.Fields, (isFloor ? "ypanningfloor" : "ypanningceiling"), (float)Math.Round(offset.y), 0f, false);
+			}
+
+			//update geometry
+			Sector.UpdateSectorGeometry(false);
+		}
 		
 		#endregion
 
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index b9c94b0ea..833d4f62f 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -144,6 +144,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			}
 		}
 
+		//mxd
+		public object HighlightedTarget { get { return target.picked; } }
+
 		new public IRenderer3D Renderer { get { return renderer; } }
 		
 		public bool IsSingleSelection { get { return singleselection; } }
@@ -2316,7 +2319,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
         [BeginAction("insertitem", BaseAction = true)] 
 		public void InsertThing()
 		{
-            Vector2D hitpos = getHitPosition();
+            Vector2D hitpos = GetHitPosition();
 
             if (!hitpos.IsFinite()) {
                 General.Interface.DisplayStatus(StatusType.Warning, "Cannot insert thing here!");
@@ -2386,7 +2389,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
                 return;
             }
             
-            Vector2D hitpos = getHitPosition();
+            Vector2D hitpos = GetHitPosition();
 
             if (!hitpos.IsFinite()) {
                 General.Interface.DisplayStatus(StatusType.Warning, "Cannot paste here!");
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs b/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
index 01f6c0ba6..019486b46 100644
--- a/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
@@ -380,90 +380,33 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		public void AlignTexture(bool alignx, bool aligny) {
 			if(!General.Map.UDMF) return;
 
+			//is is a surface with line slope?
 			float slopeAngle = level.plane.Normal.GetAngleZ() - Angle2D.PIHALF;
 
-			if(slopeAngle == 0)	return; //it's a horizontal plane
-
-			//find slope source linedef
-			Linedef slopeSource = null;
-			bool isFront = false;
-
-			foreach(Sidedef side in Sector.Sector.Sidedefs) {
-				if(side.Line.Action == 181) {
-					if(side.Line.Args[1] == 1 && side.Line.Front != null && side.Line.Front == side) {
-						slopeSource = side.Line;
-						isFront = true;
-						break;
-					} else if(side.Line.Args[1] == 2 && side.Line.Back != null && side.Line.Back == side) {
-						slopeSource = side.Line;
-						break;
+			if(slopeAngle == 0) {//it's a horizontal plane
+				alignTextureToClosestLine(alignx, aligny);
+			} else { //it can be a surface with line slope
+				Linedef slopeSource = null;
+				bool isFront = false;
+
+				foreach(Sidedef side in Sector.Sector.Sidedefs) {
+					if(side.Line.Action == 181) {
+						if(side.Line.Args[1] == 1 && side.Line.Front != null && side.Line.Front == side) {
+							slopeSource = side.Line;
+							isFront = true;
+							break;
+						} else if(side.Line.Args[1] == 2 && side.Line.Back != null && side.Line.Back == side) {
+							slopeSource = side.Line;
+							break;
+						}
 					}
 				}
-			}
-
-			if(slopeSource == null)	return;
-
-			Sector.Sector.Fields.BeforeFieldsChange();
-
-			//match rotation
-			float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(slopeSource.Angle) + 90 : -Angle2D.RadToDeg(slopeSource.Angle) - 90), 1);
-
-			if((isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight) ||
-				(!isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight)) {
-				sourceAngle = General.ClampAngle(sourceAngle + 180);
-			}
 
-			if(sourceAngle != 0) {
-				if(!Sector.Sector.Fields.ContainsKey("rotationceiling"))
-					Sector.Sector.Fields.Add("rotationceiling", new UniValue(UniversalType.Float, sourceAngle));
+				if(slopeSource != null && slopeSource.Front != null && slopeSource.Front.Sector != null && slopeSource.Back != null && slopeSource.Back.Sector != null)
+					alignTextureToSlopeLine(slopeSource, slopeAngle, isFront, alignx, aligny);
 				else
-					Sector.Sector.Fields["rotationceiling"].Value = sourceAngle;
-			} else if(Sector.Sector.Fields.ContainsKey("rotationceiling")) {
-				Sector.Sector.Fields.Remove("rotationceiling");
+					alignTextureToClosestLine(alignx, aligny);
 			}
-
-			//update scaleY
-			float scaleX = Sector.Sector.Fields.GetValue("xscaleceiling", 1.0f);
-			float scaleY = (float)Math.Round(scaleX * (1 / (float)Math.Cos(slopeAngle)), 2);
-
-			if(aligny) {
-				if(Sector.Sector.Fields.ContainsKey("yscaleceiling"))
-					Sector.Sector.Fields["yscaleceiling"].Value = scaleY;
-				else
-					Sector.Sector.Fields.Add("yscaleceiling", new UniValue(UniversalType.Float, scaleY));
-			}
-
-			//update texture offsets
-			Vector2D offset;
-			if((isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight) ||
-			  (!isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight)) {
-				offset = slopeSource.End.Position;
-			} else {
-				offset = slopeSource.Start.Position;
-			}
-
-			offset = offset.GetRotated(Angle2D.DegToRad(sourceAngle));
-
-			if(alignx) {
-				if(Texture != null)	offset.x %= Texture.Width / scaleX;
-
-				if(Sector.Sector.Fields.ContainsKey("xpanningceiling"))
-					Sector.Sector.Fields["xpanningceiling"].Value = (float)Math.Round(-offset.x);
-				else
-					Sector.Sector.Fields.Add("xpanningceiling", new UniValue(UniversalType.Float, (float)Math.Round(-offset.x)));
-			}
-
-			if(aligny) {
-				if(Texture != null)	offset.y %= Texture.Height / scaleY;
-
-				if(Sector.Sector.Fields.ContainsKey("ypanningceiling"))
-					Sector.Sector.Fields["ypanningceiling"].Value = (float)Math.Round(offset.y);
-				else
-					Sector.Sector.Fields.Add("ypanningceiling", new UniValue(UniversalType.Float, (float)Math.Round(offset.y)));
-			}
-
-			//update geometry
-			Sector.UpdateSectorGeometry(false);
 		}
 		
 		#endregion
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs b/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
index f6f1f9c65..6beb26ed4 100644
--- a/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
@@ -337,89 +337,33 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		public void AlignTexture(bool alignx, bool aligny) {
 			if(!General.Map.UDMF) return;
 
+			//is is a surface with line slope?
 			float slopeAngle = level.plane.Normal.GetAngleZ() - Angle2D.PIHALF;
 
-			if(slopeAngle == 0)	return; //it's a horizontal plane
-
-			//find slope source linedef
-			Linedef slopeSource = null;
-			bool isFront = false;
-
-			foreach(Sidedef side in Sector.Sector.Sidedefs) {
-				if(side.Line.Action == 181) {
-					if(side.Line.Args[0] == 1 && side.Line.Front != null && side.Line.Front == side) {
-						slopeSource = side.Line;
-						isFront = true;
-						break;
-					} else if(side.Line.Args[0] == 2 && side.Line.Back != null && side.Line.Back == side) {
-						slopeSource = side.Line;
-						break;
+			if(slopeAngle == 0) {//it's a horizontal plane
+				alignTextureToClosestLine(alignx, aligny);
+			} else { //it can be a surface with line slope
+				Linedef slopeSource = null;
+				bool isFront = false;
+
+				foreach(Sidedef side in Sector.Sector.Sidedefs) {
+					if(side.Line.Action == 181) {
+						if(side.Line.Args[0] == 1 && side.Line.Front != null && side.Line.Front == side) {
+							slopeSource = side.Line;
+							isFront = true;
+							break;
+						} else if(side.Line.Args[0] == 2 && side.Line.Back != null && side.Line.Back == side) {
+							slopeSource = side.Line;
+							break;
+						}
 					}
 				}
-			}
-
-			if(slopeSource == null)	return;
-
-			Sector.Sector.Fields.BeforeFieldsChange();
-
-			float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(slopeSource.Angle) + 90 : -Angle2D.RadToDeg(slopeSource.Angle) - 90), 1);
-
-			if((isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight) ||
-				(!isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight)) {
-				sourceAngle = General.ClampAngle(sourceAngle + 180);
-			}
-
-			if(sourceAngle != 0) {
-				if(!Sector.Sector.Fields.ContainsKey("rotationfloor"))
-					Sector.Sector.Fields.Add("rotationfloor", new UniValue(UniversalType.Float, sourceAngle));
-				else
-					Sector.Sector.Fields["rotationfloor"].Value = sourceAngle;
-			} else if(Sector.Sector.Fields.ContainsKey("rotationfloor")) {
-				Sector.Sector.Fields.Remove("rotationfloor");
-			}
-
-			//update scaleY
-			float scaleX = Sector.Sector.Fields.GetValue("xscalefloor", 1.0f);
-			float scaleY = (float)Math.Round(scaleX * (1 / (float)Math.Cos(slopeAngle)), 2);
-
-			if(aligny) {
-				if(Sector.Sector.Fields.ContainsKey("yscalefloor"))
-					Sector.Sector.Fields["yscalefloor"].Value = scaleY;
-				else
-					Sector.Sector.Fields.Add("yscalefloor", new UniValue(UniversalType.Float, scaleY));
-			}
-
-			//update texture offsets
-			Vector2D offset;
-			if((isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight) ||
-				(!isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight)) {
-				offset = slopeSource.End.Position;
-			} else {
-				offset = slopeSource.Start.Position;
-			}
-
-			offset = offset.GetRotated(Angle2D.DegToRad(sourceAngle));
 
-			if(alignx) {
-				if(Texture != null)	offset.x %= Texture.Width / scaleX;
-
-				if(Sector.Sector.Fields.ContainsKey("xpanningfloor"))
-					Sector.Sector.Fields["xpanningfloor"].Value = (float)Math.Round(-offset.x);
-				else
-					Sector.Sector.Fields.Add("xpanningfloor", new UniValue(UniversalType.Float, (float)Math.Round(-offset.x)));
-			}
-
-			if(aligny) {
-				if(Texture != null) offset.y %= Texture.Height / scaleY;
-
-				if(Sector.Sector.Fields.ContainsKey("ypanningfloor"))
-					Sector.Sector.Fields["ypanningfloor"].Value = (float)Math.Round(offset.y);
+				if(slopeSource != null && slopeSource.Front != null && slopeSource.Front.Sector != null && slopeSource.Back != null && slopeSource.Back.Sector != null)
+					alignTextureToSlopeLine(slopeSource, slopeAngle, isFront, alignx, aligny);
 				else
-					Sector.Sector.Fields.Add("ypanningfloor", new UniValue(UniversalType.Float, (float)Math.Round(offset.y)));
+					alignTextureToClosestLine(alignx, aligny);
 			}
-
-			//update geometry
-			Sector.UpdateSectorGeometry(false);
 		}
 		
 		#endregion
-- 
GitLab