ColorByWidget

Source

AdvancedView.js
import React from 'react';
import PropTypes from 'prop-types';

import style from 'PVWStyle/ReactWidgets/ColorByWidget.mcss';

import PresetListWidget from '../PresetListWidget';
import ScalarRangeWidget from '../ScalarRangeWidget';
import PieceWiseFunctionEditorWidget from '../PieceWiseFunctionEditorWidget';
import PieceWiseGaussianFunctionEditorWidget from '../PieceWiseGaussianFunctionEditorWidget';

export default class ColorByWidget extends React.Component {
constructor(props) {
super(props);
this.state = { activeAdvanceView: '0' };

// Bind callback
this.updatePreset = this.updatePreset.bind(this);
this.updateRange = this.updateRange.bind(this);
this.updateActiveView = this.updateActiveView.bind(this);
}

updatePreset(name) {
if (this.props.onChange) {
this.props.onChange({
type: 'updatePreset',
representation: this.props.representation.id,
preset: name,
});
}
}

updateRange(options) {
options.proxyId = this.props.source.id;
if (this.props.onChange) {
this.props.onChange({
type: 'updateScalarRange',
options,
});
}
}

updateActiveView(event) {
const activeAdvanceView = event.target.dataset.idx;
this.setState({ activeAdvanceView });
}

render() {
return (
<div className={this.props.visible ? style.advancedView : style.hidden}>
<div className={style.advancedViewControl}>
<i
data-idx="0"
onClick={this.updateActiveView}
className={
this.state.activeAdvanceView === '0'
? style.activePresetIcon
: style.presetIcon
}
/>
<i
data-idx="1"
onClick={this.updateActiveView}
className={
this.state.activeAdvanceView === '1'
? style.activeRangeIcon
: style.rangeIcon
}
/>
<i
data-idx="2"
onClick={this.updateActiveView}
className={
this.state.activeAdvanceView === '2'
? style.activeOpacityIcon
: style.opacityIcon
}
/>
</div>
<div className={style.advancedViewContent}>
<PresetListWidget
visible={this.state.activeAdvanceView === '0'}
onChange={this.updatePreset}
presets={this.props.presets}
/>
<ScalarRangeWidget
visible={this.state.activeAdvanceView === '1'}
min={this.props.min}
max={this.props.max}
onApply={this.updateRange}
/>
{this.state.activeAdvanceView === '2' && !this.props.useGaussian ? (
<PieceWiseFunctionEditorWidget
points={this.props.opacityPoints}
rangeMin={this.props.min}
rangeMax={this.props.max}
onChange={this.props.onOpacityPointsChange}
onEditModeChange={this.props.onOpacityEditModeChange}
height={this.props.opacityEditorSize[1]}
width={this.props.opacityEditorSize[0]}
hidePointControl={this.props.hidePointControl}
/>
) : null}
{this.state.activeAdvanceView === '2' && this.props.useGaussian ? (
<PieceWiseGaussianFunctionEditorWidget
points={this.props.opacityPoints}
gaussians={this.props.gaussians}
bgImage={this.props.scalarBar}
rangeMin={this.props.min}
rangeMax={this.props.max}
onChange={this.props.onOpacityPointsChange}
onEditModeChange={this.props.onOpacityEditModeChange}
height={this.props.opacityEditorSize[1]}
width={this.props.opacityEditorSize[0]}
/>
) : null}
</div>
</div>
);
}
}

// <i
// data-idx="3"
// onClick={this.updateActiveView}
// className={this.state.activeAdvanceView === '3' ? style.activeColorEditIcon : style.colorEditIcon}
// ></i>

ColorByWidget.propTypes = {
max: PropTypes.number,
min: PropTypes.number,
onChange: PropTypes.func,
presets: PropTypes.object,
representation: PropTypes.object,
scalarBar: PropTypes.string,
source: PropTypes.object,
visible: PropTypes.bool,
hidePointControl: PropTypes.bool,
opacityPoints: PropTypes.array,
onOpacityPointsChange: PropTypes.func,
onOpacityEditModeChange: PropTypes.func,
opacityEditorSize: PropTypes.array,
useGaussian: PropTypes.bool,
gaussians: PropTypes.array,
};

ColorByWidget.defaultProps = {
opacityEditorSize: [-1, 96],

max: undefined,
min: undefined,
onChange: undefined,
presets: undefined,
representation: undefined,
scalarBar: undefined,
source: undefined,
visible: undefined,
hidePointControl: undefined,
opacityPoints: undefined,
onOpacityPointsChange: undefined,
onOpacityEditModeChange: undefined,
useGaussian: undefined,
gaussians: undefined,
};
index.js
import React from 'react';
import PropTypes from 'prop-types';

import style from 'PVWStyle/ReactWidgets/ColorByWidget.mcss';

import AdvancedView from './AdvancedView';

const SEP = ':|:';

function doubleToHex(number) {
let str = Math.floor(number * 255).toString(16);
while (str.length < 2) {
str = `0${str}`;
}
return str;
}

export default class ColorByWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
advancedView: false,
colorValue: SEP,
colorValues: [],
representationValue: '',
representationValues: [],
scalarBarVisible: false,
solidColor: '#fff',
};

// Bind callback
this.onRepresentationChange = this.onRepresentationChange.bind(this);
this.onColorChange = this.onColorChange.bind(this);
this.updateState = this.updateState.bind(this);
this.toggleScalarBar = this.toggleScalarBar.bind(this);
this.toggleAdvancedView = this.toggleAdvancedView.bind(this);
}

