#if FLEET || TESTS using System; using System.Collections.Generic; using VRageMath; namespace AutoMiningScript { public partial class Program { public partial class Dashboard { sealed class RegionView { public Vector3D Center, Up, Right; public double Extent = 1, Area; public bool Search; public int Total, Complete, Scanned, Rays, Sampled, Empty, Unknown; public readonly bool[] Cells = new bool[576]; public readonly List Dots = Data.CreateList(); public readonly List Rocks = Data.CreateList(); public readonly Dictionary RockIds = Data.CreateDictionary(); public Vector2 Project(Vector3D point) { var d = point-Center; return ScreenPoint((float)Data.Dot(d,Right),(float)Data.Dot(d,Up)); } public static double RockArea(ScanRecord rock) => Math.PI*Data.LengthSquared(rock.Size)/4; public void Rock(ScanRecord rock) { if (rock.Asteroid == 0) return; int index; if (RockIds.TryGetValue(rock.Asteroid,out index)) { if (!rock.Sized) return; var old = Rocks[index]; Area -= RockArea(old); if (!old.Sized) Unknown--; Rocks[index] = rock; } else { RockIds.Add(rock.Asteroid,Rocks.Count); Rocks.Add(rock); if (!rock.Sized) Unknown++; } Area += RockArea(rock); } // Raster cells mean "a ray crossed here", never full-cell clearance. // Clip before rasterizing: off-map parallel rays must not paint an edge. public void Ray(ScanRecord scan) { var a = Project(scan.Start); var d = Project(scan.End)-a; double lo=0,hi=1; for (int axis=0;axis<2;axis++) { double x=axis==0?a.X:a.Y, v=axis==0?d.X:d.Y; if (Math.Abs(v)<.00001) { if (Math.Abs(x)>Extent) return; continue; } double t=(-Extent-x)/v,u=(Extent-x)/v; lo=Data.Max(lo,Data.Min(t,u)); hi=Data.Min(hi,Data.Max(t,u)); } if(lo>hi)return; var start=a+d*(float)lo; var span=d*(float)(hi-lo); int steps=Data.Max(1,(int)Math.Ceiling(Data.Max(Math.Abs(span.X),Math.Abs(span.Y))*12/Extent)); for(int n=0;n<=steps;n++) { var q=start+span*((float)n/steps); int x=(int)Data.Clamp(Math.Floor((q.X/Extent+1)*12),0,23),y=(int)Data.Clamp(Math.Floor((q.Y/Extent+1)*12),0,23); Cells[y*24+x]=true; } } } Dictionary mapViews=Data.CreateDictionary(),buildingViews; Dictionary> mapOres=Data.CreateDictionary>(),buildingOres; List mapRegions=Data.CreateList(),buildingRegions,buildingPages; List mapParts=Data.CreateList(),buildingParts; List buildingSource; Job buildingFirst; int jobCursor,buildingDone,completedJobs,pageCursor; int scanTotal,scanComplete,buildingScanTotal,buildingScanComplete; double nextJobRefresh; void RefreshJobs(RoleLogic role) { var jobs=role.GetJobs(); Job first=jobs.Count==0?null:jobs[0]; var replaced=jobs!=buildingSource || first!=buildingFirst; if(buildingViews==null && p.Nowjobs.Count) { buildingSource=jobs; buildingFirst=first; buildingViews=Data.CreateDictionary(); buildingOres=Data.CreateDictionary>();buildingScanTotal=buildingScanComplete=0; buildingRegions=Data.CreateList();buildingPages=Data.CreateList();buildingParts=Data.CreateList();jobCursor=buildingDone=pageCursor=0; } int work=0; while(jobCursor0) { List ores;if(!buildingOres.TryGetValue(asteroid,out ores)){ores=Data.CreateList();buildingOres[asteroid]=ores;} foreach(var item in j.ConfirmedOres.Split(',')){var ore=item.Trim();if(ore.Length>0&&!ores.Contains(ore))ores.Add(ore);} } } if(jobCursor value>=1e6?F(value/1e6,"0.00")+" km2":F(value,"0")+" m2"; static readonly string[] MapOreNames={"Iron","Cobalt","Nickel","Silicon","Magnesium","Silver","Gold","Platinum","Uranium","Ice","Stone"}; static readonly string[] MapOreSymbols={"Fe","Co","Ni","Si","Mg","Ag","Au","Pt","U","Ice","St"}; static readonly Color[] MapOreColors={Rgb(220,120,85),Rgb(80,145,255),Rgb(145,200,120),Rgb(245,190,175),Rgb(245,135,220),Rgb(220,225,230),Rgb(255,205,65),Rgb(190,165,250),Rgb(170,245,70),Rgb(90,225,240),Rgb(160,140,115)}; static Color OreColor(string ore){int n=Array.IndexOf(MapOreNames,ore);return n<0?White:MapOreColors[n];} static Vector2 CircleDirection(int step) {double angle=step*Math.PI/16;return ScreenPoint((float)Math.Cos(angle),(float)Math.Sin(angle));} void Map(Canvas c,List fleet,int listPage) { if(mapRegions.Count==0){c.Text(L.DisplayNoArea,22,110,.55f,Amber);return;} int page=listPage%mapRegions.Count;var id=mapRegions[page];var view=mapViews[id]; c.Text(view.Search?L.DisplayExplorationMap:L.DisplayMiningPlane,22,93,.6f,White); c.TextFit(view.Search?L.F(L.DisplayScanSummary,view.Scanned,view.Rays,view.Rocks.Count,view.Unknown):L.F(L.DisplayMapLegend,view.Sampled,view.Empty),22,120,c.Width-44,.40f,Blue); c.TextFit(L.F(L.DisplayScanProgress,Percent(scanTotal==0?0:(double)scanComplete/scanTotal),scanComplete,scanTotal),22,139,c.Width-44,.36f,Green); c.Box(22,154,c.Width-44,3,Panel);c.Box(22,154,scanTotal==0?0:(c.Width-44)*scanComplete/scanTotal,3,Green); float left=22,top=165,width=c.Width-44,height=c.Height-288,bottom=top+height; var origin=ScreenPoint(c.Width/2,top+height/2);var scale=(float)((Data.Min(width,height)-12)/(2*view.Extent)); c.Box(left,top,width,height,Panel); c.Line(ScreenPoint(left,origin.Y),ScreenPoint(left+width,origin.Y),Background); c.Line(ScreenPoint(origin.X,top),ScreenPoint(origin.X,bottom),Background); if(view.Search) { var cell=(float)(view.Extent/12)*scale; for(int n=0;n<576;n++)if(view.Cells[n])c.Box(origin.X+(n%24-12)*cell,origin.Y-(n/24-11)*cell,cell-0.4f,cell-0.4f,Rgb(34,84,113)); for(int n=0;n<32;n++) { c.Line(origin+CircleDirection(n)*(float)view.Extent*scale,origin+CircleDirection(n+1)*(float)view.Extent*scale,Muted); } } var dots=new HashSet(); foreach(var j in view.Dots) { var point=origin+view.Project(j.Entry)*ScreenPoint(scale,-scale); int cell=(int)(point.X/3)+(int)(point.Y/3)*4096; if(!dots.Add(cell))continue; c.Dot(point.X,point.Y,2,j.Outcome=="Blocked"||j.Outcome=="InvalidSample"?Red:j.Owner.Length>0?Amber:Finished(j)?Green:Muted); } var slots=new bool[32]; int start=mapParts[page]*8,end=Data.Min(view.Rocks.Count,start+8); for(int n=start;n ores;if(!mapOres.TryGetValue(rock.Asteroid,out ores)||ores.Count==0)ores=null; var color=ores==null?Muted:OreColor(ores[0]); float radius=rock.Sized?(float)Data.Length(rock.Size)*scale/2:4; for(int k=0;k<32;k++) { var from=point+CircleDirection(k)*radius; var to=point+CircleDirection(k+1)*radius; if(from.X>=left&&from.X<=left+width&&from.Y>=top&&from.Y<=bottom&&to.X>=left&&to.X<=left+width&&to.Y>=top&&to.Y<=bottom) c.Line(from,to,ores==null?Muted:OreColor(ores[k*ores.Count/32]),2); } point.X=(float)Data.Clamp(point.X,left+4,left+width-4);point.Y=(float)Data.Clamp(point.Y,top+3,bottom-3); float y=top+(n-start)*height/8;slots[16+(n-start)*2]=slots[17+(n-start)*2]=true; c.Line(point,ScreenPoint(left+width-174,y+7),color); c.Box(left+width-173,y,173,26,Background); c.Text("A"+(n+1)+" "+(rock.Sized?"~"+Area(RegionView.RockArea(rock)):"?"),left+width-170,y,.36f,color); if(ores!=null)for(int k=0;kleft+width||point.Ybottom; point.X=(float)Data.Clamp(point.X,left+3,left+width-3);point.Y=(float)Data.Clamp(point.Y,top+3,bottom-3); int bank=point.X>origin.X?16:0,row=(int)Data.Clamp((point.Y-top)*16/height,0,15),slot=-1; for(int n=0;n<32;n++){int candidate=(bank+row+n)%32;if(!slots[candidate]){slot=candidate;break;}} if(slot<0)break;slots[slot]=true; float x=slot<16?left+5:left+width-170,y=top+(slot%16)*height/16; var color=!Fresh(t)||t.State==FlightState.Fault?Red:Blue; c.Line(point,ScreenPoint(slot<16?x+164:x,y+7),color); c.Text("+",point.X,point.Y,.5f,color); c.Box(x-2,y-1,169,Data.Max(13,c.TextHeight(.36f)+2),Background); c.TextFit((outside?"*":"")+t.Id+(Fresh(t)?"":" / "+L.DisplayOffline),x,y,165,.36f,color); } c.TextFit(id+" "+view.Complete+"/"+view.Total+" +/- "+F(view.Extent,"0")+"m",22,bottom+8,width,.40f,White); c.TextFit(L.F(L.DisplayRockArea,Area(view.Area),view.Rocks.Count==0?0:start+1,end,view.Rocks.Count),22,bottom+28,width,.38f,Amber); c.TextFit(L.DisplayScanLegend,22,bottom+49,width,.36f,Blue); c.TextFit(L.DisplayScanCaution,22,bottom+69,width,.34f,Muted); } } } } #endif