Skip to content

A lightweight JS library for converting between units of length

License

Notifications You must be signed in to change notification settings

floledermann/another-dimension

Repository files navigation

A lightweight library for converting units of length

another-dimension helps to convert between various units of length, with a focus on units used for screen presentation (physical screen pixels) and visual perception experiments (arcminutes, arcseconds).

Why another unit conversion library?

  • Lightweight, only supporting length units, no dependencies. If you are looking for general conversion of various units, consider js-quantities, convert-units, convert or others.
  • Support for angular dimensions (degrees, arcminutes and arcseconds) which depend on viewing distance and involve trigonometric calculations in the conversion.
  • Support for physical screen pixels taking into account the (configurable) pixel density.
  • Global configuration of pixel density and viewing distance for accurate conversion from/to physical screen pixels and angular length units, as often needed for accurate reproduction of perceptual experiments and user studies.
  • Modern-style, function-based, highly configurable implementation.
  • 100% test coverage.

another-dimension was created as part of the stimsrv project to support the accurate specification of dimensions for screen-based psychological experiments.


| — In a Nutshell — | — Installation & Import — | — API Documentation — | — Supported Units — | — Credits — |


another-dimension in a Nutshell

const Dimension = require('another-dimension');

// Simple use case: unit conversion

// Create some dimensions
let width  = Dimension("10mm");       // 10 mm
let length = Dimension("1.8in");      // 1.8 inch

console.log("Metric length: " + length.toString("mm"));
// => "Metric length: 45.72mm"

// Optional: Configuration of global settings
Dimension.configure({
  defaultOutputUnit: "px", // convert to pixels when value is used as Number
  defaultUnit: "px",       // default unit to use if no unit is specified
  pixelDensity: 440,       // 440ppi, pixel density of HiDPI smartphone 
                           //   (used to convert pixel sizes)
  viewingDistance: 350     // 350mm viewing distance (typical for smartphone use),
                           //   (used to convert angular dimensions)
});

// Create more dimensions
let height = Dimension(50, "arcmin"); // 50 angular minutes
let depth  = Dimension(50);           // 50 pixels (as per defaultUnit specified above)

// Dimension objects can be used in place of numeric primitives, 
// and will implicitly be converted to pixels (as configured above per defaultOutputUnit)
// This will draw a 173.2 x 88.2 pixel rectangle!
canvasContext2D.fillRect(0, 0, width, height);

// Dimension containing the length of the diagonal in pixels (set as defaultUnit above)
let diagonal = Dimension(Math.sqrt(width ** 2 + height ** 2));

// Ouptut diagonal length in mm, using 2 digits precision
console.log("Diagonal length: " + diagonal.toString("mm",2));
// => "Diagonal length: 11.22mm"

Installation & Import

Node.js

npm install another-dimension

CommonJS require() syntax:

const Dimension = require('another-dimension');

ES module import syntax:

import Dimension from 'another-dimension';

Browser

For direct use in the browser, download the file another-dimension.js and load it using a <script> tag.

<script src="another-dimension.js"></script>

<script>
let dim = Dimension("1in");
console.log(dim.toString("mm")); // -> 25.4mm
</script>

By default, another-dimension is made available as a global variable named Dimension. You can change the name of the global variable by adding an attribute data-another-dimension-global to the script tag, specifying the global name to use for the Dimension object.

<script src="another-dimension.js" data-another-dimension-global="AnotherDimension"></script>

<script>
let dim = AnotherDimension("1in");
console.log(dim.toString("mm")); // -> 25.4mm
</script>

Note: another-dimension works out of the box like this for prototyping in modern browsers. For compatibility with older browsers and optimized delivery consider compiling your project code, including this library, using Babel or similar tools.


API Documentation

Creating Dimensions

Dimension(spec[, options])

Can be used with new as constructor, or without as a factory function.

let lengthA = new Dimension("12mm");   // Constructor syntax

let lengthB = Dimension("12mm");       // Factory syntax

spec can be a Number, a String, or an Object providing a value and optional unit entry.

// String providing value + unit
let lengthA = Dimension("12mm");   

// Object providing value and (optionally) unit
let lengthB = Dimension({value: 12, unit: "mm"});

// Number will use default unit (initially "mm")
let lengthC = Dimension(12);       

options can be a String specifying the unit, or an object with some of the following entries:

