uk.ac.diamond.scisoft.analysis.rcp.views.HistogramView.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.diamond.scisoft.analysis.rcp.views.HistogramView.java

Source

/*-
 * Copyright 2013 Diamond Light Source Ltd.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package uk.ac.diamond.scisoft.analysis.rcp.views;

import gda.observable.IObservable;
import gda.observable.IObserver;

import java.awt.Color;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.collections.map.AbstractReferenceMap;
import org.apache.commons.collections.map.ReferenceMap;
import org.dawnsci.plotting.jreality.tick.TickFormatting;
import org.dawnsci.plotting.jreality.tool.AreaSelectEvent;
import org.eclipse.dawnsci.analysis.api.downsample.DownsampleMode;
import org.eclipse.dawnsci.analysis.dataset.impl.Dataset;
import org.eclipse.dawnsci.analysis.dataset.impl.DatasetUtils;
import org.eclipse.dawnsci.analysis.dataset.impl.DoubleDataset;
import org.eclipse.dawnsci.analysis.dataset.impl.Stats;
import org.eclipse.dawnsci.plotting.api.histogram.functions.AbstractMapFunction;
import org.eclipse.dawnsci.plotting.api.histogram.functions.GlobalColourMaps;
import org.eclipse.dawnsci.plotting.api.histogram.functions.SpecialExposureFunction;
import org.eclipse.dawnsci.plotting.api.jreality.core.AxisMode;
import org.eclipse.dawnsci.plotting.api.jreality.core.ScaleType;
import org.eclipse.dawnsci.plotting.api.jreality.impl.Plot1DAppearance;
import org.eclipse.dawnsci.plotting.api.jreality.impl.Plot1DGraphTable;
import org.eclipse.dawnsci.plotting.api.jreality.impl.Plot1DStyles;
import org.eclipse.dawnsci.plotting.api.jreality.impl.PlotException;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Slider;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.diamond.scisoft.analysis.axis.AxisValues;
import uk.ac.diamond.scisoft.analysis.dataset.function.Downsample;
import uk.ac.diamond.scisoft.analysis.dataset.function.Histogram;
import uk.ac.diamond.scisoft.analysis.rcp.AnalysisRCPActivator;
import uk.ac.diamond.scisoft.analysis.rcp.histogram.HistogramDataUpdate;
import uk.ac.diamond.scisoft.analysis.rcp.histogram.HistogramUI;
import uk.ac.diamond.scisoft.analysis.rcp.histogram.HistogramUpdate;
import uk.ac.diamond.scisoft.analysis.rcp.plotting.DataSetPlotter;
import uk.ac.diamond.scisoft.analysis.rcp.plotting.IPlotUI;
import uk.ac.diamond.scisoft.analysis.rcp.plotting.PlotWindow;
import uk.ac.diamond.scisoft.analysis.rcp.plotting.PlottingMode;
import uk.ac.diamond.scisoft.analysis.rcp.preference.PreferenceConstants;
import uk.ac.diamond.scisoft.analysis.rcp.util.FloatSpinner;

/**
 * View that shows a histogram of a DataSet object, it allows to navigate in 
 * the histogram and build a colour table mapping
 */
@Deprecated
public class HistogramView extends ViewPart implements SelectionListener, IObservable, IObserver {

    public class HistrogramUIUpdater implements Runnable {

        public boolean inqueue = true;

        @Override
        public void run() {
            inqueue = false;
            // Set the ranges from the data max and min
            histrogramUIUpdate();
        }
    }

    private static final Logger logger = LoggerFactory.getLogger(HistogramView.class);

    /**
     * 
     */
    public static final String ID = "uk.ac.diamond.scisoft.analysis.rcp.views.HistogramView"; //$NON-NLS-1$

    private static HashMap<String, Integer> colourSettings = new HashMap<String, Integer>();

    protected List<Dataset> histograms = null;
    protected List<IObserver> observers = Collections.synchronizedList(new LinkedList<IObserver>());

    protected DataSetPlotter histogramPlotter;
    protected AxisValues xAxis = null;
    protected Composite parent;
    protected int histogramSize = 128;

    private static final int SLIDERSECTIONS = 1000; // Number of discrete points the sliders for max and min sliders have
    protected Dataset data = null;

    private CCombo cmbColourMap;
    private Composite composite;
    private Composite container;
    private Button btnExpertMode;
    protected CCombo cmbAlpha;
    protected CCombo cmbRedColour;
    protected CCombo cmbGreenColour;
    protected CCombo cmbBlueColour;
    protected Button btnGreenInverse;
    protected Button btnBlueInverse;
    protected Button btnAlphaInverse;
    protected Button btnRedInverse;
    protected double specialMinThreshold = 0.1;
    protected double specialMaxThreshold = 0.9;
    protected int curRedSelect = 3;
    protected int curGreenSelect = 3;
    protected int curBlueSelect = 3;
    protected int curAlphaSelect = 3;
    protected boolean curRedInverse = false;
    protected boolean curGreenInverse = false;
    protected boolean curBlueInverse = false;
    protected boolean curAlphaInverse = false;

