diff --git a/MatterControlLib/ApplicationView/AppContext.cs b/MatterControlLib/ApplicationView/AppContext.cs index 050ce1311..5bfa21579 100644 --- a/MatterControlLib/ApplicationView/AppContext.cs +++ b/MatterControlLib/ApplicationView/AppContext.cs @@ -66,13 +66,12 @@ namespace MatterHackers.MatterControl /// public static SystemWindow RootSystemWindow { get; internal set; } - public static ThemeConfig Theme => themeset.Theme; + public static ThemeConfig Theme => ThemeSet.Theme; - public static ThemeConfig MenuTheme => themeset.MenuTheme; + public static ThemeConfig MenuTheme => ThemeSet.MenuTheme; - private static ThemeSet themeset; - - public static ThemeSet ThemeSet => themeset; + private static ThemeSet _themeset; + public static ThemeSet ThemeSet => _themeset; public static Dictionary ThemeProviders { get; } @@ -106,22 +105,22 @@ namespace MatterHackers.MatterControl { if (File.Exists(ProfileManager.Instance.ProfileThemeSetPath)) { - themeset = JsonConvert.DeserializeObject(File.ReadAllText(ProfileManager.Instance.ProfileThemeSetPath)); - themeset.Theme.EnsureDefaults(); + _themeset = JsonConvert.DeserializeObject(File.ReadAllText(ProfileManager.Instance.ProfileThemeSetPath)); + ThemeSet.Theme.EnsureDefaults(); // If the serialized format is older than the current format, null and fall back to latest default below - if (themeset.SchemeVersion != ThemeSet.LatestSchemeVersion) + if (ThemeSet.SchemeVersion != ThemeSet.LatestSchemeVersion) { - themeset = null; + _themeset = null; } } } catch { } - if (themeset == null) + if (ThemeSet == null) { var themeProvider = ThemeProviders["Modern"]; - themeset = themeProvider.GetTheme("Modern-Dark"); + _themeset = themeProvider.GetTheme("Modern-Dark"); } ToolTipManager.CreateToolTip = MatterControlToolTipWidget; @@ -178,18 +177,18 @@ namespace MatterHackers.MatterControl public static void SetThemeAccentColor(Color accentColor) { - themeset.SetAccentColor(accentColor); - AppContext.SetTheme(themeset); + ThemeSet.SetAccentColor(accentColor); + AppContext.SetTheme(ThemeSet); } public static void SetTheme(ThemeSet themeSet) { - themeset = themeSet; + _themeset = themeSet; File.WriteAllText( ProfileManager.Instance.ProfileThemeSetPath, JsonConvert.SerializeObject( - themeset, + ThemeSet, Formatting.Indented, new JsonSerializerSettings { @@ -198,7 +197,7 @@ namespace MatterHackers.MatterControl UiThread.RunOnIdle(() => { - UserSettings.Instance.set(UserSettingsKey.ActiveThemeName, themeset.Name); + UserSettings.Instance.set(UserSettingsKey.ActiveThemeName, ThemeSet.Name); // Explicitly fire ReloadAll in response to user interaction ApplicationController.Instance.ReloadAll().ConfigureAwait(false); diff --git a/MatterControlLib/ApplicationView/SceneOperations.cs b/MatterControlLib/ApplicationView/SceneOperations.cs index be3ece4ba..9800e0aff 100644 --- a/MatterControlLib/ApplicationView/SceneOperations.cs +++ b/MatterControlLib/ApplicationView/SceneOperations.cs @@ -45,6 +45,7 @@ using MatterHackers.MatterControl.DesignTools; using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.MatterControl.PartPreviewWindow.View3D; +using MatterHackers.MatterControl.SettingsManagement; using MatterHackers.PolygonMesh; using MatterHackers.VectorMath; @@ -856,6 +857,7 @@ namespace MatterHackers.MatterControl Visible = OperationGroup.GetVisible("Path", false), Operations = new List() { + AddSliceSettingsOperation(), ToggleSupportOperation(), ToggleWipeTowerOperation(), } @@ -1434,6 +1436,33 @@ namespace MatterHackers.MatterControl }; } + private static SceneOperation AddSliceSettingsOperation() + { + var isExperimental = OemSettings.Instance.WindowTitleExtra == "Experimental"; + +#if !DEBUG + if (!isExperimental) + { + return null; + } +#endif + + return new SceneOperation("Add Slice Settings") + { + OperationType = typeof(IObject3D), + ResultType = typeof(SliceSettingsObject3D), + TitleGetter = () => "Add Slice Settings".Localize(), + Action = (sceneContext) => + { + var sliceSettings = new SliceSettingsObject3D(); + sliceSettings.WrapSelectedItemAndSelect(sceneContext.Scene); + }, + Icon = (theme) => StaticData.Instance.LoadIcon("settings.png", 16, 16).SetToColor(theme.TextColor), + HelpTextGetter = () => "At least 1 part must be selected".Localize().Stars(), + IsEnabled = (sceneContext) => IsMeshObject(sceneContext.Scene.SelectedItem), + }; + } + private static SceneOperation ToggleWipeTowerOperation() { return new SceneOperation("Convert to Wipe Tower") diff --git a/MatterControlLib/DesignTools/Operations/GroupObject3D.cs b/MatterControlLib/DesignTools/Operations/GroupObject3D.cs index ba3f088b0..d2a875e0f 100644 --- a/MatterControlLib/DesignTools/Operations/GroupObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/GroupObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, Lars Brubaker, John Lewin +Copyright (c) 2022, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ using MatterHackers.Localizations; namespace MatterHackers.MatterControl.DesignTools.Operations { - public class GroupObject3D : Object3D + public class GroupObject3D : Object3D { public override bool CanApply => true; diff --git a/MatterControlLib/DesignTools/Operations/SliceSettingsObject3D.cs b/MatterControlLib/DesignTools/Operations/SliceSettingsObject3D.cs new file mode 100644 index 000000000..1f1c3f556 --- /dev/null +++ b/MatterControlLib/DesignTools/Operations/SliceSettingsObject3D.cs @@ -0,0 +1,108 @@ +/* +Copyright (c) 2022, Lars Brubaker +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using MatterHackers.DataConverters3D; +using MatterHackers.DataConverters3D.UndoCommands; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.SlicerConfiguration; +using System.Linq; +using static MatterHackers.DataConverters3D.Object3DExtensions; + +namespace MatterHackers.MatterControl.DesignTools.Operations +{ + public class SliceSettingsObject3D : Object3D + { + public SliceSettingsObject3D() + { + Name = "Slice Settings".Localize(); + } + + public PrinterSettingsLayer Overrides { get; set; } + + public void WrapSelectedItemAndSelect(InteractiveScene scene) + { + var items = scene.GetSelectedItems(); + + var parent = items.First().Parent; + RebuildLocks parentLock = (parent == null) ? null : parent.RebuilLockAll(); + + var firstChild = new Object3D(); + this.Children.Add(firstChild); + + // if the items we are replacing are already in a list + if (parent != null) + { + if (scene.UndoBuffer != null) + { + foreach (var item in items) + { + firstChild.Children.Add(item.Clone()); + } + + var replace = new ReplaceCommand(items, new[] { this }); + scene.UndoBuffer.AddAndDo(replace); + } + else + { + parent.Children.Modify(list => + { + foreach (var item in items) + { + list.Remove(item); + firstChild.Children.Add(item); + } + + list.Add(this); + }); + } + } + else // just add them + { + firstChild.Children.Modify(list => + { + list.AddRange(items); + }); + } + + parentLock?.Dispose(); + + // and select this + var rootItem = this.Parents().Where(i => scene.Children.Contains(i)).FirstOrDefault(); + if (rootItem != null) + { + scene.SelectedItem = rootItem; + } + + scene.SelectedItem = this; + + this.CancelAllParentBuilding(); + parent?.Invalidate(new InvalidateArgs(parent, InvalidateType.Children)); + } + } +} \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/TransformWrapperObject3D.cs b/MatterControlLib/DesignTools/Operations/TransformWrapperObject3D.cs index 83ff91846..da11c198f 100644 --- a/MatterControlLib/DesignTools/Operations/TransformWrapperObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/TransformWrapperObject3D.cs @@ -35,7 +35,6 @@ using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; using MatterHackers.DataConverters3D.UndoCommands; using MatterHackers.Localizations; -using MatterHackers.MatterControl.PartPreviewWindow.View3D; using Newtonsoft.Json; using static MatterHackers.DataConverters3D.Object3DExtensions; diff --git a/MatterControlLib/Library/Widgets/HardwarePage/PrinterDetails.cs b/MatterControlLib/Library/Widgets/HardwarePage/PrinterDetails.cs index 85df6f698..fe5a45466 100644 --- a/MatterControlLib/Library/Widgets/HardwarePage/PrinterDetails.cs +++ b/MatterControlLib/Library/Widgets/HardwarePage/PrinterDetails.cs @@ -215,7 +215,7 @@ namespace MatterHackers.MatterControl.Library.Widgets.HardwarePage whiteBackgroundWidget.Selectable = true; whiteBackgroundWidget.ImageWidget.Click += (s, e) => { - ApplicationController.LaunchBrowser($"www.matterhackers.com/qr/{sku}"); + ApplicationController.LaunchBrowser($"https://www.matterhackers.com/qr/{sku}"); }; } diff --git a/MatterControlLib/PartPreviewWindow/ViewToolBarControls.cs b/MatterControlLib/PartPreviewWindow/ViewToolBarControls.cs index 114d27d50..ecb0ad60a 100644 --- a/MatterControlLib/PartPreviewWindow/ViewToolBarControls.cs +++ b/MatterControlLib/PartPreviewWindow/ViewToolBarControls.cs @@ -323,6 +323,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private string Expanded; private string Hidden; + private string Default; + private string Hide_All; + private string Expand_All; + private PopupMenu GenerateToolBarOptionsMenu(ThemeConfig theme) { var popupMenu = new PopupMenu(theme) @@ -330,6 +334,45 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Padding = new BorderDouble(0, 7), }; + // buttons for the control of defaults + var topButtonData = new (string, string)[] + { + (nameof(Default), "Default".Localize()), + (nameof(Expand_All), "Expand All".Localize()), + (nameof(Hide_All), "Hide All".Localize()), + }; + +#if false + var startingValue = operationGroup.Collapse ? nameof(Collapsed) : nameof(Expanded); + if (!operationGroup.Visible) + { + startingValue = nameof(Hidden); + } + + popupMenu.CreateButtonSelectMenuItem(operationGroup.Title, buttonData, startingValue, (value) => + { + switch (value) + { + case nameof(Expanded): + operationGroup.Collapse = false; + operationGroup.Visible = true; + break; + + case nameof(Collapsed): + operationGroup.Collapse = true; + operationGroup.Visible = true; + break; + + case nameof(Hidden): + operationGroup.Visible = false; + break; + } + }, 40 * GuiWidget.DeviceScale); + + popupMenu.CreateSeparator(); +#endif + + // the buttons per setting var buttonData = new (string, string)[] { (nameof(Collapsed), "Collapse".Localize()), diff --git a/StaticData/Icons/settings.png b/StaticData/Icons/settings.png new file mode 100644 index 000000000..89d8fb7ff Binary files /dev/null and b/StaticData/Icons/settings.png differ diff --git a/StaticData/Materials/3DXTech/PEEK-A.material b/StaticData/Materials/3DXTech/PEEK-A.material index b0b949096..c023f082a 100644 --- a/StaticData/Materials/3DXTech/PEEK-A.material +++ b/StaticData/Materials/3DXTech/PEEK-A.material @@ -6,6 +6,7 @@ { "layer_name": "PEEK", "layer_id": "63342beb-0f41-4afb-bf96-bf73bd168bd7", + "filament_density": "1.32", "bed_temperature": "130", "bridge_fan_speed": "50", "max_fan_speed": "0", diff --git a/StaticData/Materials/3DXTech/ezPC.material b/StaticData/Materials/3DXTech/ezPC.material index 37543ad73..4e237b686 100644 --- a/StaticData/Materials/3DXTech/ezPC.material +++ b/StaticData/Materials/3DXTech/ezPC.material @@ -4,23 +4,24 @@ "Macros": [], "MaterialLayers": [ { - "layer_name": "3DXTech ezPC", - "layer_id": "d9240109-d750-462a-9d72-4c4f53678710", - "bed_temperature": "115", - "enable_fan": "0", - "infill_speed": "30", - "max_fan_speed": "0", - "min_fan_speed": "0", - "perimeter_speed": "26", - "temperature": "290", "bed_temperature_buildtak": "110", "bed_temperature_garolite": "110", "bed_temperature_glass": "110", "bed_temperature_kapton": "110", "bed_temperature_pei": "110", "bed_temperature_pp": "110", + "bed_temperature": "115", + "enable_fan": "0", + "filament_density": "1.20", + "infill_speed": "30", + "layer_id": "d9240109-d750-462a-9d72-4c4f53678710", + "layer_name": "3DXTech ezPC", + "material_sku": "M1JH1UAQ", + "max_fan_speed": "0", + "min_fan_speed": "0", + "perimeter_speed": "26", + "temperature": "290", "top_solid_infill_speed": "22", - "material_sku": "M1JH1UAQ" } ], "OemLayer": null, diff --git a/StaticData/Materials/BASF/PRO1 Tough PLA.material b/StaticData/Materials/BASF/PRO1 Tough PLA.material index b9c3a29fc..ff3fd2ceb 100644 --- a/StaticData/Materials/BASF/PRO1 Tough PLA.material +++ b/StaticData/Materials/BASF/PRO1 Tough PLA.material @@ -4,22 +4,23 @@ "Macros": [], "MaterialLayers": [ { - "layer_name": "BASF PRO1 Tough PLA", - "layer_id": "42348031-fadf-4e93-bfbc-6b5e78a9ad39", - "temperature": "220", - "bed_temperature": "55", - "min_fan_speed_layer_time": "180", - "max_fan_speed_layer_time": "60", - "min_fan_speed": "60", - "disable_fan_first_layers": "5", - "filament_cost": "42", "bed_temperature_buildtak": "55", "bed_temperature_garolite": "70", "bed_temperature_glass": "70", "bed_temperature_kapton": "55", "bed_temperature_pei": "70", "bed_temperature_pp": "55", - "material_sku": "MZ2HXEPK" + "bed_temperature": "55", + "disable_fan_first_layers": "5", + "filament_cost": "42", + "filament_density": "1.24", + "layer_id": "42348031-fadf-4e93-bfbc-6b5e78a9ad39", + "layer_name": "BASF PRO1 Tough PLA", + "material_sku": "MZ2HXEPK", + "max_fan_speed_layer_time": "60", + "min_fan_speed_layer_time": "180", + "min_fan_speed": "60", + "temperature": "220" } ], "OemLayer": null, diff --git a/StaticData/Materials/General/PC.material b/StaticData/Materials/General/PC.material index 80c266546..e6c038b73 100644 --- a/StaticData/Materials/General/PC.material +++ b/StaticData/Materials/General/PC.material @@ -4,24 +4,25 @@ "Macros": [], "MaterialLayers": [ { - "layer_name": "Polycarbonate", - "layer_id": "d924b109-d750-4a2a-9d72-4c4f53178710", - "bed_temperature": "115", - "enable_fan": "0", - "has_fan": "False", - "infill_speed": "30", - "max_fan_speed": "0", - "min_fan_speed": "0", - "perimeter_speed": "26", - "temperature": "290", "bed_temperature_buildtak": "100", "bed_temperature_garolite": "100", "bed_temperature_glass": "100", "bed_temperature_kapton": "100", "bed_temperature_pei": "100", "bed_temperature_pp": "100", + "bed_temperature": "115", + "enable_fan": "0", + "filament_density": "1.20", + "has_fan": "False", + "infill_speed": "30", + "layer_id": "d924b109-d750-4a2a-9d72-4c4f53178710", + "layer_name": "Polycarbonate", + "material_sku": "PC", + "max_fan_speed": "0", + "min_fan_speed": "0", + "perimeter_speed": "26", + "temperature": "290", "top_solid_infill_speed": "22", - "material_sku": "PC" } ], "OemLayer": null, diff --git a/StaticData/Materials/NinjaTek/Ninja Flex.material b/StaticData/Materials/NinjaTek/Ninja Flex.material new file mode 100644 index 000000000..16cd809d7 --- /dev/null +++ b/StaticData/Materials/NinjaTek/Ninja Flex.material @@ -0,0 +1,38 @@ +{ + "DocumentVersion": 201606271, + "ID": null, + "Macros": [], + "MaterialLayers": [ + { + "layer_name": "Ninja Flex", + "layer_id": "48bf0756-ddc9-4371-bb25-800f5003d502", + "bridge_fan_speed": "100", + "filament_density": "1.28", + "first_layer_speed": "10", + "max_fan_speed": "100", + "min_fan_speed": "100", + "infill_speed": "25", + "top_solid_infill_speed": "25", + "perimeter_speed": "25", + "support_material_speed": "25", + "interface_layer_speed": "25", + "air_gap_speed": "25", + "raft_print_speed": "25", + "external_perimeter_speed": "25", + "retract_length": "0", + "temperature": "235", + "bed_temperature": "75", + "bed_temperature_buildtak": "55", + "bed_temperature_garolite": "75", + "bed_temperature_glass": "75", + "bed_temperature_kapton": "75", + "bed_temperature_pei": "75", + "bed_temperature_pp": "55", + "material_sku": "MH7SK8X3" + } + ], + "OemLayer": null, + "QualityLayers": [], + "StagedUserSettings": {}, + "UserLayer": {} +} \ No newline at end of file diff --git a/StaticData/Materials/_MatterHackers/Build Series/TPU.material b/StaticData/Materials/_MatterHackers/Build Series/TPU.material index df7e6d873..5608f2efd 100644 --- a/StaticData/Materials/_MatterHackers/Build Series/TPU.material +++ b/StaticData/Materials/_MatterHackers/Build Series/TPU.material @@ -8,7 +8,7 @@ "filament_density": "1.23", "layer_id": "80657dda-a7c1-4837-be45-54990ce1071d", "first_layer_speed": "15", - "filament_cost": "24.64", + "filament_cost": "38.25", "infill_speed": "25", "top_solid_infill_speed": "25", "perimeter_speed": "25", @@ -16,10 +16,10 @@ "interface_layer_speed": "25", "air_gap_speed": "25", "raft_print_speed": "25", - "enable_retractions": "0", - "temperature": "245", "external_perimeter_speed": "25", "min_print_speed": "25", + "enable_retractions": "0", + "temperature": "245", "bed_temperature_buildtak": "55", "bed_temperature_garolite": "55", "bed_temperature_glass": "65", diff --git a/StaticData/Materials/_MatterHackers/PRO Series/Tough PLA.material b/StaticData/Materials/_MatterHackers/PRO Series/Tough PLA.material index 801774460..789093c3a 100644 --- a/StaticData/Materials/_MatterHackers/PRO Series/Tough PLA.material +++ b/StaticData/Materials/_MatterHackers/PRO Series/Tough PLA.material @@ -12,6 +12,7 @@ "max_fan_speed_layer_time": "60", "min_fan_speed": "60", "disable_fan_first_layers": "5", + "filament_density": "1.24", "filament_cost": "42", "bed_temperature_buildtak": "55", "bed_temperature_garolite": "70", diff --git a/StaticData/Translations/Master.txt b/StaticData/Translations/Master.txt index dedd6a91d..d24ad23b8 100644 --- a/StaticData/Translations/Master.txt +++ b/StaticData/Translations/Master.txt @@ -229,6 +229,9 @@ Translated:Add Note English:Add Note... Translated:Add Note... +English:Add Slice Settings +Translated:Add Slice Settings + English:Add System File to Bed Translated:Add System File to Bed diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index a21b65528..11ebc78b7 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit a21b6552802953da38d3c180cf7043e4e1af611b +Subproject commit 11ebc78b76712d4a34c1fade8030a29f2799261b diff --git a/Tests/MatterControl.Tests/MatterControl/OemProfileTests.cs b/Tests/MatterControl.Tests/MatterControl/OemProfileTests.cs index 46eeace1a..fa853cfb6 100644 --- a/Tests/MatterControl.Tests/MatterControl/OemProfileTests.cs +++ b/Tests/MatterControl.Tests/MatterControl/OemProfileTests.cs @@ -184,7 +184,7 @@ M300 S3000 P30 ; Resume Tone"; } [Test, RunInApplicationDomain] - public void AllMaterialsHaveSkuOrUrlSet() + public void AllMaterialsLibraryHasGoodProfiles() { var materialSettingsDirectory = TestContext.CurrentContext.ResolveProjectPath(4, "StaticData", "Materials"); var directoryInfo = new DirectoryInfo(materialSettingsDirectory); @@ -193,6 +193,42 @@ M300 S3000 P30 ; Resume Tone"; var allMaterialIds = new HashSet(); + var notPresentKeys = new string[] + { + "brims", + "brims_layers", + "coast_at_end_distance", + "create_brim", + "create_skirt", + "expand_thin_walls", + "extruder_count", + "extrusion_multiplier", + "extrusion_ratio", + "filament_diameter", + "fill_angle", + // "fill_density", // BASF uses this + "fill_pattern", + "fill_thin_gaps", + "first_layer_extrusion_width", + "first_layer_height", + "layer_height", + "monotonic_solid_infill" + }; + + var isPresentKeys = new string[] + { + "bed_temperature_buildtak", + "bed_temperature_garolite", + "bed_temperature_glass", + "bed_temperature_kapton", + "bed_temperature_pei", + "filament_density", + "layer_id", + "layer_name", + "material_sku", + "temperature", + }; + for(var i = 0; i < profiles.Count; i++) { var profile = profiles[i]; @@ -201,6 +237,15 @@ M300 S3000 P30 ; Resume Tone"; profile.ActiveMaterialKey = material.LayerID; Assert.IsTrue(!string.IsNullOrEmpty(profile.GetValue(SettingsKey.material_sku)), $"All profiles should have a material_sku set {files[i].FullName}"); Assert.IsTrue(!allMaterialIds.Contains(material.LayerID), $"Every material needs a unique Id {files[i].FullName}"); + foreach(var key in isPresentKeys) + { + Assert.IsTrue(material.ContainsKey(key), $"Material {files[i].FullName} should include {key} setting"); + } + + foreach (var key in notPresentKeys) + { + Assert.IsTrue(!material.ContainsKey(key), $"Material {files[i].FullName} should not include {key} setting"); + } allMaterialIds.Add(material.LayerID); } }