@itwin/ecschema-rpcinterface-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.
@@ -63738,6 +63738,18 @@ class JsonParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_2__.AbstractPa
63738
63738
  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'.`);
63739
63739
  if (undefined !== jsonObj.scientificType && typeof (jsonObj.scientificType) !== "string")
63740
63740
  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'.`);
63741
+ if (undefined !== jsonObj.ratioType && typeof (jsonObj.ratioType) !== "string")
63742
+ 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'.`);
63743
+ if (undefined !== jsonObj.ratioSeparator && typeof (jsonObj.ratioSeparator) !== "string")
63744
+ 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'.`);
63745
+ if (undefined !== jsonObj.ratioFormatType && typeof (jsonObj.ratioFormatType) !== "string")
63746
+ 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'.`);
63747
+ // Validate EC version if ratio properties exist - they require EC version 3.3+
63748
+ if (jsonObj.ratioType !== undefined || jsonObj.ratioSeparator !== undefined || jsonObj.ratioFormatType !== undefined) {
63749
+ if (this._ecSpecVersion === undefined || this._ecSpecVersion.readVersion < 3 || (this._ecSpecVersion.readVersion === 3 && this._ecSpecVersion.writeVersion < 3)) {
63750
+ 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.`);
63751
+ }
63752
+ }
63741
63753
  if (undefined !== jsonObj.stationOffsetSize && typeof (jsonObj.stationOffsetSize) !== "number")
63742
63754
  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'.`);
63743
63755
  if (undefined !== jsonObj.stationSeparator && typeof (jsonObj.stationSeparator) !== "string")
@@ -64451,6 +64463,15 @@ class XmlParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_5__.AbstractPar
64451
64463
  const thousandSeparator = this.getOptionalAttribute(xmlElement, "thousandSeparator");
64452
64464
  const uomSeparator = this.getOptionalAttribute(xmlElement, "uomSeparator");
64453
64465
  const scientificType = this.getOptionalAttribute(xmlElement, "scientificType");
64466
+ const ratioType = this.getOptionalAttribute(xmlElement, "ratioType");
64467
+ const ratioSeparator = this.getOptionalAttribute(xmlElement, "ratioSeparator");
64468
+ const ratioFormatType = this.getOptionalAttribute(xmlElement, "ratioFormatType");
64469
+ // Validate EC version if ratio properties exist - they require EC version 3.3+
64470
+ if (ratioType !== undefined || ratioSeparator !== undefined || ratioFormatType !== undefined) {
64471
+ if (this._ecSpecVersion === undefined || this._ecSpecVersion.readVersion < 3 || (this._ecSpecVersion.readVersion === 3 && this._ecSpecVersion.writeVersion < 3)) {
64472
+ 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.`);
64473
+ }
64474
+ }
64454
64475
  const stationOffsetSize = this.getOptionalIntAttribute(xmlElement, "stationOffsetSize", `The Format ${this._currentItemFullName} has an invalid 'stationOffsetSize' attribute. It should be a numeric value.`);
64455
64476
  const stationSeparator = this.getOptionalAttribute(xmlElement, "stationSeparator");
64456
64477
  let composite;
@@ -64493,6 +64514,9 @@ class XmlParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_5__.AbstractPar
64493
64514
  thousandSeparator,
64494
64515
  uomSeparator,
64495
64516
  scientificType,
64517
+ ratioType,
64518
+ ratioSeparator,
64519
+ ratioFormatType,
64496
64520
  stationOffsetSize,
64497
64521
  stationSeparator,
64498
64522
  composite,
@@ -66236,7 +66260,7 @@ class SchemaFormatsProvider {
66236
66260
  // If no matching presentation format was found, use persistence unit format if it matches unit system.
66237
66261
  const persistenceUnit = await kindOfQuantity.persistenceUnit;
66238
66262
  const persistenceUnitSystem = await persistenceUnit?.unitSystem;
66239
- if (persistenceUnitSystem && unitSystemMatchers.some((matcher) => matcher(persistenceUnitSystem))) {
66263
+ if (persistenceUnit && persistenceUnitSystem && unitSystemMatchers.some((matcher) => matcher(persistenceUnitSystem))) {
66240
66264
  this._formatsRetrieved.add(itemKey.fullName);
66241
66265
  const props = getPersistenceUnitFormatProps(persistenceUnit);
66242
66266
  return this.convertToFormatDefinition(props, kindOfQuantity);
@@ -70467,6 +70491,9 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70467
70491
  get stationSeparator() { return this._base.stationSeparator; }
70468
70492
  get stationOffsetSize() { return this._base.stationOffsetSize; }
70469
70493
  get stationBaseFactor() { return this._base.stationBaseFactor; }
70494
+ get ratioType() { return this._base.ratioType; }
70495
+ get ratioSeparator() { return this._base.ratioSeparator; }
70496
+ get ratioFormatType() { return this._base.ratioFormatType; }
70470
70497
  get formatTraits() { return this._base.formatTraits; }
70471
70498
  get spacer() { return this._base.spacer; }
70472
70499
  get includeZero() { return this._base.includeZero; }
@@ -70488,10 +70515,13 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70488
70515
  addUnit(unit, label) {
70489
70516
  if (undefined === this._units)
70490
70517
  this._units = [];
70491
- else { // Validate that a duplicate is not added.
70492
- for (const existingUnit of this._units) {
70493
- if (unit.fullName.toLowerCase() === existingUnit[0].fullName.toLowerCase())
70494
- 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.
70518
+ else {
70519
+ const isDuplicateAllowed = this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio;
70520
+ if (!isDuplicateAllowed) {
70521
+ for (const existingUnit of this._units) {
70522
+ if (unit.fullName.toLowerCase() === existingUnit[0].fullName.toLowerCase())
70523
+ 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.
70524
+ }
70495
70525
  }
70496
70526
  }
70497
70527
  this._units.push([unit, label]);
@@ -70516,37 +70546,87 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70516
70546
  if (formatProps.composite.units.length <= 0 || formatProps.composite.units.length > 4)
70517
70547
  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.`);
70518
70548
  }
70549
+ // For Ratio formats: validate that composite is provided
70550
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio) {
70551
+ const hasComposite = undefined !== formatProps.composite && formatProps.composite.units.length > 0;
70552
+ if (!hasComposite) {
70553
+ 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.`);
70554
+ }
70555
+ }
70519
70556
  }
70520
70557
  fromJSONSync(formatProps) {
70521
70558
  super.fromJSONSync(formatProps);
70522
70559
  this.typecheck(formatProps);
70523
- if (undefined === formatProps.composite)
70524
- return;
70525
- // Units are separated from the rest of the deserialization because of the need to have separate sync and async implementation
70526
- for (const unit of formatProps.composite.units) {
70527
- const newUnit = this.schema.lookupItemSync(unit.name);
70528
- if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
70529
- throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
70530
- if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit))
70531
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
70532
- else if (_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit))
70533
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
70560
+ // Process composite units
70561
+ if (undefined !== formatProps.composite) {
70562
+ for (const unit of formatProps.composite.units) {
70563
+ const newUnit = this.schema.lookupItemSync(unit.name);
70564
+ if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
70565
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
70566
+ const lazyUnit = _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit)
70567
+ ? new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit)
70568
+ : new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit);
70569
+ this.addUnit(lazyUnit, unit.label);
70570
+ }
70571
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
70572
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && this._units && this._units.length === 2) {
70573
+ const unit1Item = this.schema.lookupItemSync(this._units[0][0].fullName);
70574
+ const unit2Item = this.schema.lookupItemSync(this._units[1][0].fullName);
70575
+ 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)))
70576
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has invalid units.`);
70577
+ const getPhenomenon = (unitItem) => {
70578
+ if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unitItem)) {
70579
+ return unitItem.phenomenon;
70580
+ }
70581
+ const invertsUnit = unitItem.invertsUnit;
70582
+ if (invertsUnit) {
70583
+ const resolvedUnit = this.schema.lookupItemSync(invertsUnit.fullName);
70584
+ return resolvedUnit && _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(resolvedUnit) ? resolvedUnit.phenomenon : undefined;
70585
+ }
70586
+ return undefined;
70587
+ };
70588
+ const phenomenon1 = getPhenomenon(unit1Item);
70589
+ const phenomenon2 = getPhenomenon(unit2Item);
70590
+ if (phenomenon1?.fullName !== phenomenon2?.fullName) {
70591
+ 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.`);
70592
+ }
70593
+ }
70534
70594
  }
70535
70595
  }
70536
70596
  async fromJSON(formatProps) {
70537
70597
  await super.fromJSON(formatProps);
70538
70598
  this.typecheck(formatProps);
70539
- if (undefined === formatProps.composite)
70540
- return;
70541
- // Units are separated from the rest of the deserialization because of the need to have separate sync and async implementation
70542
- for (const unit of formatProps.composite.units) {
70543
- const newUnit = await this.schema.lookupItem(unit.name);
70544
- if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
70545
- throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
70546
- if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit))
70547
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
70548
- else if (_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit))
70549
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
70599
+ // Process composite units
70600
+ if (undefined !== formatProps.composite) {
70601
+ for (const unit of formatProps.composite.units) {
70602
+ const newUnit = await this.schema.lookupItem(unit.name);
70603
+ if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
70604
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
70605
+ const lazyUnit = _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit)
70606
+ ? new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit)
70607
+ : new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit);
70608
+ this.addUnit(lazyUnit, unit.label);
70609
+ }
70610
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
70611
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && this._units && this._units.length === 2) {
70612
+ const unit1Item = await this.schema.lookupItem(this._units[0][0].fullName);
70613
+ const unit2Item = await this.schema.lookupItem(this._units[1][0].fullName);
70614
+ 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)))
70615
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has invalid units.`);
70616
+ // Helper to extract phenomenon from Unit or InvertedUnit
70617
+ const getPhenomenon = async (unitItem) => {
70618
+ if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unitItem)) {
70619
+ return unitItem.phenomenon;
70620
+ }
70621
+ const invertsUnit = await unitItem.invertsUnit;
70622
+ return invertsUnit ? invertsUnit.phenomenon : undefined;
70623
+ };
70624
+ const phenomenon1 = await getPhenomenon(unit1Item);
70625
+ const phenomenon2 = await getPhenomenon(unit2Item);
70626
+ if (phenomenon1?.fullName !== phenomenon2?.fullName) {
70627
+ 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.`);
70628
+ }
70629
+ }
70550
70630
  }
70551
70631
  }
70552
70632
  /**
@@ -70583,6 +70663,15 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70583
70663
  if (" " !== this.stationSeparator)
70584
70664
  schemaJson.stationSeparator = this.stationSeparator;
70585
70665
  }
70666
+ // Only include ratio properties for EC version 3.3+
70667
+ if (_itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio === this.type && this.schema.originalECSpecMajorVersion === 3 && this.schema.originalECSpecMinorVersion !== undefined && this.schema.originalECSpecMinorVersion >= 3) {
70668
+ if (undefined !== this.ratioType)
70669
+ schemaJson.ratioType = this.ratioType;
70670
+ if (undefined !== this.ratioSeparator)
70671
+ schemaJson.ratioSeparator = this.ratioSeparator;
70672
+ if (undefined !== this.ratioFormatType)
70673
+ schemaJson.ratioFormatType = this.ratioFormatType;
70674
+ }
70586
70675
  if (undefined === this.units)
70587
70676
  return schemaJson;
70588
70677
  schemaJson.composite = {};
@@ -70614,6 +70703,15 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70614
70703
  itemElement.setAttribute("minWidth", this.minWidth.toString());
70615
70704
  if (undefined !== this.scientificType)
70616
70705
  itemElement.setAttribute("scientificType", this.scientificType);
70706
+ // Only include ratio properties for EC version 3.3+
70707
+ if (this.schema.originalECSpecMajorVersion === 3 && this.schema.originalECSpecMinorVersion !== undefined && this.schema.originalECSpecMinorVersion >= 3) {
70708
+ if (undefined !== this.ratioType)
70709
+ itemElement.setAttribute("ratioType", this.ratioType);
70710
+ if (undefined !== this.ratioSeparator)
70711
+ itemElement.setAttribute("ratioSeparator", this.ratioSeparator);
70712
+ if (undefined !== this.ratioFormatType)
70713
+ itemElement.setAttribute("ratioFormatType", this.ratioFormatType);
70714
+ }
70617
70715
  if (undefined !== this.stationOffsetSize)
70618
70716
  itemElement.setAttribute("stationOffsetSize", this.stationOffsetSize.toString());
70619
70717
  const formatTraits = (0,_itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.formatTraitsToArray)(this.formatTraits);
@@ -71360,6 +71458,9 @@ class OverrideFormat {
71360
71458
  get type() { return this.parent.type; }
71361
71459
  get minWidth() { return this.parent.minWidth; }
71362
71460
  get scientificType() { return this.parent.scientificType; }
71461
+ get ratioType() { return this.parent.ratioType; }
71462
+ get ratioSeparator() { return this.parent.ratioSeparator; }
71463
+ get ratioFormatType() { return this.parent.ratioFormatType; }
71363
71464
  get showSignOption() { return this.parent.showSignOption; }
71364
71465
  get decimalSeparator() { return this.parent.decimalSeparator; }
71365
71466
  get thousandSeparator() { return this.parent.thousandSeparator; }
@@ -71384,7 +71485,7 @@ class OverrideFormat {
71384
71485
  for (const [unit, unitLabel] of this._units) {
71385
71486
  const unitSchema = koqSchema.context.getSchemaSync(unit.schemaKey);
71386
71487
  if (unitSchema === undefined)
71387
- 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.`);
71488
+ 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.`);
71388
71489
  fullName += "[";
71389
71490
  fullName += _Deserialization_XmlSerializationUtils__WEBPACK_IMPORTED_MODULE_0__.XmlSerializationUtils.createXmlTypedName(koqSchema, unitSchema, unit.name);
71390
71491
  if (unitLabel !== undefined)
@@ -108230,7 +108331,9 @@ function createMeshArgs(mesh) {
108230
108331
  if (!mesh.triangles || mesh.triangles.isEmpty || mesh.points.length === 0)
108231
108332
  return undefined;
108232
108333
  const texture = mesh.displayParams.textureMapping?.texture;
108233
- const textureMapping = texture && mesh.uvParams.length > 0 ? { texture, uvParams: mesh.uvParams } : undefined;
108334
+ const useConstantLod = mesh.displayParams.textureMapping?.params?.useConstantLod;
108335
+ const constantLodParams = mesh.displayParams.textureMapping?.params?.constantLodParams;
108336
+ const textureMapping = texture && mesh.uvParams.length > 0 ? { texture, uvParams: mesh.uvParams, useConstantLod, constantLodParams } : undefined;
108234
108337
  const colors = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorIndex();
108235
108338
  mesh.colorMap.toColorIndex(colors, mesh.colors);
108236
108339
  const features = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FeatureIndex();
@@ -161448,14 +161551,47 @@ class GltfReader {
161448
161551
  }
161449
161552
  }
161450
161553
  createDisplayParams(material, hasBakedLighting, isPointPrimitive = false) {
161554
+ let constantLodParamProps;
161555
+ let normalMapUseConstantLod = false;
161556
+ if (!(0,_common_gltf_GltfSchema__WEBPACK_IMPORTED_MODULE_12__.isGltf1Material)(material)) {
161557
+ // NOTE: EXT_textureInfo_constant_lod is not supported for occlusionTexture and metallicRoughnessTexture
161558
+ // Use the same texture fallback logic as extractTextureId
161559
+ const textureInfo = material.pbrMetallicRoughness?.baseColorTexture ?? material.emissiveTexture;
161560
+ const extConstantLod = textureInfo?.extensions?.EXT_textureInfo_constant_lod;
161561
+ const offset = extConstantLod?.offset;
161562
+ extConstantLod ? constantLodParamProps = {
161563
+ repetitions: extConstantLod?.repetitions,
161564
+ offset: offset ? { x: offset[0], y: offset[1] } : undefined,
161565
+ minDistClamp: extConstantLod?.minClampDistance,
161566
+ maxDistClamp: extConstantLod?.maxClampDistance,
161567
+ } : undefined;
161568
+ // Normal map only uses constant LOD if both the base texture and normal texture have the extension
161569
+ normalMapUseConstantLod = extConstantLod !== undefined && material.normalTexture?.extensions?.EXT_textureInfo_constant_lod !== undefined;
161570
+ }
161451
161571
  const isTransparent = this.isMaterialTransparent(material);
161452
161572
  const textureId = this.extractTextureId(material);
161453
161573
  const normalMapId = this.extractNormalMapId(material);
161454
- let textureMapping = (undefined !== textureId || undefined !== normalMapId) ? this.findTextureMapping(textureId, isTransparent, normalMapId) : undefined;
161574
+ let textureMapping = (undefined !== textureId || undefined !== normalMapId) ? this.findTextureMapping(textureId, isTransparent, normalMapId, constantLodParamProps, normalMapUseConstantLod) : undefined;
161455
161575
  const color = colorFromMaterial(material, isTransparent);
161456
161576
  let renderMaterial;
161457
- if (undefined !== textureMapping && undefined !== textureMapping.normalMapParams) {
161458
- const args = { diffuse: { color }, specular: { color: _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorDef.white }, textureMapping };
161577
+ if (undefined !== textureMapping) {
161578
+ // Convert result of findTextureMapping (TextureMapping object) to MaterialTextureMappingProps interface
161579
+ const textureMappingProps = {
161580
+ texture: textureMapping.texture,
161581
+ normalMapParams: textureMapping.normalMapParams,
161582
+ mode: textureMapping.params.mode,
161583
+ transform: textureMapping.params.textureMatrix,
161584
+ weight: textureMapping.params.weight,
161585
+ worldMapping: textureMapping.params.worldMapping,
161586
+ useConstantLod: textureMapping.params.useConstantLod,
161587
+ constantLodProps: textureMapping.params.useConstantLod ? {
161588
+ repetitions: textureMapping.params.constantLodParams.repetitions,
161589
+ offset: textureMapping.params.constantLodParams.offset,
161590
+ minDistClamp: textureMapping.params.constantLodParams.minDistClamp,
161591
+ maxDistClamp: textureMapping.params.constantLodParams.maxDistClamp,
161592
+ } : undefined,
161593
+ };
161594
+ const args = { diffuse: { color }, specular: { color: _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorDef.white }, textureMapping: textureMappingProps };
161459
161595
  renderMaterial = _IModelApp__WEBPACK_IMPORTED_MODULE_3__.IModelApp.renderSystem.createRenderMaterial(args);
161460
161596
  // DisplayParams doesn't want a separate texture mapping if the material already has one.
161461
161597
  textureMapping = undefined;
@@ -162413,7 +162549,7 @@ class GltfReader {
162413
162549
  });
162414
162550
  return renderTexture ?? false;
162415
162551
  }
162416
- findTextureMapping(id, isTransparent, normalMapId) {
162552
+ findTextureMapping(id, isTransparent, normalMapId, constantLodParamProps, normalMapUseConstantLod = false) {
162417
162553
  if (undefined === id && undefined === normalMapId)
162418
162554
  return undefined;
162419
162555
  let texture;
@@ -162435,16 +162571,18 @@ class GltfReader {
162435
162571
  nMap = {
162436
162572
  normalMap,
162437
162573
  greenUp,
162574
+ useConstantLod: normalMapUseConstantLod,
162438
162575
  };
162439
162576
  }
162440
162577
  else {
162441
162578
  texture = normalMap;
162442
- nMap = { greenUp };
162579
+ nMap = { greenUp, useConstantLod: normalMapUseConstantLod };
162443
162580
  }
162444
162581
  }
162445
162582
  if (!texture)
162446
162583
  return undefined;
162447
- const textureMapping = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping(texture, new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping.Params());
162584
+ const useConstantLod = constantLodParamProps !== undefined;
162585
+ 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 }));
162448
162586
  textureMapping.normalMapParams = nMap;
162449
162587
  return textureMapping;
162450
162588
  }
@@ -304140,6 +304278,8 @@ class BaseFormat {
304140
304278
  _stationOffsetSize; // required when type is station; positive integer > 0
304141
304279
  _stationBaseFactor; // optional positive integer base factor for station formatting; default is 1
304142
304280
  _ratioType; // required if type is ratio; options: oneToN, NToOne, ValueBased, useGreatestCommonDivisor
304281
+ _ratioFormatType; // defaults to Decimal if not specified
304282
+ _ratioSeparator; // default is ":"; separator character used in ratio formatting
304143
304283
  _azimuthBase; // value always clockwise from north
304144
304284
  _azimuthBaseUnit; // unit for azimuthBase value
304145
304285
  _azimuthCounterClockwise; // if set to true, azimuth values are returned counter-clockwise from base
@@ -304161,6 +304301,10 @@ class BaseFormat {
304161
304301
  set scientificType(scientificType) { this._scientificType = scientificType; }
304162
304302
  get ratioType() { return this._ratioType; }
304163
304303
  set ratioType(ratioType) { this._ratioType = ratioType; }
304304
+ get ratioFormatType() { return this._ratioFormatType; }
304305
+ set ratioFormatType(ratioFormatType) { this._ratioFormatType = ratioFormatType; }
304306
+ get ratioSeparator() { return this._ratioSeparator; }
304307
+ set ratioSeparator(ratioSeparator) { this._ratioSeparator = ratioSeparator; }
304164
304308
  get showSignOption() { return this._showSignOption; }
304165
304309
  set showSignOption(showSignOption) { this._showSignOption = showSignOption; }
304166
304310
  get decimalSeparator() { return this._decimalSeparator; }
@@ -304229,6 +304373,22 @@ class BaseFormat {
304229
304373
  if (undefined === formatProps.ratioType)
304230
304374
  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.`);
