@itwin/rpcinterface-full-stack-tests 5.6.0-dev.13 → 5.6.0-dev.14

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.
@@ -100347,6 +100347,18 @@ class JsonParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_2__.AbstractPa
100347
100347
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'uomSeparator' attribute. It should be of type 'string'.`);
100348
100348
  if (undefined !== jsonObj.scientificType && typeof (jsonObj.scientificType) !== "string")
100349
100349
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'scientificType' attribute. It should be of type 'string'.`);
100350
+ if (undefined !== jsonObj.ratioType && typeof (jsonObj.ratioType) !== "string")
100351
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'ratioType' attribute. It should be of type 'string'.`);
100352
+ if (undefined !== jsonObj.ratioSeparator && typeof (jsonObj.ratioSeparator) !== "string")
100353
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'ratioSeparator' attribute. It should be of type 'string'.`);
100354
+ if (undefined !== jsonObj.ratioFormatType && typeof (jsonObj.ratioFormatType) !== "string")
100355
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'ratioFormatType' attribute. It should be of type 'string'.`);
100356
+ // Validate EC version if ratio properties exist - they require EC version 3.3+
100357
+ if (jsonObj.ratioType !== undefined || jsonObj.ratioSeparator !== undefined || jsonObj.ratioFormatType !== undefined) {
100358
+ if (this._ecSpecVersion === undefined || this._ecSpecVersion.readVersion < 3 || (this._ecSpecVersion.readVersion === 3 && this._ecSpecVersion.writeVersion < 3)) {
100359
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has ratio properties that require EC version 3.3 or newer.`);
100360
+ }
100361
+ }
100350
100362
  if (undefined !== jsonObj.stationOffsetSize && typeof (jsonObj.stationOffsetSize) !== "number")