options.defaultUnit Unit to use if not specified, overrides global default unit.

// specify value and unit separately
let lengthA = Dimension(12, "mm");   

// specify defaultUnit in options
let lengthB = Dimension(12, {defaultUnit: "in"});  // => 12 inches

// specified unit takes precedence over options.defaultUnit
let lengthC = Dimension("12mm", {defaultUnit: "in"});  // => 12 mm    

Retrieving and converting Dimensions

dimensionInstance.toDimension(targetUnit)

Returns a new Dimension instance, converted to the targetUnit.

let dim = Dimension("1in");

let dimMM = dim.toDimension("mm");  // Dimension with value: 25.4 and unit: "mm"

dimensionInstance.toNumber(targetUnit)

Returns the dimension converted to targetUnit, as a Number. If targetUnit is not specified, return the unconverted value.

let dim = Dimension("1in");

let mm = dim.toNumber("mm");  // 25.4
let inches = dim.toNumber();  // 1

dimensionInstance.toString([targetUnit], [digits])

Returns a String representation of the dimension (e.g. "12.1mm").

targetUnit Unit to convert to.

digits Number of digits after the comma to include.

let dim = Dimension("1.8in");

let mmStr = dim.toString("mm");       // "45.72mm"
let mmRound = dim.toString("mm", 1);  // "45.7mm"
let round = dim.toString(2);          // "1.80in"

dimensionInstance.toFixed([digits[, targetUnit]])