304231
304375
  this._ratioType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseRatioType)(formatProps.ratioType, this.name);
304376
+ if (undefined !== formatProps.ratioSeparator) {
304377
+ if (typeof (formatProps.ratioSeparator) !== "string")
304378
+ 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'.`);
304379
+ if (formatProps.ratioSeparator.length !== 1)
304380
+ 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.`);
304381
+ this._ratioSeparator = formatProps.ratioSeparator;
304382
+ }
304383
+ else {
304384
+ this._ratioSeparator = ":"; // Apply default
304385
+ }
304386
+ if (undefined !== formatProps.ratioFormatType) {
304387
+ this._ratioFormatType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseRatioFormatType)(formatProps.ratioFormatType, this.name);
304388
+ }
304389
+ else {
304390
+ this._ratioFormatType = _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioFormatType.Decimal; // Apply default
304391
+ }
304232
304392
  }
304233
304393
  if (undefined !== formatProps.roundFactor) { // optional; default is 0.0
304234
304394
  if (typeof (formatProps.roundFactor) !== "number")
@@ -304353,6 +304513,8 @@ class Format extends BaseFormat {
304353
304513
  newFormat._azimuthBaseUnit = this._azimuthBaseUnit;
304354
304514
  newFormat._azimuthCounterClockwise = this._azimuthCounterClockwise;
304355
304515
  newFormat._ratioType = this._ratioType;
304516
+ newFormat._ratioFormatType = this._ratioFormatType;
304517
+ newFormat._ratioSeparator = this._ratioSeparator;
304356
304518
  newFormat._revolutionUnit = this._revolutionUnit;
304357
304519
  newFormat._customProps = this._customProps;
304358
304520
  this._units && (newFormat._units = [...this._units]);
@@ -304413,25 +304575,28 @@ class Format extends BaseFormat {
304413
304575
  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'`);