100351
100363
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'stationOffsetSize' attribute. It should be of type 'number'.`);
100352
100364
  if (undefined !== jsonObj.stationSeparator && typeof (jsonObj.stationSeparator) !== "string")
@@ -101060,6 +101072,15 @@ class XmlParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_5__.AbstractPar
101060
101072
  const thousandSeparator = this.getOptionalAttribute(xmlElement, "thousandSeparator");
101061
101073
  const uomSeparator = this.getOptionalAttribute(xmlElement, "uomSeparator");
101062
101074
  const scientificType = this.getOptionalAttribute(xmlElement, "scientificType");
101075
+ const ratioType = this.getOptionalAttribute(xmlElement, "ratioType");
101076
+ const ratioSeparator = this.getOptionalAttribute(xmlElement, "ratioSeparator");
101077
+ const ratioFormatType = this.getOptionalAttribute(xmlElement, "ratioFormatType");
101078
+ // Validate EC version if ratio properties exist - they require EC version 3.3+
101079
+ if (ratioType !== undefined || ratioSeparator !== undefined || ratioFormatType !== undefined) {
101080
+ if (this._ecSpecVersion === undefined || this._ecSpecVersion.readVersion < 3 || (this._ecSpecVersion.readVersion === 3 && this._ecSpecVersion.writeVersion < 3)) {
101081
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidSchemaXML, `The Format ${this._currentItemFullName} has ratio properties that require EC version 3.3 or newer.`);
101082
+ }
101083
+ }
101063
101084
  const stationOffsetSize = this.getOptionalIntAttribute(xmlElement, "stationOffsetSize", `The Format ${this._currentItemFullName} has an invalid 'stationOffsetSize' attribute. It should be a numeric value.`);
101064
101085
  const stationSeparator = this.getOptionalAttribute(xmlElement, "stationSeparator");
101065
101086
  let composite;
@@ -101102,6 +101123,9 @@ class XmlParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_5__.AbstractPar
101102
101123
  thousandSeparator,
101103
101124
  uomSeparator,
101104
101125
  scientificType,
101126
+ ratioType,
101127
+ ratioSeparator,
101128
+ ratioFormatType,
101105
101129
  stationOffsetSize,
101106
101130
  stationSeparator,
101107
101131
  composite,
@@ -102845,7 +102869,7 @@ class SchemaFormatsProvider {
102845
102869
  // If no matching presentation format was found, use persistence unit format if it matches unit system.
102846
102870
  const persistenceUnit = await kindOfQuantity.persistenceUnit;
102847
102871
  const persistenceUnitSystem = await persistenceUnit?.unitSystem;
102848
- if (persistenceUnitSystem && unitSystemMatchers.some((matcher) => matcher(persistenceUnitSystem))) {
102872
+ if (persistenceUnit && persistenceUnitSystem && unitSystemMatchers.some((matcher) => matcher(persistenceUnitSystem))) {
102849
102873
  this._formatsRetrieved.add(itemKey.fullName);
102850
102874
  const props = getPersistenceUnitFormatProps(persistenceUnit);
102851
102875
  return this.convertToFormatDefinition(props, kindOfQuantity);
@@ -107076,6 +107100,9 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
107076
107100
  get stationSeparator() { return this._base.stationSeparator; }
107077
107101
  get stationOffsetSize() { return this._base.stationOffsetSize; }
107078
107102
  get stationBaseFactor() { return this._base.stationBaseFactor; }
107103
+ get ratioType() { return this._base.ratioType; }
107104
+ get ratioSeparator() { return this._base.ratioSeparator; }
107105
+ get ratioFormatType() { return this._base.ratioFormatType; }
107079
107106
  get formatTraits() { return this._base.formatTraits; }
107080
107107
  get spacer() { return this._base.spacer; }
107081
107108
  get includeZero() { return this._base.includeZero; }
@@ -107097,10 +107124,13 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
107097
107124
  addUnit(unit, label) {
107098
107125
  if (undefined === this._units)
107099
107126
  this._units = [];
107100
- else { // Validate that a duplicate is not added.
107101
- for (const existingUnit of this._units) {
107102
- if (unit.fullName.toLowerCase() === existingUnit[0].fullName.toLowerCase())
107103
- throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has duplicate units, '${unit.fullName}'.`); // TODO: Validation - this should be a validation error not a hard failure.
107127
+ else {
107128
+ const isDuplicateAllowed = this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio;
107129
+ if (!isDuplicateAllowed) {
107130
+ for (const existingUnit of this._units) {
107131
+ if (unit.fullName.toLowerCase() === existingUnit[0].fullName.toLowerCase())
107132
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has duplicate units, '${unit.fullName}'.`); // TODO: Validation - this should be a validation error not a hard failure.
107133
+ }
107104
107134
  }
107105
107135
  }
107106
107136
  this._units.push([unit, label]);
@@ -107125,37 +107155,87 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
107125
107155
  if (formatProps.composite.units.length <= 0 || formatProps.composite.units.length > 4)
107126
107156
  throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has an invalid 'Composite' attribute. It should have 1-4 units.`);
107127
107157
  }
107158
+ // For Ratio formats: validate that composite is provided
107159
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio) {
107160
+ const hasComposite = undefined !== formatProps.composite && formatProps.composite.units.length > 0;
107161
+ if (!hasComposite) {
107162
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} is 'Ratio' type and must have 'composite' units.`);
107163
+ }
107164
+ }
107128
107165
  }
107129
107166
  fromJSONSync(formatProps) {
107130
107167
  super.fromJSONSync(formatProps);
107131
107168
  this.typecheck(formatProps);
107132
- if (undefined === formatProps.composite)
107133
- return;
107134
- // Units are separated from the rest of the deserialization because of the need to have separate sync and async implementation
107135
- for (const unit of formatProps.composite.units) {
107136
- const newUnit = this.schema.lookupItemSync(unit.name);
107137
- if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
107138
- throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
107139
- if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit))
107140
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
107141
- else if (_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit))
107142
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
107169
+ // Process composite units
107170
+ if (undefined !== formatProps.composite) {
107171
+ for (const unit of formatProps.composite.units) {
107172
+ const newUnit = this.schema.lookupItemSync(unit.name);
107173
+ if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
107174
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
107175
+ const lazyUnit = _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit)
107176
+ ? new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit)
107177
+ : new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit);
107178
+ this.addUnit(lazyUnit, unit.label);
107179
+ }
107180
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
107181
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && this._units && this._units.length === 2) {
107182
+ const unit1Item = this.schema.lookupItemSync(this._units[0][0].fullName);
107183
+ const unit2Item = this.schema.lookupItemSync(this._units[1][0].fullName);
107184
+ if (!unit1Item || !unit2Item || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unit1Item) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(unit1Item)) || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unit2Item) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(unit2Item)))
107185
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has invalid units.`);
107186
+ const getPhenomenon = (unitItem) => {
107187
+ if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unitItem)) {
107188
+ return unitItem.phenomenon;
107189
+ }
107190
+ const invertsUnit = unitItem.invertsUnit;
107191
+ if (invertsUnit) {
107192
+ const resolvedUnit = this.schema.lookupItemSync(invertsUnit.fullName);
107193
+ return resolvedUnit && _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(resolvedUnit) ? resolvedUnit.phenomenon : undefined;
107194
+ }
107195
+ return undefined;
107196
+ };
107197
+ const phenomenon1 = getPhenomenon(unit1Item);
107198
+ const phenomenon2 = getPhenomenon(unit2Item);
107199
+ if (phenomenon1?.fullName !== phenomenon2?.fullName) {
107200
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has 2-unit composite with different phenomena. Both units must have the same phenomenon.`);
107201
+ }
107202
+ }
107143
107203
  }
107144
107204
  }
107145
107205
  async fromJSON(formatProps) {
107146
107206
  await super.fromJSON(formatProps);
107147
107207
  this.typecheck(formatProps);
107148
- if (undefined === formatProps.composite)
107149
- return;
107150
- // Units are separated from the rest of the deserialization because of the need to have separate sync and async implementation
107151
- for (const unit of formatProps.composite.units) {
107152
- const newUnit = await this.schema.lookupItem(unit.name);
107153
- if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
107154
- throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
107155
- if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit))
107156
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
107157
- else if (_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit))
107158
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
107208
+ // Process composite units
107209
+ if (undefined !== formatProps.composite) {
107210
+ for (const unit of formatProps.composite.units) {
107211
+ const newUnit = await this.schema.lookupItem(unit.name);
107212
+ if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
107213
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
107214
+ const lazyUnit = _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit)
107215
+ ? new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit)
107216
+ : new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit);
107217
+ this.addUnit(lazyUnit, unit.label);
107218
+ }
107219
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
107220
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && this._units && this._units.length === 2) {
107221
+ const unit1Item = await this.schema.lookupItem(this._units[0][0].fullName);
107222
+ const unit2Item = await this.schema.lookupItem(this._units[1][0].fullName);
107223
+ if (!unit1Item || !unit2Item || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unit1Item) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(unit1Item)) || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unit2Item) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(unit2Item)))
107224
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has invalid units.`);
107225
+ // Helper to extract phenomenon from Unit or InvertedUnit
107226
+ const getPhenomenon = async (unitItem) => {
107227
+ if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unitItem)) {
107228
+ return unitItem.phenomenon;
107229
+ }
107230
+ const invertsUnit = await unitItem.invertsUnit;
107231
+ return invertsUnit ? invertsUnit.phenomenon : undefined;
107232
+ };
107233
+ const phenomenon1 = await getPhenomenon(unit1Item);
107234
+ const phenomenon2 = await getPhenomenon(unit2Item);
107235
+ if (phenomenon1?.fullName !== phenomenon2?.fullName) {
107236
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has 2-unit composite with different phenomena. Both units must have the same phenomenon.`);
107237
+ }
107238
+ }
107159
107239
  }
107160
107240
  }
107161
107241
  /**
@@ -107192,6 +107272,15 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
107192
107272
  if (" " !== this.stationSeparator)
107193
107273
  schemaJson.stationSeparator = this.stationSeparator;
107194
107274
  }
107275
+ // Only include ratio properties for EC version 3.3+
107276
+ if (_itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio === this.type && this.schema.originalECSpecMajorVersion === 3 && this.schema.originalECSpecMinorVersion !== undefined && this.schema.originalECSpecMinorVersion >= 3) {
107277
+ if (undefined !== this.ratioType)
107278
+ schemaJson.ratioType = this.ratioType;
107279
+ if (undefined !== this.ratioSeparator)
107280
+ schemaJson.ratioSeparator = this.ratioSeparator;
107281
+ if (undefined !== this.ratioFormatType)
107282
+ schemaJson.ratioFormatType = this.ratioFormatType;
107283
+ }
107195
107284
  if (undefined === this.units)
107196
107285
  return schemaJson;
107197
107286
  schemaJson.composite = {};
@@ -107223,6 +107312,15 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
107223
107312
  itemElement.setAttribute("minWidth", this.minWidth.toString());
107224
107313
  if (undefined !== this.scientificType)
107225
107314
  itemElement.setAttribute("scientificType", this.scientificType);
107315
+ // Only include ratio properties for EC version 3.3+
107316
+ if (this.schema.originalECSpecMajorVersion === 3 && this.schema.originalECSpecMinorVersion !== undefined && this.schema.originalECSpecMinorVersion >= 3) {
107317
+ if (undefined !== this.ratioType)
107318
+ itemElement.setAttribute("ratioType", this.ratioType);
107319
+ if (undefined !== this.ratioSeparator)
107320
+ itemElement.setAttribute("ratioSeparator", this.ratioSeparator);
107321
+ if (undefined !== this.ratioFormatType)
107322
+ itemElement.setAttribute("ratioFormatType", this.ratioFormatType);
107323
+ }
107226
107324
  if (undefined !== this.stationOffsetSize)
107227
107325
  itemElement.setAttribute("stationOffsetSize", this.stationOffsetSize.toString());
107228
107326
  const formatTraits = (0,_itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.formatTraitsToArray)(this.formatTraits);
@@ -107969,6 +108067,9 @@ class OverrideFormat {
107969
108067
  get type() { return this.parent.type; }
107970
108068
  get minWidth() { return this.parent.minWidth; }
107971
108069
  get scientificType() { return this.parent.scientificType; }
108070
+ get ratioType() { return this.parent.ratioType; }
108071
+ get ratioSeparator() { return this.parent.ratioSeparator; }
108072
+ get ratioFormatType() { return this.parent.ratioFormatType; }
107972
108073
  get showSignOption() { return this.parent.showSignOption; }
107973
108074
  get decimalSeparator() { return this.parent.decimalSeparator; }
107974
108075
  get thousandSeparator() { return this.parent.thousandSeparator; }
@@ -107993,7 +108094,7 @@ class OverrideFormat {
107993
108094
  for (const [unit, unitLabel] of this._units) {
107994
108095
  const unitSchema = koqSchema.context.getSchemaSync(unit.schemaKey);
107995
108096
  if (unitSchema === undefined)
107996
- throw new _Exception__WEBPACK_IMPORTED_MODULE_3__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_3__.ECSchemaStatus.InvalidECJson, `The unit schema ${unit.schemaKey} is not found in the context.`);
108097
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_3__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_3__.ECSchemaStatus.InvalidECJson, `The unit schema ${unit.schemaKey.toString()} is not found in the context.`);
107997
108098
  fullName += "[";
107998
108099
  fullName += _Deserialization_XmlSerializationUtils__WEBPACK_IMPORTED_MODULE_0__.XmlSerializationUtils.createXmlTypedName(koqSchema, unitSchema, unit.name);
107999
108100
  if (unitLabel !== undefined)
@@ -144839,7 +144940,9 @@ function createMeshArgs(mesh) {
144839
144940
  if (!mesh.triangles || mesh.triangles.isEmpty || mesh.points.length === 0)
144840
144941
  return undefined;
144841
144942
  const texture = mesh.displayParams.textureMapping?.texture;
144842
- const textureMapping = texture && mesh.uvParams.length > 0 ? { texture, uvParams: mesh.uvParams } : undefined;
144943
+ const useConstantLod = mesh.displayParams.textureMapping?.params?.useConstantLod;
144944
+ const constantLodParams = mesh.displayParams.textureMapping?.params?.constantLodParams;
144945
+ const textureMapping = texture && mesh.uvParams.length > 0 ? { texture, uvParams: mesh.uvParams, useConstantLod, constantLodParams } : undefined;
144843
144946
  const colors = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorIndex();
144844
144947
  mesh.colorMap.toColorIndex(colors, mesh.colors);
144845
144948
  const features = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FeatureIndex();
@@ -198057,14 +198160,47 @@ class GltfReader {
198057
198160
  }
198058
198161
  }
198059
198162
  createDisplayParams(material, hasBakedLighting, isPointPrimitive = false) {
198163
+ let constantLodParamProps;
198164
+ let normalMapUseConstantLod = false;
198165
+ if (!(0,_common_gltf_GltfSchema__WEBPACK_IMPORTED_MODULE_12__.isGltf1Material)(material)) {
198166
+ // NOTE: EXT_textureInfo_constant_lod is not supported for occlusionTexture and metallicRoughnessTexture
198167
+ // Use the same texture fallback logic as extractTextureId
198168
+ const textureInfo = material.pbrMetallicRoughness?.baseColorTexture ?? material.emissiveTexture;
198169
+ const extConstantLod = textureInfo?.extensions?.EXT_textureInfo_constant_lod;
198170
+ const offset = extConstantLod?.offset;
198171
+ extConstantLod ? constantLodParamProps = {
198172
+ repetitions: extConstantLod?.repetitions,
198173
+ offset: offset ? { x: offset[0], y: offset[1] } : undefined,
198174
+ minDistClamp: extConstantLod?.minClampDistance,
198175
+ maxDistClamp: extConstantLod?.maxClampDistance,
198176
+ } : undefined;
198177
+ // Normal map only uses constant LOD if both the base texture and normal texture have the extension
198178
+ normalMapUseConstantLod = extConstantLod !== undefined && material.normalTexture?.extensions?.EXT_textureInfo_constant_lod !== undefined;
198179
+ }
198060
198180
  const isTransparent = this.isMaterialTransparent(material);
198061
198181
  const textureId = this.extractTextureId(material);
198062
198182
  const normalMapId = this.extractNormalMapId(material);
198063
- let textureMapping = (undefined !== textureId || undefined !== normalMapId) ? this.findTextureMapping(textureId, isTransparent, normalMapId) : undefined;
198183
+ let textureMapping = (undefined !== textureId || undefined !== normalMapId) ? this.findTextureMapping(textureId, isTransparent, normalMapId, constantLodParamProps, normalMapUseConstantLod) : undefined;
198064
198184
  const color = colorFromMaterial(material, isTransparent);
198065
198185
  let renderMaterial;
198066
- if (undefined !== textureMapping && undefined !== textureMapping.normalMapParams) {
198067
- const args = { diffuse: { color }, specular: { color: _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorDef.white }, textureMapping };
198186
+ if (undefined !== textureMapping) {
198187
+ // Convert result of findTextureMapping (TextureMapping object) to MaterialTextureMappingProps interface
198188
+ const textureMappingProps = {
198189
+ texture: textureMapping.texture,
198190
+ normalMapParams: textureMapping.normalMapParams,
198191
+ mode: textureMapping.params.mode,
198192
+ transform: textureMapping.params.textureMatrix,
198193
+ weight: textureMapping.params.weight,
198194
+ worldMapping: textureMapping.params.worldMapping,
198195
+ useConstantLod: textureMapping.params.useConstantLod,
198196
+ constantLodProps: textureMapping.params.useConstantLod ? {
198197
+ repetitions: textureMapping.params.constantLodParams.repetitions,
198198
+ offset: textureMapping.params.constantLodParams.offset,
198199
+ minDistClamp: textureMapping.params.constantLodParams.minDistClamp,
198200
+ maxDistClamp: textureMapping.params.constantLodParams.maxDistClamp,
198201
+ } : undefined,
198202
+ };
198203
+ const args = { diffuse: { color }, specular: { color: _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorDef.white }, textureMapping: textureMappingProps };
198068
198204
  renderMaterial = _IModelApp__WEBPACK_IMPORTED_MODULE_3__.IModelApp.renderSystem.createRenderMaterial(args);
198069
198205
  // DisplayParams doesn't want a separate texture mapping if the material already has one.
198070
198206
  textureMapping = undefined;
@@ -199022,7 +199158,7 @@ class GltfReader {
199022
199158
  });
199023
199159
  return renderTexture ?? false;
199024
199160
  }
199025
- findTextureMapping(id, isTransparent, normalMapId) {
199161
+ findTextureMapping(id, isTransparent, normalMapId, constantLodParamProps, normalMapUseConstantLod = false) {
199026
199162
  if (undefined === id && undefined === normalMapId)
199027
199163
  return undefined;
199028
199164
  let texture;
@@ -199044,16 +199180,18 @@ class GltfReader {
199044
199180
  nMap = {
199045
199181
  normalMap,
199046
199182
  greenUp,
199183
+ useConstantLod: normalMapUseConstantLod,
199047
199184
  };
199048
199185
  }
199049
199186
  else {
199050
199187
  texture = normalMap;
199051
- nMap = { greenUp };
199188
+ nMap = { greenUp, useConstantLod: normalMapUseConstantLod };
199052
199189
  }
199053
199190
  }
199054
199191
  if (!texture)
199055
199192
  return undefined;
199056
- const textureMapping = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping(texture, new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping.Params());
199193
+ const useConstantLod = constantLodParamProps !== undefined;
199194
+ const textureMapping = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping(texture, new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping.Params({ useConstantLod, constantLodProps: constantLodParamProps }));
199057
199195
  textureMapping.normalMapParams = nMap;
199058
199196
  return textureMapping;
199059
199197
  }
@@ -340749,6 +340887,8 @@ class BaseFormat {
340749
340887
  _stationOffsetSize; // required when type is station; positive integer > 0
340750
340888
  _stationBaseFactor; // optional positive integer base factor for station formatting; default is 1
340751
340889
  _ratioType; // required if type is ratio; options: oneToN, NToOne, ValueBased, useGreatestCommonDivisor
340890
+ _ratioFormatType; // defaults to Decimal if not specified
340891
+ _ratioSeparator; // default is ":"; separator character used in ratio formatting
340752
340892
  _azimuthBase; // value always clockwise from north
340753
340893
  _azimuthBaseUnit; // unit for azimuthBase value
340754
340894
  _azimuthCounterClockwise; // if set to true, azimuth values are returned counter-clockwise from base
@@ -340770,6 +340910,10 @@ class BaseFormat {
340770
340910
  set scientificType(scientificType) { this._scientificType = scientificType; }
340771
340911
  get ratioType() { return this._ratioType; }
340772
340912
  set ratioType(ratioType) { this._ratioType = ratioType; }
340913
+ get ratioFormatType() { return this._ratioFormatType; }
340914
+ set ratioFormatType(ratioFormatType) { this._ratioFormatType = ratioFormatType; }
340915
+ get ratioSeparator() { return this._ratioSeparator; }
340916
+ set ratioSeparator(ratioSeparator) { this._ratioSeparator = ratioSeparator; }
340773
340917
  get showSignOption() { return this._showSignOption; }
340774
340918
  set showSignOption(showSignOption) { this._showSignOption = showSignOption; }
340775
340919
  get decimalSeparator() { return this._decimalSeparator; }
@@ -340838,6 +340982,22 @@ class BaseFormat {
340838
340982
  if (undefined === formatProps.ratioType)
340839
340983
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} is 'Ratio' type therefore the attribute 'ratioType' is required.`);
340840
340984
  this._ratioType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseRatioType)(formatProps.ratioType, this.name);
