From 87a098f32ffdec67b13ce118f95d53bc6d090e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20N=C3=A4slund?= Date: Fri, 28 Aug 2026 11:44:54 +0200 Subject: [PATCH 1/5] Method for extracting MaterialClassification from Material or IMaterialProperties --- Matter_Engine/Query/MaterialClassification.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Matter_Engine/Query/MaterialClassification.cs diff --git a/Matter_Engine/Query/MaterialClassification.cs b/Matter_Engine/Query/MaterialClassification.cs new file mode 100644 index 000000000..a3acc9b1a --- /dev/null +++ b/Matter_Engine/Query/MaterialClassification.cs @@ -0,0 +1,78 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.Dimensional; +using BH.oM.Geometry; +using BH.oM.Physical.Materials; +using BH.oM.Quantities.Attributes; +using BH.oM.Base.Attributes; +using System.ComponentModel; +using System.Linq; +using BH.Engine.Base; + +namespace BH.Engine.Matter +{ + public static partial class Query + { + /******************************************/ + /**** Public Methods ****/ + /******************************************/ + + [Description("Evaluates the material classification of a material.")] + [Input("material", "The material to evaluate the classification of.")] + [Output("classification", "The material classification.")] + public static MaterialClassification MaterialClassification(this Material material) + { + if(material == null) + { + Base.Compute.RecordError("Cannot query the material classification of a null material."); + return null; + } + + return material.Properties.OfType().FirstOrDefault(); + } + + /******************************************/ + + [Description("Evaluates the material classification of a material.")] + [Input("materialProperty", "The material to evaluate the classification of.")] + [Output("classification", "The material classification.")] + public static MaterialClassification MaterialClassification(this IMaterialProperties materialProperty) + { + if(materialProperty == null) + { + Base.Compute.RecordError("Cannot query the material classification of a null material."); + return null; + } + + return materialProperty.FindFragment(); + } + + /******************************************/ + } +} + + + + + + From b1ceaf4e44de25862c14ef1968b34b287a370a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20N=C3=A4slund?= Date: Fri, 28 Aug 2026 11:45:42 +0200 Subject: [PATCH 2/5] Method for Filtering by classification (or its parts) --- Matter_Engine/Query/FilterByClassification.cs | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 Matter_Engine/Query/FilterByClassification.cs diff --git a/Matter_Engine/Query/FilterByClassification.cs b/Matter_Engine/Query/FilterByClassification.cs new file mode 100644 index 000000000..a6bb63a93 --- /dev/null +++ b/Matter_Engine/Query/FilterByClassification.cs @@ -0,0 +1,172 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.Dimensional; +using BH.oM.Geometry; +using BH.oM.Physical.Materials; +using BH.oM.Quantities.Attributes; +using BH.oM.Base.Attributes; +using System.ComponentModel; +using System.Linq; +using BH.Engine.Base; +using System.Collections.Generic; +using System; + +namespace BH.Engine.Matter +{ + public static partial class Query + { + /******************************************/ + /**** Public Methods ****/ + /******************************************/ + + [Description("Filters a collection of Material by a MaterialClassification. Requires the provided Materials to contain a MaterialClassification as one of its properties to be able to filter. The filtering is done by matching all non-empty properties of the classification, e.g. if the classification has a non-empty Category and Type, but an empty Grade and Constituent, then all materials with the same Category and Type will be returned.")] + [Input("materials", "The collection of materials to filter.")] + [Input("classification", "The material classification to filter by.")] + [Output("filteredMaterials", "The filtered collection of materials.")] + public static List FilterByClassification(this IEnumerable materials, MaterialClassification classification) + { + if(materials == null) + { + Base.Compute.RecordError("Cannot filter by material classification on a null collection of materials."); + return null; + } + + return materials.Where(m => m.MaterialClassification().ClassificationMatches(classification)).ToList(); + } + + /******************************************/ + + [Description("Filters a collection of Material by classification properties. Requires the provided Materials to contain a MaterialClassification as one of its properties to be able to filter. The filtering is done by matching all non-empty provided properties, e.g. if the category and type are provided, but grade and constituent are empty, then all materials with the same category and type will be returned.")] + [Input("materials", "The collection of materials to filter.")] + [Input("category", "The category to filter by. If omitted or empty, this property will not be used for filtering.")] + [Input("type", "The type to filter by. If omitted or empty, this property will not be used for filtering.")] + [Input("grade", "The grade to filter by. If omitted or empty, this property will not be used for filtering.")] + [Input("constituent", "The constituent to filter by. If omitted or empty, this property will not be used for filtering.")] + [Output("filteredMaterials", "The filtered collection of materials.")] + public static List FilterByClassification(this IEnumerable materials, string category = "", string type = "", string grade = "", string constituent = "") + { + if(materials == null) + { + Base.Compute.RecordError("Cannot filter by material classification on a null collection of materials."); + return null; + } + + if(string.IsNullOrWhiteSpace(category) && string.IsNullOrWhiteSpace(type) && string.IsNullOrWhiteSpace(grade) && string.IsNullOrWhiteSpace(constituent)) + { + return materials.ToList(); + } + + return materials.FilterByClassification(new MaterialClassification() { Category = category, Type = type, Grade = grade, Constituent = constituent }); + } + + /******************************************/ + + [Description("Filters a collection of IMaterialProperties by a material classification. Requires the provided IMaterialProperties to contain a MaterialClassification as a Fragment to be able to filter. The filtering is done by matching all non-empty properties of the classification, e.g. if the classification has a non-empty Category and Type, but an empty Grade and Constituent, then all materials with the same Category and Type will be returned.")] + [Input("materialProperties", "The collection of material properties to filter.")] + [Input("classification", "The material classification to filter by.")] + [Output("filteredProperties", "The filtered collection of material properties.")] + public static List FilterByClassification(this IEnumerable materialProperties, MaterialClassification classification) where T : IMaterialProperties + { + if(materialProperties == null) + { + Base.Compute.RecordError("Cannot filter by material classification on a null collection of material properties."); + return null; + } + + return materialProperties.Where(m => m.MaterialClassification().ClassificationMatches(classification)).ToList(); + } + + /******************************************/ + + [Description("Filters a collection of IMaterialProperties by classification properties. Requires the provided IMaterialProperties to contain a MaterialClassification as a Fragment to be able to filter. The filtering is done by matching all non-empty provided properties, e.g. if the category and type are provided, but grade and constituent are empty, then all materials with the same category and type will be returned.")] + [Input("materials", "The collection of materials to filter.")] + [Input("category", "The category to filter by. If omitted or empty, this property will not be used for filtering.")] + [Input("type", "The type to filter by. If omitted or empty, this property will not be used for filtering.")] + [Input("grade", "The grade to filter by. If omitted or empty, this property will not be used for filtering.")] + [Input("constituent", "The constituent to filter by. If omitted or empty, this property will not be used for filtering.")] + [Output("filteredMaterials", "The filtered collection of materials.")] + public static List FilterByClassification(this IEnumerable materials, string category = "", string type = "", string grade = "", string constituent = "") where T : IMaterialProperties + { + if(materials == null) + { + Base.Compute.RecordError("Cannot filter by material classification on a null collection of material properties."); + return null; + } + + if(string.IsNullOrWhiteSpace(category) && string.IsNullOrWhiteSpace(type) && string.IsNullOrWhiteSpace(grade) && string.IsNullOrWhiteSpace(constituent)) + { + return materials.ToList(); + } + + return materials.FilterByClassification(new MaterialClassification() { Category = category, Type = type, Grade = grade, Constituent = constituent }); + } + + /******************************************/ + /**** Private Methods ****/ + /******************************************/ + + private static bool ClassificationMatches(this MaterialClassification classification, MaterialClassification other) + { + if(classification == null || other == null) + return false; + + //Ignore case when comparing classification properties + //This is to avoid issues with different casing in the classification properties, e.g. "Concrete" vs "concrete" + StringComparison comparison = StringComparison.OrdinalIgnoreCase; + + if(!string.IsNullOrWhiteSpace(other.Category)) + { + if(!string.Equals(classification.Category, other.Category, comparison)) + return false; + } + + if(!string.IsNullOrWhiteSpace(other.Type)) + { + if(!string.Equals(classification.Type, other.Type, comparison)) + return false; + } + + if(!string.IsNullOrWhiteSpace(other.Grade)) + { + if(!string.Equals(classification.Grade, other.Grade, comparison)) + return false; + } + + if(!string.IsNullOrWhiteSpace(other.Constituent)) + { + if(!string.Equals(classification.Constituent, other.Constituent, comparison)) + return false; + } + + return true; + } + + /******************************************/ + } +} + + + + + + From d7b9e0e66968a38a8af30daa85e5b5eee4626479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20N=C3=A4slund?= Date: Fri, 28 Aug 2026 11:46:45 +0200 Subject: [PATCH 3/5] Methods for extracting out available Categories, Types etc given a set of materials or IMaterialProperties --- Matter_Engine/Query/AvailableCategories.cs | 79 ++++++++++++++++++++ Matter_Engine/Query/AvailableConstituents.cs | 79 ++++++++++++++++++++ Matter_Engine/Query/AvailableGrades.cs | 79 ++++++++++++++++++++ Matter_Engine/Query/AvailableTypes.cs | 79 ++++++++++++++++++++ 4 files changed, 316 insertions(+) create mode 100644 Matter_Engine/Query/AvailableCategories.cs create mode 100644 Matter_Engine/Query/AvailableConstituents.cs create mode 100644 Matter_Engine/Query/AvailableGrades.cs create mode 100644 Matter_Engine/Query/AvailableTypes.cs diff --git a/Matter_Engine/Query/AvailableCategories.cs b/Matter_Engine/Query/AvailableCategories.cs new file mode 100644 index 000000000..c1fef7a9e --- /dev/null +++ b/Matter_Engine/Query/AvailableCategories.cs @@ -0,0 +1,79 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.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("Gets all distinct non-empty categories from the material classifications in a collection of materials, optionally filtered by type, grade, and constituent.")] + [Input("materials", "The collection of materials to query.")] + [Input("type", "The type to filter by. If omitted or empty, all types will be queried.")] + [Input("grade", "The grade to filter by. If omitted or empty, all grades will be queried.")] + [Input("constituent", "The constituent to filter by. If omitted or empty, all constituents will be queried.")] + [Output("categories", "The distinct material classification categories.")] + public static List AvailableCategories(this IEnumerable materials, string type = "", string grade = "", string constituent = "") + { + List filteredMaterials = materials.FilterByClassification("", type, grade, constituent); + if (filteredMaterials == null) + return null; + + return filteredMaterials.Select(material => material.MaterialClassification()?.Category) + .Where(category => !string.IsNullOrWhiteSpace(category)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + /******************************************/ + + [Description("Gets all distinct non-empty categories from the material classifications in a collection of material properties, optionally filtered by type, grade, and constituent.")] + [Input("materialProperties", "The collection of material properties to query.")] + [Input("type", "The type to filter by. If omitted or empty, all types will be queried.")] + [Input("grade", "The grade to filter by. If omitted or empty, all grades will be queried.")] + [Input("constituent", "The constituent to filter by. If omitted or empty, all constituents will be queried.")] + [Output("categories", "The distinct material classification categories.")] + public static List AvailableCategories(this IEnumerable materialProperties, string type = "", string grade = "", string constituent = "") + { + List filteredProperties = materialProperties.FilterByClassification("", type, grade, constituent); + if (filteredProperties == null) + return null; + + return filteredProperties.Select(materialProperty => materialProperty.MaterialClassification()?.Category) + .Where(category => !string.IsNullOrWhiteSpace(category)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + /******************************************/ + } +} \ No newline at end of file diff --git a/Matter_Engine/Query/AvailableConstituents.cs b/Matter_Engine/Query/AvailableConstituents.cs new file mode 100644 index 000000000..9d2fa78b7 --- /dev/null +++ b/Matter_Engine/Query/AvailableConstituents.cs @@ -0,0 +1,79 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.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("Gets all distinct non-empty constituents from the material classifications in a collection of materials, optionally filtered by category, type, and grade.")] + [Input("materials", "The collection of materials to query.")] + [Input("category", "The category to filter by. If omitted or empty, all categories will be queried.")] + [Input("type", "The type to filter by. If omitted or empty, all types will be queried.")] + [Input("grade", "The grade to filter by. If omitted or empty, all grades will be queried.")] + [Output("constituents", "The distinct material classification constituents.")] + public static List AvailableConstituents(this IEnumerable materials, string category = "", string type = "", string grade = "") + { + List filteredMaterials = materials.FilterByClassification(category, type, grade, ""); + if (filteredMaterials == null) + return null; + + return filteredMaterials.Select(material => material.MaterialClassification()?.Constituent) + .Where(constituent => !string.IsNullOrWhiteSpace(constituent)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + /******************************************/ + + [Description("Gets all distinct non-empty constituents from the material classifications in a collection of material properties, optionally filtered by category, type, and grade.")] + [Input("materialProperties", "The collection of material properties to query.")] + [Input("category", "The category to filter by. If omitted or empty, all categories will be queried.")] + [Input("type", "The type to filter by. If omitted or empty, all types will be queried.")] + [Input("grade", "The grade to filter by. If omitted or empty, all grades will be queried.")] + [Output("constituents", "The distinct material classification constituents.")] + public static List AvailableConstituents(this IEnumerable materialProperties, string category = "", string type = "", string grade = "") + { + List filteredProperties = materialProperties.FilterByClassification(category, type, grade, ""); + if (filteredProperties == null) + return null; + + return filteredProperties.Select(materialProperty => materialProperty.MaterialClassification()?.Constituent) + .Where(constituent => !string.IsNullOrWhiteSpace(constituent)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + /******************************************/ + } +} \ No newline at end of file diff --git a/Matter_Engine/Query/AvailableGrades.cs b/Matter_Engine/Query/AvailableGrades.cs new file mode 100644 index 000000000..9427f542e --- /dev/null +++ b/Matter_Engine/Query/AvailableGrades.cs @@ -0,0 +1,79 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.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("Gets all distinct non-empty grades from the material classifications in a collection of materials, optionally filtered by category, type, and constituent.")] + [Input("materials", "The collection of materials to query.")] + [Input("category", "The category to filter by. If omitted or empty, all categories will be queried.")] + [Input("type", "The type to filter by. If omitted or empty, all types will be queried.")] + [Input("constituent", "The constituent to filter by. If omitted or empty, all constituents will be queried.")] + [Output("grades", "The distinct material classification grades.")] + public static List AvailableGrades(this IEnumerable materials, string category = "", string type = "", string constituent = "") + { + List filteredMaterials = materials.FilterByClassification(category, type, "", constituent); + if (filteredMaterials == null) + return null; + + return filteredMaterials.Select(material => material.MaterialClassification()?.Grade) + .Where(grade => !string.IsNullOrWhiteSpace(grade)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + /******************************************/ + + [Description("Gets all distinct non-empty grades from the material classifications in a collection of material properties, optionally filtered by category, type, and constituent.")] + [Input("materialProperties", "The collection of material properties to query.")] + [Input("category", "The category to filter by. If omitted or empty, all categories will be queried.")] + [Input("type", "The type to filter by. If omitted or empty, all types will be queried.")] + [Input("constituent", "The constituent to filter by. If omitted or empty, all constituents will be queried.")] + [Output("grades", "The distinct material classification grades.")] + public static List AvailableGrades(this IEnumerable materialProperties, string category = "", string type = "", string constituent = "") + { + List filteredProperties = materialProperties.FilterByClassification(category, type, "", constituent); + if (filteredProperties == null) + return null; + + return filteredProperties.Select(materialProperty => materialProperty.MaterialClassification()?.Grade) + .Where(grade => !string.IsNullOrWhiteSpace(grade)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + /******************************************/ + } +} \ No newline at end of file diff --git a/Matter_Engine/Query/AvailableTypes.cs b/Matter_Engine/Query/AvailableTypes.cs new file mode 100644 index 000000000..19aa5f14e --- /dev/null +++ b/Matter_Engine/Query/AvailableTypes.cs @@ -0,0 +1,79 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.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("Gets all distinct non-empty types from the material classifications in a collection of materials, optionally filtered by category, grade, and constituent.")] + [Input("materials", "The collection of materials to query.")] + [Input("category", "The category to filter by. If omitted or empty, all categories will be queried.")] + [Input("grade", "The grade to filter by. If omitted or empty, all grades will be queried.")] + [Input("constituent", "The constituent to filter by. If omitted or empty, all constituents will be queried.")] + [Output("types", "The distinct material classification types.")] + public static List AvailableTypes(this IEnumerable materials, string category = "", string grade = "", string constituent = "") + { + List filteredMaterials = materials.FilterByClassification(category, "", grade, constituent); + if (filteredMaterials == null) + return null; + + return filteredMaterials.Select(material => material.MaterialClassification()?.Type) + .Where(type => !string.IsNullOrWhiteSpace(type)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + /******************************************/ + + [Description("Gets all distinct non-empty types from the material classifications in a collection of material properties, optionally filtered by category, grade, and constituent.")] + [Input("materialProperties", "The collection of material properties to query.")] + [Input("category", "The category to filter by. If omitted or empty, all categories will be queried.")] + [Input("grade", "The grade to filter by. If omitted or empty, all grades will be queried.")] + [Input("constituent", "The constituent to filter by. If omitted or empty, all constituents will be queried.")] + [Output("types", "The distinct material classification types.")] + public static List AvailableTypes(this IEnumerable materialProperties, string category = "", string grade = "", string constituent = "") + { + List filteredProperties = materialProperties.FilterByClassification(category, "", grade, constituent); + if (filteredProperties == null) + return null; + + return filteredProperties.Select(materialProperty => materialProperty.MaterialClassification()?.Type) + .Where(type => !string.IsNullOrWhiteSpace(type)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + /******************************************/ + } +} \ No newline at end of file From bce388d772c56ab4db81f631f1a91aa9222f0763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20N=C3=A4slund?= Date: Wed, 2 Sep 2026 14:43:04 +0200 Subject: [PATCH 4/5] Fix issue with whitespaces Completely ignores all whitespaces for filtering --- Matter_Engine/Query/FilterByClassification.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Matter_Engine/Query/FilterByClassification.cs b/Matter_Engine/Query/FilterByClassification.cs index a6bb63a93..709b7a852 100644 --- a/Matter_Engine/Query/FilterByClassification.cs +++ b/Matter_Engine/Query/FilterByClassification.cs @@ -132,29 +132,30 @@ private static bool ClassificationMatches(this MaterialClassification classifica //Ignore case when comparing classification properties //This is to avoid issues with different casing in the classification properties, e.g. "Concrete" vs "concrete" + //The comparisons below also ignore any whitespaces to make sure for example `CEM 1` matches to `CEM1` as well as `CEM 1 ` StringComparison comparison = StringComparison.OrdinalIgnoreCase; if(!string.IsNullOrWhiteSpace(other.Category)) { - if(!string.Equals(classification.Category, other.Category, comparison)) + if(!string.Equals(classification.Category.Replace(" ", string.Empty), other.Category.Replace(" ", string.Empty), comparison)) return false; } if(!string.IsNullOrWhiteSpace(other.Type)) { - if(!string.Equals(classification.Type, other.Type, comparison)) + if(!string.Equals(classification.Type.Replace(" ", string.Empty), other.Type.Replace(" ", string.Empty), comparison)) return false; } if(!string.IsNullOrWhiteSpace(other.Grade)) { - if(!string.Equals(classification.Grade, other.Grade, comparison)) + if(!string.Equals(classification.Grade.Replace(" ", string.Empty), other.Grade.Replace(" ", string.Empty), comparison)) return false; } if(!string.IsNullOrWhiteSpace(other.Constituent)) { - if(!string.Equals(classification.Constituent, other.Constituent, comparison)) + if(!string.Equals(classification.Constituent.Replace(" ", string.Empty), other.Constituent.Replace(" ", string.Empty), comparison)) return false; } From 164cc8ab8eead1ce101997bf07844b458ef3bc4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20N=C3=A4slund?= Date: Wed, 2 Sep 2026 14:45:23 +0200 Subject: [PATCH 5/5] Ensure the returned available values are all sorted --- Matter_Engine/Query/AvailableCategories.cs | 2 ++ Matter_Engine/Query/AvailableConstituents.cs | 2 ++ Matter_Engine/Query/AvailableGrades.cs | 2 ++ Matter_Engine/Query/AvailableTypes.cs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/Matter_Engine/Query/AvailableCategories.cs b/Matter_Engine/Query/AvailableCategories.cs index c1fef7a9e..bb80a8be5 100644 --- a/Matter_Engine/Query/AvailableCategories.cs +++ b/Matter_Engine/Query/AvailableCategories.cs @@ -51,6 +51,7 @@ public static List AvailableCategories(this IEnumerable materi return filteredMaterials.Select(material => material.MaterialClassification()?.Category) .Where(category => !string.IsNullOrWhiteSpace(category)) .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(category => category) .ToList(); } @@ -71,6 +72,7 @@ public static List AvailableCategories(this IEnumerable materialProperty.MaterialClassification()?.Category) .Where(category => !string.IsNullOrWhiteSpace(category)) .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(category => category) .ToList(); } diff --git a/Matter_Engine/Query/AvailableConstituents.cs b/Matter_Engine/Query/AvailableConstituents.cs index 9d2fa78b7..2a5c2d7f1 100644 --- a/Matter_Engine/Query/AvailableConstituents.cs +++ b/Matter_Engine/Query/AvailableConstituents.cs @@ -51,6 +51,7 @@ public static List AvailableConstituents(this IEnumerable mate return filteredMaterials.Select(material => material.MaterialClassification()?.Constituent) .Where(constituent => !string.IsNullOrWhiteSpace(constituent)) .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(constituent => constituent) .ToList(); } @@ -71,6 +72,7 @@ public static List AvailableConstituents(this IEnumerable materialProperty.MaterialClassification()?.Constituent) .Where(constituent => !string.IsNullOrWhiteSpace(constituent)) .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(constituent => constituent) .ToList(); } diff --git a/Matter_Engine/Query/AvailableGrades.cs b/Matter_Engine/Query/AvailableGrades.cs index 9427f542e..2f6d788a5 100644 --- a/Matter_Engine/Query/AvailableGrades.cs +++ b/Matter_Engine/Query/AvailableGrades.cs @@ -51,6 +51,7 @@ public static List AvailableGrades(this IEnumerable materials, return filteredMaterials.Select(material => material.MaterialClassification()?.Grade) .Where(grade => !string.IsNullOrWhiteSpace(grade)) .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(grade => grade) .ToList(); } @@ -71,6 +72,7 @@ public static List AvailableGrades(this IEnumerable return filteredProperties.Select(materialProperty => materialProperty.MaterialClassification()?.Grade) .Where(grade => !string.IsNullOrWhiteSpace(grade)) .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(grade => grade) .ToList(); } diff --git a/Matter_Engine/Query/AvailableTypes.cs b/Matter_Engine/Query/AvailableTypes.cs index 19aa5f14e..d8fce939e 100644 --- a/Matter_Engine/Query/AvailableTypes.cs +++ b/Matter_Engine/Query/AvailableTypes.cs @@ -51,6 +51,7 @@ public static List AvailableTypes(this IEnumerable materials, return filteredMaterials.Select(material => material.MaterialClassification()?.Type) .Where(type => !string.IsNullOrWhiteSpace(type)) .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(type => type) .ToList(); } @@ -71,6 +72,7 @@ public static List AvailableTypes(this IEnumerable return filteredProperties.Select(materialProperty => materialProperty.MaterialClassification()?.Type) .Where(type => !string.IsNullOrWhiteSpace(type)) .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(type => type) .ToList(); }