304414
304576
  }
304415
304577
  if (jsonObj.composite.units.length > 0 && jsonObj.composite.units.length <= 4) { // Composite requires 1-4 units
304416
- for (const nextUnit of jsonObj.composite.units) {
304417
- if (this._units) {
304418
- for (const existingUnit of this._units) {
304419
- const unitObj = existingUnit[0].name;
304420
- if (unitObj.toLowerCase() === nextUnit.unit.name.toLowerCase()) {
304421
- throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The unit ${unitObj} has a duplicate name.`);
304422
- }
304578
+ const isDuplicateAllowed = this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio;
304579
+ const seenUnits = new Set();
304580
+ this._units = [];
304581
+ for (const unitSpec of jsonObj.composite.units) {
304582
+ if (!isDuplicateAllowed) {
304583
+ const unitName = unitSpec.unit.name.toLowerCase();
304584
+ const existingName = seenUnits.has(unitName);
304585
+ if (existingName) {
304586
+ 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}'`);
304423
304587
  }
304588
+ seenUnits.add(unitName);
304424
304589
  }
304425
- if (undefined === this._units) {
304426
- this._units = [];
304427
- }
304428
- this._units.push([nextUnit.unit, nextUnit.label]);
304590
+ this._units.push([unitSpec.unit, unitSpec.label]);
304429
304591
  }
304430
304592
  }
304431
304593
  }
304432
304594
  if (undefined === this.units || this.units.length === 0)
304433
304595
  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'`);
304434
304596
  }
304597
+ if (this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio && (!this._units || this._units.length === 0)) {
304598
+ 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.`);
304599
+ }
304435
304600
  if (this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth || this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing) {
304436
304601
  this._azimuthBaseUnit = jsonObj.azimuthBaseUnit;
304437
304602
  this._revolutionUnit = jsonObj.revolutionUnit;
@@ -304498,6 +304663,8 @@ class Format extends BaseFormat {
304498
304663
  uomSeparator: this.uomSeparator,
304499
304664
  scientificType: this.scientificType ? this.scientificType : undefined,
304500
304665
  ratioType: this.ratioType,
304666
+ ratioFormatType: this.ratioFormatType,
304667
+ ratioSeparator: this.ratioSeparator,
304501
304668
  stationOffsetSize: this.stationOffsetSize,
304502
304669
  stationSeparator: this.stationSeparator,
304503
304670
  stationBaseFactor: this.stationBaseFactor,
@@ -304542,6 +304709,15 @@ async function resolveFormatProps(formatName, unitsProvider, jsonObj) {
304542
304709
  const unit = await resolveCompositeUnit(unitsProvider, entry.name);
304543
304710
  return { unit, label: entry.label };
304544
304711
  }));
304712
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
304713
+ const formatType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseFormatType)(jsonObj.type, formatName);
304714
+ if (formatType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio && units.length === 2) {
304715
+ const phenomenon1 = units[0].unit.phenomenon;
304716
+ const phenomenon2 = units[1].unit.phenomenon;
304717
+ if (phenomenon1 !== phenomenon2) {
304718
+ 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}'.`);
304719
+ }
304720
+ }
304545
304721
  }
304546
304722
  let azimuthBaseUnit, revolutionUnit;
304547
304723
  const type = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseFormatType)(jsonObj.type, formatName);
@@ -304582,6 +304758,7 @@ __webpack_require__.r(__webpack_exports__);
304582
304758
  /* harmony export */ FormatTraits: () => (/* binding */ FormatTraits),
304583
304759
  /* harmony export */ FormatType: () => (/* binding */ FormatType),
304584
304760
  /* harmony export */ FractionalPrecision: () => (/* binding */ FractionalPrecision),
304761
+ /* harmony export */ RatioFormatType: () => (/* binding */ RatioFormatType),
304585
304762
  /* harmony export */ RatioType: () => (/* binding */ RatioType),
304586
304763
  /* harmony export */ ScientificType: () => (/* binding */ ScientificType),
304587
304764
  /* harmony export */ ShowSignOption: () => (/* binding */ ShowSignOption),
@@ -304595,6 +304772,7 @@ __webpack_require__.r(__webpack_exports__);
304595
304772
  /* harmony export */ parseFormatType: () => (/* binding */ parseFormatType),
