diff --git a/Code/Mantid/MantidPlot/CMakeLists.txt b/Code/Mantid/MantidPlot/CMakeLists.txt index 5d4d9ea06427e35ef69c270c5678ede94820e9c9..1716ea2bf1011cda47fd21b273d9173309cc7319 100644 --- a/Code/Mantid/MantidPlot/CMakeLists.txt +++ b/Code/Mantid/MantidPlot/CMakeLists.txt @@ -204,6 +204,8 @@ set ( MANTID_SRCS src/Mantid/AbstractMantidLog.cpp src/Mantid/InstrumentWidget/UnwrappedSurface.cpp src/Mantid/InstrumentWidget/ProjectionSurface.cpp src/Mantid/InstrumentWidget/Projection3D.cpp + src/Mantid/InstrumentWidget/PeakMarker2D.cpp + src/Mantid/InstrumentWidget/PeakOverlay.cpp src/Mantid/InstrumentWidget/OneCurvePlot.cpp src/Mantid/InstrumentWidget/CollapsiblePanel.cpp src/Mantid/InstrumentWidget/DetSelector.cpp @@ -411,6 +413,8 @@ set ( MANTID_HDRS src/Mantid/AbstractMantidLog.h src/Mantid/InstrumentWidget/UnwrappedCylinder.h src/Mantid/InstrumentWidget/UnwrappedSphere.h src/Mantid/InstrumentWidget/UnwrappedSurface.h + src/Mantid/InstrumentWidget/PeakMarker2D.h + src/Mantid/InstrumentWidget/PeakOverlay.h src/Mantid/InstrumentWidget/ProjectionSurface.h src/Mantid/InstrumentWidget/Projection3D.h src/Mantid/InstrumentWidget/OneCurvePlot.h @@ -653,6 +657,7 @@ set ( MANTID_MOC_FILES src/Mantid/AlgMonitor.h src/Mantid/InstrumentWidget/OneCurvePlot.h src/Mantid/InstrumentWidget/CollapsiblePanel.h src/Mantid/InstrumentWidget/InstrumentActor.h + src/Mantid/InstrumentWidget/PeakOverlay.h src/Mantid/InstrumentWidget/ProjectionSurface.h src/Mantid/InstrumentWidget/Shape2DCollection.h ) diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.cpp index 2884c7ba39d83e303b39f171daa74d33ba6be5b1..c3fd3742df61d9988d4ba7b2b17c5eac7ef7b2e2 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.cpp @@ -4,6 +4,7 @@ #include "CollapsiblePanel.h" #include "InstrumentActor.h" #include "ProjectionSurface.h" +#include "PeakMarker2D.h" #include "MantidKernel/ConfigService.h" #include "MantidAPI/AnalysisDataService.h" @@ -344,8 +345,13 @@ void InstrumentWindowPickTab::getBinMinMaxIndex(size_t wi,size_t& imin, size_t& } } +/** + * Plot data for a detector. + * @param detid :: ID of the detector to be plotted. + */ void InstrumentWindowPickTab::plotSingle(int detid) { + m_plot->clearLabels(); InstrumentActor* instrActor = m_instrWindow->getInstrumentActor(); Mantid::API::MatrixWorkspace_const_sptr ws = instrActor->getWorkspace(); size_t wi; @@ -354,8 +360,11 @@ void InstrumentWindowPickTab::plotSingle(int detid) } catch (Mantid::Kernel::Exception::NotFoundError) { return; // Detector doesn't have a workspace index relating to it } + // get the data const Mantid::MantidVec& x = ws->readX(wi); const Mantid::MantidVec& y = ws->readY(wi); + + // find min and max for x size_t imin,imax; getBinMinMaxIndex(wi,imin,imax); @@ -364,16 +373,26 @@ void InstrumentWindowPickTab::plotSingle(int detid) m_plot->setXScale(x[imin],x[imax]); + // fins min and max for y Mantid::MantidVec::const_iterator min_it = std::min_element(y_begin,y_end); Mantid::MantidVec::const_iterator max_it = std::max_element(y_begin,y_end); + // set the data m_plot->setData(&x[0],&y[0],static_cast<int>(y.size())); m_plot->setYScale(*min_it,*max_it); + + // find any markers + ProjectionSurface* surface = mInstrumentDisplay->getSurface(); + if (surface) + { + QList<PeakMarker2D*> markers = surface->getPeakOverlay().getMarkersWithID(detid); + foreach(PeakMarker2D* marker,markers) + { + m_plot->addLabel(new PeakLabel(marker)); + //std::cerr << marker->getLabel().toStdString() << std::endl; + } + } } -//void InstrumentWindowPickTab::plotBox(const Instrument3DWidget::DetInfo & /*cursorPos*/) -//{ -//} -// void InstrumentWindowPickTab::plotTube(int detid) { InstrumentActor* instrActor = m_instrWindow->getInstrumentActor(); diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/OneCurvePlot.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/OneCurvePlot.cpp index cfbd35ecdf77aa639d5a7bfe7c4063c4a45a08d1..95d417314bea015177a526cdf4fb9741652c0fca 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/OneCurvePlot.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/OneCurvePlot.cpp @@ -1,4 +1,5 @@ #include "OneCurvePlot.h" +#include "PeakMarker2D.h" #include <qwt_plot_curve.h> #include <qwt_scale_div.h> @@ -11,6 +12,7 @@ #include <QFontMetrics> #include <QMouseEvent> #include <QContextMenuEvent> +#include <QPainter> #include <iostream> @@ -182,3 +184,39 @@ void OneCurvePlot::setYLinearScale() update(); } +/** + * Add new peak label + * @param label :: A pointer to a PeakLabel, becomes owned by OneCurvePlot + */ +void OneCurvePlot::addLabel(PeakLabel* label) +{ + label->attach(this); + m_peakLabels.append(label); +} + +/** + * Removes all peak labels. + */ +void OneCurvePlot::clearLabels() +{ + foreach(PeakLabel* label, m_peakLabels) + { + label->detach(); + delete label; + } + m_peakLabels.clear(); +} + + +/** + * Draw PeakLabel on a plot + */ +void PeakLabel::draw(QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRect &canvasRect) const +{ + int x = xMap.transform(m_marker->getTOF()); + int y = canvasRect.top() + m_marker->getLabelRect().height(); + painter->drawText(x,y,m_marker->getLabel()); + //std::cerr << x << ' ' << y << ' ' << m_marker->getLabel().toStdString() << std::endl; +} diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/OneCurvePlot.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/OneCurvePlot.h index 5032245b17de3ca6c3674cf334a75069c288e959..6ef75eaa03992cbd6ae00ef30e530ad1958c701e 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/OneCurvePlot.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/OneCurvePlot.h @@ -2,11 +2,13 @@ #define ONECURVEPLOT_H_ #include <qwt_plot.h> +#include <qwt_plot_item.h> #include <QList> class QwtPlotCurve; class QwtPlotZoomer; - +class PeakLabel; +class PeakMarker2D; /** * Implements a simple widget for plotting a single curve. */ @@ -17,6 +19,8 @@ public: OneCurvePlot(QWidget* parent); void setData(const double* x,const double* y,int dataSize); void setYAxisLabelRotation(double degrees); + void addLabel(PeakLabel*); + void clearLabels(); public slots: void setXScale(double from, double to); void setYScale(double from, double to); @@ -37,7 +41,18 @@ private: QwtPlotZoomer* m_zoomer; ///< does zooming int m_x0; ///< save x coord of last left mouse click int m_y0; ///< save y coord of last left mouse click + QList<PeakLabel*> m_peakLabels; }; +class PeakLabel: public QwtPlotItem +{ +public: + PeakLabel(const PeakMarker2D* m):m_marker(m){} + void draw(QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRect &canvasRect) const; +private: + const PeakMarker2D* m_marker; +}; #endif /*ONECURVEPLOT_H_*/ diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakMarker2D.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakMarker2D.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d76b8ef88de83727d8ffa8da62b55ab8cbc69d2 --- /dev/null +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakMarker2D.cpp @@ -0,0 +1,119 @@ +#include "PeakMarker2D.h" + +#include <QPainter> +#include <QPainterPath> +#include <QFontMetrics> +#include <QMouseEvent> +#include <QWheelEvent> + +#include <iostream> +#include <algorithm> +#include <stdexcept> +#include <cmath> + +/// Default size in screen pixels of the marker's symbol +const int PeakMarker2D::g_defaultMarkerSize = 5; + +/** + * Constructor. + * @param centre :: Centre of the marker. Represents the peak position. + * @param symbol :: Symbol to draw. One of: Circle, Diamond, or Square + * @param markerSize :: Optional size of marker's symbol. + */ +PeakMarker2D::PeakMarker2D(const QPointF& centre,Symbol symbol,int markerSize): +m_symbol(symbol) +{ + if (markerSize > 0) + { + m_markerSize = markerSize; + } + else + { + m_markerSize = g_defaultMarkerSize; + } + m_boundingRect = QRectF(centre - QPointF((qreal)m_markerSize/2,(qreal)m_markerSize/2), + QSizeF((qreal)m_markerSize,(qreal)m_markerSize)); + setScalable(false); +} + +bool PeakMarker2D::selectAt(const QPointF& p)const +{ + return contains(p); +} + +void PeakMarker2D::drawShape(QPainter& painter) const +{ + // draw the symbol + switch(m_symbol) + { + case Circle: drawCircle(painter); break; + case Diamond: drawDiamond(painter); break; + case Square: drawSquare(painter); break; + default: + drawCircle(painter); + } + // calculate label's area on the screen + QFontMetrics fm(painter.font()); + QRect r = fm.boundingRect(m_label); + m_labelRect = QRectF(r); + m_labelRect.moveTo(m_boundingRect.right() + m_markerSize,m_boundingRect.top() - m_markerSize); +} + +void PeakMarker2D::addToPath(QPainterPath& path) const +{ + path.addRect(m_boundingRect); +} + +/// Set new marker size to s +void PeakMarker2D::setMarkerSize(const int& s) +{ + if (s > 0) + { + m_markerSize = s; + } +} + +/// Draw marker as a circle +void PeakMarker2D::drawCircle(QPainter& painter)const +{ + QPainterPath path; + path.addEllipse(m_boundingRect); + painter.fillPath(path,m_color); +} + +/// Draw marker as a diamond +void PeakMarker2D::drawDiamond(QPainter& painter)const +{ + QPointF dp = origin(); + QPointF mdp(-dp.x(),-dp.y()); + // draw a diamond as a square rotated by 45 degrees + painter.save(); + painter.translate(dp); + painter.rotate(45); + painter.translate(mdp); + QPainterPath path; + path.addRect(m_boundingRect); + painter.fillPath(path,m_color); + painter.restore(); +} + +/// Draw marker as a square +void PeakMarker2D::drawSquare(QPainter& painter)const +{ + QPainterPath path; + path.addRect(m_boundingRect); + painter.fillPath(path,m_color); +} + +/** + * Save some peak information. + */ +void PeakMarker2D::setPeak(const Mantid::API::IPeak& peak) +{ + m_h = peak.getH(); + m_k = peak.getK(); + m_l = peak.getL(); + m_label = QString("%1 %2 %3").arg(QString::number(m_h),QString::number(m_k),QString::number(m_l)); + m_detID = peak.getDetectorID(); + m_tof = peak.getTOF(); +} diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakMarker2D.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakMarker2D.h new file mode 100644 index 0000000000000000000000000000000000000000..9fff883697d5c684be0e0607e0e28ef707a6068a --- /dev/null +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakMarker2D.h @@ -0,0 +1,62 @@ +#ifndef MANTIDPLOT_PEAKMARKER2D_H_ +#define MANTIDPLOT_PEAKMARKER2D_H_ + +#include "Shape2D.h" +#include "MantidAPI/IPeak.h" + +/** + * Shape representing a peak marker on un unwrapped surface. + * A marker consists of a symbol marking location of a peak + * and a text label. + */ +class PeakMarker2D: public Shape2D +{ +public: + enum Symbol {Circle = 0,Diamond,Square}; + PeakMarker2D(const QPointF& centre,Symbol symbol = Circle,int markerSize = 0); + /* --- Implemented Shape2D virtual methods --- */ + virtual Shape2D* clone()const{return new PeakMarker2D(*this);} + virtual bool selectAt(const QPointF& p)const; + virtual bool contains(const QPointF& p)const{return m_boundingRect.contains(p);} + virtual void addToPath(QPainterPath& path) const; + /* --- Own public methods --- */ + /// Set new marker size to s + void setMarkerSize(const int& s); + /// Get marker size + int getMarkerSize()const{return m_markerSize;} + /// Get default marker size + static const int getDefaultMarkerSize(){return g_defaultMarkerSize;} + Symbol getSymbol()const{return m_symbol;} + void setSymbol(Symbol s){m_symbol=s;} + void setPeak(const Mantid::API::IPeak& peak); + double getH()const{return m_h;} + double getK()const{return m_k;} + double getL()const{return m_l;} + int getDetectorID()const{return m_detID;} + double getTOF()const{return m_tof;} + /// Get label's area on the screen + const QRectF& getLabelRect()const{return m_labelRect;} + /// Allows PeakOverlay to move the label to avoid overlapping + void moveLabelRectTo(const QPointF& p)const{m_labelRect.moveTo(p);} + QString getLabel()const{return m_label;} +protected: + /* --- Implemented Shape2D protected virtual methods --- */ + virtual void drawShape(QPainter& painter) const; + virtual void refit(){} + /* --- Own protected methods --- */ + void drawCircle(QPainter& painter)const; + void drawDiamond(QPainter& painter)const; + void drawSquare(QPainter& painter)const; +private: + + int m_markerSize; + static const int g_defaultMarkerSize; + Symbol m_symbol; ///< Shape of the marker + double m_h, m_k, m_l; ///< Peak's h,k,l + int m_detID; + double m_tof; + QString m_label; + mutable QRectF m_labelRect; ///< label's area on the screen +}; + +#endif /*MANTIDPLOT_PEAKMARKER2D_H_*/ diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakOverlay.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakOverlay.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aab862cbd4e7cc04f63f61643bef78975b73c26b --- /dev/null +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakOverlay.cpp @@ -0,0 +1,141 @@ +#include "PeakOverlay.h" +#include "PeakMarker2D.h" + +#include <QPainter> +#include <QList> + +/** +* Constructor. +*/ +PeakHKL::PeakHKL(PeakMarker2D* m,const QRectF& trect): +p(m->origin()), + rect(trect), + //rectTopLeft(m->getLabelRect().topLeft()), + h(m->getH()), + k(m->getK()), + l(m->getL()), + nh(true), + nk(true), + nl(true) +{} + +/** +* Check if this rect intersects with marker's and if it does combine the labels +* @param marker :: A marker to check for intersection +* @param trect :: Transformed marker's label rect +* @return True if labels were combined, false otherwise. +*/ +bool PeakHKL::add(PeakMarker2D* marker,const QRectF& trect) +{ + if ( !rect.intersects(trect) ) + { + return false; + } + if (nh && marker->getH() != h) + { + nh = false; + } + if (nk && marker->getK() != k) + { + nk = false; + } + if (nl && marker->getL() != l) + { + nl = false; + } + return true; +} +/** +* Draw the label +* @param painter :: QPainter to draw with +* @param transform :: Current transform +*/ +void PeakHKL::draw(QPainter& painter,const QTransform& transform) +{ + QString label; + if (nh) label = QString::number(h) + " "; + else + label = "h "; + if (nk) label += QString::number(k) + " "; + else + label += "k "; + if (nl) label += QString::number(l); + else + label += "l"; + painter.drawText(rect.bottomLeft(),label); + +} + +void PeakHKL::print()const +{ + std::cerr << " " << p.x() << ' ' << p.y() << '('<<h<<','<<k<<','<<l<<")("<<nh<<','<<nk<<','<<nl<<')' << std::endl; +} + +/** + * Add new marker to the overlay. + * @param m :: Pointer to the new marker + */ +void PeakOverlay::addMarker(PeakMarker2D* m) +{ + addShape(m,false); + m_det2marker.insert(m->getDetectorID(),m); +} + +void PeakOverlay::draw(QPainter& painter) const +{ + // Draw symbols + Shape2DCollection::draw(painter); + // Sort the labels to avoid overlapping + QColor color; + QRectF clipRect(painter.viewport()); + m_labels.clear(); + foreach(Shape2D* shape,m_shapes) + { + if (!clipRect.contains(m_transform.map(shape->origin()))) continue; + PeakMarker2D* marker = dynamic_cast<PeakMarker2D*>(shape); + if (!marker) continue; + color = marker->getColor(); + QPointF p0 = marker->origin(); + QPointF p1 = m_transform.map(p0); + QRectF rect = marker->getLabelRect(); + QPointF dp = rect.topLeft() - p0; + p1 += dp; + rect.moveTo(p1); + + //painter.setPen(color); + //painter.drawRect(rect); + + bool overlap = false; + // if current label overlaps with another + // combine them substituting differing numbers with letter 'h','k', or 'l' + for(int i = 0; i < m_labels.size(); ++i) + { + PeakHKL& hkl = m_labels[i]; + overlap = hkl.add(marker,rect); + } + + if (!overlap) + { + PeakHKL hkl(marker,rect); + m_labels.append(hkl); + } + } + //std::cerr << m_labels.size() << " labels\n"; + painter.setPen(color); + for(int i = 0; i < m_labels.size(); ++i) + { + PeakHKL& hkl = m_labels[i]; + hkl.draw(painter,m_transform); + //hkl.print(); + } +} + +/** + * Return a list of markers put onto a detector + * @param detID :: A detector ID for which markers are to be returned. + * @return :: A list of zero ot more markers. + */ +QList<PeakMarker2D*> PeakOverlay::getMarkersWithID(int detID)const +{ + return m_det2marker.values(detID); +} diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakOverlay.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakOverlay.h new file mode 100644 index 0000000000000000000000000000000000000000..a508ac8d6085224402d9f81b952b27e783068aa9 --- /dev/null +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PeakOverlay.h @@ -0,0 +1,50 @@ +#ifndef MANTIDPLOT_PEAKOVERLAY_H_ +#define MANTIDPLOT_PEAKOVERLAY_H_ + +#include "Shape2DCollection.h" +#include <QHash> + +class PeakMarker2D; + +/** + * Class for managing overlapping peak labels and drawing them on screen. + * If labels of two or more peaks overlap they are combined into a single label. + * A label shows three numbers h,k, and l. A combined label replaces non-equal + * numbers of included markers with its letter. + */ +class PeakHKL +{ +public: + PeakHKL(PeakMarker2D* m,const QRectF& trect); + bool add(PeakMarker2D* marker,const QRectF& trect); + void draw(QPainter& painter,const QTransform& transform); + void print()const; + +private: + QPointF p; ///< untransformed marker origin + QRectF rect; ///< label's screen area in transformed coords + double h,k,l; ///< h,k, and l + bool nh,nk,nl; ///< true if h, k, or l is numeric + +}; + +/** + * Class for managing peak markers. + */ +class PeakOverlay: public Shape2DCollection +{ +public: + PeakOverlay():Shape2DCollection(){} + ~PeakOverlay(){} + /// Override the drawing method + void draw(QPainter& painter) const; + + void addMarker(PeakMarker2D* m); + QList<PeakMarker2D*> getMarkersWithID(int detID)const; + +private: + QMultiHash<int,PeakMarker2D*> m_det2marker; ///< detector ID to PeakMarker2D map + mutable QList<PeakHKL> m_labels; +}; + +#endif /*MANTIDPLOT_PEAKOVERLAY_H_*/ diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/ProjectionSurface.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/ProjectionSurface.h index 5fd086ec6649df17bd5678b115769cd023c265f4..40a554d4471b55e44d5bc617e00e411be6938acd 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/ProjectionSurface.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/ProjectionSurface.h @@ -7,6 +7,7 @@ #include "InstrumentActor.h" #include "Shape2DCollection.h" +#include "PeakOverlay.h" #include <QImage> #include <QList> @@ -109,6 +110,8 @@ public: bool isMasked(double x,double y)const{return m_maskShapes.isMasked(x,y);} void clearMask(){m_maskShapes.clear();} + PeakOverlay& getPeakOverlay(){return m_peakShapes;} + signals: void singleDetectorTouched(int); @@ -173,7 +176,7 @@ protected: bool m_leftButtonDown; Shape2DCollection m_maskShapes; ///< to draw mask shapes - mutable Shape2DCollection m_peakShapes; ///< to draw peak labels + mutable PeakOverlay m_peakShapes; ///< to draw peak labels }; diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2D.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2D.cpp index e2bf72ccd24e3cfc3250d36fbf5d75e67ac1c074..4b39ba2649a4bcc7613b96d719d15a1ebbabb0b0 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2D.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2D.cpp @@ -16,6 +16,7 @@ const qreal Shape2D::sizeCP = 2; Shape2D::Shape2D(): m_color(Qt::red), +m_scalable(true), m_editing(false) { diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2D.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2D.h index 16c1d7d97ecc6f7ef51e3d43287815142034f622..a7aaa98c798b2b286e00189b9db14852b7e3781e 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2D.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2D.h @@ -24,10 +24,13 @@ public: virtual Shape2D* clone()const = 0; // modify path so painter.drawPath(path) could be used to draw the shape. needed for filling in complex shapes virtual void addToPath(QPainterPath& path) const = 0; - // make sure the shape is withing the bounding box + // make sure the shape is within the bounding box virtual void refit() = 0; + // --- Public virtual methods --- // + virtual void draw(QPainter& painter) const; + virtual QPointF origin() const {return m_boundingRect.center();} virtual void moveBy(const QPointF& pos); virtual size_t getNControlPoints() const; virtual QPointF getControlPoint(size_t i) const; @@ -37,11 +40,6 @@ public: // by dx1, dy1, dx2, and dy2 correspondingly virtual void adjustBoundingRect(qreal dx1,qreal dy1,qreal dx2,qreal dy2); virtual void setBoundingRect(const QRectF& rect); - - void setColor(const QColor& color){m_color = color;} - void setFillColor(const QColor& color){m_fill_color = color;} - void edit(bool on){m_editing = on;} - bool isEditing()const{return m_editing;} // will the shape be selected if clicked at a point virtual bool selectAt(const QPointF& )const{return false;} // is a point inside the shape (closed line) @@ -49,6 +47,16 @@ public: // is a point "masked" by the shape. Only filled regians of a shape mask a point virtual bool isMasked(const QPointF& )const; + // --- Public methods --- // + + void setColor(const QColor& color){m_color = color;} + QColor getColor()const{return m_color;} + void setFillColor(const QColor& color){m_fill_color = color;} + void setScalable(bool on){m_scalable = on;} + bool isScalable() const {return m_scalable;} + void edit(bool on){m_editing = on;} + bool isEditing()const{return m_editing;} + // --- Properties. for gui interaction --- // // double properties @@ -66,6 +74,8 @@ protected: virtual void drawShape(QPainter& painter) const = 0; + // --- Protected virtual methods --- // + // return number of control points specific to this shape virtual size_t getShapeNControlPoints() const{return 0;} // returns position of a shape specific control point, 0 < i < getShapeNControlPoints() @@ -75,14 +85,19 @@ protected: // make sure the bounding box is correct virtual void resetBoundingRect() {} + // --- Protected methods --- // + // make sure that width and heigth are positive void correctBoundingRect(); + // --- Protected data --- // + static const size_t NCommonCP; static const qreal sizeCP; QRectF m_boundingRect; QColor m_color; QColor m_fill_color; + bool m_scalable; ///< shape cann be scaled when zoomed bool m_editing; }; diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2DCollection.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2DCollection.cpp index debedc4f95254b377d0840669a9bb6d2681855a5..0400a36df31347e2773d83a31216f4990c569b81 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2DCollection.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2DCollection.cpp @@ -32,18 +32,56 @@ Shape2DCollection::~Shape2DCollection() } } +/** + * Draw the collection on screen. + */ void Shape2DCollection::draw(QPainter& painter) const { if (m_shapes.isEmpty()) return; + + // separate scalable and nonscalable shapes + QList<Shape2D*> scalable; + QList<Shape2D*> nonscalable; + foreach(Shape2D* shape,m_shapes) + { + if (shape->isScalable()) + { + scalable << shape; + } + else + { + nonscalable << shape; + } + } + + // first draw the scalable ones painter.save(); painter.setTransform(m_transform); - foreach(const Shape2D* shape,m_shapes) + foreach(const Shape2D* shape,scalable) { shape->draw(painter); } painter.restore(); + + // now the nonscalable + foreach(const Shape2D* shape,nonscalable) + { + QPointF p0 = shape->origin(); + QPointF p1 = m_transform.map(p0); + QPointF dp = p1 - p0; + painter.save(); + painter.translate(dp); + shape->draw(painter); + painter.restore(); + } + //std::cerr << m_transform.m11() << ' ' << m_transform.m22() << ' ' << m_transform.m33() << std::endl; } +/** + * Add a new shape to collection. + * @param shape :: A pointer to the new shape. + * @param slct :: A bool flag to select the shape after it's added. + */ void Shape2DCollection::addShape(Shape2D* shape,bool slct) { m_shapes.push_back(shape); @@ -55,23 +93,37 @@ void Shape2DCollection::addShape(Shape2D* shape,bool slct) emit shapeCreated(); } -void Shape2DCollection::setWindow(const QRectF& rect,const QRect& viewport) const +/** + * Remove a shape from collection + * @param shape :: Pointer to the shape to remove. + */ +void Shape2DCollection::removeShape(Shape2D* shape) +{ + if (shape && m_shapes.contains(shape)) + { + m_shapes.removeOne(shape); + } +} + +/** + */ +void Shape2DCollection::setWindow(const QRectF& window,const QRect& viewport) const { m_transform.reset(); m_viewport = viewport; if ( m_windowRect.isNull() ) { - m_windowRect = rect; + m_windowRect = window; m_h = viewport.height(); - m_wx = viewport.width() / rect.width(); - m_wy = m_h / rect.height(); + m_wx = viewport.width() / window.width(); + m_wy = m_h / window.height(); } else { - double wx = viewport.width() / rect.width(); - double wy = viewport.height() / rect.height(); - double rx = m_windowRect.left() - rect.left(); - double ry = m_windowRect.top() - rect.top(); + double wx = viewport.width() / window.width(); + double wy = viewport.height() / window.height(); + double rx = m_windowRect.left() - window.left(); + double ry = m_windowRect.top() - window.top(); qreal sx = wx / m_wx; qreal sy = wy / m_wy; qreal dx = rx * wx; @@ -320,7 +372,7 @@ void Shape2DCollection::removeCurrentShape() { if (m_currentShape) { - m_shapes.removeOne(m_currentShape); + this->removeShape(m_currentShape); m_currentShape = NULL; emit shapesDeselected(); } @@ -448,11 +500,21 @@ void Shape2DCollection::getMaskedPixels(QList<QPoint>& pixels)const void Shape2DCollection::setCurrentBoundingRectReal(const QRectF& rect) { if (!m_currentShape) return; - // convert rect from real to original screen coordinates + // convert rect from real to original screen coordinates (unaffected by m_transform) double x = (rect.x() - m_windowRect.left()) * m_wx; double y = m_h - (rect.bottom() - m_windowRect.y()) * m_wy; double width = rect.width() * m_wx; double height = rect.height() * m_wy; + //QPointF c = QRectF(x,y,width,height).center(); + //std::cerr << "setCurrentBoundingRectReal: " << c.x() << ' ' << c.y() << std::endl << std::endl; m_currentShape->setBoundingRect(QRectF(x,y,width,height)); } + +QPointF Shape2DCollection::realToUntransformed(const QPointF& point)const +{ + qreal x = (point.x() - m_windowRect.left()) * m_wx; + qreal y = m_h - (point.y() - m_windowRect.y()) * m_wy; + //std::cerr << "realToUntransformed: " << x << ' ' << y << std::endl; + return QPointF(x,y); +} diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2DCollection.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2DCollection.h index fb80e38b37709b1340aebbd078dbc278fe7a05ca..fa637b728cb969a49ad8a4aa80c54a406b435fc5 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2DCollection.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/Shape2DCollection.h @@ -13,6 +13,21 @@ class QKeyEvent; /** * Class Shape2DCollection is a collection of 2D shapes. + * It supports operations on teh shapes such as adding, removing, and aditting either + * with the mouse via control points (CPs) or via properties. + * + * The shapes operate in three coordinate systems: + * 1. Some 'real' or logical coordinates + * 2. Current or transformed screen coordinates + * 3. Untransformed screen coordinates + * + * Shape2DCollection must know the boundaries of the drawing area in logical and transformed screen coords. + * They are set by calling setWindow(...) method. The first argument is the logical drawing rectangle and + * the second one is the corresponding screen viewport in pixels. The first screen viewport set with setWindow + * defines the Untransformed screen coordinates. The individual shapes draw themselves in the untransformed + * screen coords and unaware of the logical ones at all. If the size of the screen/widget changes setWindow + * must be called again. Changing the logical drawing bounds translates and zooms the picture. + * The transformation is done by Qt's QTransform object. */ class Shape2DCollection: public QObject, public Shape2D { @@ -21,9 +36,10 @@ public: Shape2DCollection(); ~Shape2DCollection(); Shape2D* clone()const{return NULL;} - void setWindow(const QRectF& rect,const QRect& viewport) const; + void setWindow(const QRectF& window,const QRect& viewport) const; virtual void draw(QPainter& painter) const; virtual void addShape(Shape2D*,bool slct = false); + virtual void removeShape(Shape2D*); void mousePressEvent(QMouseEvent*); void mouseMoveEvent(QMouseEvent*); @@ -58,7 +74,12 @@ public: // collect all screen pixels that are masked by the shapes void getMaskedPixels(QList<QPoint>& pixels)const; + // --- coordinate transformations --- // + + // set the bounding rect of the current shape such that its real rect is given by the argument void setCurrentBoundingRectReal(const QRectF& rect); + // convert a real point to the untransformed screen coordinates + QPointF realToUntransformed(const QPointF& point)const; signals: @@ -79,15 +100,15 @@ protected: void select(Shape2D* shape); QList<Shape2D*> m_shapes; - mutable QRectF m_windowRect; // original surface window in "real" cooerdinates + mutable QRectF m_windowRect; // original surface window in "real" coordinates mutable double m_wx,m_wy; mutable int m_h; // original screen viewport height mutable QRect m_viewport; // current screen viewport mutable QTransform m_transform; // current transform - bool m_creating; - bool m_editing; - bool m_moving; + bool m_creating; ///< a shape is being created with a mouse + bool m_editing; ///< current shape is being edited with a mouse. CPs are visible + bool m_moving; ///< current shape is being moved with a mouse. int m_x,m_y; QString m_shapeType; QColor m_borderColor, m_fillColor; diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.cpp index 3fc09e6163c327fdb13bb014237351962c405f1e..0d82b43bc177c480855293456a1fadee121fbefa 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.cpp @@ -2,6 +2,7 @@ #include "GLColor.h" #include "MantidGLWidget.h" #include "OpenGLError.h" +#include "PeakMarker2D.h" #include "MantidGeometry/IDetector.h" #include "MantidGeometry/Objects/Object.h" @@ -12,6 +13,7 @@ #include <QSet> #include <QMenu> #include <QMouseEvent> +#include <QApplication> #include <cfloat> #include <limits> @@ -170,7 +172,7 @@ void UnwrappedSurface::drawSurface(MantidGLWidget *widget,bool picking)const if (m_startPeakShapes) { - ceatePeakShapes(widget->rect()); + createPeakShapes(widget->rect()); } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -632,9 +634,10 @@ void UnwrappedSurface::setPeaksWorkspace(boost::shared_ptr<Mantid::API::IPeaksWo * Create the peak labels from the peaks set by setPeaksWorkspace. The method is called from the draw(...) method * @param window :: The screen window rectangle in pixels. */ -void UnwrappedSurface::ceatePeakShapes(const QRect& viewport)const +void UnwrappedSurface::createPeakShapes(const QRect& window)const { - m_peakShapes.setWindow(getSurfaceBounds(),viewport); + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + m_peakShapes.setWindow(getSurfaceBounds(),window); int nPeaks = m_peaksWorkspace->getNumberPeaks(); for(int i = 0; i < nPeaks; ++i) { @@ -645,12 +648,12 @@ void UnwrappedSurface::ceatePeakShapes(const QRect& viewport)const Mantid::Geometry::IDetector_const_sptr det = udet.detector; if (! det ) continue; if (det->getID() != detID) continue; - Shape2DRectangle* r = new Shape2DRectangle(); - r->setFillColor(QColor(255,255,255,100)); - m_peakShapes.addShape(r,true); - m_peakShapes.setCurrentBoundingRectReal(QRectF(udet.u-udet.width/2,udet.v-udet.height/2,udet.width,udet.height)); + PeakMarker2D* r = new PeakMarker2D(m_peakShapes.realToUntransformed(QPointF(udet.u,udet.v))); + r->setPeak(peak); + m_peakShapes.addMarker(r); } } m_peakShapes.deselectAll(); m_startPeakShapes = false; + QApplication::restoreOverrideCursor(); } diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.h index 0b01986b9170913afa5f61deae9936f58bbd0a10..21109020f7fdb6cdb323a7b58944d7dde6b4c513 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.h @@ -97,7 +97,7 @@ protected: void showPickedDetector(); void calcAssemblies(boost::shared_ptr<const Mantid::Geometry::IComponent> comp,const QRectF& compRect); void findAndCorrectUGap(); - void ceatePeakShapes(const QRect& viewport)const; + void createPeakShapes(const QRect& viewport)const; const InstrumentActor* m_instrActor; double m_u_min; ///< Minimum u