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.
{ value: 2002, unit: "mm" }
{ value: 200.2, unit: "cm" }
{ value: 2.002, unit: "m" }
{ 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.
internal.value = 2.002
internal.unit = "m"
Output
What the user sees - formatted with the chosen unit, precision, prefix, suffix, and display rules.
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.
const { output } = buildOutput({ presetId: "bool:open-closed" });
const v = createValue({
value: 1,
valueType: "boolean",
quantity: "bool",
unit: "bool",
output
});
v.internal
// { value: true, unit: "bool" }
formatDisplayValue(v);
// "Open"
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.
const v = createValue({
value: 5,
valueType: "number",
quantity: "count",
unit: "un"
});
v.internal
// { value: 5, unit: "un" }
formatDisplayValue(v, { unit: "un", precision: 0 });
// "5 un"
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.
const v = createValue({
value: 3.5,
valueType: "number",
quantity: "length",
unit: "in"
});
v.internal
// { value: 0.0889, unit: "m" }
formatDisplayValue(v, { unit: "in", precision: 2 });
// "3.50 in"
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.
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.
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"
"=sqrt(3^2 + 4^2) m"
"=pi * (0.05 m)^2"
"=45 deg + 30 deg"
"=1 kN + 500 N"
"=1 bar + 50 kPa"
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.
Bool
bool
true/false with label presets
Count
un
un (unit count)
Length
m
mm, cm, m, in - decimal and fractional inch output
Area
m²
cm², m², in²
Volume
m³
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"