From 51c7c1ad9554d3ac0da6bd11a1aa21f0549ca39f Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Tue, 25 Oct 2022 14:25:27 +0200 Subject: [PATCH 01/14] Add method for mapping proeprties of a pre-defined material onto another Material matching based on name and/or name of properties --- Matter_Engine/Modify/MapMaterial.cs | 153 +++++++++++++++++++++++++--- 1 file changed, 139 insertions(+), 14 deletions(-) diff --git a/Matter_Engine/Modify/MapMaterial.cs b/Matter_Engine/Modify/MapMaterial.cs index bf63e474e..b212255b4 100644 --- a/Matter_Engine/Modify/MapMaterial.cs +++ b/Matter_Engine/Modify/MapMaterial.cs @@ -38,14 +38,14 @@ public static partial class Modify /**** Public Methods ****/ /***************************************************/ - [Description("Adds a IMaterialFragment to a material based on the mapping defined by the keys and materialFragments. \n" + + [Description("Adds a IMaterialFragment to a material based on the mapping defined by the keys and materialFragments. \n" + "i.e. The materialFragment on index 3 will be added to the Material with the same name as the key at index 3.")] [Input("materials", "The Materials to Modify, will be evaluated based on their name.")] - [Input("keys", "The key is the name of the Material to be affected. The keys index in this list relates to the index of a materialFragment to add in the other list. \n" + + [Input("keys", "The key is the name of the Material to be affected. The keys index in this list relates to the index of a materialFragment to add in the other list. \n" + "Empty keys means that its related materialFragment will be disgarded.")] [Input("materialFragments", "The materialFragments to add to the Materials, the order of which relates to the keys.")] [Output("materials", "Materials with modified list of properties. Materials whos names did not appear among the keys are unaffected.")] - public static IEnumerable MapMaterial(IEnumerable materials, List keys, List materialFragments) + public static IEnumerable MapMaterial(this IEnumerable materials, List keys, List materialFragments) { if (keys.Count > materialFragments.Count) { @@ -74,21 +74,146 @@ public static IEnumerable MapMaterial(IEnumerable materials, Engine.Base.Compute.RecordError("Non-empty keys must be distinct."); return null; } - + // Add the materialFragment to the material and return - return materials.Select( (x) => + return materials.Select((x) => + { + for (int i = 0; i < culledKeys.Count; i++) + { + if (x.Name == culledKeys[i]) + { + Material mat = x.DeepClone(); + mat.Properties.Add(culledMaterialFragments[i]); + return mat; + } + } + return x; + }).ToList(); + } + + /***************************************************/ + + [Description("Maps a set of materials to a set of provided transdiciplinary materials.\n" + + "First atempts to match the name of the provided materials to the material maps.\n" + + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] + [Input("materials", "The Materials to Modify, will be evaluated based on their name and properties.")] + [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Output("materials", "Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] + public static IEnumerable MapMaterial(this IEnumerable materials, IEnumerable materialMaps) + { + ILookup nameLookup = materialMaps.ToLookup(x => x.Name); + + Dictionary, List> propertyMaps = new Dictionary, List>(); + + foreach (Material mat in materialMaps) { - for (int i = 0; i < culledKeys.Count; i++) + foreach (IMaterialProperties property in mat.Properties) { - if (x.Name == culledKeys[i]) - { - Material mat = x.DeepClone(); - mat.Properties.Add(culledMaterialFragments[i]); - return mat; - } + Tuple key = new Tuple(property.GetType(), property.Name); + if (propertyMaps.ContainsKey(key)) + propertyMaps[key].Add(mat); + else + propertyMaps[key] = new List() { mat }; } - return x; - }).ToList(); + } + + List mappedMaterials = new List(); + + foreach (Material material in materials) + { + mappedMaterials.Add(material.MapMaterial(nameLookup, propertyMaps)); + } + return mappedMaterials; + } + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + private static Material MapMaterial(this Material material, ILookup nameLookup, Dictionary, List> propertyMaps) + { + List matches = nameLookup[material.Name].ToList(); //Try match by name + + if (matches.Count == 0) //If no name match found, try match by material proeprties - Type and name + { + foreach (IMaterialProperties property in material.Properties) + { + Tuple key = new Tuple(property.GetType(), property.Name); + + List propMatches; + if (propertyMaps.TryGetValue(key, out propMatches)) + matches.AddRange(propMatches); + } + + matches = matches.GroupBy(x => x.Name).Select(x => x.First()).ToList(); + matches = matches.GroupBy(x => x.MatchScore(material)) //Get and group by score + .Where(x => x.Key > 0) //Only keep materials with score > 0, as negative score indicates at least one mismatch + .OrderByDescending(x => x.Key) //Order by score - Higher score first + .FirstOrDefault()?.ToList() ?? new List(); + } + + if (matches.Count == 1) //Exactly one best match. Success! + { + return material.MergeProperties(matches[0]); + } + else if (matches.Count == 0) //No matches, record warning + { + Base.Compute.RecordWarning($"No map found for material named {material.Name}"); + return material; + } + else //More than one match. Record warning + { + Base.Compute.RecordWarning($"Material named {material.Name} has ambiguous matches to {string.Join(", ", matches.Select(x => x.Name))}. No mapping preformed.\nEnsure that the transdiciplinaryMaterialMaps have unique names and that your Material has a name matching the transdiciplinary material you want to match."); + return material; + } + } + + /***************************************************/ + + [Description("Merges the Properties of the target and source by adding all properties on the source to the target. For duplicate types the Property on the Source is prioritised.")] + private static Material MergeProperties(this Material target, Material source) + { + Material targetClone = target.ShallowClone(); + targetClone.Properties = new List(target.Properties); + List targetTypes = target.Properties.Select(x => x.GetType()).ToList(); + foreach (IMaterialProperties property in source.Properties) + { + if (targetTypes.Contains(property.GetType())) + { + targetClone.Properties.RemoveAll(x => x.GetType() == property.GetType()); + } + targetClone.Properties.Add(property); + } + return targetClone; + } + + /***************************************************/ + + [Description("Matches two materials based on how well the properties align based on name. For each aligning property the score is increased by 1. If a single misaligned property is found a score of -1 is returned.")] + private static int MatchScore(this Material mat1, Material mat2) + { + //Method matches two materials based on their properties. + //For each proeprty of the same type and same name, the score is increased by 1. + //If a property of the same type but a different name is found the score is set to -1, as that means a mismatch of properties. + int score = 0; + ILookup matLookup = mat2.Properties.ToLookup(x => x.GetType()); + foreach (IMaterialProperties prop in mat1.Properties) + { + string name = prop.Name; + Type type = prop.GetType(); + + IEnumerable prop2 = matLookup[type]; + if (prop2.Any()) + { + if (prop2.Any(x => x.Name == name)) + score++; + else + return -1; + } + + } + return score; } /***************************************************/ From 0d08c7e284e448453c66145748906b5b79096d71 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Tue, 25 Oct 2022 14:28:45 +0200 Subject: [PATCH 02/14] Add methods for mapping MaterialCompositions and MaterialTakeoffs --- Matter_Engine/Modify/MapMaterial.cs | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Matter_Engine/Modify/MapMaterial.cs b/Matter_Engine/Modify/MapMaterial.cs index b212255b4..a1bde49d7 100644 --- a/Matter_Engine/Modify/MapMaterial.cs +++ b/Matter_Engine/Modify/MapMaterial.cs @@ -127,6 +127,38 @@ public static IEnumerable MapMaterial(this IEnumerable mater return mappedMaterials; } + /***************************************************/ + + [Description("Maps a set of materials in the MaterialCompositions to a set of provided transdiciplinary materials.\n" + + "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] + [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialCompositions will be evaluated based on the name and properties.")] + [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Output("materialCompositions", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] + public static IEnumerable MapMaterial(this IEnumerable materialCompositions, IEnumerable materialMaps) + { + IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); + Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps).ToDictionary(x => x.BHoM_Guid); + return materialCompositions.Select(x => new MaterialComposition(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Ratios)).ToList(); + } + + /***************************************************/ + + [Description("Maps a set of materials in the MaterialCompositions to a set of provided transdiciplinary materials.\n" + + "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] + [Input("volumetricMaterialTakeoffs", "The VolumetricMaterialTakeoff to Modify. Materials int he VolumetricMaterialTakeoff will be evaluated based on the name and properties.")] + [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Output("volumetricMaterialTakeoffs", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] + public static IEnumerable MapMaterial(this IEnumerable materialCompositions, IEnumerable materialMaps) + { + IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); + Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps).ToDictionary(x => x.BHoM_Guid); + return materialCompositions.Select(x => new VolumetricMaterialTakeoff(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Volumes)).ToList(); + } + /***************************************************/ /**** Private Methods ****/ /***************************************************/ From 7a05f9bcee61197aba75c2eec6d33b0a68f12cca Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Tue, 25 Oct 2022 14:34:33 +0200 Subject: [PATCH 03/14] Adds methods for getting out mapped materialcompositions and takeoffs frtom elements --- .../Query/MappedMaterialComposition.cs | 56 +++++++++++++++++++ .../Query/MappedVolumetricMaterialTakeoff.cs | 56 +++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 Matter_Engine/Query/MappedMaterialComposition.cs create mode 100644 Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs diff --git a/Matter_Engine/Query/MappedMaterialComposition.cs b/Matter_Engine/Query/MappedMaterialComposition.cs new file mode 100644 index 000000000..663840e30 --- /dev/null +++ b/Matter_Engine/Query/MappedMaterialComposition.cs @@ -0,0 +1,56 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2022, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System; +using System.Linq; +using System.Collections.Generic; +using System.ComponentModel; +using BH.oM.Base.Attributes; +using BH.oM.Base; +using BH.oM.Dimensional; +using BH.Engine.Base; +using BH.oM.Physical.Materials; + +namespace BH.Engine.Matter +{ + public static partial class Query + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Maps a set of materials in the MaterialCompositions of the provided elements to a set of provided transdiciplinary materials.\n" + + "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] + [Input("elements", "The elements to fetch MaterialComposition from.")] + [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] + [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] + public static List MappedMaterialComposition(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true) + { + return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).MapMaterial(materialMaps).ToList(); + } + + /***************************************************/ + } +} diff --git a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs new file mode 100644 index 000000000..f96ce4689 --- /dev/null +++ b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs @@ -0,0 +1,56 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2022, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System; +using System.Linq; +using System.Collections.Generic; +using System.ComponentModel; +using BH.oM.Base.Attributes; +using BH.oM.Base; +using BH.oM.Dimensional; +using BH.Engine.Base; +using BH.oM.Physical.Materials; + +namespace BH.Engine.Matter +{ + public static partial class Query + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Maps a set of materials in the VolumetricMaterialTakeoff of the provided elements to a set of provided transdiciplinary materials.\n" + + "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] + [Input("elements", "The elements to fetch VolumetricMaterialTakeoff from.")] + [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] + [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] + public static List MappedVolumetricMaterialTakeoff(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true) + { + return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).MapMaterial(materialMaps).ToList(); + } + + /***************************************************/ + } +} From e33d9430b50ce415dca3a9134051492e2547b401 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Tue, 25 Oct 2022 14:42:54 +0200 Subject: [PATCH 04/14] Improve null-handling and messaging --- Matter_Engine/Modify/MapMaterial.cs | 32 +++++++++++++++++-- .../Query/MappedMaterialComposition.cs | 5 ++- .../Query/MappedVolumetricMaterialTakeoff.cs | 3 ++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Matter_Engine/Modify/MapMaterial.cs b/Matter_Engine/Modify/MapMaterial.cs index a1bde49d7..eea0b586c 100644 --- a/Matter_Engine/Modify/MapMaterial.cs +++ b/Matter_Engine/Modify/MapMaterial.cs @@ -102,6 +102,15 @@ public static IEnumerable MapMaterial(this IEnumerable mater [Output("materials", "Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] public static IEnumerable MapMaterial(this IEnumerable materials, IEnumerable materialMaps) { + if (materials.IsNullOrEmpty()) + return null; + + if (materialMaps == null || !materialMaps.Any()) + { + Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(Material)}s returned."); + return materials; + } + ILookup nameLookup = materialMaps.ToLookup(x => x.Name); Dictionary, List> propertyMaps = new Dictionary, List>(); @@ -138,6 +147,14 @@ public static IEnumerable MapMaterial(this IEnumerable mater [Output("materialCompositions", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] public static IEnumerable MapMaterial(this IEnumerable materialCompositions, IEnumerable materialMaps) { + if (materialCompositions.IsNullOrEmpty()) + return null; + + if (materialMaps == null || !materialMaps.Any()) + { + Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(MaterialComposition)}s returned."); + return materialCompositions; + } IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps).ToDictionary(x => x.BHoM_Guid); return materialCompositions.Select(x => new MaterialComposition(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Ratios)).ToList(); @@ -152,11 +169,20 @@ public static IEnumerable MapMaterial(this IEnumerable MapMaterial(this IEnumerable materialCompositions, IEnumerable materialMaps) + public static IEnumerable MapMaterial(this IEnumerable volumetricMaterialTakeoffs, IEnumerable materialMaps) { - IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); + if (volumetricMaterialTakeoffs.IsNullOrEmpty()) + return null; + + if (materialMaps == null || !materialMaps.Any()) + { + Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(VolumetricMaterialTakeoff)}s returned."); + return volumetricMaterialTakeoffs; + } + + IEnumerable allMaterials = volumetricMaterialTakeoffs.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps).ToDictionary(x => x.BHoM_Guid); - return materialCompositions.Select(x => new VolumetricMaterialTakeoff(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Volumes)).ToList(); + return volumetricMaterialTakeoffs.Select(x => new VolumetricMaterialTakeoff(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Volumes)).ToList(); } /***************************************************/ diff --git a/Matter_Engine/Query/MappedMaterialComposition.cs b/Matter_Engine/Query/MappedMaterialComposition.cs index 663840e30..bced7d50d 100644 --- a/Matter_Engine/Query/MappedMaterialComposition.cs +++ b/Matter_Engine/Query/MappedMaterialComposition.cs @@ -48,7 +48,10 @@ public static partial class Query [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] public static List MappedMaterialComposition(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true) { - return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).MapMaterial(materialMaps).ToList(); + if (elements == null || !elements.Any()) + return new List(); + + return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).MapMaterial(materialMaps).ToList(); } /***************************************************/ diff --git a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs index f96ce4689..29b2e85bd 100644 --- a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs +++ b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs @@ -48,6 +48,9 @@ public static partial class Query [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] public static List MappedVolumetricMaterialTakeoff(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true) { + if (elements == null || !elements.Any()) + return new List(); + return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).MapMaterial(materialMaps).ToList(); } From 84c08e13bc29b302c982c51cde30728acb051197 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Tue, 25 Oct 2022 14:47:33 +0200 Subject: [PATCH 05/14] More descriptions --- Matter_Engine/Modify/MapMaterial.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Matter_Engine/Modify/MapMaterial.cs b/Matter_Engine/Modify/MapMaterial.cs index eea0b586c..082ab2b8f 100644 --- a/Matter_Engine/Modify/MapMaterial.cs +++ b/Matter_Engine/Modify/MapMaterial.cs @@ -189,6 +189,7 @@ public static IEnumerable MapMaterial(this IEnumerabl /**** Private Methods ****/ /***************************************************/ + [Description("Method preforming the mapping work. Matches the Material first by name, secondly by name of properties using the provided lookup and dictionary.")] private static Material MapMaterial(this Material material, ILookup nameLookup, Dictionary, List> propertyMaps) { List matches = nameLookup[material.Name].ToList(); //Try match by name From 2fe81af8e7d49d77ca4250a2d2bb0bdd32e13a2f Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Thu, 27 Oct 2022 11:21:33 +0200 Subject: [PATCH 06/14] Make MergeProperties public and add better control over how uniqueness check and mapping should be done --- Matter_Engine/Modify/MapMaterial.cs | 43 +++----- Matter_Engine/Modify/MergeProperties.cs | 104 ++++++++++++++++++ .../Query/MappedMaterialComposition.cs | 7 +- .../Query/MappedVolumetricMaterialTakeoff.cs | 9 +- 4 files changed, 132 insertions(+), 31 deletions(-) create mode 100644 Matter_Engine/Modify/MergeProperties.cs diff --git a/Matter_Engine/Modify/MapMaterial.cs b/Matter_Engine/Modify/MapMaterial.cs index 082ab2b8f..30c0538a5 100644 --- a/Matter_Engine/Modify/MapMaterial.cs +++ b/Matter_Engine/Modify/MapMaterial.cs @@ -99,8 +99,11 @@ public static IEnumerable MapMaterial(this IEnumerable mater "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] [Input("materials", "The Materials to Modify, will be evaluated based on their name and properties.")] [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("prioritiseMap", "If true, proprties of same type/namespace on the materialMaps will be prioritised over the counterpart on the materials to modify. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material and found matching material map, the one of the material map will be used on the returned material.\n" + + "If false, proeprties on the materials will be prioritised over the maps. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material and found matching material map, the one of the main material will be used on the returned material.")] + [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materials", "Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] - public static IEnumerable MapMaterial(this IEnumerable materials, IEnumerable materialMaps) + public static IEnumerable MapMaterial(this IEnumerable materials, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) { if (materials.IsNullOrEmpty()) return null; @@ -131,7 +134,7 @@ public static IEnumerable MapMaterial(this IEnumerable mater foreach (Material material in materials) { - mappedMaterials.Add(material.MapMaterial(nameLookup, propertyMaps)); + mappedMaterials.Add(material.MapMaterial(nameLookup, propertyMaps, prioritiseMap, uniquePerNamespace)); } return mappedMaterials; } @@ -144,8 +147,11 @@ public static IEnumerable MapMaterial(this IEnumerable mater "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialCompositions will be evaluated based on the name and properties.")] [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("prioritiseMap", "If true, proprties of same type/namespace on the materialMaps will be prioritised over the counterpart on the materials to modify. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material and found matching material map, the one of the material map will be used on the returned material.\n" + + "If false, proeprties on the materials will be prioritised over the maps. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material and found matching material map, the one of the main material will be used on the returned material.")] + [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materialCompositions", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] - public static IEnumerable MapMaterial(this IEnumerable materialCompositions, IEnumerable materialMaps) + public static IEnumerable MapMaterial(this IEnumerable materialCompositions, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) { if (materialCompositions.IsNullOrEmpty()) return null; @@ -156,7 +162,7 @@ public static IEnumerable MapMaterial(this IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); - Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps).ToDictionary(x => x.BHoM_Guid); + Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps, prioritiseMap, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); return materialCompositions.Select(x => new MaterialComposition(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Ratios)).ToList(); } @@ -168,8 +174,11 @@ public static IEnumerable MapMaterial(this IEnumerable MapMaterial(this IEnumerable volumetricMaterialTakeoffs, IEnumerable materialMaps) + public static IEnumerable MapMaterial(this IEnumerable volumetricMaterialTakeoffs, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) { if (volumetricMaterialTakeoffs.IsNullOrEmpty()) return null; @@ -181,7 +190,7 @@ public static IEnumerable MapMaterial(this IEnumerabl } IEnumerable allMaterials = volumetricMaterialTakeoffs.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); - Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps).ToDictionary(x => x.BHoM_Guid); + Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps, prioritiseMap, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); return volumetricMaterialTakeoffs.Select(x => new VolumetricMaterialTakeoff(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Volumes)).ToList(); } @@ -190,7 +199,7 @@ public static IEnumerable MapMaterial(this IEnumerabl /***************************************************/ [Description("Method preforming the mapping work. Matches the Material first by name, secondly by name of properties using the provided lookup and dictionary.")] - private static Material MapMaterial(this Material material, ILookup nameLookup, Dictionary, List> propertyMaps) + private static Material MapMaterial(this Material material, ILookup nameLookup, Dictionary, List> propertyMaps, bool prioritiseMap, bool uniquePerNamespace) { List matches = nameLookup[material.Name].ToList(); //Try match by name @@ -214,7 +223,7 @@ private static Material MapMaterial(this Material material, ILookup(target.Properties); - List targetTypes = target.Properties.Select(x => x.GetType()).ToList(); - foreach (IMaterialProperties property in source.Properties) - { - if (targetTypes.Contains(property.GetType())) - { - targetClone.Properties.RemoveAll(x => x.GetType() == property.GetType()); - } - targetClone.Properties.Add(property); - } - return targetClone; - } - - /***************************************************/ [Description("Matches two materials based on how well the properties align based on name. For each aligning property the score is increased by 1. If a single misaligned property is found a score of -1 is returned.")] private static int MatchScore(this Material mat1, Material mat2) diff --git a/Matter_Engine/Modify/MergeProperties.cs b/Matter_Engine/Modify/MergeProperties.cs new file mode 100644 index 000000000..93f8f8a49 --- /dev/null +++ b/Matter_Engine/Modify/MergeProperties.cs @@ -0,0 +1,104 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2022, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using BH.oM.Base.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BH.Engine.Base; +using BH.oM.Physical.Materials; + +namespace BH.Engine.Matter +{ + public static partial class Modify + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Merges the Properties of the target and source by adding all properties on the source to the target. Checks for duplicate type/namespaces and resolves any duplicates found depending on settings provided.")] + [Input("target", "The material to merge the properties of the source onto. The Returned material will take name and other properties from the target.")] + [Input("source", "The source Material to grab proeprties from.")] + [Input("prioritiseSource", "If true, proprties of same type/namespace on the source will be prioritised over the counterpart on the target. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the target and source, the one of the source will be used on the returned material.\n" + + "If false, proeprties on the target will be prioritised. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the target and source, the one of the target will be used on the returned material.")] + [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the target and source based on namespace. If false, this check is instead done on exact type.")] + [Output("material", "Target material with Properties from the Source merged onto it.")] + public static Material MergeProperties(this Material target, Material source, bool prioritiseSource, bool uniquePerNamespace) + { + Material targetClone = target.ShallowClone(); //Clone the target to be returned + targetClone.Properties = new List(target.Properties); //Clone the target list + + if (!uniquePerNamespace) //If not unique by namespace, check properties by type + { + List targetTypes = target.Properties.Select(x => x.GetType()).ToList(); //Get out existing types on the target + foreach (IMaterialProperties property in source.Properties) + { + Type propType = property.GetType(); //Type of the property on the source to be added + if (prioritiseSource) //If true, source object should take priority if a duplicate is found + { + if (targetTypes.Contains(propType)) //If type exists + { + targetClone.Properties.RemoveAll(x => x.GetType() == propType); //Remove all instances of the type + } + targetClone.Properties.Add(property); //Add property from source + } + else //else (prioritiseMap is false) -> only add property if a property of that type does not already exist + { + if (!targetTypes.Contains(propType)) //If property not already existing + { + targetClone.Properties.Add(property); //Add + } + } + } + } + else //If unique by namespace + { + HashSet nameSpaces = new HashSet(target.Properties.Select(x => x.GetType().Namespace)); //Get out namespaces of all Proeprties on the target + foreach (IMaterialProperties property in source.Properties) + { + string propertyNamespace = property.GetType().Namespace; //Namespace of Property to be added + if (prioritiseSource) //Prioritise source + { + if (nameSpaces.Contains(propertyNamespace)) //Target contains items from the namespace + { + targetClone.Properties.RemoveAll(x => x.GetType().Namespace == propertyNamespace); //Remove all instances in the namespace + } + targetClone.Properties.Add(property); //Add the property + } + else //Prioritise target + { + if (!nameSpaces.Contains(propertyNamespace)) //Target does not contain a property in the namespace evaluated + { + targetClone.Properties.Add(property); //Add + } + } + } + } + return targetClone; + } + + /***************************************************/ + } +} diff --git a/Matter_Engine/Query/MappedMaterialComposition.cs b/Matter_Engine/Query/MappedMaterialComposition.cs index bced7d50d..38efa6a84 100644 --- a/Matter_Engine/Query/MappedMaterialComposition.cs +++ b/Matter_Engine/Query/MappedMaterialComposition.cs @@ -45,13 +45,16 @@ public static partial class Query [Input("elements", "The elements to fetch MaterialComposition from.")] [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] + [Input("prioritiseMap", "If true, proprties of same type/namespace on the materialMaps will be prioritised over the counterpart on the materials on the elements. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material of the element and found matching material map, the one of the material map will be used on the returned material.\n" + + "If false, proeprties on the materials of the element will be prioritised over the maps. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material of the element and found matching material map, the one of the material of the element will be used on the returned material.")] + [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] - public static List MappedMaterialComposition(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true) + public static List MappedMaterialComposition(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) { if (elements == null || !elements.Any()) return new List(); - return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).MapMaterial(materialMaps).ToList(); + return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).MapMaterial(materialMaps, prioritiseMap, uniquePerNamespace).ToList(); } /***************************************************/ diff --git a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs index 29b2e85bd..974629b81 100644 --- a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs +++ b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs @@ -45,13 +45,16 @@ public static partial class Query [Input("elements", "The elements to fetch VolumetricMaterialTakeoff from.")] [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] - [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] - public static List MappedVolumetricMaterialTakeoff(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true) + [Input("prioritiseMap", "If true, proprties of same type/namespace on the materialMaps will be prioritised over the counterpart on the materials on the elements. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material of the element and found matching material map, the one of the material map will be used on the returned material.\n" + + "If false, proeprties on the materials of the element will be prioritised over the maps. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material of the element and found matching material map, the one of the material of the element will be used on the returned material.")] + [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] + [Output("volumetricMaterialTakeoff", "The VolumetricMaterialTakeoff for each element with materials mapped to the provided transdiciplinary materials.")] + public static List MappedVolumetricMaterialTakeoff(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) { if (elements == null || !elements.Any()) return new List(); - return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).MapMaterial(materialMaps).ToList(); + return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).MapMaterial(materialMaps, prioritiseMap, uniquePerNamespace).ToList(); } /***************************************************/ From e166deb3265b62a4cdeddcc4e7e5b232bfdd56c1 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Thu, 27 Oct 2022 12:10:41 +0200 Subject: [PATCH 07/14] Null-handling for MergeProperties --- Matter_Engine/Modify/MergeProperties.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Matter_Engine/Modify/MergeProperties.cs b/Matter_Engine/Modify/MergeProperties.cs index 93f8f8a49..f699258bf 100644 --- a/Matter_Engine/Modify/MergeProperties.cs +++ b/Matter_Engine/Modify/MergeProperties.cs @@ -47,6 +47,18 @@ public static partial class Modify [Output("material", "Target material with Properties from the Source merged onto it.")] public static Material MergeProperties(this Material target, Material source, bool prioritiseSource, bool uniquePerNamespace) { + if (target == null) + { + Base.Compute.RecordError($"{nameof(target)} {nameof(Material)} is null. Unable to merge proeprties of source onto target. Null returned."); + return null; + } + + if (source == null) + { + Base.Compute.RecordWarning($"{nameof(source)} {nameof(Material)} is null. Unmodified target material returned."); + return target; + } + Material targetClone = target.ShallowClone(); //Clone the target to be returned targetClone.Properties = new List(target.Properties); //Clone the target list From 4acef3cb40e1bb890ab475ff27c19d5af3e1b934 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Thu, 27 Oct 2022 12:11:10 +0200 Subject: [PATCH 08/14] typo --- Matter_Engine/Modify/MergeProperties.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Matter_Engine/Modify/MergeProperties.cs b/Matter_Engine/Modify/MergeProperties.cs index f699258bf..01f81b34e 100644 --- a/Matter_Engine/Modify/MergeProperties.cs +++ b/Matter_Engine/Modify/MergeProperties.cs @@ -49,7 +49,7 @@ public static Material MergeProperties(this Material target, Material source, bo { if (target == null) { - Base.Compute.RecordError($"{nameof(target)} {nameof(Material)} is null. Unable to merge proeprties of source onto target. Null returned."); + Base.Compute.RecordError($"{nameof(target)} {nameof(Material)} is null. Unable to merge properties of source onto target. Null returned."); return null; } From a003326fb6d0147a0776dcb9449e3467e2c03643 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Thu, 3 Nov 2022 10:02:53 +0100 Subject: [PATCH 09/14] Update desc of priortise map and merge density --- Matter_Engine/Modify/MapMaterial.cs | 9 +++------ Matter_Engine/Modify/MergeProperties.cs | 8 ++++++-- Matter_Engine/Query/MappedMaterialComposition.cs | 3 +-- Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs | 3 +-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Matter_Engine/Modify/MapMaterial.cs b/Matter_Engine/Modify/MapMaterial.cs index 30c0538a5..847bc07e4 100644 --- a/Matter_Engine/Modify/MapMaterial.cs +++ b/Matter_Engine/Modify/MapMaterial.cs @@ -99,8 +99,7 @@ public static IEnumerable MapMaterial(this IEnumerable mater "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] [Input("materials", "The Materials to Modify, will be evaluated based on their name and properties.")] [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] - [Input("prioritiseMap", "If true, proprties of same type/namespace on the materialMaps will be prioritised over the counterpart on the materials to modify. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material and found matching material map, the one of the material map will be used on the returned material.\n" + - "If false, proeprties on the materials will be prioritised over the maps. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material and found matching material map, the one of the main material will be used on the returned material.")] + [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materials", "Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] public static IEnumerable MapMaterial(this IEnumerable materials, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) @@ -147,8 +146,7 @@ public static IEnumerable MapMaterial(this IEnumerable mater "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialCompositions will be evaluated based on the name and properties.")] [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] - [Input("prioritiseMap", "If true, proprties of same type/namespace on the materialMaps will be prioritised over the counterpart on the materials to modify. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material and found matching material map, the one of the material map will be used on the returned material.\n" + - "If false, proeprties on the materials will be prioritised over the maps. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material and found matching material map, the one of the main material will be used on the returned material.")] + [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materialCompositions", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] public static IEnumerable MapMaterial(this IEnumerable materialCompositions, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) @@ -174,8 +172,7 @@ public static IEnumerable MapMaterial(this IEnumerable MapMaterial(this IEnumerable volumetricMaterialTakeoffs, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) diff --git a/Matter_Engine/Modify/MergeProperties.cs b/Matter_Engine/Modify/MergeProperties.cs index 01f81b34e..ab625dd06 100644 --- a/Matter_Engine/Modify/MergeProperties.cs +++ b/Matter_Engine/Modify/MergeProperties.cs @@ -41,8 +41,7 @@ public static partial class Modify [Description("Merges the Properties of the target and source by adding all properties on the source to the target. Checks for duplicate type/namespaces and resolves any duplicates found depending on settings provided.")] [Input("target", "The material to merge the properties of the source onto. The Returned material will take name and other properties from the target.")] [Input("source", "The source Material to grab proeprties from.")] - [Input("prioritiseSource", "If true, proprties of same type/namespace on the source will be prioritised over the counterpart on the target. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the target and source, the one of the source will be used on the returned material.\n" + - "If false, proeprties on the target will be prioritised. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the target and source, the one of the target will be used on the returned material.")] + [Input("prioritiseSource", "Controls if target or source should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, source is prioritised, if false, target is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the target and source based on namespace. If false, this check is instead done on exact type.")] [Output("material", "Target material with Properties from the Source merged onto it.")] public static Material MergeProperties(this Material target, Material source, bool prioritiseSource, bool uniquePerNamespace) @@ -62,6 +61,11 @@ public static Material MergeProperties(this Material target, Material source, bo Material targetClone = target.ShallowClone(); //Clone the target to be returned targetClone.Properties = new List(target.Properties); //Clone the target list + if (double.IsNaN(targetClone.Density)) //If density of target is NaN, use density from source no matter the setting + targetClone.Density = source.Density; + else if (prioritiseSource && !double.IsNaN(source.Density)) //Density of target is not NaN, use source density if it is not NaN and if setting to prioritise source is true + targetClone.Density = source.Density; + if (!uniquePerNamespace) //If not unique by namespace, check properties by type { List targetTypes = target.Properties.Select(x => x.GetType()).ToList(); //Get out existing types on the target diff --git a/Matter_Engine/Query/MappedMaterialComposition.cs b/Matter_Engine/Query/MappedMaterialComposition.cs index 38efa6a84..925c0d07a 100644 --- a/Matter_Engine/Query/MappedMaterialComposition.cs +++ b/Matter_Engine/Query/MappedMaterialComposition.cs @@ -45,8 +45,7 @@ public static partial class Query [Input("elements", "The elements to fetch MaterialComposition from.")] [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] - [Input("prioritiseMap", "If true, proprties of same type/namespace on the materialMaps will be prioritised over the counterpart on the materials on the elements. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material of the element and found matching material map, the one of the material map will be used on the returned material.\n" + - "If false, proeprties on the materials of the element will be prioritised over the maps. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material of the element and found matching material map, the one of the material of the element will be used on the returned material.")] + [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] public static List MappedMaterialComposition(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) diff --git a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs index 974629b81..014c988a7 100644 --- a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs +++ b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs @@ -45,8 +45,7 @@ public static partial class Query [Input("elements", "The elements to fetch VolumetricMaterialTakeoff from.")] [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] - [Input("prioritiseMap", "If true, proprties of same type/namespace on the materialMaps will be prioritised over the counterpart on the materials on the elements. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material of the element and found matching material map, the one of the material map will be used on the returned material.\n" + - "If false, proeprties on the materials of the element will be prioritised over the maps. This means that if a property of a specific type/namespace (depending on uniquePerNamespace) exits on both the material of the element and found matching material map, the one of the material of the element will be used on the returned material.")] + [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("volumetricMaterialTakeoff", "The VolumetricMaterialTakeoff for each element with materials mapped to the provided transdiciplinary materials.")] public static List MappedVolumetricMaterialTakeoff(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) From 085b5b975252d67b2f1ea411944cd8a2d14f81e9 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Thu, 3 Nov 2022 14:01:15 +0100 Subject: [PATCH 10/14] Refactor out methods into multiple steps trying to make each part of the mapping clearer --- Matter_Engine/Modify/AssignTemplate.cs | 135 ++++++++++++++ Matter_Engine/Modify/MapMaterial.cs | 172 ------------------ Matter_Engine/Modify/MergeProperties.cs | 4 +- .../Query/MappedMaterialComposition.cs | 2 +- .../Query/MappedVolumetricMaterialTakeoff.cs | 2 +- Matter_Engine/Query/MatchMaterials.cs | 152 ++++++++++++++++ 6 files changed, 291 insertions(+), 176 deletions(-) create mode 100644 Matter_Engine/Modify/AssignTemplate.cs create mode 100644 Matter_Engine/Query/MatchMaterials.cs diff --git a/Matter_Engine/Modify/AssignTemplate.cs b/Matter_Engine/Modify/AssignTemplate.cs new file mode 100644 index 000000000..7b22a3c77 --- /dev/null +++ b/Matter_Engine/Modify/AssignTemplate.cs @@ -0,0 +1,135 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2022, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System; +using System.Linq; +using System.Collections.Generic; +using System.ComponentModel; +using BH.oM.Base.Attributes; +using BH.oM.Base; + +using BH.Engine.Base; +using BH.oM.Physical.Materials; + +namespace BH.Engine.Matter +{ + public static partial class Modify + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Maps a set of template materials to a set of model materials.\n" + + "First atempts to match the name of the provided materials to the material maps.\n" + + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + + "If a unique match is found based on one of the above matching methods, all Properties from the template material is applied to the model material matched.")] + [Input("modelMaterials", "The Materials to Modify, will be evaluated based on their name and properties.")] + [Input("tempalteMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] + [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] + [Output("materials", "Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] + public static IEnumerable AssignTemplate(this IEnumerable modelMaterials, IEnumerable templateMaterials, bool prioritiseTemplate = true, bool uniquePerNamespace = true) + { + if (modelMaterials.IsNullOrEmpty()) + return null; + + if (templateMaterials == null || !templateMaterials.Any()) + { + Base.Compute.RecordWarning($"No {nameof(templateMaterials)} provied. Unmapped {nameof(Material)}s returned."); + return modelMaterials; + } + + List materialList = modelMaterials.ToList(); + List matchedTemplates = materialList.MatchMaterials(templateMaterials.ToList()); + + List results = new List(); + + for (int i = 0; i < materialList.Count; i++) + { + if (matchedTemplates[i] != null) + results.Add(materialList[i].CombineMaterials(matchedTemplates[i], prioritiseTemplate, uniquePerNamespace)); + else + results.Add(materialList[i]); + + } + + return results; + } + + /***************************************************/ + + [Description("Maps a set of materials in the MaterialCompositions to a set of provided transdiciplinary materials.\n" + + "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] + [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialCompositions will be evaluated based on the name and properties.")] + [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] + [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] + [Output("materialCompositions", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] + public static IEnumerable AssignTemplate(this IEnumerable materialCompositions, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) + { + if (materialCompositions.IsNullOrEmpty()) + return null; + + if (materialMaps == null || !materialMaps.Any()) + { + Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(MaterialComposition)}s returned."); + return materialCompositions; + } + IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); + Dictionary mappedMaterials = allMaterials.AssignTemplate(materialMaps, prioritiseMap, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); + return materialCompositions.Select(x => new MaterialComposition(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Ratios)).ToList(); + } + + /***************************************************/ + + [Description("Maps a set of materials in the MaterialCompositions to a set of provided transdiciplinary materials.\n" + + "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] + [Input("volumetricMaterialTakeoffs", "The VolumetricMaterialTakeoff to Modify. Materials int he VolumetricMaterialTakeoff will be evaluated based on the name and properties.")] + [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] + [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] + [Output("volumetricMaterialTakeoffs", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] + public static IEnumerable AssignTemplate(this IEnumerable volumetricMaterialTakeoffs, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) + { + if (volumetricMaterialTakeoffs.IsNullOrEmpty()) + return null; + + if (materialMaps == null || !materialMaps.Any()) + { + Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(VolumetricMaterialTakeoff)}s returned."); + return volumetricMaterialTakeoffs; + } + + IEnumerable allMaterials = volumetricMaterialTakeoffs.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); + Dictionary mappedMaterials = allMaterials.AssignTemplate(materialMaps, prioritiseMap, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); + return volumetricMaterialTakeoffs.Select(x => new VolumetricMaterialTakeoff(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Volumes)).ToList(); + } + + /***************************************************/ + } +} + + diff --git a/Matter_Engine/Modify/MapMaterial.cs b/Matter_Engine/Modify/MapMaterial.cs index 847bc07e4..99f5d1a73 100644 --- a/Matter_Engine/Modify/MapMaterial.cs +++ b/Matter_Engine/Modify/MapMaterial.cs @@ -93,178 +93,6 @@ public static IEnumerable MapMaterial(this IEnumerable mater /***************************************************/ - [Description("Maps a set of materials to a set of provided transdiciplinary materials.\n" + - "First atempts to match the name of the provided materials to the material maps.\n" + - "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + - "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] - [Input("materials", "The Materials to Modify, will be evaluated based on their name and properties.")] - [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] - [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] - [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] - [Output("materials", "Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] - public static IEnumerable MapMaterial(this IEnumerable materials, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) - { - if (materials.IsNullOrEmpty()) - return null; - - if (materialMaps == null || !materialMaps.Any()) - { - Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(Material)}s returned."); - return materials; - } - - ILookup nameLookup = materialMaps.ToLookup(x => x.Name); - - Dictionary, List> propertyMaps = new Dictionary, List>(); - - foreach (Material mat in materialMaps) - { - foreach (IMaterialProperties property in mat.Properties) - { - Tuple key = new Tuple(property.GetType(), property.Name); - if (propertyMaps.ContainsKey(key)) - propertyMaps[key].Add(mat); - else - propertyMaps[key] = new List() { mat }; - } - } - - List mappedMaterials = new List(); - - foreach (Material material in materials) - { - mappedMaterials.Add(material.MapMaterial(nameLookup, propertyMaps, prioritiseMap, uniquePerNamespace)); - } - return mappedMaterials; - } - - /***************************************************/ - - [Description("Maps a set of materials in the MaterialCompositions to a set of provided transdiciplinary materials.\n" + - "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + - "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + - "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] - [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialCompositions will be evaluated based on the name and properties.")] - [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] - [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] - [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] - [Output("materialCompositions", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] - public static IEnumerable MapMaterial(this IEnumerable materialCompositions, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) - { - if (materialCompositions.IsNullOrEmpty()) - return null; - - if (materialMaps == null || !materialMaps.Any()) - { - Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(MaterialComposition)}s returned."); - return materialCompositions; - } - IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); - Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps, prioritiseMap, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); - return materialCompositions.Select(x => new MaterialComposition(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Ratios)).ToList(); - } - - /***************************************************/ - - [Description("Maps a set of materials in the MaterialCompositions to a set of provided transdiciplinary materials.\n" + - "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + - "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + - "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] - [Input("volumetricMaterialTakeoffs", "The VolumetricMaterialTakeoff to Modify. Materials int he VolumetricMaterialTakeoff will be evaluated based on the name and properties.")] - [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] - [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] - [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] - [Output("volumetricMaterialTakeoffs", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] - public static IEnumerable MapMaterial(this IEnumerable volumetricMaterialTakeoffs, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) - { - if (volumetricMaterialTakeoffs.IsNullOrEmpty()) - return null; - - if (materialMaps == null || !materialMaps.Any()) - { - Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(VolumetricMaterialTakeoff)}s returned."); - return volumetricMaterialTakeoffs; - } - - IEnumerable allMaterials = volumetricMaterialTakeoffs.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); - Dictionary mappedMaterials = allMaterials.MapMaterial(materialMaps, prioritiseMap, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); - return volumetricMaterialTakeoffs.Select(x => new VolumetricMaterialTakeoff(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Volumes)).ToList(); - } - - /***************************************************/ - /**** Private Methods ****/ - /***************************************************/ - - [Description("Method preforming the mapping work. Matches the Material first by name, secondly by name of properties using the provided lookup and dictionary.")] - private static Material MapMaterial(this Material material, ILookup nameLookup, Dictionary, List> propertyMaps, bool prioritiseMap, bool uniquePerNamespace) - { - List matches = nameLookup[material.Name].ToList(); //Try match by name - - if (matches.Count == 0) //If no name match found, try match by material proeprties - Type and name - { - foreach (IMaterialProperties property in material.Properties) - { - Tuple key = new Tuple(property.GetType(), property.Name); - - List propMatches; - if (propertyMaps.TryGetValue(key, out propMatches)) - matches.AddRange(propMatches); - } - - matches = matches.GroupBy(x => x.Name).Select(x => x.First()).ToList(); - matches = matches.GroupBy(x => x.MatchScore(material)) //Get and group by score - .Where(x => x.Key > 0) //Only keep materials with score > 0, as negative score indicates at least one mismatch - .OrderByDescending(x => x.Key) //Order by score - Higher score first - .FirstOrDefault()?.ToList() ?? new List(); - } - - if (matches.Count == 1) //Exactly one best match. Success! - { - return material.MergeProperties(matches[0], prioritiseMap, uniquePerNamespace); - } - else if (matches.Count == 0) //No matches, record warning - { - Base.Compute.RecordWarning($"No map found for material named {material.Name}"); - return material; - } - else //More than one match. Record warning - { - Base.Compute.RecordWarning($"Material named {material.Name} has ambiguous matches to {string.Join(", ", matches.Select(x => x.Name))}. No mapping preformed.\nEnsure that the transdiciplinaryMaterialMaps have unique names and that your Material has a name matching the transdiciplinary material you want to match."); - return material; - } - } - - /***************************************************/ - - - [Description("Matches two materials based on how well the properties align based on name. For each aligning property the score is increased by 1. If a single misaligned property is found a score of -1 is returned.")] - private static int MatchScore(this Material mat1, Material mat2) - { - //Method matches two materials based on their properties. - //For each proeprty of the same type and same name, the score is increased by 1. - //If a property of the same type but a different name is found the score is set to -1, as that means a mismatch of properties. - int score = 0; - ILookup matLookup = mat2.Properties.ToLookup(x => x.GetType()); - foreach (IMaterialProperties prop in mat1.Properties) - { - string name = prop.Name; - Type type = prop.GetType(); - - IEnumerable prop2 = matLookup[type]; - if (prop2.Any()) - { - if (prop2.Any(x => x.Name == name)) - score++; - else - return -1; - } - - } - return score; - } - - /***************************************************/ - } } diff --git a/Matter_Engine/Modify/MergeProperties.cs b/Matter_Engine/Modify/MergeProperties.cs index ab625dd06..4f2bf129d 100644 --- a/Matter_Engine/Modify/MergeProperties.cs +++ b/Matter_Engine/Modify/MergeProperties.cs @@ -44,7 +44,7 @@ public static partial class Modify [Input("prioritiseSource", "Controls if target or source should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, source is prioritised, if false, target is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the target and source based on namespace. If false, this check is instead done on exact type.")] [Output("material", "Target material with Properties from the Source merged onto it.")] - public static Material MergeProperties(this Material target, Material source, bool prioritiseSource, bool uniquePerNamespace) + public static Material CombineMaterials(this Material target, Material source, bool prioritiseSource, bool uniquePerNamespace) { if (target == null) { @@ -61,7 +61,7 @@ public static Material MergeProperties(this Material target, Material source, bo Material targetClone = target.ShallowClone(); //Clone the target to be returned targetClone.Properties = new List(target.Properties); //Clone the target list - if (double.IsNaN(targetClone.Density)) //If density of target is NaN, use density from source no matter the setting + if (double.IsNaN(targetClone.Density)) //If density on target is NaN, use density from source no matter the setting targetClone.Density = source.Density; else if (prioritiseSource && !double.IsNaN(source.Density)) //Density of target is not NaN, use source density if it is not NaN and if setting to prioritise source is true targetClone.Density = source.Density; diff --git a/Matter_Engine/Query/MappedMaterialComposition.cs b/Matter_Engine/Query/MappedMaterialComposition.cs index 925c0d07a..21460a4d9 100644 --- a/Matter_Engine/Query/MappedMaterialComposition.cs +++ b/Matter_Engine/Query/MappedMaterialComposition.cs @@ -53,7 +53,7 @@ public static List MappedMaterialComposition(this IEnumerab if (elements == null || !elements.Any()) return new List(); - return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).MapMaterial(materialMaps, prioritiseMap, uniquePerNamespace).ToList(); + return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).AssignTemplate(materialMaps, prioritiseMap, uniquePerNamespace).ToList(); } /***************************************************/ diff --git a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs index 014c988a7..d0abf896c 100644 --- a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs +++ b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs @@ -53,7 +53,7 @@ public static List MappedVolumetricMaterialTakeoff(th if (elements == null || !elements.Any()) return new List(); - return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).MapMaterial(materialMaps, prioritiseMap, uniquePerNamespace).ToList(); + return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).AssignTemplate(materialMaps, prioritiseMap, uniquePerNamespace).ToList(); } /***************************************************/ diff --git a/Matter_Engine/Query/MatchMaterials.cs b/Matter_Engine/Query/MatchMaterials.cs new file mode 100644 index 000000000..c1a6e89c5 --- /dev/null +++ b/Matter_Engine/Query/MatchMaterials.cs @@ -0,0 +1,152 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2022, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.Engine.Base; +using BH.oM.Base; +using BH.oM.Base.Attributes; +using BH.oM.Physical.Materials; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace BH.Engine.Matter +{ + public static partial class Query + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + + [Description("Maps a set of model materials to a set of template materials by name and returns the found tempaltes, i.e. finds a material in the list of template materials that has the same name as the material in the model material and returns it. Returns null if nothing is found. The list order of the returned materials correspond to the list order of the input materials.")] + [Input("modelMaterials", "The materials to find a matching template to.")] + [Input("tempalteMaterials", "The template materials to scan.")] + [Output("matchedMaterials", "The a list of template materials matched to the model materials by name. Order corresponds to the model materials. Method returns null for cases where no match is found.")] + public static List MatchMaterials(this List modelMaterials, List templateMaterials) + { + if (modelMaterials.IsNullOrEmpty()) + return null; + + if (templateMaterials == null || !templateMaterials.Any()) + { + Base.Compute.RecordWarning($"No {nameof(templateMaterials)} provied. List of nulls returned."); + return Enumerable.Repeat(null, modelMaterials.Count).ToList(); + } + + ILookup nameLookup = templateMaterials.ToLookup(x => x.Name); + + Dictionary, List> propertyMaps = new Dictionary, List>(); + + foreach (Material mat in templateMaterials) + { + foreach (IMaterialProperties property in mat.Properties) + { + Tuple key = new Tuple(property.GetType(), property.Name); + if (propertyMaps.ContainsKey(key)) + propertyMaps[key].Add(mat); + else + propertyMaps[key] = new List() { mat }; + } + } + return modelMaterials.Select(x => x.MatchMaterial(nameLookup, propertyMaps)).ToList(); + } + + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + [Description("Method preforming the mapping work. Matches the Material first by name, secondly by name of properties using the provided lookup and dictionary.")] + private static Material MatchMaterial(this Material material, ILookup nameLookup, Dictionary, List> propertyMaps) + { + List matches = nameLookup[material.Name].ToList(); //Try match by name + + if (matches.Count == 0) //If no name match found, try match by material proeprties - Type and name + { + foreach (IMaterialProperties property in material.Properties) + { + Tuple key = new Tuple(property.GetType(), property.Name); + + List propMatches; + if (propertyMaps.TryGetValue(key, out propMatches)) + matches.AddRange(propMatches); + } + + matches = matches.GroupBy(x => x.Name).Select(x => x.First()).ToList(); + matches = matches.GroupBy(x => x.MatchScore(material)) //Get and group by score + .Where(x => x.Key > 0) //Only keep materials with score > 0, as negative score indicates at least one mismatch + .OrderByDescending(x => x.Key) //Order by score - Higher score first + .FirstOrDefault()?.ToList() ?? new List(); + } + + if (matches.Count == 1) //Exactly one best match. Success! + { + return matches[0]; + } + else if (matches.Count == 0) //No matches, record warning + { + Base.Compute.RecordWarning($"No map found for material named {material.Name}"); + return null; + } + else //More than one match. Record warning + { + Base.Compute.RecordWarning($"Material named {material.Name} has ambiguous matches to {string.Join(", ", matches.Select(x => x.Name))}. No mapping preformed.\nEnsure that the transdiciplinaryMaterialMaps have unique names and that your Material has a name matching the transdiciplinary material you want to match."); + return null; + } + } + + /***************************************************/ + + + [Description("Matches two materials based on how well the properties align based on name. For each aligning property the score is increased by 1. If a single misaligned property is found a score of -1 is returned.")] + private static int MatchScore(this Material mat1, Material mat2) + { + //Method matches two materials based on their properties. + //For each proeprty of the same type and same name, the score is increased by 1. + //If a property of the same type but a different name is found the score is set to -1, as that means a mismatch of properties. + int score = 0; + ILookup matLookup = mat2.Properties.ToLookup(x => x.GetType()); + foreach (IMaterialProperties prop in mat1.Properties) + { + string name = prop.Name; + Type type = prop.GetType(); + + IEnumerable prop2 = matLookup[type]; + if (prop2.Any()) + { + if (prop2.Any(x => x.Name == name)) + score++; + else + return -1; + } + + } + return score; + } + + /***************************************************/ + + + /***************************************************/ + } +} From fc166172b444216ba9efa0a55374b6c228e35451 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Thu, 3 Nov 2022 14:25:07 +0100 Subject: [PATCH 11/14] Further description and naming tweaks --- Matter_Engine/Modify/AssignTemplate.cs | 28 +++++++++---------- ...MergeProperties.cs => CombineMaterials.cs} | 0 .../Query/MappedMaterialComposition.cs | 8 +++--- .../Query/MappedVolumetricMaterialTakeoff.cs | 8 +++--- Matter_Engine/Query/MatchMaterials.cs | 3 -- 5 files changed, 22 insertions(+), 25 deletions(-) rename Matter_Engine/Modify/{MergeProperties.cs => CombineMaterials.cs} (100%) diff --git a/Matter_Engine/Modify/AssignTemplate.cs b/Matter_Engine/Modify/AssignTemplate.cs index 7b22a3c77..eea15fd09 100644 --- a/Matter_Engine/Modify/AssignTemplate.cs +++ b/Matter_Engine/Modify/AssignTemplate.cs @@ -43,8 +43,8 @@ public static partial class Modify "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + "If a unique match is found based on one of the above matching methods, all Properties from the template material is applied to the model material matched.")] [Input("modelMaterials", "The Materials to Modify, will be evaluated based on their name and properties.")] - [Input("tempalteMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] - [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] + [Input("templateMaterials", "The template materials to match to and assign properties from onto the model materials. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materials", "Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] public static IEnumerable AssignTemplate(this IEnumerable modelMaterials, IEnumerable templateMaterials, bool prioritiseTemplate = true, bool uniquePerNamespace = true) @@ -82,22 +82,22 @@ public static IEnumerable AssignTemplate(this IEnumerable mo "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialCompositions will be evaluated based on the name and properties.")] - [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] - [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] + [Input("templateMaterials", "The template materials to match to and assign properties from onto the model materials. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materialCompositions", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] - public static IEnumerable AssignTemplate(this IEnumerable materialCompositions, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) + public static IEnumerable AssignTemplate(this IEnumerable materialCompositions, IEnumerable templateMaterials, bool prioritiseTemplate = true, bool uniquePerNamespace = true) { if (materialCompositions.IsNullOrEmpty()) return null; - if (materialMaps == null || !materialMaps.Any()) + if (templateMaterials == null || !templateMaterials.Any()) { - Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(MaterialComposition)}s returned."); + Base.Compute.RecordWarning($"No {nameof(templateMaterials)} provied. Unmapped {nameof(MaterialComposition)}s returned."); return materialCompositions; } IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); - Dictionary mappedMaterials = allMaterials.AssignTemplate(materialMaps, prioritiseMap, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); + Dictionary mappedMaterials = allMaterials.AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); return materialCompositions.Select(x => new MaterialComposition(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Ratios)).ToList(); } @@ -108,23 +108,23 @@ public static IEnumerable AssignTemplate(this IEnumerable AssignTemplate(this IEnumerable volumetricMaterialTakeoffs, IEnumerable materialMaps, bool prioritiseMap = true, bool uniquePerNamespace = true) + public static IEnumerable AssignTemplate(this IEnumerable volumetricMaterialTakeoffs, IEnumerable templateMaterials, bool prioritiseTemplate = true, bool uniquePerNamespace = true) { if (volumetricMaterialTakeoffs.IsNullOrEmpty()) return null; - if (materialMaps == null || !materialMaps.Any()) + if (templateMaterials == null || !templateMaterials.Any()) { - Base.Compute.RecordWarning($"No {nameof(materialMaps)} provied. Unmapped {nameof(VolumetricMaterialTakeoff)}s returned."); + Base.Compute.RecordWarning($"No {nameof(templateMaterials)} provied. Unmapped {nameof(VolumetricMaterialTakeoff)}s returned."); return volumetricMaterialTakeoffs; } IEnumerable allMaterials = volumetricMaterialTakeoffs.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); - Dictionary mappedMaterials = allMaterials.AssignTemplate(materialMaps, prioritiseMap, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); + Dictionary mappedMaterials = allMaterials.AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); return volumetricMaterialTakeoffs.Select(x => new VolumetricMaterialTakeoff(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Volumes)).ToList(); } diff --git a/Matter_Engine/Modify/MergeProperties.cs b/Matter_Engine/Modify/CombineMaterials.cs similarity index 100% rename from Matter_Engine/Modify/MergeProperties.cs rename to Matter_Engine/Modify/CombineMaterials.cs diff --git a/Matter_Engine/Query/MappedMaterialComposition.cs b/Matter_Engine/Query/MappedMaterialComposition.cs index 21460a4d9..cb14cd2e5 100644 --- a/Matter_Engine/Query/MappedMaterialComposition.cs +++ b/Matter_Engine/Query/MappedMaterialComposition.cs @@ -43,17 +43,17 @@ public static partial class Query "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] [Input("elements", "The elements to fetch MaterialComposition from.")] - [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("templateMaterials", "The template materials to match to and assign properties from onto the model materials. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] - [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] + [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] - public static List MappedMaterialComposition(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) + public static List MappedMaterialComposition(this IEnumerable elements, IEnumerable templateMaterials, bool checkForTakeoffFragment = true, bool prioritiseTemplate = true, bool uniquePerNamespace = true) { if (elements == null || !elements.Any()) return new List(); - return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).AssignTemplate(materialMaps, prioritiseMap, uniquePerNamespace).ToList(); + return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace).ToList(); } /***************************************************/ diff --git a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs index d0abf896c..aab517576 100644 --- a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs +++ b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs @@ -43,17 +43,17 @@ public static partial class Query "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] [Input("elements", "The elements to fetch VolumetricMaterialTakeoff from.")] - [Input("materialMaps", "The Material maps to match to. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] + [Input("templateMaterials", "The template materials to match to and assign properties from onto the model materials. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] - [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] + [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("volumetricMaterialTakeoff", "The VolumetricMaterialTakeoff for each element with materials mapped to the provided transdiciplinary materials.")] - public static List MappedVolumetricMaterialTakeoff(this IEnumerable elements, IEnumerable materialMaps, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) + public static List MappedVolumetricMaterialTakeoff(this IEnumerable elements, IEnumerable templateMaterials, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) { if (elements == null || !elements.Any()) return new List(); - return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).AssignTemplate(materialMaps, prioritiseMap, uniquePerNamespace).ToList(); + return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).AssignTemplate(templateMaterials, prioritiseMap, uniquePerNamespace).ToList(); } /***************************************************/ diff --git a/Matter_Engine/Query/MatchMaterials.cs b/Matter_Engine/Query/MatchMaterials.cs index c1a6e89c5..8b54fac72 100644 --- a/Matter_Engine/Query/MatchMaterials.cs +++ b/Matter_Engine/Query/MatchMaterials.cs @@ -144,9 +144,6 @@ private static int MatchScore(this Material mat1, Material mat2) return score; } - /***************************************************/ - - /***************************************************/ } } From f51c4fe28e06ba895d29727c24aeef674703d79b Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Fri, 4 Nov 2022 08:27:11 +0100 Subject: [PATCH 12/14] Change from working with lists to instead single items when assigning templates for compositions and takeoffs Previous version worked with lists to aim to achieve some speeder run of the methods, but made them harder to work with. Changing to run element by element, as this is a relatively quick process anyway --- Matter_Engine/Modify/AssignTemplate.cs | 24 ++++++++----------- .../Query/MappedMaterialComposition.cs | 8 +++---- .../Query/MappedVolumetricMaterialTakeoff.cs | 8 +++---- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Matter_Engine/Modify/AssignTemplate.cs b/Matter_Engine/Modify/AssignTemplate.cs index eea15fd09..9ae502241 100644 --- a/Matter_Engine/Modify/AssignTemplate.cs +++ b/Matter_Engine/Modify/AssignTemplate.cs @@ -81,24 +81,22 @@ public static IEnumerable AssignTemplate(this IEnumerable mo "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] - [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialCompositions will be evaluated based on the name and properties.")] + [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialComposition will be evaluated based on the name and properties.")] [Input("templateMaterials", "The template materials to match to and assign properties from onto the model materials. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materialCompositions", "MaterialComposition with Materials with modified list of properties. Materials for which no unique match could be found are unaffected.")] - public static IEnumerable AssignTemplate(this IEnumerable materialCompositions, IEnumerable templateMaterials, bool prioritiseTemplate = true, bool uniquePerNamespace = true) + public static MaterialComposition AssignTemplate(this MaterialComposition materialComposition, IEnumerable templateMaterials, bool prioritiseTemplate = true, bool uniquePerNamespace = true) { - if (materialCompositions.IsNullOrEmpty()) + if (materialComposition == null) return null; if (templateMaterials == null || !templateMaterials.Any()) { Base.Compute.RecordWarning($"No {nameof(templateMaterials)} provied. Unmapped {nameof(MaterialComposition)}s returned."); - return materialCompositions; + return materialComposition; } - IEnumerable allMaterials = materialCompositions.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); - Dictionary mappedMaterials = allMaterials.AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); - return materialCompositions.Select(x => new MaterialComposition(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Ratios)).ToList(); + return new MaterialComposition(materialComposition.Materials.AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace), materialComposition.Ratios); } /***************************************************/ @@ -107,25 +105,23 @@ public static IEnumerable AssignTemplate(this IEnumerable AssignTemplate(this IEnumerable volumetricMaterialTakeoffs, IEnumerable templateMaterials, bool prioritiseTemplate = true, bool uniquePerNamespace = true) + public static VolumetricMaterialTakeoff AssignTemplate(this VolumetricMaterialTakeoff volumetricMaterialTakeoff, IEnumerable templateMaterials, bool prioritiseTemplate = true, bool uniquePerNamespace = true) { - if (volumetricMaterialTakeoffs.IsNullOrEmpty()) + if (volumetricMaterialTakeoff == null) return null; if (templateMaterials == null || !templateMaterials.Any()) { Base.Compute.RecordWarning($"No {nameof(templateMaterials)} provied. Unmapped {nameof(VolumetricMaterialTakeoff)}s returned."); - return volumetricMaterialTakeoffs; + return volumetricMaterialTakeoff; } - IEnumerable allMaterials = volumetricMaterialTakeoffs.SelectMany(x => x.Materials).GroupBy(x => x.BHoM_Guid).Select(x => x.First()); - Dictionary mappedMaterials = allMaterials.AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace).ToDictionary(x => x.BHoM_Guid); - return volumetricMaterialTakeoffs.Select(x => new VolumetricMaterialTakeoff(x.Materials.Select(mat => mappedMaterials[mat.BHoM_Guid]), x.Volumes)).ToList(); + return new VolumetricMaterialTakeoff(volumetricMaterialTakeoff.Materials.AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace), volumetricMaterialTakeoff.Volumes); } /***************************************************/ diff --git a/Matter_Engine/Query/MappedMaterialComposition.cs b/Matter_Engine/Query/MappedMaterialComposition.cs index cb14cd2e5..383a2c090 100644 --- a/Matter_Engine/Query/MappedMaterialComposition.cs +++ b/Matter_Engine/Query/MappedMaterialComposition.cs @@ -48,12 +48,12 @@ public static partial class Query [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("materialComposition", "The material compositions for each element with materials mapped to the provided transdiciplinary materials.")] - public static List MappedMaterialComposition(this IEnumerable elements, IEnumerable templateMaterials, bool checkForTakeoffFragment = true, bool prioritiseTemplate = true, bool uniquePerNamespace = true) + public static MaterialComposition MappedMaterialComposition(this IElementM element, IEnumerable templateMaterials, bool checkForTakeoffFragment = true, bool prioritiseTemplate = true, bool uniquePerNamespace = true) { - if (elements == null || !elements.Any()) - return new List(); + if (element == null) + return null; - return elements.Select(x => x.IMaterialComposition(checkForTakeoffFragment)).AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace).ToList(); + return element.IMaterialComposition(checkForTakeoffFragment).AssignTemplate(templateMaterials, prioritiseTemplate, uniquePerNamespace); } /***************************************************/ diff --git a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs index aab517576..1e76656a0 100644 --- a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs +++ b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs @@ -48,12 +48,12 @@ public static partial class Query [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("volumetricMaterialTakeoff", "The VolumetricMaterialTakeoff for each element with materials mapped to the provided transdiciplinary materials.")] - public static List MappedVolumetricMaterialTakeoff(this IEnumerable elements, IEnumerable templateMaterials, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) + public static VolumetricMaterialTakeoff MappedVolumetricMaterialTakeoff(this IElementM element, IEnumerable templateMaterials, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) { - if (elements == null || !elements.Any()) - return new List(); + if (element == null) + return null; - return elements.Select(x => x.IVolumetricMaterialTakeoff(checkForTakeoffFragment)).AssignTemplate(templateMaterials, prioritiseMap, uniquePerNamespace).ToList(); + return element.IVolumetricMaterialTakeoff(checkForTakeoffFragment).AssignTemplate(templateMaterials, prioritiseMap, uniquePerNamespace); } /***************************************************/ From 045526ec483c26a6f75743ca1e5c802728453a88 Mon Sep 17 00:00:00 2001 From: Michael Hoehn Date: Thu, 1 Dec 2022 15:16:54 -0500 Subject: [PATCH 13/14] Documentation compliance fixes --- Matter_Engine/Modify/AssignTemplate.cs | 2 +- Matter_Engine/Query/MappedMaterialComposition.cs | 2 +- Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Matter_Engine/Modify/AssignTemplate.cs b/Matter_Engine/Modify/AssignTemplate.cs index 9ae502241..274f695be 100644 --- a/Matter_Engine/Modify/AssignTemplate.cs +++ b/Matter_Engine/Modify/AssignTemplate.cs @@ -81,7 +81,7 @@ public static IEnumerable AssignTemplate(this IEnumerable mo "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] - [Input("materialCompositions", "The MaterialCompositions to Modify. Materials int he MaterialComposition will be evaluated based on the name and properties.")] + [Input("materialComposition", "The MaterialCompositions to Modify. Materials int he MaterialComposition will be evaluated based on the name and properties.")] [Input("templateMaterials", "The template materials to match to and assign properties from onto the model materials. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials and found matching material map based on namespace. If false, this check is instead done on exact type.")] diff --git a/Matter_Engine/Query/MappedMaterialComposition.cs b/Matter_Engine/Query/MappedMaterialComposition.cs index 383a2c090..22a2cb745 100644 --- a/Matter_Engine/Query/MappedMaterialComposition.cs +++ b/Matter_Engine/Query/MappedMaterialComposition.cs @@ -42,7 +42,7 @@ public static partial class Query "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] - [Input("elements", "The elements to fetch MaterialComposition from.")] + [Input("element", "The elements to fetch MaterialComposition from.")] [Input("templateMaterials", "The template materials to match to and assign properties from onto the model materials. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] diff --git a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs index 1e76656a0..61d12d6f1 100644 --- a/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs +++ b/Matter_Engine/Query/MappedVolumetricMaterialTakeoff.cs @@ -42,10 +42,10 @@ public static partial class Query "First atempts to match the name of the provided materials to the transdiciplinary material maps.\n" + "If no name match is found, atempts to instead find a material with as many matching MaterialProperties (based on type and name) as possible.\n" + "If a unique match is found based on one of the above matching methods, all Properties from the transdiciplinary material is applied to the material to be matched.")] - [Input("elements", "The elements to fetch VolumetricMaterialTakeoff from.")] + [Input("element", "The elements to fetch VolumetricMaterialTakeoff from.")] [Input("templateMaterials", "The template materials to match to and assign properties from onto the model materials. Should generally have unique names. Names of material as well as material properties will be used to map to the materials to be modified.")] [Input("checkForTakeoffFragment", "If true and the provided element is a BHoMObject, the incoming item is checked if it has a VolumetricMaterialTakeoff fragment attached, and if so, returns that Material composition corresponding to this fragment. If false, the MaterialComposition returned will be calculated, independant of fragment attached.")] - [Input("prioritiseTemplate", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] + [Input("prioritiseMap", "Controls if main material or map material should be prioritised when conflicting information is found on both in terms of Density and/or Properties. If true, map is prioritised, if false, main material is prioritised.")] [Input("uniquePerNamespace", "If true, the method is checking for similarity of MaterialProperties on the materials of the element and found matching material map based on namespace. If false, this check is instead done on exact type.")] [Output("volumetricMaterialTakeoff", "The VolumetricMaterialTakeoff for each element with materials mapped to the provided transdiciplinary materials.")] public static VolumetricMaterialTakeoff MappedVolumetricMaterialTakeoff(this IElementM element, IEnumerable templateMaterials, bool checkForTakeoffFragment = true, bool prioritiseMap = true, bool uniquePerNamespace = true) From 0b97d0338ef40737545cd72d40c8d4203e415b8e Mon Sep 17 00:00:00 2001 From: Michael Hoehn Date: Thu, 1 Dec 2022 15:25:19 -0500 Subject: [PATCH 14/14] missed one --- Matter_Engine/Query/MatchMaterials.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Matter_Engine/Query/MatchMaterials.cs b/Matter_Engine/Query/MatchMaterials.cs index 8b54fac72..d71227673 100644 --- a/Matter_Engine/Query/MatchMaterials.cs +++ b/Matter_Engine/Query/MatchMaterials.cs @@ -40,7 +40,7 @@ public static partial class Query [Description("Maps a set of model materials to a set of template materials by name and returns the found tempaltes, i.e. finds a material in the list of template materials that has the same name as the material in the model material and returns it. Returns null if nothing is found. The list order of the returned materials correspond to the list order of the input materials.")] [Input("modelMaterials", "The materials to find a matching template to.")] - [Input("tempalteMaterials", "The template materials to scan.")] + [Input("templateMaterials", "The template materials to scan.")] [Output("matchedMaterials", "The a list of template materials matched to the model materials by name. Order corresponds to the model materials. Method returns null for cases where no match is found.")] public static List MatchMaterials(this List modelMaterials, List templateMaterials) {