1#[cfg(test)]
11#[macro_use]
12mod test_support;
13
14mod adjacency;
15mod collada;
16mod collada_document;
17mod collada_fmt;
18mod color_space;
19mod constructions;
20mod csv;
21pub mod csv_cell;
25mod dfjson;
26mod error;
27mod file_schema;
28mod frame;
29mod generated;
30mod geom;
31mod gltf;
32mod hbjson;
33mod ifc5;
34mod json;
35mod jsonld;
36mod kmz;
37mod landxml_terrain;
38mod merged;
39mod mesh_input;
40mod model;
41mod quantity_analysis;
42mod quantity_analysis_derived;
43mod obj;
44mod relationships;
45mod rebar_attributes;
46mod rebar_schedule;
47mod openings;
48#[cfg(feature = "parquet-bos")]
49mod parquet_bos;
50mod rooms;
51pub mod rooted_type;
52mod schema_convert;
53mod schema_detect;
54mod schema_ifc2x3_slots;
55mod schema_enum;
56mod schema_ifc4_slots;
57mod schema_owner_history;
58mod schema_pad;
59mod schema_unrepresented;
60pub use schema_pad::padded_type_universe;
61mod shades;
62pub mod source_header;
63mod step;
64mod step_api;
65mod step_cow;
66mod step_header;
67mod step_json;
68mod step_log;
69mod step_slot;
70mod step_text;
71mod step_map_transform;
72pub use step_map_transform::{plan_map_conversion_normalization, MapConversionEntityPatch, MapConversionNormalizationPlan};
73mod usd;
74mod xml_text;
75
76pub use step_text::escape as escape_step_string;
78
79pub use collada::try_export_collada_from_meshes;
80pub use csv::{export_csv, CsvMode, CsvOptions};
81pub use dfjson::DfjsonStats;
82pub use error::ExportError;
83pub use gltf::{
84 export_glb_from_meshes, export_gltf_streaming, export_gltf_streaming_with_index,
85 project_glb_size, project_glb_size_with_index, try_export_glb, try_export_glb_from_meshes,
86 try_export_glb_streaming_bounded, try_export_glb_streaming_bounded_with_index,
87 try_export_glb_with_stats, try_export_glb_with_stats_with_index, GlbSizeProjection,
88 GltfBuffer, GltfOptions, GltfStats,
89};
90pub use hbjson::Model;
91pub use ifc_lite_core::{build_entity_index, entity_count, EntityIndex};
100pub use ifc_lite_core::{AttributeValue, DecodedEntity, IfcType};
103pub use ifc_lite_processing::prepass::UnitScales;
107pub use ifc_lite_processing::TessellationQuality;
109pub use ifc5::{export_ifc5, Ifc5Options};
110pub use relationships::{relationships, Relationships};
113mod rebar_preflight;
114mod rebar_fabrication;
115pub use rebar_fabrication::{RebarFabricationCheck, RebarFabricationCheckStatus,
116 RebarFabricationPolicy, RebarFabricationPolicyError, RebarFabricationReport,
117 RebarScheduleFabricationError};
118pub use rebar_preflight::{RebarPreflightComparison, RebarPreflightError, RebarPreflightLimits,
119 RebarPreflightReport, RebarSchedulePreflightError};
120pub use rebar_schedule::{build_rebar_schedule, build_rebar_schedule_with_preflight,
121 build_rebar_schedule_with_fabrication_precheck, AuthoredRebarAttribute, AuthoredRebarValue,
122 RebarSchedule, RebarScheduleRow, RebarSource, RebarSweep};
123pub use json::{export_json, JsonOptions};
124pub use landxml_terrain::{
125 try_export_landxml_terrain, LandXmlLinearUnit, LandXmlTerrainExport, LandXmlTerrainOptions,
126 LandXmlTerrainRefusal, LandXmlTerrainSurface,
127};
128pub use jsonld::{export_jsonld, export_jsonld_with_filter, JsonLdOptions};
129pub use kmz::{
130 ifc_angle_to_kml_heading, try_export_kmz_collada_from_meshes, AltitudeMode, KmzOptions,
131};
132pub use merged::{
133 deterministic_global_id, export_merged, export_merged_models, export_merged_with_stats,
134 leading_rooted_global_id, ContainerMergeStrategy, MergedModel, MergedOptions, MergedStats,
135 StoreyMergeStrategy, UnitReconciliation,
136};
137pub use model::{
138 build_export_model, build_export_model_with_options, stream_export_model,
139 stream_export_model_with_index, stream_export_model_with_options, EntityRow, ExportModel,
140 ModelOptions, Placement, PropValue, PropertySet, QuantitySet, QuantityValue,
141};
142pub use quantity_analysis_derived::{analyze_quantities, DerivedQuantity,
143 ProductQuantityAnalysis, QuantityAnalysis, QuantitySourceOccurrence};
144pub use quantity_analysis::{analyze_authored_quantities, AuthoredQuantity,
145 AuthoredQuantityAnalysis, ProductQuantities, QuantityConflict, QuantityUnit};
146pub use obj::{export_obj, export_obj_with_stats, ObjOptions, ObjStats};
147#[cfg(feature = "parquet-bos")]
148pub use parquet_bos::{export_bos, ParquetBosOptions};
149pub use step::{
150 export_step, export_step_to_writer, export_step_with_stats, AttrMutation, CopyOnWriteMutation,
151 PropMutation, StepOptions, StepStats,
152};
153pub use step_json::export_step_json;
154pub use step_log::{
155 export_merged_models_with_logs, export_step_with_log, export_step_with_log_to_writer, GeorefMutations, LogExportStats, LogMutation,
156 LogNewEntity, MutationKind, MutationLog, StepCounters,
157};
158pub use usd::{export_usd, UsdOptions};
159
160use ifc_lite_geometry::extract_profiles;
161
162fn sanitize_identifier(s: &str) -> String {
165 let out: String = s
166 .chars()
167 .map(|c| if c.is_alphanumeric() || c == '_' || c == '-' { c } else { '_' })
168 .collect();
169 if out.is_empty() { "model".to_string() } else { out }
170}
171
172pub struct HbjsonOptions {
174 pub name: String,
176 pub tolerance: f64,
178}
179
180impl Default for HbjsonOptions {
181 fn default() -> Self {
182 Self { name: "ifc_lite_model".to_string(), tolerance: 0.01 }
183 }
184}
185
186#[derive(Debug, Clone, serde::Serialize)]
188#[serde(rename_all = "camelCase")]
189pub struct HbjsonStats {
190 pub spaces: usize,
192 pub rooms: usize,
194 pub skipped: usize,
196 pub apertures: usize,
198 pub doors: usize,
200 pub shades: usize,
202 pub constructions: usize,
204 pub interior_adjacencies: usize,
206}
207
208pub fn export_hbjson(content: &[u8], opts: &HbjsonOptions) -> String {
214 export_hbjson_with_stats(content, opts).0
215}
216
217pub fn export_hbjson_with_stats(content: &[u8], opts: &HbjsonOptions) -> (String, HbjsonStats) {
220 let profiles = extract_profiles(content, 0);
221 let spaces = profiles.iter().filter(|p| p.ifc_type == "IfcSpace").count();
222 let (mut rooms, origin, skipped) = rooms::build_rooms(&profiles, opts.tolerance);
223 openings::attach_openings(&profiles, &mut rooms, origin);
224 let interior_adjacencies = adjacency::solve_adjacency(&mut rooms);
226 let shade_meshes = shades::build_shades(&profiles, origin);
227
228 let cons = constructions::build_constructions(content, &profiles);
230 for room in &mut rooms {
231 for f in &mut room.faces {
232 let id = match f.face_type {
233 "Wall" => cons.wall.clone(),
234 "Floor" => cons.floor.clone(),
235 "RoofCeiling" => cons.roof.clone(),
236 _ => None,
237 };
238 if let Some(id) = id {
239 f.set_construction(id);
240 }
241 }
242 }
243
244 let apertures = rooms.iter().flat_map(|r| &r.faces).map(|f| f.apertures.len()).sum();
245 let doors = rooms.iter().flat_map(|r| &r.faces).map(|f| f.doors.len()).sum();
246 let shades = shade_meshes.len();
247 let n_constructions = cons.energy.as_ref().map_or(0, |e| e.constructions.len());
248 let stats = HbjsonStats { spaces, rooms: rooms.len(), skipped, apertures, doors, shades, constructions: n_constructions, interior_adjacencies };
249
250 let model = Model::new(&sanitize_identifier(&opts.name), rooms, shade_meshes, cons.energy, opts.tolerance);
251 let json = serde_json::to_string(&model).expect("HBJSON model serializes");
252 (json, stats)
253}
254
255pub struct DfjsonOptions {
257 pub name: String,
259 pub tolerance: f64,
261}
262
263impl Default for DfjsonOptions {
264 fn default() -> Self {
265 Self { name: "ifc_lite_model".to_string(), tolerance: 0.01 }
266 }
267}
268
269pub fn export_dfjson(content: &[u8], opts: &DfjsonOptions) -> String {
273 export_dfjson_with_stats(content, opts).0
274}
275
276pub fn export_dfjson_with_stats(content: &[u8], opts: &DfjsonOptions) -> (String, DfjsonStats) {
278 let profiles = extract_profiles(content, 0);
279 let spatial = dfjson::spatial_index(content);
284 let (model, stats) =
285 dfjson::build_model(&sanitize_identifier(&opts.name), &profiles, opts.tolerance, Some(&spatial));
286 let json = serde_json::to_string(&model).expect("DFJSON model serializes");
287 (json, stats)
288}
289
290#[cfg(test)]
291#[path = "lib_tests.rs"]
292mod tests;