diff --git a/src/math.ts b/src/math.ts index b4ec603..4595b09 100644 --- a/src/math.ts +++ b/src/math.ts @@ -34,6 +34,10 @@ export const roundToNearest = (value: number, base: number) => { return Math.round(value / base) * base; }; +export const mapRange = (value: number, fromMin: number, fromMax: number, toMin: number, toMax: number) => { + return (value - fromMin)/(fromMax - fromMin) * (toMax - toMin) + toMin; +}; + export const wayThrough = (value: number, min: number, max: number) => { // ASSERT(value >= min && value <= max); return (value - min) / (max - min); diff --git a/src/ui/elements/slider.ts b/src/ui/elements/slider.ts index 3beadf2..b9dd97d 100644 --- a/src/ui/elements/slider.ts +++ b/src/ui/elements/slider.ts @@ -1,5 +1,5 @@ import { ASSERT } from '../../util'; -import { clamp } from '../../math'; +import { clamp, mapRange, wayThrough } from '../../math'; import { LabelledElement } from './labelled_element'; export class SliderElement extends LabelledElement { @@ -7,14 +7,18 @@ export class SliderElement extends LabelledElement { private _max: number; private _decimals: number; private _dragging: boolean; + private _step: number; + private _hovering: boolean; - public constructor(label: string, min: number, max: number, decimals: number, value: number) { + public constructor(label: string, min: number, max: number, decimals: number, value: number, step: number) { super(label); this._min = min; this._max = max; this._decimals = decimals; this._value = value; + this._step = step; this._dragging = false; + this._hovering = false; } public generateInnerHTML() { @@ -38,6 +42,7 @@ export class SliderElement extends LabelledElement { ASSERT(element !== null); element.onmouseenter = () => { + this._hovering = true; if (this._isEnabled) { element.classList.add('new-slider-hover'); elementBar.classList.add('new-slider-bar-hover'); @@ -45,49 +50,80 @@ export class SliderElement extends LabelledElement { }; element.onmouseleave = () => { - element.classList.remove('new-slider-hover'); - elementBar.classList.remove('new-slider-bar-hover'); + this._hovering = false; + if (!this._dragging) { + element.classList.remove('new-slider-hover'); + elementBar.classList.remove('new-slider-bar-hover'); + } }; element.onmousedown = () => { this._dragging = true; }; - document.addEventListener('mousemove', (e: any) => { + document.addEventListener('mousemove', (e: MouseEvent) => { if (this._dragging) { - this._updateValue(e); + this._onDragSlider(e); } }); - document.addEventListener('mouseup', (e: any) => { + document.addEventListener('mouseup', (e: MouseEvent) => { if (this._dragging) { - this._updateValue(e); + this._onDragSlider(e); + } + if (!this._hovering) { + element.classList.remove('new-slider-hover'); + elementBar.classList.remove('new-slider-bar-hover'); } this._dragging = false; }); + + element.addEventListener('wheel', (e: WheelEvent) => { + if (!this._dragging && this._isEnabled) { + e.preventDefault(); + this._onScrollSlider(e); + } + }); } - private _updateValue(e: MouseEvent) { + private _onScrollSlider(e: WheelEvent) { + if (!this._isEnabled) { + return; + } + ASSERT(this._value); + + this._value -= (e.deltaY / 150) * this._step; + this._value = clamp(this._value, this._min, this._max); + + this._onValueUpdated(); + } + + private _onDragSlider(e: MouseEvent) { if (!this._isEnabled) { return; } const element = document.getElementById(this._id) as HTMLDivElement; + ASSERT(element !== null); + + const box = element.getBoundingClientRect(); + const left = box.x; + const right = box.x + box.width; + + this._value = mapRange(e.clientX, left, right, this._min, this._max); + this._value = clamp(this._value, this._min, this._max); + + this._onValueUpdated(); + } + + private _onValueUpdated() { const elementBar = document.getElementById(this._id + '-bar') as HTMLDivElement; const elementValue = document.getElementById(this._id + '-value') as HTMLDivElement; - ASSERT(element !== null && elementBar !== null && elementValue !== null); + ASSERT(elementBar !== null && elementValue !== null); - - const mouseEvent = e as MouseEvent; - const xOffset = mouseEvent.clientX - elementBar.getBoundingClientRect().x; - const width = element.clientWidth; - const norm = clamp(xOffset / width, 0.0, 1.0); + const norm = wayThrough(this.getValue(), this._min, this._max); elementBar.style.width = `${norm * 100}%`; - - const value = (norm * (this._max - this._min)) + this._min; - const displayValue = value.toFixed(this._decimals); - elementValue.innerHTML = displayValue; - this._value = parseFloat(displayValue); + elementValue.innerHTML = this.getValue().toFixed(this._decimals); } protected _onEnabledChanged() { diff --git a/src/ui/layout.ts b/src/ui/layout.ts index 27de9d6..9164234 100644 --- a/src/ui/layout.ts +++ b/src/ui/layout.ts @@ -45,7 +45,7 @@ export class UI { 'simplify': { label: 'Simplify', elements: { - 'ratio': new SliderElement('Ratio', 0.0, 1.0, 2, 0.5), + 'ratio': new SliderElement('Ratio', 0.0, 1.0, 2, 0.5, 0.01), }, elementsOrder: ['ratio'], submitButton: new ButtonElement('Simplify mesh', () => { @@ -56,7 +56,7 @@ export class UI { 'build': { label: 'Build', elements: { - 'height': new SliderElement('Desired height', 3, 320, 0, 80), + 'height': new SliderElement('Desired height', 3, 320, 0, 80, 1), 'voxeliser': new ComboBoxElement('Algorithm', [ { id: 'bvhraybased', displayText: 'BVH Ray-based' }, { id: 'normalcorrectedraybased', displayText: 'NCRB' },