org.dawnsci.plotting.tools.diffraction.DiffractionImageAugmenter.java Source code

Java tutorial

Introduction

Here is the source code for org.dawnsci.plotting.tools.diffraction.DiffractionImageAugmenter.java

Source

/*
 * Copyright (c) 2012 Diamond Light Source Ltd.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.dawnsci.plotting.tools.diffraction;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.measure.unit.NonSI;
import javax.vecmath.Vector3d;

import org.dawb.common.ui.menu.MenuAction;
import org.dawb.common.ui.plot.roi.ResolutionRing;
import org.dawnsci.plotting.tools.Activator;
import org.eclipse.dawnsci.analysis.api.diffraction.DetectorProperties;
import org.eclipse.dawnsci.analysis.api.diffraction.DetectorPropertyEvent;
import org.eclipse.dawnsci.analysis.api.diffraction.DiffractionCrystalEnvironment;
import org.eclipse.dawnsci.analysis.api.diffraction.DiffractionCrystalEnvironmentEvent;
import org.eclipse.dawnsci.analysis.api.diffraction.IDetectorPropertyListener;
import org.eclipse.dawnsci.analysis.api.diffraction.IDiffractionCrystalEnvironmentListener;
import org.eclipse.dawnsci.analysis.api.diffraction.DetectorPropertyEvent.EventType;
import org.eclipse.dawnsci.analysis.api.io.ILoaderService;
import org.eclipse.dawnsci.analysis.api.metadata.IDiffractionMetadata;
import org.eclipse.dawnsci.analysis.api.roi.IROI;
import org.eclipse.dawnsci.analysis.dataset.roi.EllipticalROI;
import org.eclipse.dawnsci.analysis.dataset.roi.HyperbolicROI;
import org.eclipse.dawnsci.analysis.dataset.roi.LinearROI;
import org.eclipse.dawnsci.analysis.dataset.roi.ParabolicROI;
import org.eclipse.dawnsci.analysis.dataset.roi.PointROI;
import org.eclipse.dawnsci.plotting.api.IPlottingSystem;
import org.eclipse.dawnsci.plotting.api.region.ILockableRegion;
import org.eclipse.dawnsci.plotting.api.region.IROIListener;
import org.eclipse.dawnsci.plotting.api.region.IRegion;
import org.eclipse.dawnsci.plotting.api.region.ROIEvent;
import org.eclipse.dawnsci.plotting.api.region.RegionUtils;
import org.eclipse.dawnsci.plotting.api.region.IRegion.RegionType;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.diamond.scisoft.analysis.crystallography.CalibrantSelectedListener;
import uk.ac.diamond.scisoft.analysis.crystallography.CalibrantSelectionEvent;
import uk.ac.diamond.scisoft.analysis.crystallography.CalibrantSpacing;
import uk.ac.diamond.scisoft.analysis.crystallography.CalibrationFactory;
import uk.ac.diamond.scisoft.analysis.crystallography.CalibrationStandards;
import uk.ac.diamond.scisoft.analysis.crystallography.HKL;
import uk.ac.diamond.scisoft.analysis.diffraction.DSpacing;
import uk.ac.diamond.sda.meta.page.DiffractionMetadataCompositeEvent;
import uk.ac.diamond.sda.meta.page.IDiffractionMetadataCompositeListener;

/**
 * Class to augment a diffraction image with beam centre, rings, etc. It has actions available for adding to a menu
 */
