#region ================== Namespaces using System; using System.Reflection; using System.Runtime.Serialization; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Linq; using System.Diagnostics; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Types; #endregion namespace CodeImp.DoomBuilder.ThreeDFloorMode { public class SlopeVertexGroup { #region ================== Variables private List vertices; private List sectors; private Dictionary sectorplanes; private List taggedsectors; // For highlighting 3D floors private int id; private int height; private Vertex anchorvertex; private Vector2D anchor; private bool reposition; private bool spline; #endregion #region ================== Enums #endregion #region ================== Properties public List Vertices { get { return vertices; } set { vertices = value; ComputeHeight(); } } public List Sectors { get { return sectors; } set { sectors = value; } } public Dictionary SectorPlanes { get { return sectorplanes; } } public List TaggedSectors { get { return taggedsectors; } set { taggedsectors = value; } } public int Id { get { return id; } } public int Height { get { return height; } set { height = value; } } public bool Reposition { get { return reposition; } set { reposition = value; } } public bool Spline { get { return spline; }set { spline = value; } } #endregion #region ================== Constructors public SlopeVertexGroup(int id, Sector sector) { List list = new List { "floor", "ceiling" }; Type type = typeof(SlopeVertexGroup); this.id = id; sectors = new List(); sectorplanes = new Dictionary(); taggedsectors = new List(); vertices = new List(); anchorvertex = null; // There will always be at least two slope vertices, so add them here vertices.Add(new SlopeVertex(sector, id, 0)); vertices.Add(new SlopeVertex(sector, id, 1)); // Check if there's a third slope vertex, and add it if there is string vertexidentifier = String.Format("user_svg{0}_v2_x", id); foreach (KeyValuePair kvp in sector.Fields) { if (kvp.Key == vertexidentifier) { vertices.Add(new SlopeVertex(sector, id, 2)); break; } } // Get reposition value reposition = sector.Fields.GetValue(String.Format("user_svg{0}_reposition", id), true); // Get spline value spline = sector.Fields.GetValue(String.Format("user_svg{0}_spline", id), false); spline = false; ComputeHeight(); FindSectors(); } public SlopeVertexGroup(int id, List vertices) { this.vertices = vertices; this.id = id; sectors = new List(); sectorplanes = new Dictionary(); taggedsectors = new List(); anchorvertex = null; height = 0; ComputeHeight(); } #endregion #region ================== Methods public void FindSectors() { if (sectors == null) sectors = new List(); else sectors.Clear(); if (taggedsectors == null) taggedsectors = new List(); else taggedsectors.Clear(); sectorplanes.Clear(); foreach (Sector s in General.Map.Map.Sectors) { bool onfloor = s.Fields.GetValue("user_floorplane_id", -1) == id; bool onceiling = s.Fields.GetValue("user_ceilingplane_id", -1) == id; PlaneType pt = 0; if (!onfloor && !onceiling) continue; sectors.Add(s); if (onfloor && onceiling) pt = PlaneType.Floor | PlaneType.Ceiling; else if (onfloor) pt = PlaneType.Floor; else if (onceiling) pt = PlaneType.Ceiling; sectorplanes.Add(s, pt); GetTaggesSectors(s, pt); } } public void RemovePlanes() { foreach (Sector s in sectors.ToList()) { RemoveSector(s, this.sectorplanes[s]); } } public void RemoveFromSectors() { foreach (Sector s in sectors.ToList()) { RemoveSector(s, PlaneType.Floor); RemoveSector(s, PlaneType.Ceiling); } } private void GetTaggesSectors(Sector s, PlaneType pt) { // Check if the current sector is a 3D floor control sector. If that's the case also store the // tagged sector(s). They will be used for highlighting in slope mode foreach (Sidedef sd in s.Sidedefs) { if (sd.Line.Action >= 100 && sd.Line.Action < 300) { foreach (Sector ts in BuilderPlug.GetSectorsByTag(sd.Line.Args[0])) { if (!taggedsectors.Contains(ts)) taggedsectors.Add(ts); if(!sectorplanes.ContainsKey(ts)) sectorplanes.Add(ts, pt); } } } } public void AddSector(Sector s, PlaneType pt) { if (sectorplanes.ContainsKey(s)) { pt |= sectorplanes[s]; sectorplanes.Remove(s); } if (sectors.Contains(s)) sectors.Remove(s); sectorplanes.Add(s, pt); sectors.Add(s); GetTaggesSectors(s, pt); ApplyToSectors(); } public void RemoveSector(Sector s, PlaneType pt) { Debug.WriteLine("Removing from Sector " + s.Index.ToString() + ": " + pt.ToString()); if (sectorplanes.ContainsKey(s)) { if (sectors.Contains(s) && sectorplanes[s] == pt) { sectors.Remove(s); sectorplanes.Remove(s); } else sectorplanes[s] &= ~pt; } if ((pt & PlaneType.Floor) == PlaneType.Floor) { s.FloorSlope = new Vector3D(); s.FloorSlopeOffset = 0; s.Fields.Remove("user_floorplane_id"); } if ((pt & PlaneType.Ceiling) == PlaneType.Ceiling) { s.CeilSlope = new Vector3D(); s.CeilSlopeOffset = 0; s.Fields.Remove("user_ceilingplane_id"); } } public void RemoveUndoRedoUDMFFields(Sector s) { string fieldname = ""; string[] comp = new string[] { "x", "y", "z" }; if (s == null || s.IsDisposed) return; s.Fields.BeforeFieldsChange(); for (int i = 0; i < 3; i++) { foreach (string c in comp) { fieldname = string.Format("user_svg{0}_v{1}_{2}", id, i, c); if (s.Fields.ContainsKey(fieldname)) s.Fields.Remove(fieldname); } } // Remove reposition field fieldname = string.Format("user_svg{0}_reposition", id); if (s.Fields.ContainsKey(fieldname)) s.Fields.Remove(fieldname); } public void ApplyToSectors() { List removesectors = new List(); ComputeHeight(); foreach (Sector s in sectors) { bool hasplane = false; if (sectorplanes.ContainsKey(s) && (sectorplanes[s] & PlaneType.Floor) == PlaneType.Floor) { hasplane = true; if (s.Fields.ContainsKey("user_floorplane_id")) s.Fields["user_floorplane_id"] = new UniValue(UniversalType.Integer, id); else s.Fields.Add("user_floorplane_id", new UniValue(UniversalType.Integer, id)); } else if (s.Fields.ContainsKey("user_floorplane_id") && s.Fields.GetValue("user_floorplane_id", -1) == id) { s.Fields.Remove("user_floorplane_id"); } if (sectorplanes.ContainsKey(s) && (sectorplanes[s] & PlaneType.Ceiling) == PlaneType.Ceiling) { hasplane = true; if (s.Fields.ContainsKey("user_ceilingplane_id")) s.Fields["user_ceilingplane_id"] = new UniValue(UniversalType.Integer, id); else s.Fields.Add("user_ceilingplane_id", new UniValue(UniversalType.Integer, id)); } else if (s.Fields.ContainsKey("user_ceilingplane_id") && s.Fields.GetValue("user_ceilingplane_id", -1) == id) { s.Fields.Remove("user_ceilingplane_id"); } if (!hasplane) removesectors.Add(s); } foreach (Sector s in removesectors) sectors.Remove(s); foreach (Sector s in sectors) BuilderPlug.Me.UpdateSlopes(s); } public void StoreInSector(Sector sector) { // Make sure the field work with undo/redo sector.Fields.BeforeFieldsChange(); // Also store all slope vertices in the sector for (int i = 0; i < vertices.Count; i++) vertices[i].StoreInSector(sector, id, i); string identifier = String.Format("user_svg{0}_reposition", id); // Add, update, or delete the reposition field if (reposition) { //default action if (sector.Fields.ContainsKey(identifier)) sector.Fields.Remove(identifier); } else { if (sector.Fields.ContainsKey(identifier)) sector.Fields[identifier] = new UniValue(UniversalType.Boolean, reposition); else sector.Fields.Add(identifier, new UniValue(UniversalType.Boolean, reposition)); } // Spline identifier = String.Format("user_svg{0}_spline", id); if (!spline && sector.Fields.ContainsKey(identifier)) sector.Fields.Remove(identifier); else if (spline) { if (sector.Fields.ContainsKey(identifier)) sector.Fields[identifier] = new UniValue(UniversalType.Boolean, spline); else sector.Fields.Add(identifier, new UniValue(UniversalType.Boolean, spline)); } } public void SelectVertices(bool select) { foreach (SlopeVertex sv in vertices) sv.Selected = select; } public bool GetAnchor() { anchorvertex = null; if (sectors.Count == 0) return false; // Try to find a sector that contains a SV /* foreach (Sector s in sectors) { foreach (SlopeVertex sv in vertices) { if (s.Intersect(sv.Pos)) { anchorvertex = s.Sidedefs.First().Line.Start; anchor = new Vector2D(anchorvertex.Position); return true; } } } */ // Just grab the next best vertex foreach (Sector s in sectors) { foreach (Sidedef sd in s.Sidedefs) { anchorvertex = sd.Line.Start; anchor = new Vector2D(anchorvertex.Position); return true; } } return false; } public void RepositionByAnchor() { if (anchorvertex == null || !reposition) return; Vector2D diff = anchorvertex.Position - anchor; if (diff.x == 0.0f && diff.y == 0.0f) return; foreach (SlopeVertex sv in vertices) { sv.Pos += diff; } anchorvertex = null; } public void ComputeHeight() { List sp = new List(); for (int i = 0; i < vertices.Count; i++) { sp.Add(new Vector3D(vertices[i].Pos.x, vertices[i].Pos.y,vertices[i].Z)); } if (vertices.Count == 2) { double z = sp[0].z; Line2D line = new Line2D(sp[0], sp[1]); Vector3D perpendicular = line.GetPerpendicular(); Vector2D v = sp[0] + perpendicular; sp.Add(new Vector3D(v.x, v.y, z)); } Plane p = new Plane(sp[0], sp[1], sp[2], true); double fheight = p.GetZ(GetCircumcenter(sp)); // If something went wrong with computing the height use the height // of the first vertex as a workaround if (double.IsNaN(fheight)) height = Convert.ToInt32(sp[0].z); else height = Convert.ToInt32(fheight); } private Vector2D GetCircumcenter(List points) { double u_ray; Line2D line1 = new Line2D(points[0], points[1]); Line2D line2 = new Line2D(points[2], points[0]); // Perpendicular bisectors Line2D bisector1 = new Line2D(line1.GetCoordinatesAt(0.5f), line1.GetCoordinatesAt(0.5f) + line1.GetPerpendicular()); Line2D bisector2 = new Line2D(line2.GetCoordinatesAt(0.5f), line2.GetCoordinatesAt(0.5f) + line2.GetPerpendicular()); bisector1.GetIntersection(bisector2, out u_ray); return bisector1.GetCoordinatesAt(u_ray); } public bool VerticesAreValid() { if (vertices.Count == 2 && vertices[0].Pos == vertices[1].Pos) return false; if (vertices.Count == 3) { double side = Line2D.GetSideOfLine(vertices[0].Pos, vertices[1].Pos, vertices[3].Pos); if (side == 0.0f) return false; } return true; } #endregion } }