diff --git a/src/mol-canvas3d/camera.ts b/src/mol-canvas3d/camera.ts index 9c48d0211022b739dd37f341c37c281916797ba5..7fd63907dece9095c4b802fc5b12bea60d17f327 100644 --- a/src/mol-canvas3d/camera.ts +++ b/src/mol-canvas3d/camera.ts @@ -97,6 +97,7 @@ class Camera implements Object3D { Vec3.setMagnitude(this.deltaDirection, this.state.direction, deltaDistance) if (currentDistance < targetDistance) Vec3.negate(this.deltaDirection, this.deltaDirection) Vec3.add(this.newPosition, this.state.position, this.deltaDirection) + this.setState({ target, position: this.newPosition }) } diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index f164a4edb4ab97aa5dc3c49ca8b6e64fc3c9be40..6cee297115311fc3d6d6f0abe9ba808fa4aa3ab5 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -35,11 +35,8 @@ export const Canvas3DParams = { cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]), backgroundColor: PD.Color(Color(0x000000)), // TODO: make this an interval? - clipNear: PD.Numeric(1, { min: 1, max: 100, step: 1 }), - clipFar: PD.Numeric(100, { min: 1, max: 100, step: 1 }), - // TODO: make this an interval? - fogNear: PD.Numeric(50, { min: 1, max: 100, step: 1 }), - fogFar: PD.Numeric(100, { min: 1, max: 100, step: 1 }), + clip: PD.Interval([1, 100], { min: 1, max: 100, step: 1 }), + fog: PD.Interval([50, 100], { min: 1, max: 100, step: 1 }), pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }), showBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of render objects.' }), // debug: PD.Group({ @@ -168,13 +165,13 @@ namespace Canvas3D { const cDist = Vec3.distance(camera.state.position, camera.state.target) const bRadius = Math.max(10, scene.boundingSphere.radius) - const nearFactor = (50 - p.clipNear) / 50 - const farFactor = -(50 - p.clipFar) / 50 + const nearFactor = (50 - p.clip[0]) / 50 + const farFactor = -(50 - p.clip[1]) / 50 const near = cDist - (bRadius * nearFactor) const far = cDist + (bRadius * farFactor) - const fogNearFactor = (50 - p.fogNear) / 50 - const fogFarFactor = -(50 - p.fogFar) / 50 + const fogNearFactor = (50 - p.fog[0]) / 50 + const fogFarFactor = -(50 - p.fog[1]) / 50 const fogNear = cDist - (bRadius * fogNearFactor) const fogFar = cDist + (bRadius * fogFarFactor) @@ -378,10 +375,8 @@ namespace Canvas3D { renderer.setClearColor(props.backgroundColor) } - if (props.clipNear !== undefined) p.clipNear = props.clipNear - if (props.clipFar !== undefined) p.clipFar = props.clipFar - if (props.fogNear !== undefined) p.fogNear = props.fogNear - if (props.fogFar !== undefined) p.fogFar = props.fogFar + if (props.clip !== undefined) p.clip = [props.clip[0], props.clip[1]] + if (props.fog !== undefined) p.fog = [props.fog[0], props.fog[1]] if (props.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== renderer.props.pickingAlphaThreshold) { renderer.setPickingAlphaThreshold(props.pickingAlphaThreshold) @@ -396,10 +391,8 @@ namespace Canvas3D { return { cameraMode: camera.state.mode, backgroundColor: renderer.props.clearColor, - clipNear: p.clipNear, - clipFar: p.clipFar, - fogNear: p.fogNear, - fogFar: p.fogFar, + clip: p.clip, + fog: p.fog, pickingAlphaThreshold: renderer.props.pickingAlphaThreshold, showBoundingSpheres: boundingSphereHelper.visible } diff --git a/src/mol-plugin/skin/base/components/controls.scss b/src/mol-plugin/skin/base/components/controls.scss index 835a585261bff9f9637062e21a414a1154ea924c..31474759cc641cf90c6d4057bd704b551ec518a3 100644 --- a/src/mol-plugin/skin/base/components/controls.scss +++ b/src/mol-plugin/skin/base/components/controls.scss @@ -110,6 +110,57 @@ // } } +.msp-slider2 { + > div:first-child { + position: absolute; + height: $row-height; + line-height: $row-height; + text-align: center; + left: 0; + width: 25px; + top: 0; + bottom: 0; + font-size: 80%; + } + > div:nth-child(2) { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 25px; + width: 100%; + padding-left: 20px; + padding-right: 25px; + display: table; + + > div { + height: $row-height; + display: table-cell; + vertical-align: middle; + padding: 0 ($control-spacing + 4px); + } + } + > div:last-child { + position: absolute; + height: $row-height; + line-height: $row-height; + text-align: center; + right: 0; + width: 25px; + top: 0; + bottom: 0; + font-size: 80%; + } + + // input[type=text] { + // text-align: right; + // } + + // input[type=range] { + // width: 100%; + // } +} + .msp-toggle-color-picker { button { border: $control-spacing solid $msp-form-control-background !important; diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx index 9c3b656d372e9c97103430d042d72a945fa9f8a5..47e0abc1ab1274938c33a50680dc84dc78652889 100644 --- a/src/mol-plugin/ui/controls/parameters.tsx +++ b/src/mol-plugin/ui/controls/parameters.tsx @@ -10,7 +10,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; import { camelCaseToWords } from 'mol-util/string'; import { ColorNames } from 'mol-util/color/tables'; import { Color } from 'mol-util/color'; -import { Slider } from './slider'; +import { Slider, Slider2 } from './slider'; export interface ParameterControlsProps<P extends PD.Params = PD.Params> { params: P, @@ -49,7 +49,8 @@ function controlFor(param: PD.Any): ParamControl | undefined { case 'file': return FileControl; case 'select': return SelectControl; case 'text': return TextControl; - case 'interval': return IntervalControl; + case 'interval': return typeof param.min !== 'undefined' && typeof param.max !== 'undefined' + ? BoundedIntervalControl : IntervalControl; case 'group': return GroupControl; case 'mapped': return MappedControl; case 'line-graph': return void 0; @@ -146,16 +147,20 @@ export class SelectControl extends SimpleParam<PD.Select<any>> { } export class IntervalControl extends SimpleParam<PD.Interval> { - // onChange = (e: React.ChangeEvent<HTMLSelectElement>) => { - // this.setState({ value: e.target.value }); - // this.props.onChange(e.target.value); - // } - + onChange = (v: [number, number]) => { this.update(v); } renderControl() { return <span>interval TODO</span>; } } +export class BoundedIntervalControl extends SimpleParam<PD.Interval> { + onChange = (v: [number, number]) => { this.update(v); } + renderControl() { + return <Slider2 value={this.props.value} min={this.props.param.min!} max={this.props.param.max!} + step={this.props.param.step} onChange={this.onChange} disabled={this.props.isDisabled} />; + } +} + export class ColorControl extends SimpleParam<PD.Color> { onChange = (e: React.ChangeEvent<HTMLSelectElement>) => { this.update(Color(parseInt(e.target.value))); diff --git a/src/mol-plugin/ui/controls/slider.tsx b/src/mol-plugin/ui/controls/slider.tsx index 260903bbfa6ffd50c32716303c2ba18448b1cd90..f930a20fe558d0496ba4eeec0689a97ad4bb506d 100644 --- a/src/mol-plugin/ui/controls/slider.tsx +++ b/src/mol-plugin/ui/controls/slider.tsx @@ -38,12 +38,12 @@ export class Slider extends React.Component<{ render() { let step = this.props.step; if (step === void 0) step = 1; - return <div className='msp-slider'> - <div> + return <div className='msp-slider'> <div> - <SliderBase min={this.props.min} max={this.props.max} step={step} value={this.state.current} disabled={this.props.disabled} - onBeforeChange={this.begin} - onChange={this.updateCurrent as any} onAfterChange={this.end as any} /> + <div> + <SliderBase min={this.props.min} max={this.props.max} step={step} value={this.state.current} disabled={this.props.disabled} + onBeforeChange={this.begin} + onChange={this.updateCurrent as any} onAfterChange={this.end as any} /> </div></div> <div> {`${Math.round(100 * this.state.current) / 100}`} @@ -52,6 +52,54 @@ export class Slider extends React.Component<{ } } +export class Slider2 extends React.Component<{ + min: number, + max: number, + value: [number, number], + step?: number, + onChange: (v: [number, number]) => void, + disabled?: boolean +}, { isChanging: boolean, current: [number, number] }> { + + state = { isChanging: false, current: [0, 1] as [number, number] } + + static getDerivedStateFromProps(props: { value: [number, number] }, state: { isChanging: boolean, current: [number, number] }) { + if (state.isChanging || (props.value[0] === state.current[0]) && (props.value[1] === state.current[1])) return null; + return { current: props.value }; + } + + begin = () => { + this.setState({ isChanging: true }); + } + + end = (v: [number, number]) => { + this.setState({ isChanging: false }); + this.props.onChange(v); + } + + updateCurrent = (current: [number, number]) => { + this.setState({ current }); + } + + render() { + let step = this.props.step; + if (step === void 0) step = 1; + return <div className='msp-slider2'> + <div> + {`${Math.round(100 * this.state.current[0]) / 100}`} + </div> + <div> + <div> + <SliderBase min={this.props.min} max={this.props.max} step={step} value={this.state.current} disabled={this.props.disabled} + onBeforeChange={this.begin} onChange={this.updateCurrent as any} onAfterChange={this.end as any} range={true} pushable={true} /> + </div></div> + <div> + {`${Math.round(100 * this.state.current[1]) / 100}`} + </div> + </div>; + } +} + /** * The following code was adapted from react-components/slider library. *