340985
+ if (undefined !== formatProps.ratioSeparator) {
340986
+ if (typeof (formatProps.ratioSeparator) !== "string")
340987
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} has an invalid 'ratioSeparator' attribute. It should be of type 'string'.`);
340988
+ if (formatProps.ratioSeparator.length !== 1)
340989
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} has an invalid 'ratioSeparator' attribute. It should be a one character string.`);
340990
+ this._ratioSeparator = formatProps.ratioSeparator;
340991
+ }
340992
+ else {
340993
+ this._ratioSeparator = ":"; // Apply default
340994
+ }
340995
+ if (undefined !== formatProps.ratioFormatType) {
340996
+ this._ratioFormatType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseRatioFormatType)(formatProps.ratioFormatType, this.name);
340997
+ }
340998
+ else {
340999
+ this._ratioFormatType = _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioFormatType.Decimal; // Apply default
341000
+ }
340841
341001
  }
340842
341002
  if (undefined !== formatProps.roundFactor) { // optional; default is 0.0
340843
341003
  if (typeof (formatProps.roundFactor) !== "number")
@@ -340962,6 +341122,8 @@ class Format extends BaseFormat {
340962
341122
  newFormat._azimuthBaseUnit = this._azimuthBaseUnit;
340963
341123
  newFormat._azimuthCounterClockwise = this._azimuthCounterClockwise;
340964
341124
  newFormat._ratioType = this._ratioType;
341125
+ newFormat._ratioFormatType = this._ratioFormatType;
341126
+ newFormat._ratioSeparator = this._ratioSeparator;
340965
341127
  newFormat._revolutionUnit = this._revolutionUnit;
340966
341128
  newFormat._customProps = this._customProps;
340967
341129
  this._units && (newFormat._units = [...this._units]);
@@ -341022,25 +341184,28 @@ class Format extends BaseFormat {
341022
341184
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} has a Composite with an invalid 'units' attribute. It must be of type 'array'`);
341023
341185
  }
341024
341186
  if (jsonObj.composite.units.length > 0 && jsonObj.composite.units.length <= 4) { // Composite requires 1-4 units
341025
- for (const nextUnit of jsonObj.composite.units) {
341026
- if (this._units) {
341027
- for (const existingUnit of this._units) {
341028
- const unitObj = existingUnit[0].name;
341029
- if (unitObj.toLowerCase() === nextUnit.unit.name.toLowerCase()) {
341030
- throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The unit ${unitObj} has a duplicate name.`);
341031
- }
341187
+ const isDuplicateAllowed = this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio;
341188
+ const seenUnits = new Set();
341189
+ this._units = [];
341190
+ for (const unitSpec of jsonObj.composite.units) {
341191
+ if (!isDuplicateAllowed) {
341192
+ const unitName = unitSpec.unit.name.toLowerCase();
341193
+ const existingName = seenUnits.has(unitName);
341194
+ if (existingName) {
341195
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} contains duplicate units: '${unitSpec.unit.name}'`);
341032
341196
  }
341197
+ seenUnits.add(unitName);
341033
341198
  }
341034
- if (undefined === this._units) {
341035
- this._units = [];
341036
- }
341037
- this._units.push([nextUnit.unit, nextUnit.label]);
341199
+ this._units.push([unitSpec.unit, unitSpec.label]);
341038
341200
  }
341039
341201
  }
341040
341202
  }
341041
341203
  if (undefined === this.units || this.units.length === 0)
341042
341204
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} has a Composite with no valid 'units'`);
341043
341205
  }
341206
+ if (this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio && (!this._units || this._units.length === 0)) {
341207
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} is 'Ratio' type and must have 'composite' units.`);
341208
+ }
341044
341209
  if (this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth || this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing) {
341045
341210
  this._azimuthBaseUnit = jsonObj.azimuthBaseUnit;
341046
341211
  this._revolutionUnit = jsonObj.revolutionUnit;
@@ -341107,6 +341272,8 @@ class Format extends BaseFormat {
341107
341272
  uomSeparator: this.uomSeparator,
341108
341273
  scientificType: this.scientificType ? this.scientificType : undefined,
341109
341274
  ratioType: this.ratioType,
341275
+ ratioFormatType: this.ratioFormatType,
341276
+ ratioSeparator: this.ratioSeparator,
341110
341277
  stationOffsetSize: this.stationOffsetSize,
341111
341278
  stationSeparator: this.stationSeparator,
341112
341279
  stationBaseFactor: this.stationBaseFactor,
@@ -341151,6 +341318,15 @@ async function resolveFormatProps(formatName, unitsProvider, jsonObj) {
341151
341318
  const unit = await resolveCompositeUnit(unitsProvider, entry.name);
341152
341319
  return { unit, label: entry.label };
341153
341320
  }));
341321
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
341322
+ const formatType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseFormatType)(jsonObj.type, formatName);
341323
+ if (formatType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio && units.length === 2) {
341324
+ const phenomenon1 = units[0].unit.phenomenon;
341325
+ const phenomenon2 = units[1].unit.phenomenon;
341326
+ if (phenomenon1 !== phenomenon2) {
341327
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${formatName} has 2-unit composite with different phenomena. Both units must have the same phenomenon. Found '${phenomenon1}' and '${phenomenon2}'.`);
341328
+ }
341329
+ }
341154
341330
  }
341155
341331
  let azimuthBaseUnit, revolutionUnit;
341156
341332
  const type = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseFormatType)(jsonObj.type, formatName);
@@ -341191,6 +341367,7 @@ __webpack_require__.r(__webpack_exports__);
341191
341367
  /* harmony export */ FormatTraits: () => (/* binding */ FormatTraits),
341192
341368
  /* harmony export */ FormatType: () => (/* binding */ FormatType),
341193
341369
  /* harmony export */ FractionalPrecision: () => (/* binding */ FractionalPrecision),
341370
+ /* harmony export */ RatioFormatType: () => (/* binding */ RatioFormatType),
341194
341371
  /* harmony export */ RatioType: () => (/* binding */ RatioType),
341195
341372
  /* harmony export */ ScientificType: () => (/* binding */ ScientificType),
341196
341373
  /* harmony export */ ShowSignOption: () => (/* binding */ ShowSignOption),
@@ -341204,6 +341381,7 @@ __webpack_require__.r(__webpack_exports__);
341204
341381
  /* harmony export */ parseFormatType: () => (/* binding */ parseFormatType),
341205
341382
  /* harmony export */ parseFractionalPrecision: () => (/* binding */ parseFractionalPrecision),
341206
341383
  /* harmony export */ parsePrecision: () => (/* binding */ parsePrecision),
341384
+ /* harmony export */ parseRatioFormatType: () => (/* binding */ parseRatioFormatType),
341207
341385
  /* harmony export */ parseRatioType: () => (/* binding */ parseRatioType),
341208
341386
  /* harmony export */ parseScientificType: () => (/* binding */ parseScientificType),
341209
341387
  /* harmony export */ parseShowSignOption: () => (/* binding */ parseShowSignOption),
@@ -341348,6 +341526,16 @@ var RatioType;
341348
341526
  /** scales the input ratio to its simplest integer form using the greatest common divisor (GCD) of the values. e.g. 0.3 turns into 3:10 */
341349
341527
  RatioType["UseGreatestCommonDivisor"] = "UseGreatestCommonDivisor";
341350
341528
  })(RatioType || (RatioType = {}));
341529
+ /** The format type for the numbers within a ratio.
341530
+ * @beta
341531
+ */
341532
+ var RatioFormatType;
341533
+ (function (RatioFormatType) {
341534
+ /** Decimal display (ie 2.125) */
341535
+ RatioFormatType["Decimal"] = "Decimal";
341536
+ /** Fractional display (ie 2-1/8) */
341537
+ RatioFormatType["Fractional"] = "Fractional";
341538
+ })(RatioFormatType || (RatioFormatType = {}));
341351
341539
  /** Determines how the sign of values are displayed
341352
341540
  * @beta */
341353
341541
  var ShowSignOption;
@@ -341393,6 +341581,18 @@ function parseRatioType(ratioType, formatName) {
341393
341581
  }
341394
341582
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityStatus.InvalidJson, `The Format ${formatName} has an invalid 'ratioType' attribute.`);
341395
341583
  }
341584
+ /** @beta */
341585
+ function parseRatioFormatType(ratioFormatType, formatName) {
341586
+ const normalizedValue = ratioFormatType.toLowerCase();
341587
+ for (const key in RatioFormatType) {
341588
+ if (RatioFormatType.hasOwnProperty(key)) {
341589
+ const enumValue = RatioFormatType[key];
341590
+ if (enumValue.toLowerCase() === normalizedValue)
341591
+ return enumValue;
341592
+ }
341593
+ }
341594
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityStatus.InvalidJson, `The Format ${formatName} has an invalid 'ratioFormatType' attribute.`);
341595
+ }
341396
341596
  /** @beta */
341397
341597
  function parseShowSignOption(showSignOption, formatName) {
341398
341598
  switch (showSignOption.toLowerCase()) {
@@ -341554,10 +341754,18 @@ function parsePrecision(precision, type, formatName) {
341554
341754
  case FormatType.Decimal:
341555
341755
  case FormatType.Scientific:
341556
341756
  case FormatType.Station:
341557
- case FormatType.Ratio:
341558
341757
  case FormatType.Bearing:
341559
341758
  case FormatType.Azimuth:
341560
341759
  return parseDecimalPrecision(precision, formatName);
341760
+ case FormatType.Ratio:
341761
+ // Ratio type can use either decimal or fractional precision depending on ratioFormatType
341762
+ // Try decimal first, if it fails, try fractional
341763
+ try {
341764
+ return parseDecimalPrecision(precision, formatName);
341765
+ }
341766
+ catch {
341767
+ return parseFractionalPrecision(precision, formatName);
341768
+ }
341561
341769
  case FormatType.Fractional:
341562
341770
  return parseFractionalPrecision(precision, formatName);
341563
341771
  default:
@@ -341581,8 +341789,9 @@ __webpack_require__.r(__webpack_exports__);
341581
341789
  /* harmony export */ });
341582
341790
  /* harmony import */ var _Constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Constants */ "../../core/quantity/lib/esm/Constants.js");
