OSDN Git Service

W.I.P. #2 Designators for POI - partly implimented, and more
authormelchior <melchior@users.osdn.me>
Fri, 6 Dec 2019 01:46:24 +0000 (20:46 -0500)
committermelchior <melchior@users.osdn.me>
Fri, 6 Dec 2019 01:46:24 +0000 (20:46 -0500)
Automap/Automap.csproj
Automap/AutomapMod.cs
Automap/Data/Designator.cs [new file with mode: 0644]
Automap/Data/PointOfInterest.cs [new file with mode: 0644]
Automap/Helpers.cs [new file with mode: 0644]

index ba82df0..e5e023d 100644 (file)
   <ItemGroup>
     <Compile Include="AutomapMod.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Helpers.cs" />
+    <Compile Include="Data\PointOfInterest.cs" />
+    <Compile Include="Data\Designator.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="VS_libs\" />
+    <Folder Include="Data\" />
   </ItemGroup>
   <ItemGroup>
     <None Include="modinfo.json">
index 73fc203..dd5e950 100644 (file)
@@ -6,7 +6,7 @@ using System.Threading;
 using System.Drawing;
 using System.Drawing.Imaging;
 using System.Linq;
-
+using System.Collections.Concurrent;
 
 using Vintagestory.API.Client;
 using Vintagestory.API.Common;
@@ -18,6 +18,7 @@ using Vintagestory.GameContent;
 using Vintagestory.API.Datastructures;
 
 
+
 namespace Automap
 {
        public class AutomapMod : ModSystem
@@ -26,14 +27,16 @@ namespace Automap
                private ICoreClientAPI ClientAPI { get; set; }
                private ILogger Logger { get; set; }
 
+               private Thread cartographer_thread;
+
                private const string _mapPath = @"Maps";
                private const string _chunkPath = @"Chunks";
-               private readonly object colLock = new object( );
-               private Dictionary<Vec2i, uint> columnCounter = new Dictionary<Vec2i, uint>();
+
+               private ConcurrentDictionary<Vec2i, uint> columnCounter = new ConcurrentDictionary<Vec2i, uint>();
                private HashSet<Vec2i> knownChunkTops = new HashSet<Vec2i>();
                private Vec2i startPosition;
-
-               private Thread cartographer_thread;
+               private List<PointOfInterest> POIs;
+               private Dictionary<int,Designator> BlockID_Designators;
 
                public override bool ShouldLoad(EnumAppSide forSide)
                {
@@ -47,7 +50,8 @@ namespace Automap
                        if (api.Side == EnumAppSide.Client) {
                        this.ClientAPI = api as ICoreClientAPI;
                        this.Logger = Mod.Logger;
-                       
+
+                       ClientAPI.Logger.VerboseDebug("Automap Present!");
                        ClientAPI.Event.LevelFinalize += StartAutomap;
                        }
 
@@ -64,8 +68,9 @@ namespace Automap
                #region Internals
                private void StartAutomap( )
                {
-               Mod.Logger.Notification("AUTOMAP SETUP");
-               startPosition = new Vec2i(ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X, ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z);
+               Prefill_POI_Designators( );
+               startPosition = new Vec2i((ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X / ClientAPI.World.BlockAccessor.ChunkSize), (ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z/ ClientAPI.World.BlockAccessor.ChunkSize));
+               Logger.Notification("AUTOMAP Start {0}", startPosition);
                ClientAPI.Event.ChunkDirty += ChunkAChanging;
                
                cartographer_thread = new Thread(Cartographer);
@@ -93,6 +98,8 @@ namespace Automap
                        //Time to (re)write chunk shards
                        cartographer_thread.Interrupt( );
                        }
+               
+               ClientAPI.TriggerChatMessage($"Automap processed {knownChunkTops.Count} chunks.");
                }
 
                }
