IFCLoader.js 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. import {
  2. IFCRELAGGREGATES,
  3. IFCRELCONTAINEDINSPATIALSTRUCTURE,
  4. IFCRELDEFINESBYPROPERTIES,
  5. IFCRELDEFINESBYTYPE,
  6. IFCPROJECT,
  7. IfcAPI,
  8. } from './ifc/web-ifc-api.js';
  9. import {
  10. BufferAttribute,
  11. BufferGeometry,
  12. Mesh,
  13. Matrix4,
  14. Color,
  15. MeshLambertMaterial,
  16. DoubleSide,
  17. Group,
  18. Loader,
  19. FileLoader,
  20. } from '../../../build/three.module.js';
  21. import * as BufferGeometryUtils from '../utils/BufferGeometryUtils.js';
  22. const IdAttrName = 'expressID';
  23. const merge = ( geoms, createGroups = false ) => {
  24. return BufferGeometryUtils.mergeBufferGeometries( geoms, createGroups );
  25. };
  26. const newFloatAttr = ( data, size ) => {
  27. return new BufferAttribute( new Float32Array( data ), size );
  28. };
  29. const newIntAttr = ( data, size ) => {
  30. return new BufferAttribute( new Uint32Array( data ), size );
  31. };
  32. const DEFAULT = 'default';
  33. const PropsNames = {
  34. aggregates: {
  35. name: IFCRELAGGREGATES,
  36. relating: 'RelatingObject',
  37. related: 'RelatedObjects',
  38. key: 'children',
  39. },
  40. spatial: {
  41. name: IFCRELCONTAINEDINSPATIALSTRUCTURE,
  42. relating: 'RelatingStructure',
  43. related: 'RelatedElements',
  44. key: 'children',
  45. },
  46. psets: {
  47. name: IFCRELDEFINESBYPROPERTIES,
  48. relating: 'RelatingPropertyDefinition',
  49. related: 'RelatedObjects',
  50. key: 'hasPsets',
  51. },
  52. type: {
  53. name: IFCRELDEFINESBYTYPE,
  54. relating: 'RelatingType',
  55. related: 'RelatedObjects',
  56. key: 'hasType',
  57. },
  58. };
  59. class IFCParser {
  60. constructor( state ) {
  61. this.currentID = - 1;
  62. this.state = state;
  63. }
  64. async parse( buffer ) {
  65. if ( this.state.api.wasmModule === undefined ) await this.state.api.Init();
  66. this.currentID = this.newIfcModel( buffer );
  67. return this.loadAllGeometry();
  68. }
  69. initializeMeshBVH( computeBoundsTree, disposeBoundsTree, acceleratedRaycast ) {
  70. this.computeBoundsTree = computeBoundsTree;
  71. this.disposeBoundsTree = disposeBoundsTree;
  72. this.acceleratedRaycast = acceleratedRaycast;
  73. this.setupThreeMeshBVH();
  74. }
  75. setupThreeMeshBVH() {
  76. if (
  77. ! this.computeBoundsTree ||
  78. ! this.disposeBoundsTree ||
  79. ! this.acceleratedRaycast
  80. )
  81. return;
  82. BufferGeometry.prototype.computeBoundsTree = this.computeBoundsTree;
  83. BufferGeometry.prototype.disposeBoundsTree = this.disposeBoundsTree;
  84. Mesh.prototype.raycast = this.acceleratedRaycast;
  85. }
  86. applyThreeMeshBVH( geometry ) {
  87. if ( this.computeBoundsTree ) geometry.computeBoundsTree();
  88. }
  89. newIfcModel( buffer ) {
  90. const data = new Uint8Array( buffer );
  91. const modelID = this.state.api.OpenModel( data );
  92. this.state.models[ modelID ] = {
  93. modelID,
  94. mesh: {},
  95. items: {},
  96. types: {},
  97. };
  98. return modelID;
  99. }
  100. loadAllGeometry() {
  101. this.saveAllPlacedGeometriesByMaterial();
  102. return this.generateAllGeometriesByMaterial();
  103. }
  104. generateAllGeometriesByMaterial() {
  105. const { geometry, materials } = this.getGeometryAndMaterials();
  106. this.applyThreeMeshBVH( geometry );
  107. const mesh = new Mesh( geometry, materials );
  108. mesh.modelID = this.currentID;
  109. this.state.models[ this.currentID ].mesh = mesh;
  110. return mesh;
  111. }
  112. getGeometryAndMaterials() {
  113. const items = this.state.models[ this.currentID ].items;
  114. const mergedByMaterial = [];
  115. const materials = [];
  116. for ( const materialID in items ) {
  117. materials.push( items[ materialID ].material );
  118. const geometries = Object.values( items[ materialID ].geometries );
  119. mergedByMaterial.push( merge( geometries ) );
  120. }
  121. const geometry = merge( mergedByMaterial, true );
  122. return {
  123. geometry,
  124. materials,
  125. };
  126. }
  127. saveAllPlacedGeometriesByMaterial() {
  128. const flatMeshes = this.state.api.LoadAllGeometry( this.currentID );
  129. for ( let i = 0; i < flatMeshes.size(); i ++ ) {
  130. const flatMesh = flatMeshes.get( i );
  131. const placedGeom = flatMesh.geometries;
  132. for ( let j = 0; j < placedGeom.size(); j ++ ) {
  133. this.savePlacedGeometry( placedGeom.get( j ), flatMesh.expressID );
  134. }
  135. }
  136. }
  137. savePlacedGeometry( placedGeometry, id ) {
  138. const geometry = this.getBufferGeometry( placedGeometry );
  139. geometry.computeVertexNormals();
  140. const matrix = this.getMeshMatrix( placedGeometry.flatTransformation );
  141. geometry.applyMatrix4( matrix );
  142. this.saveGeometryByMaterial( geometry, placedGeometry, id );
  143. }
  144. getBufferGeometry( placed ) {
  145. const geometry = this.state.api.GetGeometry(
  146. this.currentID,
  147. placed.geometryExpressID
  148. );
  149. const vertexData = this.getVertices( geometry );
  150. const indices = this.getIndices( geometry );
  151. const { vertices, normals } = this.extractVertexData( vertexData );
  152. return this.ifcGeomToBufferGeom( vertices, normals, indices );
  153. }
  154. getVertices( geometry ) {
  155. const vData = geometry.GetVertexData();
  156. const vDataSize = geometry.GetVertexDataSize();
  157. return this.state.api.GetVertexArray( vData, vDataSize );
  158. }
  159. getIndices( geometry ) {
  160. const iData = geometry.GetIndexData();
  161. const iDataSize = geometry.GetIndexDataSize();
  162. return this.state.api.GetIndexArray( iData, iDataSize );
  163. }
  164. getMeshMatrix( matrix ) {
  165. const mat = new Matrix4();
  166. mat.fromArray( matrix );
  167. return mat;
  168. }
  169. ifcGeomToBufferGeom( vertices, normals, indexData ) {
  170. const geometry = new BufferGeometry();
  171. geometry.setAttribute( 'position', newFloatAttr( vertices, 3 ) );
  172. geometry.setAttribute( 'normal', newFloatAttr( normals, 3 ) );
  173. geometry.setIndex( new BufferAttribute( indexData, 1 ) );
  174. return geometry;
  175. }
  176. extractVertexData( vertexData ) {
  177. const vertices = [];
  178. const normals = [];
  179. let isNormalData = false;
  180. for ( let i = 0; i < vertexData.length; i ++ ) {
  181. isNormalData ? normals.push( vertexData[ i ] ) : vertices.push( vertexData[ i ] );
  182. if ( ( i + 1 ) % 3 == 0 ) isNormalData = ! isNormalData;
  183. }
  184. return {
  185. vertices,
  186. normals,
  187. };
  188. }
  189. saveGeometryByMaterial( geom, placedGeom, id ) {
  190. const color = placedGeom.color;
  191. const colorID = `${color.x}${color.y}${color.z}${color.w}`;
  192. this.storeGeometryAttribute( id, geom );
  193. this.createMaterial( colorID, color );
  194. const item = this.state.models[ this.currentID ].items[ colorID ];
  195. const currentGeom = item.geometries[ id ];
  196. if ( ! currentGeom ) return ( item.geometries[ id ] = geom );
  197. const merged = merge( [ currentGeom, geom ] );
  198. item.geometries[ id ] = merged;
  199. }
  200. storeGeometryAttribute( id, geometry ) {
  201. const size = geometry.attributes.position.count;
  202. const idAttribute = new Array( size ).fill( id );
  203. geometry.setAttribute( IdAttrName, newIntAttr( idAttribute, 1 ) );
  204. }
  205. createMaterial( colorID, color ) {
  206. const items = this.state.models[ this.currentID ].items;
  207. if ( items[ colorID ] ) return;
  208. const col = new Color( color.x, color.y, color.z );
  209. const newMaterial = new MeshLambertMaterial( {
  210. color: col,
  211. side: DoubleSide,
  212. } );
  213. newMaterial.transparent = color.w !== 1;
  214. if ( newMaterial.transparent ) newMaterial.opacity = color.w;
  215. items[ colorID ] = {
  216. material: newMaterial,
  217. geometries: {},
  218. };
  219. }
  220. }
  221. class SubsetManager {
  222. constructor( state ) {
  223. this.state = state;
  224. this.selected = {};
  225. }
  226. getSubset( modelID, material ) {
  227. const currentMat = this.matIDNoConfig( modelID, material );
  228. if ( ! this.selected[ currentMat ] ) return null;
  229. return this.selected[ currentMat ].mesh;
  230. }
  231. removeSubset( modelID, scene, material ) {
  232. const currentMat = this.matIDNoConfig( modelID, material );
  233. if ( ! this.selected[ currentMat ] ) return;
  234. if ( scene ) scene.remove( this.selected[ currentMat ].mesh );
  235. delete this.selected[ currentMat ];
  236. }
  237. createSubset( config ) {
  238. if ( ! this.isConfigValid( config ) ) return;
  239. if ( this.isPreviousSelection( config ) ) return;
  240. if ( this.isEasySelection( config ) )
  241. return this.addToPreviousSelection( config );
  242. this.updatePreviousSelection( config.scene, config );
  243. return this.createSelectionInScene( config );
  244. }
  245. createSelectionInScene( config ) {
  246. const filtered = this.filter( config );
  247. const { geomsByMaterial, materials } = this.getGeomAndMat( filtered );
  248. const hasDefaultMaterial = this.matID( config ) == DEFAULT;
  249. const geometry = merge( geomsByMaterial, hasDefaultMaterial );
  250. const mats = hasDefaultMaterial ? materials : config.material;
  251. const mesh = new Mesh( geometry, mats );
  252. this.selected[ this.matID( config ) ].mesh = mesh;
  253. mesh.modelID = config.modelID;
  254. config.scene.add( mesh );
  255. return mesh;
  256. }
  257. isConfigValid( config ) {
  258. return (
  259. this.isValid( config.scene ) &&
  260. this.isValid( config.modelID ) &&
  261. this.isValid( config.ids ) &&
  262. this.isValid( config.removePrevious )
  263. );
  264. }
  265. isValid( item ) {
  266. return item != undefined && item != null;
  267. }
  268. getGeomAndMat( filtered ) {
  269. const geomsByMaterial = [];
  270. const materials = [];
  271. for ( const matID in filtered ) {
  272. const geoms = Object.values( filtered[ matID ].geometries );
  273. if ( ! geoms.length ) continue;
  274. materials.push( filtered[ matID ].material );
  275. if ( geoms.length > 1 ) geomsByMaterial.push( merge( geoms ) );
  276. else geomsByMaterial.push( ...geoms );
  277. }
  278. return {
  279. geomsByMaterial,
  280. materials,
  281. };
  282. }
  283. updatePreviousSelection( scene, config ) {
  284. const previous = this.selected[ this.matID( config ) ];
  285. if ( ! previous ) return this.newSelectionGroup( config );
  286. scene.remove( previous.mesh );
  287. config.removePrevious
  288. ? ( previous.ids = new Set( config.ids ) )
  289. : config.ids.forEach( ( id ) => previous.ids.add( id ) );
  290. }
  291. newSelectionGroup( config ) {
  292. this.selected[ this.matID( config ) ] = {
  293. ids: new Set( config.ids ),
  294. mesh: {},
  295. };
  296. }
  297. isPreviousSelection( config ) {
  298. if ( ! this.selected[ this.matID( config ) ] ) return false;
  299. if ( this.containsIds( config ) ) return true;
  300. const previousIds = this.selected[ this.matID( config ) ].ids;
  301. return JSON.stringify( config.ids ) === JSON.stringify( previousIds );
  302. }
  303. containsIds( config ) {
  304. const newIds = config.ids;
  305. const previous = Array.from( this.selected[ this.matID( config ) ].ids );
  306. return newIds.every(
  307. (
  308. ( i ) => ( v ) =>
  309. ( i = previous.indexOf( v, i ) + 1 )
  310. )( 0 )
  311. );
  312. }
  313. addToPreviousSelection( config ) {
  314. const previous = this.selected[ this.matID( config ) ];
  315. const filtered = this.filter( config );
  316. const geometries = Object.values( filtered )
  317. .map( ( i ) => Object.values( i.geometries ) )
  318. .flat();
  319. const previousGeom = previous.mesh.geometry;
  320. previous.mesh.geometry = merge( [ previousGeom, ...geometries ] );
  321. config.ids.forEach( ( id ) => previous.ids.add( id ) );
  322. }
  323. filter( config ) {
  324. const items = this.state.models[ config.modelID ].items;
  325. const filtered = {};
  326. for ( const matID in items ) {
  327. filtered[ matID ] = {
  328. material: items[ matID ].material,
  329. geometries: this.filterGeometries(
  330. new Set( config.ids ),
  331. items[ matID ].geometries
  332. ),
  333. };
  334. }
  335. return filtered;
  336. }
  337. filterGeometries( selectedIDs, geometries ) {
  338. const ids = Array.from( selectedIDs );
  339. return Object.keys( geometries )
  340. .filter( ( key ) => ids.includes( parseInt( key, 10 ) ) )
  341. .reduce( ( obj, key ) => {
  342. return {
  343. ...obj,
  344. [ key ]: geometries[ key ],
  345. };
  346. }, {} );
  347. }
  348. isEasySelection( config ) {
  349. const matID = this.matID( config );
  350. const def = this.matIDNoConfig( config.modelID );
  351. if ( ! config.removePrevious && matID != def && this.selected[ matID ] )
  352. return true;
  353. }
  354. matID( config ) {
  355. if ( ! config.material ) return DEFAULT;
  356. const name = config.material.uuid || DEFAULT;
  357. return name.concat( ' - ' ).concat( config.modelID.toString() );
  358. }
  359. matIDNoConfig( modelID, material ) {
  360. let name = DEFAULT;
  361. if ( material ) name = material.uuid;
  362. return name.concat( ' - ' ).concat( modelID.toString() );
  363. }
  364. }
  365. const IfcElements = {
  366. 103090709: 'IFCPROJECT',
  367. 4097777520: 'IFCSITE',
  368. 4031249490: 'IFCBUILDING',
  369. 3124254112: 'IFCBUILDINGSTOREY',
  370. 3856911033: 'IFCSPACE',
  371. 25142252: 'IFCCONTROLLER',
  372. 32344328: 'IFCBOILER',
  373. 76236018: 'IFCLAMP',
  374. 90941305: 'IFCPUMP',
  375. 177149247: 'IFCAIRTERMINALBOX',
  376. 182646315: 'IFCFLOWINSTRUMENT',
  377. 263784265: 'IFCFURNISHINGELEMENT',
  378. 264262732: 'IFCELECTRICGENERATOR',
  379. 277319702: 'IFCAUDIOVISUALAPPLIANCE',
  380. 310824031: 'IFCPIPEFITTING',
  381. 331165859: 'IFCSTAIR',
  382. 342316401: 'IFCDUCTFITTING',
  383. 377706215: 'IFCMECHANICALFASTENER',
  384. 395920057: 'IFCDOOR',
  385. 402227799: 'IFCELECTRICMOTOR',
  386. 413509423: 'IFCSYSTEMFURNITUREELEMENT',
  387. 484807127: 'IFCEVAPORATOR',
  388. 486154966: 'IFCWINDOWSTANDARDCASE',
  389. 629592764: 'IFCLIGHTFIXTURE',
  390. 630975310: 'IFCUNITARYCONTROLELEMENT',
  391. 635142910: 'IFCCABLECARRIERFITTING',
  392. 639361253: 'IFCCOIL',
  393. 647756555: 'IFCFASTENER',
  394. 707683696: 'IFCFLOWSTORAGEDEVICE',
  395. 738039164: 'IFCPROTECTIVEDEVICE',
  396. 753842376: 'IFCBEAM',
  397. 812556717: 'IFCTANK',
  398. 819412036: 'IFCFILTER',
  399. 843113511: 'IFCCOLUMN',
  400. 862014818: 'IFCELECTRICDISTRIBUTIONBOARD',
  401. 900683007: 'IFCFOOTING',
  402. 905975707: 'IFCCOLUMNSTANDARDCASE',
  403. 926996030: 'IFCVOIDINGFEATURE',
  404. 979691226: 'IFCREINFORCINGBAR',
  405. 987401354: 'IFCFLOWSEGMENT',
  406. 1003880860: 'IFCELECTRICTIMECONTROL',
  407. 1051757585: 'IFCCABLEFITTING',
  408. 1052013943: 'IFCDISTRIBUTIONCHAMBERELEMENT',
  409. 1062813311: 'IFCDISTRIBUTIONCONTROLELEMENT',
  410. 1073191201: 'IFCMEMBER',
  411. 1095909175: 'IFCBUILDINGELEMENTPROXY',
  412. 1156407060: 'IFCPLATESTANDARDCASE',
  413. 1162798199: 'IFCSWITCHINGDEVICE',
  414. 1329646415: 'IFCSHADINGDEVICE',
  415. 1335981549: 'IFCDISCRETEACCESSORY',
  416. 1360408905: 'IFCDUCTSILENCER',
  417. 1404847402: 'IFCSTACKTERMINAL',
  418. 1426591983: 'IFCFIRESUPPRESSIONTERMINAL',
  419. 1437502449: 'IFCMEDICALDEVICE',
  420. 1509553395: 'IFCFURNITURE',
  421. 1529196076: 'IFCSLAB',
  422. 1620046519: 'IFCTRANSPORTELEMENT',
  423. 1634111441: 'IFCAIRTERMINAL',
  424. 1658829314: 'IFCENERGYCONVERSIONDEVICE',
  425. 1677625105: 'IFCCIVILELEMENT',
  426. 1687234759: 'IFCPILE',
  427. 1904799276: 'IFCELECTRICAPPLIANCE',
  428. 1911478936: 'IFCMEMBERSTANDARDCASE',
  429. 1945004755: 'IFCDISTRIBUTIONELEMENT',
  430. 1973544240: 'IFCCOVERING',
  431. 1999602285: 'IFCSPACEHEATER',
  432. 2016517767: 'IFCROOF',
  433. 2056796094: 'IFCAIRTOAIRHEATRECOVERY',
  434. 2058353004: 'IFCFLOWCONTROLLER',
  435. 2068733104: 'IFCHUMIDIFIER',
  436. 2176052936: 'IFCJUNCTIONBOX',
  437. 2188021234: 'IFCFLOWMETER',
  438. 2223149337: 'IFCFLOWTERMINAL',
  439. 2262370178: 'IFCRAILING',
  440. 2272882330: 'IFCCONDENSER',
  441. 2295281155: 'IFCPROTECTIVEDEVICETRIPPINGUNIT',
  442. 2320036040: 'IFCREINFORCINGMESH',
  443. 2347447852: 'IFCTENDONANCHOR',
  444. 2391383451: 'IFCVIBRATIONISOLATOR',
  445. 2391406946: 'IFCWALL',
  446. 2474470126: 'IFCMOTORCONNECTION',
  447. 2769231204: 'IFCVIRTUALELEMENT',
  448. 2814081492: 'IFCENGINE',
  449. 2906023776: 'IFCBEAMSTANDARDCASE',
  450. 2938176219: 'IFCBURNER',
  451. 2979338954: 'IFCBUILDINGELEMENTPART',
  452. 3024970846: 'IFCRAMP',
  453. 3026737570: 'IFCTUBEBUNDLE',
  454. 3027962421: 'IFCSLABSTANDARDCASE',
  455. 3040386961: 'IFCDISTRIBUTIONFLOWELEMENT',
  456. 3053780830: 'IFCSANITARYTERMINAL',
  457. 3079942009: 'IFCOPENINGSTANDARDCASE',
  458. 3087945054: 'IFCALARM',
  459. 3101698114: 'IFCSURFACEFEATURE',
  460. 3127900445: 'IFCSLABELEMENTEDCASE',
  461. 3132237377: 'IFCFLOWMOVINGDEVICE',
  462. 3171933400: 'IFCPLATE',
  463. 3221913625: 'IFCCOMMUNICATIONSAPPLIANCE',
  464. 3242481149: 'IFCDOORSTANDARDCASE',
  465. 3283111854: 'IFCRAMPFLIGHT',
  466. 3296154744: 'IFCCHIMNEY',
  467. 3304561284: 'IFCWINDOW',
  468. 3310460725: 'IFCELECTRICFLOWSTORAGEDEVICE',
  469. 3319311131: 'IFCHEATEXCHANGER',
  470. 3415622556: 'IFCFAN',
  471. 3420628829: 'IFCSOLARDEVICE',
  472. 3493046030: 'IFCGEOGRAPHICELEMENT',
  473. 3495092785: 'IFCCURTAINWALL',
  474. 3508470533: 'IFCFLOWTREATMENTDEVICE',
  475. 3512223829: 'IFCWALLSTANDARDCASE',
  476. 3518393246: 'IFCDUCTSEGMENT',
  477. 3571504051: 'IFCCOMPRESSOR',
  478. 3588315303: 'IFCOPENINGELEMENT',
  479. 3612865200: 'IFCPIPESEGMENT',
  480. 3640358203: 'IFCCOOLINGTOWER',
  481. 3651124850: 'IFCPROJECTIONELEMENT',
  482. 3694346114: 'IFCOUTLET',
  483. 3747195512: 'IFCEVAPORATIVECOOLER',
  484. 3758799889: 'IFCCABLECARRIERSEGMENT',
  485. 3824725483: 'IFCTENDON',
  486. 3825984169: 'IFCTRANSFORMER',
  487. 3902619387: 'IFCCHILLER',
  488. 4074379575: 'IFCDAMPER',
  489. 4086658281: 'IFCSENSOR',
  490. 4123344466: 'IFCELEMENTASSEMBLY',
  491. 4136498852: 'IFCCOOLEDBEAM',
  492. 4156078855: 'IFCWALLELEMENTEDCASE',
  493. 4175244083: 'IFCINTERCEPTOR',
  494. 4207607924: 'IFCVALVE',
  495. 4217484030: 'IFCCABLESEGMENT',
  496. 4237592921: 'IFCWASTETERMINAL',
  497. 4252922144: 'IFCSTAIRFLIGHT',
  498. 4278956645: 'IFCFLOWFITTING',
  499. 4288193352: 'IFCACTUATOR',
  500. 4292641817: 'IFCUNITARYEQUIPMENT',
  501. };
  502. class PropertyManager {
  503. constructor( state ) {
  504. this.state = state;
  505. }
  506. getExpressId( geometry, faceIndex ) {
  507. if ( ! geometry.index ) return;
  508. const geoIndex = geometry.index.array;
  509. return geometry.attributes[ IdAttrName ].getX( geoIndex[ 3 * faceIndex ] );
  510. }
  511. getItemProperties( modelID, id, recursive = false ) {
  512. return this.state.api.GetLine( modelID, id, recursive );
  513. }
  514. getAllItemsOfType( modelID, type, verbose ) {
  515. const items = [];
  516. const lines = this.state.api.GetLineIDsWithType( modelID, type );
  517. for ( let i = 0; i < lines.size(); i ++ ) items.push( lines.get( i ) );
  518. if ( verbose ) return items.map( ( id ) => this.state.api.GetLine( modelID, id ) );
  519. return items;
  520. }
  521. getPropertySets( modelID, elementID, recursive = false ) {
  522. const propSetIds = this.getAllRelatedItemsOfType(
  523. modelID,
  524. elementID,
  525. PropsNames.psets
  526. );
  527. return propSetIds.map( ( id ) =>
  528. this.state.api.GetLine( modelID, id, recursive )
  529. );
  530. }
  531. getTypeProperties( modelID, elementID, recursive = false ) {
  532. const typeId = this.getAllRelatedItemsOfType(
  533. modelID,
  534. elementID,
  535. PropsNames.type
  536. );
  537. return typeId.map( ( id ) => this.state.api.GetLine( modelID, id, recursive ) );
  538. }
  539. getSpatialStructure( modelID ) {
  540. const chunks = this.getSpatialTreeChunks( modelID );
  541. const projectID = this.state.api
  542. .GetLineIDsWithType( modelID, IFCPROJECT )
  543. .get( 0 );
  544. const project = this.newIfcProject( projectID );
  545. this.getSpatialNode( modelID, project, chunks );
  546. return project;
  547. }
  548. newIfcProject( id ) {
  549. return {
  550. expressID: id,
  551. type: 'IFCPROJECT',
  552. children: [],
  553. };
  554. }
  555. getSpatialTreeChunks( modelID ) {
  556. const treeChunks = {};
  557. this.getChunks( modelID, treeChunks, PropsNames.aggregates );
  558. this.getChunks( modelID, treeChunks, PropsNames.spatial );
  559. return treeChunks;
  560. }
  561. getChunks( modelID, chunks, propNames ) {
  562. const relation = this.state.api.GetLineIDsWithType( modelID, propNames.name );
  563. for ( let i = 0; i < relation.size(); i ++ ) {
  564. const rel = this.state.api.GetLine( modelID, relation.get( i ), false );
  565. const relating = rel[ propNames.relating ].value;
  566. const related = rel[ propNames.related ].map( ( r ) => r.value );
  567. if ( chunks[ relating ] == undefined ) {
  568. chunks[ relating ] = related;
  569. } else {
  570. chunks[ relating ] = chunks[ relating ].concat( related );
  571. }
  572. }
  573. }
  574. getSpatialNode( modelID, node, treeChunks ) {
  575. this.getChildren( modelID, node, treeChunks, PropsNames.aggregates );
  576. this.getChildren( modelID, node, treeChunks, PropsNames.spatial );
  577. }
  578. getChildren( modelID, node, treeChunks, propNames ) {
  579. const children = treeChunks[ node.expressID ];
  580. if ( children == undefined || children == null ) return;
  581. const prop = propNames.key;
  582. node[ prop ] = children.map( ( child ) => {
  583. const node = this.newNode( modelID, child );
  584. this.getSpatialNode( modelID, node, treeChunks );
  585. return node;
  586. } );
  587. }
  588. newNode( modelID, id ) {
  589. const typeID = this.state.models[ modelID ].types[ id ].toString();
  590. const typeName = IfcElements[ typeID ];
  591. return {
  592. expressID: id,
  593. type: typeName,
  594. children: [],
  595. };
  596. }
  597. getAllRelatedItemsOfType( modelID, id, propNames ) {
  598. const lines = this.state.api.GetLineIDsWithType( modelID, propNames.name );
  599. const IDs = [];
  600. for ( let i = 0; i < lines.size(); i ++ ) {
  601. const rel = this.state.api.GetLine( modelID, lines.get( i ) );
  602. const isRelated = this.isRelated( id, rel, propNames );
  603. if ( isRelated ) this.getRelated( rel, propNames, IDs );
  604. }
  605. return IDs;
  606. }
  607. getRelated( rel, propNames, IDs ) {
  608. const element = rel[ propNames.relating ];
  609. if ( ! Array.isArray( element ) ) IDs.push( element.value );
  610. else element.forEach( ( ele ) => IDs.push( ele.value ) );
  611. }
  612. isRelated( id, rel, propNames ) {
  613. const relatedItems = rel[ propNames.related ];
  614. if ( Array.isArray( relatedItems ) ) {
  615. const values = relatedItems.map( ( item ) => item.value );
  616. return values.includes( id );
  617. }
  618. return relatedItems.value === id;
  619. }
  620. }
  621. class TypeManager {
  622. constructor( state ) {
  623. this.state = state;
  624. }
  625. getAllTypes() {
  626. for ( const modelID in this.state.models ) {
  627. const types = this.state.models[ modelID ].types;
  628. if ( Object.keys( types ).length == 0 )
  629. this.getAllTypesOfModel( parseInt( modelID ) );
  630. }
  631. }
  632. getAllTypesOfModel( modelID ) {
  633. this.state.models[ modelID ].types;
  634. const elements = Object.keys( IfcElements ).map( ( e ) => parseInt( e ) );
  635. const types = this.state.models[ modelID ].types;
  636. elements.forEach( ( type ) => {
  637. const lines = this.state.api.GetLineIDsWithType( modelID, type );
  638. for ( let i = 0; i < lines.size(); i ++ ) types[ lines.get( i ) ] = type;
  639. } );
  640. }
  641. }
  642. let modelIdCounter = 0;
  643. class IFCModel extends Group {
  644. constructor( mesh, ifc ) {
  645. super();
  646. this.mesh = mesh;
  647. this.ifc = ifc;
  648. this.modelID = modelIdCounter ++;
  649. }
  650. setWasmPath( path ) {
  651. this.ifc.setWasmPath( path );
  652. }
  653. close( scene ) {
  654. this.ifc.close( this.modelID, scene );
  655. }
  656. getExpressId( geometry, faceIndex ) {
  657. return this.ifc.getExpressId( geometry, faceIndex );
  658. }
  659. getAllItemsOfType( type, verbose ) {
  660. return this.ifc.getAllItemsOfType( this.modelID, type, verbose );
  661. }
  662. getItemProperties( id, recursive = false ) {
  663. return this.ifc.getItemProperties( this.modelID, id, recursive );
  664. }
  665. getPropertySets( id, recursive = false ) {
  666. return this.ifc.getPropertySets( this.modelID, id, recursive );
  667. }
  668. getTypeProperties( id, recursive = false ) {
  669. return this.ifc.getTypeProperties( this.modelID, id, recursive );
  670. }
  671. getIfcType( id ) {
  672. return this.ifc.getIfcType( this.modelID, id );
  673. }
  674. getSpatialStructure() {
  675. return this.ifc.getSpatialStructure( this.modelID );
  676. }
  677. getSubset( material ) {
  678. return this.ifc.getSubset( this.modelID, material );
  679. }
  680. removeSubset( scene, material ) {
  681. this.ifc.removeSubset( this.modelID, scene, material );
  682. }
  683. createSubset( config ) {
  684. const modelConfig = {
  685. ...config,
  686. modelID: this.modelID,
  687. };
  688. return this.ifc.createSubset( modelConfig );
  689. }
  690. }
  691. class IFCManager {
  692. constructor() {
  693. this.state = {
  694. models: [],
  695. api: new IfcAPI(),
  696. };
  697. this.parser = new IFCParser( this.state );
  698. this.subsets = new SubsetManager( this.state );
  699. this.properties = new PropertyManager( this.state );
  700. this.types = new TypeManager( this.state );
  701. }
  702. async parse( buffer ) {
  703. const mesh = await this.parser.parse( buffer );
  704. this.types.getAllTypes();
  705. return new IFCModel( mesh, this );
  706. }
  707. setWasmPath( path ) {
  708. this.state.api.SetWasmPath( path );
  709. }
  710. setupThreeMeshBVH( computeBoundsTree, disposeBoundsTree, acceleratedRaycast ) {
  711. this.parser.initializeMeshBVH(
  712. computeBoundsTree,
  713. disposeBoundsTree,
  714. acceleratedRaycast
  715. );
  716. }
  717. close( modelID, scene ) {
  718. this.state.api.CloseModel( modelID );
  719. if ( scene ) scene.remove( this.state.models[ modelID ].mesh );
  720. delete this.state.models[ modelID ];
  721. }
  722. getExpressId( geometry, faceIndex ) {
  723. return this.properties.getExpressId( geometry, faceIndex );
  724. }
  725. getAllItemsOfType( modelID, type, verbose ) {
  726. return this.properties.getAllItemsOfType( modelID, type, verbose );
  727. }
  728. getItemProperties( modelID, id, recursive = false ) {
  729. return this.properties.getItemProperties( modelID, id, recursive );
  730. }
  731. getPropertySets( modelID, id, recursive = false ) {
  732. return this.properties.getPropertySets( modelID, id, recursive );
  733. }
  734. getTypeProperties( modelID, id, recursive = false ) {
  735. return this.properties.getTypeProperties( modelID, id, recursive );
  736. }
  737. getIfcType( modelID, id ) {
  738. const typeID = this.state.models[ modelID ].types[ id ];
  739. return IfcElements[ typeID.toString() ];
  740. }
  741. getSpatialStructure( modelID ) {
  742. return this.properties.getSpatialStructure( modelID );
  743. }
  744. getSubset( modelID, material ) {
  745. return this.subsets.getSubset( modelID, material );
  746. }
  747. removeSubset( modelID, scene, material ) {
  748. this.subsets.removeSubset( modelID, scene, material );
  749. }
  750. createSubset( config ) {
  751. return this.subsets.createSubset( config );
  752. }
  753. }
  754. class IFCLoader extends Loader {
  755. constructor( manager ) {
  756. super( manager );
  757. this.ifcManager = new IFCManager();
  758. }
  759. load( url, onLoad, onProgress, onError ) {
  760. const scope = this;
  761. const loader = new FileLoader( scope.manager );
  762. loader.setPath( scope.path );
  763. loader.setResponseType( 'arraybuffer' );
  764. loader.setRequestHeader( scope.requestHeader );
  765. loader.setWithCredentials( scope.withCredentials );
  766. loader.load(
  767. url,
  768. async function ( buffer ) {
  769. try {
  770. if ( typeof buffer == 'string' ) {
  771. throw new Error( 'IFC files must be given as a buffer!' );
  772. }
  773. onLoad( await scope.parse( buffer ) );
  774. } catch ( e ) {
  775. if ( onError ) {
  776. onError( e );
  777. } else {
  778. console.error( e );
  779. }
  780. scope.manager.itemError( url );
  781. }
  782. },
  783. onProgress,
  784. onError
  785. );
  786. }
  787. parse( buffer ) {
  788. return this.ifcManager.parse( buffer );
  789. }
  790. }
  791. export { IFCLoader };
  792. //# sourceMappingURL=IFCLoader.js.map