341583
341791
  /* harmony import */ var _Exception__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Exception */ "../../core/quantity/lib/esm/Exception.js");
341584
- /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
341585
- /* harmony import */ var _Quantity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../Quantity */ "../../core/quantity/lib/esm/Quantity.js");
341792
+ /* harmony import */ var _FormatterSpec__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./FormatterSpec */ "../../core/quantity/lib/esm/Formatter/FormatterSpec.js");
341793
+ /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
341794
+ /* harmony import */ var _Quantity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../Quantity */ "../../core/quantity/lib/esm/Quantity.js");
341586
341795
  /*---------------------------------------------------------------------------------------------
341587
341796
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
341588
341797
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -341594,6 +341803,7 @@ __webpack_require__.r(__webpack_exports__);
341594
341803
 
341595
341804
 
341596
341805
 
341806
+
341597
341807
  /** rounding additive
341598
341808
  * @internal
341599
341809
  */
@@ -341694,7 +341904,7 @@ class Formatter {
341694
341904
  static integerPartToText(wholePart, spec) {
341695
341905
  // build invariant string represent wholePart
341696
341906
  let formattedValue = wholePart.toFixed(0);
341697
- if ((formattedValue.length > 3) && (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.Use1000Separator) && (spec.format.thousandSeparator.length > 0))) {
341907
+ if ((formattedValue.length > 3) && (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.Use1000Separator) && (spec.format.thousandSeparator.length > 0))) {
341698
341908
  let numSeparators = Math.floor(formattedValue.length / 3);
341699
341909
  let groupLength = formattedValue.length % 3;
341700
341910
  if (groupLength === 0) {
@@ -341741,7 +341951,7 @@ class Formatter {
341741
341951
  else {
341742
341952
  componentText = Formatter.formatMagnitude(compositeValue, spec);
341743
341953
  }
341744
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ShowUnitLabel)) {
341954
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel)) {
341745
341955
  componentText = componentText + spec.format.uomSeparator + label;
341746
341956
  }
341747
341957
  return componentText;
@@ -341761,12 +341971,11 @@ class Formatter {
341761
341971
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has a invalid unit specification.`);
341762
341972
  if (i > 0 && unitConversion.offset !== 0) // offset should only ever be defined for major unit
341763
341973
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has a invalid unit specification.`);
341764
- let unitValue = 0.0;
341765
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio) {
341766
- if (1 !== (spec.format.units?.length ?? 0))
341767
- throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format '${spec.format.name}' with type 'ratio' must have exactly one unit.`);
341974
+ // Handle ratio format with composite units
341975
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio) {
341976
+ let ratioUnitValue = 0.0;
341768
341977
  try {
341769
- unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
341978
+ ratioUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
341770
341979
  }
341771
341980
  catch (e) {
341772
341981
  // The "InvertingZero" error is thrown when the value is zero and the conversion factor is inverted.
@@ -341774,12 +341983,13 @@ class Formatter {
341774
341983
  if (e instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && e.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
341775
341984
  return { componentText: "1:0", isNegative: false };
341776
341985
  }
341986
+ throw e;
341777
341987
  }
341778
- compositeStrings.push(this.formatRatio(unitValue, spec));
341779
- isNegative = unitValue < 0;
341988
+ compositeStrings.push(this.formatRatio(ratioUnitValue, spec));
341989
+ isNegative = ratioUnitValue < 0;
341780
341990
  continue;
341781
341991
  }
341782
- unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
341992
+ let unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
341783
341993
  if (0 === i) {
341784
341994
  // Only set isNegative from the first (major) unit conversion
341785
341995
  isNegative = unitValue < 0;
@@ -341787,16 +341997,16 @@ class Formatter {
341787
341997
  // but use higher precision if the format specifies it
341788
341998
  const precisionScale = Math.pow(10, Math.max(8, spec.format.precision));
341789
341999
  unitValue = Math.floor(unitValue * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
341790
- if ((Math.abs(unitValue) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ZeroEmpty))
342000
+ if ((Math.abs(unitValue) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ZeroEmpty))
341791
342001
  return { componentText: "", isNegative: false };
341792
342002
  }
341793
342003
  if (i < (spec.format.units?.length ?? 0) - 1) {
341794
342004
  let wholePart = Math.trunc(unitValue);
341795
342005
  // Check if the remaining fractional part will round up to a full unit in the next (smaller) component
341796
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Fractional && i === spec.unitConversions.length - 2) {
342006
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional && i === spec.unitConversions.length - 2) {
341797
342007
  // For the second-to-last unit with fractional formatting, check if rounding causes carry-over
341798
342008
  const fractionalPart = unitValue - wholePart;
341799
- const nextUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(fractionalPart, spec.unitConversions[i + 1].conversion);
342009
+ const nextUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(fractionalPart, spec.unitConversions[i + 1].conversion);
341800
342010
  // Create a FractionalNumeric to determine what the rounded value would be
341801
342011
  const fn = new FractionalNumeric(Math.abs(nextUnitValue), spec.format.precision, true);
341802
342012
  // If the fractional numeric rounds to a whole unit (integral part increased due to rounding)
@@ -341826,18 +342036,18 @@ class Formatter {
341826
342036
  */
341827
342037
  static formatMagnitude(magnitude, spec) {
341828
342038
  let posMagnitude = Math.abs(magnitude);
341829
- if ((Math.abs(posMagnitude) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ZeroEmpty))
342039
+ if ((Math.abs(posMagnitude) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ZeroEmpty))
341830
342040
  return "";
341831
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ApplyRounding))
342041
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ApplyRounding))
341832
342042
  posMagnitude = Math.abs(Formatter.roundDouble(magnitude, spec.format.roundFactor));
341833
- const isSci = ((posMagnitude > 1.0e12) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Scientific);
341834
- const isDecimal = (isSci || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Decimal || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio;
341835
- const isFractional = (!isDecimal && spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Fractional);
342043
+ const isSci = ((posMagnitude > 1.0e12) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Scientific);
342044
+ const isDecimal = (isSci || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Decimal || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio;
342045
+ const isFractional = (!isDecimal && spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional);
341836
342046
  /* const usesStops = spec.format.type === FormatType.Station; */
341837
- const isPrecisionZero = spec.format.precision === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.DecimalPrecision.Zero;
341838
- const isKeepSingleZero = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepSingleZero);
342047
+ const isPrecisionZero = spec.format.precision === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.DecimalPrecision.Zero;
342048
+ const isKeepSingleZero = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepSingleZero);
341839
342049
  const precisionScale = Math.pow(10.0, spec.format.precision);
341840
- const isKeepTrailingZeroes = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.TrailZeroes);
342050
+ const isKeepTrailingZeroes = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.TrailZeroes);
341841
342051
  let expInt = 0.0;
341842
342052
  if (isSci && (posMagnitude !== 0.0)) {
341843
342053
  let exp = Math.log10(posMagnitude);
@@ -341847,10 +342057,10 @@ class Formatter {
341847
342057
  negativeExp = true;
341848
342058
  }
341849
342059
  expInt = Math.floor(exp);
341850
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Scientific) {
341851
- if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ScientificType.ZeroNormalized && posMagnitude > 1.0)
342060
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Scientific) {
342061
+ if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ScientificType.ZeroNormalized && posMagnitude > 1.0)
341852
342062
  expInt += 1.0;
341853
- else if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ScientificType.Normalized && posMagnitude < 1.0)
342063
+ else if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ScientificType.Normalized && posMagnitude < 1.0)
341854
342064
  expInt += 1.0;
341855
342065
  if (negativeExp)
341856
342066
  expInt = -expInt;
@@ -341872,7 +342082,7 @@ class Formatter {
341872
342082
  }
341873
342083
  formattedValue = Formatter.integerPartToText(wholePart, spec);
341874
342084
  if (isPrecisionZero) {
341875
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint) && !isKeepSingleZero)
342085
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint) && !isKeepSingleZero)
341876
342086
  formattedValue = formattedValue + spec.format.decimalSeparator;
341877
342087
  else if (isKeepSingleZero)
341878
342088
  formattedValue = `${formattedValue + spec.format.decimalSeparator}0`;
@@ -341887,7 +342097,7 @@ class Formatter {
341887
342097
  if (fractionString.length > 0)
341888
342098
  formattedValue = formattedValue + spec.format.decimalSeparator + fractionString;
341889
342099
  else {
341890
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint))
342100
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint))
341891
342101
  formattedValue = formattedValue + spec.format.decimalSeparator + (isKeepSingleZero ? "0" : "");
341892
342102
  }
341893
342103
  }
@@ -341900,7 +342110,7 @@ class Formatter {
341900
342110
  const fn = new FractionalNumeric(posMagnitude, spec.format.precision, true);
341901
342111
  formattedValue = fn.getIntegralString();
341902
342112
  if (!fn.isZero && fn.hasFractionPart) {
341903
- const wholeFractionSeparator = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.FractionDash) ? "-" : " ";
342113
+ const wholeFractionSeparator = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.FractionDash) ? "-" : " ";
341904
342114
  const fractionString = `${fn.getNumeratorString()}/${fn.getDenominatorString()}`;
341905
342115
  formattedValue = formattedValue + wholeFractionSeparator + fractionString;
341906
342116
  }
@@ -341929,7 +342139,7 @@ class Formatter {
341929
342139
  else {
341930
342140
  if (isKeepTrailingZeroes)
341931
342141
  fractionString = spec.format.decimalSeparator + "".padEnd(spec.format.precision, "0");
341932
- else if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint))
342142
+ else if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint))
341933
342143
  fractionString = spec.format.decimalSeparator;
341934
342144
  formattedValue = stationString + fractionString;
341935
342145
  }
@@ -341958,21 +342168,21 @@ class Formatter {
341958
342168
  let prefix = "";
341959
342169
  let suffix = "";
341960
342170
  switch (showSignOption) {
341961
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.NegativeParentheses:
342171
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.NegativeParentheses:
341962
342172
  if (isNegative) {
341963
342173
  prefix = "(";
341964
342174
  suffix = ")";
341965
342175
  }
341966
342176
  break;
341967
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.OnlyNegative:
341968
- if (isNegative && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
342177
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.OnlyNegative:
342178
+ if (isNegative && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
341969
342179
  prefix = "-";
341970
342180
  }
341971
342181
  break;
341972
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.SignAlways:
342182
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.SignAlways:
341973
342183
  prefix = isNegative ? "-" : "+";
341974
342184
  break;
341975
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.NoSign:
342185
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.NoSign:
341976
342186
  default:
341977
342187
  break;
341978
342188
  }
@@ -341988,14 +342198,20 @@ class Formatter {
341988
342198
  let suffix = "";
341989
342199
  let formattedValue = "";
341990
342200
  // Handle bearing/azimuth special formatting
341991
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
342201
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
341992
342202
  const result = this.processBearingAndAzimuth(magnitude, spec);
341993
342203
  magnitude = result.magnitude;
341994
342204
  prefix = result.prefix ?? "";
341995
342205
  suffix = result.suffix ?? "";
341996
342206
  }
341997
342207
  let formattedMagnitude = "";
341998
- if (spec.format.hasUnits) {
342208
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && spec.unitConversions.length >= 3) {
342209
+ // Handle ratio formatting separately when 2-unit composite provides 3 conversion specs
342210
+ const ratioResult = this.formatRatioQuantity(magnitude, spec);
342211
+ formattedMagnitude = ratioResult.componentText;
342212
+ valueIsNegative = ratioResult.isNegative;
342213
+ }
342214
+ else if (spec.format.hasUnits) {
341999
342215
  const compositeResult = Formatter.formatComposite(magnitude, spec);
342000
342216
  formattedMagnitude = compositeResult.componentText;
342001
342217
  // Override the sign detection with the composite conversion result
@@ -342004,8 +342220,8 @@ class Formatter {
342004
342220
  else {
342005
342221
  // unitless quantity
342006
342222
  formattedMagnitude = Formatter.formatMagnitude(magnitude, spec);
342007
- if (formattedMagnitude.length > 0 && spec.unitConversions.length > 0 && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ShowUnitLabel)) {
342008
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.PrependUnitLabel))
342223
+ if (formattedMagnitude.length > 0 && spec.unitConversions.length > 0 && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel)) {
342224
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.PrependUnitLabel))
342009
342225
  formattedMagnitude = spec.unitConversions[0].label + spec.format.uomSeparator + formattedMagnitude;
342010
342226
  else
342011
342227
  formattedMagnitude = formattedMagnitude + spec.format.uomSeparator + spec.unitConversions[0].label;
@@ -342025,12 +342241,12 @@ class Formatter {
342025
342241
  }
342026
342242
  static processBearingAndAzimuth(magnitude, spec) {
342027
342243
  const type = spec.format.type;
342028
- if (type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing && type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth)
342244
+ if (type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing && type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth)
342029
342245
  return { magnitude };
342030
342246
  const revolution = this.getRevolution(spec);
342031
342247
  magnitude = this.normalizeAngle(magnitude, revolution);
342032
342248
  const quarterRevolution = revolution / 4;
342033
- if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing) {
342249
+ if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing) {
342034
342250
  let quadrant = 0;
342035
342251
  while (magnitude > quarterRevolution) {
342036
342252
  magnitude -= quarterRevolution;
@@ -342057,7 +342273,7 @@ class Formatter {
342057
342273
  if (quadrant === 2 && spec.unitConversions.length > 0) {
342058
342274
  // To determine if value is small, we need to convert it to the smallest unit presented and use the provided precision on it
342059
342275
  const unitConversion = spec.unitConversions[spec.unitConversions.length - 1].conversion;
342060
- const smallestFormattedDelta = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)((quarterRevolution - magnitude), unitConversion) + this.FPV_MINTHRESHOLD;
342276
+ const smallestFormattedDelta = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)((quarterRevolution - magnitude), unitConversion) + this.FPV_MINTHRESHOLD;
342061
342277
  const precisionScale = Math.pow(10.0, spec.format.precision);
342062
342278
  const floor = Math.floor((smallestFormattedDelta) * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
342063
342279
  if (floor === 0) {
@@ -342066,13 +342282,13 @@ class Formatter {
342066
342282
  }
342067
342283
  return { magnitude, prefix, suffix };
342068
342284
  }
342069
- if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
342285
+ if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
342070
342286
  let azimuthBase = 0; // default base is North
342071
342287
  if (spec.format.azimuthBase !== undefined) {
342072
342288
  if (spec.azimuthBaseConversion === undefined) {
342073
342289
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, `Missing azimuth base conversion for interpreting ${spec.name}'s azimuth base.`);
342074
342290
  }
