iForge EDP Values |

Engineering Parameters Done Right

Most engineering apps store a number and a unit string and call it done. When the user switches units, the display breaks. When precision differs between disciplines, values get corrupted. When the same number needs to appear in a table, a tooltip, and an input field, three different formatting paths diverge and drift apart.

This library solves that. Every value carries three layers - the original input the user typed, a canonical internal representation in the quantity's base unit, and the full set of output rules that control how it is displayed and edited. None of those layers are ever thrown away.

  • Structured support for 12 quantity types: bool ("Yes" / "No", "Active" / "Inactive", and 11 other label presets), count (un), length (decimal and fractional inch output), area, volume, angle, temperature, mass, force, pressure, time, and ratio
  • Precision automatically capped at the quantity's internal resolution - no silent rounding surprises
  • Separate display and edit formatting modes - clean numbers in inputs, annotated strings in labels
  • Formula input with = prefix - arithmetic expressions and embedded units evaluated at creation time, formula preserved for edit round-trips
  • Pure ES module, no build step, no framework dependency - drop it into any JavaScript environment
  • Built on Math.js for unit conversion; this library owns the domain rules on top

Pick a quantity in the nav to try samples and output presets interactively. This demo stays focused on the browser experience for the live library.

Core Idea

One Value, Three Layers

Every engineering value produced by this library carries three layers of information at once. None of them are thrown away.

Input

What the user typed and in which unit - or a formula prefixed with =. Preserved exactly as entered so the UI can round-trip it faithfully.

// plain numbers, same internal value
{ value: 2002,  unit: "mm" }
{ value: 200.2, unit: "cm" }
{ value: 2.002, unit: "m"  }

// formula - evaluated at creation time
{ value: "=7200 mm + 150 mm" }
{ value: "=sqrt(3^2 + 4^2) m" }
Internal

The canonical form used by geometry, formulas, and interoperability. Always in the system's base unit for that quantity.

// always normalized internally
internal.value = 2.002
internal.unit  = "m"
Output

What the user sees - formatted with the chosen unit, precision, prefix, suffix, and display rules.

// one value, many display forms
display(mm, p0) → "2002 mm"
display(m,  p2) → "2.00 m"
display(cm, p1) → "200.2 cm"
edit(mm)        → "2002.0"

Architecture

Two Layers of Responsibility

The library divides concerns cleanly between Math.js and iForge EDP Values. Math.js handles the numeric heavy-lifting; this library owns the domain rules.

Math.js

Unit conversion, unit parsing, dimensional validation, numeric formatting.

import { unit, format } from "mathjs";

unit(2000, "mm").toNumber("m");
// 2

unit(90, "deg").toNumber("rad");
// 1.5707963267948966
iForge EDP Values

Value type, quantity, internal unit per quantity, normalization, internal resolution, max precision, read/edit formatting, display rules.

import { createValue, formatDisplayValue }
  from "iforge-edp-values";

const v = createValue({
  value: 2002,
  valueType: "number",
  quantity: "length",
  unit: "mm"
});

formatDisplayValue(v, { unit: "m", precision: 0 });
// "2 m"

Full Example

From Input to Output

The entire round-trip from user input to formatted display and edit value. The value is stored internally in SI (meters). Precision is always controlled by the caller - the library never silently rounds or caps what you request.

import {
  createValue,
  formatDisplayValue,
  formatEditValue
} from "iforge-edp-values";


const value = createValue({
  value: 2002,
  valueType: "number",
  quantity: "length",
  unit: "mm"
});

console.log(value.input);
// { value: 2002, unit: "mm" }

console.log(value.internal);
// { value: 2.002, unit: "m" }  ← stored in SI

console.log(formatDisplayValue(value, { unit: "m", precision: 0 }));
// "2 m"

console.log(formatDisplayValue(value, { unit: "mm", precision: 1 }));
// "2002.0 mm"

console.log(formatEditValue(value, { unit: "mm", precision: 4 }));
// "2002.0000"  ← precision honored exactly as requested