public class DiffractionImageAugmenter implements IDetectorPropertyListener, IDiffractionCrystalEnvironmentListener,
        IDiffractionMetadataCompositeListener, CalibrantSelectedListener {

    private static DiffractionImageAugmenter activeAugmenter;
    /**
     * Actions should be static so that opening multiple files in an editor,
     * plots the same ring configuration without having to manually choose.
     */
    private static Action beamCentre;
    private static Action standardRings;
    private static Action iceRings;
    private static Action calibrantRings;

    static {
        standardRings = new Action("Standard rings", Activator.getImageDescriptor("/icons/standard_rings.png")) {
            @Override
            public void run() {
                if (activeAugmenter == null)
                    return;
                activeAugmenter.drawStandardRings(isChecked());
            }
        };
        standardRings.setChecked(false);

        iceRings = new Action("Ice rings", Activator.getImageDescriptor("/icons/ice_rings.png")) {
            @Override
            public void run() {
                if (activeAugmenter == null)
                    return;
                activeAugmenter.drawIceRings(isChecked());
            }
        };
        iceRings.setChecked(false);

        calibrantRings = new Action("Calibrant", Activator.getImageDescriptor("/icons/calibrant_rings.png")) {
            @Override
            public void run() {
                if (activeAugmenter == null)
                    return;
                activeAugmenter.drawCalibrantRings(isChecked(),
                        CalibrationFactory.getCalibrationStandards().getCalibrant());
            }
        };
        calibrantRings.setChecked(false);

        beamCentre = new Action("Beam centre", Activator.getImageDescriptor("/icons/beam_centre.png")) {
            @Override
            public void run() {
                if (activeAugmenter == null)
                    return;
                activeAugmenter.drawBeamCentre(isChecked());
            }
        };
        beamCentre.setChecked(false);
    }

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

    private IPlottingSystem plottingSystem;
    private DetectorProperties detprop;
    private DiffractionCrystalEnvironment diffenv;
    private IRegion crosshairs;
    private IRegion beamPosition;
    private IROIListener roilistener;

    private enum RING_TYPE {
        ICE, STANDARD, CALIBRANT, BEAM_CENTRE, BEAM_POSITION_HANDLE;
    }

    private double[] imageCentrePC;

    protected final static double[] iceResolution = new double[] { 3.897, 3.669, 3.441, 2.671, 2.249, 2.072, 1.948,
            1.918, 1.883, 1.721 };// angstrom

    /**
     * Create and tie augmenter to a plotting system
     * @param system
     */
    public DiffractionImageAugmenter(IPlottingSystem system) {
        plottingSystem = system;
        if (activeAugmenter == null)
            activeAugmenter = this;
        resROIs = new ArrayList<IROI>();

        roilistener = new IROIListener.Stub() {

            @Override
            public void roiDragged(ROIEvent evt) {
                updateBeamCentre(evt, false);
            }

            @Override
            public void roiChanged(ROIEvent evt) {
                updateBeamCentre(evt, true);
            }

            private void updateBeamCentre(ROIEvent evt, boolean force) {
                if (evt.getROI() != null && evt.getROI() instanceof PointROI) {
                    PointROI roi = (PointROI) evt.getROI();
                    if (evt.getSource() != null && evt.getSource() instanceof IRegion) {
                        IRegion region = (IRegion) evt.getSource();
                        if (region.getUserObject() == RING_TYPE.BEAM_POSITION_HANDLE) {
                            forceRedraw = force;
                            if (detprop != null)
                                detprop.setBeamCentreCoords(roi.getPoint());
                        }
                    }
                }
            }
        };
    }

    private boolean active = true;
    private IDiffractionMetadata dmd;
    private List<IROI> resROIs;
    private boolean centreMoved = false;
    private boolean forceRedraw = false;

    /**
     * @return list of ROIs representing resolution rings
     */
    public List<IROI> getResolutionROIs() {
        return resROIs;
    }

    public void activate() {
        activeAugmenter = this;
        active = true;
        updateAll();
        //registerListeners(true);
    }

    public void deactivate(boolean leaveRegions) {
        if (activeAugmenter == this)
            activeAugmenter = null;
        active = false;

        if (!leaveRegions) {
            if (crosshairs != null && plottingSystem != null) {
                plottingSystem.removeRegion(crosshairs);
                crosshairs = null;
            }

            for (RING_TYPE rt : RING_TYPE.values())
                removeConics(rt);
        }
        registerListeners(false);
        if (beamPosition != null)
            beamPosition.removeROIListener(roilistener);
        beamPosition = null;
    }

    /**
     * Set image centre (used in fall-back if detector is not available)
     * @param coords
     */
    public void setImageCentre(double... coords) {
        imageCentrePC = coords;
    }

    public boolean isShowingBeamCenter() {
        return beamCentre.isChecked();
    }

    public void drawBeamCentre(boolean isChecked) {
        if (!active)
            return; // We are likely off screen.
        beamCentre.setChecked(isChecked);

        if (isChecked) {
            DecimalFormat df = new DecimalFormat("#.##");
            if (detprop != null) {
                double[] beamCentrePC = detprop.getBeamCentreCoords();
                if (beamCentrePC[0] == 0) // ensure there are no negative zeros
                    beamCentrePC[0] = 0;
                if (beamCentrePC[1] == 0)
                    beamCentrePC[1] = 0;
                double length = (1
                        + Math.sqrt(detprop.getPx() * detprop.getPx() + detprop.getPy() * detprop.getPy()) * 0.01);
                String label = df.format(beamCentrePC[0]) + "px, " + df.format(beamCentrePC[1]) + "px";
                drawCrosshairs(beamCentrePC, length, ColorConstants.red, ColorConstants.black, "beam centre",
                        label);
            } else if (imageCentrePC != null) {
                String label = df.format(imageCentrePC[0]) + "px, " + df.format(imageCentrePC[1]) + "px";
                drawCrosshairs(imageCentrePC, imageCentrePC[1] / 50, ColorConstants.red, ColorConstants.black,
                        "beam centre", label);
            }
        } else if (crosshairs != null) {
            plottingSystem.removeRegion(crosshairs);
            crosshairs = null;
        }
    }

    @Override
    public void calibrantSelectionChanged(CalibrantSelectionEvent evt) {
        CalibrationStandards standards = (CalibrationStandards) evt.getSource();
        /**
         * Important take CalibrantSpacing from event because it might be a standards the user is
         * editing in the preference editor.
         */
        if (calibrantRings != null)
            drawCalibrantRings(calibrantRings.isChecked(), standards.getCalibrant());
    }

    public void drawCalibrantRings(boolean isChecked, CalibrantSpacing spacing) {
        if (!active)
            return; // We are likely off screen.

        if (isChecked) {
            if (!calibrantRings.isChecked()) // override setting
                calibrantRings.setChecked(true);
            List<ResolutionRing> calibrantRingsList = new ArrayList<ResolutionRing>(7);

            for (HKL hkl : spacing.getHKLs()) {
                final double d = Double.valueOf(hkl.getD().doubleValue(NonSI.ANGSTROM));
                try {
                    calibrantRingsList.add(new ResolutionRing(d, true, ColorConstants.red, true, false, false));
                } catch (NumberFormatException e) {
                    logger.warn("Could not parse item {} in standard distances", d);
                }
            }
            drawResolutionConics(calibrantRingsList, "calibrant", RING_TYPE.CALIBRANT);
            drawResolutionBeamPosition();
        } else {
            hideConics(RING_TYPE.CALIBRANT);
            hideConics(RING_TYPE.BEAM_POSITION_HANDLE);
            beamPosition = null;
        }
    }

    private void drawCrosshairs(double[] beamCentre, double length, Color colour, Color labelColour,
            String nameStub, String labelText) {
        if (!active)
            return; // We are likely off screen.

        if (crosshairs == null) {
            try {
                final String regionName = RegionUtils.getUniqueName(nameStub, plottingSystem);
                crosshairs = plottingSystem.createRegion(regionName, RegionType.LINE);
                crosshairs.setUserRegion(false);
            } catch (Exception e) {
                logger.error("Can't create region", e);
                return;
            }

            final LinearROI lroi = new LinearROI(length, 0);
            lroi.setMidPoint(beamCentre);
            lroi.setCrossHair(true);
            crosshairs.setROI(lroi);
            crosshairs.setRegionColor(colour);
            crosshairs.setAlpha(100);
            crosshairs.setShowPosition(false);
            crosshairs.setUserObject(RING_TYPE.BEAM_CENTRE);

            crosshairs.setLabel(labelText);
            crosshairs.setShowLabel(true);

            plottingSystem.addRegion(crosshairs);
            crosshairs.setMobile(false); // NOTE: Must be done **AFTER** calling the
            // addRegion method.
            crosshairs.toBack();
        } else {
            LinearROI lroi = (LinearROI) crosshairs.getROI();
            lroi.setLength(length);
            lroi.setMidPoint(beamCentre);
            crosshairs.setRegionColor(colour);
            crosshairs.setLabel(labelText);
            crosshairs.toBack();
            //         crosshairs.setROI(lroi);
        }
    }

    /**
     * Add actions to given menu
     * @param menu
     */
    public void addActions(final MenuAction menu) {

        menu.add(standardRings);
        menu.add(iceRings);
        menu.add(calibrantRings);
        menu.add(beamCentre);
    }

    public void addBeamCenterAction(IContributionManager man) {
        man.add(beamCentre);
    }

    private void hideConics(RING_TYPE marker) {
        if (plottingSystem == null)
            return;
        if (plottingSystem.getRegions() == null)
            return;
        for (IRegion region : plottingSystem.getRegions()) {
            try {
                if (region.getUserObject() != marker)
                    continue;
                region.setVisible(false);
            } catch (Throwable ne) {
                // They can delete regions themselves.
            }
        }
    }

    private void removeConics(RING_TYPE marker) {
        if (plottingSystem == null)
            return;
        if (plottingSystem.getRegions() == null)
            return;
        for (IRegion region : plottingSystem.getRegions()) {
            try {
                if (region.getUserObject() != marker)
                    continue;
                plottingSystem.removeRegion(region);
            } catch (Throwable ne) {
                // They can delete regions themselves.
            }
        }
    }

    private void drawResolutionConics(List<ResolutionRing> conicList, String typeName, final RING_TYPE marker) {

        if (!centreMoved || forceRedraw) {
            Display.getDefault().syncExec(new Runnable() {
                @Override
                public void run() {
                    removeConics(marker);
                }
            });
        }
        resROIs.clear();
        if (!active) // We are likely to be off-screen
            return;
        if (detprop == null || diffenv == null)
            return;

        int nRings = conicList.size();
        double[] alphas = new double[nRings];
        for (int i = 0; i < nRings; i++) {
            try {
                alphas[i] = DSpacing.coneAngleFromDSpacing(diffenv, conicList.get(i).getResolution());
            } catch (Exception e) {
                alphas[i] = Double.NaN;
            }
        }
        IROI[] rois = DSpacing.conicsFromAngles(detprop, alphas);
        if (rois == null)
            return;

        if (centreMoved && !forceRedraw) {
            List<IRegion> regions = new ArrayList<IRegion>();
            if (plottingSystem == null)
                return;
            if (plottingSystem.getRegions() == null)
                return;
            for (IRegion region : plottingSystem.getRegions()) {
                try {
                    if (region.getUserObject() != marker)
                        continue;
                    regions.add(region);
                } catch (Throwable ne) {
                }
            }

            if (rois.length < regions.size())
                return;

            for (int i = 0; i < regions.size(); i++) {
                IROI conic = rois[i];
                resROIs.add(conic);
                if (conic != null)
                    updateResolutionConic(conic, regions.get(i));
            }

        } else {
            for (int i = 0; i < nRings; i++) {
                IROI conic = rois[i];
                resROIs.add(conic);
                if (conic != null)
                    drawResolutionConic(conicList.get(i), conic, typeName + i, marker, false);
            }
        }
    }

    private void updateResolutionConic(IROI roi, IRegion region) {
        if (region.getROI().getClass() != roi.getClass()) {
            region.setVisible(false);
            return;
        }
        region.setROI(roi);
    }

    private void drawResolutionConic(ResolutionRing ring, IROI roi, String name, RING_TYPE marker,
            boolean isMobile) {
        RegionType type = getConicRegionType(roi);
        if (type == null)
            return;
        final String regionName = RegionUtils.getUniqueName(name, plottingSystem);
        IRegion region;
        try {
            region = plottingSystem.createRegion(regionName, type);
        } catch (Exception e) {
            logger.error("Could not create region", e);
            return;
        }

        Color colour = ring.getColour();
        plottingSystem.addRegion(region);
        region.setROI(roi);
        region.setRegionColor(colour);
        region.setAlpha(100);
        region.setUserRegion(true);

        DecimalFormat df = new DecimalFormat("#.00");
        region.setLabel(df.format(ring.getResolution()) + "");
        region.setShowLabel(true);
        if (crosshairs != null) {
            crosshairs.setShowLabel(true);
            crosshairs.setRegionColor(colour);
        }

        region.setShowPosition(false);
        region.setUserRegion(false);
        region.setVisible(true);
        region.setMobile(isMobile);
        region.setUserObject(marker);
        region.toBack();
        if (isMobile) {
            ILockableRegion lockable = region instanceof ILockableRegion ? (ILockableRegion) region : null;
            if (lockable == null)
                return;
            lockable.setCentreMovable(true);
            lockable.setOuterMovable(false);
        }
    }

    private static RegionType getConicRegionType(IROI roi) {
        RegionType type = null;
        if (roi instanceof EllipticalROI) {
            type = RegionType.ELLIPSE;
        } else if (roi instanceof ParabolicROI) {
            type = RegionType.PARABOLA;
        } else if (roi instanceof HyperbolicROI) {
            type = RegionType.HYPERBOLA;
        }
        return type;
    }

    private void drawIceRings(boolean isChecked) {
        if (!active)
            return; // We are likely off screen.

        if (isChecked) {
            List<ResolutionRing> iceRingsList = new ArrayList<ResolutionRing>(7);
            for (double res : iceResolution) {
                iceRingsList.add(new ResolutionRing(res, true, ColorConstants.blue, true, false, false));
            }
            drawResolutionConics(iceRingsList, "ice", RING_TYPE.ICE);
        } else {
            hideConics(RING_TYPE.ICE);
        }
    }

    private void drawStandardRings(boolean isChecked) {
        if (!active)
            return; // We are likely off screen.

        if (isChecked && diffenv != null && detprop != null) {
            List<ResolutionRing> standardRingsList = new ArrayList<ResolutionRing>(7);
            Double numberEvenSpacedRings = 6.0;
            double lambda = diffenv.getWavelength();
            Vector3d longestVector = detprop.getLongestVector();
            double step = longestVector.length() / numberEvenSpacedRings;
            double d, twoThetaSpacing;
            Vector3d toDetectorVector = new Vector3d();
            Vector3d beamVector = detprop.getBeamCentrePosition();
            for (int i = 0; i < numberEvenSpacedRings - 1; i++) {
                // increase the length of the vector by step.
                longestVector.normalize();
                longestVector.scale(step + (step * i));

                toDetectorVector.add(beamVector, longestVector);
                twoThetaSpacing = beamVector.angle(toDetectorVector);
                d = lambda / Math.sin(twoThetaSpacing);
                standardRingsList.add(new ResolutionRing(d, true, ColorConstants.yellow, false, true, true));
            }
            drawResolutionConics(standardRingsList, "standard", RING_TYPE.STANDARD);
        } else {
            hideConics(RING_TYPE.STANDARD);
        }
    }

    private void drawResolutionBeamPosition() {
        if (!active)
            return; // We are likely off screen.

        if (detprop == null)
            return;

        double[] beamCentrePC = detprop.getBeamCentreCoords();
        if (beamCentrePC[0] == 0) // ensure there are no negative zeros
            beamCentrePC[0] = 0;
        if (beamCentrePC[1] == 0)
            beamCentrePC[1] = 0;

        if (beamPosition == null) {
            try {
                final String regionName = RegionUtils.getUniqueName("Calibrant beam position", plottingSystem);
                beamPosition = plottingSystem.createRegion(regionName, RegionType.POINT);
            } catch (Exception e) {
                logger.error("Can't create region", e);
                return;
            }

            final PointROI proi = new PointROI(beamCentrePC);
            beamPosition.setROI(proi);
            beamPosition.setRegionColor(ColorConstants.red);
            beamPosition.setAlpha(100);
            beamPosition.setUserRegion(false);
            beamPosition.setShowPosition(false);
            beamPosition.setUserObject(RING_TYPE.BEAM_POSITION_HANDLE);

            beamPosition.addROIListener(roilistener);

            plottingSystem.addRegion(beamPosition);
            beamPosition.setMobile(true); // NOTE: Must be done **AFTER** calling the
            // addRegion method.
        } else {
            PointROI proi = (PointROI) beamPosition.getROI();
            proi.setPoint(beamCentrePC);
            beamPosition.setRegionColor(ColorConstants.red);

        }
    }

    public void dispose() {
        ILoaderService service = (ILoaderService) PlatformUI.getWorkbench().getService(ILoaderService.class);
        deactivate(service.getLockedDiffractionMetaData() != null);
    }

    @Override
    public void detectorPropertiesChanged(DetectorPropertyEvent evt) {

        if (evt.getType() == EventType.BEAM_CENTRE) {
            centreMoved = true;
        }
        updateAll();

    }

    private void updateAll() {
        beamCentre.run();
        standardRings.run();
        iceRings.run();
        calibrantRings.run();

        if ((!centreMoved || forceRedraw) && beamPosition != null)
            beamPosition.toFront();

        centreMoved = false;
        forceRedraw = false;
    }

    @Override
    public void diffractionCrystalEnvironmentChanged(DiffractionCrystalEnvironmentEvent evt) {
        standardRings.run();
        iceRings.run();
        calibrantRings.run();
    }

    @Override
    public void diffractionMetadataCompositeChanged(DiffractionMetadataCompositeEvent evt) {
        if (evt.hasBeamCentreChanged()) {
            if (beamCentre.isChecked()) {
                beamCentre.setChecked(false);
                if (crosshairs != null) {
                    plottingSystem.removeRegion(crosshairs);
                    crosshairs = null;
                }
            } else {
                beamCentre.setChecked(true);
                drawBeamCentre(true);
            }
        }
    }

    public void setDiffractionMetadata(IDiffractionMetadata metadata) {
        if (diffenv != null && detprop != null)
            registerListeners(false);
        dmd = metadata;
        diffenv = metadata.getDiffractionCrystalEnvironment();
        detprop = metadata.getDetector2DProperties();
        registerListeners(true);
        imageCentrePC = detprop != null ? detprop.getBeamCentreCoords() : null;
        updateAll();
    }

    public IDiffractionMetadata getDiffractionMetadata() {
        return dmd;
    }

    public boolean isActive() {
        return active;
    }

    private void registerListeners(boolean register) {

        if (register) {
            CalibrationFactory.addCalibrantSelectionListener(this);
        } else {
            CalibrationFactory.removeCalibrantSelectionListener(this);
        }

        if (diffenv != null) {
            if (register) {
                diffenv.addDiffractionCrystalEnvironmentListener(this);
            } else {
                diffenv.removeDiffractionCrystalEnvironmentListener(this);
            }
        } else {
            logger.error("DiffractionCrystalEnvironment is null!");
        }
        if (detprop != null) {
            if (register) {
                detprop.addDetectorPropertyListener(this);
            } else {
                detprop.removeDetectorPropertyListener(this);
            }
        } else {
            logger.error("DetectorProperties is null!");
        }

    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (active ? 1231 : 1237);
        result = prime * result + ((detprop == null) ? 0 : detprop.hashCode());
        result = prime * result + ((diffenv == null) ? 0 : diffenv.hashCode());
        result = prime * result + Arrays.hashCode(imageCentrePC);
        result = prime * result + ((plottingSystem == null) ? 0 : plottingSystem.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        DiffractionImageAugmenter other = (DiffractionImageAugmenter) obj;
        if (active != other.active)
            return false;
        if (detprop == null) {
            if (other.detprop != null)
                return false;
        } else if (!detprop.equals(other.detprop))
            return false;
        if (diffenv == null) {
            if (other.diffenv != null)
                return false;
        } else if (!diffenv.equals(other.diffenv))
            return false;
        if (!Arrays.equals(imageCentrePC, other.imageCentrePC))
            return false;
        if (plottingSystem == null) {
            if (other.plottingSystem != null)
                return false;
        } else if (!plottingSystem.equals(other.plottingSystem))
            return false;
        return true;
    }

}