342075
- const azBaseQuantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.azimuthBaseUnit, spec.format.azimuthBase);
342291
+ const azBaseQuantity = new _Quantity__WEBPACK_IMPORTED_MODULE_4__.Quantity(spec.format.azimuthBaseUnit, spec.format.azimuthBase);
342076
342292
  const azBaseConverted = azBaseQuantity.convertTo(spec.persistenceUnit, spec.azimuthBaseConversion);
342077
342293
  if (azBaseConverted === undefined || !azBaseConverted.isValid) {
342078
342294
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.UnsupportedUnit, `Failed to convert azimuth base unit to ${spec.persistenceUnit.name}.`);
@@ -342102,40 +342318,87 @@ class Formatter {
342102
342318
  if (spec.revolutionConversion === undefined) {
342103
342319
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, `Missing revolution unit conversion for calculating ${spec.name}'s revolution.`);
342104
342320
  }
342105
- const revolution = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.revolutionUnit, 1.0);
342321
+ const revolution = new _Quantity__WEBPACK_IMPORTED_MODULE_4__.Quantity(spec.format.revolutionUnit, 1.0);
342106
342322
  const converted = revolution.convertTo(spec.persistenceUnit, spec.revolutionConversion);
342107
342323
  if (converted === undefined || !converted.isValid) {
342108
342324
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.UnsupportedUnit, `Failed to convert revolution unit to ${spec.persistenceUnit.name}.`);
342109
342325
  }
342110
342326
  return converted.magnitude;
342111
342327
  }
342328
+ static formatRatioPart(value, spec, side) {
342329
+ const formatType = spec.format.ratioFormatType === "Fractional" ? _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional : _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Decimal;
342330
+ const tempFormat = spec.format.clone({ type: formatType });
342331
+ const tempSpec = new _FormatterSpec__WEBPACK_IMPORTED_MODULE_2__.FormatterSpec(spec.name, tempFormat, spec.unitConversions, spec.persistenceUnit);
342332
+ let formattedValue = this.formatMagnitude(value, tempSpec);
342333
+ // For fractional ratio formatting, suppress leading "0" if the value is purely fractional
342334
+ if (formatType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional && formattedValue.startsWith("0 ")) {
342335
+ formattedValue = formattedValue.substring(2); // Remove "0 " prefix
342336
+ }
342337
+ // Add unit label if ShowUnitLabel trait is set
342338
+ // unitConversions[0] = ratio scale factor, [1] = numerator unit, [2] = denominator unit
342339
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel) && spec.unitConversions.length >= 3) {
342340
+ const labelToAdd = side === "numerator" ? spec.unitConversions[1].label : spec.unitConversions[2].label;
342341
+ formattedValue = formattedValue + labelToAdd;
342342
+ }
342343
+ return formattedValue;
342344
+ }
342345
+ /** Format a ratio quantity value (separate from composite formatting) */
342346
+ static formatRatioQuantity(magnitude, spec) {
342347
+ const unitConversion = spec.unitConversions[0].conversion;
342348
+ let unitValue = 0.0;
342349
+ try {
342350
+ unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(magnitude, unitConversion) + this.FPV_MINTHRESHOLD;
342351
+ }
342352
+ catch (e) {
342353
+ // The "InvertingZero" error is thrown when the value is zero and the conversion factor is inverted.
342354
+ // For ratio, we return "1:0" as the formatted value.
342355
+ if (e instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && e.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
342356
+ return { componentText: "1:0", isNegative: false };
342357
+ }
342358
+ throw e;
342359
+ }
342360
+ const componentText = this.formatRatio(unitValue, spec);
342361
+ const isNegative = unitValue < 0;
342362
+ return { componentText, isNegative };
342363
+ }
342112
342364
  static formatRatio(magnitude, spec) {
342113
342365
  if (null === spec.format.ratioType)
342114
342366
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} must have a ratio type specified.`);
342115
342367
  const precisionScale = Math.pow(10.0, spec.format.precision);
342368
+ const separator = spec.format.ratioSeparator;
342116
342369
  let reciprocal = 0;
342370
+ // Helper to get unit labels if ShowUnitLabel is set
342371
+ const getUnitLabels = () => {
342372
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel) && spec.unitConversions.length >= 3) {
342373
+ return { numeratorLabel: spec.unitConversions[1].label, denominatorLabel: spec.unitConversions[2].label };
342374
+ }
342375
+ return { numeratorLabel: "", denominatorLabel: "" };
342376
+ };
342377
+ const { numeratorLabel, denominatorLabel } = getUnitLabels();
342117
342378
  if (magnitude === 0.0)
342118
- return "0:1";
342379
+ return `0${separator}1`;
342119
342380
  else
342120
342381
  reciprocal = 1.0 / magnitude;
342121
342382
  switch (spec.format.ratioType) {
342122
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.OneToN:
342123
- return `1:${this.formatMagnitude(reciprocal, spec)}`;
342124
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.NToOne:
342125
- return `${this.formatMagnitude(magnitude, spec)}:1`;
342126
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.ValueBased:
342127
- if (magnitude > 1.0)
342128
- return `${this.formatMagnitude(magnitude, spec)}:1`;
342129
- else
342130
- return `1:${this.formatMagnitude(reciprocal, spec)}`;
342131
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.UseGreatestCommonDivisor:
342383
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.OneToN:
342384
+ return `1${numeratorLabel}${separator}${this.formatRatioPart(reciprocal, spec, "denominator")}`;
342385
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.NToOne:
342386
+ return `${this.formatRatioPart(magnitude, spec, "numerator")}${separator}1${denominatorLabel}`;
342387
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.ValueBased:
342388
+ if (magnitude > 1.0) {
342389
+ return `${this.formatRatioPart(magnitude, spec, "numerator")}${separator}1${denominatorLabel}`;
342390
+ }
342391
+ else {
342392
+ return `1${numeratorLabel}${separator}${this.formatRatioPart(reciprocal, spec, "denominator")}`;
342393
+ }
342394
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.UseGreatestCommonDivisor:
342132
342395
  magnitude = Math.round(magnitude * precisionScale) / precisionScale;
342133
342396
  let numerator = magnitude * precisionScale;
342134
342397
  let denominator = precisionScale;
342135
342398
  const gcd = FractionalNumeric.getGreatestCommonFactor(numerator, denominator);
342136
342399
  numerator /= gcd;
342137
342400
  denominator /= gcd;
342138
- return `${this.formatMagnitude(numerator, spec)}:${this.formatMagnitude(denominator, spec)}`;
342401
+ return `${this.formatRatioPart(numerator, spec, "numerator")}${separator}${this.formatRatioPart(denominator, spec, "denominator")}`;
342139
342402
  default:
342140
342403
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has an invalid ratio type specified.`);
342141
342404
  }
