261 lines
9.1 KiB
C#
261 lines
9.1 KiB
C#
using System.Diagnostics;
|
|
using Ara3D.Logging;
|
|
using Ara3D.Utils;
|
|
using Speckle.Importers.Ifc.Ara3D.IfcParser.Schema;
|
|
using Speckle.Importers.Ifc.Ara3D.StepParser;
|
|
|
|
namespace Speckle.Importers.Ifc.Ara3D.IfcParser;
|
|
|
|
/// <summary>
|
|
/// This is a high-level representation of an IFC model as a graph of nodes and relations.
|
|
/// It also contains the properties, and property sets.
|
|
/// </summary>
|
|
public sealed class IfcGraph
|
|
{
|
|
public static IfcGraph Load(FilePath fp, ILogger? logger = null) =>
|
|
new IfcGraph(new StepDocument(fp, logger), logger);
|
|
|
|
public StepDocument Document { get; }
|
|
|
|
public Dictionary<uint, IfcNode> Nodes { get; } = new Dictionary<uint, IfcNode>();
|
|
public List<IfcRelation> Relations { get; } = new List<IfcRelation>();
|
|
public Dictionary<uint, List<IfcRelation>> RelationsByNode { get; } = new Dictionary<uint, List<IfcRelation>>();
|
|
public Dictionary<uint, List<IfcPropSet>> PropertySetsByNode { get; } = new Dictionary<uint, List<IfcPropSet>>();
|
|
|
|
public uint IfcProjectId { get; }
|
|
|
|
public IfcNode AddNode(IfcNode n) => Nodes[n.Id] = n;
|
|
|
|
public IfcRelation AddRelation(IfcRelation r)
|
|
{
|
|
Relations.Add(r);
|
|
var id = r.From.Id;
|
|
if (!RelationsByNode.ContainsKey(id))
|
|
RelationsByNode[id] = new();
|
|
RelationsByNode[id].Add(r);
|
|
return r;
|
|
}
|
|
|
|
public IfcGraph(StepDocument d, ILogger? logger = null)
|
|
{
|
|
Document = d;
|
|
|
|
uint ifcProjectId = 0;
|
|
logger?.Log("Computing entities");
|
|
foreach (var inst in Document.RawInstances)
|
|
{
|
|
if (!inst.IsValid())
|
|
continue;
|
|
|
|
// Property Values
|
|
if (inst.Type.Equals("IFCPROPERTYSINGLEVALUE"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[2]));
|
|
}
|
|
else if (inst.Type.Equals("IFCPROPERTYENUMERATEDVALUE"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[2]));
|
|
}
|
|
else if (inst.Type.Equals("IFCPROPERTYREFERENCEVALUE"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[3]));
|
|
}
|
|
else if (inst.Type.Equals("IFCPROPERTYLISTVALUE"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[2]));
|
|
}
|
|
else if (inst.Type.Equals("IFCCOMPLEXPROPERTY"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[3]));
|
|
}
|
|
// Quantities which are a treated as a kind of prop
|
|
// https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcphysicalquantity.htm
|
|
else if (inst.Type.Equals("IFCQUANTITYLENGTH"))
|
|
{
|
|
// https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantitylength.htm
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[3]));
|
|
}
|
|
else if (inst.Type.Equals("IFCQUANTITYAREA"))
|
|
{
|
|
// https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantityarea.htm
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[3]));
|
|
}
|
|
else if (inst.Type.Equals("IFCQUANTITYVOLUME"))
|
|
{
|
|
// https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantityvolume.htm
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[3]));
|
|
}
|
|
else if (inst.Type.Equals("IFCQUANTITYCOUNT"))
|
|
{
|
|
// https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantitycount.htm
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[3]));
|
|
}
|
|
else if (inst.Type.Equals("IFCQUANTITYWEIGHT"))
|
|
{
|
|
// https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantityweight.htm
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[3]));
|
|
}
|
|
else if (inst.Type.Equals("IFCQUANTITYTIME"))
|
|
{
|
|
// https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcquantitytime.htm
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[3]));
|
|
}
|
|
else if (inst.Type.Equals("IFCPHYSICALCOMPLEXQUANTITY"))
|
|
{
|
|
//https://iaiweb.lbl.gov/Resources/IFC_Releases/R2x3_final/ifcquantityresource/lexical/ifcphysicalcomplexquantity.htm
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcProp(this, e, e[2]));
|
|
}
|
|
// Property Set (or element quantity)
|
|
else if (inst.Type.Equals("IFCPROPERTYSET"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcPropSet(this, e, (StepList)e[4]));
|
|
}
|
|
else if (inst.Type.Equals("IFCELEMENTQUANTITY"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcPropSet(this, e, e[5] as StepList));
|
|
}
|
|
// Aggregate relation
|
|
else if (inst.Type.Equals("IFCRELAGGREGATES"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddRelation(new IfcRelationAggregate(this, e, (StepId)e[4], (StepList)e[5]));
|
|
}
|
|
// Spatial relation
|
|
else if (inst.Type.Equals("IFCRELCONTAINEDINSPATIALSTRUCTURE"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddRelation(new IfcRelationSpatial(this, e, (StepId)e[5], (StepList)e[4]));
|
|
}
|
|
// Property set relations
|
|
else if (inst.Type.Equals("IFCRELDEFINESBYPROPERTIES"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddRelation(new IfcPropSetRelation(this, e, (StepId)e[5], (StepList)e[4]));
|
|
}
|
|
// Type relations
|
|
else if (inst.Type.Equals("IFCRELDEFINESBYTYPE"))
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddRelation(new IfcRelationType(this, e, (StepId)e[5], (StepList)e[4]));
|
|
}
|
|
else if (inst.Type.Equals("IFCPROJECT"))
|
|
{
|
|
//Special case for IFC Projects, track them as a root node.
|
|
var e = d.GetInstanceWithData(inst);
|
|
ifcProjectId = inst.Id;
|
|
AddNode(new IfcProject(this, e));
|
|
}
|
|
else if (
|
|
inst.Type.Equals("IFCSITE")
|
|
|| inst.Type.Equals("IFCBUILDING")
|
|
|| inst.Type.Equals("IFCBUILDINGSTOREY")
|
|
|| inst.Type.Equals("IFCFACILITY")
|
|
|| inst.Type.Equals("IFCFACILITYPART")
|
|
|| inst.Type.Equals("IFCBRIDGE")
|
|
|| inst.Type.Equals("IFCROAD")
|
|
|| inst.Type.Equals("IFCRAILWAY")
|
|
|| inst.Type.Equals("IFCMARINEFACILITY")
|
|
)
|
|
{
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcSpatialStructureElement(this, e));
|
|
}
|
|
// Everything else
|
|
else
|
|
{
|
|
// Simple IFC node: without step entity data.
|
|
var e = d.GetInstanceWithData(inst);
|
|
AddNode(new IfcNode(this, e));
|
|
}
|
|
}
|
|
|
|
if (ifcProjectId <= 0)
|
|
throw new SpeckleIfcException("There was no IfcProject in the file");
|
|
|
|
IfcProjectId = ifcProjectId;
|
|
|
|
logger?.Log("Creating lookup of property sets");
|
|
|
|
foreach (var psr in Relations.OfType<IfcPropSetRelation>())
|
|
{
|
|
var ps = psr.PropSet;
|
|
foreach (var id in psr.GetRelatedIds())
|
|
{
|
|
if (!PropertySetsByNode.ContainsKey(id))
|
|
PropertySetsByNode[id] = [];
|
|
PropertySetsByNode[id].Add(ps);
|
|
}
|
|
}
|
|
|
|
logger?.Log("Completed creating model graph");
|
|
}
|
|
|
|
public IEnumerable<IfcNode> GetNodes() => Nodes.Values;
|
|
|
|
public IEnumerable<IfcNode> GetNodes(IEnumerable<uint> ids) => ids.Select(GetNode);
|
|
|
|
public IfcNode GetOrCreateNode(StepInstance lineData, int arg)
|
|
{
|
|
if (arg < 0 || arg >= lineData.AttributeValues.Count)
|
|
throw new SpeckleIfcException("Argument index out of range");
|
|
return GetOrCreateNode(lineData.AttributeValues[arg]);
|
|
}
|
|
|
|
public IfcNode GetOrCreateNode(StepValue o) =>
|
|
GetOrCreateNode(o is StepId id ? id.Id : throw new SpeckleIfcException($"Expected a StepId value, not {o}"));
|
|
|
|
public IfcNode GetOrCreateNode(uint id)
|
|
{
|
|
var r = Nodes.TryGetValue(id, out var node) ? node : AddNode(new IfcNode(this, Document.GetInstanceWithData(id)));
|
|
Debug.Assert(r.Id == id);
|
|
return r;
|
|
}
|
|
|
|
public List<IfcNode> GetOrCreateNodes(List<StepValue> list) => list.Select(GetOrCreateNode).ToList();
|
|
|
|
public List<IfcNode> GetOrCreateNodes(StepInstance line, int arg)
|
|
{
|
|
if (arg < 0 || arg >= line.AttributeValues.Count)
|
|
throw new SpeckleIfcException("Argument out of range");
|
|
if (line.AttributeValues[arg] is not StepList agg)
|
|
throw new SpeckleIfcException("Expected a list");
|
|
return GetOrCreateNodes(agg.Values);
|
|
}
|
|
|
|
public IfcNode GetNode(StepId id) => GetNode(id.Id);
|
|
|
|
public IfcNode GetNode(uint id)
|
|
{
|
|
var r = Nodes[id];
|
|
Debug.Assert(r.Id == id);
|
|
return r;
|
|
}
|
|
|
|
public IfcNode GetIfcProject() => GetNode(IfcProjectId);
|
|
|
|
public IEnumerable<IfcPropSet> GetPropSets() => GetNodes().OfType<IfcPropSet>();
|
|
|
|
public IEnumerable<IfcProp> GetProps() => GetNodes().OfType<IfcProp>();
|
|
|
|
public IEnumerable<IfcRelationSpatial> GetSpatialRelations() => Relations.OfType<IfcRelationSpatial>();
|
|
|
|
public IEnumerable<IfcRelationAggregate> GetAggregateRelations() => Relations.OfType<IfcRelationAggregate>();
|
|
|
|
public IReadOnlyList<IfcRelation> GetRelationsFrom(uint id) =>
|
|
RelationsByNode.TryGetValue(id, out var list) ? list : Array.Empty<IfcRelation>();
|
|
}
|