Returns a String representation of the numeric value of the dimension with fixed precision (e.g. "12.1"), similar to the JavaScript builtin `Number.toFixed().

digits Number of digits after the comma to include. If omitted, it is considered 0, i.e. rounding to full integer.

targetUnit Unit to convert to.

let dim = Dimension("1.8in");

let fixed = dim.toFixed(2);          // "1.80"
let mmFixed = dim.toFixed(1, "mm");  // "45.7"
let round = dim.toFixed();           // "2"

dimensionInstance.toJSON()

Returns a plain JavaScript object containing entries for value and unit, unless configured otherwise by setting the toJSON option with Dimension.configure().


dimensionInstance.valueOf()

Returns the numeric value of the dimension converted to the globally configured defaultOutputUnit, or the unconverted value if defaultOutputUnit has not been set.

This method is called internally by the JavaScript interpreter when a Dimension object is used in place of a primitive value, and is provided for this purpose. It should generally rarely be called explicitly (use dimensionInstance.toNumber(), dimensionInstance.toFixed() or dimensionInstance.toString() for better control over the output instead).


Using Dimensions as primitives

For Objects involved in numeric calculations, the JavaScript interpreter internally calls .valueOf() on the Object before performing the operation. By default, valueOf() of Dimension instances returns their (unconverted) numerical value. If the global option defaultOutputUnit is set, the value is converted to the specified unit first.

let dim = Dimension("1in");

console.log(dim + dim);  // 2

Dimension.configure({
  defaultOutputUnit: "mm"
});

console.log(dim + dim);  // 50.8

Global Configuration

Dimension.configure(options)

Set global configuration options.

options is an object containing global configuration options.

Option Default Description
defaultUnit "mm" Default unit to use when creating Dimension instances.
defaultOutputUnit null Unit to convert to when a Dimension instance is used as a primitive value. null uses the object's specified unit (so no conversion takes place).
anchorUnit "mm" Unit to try as intermediate unit when no direct conversion from source to target unit is available.
pixelDensity 96 Pixel density (in pixels-per-inch) to use for converting pixel values.
viewingDistance 600 Viewing distance (in mm) to use for converting angular dimensions. The default of 600mm is often used for "Desktop" settings, for mobile phones use 300-350mm.
aliases see Supported Units A key-value map of unit aliases, e.g. {'"': 'in'} to use the " character as an alias for inches. Warning: setting this here will overwrite the internal alias table. Use Dimension.addAlias() to add aliases to the internal alias table.
toJSON see above Function to use for .toJSON() of Dimension instances. Takes a Dimensions object as parameter, returns its JSON representation (plain JS Object).
dimensionRegEx see below The regular expression used to parse dimension Strings. Has to define two named groups value and unit, which will be used to extract the numeric value and unit specifier from the String.

Default config.dimensionRegEx: /^\s*(?<value>-?[0-9]*\.?[0-9]+)\s*(?<unit>[^\s\d]+)\s*$/

(This allows for padding whitespace, whitespace separating value and unit, and special characters (but no digits) in the unit specifier.)


Dimension.addConversion(fromUnit, toUnit, factorOrFunction)

Add a conversion, specified as a fixed conversion factor or a function.

fromUnit String specifying the unit to convert from.

toUnit String specifying the unit to convert to.

factorOrFunction either a Number, specifying a fixed conversion factor, or a Function that will be called for each conversion with the following parameters:

To introduce a new unit, you only need to supply a conversion to and from the anchorUnit (by default: "mm") as a bare minimum.

Example:
// (these conversions are already built in and serve only for illustration purposes)

// to convert from inch to mm, multiply with 25.4
Dimension.addConversion("in", "mm", 25.4);

// to convert from inches to pixels, multiply with config.pixelDensity
Dimension.addConversion("in", "px", (v, config) => v * config.pixelDensity);

Dimension.addAlias(unit, alias)

Add an alias (alternative name) for a unit. The aliases will be considered before any conversion. Warning: Aliases are not looked up recursively, so each alias has to refer to a unit which is actually specified (i.e. for which conversions are either built in or have been specified using Dimension.addConversion()).

unit A String specifying the base unit.

alias A String or an Array of Strings, specifying alias name(s).

Example:
// " is not configured as an alias for inches by default, as it may be confused with arcseconds.

// to use " as an alias for inches
Dimension.addAlias("in", '"');

let length = Dimension('12"');

console.log(length.toString());
// => 12in

Global Helpers

Dimension.getConversionFunction(fromUnit, toUnit[, options])

Return a specific conversion function. The returned function will accept a single parameter - the value to convert - and will return the converted value.

fromUnit String specifying the unit to convert from.

toUnit String specifying the unit to convert to.

options Object with some of the following entries:

options.freezeConfig: If set to true, the current configuration (e.g. pixelDensity, viewingDistance) will be "frozen", so that subsequent changes to those parameters won't affect the conversion function. If not set (the default), changes to the global configuration will affect the conversion performed by the returned function, if applicable.

let conv = Dimension.getConversionFunction("in", "mm");

let mm = conv(1);   // 25.4

Dimension.unAlias(unit)

Returns the canonical unit referenced by the given alias, or the unit passed in if no such alias is defined.

let unit = Dimension.unAlias("°");

console.log(unit);
// => "deg"

Dimension.parseDimensionString(dimensionString)

Parses a String into a plain JS object containing entries for value and unit, using the built-in or configured dimensionRegEx regular expression.

If the String contains no unit but a number, the defaultUnit is used as unit.

If neither dimensionRegEx nor number match, the function returns null.


Dimension.getUnits()

Returns an Array containing all currently configured Dimensions (without aliases).


Supported Units

The list of built-in units is deliberately kept short. New units can be added quickly by providing conversion functions or factors (at minimum to and from the global base unit config.anchorUnit) using Dimension.addConversion()

Metric Units

Unit / Aliases Description mm
km Kilometer 10^6
m Meter 10^3
cm Centimeter 10
mm Millimeter 1
µ / µm / um Micrometer 10^-3

Imperial Units

Unit / Aliases Description mm
in Inch 25.4
thou Thousandths of an Inch 0.0254

Typesetting Units

Unit / Aliases Description mm
pc (DTP) Pica (1/6in) ≈4.23
pt (DTP) Point (1/12pc) ≈0.353
twip Twip (1/20pt) ≈0.0176

Screen Units

Unit / Aliases Description mm
px Physical Screen Pixel Varying, depending on config.pixelDensity (≈0.265mm @ 96dpi)

Angular Units

Unit / Aliases Description mm
deg / ° Angular Degree Varying, depending on config.viewingDistance (≈10.5mm @ 600mm)
arcmin Arc Minute Varying, depending on config.viewingDistance (≈0.175mm @ 600mm)
arcsec Arc Second Varying, depending on config.viewingDistance (≈0.003mm @ 600mm)

Test coverage report

File % Stmts % Branch % Funcs % Lines
another-dimension.js 100 100 100 100

Credits

another-dimension was created by Florian Ledermann as part of the stimsrv project.

License: MIT License.

"I'll take your brains to another dimension ... pay close attention!" — In memoriam Keith Flint / The Prodigy