304596
304773
  /* harmony export */ parseFractionalPrecision: () => (/* binding */ parseFractionalPrecision),
304597
304774
  /* harmony export */ parsePrecision: () => (/* binding */ parsePrecision),
304775
+ /* harmony export */ parseRatioFormatType: () => (/* binding */ parseRatioFormatType),
304598
304776
  /* harmony export */ parseRatioType: () => (/* binding */ parseRatioType),
304599
304777
  /* harmony export */ parseScientificType: () => (/* binding */ parseScientificType),
304600
304778
  /* harmony export */ parseShowSignOption: () => (/* binding */ parseShowSignOption),
@@ -304739,6 +304917,16 @@ var RatioType;
304739
304917
  /** 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 */
304740
304918
  RatioType["UseGreatestCommonDivisor"] = "UseGreatestCommonDivisor";
304741
304919
  })(RatioType || (RatioType = {}));
304920
+ /** The format type for the numbers within a ratio.
304921
+ * @beta
304922
+ */
304923
+ var RatioFormatType;
304924
+ (function (RatioFormatType) {
304925
+ /** Decimal display (ie 2.125) */
304926
+ RatioFormatType["Decimal"] = "Decimal";
304927
+ /** Fractional display (ie 2-1/8) */
304928
+ RatioFormatType["Fractional"] = "Fractional";
304929
+ })(RatioFormatType || (RatioFormatType = {}));
304742
304930
  /** Determines how the sign of values are displayed
304743
304931
  * @beta */
304744
304932
  var ShowSignOption;
@@ -304784,6 +304972,18 @@ function parseRatioType(ratioType, formatName) {
304784
304972
  }
304785
304973
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityStatus.InvalidJson, `The Format ${formatName} has an invalid 'ratioType' attribute.`);
304786
304974
  }
304975
+ /** @beta */
304976
+ function parseRatioFormatType(ratioFormatType, formatName) {
304977
+ const normalizedValue = ratioFormatType.toLowerCase();
304978
+ for (const key in RatioFormatType) {
304979
+ if (RatioFormatType.hasOwnProperty(key)) {
304980
+ const enumValue = RatioFormatType[key];
304981
+ if (enumValue.toLowerCase() === normalizedValue)
304982
+ return enumValue;
304983
+ }
304984
+ }
304985
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityStatus.InvalidJson, `The Format ${formatName} has an invalid 'ratioFormatType' attribute.`);
304986
+ }
304787
304987
  /** @beta */
304788
304988
  function parseShowSignOption(showSignOption, formatName) {
304789
304989
  switch (showSignOption.toLowerCase()) {
@@ -304945,10 +305145,18 @@ function parsePrecision(precision, type, formatName) {
304945
305145
  case FormatType.Decimal:
304946
305146
  case FormatType.Scientific:
304947
305147
  case FormatType.Station:
304948
- case FormatType.Ratio:
304949
305148
  case FormatType.Bearing:
304950
305149
  case FormatType.Azimuth:
304951
305150
  return parseDecimalPrecision(precision, formatName);
305151
+ case FormatType.Ratio:
305152
+ // Ratio type can use either decimal or fractional precision depending on ratioFormatType
305153
+ // Try decimal first, if it fails, try fractional
305154
+ try {
305155
+ return parseDecimalPrecision(precision, formatName);
305156
+ }
305157
+ catch {
305158
+ return parseFractionalPrecision(precision, formatName);
305159
+ }
304952
305160
  case FormatType.Fractional:
304953
305161
  return parseFractionalPrecision(precision, formatName);
304954
305162
  default:
@@ -304972,8 +305180,9 @@ __webpack_require__.r(__webpack_exports__);
304972
305180
  /* harmony export */ });
304973
305181
  /* harmony import */ var _Constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Constants */ "../../core/quantity/lib/esm/Constants.js");
304974
305182
  /* harmony import */ var _Exception__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Exception */ "../../core/quantity/lib/esm/Exception.js");
304975
- /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
304976
- /* harmony import */ var _Quantity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../Quantity */ "../../core/quantity/lib/esm/Quantity.js");
305183
+ /* harmony import */ var _FormatterSpec__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./FormatterSpec */ "../../core/quantity/lib/esm/Formatter/FormatterSpec.js");
305184
+ /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
305185
+ /* harmony import */ var _Quantity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../Quantity */ "../../core/quantity/lib/esm/Quantity.js");
304977
305186
  /*---------------------------------------------------------------------------------------------
304978
305187
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
304979
305188
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -304985,6 +305194,7 @@ __webpack_require__.r(__webpack_exports__);
304985
305194
 
304986
305195
 
304987
305196
 
305197
+
304988
305198
  /** rounding additive
304989
305199
  * @internal
304990
305200
  */
