public Color FindBestMatch(Color toMatch) { if (BestMatchCache.TryGetValue(toMatch, out Color found)) { return(found); } if (toMatch.A < 30) { var cc = Materials.Air.getAverageColor(true); BestMatchCache[toMatch] = cc; return(cc); } var rt = this.colorPalette.FindBestMatch(toMatch); if (rt != default(Color)) { BestMatchCache[toMatch] = rt; } return(rt); }
/// <summary> /// /// </summary> /// <param name="worker"></param> /// <param name="isClearBestMatchCache"></param> /// <param name="materials"></param> /// <exception cref="OperationCanceledException"></exception> /// <returns></returns> public async Task CompileColorPalette(CancellationToken worker, bool isClearBestMatchCache, List <Material> materials) { bool isSide = Options.Get.IsSideView; bool isMultiLayer = Options.Get.IsMultiLayer; bool isMultiLayerRequired = Options.Get.IsMultiLayerRequired; bool isAllowGlassByItself = false && !materials.Any(x => x.IsEnabled && x.Category != "Glass" && x.Category != "Air"); if (isClearBestMatchCache) { this.BestMatchCache.Clear(); this.ColorToMaterialMap.Clear(); this.colorPalette.Clear(); this.colorPalette = new KDColorTree(); } int n = 0; int maxN = materials.Count(x => x.IsEnabled && x.Category != "Glass"); if (isMultiLayer) { if (isMultiLayerRequired) { maxN = maxN * Math.Max(1, materials.Count(x => x.IsEnabled && x.Category == "Glass")); } else { maxN += maxN * materials.Count(x => x.IsEnabled && x.Category == "Glass"); } } maxN = (int)(maxN * 1.20); // 1 for base. Reserve 20% for remaining iterations. Dictionary <Color, Material[]> colorToMaterialMap = new Dictionary <Color, Material[]>(); TaskManager.SafeReport(0, "Compiling Color Map based on selected materials."); var categoriesSelected = Materials.List.Where(m2 => m2.IsEnabled && m2.Category != "Air").Select(m2 => m2.Category).Distinct(); var baseLayer = materials.Where(m2 => m2.IsEnabled && m2.Category != "Glass" && m2.Category != "Air").ToList(); var parallelOptions = new ParallelOptions() { CancellationToken = worker, MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2) }; #region Layer 1 await Task.Run(() => Parallel.ForEach(baseLayer, parallelOptions, m => { Color cAvg = m.getAverageColor(isSide); lock (colorToMaterialMap) { colorToMaterialMap[cAvg] = new Material[1] { m }; } Interlocked.Increment(ref n); if (n % 30 == 0) { TaskManager.SafeReport(100 * n / maxN); if (worker.SafeIsCancellationRequested()) { lock (colorToMaterialMap) { colorToMaterialMap.Clear(); } worker.SafeThrowIfCancellationRequested(); } } })); #endregion Layer 1 #region Layer 2 if (isMultiLayer) { Dictionary <Color, Material[]> toAdd = new Dictionary <Color, Material[]>(); List <Material> glasses = Materials.List.Where(m2 => m2.Category == "Glass" && m2.IsEnabled).ToList(); List <Material[]> layer1; lock (colorToMaterialMap) { layer1 = colorToMaterialMap.Values.Where(cm => cm.Length == 1).ToList(); } await Task.Run(() => Parallel.ForEach(layer1, parallelOptions, mArr => { Color mArrAvgColor = mArr[0].getAverageColor(isSide); foreach (Material glassM in glasses) { Color combinedColor = OverlayColor(glassM.getAverageColor(isSide), mArrAvgColor); Material[] matMap = new Material[mArr.Length + 1]; for (int i = 0; i < mArr.Length; i++) { matMap[i] = mArr[i]; } matMap[matMap.Length - 1] = glassM; lock (colorToMaterialMap) { // Either prefer single layer, or... allow replacing because multi layer is preferred. if (isMultiLayerRequired || !colorToMaterialMap.ContainsKey(combinedColor)) { lock (toAdd) { toAdd[combinedColor] = matMap; } } } Interlocked.Increment(ref n); if (n % 30 == 0) { TaskManager.SafeReport(100 * n / maxN); if (worker.SafeIsCancellationRequested()) { lock (colorToMaterialMap) { colorToMaterialMap.Clear(); } worker.SafeThrowIfCancellationRequested(); } } } })); lock (colorToMaterialMap) { if (isMultiLayerRequired) { colorToMaterialMap.Clear(); toAdd.ToList().ForEach(kvp => colorToMaterialMap[kvp.Key] = kvp.Value); } else { foreach (Color c in toAdd.Keys) { if (!colorToMaterialMap.ContainsKey(c)) { colorToMaterialMap[c] = toAdd[c]; } } } } toAdd.Clear(); } #endregion Layer 2 #region Finalize lock (colorToMaterialMap) { TaskManager.SafeReport(80); lock (this.colorPalette) { this.colorPalette.Clear(); this.colorPalette = new KDColorTree(); colorToMaterialMap.Keys.Where(c => c.ToArgb() != 16777215 && c.A != 0) .ToList() .ForEach(c => this.colorPalette.Add(c)); } colorToMaterialMap[Materials.Air.getAverageColor(isSide)] = new Material[1] { Materials.Air }; colorToMaterialMap[Color.FromArgb(0, 255, 255, 255)] = new Material[1] { Materials.Air }; } TaskManager.SafeReport(90); lock (this.BestMatchCache) { if (isClearBestMatchCache) { BestMatchCache.Clear(); } else { this.BestMatchCache.ToList().ForEach(kvp => { if (!colorToMaterialMap.ContainsKey(kvp.Value)) { this.BestMatchCache.Remove(kvp.Key); } }); } } #endregion Finalize TaskManager.SafeReport(100, "Color map finished compiling"); this.ColorToMaterialMap = colorToMaterialMap; }