@@ -342156,7 +342419,8 @@ __webpack_require__.r(__webpack_exports__);
342156
342419
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
342157
342420
  /* harmony export */ FormatterSpec: () => (/* binding */ FormatterSpec)
342158
342421
  /* harmony export */ });
342159
- /* harmony import */ var _Formatter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Formatter */ "../../core/quantity/lib/esm/Formatter/Formatter.js");
342422
+ /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
342423
+ /* harmony import */ var _Formatter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Formatter */ "../../core/quantity/lib/esm/Formatter/Formatter.js");
342160
342424
  /*---------------------------------------------------------------------------------------------
342161
342425
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
342162
342426
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -342165,6 +342429,7 @@ __webpack_require__.r(__webpack_exports__);
342165
342429
  * @module Quantity
342166
342430
  */
342167
342431
 
342432
+
342168
342433
  // cSpell:ignore ZERONORMALIZED, nosign, onlynegative, signalways, negativeparentheses
342169
342434
  // cSpell:ignore trailzeroes, keepsinglezero, zeroempty, keepdecimalpoint, applyrounding, fractiondash, showunitlabel, prependunitlabel, exponentonlynegative
342170
342435
  /** A class that contains both formatting information and the conversion factors necessary to convert from an input unit to the units specified in the format.
@@ -342211,6 +342476,49 @@ class FormatterSpec {
342211
342476
  get persistenceUnit() { return this._persistenceUnit; }
342212
342477
  get azimuthBaseConversion() { return this._azimuthBaseConversion; }
342213
342478
  get revolutionConversion() { return this._revolutionConversion; }
342479
+ /** Build conversion specs for ratio format with 2 composite units (numerator/denominator). */
342480
+ static async getRatioUnitConversions(units, unitsProvider, persistenceUnit) {
342481
+ const conversions = [];
342482
+ const [numeratorUnit, numeratorLabel] = units[0];
342483
+ const [denominatorUnit, denominatorLabel] = units[1];
342484
+ // Compute ratio scale: how many numerator units per denominator unit (e.g., IN:FT = 12)
342485
+ const denominatorToNumerator = await unitsProvider.getConversion(denominatorUnit, numeratorUnit);
342486
+ const displayRatioScale = denominatorToNumerator.factor;
342487
+ // Avoid double-scaling: if persistence unit already encodes the display ratio, use factor 1.
342488
+ // Check by name heuristic (e.g., IN_PER_FT with ratioUnits [IN, FT] → no scaling needed)
342489
+ const persistenceName = persistenceUnit.name.toUpperCase();
342490
+ const numName = numeratorUnit.name.toUpperCase().split(".").pop() ?? "";
342491
+ const denName = denominatorUnit.name.toUpperCase().split(".").pop() ?? "";
342492
+ // Split by word boundaries (underscores, dots) and check for exact token matches
342493
+ const persistenceTokens = persistenceName.split(/[._]/);
342494
+ const isPersistenceMatchingRatio = persistenceTokens.includes(numName) && persistenceTokens.includes(denName);
342495
+ const ratioScaleFactor = isPersistenceMatchingRatio ? 1.0 : displayRatioScale;
342496
+ // First conversion spec: effective ratio unit conversion
342497
+ const ratioConversionSpec = {
342498
+ name: `${numeratorUnit.name}_per_${denominatorUnit.name}`,
342499
+ label: "",
342500
+ system: numeratorUnit.system,
342501
+ conversion: { factor: ratioScaleFactor, offset: 0.0 },
342502
+ };
342503
+ conversions.push(ratioConversionSpec);
342504
+ // Numerator unit for label lookup
342505
+ const numeratorSpec = {
342506
+ name: numeratorUnit.name,
342507
+ label: numeratorLabel?.length ? numeratorLabel : numeratorUnit.label,
342508
+ system: numeratorUnit.system,
342509
+ conversion: { factor: 1.0, offset: 0.0 },
342510
+ };
342511
+ conversions.push(numeratorSpec);
342512
+ // Denominator unit for label lookup
342513
+ const denominatorSpec = {
342514
+ name: denominatorUnit.name,
342515
+ label: denominatorLabel?.length ? denominatorLabel : denominatorUnit.label,
342516
+ system: denominatorUnit.system,
342517
+ conversion: { factor: 1.0, offset: 0.0 },
342518
+ };
342519
+ conversions.push(denominatorSpec);
342520
+ return conversions;
342521
+ }
342214
342522
  /** Get an array of UnitConversionSpecs, one for each unit that is to be shown in the formatted quantity string. */
342215
342523
  static async getUnitConversions(format, unitsProvider, inputUnit) {
342216
342524
  const conversions = [];
@@ -342224,6 +342532,10 @@ class FormatterSpec {
342224
342532
  throw new Error("Formatter Spec needs persistence unit to be specified");
342225
342533
  }
342226
342534
  }
342535
+ // Handle 2-unit composite for ratio formats (scale factors)
342536
+ if (format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_0__.FormatType.Ratio && format.units && format.units.length === 2) {
342537
+ return FormatterSpec.getRatioUnitConversions(format.units, unitsProvider, persistenceUnit);
342538
+ }
342227
342539
  if (format.units) {
342228
342540
  let convertFromUnit = inputUnit;
342229
342541
  for (const unit of format.units) {
@@ -342280,7 +342592,7 @@ class FormatterSpec {
342280
342592
  }
342281
342593
  /** Format a quantity value. */
342282
342594
  applyFormatting(magnitude) {
342283
- return _Formatter__WEBPACK_IMPORTED_MODULE_0__.Formatter.formatQuantity(magnitude, this);
342595
+ return _Formatter__WEBPACK_IMPORTED_MODULE_1__.Formatter.formatQuantity(magnitude, this);
342284
342596
  }
342285
342597
  }
342286
342598
 
@@ -342388,13 +342700,14 @@ var ParseError;
342388
342700
  ParseError[ParseError["BearingPrefixOrSuffixMissing"] = 7] = "BearingPrefixOrSuffixMissing";
342389
342701
  ParseError[ParseError["MathematicOperationFoundButIsNotAllowed"] = 8] = "MathematicOperationFoundButIsNotAllowed";
342390
342702
  ParseError[ParseError["BearingAngleOutOfRange"] = 9] = "BearingAngleOutOfRange";
342703
+ ParseError[ParseError["InvalidMathResult"] = 10] = "InvalidMathResult";
342391
342704
  })(ParseError || (ParseError = {}));
342392
342705
  var Operator;
342393
342706
  (function (Operator) {
342394
342707
  Operator["addition"] = "+";
342395
342708
  Operator["subtraction"] = "-";
342396
342709
  Operator["multiplication"] = "*";
342397
- Operator["division"] = "/"; // unsupported but we recognize it during parsing
342710
+ Operator["division"] = "/";
342398
342711
  })(Operator || (Operator = {}));
342399
342712
  function isOperator(char) {
342400
342713
  if (typeof char === "number") {
@@ -343149,36 +343462,107 @@ class Parser {
343149
343462
  magnitude = this.normalizeAngle(magnitude, revolution);
343150
343463
  return { ok: true, value: magnitude };
343151
343464
  }
343465
+ /**
343466
+ * Parse a ratio part string (numerator or denominator) to extract the numeric value and optional unit label.
343467
+ * This method processes tokens without applying unit conversions, allowing the ratio format
343468
+ * handler to manage conversions at the ratio level.
343469
+ *
343470
+ *
343471
+ * @note Fractions are already handled by parseQuantitySpecification, which converts them to
343472
+ * single numeric tokens (e.g., "1/2" becomes 0.5).
343473
+ *
343474
+ * @param partStr The string to parse, which may contain a number, fraction, or mixed fraction with optional unit label.
343475
+ * @param format The format specification used for token parsing.
343476
+ * @returns An object containing the parsed numeric value and optional unit label. Returns NaN for value if no number is found.
343477
+ */
343478
+ static parseRatioPart(partStr, format) {
343479
+ partStr = partStr.trim();
343480
+ // Parse tokens - fractions are automatically converted to decimal values by parseQuantitySpecification
343481
+ const tempFormat = format.clone({ type: _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Decimal });
343482
+ const tokens = Parser.parseQuantitySpecification(partStr, tempFormat);
343483
+ let value = NaN;
343484
+ let unitLabel;
343485
+ // Pre-process: merge negative operators with following numbers
343486
+ const processedTokens = [];
343487
+ for (let i = 0; i < tokens.length; i++) {
343488
+ const token = tokens[i];
343489
+ if (token.isOperator && i === 0 && token.value === "-" &&
343490
+ i + 1 < tokens.length && tokens[i + 1].isNumber) {
343491
+ // Merge negative sign with number
343492
+ processedTokens.push(new ParseToken(-tokens[i + 1].value));
343493
+ i++; // Skip the number token since we consumed it
343494
+ }
343495
+ else {
343496
+ processedTokens.push(token);
343497
+ }
343498
+ }
343499
+ // Extract numeric value and unit label from processed tokens
343500
+ for (const token of processedTokens) {
343501
+ if (token.isNumber && isNaN(value)) {
343502
+ value = token.value;
343503
+ }
343504
+ else if (token.isString && !token.isOperator) {
343505
+ // String token that's not an operator - treat as unit label
343506
+ unitLabel = token.value;
343507
+ }
343508
+ }
343509
+ return { value, unitLabel };
343510
+ }
343152
343511
  static parseRatioFormat(inString, spec) {
343153
343512
  if (!inString)
343154
343513
  return { ok: false, error: ParseError.NoValueOrUnitFoundInString };
343155
- const parts = inString.split(":");
343514
+ const separator = spec.format.ratioSeparator ?? ":";
343515
+ const parts = inString.split(separator);
343156
343516
  if (parts.length > 2)
343157
343517
  return { ok: false, error: ParseError.UnableToConvertParseTokensToQuantity };
343158
- const numerator = parseFloat(parts[0]);
343159
- let denominator;
343160
- if (parts.length === 1) {
343161
- denominator = 1.0;
343162
- }
343163
- else {
343164
- denominator = parseFloat(parts[1]);
343518
+ // If the string doesn't contain the expected separator but contains other ratio-like separators,
343519
+ // return an error since the wrong separator was used
343520
+ if (parts.length === 1 && !inString.includes(separator)) {
343521
+ // Check if the string contains other common ratio separators
343522
+ const otherSeparators = [":", "=", "/"];
343523
+ for (const otherSep of otherSeparators) {
343524
+ if (otherSep !== separator && inString.includes(otherSep)) {
343525
+ // The string looks like a ratio but uses the wrong separator
343526
+ return { ok: false, error: ParseError.UnableToConvertParseTokensToQuantity };
343527
+ }
343528
+ }
343529
+ // Parse as a regular quantity value (numerator only, denominator = 1)
343530
+ const result = this.parseAndProcessTokens(inString, spec.format, spec.unitConversions);
343531
+ return result;
343165
343532
  }
343166
- if (isNaN(numerator) || isNaN(denominator))
343533
+ // Parse numerator and denominator parts which may include unit labels
343534
+ const numeratorPart = this.parseRatioPart(parts[0], spec.format);
343535
+ const denominatorPart = parts.length === 1 ? { value: 1.0 } : this.parseRatioPart(parts[1], spec.format);
343536
+ if (isNaN(numeratorPart.value) || isNaN(denominatorPart.value))
343167
343537
  return { ok: false, error: ParseError.NoValueOrUnitFoundInString };
343538
+ // Handle 2-unit composite case - simpler conversion using the pre-computed scale factor
343539
+ if (spec.format.units && spec.format.units.length === 2 && spec.unitConversions.length >= 3) {
343540
+ const ratioConvSpec = spec.unitConversions[0];
343541
+ const scaleFactor = ratioConvSpec.conversion.factor;
343542
+ if (denominatorPart.value === 0) {
343543
+ return { ok: false, error: ParseError.InvalidMathResult };
343544
+ }
343545
+ // The ratio value is numerator/denominator in the display units (e.g., 12 for 12"=1')
343546
+ // Divide by scale factor to get persistence unit value (e.g., 12/12 = 1.0)
343547
+ const ratioValue = numeratorPart.value / denominatorPart.value;
343548
+ const convertedValue = ratioValue / scaleFactor;
343549
+ return { ok: true, value: convertedValue };
343550
+ }
343551
+ // Original flow for 1-unit composite - use Quantity.convertTo for proper unit conversion
343168
343552
  const defaultUnit = spec.format.units && spec.format.units.length > 0 ? spec.format.units[0][0] : undefined;
343169
343553
  const unitConversion = defaultUnit ? Parser.tryFindUnitConversion(defaultUnit.label, spec.unitConversions, defaultUnit) : undefined;
343170
343554
  if (!unitConversion) {
343171
343555
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, `Missing input unit or unit conversion for interpreting ${spec.format.name}.`);
343172
343556
  }
343173
- if (denominator === 0) {
343174
- if (unitConversion.inversion && numerator === 1)
343557
+ if (denominatorPart.value === 0) {
343558
+ if (unitConversion.inversion && numeratorPart.value === 1)
343175
343559
  return { ok: true, value: 0.0 };
343176
343560
  else
343177
- return { ok: false, error: ParseError.MathematicOperationFoundButIsNotAllowed };
343561
+ return { ok: false, error: ParseError.InvalidMathResult };
343178
343562
  }
343179
343563
  let quantity;
343180
343564
  if (spec.format.units && spec.outUnit) {
343181
- quantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.units[0][0], numerator / denominator);
343565
+ quantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.units[0][0], numeratorPart.value / denominatorPart.value);
343182
343566
  }
343183
343567
  else {
343184
343568
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, "Missing presentation unit or persistence unit for ratio format.");
@@ -343190,7 +343574,7 @@ class Parser {
343190
343574
  catch (err) {
343191
343575
  // for input of "0:N" with reversed unit
343192
343576
  if (err instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && err.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
343193
- return { ok: false, error: ParseError.MathematicOperationFoundButIsNotAllowed };
343577
+ return { ok: false, error: ParseError.InvalidMathResult };
343194
343578
  }
343195
343579
  }
343196
343580
  if (converted === undefined || !converted.isValid) {
@@ -343316,7 +343700,8 @@ __webpack_require__.r(__webpack_exports__);
343316
343700
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
343317
343701
  /* harmony export */ ParserSpec: () => (/* binding */ ParserSpec)
343318
343702
  /* harmony export */ });
343319
- /* harmony import */ var _Parser__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Parser */ "../../core/quantity/lib/esm/Parser.js");
343703
+ /* harmony import */ var _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Formatter/FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
343704
+ /* harmony import */ var _Parser__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Parser */ "../../core/quantity/lib/esm/Parser.js");
343320
343705
  /*---------------------------------------------------------------------------------------------
343321
343706
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
343322
343707
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -343325,6 +343710,7 @@ __webpack_require__.r(__webpack_exports__);
343325
343710
  * @module Quantity
343326
343711
  */