@@ -305085,7 +305295,7 @@ class Formatter {
305085
305295
  static integerPartToText(wholePart, spec) {
305086
305296
  // build invariant string represent wholePart
305087
305297
  let formattedValue = wholePart.toFixed(0);
305088
- if ((formattedValue.length > 3) && (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.Use1000Separator) && (spec.format.thousandSeparator.length > 0))) {
305298
+ if ((formattedValue.length > 3) && (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.Use1000Separator) && (spec.format.thousandSeparator.length > 0))) {
305089
305299
  let numSeparators = Math.floor(formattedValue.length / 3);
305090
305300
  let groupLength = formattedValue.length % 3;
305091
305301
  if (groupLength === 0) {
@@ -305132,7 +305342,7 @@ class Formatter {
305132
305342
  else {
305133
305343
  componentText = Formatter.formatMagnitude(compositeValue, spec);
305134
305344
  }
305135
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ShowUnitLabel)) {
305345
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel)) {
305136
305346
  componentText = componentText + spec.format.uomSeparator + label;
305137
305347
  }
305138
305348
  return componentText;
@@ -305152,12 +305362,11 @@ class Formatter {
305152
305362
  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.`);
305153
305363
  if (i > 0 && unitConversion.offset !== 0) // offset should only ever be defined for major unit
305154
305364
  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.`);
305155
- let unitValue = 0.0;
305156
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio) {
305157
- if (1 !== (spec.format.units?.length ?? 0))
305158
- 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.`);
305365
+ // Handle ratio format with composite units
305366
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio) {
305367
+ let ratioUnitValue = 0.0;
305159
305368
  try {
305160
- unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305369
+ ratioUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305161
305370
  }
305162
305371
  catch (e) {
305163
305372
  // The "InvertingZero" error is thrown when the value is zero and the conversion factor is inverted.
@@ -305165,12 +305374,13 @@ class Formatter {
305165
305374
  if (e instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && e.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
305166
305375
  return { componentText: "1:0", isNegative: false };
305167
305376
  }
305377
+ throw e;
305168
305378
  }
305169
- compositeStrings.push(this.formatRatio(unitValue, spec));
305170
- isNegative = unitValue < 0;
305379
+ compositeStrings.push(this.formatRatio(ratioUnitValue, spec));
305380
+ isNegative = ratioUnitValue < 0;
305171
305381
  continue;
305172
305382
  }
305173
- unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305383
+ let unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305174
305384
  if (0 === i) {
305175
305385
  // Only set isNegative from the first (major) unit conversion
305176
305386
  isNegative = unitValue < 0;
@@ -305178,16 +305388,16 @@ class Formatter {
305178
305388
  // but use higher precision if the format specifies it
305179
305389
  const precisionScale = Math.pow(10, Math.max(8, spec.format.precision));
305180
305390
  unitValue = Math.floor(unitValue * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
305181
- if ((Math.abs(unitValue) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ZeroEmpty))
305391
+ if ((Math.abs(unitValue) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ZeroEmpty))
305182
305392
  return { componentText: "", isNegative: false };
305183
305393
  }
305184
305394
  if (i < (spec.format.units?.length ?? 0) - 1) {
305185
305395
  let wholePart = Math.trunc(unitValue);
305186
305396
  // Check if the remaining fractional part will round up to a full unit in the next (smaller) component
305187
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Fractional && i === spec.unitConversions.length - 2) {
305397
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional && i === spec.unitConversions.length - 2) {
305188
305398
  // For the second-to-last unit with fractional formatting, check if rounding causes carry-over
305189
305399
  const fractionalPart = unitValue - wholePart;
305190
- const nextUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(fractionalPart, spec.unitConversions[i + 1].conversion);
305400
+ const nextUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(fractionalPart, spec.unitConversions[i + 1].conversion);
305191
305401
  // Create a FractionalNumeric to determine what the rounded value would be
305192
305402
  const fn = new FractionalNumeric(Math.abs(nextUnitValue), spec.format.precision, true);
305193
305403
  // If the fractional numeric rounds to a whole unit (integral part increased due to rounding)
@@ -305217,18 +305427,18 @@ class Formatter {
305217
305427
  */
305218
305428
  static formatMagnitude(magnitude, spec) {
305219
305429
  let posMagnitude = Math.abs(magnitude);
305220
- if ((Math.abs(posMagnitude) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ZeroEmpty))
305430
+ if ((Math.abs(posMagnitude) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ZeroEmpty))
305221
305431
  return "";
305222
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ApplyRounding))
305432
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ApplyRounding))
305223
305433
  posMagnitude = Math.abs(Formatter.roundDouble(magnitude, spec.format.roundFactor));
305224
- const isSci = ((posMagnitude > 1.0e12) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Scientific);
305225
- 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;
305226
- const isFractional = (!isDecimal && spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Fractional);
305434
+ const isSci = ((posMagnitude > 1.0e12) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Scientific);
305435
+ 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;
305436
+ const isFractional = (!isDecimal && spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional);
305227
305437
  /* const usesStops = spec.format.type === FormatType.Station; */
305228
- const isPrecisionZero = spec.format.precision === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.DecimalPrecision.Zero;
305229
- const isKeepSingleZero = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepSingleZero);
305438
+ const isPrecisionZero = spec.format.precision === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.DecimalPrecision.Zero;
305439
+ const isKeepSingleZero = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepSingleZero);
305230
305440
  const precisionScale = Math.pow(10.0, spec.format.precision);
305231
- const isKeepTrailingZeroes = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.TrailZeroes);
305441
+ const isKeepTrailingZeroes = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.TrailZeroes);
305232
305442
  let expInt = 0.0;
305233
305443
  if (isSci && (posMagnitude !== 0.0)) {
305234
305444
  let exp = Math.log10(posMagnitude);
@@ -305238,10 +305448,10 @@ class Formatter {
305238
305448
  negativeExp = true;
305239
305449
  }
305240
305450
  expInt = Math.floor(exp);
305241
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Scientific) {
305242
- if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ScientificType.ZeroNormalized && posMagnitude > 1.0)
305451
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Scientific) {
305452
+ if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ScientificType.ZeroNormalized && posMagnitude > 1.0)
305243
305453
  expInt += 1.0;
305244
- else if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ScientificType.Normalized && posMagnitude < 1.0)
305454
+ else if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ScientificType.Normalized && posMagnitude < 1.0)
305245
305455
  expInt += 1.0;
305246
305456
  if (negativeExp)
305247
305457
  expInt = -expInt;
@@ -305263,7 +305473,7 @@ class Formatter {
305263
305473
  }
305264
305474
  formattedValue = Formatter.integerPartToText(wholePart, spec);
305265
305475
  if (isPrecisionZero) {
305266
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint) && !isKeepSingleZero)
305476
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint) && !isKeepSingleZero)
305267
305477
  formattedValue = formattedValue + spec.format.decimalSeparator;
305268
305478
  else if (isKeepSingleZero)
305269
305479
  formattedValue = `${formattedValue + spec.format.decimalSeparator}0`;
@@ -305278,7 +305488,7 @@ class Formatter {
305278
305488
  if (fractionString.length > 0)
305279
305489
  formattedValue = formattedValue + spec.format.decimalSeparator + fractionString;
305280
305490
  else {
305281
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint))
305491
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint))
305282
305492
  formattedValue = formattedValue + spec.format.decimalSeparator + (isKeepSingleZero ? "0" : "");
305283
305493
  }
305284
305494
  }
@@ -305291,7 +305501,7 @@ class Formatter {
305291
305501
  const fn = new FractionalNumeric(posMagnitude, spec.format.precision, true);
305292
305502
  formattedValue = fn.getIntegralString();
305293
305503
  if (!fn.isZero && fn.hasFractionPart) {
305294
- const wholeFractionSeparator = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.FractionDash) ? "-" : " ";
305504
+ const wholeFractionSeparator = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.FractionDash) ? "-" : " ";
305295
305505
  const fractionString = `${fn.getNumeratorString()}/${fn.getDenominatorString()}`;
305296
305506
  formattedValue = formattedValue + wholeFractionSeparator + fractionString;
305297
305507
  }
@@ -305320,7 +305530,7 @@ class Formatter {
305320
305530
  else {
305321
305531
  if (isKeepTrailingZeroes)
305322
305532
  fractionString = spec.format.decimalSeparator + "".padEnd(spec.format.precision, "0");
305323
- else if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint))
305533
+ else if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint))
305324
305534
  fractionString = spec.format.decimalSeparator;
305325
305535
  formattedValue = stationString + fractionString;
305326
305536
  }
@@ -305349,21 +305559,21 @@ class Formatter {
305349
305559
  let prefix = "";
305350
305560
  let suffix = "";
305351
305561
  switch (showSignOption) {
305352
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.NegativeParentheses:
305562
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.NegativeParentheses:
305353
305563
  if (isNegative) {
305354
305564
  prefix = "(";
305355
305565
  suffix = ")";
305356
305566
  }
305357
305567
  break;
305358
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.OnlyNegative:
305359
- if (isNegative && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
305568
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.OnlyNegative:
305569
+ if (isNegative && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
305360
305570
  prefix = "-";
305361
305571
  }
305362
305572
  break;
305363
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.SignAlways:
305573
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.SignAlways:
305364
305574
  prefix = isNegative ? "-" : "+";
305365
305575
  break;
305366
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.NoSign:
305576
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.NoSign:
305367
305577
  default:
305368
305578
  break;
305369
305579
  }
@@ -305379,14 +305589,20 @@ class Formatter {
305379
305589
  let suffix = "";
305380
305590
  let formattedValue = "";
305381
305591
  // Handle bearing/azimuth special formatting
305382
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
305592
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
305383
305593
  const result = this.processBearingAndAzimuth(magnitude, spec);
305384
305594
  magnitude = result.magnitude;
305385
305595
  prefix = result.prefix ?? "";
305386
305596
  suffix = result.suffix ?? "";
305387
305597
  }
305388
305598
  let formattedMagnitude = "";
305389
- if (spec.format.hasUnits) {
305599
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && spec.unitConversions.length >= 3) {
305600
+ // Handle ratio formatting separately when 2-unit composite provides 3 conversion specs
305601
+ const ratioResult = this.formatRatioQuantity(magnitude, spec);
305602
+ formattedMagnitude = ratioResult.componentText;
305603
+ valueIsNegative = ratioResult.isNegative;
305604
+ }
305605
+ else if (spec.format.hasUnits) {
305390
305606
  const compositeResult = Formatter.formatComposite(magnitude, spec);
305391
305607
  formattedMagnitude = compositeResult.componentText;
305392
305608
  // Override the sign detection with the composite conversion result
@@ -305395,8 +305611,8 @@ class Formatter {
305395
305611
  else {
305396
305612
  // unitless quantity
305397
305613
  formattedMagnitude = Formatter.formatMagnitude(magnitude, spec);
305398
- if (formattedMagnitude.length > 0 && spec.unitConversions.length > 0 && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ShowUnitLabel)) {
305399
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.PrependUnitLabel))
305614
+ if (formattedMagnitude.length > 0 && spec.unitConversions.length > 0 && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel)) {
305615
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.PrependUnitLabel))
305400
305616
  formattedMagnitude = spec.unitConversions[0].label + spec.format.uomSeparator + formattedMagnitude;
305401
305617
  else
305402
305618
  formattedMagnitude = formattedMagnitude + spec.format.uomSeparator + spec.unitConversions[0].label;
@@ -305416,12 +305632,12 @@ class Formatter {
305416
305632
  }
305417
305633
  static processBearingAndAzimuth(magnitude, spec) {
305418
305634
  const type = spec.format.type;
305419
- if (type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing && type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth)
305635
+ if (type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing && type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth)
305420
305636
  return { magnitude };
305421
305637
  const revolution = this.getRevolution(spec);
305422
305638
  magnitude = this.normalizeAngle(magnitude, revolution);
305423
305639
  const quarterRevolution = revolution / 4;
305424
- if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing) {
305640
+ if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing) {
305425
305641
  let quadrant = 0;
305426
305642
  while (magnitude > quarterRevolution) {
305427
305643
  magnitude -= quarterRevolution;
@@ -305448,7 +305664,7 @@ class Formatter {
305448
305664
  if (quadrant === 2 && spec.unitConversions.length > 0) {
305449
305665
  // To determine if value is small, we need to convert it to the smallest unit presented and use the provided precision on it
305450
305666
  const unitConversion = spec.unitConversions[spec.unitConversions.length - 1].conversion;
305451
- const smallestFormattedDelta = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)((quarterRevolution - magnitude), unitConversion) + this.FPV_MINTHRESHOLD;
305667
+ const smallestFormattedDelta = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)((quarterRevolution - magnitude), unitConversion) + this.FPV_MINTHRESHOLD;
305452
305668
  const precisionScale = Math.pow(10.0, spec.format.precision);
305453
305669
  const floor = Math.floor((smallestFormattedDelta) * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
305454
305670
  if (floor === 0) {
@@ -305457,13 +305673,13 @@ class Formatter {
305457
305673
  }
305458
305674
  return { magnitude, prefix, suffix };
305459
305675
  }
305460
- if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
305676
+ if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
305461
305677
  let azimuthBase = 0; // default base is North
305462
305678
  if (spec.format.azimuthBase !== undefined) {
305463
305679
  if (spec.azimuthBaseConversion === undefined) {
305464
305680
  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.`);
305465
305681
  }
305466
- const azBaseQuantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.azimuthBaseUnit, spec.format.azimuthBase);
305682
+ const azBaseQuantity = new _Quantity__WEBPACK_IMPORTED_MODULE_4__.Quantity(spec.format.azimuthBaseUnit, spec.format.azimuthBase);
305467
305683
  const azBaseConverted = azBaseQuantity.convertTo(spec.persistenceUnit, spec.azimuthBaseConversion);
