@@ -464,11 +464,15 @@ extension Database {
464
464
public var type : String ?
465
465
public var familyName : String ?
466
466
public var category : Category ?
467
+ public var level : Level ?
468
+ public var room : Room ?
469
+ public var group : Group ?
467
470
public var workset : Workset ?
468
471
public var parameters : [ Parameter ]
469
472
470
473
/// Returns the elements instance type
471
474
public var instanceType : Element ? {
475
+ guard let name, let type else { return nil }
472
476
let predicate = #Predicate< Database. Element> { $0. name == name && $0. type != type }
473
477
let fetchDescriptor = FetchDescriptor < Database . Element > ( predicate: predicate)
474
478
guard let results = try ? modelContext? . fetch ( fetchDescriptor) , results. isNotEmpty else {
@@ -507,6 +511,15 @@ extension Database {
507
511
if let idx = data [ " Category " ] as? Int64 , idx != . empty {
508
512
category = cache. findOrCreate ( idx)
509
513
}
514
+ if let idx = data [ " Level " ] as? Int64 , idx != . empty {
515
+ level = cache. findOrCreate ( idx)
516
+ }
517
+ if let idx = data [ " Room " ] as? Int64 , idx != . empty {
518
+ room = cache. findOrCreate ( idx)
519
+ }
520
+ if let idx = data [ " Group " ] as? Int64 , idx != . empty {
521
+ group = cache. findOrCreate ( idx)
522
+ }
510
523
if let idx = data [ " Workset " ] as? Int64 , idx != . empty {
511
524
workset = cache. findOrCreate ( idx)
512
525
}
@@ -1050,146 +1063,97 @@ extension Database {
1050
1063
}
1051
1064
1052
1065
/// Provides an observable model tree
1053
- @MainActor
1054
- public class ModelTree : ObservableObject {
1055
-
1056
- public enum TreeError : Error {
1057
- case empty
1058
- case notFound
1059
- }
1060
-
1061
- public class Item : Identifiable , Hashable {
1066
+ @Observable @MainActor
1067
+ public class ModelTree {
1062
1068
1063
- public enum ItemType {
1064
- case category
1065
- case family
1066
- case type
1067
- case instance
1068
- }
1069
+ /// The title of the bim document
1070
+ public var title : String = . empty
1069
1071
1070
- public var id : Int64
1071
- public var name : String
1072
- public var type : ItemType
1073
- public var children = [ Item] ( )
1072
+ /// The top level categories
1073
+ public var categories = [ String] ( )
1074
1074
1075
- init ( id: Int64 , name: String , type: ItemType , children: [ Item ] = [ ] ) {
1076
- self . id = id
1077
- self . name = name
1078
- self . type = type
1079
- self . children = children
1080
- }
1075
+ /// A hash of unique families as the key and it's corresponding category.
1076
+ public var families = [ String: String] ( )
1081
1077
1082
- func contains( _ text: String ) -> Bool {
1083
- for child in children {
1084
- if child. contains ( text) {
1085
- return true
1086
- }
1087
- }
1088
- return name. lowercased ( ) . contains ( text)
1089
- }
1090
-
1091
- public func hash( into hasher: inout Hasher ) {
1092
- hasher. combine ( id)
1093
- hasher. combine ( name)
1094
- hasher. combine ( type)
1095
- hasher. combine ( children)
1096
- }
1097
-
1098
- public static func == ( lhs: ModelTree . Item , rhs: ModelTree . Item ) -> Bool {
1099
- lhs. id == rhs. id && lhs. type == rhs. type && lhs. name == rhs. name
1100
- }
1101
- }
1078
+ /// A hash of unique types as the key and it's corresponding family.
1079
+ public var types = [ String: String] ( )
1102
1080
1103
- @ Published
1104
- public var results : Result < [ Item ] , TreeError > = . failure ( . empty )
1081
+ /// A hash of unique instance ids as the key and it's type name.
1082
+ public var instances = [ Int64 : String ] ( )
1105
1083
1106
- @ Published
1107
- public var items = [ Item ] ( )
1084
+ /// A hash of elementIDs to their corresponding node indices (used for quick lookup back to the geometry).
1085
+ public var elementNodes : [ Int64 : Int64 ] = [ : ]
1108
1086
1109
1087
/// Initializer.
1110
1088
public init ( ) { }
1111
1089
1112
- /// Loads the tree from the bottom up.
1113
- /// The Hierarchy `Category > Family > Type > Instance`
1114
- /// - Parameter familyInstances: the family instances.
1115
- public func load( _ familyInstances: [ Database . FamilyInstance ] ) async {
1116
-
1117
- //////////////////////////////////////////
1118
- /// CATEGORY = type.category.name
1119
- /// FAMILY = instance.familyName
1120
- /// TYPE = type.name
1121
- /// INSTANCE = instance.name + elementID
1122
- //////////////////////////////////////////
1123
- struct Holder {
1124
- let categoryID : Int64
1125
- let categoryName : String
1126
- let familyID : Int64
1127
- let familyName : String
1128
- let typeID : Int64
1129
- let typeName : String
1130
- let instanceID : Int64
1131
- let instanceName : String
1090
+ /// Loads the model tree with a hierarchy that mirrors the Revit hierarchy of
1091
+ /// `Category > Family > Type > Instance`
1092
+ /// - Parameters:
1093
+ /// - modelContext: the model context to use
1094
+ public func load( modelContext: ModelContext ) async {
1095
+
1096
+ // Fetch the title from the bim document entity
1097
+ var documentDescriptor = FetchDescriptor < Database . BimDocument > ( sortBy: [ SortDescriptor ( \. index) ] )
1098
+ documentDescriptor. fetchLimit = 1
1099
+ let documents = try ? modelContext. fetch ( documentDescriptor)
1100
+ title = documents? . first? . title ?? . empty
1101
+
1102
+ // Fetch the nodes to build the tree structure
1103
+ let descriptor = FetchDescriptor < Database . Node > ( sortBy: [ SortDescriptor ( \. index) ] )
1104
+ let results = try ! modelContext. fetch ( descriptor)
1105
+
1106
+ // Map the node elementIDs to their index
1107
+ elementNodes = results. reduce ( into: [ Int64: Int64] ( ) ) { result, node in
1108
+ if let element = node. element {
1109
+ result [ element. elementId] = node. index
1110
+ }
1132
1111
}
1133
1112
1134
- var categories = [ String: Item] ( )
1135
-
1136
- // Build the tree TODO: This should be reworked - not very efficient
1137
- for instance in familyInstances {
1138
-
1139
- guard let element = instance. element,
1140
- let instanceName = element. name,
1141
- let familyName = element. familyName else { continue }
1142
-
1143
- guard let typeElement = instance. element? . instanceType,
1144
- let typeName = typeElement. name,
1145
- let category = typeElement. category else { continue }
1113
+ // Top level categories
1114
+ categories = results. compactMap { $0. element? . category? . name } . uniqued ( ) . sorted { $0 < $1 }
1146
1115
1147
- let displayName = " \( instanceName) [ \( element. elementId) ] "
1148
- let holder = Holder ( categoryID: category. index, categoryName: category. name, familyID: element. index, familyName: familyName, typeID: typeElement. index, typeName: typeName, instanceID: element. index, instanceName: displayName)
1149
-
1150
- // Category
1151
- if categories [ holder. categoryName] == nil {
1152
- categories [ holder. categoryName] = Item ( id: holder. categoryID, name: holder. categoryName, type: . category)
1116
+ // The hash of families and their category
1117
+ families = results. reduce ( into: [ String: String] ( ) ) { result, node in
1118
+ if let categoryName = node. element? . category? . name, let familyName = node. element? . familyName, familyName. isNotEmpty {
1119
+ result [ familyName] = categoryName
1153
1120
}
1154
- guard let category = categories [ holder . categoryName ] else { continue }
1121
+ }
1155
1122
1156
- // Family
1157
- if category. children. filter ( { $0. name == holder. familyName } ) . isEmpty {
1158
- category. children. append ( Item ( id: holder. familyID, name: holder. familyName, type: . family) )
1123
+ // The hash of types and their family
1124
+ types = results. reduce ( into: [ String: String] ( ) ) { result, node in
1125
+ if let familyName = node. element? . familyName, familyName. isNotEmpty, let name = node. element? . name {
1126
+ result [ name] = familyName
1159
1127
}
1160
- guard let family = category . children . filter ( { $0 . name == holder . familyName } ) . first else { continue }
1128
+ }
1161
1129
1162
- // Type
1163
- if family. children. filter ( { $0. name == holder. typeName } ) . isEmpty {
1164
- family. children. append ( Item ( id: holder. typeID, name: holder. typeName, type: . type) )
1130
+ // The hash of instances and their name
1131
+ instances = results. reduce ( into: [ Int64: String] ( ) ) { result, node in
1132
+ if let element = node. element, let name = element. name {
1133
+ result [ element. elementId] = name
1165
1134
}
1166
- guard let type = family. children. filter ( { $0. name == holder. typeName } ) . first else { continue }
1167
-
1168
- // Instance
1169
- let instanceItem = Item ( id: holder. instanceID, name: holder. instanceName, type: . instance)
1170
-
1171
- type. children. append ( instanceItem)
1172
1135
}
1136
+ }
1173
1137
1174
- items = Array ( categories. values. filter { $0. children. isNotEmpty } . sorted { $0. name < $1. name } )
1175
- results = . success( items)
1138
+ /// Returns an array of families for the specified category
1139
+ /// - Parameter category: the category name
1140
+ /// - Returns: a sorted array of unique family names in the specified category
1141
+ public func families( in category: String ) -> [ String ] {
1142
+ families. filter { $0. value == category } . keys. sorted { $0 < $1 }
1176
1143
}
1177
1144
1178
- /// Performs a search for model items that contain the following text.
1179
- /// - Parameter text: the search text
1180
- public func search( _ text: String ) async {
1181
- results = . failure( . empty)
1182
- guard items. isNotEmpty else { return }
1183
- guard text. isNotEmpty else {
1184
- results = . success( items)
1185
- return
1186
- }
1187
- let hits = items. filter { $0. contains ( text) }
1188
- guard hits. isNotEmpty else {
1189
- results = . failure( . notFound)
1190
- return
1191
- }
1192
- results = . success( hits)
1145
+ /// Returns an array of types for the specified family
1146
+ /// - Parameter family: the family name
1147
+ /// - Returns: a sorted array of unique tyes for the specified family
1148
+ public func types( in family: String ) -> [ String ] {
1149
+ types. filter { $0. value == family } . keys. sorted { $0 < $1 }
1150
+ }
1151
+
1152
+ /// Returns an array of instances for the specified type
1153
+ /// - Parameter type: the type name
1154
+ /// - Returns: a sorted array of instance id's for the specified type
1155
+ public func instances( in type: String ) -> [ Int64 ] {
1156
+ instances. filter { $0. value == type } . keys. sorted { $0 < $1 }
1193
1157
}
1194
1158
}
1195
1159
}
0 commit comments