@@ -100,37 +107,52 @@ namespace Automap
 
                private void Cartographer( )
                {
-       wake:
+               wake:
                Logger.VerboseDebug("Cartographer thread awoken");
 
                try {
-
+                       uint ejectedItem = 0;
+                       
                        while (columnCounter.Count > 0) 
                        {
                        var mostActiveCol = columnCounter.OrderByDescending(kvp => kvp.Value).First();
                        var mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(mostActiveCol.Key);
-                       Logger.VerboseDebug("Selected: ({0}) - Edits#:{1}", mostActiveCol.Key, mostActiveCol.Value);
+                       
 
                        if (mapChunk == null) {
                                Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!",mostActiveCol.Key);
-                               columnCounter.Remove(mostActiveCol.Key);
+                               
+                               columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
                                continue;
                        }
 
                        string filename = $"{mostActiveCol.Key.X}_{mostActiveCol.Key.Y}.png";
-
-                       filename = Path.Combine(ClientAPI.GetOrCreateDataPath(_mapPath), filename);
-
-                       var chkImg = GenerateChunkImage(mostActiveCol.Key, mapChunk);
+                       string path = ClientAPI.GetOrCreateDataPath(_mapPath);
+                       path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));
+                                                               
+                       filename = Path.Combine(path, filename);
                        
+                       uint pixels = 0;
+                       var chkImg = GenerateChunkImage(mostActiveCol.Key, mapChunk, out pixels);
+
+                       if (pixels > 0) {
                        chkImg.Save(filename, ImageFormat.Png);
+                       #if DEBUG
+                       Logger.VerboseDebug("Wrote chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, pixels);
+                       #endif
 
                        knownChunkTops.Add(mostActiveCol.Key);
-                       columnCounter.Remove(mostActiveCol.Key);                        
-                       } 
-
+                       columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
+                       }
+                       else {
+                       columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
+                       Logger.VerboseDebug("Un-painted chunk: ({0}) ", mostActiveCol.Key);     
+                       }
 
+                       } 
 
+               
+               
                //Then sleep until interupted again, and repeat
 
                Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name);
@@ -157,28 +179,36 @@ namespace Automap
                {
                //Logger.VerboseDebug($"Change: @({chunkCoord}) R: {reason}");
                Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z);
-               
-                       lock (colLock)
-                       {
-                       if (columnCounter.ContainsKey(topPosition)) { columnCounter[topPosition]++; } else { columnCounter.Add(topPosition, 1); }
-                       }
-
+                               
+               columnCounter.AddOrUpdate(topPosition, 1, (key, colAct) => colAct + 1);
                }
 
-               private void print_stats(float interval)
+               private void Prefill_POI_Designators( )
                {
-               if (columnCounter != null && columnCounter.Count > 0) {
-               foreach (var count in columnCounter) {
-               Logger.VerboseDebug($"({count.Key}): {count.Value}");
-               }
+               this.POIs = new List<PointOfInterest>( );
+               var roadIDs = Helpers.ArbitrarytBlockIdHunter(ClientAPI, new AssetLocation("game","stonepath"), EnumBlockMaterial.Gravel);
+
+               //Add special marker types for BlockID's of "Interest", plus a special overwrite colour for them
+               this.BlockID_Designators = new Dictionary<int, Designator>( );
+
+               foreach (var entry in roadIDs) {
+                               BlockID_Designators.Add (entry.Key, new Designator
+                                               (                                               
+                                                Color.Yellow
+                                               ));
                }
+               
+
 
                }
 
 
+
                #region COPYPASTA
-               public Bitmap GenerateChunkImage(Vec2i chunkPos, IMapChunk mc)
+               //A slightly re-written; ChunkMapLayer :: public int[] GenerateChunkImage(Vec2i chunkPos, IMapChunk mc)
+               internal Bitmap GenerateChunkImage(Vec2i chunkPos, IMapChunk mc, out uint pixelCount)
                {
+               pixelCount = 0;
                BlockPos tmpPos = new BlockPos( );
                Vec2i localpos = new Vec2i( );          
                int chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
@@ -259,6 +289,7 @@ namespace Automap
 
                chunksColumn[localChunkY].Unpack( );
                int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localpos.X, mapY % chunkSize, localpos.Y, chunkSize, chunkSize)];
+               
                Block block = ClientAPI.World.Blocks[blockId];
 
                tmpPos.Set(chunkSize * chunkPos.X + localpos.X, mapY, chunkSize * chunkPos.Y + localpos.Y);
@@ -266,11 +297,23 @@ namespace Automap
                int avgCol = block.GetColor(ClientAPI, tmpPos);
                int rndCol = block.GetRandomColor(ClientAPI, tmpPos, BlockFacing.UP);
                //Merge color?
-               int col = ColorUtil.ColorOverlay(avgCol, rndCol, 0.25f);
+               int col = ColorUtil.ColorOverlay(avgCol, rndCol, 0.125f);
                var packedFormat = ColorUtil.ColorMultiply3Clamped(col, b) | 255 << 24;//Is the Struct, truly so undesirable?
 
                Color pixelColor = Color.FromArgb(ColorUtil.ColorR(packedFormat), ColorUtil.ColorG(packedFormat), ColorUtil.ColorB(packedFormat));