componentWillMount() {
this.updateState(this.props);
}

componentWillReceiveProps(nextProps) {
this.updateState(nextProps);
}

onRepresentationChange(event) {
const representationValue = event.target.value;
this.setState({ representationValue });
if (this.props.onChange) {
this.props.onChange({
type: 'propertyChange',
changeSet: [
{
id: this.props.representation.id,
name: 'Representation',
value: representationValue,
},
],
});
}
}

onColorChange(event) {
let scalarBarVisible = this.state.scalarBarVisible;
const colorValue = event.target.value;
const [arrayLocation, arrayName] = colorValue.split(SEP);
const colorMode = arrayName ? 'array' : 'SOLID';
const vectorMode = 'Magnitude';
const vectorComponent = 0;
const rescale = false;

if (colorMode === 'SOLID') {
scalarBarVisible = false;
}

this.setState({ colorValue, scalarBarVisible, colorMode });
if (this.props.onChange) {
this.props.onChange({
type: 'colorBy',
representation: this.props.representation.id,
arrayLocation,
arrayName,
colorMode,
vectorMode,
vectorComponent,
rescale,
});
}
}

updateState(props) {
if (!props.source || !props.representation) {
return;
}

const extractRepProp = (p) => p.name === 'Representation';
const removeFieldArray = (a) => a.location !== 'FIELDS';
const representationValues = props.representation.ui.filter(
extractRepProp
)[0].values;
const representationValue = props.representation.properties.filter(
extractRepProp
)[0].value;
const colorValues = [{ name: 'Solid color' }].concat(
props.source.data.arrays.filter(removeFieldArray)
);
const colorValue = props.representation.colorBy.array
.filter((v, i) => i < 2)
.join(SEP);
const scalarBarVisible = !!props.representation.colorBy.scalarBar;
const solidColor = `#${props.representation.colorBy.color
.map(doubleToHex)
.join('')}`;

const colorMode = colorValue.split(SEP)[1] ? 'array' : 'SOLID';

this.setState({
representationValues,
representationValue,
colorValues,
colorValue,
scalarBarVisible,
solidColor,
colorMode,
});
}

toggleScalarBar() {
let scalarBarVisible = !this.state.scalarBarVisible;

if (this.state.colorMode === 'SOLID') {
scalarBarVisible = false;
}

this.setState({ scalarBarVisible });
if (this.props.onChange) {
this.props.onChange({
type: 'scalarBar',
source: this.props.source.id,
representation: this.props.representation.id,
visible: scalarBarVisible,
});
}
}

toggleAdvancedView() {
const advancedView = !this.state.advancedView;
this.setState({ advancedView });
}

render() {
if (!this.props.source || !this.props.representation) {
return null;
}

return (
<div className={[style.container, this.props.className].join(' ')}>
<div className={style.line}>
<i className={style.representationIcon} />
<select
className={style.input}
value={this.state.representationValue}
onChange={this.onRepresentationChange}
>
{this.state.representationValues.map((v, idx) => (
<option key={idx} value={v}>
{v}
</option>
))}
</select>
</div>
<div className={style.line}>
<i className={style.colorIcon} />
<select
className={style.input}
value={this.state.colorValue}
onChange={this.onColorChange}
>
{this.state.colorValues.map((c, idx) => (
<option
key={idx}
value={c.location ? [c.location, c.name].join(SEP) : ''}
>
{c.location
? `(${c.location === 'POINTS' ? 'p' : 'c'}${c.size}) ${
c.name
}`
: c.name}
</option>
))}
</select>
</div>
<div className={style.line}>
<i
onClick={this.toggleAdvancedView}
className={
this.state.advancedView
? style.advanceIconOn
: style.advanceIconOff
}
/>
{this.props.scalarBar &&
this.state.colorValue &&
this.state.colorValue.split(SEP)[1].length ? (
<img
onClick={this.toggleScalarBar}
className={style.scalarBar}
src={`data:image/png;base64,${this.props.scalarBar}`}
alt="ScalarBar"
/>
) : (
<div
className={style.scalarBar}
style={{ backgroundColor: this.state.solidColor }}
/>
)}
<i
onClick={this.toggleScalarBar}
className={
this.state.scalarBarVisible
? style.scalarBarIconOn
: style.scalarBarIconOff
}
/>
</div>
<AdvancedView visible={this.state.advancedView} {...this.props} />
</div>
);
}
}

ColorByWidget.propTypes = {
className: PropTypes.string,
max: PropTypes.number,
min: PropTypes.number,
onChange: PropTypes.func,
presets: PropTypes.object,
representation: PropTypes.object,
scalarBar: PropTypes.string,
source: PropTypes.object,
opacityPoints: PropTypes.array,
onOpacityPointsChange: PropTypes.func,
opacityEditorSize: PropTypes.array,
useGaussian: PropTypes.bool,
gaussians: PropTypes.array,
};

ColorByWidget.defaultProps = {
className: '',
useGaussian: false,
min: 0,
max: 1,
gaussians: undefined,
onChange: undefined,
presets: undefined,
representation: undefined,
scalarBar: undefined,
source: undefined,
opacityPoints: undefined,
onOpacityPointsChange: undefined,
opacityEditorSize: undefined,
};