-
Notifications
You must be signed in to change notification settings - Fork 174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] How to get all referenced instances for a particular instance? #384
Comments
Yes, this is possible. Have a look at the code we use to delete an entity. That also needs to inspect the model and find all relating entities to keep the model consistent. |
Thanks for your quick response and your advice, Martin, That's exactly what I'm looking for. But seems it doesn't take care all referring types. I have used the same method for GetReferingTypes, and ReplaceReferences in ModelHelper.cs. In the ReplaceReferences, I just commented out those lines for replacing the value. using (var model = IfcStore.Open(fileName))
{
var entities = model.Instances.OfType<IIfcCartesianPoint>();
var uniqueTypes = new HashSet<Type>(entities.Select(e => e.GetType()));
var referingTypes = new HashSet<ReferingType>(uniqueTypes.SelectMany(t => GetReferingTypes(model, t)));
foreach (var referingType in referingTypes)
ReplaceReferences<IPersistEntity, IPersistEntity>(model, entities, referingType, null);
} For example, I can't find #123 has been used in #122. (this piece is from the same sample file in the description).
It seems like the referring types of IFCCARTESIANPOINT not taking IFCBSPLINESURFACEWITHKNOTS. Did I miss something? Please let me know. Thanks a lot. |
IFCBSPLINESURFACEWITHKNOTS is one two classes in IFC using nested (2D) lists. The code looking for references might be missing these. Feel free to contribute to the project with a pull request fixing this issue. All contributions are most welcome. |
I fixed this issue in the ModelHelper, it works like a charm. Thanks a lot, Martin. I added a new prop in the struct ReferingType. private struct ReferingType
{
public ExpressType Type;
public List<ExpressMetaProperty> SingleReferences;
public List<ExpressMetaProperty> ListReferences;
public List<ExpressMetaProperty> NestedListReferences;
} And added the related part to get referring types. private static IEnumerable<ReferingType> GetReferingTypes(IModel model, Type entityType)
{
// ......
foreach (var type in types)
{
if (!ReferingTypesCache.TryGetValue(type.Type, out ReferingType rt))
{
var singleReferences = type.Properties.Values.Where(p =>
p.EntityAttribute != null && p.EntityAttribute.Order > 0 &&
p.PropertyInfo.PropertyType.GetTypeInfo().IsAssignableFrom(entityType)).ToList();
var listReferences = type.Properties.Values.Where(p =>
p.EntityAttribute != null && p.EntityAttribute.Order > 0 &&
p.PropertyInfo.PropertyType.GetTypeInfo().IsGenericType &&
p.PropertyInfo.PropertyType.GenericTypeArgumentIsAssignableFrom(entityType)).ToList();
//add nested list references
var nestedListReferences = type.Properties.Values.Where(p =>
p.EntityAttribute != null && p.EntityAttribute.Order > 0 &&
p.PropertyInfo.PropertyType.GetTypeInfo().IsGenericType &&
p.PropertyInfo.PropertyType.GetItemTypeFromGenericType().IsGenericType &&
p.PropertyInfo.PropertyType.GetItemTypeFromGenericType().GenericTypeArgumentIsAssignableFrom(entityType)).ToList();
if (!singleReferences.Any() && !listReferences.Any() && !nestedListReferences.Any())
continue;
rt = new ReferingType { Type = type, SingleReferences = singleReferences, ListReferences = listReferences, NestedListReferences = nestedListReferences };
ReferingTypesCache.TryAdd(type.Type, rt);
}
referingTypes.Add(rt);
}
return referingTypes;
} Then I used the ReplaceReferences (the one with the IEnumerable argument) to check and replace the entities. private static void ReplaceReferences<TEntity, TReplacement>(IModel model, IEnumerable<TEntity> entities, ReferingType referingType, TReplacement replacement)
where TEntity : IPersistEntity where TReplacement : TEntity
{
// ......
foreach (var toCheck in entitiesToCheck)
{
// ......
//check for nested list references
foreach (var pInfo in referingType.NestedListReferences.Select(p => p.PropertyInfo))
{
var pVal = pInfo.GetValue(toCheck);
if (pVal == null) continue;
//it might be uninitialized optional item set
if (pVal is IOptionalItemSet optSet && !optSet.Initialized)
continue;
//or it is non-optional item set implementing IList
if (!(pVal is IList nestedItemSet))
throw new XbimException($"Unable to remove items from {referingType.Type.Name}.{pInfo.Name}. No IList implementation.");
for (int i = 0; i < nestedItemSet.Count; i++)
{
if (!(nestedItemSet[i] is IList itemSet))
throw new XbimException($"Unable to remove items from {referingType.Type.Name}.{pInfo.Name}. No IList implementation.");
for (int j = 0; j < itemSet.Count; j++)
{
var item = itemSet[j];
if (!hash.Contains(item))
continue;
itemSet.RemoveAt(j);
if (replacement != null)
itemSet.Insert(j, replacement);
else
j--; // keep in sync
}
}
}
}
} I have checked the documentation, there are IfcBSplineSurface and IfcBSplineSurfaceWithKnots using the nested list in the property ControlPointsList. I have tested this piece of code with those two classes. It works fine for me for now. I will make a pull request later, but please notice that I have only updated the GetReferingTypes(IModel model, Type entityType) and ReplaceReferences<TEntity, TReplacement>(IModel model, IEnumerable entities, ReferingType referingType, TReplacement replacement) these two methods mentioned above. Since I am not very familiar with the whole project, this needs to be further adapted in other related methods. |
There is a way to get a list of all referenced instances which have used the current entity?
Take an IfcCartesianPoint for example, there is a way to get all entities that have used this point?
Assemblies and versions affected:
Using the latest stable build, Xbim.Essentials 5.1.323
Steps (or code) to reproduce the issue:
For this piece of IFC, placements #11, #14 have used point #7.
Just start with #7, could I retrieve a list with #11, #14 and others, if there are more entities that have referenced this point?
Minimal file to reproduce the issue:
This piece of IFC comes from the buildingSMART sample test files.
[Sample-Test-Files/cube-advanced-brep.ifc at master · buildingSMART/Sample-Test-Files (github.com)](https://github.com/buildingSMART/Sample-Test-Files/blob/master/IFC 4.0/BuildingSMARTSpec/cube-advanced-brep.ifc)
Expected behavior:
Get a list of instance or entity labels, who referenced or have used this instance.
Additional Details
I found a similar method Traverse() in the IfcOpenShell, basically I'm trying to accomplish the same thing.
IfcOpenShell/file.py at fc05bdf6d6465251fec8e279493d1bf5f3017617 · IfcOpenShell/IfcOpenShell (github.com)
The text was updated successfully, but these errors were encountered: