Skip to content
Snippets Groups Projects
Commit 11ed0ca8 authored by David Sehnal's avatar David Sehnal
Browse files

added draft of ParamMapping interface and control

parent 188ea6e8
Branches
No related tags found
No related merge requests found
File suppressed by a .gitattributes entry, the file's encoding is unsupported, or the file size exceeds the limit.
......@@ -116,6 +116,7 @@
"cors": "^2.8.5",
"express": "^4.17.1",
"graphql": "^14.6.0",
"immer": "^5.3.6",
"immutable": "^3.8.2",
"node-fetch": "^2.6.0",
"react": "^16.12.0",
......
......@@ -15,21 +15,25 @@ import * as React from 'react';
import LineGraphComponent from './line-graph/line-graph-component';
import { Slider, Slider2 } from './slider';
import { NumericInput, IconButton, ControlGroup } from './common';
import { _Props, _State } from '../base';
import { _Props, _State, PluginUIComponent } from '../base';
import { legendFor } from './legend';
import { Legend as LegendData } from '../../mol-util/legend';
import { CombinedColorControl, ColorValueOption, ColorOptions } from './color';
import { getPrecision } from '../../mol-util/number';
import { ParamMapping } from '../../mol-util/param-mapping';
import { PluginContext } from '../../mol-plugin/context';
export interface ParameterControlsProps<P extends PD.Params = PD.Params> {
params: P,
values: any,
onChange: ParamOnChange,
onChange: ParamsOnChange<PD.Values<P>>,
isDisabled?: boolean,
onEnter?: () => void
}
export class ParameterControls<P extends PD.Params> extends React.PureComponent<ParameterControlsProps<P>, {}> {
onChange: ParamOnChange = (params) => this.props.onChange(params, this.props.values);
render() {
const params = this.props.params;
const values = this.props.values;
......@@ -41,12 +45,32 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent<
if (param.isHidden) return null;
const Control = controlFor(param);
if (!Control) return null;
return <Control param={param} key={key} onChange={this.props.onChange} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} name={key} value={values[key]} />
return <Control param={param} key={key} onChange={this.onChange} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} name={key} value={values[key]} />
})}
</>;
}
}
export class ParameterMappingControl<S, T> extends PluginUIComponent<{ mapping: ParamMapping<S, T, PluginContext> }> {
setSettings = (p: { param: PD.Base<any>, name: string, value: any }, old: any) => {
const values = { ...old, [p.name]: p.value };
const t = this.props.mapping.update(values, this.plugin);
this.props.mapping.apply(t, this.plugin);
}
componentDidMount() {
this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
}
render() {
const t = this.props.mapping.getTarget(this.plugin);
const values = this.props.mapping.getValues(t, this.plugin);
const params = this.props.mapping.params(this.plugin) as any as PD.Params;
return <ParameterControls params={params} values={values} onChange={this.setSettings} />
}
}
function controlFor(param: PD.Any): ParamControl | undefined {
switch (param.type) {
case 'value': return void 0;
......@@ -91,6 +115,7 @@ export class ParamHelp<L extends LegendData> extends React.PureComponent<{ legen
}
}
export type ParamsOnChange<P> = (params: { param: PD.Base<any>, name: string, value: any }, values: Readonly<P>) => void
export type ParamOnChange = (params: { param: PD.Base<any>, name: string, value: any }) => void
export interface ParamProps<P extends PD.Base<any> = PD.Base<any>> {
name: string,
......
......@@ -9,10 +9,23 @@ import * as React from 'react';
import { Canvas3DParams } from '../../mol-canvas3d/canvas3d';
import { PluginCommands } from '../../mol-plugin/command';
import { ColorNames } from '../../mol-util/color/names';
import { ParameterControls } from '../controls/parameters';
import { ParameterMappingControl } from '../controls/parameters';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { PluginUIComponent } from '../base';
import { Color } from '../../mol-util/color';
import { ParamMapping } from '../../mol-util/param-mapping';
import { PluginContext } from '../../mol-plugin/context';
export class SimpleSettingsControl extends PluginUIComponent {
componentDidMount() {
this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
}
render() {
if (!this.plugin.canvas3d) return null;
return <ParameterMappingControl mapping={SimpleSettingsMapping} />
}
}
const SimpleSettingsParams = {
spin: Canvas3DParams.trackball.params.spin,
......@@ -21,80 +34,21 @@ const SimpleSettingsParams = {
'transparent': PD.EmptyGroup(),
'opaque': PD.Group({ color: PD.Color(Color(0xFCFBF9), { description: 'Custom background color' }) }, { isFlat: true })
}, { description: 'Background of the 3D canvas' }),
renderStyle: PD.Select('glossy', [['flat', 'Flat'], ['matte', 'Matte'], ['glossy', 'Glossy'], ['metallic', 'Metallic']], { description: 'Style in which the 3D scene is rendered' }),
renderStyle: PD.Select('glossy', PD.arrayToOptions(['flat', 'matte', 'glossy', 'metallic']), { description: 'Style in which the 3D scene is rendered' }),
occlusion: PD.Boolean(false, { description: 'Darken occluded crevices with the ambient occlusion effect' }),
outline: PD.Boolean(false, { description: 'Draw outline around 3D objects' }),
fog: PD.Boolean(false, { description: 'Show fog in the distance' }),
clipFar: PD.Boolean(true, { description: 'Clip scene in the distance' }),
};
export class SimpleSettingsControl extends PluginUIComponent {
setSettings = (p: { param: PD.Base<any>, name: keyof typeof SimpleSettingsParams | string, value: any }) => {
if (p.name === 'spin') {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: p.value } } });
} else if (p.name === 'camera') {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { cameraMode: p.value }});
} else if (p.name === 'background') {
if (!this.plugin.canvas3d) return;
const renderer = this.plugin.canvas3d.props.renderer;
const color: typeof SimpleSettingsParams['background']['defaultValue'] = p.value;
if (color.name === 'transparent') {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: ColorNames.white }, transparentBackground: true } });
} else {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: color.params.color }, transparentBackground: false } });
}
} else if (p.name === 'renderStyle') {
if (!this.plugin.canvas3d) return;
const renderer = this.plugin.canvas3d.props.renderer;
if (p.value === 'flat') {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
renderer: { ...renderer, lightIntensity: 0, ambientIntensity: 1, roughness: 0.4, metalness: 0 }
} });
} else if (p.value === 'matte') {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
renderer: { ...renderer, lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 1, metalness: 0 }
} });
} else if (p.value === 'glossy') {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
renderer: { ...renderer, lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 0.4, metalness: 0 }
} });
} else if (p.value === 'metallic') {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
renderer: { ...renderer, lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 0.6, metalness: 0.4 }
} });
}
} else if (p.name === 'occlusion') {
if (!this.plugin.canvas3d) return;
const postprocessing = this.plugin.canvas3d.props.postprocessing;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
postprocessing: { ...postprocessing, occlusionEnable: p.value, occlusionBias: 0.5, occlusionRadius: 64 },
} });
} else if (p.name === 'outline') {
if (!this.plugin.canvas3d) return;
const postprocessing = this.plugin.canvas3d.props.postprocessing;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
postprocessing: { ...postprocessing, outlineEnable: p.value },
} });
} else if (p.name === 'fog') {;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
cameraFog: p.value ? 50 : 0,
} });
} else if (p.name === 'clipFar') {;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
cameraClipFar: p.value,
} });
}
}
get values () {
const renderer = this.plugin.canvas3d?.props.renderer;
let renderStyle = 'custom'
let background: typeof SimpleSettingsParams['background']['defaultValue'] = { name: 'transparent', params: { } }
type SimpleSettingsParams = typeof SimpleSettingsParams
const SimpleSettingsMapping = ParamMapping({
params: SimpleSettingsParams,
target(ctx: PluginContext) { return ctx.canvas3d?.props!; } })({
values(t, ctx) {
const renderer = t.renderer;
let renderStyle: SimpleSettingsParams['renderStyle']['defaultValue'] = 'custom' as any;
if (renderer) {
if (renderer.lightIntensity === 0 && renderer.ambientIntensity === 1 && renderer.roughness === 0.4 && renderer.metalness === 0) {
renderStyle = 'flat'
......@@ -107,31 +61,42 @@ export class SimpleSettingsControl extends PluginUIComponent {
renderStyle = 'metallic'
}
}
if (renderer.backgroundColor === ColorNames.white && this.plugin.canvas3d?.props.transparentBackground) {
background = { name: 'transparent', params: { } }
} else {
background = { name: 'opaque', params: { color: renderer.backgroundColor } }
}
}
return {
spin: !!this.plugin.canvas3d?.props.trackball.spin,
camera: this.plugin.canvas3d?.props.cameraMode,
background,
spin: !!t.trackball.spin,
camera: t.cameraMode,
background: (renderer.backgroundColor === ColorNames.white && t.transparentBackground)
? { name: 'transparent', params: { } }
: { name: 'opaque', params: { color: renderer.backgroundColor } },
renderStyle,
occlusion: this.plugin.canvas3d?.props.postprocessing.occlusionEnable,
outline: this.plugin.canvas3d?.props.postprocessing.outlineEnable,
fog: this.plugin.canvas3d ? this.plugin.canvas3d.props.cameraFog > 1 : false,
clipFar: this.plugin.canvas3d?.props.cameraClipFar
}
}
componentDidMount() {
this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
occlusion: t.postprocessing.occlusionEnable,
outline: t.postprocessing.outlineEnable,
fog: ctx.canvas3d ? t.cameraFog > 1 : false,
clipFar: t.cameraClipFar
};
},
update(s, t) {
t.trackball.spin = s.spin;
t.cameraMode = s.camera;
t.transparentBackground = s.background.name === 'transparent';
t.renderer.backgroundColor = s.background.name === 'transparent' ? ColorNames.white : s.background.params.color;
switch (s.renderStyle) {
case 'flat': Object.assign(t.renderer, { lightIntensity: 0, ambientIntensity: 1, roughness: 0.4, metalness: 0 }); break;
case 'matte': Object.assign(t.renderer, { lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 1, metalness: 0 }); break;
case 'glossy': Object.assign(t.renderer, { lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 0.4, metalness: 0 }); break;
case 'metallic': Object.assign(t.renderer, { lightIntensity: 0.6, ambientIntensity: 0.4, roughness: 0.6, metalness: 0.4 }); break;
}
render() {
return <ParameterControls params={SimpleSettingsParams} values={this.values} onChange={this.setSettings} />
t.postprocessing.occlusionEnable = s.occlusion;
if (s.occlusion) {
t.postprocessing.occlusionBias = 0.5;
t.postprocessing.occlusionRadius = 64;
}
t.postprocessing.outlineEnable = s.outline;
t.cameraFog = s.fog ? 50 : 0;
t.cameraClipFar = s.clipFar;
},
apply(settings, ctx) {
return PluginCommands.Canvas3D.SetSettings.dispatch(ctx, { settings });
}
})
\ No newline at end of file
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { ParamDefinition as PD } from './param-definition';
import produce from 'immer'
import { Mutable } from './type-helpers';
export interface ParamMapping<S, T, Ctx> {
params(ctx: Ctx): PD.For<S>,
getTarget(ctx: Ctx): T,
getValues(t: T, ctx: Ctx): S,
update(s: S, ctx: Ctx): T,
apply(t: T, ctx: Ctx): void | Promise<void>
}
export function ParamMapping<S, T, Ctx>(def: {
params: ((ctx: Ctx) => PD.For<S>) | PD.For<S>,
target(ctx: Ctx): T
}): (options: {
values(t: T, ctx: Ctx): S,
update(s: S, t: Mutable<T>, ctx: Ctx): void,
apply?(t: T, ctx: Ctx): void | Promise<void>
}) => ParamMapping<S, T, Ctx> {
return ({ values, update, apply }) => ({
params: typeof def.params === 'function' ? def.params as any : ctx => def.params,
getTarget: def.target,
getValues: values,
update(s, ctx) {
const t = def.target(ctx);
return produce(t, t1 => update(s, t1 as any, ctx));
},
apply: apply ? apply : () => { }
});
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment