@bitbybit-dev/ifc 0.0.0-stage → 1.5.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +42 -0
  3. package/README.md +92 -2
  4. package/index.d.ts +1 -0
  5. package/index.js +1 -0
  6. package/lib/api/dto-registry.d.ts +8 -0
  7. package/lib/api/dto-registry.js +109 -0
  8. package/lib/api/ifc-service.d.ts +109 -0
  9. package/lib/api/ifc-service.js +55 -0
  10. package/lib/api/inputs/base-inputs.d.ts +5 -0
  11. package/lib/api/inputs/base-inputs.js +5 -0
  12. package/lib/api/inputs/ifc-inputs.d.ts +2727 -0
  13. package/lib/api/inputs/ifc-inputs.js +2498 -0
  14. package/lib/api/inputs/index.d.ts +3 -0
  15. package/lib/api/inputs/index.js +3 -0
  16. package/lib/api/resolved-inputs/index.d.ts +70 -0
  17. package/lib/api/resolved-inputs/index.js +1 -0
  18. package/lib/api/services/assemblies.d.ts +28 -0
  19. package/lib/api/services/assemblies.js +49 -0
  20. package/lib/api/services/beams.d.ts +33 -0
  21. package/lib/api/services/beams.js +59 -0
  22. package/lib/api/services/columns.d.ts +26 -0
  23. package/lib/api/services/columns.js +56 -0
  24. package/lib/api/services/defaults.constants.d.ts +44 -0
  25. package/lib/api/services/defaults.constants.js +44 -0
  26. package/lib/api/services/doors.d.ts +45 -0
  27. package/lib/api/services/doors.js +94 -0
  28. package/lib/api/services/geometry.d.ts +51 -0
  29. package/lib/api/services/geometry.js +87 -0
  30. package/lib/api/services/index.d.ts +17 -0
  31. package/lib/api/services/index.js +17 -0
  32. package/lib/api/services/materials.d.ts +46 -0
  33. package/lib/api/services/materials.js +86 -0
  34. package/lib/api/services/member-support.d.ts +7 -0
  35. package/lib/api/services/member-support.js +29 -0
  36. package/lib/api/services/members.d.ts +24 -0
  37. package/lib/api/services/members.js +51 -0
  38. package/lib/api/services/model.d.ts +244 -0
  39. package/lib/api/services/model.js +409 -0
  40. package/lib/api/services/openings.d.ts +57 -0
  41. package/lib/api/services/openings.js +102 -0
  42. package/lib/api/services/properties.d.ts +89 -0
  43. package/lib/api/services/properties.js +142 -0
  44. package/lib/api/services/quantities.d.ts +38 -0
  45. package/lib/api/services/quantities.js +51 -0
  46. package/lib/api/services/roofs.d.ts +24 -0
  47. package/lib/api/services/roofs.js +52 -0
  48. package/lib/api/services/service-support.d.ts +8 -0
  49. package/lib/api/services/service-support.js +37 -0
  50. package/lib/api/services/service-types.d.ts +3 -0
  51. package/lib/api/services/service-types.js +1 -0
  52. package/lib/api/services/site.d.ts +37 -0
  53. package/lib/api/services/site.js +80 -0
  54. package/lib/api/services/slabs.d.ts +31 -0
  55. package/lib/api/services/slabs.js +59 -0
  56. package/lib/api/services/spaces.d.ts +36 -0
  57. package/lib/api/services/spaces.js +64 -0
  58. package/lib/api/services/spatial.d.ts +54 -0
  59. package/lib/api/services/spatial.js +75 -0
  60. package/lib/api/services/walls.d.ts +140 -0
  61. package/lib/api/services/walls.js +268 -0
  62. package/lib/api/services/windows.d.ts +46 -0
  63. package/lib/api/services/windows.js +76 -0
  64. package/lib/build/assemblies.d.ts +4 -0
  65. package/lib/build/assemblies.js +45 -0
  66. package/lib/build/build-types.d.ts +345 -0
  67. package/lib/build/build-types.js +1 -0
  68. package/lib/build/checks.d.ts +6 -0
  69. package/lib/build/checks.js +32 -0
  70. package/lib/build/constants.d.ts +27 -0
  71. package/lib/build/constants.js +43 -0
  72. package/lib/build/contexts.d.ts +9 -0
  73. package/lib/build/contexts.js +52 -0
  74. package/lib/build/curves.d.ts +3 -0
  75. package/lib/build/curves.js +23 -0
  76. package/lib/build/entity-writer.d.ts +23 -0
  77. package/lib/build/entity-writer.js +97 -0
  78. package/lib/build/fillings.d.ts +7 -0
  79. package/lib/build/fillings.js +182 -0
  80. package/lib/build/georeference.d.ts +5 -0
  81. package/lib/build/georeference.js +44 -0
  82. package/lib/build/materials.d.ts +18 -0
  83. package/lib/build/materials.js +166 -0
  84. package/lib/build/math.d.ts +10 -0
  85. package/lib/build/math.js +47 -0
  86. package/lib/build/measure.d.ts +9 -0
  87. package/lib/build/measure.js +195 -0
  88. package/lib/build/moving.d.ts +4 -0
  89. package/lib/build/moving.js +61 -0
  90. package/lib/build/openings.d.ts +12 -0
  91. package/lib/build/openings.js +156 -0
  92. package/lib/build/outlines.d.ts +4 -0
  93. package/lib/build/outlines.js +40 -0
  94. package/lib/build/placement.d.ts +16 -0
  95. package/lib/build/placement.js +106 -0
  96. package/lib/build/profiles.d.ts +4 -0
  97. package/lib/build/profiles.js +35 -0
  98. package/lib/build/project.d.ts +11 -0
  99. package/lib/build/project.js +134 -0
  100. package/lib/build/properties.d.ts +10 -0
  101. package/lib/build/properties.js +182 -0
  102. package/lib/build/quantities.d.ts +7 -0
  103. package/lib/build/quantities.js +262 -0
  104. package/lib/build/relationships.d.ts +9 -0
  105. package/lib/build/relationships.js +67 -0
  106. package/lib/build/removal.d.ts +3 -0
  107. package/lib/build/removal.js +106 -0
  108. package/lib/build/representations.d.ts +6 -0
  109. package/lib/build/representations.js +46 -0
  110. package/lib/build/roof-clipping.d.ts +5 -0
  111. package/lib/build/roof-clipping.js +79 -0
  112. package/lib/build/roofs.d.ts +4 -0
  113. package/lib/build/roofs.js +131 -0
  114. package/lib/build/site.d.ts +7 -0
  115. package/lib/build/site.js +112 -0
  116. package/lib/build/slab-openings.d.ts +4 -0
  117. package/lib/build/slab-openings.js +71 -0
  118. package/lib/build/slabs-columns-beams.d.ts +8 -0
  119. package/lib/build/slabs-columns-beams.js +73 -0
  120. package/lib/build/solids.d.ts +5 -0
  121. package/lib/build/solids.js +33 -0
  122. package/lib/build/spaces.d.ts +6 -0
  123. package/lib/build/spaces.js +55 -0
  124. package/lib/build/spatial.d.ts +17 -0
  125. package/lib/build/spatial.js +111 -0
  126. package/lib/build/type-objects.d.ts +4 -0
  127. package/lib/build/type-objects.js +9 -0
  128. package/lib/build/wall-edits.d.ts +5 -0
  129. package/lib/build/wall-edits.js +138 -0
  130. package/lib/build/wall-geometry.d.ts +15 -0
  131. package/lib/build/wall-geometry.js +159 -0
  132. package/lib/build/wall-parameters.d.ts +3 -0
  133. package/lib/build/wall-parameters.js +47 -0
  134. package/lib/build/walls.d.ts +12 -0
  135. package/lib/build/walls.js +238 -0
  136. package/lib/geometry/advanced.d.ts +27 -0
  137. package/lib/geometry/advanced.js +292 -0
  138. package/lib/geometry/colours.d.ts +3 -0
  139. package/lib/geometry/colours.js +109 -0
  140. package/lib/geometry/curves.d.ts +32 -0
  141. package/lib/geometry/curves.js +370 -0
  142. package/lib/geometry/edges.constants.d.ts +5 -0
  143. package/lib/geometry/edges.constants.js +5 -0
  144. package/lib/geometry/edges.d.ts +21 -0
  145. package/lib/geometry/edges.js +164 -0
  146. package/lib/geometry/elements.d.ts +3 -0
  147. package/lib/geometry/elements.js +152 -0
  148. package/lib/geometry/errors.d.ts +3 -0
  149. package/lib/geometry/errors.js +6 -0
  150. package/lib/geometry/faceted.d.ts +31 -0
  151. package/lib/geometry/faceted.js +214 -0
  152. package/lib/geometry/geometry-types.d.ts +103 -0
  153. package/lib/geometry/geometry-types.js +1 -0
  154. package/lib/geometry/groups.d.ts +21 -0
  155. package/lib/geometry/groups.js +106 -0
  156. package/lib/geometry/items.d.ts +34 -0
  157. package/lib/geometry/items.js +313 -0
  158. package/lib/geometry/operators.d.ts +3 -0
  159. package/lib/geometry/operators.js +65 -0
  160. package/lib/geometry/profiles.d.ts +4 -0
  161. package/lib/geometry/profiles.js +277 -0
  162. package/lib/geometry/recipe-builder.d.ts +14 -0
  163. package/lib/geometry/recipe-builder.js +48 -0
  164. package/lib/geometry/references.d.ts +4 -0
  165. package/lib/geometry/references.js +17 -0
  166. package/lib/geometry/regions.d.ts +4 -0
  167. package/lib/geometry/regions.js +26 -0
  168. package/lib/geometry/surfaces.constants.d.ts +17 -0
  169. package/lib/geometry/surfaces.constants.js +17 -0
  170. package/lib/geometry/surfaces.d.ts +6 -0
  171. package/lib/geometry/surfaces.js +306 -0
  172. package/lib/geometry/swept-solids.constants.d.ts +7 -0
  173. package/lib/geometry/swept-solids.constants.js +6 -0
  174. package/lib/geometry/swept-solids.d.ts +25 -0
  175. package/lib/geometry/swept-solids.js +243 -0
  176. package/lib/index.d.ts +6 -0
  177. package/lib/index.js +6 -0
  178. package/lib/model/constants.d.ts +30 -0
  179. package/lib/model/constants.js +30 -0
  180. package/lib/model/entity-lookup.d.ts +9 -0
  181. package/lib/model/entity-lookup.js +20 -0
  182. package/lib/model/guid.d.ts +7 -0
  183. package/lib/model/guid.js +60 -0
  184. package/lib/model/id-map.d.ts +10 -0
  185. package/lib/model/id-map.js +80 -0
  186. package/lib/model/index.d.ts +4 -0
  187. package/lib/model/index.js +2 -0
  188. package/lib/model/io.d.ts +6 -0
  189. package/lib/model/io.js +136 -0
  190. package/lib/model/is-model.d.ts +9 -0
  191. package/lib/model/is-model.js +11 -0
  192. package/lib/model/model-types.d.ts +111 -0
  193. package/lib/model/model-types.js +1 -0
  194. package/lib/model/overlay-indexes.d.ts +17 -0
  195. package/lib/model/overlay-indexes.js +69 -0
  196. package/lib/model/references.d.ts +2 -0
  197. package/lib/model/references.js +51 -0
  198. package/lib/model/rows-by-id.d.ts +8 -0
  199. package/lib/model/rows-by-id.js +39 -0
  200. package/lib/model/snapshot.d.ts +36 -0
  201. package/lib/model/snapshot.js +144 -0
  202. package/lib/model/source.d.ts +29 -0
  203. package/lib/model/source.js +172 -0
  204. package/lib/model/transaction.d.ts +39 -0
  205. package/lib/model/transaction.js +250 -0
  206. package/lib/model/upgrade.d.ts +2 -0
  207. package/lib/model/upgrade.js +78 -0
  208. package/lib/schema/constants.d.ts +3 -0
  209. package/lib/schema/constants.js +3 -0
  210. package/lib/schema/generated/ifc4.d.ts +2 -0
  211. package/lib/schema/generated/ifc4.js +1204 -0
  212. package/lib/schema/generated/ifc4x3.d.ts +2 -0
  213. package/lib/schema/generated/ifc4x3.js +1325 -0
  214. package/lib/schema/registry.d.ts +7 -0
  215. package/lib/schema/registry.js +37 -0
  216. package/lib/schema/schema-types.d.ts +45 -0
  217. package/lib/schema/schema-types.js +1 -0
  218. package/lib/schema/schema.d.ts +23 -0
  219. package/lib/schema/schema.js +136 -0
  220. package/lib/step/constants.d.ts +48 -0
  221. package/lib/step/constants.js +48 -0
  222. package/lib/step/encoder.d.ts +21 -0
  223. package/lib/step/encoder.js +321 -0
  224. package/lib/step/errors.d.ts +39 -0
  225. package/lib/step/errors.js +45 -0
  226. package/lib/step/file-index.d.ts +2 -0
  227. package/lib/step/file-index.js +221 -0
  228. package/lib/step/header.d.ts +4 -0
  229. package/lib/step/header.js +52 -0
  230. package/lib/step/numbers.d.ts +3 -0
  231. package/lib/step/numbers.js +73 -0
  232. package/lib/step/reader.d.ts +2 -0
  233. package/lib/step/reader.js +160 -0
  234. package/lib/step/scanner.d.ts +13 -0
  235. package/lib/step/scanner.js +203 -0
  236. package/lib/step/step-types.d.ts +170 -0
  237. package/lib/step/step-types.js +1 -0
  238. package/lib/step/strings.d.ts +2 -0
  239. package/lib/step/strings.js +150 -0
  240. package/lib/step/values.d.ts +13 -0
  241. package/lib/step/values.js +51 -0
  242. package/package.json +51 -5
