From 5da75aa7293ef03ff3b28869097f9b0636e8160e Mon Sep 17 00:00:00 2001 From: tizayi <57833318+tizayi@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:37:33 +0000 Subject: [PATCH 1/3] using unit with the requested range --- package-lock.json | 2 +- src/plot/centrePlot.tsx | 14 +++----------- src/results/scatteringQuantities.ts | 18 ++++++++++-------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 253a2f4..dd942e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5943,7 +5943,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/src/plot/centrePlot.tsx b/src/plot/centrePlot.tsx index 35a0501..7bf1327 100644 --- a/src/plot/centrePlot.tsx +++ b/src/plot/centrePlot.tsx @@ -135,21 +135,13 @@ export default function CentrePlot(): JSX.Element { (value: number): number => { switch (state.requested) { case ScatteringOptions.d: - if (state.dUnits === WavelengthUnits.angstroms) { - value = angstroms2Nanometres(value); - } - value = convertBetweenQAndD(value); + value = convertBetweenQAndD(mathjs.unit(value,state.dUnits)); break; case ScatteringOptions.s: - if (state.sUnits === WavelengthUnits.angstroms) { - value = angstroms2Nanometres(value); - } - value = convertBetweenQAndS(value); + value = convertBetweenQAndS(mathjs.unit(value,state.sUnits)); break; default: - if (state.qUnits === ReciprocalWavelengthUnits.angstroms) { - value = nanometres2Angstroms(value); - } + value = unit(value,state.qUnits); } return value; }, diff --git a/src/results/scatteringQuantities.ts b/src/results/scatteringQuantities.ts index b708ce9..ad43fc7 100644 --- a/src/results/scatteringQuantities.ts +++ b/src/results/scatteringQuantities.ts @@ -1,15 +1,17 @@ -export const convertBetweenQAndS = (quantity: number): number => { - return 1 / quantity; +import * as mathjs from "mathjs"; + +export const convertBetweenQAndS = (quantity: mathjs.Unit): math.Unit => { + return mathjs.divide(1,quantity); }; -export const convertBetweenQAndD = (quantity: number): number => { - return (2 * Math.PI) / quantity; +export const convertBetweenQAndD = (quantity: mathjs.Unit): mathjs.Unit => { + return mathjs.divide( (2 * Math.PI) , quantity); }; -export const convertFromDTooS = (quantity: number): number => { - return quantity / (2 * Math.PI); +export const convertFromDTooS = (quantity: mathjs.Unit): mathjs.Unit => { + return mathjs.divide(quantity ,(2 * Math.PI)); }; -export const convertFromStooD = (quantity: number): number => { - return quantity * (2 * Math.PI); +export const convertFromStooD = (quantity: mathjs.Unit): mathjs.Unit => { + return mathjs.multiply(quantity ,(2 * Math.PI)); }; From cfca64aed414d264cf8a04cf81558b82cf07804c Mon Sep 17 00:00:00 2001 From: tizayi Date: Mon, 4 Dec 2023 09:47:15 +0000 Subject: [PATCH 2/3] units working the say i want --- src/calculations/numericRange.ts | 49 +-------- src/calculations/qrange.test.ts | 51 ---------- src/calculations/qvalue.ts | 24 +++-- src/calculations/unitRange.ts | 68 +++++++++++++ src/data-entry/beamlineconfigStore.ts | 2 +- src/plot/centrePlot.tsx | 141 ++++++++++++++++---------- src/plot/plotUtils.ts | 80 +++++++++++---- src/results/rangeDiagram.tsx | 21 ++-- src/results/rangeTable.tsx | 46 +++------ src/results/resultsBar.tsx | 45 ++++---- src/utils/units.test.ts | 54 ---------- src/utils/units.ts | 9 +- 12 files changed, 278 insertions(+), 312 deletions(-) delete mode 100644 src/calculations/qrange.test.ts create mode 100644 src/calculations/unitRange.ts delete mode 100644 src/utils/units.test.ts diff --git a/src/calculations/numericRange.ts b/src/calculations/numericRange.ts index 099e2a8..a411c42 100644 --- a/src/calculations/numericRange.ts +++ b/src/calculations/numericRange.ts @@ -1,6 +1,3 @@ -/** - * An object that stores a numeric range - */ export default class NumericRange { min: number; max: number; @@ -15,37 +12,20 @@ export default class NumericRange { this.min = temp; } } - /** - * Returns true if the input value falls inside the numeric range - * @param value a number - * @returns - */ + containsValue(value: number): boolean { return value >= this.min && value <= this.max; } - /** - * Returns true if another range falls inside the numeric range - * @param other another range - * @returns - */ containsRange(other: NumericRange): boolean { return other.min >= this.min && other.max <= this.max; } - /** - * Returns string represtaion of the numeric range - * @returns - */ + toString(): string { return `(min:${this.min}, max:${this.max})`; } - /** - * Returns the intersection of the numric range and another numeric range - * @param other another numeric range - * @returns the intersection of the two ranges - */ intersect(other: NumericRange | null): NumericRange | null { if (other === null) { return null; @@ -58,42 +38,23 @@ export default class NumericRange { ); } - /** - * Returns true if the neumeric ranges are equal - * @param other another numeric range - * @returns - */ + equals(other: NumericRange): boolean { return this.min === other.min && this.max === other.max; } - /** - * Returns a new numeric range with the function applied elementwise - * @param func A function to apply to both the min and max of the range - * @returns A new Numeric range with the call back applied - */ + apply(func: (value: number) => number): NumericRange { return new NumericRange(func(this.min), func(this.max)); } - /** - * Applys a function to the range in place - * @param func - * @returns - */ + inPlaceApply(func: (value: number) => number): NumericRange { this.min = func(this.min); this.max = func(this.max); return this; } - /** - * Create a new numeric range from a num max and a funtion - * @param min - * @param max - * @param func - * @returns - */ static createWithFunc( min: number, max: number, diff --git a/src/calculations/qrange.test.ts b/src/calculations/qrange.test.ts deleted file mode 100644 index 7790249..0000000 --- a/src/calculations/qrange.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { expect, test } from "vitest"; -import { computeQrange } from "./qrange"; -import { - BeamlineConfig, - Beamstop, - CircularDevice, - Detector, -} from "../utils/types"; - -test("Test computing q ranges", () => { - const detector: Detector = { - resolution: { - height: 1679, - width: 1475, - }, - pixelSize: { - height: 0.172, - width: 0.172, - }, - }; - const beamstop: Beamstop = { - centre: { x: 738, y: 840 }, - diameter: 4, - clearance: 10, - }; - const beamConfig: BeamlineConfig = { - angle: 1.57, - cameraLength: 1.9, - minWavelength: 6.2e-2, - maxWavelength: 0.335, - minCameraLength: 1.9, - maxCameraLength: 9.9, - wavelength: 0.09, - cameraLengthStep: 1, - }; - const cameraTube: CircularDevice = { - centre: { x: 738, y: 840 }, - diameter: 310, - }; - - const { ptMin, ptMax, visibleQRange, fullQRange } = computeQrange( - detector, - beamstop, - cameraTube, - beamConfig, - ); - expect(ptMin.y).toBeLessThan(ptMax.y); - expect(Math.abs(ptMin.x - ptMax.x)).toBeLessThan(1); - expect(fullQRange).toBeTruthy(); - expect(visibleQRange).toBeTruthy(); -}); diff --git a/src/calculations/qvalue.ts b/src/calculations/qvalue.ts index cbea528..2a59726 100644 --- a/src/calculations/qvalue.ts +++ b/src/calculations/qvalue.ts @@ -1,5 +1,7 @@ +import { UnitVector } from "../plot/plotUtils"; import { Ray } from "./ray"; import { Vector2 } from "three"; +import * as mathjs from "mathjs"; export const calculateQValue = ( distance: number, @@ -34,18 +36,18 @@ export const calculateDistanceFromQValue = ( }; export const getPointForQ = ( - qValue: number, - angle: number, - cameralength: number, - wavelength: number, - beamstopCentre: Vector2, -): Vector2 => { + qValue: math.Unit, + angle: math.Unit, + cameralength: math.Unit, + wavelength: math.Unit, + beamstopCentre: UnitVector, +): UnitVector => { const ray = new Ray( - new Vector2(Math.cos(angle), Math.sin(angle)), - beamstopCentre, + new Vector2(Math.cos(angle.toSI().toNumber()), Math.sin(angle.toSI().toNumber())), + new Vector2(beamstopCentre.x.toSI().toNumber(), beamstopCentre.y.toSI().toNumber()), ); - return ray.getPointAtDistance( - 1.0e3 * - (calculateDistanceFromQValue(qValue, cameralength, wavelength) ?? 0), + const result = ray.getPointAtDistance( + (calculateDistanceFromQValue(qValue.toSI().toNumber(), cameralength.toSI().toNumber(), wavelength.toSI().toNumber())) ?? 0, ); + return { x: mathjs.unit(result.x, "m"), y: mathjs.unit(result.y, "m") } }; diff --git a/src/calculations/unitRange.ts b/src/calculations/unitRange.ts new file mode 100644 index 0000000..fca123c --- /dev/null +++ b/src/calculations/unitRange.ts @@ -0,0 +1,68 @@ +import * as mathjs from "mathjs"; +import NumericRange from "./numericRange"; + +export default class UnitRange { + min: mathjs.Unit; + max: mathjs.Unit; + + constructor(min: mathjs.Unit, max: mathjs.Unit) { + this.min = min; + this.max = max; + + if (mathjs.larger(min, max)) { + const temp = max.clone(); + this.max = min; + this.min = temp; + } + } + + to(units: string): UnitRange { + return new UnitRange(this.min.to(units), this.max.to(units)) + } + + containsValue(value: mathjs.Unit): boolean { + return mathjs.largerEq(value, this.min) && mathjs.largerEq(value, this.max); + } + + containsRange(other: UnitRange): boolean { + console.log(`${other.min.formatUnits()} ${this.min.formatUnits()}`) + console.log(`${other.min.formatUnits()} ${this.min.formatUnits()}`) + return mathjs.smallerEq(this.min, other.min) && mathjs.largerEq(this.max, other.max,); + } + + toString(): string { + return `(min:${this.min.toString()}, max:${this.max.toString()})`; + } + + + intersect(other: UnitRange): UnitRange | null { + if (mathjs.larger(other.min, this.max) || mathjs.larger(this.min, other.max)) return null; + + return new UnitRange( + mathjs.max(other.min, this.min), + mathjs.min(other.max, this.max), + ); + } + + equals(other: UnitRange): boolean { + return mathjs.equal(this.min, other.min) && mathjs.equal(this.max, other.max); + } + + + apply(func: (value: mathjs.Unit) => mathjs.Unit): UnitRange { + return new UnitRange(func(this.min), func(this.max)); + } + + inPlaceApply(func: (value: mathjs.Unit) => mathjs.Unit): UnitRange { + this.min = func(this.min); + this.max = func(this.max); + return this; + } + + static fromNumericRange(range: NumericRange | null, units: string): UnitRange { + return new UnitRange( + mathjs.unit(range?.min ?? NaN, units), + mathjs.unit(range?.max ?? NaN, units), + ) + } +} diff --git a/src/data-entry/beamlineconfigStore.ts b/src/data-entry/beamlineconfigStore.ts index 9b05886..488a321 100644 --- a/src/data-entry/beamlineconfigStore.ts +++ b/src/data-entry/beamlineconfigStore.ts @@ -35,7 +35,7 @@ export const useBeamlineConfigStore = create((set) => ({ energy: wavelength2EnergyConverter(defaultConfig.wavelength).to("keV"), updateAngle: (newAngle: number | null, newUnits: string) => set({ - angle: unit(newAngle ?? NaN, newUnits as string), + angle: unit(newAngle ?? NaN, newUnits), userAngle: newAngle, }), updateAngleUnits: (newUnits: AngleUnits) => diff --git a/src/plot/centrePlot.tsx b/src/plot/centrePlot.tsx index 7bf1327..fa0a960 100644 --- a/src/plot/centrePlot.tsx +++ b/src/plot/centrePlot.tsx @@ -8,12 +8,14 @@ import { SvgRect, SvgLine, } from "@h5web/lib"; -import { Vector2, Vector3 } from "three"; +import { Vector3 } from "three"; import { useBeamstopStore } from "../data-entry/beamstopStore"; import { useDetectorStore } from "../data-entry/detectorStore"; import { useCameraTubeStore } from "../data-entry/cameraTubeStore"; import { + UnitVector, createPlotEllipse, + createPlotEllipseClearance, createPlotRange, createPlotRectangle, getDomains, @@ -29,10 +31,8 @@ import { computeQrange } from "../calculations/qrange"; import { useBeamlineConfigStore } from "../data-entry/beamlineconfigStore"; import LegendBar from "./legendBar"; import ResultsBar from "../results/resultsBar"; -import NumericRange from "../calculations/numericRange"; import { getPointForQ } from "../calculations/qvalue"; import { ScatteringOptions, useResultStore } from "../results/resultsStore"; -import { ReciprocalWavelengthUnits, WavelengthUnits } from "../utils/units"; import { convertBetweenQAndD, convertBetweenQAndS, @@ -40,6 +40,7 @@ import { import { color2String } from "./plotUtils"; import SvgAxisAlignedEllipse from "./svgEllipse"; import * as mathjs from "mathjs"; +import UnitRange from "../calculations/unitRange"; export default function CentrePlot(): JSX.Element { const plotConfig = usePlotStore(); @@ -73,6 +74,19 @@ export default function CentrePlot(): JSX.Element { return { centre: state.centre, diameter: state.diameter }; }); + // evil :( :( :( :() + /* eslint-disable */ + if (mathjs.Unit.UNITS.xpixel) { + delete mathjs.Unit.UNITS.xpixel + } + + if (mathjs.Unit.UNITS.ypixel) { + delete mathjs.Unit.UNITS.ypixel + } + /* eslint-enable */ + // evil :( :( :( :() + + mathjs.createUnit("xpixel", detector.pixelSize.width.toString()); mathjs.createUnit("ypixel", detector.pixelSize.height.toString()); @@ -84,15 +98,27 @@ export default function CentrePlot(): JSX.Element { ); // I am about here + const visibleQRangeUnits = UnitRange.fromNumericRange(visibleQRange, "m^-1").to("nm^-1"); + const fullQRangeUnits = UnitRange.fromNumericRange(fullQRange, "m^-1").to("nm^-1") - const beamstopCentre: { x: mathjs.Unit, y: mathjs.Unit } = { - x: mathjs.unit(beamstop.centre.x ?? NaN, "x_pixel"), - y: mathjs.unit(beamstop.centre.y ?? NaN, "y_pixel") + const minPoint: UnitVector = { + x: mathjs.unit(ptMin.x, "m"), + y: mathjs.unit(ptMin.y, "m") } - const cameraTubeCentre: { x: mathjs.Unit, y: mathjs.Unit } = { - x: mathjs.unit(cameraTube.centre.x ?? NaN, "x_pixel"), - y: mathjs.unit(cameraTube.centre.y ?? NaN, "y_pixel") + const maxPoint: UnitVector = { + x: mathjs.unit(ptMax.x, "m"), + y: mathjs.unit(ptMax.y, "m") + } + + const beamstopCentre: UnitVector = { + x: mathjs.unit(beamstop.centre.x ?? NaN, "xpixel"), + y: mathjs.unit(beamstop.centre.y ?? NaN, "ypixel") + } + + const cameraTubeCentre: UnitVector = { + x: mathjs.unit(cameraTube.centre.x ?? NaN, "xpixel"), + y: mathjs.unit(cameraTube.centre.y ?? NaN, "ypixel") } const plotBeamstop = createPlotEllipse( @@ -107,9 +133,10 @@ export default function CentrePlot(): JSX.Element { plotConfig.plotAxes, ); - const plotClearance = createPlotEllipse( + const plotClearance = createPlotEllipseClearance( beamstopCentre, - mathjs.add(beamstop.diameter,), + beamstop.diameter, + beamstop.clearance ?? 0, plotConfig.plotAxes, ); @@ -118,70 +145,72 @@ export default function CentrePlot(): JSX.Element { detector.resolution, plotConfig.plotAxes, ); + const plotVisibleRange = createPlotRange( - new Vector3(ptMin.x, ptMin.y), - new Vector3(ptMax.x, ptMax.y), - detector.pixelSize, + minPoint, + maxPoint, plotConfig.plotAxes, ); - const requestedRange = useResultStore((state) => { + // I am up to here + + const requestedRange = useResultStore((state) => { if (!state.requestedMax || !state.requestedMin) { return null; } - return NumericRange.createWithFunc( - state.requestedMin, - state.requestedMax, - (value: number): number => { - switch (state.requested) { - case ScatteringOptions.d: - value = convertBetweenQAndD(mathjs.unit(value,state.dUnits)); - break; - case ScatteringOptions.s: - value = convertBetweenQAndS(mathjs.unit(value,state.sUnits)); - break; - default: - value = unit(value,state.qUnits); - } - return value; - }, - ); + + const getUnit = (value: number): mathjs.Unit => { + let result: mathjs.Unit; + switch (state.requested) { + case ScatteringOptions.d: + result = convertBetweenQAndD(mathjs.unit(value, state.dUnits)); + break; + case ScatteringOptions.s: + result = convertBetweenQAndS(mathjs.unit(value, state.sUnits)); + break; + default: + result = mathjs.unit(value, state.qUnits); + } + return result; + } + + return new UnitRange( + getUnit(state.requestedMin), + getUnit(state.requestedMax) + ) }); - let requestedMinPt: Vector2 | null = new Vector2(0, 0); - let requestedMaxPt: Vector2 | null = new Vector2(0, 0); + let plotRequestedRange = { + start: new Vector3(0, 0), + end: new Vector3(0, 0), + }; if ( requestedRange && - beamlineConfig.angle && - beamlineConfig.cameraLength && - beamlineConfig.wavelength + beamlineConfig.cameraLength ) { - requestedMaxPt = getPointForQ( - requestedRange.max * 1e9, + const requestedMaxPt = getPointForQ( + requestedRange.max, beamlineConfig.angle, - beamlineConfig.cameraLength, - beamlineConfig.wavelength * 1e-9, - new Vector2(plotBeamstop.centre.x, plotBeamstop.centre.y), + mathjs.unit(beamlineConfig.cameraLength, "m"), + beamlineConfig.wavelength, + beamstopCentre, ); - requestedMinPt = getPointForQ( - requestedRange.min * 1e9, + const requestedMinPt = getPointForQ( + requestedRange.min, beamlineConfig.angle, - beamlineConfig.cameraLength, - beamlineConfig.wavelength * 1e-9, - new Vector2(plotBeamstop.centre.x, plotBeamstop.centre.y), + mathjs.unit(beamlineConfig.cameraLength, "m"), + beamlineConfig.wavelength, + beamstopCentre, + ); + plotRequestedRange = createPlotRange( + requestedMinPt, + requestedMaxPt, + plotConfig.plotAxes, ); } - const plotRequestedRange = { - start: new Vector3(requestedMinPt.x, requestedMinPt.y), - end: new Vector3(requestedMaxPt.x, requestedMaxPt.y), - }; const domains = getDomains(plotDetector, plotConfig.plotAxes); - // evil - delete mathjs.Unit.UNITS.xpixel - delete mathjs.Unit.UNITS.ypixel - return ( @@ -324,7 +353,7 @@ export default function CentrePlot(): JSX.Element { - + ); diff --git a/src/plot/plotUtils.ts b/src/plot/plotUtils.ts index 7331cd1..b70b028 100644 --- a/src/plot/plotUtils.ts +++ b/src/plot/plotUtils.ts @@ -4,6 +4,9 @@ import { PlotAxes } from "./plotStore"; import { Vector3 } from "three"; import * as mathjs from "mathjs"; + + + // Questionable is this how you would do this think about it a little more export const getDomains = ( detector: PlotRectangle, @@ -35,9 +38,14 @@ export interface PlotEllipse { endPointY: Vector3; } +export interface UnitVector { + x: math.Unit, + y: math.Unit +} + export const createPlotEllipse = ( - centre: { x: mathjs.Unit; y: mathjs.Unit }, - diameter: mathjs.Unit, + centre: UnitVector, + diameter: math.Unit, plotAxes: PlotAxes, ): PlotEllipse => { let xunit = plotAxes as string; @@ -66,6 +74,39 @@ export const createPlotEllipse = ( }; }; +export const createPlotEllipseClearance = ( + centre: UnitVector, + diameter: math.Unit, + clearance: number, + plotAxes: PlotAxes, +): PlotEllipse => { + let xunit = plotAxes as string; + let yunit = plotAxes as string; + + if (plotAxes === PlotAxes.pixel) { + xunit = "xpixel"; + yunit = "ypixel"; + } + + const centreVec = new Vector3( + centre.x.to(xunit).toNumber(), + centre.y.to(yunit).toNumber(), + ); + + return { + centre: centreVec, + endPointX: new Vector3( + centreVec.x + mathjs.divide(diameter, 2).to(xunit).toNumber() + mathjs.unit(clearance, "xpixel").to(xunit).toNumber(), + centreVec.y, + ), + endPointY: new Vector3( + centreVec.x, + centreVec.y + mathjs.divide(diameter, 2).to(yunit).toNumber() + mathjs.unit(clearance, "ypixel").to(yunit).toNumber(), + ), + }; +}; + + export interface PlotRectangle { upperBound: Vector3; lowerBound: Vector3; @@ -74,19 +115,19 @@ export interface PlotRectangle { export const createPlotRectangle = ( pinnedCorner: Vector3, resolution: { height: number; width: number }, - pixelSize: { height: number; width: number }, plotAxes: PlotAxes, ): PlotRectangle => { - let fullWidth = resolution.width; - let fullHeight = resolution.height; - switch (plotAxes) { - case PlotAxes.milimeter: - fullWidth *= pixelSize.width; - fullHeight *= pixelSize.height; + let xunit = plotAxes as string; + let yunit = plotAxes as string; + if (plotAxes === PlotAxes.pixel) { + xunit = "xpixel"; + yunit = "ypixel"; } + + return { lowerBound: pinnedCorner, - upperBound: new Vector3(fullWidth, fullHeight), + upperBound: new Vector3(mathjs.unit(resolution.width, "xpixel").to(xunit).toNumber(), mathjs.unit(resolution.height, "ypixel").to(yunit).toNumber()), }; }; @@ -96,22 +137,19 @@ export interface PlotRange { } export const createPlotRange = ( - startPoint: Vector3, - endPoint: Vector3, - pixelSize: { height: number; width: number }, + startPoint: UnitVector, + endPoint: UnitVector, plotAxes: PlotAxes, ): PlotRange => { - const pixelVector = new Vector3(pixelSize.width, pixelSize.height); - + let xunit = plotAxes as string; + let yunit = plotAxes as string; if (plotAxes === PlotAxes.pixel) { - return { - start: startPoint.clone().divide(pixelVector), - end: endPoint.clone().divide(pixelVector), - }; + xunit = "xpixel"; + yunit = "ypixel"; } return { - start: startPoint, - end: endPoint, + start: new Vector3(startPoint.x.to(xunit).toNumber(), startPoint.y.to(yunit).toNumber()), + end: new Vector3(endPoint.x.to(xunit).toNumber(), endPoint.y.to(yunit).toNumber()), }; }; diff --git a/src/results/rangeDiagram.tsx b/src/results/rangeDiagram.tsx index 2843d3e..7baf394 100644 --- a/src/results/rangeDiagram.tsx +++ b/src/results/rangeDiagram.tsx @@ -1,4 +1,4 @@ -import NumericRange from "../calculations/numericRange"; +import UnitRange from "../calculations/unitRange"; export function MessageDiagram(props: { message: string }): JSX.Element { return ( @@ -18,18 +18,15 @@ export function MessageDiagram(props: { message: string }): JSX.Element { } export function RangeDiagram(props: { - visibleQRange: NumericRange; - fullQRange: NumericRange; - requestedRange: NumericRange; + visibleRange: UnitRange; + fullRange: UnitRange; + requestedRange: UnitRange; }): JSX.Element { - const svgRange = props.visibleQRange.max - props.visibleQRange.min; - // const visableStart = (props.visibleQRange.min / svgRange) * 100; - // const visbleWidth = - // ((props.visibleQRange.max - props.visibleQRange.min) / svgRange) * 100; - - const requestedMax = (props.requestedRange.max / svgRange) * 100; - const requestedMin = (props.requestedRange.min / svgRange) * 100; - const rectColour = props.visibleQRange.containsRange(props.requestedRange) + console.log(props.requestedRange.min.formatUnits()) + const svgRange = props.visibleRange.max.toNumber() - props.visibleRange.min.toNumber(); + const requestedMax = (props.requestedRange.max.toNumber() / svgRange) * 100; + const requestedMin = (props.requestedRange.min.toNumber() / svgRange) * 100; + const rectColour = props.visibleRange.containsRange(props.requestedRange) ? "green" : "red"; diff --git a/src/results/rangeTable.tsx b/src/results/rangeTable.tsx index 96fa19e..86792bc 100644 --- a/src/results/rangeTable.tsx +++ b/src/results/rangeTable.tsx @@ -13,16 +13,17 @@ import { TableHead, TableRow, } from "@mui/material"; -import NumericRange from "../calculations/numericRange"; import { ScatteringOptions, useResultStore } from "./resultsStore"; import { ReciprocalWavelengthUnits, WavelengthUnits } from "../utils/units"; import { convertBetweenQAndD, convertBetweenQAndS, } from "./scatteringQuantities"; +import UnitRange from "../calculations/unitRange"; + export default function RangeTable(props: { - qRange: NumericRange | null; + qRange: UnitRange; }): JSX.Element { const resultsStore = useResultStore(); const updateQUnits = useResultStore((state) => state.updateQUnits); @@ -42,26 +43,11 @@ export default function RangeTable(props: { const handleDunits = (event: SelectChangeEvent) => { updateDUnits(event.target.value as WavelengthUnits); }; + const qRange = props.qRange.to(resultsStore.qUnits as string) + const sRange = props.qRange.apply(convertBetweenQAndS).to(resultsStore.sUnits as string); + const dRange = props.qRange.apply(convertBetweenQAndD).to(resultsStore.dUnits as string); - let qRange = props.qRange; - let sRange: NumericRange | null = null; - let dRange: NumericRange | null = null; - - if (props.qRange) { - sRange = props.qRange.apply(convertBetweenQAndS); - dRange = props.qRange.apply(convertBetweenQAndD); - if (resultsStore.qUnits === ReciprocalWavelengthUnits.angstroms) { - qRange = props.qRange.apply(angstroms2Nanometres); - } - - if (resultsStore.sUnits === WavelengthUnits.angstroms) { - sRange = sRange.apply(nanometres2Angstroms); - } - if (resultsStore.dUnits === WavelengthUnits.angstroms) { - dRange = dRange.apply(nanometres2Angstroms); - } - } return ( @@ -80,10 +66,10 @@ export default function RangeTable(props: { {ScatteringOptions.q} - {qRange ? qRange.min.toFixed(4) : ""} + {qRange.min.toNumber().toFixed(4)} - {qRange ? qRange.max.toFixed(4) : ""} + {qRange.max.toNumber().toFixed(4)} @@ -95,10 +81,10 @@ export default function RangeTable(props: { onChange={handleQunits} > - {ReciprocalWavelengthUnits.nanmometres} + {"1 / nm"} - {ReciprocalWavelengthUnits.angstroms} + {"1 / " + "\u212B"} @@ -109,10 +95,10 @@ export default function RangeTable(props: { {ScatteringOptions.s} - {sRange ? sRange.min.toFixed(4) : ""} + {sRange.min.toNumber().toFixed(4)} - {sRange ? sRange.max.toFixed(4) : ""} + {sRange.max.toNumber().toFixed(4)} @@ -127,7 +113,7 @@ export default function RangeTable(props: { {WavelengthUnits.nanmometres} - {WavelengthUnits.angstroms} + {"\u212B"} @@ -138,10 +124,10 @@ export default function RangeTable(props: { {ScatteringOptions.d} - {dRange ? dRange.min.toFixed(4) : ""} + {dRange.min.toNumber().toFixed(4)} - {dRange ? dRange.max.toFixed(4) : ""} + {dRange.max.toNumber().toFixed(4)} @@ -156,7 +142,7 @@ export default function RangeTable(props: { {WavelengthUnits.nanmometres} - {WavelengthUnits.angstroms} + {"\u212B"} diff --git a/src/results/resultsBar.tsx b/src/results/resultsBar.tsx index 8eeb3e5..300fc7b 100644 --- a/src/results/resultsBar.tsx +++ b/src/results/resultsBar.tsx @@ -13,8 +13,6 @@ import { import NumericRange from "../calculations/numericRange"; import { ScatteringOptions, useResultStore } from "./resultsStore"; import { - ReciprocalWavelengthUnits, - WavelengthUnits, parseNumericInput, } from "../utils/units"; import { RangeDiagram, MessageDiagram } from "./rangeDiagram"; @@ -25,10 +23,11 @@ import { convertBetweenQAndS, } from "./scatteringQuantities"; import RangeTable from "./rangeTable"; +import UnitRange from "../calculations/unitRange"; export default function ResultsBar(props: { - visableQRange: NumericRange | null; - fullQrange: NumericRange | null; + visableQRange: UnitRange; + fullQrange: UnitRange; }): JSX.Element { const resultStore = useResultStore(); const requestedRange = useResultStore((state) => { @@ -37,9 +36,9 @@ export default function ResultsBar(props: { : null; }); - let diagramVisible: NumericRange | null = null; - let diagramFull: NumericRange | null = null; - let diagramRequested: NumericRange | null = requestedRange; + let diagramVisible: UnitRange | null = null; + let diagramFull: UnitRange | null = null; + let diagramRequested: UnitRange | null = null; const handleRequestedMax = (event: React.ChangeEvent) => { resultStore.updateRequestedRange({ @@ -56,25 +55,19 @@ export default function ResultsBar(props: { if (props.visableQRange && props.fullQrange && requestedRange) { switch (resultStore.requested) { case ScatteringOptions.d: - diagramVisible = props.visableQRange.apply(convertBetweenQAndD); - diagramFull = props.fullQrange.apply(convertBetweenQAndD); - if (resultStore.dUnits === WavelengthUnits.angstroms) { - diagramRequested = requestedRange.apply(angstroms2Nanometres); - } + diagramVisible = props.visableQRange.apply(convertBetweenQAndD).to("nm"); + diagramFull = props.fullQrange.apply(convertBetweenQAndD).to("nm"); + diagramRequested = UnitRange.fromNumericRange(requestedRange, resultStore.dUnits as string).to("nm"); break; case ScatteringOptions.s: - diagramVisible = props.visableQRange.apply(convertBetweenQAndS); - diagramFull = props.fullQrange.apply(convertBetweenQAndS); - if (resultStore.sUnits === WavelengthUnits.angstroms) { - diagramRequested = requestedRange.apply(angstroms2Nanometres); - } + diagramVisible = props.visableQRange.apply(convertBetweenQAndS).to("nm"); + diagramFull = props.fullQrange.apply(convertBetweenQAndS).to("nm"); + diagramRequested = UnitRange.fromNumericRange(requestedRange, resultStore.sUnits as string).to("nm"); break; default: - diagramVisible = props.visableQRange; - diagramFull = props.fullQrange; - if (resultStore.qUnits === ReciprocalWavelengthUnits.angstroms) { - diagramRequested = requestedRange.apply(nanometres2Angstroms); - } + diagramVisible = props.visableQRange.to("nm^-1"); + diagramFull = props.fullQrange.to("nm^-1"); + diagramRequested = UnitRange.fromNumericRange(requestedRange, resultStore.qUnits as string).to("nm^-1"); } } @@ -148,14 +141,12 @@ export default function ResultsBar(props: { diagramVisible && diagramFull && diagramRequested && - { - /*diagramFull.containsRange(diagramVisible)*/ - } + diagramFull.containsRange(diagramVisible) ) { return ( ); diff --git a/src/utils/units.test.ts b/src/utils/units.test.ts deleted file mode 100644 index 0c79706..0000000 --- a/src/utils/units.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { expect, test } from "vitest"; -import { - angstroms2Nanometres, - electronVots2KiloElectronVolts, - energy2WavelengthConverter, - kiloElectronVolts2ElectronVots, - micrometre2Milimetre, - millimetre2Micrometre, - nanometres2Angstroms, - wavelength2EnergyConverter, - parseNumericInput, -} from "./units"; - -const FLOAT_THRESHOLD = 1e-12; - -test("Test energy and wavelength converters", () => { - const wavelengthNM = 0.09; - const energyKEV = wavelength2EnergyConverter(wavelengthNM); - expect(Math.abs(energyKEV - 13.777540970654243)).toBeLessThan( - FLOAT_THRESHOLD, - ); - const wavelengthOut = energy2WavelengthConverter(energyKEV); - expect(Math.abs(wavelengthOut - 0.09)).toBeLessThan(FLOAT_THRESHOLD); -}); - -test("Misc unit coverters", () => { - expect(Math.abs(kiloElectronVolts2ElectronVots(1)) - 1000).toBeLessThan( - FLOAT_THRESHOLD, - ); - expect(Math.abs(electronVots2KiloElectronVolts(1000)) - 1).toBeLessThan( - FLOAT_THRESHOLD, - ); - expect(Math.abs(nanometres2Angstroms(1)) - 10).toBeLessThan(FLOAT_THRESHOLD); - expect(Math.abs(angstroms2Nanometres(10)) - 1).toBeLessThan(FLOAT_THRESHOLD); - expect(Math.abs(millimetre2Micrometre(1)) - 1000).toBeLessThan( - FLOAT_THRESHOLD, - ); - expect(Math.abs(micrometre2Milimetre(1000)) - 1).toBeLessThan( - FLOAT_THRESHOLD, - ); -}); - -test("Test valid inputs", () => { - expect(parseNumericInput("200")).toBe(200); - expect(parseNumericInput(" 200 ")).toBe(200); - expect(parseNumericInput("-10")).toBe(-10); - expect(parseNumericInput("0")).toBe(0); - expect(parseNumericInput("0.878")).toBe(0.878); -}); - -test("Test not valid inputs", () => { - expect(parseNumericInput("t2fg00number")).toBe(null); - expect(parseNumericInput("word ")).toBe(null); -}); diff --git a/src/utils/units.ts b/src/utils/units.ts index 91e397a..865ef87 100644 --- a/src/utils/units.ts +++ b/src/utils/units.ts @@ -3,7 +3,6 @@ import * as math from "mathjs"; export const CSPEED = math.unit(299792458, "m/s"); export const PLANCK = math.unit(6.62607015e-34, "J s"); -const angstrum = "\u212B"; export enum DistanceUnits { millimetre = "mm", @@ -21,8 +20,8 @@ export enum WavelengthUnits { } export enum ReciprocalWavelengthUnits { - nanmometres = "1/nm", - angstroms = `1/${angstrum}`, + nanmometres = "nm^-1", + angstroms = `angstrom^-1`, } export enum AngleUnits { @@ -50,7 +49,7 @@ export interface UnitConfig { export const energy2WavelengthConverter = (energy: math.Unit): math.Unit => { const result = math.divide(math.multiply(PLANCK, CSPEED), energy.toSI()); if (typeof result == "number") { - throw "units for constants h and c are wrong"; + throw TypeError("units for constants h and c are wrong"); } return result; }; @@ -65,7 +64,7 @@ export const wavelength2EnergyConverter = ( ): math.Unit => { const result = math.divide(math.multiply(PLANCK, CSPEED), wavelength.toSI()); if (typeof result == "number") { - throw "units for constants h and c are wrong"; + throw TypeError("units for constants h and c are wrong"); } return result; }; From 3eed616d7383215d012fc42d37578e01abe4d8ee Mon Sep 17 00:00:00 2001 From: tizayi Date: Mon, 4 Dec 2023 11:01:34 +0000 Subject: [PATCH 3/3] type guards :( --- src/calculations/qrange.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/calculations/qrange.ts b/src/calculations/qrange.ts index 9569d7d..95229b8 100644 --- a/src/calculations/qrange.ts +++ b/src/calculations/qrange.ts @@ -57,6 +57,12 @@ export function computeQrange( beamcentreY, ); + if (typeof initialPositionX === "number" || !("units" in initialPositionX)) { + return defaultReturn; + } + if (typeof initialPositionY === "number" || !("units" in initialPositionY)) { + return defaultReturn; + } const initialPosition = new Vector2( initialPositionX.toSI().toNumber(), initialPositionY.toSI().toNumber(),