343327
343712
 
343713
+
343328
343714
  /** A ParserSpec holds information needed to parse a string into a quantity synchronously.
343329
343715
  * @beta
343330
343716
  */
@@ -343350,6 +343736,51 @@ class ParserSpec {
343350
343736
  get outUnit() { return this._outUnit; }
343351
343737
  get azimuthBaseConversion() { return this._azimuthBaseConversion; }
343352
343738
  get revolutionConversion() { return this._revolutionConversion; }
343739
+ /** Build conversion specs for ratio format with 2 composite units (numerator/denominator). */
343740
+ static async getRatioUnitConversions(units, unitsProvider, outUnit, altUnitLabelsProvider) {
343741
+ const conversions = [];
343742
+ const [numeratorUnit, numeratorLabel] = units[0];
343743
+ const [denominatorUnit, denominatorLabel] = units[1];
343744
+ // Compute ratio scale: how many numerator units per denominator unit (e.g., IN:FT = 12)
343745
+ const denominatorToNumerator = await unitsProvider.getConversion(denominatorUnit, numeratorUnit);
343746
+ const displayRatioScale = denominatorToNumerator.factor;
343747
+ // Avoid double-scaling: if persistence unit already encodes the display ratio, use factor 1.
343748
+ // Check by name heuristic (e.g., IN_PER_FT with ratioUnits [IN, FT] → no scaling needed)
343749
+ const persistenceName = outUnit.name.toUpperCase();
343750
+ const numName = numeratorUnit.name.toUpperCase().split(".").pop() ?? "";
343751
+ const denName = denominatorUnit.name.toUpperCase().split(".").pop() ?? "";
343752
+ // Split by word boundaries (underscores, dots) and check for exact token matches
343753
+ const persistenceTokens = persistenceName.split(/[._]/);
343754
+ const isPersistenceMatchingRatio = persistenceTokens.includes(numName) && persistenceTokens.includes(denName);
343755
+ const ratioScaleFactor = isPersistenceMatchingRatio ? 1.0 : displayRatioScale;
343756
+ // First conversion spec: effective ratio unit conversion
343757
+ const ratioConversionSpec = {
343758
+ name: `${numeratorUnit.name}_per_${denominatorUnit.name}`,
343759
+ label: "",
343760
+ system: numeratorUnit.system,
343761
+ conversion: { factor: ratioScaleFactor, offset: 0.0 },
343762
+ };
343763
+ conversions.push(ratioConversionSpec);
343764
+ // Numerator unit for label lookup
343765
+ const numeratorSpec = {
343766
+ name: numeratorUnit.name,
343767
+ label: numeratorLabel?.length ? numeratorLabel : numeratorUnit.label,
343768
+ system: numeratorUnit.system,
343769
+ conversion: { factor: 1.0, offset: 0.0 },
343770
+ parseLabels: altUnitLabelsProvider?.getAlternateUnitLabels(numeratorUnit),
343771
+ };
343772
+ conversions.push(numeratorSpec);
343773
+ // Denominator unit for label lookup
343774
+ const denominatorSpec = {
343775
+ name: denominatorUnit.name,
343776
+ label: denominatorLabel?.length ? denominatorLabel : denominatorUnit.label,
343777
+ system: denominatorUnit.system,
343778
+ conversion: { factor: 1.0, offset: 0.0 },
343779
+ parseLabels: altUnitLabelsProvider?.getAlternateUnitLabels(denominatorUnit),
343780
+ };
343781
+ conversions.push(denominatorSpec);
343782
+ return conversions;
343783
+ }
343353
343784
  /** Static async method to create a ParserSpec given the format and unit of the quantity that will be passed to the Parser. The input unit will
343354
343785
  * be used to generate conversion information for each unit specified in the Format. This method is async due to the fact that the units provider must make
343355
343786
  * async calls to lookup unit definitions.
@@ -343358,7 +343789,14 @@ class ParserSpec {
343358
343789
  * @param outUnit The unit a value will be formatted to. This unit is often referred to as persistence unit.
343359
343790
  */
343360
343791
  static async create(format, unitsProvider, outUnit, altUnitLabelsProvider) {
343361
- const conversions = await _Parser__WEBPACK_IMPORTED_MODULE_0__.Parser.createUnitConversionSpecsForUnit(unitsProvider, outUnit, altUnitLabelsProvider);
343792
+ let conversions;
343793
+ // For ratio formats with 2 composite units, use private helper method
343794
+ if (format.type === _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_0__.FormatType.Ratio && format.units && format.units.length === 2) {
343795
+ conversions = await ParserSpec.getRatioUnitConversions(format.units, unitsProvider, outUnit, altUnitLabelsProvider);
343796
+ }
343797
+ else {
343798
+ conversions = await _Parser__WEBPACK_IMPORTED_MODULE_1__.Parser.createUnitConversionSpecsForUnit(unitsProvider, outUnit, altUnitLabelsProvider);
343799
+ }
343362
343800
  const spec = new ParserSpec(outUnit, format, conversions);
343363
343801
  if (format.azimuthBaseUnit !== undefined) {
343364
343802
  if (outUnit !== undefined) {
@@ -343380,7 +343818,7 @@ class ParserSpec {
343380
343818
  }
343381
343819
  /** Do the parsing. Done this way to allow Custom Parser Specs to parse custom formatted strings into their quantities. */
343382
343820
  parseToQuantityValue(inString) {
343383
- return _Parser__WEBPACK_IMPORTED_MODULE_0__.Parser.parseQuantityString(inString, this);
343821
+ return _Parser__WEBPACK_IMPORTED_MODULE_1__.Parser.parseQuantityString(inString, this);
343384
343822
  }
343385
343823
  }
343386
343824
 
@@ -343578,6 +344016,7 @@ __webpack_require__.r(__webpack_exports__);
343578
344016
  /* harmony export */ QuantityConstants: () => (/* reexport safe */ _Constants__WEBPACK_IMPORTED_MODULE_0__.QuantityConstants),
343579
344017
  /* harmony export */ QuantityError: () => (/* reexport safe */ _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError),
343580
344018
  /* harmony export */ QuantityStatus: () => (/* reexport safe */ _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus),
344019
+ /* harmony export */ RatioFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.RatioFormatType),
343581
344020
  /* harmony export */ RatioType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.RatioType),
343582
344021
  /* harmony export */ ScientificType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.ScientificType),
343583
344022
  /* harmony export */ ShowSignOption: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.ShowSignOption),
@@ -343596,6 +344035,7 @@ __webpack_require__.r(__webpack_exports__);
343596
344035
  /* harmony export */ parseFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseFormatType),
343597
344036
  /* harmony export */ parseFractionalPrecision: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseFractionalPrecision),
343598
344037
  /* harmony export */ parsePrecision: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parsePrecision),
344038
+ /* harmony export */ parseRatioFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseRatioFormatType),
343599
344039
  /* harmony export */ parseRatioType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseRatioType),
343600
344040
  /* harmony export */ parseScientificType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseScientificType),
343601
344041
  /* harmony export */ parseShowSignOption: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseShowSignOption),