305468
305684
  if (azBaseConverted === undefined || !azBaseConverted.isValid) {
305469
305685
  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}.`);
@@ -305493,40 +305709,87 @@ class Formatter {
305493
305709
  if (spec.revolutionConversion === undefined) {
305494
305710
  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.`);
305495
305711
  }
305496
- const revolution = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.revolutionUnit, 1.0);
305712
+ const revolution = new _Quantity__WEBPACK_IMPORTED_MODULE_4__.Quantity(spec.format.revolutionUnit, 1.0);
305497
305713
  const converted = revolution.convertTo(spec.persistenceUnit, spec.revolutionConversion);
305498
305714
  if (converted === undefined || !converted.isValid) {
305499
305715
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.UnsupportedUnit, `Failed to convert revolution unit to ${spec.persistenceUnit.name}.`);
305500
305716
  }
305501
305717
  return converted.magnitude;
305502
305718
  }
305719
+ static formatRatioPart(value, spec, side) {
305720
+ const formatType = spec.format.ratioFormatType === "Fractional" ? _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional : _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Decimal;
305721
+ const tempFormat = spec.format.clone({ type: formatType });
305722
+ const tempSpec = new _FormatterSpec__WEBPACK_IMPORTED_MODULE_2__.FormatterSpec(spec.name, tempFormat, spec.unitConversions, spec.persistenceUnit);
305723
+ let formattedValue = this.formatMagnitude(value, tempSpec);
305724
+ // For fractional ratio formatting, suppress leading "0" if the value is purely fractional
305725
+ if (formatType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional && formattedValue.startsWith("0 ")) {
305726
+ formattedValue = formattedValue.substring(2); // Remove "0 " prefix
305727
+ }
305728
+ // Add unit label if ShowUnitLabel trait is set
305729
+ // unitConversions[0] = ratio scale factor, [1] = numerator unit, [2] = denominator unit
305730
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel) && spec.unitConversions.length >= 3) {
305731
+ const labelToAdd = side === "numerator" ? spec.unitConversions[1].label : spec.unitConversions[2].label;
305732
+ formattedValue = formattedValue + labelToAdd;
305733
+ }
305734
+ return formattedValue;
305735
+ }
305736
+ /** Format a ratio quantity value (separate from composite formatting) */
305737
+ static formatRatioQuantity(magnitude, spec) {
305738
+ const unitConversion = spec.unitConversions[0].conversion;
305739
+ let unitValue = 0.0;
305740
+ try {
305741
+ unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(magnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305742
+ }
305743
+ catch (e) {
305744
+ // The "InvertingZero" error is thrown when the value is zero and the conversion factor is inverted.
305745
+ // For ratio, we return "1:0" as the formatted value.
305746
+ if (e instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && e.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
305747
+ return { componentText: "1:0", isNegative: false };
305748
+ }
305749
+ throw e;
305750
+ }
305751
+ const componentText = this.formatRatio(unitValue, spec);
305752
+ const isNegative = unitValue < 0;
305753
+ return { componentText, isNegative };
305754
+ }
305503
305755
  static formatRatio(magnitude, spec) {
305504
305756
  if (null === spec.format.ratioType)
305505
305757
  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.`);
305506
305758
  const precisionScale = Math.pow(10.0, spec.format.precision);
305759
+ const separator = spec.format.ratioSeparator;
305507
305760
  let reciprocal = 0;
305761
+ // Helper to get unit labels if ShowUnitLabel is set
305762
+ const getUnitLabels = () => {
305763
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel) && spec.unitConversions.length >= 3) {
305764
+ return { numeratorLabel: spec.unitConversions[1].label, denominatorLabel: spec.unitConversions[2].label };
305765
+ }
305766
+ return { numeratorLabel: "", denominatorLabel: "" };
305767
+ };
305768
+ const { numeratorLabel, denominatorLabel } = getUnitLabels();
305508
305769
  if (magnitude === 0.0)
305509
- return "0:1";
305770
+ return `0${separator}1`;
305510
305771
  else
305511
305772
  reciprocal = 1.0 / magnitude;
305512
305773
  switch (spec.format.ratioType) {
305513
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.OneToN:
305514
- return `1:${this.formatMagnitude(reciprocal, spec)}`;
305515
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.NToOne:
305516
- return `${this.formatMagnitude(magnitude, spec)}:1`;
305517
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.ValueBased:
305518
- if (magnitude > 1.0)
305519
- return `${this.formatMagnitude(magnitude, spec)}:1`;
305520
- else
305521
- return `1:${this.formatMagnitude(reciprocal, spec)}`;
305522
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.UseGreatestCommonDivisor:
305774
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.OneToN:
305775
+ return `1${numeratorLabel}${separator}${this.formatRatioPart(reciprocal, spec, "denominator")}`;
305776
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.NToOne:
305777
+ return `${this.formatRatioPart(magnitude, spec, "numerator")}${separator}1${denominatorLabel}`;
305778
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.ValueBased:
305779
+ if (magnitude > 1.0) {
305780
+ return `${this.formatRatioPart(magnitude, spec, "numerator")}${separator}1${denominatorLabel}`;
305781
+ }
305782
+ else {
305783
+ return `1${numeratorLabel}${separator}${this.formatRatioPart(reciprocal, spec, "denominator")}`;
305784
+ }
305785
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.UseGreatestCommonDivisor:
305523
305786
  magnitude = Math.round(magnitude * precisionScale) / precisionScale;
305524
305787
  let numerator = magnitude * precisionScale;
305525
305788
  let denominator = precisionScale;
305526
305789
  const gcd = FractionalNumeric.getGreatestCommonFactor(numerator, denominator);
305527
305790
  numerator /= gcd;
305528
305791
  denominator /= gcd;
305529
- return `${this.formatMagnitude(numerator, spec)}:${this.formatMagnitude(denominator, spec)}`;
305792
+ return `${this.formatRatioPart(numerator, spec, "numerator")}${separator}${this.formatRatioPart(denominator, spec, "denominator")}`;
305530
305793
  default:
305531
305794
  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.`);
305532
305795
  }
@@ -305547,7 +305810,8 @@ __webpack_require__.r(__webpack_exports__);
305547
305810
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
305548
305811
  /* harmony export */ FormatterSpec: () => (/* binding */ FormatterSpec)
305549
305812
  /* harmony export */ });
305550
- /* harmony import */ var _Formatter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Formatter */ "../../core/quantity/lib/esm/Formatter/Formatter.js");
305813
+ /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
305814
+ /* harmony import */ var _Formatter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Formatter */ "../../core/quantity/lib/esm/Formatter/Formatter.js");
305551
305815
  /*---------------------------------------------------------------------------------------------
305552
305816
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
305553
305817
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -305556,6 +305820,7 @@ __webpack_require__.r(__webpack_exports__);
305556
305820
  * @module Quantity
305557
305821
  */
305558
305822
 
305823
+
305559
305824
  // cSpell:ignore ZERONORMALIZED, nosign, onlynegative, signalways, negativeparentheses
305560
305825
  // cSpell:ignore trailzeroes, keepsinglezero, zeroempty, keepdecimalpoint, applyrounding, fractiondash, showunitlabel, prependunitlabel, exponentonlynegative
305561
305826
  /** 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.
@@ -305602,6 +305867,49 @@ class FormatterSpec {
305602
305867
  get persistenceUnit() { return this._persistenceUnit; }
305603
305868
  get azimuthBaseConversion() { return this._azimuthBaseConversion; }
305604
305869
  get revolutionConversion() { return this._revolutionConversion; }
305870
+ /** Build conversion specs for ratio format with 2 composite units (numerator/denominator). */
305871
+ static async getRatioUnitConversions(units, unitsProvider, persistenceUnit) {
305872
+ const conversions = [];
305873
+ const [numeratorUnit, numeratorLabel] = units[0];
305874
+ const [denominatorUnit, denominatorLabel] = units[1];
305875
+ // Compute ratio scale: how many numerator units per denominator unit (e.g., IN:FT = 12)
305876
+ const denominatorToNumerator = await unitsProvider.getConversion(denominatorUnit, numeratorUnit);
305877
+ const displayRatioScale = denominatorToNumerator.factor;
305878
+ // Avoid double-scaling: if persistence unit already encodes the display ratio, use factor 1.
305879
+ // Check by name heuristic (e.g., IN_PER_FT with ratioUnits [IN, FT] → no scaling needed)
305880
+ const persistenceName = persistenceUnit.name.toUpperCase();
305881
+ const numName = numeratorUnit.name.toUpperCase().split(".").pop() ?? "";
305882
+ const denName = denominatorUnit.name.toUpperCase().split(".").pop() ?? "";
305883
+ // Split by word boundaries (underscores, dots) and check for exact token matches
305884
+ const persistenceTokens = persistenceName.split(/[._]/);
305885
+ const isPersistenceMatchingRatio = persistenceTokens.includes(numName) && persistenceTokens.includes(denName);
305886
+ const ratioScaleFactor = isPersistenceMatchingRatio ? 1.0 : displayRatioScale;
305887
+ // First conversion spec: effective ratio unit conversion
305888
+ const ratioConversionSpec = {
305889
+ name: `${numeratorUnit.name}_per_${denominatorUnit.name}`,
305890
+ label: "",
305891
+ system: numeratorUnit.system,
305892
+ conversion: { factor: ratioScaleFactor, offset: 0.0 },
305893
+ };
305894
+ conversions.push(ratioConversionSpec);
305895
+ // Numerator unit for label lookup
305896
+ const numeratorSpec = {
305897
+ name: numeratorUnit.name,
305898
+ label: numeratorLabel?.length ? numeratorLabel : numeratorUnit.label,
305899
+ system: numeratorUnit.system,
305900
+ conversion: { factor: 1.0, offset: 0.0 },
305901
+ };
305902
+ conversions.push(numeratorSpec);
305903
+ // Denominator unit for label lookup
305904
+ const denominatorSpec = {
305905
+ name: denominatorUnit.name,
305906
+ label: denominatorLabel?.length ? denominatorLabel : denominatorUnit.label,
305907
+ system: denominatorUnit.system,
305908
+ conversion: { factor: 1.0, offset: 0.0 },
305909
+ };
305910
+ conversions.push(denominatorSpec);
305911
+ return conversions;
305912
+ }
305605
305913
  /** Get an array of UnitConversionSpecs, one for each unit that is to be shown in the formatted quantity string. */
