Skip to content

Commit

Permalink
Add toggle color layer visibility button (#3943)
Browse files Browse the repository at this point in the history
* added toggle button and visibilty configuration for each color layer

* added changelog entry and adjusted the documentation

* applied feedback

* changed naming to disabled and added tooltip when there are disabled layers

* removed console log

* disabled other options when layer is disabled

* fixed typo

* applied feedback and changed icon to a switch

* made code pretty
  • Loading branch information
MichaelBuessemeyer authored Mar 29, 2019
1 parent cdb38f1 commit 0105048
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md).
- Segmentation ID mappings can now be used in volume and hybrid tracings. [#3949](https://github.com/scalableminds/webknossos/pull/3949)
- A maximize-button was added to the viewports in the annotation view. Maximization can also be toggled with the `.` shortcut. [#3876](https://github.com/scalableminds/webknossos/pull/3876)
- [webknossos-connect](https://github.com/scalableminds/webknossos-connect) now starts with webKnossos on local and development instances by default. [#3913](https://github.com/scalableminds/webknossos/pull/3913)
- Added a button for each color layer to enable/disable the layer. [#3943](https://github.com/scalableminds/webknossos/pull/3943)
- Paginated routes now send a `X-Total-Count` HTTP header which shows how many entries were found in total. [#3899](https://github.com/scalableminds/webknossos/pull/3899)

### Changed
Expand Down
2 changes: 2 additions & 0 deletions docs/tracing_ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,9 @@ For multi-layer datasets, each layer can be adjusted separately.
#### Colors
- `Brightness`: Increase / Decrease the brightness of the data layer.
- `Contrast`: Increase / Decrease the contrast of the data layer.
- `Opacity`: Increase / Decrease the opacity of the data layer.
- `Color`: Every data layer can be colored to make them easily identifiable. By default, all layers have a white overlay, showing the true, raw black & white data.
- `Visibility`: Use the eye icon on the right side of the name of the data layer to enable/disable this layer.

#### Segmentation
- `Segmentation Opacity`: Increases / Decreases the opacity of the segmentation layer. A low value will make the segmentation almost transparent letting you see the underlying data layers more clearly. A high value will make the segmentation opaque which is useful for adjusting and reviewing the exact fit of the segmentation layer. Only possible if your dataset has a segmentation layer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class DatasetImportView extends React.PureComponent<Props, State> {
brightness: 0,
contrast: 1,
color: [255, 255, 255],
isDisabled: false,
};
const datasetDefaultConfiguration = (await getDatasetDefaultConfiguration(
this.props.datasetId,
Expand Down
9 changes: 6 additions & 3 deletions frontend/javascripts/libs/datasource.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@
"properties": {
"name": { "type": "string" },
"category": { "enum": ["color", "segmentation"] },
"elementClass": { "enum": ["uint8", "uint16", "uint24", "uint32", "uint64", "float,", "double", "int8", "int16", "int32", "int64"] }
"elementClass": {
"enum": ["uint8", "uint16", "uint24", "uint32", "uint64", "float,", "double", "int8", "int16", "int32", "int64"]
}
},
"required": ["name", "category", "elementClass"]
},
Expand Down Expand Up @@ -128,9 +130,10 @@
"properties": {
"brightness": { "type": "number" },
"contrast": { "type": "number" },
"color": { "$ref": "#/definitions/types::Vector3" }
"color": { "$ref": "#/definitions/types::Vector3" },
"isDisabled": { "type": "boolean" }
},
"required": ["brightness", "contrast", "color"]
"required": ["brightness", "contrast", "color", "isDisabled"]
}
}
}
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/libs/datasource.types.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,6 @@ export type LayerUserConfiguration = {
brightness: number,
contrast: number,
color: Vector3,
isDisabled: boolean,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ class PlaneMaterialFactory {
updateUniformsForLayer(settings: DatasetLayerConfiguration, name: string): void {
this.uniforms[`${name}_brightness`].value = settings.brightness / 255;
this.uniforms[`${name}_contrast`].value = settings.contrast;
this.uniforms[`${name}_alpha`].value = settings.alpha / 100;
this.uniforms[`${name}_alpha`].value = settings.isDisabled ? 0 : settings.alpha / 100;

if (settings.color != null) {
const color = this.convertColor(settings.color);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ function SettingsReducer(state: OxalisState, action: Action): OxalisState {
contrast: 1,
color: [255, 255, 255],
alpha: 100,
isDisabled: false,
},
initialLayerSettings[layer.name],
);
Expand All @@ -122,7 +123,7 @@ function SettingsReducer(state: OxalisState, action: Action): OxalisState {
};
}
case "SET_VIEW_MODE": {
const allowedModes = state.tracing.restrictions.allowedModes;
const { allowedModes } = state.tracing.restrictions;
if (allowedModes.includes(action.viewMode)) {
return updateTemporaryConfig(state, { viewMode: action.viewMode });
} else {
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export type DatasetLayerConfiguration = {|
+brightness: number,
+contrast: number,
+alpha: number,
+isDisabled: boolean,
|};

export type LoadingStrategy = "BEST_QUALITY_FIRST" | "PROGRESSIVE_QUALITY";
Expand Down
50 changes: 42 additions & 8 deletions frontend/javascripts/oxalis/view/settings/dataset_settings_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @flow
*/

import { Col, Collapse, Divider, Icon, Row, Select, Tag, Tooltip } from "antd";
import { Col, Collapse, Divider, Icon, Row, Select, Switch, Tag, Tooltip } from "antd";
import type { Dispatch } from "redux";
import { connect } from "react-redux";
import * as React from "react";
Expand Down Expand Up @@ -39,8 +39,8 @@ import constants, {
} from "oxalis/constants";
import messages, { settings } from "messages";

const Panel = Collapse.Panel;
const Option = Select.Option;
const { Panel } = Collapse;
const { Option } = Select;

type DatasetSettingsProps = {|
datasetConfiguration: DatasetConfiguration,
Expand All @@ -65,20 +65,35 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps> {
isLastLayer: boolean,
) => {
const isRGB = isRgb(this.props.dataset, layerName);
const { brightness, contrast, alpha, color } = layer;
const { brightness, contrast, alpha, color, isDisabled } = layer;
return (
<div key={layerName}>
<Row>
<Col span={24}>
{/* TODO Maybe use the new antd icons instead of the switch when upgrading antd. */}
<Tooltip title={isDisabled ? "Enable" : "Disable"} placement="top">
{/* This div is necessary for the tooltip to be displayed */}
<div style={{ display: "inline-block", marginRight: 8 }}>
<Switch
size="small"
onChange={val => this.props.onChangeLayer(layerName, "isDisabled", !val)}
checked={!isDisabled}
/>
</div>
</Tooltip>
<span style={{ fontWeight: 700 }}>{layerName}</span>
<Tag style={{ cursor: "default", marginLeft: 8 }} color={isRGB && "#1890ff"}>
{isRGB ? "24-bit" : "8-bit"} Layer
</Tag>
<Tooltip title="If you are having trouble finding your data, webKnossos can try to find a position which contains data.">
<Icon
type="scan"
onClick={() => this.handleFindData(layerName)}
style={{ float: "right", marginTop: 4, cursor: "pointer" }}
onClick={!isDisabled ? () => this.handleFindData(layerName) : () => {}}
style={{
float: "right",
marginTop: 4,
cursor: !isDisabled ? "pointer" : "not-allowed",
}}
/>
</Tooltip>
</Col>
Expand All @@ -90,6 +105,7 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps> {
step={5}
value={brightness}
onChange={_.partial(this.props.onChangeLayer, layerName, "brightness")}
disabled={isDisabled}
/>
<NumberSliderSetting
label="Contrast"
Expand All @@ -98,19 +114,22 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps> {
step={0.1}
value={contrast}
onChange={_.partial(this.props.onChangeLayer, layerName, "contrast")}
disabled={isDisabled}
/>
<NumberSliderSetting
label="Opacity"
min={0}
max={100}
value={alpha}
onChange={_.partial(this.props.onChangeLayer, layerName, "alpha")}
disabled={isDisabled}
/>
<ColorSetting
label="Color"
value={Utils.rgbToHex(color)}
onChange={_.partial(this.props.onChangeLayer, layerName, "color")}
className="ant-btn"
disabled={isDisabled}
/>
{!isLastLayer && <Divider />}
</div>
Expand Down Expand Up @@ -176,16 +195,31 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps> {
);
}

renderPanelHeader = (hasInvisibleLayers: boolean) =>
hasInvisibleLayers ? (
<span>
Color Layers
<Tooltip title="Not all layers are currently visible.">
<Icon type="exclamation-circle-o" style={{ marginLeft: 16, color: "coral" }} />
</Tooltip>
</span>
) : (
"Color Layers"
);

render() {
const { layers } = this.props.datasetConfiguration;
const colorSettings = Object.entries(layers).map((entry, index) =>
// $FlowFixMe Object.entries returns mixed for Flow
this.getColorSettings(entry, index, index === _.size(layers) - 1),
);

const hasInvisibleLayers =
Object.keys(layers).find(
layerName => layers[layerName].isDisabled || layers[layerName].alpha === 0,
) != null;
return (
<Collapse bordered={false} defaultActiveKey={["1", "2", "3", "4"]}>
<Panel header="Color Layers" key="1">
<Panel header={this.renderPanelHeader(hasInvisibleLayers)} key="1">
{colorSettings}
</Panel>
{this.props.hasSegmentation ? this.getSegmentationPanel() : null}
Expand Down
34 changes: 28 additions & 6 deletions frontend/javascripts/oxalis/view/settings/setting_input_views.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ type NumberSliderSettingProps = {
max: number,
min: number,
step: number,
disabled: boolean,
};

export class NumberSliderSetting extends React.PureComponent<NumberSliderSettingProps> {
static defaultProps = {
min: 1,
step: 1,
disabled: false,
};

_onChange = (_value: number) => {
Expand All @@ -27,15 +29,22 @@ export class NumberSliderSetting extends React.PureComponent<NumberSliderSetting
};

render() {
const { value, label, max, min, step, onChange } = this.props;
const { value, label, max, min, step, onChange, disabled } = this.props;

return (
<Row type="flex" align="middle">
<Col span={9}>
<label className="setting-label">{label}</label>
</Col>
<Col span={8}>
<Slider min={min} max={max} onChange={onChange} value={value} step={step} />
<Slider
min={min}
max={max}
onChange={onChange}
value={value}
step={step}
disabled={disabled}
/>
</Col>
<Col span={5}>
<InputNumber
Expand All @@ -45,6 +54,7 @@ export class NumberSliderSetting extends React.PureComponent<NumberSliderSetting
value={value}
onChange={this._onChange}
size="small"
disabled={disabled}
/>
</Col>
</Row>
Expand Down Expand Up @@ -337,24 +347,30 @@ type ColorSettingPropTypes = {
value: string,
label: string,
onChange: (value: Vector3) => void,
disabled: boolean,
};

export class ColorSetting extends React.PureComponent<ColorSettingPropTypes> {
static defaultProps = {
disabled: false,
};

onColorChange = (evt: SyntheticInputEvent<>) => {
this.props.onChange(Utils.hexToRgb(evt.target.value));
};

render() {
const { label, value, disabled } = this.props;
return (
<Row className="margin-bottom" align="top">
<Col span={9}>
<label className="setting-label">{this.props.label}</label>
<label className="setting-label">{label}</label>
</Col>
<Col span={15}>
<div
id="color-picker-wrapper"
style={{
backgroundColor: this.props.value,
backgroundColor: value,
display: "block",
width: 16,
height: 16,
Expand All @@ -364,9 +380,15 @@ export class ColorSetting extends React.PureComponent<ColorSettingPropTypes> {
>
<input
type="color"
style={{ opacity: 0, display: "block", border: "none", cursor: "pointer" }}
style={{
opacity: 0,
display: "block",
border: "none",
cursor: disabled ? "not-allowed" : "pointer",
}}
onChange={this.onColorChange}
value={this.props.value}
value={value}
disabled={disabled}
/>
</div>
</Col>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ const datasetConfigOverrides: { [key: string]: DatasetConfiguration } = {
ROI2017_wkw_fallback: {
fourBit: false,
interpolation: true,
layers: { color: { color: [255, 255, 255], contrast: 1, brightness: 0, alpha: 100 } },
layers: {
color: { color: [255, 255, 255], contrast: 1, brightness: 0, alpha: 100, isDisabled: true },
},
quality: 0,
segmentationOpacity: 0,
highlightHoveredCellId: true,
Expand Down

0 comments on commit 0105048

Please sign in to comment.