Beyond Length

Bool, Count, and Fractional Inch

The library handles more than continuous quantities. Bool, count, and fractional inch each follow the same createValue + formatDisplayValue pattern. For bool, pair with buildOutput to select a label preset.

Bool - labeled display

Value type "boolean". Internal value is true / false. Choose from 13 label presets for display.

// Build output with a label preset
const { output } = buildOutput({ presetId: "bool:open-closed" });

// Bind the label preset at creation time
const v = createValue({
  value: 1,
  valueType: "boolean",
  quantity: "bool",
  unit: "bool",
  output
});

v.internal
// { value: true, unit: "bool" }

formatDisplayValue(v);
// "Open"

// 13 label pairs available:
// bool:yes-no → "Yes" / "No"
// bool:active-inactive → "Active" / "Inactive"
// bool:compliant-non-compliant → "Compliant" / …
// bool:approved-not-approved → "Approved" / …

formatEditValue(v, { unit: "bool" });
// "1"
Count - discrete units

Value type "number", internal unit "un". Whole-number count with optional custom suffix for parts or components.

// Create from user input
const v = createValue({
  value: 5,
  valueType: "number",
  quantity: "count",
  unit: "un"
});

v.internal
// { value: 5, unit: "un" }

// Default display
formatDisplayValue(v, { unit: "un", precision: 0 });
// "5 un"

// Custom suffix for components
formatDisplayValue(v, {
  unit: "un", precision: 0,
  showUnit: false,
  suffix: " pcs",
  suffixMode: "custom"
});
// "5 pcs"

formatEditValue(v, { unit: "un" });
// "5"
Fractional inch

Length values in inches support decimal and fraction string input ("3 1/2"). Display as decimal or with a configurable fraction format.

// Decimal input
const v = createValue({
  value: 3.5,
  valueType: "number",
  quantity: "length",
  unit: "in"
});

v.internal
// { value: 0.0889, unit: "m" }

// Decimal display
formatDisplayValue(v, { unit: "in", precision: 2 });
// "3.50 in"

// Fraction string input also works:
// createValue({ value: "3 1/2", ... })
// createValue({ value: "1/4", ... })
// createValue({ value: "1/2", ... })

formatEditValue(v, { unit: "in" });
// "3.50"

Read vs Edit

Two Distinct Formatting Modes

Display mode produces a human-readable string with unit, prefix, and suffix. Edit mode produces only the number - the unit appears in the field label or adornment.

formatDisplayValue - read mode

Used in labels, tables, annotations, and read-only fields. May include prefix, suffix, and unit symbol.

formatDisplayValue(v, { unit: "kPa", precision: 1 });
// "250.0 kPa"

formatDisplayValue(v, { unit: "bar", prefix: "~ ", precision: 2 });
// "~ 2.50 bar"

formatDisplayValue(v, { unit: "MPa", showUnit: false });
// "0"
formatEditValue - edit mode

Used inside editable inputs. Always returns only the number. Unit must be shown separately.

formatEditValue(v, { unit: "mm" });
// "2002.0"

formatEditValue(v, { unit: "m" });
// "2.0020"

// Never: "2002.0 mm" ← wrong for an input field

Formula Input

Expressions with = Prefix

Any value field accepts a formula prefixed with =. The expression is evaluated at creation time using the same Math.js engine that powers unit conversion, so embedded unit tokens work naturally. The formula string is preserved in the input layer and returned by formatEditValue, so the user always sees their expression - not a recomputed number.

Plain arithmetic formula

No embedded units. The result is a plain number; the unit parameter provides the quantity context.

// =expression evaluated, unit applied separately
const v = createValue({
  value: "=1000 + 1000",
  unit: "mm",
  quantity: "length"
});

v.internal   // { value: 2, unit: "m" }

formatDisplayValue(v, { unit: "mm", precision: 0 });
// "2000 mm"

formatEditValue(v, { unit: "mm" });
// "=1000 + 1000"  ← formula preserved
Embedded-unit formula