305606
305914
  static async getUnitConversions(format, unitsProvider, inputUnit) {
305607
305915
  const conversions = [];
@@ -305615,6 +305923,10 @@ class FormatterSpec {
305615
305923
  throw new Error("Formatter Spec needs persistence unit to be specified");
305616
305924
  }
305617
305925
  }
305926
+ // Handle 2-unit composite for ratio formats (scale factors)
305927
+ if (format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_0__.FormatType.Ratio && format.units && format.units.length === 2) {
305928
+ return FormatterSpec.getRatioUnitConversions(format.units, unitsProvider, persistenceUnit);
305929
+ }
305618
305930
  if (format.units) {
305619
305931
  let convertFromUnit = inputUnit;
305620
305932
  for (const unit of format.units) {
@@ -305671,7 +305983,7 @@ class FormatterSpec {
305671
305983
  }
305672
305984
  /** Format a quantity value. */
305673
305985
  applyFormatting(magnitude) {
305674
- return _Formatter__WEBPACK_IMPORTED_MODULE_0__.Formatter.formatQuantity(magnitude, this);
305986
+ return _Formatter__WEBPACK_IMPORTED_MODULE_1__.Formatter.formatQuantity(magnitude, this);
305675
305987
  }
305676
305988
  }
305677
305989
 
@@ -305779,13 +306091,14 @@ var ParseError;
305779
306091
  ParseError[ParseError["BearingPrefixOrSuffixMissing"] = 7] = "BearingPrefixOrSuffixMissing";
305780
306092
  ParseError[ParseError["MathematicOperationFoundButIsNotAllowed"] = 8] = "MathematicOperationFoundButIsNotAllowed";
305781
306093
  ParseError[ParseError["BearingAngleOutOfRange"] = 9] = "BearingAngleOutOfRange";
306094
+ ParseError[ParseError["InvalidMathResult"] = 10] = "InvalidMathResult";
305782
306095
  })(ParseError || (ParseError = {}));
305783
306096
  var Operator;
305784
306097
  (function (Operator) {
305785
306098
  Operator["addition"] = "+";
305786
306099
  Operator["subtraction"] = "-";
305787
306100
  Operator["multiplication"] = "*";
305788
- Operator["division"] = "/"; // unsupported but we recognize it during parsing
306101
+ Operator["division"] = "/";
305789
306102
  })(Operator || (Operator = {}));