    private Spinner spnMinValue;
    private Spinner spnMaxValue;
    private Slider sldMinValue;
    private Slider sldMaxValue;
    private FloatSpinner spnRangeStart;
    private FloatSpinner spnRangeStop;
    private Histogram histogramFunc;
    protected IPlotUI histogramUI = null;

    class MaxMin {
        public double max = Double.NaN;
        public double min = Double.NaN;
    }

    private MaxMin currentMaxMin;
    //private Map<Integer, MaxMin> cachedMaxMin;
    private ReferenceMap cachedMaxMin;

    private boolean autoContrast = true;
    private boolean lockRange = false;
    private boolean useLog = false;
    private String id;
    private String sId;
    private Composite advancedComp;
    private Group expertGroup;
    private Group threshPanel;
    private Group rangePanel;

    private HistrogramUIUpdater latestHistrogramUIUpdater = null; //used to prevent putting many events to update histogram on UI thread. -on e1 at a time

    /**
     * Default constructor
     */

    public HistogramView() {
        histogramFunc = new Histogram(histogramSize);
        xAxis = new AxisValues();
        cachedMaxMin = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT);
    }

    @Override
    public void init(IViewSite site) throws PartInitException {
        super.init(site);
        id = site.getId();
        sId = site.getSecondaryId();
        if (sId == null) {
            setPartName("Colour Mapping");
        } else {
            try {
                // You can see it's a histogram by looking but
                // you really need the file name to know which
                // view is corresponding to which file.
                setPartName("Colour Mapping: " + (new File(sId)).getName());
            } catch (Exception ne) {
                setPartName("Colour Mapping");
            }
        }
    }

    protected void buildToolbar() {
        IActionBars toolBar = getViewSite().getActionBars();
        histogramUI = new HistogramUI(this, toolBar, histogramPlotter);
        histogramUI.addIObserver(this);
        histogramPlotter.registerUI(histogramUI);
    }

    @Override
    public void createPartControl(Composite parent) {

        this.parent = parent;
        container = new Composite(parent, SWT.NONE);
        container.setLayout(new GridLayout(1, false));

        final Composite top = new Composite(container, SWT.NONE);
        top.setLayout(new GridLayout(3, false));
        top.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

        final Label scheme = new Label(top, SWT.NONE);
        scheme.setText("Colour Scheme");
        cmbColourMap = new CCombo(top, SWT.BORDER | SWT.READ_ONLY);
        cmbColourMap.setToolTipText("Change the color scheme.");
        cmbColourMap.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        btnExpertMode = new Button(top, SWT.CHECK);
        btnExpertMode.setText("Expert");
        btnExpertMode.setToolTipText("Enable expert mode for customising colour channels");
        btnExpertMode.addSelectionListener(this);

        advancedComp = new Composite(container, SWT.NONE);
        //controlGroup.setText("RGB Settings");   // Looks a bit busy with this label
        advancedComp.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
        advancedComp.setLayout(new GridLayout(3, false));

        expertGroup = new Group(advancedComp, SWT.NONE);
        expertGroup.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1));
        expertGroup.setLayout(new GridLayout(3, false));
        {
            Label lblRed = new Label(expertGroup, SWT.NONE);
            lblRed.setText("Red");
            cmbRedColour = new CCombo(expertGroup, SWT.BORDER | SWT.READ_ONLY);

            btnRedInverse = new Button(expertGroup, SWT.CHECK);
            btnRedInverse.setText("Inverse");
            btnRedInverse.addSelectionListener(this);

            Label lblGreen = new Label(expertGroup, SWT.NONE);
            lblGreen.setText("Green");
            cmbGreenColour = new CCombo(expertGroup, SWT.BORDER | SWT.READ_ONLY);
            btnGreenInverse = new Button(expertGroup, SWT.CHECK);
            btnGreenInverse.setText("Inverse");
            btnGreenInverse.addSelectionListener(this);

            Label lblBlue = new Label(expertGroup, SWT.NONE);
            lblBlue.setText("Blue");
            cmbBlueColour = new CCombo(expertGroup, SWT.BORDER | SWT.READ_ONLY);
            btnBlueInverse = new Button(expertGroup, SWT.CHECK);
            btnBlueInverse.setText("Inverse");
            btnBlueInverse.addSelectionListener(this);

            Label lblAlpha = new Label(expertGroup, SWT.NONE);
            lblAlpha.setText("Alpha");
            cmbAlpha = new CCombo(expertGroup, SWT.BORDER | SWT.READ_ONLY);
            btnAlphaInverse = new Button(expertGroup, SWT.CHECK);
            btnAlphaInverse.setText("Inverse");
            btnAlphaInverse.addSelectionListener(this);
        }
        expertGroup.setVisible(getPreferenceColourMapExpertMode());
        ((GridData) expertGroup.getLayoutData()).exclude = !getPreferenceColourMapExpertMode();

        threshPanel = new Group(advancedComp, SWT.NONE);
        threshPanel.setText("Mark's special threshhold"); // Looks a bit busy with this label
        threshPanel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
        threshPanel.setLayout(new GridLayout(2, false));
        {
            Label lblMinValue = new Label(threshPanel, SWT.NONE);
            lblMinValue.setText("Min threshold");
            spnMinValue = new Spinner(threshPanel, SWT.BORDER);
            spnMinValue.setMinimum(0);
            spnMinValue.setMaximum(100);
            spnMinValue.addSelectionListener(this);
            Label lblMaxValue = new Label(threshPanel, SWT.NONE);
            lblMaxValue.setText("Max threshold");
            spnMaxValue = new Spinner(threshPanel, SWT.BORDER);
            spnMaxValue.setMinimum(0);
            spnMaxValue.setMaximum(100);
            spnMaxValue.addSelectionListener(this);
        }
        threshPanel.setVisible(false);
        ((GridData) threshPanel.getLayoutData()).exclude = true;

        rangePanel = new Group(container, SWT.NONE);
        rangePanel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        rangePanel.setLayout(new GridLayout(3, false));
        rangePanel.setText("Histogram range");
        {
            Label lblStartValue = new Label(rangePanel, SWT.NONE);
            lblStartValue.setText("Minimum");
            spnRangeStart = new FloatSpinner(rangePanel, SWT.BORDER);
            GridData gridData = new GridData(SWT.NONE, SWT.NONE, true, false);
            spnRangeStart.setLayoutData(gridData);
            spnRangeStart.addSelectionListener(this);
            sldMinValue = new Slider(rangePanel, SWT.BORDER);
            gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
            gridData.minimumWidth = 90;
            sldMinValue.setLayoutData(gridData);
            sldMinValue.addSelectionListener(this);
            sldMinValue.setMinimum(0);
            sldMinValue.setMaximum(SLIDERSECTIONS);
            sldMinValue.setIncrement(1);
            Label lblStopValue = new Label(rangePanel, SWT.NONE);
            lblStopValue.setText("Maximum");
            gridData = new GridData(SWT.NONE, SWT.NONE, true, false);
            spnRangeStop = new FloatSpinner(rangePanel, SWT.BORDER);
            spnRangeStop.setLayoutData(gridData);
            spnRangeStop.addSelectionListener(this);
            spnRangeStart.setEnabled(false);
            spnRangeStop.setEnabled(false);
            sldMaxValue = new Slider(rangePanel, SWT.BORDER);
            gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
            gridData.minimumWidth = 90;
            sldMaxValue.setLayoutData(gridData);
            sldMaxValue.addSelectionListener(this);
            sldMaxValue.setMinimum(0);
            sldMaxValue.setMaximum(SLIDERSECTIONS);
            sldMaxValue.setIncrement(1);

            spnRangeStart.setEnabled(false);
            spnRangeStop.setEnabled(false);
            sldMinValue.setEnabled(false);
            sldMaxValue.setEnabled(false);
        }
        {
            composite = new Composite(container, SWT.NONE);
            composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
            composite.setLayout(new FillLayout());

            histogramPlotter = new DataSetPlotter(PlottingMode.BARCHART, composite, false);
            histogramPlotter.setXAxisLabel("Intensity Value");
            histogramPlotter.setYAxisLabel("Count");
            histogramPlotter.setAxisModes(AxisMode.CUSTOM, AxisMode.LINEAR, AxisMode.LINEAR);
            histogramPlotter.setXTickLabelFormat(TickFormatting.plainMode);
            histogramPlotter.setXAxisValues(xAxis, 1);

            Plot1DGraphTable colourTable = histogramPlotter.getColourTable();
            Plot1DAppearance plotApp = new Plot1DAppearance(Color.RED, Plot1DStyles.SOLID, "");
            colourTable.addEntryOnLegend(plotApp);
            plotApp = new Plot1DAppearance(Color.GREEN, Plot1DStyles.SOLID, "");
            colourTable.addEntryOnLegend(plotApp);
            plotApp = new Plot1DAppearance(Color.BLUE, Plot1DStyles.SOLID, "");
            colourTable.addEntryOnLegend(plotApp);
            plotApp = new Plot1DAppearance(Color.ORANGE, Plot1DStyles.SOLID, "");
            colourTable.addEntryOnLegend(plotApp);
        }

        cmbColourMap.addSelectionListener(this);
        cmbRedColour.addSelectionListener(this);
        cmbGreenColour.addSelectionListener(this);
        cmbBlueColour.addSelectionListener(this);
        cmbAlpha.addSelectionListener(this);
        buildToolbar();
        GlobalColourMaps.InitializeColourMaps();
        fillupColourComboBoxes();
        fillupColourMapBox();
        buildGradientImage();
    }

    /**
     * Clear out all entries in the colour map combo box
     */
    public void clearColourMapBox() {
        cmbColourMap.removeAll();
    }

    private void fillupColourMapBox() {
        for (int i = 0; i < GlobalColourMaps.colourMapNames.length; i++)
            cmbColourMap.add(GlobalColourMaps.colourMapNames[i]);
        if (colourSettings.get(getPartName()) == null)
            cmbColourMap.select(getPreferenceColourMapChoice());
        else
            cmbColourMap.select(colourSettings.get(getPartName()));

        if (cmbColourMap.getItem(cmbColourMap.getSelectionIndex()).equals("Mark's special")) {
            spnMinValue.setSelection((int) (specialMinThreshold * 100));
            spnMaxValue.setSelection((int) (specialMaxThreshold * 100));
            threshPanel.setVisible(true);
            ((GridData) threshPanel.getLayoutData()).exclude = false;
            container.layout();
        }

        cmbAlpha.select(2);
        curAlphaSelect = 2;
        buildSelection();
    }

    protected void buildSelection() {
        int selectNr = cmbColourMap.getSelectionIndex();
        int redSelect = GlobalColourMaps.colourSelectList.get(selectNr * 4);
        int greenSelect = GlobalColourMaps.colourSelectList.get(selectNr * 4 + 1);
        int blueSelect = GlobalColourMaps.colourSelectList.get(selectNr * 4 + 2);
        int alphaSelect = GlobalColourMaps.colourSelectList.get(selectNr * 4 + 3);
        cmbRedColour.select(Math.abs(redSelect));
        cmbGreenColour.select(Math.abs(greenSelect));
        cmbBlueColour.select(Math.abs(blueSelect));
        curAlphaSelect = cmbAlpha.getSelectionIndex();
        curRedSelect = cmbRedColour.getSelectionIndex();
        curGreenSelect = cmbGreenColour.getSelectionIndex();
        curBlueSelect = cmbBlueColour.getSelectionIndex();
        //   cmbAlpha.select(Math.abs(alphaSelect));
        if (redSelect < 0)
            btnRedInverse.setSelection(true);
        else
            btnRedInverse.setSelection(false);
        if (greenSelect < 0)
            btnGreenInverse.setSelection(true);
        else
            btnGreenInverse.setSelection(false);
        if (blueSelect < 0)
            btnBlueInverse.setSelection(true);
        else
            btnBlueInverse.setSelection(false);
        if (alphaSelect < 0)
            btnAlphaInverse.setSelection(false);
        curAlphaInverse = btnAlphaInverse.getSelection();
        curRedInverse = btnRedInverse.getSelection();
        curGreenInverse = btnGreenInverse.getSelection();
        curBlueInverse = btnBlueInverse.getSelection();
    }

    protected void fillupColourComboBoxes() {
        for (int i = 0; i < GlobalColourMaps.mappingFunctions.size(); i++) {
            String function = GlobalColourMaps.mappingFunctions.get(i).getMapFunctionName();
            cmbRedColour.add(function);
            cmbGreenColour.add(function);
            cmbBlueColour.add(function);
            cmbAlpha.add(function);
        }
    }

    protected void buildGradientImage() {
        GlobalColourMaps.mappingFunctions.get(0);
        AbstractMapFunction redFunc = GlobalColourMaps.mappingFunctions.get(cmbRedColour.getSelectionIndex());
        AbstractMapFunction greenFunc = GlobalColourMaps.mappingFunctions.get(cmbGreenColour.getSelectionIndex());
        AbstractMapFunction blueFunc = GlobalColourMaps.mappingFunctions.get(cmbBlueColour.getSelectionIndex());
        AbstractMapFunction alphaFunc = GlobalColourMaps.mappingFunctions.get(cmbAlpha.getSelectionIndex());
        boolean negRed = btnRedInverse.getSelection();
        boolean negGreen = btnGreenInverse.getSelection();
        boolean negBlue = btnBlueInverse.getSelection();
        boolean negAlpha = btnAlphaInverse.getSelection();
        histogramPlotter.applyColourCast(redFunc, greenFunc, blueFunc, alphaFunc, negRed, negGreen, negBlue,
                negAlpha, 0, 256);
        histogramPlotter.refresh(false);
    }

    private void notifyObservers(HistogramUpdate update) {
        Iterator<IObserver> iter = observers.iterator();
        while (iter.hasNext()) {
            IObserver listener = iter.next();
            listener.update(this, update);
        }
    }

    @Override
    public void setFocus() {
        // Nothing to do here
    }

    @Override
    public void widgetDefaultSelected(SelectionEvent e) {
        // Nothing to do here
    }

    protected boolean hasData() {
        return data != null;
    }

    protected void handleChange(@SuppressWarnings("unused") int channel) {
    }

    @Override
    public void widgetSelected(SelectionEvent e) {
        if (e.getSource().equals(cmbRedColour) || e.getSource().equals(cmbGreenColour)
                || e.getSource().equals(cmbBlueColour) || e.getSource().equals(cmbAlpha)
                || e.getSource().equals(btnRedInverse) || e.getSource().equals(btnGreenInverse)
                || e.getSource().equals(btnBlueInverse) || e.getSource().equals(btnAlphaInverse)) {
            curAlphaSelect = cmbAlpha.getSelectionIndex();
            curRedSelect = cmbRedColour.getSelectionIndex();
            curGreenSelect = cmbGreenColour.getSelectionIndex();
            curBlueSelect = cmbBlueColour.getSelectionIndex();
            curAlphaInverse = btnAlphaInverse.getSelection();
            curRedInverse = btnRedInverse.getSelection();
            curGreenInverse = btnGreenInverse.getSelection();
            curBlueInverse = btnBlueInverse.getSelection();
            if (hasData()) {
                if (e.getSource().equals(cmbRedColour))
                    handleChange(0);
                else if (e.getSource().equals(cmbGreenColour))
                    handleChange(1);
                else if (e.getSource().equals(cmbBlueColour))
                    handleChange(2);
                else if (e.getSource().equals(cmbAlpha))
                    handleChange(3);

                updateChannelGraphs();
                generateHistogramUpdate();
            }
            if (e.getSource().equals(cmbGreenColour) || e.getSource().equals(cmbRedColour)
                    || e.getSource().equals(btnRedInverse) || e.getSource().equals(btnGreenInverse)
                    || e.getSource().equals(btnBlueInverse) || e.getSource().equals(btnAlphaInverse))
                histogramPlotter.refresh(true);
            buildGradientImage();
        } else if (e.getSource().equals(cmbColourMap)) {
            colourSettings.put(getPartName(), cmbColourMap.getSelectionIndex());
            buildSelection();
            handleChange(0);
            handleChange(1);
            handleChange(2);
            if (hasData()) {
                updateChannelGraphs();
                generateHistogramUpdate();
            }
            histogramPlotter.refresh(true);
            buildGradientImage();
            if (cmbColourMap.getItem(cmbColourMap.getSelectionIndex()).equals("Mark's special")) {
                spnMinValue.setSelection((int) (specialMinThreshold * 100));
                spnMaxValue.setSelection((int) (specialMaxThreshold * 100));
                threshPanel.setVisible(true);
                ((GridData) threshPanel.getLayoutData()).exclude = false;
            } else {
                threshPanel.setVisible(false);
                ((GridData) threshPanel.getLayoutData()).exclude = true;
            }
            container.layout();
        } else if (e.getSource().equals(spnMinValue) || e.getSource().equals(spnMaxValue)) {
            SpecialExposureFunction redFunc = (SpecialExposureFunction) GlobalColourMaps.mappingFunctions
                    .get(cmbRedColour.getSelectionIndex());
            SpecialExposureFunction greenFunc = (SpecialExposureFunction) GlobalColourMaps.mappingFunctions
                    .get(cmbGreenColour.getSelectionIndex());
            SpecialExposureFunction blueFunc = (SpecialExposureFunction) GlobalColourMaps.mappingFunctions
                    .get(cmbBlueColour.getSelectionIndex());
            redFunc.setThresholds(spnMinValue.getSelection() / 100.0, spnMaxValue.getSelection() / 100.0);
            greenFunc.setThresholds(spnMinValue.getSelection() / 100.0, spnMaxValue.getSelection() / 100.0);
            blueFunc.setThresholds(spnMinValue.getSelection() / 100.0, spnMaxValue.getSelection() / 100.0);
            specialMaxThreshold = spnMaxValue.getSelection() / 100.0;
            specialMinThreshold = spnMinValue.getSelection() / 100.0;
            updateChannelGraphs();
            generateHistogramUpdate();
            histogramPlotter.refresh(true);
        } else if (e.getSource().equals(spnRangeStart.getControl())
                || e.getSource().equals(spnRangeStop.getControl())) {
            final double min = spnRangeStart.getDouble();
            final double max = spnRangeStop.getDouble();
            updateHistogramGraph(min, max);
        } else if (e.getSource().equals(sldMinValue) || e.getSource().equals(sldMaxValue)) {
            final double min = spnRangeStart.getMinimum()
                    + spnRangeStart.getRange() * (sldMinValue.getSelection() / (double) sldMinValue.getMaximum());
            final double max = spnRangeStart.getMinimum()
                    + spnRangeStart.getRange() * (sldMaxValue.getSelection() / (double) sldMaxValue.getMaximum());
            spnRangeStart.setDouble(min);
            spnRangeStop.setDouble(max);
            if (hasData())
                updateHistogramGraph(min, max);
        } else if (e.getSource().equals(btnExpertMode)) {
            expertGroup.setVisible(btnExpertMode.getSelection());
            ((GridData) expertGroup.getLayoutData()).exclude = !btnExpertMode.getSelection();
            container.layout();
        }

    }

    protected void generateHistogramUpdate() {
        HistogramUpdate update = new HistogramUpdate(GlobalColourMaps.mappingFunctions.get(curRedSelect),
                GlobalColourMaps.mappingFunctions.get(curGreenSelect),
                GlobalColourMaps.mappingFunctions.get(curBlueSelect),
                GlobalColourMaps.mappingFunctions.get(curAlphaSelect), btnRedInverse.getSelection(),
                btnGreenInverse.getSelection(), btnBlueInverse.getSelection(), btnAlphaInverse.getSelection(),
                currentMaxMin.min, currentMaxMin.max);
        notifyObservers(update);
    }

    protected void updateChannelGraphs() {
        if (histograms != null && histograms.size() > 1) {
            // remove red channel graph
            histograms.remove(0);
            // remove green channel graph
            histograms.remove(0);
            // remove blue channel graph
            histograms.remove(0);
            // remove alpha channel graph
            histograms.remove(0);
        }
        AbstractMapFunction redFunc = GlobalColourMaps.mappingFunctions.get(curRedSelect);
        AbstractMapFunction greenFunc = GlobalColourMaps.mappingFunctions.get(curGreenSelect);
        AbstractMapFunction blueFunc = GlobalColourMaps.mappingFunctions.get(curBlueSelect);
        AbstractMapFunction alphaFunc = GlobalColourMaps.mappingFunctions.get(curAlphaSelect);

        double maxValue = histograms.get(0).max().doubleValue();
        int currentSize = histograms.get(0).getSize();
        DoubleDataset redChannel = new DoubleDataset(currentSize);
        DoubleDataset greenChannel = new DoubleDataset(currentSize);
        DoubleDataset blueChannel = new DoubleDataset(currentSize);
        DoubleDataset alphaChannel = new DoubleDataset(currentSize);
        for (int i = 0; i < currentSize; i++) {
            double value = i / (double) currentSize;
            double redvalue = redFunc.getPoint(value);

            if (curRedInverse)
                redvalue = 1.0 - redvalue;

            redvalue *= maxValue;

            double greenvalue = greenFunc.getPoint(value);

            if (curGreenInverse)
                greenvalue = 1.0 - greenvalue;

            greenvalue *= maxValue;

            double bluevalue = blueFunc.getPoint(value);

            if (curBlueInverse)
                bluevalue = 1.0 - bluevalue;

            bluevalue *= maxValue;

            double alphavalue = alphaFunc.getPoint(value);

            if (curAlphaInverse)
                alphavalue = 1.0 - alphavalue;
            alphavalue *= maxValue;

            redChannel.set(Math.max(redvalue, 0.0), i);
            greenChannel.set(Math.max(greenvalue, 0.0), i);
            blueChannel.set(Math.max(bluevalue, 0.0), i);
            alphaChannel.set(Math.max(alphavalue, 0.0), i);
        }
        histograms.add(0, alphaChannel);
        histograms.add(0, blueChannel);
        histograms.add(0, greenChannel);
        histograms.add(0, redChannel);

        // Some logging to check on the histograms size
        logger.debug("number of histograms stored is {}", histograms.size());
        if (histograms.size() > 10) {
            logger.warn("Number of stored histograms is over expected levels, now at {}", histograms.size());
        }

        try {
            histogramPlotter.replaceAllPlots(histograms);
        } catch (PlotException e) {
            e.printStackTrace();
        }
    }

    private void updateHistogramGraph(double min, double max) {
        xAxis.clear();
        currentMaxMin.max = max;
        currentMaxMin.min = min;
        histogramFunc.setMinMax(min, max);
        histogramFunc.setIgnoreOutliers(true);
        // sanity check if there is actually a histogram live
        List<? extends Dataset> newHistogram = histogramFunc.value(data);
        Dataset histogram = newHistogram.get(0);
        if (histograms != null && histograms.size() > 1) {
            histograms.set(histograms.size() - 1, histogram);
        } else {
            histograms = new ArrayList<Dataset>();
            histograms.add(histogram);
        }

        // Some logging to check on the histograms size
        logger.debug("number of histograms stored is {}", histograms.size());
        if (histograms.size() > 10) {
            logger.warn("Number of stored histograms is over expected levels, now at {}", histograms.size());
        }

        xAxis.setValues(DatasetUtils.linSpace(min, max, Math.max(1, histogram.getSize() + 1), Dataset.FLOAT64));
        histogramPlotter.setXAxisValues(xAxis, 1);
        generateHistogramUpdate();
        updateChannelGraphs();
        histogramPlotter.refresh(true);
    }

    private void autoRangeHistogram() {
        histogramPlotter.clearZoomHistory();
        double[] m;
        if (autoContrast && data.getRank() == 2) {
            try {
                final int[] shape = data.getShape();
                if (shape[0] > 512 && shape[1] > 512) {
                    int yReduce = (int) Math.ceil(shape[0] / 512.0);
                    int xReduce = (int) Math.ceil(shape[1] / 512.0);
                    Downsample sample = new Downsample(DownsampleMode.MAXIMUM, xReduce, yReduce);
                    m = Stats.quantile((Dataset) sample.value(data).get(0), getPreferenceAutoContrastLo(),
                            getPreferenceAutoContrastHi());
                } else
                    m = Stats.quantile(data, getPreferenceAutoContrastLo(), getPreferenceAutoContrastHi());
            } catch (Exception e) {
                m = new double[] { data.min().doubleValue(), data.max().doubleValue() };
            }
        } else {
            m = new double[] { data.min().doubleValue(), data.max().doubleValue() };
        }

        if (Double.compare(m[1], m[0]) <= 0)
            m[1] = m[0] + PreferenceConstants.MINIMUM_CONTRAST_DELTA / 100.0;

        currentMaxMin.max = m[1];
        currentMaxMin.min = m[0];
    }

    public void createInitialHistogram() {
        if (!hasData())
            return;

        MaxMin oldMM = currentMaxMin;
        currentMaxMin = (MaxMin) cachedMaxMin.get(data.hashCode());
        if (currentMaxMin == null) {
            currentMaxMin = new MaxMin();
            cachedMaxMin.put(data.hashCode(), currentMaxMin);
        }
        if (lockRange && oldMM != null) {
            currentMaxMin.max = oldMM.max;
            currentMaxMin.min = oldMM.min;
        } else {
            if (autoContrast || Double.isNaN(currentMaxMin.max) || Double.isNaN(currentMaxMin.min))
                autoRangeHistogram();
        }

        if (latestHistrogramUIUpdater == null || !latestHistrogramUIUpdater.inqueue) {
            latestHistrogramUIUpdater = new HistrogramUIUpdater();
            parent.getDisplay().asyncExec(latestHistrogramUIUpdater);
        }
    }

    @Override
    public void update(Object theObserved, Object changeCode) {
        if (theObserved.equals(histogramUI)) {
            if (changeCode instanceof AreaSelectEvent) {
                AreaSelectEvent event = (AreaSelectEvent) changeCode;
                final double min = event.getPosition()[0];
                final double max = event.getPosition()[1];
                parent.getDisplay().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        updateHistogramGraph(min, max);
                    }
                });
            }
        }
        if (theObserved instanceof PlotWindow) {
            if (changeCode instanceof HistogramDataUpdate) {
                if (parent != null) {
                    data = ((HistogramDataUpdate) changeCode).getDataset();
                    createInitialHistogram();
                }
            }
        }
    }

    public void setData(HistogramDataUpdate histogramDataUpdate) {
        data = histogramDataUpdate.getDataset();
        createInitialHistogram();
    }

    /**
     * @return Returns the id.
     */
    public String getId() {
        return id;
    }

    @Override
    public void addIObserver(IObserver anIObserver) {
        observers.add(anIObserver);
    }

    @Override
    public void deleteIObserver(IObserver anIObserver) {
        observers.remove(anIObserver);
    }

    @Override
    public void deleteIObservers() {
        observers.clear();
    }

    @Override
    public void dispose() {
        // store current settings back into preferences
        IPreferenceStore preferenceStore = AnalysisRCPActivator.getDefault().getPreferenceStore();
        String pName = getPartName();
        Integer setting = colourSettings.get(pName);
        if (setting != null)
            preferenceStore.setValue(pName + "." + PreferenceConstants.PLOT_VIEW_PLOT2D_COLOURMAP, setting);
        preferenceStore.setValue(pName + "." + PreferenceConstants.PLOT_VIEW_PLOT2D_SCALING, (useLog ? 1 : 0));
        deleteIObservers();
        if (histogramPlotter != null)
            histogramPlotter.cleanUp();
        data = null;
        if (histogramUI != null) {
            histogramUI.deactivate(false);
            histogramUI.dispose();
        }
        if (!cmbAlpha.isDisposed())
            cmbAlpha.removeSelectionListener(this);
        if (!cmbRedColour.isDisposed())
            cmbRedColour.removeSelectionListener(this);
        if (!cmbGreenColour.isDisposed())
            cmbGreenColour.removeSelectionListener(this);
        if (!cmbBlueColour.isDisposed())
            cmbBlueColour.removeSelectionListener(this);
        if (!btnGreenInverse.isDisposed())
            btnGreenInverse.removeSelectionListener(this);
        if (!btnBlueInverse.isDisposed())
            btnBlueInverse.removeSelectionListener(this);
        if (!btnAlphaInverse.isDisposed())
            btnAlphaInverse.removeSelectionListener(this);
        if (!btnRedInverse.isDisposed())
            btnRedInverse.removeSelectionListener(this);

        super.dispose();
    }

    public void setAutoContrastScaling(boolean checked) {
        autoContrast = checked;
        if (hasData()) {
            spnRangeStart.setEnabled(!checked);
            spnRangeStop.setEnabled(!checked);
        }
        rangePanel.setEnabled(!checked);
    }

    /**
     * Lock/Unlock the histogram range
     * @param isLocked true if the histogram range should be locked otherwise false
     */
    public void setHistogramLock(boolean isLocked) {
        lockRange = isLocked;
    }

    public void setGraphLines(boolean checked) {
        if (histogramPlotter != null) {
            Plot1DGraphTable table = histogramPlotter.getColourTable();
            for (int i = 0; i < 4; i++)
                table.getLegendEntry(i).setVisible(checked);
        }
        histogramPlotter.updateAllAppearance();
        histogramPlotter.refresh(false);
    }

    public void setScaling(ScaleType newScaling) {
        useLog = (newScaling != ScaleType.LINEAR);
        histogramPlotter.setYAxisScaling(newScaling);
    }

    private int getPreferenceColourMapChoice() {
        IPreferenceStore preferenceStore = AnalysisRCPActivator.getDefault().getPreferenceStore();
        // try to retrieve setting from last session
        int i = preferenceStore.getInt(getPartName() + "." + PreferenceConstants.PLOT_VIEW_PLOT2D_COLOURMAP);
        if (i != 0)
            return i;
        return preferenceStore.isDefault(PreferenceConstants.PLOT_VIEW_PLOT2D_COLOURMAP)
                ? preferenceStore.getDefaultInt(PreferenceConstants.PLOT_VIEW_PLOT2D_COLOURMAP)
                : preferenceStore.getInt(PreferenceConstants.PLOT_VIEW_PLOT2D_COLOURMAP);
    }

    private boolean getPreferenceColourMapExpertMode() {
        IPreferenceStore preferenceStore = AnalysisRCPActivator.getDefault().getPreferenceStore();
        // try to retrieve setting from last session
        if (preferenceStore.getBoolean(getPartName() + "." + PreferenceConstants.PLOT_VIEW_PLOT2D_CMAP_EXPERT))
            return true;
        return preferenceStore.isDefault(PreferenceConstants.PLOT_VIEW_PLOT2D_CMAP_EXPERT)
                ? preferenceStore.getDefaultBoolean(PreferenceConstants.PLOT_VIEW_PLOT2D_CMAP_EXPERT)
                : preferenceStore.getBoolean(PreferenceConstants.PLOT_VIEW_PLOT2D_CMAP_EXPERT);
    }

    private double getPreferenceAutoContrastLo() {
        IPreferenceStore preferenceStore = AnalysisRCPActivator.getDefault().getPreferenceStore();
        int v = preferenceStore.isDefault(PreferenceConstants.PLOT_VIEW_PLOT2D_AUTOCONTRAST_LOTHRESHOLD)
                ? preferenceStore.getDefaultInt(PreferenceConstants.PLOT_VIEW_PLOT2D_AUTOCONTRAST_LOTHRESHOLD)
                : preferenceStore.getInt(PreferenceConstants.PLOT_VIEW_PLOT2D_AUTOCONTRAST_LOTHRESHOLD);
        return v / 100.0;
    }

    private double getPreferenceAutoContrastHi() {
        IPreferenceStore preferenceStore = AnalysisRCPActivator.getDefault().getPreferenceStore();
        int v = preferenceStore.isDefault(PreferenceConstants.PLOT_VIEW_PLOT2D_AUTOCONTRAST_HITHRESHOLD)
                ? preferenceStore.getDefaultInt(PreferenceConstants.PLOT_VIEW_PLOT2D_AUTOCONTRAST_HITHRESHOLD)
                : preferenceStore.getInt(PreferenceConstants.PLOT_VIEW_PLOT2D_AUTOCONTRAST_HITHRESHOLD);
        return v / 100.0;
    }

    private void histrogramUIUpdate() {
        if (!hasData()) {
            System.err.println("No data available in histogram view!");
            return;
        }
        double max = data.max().doubleValue();
        double min = data.min().doubleValue();
        double step = max - min;
        if (step != 0) {
            int prec = (int) Math.ceil(-Math.log10(step / 100));
            int wide = (int) Math.ceil(Math.log10(Math.max(Math.abs(max), Math.abs(min))));
            if (wide <= 0)
                wide = 2;
            if (prec <= 0)
                prec = 1;
            spnRangeStart.setFormat(prec + wide, prec);
            spnRangeStop.setFormat(prec + wide, prec);
        }
        spnRangeStart.setMaximum(max);
        spnRangeStart.setMinimum(min);
        spnRangeStop.setMaximum(max);
        spnRangeStop.setMinimum(min);

        // Set the values from the max and min which were specified by the colour table (this persists locked values)
        spnRangeStop.setDouble(currentMaxMin.max);
        spnRangeStart.setDouble(currentMaxMin.min);

        // Now the slider positions need to be calculated
        double minProportion = (currentMaxMin.min - min) / step;
        sldMinValue.setSelection((int) Math.floor((sldMinValue.getMaximum() * minProportion)));
        double maxProportion = (currentMaxMin.max - min) / step;
        sldMaxValue.setSelection((int) Math.floor((sldMinValue.getMaximum() * maxProportion)));

        spnRangeStart.setEnabled(true);
        spnRangeStop.setEnabled(true);
        sldMinValue.setEnabled(true);
        sldMaxValue.setEnabled(true);

        updateHistogramGraph(currentMaxMin.min, currentMaxMin.max);
        buildGradientImage();
    }
}