fix(measurements): Update the calibration tool to match changes in CS3D (#3505)
This change makes the OHIF side consistent with the CS3D user calibration settings, and will correctly display px and rounding values consistent with the CS3D display.
This commit is contained in:
parent
bd7f9592ea
commit
38af3112ec
@ -76,11 +76,12 @@ export function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
|
||||
|
||||
const adjustCalibration = newLength => {
|
||||
const spacingScale = newLength / length;
|
||||
const rowSpacing = spacingScale * currentRowPixelSpacing;
|
||||
const colSpacing = spacingScale * currentColumnPixelSpacing;
|
||||
|
||||
// trigger resize of the viewport to adjust the world/pixel mapping
|
||||
calibrateImageSpacing(imageId, viewport.getRenderingEngine(), rowSpacing, colSpacing);
|
||||
calibrateImageSpacing(imageId, viewport.getRenderingEngine(), {
|
||||
type: 'User',
|
||||
scale: 1 / spacingScale,
|
||||
});
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@ -99,8 +99,7 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { length, width } = targetStats;
|
||||
const unit = 'mm';
|
||||
const { length, width, unit } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@ -130,9 +129,9 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:Bidirectional');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { length, width } = annotation;
|
||||
columns.push(`Length (mm)`, `Width (mm)`);
|
||||
values.push(length, width);
|
||||
const { length, width, unit } = annotation;
|
||||
columns.push(`Length`, `Width`, 'Unit');
|
||||
values.push(length, width, unit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@ -162,7 +161,14 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { length, width, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const {
|
||||
length,
|
||||
width,
|
||||
unit,
|
||||
SeriesNumber,
|
||||
SOPInstanceUID,
|
||||
frameNumber,
|
||||
} = mappedAnnotations[0];
|
||||
const roundedLength = utils.roundNumber(length, 2);
|
||||
const roundedWidth = utils.roundNumber(width, 2);
|
||||
|
||||
@ -176,8 +182,10 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
|
||||
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
|
||||
|
||||
displayText.push(`L: ${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
|
||||
displayText.push(`W: ${roundedWidth} mm`);
|
||||
displayText.push(
|
||||
`L: ${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`
|
||||
);
|
||||
displayText.push(`W: ${roundedWidth} ${unit}`);
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
|
||||
const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@ -111,6 +111,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
stdDev,
|
||||
max,
|
||||
area,
|
||||
areaUnit,
|
||||
});
|
||||
});
|
||||
|
||||
@ -131,14 +132,20 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:CircleROI');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { mean, stdDev, max, area, unit } = annotation;
|
||||
const { mean, stdDev, max, area, unit, areaUnit } = annotation;
|
||||
|
||||
if (!mean || !unit || !max || !area) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
|
||||
values.push(max, mean, stdDev, area);
|
||||
columns.push(
|
||||
`max (${unit})`,
|
||||
`mean (${unit})`,
|
||||
`std (${unit})`,
|
||||
'Area',
|
||||
'Unit'
|
||||
);
|
||||
values.push(max, mean, stdDev, area, areaUnit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@ -168,7 +175,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const { area, SOPInstanceUID, frameNumber, areaUnit } = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
@ -182,7 +189,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
|
||||
// Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
|
||||
const roundedArea = utils.roundNumber(area || 0, 2);
|
||||
displayText.push(`${roundedArea} mm<sup>2</sup>`);
|
||||
displayText.push(`${roundedArea} ${areaUnit}`);
|
||||
|
||||
// Todo: we need a better UI for displaying all these information
|
||||
mappedAnnotations.forEach(mappedAnnotation => {
|
||||
|
||||
@ -98,7 +98,7 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
|
||||
const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@ -107,6 +107,7 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
frameNumber,
|
||||
Modality,
|
||||
unit: modalityUnit,
|
||||
areaUnit,
|
||||
mean,
|
||||
stdDev,
|
||||
max,
|
||||
@ -131,14 +132,20 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:EllipticalROI');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { mean, stdDev, max, area, unit } = annotation;
|
||||
const { mean, stdDev, max, area, unit, areaUnit } = annotation;
|
||||
|
||||
if (!mean || !unit || !max || !area) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
|
||||
values.push(max, mean, stdDev, area);
|
||||
columns.push(
|
||||
`max (${unit})`,
|
||||
`mean (${unit})`,
|
||||
`std (${unit})`,
|
||||
'Area',
|
||||
'Unit'
|
||||
);
|
||||
values.push(max, mean, stdDev, area, areaUnit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@ -168,7 +175,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const { area, SOPInstanceUID, frameNumber, areaUnit } = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
@ -180,9 +187,8 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
|
||||
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
|
||||
|
||||
// Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
|
||||
const roundedArea = utils.roundNumber(area || 0, 2);
|
||||
displayText.push(`${roundedArea} mm<sup>2</sup>`);
|
||||
const roundedArea = utils.roundNumber(area, 2);
|
||||
displayText.push(`${roundedArea} ${areaUnit}`);
|
||||
|
||||
// Todo: we need a better UI for displaying all these information
|
||||
mappedAnnotations.forEach(mappedAnnotation => {
|
||||
|
||||
@ -32,7 +32,11 @@ const Length = {
|
||||
throw new Error('Tool not supported');
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
} = getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
cornerstoneViewportService,
|
||||
viewportId
|
||||
@ -94,8 +98,11 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
throw new Error('Non-acquisition plane measurement mapping not supported');
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
|
||||
getSOPInstanceAttributes(referencedImageId);
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
frameNumber,
|
||||
} = getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
@ -104,8 +111,7 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { length } = targetStats;
|
||||
const unit = 'mm';
|
||||
const { length, unit = 'mm' } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@ -134,9 +140,11 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:Length');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { length } = annotation;
|
||||
columns.push(`Length (mm)`);
|
||||
const { length, unit } = annotation;
|
||||
columns.push(`Length`);
|
||||
values.push(length);
|
||||
columns.push('Unit');
|
||||
values.push(unit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@ -166,7 +174,13 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { length, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const {
|
||||
length,
|
||||
SeriesNumber,
|
||||
SOPInstanceUID,
|
||||
frameNumber,
|
||||
unit,
|
||||
} = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
@ -182,7 +196,9 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
return displayText;
|
||||
}
|
||||
const roundedLength = utils.roundNumber(length, 2);
|
||||
displayText.push(`${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
|
||||
displayText.push(
|
||||
`${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`
|
||||
);
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
@ -25,7 +25,11 @@ const RectangleROI = {
|
||||
throw new Error('Tool not supported');
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
} = getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
CornerstoneViewportService,
|
||||
viewportId
|
||||
@ -88,8 +92,11 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
throw new Error('Non-acquisition plane measurement mapping not supported');
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
|
||||
getSOPInstanceAttributes(referencedImageId);
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
frameNumber,
|
||||
} = getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
@ -98,7 +105,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
|
||||
const { mean, stdDev, max, area, Modality, modalityUnit, areaUnit } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@ -111,6 +118,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
stdDev,
|
||||
max,
|
||||
area,
|
||||
areaUnit,
|
||||
});
|
||||
});
|
||||
|
||||
@ -131,14 +139,14 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:RectangleROI');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { mean, stdDev, max, area, unit } = annotation;
|
||||
const { mean, stdDev, max, area, unit, areaUnit } = annotation;
|
||||
|
||||
if (!mean || !unit || !max || !area) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
|
||||
values.push(max, mean, stdDev, area);
|
||||
columns.push(`Maximum`, `Mean`, `Std Dev`, 'Pixel Unit', `Area`, 'Unit');
|
||||
values.push(max, mean, stdDev, unit, area, areaUnit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@ -168,7 +176,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const { area, SOPInstanceUID, frameNumber, areaUnit } = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
@ -182,7 +190,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
|
||||
// Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
|
||||
const roundedArea = utils.roundNumber(area || 0, 2);
|
||||
displayText.push(`${roundedArea} mm<sup>2</sup>`);
|
||||
displayText.push(`${roundedArea} ${areaUnit}`);
|
||||
|
||||
// Todo: we need a better UI for displaying all these information
|
||||
mappedAnnotations.forEach(mappedAnnotation => {
|
||||
|
||||
@ -68,7 +68,7 @@ class MetadataProvider {
|
||||
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (frameNumber && combineFrameInstance(frameNumber, instance)) || instance;
|
||||
}
|
||||
@ -114,8 +114,20 @@ class MetadataProvider {
|
||||
return this._getCornerstoneDICOMImageLoaderTag(naturalizedTagOrWADOImageLoaderTag, instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new handler for the given tag. The handler will be provided an
|
||||
* instance object that it can read values from.
|
||||
*/
|
||||
public addHandler(
|
||||
wadoImageLoaderTag: string,
|
||||
handler
|
||||
) {
|
||||
WADO_IMAGE_LOADER[wadoImageLoaderTag] = handler;
|
||||
}
|
||||
|
||||
_getCornerstoneDICOMImageLoaderTag(wadoImageLoaderTag, instance) {
|
||||
let metadata;
|
||||
let metadata = WADO_IMAGE_LOADER[wadoImageLoaderTag]?.(instance);
|
||||
if (metadata) return metadata;
|
||||
|
||||
switch (wadoImageLoaderTag) {
|
||||
case WADO_IMAGE_LOADER_TAGS.GENERAL_SERIES_MODULE:
|
||||
@ -153,45 +165,6 @@ class MetadataProvider {
|
||||
patientSex: instance.PatientSex,
|
||||
};
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.IMAGE_PLANE_MODULE:
|
||||
const { ImageOrientationPatient } = instance;
|
||||
|
||||
// Fallback for DX images.
|
||||
// TODO: We should use the rest of the results of this function
|
||||
// to update the UI somehow
|
||||
const { PixelSpacing } = getPixelSpacingInformation(instance);
|
||||
|
||||
let rowPixelSpacing;
|
||||
let columnPixelSpacing;
|
||||
|
||||
let rowCosines;
|
||||
let columnCosines;
|
||||
|
||||
if (PixelSpacing) {
|
||||
rowPixelSpacing = PixelSpacing[0];
|
||||
columnPixelSpacing = PixelSpacing[1];
|
||||
}
|
||||
|
||||
if (ImageOrientationPatient) {
|
||||
rowCosines = ImageOrientationPatient.slice(0, 3);
|
||||
columnCosines = ImageOrientationPatient.slice(3, 6);
|
||||
}
|
||||
|
||||
metadata = {
|
||||
frameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||
rows: toNumber(instance.Rows),
|
||||
columns: toNumber(instance.Columns),
|
||||
imageOrientationPatient: toNumber(ImageOrientationPatient),
|
||||
rowCosines: toNumber(rowCosines || [0, 1, 0]),
|
||||
columnCosines: toNumber(columnCosines || [0, 0, -1]),
|
||||
imagePositionPatient: toNumber(instance.ImagePositionPatient || [0, 0, 0]),
|
||||
sliceThickness: toNumber(instance.SliceThickness),
|
||||
sliceLocation: toNumber(instance.SliceLocation),
|
||||
pixelSpacing: toNumber(PixelSpacing || 1),
|
||||
rowPixelSpacing: toNumber(rowPixelSpacing || 1),
|
||||
columnPixelSpacing: toNumber(columnPixelSpacing || 1),
|
||||
};
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.IMAGE_PIXEL_MODULE:
|
||||
metadata = {
|
||||
samplesPerPixel: toNumber(instance.SamplesPerPixel),
|
||||
@ -484,11 +457,54 @@ const metadataProvider = new MetadataProvider();
|
||||
|
||||
export default metadataProvider;
|
||||
|
||||
const WADO_IMAGE_LOADER = {
|
||||
imagePlaneModule: instance => {
|
||||
const { ImageOrientationPatient } = instance;
|
||||
|
||||
// Fallback for DX images.
|
||||
// TODO: We should use the rest of the results of this function
|
||||
// to update the UI somehow
|
||||
const { PixelSpacing } = getPixelSpacingInformation(instance);
|
||||
|
||||
let rowPixelSpacing;
|
||||
let columnPixelSpacing;
|
||||
|
||||
let rowCosines;
|
||||
let columnCosines;
|
||||
|
||||
if (PixelSpacing) {
|
||||
rowPixelSpacing = PixelSpacing[0];
|
||||
columnPixelSpacing = PixelSpacing[1];
|
||||
}
|
||||
|
||||
if (ImageOrientationPatient) {
|
||||
rowCosines = ImageOrientationPatient.slice(0, 3);
|
||||
columnCosines = ImageOrientationPatient.slice(3, 6);
|
||||
}
|
||||
|
||||
return {
|
||||
frameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||
rows: toNumber(instance.Rows),
|
||||
columns: toNumber(instance.Columns),
|
||||
imageOrientationPatient: toNumber(ImageOrientationPatient),
|
||||
rowCosines: toNumber(rowCosines || [0, 1, 0]),
|
||||
columnCosines: toNumber(columnCosines || [0, 0, -1]),
|
||||
imagePositionPatient: toNumber(
|
||||
instance.ImagePositionPatient || [0, 0, 0]
|
||||
),
|
||||
sliceThickness: toNumber(instance.SliceThickness),
|
||||
sliceLocation: toNumber(instance.SliceLocation),
|
||||
pixelSpacing: toNumber(PixelSpacing || 1),
|
||||
rowPixelSpacing: rowPixelSpacing ? toNumber(rowPixelSpacing) : null,
|
||||
columnPixelSpacing: columnPixelSpacing ? toNumber(columnPixelSpacing) : null,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const WADO_IMAGE_LOADER_TAGS = {
|
||||
// dicomImageLoader specific
|
||||
GENERAL_SERIES_MODULE: 'generalSeriesModule',
|
||||
PATIENT_STUDY_MODULE: 'patientStudyModule',
|
||||
IMAGE_PLANE_MODULE: 'imagePlaneModule',
|
||||
IMAGE_PIXEL_MODULE: 'imagePixelModule',
|
||||
VOI_LUT_MODULE: 'voiLutModule',
|
||||
MODALITY_LUT_MODULE: 'modalityLutModule',
|
||||
@ -1,9 +1,34 @@
|
||||
/**
|
||||
* @param {string | number} value
|
||||
* @param {number} decimals
|
||||
* Truncates decimal points to that there is at least 1+precision significant
|
||||
* digits.
|
||||
*
|
||||
* For example, with the default precision 2 (3 significant digits)
|
||||
* * Values larger than 100 show no information after the decimal point
|
||||
* * Values between 10 and 99 show 1 decimal point
|
||||
* * Values between 1 and 9 show 2 decimal points
|
||||
*
|
||||
* @param value - to return a fixed measurement value from
|
||||
* @param precision - defining how many digits after 1..9 are desired
|
||||
*/
|
||||
function _round(value, decimals) {
|
||||
return Number(value).toFixed(decimals);
|
||||
function roundNumber(value: string | number, precision = 2): string {
|
||||
if (value === undefined || value === null || value === '') return 'NaN';
|
||||
value = Number(value);
|
||||
if (value < 0.0001) return `${value}`;
|
||||
const fixedPrecision =
|
||||
value >= 100
|
||||
? precision - 2
|
||||
: value >= 10
|
||||
? precision - 1
|
||||
: value >= 1
|
||||
? precision
|
||||
: value >= 0.1
|
||||
? precision + 1
|
||||
: value >= 0.01
|
||||
? precision + 2
|
||||
: value >= 0.001
|
||||
? precision + 3
|
||||
: precision + 4;
|
||||
return value.toFixed(fixedPrecision);
|
||||
}
|
||||
|
||||
export default _round;
|
||||
export default roundNumber;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user