305790
306103
  function isOperator(char) {
305791
306104
  if (typeof char === "number") {
@@ -306540,36 +306853,107 @@ class Parser {
306540
306853
  magnitude = this.normalizeAngle(magnitude, revolution);
306541
306854
  return { ok: true, value: magnitude };
306542
306855
  }
306856
+ /**
306857
+ * Parse a ratio part string (numerator or denominator) to extract the numeric value and optional unit label.
306858
+ * This method processes tokens without applying unit conversions, allowing the ratio format
306859
+ * handler to manage conversions at the ratio level.
306860
+ *
306861
+ *
306862
+ * @note Fractions are already handled by parseQuantitySpecification, which converts them to
306863
+ * single numeric tokens (e.g., "1/2" becomes 0.5).
306864
+ *
306865
+ * @param partStr The string to parse, which may contain a number, fraction, or mixed fraction with optional unit label.
306866
+ * @param format The format specification used for token parsing.
306867
+ * @returns An object containing the parsed numeric value and optional unit label. Returns NaN for value if no number is found.
306868
+ */
306869
+ static parseRatioPart(partStr, format) {
306870
+ partStr = partStr.trim();
306871
+ // Parse tokens - fractions are automatically converted to decimal values by parseQuantitySpecification
306872
+ const tempFormat = format.clone({ type: _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Decimal });
306873
+ const tokens = Parser.parseQuantitySpecification(partStr, tempFormat);
306874
+ let value = NaN;
306875
+ let unitLabel;
306876
+ // Pre-process: merge negative operators with following numbers
306877
+ const processedTokens = [];
306878
+ for (let i = 0; i < tokens.length; i++) {
306879
+ const token = tokens[i];
306880
+ if (token.isOperator && i === 0 && token.value === "-" &&
306881
+ i + 1 < tokens.length && tokens[i + 1].isNumber) {
306882
+ // Merge negative sign with number
306883
+ processedTokens.push(new ParseToken(-tokens[i + 1].value));
306884
+ i++; // Skip the number token since we consumed it
306885
+ }
306886
+ else {
306887
+ processedTokens.push(token);
306888
+ }
306889
+ }
306890
+ // Extract numeric value and unit label from processed tokens
306891
+ for (const token of processedTokens) {
306892
+ if (token.isNumber && isNaN(value)) {
306893
+ value = token.value;
306894
+ }
306895
+ else if (token.isString && !token.isOperator) {
306896
+ // String token that's not an operator - treat as unit label
306897
+ unitLabel = token.value;
306898
+ }
306899
+ }
306900
+ return { value, unitLabel };
306901
+ }
306543
306902
  static parseRatioFormat(inString, spec) {
306544
306903
  if (!inString)
306545
306904
  return { ok: false, error: ParseError.NoValueOrUnitFoundInString };
306546
- const parts = inString.split(":");
306905
+ const separator = spec.format.ratioSeparator ?? ":";
306906
+ const parts = inString.split(separator);
306547
306907
  if (parts.length > 2)
306548
306908
  return { ok: false, error: ParseError.UnableToConvertParseTokensToQuantity };
306549
- const numerator = parseFloat(parts[0]);
306550
- let denominator;
306551
- if (parts.length === 1) {
306552
- denominator = 1.0;
306553
- }
306554
- else {
306555
- denominator = parseFloat(parts[1]);
306909
+ // If the string doesn't contain the expected separator but contains other ratio-like separators,
306910
+ // return an error since the wrong separator was used
306911
+ if (parts.length === 1 && !inString.includes(separator)) {
306912
+ // Check if the string contains other common ratio separators
306913
+ const otherSeparators = [":", "=", "/"];
306914
+ for (const otherSep of otherSeparators) {
306915
+ if (otherSep !== separator && inString.includes(otherSep)) {
306916
+ // The string looks like a ratio but uses the wrong separator
306917
+ return { ok: false, error: ParseError.UnableToConvertParseTokensToQuantity };
306918
+ }
306919
+ }
306920
+ // Parse as a regular quantity value (numerator only, denominator = 1)
306921
+ const result = this.parseAndProcessTokens(inString, spec.format, spec.unitConversions);
306922
+ return result;
306556
306923
  }
306557
- if (isNaN(numerator) || isNaN(denominator))
306924
+ // Parse numerator and denominator parts which may include unit labels
306925
+ const numeratorPart = this.parseRatioPart(parts[0], spec.format);
306926
+ const denominatorPart = parts.length === 1 ? { value: 1.0 } : this.parseRatioPart(parts[1], spec.format);
306927
+ if (isNaN(numeratorPart.value) || isNaN(denominatorPart.value))
306558
306928
  return { ok: false, error: ParseError.NoValueOrUnitFoundInString };
306929
+ // Handle 2-unit composite case - simpler conversion using the pre-computed scale factor
306930
+ if (spec.format.units && spec.format.units.length === 2 && spec.unitConversions.length >= 3) {
306931
+ const ratioConvSpec = spec.unitConversions[0];
306932
+ const scaleFactor = ratioConvSpec.conversion.factor;
306933
+ if (denominatorPart.value === 0) {
306934
+ return { ok: false, error: ParseError.InvalidMathResult };
306935
+ }
306936
+ // The ratio value is numerator/denominator in the display units (e.g., 12 for 12"=1')
306937
+ // Divide by scale factor to get persistence unit value (e.g., 12/12 = 1.0)
306938
+ const ratioValue = numeratorPart.value / denominatorPart.value;
306939
+ const convertedValue = ratioValue / scaleFactor;
306940
+ return { ok: true, value: convertedValue };
306941
+ }
306942
+ // Original flow for 1-unit composite - use Quantity.convertTo for proper unit conversion
306559
306943
  const defaultUnit = spec.format.units && spec.format.units.length > 0 ? spec.format.units[0][0] : undefined;
306560
306944
  const unitConversion = defaultUnit ? Parser.tryFindUnitConversion(defaultUnit.label, spec.unitConversions, defaultUnit) : undefined;
306561
306945
  if (!unitConversion) {
306562
306946
  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}.`);
306563
306947
  }
306564
- if (denominator === 0) {
306565
- if (unitConversion.inversion && numerator === 1)
306948
+ if (denominatorPart.value === 0) {
306949
+ if (unitConversion.inversion && numeratorPart.value === 1)
306566
306950
  return { ok: true, value: 0.0 };
306567
306951
  else
306568
- return { ok: false, error: ParseError.MathematicOperationFoundButIsNotAllowed };
306952
+ return { ok: false, error: ParseError.InvalidMathResult };
306569
306953
  }
306570
306954
  let quantity;
306571
306955
  if (spec.format.units && spec.outUnit) {
306572
- quantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.units[0][0], numerator / denominator);
306956
+ quantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.units[0][0], numeratorPart.value / denominatorPart.value);
306573
306957
  }
306574
306958
  else {
306575
306959
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, "Missing presentation unit or persistence unit for ratio format.");
@@ -306581,7 +306965,7 @@ class Parser {
306581
306965
  catch (err) {
306582
306966
  // for input of "0:N" with reversed unit
306583
306967
  if (err instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && err.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
306584
- return { ok: false, error: ParseError.MathematicOperationFoundButIsNotAllowed };
306968
+ return { ok: false, error: ParseError.InvalidMathResult };
306585
306969
  }
306586
306970
  }
306587
306971
  if (converted === undefined || !converted.isValid) {
@@ -306707,7 +307091,8 @@ __webpack_require__.r(__webpack_exports__);
306707
307091
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
306708
307092
  /* harmony export */ ParserSpec: () => (/* binding */ ParserSpec)
306709
307093
  /* harmony export */ });
306710
- /* harmony import */ var _Parser__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Parser */ "../../core/quantity/lib/esm/Parser.js");
307094
+ /* harmony import */ var _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Formatter/FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
307095
+ /* harmony import */ var _Parser__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Parser */ "../../core/quantity/lib/esm/Parser.js");
306711
307096
  /*---------------------------------------------------------------------------------------------
306712
307097
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
306713
307098
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -306716,6 +307101,7 @@ __webpack_require__.r(__webpack_exports__);
306716
307101
  * @module Quantity
306717
307102
  */
306718
307103
 
307104
+
306719
307105
  /** A ParserSpec holds information needed to parse a string into a quantity synchronously.
306720
307106
  * @beta
306721
307107
  */
@@ -306741,6 +307127,51 @@ class ParserSpec {
306741
307127
  get outUnit() { return this._outUnit; }
306742
307128
  get azimuthBaseConversion() { return this._azimuthBaseConversion; }
306743
307129
  get revolutionConversion() { return this._revolutionConversion; }
307130
+ /** Build conversion specs for ratio format with 2 composite units (numerator/denominator). */
307131
+ static async getRatioUnitConversions(units, unitsProvider, outUnit, altUnitLabelsProvider) {
307132
+ const conversions = [];
307133
+ const [numeratorUnit, numeratorLabel] = units[0];
307134
+ const [denominatorUnit, denominatorLabel] = units[1];
307135
+ // Compute ratio scale: how many numerator units per denominator unit (e.g., IN:FT = 12)
307136
+ const denominatorToNumerator = await unitsProvider.getConversion(denominatorUnit, numeratorUnit);
307137
+ const displayRatioScale = denominatorToNumerator.factor;
307138
+ // Avoid double-scaling: if persistence unit already encodes the display ratio, use factor 1.
307139
+ // Check by name heuristic (e.g., IN_PER_FT with ratioUnits [IN, FT] → no scaling needed)
307140
+ const persistenceName = outUnit.name.toUpperCase();
307141
+ const numName = numeratorUnit.name.toUpperCase().split(".").pop() ?? "";
307142
+ const denName = denominatorUnit.name.toUpperCase().split(".").pop() ?? "";
307143
+ // Split by word boundaries (underscores, dots) and check for exact token matches
307144
+ const persistenceTokens = persistenceName.split(/[._]/);
307145
+ const isPersistenceMatchingRatio = persistenceTokens.includes(numName) && persistenceTokens.includes(denName);
307146
+ const ratioScaleFactor = isPersistenceMatchingRatio ? 1.0 : displayRatioScale;
307147
+ // First conversion spec: effective ratio unit conversion
307148
+ const ratioConversionSpec = {
307149
+ name: `${numeratorUnit.name}_per_${denominatorUnit.name}`,
307150
+ label: "",
307151
+ system: numeratorUnit.system,
307152
+ conversion: { factor: ratioScaleFactor, offset: 0.0 },
307153
+ };
307154
+ conversions.push(ratioConversionSpec);
307155
+ // Numerator unit for label lookup
307156
+ const numeratorSpec = {
307157
+ name: numeratorUnit.name,
307158
+ label: numeratorLabel?.length ? numeratorLabel : numeratorUnit.label,
307159
+ system: numeratorUnit.system,
307160
+ conversion: { factor: 1.0, offset: 0.0 },
307161
+ parseLabels: altUnitLabelsProvider?.getAlternateUnitLabels(numeratorUnit),
307162
+ };
307163
+ conversions.push(numeratorSpec);
307164
+ // Denominator unit for label lookup
307165
+ const denominatorSpec = {
307166
+ name: denominatorUnit.name,
307167
+ label: denominatorLabel?.length ? denominatorLabel : denominatorUnit.label,
307168
+ system: denominatorUnit.system,
307169
+ conversion: { factor: 1.0, offset: 0.0 },
307170
+ parseLabels: altUnitLabelsProvider?.getAlternateUnitLabels(denominatorUnit),
307171
+ };
307172
+ conversions.push(denominatorSpec);
307173
+ return conversions;
307174
+ }
306744
307175
  /** 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
306745
307176
  * 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
306746
307177
  * async calls to lookup unit definitions.
@@ -306749,7 +307180,14 @@ class ParserSpec {
306749
307180
  * @param outUnit The unit a value will be formatted to. This unit is often referred to as persistence unit.
306750
307181
  */
306751
307182
  static async create(format, unitsProvider, outUnit, altUnitLabelsProvider) {
306752
- const conversions = await _Parser__WEBPACK_IMPORTED_MODULE_0__.Parser.createUnitConversionSpecsForUnit(unitsProvider, outUnit, altUnitLabelsProvider);
307183
+ let conversions;
307184
+ // For ratio formats with 2 composite units, use private helper method
307185
+ if (format.type === _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_0__.FormatType.Ratio && format.units && format.units.length === 2) {
307186
+ conversions = await ParserSpec.getRatioUnitConversions(format.units, unitsProvider, outUnit, altUnitLabelsProvider);
307187
+ }
307188
+ else {
307189
+ conversions = await _Parser__WEBPACK_IMPORTED_MODULE_1__.Parser.createUnitConversionSpecsForUnit(unitsProvider, outUnit, altUnitLabelsProvider);
307190
+ }
306753
307191
  const spec = new ParserSpec(outUnit, format, conversions);
306754
307192
  if (format.azimuthBaseUnit !== undefined) {
306755
307193
  if (outUnit !== undefined) {
@@ -306771,7 +307209,7 @@ class ParserSpec {
306771
307209
  }
306772
307210
  /** Do the parsing. Done this way to allow Custom Parser Specs to parse custom formatted strings into their quantities. */
306773
307211
  parseToQuantityValue(inString) {
306774
- return _Parser__WEBPACK_IMPORTED_MODULE_0__.Parser.parseQuantityString(inString, this);
307212
+ return _Parser__WEBPACK_IMPORTED_MODULE_1__.Parser.parseQuantityString(inString, this);
306775
307213
  }
306776
307214
  }
306777
307215
 
@@ -306969,6 +307407,7 @@ __webpack_require__.r(__webpack_exports__);
306969
307407
  /* harmony export */ QuantityConstants: () => (/* reexport safe */ _Constants__WEBPACK_IMPORTED_MODULE_0__.QuantityConstants),
306970
307408
  /* harmony export */ QuantityError: () => (/* reexport safe */ _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError),
306971
307409
  /* harmony export */ QuantityStatus: () => (/* reexport safe */ _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus),
307410
+ /* harmony export */ RatioFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.RatioFormatType),
306972
307411
  /* harmony export */ RatioType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.RatioType),
306973
307412
  /* harmony export */ ScientificType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.ScientificType),
306974
307413
  /* harmony export */ ShowSignOption: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.ShowSignOption),
@@ -306987,6 +307426,7 @@ __webpack_require__.r(__webpack_exports__);
306987
307426
  /* harmony export */ parseFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseFormatType),
306988
307427
  /* harmony export */ parseFractionalPrecision: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseFractionalPrecision),
306989
307428
  /* harmony export */ parsePrecision: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parsePrecision),
307429
+ /* harmony export */ parseRatioFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseRatioFormatType),
306990
307430
  /* harmony export */ parseRatioType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseRatioType),
306991
307431
  /* harmony export */ parseScientificType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseScientificType),
306992
307432
  /* harmony export */ parseShowSignOption: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseShowSignOption),
@@ -320696,7 +321136,7 @@ var loadLanguages = instance.loadLanguages;
320696
321136
  /***/ ((module) => {
320697
321137
 
320698
321138
  "use strict";
320699
- 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"}}');
321139
+ 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"}}');
320700
321140
 
320701
321141
  /***/ })
320702
321142