@@ -0,0 +1,134 @@
1
+ import { WORLD_AXES } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
2
+ import { projectGlobalId, randomGlobalId } from "../model/guid";
3
+ import { emptyModel } from "../model/io";
4
+ import { IfcTransaction } from "../model/transaction";
5
+ import { DERIVED, enumValue, isEnumeration, isList, isReference, isTyped, ref } from "../step/values";
6
+ import * as Inputs from "../api/inputs";
7
+ import { AGGREGATION, DEFAULT_PRECISION_METRES, LENGTH_PREFIXES, METRES_TO_MILLIMETRES } from "./constants";
8
+ import { EntityWriter } from "./entity-writer";
9
+ import { relate } from "./relationships";
10
+ export const SCHEMA_FOR_NEW_MODELS = "IFC4";
11
+ export const DESIGN_TRANSFER_VIEW = "ViewDefinition [DesignTransferView_V1.0]";
12
+ const PREFIX_OF_UNIT = {
13
+ [Inputs.IFC.lengthUnitEnum.millimetre]: "MILLI",
14
+ [Inputs.IFC.lengthUnitEnum.centimetre]: "CENTI",
15
+ [Inputs.IFC.lengthUnitEnum.metre]: undefined,
16
+ };
17
+ function siUnit(writer, unitType, name, prefix) {
18
+ return writer.create("IfcSIUnit", { Dimensions: DERIVED, UnitType: enumValue(unitType), Prefix: prefix === undefined ? null : enumValue(prefix), Name: enumValue(name) });
19
+ }
20
+ export function precisionFor(unit) {
21
+ return DEFAULT_PRECISION_METRES / metresPerUnit(PREFIX_OF_UNIT[unit]);
22
+ }
23
+ const UNIT_POWERS = { LENGTHUNIT: 1, AREAUNIT: 2, VOLUMEUNIT: 3, PLANEANGLEUNIT: 1 };
24
+ const NAMED_UNIT = "IfcNamedUnit";
25
+ const MAX_UNIT_DEPTH = 8;
26
+ function metresPerUnit(prefix) {
27
+ var _a;
28
+ return prefix === undefined ? 1 : (_a = LENGTH_PREFIXES[prefix]) !== null && _a !== void 0 ? _a : 1;
29
+ }
30
+ export function createProject(project) {
31
+ const globalId = project.seed === undefined ? randomGlobalId() : projectGlobalId(project.seed);
32
+ const tx = new IfcTransaction(emptyModel(SCHEMA_FOR_NEW_MODELS, project.header), globalId);
33
+ const writer = new EntityWriter(tx);
34
+ const precision = precisionFor(project.lengthUnit);
35
+ const units = writer.create("IfcUnitAssignment", {
36
+ Units: [
37
+ siUnit(writer, "LENGTHUNIT", "METRE", PREFIX_OF_UNIT[project.lengthUnit]),
38
+ siUnit(writer, "AREAUNIT", "SQUARE_METRE"),
39
+ siUnit(writer, "VOLUMEUNIT", "CUBIC_METRE"),
40
+ siUnit(writer, "PLANEANGLEUNIT", "RADIAN"),
41
+ ].map(ref),
42
+ });
43
+ const model3d = writer.create("IfcGeometricRepresentationContext", {
44
+ ContextType: "Model",
45
+ CoordinateSpaceDimension: 3,
46
+ Precision: precision,
47
+ WorldCoordinateSystem: ref(writer.placement3(WORLD_AXES)),
48
+ TrueNorth: ref(writer.direction([0, 1])),
49
+ });
50
+ const plan = writer.create("IfcGeometricRepresentationContext", {
51
+ ContextType: "Plan",
52
+ CoordinateSpaceDimension: 2,
53
+ Precision: precision,
54
+ WorldCoordinateSystem: ref(writer.placement2([0, 0])),
55
+ });
56
+ const subContext = (parent, identifier, type, view) => writer.create("IfcGeometricRepresentationSubContext", {
57
+ ContextIdentifier: identifier,
58
+ ContextType: type,
59
+ ParentContext: ref(parent),
60
+ TargetView: enumValue(view),
61
+ });
62
+ subContext(model3d, "Body", "Model", "MODEL_VIEW");
63
+ subContext(model3d, "Axis", "Model", "GRAPH_VIEW");
64
+ subContext(model3d, "Box", "Model", "MODEL_VIEW");
65
+ subContext(plan, "Axis", "Plan", "GRAPH_VIEW");
66
+ subContext(plan, "FootPrint", "Plan", "PLAN_VIEW");
67
+ const projectId = writer.create("IfcProject", {
68
+ GlobalId: tx.givenGlobalId(globalId),
69
+ Name: project.name,
70
+ RepresentationContexts: [ref(model3d), ref(plan)],
71
+ UnitsInContext: ref(units),
72
+ });
73
+ const sitePlacement = writer.localPlacement(undefined, WORLD_AXES);
74
+ const site = writer.create("IfcSite", {
75
+ GlobalId: tx.structureGlobalId("site"),
76
+ Name: project.siteName,
77
+ ObjectPlacement: ref(sitePlacement),
78
+ CompositionType: enumValue("ELEMENT"),
79
+ });
80
+ const building = writer.create("IfcBuilding", {
81
+ GlobalId: tx.structureGlobalId("building"),
82
+ Name: project.buildingName,
83
+ ObjectPlacement: ref(writer.localPlacement(sitePlacement, WORLD_AXES)),
84
+ CompositionType: enumValue("ELEMENT"),
85
+ });
86
+ relate(tx, writer, AGGREGATION, projectId, [site]);
87
+ relate(tx, writer, AGGREGATION, site, [building]);
88
+ return tx.commit();
89
+ }
90
+ export function projectOf(model) {
91
+ const project = model.byType("IfcProject")[0];
92
+ if (!project) {
93
+ throw new Error("The model has no IfcProject");
94
+ }
95
+ return project.id;
96
+ }
97
+ function siFactor(model, unit, power, depth) {
98
+ if (depth > MAX_UNIT_DEPTH) {
99
+ throw new Error(`The unit #${unit} is converted through more than ${MAX_UNIT_DEPTH} other units, or through itself`);
100
+ }
101
+ const entity = model.entity(unit);
102
+ if (entity.type === "IfcSIUnit") {
103
+ const prefix = model.attribute(unit, "Prefix");
104
+ return Math.pow(metresPerUnit(isEnumeration(prefix) ? prefix.enum : undefined), power);
105
+ }
106
+ const measure = model.schema.isSubtypeOf(entity.type, "IfcConversionBasedUnit") ? model.attribute(unit, "ConversionFactor") : null;
107
+ if (!isReference(measure)) {
108
+ return 1;
109
+ }
110
+ const value = model.attribute(measure.ref, "ValueComponent");
111
+ const base = model.attribute(measure.ref, "UnitComponent");
112
+ const factor = isTyped(value) && typeof value.value === "number" ? value.value : 1;
113
+ return factor * (isReference(base) ? siFactor(model, base.ref, power, depth + 1) : 1);
114
+ }
115
+ export function siPerUnit(model, unitType) {
116
+ const units = model.attribute(projectOf(model), "UnitsInContext");
117
+ const list = isReference(units) ? model.attribute(units.ref, "Units") : null;
118
+ for (const unit of isList(list) ? list : []) {
119
+ if (!isReference(unit) || !model.schema.isSubtypeOf(model.entity(unit.ref).type, NAMED_UNIT)) {
120
+ continue;
121
+ }
122
+ const type = model.attribute(unit.ref, "UnitType");
123
+ if (isEnumeration(type) && type.enum === unitType) {
124
+ return siFactor(model, unit.ref, UNIT_POWERS[unitType], 0);
125
+ }
126
+ }
127
+ return 1;
128
+ }
129
+ export function millimetresPerUnit(model) {
130
+ return siPerUnit(model, "LENGTHUNIT") * METRES_TO_MILLIMETRES;
131
+ }
132
+ export function radiansPerUnit(model) {
133
+ return siPerUnit(model, "PLANEANGLEUNIT");
134
+ }
@@ -0,0 +1,10 @@
1
+ import type * as Inputs from "../api/inputs";
2
+ import type { IfcTransaction } from "../model/transaction";
3
+ import type { ModelReader, PropertySpec } from "./build-types";
4
+ import type { EntityWriter } from "./entity-writer";
5
+ export declare function addPropertySet(tx: IfcTransaction, writer: EntityWriter, objects: readonly number[], name: string, properties: readonly PropertySpec[]): number;
6
+ export declare function definitionsOf(reader: ModelReader, object: number): number[];
7
+ export declare function propertySetsOf(reader: ModelReader, object: number): Inputs.IFC.PropertySetInfoDto[];
8
+ export declare function setPropertyValues(tx: IfcTransaction, writer: EntityWriter, object: number, name: string, properties: readonly PropertySpec[]): void;
9
+ export declare function removePropertyValues(tx: IfcTransaction, writer: EntityWriter, object: number, name: string, names: readonly string[]): void;
10
+ export declare function removePropertySet(tx: IfcTransaction, writer: EntityWriter, object: number, name: string): void;
@@ -0,0 +1,182 @@
1
+ import { isList, isReference, isTyped, ref, typed } from "../step/values";
2
+ import { PROPERTY_DEFINITION } from "./constants";
3
+ import { relate } from "./relationships";
4
+ import { describeObject } from "./spatial";
5
+ const TYPE_OBJECT = "IfcTypeObject";
6
+ function nominalValue(property) {
7
+ if (property.type !== undefined) {
8
+ return typed(property.type, property.value);
9
+ }
10
+ if (typeof property.value === "string") {
11
+ return typed("IfcLabel", property.value);
12
+ }
13
+ if (typeof property.value === "boolean") {
14
+ return typed("IfcBoolean", property.value);
15
+ }
16
+ return typed(Number.isSafeInteger(property.value) ? "IfcInteger" : "IfcReal", property.value);
17
+ }
18
+ function referencesOf(value) {
19
+ if (isReference(value)) {
20
+ return [value.ref];
21
+ }
22
+ const items = isTyped(value) ? value.value : value;
23
+ return isList(items) ? items.filter(isReference).map((item) => item.ref) : [];
24
+ }
25
+ function isType(reader, object) {
26
+ return reader.schema.isSubtypeOf(reader.entity(object).type, TYPE_OBJECT);
27
+ }
28
+ function writeProperties(writer, properties) {
29
+ const names = new Set();
30
+ return properties.map((property) => {
31
+ if (names.has(property.name)) {
32
+ throw new Error(`The property set names '${property.name}' twice`);
33
+ }
34
+ names.add(property.name);
35
+ return writer.create("IfcPropertySingleValue", { Name: property.name, NominalValue: nominalValue(property) });
36
+ });
37
+ }
38
+ function attach(tx, writer, objects, set) {
39
+ const types = objects.filter((object) => isType(tx, object));
40
+ const occurrences = objects.filter((object) => !types.includes(object));
41
+ for (const type of types) {
42
+ tx.update(type, { HasPropertySets: [...referencesOf(tx.attribute(type, "HasPropertySets")).map(ref), ref(set)] });
43
+ }
44
+ if (occurrences.length) {
45
+ relate(tx, writer, PROPERTY_DEFINITION, set, occurrences);
46
+ }
47
+ }
48
+ export function addPropertySet(tx, writer, objects, name, properties) {
49
+ if (!properties.length) {
50
+ throw new Error("A property set needs at least one property");
51
+ }
52
+ const propertyIds = writeProperties(writer, properties);
53
+ const set = writer.create("IfcPropertySet", { GlobalId: tx.globalId(undefined), Name: name, HasProperties: propertyIds.map(ref) });
54
+ attach(tx, writer, objects, set);
55
+ return set;
56
+ }
57
+ export function definitionsOf(reader, object) {
58
+ if (isType(reader, object)) {
59
+ return referencesOf(reader.attribute(object, "HasPropertySets"));
60
+ }
61
+ return reader.referencesTo(object)
62
+ .filter((user) => reader.typeOf(user) === PROPERTY_DEFINITION.relationship && referencesOf(reader.attribute(user, PROPERTY_DEFINITION.related)).includes(object))
63
+ .flatMap((user) => referencesOf(reader.attribute(user, PROPERTY_DEFINITION.relating)));
64
+ }
65
+ function singleValues(reader, set) {
66
+ const values = {};
67
+ for (const property of referencesOf(reader.attribute(set, "HasProperties"))) {
68
+ if (reader.entity(property).type !== "IfcPropertySingleValue") {
69
+ continue;
70
+ }
71
+ const name = reader.attribute(property, "Name");
72
+ const value = reader.attribute(property, "NominalValue");
73
+ const inner = isTyped(value) ? value.value : null;
74
+ if (typeof name === "string" && (typeof inner === "string" || typeof inner === "number" || typeof inner === "boolean")) {
75
+ values[name] = inner;
76
+ }
77
+ }
78
+ return values;
79
+ }
80
+ export function propertySetsOf(reader, object) {
81
+ return definitionsOf(reader, object)
82
+ .filter((definition) => reader.entity(definition).type === "IfcPropertySet")
83
+ .map((set) => {
84
+ const name = reader.attribute(set, "Name");
85
+ return { name: typeof name === "string" ? name : "", properties: singleValues(reader, set) };
86
+ });
87
+ }
88
+ function nameOf(reader, id) {
89
+ const name = reader.attribute(id, "Name");
90
+ return typeof name === "string" ? name : "";
91
+ }
92
+ function findSet(reader, object, name) {
93
+ return definitionsOf(reader, object).find((definition) => reader.entity(definition).type === "IfcPropertySet" && nameOf(reader, definition) === name);
94
+ }
95
+ function namedSet(reader, object, name) {
96
+ const set = findSet(reader, object, name);
97
+ if (set === undefined) {
98
+ throw new Error(`${describeObject(reader, object)} has no property set named '${name}'`);
99
+ }
100
+ return set;
101
+ }
102
+ function holdersOf(reader, set) {
103
+ return reader.referencesTo(set).reduce((count, user) => {
104
+ if (reader.typeOf(user) === PROPERTY_DEFINITION.relationship) {
105
+ return count + referencesOf(reader.attribute(user, PROPERTY_DEFINITION.related)).length;
106
+ }
107
+ return count + (isType(reader, user) ? 1 : 0);
108
+ }, 0);
109
+ }
110
+ function detachSet(tx, writer, object, set) {
111
+ if (isType(tx, object)) {
112
+ const kept = referencesOf(tx.attribute(object, "HasPropertySets")).filter((id) => id !== set);
113
+ tx.update(object, { HasPropertySets: kept.length ? kept.map(ref) : null });
114
+ return;
115
+ }
116
+ for (const user of tx.referencesTo(set)) {
117
+ const related = tx.typeOf(user) === PROPERTY_DEFINITION.relationship ? referencesOf(tx.attribute(user, PROPERTY_DEFINITION.related)) : [];
118
+ if (!related.includes(object)) {
119
+ continue;
120
+ }
121
+ const siblings = referencesOf(tx.attribute(user, PROPERTY_DEFINITION.relating)).filter((id) => id !== set);
122
+ const kept = related.filter((id) => id !== object);
123
+ if (kept.length) {
124
+ tx.update(user, { [PROPERTY_DEFINITION.related]: kept.map(ref) });
125
+ }
126
+ else {
127
+ tx.delete(user);
128
+ }
129
+ siblings.forEach((sibling) => relate(tx, writer, PROPERTY_DEFINITION, sibling, [object]));
130
+ }
131
+ }
132
+ function ownSet(tx, writer, object, set) {
133
+ if (holdersOf(tx, set) <= 1) {
134
+ return set;
135
+ }
136
+ const copy = writer.create("IfcPropertySet", {
137
+ GlobalId: tx.globalId(undefined),
138
+ Name: tx.attribute(set, "Name"),
139
+ Description: tx.attribute(set, "Description"),
140
+ HasProperties: tx.attribute(set, "HasProperties"),
141
+ });
142
+ detachSet(tx, writer, object, set);
143
+ attach(tx, writer, [object], copy);
144
+ return copy;
145
+ }
146
+ export function setPropertyValues(tx, writer, object, name, properties) {
147
+ if (!properties.length) {
148
+ throw new Error("Give at least one property to set");
149
+ }
150
+ const found = findSet(tx, object, name);
151
+ if (found === undefined) {
152
+ addPropertySet(tx, writer, [object], name, properties);
153
+ return;
154
+ }
155
+ const set = ownSet(tx, writer, object, found);
156
+ const changed = new Set(properties.map((property) => property.name));
157
+ const current = referencesOf(tx.attribute(set, "HasProperties"));
158
+ const kept = current.filter((property) => !changed.has(nameOf(tx, property)));
159
+ tx.update(set, { HasProperties: [...kept, ...writeProperties(writer, properties)].map(ref) });
160
+ tx.dropIfUnused(current.filter((property) => !kept.includes(property)));
161
+ }
162
+ export function removePropertyValues(tx, writer, object, name, names) {
163
+ const found = namedSet(tx, object, name);
164
+ const current = referencesOf(tx.attribute(found, "HasProperties"));
165
+ const missing = names.find((property) => !current.some((id) => nameOf(tx, id) === property));
166
+ if (missing !== undefined) {
167
+ throw new Error(`The property set '${name}' of ${describeObject(tx, object)} has no property named '${missing}'`);
168
+ }
169
+ const kept = current.filter((property) => !names.includes(nameOf(tx, property)));
170
+ if (!kept.length) {
171
+ removePropertySet(tx, writer, object, name);
172
+ return;
173
+ }
174
+ const set = ownSet(tx, writer, object, found);
175
+ tx.update(set, { HasProperties: kept.map(ref) });
176
+ tx.dropIfUnused(current.filter((property) => !kept.includes(property)));
177
+ }
178
+ export function removePropertySet(tx, writer, object, name) {
179
+ const set = namedSet(tx, object, name);
180
+ detachSet(tx, writer, object, set);
181
+ tx.dropIfUnused([set]);
182
+ }
@@ -0,0 +1,7 @@
1
+ import type * as Inputs from "../api/inputs";
2
+ import type { ModelSnapshot } from "../model/snapshot";
3
+ import type { IfcTransaction } from "../model/transaction";
4
+ import type { ModelReader } from "./build-types";
5
+ import type { EntityWriter } from "./entity-writer";
6
+ export declare function computeQuantities(tx: IfcTransaction, writer: EntityWriter, model: ModelSnapshot, elements: readonly number[] | undefined, tolerance: number): void;
7
+ export declare function quantitiesOf(reader: ModelReader, element: number): Inputs.IFC.QuantitySetInfoDto[];
@@ -0,0 +1,262 @@
1
+ import { composeAxes } from "@bitbybit-dev/base/lib/api/services/helpers/frame-axes";
2
+ import { add2, scale2 } from "@bitbybit-dev/base/lib/api/services/helpers/vectors";
3
+ import { isList, isReference, ref, textValue } from "../step/values";
4
+ import { AGGREGATION, PROPERTY_DEFINITION, VOIDING } from "./constants";
5
+ import { areaBetween, areaWithin, boundsOf, extrudedBodyOf, profileShape, volumeBetween, withLevels } from "./measure";
6
+ import { openingsOf, readWallOpening } from "./openings";
7
+ import { absoluteFrame, objectPlacementOf } from "./placement";
8
+ import { siPerUnit } from "./project";
9
+ import { definitionsOf } from "./properties";
10
+ import { relate, relatedOf, relatingOf } from "./relationships";
11
+ import { firstItem, unwrapClippings } from "./representations";
12
+ import { clippingPlanes } from "./roof-clipping";
13
+ import { frameOf } from "./solids";
14
+ import { describeObject } from "./spatial";
15
+ import { readableWall, sideLine } from "./wall-geometry";
16
+ import { placedOnPlan } from "./math";
17
+ const QUANTITY_ENTITIES = { length: "IfcQuantityLength", area: "IfcQuantityArea", volume: "IfcQuantityVolume" };
18
+ const VALUE_ATTRIBUTES = {
19
+ IfcQuantityLength: "LengthValue",
20
+ IfcQuantityArea: "AreaValue",
21
+ IfcQuantityVolume: "VolumeValue",
22
+ IfcQuantityCount: "CountValue",
23
+ IfcQuantityWeight: "WeightValue",
24
+ IfcQuantityTime: "TimeValue",
25
+ };
26
+ const MEMBER_SETS = { IfcColumn: "Qto_ColumnBaseQuantities", IfcBeam: "Qto_BeamBaseQuantities", IfcMember: "Qto_MemberBaseQuantities" };
27
+ const FILLING_SETS = { IfcDoor: "Qto_DoorBaseQuantities", IfcWindow: "Qto_WindowBaseQuantities" };
28
+ const MEASURED_TYPES = ["IfcWall", "IfcSlab", "IfcColumn", "IfcBeam", "IfcMember", "IfcDoor", "IfcWindow", "IfcOpeningElement", "IfcSpace", "IfcRoof"];
29
+ const ELEMENT_QUANTITY = "IfcElementQuantity";
30
+ const PLAIN_HALF_SPACE = "IfcHalfSpaceSolid";
31
+ function quantity(name, kind, value) {
32
+ return { name, kind, value: Math.max(0, value) };
33
+ }
34
+ function wallSet(reader, wall, tolerance) {
35
+ var _a;
36
+ const geometry = readableWall(reader, wall);
37
+ const items = (geometry === null || geometry === void 0 ? void 0 : geometry.body) === undefined ? null : reader.attribute(geometry.body, "Items");
38
+ if (!(geometry === null || geometry === void 0 ? void 0 : geometry.body) || !isList(items) || items.length !== 1) {
39
+ return undefined;
40
+ }
41
+ const [solid, halfSpaces] = unwrapClippings(reader, firstItem(reader, geometry.body));
42
+ const profile = reader.entity(solid).type === "IfcExtrudedAreaSolid" ? reader.attribute(solid, "SweptArea") : null;
43
+ const outline = isReference(profile) ? (_a = profileShape(reader, profile.ref)) === null || _a === void 0 ? void 0 : _a.outline : undefined;
44
+ if (!outline || halfSpaces.some((halfSpace) => reader.entity(halfSpace).type !== PLAIN_HALF_SPACE)) {
45
+ return undefined;
46
+ }
47
+ const solidFrame = composeAxes(geometry.frame, frameOf(reader, reader.attribute(solid, "Position")));
48
+ const footprint = outline.map((point) => placedOnPlan(solidFrame, point));
49
+ const base = geometry.frame.origin[2] + geometry.base;
50
+ const bounds = boundsOf(base, base + geometry.height, clippingPlanes(reader, geometry, geometry.body));
51
+ const thickness = geometry.high - geometry.low;
52
+ const middle = sideLine(geometry, (geometry.low + geometry.high) / 2);
53
+ const along = (distance) => add2(middle.point, scale2(middle.direction, distance));
54
+ const grossSide = areaBetween(along(0), along(geometry.length), bounds);
55
+ const grossVolume = volumeBetween(footprint, bounds);
56
+ const footprintArea = areaWithin(footprint, bounds.sides);
57
+ const places = openingsOf(reader, wall).flatMap((opening) => { var _a; return (_a = readWallOpening(reader, geometry, objectPlacementOf(reader, wall), opening, tolerance)) !== null && _a !== void 0 ? _a : []; });
58
+ const cutSide = places.reduce((sum, place) => sum + areaBetween(along(place.offset), along(place.offset + place.width), withLevels(bounds, base + place.sill, base + place.sill + place.height)), 0);
59
+ const cutFootprint = places.filter((place) => place.sill <= tolerance).reduce((sum, place) => sum + place.width * thickness, 0);
60
+ return {
61
+ name: "Qto_WallBaseQuantities",
62
+ quantities: [
63
+ quantity("Length", "length", geometry.length),
64
+ quantity("Width", "length", thickness),
65
+ quantity("Height", "length", geometry.height),
66
+ quantity("GrossFootprintArea", "area", footprintArea),
67
+ quantity("NetFootprintArea", "area", footprintArea - cutFootprint),
68
+ quantity("GrossSideArea", "area", grossSide),
69
+ quantity("NetSideArea", "area", grossSide - cutSide),
70
+ quantity("GrossVolume", "volume", grossVolume),
71
+ quantity("NetVolume", "volume", grossVolume - cutSide * thickness),
72
+ ],
73
+ };
74
+ }
75
+ function slabSet(reader, slab) {
76
+ const extruded = extrudedBodyOf(reader, slab);
77
+ if (!extruded) {
78
+ return undefined;
79
+ }
80
+ const cut = openingsOf(reader, slab).reduce((sum, opening) => { var _a, _b; return sum + ((_b = (_a = extrudedBodyOf(reader, opening)) === null || _a === void 0 ? void 0 : _a.shape.area) !== null && _b !== void 0 ? _b : 0); }, 0);
81
+ const { area, perimeter } = extruded.shape;
82
+ return {
83
+ name: "Qto_SlabBaseQuantities",
84
+ quantities: [
85
+ quantity("Width", "length", extruded.depth),
86
+ quantity("Perimeter", "length", perimeter),
87
+ quantity("GrossArea", "area", area),
88
+ quantity("NetArea", "area", area - cut),
89
+ quantity("GrossVolume", "volume", area * extruded.depth),
90
+ quantity("NetVolume", "volume", (area - cut) * extruded.depth),
91
+ ],
92
+ };
93
+ }
94
+ function memberSet(reader, member, name) {
95
+ const extruded = extrudedBodyOf(reader, member);
96
+ if (!extruded) {
97
+ return undefined;
98
+ }
99
+ const { area, perimeter } = extruded.shape;
100
+ return {
101
+ name,
102
+ quantities: [
103
+ quantity("Length", "length", extruded.depth),
104
+ quantity("CrossSectionArea", "area", area),
105
+ quantity("OuterSurfaceArea", "area", perimeter * extruded.depth),
106
+ quantity("GrossVolume", "volume", area * extruded.depth),
107
+ quantity("NetVolume", "volume", area * extruded.depth),
108
+ ],
109
+ };
110
+ }
111
+ function fillingSet(reader, filling, name) {
112
+ const width = reader.attribute(filling, "OverallWidth");
113
+ const height = reader.attribute(filling, "OverallHeight");
114
+ if (typeof width !== "number" || typeof height !== "number") {
115
+ return undefined;
116
+ }
117
+ return {
118
+ name,
119
+ quantities: [
120
+ quantity("Width", "length", width),
121
+ quantity("Height", "length", height),
122
+ quantity("Perimeter", "length", 2 * (width + height)),
123
+ quantity("Area", "area", width * height),
124
+ ],
125
+ };
126
+ }
127
+ function openingSet(reader, opening, tolerance) {
128
+ const host = relatingOf(reader, VOIDING, opening);
129
+ const wall = host === undefined ? undefined : readableWall(reader, host);
130
+ const place = wall === undefined || host === undefined ? undefined : readWallOpening(reader, wall, objectPlacementOf(reader, host), opening, tolerance);
131
+ if (!wall || !place) {
132
+ return undefined;
133
+ }
134
+ return {
135
+ name: "Qto_OpeningElementBaseQuantities",
136
+ quantities: [quantity("Width", "length", place.width), quantity("Height", "length", place.height), quantity("Depth", "length", wall.high - wall.low)],
137
+ };
138
+ }
139
+ function spaceSet(reader, space) {
140
+ const extruded = extrudedBodyOf(reader, space);
141
+ if (!extruded) {
142
+ return undefined;
143
+ }
144
+ const { area, perimeter } = extruded.shape;
145
+ return {
146
+ name: "Qto_SpaceBaseQuantities",
147
+ quantities: [
148
+ quantity("Height", "length", extruded.depth),
149
+ quantity("GrossPerimeter", "length", perimeter),
150
+ quantity("GrossFloorArea", "area", area),
151
+ quantity("NetFloorArea", "area", area),
152
+ quantity("GrossVolume", "volume", area * extruded.depth),
153
+ quantity("NetVolume", "volume", area * extruded.depth),
154
+ ],
155
+ };
156
+ }
157
+ function roofSet(reader, roof) {
158
+ const parts = relatedOf(reader, AGGREGATION, roof);
159
+ const measured = parts.flatMap((part) => {
160
+ const extruded = extrudedBodyOf(reader, part);
161
+ if (!extruded) {
162
+ return [];
163
+ }
164
+ const frame = composeAxes(absoluteFrame(reader, objectPlacementOf(reader, part)), extruded.frame);
165
+ return [{ area: extruded.shape.area, projected: extruded.shape.area * Math.abs(frame.z[2]) }];
166
+ });
167
+ if (!measured.length || measured.length !== parts.length) {
168
+ return undefined;
169
+ }
170
+ const area = measured.reduce((sum, part) => sum + part.area, 0);
171
+ return {
172
+ name: "Qto_RoofBaseQuantities",
173
+ quantities: [
174
+ quantity("GrossArea", "area", area),
175
+ quantity("NetArea", "area", area),
176
+ quantity("ProjectedArea", "area", measured.reduce((sum, part) => sum + part.projected, 0)),
177
+ ],
178
+ };
179
+ }
180
+ function measuredSetOf(reader, element, tolerance) {
181
+ const type = reader.entity(element).type;
182
+ const is = (ancestor) => reader.schema.isSubtypeOf(type, ancestor);
183
+ if (is("IfcWall")) {
184
+ return wallSet(reader, element, tolerance);
185
+ }
186
+ if (is("IfcSlab")) {
187
+ return slabSet(reader, element);
188
+ }
189
+ const member = Object.keys(MEMBER_SETS).find(is);
190
+ if (member !== undefined) {
191
+ return memberSet(reader, element, MEMBER_SETS[member]);
192
+ }
193
+ const filling = Object.keys(FILLING_SETS).find(is);
194
+ if (filling !== undefined) {
195
+ return fillingSet(reader, element, FILLING_SETS[filling]);
196
+ }
197
+ if (is("IfcOpeningElement")) {
198
+ return openingSet(reader, element, tolerance);
199
+ }
200
+ if (is("IfcSpace")) {
201
+ return spaceSet(reader, element);
202
+ }
203
+ return is("IfcRoof") ? roofSet(reader, element) : undefined;
204
+ }
205
+ function scales(model) {
206
+ const metres = siPerUnit(model, "LENGTHUNIT");
207
+ return { length: 1, area: metres * metres / siPerUnit(model, "AREAUNIT"), volume: metres * metres * metres / siPerUnit(model, "VOLUMEUNIT") };
208
+ }
209
+ function replaceSet(tx, writer, element, set, scale) {
210
+ for (const user of tx.referencesTo(element)) {
211
+ const relating = tx.typeOf(user) === PROPERTY_DEFINITION.relationship ? tx.attribute(user, PROPERTY_DEFINITION.relating) : null;
212
+ if (!isReference(relating) || tx.entity(relating.ref).type !== ELEMENT_QUANTITY || tx.attribute(relating.ref, "Name") !== set.name) {
213
+ continue;
214
+ }
215
+ const related = tx.attribute(user, PROPERTY_DEFINITION.related);
216
+ const kept = (isList(related) ? related : []).filter((item) => !(isReference(item) && item.ref === element));
217
+ if (kept.length) {
218
+ tx.update(user, { [PROPERTY_DEFINITION.related]: kept });
219
+ }
220
+ else {
221
+ tx.delete(user);
222
+ }
223
+ tx.dropIfUnused([relating.ref]);
224
+ }
225
+ const quantities = set.quantities.map((measured) => {
226
+ const entity = QUANTITY_ENTITIES[measured.kind];
227
+ return writer.create(entity, { Name: measured.name, [VALUE_ATTRIBUTES[entity]]: measured.value * scale[measured.kind] });
228
+ });
229
+ const written = writer.create(ELEMENT_QUANTITY, { GlobalId: tx.globalId(undefined), Name: set.name, MethodOfMeasurement: "BaseQuantities", Quantities: quantities.map(ref) });
230
+ relate(tx, writer, PROPERTY_DEFINITION, written, [element]);
231
+ }
232
+ export function computeQuantities(tx, writer, model, elements, tolerance) {
233
+ const scale = scales(model);
234
+ const targets = elements !== null && elements !== void 0 ? elements : [...new Set(MEASURED_TYPES.flatMap((type) => model.byType(type).map((entity) => entity.id)))];
235
+ for (const element of targets) {
236
+ const set = measuredSetOf(tx, element, tolerance);
237
+ if (set) {
238
+ replaceSet(tx, writer, element, set, scale);
239
+ }
240
+ else if (elements) {
241
+ throw new Error(`${describeObject(tx, element)} is not an element whose base quantities this library measures`);
242
+ }
243
+ }
244
+ }
245
+ export function quantitiesOf(reader, element) {
246
+ return definitionsOf(reader, element)
247
+ .filter((definition) => reader.entity(definition).type === ELEMENT_QUANTITY)
248
+ .map((set) => {
249
+ var _a;
250
+ const values = {};
251
+ const quantities = reader.attribute(set, "Quantities");
252
+ for (const item of isList(quantities) ? quantities : []) {
253
+ const attribute = isReference(item) ? VALUE_ATTRIBUTES[reader.entity(item.ref).type] : undefined;
254
+ const name = isReference(item) ? reader.attribute(item.ref, "Name") : null;
255
+ const value = isReference(item) && attribute !== undefined ? reader.attribute(item.ref, attribute) : null;
256
+ if (typeof name === "string" && typeof value === "number") {
257
+ values[name] = value;
258
+ }
259
+ }
260
+ return { name: (_a = textValue(reader.attribute(set, "Name"))) !== null && _a !== void 0 ? _a : "", quantities: values };
261
+ });
262
+ }
@@ -0,0 +1,9 @@
1
+ import type { IfcTransaction } from "../model/transaction";
2
+ import type { ModelReader, RelationshipRoles } from "./build-types";
3
+ import type { EntityWriter } from "./entity-writer";
4
+ export declare function relatingOf(reader: ModelReader, roles: RelationshipRoles, part: number): number | undefined;
5
+ export declare function relationshipsOf(reader: ModelReader, roles: RelationshipRoles, whole: number): number[];
6
+ export declare function relate(tx: IfcTransaction, writer: EntityWriter, roles: RelationshipRoles, whole: number, parts: readonly number[]): number;
7
+ export declare function appendToRelationship(tx: IfcTransaction, writer: EntityWriter, roles: RelationshipRoles, whole: number, parts: readonly number[]): number;
8
+ export declare function detach(tx: IfcTransaction, roles: RelationshipRoles, part: number): number[];
9
+ export declare function relatedOf(reader: ModelReader, roles: RelationshipRoles, whole: number): number[];