///|
fn relation_role(name : String) -> String {
match name.to_upper() {
"IFCRELAGGREGATES" => "aggregates"
"IFCRELCONTAINEDINSPATIALSTRUCTURE" => "contains"
"IFCRELDEFINESBYPROPERTIES" => "defines_properties"
"IFCRELASSOCIATESMATERIAL" => "associates_material"
"IFCRELASSOCIATESCLASSIFICATION" => "associates_classification"
"IFCRELCONNECTSELEMENTS" => "connects"
"IFCRELVOIDSELEMENT" => "voids"
"IFCRELFILLSELEMENT" => "fills"
_ => "relationship"
}
}
///|
fn relation_link_for(entity : Entity) -> RelationLink? {
let name = entity.name.to_upper()
let relating_position = match name {
"IFCRELAGGREGATES"
| "IFCRELCONTAINEDINSPATIALSTRUCTURE"
| "IFCRELDEFINESBYPROPERTIES"
| "IFCRELASSOCIATESMATERIAL"
| "IFCRELASSOCIATESCLASSIFICATION" => 5
"IFCRELCONNECTSELEMENTS" => 5
"IFCRELVOIDSELEMENT" => 5
_ => -1
}
let related_position = match name {
"IFCRELAGGREGATES"
| "IFCRELCONTAINEDINSPATIALSTRUCTURE"
| "IFCRELDEFINESBYPROPERTIES"
| "IFCRELASSOCIATESMATERIAL"
| "IFCRELASSOCIATESCLASSIFICATION" => 4
"IFCRELCONNECTSELEMENTS" | "IFCRELVOIDSELEMENT" => 4
_ => -1
}
if relating_position < 0 || related_position < 0 {
None
} else {
Some({
relation_id: entity.id,
relation_type: entity.name,
relating_id: optional_reference_argument(entity, relating_position),
related_ids: relation_objects(entity, related_position),
role: relation_role(entity.name),
})
}
}
///|
fn relation_add_map_value(
map : Map[Int, Array[Int]],
key : Int,
value : Int,
) -> Unit {
map.update_or_default(key, [value], values => {
if !values.contains(value) {
values.push(value)
}
values
})
}
///|
/// Build normalized relation maps from raw IFC relationship entities.
pub fn RelationIndex::new(index : ModelIndex) -> RelationIndex {
let links : Array[RelationLink] = []
let parents : Map[Int, Array[Int]] = Map([])
let children : Map[Int, Array[Int]] = Map([])
let containers : Map[Int, Int] = Map([])
let property_sets : Map[Int, Array[Int]] = Map([])
let classifications : Map[Int, Array[Int]] = Map([])
for entity in index.all() {
match relation_link_for(entity) {
Some(link) => {
links.push(link)
match link.role {
"aggregates" =>
match link.relating_id {
Some(parent) =>
for child in link.related_ids {
relation_add_map_value(children, parent, child)
relation_add_map_value(parents, child, parent)
}
None => ()
}
"contains" =>
match link.relating_id {
Some(container) =>
for product in link.related_ids {
containers[product] = container
}
None => ()
}
"defines_properties" =>
match link.relating_id {
Some(property_set) =>
for product in link.related_ids {
relation_add_map_value(property_sets, product, property_set)
}
None => ()
}
"associates_classification" =>
match link.relating_id {
Some(classification) =>
for product in link.related_ids {
relation_add_map_value(
classifications, product, classification,
)
}
None => ()
}
_ => ()
}
}
None => ()
}
}
{ links, parents, children, containers, property_sets, classifications }
}
///|
pub fn relation_index(model : Model) -> RelationIndex {
RelationIndex::new(model.index())
}
///|
pub fn RelationIndex::link_count(self : RelationIndex) -> Int {
self.links.length()
}
///|
pub fn RelationIndex::links(self : RelationIndex) -> Array[RelationLink] {
self.links.copy()
}
///|
pub fn RelationIndex::links_for(
self : RelationIndex,
id : Int,
) -> Array[RelationLink] {
self.links.filter(link => {
link.relating_id == Some(id) || link.related_ids.contains(id)
})
}
///|
pub fn RelationIndex::parent_ids(self : RelationIndex, id : Int) -> Array[Int] {
self.parents.get(id).unwrap_or([])
}
///|
pub fn RelationIndex::child_ids(self : RelationIndex, id : Int) -> Array[Int] {
self.children.get(id).unwrap_or([])
}
///|
pub fn RelationIndex::container_id(
self : RelationIndex,
product_id : Int,
) -> Int? {
self.containers.get(product_id)
}
///|
pub fn RelationIndex::property_set_ids(
self : RelationIndex,
product_id : Int,
) -> Array[Int] {
self.property_sets.get(product_id).unwrap_or([])
}
///|
pub fn RelationIndex::classification_ids(
self : RelationIndex,
product_id : Int,
) -> Array[Int] {
self.classifications.get(product_id).unwrap_or([])
}
///|
fn relation_unique_ids(values : Array[Int]) -> Array[Int] {
let result : Array[Int] = []
for value in values {
if !result.contains(value) {
result.push(value)
}
}
result
}
///|
pub fn RelationIndex::ancestors(self : RelationIndex, id : Int) -> Array[Int] {
let result : Array[Int] = []
let queue : Array[Int] = [id]
while queue.length() > 0 {
let current = queue.remove(0)
for parent in self.parent_ids(current) {
if !result.contains(parent) {
result.push(parent)
queue.push(parent)
}
}
}
result
}
///|
pub fn RelationIndex::descendants(self : RelationIndex, id : Int) -> Array[Int] {
let result : Array[Int] = []
let queue : Array[Int] = [id]
while queue.length() > 0 {
let current = queue.remove(0)
for child in self.child_ids(current) {
if !result.contains(child) {
result.push(child)
queue.push(child)
}
}
}
result
}
///|
pub fn RelationIndex::root_ids(self : RelationIndex) -> Array[Int] {
let result : Array[Int] = []
for parent in self.children {
if !self.parents.contains(parent.0) {
result.push(parent.0)
}
}
relation_unique_ids(result)
}
///|
pub fn RelationIndex::relationship_histogram(
self : RelationIndex,
) -> Map[String, Int] {
self.links.fold(init=Map([]), (result, link) => {
result.update_or_default(link.role, 1, count => count + 1)
result
})
}
///|
///|
pub fn RelationIndex::products_without_container(
self : RelationIndex,
products : Array[Product],
) -> Array[Int] {
products
.filter(product => !self.containers.contains(product.id))
.map(product => product.id)
}
///|
///|
pub fn RelationIndex::products_without_properties(
self : RelationIndex,
products : Array[Product],
) -> Array[Int] {
products
.filter(product => !self.property_sets.contains(product.id))
.map(product => product.id)
}
///|
pub fn RelationLink::is_many_to_one(self : RelationLink) -> Bool {
self.related_ids.length() > 1 && self.relating_id is Some(_)
}
///|
pub fn RelationLink::headline(self : RelationLink) -> String {
self.relation_type +
" #" +
self.relation_id.to_string() +
" " +
self.relating_id.map(id => "#" + id.to_string()).unwrap_or("-") +
" -> " +
self.related_ids.map(id => "#" + id.to_string()).join(",")
}
///|
pub fn RelationIndex::headline(self : RelationIndex) -> String {
"links=" +
self.links.length().to_string() +
" aggregates=" +
self.children.length().to_string() +
" containers=" +
self.containers.length().to_string() +
" property_products=" +
self.property_sets.length().to_string()
}
///|
pub fn RelationIndex::to_json(self : RelationIndex) -> String {
let links = self.links
.map(link => {
"{\"id\":" +
link.relation_id.to_string() +
",\"type\":\"" +
link.relation_type +
"\",\"role\":\"" +
link.role +
"\",\"relating_id\":" +
link.relating_id.map(id => id.to_string()).unwrap_or("null") +
",\"related_ids\":[" +
link.related_ids.map(id => id.to_string()).join(",") +
"]}"
})
.join(",")
"{\"link_count\":" +
self.links.length().to_string() +
",\"links\":[" +
links +
"]}"
}
///|
pub fn relation_links_for_product(
model : Model,
product_id : Int,
) -> Array[RelationLink] {
relation_index(model).links_for(product_id)
}
///|
pub fn relation_ancestors(model : Model, id : Int) -> Array[Int] {
relation_index(model).ancestors(id)
}
///|
pub fn relation_descendants(model : Model, id : Int) -> Array[Int] {
relation_index(model).descendants(id)
}
///|
pub fn relation_histogram(model : Model) -> Map[String, Int] {
relation_index(model).relationship_histogram()
}