// Constructor public NearestLineBlockmap(RectangleF range) : base(range) { List <Linedef> singlesided = new List <Linedef>(General.Map.Map.Linedefs.Count); foreach (Linedef ld in General.Map.Map.Linedefs) { if (ld.Back == null) { singlesided.Add(ld); } } AddLinedefsSet(singlesided); // Blocks that do not have any linedefs in them must get the nearest line in them! for (int x = 0; x < size.Width; x++) { for (int y = 0; y < size.Height; y++) { BlockEntry be = blockmap[x, y]; if (be.Lines.Count == 0) { Vector2D bc = GetBlockCenter(new Point(x, y)); Linedef ld = MapSet.NearestLinedef(singlesided, bc); be.Lines.Add(ld); } } } }
//mxd private void CreateTiles() { Point lt = TileForPoint(mapbounds.Left - Tile.TILE_SIZE, mapbounds.Top - Tile.TILE_SIZE); Point rb = TileForPoint(mapbounds.Right + Tile.TILE_SIZE, mapbounds.Bottom + Tile.TILE_SIZE); Rectangle tilesrect = new Rectangle(lt.X, lt.Y, rb.X - lt.X, rb.Y - lt.Y); NearestLineBlockmap blockmap = new NearestLineBlockmap(tilesrect); for (int x = tilesrect.X; x <= tilesrect.Right; x += Tile.TILE_SIZE) { for (int y = tilesrect.Y; y <= tilesrect.Bottom; y += Tile.TILE_SIZE) { // If the tile is obviously outside the map, don't create it Vector2D pc = new Vector2D(x + (Tile.TILE_SIZE >> 1), y + (Tile.TILE_SIZE >> 1)); Linedef ld = MapSet.NearestLinedef(blockmap.GetBlockAt(pc).Lines, pc); double distancesq = ld.DistanceToSq(pc, true); if (distancesq > (Tile.TILE_SIZE * Tile.TILE_SIZE)) { double side = ld.SideOfLine(pc); if ((side > 0.0f) && (ld.Back == null)) { continue; } } Point tp = new Point(x, y); tiles.Add(tp, new Tile(tp)); } } }
//mxd public override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); closestline = MapSet.NearestLinedef(selectedlines, mousedownmappos); // Special cases... int distance; if(panel.FixedCurve) { if(panel.Angle > 0) { // Calculate diameter for current angle... double ma = Angle2D.DegToRad(panel.Angle); double d = (closestline.Length / Math.Tan(ma / 2f)) / 2; double D = d / Math.Cos(ma / 2f); distance = (int)Math.Round(D - d) * Math.Sign(panel.Distance); } else { distance = 0; // Special cases... } } else { distance = panel.Distance; } // Store offset between intial mouse position and curve top Vector2D perpendicular = closestline.Line.GetPerpendicular().GetNormal(); if(distance != 0) perpendicular *= distance; // Special cases... Vector2D curvetop = closestline.GetCenterPoint() - perpendicular; mousedownoffset = mousedownmappos - curvetop; }
// This determines which sector the thing is in and links it public void DetermineSector() { //TODO: check if the sprite vertical coords are inside found sector, because overlapping sectors are a thing //mxd. First check if the sprite is still in the same sector if (sector != null && !sector.IsDisposed && sector.Intersect(pos)) { return; } // Find the nearest linedef on the map Linedef nl = map.NearestLinedef(pos); if (nl != null) { // Check what side of line we are at if (nl.SideOfLine(pos) < 0f) { // Front side sector = (nl.Front != null ? nl.Front.Sector : null); } else { // Back side sector = (nl.Back != null ? nl.Back.Sector : null); } } else { sector = null; } }
public static LuaLinedef NearestLinedef(LuaVector2D pos) { Linedef l = MapSet.NearestLinedef(General.Map.Map.Linedefs, pos.vec); if (l == null) { return null; } return new LuaLinedef(l); }
/// <summary> /// Determines what sector a thing is in, given a blockmap /// </summary> /// <param name="blockmap">The blockmap to use</param> public void DetermineSector(BlockMap <BlockEntry> blockmap) { BlockEntry be = blockmap.GetBlockAt(pos); List <Sector> sectors = new List <Sector>(1); foreach (Sector s in be.Sectors) { if (s.Intersect(pos)) { sectors.Add(s); } } if (sectors.Count == 0) { sector = null; } else if (sectors.Count == 1) { sector = sectors[0]; } else { // Having multiple intersections indicates that there are self-referencing sectors in this spot. // In this case we have to check which side of the nearest linedef pos is on, and then use that sector HashSet <Linedef> linedefs = new HashSet <Linedef>(sectors[0].Sidedefs.Count * sectors.Count); foreach (Sector s in sectors) { foreach (Sidedef sd in s.Sidedefs) { linedefs.Add(sd.Line); } } Linedef nearest = MapSet.NearestLinedef(linedefs, pos); double d = nearest.SideOfLine(pos); if (d <= 0.0 && nearest.Front != null) { sector = nearest.Front.Sector; } else if (nearest.Back != null) { sector = nearest.Back.Sector; } else { sector = null; } } }
public Sector GetSectorAt(Vector2D pos) { List <Sector> sectors = new List <Sector>(1); foreach (VisualBlockEntry e in GetBlocks(pos)) { foreach (Sector s in e.Sectors) { if (s.Intersect(pos)) { sectors.Add(s); } } } if (sectors.Count == 0) { return(null); } else if (sectors.Count == 1) { return(sectors[0]); } else { // Having multiple intersections indicates that there are self-referencing sectors in this spot. // In this case we have to check which side of the nearest linedef pos is on, and then use that sector HashSet <Linedef> linedefs = new HashSet <Linedef>(sectors[0].Sidedefs.Count * sectors.Count); foreach (Sector s in sectors) { foreach (Sidedef sd in s.Sidedefs) { linedefs.Add(sd.Line); } } Linedef nearest = MapSet.NearestLinedef(linedefs, pos); double d = nearest.SideOfLine(pos); if (d <= 0.0 && nearest.Front != null) { return(nearest.Front.Sector); } else if (nearest.Back != null) { return(nearest.Back.Sector); } } return(null); }
// This determines which sector the thing is in and links it public void DetermineSector() { Linedef nl; // Find the nearest linedef on the map nl = map.NearestLinedef(pos); if (nl != null) { // Check what side of line we are at if (nl.SideOfLine(pos) < 0f) { // Front side if (nl.Front != null) { sector = nl.Front.Sector; } else { sector = null; } } else { // Back side if (nl.Back != null) { sector = nl.Back.Sector; } else { sector = null; } } } else { sector = null; } }
// This preforms visibility culling protected void DoCulling() { HashSet <Linedef> visiblelines = new HashSet <Linedef>(); Vector2D campos2d = General.Map.VisualCamera.Position; // Make collections visiblesectors = new Dictionary <Sector, VisualSector>(visiblesectors.Count); visiblegeometry = new List <VisualGeometry>(visiblegeometry.Capacity); visiblethings = new List <VisualThing>(visiblethings.Capacity); // Get the blocks within view range visibleblocks = blockmap.GetFrustumRange(renderer.Frustum2D); // Fill collections with geometry and things foreach (VisualBlockEntry block in visibleblocks) { if (processgeometry) { // Lines foreach (Linedef ld in block.Lines) { // Line not already processed? if (!visiblelines.Contains(ld)) { // Add line if not added yet visiblelines.Add(ld); // Which side of the line is the camera on? if (ld.SideOfLine(campos2d) < 0) { // Do front of line if (ld.Front != null) { ProcessSidedefCulling(ld.Front); } } else { // Do back of line if (ld.Back != null) { ProcessSidedefCulling(ld.Back); } } } } } if (processthings) { // Things foreach (Thing t in block.Things) { // Not filtered out? if (General.Map.ThingsFilter.IsThingVisible(t)) { VisualThing vt; if (allthings.ContainsKey(t)) { vt = allthings[t]; } else { // Create new visual thing vt = CreateVisualThing(t); allthings.Add(t, vt); } if (vt != null) { visiblethings.Add(vt); } } } } } if (processgeometry) { // Find camera sector Linedef nld = MapSet.NearestLinedef(visiblelines, campos2d); if (nld != null) { General.Map.VisualCamera.Sector = GetCameraSectorFromLinedef(nld); } else { // Exceptional case: no lines found in any nearby blocks! // This could happen in the middle of an extremely large sector and in this case // the above code will not have found any sectors/sidedefs for rendering. // Here we handle this special case with brute-force. Let's find the sector // the camera is in by searching the entire map and render that sector only. nld = General.Map.Map.NearestLinedef(campos2d); if (nld != null) { General.Map.VisualCamera.Sector = GetCameraSectorFromLinedef(nld); if (General.Map.VisualCamera.Sector != null) { foreach (Sidedef sd in General.Map.VisualCamera.Sector.Sidedefs) { float side = sd.Line.SideOfLine(campos2d); if (((side < 0) && sd.IsFront) || ((side > 0) && !sd.IsFront)) { ProcessSidedefCulling(sd); } } } else { // Too far away from the map to see anything General.Map.VisualCamera.Sector = null; } } else { // Map is empty General.Map.VisualCamera.Sector = null; } } } }
//mxd protected void AlignTextureToClosestLine(bool alignx, bool aligny) { if (!(mode.HighlightedObject is BaseVisualSector)) { return; } // Do we need to align this? (and also grab texture scale while we are at it) float scaleX, scaleY; bool isFloor = (geometrytype == VisualGeometryType.FLOOR); if (mode.HighlightedTarget is VisualFloor) { VisualFloor target = (VisualFloor)mode.HighlightedTarget; // Check texture if (target.Sector.Sector.FloorTexture != (isFloor ? Sector.Sector.FloorTexture : Sector.Sector.CeilTexture)) { return; } scaleX = target.Sector.Sector.Fields.GetValue("xscalefloor", 1.0f); scaleY = target.Sector.Sector.Fields.GetValue("yscalefloor", 1.0f); } else { VisualCeiling target = (VisualCeiling)mode.HighlightedTarget; // Check texture if (target.Sector.Sector.CeilTexture != (isFloor ? Sector.Sector.FloorTexture : Sector.Sector.CeilTexture)) { return; } scaleX = target.Sector.Sector.Fields.GetValue("xscaleceiling", 1.0f); scaleY = target.Sector.Sector.Fields.GetValue("yscaleceiling", 1.0f); } //find a linedef to align to Vector2D hitpos = mode.GetHitPosition(); if (!hitpos.IsFinite()) { return; } //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; } bool isFront = targetLine.SideOfLine(hitpos) > 0; 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); } //update angle UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "rotationfloor" : "rotationceiling"), sourceAngle, 0f); //set scale UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xscalefloor" : "xscaleceiling"), scaleX, 1.0f); UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "yscalefloor" : "yscaleceiling"), scaleY, 1.0f); //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 && Texture.IsImageLoaded) { offset.x %= Texture.Width / scaleX; } UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xpanningfloor" : "xpanningceiling"), (float)Math.Round(-offset.x), 0f); } if (aligny) { if (Texture != null && Texture.IsImageLoaded) { offset.y %= Texture.Height / scaleY; } UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "ypanningfloor" : "ypanningceiling"), (float)Math.Round(offset.y), 0f); } //update geometry Sector.UpdateSectorGeometry(false); }
// Mode is engaged public override void OnEngage() { base.OnEngage(); // Update projection (mxd) General.Map.CRenderer3D.CreateProjection(); // Update the used textures General.Map.Data.UpdateUsedTextures(); // Fill the blockmap FillBlockMap(); //mxd. Synch camera position to cursor position or center of the screen in 2d-mode if (General.Settings.GZSynchCameras) { // Keep previous camera position if Control is held and camera was previously moved in Visual mode if (!General.Interface.CtrlState || General.Map.VisualCamera.Position.GetLengthSq() == 0) { //If initial position is inside or nearby a sector - adjust camera.z accordingly float posz = General.Map.VisualCamera.Position.z; Sector nearestsector = General.Map.Map.GetSectorByCoordinates(initialcameraposition, blockmap); if (nearestsector == null) { Linedef nearestline = MapSet.NearestLinedef(General.Map.Map.Linedefs, initialcameraposition); if (nearestline != null) { float side = nearestline.SideOfLine(initialcameraposition); Sidedef nearestside = (side < 0.0f ? nearestline.Front : nearestline.Back) ?? (side < 0.0f ? nearestline.Back : nearestline.Front); if (nearestside != null) { nearestsector = nearestside.Sector; } } } if (nearestsector != null) { int sectorheight = nearestsector.CeilHeight - nearestsector.FloorHeight; if (sectorheight < 41) { posz = nearestsector.FloorHeight + Math.Max(16, sectorheight / 2); } else if (General.Map.VisualCamera.Position.z < nearestsector.FloorHeight + 41) { posz = nearestsector.FloorHeight + 41; // same as in doom } else if (General.Map.VisualCamera.Position.z > nearestsector.CeilHeight) { posz = nearestsector.CeilHeight - 4; } } General.Map.VisualCamera.Position = new Vector3D(initialcameraposition.x, initialcameraposition.y, posz); } } else { General.Map.VisualCamera.PositionAtThing(); } // Start special input mode General.Interface.EnableProcessing(); General.Interface.StartExclusiveMouseInput(); }
//mxd protected void AlignTextureToClosestLine(bool alignx, bool aligny) { if (!(mode.HighlightedObject is BaseVisualSector)) { return; } // Do we need to align this? (and also grab texture scale while we are at it) float scaleX, scaleY; bool isFloor = (geometrytype == VisualGeometryType.FLOOR); if (mode.HighlightedTarget is VisualFloor) { Sector target; VisualFloor vf = (VisualFloor)mode.HighlightedTarget; // Use the control sector if the floor belongs to a 3D floor if (vf.ExtraFloor == null) { target = vf.Sector.Sector; } else { target = vf.GetControlSector(); } // Check texture if (target.FloorTexture != (isFloor ? Sector.Sector.FloorTexture : Sector.Sector.CeilTexture)) { return; } scaleX = target.Fields.GetValue("xscalefloor", 1.0f); scaleY = target.Fields.GetValue("yscalefloor", 1.0f); } else { Sector target; VisualCeiling vc = (VisualCeiling)mode.HighlightedTarget; // Use the control sector if the ceiling belongs to a 3D floor if (vc.ExtraFloor == null) { target = vc.Sector.Sector; } else { target = vc.GetControlSector(); } // Check texture if (target.CeilTexture != (isFloor ? Sector.Sector.FloorTexture : Sector.Sector.CeilTexture)) { return; } scaleX = target.Fields.GetValue("xscaleceiling", 1.0f); scaleY = target.Fields.GetValue("yscaleceiling", 1.0f); } //find a linedef to align to Vector2D hitpos = mode.GetHitPosition(); if (!hitpos.IsFinite()) { return; } //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; } bool isFront = targetLine.SideOfLine(hitpos) > 0; 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); } //update angle UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "rotationfloor" : "rotationceiling"), sourceAngle, 0f); // Scale texture if it's a slope and the appropriate option is set if (level.plane.Normal.z != 1.0f && BuilderPlug.Me.ScaleTexturesOnSlopes != 2) { Vector2D basescale = new Vector2D(1.0f, 1.0f); // User wants to use the current scale as a base? if (BuilderPlug.Me.ScaleTexturesOnSlopes == 1) { basescale.x = scaleX; basescale.y = scaleY; } // Create a unit vector of the direction of the target line in 3D space Vector3D targetlinevector = new Line3D(new Vector3D(targetLine.Start.Position, level.plane.GetZ(targetLine.Start.Position)), new Vector3D(targetLine.End.Position, level.plane.GetZ(targetLine.End.Position))).GetDelta().GetNormal(); // Get a perpendicular vector of the target line in 3D space. This is used to get the slope angle relative to the target line Vector3D targetlineperpendicular = Vector3D.CrossProduct(targetlinevector, level.plane.Normal); if (alignx) { scaleX = Math.Abs(basescale.x * (1.0f / (float)Math.Cos(targetlinevector.GetAngleZ()))); } if (aligny) { scaleY = Math.Abs(basescale.y * (1.0f / (float)Math.Cos(targetlineperpendicular.GetAngleZ()))); } } //set scale UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xscalefloor" : "xscaleceiling"), scaleX, 1.0f); UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "yscalefloor" : "yscaleceiling"), scaleY, 1.0f); //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 && Texture.IsImageLoaded) { offset.x %= Texture.Width / scaleX; } UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xpanningfloor" : "xpanningceiling"), (float)Math.Round(-offset.x), 0f); } if (aligny) { if (Texture != null && Texture.IsImageLoaded) { offset.y %= Texture.Height / scaleY; } UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "ypanningfloor" : "ypanningceiling"), (float)Math.Round(offset.y), 0f); } //update geometry Sector.UpdateSectorGeometry(false); }
// Mode starts public override void OnEngage() { Cursor.Current = Cursors.WaitCursor; base.OnEngage(); General.Interface.DisplayStatus(StatusType.Busy, "Setting up test environment..."); CleanUp(); BuilderPlug.InterfaceForm.AddToInterface(); lastviewstats = BuilderPlug.InterfaceForm.ViewStats; // Export the current map to a temporary WAD file tempfile = BuilderPlug.MakeTempFilename(".wad"); General.Map.ExportToFile(tempfile); // Load the map in VPO_DLL BuilderPlug.VPO.Start(tempfile, General.Map.Options.LevelName); // Determine map boundary mapbounds = Rectangle.Round(MapSet.CreateArea(General.Map.Map.Vertices)); // Create tiles for all points inside the map Point lt = TileForPoint(mapbounds.Left - Tile.TILE_SIZE, mapbounds.Top - Tile.TILE_SIZE); Point rb = TileForPoint(mapbounds.Right + Tile.TILE_SIZE, mapbounds.Bottom + Tile.TILE_SIZE); Rectangle tilesrect = new Rectangle(lt.X, lt.Y, rb.X - lt.X, rb.Y - lt.Y); NearestLineBlockmap blockmap = new NearestLineBlockmap(tilesrect); for (int x = tilesrect.X; x <= tilesrect.Right; x += Tile.TILE_SIZE) { for (int y = tilesrect.Y; y <= tilesrect.Bottom; y += Tile.TILE_SIZE) { // If the tile is obviously outside the map, don't create it Vector2D pc = new Vector2D(x + (Tile.TILE_SIZE >> 1), y + (Tile.TILE_SIZE >> 1)); Linedef ld = MapSet.NearestLinedef(blockmap.GetBlockAt(pc).Lines, pc); float distancesq = ld.DistanceToSq(pc, true); if (distancesq > (Tile.TILE_SIZE * Tile.TILE_SIZE)) { float side = ld.SideOfLine(pc); if ((side > 0.0f) && (ld.Back == null)) { continue; } } Point tp = new Point(x, y); tiles.Add(tp, new Tile(tp)); } } QueuePoints(0); // Make an image to draw on. // The BitmapImage for Doom Builder's resources must be Format32bppArgb and NOT using color correction, // otherwise DB will make a copy of the bitmap when LoadImage() is called! This is normally not a problem, // but we want to keep drawing to the same bitmap. int width = General.NextPowerOf2(General.Interface.Display.ClientSize.Width); int height = General.NextPowerOf2(General.Interface.Display.ClientSize.Height); canvas = new Bitmap(width, height, PixelFormat.Format32bppArgb); image = new DynamicBitmapImage(canvas, "_CANVAS_"); image.UseColorCorrection = false; image.MipMapLevels = 1; image.LoadImage(); image.CreateTexture(); // Make custom presentation CustomPresentation p = new CustomPresentation(); p.AddLayer(new PresentLayer(RendererLayer.Overlay, BlendingMode.Mask, 1f, false)); p.AddLayer(new PresentLayer(RendererLayer.Grid, BlendingMode.Mask)); p.AddLayer(new PresentLayer(RendererLayer.Geometry, BlendingMode.Alpha, 1f, true)); renderer.SetPresentation(p); // Setup processing nextupdate = DateTime.Now + new TimeSpan(0, 0, 0, 0, 100); General.Interface.EnableProcessing(); processingenabled = true; RedrawAllTiles(); Cursor.Current = Cursors.Default; General.Interface.SetCursor(Cursors.Cross); General.Interface.DisplayReady(); }
// This finds the line closest to the specified position public Linedef NearestLinedef(Vector2D pos) { return(MapSet.NearestLinedef(linedefs, pos)); }
// Mouse moves public override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); // Not holding any buttons? if (e.Button == MouseButtons.None) { General.Interface.SetCursor(Cursors.Default); //mxd. Find the nearest linedef within default highlight range Linedef nl = MapSet.NearestLinedefRange(blockmap, mousemappos, 20 / renderer.Scale); //mxd. We are not interested in single-sided lines (unless they have "blocksound" flag set)... if (nl != null && (nl.Front == null || nl.Back == null) && !nl.IsFlagSet(BlockSoundFlag)) { nl = null; } //mxd. Set as highlighted bool redrawrequired = (highlightedline != nl); highlightedline = nl; // Find the nearest linedef within highlight range Linedef l = MapSet.NearestLinedef(blockmap, mousemappos); if (l != null) { // Check on which side of the linedef the mouse is double side = l.SideOfLine(mousemappos); if (side > 0) { // Is there a sidedef here? if (l.Back != null) { // Highlight if not the same if (l.Back.Sector != highlighted) { Highlight(l.Back.Sector); redrawrequired = true; //mxd } } else if (highlighted != null) { // Highlight nothing Highlight(null); redrawrequired = true; //mxd } } else { // Is there a sidedef here? if (l.Front != null) { // Highlight if not the same if (l.Front.Sector != highlighted) { Highlight(l.Front.Sector); redrawrequired = true; //mxd } } else if (highlighted != null) { // Highlight nothing Highlight(null); redrawrequired = true; //mxd } } } else if (highlighted != null) { // Highlight nothing Highlight(null); redrawrequired = true; //mxd } //mxd if (redrawrequired) { // Show highlight info if (highlightedline != null && !highlightedline.IsDisposed) { General.Interface.ShowLinedefInfo(highlightedline); } else if (highlighted != null && !highlighted.IsDisposed) { General.Interface.ShowSectorInfo(highlighted); } else { General.Interface.HideInfo(); } // Redraw display General.Interface.RedrawDisplay(); } } }