From 29d78e43098149343d6a7bea786c358a72ecb59a Mon Sep 17 00:00:00 2001 From: David Stansby Date: Mon, 28 Aug 2023 06:34:06 +0200 Subject: [PATCH] Add some comments to lerp and invlerp (#485) --- src/neuroglancer/util/lerp.ts | 13 +++++++++++++ src/neuroglancer/widget/invlerp.ts | 31 ++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/neuroglancer/util/lerp.ts b/src/neuroglancer/util/lerp.ts index 62edbaff9..6a98533e5 100644 --- a/src/neuroglancer/util/lerp.ts +++ b/src/neuroglancer/util/lerp.ts @@ -34,6 +34,12 @@ export const defaultDataTypeRange: Record = { [DataType.FLOAT32]: [0, 1], }; +/** + * Compute inverse linear interpolation on the interval [0, 1]. + * @param range Values at start and end of interval. + * @param value Value to interpolate at. + * @returns Coordinate of interpolated point. + */ export function computeInvlerp(range: DataTypeInterval, value: number|Uint64): number { if (typeof value === 'number') { const minValue = range[0] as number; @@ -54,6 +60,13 @@ export function computeInvlerp(range: DataTypeInterval, value: number|Uint64): n } } +/** + * Compute linear interpolation on the interval [0, 1]. + * @param range Values at start and end of interval. + * @param dataType + * @param value Coordinate to interpolate at. + * @returns Interpolated value. + */ export function computeLerp(range: DataTypeInterval, dataType: DataType, value: number): number| Uint64 { if (typeof range[0] === 'number') { diff --git a/src/neuroglancer/widget/invlerp.ts b/src/neuroglancer/widget/invlerp.ts index 5dbe9376a..addf3c4f2 100644 --- a/src/neuroglancer/widget/invlerp.ts +++ b/src/neuroglancer/widget/invlerp.ts @@ -66,10 +66,10 @@ export class CdfController extends RefCounted const value = this.getTargetValue(mouseEvent); if (value === undefined) return; const clampedRange = getClampedInterval(bounds.window, bounds.range); - const endpoint = getClosestEndpoint(clampedRange, value); + const endpointIndex = getClosestEndpoint(clampedRange, value); const setEndpoint = (value: number|Uint64) => { const bounds = this.getModel(); - this.setModel(getUpdatedRangeAndWindowParameters(bounds, 'range', endpoint, value)); + this.setModel(getUpdatedRangeAndWindowParameters(bounds, 'range', endpointIndex, value)); }; setEndpoint(value); startRelativeMouseDrag(mouseEvent, (newEvent: MouseEvent) => { @@ -86,6 +86,7 @@ export class CdfController extends RefCounted const mouseEvent = actionEvent.detail; const initialRelativeX = this.getTargetFraction(mouseEvent); const initialValue = this.getWindowLerp(initialRelativeX); + // Index for bound being adjusted const endpointIndex = (initialRelativeX < 0.5) ? 0 : 1; const setEndpoint = (value: number|Uint64) => { const bounds = this.getModel(); @@ -126,11 +127,19 @@ export class CdfController extends RefCounted }); } + /** + * Get fraction of distance in x along bounding rect for a MouseEvent. + */ getTargetFraction(event: MouseEvent) { const clientRect = this.element.getBoundingClientRect(); return (event.clientX - clientRect.left) / clientRect.width; } + /** + * Interpolate a value along the model interval. + * @param relativeX Relative x coordinate within the interval. + * @returns Interpolated value. + */ getWindowLerp(relativeX: number) { return computeLerp(this.getModel().window, this.dataType, relativeX); } @@ -144,21 +153,36 @@ export class CdfController extends RefCounted const histogramSamplerTextureUnit = Symbol('histogramSamplerTexture'); +/** + * An interval with coordinates `range` and endpoint values `window`. + * Can be thought of representing associated intervals in x (range) and y (window). + */ export interface RangeAndWindowIntervals { range: DataTypeInterval; window: DataTypeInterval; } +/** + * Update the value of one endpoint, and return new interval. + * @param existingBounds Initial bounds. + * @param boundType 'range' to update endpoint coordinates, 'window' to update endpoint values. + * @param endpointIndex Index of bound to update. + * @param newEndpoint New value of bound being updated. + * @param fitRangeInWindow + * @returns New bounds. + */ export function getUpdatedRangeAndWindowParameters( existingBounds: T, boundType: 'range'|'window', endpointIndex: number, newEndpoint: number|Uint64, fitRangeInWindow = false): T { const newBounds = {...existingBounds}; const existingInterval = existingBounds[boundType]; newBounds[boundType] = [existingInterval[0], existingInterval[1]] as DataTypeInterval; + // Update bound newBounds[boundType][endpointIndex] = newEndpoint; if (boundType === 'window' && dataTypeCompare(newEndpoint, existingInterval[1 - endpointIndex]) * (2 * endpointIndex - 1) < 0) { + // If new endpoint has gone past other bound, adjust other bound to match newBounds[boundType][1 - endpointIndex] = newEndpoint; } if (boundType === 'range' && fitRangeInWindow) { @@ -180,6 +204,9 @@ export function getUpdatedRangeAndWindowParameters