+               
+               //============ POI Population =================
+               if (BlockID_Designators.ContainsKey(blockId)) {
+               var desig = BlockID_Designators[blockId];
+               pixelColor = desig.OverwriteColor;
+
+                       if (desig.SpecialAction != null) {
+                       desig.SpecialAction.Invoke(tmpPos, block);
+                       }
+               }
+
                chunkImage.SetPixel(localX,localZ, pixelColor);
+               pixelCount++;
                }
 
 
diff --git a/Automap/Data/Designator.cs b/Automap/Data/Designator.cs
new file mode 100644 (file)
index 0000000..daf4a3b
--- /dev/null
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Drawing;
+
+using Vintagestory.API.Common;
+using Vintagestory.API.MathTools;
+
+namespace Automap
+{
+       /// <summary>
+       /// Point of Interest Rule Designator
+       /// </summary>
+       public class Designator
+       {               
+               public Color OverwriteColor;
+               public System.Action<BlockPos, Block> SpecialAction;
+
+               private Designator( )
+               {
+               throw new NotSupportedException( );
+               }
+
+               public Designator( Color overwriteColor)
+               {                       
+                       this.OverwriteColor = overwriteColor;
+               }
+
+               public Designator(int blockID, Color overwriteColor, System.Action<BlockPos, Block> specialAct )
+               {
+
+               this.OverwriteColor = overwriteColor;
+               this.SpecialAction = specialAct;
+               }
+       }
+}
+
diff --git a/Automap/Data/PointOfInterest.cs b/Automap/Data/PointOfInterest.cs
new file mode 100644 (file)
index 0000000..15fdda8
--- /dev/null
@@ -0,0 +1,20 @@
+using System;
+
+using Vintagestory.API.Common;
+using Vintagestory.API.MathTools;
+
+namespace Automap
+{
+       /// <summary>
+       /// Actual Physical Point in space - that is interesting.
+       /// </summary>
+       public struct PointOfInterest
+       {
+               CollectibleObject Thing;
+               string Notes;
+               BlockPos Location;
+               DateTimeOffset Timestamp;
+       }
+
+}
+
diff --git a/Automap/Helpers.cs b/Automap/Helpers.cs
new file mode 100644 (file)
index 0000000..fa41c55
--- /dev/null
@@ -0,0 +1,158 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+
+using Vintagestory.API.Client;
+using Vintagestory.API.Common;
+using Vintagestory.API.MathTools;
+
+namespace Automap
+{
+       public static class Helpers
+       {
+               
+               /// <summary>
+               /// Hue, Saturation Value colorspace
+               /// </summary>
+               /// <returns>The color equiv.</returns>
+               /// <param name="hue">0 - 360 for hue.</param>
+               /// <param name="saturation"> 0 - 1 for saturation or value..</param>
+               /// <param name="value"> 0 - 1 for saturation or value..</param>
+               public static Color FromHSV(double hue, double saturation, double value)
+               {
+                       int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
+                       double f = hue / 60 - Math.Floor(hue / 60);
+
+                       value = value * 255;
+                       int v = Convert.ToInt32(value);
+                       int p = Convert.ToInt32(value * (1 - saturation));
+                       int q = Convert.ToInt32(value * (1 - f * saturation));
+                       int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
+
+                       if (hi == 0)
+                               return Color.FromArgb(255, v, t, p);
+                       else if (hi == 1)
+                               return Color.FromArgb(255, q, v, p);
+                       else if (hi == 2)
+                               return Color.FromArgb(255, p, v, t);
+                       else if (hi == 3)
+                               return Color.FromArgb(255, p, q, v);
+                       else if (hi == 4)
+                               return Color.FromArgb(255, t, p, v);
+                       else
+                               return Color.FromArgb(255, v, p, q);
+               }
+
+               public static string PrettyCoords(this BlockPos location, ICoreClientAPI ClientApi)
+               {
+                       var start = ClientApi.World.DefaultSpawnPosition.AsBlockPos;
+
+                       return string.Format("X{0}, Y{1}, Z{2}", location.X - start.X, location.Y, location.Z - start.Z );
+               }
+
+               public static BlockPos AverageHighestPos(List<BlockPos> positions)
+               {
+                       int x = 0, y = 0, z = 0, length = positions.Count;
+                       foreach (BlockPos pos in positions)
+                       {
+                               x += pos.X;
+                               y = Math.Max(y, pos.Y);//Mutant Y-axis, take "HIGHEST"
+                               z += pos.Z;
+                       }
+                       return new BlockPos(x/ length, y, z / length);
+               }
+
+               public static BlockPos PickRepresentativePosition(List<BlockPos> positions)
+               {
+                       var averagePos = AverageHighestPos( positions );
+                       if ( positions.Any( pos => pos.X == averagePos.X && pos.Y == averagePos.Y && pos.Z == averagePos.Z ) ) {
+                               return averagePos;//lucky ~ center was it!
+                       }
+
+                       //Otherwise...pick one
+                       var whichever = positions.Last(poz => poz.Y == averagePos.Y);
+
+                       return whichever;
+               }
+
+
+
+               /// <summary>
+               /// Find a BLOCK partial path match: BlockID
+               /// </summary>
+               /// <returns>Matching finds</returns>
+               /// <param name="assetName">Asset name.</param>
+               public static Dictionary<int, string> ArbitrarytBlockIdHunter(this ICoreAPI CoreApi ,AssetLocation assetName, EnumBlockMaterial? material = null)
+               {
+                       Dictionary<int, string> arbBlockIDTable = new Dictionary<int, string>( );
+                       uint emptyCount = 0;
+
+                       if (CoreApi.World.Blocks != null) {
+
+                               #if DEBUG
+                               CoreApi.World.Logger.VerboseDebug(" World Blocks [Count: {0}]", CoreApi.World.Blocks.Count);
+                               #endif
+                               //If Brute force won't work; use GROOT FORCE!
+                               //var theBlock = ClientApi.World.BlockAccessor.GetBlock(0);
+
+                               if (!material.HasValue) {
+                                       foreach (Block blk in CoreApi.World.Blocks) {
+                                               if (blk.IsMissing || blk.Id == 0 || blk.BlockId == 0) {
+                                                       emptyCount++;
+                                               } else if (blk.Code != null && blk.Code.BeginsWith(assetName.Domain, assetName.Path)) {
+                                                       #if DEBUG
+                                                       //CoreApi.World.Logger.VerboseDebug("Block: [{0} ({1})] =  #{2}", blk.Code.Path, blk.BlockMaterial, blk.BlockId);
+                                                       #endif
+
+                                                       arbBlockIDTable.Add(blk.BlockId, blk.Code.Path);
+                                               }
+                                       }
+                               } else {
+                                       foreach (Block blk in CoreApi.World.Blocks) {
+                                               if (blk.IsMissing || blk.Id == 0 || blk.BlockId == 0) {
+                                                       emptyCount++;
+                                               } else if (blk.Code != null && material.Value == blk.BlockMaterial && blk.Code.BeginsWith(assetName.Domain, assetName.Path)) {
+                                                       #if DEBUG
+                                                       //CoreApi.World.Logger.VerboseDebug("Block: [{0} ({1})] =  #{2}", blk.Code.Path, blk.BlockMaterial, blk.BlockId);
+                                                       #endif
+
+                                                       arbBlockIDTable.Add(blk.BlockId, blk.Code.Path);
+                                               }
+                                       }
+                               }
+
+                               #if DEBUG
+                               CoreApi.World.Logger.VerboseDebug("Block gaps: {0}", emptyCount);
+                               #endif
+                       }
+
+                       return arbBlockIDTable;
+               }
+
+
+
+               /// <summary>
+               /// Chunk local index. Not block position!
+               /// </summary>
+               /// <remarks>Clamps to 5 bit ranges automagically</remarks>
+               public static int ChunkBlockIndicie16(int X_index, int Y_index, int Z_index)
+               {                       
+                       return ((Y_index & 31) * 32 + (Z_index & 31)) * 32 + (X_index & 31);
+               }
+
+               /// <summary>
+               /// Chunk index converted from block position (in world)
+               /// </summary>
+               /// <returns>The block indicie.</returns>
+               /// <param name="blockPos">Block position.</param>
+               /// <remarks>Clamps to 5 bit ranges automagically</remarks>
+               public static int ChunkBlockIndicie16(BlockPos blockPos)
+               {
+                       //Chunk masked
+                       return ((blockPos.Y & 31) * 32 + (blockPos.Z & 31)) * 32 + (blockPos.X & 31);
+               }
+       }
+}
+