diff --git a/src/Aardvark.Data.Ifc/IFCData.cs b/src/Aardvark.Data.Ifc/IFCData.cs index c909060..b9e6574 100644 --- a/src/Aardvark.Data.Ifc/IFCData.cs +++ b/src/Aardvark.Data.Ifc/IFCData.cs @@ -31,7 +31,7 @@ public IFCData(IfcStore model, IInverseCache inversCache, IEntityCache entityCac Content = content; UnitScale = scale; Hierarchy = hierarchy; - Project = IFCHelper.GetProject(m_model); + Project = GeneralExt.GetProject(m_model); ProjectName = Project.LongName; Materials = materials; } diff --git a/src/Aardvark.Data.Ifc/IFCHelper.cs b/src/Aardvark.Data.Ifc/IFCHelper.cs deleted file mode 100644 index f74bd88..0000000 --- a/src/Aardvark.Data.Ifc/IFCHelper.cs +++ /dev/null @@ -1,2348 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Aardvark.Base; -using Aardvark.Data.Photometry; -using Aardvark.Geometry; - -using Xbim.Common; -using Xbim.Common.Enumerations; -using Xbim.Common.ExpressValidation; -using Xbim.Common.Step21; -using Xbim.Ifc; -using Xbim.Ifc.Extensions; -using Xbim.Ifc4.Interfaces; -using Xbim.Ifc4.DateTimeResource; -using Xbim.Ifc4.ElectricalDomain; -using Xbim.Ifc4.GeometricConstraintResource; -using Xbim.Ifc4.GeometricModelResource; -using Xbim.Ifc4.GeometryResource; -using Xbim.Ifc4.Kernel; -using Xbim.Ifc4.MaterialResource; -using Xbim.Ifc4.MeasureResource; -using Xbim.Ifc4.PresentationAppearanceResource; -using Xbim.Ifc4.PresentationDefinitionResource; -using Xbim.Ifc4.PresentationOrganizationResource; -using Xbim.Ifc4.ProductExtension; -using Xbim.Ifc4.ProfileResource; -using Xbim.Ifc4.PropertyResource; -using Xbim.Ifc4.QuantityResource; -using Xbim.Ifc4.RepresentationResource; -using Xbim.Ifc4.SharedBldgElements; - -namespace Aardvark.Data.Ifc -{ - public static class IFCHelper - { - #region Properties - private static bool EqualOrContainsName(this IIfcPropertySingleValue value, string queryString) - => string.Equals(value.Name, queryString, StringComparison.OrdinalIgnoreCase) || value.Name.ToString().ToLower().Contains(queryString.ToLower()); - - public static IEnumerable GetProperties(this IIfcObject o) - { - // TODO: misses to query for Ifc2x3 IsDefinedByProperties - return o.IsDefinedBy.Where(r => r.RelatingPropertyDefinition is IIfcPropertySet) - .SelectMany(r => ((IIfcPropertySet)r.RelatingPropertyDefinition).HasProperties) - .OfType(); - } - - public static IEnumerable GetProperties(this IIfcObject o, string propertySetName) - { - return o.IsDefinedBy.Where(r => r.RelatingPropertyDefinition is IIfcPropertySet) - .Where(r => ((IIfcPropertySet)r.RelatingPropertyDefinition).Name == propertySetName) - .SelectMany(r => ((IIfcPropertySet)r.RelatingPropertyDefinition).HasProperties) - .OfType(); - } - - public static IEnumerable GetPropertiesFromType(this IIfcObject o) - { - return - o.IsTypedBy - .SelectMany(o => o.RelatingType.HasPropertySets) - .OfType() - .SelectMany(pset => pset.HasProperties) - .OfType(); - } - - public static IEnumerable GetAllProperties(this IIfcObject o) - { - var p1 = o.GetProperties(); - var p2 = o.GetPropertiesFromType(); - - return p1.Concat(p2); - } - - public static IIfcValue GetPropertyFromType(this IIfcObject o, string name) - => o.GetPropertiesFromType().FirstOrDefault(p => p.EqualOrContainsName(name))?.NominalValue; - - public static IIfcValue GetPropertyLocal(this IIfcObject o, string name) - => o.GetProperties().FirstOrDefault(p => p.EqualOrContainsName(name))?.NominalValue; - - public static IIfcValue GetProperty(this IIfcObject o, string name) - { - // first check for local properties - var res = o.GetPropertyLocal(name); - if (res != null) return res; - - // if not available check for properties from type-object - return o.GetPropertyFromType(name); - } - - #region Property-Value - - public static bool TryGetSimpleValue2(this IExpressValueType ifcValue, out T result) where T : struct - { - static DateTime ReadDateTime(string str) - { - try - { - var parts = str.Split([':', '-', 'T', 'Z'], StringSplitOptions.RemoveEmptyEntries); - if (parts.Length == 6) //it is a date time - { - var year = Convert.ToInt32(parts[0]); - var month = Convert.ToInt32(parts[1]); - var day = Convert.ToInt32(parts[2]); - var hours = Convert.ToInt32(parts[3]); - var minutes = Convert.ToInt32(parts[4]); - var seconds = Convert.ToInt32(parts[5]); - return new DateTime(year, month, day, hours, minutes, seconds, str.Last() == 'Z' ? DateTimeKind.Utc : DateTimeKind.Unspecified); - } - if (parts.Length == 3) //it is a date - { - var year = Convert.ToInt32(parts[0]); - var month = Convert.ToInt32(parts[1]); - var day = Convert.ToInt32(parts[2]); - return new DateTime(year, month, day); - } - } - catch (Exception) - { - Report.Warn("Date Time Conversion: An illegal date time string has been found [{stringValue}]", str); - } - return default; - } - - var value = new T(); - - try - { - if (ifcValue is IfcMonetaryMeasure) - { - value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); - } - else if (ifcValue is IfcTimeStamp timeStamp) - { - value = (T)Convert.ChangeType(timeStamp.ToDateTime(), typeof(T)); - } - else if (value is DateTime) //sometimes these are written as strings in the ifc file - { - value = (T)Convert.ChangeType(ReadDateTime(ifcValue.Value.ToString()), typeof(T)); - } - else if (ifcValue.UnderlyingSystemType == typeof(int) || ifcValue.UnderlyingSystemType == typeof(long) || ifcValue.UnderlyingSystemType == typeof(short) || ifcValue.UnderlyingSystemType == typeof(byte)) - { - value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); - } - else if (ifcValue.UnderlyingSystemType == typeof(double) || ifcValue.UnderlyingSystemType == typeof(float)) - { - value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); - } - else if (ifcValue.UnderlyingSystemType == typeof(string)) - { - value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); - } - else if (ifcValue.UnderlyingSystemType == typeof(bool) || ifcValue.UnderlyingSystemType == typeof(bool?)) - { - value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); - } - } - catch (Exception) - { - result = default; - return false; - } - - result = value; - return true; - } - - public static bool TryGetSimpleValue(this IExpressValueType ifcValue, out T result) where T : struct - { - var targetType = typeof(T); - - //handle null value if is it acceptable - if (ifcValue == null || ifcValue.Value == null) - { - result = default; - //return true if null is acceptable value - return targetType.IsClass || - (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>)); - } - - if (targetType == typeof(string)) - { - result = (T)(object)ifcValue.ToString(); - return true; - } - - if (targetType == typeof(float) || targetType == typeof(float?) || targetType == typeof(double) || targetType == typeof(double?)) - { - try - { - result = (T)(object)Convert.ToDouble(ifcValue.Value, CultureInfo.InvariantCulture); - return true; - } - catch (Exception ex) - { - if (ex is NullReferenceException || ex is ArgumentNullException || ex is FormatException || ex is OverflowException) - { - if (typeof(T) == typeof(float?) || - typeof(T) == typeof(double?)) - { - result = default; - return true; - } - } - } - - result = default; - return false; - } - - if (targetType == typeof(bool) || targetType == typeof(bool?)) - { - try - { - result = (T)(object)Convert.ToBoolean(ifcValue.Value); - return true; - } - catch (Exception ex) - { - if (ex is NullReferenceException || ex is ArgumentNullException) - { - if (typeof(T) == typeof(bool?)) - { - result = default; - return true; - } - } - - if (ex is FormatException) Report.Warn("Boolean Conversion: String does not consist of an " + "optional sign followed by a series of digits."); - if (ex is OverflowException) Report.Warn("Boolean Conversion: Overflow in string to int conversion."); - } - - result = default; - return false; - } - - if (targetType == typeof(int) || targetType == typeof(int?) || targetType == typeof(long) || targetType == typeof(long?)) - { - try - { - result = (T)(object)Convert.ToInt32(ifcValue.Value); - return true; - } - catch (Exception ex) { - if (ex is NullReferenceException || ex is ArgumentNullException) { - - if (targetType == typeof(int?) || targetType == typeof(long?)) - { - result = default; - return true; - } - } - } - - result = default; - return false; - } - - if (targetType == typeof(DateTime) || targetType == typeof(DateTime?)) - { - try - { - result = (T)(object)Convert.ToDateTime(ifcValue.Value); - return true; - } - catch (Exception) - { - result = default; - return targetType == typeof(DateTime?); - } - } - - result = default; - return false; - } - - public static bool TryGetSimpleValue(this IIfcPropertySingleValue property, out T result) where T : struct - { - var isValid = property.NominalValue.TryGetSimpleValue(out T res); - result = res; - - return isValid; - } - - public static IfcPropertySingleValue CreatePropertySingleValue(this IModel model, string name, V value) where V : IfcValue - { - return model.New(p => { - p.Name = name; - p.NominalValue = value; - }); - } - - public static IfcPropertyEnumeratedValue CreatePropertyEnumeratedValue(this IModel model, string name, V value) where V : IfcValue - { - return model.New(p => { - p.Name = name; - p.EnumerationValues.Add(value); - }); - } - - #endregion - - #region Quantities - - public static bool TryGetSimpleValue(this IIfcPhysicalQuantity ifcQuantity, out T result) where T : struct - { - if (ifcQuantity is IIfcQuantityLength ifcQuantityLength) - return TryGetSimpleValue(ifcQuantityLength.LengthValue, out result); - - if (ifcQuantity is IIfcQuantityArea ifcQuantityArea) - return TryGetSimpleValue(ifcQuantityArea.AreaValue, out result); - - if (ifcQuantity is IIfcQuantityVolume ifcQuantityVolume) - return TryGetSimpleValue(ifcQuantityVolume.VolumeValue, out result); - - if (ifcQuantity is IIfcQuantityCount ifcQuantityCount) - return TryGetSimpleValue(ifcQuantityCount.CountValue, out result); - - if (ifcQuantity is IIfcQuantityWeight ifcQuantityWeight) - return TryGetSimpleValue(ifcQuantityWeight.WeightValue, out result); - - if (ifcQuantity is IIfcQuantityTime ifcQuantityTime) - return TryGetSimpleValue(ifcQuantityTime.TimeValue, out result); - - if (ifcQuantity is IIfcPhysicalComplexQuantity) - { - Report.Warn("Complex Types are not supported!"); - } - result = default; - return false; - } - - public static IfcQuantityWeight CreateQuantityWeight(this IModel model, string name, double value) - { - return model.New(w => - { - w.Name = name; - w.WeightValue = new IfcMassMeasure(value); - }); - } - - public static IfcQuantityLength CreateQuantityLength(this IModel model, string name, double value) - { - return model.New(w => - { - w.Name = name; - w.LengthValue = new IfcLengthMeasure(value); - }); - } - - public static IfcQuantityArea CreateQuantityArea(this IModel model, string name, double value) - { - return model.New(w => - { - w.Name = name; - w.AreaValue = new IfcAreaMeasure(value); - }); - } - - public static IfcQuantityVolume CreateQuantityVolume(this IModel model, string name, double value) - { - return model.New(w => - { - w.Name = name; - w.VolumeValue = new IfcVolumeMeasure(value); - }); - } - - public static IfcQuantityCount CreateQuantityCount(this IModel model, string name, double value) - { - return model.New(w => - { - w.Name = name; - w.CountValue = new IfcCountMeasure(value); - }); - } - - public static IfcQuantityTime CreateQuantityTime(this IModel model, string name, double value) - { - return model.New(w => - { - w.Name = name; - w.TimeValue = new IfcTimeMeasure(value); - }); - } - - #endregion - - public static Dictionary DistinctDictionaryFromPropertiesString(IEnumerable input) - => input.ToDictionaryDistinct(x => x.Name.ToString(), x => x.NominalValue.ToString(), (x, w) => true); - - public static Dictionary DistinctDictionaryFromPropertiesValues(IEnumerable input) - => input.ToDictionaryDistinct(x => x.Name.ToString(), x => x, (x, w) => true); - - public static T TryGetProperty(this Dictionary dict, string input) where T : struct - { - if (dict.TryGetValue(input, out var value)) - { - if (value.TryGetSimpleValue(out T result)) return result; - } - - return default; - } - - public static Dictionary DistinctDictionaryFromPropertiesOfType(IEnumerable input) where T : struct - { - return input - .Select(x => { - var valid = x.TryGetSimpleValue(out T result); - return Tuple.Create(x.Name.ToString(), valid, result); - }) - .Where(t => t.Item2) - .ToDictionaryDistinct(t => t.Item1, t => t.Item3, (x,w) => true); - } - - public static Dictionary GetPropertiesDict(this IIfcObject o, string propertySetName = null) - => DistinctDictionaryFromPropertiesString((propertySetName == null) ? o.GetProperties() : o.GetProperties(propertySetName)); - - public static Dictionary GetSharedPropertiesDict(this IIfcObject o) - => DistinctDictionaryFromPropertiesString(GetPropertiesFromType(o)); - - public static Dictionary GetAllPropertiesDict(this IIfcObject o) - => DistinctDictionaryFromPropertiesString(o.GetAllProperties()); - - [Obsolete] - public static Dictionary GetHilitePropertiesDict(this IIfcObject o) - => o.GetPropertiesDict("Hilite"); - - - public static IfcPropertySet CreatePropertySet(this IModel model, string setName, Dictionary parameters) - { - // supports the following types: https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcmeasureresource/lexical/ifcvalue.htm - var set = model.New(pset => { - pset.Name = setName; - pset.HasProperties.AddRange( - parameters.Select(x => model.New(p => - { - p.Name = x.Key; - p.NominalValue = x.Value switch - { - double d => new IfcReal(d), //double lum => new IfcLuminousFluxMeasure(lum), - float r => new IfcReal(r), - int i => new IfcInteger(i), - bool b => new IfcBoolean(b), - _ => new IfcText(x.Value.ToString()), - }; - })) - ); - }); - - return set; - } - - - public static IfcProduct AttachPropertySet(this IfcProduct o, IfcPropertySet set) - { - o.AddPropertySet(set); - return o; - } - - public static IfcPropertySet CreateAttachPropertySet(this IfcObject o, string setName, Dictionary parameters) - { - var set = o.Model.CreatePropertySet(setName, parameters); - - o.AddPropertySet(set); - - return set; - } - - public static void PurgePropertySingleValue(this IfcObject o, string pSetName, string propertyName) - { - // removes property and in case of single-propset resulting empty set globally - // CAUTION: affects all objects re-using it - - IIfcPropertySet propertySet = o.GetPropertySet(pSetName); - if (propertySet != null) - { - IIfcPropertySingleValue ifcPropertySingleValue = propertySet.HasProperties.FirstOrDefault((IIfcPropertySingleValue p) => p.Name == (IfcIdentifier)propertyName); - if (ifcPropertySingleValue != null) - { - propertySet.HasProperties.Remove(ifcPropertySingleValue); - - // delete property from model - o.Model.Delete(ifcPropertySingleValue); - } - - if (propertySet.HasProperties.IsEmpty()) - { - // remove reference of empty set and object - var rel = o.IsDefinedBy.FirstOrDefault((IfcRelDefinesByProperties r) => r.RelatingPropertyDefinition.PropertySetDefinitions.FirstOrDefault(a => a.Name == pSetName) != null); - if (rel != null) o.Model.Delete(rel); - - // remove empty set from model - o.Model.Delete(propertySet); - } - } - } - - public static void PurgePropertySet(this IfcObject o, string pSetName) - { - // removes set and all its properties globally - // CAUTION: affects all objects re-using it - - IIfcPropertySet propertySet = o.GetPropertySet(pSetName); - if (propertySet != null) - { - // stash for later removal - var temp = propertySet.HasProperties.ToArray(); - - // release properties from set - propertySet.HasProperties.Clear(); - - // detlete properties from model - temp.ForEach(o.Model.Delete); - - // remove reference of empty set and object - var rel = o.IsDefinedBy.FirstOrDefault((IfcRelDefinesByProperties r) => r.RelatingPropertyDefinition.PropertySetDefinitions.FirstOrDefault(a => a.Name == pSetName) != null); - if (rel != null) o.Model.Delete(rel); - - // remove empty set from model - o.Model.Delete(propertySet); - } - } - - #endregion - - #region Convenient Functions - public static T New(this IModel model, Action func) where T : IInstantiableEntity - { - // Convenient function to directly create entities from model - return model.Instances.New(func); - } - public static IIfcProject GetProject(this IfcStore model) - => model.Instances.FirstOrDefault(); - - public static IIfcObjectDefinition GetParent(this IIfcObjectDefinition o) - => o.Decomposes.Select(r => r.RelatingObject).FirstOrDefault(); - - public static IEnumerable GetSiblings(this IIfcObjectDefinition o) - => o.Decomposes.SelectMany(r => r.RelatedObjects).Where(x => !o.Equals(x)); - - public static IEnumerable GetChildren(this IIfcObjectDefinition o) - { - var children = o.IsDecomposedBy.SelectMany(r => r.RelatedObjects); - - if ((o as IIfcSpatialStructureElement) != null) - { - children = children.Concat(((IIfcSpatialStructureElement)o).ContainsElements.SelectMany(rel => rel.RelatedElements).Cast()); - } - - return children; - } - - #endregion - - #region Hierarchy - - public static IFCNode CreateHierarchy(IfcStore model) - => CreateHierarchy(model.GetProject()); - - private static IFCNode CreateHierarchy(IIfcObjectDefinition obj) - => new IFCNode(obj, obj.GetChildren().Select(x => (IIFCNode)CreateHierarchy(x)).ToList()); - - public static void PrintHierarchy(string file) - { - using var model = IfcStore.Open(file); - var project = model.Instances.FirstOrDefault(); - Report.Line("HIRARCHY of file: {0}\n", file); - PrintHierarchy(project, 0); - } - - public static void PrintHierarchy(IIfcObjectDefinition o, int level) - { - Report.Line(string.Format("{0}{1} [{2}]", GetIndent(level), o.Name, o.GetType().Name)); - - var parent = o.GetParent(); - if (parent != null) Report.Line("parent: " + parent.ToString()); - - var children = GetChildren(o); - Report.Line("children count: {0}", children.Count()); - children.ForEach(element => Report.Line(string.Format("{0} ->{1} [{2}]", GetIndent(level), element.Name, element.GetType().Name))); - - var siblings = o.GetSiblings(); - Report.Line("sibling count: {0}", siblings.Count()); - siblings.ForEach(s => Report.Line("siblings: "+s.ToString())); - - Report.Line(); - - ////only spatial elements can contain building elements - //var spatialElement = o as IIfcSpatialStructureElement; - //if (spatialElement != null) - //{ - // //using IfcRelContainedInSpatialElement to get contained elements - // var containedElements = spatialElement.ContainsElements.SelectMany(rel => rel.RelatedElements); - // foreach (var element in containedElements) - // Console.WriteLine(string.Format("{0} ->{1} [{2}]", GetIndent(level), element.Name, element.GetType().Name)); - //} - - //using IfcRelAggregares to get spatial decomposition of spatial structure elements - foreach (var item in o.IsDecomposedBy.SelectMany(r => r.RelatedObjects)) - PrintHierarchy(item, level + 1); - } - - private static string GetIndent(int level) - { - var indent = ""; - for (int i = 0; i < level; i++) - indent += " "; - return indent; - } - - #endregion - - #region Group - - public static IfcGroup CreateGroup(this IModel model, string groupName) - => model.New(g => g.Name = groupName); - - public static IfcGroup CreateGroup(this IModel model, string groupName, IEnumerable relatedObjects, IfcObjectTypeEnum groupType = IfcObjectTypeEnum.PRODUCT) - { - var group = model.CreateGroup(groupName); - - // Link related objects to group via IfcRelAssignsToGroup - model.New(rel => { - rel.RelatingGroup = group; - rel.RelatedObjects.AddRange(relatedObjects); - rel.RelatedObjectsType = groupType; - }); - - return group; - } - - public static IfcGroup CreateGroup(this IModel model, string groupName, IEnumerable relatedObjects) - => model.CreateGroup(groupName, relatedObjects, IfcObjectTypeEnum.GROUP); - - #endregion - - #region Layer - - public static IfcPresentationLayerAssignment CreateLayer(this IModel model, string layerName, IEnumerable items = null) - { - // IfcPresentationLayerAssignment only allows: IFC4.IFCSHAPEREPRESENTATION", "IFC4.IFCGEOMETRICREPRESENTATIONITEM", "IFC4.IFCMAPPEDITEM - return model.New(layer => { - layer.Name = layerName; - if (!items.IsEmptyOrNull()) layer.AssignedItems.AddRange(items); - }); - } - - public static IfcPresentationLayerAssignment CreateLayer(this IModel model, string layerName, IfcLayeredItem items) - => model.CreateLayer(layerName, [items]); - - public static IfcPresentationLayerWithStyle CreateLayerWithStyle(this IModel model, string layerName, IEnumerable styles, bool layerVisibility = true, IEnumerable items = null) - { - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationorganizationresource/lexical/ifcpresentationlayerwithstyle.htm - // IfcPresentationLayerWithStyle only allows: "IFC4.IFCGEOMETRICREPRESENTATIONITEM", "IFC4.IFCMAPPEDITEM" - - return model.New(layer => { - layer.Name = layerName; - - // Visibility Control - layer.LayerOn = layerVisibility; // visibility control allows to define a layer to be either 'on' or 'off', and/or 'frozen' or 'not frozen' - //layer.LayerFrozen = true; - - // Access control - //layer.LayerBlocked = true; // access control allows to block graphical entities from manipulations - - // NOTE: ORDER seems to be important! BIM-Viewer tend to use only color information of first item! - layer.LayerStyles.AddRange(styles); - if (items != null && !items.IsEmpty()) layer.AssignedItems.AddRange(items); - }); - } - - public static IfcPresentationLayerWithStyle CreateLayerWithStyle(this IModel model, string layerName, IEnumerable styles, IfcGeometricRepresentationItem item, bool layerVisibility = true) - => model.CreateLayerWithStyle(layerName, styles, layerVisibility, [item]); - - public static IfcLayeredItem AssignLayer(this IfcLayeredItem item, IfcPresentationLayerAssignment layer) - { - if (layer is IfcPresentationLayerWithStyle && item is IfcShapeRepresentation) - { - // IfcPresentationLayerWithStyle only allows "IFC4.IFCGEOMETRICREPRESENTATIONITEM", "IFC4.IFCMAPPEDITEM" - throw new ArgumentException("IfcShapeRepresentation cannot be assigened to IfcPresentationLayerWithStyle"); - } - // IfcPresentationLayerAssignment only allows: IFC4.IFCSHAPEREPRESENTATION", "IFC4.IFCGEOMETRICREPRESENTATIONITEM", "IFC4.IFCMAPPEDITEM - if (!layer.AssignedItems.Contains(item)) layer.AssignedItems.Add(item); - return item; - } - - public static IfcGeometricRepresentationItem AssignLayer(this IfcGeometricRepresentationItem item, IfcPresentationLayerAssignment layer) - { - if (!layer.AssignedItems.Contains(item)) layer.AssignedItems.Add(item); - return item; - } - - public static IfcMappedItem AssignLayer(this IfcMappedItem item, IfcPresentationLayerAssignment layer) - { - if (!layer.AssignedItems.Contains(item)) layer.AssignedItems.Add(item); - return item; - } - - #endregion - - #region Placement - - public static IfcCartesianPoint CreatePoint(this IModel model, V2d point) - => model.New(c => c.Set(point)); - - public static IfcCartesianPoint CreatePoint(this IModel model, V3d point) - => model.New(c => c.Set(point)); - - public static IfcDirection CreateDirection(this IModel model, V2d direction) - => model.New(rd => rd.Set(direction)); // NOTE: Direction may be normalized! - - public static IfcDirection CreateDirection(this IModel model, V3d direction) - => model.New(rd => rd.Set(direction)); // NOTE: Direction may be normalized! - - public static IfcVector CreateVector(this IModel model, V3d vector) - { - return model.New(v => { - v.Magnitude = vector.Length; - v.Orientation = model.CreateDirection(vector.Normalized); - }); - } - - public static IfcVector CreateVector(this IModel model, V2d vector) - { - return model.New(v => { - v.Magnitude = vector.Length; - v.Orientation = model.CreateDirection(vector.Normalized); - }); - } - - public static IfcAxis2Placement3D CreateAxis2Placement3D(this IModel model, V3d location, V3d refDir, V3d axis) - { - return model.New(a => { - a.Location = model.CreatePoint(location); - a.RefDirection = model.CreateDirection(refDir); // default x-axis - a.Axis = model.CreateDirection(axis); // default z-axis - }); - } - - public static IIfcAxis2Placement3D CreateAxis2Placement3D(this IModel model, Trafo3d trafo) - => model.CreateAxis2Placement3D(trafo.Forward.C3.XYZ, trafo.Forward.C0.XYZ, trafo.Forward.C2.XYZ); - - public static IfcAxis2Placement3D CreateAxis2Placement3D(this IModel model, V3d location) - => model.CreateAxis2Placement3D(location, V3d.XAxis, V3d.ZAxis); - - public static IfcAxis2Placement2D CreateAxis2Placement2D(this IModel model, V2d location, V2d refDir) - { - return model.New(a => { - a.Location = model.CreatePoint(location); - a.RefDirection = model.CreateDirection(refDir); - }); - } - public static IfcAxis2Placement2D CreateAxis2Placement2D(this IModel model, V2d location) - => model.CreateAxis2Placement2D(location, V2d.XAxis); - - public static IfcLocalPlacement CreateLocalPlacement(this IModel model, V3d shift) - => model.New(p => p.RelativePlacement = model.CreateAxis2Placement3D(shift)); - - #endregion - - #region Grid - public static IfcGridAxis CreateGridAxis(this IModel model, string name, V2d start, V2d end) - { - return model.New(a => - { - a.AxisTag = name; - a.AxisCurve = model.CreatePolyLine(start, end); - a.SameSense = true; - }); - } - - public static IfcGrid CreateGrid(this IModel model, string name, string[] xAxis, string[] vAxes, double offset) - { - // Create axis - var xAxisEntities = xAxis.Select((a, i) => model.CreateGridAxis(a, new V2d(-offset / 2.0, offset * i), new V2d(offset * vAxes.Length, offset * i))); - var yAxisEntities = vAxes.Select((a, i) => model.CreateGridAxis(a, new V2d(offset * i, -offset / 2.0), new V2d(offset * i, offset * xAxis.Length))); - - // Create regular grid - var grid = model.New(g => - { - g.Name = name; - g.UAxes.AddRange(xAxisEntities); - g.VAxes.AddRange(yAxisEntities); - g.PredefinedType = IfcGridTypeEnum.RECTANGULAR; - g.ObjectPlacement = model.CreateLocalPlacement(V3d.Zero); - }); - - return grid; - } - - #endregion - - #region Styling - public static IfcColourRgb CreateColor(this IModel model, C3f colour) - => model.New(x => x.Set(colour)); - - public static IfcColourRgb CreateColor(this IModel model, C3d colour) - => model.New(x => x.Set(colour)); - - #region Text Styling - public static IfcTextStyleForDefinedFont CreateTextStyleForDefinedFont(this IModel model, C3f colour, C3f background, string name = null) - { - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextstylefordefinedfont.htm - - return model.New(f => { - f.Colour = model.CreateColor(colour); - // optional - f.BackgroundColour = model.CreateColor(background); - }); - } - - public enum TextDecoration { None, UnderLine, Overline, Linethrough } - public enum TextTransform { Capitalize, Uppercase, Lowercase, None } - public enum TextAlignment { Left, Right, Center, Justify } - - public static IfcTextStyleTextModel CreateTextStyleTextModel(this IModel model, double textIndent, TextAlignment textAlign, TextDecoration textDecoration, TextTransform textTransform, double letterSpacing, double wordSpacing, double lineHeight) - { - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextstyletextmodel.htm - - string decoration = textDecoration switch - { - TextDecoration.UnderLine => "underLine", - TextDecoration.Overline => "overline", - TextDecoration.Linethrough => "line-through", - _ => "none" - }; - - string transform = textTransform switch - { - TextTransform.Capitalize => "capitalize", - TextTransform.Uppercase => "uppercase", - TextTransform.Lowercase => "lowercase", - _ => "none" - }; - - string alignment = textAlign switch - { - TextAlignment.Left => "left", - TextAlignment.Right => "right", - TextAlignment.Center => "center", - _ => "justify" - }; - - return model.New(tm => - { - // optional - tm.TextIndent = new IfcLengthMeasure(textIndent); // The property specifies the indentation that appears before the first formatted line. - tm.TextAlign = new IfcTextAlignment(alignment); - tm.TextDecoration = new IfcTextDecoration(decoration); - tm.TextTransform = new IfcTextTransformation(transform); - tm.LetterSpacing = new IfcLengthMeasure(letterSpacing); - tm.WordSpacing = new IfcLengthMeasure(wordSpacing); - tm.LineHeight = new IfcLengthMeasure(lineHeight); - }); - } - - public enum FontStyle { Normal, Italic, Oblique } - public enum FontWeight { Normal, Bold } - public enum FontVariant { Normal, Smallcaps } - public static IfcTextStyleFontModel CreateTextStyleFontModel(this IModel model, double fontSize, string fontFamily, string fontModelName, FontStyle fontStyle = FontStyle.Normal, FontWeight fontWeight = FontWeight.Normal, FontVariant fontVariant = FontVariant.Normal) - { - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextstylefontmodel.htm - - string style = fontStyle switch - { - FontStyle.Normal => "normal", - FontStyle.Italic => "italic", - _ => "oblique" - }; - - string weight = fontWeight switch - { - FontWeight.Normal => "400", - _ => "700", - }; - - string variant = fontVariant switch - { - FontVariant.Normal => "normal", - _ => "small-caps", - }; - - return model.New(f => - { - f.Name = fontModelName; - f.FontSize = new IfcLengthMeasure(fontSize); - f.FontFamily.Add(new IfcTextFontName(fontFamily)); // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextfontname.htm - // optional - f.FontStyle = new IfcFontStyle(style); - f.FontWeight = new IfcFontWeight(weight); - f.FontVariant = new IfcFontVariant(variant); - }); - } - - public static IfcTextStyle CreateTextStyle(this IModel model, double fontSize, C3f colour, C3f background, string fontModelName, string fontFamily = "serif", bool modelOrDrauting = true, string name = null) - { - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextstyle.htm - - return model.New(ts => - { - ts.ModelOrDraughting = modelOrDrauting; - - if (name != null) ts.Name = name; - - ts.TextFontStyle = model.CreateTextStyleFontModel(fontSize, fontFamily, fontModelName); - - // optional - ts.TextCharacterAppearance = model.CreateTextStyleForDefinedFont(colour, background); - ts.TextStyle = model.CreateTextStyleTextModel(10, TextAlignment.Right, TextDecoration.None, TextTransform.None, 10, 10, 20); - }); - } - - #endregion - - #region Surface Styling - public static IfcSurfaceStyleShading CreateSurfaceStyleShading(this IModel model, C3d surface, double transparency = 0.0) - { - // https://standards.buildingsmart.org/MVD/RELEASE/IFC4/ADD2_TC1/RV1_2/HTML/schema/ifcpresentationappearanceresource/lexical/ifcsurfacestyleshading.htm - - return model.New(l => - { - l.SurfaceColour = model.CreateColor(surface); - l.Transparency = new IfcNormalisedRatioMeasure(transparency); // [0 = opaque .. 1 = transparent] - }); - } - - public static IfcSurfaceStyleRendering CreateSurfaceStyleRendering(this IModel model, C3d surface, double transparency, C3d diffuse, C3d diffuseTransmission, C3d transmission, C3d specular, double specularHighlight, C3d reflection, IfcReflectanceMethodEnum reflectionType) - { - // https://standards.buildingsmart.org/MVD/RELEASE/IFC4/ADD2_TC1/RV1_2/HTML/schema/ifcpresentationappearanceresource/lexical/ifcsurfacestylerendering.htm - - return model.New(l => - { - l.SurfaceColour = model.CreateColor(surface); - l.Transparency = new IfcNormalisedRatioMeasure(transparency); // [0 = opaque .. 1 = transparent] - l.DiffuseColour = model.CreateColor(diffuse); - - l.TransmissionColour = model.CreateColor(transmission); - l.DiffuseTransmissionColour = model.CreateColor(diffuseTransmission); - - l.ReflectionColour = model.CreateColor(reflection); - l.ReflectanceMethod = reflectionType; - - // The IfcSpecularExponent defines the datatype for exponent determining the sharpness of the 'reflection'. - // The reflection is made sharper with large values of the exponent, such as 10.0. - // Small values, such as 1.0, decrease the specular fall - off. - // IfcSpecularExponent is of type REAL. - l.SpecularHighlight = new IfcSpecularExponent(specularHighlight); - l.SpecularColour = model.CreateColor(specular); - }); - } - - public static IfcSurfaceStyleLighting CreateSurfaceStyleLighting(this IModel model, C3d diffuseTransmission, C3d diffuseReflection, C3d transmission, C3d reflectance) - { - // https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2/HTML/schema/ifcpresentationappearanceresource/lexical/ifcsurfacestylelighting.htm - - return model.New(l => - { - l.DiffuseTransmissionColour = model.CreateColor(diffuseTransmission); - l.DiffuseReflectionColour = model.CreateColor(diffuseReflection); - l.TransmissionColour = model.CreateColor(transmission); - l.ReflectanceColour = model.CreateColor(reflectance); - }); - } - - public static IfcSurfaceStyle CreateSurfaceStyle(this IModel model, IEnumerable styles, string name = null) - { - // IfcSurfaceStyle is an assignment of one or many surface style elements to a surface, defined by subtypes of - // IfcSurface, IfcFaceBasedSurfaceModel, IfcShellBasedSurfaceModel, or by subtypes of IfcSolidModel. - // The positive direction of the surface normal relates to the positive side. In case of solids the outside of the solid is to be taken as positive side. - - return model.New(style => - { - if (name != null) style.Name = name; - style.Side = IfcSurfaceSide.BOTH; - style.Styles.AddRange(styles); // [1:5] [IfcSurfaceStyleShading -> IfcSurfaceStyleRendering | IfcSurfaceStyleLighting | IfcSurfaceStyleWithTextures | IfcExternallyDefinedSurfaceStyle | IfcSurfaceStyleRefraction - }); - } - - public static IfcSurfaceStyle CreateSurfaceStyle(this IModel model, IfcSurfaceStyleElementSelect style, string name = null) - => CreateSurfaceStyle(model, [style], name); - - public static IfcSurfaceStyle CreateSurfaceStyle(this IModel model, C3d surface, double transparency = 0.0, string name = null) - => CreateSurfaceStyle(model, model.CreateSurfaceStyleShading(surface, transparency), name); - - #endregion - - #region Curve Styling - public static IfcCurveStyle CreateCurveStyle(this IModel model, C3d color, double width, double visibleLengh = 0, double invisibleLength = 0, bool modelOrDraughting = true) - { - return model.New(c => - { - c.ModelOrDraughting = modelOrDraughting; - c.CurveColour = model.CreateColor(color); - c.CurveWidth = new IfcPositiveLengthMeasure(width); - if (visibleLengh > 0) - { - c.CurveFont = model.New(f => - f.PatternList.Add(model.New(p => - { - p.VisibleSegmentLength = visibleLengh; - if (invisibleLength > 0) p.InvisibleSegmentLength = invisibleLength; - }) - )); - } - }); - } - #endregion - - #region Area Styling - public static IfcFillAreaStyleHatching CreateFillAreaStyleHatching(this IModel model, double angle, double startOfNextHatchLine, IfcCurveStyle curveStyle) - { - return model.New(h => - { - h.HatchLineAppearance = curveStyle; - h.HatchLineAngle = new IfcPlaneAngleMeasure(angle); - h.StartOfNextHatchLine = new IfcPositiveLengthMeasure(startOfNextHatchLine); - }); - } - - public static IfcFillAreaStyle CreateFillAreaStyle(this IModel model, C3d backgroundColor, bool modelOrDrauting = true, string name = null) - { - // NOTE: Color information of surfaces for rendering is assigned by using IfcSurfaceStyle, not by using IfcFillAreaStyle. - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifcfillareastyle.htm - return model.New(a => - { - if (name != null) a.Name = name; - a.ModelorDraughting = modelOrDrauting; - // Solid fill for areas and surfaces by only assigning IfcColour to the set of FillStyles. It then provides the background colour for the filled area or surface. - a.FillStyles.Add(model.CreateColor(backgroundColor)); - }); - } - - public static IfcFillAreaStyle CreateFillAreaStyle(this IModel model, C3d hatchingColour, double angle, double startOfNextHatchLine, IfcCurveStyle curveStyle, bool modelOrDrauting = true, string name = null) - { - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifcfillareastyle.htm - // - return model.New(a => - { - if (name != null) a.Name = name; - a.ModelorDraughting = modelOrDrauting; - // Solid fill for areas and surfaces by only assigning IfcColour to the set of FillStyles. It then provides the background colour for the filled area or surface. - a.FillStyles.AddRange([ - model.CreateColor(hatchingColour), - model.CreateFillAreaStyleHatching(angle, startOfNextHatchLine, curveStyle) - ]); - }); - } - #endregion - - #region Style Item - public static IfcStyledItem CreateStyleItem(this IfcRepresentationItem item, IEnumerable styles) - { - // Each subtype of IfcPresentationStyle is assigned to the IfcGeometricRepresentationItem's through an intermediate IfcStyledItem. - return item.Model.New(styleItem => { - styleItem.Styles.AddRange(styles); - if (item != null) styleItem.Item = item; - }); - } - - public static IfcStyledItem CreateStyleItem(this IfcRepresentationItem item, IfcPresentationStyle style) - => CreateStyleItem(item, [style]); - - #endregion - - #endregion - - #region Material - - public static IEnumerable GetProperties(this IIfcMaterial mat) - { - return mat.HasProperties - .SelectMany(mp => mp.Properties) - .OfType(); - } - - public static Dictionary GetPropertiesDict(this IIfcMaterial mat) => DistinctDictionaryFromPropertiesValues(mat.GetProperties()); - - public static IfcMaterialProperties CreateAttachPsetMaterialCommon(this IfcMaterial material, double molecularWeight, double porosity, double massDensity) - { - return material.Model.New(ps => { - ps.Name = "Pset_MaterialCommon"; - ps.Material = material; - ps.Properties.AddRange([ - material.Model.New(p => - { - p.Name = "MolecularWeight"; - p.NominalValue = new IfcMolecularWeightMeasure(molecularWeight); - }), - material.Model.New(p => - { - p.Name = "Porosity"; - p.NominalValue = new IfcNormalisedRatioMeasure(porosity); - }), - material.Model.New(p => - { - p.Name = "MassDensity"; - p.NominalValue = new IfcMassDensityMeasure(massDensity); - }) - ]); - }); - } - - public static IfcMaterialProperties CreateAttachPsetMaterialThermal(this IfcMaterial material, double thermalConductivity, double specificHeatCapacity, double boilingPoint = 100.0, double freezingPoint = 0.0) - { - return material.Model.New(ps => { - ps.Name = "Pset_MaterialThermal"; - ps.Material = material; - ps.Properties.AddRange([ - material.Model.New(p => - { - p.Name = "ThermalConductivity"; - p.NominalValue = new IfcThermalConductivityMeasure(thermalConductivity); - }), - material.Model.New(p => - { - p.Name = "SpecificHeatCapacity"; - p.NominalValue = new IfcSpecificHeatCapacityMeasure(specificHeatCapacity); - }), - material.Model.New(p => - { - p.Name = "BoilingPoint"; - p.NominalValue = new IfcThermodynamicTemperatureMeasure(boilingPoint); - }), - material.Model.New(p => - { - p.Name = "FreezingPoint"; - p.NominalValue = new IfcThermodynamicTemperatureMeasure(freezingPoint); - }) - ]); - }); - } - public static IfcMaterialDefinitionRepresentation CreateAttachPresentation(this IfcMaterial material, C3d surfaceColor) - { - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcrepresentationresource/lexical/ifcmaterialdefinitionrepresentation.htm - - var model = material.Model; - - return model.New(def => - { - def.RepresentedMaterial = material; - def.Representations.Add(model.New(rep => { - rep.ContextOfItems = model.GetGeometricRepresentationContextModel(); - rep.Items.Add(model.New(styleItem => { - //styleItem.Item -> NOTE: If the IfcStyledItem is used within a reference from an IfcMaterialDefinitionRepresentation then no Item shall be provided. - styleItem.Styles.Add(model.CreateSurfaceStyle(surfaceColor)); - })); - })); - }); - } - #endregion - - #region IfcGeometricRepresentationItem - - public static IfcGeometricRepresentationContext GetGeometricRepresentationContextPlan(this IModel model) - => model.Instances.OfType().Where(c => c.ContextType == "Plan").First(); - - public static IfcGeometricRepresentationContext GetGeometricRepresentationContextModel(this IModel model) - => model.Instances.OfType().Where(c => c.ContextType == "Model").First(); - - #region Lines - - public static IfcCartesianPointList2D CreateCartesianPointList2D(this IModel model, params V2d[] points) - { - return model.New(pl => - { - for (int i = 0; i < points.Length; i++) - { - pl.CoordList.GetAt(i).AddRange(points[i].ToArray().Select(v => new IfcLengthMeasure(v))); - } - }); - } - - public static IfcCartesianPointList3D CreateCartesianPointList3D(this IModel model, params V3d[] points) - { - return model.New(pl => - { - for (int i = 0; i < points.Length; i++) - { - pl.CoordList.GetAt(i).AddRange(points[i].ToArray().Select(v => new IfcLengthMeasure(v))); - } - }); - } - - public static IfcLine CreateLine(this IModel model, V2d start, V2d end) - { - var diff = end - start; - - return model.New(line => - { - line.Pnt = model.CreatePoint(start); - line.Dir = model.CreateVector(diff); - }); - } - - public static IfcLine CreateLine(this IModel model, V3d start, V3d end) - { - var diff = end - start; - - return model.New(line => - { - line.Pnt = model.CreatePoint(start); - line.Dir = model.CreateVector(diff); - }); - } - - public static IfcLine CreateLine(this IModel model, Line2d line) - => model.CreateLine(line.P0, line.P1); - - public static IfcLine CreateLine(this IModel model, Line3d line) - => model.CreateLine(line.P0, line.P1); - - public static IfcPolyline CreatePolyLine(this IModel model, params V2d[] points) - { - return model.New(line => - { - line.Points.AddRange(points.Select(x => model.CreatePoint(x))); - }); - } - - public static IfcPolyline CreatePolyLine(this IModel model, params V3d[] points) - { - return model.New(line => - { - line.Points.AddRange(points.Select(x => model.CreatePoint(x))); - }); - } - - public static IfcPolyline CreatePolyLine(this IModel model, IEnumerable points) - => model.CreatePolyLine(points.ToArray()); - - public static IfcPolyline CreatePolyLine(this IModel model, IEnumerable points) - => model.CreatePolyLine(points.ToArray()); - - public static IfcIndexedPolyCurve CreateIndexedPolyCurve(this IModel model, IEnumerable points, IEnumerable indices = null) - { - // NOTE: Indices start with 1! - return model.New(poly => - { - poly.Points = model.CreateCartesianPointList2D(points.ToArray()); - - if (indices != null) - { - var index = indices.Select(i => { - if (i.Length == 3) return (IfcSegmentIndexSelect)new IfcArcIndex(i.Select(x => new IfcPositiveInteger(x)).ToList()); - else if (i.Length == 2) return (IfcSegmentIndexSelect)new IfcLineIndex(i.Select(x => new IfcPositiveInteger(x)).ToList()); - else return null; - }); - poly.Segments.AddRange(index); - } - }); - } - - #endregion - - #region Surfaces - - public static IfcPlane CreatePlane(this IModel model, Plane3d plane) - { - var refDir = (plane.Normal.MajorDim == 2) ? plane.Normal.ZXY : plane.Normal.YXZ; - return model.New(pl => pl.Position = model.CreateAxis2Placement3D(plane.Point, refDir, plane.Normal)); - } - - public static IfcCurveBoundedPlane CreateCurveBoundedPlane(this IModel model, Plane3d plane, Polygon2d poly) - { - return model.New(p => - { - p.BasisSurface = model.CreatePlane(plane); - p.OuterBoundary = model.CreatePolyLine(poly.Points); - }); - } - - #endregion - - #region Lights - - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/link/lighting-geometry.htm - - public static IfcLightSourceAmbient CreateLightSourceAmbient(this IModel model, C3d color, string name = null, double? intensity = null, double? ambientIntensity = null) - { - return model.New(ls => { - ls.LightColour = model.CreateColor(color); - - if (name != null) ls.Name = name; - - // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). - // The intensity field specifies the brightness of the direct emission from the ligth. - if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); - // The ambientIntensity specifies the intensity of the ambient emission from the light. - if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; - }); - } - - public static IfcLightSourceDirectional CreateLightSourceDirectional(this IModel model, C3d color, V3d direction, string name = null, double? intensity = null, double? ambientIntensity = null) - { - return model.New(ls => { - ls.LightColour = model.CreateColor(color); - - if (name != null) ls.Name = name; - - // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). - // The intensity field specifies the brightness of the direct emission from the ligth. - if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); - // The ambientIntensity specifies the intensity of the ambient emission from the light. - if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; - - // Directional properties - ls.Orientation = model.CreateDirection(direction); - }); - } - - public static IfcLightSourcePositional CreateLightSourcePositional(this IModel model, C3d color, V3d position, double radius, double constantAttenuation, double distanceAttenuation, double quadricAttenuation, string name = null, double? intensity = null, double? ambientIntensity = null) - { - // The Point light node specifies a point light source at a 3D location in the local coordinate system. - // A point light source emits light equally in all directions; that is, it is omnidirectional. - - return model.New(ls => { - ls.LightColour = model.CreateColor(color); - - if (name != null) ls.Name = name; - - // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). - // The intensity field specifies the brightness of the direct emission from the ligth. - if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); - // The ambientIntensity specifies the intensity of the ambient emission from the light. - if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; - - // Definition from ISO/CD 10303-46:1992: The Cartesian point indicates the position of the light source. Definition from VRML97 - ISO/IEC 14772-1:1997: A Point light node illuminates geometry within radius of its location. - ls.Position = ls.Model.CreatePoint(position); - - // The maximum distance from the light source for a surface still to be illuminated. Definition from VRML97 - ISO/IEC 14772-1:1997: A Point light node illuminates geometry within radius of its location. - ls.Radius = new IfcPositiveLengthMeasure(radius); - - // Definition from ISO/CD 10303-46:1992: This real indicates the value of the attenuation in the lighting equation that is constant. - ls.ConstantAttenuation = constantAttenuation; - - // Definition from ISO/CD 10303-46:1992: This real indicates the value of the attenuation in the lighting equation that proportional to the distance from the light source. - ls.DistanceAttenuation = distanceAttenuation; - - // This real indicates the value of the attenuation in the lighting equation that proportional to the square value of the distance from the light source. - ls.QuadricAttenuation = quadricAttenuation; - }); - } - - public static IfcLightSourceSpot CreateLightSourceSpot (this IModel model, C3d color, V3d position, double radius, double constantAttenuation, double distanceAttenuation, double quadricAttenuation, V3d direction, double spreadAngle, double beamWidthAngle, string name = null, double? intensity = null, double? ambientIntensity = null, double? concentrationExponent = null) - { - // The Point light node specifies a point light source at a 3D location in the local coordinate system. - // A point light source emits light equally in all directions; that is, it is omnidirectional. - - return model.New(ls => { - ls.LightColour = model.CreateColor(color); - - if (name != null) ls.Name = name; - - // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). - // The intensity field specifies the brightness of the direct emission from the ligth. - if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); - // The ambientIntensity specifies the intensity of the ambient emission from the light. - if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; - - // Definition from ISO/CD 10303-46:1992: The Cartesian point indicates the position of the light source. Definition from VRML97 - ISO/IEC 14772-1:1997: A Point light node illuminates geometry within radius of its location. - ls.Position = ls.Model.CreatePoint(position); - - // The maximum distance from the light source for a surface still to be illuminated. Definition from VRML97 - ISO/IEC 14772-1:1997: A Point light node illuminates geometry within radius of its location. - ls.Radius = new IfcPositiveLengthMeasure(radius); - - // Definition from ISO/CD 10303-46:1992: This real indicates the value of the attenuation in the lighting equation that is constant. - ls.ConstantAttenuation = constantAttenuation; - - // Definition from ISO/CD 10303-46:1992: This real indicates the value of the attenuation in the lighting equation that proportional to the distance from the light source. - ls.DistanceAttenuation = distanceAttenuation; - - // This real indicates the value of the attenuation in the lighting equation that proportional to the square value of the distance from the light source. - ls.QuadricAttenuation = quadricAttenuation; - - //Definition from ISO / CD 10303 - 46:1992: This is the direction of the axis of the cone of the light source specified in the coordinate space of the representation being projected..Definition from VRML97 -ISO / IEC 14772 - 1:1997: The direction field specifies the direction vector of the light's central axis defined in the local coordinate system. X - ls.Orientation = ls.Model.CreateDirection(direction); - - // Definition from ISO / CD 10303 - 46:1992: This real is the exponent on the cosine of the angle between the line that starts at the position of the spot light source and is in the direction of the orientation of the spot light source and a line that starts at the position of the spot light source and goes through a point on the surface being shaded.NOTE This attribute does not exists in ISO / IEC 14772 - 1:1997.X - if(concentrationExponent.HasValue) ls.ConcentrationExponent = concentrationExponent.Value; - // Definition from ISO / CD 10303 - 46:1992: This planar angle measure is the angle between the line that starts at the position of the spot light source and is in the direction of the spot light source and any line on the boundary of the cone of influence. Definition from VRML97 - ISO / IEC 14772 - 1:1997: The cutOffAngle(name of spread angle in VRML) field specifies the outer bound of the solid angle.The light source does not emit light outside of this solid angle. X - ls.SpreadAngle = new IfcPositivePlaneAngleMeasure(spreadAngle); - // Definition from VRML97 - ISO / IEC 14772 - 1:1997: The beamWidth field specifies an inner solid angle in which the light source emits light at uniform full intensity. The light source's emission intensity drops off from the inner solid angle (beamWidthAngle) to the outer solid angle (spreadAngle). X - ls.BeamWidthAngle = new IfcPositivePlaneAngleMeasure(beamWidthAngle); - }); - } - - public readonly struct AngleAndIntensity - { - public double AnglesInDegree { get; } - public double IntentsityInCandelaPerLumen { get; } - - public AngleAndIntensity(double anglesInDegree, double intentsityInCandelaPerLumen) - { - AnglesInDegree = anglesInDegree; - IntentsityInCandelaPerLumen = intentsityInCandelaPerLumen; - } - } - - public readonly struct LightIntensityDistributionData - { - public double MainAngleInDegree { get; } - public AngleAndIntensity[] SecondaryAnglesAndIntensities { get; } - - public LightIntensityDistributionData(double angleInDegree, AngleAndIntensity[] data) - { - MainAngleInDegree = angleInDegree; - SecondaryAnglesAndIntensities = data; - } - } - - public static IfcLightIntensityDistribution CreateLightIntensityDistribution(this IModel model, IfcLightDistributionCurveEnum distributionEnum, IEnumerable data) - { - return model.New(d => - { - // Type C is the recommended standard system. The C-Plane system equals a globe with a vertical axis. C-Angles are valid from 0° to 360°, γ-Angles are valid from 0° (south pole) to 180° (north pole). - // Type B is sometimes used for floodlights.The B-Plane System has a horizontal axis.B - Angles are valid from - 180° to + 180° with B 0° at the bottom and B180°/ B - 180° at the top, β - Angles are valid from - 90° to + 90°. - // Type A is basically not used.For completeness the Type A Photometry equals the Type B rotated 90° around the Z - Axis counter clockwise. - d.LightDistributionCurve = distributionEnum; - d.DistributionData.AddRange(data.Select(a => - { - return model.New(data => - { - // The main plane angle (A, B or C angles, according to the light distribution curve chosen). - data.MainPlaneAngle = new IfcPlaneAngleMeasure(a.MainAngleInDegree.RadiansFromDegrees()); // measured in radians - - // The list of secondary plane angles (the α, β or γ angles) according to the light distribution curve chosen. - // NOTE: The SecondaryPlaneAngle and LuminousIntensity lists are corresponding lists. - data.SecondaryPlaneAngle.AddRange(a.SecondaryAnglesAndIntensities.Select(sa => new IfcPlaneAngleMeasure(sa.AnglesInDegree.RadiansFromDegrees()))); - - // The luminous intensity distribution measure for this pair of main and secondary plane angles according to the light distribution curve chosen. - data.LuminousIntensity.AddRange(a.SecondaryAnglesAndIntensities.Select(m => new IfcLuminousIntensityDistributionMeasure(m.IntentsityInCandelaPerLumen))); // measured in Candela/Lumen (cd/lm) or (cd/klm). - }); - })); - }); - } - - public static IfcLightSourceGoniometric CreateLightSourceGoniometric(this IModel model, C3d color, double colourTemperature, double luminousFlux, - IfcLightEmissionSourceEnum lightEmissionSource, IfcLightIntensityDistribution data, IfcAxis2Placement3D placement, C3d? appearance = null, string name = null, double? intensity = null, double? ambientIntensity = null) - { - return model.New(ls => { - - ls.LightColour = model.CreateColor(color); - - if (name != null) ls.Name = name; - - // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). - // The intensity field specifies the brightness of the direct emission from the ligth. - if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); - // The ambientIntensity specifies the intensity of the ambient emission from the light. - if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; - - // Goniometric Properties - ls.Position = placement; - if (appearance.HasValue) ls.ColourAppearance = model.CreateColor(appearance.Value); - ls.ColourTemperature = new IfcThermodynamicTemperatureMeasure(colourTemperature); - ls.LuminousFlux = new IfcLuminousFluxMeasure(luminousFlux); - - ls.LightEmissionSource = lightEmissionSource; - - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/link/lighting-geometry.htm - ls.LightDistributionDataSource = data; - - }); - } - - #endregion - - #endregion - - #region IfcShapeRepresentation - - #region SurveyPoints (broken) - IfcShapeRepresentation - public static IfcShapeRepresentation CreateShapeRepSurveyPoints(this IModel model, IfcPresentationLayerWithStyle layer, params V2d[] points) - { - // Set of Survey points 2D https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Annotation_Geometry/Set_Of_Survey_Points/content.html - IfcGeometricRepresentationItem item = model.CreateCartesianPointList2D(points).AssignLayer(layer); - - return model.New(r => - { - r.RepresentationIdentifier = "Annotation"; - r.RepresentationType = "Point"; - r.Items.Add(item); - r.ContextOfItems = model.GetGeometricRepresentationContextPlan(); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationSurveyPoints(this IModel model, params V2d[] points) - => CreateShapeRepSurveyPoints(model, null, points); - - public static IfcShapeRepresentation CreateShapeRepresentationSurveyPoints(this IModel model, IfcPresentationLayerWithStyle layer, params V3d[] points) - { - // Set of Survey points 3D https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Annotation_Geometry/Set_Of_Survey_Points/content.html - IfcGeometricRepresentationItem item = model.CreateCartesianPointList3D(points).AssignLayer(layer); - - return model.New(r => - { - r.RepresentationIdentifier = "Annotation"; - r.RepresentationType = "Point"; - r.Items.Add(item); - r.ContextOfItems = model.GetGeometricRepresentationContextModel(); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationSurveyPoints(this IModel model, params V3d[] points) - => CreateShapeRepresentationSurveyPoints(model, null, points); - - #endregion - - #region Annotation - IfcShapeRepresentation - // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/link/object-predefined-type.htm - #region 3D Annotations - IfcShapeRepresentation - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3d(this IfcRepresentationItem item) - { - return item.Model.New(r => - { - r.RepresentationIdentifier = "Annotation"; - r.RepresentationType = "GeometricSet"; - r.Items.Add(item); - r.ContextOfItems = item.Model.GetGeometricRepresentationContextModel(); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3dPoint(this IModel model, V3d point, IfcPresentationLayerWithStyle layer = null) - { - var content = model.CreatePoint(point); - layer?.AssignedItems.Add(content); - - return content.CreateShapeRepresentationAnnotation3d(); - } - - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3dCurve(this IModel model, V3d[] points, IfcPresentationLayerWithStyle layer = null) - { - IfcGeometricRepresentationItem content = model.CreatePolyLine(points); - layer?.AssignedItems.Add(content); - - return content.CreateShapeRepresentationAnnotation3d(); - } - - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3dCross(this IModel model, V3d origin, V3d normal, double angleInDegree, double scale, IfcPresentationLayerWithStyle layer = null) - { - //var crossPoints = new V3d[] { - // origin, origin+(axis1.Normalized * scale), - // origin, origin-(axis1.Normalized * scale), - // origin, origin+(axis2.Normalized * scale), - // origin, origin-(axis2.Normalized * scale), - //}; - var plane = new Plane3d(normal, 0.0); - var d = new Rot2d(angleInDegree.RadiansFromDegrees()) * V2d.XAxis * scale; - - var crossPoints = new V3d[] { - origin, origin+plane.Unproject( d), //+dir.XYO, - origin, origin+plane.Unproject(-d), //-dir.XYO, - origin, origin+plane.Unproject( d.YX * new V2d(-1,1)), //+dir.YXO * new V3d(-1,1,0), - origin, origin+plane.Unproject( d.YX * new V2d(1,-1)), //+dir.YXO * new V3d(1,-1,0) - }; - - IfcGeometricRepresentationItem content = model.CreatePolyLine(crossPoints); - layer?.AssignedItems.Add(content); - - return content.CreateShapeRepresentationAnnotation3d(); - } - - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3dSurface(this IModel model, Plane3d plane, Polygon2d poly, IfcPresentationLayerWithStyle layer = null) - { - IfcGeometricRepresentationItem content = model.CreateCurveBoundedPlane(plane, poly); - layer?.AssignedItems.Add(content); - - return content.CreateShapeRepresentationAnnotation3d(); - } - #endregion - - #region 2D Annotations - IfcShapeRepresentation - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2D(this IfcRepresentationItem item) - { - return item.Model.New(r => - { - r.RepresentationIdentifier = "Annotation"; - r.RepresentationType = "Annotation2D"; - r.Items.Add(item); - r.ContextOfItems = item.Model.GetGeometricRepresentationContextPlan(); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2dPoint(this IModel model, V2d point, IfcPresentationLayerWithStyle layer = null) - { - var item = model.CreatePoint(point); - layer?.AssignedItems.Add(item); - - return item.CreateShapeRepresentationAnnotation2D(); - } - - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2dCurve(this IModel model, V2d[] points, IEnumerable indices = null, IfcPresentationLayerWithStyle layer = null) - { - IfcGeometricRepresentationItem item = model.CreateIndexedPolyCurve(points, indices); - layer?.AssignedItems.Add(item); - - return item.CreateShapeRepresentationAnnotation2D(); - } - - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2dText(this IModel model, string label, V2d position, IfcPresentationLayerWithStyle layer = null) - { - // ONLY visible in "BIMVISION" - IfcGeometricRepresentationItem item = model.New(l => - { - // https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD1/HTML/schema/ifcpresentationdefinitionresource/lexical/ifctextliteral.htm - l.Path = IfcTextPath.RIGHT; - l.Literal = label; - //// Attributes for - //l.BoxAlignment = new IfcBoxAlignment("center"); - //l.Extent = model.New(e => - //{ - // e.SizeInX = new IfcLengthMeasure(300.0); - // e.SizeInY = new IfcLengthMeasure(200.0); - //}); - l.Placement = model.CreateAxis2Placement2D(position); - }); - layer?.AssignedItems.Add(item); - - return item.CreateShapeRepresentationAnnotation2D(); - } - - public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2dArea(this IModel model, Box2d rect, IfcPresentationLayerWithStyle layer = null) - { - IfcGeometricRepresentationItem item = model.New(l => - { - l.OuterBoundary = model.CreateIndexedPolyCurve(rect.ComputeCornersCCW()); - l.InnerBoundaries.Add(model.CreateIndexedPolyCurve(rect.ShrunkBy(new V2d(0.3)).ComputeCornersCCW())); - }); - layer?.AssignedItems.Add(item); - - return item.CreateShapeRepresentationAnnotation2D(); - } - - #endregion - - #endregion - - - public static IfcRepresentationMap CreateRepresentationMap(this IfcRepresentation item) - { - return item.Model.New(map => - { - map.MappingOrigin = item.Model.CreateAxis2Placement3D(V3d.Zero); - map.MappedRepresentation = item; - }); - } - - public static IfcShapeRepresentation Instantiate(this IfcRepresentationMap map, Trafo3d trafo) - { - var item = map.Model.New(m => - { - m.MappingSource = map; - m.MappingTarget = map.Model.New(x => - { - var scale = trafo.GetScaleVector(); - x.Axis1 = map.Model.CreateDirection(trafo.Forward.C0.XYZ.Normalized); // X - Axis - x.Axis2 = map.Model.CreateDirection(trafo.Forward.C1.XYZ.Normalized); // Y - Axis - x.Axis3 = map.Model.CreateDirection(trafo.Forward.C2.XYZ.Normalized); // Z - Axis - x.LocalOrigin = map.Model.CreatePoint(trafo.Forward.C3.XYZ); - x.Scale = scale.X; - x.Scale2 = scale.Y; - x.Scale3 = scale.Z; - }); - }); - - return item.Model.New(r => - { - r.RepresentationIdentifier = "Body"; - r.RepresentationType = "MappedRepresentation"; - r.Items.Add(item); - r.ContextOfItems = item.Model.GetGeometricRepresentationContextModel(); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationLighting(this IfcRepresentationItem item) - { - return item.Model.New(r => - { - r.RepresentationIdentifier = "Lighting"; - r.RepresentationType = "LightSource"; - r.Items.Add(item); - r.ContextOfItems = item.Model.GetGeometricRepresentationContextModel(); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationLightingAmbient(this IModel model, C3d color, string name = null, double? intensity = null, double? ambientIntensity = null, IfcPresentationLayerAssignment layer = null) - { - - IfcGeometricRepresentationItem item = model.CreateLightSourceAmbient(color, name, intensity, ambientIntensity); - layer?.AssignedItems.Add(item); - - return CreateShapeRepresentationLighting(item); - } - - public static IfcShapeRepresentation CreateShapeRepresentationLightingDirectional(this IModel model, C3d color, V3d direction, string name = null, double? intensity = null, double? ambientIntensity = null, IfcPresentationLayerAssignment layer = null) - { - IfcGeometricRepresentationItem item = model.CreateLightSourceDirectional(color, direction, name, intensity, ambientIntensity); - layer?.AssignedItems.Add(item); - - return CreateShapeRepresentationLighting(item); - } - - public static IfcShapeRepresentation CreateShapeRepresentationLightingPositional (this IModel model, C3d color, V3d position, double radius, double constantAttenuation, double distanceAttenuation, double quadricAttenuation, string name = null, double? intensity = null, double? ambientIntensity = null, IfcPresentationLayerAssignment layer = null) - { - IfcGeometricRepresentationItem item = model.CreateLightSourcePositional(color, position, radius, constantAttenuation, distanceAttenuation, quadricAttenuation, name, intensity, ambientIntensity); - layer?.AssignedItems.Add(item); - - return CreateShapeRepresentationLighting(item); - } - - public static IfcShapeRepresentation CreateShapeRepresentationLightingSpot(this IModel model, C3d color, V3d position, V3d direction, double radius, double constantAttenuation, double distanceAttenuation, double quadricAttenuation, double spreadAngle, double beamWidthAngle, string name = null, double? intensity = null, double? ambientIntensity = null, double? concentrationExponent = null, IfcPresentationLayerAssignment layer = null) - { - IfcGeometricRepresentationItem item = model.CreateLightSourceSpot(color, position, radius, constantAttenuation, distanceAttenuation, quadricAttenuation, direction, spreadAngle, beamWidthAngle, name, intensity, ambientIntensity, concentrationExponent); - layer?.AssignedItems.Add(item); - - return CreateShapeRepresentationLighting(item); - } - - public static IfcShapeRepresentation CreateShapeRepresentationLightingGoniometric(this IModel model, C3d color, V3d location, double colourTemperature, double luminousFlux, IfcLightIntensityDistribution distribution, IfcLightEmissionSourceEnum lightEmissionSource = IfcLightEmissionSourceEnum.NOTDEFINED, IfcPresentationLayerAssignment layer = null) - { - var placement = model.CreateAxis2Placement3D(location); - IfcGeometricRepresentationItem item = model.CreateLightSourceGoniometric(color, colourTemperature, luminousFlux, lightEmissionSource, distribution, placement); - layer?.AssignedItems.Add(item); - - return CreateShapeRepresentationLighting(item); - } - - public static IfcShapeRepresentation CreateShapeRepresentationBoundingBox(this IModel model, Box3d box, IfcPresentationLayerAssignment layer = null) - { - IfcGeometricRepresentationItem item = model.New(b => b.Set(box)); - layer?.AssignedItems.Add(item); - - return model.New(r => - { - r.RepresentationIdentifier = "Box"; - r.RepresentationType = "BoundingBox"; - r.Items.Add(item); - r.ContextOfItems = model.GetGeometricRepresentationContextModel(); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationSolidBox(this IModel model, Box3d box, IfcPresentationLayerAssignment layer = null) - { - // Box creation by extruding box-base along Z-Axis - var rectProf = model.New(p => - { - p.ProfileName = "RectArea"; - p.ProfileType = IfcProfileTypeEnum.AREA; - p.XDim = box.SizeX; - p.YDim = box.SizeY; - //p.Position = model.CreateAxis2Placement2D(V2d.Zero); - }); - - IfcGeometricRepresentationItem item = model.New(solid => - { - solid.Position = model.CreateAxis2Placement3D(box.Min); - solid.Depth = box.SizeZ; - solid.ExtrudedDirection = model.CreateDirection(V3d.ZAxis); - solid.SweptArea = rectProf; - }); - layer?.AssignedItems.Add(item); - - return model.New(s => { - s.ContextOfItems = model.GetGeometricRepresentationContextModel(); - s.RepresentationType = "SweptSolid"; - s.RepresentationIdentifier = "Body"; - s.Items.Add(item); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationSurface(this IModel model, Plane3d plane, Polygon2d poly, IfcPresentationLayerAssignment layer = null) - { - if (!poly.IsCcw()) - { - poly.Reverse(); - } - - IfcGeometricRepresentationItem item = model.CreateCurveBoundedPlane(plane, poly); - layer?.AssignedItems.Add(item); - - // Box creation by extruding box-base along Z-Axis - return model.New(r => - { - r.RepresentationIdentifier = "Surface"; - r.RepresentationType = "Surface3D"; - r.Items.Add(item); - r.ContextOfItems = model.GetGeometricRepresentationContextModel(); - }); - } - - private static IfcTriangulatedFaceSet CreateTriangulatedFaceSet(IModel model, PolyMesh inputMesh) - { - var triangleMesh = inputMesh.TriangulatedCopy(); - - return model.New(tfs => - { - tfs.Closed = true; - tfs.Coordinates = model.CreateCartesianPointList3D(triangleMesh.PositionArray); - - for (int i = 0; i < triangleMesh.FirstIndexArray.Length - 1; i++) - { - var firstIndex = triangleMesh.FirstIndexArray[i]; - var values = new long[3].SetByIndex(x => triangleMesh.VertexIndexArray[firstIndex + x]).Select(v => new IfcPositiveInteger(v + 1)); // CAUTION! Indices are 1 based in IFC! - tfs.CoordIndex.GetAt(i).AddRange(values); - } - }); - } - - private static IfcPolygonalFaceSet CreatePolygonalFaceSet(IModel model, PolyMesh inputMesh) - { - // only available in IFC4 - if (model.SchemaVersion != XbimSchemaVersion.Ifc4) - return null; - - var faces = new List(inputMesh.Faces.Count()); - - foreach (var face in inputMesh.Faces) - { - faces.Add(model.New(f => f.CoordIndex.AddRange(face.VertexIndices.Select(v => new IfcPositiveInteger(v + 1))))); // CAUTION! Indices are 1 based in IFC! - } - - return model.New(p => - { - p.Closed = true; - p.Coordinates = model.CreateCartesianPointList3D(inputMesh.PositionArray); - p.Faces.AddRange(faces); - }); - } - - public static IfcShapeRepresentation CreateShapeRepresentationTessellation(this IModel model, PolyMesh mesh, IfcPresentationLayerAssignment layer = null, bool triangulated = true) - { - IfcGeometricRepresentationItem item = triangulated ? CreateTriangulatedFaceSet(model, mesh) : CreatePolygonalFaceSet(model, mesh); - layer?.AssignedItems.Add(item); - - return model.New(s => { - s.ContextOfItems = model.GetGeometricRepresentationContextModel(); - s.RepresentationType = "Tessellation"; - s.RepresentationIdentifier = "Body"; - s.Items.Add(item); - }); - } - - #endregion - - #region Element with PolyMesh - - public static T CreateElement(this IModel model, string elementName, IfcObjectPlacement placement, PolyMesh mesh, IfcSurfaceStyle surfaceStyle = null, IfcPresentationLayerAssignment layer = null, bool triangulated = true) where T : IfcProduct, IInstantiableEntity - { - // create a Definition shape to hold the geometry - var shape = model.CreateShapeRepresentationTessellation(mesh, layer, triangulated); - IfcRepresentationItem repItem = shape.Items.First(); - - // apply specific surfaceStyle / otherwise use mesh color / apply surface style / fallback-color - IfcSurfaceStyle surf; - - if (surfaceStyle != null) - { - surf = surfaceStyle; - } - else if (mesh.HasColors) - { - // TODO: add mesh-color cach (could have implications on other re-used objects) - var col = ((C4b)mesh.VertexAttributes.Get(PolyMesh.Property.Colors).GetValue(0)).ToC4d(); - surf = model.CreateSurfaceStyle(col.RGB, (1.0 - col.A).Clamp(0, 1), "MeshColor"); - } - else if (layer is IfcPresentationLayerWithStyle a && a.LayerStyles.OfType().FirstOrDefault() != null) - { - surf = a.LayerStyles.OfType().First(); - } - else - { - // caching / re-using of default_surfaces - var defaultSurface = model.Instances.OfType().Where(x => x.Name == "Default_Surface").FirstOrDefault(); - surf = defaultSurface ?? model.CreateSurfaceStyle(C3d.Red, 0.0, "Default_Surface"); - } - - // create visual style (works with 3d-geometry - body) - repItem.CreateStyleItem(surf); - - var proxy = model.New(c => { - c.Name = elementName; - - // create a Product Definition and add the model geometry to the cube - c.Representation = model.New(r => r.Representations.Add(shape)); - - // now place the object into the model - c.ObjectPlacement = placement; - }); - - return proxy; - } - - public static T CreateElement(this IModel model, string elementName, V3d position, PolyMesh mesh, IfcSurfaceStyle surfaceStyle = null, IfcPresentationLayerAssignment layer = null, bool triangulated = true) where T : IfcProduct, IInstantiableEntity - { - var placement = model.CreateLocalPlacement(position); - return model.CreateElement(elementName, placement, mesh, surfaceStyle, layer, triangulated); - } - - public static T CreateAttachElement(this IfcSpatialStructureElement parent, string elementName, IfcObjectPlacement placement, PolyMesh mesh, IfcSurfaceStyle surfaceStyle = null, IfcPresentationLayerAssignment layer = null, bool triangulated = true) where T : IfcProduct, IInstantiableEntity - { - var element = parent.Model.CreateElement(elementName, placement, mesh, surfaceStyle, layer, triangulated); - parent.AddElement(element); - return element; - } - - public static T CreateAttachElement(this IfcSpatialStructureElement parent, string elementName, V3d position, PolyMesh mesh, IfcSurfaceStyle surfaceStyle = null, IfcPresentationLayerAssignment layer = null, bool triangulated = true) where T : IfcProduct, IInstantiableEntity - { - var element = parent.Model.CreateElement(elementName, position, mesh, surfaceStyle, layer, triangulated); - parent.AddElement(element); - return element; - } - - #endregion - - #region Scene - public static IEnumerable ValidateModel(this IfcStore model) - { - var validator = new Validator() - { - CreateEntityHierarchy = true, - ValidateLevel = ValidationFlags.All - }; - - var result = validator.Validate(model.Instances); - - result.ForEach(error => - { - Report.Line(error.Message + " with " + error.Details.Count()); - error.Details.ForEach(detail => Report.Line(detail.IssueSource + " " + detail.IssueType)); - }); - - return result; - } - - public static void CreateMinimalProject(this IfcStore model, ProjectUnits units = ProjectUnits.SIUnitsUK, string projectName = "Project", string siteName = "Site") - { - using var txnInit = model.BeginTransaction("Init Project"); - // there should always be one project in the model - var project = model.New(p => p.Name = projectName); - // our shortcut to define basic default units - project.Initialize(units); - - // add site - var site = model.New(w => w.Name = siteName); - project.AddSite(site); - - txnInit.Commit(); - } - #endregion - - - public static T LinkToType(this T obj, IIfcTypeObject objType) where T : IIfcObject - { - obj.AddDefiningType(objType); - return obj; - } - - public static T CreateBuildingElementType(this IModel model, IEnumerable repMaps, IEnumerable properties, string name = "") where T : IIfcTypeProduct, IInstantiableEntity - { - return model.New(l => - { - //l.PredefinedType; // Has to be set afterwards! - l.Name = name; - if (!repMaps.IsEmptyOrNull()) l.RepresentationMaps.AddRange(repMaps); // shared geometries - if (!properties.IsEmptyOrNull()) l.HasPropertySets.AddRange(properties); // shared properties - }); - } - - public static I Instantiate(this T objectType, string name, IfcObjectPlacement placement, Trafo3d trafo) where T : IfcElementType where I : IIfcProduct, IInstantiableEntity - { - // create instance and transform all representations by global trafo - var instance = objectType.Model.New(t => - { - t.Name = name; - t.ObjectPlacement = placement; - t.Representation = objectType.Model.New(r => - t.Representation = objectType.Model.New(r => - r.Representations.AddRange(objectType.RepresentationMaps.Select(m => m.Instantiate(trafo))))); - }); - - return instance.LinkToType(objectType); - } - - public static I Instantiate(this T objectType, string name, IfcObjectPlacement placement, Dictionary trafos) where T : IfcElementType where I : IIfcProduct, IInstantiableEntity - { - // create instance and transform indevidual representations - var instance = objectType.Model.New(t => - { - t.Name = name; - t.ObjectPlacement = placement; - t.Representation = objectType.Model.New(r => - r.Representations.AddRange(objectType.RepresentationMaps.Select(m => - trafos.TryGetValue(m, out Trafo3d trafo) ? m.Instantiate(trafo) : m.Instantiate(Trafo3d.Identity)))); - }); - - return instance.LinkToType(objectType); - } - - public static IfcBuildingElementProxyType CreateProxyType(this IModel model, IfcBuildingElementProxyTypeEnum proxyType, IEnumerable repMaps, IEnumerable properties, string name = "") - { - var asdf = new EntityCreator(model); - asdf.Wall(w => w.Name = ""); - - var proxy = CreateBuildingElementType(model, repMaps, properties, name); - proxy.PredefinedType = proxyType; - return proxy; - } - } - - public static class IfcObjectExtensions - { - public static IIfcValue GetArea(this IfcObject o) - { - // short-cut - var area = o.PhysicalSimpleQuantities.OfType().FirstOrDefault()?.AreaValue; //.TryGetSimpleValue(out double areaValue); - - ////try to get the value from quantities first - //var area = - // //get all relations which can define property and quantity sets - // obj.IsDefinedBy - - // //Search across all property and quantity sets. - // //You might also want to search in a specific quantity set by name - // .SelectMany(r => r.RelatingPropertyDefinition.PropertySetDefinitions) - - // //Only consider quantity sets in this case. - // .OfType() - - // //Get all quantities from all quantity sets - // .SelectMany(qset => qset.Quantities) - - // //We are only interested in areas - // .OfType() - - // //We will take the first one. There might obviously be more than one area properties - // //so you might want to check the name. But we will keep it simple for this example. - // .FirstOrDefault()?.AreaValue; - - if (area != null) return area; - - //try to get the value from properties - return IFCHelper.GetProperty(o, "Area"); // .TryGetSimpleValue(out double areaValue2); - } - - public static IfcSlab CreateAttachSlab(this IfcSpatialStructureElement parent, string elementName, IfcPresentationLayerAssignment layer, IfcMaterial material) - { - var model = parent.Model; - - var box = new Box3d(V3d.Zero, new V3d(100.0, 100.0, 300.0)); - - var shape = model.New(s => { - - var rectProf = model.New(p => - { - p.ProfileName = "RectArea"; - p.ProfileType = IfcProfileTypeEnum.AREA; - p.XDim = box.SizeX; - p.YDim = box.SizeY; - }); - - IfcGeometricRepresentationItem item = model.New(solid => - { - solid.Position = parent.Model.CreateAxis2Placement3D(box.Min); - solid.Depth = box.SizeZ; // CAUTION: this must be the layer-thickness - solid.ExtrudedDirection = parent.Model.CreateDirection(V3d.ZAxis); // CAUTION: this must be the layer-orientation - solid.SweptArea = rectProf; - }); - layer?.AssignedItems.Add(item); - - s.ContextOfItems = model.Instances.OfType().First(); - s.RepresentationType = "SweptSolid"; - s.RepresentationIdentifier = "Body"; - s.Items.Add(item); - }); - - var slab = model.New(c => { - c.Name = elementName; - c.Representation = model.New(r => r.Representations.Add(shape)); - c.ObjectPlacement = parent.Model.CreateLocalPlacement(new V3d(500, 500, 500)); - }); - parent.AddElement(slab); - - // Link Material via RelAssociatesMaterial - model.New(mat => - { - // Material Layer Set Usage (HAS TO BE MANUALLY SYNCHED!) - IfcMaterialLayerSetUsage usage = model.New(u => - { - u.DirectionSense = IfcDirectionSenseEnum.NEGATIVE; - u.LayerSetDirection = IfcLayerSetDirectionEnum.AXIS3; - u.OffsetFromReferenceLine = 0; - u.ForLayerSet = model.New(set => - { - set.LayerSetName = "Concrete Layer Set"; - set.MaterialLayers.Add(model.New(layer => - { - layer.Name = "Layer1"; - layer.Material = material; - layer.LayerThickness = box.SizeZ; - layer.IsVentilated = false; - layer.Category = "Core"; - })); - }); - }); - - mat.Name = "RelMat"; - mat.RelatingMaterial = usage; - mat.RelatedObjects.Add(slab); - }); - - return slab; - } - - public static IfcAnnotation CreateAnnotation(this IModel model, string text, IfcObjectPlacement placement, V3d position, IfcPresentationLayerWithStyle layer) - { - // Anotation-Experiments https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcAnnotation.htm - return model.New(a => - { - var box = new Box3d(V3d.Zero, new V3d(200, 100, 500)); // mm - - a.Name = "Intersection of " + text; - a.ObjectPlacement = placement; - a.Representation = model.New(r => { - r.Representations.AddRange([ - model.CreateShapeRepresentationAnnotation2dText(text, position.XY, layer), - model.CreateShapeRepresentationAnnotation2dCurve([position.XY, (position.XY + new V2d(500, 750.0)), (position.XY + new V2d(1000,1000))], [[1,2,3]], layer), - model.CreateShapeRepresentationAnnotation3dCurve([position, (position + new V3d(500, 750.0, 100)), (position + new V3d(1000,1000, 200))], layer), - model.CreateShapeRepresentationAnnotation3dSurface(Plane3d.ZPlane, new Polygon2d(box.XY.Translated(position.XY-box.XY.Center).ComputeCornersCCW()), layer), - model.CreateShapeRepresentationAnnotation3dCross(position, V3d.YAxis, 45, 1000.0, layer) - //// NOT-displayed in BIMVision - //model.CreateShapeRepresentationAnnotation2dPoint(position.XY, layer), - //model.CreateShapeRepresentationAnnotation3dPoint(position, layer), - //model.CreateShapeRepresentationAnnotation2dArea(new Box2d(V2d.Zero, V2d.One*1000.0), layer), - - // broken - //model.CreateShapeRepresentationSurveyPoints(position.XY), - //model.CreateShapeRepresentationSurveyPoints(position), - ]); - }); - }); - } - } - - public static class IfcLightingExtensions - { - public static IfcLightFixtureType CreateLightType(this IModel model, IfcLightFixtureTypeEnum lightType, IEnumerable repMaps, IEnumerable properties, string name = "") - { - var proxy = IFCHelper.CreateBuildingElementType(model, repMaps, properties, name); - proxy.PredefinedType = lightType; - return proxy; - } - - public static IfcLightFixture Instantiate(this IfcLightFixtureType lightType, string name, IfcObjectPlacement placement, Trafo3d trafo, IfcLightFixtureTypeEnum? ltenum = null) - { - var instance = IFCHelper.Instantiate(lightType, name, placement, trafo); - if (lightType.PredefinedType != IfcLightFixtureTypeEnum.NOTDEFINED && ltenum.HasValue) instance.PredefinedType = ltenum; - return instance; - } - - public static IfcLightFixture Instantiate(this IfcLightFixtureType lightType, string name, IfcObjectPlacement placement, Dictionary trafos, IfcLightFixtureTypeEnum? ltenum = null) - { - var instance = IFCHelper.Instantiate(lightType, name, placement, trafos); - if (lightType.PredefinedType != IfcLightFixtureTypeEnum.NOTDEFINED && ltenum.HasValue) instance.PredefinedType = ltenum; - return instance; - } - - public static IfcPropertySet Pset_LightFixtureTypeCommon(this IfcLightFixture light, string reference, int numberOfSources, double totalWattage, - double maintenanceFactor, double maximumPlenumSensibleLoad, double maximumSpaceSensibleLoad, double sensibleLoadToRadiant, - string status, string lightFixtureMountingType, string lightFixturePlacingType) - { - var propertySet = light.Model.New(pset => { - pset.Name = "Pset_LightFixtureTypeCommon"; - pset.HasProperties.Add(light.Model.CreatePropertySingleValue("Reference", new IfcIdentifier(reference))); - pset.HasProperties.Add(light.Model.CreatePropertySingleValue("NumberOfSources", new IfcInteger(numberOfSources))); - pset.HasProperties.Add(light.Model.CreatePropertySingleValue("TotalWattage", new IfcPowerMeasure(totalWattage))); - pset.HasProperties.Add(light.Model.CreatePropertySingleValue("MaintenanceFactor", new IfcReal(maintenanceFactor))); - pset.HasProperties.Add(light.Model.CreatePropertySingleValue("MaximumPlenumSensibleLoad", new IfcPowerMeasure(maximumPlenumSensibleLoad))); - pset.HasProperties.Add(light.Model.CreatePropertySingleValue("MaximumSpaceSensibleLoad", new IfcPowerMeasure(maximumSpaceSensibleLoad))); - pset.HasProperties.Add(light.Model.CreatePropertySingleValue("SensibleLoadToRadiant", new IfcPositiveRatioMeasure(sensibleLoadToRadiant))); - pset.HasProperties.Add(light.Model.CreatePropertyEnumeratedValue("Status", new IfcLabel(status))); - pset.HasProperties.Add(light.Model.CreatePropertyEnumeratedValue("LightFixtureMountingType", new IfcLabel(lightFixtureMountingType))); - pset.HasProperties.Add(light.Model.CreatePropertyEnumeratedValue("LightFixturePlacingType", new IfcLabel(lightFixturePlacingType))); - }); - - light.AddPropertySet(propertySet); - return propertySet; - } - - public static IIfcElementQuantity Qto_LightFixtureBaseQuantities(this IfcLightFixture light, double weight) - { - return light.AddQuantity("Qto_LightFixtureBaseQuantities", light.Model.CreateQuantityWeight("GrossWeight", weight)); - } - - public static IfcLightFixture CreateLightEmpty(this IModel model, string name, IfcObjectPlacement placement, IfcShapeRepresentation lightShape, IfcLightFixtureTypeEnum? lightType = null) - { - return model.New(t => - { - if (lightType != null) t.PredefinedType = lightType.Value; - t.Name = name; - t.ObjectPlacement = placement; - t.Representation = model.New(r => r.Representations.Add(lightShape)); - }); - } - - public static IfcLightFixture CreateLightAmbient(this IModel model, string name, C3d color, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) - { - var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.POINTSOURCE); - light.Representation.Representations.Add(model.CreateShapeRepresentationLightingAmbient(color)); - return light; - } - - public static IfcLightFixture CreateLightPositional(this IModel model, string name, C3d color, V3d position, double radius, V3d attenuation, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) - { - var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.POINTSOURCE); - light.Representation.Representations.Add(model.CreateShapeRepresentationLightingPositional(color, position, radius, attenuation.X, attenuation.Y, attenuation.Z)); - return light; - } - - public static IfcLightFixture CreateLightDirectional(this IModel model, string name, C3d color, V3d direction, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) - { - var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.DIRECTIONSOURCE); - light.Representation.Representations.Add(model.CreateShapeRepresentationLightingDirectional(color, direction)); - return light; - } - - public static IfcLightFixture CreateLightSpot(this IModel model, string name, C3d color, V3d position, V3d direction, double radius, V3d attenuation, double spreadAngle, double beamWidthAngle, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) - { - var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.DIRECTIONSOURCE); - light.Representation.Representations.Add(model.CreateShapeRepresentationLightingSpot(color, position, direction, radius, attenuation.X, attenuation.Y, attenuation.Z, spreadAngle, beamWidthAngle)); - return light; - } - - public static IfcLightIntensityDistribution CreateLightGoniometricDistribution(this IModel model, LightMeasurementData data) - { - // TODO: resolve symmetry modes from light measurment data - // TODO: convert intensities to candela per lumen - - throw new NotImplementedException(); - - //IEnumerable lightData = [ - // // Main plane-angle and its secondary-plane-angles - // new IFCHelper.LightIntensityDistributionData(0, [new IFCHelper.AngleAndIntensity(0.0, 100.0), new IFCHelper.AngleAndIntensity(90.0, 200.0), new IFCHelper.AngleAndIntensity(180.0, 100.0)]), - // new IFCHelper.LightIntensityDistributionData(180, [new IFCHelper.AngleAndIntensity(0.0, 10.0), new IFCHelper.AngleAndIntensity(45.0, 15.0), new IFCHelper.AngleAndIntensity(90.0, 20.0), new IFCHelper.AngleAndIntensity(135.0, 15.0), new IFCHelper.AngleAndIntensity(180.0, 10.0)]) - //]; - - //return model.CreateLightIntensityDistribution(IfcLightDistributionCurveEnum.TYPE_C, lightData); - } - - public static IfcLightFixture CreateLightGoniometric(this IModel model, string name, C3d color, V3d position, double colourTemperature, double luminousFlux, LightMeasurementData data, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) - => CreateLightGoniometric(model, name, color, position, colourTemperature, luminousFlux, CreateLightGoniometricDistribution(model, data), placement, lightShape); - - public static IfcLightFixture CreateLightGoniometric(this IModel model, string name, C3d color, V3d position, double colourTemperature, double luminousFlux, IfcLightIntensityDistribution distribution, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) - { - var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.POINTSOURCE); - light.Representation.Representations.Add(model.CreateShapeRepresentationLightingGoniometric(color, position, colourTemperature, luminousFlux, distribution)); - return light; - } - } - - public static class AardvarkExtensions - { - // Overrides values from Aardvark-Types - public static void Set(this IfcCartesianPoint point, V2d vec) => point.SetXY(vec.X, vec.Y); - - public static void Set(this IfcCartesianPoint point, V3d vec) => point.SetXYZ(vec.X, vec.Y, vec.Z); - - public static void Set(this IfcDirection dir, V3d d) => dir.SetXYZ(d.X, d.Y, d.Z); - - public static void Set(this IfcDirection dir, V2d d) => dir.SetXY(d.X, d.Y); - - public static void Set(this IfcColourRgb c, C3d colour) - { - c.Red = colour.R; - c.Green = colour.G; - c.Blue = colour.B; - } - - public static void Set(this IfcColourRgb c, C3f colour) - { - c.Red = colour.R; - c.Green = colour.G; - c.Blue = colour.B; - } - - public static void Set(this IfcBoundingBox b, Box3d box) - { - b.Corner = b.Model.CreatePoint(box.Min); - b.XDim = box.SizeX; - b.YDim = box.SizeY; - b.ZDim = box.SizeZ; - } - - - // Cast Ifc-Types to Aardvark-Types - public static V3d ToV3d(this IIfcCartesianPoint point) - => new(point.X, point.Y, point.Z); - - public static V3d ToV3d(this IIfcDirection direction) - => new(direction.X, direction.Y, direction.Z); - - public static V3d ToV3d(this IIfcVector vec) - => vec.Orientation.ToV3d() * vec.Magnitude; - - public static C3d ToC3d(this IIfcColourRgb col) - => new(col.Red, col.Green, col.Blue); - - public static C3f ToC3f(this IIfcColourRgb col) - => col.ToC3d().ToC3f(); - - public static Box3d ToBox3d(this IIfcBoundingBox box) - { - var min = box.Corner.ToV3d(); - var max = min + new V3d(box.XDim, box.YDim, box.ZDim); - return new Box3d(min, max); - } - - public static IEnumerable ToV3d(this IIfcCartesianPointList list) - { - return list switch - { - IIfcCartesianPointList2D list2D => list2D.CoordList.Select(v => new V3d(v[0], v[1], 0.0)), - IIfcCartesianPointList3D list3D => list3D.CoordList.Select(v => new V3d(v[0], v[1], v[2])), - _ => [] - }; - } - - public static IEnumerable ToV3d(this IIfcCurve curve) - { - return curve switch - { - IIfcPolyline poly => poly.Points.Select(p => p.ToV3d()), - IIfcIndexedPolyCurve indexed => indexed.Points.ToV3d(), - IIfcCompositeCurve comp => comp.Segments.SelectMany(s => s.ParentCurve.ToV3d()), - _ => [] - }; - } - - public static Trafo3d ToTrafo3d(this IIfcAxis2Placement3D p) - { - var xAxis = p.RefDirection == null ? V3d.XAxis : p.RefDirection.ToV3d(); - var zAxis = p.Axis == null ? V3d.ZAxis : p.Axis.ToV3d(); - var yAxis = zAxis.Cross(xAxis); - return Trafo3d.FromBasis(xAxis, yAxis, zAxis, p.Location.ToV3d()); - } - } -} \ No newline at end of file diff --git a/src/Aardvark.Data.Ifc/IFCParser.cs b/src/Aardvark.Data.Ifc/IFCParser.cs index f2c0f29..b0ba472 100644 --- a/src/Aardvark.Data.Ifc/IFCParser.cs +++ b/src/Aardvark.Data.Ifc/IFCParser.cs @@ -65,7 +65,7 @@ public static IFCData PreprocessIFC(string filePath, XbimEditorCredentials edito var cacheEntity = model.BeginEntityCaching(); (content, materials) = ParseIFC(model); - var hierarchy = IFCHelper.CreateHierarchy(model); + var hierarchy = HierarchyExt.CreateHierarchy(model); return new IFCData(model, cacheInverse, cacheEntity, content, materials, projectScale, hierarchy); } diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/AardvarkExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/AardvarkExt.cs new file mode 100644 index 0000000..77e93bb --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/AardvarkExt.cs @@ -0,0 +1,98 @@ +using Aardvark.Base; +using System.Collections.Generic; +using System.Linq; + +using Xbim.Ifc4.GeometricModelResource; +using Xbim.Ifc4.GeometryResource; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.PresentationAppearanceResource; + +namespace Aardvark.Data.Ifc +{ + public static class AardvarkExt + { + // Overrides values from Aardvark-Types + public static void Set(this IfcCartesianPoint point, V2d vec) => point.SetXY(vec.X, vec.Y); + + public static void Set(this IfcCartesianPoint point, V3d vec) => point.SetXYZ(vec.X, vec.Y, vec.Z); + + public static void Set(this IfcDirection dir, V3d d) => dir.SetXYZ(d.X, d.Y, d.Z); + + public static void Set(this IfcDirection dir, V2d d) => dir.SetXY(d.X, d.Y); + + public static void Set(this IfcColourRgb c, C3d colour) + { + c.Red = colour.R; + c.Green = colour.G; + c.Blue = colour.B; + } + + public static void Set(this IfcColourRgb c, C3f colour) + { + c.Red = colour.R; + c.Green = colour.G; + c.Blue = colour.B; + } + + public static void Set(this IfcBoundingBox b, Box3d box) + { + b.Corner = b.Model.CreatePoint(box.Min); + b.XDim = box.SizeX; + b.YDim = box.SizeY; + b.ZDim = box.SizeZ; + } + + + // Cast Ifc-Types to Aardvark-Types + public static V3d ToV3d(this IIfcCartesianPoint point) + => new(point.X, point.Y, point.Z); + + public static V3d ToV3d(this IIfcDirection direction) + => new(direction.X, direction.Y, direction.Z); + + public static V3d ToV3d(this IIfcVector vec) + => vec.Orientation.ToV3d() * vec.Magnitude; + + public static C3d ToC3d(this IIfcColourRgb col) + => new(col.Red, col.Green, col.Blue); + + public static C3f ToC3f(this IIfcColourRgb col) + => col.ToC3d().ToC3f(); + + public static Box3d ToBox3d(this IIfcBoundingBox box) + { + var min = box.Corner.ToV3d(); + var max = min + new V3d(box.XDim, box.YDim, box.ZDim); + return new Box3d(min, max); + } + + public static IEnumerable ToV3d(this IIfcCartesianPointList list) + { + return list switch + { + IIfcCartesianPointList2D list2D => list2D.CoordList.Select(v => new V3d(v[0], v[1], 0.0)), + IIfcCartesianPointList3D list3D => list3D.CoordList.Select(v => new V3d(v[0], v[1], v[2])), + _ => [] + }; + } + + public static IEnumerable ToV3d(this IIfcCurve curve) + { + return curve switch + { + IIfcPolyline poly => poly.Points.Select(p => p.ToV3d()), + IIfcIndexedPolyCurve indexed => indexed.Points.ToV3d(), + IIfcCompositeCurve comp => comp.Segments.SelectMany(s => s.ParentCurve.ToV3d()), + _ => [] + }; + } + + public static Trafo3d ToTrafo3d(this IIfcAxis2Placement3D p) + { + var xAxis = p.RefDirection == null ? V3d.XAxis : p.RefDirection.ToV3d(); + var zAxis = p.Axis == null ? V3d.ZAxis : p.Axis.ToV3d(); + var yAxis = zAxis.Cross(xAxis); + return Trafo3d.FromBasis(xAxis, yAxis, zAxis, p.Location.ToV3d()); + } + } +} diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/GeneralExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/GeneralExt.cs new file mode 100644 index 0000000..697c0f0 --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/GeneralExt.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Aardvark.Base; + +using Xbim.Common; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.Kernel; +using Xbim.Ifc4.ProductExtension; +using Xbim.Ifc; +using Xbim.Common.ExpressValidation; +using Xbim.Common.Enumerations; + +namespace Aardvark.Data.Ifc +{ + public static class GeneralExt + { + #region Convenient Functions + public static T New(this IModel model, Action func) where T : IInstantiableEntity + { + // Convenient function to directly create entities from model + return model.Instances.New(func); + } + + public static IIfcProject GetProject(this IfcStore model) + => model.Instances.FirstOrDefault(); + + public static IIfcObjectDefinition GetParent(this IIfcObjectDefinition o) + => o.Decomposes.Select(r => r.RelatingObject).FirstOrDefault(); + + public static IEnumerable GetSiblings(this IIfcObjectDefinition o) + => o.Decomposes.SelectMany(r => r.RelatedObjects).Where(x => !o.Equals(x)); + + public static IEnumerable GetChildren(this IIfcObjectDefinition o) + { + var children = o.IsDecomposedBy.SelectMany(r => r.RelatedObjects); + + if ((o as IIfcSpatialStructureElement) != null) + { + children = children.Concat(((IIfcSpatialStructureElement)o).ContainsElements.SelectMany(rel => rel.RelatedElements).Cast()); + } + + return children; + } + + #endregion + + #region Scene + public static IEnumerable ValidateModel(this IfcStore model) + { + var validator = new Validator() + { + CreateEntityHierarchy = true, + ValidateLevel = ValidationFlags.All + }; + + var result = validator.Validate(model.Instances); + + result.ForEach(error => + { + Report.Line(error.Message + " with " + error.Details.Count()); + error.Details.ForEach(detail => Report.Line(detail.IssueSource + " " + detail.IssueType)); + }); + + return result; + } + + public static void CreateMinimalProject(this IfcStore model, ProjectUnits units = ProjectUnits.SIUnitsUK, string projectName = "Project", string siteName = "Site") + { + using var txnInit = model.BeginTransaction("Init Project"); + // there should always be one project in the model + var project = model.New(p => p.Name = projectName); + // our shortcut to define basic default units + project.Initialize(units); + + // add site + var site = model.New(w => w.Name = siteName); + project.AddSite(site); + + txnInit.Commit(); + } + #endregion + } +} \ No newline at end of file diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/GeometricRepresentationItemExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/GeometricRepresentationItemExt.cs new file mode 100644 index 0000000..86016bb --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/GeometricRepresentationItemExt.cs @@ -0,0 +1,401 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Aardvark.Base; + +using Xbim.Common; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.GeometricModelResource; +using Xbim.Ifc4.GeometryResource; +using Xbim.Ifc4.MeasureResource; +using Xbim.Ifc4.PresentationOrganizationResource; +using Xbim.Ifc4.RepresentationResource; +using Xbim.Ifc4.GeometricConstraintResource; + +namespace Aardvark.Data.Ifc +{ + public static class GeometricRepresentationItemExt + { + public static IfcGeometricRepresentationContext GetGeometricRepresentationContextPlan(this IModel model) + => model.Instances.OfType().Where(c => c.ContextType == "Plan").First(); + + public static IfcGeometricRepresentationContext GetGeometricRepresentationContextModel(this IModel model) + => model.Instances.OfType().Where(c => c.ContextType == "Model").First(); + + #region Placement + public static IfcCartesianPoint CreatePoint(this IModel model, V2d point) + => model.New(c => c.Set(point)); + + public static IfcCartesianPoint CreatePoint(this IModel model, V3d point) + => model.New(c => c.Set(point)); + + public static IfcDirection CreateDirection(this IModel model, V2d direction) + => model.New(rd => rd.Set(direction)); // NOTE: Direction may be normalized! + + public static IfcDirection CreateDirection(this IModel model, V3d direction) + => model.New(rd => rd.Set(direction)); // NOTE: Direction may be normalized! + + public static IfcVector CreateVector(this IModel model, V3d vector) + { + return model.New(v => + { + v.Magnitude = vector.Length; + v.Orientation = model.CreateDirection(vector.Normalized); + }); + } + + public static IfcVector CreateVector(this IModel model, V2d vector) + { + return model.New(v => + { + v.Magnitude = vector.Length; + v.Orientation = model.CreateDirection(vector.Normalized); + }); + } + + public static IfcAxis2Placement3D CreateAxis2Placement3D(this IModel model, V3d location, V3d refDir, V3d axis) + { + return model.New(a => + { + a.Location = model.CreatePoint(location); + a.RefDirection = model.CreateDirection(refDir); // default x-axis + a.Axis = model.CreateDirection(axis); // default z-axis + }); + } + + public static IIfcAxis2Placement3D CreateAxis2Placement3D(this IModel model, Trafo3d trafo) + => model.CreateAxis2Placement3D(trafo.Forward.C3.XYZ, trafo.Forward.C0.XYZ, trafo.Forward.C2.XYZ); + + public static IfcAxis2Placement3D CreateAxis2Placement3D(this IModel model, V3d location) + => model.CreateAxis2Placement3D(location, V3d.XAxis, V3d.ZAxis); + + public static IfcAxis2Placement2D CreateAxis2Placement2D(this IModel model, V2d location, V2d refDir) + { + return model.New(a => + { + a.Location = model.CreatePoint(location); + a.RefDirection = model.CreateDirection(refDir); + }); + } + public static IfcAxis2Placement2D CreateAxis2Placement2D(this IModel model, V2d location) + => model.CreateAxis2Placement2D(location, V2d.XAxis); + + public static IfcLocalPlacement CreateLocalPlacement(this IModel model, V3d shift) + => model.New(p => p.RelativePlacement = model.CreateAxis2Placement3D(shift)); + #endregion + + #region Lines + + public static IfcCartesianPointList2D CreateCartesianPointList2D(this IModel model, params V2d[] points) + { + return model.New(pl => + { + for (int i = 0; i < points.Length; i++) + { + pl.CoordList.GetAt(i).AddRange(points[i].ToArray().Select(v => new IfcLengthMeasure(v))); + } + }); + } + + public static IfcCartesianPointList3D CreateCartesianPointList3D(this IModel model, params V3d[] points) + { + return model.New(pl => + { + for (int i = 0; i < points.Length; i++) + { + pl.CoordList.GetAt(i).AddRange(points[i].ToArray().Select(v => new IfcLengthMeasure(v))); + } + }); + } + + public static IfcLine CreateLine(this IModel model, V2d start, V2d end) + { + var diff = end - start; + + return model.New(line => + { + line.Pnt = model.CreatePoint(start); + line.Dir = model.CreateVector(diff); + }); + } + + public static IfcLine CreateLine(this IModel model, V3d start, V3d end) + { + var diff = end - start; + + return model.New(line => + { + line.Pnt = model.CreatePoint(start); + line.Dir = model.CreateVector(diff); + }); + } + + public static IfcLine CreateLine(this IModel model, Line2d line) + => model.CreateLine(line.P0, line.P1); + + public static IfcLine CreateLine(this IModel model, Line3d line) + => model.CreateLine(line.P0, line.P1); + + public static IfcPolyline CreatePolyLine(this IModel model, params V2d[] points) + { + return model.New(line => + { + line.Points.AddRange(points.Select(x => model.CreatePoint(x))); + }); + } + + public static IfcPolyline CreatePolyLine(this IModel model, params V3d[] points) + { + return model.New(line => + { + line.Points.AddRange(points.Select(x => model.CreatePoint(x))); + }); + } + + public static IfcPolyline CreatePolyLine(this IModel model, IEnumerable points) + => model.CreatePolyLine(points.ToArray()); + + public static IfcPolyline CreatePolyLine(this IModel model, IEnumerable points) + => model.CreatePolyLine(points.ToArray()); + + public static IfcIndexedPolyCurve CreateIndexedPolyCurve(this IModel model, IEnumerable points, IEnumerable indices = null) + { + // NOTE: Indices start with 1! + return model.New(poly => + { + poly.Points = model.CreateCartesianPointList2D(points.ToArray()); + + if (indices != null) + { + var index = indices.Select(i => + { + if (i.Length == 3) return (IfcSegmentIndexSelect)new IfcArcIndex(i.Select(x => new IfcPositiveInteger(x)).ToList()); + else if (i.Length == 2) return (IfcSegmentIndexSelect)new IfcLineIndex(i.Select(x => new IfcPositiveInteger(x)).ToList()); + else return null; + }); + poly.Segments.AddRange(index); + } + }); + } + + #endregion + + #region Surfaces + + public static IfcPlane CreatePlane(this IModel model, Plane3d plane) + { + var refDir = (plane.Normal.MajorDim == 2) ? plane.Normal.ZXY : plane.Normal.YXZ; + return model.New(pl => pl.Position = model.CreateAxis2Placement3D(plane.Point, refDir, plane.Normal)); + } + + public static IfcCurveBoundedPlane CreateCurveBoundedPlane(this IModel model, Plane3d plane, Polygon2d poly) + { + return model.New(p => + { + p.BasisSurface = model.CreatePlane(plane); + p.OuterBoundary = model.CreatePolyLine(poly.Points); + }); + } + + #endregion + + #region Lights + + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/link/lighting-geometry.htm + + public static IfcLightSourceAmbient CreateLightSourceAmbient(this IModel model, C3d color, string name = null, double? intensity = null, double? ambientIntensity = null) + { + return model.New(ls => + { + ls.LightColour = model.CreateColor(color); + + if (name != null) ls.Name = name; + + // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). + // The intensity field specifies the brightness of the direct emission from the ligth. + if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); + // The ambientIntensity specifies the intensity of the ambient emission from the light. + if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; + }); + } + + public static IfcLightSourceDirectional CreateLightSourceDirectional(this IModel model, C3d color, V3d direction, string name = null, double? intensity = null, double? ambientIntensity = null) + { + return model.New(ls => + { + ls.LightColour = model.CreateColor(color); + + if (name != null) ls.Name = name; + + // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). + // The intensity field specifies the brightness of the direct emission from the ligth. + if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); + // The ambientIntensity specifies the intensity of the ambient emission from the light. + if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; + + // Directional properties + ls.Orientation = model.CreateDirection(direction); + }); + } + + public static IfcLightSourcePositional CreateLightSourcePositional(this IModel model, C3d color, V3d position, double radius, double constantAttenuation, double distanceAttenuation, double quadricAttenuation, string name = null, double? intensity = null, double? ambientIntensity = null) + { + // The Point light node specifies a point light source at a 3D location in the local coordinate system. + // A point light source emits light equally in all directions; that is, it is omnidirectional. + + return model.New(ls => + { + ls.LightColour = model.CreateColor(color); + + if (name != null) ls.Name = name; + + // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). + // The intensity field specifies the brightness of the direct emission from the ligth. + if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); + // The ambientIntensity specifies the intensity of the ambient emission from the light. + if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; + + // Definition from ISO/CD 10303-46:1992: The Cartesian point indicates the position of the light source. Definition from VRML97 - ISO/IEC 14772-1:1997: A Point light node illuminates geometry within radius of its location. + ls.Position = ls.Model.CreatePoint(position); + + // The maximum distance from the light source for a surface still to be illuminated. Definition from VRML97 - ISO/IEC 14772-1:1997: A Point light node illuminates geometry within radius of its location. + ls.Radius = new IfcPositiveLengthMeasure(radius); + + // Definition from ISO/CD 10303-46:1992: This real indicates the value of the attenuation in the lighting equation that is constant. + ls.ConstantAttenuation = constantAttenuation; + + // Definition from ISO/CD 10303-46:1992: This real indicates the value of the attenuation in the lighting equation that proportional to the distance from the light source. + ls.DistanceAttenuation = distanceAttenuation; + + // This real indicates the value of the attenuation in the lighting equation that proportional to the square value of the distance from the light source. + ls.QuadricAttenuation = quadricAttenuation; + }); + } + + public static IfcLightSourceSpot CreateLightSourceSpot(this IModel model, C3d color, V3d position, double radius, double constantAttenuation, double distanceAttenuation, double quadricAttenuation, V3d direction, double spreadAngle, double beamWidthAngle, string name = null, double? intensity = null, double? ambientIntensity = null, double? concentrationExponent = null) + { + // The Point light node specifies a point light source at a 3D location in the local coordinate system. + // A point light source emits light equally in all directions; that is, it is omnidirectional. + + return model.New(ls => + { + ls.LightColour = model.CreateColor(color); + + if (name != null) ls.Name = name; + + // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). + // The intensity field specifies the brightness of the direct emission from the ligth. + if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); + // The ambientIntensity specifies the intensity of the ambient emission from the light. + if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; + + // Definition from ISO/CD 10303-46:1992: The Cartesian point indicates the position of the light source. Definition from VRML97 - ISO/IEC 14772-1:1997: A Point light node illuminates geometry within radius of its location. + ls.Position = ls.Model.CreatePoint(position); + + // The maximum distance from the light source for a surface still to be illuminated. Definition from VRML97 - ISO/IEC 14772-1:1997: A Point light node illuminates geometry within radius of its location. + ls.Radius = new IfcPositiveLengthMeasure(radius); + + // Definition from ISO/CD 10303-46:1992: This real indicates the value of the attenuation in the lighting equation that is constant. + ls.ConstantAttenuation = constantAttenuation; + + // Definition from ISO/CD 10303-46:1992: This real indicates the value of the attenuation in the lighting equation that proportional to the distance from the light source. + ls.DistanceAttenuation = distanceAttenuation; + + // This real indicates the value of the attenuation in the lighting equation that proportional to the square value of the distance from the light source. + ls.QuadricAttenuation = quadricAttenuation; + + //Definition from ISO / CD 10303 - 46:1992: This is the direction of the axis of the cone of the light source specified in the coordinate space of the representation being projected..Definition from VRML97 -ISO / IEC 14772 - 1:1997: The direction field specifies the direction vector of the light's central axis defined in the local coordinate system. X + ls.Orientation = ls.Model.CreateDirection(direction); + + // Definition from ISO / CD 10303 - 46:1992: This real is the exponent on the cosine of the angle between the line that starts at the position of the spot light source and is in the direction of the orientation of the spot light source and a line that starts at the position of the spot light source and goes through a point on the surface being shaded.NOTE This attribute does not exists in ISO / IEC 14772 - 1:1997.X + if (concentrationExponent.HasValue) ls.ConcentrationExponent = concentrationExponent.Value; + // Definition from ISO / CD 10303 - 46:1992: This planar angle measure is the angle between the line that starts at the position of the spot light source and is in the direction of the spot light source and any line on the boundary of the cone of influence. Definition from VRML97 - ISO / IEC 14772 - 1:1997: The cutOffAngle(name of spread angle in VRML) field specifies the outer bound of the solid angle.The light source does not emit light outside of this solid angle. X + ls.SpreadAngle = new IfcPositivePlaneAngleMeasure(spreadAngle); + // Definition from VRML97 - ISO / IEC 14772 - 1:1997: The beamWidth field specifies an inner solid angle in which the light source emits light at uniform full intensity. The light source's emission intensity drops off from the inner solid angle (beamWidthAngle) to the outer solid angle (spreadAngle). X + ls.BeamWidthAngle = new IfcPositivePlaneAngleMeasure(beamWidthAngle); + }); + } + + public readonly struct AngleAndIntensity + { + public double AnglesInDegree { get; } + public double IntentsityInCandelaPerLumen { get; } + + public AngleAndIntensity(double anglesInDegree, double intentsityInCandelaPerLumen) + { + AnglesInDegree = anglesInDegree; + IntentsityInCandelaPerLumen = intentsityInCandelaPerLumen; + } + } + + public readonly struct LightIntensityDistributionData + { + public double MainAngleInDegree { get; } + public AngleAndIntensity[] SecondaryAnglesAndIntensities { get; } + + public LightIntensityDistributionData(double angleInDegree, AngleAndIntensity[] data) + { + MainAngleInDegree = angleInDegree; + SecondaryAnglesAndIntensities = data; + } + } + + public static IfcLightIntensityDistribution CreateLightIntensityDistribution(this IModel model, IfcLightDistributionCurveEnum distributionEnum, IEnumerable data) + { + return model.New(d => + { + // Type C is the recommended standard system. The C-Plane system equals a globe with a vertical axis. C-Angles are valid from 0° to 360°, γ-Angles are valid from 0° (south pole) to 180° (north pole). + // Type B is sometimes used for floodlights.The B-Plane System has a horizontal axis.B - Angles are valid from - 180° to + 180° with B 0° at the bottom and B180°/ B - 180° at the top, β - Angles are valid from - 90° to + 90°. + // Type A is basically not used.For completeness the Type A Photometry equals the Type B rotated 90° around the Z - Axis counter clockwise. + d.LightDistributionCurve = distributionEnum; + d.DistributionData.AddRange(data.Select(a => + { + return model.New(data => + { + // The main plane angle (A, B or C angles, according to the light distribution curve chosen). + data.MainPlaneAngle = new IfcPlaneAngleMeasure(a.MainAngleInDegree.RadiansFromDegrees()); // measured in radians + + // The list of secondary plane angles (the α, β or γ angles) according to the light distribution curve chosen. + // NOTE: The SecondaryPlaneAngle and LuminousIntensity lists are corresponding lists. + data.SecondaryPlaneAngle.AddRange(a.SecondaryAnglesAndIntensities.Select(sa => new IfcPlaneAngleMeasure(sa.AnglesInDegree.RadiansFromDegrees()))); + + // The luminous intensity distribution measure for this pair of main and secondary plane angles according to the light distribution curve chosen. + data.LuminousIntensity.AddRange(a.SecondaryAnglesAndIntensities.Select(m => new IfcLuminousIntensityDistributionMeasure(m.IntentsityInCandelaPerLumen))); // measured in Candela/Lumen (cd/lm) or (cd/klm). + }); + })); + }); + } + + public static IfcLightSourceGoniometric CreateLightSourceGoniometric(this IModel model, C3d color, double colourTemperature, double luminousFlux, + IfcLightEmissionSourceEnum lightEmissionSource, IfcLightIntensityDistribution data, IfcAxis2Placement3D placement, C3d? appearance = null, string name = null, double? intensity = null, double? ambientIntensity = null) + { + return model.New(ls => + { + + ls.LightColour = model.CreateColor(color); + + if (name != null) ls.Name = name; + + // Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity). + // The intensity field specifies the brightness of the direct emission from the ligth. + if (intensity.HasValue) ls.Intensity = new IfcNormalisedRatioMeasure(intensity.Value); + // The ambientIntensity specifies the intensity of the ambient emission from the light. + if (ambientIntensity.HasValue) ls.AmbientIntensity = ambientIntensity.Value; + + // Goniometric Properties + ls.Position = placement; + if (appearance.HasValue) ls.ColourAppearance = model.CreateColor(appearance.Value); + ls.ColourTemperature = new IfcThermodynamicTemperatureMeasure(colourTemperature); + ls.LuminousFlux = new IfcLuminousFluxMeasure(luminousFlux); + + ls.LightEmissionSource = lightEmissionSource; + + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/link/lighting-geometry.htm + ls.LightDistributionDataSource = data; + + }); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/GridExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/GridExt.cs new file mode 100644 index 0000000..99d470c --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/GridExt.cs @@ -0,0 +1,46 @@ +using Aardvark.Base; + +using System.Linq; + +using Xbim.Common; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.GeometricConstraintResource; +using Xbim.Ifc4.ProductExtension; + +namespace Aardvark.Data.Ifc +{ + public static class GridExt + { + #region Grid + public static IfcGridAxis CreateGridAxis(this IModel model, string name, V2d start, V2d end) + { + return model.New(a => + { + a.AxisTag = name; + a.AxisCurve = model.CreatePolyLine(start, end); + a.SameSense = true; + }); + } + + public static IfcGrid CreateGrid(this IModel model, string name, string[] xAxis, string[] vAxes, double offset) + { + // Create axis + var xAxisEntities = xAxis.Select((a, i) => model.CreateGridAxis(a, new V2d(-offset / 2.0, offset * i), new V2d(offset * vAxes.Length, offset * i))); + var yAxisEntities = vAxes.Select((a, i) => model.CreateGridAxis(a, new V2d(offset * i, -offset / 2.0), new V2d(offset * i, offset * xAxis.Length))); + + // Create regular grid + var grid = model.New(g => + { + g.Name = name; + g.UAxes.AddRange(xAxisEntities); + g.VAxes.AddRange(yAxisEntities); + g.PredefinedType = IfcGridTypeEnum.RECTANGULAR; + g.ObjectPlacement = model.CreateLocalPlacement(V3d.Zero); + }); + + return grid; + } + + #endregion + } +} diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/GroupExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/GroupExt.cs new file mode 100644 index 0000000..c23e2bb --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/GroupExt.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +using Xbim.Common; +using Xbim.Ifc; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.Kernel; + + +namespace Aardvark.Data.Ifc +{ + public static class GroupExt + { + + public static IfcGroup CreateGroup(this IModel model, string groupName) + => model.New(g => g.Name = groupName); + + public static IfcGroup CreateGroup(this IModel model, string groupName, IEnumerable relatedObjects, IfcObjectTypeEnum groupType = IfcObjectTypeEnum.PRODUCT) + { + var group = model.CreateGroup(groupName); + + // Link related objects to group via IfcRelAssignsToGroup + model.New(rel => { + rel.RelatingGroup = group; + rel.RelatedObjects.AddRange(relatedObjects); + rel.RelatedObjectsType = groupType; + }); + + return group; + } + + public static IfcGroup CreateGroup(this IModel model, string groupName, IEnumerable relatedObjects) + => model.CreateGroup(groupName, relatedObjects, IfcObjectTypeEnum.GROUP); + } +} \ No newline at end of file diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/HierarchyExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/HierarchyExt.cs new file mode 100644 index 0000000..953fcd4 --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/HierarchyExt.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.Linq; +using Aardvark.Base; + +using Xbim.Common; +using Xbim.Ifc; +using Xbim.Ifc4.Interfaces; + +namespace Aardvark.Data.Ifc +{ + public static class HierarchyExt + { + public static IFCNode CreateHierarchy(IfcStore model) + => CreateHierarchy(model.GetProject()); + + private static IFCNode CreateHierarchy(IIfcObjectDefinition obj) + => new IFCNode(obj, obj.GetChildren().Select(x => (IIFCNode)CreateHierarchy(x)).ToList()); + + public static void PrintHierarchy(string file) + { + using var model = IfcStore.Open(file); + var project = model.Instances.FirstOrDefault(); + Report.Line("HIRARCHY of file: {0}\n", file); + PrintHierarchy(project, 0); + } + + public static void PrintHierarchy(IIfcObjectDefinition o, int level) + { + Report.Line(string.Format("{0}{1} [{2}]", GetIndent(level), o.Name, o.GetType().Name)); + + var parent = o.GetParent(); + if (parent != null) Report.Line("parent: " + parent.ToString()); + + var children = GeneralExt.GetChildren(o); + Report.Line("children count: {0}", children.Count()); + children.ForEach(element => Report.Line(string.Format("{0} ->{1} [{2}]", GetIndent(level), element.Name, element.GetType().Name))); + + var siblings = o.GetSiblings(); + Report.Line("sibling count: {0}", siblings.Count()); + siblings.ForEach(s => Report.Line("siblings: " + s.ToString())); + + Report.Line(); + + ////only spatial elements can contain building elements + //var spatialElement = o as IIfcSpatialStructureElement; + //if (spatialElement != null) + //{ + // //using IfcRelContainedInSpatialElement to get contained elements + // var containedElements = spatialElement.ContainsElements.SelectMany(rel => rel.RelatedElements); + // foreach (var element in containedElements) + // Console.WriteLine(string.Format("{0} ->{1} [{2}]", GetIndent(level), element.Name, element.GetType().Name)); + //} + + //using IfcRelAggregares to get spatial decomposition of spatial structure elements + foreach (var item in o.IsDecomposedBy.SelectMany(r => r.RelatedObjects)) + PrintHierarchy(item, level + 1); + } + + private static string GetIndent(int level) + { + var indent = ""; + for (int i = 0; i < level; i++) + indent += " "; + return indent; + } + } +} diff --git a/src/Aardvark.Data.Ifc/Xbim.Extensions/Ifc4x3Extensions.cs b/src/Aardvark.Data.Ifc/XbimExtensions/Ifc4x3Ext.cs similarity index 98% rename from src/Aardvark.Data.Ifc/Xbim.Extensions/Ifc4x3Extensions.cs rename to src/Aardvark.Data.Ifc/XbimExtensions/Ifc4x3Ext.cs index dcbdab6..b975ef6 100644 --- a/src/Aardvark.Data.Ifc/Xbim.Extensions/Ifc4x3Extensions.cs +++ b/src/Aardvark.Data.Ifc/XbimExtensions/Ifc4x3Ext.cs @@ -7,7 +7,7 @@ namespace Aardvark.Data.Ifc { - public static class Ifc4x3Extensions + public static class Ifc4x3Ext { public static IfcRelDefinesByType AddDefiningType(this IfcObject obj, IfcTypeObject theType) { diff --git a/src/Aardvark.Data.Ifc/Xbim.Extensions/InterfaceExtensions.cs b/src/Aardvark.Data.Ifc/XbimExtensions/InterfaceExt.cs similarity index 98% rename from src/Aardvark.Data.Ifc/Xbim.Extensions/InterfaceExtensions.cs rename to src/Aardvark.Data.Ifc/XbimExtensions/InterfaceExt.cs index 998ee4f..925e4e8 100644 --- a/src/Aardvark.Data.Ifc/Xbim.Extensions/InterfaceExtensions.cs +++ b/src/Aardvark.Data.Ifc/XbimExtensions/InterfaceExt.cs @@ -3,7 +3,7 @@ namespace Aardvark.Data.Ifc { - public static class InterfaceExtensions + public static class InterfaceExt { public static IIfcRelDefinesByType AddDefiningType(this IIfcObject obj, IIfcTypeObject theType) { diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/LayerExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/LayerExt.cs new file mode 100644 index 0000000..ae3531f --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/LayerExt.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using Aardvark.Base; + +using Xbim.Common; +using Xbim.Ifc; +using Xbim.Ifc.Extensions; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.GeometryResource; +using Xbim.Ifc4.PresentationAppearanceResource; +using Xbim.Ifc4.PresentationOrganizationResource; +using Xbim.Ifc4.RepresentationResource; + +namespace Aardvark.Data.Ifc +{ + public static class LayerExt + { + public static IfcPresentationLayerAssignment CreateLayer(this IModel model, string layerName, IEnumerable items = null) + { + // IfcPresentationLayerAssignment only allows: IFC4.IFCSHAPEREPRESENTATION", "IFC4.IFCGEOMETRICREPRESENTATIONITEM", "IFC4.IFCMAPPEDITEM + return model.New(layer => { + layer.Name = layerName; + if (!items.IsEmptyOrNull()) layer.AssignedItems.AddRange(items); + }); + } + + public static IfcPresentationLayerAssignment CreateLayer(this IModel model, string layerName, IfcLayeredItem items) + => model.CreateLayer(layerName, [items]); + + public static IfcPresentationLayerWithStyle CreateLayerWithStyle(this IModel model, string layerName, IEnumerable styles, bool layerVisibility = true, IEnumerable items = null) + { + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationorganizationresource/lexical/ifcpresentationlayerwithstyle.htm + // IfcPresentationLayerWithStyle only allows: "IFC4.IFCGEOMETRICREPRESENTATIONITEM", "IFC4.IFCMAPPEDITEM" + + return model.New(layer => { + layer.Name = layerName; + + // Visibility Control + layer.LayerOn = layerVisibility; // visibility control allows to define a layer to be either 'on' or 'off', and/or 'frozen' or 'not frozen' + //layer.LayerFrozen = true; + + // Access control + //layer.LayerBlocked = true; // access control allows to block graphical entities from manipulations + + // NOTE: ORDER seems to be important! BIM-Viewer tend to use only color information of first item! + layer.LayerStyles.AddRange(styles); + if (items != null && !items.IsEmpty()) layer.AssignedItems.AddRange(items); + }); + } + + public static IfcPresentationLayerWithStyle CreateLayerWithStyle(this IModel model, string layerName, IEnumerable styles, IfcGeometricRepresentationItem item, bool layerVisibility = true) + => model.CreateLayerWithStyle(layerName, styles, layerVisibility, [item]); + + public static IfcLayeredItem AssignLayer(this IfcLayeredItem item, IfcPresentationLayerAssignment layer) + { + if (layer is IfcPresentationLayerWithStyle && item is IfcShapeRepresentation) + { + // IfcPresentationLayerWithStyle only allows "IFC4.IFCGEOMETRICREPRESENTATIONITEM", "IFC4.IFCMAPPEDITEM" + throw new ArgumentException("IfcShapeRepresentation cannot be assigened to IfcPresentationLayerWithStyle"); + } + // IfcPresentationLayerAssignment only allows: IFC4.IFCSHAPEREPRESENTATION", "IFC4.IFCGEOMETRICREPRESENTATIONITEM", "IFC4.IFCMAPPEDITEM + if (!layer.AssignedItems.Contains(item)) layer.AssignedItems.Add(item); + return item; + } + + public static IfcGeometricRepresentationItem AssignLayer(this IfcGeometricRepresentationItem item, IfcPresentationLayerAssignment layer) + { + if (!layer.AssignedItems.Contains(item)) layer.AssignedItems.Add(item); + return item; + } + + public static IfcMappedItem AssignLayer(this IfcMappedItem item, IfcPresentationLayerAssignment layer) + { + if (!layer.AssignedItems.Contains(item)) layer.AssignedItems.Add(item); + return item; + } + } +} diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/LightExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/LightExt.cs new file mode 100644 index 0000000..eec1f05 --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/LightExt.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using Aardvark.Base; +using Aardvark.Data.Photometry; + +using Xbim.Common; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.ElectricalDomain; +using Xbim.Ifc4.GeometricConstraintResource; +using Xbim.Ifc4.GeometryResource; +using Xbim.Ifc4.Kernel; +using Xbim.Ifc4.MeasureResource; +using Xbim.Ifc4.PresentationOrganizationResource; +using Xbim.Ifc4.RepresentationResource; + +namespace Aardvark.Data.Ifc +{ + public static class IfcLightingExtensions + { + public static IfcLightFixtureType CreateLightType(this IModel model, IfcLightFixtureTypeEnum lightType, IEnumerable repMaps, IEnumerable properties, string name = "") + { + var proxy = ProductExt.CreateTypeProduct(model, repMaps, properties, name); + proxy.PredefinedType = lightType; + return proxy; + } + + public static IfcLightFixture Instantiate(this IfcLightFixtureType lightType, string name, IfcObjectPlacement placement, Trafo3d trafo, IfcLightFixtureTypeEnum? ltenum = null) + { + var instance = ProductExt.Instantiate(lightType, name, placement, trafo); + if (lightType.PredefinedType != IfcLightFixtureTypeEnum.NOTDEFINED && ltenum.HasValue) instance.PredefinedType = ltenum; + return instance; + } + + public static IfcLightFixture Instantiate(this IfcLightFixtureType lightType, string name, IfcObjectPlacement placement, Dictionary trafos, IfcLightFixtureTypeEnum? ltenum = null) + { + var instance = ProductExt.Instantiate(lightType, name, placement, trafos); + if (lightType.PredefinedType != IfcLightFixtureTypeEnum.NOTDEFINED && ltenum.HasValue) instance.PredefinedType = ltenum; + return instance; + } + + public static IfcPropertySet Pset_LightFixtureTypeCommon(this IfcLightFixture light, string reference, int numberOfSources, double totalWattage, + double maintenanceFactor, double maximumPlenumSensibleLoad, double maximumSpaceSensibleLoad, double sensibleLoadToRadiant, + string status, string lightFixtureMountingType, string lightFixturePlacingType) + { + var propertySet = light.Model.New(pset => { + pset.Name = "Pset_LightFixtureTypeCommon"; + pset.HasProperties.Add(light.Model.CreatePropertySingleValue("Reference", new IfcIdentifier(reference))); + pset.HasProperties.Add(light.Model.CreatePropertySingleValue("NumberOfSources", new IfcInteger(numberOfSources))); + pset.HasProperties.Add(light.Model.CreatePropertySingleValue("TotalWattage", new IfcPowerMeasure(totalWattage))); + pset.HasProperties.Add(light.Model.CreatePropertySingleValue("MaintenanceFactor", new IfcReal(maintenanceFactor))); + pset.HasProperties.Add(light.Model.CreatePropertySingleValue("MaximumPlenumSensibleLoad", new IfcPowerMeasure(maximumPlenumSensibleLoad))); + pset.HasProperties.Add(light.Model.CreatePropertySingleValue("MaximumSpaceSensibleLoad", new IfcPowerMeasure(maximumSpaceSensibleLoad))); + pset.HasProperties.Add(light.Model.CreatePropertySingleValue("SensibleLoadToRadiant", new IfcPositiveRatioMeasure(sensibleLoadToRadiant))); + pset.HasProperties.Add(light.Model.CreatePropertyEnumeratedValue("Status", new IfcLabel(status))); + pset.HasProperties.Add(light.Model.CreatePropertyEnumeratedValue("LightFixtureMountingType", new IfcLabel(lightFixtureMountingType))); + pset.HasProperties.Add(light.Model.CreatePropertyEnumeratedValue("LightFixturePlacingType", new IfcLabel(lightFixturePlacingType))); + }); + + light.AddPropertySet(propertySet); + return propertySet; + } + + public static IIfcElementQuantity Qto_LightFixtureBaseQuantities(this IfcLightFixture light, double weight) + { + return light.AddQuantity("Qto_LightFixtureBaseQuantities", light.Model.CreateQuantityWeight("GrossWeight", weight)); + } + + public static IfcLightFixture CreateLightEmpty(this IModel model, string name, IfcObjectPlacement placement, IfcShapeRepresentation lightShape, IfcLightFixtureTypeEnum? lightType = null) + { + return model.New(t => + { + if (lightType != null) t.PredefinedType = lightType.Value; + t.Name = name; + t.ObjectPlacement = placement; + t.Representation = model.New(r => r.Representations.Add(lightShape)); + }); + } + + public static IfcLightFixture CreateLightAmbient(this IModel model, string name, C3d color, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) + { + var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.POINTSOURCE); + light.Representation.Representations.Add(model.CreateShapeRepresentationLightingAmbient(color)); + return light; + } + + public static IfcLightFixture CreateLightPositional(this IModel model, string name, C3d color, V3d position, double radius, V3d attenuation, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) + { + var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.POINTSOURCE); + light.Representation.Representations.Add(model.CreateShapeRepresentationLightingPositional(color, position, radius, attenuation.X, attenuation.Y, attenuation.Z)); + return light; + } + + public static IfcLightFixture CreateLightDirectional(this IModel model, string name, C3d color, V3d direction, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) + { + var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.DIRECTIONSOURCE); + light.Representation.Representations.Add(model.CreateShapeRepresentationLightingDirectional(color, direction)); + return light; + } + + public static IfcLightFixture CreateLightSpot(this IModel model, string name, C3d color, V3d position, V3d direction, double radius, V3d attenuation, double spreadAngle, double beamWidthAngle, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) + { + var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.DIRECTIONSOURCE); + light.Representation.Representations.Add(model.CreateShapeRepresentationLightingSpot(color, position, direction, radius, attenuation.X, attenuation.Y, attenuation.Z, spreadAngle, beamWidthAngle)); + return light; + } + + public static IfcLightIntensityDistribution CreateLightGoniometricDistribution(this IModel model, LightMeasurementData data) + { + // TODO: resolve symmetry modes from light measurment data + // TODO: convert intensities to candela per lumen + + throw new NotImplementedException(); + + //IEnumerable lightData = [ + // // Main plane-angle and its secondary-plane-angles + // new IFCHelper.LightIntensityDistributionData(0, [new IFCHelper.AngleAndIntensity(0.0, 100.0), new IFCHelper.AngleAndIntensity(90.0, 200.0), new IFCHelper.AngleAndIntensity(180.0, 100.0)]), + // new IFCHelper.LightIntensityDistributionData(180, [new IFCHelper.AngleAndIntensity(0.0, 10.0), new IFCHelper.AngleAndIntensity(45.0, 15.0), new IFCHelper.AngleAndIntensity(90.0, 20.0), new IFCHelper.AngleAndIntensity(135.0, 15.0), new IFCHelper.AngleAndIntensity(180.0, 10.0)]) + //]; + + //return model.CreateLightIntensityDistribution(IfcLightDistributionCurveEnum.TYPE_C, lightData); + } + + public static IfcLightFixture CreateLightGoniometric(this IModel model, string name, C3d color, V3d position, double colourTemperature, double luminousFlux, LightMeasurementData data, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) + => CreateLightGoniometric(model, name, color, position, colourTemperature, luminousFlux, CreateLightGoniometricDistribution(model, data), placement, lightShape); + + public static IfcLightFixture CreateLightGoniometric(this IModel model, string name, C3d color, V3d position, double colourTemperature, double luminousFlux, IfcLightIntensityDistribution distribution, IfcObjectPlacement placement, IfcShapeRepresentation lightShape) + { + var light = CreateLightEmpty(model, name, placement, lightShape, IfcLightFixtureTypeEnum.POINTSOURCE); + light.Representation.Representations.Add(model.CreateShapeRepresentationLightingGoniometric(color, position, colourTemperature, luminousFlux, distribution)); + return light; + } + } + +} diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/MaterialExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/MaterialExt.cs new file mode 100644 index 0000000..b1545d5 --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/MaterialExt.cs @@ -0,0 +1,64 @@ +using Aardvark.Base; +using System.Collections.Generic; +using System.Linq; + +using Xbim.Ifc4.MaterialResource; +using Xbim.Ifc4.MeasureResource; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.PresentationAppearanceResource; +using Xbim.Ifc4.RepresentationResource; + +namespace Aardvark.Data.Ifc +{ + public static class MaterialExt + { + public static IEnumerable GetProperties(this IIfcMaterial mat) + { + return mat.HasProperties + .SelectMany(mp => mp.Properties) + .OfType(); + } + + public static Dictionary GetPropertiesDict(this IIfcMaterial mat) => PropertiesExt.DistinctDictionaryFromPropertiesValues(mat.GetProperties()); + + public static IfcMaterialProperties CreateAttachPsetMaterialCommon(this IfcMaterial material, double molecularWeight, double porosity, double massDensity) + { + return material.Model.New(ps => { + ps.Name = "Pset_MaterialCommon"; + ps.Material = material; + ps.Properties.Add(material.Model.CreatePropertySingleValue("MolecularWeight", new IfcMolecularWeightMeasure(molecularWeight))); + ps.Properties.Add(material.Model.CreatePropertySingleValue("Porosity", new IfcNormalisedRatioMeasure(porosity))); + ps.Properties.Add(material.Model.CreatePropertySingleValue("MassDensity", new IfcMassDensityMeasure(massDensity))); + }); + } + + public static IfcMaterialProperties CreateAttachPsetMaterialThermal(this IfcMaterial material, double thermalConductivity, double specificHeatCapacity, double boilingPoint = 100.0, double freezingPoint = 0.0) + { + return material.Model.New(ps => { + ps.Name = "Pset_MaterialThermal"; + ps.Material = material; + ps.Properties.Add(material.Model.CreatePropertySingleValue("ThermalConductivity", new IfcThermalConductivityMeasure(thermalConductivity))); + ps.Properties.Add(material.Model.CreatePropertySingleValue("SpecificHeatCapacity", new IfcSpecificHeatCapacityMeasure(specificHeatCapacity))); + ps.Properties.Add(material.Model.CreatePropertySingleValue("BoilingPoint", new IfcThermodynamicTemperatureMeasure(boilingPoint))); + ps.Properties.Add(material.Model.CreatePropertySingleValue("FreezingPoint", new IfcThermodynamicTemperatureMeasure(freezingPoint))); + + }); + } + + public static IfcMaterialDefinitionRepresentation CreateAttachPresentation(this IfcMaterial material, C3d surfaceColor) + { + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcrepresentationresource/lexical/ifcmaterialdefinitionrepresentation.htm + return material.Model.New(def => + { + def.RepresentedMaterial = material; + def.Representations.Add(material.Model.New(rep => { + rep.ContextOfItems = material.Model.GetGeometricRepresentationContextModel(); + rep.Items.Add(material.Model.New(styleItem => { + //styleItem.Item -> NOTE: If the IfcStyledItem is used within a reference from an IfcMaterialDefinitionRepresentation then no Item shall be provided. + styleItem.Styles.Add(material.Model.CreateSurfaceStyle(surfaceColor)); + })); + })); + }); + } + } +} diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/ProductExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/ProductExt.cs new file mode 100644 index 0000000..3858998 --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/ProductExt.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Aardvark.Base; +using Aardvark.Geometry; + +using Xbim.Common; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.GeometricConstraintResource; +using Xbim.Ifc4.GeometryResource; +using Xbim.Ifc4.Kernel; +using Xbim.Ifc4.PresentationAppearanceResource; +using Xbim.Ifc4.PresentationOrganizationResource; +using Xbim.Ifc4.ProductExtension; +using Xbim.Ifc4.RepresentationResource; +using Xbim.Ifc4.SharedBldgElements; +using Xbim.Ifc4.ProfileResource; +using Xbim.Ifc4.MaterialResource; +using Xbim.Ifc4.GeometricModelResource; + +namespace Aardvark.Data.Ifc +{ + public static class ProductExt + { + public static T CreateTypeProduct(this IModel model, IEnumerable repMaps, IEnumerable properties, string name = "") where T : IIfcTypeProduct, IInstantiableEntity + { + return model.New(l => + { + //l.PredefinedType; // Has to be set afterwards! + l.Name = name; + if (!repMaps.IsEmptyOrNull()) l.RepresentationMaps.AddRange(repMaps); // shared geometries + if (!properties.IsEmptyOrNull()) l.HasPropertySets.AddRange(properties); // shared properties + }); + } + + public static T LinkToType(this T obj, IIfcTypeObject objType) where T : IIfcObject + { + obj.AddDefiningType(objType); + return obj; + } + + public static I Instantiate(this T objectType, string name, IfcObjectPlacement placement, Trafo3d trafo) where T : IfcElementType where I : IIfcProduct, IInstantiableEntity + { + // create instance and transform all representations by global trafo + var instance = objectType.Model.New(t => + { + t.Name = name; + t.ObjectPlacement = placement; + t.Representation = objectType.Model.New(r => + t.Representation = objectType.Model.New(r => + r.Representations.AddRange(objectType.RepresentationMaps.Select(m => m.Instantiate(trafo))))); + }); + + return instance.LinkToType(objectType); + } + + public static I Instantiate(this T objectType, string name, IfcObjectPlacement placement, Dictionary trafos) where T : IfcElementType where I : IIfcProduct, IInstantiableEntity + { + // create instance and transform indevidual representations + var instance = objectType.Model.New(t => + { + t.Name = name; + t.ObjectPlacement = placement; + t.Representation = objectType.Model.New(r => + r.Representations.AddRange(objectType.RepresentationMaps.Select(m => + trafos.TryGetValue(m, out Trafo3d trafo) ? m.Instantiate(trafo) : m.Instantiate(Trafo3d.Identity)))); + }); + + return instance.LinkToType(objectType); + } + + + public static IfcBuildingElementProxyType CreateProxyType(this IModel model, IfcBuildingElementProxyTypeEnum proxyType, IEnumerable repMaps, IEnumerable properties, string name = "") + { + var asdf = new EntityCreator(model); + asdf.Wall(w => w.Name = ""); + + var proxy = CreateTypeProduct(model, repMaps, properties, name); + proxy.PredefinedType = proxyType; + return proxy; + } + + public static T CreateElement(this IModel model, string elementName, IfcObjectPlacement placement, PolyMesh mesh, IfcSurfaceStyle surfaceStyle = null, IfcPresentationLayerAssignment layer = null, bool triangulated = true) where T : IfcProduct, IInstantiableEntity + { + // create a Definition shape to hold the geometry + var shape = model.CreateShapeRepresentationTessellation(mesh, layer, triangulated); + IfcRepresentationItem repItem = shape.Items.First(); + + // apply specific surfaceStyle / otherwise use mesh color / apply surface style / fallback-color + IfcSurfaceStyle surf; + + if (surfaceStyle != null) + { + surf = surfaceStyle; + } + else if (mesh.HasColors) + { + // TODO: add mesh-color cach (could have implications on other re-used objects) + var col = ((C4b)mesh.VertexAttributes.Get(PolyMesh.Property.Colors).GetValue(0)).ToC4d(); + surf = model.CreateSurfaceStyle(col.RGB, (1.0 - col.A).Clamp(0, 1), "MeshColor"); + } + else if (layer is IfcPresentationLayerWithStyle a && a.LayerStyles.OfType().FirstOrDefault() != null) + { + surf = a.LayerStyles.OfType().First(); + } + else + { + // caching / re-using of default_surfaces + var defaultSurface = model.Instances.OfType().Where(x => x.Name == "Default_Surface").FirstOrDefault(); + surf = defaultSurface ?? model.CreateSurfaceStyle(C3d.Red, 0.0, "Default_Surface"); + } + + // create visual style (works with 3d-geometry - body) + repItem.CreateStyleItem(surf); + + var proxy = model.New(c => { + c.Name = elementName; + + // create a Product Definition and add the model geometry to the cube + c.Representation = model.New(r => r.Representations.Add(shape)); + + // now place the object into the model + c.ObjectPlacement = placement; + }); + + return proxy; + } + + public static T CreateElement(this IModel model, string elementName, V3d position, PolyMesh mesh, IfcSurfaceStyle surfaceStyle = null, IfcPresentationLayerAssignment layer = null, bool triangulated = true) where T : IfcProduct, IInstantiableEntity + { + var placement = model.CreateLocalPlacement(position); + return model.CreateElement(elementName, placement, mesh, surfaceStyle, layer, triangulated); + } + + public static T CreateAttachElement(this IfcSpatialStructureElement parent, string elementName, IfcObjectPlacement placement, PolyMesh mesh, IfcSurfaceStyle surfaceStyle = null, IfcPresentationLayerAssignment layer = null, bool triangulated = true) where T : IfcProduct, IInstantiableEntity + { + var element = parent.Model.CreateElement(elementName, placement, mesh, surfaceStyle, layer, triangulated); + parent.AddElement(element); + return element; + } + + public static T CreateAttachElement(this IfcSpatialStructureElement parent, string elementName, V3d position, PolyMesh mesh, IfcSurfaceStyle surfaceStyle = null, IfcPresentationLayerAssignment layer = null, bool triangulated = true) where T : IfcProduct, IInstantiableEntity + { + var element = parent.Model.CreateElement(elementName, position, mesh, surfaceStyle, layer, triangulated); + parent.AddElement(element); + return element; + } + + public static IfcSlab CreateAttachSlab(this IfcSpatialStructureElement parent, string elementName, IfcPresentationLayerAssignment layer, IfcMaterial material) + { + var model = parent.Model; + + var box = new Box3d(V3d.Zero, new V3d(100.0, 100.0, 300.0)); + + var shape = model.New(s => { + + var rectProf = model.New(p => + { + p.ProfileName = "RectArea"; + p.ProfileType = IfcProfileTypeEnum.AREA; + p.XDim = box.SizeX; + p.YDim = box.SizeY; + }); + + IfcGeometricRepresentationItem item = model.New(solid => + { + solid.Position = parent.Model.CreateAxis2Placement3D(box.Min); + solid.Depth = box.SizeZ; // CAUTION: this must be the layer-thickness + solid.ExtrudedDirection = parent.Model.CreateDirection(V3d.ZAxis); // CAUTION: this must be the layer-orientation + solid.SweptArea = rectProf; + }); + layer?.AssignedItems.Add(item); + + s.ContextOfItems = model.Instances.OfType().First(); + s.RepresentationType = "SweptSolid"; + s.RepresentationIdentifier = "Body"; + s.Items.Add(item); + }); + + var slab = model.New(c => { + c.Name = elementName; + c.Representation = model.New(r => r.Representations.Add(shape)); + c.ObjectPlacement = parent.Model.CreateLocalPlacement(new V3d(500, 500, 500)); + }); + parent.AddElement(slab); + + // Link Material via RelAssociatesMaterial + model.New(mat => + { + // Material Layer Set Usage (HAS TO BE MANUALLY SYNCHED!) + IfcMaterialLayerSetUsage usage = model.New(u => + { + u.DirectionSense = IfcDirectionSenseEnum.NEGATIVE; + u.LayerSetDirection = IfcLayerSetDirectionEnum.AXIS3; + u.OffsetFromReferenceLine = 0; + u.ForLayerSet = model.New(set => + { + set.LayerSetName = "Concrete Layer Set"; + set.MaterialLayers.Add(model.New(layer => + { + layer.Name = "Layer1"; + layer.Material = material; + layer.LayerThickness = box.SizeZ; + layer.IsVentilated = false; + layer.Category = "Core"; + })); + }); + }); + + mat.Name = "RelMat"; + mat.RelatingMaterial = usage; + mat.RelatedObjects.Add(slab); + }); + + return slab; + } + + public static IfcAnnotation CreateAnnotation(this IModel model, string text, IfcObjectPlacement placement, V3d position, IfcPresentationLayerWithStyle layer) + { + // Anotation-Experiments https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcAnnotation.htm + return model.New(a => + { + var box = new Box3d(V3d.Zero, new V3d(200, 100, 500)); // mm + + a.Name = "Intersection of " + text; + a.ObjectPlacement = placement; + a.Representation = model.New(r => { + r.Representations.AddRange([ + model.CreateShapeRepresentationAnnotation2dText(text, position.XY, layer), + model.CreateShapeRepresentationAnnotation2dCurve([position.XY, (position.XY + new V2d(500, 750.0)), (position.XY + new V2d(1000,1000))], [[1,2,3]], layer), + model.CreateShapeRepresentationAnnotation3dCurve([position, (position + new V3d(500, 750.0, 100)), (position + new V3d(1000,1000, 200))], layer), + model.CreateShapeRepresentationAnnotation3dSurface(Plane3d.ZPlane, new Polygon2d(box.XY.Translated(position.XY-box.XY.Center).ComputeCornersCCW()), layer), + model.CreateShapeRepresentationAnnotation3dCross(position, V3d.YAxis, 45, 1000.0, layer) + //// NOT-displayed in BIMVision + //model.CreateShapeRepresentationAnnotation2dPoint(position.XY, layer), + //model.CreateShapeRepresentationAnnotation3dPoint(position, layer), + //model.CreateShapeRepresentationAnnotation2dArea(new Box2d(V2d.Zero, V2d.One*1000.0), layer), + + // broken + //model.CreateShapeRepresentationSurveyPoints(position.XY), + //model.CreateShapeRepresentationSurveyPoints(position), + ]); + }); + }); + } + } +} \ No newline at end of file diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/PropertiesExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/PropertiesExt.cs new file mode 100644 index 0000000..1662c98 --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/PropertiesExt.cs @@ -0,0 +1,544 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Aardvark.Base; + +using Xbim.Common; +using Xbim.Ifc.Extensions; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.DateTimeResource; +using Xbim.Ifc4.Kernel; +using Xbim.Ifc4.MeasureResource; +using Xbim.Ifc4.PropertyResource; +using Xbim.Ifc4.QuantityResource; + +namespace Aardvark.Data.Ifc +{ + public static class PropertiesExt + { + private static bool EqualOrContainsName(this IIfcPropertySingleValue value, string queryString) + => string.Equals(value.Name, queryString, StringComparison.OrdinalIgnoreCase) || value.Name.ToString().ToLower().Contains(queryString.ToLower()); + + public static IEnumerable GetProperties(this IIfcObject o) + { + // TODO: misses to query for Ifc2x3 IsDefinedByProperties + return o.IsDefinedBy.Where(r => r.RelatingPropertyDefinition is IIfcPropertySet) + .SelectMany(r => ((IIfcPropertySet)r.RelatingPropertyDefinition).HasProperties) + .OfType(); + } + + public static IEnumerable GetProperties(this IIfcObject o, string propertySetName) + { + return o.IsDefinedBy.Where(r => r.RelatingPropertyDefinition is IIfcPropertySet) + .Where(r => ((IIfcPropertySet)r.RelatingPropertyDefinition).Name == propertySetName) + .SelectMany(r => ((IIfcPropertySet)r.RelatingPropertyDefinition).HasProperties) + .OfType(); + } + + public static IEnumerable GetPropertiesFromType(this IIfcObject o) + { + return + o.IsTypedBy + .SelectMany(o => o.RelatingType.HasPropertySets) + .OfType() + .SelectMany(pset => pset.HasProperties) + .OfType(); + } + + public static IEnumerable GetAllProperties(this IIfcObject o) + { + var p1 = o.GetProperties(); + var p2 = o.GetPropertiesFromType(); + + return p1.Concat(p2); + } + + public static IIfcValue GetPropertyFromType(this IIfcObject o, string name) + => o.GetPropertiesFromType().FirstOrDefault(p => p.EqualOrContainsName(name))?.NominalValue; + + public static IIfcValue GetPropertyLocal(this IIfcObject o, string name) + => o.GetProperties().FirstOrDefault(p => p.EqualOrContainsName(name))?.NominalValue; + + public static IIfcValue GetProperty(this IIfcObject o, string name) + { + // first check for local properties + var res = o.GetPropertyLocal(name); + if (res != null) return res; + + // if not available check for properties from type-object + return o.GetPropertyFromType(name); + } + + public static IIfcValue GetArea(this IfcObject o) + { + // short-cut + var area = o.PhysicalSimpleQuantities.OfType().FirstOrDefault()?.AreaValue; //.TryGetSimpleValue(out double areaValue); + + ////try to get the value from quantities first + //var area = + // //get all relations which can define property and quantity sets + // obj.IsDefinedBy + + // //Search across all property and quantity sets. + // //You might also want to search in a specific quantity set by name + // .SelectMany(r => r.RelatingPropertyDefinition.PropertySetDefinitions) + + // //Only consider quantity sets in this case. + // .OfType() + + // //Get all quantities from all quantity sets + // .SelectMany(qset => qset.Quantities) + + // //We are only interested in areas + // .OfType() + + // //We will take the first one. There might obviously be more than one area properties + // //so you might want to check the name. But we will keep it simple for this example. + // .FirstOrDefault()?.AreaValue; + + if (area != null) return area; + + //try to get the value from properties + return GetProperty(o, "Area"); // .TryGetSimpleValue(out double areaValue2); + } + + #region Property-Value + + public static bool TryGetSimpleValue2(this IExpressValueType ifcValue, out T result) where T : struct + { + static DateTime ReadDateTime(string str) + { + try + { + var parts = str.Split([':', '-', 'T', 'Z'], StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 6) //it is a date time + { + var year = Convert.ToInt32(parts[0]); + var month = Convert.ToInt32(parts[1]); + var day = Convert.ToInt32(parts[2]); + var hours = Convert.ToInt32(parts[3]); + var minutes = Convert.ToInt32(parts[4]); + var seconds = Convert.ToInt32(parts[5]); + return new DateTime(year, month, day, hours, minutes, seconds, str.Last() == 'Z' ? DateTimeKind.Utc : DateTimeKind.Unspecified); + } + if (parts.Length == 3) //it is a date + { + var year = Convert.ToInt32(parts[0]); + var month = Convert.ToInt32(parts[1]); + var day = Convert.ToInt32(parts[2]); + return new DateTime(year, month, day); + } + } + catch (Exception) + { + Report.Warn("Date Time Conversion: An illegal date time string has been found [{stringValue}]", str); + } + return default; + } + + var value = new T(); + + try + { + if (ifcValue is IfcMonetaryMeasure) + { + value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); + } + else if (ifcValue is IfcTimeStamp timeStamp) + { + value = (T)Convert.ChangeType(timeStamp.ToDateTime(), typeof(T)); + } + else if (value is DateTime) //sometimes these are written as strings in the ifc file + { + value = (T)Convert.ChangeType(ReadDateTime(ifcValue.Value.ToString()), typeof(T)); + } + else if (ifcValue.UnderlyingSystemType == typeof(int) || ifcValue.UnderlyingSystemType == typeof(long) || ifcValue.UnderlyingSystemType == typeof(short) || ifcValue.UnderlyingSystemType == typeof(byte)) + { + value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); + } + else if (ifcValue.UnderlyingSystemType == typeof(double) || ifcValue.UnderlyingSystemType == typeof(float)) + { + value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); + } + else if (ifcValue.UnderlyingSystemType == typeof(string)) + { + value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); + } + else if (ifcValue.UnderlyingSystemType == typeof(bool) || ifcValue.UnderlyingSystemType == typeof(bool?)) + { + value = (T)Convert.ChangeType(ifcValue.Value, typeof(T)); + } + } + catch (Exception) + { + result = default; + return false; + } + + result = value; + return true; + } + + public static bool TryGetSimpleValue(this IExpressValueType ifcValue, out T result) where T : struct + { + var targetType = typeof(T); + + //handle null value if is it acceptable + if (ifcValue == null || ifcValue.Value == null) + { + result = default; + //return true if null is acceptable value + return targetType.IsClass || + (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>)); + } + + if (targetType == typeof(string)) + { + result = (T)(object)ifcValue.ToString(); + return true; + } + + if (targetType == typeof(float) || targetType == typeof(float?) || targetType == typeof(double) || targetType == typeof(double?)) + { + try + { + result = (T)(object)Convert.ToDouble(ifcValue.Value, CultureInfo.InvariantCulture); + return true; + } + catch (Exception ex) + { + if (ex is NullReferenceException || ex is ArgumentNullException || ex is FormatException || ex is OverflowException) + { + if (typeof(T) == typeof(float?) || + typeof(T) == typeof(double?)) + { + result = default; + return true; + } + } + } + + result = default; + return false; + } + + if (targetType == typeof(bool) || targetType == typeof(bool?)) + { + try + { + result = (T)(object)Convert.ToBoolean(ifcValue.Value); + return true; + } + catch (Exception ex) + { + if (ex is NullReferenceException || ex is ArgumentNullException) + { + if (typeof(T) == typeof(bool?)) + { + result = default; + return true; + } + } + + if (ex is FormatException) Report.Warn("Boolean Conversion: String does not consist of an " + "optional sign followed by a series of digits."); + if (ex is OverflowException) Report.Warn("Boolean Conversion: Overflow in string to int conversion."); + } + + result = default; + return false; + } + + if (targetType == typeof(int) || targetType == typeof(int?) || targetType == typeof(long) || targetType == typeof(long?)) + { + try + { + result = (T)(object)Convert.ToInt32(ifcValue.Value); + return true; + } + catch (Exception ex) + { + if (ex is NullReferenceException || ex is ArgumentNullException) + { + + if (targetType == typeof(int?) || targetType == typeof(long?)) + { + result = default; + return true; + } + } + } + + result = default; + return false; + } + + if (targetType == typeof(DateTime) || targetType == typeof(DateTime?)) + { + try + { + result = (T)(object)Convert.ToDateTime(ifcValue.Value); + return true; + } + catch (Exception) + { + result = default; + return targetType == typeof(DateTime?); + } + } + + result = default; + return false; + } + + public static bool TryGetSimpleValue(this IIfcPropertySingleValue property, out T result) where T : struct + { + var isValid = property.NominalValue.TryGetSimpleValue(out T res); + result = res; + + return isValid; + } + + public static IfcPropertySingleValue CreatePropertySingleValue(this IModel model, string name, V value) where V : IfcValue + { + return model.New(p => { + p.Name = name; + p.NominalValue = value; + }); + } + + public static IfcPropertyEnumeratedValue CreatePropertyEnumeratedValue(this IModel model, string name, V value) where V : IfcValue + { + return model.New(p => { + p.Name = name; + p.EnumerationValues.Add(value); + }); + } + + #endregion + + #region Quantities + + public static bool TryGetSimpleValue(this IIfcPhysicalQuantity ifcQuantity, out T result) where T : struct + { + if (ifcQuantity is IIfcQuantityLength ifcQuantityLength) + return TryGetSimpleValue(ifcQuantityLength.LengthValue, out result); + + if (ifcQuantity is IIfcQuantityArea ifcQuantityArea) + return TryGetSimpleValue(ifcQuantityArea.AreaValue, out result); + + if (ifcQuantity is IIfcQuantityVolume ifcQuantityVolume) + return TryGetSimpleValue(ifcQuantityVolume.VolumeValue, out result); + + if (ifcQuantity is IIfcQuantityCount ifcQuantityCount) + return TryGetSimpleValue(ifcQuantityCount.CountValue, out result); + + if (ifcQuantity is IIfcQuantityWeight ifcQuantityWeight) + return TryGetSimpleValue(ifcQuantityWeight.WeightValue, out result); + + if (ifcQuantity is IIfcQuantityTime ifcQuantityTime) + return TryGetSimpleValue(ifcQuantityTime.TimeValue, out result); + + if (ifcQuantity is IIfcPhysicalComplexQuantity) + { + Report.Warn("Complex Types are not supported!"); + } + result = default; + return false; + } + + public static IfcQuantityWeight CreateQuantityWeight(this IModel model, string name, double value) + { + return model.New(w => + { + w.Name = name; + w.WeightValue = new IfcMassMeasure(value); + }); + } + + public static IfcQuantityLength CreateQuantityLength(this IModel model, string name, double value) + { + return model.New(w => + { + w.Name = name; + w.LengthValue = new IfcLengthMeasure(value); + }); + } + + public static IfcQuantityArea CreateQuantityArea(this IModel model, string name, double value) + { + return model.New(w => + { + w.Name = name; + w.AreaValue = new IfcAreaMeasure(value); + }); + } + + public static IfcQuantityVolume CreateQuantityVolume(this IModel model, string name, double value) + { + return model.New(w => + { + w.Name = name; + w.VolumeValue = new IfcVolumeMeasure(value); + }); + } + + public static IfcQuantityCount CreateQuantityCount(this IModel model, string name, double value) + { + return model.New(w => + { + w.Name = name; + w.CountValue = new IfcCountMeasure(value); + }); + } + + public static IfcQuantityTime CreateQuantityTime(this IModel model, string name, double value) + { + return model.New(w => + { + w.Name = name; + w.TimeValue = new IfcTimeMeasure(value); + }); + } + + #endregion + + #region Dictionary Helper + public static Dictionary DistinctDictionaryFromPropertiesString(IEnumerable input) + => input.ToDictionaryDistinct(x => x.Name.ToString(), x => x.NominalValue.ToString(), (x, w) => true); + + public static Dictionary DistinctDictionaryFromPropertiesValues(IEnumerable input) + => input.ToDictionaryDistinct(x => x.Name.ToString(), x => x, (x, w) => true); + + public static T TryGetProperty(this Dictionary dict, string input) where T : struct + { + if (dict.TryGetValue(input, out var value)) + { + if (value.TryGetSimpleValue(out T result)) return result; + } + + return default; + } + + public static Dictionary DistinctDictionaryFromPropertiesOfType(IEnumerable input) where T : struct + { + return input + .Select(x => { + var valid = x.TryGetSimpleValue(out T result); + return Tuple.Create(x.Name.ToString(), valid, result); + }) + .Where(t => t.Item2) + .ToDictionaryDistinct(t => t.Item1, t => t.Item3, (x, w) => true); + } + + public static Dictionary GetPropertiesDict(this IIfcObject o, string propertySetName = null) + => DistinctDictionaryFromPropertiesString((propertySetName == null) ? o.GetProperties() : o.GetProperties(propertySetName)); + + public static Dictionary GetSharedPropertiesDict(this IIfcObject o) + => DistinctDictionaryFromPropertiesString(GetPropertiesFromType(o)); + + public static Dictionary GetAllPropertiesDict(this IIfcObject o) + => DistinctDictionaryFromPropertiesString(o.GetAllProperties()); + + [Obsolete] + public static Dictionary GetHilitePropertiesDict(this IIfcObject o) + => o.GetPropertiesDict("Hilite"); + #endregion + + #region PropertySet + public static IfcPropertySet CreatePropertySet(this IModel model, string setName, Dictionary parameters) + { + // supports the following types: https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcmeasureresource/lexical/ifcvalue.htm + var set = model.New(pset => { + pset.Name = setName; + pset.HasProperties.AddRange( + parameters.Select(x => model.New(p => + { + p.Name = x.Key; + p.NominalValue = x.Value switch + { + double d => new IfcReal(d), //double lum => new IfcLuminousFluxMeasure(lum), + float r => new IfcReal(r), + int i => new IfcInteger(i), + bool b => new IfcBoolean(b), + _ => new IfcText(x.Value.ToString()), + }; + })) + ); + }); + + return set; + } + + public static IfcProduct AttachPropertySet(this IfcProduct o, IfcPropertySet set) + { + o.AddPropertySet(set); + return o; + } + + public static IfcPropertySet CreateAttachPropertySet(this IfcObject o, string setName, Dictionary parameters) + { + var set = o.Model.CreatePropertySet(setName, parameters); + + o.AddPropertySet(set); + + return set; + } + + public static void PurgePropertySingleValue(this IfcObject o, string pSetName, string propertyName) + { + // removes property and in case of single-propset resulting empty set globally + // CAUTION: affects all objects re-using it + + IIfcPropertySet propertySet = o.GetPropertySet(pSetName); + if (propertySet != null) + { + IIfcPropertySingleValue ifcPropertySingleValue = propertySet.HasProperties.FirstOrDefault((IIfcPropertySingleValue p) => p.Name == (IfcIdentifier)propertyName); + if (ifcPropertySingleValue != null) + { + propertySet.HasProperties.Remove(ifcPropertySingleValue); + + // delete property from model + o.Model.Delete(ifcPropertySingleValue); + } + + if (propertySet.HasProperties.IsEmpty()) + { + // remove reference of empty set and object + var rel = o.IsDefinedBy.FirstOrDefault((IfcRelDefinesByProperties r) => r.RelatingPropertyDefinition.PropertySetDefinitions.FirstOrDefault(a => a.Name == pSetName) != null); + if (rel != null) o.Model.Delete(rel); + + // remove empty set from model + o.Model.Delete(propertySet); + } + } + } + + public static void PurgePropertySet(this IfcObject o, string pSetName) + { + // removes set and all its properties globally + // CAUTION: affects all objects re-using it + + IIfcPropertySet propertySet = o.GetPropertySet(pSetName); + if (propertySet != null) + { + // stash for later removal + var temp = propertySet.HasProperties.ToArray(); + + // release properties from set + propertySet.HasProperties.Clear(); + + // detlete properties from model + temp.ForEach(o.Model.Delete); + + // remove reference of empty set and object + var rel = o.IsDefinedBy.FirstOrDefault((IfcRelDefinesByProperties r) => r.RelatingPropertyDefinition.PropertySetDefinitions.FirstOrDefault(a => a.Name == pSetName) != null); + if (rel != null) o.Model.Delete(rel); + + // remove empty set from model + o.Model.Delete(propertySet); + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/ShapeRepresentationExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/ShapeRepresentationExt.cs new file mode 100644 index 0000000..0b9a2d9 --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/ShapeRepresentationExt.cs @@ -0,0 +1,399 @@ + +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Aardvark.Base; +using Aardvark.Geometry; + +using Xbim.Common; +using Xbim.Common.Step21; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.GeometricModelResource; +using Xbim.Ifc4.GeometryResource; +using Xbim.Ifc4.MeasureResource; +using Xbim.Ifc4.PresentationDefinitionResource; +using Xbim.Ifc4.PresentationOrganizationResource; +using Xbim.Ifc4.ProfileResource; +using Xbim.Ifc4.RepresentationResource; + +namespace Aardvark.Data.Ifc +{ + public static class ShapeRepresentationExt + { + #region SurveyPoints (broken) + public static IfcShapeRepresentation CreateShapeRepSurveyPoints(this IModel model, IfcPresentationLayerWithStyle layer, params V2d[] points) + { + // Set of Survey points 2D https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Annotation_Geometry/Set_Of_Survey_Points/content.html + IfcGeometricRepresentationItem item = model.CreateCartesianPointList2D(points).AssignLayer(layer); + + return model.New(r => + { + r.RepresentationIdentifier = "Annotation"; + r.RepresentationType = "Point"; + r.Items.Add(item); + r.ContextOfItems = model.GetGeometricRepresentationContextPlan(); + }); + } + + public static IfcShapeRepresentation CreateShapeRepresentationSurveyPoints(this IModel model, params V2d[] points) + => CreateShapeRepSurveyPoints(model, null, points); + + public static IfcShapeRepresentation CreateShapeRepresentationSurveyPoints(this IModel model, IfcPresentationLayerWithStyle layer, params V3d[] points) + { + // Set of Survey points 3D https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Annotation_Geometry/Set_Of_Survey_Points/content.html + IfcGeometricRepresentationItem item = model.CreateCartesianPointList3D(points).AssignLayer(layer); + + return model.New(r => + { + r.RepresentationIdentifier = "Annotation"; + r.RepresentationType = "Point"; + r.Items.Add(item); + r.ContextOfItems = model.GetGeometricRepresentationContextModel(); + }); + } + + public static IfcShapeRepresentation CreateShapeRepresentationSurveyPoints(this IModel model, params V3d[] points) + => CreateShapeRepresentationSurveyPoints(model, null, points); + + #endregion + + #region 3D Annotations + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3d(this IfcRepresentationItem item) + { + return item.Model.New(r => + { + r.RepresentationIdentifier = "Annotation"; + r.RepresentationType = "GeometricSet"; + r.Items.Add(item); + r.ContextOfItems = item.Model.GetGeometricRepresentationContextModel(); + }); + } + + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3dPoint(this IModel model, V3d point, IfcPresentationLayerWithStyle layer = null) + { + var content = model.CreatePoint(point); + layer?.AssignedItems.Add(content); + + return content.CreateShapeRepresentationAnnotation3d(); + } + + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3dCurve(this IModel model, V3d[] points, IfcPresentationLayerWithStyle layer = null) + { + IfcGeometricRepresentationItem content = model.CreatePolyLine(points); + layer?.AssignedItems.Add(content); + + return content.CreateShapeRepresentationAnnotation3d(); + } + + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3dCross(this IModel model, V3d origin, V3d normal, double angleInDegree, double scale, IfcPresentationLayerWithStyle layer = null) + { + //var crossPoints = new V3d[] { + // origin, origin+(axis1.Normalized * scale), + // origin, origin-(axis1.Normalized * scale), + // origin, origin+(axis2.Normalized * scale), + // origin, origin-(axis2.Normalized * scale), + //}; + var plane = new Plane3d(normal, 0.0); + var d = new Rot2d(angleInDegree.RadiansFromDegrees()) * V2d.XAxis * scale; + + var crossPoints = new V3d[] { + origin, origin+plane.Unproject( d), //+dir.XYO, + origin, origin+plane.Unproject(-d), //-dir.XYO, + origin, origin+plane.Unproject( d.YX * new V2d(-1,1)), //+dir.YXO * new V3d(-1,1,0), + origin, origin+plane.Unproject( d.YX * new V2d(1,-1)), //+dir.YXO * new V3d(1,-1,0) + }; + + IfcGeometricRepresentationItem content = model.CreatePolyLine(crossPoints); + layer?.AssignedItems.Add(content); + + return content.CreateShapeRepresentationAnnotation3d(); + } + + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation3dSurface(this IModel model, Plane3d plane, Polygon2d poly, IfcPresentationLayerWithStyle layer = null) + { + IfcGeometricRepresentationItem content = model.CreateCurveBoundedPlane(plane, poly); + layer?.AssignedItems.Add(content); + + return content.CreateShapeRepresentationAnnotation3d(); + } + #endregion + + #region 2D Annotations + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2D(this IfcRepresentationItem item) + { + return item.Model.New(r => + { + r.RepresentationIdentifier = "Annotation"; + r.RepresentationType = "Annotation2D"; + r.Items.Add(item); + r.ContextOfItems = item.Model.GetGeometricRepresentationContextPlan(); + }); + } + + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2dPoint(this IModel model, V2d point, IfcPresentationLayerWithStyle layer = null) + { + var item = model.CreatePoint(point); + layer?.AssignedItems.Add(item); + + return item.CreateShapeRepresentationAnnotation2D(); + } + + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2dCurve(this IModel model, V2d[] points, IEnumerable indices = null, IfcPresentationLayerWithStyle layer = null) + { + IfcGeometricRepresentationItem item = model.CreateIndexedPolyCurve(points, indices); + layer?.AssignedItems.Add(item); + + return item.CreateShapeRepresentationAnnotation2D(); + } + + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2dText(this IModel model, string label, V2d position, IfcPresentationLayerWithStyle layer = null) + { + // ONLY visible in "BIMVISION" + IfcGeometricRepresentationItem item = model.New(l => + { + // https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD1/HTML/schema/ifcpresentationdefinitionresource/lexical/ifctextliteral.htm + l.Path = IfcTextPath.RIGHT; + l.Literal = label; + //// Attributes for + //l.BoxAlignment = new IfcBoxAlignment("center"); + //l.Extent = model.New(e => + //{ + // e.SizeInX = new IfcLengthMeasure(300.0); + // e.SizeInY = new IfcLengthMeasure(200.0); + //}); + l.Placement = model.CreateAxis2Placement2D(position); + }); + layer?.AssignedItems.Add(item); + + return item.CreateShapeRepresentationAnnotation2D(); + } + + public static IfcShapeRepresentation CreateShapeRepresentationAnnotation2dArea(this IModel model, Box2d rect, IfcPresentationLayerWithStyle layer = null) + { + IfcGeometricRepresentationItem item = model.New(l => + { + l.OuterBoundary = model.CreateIndexedPolyCurve(rect.ComputeCornersCCW()); + l.InnerBoundaries.Add(model.CreateIndexedPolyCurve(rect.ShrunkBy(new V2d(0.3)).ComputeCornersCCW())); + }); + layer?.AssignedItems.Add(item); + + return item.CreateShapeRepresentationAnnotation2D(); + } + + #endregion + + #region RepresentationMap (instancing) + public static IfcRepresentationMap CreateRepresentationMap(this IfcRepresentation item) + { + return item.Model.New(map => + { + map.MappingOrigin = item.Model.CreateAxis2Placement3D(V3d.Zero); + map.MappedRepresentation = item; + }); + } + + public static IfcShapeRepresentation Instantiate(this IfcRepresentationMap map, Trafo3d trafo) + { + var item = map.Model.New(m => + { + m.MappingSource = map; + m.MappingTarget = map.Model.New(x => + { + var scale = trafo.GetScaleVector(); + x.Axis1 = map.Model.CreateDirection(trafo.Forward.C0.XYZ.Normalized); // X - Axis + x.Axis2 = map.Model.CreateDirection(trafo.Forward.C1.XYZ.Normalized); // Y - Axis + x.Axis3 = map.Model.CreateDirection(trafo.Forward.C2.XYZ.Normalized); // Z - Axis + x.LocalOrigin = map.Model.CreatePoint(trafo.Forward.C3.XYZ); + x.Scale = scale.X; + x.Scale2 = scale.Y; + x.Scale3 = scale.Z; + }); + }); + + return item.Model.New(r => + { + r.RepresentationIdentifier = "Body"; + r.RepresentationType = "MappedRepresentation"; + r.Items.Add(item); + r.ContextOfItems = item.Model.GetGeometricRepresentationContextModel(); + }); + } + #endregion + + #region Lights + public static IfcShapeRepresentation CreateShapeRepresentationLighting(this IfcRepresentationItem item) + { + return item.Model.New(r => + { + r.RepresentationIdentifier = "Lighting"; + r.RepresentationType = "LightSource"; + r.Items.Add(item); + r.ContextOfItems = item.Model.GetGeometricRepresentationContextModel(); + }); + } + + public static IfcShapeRepresentation CreateShapeRepresentationLightingAmbient(this IModel model, C3d color, string name = null, double? intensity = null, double? ambientIntensity = null, IfcPresentationLayerAssignment layer = null) + { + + IfcGeometricRepresentationItem item = model.CreateLightSourceAmbient(color, name, intensity, ambientIntensity); + layer?.AssignedItems.Add(item); + + return CreateShapeRepresentationLighting(item); + } + + public static IfcShapeRepresentation CreateShapeRepresentationLightingDirectional(this IModel model, C3d color, V3d direction, string name = null, double? intensity = null, double? ambientIntensity = null, IfcPresentationLayerAssignment layer = null) + { + IfcGeometricRepresentationItem item = model.CreateLightSourceDirectional(color, direction, name, intensity, ambientIntensity); + layer?.AssignedItems.Add(item); + + return CreateShapeRepresentationLighting(item); + } + + public static IfcShapeRepresentation CreateShapeRepresentationLightingPositional(this IModel model, C3d color, V3d position, double radius, double constantAttenuation, double distanceAttenuation, double quadricAttenuation, string name = null, double? intensity = null, double? ambientIntensity = null, IfcPresentationLayerAssignment layer = null) + { + IfcGeometricRepresentationItem item = model.CreateLightSourcePositional(color, position, radius, constantAttenuation, distanceAttenuation, quadricAttenuation, name, intensity, ambientIntensity); + layer?.AssignedItems.Add(item); + + return CreateShapeRepresentationLighting(item); + } + + public static IfcShapeRepresentation CreateShapeRepresentationLightingSpot(this IModel model, C3d color, V3d position, V3d direction, double radius, double constantAttenuation, double distanceAttenuation, double quadricAttenuation, double spreadAngle, double beamWidthAngle, string name = null, double? intensity = null, double? ambientIntensity = null, double? concentrationExponent = null, IfcPresentationLayerAssignment layer = null) + { + IfcGeometricRepresentationItem item = model.CreateLightSourceSpot(color, position, radius, constantAttenuation, distanceAttenuation, quadricAttenuation, direction, spreadAngle, beamWidthAngle, name, intensity, ambientIntensity, concentrationExponent); + layer?.AssignedItems.Add(item); + + return CreateShapeRepresentationLighting(item); + } + + public static IfcShapeRepresentation CreateShapeRepresentationLightingGoniometric(this IModel model, C3d color, V3d location, double colourTemperature, double luminousFlux, IfcLightIntensityDistribution distribution, IfcLightEmissionSourceEnum lightEmissionSource = IfcLightEmissionSourceEnum.NOTDEFINED, IfcPresentationLayerAssignment layer = null) + { + var placement = model.CreateAxis2Placement3D(location); + IfcGeometricRepresentationItem item = model.CreateLightSourceGoniometric(color, colourTemperature, luminousFlux, lightEmissionSource, distribution, placement); + layer?.AssignedItems.Add(item); + + return CreateShapeRepresentationLighting(item); + } + #endregion + + #region Box + public static IfcShapeRepresentation CreateShapeRepresentationBoundingBox(this IModel model, Box3d box, IfcPresentationLayerAssignment layer = null) + { + IfcGeometricRepresentationItem item = model.New(b => b.Set(box)); + layer?.AssignedItems.Add(item); + + return model.New(r => + { + r.RepresentationIdentifier = "Box"; + r.RepresentationType = "BoundingBox"; + r.Items.Add(item); + r.ContextOfItems = model.GetGeometricRepresentationContextModel(); + }); + } + + public static IfcShapeRepresentation CreateShapeRepresentationSolidBox(this IModel model, Box3d box, IfcPresentationLayerAssignment layer = null) + { + // Box creation by extruding box-base along Z-Axis + var rectProf = model.New(p => + { + p.ProfileName = "RectArea"; + p.ProfileType = IfcProfileTypeEnum.AREA; + p.XDim = box.SizeX; + p.YDim = box.SizeY; + //p.Position = model.CreateAxis2Placement2D(V2d.Zero); + }); + + IfcGeometricRepresentationItem item = model.New(solid => + { + solid.Position = model.CreateAxis2Placement3D(box.Min); + solid.Depth = box.SizeZ; + solid.ExtrudedDirection = model.CreateDirection(V3d.ZAxis); + solid.SweptArea = rectProf; + }); + layer?.AssignedItems.Add(item); + + return model.New(s => { + s.ContextOfItems = model.GetGeometricRepresentationContextModel(); + s.RepresentationType = "SweptSolid"; + s.RepresentationIdentifier = "Body"; + s.Items.Add(item); + }); + } + #endregion + + #region Surface + public static IfcShapeRepresentation CreateShapeRepresentationSurface(this IModel model, Plane3d plane, Polygon2d poly, IfcPresentationLayerAssignment layer = null) + { + if (!poly.IsCcw()) + { + poly.Reverse(); + } + + IfcGeometricRepresentationItem item = model.CreateCurveBoundedPlane(plane, poly); + layer?.AssignedItems.Add(item); + + // Box creation by extruding box-base along Z-Axis + return model.New(r => + { + r.RepresentationIdentifier = "Surface"; + r.RepresentationType = "Surface3D"; + r.Items.Add(item); + r.ContextOfItems = model.GetGeometricRepresentationContextModel(); + }); + } + #endregion + + #region PolyMesh + private static IfcTriangulatedFaceSet CreateTriangulatedFaceSet(IModel model, PolyMesh inputMesh) + { + var triangleMesh = inputMesh.TriangulatedCopy(); + + return model.New(tfs => + { + tfs.Closed = true; + tfs.Coordinates = model.CreateCartesianPointList3D(triangleMesh.PositionArray); + + for (int i = 0; i < triangleMesh.FirstIndexArray.Length - 1; i++) + { + var firstIndex = triangleMesh.FirstIndexArray[i]; + var values = new long[3].SetByIndex(x => triangleMesh.VertexIndexArray[firstIndex + x]).Select(v => new IfcPositiveInteger(v + 1)); // CAUTION! Indices are 1 based in IFC! + tfs.CoordIndex.GetAt(i).AddRange(values); + } + }); + } + + private static IfcPolygonalFaceSet CreatePolygonalFaceSet(IModel model, PolyMesh inputMesh) + { + // only available in IFC4 + if (model.SchemaVersion != XbimSchemaVersion.Ifc4) + return null; + + var faces = new List(inputMesh.Faces.Count()); + + foreach (var face in inputMesh.Faces) + { + faces.Add(model.New(f => f.CoordIndex.AddRange(face.VertexIndices.Select(v => new IfcPositiveInteger(v + 1))))); // CAUTION! Indices are 1 based in IFC! + } + + return model.New(p => + { + p.Closed = true; + p.Coordinates = model.CreateCartesianPointList3D(inputMesh.PositionArray); + p.Faces.AddRange(faces); + }); + } + + public static IfcShapeRepresentation CreateShapeRepresentationTessellation(this IModel model, PolyMesh mesh, IfcPresentationLayerAssignment layer = null, bool triangulated = true) + { + IfcGeometricRepresentationItem item = triangulated ? CreateTriangulatedFaceSet(model, mesh) : CreatePolygonalFaceSet(model, mesh); + layer?.AssignedItems.Add(item); + + return model.New(s => { + s.ContextOfItems = model.GetGeometricRepresentationContextModel(); + s.RepresentationType = "Tessellation"; + s.RepresentationIdentifier = "Body"; + s.Items.Add(item); + }); + } + #endregion + } +} \ No newline at end of file diff --git a/src/Aardvark.Data.Ifc/XbimExtensions/StylingExt.cs b/src/Aardvark.Data.Ifc/XbimExtensions/StylingExt.cs new file mode 100644 index 0000000..b71b9ea --- /dev/null +++ b/src/Aardvark.Data.Ifc/XbimExtensions/StylingExt.cs @@ -0,0 +1,286 @@ + +using System.Collections.Generic; +using Aardvark.Base; + +using Xbim.Common; +using Xbim.Ifc4.Interfaces; +using Xbim.Ifc4.GeometryResource; +using Xbim.Ifc4.MeasureResource; +using Xbim.Ifc4.PresentationAppearanceResource; + +namespace Aardvark.Data.Ifc +{ + public static class StylingExt + { + public static IfcColourRgb CreateColor(this IModel model, C3f colour) + => model.New(x => x.Set(colour)); + + public static IfcColourRgb CreateColor(this IModel model, C3d colour) + => model.New(x => x.Set(colour)); + + #region Text Styling + public static IfcTextStyleForDefinedFont CreateTextStyleForDefinedFont(this IModel model, C3f colour, C3f background, string name = null) + { + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextstylefordefinedfont.htm + + return model.New(f => { + f.Colour = model.CreateColor(colour); + // optional + f.BackgroundColour = model.CreateColor(background); + }); + } + + public enum TextDecoration { None, UnderLine, Overline, Linethrough } + public enum TextTransform { Capitalize, Uppercase, Lowercase, None } + public enum TextAlignment { Left, Right, Center, Justify } + + public static IfcTextStyleTextModel CreateTextStyleTextModel(this IModel model, double textIndent, TextAlignment textAlign, TextDecoration textDecoration, TextTransform textTransform, double letterSpacing, double wordSpacing, double lineHeight) + { + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextstyletextmodel.htm + + string decoration = textDecoration switch + { + TextDecoration.UnderLine => "underLine", + TextDecoration.Overline => "overline", + TextDecoration.Linethrough => "line-through", + _ => "none" + }; + + string transform = textTransform switch + { + TextTransform.Capitalize => "capitalize", + TextTransform.Uppercase => "uppercase", + TextTransform.Lowercase => "lowercase", + _ => "none" + }; + + string alignment = textAlign switch + { + TextAlignment.Left => "left", + TextAlignment.Right => "right", + TextAlignment.Center => "center", + _ => "justify" + }; + + return model.New(tm => + { + // optional + tm.TextIndent = new IfcLengthMeasure(textIndent); // The property specifies the indentation that appears before the first formatted line. + tm.TextAlign = new IfcTextAlignment(alignment); + tm.TextDecoration = new IfcTextDecoration(decoration); + tm.TextTransform = new IfcTextTransformation(transform); + tm.LetterSpacing = new IfcLengthMeasure(letterSpacing); + tm.WordSpacing = new IfcLengthMeasure(wordSpacing); + tm.LineHeight = new IfcLengthMeasure(lineHeight); + }); + } + + public enum FontStyle { Normal, Italic, Oblique } + public enum FontWeight { Normal, Bold } + public enum FontVariant { Normal, Smallcaps } + public static IfcTextStyleFontModel CreateTextStyleFontModel(this IModel model, double fontSize, string fontFamily, string fontModelName, FontStyle fontStyle = FontStyle.Normal, FontWeight fontWeight = FontWeight.Normal, FontVariant fontVariant = FontVariant.Normal) + { + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextstylefontmodel.htm + + string style = fontStyle switch + { + FontStyle.Normal => "normal", + FontStyle.Italic => "italic", + _ => "oblique" + }; + + string weight = fontWeight switch + { + FontWeight.Normal => "400", + _ => "700", + }; + + string variant = fontVariant switch + { + FontVariant.Normal => "normal", + _ => "small-caps", + }; + + return model.New(f => + { + f.Name = fontModelName; + f.FontSize = new IfcLengthMeasure(fontSize); + f.FontFamily.Add(new IfcTextFontName(fontFamily)); // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextfontname.htm + // optional + f.FontStyle = new IfcFontStyle(style); + f.FontWeight = new IfcFontWeight(weight); + f.FontVariant = new IfcFontVariant(variant); + }); + } + + public static IfcTextStyle CreateTextStyle(this IModel model, double fontSize, C3f colour, C3f background, string fontModelName, string fontFamily = "serif", bool modelOrDrauting = true, string name = null) + { + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifctextstyle.htm + + return model.New(ts => + { + ts.ModelOrDraughting = modelOrDrauting; + + if (name != null) ts.Name = name; + + ts.TextFontStyle = model.CreateTextStyleFontModel(fontSize, fontFamily, fontModelName); + + // optional + ts.TextCharacterAppearance = model.CreateTextStyleForDefinedFont(colour, background); + ts.TextStyle = model.CreateTextStyleTextModel(10, TextAlignment.Right, TextDecoration.None, TextTransform.None, 10, 10, 20); + }); + } + + #endregion + + #region Surface Styling + public static IfcSurfaceStyleShading CreateSurfaceStyleShading(this IModel model, C3d surface, double transparency = 0.0) + { + // https://standards.buildingsmart.org/MVD/RELEASE/IFC4/ADD2_TC1/RV1_2/HTML/schema/ifcpresentationappearanceresource/lexical/ifcsurfacestyleshading.htm + + return model.New(l => + { + l.SurfaceColour = model.CreateColor(surface); + l.Transparency = new IfcNormalisedRatioMeasure(transparency); // [0 = opaque .. 1 = transparent] + }); + } + + public static IfcSurfaceStyleRendering CreateSurfaceStyleRendering(this IModel model, C3d surface, double transparency, C3d diffuse, C3d diffuseTransmission, C3d transmission, C3d specular, double specularHighlight, C3d reflection, IfcReflectanceMethodEnum reflectionType) + { + // https://standards.buildingsmart.org/MVD/RELEASE/IFC4/ADD2_TC1/RV1_2/HTML/schema/ifcpresentationappearanceresource/lexical/ifcsurfacestylerendering.htm + + return model.New(l => + { + l.SurfaceColour = model.CreateColor(surface); + l.Transparency = new IfcNormalisedRatioMeasure(transparency); // [0 = opaque .. 1 = transparent] + l.DiffuseColour = model.CreateColor(diffuse); + + l.TransmissionColour = model.CreateColor(transmission); + l.DiffuseTransmissionColour = model.CreateColor(diffuseTransmission); + + l.ReflectionColour = model.CreateColor(reflection); + l.ReflectanceMethod = reflectionType; + + // The IfcSpecularExponent defines the datatype for exponent determining the sharpness of the 'reflection'. + // The reflection is made sharper with large values of the exponent, such as 10.0. + // Small values, such as 1.0, decrease the specular fall - off. + // IfcSpecularExponent is of type REAL. + l.SpecularHighlight = new IfcSpecularExponent(specularHighlight); + l.SpecularColour = model.CreateColor(specular); + }); + } + + public static IfcSurfaceStyleLighting CreateSurfaceStyleLighting(this IModel model, C3d diffuseTransmission, C3d diffuseReflection, C3d transmission, C3d reflectance) + { + // https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2/HTML/schema/ifcpresentationappearanceresource/lexical/ifcsurfacestylelighting.htm + + return model.New(l => + { + l.DiffuseTransmissionColour = model.CreateColor(diffuseTransmission); + l.DiffuseReflectionColour = model.CreateColor(diffuseReflection); + l.TransmissionColour = model.CreateColor(transmission); + l.ReflectanceColour = model.CreateColor(reflectance); + }); + } + + public static IfcSurfaceStyle CreateSurfaceStyle(this IModel model, IEnumerable styles, string name = null) + { + // IfcSurfaceStyle is an assignment of one or many surface style elements to a surface, defined by subtypes of + // IfcSurface, IfcFaceBasedSurfaceModel, IfcShellBasedSurfaceModel, or by subtypes of IfcSolidModel. + // The positive direction of the surface normal relates to the positive side. In case of solids the outside of the solid is to be taken as positive side. + + return model.New(style => + { + if (name != null) style.Name = name; + style.Side = IfcSurfaceSide.BOTH; + style.Styles.AddRange(styles); // [1:5] [IfcSurfaceStyleShading -> IfcSurfaceStyleRendering | IfcSurfaceStyleLighting | IfcSurfaceStyleWithTextures | IfcExternallyDefinedSurfaceStyle | IfcSurfaceStyleRefraction + }); + } + + public static IfcSurfaceStyle CreateSurfaceStyle(this IModel model, IfcSurfaceStyleElementSelect style, string name = null) + => CreateSurfaceStyle(model, [style], name); + + public static IfcSurfaceStyle CreateSurfaceStyle(this IModel model, C3d surface, double transparency = 0.0, string name = null) + => CreateSurfaceStyle(model, model.CreateSurfaceStyleShading(surface, transparency), name); + + #endregion + + #region Curve Styling + public static IfcCurveStyle CreateCurveStyle(this IModel model, C3d color, double width, double visibleLengh = 0, double invisibleLength = 0, bool modelOrDraughting = true) + { + return model.New(c => + { + c.ModelOrDraughting = modelOrDraughting; + c.CurveColour = model.CreateColor(color); + c.CurveWidth = new IfcPositiveLengthMeasure(width); + if (visibleLengh > 0) + { + c.CurveFont = model.New(f => + f.PatternList.Add(model.New(p => + { + p.VisibleSegmentLength = visibleLengh; + if (invisibleLength > 0) p.InvisibleSegmentLength = invisibleLength; + }) + )); + } + }); + } + #endregion + + #region Area Styling + public static IfcFillAreaStyleHatching CreateFillAreaStyleHatching(this IModel model, double angle, double startOfNextHatchLine, IfcCurveStyle curveStyle) + { + return model.New(h => + { + h.HatchLineAppearance = curveStyle; + h.HatchLineAngle = new IfcPlaneAngleMeasure(angle); + h.StartOfNextHatchLine = new IfcPositiveLengthMeasure(startOfNextHatchLine); + }); + } + + public static IfcFillAreaStyle CreateFillAreaStyle(this IModel model, C3d backgroundColor, bool modelOrDrauting = true, string name = null) + { + // NOTE: Color information of surfaces for rendering is assigned by using IfcSurfaceStyle, not by using IfcFillAreaStyle. + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifcfillareastyle.htm + return model.New(a => + { + if (name != null) a.Name = name; + a.ModelorDraughting = modelOrDrauting; + // Solid fill for areas and surfaces by only assigning IfcColour to the set of FillStyles. It then provides the background colour for the filled area or surface. + a.FillStyles.Add(model.CreateColor(backgroundColor)); + }); + } + + public static IfcFillAreaStyle CreateFillAreaStyle(this IModel model, C3d hatchingColour, double angle, double startOfNextHatchLine, IfcCurveStyle curveStyle, bool modelOrDrauting = true, string name = null) + { + // https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcpresentationappearanceresource/lexical/ifcfillareastyle.htm + // + return model.New(a => + { + if (name != null) a.Name = name; + a.ModelorDraughting = modelOrDrauting; + // Solid fill for areas and surfaces by only assigning IfcColour to the set of FillStyles. It then provides the background colour for the filled area or surface. + a.FillStyles.AddRange([ + model.CreateColor(hatchingColour), + model.CreateFillAreaStyleHatching(angle, startOfNextHatchLine, curveStyle) + ]); + }); + } + #endregion + + #region Style Item + public static IfcStyledItem CreateStyleItem(this IfcRepresentationItem item, IEnumerable styles) + { + // Each subtype of IfcPresentationStyle is assigned to the IfcGeometricRepresentationItem's through an intermediate IfcStyledItem. + return item.Model.New(styleItem => { + styleItem.Styles.AddRange(styles); + if (item != null) styleItem.Item = item; + }); + } + + public static IfcStyledItem CreateStyleItem(this IfcRepresentationItem item, IfcPresentationStyle style) + => CreateStyleItem(item, [style]); + + #endregion + } +}