Unit tokens inside the expression. The parser resolves unit mismatches and converts to the quantity's base unit automatically.

// mixed units inside the expression
const v = createValue({
  value: "=7200 mm + 150 mm",
  quantity: "length"
});

v.internal   // { value: 7.35, unit: "m" }

formatDisplayValue(v, { unit: "mm", precision: 0 });
// "7350 mm"

// more examples
"=sqrt(3^2 + 4^2) m"    // → 5 m
"=pi * (0.05 m)^2"      // → 0.00785… m²
"=45 deg + 30 deg"      // → 75° (stored in rad)
"=1 kN + 500 N"         // → 1500 N
"=1 bar + 50 kPa"       // → 150 000 Pa
// Supported operators and functions
// Arithmetic  : +  -  *  /  ^
// Math        : sqrt  abs  ceil  floor  round  log  log10  exp  min  max
// Trig        : sin  cos  tan  asin  acos  atan  atan2
// Constants   : pi  e  phi

const beam = createValue({ value: "=sqrt(3^2 + 4^2) m", quantity: "length" });
beam.internal;   // { value: 5, unit: "m" }

const slab = createValue({ value: "=450 m^2 * 0.2 m", quantity: "volume" });
slab.internal;   // { value: 90, unit: "m^3" }

const ramp = createValue({ value: "=atan(1.5 / 4) rad", quantity: "angle" });
ramp.internal;   // { value: 0.3588…, unit: "rad" }   (~20.6°)

Supported Quantities

Internal Units Per Quantity

Each quantity has a fixed internal unit. All calculations, geometry, and interoperability must use internal.value with this unit - never the user's typed text.

Quantity Internal unit Common display units
Bool bool true/false with label presets
Count un un (unit count)
Length m mm, cm, m, in - decimal and fractional inch output
Area cm², m², in²
Volume cm³, m³, in³, L
Angle rad deg, rad
Temperature °C °C, °F, K
Mass kg kg
Force N N, kN
Pressure Pa Pa, kPa, MPa, bar
Time s s, min, h
Ratio % % (0–100 scale, stored as-is)

Key Rules

The Non-Negotiables

Never calculate with the user's text

Always use internal.value and internal.unit for geometry, formulas, and interoperability.

Dot is the only decimal separator

10.5 is accepted. 10,5 is rejected. Avoids ambiguity in JavaScript, JSON, Math.js, and APIs.

Unit preference belongs to the UI

Stored values have no preferred display unit. That choice lives in the user preference, project config, or property definition.

Edit mode shows only the number

The unit appears in the label or adornment - never inside the <input> value itself.

Precision is always under your control

The library honors exactly the precision you request. Use output presets to enforce consistent precision per property type across the UI.

Input is preserved, not discarded

The original text, value, and unit typed by the user are stored alongside the internal form - always available for display round-trips.

Property Example

Wiring to a Parametric Family

The typical integration pattern: a property definition drives how values are created and displayed. Unit preferences live on the property or the user config - not inside the stored value.

const outsideDiameterProperty = {
  id: "outsideDiameter",
  name: "Outside Diameter",
  valueType: "number",
  quantity: "length",
  defaultInputUnit: "mm",

  display: { unit: "mm", precision: 1 }
};

// User types "200.25" in mm
const value = createValue({
  value: 200.25,
  valueType: outsideDiameterProperty.valueType,
  quantity: outsideDiameterProperty.quantity,
  unit: outsideDiameterProperty.defaultInputUnit
});


// Internal value: full precision stored in SI
console.log(value.internal);
// { value: 0.20025, unit: "m" }
// → 200.25 mm preserved exactly

// Read mode: precision comes from the display config
console.log(formatDisplayValue(value, outsideDiameterProperty.display));
// "200.3 mm"  ← p1 rounds for display only

// Edit mode - clean number, unit shown in the label separately
console.log(formatEditValue(value, outsideDiameterProperty.display));
// "200.3"