@@ -344555,7 +344995,7 @@ class TestContext {
344555
344995
  this.initializeRpcInterfaces({ title: this.settings.Backend.name, version: this.settings.Backend.version });
344556
344996
  const iModelClient = new imodels_client_management_1.IModelsClient({ api: { baseUrl: `https://${process.env.IMJS_URL_PREFIX ?? ""}api.bentley.com/imodels` } });
344557
344997
  await core_frontend_1.NoRenderApp.startup({
344558
- applicationVersion: "5.6.0-dev.13",
344998
+ applicationVersion: "5.6.0-dev.14",
344559
344999
  applicationId: this.settings.gprid,
344560
345000
  authorizationClient: new frontend_1.TestFrontendAuthorizationClient(this.serviceAuthToken),
344561
345001
  hubAccess: new imodels_access_frontend_1.FrontendIModelsAccess(iModelClient),
@@ -371248,7 +371688,7 @@ var loadLanguages = instance.loadLanguages;
371248
371688
  /***/ ((module) => {
371249
371689
 
371250
371690
  "use strict";
371251
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@itwin/core-frontend","version":"5.6.0-dev.13","description":"iTwin.js frontend components","main":"lib/cjs/core-frontend.js","module":"lib/esm/core-frontend.js","typings":"lib/cjs/core-frontend","license":"MIT","scripts":{"build":"npm run -s copy:public && npm run -s build:cjs && npm run -s build:esm && npm run -s webpackWorkers && npm run -s copy:workers && npm run -s copy:draco","build:cjs":"npm run -s copy:js:cjs && tsc 1>&2 --outDir lib/cjs","build:esm":"npm run -s copy:js:esm && tsc 1>&2 --module ES2022 --outDir lib/esm","clean":"rimraf -g lib .rush/temp/package-deps*.json","copy:public":"cpx \\"./src/public/**/*\\" ./lib/public","copy:js:cjs":"cpx \\"./src/**/*.js\\" ./lib/cjs","copy:js:esm":"cpx \\"./src/**/*.js\\" ./lib/esm","copy:workers":"cpx \\"./lib/workers/webpack/parse-imdl-worker.js\\" ./lib/public/scripts","copy:draco":"cpx \\"./node_modules/@loaders.gl/draco/dist/libs/*\\" ./lib/public/scripts","docs":"betools docs --json=../../generated-docs/core/core-frontend/file.json --tsIndexFile=./core-frontend.ts --onlyJson --excludes=webgl/**/*,**/map/*.d.ts,**/tile/*.d.ts,**/*-css.ts","extract-api":"betools extract-api --entry=core-frontend && npm run extract-extension-api","extract-extension-api":"eslint --no-inline-config -c extraction.eslint.config.js \\"./src/**/*.ts\\" 1>&2","lint":"eslint \\"./src/**/*.ts\\" 1>&2","lint-fix":"eslint --fix -f visualstudio \\"./src/**/*.ts\\" 1>&2","lint-deprecation":"eslint --fix -f visualstudio --no-inline-config -c ../../common/config/eslint/eslint.config.deprecation-policy.js \\"./src/**/*.ts\\"","pseudolocalize":"betools pseudolocalize --englishDir ./src/public/locales/en --out ./public/locales/en-PSEUDO","test":"npm run webpackTestWorker && vitest --run","cover":"npm run webpackTestWorker && vitest --run","webpackTests":"webpack --config ./src/test/utils/webpack.config.js 1>&2 && npm run -s webpackTestWorker","webpackTestWorker":"webpack --config ./src/test/worker/webpack.config.js 1>&2 && cpx \\"./lib/test/test-worker.js\\" ./lib/test","webpackWorkers":"webpack --config ./src/workers/ImdlParser/webpack.config.js 1>&2"},"repository":{"type":"git","url":"https://github.com/iTwin/itwinjs-core.git","directory":"core/frontend"},"keywords":["Bentley","BIM","iModel","digital-twin","iTwin"],"author":{"name":"Bentley Systems, Inc.","url":"http://www.bentley.com"},"peerDependencies":{"@itwin/appui-abstract":"workspace:*","@itwin/core-bentley":"workspace:*","@itwin/core-common":"workspace:*","@itwin/core-geometry":"workspace:*","@itwin/core-orbitgt":"workspace:*","@itwin/core-quantity":"workspace:*","@itwin/ecschema-metadata":"workspace:*","@itwin/ecschema-rpcinterface-common":"workspace:*"},"//devDependencies":["NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install","NOTE: All tools used by scripts in this package must be listed as devDependencies"],"devDependencies":{"@itwin/appui-abstract":"workspace:*","@itwin/build-tools":"workspace:*","@itwin/core-bentley":"workspace:*","@itwin/core-common":"workspace:*","@itwin/core-geometry":"workspace:*","@itwin/core-orbitgt":"workspace:*","@itwin/core-quantity":"workspace:*","@itwin/ecschema-metadata":"workspace:*","@itwin/ecschema-rpcinterface-common":"workspace:*","@itwin/object-storage-core":"^3.0.4","@itwin/eslint-plugin":"5.2.2-dev.2","@types/chai-as-promised":"^7","@types/draco3d":"^1.4.10","@types/sinon":"^17.0.2","@vitest/browser":"^3.0.6","@vitest/coverage-v8":"^3.0.6","cpx2":"^8.0.0","eslint":"^9.31.0","glob":"^10.5.0","playwright":"~1.56.1","rimraf":"^6.0.1","sinon":"^17.0.2","source-map-loader":"^5.0.0","typescript":"~5.6.2","typemoq":"^2.1.0","vitest":"^3.0.6","vite-multiple-assets":"^1.3.1","vite-plugin-static-copy":"2.2.0","webpack":"^5.97.1"},"//dependencies":["NOTE: these dependencies should be only for things that DO NOT APPEAR IN THE API","NOTE: core-frontend should remain UI technology agnostic, so no react/angular dependencies are allowed"],"dependencies":{"@itwin/core-i18n":"workspace:*","@itwin/webgl-compatibility":"workspace:*","@loaders.gl/core":"^4.3.4","@loaders.gl/draco":"^4.3.4","fuse.js":"^3.3.0","wms-capabilities":"0.4.0"}}');
371691
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@itwin/core-frontend","version":"5.6.0-dev.14","description":"iTwin.js frontend components","main":"lib/cjs/core-frontend.js","module":"lib/esm/core-frontend.js","typings":"lib/cjs/core-frontend","license":"MIT","scripts":{"build":"npm run -s copy:public && npm run -s build:cjs && npm run -s build:esm && npm run -s webpackWorkers && npm run -s copy:workers && npm run -s copy:draco","build:cjs":"npm run -s copy:js:cjs && tsc 1>&2 --outDir lib/cjs","build:esm":"npm run -s copy:js:esm && tsc 1>&2 --module ES2022 --outDir lib/esm","clean":"rimraf -g lib .rush/temp/package-deps*.json","copy:public":"cpx \\"./src/public/**/*\\" ./lib/public","copy:js:cjs":"cpx \\"./src/**/*.js\\" ./lib/cjs","copy:js:esm":"cpx \\"./src/**/*.js\\" ./lib/esm","copy:workers":"cpx \\"./lib/workers/webpack/parse-imdl-worker.js\\" ./lib/public/scripts","copy:draco":"cpx \\"./node_modules/@loaders.gl/draco/dist/libs/*\\" ./lib/public/scripts","docs":"betools docs --json=../../generated-docs/core/core-frontend/file.json --tsIndexFile=./core-frontend.ts --onlyJson --excludes=webgl/**/*,**/map/*.d.ts,**/tile/*.d.ts,**/*-css.ts","extract-api":"betools extract-api --entry=core-frontend && npm run extract-extension-api","extract-extension-api":"eslint --no-inline-config -c extraction.eslint.config.js \\"./src/**/*.ts\\" 1>&2","lint":"eslint \\"./src/**/*.ts\\" 1>&2","lint-fix":"eslint --fix -f visualstudio \\"./src/**/*.ts\\" 1>&2","lint-deprecation":"eslint --fix -f visualstudio --no-inline-config -c ../../common/config/eslint/eslint.config.deprecation-policy.js \\"./src/**/*.ts\\"","pseudolocalize":"betools pseudolocalize --englishDir ./src/public/locales/en --out ./public/locales/en-PSEUDO","test":"npm run webpackTestWorker && vitest --run","cover":"npm run webpackTestWorker && vitest --run","webpackTests":"webpack --config ./src/test/utils/webpack.config.js 1>&2 && npm run -s webpackTestWorker","webpackTestWorker":"webpack --config ./src/test/worker/webpack.config.js 1>&2 && cpx \\"./lib/test/test-worker.js\\" ./lib/test","webpackWorkers":"webpack --config ./src/workers/ImdlParser/webpack.config.js 1>&2"},"repository":{"type":"git","url":"https://github.com/iTwin/itwinjs-core.git","directory":"core/frontend"},"keywords":["Bentley","BIM","iModel","digital-twin","iTwin"],"author":{"name":"Bentley Systems, Inc.","url":"http://www.bentley.com"},"peerDependencies":{"@itwin/appui-abstract":"workspace:*","@itwin/core-bentley":"workspace:*","@itwin/core-common":"workspace:*","@itwin/core-geometry":"workspace:*","@itwin/core-orbitgt":"workspace:*","@itwin/core-quantity":"workspace:*","@itwin/ecschema-metadata":"workspace:*","@itwin/ecschema-rpcinterface-common":"workspace:*"},"//devDependencies":["NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install","NOTE: All tools used by scripts in this package must be listed as devDependencies"],"devDependencies":{"@itwin/appui-abstract":"workspace:*","@itwin/build-tools":"workspace:*","@itwin/core-bentley":"workspace:*","@itwin/core-common":"workspace:*","@itwin/core-geometry":"workspace:*","@itwin/core-orbitgt":"workspace:*","@itwin/core-quantity":"workspace:*","@itwin/ecschema-metadata":"workspace:*","@itwin/ecschema-rpcinterface-common":"workspace:*","@itwin/object-storage-core":"^3.0.4","@itwin/eslint-plugin":"^6.0.0","@types/chai-as-promised":"^7","@types/draco3d":"^1.4.10","@types/sinon":"^17.0.2","@vitest/browser":"^3.0.6","@vitest/coverage-v8":"^3.0.6","cpx2":"^8.0.0","eslint":"^9.31.0","glob":"^10.5.0","playwright":"~1.56.1","rimraf":"^6.0.1","sinon":"^17.0.2","source-map-loader":"^5.0.0","typescript":"~5.6.2","typemoq":"^2.1.0","vitest":"^3.0.6","vite-multiple-assets":"^1.3.1","vite-plugin-static-copy":"2.2.0","webpack":"^5.97.1"},"//dependencies":["NOTE: these dependencies should be only for things that DO NOT APPEAR IN THE API","NOTE: core-frontend should remain UI technology agnostic, so no react/angular dependencies are allowed"],"dependencies":{"@itwin/core-i18n":"workspace:*","@itwin/webgl-compatibility":"workspace:*","@loaders.gl/core":"^4.3.4","@loaders.gl/draco":"^4.3.4","fuse.js":"^3.3.0","wms-capabilities":"0.4.0"}}');
371252
371692
 
371253
371693
  /***/ }),
371254
371694