From 113f8f75e3e3789e90894ef0264b81c6334bc545 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Mon, 10 Aug 2015 23:13:41 -0600 Subject: [PATCH 01/37] initial work on png/jpeg plugin --- carta/cpp/CartaLib/IImage.h | 7 +- carta/cpp/CartaLib/IPlugin.h | 42 +- carta/cpp/core/CallbackList.h | 2 +- carta/cpp/core/DummyGridRenderer.cpp | 2 +- carta/cpp/desktop/desktopMain.cpp | 69 +- .../CasaImageLoader/CCCoordinateFormatter.h | 6 +- carta/cpp/plugins/CasaImageLoader/CCImage.h | 1 - .../CasaImageLoader/CCMetaDataInterface.cpp | 6 - carta/cpp/plugins/CasaImageLoader/CCRawView.h | 7 +- .../CasaImageLoader/CasaImageLoader.cpp | 2 +- .../plugins/CasaImageLoader/CasaImageLoader.h | 2 +- carta/cpp/plugins/Histogram/Histogram1.cpp | 3 +- carta/cpp/plugins/casatest1/CasaTest1.cpp | 305 --------- carta/cpp/plugins/casatest1/plugin.json | 9 - carta/cpp/plugins/plugins.pro | 17 +- carta/cpp/plugins/qimage/QImagePlugin.cpp | 598 ++++++++++++++++++ .../CasaTest1.h => qimage/QImagePlugin.h} | 7 +- carta/cpp/plugins/qimage/plugin.json | 7 +- .../casatest1.pro => qimage/qimage.pro} | 4 +- carta/html5/html5.iml | 1 + 20 files changed, 702 insertions(+), 395 deletions(-) delete mode 100644 carta/cpp/plugins/casatest1/CasaTest1.cpp delete mode 100644 carta/cpp/plugins/casatest1/plugin.json create mode 100644 carta/cpp/plugins/qimage/QImagePlugin.cpp rename carta/cpp/plugins/{casatest1/CasaTest1.h => qimage/QImagePlugin.h} (70%) rename carta/cpp/plugins/{casatest1/casatest1.pro => qimage/qimage.pro} (97%) create mode 120000 carta/html5/html5.iml diff --git a/carta/cpp/CartaLib/IImage.h b/carta/cpp/CartaLib/IImage.h index 5acc69d9..1d739b94 100644 --- a/carta/cpp/CartaLib/IImage.h +++ b/carta/cpp/CartaLib/IImage.h @@ -105,16 +105,14 @@ class RawViewInterface virtual ~RawViewInterface() { } - // ===-----------------------------------------------------------------------=== - // experimental APIs below, not yet finalized and definitely not yet implemented - // Probably we'll only implement one of these, not all of them. - // ===-----------------------------------------------------------------------=== /// \brief create a view into this view /// \param sliceInfo which view to get /// \return a new view /// \warning for efficiency reasons we are not doing shared pointers, etc... since the returned /// view needs to have pointer to the instance of the image interface, the image interface /// needs to remain valid while accessing the view! i.e. don't delete it + /// \todo the above warning I think we should treat as a bug! I think we can do this safely + /// with shared pointers while maintaining speed (look at qimage plugin) virtual RawViewInterface * getView( const SliceND & sliceInfo ) = 0; @@ -276,6 +274,7 @@ class MetaDataInterface // coordinateGridPlotter() = 0; /// get a labeler algorithm + /// \note this is not being used anywhere, and maybe it won't be used ever virtual PlotLabelGeneratorInterface::SharedPtr plotLabelGenerator() = 0; diff --git a/carta/cpp/CartaLib/IPlugin.h b/carta/cpp/CartaLib/IPlugin.h index d73f11ef..d2422c33 100644 --- a/carta/cpp/CartaLib/IPlugin.h +++ b/carta/cpp/CartaLib/IPlugin.h @@ -160,27 +160,27 @@ class PreRender : public BaseHook }; /// load image and convert it to QImage -class LoadImage : public BaseHook -{ - CARTA_HOOK_BOILER1(LoadImage) - -public: - typedef QImage ResultType; - struct Params { - Params( QString p_fileName, int p_channel ) - { - fileName = p_fileName; - frame = p_channel; - } - - QString fileName; - int frame; - }; - LoadImage( Params * pptr ) : BaseHook( staticId ), paramsPtr( pptr ) { } - - ResultType result; - Params * paramsPtr; -}; +//class LoadImage : public BaseHook +//{ +// CARTA_HOOK_BOILER1(LoadImage) + +//public: +// typedef QImage ResultType; +// struct Params { +// Params( QString p_fileName, int p_channel ) +// { +// fileName = p_fileName; +// frame = p_channel; +// } + +// QString fileName; +// int frame; +// }; +// LoadImage( Params * pptr ) : BaseHook( staticId ), paramsPtr( pptr ) { } + +// ResultType result; +// Params * paramsPtr; +//}; // this is needed to setup the Qt metatype system to enable qobject_cast<> downcasting // must be outside of any namespace diff --git a/carta/cpp/core/CallbackList.h b/carta/cpp/core/CallbackList.h index 7c359ce5..6390aaf6 100644 --- a/carta/cpp/core/CallbackList.h +++ b/carta/cpp/core/CallbackList.h @@ -72,7 +72,7 @@ class CallbackList CallbackList() { m_pimpl = new PIMPL; - qDebug() << "constructor this=" << this << " m_pimpl=" << m_pimpl; +// qDebug() << "constructor this=" << this << " m_pimpl=" << m_pimpl; } /// adds a callback diff --git a/carta/cpp/core/DummyGridRenderer.cpp b/carta/cpp/core/DummyGridRenderer.cpp index f477a84d..daad15d6 100644 --- a/carta/cpp/core/DummyGridRenderer.cpp +++ b/carta/cpp/core/DummyGridRenderer.cpp @@ -74,7 +74,7 @@ DummyGridRenderer::startRendering( Lib::IWcsGridRenderService::JobId jobId ) m_jobId = jobId; } if ( ! m_timer.isActive() ) { - m_timer.start( 1 ); + m_timer.start( 0 ); } return m_jobId; } diff --git a/carta/cpp/desktop/desktopMain.cpp b/carta/cpp/desktop/desktopMain.cpp index d3a3327f..56b8ead6 100644 --- a/carta/cpp/desktop/desktopMain.cpp +++ b/carta/cpp/desktop/desktopMain.cpp @@ -21,17 +21,18 @@ /// /// @warning keep this in sync with serverMain until refactored to commonMain /// -int main(int argc, char ** argv) +static int +mainCPP( int argc, char * * argv ) { // // initialize Qt // - MyQApp qapp( argc, argv); + MyQApp qapp( argc, argv ); #ifdef QT_DEBUG - MyQApp::setApplicationName( "carta-desktop-debug"); + MyQApp::setApplicationName( "carta-desktop-debug" ); #else - MyQApp::setApplicationName( "carta-desktop-release"); + MyQApp::setApplicationName( "carta-desktop-release" ); #endif qDebug() << "Starting" << qapp.applicationName() << qapp.applicationVersion(); @@ -41,37 +42,39 @@ int main(int argc, char ** argv) // parse command line arguments & environment variables // ==================================================== - auto cmdLineInfo = CmdLine::parse( MyQApp::arguments()); - globals.setCmdLineInfo( & cmdLineInfo); + auto cmdLineInfo = CmdLine::parse( MyQApp::arguments() ); + globals.setCmdLineInfo( & cmdLineInfo ); // load the config file // ==================== QString configFilePath = cmdLineInfo.configFilePath(); - auto mainConfig = MainConfig::parse( configFilePath); - globals.setMainConfig( & mainConfig); - qDebug() << "plugin directories:\n - " + mainConfig.pluginDirectories().join( "\n - "); + auto mainConfig = MainConfig::parse( configFilePath ); + globals.setMainConfig( & mainConfig ); + qDebug() << "plugin directories:\n - " + mainConfig.pluginDirectories().join( "\n - " ); // initialize platform // =================== // platform gets command line & main config file via globals auto platform = new DesktopPlatform(); - globals.setPlatform( platform); + globals.setPlatform( platform ); // prepare connector // ================= // connector is created via platform, but we put it into globals explicitely here IConnector * connector = platform-> connector(); - if( ! connector) { - qFatal( "Could not initialize connector!"); + if ( ! connector ) { + qFatal( "Could not initialize connector!" ); } - globals.setConnector( connector); + globals.setConnector( connector ); // initialize plugin manager // ========================= - globals.setPluginManager( std::make_shared() ); + globals.setPluginManager( std::make_shared < PluginManager > () ); auto pm = globals.pluginManager(); + // tell plugin manager where to find plugins pm-> setPluginSearchPaths( globals.mainConfig()->pluginDirectories() ); + // find and load plugins pm-> loadPlugins(); qDebug() << "Loading plugins..."; @@ -85,28 +88,48 @@ int main(int argc, char ** argv) // ================= Viewer viewer; Hacks::HackViewer::UniquePtr hackViewer = nullptr; - if( globals.mainConfig()-> hacksEnabled()) { - hackViewer.reset( new Hacks::HackViewer); + if ( globals.mainConfig()-> hacksEnabled() ) { + hackViewer.reset( new Hacks::HackViewer ); } - if ( globals.mainConfig()->isDeveloperLayout()){ + if ( globals.mainConfig()->isDeveloperLayout() ) { viewer.setDeveloperView(); } // prepare closure to execute when connector is initialized - IConnector::InitializeCallback initCB = [&](bool valid) -> void { - if( ! valid) { - qFatal( "Could not initialize connector"); + IConnector::InitializeCallback initCB = [&] ( bool valid ) -> void { + if ( ! valid ) { + qFatal( "Could not initialize connector" ); } viewer.start(); - if( hackViewer) { + if ( hackViewer ) { hackViewer-> start(); } }; // initialize connector - connector-> initialize( initCB); + connector-> initialize( initCB ); // qt now has control return qapp.exec(); -} +} // mainCPP +int +main( int argc, char * * argv ) +{ + try { + return mainCPP( argc, argv ); + } + catch ( const char * err ) { + qCritical() << "Exception(char*):" << err; + } + catch ( const std::string & err ) { + qCritical() << "Exception(std::string &):" << err.c_str(); + } + catch ( const QString & err ) { + qCritical() << "Exception(QString &):" << err; + } + catch ( ... ) { + qCritical() << "Exception(unknown type)!"; + } + return - 1; +} // main diff --git a/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.h b/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.h index fb1f08f6..72c81548 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.h +++ b/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.h @@ -12,13 +12,13 @@ class CCCoordinateFormatter : public CoordinateFormatterInterface { + /// shortcut to AxisInfo type + typedef Carta::Lib::AxisInfo AxisInfo; + CLASS_BOILERPLATE( CCCoordinateFormatter ); public: - /// shortcut to AxisInfo type - typedef Carta::Lib::AxisInfo AxisInfo; - CCCoordinateFormatter( std::shared_ptr < casa::CoordinateSystem > casaCS ); virtual CCCoordinateFormatter * clone() const override; diff --git a/carta/cpp/plugins/CasaImageLoader/CCImage.h b/carta/cpp/plugins/CasaImageLoader/CCImage.h index 6103e66d..0549e73e 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCImage.h +++ b/carta/cpp/plugins/CasaImageLoader/CCImage.h @@ -147,7 +147,6 @@ class CCImage } /// this should be protected... but I don't have time to fix the compiler errors - /// basically we // template < class X > // friend class CCImage; // friend class CCImage; diff --git a/carta/cpp/plugins/CasaImageLoader/CCMetaDataInterface.cpp b/carta/cpp/plugins/CasaImageLoader/CCMetaDataInterface.cpp index 3a987a59..dd11a497 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCMetaDataInterface.cpp +++ b/carta/cpp/plugins/CasaImageLoader/CCMetaDataInterface.cpp @@ -21,14 +21,8 @@ Image::MetaDataInterface *CCMetaDataInterface::clone() CoordinateFormatterInterface::SharedPtr CCMetaDataInterface::coordinateFormatter() { return std::make_shared( m_casaCS); -// qFatal( "not implemented"); } -//CoordinateGridPlotterInterface::SharedPtr CCMetaDataInterface::coordinateGridPlotter() -//{ -// qFatal( "not implemented"); -//} - PlotLabelGeneratorInterface::SharedPtr CCMetaDataInterface::plotLabelGenerator() { qFatal( "not implemented"); diff --git a/carta/cpp/plugins/CasaImageLoader/CCRawView.h b/carta/cpp/plugins/CasaImageLoader/CCRawView.h index 7cda6ca9..12e2cb09 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCRawView.h +++ b/carta/cpp/plugins/CasaImageLoader/CCRawView.h @@ -61,7 +61,7 @@ class CCRawView // create applied result that combines m_appliedSlice with ar SliceND::ApplyResult newAr = SliceND::ApplyResult::combine( m_appliedSlice, ar); - // return a new view bases on the new slice + // return a new view based on the new slice return new CCRawView( m_ccimage, newAr); } @@ -195,7 +195,7 @@ CCRawView < PType >::get( const NdArray::RawViewInterface::VI & pos ) + p * m_appliedSlice.dims()[i].step; } - // for some reason casa::ImageInterface::operator() returns the result by value + // casa::ImageInterface::operator() returns the result by value // so in order to return reference (to satisfy our API) we need to store this // in a buffer first... m_buff = m_ccimage-> m_casaII-> @@ -210,7 +210,7 @@ CCRawView < PType >::forEach( std::function < void (const char *) > func, NdArray::RawViewInterface::Traversal traversal ) { - if ( traversal == NdArray::RawViewInterface::Traversal::Optimal ) { + if ( traversal != NdArray::RawViewInterface::Traversal::Sequential ) { qFatal( "sorry, not implemented yet" ); } auto casaII = m_ccimage-> m_casaII; @@ -257,5 +257,6 @@ template < typename PType > const NdArray::RawViewInterface::VI & CCRawView < PType >::currentPos() { + qFatal( "Not implemented yet"); return m_currPosView; } diff --git a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp index 3c7600c7..a75082d7 100755 --- a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp +++ b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp @@ -76,7 +76,7 @@ static CCImageBase::SharedPtr tryCast( casa::LatticeBase * lat) /// Image::ImageInterface::SharedPtr CasaImageLoader::loadImage( const QString & fname) { - qDebug() << "CasaImageLoader plugin trying to load image: " << fname; + qDebug() << "CasaImageLoader plugin trying to load image: " << fname; // // first we open the image as a lattice diff --git a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.h b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.h index 2bb4165d..a81c8c84 100644 --- a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.h +++ b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.h @@ -19,7 +19,7 @@ class CasaImageLoader : public QObject, public IPlugin virtual bool handleHook(BaseHook & hookData) override; virtual std::vector getInitialHookList() override; - void forgot_to_define_this(); +// void forgot_to_define_this(); private: diff --git a/carta/cpp/plugins/Histogram/Histogram1.cpp b/carta/cpp/plugins/Histogram/Histogram1.cpp index 44ef24fc..e1107c6e 100755 --- a/carta/cpp/plugins/Histogram/Histogram1.cpp +++ b/carta/cpp/plugins/Histogram/Histogram1.cpp @@ -240,7 +240,8 @@ bool Histogram1::handleHook( BaseHook & hookData ){ CCImageBase * ptr1 = dynamic_cast( m_cartaImage.get()); if( ! ptr1) { - throw "not an image created by casaimageloader..."; + qWarning() << "Histogram plugin: not an image created by casaimageloader..."; + return false; } if (m_cartaImage->pixelType() == Image::PixelType::Real32 ){ diff --git a/carta/cpp/plugins/casatest1/CasaTest1.cpp b/carta/cpp/plugins/casatest1/CasaTest1.cpp deleted file mode 100644 index 32ca9609..00000000 --- a/carta/cpp/plugins/casatest1/CasaTest1.cpp +++ /dev/null @@ -1,305 +0,0 @@ -#include "CasaTest1.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -CasaTest1::CasaTest1(QObject *parent) : - QObject(parent) -{ -} - -bool CasaTest1::handleHook(BaseHook & hookData) -{ - qDebug() << "CasaTest Plugin is handling hook #" << hookData.hookId(); - if( hookData.hookId() == Initialize::staticId ) { - // Register FITS and Miriad image types - casa::FITSImage::registerOpenFunction(); - casa::MIRIADImage::registerOpenFunction(); - return true; - } - - if( hookData.hookId() == LoadImage::staticId) { - LoadImage & hook = static_cast( hookData); - auto fname = hook.paramsPtr->fileName; - auto channel = hook.paramsPtr->frame; - qDebug()<<"CasaTest1 loading image "< CasaTest1::getInitialHookList() -{ - return { - Initialize::staticId, - LoadImage::staticId - }; -} - -/// -/// \brief tries to open a given image using any of the supported formats -/// \param fname filename of the image to open -/// \return on success a pointer to casa::ImageInterface -/// -static -casa::ImageInterface * openCasaImageF( const QString & fname) -{ - qDebug() << "Opening (casa)image " << fname; - casa::LatticeBase * lat = 0; - - // try using image opener & paged array - casa::FITSImage::registerOpenFunction (); - try { - lat = casa::ImageOpener::openPagedImage ( fname.toStdString()); - qDebug() << "\t-opened as paged image"; - } catch ( ... ) { - qDebug() << "\t-paged image open failed"; - } - - // try using image opener & unpaged array - if( lat == 0 ) { - try { - lat = casa::ImageOpener::openImage ( fname.toStdString()); - qDebug() << "\t-opened as unpaged image"; - } catch ( ... ) { - qDebug() << "\t-unpaged image open failed"; - } - } - // if we still failed to open the lattice, we are done - if( lat == 0 ) { - qDebug() << "\t-out of ideas, bailing out"; - return 0; - } - - // try to convert to float image interface - casa::ImageInterface * img = 0; - - // see if we can convert to ImageInterface directly - if( lat->dataType () == casa::TpFloat) { - try { - img = dynamic_cast*> ( lat ); - qDebug() << "\t-simple conversion to image interface float successful"; - } catch ( ... ) {} - } - // if the initial conversion attempt failed, try a LEL expression - if( img == 0) { - try { - qDebug() << "\t-simple conversion to image interface failed"; - std::string expr = "float('" + fname.toStdString() + "')"; - casa::LatticeExpr le ( casa::ImageExprParse::command( expr )); - img = new casa::ImageExpr ( le, expr ); - qDebug() << "\t-LEL conversion successful"; - } catch ( ... ) {} - } - if( img == 0) { - qDebug() << "\t-could not convert to float image, bailing out"; - } - - return img; -} - - -/// -/// \brief Attempts to load an image using casacore library, namely the very first -/// frame of it. Then converts the frame to a QImage using 95% histogram clip values. -/// \param fname file name with the image -/// \param result where to store the result -/// \return true if successful, false otherwise -/// -bool CasaTest1::loadImage( const QString & fname, int channel, QImage & result) -{ - qDebug() << "CasaTest Plugin trying to load image: " << fname<<" channel="< CImage; - - // auto-release casa image pointer - std::unique_ptr< CImage > imgptr; - - // try to open the image using casacore's fits implementation - try { - CImage * img = nullptr; - // img = new casa::FITSImage( fname.toStdString()); - img = openCasaImageF( fname); - imgptr.reset( img); - } catch (...) { - qDebug() << "Casa failed to load the image."; - } - - // if we failed, we are done - if( ! imgptr) { - return false; - } - - // make sure we have at least 2 dimensions - int dims = imgptr-> ndim(); - if( dims < 2 ) { - qDebug() << "Casa opened the image but it has less than 2 dimensions..."; - return false; - } - - int width = imgptr-> shape()[0]; - int height = imgptr-> shape()[1]; - int channelCount = 0; - if ( dims >= 3 ){ - channelCount = imgptr->shape()[2]; - } - qDebug() << "Channel count="< " << width * height - allValues.size() << "nans"; - allValues.resize( 0); - } - - { - casa::IPosition cursorShape(2, width, height); - casa::LatticeStepper stepper( imgptr-> shape(), cursorShape); - casa::IPosition blc(dims, 0); - casa::IPosition trc(dims, 0); - trc(0) = width-1; - trc(1) = height-1; - if ( dims > 2 ){ - trc(2) = channel; - blc(2) = channel; - } - stepper.subSection(blc, trc); - - casa::RO_LatticeIterator iterator(* imgptr, stepper); - - for (iterator.reset(); !iterator.atEnd(); iterator++) { - for( const auto & val : iterator.cursor()) { - if( std::isfinite( val)) { - allValues.push_back( val); - } - } - } - qDebug() << "Found: " << allValues.size() << " values"; - qDebug() << " ==> " << width * height - allValues.size() << "nans"; - } - - // make sure there is at least 1 non-nan value, if not, return a black image - if( allValues.size() == 0) { - // there were no values.... return a black image - result = QImage( width, height, QImage::Format_ARGB32_Premultiplied); - result.fill( 0); - return true; - } - - if( false) { - // report min/max of the frame, for debugging purposes only - - // make sure first element is the smallest - std::nth_element( allValues.begin(), allValues.begin(), allValues.end()); - // last element is the largest - std::nth_element( allValues.begin(), allValues.end()-1, allValues.end()); - - Pixel min = allValues.front(); - Pixel max = allValues.back(); - qDebug() << "minmax=" << min << max; - } - - double hist = 95; // 95% - Pixel clip1, clip2; - { - hist = (1.0 - hist / 100) / 2.0; - int x1 = allValues.size() * hist; - std::nth_element( allValues.begin(), allValues.begin() + x1, allValues.end()); - clip1 = allValues[x1]; - x1 = allValues.size() - x1; - std::nth_element( allValues.begin(), allValues.begin() + x1, allValues.end()); - clip2 = allValues[x1]; - qDebug() << "clips=" << clip1 << clip2; - } - Pixel clipd = clip2 - clip1; - Pixel clipdinv = 1.0 / clipd; // precalculated linear transformation coeff. - - // - // revisit the pixels again, but this time construct the image using the clips - // - - casa::IPosition cursorShape(2, width, height); - casa::LatticeStepper stepper( imgptr-> shape(), cursorShape); - casa::IPosition blc(dims, 0); - casa::IPosition trc(dims, 0); - trc(0) = width-1; - trc(1) = height-1; - if ( dims > 2 ){ - trc(2) = channel; - blc(2) = channel; - } - stepper.subSection(blc, trc); // limit to first frame only - - casa::RO_LatticeIterator iterator(* imgptr, stepper); - iterator.reset(); - const casa::Matrix & srcm = iterator.matrixCursor(); - qDebug() << "matrix cursor" << srcm.shape()[0] << srcm.shape()[1]; - auto srci = srcm.begin(); - - // stuff the pixels into an qrgb buffer, because constructing qimage this way is - // the fastest - static std::vector < QRgb > outBuffer; - outBuffer.resize( width * height); - - for( int y = 0 ; y < height ; y ++ ) { - QRgb * outPtr = & (outBuffer[(height-y-1) * width]); - for( int x = 0 ; x < width ; x ++ ) { - Pixel val = * srci; - if( std::isfinite( val)) { - val = (val - clip1) * clipdinv; - int gray = val * 255; - if( gray > 255) gray = 255; - if( gray < 0) gray = 0; - * outPtr = qRgb( gray, gray, gray); - } - else { - * outPtr = qRgb( 255, 0, 0); - } - outPtr ++; - srci ++; - } - } - - // construct the qimage from the temporary buffer - result = QImage( reinterpret_cast( & outBuffer[0]), - width, height, QImage::Format_ARGB32); - - return true; -} diff --git a/carta/cpp/plugins/casatest1/plugin.json b/carta/cpp/plugins/casatest1/plugin.json deleted file mode 100644 index 424ac730..00000000 --- a/carta/cpp/plugins/casatest1/plugin.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "api" : "1", - "name" : "casatest1", - "version" : "1", - "type" : "C++", - "description": "Adds ability to load casa and fits files as QImages", - "about" : "Part of carta. Written by Pavol", - "depends" : [ "casaCore"] -} diff --git a/carta/cpp/plugins/plugins.pro b/carta/cpp/plugins/plugins.pro index 1f3a7cf0..bd97418c 100644 --- a/carta/cpp/plugins/plugins.pro +++ b/carta/cpp/plugins/plugins.pro @@ -1,18 +1,21 @@ TEMPLATE = subdirs #CONFIG += ordered -SUBDIRS += tester1 -SUBDIRS += clock1 -SUBDIRS += casatest1 -#SUBDIRS += qimage SUBDIRS += casaCore-2.0.1 -SUBDIRS += python273 -SUBDIRS += noisepy -SUBDIRS += blurpy SUBDIRS += CasaImageLoader SUBDIRS += Colormaps1 SUBDIRS += Histogram SUBDIRS += WcsPlotter +SUBDIRS += qimage + +# experimental plugins: + +SUBDIRS += tester1 +SUBDIRS += clock1 +SUBDIRS += python273 +SUBDIRS += noisepy +SUBDIRS += blurpy + diff --git a/carta/cpp/plugins/qimage/QImagePlugin.cpp b/carta/cpp/plugins/qimage/QImagePlugin.cpp new file mode 100644 index 00000000..54c503fe --- /dev/null +++ b/carta/cpp/plugins/qimage/QImagePlugin.cpp @@ -0,0 +1,598 @@ +#include "QImagePlugin.h" +#include "CartaLib/Hooks/LoadAstroImage.h" +#include +#include +#include +#include + +typedef Carta::Lib::HtmlString HtmlString; +typedef Carta::Lib::AxisInfo AxisInfo; + +/// shortcut for LoadAstroImage +typedef Carta::Lib::Hooks::LoadAstroImage LoadAstroImage; + +class QImageII; + +/// our implementation of RawView +class QImageRawView + : public NdArray::RawViewInterface +{ +public: + + QImageRawView() = delete; + + // construct a view from raw gray data for the specified slice + // the original data has dimesions 'dims' + QImageRawView( std::shared_ptr < std::vector < unsigned char > > data, + const VI & dims, + const SliceND & sliceInfo ) + { + // remember the data (shallow copy) + m_data = data; + m_rawData = & data-> at( 0 ); + + // remember original dimensions of the data + m_origDims = dims; + + // figure out what data to extract for each of the dimensions + m_appliedSlice = sliceInfo.apply( dims ); + + // cache the dimensions of the resulting view + for ( auto & x : m_appliedSlice.dims() ) { + m_viewDims.push_back( x.count ); + } + } + + // similar to the first constructor, but the view is for an applied slice + QImageRawView( std::shared_ptr < std::vector < unsigned char > > data, + const VI & dims, + const SliceND::ApplyResult & applyResult ) + { + // remember the data (shallow copy) + m_data = data; + m_rawData = & data-> at( 0 ); + + // remember original dimensions of the data + m_origDims = dims; + + // figure out what data to extract for each of the dimensions + m_appliedSlice = applyResult; + + // cache the dimensions of the resulting view + for ( auto & x : m_appliedSlice.dims() ) { + m_viewDims.push_back( x.count ); + } + } + + virtual PixelType + pixelType() override + { + return PixelType::Real32; + } + + virtual const VI & + dims() override + { + return m_viewDims; + } + + virtual const char * + get( const VI & pos ) override + { + int x = pos[0]; + int y = pos[1]; + + m_floatBuff = float (m_rawData[x + y * m_origDims[0]]) / float (255.0); + return reinterpret_cast < const char * > ( & m_floatBuff ); + } + + virtual void + forEach( std::function < void (const char *) > func, Traversal traversal ) override + { + if ( traversal != NdArray::RawViewInterface::Traversal::Sequential ) { + qFatal( "sorry, not implemented yet" ); + } + + const std::vector < Slice1D::ApplyResult > & dims = m_appliedSlice.dims(); + + int y = dims[1].start; + for ( int yc = 0 ; yc < dims[1].count ; ++yc ) { + unsigned char * row = & m_rawData[m_origDims[0] * y]; + int x = dims[0].start; + for ( int xc = 0 ; xc < dims[0].count ; ++xc ) { + float buff = float (row[x]) / float (255.0); + func( reinterpret_cast < const char * > ( & buff ) ); + + x += dims[0].step; + } + y += dims[1].step; + } + } // forEach + + virtual const VI & + currentPos() override + { + qFatal( "Not implemented yet" ); + return m_currPosView; + } + + virtual NdArray::RawViewInterface * + getView( const SliceND & sliceInfo ) override + { + // apply the slice to dimensions of this view + SliceND::ApplyResult ar = sliceInfo.apply( dims() ); + + // create applied result that combines m_appliedSlice with ar + SliceND::ApplyResult newAr = SliceND::ApplyResult::combine( m_appliedSlice, ar ); + + // return a new view bases on the new slice + return new QImageRawView( m_data, m_origDims, newAr ); + } + + virtual int64_t + read( int64_t buffSize, char * buff, Traversal traversal ) override + { + Q_UNUSED( buffSize ); + Q_UNUSED( buff ); + Q_UNUSED( traversal ); + qFatal( "not implemented" ); + } + + virtual void + seek( int64_t ind ) override + { + Q_UNUSED( ind ); + qFatal( "not implemented" ); + } + + virtual int64_t + read( int64_t chunk, int64_t buffSize, char * buff, Traversal traversal ) override + { + Q_UNUSED( chunk ); + Q_UNUSED( buffSize ); + Q_UNUSED( buff ); + Q_UNUSED( traversal ); + qFatal( "not implemented" ); + } + + virtual void + forEach( int64_t buffSize, + std::function < void (const char *, int64_t) > func, + char * buff, + Traversal traversal ) override + { + Q_UNUSED( buffSize ); + Q_UNUSED( func ); + Q_UNUSED( buff ); + Q_UNUSED( traversal ); + qFatal( "not implemented" ); + } + +private: + + // dimensions of the view + VI m_viewDims; + + // dimesions of the original data + VI m_origDims; + + // we remember shared pointer to prevent data from disappearing + std::shared_ptr < std::vector < unsigned char > > m_data = nullptr; + + // we remember raw data pointer for faster access + unsigned char * m_rawData = nullptr; + float m_floatBuff; + VI m_currPosView; + + // the current resolved slice for the data we have + SliceND::ApplyResult m_appliedSlice; +}; + +/// we need to implement our own coordinate formatter +class QImageCF : public + CoordinateFormatterInterface +{ +public: + + QImageCF() + { + // setup axis infos + m_axisInfos.push_back( + AxisInfo() + .setKnownType( AxisInfo::KnownType::OTHER ) + .setLongLabel( HtmlString::fromPlain( "X coordinate" ) ) + .setShortLabel( HtmlString::fromPlain( "X" ) ) + .setUnit( "n/a" ) ); + m_axisInfos.push_back( + AxisInfo() + .setKnownType( AxisInfo::KnownType::OTHER ) + .setLongLabel( HtmlString::fromPlain( "Y coordinate" ) ) + .setShortLabel( HtmlString::fromPlain( "Y" ) ) + .setUnit( "n/a" ) ); + + // setup precisions + m_precisions.push_back( 3 ); + m_precisions.push_back( 3 ); + } + + virtual CoordinateFormatterInterface * + clone() const override + { + // we'll use copy constructor for this + auto res = new QImageCF( * this ); + return res; + } + + virtual int + nAxes() const override + { + return 2; + } + + virtual QStringList + formatFromPixelCoordinate( const VD & pix ) override + { + CARTA_ASSERT( pix.size() >= 2 ); + QStringList res; + res.append( QString::number( pix[0] ) ); + res.append( QString::number( pix[1] ) ); + return res; + } + + virtual QString + calculateFormatDistance( const VD & p1, const VD & p2 ) override + { + Q_UNUSED( p1 ); + Q_UNUSED( p2 ); + + qFatal( "not implemented" ); + } + + virtual void + setTextOutputFormat( TextFormat fmt ) override + { + Q_UNUSED( fmt ); + } + + virtual const Carta::Lib::AxisInfo & + axisInfo( int ind ) const override + { + CARTA_ASSERT( ind >= 0 && ind < nAxes() ); + return m_axisInfos[ind]; + } + + virtual Me & + disableAxis( int ind ) override + { + Q_UNUSED( ind ); + qFatal( "not implemented" ); + return * this; + } + + virtual Me & + enableAxis( int ind ) override + { + Q_UNUSED( ind ); + qFatal( "not implemented" ); + return * this; + } + + virtual KnownSkyCS + skyCS() override + { + return KnownSkyCS::Unknown; + } + + virtual Me & + setSkyCS( const KnownSkyCS & scs ) override + { + Q_UNUSED( scs ); + return * this; + } + + virtual SkyFormatting + skyFormatting() override + { + return SkyFormatting::Default; + } + + virtual Me & + setSkyFormatting( SkyFormatting format ) override + { + Q_UNUSED( format ); + return * this; + } + + virtual int + axisPrecision( int axis ) override + { + CARTA_ASSERT( axis >= 0 && axis < nAxes() ); + return m_precisions[axis]; + } + + virtual Me & + setAxisPrecision( int precision, int axis ) override + { + CARTA_ASSERT( axis >= 0 && axis < nAxes() ); + m_precisions[axis] = precision; + return * this; + } + + virtual bool + toWorld( const VD & pixel, VD & world ) const override + { + world = pixel; + return true; + } + + virtual bool + toPixel( const VD & world, VD & pixel ) const override + { + pixel = world; + return true; + } + +private: + + /// cached info per axis + std::vector < AxisInfo > m_axisInfos; + + /// precisions + std::vector < int > m_precisions; +}; + +/// we need to implement our own MetaDataInterface +class QImageMDI : public Image::MetaDataInterface +{ +public: + + QImageMDI( std::shared_ptr < QImageII > ii ) + { + m_ii = ii; + m_coordinateFormatter = std::make_shared < QImageCF > (); + } + + virtual Image::MetaDataInterface * + clone() override + { + qFatal( "not implemented" ); + return nullptr; + } + + virtual CoordinateFormatterInterface::SharedPtr + coordinateFormatter() override + { + return m_coordinateFormatter; + } + + virtual PlotLabelGeneratorInterface::SharedPtr + plotLabelGenerator() override + { + qFatal( "not implemented" ); + return nullptr; + } + + virtual QString + title( TextFormat format ) override + { + if ( format == TextFormat::Plain ) { + return m_title.plain(); + } + else { + return m_title.html(); + } + } + + virtual QStringList + otherInfo( TextFormat format ) override + { + Q_UNUSED( format ); + return QStringList() + << "This is a qimage"; + } + +private: + + Carta::Lib::HtmlString m_title; + CoordinateFormatterInterface::SharedPtr m_coordinateFormatter = nullptr; + std::shared_ptr < QImageII > m_ii = nullptr; +}; + +/// we need to implement our own ImageInterface +class QImageII + : public Image::ImageInterface + , public std::enable_shared_from_this < QImageII > +{ +public: + + static std::shared_ptr < QImageII > + load( QString fname ) + { + // c++ does not allow this with QImageII having a private constructor: + // std::shared_ptr < QImageII > ptr = std::make_shared < QImageII > (); + // so instead we need to do this /facepalm + struct CPPSUCKS : public QImageII { }; + std::shared_ptr < QImageII > ptr = std::make_shared < CPPSUCKS > (); + + if ( ptr-> init( fname ) ) { + return ptr; + } + else { + return nullptr; + } + } // load + + virtual const Unit & + getPixelUnit() const override + { + return m_unit; + } + + virtual const VI & + dims() const override + { + return m_dims; + } + + virtual bool + hasMask() const override + { + return false; + } + + virtual bool + hasErrorsInfo() const override + { + return false; + } + + virtual Image::PixelType + pixelType() const override + { + return Image::PixelType::Real32; + } + + virtual Image::PixelType + errorType() const override + { + return Image::PixelType::Real32; + } + + virtual NdArray::RawViewInterface * + getDataSlice( const SliceND & sliceInfo ) override + { +// return new QImageRawView( shared_from_this(), sliceInfo ); + return new QImageRawView( m_data, m_dims, sliceInfo ); + } + + virtual NdArray::Byte * + getMaskSlice( const SliceND & sliceInfo ) override + { + Q_UNUSED( sliceInfo ); + return nullptr; + } + + virtual NdArray::RawViewInterface * + getErrorSlice( const SliceND & sliceInfo ) override + { + Q_UNUSED( sliceInfo ); + return nullptr; + } + + virtual Image::MetaDataInterface::SharedPtr + metaData() override + { + return m_mdi; + } + + bool + valid() const { return m_valid; } + +private: + + QImageII() + { + m_valid = false; + m_dims.resize( 2, 0 ); + + // prepare unit + m_unit = Unit( "n/a" ); + } + + // do not call this directly + bool + init( QString fname ) + { + QImage qimg; + m_valid = qimg.load( fname ); + if ( ! m_valid ) { + return false; + } + qimg = qimg.mirrored( false, true ); + if ( qimg.format() != QImage::Format_ARGB32_Premultiplied ) { + qimg = qimg.convertToFormat( QImage::Format_ARGB32_Premultiplied ); + } + + // prepare dimensions + m_dims[0] = qimg.width(); + m_dims[1] = qimg.height(); + + // prepare metadata + m_mdi = std::make_shared < QImageMDI > ( shared_from_this() ); + + // extract the actual pixels and store it in our own data buffer (only one byte + // per pixel) + m_data = std::make_shared < std::vector < unsigned char > > ( m_dims[0] * m_dims[1] ); + unsigned int * src = (unsigned int *) qimg.bits(); + int count = qimg.width() * qimg.height(); + unsigned char * dst = & m_data-> at( 0 ); + for ( int i = 0 ; i < count ; ++i ) { + * dst = qGray( * src ); + src++; + dst++; + } + return true; + } // init + +private: // data + + Unit m_unit; + VI m_dims; + Image::MetaDataInterface::SharedPtr m_mdi = nullptr; + bool m_valid = false; + + // here we'll store the actual gray scale data + std::shared_ptr < std::vector < unsigned char > > m_data = nullptr; +}; + +QImagePlugin::QImagePlugin( QObject * parent ) : + QObject( parent ) +{ } + +bool +QImagePlugin::handleHook( BaseHook & hookData ) +{ + qDebug() << "QImagePlugin is handling hook #" << hookData.hookId(); + if ( hookData.is < Initialize > () ) { + // we may need to initialize some qt formats here in the future... + return true; + } + else if ( hookData.is < LoadAstroImage > () ) { + LoadAstroImage & hook = static_cast < LoadAstroImage & > ( hookData ); + auto fname = hook.paramsPtr->fileName; + +// hook.result = loadImage( fname ); + hook.result = QImageII::load( fname ); + + // return true if result is not null + return hook.result != nullptr; + } + + qWarning() << "QImagePlugin: Sorrry, dont' know how to handle this hook"; + return false; +} // handleHook + +std::vector < HookId > +QImagePlugin::getInitialHookList() +{ + return { + Initialize::staticId, + LoadAstroImage::staticId + }; +} + +//Image::ImageInterface::SharedPtr +//QImagePlugin::loadImage( const QString & fname ) +//{ +// qDebug() << "qimageplugin loadImage" << fname; + +//// auto qi = std::make_shared < QImageII > ( fname ); +// auto qi = QImageII::load( fname ); +// qDebug() << "qimageplugin loadImage done"; +// if ( qi-> valid() ) { +// return qi; +// } +// else { +// return nullptr; +// } +//} diff --git a/carta/cpp/plugins/casatest1/CasaTest1.h b/carta/cpp/plugins/qimage/QImagePlugin.h similarity index 70% rename from carta/cpp/plugins/casatest1/CasaTest1.h rename to carta/cpp/plugins/qimage/QImagePlugin.h index 557d7d33..dbe64bea 100644 --- a/carta/cpp/plugins/casatest1/CasaTest1.h +++ b/carta/cpp/plugins/qimage/QImagePlugin.h @@ -6,7 +6,7 @@ #include #include -class CasaTest1 : public QObject, public IPlugin +class QImagePlugin : public QObject, public IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.cartaviewer.IPlugin") @@ -14,11 +14,12 @@ class CasaTest1 : public QObject, public IPlugin public: - CasaTest1(QObject *parent = 0); + QImagePlugin(QObject *parent = 0); virtual bool handleHook(BaseHook & hookData) override; virtual std::vector getInitialHookList() override; private: - bool loadImage( const QString & fname, int channel, QImage & result); +// Image::ImageInterface::SharedPtr loadImage(const QString & fname); + }; diff --git a/carta/cpp/plugins/qimage/plugin.json b/carta/cpp/plugins/qimage/plugin.json index 8b0007e0..8dec5f5e 100644 --- a/carta/cpp/plugins/qimage/plugin.json +++ b/carta/cpp/plugins/qimage/plugin.json @@ -1,8 +1,9 @@ { - "name" : "jpeg-png", + "api" : "1", + "name" : "qimage", "version" : "1", "type" : "C++", - "description": "Adds ability to load png and jpeg files", + "description": "Adds ability to load jpeg/png images. (anything Qt supports)", "about" : "Part of carta. Written by Pavol", - "depends" : [] + "depends" : [ ] } diff --git a/carta/cpp/plugins/casatest1/casatest1.pro b/carta/cpp/plugins/qimage/qimage.pro similarity index 97% rename from carta/cpp/plugins/casatest1/casatest1.pro rename to carta/cpp/plugins/qimage/qimage.pro index a9b86fe7..1be919f4 100644 --- a/carta/cpp/plugins/casatest1/casatest1.pro +++ b/carta/cpp/plugins/qimage/qimage.pro @@ -16,10 +16,10 @@ TEMPLATE = lib CONFIG += plugin SOURCES += \ - CasaTest1.cpp + QImagePlugin.cpp HEADERS += \ - CasaTest1.h + QImagePlugin.h casacoreLIBS += -L$${CASACOREDIR}/lib diff --git a/carta/html5/html5.iml b/carta/html5/html5.iml new file mode 120000 index 00000000..31e4f119 --- /dev/null +++ b/carta/html5/html5.iml @@ -0,0 +1 @@ +../../../CARTAvis-externals/html5.iml \ No newline at end of file From 7003db91e55896e1f3968008cc3f0940153f52d8 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Wed, 12 Aug 2015 00:07:38 -0600 Subject: [PATCH 02/37] moved some core classes into carta::lib namespace --- .../cpp/CartaLib/Algorithms/ContourConrec.cpp | 4 +- carta/cpp/CartaLib/Hooks/Histogram.h | 6 +-- carta/cpp/CartaLib/IImage.cpp | 6 +++ carta/cpp/CartaLib/IImage.h | 20 +++++---- carta/cpp/CartaLib/PixelType.cpp | 9 ++-- carta/cpp/CartaLib/PixelType.h | 11 ++--- .../Algorithms/RawView2QImageConverter.cpp | 10 ++--- .../core/Algorithms/RawView2QImageConverter.h | 4 +- .../cpp/core/Algorithms/quantileAlgorithms.h | 4 +- carta/cpp/core/Data/Histogram/Histogram.cpp | 6 +-- carta/cpp/core/Data/Histogram/Histogram.h | 9 ++-- carta/cpp/core/Data/IColoredView.h | 4 ++ carta/cpp/core/Data/Image/Controller.cpp | 4 +- carta/cpp/core/Data/Image/Controller.h | 2 +- carta/cpp/core/Data/Image/DataSource.cpp | 24 +++++------ carta/cpp/core/Data/Image/DataSource.h | 23 +++++----- .../core/DefaultContourGeneratorService.cpp | 2 +- .../cpp/core/DefaultContourGeneratorService.h | 4 +- carta/cpp/core/DummyGridRenderer.cpp | 2 +- carta/cpp/core/DummyGridRenderer.h | 2 +- .../core/Hacks/ContourEditorController.cpp | 2 +- .../cpp/core/Hacks/ContourEditorController.h | 2 +- carta/cpp/core/Hacks/ImageViewController.cpp | 4 +- carta/cpp/core/Hacks/ImageViewController.h | 2 +- carta/cpp/core/ImageRenderService.cpp | 2 + carta/cpp/core/ImageRenderService.h | 4 +- carta/cpp/core/ImageSaveService.cpp | 4 +- carta/cpp/core/ImageSaveService.h | 10 ++--- carta/cpp/plugins/CasaImageLoader/CCImage.h | 24 +++++------ .../CasaImageLoader/CCMetaDataInterface.cpp | 2 +- .../CasaImageLoader/CCMetaDataInterface.h | 5 ++- carta/cpp/plugins/CasaImageLoader/CCRawView.h | 10 ++--- .../CasaImageLoader/CasaImageLoader.cpp | 4 +- .../plugins/CasaImageLoader/CasaImageLoader.h | 2 +- carta/cpp/plugins/Histogram/Histogram1.cpp | 8 ++-- carta/cpp/plugins/Histogram/Histogram1.h | 2 +- .../WcsPlotter/AstWcsGridRenderService.cpp | 2 +- .../WcsPlotter/AstWcsGridRenderService.h | 4 +- .../WcsPlotter/FitsHeaderExtractor.cpp | 2 +- .../plugins/WcsPlotter/FitsHeaderExtractor.h | 4 +- carta/cpp/plugins/qimage/QImagePlugin.cpp | 42 +++++++++---------- carta/scripts/fabfile2.py | 2 +- 42 files changed, 160 insertions(+), 139 deletions(-) diff --git a/carta/cpp/CartaLib/Algorithms/ContourConrec.cpp b/carta/cpp/CartaLib/Algorithms/ContourConrec.cpp index 99d611c1..2f7be094 100644 --- a/carta/cpp/CartaLib/Algorithms/ContourConrec.cpp +++ b/carta/cpp/CartaLib/Algorithms/ContourConrec.cpp @@ -33,7 +33,7 @@ typedef std::vector < double > VD; */ static Carta::Lib::Algorithms::ContourConrec::Result conrecFaster( - NdArray::RawViewInterface * view, + Carta::Lib::NdArray::RawViewInterface * view, int ilb, int iub, int jlb, @@ -65,7 +65,7 @@ conrecFaster( nextRowToReadIn++; // make a double view of this raw row view - NdArray::Double dview( rawRowView, true ); + Carta::Lib::NdArray::Double dview( rawRowView, true ); // shift the row up // note: we could avoid this memory copy if we swapped row[] pointers instead, diff --git a/carta/cpp/CartaLib/Hooks/Histogram.h b/carta/cpp/CartaLib/Hooks/Histogram.h index 9317ecb0..4e0775da 100755 --- a/carta/cpp/CartaLib/Hooks/Histogram.h +++ b/carta/cpp/CartaLib/Hooks/Histogram.h @@ -10,13 +10,13 @@ #include #include "CartaLib/Hooks/HistogramResult.h" -namespace Image { -class ImageInterface; -} namespace Carta { namespace Lib { +namespace Image { +class ImageInterface; +} namespace Hooks { class HistogramHook : public BaseHook diff --git a/carta/cpp/CartaLib/IImage.cpp b/carta/cpp/CartaLib/IImage.cpp index 75e0773d..4b75e3e6 100644 --- a/carta/cpp/CartaLib/IImage.cpp +++ b/carta/cpp/CartaLib/IImage.cpp @@ -6,6 +6,9 @@ #include "IImage.h" +namespace Carta { +namespace Lib { + const Unit &Image::ImageInterface::getPixelUnit() const { qFatal( "Calling unimplemented virtual function... "); @@ -65,3 +68,6 @@ Image::MetaDataInterface::~MetaDataInterface() { } + +} +} diff --git a/carta/cpp/CartaLib/IImage.h b/carta/cpp/CartaLib/IImage.h index 1d739b94..30a2ab3e 100644 --- a/carta/cpp/CartaLib/IImage.h +++ b/carta/cpp/CartaLib/IImage.h @@ -17,14 +17,12 @@ /// @todo move everything here Carta::Lib namespace - #pragma once #include "PixelType.h" #include "Nullable.h" #include "Slice.h" #include "ICoordinateFormatter.h" -//#include "ICoordinateGridPlotter.h" #include "IPlotLabelGenerator.h" #include #include @@ -32,6 +30,10 @@ #include #include +namespace Carta { +namespace Lib { + + /// description of a unit /// this will hopefully evolve a lot... /// for now it's essentially an alias for QString @@ -61,7 +63,7 @@ class RawViewInterface public: typedef std::vector < int > VI; - typedef Image::PixelType PixelType; + typedef Carta::Lib::Image::PixelType PixelType; /// traversal order enum class Traversal @@ -371,6 +373,10 @@ class ImageInterface }; } // namespace Image + +} +} + /// API testing /// this is never executed, only compiled for API testing __attribute__ ( ( unused ) ) @@ -378,7 +384,7 @@ static void test_apis() { // get an image.... (pretend) - Image::ImageInterface * ii = nullptr; + Carta::Lib::Image::ImageInterface * ii = nullptr; Slice1D r; r.start( 10 ).start( 20 ).step( 8 ); @@ -402,7 +408,7 @@ test_apis() SliceND si; // get the raw view of the data - NdArray::RawViewInterface * rawView = ii-> getDataSlice( si ); + Carta::Lib::NdArray::RawViewInterface * rawView = ii-> getDataSlice( si ); // access a single pixel in the data, returned in raw binary form const char * ptr = rawView-> get( { 1, 2, 3 } @@ -416,10 +422,10 @@ test_apis() ); // make a double view (from the raw view) - NdArray::TypedView < double > doubleReader( rawView ); + Carta::Lib::NdArray::TypedView < double > doubleReader( rawView ); // or using the typedef - NdArray::Double doubleReader2( rawView ); + Carta::Lib::NdArray::Double doubleReader2( rawView ); // extract a single pixel double x = doubleReader2.get( { 1, 2, 3 } diff --git a/carta/cpp/CartaLib/PixelType.cpp b/carta/cpp/CartaLib/PixelType.cpp index bb0d1131..74743bb0 100644 --- a/carta/cpp/CartaLib/PixelType.cpp +++ b/carta/cpp/CartaLib/PixelType.cpp @@ -5,6 +5,9 @@ #include "PixelType.h" #include +namespace Carta { +namespace Lib { + int Image::pixelType2int( const Image::PixelType & type ) { @@ -27,9 +30,6 @@ Image::pixelType2int( const Image::PixelType & type ) // } //} -namespace Carta { - - QString toStr( Image::PixelType t ) { @@ -67,3 +67,6 @@ toStr( Image::PixelType t ) } } +} + + diff --git a/carta/cpp/CartaLib/PixelType.h b/carta/cpp/CartaLib/PixelType.h index 31cf08bc..d776dcb4 100644 --- a/carta/cpp/CartaLib/PixelType.h +++ b/carta/cpp/CartaLib/PixelType.h @@ -8,6 +8,8 @@ #include #include +namespace Carta { +namespace Lib { namespace Image { /// supported pixel types in images @@ -114,6 +116,7 @@ struct CType2PixelType { } + /// template ton convert from one type to another template struct TypedConverters; @@ -143,7 +146,7 @@ struct Type2CvtFunc{ }; template < typename DstType> -typename Type2CvtFunc::Type getConverter( Image::PixelType srcType) +typename Type2CvtFunc::Type getConverter( Carta::Lib::Image::PixelType srcType) { switch (srcType) { case Image::PixelType::Byte: @@ -167,9 +170,6 @@ typename Type2CvtFunc::Type getConverter( Image::PixelType srcType) } } - -namespace Carta { - /// convenience function to convert a type to a string QString toStr( Image::PixelType t); @@ -185,5 +185,6 @@ QString toStr( Image::PixelType t); //// return sizeof( Image::PixelType2CType::type); //} - } +} + diff --git a/carta/cpp/core/Algorithms/RawView2QImageConverter.cpp b/carta/cpp/core/Algorithms/RawView2QImageConverter.cpp index 018a2017..f2aff9fb 100644 --- a/carta/cpp/core/Algorithms/RawView2QImageConverter.cpp +++ b/carta/cpp/core/Algorithms/RawView2QImageConverter.cpp @@ -16,7 +16,7 @@ template < typename Scalar > static typename std::tuple < Scalar, Scalar > computeClips( - NdArray::TypedView < Scalar > & view, + Carta::Lib::NdArray::TypedView < Scalar > & view, double perc ) { @@ -37,7 +37,7 @@ computeClips( /// \param m_qImage template < class Pipeline > static void -rawView2QImage( NdArray::RawViewInterface * rawView, Pipeline & pipe, QImage & qImage ) +rawView2QImage( Carta::Lib::NdArray::RawViewInterface * rawView, Pipeline & pipe, QImage & qImage ) { qDebug() << "rv2qi" << rawView-> dims(); typedef double Scalar; @@ -58,7 +58,7 @@ rawView2QImage( NdArray::RawViewInterface * rawView, Pipeline & pipe, QImage & q qImage.bits() + size.width() * ( size.height() - 1 ) * 4 ); // make a double view - NdArray::TypedView < Scalar > typedView( rawView, false ); + Carta::Lib::NdArray::TypedView < Scalar > typedView( rawView, false ); /// @todo for more efficiency we should switch to the higher performance view apis /// and apply some basic openmp/cilk @@ -157,10 +157,10 @@ RawView2QImageConverter3::setColormap( Lib::PixelPipeline::IColormapNamed::Share } RawView2QImageConverter3 & -RawView2QImageConverter3::setView( NdArray::RawViewInterface * rawView, QString id ) +RawView2QImageConverter3::setView( Carta::Lib::NdArray::RawViewInterface * rawView, QString id ) { Q_UNUSED( id ); - m_typedView.reset( new NdArray::TypedView < double > ( rawView, false ) ); + m_typedView.reset( new Carta::Lib::NdArray::TypedView < double > ( rawView, false ) ); return * this; } diff --git a/carta/cpp/core/Algorithms/RawView2QImageConverter.h b/carta/cpp/core/Algorithms/RawView2QImageConverter.h index 51ee8bd4..b382127b 100644 --- a/carta/cpp/core/Algorithms/RawView2QImageConverter.h +++ b/carta/cpp/core/Algorithms/RawView2QImageConverter.h @@ -72,7 +72,7 @@ class RawView2QImageConverter3 /// Make sure it's valid /// during a call to go()! Me & - setView( NdArray::RawViewInterface * rawView, QString id = "" ); + setView( Carta::Lib::NdArray::RawViewInterface * rawView, QString id = "" ); /// compute clips using the current view (set by setView()) /// @param clip for example 0.95 means 95% @@ -103,7 +103,7 @@ class RawView2QImageConverter3 private: /// pointer to the view // NdArray::RawViewInterface * m_rawView = nullptr; - NdArray::TypedView::UniquePtr m_typedView; + Carta::Lib::NdArray::TypedView::UniquePtr m_typedView; /// cache for images QCache < QString, QImage > m_imageCache; /// unoptimized pipeline diff --git a/carta/cpp/core/Algorithms/quantileAlgorithms.h b/carta/cpp/core/Algorithms/quantileAlgorithms.h index 4f2668e9..d5867b46 100644 --- a/carta/cpp/core/Algorithms/quantileAlgorithms.h +++ b/carta/cpp/core/Algorithms/quantileAlgorithms.h @@ -37,7 +37,7 @@ template < typename Scalar > static typename std::vector < Scalar > quantiles2pixels( - NdArray::TypedView < Scalar > & view, + Carta::Lib::NdArray::TypedView < Scalar > & view, std::vector < double > quant ) { @@ -98,7 +98,7 @@ quantiles2pixels( /// algorithm for finding quantile from pixel value template < typename Scalar > static -double pixel2quantile ( NdArray::TypedView < Scalar > & view, Scalar pixel) +double pixel2quantile ( Carta::Lib::NdArray::TypedView < Scalar > & view, Scalar pixel) { u_int64_t totalCount = 0; u_int64_t countBelow = 0; diff --git a/carta/cpp/core/Data/Histogram/Histogram.cpp b/carta/cpp/core/Data/Histogram/Histogram.cpp index dd86b0a7..c401926b 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.cpp +++ b/carta/cpp/core/Data/Histogram/Histogram.cpp @@ -242,8 +242,8 @@ void Histogram::_finishColor(){ m_stateData.flushState(); } -std::vector> Histogram::_generateData(Controller* controller){ - std::vector> result; +std::vector> Histogram::_generateData(Controller* controller){ + std::vector> result; if ( controller != nullptr ){ result = controller->getDataSources(); } @@ -925,7 +925,7 @@ void Histogram::_loadData( Controller* controller ){ double minIntensity = _getBufferedIntensity( CLIP_MIN, CLIP_MIN_PERCENT ); double maxIntensity = _getBufferedIntensity( CLIP_MAX, CLIP_MAX_PERCENT ); - std::vector> dataSources; + std::vector> dataSources; if ( controller != nullptr ){ int stackedImageCount = controller->getStackedImageCount(); if ( stackedImageCount > 0 ){ diff --git a/carta/cpp/core/Data/Histogram/Histogram.h b/carta/cpp/core/Data/Histogram/Histogram.h index fa260423..2a2358da 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.h +++ b/carta/cpp/core/Data/Histogram/Histogram.h @@ -17,13 +17,12 @@ namespace Lib { namespace PixelPipeline { class IColormapNamed; } -} -} - namespace Image { class ImageInterface; - } +} +} + class ImageView; @@ -376,7 +375,7 @@ private slots: */ QString _getActualGraphStyle( const QString& styleStr ); - std::vector> _generateData(Controller* controller); + std::vector> _generateData(Controller* controller); //Bin count <-> Bin width conversion. double _toBinWidth( int count ) const; diff --git a/carta/cpp/core/Data/IColoredView.h b/carta/cpp/core/Data/IColoredView.h index f9b32ec3..ffc47953 100755 --- a/carta/cpp/core/Data/IColoredView.h +++ b/carta/cpp/core/Data/IColoredView.h @@ -6,9 +6,13 @@ #ifndef ICOLOREDVIEW_H_ #define ICOLOREDVIEW_H_ +namespace Carta { +namespace Lib { namespace Image { class ImageInterface; } +} +} namespace Carta { diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 7314fa30..d50555fe 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -245,9 +245,9 @@ double Controller::getPercentile( int frameLow, int frameHigh, double intensity } -std::vector> Controller::getDataSources(){ +std::vector> Controller::getDataSources(){ //For right now, we are only going to do a histogram of a single image. - std::vector> images; + std::vector> images; int dataCount = m_datas.size(); if ( dataCount > 0 ){ int imageIndex = m_selectImage->getIndex(); diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index efac3f73..511c81b7 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -108,7 +108,7 @@ class Controller: public QObject, public Carta::State::CartaObject, public IColo - std::vector> getDataSources(); + std::vector> getDataSources(); /** diff --git a/carta/cpp/core/Data/Image/DataSource.cpp b/carta/cpp/core/Data/Image/DataSource.cpp index 72a826e5..03ace6db 100755 --- a/carta/cpp/core/Data/Image/DataSource.cpp +++ b/carta/cpp/core/Data/Image/DataSource.cpp @@ -172,9 +172,9 @@ QPointF DataSource::_getImagePt( QPointF screenPt, bool* valid ) const { QString DataSource::_getPixelValue( double x, double y, int frameIndex ) const { QString pixelValue = ""; if ( x >= 0 && x < m_image->dims()[0] && y >= 0 && y < m_image->dims()[1] ) { - NdArray::RawViewInterface* rawData = _getRawData( frameIndex, frameIndex ); + Carta::Lib::NdArray::RawViewInterface* rawData = _getRawData( frameIndex, frameIndex ); if ( rawData != nullptr ){ - NdArray::TypedView view( rawData, false ); + Carta::Lib::NdArray::TypedView view( rawData, false ); pixelValue = QString::number( view.get( {(int)(round(x)), (int)(round(y)) } ) ); } } @@ -228,7 +228,7 @@ QString DataSource::_getFileName() const { return m_state.getValue(DATA_PATH); } -std::shared_ptr DataSource::_getImage(){ +std::shared_ptr DataSource::_getImage(){ return m_image; } @@ -241,9 +241,9 @@ std::shared_ptr DataSource bool DataSource::_getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const { bool intensityFound = false; - NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh ); + Carta::Lib::NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh ); if ( rawData != nullptr ){ - NdArray::TypedView view( rawData, false ); + Carta::Lib::NdArray::TypedView view( rawData, false ); // read in all values from the view into an array // we need our own copy because we'll do quickselect on it... std::vector < double > allValues; @@ -272,11 +272,11 @@ bool DataSource::_getIntensity( int frameLow, int frameHigh, double percentile, double DataSource::_getPercentile( int frameLow, int frameHigh, double intensity ) const { double percentile = 0; - NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh ); + Carta::Lib::NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh ); if ( rawData != nullptr ){ u_int64_t totalCount = 0; u_int64_t countBelow = 0; - NdArray::TypedView view( rawData, false ); + Carta::Lib::NdArray::TypedView view( rawData, false ); view.forEach([&](const double& val) { if( Q_UNLIKELY( std::isnan(val))){ return; @@ -313,8 +313,8 @@ QString DataSource::_getPixelUnits() const { return units; } -NdArray::RawViewInterface * DataSource::_getRawData( int channelStart, int channelEnd ) const { - NdArray::RawViewInterface* rawData = nullptr; +Carta::Lib::NdArray::RawViewInterface * DataSource::_getRawData( int channelStart, int channelEnd ) const { + Carta::Lib::NdArray::RawViewInterface* rawData = nullptr; if ( m_image ){ auto frameSlice = SliceND().next(); for( size_t i=2; i < m_image->dims().size(); i++ ){ @@ -425,7 +425,7 @@ void DataSource::_load(int frameIndex, bool /*recomputeClipsOnNewFrame*/, double } // get a view of the data using the slice description and make a shared pointer out of it - NdArray::RawViewInterface::SharedPtr view( m_image-> getDataSlice( frameSlice ) ); + Carta::Lib::NdArray::RawViewInterface::SharedPtr view( m_image-> getDataSlice( frameSlice ) ); //Update the clip values _updateClips( view, frameIndex, minClipPercentile, maxClipPercentile ); @@ -603,10 +603,10 @@ void DataSource::setGamma( double gamma ){ } -void DataSource::_updateClips( std::shared_ptr& view, int frameIndex, +void DataSource::_updateClips( std::shared_ptr& view, int frameIndex, double minClipPercentile, double maxClipPercentile ){ std::vector clips = m_quantileCache[ frameIndex]; - NdArray::Double doubleView( view.get(), false ); + Carta::Lib::NdArray::Double doubleView( view.get(), false ); std::vector newClips = Carta::Core::Algorithms::quantiles2pixels( doubleView, {minClipPercentile, maxClipPercentile }); bool clipsChanged = false; diff --git a/carta/cpp/core/Data/Image/DataSource.h b/carta/cpp/core/Data/Image/DataSource.h index b56a8b93..18b7b6f8 100755 --- a/carta/cpp/core/Data/Image/DataSource.h +++ b/carta/cpp/core/Data/Image/DataSource.h @@ -12,14 +12,6 @@ #include #include -namespace NdArray { - class RawViewInterface; -} - -namespace Image { - class ImageInterface; -} - class CoordinateFormatterInterface; namespace Carta { @@ -28,6 +20,13 @@ namespace Lib { namespace PixelPipeline { class CustomizablePixelPipeline; } + namespace NdArray { + class RawViewInterface; + } + + namespace Image { + class ImageInterface; + } } namespace Core { namespace ImageRenderService { @@ -173,7 +172,7 @@ private slots: /** * Returns the underlying image. */ - std::shared_ptr _getImage(); + std::shared_ptr _getImage(); /** * Returns the image's file name. @@ -272,7 +271,7 @@ private slots: * @param frameHigh the upper bound for the channel range or -1 for the whole image. * @return the raw data or nullptr if there is none. */ - NdArray::RawViewInterface * _getRawData( int frameLow, int frameHigh ) const; + Carta::Lib::NdArray::RawViewInterface * _getRawData( int frameLow, int frameHigh ) const; void _gridChanged( const Carta::State::StateInterface& state, bool renderImage ); @@ -357,7 +356,7 @@ private slots: - void _updateClips( std::shared_ptr& view, int frameIndex, + void _updateClips( std::shared_ptr& view, int frameIndex, double minClipPercentile, double maxClipPercentile ); /** @@ -378,7 +377,7 @@ private slots: static CoordinateSystems* m_coords; //Pointer to image interface. - std::shared_ptr m_image; + std::shared_ptr m_image; /// coordinate formatter std::shared_ptr m_coordinateFormatter; diff --git a/carta/cpp/core/DefaultContourGeneratorService.cpp b/carta/cpp/core/DefaultContourGeneratorService.cpp index 253cef13..76791fcf 100644 --- a/carta/cpp/core/DefaultContourGeneratorService.cpp +++ b/carta/cpp/core/DefaultContourGeneratorService.cpp @@ -25,7 +25,7 @@ DefaultContourGeneratorService::setLevels( const std::vector < double > & levels } void -DefaultContourGeneratorService::setInput( NdArray::RawViewInterface::SharedPtr rawView ) +DefaultContourGeneratorService::setInput( Carta::Lib::NdArray::RawViewInterface::SharedPtr rawView ) { m_rawView = rawView; } diff --git a/carta/cpp/core/DefaultContourGeneratorService.h b/carta/cpp/core/DefaultContourGeneratorService.h index 47d36003..7c732d22 100644 --- a/carta/cpp/core/DefaultContourGeneratorService.h +++ b/carta/cpp/core/DefaultContourGeneratorService.h @@ -27,7 +27,7 @@ class DefaultContourGeneratorService : public Lib::IContourGeneratorService setLevels( const std::vector < double > & levels ) override; virtual void - setInput( NdArray::RawViewInterface::SharedPtr rawView ) override; + setInput( Carta::Lib::NdArray::RawViewInterface::SharedPtr rawView ) override; virtual JobId start( JobId jobId ) override; @@ -42,7 +42,7 @@ private slots: std::vector < double > m_levels; JobId m_lastJobId = - 1; - NdArray::RawViewInterface::SharedPtr m_rawView = nullptr; + Carta::Lib::NdArray::RawViewInterface::SharedPtr m_rawView = nullptr; QTimer m_timer; }; diff --git a/carta/cpp/core/DummyGridRenderer.cpp b/carta/cpp/core/DummyGridRenderer.cpp index daad15d6..8bc3cb15 100644 --- a/carta/cpp/core/DummyGridRenderer.cpp +++ b/carta/cpp/core/DummyGridRenderer.cpp @@ -17,7 +17,7 @@ DummyGridRenderer::DummyGridRenderer() } void -DummyGridRenderer::setInputImage( Image::ImageInterface::SharedPtr ) +DummyGridRenderer::setInputImage( Carta::Lib::Image::ImageInterface::SharedPtr ) { } void diff --git a/carta/cpp/core/DummyGridRenderer.h b/carta/cpp/core/DummyGridRenderer.h index 035dc80d..c5af442f 100644 --- a/carta/cpp/core/DummyGridRenderer.h +++ b/carta/cpp/core/DummyGridRenderer.h @@ -28,7 +28,7 @@ class DummyGridRenderer DummyGridRenderer(); virtual void - setInputImage( Image::ImageInterface::SharedPtr ) override; + setInputImage( Carta::Lib::Image::ImageInterface::SharedPtr ) override; virtual void setImageRect( const QRectF & ) override; diff --git a/carta/cpp/core/Hacks/ContourEditorController.cpp b/carta/cpp/core/Hacks/ContourEditorController.cpp index cce1735d..83a2f73f 100644 --- a/carta/cpp/core/Hacks/ContourEditorController.cpp +++ b/carta/cpp/core/Hacks/ContourEditorController.cpp @@ -93,7 +93,7 @@ ContourEditorController::startRendering( ContourEditorController::JobId jobId ) } void -ContourEditorController::setInput( NdArray::RawViewInterface::SharedPtr rawView ) +ContourEditorController::setInput( Carta::Lib::NdArray::RawViewInterface::SharedPtr rawView ) { m_contourSvc-> setInput( rawView ); } diff --git a/carta/cpp/core/Hacks/ContourEditorController.h b/carta/cpp/core/Hacks/ContourEditorController.h index 19da5c57..0cbce1ed 100644 --- a/carta/cpp/core/Hacks/ContourEditorController.h +++ b/carta/cpp/core/Hacks/ContourEditorController.h @@ -55,7 +55,7 @@ class ContourEditorController : public QObject /// set the input data void - setInput( NdArray::RawViewInterface::SharedPtr rawView ); + setInput( Carta::Lib::NdArray::RawViewInterface::SharedPtr rawView ); signals: diff --git a/carta/cpp/core/Hacks/ImageViewController.cpp b/carta/cpp/core/Hacks/ImageViewController.cpp index e47bb640..dbd01e5c 100644 --- a/carta/cpp/core/Hacks/ImageViewController.cpp +++ b/carta/cpp/core/Hacks/ImageViewController.cpp @@ -429,12 +429,12 @@ ImageViewController::loadFrame( int frame ) } // get a view of the data using the slice description and make a shared pointer out of it - NdArray::RawViewInterface::SharedPtr view( m_astroImage-> getDataSlice( frameSlice ) ); + Carta::Lib::NdArray::RawViewInterface::SharedPtr view( m_astroImage-> getDataSlice( frameSlice ) ); // compute 95% clip values, unless we already have them in the cache std::vector < double > clips = m_quantileCache[m_currentFrame]; if ( clips.size() < 2 ) { - NdArray::Double doubleView( view.get(), false ); + Carta::Lib::NdArray::Double doubleView( view.get(), false ); clips = Carta::Core::Algorithms::quantiles2pixels( doubleView, { 0.025, 0.975 } ); diff --git a/carta/cpp/core/Hacks/ImageViewController.h b/carta/cpp/core/Hacks/ImageViewController.h index 8585a7b8..3d89c503 100644 --- a/carta/cpp/core/Hacks/ImageViewController.h +++ b/carta/cpp/core/Hacks/ImageViewController.h @@ -303,7 +303,7 @@ private slots: QString m_rawViewName; /// the loaded astro image - Image::ImageInterface::SharedPtr m_astroImage = nullptr; + Carta::Lib::Image::ImageInterface::SharedPtr m_astroImage = nullptr; /// clip cache, hard-coded to single quantile std::vector < std::vector < double > > m_quantileCache; diff --git a/carta/cpp/core/ImageRenderService.cpp b/carta/cpp/core/ImageRenderService.cpp index 300d8ebe..bc66c12a 100644 --- a/carta/cpp/core/ImageRenderService.cpp +++ b/carta/cpp/core/ImageRenderService.cpp @@ -7,6 +7,8 @@ #include #include +namespace NdArray = Carta::Lib::NdArray; + // most optimal Qt format seems to be Format_ARGB32_Premultiplied static constexpr QImage::Format OptimalQImageFormat = QImage::Format_ARGB32_Premultiplied; diff --git a/carta/cpp/core/ImageRenderService.h b/carta/cpp/core/ImageRenderService.h index 6d0e823c..148efb7c 100644 --- a/carta/cpp/core/ImageRenderService.h +++ b/carta/cpp/core/ImageRenderService.h @@ -151,7 +151,7 @@ class Service : public QObject /// caching will not be used) /// void - setInputView( NdArray::RawViewInterface::SharedPtr view, QString cacheId = QString() ); + setInputView( Carta::Lib::NdArray::RawViewInterface::SharedPtr view, QString cacheId = QString() ); /// /// \brief set the desired output size of the image @@ -256,7 +256,7 @@ protected slots: private: // the following are rendering parameters - NdArray::RawViewInterface::SharedPtr m_inputView = nullptr; + Carta::Lib::NdArray::RawViewInterface::SharedPtr m_inputView = nullptr; QString m_inputViewCacheId; QString m_pixelPipelineCacheId; QSize m_outputSize = QSize( 10, 10 ); diff --git a/carta/cpp/core/ImageSaveService.cpp b/carta/cpp/core/ImageSaveService.cpp index 32efe954..1303fff5 100644 --- a/carta/cpp/core/ImageSaveService.cpp +++ b/carta/cpp/core/ImageSaveService.cpp @@ -9,7 +9,7 @@ namespace Core namespace ImageSaveService { -ImageSaveService::ImageSaveService( QString savename, std::shared_ptr &m_image, std::shared_ptr &m_pixelPipeline, QString filename, QObject * parent ) : QObject( parent ) +ImageSaveService::ImageSaveService( QString savename, std::shared_ptr &m_image, std::shared_ptr &m_pixelPipeline, QString filename, QObject * parent ) : QObject( parent ) { m_outputFilename = savename; m_imageCopy = m_image; @@ -64,7 +64,7 @@ void ImageSaveService::_prepareData( int frameIndex, double /*minClipPercentile* } // get a view of the data using the slice description and make a shared pointer out of it - NdArray::RawViewInterface::SharedPtr view( m_imageCopy-> getDataSlice( frameSlice ) ); + Carta::Lib::NdArray::RawViewInterface::SharedPtr view( m_imageCopy-> getDataSlice( frameSlice ) ); m_renderService-> setPixelPipeline( m_pixelPipelineCopy, m_pixelPipelineCopy-> cacheId()); diff --git a/carta/cpp/core/ImageSaveService.h b/carta/cpp/core/ImageSaveService.h index 07122f72..1428c48b 100644 --- a/carta/cpp/core/ImageSaveService.h +++ b/carta/cpp/core/ImageSaveService.h @@ -22,11 +22,11 @@ namespace Carta { namespace PixelPipeline { class CustomizablePixelPipeline; } + namespace Image { + class ImageInterface; + } } } -namespace Image { - class ImageInterface; -} namespace Carta{ namespace Core{ @@ -44,7 +44,7 @@ class ImageSaveService : public QObject /// \param m_pixelPipeline pixel pipeline. /// \param filename the input FITS file explicit - ImageSaveService( QString savename, std::shared_ptr &m_image, std::shared_ptr &m_pixelPipeline, QString filename, QObject * parent = 0 ); + ImageSaveService( QString savename, std::shared_ptr &m_image, std::shared_ptr &m_pixelPipeline, QString filename, QObject * parent = 0 ); /// /// \brief set the desired output size of the image @@ -119,7 +119,7 @@ private slots: Qt::AspectRatioMode m_aspectRatioMode; //Pointer to image interface. - std::shared_ptr m_imageCopy; + std::shared_ptr m_imageCopy; ///pixel pipeline std::shared_ptr m_pixelPipelineCopy; diff --git a/carta/cpp/plugins/CasaImageLoader/CCImage.h b/carta/cpp/plugins/CasaImageLoader/CCImage.h index 0549e73e..280f99ec 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCImage.h +++ b/carta/cpp/plugins/CasaImageLoader/CCImage.h @@ -15,7 +15,7 @@ /// helper base class so that we can easily determine if this is a an image /// interface created by this plugin if we ever want to down-cast it... class CCImageBase - : public Image::ImageInterface + : public Carta::Lib::Image::ImageInterface { CLASS_BOILERPLATE( CCImageBase ); @@ -51,7 +51,7 @@ class CCImage CLASS_BOILERPLATE( CCImage ); public: - virtual const Unit & + virtual const Carta::Lib::Unit & getPixelUnit() const override { return m_unit; @@ -77,26 +77,26 @@ class CCImage /// it is safe to static_cast the instance of CCImageBase to CCImage /// based on this type - virtual Image::PixelType + virtual Carta::Lib::Image::PixelType pixelType() const override { return m_pixelType; } - virtual Image::PixelType + virtual Carta::Lib::Image::PixelType errorType() const override { qFatal( "not implemented" ); } - virtual NdArray::RawViewInterface * + virtual Carta::Lib::NdArray::RawViewInterface * getDataSlice( const SliceND & sliceInfo ) override { return new CCRawView < PType > ( this, sliceInfo ); } /// \todo implement this - virtual NdArray::Byte * + virtual Carta::Lib::NdArray::Byte * getMaskSlice( const SliceND & sliceInfo) override { Q_UNUSED( sliceInfo ); @@ -104,14 +104,14 @@ class CCImage } /// \todo implement this - virtual NdArray::RawViewInterface * + virtual Carta::Lib::NdArray::RawViewInterface * getErrorSlice( const SliceND & sliceInfo) override { Q_UNUSED( sliceInfo ); qFatal( "not implemented" ); } - virtual Image::MetaDataInterface::SharedPtr + virtual Carta::Lib::Image::MetaDataInterface::SharedPtr metaData() override { return m_meta; @@ -123,10 +123,10 @@ class CCImage // create an image interface instance and populate it with various // values from casa::ImageInterface CCImage::SharedPtr img = std::make_shared < CCImage < PType > > (); - img-> m_pixelType = Image::CType2PixelType < PType >::type; + img-> m_pixelType = Carta::Lib::Image::CType2PixelType < PType >::type; img-> m_dims = casaImage-> shape().asStdVector(); img-> m_casaII = casaImage; - img-> m_unit = Unit( casaImage-> units().getName().c_str() ); + img-> m_unit = Carta::Lib::Unit( casaImage-> units().getName().c_str() ); // get title and escape html characters in case there are any QString htmlTitle = casaImage->imageInfo().objectName().c_str(); @@ -163,7 +163,7 @@ class CCImage protected: /// type of the image data - Image::PixelType m_pixelType; + Carta::Lib::Image::PixelType m_pixelType; /// cached dimensions of the image std::vector < int > m_dims; @@ -172,7 +172,7 @@ class CCImage casa::ImageInterface < PType > * m_casaII; /// cached unit - Unit m_unit; + Carta::Lib::Unit m_unit; /// meta data pointer CCMetaDataInterface::SharedPtr m_meta; diff --git a/carta/cpp/plugins/CasaImageLoader/CCMetaDataInterface.cpp b/carta/cpp/plugins/CasaImageLoader/CCMetaDataInterface.cpp index dd11a497..8bd3e73b 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCMetaDataInterface.cpp +++ b/carta/cpp/plugins/CasaImageLoader/CCMetaDataInterface.cpp @@ -13,7 +13,7 @@ CCMetaDataInterface::CCMetaDataInterface(QString htmlTitle, std::shared_ptr -class CCMetaDataInterface : public Image::MetaDataInterface +class CCMetaDataInterface + : public Carta::Lib::Image::MetaDataInterface { CLASS_BOILERPLATE( CCMetaDataInterface); public: CCMetaDataInterface(QString htmlTitle, std::shared_ptr casaCS); - virtual Image::MetaDataInterface * + virtual Carta::Lib::Image::MetaDataInterface * clone() override; virtual CoordinateFormatterInterface::SharedPtr diff --git a/carta/cpp/plugins/CasaImageLoader/CCRawView.h b/carta/cpp/plugins/CasaImageLoader/CCRawView.h index 12e2cb09..e0f39d1b 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCRawView.h +++ b/carta/cpp/plugins/CasaImageLoader/CCRawView.h @@ -21,7 +21,7 @@ class CCImage; /// \todo Implement indexed slices (i.e. axis removal) template < typename PType > class CCRawView - : public NdArray::RawViewInterface + : public Carta::Lib::NdArray::RawViewInterface { public: @@ -173,7 +173,7 @@ CCRawView < PType >::CCRawView( CCImage < PType > * ccimage, const SliceND::Appl template < typename PType > const char * -CCRawView < PType >::get( const NdArray::RawViewInterface::VI & pos ) +CCRawView < PType >::get( const Carta::Lib::NdArray::RawViewInterface::VI & pos ) { // preconditions if ( CARTA_RUNTIME_CHECKS && pos.size() > dims().size() ) { @@ -208,9 +208,9 @@ template < typename PType > void CCRawView < PType >::forEach( std::function < void (const char *) > func, - NdArray::RawViewInterface::Traversal traversal ) + Carta::Lib::NdArray::RawViewInterface::Traversal traversal ) { - if ( traversal != NdArray::RawViewInterface::Traversal::Sequential ) { + if ( traversal != Carta::Lib::NdArray::RawViewInterface::Traversal::Sequential ) { qFatal( "sorry, not implemented yet" ); } auto casaII = m_ccimage-> m_casaII; @@ -254,7 +254,7 @@ CCRawView < PType >::forEach( } // forEach template < typename PType > -const NdArray::RawViewInterface::VI & +const Carta::Lib::NdArray::RawViewInterface::VI & CCRawView < PType >::currentPos() { qFatal( "Not implemented yet"); diff --git a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp index a75082d7..bcc1f70e 100755 --- a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp +++ b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp @@ -74,7 +74,7 @@ static CCImageBase::SharedPtr tryCast( casa::LatticeBase * lat) /// \param result where to store the result /// \return true if successful, false otherwise /// -Image::ImageInterface::SharedPtr CasaImageLoader::loadImage( const QString & fname) +Carta::Lib::Image::ImageInterface::SharedPtr CasaImageLoader::loadImage( const QString & fname) { qDebug() << "CasaImageLoader plugin trying to load image: " << fname; @@ -140,7 +140,7 @@ Image::ImageInterface::SharedPtr CasaImageLoader::loadImage( const QString & fna } if( res) { - qDebug() << "Created image interface with type=" << Carta::toStr( res->pixelType()); + qDebug() << "Created image interface with type=" << Carta::Lib::toStr( res->pixelType()); return res; } diff --git a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.h b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.h index a81c8c84..f683aebe 100644 --- a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.h +++ b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.h @@ -23,5 +23,5 @@ class CasaImageLoader : public QObject, public IPlugin private: - Image::ImageInterface::SharedPtr loadImage(const QString & fname); + Carta::Lib::Image::ImageInterface::SharedPtr loadImage(const QString & fname); }; diff --git a/carta/cpp/plugins/Histogram/Histogram1.cpp b/carta/cpp/plugins/Histogram/Histogram1.cpp index e1107c6e..fc316eb8 100755 --- a/carta/cpp/plugins/Histogram/Histogram1.cpp +++ b/carta/cpp/plugins/Histogram/Histogram1.cpp @@ -45,7 +45,7 @@ std::pair Histogram1::_getChannelBounds( double freqMin, double freqMax if ( m_cartaImage ){ CCImageBase * ptr1 = dynamic_cast( m_cartaImage.get()); if ( ptr1 ){ - if (m_cartaImage->pixelType() == Image::PixelType::Real32 ){ + if (m_cartaImage->pixelType() == Carta::Lib::Image::PixelType::Real32 ){ casa::ImageInterface * casaImage = nullptr; #ifndef Q_OS_MAC casaImage = dynamic_cast * > (ptr1-> getCasaImage()); @@ -149,7 +149,7 @@ std::pair Histogram1::_getFrequencyBounds( int channelMin, int ch if ( m_cartaImage ){ CCImageBase * ptr1 = dynamic_cast( m_cartaImage.get()); if ( ptr1 ){ - if (m_cartaImage->pixelType() == Image::PixelType::Real32 ){ + if (m_cartaImage->pixelType() == Carta::Lib::Image::PixelType::Real32 ){ casa::ImageInterface * casaImage = nullptr; #ifndef Q_OS_MAC casaImage = dynamic_cast * > (ptr1-> getCasaImage()); @@ -233,7 +233,7 @@ bool Histogram1::handleHook( BaseHook & hookData ){ Carta::Lib::Hooks::HistogramHook & hook = static_cast < Carta::Lib::Hooks::HistogramHook & > ( hookData ); - std::vector> images = hook.paramsPtr->dataSource; + std::vector> images = hook.paramsPtr->dataSource; casa::ImageInterface * casaImage = nullptr; if ( images.size() > 0 ){ m_cartaImage = images.front(); @@ -244,7 +244,7 @@ bool Histogram1::handleHook( BaseHook & hookData ){ return false; } - if (m_cartaImage->pixelType() == Image::PixelType::Real32 ){ + if (m_cartaImage->pixelType() == Carta::Lib::Image::PixelType::Real32 ){ casa::ImageInterface * casaImage = nullptr; #ifndef Q_OS_MAC diff --git a/carta/cpp/plugins/Histogram/Histogram1.h b/carta/cpp/plugins/Histogram/Histogram1.h index 2bad3f71..62c8e59b 100755 --- a/carta/cpp/plugins/Histogram/Histogram1.h +++ b/carta/cpp/plugins/Histogram/Histogram1.h @@ -58,6 +58,6 @@ class Histogram1 : public QObject, public IPlugin //Current histogram image. Member variable so image pointer //is not destroyed. - std::shared_ptr m_cartaImage; + std::shared_ptr m_cartaImage; }; diff --git a/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.cpp b/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.cpp index cd4ba935..f4459ee2 100644 --- a/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.cpp +++ b/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.cpp @@ -64,7 +64,7 @@ AstWcsGridRenderService::~AstWcsGridRenderService() { } void -AstWcsGridRenderService::setInputImage( Image::ImageInterface::SharedPtr image ) +AstWcsGridRenderService::setInputImage( Carta::Lib::Image::ImageInterface::SharedPtr image ) { CARTA_ASSERT( image ); diff --git a/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.h b/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.h index 658fba52..e0c79b11 100644 --- a/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.h +++ b/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.h @@ -29,7 +29,7 @@ class AstWcsGridRenderService : public Carta::Lib::IWcsGridRenderService ~AstWcsGridRenderService(); virtual void - setInputImage( Image::ImageInterface::SharedPtr image ) override; + setInputImage( Carta::Lib::Image::ImageInterface::SharedPtr image ) override; virtual void setOutputSize( const QSize & size ) override; @@ -92,7 +92,7 @@ private slots: Carta::Lib::VectorGraphics::VGComposer m_vgc; // VGList m_vgList; - Image::ImageInterface::SharedPtr m_iimage = nullptr; + Carta::Lib::Image::ImageInterface::SharedPtr m_iimage = nullptr; QRectF m_imgRect, m_outRect; QSize m_outSize = QSize( 10, 10 ); QColor m_lineColor = QColor( "yellow" ); diff --git a/carta/cpp/plugins/WcsPlotter/FitsHeaderExtractor.cpp b/carta/cpp/plugins/WcsPlotter/FitsHeaderExtractor.cpp index 6e34c794..ee44ce62 100644 --- a/carta/cpp/plugins/WcsPlotter/FitsHeaderExtractor.cpp +++ b/carta/cpp/plugins/WcsPlotter/FitsHeaderExtractor.cpp @@ -27,7 +27,7 @@ FitsHeaderExtractor::~FitsHeaderExtractor() { } void -FitsHeaderExtractor::setInput( Image::ImageInterface::SharedPtr image ) +FitsHeaderExtractor::setInput( Carta::Lib::Image::ImageInterface::SharedPtr image ) { m_cartaImage = image; } diff --git a/carta/cpp/plugins/WcsPlotter/FitsHeaderExtractor.h b/carta/cpp/plugins/WcsPlotter/FitsHeaderExtractor.h index 35a89ccb..8d904c04 100644 --- a/carta/cpp/plugins/WcsPlotter/FitsHeaderExtractor.h +++ b/carta/cpp/plugins/WcsPlotter/FitsHeaderExtractor.h @@ -20,7 +20,7 @@ class FitsHeaderExtractor /// \brief Set the input image /// \param image pointer to the image from which to extract the header void - setInput( Image::ImageInterface::SharedPtr image ); + setInput( Carta::Lib::Image::ImageInterface::SharedPtr image ); /// \brief get the header /// \return the fits header, with 'END' included as last entry @@ -38,6 +38,6 @@ class FitsHeaderExtractor QStringList tryRawFits( QString fname); QStringList tryCasaCoreFitsConverter( casa::LatticeBase * lbase); - Image::ImageInterface::SharedPtr m_cartaImage = nullptr; + Carta::Lib::Image::ImageInterface::SharedPtr m_cartaImage = nullptr; QStringList m_errors; }; diff --git a/carta/cpp/plugins/qimage/QImagePlugin.cpp b/carta/cpp/plugins/qimage/QImagePlugin.cpp index 54c503fe..72642943 100644 --- a/carta/cpp/plugins/qimage/QImagePlugin.cpp +++ b/carta/cpp/plugins/qimage/QImagePlugin.cpp @@ -15,7 +15,7 @@ class QImageII; /// our implementation of RawView class QImageRawView - : public NdArray::RawViewInterface + : public Carta::Lib::NdArray::RawViewInterface { public: @@ -89,7 +89,7 @@ class QImageRawView virtual void forEach( std::function < void (const char *) > func, Traversal traversal ) override { - if ( traversal != NdArray::RawViewInterface::Traversal::Sequential ) { + if ( traversal != Carta::Lib::NdArray::RawViewInterface::Traversal::Sequential ) { qFatal( "sorry, not implemented yet" ); } @@ -116,7 +116,7 @@ class QImageRawView return m_currPosView; } - virtual NdArray::RawViewInterface * + virtual Carta::Lib::NdArray::RawViewInterface * getView( const SliceND & sliceInfo ) override { // apply the slice to dimensions of this view @@ -342,7 +342,7 @@ class QImageCF : public }; /// we need to implement our own MetaDataInterface -class QImageMDI : public Image::MetaDataInterface +class QImageMDI : public Carta::Lib::Image::MetaDataInterface { public: @@ -352,7 +352,7 @@ class QImageMDI : public Image::MetaDataInterface m_coordinateFormatter = std::make_shared < QImageCF > (); } - virtual Image::MetaDataInterface * + virtual Carta::Lib::Image::MetaDataInterface * clone() override { qFatal( "not implemented" ); @@ -400,7 +400,7 @@ class QImageMDI : public Image::MetaDataInterface /// we need to implement our own ImageInterface class QImageII - : public Image::ImageInterface + : public Carta::Lib::Image::ImageInterface , public std::enable_shared_from_this < QImageII > { public: @@ -412,17 +412,17 @@ class QImageII // std::shared_ptr < QImageII > ptr = std::make_shared < QImageII > (); // so instead we need to do this /facepalm struct CPPSUCKS : public QImageII { }; - std::shared_ptr < QImageII > ptr = std::make_shared < CPPSUCKS > (); + std::shared_ptr < QImageII > image = std::make_shared < CPPSUCKS > (); - if ( ptr-> init( fname ) ) { - return ptr; + if ( image-> init( fname ) ) { + return image; } else { return nullptr; } } // load - virtual const Unit & + virtual const Carta::Lib::Unit & getPixelUnit() const override { return m_unit; @@ -446,40 +446,40 @@ class QImageII return false; } - virtual Image::PixelType + virtual Carta::Lib::Image::PixelType pixelType() const override { - return Image::PixelType::Real32; + return Carta::Lib::Image::PixelType::Real32; } - virtual Image::PixelType + virtual Carta::Lib::Image::PixelType errorType() const override { - return Image::PixelType::Real32; + return Carta::Lib::Image::PixelType::Real32; } - virtual NdArray::RawViewInterface * + virtual Carta::Lib::NdArray::RawViewInterface * getDataSlice( const SliceND & sliceInfo ) override { // return new QImageRawView( shared_from_this(), sliceInfo ); return new QImageRawView( m_data, m_dims, sliceInfo ); } - virtual NdArray::Byte * + virtual Carta::Lib::NdArray::Byte * getMaskSlice( const SliceND & sliceInfo ) override { Q_UNUSED( sliceInfo ); return nullptr; } - virtual NdArray::RawViewInterface * + virtual Carta::Lib::NdArray::RawViewInterface * getErrorSlice( const SliceND & sliceInfo ) override { Q_UNUSED( sliceInfo ); return nullptr; } - virtual Image::MetaDataInterface::SharedPtr + virtual Carta::Lib::Image::MetaDataInterface::SharedPtr metaData() override { return m_mdi; @@ -496,7 +496,7 @@ class QImageII m_dims.resize( 2, 0 ); // prepare unit - m_unit = Unit( "n/a" ); + m_unit = Carta::Lib::Unit( "n/a" ); } // do not call this directly @@ -536,9 +536,9 @@ class QImageII private: // data - Unit m_unit; + Carta::Lib::Unit m_unit; VI m_dims; - Image::MetaDataInterface::SharedPtr m_mdi = nullptr; + Carta::Lib::Image::MetaDataInterface::SharedPtr m_mdi = nullptr; bool m_valid = false; // here we'll store the actual gray scale data diff --git a/carta/scripts/fabfile2.py b/carta/scripts/fabfile2.py index 5a0d5b54..31bcca39 100644 --- a/carta/scripts/fabfile2.py +++ b/carta/scripts/fabfile2.py @@ -17,7 +17,7 @@ env.hosts = ['almatest.cyberska.org'] pureWebRoot = "PureWeb/4.1.1" remoteDeployDir = "PavolDeploy2" -localSourceDir = "/home/pfederl/Work/ALMAvis" +localSourceDir = "/home/pfederl/Work/CARTAvis" if localSourceDir.endswith("/"): abort("Please don't terminate localSourceDir with a slash") From bf063b00c88bcaf7614282014cb68e43861f0eb5 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Mon, 24 Aug 2015 21:38:14 -0600 Subject: [PATCH 03/37] initial work on cyberska integration plugin --- carta/carta.pro | 11 -- .../cpp/CartaLib/Algorithms/LineCombiner.cpp | 2 +- carta/cpp/CartaLib/CartaLib.h | 6 +- carta/cpp/CartaLib/CartaLib.pro | 3 +- carta/cpp/CartaLib/Hooks/GetInitialFileList.h | 52 +++++ carta/cpp/CartaLib/Hooks/HookIDs.h | 1 + carta/cpp/CartaLib/IPlugin.h | 2 + carta/cpp/common.pri | 18 +- .../Algorithms/RawView2QImageConverter.cpp | 187 ------------------ .../core/Algorithms/RawView2QImageConverter.h | 125 ------------ .../cpp/core/Algorithms/quantileAlgorithms.h | 1 + carta/cpp/core/IPlatform.h | 23 ++- carta/cpp/core/ImageRenderService.cpp | 1 + carta/cpp/core/MainConfig.cpp | 6 + carta/cpp/core/MainConfig.h | 5 + carta/cpp/core/MyQApp.cpp | 66 +++++-- carta/cpp/core/MyQApp.h | 18 +- carta/cpp/core/PluginManager.cpp | 11 +- carta/cpp/core/VGView.cpp | 1 + carta/cpp/core/core.pro | 8 +- carta/cpp/core/coreMain.cpp | 2 + carta/cpp/core/coreMain.h | 153 ++++++++++++++ carta/cpp/desktop/DesktopConnector.h | 4 - carta/cpp/desktop/desktopMain.cpp | 14 ++ .../CasaImageLoader/CCCoordinateFormatter.cpp | 26 +-- carta/cpp/plugins/CyberSKA/CyberSKA.pro | 31 +++ carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp | 146 ++++++++++++++ carta/cpp/plugins/CyberSKA/CyberSKAplugin.h | 35 ++++ carta/cpp/plugins/CyberSKA/plugin.json | 9 + carta/cpp/plugins/plugins.pro | 2 + carta/cpp/plugins/qimage/QImagePlugin.h | 6 +- carta/cpp/plugins/qimage/qimage.pro | 21 -- carta/cpp/server/ServerConnector.cpp | 70 ++++--- carta/cpp/server/ServerConnector.h | 15 +- carta/cpp/server/ServerPlatform.cpp | 7 +- carta/cpp/server/ServerPlatform.h | 3 +- carta/cpp/server/serverMain.cpp | 87 +++++--- 37 files changed, 682 insertions(+), 496 deletions(-) create mode 100644 carta/cpp/CartaLib/Hooks/GetInitialFileList.h delete mode 100644 carta/cpp/core/Algorithms/RawView2QImageConverter.cpp delete mode 100644 carta/cpp/core/Algorithms/RawView2QImageConverter.h create mode 100644 carta/cpp/core/coreMain.cpp create mode 100644 carta/cpp/core/coreMain.h create mode 100644 carta/cpp/plugins/CyberSKA/CyberSKA.pro create mode 100644 carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp create mode 100644 carta/cpp/plugins/CyberSKA/CyberSKAplugin.h create mode 100644 carta/cpp/plugins/CyberSKA/plugin.json diff --git a/carta/carta.pro b/carta/carta.pro index 50d1e644..d1b7fc05 100644 --- a/carta/carta.pro +++ b/carta/carta.pro @@ -34,17 +34,6 @@ CONFIG(release,debug|release) { error("Cannot include cpp/common.pri file") } -#isEmpty( CARTA_BUILD_TYPE) { -# warning( "You did not specify CARTA_BUILD_TYPE to qmake...") -# warning( "supported builds are: release dev bughunter") -# warning( "please rerun qmake with an argument 'CARTA_BUILD_TYPE=dev'") -#} - -#dbg( "root PWD=$$PWD") -#dbg( "root IN_PWD=$$IN_PWD") -#dbg( "root _PRO_FILE_PWD_=$$_PRO_FILE_PWD_") -#dbg( "root OUT_PWD=$$OUT_PWD") - dbg( "===----------Adjusted compiler settings---------====") dbg( "QMAKE_CXX $$QMAKE_CXX") dbg( "QMAKE_CXXFLAGS_DEBUG $$QMAKE_CXXFLAGS_DEBUG") diff --git a/carta/cpp/CartaLib/Algorithms/LineCombiner.cpp b/carta/cpp/CartaLib/Algorithms/LineCombiner.cpp index a02b3815..ea1bf913 100644 --- a/carta/cpp/CartaLib/Algorithms/LineCombiner.cpp +++ b/carta/cpp/CartaLib/Algorithms/LineCombiner.cpp @@ -181,7 +181,7 @@ LineCombiner::add( QPointF p1, QPointF p2 ) CARTA_ASSERT( ok ); ok = findCell( ip2c.poly->back() )-> pts.removeAll( { ip2c.poly, true } ); - CARTA_ASSERT( ok ); + CARTA_ASSERT( ok ); Q_UNUSED(ok); // we need to handle 4 cases for merging... in any case, we'll be re-using poly1 and // appending/prepending to it all elements from poly2 diff --git a/carta/cpp/CartaLib/CartaLib.h b/carta/cpp/CartaLib/CartaLib.h index 77e57dfe..bf9a2420 100644 --- a/carta/cpp/CartaLib/CartaLib.h +++ b/carta/cpp/CartaLib/CartaLib.h @@ -12,10 +12,6 @@ #include #include -#ifndef CARTA_RUNTIME_CHECKS -#define CARTA_RUNTIME_CHECKS 0 -#endif - /// all carta code lives here (or will eventually) namespace Carta { @@ -128,7 +124,7 @@ clamp( const T & v, const T & v1, const T & v2 ) /// assert macro that is compiled out in release, with additional message /// \note the CARTA_RUNTIME_CHECKS is defined in common.pri, depending on the type of build -#ifdef CARTA_RUNTIME_CHECKS +#if CARTA_RUNTIME_CHECKS > 0 #define CARTA_ASSERT_X( cond, msg ) CARTA_ASSERT_ALWAYS_X( cond, msg ) #else #define CARTA_ASSERT_X( cond, msg ) qt_noop(); diff --git a/carta/cpp/CartaLib/CartaLib.pro b/carta/cpp/CartaLib/CartaLib.pro index 6e3d890b..c4384e6f 100755 --- a/carta/cpp/CartaLib/CartaLib.pro +++ b/carta/cpp/CartaLib/CartaLib.pro @@ -62,7 +62,8 @@ HEADERS += \ IWcsGridRenderService.h \ IContourGeneratorService.h \ ContourSet.h \ - Algorithms/LineCombiner.h + Algorithms/LineCombiner.h \ + Hooks/GetInitialFileList.h unix { target.path = /usr/lib diff --git a/carta/cpp/CartaLib/Hooks/GetInitialFileList.h b/carta/cpp/CartaLib/Hooks/GetInitialFileList.h new file mode 100644 index 00000000..de3913c7 --- /dev/null +++ b/carta/cpp/CartaLib/Hooks/GetInitialFileList.h @@ -0,0 +1,52 @@ +/** + * Defines a hook for obtaining an initial file list. + * + **/ + +#pragma once + +#include "CartaLib/CartaLib.h" +#include "CartaLib/IPlugin.h" +#include + +namespace Carta +{ +namespace Lib +{ +namespace Hooks +{ +/// \brief Hook for loading a plugin of an unknown type +/// +class GetInitialFileList : public BaseHook +{ + CARTA_HOOK_BOILER1( GetInitialFileList ); + +public: + + /// result of the hook is a list of filenames + typedef QStringList ResultType; + + /// input parameters are: + /// map of all url parameters + struct Params { + Params( const std::map< QString, QString> & p_urlParams ) + { + urlParams = p_urlParams; + } + + std::map< QString, QString> urlParams; + }; + + /// standard constructor (could be probably a macro) + GetInitialFileList( Params * pptr ) : BaseHook( staticId ), paramsPtr( pptr ) + { + // force instantiation of templates + CARTA_ASSERT( is < Me > () ); + } + + ResultType result; + Params * paramsPtr = nullptr; +}; +} +} +} diff --git a/carta/cpp/CartaLib/Hooks/HookIDs.h b/carta/cpp/CartaLib/Hooks/HookIDs.h index 668a9f23..aedb40f5 100644 --- a/carta/cpp/CartaLib/Hooks/HookIDs.h +++ b/carta/cpp/CartaLib/Hooks/HookIDs.h @@ -23,6 +23,7 @@ enum class UniqueHookIDs { ColormapsScalarHook_ID, LoadPlugin_ID, GetWcsGridRendererHook_ID, + GetInitialFileList_ID, /// experimental, soon to be removed: PreRender_ID, diff --git a/carta/cpp/CartaLib/IPlugin.h b/carta/cpp/CartaLib/IPlugin.h index d2422c33..8665e6dd 100644 --- a/carta/cpp/CartaLib/IPlugin.h +++ b/carta/cpp/CartaLib/IPlugin.h @@ -7,6 +7,7 @@ #include #include #include +#include #include /// Every hook as a unique ID and we are using 64bit integers for hook IDs @@ -98,6 +99,7 @@ class IPlugin QString pluginPath; /// parsed json + QJsonObject json; }; /// called immediately after the plugin was loaded diff --git a/carta/cpp/common.pri b/carta/cpp/common.pri index 7b4d3ee5..0ba2f034 100644 --- a/carta/cpp/common.pri +++ b/carta/cpp/common.pri @@ -28,7 +28,7 @@ else { CARTA_CONFIG = dbgout runtimeChecks } -#message("CARTA_CONFIG is now: $$CARTA_CONFIG") +message("CARTA_CONFIG is now: $$CARTA_CONFIG") #remove known optimizations and debugger flags QMAKE_CXXFLAGS -= -O -O0 -O1 -O2 -O3 -g @@ -47,20 +47,22 @@ contains( CARTA_CONFIG, gdb) { message( "- NO debugger support") } contains( CARTA_CONFIG, dbgout) { - QMAKE_CXXFLAGS += -DCARTA_DEBUG_OUTPUT - QMAKE_CFLAGS += -DCARTA_DEBUG_OUTPUT - QMAKE_LFLAGS += -DCARTA_DEBUG_OUTPUT message( "+ extra debug output") } else { - QMAKE_CXXFLAGS += -DQT_NO_DEBUG_OUTPUT - QMAKE_CFLAGS += -DQT_NO_DEBUG_OUTPUT + QMAKE_CXXFLAGS += -DQT_NO_DEBUG_OUTPUT -DQT_NO_WARNING_OUTPUT + QMAKE_CFLAGS += -DQT_NO_DEBUG_OUTPUT -DQT_NO_WARNING_OUTPUT + CONFIG -= debug message( "- NO extra debug output") } contains( CARTA_CONFIG, runtimeChecks) { - QMAKE_CXXFLAGS += -DCARTA_RUNTIME_CHECKS - QMAKE_CFLAGS += -DCARTA_RUNTIME_CHECKS + QMAKE_CXXFLAGS += -DCARTA_RUNTIME_CHECKS=1 + QMAKE_CFLAGS += -DCARTA_RUNTIME_CHECKS=1 + CONFIG += debug message( "+ extra runtime checks") } else { + QMAKE_CXXFLAGS += -DCARTA_RUNTIME_CHECKS=0 + QMAKE_CFLAGS += -DCARTA_RUNTIME_CHECKS=0 + CONFIG -= debug message( "- NO extra runtime checks") } contains( CARTA_CONFIG, noOpt) { diff --git a/carta/cpp/core/Algorithms/RawView2QImageConverter.cpp b/carta/cpp/core/Algorithms/RawView2QImageConverter.cpp deleted file mode 100644 index f2aff9fb..00000000 --- a/carta/cpp/core/Algorithms/RawView2QImageConverter.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/// \todo This is getting way too object oriented. We should probably refactor this into - -/// pure template implementation to regain some speed via vectorization, at least for -/// the cached pixel transfer function case. - -#include "CartaLib/PixelPipeline/IPixelPipeline.h" -#include "CartaLib/CartaLib.h" -#include "GrayColormap.h" -#include "RawView2QImageConverter.h" - -#include -#include -#include - -template < typename Scalar > -static -typename std::tuple < Scalar, Scalar > -computeClips( - Carta::Lib::NdArray::TypedView < Scalar > & view, - double perc - ) -{ - qDebug() << "perc=" << perc; - perc = (1 - perc) / 2; - std::vector res = Carta::Core::Algorithms::quantiles2pixels( - view, { perc, 1 - perc}); - return std::make_tuple( res[0], res[1]); -} - - -/// algorithm for converting an instance of image interface to qimage -/// using the pixel pipeline -/// -/// \tparam Pipeline -/// \param m_rawView -/// \param pipe -/// \param m_qImage -template < class Pipeline > -static void -rawView2QImage( Carta::Lib::NdArray::RawViewInterface * rawView, Pipeline & pipe, QImage & qImage ) -{ - qDebug() << "rv2qi" << rawView-> dims(); - typedef double Scalar; - QSize size( rawView->dims()[0], rawView->dims()[1] ); - - if ( qImage.format() != QImage::Format_ARGB32_Premultiplied || - qImage.size() != size ) { - qImage = QImage( size, QImage::Format_ARGB32_Premultiplied ); - } - - // construct image - auto bytesPerLine = qImage.bytesPerLine(); - CARTA_ASSERT( bytesPerLine == size.width() * 4 ); - - // start with a pointer to the beginning of last row (we are constructing image - // bottom-up) - QRgb * outPtr = reinterpret_cast < QRgb * > ( - qImage.bits() + size.width() * ( size.height() - 1 ) * 4 ); - - // make a double view - Carta::Lib::NdArray::TypedView < Scalar > typedView( rawView, false ); - - /// @todo for more efficiency we should switch to the higher performance view apis - /// and apply some basic openmp/cilk - int64_t counter = 0; - QRgb nanColor = qRgb( 255, 0, 0); - auto lambda = [&] ( const Scalar & ival ) - { - if( ! std::isnan( ival)) { - pipe.convertq( ival, * outPtr ); - } - else { - * outPtr = nanColor; - } - outPtr++; - counter++; - - // build the image bottom-up - if ( counter % size.width() == 0 ) { - outPtr -= size.width() * 2; - } - }; - typedView.forEach( lambda ); -} // rawView2QImage - - -namespace Carta -{ -namespace Core -{ -RawView2QImageConverter3::RawView2QImageConverter3() -{ - m_customPipeline.reset( new Lib::PixelPipeline::CustomizablePixelPipeline ); -} - -RawView2QImageConverter3 & RawView2QImageConverter3::setInvert(bool flag) -{ - CARTA_ASSERT( m_customPipeline ); - m_customPipeline->setInvert( flag ); - return * this; -} - -RawView2QImageConverter3 & RawView2QImageConverter3::setReverse(bool flag) -{ - CARTA_ASSERT( m_customPipeline ); - m_customPipeline->setReverse( flag ); - return * this; -} - -QString -RawView2QImageConverter3::getCmapPreview( int n ) -{ - QStringList list; - for ( int i = 0 ; i <= n ; i++ ) { - double x = ( m_clipMax - m_clipMin ) / n * i + m_clipMin; - QRgb col; - m_customPipeline-> convertq( x, col ); - list << QColor( col ).name().mid( 1 ); - } - return list.join( "," ); -} - -/// @todo some optimization would be useful here, i.e. no need to recompute the cached -/// pipeline every single time... only when related parameters are changed, but this would -/// require some interesting refactoring to not loose the template speedup, without -/// affecting memory consumption -void -RawView2QImageConverter3::convert( QImage & img ) -{ - CARTA_ASSERT( m_typedView ); - CARTA_ASSERT( m_customPipeline ); - - if ( m_cmapCachingEnabled ) { - if ( m_cmapCachingInterpolated ) { - Lib::PixelPipeline::CachedPipeline < true > cachedPixPipe; - cachedPixPipe.cache( * m_customPipeline, m_cmapCacheSize, m_clipMin, m_clipMax ); - ::rawView2QImage( m_typedView-> rawView(), cachedPixPipe, img ); - } - else { - Lib::PixelPipeline::CachedPipeline < false > cachedPixPipe; - cachedPixPipe.cache( * m_customPipeline, m_cmapCacheSize, m_clipMin, m_clipMax ); - ::rawView2QImage( m_typedView-> rawView(), cachedPixPipe, img ); - } - } - else { - ::rawView2QImage( m_typedView-> rawView(), * m_customPipeline, img ); - } -} // convert - -RawView2QImageConverter3 & -RawView2QImageConverter3::setColormap( Lib::PixelPipeline::IColormapNamed::SharedPtr colormap ) -{ - CARTA_ASSERT( m_customPipeline ); - CARTA_ASSERT( colormap ); - m_customPipeline->setColormap( colormap ); - return * this; -} - -RawView2QImageConverter3 & -RawView2QImageConverter3::setView( Carta::Lib::NdArray::RawViewInterface * rawView, QString id ) -{ - Q_UNUSED( id ); - m_typedView.reset( new Carta::Lib::NdArray::TypedView < double > ( rawView, false ) ); - return * this; -} - -RawView2QImageConverter3 & -RawView2QImageConverter3::computeClips( double clip ) -{ - CARTA_ASSERT( m_typedView ); - double min, max; - std::tie( min, max ) = ::computeClips < double > ( * m_typedView, clip ); - setClips( min, max ); - return * this; -} - -RawView2QImageConverter3 & -RawView2QImageConverter3::setClips( double min, double max ) -{ - CARTA_ASSERT( m_customPipeline ); - m_clipMin = min; - m_clipMax = max; - m_customPipeline-> setMinMax( min, max ); - return * this; -} -} -} diff --git a/carta/cpp/core/Algorithms/RawView2QImageConverter.h b/carta/cpp/core/Algorithms/RawView2QImageConverter.h deleted file mode 100644 index b382127b..00000000 --- a/carta/cpp/core/Algorithms/RawView2QImageConverter.h +++ /dev/null @@ -1,125 +0,0 @@ -/*** - * Coordinates selections on one or more data controllers. - * - */ - -#pragma once - -#include "CartaLib/IImage.h" -#include "CartaLib/PixelPipeline/CustomizablePixelPipeline.h" -#include "quantileAlgorithms.h" -#include -#include - -/// controlling raw data to image conversion: -/// - invert on/off -/// - reverse on/off -/// - change colormap IColormapScalar -/// - log scaling/gamma-correction/casa-cycles enum -/// - manual clip setting -/// - percentage based clip computation -/// - pipeline caching (on/off/size/interpolation) -/// - image caching (on/off/size) -/// - user defined stages -/// - stages implementable through plugins -/// -/// \warning deprecated in favor of ImageRenderService - -namespace Carta -{ -namespace Core -{ -class RawView2QImageConverter3 -{ - CLASS_BOILERPLATE( RawView2QImageConverter3 ); - -public: - - RawView2QImageConverter3(); - - /// what size cache should we use (in bytes) for image caching - Me & - setImageCacheSize( int64_t size ); - - /// how many entries to use for pipeline caching - Me & - setPixelPipelineCacheSize( int64_t size ) { - if( size < 2) size = 2; - m_cmapCacheSize = size; - return * this; - } - - /// whether to use linear interpolation for pixel pipeline cache - Me & - setPixelPipelineInterpolation( bool flag ) { - m_cmapCachingInterpolated = flag; - return * this; - } - - /// whether to use pipeline cache at all - Me & - setPixelPipelineCacheEnabled( bool flag ) { - m_cmapCachingEnabled = flag; - return * this; - } - - /// Sets the view on which we'll operate - /// @param rawView pointer to the raw view, caller retains ownership but read - /// the warning below - /// @param id string unique for the view, as it will be used by caching - /// it could be something like "/home/john/file.fits//0:20:10,::-1,1:20:2" - /// \warning the pointer is kept for invocations of convert() and computeClips(). - /// Make sure it's valid - /// during a call to go()! - Me & - setView( Carta::Lib::NdArray::RawViewInterface * rawView, QString id = "" ); - - /// compute clips using the current view (set by setView()) - /// @param clip for example 0.95 means 95% - Me & - computeClips( double clip ); - - /// manual setting of clips - Me & - setClips( double min, double max ); - - Me & - setInvert( bool flag ) - ; - - Me & - setReverse( bool flag ) - ; - - Me & - setColormap( Lib::PixelPipeline::IColormapNamed::SharedPtr colormap ) - ; - - QString getCmapPreview( int n = 10); - - /// do the actual conversion with the previously set parameters - void convert(QImage & img); - -private: - /// pointer to the view -// NdArray::RawViewInterface * m_rawView = nullptr; - Carta::Lib::NdArray::TypedView::UniquePtr m_typedView; - /// cache for images - QCache < QString, QImage > m_imageCache; - /// unoptimized pipeline - Lib::PixelPipeline::CustomizablePixelPipeline::UniquePtr m_customPipeline; - /// optimized pipeline -// Lib::PixelPipeline::CachedPipeline::UniquePtr m_cachedPipeline; - - /// do pixel caching or not - bool m_cmapCachingEnabled = true; - /// do pixel caching using interpolation or not - bool m_cmapCachingInterpolated = false; - /// requested cache size for pixel pipepline caching - int m_cmapCacheSize = 1000; - - double m_clipMin = 0.0, m_clipMax = 1.0; - -}; -} -} diff --git a/carta/cpp/core/Algorithms/quantileAlgorithms.h b/carta/cpp/core/Algorithms/quantileAlgorithms.h index d5867b46..55f7403f 100644 --- a/carta/cpp/core/Algorithms/quantileAlgorithms.h +++ b/carta/cpp/core/Algorithms/quantileAlgorithms.h @@ -47,6 +47,7 @@ quantiles2pixels( if ( CARTA_RUNTIME_CHECKS ) { for ( auto q : quant ) { CARTA_ASSERT( 0.0 <= q && q <= 1.0 ); + Q_UNUSED(q); } } diff --git a/carta/cpp/core/IPlatform.h b/carta/cpp/core/IPlatform.h index e345c1db..fee3947b 100644 --- a/carta/cpp/core/IPlatform.h +++ b/carta/cpp/core/IPlatform.h @@ -9,29 +9,32 @@ class QString; * differently on desktop vs server side vs scripted viewer. */ -class IPlatform { - +class IPlatform +{ public: /// \brief returns a connector specific to this platform. /// \return connector specific to this platform - virtual IConnector * connector() = 0; + virtual IConnector * + connector() = 0; /// \brief return an initial list of files to load /// On desktop this will probably come from command line, on server /// this will probably come somehow via url encoded arguments... - virtual const QStringList & initialFileList() = 0; + virtual const QStringList & + initialFileList() = 0; /// Returns whether the code is running in an environment where security /// needs to be restricted, for example, access to the the file system. - virtual bool isSecurityRestricted() const = 0; - + virtual bool + isSecurityRestricted() const = 0; - /// returns CARTA data directory this is where snapshots and images are stored + /// returns CARTA data directory this is where snapshots and images are stored /// On desktop this will be $HOME/CARTA on server /scratch - virtual QString getCARTADirectory() = 0; + virtual QString + getCARTADirectory() = 0; /// empty virtual destructor - virtual ~IPlatform() {;} - + virtual + ~IPlatform() {; } }; diff --git a/carta/cpp/core/ImageRenderService.cpp b/carta/cpp/core/ImageRenderService.cpp index bc66c12a..897ac415 100644 --- a/carta/cpp/core/ImageRenderService.cpp +++ b/carta/cpp/core/ImageRenderService.cpp @@ -46,6 +46,7 @@ iView2qImage( NdArray::RawViewInterface * rawView, Pipeline & pipe, QImage & qIm } auto bytesPerLine = qImage.bytesPerLine(); CARTA_ASSERT( bytesPerLine == size.width() * 4 ); + Q_UNUSED( bytesPerLine); // start with a pointer to the beginning of last row (we are constructing image // bottom-up) diff --git a/carta/cpp/core/MainConfig.cpp b/carta/cpp/core/MainConfig.cpp index 2a97a47f..5e69ad4c 100644 --- a/carta/cpp/core/MainConfig.cpp +++ b/carta/cpp/core/MainConfig.cpp @@ -41,6 +41,7 @@ ParsedInfo parse(const QString & filePath) return info; } QJsonObject json = jsonDoc.object(); + info.m_json = json; // extract plugin directories auto pluginDirs = json[ "pluginDirs"].toArray().toVariantList(); @@ -84,6 +85,11 @@ bool ParsedInfo::isDeveloperLayout() const { return m_developerLayout; } +const QJsonObject &ParsedInfo::json() const +{ + return m_json; +} + } // namespace MainConfig diff --git a/carta/cpp/core/MainConfig.h b/carta/cpp/core/MainConfig.h index 8d8287a1..520c684f 100644 --- a/carta/cpp/core/MainConfig.h +++ b/carta/cpp/core/MainConfig.h @@ -5,6 +5,7 @@ #pragma once +#include #include class QString; @@ -31,11 +32,15 @@ class ParsedInfo { */ bool isDeveloperLayout() const; + /// the whole config file as json + const QJsonObject & json() const; + protected: QStringList m_pluginDirectories; bool m_hacksEnabled = false; bool m_developerLayout = false; + QJsonObject m_json; friend ParsedInfo parse( const QString & filePath); }; diff --git a/carta/cpp/core/MyQApp.cpp b/carta/cpp/core/MyQApp.cpp index 00891a19..2f29b361 100644 --- a/carta/cpp/core/MyQApp.cpp +++ b/carta/cpp/core/MyQApp.cpp @@ -9,33 +9,67 @@ #include #include -MyQApp::MyQApp(int & argc, char ** argv) : - QApplication( argc, argv) +MyQApp::MyQApp( int & argc, char * * argv ) : + QApplication( argc, argv ) { - setApplicationName( "carta"); - setApplicationVersion( "0.0.1"); + setApplicationName( "carta" ); + setApplicationVersion( "0.0.1" ); } +#if CARTA_RUNTIME_CHECKS > 0 + +bool +MyQApp::notify( QObject * obj, QEvent * ev ) +{ + QStringList err; + try { + return QApplication::notify( obj, ev ); + } + catch ( std::exception & e ) { + err << QString( "Exception (std::exception): %1" ).arg( e.what() ); + } + catch ( const char * str ) { + err << QString( "Exception (char *): %1" ).arg( str ); + } + catch ( const std::string & str ) { + err << QString( "Exception (std::string): %1" ).arg( str.c_str() ); + } + catch ( const QString & str ) { + err << QString( "Exception (QString): %1" ).arg( str ); + } + catch ( ... ) { + err << QString( "Exception (...)" ); + } + + err << "...caught in MyQapp::nofify()"; + qFatal( "%s", err.join( '\n' ).toStdString().c_str() ); + + return false; +} // notify + +#endif // if CARTA_RUNTIME_CHECKS > 0 + typedef DeferHelper::VoidFunc VoidFunc; // we need this for signal/slot to send/receive VoidFunc arguments -Q_DECLARE_METATYPE( VoidFunc) +Q_DECLARE_METATYPE( VoidFunc ) /// /// \brief contains one helper per thread /// -static QThreadStorage< DeferHelper *> deferHelpers; +static QThreadStorage < DeferHelper * > deferHelpers; -void defer(const VoidFunc & function) +void +defer( const VoidFunc & function ) { // create a helper in this thread if not yet created - if( ! deferHelpers.hasLocalData()) { - deferHelpers.setLocalData( new DeferHelper); + if ( ! deferHelpers.hasLocalData() ) { + deferHelpers.setLocalData( new DeferHelper ); } // queue up the function execution in this same thread // (because the object was created in this same thread) - deferHelpers.localData()-> queue( function); + deferHelpers.localData()-> queue( function ); } /// @@ -43,19 +77,19 @@ void defer(const VoidFunc & function) /// via queued connection /// \param func /// -void DeferHelper::queue(const DeferHelper::VoidFunc &func) +void +DeferHelper::queue( const DeferHelper::VoidFunc & func ) { - qRegisterMetaType("VoidFunc"); - QMetaObject::invokeMethod( this, "execute", Qt::QueuedConnection, Q_ARG( VoidFunc, func)); + qRegisterMetaType < VoidFunc > ( "VoidFunc" ); + QMetaObject::invokeMethod( this, "execute", Qt::QueuedConnection, Q_ARG( VoidFunc, func ) ); } /// /// \brief executes the function \param func immediately /// \param func function to execute /// -void DeferHelper::execute(const DeferHelper::VoidFunc &func) +void +DeferHelper::execute( const DeferHelper::VoidFunc & func ) { func(); } - - diff --git a/carta/cpp/core/MyQApp.h b/carta/cpp/core/MyQApp.h index eff7beb1..354b0261 100644 --- a/carta/cpp/core/MyQApp.h +++ b/carta/cpp/core/MyQApp.h @@ -1,13 +1,11 @@ #pragma once +/// \todo move this all into carta::core namespace + #include #include #include "IConnector.h" -/// I suspect somewhere down the road we'll want to override notify() or some -/// other functionality of QApplication, so we might as well provision for it by -/// subclassing QApplication and using the subclass... -/// /// The only code that should go into this class is Qt specific stuff that needs /// access to the internals of QApplication. /// @@ -21,10 +19,14 @@ class MyQApp : public QApplication explicit MyQApp( int & argc, char * * argv ); - /** - * @brief set the platform - * @param return the platform - */ +#if CARTA_RUNTIME_CHECKS > 0 + + /// we override notify() to be able to report exceptions that are thrown + /// inside slots + virtual bool + notify( QObject *, QEvent * ) override; + +#endif protected: }; diff --git a/carta/cpp/core/PluginManager.cpp b/carta/cpp/core/PluginManager.cpp index 06029ff9..0a9fdf3c 100644 --- a/carta/cpp/core/PluginManager.cpp +++ b/carta/cpp/core/PluginManager.cpp @@ -7,6 +7,8 @@ #include "Algorithms/Graphs/TopoSort.h" #include "CartaLib/HtmlString.h" #include "CartaLib/Hooks/LoadPlugin.h" +#include "Globals.h" +#include "MainConfig.h" #include #include #include @@ -47,7 +49,7 @@ void PluginManager::loadPlugins() qDebug() << " " << ind << m_discoveredPlugins[ind].json.name; } - // build a dependency to index lookup + // assign a unique integer for each plugin found std::map< QString, int > dep2ind; for( size_t i = 0 ; i < m_discoveredPlugins.size() ; i ++ ) { PluginInfo & pInfo = m_discoveredPlugins[i]; @@ -66,11 +68,6 @@ void PluginManager::loadPlugins() if( ! pInfo.errors.empty()) { continue; } - // skip non-native plugins - // TODO: we probably don't want to skip them... -// if( pInfo.json.typeString != "c++" && pInfo.json.typeString != "lib") { -// continue; -// } // for every dependency add appropriate arrow to toposort for( QString & dep : pInfo.json.depends) { // convert dependency to index... @@ -147,6 +144,8 @@ void PluginManager::loadPlugins() qDebug() << "Calling plugin's initialize"; IPlugin::InitInfo initInfo; initInfo.pluginPath = pInfo.dirPath; + auto json = Globals::instance()-> mainConfig()-> json(); + initInfo.json = json["plugins"].toObject()[pInfo.json.name].toObject(); pInfo.rawPlugin->initialize( initInfo); // find out what hooks this plugin wants to listen to diff --git a/carta/cpp/core/VGView.cpp b/carta/cpp/core/VGView.cpp index f3643793..382fc152 100644 --- a/carta/cpp/core/VGView.cpp +++ b/carta/cpp/core/VGView.cpp @@ -43,6 +43,7 @@ void VGView::registration( IConnector * connector ) { CARTA_ASSERT( connector = m_connector ); + Q_UNUSED(connector); } const QString & diff --git a/carta/cpp/core/core.pro b/carta/cpp/core/core.pro index 1f0538bc..3ed9c074 100755 --- a/carta/cpp/core/core.pro +++ b/carta/cpp/core/core.pro @@ -18,7 +18,6 @@ HEADERS += \ PluginManager.h \ Globals.h \ Algorithms/Graphs/TopoSort.h \ - Algorithms/RawView2QImageConverter.h \ stable.h \ CmdLine.h \ MainConfig.h \ @@ -95,7 +94,8 @@ HEADERS += \ Hacks/SharedState.h \ Hacks/ContourEditorController.h \ VGView.h \ - DummyGridRenderer.h + DummyGridRenderer.h \ + coreMain.h SOURCES += \ Viewer.cpp \ @@ -151,7 +151,6 @@ SOURCES += \ Data/ViewManager.cpp \ Data/ViewPlugins.cpp \ GrayColormap.cpp \ - Algorithms/RawView2QImageConverter.cpp \ Histogram/HistogramGenerator.cpp \ Histogram/HistogramSelection.cpp \ Histogram/HistogramPlot.cpp \ @@ -175,7 +174,8 @@ SOURCES += \ Hacks/SharedState.cpp \ Hacks/ContourEditorController.cpp \ VGView.cpp \ - DummyGridRenderer.cpp + DummyGridRenderer.cpp \ + coreMain.cpp #message( "common PWD=$$PWD") diff --git a/carta/cpp/core/coreMain.cpp b/carta/cpp/core/coreMain.cpp new file mode 100644 index 00000000..80da36e7 --- /dev/null +++ b/carta/cpp/core/coreMain.cpp @@ -0,0 +1,2 @@ +#include "coreMain.h" + diff --git a/carta/cpp/core/coreMain.h b/carta/cpp/core/coreMain.h new file mode 100644 index 00000000..7e858483 --- /dev/null +++ b/carta/cpp/core/coreMain.h @@ -0,0 +1,153 @@ +#pragma once + +/* + * This is the 'main' that is shared between both server and desktop versions. + */ + +#include "core/Viewer.h" +#include "core/Hacks/HackViewer.h" +#include "core/MyQApp.h" +#include "core/CmdLine.h" +#include "core/MainConfig.h" +#include "core/Globals.h" +#include + +namespace Carta +{ +namespace Core +{ +/// +/// \brief internal main entry point for the viewer +/// \param argc standard argc +/// \param argv standard argv +/// \return standard main return (0=success) +/// +template < class Platform > +static int +coreMainCPP( QString platformString, int argc, char * * argv ) +{ + // + // initialize Qt + // + // don't require a window manager even though we're a QGuiApplication +// qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("minimal")); + + MyQApp qapp( argc, argv ); + + QString appName = "carta-" + platformString; +#ifndef QT_NO_DEBUG_OUTPUT + appName += "-verbose"; +#endif + if( CARTA_RUNTIME_CHECKS) { + appName += "-runtimeChecks"; + } + MyQApp::setApplicationName( appName ); + + qDebug() << "Starting" << qapp.applicationName() << qapp.applicationVersion(); + + // alias globals + auto & globals = * Globals::instance(); + + // parse command line arguments & environment variables + // ==================================================== + auto cmdLineInfo = CmdLine::parse( MyQApp::arguments() ); + globals.setCmdLineInfo( & cmdLineInfo ); + + // load the config file + // ==================== + QString configFilePath = cmdLineInfo.configFilePath(); + MainConfig::ParsedInfo mainConfig = MainConfig::parse( configFilePath ); + globals.setMainConfig( & mainConfig ); + qDebug() << "plugin directories:\n - " + mainConfig.pluginDirectories().join( "\n - " ); + + // initialize plugin manager + // ========================= + globals.setPluginManager( std::make_shared < PluginManager > () ); + auto pm = globals.pluginManager(); + // tell plugin manager where to find plugins + pm-> setPluginSearchPaths( globals.mainConfig()->pluginDirectories() ); + // find and load plugins + pm-> loadPlugins(); + qDebug() << "Loading plugins..."; + auto infoList = pm-> getInfoList(); + qDebug() << "List of loaded plugins: [" << infoList.size() << "]"; + for ( const auto & entry : infoList ) { + qDebug() << " path:" << entry.json.name; + } + + // initialize platform + // =================== + // platform get access to + // - command line + // - main config file + // - plugin manager + // via Globals::instance() + globals.setPlatform( new Platform() ); + + // prepare connector + // ================= + // connector is created via platform, but we put it into globals explicitely here + IConnector * connector = globals.platform()-> connector(); + if ( ! connector ) { + qFatal( "Could not initialize connector!" ); + } + globals.setConnector( connector ); + + // create the viewer + // ================= + Viewer viewer; + Hacks::HackViewer::UniquePtr hackViewer = nullptr; + if ( globals.mainConfig()-> hacksEnabled() ) { + hackViewer.reset( new Hacks::HackViewer ); + } + if ( globals.mainConfig()->isDeveloperLayout() ) { + viewer.setDeveloperView(); + } + + // prepare closure to execute when connector is initialized + IConnector::InitializeCallback initCB = [&] ( bool valid ) -> void { + if ( ! valid ) { + qFatal( "Could not initialize connector" ); + } + viewer.start(); + if ( hackViewer ) { + hackViewer-> start(); + } + }; + + // initialize connector + connector-> initialize( initCB ); + + // give QT control + int res = qapp.exec(); + + // if we get here, it means we are quitting... + qDebug() << "Exiting"; + return res; +} // mainCPP + +/// main entry point, wrapped in try/catch to report exceptions during startup +template < class Platform > +int +coreMain( QString platformString, int argc, char * * argv ) +{ + try { + return coreMainCPP( platformString, argc, argv ); + } + catch ( const char * err ) { + qCritical() << "Exception(char*):" << err; + } + catch ( const std::string & err ) { + qCritical() << "Exception(std::string &):" << err.c_str(); + } + catch ( const QString & err ) { + qCritical() << "Exception(QString &):" << err; + } + catch ( ... ) { + qCritical() << "Exception(unknown type)!"; + } + qFatal( "%s", "...caught in main()" ); + return - 1; +} // main +} +} diff --git a/carta/cpp/desktop/DesktopConnector.h b/carta/cpp/desktop/DesktopConnector.h index 4becc882..12f4cc35 100644 --- a/carta/cpp/desktop/DesktopConnector.h +++ b/carta/cpp/desktop/DesktopConnector.h @@ -10,10 +10,6 @@ #include "core/IConnector.h" #include "core/CallbackList.h" -#ifndef CARTA_RUNTIME_CHECKS -#define CARTA_RUNTIME_CHECKS 0 -#endif - class MainWindow; class IView; diff --git a/carta/cpp/desktop/desktopMain.cpp b/carta/cpp/desktop/desktopMain.cpp index 56b8ead6..a819540d 100644 --- a/carta/cpp/desktop/desktopMain.cpp +++ b/carta/cpp/desktop/desktopMain.cpp @@ -1,3 +1,15 @@ +#include "DesktopPlatform.h" +#include "core/coreMain.h" + +int +main( int argc, char * * argv ) +{ + return Carta::Core::coreMain( "desktop", argc, argv); +} + + +#ifdef DONT_COMPILE + /* * This is the desktop main */ @@ -133,3 +145,5 @@ main( int argc, char * * argv ) } return - 1; } // main + +#endif diff --git a/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.cpp b/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.cpp index e0d914f7..b3fa3b0a 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.cpp +++ b/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.cpp @@ -7,6 +7,7 @@ #include #include +#ifdef DONT_COMPILE #define CARTA_DEBUG_THIS_FILE 0 #if CARTA_DEBUG_THIS_FILE > 0 && defined ( CARTA_DEBUG_OUTPUT ) @@ -15,6 +16,7 @@ #define cartaDebug while ( false ) \ qDebug #endif +#endif /// shortcut to HtmlString typedef Carta::Lib::HtmlString HtmlString; @@ -266,7 +268,7 @@ CCCoordinateFormatter::skyCS() CCCoordinateFormatter::Me & CCCoordinateFormatter::setSkyCS( const KnownSkyCS & scs ) { - cartaDebug() << "setSkyCS" << static_cast < int > ( scs ); + qDebug() << "setSkyCS" << static_cast < int > ( scs ); // don't even try to set this to unknown if ( scs == KnownSkyCS::Unknown ) { @@ -310,7 +312,7 @@ CCCoordinateFormatter::setSkyCS( const KnownSkyCS & scs ) mdir = casa::MDirection::GALACTIC; break; default : - CARTA_ASSERT( false ); + CARTA_ASSERT_ALWAYS_X( false, "Internal error" ); break; } // switch dirCoordCopy.setReferenceConversion( mdir ); @@ -351,12 +353,12 @@ CCCoordinateFormatter::setSkyFormatting( SkyFormatting format ) void CCCoordinateFormatter::parseCasaCS() { - cartaDebug() << "CCC nAxes=" << nAxes(); + qDebug() << "CCC nAxes=" << nAxes(); for ( auto & u : m_casaCS->worldAxisUnits() ) { - cartaDebug() << "all units:" << u.c_str(); + qDebug() << "all units:" << u.c_str(); } for ( auto & u : m_casaCS->worldAxisNames() ) { - cartaDebug() << "all names:" << u.c_str(); + qDebug() << "all names:" << u.c_str(); } // default precision is 3 @@ -365,11 +367,11 @@ CCCoordinateFormatter::parseCasaCS() for ( int i = 0 ; i < nAxes() ; i++ ) { parseCasaCSi( i ); } - cartaDebug() << "Parsed axis infos:"; + qDebug() << "Parsed axis infos:"; for ( auto & ai : m_axisInfos ) { - cartaDebug() << " lp:" << ai.longLabel().plain() << "lh:" << ai.longLabel().html() - << "sp:" << ai.shortLabel().html() << "sh:" << ai.shortLabel().html() - << "u:" << ai.unit(); + qDebug() << " lp:" << ai.longLabel().plain() << "lh:" << ai.longLabel().html() + << "sp:" << ai.shortLabel().html() << "sh:" << ai.shortLabel().html() + << "u:" << ai.unit(); } // set formatting to default @@ -392,9 +394,9 @@ CCCoordinateFormatter::parseCasaCSi( int pixelAxis ) m_casaCS->findPixelAxis( coord, coord2, pixelAxis ); - cartaDebug() << pixelAxis << "-->" << coord << "," << coord2; - cartaDebug() << " " - << casa::Coordinate::typeToString( m_casaCS->coordinate( coord ).type() ).c_str(); + qDebug() << pixelAxis << "-->" << coord << "," << coord2; + qDebug() << " " + << casa::Coordinate::typeToString( m_casaCS->coordinate( coord ).type() ).c_str(); AxisInfo & aInfo = m_axisInfos[pixelAxis]; diff --git a/carta/cpp/plugins/CyberSKA/CyberSKA.pro b/carta/cpp/plugins/CyberSKA/CyberSKA.pro new file mode 100644 index 00000000..dae93200 --- /dev/null +++ b/carta/cpp/plugins/CyberSKA/CyberSKA.pro @@ -0,0 +1,31 @@ +! include(../../common.pri) { + error( "Could not find the common.pri file!" ) +} + +INCLUDEPATH += $$PROJECT_ROOT +DEPENDPATH += $$PROJECT_ROOT + +QT += core gui + +TARGET = plugin +TEMPLATE = lib +CONFIG += plugin + +SOURCES += \ + CyberSKAplugin.cpp +HEADERS += \ + CyberSKAplugin.h + +OTHER_FILES += \ + plugin.json + +# copy json to build directory +#MYFILES = $$files($${PWD}/files/*.*) +MYFILES = plugin.json +copy_files.name = copy large files +copy_files.input = MYFILES +# change datafiles to a directory you want to put the files to +copy_files.output = $${OUT_PWD}/${QMAKE_FILE_BASE}${QMAKE_FILE_EXT} +copy_files.commands = ${COPY_FILE} ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} +copy_files.CONFIG += no_link target_predeps +QMAKE_EXTRA_COMPILERS += copy_files diff --git a/carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp b/carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp new file mode 100644 index 00000000..6a8b64bd --- /dev/null +++ b/carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp @@ -0,0 +1,146 @@ +#include "CyberSKAplugin.h" +#include "CartaLib/Hooks/GetInitialFileList.h" +#include +#include +#include +#include +#include +#include +#include + +CyberSKAplugin::CyberSKAplugin( QObject * parent ) : + QObject( parent ) +{ } + +bool +CyberSKAplugin::handleHook( BaseHook & hookData ) +{ + qDebug() << "CyberSKAplugin is handling hook #" << hookData.hookId(); + if ( hookData.is < Initialize > () ) { + // we may need to initialize some qt formats here in the future... + return true; + } + else if ( hookData.is < Carta::Lib::Hooks::GetInitialFileList > () ) { + Carta::Lib::Hooks::GetInitialFileList & hook = + static_cast < Carta::Lib::Hooks::GetInitialFileList & > ( hookData ); + +// auto fname = hook.paramsPtr->fileName; + +// hook.result = QImageII::load( fname ); + +// // return true if result is not null +// return hook.result != nullptr; + + hook.result = getParamsFromVizMan( "kkk"); +// hook.result = { +// "/scratch/Images/sky.jpg" +// }; + return true; + } + + qWarning() << "CyberSKAplugin: Sorrry, dont' know how to handle this hook"; + return false; +} // handleHook + +std::vector < HookId > +CyberSKAplugin::getInitialHookList() +{ + return { + Initialize::staticId, + Carta::Lib::Hooks::GetInitialFileList::staticId + }; +} + +void +CyberSKAplugin::initialize( const IPlugin::InitInfo & info ) +{ + qDebug() << "CyberSKA plugin initialized"; + QJsonDocument doc( info.json ); + QString docs = doc.toJson(); + qDebug() << docs; + + m_urlPattern = doc.object()["vizmanUrl"].toString(); + m_timeout = doc.object().value( "timeout" ).toDouble( 5.0 ); + qDebug() << "url" << m_urlPattern; + qDebug() << "timeout" << m_timeout; +} + +QStringList +CyberSKAplugin::getParamsFromVizMan( QString sessionKey ) +{ + QStringList res; + + if ( m_urlPattern.isEmpty() ) { + qWarning( "CyberSKA plugin: vizMan.urlPattern cannot be empty in config" ); + return res; + } + + // insert session key into the pattern + QString urlString = QString( m_urlPattern ).replace( "%%", sessionKey ); + + // try to validate the url + QUrl qUrl( urlString ); + if ( ! qUrl.isValid() ) { + qWarning() << "cyberska plugin: bad url:" << qUrl.errorString(); + return res; + } + + // get the json + std::unique_ptr < QNetworkAccessManager > networkMgr( new QNetworkAccessManager() ); + std::unique_ptr < QNetworkReply > reply( + networkMgr->get( QNetworkRequest( qUrl ) ) ); + + // do this asynchronously, but with a timeout + QEventLoop loop; + QTimer::singleShot( m_timeout * 1000, & loop, SLOT( quit() ) ); + QObject::connect( reply.get(), & QNetworkReply::finished, + & loop, & QEventLoop::quit ); + + QObject::connect( reply.get(), SIGNAL( finished() ), & loop, SLOT( quit() ) ); + loop.exec(); + + // if there was an error, return invalid paramters + if ( reply->error() != QNetworkReply::NoError ) { + qWarning() << "Network error: " << reply-> errorString() << "(" << reply->error() << ")\n"; + return res; + } + + // did the reply finish? + qDebug() << "reply finished = " << reply-> isFinished() << "\n"; + qDebug() << "reply running = " << reply-> isRunning() << "\n"; + + // if the reply was not finished, it means the timeout kicked in + if ( reply->isRunning() ) { + qWarning() << QString( "Timeout - no reply within %1 sec" ).arg( m_timeout ); + return res; + } + + // read the response + QByteArray jsonRaw = reply->readAll(); + qDebug() << "Raw JSON below:\n" + << jsonRaw << "\n" + << "---------------end of json---------------\n"; + reply->deleteLater(); + + // try to parse this into JSON + QJsonParseError errors; + QJsonDocument jsonDoc = QJsonDocument::fromJson( jsonRaw, & errors ); + if ( jsonDoc.isNull() ) { + qWarning() << "Could not parse reply"; + qWarning() << "Errors below:"; + qWarning() << errors.errorString(); + return res; + } + QJsonObject json = jsonDoc.object(); + + // get the filename out of it + QString filename = json.value("filename").toString(); + if( filename.isEmpty()) { + qWarning() << "Could not get filename from reply"; + return res; + } + + res << filename; + + return res; +} // getParamsFromVizMan diff --git a/carta/cpp/plugins/CyberSKA/CyberSKAplugin.h b/carta/cpp/plugins/CyberSKA/CyberSKAplugin.h new file mode 100644 index 00000000..ffa688b4 --- /dev/null +++ b/carta/cpp/plugins/CyberSKA/CyberSKAplugin.h @@ -0,0 +1,35 @@ +/// This plugin can read image formats that Qt supports. + +#pragma once + +#include "CartaLib/IPlugin.h" +#include +#include + +class CyberSKAplugin : public QObject, public IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA( IID "org.cartaviewer.IPlugin" ) + Q_INTERFACES( IPlugin ) + +public : + + CyberSKAplugin( QObject * parent = 0 ); + + virtual bool + handleHook( BaseHook & hookData ) override; + + virtual std::vector < HookId > + getInitialHookList() override; + + virtual void + initialize( const InitInfo & ) override; + +protected: + + QStringList getParamsFromVizMan(QString sessionKey); + + + QString m_urlPattern; + double m_timeout = 5.0; +}; diff --git a/carta/cpp/plugins/CyberSKA/plugin.json b/carta/cpp/plugins/CyberSKA/plugin.json new file mode 100644 index 00000000..b1310f84 --- /dev/null +++ b/carta/cpp/plugins/CyberSKA/plugin.json @@ -0,0 +1,9 @@ +{ + "api" : "1", + "name" : "CyberSKA", + "version" : "1", + "type" : "C++", + "description": "CyberSKA integration", + "about" : "Part of carta. Written by Pavol", + "depends" : [ ] +} diff --git a/carta/cpp/plugins/plugins.pro b/carta/cpp/plugins/plugins.pro index bd97418c..8f659646 100644 --- a/carta/cpp/plugins/plugins.pro +++ b/carta/cpp/plugins/plugins.pro @@ -18,4 +18,6 @@ SUBDIRS += noisepy SUBDIRS += blurpy +SUBDIRS += CyberSKA + diff --git a/carta/cpp/plugins/qimage/QImagePlugin.h b/carta/cpp/plugins/qimage/QImagePlugin.h index dbe64bea..722c34bb 100644 --- a/carta/cpp/plugins/qimage/QImagePlugin.h +++ b/carta/cpp/plugins/qimage/QImagePlugin.h @@ -1,4 +1,4 @@ -/// This plugin can read image formats that casacore supports. +/// This plugin can read image formats that Qt supports. #pragma once @@ -18,8 +18,4 @@ class QImagePlugin : public QObject, public IPlugin virtual bool handleHook(BaseHook & hookData) override; virtual std::vector getInitialHookList() override; -private: - -// Image::ImageInterface::SharedPtr loadImage(const QString & fname); - }; diff --git a/carta/cpp/plugins/qimage/qimage.pro b/carta/cpp/plugins/qimage/qimage.pro index 1be919f4..ed4db8cf 100644 --- a/carta/cpp/plugins/qimage/qimage.pro +++ b/carta/cpp/plugins/qimage/qimage.pro @@ -2,10 +2,6 @@ error( "Could not find the common.pri file!" ) } -#! include(../../common_config.pri) { -# error( "Could not find the common_config.pri file!" ) -#} - INCLUDEPATH += $$PROJECT_ROOT DEPENDPATH += $$PROJECT_ROOT @@ -21,23 +17,6 @@ SOURCES += \ HEADERS += \ QImagePlugin.h - -casacoreLIBS += -L$${CASACOREDIR}/lib -casacoreLIBS += -lcasa_images -lcasa_coordinates -lcasa_fits -lcasa_measures -casacoreLIBS += -lcasa_lattices -lcasa_tables -lcasa_scimath -lcasa_scimath_f -lcasa_mirlib -casacoreLIBS += -lcasa_casa -llapack -lblas -lgfortran -ldl -casacoreLIBS += -L$${WCSLIBDIR}/lib -lwcs -casacoreLIBS += -L$${CFITSIODIR}/lib -lcfitsio -unix:macx { - casacoreLIBS += -L/usr/local/lib -} -LIBS += $${casacoreLIBS} - -INCLUDEPATH += $${CASACOREDIR}/include -INCLUDEPATH += $${WCSLIBDIR}/include -INCLUDEPATH += $${CFITSIODIR}/include - - OTHER_FILES += \ plugin.json diff --git a/carta/cpp/server/ServerConnector.cpp b/carta/cpp/server/ServerConnector.cpp index 0c5b3116..686b9679 100644 --- a/carta/cpp/server/ServerConnector.cpp +++ b/carta/cpp/server/ServerConnector.cpp @@ -1,28 +1,14 @@ #include "ServerConnector.h" #include "core/MyQApp.h" +#include "core/Globals.h" +#include "CartaLib/Hooks/GetInitialFileList.h" #include #include #include #include - #include -// define convenience conversion between CSI::String and QString -// accomplished by going to std::string as an intermediate step -//namespace CSI { -//template<> -//struct cast_helper -//{ -// static bool try_cast(String const& value, QString & result) -// { -// std::string stdstr = value.As(); -// result = stdstr.c_str(); -// return true; -// } -//}; -//} - ServerConnector::ServerConnector() : QObject( nullptr) { @@ -30,6 +16,7 @@ ServerConnector::ServerConnector() m_initialized = false; } +static void OnPWStateInitialized(CSI::PureWeb::Server::StateManager &, CSI::EmptyEventArgs &) { qDebug() << "State manager is now initialized"; @@ -58,17 +45,23 @@ void ServerConnector::initialize(const InitializeCallback & cb) m_server->Start(m_stateManager.get()); m_server->ShutdownRequested() += OnPureWebShutdown; + // register generic command listener + CSI::PureWeb::Server::StateManager::Instance()->CommandManager().AddUiHandler( + "generic", CSI::Bind( this, &ServerConnector::genericCommandListener)); + // extract URL encoded arguments for( auto kv : m_server-> StartupParameters()) { QString key = kv.first.ToAscii().begin(); QString val = kv.second.ToAscii().begin(); m_urlParams[ key ] = val; - //qDebug() << key << "=" << val; } - // register generic command listener - CSI::PureWeb::Server::StateManager::Instance()->CommandManager().AddUiHandler( - "generic", CSI::Bind( this, &ServerConnector::genericCommandListener)); + // make a list of initial files from this + auto & pm = * Globals::instance()->pluginManager(); + auto list = pm.prepare < Carta::Lib::Hooks::GetInitialFileList > ( m_urlParams).first(); + if( list.isSet()) { + m_initialFileList = list.val(); + } m_initialized = true; } @@ -96,17 +89,6 @@ QString ServerConnector::getStateLocation( const QString & saveName ) const { return "/tmp/"+saveName + ".json"; } -// Commenting this out because (a) it was not used, (b) it was probably not converting -// CSI::String to std::string correctly. (Pavol) -/* -QString ServerConnector::toQString( const CSI::String source) const { - std::string treeValueStr = source.As(); - QString treeValueQ = treeValueStr.c_str(); - return treeValueQ; -} -*/ - - QString ServerConnector::getState(const QString& path) { Q_ASSERT( m_initialized); @@ -175,12 +157,12 @@ IConnector::CallbackID ServerConnector::addStateCallback(IConnector::CSR path, c return m_callbackNextId++; } -const std::map & ServerConnector::urlParams() -{ - Q_ASSERT( m_initialized); +//const std::map & ServerConnector::urlParams() +//{ +// Q_ASSERT( m_initialized); - return m_urlParams; -} +// return m_urlParams; +//} void ServerConnector::pureWebValueChangedCB(const CSI::ValueChangedEventArgs &val) { @@ -329,3 +311,19 @@ void ServerConnector::removeStateCallback(const IConnector::CallbackID & /*id*/) qFatal( "Not implemented"); } +const QStringList & ServerConnector::initialFileList() +{ + return m_initialFileList; + +// m_initialFileList.clear(); +// auto it = m_urlParams.find( "file"); +// if( it != m_urlParams.end()) { +// m_initialFileList << ( * it).second; +// } + + + +// m_initialFileList = { "/scratch/Images/sky.jpg" }; +// return m_initialFileList; +} + diff --git a/carta/cpp/server/ServerConnector.h b/carta/cpp/server/ServerConnector.h index 5d168e6d..c9f4f894 100644 --- a/carta/cpp/server/ServerConnector.h +++ b/carta/cpp/server/ServerConnector.h @@ -90,10 +90,6 @@ class ServerConnector : public QObject, public IConnector /// add a state callback virtual CallbackID addStateCallback(CSR path, const StateChangedCallback &cb) Q_DECL_OVERRIDE; - /// returns a map of url encoded parameters that were used to invoke the main program - /// this only works after initialize() was called - const std::map< QString, QString> & urlParams(); - /// register view implementation virtual void registerView(IView * view) Q_DECL_OVERRIDE; @@ -106,6 +102,14 @@ class ServerConnector : public QObject, public IConnector /// remove state callback implementation virtual void removeStateCallback( const CallbackID & id) Q_DECL_OVERRIDE; + /// get the initial file list (used by server platform) + virtual const QStringList & initialFileList(); + + /// returns a map of url encoded parameters that were used to invoke the main program + /// this only works after initialize() was called +// const std::map< QString, QString> & urlParams(); + + protected: /// this gets called when pureweb server is shutting down @@ -139,6 +143,9 @@ class ServerConnector : public QObject, public IConnector // list of url encoded parameters std::map< QString, QString> m_urlParams; + // initial file list + QStringList m_initialFileList; + // whether initialize was called bool m_initialized; diff --git a/carta/cpp/server/ServerPlatform.cpp b/carta/cpp/server/ServerPlatform.cpp index ded21a17..996c5b53 100644 --- a/carta/cpp/server/ServerPlatform.cpp +++ b/carta/cpp/server/ServerPlatform.cpp @@ -50,12 +50,7 @@ IConnector * ServerPlatform::connector() const QStringList & ServerPlatform::initialFileList() { - auto params = m_connector-> urlParams(); - auto it = this-> m_connector-> urlParams().find( "file"); - if( it != this-> m_connector-> urlParams().end()) { - m_initialFileList << ( * it).second; - } - return m_initialFileList; + return m_connector-> initialFileList(); } QString ServerPlatform::getCARTADirectory() diff --git a/carta/cpp/server/ServerPlatform.h b/carta/cpp/server/ServerPlatform.h index 2eb13603..4a6bba9e 100644 --- a/carta/cpp/server/ServerPlatform.h +++ b/carta/cpp/server/ServerPlatform.h @@ -29,7 +29,6 @@ class ServerPlatform : public IPlatform protected: - ServerConnector * m_connector; - QStringList m_initialFileList; + ServerConnector * m_connector = nullptr; }; diff --git a/carta/cpp/server/serverMain.cpp b/carta/cpp/server/serverMain.cpp index 7daa3b1b..f650c8ea 100644 --- a/carta/cpp/server/serverMain.cpp +++ b/carta/cpp/server/serverMain.cpp @@ -4,6 +4,19 @@ #include "ServerPlatform.h" #include "core/Viewer.h" +#include "ServerPlatform.h" +#include "core/coreMain.h" + +int +main( int argc, char * * argv ) +{ + return Carta::Core::coreMain( "server", argc, argv); +} + + +#ifdef DONT_COMPILE + + #include "core/Hacks/HackViewer.h" #include "core/MyQApp.h" #include "core/CmdLine.h" @@ -21,7 +34,8 @@ /// /// @warning keep this in sync with serverMain until refactored to commonMain /// -int main(int argc, char ** argv) +static int +mainCPP( int argc, char * * argv ) { // // initialize Qt @@ -29,12 +43,12 @@ int main(int argc, char ** argv) // don't require a window manager even though we're a QGuiApplication // qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("minimal")); - MyQApp qapp( argc, argv); + MyQApp qapp( argc, argv ); #ifdef QT_DEBUG - MyQApp::setApplicationName( "carta-server-debug"); + MyQApp::setApplicationName( "carta-server-debug" ); #else - MyQApp::setApplicationName( "carta-server-release"); + MyQApp::setApplicationName( "carta-server-release" ); #endif qDebug() << "Starting" << qapp.applicationName() << qapp.applicationVersion(); @@ -44,37 +58,39 @@ int main(int argc, char ** argv) // parse command line arguments & environment variables // ==================================================== - auto cmdLineInfo = CmdLine::parse( MyQApp::arguments()); - globals.setCmdLineInfo( & cmdLineInfo); + auto cmdLineInfo = CmdLine::parse( MyQApp::arguments() ); + globals.setCmdLineInfo( & cmdLineInfo ); // load the config file // ==================== QString configFilePath = cmdLineInfo.configFilePath(); - auto mainConfig = MainConfig::parse( configFilePath); - globals.setMainConfig( & mainConfig); - qDebug() << "plugin directories:\n - " + mainConfig.pluginDirectories().join( "\n - "); + auto mainConfig = MainConfig::parse( configFilePath ); + globals.setMainConfig( & mainConfig ); + qDebug() << "plugin directories:\n - " + mainConfig.pluginDirectories().join( "\n - " ); // initialize platform // =================== // platform gets command line & main config file via globals auto platform = new ServerPlatform(); - globals.setPlatform( platform); + globals.setPlatform( platform ); // prepare connector // ================= // connector is created via platform, but we put it into globals explicitely here IConnector * connector = platform-> connector(); - if( ! connector) { - qFatal( "Could not initialize connector!"); + if ( ! connector ) { + qFatal( "Could not initialize connector!" ); } - globals.setConnector( connector); + globals.setConnector( connector ); // initialize plugin manager // ========================= - globals.setPluginManager( std::make_shared() ); + globals.setPluginManager( std::make_shared < PluginManager > () ); auto pm = globals.pluginManager(); + // tell plugin manager where to find plugins pm-> setPluginSearchPaths( globals.mainConfig()->pluginDirectories() ); + // find and load plugins pm-> loadPlugins(); qDebug() << "Loading plugins..."; @@ -88,27 +104,50 @@ int main(int argc, char ** argv) // ================= Viewer viewer; Hacks::HackViewer::UniquePtr hackViewer = nullptr; - if( globals.mainConfig()-> hacksEnabled()) { - hackViewer.reset( new Hacks::HackViewer); + if ( globals.mainConfig()-> hacksEnabled() ) { + hackViewer.reset( new Hacks::HackViewer ); } - if ( globals.mainConfig()->isDeveloperLayout()){ - viewer.setDeveloperView(); + if ( globals.mainConfig()->isDeveloperLayout() ) { + viewer.setDeveloperView(); } // prepare closure to execute when connector is initialized - IConnector::InitializeCallback initCB = [&](bool valid) -> void { - if( ! valid) { - qFatal( "Could not initialize connector"); + IConnector::InitializeCallback initCB = [&] ( bool valid ) -> void { + if ( ! valid ) { + qFatal( "Could not initialize connector" ); } viewer.start(); - if( hackViewer) { + if ( hackViewer ) { hackViewer-> start(); } }; // initialize connector - connector-> initialize( initCB); + connector-> initialize( initCB ); // qt now has control return qapp.exec(); -} +} // mainCPP + +int +main( int argc, char * * argv ) +{ + try { + return mainCPP( argc, argv ); + } + catch ( const char * err ) { + qCritical() << "Exception(char*):" << err; + } + catch ( const std::string & err ) { + qCritical() << "Exception(std::string &):" << err.c_str(); + } + catch ( const QString & err ) { + qCritical() << "Exception(QString &):" << err; + } + catch ( ... ) { + qCritical() << "Exception(unknown type)!"; + } + return - 1; +} // main + +#endif From 97233b16c2c051efa8c8bfc3d17ea30a1af75013 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Tue, 1 Sep 2015 23:28:39 -0600 Subject: [PATCH 04/37] re-added the ability to load files on server via FILE in url this was done by creating a DevIntegration plugin. For security reasons it needs to be enabled by adding the following to .cartavis/config.json: "plugins": { "DevIntegration" : { "enabled" : true } }, --- carta/cpp/CartaLib/CartaLib.pro | 3 +- carta/cpp/CartaLib/Hooks/ColormapsScalar.h | 2 +- carta/cpp/CartaLib/Hooks/GetWcsGridRenderer.h | 2 +- carta/cpp/CartaLib/Hooks/Histogram.h | 2 +- carta/cpp/CartaLib/Hooks/Initialize.h | 41 +++++++ carta/cpp/CartaLib/Hooks/LoadAstroImage.h | 2 +- carta/cpp/CartaLib/IPlugin.h | 67 +++--------- carta/cpp/core/Viewer.cpp | 4 +- .../CasaImageLoader/CasaImageLoader.cpp | 14 +-- carta/cpp/plugins/Colormaps1/Colormaps1.cpp | 5 +- carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp | 40 ++++--- carta/cpp/plugins/CyberSKA/plugin.json | 6 +- .../plugins/DevIntegration/DevIntegration.cpp | 70 ++++++++++++ .../plugins/DevIntegration/DevIntegration.h | 30 +++++ .../plugins/DevIntegration/DevIntegration.pro | 31 ++++++ carta/cpp/plugins/DevIntegration/plugin.json | 12 ++ carta/cpp/plugins/Histogram/Histogram1.cpp | 5 +- carta/cpp/plugins/clock1/Clock1Plugin.cpp | 3 + carta/cpp/plugins/plugins.pro | 3 +- carta/cpp/plugins/qimage/QImagePlugin.cpp | 2 + carta/cpp/plugins/tester1/GenericPlugin.cpp | 28 +---- .../skel/source/class/skel/hacks/HackView.js | 4 - carta/html5/server/serverIndex.html | 1 + carta/scripts/deploy-viz2.py | 103 ++++++++++++++++++ 24 files changed, 363 insertions(+), 117 deletions(-) create mode 100644 carta/cpp/CartaLib/Hooks/Initialize.h create mode 100644 carta/cpp/plugins/DevIntegration/DevIntegration.cpp create mode 100644 carta/cpp/plugins/DevIntegration/DevIntegration.h create mode 100644 carta/cpp/plugins/DevIntegration/DevIntegration.pro create mode 100644 carta/cpp/plugins/DevIntegration/plugin.json create mode 100755 carta/scripts/deploy-viz2.py diff --git a/carta/cpp/CartaLib/CartaLib.pro b/carta/cpp/CartaLib/CartaLib.pro index c4384e6f..e34cad81 100755 --- a/carta/cpp/CartaLib/CartaLib.pro +++ b/carta/cpp/CartaLib/CartaLib.pro @@ -63,7 +63,8 @@ HEADERS += \ IContourGeneratorService.h \ ContourSet.h \ Algorithms/LineCombiner.h \ - Hooks/GetInitialFileList.h + Hooks/GetInitialFileList.h \ + Hooks/Initialize.h unix { target.path = /usr/lib diff --git a/carta/cpp/CartaLib/Hooks/ColormapsScalar.h b/carta/cpp/CartaLib/Hooks/ColormapsScalar.h index b499e995..f18100b8 100644 --- a/carta/cpp/CartaLib/Hooks/ColormapsScalar.h +++ b/carta/cpp/CartaLib/Hooks/ColormapsScalar.h @@ -18,7 +18,7 @@ namespace Hooks { class ColormapsScalarHook : public BaseHook { - CARTA_HOOK_BOILER1( ColormapsScalarHook ) + CARTA_HOOK_BOILER1( ColormapsScalarHook ); public: diff --git a/carta/cpp/CartaLib/Hooks/GetWcsGridRenderer.h b/carta/cpp/CartaLib/Hooks/GetWcsGridRenderer.h index eb488b53..247acdcc 100644 --- a/carta/cpp/CartaLib/Hooks/GetWcsGridRenderer.h +++ b/carta/cpp/CartaLib/Hooks/GetWcsGridRenderer.h @@ -26,7 +26,7 @@ namespace Hooks class GetWcsGridRendererHook : public BaseHook { // all hooks should have this: - CARTA_HOOK_BOILER1( GetWcsGridRendererHook ) + CARTA_HOOK_BOILER1( GetWcsGridRendererHook ); public: diff --git a/carta/cpp/CartaLib/Hooks/Histogram.h b/carta/cpp/CartaLib/Hooks/Histogram.h index 4e0775da..5fb7166b 100755 --- a/carta/cpp/CartaLib/Hooks/Histogram.h +++ b/carta/cpp/CartaLib/Hooks/Histogram.h @@ -21,7 +21,7 @@ namespace Hooks { class HistogramHook : public BaseHook { - CARTA_HOOK_BOILER1( HistogramHook ) + CARTA_HOOK_BOILER1( HistogramHook ); public: diff --git a/carta/cpp/CartaLib/Hooks/Initialize.h b/carta/cpp/CartaLib/Hooks/Initialize.h new file mode 100644 index 00000000..fbfc2158 --- /dev/null +++ b/carta/cpp/CartaLib/Hooks/Initialize.h @@ -0,0 +1,41 @@ +/** + * Defines a hook that is executed once, when core is starting up. + * + **/ + +#pragma once + +#include "CartaLib/CartaLib.h" +#include "CartaLib/IPlugin.h" +#include + +namespace Carta +{ +namespace Lib +{ +namespace Hooks +{ +/// \brief Hook for loading a plugin of an unknown type +/// +class Initialize : public BaseHook +{ + CARTA_HOOK_BOILER1( Initialize ); + +public: + + typedef FakeVoid ResultType; + typedef struct { } Params; + + /// standard constructor (could be probably a macro) + Initialize( Params * pptr ) : BaseHook( staticId ), paramsPtr( pptr ) + { + // force instantiation of templates + CARTA_ASSERT( is < Me > () ); + } + + ResultType result; + Params * paramsPtr = nullptr; +}; +} +} +} diff --git a/carta/cpp/CartaLib/Hooks/LoadAstroImage.h b/carta/cpp/CartaLib/Hooks/LoadAstroImage.h index 012bae81..c6316b3c 100644 --- a/carta/cpp/CartaLib/Hooks/LoadAstroImage.h +++ b/carta/cpp/CartaLib/Hooks/LoadAstroImage.h @@ -18,7 +18,7 @@ namespace Hooks /// load an (astronomical) image and convert to an instance of Image::ImageInterface class LoadAstroImage : public BaseHook { - CARTA_HOOK_BOILER1( LoadAstroImage ) + CARTA_HOOK_BOILER1( LoadAstroImage ); public: diff --git a/carta/cpp/CartaLib/IPlugin.h b/carta/cpp/CartaLib/IPlugin.h index 8665e6dd..66e0dd23 100644 --- a/carta/cpp/CartaLib/IPlugin.h +++ b/carta/cpp/CartaLib/IPlugin.h @@ -22,6 +22,7 @@ class BaseHook // : public QObject // Q_OBJECT public: + /// TODO: I have not yet figured out how to specialize some of the templates for /// void return type... hence the FakeVoid aliased to char typedef char FakeVoid; @@ -45,7 +46,7 @@ class BaseHook // : public QObject static bool isHook( const BaseHook & hook ) { - return hook.hookId() == HookType::staticId; + return hook.hookId() == HookType::staticId; } /// returns treu if this hook is an instace of HookType @@ -56,14 +57,12 @@ class BaseHook // : public QObject return hookId() == HookType::staticId; } - protected: /// dynamic hook ID HookId m_hookId; }; - /// parsed plugin.json information struct PluginJson { /// API version against which @@ -93,6 +92,7 @@ struct PluginJson { class IPlugin { public: + /// information passed to plugins during initialize() struct InitInfo { /// full path to the directory from where the plugin was loaded @@ -103,9 +103,12 @@ class IPlugin }; /// called immediately after the plugin was loaded - /// TODO: should be pure virtual + /// TODO: should be pure virtual, don't be lazy! virtual void - initialize( const InitInfo & /*initInfo*/ ) { } + initialize( const InitInfo & initInfo ) + { + Q_UNUSED( initInfo ); + } /// at startup plugins will be asked to return a list of hook ids they are /// interested in listening to @@ -121,39 +124,28 @@ class IPlugin ~IPlugin() { } }; -#define CARTA_HOOK_BOILER1(name) \ - CLASS_BOILERPLATE(name) ;\ - enum { staticId = static_cast( Carta::Lib::Hooks::UniqueHookIDs::name ## _ID) }; - -/// initialize hook is called once at the beginning of the application -class Initialize : public BaseHook -{ - CARTA_HOOK_BOILER1(Initialize) - -public: - typedef FakeVoid ResultType; - typedef struct { } Params; - Initialize( Params * ) : BaseHook( staticId ) { } - - ResultType result; -}; +#define CARTA_HOOK_BOILER1( name ) \ + CLASS_BOILERPLATE( name ); \ + enum { staticId = static_cast < HookId > ( Carta::Lib::Hooks::UniqueHookIDs::name ## _ID ) } /// just before rendering a view, plugins are given a chance to modify the rendered image +/// \todo remove this, it was only a proof of concept hook class PreRender : public BaseHook { - CARTA_HOOK_BOILER1(PreRender) + CARTA_HOOK_BOILER1( PreRender ); public: + typedef FakeVoid ResultType; struct Params { Params( QString p_viewName, QImage * p_imgPtr ) { - imgPtr = p_imgPtr; + imgPtr = p_imgPtr; viewName = p_viewName; } QImage * imgPtr; - QString viewName; + QString viewName; }; PreRender( Params * pptr ) : BaseHook( staticId ), paramsPtr( pptr ) { } @@ -161,29 +153,6 @@ class PreRender : public BaseHook Params * paramsPtr; }; -/// load image and convert it to QImage -//class LoadImage : public BaseHook -//{ -// CARTA_HOOK_BOILER1(LoadImage) - -//public: -// typedef QImage ResultType; -// struct Params { -// Params( QString p_fileName, int p_channel ) -// { -// fileName = p_fileName; -// frame = p_channel; -// } - -// QString fileName; -// int frame; -// }; -// LoadImage( Params * pptr ) : BaseHook( staticId ), paramsPtr( pptr ) { } - -// ResultType result; -// Params * paramsPtr; -//}; - -// this is needed to setup the Qt metatype system to enable qobject_cast<> downcasting -// must be outside of any namespace +// This is needed to setup the Qt metatype system to enable qobject_cast<> downcasting. +// It must be outside of any namespace!!! Q_DECLARE_INTERFACE( IPlugin, "org.cartaviewer.IPlugin" ) diff --git a/carta/cpp/core/Viewer.cpp b/carta/cpp/core/Viewer.cpp index 915af9ba..6be2ca70 100644 --- a/carta/cpp/core/Viewer.cpp +++ b/carta/cpp/core/Viewer.cpp @@ -1,5 +1,6 @@ #include "CartaLib/CartaLib.h" #include "CartaLib/Hooks/ColormapsScalar.h" +#include "CartaLib/Hooks/Initialize.h" #include "GrayColormap.h" #include "Viewer.h" #include "Globals.h" @@ -30,6 +31,7 @@ #include + using namespace rapidjson; /// Recursively parse through a directory structure contained in a json value @@ -87,7 +89,7 @@ Viewer::start() auto & globals = * Globals::instance(); // tell all plugins that the core has initialized - globals.pluginManager()-> prepare < Initialize > ().executeAll(); + globals.pluginManager()-> prepare < Carta::Lib::Hooks::Initialize > ().executeAll(); // ask plugins to load the image qDebug() << "======== trying to load image ========"; diff --git a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp index bcc1f70e..98070c2c 100755 --- a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp +++ b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.cpp @@ -1,5 +1,6 @@ #include "CasaImageLoader.h" #include "CCImage.h" +#include "CartaLib/Hooks/Initialize.h" #include "CartaLib/Hooks/LoadAstroImage.h" #include #include @@ -17,8 +18,6 @@ #include #include -typedef Carta::Lib::Hooks::LoadAstroImage LoadAstroImage; - CasaImageLoader::CasaImageLoader(QObject *parent) : QObject(parent) { @@ -27,15 +26,16 @@ CasaImageLoader::CasaImageLoader(QObject *parent) : bool CasaImageLoader::handleHook(BaseHook & hookData) { qDebug() << "CasaImageLoader plugin is handling hook #" << hookData.hookId(); - if( hookData.is()) { + if( hookData.is()) { // Register FITS and Miriad image types casa::FITSImage::registerOpenFunction(); casa::MIRIADImage::registerOpenFunction(); return true; } - else if( hookData.is()) { - LoadAstroImage & hook = static_cast( hookData); + else if( hookData.is()) { + Carta::Lib::Hooks::LoadAstroImage & hook + = static_cast( hookData); auto fname = hook.paramsPtr->fileName; hook.result = loadImage( fname); // return true if result is not null @@ -50,8 +50,8 @@ std::vector CasaImageLoader::getInitialHookList() { // forgot_to_define_this(); return { - Initialize::staticId, - LoadAstroImage::staticId + Carta::Lib::Hooks::Initialize::staticId, + Carta::Lib::Hooks::LoadAstroImage::staticId }; } diff --git a/carta/cpp/plugins/Colormaps1/Colormaps1.cpp b/carta/cpp/plugins/Colormaps1/Colormaps1.cpp index 494d292a..954d4f09 100644 --- a/carta/cpp/plugins/Colormaps1/Colormaps1.cpp +++ b/carta/cpp/plugins/Colormaps1/Colormaps1.cpp @@ -1,9 +1,12 @@ #include "Colormaps1.h" #include "CartaLib/Hooks/ColormapsScalar.h" +#include "CartaLib/Hooks/Initialize.h" #include "CartaLib/PWLinear.h" #include #include +typedef Carta::Lib::Hooks::Initialize Initialize; + Colormap1::Colormap1( QObject * parent ) : QObject( parent ) { } @@ -11,7 +14,7 @@ Colormap1::Colormap1( QObject * parent ) : bool Colormap1::handleHook( BaseHook & hookData ) { - if ( hookData.is < Initialize > () ) { + if ( hookData.is < Carta::Lib::Hooks::Initialize > () ) { return true; } else if ( hookData.is < Carta::Lib::Hooks::ColormapsScalarHook > () ) { diff --git a/carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp b/carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp index 6a8b64bd..dd741990 100644 --- a/carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp +++ b/carta/cpp/plugins/CyberSKA/CyberSKAplugin.cpp @@ -1,5 +1,6 @@ #include "CyberSKAplugin.h" #include "CartaLib/Hooks/GetInitialFileList.h" +#include "CartaLib/Hooks/Initialize.h" #include #include #include @@ -16,26 +17,18 @@ bool CyberSKAplugin::handleHook( BaseHook & hookData ) { qDebug() << "CyberSKAplugin is handling hook #" << hookData.hookId(); - if ( hookData.is < Initialize > () ) { + if ( hookData.is < Carta::Lib::Hooks::Initialize > () ) { // we may need to initialize some qt formats here in the future... return true; } else if ( hookData.is < Carta::Lib::Hooks::GetInitialFileList > () ) { Carta::Lib::Hooks::GetInitialFileList & hook = static_cast < Carta::Lib::Hooks::GetInitialFileList & > ( hookData ); - -// auto fname = hook.paramsPtr->fileName; - -// hook.result = QImageII::load( fname ); - -// // return true if result is not null -// return hook.result != nullptr; - - hook.result = getParamsFromVizMan( "kkk"); -// hook.result = { -// "/scratch/Images/sky.jpg" -// }; - return true; + QString key = hook.paramsPtr->urlParams["key"]; + qDebug() << "key" << key; + hook.result = getParamsFromVizMan( key); + // return success if we made a list of at least one filename + return ! hook.result.isEmpty(); } qWarning() << "CyberSKAplugin: Sorrry, dont' know how to handle this hook"; @@ -46,7 +39,7 @@ std::vector < HookId > CyberSKAplugin::getInitialHookList() { return { - Initialize::staticId, + Carta::Lib::Hooks::Initialize::staticId, Carta::Lib::Hooks::GetInitialFileList::staticId }; } @@ -55,13 +48,18 @@ void CyberSKAplugin::initialize( const IPlugin::InitInfo & info ) { qDebug() << "CyberSKA plugin initialized"; - QJsonDocument doc( info.json ); - QString docs = doc.toJson(); - qDebug() << docs; +// QJsonDocument doc( info.json ); +// QString docs = doc.toJson(); +// qDebug() << docs; + +// m_urlPattern = doc.object()["vizmanUrl"].toString(); +// m_timeout = doc.object().value( "timeout" ).toDouble( 5.0 ); + + m_urlPattern = info.json.value( "vizmanUrl").toString(); + m_timeout = info.json.value( "timeout").toDouble( 5.0); + - m_urlPattern = doc.object()["vizmanUrl"].toString(); - m_timeout = doc.object().value( "timeout" ).toDouble( 5.0 ); - qDebug() << "url" << m_urlPattern; + qDebug() << "vizmanUrl" << m_urlPattern; qDebug() << "timeout" << m_timeout; } diff --git a/carta/cpp/plugins/CyberSKA/plugin.json b/carta/cpp/plugins/CyberSKA/plugin.json index b1310f84..d8a5c7b0 100644 --- a/carta/cpp/plugins/CyberSKA/plugin.json +++ b/carta/cpp/plugins/CyberSKA/plugin.json @@ -3,7 +3,11 @@ "name" : "CyberSKA", "version" : "1", "type" : "C++", - "description": "CyberSKA integration", + "description": [ + "CyberSKA integration plugin.", + "Allows location of initial filename to be specified via key in url. This key", + "is then used to request the filename securely." + ], "about" : "Part of carta. Written by Pavol", "depends" : [ ] } diff --git a/carta/cpp/plugins/DevIntegration/DevIntegration.cpp b/carta/cpp/plugins/DevIntegration/DevIntegration.cpp new file mode 100644 index 00000000..6ff955dc --- /dev/null +++ b/carta/cpp/plugins/DevIntegration/DevIntegration.cpp @@ -0,0 +1,70 @@ +#include "DevIntegration.h" +#include "CartaLib/Hooks/GetInitialFileList.h" +#include "CartaLib/Hooks/Initialize.h" +#include +#include +#include +#include +#include +#include +#include + +typedef Carta::Lib::Hooks::Initialize Initialize; + +static const char * PluginName = "DevIntegrationPlugin:"; + +DevIntegrationPlugin::DevIntegrationPlugin( QObject * parent ) : + QObject( parent ) +{ } + +bool +DevIntegrationPlugin::handleHook( BaseHook & hookData ) +{ + qDebug() << PluginName << "handling hook #" << hookData.hookId(); + if ( hookData.is < Initialize > () ) { + // we may need to initialize some qt formats here in the future... + return true; + } + else if ( hookData.is < Carta::Lib::Hooks::GetInitialFileList > () ) { + if ( ! m_enabled ) { + return false; + } + Carta::Lib::Hooks::GetInitialFileList & hook = + static_cast < Carta::Lib::Hooks::GetInitialFileList & > ( hookData ); + QString file = hook.paramsPtr->urlParams["file"]; + qDebug() << PluginName << "file" << file; + if ( file.isEmpty() ) { + hook.result.clear(); + } + else { + hook.result = { + file + }; + } + + // return success if we made a list of at least one filename + return ! hook.result.isEmpty(); + } + + qWarning() << PluginName << "Sorrry, dont' know how to handle this hook"; + return false; +} // handleHook + +std::vector < HookId > +DevIntegrationPlugin::getInitialHookList() +{ + return { + Initialize::staticId, + Carta::Lib::Hooks::GetInitialFileList::staticId + }; +} + +void +DevIntegrationPlugin::initialize( const IPlugin::InitInfo & info ) +{ + qDebug() << PluginName << "initialized"; + + m_enabled = info.json.value( "enabled" ).toBool( false ); + + qDebug() << PluginName << "enabled=" << m_enabled; +} diff --git a/carta/cpp/plugins/DevIntegration/DevIntegration.h b/carta/cpp/plugins/DevIntegration/DevIntegration.h new file mode 100644 index 00000000..0721a46d --- /dev/null +++ b/carta/cpp/plugins/DevIntegration/DevIntegration.h @@ -0,0 +1,30 @@ +/// This plugin can read image formats that Qt supports. + +#pragma once + +#include "CartaLib/IPlugin.h" +#include +#include + +class DevIntegrationPlugin : public QObject, public IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA( IID "org.cartaviewer.IPlugin" ) + Q_INTERFACES( IPlugin ) + +public : + DevIntegrationPlugin( QObject * parent = 0 ); + + virtual bool + handleHook( BaseHook & hookData ) override; + + virtual std::vector < HookId > + getInitialHookList() override; + + virtual void + initialize( const InitInfo & ) override; + +protected: + + bool m_enabled = false; +}; diff --git a/carta/cpp/plugins/DevIntegration/DevIntegration.pro b/carta/cpp/plugins/DevIntegration/DevIntegration.pro new file mode 100644 index 00000000..94007ad9 --- /dev/null +++ b/carta/cpp/plugins/DevIntegration/DevIntegration.pro @@ -0,0 +1,31 @@ +! include(../../common.pri) { + error( "Could not find the common.pri file!" ) +} + +INCLUDEPATH += $$PROJECT_ROOT +DEPENDPATH += $$PROJECT_ROOT + +QT += core gui + +TARGET = plugin +TEMPLATE = lib +CONFIG += plugin + +SOURCES += \ + DevIntegration.cpp +HEADERS += \ + DevIntegration.h + +OTHER_FILES += \ + plugin.json + +# copy json to build directory +#MYFILES = $$files($${PWD}/files/*.*) +MYFILES = plugin.json +copy_files.name = copy large files +copy_files.input = MYFILES +# change datafiles to a directory you want to put the files to +copy_files.output = $${OUT_PWD}/${QMAKE_FILE_BASE}${QMAKE_FILE_EXT} +copy_files.commands = ${COPY_FILE} ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} +copy_files.CONFIG += no_link target_predeps +QMAKE_EXTRA_COMPILERS += copy_files diff --git a/carta/cpp/plugins/DevIntegration/plugin.json b/carta/cpp/plugins/DevIntegration/plugin.json new file mode 100644 index 00000000..42ca01c3 --- /dev/null +++ b/carta/cpp/plugins/DevIntegration/plugin.json @@ -0,0 +1,12 @@ +{ + "api" : "1", + "name" : "DevIntegration", + "version" : "1", + "type" : "C++", + "description": [ + "Dev integration plugin.", + "This allows insecurely specify a filename to load via " + ], + "about" : "Part of carta. Written by Pavol", + "depends" : [ ] +} diff --git a/carta/cpp/plugins/Histogram/Histogram1.cpp b/carta/cpp/plugins/Histogram/Histogram1.cpp index fc316eb8..e4499a2b 100755 --- a/carta/cpp/plugins/Histogram/Histogram1.cpp +++ b/carta/cpp/plugins/Histogram/Histogram1.cpp @@ -2,6 +2,7 @@ #include "CartaLib/Hooks/Histogram.h" #include "ImageHistogram.h" #include "CartaLib/Hooks/LoadAstroImage.h" +#include "CartaLib/Hooks/Initialize.h" #include #include @@ -225,7 +226,7 @@ std::pair Histogram1::_getFrequencyBounds( int channelMin, int ch } bool Histogram1::handleHook( BaseHook & hookData ){ - if ( hookData.is < Initialize > () ) { + if ( hookData.is < Carta::Lib::Hooks::Initialize > () ) { return true; } else if ( hookData.is < Carta::Lib::Hooks::HistogramHook > () ) { @@ -313,7 +314,7 @@ bool Histogram1::handleHook( BaseHook & hookData ){ std::vector < HookId > Histogram1::getInitialHookList(){ return { - Initialize::staticId, + Carta::Lib::Hooks::Initialize::staticId, Carta::Lib::Hooks::HistogramHook::staticId }; } diff --git a/carta/cpp/plugins/clock1/Clock1Plugin.cpp b/carta/cpp/plugins/clock1/Clock1Plugin.cpp index dbe57738..cebda2bc 100644 --- a/carta/cpp/plugins/clock1/Clock1Plugin.cpp +++ b/carta/cpp/plugins/clock1/Clock1Plugin.cpp @@ -1,8 +1,11 @@ #include "Clock1Plugin.h" +#include "CartaLib/Hooks/Initialize.h" #include #include #include +typedef Carta::Lib::Hooks::Initialize Initialize; + Clock1Plugin::Clock1Plugin(QObject *parent) : QObject(parent) { diff --git a/carta/cpp/plugins/plugins.pro b/carta/cpp/plugins/plugins.pro index 8f659646..35743d00 100644 --- a/carta/cpp/plugins/plugins.pro +++ b/carta/cpp/plugins/plugins.pro @@ -19,5 +19,4 @@ SUBDIRS += blurpy SUBDIRS += CyberSKA - - +SUBDIRS += DevIntegration diff --git a/carta/cpp/plugins/qimage/QImagePlugin.cpp b/carta/cpp/plugins/qimage/QImagePlugin.cpp index 72642943..5ca8002a 100644 --- a/carta/cpp/plugins/qimage/QImagePlugin.cpp +++ b/carta/cpp/plugins/qimage/QImagePlugin.cpp @@ -1,5 +1,6 @@ #include "QImagePlugin.h" #include "CartaLib/Hooks/LoadAstroImage.h" +#include "CartaLib/Hooks/Initialize.h" #include #include #include @@ -10,6 +11,7 @@ typedef Carta::Lib::AxisInfo AxisInfo; /// shortcut for LoadAstroImage typedef Carta::Lib::Hooks::LoadAstroImage LoadAstroImage; +typedef Carta::Lib::Hooks::Initialize Initialize; class QImageII; diff --git a/carta/cpp/plugins/tester1/GenericPlugin.cpp b/carta/cpp/plugins/tester1/GenericPlugin.cpp index 5df34a5f..d372b3ff 100644 --- a/carta/cpp/plugins/tester1/GenericPlugin.cpp +++ b/carta/cpp/plugins/tester1/GenericPlugin.cpp @@ -1,7 +1,10 @@ #include "GenericPlugin.h" +#include "CartaLib/Hooks/Initialize.h" #include #include +typedef Carta::Lib::Hooks::Initialize Initialize; + GenericPlugin::GenericPlugin(QObject *parent) : QObject(parent) { @@ -9,34 +12,12 @@ GenericPlugin::GenericPlugin(QObject *parent) : bool GenericPlugin::handleHook(BaseHook &hookData) { - //qDebug() << "GenericPlugin is handling hook #" << hookData.hookId(); if( hookData.is()) { -// Initialize & initHook = static_cast( hookData); - qDebug() << "Woohoo, generic plugin received initialize request."; - -// qDebug() << "You should see debug from Initialize below"; -// initHook.debug(); - return true; } if( hookData.hookId() == PreRender::staticId ) { -// PreRender & hook = static_cast( hookData); - - //qDebug() << "Prerender hook received by generic plugin"; - //qDebug() << " " << hook.paramsPtr->viewName; - //qDebug() << " " << hook.paramsPtr->imgPtr->size(); - - /*QPainter p( hook.paramsPtr->imgPtr); - QString txt = "(C) Generic Plugin"; - QRectF rect = hook.paramsPtr->imgPtr->rect(); - p.setFont( QFont( "Arial", 20)); - rect = p.boundingRect( rect, Qt::AlignRight | Qt::AlignBottom, txt); - p.fillRect( rect, QColor( 0,0,0,128)); - p.setPen( QColor( "white")); - p.drawText( hook.paramsPtr->imgPtr->rect(), Qt::AlignRight | Qt::AlignBottom, txt); -*/ return true; } @@ -48,7 +29,6 @@ bool GenericPlugin::handleHook(BaseHook &hookData) std::vector GenericPlugin::getInitialHookList() { return { - Initialize::staticId, - PreRender::staticId + Initialize::staticId }; } diff --git a/carta/html5/common/skel/source/class/skel/hacks/HackView.js b/carta/html5/common/skel/source/class/skel/hacks/HackView.js index cda82dc8..e32164dd 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/HackView.js +++ b/carta/html5/common/skel/source/class/skel/hacks/HackView.js @@ -40,8 +40,6 @@ qx.Class.define( "skel.hacks.HackView", { x: ev.getDocumentLeft() - box.left, y: ev.getDocumentTop() - box.top }; - console.log( "vwid mm", pt.x, pt.y); - this.m_pointerMoveVar.set( "" + pt.x + " " + pt.y); @@ -53,7 +51,6 @@ qx.Class.define( "skel.hacks.HackView", { x: ev.getDocumentLeft() - box.left, y: ev.getDocumentTop() - box.top }; - console.log( "vwid wheel", pt.x, pt.y, ev.getWheelDelta()); this.m_connector.sendCommand( this.m_prefix + "zoom", "" + pt.x + " " + pt.y + " " + ev.getWheelDelta()); @@ -65,7 +62,6 @@ qx.Class.define( "skel.hacks.HackView", { x: ev.getDocumentLeft() - box.left, y: ev.getDocumentTop() - box.top }; - console.log( "vwid click", pt.x, pt.y, ev.getButton()); this.m_connector.sendCommand( this.m_prefix + "center", "" + pt.x + " " + pt.y + " " + ev.getButton()); diff --git a/carta/html5/server/serverIndex.html b/carta/html5/server/serverIndex.html index fc3a42e8..2cf3a1d3 100644 --- a/carta/html5/server/serverIndex.html +++ b/carta/html5/server/serverIndex.html @@ -22,6 +22,7 @@ + + + + + + +Version 1.0 + + diff --git a/carta/html5/server/serverIndexRelease.html b/carta/html5/server/serverIndexRelease.html new file mode 100644 index 00000000..5febb6b2 --- /dev/null +++ b/carta/html5/server/serverIndexRelease.html @@ -0,0 +1,36 @@ + + + + + + + CartaVis + + + + + + + + + + + + + + + diff --git a/carta/scripts/buildRelease.py b/carta/scripts/buildRelease.py new file mode 100644 index 00000000..062d42df --- /dev/null +++ b/carta/scripts/buildRelease.py @@ -0,0 +1,173 @@ +import os +import json +import sys +import readline + +defaultSettings = { + "version" : "0.0.0", + "destination" : "/tmp/cartaBuild-0.0.0", + "qmakebin" : "/usr/bin/qmake", + "makeflags" : [ "-j4" ] + +} + + +def rlinput(prompt, prefill=''): + """kind of like ras_input but with readline support and pre-filled answer""" + readline.set_startup_hook(lambda: readline.insert_text(prefill)) + try: + return raw_input(prompt) + finally: + readline.set_startup_hook() + +def checkMkPath( path): + try: + os.mkdir(path) + except Exception: + pass + return os.path.isdir(path) + +def getJsonPath( srcRoot): + return srcRoot + "/carta/scripts/lastBuild.json" + +def saveSettings( srcRoot, settings): + """Save settings as json""" + jsonPath = getJsonPath(srcRoot) + try: + jsonFile = open(jsonPath, "w+") + jsonFile.write(json.dumps(settings, + sort_keys=True, + indent=4, + separators=(',', ': '))) + jsonFile.close() + return + except Exception, e: + print str(e) + except: + print "Could not save settings!!!" + print "Unexpected error:", sys.exc_info()[0] + + sys.exit( "Aborting") + + +def getLastSettings(srcRoot): + """Retrieve last build settings from json file""" + jsonPath = getJsonPath(srcRoot) + try: + jsonFile = open( jsonPath, "r") + data = json.load(jsonFile) + jsonFile.close() + settings = defaultSettings.copy() + settings.update( data) + return settings + except: + print "Previous build settings not found!" + + saveSettings( srcRoot, defaultSettings) + +# figure out root of sources +srcRoot = os.path.dirname( os.path.abspath(__file__)) +srcRoot = os.path.abspath( srcRoot) +srcRoot = os.path.abspath( srcRoot + "/../..") + +print "srcRoot", srcRoot + +# get the last settings +settings = getLastSettings( srcRoot) +print settings + +# ask user if this is ok +while True: + print "Current settings:" + print " 1) Version:" , settings["version"] + print " 2) Destination:" , settings["destination"] + print " 3) qmake:" , settings["qmakebin"] + print " 0) Go" + + try: + choice = int(raw_input("Choice:")) + except ValueError: + print "Invalid!" + continue + + if choice == 1: + settings["version"] = rlinput( "New version: ", settings["version"]) + elif choice == 2: + settings["destination"] = rlinput( "New destination: ", settings["destination"]) + elif choice == 3: + settings["qmakebin"] = rlinput( "New qmakebin: ", settings["qmakebin"]) + elif choice == 0: + break + else: + print "Invalid!" + +# save settings in case we modified them +saveSettings( srcRoot, settings) + +# prepare the structure of the destination +checkMkPath( settings["destination"]) + +# change directory to destination +os.chdir( settings["destination"]) + +# invoke qmake in destination directory +from subprocess import call +call([ settings["qmakebin"], "CARTA_BUILD_TYPE=release","-r", srcRoot + "/carta/carta.pro"]) + +# invoke make on c++ code +makeArgs = settings["makeflags"][:] +makeArgs.insert( 0, "make") +call( makeArgs) + +# invoke qooxdoo +os.chdir( srcRoot + "/carta/html5/common/skel") +qooxdooCall = [ "./generate.py" ] +qooxdooCall.append( "-m") +qooxdooCall.append( "BUILD_PATH:" + settings["destination"] + "/html") +qooxdooCall.append( "build") +call( qooxdooCall) + +# copy serverIndex release mode +import shutil +shutil.copy( srcRoot + "/carta/html5/desktop/desktopIndexRelease.html", + settings["destination"] + "/html") +shutil.copy( srcRoot + "/carta/html5/server/serverIndexRelease.html", + settings["destination"] + "/html") +shutil.copy( srcRoot + "/carta/html5/common/libs.js", + settings["destination"] + "/html") +shutil.copy( srcRoot + "/carta/html5/common/CallbackList.js", + settings["destination"] + "/html") +shutil.copy( srcRoot + "/carta/html5/desktop/desktopConnector.js", + settings["destination"] + "/html") +shutil.copy( srcRoot + "/carta/html5/server/pureweb.min.js", + settings["destination"] + "/html") +shutil.copy( srcRoot + "/carta/html5/server/serverConnector.js", + settings["destination"] + "/html") + + +done() + +call(["ls", "-l"]) + +def getLastBuildVersion(): + f = open('../lastBuildVersion', 'r') + line = f.readline() + +def getBuildVersion(): + # check if we have last version + version = getLastBuildVersion() + + + +def updateJsonFile(): + jsonFile = open("replayScript.json", "r") + data = json.load(jsonFile) + jsonFile.close() + + tmp = data["location"] + data["location"] = path + data["mode"] = "replay" + + jsonFile = open("replayScript.json", "w+") + jsonFile.write(json.dumps(data)) + jsonFile.close() diff --git a/carta/scripts/lastBuild.json b/carta/scripts/lastBuild.json new file mode 100644 index 00000000..406753de --- /dev/null +++ b/carta/scripts/lastBuild.json @@ -0,0 +1,8 @@ +{ + "destination": "/tmp/cartaBuild-0.0.0", + "makeflags": [ + "-j4" + ], + "qmakebin": "/home/pfederl/Software/Qt/5.3/gcc_64/bin/qmake", + "version": "0.0.0" +} \ No newline at end of file From 79351e5a54ff2b5947f3cc7e709b5943b8e20db5 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Tue, 29 Sep 2015 13:49:09 -0600 Subject: [PATCH 12/37] more work on release build --- .../skel/widgets/CustomUI/ColorSelector.js | 9 ++++-- .../source/class/skel/widgets/Menu/MenuBar.js | 3 +- carta/scripts/buildRelease.py | 31 +++---------------- carta/scripts/lastBuild.json | 2 +- 4 files changed, 13 insertions(+), 32 deletions(-) diff --git a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/ColorSelector.js b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/ColorSelector.js index bb05e723..7b28eb28 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/ColorSelector.js +++ b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/ColorSelector.js @@ -192,7 +192,8 @@ qx.Class.define( "skel.widgets.CustomUI.ColorSelector", case "input-field-set": //control = new qx.ui.groupbox.GroupBox(this.tr("Details")); - control = new qx.ui.container.Composite(); + //control = new qx.ui.container.Composite(); + control = new qx.ui.groupbox.GroupBox("Details"); var controlLayout = new qx.ui.layout.VBox(); controlLayout.setSpacing(2); control.setLayout(controlLayout); @@ -221,7 +222,8 @@ qx.Class.define( "skel.widgets.CustomUI.ColorSelector", break; case "preset-field-set": //control = new qx.ui.groupbox.GroupBox(/*this.tr("Presets")*/""); - control = new qx.ui.container.Composite(); + //control = new qx.ui.container.Composite(); + control = new qx.ui.groupbox.GroupBox( "Presets"); control.setLayout(new qx.ui.layout.Grow()); control.add(this.getChildControl("preset-grid")); break; @@ -250,7 +252,8 @@ qx.Class.define( "skel.widgets.CustomUI.ColorSelector", case "visual-pane": //control = new qx.ui.groupbox.GroupBox(this.tr("Visual")); - control = new qx.ui.container.Composite(); + //control = new qx.ui.container.Composite(); + control = new qx.ui.groupbox.GroupBox("Visual"); control.setLayout(new qx.ui.layout.HBox(2)); control.add(this.getChildControl("hue-saturation-pane")); control.add(this.getChildControl("brightness-pane")); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Menu/MenuBar.js b/carta/html5/common/skel/source/class/skel/widgets/Menu/MenuBar.js index 3fb04b32..61f76765 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Menu/MenuBar.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Menu/MenuBar.js @@ -18,7 +18,8 @@ qx.Class.define("skel.widgets.Menu.MenuBar", { this.base(arguments); this.m_connector = mImport("connector"); - this.m_menuPart = new skel.widgets.Menu.MenuBarPart(); + //this.m_menuPart = new skel.widgets.Menu.MenuBarPart(); + this.m_menuPart = new qx.ui.toolbar.Part(); this.add(this.m_menuPart); this._initMenu(); this.addSpacer(); diff --git a/carta/scripts/buildRelease.py b/carta/scripts/buildRelease.py index 062d42df..f2d576ac 100644 --- a/carta/scripts/buildRelease.py +++ b/carta/scripts/buildRelease.py @@ -7,7 +7,7 @@ "version" : "0.0.0", "destination" : "/tmp/cartaBuild-0.0.0", "qmakebin" : "/usr/bin/qmake", - "makeflags" : [ "-j4" ] + "makeflags" : [ "-j12" ] } @@ -78,10 +78,11 @@ def getLastSettings(srcRoot): # ask user if this is ok while True: - print "Current settings:" + print "Current settings (from", getJsonPath(srcRoot), "):" print " 1) Version:" , settings["version"] print " 2) Destination:" , settings["destination"] print " 3) qmake:" , settings["qmakebin"] + print " ) makeflagse:" , " ".join(settings["makeflags"]) print " 0) Go" try: @@ -145,29 +146,5 @@ def getLastSettings(srcRoot): settings["destination"] + "/html") -done() -call(["ls", "-l"]) - -def getLastBuildVersion(): - f = open('../lastBuildVersion', 'r') - line = f.readline() - -def getBuildVersion(): - # check if we have last version - version = getLastBuildVersion() - - - -def updateJsonFile(): - jsonFile = open("replayScript.json", "r") - data = json.load(jsonFile) - jsonFile.close() - - tmp = data["location"] - data["location"] = path - data["mode"] = "replay" - - jsonFile = open("replayScript.json", "w+") - jsonFile.write(json.dumps(data)) - jsonFile.close() +print "Done" \ No newline at end of file diff --git a/carta/scripts/lastBuild.json b/carta/scripts/lastBuild.json index 406753de..98257981 100644 --- a/carta/scripts/lastBuild.json +++ b/carta/scripts/lastBuild.json @@ -1,7 +1,7 @@ { "destination": "/tmp/cartaBuild-0.0.0", "makeflags": [ - "-j4" + "-j12" ], "qmakebin": "/home/pfederl/Software/Qt/5.3/gcc_64/bin/qmake", "version": "0.0.0" From 89fb4bb1eed3257a5808579c6bee09b66070f02b Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Wed, 30 Sep 2015 22:30:01 -0600 Subject: [PATCH 13/37] Qt gui can be now disabled & fileq protocol - you can control whether to show the url/reload/back/etc controls in desktop mode via editing ~/.carta/config.json, i.e. add this to disable it: "qtDecorations" : false - in desktop mode new protocol 'fileq:///' is now recognized, which will allow you to load source-all debug version of the app without running out of file descriptors --- carta/cpp/desktop/DesktopPlatform.cpp | 6 +- carta/cpp/desktop/MainWindow.cpp | 21 ++- carta/cpp/desktop/MainWindow.h | 17 +- carta/cpp/desktop/NetworkAccessManager.cpp | 39 +++++ carta/cpp/desktop/NetworkAccessManager.h | 40 +++++ carta/cpp/desktop/NetworkReplyFileq.cpp | 177 +++++++++++++++++++++ carta/cpp/desktop/NetworkReplyFileq.h | 54 +++++++ carta/cpp/desktop/desktop.pro | 8 +- 8 files changed, 337 insertions(+), 25 deletions(-) create mode 100644 carta/cpp/desktop/NetworkAccessManager.cpp create mode 100644 carta/cpp/desktop/NetworkAccessManager.h create mode 100644 carta/cpp/desktop/NetworkReplyFileq.cpp create mode 100644 carta/cpp/desktop/NetworkReplyFileq.h diff --git a/carta/cpp/desktop/DesktopPlatform.cpp b/carta/cpp/desktop/DesktopPlatform.cpp index 16874f36..9c6e169c 100644 --- a/carta/cpp/desktop/DesktopPlatform.cpp +++ b/carta/cpp/desktop/DesktopPlatform.cpp @@ -92,15 +92,15 @@ DesktopPlatform::DesktopPlatform() // enable web inspector QWebSettings::globalSettings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true); -// QWebSettings::globalSettings()->setAttribute( QWebSettings::Accelerated2dCanvasEnabled, false); +// QWebSettings::globalSettings()->setAttribute( QWebSettings::Accelerated2dCanvasEnabled, true); // create main window m_mainWindow = new MainWindow(); m_mainWindow-> resize( 1000, 700); // add platform and connector to JS exports - m_mainWindow->exportToJs( "QtPlatform", this); - m_mainWindow->exportToJs( "QtConnector", m_connector); + m_mainWindow->addJSExport( "QtPlatform", this); + m_mainWindow->addJSExport( "QtConnector", m_connector); // load the url m_mainWindow->loadUrl( url); diff --git a/carta/cpp/desktop/MainWindow.cpp b/carta/cpp/desktop/MainWindow.cpp index d9edbc95..f428a6d0 100644 --- a/carta/cpp/desktop/MainWindow.cpp +++ b/carta/cpp/desktop/MainWindow.cpp @@ -1,5 +1,8 @@ #include "MainWindow.h" #include "CustomWebPage.h" +#include "NetworkAccessManager.h" +#include "core/Globals.h" +#include "core/MainConfig.h" #include #include #include @@ -13,7 +16,6 @@ MainWindow::MainWindow( ) QNetworkProxyFactory::setUseSystemConfiguration(true); m_view = new QWebView(this); -// m_view-> setPage( new CustomWebPage(this)); connect(m_view, SIGNAL(loadFinished(bool)), SLOT(adjustLocation())); connect(m_view, SIGNAL(titleChanged(QString)), SLOT(adjustTitle())); connect(m_view, SIGNAL(loadProgress(int)), SLOT(setProgress(int))); @@ -44,6 +46,15 @@ MainWindow::MainWindow( ) & QWebFrame::javaScriptWindowObjectCleared, this, & MainWindow::addToJavaScript ); + + if( ! Globals::instance()->mainConfig()->json().value( "qtDecorations").toBool()) { + menuBar()->setVisible( false); + toolBar->setVisible( false); + statusBar()->setVisible( false); + } + + // install 'fileq' protocol handler + m_view->page()->setNetworkAccessManager( new Carta::Desktop::NetworkAccessManager(this)); } void MainWindow::loadUrl(const QUrl & url) @@ -51,17 +62,11 @@ void MainWindow::loadUrl(const QUrl & url) m_view-> load( url ); } -void MainWindow::exportToJs(const QString &name, QObject *objPtr) +void MainWindow::addJSExport(const QString &name, QObject *objPtr) { m_jsExports.push_back( std::make_pair( name, objPtr)); - -// m_view->page()->mainFrame()->addToJavaScriptWindowObject( name, objPtr); } -//const QImage & MainWindow::getImg() const { -// return m_img; -//} - void MainWindow::adjustLocation() { m_locationEdit->setText(m_view->url().toString()); diff --git a/carta/cpp/desktop/MainWindow.h b/carta/cpp/desktop/MainWindow.h index 0b892b9f..be46bf83 100644 --- a/carta/cpp/desktop/MainWindow.h +++ b/carta/cpp/desktop/MainWindow.h @@ -10,9 +10,9 @@ class QWebInspector; class MainWindow : public QMainWindow { Q_OBJECT -// Q_PROPERTY(QImage img READ getImg) public: + /// constructor initializes the GUI MainWindow(); @@ -22,9 +22,7 @@ class MainWindow : public QMainWindow /// adds the given QObject to javascript exports /// these will be exported automatically on page reload /// so call this before calling loadUrl() - void exportToJs( const QString & name, QObject * objPtr); - -// const QImage & getImg() const; + void addJSExport( const QString & name, QObject * objPtr); signals: @@ -32,10 +30,6 @@ public slots: protected: -// Q_INVOKABLE QString getState( const QString & key) { -// return QString( "ok " + key + "."); -// } - protected slots: void adjustLocation(); @@ -47,11 +41,10 @@ protected slots: void addToJavaScript(); private: - QWebView * m_view; - QLineEdit * m_locationEdit; + QWebView * m_view = nullptr; + QLineEdit * m_locationEdit = nullptr; int m_progress; -// QImage m_img; - QWebInspector * m_inspector; // = nullptr; + QWebInspector * m_inspector = nullptr; // = nullptr; std::vector< std::pair< QString, QObject *> > m_jsExports; }; diff --git a/carta/cpp/desktop/NetworkAccessManager.cpp b/carta/cpp/desktop/NetworkAccessManager.cpp new file mode 100644 index 00000000..9a918c47 --- /dev/null +++ b/carta/cpp/desktop/NetworkAccessManager.cpp @@ -0,0 +1,39 @@ +/** + * + **/ + +#include "NetworkAccessManager.h" +#include "NetworkReplyFileq.h" +#include +#include + +namespace Carta +{ +namespace Desktop +{ +NetworkAccessManager::NetworkAccessManager( QObject * parent ) + : QNetworkAccessManager( parent ) +{ } + +QNetworkReply * +NetworkAccessManager::createRequest( + QNetworkAccessManager::Operation op, + const QNetworkRequest & request, + QIODevice * outgoingData ) +{ + QString scheme = request.url().scheme().toLower(); + + // we only override 'fileq' scheme + if ( ( op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation ) + && ( scheme == QLatin1String( "fileq" ) + ) ) { + qDebug() << "Intercepting" << request.url().toString(); + return new NetworkReplyFileq( this, request, op ); + } + + // all other schemes are handled by the default implementation + qDebug() << "Not intercepting" << request.url().toString(); + return QNetworkAccessManager::createRequest( op, request, outgoingData ); +} +} +} diff --git a/carta/cpp/desktop/NetworkAccessManager.h b/carta/cpp/desktop/NetworkAccessManager.h new file mode 100644 index 00000000..a454716e --- /dev/null +++ b/carta/cpp/desktop/NetworkAccessManager.h @@ -0,0 +1,40 @@ +/** + * Custom network access manager implementation that allows new protocol implementation. + * + * The new protocol is 'fileq:///', which behaves exactly the same as file:///, except it + * minimizes the number of simultaneous open files. + * + * The way Qt handles file:/// requests is not implemented very well, as it implements no + * throttling on the number of files opened simultaneously. For example, a qooxdoo application + * compiled in 'source-all' mode will often need 1k+ files opened simultaneously. One solution + * is to increase ulimit... Another one is this one here :) + * + **/ + +#pragma once + +#include +#include + +namespace Carta +{ +namespace Desktop +{ +class NetworkAccessManager + : public QNetworkAccessManager +{ + Q_OBJECT + +public: + + NetworkAccessManager( QObject * parent ); + +protected: + + /// we only need to override this method + virtual QNetworkReply * + createRequest( Operation op, const QNetworkRequest & request, + QIODevice * outgoingData ) override; +}; +} +} diff --git a/carta/cpp/desktop/NetworkReplyFileq.cpp b/carta/cpp/desktop/NetworkReplyFileq.cpp new file mode 100644 index 00000000..2e3d74b0 --- /dev/null +++ b/carta/cpp/desktop/NetworkReplyFileq.cpp @@ -0,0 +1,177 @@ +#include "NetworkReplyFileq.h" + +#include +#include +#include + +namespace Carta +{ +namespace Desktop +{ +NetworkReplyFileq::~NetworkReplyFileq() +{ } + +void +NetworkReplyFileq::close() +{ + m_isClosed = true; +} + +void +NetworkReplyFileq::abort() +{ + m_isClosed = true; +} + +qint64 +NetworkReplyFileq::bytesAvailable() const +{ + if ( m_isClosed ) { + return QNetworkReply::bytesAvailable(); + } + return QNetworkReply::bytesAvailable() + m_fileInfo.size() - m_offset; +} + +bool +NetworkReplyFileq::isSequential() const +{ + return true; +} + +qint64 +NetworkReplyFileq::size() const +{ + return m_fileInfo.size(); +} + +qint64 +NetworkReplyFileq::readData( char * data, qint64 maxlen ) +{ + if ( m_isClosed ) { + return - 1; + } + + QFile file( m_fileInfo.filePath() ); + if ( ! file.open( QIODevice::ReadOnly ) ) { + return - 1; + } + if ( ! file.seek( m_offset ) ) { + return - 1; + } + qint64 ret = file.read( data, maxlen ); + + if ( ret >= 0 && file.pos() != m_offset + ret ) { + qWarning( "offset problems?" ); + } + m_offset = file.pos(); + + file.close(); + return ret; +} // readData + +NetworkReplyFileq::NetworkReplyFileq( QObject * parent, + const QNetworkRequest & req, + const QNetworkAccessManager::Operation op ) + : QNetworkReply( parent ) +{ + setRequest( req ); + setUrl( req.url() ); + setOperation( op ); + setFinished( true ); + QNetworkReply::open( QIODevice::ReadOnly ); + + QUrl url = req.url(); + if ( url.host() == QLatin1String( "localhost" ) ) { + url.setHost( QString() ); + } + +#if ! defined ( Q_OS_WIN ) + + // do not allow UNC paths on Unix + if ( ! url.host().isEmpty() ) { + // we handle only local files + QString msg = QCoreApplication::translate( "QNetworkAccessFileBackend", + "Request for opening non-local file %1" ).arg( + url.toString() ); + setError( QNetworkReply::ProtocolInvalidOperationError, msg ); + QMetaObject::invokeMethod( this, "error", Qt::QueuedConnection, + Q_ARG( QNetworkReply::NetworkError, + QNetworkReply::ProtocolInvalidOperationError ) ); + QMetaObject::invokeMethod( this, "finished", Qt::QueuedConnection ); + return; + } +#endif + if ( url.path().isEmpty() ) { + url.setPath( QLatin1String( "/" ) ); + } + setUrl( url ); + + QString fileName = url.toLocalFile(); + if ( fileName.isEmpty() ) { + if ( url.scheme() == QLatin1String( "qrc" ) ) { + fileName = QLatin1Char( ':' ) + url.path(); + } + else { +#if defined ( Q_OS_ANDROID ) + if ( url.scheme() == QLatin1String( "assets" ) ) { + fileName = QLatin1String( "assets:" ) + url.path(); + } + else +#endif + fileName = url.toString( + QUrl::RemoveScheme | QUrl::RemoveAuthority | QUrl::RemoveFragment | + QUrl::RemoveQuery ); + } + } + + m_fileInfo.setFile( fileName ); + if ( m_fileInfo.isDir() ) { + QString msg = QCoreApplication::translate( "QNetworkAccessFileBackend", + "Cannot open %1: Path is a directory" ).arg( + url.toString() ); + setError( QNetworkReply::ContentOperationNotPermittedError, msg ); + QMetaObject::invokeMethod( this, "error", Qt::QueuedConnection, + Q_ARG( QNetworkReply::NetworkError, + QNetworkReply::ContentOperationNotPermittedError ) ); + QMetaObject::invokeMethod( this, "finished", Qt::QueuedConnection ); + return; + } + + bool canOpen = m_fileInfo.isReadable(); + + // could we open the file? + if ( ! canOpen ) { + QString msg = QCoreApplication::translate( "QNetworkAccessFileBackend", + "Error opening %1: %2" ) + .arg( fileName, "cannot open file" ); + + if ( m_fileInfo.exists() ) { + setError( QNetworkReply::ContentAccessDenied, msg ); + QMetaObject::invokeMethod( this, "error", Qt::QueuedConnection, + Q_ARG( QNetworkReply::NetworkError, + QNetworkReply::ContentAccessDenied ) ); + } + else { + setError( QNetworkReply::ContentNotFoundError, msg ); + QMetaObject::invokeMethod( this, "error", Qt::QueuedConnection, + Q_ARG( QNetworkReply::NetworkError, + QNetworkReply::ContentNotFoundError ) ); + } + QMetaObject::invokeMethod( this, "finished", Qt::QueuedConnection ); + return; + } + + setHeader( QNetworkRequest::LastModifiedHeader, m_fileInfo.lastModified() ); + setHeader( QNetworkRequest::ContentLengthHeader, m_fileInfo.size() ); + + QMetaObject::invokeMethod( this, "metaDataChanged", Qt::QueuedConnection ); + QMetaObject::invokeMethod( this, "downloadProgress", Qt::QueuedConnection, + Q_ARG( qint64, m_fileInfo.size() ), Q_ARG( qint64, m_fileInfo.size() ) ); + QMetaObject::invokeMethod( this, "readyRead", Qt::QueuedConnection ); + QMetaObject::invokeMethod( this, "finished", Qt::QueuedConnection ); + + m_isClosed = false; +} +} +} + diff --git a/carta/cpp/desktop/NetworkReplyFileq.h b/carta/cpp/desktop/NetworkReplyFileq.h new file mode 100644 index 00000000..8d162913 --- /dev/null +++ b/carta/cpp/desktop/NetworkReplyFileq.h @@ -0,0 +1,54 @@ +/** + * This is the class that actually implements the fileq protocol. + * + * Most of the code here was copied from QNetworkReplyFileImpl + * + **/ + +#pragma once + +#include +#include +#include +#include + +namespace Carta +{ +namespace Desktop +{ +class NetworkReplyFileq : public QNetworkReply +{ + Q_OBJECT + +public: + + NetworkReplyFileq( QObject * parent, + const QNetworkRequest & req, + const QNetworkAccessManager::Operation op ); + ~NetworkReplyFileq(); + virtual void + abort() override; + + virtual void + close() override; + + virtual qint64 + bytesAvailable() const override; + + virtual bool + isSequential() const override; + + qint64 + size() const override; + + virtual qint64 + readData( char * data, qint64 maxlen ) override; + +private: + + bool m_isClosed = true; + qint64 m_offset = 0; + QFileInfo m_fileInfo; +}; +} +} diff --git a/carta/cpp/desktop/desktop.pro b/carta/cpp/desktop/desktop.pro index 6ac6343c..6e9e917c 100644 --- a/carta/cpp/desktop/desktop.pro +++ b/carta/cpp/desktop/desktop.pro @@ -8,14 +8,18 @@ HEADERS += \ MainWindow.h \ CustomWebPage.h \ DesktopPlatform.h \ - DesktopConnector.h + DesktopConnector.h \ + NetworkReplyFileq.h \ + NetworkAccessManager.h SOURCES += \ MainWindow.cpp \ CustomWebPage.cpp \ DesktopPlatform.cpp \ desktopMain.cpp \ - DesktopConnector.cpp + DesktopConnector.cpp \ + NetworkAccessManager.cpp \ + NetworkReplyFileq.cpp RESOURCES = resources.qrc From a2bedbcedf6f0ab7ba72ec618a2eb07674f3b63a Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Mon, 5 Oct 2015 21:55:53 -0600 Subject: [PATCH 14/37] first pass of release build complete both desktop and server apps are built, in optimized mode (C++ and JS) still some debugging output present (python/casacore) desktop version no longer needs html parameter to run, it runs its own html/javascript/png/css/etc built into the executable --- carta/cpp/desktop/DesktopPlatform.cpp | 10 ++- carta/cpp/desktop/desktop.pro | 12 +++ .../CasaImageLoader/CasaImageLoader.pro | 1 - carta/cpp/plugins/ColormapsPy/ColormapsPy.py | 2 - carta/scripts/buildRelease.py | 89 +++++++++++++++---- 5 files changed, 91 insertions(+), 23 deletions(-) diff --git a/carta/cpp/desktop/DesktopPlatform.cpp b/carta/cpp/desktop/DesktopPlatform.cpp index 9c6e169c..4cbed2a1 100644 --- a/carta/cpp/desktop/DesktopPlatform.cpp +++ b/carta/cpp/desktop/DesktopPlatform.cpp @@ -76,16 +76,18 @@ DesktopPlatform::DesktopPlatform() QUrl url; auto & cmdLineInfo = * Globals::instance()->cmdLineInfo(); if( cmdLineInfo.htmlPath().isEmpty()) { - url = QUrl("qrc:///html5/desktop/desktopIndex.html"); + // check if we have index in qrc + if( QFileInfo(":/html/desktopIndexRelease.html").exists()) { + url = "qrc:///html/desktopIndexRelease.html"; + } else { + url = "http://www.google.com"; + } } else { url = QUrl::fromUserInput( cmdLineInfo.htmlPath()); } // get the filename sfrom the command line m_initialFileList = cmdLineInfo.fileList(); - if( m_initialFileList.isEmpty()) { - //qFatal( "No input files to open..."); - } // create the connector m_connector = new DesktopConnector(); diff --git a/carta/cpp/desktop/desktop.pro b/carta/cpp/desktop/desktop.pro index 6e9e917c..5c488e07 100644 --- a/carta/cpp/desktop/desktop.pro +++ b/carta/cpp/desktop/desktop.pro @@ -44,3 +44,15 @@ else{ PRE_TARGETDEPS += $$OUT_PWD/../core/libcore.so } +# for release builds +carta_qrc { + +PREPROCESS_FILES = . +preprocess.name = autogenerate qrc file for release mode +preprocess.input = PREPROCESS_FILES +preprocess.output = files.qrc +preprocess.commands = touch files.qrc +preprocess.variable_out = RESOURCES +QMAKE_EXTRA_COMPILERS += preprocess + +} diff --git a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.pro b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.pro index 87276709..197b1d0b 100644 --- a/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.pro +++ b/carta/cpp/plugins/CasaImageLoader/CasaImageLoader.pro @@ -38,7 +38,6 @@ INCLUDEPATH += $${CFITSIODIR}/include #INCLUDEPATH += $$PWD/../../core DEPENDPATH += $$PWD/../../core - OTHER_FILES += \ plugin.json diff --git a/carta/cpp/plugins/ColormapsPy/ColormapsPy.py b/carta/cpp/plugins/ColormapsPy/ColormapsPy.py index 90d0bf85..0cc5c11a 100644 --- a/carta/cpp/plugins/ColormapsPy/ColormapsPy.py +++ b/carta/cpp/plugins/ColormapsPy/ColormapsPy.py @@ -11,8 +11,6 @@ def print(*args, **kwargs): global basename, oldprint return oldprint( basename, ':', *args, **kwargs) -print("hello") - # old code: # #import scipy diff --git a/carta/scripts/buildRelease.py b/carta/scripts/buildRelease.py index f2d576ac..0f2d4cb8 100644 --- a/carta/scripts/buildRelease.py +++ b/carta/scripts/buildRelease.py @@ -2,6 +2,7 @@ import json import sys import readline +from subprocess import call defaultSettings = { "version" : "0.0.0", @@ -11,6 +12,26 @@ } +def writeQrc(resources=[], prefix = "/", qrcfname = "file.qrc"): + """ + Write to the qrc file under the prefix specified + """ + with open('%s'%qrcfname,'w') as f: + f.write('\n \n'%prefix) + for r in resources: + f.write(' %s\n'%r) + f.write(' \n\n') + + +def scanDir(directory): + """ + Scan tree starting from direc + """ + resources = [] + for path, dirs, files in os.walk(directory): + resources += [os.path.join(path,f) for f in files] + return resources + def rlinput(prompt, prefill=''): """kind of like ras_input but with readline support and pre-filled answer""" @@ -20,13 +41,22 @@ def rlinput(prompt, prefill=''): finally: readline.set_startup_hook() -def checkMkPath( path): +def checkMkPathBad( path): try: os.mkdir(path) except Exception: pass return os.path.isdir(path) +def checkMkPath(path): + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == os.errno.EEXIST and os.path.isdir(path): + pass + else: raise + + def getJsonPath( srcRoot): return srcRoot + "/carta/scripts/lastBuild.json" @@ -107,20 +137,9 @@ def getLastSettings(srcRoot): # prepare the structure of the destination checkMkPath( settings["destination"]) +checkMkPath( settings["destination"] + "/cpp/desktop") -# change directory to destination -os.chdir( settings["destination"]) - -# invoke qmake in destination directory -from subprocess import call -call([ settings["qmakebin"], "CARTA_BUILD_TYPE=release","-r", srcRoot + "/carta/carta.pro"]) - -# invoke make on c++ code -makeArgs = settings["makeflags"][:] -makeArgs.insert( 0, "make") -call( makeArgs) - -# invoke qooxdoo +# invoke qooxdoo (we chdir to sources, but the output will be in destination...) os.chdir( srcRoot + "/carta/html5/common/skel") qooxdooCall = [ "./generate.py" ] qooxdooCall.append( "-m") @@ -128,7 +147,7 @@ def getLastSettings(srcRoot): qooxdooCall.append( "build") call( qooxdooCall) -# copy serverIndex release mode +# copy other html/javascript files import shutil shutil.copy( srcRoot + "/carta/html5/desktop/desktopIndexRelease.html", settings["destination"] + "/html") @@ -145,6 +164,44 @@ def getLastSettings(srcRoot): shutil.copy( srcRoot + "/carta/html5/server/serverConnector.js", settings["destination"] + "/html") +# make a symlink inside cpp/desktop to html, so that we can create a working qrc +if os.path.exists( settings["destination"] + "/cpp/desktop/html"): + os.unlink( settings["destination"] + "/cpp/desktop/html") +os.symlink( "../../html", settings["destination"] + "/cpp/desktop/html") + +# create qrc in the desktop directory +os.chdir( settings["destination"] + "/cpp/desktop") +qrcfiles = [] +qrcfiles.append( "html/desktopIndexRelease.html") +qrcfiles.append( "html/libs.js") +qrcfiles.append( "html/CallbackList.js") +qrcfiles.append( "html/desktopConnector.js") +qrcfiles += scanDir( "html/resource") +qrcfiles += scanDir( "html/script") +writeQrc( qrcfiles, "/", "files.qrc") + +# change directory to destination +os.chdir( settings["destination"]) + +# invoke qmake in destination directory +call([ settings["qmakebin"], "CARTA_BUILD_TYPE=release","-r", + "CONFIG+=carta_qrc", + srcRoot + "/carta/carta.pro"]) + +# invoke make on c++ code +makeArgs = settings["makeflags"][:] +makeArgs.insert( 0, "make") +call( makeArgs) + +# make some symlinks to executables +try: + os.symlink( "cpp/desktop/desktop", settings["destination"] + "/cartaviewer") +except: + pass +try: + os.symlink( "cpp/server/server", settings["destination"] + "/cartaserver") +except: + pass -print "Done" \ No newline at end of file +print "Done" From 91271e8b84794b866f88539127c2fbee938b4ebb Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Tue, 13 Oct 2015 23:18:18 -0600 Subject: [PATCH 15/37] added deploy script for cyberska some other cosmetic fixes as well --- carta/cpp/core/Hacks/ILayeredView.h | 10 +- carta/cpp/core/IConnector.h | 9 +- carta/cpp/core/State/ObjectManager.cpp | 55 +++++------ carta/cpp/core/State/ObjectManager.h | 11 ++- carta/cpp/core/VGView.h | 128 ++++++++++++++++++++++++- carta/cpp/desktop/DesktopConnector.h | 24 ++--- carta/scripts/deploy-viz2-release.py | 101 +++++++++++++++++++ carta/scripts/scripts.pro | 3 + 8 files changed, 289 insertions(+), 52 deletions(-) create mode 100755 carta/scripts/deploy-viz2-release.py diff --git a/carta/cpp/core/Hacks/ILayeredView.h b/carta/cpp/core/Hacks/ILayeredView.h index bad79413..93477837 100644 --- a/carta/cpp/core/Hacks/ILayeredView.h +++ b/carta/cpp/core/Hacks/ILayeredView.h @@ -1,5 +1,5 @@ /** - * + * Experimental code below. None of it is actually being used anywhere (yet). **/ #pragma once @@ -12,6 +12,14 @@ #include #include +namespace Carta +{ +namespace Lib +{ + +} +} + namespace Carta { namespace Lib diff --git a/carta/cpp/core/IConnector.h b/carta/cpp/core/IConnector.h index aab3604b..fc28f518 100644 --- a/carta/cpp/core/IConnector.h +++ b/carta/cpp/core/IConnector.h @@ -54,9 +54,6 @@ class IConnector { /// set state to a new value virtual void setState( const QString & path, const QString & value) = 0; - //Return a string indicating the location where state is saved/restored. - virtual QString getStateLocation( const QString& saveName ) const = 0; - /// read state virtual QString getState( const QString & path) = 0; @@ -72,6 +69,12 @@ class IConnector { /// \todo maybe we can have a universal 'removeCallback' for commands/states virtual void removeStateCallback( const CallbackID & id ) = 0; + /// return filename where state is saved/restored. + /// \todo this should not be part of connector, as it has nothing to do with communication + /// between C++ and JavaScript + virtual QString getStateLocation( const QString& saveName ) const = 0; + + virtual ~IConnector() {} }; diff --git a/carta/cpp/core/State/ObjectManager.cpp b/carta/cpp/core/State/ObjectManager.cpp index e520f0f4..0e41d2f6 100644 --- a/carta/cpp/core/State/ObjectManager.cpp +++ b/carta/cpp/core/State/ObjectManager.cpp @@ -25,7 +25,7 @@ QList CartaObjectFactory::globalIds = {"AnimationTypes","ChannelUnits", QString CartaObject::addIdToCommand (const QString & command) const { QString fullCommand = m_path; if ( command.size() > 0 ){ - fullCommand = fullCommand + m_Delimiter + command; + fullCommand = fullCommand + CommandDelimiter + command; } return fullCommand; } @@ -108,49 +108,50 @@ void CartaObject::resetStateData( const QString& /*state*/ ){ void CartaObject::addCommandCallback (const QString & rawCommand, IConnector::CommandCallback callback) { - IConnector * connector = Globals::instance()->connector(); - QString actualCmd = addIdToCommand( rawCommand ); - connector->addCommandCallback ( actualCmd, callback); + conn()-> addCommandCallback ( addIdToCommand( rawCommand ), callback); } -int64_t CartaObject::addStateCallback( const QString& statePath, const IConnector::StateChangedCallback & cb){ - IConnector * connector = Globals::instance()->connector(); - return connector->addStateCallback( statePath, cb ); +int64_t CartaObject::addStateCallback( const QString& statePath, const IConnector::StateChangedCallback & cb) +{ + return conn()-> addStateCallback( statePath, cb ); } -void CartaObject::registerView( IView * view){ - IConnector * connector = Globals::instance()->connector(); - connector->registerView( view ); +void CartaObject::registerView( IView * view) +{ + conn()-> registerView( view ); } -void CartaObject::refreshView( IView* view ){ - IConnector * connector = Globals::instance()->connector(); - connector->refreshView( view ); +void CartaObject::refreshView( IView* view ) +{ + conn()-> refreshView( view ); } -void CartaObject::unregisterView(){ - IConnector * connector = Globals::instance()->connector(); - QString viewId = m_path +"/view"; - connector->unregisterView( viewId ); +void CartaObject::unregisterView() +{ + conn()-> unregisterView( m_path +"/view" ); } -QString CartaObject::getStateLocation( const QString& name ) const { - IConnector * connector = Globals::instance()->connector(); - return connector->getStateLocation( name ); +QString CartaObject::getStateLocation( const QString& name ) const +{ + return conn()-> getStateLocation( name ); } QString -CartaObject::removeId (const QString & commandAndId){ - // Command should have the ID as a prefix. Strip off - // that part plus the delimiter and return the rest. - - assert (commandAndId.count (m_Delimiter) == 1); +CartaObject::removeId (const QString & commandAndId) +{ + // Command should have the ID as a prefix. + CARTA_ASSERT(commandAndId.count (CommandDelimiter) == 1); - return commandAndId.section (m_Delimiter, 1); + // return command without the ID prefix + return commandAndId.section (CommandDelimiter, 1); } - +IConnector * +CartaObject::conn() { + static IConnector * conn = Globals::instance()-> connector(); + return conn; +} const QString ObjectManager::CreateObject = "CreateObject"; const QString ObjectManager::ClassName = "ClassName"; diff --git a/carta/cpp/core/State/ObjectManager.h b/carta/cpp/core/State/ObjectManager.h index dc083662..594ff7a0 100644 --- a/carta/cpp/core/State/ObjectManager.h +++ b/carta/cpp/core/State/ObjectManager.h @@ -75,11 +75,13 @@ class CartaObject { /** * Return the type of the object. + * + * Normally the type of an object will be the class name, but for snapshots such as session snapshots + * this method may be overriden to append an additional identifier such as 'data' after the class name. + * * @param snapType the type of Snapshot. * @return an identifier for the type of the object. */ - //Normally the type of an object will be the class name, but for snapshots such as session snapshots - //this method may be overriden to append an additional identifier such as 'data' after the class name. virtual QString getSnapType(CartaObject::SnapshotType snapType= CartaObject::SnapshotType::SNAPSHOT_INFO) const; QString getType() const; @@ -127,6 +129,9 @@ class CartaObject { Object * m_object; }; + /// helper to get connector + static IConnector * conn(); + protected: StateInterface m_state; @@ -136,7 +141,7 @@ class CartaObject { QString m_id; QString m_path; - static const char m_Delimiter = ':'; + static const char CommandDelimiter = ':'; }; diff --git a/carta/cpp/core/VGView.h b/carta/cpp/core/VGView.h index 8b73cece..dd35c53b 100644 --- a/carta/cpp/core/VGView.h +++ b/carta/cpp/core/VGView.h @@ -46,7 +46,7 @@ class VGView : public QObject, protected IView /// whether to render VG on server /// by default it's set to server side void - setServerSideVGRendering( bool flag); + setServerSideVGRendering( bool flag ); virtual ~VGView() { } @@ -77,8 +77,6 @@ private slots: bool m_serverSideVG = true; QImage m_rasterImage; - // IView interface - private: virtual void @@ -102,5 +100,129 @@ private slots: virtual void handleKeyEvent( const QKeyEvent & event ) override; }; + + +/// +/// \brief The RemoteView class is an API specification for interacting with +/// graphical views displayed on client(s) +/// +/// It offers the basic building block of rendering raster and/or vector graphics +/// on the client. The vector graphics is always layed over the raster graphics. +/// +class IRemoteView + : public QObject +{ + Q_OBJECT + + +public: + + typedef Carta::Lib::VectorGraphics::VGList VGList; + + /// returns size of the view in the master's UI + virtual const QSize & + size() = 0; + + /// returns the unique name of this view + virtual QString + name() = 0; + + /// sets the raster to be rendered in the view + virtual void + setRaster( const QColor & color ) = 0; + + virtual void + setRaster( const QImage & image ) = 0; + + /// sets the VG to be rendered on top of the raster + virtual void + setVG( const VGList & vglist ) = 0; + +public slots: + + /// schedule a refresh with a given ID + /// this allows the caller to be notified when the latest painting has reached the client + virtual qint64 + scheduleRepaint( qint64 id = - 1 ) = 0; + +signals: + + /// this signal is emitted when the master UI changes the size of the view + void + sizeChanged(); + + /// emitted when client repainted the view + void + repainted( qint64 id ); +}; + +std::shared_ptr createRemoteView ( QString viewName); + +class IQImageCombiner { +public: + virtual void combine( QImage & src1dst, const QImage & src2) = 0; +}; + +class AlphaCombiner : public IQImageCombiner { + +public: + void setAlpha( double alpha) { + CARTA_ASSERT( alpha >= 0.0 && alpha <= 1.0); + m_alpha = alpha; + } + + virtual void combine(QImage & src1dst, const QImage & src2) override + { + QPainter p( & src1dst); + p.setOpacity( m_alpha); + p.drawImage( 0, 0, src2); + } +private: + double m_alpha = 1.0; +}; + +class PixelMaskCombiner : public IQImageCombiner { +public: + void setMask( quint32 mask = DefaultMask) { + m_mask = mask; + } + void setAlpha( double alpha = 1.0) { + CARTA_ASSERT( alpha >= 0.0 && alpha <= 1.0); + m_alpha = alpha; + } + virtual void combine(QImage & src1dst, const QImage & src2) override + { + // if the mask is default, we do simple alpha composition + if( m_mask == DefaultMask) { + QPainter p( & src1dst); + p.setOpacity( m_alpha); + p.drawImage( 0, 0, src2); + return; + } + // otherwise we need to + QImage src22 = src2; + if( src22.format() != QImage::Format_ARGB32) { + src22 = src22.convertToFormat( QImage::Format_ARGB32); + } + for( int y = 0 ; y < src2.height() ; y ++) { + unsigned char * chr = src22.scanLine( y); + QRgb * ptr = (QRgb *) (chr); + for( int x = 0 ; x < src22.width() ; x ++) { + * ptr = ( * ptr) & m_mask; + ptr ++; + } + } + QPainter p( & src1dst); + p.setOpacity( m_alpha); + p.drawImage( 0, 0, src22); + } + + static constexpr quint32 DefaultMask = 0xffffffff; + +private: + quint32 m_mask = DefaultMask; + double m_alpha = 1.0; +}; + } } diff --git a/carta/cpp/desktop/DesktopConnector.h b/carta/cpp/desktop/DesktopConnector.h index 12f4cc35..fca863df 100644 --- a/carta/cpp/desktop/DesktopConnector.h +++ b/carta/cpp/desktop/DesktopConnector.h @@ -26,21 +26,15 @@ class DesktopConnector : public QObject, public IConnector explicit DesktopConnector(); // implementation of IConnector interface - virtual void initialize( const InitializeCallback & cb) Q_DECL_OVERRIDE; - //virtual void setState(const QString & path, const QString & newValue) Q_DECL_OVERRIDE; - //virtual QString getState(const QString &path) Q_DECL_OVERRIDE; - virtual void setState(const QString& state, const QString & newValue) Q_DECL_OVERRIDE; - - //Return the value of the state with the given key and window id. - virtual QString getState(const QString&) Q_DECL_OVERRIDE; - - virtual CallbackID addCommandCallback( const QString & cmd, const CommandCallback & cb) Q_DECL_OVERRIDE; - virtual CallbackID addStateCallback(CSR path, const StateChangedCallback &cb) Q_DECL_OVERRIDE; - virtual void registerView(IView * view) Q_DECL_OVERRIDE; - - void unregisterView( const QString& viewName ) Q_DECL_OVERRIDE; - virtual void refreshView(IView *view) Q_DECL_OVERRIDE; - virtual void removeStateCallback( const CallbackID & id); + virtual void initialize( const InitializeCallback & cb) override; + virtual void setState(const QString& state, const QString & newValue) override; + virtual QString getState(const QString&) override; + virtual CallbackID addCommandCallback( const QString & cmd, const CommandCallback & cb) override; + virtual CallbackID addStateCallback(CSR path, const StateChangedCallback &cb) override; + virtual void registerView(IView * view) override; + void unregisterView( const QString& viewName ) override; + virtual void refreshView(IView *view) override; + virtual void removeStateCallback( const CallbackID & id) override; /// Return the location where the state is saved. virtual QString getStateLocation( const QString& saveName ) const; diff --git a/carta/scripts/deploy-viz2-release.py b/carta/scripts/deploy-viz2-release.py new file mode 100755 index 00000000..424ba3e5 --- /dev/null +++ b/carta/scripts/deploy-viz2-release.py @@ -0,0 +1,101 @@ +#!/usr/bin/python + +from __future__ import with_statement +from fabric.api import * +from fabric.contrib.console import confirm +import os, os.path +from fabric.contrib.files import exists +from fabric.contrib.project import rsync_project + +print "Hello from deploy-viz2-release.py" + + + +# intended usage: +# 1) cd into build directory where the release was compiled using the buildRelease.py +# +# cd /scratch/builds/carta/Dev/ +# 2) run the deploy script from the source directory +# python -m fabric -f ~/Work/CARTAvis/carta/scripts/fabfile2.py deploy + + +env.user = "calsci" +env.hosts = ['viz2.cyberska.org'] +pureWebRoot = "PureWeb/4.1.1" +remoteDeployDir = "CartaViewerRelease" +# localSourceDir = "/home/pfederl/Work/CARTAvis" +# +# if localSourceDir.endswith("/"): +# abort("Please don't terminate localSourceDir with a slash") + +def checkLocalDirectory(): + puts("checking local directory") + ok = True + ok = ok and os.path.isfile("Makefile") + ok = ok and os.path.isfile("cpp/server/server") + ok = ok and os.path.isfile("cartaviewer") + if not ok: + abort("Please run me from the build directory") + puts("OK") + +# def makeClean(): +# puts("building...") +# local("make -j4", capture=True) +# puts("OK") + +def checkServerUp(): + puts("Checking if server is reachable") + with settings(warn_only=True),cd(pureWebRoot): + result = run('pwd') + if result.failed: + abort("Server is not reachable or PureWeb directory not found.") + puts("OK") + +def checkDeployDir(): + puts("Checking if deploy directory exists") + if not exists(remoteDeployDir,verbose=True): + abort("Deploy directory cannot be found on server: " + remoteDeployDir) + puts("OK") + + +def uploadBuildFiles(): + puts("Uploading build files") + rsync_project( + local_dir=".", + remote_dir=remoteDeployDir, + extra_opts="--links --delete") + puts("OK") + return + +# def uploadHtmlFiles(): +# puts("Uploading html files") +# rsync_project( +# local_dir=localSourceDir+"/", +# remote_dir=remoteDeployDir+"/sources", +# extra_opts="--links") +# puts("OK") +# return + +# def fixQooxdooSymlink(): +# puts("Fixing qooxdoo symlink on remote host") +# with cd(remoteDeployDir+"/sources/carta/html5/common"): +# run("ln -nfs /home/calsci/qooxdoo-3.5-sdk .") +# puts("OK") +# return + +@task +def deploy(): + checkLocalDirectory() + # makeClean() + puts("Deploying to %(host)s as %(user)s" % env) + puts("Destination: %s" % remoteDeployDir ) + if not confirm("You are about to deploy to %(host)s. Continue?" % env,default=False): + abort("Ok, aborting") + checkServerUp() + checkDeployDir() + uploadBuildFiles() + # uploadHtmlFiles() + # fixQooxdooSymlink() + +if __name__ == '__main__': + execute(deploy) diff --git a/carta/scripts/scripts.pro b/carta/scripts/scripts.pro index 99f4b949..33adcd91 100644 --- a/carta/scripts/scripts.pro +++ b/carta/scripts/scripts.pro @@ -11,3 +11,6 @@ copy_files.output = ${QMAKE_FILE_BASE}${QMAKE_FILE_EXT} copy_files.commands = ${COPY_FILE} ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} copy_files.CONFIG += no_link target_predeps QMAKE_EXTRA_COMPILERS += copy_files + +DISTFILES += \ + deploy-viz2-release.py From 08768d5034fcb8f47c9950dd6acfc94bce91e37f Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Mon, 26 Oct 2015 23:18:18 -0600 Subject: [PATCH 16/37] working on better layered demo --- carta/cpp/CartaLib/CartaLib.pro | 6 +- carta/cpp/CartaLib/IRemoteVGView.cpp | 180 +++++++++++ carta/cpp/CartaLib/IRemoteVGView.h | 296 ++++++++++++++++++ .../CartaLib/VectorGraphics/BetterQPainter.h | 19 +- carta/cpp/CartaLib/VectorGraphics/VGList.h | 32 ++ carta/cpp/core/Hacks/HackViewer.cpp | 187 ++++++++++- carta/cpp/core/Hacks/ILayeredView.cpp | 5 +- carta/cpp/core/Hacks/ILayeredView.h | 152 ++++++++- carta/cpp/core/Hacks/ManagedLayerView.cpp | 36 +++ carta/cpp/core/Hacks/ManagedLayerView.h | 108 +++++++ carta/cpp/core/IConnector.h | 11 + carta/cpp/core/SimpleRemoteVGView.cpp | 116 +++++++ carta/cpp/core/SimpleRemoteVGView.h | 91 ++++++ carta/cpp/core/VGView.h | 124 +------- carta/cpp/core/core.pro | 8 +- carta/cpp/cpp.pro | 2 +- carta/cpp/desktop/DesktopConnector.cpp | 9 +- carta/cpp/desktop/DesktopConnector.h | 3 + carta/cpp/desktop/resources.qrc | 7 - carta/cpp/server/ServerConnector.cpp | 16 +- carta/cpp/server/ServerConnector.h | 5 +- carta/html5/common/skel/config.json | 257 +++++++-------- .../skel/boundWidgets/View/SuffixedView.js | 33 -- .../class/skel/boundWidgets/View/VGView.js | 46 +++ .../skel/source/class/skel/hacks/HackView.js | 6 +- .../skel/source/class/skel/hacks/Hacks.js | 39 ++- .../class/skel/hacks/LayeredViewHack.js | 49 +++ 27 files changed, 1476 insertions(+), 367 deletions(-) create mode 100644 carta/cpp/CartaLib/IRemoteVGView.cpp create mode 100644 carta/cpp/CartaLib/IRemoteVGView.h create mode 100644 carta/cpp/core/Hacks/ManagedLayerView.cpp create mode 100644 carta/cpp/core/Hacks/ManagedLayerView.h create mode 100644 carta/cpp/core/SimpleRemoteVGView.cpp create mode 100644 carta/cpp/core/SimpleRemoteVGView.h delete mode 100644 carta/html5/common/skel/source/class/skel/boundWidgets/View/SuffixedView.js create mode 100644 carta/html5/common/skel/source/class/skel/boundWidgets/View/VGView.js create mode 100644 carta/html5/common/skel/source/class/skel/hacks/LayeredViewHack.js diff --git a/carta/cpp/CartaLib/CartaLib.pro b/carta/cpp/CartaLib/CartaLib.pro index 33c2c0c7..e2c5e516 100755 --- a/carta/cpp/CartaLib/CartaLib.pro +++ b/carta/cpp/CartaLib/CartaLib.pro @@ -31,7 +31,8 @@ SOURCES += \ IWcsGridRenderService.cpp \ ContourSet.cpp \ Algorithms/LineCombiner.cpp \ - IImageRenderService.cpp + IImageRenderService.cpp \ + IRemoteVGView.cpp HEADERS += \ CartaLib.h\ @@ -67,7 +68,8 @@ HEADERS += \ Hooks/GetInitialFileList.h \ Hooks/Initialize.h \ IImageRenderService.h \ - Hooks/GetImageRenderService.h + Hooks/GetImageRenderService.h \ + IRemoteVGView.h unix { target.path = /usr/lib diff --git a/carta/cpp/CartaLib/IRemoteVGView.cpp b/carta/cpp/CartaLib/IRemoteVGView.cpp new file mode 100644 index 00000000..823c10d4 --- /dev/null +++ b/carta/cpp/CartaLib/IRemoteVGView.cpp @@ -0,0 +1,180 @@ +/** + * + **/ + +#include "IRemoteVGView.h" +#include "core/IConnector.h" +#include "VectorGraphics/VGList.h" + +#include +#include + +namespace Carta +{ +namespace Lib +{ +LayeredRemoteVGView::SharedPtr +LayeredRemoteVGView::create( IConnector * connector, QString viewName, QObject * parent ) +{ + // return std::make_shared< LayeredRemoteVGView > ( connector, viewName, parent); + // we love C++ /sarcasm + LayeredRemoteVGView * lv = new LayeredRemoteVGView( connector, viewName, parent ); + return std::shared_ptr < LayeredRemoteVGView > ( lv ); +} + +int +LayeredRemoteVGView::nRasterLayers() +{ + return m_rasterLayers.size(); +} + +int +LayeredRemoteVGView::nVGLayers() +{ + return m_vgLayers.size(); +} + +int +LayeredRemoteVGView::nLayers() +{ + return nRasterLayers() + nVGLayers(); +} + +void +LayeredRemoteVGView::resetLayers() +{ + m_vgLayers.clear(); + m_rasterLayers.clear(); +} + +void +LayeredRemoteVGView::setRasterLayer( int layer, const QImage & img ) +{ + CARTA_ASSERT( layer >= 0 && layer < 1000 ); + if ( int ( m_rasterLayers.size() ) <= layer ) { + m_rasterLayers.resize( layer + 1 ); + } + m_rasterLayers[layer].qimg = img; +} + +void +LayeredRemoteVGView::setRasterLayerCombiner( int layer, IQImageCombiner::SharedPtr combiner ) +{ + CARTA_ASSERT( layer >= 0 && layer < 1000 ); + if ( int ( m_rasterLayers.size() ) <= layer ) { + m_rasterLayers.resize( layer + 1 ); + } + m_rasterLayers[layer].combiner = combiner; +} + +void +LayeredRemoteVGView::setVGLayer( int layer, const VectorGraphics::VGList & vglist ) +{ + CARTA_ASSERT( layer >= 0 && layer < 1000 ); + if ( int ( m_vgLayers.size() ) <= layer ) { + m_vgLayers.resize( layer + 1 ); + } + m_vgLayers[layer].vglist = vglist; +} + +QSize +LayeredRemoteVGView::getClientSize() +{ + return m_vgView-> getClientSize(); +} + +qint64 +LayeredRemoteVGView::scheduleRepaint( qint64 id ) +{ + if ( id == - 1 ) { + id = m_repaintId++; + } + m_repaintId = id; + if ( ! m_timer-> isActive() ) { + m_timer-> start(); + } + return m_repaintId; +} + +LayeredRemoteVGView::LayeredRemoteVGView( IConnector * connector, + QString viewName, + QObject * parent ) + : QObject( parent ) +{ + m_vgView.reset( connector-> makeRemoteVGView( viewName )); + + /// forward signals directly + connect( m_vgView.get(), SIGNAL( sizeChanged() ), this, SIGNAL( sizeChanged() ) ); + connect( m_vgView.get(), SIGNAL( repainted() ), this, SIGNAL( repainted() ) ); + + m_timer = new QTimer( this ); + m_timer-> setSingleShot( true ); + m_timer-> setInterval( 1 ); + connect( m_timer, & QTimer::timeout, this, & Me::timerCB ); +} + +void +LayeredRemoteVGView::timerCB() +{ + // figure out the size of the buffer (max of the raster sizes) + QSize size( 1, 1); + for ( auto & layer : m_rasterLayers ) { + size = size.expandedTo( layer.qimg.size()); + } + + QImage buff( size, QImage::Format_ARGB32_Premultiplied ); + buff.fill( QColor( 0, 0, 0, 255 ) ); + + // combine all raster layers into buff using their respective compositors +// if ( m_rasterLayers.size() > 0 ) { +// // the background will be the same size as the first layer, but filled with black +// buff = m_rasterLayers[0].qimg; +// buff.fill( QColor( 0, 0, 0, 255 ) ); + + // now go through the layers and paint them on top of the last result + for ( auto & layer : m_rasterLayers ) { + IQImageCombiner::SharedPtr combiner = layer.combiner; + if ( ! combiner ) { + combiner = std::make_shared < DefaultCombiner > (); + } + combiner->combine( buff, layer.qimg ); + } + +// for ( size_t i = 0 ; i < m_rasterLayers.size() ; ++i ) { +// IQImageCombiner::SharedPtr combiner = m_rasterLayers[i].combiner; +// if ( ! combiner ) { +// combiner = std::make_shared < DefaultCombiner > (); +// } +// combiner->combine( buff, m_rasterLayers[i].qimg ); +// } +// } + + m_vgView-> setRaster( buff ); + + // concatenate all VG lists into one + VectorGraphics::VGComposer composer; + for ( auto & vglayer : m_vgLayers ) { + composer.append < VectorGraphics::Entries::Reset > (); + composer.appendList( vglayer.vglist ); + } + + m_vgView-> setVG( composer.vgList()); + + // now we render all VG layers on top of buff +// for ( auto & vglayer : m_vgLayers ) { +// VectorGraphics::VGListQPainterRenderer renderer; +// QPainter painter( & buff ); +// renderer.render( vglayer.vglist, painter ); +// } + + // render all layers on top of each other +// QColor color( qrand() % 255, qrand() % 255, qrand() % 255 ); +// QImage img( 100, 100, QImage::Format_ARGB32_Premultiplied); +// img.fill( color); +// m_vgView-> setRaster( buff ); + + // schedule repaint + (void) m_vgView-> scheduleRepaint( m_repaintId ); +} // timerCB +} +} diff --git a/carta/cpp/CartaLib/IRemoteVGView.h b/carta/cpp/CartaLib/IRemoteVGView.h new file mode 100644 index 00000000..6464cc34 --- /dev/null +++ b/carta/cpp/CartaLib/IRemoteVGView.h @@ -0,0 +1,296 @@ +#pragma once + +#include "CartaLib.h" +#include "core/IView.h" +#include "VectorGraphics/VGList.h" + +#include +#include +#include + +namespace Carta +{ +namespace Lib +{ +/// +/// \brief An API specification for rendering graphical views to be displayed by clients. +/// +/// It offers the basic building block of rendering raster and/or vector graphics +/// on the client. The vector graphics is always layed over the raster graphics. +/// +/// The reasons for having an abstracted API for this: +/// a) desktop might want to implement this differently from server +/// b) one day someone might want to write a plugin... +/// +class IRemoteVGView + : public QObject +{ + Q_OBJECT + CLASS_BOILERPLATE( IRemoteVGView ); + +public: + + typedef Carta::Lib::VectorGraphics::VGList VGList; + + IRemoteVGView( QObject * parent ) : QObject( parent ) { } + + /// returns size of the view in client (the master's UI) + virtual const QSize & + getClientSize() = 0; + + /// returns the unique name of this view + virtual const QString & + getRVGViewName() = 0; + + /// sets the raster to be rendered in the view + virtual void + setRaster( const QImage & image ) = 0; + + /// sets the VG to be rendered on top of the raster + virtual void + setVG( const VGList & vglist ) = 0; + +public slots: + + /// schedule a refresh with a given ID + /// this allows the caller to be notified when the latest painting has reached the client + virtual qint64 + scheduleRepaint( qint64 id = - 1 ) = 0; + +signals: + + /// this signal is emitted when the master UI changes the size of the view + void + sizeChanged(); + + /// emitted when client repainted the view + void + repainted( qint64 id ); +}; + +class IQImageCombiner +{ + CLASS_BOILERPLATE( IQImageCombiner ); + +public: + + virtual void + combine( QImage & src1dst, const QImage & src2 ) = 0; + + virtual + ~IQImageCombiner() { } +}; + +class LayeredRemoteVGView; + +class LayerHandle +{ + CLASS_BOILERPLATE( LayerHandle ); + +public: + + void + setRaster( const QImage & ); + + void + setVG( const VectorGraphics::VGList & ); + + void + setRasterCombiner( IQImageCombiner::SharedPtr combiner ); + + virtual + ~LayerHandle() { } +}; + +/// lowest level functionality for layered views +class LayeredRemoteVGView + : public QObject +{ + Q_OBJECT + CLASS_BOILERPLATE( LayeredRemoteVGView ); + +public: + + static + LayeredRemoteVGView::SharedPtr + create( IConnector * connector, QString viewName, QObject * parent = nullptr ); + + int + nRasterLayers(); + + int + nVGLayers(); + + int + nLayers(); + + void + resetLayers(); + + void + setRasterLayer( int layer, const QImage & img ); + + void + setRasterLayerCombiner( int layer, IQImageCombiner::SharedPtr combiner ); + + void + setVGLayer( int layer, const VectorGraphics::VGList & vglist ); + +// LayerHandle +// addRasterLayer(); + +// LayerHandle +// addVGLayer(); + + QSize + getClientSize(); + +public slots: + + /// schedule a refresh with a given ID + /// this allows the caller to be notified when the latest painting has reached the client + qint64 + scheduleRepaint( qint64 id = - 1 ); + +signals: + + /// this signal is emitted when the master UI changes the size of the view + void + sizeChanged(); + + /// emitted when client repainted the view + void + repainted( qint64 id ); + +private: + + /// private constructor to make sure we only create shared pointers of this + LayeredRemoteVGView( IConnector * connector, QString viewName, QObject * parent = nullptr ); + + IRemoteVGView::UniquePtr m_vgView = nullptr; + + struct RasterLayerInfo { + QImage qimg; + IQImageCombiner::SharedPtr combiner = nullptr; + }; + + struct VGLayerInfo { + VectorGraphics::VGList vglist; + }; + + std::vector < RasterLayerInfo > m_rasterLayers; + std::vector < VGLayerInfo > m_vgLayers; + + qint64 m_repaintId = - 1; + QTimer * m_timer = nullptr; + +private slots: + + void + timerCB(); +}; + +/// paints one raster over the other one, overwriting the bottom one +/// it does honor the alphas of the source pixels +class DefaultCombiner : public IQImageCombiner +{ +public: + + virtual void + combine( QImage & src1dst, const QImage & src2 ) override + { + QPainter p( & src1dst ); + p.drawImage( 0, 0, src2 ); + } +}; + +/// same as DefaultCombiner, but it's possible to set the alpha for the whole +/// src image +class AlphaCombiner : public IQImageCombiner +{ +public: + + void + setAlpha( double alpha ) + { + CARTA_ASSERT( alpha >= 0.0 && alpha <= 1.0 ); + m_alpha = alpha; + } + + virtual void + combine( QImage & src1dst, const QImage & src2 ) override + { + QPainter p( & src1dst ); + p.setOpacity( m_alpha ); + p.drawImage( 0, 0, src2 ); + } + +private: + + double m_alpha = 1.0; +}; + +/// applies an RGB mask to the source pixels before painting the source image +/// over the destination +/// one can also set alpha for the whole src image +/// painting is done using QPainter::CompositionMode_Plus by default +/// +/// So the inputs are: +/// alpha +/// mask +/// composition mode +class PixelMaskCombiner : public IQImageCombiner +{ +public: + + void + setCompositionMode( const QPainter::CompositionMode & mode = QPainter::CompositionMode_Plus ) + { + m_compositionMode = mode; + } + + void + setMask( quint32 mask = DefaultMask ) + { + m_mask = mask; + } + + void + setAlpha( double alpha = 1.0 ) + { + CARTA_ASSERT( alpha >= 0.0 && alpha <= 1.0 ); + m_alpha = alpha; + } + + virtual void + combine( QImage & src1dst, const QImage & src2 ) override + { + // otherwise we need to + QImage src22 = src2; + if ( src22.format() != QImage::Format_ARGB32 ) { + src22 = src22.convertToFormat( QImage::Format_ARGB32 ); + } + for ( int y = 0 ; y < src2.height() ; y++ ) { + unsigned char * chr = src22.scanLine( y ); + QRgb * ptr = (QRgb *) ( chr ); + for ( int x = 0 ; x < src22.width() ; x++ ) { + * ptr = ( * ptr ) & m_mask; + ptr++; + } + } + QPainter p( & src1dst ); + p.setCompositionMode( m_compositionMode ); + p.setOpacity( m_alpha ); + p.drawImage( 0, 0, src22 ); + } // combine + + static constexpr quint32 DefaultMask = 0xffffffff; + +private: + + QPainter::CompositionMode m_compositionMode = QPainter::CompositionMode_Plus; + quint32 m_mask = DefaultMask; + double m_alpha = 1.0; +}; +} +} diff --git a/carta/cpp/CartaLib/VectorGraphics/BetterQPainter.h b/carta/cpp/CartaLib/VectorGraphics/BetterQPainter.h index 67f5f9c7..c9aed2f0 100644 --- a/carta/cpp/CartaLib/VectorGraphics/BetterQPainter.h +++ b/carta/cpp/CartaLib/VectorGraphics/BetterQPainter.h @@ -30,12 +30,12 @@ class BetterQPainter class FontInfo { -public: + public: QString name() { return "some font"; } -private: + private: QString m_name; friend class BetterQPainter; @@ -44,6 +44,21 @@ class BetterQPainter BetterQPainter( QPainter & qPainter ) : m_qPainter( qPainter ) { + reset(); + } + + void reset() { + /// @todo is this the best way to reset QPainter to defaults? + /// a documented way (and presumably preferred way) to do this would be via + /// save/restore mechanism, could we do that? + auto device = m_qPainter.device(); + m_qPainter.end(); + m_qPainter.begin(device); + + m_fonts.clear(); + m_indexedPens.clear(); + m_indexedBrushes.clear(); + // setup default fonts m_fonts.push_back( QFont( "Helvetica", 10 ) ); // <-- default m_fonts.push_back( QFont( "Monospace", 10 ) ); diff --git a/carta/cpp/CartaLib/VectorGraphics/VGList.h b/carta/cpp/CartaLib/VectorGraphics/VGList.h index 059c94fb..36f736be 100644 --- a/carta/cpp/CartaLib/VectorGraphics/VGList.h +++ b/carta/cpp/CartaLib/VectorGraphics/VGList.h @@ -39,6 +39,22 @@ class IVGListEntry namespace Entries { +/// reset the state of painter to defaults +class Reset : public IVGListEntry +{ + CLASS_BOILERPLATE( Reset); +public: + virtual void cplusplus(BetterQPainter & painter) override + { + painter.reset(); + } + virtual QStringList javascript() override + { + return QStringList() + << QString( "p.reset();" ); + } +}; + /// line entry implementation class DrawLine : public IVGListEntry { @@ -676,6 +692,14 @@ class VGComposer { public: + /// constructor that starts with an empty VGList + VGComposer() {} + + /// constructor that starts with the supplied VGList + VGComposer( const VGList & vgList) { + m_vgList = vgList; + } + /// /// \brief returns the vector graphics list /// \return the vector graphics list @@ -716,6 +740,14 @@ class VGComposer return setEntry( ind, new EntryType( std::forward < Args > ( params ) ... ) ); } + /// append another list + void appendList( const VGList & vglist) + { + m_vgList.m_entries.insert( m_vgList.m_entries.end(), + vglist.m_entries.begin(), + vglist.m_entries.end()); + } + /// clear all entries void clear() diff --git a/carta/cpp/core/Hacks/HackViewer.cpp b/carta/cpp/core/Hacks/HackViewer.cpp index a80a107c..eabaae47 100644 --- a/carta/cpp/core/Hacks/HackViewer.cpp +++ b/carta/cpp/core/Hacks/HackViewer.cpp @@ -12,6 +12,8 @@ #include "core/Hacks/MainModel.h" #include "core/GrayColormap.h" #include "CartaLib/LinearMap.h" +#include "CartaLib/IRemoteVGView.h" +#include #include #include @@ -60,6 +62,7 @@ HackViewer::start() qDebug() << " " << cmap-> name(); } } + // tell clients about available colormaps for ( size_t i = 0 ; i < m_allColormaps.size() ; i++ ) { prefixedSetState( QString( "cm-names-%1" ).arg( i ), m_allColormaps[i]-> name() ); @@ -78,7 +81,7 @@ HackViewer::start() // initialize asynchronous renderer m_imageViewController.reset( - new Hacks::ImageViewController( m_statePrefix + "/views/IVC7", "7" ) ); + new Hacks::ImageViewController( m_statePrefix + "/views/IVC7", "7" ) ); // tell the view controller to load the image specified on the command line/url m_imageViewController-> loadImage( fname ); @@ -150,17 +153,177 @@ HackViewer::start() prefixedAddStateCallback( "cm-current", colormapCB2 ); // tell clients about available coordinate systems - prefixedSetState( "knownSkyCS/s0", QString("%1 J2000").arg( - Carta::Lib::knownSkyCS2int( Carta::Lib::KnownSkyCS::J2000))); - prefixedSetState( "knownSkyCS/s1", QString("%1 B1950").arg( - static_cast( Carta::Lib::KnownSkyCS::B1950))); - prefixedSetState( "knownSkyCS/s2", QString("%1 ICRS").arg( - static_cast( Carta::Lib::KnownSkyCS::ICRS))); - prefixedSetState( "knownSkyCS/s3", QString("%1 Galactic").arg( - static_cast( Carta::Lib::KnownSkyCS::Galactic))); - prefixedSetState( "knownSkyCS/s4", QString("%1 Ecliptic").arg( - static_cast( Carta::Lib::KnownSkyCS::Ecliptic))); - prefixedSetState( "knownSkyCS/count", "5"); + prefixedSetState( "knownSkyCS/s0", QString( "%1 J2000" ).arg( + Carta::Lib::knownSkyCS2int( Carta::Lib::KnownSkyCS::J2000 ) ) ); + prefixedSetState( "knownSkyCS/s1", QString( "%1 B1950" ).arg( + static_cast < int > ( Carta::Lib::KnownSkyCS::B1950 ) ) ); + prefixedSetState( "knownSkyCS/s2", QString( "%1 ICRS" ).arg( + static_cast < int > ( Carta::Lib::KnownSkyCS::ICRS ) ) ); + prefixedSetState( "knownSkyCS/s3", QString( "%1 Galactic" ).arg( + static_cast < int > ( Carta::Lib::KnownSkyCS::Galactic ) ) ); + prefixedSetState( "knownSkyCS/s4", QString( "%1 Ecliptic" ).arg( + static_cast < int > ( Carta::Lib::KnownSkyCS::Ecliptic ) ) ); + prefixedSetState( "knownSkyCS/count", "5" ); + + // layered view stuff + static Carta::Lib::IRemoteVGView::SharedPtr vgview( m_connector-> makeRemoteVGView( "vgview1" )); + QImage img( 200, 100, QImage::Format_ARGB32_Premultiplied ); + img.fill( 0xff000000 ); + { + QPainter p( & img ); + p.setBrush( QColor( "red" ) ); + p.drawEllipse( 100, 50, 30, 55 ); + } + vgview-> setRaster( img ); + connect( vgview.get(), & Carta::Lib::IRemoteVGView::sizeChanged, [&] () { + qDebug() << "New size" << vgview-> getRVGViewName() << vgview-> getClientSize(); + } + ); + Carta::Lib::VectorGraphics::VGComposer comp; + comp.append < Carta::Lib::VectorGraphics::Entries::FillRect > ( QRectF( 5, 5, 20, + 40 ), QColor( "blue" ) ); + +// comp.append< Carta::Lib::VectorGraphics::Entries::SetBrush> ( QColor( "white")); +// comp.append< Carta::Lib::VectorGraphics::Entries::DrawRect> ( QRectF( 0, 0, 200, 40)); + vgview-> setVG( comp.vgList() ); + vgview-> scheduleRepaint(); + + static QTimer * timer = new QTimer( this ); + static double xxx = 0; + connect( timer, & QTimer::timeout, [ = ] () { + Carta::Lib::VectorGraphics::VGComposer comp; + xxx += 0.1; + if ( xxx > 1 ) { + xxx = 0.0; + } + comp.append < Carta::Lib::VectorGraphics::Entries::FillRect > ( QRectF( xxx * 10 + + 50, 55, 70, + 40 ), + QColor( 0, 0, 255, + 128 ) ); + vgview-> setVG( comp.vgList() ); + vgview-> scheduleRepaint(); + } + ); + timer->setInterval( 100 ); + timer->start(); + + // layered view 2 stuff + auto ccl = [] ( int x, int y, int r, QColor color ) -> QImage { + QImage img( 500, 500, QImage::Format_ARGB32_Premultiplied ); + img.fill( 0xff000000 ); + QPainter p( & img ); + p.setPen( "white" ); + p.setBrush( color ); + p.drawEllipse( QPoint( x, y ), r, r ); + p.end(); + return img; + }; + static Carta::Lib::LayeredRemoteVGView::SharedPtr vgview2 = + Carta::Lib::LayeredRemoteVGView::create( m_connector, "vgview2", this ); + vgview2-> setRasterLayer( 0, ccl( 50, 50, 45, "white" ) ); + auto acombgreen = std::make_shared < Carta::Lib::PixelMaskCombiner > (); + acombgreen-> setAlpha( 1.0 ); + acombgreen-> setMask( 0xff00ff00 ); + vgview2-> setRasterLayerCombiner( 0, acombgreen ); + + vgview2-> setRasterLayer( 1, ccl( 70, 70, 45, QColor( "white" ) ) ); + auto acombred = std::make_shared < Carta::Lib::PixelMaskCombiner > (); + acombred-> setAlpha( 0.9 ); + acombred-> setMask( 0xffff0000 ); + vgview2-> setRasterLayerCombiner( 1, acombred ); + + vgview2-> setRasterLayer( 2, ccl( 50, 80, 45, QColor( "white" ) ) ); + auto acombblue = std::make_shared < Carta::Lib::PixelMaskCombiner > (); + acombblue-> setAlpha( 0.9 ); + acombblue-> setMask( 0xff0000ff ); + vgview2-> setRasterLayerCombiner( 2, acombblue ); + + vgview2-> scheduleRepaint(); + + // commands: + // [args] + // 0 load c1 + // 0 load v1 + // 0 alpha 0.5 + // 0 mask 0xff0000 0.5 + auto cmdCB = + [&] ( const QString & cmd, const QString & params, const QString & sid ) -> QString { + qDebug() << "cmd" << cmd << params << sid; + QStringList list = params.split( ' ', QString::SkipEmptyParts ); + if ( list.size() < 2 ) { + qWarning() << "Bad command" << params; + return "Bad command"; + } + bool ok; + int layer = list[0].toInt( & ok ); + if ( ! ok ) { + qWarning() << "Bad layer in command" << params; + return "Bad layer in command"; + } + + // get op + QString opcmd = list[1]; + if ( opcmd == "load" ) { + if ( list.size() < 3 ) { + return "What do you want to load?"; + } + QString load = list[2]; + if ( load.startsWith( '/') && QFileInfo(load).exists() ) { + QImage img; + if( !img.load( load)) { + return "Could not load image"; + } + vgview2-> setRasterLayer( layer, img); + } + else if ( load == "c1" ) { + vgview2-> setRasterLayer( layer, ccl( 50, 50, 45, "white" ) ); + } + else if ( load == "c2" ) { + vgview2-> setRasterLayer( layer, ccl( 70, 70, 45, "white" ) ); + } + else { + return "Don't know how to load this"; + } + } + else if ( opcmd == "alpha" ) { + if ( list.size() < 3 ) { + return "No alpha value?"; + } + double alpha = list[2].toDouble( & ok); + if( ! ok || ! (alpha >= 0 && alpha <= 1)) { + return "Bad alpha"; + } + auto comp = std::make_shared(); + comp-> setAlpha( alpha); + vgview2-> setRasterLayerCombiner( layer, comp); + } + else if ( opcmd == "mask" ) { + if ( list.size() < 4 ) { + return "No mask and/or alpha value?"; + } + quint32 mask = list[2].toInt( & ok, 16); + if( ! ok ) { + return "Bad mask"; + } + mask |= 0xff000000; + double alpha = list[3].toDouble( & ok); + if( ! ok || ! (alpha >= 0 && alpha <= 1)) { + return "Bad alpha"; + } + auto comp = std::make_shared(); + comp-> setAlpha( alpha); + comp-> setMask( mask); + vgview2-> setRasterLayerCombiner( layer, comp); + } + else { + qWarning() << "Bad layer operation" << params; + return "Bad layer operation"; + } + vgview2-> scheduleRepaint(); + return "OK"; + }; + m_connector-> addCommandCallback( "lvgview-cmd", cmdCB ); qDebug() << "HackViewer has been initialized."; } // start diff --git a/carta/cpp/core/Hacks/ILayeredView.cpp b/carta/cpp/core/Hacks/ILayeredView.cpp index cc48a1af..30c139d0 100644 --- a/carta/cpp/core/Hacks/ILayeredView.cpp +++ b/carta/cpp/core/Hacks/ILayeredView.cpp @@ -1,7 +1,9 @@ /** - * + * Experimental code below. None of it is actually being used anywhere (yet). **/ +#ifdef DONT_COMPILE + #include "ILayeredView.h" static int @@ -177,3 +179,4 @@ apitest() static int activator = apitest(); +#endif diff --git a/carta/cpp/core/Hacks/ILayeredView.h b/carta/cpp/core/Hacks/ILayeredView.h index 93477837..6ec63ed7 100644 --- a/carta/cpp/core/Hacks/ILayeredView.h +++ b/carta/cpp/core/Hacks/ILayeredView.h @@ -5,20 +5,155 @@ #pragma once #include "CartaLib/CartaLib.h" -#include "IView.h" - -#include -#include -#include -#include +#include "CartaLib/VectorGraphics/VGList.h" +#include "CartaLib/IRemoteVGView.h" +#include "core/IConnector.h" +#include +#include namespace Carta { -namespace Lib +namespace Core +{ +namespace Hacks +{ +class LayerHandle; +enum class LayerType +{ + VG, Raster +}; + +enum class InputMaskBits : std::uint64_t +{ + Key = 0b0000001, + MouseActive = 0b0000010, + MousePassive = 0b0000100, + PointerActive = 0b1000, + PointerPassive = 0b1000, + ALL = 0xffffffff +}; + +enum class InputEventType +{ + Key, + Mouse, + Pointer +}; + +class InputEvent +{ +public: + + InputEventType + type(); + + // mouse events + QPointF + cursorPosition(); + + Qt::MouseButton + button(); + + Qt::MouseButtons + buttons(); +}; + +class ManagedLayerView : public QObject +{ + Q_OBJECT + CLASS_BOILERPLATE( ManagedLayerView ); + +public: + + ManagedLayerView( IConnector * connector, QString viewName, QObject * parent = nullptr ) + : QObject( parent ) { } + + LayerHandle * + addNewLayer( QString name ); + + std::vector < LayerHandle * > + layers(); + + virtual + ~ManagedLayerView() { } +}; + +class LayerHandle : public QObject { + Q_OBJECT + CLASS_BOILERPLATE( LayerHandle ); + +public: + + QString + name(); + + void + setInputMask( InputMaskBits mask ); + InputMaskBits + inputMask(); + + int + position(); + + void + setPosition( int ); + + void + setName( QString name ); + + void + setRaster( const QImage & ); + + void + setRasterCombiner( Carta::Lib::IQImageCombiner::SharedPtr combiner ); + + void + setVG( const Carta::Lib::VectorGraphics::VGList & ); + + virtual + ~LayerHandle() { } + +signals: + + // keyboard events + void + key( std::shared_ptr < QKeyEvent > ); + + // active mouse events + void + mouseClick( std::shared_ptr < QMouseEvent > ); + + void + mouseDown( std::shared_ptr < QMouseEvent > ); + + void + mouseUp( std::shared_ptr < QMouseEvent > ); + + void + mouseDrag( std::shared_ptr < QMouseEvent > ); + + // passive mouse events + void + mouseMove( std::shared_ptr < QMouseEvent > ); +}; } } +} + + +#ifdef DONT_COMPILE + +#pragma once + +#include "CartaLib/CartaLib.h" +#include "IView.h" + +#include +#include +#include +#include namespace Carta { @@ -205,7 +340,6 @@ public slots: done(); }; - /// interface representing the minimal APIs for rendering VGlist class IRenderer { @@ -392,3 +526,5 @@ class VGList std::unordered_set < u_int64_t > m_changes; }; } + +#endif // ifdef DONT_COMPILE diff --git a/carta/cpp/core/Hacks/ManagedLayerView.cpp b/carta/cpp/core/Hacks/ManagedLayerView.cpp new file mode 100644 index 00000000..0a19cc9d --- /dev/null +++ b/carta/cpp/core/Hacks/ManagedLayerView.cpp @@ -0,0 +1,36 @@ +/** + * + **/ + + +#include "ManagedLayerView.h" + +namespace Carta +{ +namespace Hacks +{ + +ManagedLayer::ManagedLayer(ManagedLayerViewInh * mlv) +{ + m_mlv = mlv; + m_mlv-> p_addLayer( this); +} + +ManagedLayerViewInh::ManagedLayerViewInh(QString viewName, IConnector * connector, QObject * parent) +{ + m_connector = connector; + + m_rvgv.reset( m_connector-> makeRemoteVGView( viewName)); +} + +IConnector *ManagedLayerViewInh::connector() +{ + return m_connector; +} + +void ManagedLayerViewInh::p_addLayer(ManagedLayer * layer) +{ + m_layers.push_back( layer); +} + +}} diff --git a/carta/cpp/core/Hacks/ManagedLayerView.h b/carta/cpp/core/Hacks/ManagedLayerView.h new file mode 100644 index 00000000..c1d757ba --- /dev/null +++ b/carta/cpp/core/Hacks/ManagedLayerView.h @@ -0,0 +1,108 @@ +/** + * These classes illustrate how to wrap the basic functionality of IRemoteVGView to create + * higher-level features. + * + **/ + +#pragma once + +#include "CartaLib/CartaLib.h" +#include "IConnector.h" +#include "CartaLib/IRemoteVGView.h" +#include + +namespace Carta +{ +namespace Hacks +{ +class ManagedLayerViewInh; + +class ManagedLayer : public QObject +{ + Q_OBJECT + +public: + + ManagedLayer( ManagedLayerViewInh * ); + virtual + ~ManagedLayer() { } + +private: + + ManagedLayerViewInh * m_mlv = nullptr; + +}; + +class ManagedLayerViewInh : public QObject +{ + Q_OBJECT + +public: + + ManagedLayerViewInh( QString viewName, IConnector * connector, QObject * parent = nullptr); + + +// template < class ILayer, typename ... Args > +// std::shared_ptr < ILayer > +// create( Args && ... args ) +// { +// std::shared_ptr < ILayer > layer = +// std::make_shared < ILayer > ( std::forward < Args > ( args ) ... ); +// return layer; +// } + + virtual + ~ManagedLayerViewInh() { } + + IConnector * connector(); + +private: + + virtual void p_addLayer( ManagedLayer * layer); + + friend class ManagedLayer; + + std::vector< ManagedLayer * > m_layers; + IConnector * m_connector = nullptr; + + Carta::Lib::IRemoteVGView::UniquePtr m_rvgv = nullptr; + +}; + +class EyesLayer : public ManagedLayer +{ + Q_OBJECT +public: + + EyesLayer( ManagedLayerViewInh * mlv ); +}; + +//std::shared_ptr < ManagedLayerViewInh > +//createManagedLayerView( IConnector * connector, QString viewName ) +//{ +// return std::make_shared < ManagedLayerViewInh > (); +//} + +/* +static int +apiTest() +{ + IConnector * connector = nullptr; + + // create a new view + ManagedLayerViewInh * mlv = new ManagedLayerViewInh( "mlv1", connector, nullptr); + + // create a layer in this view + auto layer1 = new EyesLayer( mlv ); + + + Q_UNUSED( layer1); + return 0; +} + +static auto foo = apiTest(); + +*/ + +} +} diff --git a/carta/cpp/core/IConnector.h b/carta/cpp/core/IConnector.h index fc28f518..88042fd6 100644 --- a/carta/cpp/core/IConnector.h +++ b/carta/cpp/core/IConnector.h @@ -2,6 +2,7 @@ #define ICONNECTOR_H #include "IView.h" +//#include "CartaLib/IRemoteVGView.h" #include #include @@ -10,6 +11,12 @@ #include #include +namespace Carta { +namespace Lib { +class IRemoteVGView; +} +} + /** * @brief The IConnector interface offers common API that the C++ code uses * to communicate with the JavaScript side. Different implementations are used @@ -74,6 +81,10 @@ class IConnector { /// between C++ and JavaScript virtual QString getStateLocation( const QString& saveName ) const = 0; + /// create a vector graphics view +// virtual Carta::Lib::IRemoteVGView::SharedPtr + virtual Carta::Lib::IRemoteVGView * + makeRemoteVGView( QString viewName) = 0; virtual ~IConnector() {} }; diff --git a/carta/cpp/core/SimpleRemoteVGView.cpp b/carta/cpp/core/SimpleRemoteVGView.cpp new file mode 100644 index 00000000..eeebfa3a --- /dev/null +++ b/carta/cpp/core/SimpleRemoteVGView.cpp @@ -0,0 +1,116 @@ +/** + * + **/ + +#include "SimpleRemoteVGView.h" +#include "IConnector.h" +#include + +namespace Carta +{ +namespace Core +{ +const QSize & +SimpleRemoteVGView::getClientSize() +{ + return m_remoteSize; +} + +const QString & +SimpleRemoteVGView::getRVGViewName() +{ + return m_viewName; +} + +//void +//SimpleRemoteVGView::setRaster( const QColor & color ) +//{ +// m_raster = QImage(); +// m_bgColor = color; +//} + +void +SimpleRemoteVGView::setRaster( const QImage & image ) +{ + m_raster = image; +} + +void +SimpleRemoteVGView::setVG( const Lib::IRemoteVGView::VGList & vglist ) +{ + m_vgList = vglist; +} + +qint64 +SimpleRemoteVGView::scheduleRepaint( qint64 id ) +{ + m_buffer = m_raster; + QPainter painter( & m_buffer); + Carta::Lib::VectorGraphics::VGListQPainterRenderer renderer; + renderer.render( m_vgList, painter); + painter.end(); + + if ( id == - 1 ) { + id = m_lastRepaintId++; + } + m_lastRepaintId = id; + m_connector-> refreshView( this); + return id; +} + +SimpleRemoteVGView::SimpleRemoteVGView( QObject * parent, + QString viewName, + IConnector * connector ) + : IRemoteVGView( parent ) +{ + m_viewName = viewName; + m_connector = connector; + + m_raster = QImage( 100, 100, QImage::Format_ARGB32_Premultiplied ); + m_raster.fill( 0xff000000 ); + + m_connector-> registerView( this ); +} + +void +SimpleRemoteVGView::registration( IConnector * connector ) +{ + CARTA_ASSERT( connector == m_connector ); + Q_UNUSED(connector); // get rid of warning in release build + qDebug() << "VGView " << m_viewName << "registered"; +} + +const QString & +SimpleRemoteVGView::name() const +{ + return m_viewName; +} + +QSize +SimpleRemoteVGView::size() +{ + return m_buffer.size(); +} + +const QImage & +SimpleRemoteVGView::getBuffer() +{ + return m_buffer; +} + +void +SimpleRemoteVGView::handleResizeRequest( const QSize & size ) +{ + m_remoteSize = size; + emit sizeChanged(); +} + +void +SimpleRemoteVGView::handleMouseEvent( const QMouseEvent & /*event*/ ) +{ } + +void +SimpleRemoteVGView::handleKeyEvent( const QKeyEvent & /*event*/ ) +{ } +} +} diff --git a/carta/cpp/core/SimpleRemoteVGView.h b/carta/cpp/core/SimpleRemoteVGView.h new file mode 100644 index 00000000..bc57cb9d --- /dev/null +++ b/carta/cpp/core/SimpleRemoteVGView.h @@ -0,0 +1,91 @@ +/** + * Basic implementation of IRemoteVGView api. This class will be probably removed in the future, + * once we decide to specialize it for desktop/server environments. + **/ + +#pragma once + +#include "CartaLib/IRemoteVGView.h" +#include "IView.h" +#include +#include +#include +#include + +class ServerConnector; +class DesktopConnector; +class IConnector; + +namespace Carta +{ +namespace Core +{ +class SimpleRemoteVGView + : public Carta::Lib::IRemoteVGView + , public IView +{ + Q_OBJECT + CLASS_BOILERPLATE( SimpleRemoteVGView ); + +public: + + virtual const QSize & + getClientSize() override; + + virtual const QString & + getRVGViewName() override; + +// virtual void +// setRaster( const QColor & color ) override; + + virtual void + setRaster( const QImage & image ) override; + + virtual void + setVG( const VGList & vglist ) override; + +public slots: + + virtual qint64 + scheduleRepaint( qint64 id = - 1 ) override; + +private: + + friend class ::DesktopConnector; + friend class ::ServerConnector; + + SimpleRemoteVGView( QObject * parent, QString viewName, IConnector * connector ); + + QSize m_remoteSize = QSize( 1, 1 ); + QString m_viewName; + IConnector * m_connector = nullptr; + QImage m_raster, m_buffer; + + VGList m_vgList; + qint64 m_lastRepaintId = - 1; + + // IView interface + + virtual void + registration( IConnector * connector ) override; + + virtual const QString & + name() const override; + + virtual QSize + size() override; + + virtual const QImage & + getBuffer() override; + + virtual void + handleResizeRequest( const QSize & size ) override; + + virtual void + handleMouseEvent( const QMouseEvent & event ) override; + + virtual void + handleKeyEvent( const QKeyEvent & event ) override; +}; +} +} diff --git a/carta/cpp/core/VGView.h b/carta/cpp/core/VGView.h index dd35c53b..3d4054f8 100644 --- a/carta/cpp/core/VGView.h +++ b/carta/cpp/core/VGView.h @@ -1,5 +1,5 @@ /** - * + * Everyting here is experimental code, not used anywhere yet. **/ #pragma once @@ -9,6 +9,7 @@ class QString; #include "IView.h" #include "CartaLib/VectorGraphics/VGList.h" +#include "CartaLib/IRemoteVGView.h" #include namespace Carta @@ -102,127 +103,6 @@ private slots: }; -/// -/// \brief The RemoteView class is an API specification for interacting with -/// graphical views displayed on client(s) -/// -/// It offers the basic building block of rendering raster and/or vector graphics -/// on the client. The vector graphics is always layed over the raster graphics. -/// -class IRemoteView - : public QObject -{ - Q_OBJECT - - -public: - - typedef Carta::Lib::VectorGraphics::VGList VGList; - - /// returns size of the view in the master's UI - virtual const QSize & - size() = 0; - - /// returns the unique name of this view - virtual QString - name() = 0; - - /// sets the raster to be rendered in the view - virtual void - setRaster( const QColor & color ) = 0; - - virtual void - setRaster( const QImage & image ) = 0; - - /// sets the VG to be rendered on top of the raster - virtual void - setVG( const VGList & vglist ) = 0; - -public slots: - - /// schedule a refresh with a given ID - /// this allows the caller to be notified when the latest painting has reached the client - virtual qint64 - scheduleRepaint( qint64 id = - 1 ) = 0; - -signals: - - /// this signal is emitted when the master UI changes the size of the view - void - sizeChanged(); - - /// emitted when client repainted the view - void - repainted( qint64 id ); -}; - -std::shared_ptr createRemoteView ( QString viewName); - -class IQImageCombiner { -public: - virtual void combine( QImage & src1dst, const QImage & src2) = 0; -}; - -class AlphaCombiner : public IQImageCombiner { - -public: - void setAlpha( double alpha) { - CARTA_ASSERT( alpha >= 0.0 && alpha <= 1.0); - m_alpha = alpha; - } - - virtual void combine(QImage & src1dst, const QImage & src2) override - { - QPainter p( & src1dst); - p.setOpacity( m_alpha); - p.drawImage( 0, 0, src2); - } -private: - double m_alpha = 1.0; -}; - -class PixelMaskCombiner : public IQImageCombiner { -public: - void setMask( quint32 mask = DefaultMask) { - m_mask = mask; - } - void setAlpha( double alpha = 1.0) { - CARTA_ASSERT( alpha >= 0.0 && alpha <= 1.0); - m_alpha = alpha; - } - virtual void combine(QImage & src1dst, const QImage & src2) override - { - // if the mask is default, we do simple alpha composition - if( m_mask == DefaultMask) { - QPainter p( & src1dst); - p.setOpacity( m_alpha); - p.drawImage( 0, 0, src2); - return; - } - // otherwise we need to - QImage src22 = src2; - if( src22.format() != QImage::Format_ARGB32) { - src22 = src22.convertToFormat( QImage::Format_ARGB32); - } - for( int y = 0 ; y < src2.height() ; y ++) { - unsigned char * chr = src22.scanLine( y); - QRgb * ptr = (QRgb *) (chr); - for( int x = 0 ; x < src22.width() ; x ++) { - * ptr = ( * ptr) & m_mask; - ptr ++; - } - } - QPainter p( & src1dst); - p.setOpacity( m_alpha); - p.drawImage( 0, 0, src22); - } - - static constexpr quint32 DefaultMask = 0xffffffff; - -private: - quint32 m_mask = DefaultMask; - double m_alpha = 1.0; -}; } } diff --git a/carta/cpp/core/core.pro b/carta/cpp/core/core.pro index c26a8c01..b019d3f4 100755 --- a/carta/cpp/core/core.pro +++ b/carta/cpp/core/core.pro @@ -95,7 +95,9 @@ HEADERS += \ Hacks/ContourEditorController.h \ VGView.h \ DummyGridRenderer.h \ - coreMain.h + coreMain.h \ + SimpleRemoteVGView.h \ + Hacks/ManagedLayerView.h SOURCES += \ Viewer.cpp \ @@ -175,7 +177,9 @@ SOURCES += \ Hacks/ContourEditorController.cpp \ VGView.cpp \ DummyGridRenderer.cpp \ - coreMain.cpp + coreMain.cpp \ + SimpleRemoteVGView.cpp \ + Hacks/ManagedLayerView.cpp #message( "common PWD=$$PWD") diff --git a/carta/cpp/cpp.pro b/carta/cpp/cpp.pro index 05dd7d1b..356ba6bc 100644 --- a/carta/cpp/cpp.pro +++ b/carta/cpp/cpp.pro @@ -15,7 +15,7 @@ isEmpty(NOSERVER) { } # explicit dependencies, to make sure parallel make works (i.e. make -j4...) -common.depends = CartaLib +core.depends = CartaLib desktop.depends = core server.depends = core plugins.depends = core diff --git a/carta/cpp/desktop/DesktopConnector.cpp b/carta/cpp/desktop/DesktopConnector.cpp index 6073e8e0..065d3581 100644 --- a/carta/cpp/desktop/DesktopConnector.cpp +++ b/carta/cpp/desktop/DesktopConnector.cpp @@ -5,6 +5,7 @@ #include "DesktopConnector.h" #include "CartaLib/LinearMap.h" #include "core/MyQApp.h" +#include "core/SimpleRemoteVGView.h" #include #include #include @@ -182,6 +183,12 @@ void DesktopConnector::removeStateCallback(const IConnector::CallbackID & /*id*/ qFatal( "not implemented"); } + +Carta::Lib::IRemoteVGView * DesktopConnector::makeRemoteVGView(QString viewName) +{ + return new Carta::Core::SimpleRemoteVGView( this, viewName, this); +} + void DesktopConnector::jsSetStateSlot(const QString & key, const QString & value) { // it's ok to call setState directly, because callbacks will be invoked // from there asynchronously @@ -209,7 +216,7 @@ void DesktopConnector::jsSendCommandSlot(const QString &cmd, const QString & par emit jsCommandResultsSignal( results.join("|")); if( allCallbacks.size() == 0) { - qWarning() << "JS command has no server listener:" << cmd; + qWarning() << "JS command has no server listener:" << cmd << parameter; } }); } diff --git a/carta/cpp/desktop/DesktopConnector.h b/carta/cpp/desktop/DesktopConnector.h index fca863df..840668a3 100644 --- a/carta/cpp/desktop/DesktopConnector.h +++ b/carta/cpp/desktop/DesktopConnector.h @@ -9,6 +9,7 @@ #include #include "core/IConnector.h" #include "core/CallbackList.h" +#include "CartaLib/IRemoteVGView.h" class MainWindow; class IView; @@ -35,6 +36,8 @@ class DesktopConnector : public QObject, public IConnector void unregisterView( const QString& viewName ) override; virtual void refreshView(IView *view) override; virtual void removeStateCallback( const CallbackID & id) override; + virtual Carta::Lib::IRemoteVGView * + makeRemoteVGView( QString viewName) override; /// Return the location where the state is saved. virtual QString getStateLocation( const QString& saveName ) const; diff --git a/carta/cpp/desktop/resources.qrc b/carta/cpp/desktop/resources.qrc index 1db887fe..09c6947b 100644 --- a/carta/cpp/desktop/resources.qrc +++ b/carta/cpp/desktop/resources.qrc @@ -1,12 +1,5 @@ - html5/desktop/desktopConnector.js - html5/desktop/desktopIndex.html - html5/common/libs.js - html5/common/stuff.js icons/inspector.png - html5/common/main.js - html5/common/CallbackList.js - html5/common/skel/build/script/skel.js diff --git a/carta/cpp/server/ServerConnector.cpp b/carta/cpp/server/ServerConnector.cpp index 686b9679..56e6e65c 100644 --- a/carta/cpp/server/ServerConnector.cpp +++ b/carta/cpp/server/ServerConnector.cpp @@ -2,6 +2,7 @@ #include "core/MyQApp.h" #include "core/Globals.h" #include "CartaLib/Hooks/GetInitialFileList.h" +#include "core/SimpleRemoteVGView.h" #include #include @@ -9,6 +10,7 @@ #include #include + ServerConnector::ServerConnector() : QObject( nullptr) { @@ -314,16 +316,10 @@ void ServerConnector::removeStateCallback(const IConnector::CallbackID & /*id*/) const QStringList & ServerConnector::initialFileList() { return m_initialFileList; +} -// m_initialFileList.clear(); -// auto it = m_urlParams.find( "file"); -// if( it != m_urlParams.end()) { -// m_initialFileList << ( * it).second; -// } - - - -// m_initialFileList = { "/scratch/Images/sky.jpg" }; -// return m_initialFileList; +Carta::Lib::IRemoteVGView * ServerConnector::makeRemoteVGView(QString viewName) +{ + return new Carta::Core::SimpleRemoteVGView( this, viewName, this); } diff --git a/carta/cpp/server/ServerConnector.h b/carta/cpp/server/ServerConnector.h index c9f4f894..15a9e9f9 100644 --- a/carta/cpp/server/ServerConnector.h +++ b/carta/cpp/server/ServerConnector.h @@ -1,5 +1,6 @@ #pragma once +#include "CartaLib/IRemoteVGView.h" #include "core/IConnector.h" #include "CSI/Standard/CsiStandard.h" #include "CSI/Standard/CsiThreading.h" @@ -11,7 +12,6 @@ #include #include - class QtMessageTickler : public QObject , public CSI::PureWeb::Server::IResponseProvider @@ -109,6 +109,8 @@ class ServerConnector : public QObject, public IConnector /// this only works after initialize() was called // const std::map< QString, QString> & urlParams(); + virtual Carta::Lib::IRemoteVGView * + makeRemoteVGView( QString viewName) override; protected: @@ -132,7 +134,6 @@ class ServerConnector : public QObject, public IConnector std::map m_commandCallbackMap; typedef std::vector StateCBList; std::map m_stateCallbackList; -// bool m_startServer; // set to tell us whether a pureweb statechange callback is registered or not std::unordered_set< std::string > m_pwStateCBset; diff --git a/carta/html5/common/skel/config.json b/carta/html5/common/skel/config.json index 12baf469..51ede2ef 100644 --- a/carta/html5/common/skel/config.json +++ b/carta/html5/common/skel/config.json @@ -1,152 +1,109 @@ { - "name" : "skel", - - "include" : - [ - { - "path" : "${QOOXDOO_PATH}/tool/data/config/application.json" - } - ], - - "export" : - [ - "api", - "api-data", - "build", - "clean", - "distclean", - "dependencies", - "fix", - "info", - "inspector", - "lint", - "migration", - "pretty", - "profiling", - "source", - "source-all", - "source-hybrid", - "source-server", - "source-server-reload", - "source-httpd-config", - "simulation-build", - "simulation-run", - "test", - "test-source", - "translation", - "validate-config", - "validate-manifest", - "watch", - "all" - ], - - "default-job" : "all", - - "let" : - { - "APPLICATION" : "skel", - "QOOXDOO_PATH" : "../qooxdoo-3.5-sdk", -// "QOOXDOO_PATH" : "../qooxdoo-4.0.1-sdk", - "QXTHEME" : "skel.theme.Theme", - "API_EXCLUDE" : ["qx.test.*", "${APPLICATION}.theme.*", "${APPLICATION}.test.*", "${APPLICATION}.simulation.*"], - "LOCALES" : [ "en" ], - "CACHE" : "${TMPDIR}/qx${QOOXDOO_VERSION}/cache", - "ROOT" : "." - }, - - // You only need to edit the remainder of this file, if you want to customize - // specific jobs, or add own job definitions. - - "jobs" : - { - // the default job runs multiple jobs - "all" : - { - "run" : - [ - "source-all", - "build", - "api" - ] - } - - , "source-script": { - "compile-options": { - "uris": { - "add-nocache-param": true - }, - - "paths": { - "app-root": "../.." - - }, - - "code": { - "decode-uris-plug": "customuris.js" - } - - } - } - - , "source-all-script": { - "compile-options": { - "uris": { - "add-nocache-param": true - }, - - "paths": { - "app-root": "../.." - - }, - - "code": { - "decode-uris-plug": "customuris.js" - } - - } - - , "build-script": { - "compile-options": { - "code": { - "format": false - } - } - } - - } - - - - // Uncomment the following entry to add a contrib or library to your - // project; make sure to adapt the path to the Manifest.json; if you are - // using a contrib: library, it will be downloaded into the path specified - // by the 'cache/downloads' config key - /* - "libraries" : - { - "library" : - [ - { - "manifest" : "contrib://SkeletonApplication/trunk/Manifest.json" - } - ] - } - */ - - // If you want to tweak a job setting, see the following sample where - // the "format" feature of the "build-script" job is overridden. - // To see a list of available jobs, invoke 'generate.py x'. - /* - ,"build-script" : - { - "compile-options" : - { - "code" : - { - "format" : false - } - } - } - */ - } + "name" : "skel", + + "include" : + [ + { + "path" : "${QOOXDOO_PATH}/tool/data/config/application.json" + } + ], + + "export" : + [ + "api", + "api-data", + "build", + "clean", + "distclean", + "dependencies", + "fix", + "info", + "inspector", + "lint", + "migration", + "pretty", + "profiling", + "source", + "source-all", + "source-hybrid", + "source-server", + "source-server-reload", + "source-httpd-config", + "simulation-build", + "simulation-run", + "test", + "test-source", + "translation", + "validate-config", + "validate-manifest", + "watch", + "all" + ], + + "default-job" : "all", + + "let" : + { + "APPLICATION" : "skel", + "QOOXDOO_PATH" : "../qooxdoo-3.5-sdk", + // "QOOXDOO_PATH" : "../qooxdoo-4.0.1-sdk", + "QXTHEME" : "skel.theme.Theme", + "API_EXCLUDE" : ["qx.test.*", "${APPLICATION}.theme.*", "${APPLICATION}.test.*", "${APPLICATION}.simulation.*"], + "LOCALES" : [ "en" ], + "CACHE" : "${TMPDIR}/qx${QOOXDOO_VERSION}/cache", + "ROOT" : "." + }, + + // You only need to edit the remainder of this file, if you want to customize + // specific jobs, or add own job definitions. + + "jobs" : { + // the default job runs multiple jobs + "all" : { + "run" : + [ + "source-all", + "build", + "api" + ] + } + + , "source-script": { + "compile-options": { + "uris": { + "add-nocache-param": true + }, + "paths": { + "app-root": "../.." + + }, + "code": { + "decode-uris-plug": "customuris.js" + } + } + } + , "source-all-script": { + "compile-options": { + "uris": { + "add-nocache-param": true + }, + "paths": { + "app-root": "../.." + }, + "code": { + "decode-uris-plug": "customuris.js" + } + } + }, + +/* + , "build-script": { + "compile-options": { + "code": { + "format": false + } + } + } +*/ + } } diff --git a/carta/html5/common/skel/source/class/skel/boundWidgets/View/SuffixedView.js b/carta/html5/common/skel/source/class/skel/boundWidgets/View/SuffixedView.js deleted file mode 100644 index 1516d758..00000000 --- a/carta/html5/common/skel/source/class/skel/boundWidgets/View/SuffixedView.js +++ /dev/null @@ -1,33 +0,0 @@ -/* *************************************************************************************** - * Created by pfederl on Feb 9, 2015 - * - * ***************************************************************************************/ - -/* global qx, mImport */ -/* jshint strict: false */ - -/** - * - * This is a simple extension of the raw skel.boundWidgets.View.View, where we append a suffix - * to the end of the view name. The suffix is usually '/view', but it is determined - * by calling an external singleton, i.e.: - * - * suffix = skel.widgets.Path.getInstance().SEP + skel.widgets.Path.getInstance().VIEW - * - */ -qx.Class.define( "skel.boundWidgets.View.SuffixedView", { - - extend: skel.boundWidgets.View.View, - - /** - * @param partialViewName {String} partial name of the view. - */ - construct: function( partialViewName ) - { - var path = skel.widgets.Path.getInstance(); - var suffix = path.SEP + path.VIEW; // e.g. "/view" - - this.base( arguments, partialViewName + suffix ); - } - -} ); diff --git a/carta/html5/common/skel/source/class/skel/boundWidgets/View/VGView.js b/carta/html5/common/skel/source/class/skel/boundWidgets/View/VGView.js new file mode 100644 index 00000000..3b08ad57 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/boundWidgets/View/VGView.js @@ -0,0 +1,46 @@ +/** + * Created by pfederl. + * + * New view widget capable of displaying vector graphics. + */ + +qx.Class.define( "skel.boundWidgets.View.VGView", { + + extend: qx.ui.container.Composite, + + /** + * Constructor + */ + construct: function( viewName) { + this.base( arguments); + this.setLayout( new qx.ui.layout.Grow()); + this.m_viewWidget = new skel.boundWidgets.View.View( viewName ); + this.add( this.m_viewWidget); + this.m_overlayWidget = new qx.ui.core.Widget(); + this.add( this.m_overlayWidget); + // this.m_overlayWidget.setBackgroundColor( "rgba(255,0,0,0.2)"); + }, + + members: { + + m_overlayWidget: null, + m_viewWidget: null, + + /** + * Get the overlay widget + */ + overlayWidget: function() + { + return this.m_overlayWidget; + }, + + /** + * Get the view widget + */ + viewWidget: function() + { + return this.m_viewWidget; + } + } + +}); diff --git a/carta/html5/common/skel/source/class/skel/hacks/HackView.js b/carta/html5/common/skel/source/class/skel/hacks/HackView.js index e32164dd..a757f8e6 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/HackView.js +++ b/carta/html5/common/skel/source/class/skel/hacks/HackView.js @@ -20,9 +20,9 @@ qx.Class.define( "skel.hacks.HackView", { this.base( arguments, viewId ); // monitor mouse move - this.addListener( "mousemove", this._mouseMoveCB.bind(this)); - this.addListener( "mousewheel", this._mouseWheelCB.bind(this)); - this.addListener( "click", this._mouseClickCB.bind(this)); + this.overlayWidget().addListener( "mousemove", this._mouseMoveCB.bind(this)); + this.overlayWidget().addListener( "mousewheel", this._mouseWheelCB.bind(this)); + this.overlayWidget().addListener( "click", this._mouseClickCB.bind(this)); this.m_viewId = viewId; this.m_connector = mImport( "connector"); diff --git a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js index 3952bbdc..cd956c65 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js +++ b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js @@ -85,20 +85,37 @@ qx.Class.define("skel.hacks.Hacks", { mp.gridTB = new skel.boundWidgets.Toggle( "Grid", mp.prefix + "/gridToggle"); mp.container.add( mp.gridTB); win2.add( mp.container); - - // add a contour box - //var cbox = new qx.ui.container.Composite( new qx.ui.layout.HBox(5)); - //cbox.getLayout().set({ alignY: "middle"}); - //cbox.add( new qx.ui.basic.Label( "Contours:")); - //var contoursTF = new skel.boundWidgets.TextField( mp.prefix + "/contourLevels"); - //contoursTF.setToolTipText( "Enter contour levels here, separated by spaces or commas"); - //cbox.add( contoursTF, { flex: 1}); - //win2.add(cbox); - - // pop up the window this.m_app.getRoot().add( win2, {left: 100, top: 100} ); win2.open(); + // ================================================================================== + // VGview hack window + // ================================================================================== + var vgWin = new qx.ui.window.Window( "VGhack" ); + vgWin.setWidth( 600 ); + vgWin.setHeight( 400 ); + vgWin.setShowMinimize( false ); + vgWin.setUseResizeFrame( false); + vgWin.setContentPadding( 5, 5, 5, 5 ); + vgWin.setLayout( new qx.ui.layout.VBox(5) ); + //vgWin.add( new skel.hacks.HackView( "vgview1"), { flex: 1 }); + //vgWin.add( new skel.boundWidgets.View.View( "vgview1"), { flex: 1 }); + vgWin.add( new skel.hacks.LayeredViewHack( "vgview1"), { flex: 1 }); + this.m_app.getRoot().add( vgWin, {left: 150, top: 120} ); + vgWin.open(); + + var vgWin2 = new qx.ui.window.Window( "VGhack2" ); + vgWin2.setWidth( 600 ); + vgWin2.setHeight( 400 ); + vgWin2.setShowMinimize( false ); + vgWin2.setUseResizeFrame( false); + vgWin2.setContentPadding( 5, 5, 5, 5 ); + vgWin2.setLayout( new qx.ui.layout.VBox(5) ); + vgWin2.add( new skel.hacks.LayeredViewHack( "vgview2"), { flex: 1 }); + this.m_app.getRoot().add( vgWin2, {left: 170, top: 140} ); + vgWin2.open(); + + // create cursor window this.m_cursorWindow = new skel.boundWidgets.CursorWindow(); diff --git a/carta/html5/common/skel/source/class/skel/hacks/LayeredViewHack.js b/carta/html5/common/skel/source/class/skel/hacks/LayeredViewHack.js new file mode 100644 index 00000000..a045c2c7 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/hacks/LayeredViewHack.js @@ -0,0 +1,49 @@ +/** + * @ignore( mImport) + */ + +/*global mImport,qx */ + +qx.Class.define( "skel.hacks.LayeredViewHack", { + extend: qx.ui.container.Composite, + + construct: function( viewName) { + this.base( arguments); + this.m_viewName = viewName; + + this.m_connector = mImport( "connector"); + + this.setLayout( new qx.ui.layout.VBox( 3)); + this.m_vgview = new skel.boundWidgets.View.View( this.m_viewName); + this.add( this.m_vgview, { flex: 1 }); + + this.m_status = new qx.ui.basic.Label( "status..."); + this.add( this.m_status); + //this.m_tf = new skel.boundWidgets.TextField( "/hacks/layercommands/" + this.m_viewName); + this.m_tf = new qx.ui.form.TextField(); + this.m_tf.addListener( "changeValue", this._tfChangeValueCB.bind( this ) ); + + + this.add( this.m_tf); + + this.setMinWidth( 100); + this.setMinHeight( 100); + }, + + members: { + + _tfChangeValueCB : function( e){ + var txt = e.getData(); + this.m_connector.sendCommand( "lvgview-cmd", txt, this._commandCB.bind(this)); + }, + + _commandCB : function( txt) { + //console.log( "commandCB", arguments); + this.m_status.setValue( "Status: " + txt); + }, + + m_connector: null, + m_viewName: "" + } + +}); \ No newline at end of file From cba3578110fdeb85f9aa3460291575a73cabce1a Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Wed, 28 Oct 2015 22:29:48 -0600 Subject: [PATCH 17/37] views are now able to let the server side know when they're refreshed this is especially handy for trying to play movies at full speed without skipping any frames... --- carta/cpp/CartaLib/IRemoteVGView.cpp | 56 ++-- carta/cpp/CartaLib/IRemoteVGView.h | 6 +- carta/cpp/core/Hacks/HackViewer.cpp | 50 ++-- carta/cpp/core/Hacks/ImageViewController.cpp | 20 +- carta/cpp/core/Hacks/ImageViewController.h | 4 + carta/cpp/core/IConnector.h | 5 +- carta/cpp/core/IView.h | 5 + carta/cpp/core/ImageView.h | 1 + carta/cpp/core/SimpleRemoteVGView.cpp | 5 + carta/cpp/core/SimpleRemoteVGView.h | 4 + carta/cpp/core/VGView.cpp | 80 ------ carta/cpp/core/VGView.h | 108 -------- carta/cpp/core/core.pro | 2 - carta/cpp/desktop/DesktopConnector.cpp | 30 +- carta/cpp/desktop/DesktopConnector.h | 6 +- carta/cpp/server/ServerConnector.cpp | 277 ++++++++++++------- carta/cpp/server/ServerConnector.h | 15 +- carta/html5/desktop/desktopConnector.js | 3 +- carta/html5/server/serverConnector.js | 4 +- 19 files changed, 311 insertions(+), 370 deletions(-) delete mode 100644 carta/cpp/core/VGView.cpp delete mode 100644 carta/cpp/core/VGView.h diff --git a/carta/cpp/CartaLib/IRemoteVGView.cpp b/carta/cpp/CartaLib/IRemoteVGView.cpp index 823c10d4..22cbff9d 100644 --- a/carta/cpp/CartaLib/IRemoteVGView.cpp +++ b/carta/cpp/CartaLib/IRemoteVGView.cpp @@ -104,17 +104,19 @@ LayeredRemoteVGView::LayeredRemoteVGView( IConnector * connector, m_vgView.reset( connector-> makeRemoteVGView( viewName )); /// forward signals directly - connect( m_vgView.get(), SIGNAL( sizeChanged() ), this, SIGNAL( sizeChanged() ) ); connect( m_vgView.get(), SIGNAL( repainted() ), this, SIGNAL( repainted() ) ); + // connect( m_vgView.get(), SIGNAL( sizeChanged() ), this, SIGNAL( sizeChanged() ) ); + connect( m_vgView.get(), & IRemoteVGView::sizeChanged, this, & Me::p_sizeChangedCB); + m_timer = new QTimer( this ); m_timer-> setSingleShot( true ); m_timer-> setInterval( 1 ); - connect( m_timer, & QTimer::timeout, this, & Me::timerCB ); + connect( m_timer, & QTimer::timeout, this, & Me::p_timerCB ); } void -LayeredRemoteVGView::timerCB() +LayeredRemoteVGView::p_timerCB() { // figure out the size of the buffer (max of the raster sizes) QSize size( 1, 1); @@ -125,29 +127,14 @@ LayeredRemoteVGView::timerCB() QImage buff( size, QImage::Format_ARGB32_Premultiplied ); buff.fill( QColor( 0, 0, 0, 255 ) ); - // combine all raster layers into buff using their respective compositors -// if ( m_rasterLayers.size() > 0 ) { -// // the background will be the same size as the first layer, but filled with black -// buff = m_rasterLayers[0].qimg; -// buff.fill( QColor( 0, 0, 0, 255 ) ); - - // now go through the layers and paint them on top of the last result - for ( auto & layer : m_rasterLayers ) { - IQImageCombiner::SharedPtr combiner = layer.combiner; - if ( ! combiner ) { - combiner = std::make_shared < DefaultCombiner > (); - } - combiner->combine( buff, layer.qimg ); + // now go through the layers and paint them on top of the last result + for ( auto & layer : m_rasterLayers ) { + IQImageCombiner::SharedPtr combiner = layer.combiner; + if ( ! combiner ) { + combiner = std::make_shared < DefaultCombiner > (); } - -// for ( size_t i = 0 ; i < m_rasterLayers.size() ; ++i ) { -// IQImageCombiner::SharedPtr combiner = m_rasterLayers[i].combiner; -// if ( ! combiner ) { -// combiner = std::make_shared < DefaultCombiner > (); -// } -// combiner->combine( buff, m_rasterLayers[i].qimg ); -// } -// } + combiner->combine( buff, layer.qimg ); + } m_vgView-> setRaster( buff ); @@ -158,23 +145,16 @@ LayeredRemoteVGView::timerCB() composer.appendList( vglayer.vglist ); } + // render the combined vector graphics list m_vgView-> setVG( composer.vgList()); - // now we render all VG layers on top of buff -// for ( auto & vglayer : m_vgLayers ) { -// VectorGraphics::VGListQPainterRenderer renderer; -// QPainter painter( & buff ); -// renderer.render( vglayer.vglist, painter ); -// } - - // render all layers on top of each other -// QColor color( qrand() % 255, qrand() % 255, qrand() % 255 ); -// QImage img( 100, 100, QImage::Format_ARGB32_Premultiplied); -// img.fill( color); -// m_vgView-> setRaster( buff ); - // schedule repaint (void) m_vgView-> scheduleRepaint( m_repaintId ); +} + +void LayeredRemoteVGView::p_sizeChangedCB() +{ + emit sizeChanged(); } // timerCB } } diff --git a/carta/cpp/CartaLib/IRemoteVGView.h b/carta/cpp/CartaLib/IRemoteVGView.h index 6464cc34..14b4b614 100644 --- a/carta/cpp/CartaLib/IRemoteVGView.h +++ b/carta/cpp/CartaLib/IRemoteVGView.h @@ -184,10 +184,14 @@ public slots: qint64 m_repaintId = - 1; QTimer * m_timer = nullptr; + private slots: void - timerCB(); + p_timerCB(); + + void p_sizeChangedCB(); + }; /// paints one raster over the other one, overwriting the bottom one diff --git a/carta/cpp/core/Hacks/HackViewer.cpp b/carta/cpp/core/Hacks/HackViewer.cpp index eabaae47..e29f0391 100644 --- a/carta/cpp/core/Hacks/HackViewer.cpp +++ b/carta/cpp/core/Hacks/HackViewer.cpp @@ -166,7 +166,7 @@ HackViewer::start() prefixedSetState( "knownSkyCS/count", "5" ); // layered view stuff - static Carta::Lib::IRemoteVGView::SharedPtr vgview( m_connector-> makeRemoteVGView( "vgview1" )); + static Carta::Lib::IRemoteVGView::SharedPtr vgview( m_connector-> makeRemoteVGView( "vgview1" ) ); QImage img( 200, 100, QImage::Format_ARGB32_Premultiplied ); img.fill( 0xff000000 ); { @@ -196,17 +196,18 @@ HackViewer::start() if ( xxx > 1 ) { xxx = 0.0; } - comp.append < Carta::Lib::VectorGraphics::Entries::FillRect > ( QRectF( xxx * 10 + - 50, 55, 70, - 40 ), - QColor( 0, 0, 255, - 128 ) ); + using Carta::Lib::VectorGraphics::Entries::FillRect; + comp.append < FillRect > ( QRectF( xxx * 10 + + 50, 55, 70, + 40 ), + QColor( 0, 0, 255, + 128 ) ); vgview-> setVG( comp.vgList() ); vgview-> scheduleRepaint(); } ); timer->setInterval( 100 ); - timer->start(); +// timer->start(); // layered view 2 stuff auto ccl = [] ( int x, int y, int r, QColor color ) -> QImage { @@ -248,7 +249,7 @@ HackViewer::start() // 0 alpha 0.5 // 0 mask 0xff0000 0.5 auto cmdCB = - [&] ( const QString & cmd, const QString & params, const QString & sid ) -> QString { + [&] ( const QString & cmd, const QString & params, const QString & sid ) -> QString { qDebug() << "cmd" << cmd << params << sid; QStringList list = params.split( ' ', QString::SkipEmptyParts ); if ( list.size() < 2 ) { @@ -269,12 +270,12 @@ HackViewer::start() return "What do you want to load?"; } QString load = list[2]; - if ( load.startsWith( '/') && QFileInfo(load).exists() ) { + if ( load.startsWith( '/' ) && QFileInfo( load ).exists() ) { QImage img; - if( !img.load( load)) { + if ( ! img.load( load ) ) { return "Could not load image"; } - vgview2-> setRasterLayer( layer, img); + vgview2-> setRasterLayer( layer, img ); } else if ( load == "c1" ) { vgview2-> setRasterLayer( layer, ccl( 50, 50, 45, "white" ) ); @@ -290,32 +291,33 @@ HackViewer::start() if ( list.size() < 3 ) { return "No alpha value?"; } - double alpha = list[2].toDouble( & ok); - if( ! ok || ! (alpha >= 0 && alpha <= 1)) { + double alpha = list[2].toDouble( & ok ); + if ( ! ok || ! ( alpha >= 0 && alpha <= 1 ) ) { return "Bad alpha"; } - auto comp = std::make_shared(); - comp-> setAlpha( alpha); - vgview2-> setRasterLayerCombiner( layer, comp); + auto comp = std::make_shared < Carta::Lib::AlphaCombiner > (); + comp-> setAlpha( alpha ); + vgview2-> setRasterLayerCombiner( layer, comp ); } else if ( opcmd == "mask" ) { if ( list.size() < 4 ) { return "No mask and/or alpha value?"; } - quint32 mask = list[2].toInt( & ok, 16); - if( ! ok ) { + quint32 mask = list[2].toInt( & ok, 16 ); + if ( ! ok ) { return "Bad mask"; } mask |= 0xff000000; - double alpha = list[3].toDouble( & ok); - if( ! ok || ! (alpha >= 0 && alpha <= 1)) { + double alpha = list[3].toDouble( & ok ); + if ( ! ok || ! ( alpha >= 0 && alpha <= 1 ) ) { return "Bad alpha"; } - auto comp = std::make_shared(); - comp-> setAlpha( alpha); - comp-> setMask( mask); - vgview2-> setRasterLayerCombiner( layer, comp); + auto comp = std::make_shared < Carta::Lib::PixelMaskCombiner > (); + comp-> setAlpha( alpha ); + comp-> setMask( mask ); + vgview2-> setRasterLayerCombiner( layer, comp ); } + else if ( opcmd == "time" ) {} else { qWarning() << "Bad layer operation" << params; return "Bad layer operation"; diff --git a/carta/cpp/core/Hacks/ImageViewController.cpp b/carta/cpp/core/Hacks/ImageViewController.cpp index 67d513fd..f9d2b4d0 100644 --- a/carta/cpp/core/Hacks/ImageViewController.cpp +++ b/carta/cpp/core/Hacks/ImageViewController.cpp @@ -194,6 +194,11 @@ ImageViewController::ImageViewController( QString statePrefix, QString viewName, void ImageViewController::playMovieToggleCB() { + if( m_playToggle-> get()) { + loadNextFrame(); + } + return; + if ( m_playToggle-> get() ) { m_movieTimer.start( 1000 / 60 ); } @@ -363,10 +368,17 @@ ImageViewController::handleResizeRequest( const QSize & size ) requestImageAndGridUpdate(); } +void ImageViewController::viewRefreshed(qint64 id) { + qDebug() << "ImageViewController view" << name() << "refreshed" << id; + if( m_playToggle-> get()) { + loadNextFrame(); + } +} + void ImageViewController::loadImage( QString fname ) { -// qDebug() << "xyz ImageViewController::loadImage" << fname; + // qDebug() << "xyz ImageViewController::loadImage" << fname; m_fileName = fname; qDebug() << "loadImage() Trying to load astroImage..."; @@ -399,7 +411,7 @@ ImageViewController::loadImage( QString fname ) void ImageViewController::frameVarCB() { -// qDebug() << "frameVar" << m_frameVar-> get() << "xyz"; + qDebug() << "frameVar" << m_frameVar-> get(); if ( m_astroImage-> dims().size() < 2 ) { return; } @@ -415,6 +427,7 @@ ImageViewController::frameVarCB() // make sure frame integer is valid frame = Carta::Lib::clamp < int > ( frame, 0, nf - 1 ); + qDebug() << "current frame" << m_currentFrame << " frame=" << frame; // load the actual frame if ( frame != m_currentFrame ) { loadFrame( frame ); @@ -494,7 +507,8 @@ ImageViewController::loadNextFrame() int currFrame = std::round( m_frameVar-> get() * nf / 1e6 ); int nextFrame = ( currFrame + 1 ) % nf; - m_frameVar-> set( nextFrame * 1e6 / nf ); + qDebug() << "Setting next frame to" << nextFrame; + m_frameVar-> set( (nextFrame+ 0.1) * 1e6 / nf ); } // loadFrame void diff --git a/carta/cpp/core/Hacks/ImageViewController.h b/carta/cpp/core/Hacks/ImageViewController.h index 597e5495..78c161c0 100644 --- a/carta/cpp/core/Hacks/ImageViewController.h +++ b/carta/cpp/core/Hacks/ImageViewController.h @@ -226,6 +226,10 @@ class ImageViewController : public QObject, public IView virtual void handleKeyEvent( const QKeyEvent & ) override { } + /// IView interface + virtual void + viewRefreshed( qint64 id); + void setColormap( Carta::Lib::PixelPipeline::IColormapNamed::SharedPtr ); void setPPCsettings( PPCsettings settings ); diff --git a/carta/cpp/core/IConnector.h b/carta/cpp/core/IConnector.h index 88042fd6..a89e2fa2 100644 --- a/carta/cpp/core/IConnector.h +++ b/carta/cpp/core/IConnector.h @@ -2,7 +2,6 @@ #define ICONNECTOR_H #include "IView.h" -//#include "CartaLib/IRemoteVGView.h" #include #include @@ -53,7 +52,9 @@ class IConnector { virtual void registerView( IView * view) = 0; /// asks the connector to schedule a redraw of the view - virtual void refreshView( IView * view) = 0; + /// \param view which view to refresh + /// \return the id for the refresh (always increasing) + virtual qint64 refreshView( IView * view) = 0; /// unregister a view with the connector virtual void unregisterView( const QString& viewName ) = 0; diff --git a/carta/cpp/core/IView.h b/carta/cpp/core/IView.h index b52113f8..12c08c50 100644 --- a/carta/cpp/core/IView.h +++ b/carta/cpp/core/IView.h @@ -11,6 +11,8 @@ class QKeyEvent; class QString; class QSize; +#include + /** * @brief The IView interface defines the necessary functionality that a server side view * has to implement to create an object that can interact with the client side view. @@ -74,6 +76,9 @@ class IView virtual void handleKeyEvent( const QKeyEvent & event ) = 0; + /// this is called when a view is refreshed in the GUI + virtual void viewRefreshed( qint64 id) = 0; + virtual ~IView() { } }; diff --git a/carta/cpp/core/ImageView.h b/carta/cpp/core/ImageView.h index d2bb9c41..b5336ce8 100755 --- a/carta/cpp/core/ImageView.h +++ b/carta/cpp/core/ImageView.h @@ -30,6 +30,7 @@ Q_OBJECT virtual void handleResizeRequest(const QSize & size); virtual void handleMouseEvent(const QMouseEvent & ev); virtual void handleKeyEvent(const QKeyEvent & /*event*/) override {} + virtual void viewRefreshed( qint64 /*id*/) override {} static const QString MOUSE; static const QString MOUSE_X; static const QString MOUSE_Y; diff --git a/carta/cpp/core/SimpleRemoteVGView.cpp b/carta/cpp/core/SimpleRemoteVGView.cpp index eeebfa3a..232ca8dd 100644 --- a/carta/cpp/core/SimpleRemoteVGView.cpp +++ b/carta/cpp/core/SimpleRemoteVGView.cpp @@ -112,5 +112,10 @@ SimpleRemoteVGView::handleMouseEvent( const QMouseEvent & /*event*/ ) void SimpleRemoteVGView::handleKeyEvent( const QKeyEvent & /*event*/ ) { } + +void SimpleRemoteVGView::viewRefreshed(qint64 id) +{ + Q_UNUSED( id); +} } } diff --git a/carta/cpp/core/SimpleRemoteVGView.h b/carta/cpp/core/SimpleRemoteVGView.h index bc57cb9d..c7be31b5 100644 --- a/carta/cpp/core/SimpleRemoteVGView.h +++ b/carta/cpp/core/SimpleRemoteVGView.h @@ -86,6 +86,10 @@ public slots: virtual void handleKeyEvent( const QKeyEvent & event ) override; + + virtual void + viewRefreshed( qint64 id) override ; + }; } } diff --git a/carta/cpp/core/VGView.cpp b/carta/cpp/core/VGView.cpp deleted file mode 100644 index 382fc152..00000000 --- a/carta/cpp/core/VGView.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/** - * - **/ - -#include "VGView.h" -#include "IConnector.h" - -namespace Carta -{ -namespace Core -{ -VGView::VGView( QString viewName, IConnector * connector, QObject * parent ) : QObject( parent ) -{ - m_connector = connector; - m_viewName = viewName; - m_rasterImage = QImage(); - - m_connector->registerView( this ); -} - -void -VGView::setServerSideVGRendering( bool flag ) -{ - CARTA_ASSERT( flag == true ); - - m_serverSideVG = flag; -} - -qint64 -VGView::scheduleRepaint( qint64 id ) -{ - if ( id >= 0 ) { - m_id = id; - } - else { - m_id++; - } - - return m_id; -} - -void -VGView::registration( IConnector * connector ) -{ - CARTA_ASSERT( connector = m_connector ); - Q_UNUSED(connector); -} - -const QString & -VGView::name() const -{ - return m_viewName; -} - -QSize -VGView::size() -{ - /// @todo probably incorrect - return m_rasterImage.size(); -} - -const QImage & -VGView::getBuffer() -{ - return m_rasterImage; -} - -void -VGView::handleResizeRequest( const QSize & /*size*/ ) -{ } - -void -VGView::handleMouseEvent( const QMouseEvent & /*event*/ ) -{ } - -void -VGView::handleKeyEvent( const QKeyEvent & /*event*/ ) -{ } -} -} diff --git a/carta/cpp/core/VGView.h b/carta/cpp/core/VGView.h deleted file mode 100644 index 3d4054f8..00000000 --- a/carta/cpp/core/VGView.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Everyting here is experimental code, not used anywhere yet. - **/ - -#pragma once - -class IConnector; -class QString; - -#include "IView.h" -#include "CartaLib/VectorGraphics/VGList.h" -#include "CartaLib/IRemoteVGView.h" -#include - -namespace Carta -{ -namespace Core -{ -/// a view implementation that does not need subclassing, and is capable -/// of rendering vector graphics -class VGView : public QObject, protected IView -{ - Q_OBJECT - -public: - - VGView( QString viewName, - IConnector * connector, - QObject * parent = nullptr ); - - /// return current size - QSize - getSize(); - - /// get the name of the view - QString - getName(); - - /// what image to render - void - setImage( const QImage & ); - - /// what vg overlay to render - void - setVG( const Carta::Lib::VectorGraphics::VGList & ); - - /// whether to render VG on server - /// by default it's set to server side - void - setServerSideVGRendering( bool flag ); - - virtual - ~VGView() { } - -signals: - - /// emitted when the client resizes the view - void - resized( const QSize & ); - - /// emitted when client refreshed the view - void - refreshed( qint64 id ); - -public slots: - - /// schedule a refresh with a given ID - qint64 - scheduleRepaint( qint64 id = - 1 ); - -private slots: - -private: - - IConnector * m_connector = nullptr; - qint64 m_id = - 1; - QString m_viewName; - bool m_serverSideVG = true; - QImage m_rasterImage; - -private: - - virtual void - registration( IConnector * connector ) override; - - virtual const QString & - name() const override; - - virtual QSize - size() override; - - virtual const QImage & - getBuffer() override; - - virtual void - handleResizeRequest( const QSize & size ) override; - - virtual void - handleMouseEvent( const QMouseEvent & event ) override; - - virtual void - handleKeyEvent( const QKeyEvent & event ) override; -}; - - - -} -} diff --git a/carta/cpp/core/core.pro b/carta/cpp/core/core.pro index b019d3f4..729a075d 100755 --- a/carta/cpp/core/core.pro +++ b/carta/cpp/core/core.pro @@ -93,7 +93,6 @@ HEADERS += \ Hacks/WcsGridOptionsController.h \ Hacks/SharedState.h \ Hacks/ContourEditorController.h \ - VGView.h \ DummyGridRenderer.h \ coreMain.h \ SimpleRemoteVGView.h \ @@ -175,7 +174,6 @@ SOURCES += \ Hacks/WcsGridOptionsController.cpp \ Hacks/SharedState.cpp \ Hacks/ContourEditorController.cpp \ - VGView.cpp \ DummyGridRenderer.cpp \ coreMain.cpp \ SimpleRemoteVGView.cpp \ diff --git a/carta/cpp/desktop/DesktopConnector.cpp b/carta/cpp/desktop/DesktopConnector.cpp index 065d3581..0c60e51f 100644 --- a/carta/cpp/desktop/DesktopConnector.cpp +++ b/carta/cpp/desktop/DesktopConnector.cpp @@ -23,7 +23,8 @@ struct DesktopConnector::ViewInfo { - /// the implemented IView + /// pointer to user supplied IView + /// this is a NON-OWNING pointer IView * view; /// last received client size @@ -35,6 +36,9 @@ struct DesktopConnector::ViewInfo /// refresh timer for this object QTimer refreshTimer; + /// refresh ID + qint64 refreshId = -1; + ViewInfo( IView * pview ) { view = pview; @@ -141,6 +145,7 @@ void DesktopConnector::registerView(IView * view) // connect the view's refresh timer to a lambda, which will in turn call // refreshViewNow() + // this is instead of using std::bind... connect( & viewInfo->refreshTimer, & QTimer::timeout, [=] () { refreshViewNow( view); @@ -159,14 +164,14 @@ void DesktopConnector::unregisterView( const QString& viewName ){ // static QTime st; // schedule a view refresh -void DesktopConnector::refreshView(IView * view) +qint64 DesktopConnector::refreshView(IView * view) { // find the corresponding view info ViewInfo * viewInfo = findViewInfo( view-> name()); if( ! viewInfo) { // this is an internal error... qCritical() << "refreshView cannot find this view: " << view-> name(); - return; + return -1; } // start the timer for this view if it's not already started @@ -176,6 +181,9 @@ void DesktopConnector::refreshView(IView * view) else { // qDebug() << "########### saved refresh for " << view->name(); } + + viewInfo-> refreshId ++; + return viewInfo-> refreshId; } void DesktopConnector::removeStateCallback(const IConnector::CallbackID & /*id*/) @@ -279,13 +287,13 @@ void DesktopConnector::refreshViewNow(IView *view) viewInfo-> ty = Carta::Lib::LinearMap1D( yOffset, yOffset + destImage.size().height()-1, 0, origImage.height()-1); - emit jsViewUpdatedSignal( view-> name(), pix); + emit jsViewUpdatedSignal( view-> name(), pix, viewInfo-> refreshId); } else { viewInfo-> tx = Carta::Lib::LinearMap1D( 0, 1, 0, 1); viewInfo-> ty = Carta::Lib::LinearMap1D( 0, 1, 0, 1); - emit jsViewUpdatedSignal( view-> name(), origImage); + emit jsViewUpdatedSignal( view-> name(), origImage, viewInfo-> refreshId); } } @@ -306,6 +314,18 @@ void DesktopConnector::jsUpdateViewSlot(const QString & viewName, int width, int }); } +void DesktopConnector::jsViewRefreshedSlot(const QString & viewName, qint64 id) +{ + qDebug() << "jsViewRefreshedSlot()" << viewName << id; + ViewInfo * viewInfo = findViewInfo( viewName); + if( ! viewInfo) { + qCritical() << "Received refresh view signal for unknown view" << viewName; + return; + } + CARTA_ASSERT( viewInfo-> view); + viewInfo-> view-> viewRefreshed( id); +} + void DesktopConnector::jsMouseMoveSlot(const QString &viewName, int x, int y) { ViewInfo * viewInfo = findViewInfo( viewName); diff --git a/carta/cpp/desktop/DesktopConnector.h b/carta/cpp/desktop/DesktopConnector.h index 840668a3..71fffba1 100644 --- a/carta/cpp/desktop/DesktopConnector.h +++ b/carta/cpp/desktop/DesktopConnector.h @@ -34,7 +34,7 @@ class DesktopConnector : public QObject, public IConnector virtual CallbackID addStateCallback(CSR path, const StateChangedCallback &cb) override; virtual void registerView(IView * view) override; void unregisterView( const QString& viewName ) override; - virtual void refreshView(IView *view) override; + virtual qint64 refreshView( IView * view) override; virtual void removeStateCallback( const CallbackID & id) override; virtual Carta::Lib::IRemoteVGView * makeRemoteVGView( QString viewName) override; @@ -52,6 +52,8 @@ public slots: void jsConnectorReadySlot(); /// javascript calls this when view is resized void jsUpdateViewSlot( const QString & viewName, int width, int height); + /// javascript calls this when the view is refreshed + void jsViewRefreshedSlot( const QString & viewName, qint64 id); /// javascript calls this on mouse move inside a view /// \deprecated void jsMouseMoveSlot( const QString & viewName, int x, int y); @@ -70,7 +72,7 @@ public slots: /// javascript listens to it void jsCommandResultsSignal( const QString & results); /// emitted by c++ when we want javascript to repaint the view - void jsViewUpdatedSignal( const QString & viewName, const QImage & img); + void jsViewUpdatedSignal( const QString & viewName, const QImage & img, qint64 id); public: diff --git a/carta/cpp/server/ServerConnector.cpp b/carta/cpp/server/ServerConnector.cpp index 56e6e65c..6496ef5f 100644 --- a/carta/cpp/server/ServerConnector.cpp +++ b/carta/cpp/server/ServerConnector.cpp @@ -11,20 +11,128 @@ #include -ServerConnector::ServerConnector() - : QObject( nullptr) -{ - m_callbackNextId = 0; - m_initialized = false; -} static void OnPWStateInitialized(CSI::PureWeb::Server::StateManager &, CSI::EmptyEventArgs &) { - qDebug() << "State manager is now initialized"; + qDebug() << "PureWeb StateManager is now initialized"; } +/// internal class used by ServerConnector to bridge between PureWeb's view and Carta's +/// connector view +class PWIViewConverter : public CSI::PureWeb::Server::IRenderedView +{ +public: + PWIViewConverter( IView * iview, CSI::CountedPtr sm) { + m_iview = iview; + m_sm = sm; + } + + virtual void SetClientSize(CSI::PureWeb::Size clientSize) Q_DECL_OVERRIDE + { + m_iview->handleResizeRequest( QSize( clientSize.Width, clientSize.Height)); + } + virtual CSI::PureWeb::Size GetActualSize() Q_DECL_OVERRIDE + { + auto qtsize = m_iview->size(); + return CSI::PureWeb::Size( qtsize.width(), qtsize.height()); + } + virtual void RenderView(CSI::PureWeb::Server::RenderTarget target) Q_DECL_OVERRIDE + { + CSI::ByteArray bits = target.RenderTargetImage().ImageBytes(); + + const QImage & qimage = m_iview->getBuffer(); + if( qimage.format() != QImage::Format_ARGB32_Premultiplied) { + // @todo could we do SSSE3 byte shuffle here as we are copying? + // e.g. __m128i _mm_shuffle_epi8 + QImage tmpImage = qimage.convertToFormat( QImage::Format_ARGB32_Premultiplied); + CSI::ByteArray::Copy(tmpImage.scanLine(0), bits, 0, bits.Count()); + } + else { + CSI::ByteArray::Copy(qimage.scanLine(0), bits, 0, bits.Count()); + } + + // tell the clients the ID of this refresh + auto map = target.Parameters(); +// qint64 id = refreshId(); + map["refreshId"] = QString::number( m_refreshId).toStdString().c_str(); + } + virtual void PostKeyEvent(const CSI::PureWeb::Ui::PureWebKeyboardEventArgs & /*keyEvent*/) Q_DECL_OVERRIDE + {} + virtual void PostMouseEvent(const CSI::PureWeb::Ui::PureWebMouseEventArgs & mouseEvent) Q_DECL_OVERRIDE + { + QEvent::Type action = QEvent::None; + Qt::MouseButton button; + Qt::MouseButtons buttons; + Qt::KeyboardModifiers keys = 0; + + switch(mouseEvent.EventType) + { + case CSI::PureWeb::Ui::MouseEventType::MouseDown: + action = QEvent::MouseButtonPress; + break; + case CSI::PureWeb::Ui::MouseEventType::MouseUp: + action = QEvent::MouseButtonRelease; + break; + case CSI::PureWeb::Ui::MouseEventType::MouseMove: + action = QEvent::MouseMove; + break; + default: + return; + break; + } + + switch (mouseEvent.ChangedButton) + { + case CSI::PureWeb::Ui::MouseButtons::Left: + button = Qt::LeftButton; + break; + case CSI::PureWeb::Ui::MouseButtons::Right: + button = Qt::RightButton; + break; + default: + button = Qt::NoButton; + break; + } + + if (0 != (mouseEvent.Modifiers & CSI::PureWeb::Ui::Modifiers::Shift)) {keys |= Qt::ShiftModifier; } + if (0 != (mouseEvent.Modifiers & CSI::PureWeb::Ui::Modifiers::Control)) {keys |= Qt::ControlModifier; } + if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::Left)) {buttons |= Qt::LeftButton; } + if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::Right)) {buttons |= Qt::RightButton; } + if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::Middle)) {buttons |= Qt::MidButton; } + if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::XButton1)) {buttons |= Qt::XButton1; } + if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::XButton2)) {buttons |= Qt::XButton2; } + + QMouseEvent * m = new QMouseEvent(action, QPoint(mouseEvent.X,mouseEvent.Y), button, buttons, keys); + m_iview->handleMouseEvent( *m ); + delete m; + } // PostMouseEvent + + /// schedule refresh and return ID of this refresh + qint64 refresh() { + m_refreshId ++; + m_sm->ViewManager().RenderViewDeferred( m_iview->name().toStdString()); + return m_refreshId; + } + + void viewRefreshed( qint64 id) { + CARTA_ASSERT( m_iview); + m_iview-> viewRefreshed( id); + } + + IView * m_iview = nullptr; + qint64 m_refreshId = -1; + CSI::CountedPtr m_sm; +}; + +ServerConnector::ServerConnector() + : QObject( nullptr) +{ + m_callbackNextId = 0; + m_initialized = false; +} + void ServerConnector::initialize(const InitializeCallback & cb) { try { @@ -51,6 +159,10 @@ void ServerConnector::initialize(const InitializeCallback & cb) CSI::PureWeb::Server::StateManager::Instance()->CommandManager().AddUiHandler( "generic", CSI::Bind( this, &ServerConnector::genericCommandListener)); + // register view refresh command listener + CSI::PureWeb::Server::StateManager::Instance()->CommandManager().AddUiHandler( + "viewrefreshed", CSI::Bind( this, &ServerConnector::viewRefreshedCommandCB)); + // extract URL encoded arguments for( auto kv : m_server-> StartupParameters()) { QString key = kv.first.ToAscii().begin(); @@ -141,6 +253,34 @@ void ServerConnector::genericCommandListener(CSI::Guid sessionid, const CSI::Typ responses["result"] = results.join("|").toStdString(); } +void +ServerConnector::viewRefreshedCommandCB( + CSI::Guid sessionid, + const CSI::Typeless & command, + CSI::Typeless & /*responses*/) +{ + + QString viewName = command["viewName"].ValueOr("").ToAscii().begin(); + QString sid = sessionid.ToString().ToAscii().begin(); + QString idStr = command["id"].ValueOr("").ToAscii().begin(); + bool ok; + qint64 id = idStr.toInt( & ok); + if( ! ok) { + qCritical() << "Invalid refresh ID" << idStr << "for view" << viewName; + } + + qDebug() << "viewRefreshedCommandCB" << sid << viewName << id; + + auto pwview = m_pwviews.find( viewName); + if( pwview == m_pwviews.end()) { + qCritical() << "Got refresh for view that does not exist" << viewName; + return; + } + CARTA_ASSERT( pwview-> second); + pwview-> second-> viewRefreshed( id); + +} + IConnector::CallbackID ServerConnector::addStateCallback(IConnector::CSR path, const IConnector::StateChangedCallback &cb) { // do we have a pureweb callback with this path registered already? @@ -190,97 +330,17 @@ void ServerConnector::pureWebValueChangedCB(const CSI::ValueChangedEventArgs &va }); } -/// internal class used by ServerConnector to bridge between PureWeb's view and Carta's -/// connector view -class PWIViewConverter : public CSI::PureWeb::Server::IRenderedView +// unregister the view +void ServerConnector::unregisterView( const QString & viewName ) { -public: - PWIViewConverter( IView * iview) { - m_iview = iview; - } - - virtual void SetClientSize(CSI::PureWeb::Size clientSize) Q_DECL_OVERRIDE - { - m_iview->handleResizeRequest( QSize( clientSize.Width, clientSize.Height)); - } - virtual CSI::PureWeb::Size GetActualSize() Q_DECL_OVERRIDE - { - auto qtsize = m_iview->size(); - return CSI::PureWeb::Size( qtsize.width(), qtsize.height()); - } - virtual void RenderView(CSI::PureWeb::Server::RenderTarget target) Q_DECL_OVERRIDE - { - CSI::ByteArray bits = target.RenderTargetImage().ImageBytes(); - - const QImage & qimage = m_iview->getBuffer(); - if( qimage.format() != QImage::Format_ARGB32_Premultiplied) { - // @todo could we do SSSE3 byte shuffle here as we are copying? - // e.g. __m128i _mm_shuffle_epi8 - QImage tmpImage = qimage.convertToFormat( QImage::Format_ARGB32_Premultiplied); - CSI::ByteArray::Copy(tmpImage.scanLine(0), bits, 0, bits.Count()); - } - else { - CSI::ByteArray::Copy(qimage.scanLine(0), bits, 0, bits.Count()); + m_stateManager->ViewManager().UnregisterView( viewName.toStdString() ); + auto pwview = m_pwviews.find( viewName); + if( pwview != m_pwviews.end()) { + if( pwview-> second) { + delete pwview-> second; } + m_pwviews.erase( pwview); } - virtual void PostKeyEvent(const CSI::PureWeb::Ui::PureWebKeyboardEventArgs & /*keyEvent*/) Q_DECL_OVERRIDE - {} - virtual void PostMouseEvent(const CSI::PureWeb::Ui::PureWebMouseEventArgs & mouseEvent) Q_DECL_OVERRIDE - { - QEvent::Type action = QEvent::None; - Qt::MouseButton button; - Qt::MouseButtons buttons; - Qt::KeyboardModifiers keys = 0; - - switch(mouseEvent.EventType) - { - case CSI::PureWeb::Ui::MouseEventType::MouseDown: - action = QEvent::MouseButtonPress; - break; - case CSI::PureWeb::Ui::MouseEventType::MouseUp: - action = QEvent::MouseButtonRelease; - break; - case CSI::PureWeb::Ui::MouseEventType::MouseMove: - action = QEvent::MouseMove; - break; - default: - return; - break; - } - - switch (mouseEvent.ChangedButton) - { - case CSI::PureWeb::Ui::MouseButtons::Left: - button = Qt::LeftButton; - break; - case CSI::PureWeb::Ui::MouseButtons::Right: - button = Qt::RightButton; - break; - default: - button = Qt::NoButton; - break; - } - - if (0 != (mouseEvent.Modifiers & CSI::PureWeb::Ui::Modifiers::Shift)) {keys |= Qt::ShiftModifier; } - if (0 != (mouseEvent.Modifiers & CSI::PureWeb::Ui::Modifiers::Control)) {keys |= Qt::ControlModifier; } - if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::Left)) {buttons |= Qt::LeftButton; } - if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::Right)) {buttons |= Qt::RightButton; } - if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::Middle)) {buttons |= Qt::MidButton; } - if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::XButton1)) {buttons |= Qt::XButton1; } - if (0 != (mouseEvent.Buttons & CSI::PureWeb::Ui::MouseButtons::XButton2)) {buttons |= Qt::XButton2; } - - QMouseEvent * m = new QMouseEvent(action, QPoint(mouseEvent.X,mouseEvent.Y), button, buttons, keys); - m_iview->handleMouseEvent( *m ); - delete m; - } // PostMouseEvent - - IView * m_iview; -}; - -// unregister the view -void ServerConnector::unregisterView( const QString& viewName ){ - std::string vn = viewName.toStdString(); - m_stateManager->ViewManager().UnregisterView( vn ); } // registerView @@ -294,7 +354,10 @@ void ServerConnector::registerView(IView *view) std::string vn = view->name().toStdString(); /// \bug resource leak /// \todo this should be cleaned up when we (a) destory connector (b) unregister view - PWIViewConverter * cvt = new PWIViewConverter( view); + /// \note these should now be resolved + PWIViewConverter * cvt = new PWIViewConverter( view, m_stateManager); + // store this in our map so we can look it up later + m_pwviews[ view-> name()] = cvt; m_stateManager->ViewManager().RegisterView( view->name().toStdString(), cvt); m_stateManager->ViewManager().SetViewImageFormat( view->name().toStdString(), viewImageFormat); @@ -303,9 +366,18 @@ void ServerConnector::registerView(IView *view) view->registration( this); } -void ServerConnector::refreshView(IView *view) +qint64 ServerConnector::refreshView(IView *view) { - m_stateManager->ViewManager().RenderViewDeferred( view->name().toStdString()); + auto pwview = m_pwviews.find( view-> name()); + if( pwview == m_pwviews.end()) { + qCritical() << "ServerConnector::refreshView::could not find this view"; + m_stateManager->ViewManager().RenderViewDeferred( view->name().toStdString()); + return -1; + } +// qint64 id = pwview-> second-> refreshId() + 1; +// pwview-> second-> setRefreshId( id); + auto id = pwview-> second-> refresh(); + return id; } void ServerConnector::removeStateCallback(const IConnector::CallbackID & /*id*/) @@ -323,3 +395,12 @@ Carta::Lib::IRemoteVGView * ServerConnector::makeRemoteVGView(QString viewName) return new Carta::Core::SimpleRemoteVGView( this, viewName, this); } +ServerConnector::~ServerConnector() +{ + for( auto & pwview : m_pwviews) { + if( pwview.second) { + delete pwview.second; + } + } +} + diff --git a/carta/cpp/server/ServerConnector.h b/carta/cpp/server/ServerConnector.h index 15a9e9f9..6ccd9699 100644 --- a/carta/cpp/server/ServerConnector.h +++ b/carta/cpp/server/ServerConnector.h @@ -59,6 +59,7 @@ class QtMessageTickler int m_eventCounter; }; +class PWIViewConverter; class ServerConnector : public QObject, public IConnector { @@ -97,7 +98,7 @@ class ServerConnector : public QObject, public IConnector virtual void unregisterView( const QString& viewName ) Q_DECL_OVERRIDE; /// refresh view implementation - virtual void refreshView(IView *view) Q_DECL_OVERRIDE; + virtual qint64 refreshView( IView * view) override; /// remove state callback implementation virtual void removeStateCallback( const CallbackID & id) Q_DECL_OVERRIDE; @@ -112,7 +113,9 @@ class ServerConnector : public QObject, public IConnector virtual Carta::Lib::IRemoteVGView * makeRemoteVGView( QString viewName) override; -protected: + virtual ~ServerConnector(); + +private: /// this gets called when pureweb server is shutting down static void OnPureWebShutdown(CSI::PureWeb::Server::StateManagerServer&, @@ -121,6 +124,9 @@ class ServerConnector : public QObject, public IConnector /// internal true pureweb command listener, our command callback piggyback to this one void genericCommandListener(CSI::Guid sessionid, CSI::Typeless const& command, CSI::Typeless& responses); + /// internal listener for view refresh commands from the clients + void viewRefreshedCommandCB(CSI::Guid sessionid, CSI::Typeless const& command, CSI::Typeless& responses); + /// pointer to the server CSI::CountedPtr m_server; @@ -150,9 +156,8 @@ class ServerConnector : public QObject, public IConnector // whether initialize was called bool m_initialized; -private: - void print( CSI::Typeless treeRoot ) const; -// QString toQString( const CSI::String source) const; + + std::map< QString, PWIViewConverter *> m_pwviews; }; diff --git a/carta/html5/desktop/desktopConnector.js b/carta/html5/desktop/desktopConnector.js index bb917f0a..7a204a07 100644 --- a/carta/html5/desktop/desktopConnector.js +++ b/carta/html5/desktop/desktopConnector.js @@ -91,13 +91,14 @@ }); // listen for jsViewUpdatedSignal to render the image - QtConnector.jsViewUpdatedSignal.connect(function(viewName, buffer) { + QtConnector.jsViewUpdatedSignal.connect(function(viewName, buffer, refreshId) { var view = m_views[viewName]; if (view == null) { console.warn("Ignoring update for unconnected view '" + viewName + "'"); return; } buffer.assignToHTMLImageElement(view.m_imgTag); + QtConnector.jsViewRefreshedSlot( view.getName(), refreshId); }); // convenience function to create & get or just get a state diff --git a/carta/html5/server/serverConnector.js b/carta/html5/server/serverConnector.js index 33a34849..9dc60caa 100644 --- a/carta/html5/server/serverConnector.js +++ b/carta/html5/server/serverConnector.js @@ -77,7 +77,9 @@ function( e ) { var params = e.args.getEncodingParameters(); -// console.log( "View '" + this.m_pwview.getViewName() + "' updated, params=", params ); + console.log( "View '" + this.m_pwview.getViewName() + "' updated, params=", params ); + pureweb.getClient().queueCommand( "viewrefreshed", + { viewName: viewName, id: params.refreshId }, null, this ); }, false, this ); pureweb.listen( this.m_pwview, pureweb.client.View.EventType.TRANSFORMS_CHANGED, function( ) From 22fd6d255ac5939ec095501d02b0f86df6737aa0 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Wed, 28 Oct 2015 22:39:29 -0600 Subject: [PATCH 18/37] fixed a bug where contours did not pan/zoom --- carta/cpp/core/Hacks/ImageViewController.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/carta/cpp/core/Hacks/ImageViewController.cpp b/carta/cpp/core/Hacks/ImageViewController.cpp index f9d2b4d0..84bd19b5 100644 --- a/carta/cpp/core/Hacks/ImageViewController.cpp +++ b/carta/cpp/core/Hacks/ImageViewController.cpp @@ -535,11 +535,13 @@ ImageViewController::imageAndGridDoneSlot( } qDebug() << "Grid VG rendered in" << t.elapsed() / 1000.0 << "sec" << "xyz"; + // insert a matrix transform at the beginning + Carta::Lib::VectorGraphics::VGComposer composer; t.restart(); { - QPen lineColor( QColor( "red" ), 1 ); - lineColor.setCosmetic( true ); - painter.setPen( lineColor ); +// QPen lineColor( QColor( "red" ), 1 ); +// lineColor.setCosmetic( true ); +// painter.setPen( lineColor ); // where does 0.5, 0.5 map to? QPointF p1 = m_renderService-> img2screen( { 0.5, 0.5 } @@ -559,7 +561,10 @@ ImageViewController::imageAndGridDoneSlot( double m31 = p1.x() - m11 * 0.5; double m32 = p1.y() - m22 * 0.5; tf.setMatrix( m11, m12, m13, m21, m22, m23, m31, m32, m33 ); - painter.setTransform( tf ); +// painter.setTransform( tf ); + composer.append< Carta::Lib::VectorGraphics::Entries::SetTransform >( tf); + composer.appendList( contourVG); + contourVG = composer.vgList(); } if ( ! vgRenderer.render( contourVG, painter ) ) { qWarning() << "could not render contour vector graphics"; From 2d031a635a714de10f085f08a5c36dae5b1a5d63 Mon Sep 17 00:00:00 2001 From: slovelan Date: Fri, 30 Oct 2015 10:38:46 -0600 Subject: [PATCH 19/37] Initial image stack management. --- carta/cpp/core/Data/Animator/Animator.cpp | 11 +- carta/cpp/core/Data/Animator/Animator.h | 2 +- carta/cpp/core/Data/Histogram/Histogram.cpp | 17 +- carta/cpp/core/Data/Image/Controller.cpp | 503 +++++++++++------- carta/cpp/core/Data/Image/Controller.h | 87 ++- carta/cpp/core/Data/Image/ControllerData.cpp | 48 +- carta/cpp/core/Data/Image/ControllerData.h | 30 +- carta/cpp/core/Data/Image/DataSource.cpp | 7 - carta/cpp/core/Data/Image/DataSource.h | 7 - .../cpp/core/Histogram/HistogramGenerator.cpp | 10 +- carta/cpp/core/Histogram/HistogramGenerator.h | 4 +- carta/cpp/core/State/StateInterface.cpp | 9 +- .../skel/source/class/skel/Application.js | 4 +- .../class/skel/Command/Data/CommandData.js | 14 + .../skel/Command/Data/CommandDataClose.js | 6 +- .../skel/Command/Data/CommandDataHide.js | 69 +++ .../skel/Command/Data/CommandDataHideImage.js | 48 ++ .../skel/Command/Data/CommandDataShow.js | 69 +++ .../skel/Command/Data/CommandDataShowImage.js | 48 ++ .../skel/Command/Session/CommandShare.js | 5 +- .../class/skel/widgets/Image/ImageControls.js | 7 +- .../skel/widgets/Image/Stack/DragDropList.js | 236 ++++++++ .../skel/widgets/Image/Stack/StackControls.js | 90 ++++ .../class/skel/widgets/Menu/StatusBar.js | 10 +- .../skel/source/class/skel/widgets/Path.js | 2 + .../skel/source/class/skel/widgets/Util.js | 4 +- .../skel/widgets/Window/DisplayWindow.js | 2 +- .../skel/widgets/Window/DisplayWindowImage.js | 27 +- carta/html5/server/serverConnector.js | 8 +- 29 files changed, 1097 insertions(+), 287 deletions(-) create mode 100644 carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHide.js create mode 100644 carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHideImage.js create mode 100644 carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js create mode 100644 carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShowImage.js create mode 100755 carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js create mode 100755 carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js diff --git a/carta/cpp/core/Data/Animator/Animator.cpp b/carta/cpp/core/Data/Animator/Animator.cpp index 55f52d75..b9b29aff 100644 --- a/carta/cpp/core/Data/Animator/Animator.cpp +++ b/carta/cpp/core/Data/Animator/Animator.cpp @@ -133,7 +133,7 @@ QString Animator::addAnimator( const QString& type, QString& animatorTypeId ){ } void Animator::_addRemoveImageAnimator(){ - int maxImages = _getMaxImageCount(); + int maxImages = getMaxImageCount(); if ( maxImages > 1 ){ QString animId; addAnimator( Selection::IMAGE, animId ); @@ -272,7 +272,7 @@ int Animator::getMaxImageCount() const { for ( int i = 0; i < linkCount; i++ ){ Controller* controller = dynamic_cast( m_linkImpl->getLink(i)); if ( controller != nullptr ){ - int imageCount = controller->getStackedImageCount(); + int imageCount = controller->getStackedImageCountVisible(); if ( maxImages < imageCount ){ maxImages = imageCount; } @@ -281,11 +281,6 @@ int Animator::getMaxImageCount() const { return maxImages; } -int Animator::_getMaxImageCount() const { - int maxImages = getMaxImageCount(); - return maxImages; -} - QString Animator::getStateString( const QString& /*sessionId*/, SnapshotType type ) const{ QString result; if ( type == SNAPSHOT_PREFERENCES ){ @@ -448,7 +443,7 @@ QString Animator::removeLink( CartaObject* cartaObject ){ void Animator::_resetAnimationParameters( int selectedImage ){ _addRemoveImageAnimator(); if ( m_animators.contains( Selection::IMAGE) ){ - int maxImages = _getMaxImageCount(); + int maxImages = getMaxImageCount(); if ( maxImages == 0 ){ m_animators[Selection::IMAGE]->setUpperBound( 1 ); } diff --git a/carta/cpp/core/Data/Animator/Animator.h b/carta/cpp/core/Data/Animator/Animator.h index de73cefe..2bc37b54 100644 --- a/carta/cpp/core/Data/Animator/Animator.h +++ b/carta/cpp/core/Data/Animator/Animator.h @@ -157,7 +157,7 @@ private slots: void _adjustStateAnimatorTypes(); void _addRemoveImageAnimator(); Controller* _getControllerSelected() const; - int _getMaxImageCount() const; + int _getAnimatorTypeVisibleCount() const; void _initializeState(); void _initializeAnimators(); diff --git a/carta/cpp/core/Data/Histogram/Histogram.cpp b/carta/cpp/core/Data/Histogram/Histogram.cpp index a0ff5a64..c10ed423 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.cpp +++ b/carta/cpp/core/Data/Histogram/Histogram.cpp @@ -1007,7 +1007,7 @@ void Histogram::_loadData( Controller* controller ){ double maxIntensity = _getBufferedIntensity( CLIP_MAX, CLIP_MAX_PERCENT ); std::vector> dataSources; if ( controller != nullptr ){ - int stackedImageCount = controller->getStackedImageCount(); + int stackedImageCount = controller->getStackedImageCountVisible(); if ( stackedImageCount > 0 ){ dataSources = _generateData( controller ); auto result = Globals::instance()-> pluginManager() @@ -1770,7 +1770,6 @@ QString Histogram::setPlaneMode( const QString& planeModeStr ){ } - QString Histogram::saveHistogram( const QString& fileName ){ QString result = ""; //Check and make sure the directory exists. @@ -1788,9 +1787,17 @@ QString Histogram::saveHistogram( const QString& fileName ){ int width = prefSave->getWidth(); int height = prefSave->getHeight(); Qt::AspectRatioMode aspectRatioMode = prefSave->getAspectRatioMode(); - QImage* histogramImage = m_histogram->toImage(); - QSize outputSize( width, height ); - QImage imgScaled = histogramImage->scaled( outputSize, aspectRatioMode, Qt::SmoothTransformation ); + QImage imgScaled; + QImage* histogramImage = nullptr; + if ( aspectRatioMode == Qt::IgnoreAspectRatio ){ + histogramImage = m_histogram->toImage( width, height ); + imgScaled = *histogramImage; + } + else { + histogramImage = m_histogram->toImage(); + QSize outputSize( width, height ); + imgScaled = histogramImage->scaled( outputSize, aspectRatioMode, Qt::SmoothTransformation ); + } bool saveSuccessful = imgScaled.save( fileName, 0, 100 ); if ( !saveSuccessful ){ result = "The image could not be saved; please check the path: "+fileName+" is valid."; diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 67f62082..a650510f 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -50,6 +50,7 @@ const QString Controller::DATA = "data"; const QString Controller::DATA_PATH = "dataPath"; const QString Controller::CURSOR = "formattedCursorCoordinates"; const QString Controller::CENTER = "center"; +const QString Controller::IMAGE = "image"; const QString Controller::POINTER_MOVE = "pointer-move"; const QString Controller::ZOOM = "zoom"; const QString Controller::REGIONS = "regions"; @@ -111,7 +112,7 @@ bool Controller::addData(const QString& fileName) { //Find the location of the data, if it already exists. int targetIndex = -1; for (int i = 0; i < m_datas.size(); i++) { - if (m_datas[i]->_contains(fileName)) { + if (m_datas[i]->_isMatch(fileName)) { targetIndex = i; break; } @@ -135,7 +136,8 @@ bool Controller::addData(const QString& fileName) { targetSource->_viewResize( m_viewSize ); //Update the data selectors upper bound based on the data. - m_selectImage->setUpperBound(m_datas.size()); + int visibleCount = getStackedImageCountVisible(); + m_selectImage->setUpperBound( visibleCount ); } bool successfulLoad = m_datas[targetIndex]->_setFileName(fileName ); @@ -222,12 +224,17 @@ void Controller::_clearData(){ } } +void Controller::_clearStatistics(){ + m_stateMouse.setValue( CURSOR, "" ); + m_stateMouse.flushState(); +} + QString Controller::closeImage( const QString& name ){ int targetIndex = -1; QString result; int dataCount = m_datas.size(); for ( int i = 0; i < dataCount; i++ ){ - if ( m_datas[i]->_contains( name )){ + if ( m_datas[i]->_isMatch( name )){ targetIndex = i; break; } @@ -243,35 +250,49 @@ QString Controller::closeImage( const QString& name ){ return result; } +int Controller::_getDataIndex( ) const { + int index = m_selectImage->getIndex(); + int dataIndex = -1; + int visibleIndex = -1; + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isVisible() ){ + visibleIndex++; + if ( visibleIndex == index ){ + dataIndex = i; + break; + } + } + } + return dataIndex; +} + void Controller::centerOnPixel( double centerX, double centerY ){ - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size()){ - m_datas[imageIndex]->_setPan( centerX, centerY ); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + m_datas[dataIndex]->_setPan( centerX, centerY ); _render(); } } void Controller::_contoursChanged(){ - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size() ){ - std::vector frames = _getFrameIndices( imageIndex ); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + std::vector frames = _getFrameIndices( dataIndex ); const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); - m_datas[imageIndex]->_render( frames, cs ); + m_datas[dataIndex]->_render( frames, cs ); } } void Controller::_displayAxesChanged(std::vector displayAxisTypes, bool applyAll ){ - int imageIndex = 0; - if (m_selectImage != nullptr) { - imageIndex = m_selectImage->getIndex(); - } if ( !applyAll ){ - if (imageIndex >= 0 && imageIndex < m_datas.size()) { - if (m_datas[imageIndex] != nullptr) { - std::vector frames = _getFrameIndices( imageIndex ); - m_datas[imageIndex]->_displayAxesChanged( displayAxisTypes, frames ); + int dataIndex = _getDataIndex(); + if (dataIndex >= 0 ) { + if (m_datas[dataIndex] != nullptr) { + std::vector frames = _getFrameIndices( dataIndex ); + m_datas[dataIndex]->_displayAxesChanged( displayAxisTypes, frames ); } } } @@ -290,10 +311,10 @@ void Controller::_displayAxesChanged(std::vector displayAxi } std::vector Controller::_getAxisZTypes() const { - int imageIndex = m_selectImage->getIndex(); + int dataIndex = _getDataIndex(); std::vector zTypes; - if ( 0 <= imageIndex && imageIndex < m_datas.size() ){ - zTypes = m_datas[imageIndex]->_getAxisZTypes(); + if ( 0 <= dataIndex ){ + zTypes = m_datas[dataIndex]->_getAxisZTypes(); } return zTypes; } @@ -302,12 +323,13 @@ std::set Controller::_getAxesHidden() const { int dataCount = m_datas.size(); std::set axes; for ( int i = 0; i < dataCount; i++ ){ - std::vector zAxes = m_datas[i]->_getAxisZTypes(); - int zAxesCount = zAxes.size(); - for ( int j = 0; j < zAxesCount; j++ ){ - axes.insert( zAxes[j] ); + if ( m_datas[i]->_isVisible() ){ + std::vector zAxes = m_datas[i]->_getAxisZTypes(); + int zAxesCount = zAxes.size(); + for ( int j = 0; j < zAxesCount; j++ ){ + axes.insert( zAxes[j] ); + } } - } return axes; } @@ -323,10 +345,10 @@ double Controller::getClipPercentileMin() const { } Carta::Lib::KnownSkyCS Controller::getCoordinateSystem() const { - int imageIndex = m_selectImage->getIndex(); + int dataIndex = _getDataIndex(); Carta::Lib::KnownSkyCS cs = Carta::Lib::KnownSkyCS::Unknown; - if ( 0 <= imageIndex && imageIndex < m_datas.size() ){ - cs = m_datas[imageIndex]->_getCoordinateSystem(); + if ( 0 <= dataIndex ){ + cs = m_datas[dataIndex]->_getCoordinateSystem(); } return cs; } @@ -347,9 +369,9 @@ int Controller::getFrameUpperBound( AxisInfo::KnownType axisType ) const { int Controller::getFrame( AxisInfo::KnownType axisType ) const { int frame = -1; if ( axisType != AxisInfo::KnownType::OTHER ){ - int imageIndex = m_selectImage->getIndex(); - if ( 0 <= imageIndex && imageIndex < m_datas.size()){ - std::vector supportedAxes = m_datas[imageIndex]->_getAxisTypes(); + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ + std::vector supportedAxes = m_datas[dataIndex]->_getAxisTypes(); //Make sure the axis is an axis in the image. std::vector::iterator it = find( supportedAxes.begin(), supportedAxes.end(), axisType ); if ( it != supportedAxes.end() ){ @@ -361,6 +383,48 @@ int Controller::getFrame( AxisInfo::KnownType axisType ) const { return frame; } +QStringList Controller::getCenterPixel() { + int dataIndex = _getDataIndex(); + QStringList returnValue = QStringList( "null" ); + if ( dataIndex >= 0 ) { + QPointF center = m_datas[dataIndex]->_getCenter(); + returnValue = QStringList( QString::number( center.x() ) ); + returnValue.append( QString::number( center.y() ) ); + } + return returnValue; +} + +QStringList Controller::getImageDimensions( ){ + QStringList result; + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + int dimensions = m_datas[dataIndex]->_getDimensions(); + for ( int i = 0; i < dimensions; i++ ) { + int d = m_datas[dataIndex]->_getDimension( i ); + result.append( QString::number( d ) ); + } + } + else { + result = QStringList(""); + } + return result; +} + +QStringList Controller::getOutputSize( ){ + QStringList result; + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + QSize outputSize = m_datas[dataIndex]->_getOutputSize(); + result.append( QString::number( outputSize.width() ) ); + result.append( QString::number( outputSize.height() ) ); + } + else { + result = QStringList(""); + } + return result; +} + + bool Controller::getIntensity( double percentile, double* intensity ) const{ int currentFrame = getFrame( AxisInfo::KnownType::SPECTRAL); bool validIntensity = getIntensity( currentFrame, currentFrame, percentile, intensity ); @@ -369,9 +433,9 @@ bool Controller::getIntensity( double percentile, double* intensity ) const{ bool Controller::getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const{ bool validIntensity = false; - int imageIndex = m_selectImage->getIndex(); - if ( 0 <= imageIndex && imageIndex < m_datas.size() && percentile >= 0.0 && percentile <= 1.0 ){ - validIntensity = m_datas[imageIndex]->_getIntensity( frameLow, frameHigh, percentile, intensity ); + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex && percentile >= 0.0 && percentile <= 1.0 ){ + validIntensity = m_datas[dataIndex]->_getIntensity( frameLow, frameHigh, percentile, intensity ); } return validIntensity; } @@ -383,9 +447,9 @@ double Controller::getPercentile( double intensity ) const { double Controller::getPercentile( int frameLow, int frameHigh, double intensity ) const { double percentile = -1; - int imageIndex = m_selectImage->getIndex(); - if ( 0 <= imageIndex && imageIndex < m_datas.size()){ - percentile = m_datas[imageIndex]->_getPercentile( frameLow, frameHigh, intensity ); + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ + percentile = m_datas[dataIndex]->_getPercentile( frameLow, frameHigh, intensity ); } return percentile; } @@ -396,9 +460,9 @@ std::vector> Controller::getDataSources() std::vector> images; int dataCount = m_datas.size(); if ( dataCount > 0 ){ - int imageIndex = m_selectImage->getIndex(); - if ( 0 <= imageIndex && imageIndex < dataCount ){ - images.push_back( m_datas[imageIndex]->_getImage()); + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ + images.push_back( m_datas[dataIndex]->_getImage()); } } return images; @@ -406,7 +470,8 @@ std::vector> Controller::getDataSources() int Controller::getSelectImageIndex() const { int selectImageIndex = -1; - if ( m_datas.size() >= 1 ){ + int stackedImageVisibleCount = getStackedImageCountVisible(); + if ( stackedImageVisibleCount >= 1 ){ selectImageIndex = m_selectImage->getIndex(); } return selectImageIndex; @@ -422,46 +487,46 @@ QString Controller::getImageName(int index) const{ std::shared_ptr Controller::getPipeline() const { std::shared_ptr pipeline(nullptr); - int selectImageIndex = m_selectImage->getIndex(); - if ( 0 <= selectImageIndex && selectImageIndex < m_datas.size() ){ - pipeline = m_datas[selectImageIndex]->_getPipeline(); + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ + pipeline = m_datas[dataIndex]->_getPipeline(); } return pipeline; } QStringList Controller::getPixelCoordinates( double ra, double dec ) const { QStringList result(""); - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size()){ - result = m_datas[imageIndex]->_getPixelCoordinates( ra, dec ); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + result = m_datas[dataIndex]->_getPixelCoordinates( ra, dec ); } return result; } QString Controller::getPixelValue( double x, double y ) const { QString result(""); - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size()){ - std::vector frames = _getFrameIndices( imageIndex ); - result = m_datas[imageIndex]->_getPixelValue( x, y, frames ); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + std::vector frames = _getFrameIndices( dataIndex ); + result = m_datas[dataIndex]->_getPixelValue( x, y, frames ); } return result; } QString Controller::getPixelUnits() const { QString result(""); - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size()){ - result = m_datas[imageIndex]->_getPixelUnits(); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + result = m_datas[dataIndex]->_getPixelUnits(); } return result; } QStringList Controller::getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system) const { QStringList result; - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size()){ - QStringList coordList = m_datas[imageIndex]->_getCoordinates( x, y, system, _getFrameIndices(imageIndex) ); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + QStringList coordList = m_datas[dataIndex]->_getCoordinates( x, y, system, _getFrameIndices(dataIndex) ); for ( int i = 0; i <= 1; i++ ){ result.append( coordList[i] ); } @@ -496,6 +561,17 @@ int Controller::getStackedImageCount() const { return count; } +int Controller::getStackedImageCountVisible() const { + int visibleCount = 0; + int imageCount = m_datas.size(); + for ( int i = 0; i < imageCount; i++ ){ + if ( m_datas[i]->_isVisible() ){ + visibleCount++; + } + } + return visibleCount; +} + QString Controller::getStateString( const QString& sessionId, SnapshotType type ) const{ QString result(""); @@ -518,7 +594,8 @@ QString Controller::getStateString( const QString& sessionId, SnapshotType type dataState.insertArray( DATA, dataCount ); for ( int i = 0; i < dataCount; i++ ){ QString lookup = Carta::State::UtilState::getLookup( DATA, i ); - dataState.setObject( lookup, m_datas[i]->_getStateString()); + QString dataStateStr = m_datas[i]->_getStateString(); + dataState.setObject( lookup, dataStateStr ); } dataState.setValue( StateInterface::OBJECT_TYPE, CLASS_NAME + StateInterface::STATE_DATA); dataState.setValue(StateInterface::INDEX, getIndex() ); @@ -531,7 +608,6 @@ QString Controller::getStateString( const QString& sessionId, SnapshotType type dataState.insertValue( Selection::IMAGE, m_selectImage->getStateString()); dataState.insertValue( ContourControls::CLASS_NAME, m_contourControls->getStateString( sessionId, type)); result = dataState.toString(); - } return result; } @@ -548,29 +624,26 @@ QString Controller::getSnapType(CartaObject::SnapshotType snapType) const { double Controller::getZoomLevel( ){ double zoom = DataSource::ZOOM_DEFAULT; - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size() ){ - zoom = m_datas[imageIndex]->_getZoom( ); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + zoom = m_datas[dataIndex]->_getZoom( ); _render(); } return zoom; } void Controller::_gridChanged( const StateInterface& state, bool applyAll ){ - int imageIndex = 0; - if (m_selectImage != nullptr) { - imageIndex = m_selectImage->getIndex(); - } - if ( imageIndex >= 0 && imageIndex < m_datas.size() ){ - std::vector frames = _getFrameIndices( imageIndex ); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + std::vector frames = _getFrameIndices( dataIndex ); if ( !applyAll ){ - m_datas[imageIndex]->_gridChanged( state, true, frames ); + m_datas[dataIndex]->_gridChanged( state, true, frames ); } else { int dataCount = m_datas.size(); for ( int i = 0; i < dataCount; i++ ){ bool renderedImage = false; - if ( i == imageIndex ){ + if ( i == dataIndex ){ renderedImage = true; } if ( m_datas[i] != nullptr ){ @@ -583,6 +656,37 @@ void Controller::_gridChanged( const StateInterface& state, bool applyAll ){ } void Controller::_initializeCallbacks(){ + addCommandCallback( "hideImage", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {IMAGE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString imageName = dataValues[*keys.begin()]; + QString result = setImageVisibility( imageName, false ); + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "showImage", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {IMAGE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString imageName = dataValues[*keys.begin()]; + QString result = setImageVisibility( imageName, true ); + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "setImageOrder", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {"images"}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QStringList images = dataValues[*keys.begin()].split(";"); + QString result = setImageOrder( images ); + Util::commandPostProcess( result ); + return result; + }); + + //Listen for updates to the clip and reload the frame. addCommandCallback( "setClipValue", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { @@ -790,16 +894,12 @@ void Controller::_initializeState(){ void Controller::_loadView( bool newClips ) { m_reloadFrameQueued = false; //Determine the index of the data to load. - int imageIndex = 0; - if (m_selectImage != nullptr) { - imageIndex = m_selectImage->getIndex(); - } - - if (imageIndex >= 0 && imageIndex < m_datas.size()) { - if (m_datas[imageIndex] != nullptr) { + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ) { + if (m_datas[dataIndex] != nullptr) { //Determine the indices of the frames to load. - vector frames = _getFrameIndices( imageIndex ); + vector frames = _getFrameIndices( dataIndex ); //Load the image. bool autoClip = m_state.getValue(AUTO_CLIP); @@ -809,10 +909,10 @@ void Controller::_loadView( bool newClips ) { double clipValueMin = m_state.getValue(CLIP_VALUE_MIN); double clipValueMax = m_state.getValue(CLIP_VALUE_MAX); const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); - m_datas[imageIndex]->_load(frames, autoClip, clipValueMin, clipValueMax, cs); + m_datas[dataIndex]->_load(frames, autoClip, clipValueMin, clipValueMax, cs); } else { - qDebug() << "Uninitialized image: "<getIndex(); + int selectedDataIndex = _getDataIndex(); QString id = m_datas[index]->getId(); + bool visible = m_datas[index]->_isVisible(); Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); objMan->removeObject( id ); m_datas.removeAt( index ); - m_selectImage->setUpperBound( m_datas.size()); - if ( selectedImage == index ){ + int visibleImageCount = getStackedImageCountVisible(); + m_selectImage->setUpperBound( visibleImageCount ); + if ( selectedDataIndex == index ){ m_selectImage->setIndex( 0 ); } - else if ( index < selectedImage ){ + else if ( index < selectedDataIndex && visible ){ int imageCountDecreased = selectedImage - 1; m_selectImage->setIndex( imageCountDecreased ); } //Update the channel upper bound and index if necessary - int targetImage = m_selectImage->getIndex(); + int targetData = _getDataIndex(); int selectCount = m_selects.size(); for ( int i = 0; i < selectCount; i++ ){ int frameCount = 0; AxisInfo::KnownType axisType= static_cast( i ); - if ( targetImage >= 0 && targetImage < m_datas.size() ){ - frameCount = m_datas[targetImage]->_getFrameCount( axisType ); + if ( targetData >= 0 ){ + frameCount = m_datas[targetData]->_getFrameCount( axisType ); } int oldIndex = m_selects[i]->getIndex(); if ( oldIndex >= frameCount && frameCount > 0){ @@ -869,20 +973,20 @@ void Controller::_removeData( int index ){ this->_loadView(); //Clear the statistics window if there are no images. - if ( m_datas.size() == 0 ){ - m_stateMouse.setValue( CURSOR, "" ); - m_stateMouse.flushState(); + if ( visibleImageCount == 0 ){ + _clearStatistics(); } saveState(); } + void Controller::_render(){ - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size()){ - std::vector frames = _getFrameIndices( imageIndex ); + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ + std::vector frames = _getFrameIndices( dataIndex ); const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); - m_datas[imageIndex]->_render( frames, cs ); + m_datas[dataIndex]->_render( frames, cs ); } } @@ -890,6 +994,49 @@ void Controller::_renderingDone( QImage img){ _scheduleFrameRepaint( img ); } +QString Controller::setImageOrder( const QStringList& imageNames ){ + QString result; + bool imageReordered = false; + int selectedIndex = _getDataIndex(); + int dataCount = m_datas.size(); + int nameCount = imageNames.size(); + if ( nameCount != dataCount ){ + result = "Reorder image size must match the stack count: "+QString::number(dataCount); + } + else { + DataLoader* dataLoader = Util::findSingletonObject(); + QString rootDir = dataLoader->getRootDir( "" ); + for ( int i = 0; i < nameCount; i++ ){ + QString targetName = rootDir +QDir::separator()+imageNames[i]; + int targetIndex = -1; + for ( int j = i; j < dataCount; j++ ){ + QString imageName = m_datas[j]->_getFileName(); + if ( targetName == imageName ){ + targetIndex = j; + break; + } + } + if ( targetIndex < 0 ){ + result = "Reorder failed: unknown image: "+targetName; + break; + } + //Insert the image at the target index at position i. + else if ( targetIndex > i ){ + std::shared_ptr item = m_datas.takeAt( targetIndex ); + m_datas.insert(i, item ); + + if ( targetIndex == selectedIndex || i == selectedIndex ){ + imageReordered = true; + } + } + } + } + if ( imageReordered ){ + _render(); + } + return result; +} + void Controller::_repaintFrameNow(){ m_view->scheduleRedraw(); m_repaintFrameQueued = false; @@ -948,9 +1095,9 @@ void Controller::resetStateData( const QString& state ){ emit dataChanged( this ); //Reset the state of the grid controls based on the selected image. - int imageIndex = m_selectImage->getIndex(); - if ( 0 <= imageIndex && imageIndex < m_datas.size()){ - StateInterface controlState = m_datas[imageIndex]->_getGridState(); + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ + StateInterface controlState = m_datas[dataIndex]->_getGridState(); this->m_gridControls->_resetState( controlState ); } @@ -980,39 +1127,23 @@ void Controller::resetZoom(){ } void Controller::saveState() { - bool stateChanged = false; int dataCount = m_datas.size(); int oldDataCount = m_stateData.getArraySize(DATA ); if ( oldDataCount != dataCount ){ - stateChanged = true; //Insert the names of the data items for display purposes. - m_stateData.resizeArray(DATA, dataCount, StateInterface::PreserveAll ); - QStringList longNames; - for (int i = 0; i < dataCount; i++) { - longNames.append( m_datas[i]->_getFileName() ); - } - - DataLoader* dataLoader = Util::findSingletonObject(); - QStringList shortNames = dataLoader->getShortNames( longNames ); - - int shortNameCount = shortNames.size(); - for ( int i = 0; i < shortNameCount; i++ ){ - QString dataKey = UtilState::getLookup( DATA, i); - QString oldViewName; - if ( i < oldDataCount ){ - oldViewName = m_stateData.getValue(dataKey); - } - if ( shortNames[i] != oldViewName ){ - m_stateData.setValue( dataKey, shortNames[i] ); - } - } + m_stateData.resizeArray(DATA, dataCount, StateInterface::PreserveNone ); + } + for (int i = 0; i < dataCount; i++) { + QString layerString = m_datas[i]->_getLayerString(); + QString dataKey = UtilState::getLookup( DATA, i); + m_stateData.setObject( dataKey, layerString); } + /*int regionCount = m_regions.size(); m_state.resizeArray( REGIONS, regionCount ); _saveRegions();*/ - if ( stateChanged ){ - m_stateData.flushState(); - } + + m_stateData.flushState(); } @@ -1026,8 +1157,8 @@ QString Controller::saveImage( const QString& fileName, double scale ){ DataLoader* dLoader = Util::findSingletonObject(); bool securityRestricted = dLoader->isSecurityRestricted(); if ( !securityRestricted ){ - int imageIndex = m_selectImage->getIndex(); - if ( 0<= imageIndex && imageIndex < m_datas.size()){ + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ //Check and make sure the directory exists. int dirIndex = fileName.lastIndexOf( QDir::separator() ); QString dirName = fileName; @@ -1039,8 +1170,8 @@ QString Controller::saveImage( const QString& fileName, double scale ){ result = "Please make sure the save path is valid: "+fileName; } else { - std::vector frames = _getFrameIndices( imageIndex ); - result = m_datas[imageIndex]->_saveImage( fileName, scale, frames ); + std::vector frames = _getFrameIndices( dataIndex ); + result = m_datas[dataIndex]->_saveImage( fileName, scale, frames ); } } else { @@ -1170,9 +1301,8 @@ void Controller::_setFrameAxis(int value, AxisInfo::KnownType axisType ) { m_selects[axisIndex]->setIndex(value); //We only need to update the cursor if the axis is a hidden axis //for the current image. - int imageIndex = m_selectImage->getIndex(); - int dataCount = m_datas.size(); - if ( 0 <= imageIndex && imageIndex < dataCount ){ + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ _updateCursorText( true ); emit channelChanged( this ); } @@ -1185,12 +1315,12 @@ void Controller::setFrameImage( int val) { int oldIndex = m_selectImage->getIndex(); if ( oldIndex != val ){ m_selectImage->setIndex(val); - int imageIndex = m_selectImage->getIndex(); - if ( 0 <= imageIndex && imageIndex < m_datas.size() ){ + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ int selectCount = m_selects.size(); for ( int i = 0; i < selectCount; i++ ){ AxisInfo::KnownType type = static_cast(i); - int upperBound = m_datas[imageIndex]->_getFrameCount( type ); + int upperBound = m_datas[dataIndex]->_getFrameCount( type ); m_selects[i]->setUpperBound( upperBound ); if ( m_selects[i]->getIndex() > m_selects[i]->getUpperBound()){ m_selects[i]->setIndex( 0 ); @@ -1200,7 +1330,7 @@ void Controller::setFrameImage( int val) { } } - Carta::State::StateInterface gridState = m_datas[imageIndex]->_getGridState(); + Carta::State::StateInterface gridState = m_datas[dataIndex]->_getGridState(); m_gridControls->_resetState( gridState ); } _updateCursorText( true ); @@ -1216,7 +1346,42 @@ void Controller::setGamma( double gamma ){ _render(); } - +QString Controller::setImageVisibility( const QString& name, bool visible ){ + QString result; + bool visibilitySet = false; + int dataCount = m_datas.size(); + int dataIndex = -1; + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isMatch( name ) ){ + bool oldVisible = m_datas[i]->_isVisible(); + if ( oldVisible != visible ){ + m_datas[i]->_setVisible( visible ); + visibilitySet = true; + } + dataIndex = i; + break; + } + } + if ( !visibilitySet ){ + result = "Could not reset the visibility of "+name; + } + else { + int selectedImageIndex = _getDataIndex(); + //Update the upper bound on the number of images available. + int visibleCount = getStackedImageCountVisible(); + m_selectImage->setUpperBound( visibleCount ); + emit dataChanged( this ); + //Render the image if it is the one currently being viewed. + if ( selectedImageIndex == dataIndex ){ + _loadView(); + } + if ( visibleCount == 0 ){ + _clearStatistics(); + } + saveState(); + } + return result; +} void Controller::setTransformData( const QString& name ){ for ( std::shared_ptr data : m_datas ){ @@ -1226,10 +1391,10 @@ void Controller::setTransformData( const QString& name ){ } void Controller::setZoomLevel( double zoomFactor ){ - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && m_datas.size() > 0 ){ + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ //Set the zoom - m_datas[imageIndex]->_setZoom( zoomFactor ); + m_datas[dataIndex]->_setZoom( zoomFactor ); _render(); } } @@ -1253,10 +1418,11 @@ void Controller::_updateCursor( int mouseX, int mouseY ){ void Controller::_updateCursorText(bool notifyClients ){ QString formattedCursor; int imageIndex = m_selectImage->getIndex(); - if ( 0 <= imageIndex && imageIndex < m_datas.size() ){ + int dataIndex = _getDataIndex(); + if ( 0 <= dataIndex ){ int mouseX = m_stateMouse.getValue(ImageView::MOUSE_X ); int mouseY = m_stateMouse.getValue(ImageView::MOUSE_Y ); - QString cursorText = m_datas[imageIndex]->_getCursorText( mouseX, mouseY, _getFrameIndices( imageIndex)); + QString cursorText = m_datas[dataIndex]->_getCursorText( mouseX, mouseY, _getFrameIndices( imageIndex)); if ( cursorText != m_stateMouse.getValue(CURSOR)){ m_stateMouse.setValue( CURSOR, cursorText ); if ( notifyClients ){ @@ -1285,17 +1451,16 @@ void Controller::_updateDisplayAxes( int targetIndex ){ void Controller::updateZoom( double centerX, double centerY, double zoomFactor ){ - - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size()){ + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ //Remember where the user clicked QPointF clickPtScreen( centerX, centerY); bool validImage = false; - QPointF clickPtImageOld = m_datas[imageIndex]->_getImagePt( clickPtScreen, &validImage ); + QPointF clickPtImageOld = m_datas[dataIndex]->_getImagePt( clickPtScreen, &validImage ); if ( validImage ){ //Set the zoom double newZoom = 1; - double oldZoom = m_datas[imageIndex]->_getZoom(); + double oldZoom = m_datas[dataIndex]->_getZoom(); if ( zoomFactor < 0 ) { newZoom = oldZoom / 0.9; } @@ -1307,13 +1472,13 @@ void Controller::updateZoom( double centerX, double centerY, double zoomFactor ) } // what is the new image pixel under the mouse cursor? - QPointF clickPtImageNew = m_datas[imageIndex]->_getImagePt( clickPtScreen, &validImage ); + QPointF clickPtImageNew = m_datas[dataIndex]->_getImagePt( clickPtScreen, &validImage ); // calculate the difference QPointF delta = clickPtImageOld - clickPtImageNew; // add the delta to the current center - QPointF currCenter = m_datas[imageIndex]->_getCenter(); + QPointF currCenter = m_datas[dataIndex]->_getCenter(); QPointF newCenter = currCenter + delta; for ( std::shared_ptr data : m_datas ){ data->_setPan( newCenter.x(), newCenter.y() ); @@ -1324,10 +1489,10 @@ void Controller::updateZoom( double centerX, double centerY, double zoomFactor ) } void Controller::updatePan( double centerX , double centerY){ - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size()){ + int dataIndex = _getDataIndex(); + if ( dataIndex >= 0 ){ bool validImage = false; - QPointF oldImageCenter = m_datas[imageIndex]-> _getImagePt( { centerX, centerY }, &validImage ); + QPointF oldImageCenter = m_datas[dataIndex]-> _getImagePt( { centerX, centerY }, &validImage ); if ( validImage ){ double imageX = oldImageCenter.x(); @@ -1341,46 +1506,6 @@ void Controller::updatePan( double centerX , double centerY){ } } -QStringList Controller::getCenterPixel() { - int imageIndex = m_selectImage->getIndex(); - QStringList returnValue = QStringList( "null" ); - if ( imageIndex >= 0 && imageIndex < m_datas.size() ) { - QPointF center = m_datas[imageIndex]->_getCenter(); - returnValue = QStringList( QString::number( center.x() ) ); - returnValue.append( QString::number( center.y() ) ); - } - return returnValue; -} - -QStringList Controller::getImageDimensions( ){ - QStringList result; - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size() ){ - int dimensions = m_datas[imageIndex]->_getDimensions(); - for ( int i = 0; i < dimensions; i++ ) { - int d = m_datas[imageIndex]->_getDimension( i ); - result.append( QString::number( d ) ); - } - } - else { - result = QStringList(""); - } - return result; -} - -QStringList Controller::getOutputSize( ){ - QStringList result; - int imageIndex = m_selectImage->getIndex(); - if ( imageIndex >= 0 && imageIndex < m_datas.size() ){ - QSize outputSize = m_datas[imageIndex]->_getOutputSize(); - result.append( QString::number( outputSize.width() ) ); - result.append( QString::number( outputSize.height() ) ); - } - else { - result = QStringList(""); - } - return result; -} void Controller::_viewResize( const QSize& newSize ){ for ( int i = 0; i < m_datas.size(); i++ ){ diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index 5c80d1a3..df66a84a 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -82,12 +82,22 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void centerOnPixel( double imgX , double imgY); + + /** * Close the given image. * @param name an identifier for the image to close. */ QString closeImage( const QString& name ); + + /** + * Get the image pixel that is currently centered. + * @return a list of the x- and y-coordinates of the center pixel, + * or error information if the center pixel could not be obtained. + */ + QStringList getCenterPixel(); + /** * Return the coordinate system in use. * @return - an enumerated coordinate system type. @@ -153,10 +163,6 @@ class Controller: public QObject, public Carta::State::CartaObject, */ bool getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; - - - - std::vector> getDataSources(); /** @@ -184,13 +190,21 @@ class Controller: public QObject, public Carta::State::CartaObject, QString getPreferencesId() const; - /** - * Return a count of the number of images in the stack. - * @return the number of images in the stack. + * Return a count of the number of image layers in the stack. + * @return the number of image layers in the stack. */ + //Note: this will include image layers that the user may not see because they + //are hidden. int getStackedImageCount() const; + /** + * Returns the number of visibile image layers in the stack. + * @return a count of the number of image layers that have not been hidden + * and are available for the user to see. + */ + int getStackedImageCountVisible() const; + /** * Return the pixel coordinates corresponding to the given world coordinates. * @param ra the right ascension (in radians) of the world coordinates. @@ -252,6 +266,7 @@ class Controller: public QObject, public Carta::State::CartaObject, virtual QString getStateString( const QString& sessionId, SnapshotType type ) const Q_DECL_OVERRIDE; + /** * Center the image. */ @@ -263,17 +278,19 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void resetState( const QString& state ); + /** + * Reset the images that are loaded and other data associated state. + * @param state - the data state. + */ + virtual void resetStateData( const QString& state ) Q_DECL_OVERRIDE; + + /** * Reset the zoom to its original value. */ void resetZoom(); - /** - * Get the image pixel that is currently centered. - * @return a list of the x- and y-coordinates of the center pixel, - * or error information if the center pixel could not be obtained. - */ - QStringList getCenterPixel(); + /** * Save a copy of the full image in the current image view. @@ -333,11 +350,6 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void setZoomLevel( double zoomLevel ); - /** - * Reset the images that are loaded and other data associated state. - * @param state - the data state. - */ - virtual void resetStateData( const QString& state ) Q_DECL_OVERRIDE; /** @@ -347,6 +359,22 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString setClipValue( double clipValue ); + /** + * Specify a new image order. + * @param imageNames - a list specifying a new order for the images in + * a layer. + * @return an error message if the new image order could not be set; + * otherwise, an empty string. + */ + QString setImageOrder( const QStringList& imageNames ); + + /** + * Show/hide a particular layer in the stack. + * @param name - an identifier for a layer in the stack. + * @param visible - true if the layer should be visible; false otherwise. + */ + QString setImageVisibility( const QString& name, bool visible ); + /** * Change the pan of the current image. * @param imgX the x-coordinate for the center of the pan. @@ -355,7 +383,6 @@ class Controller: public QObject, public Carta::State::CartaObject, void updatePan( double imgX , double imgY); - /** * Update the zoom settings. * @param centerX the screen x-coordinate where the zoom was centered. @@ -378,6 +405,13 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void axesChanged(); + /** + * Notification that the channel/selection managed by this controller has + * changed. + * @param controller this Controller. + */ + void channelChanged( Controller* controller ); + /** * Notification that the image clip values have changed. * @param minPercentile - the new minimum clip percentile. @@ -392,12 +426,7 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void dataChanged(Controller* controller ); - /** - * Notification that the channel/selection managed by this controller has - * changed. - * @param controller this Controller. - */ - void channelChanged( Controller* controller ); + @@ -453,9 +482,16 @@ private slots: class Factory; + //Clear image statistics. + void _clearStatistics(); + std::vector _getFrameIndices( int imageIndex ) const; set _getAxesHidden() const; std::vector _getAxisZTypes() const; + //Get the actual data index of the selection with the given index. This method + //takes into account that some images may be hidden, i.e., temporarily not seen + //on the stack. + int _getDataIndex( ) const; //Provide default values for state. void _initializeState(); @@ -490,6 +526,7 @@ private slots: static const QString AUTO_CLIP; static const QString DATA; static const QString DATA_PATH; + static const QString IMAGE; static const QString REGIONS; static const QString CENTER; static const QString POINTER_MOVE; diff --git a/carta/cpp/core/Data/Image/ControllerData.cpp b/carta/cpp/core/Data/Image/ControllerData.cpp index c2e40ba4..0d6cf43f 100755 --- a/carta/cpp/core/Data/Image/ControllerData.cpp +++ b/carta/cpp/core/Data/Image/ControllerData.cpp @@ -4,6 +4,7 @@ #include "DataSource.h" #include "DrawSynchronizer.h" #include "Data/Preferences/PreferencesSave.h" +#include "Data/DataLoader.h" #include "Data/Util.h" #include "Data/Image/Grid/AxisMapper.h" #include "Data/Image/Grid/LabelFormats.h" @@ -26,6 +27,8 @@ namespace Carta { namespace Data { const QString ControllerData::CLASS_NAME = "ControllerData"; +const QString ControllerData::LAYER = "layer"; + class ControllerData::Factory : public Carta::State::CartaObjectFactory { public: @@ -64,13 +67,6 @@ void ControllerData::_clearData(){ } } -bool ControllerData::_contains(const QString& fileName) const { - bool representsData = false; - if ( m_dataSource ){ - representsData = m_dataSource->_contains( fileName ); - } - return representsData; -} void ControllerData::_displayAxesChanged(std::vector displayAxisTypes, const std::vector& frames ){ @@ -301,12 +297,43 @@ void ControllerData::_gridChanged( const Carta::State::StateInterface& state, bo } } +QString ControllerData::_getLayerString() const { + QStringList longNames; + longNames.append( _getFileName() ); + + DataLoader* dataLoader = Util::findSingletonObject(); + QStringList shortNames = dataLoader->getShortNames( longNames ); + + Carta::State::StateInterface layerState( ""); + layerState.insertValue( LAYER, shortNames[0] ); + layerState.insertValue( Util::VISIBLE, m_state.getValue(Util::VISIBLE) ); + return layerState.toString(); +} + void ControllerData::_initializeState(){ m_state.insertValue(DataSource::DATA_PATH, ""); + m_state.insertValue(Util::VISIBLE, true ); QString gridState = _getGridState().toString(); m_state.insertObject(DataGrid::GRID, gridState ); } +bool ControllerData::_isVisible() const { + return m_state.getValue(Util::VISIBLE); +} + +bool ControllerData::_isMatch( const QString& name ) const { + QStringList longNames; + longNames.append( _getFileName() ); + + DataLoader* dataLoader = Util::findSingletonObject(); + QStringList shortNames = dataLoader->getShortNames( longNames ); + bool matched = false; + if ( shortNames[0] == name ){ + matched = true; + } + return matched; +} + void ControllerData::_renderingDone( QImage image, Carta::Lib::VectorGraphics::VGList gridVG, @@ -561,6 +588,13 @@ void ControllerData::setColorAmounts( double newRed, double newGreen, double new } } +void ControllerData::_setVisible( bool visible ){ + bool oldVisible = m_state.getValue(Util::VISIBLE); + if ( visible != oldVisible ){ + m_state.setValue( Util::VISIBLE, visible ); + } +} + void ControllerData::_setPan( double imgX, double imgY ){ if ( m_dataSource ){ m_dataSource-> _setPan( imgX, imgY ); diff --git a/carta/cpp/core/Data/Image/ControllerData.h b/carta/cpp/core/Data/Image/ControllerData.h index 1e3d6905..fe865d73 100755 --- a/carta/cpp/core/Data/Image/ControllerData.h +++ b/carta/cpp/core/Data/Image/ControllerData.h @@ -88,7 +88,7 @@ Q_OBJECT virtual void setGamma( double gamma ) Q_DECL_OVERRIDE; static const QString CLASS_NAME; - + static const QString LAYER; virtual ~ControllerData(); @@ -116,12 +116,7 @@ private slots: private: void _clearData(); - /** - * Returns true if this data source manages the data corresponding - * to the fileName; false, otherwise. - * @param fileName a locator for data. - */ - bool _contains(const QString& fileName) const; + Carta::Lib::AxisInfo::KnownType _getAxisXType() const; Carta::Lib::AxisInfo::KnownType _getAxisYType() const; std::vector _getAxisZTypes() const; @@ -220,7 +215,7 @@ private slots: * @return true if the computed intensity is valid; otherwise false. */ bool _getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; - + QString _getLayerString() const; /** * Returns the pipeline responsible for rendering the image. * @retun the pipeline responsible for rendering the image. @@ -297,6 +292,18 @@ private slots: void _initializeState(); void _initializeSingletons( ); + /** + * Returns true if this layer is not hidden; false otherwise. + * @return true if the layer is visible; false otherwise. + */ + bool _isVisible() const; + + /** + * Returns true if the name identifies this layer; false otherwise. + * @return true if the name identifies this layer; false otherwise. + */ + bool _isMatch( const QString& name ) const; + /** * Loads the data source as a QImage. * @param frames - list of frames to load, one for each of the known axis types. @@ -348,6 +355,12 @@ private slots: //Note: The rendered contour set is an accumulation of all the contour sets. void _setContours( std::shared_ptr contours ); + /** + * Show/hide this layer. + * @param visible - true to show the layer; false to hide it. + */ + void _setVisible( bool visible ); + /** * Set the center for this image's display. * @param imgX the x-coordinate of the center. @@ -392,6 +405,7 @@ private slots: static bool m_registered; + std::unique_ptr m_dataGrid; std::shared_ptr m_dataContours; diff --git a/carta/cpp/core/Data/Image/DataSource.cpp b/carta/cpp/core/Data/Image/DataSource.cpp index 67cede19..15ddc19b 100755 --- a/carta/cpp/core/Data/Image/DataSource.cpp +++ b/carta/cpp/core/Data/Image/DataSource.cpp @@ -52,13 +52,6 @@ DataSource::DataSource() : m_renderService-> setPixelPipeline( m_pixelPipeline, m_pixelPipeline-> cacheId()); } -bool DataSource::_contains(const QString& fileName) const { - bool representsData = false; - if ( m_fileName.endsWith(fileName)) { - representsData = true; - } - return representsData; -} int DataSource::_getFrameIndex( int sourceFrameIndex, const vector& sourceFrames ) const { int frameIndex = 0; diff --git a/carta/cpp/core/Data/Image/DataSource.h b/carta/cpp/core/Data/Image/DataSource.h index 52f1975d..010a6dc5 100755 --- a/carta/cpp/core/Data/Image/DataSource.h +++ b/carta/cpp/core/Data/Image/DataSource.h @@ -92,13 +92,6 @@ Q_OBJECT private: - /** - * Returns true if this data source manages the data corresponding - * to the fileName; false, otherwise. - * @param fileName a locator for data. - */ - bool _contains(const QString& fileName) const; - /** * Resizes the frame indices to fit the current image. * @param sourceFrames - a list of current image frames. diff --git a/carta/cpp/core/Histogram/HistogramGenerator.cpp b/carta/cpp/core/Histogram/HistogramGenerator.cpp index c0fca8fe..37504391 100755 --- a/carta/cpp/core/Histogram/HistogramGenerator.cpp +++ b/carta/cpp/core/Histogram/HistogramGenerator.cpp @@ -241,9 +241,15 @@ void HistogramGenerator::setStyle( QString style ){ m_histogram->setDrawStyle( style ); } -QImage * HistogramGenerator::toImage( ) const { +QImage * HistogramGenerator::toImage( int width, int height ) const { QwtPlotRenderer renderer; - QImage * histogramImage =new QImage(m_width, m_height, QImage::Format_RGB32); + if ( width <= 0 ){ + width = m_width; + } + if ( height <= 0 ){ + height = m_height; + } + QImage * histogramImage =new QImage(width, height, QImage::Format_RGB32); renderer.renderTo(m_plot, *histogramImage ); return histogramImage; } diff --git a/carta/cpp/core/Histogram/HistogramGenerator.h b/carta/cpp/core/Histogram/HistogramGenerator.h index 39e70302..581f4b29 100755 --- a/carta/cpp/core/Histogram/HistogramGenerator.h +++ b/carta/cpp/core/Histogram/HistogramGenerator.h @@ -155,8 +155,10 @@ class HistogramGenerator{ /** * Returns the QImage reflection the current state of the histogram. * @return QImage the histogram image. + * @param width - the width of the desired image. + * @param height - the height of the desired image. */ - QImage * toImage() const; + QImage * toImage( int width = 0, int height = 0) const; /** * Gets new clips calculated in histogram selection and updates them on the plot diff --git a/carta/cpp/core/State/StateInterface.cpp b/carta/cpp/core/State/StateInterface.cpp index 81c94b5c..cbec78f4 100644 --- a/carta/cpp/core/State/StateInterface.cpp +++ b/carta/cpp/core/State/StateInterface.cpp @@ -92,9 +92,12 @@ const QString StateInterface::INDEX = "index"; StateInterface::StateInterface (const QString & path, const QString& type, const QString& initialState ) : impl_p (new StateInterfaceImpl (path) ) { - insertValue( OBJECT_TYPE, type ); - insertValue(INDEX, 0 ); - insertValue(FLUSH_STATE, false ); + if ( path.length() > 0 || type.length() > 0 ){ + insertValue( OBJECT_TYPE, type ); + insertValue(FLUSH_STATE, false ); + insertValue(INDEX, 0 ); + } + if ( initialState.trimmed().size() > 0 ){ flushStateImpl( initialState ); diff --git a/carta/html5/common/skel/source/class/skel/Application.js b/carta/html5/common/skel/source/class/skel/Application.js index e8465237..9998443b 100644 --- a/carta/html5/common/skel/source/class/skel/Application.js +++ b/carta/html5/common/skel/source/class/skel/Application.js @@ -234,10 +234,10 @@ qx.Class.define( "skel.Application", this._showSaveBrowser(message); }, this ); qx.event.message.Bus.subscribe( "cancelSaveBrowser", function( message ){ - this._hideWidget( this.m_saveBrowser ); + this._hideWidget( this.m_saveBrowser ); }, this ); qx.event.message.Bus.subscribe( "shareSession", function( ev ){ - this.m_statusBar.updateSessionSharing( ev.getData() ); + this.m_statusBar.updateSessionSharing( ev ); }, this ); qx.event.message.Bus.subscribe( "showSessionRestoreDialog", function(message){ this._showSessionRestore( message ); diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandData.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandData.js index b644b994..d9bb01a1 100644 --- a/carta/html5/common/skel/source/class/skel/Command/Data/CommandData.js +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandData.js @@ -20,16 +20,30 @@ qx.Class.define("skel.Command.Data.CommandData", { this.m_cmds = []; this.m_cmds[0] = skel.Command.Data.CommandDataOpen.getInstance(); this.m_cmds[1] = skel.Command.Data.CommandDataClose.getInstance(); + this.m_cmds[2] = skel.Command.Data.CommandDataHide.getInstance(); + this.m_cmds[3] = skel.Command.Data.CommandDataShow.getInstance(); this.setValue( this.m_cmds ); }, members : { + + /** + * Called when an image window has been selected and the images available + * for closing, etc, have changed. + */ + datasChanged : function(){ + for ( var i = 1; i < this.m_cmds.length; i++ ){ + this.m_cmds[i].datasChanged(); + } + }, + //Note: Overriden so that the _resetEnabled method of CommandDataClose will //be called, giving it the opportunity to add CommandDataCloseImage commands //based on the window(s) selected. _resetEnabled : function( ){ arguments.callee.base.apply(this, arguments); this.m_cmds[1]._resetEnabled(); + this.m_cmds[2]._resetEnabled(); var enabled = this.isEnabled(); for ( var i = 0; i < this.m_cmds.length; i++ ){ this.m_cmds[i].setEnabled( enabled ); diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataClose.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataClose.js index 22047167..527ce2e0 100644 --- a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataClose.js +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataClose.js @@ -29,7 +29,7 @@ qx.Class.define("skel.Command.Data.CommandDataClose", { // Needed so that if data is added to an image that is already selected, i.e., // enabled status has not changed, but data count has, the close image commands // will be updated. - closeChanged : function(){ + datasChanged : function(){ this._resetEnabled(); }, @@ -44,9 +44,9 @@ qx.Class.define("skel.Command.Data.CommandDataClose", { var dataCmd = skel.Command.Data.CommandData.getInstance(); for ( var i = 0; i < activeWins.length; i++ ){ if ( activeWins[i].isCmdSupported( dataCmd ) ){ - var closes = activeWins[i].getCloses(); + var closes = activeWins[i].getDatas(); for ( var j = 0; j < closes.length; j++ ){ - this.m_cmds[k] = new skel.Command.Data.CommandDataCloseImage( closes[j]); + this.m_cmds[k] = new skel.Command.Data.CommandDataCloseImage( closes[j].layer ); k++; } } diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHide.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHide.js new file mode 100644 index 00000000..51ac13a4 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHide.js @@ -0,0 +1,69 @@ +/** + * Container for commands to hide specific images. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.Command.Data.CommandDataHide", { + extend : skel.Command.CommandComposite, + type : "singleton", + + /** + * Constructor. + */ + construct : function( ) { + this.base( arguments, "Hide" ); + this.m_cmds = []; + this.setEnabled( false ); + this.m_global = false; + this.setToolTipText("Hide data..."); + this.setValue( this.m_cmds ); + }, + + members : { + /** + * The commands to hide individual images have changed. + */ + // Needed so that if data is added to an image that is already selected, i.e., + // enabled status has not changed, but data count has, the hide image commands + // will be updated. + datasChanged : function(){ + this._resetEnabled(); + }, + + _resetEnabled : function( ){ + arguments.callee.base.apply( this, arguments ); + //Dynamically create hide image commands based on the active windows. + this.m_cmds = []; + var activeWins = skel.Command.Command.m_activeWins; + if ( activeWins !== null && activeWins.length > 0 ){ + //Use the first one in the list that supports this cmd. + var k = 0; + var dataCmd = skel.Command.Data.CommandData.getInstance(); + for ( var i = 0; i < activeWins.length; i++ ){ + if ( activeWins[i].isCmdSupported( dataCmd ) ){ + var closes = activeWins[i].getDatas(); + for ( var j = 0; j < closes.length; j++ ){ + if ( closes[j].visible ){ + this.m_cmds[k] = new skel.Command.Data.CommandDataHideImage( closes[j].layer); + k++; + } + } + } + } + } + if ( this.m_cmds.length > 0 ){ + this.setEnabled( true ); + } + else { + this.setEnabled( false ); + } + this.setValue( this.m_cmds ); + qx.event.message.Bus.dispatch(new qx.event.message.Message( + "commandsChanged", null)); + } + } + +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHideImage.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHideImage.js new file mode 100644 index 00000000..e1988e7b --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHideImage.js @@ -0,0 +1,48 @@ +/** + * Command to hide a specific image in the stack. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.Command.Data.CommandDataHideImage", { + extend : skel.Command.Command, + + /** + * Constructor. + * @param label {String} name of the image to hide. + */ + construct : function( label ) { + var path = skel.widgets.Path.getInstance(); + var cmd = path.SEP_COMMAND + path.HIDE_IMAGE; + this.base( arguments, label, cmd); + this.m_toolBarVisible = false; + this.setEnabled( true ); + this.m_global = false; + this.setToolTipText("Hide the image " + this.getLabel() + "."); + }, + + members : { + + doAction : function( vals, undoCB ){ + var path = skel.widgets.Path.getInstance(); + var label = this.getLabel(); + var params = this.m_params + label; + var errMan = skel.widgets.ErrorHandler.getInstance(); + if ( skel.Command.Command.m_activeWins.length > 0 ){ + for ( var i = 0; i < skel.Command.Command.m_activeWins.length; i++ ){ + var windowInfo = skel.Command.Command.m_activeWins[i]; + var id = windowInfo.getIdentifier(); + this.sendCommand( id, params, undoCB ); + } + errMan.clearErrors(); + } + else { + errMan.updateErrors( "Error hiding image."); + } + }, + + m_params : "image:" + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js new file mode 100644 index 00000000..411d5bf4 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js @@ -0,0 +1,69 @@ +/** + * Container for commands to show specific images that have been hidden. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.Command.Data.CommandDataShow", { + extend : skel.Command.CommandComposite, + type : "singleton", + + /** + * Constructor. + */ + construct : function( ) { + this.base( arguments, "Show" ); + this.m_cmds = []; + this.setEnabled( false ); + this.m_global = false; + this.setToolTipText("Show data..."); + this.setValue( this.m_cmds ); + }, + + members : { + /** + * The commands to show individual images have changed. + */ + // Needed so that if data is added to an image that is already selected, i.e., + // enabled status has not changed, but data count has, the close image commands + // will be updated. + datasChanged : function(){ + this._resetEnabled(); + }, + + _resetEnabled : function( ){ + arguments.callee.base.apply( this, arguments ); + //Dynamically create close image commands based on the active windows. + this.m_cmds = []; + var activeWins = skel.Command.Command.m_activeWins; + if ( activeWins !== null && activeWins.length > 0 ){ + //Use the first one in the list that supports this cmd. + var k = 0; + var dataCmd = skel.Command.Data.CommandData.getInstance(); + for ( var i = 0; i < activeWins.length; i++ ){ + if ( activeWins[i].isCmdSupported( dataCmd ) ){ + var closes = activeWins[i].getDatas(); + for ( var j = 0; j < closes.length; j++ ){ + if ( !closes[j].visible ){ + this.m_cmds[k] = new skel.Command.Data.CommandDataShowImage( closes[j].layer); + k++; + } + } + } + } + } + if ( this.m_cmds.length > 0 ){ + this.setEnabled( true ); + } + else { + this.setEnabled( false ); + } + this.setValue( this.m_cmds ); + qx.event.message.Bus.dispatch(new qx.event.message.Message( + "commandsChanged", null)); + } + } + +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShowImage.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShowImage.js new file mode 100644 index 00000000..b842a451 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShowImage.js @@ -0,0 +1,48 @@ +/** + * Command to make visible a specific image in the stack. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.Command.Data.CommandDataShowImage", { + extend : skel.Command.Command, + + /** + * Constructor. + * @param label {String} name of the image to show. + */ + construct : function( label ) { + var path = skel.widgets.Path.getInstance(); + var cmd = path.SEP_COMMAND + path.SHOW_IMAGE; + this.base( arguments, label, cmd); + this.m_toolBarVisible = false; + this.setEnabled( true ); + this.m_global = false; + this.setToolTipText("Show the image " + this.getLabel() + "."); + }, + + members : { + + doAction : function( vals, undoCB ){ + var path = skel.widgets.Path.getInstance(); + var label = this.getLabel(); + var params = this.m_params + label; + var errMan = skel.widgets.ErrorHandler.getInstance(); + if ( skel.Command.Command.m_activeWins.length > 0 ){ + for ( var i = 0; i < skel.Command.Command.m_activeWins.length; i++ ){ + var windowInfo = skel.Command.Command.m_activeWins[i]; + var id = windowInfo.getIdentifier(); + this.sendCommand( id, params, undoCB ); + } + errMan.clearErrors(); + } + else { + errMan.updateErrors( "Error showing image."); + } + }, + + m_params : "image:" + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/Command/Session/CommandShare.js b/carta/html5/common/skel/source/class/skel/Command/Session/CommandShare.js index 030d9f4b..d2f82c83 100644 --- a/carta/html5/common/skel/source/class/skel/Command/Session/CommandShare.js +++ b/carta/html5/common/skel/source/class/skel/Command/Session/CommandShare.js @@ -17,7 +17,10 @@ qx.Class.define("skel.Command.Session.CommandShare", { members : { doAction : function( vals, undoCB ){ - qx.event.message.Bus.dispatch( new qx.event.message.Message("shareSession", this.getValue())); + var data = { + share: vals + } + qx.event.message.Bus.dispatch( new qx.event.message.Message("shareSession", data)); }, getType : function(){ diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/ImageControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/ImageControls.js index 02a3e9f5..c7dd483d 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/ImageControls.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/ImageControls.js @@ -39,6 +39,9 @@ qx.Class.define("skel.widgets.Image.ImageControls", { this.m_contourControls = new skel.widgets.Image.Contour.ContourControls(); this.add( this.m_contourControls ); + this.m_stackControls = new skel.widgets.Image.Stack.StackControls(); + //this.add( this.m_stackControls ); + }, @@ -49,11 +52,13 @@ qx.Class.define("skel.widgets.Image.ImageControls", { setId : function( imageId ){ this.m_gridControls.setId( imageId ); this.m_contourControls.setId( imageId ); + this.m_stackControls.setId( imageId ); }, m_id : null, m_gridControls : null, - m_contourControls : null + m_contourControls : null, + m_stackControls : null } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js new file mode 100755 index 00000000..ed469315 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js @@ -0,0 +1,236 @@ +/** + * List that supports drag and drop. + */ + + +qx.Class.define("skel.widgets.Image.Stack.DragDropList", { + extend : qx.ui.core.Widget, + + /** + * Constructor. + */ + construct : function( width ) { + this.base(arguments); + this._init( width ); + }, + + events : { + "listReordered" : "qx.event.type.Data" + }, + + members : { + + /** + * Initializes the UI. + */ + _init : function( width ) { + this._setLayout( new qx.ui.layout.VBox(1) ); + this.m_listContainer = new qx.ui.container.Composite(); + this.m_listContainer.setLayout( new qx.ui.layout.Basic() ); + this._add( this.m_listContainer ); + + this._initList( width ); + this._initDragIndicator( width ); + }, + + /** + * Initialize the drag indicator. + * @param width {Number} - how wide to make the drag marker. + */ + _initDragIndicator : function( width ){ + this.m_dragMarker = new qx.ui.core.Widget(); + this.m_dragMarker.setDecorator(new qx.ui.decoration.Decorator().set({ + widthTop: 2, + styleTop: "solid", + colorTop: "gray" + })); + this.m_dragMarker.setHeight(2); + this.m_dragMarker.setWidth( width ); + this.m_dragMarker.setOpacity(0.5); + this.m_dragMarker.setZIndex(100); + this.m_dragMarker.setDroppable(true); + this.m_listContainer.add(this.m_dragMarker, {left:0,top:-5}); + + this.m_dragMarker.addListener( "drop", function(e){ + this._reorderList( this.m_dragMarker ); + this.m_drag = false; + }, this ); + }, + + /** + * Initialize the list. + * @param width {Number} - how wide to make the list. + */ + _initList : function( width ){ + this.m_list = new qx.ui.form.List(); + this.m_list.setMinWidth( width ); + this.m_list.setSelectionMode("multi"); + this.m_list.setDraggable(true); + this.m_list.setDroppable( true ); + this.m_listContainer.add( this.m_list, {left:0,top:0}); + + //Support moves. + this.m_list.addListener("dragstart", function(e) { + e.addAction("move"); + this.m_drag = true; + }, this); + + this.m_list.addListener("dragend", function(e){ + if ( this.m_dragMarker !== null ){ + //Hide the drag marker + if ( this.m_dragMarker !== null ){ + this.m_dragMarker.setLayoutProperties( {left:0, top:-5} ); + } + this.m_drag = false; + } + }, this ); + + this.m_list.addListener("drag", function(e){ + this._doDrag(e); + }, this ); + + this.m_list.addListener("dragover", function(e){ + // Stop when the dragging comes from outside + if (e.getRelatedTarget()) { + e.preventDefault(); + } + }, this ); + + this.m_list.addListener("drop", function(e) { + var orig = e.getOriginalTarget(); + this._reorderList(orig); + this.m_drag = false; + }, this); + }, + + /** + * Update the drag marker while the drag is in progress. + * @param e {qx.event.Event} - the drag event. + */ + _doDrag : function( e ){ + if ( !this.m_drag ){ + return; + } + var orig = e.getOriginalTarget(); + + //Determine if we are at a list item or at the end of + //the list. + var listItem =false; + if (orig instanceof qx.ui.form.ListItem) { + listItem = true; + } + + var origCoords = orig.getContentLocation(); + var listCoords = this.m_list.getContentLocation(); + var origTop = 0; + if ( listItem ){ + //Between list items. + origTop = origCoords.top - listCoords.top; + } + else { + //At the end of the list. + var childs = this.m_list.getChildren(); + var lastItem = childs[childs.length - 1]; + var lastItemBottom = this.m_list.getItemBottom( lastItem ); + origTop = lastItemBottom + 4; + } + + //Position the drag marker + if ( this.m_dragMarker !== null ){ + this.m_dragMarker.setLayoutProperties( {left:0, top:origTop} ); + } + }, + + /** + * Reorder the list. + * @param orig {Object} - the list item where things should be dropped + * or the drag marker if we are dropping at the end of the list. + */ + _reorderList : function( orig ){ + // Only continue if the target is a list item. + var listItem = false; + if (orig instanceof qx.ui.form.ListItem) { + listItem = true; + } + + var sel = this.m_list.getSortedSelection(); + var lastChild = null; + //If we are not at a list item, we need to find the end + //of the list. + if ( !listItem ){ + var children = this.m_list.getChildren(); + if ( children.length > 0 ){ + lastChild = children[children.length - 1]; + } + } + for (var i = 0; i < sel.length; i++) { + if ( listItem ){ + //At a list item. + this.m_list.addBefore(sel[i], orig); + } + else { + //Add at the end of the list + this.m_list.addAfter( sel[i], lastChild ); + } + } + + var names = []; + var children = this.m_list.getChildren(); + for ( var j = 0; j < children.length; j++ ){ + names[j] = children[j].getLabel(); + } + var data = { + "listItems" : names + }; + this.fireDataEvent( "listReordered", data ); + }, + + /** + * Update the items in the list. + * @param items {Array} - a list of strings. + */ + setListItems : function( items ){ + this.m_list.removeAll(); + var dataCount = items.length; + for ( var i = 0; i < dataCount; i++ ){ + var listItem = new qx.ui.form.ListItem( items[i].layer ); + this.m_list.add( listItem ); + var visible = items[i].visible; + var contextMenu = new qx.ui.menu.Menu(); + + //Close button + var closeCmd = new skel.Command.Data.CommandDataCloseImage( items[i].layer ); + var closeButton = new qx.ui.menu.Button( "Close"); + closeButton.addListener( "execute", function(){ + this.doAction( true, function(){} ); + }, closeCmd ); + contextMenu.add( closeButton ); + + if ( visible ){ + //Hide button + var hideCmd = new skel.Command.Data.CommandDataHideImage( items[i].layer ); + var hideButton = new qx.ui.menu.Button( "Hide"); + hideButton.addListener( "execute", function(){ + this.doAction( true, function(){}); + }, hideCmd ); + contextMenu.add( hideButton ); + } + else { + //Show button + var showCmd = new skel.Command.Data.CommandDataShowImage( items[i].layer ); + var showButton = new qx.ui.menu.Button( "Show"); + showButton.addListener( "execute", function(){ + this.doAction( true, function(){}); + }, showCmd ); + contextMenu.add( showButton ); + } + listItem.setContextMenu(contextMenu); + } + }, + + m_list : null, + m_dragMarker : null, + m_drag : false, + m_listContainer : null + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js new file mode 100755 index 00000000..32c665ff --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js @@ -0,0 +1,90 @@ +/** + * Displays controls for customizing the image stack. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.widgets.Image.Stack.StackControls", { + extend : qx.ui.tabview.Page, + + /** + * Constructor. + */ + construct : function( ) { + this.base(arguments, "Stack", ""); + this.m_connector = mImport("connector"); + this._init(); + }, + + members : { + + /** + * Callback for a change in stack settings. + */ + _controlsChangedCB : function(){ + var val = this.m_sharedVar.get(); + if ( val ){ + try { + var controls = JSON.parse( val ); + this.m_imageList.setListItems( controls.data ); + + var errorMan = skel.widgets.ErrorHandler.getInstance(); + errorMan.clearErrors(); + } + catch( err ){ + console.log( "Stack controls could not parse: "+val+" error: "+err ); + } + } + }, + + /** + * Initializes the UI. + */ + _init : function( ) { + this.setPadding( 0, 0, 0, 0 ); + this._setLayout( new qx.ui.layout.HBox(1)); + this.m_imageList = new skel.widgets.Image.Stack.DragDropList( 300 ); + this.m_imageList.addListener( "listReordered", this._sendReorderCmd, this ); + this._add( this.m_imageList ); + }, + + /** + * Register to get updates on stack settings from the server. + */ + _registerControls : function(){ + var path = skel.widgets.Path.getInstance(); + var dataPath = this.m_id + path.SEP + "data"; + this.m_sharedVar = this.m_connector.getSharedVar( dataPath ); + this.m_sharedVar.addCB(this._controlsChangedCB.bind(this)); + this._controlsChangedCB(); + }, + + /** + * Send a command to the server to reorder the images in the stack. + * @param msg {Array} - a list specifying the new image order. + */ + _sendReorderCmd : function( msg ){ + var imageList = msg.getData().listItems; + var params = "images:"+imageList.join(";"); + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setImageOrder"; + this.m_connector.sendCommand( cmd, params, function(){}); + }, + + /** + * Send a command to the server to get the stack control id. + * @param imageId {String} the server side id of the image object. + */ + setId : function( imageId ){ + this.m_id = imageId; + this._registerControls(); + }, + + m_id : null, + m_connector : null, + m_sharedVar : null, + m_imageList : null + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Menu/StatusBar.js b/carta/html5/common/skel/source/class/skel/widgets/Menu/StatusBar.js index ba59fafa..960274e7 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Menu/StatusBar.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Menu/StatusBar.js @@ -240,7 +240,8 @@ qx.Class.define("skel.widgets.Menu.StatusBar", { _shareSessionCB : function(url, error) { if (url !== null) { this.setSharedUrl(url); - } else if (error) { + } + else if (error) { this.showErrors(error); } }, @@ -249,14 +250,15 @@ qx.Class.define("skel.widgets.Menu.StatusBar", { * Initiate or revoke a shared session. * @param sessionShared {Boolean} whether the session should be shared or unshared. */ - updateSessionSharing : function(sessionShared) { + updateSessionSharing : function(msg) { var con = mImport("connector"); var statusCopy = this; - if (sessionShared) { + if (msg.getData().share) { con.shareSession(function(url, error) { statusCopy._shareSessionCB(url, error); }, "a", null, 60*60*1000000000); - } else { + } + else { con.unShareSession(function(error) { statusCopy.showErrors(error); }); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Path.js b/carta/html5/common/skel/source/class/skel/widgets/Path.js index 81e95a58..0c38ed58 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Path.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Path.js @@ -65,6 +65,7 @@ qx.Class.define("skel.widgets.Path", { FONTS : "", GRID_PLUGIN : "GridControls", HIDDEN : "Hidden", + HIDE_IMAGE : "hideImage", HISTOGRAM_PLUGIN : "Histogram", LABEL_FORMATS : "", LAYOUT : "", @@ -84,6 +85,7 @@ qx.Class.define("skel.widgets.Path", { SEP : "/", SEP_COMMAND : ":", SETTINGS : "", + SHOW_IMAGE : "showImage", SNAPSHOTS : "", STATE_LAYOUT : "Layout", STATE_SESSION : "Session", diff --git a/carta/html5/common/skel/source/class/skel/widgets/Util.js b/carta/html5/common/skel/source/class/skel/widgets/Util.js index 4e35fc63..33c0bd8d 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Util.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Util.js @@ -164,9 +164,9 @@ qx.Class.define("skel.widgets.Util", { checkBox = new qx.ui.form.CheckBox(); } checkBox.setCommand( cmd ); - var labelFunction = function( cmd, button){ + var labelFunction = function( cmd, checkBox){ return function(){ - cmd.doAction( button.getValue(), null); + cmd.doAction( checkBox.getValue(), null); }; }; checkBox.addListener( "changeValue", labelFunction( cmd,checkBox), checkBox); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Window/DisplayWindow.js b/carta/html5/common/skel/source/class/skel/widgets/Window/DisplayWindow.js index 004f57c5..31c32e65 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Window/DisplayWindow.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Window/DisplayWindow.js @@ -125,7 +125,7 @@ qx.Class.define("skel.widgets.Window.DisplayWindow", { * Overriden by subclasses to return a list of data that can be closed. * @return {Array} a list of data that could be closed. */ - getCloses : function(){ + getDatas : function(){ return []; }, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Window/DisplayWindowImage.js b/carta/html5/common/skel/source/class/skel/widgets/Window/DisplayWindowImage.js index 01441288..1ee962c6 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Window/DisplayWindowImage.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Window/DisplayWindowImage.js @@ -92,7 +92,7 @@ qx.Class.define("skel.widgets.Window.DisplayWindowImage", { * Return the list of data that is currently open and could be closed. * @return {Array} a list of images that could be closed. */ - getCloses : function(){ + getDatas : function(){ return this.m_datas; }, @@ -288,20 +288,31 @@ qx.Class.define("skel.widgets.Window.DisplayWindowImage", { var winObj = JSON.parse( val ); this.m_datas = []; //Add close menu buttons for all the images that are loaded. - if ( winObj.data && winObj.data.length > 0){ - for ( var i = 0; i < winObj.data.length; i++ ){ - this.m_datas[i] = winObj.data[i]; + var dataObjs = winObj.data; + var visibleData = false; + if ( dataObjs ){ + for ( var j = 0; j < dataObjs.length; j++ ){ + if ( dataObjs[j].visible ){ + visibleData = true; + } } - this._dataLoadedCB(); } - else { + if ( dataObjs && dataObjs.length > 0 ){ + for ( var i = 0; i < dataObjs.length; i++ ){ + this.m_datas[i] = dataObjs[i]; + } + if ( visibleData ){ + this._dataLoadedCB(); + } + } + if ( !visibleData ){ //No images to show so set the view hidden. if ( this.m_view !== null ){ this.m_view.setVisibility( "hidden" ); } } - var closeCmd = skel.Command.Data.CommandDataClose.getInstance(); - closeCmd.closeChanged(); + var dataCmd = skel.Command.Data.CommandData.getInstance(); + dataCmd.datasChanged(); this._initContextMenu(); } catch( err ){ diff --git a/carta/html5/server/serverConnector.js b/carta/html5/server/serverConnector.js index 33a34849..fe13c88a 100644 --- a/carta/html5/server/serverConnector.js +++ b/carta/html5/server/serverConnector.js @@ -258,8 +258,12 @@ callback( getUrl ); } else { - window.alert( 'An error occurred creating the share URL: ' + exception.description ); - callback( null, exception.description ); + var msg="An error occurred creating the share URL"; + if ( exception !== null ){ + msg = msg + ": "+exception.description; + } + window.alert( msg ); + callback( null, msg ); } } ); m_lastShareUrl = ""; From 2895f2772661af0b2d043d6d2a2b635d06076970 Mon Sep 17 00:00:00 2001 From: slovelan Date: Tue, 3 Nov 2015 13:29:30 -0700 Subject: [PATCH 20/37] Default nan settable. --- carta/cpp/core/Data/Colormap/ColorState.cpp | 375 ++++++++++++ carta/cpp/core/Data/Colormap/ColorState.h | 212 +++++++ carta/cpp/core/Data/Colormap/Colormap.cpp | 543 +++++++++--------- carta/cpp/core/Data/Colormap/Colormap.h | 150 ++--- carta/cpp/core/Data/Histogram/Histogram.cpp | 10 +- carta/cpp/core/Data/Histogram/Histogram.h | 14 +- carta/cpp/core/Data/IColoredView.h | 58 -- .../Data/Image/Contour/ContourControls.cpp | 177 ++++-- carta/cpp/core/Data/Image/Controller.cpp | 267 +++++---- carta/cpp/core/Data/Image/Controller.h | 78 +-- carta/cpp/core/Data/Image/ControllerData.cpp | 81 ++- carta/cpp/core/Data/Image/ControllerData.h | 84 +-- carta/cpp/core/Data/Image/DataSource.cpp | 48 +- carta/cpp/core/Data/Image/DataSource.h | 92 +-- carta/cpp/core/Data/Util.cpp | 21 +- carta/cpp/core/Data/Util.h | 12 +- carta/cpp/core/ImageRenderService.cpp | 54 +- carta/cpp/core/ImageRenderService.h | 20 + .../cpp/core/ScriptedClient/ScriptFacade.cpp | 4 +- carta/cpp/core/State/StateInterface.cpp | 7 +- carta/cpp/core/core.pro | 3 +- .../class/skel/widgets/Colormap/ColorMix.js | 2 +- .../class/skel/widgets/Colormap/Colormap.js | 2 +- .../skel/widgets/Colormap/PageColorMap.js | 4 +- .../class/skel/widgets/Colormap/PageNan.js | 159 +++++ .../class/skel/widgets/Colormap/Settings.js | 5 + .../class/skel/widgets/Image/ImageControls.js | 2 +- 27 files changed, 1737 insertions(+), 747 deletions(-) create mode 100644 carta/cpp/core/Data/Colormap/ColorState.cpp create mode 100644 carta/cpp/core/Data/Colormap/ColorState.h delete mode 100755 carta/cpp/core/Data/IColoredView.h create mode 100755 carta/html5/common/skel/source/class/skel/widgets/Colormap/PageNan.js diff --git a/carta/cpp/core/Data/Colormap/ColorState.cpp b/carta/cpp/core/Data/Colormap/ColorState.cpp new file mode 100644 index 00000000..8d61111b --- /dev/null +++ b/carta/cpp/core/Data/Colormap/ColorState.cpp @@ -0,0 +1,375 @@ +#include "ColorState.h" +#include "Colormaps.h" +#include "Data/Image/Controller.h" +#include "TransformsData.h" +#include "Data/Util.h" +#include "State/StateInterface.h" +#include "State/UtilState.h" +#include +#include +#include + +namespace Carta { + +namespace Data { + +const QString ColorState::CLASS_NAME = "ColorState"; +const QString ColorState::COLOR_MAP_NAME = "colorMapName"; +const QString ColorState::COLORED_OBJECT = "coloredObject"; +const QString ColorState::REVERSE = "reverse"; +const QString ColorState::INVERT = "invert"; +const QString ColorState::GLOBAL = "global"; +const QString ColorState::COLOR_MIX = "colorMix"; +const QString ColorState::INTENSITY_MIN = "intensityMin"; +const QString ColorState::INTENSITY_MAX = "intensityMax"; +const QString ColorState::NAN_COLOR = "nanColor"; +const QString ColorState::NAN_DEFAULT = "nanDefault"; +const QString ColorState::SCALE_1 = "scale1"; +const QString ColorState::SCALE_2 = "scale2"; +const QString ColorState::GAMMA = "gamma"; +const QString ColorState::SIGNIFICANT_DIGITS = "significantDigits"; +const QString ColorState::TRANSFORM_IMAGE = "imageTransform"; +const QString ColorState::TRANSFORM_DATA = "dataTransform"; + +Colormaps* ColorState::m_colors = nullptr; +TransformsData* ColorState::m_dataTransforms = nullptr; + +class ColorState::Factory : public Carta::State::CartaObjectFactory { + + public: + + Carta::State::CartaObject * create (const QString & path, const QString & id) + { + return new ColorState (path, id); + } + }; + +bool ColorState::m_registered = + Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new ColorState::Factory()); + +ColorState::ColorState( const QString& path, const QString& id): + CartaObject( CLASS_NAME, path, id ){ + _initializeDefaultState( m_state ); + _initializeStatics(); + _setErrorMargin(); +} + +QString ColorState::_getColorMap() const { + return m_state.getValue( COLOR_MAP_NAME ); +} + +QString ColorState::_getDataTransform() const { + return m_state.getValue( TRANSFORM_DATA ); +} + +double ColorState::_getGamma() const { + return m_state.getValue( GAMMA ); +} + +double ColorState::_getMixGreen() const { + QString greenLookup = Carta::State::UtilState::getLookup( COLOR_MIX, Util::GREEN ); + return m_state.getValue( greenLookup ); +} + +double ColorState::_getMixRed() const { + QString redLookup = Carta::State::UtilState::getLookup( COLOR_MIX, Util::RED ); + return m_state.getValue( redLookup ); +} + +double ColorState::_getMixBlue() const { + QString blueLookup = Carta::State::UtilState::getLookup( COLOR_MIX, Util::BLUE ); + return m_state.getValue( blueLookup ); +} + +int ColorState::_getNanGreen() const { + QString greenLookup = Carta::State::UtilState::getLookup( NAN_COLOR, Util::GREEN ); + return m_state.getValue( greenLookup ); +} + +int ColorState::_getNanRed() const { + QString redLookup = Carta::State::UtilState::getLookup( NAN_COLOR, Util::RED ); + return m_state.getValue( redLookup ); +} + +int ColorState::_getNanBlue() const { + QString blueLookup = Carta::State::UtilState::getLookup( NAN_COLOR, Util::BLUE ); + return m_state.getValue( blueLookup ); +} + +QString ColorState::getStateString( const QString& /*sessionId*/, SnapshotType type ) const{ + QString result(""); + if ( type == SNAPSHOT_PREFERENCES ){ + result = m_state.toString(); + } + return result; +} + + +void ColorState::_initializeDefaultState( Carta::State::StateInterface& state ){ + state.insertValue( COLOR_MAP_NAME, "Gray" ); + state.insertValue(REVERSE, false); + state.insertValue(INVERT, false ); + state.insertValue(GLOBAL, true ); + state.insertValue(NAN_DEFAULT, true ); + + state.insertValue(GAMMA, 1.0 ); + state.insertValue(SCALE_1, 0.0 ); + state.insertValue(SCALE_2, 0.0 ); + + //Color mix + state.insertObject( COLOR_MIX ); + QString redKey = Carta::State::UtilState::getLookup( COLOR_MIX, Util::RED ); + state.insertValue( redKey, 1 ); + QString greenKey = Carta::State::UtilState::getLookup( COLOR_MIX, Util::GREEN ); + state.insertValue( greenKey, 1 ); + QString blueKey = Carta::State::UtilState::getLookup( COLOR_MIX, Util::BLUE ); + state.insertValue( blueKey, 1 ); + + state.insertValue(SIGNIFICANT_DIGITS, 6 ); + state.insertValue(TRANSFORM_IMAGE, "Gamma"); + state.insertValue(TRANSFORM_DATA, "None"); + + //Nan color + state.insertObject( NAN_COLOR ); + QString redLookup = Carta::State::UtilState::getLookup( NAN_COLOR, Util::RED ); + state.insertValue( redLookup, 255 ); + QString blueLookup = Carta::State::UtilState::getLookup( NAN_COLOR, Util::BLUE ); + state.insertValue( blueLookup, 0 ); + QString greenLookup = Carta::State::UtilState::getLookup( NAN_COLOR, Util::GREEN ); + state.insertValue( greenLookup, 0 ); + QString alphaLookup = Carta::State::UtilState::getLookup( NAN_COLOR, Util::ALPHA ); + state.insertValue( alphaLookup, 255 ); + +} + + + +void ColorState::_initializeStatics(){ + //Load the available color maps. + if ( m_colors == nullptr ){ + m_colors = Util::findSingletonObject(); + } + + //Data transforms + if ( m_dataTransforms == nullptr ){ + m_dataTransforms = Util::findSingletonObject(); + } +} + +bool ColorState::_isGlobal() const { + return m_state.getValue( GLOBAL ); +} + +bool ColorState::_isNanDefault() const { + return m_state.getValue( NAN_DEFAULT ); +} + +bool ColorState::_isReversed() const { + return m_state.getValue( REVERSE ); +} + +bool ColorState::_isInverted() const { + return m_state.getValue( INVERT ); +} + + +void ColorState::_replicateTo( Carta::State::StateInterface& otherState ){ + QString colorMapName = m_state.getValue(COLOR_MAP_NAME ); + otherState.setValue(COLOR_MAP_NAME, colorMapName ); + bool global = _isGlobal(); + otherState.setValue( GLOBAL, global ); + bool inverted = _isInverted(); + otherState.setValue( INVERT, inverted ); + bool reversed = _isReversed(); + otherState.setValue( REVERSE, reversed ); + bool nanDefault = _isNanDefault(); + otherState.setValue( NAN_DEFAULT, nanDefault ); + + //Color Mix + QString redKey = Carta::State::UtilState::getLookup( COLOR_MIX, Util::RED ); + double redPercent = m_state.getValue(redKey); + QString greenKey = Carta::State::UtilState::getLookup( COLOR_MIX, Util::GREEN ); + double greenPercent = m_state.getValue( greenKey ); + QString blueKey = Carta::State::UtilState::getLookup( COLOR_MIX, Util::BLUE ); + double bluePercent = m_state.getValue( blueKey ); + otherState.setValue( redKey, redPercent ); + otherState.setValue( greenKey, greenPercent ); + otherState.setValue( blueKey, bluePercent ); + + //Nan color + redKey = Carta::State::UtilState::getLookup( NAN_COLOR, Util::RED ); + int red = m_state.getValue(redKey); + greenKey = Carta::State::UtilState::getLookup( NAN_COLOR, Util::GREEN ); + int green = m_state.getValue( greenKey ); + blueKey = Carta::State::UtilState::getLookup( NAN_COLOR, Util::BLUE ); + int blue = m_state.getValue( blueKey ); + otherState.setValue( redKey, red ); + otherState.setValue( greenKey, green ); + otherState.setValue( blueKey, blue ); + + double gamma = m_state.getValue( GAMMA ); + otherState.setValue(GAMMA, gamma ); + QString dataTransform = m_state.getValue( TRANSFORM_DATA ); + otherState.setValue(TRANSFORM_DATA, dataTransform); +} + + +QString ColorState::_setColorMix( double redValue, double greenValue, double blueValue){ + QString result; + bool greenChanged = _setColorMix( Util::GREEN, greenValue, result ); + bool redChanged = _setColorMix( Util::RED, redValue, result ); + bool blueChanged = _setColorMix( Util::BLUE, blueValue, result ); + if ( redChanged || blueChanged || greenChanged ){ + emit colorStateChanged(); + } + return result; +} + +bool ColorState::_setColorMix( const QString& key, double colorPercent, QString& errorMsg ){ + bool colorChanged = false; + if ( colorPercent<0 || colorPercent > 1 ){ + errorMsg = errorMsg + "Mix color "+key + " must be in [0,1]. "; + } + else { + QString mixKey = Carta::State::UtilState::getLookup( COLOR_MIX, key ); + double oldColorPercent = m_state.getValue( mixKey ); + if ( abs( colorPercent - oldColorPercent ) >= 0.001f ){ + m_state.setValue( mixKey, colorPercent ); + colorChanged = true; + } + } + return colorChanged; +} + + +void ColorState::_setInvert( bool invert ){ + bool oldInvert = m_state.getValue(INVERT ); + if ( invert != oldInvert ){ + m_state.setValue(INVERT, invert ); + emit colorStateChanged(); + } +} + +QString ColorState::_setNanColor( int redValue, int greenValue, int blueValue){ + QString result; + bool greenChanged = _setNanColor( Util::GREEN, greenValue, result ); + bool redChanged = _setNanColor( Util::RED, redValue, result ); + bool blueChanged = _setNanColor( Util::BLUE, blueValue, result ); + if ( redChanged || blueChanged || greenChanged ){ + emit colorStateChanged(); + } + return result; +} + +bool ColorState::_setNanColor( const QString& key, int colorAmount, QString& errorMsg ){ + bool colorChanged = false; + if ( colorAmount<0 || colorAmount > 255 ){ + errorMsg = errorMsg + "Nan color "+key + " must be in [0,255]. "; + } + else { + QString nanKey = Carta::State::UtilState::getLookup( NAN_COLOR, key ); + double oldColorAmount = m_state.getValue( nanKey ); + if ( colorAmount != oldColorAmount ){ + m_state.setValue(nanKey, colorAmount ); + colorChanged = true; + } + } + return colorChanged; +} + +void ColorState::_setNanDefault( bool useDefault ){ + bool oldNanDefault = m_state.getValue( NAN_DEFAULT ); + if ( useDefault != oldNanDefault ){ + m_state.setValue( NAN_DEFAULT, useDefault ); + m_state.flushState(); + emit colorStateChanged(); + } +} + +void ColorState::_setReverse( bool reverse ){ + bool oldReverse = m_state.getValue(REVERSE); + if ( reverse != oldReverse ){ + m_state.setValue(REVERSE, reverse ); + m_state.flushState(); + emit colorStateChanged(); + } +} + + +QString ColorState::_setColorMap( const QString& colorMapStr ){ + QString mapName = m_state.getValue(COLOR_MAP_NAME); + QString result; + if ( m_colors != nullptr ){ + if( m_colors->isMap( colorMapStr ) ){ + if ( colorMapStr != mapName ){ + m_state.setValue(COLOR_MAP_NAME, colorMapStr ); + m_state.flushState(); + emit colorStateChanged(); + } + } + else { + result = "Invalid ColorState: " + colorMapStr; + } + } + return result; +} + +QString ColorState::_setGamma( double gamma ){ + QString result; + double oldGamma = m_state.getValue( GAMMA ); + + if ( qAbs( gamma - oldGamma) > m_errorMargin ){ + int digits = m_state.getValue(SIGNIFICANT_DIGITS); + m_state.setValue(GAMMA, Util::roundToDigits(gamma, digits )); + emit colorStateChanged(); + } + return result; +} + +QString ColorState::_setDataTransform( const QString& transformString ){ + QString result(""); + QString transformName = m_state.getValue(TRANSFORM_DATA); + if ( m_dataTransforms != nullptr ){ + QString actualTransform; + bool recognizedTransform = m_dataTransforms->isTransform( transformString, actualTransform ); + if( recognizedTransform ){ + if ( actualTransform != transformName ){ + m_state.setValue(TRANSFORM_DATA, actualTransform ); + m_state.flushState(); + emit colorStateChanged(); + } + } + else { + result = "Invalid data transform: " + transformString; + } + } + return result; +} + + +QString ColorState::_setSignificantDigits( int digits ){ + QString result; + if ( digits <= 0 ){ + result = "Invalid significant digits; must be positive: "+QString::number( digits ); + } + else { + if ( m_state.getValue(SIGNIFICANT_DIGITS) != digits ){ + m_state.setValue(SIGNIFICANT_DIGITS, digits ); + _setErrorMargin(); + emit colorStateChanged(); + } + } + return result; +} + +void ColorState::_setErrorMargin(){ + int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS ); + m_errorMargin = 1.0/qPow(10,significantDigits); +} + + +ColorState::~ColorState(){ + +} +} +} diff --git a/carta/cpp/core/Data/Colormap/ColorState.h b/carta/cpp/core/Data/Colormap/ColorState.h new file mode 100644 index 00000000..259c8089 --- /dev/null +++ b/carta/cpp/core/Data/Colormap/ColorState.h @@ -0,0 +1,212 @@ +/*** + * Stores the state for a color map. + */ + +#pragma once + +#include "State/ObjectManager.h" +#include "State/StateInterface.h" + + +namespace Carta { +namespace Lib { +namespace PixelPipeline { +class IColormapNamed; +} +} +} + +namespace Carta { + +namespace Data { + +class Colormaps; +class Controller; +class TransformsData; + +class ColorState : public QObject, public Carta::State::CartaObject { + + friend class Colormap; + friend class ControllerData; + + Q_OBJECT + +public: + + /** + * Return a string representing the colormap state of a particular type. + * @param sessionId - an identifier for the user's session. + * @param type - the type of state needed. + * @return a QString representing the corresponding colormap state. + */ + virtual QString getStateString( const QString& sessionId, SnapshotType type ) const Q_DECL_OVERRIDE; + + + virtual ~ColorState(); + const static QString CLASS_NAME; + +signals: + /* + * Emitted when the state of the color map has changed. + */ + void colorStateChanged(); + + +private: + + /** + * Return the name of the color map. + * @return - the name of the color map. + */ + QString _getColorMap() const; + + QString _getDataTransform() const; + + double _getGamma() const; + + double _getMixGreen() const; + double _getMixRed() const; + double _getMixBlue() const; + + int _getNanGreen() const; + int _getNanRed() const; + int _getNanBlue() const; + + void _initializeDefaultState( Carta::State::StateInterface& state ); + void _initializeStatics(); + + + bool _isGlobal() const; + bool _isNanDefault() const; + + /** + * Returns whether or not the colormap is reversed. + * @return true if the colormap is reversed; false otherwise. + */ + bool _isReversed() const; + + /** + * Returns whether or not the colormap is inverted. + * @return true if the colormap is inverted; false, otherwise. + */ + bool _isInverted() const; + + /** + * Copy the state of this ColorState into the other state. + * @param other - the StateInterface that should bee a copy of this one. + */ + void _replicateTo( Carta::State::StateInterface& other ); + + + /** + * Set the name of the current color map. + * @param colorMapName a unique identifier for the color map. + * @return error information if the color map was not successfully set. + */ + QString _setColorMap( const QString& colorMapName ); + + /** + * Set a color mix. + * @param redValue a number in [0,1] representing the amount of red in the mix. + * @param greenValue a number in [0,1] representing the amount of green in the mix. + * @param blueValue a number in [0,1] representing the amount of blue in the mix. + * @return error information if the color mix was not successfully set. + */ + QString _setColorMix( double redValue, double greenValue, double blueValue ); + + /** + * Helper function that sets an individual color in the mix. + * @param key - an identifier for the individual color. + * @param colorPercent - the color value in [0,1]. + * @param errorMsg - information about the problem if the color was not set. + * @return true if the color changed value; false otherwise. + */ + bool _setColorMix( const QString& key, double colorPercent, QString& errorMsg ); + + /** + * Set the name of the data transform. + * @param transformString a unique identifier for a data transform. + * @return error information if the data transfrom was not set. + */ + QString _setDataTransform( const QString& transformString); + + + /** + * Set the error margin used to determine when two doubles are equal. + */ + void _setErrorMargin(); + + /** + * Set the gamma color map parameter. + * @param gamma a parameter for color mapping. + * @return error information if gamma could not be set. + */ + QString _setGamma( double gamma ); + + /** + * Invert the current colormap. + * @param invert - true if the color map should be inverted; false otherwise.. + * @return error information if the color map was not successfully inverted. + */ + void _setInvert( bool invert ); + + QString _setNanColor( int redValue, int greenValue, int blueValue ); + + bool _setNanColor( const QString& key, int colorAmount, QString& errorMsg ); + + void _setNanDefault( bool defaultNan ); + /** + * Reverse the current colormap. + * @param reverse - true if the colormap should be reversed; false otherwise. + * @return error information if the color map was not successfully reversed. + */ + void _setReverse( bool reverse ); + + + /** + * Set the number of significant digits to use/display in colormap calculations. + * @param digits - the number of significant digits to use in calculations. + * @return an error message if the significant digits could not be sent; an + * empty string otherwise. + */ + QString _setSignificantDigits( int digits ); + + + static bool m_registered; + const static QString COLOR_MAP_NAME; + const static QString REVERSE; + const static QString INVERT; + const static QString GLOBAL; + const static QString COLORED_OBJECT; + const static QString COLOR_MIX; + + const static QString INTENSITY_MIN; + const static QString INTENSITY_MAX; + const static QString SCALE_1; + const static QString SCALE_2; + const static QString GAMMA; + const static QString NAN_COLOR; + const static QString NAN_DEFAULT; + const static QString SIGNIFICANT_DIGITS; + const static QString TRANSFORM_IMAGE; + const static QString TRANSFORM_DATA; + + ColorState( const QString& path, const QString& id ); + + class Factory; + + + //Supported color maps + static Colormaps* m_colors; + + //Supported data transforms + static TransformsData* m_dataTransforms; + + + double m_errorMargin; + + ColorState( const ColorState& other); + ColorState& operator=( const ColorState& other ); +}; +} +} diff --git a/carta/cpp/core/Data/Colormap/Colormap.cpp b/carta/cpp/core/Data/Colormap/Colormap.cpp index 7bdc1f54..d0dbbded 100644 --- a/carta/cpp/core/Data/Colormap/Colormap.cpp +++ b/carta/cpp/core/Data/Colormap/Colormap.cpp @@ -1,10 +1,8 @@ #include "Colormap.h" -#include "Colormaps.h" +#include "ColorState.h" #include "Data/Settings.h" #include "Data/Image/Controller.h" #include "Data/Histogram/Histogram.h" -#include "TransformsData.h" -#include "Data/IColoredView.h" #include "Data/Util.h" #include "State/StateInterface.h" #include "State/UtilState.h" @@ -20,28 +18,9 @@ namespace Carta { namespace Data { const QString Colormap::CLASS_NAME = "Colormap"; -const QString Colormap::COLOR_MAP_NAME = "colorMapName"; -const QString Colormap::COLORED_OBJECT = "coloredObject"; -const QString Colormap::REVERSE = "reverse"; -const QString Colormap::INVERT = "invert"; -const QString Colormap::COLOR_MIX = "colorMix"; -const QString Colormap::RED_PERCENT = "redPercent"; -const QString Colormap::GREEN_PERCENT = "greenPercent"; -const QString Colormap::BLUE_PERCENT = "bluePercent"; -const QString Colormap::COLOR_MIX_RED = COLOR_MIX + "/" + RED_PERCENT; -const QString Colormap::COLOR_MIX_GREEN = COLOR_MIX + "/" + GREEN_PERCENT; -const QString Colormap::COLOR_MIX_BLUE = COLOR_MIX + "/" + BLUE_PERCENT; const QString Colormap::INTENSITY_MIN = "intensityMin"; const QString Colormap::INTENSITY_MAX = "intensityMax"; -const QString Colormap::SCALE_1 = "scale1"; -const QString Colormap::SCALE_2 = "scale2"; -const QString Colormap::GAMMA = "gamma"; -const QString Colormap::SIGNIFICANT_DIGITS = "significantDigits"; -const QString Colormap::TRANSFORM_IMAGE = "imageTransform"; -const QString Colormap::TRANSFORM_DATA = "dataTransform"; -Colormaps* Colormap::m_colors = nullptr; -TransformsData* Colormap::m_dataTransforms = nullptr; class Colormap::Factory : public Carta::State::CartaObjectFactory { @@ -66,10 +45,14 @@ Colormap::Colormap( const QString& path, const QString& id): Settings* prefObj = objMan->createObject(); m_settings.reset( prefObj ); + ColorState* colorStateObj = objMan->createObject(); + m_stateColorGlobal.reset( colorStateObj ); + connect( colorStateObj, SIGNAL( colorStateChanged()), this, SLOT( _colorStateChanged())); + m_stateColorGlobal->_initializeDefaultState( m_state ); + m_stateColors.push_back( m_stateColorGlobal); + _initializeDefaultState(); _initializeCallbacks(); - _initializeStatics(); - _setErrorMargin(); } QString Colormap::addLink( CartaObject* cartaObject ){ @@ -77,11 +60,11 @@ QString Colormap::addLink( CartaObject* cartaObject ){ bool objAdded = false; QString result; if ( target != nullptr ){ + objAdded = m_linkImpl->addLink( target ); if ( objAdded ){ - setColorProperties( target ); - connect( target, SIGNAL(dataChanged(Controller*)), this, SLOT(setColorProperties(Controller*))); - + target->setGlobalColor( m_stateColorGlobal ); + connect( target, SIGNAL(colorChanged(Controller*)), this, SLOT(_setColorStates(Controller*))); } } else { @@ -89,8 +72,8 @@ QString Colormap::addLink( CartaObject* cartaObject ){ if ( hist != nullptr ){ objAdded = m_linkImpl->addLink( hist ); if ( objAdded ){ - connect( this, SIGNAL(colorMapChanged( Colormap*)), hist, SLOT( updateColorMap( Colormap*))); - hist->updateColorMap( this ); + //connect( this, SIGNAL(colorMapChanged( Colormap*)), hist, SLOT( updateColorMap( Colormap*))); + //hist->updateColorMap( this ); connect( hist,SIGNAL(colorIntensityBoundsChanged(double,double)), this, SLOT(_updateIntensityBounds( double, double ))); } @@ -103,22 +86,28 @@ QString Colormap::addLink( CartaObject* cartaObject ){ } - void Colormap::clear(){ m_linkImpl->clear(); } +void Colormap::_colorStateChanged(){ + if ( m_stateColors.size() > 0 ){ + m_stateColors[0]->_replicateTo( m_state ); + m_state.flushState(); + } +} + QString Colormap::_commandInvertColorMap( const QString& params ){ QString result; - std::set keys = {INVERT}; + std::set keys = {ColorState::INVERT}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); QString invertStr = dataValues[*keys.begin()]; bool validBool = false; bool invert = Util::toBool( invertStr, &validBool ); if ( validBool ){ - result = invertColorMap( invert ); + result = setInvert( invert ); } else { result = "Invert color map parameters must be true/false: "+params; @@ -127,11 +116,61 @@ QString Colormap::_commandInvertColorMap( const QString& params ){ return result; } +QString Colormap::_commandReverseColorMap( const QString& params ){ + QString result; + std::set keys = {ColorState::REVERSE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString reverseStr = dataValues[*keys.begin()]; + bool validBool = false; + bool reverse = Util::toBool(reverseStr, &validBool); + if ( validBool ){ + result = setReverse( reverse ); + } + else { + result = "Invalid color map reverse parameters: "+ params; + } + Util::commandPostProcess( result ); + return result; +} + +QString Colormap::_commandSetColorMix( const QString& params ){ + QString result; + std::set keys = {Util::RED, Util::GREEN, Util::BLUE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + + bool validRed = false; + double redValue = dataValues[Util::RED].toDouble(&validRed ); + + bool validBlue = false; + double blueValue = dataValues[Util::BLUE].toDouble(&validBlue ); + + bool validGreen = false; + double greenValue = dataValues[Util::GREEN].toDouble(&validGreen); + + if ( validRed && validBlue && validGreen ){ + result = setColorMix( redValue, greenValue, blueValue ); + } + else { + result = "Color mix values must be numbers: "+params; + } + Util::commandPostProcess( result ); + return result; +} + +QString Colormap::_commandSetColorMap( const QString& params ){ + std::set keys = {"name"}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString colorMapStr = dataValues[*keys.begin()]; + QString result = setColorMap( colorMapStr ); + Util::commandPostProcess( result ); + return result; +} + QList Colormap::getLinks() const { return m_linkImpl->getLinkIds(); } -QString Colormap::getPreferencesId() const { +QString Colormap::_getPreferencesId() const { return m_settings->getPath(); } @@ -152,28 +191,15 @@ QString Colormap::getStateString( const QString& sessionId, SnapshotType type ) void Colormap::_initializeDefaultState(){ - m_state.insertValue( COLOR_MAP_NAME, "Gray" ); - m_state.insertValue(REVERSE, false); - m_state.insertValue(INVERT, false ); - - m_state.insertValue(GAMMA, 1.0 ); - m_state.insertValue(SCALE_1, 0.0 ); - m_state.insertValue(SCALE_2, 0.0 ); - - m_state.insertObject( COLOR_MIX ); - m_state.insertValue( COLOR_MIX_RED, 1 ); - m_state.insertValue(COLOR_MIX_GREEN, 1 ); - m_state.insertValue(COLOR_MIX_BLUE, 1 ); - - m_state.insertValue(SIGNIFICANT_DIGITS, 6 ); - m_state.insertValue(TRANSFORM_IMAGE, "Gamma"); - m_state.insertValue(TRANSFORM_DATA, "None"); + //Color state m_state.flushState(); + //Image dependent intensity bounds m_stateData.insertValue( INTENSITY_MIN, 0 ); m_stateData.insertValue( INTENSITY_MAX, 1 ); m_stateData.flushState(); + //Mouse m_stateMouse.insertObject( ImageView::MOUSE ); m_stateMouse.insertValue(ImageView::MOUSE_X, 0 ); m_stateMouse.insertValue(ImageView::MOUSE_Y, 0 ); @@ -185,14 +211,14 @@ void Colormap::_initializeCallbacks(){ addCommandCallback( "registerPreferences", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QString result = getPreferencesId(); + QString result = _getPreferencesId(); return result; }); addCommandCallback( "setDataTransform", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {TRANSFORM_DATA}; + std::set keys = {ColorState::TRANSFORM_DATA}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); QString dataTransformStr = dataValues[*keys.begin()]; QString result = setDataTransform( dataTransformStr ); @@ -221,34 +247,60 @@ void Colormap::_initializeCallbacks(){ addCommandCallback( "setColorMix", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { QString result = _commandSetColorMix( params ); + return result; }); + addCommandCallback( "setNanColor", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + QString result; + std::set keys = {Util::RED, Util::GREEN, Util::BLUE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + + bool validRed = false; + double redValue = dataValues[Util::RED].toInt(&validRed ); + + bool validBlue = false; + double blueValue = dataValues[Util::BLUE].toInt(&validBlue ); + + bool validGreen = false; + double greenValue = dataValues[Util::GREEN].toInt(&validGreen); + + if ( validRed && validBlue && validGreen ){ + result = setNanColor( redValue, greenValue, blueValue ); + } + else { + result = "Nan color values must be in [0,255]: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + addCommandCallback( "setScales", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { QString result; - std::set keys = {SCALE_1, SCALE_2}; + std::set keys = {ColorState::SCALE_1, ColorState::SCALE_2}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); bool valid = false; - double scale1 = dataValues[SCALE_1].toDouble( & valid ); + double scale1 = dataValues[ColorState::SCALE_1].toDouble( & valid ); if ( !valid ){ result = "Invalid color scale: "+params; } else { - double scale2 = dataValues[SCALE_2].toDouble( &valid ); + double scale2 = dataValues[ColorState::SCALE_2].toDouble( &valid ); if ( !valid ){ result = "Invalid color scale: "+params; } else { - double oldScale1 = m_state.getValue(SCALE_1); - double oldScale2 = m_state.getValue(SCALE_2); + double oldScale1 = m_state.getValue(ColorState::SCALE_1); + double oldScale2 = m_state.getValue(ColorState::SCALE_2); bool changedState = false; if ( scale1 != oldScale1 ){ - m_state.setValue(SCALE_1, scale1 ); + m_state.setValue(ColorState::SCALE_1, scale1 ); changedState = true; } if ( scale2 != oldScale2 ){ - m_state.setValue(SCALE_2, scale2 ); + m_state.setValue(ColorState::SCALE_2, scale2 ); changedState = true; } if ( changedState ){ @@ -272,9 +324,9 @@ void Colormap::_initializeCallbacks(){ addCommandCallback( "setSignificantDigits", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { QString result; - std::set keys = {SIGNIFICANT_DIGITS}; + std::set keys = {ColorState::SIGNIFICANT_DIGITS}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString digitsStr = dataValues[SIGNIFICANT_DIGITS]; + QString digitsStr = dataValues[ColorState::SIGNIFICANT_DIGITS]; bool validDigits = false; int digits = digitsStr.toInt( &validDigits ); if ( validDigits ){ @@ -286,18 +338,50 @@ void Colormap::_initializeCallbacks(){ Util::commandPostProcess( result ); return result; }); + + addCommandCallback( "setDefaultNan", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {ColorState::NAN_DEFAULT}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString defaultNanStr = dataValues[*keys.begin()]; + bool validBool = false; + bool useDefaultNan = Util::toBool( defaultNanStr, &validBool ); + QString result; + if ( validBool ){ + setNanDefault( useDefaultNan ); + } + else { + result = "Please specify true/false for use default Nan: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + +} + + +bool Colormap::_isGlobal() const { + bool global = true; + if ( m_stateColors.size() > 0 ){ + global = m_stateColors[0]->_isGlobal(); + } + return global; } -void Colormap::_initializeStatics(){ - //Load the available color maps. - if ( m_colors == nullptr ){ - m_colors = Util::findSingletonObject(); +bool Colormap::isInverted() const { + bool inverted = false; + if ( m_stateColors.size() > 0 ){ + inverted = m_stateColors[0]->_isInverted(); } + return inverted; +} - //Data transforms - if ( m_dataTransforms == nullptr ){ - m_dataTransforms = Util::findSingletonObject(); +bool Colormap::isNanDefault() const { + bool nanDefault = false; + if ( m_stateColors.size() > 0 ){ + nanDefault = m_stateColors[0]->_isNanDefault(); } + return nanDefault; } bool Colormap::isLinked( const QString& linkId ) const { @@ -311,104 +395,13 @@ bool Colormap::isLinked( const QString& linkId ) const { bool Colormap::isReversed() const { - return m_state.getValue( REVERSE ); -} - -bool Colormap::isInverted() const { - return m_state.getValue( INVERT ); -} - - -QString Colormap::invertColorMap( bool invert ){ - QString result; - bool oldInvert = m_state.getValue(INVERT ); - if ( invert != oldInvert ){ - m_state.setValue(INVERT, invert ); - m_state.flushState(); - int linkCount = m_linkImpl->getLinkCount(); - for( int i = 0; i < linkCount; i++ ){ - Controller* controller = dynamic_cast( m_linkImpl->getLink(i)); - if ( controller != nullptr ){ - controller -> setColorInverted ( invert ); - } - } - emit colorMapChanged( this ); - } - return result; -} - -QString Colormap::_commandSetColorMix( const QString& params ){ - QString result; - std::set keys = {RED_PERCENT, GREEN_PERCENT, BLUE_PERCENT}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - - bool validRed = false; - double redValue = dataValues[RED_PERCENT].toDouble(&validRed ); - - bool validBlue = false; - double blueValue = dataValues[BLUE_PERCENT].toDouble(&validBlue ); - - bool validGreen = false; - double greenValue = dataValues[GREEN_PERCENT].toDouble(&validGreen); - - if ( validRed && validBlue && validGreen ){ - result = setColorMix( redValue, greenValue, blueValue ); - } - else { - result = "Color mix values must be numbers: "+params; - } - Util::commandPostProcess( result ); - return result; -} - -QString Colormap::setColorMix( double redValue, double greenValue, double blueValue ){ - QString result; - bool greenChanged = _setColorMix( COLOR_MIX_GREEN, greenValue, result ); - bool redChanged = _setColorMix( COLOR_MIX_RED, redValue, result ); - bool blueChanged = _setColorMix( COLOR_MIX_BLUE, blueValue, result ); - if ( redChanged || blueChanged || greenChanged ){ - m_state.flushState(); - double newRed = m_state.getValue(COLOR_MIX_RED ); - double newGreen = m_state.getValue(COLOR_MIX_GREEN ); - double newBlue = m_state.getValue(COLOR_MIX_BLUE ); - int linkCount = m_linkImpl->getLinkCount(); - for( int i = 0; i < linkCount; i++ ){ - Controller* controller = dynamic_cast( m_linkImpl->getLink(i)); - if ( controller != nullptr ){ - controller->setColorAmounts( newRed, newGreen, newBlue ); - } - } - emit colorMapChanged( this ); + bool reversed = false; + if ( m_stateColors.size() > 0 ){ + reversed = m_stateColors[0]->_isReversed(); } - return result; + return reversed; } -QString Colormap::_commandReverseColorMap( const QString& params ){ - QString result; - std::set keys = {REVERSE}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString reverseStr = dataValues[*keys.begin()]; - bool validBool = false; - bool reverse = Util::toBool(reverseStr, &validBool); - if ( validBool ){ - result = reverseColorMap( reverse ); - } - else { - result = "Invalid color map reverse parameters: "+ params; - } - Util::commandPostProcess( result ); - return result; -} - - - -QString Colormap::_commandSetColorMap( const QString& params ){ - std::set keys = {"name"}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString colorMapStr = dataValues[*keys.begin()]; - QString result = setColorMap( colorMapStr ); - return result; -} void Colormap::refreshState(){ CartaObject::refreshState(); @@ -428,23 +421,7 @@ void Colormap::resetState( const QString& state ){ m_state.flushState(); } -QString Colormap::reverseColorMap( bool reverse ){ - QString result; - bool oldReverse = m_state.getValue(REVERSE); - if ( reverse != oldReverse ){ - m_state.setValue(REVERSE, reverse ); - m_state.flushState(); - int linkCount = m_linkImpl->getLinkCount(); - for( int i = 0; i < linkCount; i++ ){ - Controller* controller = dynamic_cast( m_linkImpl->getLink(i)); - if ( controller != nullptr ){ - controller -> setColorReversed( reverse ); - } - } - emit colorMapChanged( this ); - } - return result; -} + QString Colormap::removeLink( CartaObject* cartaObject ){ Controller* controller = dynamic_cast(cartaObject); @@ -471,127 +448,165 @@ QString Colormap::removeLink( CartaObject* cartaObject ){ return result; } -bool Colormap::_setColorMix( const QString& key, double colorPercent, QString& errorMsg ){ - bool colorChanged = false; - if ( colorPercent<0 || colorPercent > 1 ){ - errorMsg = errorMsg + key + " mix must be in [0,1]. "; - } - else { - double oldColorPercent = m_state.getValue( key ); - if ( abs( colorPercent - oldColorPercent ) >= 0.001f ){ - m_state.setValue(key, colorPercent ); - colorChanged = true; + +QString Colormap::setColorMap( const QString& colorMapStr ){ + int stateColorCount = m_stateColors.size(); + QString result; + for ( int i = 0; i < stateColorCount; i++ ){ + result = m_stateColors[i]->_setColorMap( colorMapStr ); + if ( !result.isEmpty() ){ + break; } } - return colorChanged; + if ( result.isEmpty() ){ + _colorStateChanged(); + } + return result; } -QString Colormap::setColorMap( const QString& colorMapStr ){ - QString mapName = m_state.getValue(COLOR_MAP_NAME); +QString Colormap::setColorMix( double redValue, double greenValue, double blueValue){ + int stateColorCount = m_stateColors.size(); QString result; - if ( m_colors != nullptr ){ - if( m_colors->isMap( colorMapStr ) ){ - if ( colorMapStr != mapName ){ - m_state.setValue(COLOR_MAP_NAME, colorMapStr ); - m_state.flushState(); - int linkCount = m_linkImpl->getLinkCount(); - for( int i = 0; i < linkCount; i++ ){ - Controller* controller = dynamic_cast( m_linkImpl->getLink(i)); - if ( controller != nullptr ){ - controller->setColorMap( colorMapStr ); - } - } - emit colorMapChanged( this ); - } + for ( int i = 0; i < stateColorCount; i++ ){ + result = m_stateColors[i]->_setColorMix( redValue, greenValue, blueValue ); + if ( !result.isEmpty()){ + break; } - else { - result = "Invalid colormap: " + colorMapStr; - } } + if ( result.isEmpty() ){ + _colorStateChanged(); + } + return result; +} - Util::commandPostProcess( result ); + +QString Colormap::setInvert( bool invert ){ + int stateColorCount = m_stateColors.size(); + QString result; + for ( int i = 0; i < stateColorCount; i++ ){ + m_stateColors[i]->_setInvert( invert ); + } + if ( stateColorCount == 0 ){ + result = "There were no color maps to invert."; + } + else { + _colorStateChanged(); + } return result; } QString Colormap::setGamma( double gamma ){ + int stateColorCount = m_stateColors.size(); QString result; - double oldGamma = m_state.getValue( GAMMA ); - if ( qAbs( gamma - oldGamma) > m_errorMargin ){ - int digits = m_state.getValue(SIGNIFICANT_DIGITS); - m_state.setValue(GAMMA, Util::roundToDigits(gamma, digits )); - m_state.flushState(); - //Let the controllers know gamma has changed. - int linkCount = m_linkImpl->getLinkCount(); - for( int i = 0; i < linkCount; i++ ){ - Controller* controller = dynamic_cast( m_linkImpl->getLink(i)); - if ( controller != nullptr ){ - controller->setGamma( gamma ); - } + for ( int i = 0; i < stateColorCount; i++ ){ + result = m_stateColors[i]->_setGamma( gamma ); + if ( !result.isEmpty()){ + break; } - emit colorMapChanged( this ); + } + if ( result.isEmpty() ){ + _colorStateChanged(); } return result; } QString Colormap::setDataTransform( const QString& transformString){ - QString result(""); - QString transformName = m_state.getValue(TRANSFORM_DATA); - if ( m_dataTransforms != nullptr ){ - QString actualTransform; - bool recognizedTransform = m_dataTransforms->isTransform( transformString, actualTransform ); - if( recognizedTransform ){ - if ( actualTransform != transformName ){ - m_state.setValue(TRANSFORM_DATA, actualTransform ); - m_state.flushState(); - int linkCount = m_linkImpl->getLinkCount(); - for( int i = 0; i < linkCount; i++ ){ - Controller* controller = dynamic_cast( m_linkImpl->getLink(i)); - if ( controller != nullptr ){ - controller->setTransformData( actualTransform ); - } - } - emit colorMapChanged( this ); - } - } - else { - result = "Invalid data transform: " + transformString; + int stateColorCount = m_stateColors.size(); + QString result; + for ( int i = 0; i < stateColorCount; i++ ){ + result = m_stateColors[i]->_setDataTransform( transformString ); + if ( !result.isEmpty() ){ + break; } } + if ( result.isEmpty() ){ + _colorStateChanged(); + } return result; } -void Colormap::setColorProperties( Controller* target ){ - target->setColorMap( m_state.getValue(COLOR_MAP_NAME)); - target->setColorInverted( m_state.getValue(INVERT) ); - target->setColorReversed( m_state.getValue(REVERSE) ); - double redPercent = m_state.getValue(COLOR_MIX_RED); - double greenPercent = m_state.getValue(COLOR_MIX_GREEN ); - double bluePercent = m_state.getValue(COLOR_MIX_BLUE); - target->setColorAmounts( redPercent, greenPercent, bluePercent ); - target->setGamma( m_state.getValue(GAMMA) ); - target->setTransformData( m_state.getValue(TRANSFORM_DATA )); +void Colormap::_setColorStates( Controller* controller ){ + + std::vector< std::shared_ptr > selectedColorStates = controller->getSelectedColorStates(); + m_stateColors.clear(); + int stateColorCount = selectedColorStates.size(); + + /** + * Note: could be complication if one is a global map and + * the others are not. + */ + + for ( int i = 0; i < stateColorCount; i++ ){ + m_stateColors.push_back( selectedColorStates[i] ); + } +} + +QString Colormap::setNanColor( int redValue, int greenValue, int blueValue){ + int stateColorCount = m_stateColors.size(); + QString result; + if ( isNanDefault() ){ + setNanDefault( false ); + } + for ( int i = 0; i < stateColorCount; i++ ){ + result = m_stateColors[i]->_setNanColor( redValue, greenValue, blueValue ); + if ( !result.isEmpty()){ + break; + } + } + if ( result.isEmpty() ){ + _colorStateChanged(); + } + return result; +} + +QString Colormap::setNanDefault( bool nanDefault ) { + int stateColorCount = m_stateColors.size(); + QString result; + for ( int i = 0; i < stateColorCount; i++ ){ + m_stateColors[i]->_setNanDefault( nanDefault ); + } + if ( stateColorCount == 0 ){ + result = "There were no color maps to for nan default."; + } + else { + _colorStateChanged(); + } + return result; } -QString Colormap::setSignificantDigits( int digits ){ +QString Colormap::setReverse( bool reverse ){ + int stateColorCount = m_stateColors.size(); QString result; - if ( digits <= 0 ){ - result = "Invalid significant digits; must be positive: "+QString::number( digits ); + for ( int i = 0; i < stateColorCount; i++ ){ + m_stateColors[i]->_setReverse( reverse ); + } + if ( stateColorCount == 0 ){ + result = "There were no color maps to reverse."; } else { - if ( m_state.getValue(SIGNIFICANT_DIGITS) != digits ){ - m_state.setValue(SIGNIFICANT_DIGITS, digits ); - _setErrorMargin(); - } + _colorStateChanged(); } return result; } -void Colormap::_setErrorMargin(){ - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS ); - m_errorMargin = 1.0/qPow(10,significantDigits); +QString Colormap::setSignificantDigits( int digits ){ + int stateColorCount = m_stateColors.size(); + QString result; + for ( int i = 0; i < stateColorCount; i++ ){ + result = m_stateColors[i]->_setSignificantDigits( digits ); + if ( !result.isEmpty() ){ + break; + } + } + if ( result.isEmpty() ){ + _colorStateChanged(); + } + return result; } + + void Colormap::_updateIntensityBounds( double minIntensity, double maxIntensity ){ double oldMinIntensity = m_stateData.getValue( INTENSITY_MIN ); bool intensityChanged = false; diff --git a/carta/cpp/core/Data/Colormap/Colormap.h b/carta/cpp/core/Data/Colormap/Colormap.h index ef02859b..db0101b6 100644 --- a/carta/cpp/core/Data/Colormap/Colormap.h +++ b/carta/cpp/core/Data/Colormap/Colormap.h @@ -24,10 +24,8 @@ namespace Carta { namespace Data { -class Colormaps; -class IColoredView; +class ColorState; class Controller; -class TransformsData; class Settings; class Colormap : public QObject, public Carta::State::CartaObject, public ILinkable { @@ -46,11 +44,9 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ void clear(); - /** - * Return the server side id of the preferences for this colormap. - * @return the server side id of this colormap's preferences. - */ - QString getPreferencesId() const; + + std::shared_ptr getColorMap( ) const; + /** * Return a string representing the colormap state of a particular type. * @param sessionId - an identifier for the user's session. @@ -59,18 +55,6 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ virtual QString getStateString( const QString& sessionId, SnapshotType type ) const Q_DECL_OVERRIDE; - /** - * Returns whether or not the object with the given id is already linked to this object. - * @param linkId - a QString identifier for an object. - * @return true if this object is already linked to the one identified by the id; false otherwise. - */ - virtual bool isLinked( const QString& linkId ) const Q_DECL_OVERRIDE; - - /** - * Returns whether or not the colormap is reversed. - * @return true if the colormap is reversed; false otherwise. - */ - bool isReversed() const; /** * Returns whether or not the colormap is inverted. @@ -78,33 +62,29 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ bool isInverted() const; - /** - * Set the name of the current color map. - * @param colorMapName a unique identifier for the color map. - * @return error information if the color map was not successfully set. + * Returns whether or not the object with the given id is already linked to this object. + * @param linkId - a QString identifier for an object. + * @return true if this object is already linked to the one identified by the id; false otherwise. */ - QString setColorMap( const QString& colorMapName ); + virtual bool isLinked( const QString& linkId ) const Q_DECL_OVERRIDE; /** - * Force a state reload. + * Returns true if the nan color will be derived from the bottom of the color map; false, + * if the nan color is set be the user. */ - void refreshState() Q_DECL_OVERRIDE; + bool isNanDefault() const; /** - * Reverse the current colormap. - * @param reverse - true if the colormap should be reversed; false otherwise. - * @return error information if the color map was not successfully reversed. + * Returns whether or not the colormap is reversed. + * @return true if the colormap is reversed; false otherwise. */ - QString reverseColorMap( bool reverse ); - + bool isReversed() const; /** - * Invert the current colormap. - * @param invert - true if the color map should be inverted; false otherwise.. - * @return error information if the color map was not successfully inverted. + * Force a state reload. */ - QString invertColorMap( bool invert ); + void refreshState() Q_DECL_OVERRIDE; /** * Restore the state from a string representation. @@ -112,6 +92,14 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ virtual void resetState( const QString& state ) Q_DECL_OVERRIDE; + /** + * Set the name of the current color map. + * @param colorMapName a unique identifier for the color map. + * @return error information if the color map was not successfully set. + */ + QString setColorMap( const QString& colorMapName ); + + /** * Set a color mix. * @param redValue a number in [0,1] representing the amount of red in the mix. @@ -135,7 +123,41 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ QString setGamma( double gamma ); - std::shared_ptr getColorMap( ) const; + /** + * Invert the current colormap. + * @param invert - true if the color map should be inverted; false otherwise.. + * @return error information if the color map was not successfully inverted. + */ + QString setInvert( bool invert ); + + /** + * Set the color used for nan values. + * @param redValue - the amount of red [0,255]. + * @param greenValue - the amount of green [0,255]. + * @param blueValue - the amount of blue [0,255]. + */ + //Note: This color will only be used if using the default nan color is + //set to false. If using the default nan is true, the nan color will be + //ignored. + QString setNanColor( int redValue, int greenValue, int blueValue ); + + /** + * Set whether or not to use the default nan color (bottom of the color + * map). + * @param nanDefault - true if the bottom color map value should be the + * nan color; false if the nan color should be user specified. + * @return an error message if whether or not to use the default nan + * color could not be set. + */ + QString setNanDefault( bool nanDefault ); + + /** + * Reverse the current colormap. + * @param reverse - true if the colormap should be reversed; false otherwise. + * @return error information if the color map was not successfully reversed. + */ + QString setReverse( bool reverse ); + /** * Set the number of significant digits to use/display in colormap calculations. @@ -149,14 +171,13 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka virtual ~Colormap(); const static QString CLASS_NAME; -signals: - void colorMapChanged( Colormap* map ); - -public slots: - void setColorProperties( Controller* target ); - private slots: void _updateIntensityBounds( double minIntensity, double maxIntensity ); + void _colorStateChanged(); + /** + * Set the color states managed by this color map. + */ + void _setColorStates( Controller* target ); private: QString _commandSetColorMap( const QString& params ); @@ -164,34 +185,22 @@ private slots: QString _commandReverseColorMap( const QString& params ); QString _commandSetColorMix( const QString& params ); - bool _setColorMix( const QString& key, double colorPercent, QString& errorMsg ); + /** + * Return the server side id of the preferences for this colormap. + * @return the server side id of this colormap's preferences. + */ + QString _getPreferencesId() const; + void _initializeDefaultState(); void _initializeCallbacks(); - void _initializeStatics(); - - void _setErrorMargin(); + bool _isGlobal() const; static bool m_registered; - const static QString COLOR_MAP_NAME; - const static QString REVERSE; - const static QString INVERT; - const static QString RED_PERCENT; - const static QString GREEN_PERCENT; - const static QString BLUE_PERCENT; - const static QString COLORED_OBJECT; - const static QString COLOR_MIX; - const static QString COLOR_MIX_RED; - const static QString COLOR_MIX_GREEN; - const static QString COLOR_MIX_BLUE; + const static QString INTENSITY_MIN; const static QString INTENSITY_MAX; - const static QString SCALE_1; - const static QString SCALE_2; - const static QString GAMMA; - const static QString SIGNIFICANT_DIGITS; - const static QString TRANSFORM_IMAGE; - const static QString TRANSFORM_DATA; + Colormap( const QString& path, const QString& id ); @@ -202,21 +211,16 @@ private slots: std::unique_ptr m_settings; - //Supported color maps - static Colormaps* m_colors; - - //Supported data transforms - static TransformsData* m_dataTransforms; - Carta::State::StateInterface m_stateData; //Separate state for mouse events since they get updated rapidly and not //everyone wants to listen to them. Carta::State::StateInterface m_stateMouse; - - - double m_errorMargin; + //Holds the current color state + std::vector< std::shared_ptr > m_stateColors; + //Holds the global color state; + std::shared_ptr m_stateColorGlobal; Colormap( const Colormap& other); Colormap& operator=( const Colormap& other ); diff --git a/carta/cpp/core/Data/Histogram/Histogram.cpp b/carta/cpp/core/Data/Histogram/Histogram.cpp index c10ed423..e4406b73 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.cpp +++ b/carta/cpp/core/Data/Histogram/Histogram.cpp @@ -433,7 +433,7 @@ QString Histogram::_getActualPlaneMode( const QString& planeModeStr ){ return result; } -QString Histogram::getPreferencesId() const { +QString Histogram::_getPreferencesId() const { return m_preferences->getPath(); } @@ -555,7 +555,7 @@ void Histogram::_initializeCallbacks(){ addCommandCallback( "registerPreferences", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QString result = getPreferencesId(); + QString result = _getPreferencesId(); return result; }); @@ -2014,17 +2014,17 @@ void Histogram::_updateSelection(int x){ } -void Histogram::updateColorMap( Colormap* map ){ +/*void Histogram::updateColorMap( Colormap* map ){ if ( map != nullptr ){ Controller* controller = _getControllerSelected(); if ( controller != nullptr ){ - map->setColorProperties( controller ); + //map->setColorProperties( controller ); std::shared_ptr pipeline = controller->getPipeline(); m_histogram->setPipeline( pipeline ); } } _generateHistogram( false ); -} +}*/ void Histogram::_updateSize( const QSize& size ){ diff --git a/carta/cpp/core/Data/Histogram/Histogram.h b/carta/cpp/core/Data/Histogram/Histogram.h index 1eec02bc..e444a5a9 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.h +++ b/carta/cpp/core/Data/Histogram/Histogram.h @@ -68,12 +68,6 @@ class Histogram : public QObject, public Carta::State::CartaObject, public ILink */ void clearSelection(); - /** - * Returns the server side id of the histogram user preferences. - * @return the unique server side id of the user preferences. - */ - QString getPreferencesId() const; - /** * Return a string representing the histogram state of a particular type. * @param type - the type of state needed. @@ -337,7 +331,7 @@ class Histogram : public QObject, public Carta::State::CartaObject, public ILink void colorIntensityBoundsChanged( double minIntensity, double maxIntensity ); public slots: - void updateColorMap( Colormap* ); + //void updateColorMap( Colormap* ); protected: @@ -391,6 +385,12 @@ private slots: std::vector> _generateData(Controller* controller); + /** + * Returns the server side id of the histogram user preferences. + * @return the unique server side id of the user preferences. + */ + QString _getPreferencesId() const; + //Bin count <-> Bin width conversion. double _toBinWidth( int count ) const; int _toBinCount( double width ) const; diff --git a/carta/cpp/core/Data/IColoredView.h b/carta/cpp/core/Data/IColoredView.h deleted file mode 100755 index 5117bc73..00000000 --- a/carta/cpp/core/Data/IColoredView.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Interface implemented by classes wanting to receive notification of - * color map changes. - */ - -#ifndef ICOLOREDVIEW_H_ -#define ICOLOREDVIEW_H_ - -#include - -namespace Image { -class ImageInterface; -} - -namespace Carta { - -namespace Data { - -class IColoredView { -public: - /** - * Notification that the selected color map has changed. - * @param colorMapName a QString identifying the selected color map. - */ - virtual void setColorMap( const QString& colorMapName )=0; - - /** - * Sets whether or not the colors in the map have been inverted. - * @param inverted true if the colors are inverted; false otherwise. - */ - virtual void setColorInverted( bool inverted ) = 0; - - /** - * Sets whether or not the colors in the map have been reversed. - * @param reversed true if the colors have been reversed; false otherwise. - */ - virtual void setColorReversed( bool reversed ) = 0; - - /** - * Set the amount of red, green, and blue in the color scale. - * @param newRed the amount of red; should be in the range [0,1]. - * @param newGreen the amount of green; should be in the range [0,1]. - * @param newBlue the amount of blue; should be in the range[0,1]. - */ - virtual void setColorAmounts( double newRed, double newGreen, double newBlue ) = 0; - - /** - * Set the gamma color map parameter. - * @param gamma a color map parameter. - */ - virtual void setGamma( double gamma ) = 0; - -}; -} -} - - -#endif /* ICOLOREDVIEW_H_ */ diff --git a/carta/cpp/core/Data/Image/Contour/ContourControls.cpp b/carta/cpp/core/Data/Image/Contour/ContourControls.cpp index 8fc47c02..8924b017 100644 --- a/carta/cpp/core/Data/Image/Contour/ContourControls.cpp +++ b/carta/cpp/core/Data/Image/Contour/ContourControls.cpp @@ -362,11 +362,17 @@ void ContourControls::_initializeCallbacks(){ if ( validInt ){ QString setName = dataValues[CONTOUR_SET_NAME ]; QString levelStr = dataValues[LEVEL_LIST]; - std::vector levels = Util::string2VectorDouble( levelStr, LEVEL_SEPARATOR ); - result = setAlpha( setName, levels, alpha ); + bool validDouble = false; + std::vector levels = Util::string2VectorDouble( levelStr, &validDouble, LEVEL_SEPARATOR ); + if ( validDouble ){ + result = setAlpha( setName, levels, alpha ); + } + else { + result = "Could not set contour transparency, levels must be numbers: "+params; + } } else { - result = "Contour transparency level must be an integer: "+dataValues[Util::ALPHA]; + result = "Contour transparency must be an integer: "+dataValues[Util::ALPHA]; } Util::commandPostProcess( result ); return result; @@ -386,9 +392,15 @@ void ContourControls::_initializeCallbacks(){ if ( validRed && validGreen && validBlue ){ QString setName = dataValues[CONTOUR_SET_NAME ]; QString levelStr = dataValues[LEVEL_LIST]; - std::vector levels = Util::string2VectorDouble( levelStr, LEVEL_SEPARATOR ); - QStringList errorList = setColor( setName, levels, red, green, blue ); - result = errorList.join(","); + bool validLevels = false; + std::vector levels = Util::string2VectorDouble( levelStr, &validLevels, LEVEL_SEPARATOR ); + if ( validLevels ){ + QStringList errorList = setColor( setName, levels, red, green, blue ); + result = errorList.join(","); + } + else { + result = "Could not set color; contour levels must numbers: "+levelStr; + } } else { result = "Contour colors must be integer(s): "+dataValues[Util::RED]+","+dataValues[Util::GREEN]+ @@ -471,13 +483,14 @@ void ContourControls::_initializeCallbacks(){ QString style = dataValues[Contour::STYLE]; QString setName = dataValues[CONTOUR_SET_NAME]; QString levelStr = dataValues[LEVEL_LIST]; - std::vector levels = Util::string2VectorDouble( levelStr, LEVEL_SEPARATOR ); QString result; - if ( levels.size() > 0 ){ + bool validLevels = false; + std::vector levels = Util::string2VectorDouble( levelStr, &validLevels, LEVEL_SEPARATOR ); + if ( validLevels ){ result = setLineStyle( setName, levels, style ); } else { - result = "Contour level(s) must be numbers:"+levelStr; + result = "Style could not be set because contour levels were invalid:"+levelStr; } Util::commandPostProcess( result ); return result; @@ -489,8 +502,15 @@ void ContourControls::_initializeCallbacks(){ std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); QString setName = dataValues[CONTOUR_SET_NAME]; QString levelStr = dataValues[LEVEL_LIST]; - std::vector levels = Util::string2VectorDouble( levelStr, LEVEL_SEPARATOR ); - QString result = setLevels( setName, levels); + bool validLevels = false; + std::vector levels = Util::string2VectorDouble( levelStr, &validLevels, LEVEL_SEPARATOR ); + QString result; + if ( validLevels ){ + result = setLevels( setName, levels); + } + else { + result = "Invalid contour levels: "+levelStr; + } Util::commandPostProcess( result ); return result; }); @@ -556,8 +576,14 @@ void ContourControls::_initializeCallbacks(){ if ( validDouble ){ QString setName = dataValues[CONTOUR_SET_NAME ]; QString levelStr = dataValues[LEVEL_LIST]; - std::vector levels = Util::string2VectorDouble( levelStr, LEVEL_SEPARATOR ); - result = setThickness( setName, levels, thickness ); + bool validLevels = false; + std::vector levels = Util::string2VectorDouble( levelStr, &validLevels, LEVEL_SEPARATOR ); + if ( validLevels ){ + result = setThickness( setName, levels, thickness ); + } + else { + result = "Could not set thickness; contour levels were invalid: "+levelStr; + } } else { result = "Contour thickness must be an integer: "+dataValues[Util::PEN_WIDTH]; @@ -577,12 +603,13 @@ void ContourControls::_initializeCallbacks(){ if ( validBool ){ QString setName = dataValues[CONTOUR_SET_NAME]; QString levelListStr = dataValues[LEVEL_LIST]; - std::vector levels = Util::string2VectorDouble( levelListStr, LEVEL_SEPARATOR ); - if ( levels.size() > 0 ){ + bool validLevels = false; + std::vector levels = Util::string2VectorDouble( levelListStr, &validLevels, LEVEL_SEPARATOR ); + if ( validLevels ){ result = setVisibility( setName, levels, visible ); } else { - result = "Contour level(s) must be numbers: "+levelListStr; + result = "Could not set visibility; invalid contour levels: "+levelListStr; } } else { @@ -655,15 +682,21 @@ void ContourControls::selectContourSet( const QString& name ){ QString ContourControls::setAlpha( const QString& contourName, std::vector& levels, int transparency ){ QString result; - DataContours* target = _getContour( contourName ); - if ( target != nullptr ){ - result = target->setAlpha( levels, transparency ); - if ( result.isEmpty() ){ - _updateContourSetState(); + int levelCount = levels.size(); + if ( levelCount > 0 ){ + DataContours* target = _getContour( contourName ); + if ( target != nullptr ){ + result = target->setAlpha( levels, transparency ); + if ( result.isEmpty() ){ + _updateContourSetState(); + } + } + else { + result = "Unrecognized contour set: "+ contourName; } } else { - result = "Unrecognized contour set: "+ contourName; + result = "Please specify one or more levels when setting transparency"; } return result; } @@ -671,15 +704,21 @@ QString ContourControls::setAlpha( const QString& contourName, QStringList ContourControls::setColor( const QString& contourName, std::vector& levels, int red, int green, int blue ){ QStringList result; - DataContours* target = _getContour( contourName ); - if ( target != nullptr ){ - result = target->setColor( levels, red, green, blue ); - if ( result.isEmpty() ){ - _updateContourSetState(); + int levelCount = levels.size(); + if ( levelCount > 0 ){ + DataContours* target = _getContour( contourName ); + if ( target != nullptr ){ + result = target->setColor( levels, red, green, blue ); + if ( result.isEmpty() ){ + _updateContourSetState(); + } + } + else { + result.append( "Unrecognized contour set: "+ contourName); } } else { - result.append( "Unrecognized contour set: "+ contourName); + result.append( "Please specify one or more contour levels when specifying the color"); } return result; } @@ -735,22 +774,28 @@ QString ContourControls::setLevelMin( double value ){ QString ContourControls::setLevels( const QString& contourName, std::vector& levels ){ QString result; DataContours* target = _getContour( contourName ); - if ( target != nullptr ){ - bool levelCount = target->getLevelCount(); - bool levelsChanged = target->setLevels( levels ); - if ( levelsChanged ){ - _updateContourSetState(); - if ( levelCount != levels.size() ){ - //Note: A state refresh here is needed because the user could add a duplicate contour level in - //the UI. The code will refuse to accept it so the state will not officially change and get flushed - //to the UI. However, the UI needs the old state so it can remove the duplicate the user added. - CartaObject::refreshState(); - m_state.flushState(); + int newLevelCount = levels.size(); + if ( newLevelCount > 0 ){ + if ( target != nullptr ){ + bool levelCount = target->getLevelCount(); + bool levelsChanged = target->setLevels( levels ); + if ( levelsChanged ){ + _updateContourSetState(); + if ( levelCount != newLevelCount ){ + //Note: A state refresh here is needed because the user could add a duplicate contour level in + //the UI. The code will refuse to accept it so the state will not officially change and get flushed + //to the UI. However, the UI needs the old state so it can remove the duplicate the user added. + CartaObject::refreshState(); + m_state.flushState(); + } } } + else { + result = "Unrecognized contour set: "+ contourName; + } } else { - result = "Unrecognized contour set: "+ contourName; + result = "A contour set must have at least one level."; } return result; } @@ -758,14 +803,20 @@ QString ContourControls::setLevels( const QString& contourName, std::vector& levels, const QString& lineStyle ){ QString result; DataContours* target = _getContour( contourName ); - if ( target != nullptr ){ - result = target->setLineStyle( levels, lineStyle ); - if ( result.isEmpty() ){ - _updateContourSetState(); + int levelCount = levels.size(); + if ( levelCount > 0 ){ + if ( target != nullptr ){ + result = target->setLineStyle( levels, lineStyle ); + if ( result.isEmpty() ){ + _updateContourSetState(); + } + } + else { + result = "Unrecognized contour set: "+ contourName; } } else { - result = "Unrecognized contour set: "+ contourName; + result = "Please specify one or more levels when setting the line style."; } return result; } @@ -778,15 +829,21 @@ QString ContourControls::setSpacingInterval( double interval ){ QString ContourControls::setThickness( const QString& contourName, std::vector& levels, double thickness ){ QString result; - DataContours* target = _getContour( contourName ); - if ( target != nullptr ){ - result = target->setThickness( levels, thickness ); - if ( result.isEmpty() ){ - _updateContourSetState(); + int levelCount = levels.size(); + if ( levelCount > 0 ){ + DataContours* target = _getContour( contourName ); + if ( target != nullptr ){ + result = target->setThickness( levels, thickness ); + if ( result.isEmpty() ){ + _updateContourSetState(); + } + } + else { + result = "Unrecognized contour set: "+ contourName; } } else { - result = "Unrecognized contour set: "+ contourName; + result = "Please specify one or more contour levels when setting contour level thickness"; } return result; } @@ -794,15 +851,21 @@ QString ContourControls::setThickness( const QString& contourName, QString ContourControls::setVisibility( const QString& contourName, std::vector& levels, bool visible ){ QString result; - DataContours* target = _getContour( contourName ); - if ( target != nullptr ){ - result = target->setVisibility( levels, visible ); - if ( result.isEmpty() ){ - _updateContourSetState(); + int levelCount = levels.size(); + if ( levelCount > 0 ){ + DataContours* target = _getContour( contourName ); + if ( target != nullptr ){ + result = target->setVisibility( levels, visible ); + if ( result.isEmpty() ){ + _updateContourSetState(); + } + } + else { + result = "Unrecognized contour set: "+ contourName; } } else { - result = "Unrecognized contour set: "+ contourName; + result = "Please specify one or more contour levels when setting the visibility."; } return result; } diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index a650510f..06569758 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -135,6 +135,9 @@ bool Controller::addData(const QString& fileName) { m_datas.append(std::shared_ptr(targetSource)); targetSource->_viewResize( m_viewSize ); + targetSource->_setGlobalColor( m_stateColor ); + connect( targetSource, SIGNAL(colorStateChanged()), this, SLOT( _colorMapChanged() )); + //Update the data selectors upper bound based on the data. int visibleCount = getStackedImageCountVisible(); m_selectImage->setUpperBound( visibleCount ); @@ -250,6 +253,10 @@ QString Controller::closeImage( const QString& name ){ return result; } +void Controller::_colorMapChanged(){ + _render(); +} + int Controller::_getDataIndex( ) const { int index = m_selectImage->getIndex(); int dataIndex = -1; @@ -548,7 +555,7 @@ std::vector Controller::_getFrameIndices( int imageIndex ) const { return frames; } -QString Controller::getPreferencesId() const { +QString Controller::_getPreferencesId() const { QString id; if ( m_settings.get() != nullptr ){ id = m_settings->getPath(); @@ -556,6 +563,17 @@ QString Controller::getPreferencesId() const { return id; } +std::vector< std::shared_ptr > Controller::getSelectedColorStates(){ + std::vector< std::shared_ptr > colorStates; + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isSelected() ){ + colorStates.push_back( m_datas[i]->_getColorState() ); + } + } + return colorStates; +} + int Controller::getStackedImageCount() const { int count = m_datas.size(); return count; @@ -658,30 +676,51 @@ void Controller::_gridChanged( const StateInterface& state, bool applyAll ){ void Controller::_initializeCallbacks(){ addCommandCallback( "hideImage", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {IMAGE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString imageIndexStr = dataValues[*keys.begin()]; + bool validInt = false; + int imageIndex = imageIndexStr.toInt( &validInt ); + QString result; + if ( validInt ){ + result = setImageVisibility( imageIndex, false ); + } + else { + result = "Invalid image hide index: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "showImage", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { std::set keys = {IMAGE}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString imageName = dataValues[*keys.begin()]; - QString result = setImageVisibility( imageName, false ); + QString imageIndexStr = dataValues[*keys.begin()]; + bool validInt = false; + int imageIndex = imageIndexStr.toInt( &validInt ); + QString result; + if ( validInt ){ + result = setImageVisibility( imageIndex, true ); + } + else { + result = "Invalid image show index: "+params; + } Util::commandPostProcess( result ); return result; }); - addCommandCallback( "showImage", [=] (const QString & /*cmd*/, - const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {IMAGE}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString imageName = dataValues[*keys.begin()]; - QString result = setImageVisibility( imageName, true ); - Util::commandPostProcess( result ); - return result; - }); - addCommandCallback( "setImageOrder", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {"images"}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QStringList images = dataValues[*keys.begin()].split(";"); - QString result = setImageOrder( images ); + bool parseError = false; + std::vector vals = Util::string2VectorInt( params, &parseError ); + QString result; + if ( !parseError ){ + result = setImageOrder( vals ); + } + else { + result = "Image order must be expressed as nonnegative integers: "+params; + } Util::commandPostProcess( result ); return result; }); @@ -751,17 +790,24 @@ void Controller::_initializeCallbacks(){ addCommandCallback( CENTER, [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) ->QString { - auto vals = Util::string2VectorDouble( params ); + bool parseError = false; + QString result; + auto vals = Util::string2VectorDouble( params, &parseError ); int count = vals.size(); if ( count > 1 ) { updatePan( vals[0], vals[1]); } - return ""; + else { + result = "Center command must include doubles specifying the point to center: "+params; + } + Util::commandPostProcess( result ); + return result; }); addCommandCallback( ZOOM, [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) ->QString { - auto vals = Util::string2VectorDouble( params ); + bool error = false; + auto vals = Util::string2VectorDouble( params, &error ); if ( vals.size() > 2 ) { double centerX = vals[0]; double centerY = vals[1]; @@ -791,7 +837,7 @@ void Controller::_initializeCallbacks(){ addCommandCallback( "registerPreferences", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QString result = getPreferencesId(); + QString result = _getPreferencesId(); return result; }); @@ -838,6 +884,21 @@ void Controller::_initializeCallbacks(){ return result; }); + addCommandCallback( "setLayersSelected", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + QString result; + bool error = false; + std::vector vals = Util::string2VectorInt( params, &error ); + if ( error ){ + result = "Please specify the layers to select as nonnegative integers"; + } + else { + result = setLayersSelected( vals ); + } + Util::commandPostProcess( result ); + return result; + }); + addCommandCallback( "saveImage", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { std::set keys = {DATA_PATH}; @@ -994,34 +1055,26 @@ void Controller::_renderingDone( QImage img){ _scheduleFrameRepaint( img ); } -QString Controller::setImageOrder( const QStringList& imageNames ){ +QString Controller::setImageOrder( const std::vector& indices ){ QString result; bool imageReordered = false; int selectedIndex = _getDataIndex(); int dataCount = m_datas.size(); - int nameCount = imageNames.size(); - if ( nameCount != dataCount ){ + int indexCount = indices.size(); + if ( indexCount != dataCount ){ result = "Reorder image size must match the stack count: "+QString::number(dataCount); } else { - DataLoader* dataLoader = Util::findSingletonObject(); - QString rootDir = dataLoader->getRootDir( "" ); - for ( int i = 0; i < nameCount; i++ ){ - QString targetName = rootDir +QDir::separator()+imageNames[i]; - int targetIndex = -1; - for ( int j = i; j < dataCount; j++ ){ - QString imageName = m_datas[j]->_getFileName(); - if ( targetName == imageName ){ - targetIndex = j; - break; - } - } - if ( targetIndex < 0 ){ - result = "Reorder failed: unknown image: "+targetName; + + for ( int i = 0; i < indexCount; i++ ){ + int targetIndex = indices[i]; + + if ( targetIndex < 0 || targetIndex >= dataCount ){ + result = "Reorder failed: unknown image index: "+targetIndex; break; } //Insert the image at the target index at position i. - else if ( targetIndex > i ){ + else { std::shared_ptr item = m_datas.takeAt( targetIndex ); m_datas.insert(i, item ); @@ -1061,8 +1114,6 @@ void Controller::resetStateData( const QString& state ){ Carta::State::StateInterface dataState( ""); dataState.setState( state ); - - //Reset the image select state. QString dataStateStr = dataState.getValue( Selection::IMAGE ); m_selectImage ->resetState( dataStateStr ); @@ -1126,6 +1177,7 @@ void Controller::resetZoom(){ } } + void Controller::saveState() { int dataCount = m_datas.size(); int oldDataCount = m_stateData.getArraySize(DATA ); @@ -1262,36 +1314,6 @@ QString Controller::setClipValue( double clipVal ) { } -void Controller::setColorInverted( bool inverted ){ - for ( std::shared_ptr data : m_datas ){ - data->setColorInverted( inverted ); - } - _render(); -} - -void Controller::setColorMap( const QString& name ){ - for ( std::shared_ptr data : m_datas ){ - data->setColorMap( name ); - } - _render(); -} - -void Controller::setColorReversed( bool reversed ){ - for ( std::shared_ptr data : m_datas ){ - data->setColorReversed( reversed ); - } - _render(); -} - -void Controller::setColorAmounts( double newRed, double newGreen, double newBlue ){ - for ( std::shared_ptr data : m_datas ){ - data->setColorAmounts( newRed, newGreen, newBlue ); - } - _render(); -} - - - void Controller::_setFrameAxis(int value, AxisInfo::KnownType axisType ) { int axisIndex = static_cast( axisType ); int selectCount = m_selects.size(); @@ -1339,56 +1361,89 @@ void Controller::setFrameImage( int val) { } } -void Controller::setGamma( double gamma ){ - for ( std::shared_ptr data : m_datas ){ - data->setGamma( gamma ); - } - _render(); + +void Controller::setGlobalColor( std::shared_ptr colorState ){ + m_stateColor = colorState; } -QString Controller::setImageVisibility( const QString& name, bool visible ){ +QString Controller::setImageVisibility( int dataIndex, bool visible ){ QString result; - bool visibilitySet = false; int dataCount = m_datas.size(); - int dataIndex = -1; + if ( dataIndex >= 0 && dataIndex < dataCount ){ + bool oldVisible = m_datas[dataIndex]->_isVisible(); + if ( oldVisible != visible ){ + m_datas[dataIndex]->_setVisible( visible ); + + int selectedImageIndex = _getDataIndex(); + //Update the upper bound on the number of images available. + int visibleCount = getStackedImageCountVisible(); + m_selectImage->setUpperBound( visibleCount ); + emit dataChanged( this ); + //Render the image if it is the one currently being viewed. + if ( selectedImageIndex == dataIndex ){ + _loadView(); + } + if ( visibleCount == 0 ){ + _clearStatistics(); + } + saveState(); + } + } + else { + result = "Could not set image visibility; invalid index: "+ QString::number( dataIndex ); + } + return result; +} + +QString Controller::setLayersSelected( const std::vector indices ){ + int dataCount = m_datas.size(); + //Note: we could to a careful check of the indices here to make sure + //the are all nonnegative, and not outside of the required bounds, but + //that would be slower so we just do a rudimentary check that the size + //is okay. + QString result; + std::vector sortedLayers = indices; + std::sort( sortedLayers.begin(), sortedLayers.end()); + int selectIndex = 0; + int indexCount = indices.size(); + int selectedIndex = dataCount; + if ( indexCount > 0 ){ + selectedIndex = indices[0]; + } + std::vector oldSelects; for ( int i = 0; i < dataCount; i++ ){ - if ( m_datas[i]->_isMatch( name ) ){ - bool oldVisible = m_datas[i]->_isVisible(); - if ( oldVisible != visible ){ - m_datas[i]->_setVisible( visible ); - visibilitySet = true; + if ( m_datas[i]->_isSelected() ){ + oldSelects.push_back( i ); + } + if ( i < selectedIndex ){ + m_datas[i]->_setSelected( false ); + } + else if ( i == selectedIndex ){ + m_datas[i]->_setSelected( true ); + selectIndex++; + if ( selectIndex < indexCount ){ + selectedIndex = indices[selectIndex]; } - dataIndex = i; + } + else { + result = "Invalid selection index: "+ QString::number( selectedIndex ); break; } } - if ( !visibilitySet ){ - result = "Could not reset the visibility of "+name; + if ( selectIndex != indexCount){ + result = "Invalid selection indices"; + } + if ( !result.isEmpty( )){ + //Set the selections back the way they were + setLayersSelected( oldSelects ); } else { - int selectedImageIndex = _getDataIndex(); - //Update the upper bound on the number of images available. - int visibleCount = getStackedImageCountVisible(); - m_selectImage->setUpperBound( visibleCount ); - emit dataChanged( this ); - //Render the image if it is the one currently being viewed. - if ( selectedImageIndex == dataIndex ){ - _loadView(); - } - if ( visibleCount == 0 ){ - _clearStatistics(); - } - saveState(); + emit colorChanged( this ); } return result; } -void Controller::setTransformData( const QString& name ){ - for ( std::shared_ptr data : m_datas ){ - data->_setTransformData( name ); - } - _render(); -} + void Controller::setZoomLevel( double zoomFactor ){ int dataIndex = _getDataIndex(); diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index df66a84a..b17647fa 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -6,7 +6,6 @@ #include #include -#include #include #include "CartaLib/CartaLib.h" #include "CartaLib/AxisInfo.h" @@ -35,6 +34,7 @@ namespace Carta { namespace Carta { namespace Data { +class ColorState; class ControllerData; class DisplayControls; class GridControls; @@ -45,7 +45,7 @@ class RegionRectangle; class Selection; class Controller: public QObject, public Carta::State::CartaObject, - public IColoredView, public IPercentIntensityMap { + public IPercentIntensityMap { friend class Animator; @@ -188,7 +188,15 @@ class Controller: public QObject, public Carta::State::CartaObject, QStringList getOutputSize( ); - QString getPreferencesId() const; + QString _getPreferencesId() const; + + /** + * Get the color map information for the data sources that have been + * selected. + * @return - a list containing color map information for the data sources + * that have been selected. + */ + std::vector< std::shared_ptr > getSelectedColorStates(); /** * Return a count of the number of image layers in the stack. @@ -309,6 +317,12 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString saveImage( const QString& filename ); + + /** + * Save the state of this controller. + */ + void saveState(); + /** * Set whether or not clip values should be recomputed when the frame changes. * @param autoClip - whether or not clips should be recomputed when the frame @@ -322,36 +336,12 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void setFrameImage(int imageIndex); - - /** - * Set the data transform. - * @param name QString a unique identifier for a data transform. - */ - void setTransformData( const QString& name ); - - //IColoredView interface. - virtual void setColorMap( const QString& colorMapName ) Q_DECL_OVERRIDE; - virtual void setColorInverted( bool inverted ) Q_DECL_OVERRIDE; - virtual void setColorReversed( bool reversed ) Q_DECL_OVERRIDE; - virtual void setColorAmounts( double newRed, double newGreen, double newBlue ) Q_DECL_OVERRIDE; - virtual void setGamma( double gamma ) Q_DECL_OVERRIDE; - - - /** - * Save the state of this controller. - */ - void saveState(); - - - /** * Set the zoom level * @param zoomLevel either positive or negative depending on the desired zoom direction. */ void setZoomLevel( double zoomLevel ); - - /** * Set the overall clip amount for the data. * @param clipValue a number between 0 and 1. @@ -359,21 +349,33 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString setClipValue( double clipValue ); + /** + * Set a new color map state. + * @param colorState - the new color map information. + */ + void setGlobalColor( std::shared_ptr colorState ); + /** * Specify a new image order. - * @param imageNames - a list specifying a new order for the images in + * @param imageIndices - a list specifying a new order for the images in * a layer. * @return an error message if the new image order could not be set; * otherwise, an empty string. */ - QString setImageOrder( const QStringList& imageNames ); + QString setImageOrder( const std::vector& imageIndices ); /** * Show/hide a particular layer in the stack. - * @param name - an identifier for a layer in the stack. + * @param dataIndex - the index of a layer in the stack. * @param visible - true if the layer should be visible; false otherwise. */ - QString setImageVisibility( const QString& name, bool visible ); + QString setImageVisibility( int dataIndex, bool visible ); + + /** + * Set the indices of the selected data sources. + * @param indices - a list of indices of selected data sources. + */ + QString setLayersSelected( const std::vector indices ); /** * Change the pan of the current image. @@ -419,6 +421,11 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void clipsChanged( double minPercentile, double maxPercentile ); + /** + * Notification that one or more color map(s) have changed. + */ + void colorChanged( Controller* controller ); + /** * Notification that the image/selection managed by this controller has * changed. @@ -427,13 +434,12 @@ class Controller: public QObject, public Carta::State::CartaObject, void dataChanged(Controller* controller ); - - - /// Return the result of SaveFullImage() after the image has been rendered /// and a save attempt made. void saveImageResult( bool result ); + + protected: virtual QString getSnapType(CartaObject::SnapshotType snapType) const Q_DECL_OVERRIDE; @@ -441,6 +447,8 @@ private slots: void _displayAxesChanged(std::vector displayAxisTypes, bool applyAll); + void _colorMapChanged(); + void _contoursChanged(); void _gridChanged( const Carta::State::StateInterface& state, bool applyAll ); @@ -549,6 +557,8 @@ private slots: //Data available to and managed by this controller. QList > m_datas; + std::shared_ptr m_stateColor; + QList m_regions; diff --git a/carta/cpp/core/Data/Image/ControllerData.cpp b/carta/cpp/core/Data/Image/ControllerData.cpp index 0d6cf43f..9b0fc321 100755 --- a/carta/cpp/core/Data/Image/ControllerData.cpp +++ b/carta/cpp/core/Data/Image/ControllerData.cpp @@ -6,8 +6,10 @@ #include "Data/Preferences/PreferencesSave.h" #include "Data/DataLoader.h" #include "Data/Util.h" +#include "Data/Colormap/ColorState.h" #include "Data/Image/Grid/AxisMapper.h" #include "Data/Image/Grid/LabelFormats.h" +#include "State/UtilState.h" #include "CartaLib/PixelPipeline/CustomizablePixelPipeline.h" #include "CartaLib/IWcsGridRenderService.h" @@ -28,6 +30,7 @@ namespace Data { const QString ControllerData::CLASS_NAME = "ControllerData"; const QString ControllerData::LAYER = "layer"; +const QString ControllerData::SELECTED = "selected"; class ControllerData::Factory : public Carta::State::CartaObjectFactory { @@ -144,6 +147,9 @@ QPointF ControllerData::_getCenter() const{ return center;; } +std::shared_ptr ControllerData::_getColorState(){ + return m_stateColor; +} Carta::State::StateInterface ControllerData::_getGridState() const { return m_dataGrid->_getState(); @@ -313,10 +319,20 @@ QString ControllerData::_getLayerString() const { void ControllerData::_initializeState(){ m_state.insertValue(DataSource::DATA_PATH, ""); m_state.insertValue(Util::VISIBLE, true ); + m_state.insertValue(SELECTED, false ); + QString gridState = _getGridState().toString(); m_state.insertObject(DataGrid::GRID, gridState ); } +/*bool ControllerData::_isGlobalColorMap() const { + return m_stateColor->_isGlobal(); +}*/ + +bool ControllerData::_isSelected() const { + return m_state.getValue( SELECTED ); +} + bool ControllerData::_isVisible() const { return m_state.getValue(Util::VISIBLE); } @@ -409,6 +425,7 @@ void ControllerData::_load(vector frames, bool recomputeClipsOnNewFrame, gridService->setInputImage( m_dataSource->_getImage() ); } } + _render( frames, cs ); } } @@ -564,28 +581,53 @@ bool ControllerData::_setFileName( const QString& fileName ){ return successfulLoad; } -void ControllerData::setColorMap( const QString& name ){ - if ( m_dataSource ){ - m_dataSource->setColorMap( name ); - } -} -void ControllerData::setColorInverted( bool inverted ){ - if ( m_dataSource ){ - m_dataSource->setColorInverted( inverted ); +void ControllerData::_setGlobalColor( std::shared_ptr colorState ){ + if ( m_stateColor ){ + disconnect( m_stateColor.get() ); } + m_stateColor = colorState; + _colorChanged( ); + connect( m_stateColor.get(), SIGNAL( colorStateChanged()), this, SLOT(_colorChanged())); } -void ControllerData::setColorReversed( bool reversed ){ + +void ControllerData::_colorChanged(){ if ( m_dataSource ){ - m_dataSource->setColorReversed( reversed ); + + QString mapName = m_stateColor->_getColorMap(); + qDebug() << "ControllerData color changed "<_setColorMap( mapName ); + m_dataSource->_setTransformData( m_stateColor->_getDataTransform() ); + m_dataSource->_setGamma( m_stateColor->_getGamma() ); + m_dataSource->_setColorReversed( m_stateColor->_isReversed() ); + m_dataSource->_setColorInverted( m_stateColor->_isInverted() ); + double redAmount = m_stateColor->_getMixRed(); + double greenAmount = m_stateColor->_getMixGreen(); + double blueAmount = m_stateColor->_getMixBlue(); + m_dataSource->_setColorAmounts( redAmount, greenAmount, blueAmount ); + bool defaultNan = m_stateColor->_isNanDefault(); + m_dataSource->_setNanDefault( defaultNan ); + qDebug() << "Controller data defaultNan="<_getNanRed(); + int greenAmount = m_stateColor->_getNanGreen(); + int blueAmount = m_stateColor->_getNanBlue(); + m_dataSource->_setColorNan( redAmount, greenAmount, blueAmount ); + } + //We are using the default nan color. Update the state to the default color. + else { + QColor nanColor = m_dataSource->_getNanColor(); + m_stateColor->_setNanColor( nanColor.red(), nanColor.green(), nanColor.blue() ); + } + emit colorStateChanged(); } } -void ControllerData::setColorAmounts( double newRed, double newGreen, double newBlue ){ - if ( m_dataSource ){ - m_dataSource->setColorAmounts( newRed, newGreen, newBlue ); - } +void ControllerData::_setSelected( bool selected ){ + m_state.setValue( SELECTED, selected ); } void ControllerData::_setVisible( bool visible ){ @@ -601,11 +643,6 @@ void ControllerData::_setPan( double imgX, double imgY ){ } } -void ControllerData::_setTransformData( const QString& name ){ - if ( m_dataSource ){ - m_dataSource->_setTransformData( name ); - } -} void ControllerData::_setZoom( double zoomAmount){ if ( m_dataSource ){ @@ -615,12 +652,6 @@ void ControllerData::_setZoom( double zoomAmount){ -void ControllerData::setGamma( double gamma ){ - if ( m_dataSource ){ - m_dataSource->setGamma( gamma ); - } -} - void ControllerData::_updateClips( std::shared_ptr& view, double minClipPercentile, double maxClipPercentile, const std::vector& frames ){ if ( m_dataSource ){ diff --git a/carta/cpp/core/Data/Image/ControllerData.h b/carta/cpp/core/Data/Image/ControllerData.h index fe865d73..94639e1b 100755 --- a/carta/cpp/core/Data/Image/ControllerData.h +++ b/carta/cpp/core/Data/Image/ControllerData.h @@ -6,7 +6,6 @@ #include "State/ObjectManager.h" #include "State/StateInterface.h" -#include "Data/IColoredView.h" #include "CartaLib/IImage.h" #include "CartaLib/AxisInfo.h" #include "CartaLib/AxisLabelInfo.h" @@ -40,12 +39,14 @@ namespace Core { namespace Data { +class ColorState; class DrawSynchronizer; class DataContours; class DataGrid; class DataSource; -class ControllerData : public QObject, public Carta::State::CartaObject, public IColoredView { + +class ControllerData : public QObject, public Carta::State::CartaObject { friend class Controller; @@ -53,42 +54,9 @@ Q_OBJECT public: - /** - * Sets a new color map. - * @param name the identifier for the color map. - */ - virtual void setColorMap( const QString& name ) Q_DECL_OVERRIDE; - - /** - * Sets whether the colors in the map are inverted. - * @param inverted true if the colors in the map are inverted; false - * otherwise. - */ - virtual void setColorInverted( bool inverted ) Q_DECL_OVERRIDE; - - /** - * Sets whether the colors in the map are reversed. - * @param reversed true if the colors in the map are reversed; false - * otherwise. - */ - virtual void setColorReversed( bool reversed ) Q_DECL_OVERRIDE; - - /** - * Set the amount of red, green, and blue in the color scale. - * @param newRed the amount of red; should be in the range [0,1]. - * @param newGreen the amount of green; should be in the range [0,1]. - * @param newBlue the amount of blue; should be in the range[0,1]. - */ - virtual void setColorAmounts( double newRed, double newGreen, double newBlue ) Q_DECL_OVERRIDE; - - /** - * Set the gamma color map parameter. - * @param gamma a color map parameter. - */ - virtual void setGamma( double gamma ) Q_DECL_OVERRIDE; - - static const QString CLASS_NAME; - static const QString LAYER; + + static const QString CLASS_NAME; + virtual ~ControllerData(); @@ -101,6 +69,8 @@ Q_OBJECT /// and a save attempt made. void saveImageResult( bool result ); + void colorStateChanged(); + private slots: //Notification from the rendering service that a new image and assiciated vector @@ -114,6 +84,8 @@ private slots: // Asynchronous result from saveFullImage(). void _saveImageResultCB( bool result ); + void _colorChanged(); + private: void _clearData(); @@ -129,6 +101,7 @@ private slots: * @return - information about how the axis labels should be formatted. */ Carta::Lib::AxisLabelInfo _getAxisLabelInfo( int axisIndex, Carta::Lib::AxisInfo::KnownType axisType ) const; + /** * Return the number of frames for the given axis in the image. * @param type - the axis for which a frame count is needed. @@ -215,7 +188,13 @@ private slots: * @return true if the computed intensity is valid; otherwise false. */ bool _getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; + + /** + * Returns information about this layer in the stack. + * @return - a string representation of layer specific information. + */ QString _getLayerString() const; + /** * Returns the pipeline responsible for rendering the image. * @retun the pipeline responsible for rendering the image. @@ -261,6 +240,12 @@ private slots: */ QString _getPixelUnits() const; + /** + * Return stored information about the color map. + * @return - information about the color map. + */ + std::shared_ptr _getColorState(); + /** * Return the coordinates at pixel (x, y) in the given coordinate system. * @param x the x-coordinate of the desired pixel. @@ -292,6 +277,12 @@ private slots: void _initializeState(); void _initializeSingletons( ); + /** + * Returns true if this data is selected; false otherwise. + * @return true if this data is selected; false otherwise. + */ + bool _isSelected() const; + /** * Returns true if this layer is not hidden; false otherwise. * @return true if the layer is visible; false otherwise. @@ -355,6 +346,12 @@ private slots: //Note: The rendered contour set is an accumulation of all the contour sets. void _setContours( std::shared_ptr contours ); + /** + * Reset the color map information for this data. + * @param colorState - stored information about the color map. + */ + void _setGlobalColor( std::shared_ptr colorState ); + /** * Show/hide this layer. * @param visible - true to show the layer; false to hide it. @@ -382,12 +379,12 @@ private slots: */ bool _setFileName( const QString& fileName ); - /** - * Set the data transform. - * @param name QString a unique identifier for a data transform. + * Set this data source selected. + * @param selected - true if the data source is selected; false otherwise. */ - void _setTransformData( const QString& name ); + void _setSelected( bool selected ); + /** * Resize the view of the image. */ @@ -404,7 +401,10 @@ private slots: class Factory; static bool m_registered; + static const QString LAYER; + static const QString SELECTED; + std::shared_ptr m_stateColor; std::unique_ptr m_dataGrid; diff --git a/carta/cpp/core/Data/Image/DataSource.cpp b/carta/cpp/core/Data/Image/DataSource.cpp index 15ddc19b..4e4c1845 100755 --- a/carta/cpp/core/Data/Image/DataSource.cpp +++ b/carta/cpp/core/Data/Image/DataSource.cpp @@ -374,6 +374,11 @@ bool DataSource::_getIntensity( int frameLow, int frameHigh, double percentile, return intensityFound; } +QColor DataSource::_getNanColor() const { + QColor nanColor = m_renderService->getNanColor(); + return nanColor; +} + double DataSource::_getPercentile( int frameLow, int frameHigh, double intensity ) const { double percentile = 0; int spectralIndex = _getAxisIndex( AxisInfo::KnownType::SPECTRAL); @@ -614,7 +619,17 @@ void DataSource::_resetPan(){ } } - +void DataSource::_resizeQuantileCache(){ + m_quantileCache.resize(0); + int nf = 1; + int imageSize = m_image->dims().size(); + for ( int i = 0; i < imageSize; i++ ){ + if ( i != m_axisIndexX && i != m_axisIndexY ){ + nf = nf * m_image->dims()[i]; + } + } + m_quantileCache.resize( nf); +} bool DataSource::_setFileName( const QString& fileName ){ QString file = fileName.trimmed(); @@ -656,19 +671,7 @@ bool DataSource::_setFileName( const QString& fileName ){ } -void DataSource::_resizeQuantileCache(){ - m_quantileCache.resize(0); - int nf = 1; - int imageSize = m_image->dims().size(); - for ( int i = 0; i < imageSize; i++ ){ - if ( i != m_axisIndexX && i != m_axisIndexY ){ - nf = nf * m_image->dims()[i]; - } - } - m_quantileCache.resize( nf); -} - -void DataSource::setColorMap( const QString& name ){ +void DataSource::_setColorMap( const QString& name ){ Carta::State::ObjectManager* objManager = Carta::State::ObjectManager::objectManager(); Carta::State::CartaObject* obj = objManager->getObject( Colormaps::CLASS_NAME ); Colormaps* maps = dynamic_cast(obj); @@ -676,17 +679,17 @@ void DataSource::setColorMap( const QString& name ){ m_renderService ->setPixelPipeline( m_pixelPipeline, m_pixelPipeline->cacheId()); } -void DataSource::setColorInverted( bool inverted ){ +void DataSource::_setColorInverted( bool inverted ){ m_pixelPipeline-> setInvert( inverted ); m_renderService-> setPixelPipeline( m_pixelPipeline, m_pixelPipeline-> cacheId()); } -void DataSource::setColorReversed( bool reversed ){ +void DataSource::_setColorReversed( bool reversed ){ m_pixelPipeline-> setReverse( reversed ); m_renderService-> setPixelPipeline( m_pixelPipeline, m_pixelPipeline-> cacheId()); } -void DataSource::setColorAmounts( double newRed, double newGreen, double newBlue ){ +void DataSource::_setColorAmounts( double newRed, double newGreen, double newBlue ){ std::array colorArray; colorArray[0] = newRed; colorArray[1] = newGreen; @@ -695,6 +698,11 @@ void DataSource::setColorAmounts( double newRed, double newGreen, double newBlue m_renderService->setPixelPipeline( m_pixelPipeline, m_pixelPipeline->cacheId()); } +void DataSource::_setColorNan( double red, double green, double blue ){ + QColor nanColor( red, green, blue ); + m_renderService->setNanColor( nanColor ); +} + bool DataSource::_setDisplayAxis( AxisInfo::KnownType axisType, int* axisIndex ){ bool displayAxisChanged = false; if ( m_image ){ @@ -725,6 +733,10 @@ void DataSource::_setDisplayAxes(std::vector displayAxisTyp } } +void DataSource::_setNanDefault( bool nanDefault ){ + m_renderService->setDefaultNan( nanDefault ); +} + void DataSource::_setPan( double imgX, double imgY ){ m_renderService-> setPan( QPointF(imgX,imgY) ); } @@ -743,7 +755,7 @@ void DataSource::_setZoom( double zoomAmount){ -void DataSource::setGamma( double gamma ){ +void DataSource::_setGamma( double gamma ){ m_pixelPipeline->setGamma( gamma ); m_renderService->setPixelPipeline( m_pixelPipeline, m_pixelPipeline->cacheId()); } diff --git a/carta/cpp/core/Data/Image/DataSource.h b/carta/cpp/core/Data/Image/DataSource.h index 010a6dc5..84803218 100755 --- a/carta/cpp/core/Data/Image/DataSource.h +++ b/carta/cpp/core/Data/Image/DataSource.h @@ -5,7 +5,6 @@ #pragma once #include "CartaLib/Nullable.h" -#include "Data/IColoredView.h" #include "CartaLib/AxisDisplayInfo.h" #include "CartaLib/CartaLib.h" #include "CartaLib/AxisInfo.h" @@ -41,7 +40,7 @@ namespace Data { class CoordinateSystems; -class DataSource : public QObject, public IColoredView { +class DataSource : public QObject { friend class ControllerData; @@ -49,39 +48,6 @@ Q_OBJECT public: - /** - * Sets a new color map. - * @param name the identifier for the color map. - */ - virtual void setColorMap( const QString& name ) Q_DECL_OVERRIDE; - - /** - * Sets whether the colors in the map are inverted. - * @param inverted true if the colors in the map are inverted; false - * otherwise. - */ - virtual void setColorInverted( bool inverted ) Q_DECL_OVERRIDE; - - /** - * Sets whether the colors in the map are reversed. - * @param reversed true if the colors in the map are reversed; false - * otherwise. - */ - virtual void setColorReversed( bool reversed ) Q_DECL_OVERRIDE; - - /** - * Set the amount of red, green, and blue in the color scale. - * @param newRed the amount of red; should be in the range [0,1]. - * @param newGreen the amount of green; should be in the range [0,1]. - * @param newBlue the amount of blue; should be in the range[0,1]. - */ - virtual void setColorAmounts( double newRed, double newGreen, double newBlue ) Q_DECL_OVERRIDE; - - /** - * Set the gamma color map parameter. - * @param gamma a color map parameter. - */ - virtual void setGamma( double gamma ) Q_DECL_OVERRIDE; static const QString CLASS_NAME; static const double ZOOM_DEFAULT; @@ -246,6 +212,12 @@ Q_OBJECT */ bool _getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; + /** + * Returns the color used to draw nan pixels. + * @return - the color used to draw nan pixels. + */ + QColor _getNanColor() const; + /** * Returns the pipeline responsible for rendering the image. * @retun the pipeline responsible for rendering the image. @@ -354,6 +326,42 @@ Q_OBJECT void _resizeQuantileCache(); + /** + * Sets a new color map. + * @param name the identifier for the color map. + */ + void _setColorMap( const QString& name ); + + /** + * Sets whether the colors in the map are inverted. + * @param inverted true if the colors in the map are inverted; false + * otherwise. + */ + void _setColorInverted( bool inverted ); + + /** + * Sets whether the colors in the map are reversed. + * @param reversed true if the colors in the map are reversed; false + * otherwise. + */ + void _setColorReversed( bool reversed ); + + /** + * Set the amount of red, green, and blue in the color scale. + * @param newRed the amount of red; should be in the range [0,1]. + * @param newGreen the amount of green; should be in the range [0,1]. + * @param newBlue the amount of blue; should be in the range[0,1]. + */ + void _setColorAmounts( double newRed, double newGreen, double newBlue ); + + /** + * Set the color used to draw nan pixels. + * @param red - the amount of red in [0,255]. + * @param green - the amount of green in [0,255]. + * @param blue - the amount of blue in [0,255]. + */ + void _setColorNan( double red, double green, double blue ); + /** * Set the x-, y-, and z- axes that are to be displayed. * @param displayAxisTypes - the list of display axes. @@ -370,6 +378,20 @@ Q_OBJECT */ bool _setDisplayAxis( Carta::Lib::AxisInfo::KnownType axisType, int* axisIndex ); + /** + * Set the gamma color map parameter. + * @param gamma a color map parameter. + */ + void _setGamma( double gamma ); + + /** + * Set whether or not to use the default nan color (bottom of the color map). + * @param nanDefault - true if the default nan color should be used; false if + * a user set nan color should be used. + */ + void _setNanDefault( bool nanDefault ); + + /** * Set the center for this image's display. * @param imgX the x-coordinate of the center. diff --git a/carta/cpp/core/Data/Util.cpp b/carta/cpp/core/Data/Util.cpp index bbc20e96..19e6920b 100644 --- a/carta/cpp/core/Data/Util.cpp +++ b/carta/cpp/core/Data/Util.cpp @@ -79,13 +79,32 @@ double Util::roundToDigits(double value, int digits) } /// convert string to array of doubles -std::vector < double > Util::string2VectorDouble( QString s, QString sep){ +std::vector < double > Util::string2VectorDouble( QString s, bool* error, QString sep ){ QStringList lst = s.split( sep ); std::vector < double > res; + *error = false; for ( auto v : lst ) { bool ok; double val = v.toDouble( & ok ); if ( ! ok ) { + *error = true; + return res; + } + res.push_back( val ); + } + return res; +} + +/// convert string to array of doubles +std::vector < int > Util::string2VectorInt( QString s, bool* error, QString sep ){ + QStringList lst = s.split( sep ); + std::vector < int > res; + *error = false; + for ( auto v : lst ) { + bool ok; + int val = v.toInt( & ok ); + if ( ! ok ) { + *error = true; return res; } res.push_back( val ); diff --git a/carta/cpp/core/Data/Util.h b/carta/cpp/core/Data/Util.h index 09e51106..585972b2 100644 --- a/carta/cpp/core/Data/Util.h +++ b/carta/cpp/core/Data/Util.h @@ -78,9 +78,19 @@ class Util { * Converts a string containing doubles with a separator between them to a vector. * @param sep {QString} the separator. * @param s {QString} a string containing doubles separated by a delimiter. + * @param error {boolean} - true if there was an error converting to doubles; false otherwise. * @return a vector of doubles. */ - static std::vector < double > string2VectorDouble( QString s, QString sep = " " ); + static std::vector < double > string2VectorDouble( QString s, bool* error, QString sep = " " ); + + /** + * Converts a string containing integers with a separator between them to a vector. + * @param sep {QString} the separator. + * @param s {QString} a string containing integers separated by a delimiter. + * @param error {boolean} - true if there was an error converting to integers; false otherwise. + * @return a vector of integers. + */ + static std::vector < int > string2VectorInt( QString s, bool* error, QString sep = " " ); static const QString PREFERENCES; static const QString ALPHA; diff --git a/carta/cpp/core/ImageRenderService.cpp b/carta/cpp/core/ImageRenderService.cpp index dd100da9..f8786b44 100644 --- a/carta/cpp/core/ImageRenderService.cpp +++ b/carta/cpp/core/ImageRenderService.cpp @@ -26,7 +26,8 @@ static constexpr bool QtPremultipliedBugStillExists = true; /// \param m_qImage template < class Pipeline > static void -iView2qImage( NdArray::RawViewInterface * rawView, Pipeline & pipe, QImage & qImage ) +iView2qImage( NdArray::RawViewInterface * rawView, Pipeline & pipe, QImage & qImage, + QRgb nanColor) { //qDebug() << "rv2qi2" << rawView-> dims(); typedef double Scalar; @@ -65,7 +66,8 @@ iView2qImage( NdArray::RawViewInterface * rawView, Pipeline & pipe, QImage & qIm /// @todo for more efficiency, instead of forEach() we should switch to one of the /// higher performance APIs and maybe even sprinkle it with some openmp/cilk magic :) int64_t counter = 0; - QRgb nanColor = qRgb( 255, 0, 0 ); + + auto lambda = [&] ( const Scalar & ival ) { if ( Q_LIKELY( ! std::isnan( ival ) ) ) { @@ -115,6 +117,26 @@ Service::outputSize() const return m_outputSize; } +void Service::setNanColor( QColor color ){ + if ( m_nanColor != color ){ + m_nanColor = color; + } +} + +QColor Service::getNanColor() const { + QRgb nanColor = m_nanColor.rgb(); + if ( m_pixelPipelineRaw && m_defaultNan){ + double clipMin, clipMax; + m_pixelPipelineRaw-> getClips( clipMin, clipMax ); + m_pixelPipelineRaw->convertq( clipMin, nanColor ); + } + return QColor( nanColor ); +} + +void Service::setDefaultNan( bool useNanDefault){ + m_defaultNan = useNanDefault; +} + void Service::setPan( QPointF pt ) { @@ -191,7 +213,9 @@ Service::render( JobId jobId ) return m_lastSubmittedJobId; } -Service::Service( QObject * parent ) : QObject( parent ) +Service::Service( QObject * parent ) : QObject( parent ), + m_defaultNan( true ), + m_nanColor( 255, 0, 0 ) { // hook up the internal schedule helper signal to the scheduleJob slot, using // queued connection @@ -255,22 +279,33 @@ Service::internalRenderSlot() return QByteArray( (char *) ( & x ), sizeof( x ) ).toBase64(); }; + double clipMin, clipMax; + m_pixelPipelineRaw-> getClips( clipMin, clipMax ); + + QRgb nanColor = m_nanColor.rgb(); + if ( m_defaultNan ){ + m_pixelPipelineRaw->convertq( clipMin, nanColor ); + } + // cache id will be concatenation of: // view id // pipeline id // output size // pan // zoom + // nan // pixel pipeline cache settings // Floats are binary-encoded (base64) - QString cacheId = QString( "%1/%2/%3x%4/%5,%6/%7" ) + QString cacheId = QString( "%1/%2/%3x%4/%5,%6/%7/%8" ) .arg( m_inputViewCacheId ) .arg( m_pixelPipelineCacheId ) .arg( m_outputSize.width() ) .arg( m_outputSize.height() ) .arg( d2hex( m_pan.x() ) ) .arg( d2hex( m_pan.y() ) ) - .arg( d2hex( m_zoom ) ); + .arg( d2hex( m_zoom ) ) + .arg( QString::number(nanColor) ); + if ( m_pixelPipelineCacheSettings.enabled ) { cacheId += QString( "/1/%1/%2" ) @@ -308,8 +343,7 @@ Service::internalRenderSlot() return; } - double clipMin, clipMax; - m_pixelPipelineRaw-> getClips( clipMin, clipMax ); + // render the frame if needed if ( m_frameImage.isNull() ) { @@ -321,7 +355,7 @@ Service::internalRenderSlot() m_cachedPPinterp-> cache( * m_pixelPipelineRaw, pixelPipelineCacheSettings().size, clipMin, clipMax ); } - ::iView2qImage( m_inputView.get(), * m_cachedPPinterp, m_frameImage ); + ::iView2qImage( m_inputView.get(), * m_cachedPPinterp, m_frameImage, nanColor ); } else { if ( ! m_cachedPP ) { @@ -329,11 +363,11 @@ Service::internalRenderSlot() m_cachedPP-> cache( * m_pixelPipelineRaw, pixelPipelineCacheSettings().size, clipMin, clipMax ); } - ::iView2qImage( m_inputView.get(), * m_cachedPP, m_frameImage ); + ::iView2qImage( m_inputView.get(), * m_cachedPP, m_frameImage, nanColor ); } } else { - ::iView2qImage( m_inputView.get(), * m_pixelPipelineRaw, m_frameImage ); + ::iView2qImage( m_inputView.get(), * m_pixelPipelineRaw, m_frameImage, nanColor ); } } diff --git a/carta/cpp/core/ImageRenderService.h b/carta/cpp/core/ImageRenderService.h index c84fb046..27ab6159 100644 --- a/carta/cpp/core/ImageRenderService.h +++ b/carta/cpp/core/ImageRenderService.h @@ -34,6 +34,7 @@ #include "CartaLib/Nullable.h" #include #include +#include #include #include #include @@ -166,6 +167,20 @@ class Service : public QObject /// QSize outputSize() const; + //Set the color to use for nan values. + //Note: this color will be ignored if we are using a default nan value from + //the bottom of the color map. + void + setNanColor( QColor color ); + + //Return the color that will be used to draw nan values. + QColor + getNanColor() const; + + //Set whether or not to use the default nan value (bottom of the color map). + void + setDefaultNan( bool useDefaultNan ); + /// set coordinates of the data pixel to be centered in the generated /// image, in zero-based image coordinates, e.g. (0,0) is bottom left corner of pixel /// (0,0), while (1,1) is it's right-top corner, and (1/2,1/2) is it's center @@ -285,6 +300,11 @@ protected slots: /// last requested job id JobId m_lastSubmittedJobId = -1; + /// Whether or not to use the default nan value (bottom of color map). + bool m_defaultNan; + /// User-settable color for nan values when the default nan value is not used. + QColor m_nanColor; + /// timer to make sure we only fire one render signal even if multiple requests /// are submitted QTimer m_renderTimer; diff --git a/carta/cpp/core/ScriptedClient/ScriptFacade.cpp b/carta/cpp/core/ScriptedClient/ScriptFacade.cpp index f92a40c8..02bc8a45 100644 --- a/carta/cpp/core/ScriptedClient/ScriptFacade.cpp +++ b/carta/cpp/core/ScriptedClient/ScriptFacade.cpp @@ -215,7 +215,7 @@ QStringList ScriptFacade::reverseColorMap( const QString& colormapId, const QStr reverse = Carta::Data::Util::toBool( reverseStr, &validBool ); } if ( validBool ){ - QString result = colormap->reverseColorMap( reverse ); + QString result = colormap->setReverse( reverse ); resultList = QStringList( result ); } else { @@ -247,7 +247,7 @@ QStringList ScriptFacade::invertColorMap( const QString& colormapId, const QStri invert = Carta::Data::Util::toBool( invertStr, &validBool ); } if ( validBool ){ - QString result = colormap->invertColorMap( invert ); + QString result = colormap->setInvert( invert ); resultList = QStringList( result ); } else { diff --git a/carta/cpp/core/State/StateInterface.cpp b/carta/cpp/core/State/StateInterface.cpp index cbec78f4..f60bae4c 100644 --- a/carta/cpp/core/State/StateInterface.cpp +++ b/carta/cpp/core/State/StateInterface.cpp @@ -93,11 +93,12 @@ StateInterface::StateInterface (const QString & path, const QString& type, const : impl_p (new StateInterfaceImpl (path) ) { if ( path.length() > 0 || type.length() > 0 ){ - insertValue( OBJECT_TYPE, type ); + insertValue(FLUSH_STATE, false ); - insertValue(INDEX, 0 ); - } + } + insertValue( OBJECT_TYPE, type ); + insertValue(INDEX, 0 ); if ( initialState.trimmed().size() > 0 ){ flushStateImpl( initialState ); diff --git a/carta/cpp/core/core.pro b/carta/cpp/core/core.pro index 27701af6..2c361435 100755 --- a/carta/cpp/core/core.pro +++ b/carta/cpp/core/core.pro @@ -31,6 +31,7 @@ HEADERS += \ Data/Clips.h \ Data/Colormap/Colormap.h \ Data/Colormap/Colormaps.h \ + Data/Colormap/ColorState.h \ Data/Colormap/TransformsData.h \ Data/Colormap/TransformsImage.h \ Data/DataLoader.h \ @@ -38,7 +39,6 @@ HEADERS += \ Data/Error/ErrorManager.h \ Data/Histogram/Histogram.h \ Data/Histogram/ChannelUnits.h \ - Data/IColoredView.h \ Data/ILinkable.h \ Data/Settings.h \ Data/Image/Controller.h \ @@ -125,6 +125,7 @@ SOURCES += \ Data/Clips.cpp \ Data/Colormap/Colormap.cpp \ Data/Colormap/Colormaps.cpp \ + Data/Colormap/ColorState.cpp \ Data/Colormap/TransformsData.cpp \ Data/Colormap/TransformsImage.cpp \ Data/Image/Controller.cpp \ diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMix.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMix.js index caac14c6..d95603ac 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMix.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMix.js @@ -173,7 +173,7 @@ qx.Class.define("skel.widgets.Colormap.ColorMix", { var bluePercent = this._getColorPercent( this.m_blueSlider.getValue()); var path = skel.widgets.Path.getInstance(); var cmd = this.m_id + path.SEP_COMMAND + skel.widgets.Colormap.ColorMix.CMD_SET_COLORMIX; - var params = "redPercent:"+redPercent+",greenPercent:"+greenPercent+",bluePercent:"+bluePercent; + var params = "red:"+redPercent+",green:"+greenPercent+",blue:"+bluePercent; this.m_connector.sendCommand( cmd, params, this._errorColorsCB( this )); } } diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js index 1f52381f..768c41e4 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js @@ -105,7 +105,7 @@ qx.Class.define("skel.widgets.Colormap.Colormap", this.m_view.setColorName( cMap.colorMapName ); this.m_view.setInvert( cMap.invert ); this.m_view.setReverse( cMap.reverse ); - this.m_view.setScales( cMap.colorMix.redPercent, cMap.colorMix.greenPercent, cMap.colorMix.bluePercent ); + this.m_view.setScales( cMap.colorMix.red, cMap.colorMix.green, cMap.colorMix.blue ); } this.m_settings.setControls( cMap ); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js index 2da3a5da..4205a172 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js @@ -51,8 +51,8 @@ qx.Class.define("skel.widgets.Colormap.PageColorMap", { if ( this.m_colorMixSettings !== null ){ this.m_colorMixSettings.setReverse( controls.reverse ); this.m_colorMixSettings.setInvert( controls.invert ); - this.m_colorMixSettings.setMix( controls.colorMix.redPercent, - controls.colorMix.greenPercent, controls.colorMix.bluePercent ); + this.m_colorMixSettings.setMix( controls.colorMix.red, + controls.colorMix.green, controls.colorMix.blue ); this.m_colorMixSettings.setColorMapName( controls.colorMapName ); } }, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageNan.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageNan.js new file mode 100755 index 00000000..aa2d3f6f --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageNan.js @@ -0,0 +1,159 @@ +/** + * Displays controls for nan colors. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.widgets.Colormap.PageNan", { + extend : qx.ui.tabview.Page, + + /** + * Constructor. + */ + construct : function( ) { + this.base(arguments, "NaN", ""); + if ( typeof mImport !== "undefined"){ + this.m_connector = mImport("connector"); + } + this._init( ); + }, + + members : { + + /** + * Initializes the UI. + */ + _init : function( ) { + this.setPadding( 0, 0, 0, 0 ); + this.setMargin( 1, 1, 1, 1 ); + this._setLayout(new qx.ui.layout.HBox(2)); + this.m_nanDefaultCheck = new qx.ui.form.CheckBox( "Default"); + this.m_nanDefaultCheck.setToolTipText( "The bottom color map value will be used for the nan color when 'Default' is selected.") + this.m_nanId = this.m_nanDefaultCheck.addListener( "changeValue", this._nanDefaultChanged, this ); + this.m_colorSelector = new skel.widgets.CustomUI.ColorSelector(); + this.m_colorId = this.m_colorSelector.addListener( 'appear', function(){ + this.m_colorAppeared = true; + this._setColor( this.m_red, this.m_green, this.m_blue); + }, this ); + this._add( this.m_nanDefaultCheck ); + this._add( this.m_colorSelector ); + }, + + /** + * Update the enabled status & notify the server when the user + * has changed whether or not to use the default nan. + */ + _nanDefaultChanged : function(){ + this._updateColorPickerEnabledStatus(); + this._sendDefaultNanCmd(); + }, + + /** + * Updates the enabled status of the color picker based on whether + * a default nan is being used. + */ + _updateColorPickerEnabledStatus : function(){ + var defaultNan = this.m_nanDefaultCheck.getValue(); + this.m_colorSelector.setEnabled( !defaultNan ); + }, + + /** + * Notify the parent that the nan color has changed. + */ + _sendColorCmd : function(){ + if ( this.m_connector !== null ){ + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setNanColor"; + var red = this.m_colorSelector.getRed(); + var green = this.m_colorSelector.getGreen(); + var blue = this.m_colorSelector.getBlue(); + var param = "red:"+red+",green:"+green+",blue:"+blue; + this.m_connector.sendCommand( cmd, param, function(){} ); + } + }, + + /** + * Notify the server that whether or not to use the nan default color + * has changed. + */ + _sendDefaultNanCmd : function(){ + if ( this.m_connector !== null ){ + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setDefaultNan"; + var val = this.m_nanDefaultCheck.getValue(); + var param = "nanDefault:"+val; + this.m_connector.sendCommand( cmd, param, function(){} ); + } + }, + + + /** + * Update from the server when the nan color settings have changed. + * @param controls {Object} - information about the nan color + * settings from the server. + */ + setControls : function( controls ){ + var red = controls.nanColor.red; + var green = controls.nanColor.green; + var blue = controls.nanColor.blue; + this._setColor( red, green, blue ); + this._setNanDefault( controls.nanDefault ); + }, + + /** + * Update the color picker with the new nan color. + * @param red {Number} - the amount of red [0,255]. + * @param green {Number} - the amount of green [0,255]. + * @param blue {Number} - the amount of blue [0,255]. + */ + _setColor : function( red, green, blue ){ + if ( this.m_colorAppeared ){ + this.m_colorSelector.removeListenerById( this.m_colorId ); + this.m_colorSelector.setRed( red ); + this.m_colorSelector.setGreen( green ); + this.m_colorSelector.setBlue( blue ); + this.m_colorId = this.m_colorSelector.addListener( "changeValue", this._sendColorCmd, this ); + } + else { + this.m_red = red; + this.m_green = green; + this.m_blue = blue; + } + }, + + /** + * Update the UI with whether or to use the nan default color or + * not. + */ + _setNanDefault : function( nanDefault ){ + this.m_nanDefaultCheck.removeListenerById( this.m_nanId ); + this.m_nanDefaultCheck.setValue( nanDefault ); + this._updateColorPickerEnabledStatus(); + this.m_nanId = this.m_nanDefaultCheck.addListener( "changeValue", this._nanDefaultChanged, this ); + }, + + /** + * Set the server side id of this control UI. + * @param id {String} the server side id of the object that contains + * data for this control UI. + */ + setId : function( id ){ + this.m_id = id; + + }, + + m_colorAppeared : false, + m_colorId : null, + m_colorSelector : null, + m_connector : null, + m_red : null, + m_green : null, + m_blue : null, + m_id : null, + m_nanDefaultCheck : null, + m_nanId : null + + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/Settings.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/Settings.js index 83baae14..fdd694d5 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/Settings.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/Settings.js @@ -37,9 +37,11 @@ qx.Class.define("skel.widgets.Colormap.Settings", { this.m_colormapPage = new skel.widgets.Colormap.PageColorMap(); this.m_transformPage = new skel.widgets.Colormap.PageTransform(); + this.m_nanPage = new skel.widgets.Colormap.PageNan(); this.m_tabView.add( this.m_colormapPage ); this.m_tabView.add( this.m_transformPage ); + this.m_tabView.add( this.m_nanPage ); }, /** @@ -49,6 +51,7 @@ qx.Class.define("skel.widgets.Colormap.Settings", { setControls : function( controls ){ this.m_colormapPage.setControls( controls ); this.m_transformPage.setControls( controls ); + this.m_nanPage.setControls( controls ); }, @@ -60,6 +63,7 @@ qx.Class.define("skel.widgets.Colormap.Settings", { this.m_id = id; this.m_colormapPage.setId( id ); this.m_transformPage.setId( id ); + this.m_nanPage.setId( id ); }, m_id : null, @@ -67,6 +71,7 @@ qx.Class.define("skel.widgets.Colormap.Settings", { m_tabView : null, m_colormapPage : null, + m_nanPage : null, m_transformPage : null } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/ImageControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/ImageControls.js index c7dd483d..f9d5c533 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/ImageControls.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/ImageControls.js @@ -40,7 +40,7 @@ qx.Class.define("skel.widgets.Image.ImageControls", { this.add( this.m_contourControls ); this.m_stackControls = new skel.widgets.Image.Stack.StackControls(); - //this.add( this.m_stackControls ); + this.add( this.m_stackControls ); }, From 297e21ebbc474617193ac14cd502c9b107f5662a Mon Sep 17 00:00:00 2001 From: slovelan Date: Wed, 4 Nov 2015 16:09:58 -0700 Subject: [PATCH 21/37] Ability to set color map per image rather than per stack. --- carta/cpp/core/Data/Colormap/ColorState.cpp | 123 ++++++++-------- carta/cpp/core/Data/Colormap/ColorState.h | 16 ++- carta/cpp/core/Data/Colormap/Colormap.cpp | 42 ++++-- carta/cpp/core/Data/Colormap/Colormap.h | 6 + carta/cpp/core/Data/Image/Controller.cpp | 109 +++++++++++--- carta/cpp/core/Data/Image/Controller.h | 47 ++++-- carta/cpp/core/Data/Image/ControllerData.cpp | 61 ++++++-- carta/cpp/core/Data/Image/ControllerData.h | 7 +- .../skel/widgets/Colormap/ColorMapsWidget.js | 23 +++ .../class/skel/widgets/Colormap/Colormap.js | 2 + .../skel/widgets/Image/Stack/DragDropList.js | 134 ++++++++++++++++-- .../skel/widgets/Image/Stack/StackControls.js | 103 ++++++++++++-- 12 files changed, 549 insertions(+), 124 deletions(-) diff --git a/carta/cpp/core/Data/Colormap/ColorState.cpp b/carta/cpp/core/Data/Colormap/ColorState.cpp index 8d61111b..e3d0f21e 100644 --- a/carta/cpp/core/Data/Colormap/ColorState.cpp +++ b/carta/cpp/core/Data/Colormap/ColorState.cpp @@ -172,6 +172,11 @@ bool ColorState::_isInverted() const { return m_state.getValue( INVERT ); } +void ColorState::_replicateTo( ColorState* otherState ){ + if ( otherState != nullptr ){ + _replicateTo( otherState->m_state ); + } +} void ColorState::_replicateTo( Carta::State::StateInterface& otherState ){ QString colorMapName = m_state.getValue(COLOR_MAP_NAME ); @@ -241,6 +246,68 @@ bool ColorState::_setColorMix( const QString& key, double colorPercent, QString& return colorChanged; } +QString ColorState::_setColorMap( const QString& colorMapStr ){ + QString mapName = m_state.getValue(COLOR_MAP_NAME); + QString result; + if ( m_colors != nullptr ){ + if( m_colors->isMap( colorMapStr ) ){ + if ( colorMapStr != mapName ){ + m_state.setValue(COLOR_MAP_NAME, colorMapStr ); + m_state.flushState(); + emit colorStateChanged(); + } + } + else { + result = "Invalid ColorState: " + colorMapStr; + } + } + return result; +} + +QString ColorState::_setDataTransform( const QString& transformString ){ + QString result(""); + QString transformName = m_state.getValue(TRANSFORM_DATA); + if ( m_dataTransforms != nullptr ){ + QString actualTransform; + bool recognizedTransform = m_dataTransforms->isTransform( transformString, actualTransform ); + if( recognizedTransform ){ + if ( actualTransform != transformName ){ + m_state.setValue(TRANSFORM_DATA, actualTransform ); + m_state.flushState(); + emit colorStateChanged(); + } + } + else { + result = "Invalid data transform: " + transformString; + } + } + return result; +} + +void ColorState::_setErrorMargin(){ + int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS ); + m_errorMargin = 1.0/qPow(10,significantDigits); +} + + +QString ColorState::_setGamma( double gamma ){ + QString result; + double oldGamma = m_state.getValue( GAMMA ); + + if ( qAbs( gamma - oldGamma) > m_errorMargin ){ + int digits = m_state.getValue(SIGNIFICANT_DIGITS); + m_state.setValue(GAMMA, Util::roundToDigits(gamma, digits )); + emit colorStateChanged(); + } + return result; +} + +void ColorState::_setGlobal( bool global ){ + bool oldGlobal = m_state.getValue(GLOBAL); + if ( global != oldGlobal ){ + m_state.setValue(GLOBAL, global); + } +} void ColorState::_setInvert( bool invert ){ bool oldInvert = m_state.getValue(INVERT ); @@ -296,57 +363,6 @@ void ColorState::_setReverse( bool reverse ){ } -QString ColorState::_setColorMap( const QString& colorMapStr ){ - QString mapName = m_state.getValue(COLOR_MAP_NAME); - QString result; - if ( m_colors != nullptr ){ - if( m_colors->isMap( colorMapStr ) ){ - if ( colorMapStr != mapName ){ - m_state.setValue(COLOR_MAP_NAME, colorMapStr ); - m_state.flushState(); - emit colorStateChanged(); - } - } - else { - result = "Invalid ColorState: " + colorMapStr; - } - } - return result; -} - -QString ColorState::_setGamma( double gamma ){ - QString result; - double oldGamma = m_state.getValue( GAMMA ); - - if ( qAbs( gamma - oldGamma) > m_errorMargin ){ - int digits = m_state.getValue(SIGNIFICANT_DIGITS); - m_state.setValue(GAMMA, Util::roundToDigits(gamma, digits )); - emit colorStateChanged(); - } - return result; -} - -QString ColorState::_setDataTransform( const QString& transformString ){ - QString result(""); - QString transformName = m_state.getValue(TRANSFORM_DATA); - if ( m_dataTransforms != nullptr ){ - QString actualTransform; - bool recognizedTransform = m_dataTransforms->isTransform( transformString, actualTransform ); - if( recognizedTransform ){ - if ( actualTransform != transformName ){ - m_state.setValue(TRANSFORM_DATA, actualTransform ); - m_state.flushState(); - emit colorStateChanged(); - } - } - else { - result = "Invalid data transform: " + transformString; - } - } - return result; -} - - QString ColorState::_setSignificantDigits( int digits ){ QString result; if ( digits <= 0 ){ @@ -362,11 +378,6 @@ QString ColorState::_setSignificantDigits( int digits ){ return result; } -void ColorState::_setErrorMargin(){ - int significantDigits = m_state.getValue(SIGNIFICANT_DIGITS ); - m_errorMargin = 1.0/qPow(10,significantDigits); -} - ColorState::~ColorState(){ diff --git a/carta/cpp/core/Data/Colormap/ColorState.h b/carta/cpp/core/Data/Colormap/ColorState.h index 259c8089..f95204a6 100644 --- a/carta/cpp/core/Data/Colormap/ColorState.h +++ b/carta/cpp/core/Data/Colormap/ColorState.h @@ -93,11 +93,18 @@ class ColorState : public QObject, public Carta::State::CartaObject { /** * Copy the state of this ColorState into the other state. - * @param other - the StateInterface that should bee a copy of this one. + * @param other - the StateInterface that should be a copy of this one. */ void _replicateTo( Carta::State::StateInterface& other ); + /** + * Copy the state of this ColorState into the other state. + * @param other - the ColorState that should be a copy of this one. + */ + void _replicateTo( ColorState* cState ); + + /** * Set the name of the current color map. * @param colorMapName a unique identifier for the color map. @@ -143,6 +150,13 @@ class ColorState : public QObject, public Carta::State::CartaObject { */ QString _setGamma( double gamma ); + /** + * Set whether or not this is the global color state. + * @param global - true if it is the global color state; false, if it just applies + * to a single layer on the stack. + */ + void _setGlobal( bool global ); + /** * Invert the current colormap. * @param invert - true if the color map should be inverted; false otherwise.. diff --git a/carta/cpp/core/Data/Colormap/Colormap.cpp b/carta/cpp/core/Data/Colormap/Colormap.cpp index d0dbbded..36882a1e 100644 --- a/carta/cpp/core/Data/Colormap/Colormap.cpp +++ b/carta/cpp/core/Data/Colormap/Colormap.cpp @@ -63,7 +63,7 @@ QString Colormap::addLink( CartaObject* cartaObject ){ objAdded = m_linkImpl->addLink( target ); if ( objAdded ){ - target->setGlobalColor( m_stateColorGlobal ); + target->_setColorMapGlobal( m_stateColorGlobal ); connect( target, SIGNAL(colorChanged(Controller*)), this, SLOT(_setColorStates(Controller*))); } } @@ -357,6 +357,23 @@ void Colormap::_initializeCallbacks(){ return result; }); + addCommandCallback( "setGlobal", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {ColorState::GLOBAL}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString globalStr = dataValues[*keys.begin()]; + bool validBool = false; + bool global = Util::toBool( globalStr, &validBool ); + QString result; + if ( validBool ){ + setGlobal( global ); + } + else { + result = "Please specify true/false when setting whether or not the color map is global: "+params; + } + Util::commandPostProcess( result ); + return result; + }); } @@ -527,19 +544,28 @@ QString Colormap::setDataTransform( const QString& transformString){ void Colormap::_setColorStates( Controller* controller ){ - std::vector< std::shared_ptr > selectedColorStates = controller->getSelectedColorStates(); m_stateColors.clear(); int stateColorCount = selectedColorStates.size(); - - /** - * Note: could be complication if one is a global map and - * the others are not. - */ - for ( int i = 0; i < stateColorCount; i++ ){ m_stateColors.push_back( selectedColorStates[i] ); } + + //Update the state the client is listening to. + _colorStateChanged(); +} + +void Colormap::setGlobal( bool global ){ + //Notify all the controllers to replace their global color maps with + //individual ones or vice versa. + int linkCount = m_linkImpl->getLinkCount(); + for ( int i = 0; i < linkCount; i++ ){ + CartaObject* obj = m_linkImpl->getLink( i ); + Controller* controller = dynamic_cast(obj); + if ( controller != nullptr ){ + controller->_setColorMapUseGlobal( global ); + } + } } QString Colormap::setNanColor( int redValue, int greenValue, int blueValue){ diff --git a/carta/cpp/core/Data/Colormap/Colormap.h b/carta/cpp/core/Data/Colormap/Colormap.h index db0101b6..54132c27 100644 --- a/carta/cpp/core/Data/Colormap/Colormap.h +++ b/carta/cpp/core/Data/Colormap/Colormap.h @@ -123,6 +123,12 @@ class Colormap : public QObject, public Carta::State::CartaObject, public ILinka */ QString setGamma( double gamma ); + /** + * Set whether or not the color map is global. + * @param global - true if the colormap is global; false otherwise. + */ + void setGlobal( bool global ); + /** * Invert the current colormap. * @param invert - true if the color map should be inverted; false otherwise.. diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 06569758..3a7845b9 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -55,6 +55,7 @@ const QString Controller::POINTER_MOVE = "pointer-move"; const QString Controller::ZOOM = "zoom"; const QString Controller::REGIONS = "regions"; const QString Controller::PLUGIN_NAME = "CasaImageLoader"; +const QString Controller::STACK_SELECT_AUTO = "stackAutoSelect"; const QString Controller::CLASS_NAME = "Controller"; bool Controller::m_registered = @@ -133,9 +134,16 @@ bool Controller::addData(const QString& fileName) { connect( targetSource, SIGNAL(renderingDone(QImage)), this, SLOT(_renderingDone(QImage))); connect( targetSource, & ControllerData::saveImageResult, this, & Controller::saveImageResultCB ); m_datas.append(std::shared_ptr(targetSource)); + targetSource->_viewResize( m_viewSize ); - targetSource->_setGlobalColor( m_stateColor ); + targetSource->_setColorMapGlobal( m_stateColor ); + //If it is the only image, set it selected. + if ( m_datas.size() == 1 ){ + std::vector indices(1); + indices[0] = 0; + setLayersSelected( indices ); + } connect( targetSource, SIGNAL(colorStateChanged()), this, SLOT( _colorMapChanged() )); //Update the data selectors upper bound based on the data. @@ -713,7 +721,7 @@ void Controller::_initializeCallbacks(){ addCommandCallback( "setImageOrder", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { bool parseError = false; - std::vector vals = Util::string2VectorInt( params, &parseError ); + std::vector vals = Util::string2VectorInt( params, &parseError, ";" ); QString result; if ( !parseError ){ result = setImageOrder( vals ); @@ -888,7 +896,7 @@ void Controller::_initializeCallbacks(){ const QString & params, const QString & /*sessionId*/) -> QString { QString result; bool error = false; - std::vector vals = Util::string2VectorInt( params, &error ); + std::vector vals = Util::string2VectorInt( params, &error, ";" ); if ( error ){ result = "Please specify the layers to select as nonnegative integers"; } @@ -907,6 +915,24 @@ void Controller::_initializeCallbacks(){ Util::commandPostProcess( result ); return result; }); + + addCommandCallback( "setStackSelectAuto", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {STACK_SELECT_AUTO}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString autoModeStr = dataValues[STACK_SELECT_AUTO]; + bool validBool = false; + bool autoSelect = Util::toBool( autoModeStr, &validBool ); + QString result; + if ( validBool ){ + setStackSelectAuto( autoSelect ); + } + else { + result = "Please specify true/false when setting whether stack selection should be automatic: "+autoModeStr; + } + Util::commandPostProcess( result ); + return result; + }); } @@ -928,6 +954,7 @@ void Controller::_initializeState(){ //Set whether or not to auto clip //First the preference state. m_state.insertValue( AUTO_CLIP, true ); + m_state.insertValue( STACK_SELECT_AUTO, true ); m_state.insertValue( CLIP_VALUE_MIN, 0.025 ); m_state.insertValue( CLIP_VALUE_MAX, 0.975 ); m_state.flushState(); @@ -950,7 +977,9 @@ void Controller::_initializeState(){ m_stateMouse.flushState(); } - +bool Controller::isStackSelectAuto() const { + return m_state.getValue( STACK_SELECT_AUTO ); +} void Controller::_loadView( bool newClips ) { m_reloadFrameQueued = false; @@ -1194,7 +1223,6 @@ void Controller::saveState() { /*int regionCount = m_regions.size(); m_state.resizeArray( REGIONS, regionCount ); _saveRegions();*/ - m_stateData.flushState(); } @@ -1337,6 +1365,11 @@ void Controller::setFrameImage( int val) { int oldIndex = m_selectImage->getIndex(); if ( oldIndex != val ){ m_selectImage->setIndex(val); + if ( isStackSelectAuto() ){ + std::vector indices(1); + indices[0] = val; + _setLayersSelected( indices ); + } int dataIndex = _getDataIndex(); if ( 0 <= dataIndex ){ int selectCount = m_selects.size(); @@ -1362,10 +1395,26 @@ void Controller::setFrameImage( int val) { } -void Controller::setGlobalColor( std::shared_ptr colorState ){ +void Controller::_setColorMapGlobal( std::shared_ptr colorState ){ m_stateColor = colorState; } +void Controller::_setColorMapUseGlobal( bool global ) { + //Loop through the data and reset the color maps. + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isSelected() ){ + if ( global ){ + m_datas[i]->_setColorMapGlobal( m_stateColor ); + } + else { + m_datas[i]->_setColorMapGlobal( nullptr ); + } + } + } + emit colorChanged( this ); +} + QString Controller::setImageVisibility( int dataIndex, bool visible ){ QString result; int dataCount = m_datas.size(); @@ -1396,6 +1445,18 @@ QString Controller::setImageVisibility( int dataIndex, bool visible ){ } QString Controller::setLayersSelected( const std::vector indices ){ + QString result; + bool selectModeAuto = isStackSelectAuto(); + if ( !selectModeAuto ){ + result = _setLayersSelected( indices ); + } + else { + result = "Enable manual layer selection mode before setting layers."; + } + return result; +} + +QString Controller::_setLayersSelected( const std::vector indices ){ int dataCount = m_datas.size(); //Note: we could to a careful check of the indices here to make sure //the are all nonnegative, and not outside of the required bounds, but @@ -1406,29 +1467,32 @@ QString Controller::setLayersSelected( const std::vector indices ){ std::sort( sortedLayers.begin(), sortedLayers.end()); int selectIndex = 0; int indexCount = indices.size(); - int selectedIndex = dataCount; + int selectedIndex = indexCount; if ( indexCount > 0 ){ - selectedIndex = indices[0]; + selectedIndex = sortedLayers[0]; } std::vector oldSelects; + bool selectStateChanged = false; for ( int i = 0; i < dataCount; i++ ){ if ( m_datas[i]->_isSelected() ){ oldSelects.push_back( i ); } - if ( i < selectedIndex ){ - m_datas[i]->_setSelected( false ); + if ( i != selectedIndex ){ + bool stateChange = m_datas[i]->_setSelected( false ); + if ( stateChange ){ + selectStateChanged = true; + } } - else if ( i == selectedIndex ){ - m_datas[i]->_setSelected( true ); + else { + bool stateChange = m_datas[i]->_setSelected( true ); + if ( stateChange ){ + selectStateChanged = true; + } selectIndex++; if ( selectIndex < indexCount ){ - selectedIndex = indices[selectIndex]; + selectedIndex = sortedLayers[selectIndex]; } } - else { - result = "Invalid selection index: "+ QString::number( selectedIndex ); - break; - } } if ( selectIndex != indexCount){ result = "Invalid selection indices"; @@ -1437,13 +1501,20 @@ QString Controller::setLayersSelected( const std::vector indices ){ //Set the selections back the way they were setLayersSelected( oldSelects ); } - else { + else if ( selectStateChanged ){ + saveState(); emit colorChanged( this ); } return result; } - +void Controller::setStackSelectAuto( bool automatic ){ + bool oldStackSelectAuto = m_state.getValue(STACK_SELECT_AUTO ); + if ( oldStackSelectAuto != automatic ){ + m_state.setValue( STACK_SELECT_AUTO, automatic ); + m_state.flushState(); + } +} void Controller::setZoomLevel( double zoomFactor ){ int dataIndex = _getDataIndex(); diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index b17647fa..8b0bf657 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -48,6 +48,7 @@ class Controller: public QObject, public Carta::State::CartaObject, public IPercentIntensityMap { friend class Animator; + friend class Colormap; Q_OBJECT @@ -82,8 +83,6 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void centerOnPixel( double imgX , double imgY); - - /** * Close the given image. * @param name an identifier for the image to close. @@ -273,7 +272,13 @@ class Controller: public QObject, public Carta::State::CartaObject, */ virtual QString getStateString( const QString& sessionId, SnapshotType type ) const Q_DECL_OVERRIDE; - + /** + * Returns whether or not the image stack layers are selected based on the + * animator (auto) or whether the user has indicated a manual selection. + * @return - true if the stack layers are selected based on the current layer; false + * if the user has specified manual selection. + */ + bool isStackSelectAuto() const; /** * Center the image. @@ -349,12 +354,6 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString setClipValue( double clipValue ); - /** - * Set a new color map state. - * @param colorState - the new color map information. - */ - void setGlobalColor( std::shared_ptr colorState ); - /** * Specify a new image order. * @param imageIndices - a list specifying a new order for the images in @@ -377,6 +376,13 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString setLayersSelected( const std::vector indices ); + /** + * Set whether or not selection of layers in the stack should be based on the + * current layer or whether the user wants to make a manual selection. + * @param automatic - true for automatic selection; false for manual selection. + */ + void setStackSelectAuto( bool automatic ); + /** * Change the pan of the current image. * @param imgX the x-coordinate for the center of the pan. @@ -490,6 +496,10 @@ private slots: class Factory; + //Clear the color map. + void _clearColorMap(); + //Clear data sources + void _clearData(); //Clear image statistics. void _clearStatistics(); @@ -506,13 +516,28 @@ private slots: void _initializeCallbacks(); void _initializeSelections(); - void _clearData(); + QString _makeRegion( const QString& regionType ); + void _removeData( int index ); void _render(); + void _saveRegions(); void _scheduleFrameRepaint( const QImage& img ); + /** + * Set whether or not the selected layers should be using the global + * colormap. + * @param global - true if selected layers should use the global color map; + * false, otherwise. + */ + void _setColorMapUseGlobal( bool global ); + + /** + * Set the global color map.. + * @param colorState - the global color map information. + */ + void _setColorMapGlobal( std::shared_ptr colorState ); /** * Make a frame selection. @@ -520,6 +545,7 @@ private slots: * @param val a frame index for the axis. */ void _setFrameAxis(int frameIndex, Carta::Lib::AxisInfo::KnownType axisType ); + QString _setLayersSelected( const std::vector indices ); void _updateCursor( int mouseX, int mouseY ); @@ -538,6 +564,7 @@ private slots: static const QString REGIONS; static const QString CENTER; static const QString POINTER_MOVE; + static const QString STACK_SELECT_AUTO; static const QString ZOOM; //Data Selections diff --git a/carta/cpp/core/Data/Image/ControllerData.cpp b/carta/cpp/core/Data/Image/ControllerData.cpp index 9b0fc321..d3873e51 100755 --- a/carta/cpp/core/Data/Image/ControllerData.cpp +++ b/carta/cpp/core/Data/Image/ControllerData.cpp @@ -48,8 +48,8 @@ bool ControllerData::m_registered = ControllerData::ControllerData(const QString& path, const QString& id) : CartaObject( CLASS_NAME, path, id), + m_stateColor( nullptr ), m_dataSource( new DataSource() ), - m_drawSync( nullptr ){ Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); @@ -59,6 +59,13 @@ ControllerData::ControllerData(const QString& path, const QString& id) : _initializeState(); } +void ControllerData::_clearColorMap(){ + if ( m_stateColor ){ + disconnect( m_stateColor.get() ); + Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); + objMan->removeObject( m_stateColor->getId() ); + } +} void ControllerData::_clearData(){ Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); @@ -313,6 +320,7 @@ QString ControllerData::_getLayerString() const { Carta::State::StateInterface layerState( ""); layerState.insertValue( LAYER, shortNames[0] ); layerState.insertValue( Util::VISIBLE, m_state.getValue(Util::VISIBLE) ); + layerState.insertValue( SELECTED, m_state.getValue(SELECTED ) ); return layerState.toString(); } @@ -582,21 +590,44 @@ bool ControllerData::_setFileName( const QString& fileName ){ } -void ControllerData::_setGlobalColor( std::shared_ptr colorState ){ - if ( m_stateColor ){ - disconnect( m_stateColor.get() ); +void ControllerData::_setColorMapGlobal( std::shared_ptr colorState ){ + + //Decide if we are going to use our own separate map that is a copy of our current + //one or reset to a shared global color map based on whether the passed in map + //is null + bool colorReset = false; + if ( colorState ){ + if ( m_stateColor.get() == nullptr || !m_stateColor->_isGlobal() ){ + //Use a common global map + _clearColorMap(); + m_stateColor = colorState; + colorReset = true; + } + } + else { + //We are going to use our own color map + if ( m_stateColor->_isGlobal() ){ + if ( m_stateColor ){ + disconnect( m_stateColor.get() ); + } + Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); + ColorState* cObject = objMan->createObject(); + m_stateColor->_replicateTo( cObject ); + cObject->_setGlobal( false ); + m_stateColor.reset (cObject); + colorReset = true; + } + } + if ( colorReset ){ + _colorChanged( ); + connect( m_stateColor.get(), SIGNAL( colorStateChanged()), this, SLOT(_colorChanged())); } - m_stateColor = colorState; - _colorChanged( ); - connect( m_stateColor.get(), SIGNAL( colorStateChanged()), this, SLOT(_colorChanged())); } void ControllerData::_colorChanged(){ if ( m_dataSource ){ - QString mapName = m_stateColor->_getColorMap(); - qDebug() << "ControllerData color changed "<_setColorMap( mapName ); m_dataSource->_setTransformData( m_stateColor->_getDataTransform() ); m_dataSource->_setGamma( m_stateColor->_getGamma() ); @@ -608,7 +639,7 @@ void ControllerData::_colorChanged(){ m_dataSource->_setColorAmounts( redAmount, greenAmount, blueAmount ); bool defaultNan = m_stateColor->_isNanDefault(); m_dataSource->_setNanDefault( defaultNan ); - qDebug() << "Controller data defaultNan="<( SELECTED, selected ); +bool ControllerData::_setSelected( bool selected ){ + bool stateChanged = false; + bool oldSelected = m_state.getValue(SELECTED ); + if ( oldSelected != selected ){ + m_state.setValue( SELECTED, selected ); + stateChanged = true; + } + return stateChanged; } void ControllerData::_setVisible( bool visible ){ diff --git a/carta/cpp/core/Data/Image/ControllerData.h b/carta/cpp/core/Data/Image/ControllerData.h index 94639e1b..06fcdbc0 100755 --- a/carta/cpp/core/Data/Image/ControllerData.h +++ b/carta/cpp/core/Data/Image/ControllerData.h @@ -87,6 +87,7 @@ private slots: void _colorChanged(); private: + void _clearColorMap(); void _clearData(); Carta::Lib::AxisInfo::KnownType _getAxisXType() const; @@ -350,7 +351,8 @@ private slots: * Reset the color map information for this data. * @param colorState - stored information about the color map. */ - void _setGlobalColor( std::shared_ptr colorState ); + void _setColorMapGlobal( std::shared_ptr colorState ); + /** * Show/hide this layer. @@ -382,8 +384,9 @@ private slots: /** * Set this data source selected. * @param selected - true if the data source is selected; false otherwise. + * @return -true if the selected state changed; false otherwise. */ - void _setSelected( bool selected ); + bool _setSelected( bool selected ); /** * Resize the view of the image. diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js index 7cd99273..d80429f3 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js @@ -83,6 +83,19 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { },this); this._add( this.m_mapCombo ); + this.m_globalCheck = new qx.ui.form.CheckBox( "Global"); + this.m_globalCheck.addListener( skel.widgets.Path.CHANGE_VALUE, function(e){ + if ( this.m_id !== null ){ + var global = this.m_globalCheck.getValue(); + //Send a command to the server to let them know the map changed. + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setGlobal"; + var params = "global:"+global; + this.m_connector.sendCommand( cmd, params, function(){}); + } + },this); + this._add( this.m_globalCheck ); + this._add( new qx.ui.core.Spacer(), {flex:1}); this.m_intensityHighLabel = new qx.ui.basic.Label(""); this._add( this.m_intensityHighLabel ); @@ -168,10 +181,20 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { this._colormapDataCB(); }, + /** + * Server update as to whether this is a global colormap. + * @param globalMap {boolean} - true if this is a global color map; + * false otherwise. + */ + setGlobal : function( globalMap ){ + this.m_globalCheck.setValue( globalMap ); + }, + m_intensityLowLabel : null, m_intensityHighLabel : null, m_mapCombo : null, + m_globalCheck : null, m_id : null, m_connector : null, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js index 768c41e4..5d998fe5 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/Colormap.js @@ -108,6 +108,8 @@ qx.Class.define("skel.widgets.Colormap.Colormap", this.m_view.setScales( cMap.colorMix.red, cMap.colorMix.green, cMap.colorMix.blue ); } this.m_settings.setControls( cMap ); + this.m_mapControl.setMapName( cMap.colorMapName ); + this.m_mapControl.setGlobal( cMap.global ); } catch( err ){ diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js index ed469315..47a3ca08 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js @@ -15,12 +15,28 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { }, events : { - "listReordered" : "qx.event.type.Data" + "listReordered" : "qx.event.type.Data", + "listSelection" : "qx.event.type.Data" }, members : { /** + * Return a list of the indices of selected list items. + * @return {Array} - a list of indices of list items that have + * been selected. + */ + getSelectedIndices : function(){ + var indices = []; + var children = this.m_list.getChildren(); + for ( var i = 0; i < children.length; i++ ){ + if ( this.m_list.isSelected( children[i] ) ){ + indices.push( i ); + } + } + return indices; + }, + /* * Initializes the UI. */ _init : function( width ) { @@ -68,7 +84,10 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { this.m_list.setDraggable(true); this.m_list.setDroppable( true ); this.m_listContainer.add( this.m_list, {left:0,top:0}); - + this.m_list.addListener( "changeSelection", function(){ + var data = {}; + this.fireDataEvent( "listSelection", data ); + }, this ); //Support moves. this.m_list.addListener("dragstart", function(e) { e.addAction("move"); @@ -140,6 +159,82 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { this.m_dragMarker.setLayoutProperties( {left:0, top:origTop} ); } }, + + /** + * Return the index of the item in the list. + * @param item {qx.ui.core.Widget} - a list item. + * @return {Number} - the index of the item in the list. + */ + _getIndex : function( item ){ + var children = this.m_list.getChildren(); + var index = -1; + for ( var i = 0; i < children.length; i++ ){ + if ( children[i] == item ){ + index = i; + break; + } + } + return index; + }, + + /** + * Returns the permuted order of the list indices after an insertion at the + * end of the list. + * @param insertIndices {Array} - a list of the indices of list items that + * were inserted. + * @return {Array} - a list of permuted indices. + */ + _getListIndicesAfter : function( insertIndices ){ + //Go through the list and add any index that is not being + //inserter. + var indices = []; + var listCount = this.m_list.getChildren().length; + for ( var j = 0; j < listCount; j++ ){ + if ( insertIndices.indexOf( j) < 0 ){ + indices.push( j ); + } + } + //Now go through and add the insert indices + for ( j = 0; j < insertIndices.length; j++ ){ + indices.push( insertIndices[j] ); + } + return indices; + }, + + /** + * Returns the permuted order of the list indices after an insertion before an + * item in the list. + * @param insertIndices {Array} - a list of the indices of list items that + * were inserted. + * @param origIndex {Number} - the index of the list item just after where + * the insertion took place. + * @return {Array} - a list of permuted indices. + */ + _getListIndicesBefore : function( insertIndices, origIndex ){ + //First add in all the non-selected items, up to + //the origIndex. + var indices = []; + for ( var j = 0; j < origIndex; j++ ){ + var insertIndex = insertIndices.indexOf( j ); + if ( insertIndices.indexOf(j) < 0 ){ + indices.push( j ); + } + } + //Now add in all the selectedItems + for ( j = 0; j < insertIndices.length; j++ ){ + indices.push( insertIndices[j]); + } + //Finally add in from the original index to the end, + //leaving off the insert indices. + var listCount = this.m_list.getChildren().length; + for ( j = origIndex; j < listCount; j++ ){ + var insertIndex = insertIndices.indexOf( j ); + if ( insertIndex < 0 ){ + indices.push( j ); + } + } + return indices; + }, /** * Reorder the list. @@ -149,7 +244,9 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { _reorderList : function( orig ){ // Only continue if the target is a list item. var listItem = false; + var origIndex = -1; if (orig instanceof qx.ui.form.ListItem) { + origIndex = this._getIndex( orig ); listItem = true; } @@ -160,30 +257,41 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { if ( !listItem ){ var children = this.m_list.getChildren(); if ( children.length > 0 ){ - lastChild = children[children.length - 1]; + origIndex = children.length - 1; + lastChild = children[ origIndex ]; } } + + var insertIndices = []; for (var i = 0; i < sel.length; i++) { if ( listItem ){ //At a list item. + var beforeIndex = this._getIndex( sel[i] ); + insertIndices.push( beforeIndex ); this.m_list.addBefore(sel[i], orig); } else { //Add at the end of the list + var afterIndex = this._getIndex( sel[i] ); + insertIndices.push( afterIndex ); this.m_list.addAfter( sel[i], lastChild ); } } - var names = []; - var children = this.m_list.getChildren(); - for ( var j = 0; j < children.length; j++ ){ - names[j] = children[j].getLabel(); + var indices = []; + if ( listItem ){ + indices = this._getListIndicesBefore( insertIndices, origIndex ); + } + else { + indices = this._getListIndicesAfter( insertIndices ); } + var data = { - "listItems" : names + "listItems" : indices }; this.fireDataEvent( "listReordered", data ); }, + /** * Update the items in the list. @@ -192,10 +300,14 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { setListItems : function( items ){ this.m_list.removeAll(); var dataCount = items.length; + var selectedItems = []; for ( var i = 0; i < dataCount; i++ ){ var listItem = new qx.ui.form.ListItem( items[i].layer ); this.m_list.add( listItem ); var visible = items[i].visible; + if ( items[i].selected ){ + selectedItems.push( i ); + } var contextMenu = new qx.ui.menu.Menu(); //Close button @@ -226,6 +338,12 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { } listItem.setContextMenu(contextMenu); } + var children = this.m_list.getChildren(); + var selectedChildren = []; + for ( var i = 0; i < selectedItems.length; i++ ){ + selectedChildren.push( children[selectedItems[i]] ); + } + this.m_list.setSelection( selectedChildren ); }, m_list : null, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js index 32c665ff..9555926f 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js @@ -19,6 +19,15 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { }, members : { + + /** + * User has changed the auto-select mode of the list. + */ + _autoSelectChanged : function(){ + var auto = this.m_autoSelectCheck.getValue(); + this.m_imageList.setEnabled( !auto ); + this._sendStackAutoSelectCmd(); + }, /** * Callback for a change in stack settings. @@ -28,7 +37,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { if ( val ){ try { var controls = JSON.parse( val ); - this.m_imageList.setListItems( controls.data ); + this.m_autoSelectCheck.setValue( controls.stackAutoSelect ); var errorMan = skel.widgets.ErrorHandler.getInstance(); errorMan.clearErrors(); @@ -38,28 +47,72 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { } } }, + + /** + * Callback for a change in stack data settings. + */ + _controlsDataChangedCB : function(){ + var val = this.m_sharedVarData.get(); + if ( val ){ + try { + var controls = JSON.parse( val ); + this.m_imageList.setListItems( controls.data ); + + var errorMan = skel.widgets.ErrorHandler.getInstance(); + errorMan.clearErrors(); + } + catch( err ){ + console.log( "Stack controls could not parse data: "+val+" error: "+err ); + } + } + }, /** * Initializes the UI. */ _init : function( ) { this.setPadding( 0, 0, 0, 0 ); - this._setLayout( new qx.ui.layout.HBox(1)); + this._setLayout( new qx.ui.layout.VBox(1)); + + //Auto select check + var selectContainer = new qx.ui.container.Composite(); + selectContainer.setLayout( new qx.ui.layout.HBox(1) ); + selectContainer.add( new qx.ui.core.Spacer(), {flex:1}); + this.m_autoSelectCheck = new qx.ui.form.CheckBox( "Auto Select"); + this.m_autoSelectCheck.setToolTipText( "Auto selection based on animator or manual selection of layer(s)."); + this.m_autoSelectCheck.addListener( "changeValue", this._autoSelectChanged, this ); + selectContainer.add( this.m_autoSelectCheck ); + selectContainer.add( new qx.ui.core.Spacer(), {flex:1}); + + //List this.m_imageList = new skel.widgets.Image.Stack.DragDropList( 300 ); this.m_imageList.addListener( "listReordered", this._sendReorderCmd, this ); + this.m_imageList.addListener( "listSelection", this._sendSelectionCmd, this ); + + //Add to main container. + this._add( selectContainer ); this._add( this.m_imageList ); }, - + /** * Register to get updates on stack settings from the server. */ _registerControls : function(){ - var path = skel.widgets.Path.getInstance(); - var dataPath = this.m_id + path.SEP + "data"; - this.m_sharedVar = this.m_connector.getSharedVar( dataPath ); + this.m_sharedVar = this.m_connector.getSharedVar( this.m_id ); this.m_sharedVar.addCB(this._controlsChangedCB.bind(this)); this._controlsChangedCB(); }, + + /** + * Register to get updates on stack data settings from the server. + */ + _registerControlsData : function(){ + var path = skel.widgets.Path.getInstance(); + var dataPath = this.m_id + path.SEP + "data"; + this.m_sharedVarData = this.m_connector.getSharedVar( dataPath ); + this.m_sharedVarData.addCB(this._controlsDataChangedCB.bind(this)); + this._controlsDataChangedCB(); + }, /** * Send a command to the server to reorder the images in the stack. @@ -67,9 +120,40 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { */ _sendReorderCmd : function( msg ){ var imageList = msg.getData().listItems; - var params = "images:"+imageList.join(";"); + if ( imageList.length > 0 ){ + var params = imageList.join(";"); + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setImageOrder"; + this.m_connector.sendCommand( cmd, params, function(){}); + } + }, + + /** + * Send a command to the server to select particular images in the stack. + */ + _sendSelectionCmd : function(){ + //Only send list selections if the user is manually doing a + //selection. + if ( ! this.m_autoSelectCheck.getValue() ){ + var indices = this.m_imageList.getSelectedIndices(); + if ( indices.length > 0 ){ + var params = indices.join(";"); + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setLayersSelected"; + this.m_connector.sendCommand( cmd, params, function(){}); + } + } + }, + + /** + * Send a command to the server to reorder the images in the stack. + * @param msg {Array} - a list specifying the new image order. + */ + _sendStackAutoSelectCmd : function( msg ){ + var autoSelect = this.m_autoSelectCheck.getValue(); + var params = "stackAutoSelect:"+autoSelect; var path = skel.widgets.Path.getInstance(); - var cmd = this.m_id + path.SEP_COMMAND + "setImageOrder"; + var cmd = this.m_id + path.SEP_COMMAND + "setStackSelectAuto"; this.m_connector.sendCommand( cmd, params, function(){}); }, @@ -80,11 +164,14 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { setId : function( imageId ){ this.m_id = imageId; this._registerControls(); + this._registerControlsData(); }, m_id : null, m_connector : null, m_sharedVar : null, + m_sharedVarData : null, + m_autoSelectCheck : null, m_imageList : null } }); \ No newline at end of file From 979aac666e647a3f2d50172ec7c3c600cb1dfcbb Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Thu, 5 Nov 2015 15:00:54 -0700 Subject: [PATCH 22/37] added FPS & quality control to VGView on javascript side better error reporting from javascript... basically exceptions thrown in JS while inside code that was connected to C++ signals were being ignored, so I wrapped all these inside try/catch constructs, and at least print out console.error() messages. --- carta/cpp/core/Hacks/HackViewer.cpp | 5 +- carta/html5/common/CallbackList.js | 34 ++- carta/html5/common/IConnector.js | 19 +- carta/html5/common/libs.js | 4 +- .../class/skel/boundWidgets/View/View.js | 54 +++- .../skel/source/class/skel/hacks/Hacks.js | 23 +- .../class/skel/hacks/LayeredViewHack.js | 4 +- .../skel/source/class/skel/hacks/VGView.js | 285 ++++++++++++++++++ carta/html5/desktop/desktopConnector.js | 109 ++++--- carta/html5/server/serverConnector.js | 36 ++- 10 files changed, 501 insertions(+), 72 deletions(-) create mode 100644 carta/html5/common/skel/source/class/skel/hacks/VGView.js diff --git a/carta/cpp/core/Hacks/HackViewer.cpp b/carta/cpp/core/Hacks/HackViewer.cpp index e29f0391..d1a33685 100644 --- a/carta/cpp/core/Hacks/HackViewer.cpp +++ b/carta/cpp/core/Hacks/HackViewer.cpp @@ -206,8 +206,8 @@ HackViewer::start() vgview-> scheduleRepaint(); } ); - timer->setInterval( 100 ); -// timer->start(); + timer->setInterval( 200 ); + timer->start(); // layered view 2 stuff auto ccl = [] ( int x, int y, int r, QColor color ) -> QImage { @@ -246,6 +246,7 @@ HackViewer::start() // [args] // 0 load c1 // 0 load v1 + // 0 load /scratch/Images/tree.jpg // 0 alpha 0.5 // 0 mask 0xff0000 0.5 auto cmdCB = diff --git a/carta/html5/common/CallbackList.js b/carta/html5/common/CallbackList.js index 814043e7..b8492e79 100644 --- a/carta/html5/common/CallbackList.js +++ b/carta/html5/common/CallbackList.js @@ -8,7 +8,7 @@ * CallbackID add( callback) * bool remove( CallbackID) * void callEveryone() - * destory + * destroy * * What is special about this data structure? The fact that all of the methods that * modify the list (add, remove, destructor) can be called within callEveryone(), which @@ -37,13 +37,13 @@ return idCounter ++; } - var CallbackList = function CallbackList() + /*var CallbackList = */function CallbackList() { this.m_insideLoop = false; this.m_pendingCleanup = false; this.m_cbList = {}; this.m_destroyed = false; - }; + } /** @@ -105,21 +105,47 @@ /** * Calls every callback with the given parameters */ + CallbackList.prototype.callEveryone = function callEveryone( /* args ... */) { + console.log( "callEveryone enter", this.m_insideLoop); + + var res = this.callEveryoneW.apply( this, arguments); + if( this.m_insideLoop) { + console.error( "big problem"); + throw "BIG PROBLEM"; + debugger; + } + console.log( "callEveryone exit", this.m_insideLoop); + return res; + }; + + CallbackList.prototype.callEveryoneW = function callEveryone( /* args ... */) + { + console.log( "callEveryoneW", this.m_insideLoop); + if( this.m_insideLoop) { + console.warn( "insideLoop"); + //throw "fit"; + //debugger; + return; + } assert( ! this.m_insideLoop, "You are calling callEveryone recursively"); // indicate we are inside the callback loop this.m_insideLoop = true; + console.log( "callEveryone2"); // iterate through all callbacks for (var key in this.m_cbList) { + console.log( "key", key); if (! this.m_cbList.hasOwnProperty(key)) { + console.log( "early exit"); continue; } var info = this.m_cbList[key]; // only call active callbacks + console.log( "info.status", info.status, info); if( info.status === "active") { info.cb.apply( this, arguments); } @@ -156,6 +182,8 @@ // we are not inside the loop anymore this.m_insideLoop = false; + + console.log( "reset insideLoop", this.m_insideLoop ); }; /** diff --git a/carta/html5/common/IConnector.js b/carta/html5/common/IConnector.js index 2128fad1..3fd71bac 100644 --- a/carta/html5/common/IConnector.js +++ b/carta/html5/common/IConnector.js @@ -6,6 +6,9 @@ * * The design of the connector is limited by the functionality offered by PureWeb, since * we are using PureWeb to implements the client/server communication. + * + * Note: this is not actual code. It is not used anywhere. It is just a place to document + * the APIs of the real connectors: desktop & server at the moment. */ var IConnector; @@ -189,15 +192,29 @@ IConnector.registerViewElement = function( divElement, viewName) {}; */ var IView = {}; +/** + * returns bool whether the view supports quality control (i.e. true for server, false + * for desktop). + */ +IView.supportsQuality = function() { return false; }; + /** * Set the quality of the view. * Setting to 100 will attempt loss-less compression, all other levels are lossy. * For example quality=100 could be PNG. * - * @param quality {int} 0 = lowest, 100 = highest quality + * This has no effect for desktop connector. There the quality is always 100%. + * + * @param quality {int} 0 = lowest, 100 = highest quality jpeg, 101 = png, 102 = mpeg? */ IView.setQuality = function( quality) {}; +/** + * Returns the current quality (integer 0..102). + * + */ +IView.getQuality = function() {}; + /** * This needs to be called when the container of the view was resized. */ diff --git a/carta/html5/common/libs.js b/carta/html5/common/libs.js index 0c13b57e..cd07426b 100644 --- a/carta/html5/common/libs.js +++ b/carta/html5/common/libs.js @@ -114,7 +114,9 @@ function assert(condition, message) { if (!condition) { - throw message || "Assertion failed"; + console.error( "Assertion failed:", message); + console.trace(); + throw (message || "Assertion failed"); } } diff --git a/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js b/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js index 8d2f36f1..73377428 100644 --- a/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js +++ b/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js @@ -20,6 +20,12 @@ qx.Class.define( "skel.boundWidgets.View.View", { extend: qx.ui.core.Widget, + events: + { + // fired when the view is updated + "viewRefreshed" : "qx.event.type.Data", + }, + /** * @param viewName {String} the name of the view. */ @@ -33,14 +39,7 @@ qx.Class.define( "skel.boundWidgets.View.View", { var setZeroTimeout = mImport( "setZeroTimeout" ); - var appearListenerId = this.addListener( "appear", function( e ) - { - this.m_iview = this.m_connector.registerViewElement( this - .getContentElement().getDomElement(), this.m_viewName ); - this.removeListenerById( appearListenerId ); - - this.m_iview.updateSize(); - }, this ); + this.addListenerOnce( "appear", this._appearCB.bind(this)); this.addListener( "resize", function( /*e*/ ) { @@ -63,6 +62,41 @@ qx.Class.define( "skel.boundWidgets.View.View", { members: { + // set quality of the view + setQuality : function( quality) { + this.m_quality = quality; + if( this.m_iview) { + this.m_iview.setQuality( this.m_quality); + } + }, + + // return the underlying iview + getIView : function() { + return this.m_iview; + }, + + // return the name of the view + viewName : function() { + return this.m_viewName; + }, + + // callback for appear event + _appearCB: function() + { + this.m_iview = this.m_connector.registerViewElement( + this.getContentElement().getDomElement(), this.m_viewName ); + + this.m_iview.updateSize(); + this.m_iview.addViewCallback( this._iviewRefreshCB.bind( this ) ); + this.setQuality( this.m_quality); + }, + + // callback for iView refresh + _iviewRefreshCB : function() { + console.log( "firing viewRefreshed", this.m_viewName); + this.fireDataEvent( "viewRefreshed"); + }, + // overridden _createContentElement: function() { @@ -99,7 +133,9 @@ qx.Class.define( "skel.boundWidgets.View.View", { /** * @type {Connector} cached instance of the connector */ - m_connector: null + m_connector: null, + + m_quality: 90 }, destruct: function() diff --git a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js index cd956c65..30ee731c 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js +++ b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js @@ -61,7 +61,8 @@ qx.Class.define("skel.hacks.Hacks", { win2.setUseResizeFrame( false); win2.setContentPadding( 5, 5, 5, 5 ); win2.setLayout( new qx.ui.layout.VBox(5) ); - win2.add( new skel.hacks.HackView( newViewName), { flex: 1 }); + var newView = new skel.hacks.HackView( newViewName); + win2.add( newView, { flex: 1 }); // add mini movie player var mp = {}; mp.prefix = "/hacks/views/" + newViewName; @@ -81,9 +82,29 @@ qx.Class.define("skel.hacks.Hacks", { mp.container.add( mp.delayTF); mp.container.add( new skel.boundWidgets.Label( "Frame:", "", mp.prefix + "/frame")); //mp.container.add( (new qx.ui.core.Spacer()).set({ allowStretchX: true })); + mp.fps = new qx.ui.basic.Label( "FPS"); + mp.container.add( mp.fps); mp.container.add( new qx.ui.core.Spacer(), { flex: 1 }); mp.gridTB = new skel.boundWidgets.Toggle( "Grid", mp.prefix + "/gridToggle"); mp.container.add( mp.gridTB); + var intervalId = setInterval( function() { + if( ! newView.viewWidget().getIView()) { + console.log( "trying later for view", newView.viewWidget().getName()); + } + clearInterval( intervalId); + mp.fps.setValue( "OK fps"); + var accum_fps = 0; + var lastRefresh = window.performance.now(); + //newView.viewWidget().getIView().addViewCallback( function() { + newView.viewWidget().addListener( "viewRefreshed", function() { + console.log( "in viewcallback of IVC7"); + var currRefresh = window.performance.now(); + var fps = 1000 / (currRefresh - lastRefresh); + lastRefresh = currRefresh; + accum_fps = 0.9 * accum_fps + 0.1 * fps; + mp.fps.setValue(""+ accum_fps + "(" + fps + ")"); + }); + }, 500); win2.add( mp.container); this.m_app.getRoot().add( win2, {left: 100, top: 100} ); win2.open(); diff --git a/carta/html5/common/skel/source/class/skel/hacks/LayeredViewHack.js b/carta/html5/common/skel/source/class/skel/hacks/LayeredViewHack.js index a045c2c7..0b2834dc 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/LayeredViewHack.js +++ b/carta/html5/common/skel/source/class/skel/hacks/LayeredViewHack.js @@ -14,7 +14,9 @@ qx.Class.define( "skel.hacks.LayeredViewHack", { this.m_connector = mImport( "connector"); this.setLayout( new qx.ui.layout.VBox( 3)); - this.m_vgview = new skel.boundWidgets.View.View( this.m_viewName); + //this.m_vgview = new skel.boundWidgets.View.View( this.m_viewName); + this.m_vgview = new skel.hacks.VGView( this.m_viewName); + this.add( this.m_vgview, { flex: 1 }); this.m_status = new qx.ui.basic.Label( "status..."); diff --git a/carta/html5/common/skel/source/class/skel/hacks/VGView.js b/carta/html5/common/skel/source/class/skel/hacks/VGView.js new file mode 100644 index 00000000..445cf4de --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/hacks/VGView.js @@ -0,0 +1,285 @@ +/** + * Created by pfederl on 04/01/15. + * + * Widget capable of displaying raster images overlayed with vector graphics. + */ + +qx.Class.define( "skel.hacks.VGView", { + + extend: qx.ui.container.Composite, + + events: + { + "viewRefreshed" : "qx.event.type.Data" + }, + + /** + * Constructor + */ + construct: function( viewName ) + { + this.base( arguments ); + this.setLayout( new qx.ui.layout.Canvas() ); + // create a basic view widget as the bottom layer + this.m_viewWidget = new skel.boundWidgets.View.View( viewName ); + this.add( this.m_viewWidget, { edge: 0 } ); + // create an input overlay widget (transparent, used only to capture + // input events) + this.m_overlayWidget = new qx.ui.core.Widget(); + this.add( this.m_overlayWidget, { edge: 0 } ); + + this.m_overlayWidget.addListener( "click", function() { + console.log( "Click"); + }); + + // create a settings UI layer + this.m_settingsLayer = this._createSettingsUI(); + this.add( this.m_settingsLayer, { top: 10, right: 10 } ); + + this.m_overlayWidget.addListener( "keydown", function( e ) + { + console.log( "keydown", e ); + console.log( "keyid", e.getKeyIdentifier(), e.getModifiers() ); + if( e.isCtrlPressed() && e.isShiftPressed() && e.getKeyIdentifier() === 'S') { + if( this.m_settingsLayer.getVisibility() == "visible") { + this.m_settingsLayer.setVisibility( "excluded"); + } else { + this.m_settingsLayer.setVisibility( "visible"); + } + } + }.bind(this) ); + this.m_overlayWidget.addListener( "keypress", function() + { + console.log( "keypress", arguments ); + } ); + + // once the View widget is up, we can finally + //this.m_viewWidget.addListener( "appear", function(){ + // this.setQuality( this.m_qualityValue); + //}.bind(this)); + this.setQuality( this.m_qualityValue); + + // listen for the raster view refreshes + this.m_viewWidget.addListener( "viewRefreshed", this._rasterViewRefreshCB.bind(this)); + }, + + members: { + + m_overlayWidget: null, + m_viewWidget : null, + m_settingsLayer: null, + m_qualitySlider: null, + m_fpsLabel: null, + m_qualityLabel: null, + m_qualityValue: 90, + m_fpsAvg: 0, + m_lastRefresh: null, + + /** + * Get the overlay widget + */ + overlayWidget: function() + { + return this.m_overlayWidget; + }, + + /** + * Get the view widget + */ + viewWidget: function() + { + return this.m_viewWidget; + }, + + _rasterViewRefreshCB: function() + { + + // update FPS + var currRefresh = window.performance.now(); + if( this.m_lastRefresh === null) { + this.m_lastRefresh = currRefresh - 1000; + } + var fps = 1000 / (currRefresh - this.m_lastRefresh); + this.m_lastRefresh = currRefresh; + this.m_fpsAvg = 0.9 * this.m_fpsAvg + 0.1 * fps; + + this.m_fpsLabel.setValue("FPS:"+ this.m_fpsAvg.toPrecision(3) + "(" + fps.toPrecision(3) + ")"); + + // also refire the event + this.fireDataEvent( "viewRefreshed"); + }, + + _createSettingsUI: function() + { + var settings = new qx.ui.container.Composite( new qx.ui.layout.VBox() ); + settings.set( { + opacity : 0.9, + backgroundColor: "rgba(255,255,255,0.7)", + padding : 5, + visibility : "excluded", + //keepActive: true, + //keepFocus: true, + minWidth: 200 + } ); + var fpsLabel = new qx.ui.basic.Label( "FPS:" ); + settings.add( fpsLabel ); + var qualityLabel = new qx.ui.basic.Label( "Quality:" ); + settings.add( qualityLabel ); + var qualitySlider = new qx.ui.form.Slider(); + qualitySlider.set({ + maximum: 102, + value: this.m_qualityValue + }); + settings.add( qualitySlider ); + var closeButton = new qx.ui.form.Button( "Close" ); + closeButton.set( {alignX: "right", allowStretchX: false} ); + settings.add( closeButton ); + closeButton.addListener( "execute", function() + { + console.log( "closeButton clicked" ); + settings.exclude(); + } ); + + var dec = new qx.ui.decoration.Decorator(); + dec.set({ + color: "rgba(0,0,0,0.5)", + startColor: "rgba(255,255,255,0.5)", + endColor: "rgba(225,225,255,1)", + radius: [10, 0, 10, 10], + width: 2 + }); + settings.setDecorator( dec); + + this.m_qualityLabel = qualityLabel; + this.m_fpsLabel = fpsLabel; + this.m_qualitySlider = qualitySlider; + + // hook up quality slider callback + this.m_qualitySlider.addListener( "changeValue", this._qualitySliderCB.bind(this)); + + return settings; + }, + + setQuality: function( quality) { + this.m_viewWidget.setQuality( quality); + var qtext = "" + quality; + if( quality === 0) { + qtext += " (lowest JPG)"; + } + if( quality === 100) { + qtext += " (highest JPG)"; + } + if( quality === 101) { + qtext += " (PNG)"; + } + if( quality === 102) { + qtext += " (MPEG*)"; + } + this.m_qualityLabel.setValue( "Quality: " + qtext); + }, + + _qualitySliderCB: function(e) { + this.setQuality( e.getData()); + } + } + +} ); + +/* + var row1 = cqoo( { + type: "hbox", + id : "hb1", + lopt: { + flex: 1 + }, + lset: { + spacing: 5 + }, + set: { + backgroundColor: "#00ff00" + }, + add : [ + { + type : "label", + value: "FPS...:", + lopt : {flex: 0}, + set : {background: "#ff0000"} + }, + { + type: "label", + lopt: {flex: 1} + } + ] + } ); + var allRows = cqoo( { + type: "vbox", + id : "vb1", + add : [row1] + } ); + + +function cqoo_hbox( obj) { + return new qx.ui.container.Composite( new qx.ui.layout.HBox()); +} + +function cqoo( obj ) +{ + + if( obj.$_widget !== undefined ) { + return obj; + } + + var res = { + $_widget: null, + $_lopt : obj.lopt || {}, + $_id : null + }; + + // obj must have type + var type = obj.type; + if( ! type ) { + throw "No type"; + } + + // construct widget + if( type == "hbox" ) { + res.$_widget = cqoo_hbox( obj ); + } + else if( type == "vbox" ) { + res.$_widget = cqoo_vbox( obj ); + } + else if( type == "label" ) { + res.$_widget = cqoo_label( obj ); + } + else { + throw "Bad type: " + type; + } + + // if we have layout options, apply them + if( obj.lset != null && res.$_widget.getLayout && res.$_widget.getLayout()) { + res.$_widget.getLayout().set( obj.lset); + } + + // apply set + if( obj.set != null) { + res.$_widget.set( obj.set); + } + + // do we have kids? + if( obj.add != null ) { + for( kidObj in obj.add ) { + var kid = cqoo( kidObj ); + res.$_widget.add( kid.$_widget, kid.$_lopt ); + if( kid.$_id != null) { + res[kid.$_id] = kid; + } + } + } + + // do we have id? + var id = obj.id; + if( id != null ) { + res[id] = widget; + } +} +*/ diff --git a/carta/html5/desktop/desktopConnector.js b/carta/html5/desktop/desktopConnector.js index 7a204a07..3c82df7a 100644 --- a/carta/html5/desktop/desktopConnector.js +++ b/carta/html5/desktop/desktopConnector.js @@ -16,6 +16,7 @@ var setZeroTimeout = mImport( "setZeroTimeout" ); var console = mImport( "console" ); var defer = mImport( "defer" ); + var assert = mImport( "assert" ); var CallbackList = mImport( "CallbackList"); /** @@ -57,48 +58,47 @@ // listen for command results callbacks and always invoke the top callback // in the list // the command results always arrive in the same order they were sent - QtConnector.jsCommandResultsSignal.connect(function(result) { - //console.log( "DesktopConnector callback result="+result); - if (m_commandCallbacks.length < 1) { - console - .warn("Received command results but no callbacks for this!!!"); - console.warn("The result: ", result); - return; - } - /*** - * Note: Code below was changed because callbacks do not come back in - * the same order they were called, but in stack order. Code is single-threaded - * for the desktop version. - * Example: Cmd ->setPlugin - * Does a state change on the server. On the Javascript - * side we have a state listener. This state listener triggers: - * Cmd ->registerView (returns objectId) - * Cmd ->registerView (returns objectId) - * Finally the command setPlugin returns from the server. - */ - var cb = m_commandCallbacks.shift(); - //var cb = m_commandCallbacks.pop(); - if (cb == null) { - console.log( "Desktop skipping cb was null"); - // skip this callback - return; - } - if (typeof cb !== "function") { - console.warn("Registered callback for command is not a function!"); - } else { + QtConnector.jsCommandResultsSignal.connect( function( result ) + { + try { + if( m_commandCallbacks.length < 1 ) { + console.warn( "Received command results but no callbacks for this!!!" ); + console.warn( "The result: ", result ); + return; + } + var cb = m_commandCallbacks.shift(); + if( cb == null ) { + return; + } + if( typeof cb !== "function" ) { + console.warn( "Registered callback for command is not a function!" ); + return; + } cb( result ); } + catch( error ) { + window.console.error( "Caught error in command callback ", error ); + window.console.trace(); + } }); // listen for jsViewUpdatedSignal to render the image - QtConnector.jsViewUpdatedSignal.connect(function(viewName, buffer, refreshId) { - var view = m_views[viewName]; - if (view == null) { - console.warn("Ignoring update for unconnected view '" + viewName + "'"); - return; - } - buffer.assignToHTMLImageElement(view.m_imgTag); - QtConnector.jsViewRefreshedSlot( view.getName(), refreshId); + QtConnector.jsViewUpdatedSignal.connect( function(viewName, buffer, refreshId) + { + try { + var view = m_views[viewName]; + if( view == null ) { + console.warn( "Ignoring update for unconnected view '" + viewName + "'" ); + return; + } + buffer.assignToHTMLImageElement( view.m_imgTag ); + QtConnector.jsViewRefreshedSlot( view.getName(), refreshId ); + view._callViewCallbacks(); + } + catch( error ) { + window.console.error( "Caught error in view updated callback ", error ); + window.console.trace(); + } }); // convenience function to create & get or just get a state @@ -147,6 +147,7 @@ this.m_mouseMoveTimeoutHandle = null; this.m_mousePos = { x : 0, y: 0 }; this.m_mousePosSlotScheduled = false; + this.m_viewCallbacks = new CallbackList(); }; /** @@ -196,8 +197,15 @@ this.m_mousePos.y); }; + View.prototype.supportsQuality = function() { + return false; + }; View.prototype.setQuality = function setQuality() { - // desktop does not have quality + // desktop only supports quality 101 + }; + View.prototype.getQuality = function setQuality() { + // desktop only supports quality 101 + return 101; }; View.prototype.updateSize = function() { // this.m_imgTag.width = this.m_container.offsetWidth; @@ -223,6 +231,10 @@ return coordinate; }; View.prototype.addViewCallback = function(callback) { + return this.m_viewCallbacks.add( callback); + }; + View.prototype._callViewCallbacks = function () { + this.m_viewCallbacks.callEveryone(); }; connector.registerViewElement = function( divElement, viewName ) @@ -263,16 +275,18 @@ } // listen for changes to the state - QtConnector.stateChangedSignal.connect(function(key, val) { - //console.log("stateUpdate", key, val); - var st = getOrCreateState(key); - // save the value - st.value = val; - // now go through all callbacks and call them + QtConnector.stateChangedSignal.connect(function(key, val) + { try { - st.callbacks.callEveryone( st.value ); - } catch ( err) { - window.console.error( "Caught error ", err); + var st = getOrCreateState( key ); + // save the value + st.value = val; + // now go through all callbacks and call them + st.callbacks.callEveryone( st.value ); + } + catch( error ) { + window.console.error( "Caught error in state callback ", error ); + window.console.trace(); } }); @@ -281,7 +295,6 @@ if (m_connectionCB != null) { setZeroTimeout(m_connectionCB); - // m_connectionCB(); } }; diff --git a/carta/html5/server/serverConnector.js b/carta/html5/server/serverConnector.js index 9dc60caa..444d712e 100644 --- a/carta/html5/server/serverConnector.js +++ b/carta/html5/server/serverConnector.js @@ -20,6 +20,7 @@ var setZeroTimeout = mImport( "setZeroTimeout" ); var console = mImport( "console" ); var defer = mImport( "defer" ); + var CallbackList = mImport( "CallbackList"); /** * Numerical constants representing status of the connection. @@ -77,34 +78,53 @@ function( e ) { var params = e.args.getEncodingParameters(); - console.log( "View '" + this.m_pwview.getViewName() + "' updated, params=", params ); + // tell the server side we updated view, and send back also the attached refreshId pureweb.getClient().queueCommand( "viewrefreshed", - { viewName: viewName, id: params.refreshId }, null, this ); + { viewName: viewName, id: params.refreshId }, function() {} ); + // also call any user registered callbacks + this._callViewCallbacks(); + }, false, this ); + // no user callbacks + this.m_viewCallbacks = new CallbackList(); pureweb.listen( this.m_pwview, pureweb.client.View.EventType.TRANSFORMS_CHANGED, function( ) { console.log( "View '" + this.m_pwview.getViewName() + "' txupdate"); }, false, this ); }; + View.prototype.supportsQuality = function() { + return true; + }; View.prototype.setQuality = function setQuality( quality ) { + if( quality < 1) { + quality = 1; + } + this.m_quality = quality; var params = {}; // support for safari... (browsers that don't support binary format?) if( ! m_client.supportsBinary() ) { params = {'UseBase64': true}; } var ef; - if( quality < 100 ) { + if( quality <= 100 ) { ef = new pureweb.client.EncoderFormat( pureweb.SupportedEncoderMimeType.JPEG, quality, params ); } - else { + else if( quality == 101) { ef = new pureweb.client.EncoderFormat( pureweb.SupportedEncoderMimeType.PNG, null, params ); } + else { + console.error( "server connector does not support mpeg quality yet"); + ef = new pureweb.client.EncoderFormat( pureweb.SupportedEncoderMimeType.JPEG, 100, params ); + } var ec = new pureweb.client.EncoderConfiguration( ef, ef ); this.m_pwview.setEncoderConfiguration( ec ); this.m_pwview.refresh(); }; + View.prototype.getQuality = function() { + return this.m_quality; + }; View.prototype.updateSize = function( ) { this.m_pwview.resize(); @@ -127,6 +147,11 @@ }; View.prototype.addViewCallback = function( callback ) { + console.log( "Adding view callback to", this.getName()); + return this.m_viewCallbacks.add( callback); + }; + View.prototype._callViewCallbacks = function () { + this.m_viewCallbacks.callEveryone(); }; connector.registerViewElement = function( divElement, viewName) { @@ -446,14 +471,13 @@ connector.sendCommand = function( cmd, params, callback ) { -// m_client.queueCommand( cmd, param, callback ); m_client.queueCommand( "generic", { cmd: cmd, params: params}, function( caller, data) { var response = data.getResponse(); if ( response.textContent ){ response = response.textContent.replace(/(\r\n|\n|\r)/gm,""); response = response.trim(); } - callback( response ); + callback && callback( response ); } ); }; From 86cbb250b0faaccd1d1111eb283c5d527ee9d4c2 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Thu, 5 Nov 2015 22:31:24 -0700 Subject: [PATCH 23/37] removed some of the unnecessary debug output --- carta/cpp/desktop/CustomWebPage.h | 2 +- carta/html5/common/CallbackList.js | 32 +++---------------- carta/html5/common/libs.js | 12 +++++++ .../class/skel/boundWidgets/View/View.js | 1 - .../skel/source/class/skel/hacks/Hacks.js | 15 +++++++++ 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/carta/cpp/desktop/CustomWebPage.h b/carta/cpp/desktop/CustomWebPage.h index 7e116e6a..af80d574 100644 --- a/carta/cpp/desktop/CustomWebPage.h +++ b/carta/cpp/desktop/CustomWebPage.h @@ -14,7 +14,7 @@ class CustomWebPage : public QWebPage protected: - void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID); + void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) override; signals: diff --git a/carta/html5/common/CallbackList.js b/carta/html5/common/CallbackList.js index b8492e79..e587960f 100644 --- a/carta/html5/common/CallbackList.js +++ b/carta/html5/common/CallbackList.js @@ -87,7 +87,7 @@ // if the callback could not be found, log the problem if( iter === undefined) { - console.log( "invalid callback id to remove: ", id); + console.error( "invalid callback id to remove: ", id); return; } @@ -105,28 +105,10 @@ /** * Calls every callback with the given parameters */ - CallbackList.prototype.callEveryone = function callEveryone( /* args ... */) { - console.log( "callEveryone enter", this.m_insideLoop); - - var res = this.callEveryoneW.apply( this, arguments); - if( this.m_insideLoop) { - console.error( "big problem"); - throw "BIG PROBLEM"; - debugger; - } - console.log( "callEveryone exit", this.m_insideLoop); - return res; - }; - - CallbackList.prototype.callEveryoneW = function callEveryone( /* args ... */) - { - console.log( "callEveryoneW", this.m_insideLoop); if( this.m_insideLoop) { - console.warn( "insideLoop"); - //throw "fit"; - //debugger; + console.error( "recursive call of CallbackList.callEveryone()"); return; } @@ -134,18 +116,15 @@ // indicate we are inside the callback loop this.m_insideLoop = true; - console.log( "callEveryone2"); // iterate through all callbacks for (var key in this.m_cbList) { - console.log( "key", key); if (! this.m_cbList.hasOwnProperty(key)) { console.log( "early exit"); continue; } var info = this.m_cbList[key]; // only call active callbacks - console.log( "info.status", info.status, info); if( info.status === "active") { info.cb.apply( this, arguments); } @@ -162,8 +141,6 @@ // if we had insertions/deletions while inside one of the callbacks // clean up the deletions and update insertions to active status if( this.m_pendingCleanup) { - console.log( " cleaning up callback list"); - for (var key in this.m_cbList) { if (! this.m_cbList.hasOwnProperty(key)) { continue; @@ -182,8 +159,6 @@ // we are not inside the loop anymore this.m_insideLoop = false; - - console.log( "reset insideLoop", this.m_insideLoop ); }; /** @@ -206,6 +181,8 @@ })(); +/* + (function(){ "use strict"; @@ -232,6 +209,7 @@ })(); +*/ diff --git a/carta/html5/common/libs.js b/carta/html5/common/libs.js index cd07426b..b0b6d8a2 100644 --- a/carta/html5/common/libs.js +++ b/carta/html5/common/libs.js @@ -8,6 +8,10 @@ /** * Define mExport and mImport + * + * \todo mExport could be moved into mImport, i.e. in order to export something, you would + * need to first mExport = mImport( "mExport"); + * This way we would only pollute the global namespace with just one symbol... */ (function() { @@ -114,8 +118,16 @@ function assert(condition, message) { if (!condition) { + // try to dump as much info into the console as possible, in case + // exceptions are eaten up later by the caller (e.g. qtwebkit bridge + // just ignores exceptions thrown inside callbacks connected to signals) console.error( "Assertion failed:", message); + if( qx && qx.dev && qx.dev.StackTrace && qx.dev.StackTrace.getStackTrace) { + console.error("Trace:", qx.dev.StackTrace.getStackTrace()); + } + // also try console.trace() in case it works (it does not work in qt webkit) console.trace(); + // now try to throw an exception, but it might be ignored throw (message || "Assertion failed"); } } diff --git a/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js b/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js index 73377428..4818b95f 100644 --- a/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js +++ b/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js @@ -93,7 +93,6 @@ qx.Class.define( "skel.boundWidgets.View.View", { // callback for iView refresh _iviewRefreshCB : function() { - console.log( "firing viewRefreshed", this.m_viewName); this.fireDataEvent( "viewRefreshed"); }, diff --git a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js index 30ee731c..210a00f4 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js +++ b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js @@ -87,6 +87,20 @@ qx.Class.define("skel.hacks.Hacks", { mp.container.add( new qx.ui.core.Spacer(), { flex: 1 }); mp.gridTB = new skel.boundWidgets.Toggle( "Grid", mp.prefix + "/gridToggle"); mp.container.add( mp.gridTB); + + var accum_fps = 0; + var lastRefresh = window.performance.now(); + //newView.viewWidget().getIView().addViewCallback( function() { + newView.viewWidget().addListener( "viewRefreshed", function() { + console.log( "in viewcallback of IVC7"); + var currRefresh = window.performance.now(); + var fps = 1000 / (currRefresh - lastRefresh); + lastRefresh = currRefresh; + accum_fps = 0.9 * accum_fps + 0.1 * fps; + mp.fps.setValue(""+ accum_fps + "(" + fps + ")"); + }); + +/* var intervalId = setInterval( function() { if( ! newView.viewWidget().getIView()) { console.log( "trying later for view", newView.viewWidget().getName()); @@ -105,6 +119,7 @@ qx.Class.define("skel.hacks.Hacks", { mp.fps.setValue(""+ accum_fps + "(" + fps + ")"); }); }, 500); +*/ win2.add( mp.container); this.m_app.getRoot().add( win2, {left: 100, top: 100} ); win2.open(); From 0694dfa139daec7b488aa3cd77df48b137003c54 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Thu, 5 Nov 2015 22:48:15 -0700 Subject: [PATCH 24/37] more extra debug statements removed from javascript --- .../class/skel/boundWidgets/View/View.js | 4 +-- .../source/class/skel/hacks/BoundSlider.js | 1 - .../skel/source/class/skel/hacks/HackView.js | 3 ++- .../skel/source/class/skel/hacks/Hacks.js | 26 +++---------------- 4 files changed, 6 insertions(+), 28 deletions(-) diff --git a/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js b/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js index 4818b95f..a9af750a 100644 --- a/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js +++ b/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js @@ -23,7 +23,7 @@ qx.Class.define( "skel.boundWidgets.View.View", { events: { // fired when the view is updated - "viewRefreshed" : "qx.event.type.Data", + "viewRefreshed" : "qx.event.type.Data" }, /** @@ -56,8 +56,6 @@ qx.Class.define( "skel.boundWidgets.View.View", { }, - events: {}, - properties: {}, members: { diff --git a/carta/html5/common/skel/source/class/skel/hacks/BoundSlider.js b/carta/html5/common/skel/source/class/skel/hacks/BoundSlider.js index cc62e15b..153aabd8 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/BoundSlider.js +++ b/carta/html5/common/skel/source/class/skel/hacks/BoundSlider.js @@ -41,7 +41,6 @@ qx.Class.define( "skel.hacks.BoundSlider", { members: { _sharedVarCB: function( val) { - console.log( "BoundSlider::_sharedVarCB", val); if( this.m_ignoreSharedVarCB) return; var newValue = this.m_config.val2slider( Number(val)); this.m_ignoreChangeValueCB = true; diff --git a/carta/html5/common/skel/source/class/skel/hacks/HackView.js b/carta/html5/common/skel/source/class/skel/hacks/HackView.js index a757f8e6..e00b9315 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/HackView.js +++ b/carta/html5/common/skel/source/class/skel/hacks/HackView.js @@ -10,7 +10,8 @@ qx.Class.define( "skel.hacks.HackView", { - extend: skel.boundWidgets.View.ViewWithInputDiv, + //extend: skel.boundWidgets.View.ViewWithInputDiv, + extend: skel.hacks.VGView, /** * Constructor diff --git a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js index 210a00f4..421ae38a 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js +++ b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js @@ -82,43 +82,23 @@ qx.Class.define("skel.hacks.Hacks", { mp.container.add( mp.delayTF); mp.container.add( new skel.boundWidgets.Label( "Frame:", "", mp.prefix + "/frame")); //mp.container.add( (new qx.ui.core.Spacer()).set({ allowStretchX: true })); - mp.fps = new qx.ui.basic.Label( "FPS"); - mp.container.add( mp.fps); + //mp.fps = new qx.ui.basic.Label( "FPS"); + //mp.container.add( mp.fps); mp.container.add( new qx.ui.core.Spacer(), { flex: 1 }); mp.gridTB = new skel.boundWidgets.Toggle( "Grid", mp.prefix + "/gridToggle"); mp.container.add( mp.gridTB); +/* var accum_fps = 0; var lastRefresh = window.performance.now(); //newView.viewWidget().getIView().addViewCallback( function() { newView.viewWidget().addListener( "viewRefreshed", function() { - console.log( "in viewcallback of IVC7"); var currRefresh = window.performance.now(); var fps = 1000 / (currRefresh - lastRefresh); lastRefresh = currRefresh; accum_fps = 0.9 * accum_fps + 0.1 * fps; mp.fps.setValue(""+ accum_fps + "(" + fps + ")"); }); - -/* - var intervalId = setInterval( function() { - if( ! newView.viewWidget().getIView()) { - console.log( "trying later for view", newView.viewWidget().getName()); - } - clearInterval( intervalId); - mp.fps.setValue( "OK fps"); - var accum_fps = 0; - var lastRefresh = window.performance.now(); - //newView.viewWidget().getIView().addViewCallback( function() { - newView.viewWidget().addListener( "viewRefreshed", function() { - console.log( "in viewcallback of IVC7"); - var currRefresh = window.performance.now(); - var fps = 1000 / (currRefresh - lastRefresh); - lastRefresh = currRefresh; - accum_fps = 0.9 * accum_fps + 0.1 * fps; - mp.fps.setValue(""+ accum_fps + "(" + fps + ")"); - }); - }, 500); */ win2.add( mp.container); this.m_app.getRoot().add( win2, {left: 100, top: 100} ); From cbd20a7ce515682c57e312e2137f3439e8cd4f8d Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Fri, 6 Nov 2015 17:40:22 -0700 Subject: [PATCH 25/37] alt-ctrl-s settings in VGview: quality slider disabled on desktop --- carta/html5/common/IConnector.js | 11 ++++++----- .../common/skel/source/class/skel/hacks/Hacks.js | 3 --- .../common/skel/source/class/skel/hacks/VGView.js | 9 +++++++++ carta/html5/desktop/desktopConnector.js | 8 +++++--- carta/html5/server/serverConnector.js | 9 ++++++--- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/carta/html5/common/IConnector.js b/carta/html5/common/IConnector.js index 3fd71bac..f67ea86c 100644 --- a/carta/html5/common/IConnector.js +++ b/carta/html5/common/IConnector.js @@ -188,15 +188,16 @@ IConnector.sendCommand = function( cmd, params, callback) {}; IConnector.registerViewElement = function( divElement, viewName) {}; /** - * View interface. Instance returned by IConnector.registerViewElement + * returns true if the raster views support quality control (i.e. true for server, false + * for desktop). */ -var IView = {}; +IConnector.supportsRasterViewQuality = function() {}; + /** - * returns bool whether the view supports quality control (i.e. true for server, false - * for desktop). + * View interface. Instance returned by IConnector.registerViewElement */ -IView.supportsQuality = function() { return false; }; +var IView = {}; /** * Set the quality of the view. diff --git a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js index 421ae38a..efad336c 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js +++ b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js @@ -81,9 +81,6 @@ qx.Class.define("skel.hacks.Hacks", { mp.delayTF = new skel.boundWidgets.TextField( mp.prefix + "/delay"); mp.container.add( mp.delayTF); mp.container.add( new skel.boundWidgets.Label( "Frame:", "", mp.prefix + "/frame")); - //mp.container.add( (new qx.ui.core.Spacer()).set({ allowStretchX: true })); - //mp.fps = new qx.ui.basic.Label( "FPS"); - //mp.container.add( mp.fps); mp.container.add( new qx.ui.core.Spacer(), { flex: 1 }); mp.gridTB = new skel.boundWidgets.Toggle( "Grid", mp.prefix + "/gridToggle"); mp.container.add( mp.gridTB); diff --git a/carta/html5/common/skel/source/class/skel/hacks/VGView.js b/carta/html5/common/skel/source/class/skel/hacks/VGView.js index 445cf4de..2178364e 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/VGView.js +++ b/carta/html5/common/skel/source/class/skel/hacks/VGView.js @@ -19,6 +19,7 @@ qx.Class.define( "skel.hacks.VGView", { construct: function( viewName ) { this.base( arguments ); + this.m_connector = mImport( "connector" ); this.setLayout( new qx.ui.layout.Canvas() ); // create a basic view widget as the bottom layer this.m_viewWidget = new skel.boundWidgets.View.View( viewName ); @@ -53,6 +54,8 @@ qx.Class.define( "skel.hacks.VGView", { console.log( "keypress", arguments ); } ); + this.m_qualityValue = this.m_connector.supportsRasterViewQuality() ? 90 : 101; + // once the View widget is up, we can finally //this.m_viewWidget.addListener( "appear", function(){ // this.setQuality( this.m_qualityValue); @@ -74,6 +77,7 @@ qx.Class.define( "skel.hacks.VGView", { m_qualityValue: 90, m_fpsAvg: 0, m_lastRefresh: null, + m_connector: null, /** * Get the overlay widget @@ -157,6 +161,11 @@ qx.Class.define( "skel.hacks.VGView", { // hook up quality slider callback this.m_qualitySlider.addListener( "changeValue", this._qualitySliderCB.bind(this)); + // disable quality slider if view does not support it + if( ! this.m_connector.supportsRasterViewQuality()) { + this.m_qualitySlider.setEnabled( false); + } + return settings; }, diff --git a/carta/html5/desktop/desktopConnector.js b/carta/html5/desktop/desktopConnector.js index 3c82df7a..8fa21840 100644 --- a/carta/html5/desktop/desktopConnector.js +++ b/carta/html5/desktop/desktopConnector.js @@ -197,9 +197,6 @@ this.m_mousePos.y); }; - View.prototype.supportsQuality = function() { - return false; - }; View.prototype.setQuality = function setQuality() { // desktop only supports quality 101 }; @@ -237,6 +234,11 @@ this.m_viewCallbacks.callEveryone(); }; + connector.supportsRasterViewQuality = function() + { + return false; + }; + connector.registerViewElement = function( divElement, viewName ) { var view = m_views[ viewName]; diff --git a/carta/html5/server/serverConnector.js b/carta/html5/server/serverConnector.js index 444d712e..e87405b0 100644 --- a/carta/html5/server/serverConnector.js +++ b/carta/html5/server/serverConnector.js @@ -93,9 +93,7 @@ console.log( "View '" + this.m_pwview.getViewName() + "' txupdate"); }, false, this ); }; - View.prototype.supportsQuality = function() { - return true; - }; + View.prototype.setQuality = function setQuality( quality ) { if( quality < 1) { @@ -154,6 +152,11 @@ this.m_viewCallbacks.callEveryone(); }; + connector.supportsRasterViewQuality = function() + { + return true; + }; + connector.registerViewElement = function( divElement, viewName) { return new View( divElement, viewName); }; From 5c87a40b317f6c21a7abecb3c9222f9471eab591 Mon Sep 17 00:00:00 2001 From: slovelan Date: Tue, 10 Nov 2015 15:39:28 -0700 Subject: [PATCH 26/37] Testing - selenium test & bug fixing. --- carta/cpp/core/Data/Colormap/Colormap.cpp | 5 +- carta/cpp/core/Data/Histogram/Histogram.cpp | 13 -- carta/cpp/core/Data/Histogram/Histogram.h | 5 +- carta/cpp/core/Data/Image/Controller.cpp | 35 +++-- carta/cpp/core/Data/Image/ControllerData.cpp | 10 +- carta/cpp/core/Data/Layout/Layout.cpp | 6 +- carta/cpp/core/Data/ViewManager.cpp | 8 +- .../skel/Command/Data/CommandDataHide.js | 2 +- .../skel/Command/Data/CommandDataHideImage.js | 9 +- .../skel/Command/Data/CommandDataShow.js | 2 +- .../skel/Command/Data/CommandDataShowImage.js | 8 +- .../class/skel/boundWidgets/Animator.js | 1 + .../skel/source/class/skel/simulation/Util.py | 140 +++++++++++------ .../source/class/skel/simulation/suite.py | 2 + .../source/class/skel/simulation/tAnimator.py | 41 ++--- .../skel/simulation/tAnimatorSettings.py | 113 +------------ .../skel/simulation/tAnimatorTapeDeck.py | 140 ++--------------- .../source/class/skel/simulation/tAxis.py | 2 +- .../source/class/skel/simulation/tColorMap.py | 148 ++++++++++++++++++ .../source/class/skel/simulation/tContour.py | 4 +- .../source/class/skel/simulation/tStack.py | 71 +++++++++ .../source/class/skel/simulation/tView.py | 2 +- .../skel/widgets/Colormap/ColorMapsWidget.js | 46 +----- .../skel/widgets/Image/Stack/DragDropList.js | 5 +- .../skel/widgets/Image/Stack/StackControls.js | 1 + 25 files changed, 408 insertions(+), 411 deletions(-) create mode 100644 carta/html5/common/skel/source/class/skel/simulation/tColorMap.py create mode 100644 carta/html5/common/skel/source/class/skel/simulation/tStack.py diff --git a/carta/cpp/core/Data/Colormap/Colormap.cpp b/carta/cpp/core/Data/Colormap/Colormap.cpp index 36882a1e..52c3bc20 100644 --- a/carta/cpp/core/Data/Colormap/Colormap.cpp +++ b/carta/cpp/core/Data/Colormap/Colormap.cpp @@ -50,6 +50,7 @@ Colormap::Colormap( const QString& path, const QString& id): connect( colorStateObj, SIGNAL( colorStateChanged()), this, SLOT( _colorStateChanged())); m_stateColorGlobal->_initializeDefaultState( m_state ); m_stateColors.push_back( m_stateColorGlobal); + _colorStateChanged(); _initializeDefaultState(); _initializeCallbacks(); @@ -60,11 +61,11 @@ QString Colormap::addLink( CartaObject* cartaObject ){ bool objAdded = false; QString result; if ( target != nullptr ){ - objAdded = m_linkImpl->addLink( target ); if ( objAdded ){ target->_setColorMapGlobal( m_stateColorGlobal ); connect( target, SIGNAL(colorChanged(Controller*)), this, SLOT(_setColorStates(Controller*))); + _setColorStates( target ); } } else { @@ -191,8 +192,6 @@ QString Colormap::getStateString( const QString& sessionId, SnapshotType type ) void Colormap::_initializeDefaultState(){ - //Color state - m_state.flushState(); //Image dependent intensity bounds m_stateData.insertValue( INTENSITY_MIN, 0 ); diff --git a/carta/cpp/core/Data/Histogram/Histogram.cpp b/carta/cpp/core/Data/Histogram/Histogram.cpp index e4406b73..b57fe66b 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.cpp +++ b/carta/cpp/core/Data/Histogram/Histogram.cpp @@ -2014,19 +2014,6 @@ void Histogram::_updateSelection(int x){ } -/*void Histogram::updateColorMap( Colormap* map ){ - if ( map != nullptr ){ - Controller* controller = _getControllerSelected(); - if ( controller != nullptr ){ - //map->setColorProperties( controller ); - std::shared_ptr pipeline = controller->getPipeline(); - m_histogram->setPipeline( pipeline ); - } - } - _generateHistogram( false ); -}*/ - - void Histogram::_updateSize( const QSize& size ){ bool newSize = m_histogram->setSize( size.width(), size.height()); if ( newSize ){ diff --git a/carta/cpp/core/Data/Histogram/Histogram.h b/carta/cpp/core/Data/Histogram/Histogram.h index e444a5a9..dd6d0fae 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.h +++ b/carta/cpp/core/Data/Histogram/Histogram.h @@ -243,7 +243,7 @@ class Histogram : public QObject, public Carta::State::CartaObject, public ILink /** * Set the range in intensity units for the custom clip. * @param colorMin a lower bound for the custom clip in real units. - * @param colorMax an upper bound for the dustom clip in real units. + * @param colorMax an upper bound for the custom clip in real units. * @return an error message if there was a problem setting the custom clip range; * false otherwise. */ @@ -330,9 +330,6 @@ class Histogram : public QObject, public Carta::State::CartaObject, public ILink signals: void colorIntensityBoundsChanged( double minIntensity, double maxIntensity ); -public slots: - //void updateColorMap( Colormap* ); - protected: virtual QString getSnapType(CartaObject::SnapshotType snapType) const Q_DECL_OVERRIDE; diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 3a7845b9..22c6acae 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -128,6 +128,7 @@ bool Controller::addData(const QString& fileName) { std::shared_ptr contourPtr( contourObj ); //Controller Data is in charge of drawing the contours. targetSource->_setContours( contourPtr ); + //Contour controls is in charge of setting the UI for the contours. m_contourControls->_setDrawContours( contourPtr ); targetIndex = m_datas.size(); @@ -137,13 +138,8 @@ bool Controller::addData(const QString& fileName) { targetSource->_viewResize( m_viewSize ); + //Colormap targetSource->_setColorMapGlobal( m_stateColor ); - //If it is the only image, set it selected. - if ( m_datas.size() == 1 ){ - std::vector indices(1); - indices[0] = 0; - setLayersSelected( indices ); - } connect( targetSource, SIGNAL(colorStateChanged()), this, SLOT( _colorMapChanged() )); //Update the data selectors upper bound based on the data. @@ -160,6 +156,11 @@ bool Controller::addData(const QString& fileName) { m_selects[i]->setUpperBound( frameCount ); } m_selectImage->setIndex(targetIndex); + if ( isStackSelectAuto() ){ + std::vector selectedLayers(1); + selectedLayers[0] = targetIndex; + _setLayersSelected( selectedLayers ); + } saveState(); @@ -502,7 +503,15 @@ QString Controller::getImageName(int index) const{ std::shared_ptr Controller::getPipeline() const { std::shared_ptr pipeline(nullptr); + //Color map should be based on the selected image rather than the current image. int dataIndex = _getDataIndex(); + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isSelected() ){ + dataIndex = i; + break; + } + } if ( 0 <= dataIndex ){ pipeline = m_datas[dataIndex]->_getPipeline(); } @@ -1087,33 +1096,30 @@ void Controller::_renderingDone( QImage img){ QString Controller::setImageOrder( const std::vector& indices ){ QString result; bool imageReordered = false; - int selectedIndex = _getDataIndex(); int dataCount = m_datas.size(); int indexCount = indices.size(); + QList > reorderedList; if ( indexCount != dataCount ){ result = "Reorder image size must match the stack count: "+QString::number(dataCount); } else { - for ( int i = 0; i < indexCount; i++ ){ int targetIndex = indices[i]; - if ( targetIndex < 0 || targetIndex >= dataCount ){ result = "Reorder failed: unknown image index: "+targetIndex; break; } //Insert the image at the target index at position i. else { - std::shared_ptr item = m_datas.takeAt( targetIndex ); - m_datas.insert(i, item ); - - if ( targetIndex == selectedIndex || i == selectedIndex ){ + reorderedList.append( m_datas[targetIndex] ); + if ( targetIndex != i ){ imageReordered = true; } } } } if ( imageReordered ){ + m_datas = reorderedList; _render(); } return result; @@ -1403,7 +1409,8 @@ void Controller::_setColorMapUseGlobal( bool global ) { //Loop through the data and reset the color maps. int dataCount = m_datas.size(); for ( int i = 0; i < dataCount; i++ ){ - if ( m_datas[i]->_isSelected() ){ + bool selected = m_datas[i]->_isSelected(); + if ( selected ){ if ( global ){ m_datas[i]->_setColorMapGlobal( m_stateColor ); } diff --git a/carta/cpp/core/Data/Image/ControllerData.cpp b/carta/cpp/core/Data/Image/ControllerData.cpp index d3873e51..6aabea07 100755 --- a/carta/cpp/core/Data/Image/ControllerData.cpp +++ b/carta/cpp/core/Data/Image/ControllerData.cpp @@ -333,9 +333,7 @@ void ControllerData::_initializeState(){ m_state.insertObject(DataGrid::GRID, gridState ); } -/*bool ControllerData::_isGlobalColorMap() const { - return m_stateColor->_isGlobal(); -}*/ + bool ControllerData::_isSelected() const { return m_state.getValue( SELECTED ); @@ -606,13 +604,15 @@ void ControllerData::_setColorMapGlobal( std::shared_ptr colorState } else { //We are going to use our own color map - if ( m_stateColor->_isGlobal() ){ + if ( m_stateColor.get() == nullptr || m_stateColor->_isGlobal() ){ if ( m_stateColor ){ disconnect( m_stateColor.get() ); } Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); ColorState* cObject = objMan->createObject(); - m_stateColor->_replicateTo( cObject ); + if ( m_stateColor.get() != nullptr ){ + m_stateColor->_replicateTo( cObject ); + } cObject->_setGlobal( false ); m_stateColor.reset (cObject); colorReset = true; diff --git a/carta/cpp/core/Data/Layout/Layout.cpp b/carta/cpp/core/Data/Layout/Layout.cpp index 95ff1c92..81605105 100644 --- a/carta/cpp/core/Data/Layout/Layout.cpp +++ b/carta/cpp/core/Data/Layout/Layout.cpp @@ -380,6 +380,7 @@ void Layout::resetState( const Carta::State::StateInterface& savedState ){ QString Layout::_removeWindow( const QString& locationId ){ QString result; bool windowRemoved = false; + QStringList oldNames = getPluginList(); if ( m_layoutRoot != nullptr ){ LayoutNode* firstChild = m_layoutRoot->getChildFirst(); LayoutNode* secondChild = m_layoutRoot->getChildSecond(); @@ -399,7 +400,10 @@ QString Layout::_removeWindow( const QString& locationId ){ } if ( !windowRemoved ){ result = "There was a problem removing the window."; - qDebug() << "There was a problem removing the window at " + locationId; + } + else { + QStringList newNames = getPluginList(); + emit pluginListChanged( newNames, oldNames ); } return result; } diff --git a/carta/cpp/core/Data/ViewManager.cpp b/carta/cpp/core/Data/ViewManager.cpp index fef1f999..bbadb2ae 100644 --- a/carta/cpp/core/Data/ViewManager.cpp +++ b/carta/cpp/core/Data/ViewManager.cpp @@ -366,7 +366,7 @@ void ViewManager::_initCallbacks(){ addStateCallback( Layout::CLASS_NAME, [=] ( const QString& /*path*/, const QString& /*value*/ ) { _makeLayout(); QStringList pluginList = m_layout->getPluginList(); - this->setPlugins( pluginList ); + setPlugins( pluginList ); }); } @@ -694,7 +694,6 @@ void ViewManager::_pluginsChanged( const QStringList& names, const QStringList& for ( QString key : keys ){ _adjustSize( pluginMap[key], key, insertionIndices[key]); } - } void ViewManager::_refreshStateSingletons(){ @@ -912,10 +911,7 @@ bool ViewManager::setPlugins( const QStringList& names ){ if ( m_layout ){ QStringList oldNames = m_layout->getPluginList(); bool valid = m_layout->_setPlugin( names, true); - if ( !valid ){ - qDebug() << "Invalid plugins: "< 0 ){ for ( var i = 0; i < skel.Command.Command.m_activeWins.length; i++ ){ @@ -42,7 +43,7 @@ qx.Class.define("skel.Command.Data.CommandDataHideImage", { errMan.updateErrors( "Error hiding image."); } }, - + m_index : 0, m_params : "image:" } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js index 411d5bf4..09eda653 100644 --- a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js @@ -47,7 +47,7 @@ qx.Class.define("skel.Command.Data.CommandDataShow", { var closes = activeWins[i].getDatas(); for ( var j = 0; j < closes.length; j++ ){ if ( !closes[j].visible ){ - this.m_cmds[k] = new skel.Command.Data.CommandDataShowImage( closes[j].layer); + this.m_cmds[k] = new skel.Command.Data.CommandDataShowImage( closes[j].layer, j); k++; } } diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShowImage.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShowImage.js index b842a451..01125a0e 100644 --- a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShowImage.js +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShowImage.js @@ -13,12 +13,13 @@ qx.Class.define("skel.Command.Data.CommandDataShowImage", { * Constructor. * @param label {String} name of the image to show. */ - construct : function( label ) { + construct : function( label, index ) { var path = skel.widgets.Path.getInstance(); var cmd = path.SEP_COMMAND + path.SHOW_IMAGE; this.base( arguments, label, cmd); this.m_toolBarVisible = false; this.setEnabled( true ); + this.m_index = index; this.m_global = false; this.setToolTipText("Show the image " + this.getLabel() + "."); }, @@ -27,8 +28,8 @@ qx.Class.define("skel.Command.Data.CommandDataShowImage", { doAction : function( vals, undoCB ){ var path = skel.widgets.Path.getInstance(); - var label = this.getLabel(); - var params = this.m_params + label; + //var label = this.getLabel(); + var params = this.m_params + this.m_index; var errMan = skel.widgets.ErrorHandler.getInstance(); if ( skel.Command.Command.m_activeWins.length > 0 ){ for ( var i = 0; i < skel.Command.Command.m_activeWins.length; i++ ){ @@ -43,6 +44,7 @@ qx.Class.define("skel.Command.Data.CommandDataShowImage", { } }, + m_index : 0, m_params : "image:" } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/boundWidgets/Animator.js b/carta/html5/common/skel/source/class/skel/boundWidgets/Animator.js index 4e3a987b..d1b8d915 100644 --- a/carta/html5/common/skel/source/class/skel/boundWidgets/Animator.js +++ b/carta/html5/common/skel/source/class/skel/boundWidgets/Animator.js @@ -344,6 +344,7 @@ qx.Class.define("skel.boundWidgets.Animator", { var endLabel = new qx.ui.basic.Label("<= "); this.m_endLabel = new qx.ui.basic.Label(""); + skel.widgets.TestID.addTestId( this.m_endLabel, this.m_title+"AnimatorUpperBound"); var locationComposite = new qx.ui.container.Composite(); locationComposite.setLayout(new qx.ui.layout.HBox(5)); locationComposite.add(titleLabel); diff --git a/carta/html5/common/skel/source/class/skel/simulation/Util.py b/carta/html5/common/skel/source/class/skel/simulation/Util.py index 56540d3b..10625e3e 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/Util.py +++ b/carta/html5/common/skel/source/class/skel/simulation/Util.py @@ -28,6 +28,36 @@ def setUp(self, browser): self.driver.implicitly_wait(20) time.sleep(5) + +# Adds a window below the main image viewer. Return the window for future actions +def add_window(unittest, driver): + timeout = selectBrowser._getSleep() + + # Click the Window button + windowButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.toolbar.MenuButton']/div[text()='Window']/.."))) + ActionChains(driver).click(windowButton).perform() + + # Look for the add button in the submenu. + addButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div/div[text()='Add']/.."))) + ActionChains(driver).click( addButton ).send_keys(Keys.ARROW_RIGHT).send_keys(Keys.ENTER).perform() + + # Check that we now have a generic empty window in the display and that the window count has gone up by one. + emptyWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowGenericPlugin']"))) + ActionChains(driver).click( emptyWindow ).perform() + + return emptyWindow + + +# Change the animation window to an image window +def animation_to_image_window(unittest, driver): + # Make sure the animation window is enabled by clicking an element within the window + animWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowAnimation']"))) + ActionChains(driver).click( animWindow ).perform(); + # Change the plugin of the animation window to an image loader + viewMenuButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.toolbar.MenuButton']/div[text()='View']/.."))) + ActionChains(driver).click( viewMenuButton ).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() + + # Clear text box and change value of the text box # Selenium clear() method is sometimes unable to clear # default text, therefore a manual method has been implemented @@ -44,30 +74,32 @@ def _changeElementText(self, driver, elementText, inputValue): elementValue = elementText.get_attribute("value") return elementValue -# Change the animation window to an image window -def animation_to_image_window(unittest, driver): - # Make sure the animation window is enabled by clicking an element within the window - animWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowAnimation']"))) - ActionChains(driver).click( animWindow ).perform(); - # Change the plugin of the animation window to an image loader - viewMenuButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.toolbar.MenuButton']/div[text()='View']/.."))) - ActionChains(driver).click( viewMenuButton ).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() +# Click the tab with the given name +def clickTab( driver, tabName ): + locator = "//div[@qxclass='qx.ui.tabview.TabButton']/div[text()='{0}']/..".format( tabName ) + print "Locator=",locator + tab = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, locator ))) + driver.execute_script( "arguments[0].scrollIntoView(true);", tab) + ActionChains(driver).click( tab ).perform() # Return the number of windows present within the application window def get_window_count(unittest, driver): windowList = driver.find_elements_by_xpath("//div[@qxclass='qx.ui.window.Desktop']") windowCount = len( windowList ) return windowCount - + + # Determine whether the check box is checked def isChecked(unittest, checkBox): styleAtt = checkBox.get_attribute( "style"); - #print "Style", styleAtt + print "Style", styleAtt oldChecked = False if "checked.png" in styleAtt: oldChecked = True + print "Found checked.png" return oldChecked - + + #Set a custom layout with the given number of rows and columns def layout_custom(unittest, driver, rows, cols ): timeout = selectBrowser._getSleep() @@ -113,9 +145,30 @@ def layout_custom(unittest, driver, rows, cols ): closeButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[starts-with(@id,'customLayoutOK')]"))) ActionChains(driver).click(closeButton).perform() time.sleep(timeout) + + +# Link second image +def link_second_image(unittest, driver, imageWindow2): + timeout = selectBrowser._getSleep() + + # Get window size + windowSize = imageWindow2.size + width = windowSize['width'] / 2 + height = windowSize['height'] / 2 + + # Find the link canvas + canvas = driver.find_element_by_xpath( "//canvas[@qxclass='skel.widgets.Link.LinkCanvas']") + + # Link to the second image + ActionChains(driver).click( canvas ).move_by_offset( -width, height ).double_click().perform() + time.sleep( timeout ) + + # Exit links + ActionChains(driver).send_keys(Keys.ESCAPE).perform() + # Load an image in the main image window -def load_image(unittest, driver, imageName): +def load_image(unittest, driver, imageName, imageId = "pwUID0"): browser = selectBrowser._getBrowser() timeout = selectBrowser._getSleep() @@ -161,27 +214,10 @@ def load_image(unittest, driver, imageName): # Check that the window is displaying an image. viewElement = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.boundWidgets.View.View']"))) - imageElement = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.ID, "pwUID0"))) + imageElement = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.ID, imageId))) return imageWindow -# Adds a window below the main image viewer. Return the window for future actions -def add_window(unittest, driver): - timeout = selectBrowser._getSleep() - - # Click the Window button - windowButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.toolbar.MenuButton']/div[text()='Window']/.."))) - ActionChains(driver).click(windowButton).perform() - - # Look for the add button in the submenu. - addButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div/div[text()='Add']/.."))) - ActionChains(driver).click( addButton ).send_keys(Keys.ARROW_RIGHT).send_keys(Keys.ENTER).perform() - - # Check that we now have a generic empty window in the display and that the window count has gone up by one. - emptyWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowGenericPlugin']"))) - ActionChains(driver).click( emptyWindow ).perform() - - return emptyWindow # Add a window, convert the window to an image window. Use the new image window to load an image def load_image_different_window(unittest, driver, imageName): @@ -201,6 +237,7 @@ def load_image_different_window(unittest, driver, imageName): ).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ARROW_RIGHT).send_keys(Keys.ENTER).perform() return load_image_windowIndex( unittest, driver,imageName, 2 ) + def load_image_windowIndex( unittest,driver,imageName,windowIndex): # Ensure that there is a new image window windowId = "CasaImageLoader"+str(windowIndex) @@ -236,13 +273,21 @@ def load_image_windowIndex( unittest,driver,imageName,windowIndex): # Return the second image loader window for further linking tests return imageWindow + # Open image settings by clicking on image settings checkbox located on the menu bar -def openImageSettings(unittest, driver): - imageSettingsCheckbox = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.form.CheckBox']/div[text()='Image Settings']/following-sibling::div[@class='qx-checkbox']"))) +def openSettings(unittest, driver, name ): + settingsText = name + " Settings" + print "Settings text=",settingsText + searchPath ="//div[@qxclass='qx.ui.form.CheckBox']/div[text()='{0}']/following-sibling::div[@class='qx-checkbox']".format( settingsText ) + print "Search path=",searchPath + imageSettingsCheckbox = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, searchPath ))) settingsOpen = isChecked(unittest, imageSettingsCheckbox ) if not settingsOpen: ActionChains(driver).click( imageSettingsCheckbox ).perform() + + + # Remove link from main casa image loader def remove_main_link(unittest, driver, imageWindow): timeout = selectBrowser._getSleep() @@ -262,28 +307,25 @@ def remove_main_link(unittest, driver, imageWindow): # Exit links ActionChains(driver).send_keys(Keys.ESCAPE).perform() -# Link second image -def link_second_image(unittest, driver, imageWindow2): - timeout = selectBrowser._getSleep() - - # Get window size - windowSize = imageWindow2.size - width = windowSize['width'] / 2 - height = windowSize['height'] / 2 - - # Find the link canvas - canvas = driver.find_element_by_xpath( "//canvas[@qxclass='skel.widgets.Link.LinkCanvas']") - # Link to the second image - ActionChains(driver).click( canvas ).move_by_offset( -width, height ).double_click().perform() - time.sleep( timeout ) +# Set the checked status of the checkbox. +def setChecked(self, driver, checkBox, checked): + oldChecked = isChecked( self, checkBox ) + print "oldChecked=",oldChecked + if checked != oldChecked : + ActionChains(driver).click( checkBox ).perform() - # Exit links - ActionChains(driver).send_keys(Keys.ESCAPE).perform() - # Verify that the number of animators that are visible is equal to the expected count def verifyAnimationCount(unittest, parentWidget, expectedCount): animatorList = parentWidget.find_elements_by_xpath( ".//div[@qxclass='skel.boundWidgets.Animator']" ) animatorCount = len( animatorList ) print "Animator list count=", animatorCount unittest.assertEqual( animatorCount, expectedCount, "Animator count does not match expected count") + +# Verify that the number of animators that are visible is equal to the expected count +def verifyAnimatorUpperBound(unittest, driver, expectedCount, animatorName): + fullId = animatorName + "AnimatorUpperBound" + animatorLabel = WebDriverWait(driver, 10).until(EC.presence_of_element_located( ( By.ID, fullId ) ) ) + upperBound = animatorLabel.text + print "Animator upper bound=", upperBound + unittest.assertEqual( upperBound, str(expectedCount), "Animator upper bound was not correct") diff --git a/carta/html5/common/skel/source/class/skel/simulation/suite.py b/carta/html5/common/skel/source/class/skel/simulation/suite.py index 167ad992..0a3d368f 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/suite.py +++ b/carta/html5/common/skel/source/class/skel/simulation/suite.py @@ -31,6 +31,8 @@ def suite(): test_suite.addTest( unittest.makeSuite(tSnapshotLayout.tSnapshotLayout)) test_suite.addTest( unittest.makeSuite(tSnapshotPreferences.tSnapshotPreferences)) test_suite.addTest( unittest.makeSuite(tContour.tContour)) + test_suite.addTest( unittest.makeSuite(tStack.tStack)) + test_suite.addTest( unittest.makeSuite(tColorMap.tColorMap)) return test_suite mySuite = suite() diff --git a/carta/html5/common/skel/source/class/skel/simulation/tAnimator.py b/carta/html5/common/skel/source/class/skel/simulation/tAnimator.py index c741b37e..ffc09bfc 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tAnimator.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tAnimator.py @@ -72,41 +72,20 @@ def _animateForwardImage(self, driver): self.assertIsNotNone( forwardAnimateButton, "Could not find forward animation button") driver.execute_script( "arguments[0].scrollIntoView(true);", forwardAnimateButton) ActionChains(driver).click( forwardAnimateButton ).perform() - # Click the stop button on the tapedeck - def _stopAnimation(self, driver, animator): - stopButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, animator+"TapeDeckStopAnimation"))) - driver.execute_script( "arguments[0].scrollIntoView(true);", stopButton) - ActionChains(driver).click( stopButton ).perform() - - - - # Change the Channel Animator to an Image Animator - def channel_to_image_animator(self, driver): - timeout = selectBrowser._getSleep() - - animWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowAnimation']"))) - ActionChains(driver).context_click( animWindow ).perform() - ActionChains(driver).send_keys(Keys.ARROW_DOWN).send_keys( - ).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN - ).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN - ).send_keys(Keys.ARROW_RIGHT).send_keys(Keys.ENTER).perform() - time.sleep( timeout) - - - def hideImageAnimator(self, driver): - animWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowAnimation']"))) - ActionChains(driver).click( animWindow ).perform() - # Click on the animate button on the menu tool bar - animateToolBar = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.toolbar.MenuButton']/div[text()='Animate']"))) - ActionChains(driver).click( animateToolBar ).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() - + - # Open Settings + # Open Settings for a particular animation window def _openSettings(self, driver, name): - # Click Settings to reveal animation settings settingsId = name + "SettingsCheck" print "Settings id ", settingsId settingsXPath = "//div[@id='" + settingsId + "']/div[@qxclass='qx.ui.basic.Image']" settingsCheckBox = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, settingsXPath ))) driver.execute_script( "arguments[0].scrollIntoView(true);", settingsCheckBox) - ActionChains(driver).click( settingsCheckBox ).perform() \ No newline at end of file + ActionChains(driver).click( settingsCheckBox ).perform() + + + # Click the stop button on the tapedeck + def _stopAnimation(self, driver, animator): + stopButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, animator+"TapeDeckStopAnimation"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", stopButton) + ActionChains(driver).click( stopButton ).perform() \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/simulation/tAnimatorSettings.py b/carta/html5/common/skel/source/class/skel/simulation/tAnimatorSettings.py index dd39e959..1fc6f8a0 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tAnimatorSettings.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tAnimatorSettings.py @@ -73,20 +73,20 @@ def test_animatorAddRemove(self): self._click( driver, animateCheck ) time.sleep( timeout ) - # Verify that the animation window has no animators. - Util.verifyAnimationCount( self, animWindow, 0) + # Verify that the animation window has only a Stokes animator + Util.verifyAnimationCount( self, animWindow, 1) # Check the image animate button and verify that the image animator shows up self._click( driver, animateCheck ) imageAnimator = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.boundWidgets.Animator']/div/div/div[text()='Image']"))) time.sleep( timeout ) - Util.verifyAnimationCount( self, animWindow, 1) + Util.verifyAnimationCount( self, animWindow, 2) # Check the channel animator button and verify there are now two animators, one channel, one image. self._click( driver, channelCheck ) channelAnimator = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.boundWidgets.Animator']/div/div/div[text()='Channel']"))) time.sleep( timeout ) - Util.verifyAnimationCount( self, animWindow, 2 ) + Util.verifyAnimationCount( self, animWindow, 3 ) # Chrome gives an error trying to close the page; therefore, refresh the page before # closing the browser. This is required because otherwise memory is not freed. @@ -138,10 +138,6 @@ def test_animatorJump(self): Util.load_image( self, driver, "aH.fits") Util.load_image( self, driver, "aJ.fits") Util.load_image( self, driver, "Default") - - # Hide the image animator - # Click on the animate button on the menu tool bar - self.hideImageAnimator( driver ) # Record last channel value of the test image self._getLastValue( driver, "Channel" ) @@ -157,7 +153,6 @@ def test_animatorJump(self): # Open settings self._openSettings( driver, "Channel" ) - # In settings, click the Jump radio button. Scroll into view if button is not visible jumpButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ChannelJumpRadioButton"))) driver.execute_script( "arguments[0].scrollIntoView(true);", jumpButton) @@ -177,9 +172,6 @@ def test_animatorJump(self): currChannelValue = self._getCurrentValue( driver, "Channel" ) print "Current channel", currChannelValue self.assertEqual( int(firstChannelValue), int(currChannelValue), "Channel Animator did not jump to first channel value") - - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) # Open settings self._openSettings( driver, "Image" ) @@ -260,9 +252,6 @@ def test_channelAnimatorWrap(self): print "Current channel", currChannelValue self.assertGreater( int(currChannelValue), int(firstChannelValue), "Channel did not increase after animating first channel value") - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - # Open settings self._openSettings( driver, "Image" ) @@ -296,90 +285,7 @@ def test_channelAnimatorWrap(self): print "Current image", currImageValue self.assertGreater( int(currImageValue), int(firstImageValue), "Image value did not increase after animating first image") - # Test that the Animator reverse setting animates in the reverse direction after - # reaching the last channel value. - # Note: this test is dependent on the animator speed and may fail because of changes - # in speed. - def test_channelAnimatorReverse(self): - driver = self.driver - timeout = selectBrowser._getSleep() - - # Open a test image so we have something to animate - Util.load_image( self, driver, "aH.fits") - Util.load_image( self, driver, "aJ.fits") - Util.load_image( self, driver, "Orion.cont.image.fits") - Util.load_image( self, driver, "Default") - - # Open settings - self._openSettings( driver, "Channel" ) - - # Go to last channel value and record the last channel value of the test image - self._getLastValue( driver, "Channel" ) - lastChannelValue = self._getCurrentValue( driver, "Channel" ) - - print "Testing Channel Animator Reverse Setting..." - print "Last channel value:", lastChannelValue - - # In settings, click the Reverse radio button. Scroll into view if button is not visible - reverseButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ChannelReverseRadioButton"))) - driver.execute_script( "arguments[0].scrollIntoView(true);", reverseButton) - ActionChains(driver).click( reverseButton ).perform() - time.sleep(2) - - # Click the forward animate button - # Allow the image to animate for 4 seconds (takes 4 seconds to reverse direction) - self._animateForward( driver, "Channel" ) - time.sleep(4) - - # Check that the current channel value is less than the last channel value - currChannelValue = self._getCurrentValue( driver, "Channel" ) - print "Current channel", currChannelValue - self.assertGreater( int(lastChannelValue), int(currChannelValue), "Channel Animator did not reverse direction after animating last channel value") - - # Stop animation. - self._stopAnimation( driver, "Channel") - - # Go to first channel value and record the first channel value of the test image - self._getFirstValue( driver, "Channel" ) - firstChannelValue = self._getCurrentValue( driver, "Channel" ) - print "First channel value:", firstChannelValue - - # Click the forward animate button - # Allow image to animate for 2 seconds - self._animateForward( driver, "Channel") - time.sleep(2) - self._stopAnimation( driver, "Channel") - - # Check that the channel value is at a higher value than the first channel value - currChannelValue = self._getCurrentValue( driver, "Channel" ) - print "Current channel", currChannelValue - self.assertGreater( int(currChannelValue), int(firstChannelValue), "Channel Animator did not increase channel after animating first channel value") - - # Open settings - self._openSettings( driver, "Image" ) - - # Go to the last image and record the last image value - self._getLastValue( driver, "Image" ) - lastImageValue = self._getCurrentValue( driver, "Image" ) - - print "Testing Image Animator Reverse Setting..." - print "Last image value:", lastImageValue - - # In settings, click the Reverse radio button. Scroll into view if button is not visible - reverseButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ImageTapeDeckReversePlay"))) - driver.execute_script( "arguments[0].scrollIntoView(true);", reverseButton) - ActionChains(driver).click( reverseButton ).perform() - - # Click the forward animate button - # Allow the image to animate - self._animateForward( driver, "Image" ) - time.sleep(.1) - self._stopAnimation( driver, "Image") - - # Check that the current image value is less than the last image value - currImageValue = self._getCurrentValue( driver, "Image" ) - print "Current image", currImageValue - self.assertGreater( int(lastImageValue), int(currImageValue), "Image Animator did not reverse direction after animating the last image") + # Test that adjustment of Animator rate will speed up/slow down channel animation # Under default settings, it takes roughly 2 seconds for the channel to change by 1 @@ -552,9 +458,6 @@ def test_channelAnimatorStepIncrement(self): print "Current channel", currChannelValue self.assertEqual( int(currChannelValue), 2, "Channel Animator did not increase by a step increment of 2") - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - # Open settings self._openSettings( driver, "Image") @@ -605,9 +508,6 @@ def test_animatorIncreaseFrame(self): print "oldChannelValue= 0 newChannelValue=", currChannelValue self.assertEqual( int(currChannelValue), int(firstChannelValue)+1, "Failed to increment Channel Animator") - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - # Record the first image value self._getFirstValue( driver, "Image" ) firstImageValue = self._getCurrentValue( driver, "Image" ) @@ -647,9 +547,6 @@ def test_animatorDecreaseFrame(self): print "oldChannelValue=", lastChannelValue, "newChannelValue=",currChannelValue self.assertEqual( int(currChannelValue), int(lastChannelValue)-1, "Failed to decrement the Channel Animator") - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - # Record the first image value self._getLastValue( driver, "Image" ) lastImageValue = self._getCurrentValue( driver, "Image" ) diff --git a/carta/html5/common/skel/source/class/skel/simulation/tAnimatorTapeDeck.py b/carta/html5/common/skel/source/class/skel/simulation/tAnimatorTapeDeck.py index 402d689b..52f0f066 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tAnimatorTapeDeck.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tAnimatorTapeDeck.py @@ -17,105 +17,8 @@ def setUp(self): browser = selectBrowser._getBrowser() Util.setUp(self, browser) - # Test that the Animator can animate in the forward direction - # Under default settings, it takes roughly 2 seconds for the channel to change by 1 - def test_channelAnimatorForwardAnimation(self): - driver = self.driver - - # Open a test images - # Note: The test will fail unless the last image loaded has more than one channel - Util.load_image( self, driver, "aH.fits") - Util.load_image( self, driver, "aJ.fits") - Util.load_image( self, driver, "Default") - - # Record the first channel value of the test image - self._getFirstValue( driver, "Channel" ) - - print "Testing Channel Animator Forward Animation..." - print "First channel value: 0" - - # Click the forward animate button - # Allow the image to animate for 2 seconds - self._animateForward( driver, "Channel" ) - time.sleep(2) - - # Check that the channel value is greater than the first channel value - currChannelValue = self._getCurrentValue( driver, "Channel" ) - print "Current channel", currChannelValue - self.assertGreater( int(currChannelValue), 0, "Channel value did not increase for forward animation.") - self._stopAnimation( driver, "Channel") - - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - - # Go to the first image and record the first image value - self._getFirstValue( driver, "Image" ) - - print "Testing Image Animator Forward Animation..." - print "First image value: 0" - - # Click the forward animate button - # Allow animation for 2 seconds - self._animateForward( driver, "Image" ) - time.sleep(2) - - # Check that the image value is greater than the first image value - currImageValue = self._getCurrentValue( driver, "Image" ) - print "Current image", currImageValue - self.assertGreater( int(currImageValue), 0, "Image value did not increase for forward animation") - - # Test that the Animator can animate in the reverse direction - # Under default settings, it takes roughly 2 seconds for the channel to change by 1 - def test_channelAnimatorReverseAnimation(self): - driver = self.driver - - # Open a test image so we have something to animate - Util.load_image( self, driver, "aH.fits") - Util.load_image( self, driver, "aJ.fits") - Util.load_image( self, driver, "Default") - - # Record the last channel value of the test image - self._getLastValue( driver, "Channel" ) - lastChannelValue = self._getCurrentValue( driver, "Channel" ) - - print "Testing Channel Animator Reverse Animation..." - print "Last channel value:", lastChannelValue - - # Click the reverse animate button. Scroll into view if not visible - # Allow image to animate for 2 seconds - reverseAnimateButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ChannelTapeDeckReversePlay"))) - driver.execute_script( "arguments[0].scrollIntoView(true);", reverseAnimateButton) - ActionChains(driver).click( reverseAnimateButton ).perform() - time.sleep(2) - - # Check that the channel value is at a value less than the last channel value - currChannelValue = self._getCurrentValue( driver, "Channel" ) - print "Current channel", currChannelValue - self.assertLess( int(currChannelValue), int(lastChannelValue), "Channel value did not decrease for reverse animation.") - self._stopAnimation( driver, "Channel") - - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - - # Go to the last image and record the last image value - self._getLastValue( driver, "Image" ) - lastImageValue = self._getCurrentValue( driver, "Image" ) - - print "Testing Image Animator Reverse Animation..." - print "Last image value:", lastImageValue - - # Click the reverse animate button. Scroll into view if not visible - # Allow image to animate for 2 seconds - reverseAnimateButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ImageTapeDeckReversePlay"))) - driver.execute_script( "arguments[0].scrollIntoView(true);", reverseAnimateButton) - ActionChains(driver).click( reverseAnimateButton ).perform() - time.sleep(2) - - # Check that the image value is at a value less than the last image value - currImageValue = self._getCurrentValue( driver, "Image" ) - print "Current image", currImageValue - self.assertLess( int(currImageValue), int(lastImageValue), "Image value did not decrease for reverse animation") + # Test that the Channel Animator can stop the animation def test_channelAnimatorStopAnimation(self): driver = self.driver @@ -128,30 +31,25 @@ def test_channelAnimatorStopAnimation(self): # Allow the image to animate for 2 seconds self._animateForward( driver, "Channel" ) time.sleep(2) - + # Click on the Stop button. Scroll into view if not visible - stopButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ChannelTapeDeckStopAnimation"))) - driver.execute_script( "arguments[0].scrollIntoView(true);", stopButton) - ActionChains(driver).click( stopButton ).perform() + self._stopAnimation( driver, "Channel") channelValue = self._getCurrentValue( driver, "Channel" ) - + print "Channel value ", channelValue # Wait for another 2 seconds. Ensure the channel value did not change time.sleep(2) currChannelValue = self._getCurrentValue( driver, "Channel" ) self.assertEqual( int(currChannelValue), int(channelValue), "Channel animation did not stop" ) - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - + # Allow animation for 2 seconds self._animateForward( driver, "Image" ) - time.sleep(2) + time.sleep(1) # Click on the Stop button. Scroll into view if not visible - stopButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ImageTapeDeckStopAnimation"))) - driver.execute_script( "arguments[0].scrollIntoView(true);", stopButton) - ActionChains(driver).click( stopButton ).perform() + self._stopAnimation( driver, "Image") imageValue = self._getCurrentValue( driver, "Image" ) + print "Image value ",imageValue # Wait for another 2 seconds. Ensure that the image value did not change time.sleep(2) @@ -176,13 +74,11 @@ def test_channelAnimatorFirstValue(self): # Check that the channel value is the same as the first channel value self._animateForward( driver, "Channel" ) time.sleep(2) + self._stopAnimation( driver, "Channel") self._getFirstValue( driver, "Channel" ) currChannelValue = self._getCurrentValue( driver, "Channel" ) self.assertEqual( int(currChannelValue), int(firstChannelValue), "Channel Animator did not return to first channel value") - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - # Go to the first image and record the first image value self._getFirstValue( driver, "Image" ) firstImageValue = self._getCurrentValue( driver, "Image" ) @@ -190,11 +86,13 @@ def test_channelAnimatorFirstValue(self): # Allow animation for 2 seconds self._animateForward( driver, "Image" ) time.sleep(2) - + self._stopAnimation( driver, "Image") + # Click the first valid value button # Check that the image value is the same as the first image value self._getFirstValue( driver, "Image" ) currImageValue = self._getCurrentValue( driver, "Image" ) + print "Current Value=", currImageValue," firstValue=",firstImageValue self.assertEqual( int(currImageValue), int(firstImageValue), "Image Animator did not return to first image") # Test that the Channel Animator can go to the last frame value of the test image @@ -212,7 +110,8 @@ def test_channelAnimatorLastValue(self): self._getFirstValue( driver, "Channel" ) self._animateForward( driver, "Channel" ) time.sleep(2) - + self._stopAnimation( driver, "Channel") + # Click the last valid value button # Check that the channel value is the same as the last channel value self._getLastValue( driver, "Channel" ) @@ -221,8 +120,6 @@ def test_channelAnimatorLastValue(self): # Load another image so the image animator is available. Util.load_image( self, driver, "aJ.fits") - - self.channel_to_image_animator( driver ) # Go to the first image and record the first image value self._getLastValue( driver, "Image" ) @@ -231,6 +128,7 @@ def test_channelAnimatorLastValue(self): # Allow animation for 2 seconds self._animateForward( driver, "Image" ) time.sleep(2) + self._stopAnimation( driver, "Image") # Click the first valid value button # Check that the image value is the same as the first image value @@ -275,10 +173,7 @@ def test_animatorBoundary(self): # Check that the input to the upper spin box cannot be less than the first channel value upperBoundValue = Util._changeElementText( self, driver, upperBoundText, -10) - self.assertGreaterEqual( int(upperBoundValue), 0, "Channel Upper bound value is negative") - - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) + self.assertGreaterEqual( int(upperBoundValue), 0, "Channel Upper bound value is negative" ) # Record the last image value self._getLastValue( driver, "Image" ) @@ -355,9 +250,6 @@ def test_imageAnimatorBoundaryAnimation(self): Util.load_image( self, driver, "aJ.fits") Util.load_image( self, driver, "Default") - # Change the Channel Animator to an Image Animator - self.channel_to_image_animator( driver ) - # Record the first image value self._getFirstValue( driver, "Image" ) diff --git a/carta/html5/common/skel/source/class/skel/simulation/tAxis.py b/carta/html5/common/skel/source/class/skel/simulation/tAxis.py index 65f823ab..467885d0 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tAxis.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tAxis.py @@ -76,7 +76,7 @@ def test_permuteAxes(self): animatorTypeStokes = self._getAnimatorType(driver, "StokesAnimatorType") #Open the image settings tab - Util.openImageSettings( self, driver ) + Util.openSettings( self, driver, "Image" ) #Click on the Axes/Border tab to display controls for permuting axes self._clickGridAxesTab( driver ) diff --git a/carta/html5/common/skel/source/class/skel/simulation/tColorMap.py b/carta/html5/common/skel/source/class/skel/simulation/tColorMap.py new file mode 100644 index 00000000..74eb6669 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/simulation/tColorMap.py @@ -0,0 +1,148 @@ +import unittest +import Util +import time +import selectBrowser +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + +#Test color map functionality. +class tColorMap(unittest.TestCase): + def setUp(self): + browser = selectBrowser._getBrowser() + Util.setUp(self, browser) + + # Go to the next valid value + def _nextImage(self, driver): + timeout = selectBrowser._getSleep() + incrementButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ImageTapeDeckIncrement"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", incrementButton) + ActionChains(driver).click( incrementButton ).perform() + time.sleep( timeout ) + + def _getColorMapName( self, driver ): + colorMapCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "colorMapName"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", colorMapCombo ) + colorMapText = colorMapCombo.find_element_by_xpath( ".//div/div") + mapName = colorMapText.text + return mapName + + def _chooseNewColorMap(self, driver ): + timeout = selectBrowser._getSleep() + colorMapCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "colorMapName"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", colorMapCombo ) + ActionChains(driver).click(colorMapCombo).send_keys( Keys.ARROW_DOWN).send_keys( Keys.ARROW_DOWN + ).send_keys( Keys.ENTER).perform() + time.sleep( timeout ) + + + # Load 3 images and check that if we change the color map of one of them, + # all three of them are changed. + def test_globalIsGlobal(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + #Load a default image + Util.load_image( self, driver, "Default") + Util.load_image( self, driver, "aJ.fits") + Util.load_image( self, driver, "aH.fits") + + #Store the old colormap + oldMapName = self._getColorMapName( driver ) + print "Old map name=",oldMapName + + #Choose a new color map + self._chooseNewColorMap( driver ) + newMapName = self._getColorMapName( driver ) + print "New map name=", newMapName + self.assertTrue( oldMapName != newMapName, "Color map name did not change") + + #Animate through the images and make sure they are all using the + #new color map. + for i in range(0,2): + self._nextImage( driver ) + imageMapName = self._getColorMapName( driver ) + print "Image name=",imageMapName + self.assertTrue( imageMapName == newMapName, "Image name was not changed") + + + # Load 3 images + # Select the bottom two in the stack + # Turn of global in the color map + # Change the color map + # Check that the first two have the changed color map and the third does not + def test_notGlobalIsNotGlobal(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + #Load a default image + image1 = "Default" + image2 = "aJ.fits" + image3 = "aH.fits" + Util.load_image( self, driver, image1 ) + Util.load_image( self, driver, image2 ) + Util.load_image( self, driver, image3 ) + + #Store the old colormap + oldMapName = self._getColorMapName( driver ) + print "Old map name=",oldMapName + + #Open the stack tab + Util.openSettings( self, driver, "Image" ) + Util.clickTab( driver, "Stack" ) + + #Change to manual selection + autoSelectCheck = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "autoSelectImages"))) + ActionChains(driver).click( autoSelectCheck ).perform() + + #Select the first two images in the stack + imageFirst = "//div[text()='{0}']/..".format( image2 ) + print "imageFirst=",imageFirst + firstItem = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, imageFirst ))) + imageSecond = "//div[text()='{0}']/..".format( image3 ) + print "imageSecond=",imageSecond + secondItem = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, imageSecond ))) + ActionChains( driver ).send_keys( Keys.SHIFT ).click( firstItem ).click( secondItem ).perform() + + #Uncheck using a global color map + globalCheck = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "colorMapGlobal"))) + ActionChains(driver).click( globalCheck ).perform() + + #Choose a new color map + self._chooseNewColorMap( driver ) + newMapName = self._getColorMapName( driver ) + print "New map name=", newMapName + self.assertTrue( oldMapName != newMapName, "Color map name did not change") + + #Change back to auto select so that the color map changes as we animate + ActionChains(driver).click( autoSelectCheck ).perform() + + #Animate through images. Make sure the first and second ones are using the new map + #and the third is not + self._nextImage( driver ) + imageMapName = self._getColorMapName( driver ) + print "Image 1 name=",imageMapName + self.assertTrue( imageMapName == oldMapName, "Color map name 1 incorrect") + self._nextImage( driver ) + imageMapName = self._getColorMapName( driver ) + print "Image 2 name=",imageMapName + self.assertTrue( imageMapName == newMapName, "Color map name 2 incorrect") + self._nextImage( driver ) + imageMapName = self._getColorMapName( driver ) + print "Image 3 name=",imageMapName + self.assertTrue( imageMapName == newMapName, "Color map name 3 incorrect") + + + def tearDown(self): + #Close the browser + self.driver.close() + #Allow browser to fully close before continuing + time.sleep(2) + #Close the session and delete temporary files + self.driver.quit() + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/simulation/tContour.py b/carta/html5/common/skel/source/class/skel/simulation/tContour.py index 6d47b3ba..39fec7c1 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tContour.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tContour.py @@ -103,7 +103,7 @@ def test_rangeMethod(self): Util.load_image( self, driver, "aH.fits") #Show the image settings - Util.openImageSettings( self, driver ) + Util.openSettings( self, driver, "Image" ) # Navigate to Contour tab of the Histogram Settings self._clickContourTab( driver ) @@ -139,7 +139,7 @@ def test_deleteContourSet(self): Util.load_image( self, driver, "aH.fits") #Show the image settings - Util.openImageSettings( self, driver ) + Util.openSettings( self, driver, "Image" ) # Navigate to Contour tab of the Histogram Settings self._clickContourTab( driver ) diff --git a/carta/html5/common/skel/source/class/skel/simulation/tStack.py b/carta/html5/common/skel/source/class/skel/simulation/tStack.py new file mode 100644 index 00000000..69f71c4d --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/simulation/tStack.py @@ -0,0 +1,71 @@ +import unittest +import Util +import time +import selectBrowser +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + +#Stack functionality. +class tStack(unittest.TestCase): + def setUp(self): + browser = selectBrowser._getBrowser() + Util.setUp(self, browser) + + + + + # Load 3 images + # Hide the second image; check the count goes down to 2 + # Show the second image; check the count goes up to 3 + def test_hideShow(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + #Load images + Util.load_image( self, driver, "Default") + Util.load_image( self, driver, "aJ.fits") + Util.load_image( self, driver, "aH.fits") + + #Verify the image animator sees three images. + Util.verifyAnimatorUpperBound( self, driver, 2, "Image" ) + + #Open the image settings + #Open the stack tab + Util.openSettings( self, driver, "Image" ) + Util.clickTab( driver, "Stack" ) + + #Turn off auto select + autoSelectCheck = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "autoSelectImages"))) + ActionChains(driver).click( autoSelectCheck ).perform() + + #Hide the second image + secondItem = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.form.ListItem']/div[text()='aJ.fits']/.."))) + ActionChains(driver).context_click( secondItem ).perform() + ActionChains(driver).send_keys( Keys.ARROW_DOWN ).send_keys( Keys.ARROW_DOWN).send_keys( Keys.ENTER ).perform() + + #Verify the animator sees two images + Util.verifyAnimatorUpperBound(self, driver, 1, "Image" ) + + #Show the second image + secondItem = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.form.ListItem']/div[text()='aJ.fits']/.."))) + ActionChains(driver).context_click( secondItem ).perform() + ActionChains(driver).send_keys( Keys.ARROW_DOWN ).send_keys( Keys.ARROW_DOWN).send_keys( Keys.ENTER ).perform() + + #Verify the animator sees three images + Util.verifyAnimatorUpperBound( self, driver, 2, "Image") + + + def tearDown(self): + #Close the browser + self.driver.close() + #Allow browser to fully close before continuing + time.sleep(2) + #Close the session and delete temporary files + self.driver.quit() + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/simulation/tView.py b/carta/html5/common/skel/source/class/skel/simulation/tView.py index a22470d9..67a1ec5d 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tView.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tView.py @@ -124,7 +124,7 @@ def test_replaceImageLoader(self): # Finally, click on the first image window and verify we can load an image # in it ActionChains( driver).click( imageWindows[0]).perform() - Util.load_image( self, driver, "aJ.fits") + Util.load_image( self, driver, "aJ.fits", "img_pwUID2") def tearDown(self): #Close the browser diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js index d80429f3..bd11955a 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorMapsWidget.js @@ -69,21 +69,13 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { this._add( this.m_intensityLowLabel ); this._add( new qx.ui.core.Spacer(), {flex:1}); - this.m_mapCombo = new qx.ui.form.ComboBox(); + this.m_mapCombo = new skel.widgets.CustomUI.SelectBox( skel.widgets.Colormap.ColorMapsWidget.CMD_SET_MAP, "name"); + skel.widgets.TestID.addTestId( this.m_mapCombo, "colorMapName" ); this.m_mapCombo.setToolTipText( "Select a color map."); - this.m_mapCombo.addListener( skel.widgets.Path.CHANGE_VALUE, function(e){ - if ( this.m_id !== null ){ - var mapName = e.getData(); - //Send a command to the server to let them know the map changed. - var path = skel.widgets.Path.getInstance(); - var cmd = this.m_id + path.SEP_COMMAND + skel.widgets.Colormap.ColorMapsWidget.CMD_SET_MAP; - var params = "name:"+mapName; - this.m_connector.sendCommand( cmd, params, this._errorMapIndexCB( this )); - } - },this); this._add( this.m_mapCombo ); this.m_globalCheck = new qx.ui.form.CheckBox( "Global"); + skel.widgets.TestID.addTestId( this.m_globalCheck, "colorMapGlobal"); this.m_globalCheck.addListener( skel.widgets.Path.CHANGE_VALUE, function(e){ if ( this.m_id !== null ){ var global = this.m_globalCheck.getValue(); @@ -111,23 +103,11 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { try { var oldName = this.m_mapCombo.getValue(); var colorMaps = JSON.parse( val ); - var mapCount = colorMaps.colorMapCount; - this.m_mapCombo.removeAll(); - for ( var i = 0; i < mapCount; i++ ){ - var colorMapName = colorMaps.maps[i]; - var tempItem = new qx.ui.form.ListItem( colorMapName ); - this.m_mapCombo.add( tempItem ); - } + this.m_mapCombo.setSelectItems( colorMaps.maps ); + //Try to reset the old selection if ( oldName !== null ){ - this.m_mapCombo.setValue( oldName ); - } - //Select the first item - else if ( mapCount > 0 ){ - var selectables = this.m_mapCombo.getChildrenContainer().getSelectables(true); - if ( selectables.length > 0 ){ - this.m_mapCombo.setValue( selectables[0].getLabel()); - } + this.m_mapCombo.setSelectValue( oldName ); } } catch( err ){ @@ -154,18 +134,7 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { * @param mapName {String} the name of the selected color map. */ setMapName : function( mapName ){ - var selectables = this.m_mapCombo.getChildrenContainer().getSelectables(); - var currValue = this.m_mapCombo.getValue(); - for ( var i = 0; i < selectables.length; i++ ){ - var mapItem = selectables[i]; - var newValue = selectables[i].getLabel(); - if ( newValue == mapName ){ - if ( currValue != newValue ){ - this.m_mapCombo.setValue( newValue ); - } - break; - } - } + var selectables = this.m_mapCombo.setSelectValue( mapName ); }, /** @@ -174,6 +143,7 @@ qx.Class.define("skel.widgets.Colormap.ColorMapsWidget", { */ setId : function( id ){ this.m_id = id; + this.m_mapCombo.setId( id ); var path = skel.widgets.Path.getInstance(); var dataPath = this.m_id + path.SEP + "data"; this.m_sharedVarData = this.m_connector.getSharedVar( dataPath ); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js index 47a3ca08..4a47bdc8 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js @@ -79,6 +79,7 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { */ _initList : function( width ){ this.m_list = new qx.ui.form.List(); + skel.widgets.TestID.addTestId( this.m_list, "imageStackList" ); this.m_list.setMinWidth( width ); this.m_list.setSelectionMode("multi"); this.m_list.setDraggable(true); @@ -320,7 +321,7 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { if ( visible ){ //Hide button - var hideCmd = new skel.Command.Data.CommandDataHideImage( items[i].layer ); + var hideCmd = new skel.Command.Data.CommandDataHideImage( items[i].layer, i ); var hideButton = new qx.ui.menu.Button( "Hide"); hideButton.addListener( "execute", function(){ this.doAction( true, function(){}); @@ -329,7 +330,7 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { } else { //Show button - var showCmd = new skel.Command.Data.CommandDataShowImage( items[i].layer ); + var showCmd = new skel.Command.Data.CommandDataShowImage( items[i].layer, i ); var showButton = new qx.ui.menu.Button( "Show"); showButton.addListener( "execute", function(){ this.doAction( true, function(){}); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js index 9555926f..9655d494 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js @@ -79,6 +79,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { selectContainer.setLayout( new qx.ui.layout.HBox(1) ); selectContainer.add( new qx.ui.core.Spacer(), {flex:1}); this.m_autoSelectCheck = new qx.ui.form.CheckBox( "Auto Select"); + skel.widgets.TestID.addTestId( this.m_autoSelectCheck, "autoSelectImages" ); this.m_autoSelectCheck.setToolTipText( "Auto selection based on animator or manual selection of layer(s)."); this.m_autoSelectCheck.addListener( "changeValue", this._autoSelectChanged, this ); selectContainer.add( this.m_autoSelectCheck ); From 0c5d789c6228a2682ef3f22869dd765df6e6ff28 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Thu, 12 Nov 2015 21:21:02 -0700 Subject: [PATCH 27/37] temporary checkin so that susan can inspect the IRemoteVGView.h class --- carta/cpp/CartaLib/IRemoteVGView.h | 121 +++++++++++++++- carta/cpp/core/Hacks/ILayeredView.h | 8 ++ carta/cpp/core/Hacks/IVectorGraphicsView.cpp | 3 + carta/cpp/core/Hacks/IVectorGraphicsView.h | 3 + carta/cpp/core/Hacks/ManagedLayerView.cpp | 13 +- carta/cpp/core/Hacks/ManagedLayerView.h | 70 +++++++-- carta/cpp/core/SimpleRemoteVGView.cpp | 25 ++++ carta/cpp/core/SimpleRemoteVGView.h | 19 ++- .../class/skel/hacks/LayeredViewManager.js | 133 ++++++++++++++++++ .../skel/source/class/skel/hacks/VGView.js | 36 ++++- 10 files changed, 404 insertions(+), 27 deletions(-) create mode 100644 carta/html5/common/skel/source/class/skel/hacks/LayeredViewManager.js diff --git a/carta/cpp/CartaLib/IRemoteVGView.h b/carta/cpp/CartaLib/IRemoteVGView.h index 14b4b614..18a5ea97 100644 --- a/carta/cpp/CartaLib/IRemoteVGView.h +++ b/carta/cpp/CartaLib/IRemoteVGView.h @@ -12,6 +12,87 @@ namespace Carta { namespace Lib { + +/// brainstorming: +/// - right now we really only care about users with a keyboard and mouse, but... +/// - we want the ability (in the future) to make the viewer work on mobile devices, which +/// are most likely tablets with touch screens +/// - we want the ability to make the user interface as intuitive as possible on both +/// mobile and desktop +/// - we want to avoid silly emulation of mouse events using touch, because that would not +/// produce intuitive experience for touch users, consider this as an example: +/// - on desktop we pan using a single tap +/// - on desktop we zoom using mouse scroll-wheel +/// - on touch the accepted zoom is via pinch, and pan is by sliding a single finger, +/// and you can seemlessly swith from one to the other (try google maps for example) +/// - how would you emulate the desktop pan/zoom with the touches to produce the +/// expected behavior? +/// - some users will want to assign different gestures to different actions, it would be nice +/// if we could do all of it in javascript on the client side +/// - the VGView will support some basic events that have easy counterparts on both mouse & +/// touch devices +/// - but it'll be easy to extend/override these +/// - if we do want to react to specific events (like pinch, on keyboard), it should be possible +/// to do so, but maybe we should refrain from those as much as possible +/// +/// Data associated with events: +/// - point P +/// - scalar S +/// - integer I (we could cram this into S) +/// - boolean B +/// +/// single tap: P +/// double tap: P +/// pinch: P,S +/// two finger tap: P, P +/// scroll-wheel: P, S +/// two finger drag: P, P +/// hover: P +/// keyboard: P, I +/// left-click: P, S +/// middle-click: P, S +/// right-click: P, S +/// shift-click: P, S +/// ctrl-alt-shift-right-click: P,S or P,I or P,I,I, or P,I,B,B,B +/// swipe: P, S, S (origin, direction angle, speed) +/// +/// avoiding virtual inheritance... passing pointers in signals can be tricky business, +/// because who is the owner (consider two receivers in separate threads, connected using +/// queued connection, who frees up the pointer?). This could probably be resolved using +/// smart (shared?) pointers, but we need to test it. +/// +class InputEvent { +public: + enum class Type { + Tap, // e.g. click, or one finger tap + Press, // e.g. long one finger press, or middle mouse click? + DoubleTap, // e.g. double click, or double tap of a single finger + Hover, // e.g. mouse move, or long press and drag? or something stateful? + Custom + }; + + InputEvent( Type t, QString ct = QString()) { m_type = t; m_customType = ct; } + + Type type() const { return m_type; } + QString custom() const { return m_customType; } + const std::vector & points() const { return m_points; } + const std::vector & scalars() const { return m_scalars; } + const std::vector & integers() const { return m_integers; } + const std::vector & bools() const { return m_bools; } + + /// if we really need to store something else in here? + const std::vector & extraBuff() const { return m_extraBuffer; } + +private: + Type m_type; + QString m_customType; + std::vector m_points; + std::vector m_scalars; + std::vector m_integers; + std::vector m_bools; + std::vector m_extraBuffer; +}; + /// /// \brief An API specification for rendering graphical views to be displayed by clients. /// @@ -32,6 +113,7 @@ class IRemoteVGView typedef Carta::Lib::VectorGraphics::VGList VGList; + /// constructor IRemoteVGView( QObject * parent ) : QObject( parent ) { } /// returns size of the view in client (the master's UI) @@ -42,14 +124,32 @@ class IRemoteVGView virtual const QString & getRVGViewName() = 0; - /// sets the raster to be rendered in the view + /// sets the raster to be rendered in the view, as soon as possible, + /// without synchronizing with VG virtual void setRaster( const QImage & image ) = 0; - /// sets the VG to be rendered on top of the raster + /// sets the VG to be rendered on top of the raster, as soon as possible + /// without synchronizing with raster virtual void setVG( const VGList & vglist ) = 0; + /// sets the raster and VG simultaneously, making sure both appear at the same time + virtual void + setRasterAndVG( const QImage & image, const VGList & vglist ) = 0; + + /// sets where VG rendered (client vs server) + virtual void + setVGrenderedOnServer( bool flag) = 0; + + /// returns true if VG is rendered on server, false if on client + virtual bool + isVGrenderedOnServer() = 0; + + /// tell UI which events we want to receive + virtual void + enableInputEvent( InputEvent::Type type, QString name = QString()) = 0; + public slots: /// schedule a refresh with a given ID @@ -66,6 +166,10 @@ public slots: /// emitted when client repainted the view void repainted( qint64 id ); + + /// emitted when client sends us a gesture + void + inputEvent( InputEvent e); }; class IQImageCombiner @@ -81,6 +185,7 @@ class IQImageCombiner ~IQImageCombiner() { } }; +/* class LayeredRemoteVGView; class LayerHandle @@ -101,8 +206,15 @@ class LayerHandle virtual ~LayerHandle() { } }; +*/ /// lowest level functionality for layered views +/// - allows arbitrary many layers, and ability to associate compositors with each raster +/// layer, that's about it +/// - no layer deletion, re-ordering, funnelling input, etc... +/// this is still in experimental stage actually, we difinitely need input apis for example +/// maybe we want to implement this on top of an existing instance of IRemoteVGView instead +/// of creating our own internally? class LayeredRemoteVGView : public QObject { @@ -184,14 +296,13 @@ public slots: qint64 m_repaintId = - 1; QTimer * m_timer = nullptr; - private slots: void p_timerCB(); - void p_sizeChangedCB(); - + void + p_sizeChangedCB(); }; /// paints one raster over the other one, overwriting the bottom one diff --git a/carta/cpp/core/Hacks/ILayeredView.h b/carta/cpp/core/Hacks/ILayeredView.h index 6ec63ed7..1f472044 100644 --- a/carta/cpp/core/Hacks/ILayeredView.h +++ b/carta/cpp/core/Hacks/ILayeredView.h @@ -25,11 +25,17 @@ enum class LayerType enum class InputMaskBits : std::uint64_t { + /// keyboard events Key = 0b0000001, + /// active mouse events: click, down, up, drag MouseActive = 0b0000010, + /// passive mouse events: hover, enter, leave MousePassive = 0b0000100, + /// just about all pointer events PointerActive = 0b1000, + /// are there any passive pointer events really? PointerPassive = 0b1000, + /// all events ALL = 0xffffffff }; @@ -58,6 +64,7 @@ class InputEvent buttons(); }; +/* class ManagedLayerView : public QObject { Q_OBJECT @@ -77,6 +84,7 @@ class ManagedLayerView : public QObject virtual ~ManagedLayerView() { } }; +*/ class LayerHandle : public QObject { diff --git a/carta/cpp/core/Hacks/IVectorGraphicsView.cpp b/carta/cpp/core/Hacks/IVectorGraphicsView.cpp index 926c12bf..92fe3c5a 100644 --- a/carta/cpp/core/Hacks/IVectorGraphicsView.cpp +++ b/carta/cpp/core/Hacks/IVectorGraphicsView.cpp @@ -2,9 +2,12 @@ * **/ +#ifdef DONT_COMPILE #include "IVectorGraphicsView.h" namespace Hacks { } + +#endif diff --git a/carta/cpp/core/Hacks/IVectorGraphicsView.h b/carta/cpp/core/Hacks/IVectorGraphicsView.h index 7d21e260..7f561b68 100644 --- a/carta/cpp/core/Hacks/IVectorGraphicsView.h +++ b/carta/cpp/core/Hacks/IVectorGraphicsView.h @@ -7,6 +7,7 @@ // WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING // ================================================================================== +#ifdef DONT_COMPILE #pragma once @@ -98,3 +99,5 @@ class IVectorGraphicsView }; } + +#endif diff --git a/carta/cpp/core/Hacks/ManagedLayerView.cpp b/carta/cpp/core/Hacks/ManagedLayerView.cpp index 0a19cc9d..e82a32c6 100644 --- a/carta/cpp/core/Hacks/ManagedLayerView.cpp +++ b/carta/cpp/core/Hacks/ManagedLayerView.cpp @@ -10,17 +10,25 @@ namespace Carta namespace Hacks { -ManagedLayer::ManagedLayer(ManagedLayerViewInh * mlv) +ManagedLayer::ManagedLayer(ManagedLayerViewInh * mlv, QString name) { m_mlv = mlv; + m_layerName = name; m_mlv-> p_addLayer( this); } +void ManagedLayer::setRaster(const QImage & image) +{ + +} + ManagedLayerViewInh::ManagedLayerViewInh(QString viewName, IConnector * connector, QObject * parent) + : QObject( parent) { m_connector = connector; m_rvgv.reset( m_connector-> makeRemoteVGView( viewName)); + } IConnector *ManagedLayerViewInh::connector() @@ -28,9 +36,10 @@ IConnector *ManagedLayerViewInh::connector() return m_connector; } -void ManagedLayerViewInh::p_addLayer(ManagedLayer * layer) +ManagedLayer::ID ManagedLayerViewInh::p_addLayer(ManagedLayer * layer) { m_layers.push_back( layer); + return m_nextId ++ ; } }} diff --git a/carta/cpp/core/Hacks/ManagedLayerView.h b/carta/cpp/core/Hacks/ManagedLayerView.h index c1d757ba..1bd36c08 100644 --- a/carta/cpp/core/Hacks/ManagedLayerView.h +++ b/carta/cpp/core/Hacks/ManagedLayerView.h @@ -17,20 +17,38 @@ namespace Hacks { class ManagedLayerViewInh; +/// derive from this class to create custom managed layers class ManagedLayer : public QObject { Q_OBJECT public: - ManagedLayer( ManagedLayerViewInh * ); + typedef int ID; + + ManagedLayer( ManagedLayerViewInh *, QString name ); + + virtual QString + layerName() { return m_layerName; } + + /// reimplement this to react to view resizes + virtual void + onResize( const QSize & size ) { Q_UNUSED( size ); } + + /// call this to update the rendering of raster + void setRaster( const QImage & image); + + /// this returns a unique ID of the layer (wrt. to the manager) + ID layerID() { return m_id; } + virtual ~ManagedLayer() { } -private: +protected: ManagedLayerViewInh * m_mlv = nullptr; - + QString m_layerName; + ID m_id = -1; }; class ManagedLayerViewInh : public QObject @@ -39,8 +57,7 @@ class ManagedLayerViewInh : public QObject public: - ManagedLayerViewInh( QString viewName, IConnector * connector, QObject * parent = nullptr); - + ManagedLayerViewInh( QString viewName, IConnector * connector, QObject * parent = nullptr ); // template < class ILayer, typename ... Args > // std::shared_ptr < ILayer > @@ -54,27 +71,44 @@ class ManagedLayerViewInh : public QObject virtual ~ManagedLayerViewInh() { } - IConnector * connector(); + IConnector * + connector(); private: - virtual void p_addLayer( ManagedLayer * layer); + virtual ManagedLayer::ID + p_addLayer( ManagedLayer * layer ); friend class ManagedLayer; - std::vector< ManagedLayer * > m_layers; + std::vector < ManagedLayer * > m_layers; IConnector * m_connector = nullptr; + ManagedLayer::ID m_nextId = 0; Carta::Lib::IRemoteVGView::UniquePtr m_rvgv = nullptr; - }; class EyesLayer : public ManagedLayer { Q_OBJECT + +public: + + EyesLayer( ManagedLayerViewInh * mlv, QString layerName ) + : ManagedLayer( mlv, layerName ) + { } + +private: +}; + +class BouncyLayer : public ManagedLayer +{ + Q_OBJECT + public: - EyesLayer( ManagedLayerViewInh * mlv ); + BouncyLayer( ManagedLayerViewInh * mlv, QString layerName ) + : ManagedLayer( mlv, layerName ) { } }; //std::shared_ptr < ManagedLayerViewInh > @@ -104,5 +138,21 @@ static auto foo = apiTest(); */ +static int +apiTest() +{ + // create a new managed layer view + IConnector * connector = nullptr; + auto mlv = new ManagedLayerViewInh( "mlv1", connector, nullptr ); + + // add some layers to the view + auto eyesLayer1 = new EyesLayer( mlv, "eyes1" ); + auto eyesLayer2 = new EyesLayer( mlv, "eyes two" ); + auto bouncyBallLayer1 = new BouncyLayer( mlv, "Bouncy" ); + + return 7; +} + +//static int test = apiTest(); } } diff --git a/carta/cpp/core/SimpleRemoteVGView.cpp b/carta/cpp/core/SimpleRemoteVGView.cpp index 232ca8dd..0a8e2b71 100644 --- a/carta/cpp/core/SimpleRemoteVGView.cpp +++ b/carta/cpp/core/SimpleRemoteVGView.cpp @@ -41,6 +41,31 @@ SimpleRemoteVGView::setVG( const Lib::IRemoteVGView::VGList & vglist ) m_vgList = vglist; } +void SimpleRemoteVGView::setRasterAndVG(const QImage & image, const Lib::IRemoteVGView::VGList & vglist) +{ + setRaster( image); + setVG( vglist); +} + +void SimpleRemoteVGView::setVGrenderedOnServer(bool flag) +{ + /// \todo client side rendering is not implemented yet + CARTA_ASSERT( flag == true); + Q_UNUSED( flag); +} + +bool SimpleRemoteVGView::isVGrenderedOnServer() +{ + /// \todo only server side is supported at the moment + return true; +} + +void SimpleRemoteVGView::enableInputEvent( Carta::Lib::InputEvent::Type type, QString name) +{ + Q_UNUSED( type); + Q_UNUSED( name); +} + qint64 SimpleRemoteVGView::scheduleRepaint( qint64 id ) { diff --git a/carta/cpp/core/SimpleRemoteVGView.h b/carta/cpp/core/SimpleRemoteVGView.h index c7be31b5..e595e4f7 100644 --- a/carta/cpp/core/SimpleRemoteVGView.h +++ b/carta/cpp/core/SimpleRemoteVGView.h @@ -20,6 +20,10 @@ namespace Carta { namespace Core { + +/// Basic implementation of IRemoteVGView api. We'll most likely replace this with +/// specialized desktop/server versions. + class SimpleRemoteVGView : public Carta::Lib::IRemoteVGView , public IView @@ -35,15 +39,24 @@ class SimpleRemoteVGView virtual const QString & getRVGViewName() override; -// virtual void -// setRaster( const QColor & color ) override; - virtual void setRaster( const QImage & image ) override; virtual void setVG( const VGList & vglist ) override; + virtual void + setRasterAndVG( const QImage & image, const VGList & vglist ) override; + + virtual void + setVGrenderedOnServer( bool flag) override; + + virtual bool + isVGrenderedOnServer() override; + + virtual void + enableInputEvent( Carta::Lib::InputEvent::Type type, QString name = QString()) override; + public slots: virtual qint64 diff --git a/carta/html5/common/skel/source/class/skel/hacks/LayeredViewManager.js b/carta/html5/common/skel/source/class/skel/hacks/LayeredViewManager.js new file mode 100644 index 00000000..d91de5c4 --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/hacks/LayeredViewManager.js @@ -0,0 +1,133 @@ +/** + * Created by pfederl on 04/01/15. + * + * Widget for controling layers in a layered view. + * + * @asset(qx/icon/${qx.icontheme}/16/actions/list-add.png) + * @asset(qx/icon/${qx.icontheme}/16/actions/go-up.png) + * @asset(qx/icon/${qx.icontheme}/16/actions/go-down.png) + * @asset(qx/icon/${qx.icontheme}/16/actions/edit-delete.png) + * + * @ignore( mImport) + */ + +qx.Class.define( "skel.hacks.LayeredViewManager", { + + extend: qx.ui.container.Composite, + + events: { + "viewRefreshed": "qx.event.type.Data" + }, + + /** + * Constructor + */ + construct: function( viewName ) + { + this.base( arguments ); + this.m_connector = mImport( "connector" ); + this.m_viewName = viewName; + this.setLayout( new qx.ui.layout.VBox( 5 ) ); + // create a list widget + this.m_listWidget = new qx.ui.form.List(); + this.add( this.m_listWidget, {flex: 1} ); + + // create buttons + var m_row1Widget = new qx.ui.container.Composite( new qx.ui.layout.HBox( 3 ) ); + this.add( m_row1Widget ); + var addButton = new qx.ui.form.Button( "Add", "icon/16/actions/list-add.png" ); + addButton.set( {toolTipText: "Add new layer", show: "icon", padding: 1} ); + m_row1Widget.add( addButton ); + var upButton = new qx.ui.form.Button( "Up", "icon/16/actions/go-up.png" ); + upButton.set( {toolTipText: "Move layer up", show: "icon", padding: 1} ); + m_row1Widget.add( upButton ); + var downButton = new qx.ui.form.Button( "Down", "icon/16/actions/go-down.png" ); + downButton.set( {toolTipText: "Move layer down", show: "icon", padding: 1} ); + m_row1Widget.add( downButton ); + var deleteButton = new qx.ui.form.Button( "Down", "icon/16/actions/edit-delete.png" ); + deleteButton.set( {toolTipText: "Delete layer", show: "icon", padding: 1} ); + m_row1Widget.add( deleteButton ); + + // create rename textbox + this.m_renameTextField = new qx.ui.form.TextField(""); + this.m_renameTextField.set({ toolTipText: "Type new name and press enter"}); + this.add( this.m_renameTextField); + + // alpha slider + var alphaRow = new qx.ui.container.Composite( new qx.ui.layout.HBox()); + this.add( alphaRow); + this.m_alphaLabel = new qx.ui.basic.Label( ""); + this.m_alphaLabel.set({ rich: true, font: "monospace" }); + alphaRow.add( this.m_alphaLabel); + this.m_alphaSlider = new qx.ui.form.Slider(); + this.m_alphaSlider.set({ minimum: 0, maximum: 255, value: 10}); + alphaRow.add( this.m_alphaSlider, { flex: 1 }); + this.m_alphaSlider.addListener( "changeValue", this._sliderCB.bind(this)); + this.m_alphaSlider.set({ toolTipText: "Change alpha for selected layers."}); + + // mask toggles + var maskToggles = new qx.ui.container.Composite( new qx.ui.layout.HBox(2)); + this.add( maskToggles); + var CLS = qx.ui.form.CheckBox; + this.m_redToggle = new CLS( "R"); + maskToggles.add( this.m_redToggle); + this.m_greenToggle = new CLS( "G"); + maskToggles.add( this.m_greenToggle); + this.m_blueToggle = new CLS( "B"); + maskToggles.add( this.m_blueToggle); + this.m_noMaskToggle = new CLS( "None"); + maskToggles.add( this.m_noMaskToggle); + + this.m_redToggle.addListener( "changeValue", this._maskToggleCB.bind(this, "R")); + this.m_greenToggle.addListener( "changeValue", this._maskToggleCB.bind(this, "G")); + this.m_blueToggle.addListener( "changeValue", this._maskToggleCB.bind(this, "B")); + this.m_noMaskToggle.addListener( "changeValue", this._maskToggleCB.bind(this, "X")); + + // update the slider label + this._sliderCB(); + }, + + members: { + + m_viewName : null, + m_listWidget: null, + m_connector : null, + + /** + * Get the overlay widget + */ + viewName: function() + { + return this.m_viewName; + }, + + _sliderCB: function() { + var num = "" + this.m_alphaSlider.getValue(); + while( num.length < 3) { num = " " + num; } + this.m_alphaLabel.setValue( "α" + num); + }, + + _maskToggleCB : function( which, e) + { + console.log( which, e); + var flag = e.getData(); + if( which === "R" && flag) { + this.m_noMaskToggle.setValue( false); + } + if( which === "G" && flag) { + this.m_noMaskToggle.setValue( false); + } + if( which === "B" && flag) { + this.m_noMaskToggle.setValue( false); + } + if( which === "X" && flag) { + this.m_redToggle.setValue( false); + this.m_greenToggle.setValue( false); + this.m_blueToggle.setValue( false); + } + + } + + } +} ); + diff --git a/carta/html5/common/skel/source/class/skel/hacks/VGView.js b/carta/html5/common/skel/source/class/skel/hacks/VGView.js index 2178364e..26281994 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/VGView.js +++ b/carta/html5/common/skel/source/class/skel/hacks/VGView.js @@ -2,6 +2,11 @@ * Created by pfederl on 04/01/15. * * Widget capable of displaying raster images overlayed with vector graphics. + * + * Note: we may need different implementation for desktop/server environments to get + * the most performance, e.g. for rendering VG. + * + * @ignore( mImport) */ qx.Class.define( "skel.hacks.VGView", { @@ -23,19 +28,29 @@ qx.Class.define( "skel.hacks.VGView", { this.setLayout( new qx.ui.layout.Canvas() ); // create a basic view widget as the bottom layer this.m_viewWidget = new skel.boundWidgets.View.View( viewName ); - this.add( this.m_viewWidget, { edge: 0 } ); + this.add( this.m_viewWidget, {edge: 0} ); // create an input overlay widget (transparent, used only to capture // input events) this.m_overlayWidget = new qx.ui.core.Widget(); - this.add( this.m_overlayWidget, { edge: 0 } ); + this.add( this.m_overlayWidget, {edge: 0} ); - this.m_overlayWidget.addListener( "click", function() { - console.log( "Click"); - }); + this.m_overlayWidget.addListener( "click", function() + { + console.log( "Click" ); + } ); // create a settings UI layer this.m_settingsLayer = this._createSettingsUI(); - this.add( this.m_settingsLayer, { top: 10, right: 10 } ); + this.add( this.m_settingsLayer, {top: 10, right: 10} ); + + this.m_layerManager = new skel.hacks.LayeredViewManager( viewName ); + this.m_layerManager.setVisibility( "excluded" ); + this.m_layerManager.set( { + visibility : "excluded", + backgroundColor: "white", + padding: 5 + } ); + this.add( this.m_layerManager, { bottom: 10, right: 10 }); this.m_overlayWidget.addListener( "keydown", function( e ) { @@ -48,7 +63,14 @@ qx.Class.define( "skel.hacks.VGView", { this.m_settingsLayer.setVisibility( "visible"); } } - }.bind(this) ); + if( e.isCtrlPressed() && e.isShiftPressed() && e.getKeyIdentifier() === 'L') { + if( this.m_layerManager.getVisibility() == "visible") { + this.m_layerManager.setVisibility( "excluded"); + } else { + this.m_layerManager.setVisibility( "visible"); + } + } + }.bind(this) ); this.m_overlayWidget.addListener( "keypress", function() { console.log( "keypress", arguments ); From 889e32ad9e9f39e4183e5a413f0dddcb1734804f Mon Sep 17 00:00:00 2001 From: slovelan Date: Fri, 13 Nov 2015 09:14:31 -0700 Subject: [PATCH 28/37] Fix of image namespace issues. --- carta/cpp/CartaLib/IWcsGridRenderService.h | 2 +- carta/cpp/core/Data/Histogram/Histogram.cpp | 5 ++-- carta/cpp/core/Data/Image/Controller.cpp | 9 +++---- carta/cpp/core/Data/Image/Controller.h | 12 ++++++--- carta/cpp/core/Data/Image/ControllerData.cpp | 10 +++---- carta/cpp/core/Data/Image/ControllerData.h | 13 +++++---- carta/cpp/core/Data/Image/DataSource.cpp | 27 +++++++++---------- carta/cpp/core/Data/Image/DataSource.h | 22 ++++++++------- .../cpp/core/Data/Image/DrawSynchronizer.cpp | 2 +- carta/cpp/core/Data/Image/DrawSynchronizer.h | 9 ++++--- .../core/Data/Image/IPercentIntensityMap.h | 4 +-- carta/cpp/core/DummyGridRenderer.cpp | 2 +- carta/cpp/core/DummyGridRenderer.h | 2 +- carta/cpp/core/Hacks/ManagedLayerView.cpp | 2 +- carta/cpp/core/ImageRenderService.cpp | 2 +- carta/cpp/core/ImageSaveService.cpp | 3 ++- carta/cpp/core/ImageSaveService.h | 11 ++++---- carta/cpp/core/MainConfig.cpp | 9 ++++++- carta/cpp/core/MainConfig.h | 2 ++ carta/cpp/plugins/CasaImageLoader/CCImage.h | 4 +-- carta/cpp/plugins/Histogram/Histogram1.cpp | 5 ++-- .../cpp/plugins/Histogram/ImageHistogram.cpp | 19 +++++++------ carta/cpp/plugins/Histogram/ImageHistogram.h | 1 - .../WcsPlotter/AstWcsGridRenderService.h | 2 +- carta/cpp/plugins/qimage/QImagePlugin.cpp | 5 ++++ 25 files changed, 101 insertions(+), 83 deletions(-) diff --git a/carta/cpp/CartaLib/IWcsGridRenderService.h b/carta/cpp/CartaLib/IWcsGridRenderService.h index 2713966c..ccd5794f 100644 --- a/carta/cpp/CartaLib/IWcsGridRenderService.h +++ b/carta/cpp/CartaLib/IWcsGridRenderService.h @@ -57,7 +57,7 @@ class IWcsGridRenderService : public QObject /// set the input data, shold have at least 2 dimensions /// \note the data is not imporant, only meta data attached to this is important virtual void - setInputImage( Image::ImageInterface::SharedPtr image ) = 0; + setInputImage( Carta::Lib::Image::ImageInterface::SharedPtr image ) = 0; /// which part of the input image data to render (in casa coordinates) /// this always assumes we are doing axis1/axis2 grid diff --git a/carta/cpp/core/Data/Histogram/Histogram.cpp b/carta/cpp/core/Data/Histogram/Histogram.cpp index 6878fe72..3b4f091d 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.cpp +++ b/carta/cpp/core/Data/Histogram/Histogram.cpp @@ -1054,9 +1054,8 @@ void Histogram::refreshState() { } void Histogram::_refreshView(){ -// QImage * histogramImage = m_histogram->toImage(); -// m_view->resetImage( *histogramImage ); - m_view->resetImage( m_histogram->toImage() ); + QImage * histogramImage = m_histogram->toImage(); + m_view->resetImage( *histogramImage ); m_view->scheduleRedraw(); } diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 75319c08..7fd88fd2 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -1,14 +1,14 @@ #include "State/ObjectManager.h" #include "State/UtilState.h" -#include "Controller.h" +#include "Data/Image/Controller.h" #include "Data/Image/Grid/AxisMapper.h" #include "Data/Image/Grid/DataGrid.h" #include "Data/Image/Grid/GridControls.h" #include "Data/Image/Contour/ContourControls.h" #include "Data/Image/Contour/DataContours.h" +#include "ControllerData.h" #include "Data/Settings.h" #include "Data/DataLoader.h" -#include "ControllerData.h" #include "DataSource.h" #include "Data/Error/ErrorManager.h" @@ -470,10 +470,9 @@ double Controller::getPercentile( int frameLow, int frameHigh, double intensity return percentile; } - -std::vector> Controller::getDataSources(){ +std::vector< std::shared_ptr > Controller::getDataSources(){ //For right now, we are only going to do a histogram of a single image. - std::vector> images; + std::vector > images; int dataCount = m_datas.size(); if ( dataCount > 0 ){ int dataIndex = _getDataIndex(); diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index 8b0bf657..d6f2318b 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -20,15 +20,19 @@ class ImageView; class CoordinateFormatterInterface; -namespace NdArray { - class RawViewInterface; -} + namespace Carta { namespace Lib { namespace PixelPipeline { class CustomizablePixelPipeline; } + namespace Image { + class ImageInterface; + } + namespace NdArray { + class RawViewInterface; + } } } @@ -162,7 +166,7 @@ class Controller: public QObject, public Carta::State::CartaObject, */ bool getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; - std::vector> getDataSources(); + std::vector > getDataSources(); /** * Return the current axis frame. diff --git a/carta/cpp/core/Data/Image/ControllerData.cpp b/carta/cpp/core/Data/Image/ControllerData.cpp index 6aabea07..8639b348 100755 --- a/carta/cpp/core/Data/Image/ControllerData.cpp +++ b/carta/cpp/core/Data/Image/ControllerData.cpp @@ -228,8 +228,8 @@ QString ControllerData::_getFileName() const { return fileName; } -std::shared_ptr ControllerData::_getImage(){ - std::shared_ptr image; +std::shared_ptr ControllerData::_getImage(){ + std::shared_ptr image; if ( m_dataSource ){ image = m_dataSource->_getImage(); } @@ -478,7 +478,7 @@ void ControllerData::_render( const std::vector& frames, const Carta::Lib:: } gridService->setAxisDisplayInfo( axisInfo ); - std::shared_ptr rawData( m_dataSource->_getRawData( frames )); + std::shared_ptr rawData( m_dataSource->_getRawData( frames )); m_drawSync->setInput( rawData ); m_drawSync->setContours( m_dataContours ); @@ -527,7 +527,7 @@ QString ControllerData::_saveImage( const QString& saveName, double scale, m_saveService = new Carta::Core::ImageSaveService::ImageSaveService( saveName, pipeline ); - std::shared_ptr view( m_dataSource->_getRawData( frames )); + std::shared_ptr view( m_dataSource->_getRawData( frames )); if ( view != nullptr ){ QString viewId = m_dataSource->_getViewIdCurrent( frames ); m_saveService->setInputView( view, viewId ); @@ -689,7 +689,7 @@ void ControllerData::_setZoom( double zoomAmount){ -void ControllerData::_updateClips( std::shared_ptr& view, +void ControllerData::_updateClips( std::shared_ptr& view, double minClipPercentile, double maxClipPercentile, const std::vector& frames ){ if ( m_dataSource ){ m_dataSource->_updateClips( view, minClipPercentile, maxClipPercentile, frames ); diff --git a/carta/cpp/core/Data/Image/ControllerData.h b/carta/cpp/core/Data/Image/ControllerData.h index 06fcdbc0..7d56b56c 100755 --- a/carta/cpp/core/Data/Image/ControllerData.h +++ b/carta/cpp/core/Data/Image/ControllerData.h @@ -14,9 +14,7 @@ #include -namespace Image { - class ImageInterface; -} + class CoordinateFormatterInterface; @@ -27,6 +25,9 @@ namespace Lib { namespace PixelPipeline { class CustomizablePixelPipeline; } + namespace NdArray { + class RawViewInterface; + } } namespace Core { namespace ImageRenderService { @@ -37,6 +38,8 @@ namespace Core { } } + + namespace Data { class ColorState; @@ -154,7 +157,7 @@ private slots: /** * Returns the underlying image. */ - std::shared_ptr _getImage(); + std::shared_ptr _getImage(); /** * Returns the image's file name. @@ -393,7 +396,7 @@ private slots: */ void _viewResize( const QSize& newSize ); - void _updateClips( std::shared_ptr& view, + void _updateClips( std::shared_ptr& view, double minClipPercentile, double maxClipPercentile, const std::vector& frames ); /** diff --git a/carta/cpp/core/Data/Image/DataSource.cpp b/carta/cpp/core/Data/Image/DataSource.cpp index dc1b7e75..817e7e07 100755 --- a/carta/cpp/core/Data/Image/DataSource.cpp +++ b/carta/cpp/core/Data/Image/DataSource.cpp @@ -261,7 +261,7 @@ QString DataSource::_getPixelValue( double x, double y, const std::vector& int valX = (int)(round(x)); int valY = (int)(round(y)); if ( valX >= 0 && valX < m_image->dims()[m_axisIndexX] && valY >= 0 && valY < m_image->dims()[m_axisIndexY] ) { - NdArray::RawViewInterface* rawData = _getRawData( frames ); + Carta::Lib::NdArray::RawViewInterface* rawData = _getRawData( frames ); if ( rawData != nullptr ){ Carta::Lib::NdArray::TypedView view( rawData, false ); double val = view.get( { valX, valY } ); @@ -347,7 +347,7 @@ std::shared_ptr DataSource::_getRender bool DataSource::_getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const { bool intensityFound = false; int spectralIndex = _getAxisIndex( AxisInfo::KnownType::SPECTRAL ); - NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh, spectralIndex ); + Carta::Lib::NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh, spectralIndex ); if ( rawData != nullptr ){ Carta::Lib::NdArray::TypedView view( rawData, false ); // read in all values from the view into an array @@ -382,7 +382,7 @@ QColor DataSource::_getNanColor() const { double DataSource::_getPercentile( int frameLow, int frameHigh, double intensity ) const { double percentile = 0; int spectralIndex = _getAxisIndex( AxisInfo::KnownType::SPECTRAL); - NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh, spectralIndex ); + Carta::Lib::NdArray::RawViewInterface* rawData = _getRawData( frameLow, frameHigh, spectralIndex ); if ( rawData != nullptr ){ u_int64_t totalCount = 0; u_int64_t countBelow = 0; @@ -423,7 +423,7 @@ QString DataSource::_getPixelUnits() const { return units; } -NdArray::RawViewInterface* DataSource::_getRawData( int frameStart, int frameEnd, int axisIndex ) const { +Carta::Lib::NdArray::RawViewInterface* DataSource::_getRawData( int frameStart, int frameEnd, int axisIndex ) const { Carta::Lib::NdArray::RawViewInterface* rawData = nullptr; if ( m_image ){ int imageDim =m_image->dims().size(); @@ -481,8 +481,8 @@ int DataSource::_getQuantileCacheIndex( const std::vector& frames) const { return cacheIndex; } -std::shared_ptr DataSource::_getPermutedImage() const { - std::shared_ptr permuteImage(nullptr); +std::shared_ptr DataSource::_getPermutedImage() const { + std::shared_ptr permuteImage(nullptr); if ( m_image ){ //Build a vector showing the permute order. int imageDim = m_image->dims().size(); @@ -501,9 +501,8 @@ std::shared_ptr DataSource::_getPermutedImage() const { return permuteImage; } - -NdArray::RawViewInterface* DataSource::_getRawData( const std::vector frames ) const { - NdArray::RawViewInterface* rawData = nullptr; +Carta::Lib::NdArray::RawViewInterface* DataSource::_getRawData( const std::vector frames ) const { + Carta::Lib::NdArray::RawViewInterface* rawData = nullptr; std::vector mFrames = _fitFramesToImage( frames ); if ( m_permuteImage ){ int imageDim =m_permuteImage->dims().size(); @@ -593,7 +592,7 @@ void DataSource::_load(std::vector frames, bool recomputeClipsOnNewFrame, int frameSize = frames.size(); CARTA_ASSERT( frameSize == static_cast(AxisInfo::KnownType::OTHER)); std::vector mFrames = _fitFramesToImage( frames ); - std::shared_ptr view ( _getRawData( mFrames ) ); + std::shared_ptr view ( _getRawData( mFrames ) ); std::vector dimVector = view->dims(); //Update the clip values if ( recomputeClipsOnNewFrame ){ @@ -761,12 +760,12 @@ void DataSource::_setGamma( double gamma ){ } -void DataSource::_updateClips( std::shared_ptr& view, +void DataSource::_updateClips( std::shared_ptr& view, double minClipPercentile, double maxClipPercentile, const std::vector& frames ){ std::vector mFrames = _fitFramesToImage( frames ); int quantileIndex = _getQuantileCacheIndex( mFrames ); std::vector clips = m_quantileCache[ quantileIndex]; - NdArray::Double doubleView( view.get(), false ); + Carta::Lib::NdArray::Double doubleView( view.get(), false ); std::vector newClips = Carta::Core::Algorithms::quantiles2pixels( doubleView, {minClipPercentile, maxClipPercentile }); bool clipsChanged = false; @@ -792,9 +791,9 @@ void DataSource::_updateClips( std::shared_ptr& view, } } -std::shared_ptr DataSource::_updateRenderedView( const std::vector& frames ){ +std::shared_ptr DataSource::_updateRenderedView( const std::vector& frames ){ // get a view of the data using the slice description and make a shared pointer out of it - std::shared_ptr view( _getRawData( frames ) ); + std::shared_ptr view( _getRawData( frames ) ); // tell the render service to render this job QString renderId = _getViewIdCurrent( frames ); m_renderService-> setInputView( view, renderId/*, m_axisIndexX, m_axisIndexY*/ ); diff --git a/carta/cpp/core/Data/Image/DataSource.h b/carta/cpp/core/Data/Image/DataSource.h index 8154297a..e7550d64 100755 --- a/carta/cpp/core/Data/Image/DataSource.h +++ b/carta/cpp/core/Data/Image/DataSource.h @@ -21,14 +21,16 @@ namespace Lib { namespace PixelPipeline { class CustomizablePixelPipeline; } - namespace NdArray { - class RawViewInterface; - } - namespace Image { class ImageInterface; } + namespace NdArray { + class RawViewInterface; + } } + + + namespace Core { namespace ImageRenderService { class Service; @@ -284,16 +286,16 @@ Q_OBJECT * @param axisIndex - the axis for the frames or -1 for all axes. * @return the raw data or nullptr if there is none. */ - NdArray::RawViewInterface * _getRawData( int frameLow, int frameHigh, int axisIndex ) const; + Carta::Lib::NdArray::RawViewInterface * _getRawData( int frameLow, int frameHigh, int axisIndex ) const; /** * Returns the raw data for the current view. * @param frames - a list of current image frames. * @return the raw data for the current view or nullptr if there is none. */ - NdArray::RawViewInterface* _getRawData( const std::vector frames ) const; + Carta::Lib::NdArray::RawViewInterface* _getRawData( const std::vector frames ) const; - std::shared_ptr _getPermutedImage() const; + std::shared_ptr _getPermutedImage() const; //Returns an identifier for the current image slice being rendered. QString _getViewIdCurrent( const std::vector& frames ) const; @@ -424,7 +426,7 @@ Q_OBJECT * such as when different display axes have been selected. * @param frames - a list of current image frames. */ - std::shared_ptr _updateRenderedView( const std::vector& frames ); + std::shared_ptr _updateRenderedView( const std::vector& frames ); /** * Resize the view of the image. @@ -432,7 +434,7 @@ Q_OBJECT void _viewResize( const QSize& newSize ); - void _updateClips( std::shared_ptr& view, + void _updateClips( std::shared_ptr& view, double minClipPercentile, double maxClipPercentile, const std::vector& frames ); /** @@ -450,7 +452,7 @@ Q_OBJECT //Pointer to image interface. std::shared_ptr m_image; - std::shared_ptr m_permuteImage; + std::shared_ptr m_permuteImage; /// coordinate formatter std::shared_ptr m_coordinateFormatter; diff --git a/carta/cpp/core/Data/Image/DrawSynchronizer.cpp b/carta/cpp/core/Data/Image/DrawSynchronizer.cpp index 1c4c22d5..a60ae6e1 100644 --- a/carta/cpp/core/Data/Image/DrawSynchronizer.cpp +++ b/carta/cpp/core/Data/Image/DrawSynchronizer.cpp @@ -83,7 +83,7 @@ void DrawSynchronizer::_irsDone( QImage img, int64_t jobId ){ } } -void DrawSynchronizer::setInput( std::shared_ptr rawView ){ +void DrawSynchronizer::setInput( std::shared_ptr rawView ){ m_cec->setInput( rawView ); } diff --git a/carta/cpp/core/Data/Image/DrawSynchronizer.h b/carta/cpp/core/Data/Image/DrawSynchronizer.h index 5ce77f9d..68405598 100644 --- a/carta/cpp/core/Data/Image/DrawSynchronizer.h +++ b/carta/cpp/core/Data/Image/DrawSynchronizer.h @@ -5,9 +5,7 @@ #pragma once #include -namespace NdArray { - class RawViewInterface; -} + namespace Carta { namespace Lib { @@ -17,6 +15,9 @@ namespace Lib { namespace VectorGraphics { class VGList; } + namespace NdArray { + class RawViewInterface; + } } namespace Core { @@ -42,7 +43,7 @@ class DrawSynchronizer : public QObject { * Sets the data to be used in calculating contours. * @param rawView - the data for calculating contours. */ - void setInput( std::shared_ptr rawView ); + void setInput( std::shared_ptr rawView ); /** * Sets the contour set to be drawn. diff --git a/carta/cpp/core/Data/Image/IPercentIntensityMap.h b/carta/cpp/core/Data/Image/IPercentIntensityMap.h index 1d323eab..0aec1167 100755 --- a/carta/cpp/core/Data/Image/IPercentIntensityMap.h +++ b/carta/cpp/core/Data/Image/IPercentIntensityMap.h @@ -7,9 +7,7 @@ #include -namespace Image { -class ImageInterface; -} + namespace Carta { diff --git a/carta/cpp/core/DummyGridRenderer.cpp b/carta/cpp/core/DummyGridRenderer.cpp index 79ca025b..3c4c10dd 100644 --- a/carta/cpp/core/DummyGridRenderer.cpp +++ b/carta/cpp/core/DummyGridRenderer.cpp @@ -21,7 +21,7 @@ DummyGridRenderer::setAxisDisplayInfo( std::vector {} void -DummyGridRenderer::setInputImage( Image::ImageInterface::SharedPtr ) +DummyGridRenderer::setInputImage( Carta::Lib::Image::ImageInterface::SharedPtr ) { } void diff --git a/carta/cpp/core/DummyGridRenderer.h b/carta/cpp/core/DummyGridRenderer.h index d5b46a7a..1ce2b541 100644 --- a/carta/cpp/core/DummyGridRenderer.h +++ b/carta/cpp/core/DummyGridRenderer.h @@ -31,7 +31,7 @@ class DummyGridRenderer setAxisDisplayInfo( std::vector perms ) override; virtual void - setInputImage( Image::ImageInterface::SharedPtr ) override; + setInputImage( Carta::Lib::Image::ImageInterface::SharedPtr ) override; virtual void setImageRect( const QRectF & ) override; diff --git a/carta/cpp/core/Hacks/ManagedLayerView.cpp b/carta/cpp/core/Hacks/ManagedLayerView.cpp index 0a19cc9d..7eb80aa2 100644 --- a/carta/cpp/core/Hacks/ManagedLayerView.cpp +++ b/carta/cpp/core/Hacks/ManagedLayerView.cpp @@ -16,7 +16,7 @@ ManagedLayer::ManagedLayer(ManagedLayerViewInh * mlv) m_mlv-> p_addLayer( this); } -ManagedLayerViewInh::ManagedLayerViewInh(QString viewName, IConnector * connector, QObject * parent) +ManagedLayerViewInh::ManagedLayerViewInh(QString viewName, IConnector * connector, QObject * /*parent*/) { m_connector = connector; diff --git a/carta/cpp/core/ImageRenderService.cpp b/carta/cpp/core/ImageRenderService.cpp index e686ecc6..ae40e35d 100644 --- a/carta/cpp/core/ImageRenderService.cpp +++ b/carta/cpp/core/ImageRenderService.cpp @@ -216,7 +216,7 @@ Service::render( JobId jobId ) return m_lastSubmittedJobId; } -Service::Service( QObject * parent ) : QObject( parent ), +Service::Service( QObject * parent ) : Carta::Lib::IImageRenderService( parent ), m_defaultNan( true ), m_nanColor( 255, 0, 0 ) { diff --git a/carta/cpp/core/ImageSaveService.cpp b/carta/cpp/core/ImageSaveService.cpp index ae7e6832..9f24b1a6 100644 --- a/carta/cpp/core/ImageSaveService.cpp +++ b/carta/cpp/core/ImageSaveService.cpp @@ -38,7 +38,8 @@ void ImageSaveService::setOutputSize( QSize size ){ } -void ImageSaveService::setInputView( std::shared_ptr view, const QString& viewId ){ +void ImageSaveService::setInputView( std::shared_ptr view, + const QString& viewId ){ m_inputView = view; m_inputViewId = viewId; } diff --git a/carta/cpp/core/ImageSaveService.h b/carta/cpp/core/ImageSaveService.h index 25199d1b..8a894731 100644 --- a/carta/cpp/core/ImageSaveService.h +++ b/carta/cpp/core/ImageSaveService.h @@ -22,12 +22,13 @@ namespace Carta { namespace Image { class ImageInterface; } + namespace NdArray { + class RawViewInterface; + } } } -namespace NdArray { - class RawViewInterface; -} + namespace Carta{ namespace Core{ @@ -66,7 +67,7 @@ class ImageSaveService : public QObject /// data being displayed. /// \param view - the data /// \param viewId - an identifier for the data being displayed. - void setInputView( std::shared_ptr view, const QString& viewId ); + void setInputView( std::shared_ptr view, const QString& viewId ); /// specify zoom /// \param zoom how many screen pixels does a data pixel occupy on screen @@ -107,7 +108,7 @@ private slots: bool _prepareData(); /// Input data and id. - std::shared_ptr m_inputView; + std::shared_ptr m_inputView; QString m_inputViewId; /// Full path of the output image diff --git a/carta/cpp/core/MainConfig.cpp b/carta/cpp/core/MainConfig.cpp index ee2ce5ac..08e8a814 100644 --- a/carta/cpp/core/MainConfig.cpp +++ b/carta/cpp/core/MainConfig.cpp @@ -62,7 +62,8 @@ ParsedInfo parse(const QString & filePath) || raw == "t"); qDebug() << "Hacks enabled:" << info.m_hacksEnabled << raw; - info.m_hacksEnabled = json[ "hacksEnabled"].toBool(); + //info.m_hacksEnabled = json[ "hacksEnabled"].toBool(); + //qDebug() << "Hacks enabled="< + virtual std::shared_ptr getPermuted(const std::vector & indices ) override{ //Make sure the passed in indices make sense for this image. @@ -108,7 +108,7 @@ class CCImage //Finish the copy and create a CARTA image with permuted axes. casa::ImageUtilities::copyMiscellaneous( *newImage, *m_casaII ); - std::shared_ptr permuteImage = create( newImage); + std::shared_ptr permuteImage = create( newImage); return permuteImage; } diff --git a/carta/cpp/plugins/Histogram/Histogram1.cpp b/carta/cpp/plugins/Histogram/Histogram1.cpp index e6bca3a2..03a9c689 100755 --- a/carta/cpp/plugins/Histogram/Histogram1.cpp +++ b/carta/cpp/plugins/Histogram/Histogram1.cpp @@ -37,7 +37,7 @@ Histogram1::_getChannelBounds( double freqMin, double freqMax, const QString & u { std::pair < int, int > bounds( - 1, - 1 ); if ( ! m_cartaImage ) { - qWarning() << "No image available for channel bounds."; + //qWarning() << "No image available for channel bounds."; return bounds; } @@ -117,7 +117,7 @@ Histogram1::_getFrequencyBounds( int channelMin, int channelMax, const QString & { std::pair < double, double > bounds( - 1, - 1 ); if( ! m_cartaImage) { - qWarning() << "No image available for channel bounds."; + //qWarning() << "No image available for channel bounds."; return bounds; } @@ -173,7 +173,6 @@ Histogram1::handleHook( BaseHook & hookData ) = static_cast < Carta::Lib::Hooks::HistogramHook & > ( hookData ); const auto & images = hook.paramsPtr-> dataSource; - qWarning() << "images[0]" << images[0].get(); if ( images.size() == 0 ) { return false; } diff --git a/carta/cpp/plugins/Histogram/ImageHistogram.cpp b/carta/cpp/plugins/Histogram/ImageHistogram.cpp index 4489bcda..e87a472f 100755 --- a/carta/cpp/plugins/Histogram/ImageHistogram.cpp +++ b/carta/cpp/plugins/Histogram/ImageHistogram.cpp @@ -143,13 +143,14 @@ casa::LatticeHistograms* ImageHistogram::_filterByChannels( const casa::Im endPos[spectralIndex] = endIndex; casa::Slicer channelSlicer( startPos, endPos, stride, casa::Slicer::endIsLast ); - m_subImage.reset(new casa::SubImage (*image, channelSlicer )); - imageHistogram = new casa::LatticeHistograms( *m_subImage.get() ); + casa::ImageInterface* img = new casa::SubImage(*image, channelSlicer ); + imageHistogram = new casa::LatticeHistograms( *img ); } } } else { - imageHistogram = new casa::LatticeHistograms( *m_image ); + casa::ImageInterface* img = new casa::SubImage(*image ); + imageHistogram = new casa::LatticeHistograms( *img ); } return imageHistogram; } @@ -182,13 +183,11 @@ bool ImageHistogram::_reset(){ } else { //Make the histogram based on the region - m_subImage.reset(new casa::SubImage( *m_image, *m_region )); - if ( m_subImage.get() != NULL ){ - m_histogramMaker = _filterByChannels( m_subImage.get() ); - } - else { - success = false; - } + casa::SubImage* subImage = new casa::SubImage( *m_image, *m_region ); + m_histogramMaker = _filterByChannels( subImage ); + //Filter will make a new image based on this one so we can delete this sub + //image once filter is done. + delete subImage; } if ( success ){ success = compute(); diff --git a/carta/cpp/plugins/Histogram/ImageHistogram.h b/carta/cpp/plugins/Histogram/ImageHistogram.h index fc9b8e1d..8f6d2e85 100755 --- a/carta/cpp/plugins/Histogram/ImageHistogram.h +++ b/carta/cpp/plugins/Histogram/ImageHistogram.h @@ -73,7 +73,6 @@ class ImageHistogram : public IImageHistogram { vector m_yValues; casa::LatticeHistograms* m_histogramMaker; casa::ImageRegion* m_region; - std::shared_ptr > m_subImage; const int ALL_CHANNELS; const int ALL_INTENSITIES; const casa::ImageInterface* m_image; //Use diff --git a/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.h b/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.h index eb857431..c322f4be 100644 --- a/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.h +++ b/carta/cpp/plugins/WcsPlotter/AstWcsGridRenderService.h @@ -34,7 +34,7 @@ class AstWcsGridRenderService : public Carta::Lib::IWcsGridRenderService setAxisDisplayInfo( std::vector displayInfo ) override; virtual void - setInputImage( Image::ImageInterface::SharedPtr image ) override; + setInputImage( Carta::Lib::Image::ImageInterface::SharedPtr image ) override; virtual void setOutputSize( const QSize & size ) override; diff --git a/carta/cpp/plugins/qimage/QImagePlugin.cpp b/carta/cpp/plugins/qimage/QImagePlugin.cpp index 5ea8f8af..7ac0e14c 100644 --- a/carta/cpp/plugins/qimage/QImagePlugin.cpp +++ b/carta/cpp/plugins/qimage/QImagePlugin.cpp @@ -437,6 +437,11 @@ class QImageII return m_dims; } + virtual std::shared_ptr + getPermuted(const std::vector & /*indices*/){ + qFatal( "Not implemented"); + } + virtual bool hasMask() const override { From 645cf5a68234a0d27b154e1d6182d40d9d1accd5 Mon Sep 17 00:00:00 2001 From: Pavol Federl Date: Thu, 19 Nov 2015 15:01:34 -0700 Subject: [PATCH 29/37] layer manager with input & trivial GUI press ctrl-shift-L in "Managed layers hack" window to pop up the interface. then start tapping around to move one of the ellipses to change which layer will receive the tap, click on the layer you can have more than one layer receive the tap event --- carta/cpp/CartaLib/IRemoteVGView.cpp | 51 ++-- carta/cpp/CartaLib/IRemoteVGView.h | 183 ++++++++++-- .../core/Hacks/ContourEditorController.cpp | 5 + .../cpp/core/Hacks/ContourEditorController.h | 3 + carta/cpp/core/Hacks/HackViewer.cpp | 15 +- carta/cpp/core/Hacks/HackViewer.h | 9 +- carta/cpp/core/Hacks/ImageViewController.cpp | 3 + carta/cpp/core/Hacks/ImageViewController.h | 3 + carta/cpp/core/Hacks/LayeredViewDemo.cpp | 273 ++++++++++++++++++ carta/cpp/core/Hacks/LayeredViewDemo.h | 36 +++ carta/cpp/core/Hacks/MainModel.cpp | 3 + carta/cpp/core/Hacks/MainModel.h | 3 + carta/cpp/core/Hacks/ManagedLayerView.cpp | 122 +++++++- carta/cpp/core/Hacks/ManagedLayerView.h | 158 ++++++---- .../core/Hacks/WcsGridOptionsController.cpp | 3 + .../cpp/core/Hacks/WcsGridOptionsController.h | 3 + carta/cpp/core/SimpleRemoteVGView.cpp | 28 +- carta/cpp/core/SimpleRemoteVGView.h | 7 +- carta/cpp/core/core.pro | 6 +- carta/html5/common/IConnector.js | 2 +- .../skel/source/class/skel/hacks/Hacks.js | 7 +- .../class/skel/hacks/LayeredViewManager.js | 77 +++++ .../skel/source/class/skel/hacks/VGView.js | 90 ++++-- 23 files changed, 937 insertions(+), 153 deletions(-) create mode 100644 carta/cpp/core/Hacks/LayeredViewDemo.cpp create mode 100644 carta/cpp/core/Hacks/LayeredViewDemo.h diff --git a/carta/cpp/CartaLib/IRemoteVGView.cpp b/carta/cpp/CartaLib/IRemoteVGView.cpp index 22cbff9d..9da86de3 100644 --- a/carta/cpp/CartaLib/IRemoteVGView.cpp +++ b/carta/cpp/CartaLib/IRemoteVGView.cpp @@ -13,14 +13,14 @@ namespace Carta { namespace Lib { -LayeredRemoteVGView::SharedPtr -LayeredRemoteVGView::create( IConnector * connector, QString viewName, QObject * parent ) -{ - // return std::make_shared< LayeredRemoteVGView > ( connector, viewName, parent); - // we love C++ /sarcasm - LayeredRemoteVGView * lv = new LayeredRemoteVGView( connector, viewName, parent ); - return std::shared_ptr < LayeredRemoteVGView > ( lv ); -} +//LayeredRemoteVGView::SharedPtr +//LayeredRemoteVGView::create( IConnector * connector, QString viewName, QObject * parent ) +//{ +// // return std::make_shared< LayeredRemoteVGView > ( connector, viewName, parent); +// // we love C++ /sarcasm +// LayeredRemoteVGView * lv = new LayeredRemoteVGView( connector, viewName, parent ); +// return std::shared_ptr < LayeredRemoteVGView > ( lv ); +//} int LayeredRemoteVGView::nRasterLayers() @@ -77,6 +77,12 @@ LayeredRemoteVGView::setVGLayer( int layer, const VectorGraphics::VGList & vglis m_vgLayers[layer].vglist = vglist; } +QString +LayeredRemoteVGView::viewName() +{ + return m_vgView-> getRVGViewName(); +} + QSize LayeredRemoteVGView::getClientSize() { @@ -101,13 +107,17 @@ LayeredRemoteVGView::LayeredRemoteVGView( IConnector * connector, QObject * parent ) : QObject( parent ) { - m_vgView.reset( connector-> makeRemoteVGView( viewName )); + m_vgView.reset( connector-> makeRemoteVGView( viewName ) ); - /// forward signals directly - connect( m_vgView.get(), SIGNAL( repainted() ), this, SIGNAL( repainted() ) ); - // connect( m_vgView.get(), SIGNAL( sizeChanged() ), this, SIGNAL( sizeChanged() ) ); - connect( m_vgView.get(), & IRemoteVGView::sizeChanged, this, & Me::p_sizeChangedCB); + /// forward repainted signals directly + connect( m_vgView.get(), SIGNAL( repainted( qint64 ) ), this, SIGNAL( repainted( qint64 ) ) ); + /// forward input signals directly + connect( m_vgView.get(), SIGNAL( inputEvent( InputEvent ) ), this, + SIGNAL( inputEvent( InputEvent ) ) ); + + // connect( m_vgView.get(), SIGNAL( sizeChanged() ), this, SIGNAL( sizeChanged() ) ); + connect( m_vgView.get(), & IRemoteVGView::sizeChanged, this, & Me::p_sizeChangedCB ); m_timer = new QTimer( this ); m_timer-> setSingleShot( true ); @@ -119,9 +129,9 @@ void LayeredRemoteVGView::p_timerCB() { // figure out the size of the buffer (max of the raster sizes) - QSize size( 1, 1); + QSize size( 1, 1 ); for ( auto & layer : m_rasterLayers ) { - size = size.expandedTo( layer.qimg.size()); + size = size.expandedTo( layer.qimg.size() ); } QImage buff( size, QImage::Format_ARGB32_Premultiplied ); @@ -142,17 +152,22 @@ LayeredRemoteVGView::p_timerCB() VectorGraphics::VGComposer composer; for ( auto & vglayer : m_vgLayers ) { composer.append < VectorGraphics::Entries::Reset > (); + +// composer.append < VectorGraphics::Entries::Save > (); composer.appendList( vglayer.vglist ); + +// composer.append < VectorGraphics::Entries::Restore > (); } // render the combined vector graphics list - m_vgView-> setVG( composer.vgList()); + m_vgView-> setVG( composer.vgList() ); // schedule repaint (void) m_vgView-> scheduleRepaint( m_repaintId ); -} +} // p_timerCB -void LayeredRemoteVGView::p_sizeChangedCB() +void +LayeredRemoteVGView::p_sizeChangedCB() { emit sizeChanged(); } // timerCB diff --git a/carta/cpp/CartaLib/IRemoteVGView.h b/carta/cpp/CartaLib/IRemoteVGView.h index 18a5ea97..a9feb604 100644 --- a/carta/cpp/CartaLib/IRemoteVGView.h +++ b/carta/cpp/CartaLib/IRemoteVGView.h @@ -7,11 +7,113 @@ #include #include #include +#include +#include namespace Carta { namespace Lib { +namespace InputEvents +{ +class BaseEvent +{ +public: + + BaseEvent( const QJsonObject & json ) + { + m_json = json; + } + + const QJsonObject & + json() const { return m_json; } + +private: + + QJsonObject m_json; +}; + +class TouchEvent +{ +public: + + TouchEvent( const BaseEvent & baseEvent ) + { + + if ( baseEvent.json()["type"] != "tap" ) { return; } + + if ( ! baseEvent.json()["x"].isDouble() ) { return; } + if ( ! baseEvent.json()["y"].isDouble() ) { return; } + m_pos.rx() = baseEvent.json()["x"].toDouble(); + m_pos.ry() = baseEvent.json()["y"].toDouble(); + + m_valid = true; + } + + const QPointF & + pos() const { return m_pos; } + + bool + valid() const + { + return m_valid; + } + +private: + + bool m_valid = false; + QPointF m_pos; +}; +/* +template < class Type > +Type * +convertInputEvent( const BaseEvent & baseEvent ) +{ + try { + return new Type( baseEvent ); + } + catch ( ... ) { + return nullptr; + } +} +*/ + +static int +eventApiTest() +{ + QString s = "{ 'type': 'tap', 'x': 1, 'y': 2.3 }"; + qDebug() << "Testing events" << s.toLatin1(); + +// QJsonParseError error; +// QJsonDocument jdoc = QJsonDocument::fromJson(s.toLatin1(), & error); +// if( ! jdoc.isObject()) { +// qCritical() << "json error:" << error.errorString(); +// return 0; +// } +// QJsonObject jobj = jdoc.object(); + + QJsonObject jobj; + jobj["type"] = "tap"; + jobj["x"] = 3.1; + jobj["y"] = 7; + +// std::unique_ptr< BaseEvent > be = new BaseEvent( jobj); + BaseEvent be( jobj ); + + TouchEvent te( be ); + +// std::unique_ptr < TouchEvent > te( convertInputEvent < TouchEvent > ( be ) ); + if ( te.valid() ) { + qDebug() << "Touch event good" << te.pos(); + } + else { + qDebug() << "Touch event no good"; + } + return 1; +} // eventApiTest + +static auto xxx = eventApiTest(); +} /// brainstorming: /// - right now we really only care about users with a keyboard and mouse, but... @@ -61,9 +163,12 @@ namespace Lib /// queued connection, who frees up the pointer?). This could probably be resolved using /// smart (shared?) pointers, but we need to test it. /// -class InputEvent { +class InputEvent2 +{ public: - enum class Type { + + enum class Type + { Tap, // e.g. click, or one finger tap Press, // e.g. long one finger press, or middle mouse click? DoubleTap, // e.g. double click, or double tap of a single finger @@ -71,28 +176,50 @@ class InputEvent { Custom }; - InputEvent( Type t, QString ct = QString()) { m_type = t; m_customType = ct; } + InputEvent2( Type t, QString ct = QString(), QJsonObject json = QJsonObject() ) + { + m_type = t; + m_customType = ct; + m_json = json; + } + + Type + type() const { return m_type; } + + QString + custom() const { return m_customType; } + + const std::vector < QPointF > & + points() const { return m_points; } - Type type() const { return m_type; } - QString custom() const { return m_customType; } - const std::vector & points() const { return m_points; } - const std::vector & scalars() const { return m_scalars; } - const std::vector & integers() const { return m_integers; } - const std::vector & bools() const { return m_bools; } + const std::vector < float > & + scalars() const { return m_scalars; } + + const std::vector < int64_t > & + integers() const { return m_integers; } + + const std::vector < bool > & + bools() const { return m_bools; } /// if we really need to store something else in here? - const std::vector & extraBuff() const { return m_extraBuffer; } + const std::vector < char > & + extraBuff() const { return m_extraBuffer; } private: + Type m_type; QString m_customType; - std::vector m_points; - std::vector m_scalars; - std::vector m_integers; - std::vector m_bools; - std::vector m_extraBuffer; + std::vector < QPointF > m_points; + std::vector < float > m_scalars; + std::vector < int64_t > m_integers; + std::vector < bool > m_bools; + std::vector < char > m_extraBuffer; + + QJsonObject m_json; }; +typedef InputEvents::BaseEvent InputEvent; + /// /// \brief An API specification for rendering graphical views to be displayed by clients. /// @@ -140,15 +267,15 @@ class IRemoteVGView /// sets where VG rendered (client vs server) virtual void - setVGrenderedOnServer( bool flag) = 0; + setVGrenderedOnServer( bool flag ) = 0; /// returns true if VG is rendered on server, false if on client virtual bool isVGrenderedOnServer() = 0; /// tell UI which events we want to receive - virtual void - enableInputEvent( InputEvent::Type type, QString name = QString()) = 0; +// virtual void +// enableInputEvent( InputEvent::Type type, QString name = QString() ) = 0; public slots: @@ -169,7 +296,7 @@ public slots: /// emitted when client sends us a gesture void - inputEvent( InputEvent e); + inputEvent( InputEvent e ); }; class IQImageCombiner @@ -223,9 +350,12 @@ class LayeredRemoteVGView public: - static - LayeredRemoteVGView::SharedPtr - create( IConnector * connector, QString viewName, QObject * parent = nullptr ); +// static +// LayeredRemoteVGView::SharedPtr +// create( IConnector * connector, QString viewName, QObject * parent = nullptr ); + + /// constructor + LayeredRemoteVGView( IConnector * connector, QString viewName, QObject * parent = nullptr ); int nRasterLayers(); @@ -254,6 +384,8 @@ class LayeredRemoteVGView // LayerHandle // addVGLayer(); + QString viewName(); + QSize getClientSize(); @@ -274,10 +406,11 @@ public slots: void repainted( qint64 id ); -private: + /// emitted when client sends us a gesture + void + inputEvent( InputEvent e ); - /// private constructor to make sure we only create shared pointers of this - LayeredRemoteVGView( IConnector * connector, QString viewName, QObject * parent = nullptr ); +private: IRemoteVGView::UniquePtr m_vgView = nullptr; diff --git a/carta/cpp/core/Hacks/ContourEditorController.cpp b/carta/cpp/core/Hacks/ContourEditorController.cpp index 83a2f73f..8337b17d 100644 --- a/carta/cpp/core/Hacks/ContourEditorController.cpp +++ b/carta/cpp/core/Hacks/ContourEditorController.cpp @@ -25,6 +25,9 @@ s2vd( QString s, QString sep = " " ) } */ +namespace Carta +{ + namespace Hacks { ContourEditorController::ContourEditorController( QObject * parent, @@ -218,3 +221,5 @@ ContourEditorController::stdVarCB() // emit updated(); } } + +} diff --git a/carta/cpp/core/Hacks/ContourEditorController.h b/carta/cpp/core/Hacks/ContourEditorController.h index 0cbce1ed..37f2919c 100644 --- a/carta/cpp/core/Hacks/ContourEditorController.h +++ b/carta/cpp/core/Hacks/ContourEditorController.h @@ -19,6 +19,8 @@ #include "core/DefaultContourGeneratorService.h" #include +namespace Carta +{ namespace Hacks { namespace SS = Carta::Lib::SharedState; @@ -99,3 +101,4 @@ private slots: std::vector m_pens; }; } +} diff --git a/carta/cpp/core/Hacks/HackViewer.cpp b/carta/cpp/core/Hacks/HackViewer.cpp index d1a33685..049261c3 100644 --- a/carta/cpp/core/Hacks/HackViewer.cpp +++ b/carta/cpp/core/Hacks/HackViewer.cpp @@ -17,6 +17,8 @@ #include #include +namespace Carta +{ namespace Hacks { HackViewer::HackViewer( QString prefix ) : @@ -166,6 +168,7 @@ HackViewer::start() prefixedSetState( "knownSkyCS/count", "5" ); // layered view stuff + /* static Carta::Lib::IRemoteVGView::SharedPtr vgview( m_connector-> makeRemoteVGView( "vgview1" ) ); QImage img( 200, 100, QImage::Format_ARGB32_Premultiplied ); img.fill( 0xff000000 ); @@ -208,6 +211,9 @@ HackViewer::start() ); timer->setInterval( 200 ); timer->start(); +*/ + // managed layer demo + m_lvDemo.reset( new LayeredViewDemo( this)); // layered view 2 stuff auto ccl = [] ( int x, int y, int r, QColor color ) -> QImage { @@ -220,8 +226,11 @@ HackViewer::start() p.end(); return img; }; - static Carta::Lib::LayeredRemoteVGView::SharedPtr vgview2 = - Carta::Lib::LayeredRemoteVGView::create( m_connector, "vgview2", this ); +// static Carta::Lib::LayeredRemoteVGView::SharedPtr vgview2 = +// Carta::Lib::LayeredRemoteVGView::create( m_connector, "vgview2", this ); + static Carta::Lib::LayeredRemoteVGView::SharedPtr vgview2( + new Carta::Lib::LayeredRemoteVGView( m_connector, "vgview2", this )); + vgview2-> setRasterLayer( 0, ccl( 50, 50, 45, "white" ) ); auto acombgreen = std::make_shared < Carta::Lib::PixelMaskCombiner > (); acombgreen-> setAlpha( 1.0 ); @@ -351,6 +360,8 @@ HackViewer::prefixedAddStateCallback( QString path, IConnector::StateChangedCall return m_connector-> addStateCallback( m_statePrefix + "/" + path, cb ); } } +} + /// experiment, currently unused /// diff --git a/carta/cpp/core/Hacks/HackViewer.h b/carta/cpp/core/Hacks/HackViewer.h index 70a8d142..4c481da5 100644 --- a/carta/cpp/core/Hacks/HackViewer.h +++ b/carta/cpp/core/Hacks/HackViewer.h @@ -9,10 +9,13 @@ #include "../IPlatform.h" #include "CartaLib/IImage.h" #include "ImageViewController.h" +#include "LayeredViewDemo.h" #include #include +namespace Carta +{ namespace Hacks { @@ -62,8 +65,12 @@ protected slots: QString m_statePrefix; // view controller with the new render service - Hacks::ImageViewController::UniquePtr m_imageViewController; + Carta::Hacks::ImageViewController::UniquePtr m_imageViewController; std::vector < Carta::Lib::PixelPipeline::IColormapNamed::SharedPtr > m_allColormaps; + LayeredViewDemo::UniquePtr m_lvDemo = nullptr; + }; } +} + diff --git a/carta/cpp/core/Hacks/ImageViewController.cpp b/carta/cpp/core/Hacks/ImageViewController.cpp index 84bd19b5..124ddf19 100644 --- a/carta/cpp/core/Hacks/ImageViewController.cpp +++ b/carta/cpp/core/Hacks/ImageViewController.cpp @@ -35,6 +35,8 @@ s2vd( QString s, QString sep = " " ) } } +namespace Carta +{ namespace Hacks { ImageViewController::ImageViewController( QString statePrefix, QString viewName, QObject * parent ) @@ -608,3 +610,4 @@ ImageViewController::imageAndGridDoneSlot( m_connector-> refreshView( this ); } // imageAndGridDoneSlot } +} diff --git a/carta/cpp/core/Hacks/ImageViewController.h b/carta/cpp/core/Hacks/ImageViewController.h index 78c161c0..5cb5123a 100644 --- a/carta/cpp/core/Hacks/ImageViewController.h +++ b/carta/cpp/core/Hacks/ImageViewController.h @@ -21,6 +21,8 @@ #include #include +namespace Carta +{ namespace Hacks { @@ -351,3 +353,4 @@ private slots: ServiceSync::UniquePtr m_syncSvc = nullptr; }; } +} diff --git a/carta/cpp/core/Hacks/LayeredViewDemo.cpp b/carta/cpp/core/Hacks/LayeredViewDemo.cpp new file mode 100644 index 00000000..7f945b96 --- /dev/null +++ b/carta/cpp/core/Hacks/LayeredViewDemo.cpp @@ -0,0 +1,273 @@ +/** + * + **/ + +#include "LayeredViewDemo.h" +#include "core/Globals.h" +#include +#include +#include +#include +#include +#include +#include + +class EyesLayer : public Carta::Hacks::ManagedLayerBase +{ + Q_OBJECT + +public: + + EyesLayer( Carta::Hacks::ManagedLayerView * mlv, QString layerName ) + : Carta::Hacks::ManagedLayerBase( mlv, layerName ) + { + onResize( { 100, 100 } ); + } + +private: + + virtual void + onInputEvent( const Carta::Lib::InputEvent & ev ) override + { + qDebug() << "eyes layer received event" << ev.json()["type"]; + Carta::Lib::InputEvents::TouchEvent touch( ev ); + if ( touch.valid() ) { + qDebug() << "eyes touch:" << touch.pos(); + m_center = touch.pos(); + rerender(); + } + } + + virtual void + onResize( const QSize & size ) override + { + m_clientSize = size; + m_center = QPointF( size.width() / 2.0, size.height() / 2.0 ); + rerender(); + } + + void + rerender() + { + QImage img( m_clientSize, QImage::Format_ARGB32_Premultiplied ); + img.fill( QColor( 0, 128, 0, 128 ) ); + QPainter p( & img ); + p.setBrush( QColor( "red" ) ); + p.setPen( QPen( QColor( "blue" ), 5 ) ); + p.drawEllipse( m_center, + 20, 10 ); + p.end(); + setRaster( img ); + } + + QPointF m_center; + QSize m_clientSize = QSize( 100, 100 ); +}; + +class BouncyLayer : public Carta::Hacks::ManagedLayerBase +{ + Q_OBJECT + +public: + + BouncyLayer( Carta::Hacks::ManagedLayerView * mlv, QString layerName ) + : Carta::Hacks::ManagedLayerBase( mlv, layerName ) + { + m_timer.setInterval( 1000 ); + m_timer.start(); + connect( & m_timer, & QTimer::timeout, this, & BouncyLayer::timerCB ); + onResize( { 100, 100 } ); + } + +private: + + virtual void + onInputEvent( const Carta::Lib::InputEvent & ev ) override + { + qDebug() << "Bouncy layer received event" << ev.json()["type"]; + Carta::Lib::InputEvents::TouchEvent touch( ev ); + if ( touch.valid() ) { + qDebug() << "Bouncy touch:" << touch.pos(); + m_center = touch.pos(); + rerender(); + } + } + + virtual void + onResize( const QSize & size ) override + { + m_clientSize = size; + m_center = QPointF( size.width() - m_radius, size.height() - m_radius ); + rerender(); + } + + void + rerender() + { + QTime time = QTime::currentTime(); + QColor shadow = QColor( 0, 0, 0, 192 ); + QImage img( m_clientSize, QImage::Format_ARGB32_Premultiplied ); + img.fill( QColor( 128, 0, 0, 128 ) ); + QPainter p( & img ); + p.setRenderHint( QPainter::Antialiasing, true ); + p.setBrush( QColor( "black" ) ); + p.setPen( QPen( QColor( "white" ), 2 ) ); + p.drawEllipse( m_center, m_radius, m_radius ); + p.setPen( QPen( QColor( "white" ), 2 ) ); + double tickLenght = 5; + for ( int h = 0 ; h < 12 ; h++ ) { + double alpha = h * M_PI * 2 / 12; + p.drawLine( a2p( alpha, m_radius - tickLenght - ( h % 3 == 0 ? 2 : 0 ) ), + a2p( alpha, m_radius ) ); + } + double hour = time.hour() % 12 + time.minute() / 60.0 + time.second() / 3600.0; + p.setPen( QPen( QColor( "white" ), 4 ) ); + p.drawLine( m_center, a2p( hour / 12.0 * M_PI * 2, m_radius / 2 ) ); + double minute = time.minute() + time.second() / 60.0; + p.setPen( QPen( shadow, 4 ) ); + p.drawLine( m_center, a2p( minute / 60.0 * M_PI * 2, m_radius * 3 / 4 ) ); + p.setPen( QPen( QColor( "white" ), 3 ) ); + p.drawLine( m_center, a2p( minute / 60.0 * M_PI * 2, m_radius * 3 / 4 ) ); + p.setPen( QPen( shadow, 2 ) ); + p.drawLine( m_center, a2p( time.second() / 60.0 * M_PI * 2, m_radius - 2 ) ); + p.setPen( QPen( QColor( "yellow" ), 1 ) ); + p.drawLine( m_center, a2p( time.second() / 60.0 * M_PI * 2, m_radius - 2 ) ); + p.end(); + setRaster( img ); + } // rerender + + static QVector2D + a2v( double a ) + { + return QVector2D( sin( a ), - cos( a ) ); + } + + QPointF + a2p( double alpha, double radius ) + { + return ( QVector2D( m_center ) + radius * a2v( alpha ) ).toPointF(); + } + + void + timerCB() + { + rerender(); + } + + QPointF m_center; + QSize m_clientSize; + double m_radius = 50; + QTimer m_timer; +}; + +class LayeredViewController : public QObject +{ + Q_OBJECT + CLASS_BOILERPLATE( LayeredViewController ); + +public: + + LayeredViewController( Carta::Hacks::ManagedLayerView * mlv, + QObject * parent = nullptr ) : QObject( parent ) + { + m_mlv = mlv; + + connect( m_mlv, & Carta::Hacks::ManagedLayerView::layersUpdated, + this, & Me::mlvUpdatedCB ); + + using namespace std::placeholders; + + Globals::instance()->connector()->addCommandCallback( + QString( "/hacks/LayeredViewController/%1/setSelection").arg( m_mlv->viewName()), + std::bind( & Me::setSelectionCB, this, _1, _2, _3)); + + + mlvUpdatedCB(); + } + + virtual + ~LayeredViewController() { } + +private slots: + + void mlvUpdatedCB() { + // get the list of layers from the view + auto & layers = m_mlv-> layers(); + QJsonArray ja; + for( auto layer : layers) { + QJsonObject jo; + jo["name"] = layer-> layerName(); + jo["id"] = layer-> layerID(); + jo["input"] = layer-> hasInput(); + ja.push_back( jo); + } + QJsonObject job; + job["list"] = ja; + QJsonDocument doc(job); + QByteArray jstring = doc.toJson(); + qDebug() << "================ JSON ================"; + qDebug() << jstring; + qDebug() << "======================================"; + + Globals::instance()->connector()->setState( QString("/hacks/LayeredViewController/%1").arg( m_mlv-> viewName()), jstring); + + } + +private: + + QString setSelectionCB( const QString & cmd, const QString & params, const QString & sessionId){ + QJsonDocument doc = QJsonDocument::fromJson( params.toLatin1()); + if( ! doc.isArray()) return ""; + QJsonArray arr = doc.array(); + std::vector selection; + for( auto val : arr){ + qDebug() << "Selection" << val.toInt(); + selection.push_back( val.toInt()); + } + + m_mlv->setInputLayers( selection); + + return ""; + } + + Carta::Hacks::ManagedLayerView * m_mlv = nullptr; +}; + +// hack for declaring qobject classes inside .cpp instead of headers. This will force +// moc to process the .cpp file... +#include "LayeredViewDemo.moc" + +namespace Carta +{ +namespace Hacks +{ +struct LayeredViewDemo::Pimpl { + LayeredViewController::UniquePtr lvc = nullptr; + ManagedLayerView::UniquePtr mlv = nullptr; +}; + +LayeredViewDemo::LayeredViewDemo( QObject * parent ) : QObject( parent ) +{ + m_pimpl = new Pimpl; + + m_pimpl-> mlv.reset( new ManagedLayerView( "mlv1", Globals::instance()->connector(), this ) ); + + EyesLayer * eyes1 = new EyesLayer( m_pimpl-> mlv.get(), "ellipse1" ); + BouncyLayer * bouncy = new BouncyLayer( m_pimpl-> mlv.get(), "clock" ); + EyesLayer * eyes2 = new EyesLayer( m_pimpl-> mlv.get(), "ellipse2" ); + +// m_mlv-> setInputLayers( { eyes->layerID(), bouncy-> layerID() } + m_pimpl-> mlv-> setInputLayers( { eyes2->layerID() }); + + m_pimpl-> lvc.reset( new LayeredViewController( m_pimpl-> mlv.get() ) ); +} + +LayeredViewDemo::~LayeredViewDemo() +{ + if ( m_pimpl ) { + delete m_pimpl; + m_pimpl = nullptr; + } +} +} +} diff --git a/carta/cpp/core/Hacks/LayeredViewDemo.h b/carta/cpp/core/Hacks/LayeredViewDemo.h new file mode 100644 index 00000000..86ba4a55 --- /dev/null +++ b/carta/cpp/core/Hacks/LayeredViewDemo.h @@ -0,0 +1,36 @@ +/** + * + **/ + + +#pragma once + +#include "CartaLib/CartaLib.h" +#include "ManagedLayerView.h" +#include + +namespace Carta +{ +namespace Hacks +{ +class LayeredViewDemo : public QObject +{ + Q_OBJECT + CLASS_BOILERPLATE( LayeredViewDemo); + +public: + explicit LayeredViewDemo(QObject *parent = 0); + ~LayeredViewDemo(); + +signals: + +public slots: + +private: + struct Pimpl; + Pimpl * m_pimpl = nullptr; + +}; + +} +} diff --git a/carta/cpp/core/Hacks/MainModel.cpp b/carta/cpp/core/Hacks/MainModel.cpp index fa9b7922..199f26ef 100644 --- a/carta/cpp/core/Hacks/MainModel.cpp +++ b/carta/cpp/core/Hacks/MainModel.cpp @@ -4,6 +4,8 @@ #include "MainModel.h" +namespace Carta +{ namespace Hacks { namespace Model @@ -45,4 +47,5 @@ GlobalsH::GlobalsH() } } +} diff --git a/carta/cpp/core/Hacks/MainModel.h b/carta/cpp/core/Hacks/MainModel.h index 7f2c141f..f01a6659 100644 --- a/carta/cpp/core/Hacks/MainModel.h +++ b/carta/cpp/core/Hacks/MainModel.h @@ -25,6 +25,8 @@ public: \ private: \ PluginManager::SharedPtr m_pluginManager = nullptr +namespace Carta +{ namespace Hacks { namespace Model @@ -167,6 +169,7 @@ class GlobalsH // : public Model::PMMix < GlobalsH > GlobalsH(); }; } +} #undef PM_MIXIN diff --git a/carta/cpp/core/Hacks/ManagedLayerView.cpp b/carta/cpp/core/Hacks/ManagedLayerView.cpp index e82a32c6..f10e9bbd 100644 --- a/carta/cpp/core/Hacks/ManagedLayerView.cpp +++ b/carta/cpp/core/Hacks/ManagedLayerView.cpp @@ -2,44 +2,136 @@ * **/ - #include "ManagedLayerView.h" namespace Carta { namespace Hacks { - -ManagedLayer::ManagedLayer(ManagedLayerViewInh * mlv, QString name) +ManagedLayerBase::ManagedLayerBase( ManagedLayerView * mlv, QString name ) { m_mlv = mlv; m_layerName = name; - m_mlv-> p_addLayer( this); + m_id = m_mlv-> p_addLayer( this ); } -void ManagedLayer::setRaster(const QImage & image) +void +ManagedLayerBase::setRaster( const QImage & image ) { - + m_rasterBuff = image; + m_isRaster = true; + m_mlv-> scheduleRepaint(); } -ManagedLayerViewInh::ManagedLayerViewInh(QString viewName, IConnector * connector, QObject * parent) - : QObject( parent) +ManagedLayerView::ManagedLayerView( QString viewName, + IConnector * connector, + QObject * parent ) + : QObject( parent ) { m_connector = connector; - m_rvgv.reset( m_connector-> makeRemoteVGView( viewName)); + // make the low level layered view + m_lrv.reset( new Carta::Lib::LayeredRemoteVGView( m_connector, viewName, this ) ); + + // listen for resize events + connect( m_lrv.get(), & Carta::Lib::LayeredRemoteVGView::sizeChanged, + this, & Me::clientSizeChangedCB ); + + // listen for input events + connect( m_lrv.get(), & Carta::Lib::LayeredRemoteVGView::inputEvent, + this, & Me::inputEventCB ); +} + +void +ManagedLayerView::setInputLayers( const std::vector < ManagedLayerBase::ID > & list ) +{ + for ( auto layer : m_layers ) { + layer-> m_hasInput = std::find( list.begin(), list.end(), layer-> layerID() ) != list.end(); + } + + emit layersUpdated(); +} + +//IConnector * +//ManagedLayerView::connector() +//{ +// return m_connector; +//} +qint64 +ManagedLayerView::scheduleRepaint() +{ + m_lrv-> resetLayers(); + int rasterInd = 0; + int vgInd = 0; + for ( auto * l : m_layers ) { + if ( ! l-> isRaster() ) { + continue; + } + m_lrv-> setRasterLayer( rasterInd, l-> raster() ); + m_lrv-> setRasterLayerCombiner( rasterInd, l-> rasterCombiner() ); + rasterInd++; + } + for ( auto * l : m_layers ) { + if ( l-> isRaster() ) { + continue; + } + m_lrv-> setVGLayer( vgInd, l-> vgList() ); + vgInd++; + } + + return m_lrv-> scheduleRepaint(); +} // scheduleRepaint + +void +ManagedLayerView::clientSizeChangedCB() +{ + // tell all layers about the size changes + auto clientSize = m_lrv-> getClientSize(); + for ( auto * layer : m_layers ) { + layer-> onResize( clientSize ); + } } -IConnector *ManagedLayerViewInh::connector() +void +ManagedLayerView::inputEventCB( Lib::InputEvent e ) { - return m_connector; + qDebug() << "Received input event" << e.json()["type"].toString(); + + // now go through our list of input layers and invoke their input event callbacks + for ( auto layer : m_layers ) { + if ( layer-> hasInput() ) { + layer-> onInputEvent( e ); + } + } +} // inputEventCB + +ManagedLayerBase::ID +ManagedLayerView::p_addLayer( ManagedLayerBase * layer ) +{ + m_layers.push_back( layer ); + auto currId = m_nextId; + m_nextId++; + return currId; } -ManagedLayer::ID ManagedLayerViewInh::p_addLayer(ManagedLayer * layer) +/* +static int +apiTest() { - m_layers.push_back( layer); - return m_nextId ++ ; + // create a new managed layer view + IConnector * connector = nullptr; + auto mlv = new ManagedLayerViewInh( "mlv1", connector, nullptr ); + + // add some layers to the view + auto eyesLayer1 = new EyesLayer( mlv, "eyes1" ); + auto eyesLayer2 = new EyesLayer( mlv, "eyes two" ); + auto bouncyBallLayer1 = new BouncyLayer( mlv, "Bouncy" ); + + return 7; } +*/ -}} +//static int test = apiTest(); +} +} diff --git a/carta/cpp/core/Hacks/ManagedLayerView.h b/carta/cpp/core/Hacks/ManagedLayerView.h index 1bd36c08..95a3785f 100644 --- a/carta/cpp/core/Hacks/ManagedLayerView.h +++ b/carta/cpp/core/Hacks/ManagedLayerView.h @@ -7,26 +7,30 @@ #pragma once #include "CartaLib/CartaLib.h" -#include "IConnector.h" #include "CartaLib/IRemoteVGView.h" +#include "core/IConnector.h" #include namespace Carta { namespace Hacks { -class ManagedLayerViewInh; +class ManagedLayerView; -/// derive from this class to create custom managed layers -class ManagedLayer : public QObject +/// derive from this class to create custom layers +class ManagedLayerBase : public QObject { Q_OBJECT + CLASS_BOILERPLATE( ManagedLayerBase ); public: typedef int ID; - ManagedLayer( ManagedLayerViewInh *, QString name ); + /// + /// constructor - creates a link between the managed layered view and this layer + /// + ManagedLayerBase( ManagedLayerView * mlv, QString name ); virtual QString layerName() { return m_layerName; } @@ -35,29 +39,71 @@ class ManagedLayer : public QObject virtual void onResize( const QSize & size ) { Q_UNUSED( size ); } + /// reimplement this to react to input events + virtual void + onInputEvent( const Carta::Lib::InputEvent & event ) { Q_UNUSED( event ); } + /// call this to update the rendering of raster - void setRaster( const QImage & image); + void + setRaster( const QImage & image ); + + /// does the layer have input? + bool + hasInput() + { + return m_hasInput; + } - /// this returns a unique ID of the layer (wrt. to the manager) - ID layerID() { return m_id; } + /// returns a unique ID of the layer (wrt. to the manager) + ID + layerID() { return m_id; } virtual - ~ManagedLayer() { } + ~ManagedLayerBase() { } + +private: -protected: + /// pointer to the managed layer view + ManagedLayerView * m_mlv = nullptr; - ManagedLayerViewInh * m_mlv = nullptr; + /// user assigned layer name QString m_layerName; - ID m_id = -1; + + /// unique id assigned to us by managed layer view + ID m_id = - 1; + + // apis used be the manager to keep extra info on layers + bool + isRaster() { return m_isRaster; } + + bool m_isRaster = true; + QImage m_rasterBuff; + Carta::Lib::IQImageCombiner::SharedPtr m_rasterCombiner; + Carta::Lib::VectorGraphics::VGList m_vgListBuff; + + Carta::Lib::IQImageCombiner::SharedPtr + rasterCombiner() { return m_rasterCombiner; } + + const QImage & + raster() { return m_rasterBuff; } + + const Carta::Lib::VectorGraphics::VGList & + vgList() { return m_vgListBuff; } + + /// does the layer receive input? + bool m_hasInput = false; + + friend class ManagedLayerView; }; -class ManagedLayerViewInh : public QObject +class ManagedLayerView : public QObject { Q_OBJECT + CLASS_BOILERPLATE( ManagedLayerView ); public: - ManagedLayerViewInh( QString viewName, IConnector * connector, QObject * parent = nullptr ); + ManagedLayerView( QString viewName, IConnector * connector, QObject * parent = nullptr ); // template < class ILayer, typename ... Args > // std::shared_ptr < ILayer > @@ -68,47 +114,62 @@ class ManagedLayerViewInh : public QObject // return layer; // } + /// get the list of layers + const std::vector < ManagedLayerBase * > & + layers() const { return m_layers; } + + /// set which layers are to receive input events + void + setInputLayers( const std::vector < ManagedLayerBase::ID > & list = { } ); + virtual - ~ManagedLayerViewInh() { } + ~ManagedLayerView() { } - IConnector * - connector(); + QString viewName() { return m_lrv-> viewName(); } -private: +// IConnector * +// connector(); - virtual ManagedLayer::ID - p_addLayer( ManagedLayer * layer ); +signals: - friend class ManagedLayer; + /// emitted when the list of layers has been updated + void + layersUpdated(); - std::vector < ManagedLayer * > m_layers; - IConnector * m_connector = nullptr; - ManagedLayer::ID m_nextId = 0; +public slots: - Carta::Lib::IRemoteVGView::UniquePtr m_rvgv = nullptr; -}; + qint64 + scheduleRepaint(); -class EyesLayer : public ManagedLayer -{ - Q_OBJECT +private slots: -public: + // callback for client size changes from LayeredRemoteVGView + void + clientSizeChangedCB(); - EyesLayer( ManagedLayerViewInh * mlv, QString layerName ) - : ManagedLayer( mlv, layerName ) - { } + // callback for input events + void + inputEventCB( Carta::Lib::InputEvent e ); private: -}; -class BouncyLayer : public ManagedLayer -{ - Q_OBJECT + virtual ManagedLayerBase::ID + p_addLayer( ManagedLayerBase * layer ); -public: + friend class ManagedLayerBase; + + IConnector * m_connector = nullptr; + ManagedLayerBase::ID m_nextId = 0; + + // we use layered remote view to do the rendering + Carta::Lib::LayeredRemoteVGView::SharedPtr m_lrv = nullptr; + + // here we keep track of all our layers, and since we don't own the layers + // we keep raw pointers + std::vector < ManagedLayerBase * > m_layers; - BouncyLayer( ManagedLayerViewInh * mlv, QString layerName ) - : ManagedLayer( mlv, layerName ) { } + // which layers have input +// std::vector < ManagedLayerBase::ID > m_inputLayers; }; //std::shared_ptr < ManagedLayerViewInh > @@ -137,22 +198,5 @@ apiTest() static auto foo = apiTest(); */ - -static int -apiTest() -{ - // create a new managed layer view - IConnector * connector = nullptr; - auto mlv = new ManagedLayerViewInh( "mlv1", connector, nullptr ); - - // add some layers to the view - auto eyesLayer1 = new EyesLayer( mlv, "eyes1" ); - auto eyesLayer2 = new EyesLayer( mlv, "eyes two" ); - auto bouncyBallLayer1 = new BouncyLayer( mlv, "Bouncy" ); - - return 7; -} - -//static int test = apiTest(); } } diff --git a/carta/cpp/core/Hacks/WcsGridOptionsController.cpp b/carta/cpp/core/Hacks/WcsGridOptionsController.cpp index 59feacc0..7a3bc18a 100644 --- a/carta/cpp/core/Hacks/WcsGridOptionsController.cpp +++ b/carta/cpp/core/Hacks/WcsGridOptionsController.cpp @@ -6,6 +6,8 @@ #include "core/Globals.h" #include +namespace Carta +{ namespace Hacks { WcsGridOptionsController::WcsGridOptionsController( @@ -153,3 +155,4 @@ void WcsGridOptionsController::stdVarCB() } } +} diff --git a/carta/cpp/core/Hacks/WcsGridOptionsController.h b/carta/cpp/core/Hacks/WcsGridOptionsController.h index c32a5692..229da2da 100644 --- a/carta/cpp/core/Hacks/WcsGridOptionsController.h +++ b/carta/cpp/core/Hacks/WcsGridOptionsController.h @@ -13,6 +13,8 @@ #include "core/Hacks/SharedState.h" #include +namespace Carta +{ namespace Hacks { namespace SS = Carta::Lib::SharedState; @@ -68,3 +70,4 @@ public slots: }; } +} diff --git a/carta/cpp/core/SimpleRemoteVGView.cpp b/carta/cpp/core/SimpleRemoteVGView.cpp index 0a8e2b71..b48cc9af 100644 --- a/carta/cpp/core/SimpleRemoteVGView.cpp +++ b/carta/cpp/core/SimpleRemoteVGView.cpp @@ -5,6 +5,7 @@ #include "SimpleRemoteVGView.h" #include "IConnector.h" #include +#include namespace Carta { @@ -60,11 +61,11 @@ bool SimpleRemoteVGView::isVGrenderedOnServer() return true; } -void SimpleRemoteVGView::enableInputEvent( Carta::Lib::InputEvent::Type type, QString name) -{ - Q_UNUSED( type); - Q_UNUSED( name); -} +//void SimpleRemoteVGView::enableInputEvent( Carta::Lib::InputEvent::Type type, QString name) +//{ +// Q_UNUSED( type); +// Q_UNUSED( name); +//} qint64 SimpleRemoteVGView::scheduleRepaint( qint64 id ) @@ -95,6 +96,12 @@ SimpleRemoteVGView::SimpleRemoteVGView( QObject * parent, m_raster.fill( 0xff000000 ); m_connector-> registerView( this ); + + using namespace std::placeholders; + + m_connector->addCommandCallback( + QString( "vgview/inputEvent/%1").arg(viewName), + std::bind( & Me::inputEventCB, this, _1, _2, _3)); } void @@ -142,5 +149,16 @@ void SimpleRemoteVGView::viewRefreshed(qint64 id) { Q_UNUSED( id); } + +QString SimpleRemoteVGView::inputEventCB(const QString & cmd, const QString & params, const QString & sessionId) +{ + Q_UNUSED( cmd); + Q_UNUSED( sessionId); + qDebug() << "Input event[" << this->m_viewName << "]:" << params; + +// emit inputEvent( Carta::Lib::InputEvent( Carta::Lib::InputEvent::Type::Custom, "tap")); + emit inputEvent( Carta::Lib::InputEvent( QJsonDocument::fromJson( params.toLatin1()).object())); + return QString(); +} } } diff --git a/carta/cpp/core/SimpleRemoteVGView.h b/carta/cpp/core/SimpleRemoteVGView.h index e595e4f7..818f5e1f 100644 --- a/carta/cpp/core/SimpleRemoteVGView.h +++ b/carta/cpp/core/SimpleRemoteVGView.h @@ -54,8 +54,8 @@ class SimpleRemoteVGView virtual bool isVGrenderedOnServer() override; - virtual void - enableInputEvent( Carta::Lib::InputEvent::Type type, QString name = QString()) override; +// virtual void +// enableInputEvent( Carta::Lib::InputEvent::Type type, QString name = QString()) override; public slots: @@ -103,6 +103,9 @@ public slots: virtual void viewRefreshed( qint64 id) override ; + // for now this is how we handle input events... same for desktop and server + QString inputEventCB( const QString & cmd, const QString & params, const QString & sessionId); + }; } } diff --git a/carta/cpp/core/core.pro b/carta/cpp/core/core.pro index 729a075d..ba10c606 100755 --- a/carta/cpp/core/core.pro +++ b/carta/cpp/core/core.pro @@ -96,7 +96,8 @@ HEADERS += \ DummyGridRenderer.h \ coreMain.h \ SimpleRemoteVGView.h \ - Hacks/ManagedLayerView.h + Hacks/ManagedLayerView.h \ + Hacks/LayeredViewDemo.h SOURCES += \ Viewer.cpp \ @@ -177,7 +178,8 @@ SOURCES += \ DummyGridRenderer.cpp \ coreMain.cpp \ SimpleRemoteVGView.cpp \ - Hacks/ManagedLayerView.cpp + Hacks/ManagedLayerView.cpp \ + Hacks/LayeredViewDemo.cpp #message( "common PWD=$$PWD") diff --git a/carta/html5/common/IConnector.js b/carta/html5/common/IConnector.js index f67ea86c..51c9fd28 100644 --- a/carta/html5/common/IConnector.js +++ b/carta/html5/common/IConnector.js @@ -169,7 +169,7 @@ SharedVar.destroy = function() {}; /** * Send a command to the application. * @param cmd - * @param params + * @param params stringified parameters * @param callback (optional) * * callback has signature: diff --git a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js index efad336c..e786e9aa 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/Hacks.js +++ b/carta/html5/common/skel/source/class/skel/hacks/Hacks.js @@ -104,7 +104,7 @@ qx.Class.define("skel.hacks.Hacks", { // ================================================================================== // VGview hack window // ================================================================================== - var vgWin = new qx.ui.window.Window( "VGhack" ); + var vgWin = new qx.ui.window.Window( "Managed layers hack" ); vgWin.setWidth( 600 ); vgWin.setHeight( 400 ); vgWin.setShowMinimize( false ); @@ -113,7 +113,10 @@ qx.Class.define("skel.hacks.Hacks", { vgWin.setLayout( new qx.ui.layout.VBox(5) ); //vgWin.add( new skel.hacks.HackView( "vgview1"), { flex: 1 }); //vgWin.add( new skel.boundWidgets.View.View( "vgview1"), { flex: 1 }); - vgWin.add( new skel.hacks.LayeredViewHack( "vgview1"), { flex: 1 }); + //vgWin.add( new skel.hacks.LayeredViewHack( "vgview1"), { flex: 1 }); + var vgview = new skel.hacks.VGView( "mlv1"); + vgview.installDefaultInputHandler( vgview.INPUT_ALL_BUILTINS); + vgWin.add( vgview, { flex: 1 }); this.m_app.getRoot().add( vgWin, {left: 150, top: 120} ); vgWin.open(); diff --git a/carta/html5/common/skel/source/class/skel/hacks/LayeredViewManager.js b/carta/html5/common/skel/source/class/skel/hacks/LayeredViewManager.js index d91de5c4..a3831cd8 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/LayeredViewManager.js +++ b/carta/html5/common/skel/source/class/skel/hacks/LayeredViewManager.js @@ -30,6 +30,20 @@ qx.Class.define( "skel.hacks.LayeredViewManager", { this.setLayout( new qx.ui.layout.VBox( 5 ) ); // create a list widget this.m_listWidget = new qx.ui.form.List(); + this.m_listWidget.setSelectionMode( "multi"); + + //var rawData = []; + //for (var i = 0; i < 3; i++) { + // rawData[i] = "Item No " + i; + //} + //this.m_model = qx.data.marshal.Json.createModel(rawData); + // + //this.m_listWidget = new qx.ui.list.List(this.m_model); + + for (var i = 0; i < 3; i++) { + this.m_listWidget.add( new qx.ui.form.ListItem( "item" + i)); + } + this.add( this.m_listWidget, {flex: 1} ); // create buttons @@ -85,6 +99,13 @@ qx.Class.define( "skel.hacks.LayeredViewManager", { // update the slider label this._sliderCB(); + + // listen for state updates + this.m_sharedVar = this.m_connector.getSharedVar( "/hacks/LayeredViewController/" + this.m_viewName); + this.m_sharedVar.addCB(this._sharedVarCB.bind(this)); + this._sharedVarCB(this.m_sharedVar.get()); + + this.m_listWidget.addListener( "changeSelection", this._listSelectionCB.bind(this)); }, members: { @@ -101,6 +122,46 @@ qx.Class.define( "skel.hacks.LayeredViewManager", { return this.m_viewName; }, + _sharedVarCB : function(val) + { + this.m_insideSharedVar = true; + try { + console.log( "Layers:", val); + var obj = JSON.parse( val); + console.log( "Layers obj:", obj); + + // if we didn't receive a valid state, make a default one + if( ! obj) obj = { list: []}; + + var list = obj.list; + + this.m_listWidget.removeAll(); + var selectedItems = []; + list.forEach( function(entry) { + var item = new qx.ui.form.ListItem( entry.name); + if( entry.input) selectedItems.push( item); + item.setUserData( "layerID", entry.id); + this.m_listWidget.add( item); + }.bind(this)); + this.m_listWidget.setSelection( selectedItems); + + //this.m_model = qx.data.marshal.Json.createModel(names); + //this.m_listWidget.setModel( this.m_model); + // + //this.m_listWidget.resetSelection(); + //var selection = this.m_listWidget.getSelection(); + //list.forEach( function(entry, id) { + // if( entry.input) selection.push( this.m_model.getItem(id)); + //}.bind(this)); + // + //this.m_listWidget.setSelection(selection); + } catch(e) { + + } + this.m_insideSharedVar = false; + + }, + _sliderCB: function() { var num = "" + this.m_alphaSlider.getValue(); while( num.length < 3) { num = " " + num; } @@ -126,6 +187,22 @@ qx.Class.define( "skel.hacks.LayeredViewManager", { this.m_blueToggle.setValue( false); } + }, + + _listSelectionCB : function ( e) + { + if( this.m_insideSharedVar) return; + + console.log( "...", this.m_listWidget.getSelection() ); + var selected = []; + this.m_listWidget.getSelection().forEach( function(item){ + console.log( "Selected:" + item.getUserData("layerID")); + selected.push( item.getUserData("layerID")); + }.bind(this)); + console.log( "selected layers:", selected); + + this.m_connector.sendCommand( "/hacks/LayeredViewController/" + this.m_viewName + "/setSelection", JSON.stringify( selected)); + } } diff --git a/carta/html5/common/skel/source/class/skel/hacks/VGView.js b/carta/html5/common/skel/source/class/skel/hacks/VGView.js index 26281994..df60c55c 100644 --- a/carta/html5/common/skel/source/class/skel/hacks/VGView.js +++ b/carta/html5/common/skel/source/class/skel/hacks/VGView.js @@ -13,6 +13,14 @@ qx.Class.define( "skel.hacks.VGView", { extend: qx.ui.container.Composite, + statics: + { + INPUT_ALL_BUILTINS: "all", + INPUT_TAP: "tap", + INPUT_DRAG: "drag", + INPUT_HOVER: "hover" + }, + events: { "viewRefreshed" : "qx.event.type.Data" @@ -34,11 +42,6 @@ qx.Class.define( "skel.hacks.VGView", { this.m_overlayWidget = new qx.ui.core.Widget(); this.add( this.m_overlayWidget, {edge: 0} ); - this.m_overlayWidget.addListener( "click", function() - { - console.log( "Click" ); - } ); - // create a settings UI layer this.m_settingsLayer = this._createSettingsUI(); this.add( this.m_settingsLayer, {top: 10, right: 10} ); @@ -117,6 +120,65 @@ qx.Class.define( "skel.hacks.VGView", { return this.m_viewWidget; }, + setQuality: function( quality) { + this.m_viewWidget.setQuality( quality); + var qtext = "" + quality; + if( quality === 0) { + qtext += " (lowest JPG)"; + } + if( quality === 100) { + qtext += " (highest JPG)"; + } + if( quality === 101) { + qtext += " (PNG)"; + } + if( quality === 102) { + qtext += " (MPEG*)"; + } + this.m_qualityLabel.setValue( "Quality: " + qtext); + }, + + /** + * Installs built in handler[s]. + * @param which which handlers to install + * + * Example for use: installDefaultInputHandler([ INPUT_TAP, INPUT_HOVER ]) + */ + installDefaultInputHandler: function( list) + { + if( ! qx.lang.Type.isArray( which ) ) { + list = [list]; + } + for( var which in list ) { + which = list[which]; + var handled = false; + if( which === this.INPUT_TAP || which === this.INPUT_ALL_BUILTINS) { + console.log( "Installing tap handler on view", this.m_viewWidget.viewName()) + handled = true; + this.overlayWidget().addListener( "click", function( e ) + { + var box = this.overlayWidget().getContentLocation(); + var mouseX = e.getDocumentLeft() - box.left + var mouseY = e.getDocumentTop() - box.top + this.sendInputEvent( {type: "tap", x: mouseX, y: mouseY} ); + }.bind( this ) ); + } + if( ! handled) { + console.warn( "Don't understand built in event", which); + } + } + }, + + /** + * Send an input event to the server side. + * @param e + */ + sendInputEvent: function(e) { + console.log( "Sending input event", e); + var params = JSON.stringify( e); + this.m_connector.sendCommand( "vgview/inputEvent/" + this.m_viewWidget.viewName(), params); + }, + _rasterViewRefreshCB: function() { @@ -191,24 +253,6 @@ qx.Class.define( "skel.hacks.VGView", { return settings; }, - setQuality: function( quality) { - this.m_viewWidget.setQuality( quality); - var qtext = "" + quality; - if( quality === 0) { - qtext += " (lowest JPG)"; - } - if( quality === 100) { - qtext += " (highest JPG)"; - } - if( quality === 101) { - qtext += " (PNG)"; - } - if( quality === 102) { - qtext += " (MPEG*)"; - } - this.m_qualityLabel.setValue( "Quality: " + qtext); - }, - _qualitySliderCB: function(e) { this.setQuality( e.getData()); } From 59ad1d6bde5f1cdeafa9d78d7adf56d2dac273cf Mon Sep 17 00:00:00 2001 From: slovelan Date: Fri, 20 Nov 2015 08:59:24 -0700 Subject: [PATCH 30/37] Layered View work. --- carta/cpp/core/Data/DataLoader.cpp | 4 + carta/cpp/core/Data/DataLoader.h | 8 + .../Data/Image/Contour/ContourControls.cpp | 6 +- carta/cpp/core/Data/Image/Controller.cpp | 778 +++++++++++------- carta/cpp/core/Data/Image/Controller.h | 202 +++-- carta/cpp/core/Data/Image/ControllerData.cpp | 319 ++++--- carta/cpp/core/Data/Image/ControllerData.h | 242 +++--- carta/cpp/core/MainConfig.cpp | 129 +-- carta/cpp/core/MainConfig.h | 31 + carta/cpp/core/State/ObjectManager.cpp | 5 + carta/cpp/core/State/ObjectManager.h | 12 + carta/cpp/desktop/MainWindow.cpp | 4 +- .../skel/Command/Data/CommandDataClose.js | 2 +- .../skel/Command/Data/CommandDataHide.js | 2 +- .../skel/Command/Data/CommandDataShow.js | 2 +- .../class/skel/boundWidgets/View/View.js | 24 +- .../class/skel/widgets/Colormap/ColorScale.js | 30 +- .../skel/widgets/Colormap/PageColorMap.js | 4 +- .../class/skel/widgets/CustomUI/TextSlider.js | 12 +- .../skel/widgets/Image/Stack/DragDropList.js | 8 +- .../skel/widgets/Image/Stack/MaskControls.js | 107 +++ .../widgets/Image/Stack/MaskControlsColor.js | 322 ++++++++ .../skel/widgets/Image/Stack/StackControls.js | 47 +- 23 files changed, 1595 insertions(+), 705 deletions(-) create mode 100755 carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js create mode 100755 carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js diff --git a/carta/cpp/core/Data/DataLoader.cpp b/carta/cpp/core/Data/DataLoader.cpp index ff56faec..ff8cb4db 100644 --- a/carta/cpp/core/Data/DataLoader.cpp +++ b/carta/cpp/core/Data/DataLoader.cpp @@ -106,6 +106,10 @@ QStringList DataLoader::getShortNames( const QStringList& longNames ) const { return shortNames; } +QString DataLoader::getLongName( const QString& shortName, const QString& sessionId ) const { + return getRootDir( sessionId) + QDir::separator() + shortName; +} + void DataLoader::_initCallbacks(){ //Callback for returning a list of data files that can be loaded. diff --git a/carta/cpp/core/Data/DataLoader.h b/carta/cpp/core/Data/DataLoader.h index 9c2be1b4..faab35a7 100644 --- a/carta/cpp/core/Data/DataLoader.h +++ b/carta/cpp/core/Data/DataLoader.h @@ -37,6 +37,14 @@ class DataLoader : public Carta::State::CartaObject { */ QString getFile( const QString& fakePath, const QString& sessionId ) const; + /** + * Return the absolute path of the file with the root directories stripped off. + * @param sessionId - an identifier for the user's session. + * @param shortName - a path with the top level directory stripped off. + * @return - the full path to the file. + */ + QString getLongName( const QString& shortName, const QString& sessionId ) const; + /** * Return the top level directory for the data file search. * @param sessionId - an identifier for the user's session. diff --git a/carta/cpp/core/Data/Image/Contour/ContourControls.cpp b/carta/cpp/core/Data/Image/Contour/ContourControls.cpp index 8924b017..4bd57f82 100644 --- a/carta/cpp/core/Data/Image/Contour/ContourControls.cpp +++ b/carta/cpp/core/Data/Image/Contour/ContourControls.cpp @@ -392,9 +392,9 @@ void ContourControls::_initializeCallbacks(){ if ( validRed && validGreen && validBlue ){ QString setName = dataValues[CONTOUR_SET_NAME ]; QString levelStr = dataValues[LEVEL_LIST]; - bool validLevels = false; - std::vector levels = Util::string2VectorDouble( levelStr, &validLevels, LEVEL_SEPARATOR ); - if ( validLevels ){ + bool levelError = false; + std::vector levels = Util::string2VectorDouble( levelStr, &levelError, LEVEL_SEPARATOR ); + if ( !levelError ){ QStringList errorList = setColor( setName, levels, red, green, blue ); result = errorList.join(","); } diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 7fd88fd2..015d22cb 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -18,8 +18,10 @@ #include "Data/Util.h" #include "ImageView.h" #include "CartaLib/IImage.h" +#include "CartaLib/IRemoteVGView.h" #include "CartaLib/PixelPipeline/CustomizablePixelPipeline.h" + #include #include #include @@ -56,6 +58,7 @@ const QString Controller::ZOOM = "zoom"; const QString Controller::REGIONS = "regions"; const QString Controller::PLUGIN_NAME = "CasaImageLoader"; const QString Controller::STACK_SELECT_AUTO = "stackAutoSelect"; +const QString Controller::VIEW = "view"; const QString Controller::CLASS_NAME = "Controller"; bool Controller::m_registered = @@ -71,9 +74,9 @@ Controller::Controller( const QString& path, const QString& id ) : m_selectImage(nullptr), m_view(nullptr), m_stateData( UtilState::getLookup(path, StateInterface::STATE_DATA )), - m_stateMouse(UtilState::getLookup(path, ImageView::VIEW)), - m_viewSize( 400, 400){ - m_view.reset( new ImageView( path, QColor("pink"), QImage(), &m_stateMouse)); + m_stateMouse(UtilState::getLookup(path, VIEW)){ + QString viewName = Carta::State::UtilState::getLookup( path, VIEW); + m_view = makeRemoteView( viewName ); m_reloadFrameQueued = false; m_repaintFrameQueued = false; @@ -81,9 +84,8 @@ Controller::Controller( const QString& path, const QString& id ) : _initializeSelections(); _initializeState(); - registerView(m_view.get()); - connect( m_view.get(), SIGNAL(resize(const QSize&)), this, SLOT(_viewResize(const QSize&))); + connect( m_view.get(), SIGNAL(sizeChanged()), this, SLOT(_viewResize()) ); Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); GridControls* gridObj = objMan->createObject(); @@ -132,11 +134,11 @@ bool Controller::addData(const QString& fileName) { //Contour controls is in charge of setting the UI for the contours. m_contourControls->_setDrawContours( contourPtr ); targetIndex = m_datas.size(); - connect( targetSource, SIGNAL(renderingDone(QImage)), this, SLOT(_renderingDone(QImage))); + connect( targetSource, SIGNAL(renderingDone()), this, SLOT(_scheduleFrameRepaint())); connect( targetSource, & ControllerData::saveImageResult, this, & Controller::saveImageResultCB ); m_datas.append(std::shared_ptr(targetSource)); - targetSource->_viewResize( m_viewSize ); + targetSource->_viewResize( m_view->getClientSize() ); //Colormap targetSource->_setColorMapGlobal( m_stateColor ); @@ -244,14 +246,15 @@ void Controller::_clearStatistics(){ QString Controller::closeImage( const QString& name ){ int targetIndex = -1; QString result; + DataLoader* dataLoader = Util::findSingletonObject(); + QString longName = dataLoader->getLongName( name, ""); int dataCount = m_datas.size(); for ( int i = 0; i < dataCount; i++ ){ - if ( m_datas[i]->_isMatch( name )){ + if ( m_datas[i]->_isMatch( longName )){ targetIndex = i; break; } } - if ( targetIndex >= 0 ){ _removeData( targetIndex ); emit dataChanged( this ); @@ -266,34 +269,19 @@ void Controller::_colorMapChanged(){ _render(); } -int Controller::_getDataIndex( ) const { - int index = m_selectImage->getIndex(); - int dataIndex = -1; - int visibleIndex = -1; - int dataCount = m_datas.size(); - for ( int i = 0; i < dataCount; i++ ){ - if ( m_datas[i]->_isVisible() ){ - visibleIndex++; - if ( visibleIndex == index ){ - dataIndex = i; - break; - } - } - } - return dataIndex; -} + void Controller::centerOnPixel( double centerX, double centerY ){ - int dataIndex = _getDataIndex(); - if ( dataIndex >= 0 ){ - m_datas[dataIndex]->_setPan( centerX, centerY ); + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + m_datas[i]->_setPan( centerX, centerY ); _render(); } } void Controller::_contoursChanged(){ - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ std::vector frames = _getFrameIndices( dataIndex ); const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); @@ -304,7 +292,7 @@ void Controller::_contoursChanged(){ void Controller::_displayAxesChanged(std::vector displayAxisTypes, bool applyAll ){ if ( !applyAll ){ - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if (dataIndex >= 0 ) { if (m_datas[dataIndex] != nullptr) { std::vector frames = _getFrameIndices( dataIndex ); @@ -327,7 +315,7 @@ void Controller::_displayAxesChanged(std::vector displayAxi } std::vector Controller::_getAxisZTypes() const { - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); std::vector zTypes; if ( 0 <= dataIndex ){ zTypes = m_datas[dataIndex]->_getAxisZTypes(); @@ -350,6 +338,17 @@ std::set Controller::_getAxesHidden() const { return axes; } +QStringList Controller::getCenterPixel() { + int dataIndex = _getIndexCurrent(); + QStringList returnValue = QStringList( "null" ); + if ( dataIndex >= 0 ) { + QPointF center = m_datas[dataIndex]->_getCenter(); + returnValue = QStringList( QString::number( center.x() ) ); + returnValue.append( QString::number( center.y() ) ); + } + return returnValue; +} + double Controller::getClipPercentileMax() const { double clipValueMax = m_state.getValue(CLIP_VALUE_MAX); return clipValueMax; @@ -361,7 +360,7 @@ double Controller::getClipPercentileMin() const { } Carta::Lib::KnownSkyCS Controller::getCoordinateSystem() const { - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); Carta::Lib::KnownSkyCS cs = Carta::Lib::KnownSkyCS::Unknown; if ( 0 <= dataIndex ){ cs = m_datas[dataIndex]->_getCoordinateSystem(); @@ -369,8 +368,29 @@ Carta::Lib::KnownSkyCS Controller::getCoordinateSystem() const { return cs; } -std::shared_ptr Controller::getGridControls() { - return m_gridControls; +QStringList Controller::getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system) const { + QStringList result; + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + QStringList coordList = m_datas[dataIndex]->_getCoordinates( x, y, system, _getFrameIndices(dataIndex) ); + for ( int i = 0; i <= 1; i++ ){ + result.append( coordList[i] ); + } + } + return result; +} + +std::vector< std::shared_ptr > Controller::getDataSources(){ + //For right now, we are only going to do a histogram of a single image. + std::vector > images; + int dataCount = m_datas.size(); + if ( dataCount > 0 ){ + int dataIndex = _getIndexCurrent(); + if ( 0 <= dataIndex ){ + images.push_back( m_datas[dataIndex]->_getImage()); + } + } + return images; } int Controller::getFrameUpperBound( AxisInfo::KnownType axisType ) const { @@ -385,7 +405,7 @@ int Controller::getFrameUpperBound( AxisInfo::KnownType axisType ) const { int Controller::getFrame( AxisInfo::KnownType axisType ) const { int frame = -1; if ( axisType != AxisInfo::KnownType::OTHER ){ - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex ){ std::vector supportedAxes = m_datas[dataIndex]->_getAxisTypes(); //Make sure the axis is an axis in the image. @@ -399,20 +419,28 @@ int Controller::getFrame( AxisInfo::KnownType axisType ) const { return frame; } -QStringList Controller::getCenterPixel() { - int dataIndex = _getDataIndex(); - QStringList returnValue = QStringList( "null" ); - if ( dataIndex >= 0 ) { - QPointF center = m_datas[dataIndex]->_getCenter(); - returnValue = QStringList( QString::number( center.x() ) ); - returnValue.append( QString::number( center.y() ) ); +std::vector Controller::_getFrameIndices( int imageIndex ) const { + int selectCount = m_selects.size(); + std::vector frames( selectCount ); + if ( imageIndex >= 0 && imageIndex < m_datas.size() ){ + if ( m_datas[imageIndex] != nullptr ){ + //Determine the index of the frame to load. + for ( int i = 0; i < selectCount; i++ ){ + frames[i] = m_selects[i]->getIndex(); + } + } } - return returnValue; + return frames; +} + + +std::shared_ptr Controller::getGridControls() { + return m_gridControls; } QStringList Controller::getImageDimensions( ){ QStringList result; - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ int dimensions = m_datas[dataIndex]->_getDimensions(); for ( int i = 0; i < dimensions; i++ ) { @@ -426,18 +454,45 @@ QStringList Controller::getImageDimensions( ){ return result; } -QStringList Controller::getOutputSize( ){ - QStringList result; - int dataIndex = _getDataIndex(); - if ( dataIndex >= 0 ){ - QSize outputSize = m_datas[dataIndex]->_getOutputSize(); - result.append( QString::number( outputSize.width() ) ); - result.append( QString::number( outputSize.height() ) ); +QString Controller::getImageName(int index) const{ + QString name; + if ( 0 <= index && index < m_datas.size()){ + name = m_datas[index]->_getFileName(); } - else { - result = QStringList(""); + return name; +} + +int Controller::_getIndexData( ControllerData* target ) const { + int targetIndex = -1; + if ( target != nullptr ){ + int dataCount = m_datas.size(); + QString targetName = target->_getFileName(); + for ( int i = 0; i < dataCount; i++ ){ + QString dataName = m_datas[i]->_getFileName(); + if ( targetName == dataName ){ + targetIndex = i; + break; + } + } } - return result; + return targetIndex; +} + +int Controller::_getIndexCurrent( ) const { + int index = m_selectImage->getIndex(); + int dataIndex = -1; + int visibleIndex = -1; + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isVisible() ){ + visibleIndex++; + if ( visibleIndex == index ){ + dataIndex = i; + break; + } + } + } + return dataIndex; } @@ -449,13 +504,30 @@ bool Controller::getIntensity( double percentile, double* intensity ) const{ bool Controller::getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const{ bool validIntensity = false; - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex && percentile >= 0.0 && percentile <= 1.0 ){ validIntensity = m_datas[dataIndex]->_getIntensity( frameLow, frameHigh, percentile, intensity ); } return validIntensity; } +QStringList Controller::getOutputSize( ){ + QStringList result; + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + QSize outputSize = m_datas[dataIndex]->_getOutputSize(); + result.append( QString::number( outputSize.width() ) ); + result.append( QString::number( outputSize.height() ) ); + } + else { + result = QStringList(""); + } + return result; +} + + + + double Controller::getPercentile( double intensity ) const { int currentFrame = getFrame( AxisInfo::KnownType::SPECTRAL ); return getPercentile( currentFrame, currentFrame, intensity ); @@ -463,47 +535,17 @@ double Controller::getPercentile( double intensity ) const { double Controller::getPercentile( int frameLow, int frameHigh, double intensity ) const { double percentile = -1; - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex ){ percentile = m_datas[dataIndex]->_getPercentile( frameLow, frameHigh, intensity ); } return percentile; } -std::vector< std::shared_ptr > Controller::getDataSources(){ - //For right now, we are only going to do a histogram of a single image. - std::vector > images; - int dataCount = m_datas.size(); - if ( dataCount > 0 ){ - int dataIndex = _getDataIndex(); - if ( 0 <= dataIndex ){ - images.push_back( m_datas[dataIndex]->_getImage()); - } - } - return images; -} - -int Controller::getSelectImageIndex() const { - int selectImageIndex = -1; - int stackedImageVisibleCount = getStackedImageCountVisible(); - if ( stackedImageVisibleCount >= 1 ){ - selectImageIndex = m_selectImage->getIndex(); - } - return selectImageIndex; -} - -QString Controller::getImageName(int index) const{ - QString name; - if ( 0 <= index && index < m_datas.size()){ - name = m_datas[index]->_getFileName(); - } - return name; -} - std::shared_ptr Controller::getPipeline() const { std::shared_ptr pipeline(nullptr); //Color map should be based on the selected image rather than the current image. - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); int dataCount = m_datas.size(); for ( int i = 0; i < dataCount; i++ ){ if ( m_datas[i]->_isSelected() ){ @@ -519,7 +561,7 @@ std::shared_ptr Controller QStringList Controller::getPixelCoordinates( double ra, double dec ) const { QStringList result(""); - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ result = m_datas[dataIndex]->_getPixelCoordinates( ra, dec ); } @@ -528,7 +570,7 @@ QStringList Controller::getPixelCoordinates( double ra, double dec ) const { QString Controller::getPixelValue( double x, double y ) const { QString result(""); - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ std::vector frames = _getFrameIndices( dataIndex ); result = m_datas[dataIndex]->_getPixelValue( x, y, frames ); @@ -538,47 +580,31 @@ QString Controller::getPixelValue( double x, double y ) const { QString Controller::getPixelUnits() const { QString result(""); - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ result = m_datas[dataIndex]->_getPixelUnits(); } return result; } -QStringList Controller::getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system) const { - QStringList result; - int dataIndex = _getDataIndex(); - if ( dataIndex >= 0 ){ - QStringList coordList = m_datas[dataIndex]->_getCoordinates( x, y, system, _getFrameIndices(dataIndex) ); - for ( int i = 0; i <= 1; i++ ){ - result.append( coordList[i] ); - } +QString Controller::_getPreferencesId() const { + QString id; + if ( m_settings.get() != nullptr ){ + id = m_settings->getPath(); } - return result; + return id; } -std::vector Controller::_getFrameIndices( int imageIndex ) const { - int selectCount = m_selects.size(); - std::vector frames( selectCount ); - if ( imageIndex >= 0 && imageIndex < m_datas.size() ){ - if ( m_datas[imageIndex] != nullptr ){ - //Determine the index of the frame to load. - for ( int i = 0; i < selectCount; i++ ){ - frames[i] = m_selects[i]->getIndex(); - } - } +int Controller::getSelectImageIndex() const { + int selectImageIndex = -1; + int stackedImageVisibleCount = getStackedImageCountVisible(); + if ( stackedImageVisibleCount >= 1 ){ + selectImageIndex = m_selectImage->getIndex(); } - return frames; + return selectImageIndex; } -QString Controller::_getPreferencesId() const { - return m_settings ? m_settings-> getPath() : QString(); -// QString id; -// if ( m_settings.get() != nullptr ){ -// id = m_settings->getPath(); -// } -// return id; -} + std::vector< std::shared_ptr > Controller::getSelectedColorStates(){ std::vector< std::shared_ptr > colorStates; @@ -591,6 +617,8 @@ std::vector< std::shared_ptr > Controller::getSelectedColorStates() return colorStates; } + + int Controller::getStackedImageCount() const { return m_datas.size(); } @@ -606,7 +634,6 @@ int Controller::getStackedImageCountVisible() const { return visibleCount; } - QString Controller::getStateString( const QString& sessionId, SnapshotType type ) const{ QString result(""); if ( type == SNAPSHOT_PREFERENCES ){ @@ -658,7 +685,7 @@ QString Controller::getSnapType(CartaObject::SnapshotType snapType) const { double Controller::getZoomLevel( ){ double zoom = DataSource::ZOOM_DEFAULT; - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ zoom = m_datas[dataIndex]->_getZoom( ); _render(); @@ -667,7 +694,7 @@ double Controller::getZoomLevel( ){ } void Controller::_gridChanged( const StateInterface& state, bool applyAll ){ - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ std::vector frames = _getFrameIndices( dataIndex ); if ( !applyAll ){ @@ -692,54 +719,54 @@ void Controller::_gridChanged( const StateInterface& state, bool applyAll ){ void Controller::_initializeCallbacks(){ addCommandCallback( "hideImage", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {IMAGE}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString imageIndexStr = dataValues[*keys.begin()]; - bool validInt = false; - int imageIndex = imageIndexStr.toInt( &validInt ); - QString result; - if ( validInt ){ - result = setImageVisibility( imageIndex, false ); - } - else { - result = "Invalid image hide index: "+params; - } - Util::commandPostProcess( result ); - return result; - }); + std::set keys = {IMAGE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString imageIndexStr = dataValues[*keys.begin()]; + bool validInt = false; + int imageIndex = imageIndexStr.toInt( &validInt ); + QString result; + if ( validInt ){ + result = setImageVisibility( imageIndex, false ); + } + else { + result = "Invalid image hide index: "+params; + } + Util::commandPostProcess( result ); + return result; + }); addCommandCallback( "showImage", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {IMAGE}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString imageIndexStr = dataValues[*keys.begin()]; - bool validInt = false; - int imageIndex = imageIndexStr.toInt( &validInt ); - QString result; - if ( validInt ){ - result = setImageVisibility( imageIndex, true ); - } - else { - result = "Invalid image show index: "+params; - } - Util::commandPostProcess( result ); - return result; - }); + std::set keys = {IMAGE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString imageIndexStr = dataValues[*keys.begin()]; + bool validInt = false; + int imageIndex = imageIndexStr.toInt( &validInt ); + QString result; + if ( validInt ){ + result = setImageVisibility( imageIndex, true ); + } + else { + result = "Invalid image show index: "+params; + } + Util::commandPostProcess( result ); + return result; + }); addCommandCallback( "setImageOrder", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - bool parseError = false; - std::vector vals = Util::string2VectorInt( params, &parseError, ";" ); - QString result; - if ( !parseError ){ - result = setImageOrder( vals ); - } - else { - result = "Image order must be expressed as nonnegative integers: "+params; - } - Util::commandPostProcess( result ); - return result; - }); + bool parseError = false; + std::vector vals = Util::string2VectorInt( params, &parseError, ";" ); + QString result; + if ( !parseError ){ + result = setImageOrder( vals ); + } + else { + result = "Image order must be expressed as nonnegative integers: "+params; + } + Util::commandPostProcess( result ); + return result; + }); //Listen for updates to the clip and reload the frame. @@ -780,7 +807,7 @@ void Controller::_initializeCallbacks(){ return result; }); - QString pointerPath= UtilState::getLookup( getPath(), UtilState::getLookup( ImageView::VIEW, POINTER_MOVE)); + QString pointerPath= UtilState::getLookup( getPath(), UtilState::getLookup( VIEW, POINTER_MOVE)); addStateCallback( pointerPath, [=] ( const QString& /*path*/, const QString& value ) { QStringList mouseList = value.split( " "); if ( mouseList.size() == 2 ){ @@ -802,23 +829,23 @@ void Controller::_initializeCallbacks(){ QString imageName = dataValues[*keys.begin()]; QString result = closeImage( imageName ); return result; - }); + }); addCommandCallback( CENTER, [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) ->QString { - bool parseError = false; - QString result; - auto vals = Util::string2VectorDouble( params, &parseError ); - int count = vals.size(); - if ( count > 1 ) { - updatePan( vals[0], vals[1]); - } - else { - result = "Center command must include doubles specifying the point to center: "+params; - } - Util::commandPostProcess( result ); - return result; - }); + bool parseError = false; + QString result; + auto vals = Util::string2VectorDouble( params, &parseError ); + int count = vals.size(); + if ( count > 1 ) { + updatePan( vals[0], vals[1]); + } + else { + result = "Center command must include doubles specifying the point to center: "+params; + } + Util::commandPostProcess( result ); + return result; + }); addCommandCallback( ZOOM, [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) ->QString { @@ -835,12 +862,12 @@ void Controller::_initializeCallbacks(){ addCommandCallback( "registerContourControls", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QString result; - if ( m_contourControls.get() != nullptr ){ - result = m_contourControls->getPath(); - } - return result; - }); + QString result; + if ( m_contourControls.get() != nullptr ){ + result = m_contourControls->getPath(); + } + return result; + }); addCommandCallback( "registerGridControls", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { @@ -853,9 +880,9 @@ void Controller::_initializeCallbacks(){ addCommandCallback( "registerPreferences", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QString result = _getPreferencesId(); - return result; - }); + QString result = _getPreferencesId(); + return result; + }); addCommandCallback( "registerShape", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { @@ -887,63 +914,127 @@ void Controller::_initializeCallbacks(){ addCommandCallback( "resetPan", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QString result; - resetPan(); - return result; - }); + QString result; + resetPan(); + return result; + }); addCommandCallback( "resetZoom", [=] (const QString & /*cmd*/, const QString & /*params*/, const QString & /*sessionId*/) -> QString { - QString result; - resetZoom(); - return result; - }); + QString result; + resetZoom(); + return result; + }); addCommandCallback( "setLayersSelected", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - QString result; - bool error = false; - std::vector vals = Util::string2VectorInt( params, &error, ";" ); - if ( error ){ - result = "Please specify the layers to select as nonnegative integers"; - } - else { - result = setLayersSelected( vals ); - } - Util::commandPostProcess( result ); - return result; - }); + QString result; + bool error = false; + std::vector vals = Util::string2VectorInt( params, &error, ";" ); + if ( error ){ + result = "Please specify the layers to select as nonnegative integers"; + } + else { + result = setLayersSelected( vals ); + } + Util::commandPostProcess( result ); + return result; + }); addCommandCallback( "saveImage", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {DATA_PATH}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString result = saveImage( dataValues[DATA_PATH]); - Util::commandPostProcess( result ); - return result; - }); + std::set keys = {DATA_PATH}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString result = saveImage( dataValues[DATA_PATH]); + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "setMaskColor", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {Util::RED, Util::GREEN, Util::BLUE}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + + QString result; + QString redStr = dataValues[Util::RED]; + bool validRed = false; + int redAmount = redStr.toInt( &validRed ); + QString greenStr = dataValues[Util::GREEN]; + bool validGreen = false; + int greenAmount = greenStr.toInt( &validGreen ); + QString blueStr = dataValues[Util::BLUE]; + bool validBlue = false; + int blueAmount = blueStr.toInt( &validBlue ); + if ( validRed && validGreen && validBlue ){ + QStringList errorList = setMaskColor( redAmount, greenAmount, blueAmount ); + result = errorList.join(";"); + } + else { + result = "Invalid mask color(s): "+params; + } + + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "setMaskOpacity", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = { Util::ALPHA }; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString result; + QString alphaStr = dataValues[Util::ALPHA]; + bool validAlpha = false; + int alphaAmount = alphaStr.toInt( &validAlpha ); + if ( validAlpha ){ + result = setMaskOpacity( alphaAmount ); + } + else { + result = "Invalid mask opacity: "+params; + } + Util::commandPostProcess( result ); + return result; + }); addCommandCallback( "setStackSelectAuto", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {STACK_SELECT_AUTO}; - std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString autoModeStr = dataValues[STACK_SELECT_AUTO]; - bool validBool = false; - bool autoSelect = Util::toBool( autoModeStr, &validBool ); - QString result; - if ( validBool ){ - setStackSelectAuto( autoSelect ); - } - else { - result = "Please specify true/false when setting whether stack selection should be automatic: "+autoModeStr; - } - Util::commandPostProcess( result ); - return result; - }); + std::set keys = {STACK_SELECT_AUTO}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString autoModeStr = dataValues[STACK_SELECT_AUTO]; + bool validBool = false; + bool autoSelect = Util::toBool( autoModeStr, &validBool ); + QString result; + if ( validBool ){ + setStackSelectAuto( autoSelect ); + } + else { + result = "Please specify true/false when setting whether stack selection should be automatic: "+autoModeStr; + } + Util::commandPostProcess( result ); + return result; + }); + + addCommandCallback( "setUseMask", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {ControllerData::APPLY}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString result; + QString applyStr = dataValues[ControllerData::APPLY]; + bool validBool = false; + bool apply = Util::toBool( applyStr, &validBool ); + if ( validBool ){ + result = setUseMask( apply ); + } + else { + result = "Use mask must be true/false: "+params; + } + Util::commandPostProcess( result ); + return result; + }); } + void Controller::_initializeSelections(){ int axisCount = static_cast(AxisInfo::KnownType::OTHER); Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); @@ -992,7 +1083,7 @@ bool Controller::isStackSelectAuto() const { void Controller::_loadView( bool newClips ) { m_reloadFrameQueued = false; //Determine the index of the data to load. - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ) { if (m_datas[dataIndex] != nullptr) { @@ -1013,9 +1104,6 @@ void Controller::_loadView( bool newClips ) { qDebug() << "Uninitialized image: "<getIndex(); - int selectedDataIndex = _getDataIndex(); + int selectedDataIndex = _getIndexCurrent(); QString id = m_datas[index]->getId(); bool visible = m_datas[index]->_isVisible(); Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); @@ -1051,7 +1139,7 @@ void Controller::_removeData( int index ){ m_selectImage->setIndex( imageCountDecreased ); } //Update the channel upper bound and index if necessary - int targetData = _getDataIndex(); + int targetData = _getIndexCurrent(); int selectCount = m_selects.size(); for ( int i = 0; i < selectCount; i++ ){ int frameCount = 0; @@ -1080,7 +1168,7 @@ void Controller::_removeData( int index ){ void Controller::_render(){ - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ std::vector frames = _getFrameIndices( dataIndex ); const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); @@ -1088,44 +1176,8 @@ void Controller::_render(){ } } -void Controller::_renderingDone( QImage img){ - _scheduleFrameRepaint( img ); -} - -QString Controller::setImageOrder( const std::vector& indices ){ - QString result; - bool imageReordered = false; - int dataCount = m_datas.size(); - int indexCount = indices.size(); - QList > reorderedList; - if ( indexCount != dataCount ){ - result = "Reorder image size must match the stack count: "+QString::number(dataCount); - } - else { - for ( int i = 0; i < indexCount; i++ ){ - int targetIndex = indices[i]; - if ( targetIndex < 0 || targetIndex >= dataCount ){ - result = "Reorder failed: unknown image index: "+targetIndex; - break; - } - //Insert the image at the target index at position i. - else { - reorderedList.append( m_datas[targetIndex] ); - if ( targetIndex != i ){ - imageReordered = true; - } - } - } - } - if ( imageReordered ){ - m_datas = reorderedList; - _render(); - } - return result; -} - void Controller::_repaintFrameNow(){ - m_view->scheduleRedraw(); + m_view->scheduleRepaint(); m_repaintFrameQueued = false; } @@ -1180,7 +1232,7 @@ void Controller::resetStateData( const QString& state ){ emit dataChanged( this ); //Reset the state of the grid controls based on the selected image. - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex ){ StateInterface controlState = m_datas[dataIndex]->_getGridState(); this->m_gridControls->_resetState( controlState ); @@ -1242,7 +1294,7 @@ QString Controller::saveImage( const QString& fileName, double scale ){ DataLoader* dLoader = Util::findSingletonObject(); bool securityRestricted = dLoader->isSecurityRestricted(); if ( !securityRestricted ){ - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex ){ //Check and make sure the directory exists. int dirIndex = fileName.lastIndexOf( QDir::separator() ); @@ -1294,16 +1346,44 @@ void Controller::_saveRegions(){ } } -void Controller::_scheduleFrameRepaint( const QImage& img ){ - if ( m_datas.size() > 0 ){ - // if reload is already pending, do nothing - if ( m_repaintFrameQueued ) { - return; +void Controller::_scheduleFrameRepaint(){ + int dataCount = m_datas.size(); + // if reload is already pending, do nothing + if ( m_repaintFrameQueued ) { + return; + } + m_view->resetLayers(); + + //We want the selected index to be the last one in the stack. + int selectIndex = m_selectImage->getIndex(); + int layerIndex = 0; + for ( int i = 0; i < dataCount; i++ ){ + int dIndex = ( selectIndex + i + 1 ) % dataCount; + if ( m_datas[dIndex]->_isVisible() ){ + QImage image = m_datas[dIndex]->_getQImage(); + Carta::Lib::VectorGraphics::VGList graphicsList = m_datas[dIndex]->_getVectorGraphics(); + m_view->setRasterLayer( layerIndex, image ); + bool masked = m_datas[dIndex]->_isMasked(); + if ( masked ){ + std::shared_ptr pmc = + std::make_shared < Carta::Lib::PixelMaskCombiner > (); + float alphaVal = m_datas[dIndex]->_getMaskAlpha(); + pmc-> setAlpha( alphaVal ); + qint32 maskColor = m_datas[dIndex]->_getMaskColor(); + pmc-> setMask( maskColor ); + m_view->setRasterLayerCombiner( layerIndex, pmc ); + } + else { + std::shared_ptr dc = + std::make_shared < Carta::Lib::DefaultCombiner > (); + m_view->setRasterLayerCombiner( layerIndex, dc ); + } + m_view->setVGLayer( layerIndex, graphicsList ); + layerIndex++; } - m_view->resetImage( img); - m_repaintFrameQueued = true; - QMetaObject::invokeMethod( this, "_repaintFrameNow", Qt::QueuedConnection ); } + m_repaintFrameQueued = true; + QMetaObject::invokeMethod( this, "_repaintFrameNow", Qt::QueuedConnection ); } @@ -1356,7 +1436,7 @@ void Controller::_setFrameAxis(int value, AxisInfo::KnownType axisType ) { m_selects[axisIndex]->setIndex(value); //We only need to update the cursor if the axis is a hidden axis //for the current image. - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex ){ _updateCursorText( true ); emit channelChanged( this ); @@ -1375,7 +1455,7 @@ void Controller::setFrameImage( int val) { indices[0] = val; _setLayersSelected( indices ); } - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex ){ int selectCount = m_selects.size(); for ( int i = 0; i < selectCount; i++ ){ @@ -1421,6 +1501,38 @@ void Controller::_setColorMapUseGlobal( bool global ) { emit colorChanged( this ); } +QString Controller::setImageOrder( const std::vector& indices ){ + QString result; + bool imageReordered = false; + int dataCount = m_datas.size(); + int indexCount = indices.size(); + QList > reorderedList; + if ( indexCount != dataCount ){ + result = "Reorder image size must match the stack count: "+QString::number(dataCount); + } + else { + for ( int i = 0; i < indexCount; i++ ){ + int targetIndex = indices[i]; + if ( targetIndex < 0 || targetIndex >= dataCount ){ + result = "Reorder failed: unknown image index: "+targetIndex; + break; + } + //Insert the image at the target index at position i. + else { + reorderedList.append( m_datas[targetIndex] ); + if ( targetIndex != i ){ + imageReordered = true; + } + } + } + } + if ( imageReordered ){ + m_datas = reorderedList; + _render(); + } + return result; +} + QString Controller::setImageVisibility( int dataIndex, bool visible ){ QString result; int dataCount = m_datas.size(); @@ -1429,7 +1541,7 @@ QString Controller::setImageVisibility( int dataIndex, bool visible ){ if ( oldVisible != visible ){ m_datas[dataIndex]->_setVisible( visible ); - int selectedImageIndex = _getDataIndex(); + int selectedImageIndex = _getIndexCurrent(); //Update the upper bound on the number of images available. int visibleCount = getStackedImageCountVisible(); m_selectImage->setUpperBound( visibleCount ); @@ -1514,6 +1626,55 @@ QString Controller::_setLayersSelected( const std::vector indices ){ return result; } +QStringList Controller::setMaskColor( int redAmount, int greenAmount, int blueAmount ){ + QStringList result; + int dataCount = m_datas.size(); + bool dataSelected = false; + bool dataChanged = false; + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isSelected() ){ + dataSelected = true; + bool changed = m_datas[i]->_setMaskColor( redAmount, + greenAmount, blueAmount, result ); + if ( changed ){ + dataChanged = true; + } + } + } + if ( !dataSelected ){ + result.append( "Please select one or more data sets for the mask color."); + } + if ( dataChanged ){ + saveState(); + _scheduleFrameReload( false ); + } + return result; +} + +QString Controller::setMaskOpacity( int alphaAmount ){ + QString result; + int dataCount = m_datas.size(); + bool dataSelected = false; + bool dataChanged = false; + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isSelected() ){ + dataSelected = true; + bool changed = m_datas[i]->_setMaskOpacity( alphaAmount, result ); + if ( changed ){ + dataChanged = true; + } + } + } + if ( !dataSelected ){ + result = "Please select the data for the mask opacity"; + } + if ( dataChanged ){ + saveState(); + _scheduleFrameReload( false ); + } + return result; +} + void Controller::setStackSelectAuto( bool automatic ){ bool oldStackSelectAuto = m_state.getValue(STACK_SELECT_AUTO ); if ( oldStackSelectAuto != automatic ){ @@ -1522,11 +1683,35 @@ void Controller::setStackSelectAuto( bool automatic ){ } } +QString Controller::setUseMask( bool useMask ){ + QString result; + int dataCount = m_datas.size(); + bool dataSelected = false; + bool dataChanged = false; + for( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isSelected() ){ + dataSelected = true; + bool stateChanged = m_datas[i]->_setUseMask( useMask ); + if ( stateChanged ){ + dataChanged = true; + } + } + } + if ( !dataSelected ){ + result = "Data must be selected to apply a mask."; + } + if ( dataChanged ){ + saveState(); + _scheduleFrameReload( false ); + } + return result; +} + void Controller::setZoomLevel( double zoomFactor ){ - int dataIndex = _getDataIndex(); - if ( dataIndex >= 0 ){ + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ //Set the zoom - m_datas[dataIndex]->_setZoom( zoomFactor ); + m_datas[i]->_setZoom( zoomFactor ); _render(); } } @@ -1550,7 +1735,7 @@ void Controller::_updateCursor( int mouseX, int mouseY ){ void Controller::_updateCursorText(bool notifyClients ){ QString formattedCursor; int imageIndex = m_selectImage->getIndex(); - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex ){ int mouseX = m_stateMouse.getValue(ImageView::MOUSE_X ); int mouseY = m_stateMouse.getValue(ImageView::MOUSE_Y ); @@ -1583,7 +1768,7 @@ void Controller::_updateDisplayAxes( int targetIndex ){ void Controller::updateZoom( double centerX, double centerY, double zoomFactor ){ - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ //Remember where the user clicked QPointF clickPtScreen( centerX, centerY); @@ -1621,7 +1806,7 @@ void Controller::updateZoom( double centerX, double centerY, double zoomFactor ) } void Controller::updatePan( double centerX , double centerY){ - int dataIndex = _getDataIndex(); + int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ bool validImage = false; QPointF oldImageCenter = m_datas[dataIndex]-> _getImagePt( { centerX, centerY }, &validImage ); @@ -1639,11 +1824,11 @@ void Controller::updatePan( double centerX , double centerY){ } -void Controller::_viewResize( const QSize& newSize ){ +void Controller::_viewResize( ){ + QSize clientSize = m_view->getClientSize(); for ( int i = 0; i < m_datas.size(); i++ ){ - m_datas[i]->_viewResize( newSize ); + m_datas[i]->_viewResize( clientSize ); } - m_viewSize = newSize; _render(); } @@ -1662,17 +1847,20 @@ Controller::~Controller(){ objMan->destroyObject( m_selectImage->getId()); m_selectImage = nullptr; } + if ( m_gridControls != nullptr ){ objMan->removeObject( m_gridControls->getId()); } + if ( m_contourControls != nullptr ){ objMan->removeObject( m_contourControls->getId()); } + if ( m_settings != nullptr ){ objMan->removeObject( m_settings->getId()); } - _clearData(); + _clearData(); for ( Region* region : m_regions ){ objMan->destroyObject( region->getId()); diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index d6f2318b..1672e8df 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -9,6 +9,7 @@ #include #include "CartaLib/CartaLib.h" #include "CartaLib/AxisInfo.h" +#include "CartaLib/VectorGraphics/VGList.h" #include #include @@ -17,11 +18,8 @@ #include -class ImageView; class CoordinateFormatterInterface; - - namespace Carta { namespace Lib { namespace PixelPipeline { @@ -33,6 +31,7 @@ namespace Carta { namespace NdArray { class RawViewInterface; } + class LayeredRemoteVGView; } } @@ -120,33 +119,47 @@ class Controller: public QObject, public Carta::State::CartaObject, double getClipPercentileMax() const; /** - * Return a shared pointer to the grid controls. - * @return - a shared pointer to the grid controls. + * Return the coordinates at pixel (x, y) in the given coordinate system. + * @param x the x-coordinate of the desired pixel. + * @param y the y-coordinate of the desired pixel. + * @param system the desired coordinate system. + * @return the coordinates at pixel (x, y). */ - std::shared_ptr getGridControls(); + QStringList getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system ) const; + + std::vector > getDataSources(); /** - * Return the percentile corresponding to the given intensity in the current frame. - * @param intensity a value for which a percentile is needed. - * @return the percentile corresponding to the intensity. + * Return the current axis frame. + * @param axisType - the axis for which the frame index is needed. + * @return the current frame for the axis. */ - double getPercentile( double intensity ) const; + int getFrame( Carta::Lib::AxisInfo::KnownType axisType ) const; /** - * Return the percentile corresponding to the given intensity. - * @param frameLow a lower bound for the channel range or -1 if there is no lower bound. - * @param frameHigh an upper bound for the channel range or -1 if there is no upper bound. - * @param intensity a value for which a percentile is needed. - * @return the percentile corresponding to the intensity. + * Return the frame upper bound. + * @param type - the axis for which a frame upper bound is needed. + * @return the largest frame for a particular axis in the image. */ - double getPercentile( int frameLow, int frameHigh, double intensity ) const; + int getFrameUpperBound( Carta::Lib::AxisInfo::KnownType type ) const; /** - * Return the pipeline being used to draw the image. - * @return a Carta::Lib::PixelPipeline::CustomizablePixelPipeline being used to draw the - * image. + * Return a shared pointer to the grid controls. + * @return - a shared pointer to the grid controls. */ - std::shared_ptr getPipeline() const; + std::shared_ptr getGridControls(); + + /** + * Get the image dimensions. + */ + QStringList getImageDimensions( ); + + /** + * Returns an identifier for the data source at the given index. + * @param index the index of a data source. + * @return an identifier for the image. + */ + QString getImageName(int index) const; /** * Returns the intensity corresponding to a given percentile in the current frame. @@ -166,55 +179,33 @@ class Controller: public QObject, public Carta::State::CartaObject, */ bool getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; - std::vector > getDataSources(); - - /** - * Return the current axis frame. - * @param axisType - the axis for which the frame index is needed. - * @return the current frame for the axis. - */ - int getFrame( Carta::Lib::AxisInfo::KnownType axisType ) const; - - /** - * Get the current zoom level - */ - double getZoomLevel( ); - - /** - * Get the image dimensions. - */ - QStringList getImageDimensions( ); - /** * Get the dimensions of the image viewer (window size). */ QStringList getOutputSize( ); - - QString _getPreferencesId() const; - /** - * Get the color map information for the data sources that have been - * selected. - * @return - a list containing color map information for the data sources - * that have been selected. + * Return the percentile corresponding to the given intensity in the current frame. + * @param intensity a value for which a percentile is needed. + * @return the percentile corresponding to the intensity. */ - std::vector< std::shared_ptr > getSelectedColorStates(); + double getPercentile( double intensity ) const; /** - * Return a count of the number of image layers in the stack. - * @return the number of image layers in the stack. + * Return the percentile corresponding to the given intensity. + * @param frameLow a lower bound for the channel range or -1 if there is no lower bound. + * @param frameHigh an upper bound for the channel range or -1 if there is no upper bound. + * @param intensity a value for which a percentile is needed. + * @return the percentile corresponding to the intensity. */ - //Note: this will include image layers that the user may not see because they - //are hidden. - int getStackedImageCount() const; + double getPercentile( int frameLow, int frameHigh, double intensity ) const; /** - * Returns the number of visibile image layers in the stack. - * @return a count of the number of image layers that have not been hidden - * and are available for the user to see. + * Return the pipeline being used to draw the image. + * @return a Carta::Lib::PixelPipeline::CustomizablePixelPipeline being used to draw the + * image. */ - int getStackedImageCountVisible() const; + std::shared_ptr getPipeline() const; /** * Return the pixel coordinates corresponding to the given world coordinates. @@ -239,15 +230,6 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString getPixelUnits() const; - /** - * Return the coordinates at pixel (x, y) in the given coordinate system. - * @param x the x-coordinate of the desired pixel. - * @param y the y-coordinate of the desired pixel. - * @param system the desired coordinate system. - * @return the coordinates at pixel (x, y). - */ - QStringList getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system ) const; - /** * Return the index of the image that is currently at the top of the stack. * @return the index of the current image. @@ -255,27 +237,41 @@ class Controller: public QObject, public Carta::State::CartaObject, int getSelectImageIndex() const ; /** - * Return the frame upper bound. - * @param type - the axis for which a frame upper bound is needed. - * @return the largest frame for a particular axis in the image. + * Get the color map information for the data sources that have been + * selected. + * @return - a list containing color map information for the data sources + * that have been selected. */ - int getFrameUpperBound( Carta::Lib::AxisInfo::KnownType type ) const; + std::vector< std::shared_ptr > getSelectedColorStates(); /** - * Returns an identifier for the data source at the given index. - * @param index the index of a data source. - * @return an identifier for the image. + * Return a count of the number of image layers in the stack. + * @return the number of image layers in the stack. */ - QString getImageName(int index) const; + //Note: this will include image layers that the user may not see because they + //are hidden. + int getStackedImageCount() const; /** - * Returns a json string representing the state of this controller. - * @param type - the type of snapshot to return. - * @param sessionId - an identifier for the user's session. - * @return a string representing the state of this controller. + * Returns the number of visibile image layers in the stack. + * @return a count of the number of image layers that have not been hidden + * and are available for the user to see. */ + int getStackedImageCountVisible() const; + + /** + * Returns a json string representing the state of this controller. + * @param type - the type of snapshot to return. + * @param sessionId - an identifier for the user's session. + * @return a string representing the state of this controller. + */ virtual QString getStateString( const QString& sessionId, SnapshotType type ) const Q_DECL_OVERRIDE; + /** + * Get the current zoom level + */ + double getZoomLevel( ); + /** * Returns whether or not the image stack layers are selected based on the * animator (auto) or whether the user has indicated a manual selection. @@ -301,14 +297,11 @@ class Controller: public QObject, public Carta::State::CartaObject, */ virtual void resetStateData( const QString& state ) Q_DECL_OVERRIDE; - /** * Reset the zoom to its original value. */ void resetZoom(); - - /** * Save a copy of the full image in the current image view. * @param filename the full path where the file is to be saved. @@ -326,7 +319,6 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString saveImage( const QString& filename ); - /** * Save the state of this controller. */ @@ -380,6 +372,26 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString setLayersSelected( const std::vector indices ); + /** + * Set the color to use for the mask. + * @param redAmount - the amount of red in [0,255]. + * @param greenAmount - the amount of green in [0,255]. + * @param blueAmount - the amount of blue in [0,255]. + * @return - a list containing any errors that may have occurred in setting + * the mask color. + */ + //Note: Mask color will not take affect unless use mask is also true. + QStringList setMaskColor( int redAmount, int greenAmount, int blueAmount ); + + /** + * Set the opacity of the mask. + * @param alphaAmount - the transparency level in [0,255] with 255 being opaque. + * @return - an error message if there was a problem setting the mask opacity or + * an empty string otherwise. + */ + //Note: Mask opacity will not take affect unless use mask is also true. + QString setMaskOpacity( int alphaAmount ); + /** * Set whether or not selection of layers in the stack should be based on the * current layer or whether the user wants to make a manual selection. @@ -387,6 +399,14 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void setStackSelectAuto( bool automatic ); + /** + * Set whether or not to apply a color mask to the image. + * @param useMask - true if a color mask should be applied; false otherwise. + * @return an error message if there was a problem setting whether or not to + * use a mask; an empty error message otherwise. + */ + QString setUseMask( bool useMask ); + /** * Change the pan of the current image. * @param imgX the x-coordinate for the center of the pan. @@ -461,6 +481,9 @@ private slots: void _contoursChanged(); + QString _getPreferencesId() const; + + void _gridChanged( const Carta::State::StateInterface& state, bool applyAll ); //Refresh the view based on the latest data selection information. @@ -468,14 +491,14 @@ private slots: void _loadView( bool newClips = false ); /** - * The rendering service has finished and produced a new QImage for display. + * Notification that a stack layer has changed its image or vector graphics. */ - void _renderingDone( QImage img ); + void _scheduleFrameRepaint(); /** * The view has been resized. */ - void _viewResize( const QSize& newSize ); + void _viewResize(); /** * Schedule a frame reload event. @@ -513,7 +536,10 @@ private slots: //Get the actual data index of the selection with the given index. This method //takes into account that some images may be hidden, i.e., temporarily not seen //on the stack. - int _getDataIndex( ) const; + int _getIndexCurrent( ) const; + + //Get the actual index of the passed in data. + int _getIndexData( ControllerData* cd ) const; //Provide default values for state. void _initializeState(); @@ -527,7 +553,6 @@ private slots: void _render(); void _saveRegions(); - void _scheduleFrameRepaint( const QImage& img ); /** * Set whether or not the selected layers should be using the global @@ -569,6 +594,7 @@ private slots: static const QString CENTER; static const QString POINTER_MOVE; static const QString STACK_SELECT_AUTO; + static const QString VIEW; static const QString ZOOM; //Data Selections @@ -576,7 +602,7 @@ private slots: std::vector m_selects; //Data View - std::shared_ptr m_view; + std::shared_ptr m_view; std::shared_ptr m_gridControls; std::shared_ptr m_contourControls; @@ -600,8 +626,6 @@ private slots: //everyone wants to listen to them. Carta::State::StateInterface m_stateMouse; - QSize m_viewSize; - bool m_reloadFrameQueued; bool m_repaintFrameQueued; diff --git a/carta/cpp/core/Data/Image/ControllerData.cpp b/carta/cpp/core/Data/Image/ControllerData.cpp index 8639b348..5756bc0c 100755 --- a/carta/cpp/core/Data/Image/ControllerData.cpp +++ b/carta/cpp/core/Data/Image/ControllerData.cpp @@ -29,7 +29,9 @@ namespace Carta { namespace Data { const QString ControllerData::CLASS_NAME = "ControllerData"; +const QString ControllerData::APPLY = "apply"; const QString ControllerData::LAYER = "layer"; +const QString ControllerData::MASK = "mask"; const QString ControllerData::SELECTED = "selected"; class ControllerData::Factory : public Carta::State::CartaObjectFactory { @@ -109,8 +111,6 @@ std::vector ControllerData::_getAxisZTypes() const { return axisTypes; } - - std::vector ControllerData::_getAxisTypes() const { std::vector axisTypes; if ( m_dataSource ){ @@ -119,6 +119,19 @@ std::vector ControllerData::_getAxisTypes() const { return axisTypes; } + +QPointF ControllerData::_getCenter() const{ + QPointF center; + if ( m_dataSource ){ + center = m_dataSource->_getCenter(); + } + return center;; +} + +std::shared_ptr ControllerData::_getColorState(){ + return m_stateColor; +} + QStringList ControllerData::_getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system, const std::vector& frames ) const{ QStringList coordStr; @@ -146,63 +159,6 @@ QString ControllerData::_getCursorText( int mouseX, int mouseY, const std::vecto } -QPointF ControllerData::_getCenter() const{ - QPointF center; - if ( m_dataSource ){ - center = m_dataSource->_getCenter(); - } - return center;; -} - -std::shared_ptr ControllerData::_getColorState(){ - return m_stateColor; -} - -Carta::State::StateInterface ControllerData::_getGridState() const { - return m_dataGrid->_getState(); -} - -QPointF ControllerData::_getImagePt( QPointF screenPt, bool* valid ) const { - QPointF imagePt; - if ( m_dataSource ){ - imagePt = m_dataSource->_getImagePt( screenPt, valid ); - } - else { - *valid = false; - } - return imagePt; -} - -QString ControllerData::_getPixelValue( double x, double y, const std::vector& frames ) const { - QString pixelValue = ""; - if ( m_dataSource ){ - pixelValue = m_dataSource->_getPixelValue( x, y, frames ); - } - return pixelValue; -} - - -QPointF ControllerData::_getScreenPt( QPointF imagePt, bool* valid ) const { - QPointF screenPt; - if ( m_dataSource ){ - screenPt = m_dataSource->_getScreenPt( imagePt, valid ); - } - else { - *valid = false; - } - return screenPt; -} - -int ControllerData::_getFrameCount( AxisInfo::KnownType type ) const { - int frameCount = 1; - if ( m_dataSource ){ - frameCount = m_dataSource->_getFrameCount( type ); - } - return frameCount; -} - - - int ControllerData::_getDimension( int coordIndex ) const { int dim = -1; if ( m_dataSource ){ @@ -228,6 +184,18 @@ QString ControllerData::_getFileName() const { return fileName; } +int ControllerData::_getFrameCount( AxisInfo::KnownType type ) const { + int frameCount = 1; + if ( m_dataSource ){ + frameCount = m_dataSource->_getFrameCount( type ); + } + return frameCount; +} + +Carta::State::StateInterface ControllerData::_getGridState() const { + return m_dataGrid->_getState(); +} + std::shared_ptr ControllerData::_getImage(){ std::shared_ptr image; if ( m_dataSource ){ @@ -236,16 +204,18 @@ std::shared_ptr ControllerData::_getImage(){ return image; } - -std::shared_ptr ControllerData::_getPipeline() const { - std::shared_ptr pipeline; +QPointF ControllerData::_getImagePt( QPointF screenPt, bool* valid ) const { + QPointF imagePt; if ( m_dataSource ){ - pipeline = m_dataSource->_getPipeline(); + imagePt = m_dataSource->_getImagePt( screenPt, valid ); } - return pipeline; - + else { + *valid = false; + } + return imagePt; } + bool ControllerData::_getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const { bool intensityFound = false; if ( m_dataSource ){ @@ -254,6 +224,44 @@ bool ControllerData::_getIntensity( int frameLow, int frameHigh, double percenti return intensityFound; } +QString ControllerData::_getLayerString() const { + QStringList longNames; + longNames.append( _getFileName() ); + + DataLoader* dataLoader = Util::findSingletonObject(); + QStringList shortNames = dataLoader->getShortNames( longNames ); + + Carta::State::StateInterface layerState( m_state); + layerState.setValue( DataSource::DATA_PATH, shortNames[0]); + return layerState.toString(); +} + +float ControllerData::_getMaskAlpha() const { + QString key = Carta::State::UtilState::getLookup( MASK, Util::ALPHA ); + float maskInt = m_state.getValue( key ); + float mask = maskInt / Util::MAX_COLOR; + return mask; +} + +quint32 ControllerData::_getMaskColor() const { + QString redKey = Carta::State::UtilState::getLookup( MASK, Util::RED ); + int redColor = m_state.getValue( redKey ); + QString greenKey = Carta::State::UtilState::getLookup( MASK, Util::GREEN ); + int greenColor = m_state.getValue( greenKey ); + QString blueKey = Carta::State::UtilState::getLookup( MASK, Util::BLUE ); + int blueColor = m_state.getValue( blueKey ); + QRgb rgbCol = qRgba( redColor, greenColor, blueColor, 255 ); + return rgbCol; +} + +QSize ControllerData::_getOutputSize() const { + QSize size; + if ( m_dataSource ){ + size = m_dataSource-> _getOutputSize(); + } + return size; +} + double ControllerData::_getPercentile( int frameLow, int frameHigh, double intensity ) const { double percentile = 0; if ( m_dataSource ){ @@ -262,6 +270,14 @@ double ControllerData::_getPercentile( int frameLow, int frameHigh, double inten return percentile; } +std::shared_ptr ControllerData::_getPipeline() const { + std::shared_ptr pipeline; + if ( m_dataSource ){ + pipeline = m_dataSource->_getPipeline(); + } + return pipeline; +} + QStringList ControllerData::_getPixelCoordinates( double ra, double dec ) const{ QStringList result(""); if ( m_dataSource ){ @@ -278,11 +294,40 @@ QString ControllerData::_getPixelUnits() const { return units; } +QString ControllerData::_getPixelValue( double x, double y, const std::vector& frames ) const { + QString pixelValue = ""; + if ( m_dataSource ){ + pixelValue = m_dataSource->_getPixelValue( x, y, frames ); + } + return pixelValue; +} + +QImage ControllerData::_getQImage() const { + return m_qimage; +} + +QPointF ControllerData::_getScreenPt( QPointF imagePt, bool* valid ) const { + QPointF screenPt; + if ( m_dataSource ){ + screenPt = m_dataSource->_getScreenPt( imagePt, valid ); + } + else { + *valid = false; + } + return screenPt; +} + QString ControllerData::_getStateString() const{ - QString stateStr = m_state.toString(); + Carta::State::StateInterface copyState( m_state ); + copyState.insertObject( DataGrid::GRID, m_dataGrid->_getState().toString() ); + QString stateStr = copyState.toString(); return stateStr; } +Carta::Lib::VectorGraphics::VGList ControllerData::_getVectorGraphics(){ + return m_vectorGraphics; +} + double ControllerData::_getZoom() const { double zoom = DataSource::ZOOM_DEFAULT; if ( m_dataSource ){ @@ -291,49 +336,40 @@ double ControllerData::_getZoom() const { return zoom; } -QSize ControllerData::_getOutputSize() const { - QSize size; - if ( m_dataSource ){ - size = m_dataSource-> _getOutputSize(); - } - return size; -} - - void ControllerData::_gridChanged( const Carta::State::StateInterface& state, bool renderImage, const std::vector& frames ){ m_dataGrid->_resetState( state ); - m_state.setObject(DataGrid::GRID, m_dataGrid->_getState().toString()); + //m_state.setObject(DataGrid::GRID, m_dataGrid->_getState().toString()); if ( renderImage ){ const Carta::Lib::KnownSkyCS& cs = _getCoordinateSystem(); _render( frames, cs ); } } -QString ControllerData::_getLayerString() const { - QStringList longNames; - longNames.append( _getFileName() ); - - DataLoader* dataLoader = Util::findSingletonObject(); - QStringList shortNames = dataLoader->getShortNames( longNames ); - - Carta::State::StateInterface layerState( ""); - layerState.insertValue( LAYER, shortNames[0] ); - layerState.insertValue( Util::VISIBLE, m_state.getValue(Util::VISIBLE) ); - layerState.insertValue( SELECTED, m_state.getValue(SELECTED ) ); - return layerState.toString(); -} void ControllerData::_initializeState(){ m_state.insertValue(DataSource::DATA_PATH, ""); m_state.insertValue(Util::VISIBLE, true ); m_state.insertValue(SELECTED, false ); - QString gridState = _getGridState().toString(); - m_state.insertObject(DataGrid::GRID, gridState ); + //Color mix + m_state.insertObject( MASK ); + QString applyKey = Carta::State::UtilState::getLookup( MASK, APPLY ); + m_state.insertValue( applyKey, false ); + QString redKey = Carta::State::UtilState::getLookup( MASK, Util::RED ); + m_state.insertValue( redKey, 255 ); + QString greenKey = Carta::State::UtilState::getLookup( MASK, Util::GREEN ); + m_state.insertValue( greenKey, 0 ); + QString blueKey = Carta::State::UtilState::getLookup( MASK, Util::BLUE ); + m_state.insertValue( blueKey, 0 ); + QString alphaKey = Carta::State::UtilState::getLookup( MASK, Util::ALPHA ); + m_state.insertValue( alphaKey, 100 ); } - +bool ControllerData::_isMasked() const { + QString maskApplyKey = Carta::State::UtilState::getLookup( MASK, APPLY ); + return m_state.getValue( maskApplyKey ); +} bool ControllerData::_isSelected() const { return m_state.getValue( SELECTED ); @@ -344,13 +380,9 @@ bool ControllerData::_isVisible() const { } bool ControllerData::_isMatch( const QString& name ) const { - QStringList longNames; - longNames.append( _getFileName() ); - - DataLoader* dataLoader = Util::findSingletonObject(); - QStringList shortNames = dataLoader->getShortNames( longNames ); bool matched = false; - if ( shortNames[0] == name ){ + QString fileName = _getFileName(); + if ( name == fileName ){ matched = true; } return matched; @@ -364,28 +396,12 @@ void ControllerData::_renderingDone( /// \todo we should make sure the jobId matches the last submitted job... m_qimage = image; - - // draw the grid over top - //QTime t; - //t.restart(); - QPainter painter( & m_qimage ); - painter.setRenderHint( QPainter::Antialiasing, true ); - Carta::Lib::VectorGraphics::VGListQPainterRenderer vgRenderer; - if ( m_dataGrid->_isGridVisible() ){ - if ( ! vgRenderer.render( gridVG, painter ) ) { - qWarning() << "could not render grid vector graphics"; - } - //qDebug() << "Grid VG rendered in" << t.elapsed() / 1000.0 << "sec" << "xyz"; - } - //t.restart(); + Carta::Lib::VectorGraphics::VGComposer comp( gridVG ); if ( m_dataContours->isContourDraw()){ - QPen lineColor( QColor( "red" ), 1 ); - lineColor.setCosmetic( true ); - painter.setPen( lineColor ); - // where does 0.5, 0.5 map to? if ( m_dataSource ){ + bool valid1 = false; QPointF p1 = m_dataSource->_getScreenPt( { 0.5, 0.5 }, &valid1 ); @@ -404,19 +420,17 @@ void ControllerData::_renderingDone( double m31 = p1.x() - m11 * 0.5; double m32 = p1.y() - m22 * 0.5; tf.setMatrix( m11, m12, m13, m21, m22, m23, m31, m32, m33 ); - painter.setTransform( tf ); + Carta::Lib::VectorGraphics::VGComposer contourComp; + contourComp.append< Carta::Lib::VectorGraphics::Entries::SetTransform >( tf ); + contourComp.appendList( contourVG); + comp.appendList( contourComp.vgList() ); } } - - if ( ! vgRenderer.render( contourVG, painter ) ) { - qWarning() << "could not render contour vector graphics"; - } - //qDebug() << "Contour VG rendered in" << t.elapsed() / 1000.0 << "sec" << "xyz"; } - + m_vectorGraphics = comp.vgList(); // schedule a repaint with the connector - emit renderingDone( m_qimage ); + emit renderingDone(); } @@ -657,6 +671,52 @@ void ControllerData::_colorChanged(){ } } +bool ControllerData::_setMaskColor( int redAmount, + int greenAmount, int blueAmount, QStringList& result ){ + bool changed = false; + if ( 0 > redAmount || redAmount > 255 ){ + result.append( "Invalid red mask color [0,255]: "+QString::number( redAmount ) ); + } + if ( 0 > greenAmount || greenAmount > 255 ){ + result.append( "Invalid green mask color [0,255]: "+QString::number( greenAmount ) ); + } + if ( 0 > blueAmount || blueAmount > 255 ){ + result.append( "Invalid blue mask color [0,255]: "+QString::number( blueAmount ) ); + } + if ( result.length() == 0 ){ + QString redKey = Carta::State::UtilState::getLookup( MASK, Util::RED ); + int oldRedAmount = m_state.getValue( redKey ); + QString greenKey = Carta::State::UtilState::getLookup( MASK, Util::GREEN ); + int oldGreenAmount = m_state.getValue( greenKey ); + QString blueKey = Carta::State::UtilState::getLookup( MASK, Util::BLUE ); + int oldBlueAmount = m_state.getValue( blueKey ); + if ( redAmount != oldRedAmount || greenAmount != oldGreenAmount || + blueAmount != oldBlueAmount ){ + changed = true; + m_state.setValue( redKey, redAmount ); + m_state.setValue( greenKey, greenAmount ); + m_state.setValue( blueKey, blueAmount ); + } + } + return changed; +} + +bool ControllerData::_setMaskOpacity( int alphaAmount, QString& result ){ + bool changed = false; + if ( 0 > alphaAmount || alphaAmount > 255 ){ + result = "Invalid mask opacity [0,255]:"+QString::number( alphaAmount ); + } + else { + QString lookup = Carta::State::UtilState::getLookup( MASK, Util::ALPHA ); + int oldAlpha = m_state.getValue( lookup ); + if ( oldAlpha != alphaAmount ){ + changed = true; + m_state.setValue( lookup, alphaAmount ); + } + } + return changed; +} + bool ControllerData::_setSelected( bool selected ){ bool stateChanged = false; bool oldSelected = m_state.getValue(SELECTED ); @@ -680,6 +740,17 @@ void ControllerData::_setPan( double imgX, double imgY ){ } } +bool ControllerData::_setUseMask( bool useMask ){ + bool stateChanged = false; + + QString key = Carta::State::UtilState::getLookup( MASK, APPLY); + bool oldUseMask = m_state.getValue( key ); + if ( oldUseMask != useMask ){ + m_state.setValue( key, useMask ); + stateChanged = true; + } + return stateChanged; +} void ControllerData::_setZoom( double zoomAmount){ if ( m_dataSource ){ diff --git a/carta/cpp/core/Data/Image/ControllerData.h b/carta/cpp/core/Data/Image/ControllerData.h index 7d56b56c..9debb26c 100755 --- a/carta/cpp/core/Data/Image/ControllerData.h +++ b/carta/cpp/core/Data/Image/ControllerData.h @@ -66,7 +66,7 @@ Q_OBJECT signals: //Notification that a new image has been produced. - void renderingDone( QImage img); + void renderingDone(); /// Return the result of SaveFullImage() after the image has been rendered /// and a save attempt made. @@ -78,7 +78,7 @@ private slots: //Notification from the rendering service that a new image and assiciated vector //graphics have been produced. - void _renderingDone( QImage image, + void _renderingDone( QImage image, Carta::Lib::VectorGraphics::VGList vgList, Carta::Lib::VectorGraphics::VGList contourList, int64_t jobId ); @@ -107,41 +107,45 @@ private slots: Carta::Lib::AxisLabelInfo _getAxisLabelInfo( int axisIndex, Carta::Lib::AxisInfo::KnownType axisType ) const; /** - * Return the number of frames for the given axis in the image. - * @param type - the axis for which a frame count is needed. - * @return the number of frames for the given axis in the image. + * Return the current pan center. + * @return the centered image location. */ - int _getFrameCount( Carta::Lib::AxisInfo::KnownType type ) const; + QPointF _getCenter() const; /** - * Return the number of dimensions in the image. - * @return the number of image dimensions. + * Return stored information about the color map. + * @return - information about the color map. */ - int _getDimensions() const; + std::shared_ptr _getColorState(); /** - * Returns the location on the image corresponding to a screen point in - * pixels. - * @param screenPt an (x,y) pair of pixel coordinates. - * @param valid set to true if an image is loaded that can do the translation; otherwise false; - * @return the corresponding location on the image. + * Return the coordinates at pixel (x, y) in the given coordinate system. + * @param x the x-coordinate of the desired pixel. + * @param y the y-coordinate of the desired pixel. + * @param system the desired coordinate system. + * @param frames - list of image frames. + * @param system - an enumerated coordinate system type. + * @return the coordinates at pixel (x, y). */ - QPointF _getImagePt( QPointF screenPt, bool* valid ) const; - + QStringList _getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system, + const std::vector& frames) const; + /** - * Returns the location on the screen corresponding to a location in image coordinates. - * @param imagePt an (x,y) pair of image coordinates. - * @param valid set to true if an image is loaded that can do the translation; otherwise false; - * @return the corresponding pixel coordinates. + * Return the coordinate system in use. + * @return - an enumerated coordinate system type. */ - QPointF _getScreenPt( QPointF imagePt, bool* valid ) const; + Carta::Lib::KnownSkyCS _getCoordinateSystem() const; /** - * Return the current pan center. - * @return the centered image location. + * Returns information about the image at the current location of the cursor. + * @param mouseX the mouse x-position in screen coordinates. + * @param mouseY the mouse y-position in screen coordinates. + * @param frames - list of image frames. + * @return a QString containing cursor text. */ - QPointF _getCenter() const; - + QString _getCursorText( int mouseX, int mouseY, const std::vector& frames ); + + /** * Return the image size for the given coordinate index. @@ -150,39 +154,45 @@ private slots: */ int _getDimension( int coordIndex ) const; - //Return data source state. - Carta::State::StateInterface _getGridState() const; - QString _getStateString() const; + /** + * Return the number of dimensions in the image. + * @return the number of image dimensions. + */ + int _getDimensions() const; /** - * Returns the underlying image. + * Return the number of frames for the given axis in the image. + * @param type - the axis for which a frame count is needed. + * @return the number of frames for the given axis in the image. */ - std::shared_ptr _getImage(); + int _getFrameCount( Carta::Lib::AxisInfo::KnownType type ) const; + + + //Return data source state. + Carta::State::StateInterface _getGridState() const; /** * Returns the image's file name. * @return the path to the image. */ QString _getFileName() const; - + /** - * Returns information about the image at the current location of the cursor. - * @param mouseX the mouse x-position in screen coordinates. - * @param mouseY the mouse y-position in screen coordinates. - * @param frames - list of image frames. - * @return a QString containing cursor text. + * Returns the underlying image. */ - QString _getCursorText( int mouseX, int mouseY, const std::vector& frames ); + std::shared_ptr _getImage(); + + QImage _getQImage() const; /** - * Return the percentile corresponding to the given intensity. - * @param frameLow a lower bound for the frame index or -1 if there is no lower bound. - * @param frameHigh an upper bound for the frame index or -1 if there is no upper bound. - * @param intensity a value for which a percentile is needed. - * @return the percentile corresponding to the intensity. + * Returns the location on the image corresponding to a screen point in + * pixels. + * @param screenPt an (x,y) pair of pixel coordinates. + * @param valid set to true if an image is loaded that can do the translation; otherwise false; + * @return the corresponding location on the image. */ - double _getPercentile( int frameLow, int frameHigh, double intensity ) const; - + QPointF _getImagePt( QPointF screenPt, bool* valid ) const; + /** * Returns the intensity corresponding to a given percentile. * @param frameLow a lower bound for the image frames or -1 if there is no lower bound. @@ -193,29 +203,36 @@ private slots: */ bool _getIntensity( int frameLow, int frameHigh, double percentile, double* intensity ) const; + /** * Returns information about this layer in the stack. * @return - a string representation of layer specific information. */ QString _getLayerString() const; + float _getMaskAlpha() const; + quint32 _getMaskColor() const; /** - * Returns the pipeline responsible for rendering the image. - * @retun the pipeline responsible for rendering the image. + * Get the dimensions of the image viewer (window size). + * @return the image viewer dimensions. */ - std::shared_ptr _getPipeline() const; + QSize _getOutputSize() const; /** - * Return the zoom factor for this image. - * @return the zoom multiplier. + * Return the percentile corresponding to the given intensity. + * @param frameLow a lower bound for the frame index or -1 if there is no lower bound. + * @param frameHigh an upper bound for the frame index or -1 if there is no upper bound. + * @param intensity a value for which a percentile is needed. + * @return the percentile corresponding to the intensity. */ - double _getZoom() const; + double _getPercentile( int frameLow, int frameHigh, double intensity ) const; /** - * Get the dimensions of the image viewer (window size). - * @return the image viewer dimensions. + * Returns the pipeline responsible for rendering the image. + * @retun the pipeline responsible for rendering the image. */ - QSize _getOutputSize() const; + std::shared_ptr _getPipeline() const; + /** * Return the pixel coordinates corresponding to the given world coordinates. @@ -226,6 +243,12 @@ private slots: */ QStringList _getPixelCoordinates( double ra, double dec ) const; + /** + * Return the units of the pixels. + * @return the units of the pixels, or blank if units could not be obtained. + */ + QString _getPixelUnits() const; + /** * Return the value of the pixel at (x, y). * @param x the x-coordinate of the desired pixel @@ -239,34 +262,23 @@ private slots: QString _getPixelValue( double x, double y, const std::vector& frames ) const; /** - * Return the units of the pixels. - * @return the units of the pixels, or blank if units could not be obtained. + * Returns the location on the screen corresponding to a location in image coordinates. + * @param imagePt an (x,y) pair of image coordinates. + * @param valid set to true if an image is loaded that can do the translation; otherwise false; + * @return the corresponding pixel coordinates. */ - QString _getPixelUnits() const; + QPointF _getScreenPt( QPointF imagePt, bool* valid ) const; - /** - * Return stored information about the color map. - * @return - information about the color map. - */ - std::shared_ptr _getColorState(); + QString _getStateString() const; - /** - * Return the coordinates at pixel (x, y) in the given coordinate system. - * @param x the x-coordinate of the desired pixel. - * @param y the y-coordinate of the desired pixel. - * @param system the desired coordinate system. - * @param frames - list of image frames. - * @param system - an enumerated coordinate system type. - * @return the coordinates at pixel (x, y). - */ - QStringList _getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system, - const std::vector& frames) const; + Carta::Lib::VectorGraphics::VGList _getVectorGraphics(); /** - * Return the coordinate system in use. - * @return - an enumerated coordinate system type. + * Return the zoom factor for this image. + * @return the zoom multiplier. */ - Carta::Lib::KnownSkyCS _getCoordinateSystem() const; + double _getZoom() const; + void _gridChanged( const Carta::State::StateInterface& state, bool renderImage, const std::vector& frames ); @@ -281,24 +293,28 @@ private slots: void _initializeState(); void _initializeSingletons( ); + + bool _isMasked() const; + + /** + * Returns true if the name identifies this layer; false otherwise. + * @return true if the name identifies this layer; false otherwise. + */ + bool _isMatch( const QString& name ) const; + /** * Returns true if this data is selected; false otherwise. * @return true if this data is selected; false otherwise. */ bool _isSelected() const; + /** * Returns true if this layer is not hidden; false otherwise. * @return true if the layer is visible; false otherwise. */ bool _isVisible() const; - /** - * Returns true if the name identifies this layer; false otherwise. - * @return true if the name identifies this layer; false otherwise. - */ - bool _isMatch( const QString& name ) const; - /** * Loads the data source as a QImage. * @param frames - list of frames to load, one for each of the known axis types. @@ -343,6 +359,12 @@ private slots: */ QString _saveImage( const QString& saveName, double scale, const std::vector& frames ); + /** + * Reset the color map information for this data. + * @param colorState - stored information about the color map. + */ + void _setColorMapGlobal( std::shared_ptr colorState ); + /** * Set contour set to be rendered. * @param contours - the rendered contour set. @@ -350,18 +372,34 @@ private slots: //Note: The rendered contour set is an accumulation of all the contour sets. void _setContours( std::shared_ptr contours ); + /** - * Reset the color map information for this data. - * @param colorState - stored information about the color map. + * Returns whether or not the data was successfully loaded. + * @param fileName an identifier for the location of a data source. + * @return true if the data souce was successfully loaded; false otherwise. */ - void _setColorMapGlobal( std::shared_ptr colorState ); + bool _setFileName( const QString& fileName ); + /** + * Set the color to use for the mask. + * @param redAmount - the amount of red in [0,255]. + * @param greenAmount - the amount of green in [0,255]. + * @param blueAmount - the amount of blue in [0,255]. + * @param result - a list of errors that might have occurred in setting the + * mask color; an empty string otherwise. + * @return - true if the mask color was changed; false otherwise. + */ + bool _setMaskColor( int redAmount, + int greenAmount, int blueAmount, QStringList& result ); /** - * Show/hide this layer. - * @param visible - true to show the layer; false to hide it. + * Set the opacity of the mask. + * @param alphaAmount - the transparency level in [0,255] with 255 being opaque. + * @param result - an error message if there was a problem setting the mask opacity or + * an empty string otherwise. + * @return - true if the mask opacity was changed; false otherwise. */ - void _setVisible( bool visible ); + bool _setMaskOpacity( int alphaAmount, QString& result ); /** * Set the center for this image's display. @@ -371,25 +409,30 @@ private slots: void _setPan( double imgX, double imgY ); /** - * Set the zoom factor for this image. - * @param zoomFactor the zoom multiplier. + * Set this data source selected. + * @param selected - true if the data source is selected; false otherwise. + * @return -true if the selected state changed; false otherwise. */ - void _setZoom( double zoomFactor ); + bool _setSelected( bool selected ); + /** + * Set whether or not to apply a color mask to the image. + * @param useMask - true if a color mask should be applied; false otherwise. + * @return - true if the mask status was changed; false otherwise. + */ + bool _setUseMask( bool useMask ); /** - * Returns whether or not the data was successfully loaded. - * @param fileName an identifier for the location of a data source. - * @return true if the data souce was successfully loaded; false otherwise. + * Show/hide this layer. + * @param visible - true to show the layer; false to hide it. */ - bool _setFileName( const QString& fileName ); + void _setVisible( bool visible ); /** - * Set this data source selected. - * @param selected - true if the data source is selected; false otherwise. - * @return -true if the selected state changed; false otherwise. + * Set the zoom factor for this image. + * @param zoomFactor the zoom multiplier. */ - bool _setSelected( bool selected ); + void _setZoom( double zoomFactor ); /** * Resize the view of the image. @@ -407,7 +450,9 @@ private slots: class Factory; static bool m_registered; + static const QString APPLY; static const QString LAYER; + static const QString MASK; static const QString SELECTED; std::shared_ptr m_stateColor; @@ -426,6 +471,7 @@ private slots: /// Saves images Carta::Core::ImageSaveService::ImageSaveService *m_saveService; QImage m_qimage; + Carta::Lib::VectorGraphics::VGList m_vectorGraphics; ControllerData(const ControllerData& other); ControllerData& operator=(const ControllerData& other); }; diff --git a/carta/cpp/core/MainConfig.cpp b/carta/cpp/core/MainConfig.cpp index 08e8a814..c01477be 100644 --- a/carta/cpp/core/MainConfig.cpp +++ b/carta/cpp/core/MainConfig.cpp @@ -15,6 +15,35 @@ namespace MainConfig { +namespace { +void _storeBool( const QJsonValue& jsonValue, bool* storeLocation, + const QString& typeDescription ){ + QString errorMsg; + *storeLocation = ParsedInfo::toBool( jsonValue, errorMsg ); + if ( !errorMsg.isEmpty() ){ + qWarning() << "Error setting "<< typeDescription<<": "< 0 ){ + *storeLocation = val; + } + else if ( val < -1 ){ + qWarning()<<"Error "< 0 ){ - info.m_histogramBinCountMax = maxBinCount; - } - else { - qWarning()<<"Maximum histogram bin count must be a positive integer."; - } - } - else { - qWarning() << "Maximum histogram bin count must be a number."; - } - - // maximum contour level count - QString contourLevelCountMaxStr = json[ "contourLevelCountMax"].toString(); - int maxContourLevelCount = contourLevelCountMaxStr.toInt( &validInt ); - if ( validInt ){ - if ( maxContourLevelCount > 0 ){ - info.m_contourLevelCountMax = maxContourLevelCount; - } - else { - qWarning()<<"Maximum contour level count must be a positive integer."; - } - } - else { - qWarning() << "Maximum contour level count must be a number."; - } + _storePositiveInt( json["histogramBinCountMax"], &info.m_histogramBinCountMax, "histogram bin count max"); + _storePositiveInt( json["contourLevelCountMax"], &info.m_contourLevelCountMax, "contour level count max"); return info; } @@ -117,6 +104,11 @@ bool ParsedInfo::hacksEnabled() const return m_hacksEnabled; } +bool ParsedInfo::isDeveloperDecorations() const { + return m_developerDecorations; +} + + bool ParsedInfo::isDeveloperLayout() const { return m_developerLayout; } @@ -134,6 +126,49 @@ const QJsonObject &ParsedInfo::json() const return m_json; } + +bool ParsedInfo::toBool( const QJsonValue& jsonValue, QString& errorMsg ){ + bool val = false; + if ( jsonValue.isString() ){ + QString lowerStr = jsonValue.toString().toLower(); + val = ( lowerStr == "yes" || lowerStr == "true" || + lowerStr == "1" || lowerStr == "y"); + } + else if ( jsonValue.isBool() ){ + val = jsonValue.toBool(); + } + else if ( !jsonValue.isUndefined() && !jsonValue.isNull() ){ + errorMsg = "Not a valid boolean."; + } + return val; +} + +int ParsedInfo::toInt( const QJsonValue& jsonValue, QString& errorMsg ){ + int val = -1; + if ( jsonValue.isString() ){ + QString valStr = jsonValue.toString(); + bool validInt = false; + val = valStr.toInt( &validInt ); + if ( !validInt ){ + errorMsg = "Invalid integer type:"+valStr; + } + } + else if ( jsonValue.isDouble() ){ + double doubleVal = jsonValue.toDouble(); + double integerPart; + if ( std::modf( doubleVal, &integerPart) == 0 ){ + val = static_cast( integerPart ); + } + else { + errorMsg = "Number must be an integer: "+ QString::number( doubleVal ); + } + } + else if ( !jsonValue.isUndefined() && !jsonValue.isNull() ){ + errorMsg = "Not a valid integer."; + } + return val; +} + } // namespace MainConfig diff --git a/carta/cpp/core/MainConfig.h b/carta/cpp/core/MainConfig.h index 413dc665..880b2887 100644 --- a/carta/cpp/core/MainConfig.h +++ b/carta/cpp/core/MainConfig.h @@ -49,13 +49,42 @@ class ParsedInfo { */ bool isDeveloperLayout() const; + /** + * Returns whether CARTA should come up with decorations such + * as the Debug console enabled on the desktop version. + * @return true - for developer debug widgets enabled; false otherwise. + */ + bool isDeveloperDecorations() const; + /// the whole config file as json const QJsonObject & json() const; + /** + * Converts a jsonValue to a boolean. + * @param jsonValue - the value to convert. + * @param errorMsg - a place holder for setting an error message if the + * passed in value cannot be converted. + * @return - the corresponding boolean; default is false if there is an + * error doing the conversion. + */ + static bool toBool( const QJsonValue& jsonValue, QString& errorMsg ); + + /** + * Converts a jsonValue to an integer. + * @param jsonValue - the value to convert. + * @param errorMsg - a place holder for setting an error message if the + * passed in value cannot be converted. + * @return - the corresponding integer; default is -1 if there is an + * error doing the conversion. + */ + static int toInt( const QJsonValue& jsonValue, QString& errorMsg ); + + protected: QStringList m_pluginDirectories; bool m_hacksEnabled = false; + bool m_developerDecorations = false; bool m_developerLayout = false; int m_histogramBinCountMax = -1; int m_contourLevelCountMax = -1; @@ -72,4 +101,6 @@ class ParsedInfo { /// ParsedInfo parse( const QString & filePath); + + } diff --git a/carta/cpp/core/State/ObjectManager.cpp b/carta/cpp/core/State/ObjectManager.cpp index 65689ff1..7b342a8c 100644 --- a/carta/cpp/core/State/ObjectManager.cpp +++ b/carta/cpp/core/State/ObjectManager.cpp @@ -8,6 +8,7 @@ #include "ObjectManager.h" #include "Globals.h" #include "UtilState.h" +#include "CartaLib/IRemoteVGView.h" #include #include #include @@ -138,6 +139,10 @@ void CartaObject::unregisterView() conn()-> unregisterView( m_path +"/view" ); } +std::shared_ptr CartaObject::makeRemoteView( const QString& path ){ + return Carta::Lib::LayeredRemoteVGView::create( conn(), path ); +} + QString CartaObject::getStateLocation( const QString& name ) const { return conn()-> getStateLocation( name ); diff --git a/carta/cpp/core/State/ObjectManager.h b/carta/cpp/core/State/ObjectManager.h index 594ff7a0..ab5dc303 100644 --- a/carta/cpp/core/State/ObjectManager.h +++ b/carta/cpp/core/State/ObjectManager.h @@ -14,6 +14,12 @@ #include "StateInterface.h" #include "../IConnector.h" +namespace Carta { + namespace Lib { + class LayeredRemoteVGView; + } +} + namespace Carta { namespace State { @@ -105,6 +111,12 @@ class CartaObject { /// unregister a view with the connector void unregisterView(); + /** + * Construct a layered view and return it. + * @param path - a unique identifier for the remote view. + */ + std::shared_ptr makeRemoteView( const QString& path ); + //Return the full location for the state with the given name. QString getStateLocation( const QString& name ) const; diff --git a/carta/cpp/desktop/MainWindow.cpp b/carta/cpp/desktop/MainWindow.cpp index f428a6d0..15ee9ab4 100644 --- a/carta/cpp/desktop/MainWindow.cpp +++ b/carta/cpp/desktop/MainWindow.cpp @@ -46,8 +46,8 @@ MainWindow::MainWindow( ) & QWebFrame::javaScriptWindowObjectCleared, this, & MainWindow::addToJavaScript ); - - if( ! Globals::instance()->mainConfig()->json().value( "qtDecorations").toBool()) { + bool qtDecorationsEnabled = Globals::instance()->mainConfig()->isDeveloperDecorations(); + if( !qtDecorationsEnabled ) { menuBar()->setVisible( false); toolBar->setVisible( false); statusBar()->setVisible( false); diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataClose.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataClose.js index 527ce2e0..4cc7f8b3 100644 --- a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataClose.js +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataClose.js @@ -46,7 +46,7 @@ qx.Class.define("skel.Command.Data.CommandDataClose", { if ( activeWins[i].isCmdSupported( dataCmd ) ){ var closes = activeWins[i].getDatas(); for ( var j = 0; j < closes.length; j++ ){ - this.m_cmds[k] = new skel.Command.Data.CommandDataCloseImage( closes[j].layer ); + this.m_cmds[k] = new skel.Command.Data.CommandDataCloseImage( closes[j].file ); k++; } } diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHide.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHide.js index e0beac69..80a0a1e7 100644 --- a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHide.js +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataHide.js @@ -47,7 +47,7 @@ qx.Class.define("skel.Command.Data.CommandDataHide", { var closes = activeWins[i].getDatas(); for ( var j = 0; j < closes.length; j++ ){ if ( closes[j].visible ){ - this.m_cmds[k] = new skel.Command.Data.CommandDataHideImage( closes[j].layer, j); + this.m_cmds[k] = new skel.Command.Data.CommandDataHideImage( closes[j].file, j); k++; } } diff --git a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js index 09eda653..6b9715d5 100644 --- a/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js +++ b/carta/html5/common/skel/source/class/skel/Command/Data/CommandDataShow.js @@ -47,7 +47,7 @@ qx.Class.define("skel.Command.Data.CommandDataShow", { var closes = activeWins[i].getDatas(); for ( var j = 0; j < closes.length; j++ ){ if ( !closes[j].visible ){ - this.m_cmds[k] = new skel.Command.Data.CommandDataShowImage( closes[j].layer, j); + this.m_cmds[k] = new skel.Command.Data.CommandDataShowImage( closes[j].file, j); k++; } } diff --git a/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js b/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js index 645d99ef..23fb9d92 100644 --- a/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js +++ b/carta/html5/common/skel/source/class/skel/boundWidgets/View/View.js @@ -79,16 +79,13 @@ qx.Class.define( "skel.boundWidgets.View.View", { // callback for appear event _appearCB: function() { - if ( !this.m_appeared ){ - this.m_iview = this.m_connector.registerViewElement( - this.getContentElement().getDomElement(), this.m_viewName ); - - this.m_iview.updateSize(); - this.m_iview.addViewCallback( this._iviewRefreshCB.bind( this ) ); - this.setQuality( this.m_quality); - this.m_appeared = true; - } - } + this.m_iview = this.m_connector.registerViewElement( + this.getContentElement().getDomElement(), this.m_viewName ); + + this.m_iview.updateSize(); + this.m_iview.addViewCallback( this._iviewRefreshCB.bind( this ) ); + this.setQuality( this.m_quality); + }, // callback for iView refresh _iviewRefreshCB : function() { @@ -131,12 +128,7 @@ qx.Class.define( "skel.boundWidgets.View.View", { /** * @type {Connector} cached instance of the connector */ - m_connector: null, - - /** - * Prevent re-registration when we are adding removing views. - */ - m_appeared: false + m_connector: null }, destruct: function() diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorScale.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorScale.js index a3455c1c..b6acab3b 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorScale.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/ColorScale.js @@ -56,10 +56,19 @@ qx.Class.define("skel.widgets.Colormap.ColorScale", { * Initializes the UI. */ _init : function( ) { - var widgetLayout = new qx.ui.layout.VBox(); + var widgetLayout = new qx.ui.layout.HBox(); this._setLayout(widgetLayout); - this._add( new qx.ui.core.Spacer(), {flex:1}); + this._add( new qx.ui.core.Spacer(), {flex:1}); + this._initInvertReverse(); + this._add( new qx.ui.core.Spacer(), {flex:1}); + + }, + + /** + * Initialize the revert & invert controls. + */ + _initInvertReverse : function(){ this.m_invertCheck = new qx.ui.form.CheckBox( "Invert"); this.m_invertCheck.setToolTipText( "Invert the colors in the map."); this.m_invertCheck.addListener( skel.widgets.Path.CHANGE_VALUE, function(e){ @@ -82,24 +91,11 @@ qx.Class.define("skel.widgets.Colormap.ColorScale", { var mapComposite = new qx.ui.container.Composite(); mapComposite.setLayout(new qx.ui.layout.VBox(1)); - var revComp = new qx.ui.container.Composite(); - revComp.setLayout( new qx.ui.layout.HBox()); - revComp.add( this.m_reverseCheck ); - revComp.add( new qx.ui.core.Spacer(1), {flex:1}); - mapComposite.add( revComp ); - - var invertComp = new qx.ui.container.Composite(); - invertComp.setLayout( new qx.ui.layout.HBox()); - invertComp.add( this.m_invertCheck ); - invertComp.add( new qx.ui.core.Spacer(1), {flex:1}); - mapComposite.add( invertComp ); - + mapComposite.add( this.m_reverseCheck ); + mapComposite.add( this.m_invertCheck ); this._add( mapComposite ); - this._add( new qx.ui.core.Spacer(), {flex:1}); }, - - /** * Set whether or not to invert the color map. * @param invertMap {boolean} true if the map should be inverted; false otherwise. diff --git a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js index 4205a172..13b42556 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Colormap/PageColorMap.js @@ -25,13 +25,13 @@ qx.Class.define("skel.widgets.Colormap.PageColorMap", { _init : function( ) { this.setPadding( 0, 0, 0, 0 ); this.setMargin( 1, 1, 1, 1 ); - this._setLayout(new qx.ui.layout.HBox(2)); + this._setLayout(new qx.ui.layout.VBox(2)); this.m_scaleSettings = new skel.widgets.Colormap.ColorScale(); this.m_colorMixSettings = new skel.widgets.Colormap.ColorMix(); + this.add( this.m_colorMixSettings); this.add( this.m_scaleSettings ); - this.add( this.m_colorMixSettings, {flex:1} ); }, diff --git a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/TextSlider.js b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/TextSlider.js index 2c6ed062..aac50c01 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/TextSlider.js +++ b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/TextSlider.js @@ -187,7 +187,7 @@ qx.Class.define("skel.widgets.CustomUI.TextSlider", { var params = this.m_paramId + ":"+percentValue; this.m_connector.sendCommand( cmd, params, this._errorCB(this)); } - else { + if ( this.m_connector === null || this.m_id === null || this.m_notify ){ var data = { "value" : value } @@ -275,6 +275,15 @@ qx.Class.define("skel.widgets.CustomUI.TextSlider", { this.m_id = id; }, + /** + * Set whether not to fire events when the value changes. + * @param notify {boolean} - true if events should be fired when the + * value changes. + */ + setNotify : function( notify ){ + this.m_notify = notify; + }, + /** * Set this widget enabled/disabled. * @param enabled {boolean} - true to enable the widget; false otherwise. @@ -288,6 +297,7 @@ qx.Class.define("skel.widgets.CustomUI.TextSlider", { m_paramId : null, m_logScale : false, m_normalize : false, + m_notify : false, m_text : null, m_slider : null, m_listenerId : null, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js index 4a47bdc8..f9fda4ca 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/DragDropList.js @@ -303,7 +303,7 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { var dataCount = items.length; var selectedItems = []; for ( var i = 0; i < dataCount; i++ ){ - var listItem = new qx.ui.form.ListItem( items[i].layer ); + var listItem = new qx.ui.form.ListItem( items[i].file ); this.m_list.add( listItem ); var visible = items[i].visible; if ( items[i].selected ){ @@ -312,7 +312,7 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { var contextMenu = new qx.ui.menu.Menu(); //Close button - var closeCmd = new skel.Command.Data.CommandDataCloseImage( items[i].layer ); + var closeCmd = new skel.Command.Data.CommandDataCloseImage( items[i].file ); var closeButton = new qx.ui.menu.Button( "Close"); closeButton.addListener( "execute", function(){ this.doAction( true, function(){} ); @@ -321,7 +321,7 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { if ( visible ){ //Hide button - var hideCmd = new skel.Command.Data.CommandDataHideImage( items[i].layer, i ); + var hideCmd = new skel.Command.Data.CommandDataHideImage( items[i].file, i ); var hideButton = new qx.ui.menu.Button( "Hide"); hideButton.addListener( "execute", function(){ this.doAction( true, function(){}); @@ -330,7 +330,7 @@ qx.Class.define("skel.widgets.Image.Stack.DragDropList", { } else { //Show button - var showCmd = new skel.Command.Data.CommandDataShowImage( items[i].layer, i ); + var showCmd = new skel.Command.Data.CommandDataShowImage( items[i].file, i ); var showButton = new qx.ui.menu.Button( "Show"); showButton.addListener( "execute", function(){ this.doAction( true, function(){}); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js new file mode 100755 index 00000000..e287caaa --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js @@ -0,0 +1,107 @@ +/** + * Controls for setting an image mask. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + + +qx.Class.define("skel.widgets.Image.Stack.MaskControls", { + extend : qx.ui.core.Widget, + + /** + * Constructor. + */ + construct : function( ) { + this.base(arguments); + this.m_connector = mImport("connector"); + this._init( ); + }, + + + members : { + + /* + * Initializes the UI. + */ + _init : function( ) { + this._setLayout( new qx.ui.layout.VBox(1) ); + this.m_content = new qx.ui.groupbox.GroupBox( /*"Mask"*/); + this.m_content.setLayout( new qx.ui.layout.VBox(1) ); + this._add( this.m_content ); + this._initCheck(); + this._initMask(); + }, + + /** + * Initialize UI as to whether or not to apply a mask. + */ + _initCheck : function(){ + var checkContainer = new qx.ui.container.Composite(); + checkContainer.setLayout( new qx.ui.layout.HBox(1) ); + this.m_applyCheck = new qx.ui.form.CheckBox( "Apply Mask" ); + this.m_applyCheck.setToolTipText( "Apply a color/transparency mask to the selected layer(s)." ); + this.m_applyId = this.m_applyCheck.addListener( "changeValue", this._sendApplyCmd, this ); + checkContainer.add( new qx.ui.core.Spacer(1), {flex:1} ); + checkContainer.add( this.m_applyCheck ); + checkContainer.add( new qx.ui.core.Spacer(1), {flex:1} ); + this.m_content.add( checkContainer ); + }, + + + /** + * Initialize the mask color controls. + */ + _initMask : function(){ + this.m_maskColor = new skel.widgets.Image.Stack.MaskControlsColor(); + this.m_content.add( this.m_maskColor ); + }, + + /** + * Notify the server as to whether a mask should be applied to the selected + * layers. + */ + _sendApplyCmd : function(){ + if ( this.m_id !== null ){ + var apply = this.m_applyCheck.getValue(); + var params = "apply:"+apply; + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setUseMask"; + this.m_connector.sendCommand( cmd, params, function(){}); + } + }, + + /** + * Update the UI with server information. + * @param mask {Object} - information from server about the mask. + */ + setControls : function(mask){ + this.m_applyCheck.removeListenerById( this.m_applyId ); + this.m_applyCheck.setValue( mask.apply ); + this.m_applyId = this.m_applyCheck.addListener( "changeValue", this._sendApplyCmd, this ); + + this.m_maskColor.setControls( mask ); + this.m_maskColor.setControlsEnabled( mask.apply ); + }, + + /** + * Set the server-side id of the object that handles masking of + * images. + * @param id {String} - server-side id for mask controls. + */ + setId : function( id ){ + this.m_id = id; + this.m_maskColor.setId( id ); + }, + + m_applyCheck : null, + m_applyId : null, + m_connector : null, + m_content : null, + m_id : null, + + m_maskColor : null + + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js new file mode 100755 index 00000000..33bc2d1c --- /dev/null +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js @@ -0,0 +1,322 @@ +/** + * Controls for setting the mask color and transparency. + */ +/*global mImport */ +/******************************************************************************* + * @ignore( mImport) + ******************************************************************************/ + +qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { + extend : qx.ui.core.Widget, + + /** + * Constructor. + */ + construct : function( ) { + this.base(arguments); + this.m_connector = mImport("connector"); + this._init( ); + }, + + members : { + + /** + * Internal method for adding a control that allows setting of a primary + * color to the UI. + * @param parent {qx.ui.container.Composite} - the container for the control. + * @param colorRgb {qx.ui.form.Spinner} - the control to add. + * @param labelText {String} - descriptive text for the control. + * @param rowIndex {Number} - layout row index. + */ + _addColorRgb : function( parent, colorRgb, labelText, rowIndex ){ + var label = new qx.ui.basic.Label( labelText ); + parent.add( label, {row:rowIndex, column:0} ); + parent.add( colorRgb, {row:rowIndex, column:1} ); + }, + + /* + * Initializes the UI. + */ + _init : function( ) { + this._setLayout( new qx.ui.layout.VBox(1) ); + this._initPresets(); + this._add( new qx.ui.core.Spacer(1), {flex:1} ); + this._initRgbs(); + this._initOpacity(); + this._initPreview(); + }, + + /** + * Initialize the opacity control. + */ + _initOpacity : function(){ + var transContainer = new qx.ui.container.Composite(); + transContainer.setLayout( new qx.ui.layout.HBox(1)); + this.m_transparency = new skel.widgets.CustomUI.TextSlider("setMaskOpacity", "alpha", + 0, skel.widgets.Path.MAX_RGB, 0, + "Opacity", false, + "Set the mask opacity.", "Slide to set the grid opacity.", + "maskOpacityTextField", "maskOpacitySlider", false); + this.m_transparency.setNotify( true ); + this.m_transparency.addListener( "textSliderChanged", this._setPreviewColor, this ); + transContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); + transContainer.add( this.m_transparency ); + transContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); + this._add( transContainer ); + }, + + /** + * Initialize the preset colors. + */ + _initPresets : function(){ + var presetContainer = new qx.ui.container.Composite(); + presetContainer.setLayout( new qx.ui.layout.HBox(1)); + this.m_presetRed = this._makePreset( "#ff0000" ); + this.m_presetRed.setToolTipText( "Apply a red color mask to the selected layer(s)."); + this.m_presetGreen = this._makePreset( "#00ff00"); + this.m_presetGreen.setToolTipText( "Apply a green color mask to the selected layer(s)."); + this.m_presetBlue = this._makePreset( "#0000ff"); + this.m_presetBlue.setToolTipText( "Apply a blue color mask to the selected layer(s)."); + this.m_presetYellow = this._makePreset( "#ffff00"); + this.m_presetYellow.setToolTipText( "Apply a yellow color mask to the selected layer(s)."); + this.m_presetRed.addListener( "mousedown", this._presetRedSelected, this ); + this.m_presetGreen.addListener( "mousedown", this._presetGreenSelected, this ); + this.m_presetBlue.addListener( "mousedown", this._presetBlueSelected, this ); + this.m_presetYellow.addListener( "mousedown", this._presetYellowSelected, this ); + presetContainer.add( this.m_presetRed ); + presetContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); + presetContainer.add( this.m_presetGreen ); + presetContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); + presetContainer.add( this.m_presetBlue ); + presetContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); + presetContainer.add( this.m_presetYellow ); + + this._add( presetContainer ); + }, + + /** + * Initialize controls for setting RGB. + */ + _initRgbs : function(){ + var rgbContainer = new qx.ui.container.Composite(); + rgbContainer.setLayout( new qx.ui.layout.Grid()); + this.m_spinRed = new qx.ui.form.Spinner( 0, 0, 255 ); + this.m_spinRed.setToolTipText( "Set the red mask color amount in the selected layer(s)."); + this.m_spinRedId = this.m_spinRed.addListener("changeValue", this._primaryColorChanged, this ); + this.m_spinBlue = new qx.ui.form.Spinner( 0, 0, 255 ); + this.m_spinBlue.setToolTipText( "Set the blue mask color amount in the selected layer(s)."); + this.m_spinBlueId = this.m_spinBlue.addListener( "changeValue", this._primaryColorChanged, this ); + this.m_spinGreen = new qx.ui.form.Spinner( 0, 0, 255 ); + this.m_spinGreen.setToolTipText( "Set the green mask color amount in the selected layer(s)."); + this.m_spinGreenId = this.m_spinGreen.addListener( "changeValue", this._primaryColorChanged, this ); + this._addColorRgb( rgbContainer, this.m_spinRed, "Red:", 0); + this._addColorRgb( rgbContainer, this.m_spinGreen, "Green:", 1); + this._addColorRgb( rgbContainer, this.m_spinBlue, "Blue:", 2); + this._add( rgbContainer ); + }, + + /** + * Initialize the color preview. + */ + _initPreview : function(){ + this.m_preview = new qx.ui.core.Widget(); + this.m_preview.setDecorator( this.m_BORDER_LINE ); + this.m_preview.setBackgroundColor( "#000000"); + this.m_preview.setWidth( 100 ); + this.m_preview.setHeight( 20 ); + this._add( this.m_preview ); + }, + + /** + * Construct a square for a preset color. + * @param colorParam {String} - the preset color. + */ + _makePreset : function( colorParam ){ + var preset = new qx.ui.core.Widget(); + preset.setBackgroundColor( colorParam ); + preset.setWidth( 15 ); + preset.setHeight( 15 ); + return preset; + }, + + /** + * Update the UI based on a preset color of red. + */ + _presetRedSelected : function(){ + this._setRgbColors( 255, 0, 0 ); + this.m_presetRed.setDecorator( this.m_BORDER_LINE ); + this.m_presetGreen.setDecorator( this.m_BORDER_NONE ); + this.m_presetBlue.setDecorator( this.m_BORDER_NONE ); + this.m_presetYellow.setDecorator( this.m_BORDER_NONE ); + }, + + /** + * Update the UI based on a preset color of green. + */ + _presetGreenSelected : function(){ + this._setRgbColors( 0, 255, 0 ); + this.m_presetRed.setDecorator( this.m_BORDER_NONE ); + this.m_presetGreen.setDecorator( this.m_BORDER_LINE ); + this.m_presetBlue.setDecorator( this.m_BORDER_NONE ); + this.m_presetYellow.setDecorator( this.m_BORDER_NONE ); + }, + + /** + * Update the UI based on a preset color of blue. + */ + _presetBlueSelected : function(){ + this._setRgbColors( 0, 0, 255 ); + this.m_presetRed.setDecorator( this.m_BORDER_NONE ); + this.m_presetGreen.setDecorator( this.m_BORDER_NONE ); + this.m_presetBlue.setDecorator( this.m_BORDER_LINE ); + this.m_presetYellow.setDecorator( this.m_BORDER_NONE ); + }, + + /** + * Update the UI based on a preset color of yellow. + */ + _presetYellowSelected : function(){ + this._setRgbColors( 255, 255, 0 ); + this.m_presetRed.setDecorator( this.m_BORDER_NONE ); + this.m_presetGreen.setDecorator( this.m_BORDER_NONE ); + this.m_presetBlue.setDecorator( this.m_BORDER_NONE ); + this.m_presetYellow.setDecorator( this.m_BORDER_LINE ); + }, + + /** + * Update the UI based on a custom color. + */ + _presetsNotSelected : function(){ + this.m_presetRed.setDecorator( this.m_BORDER_NONE ); + this.m_presetGreen.setDecorator( this.m_BORDER_NONE ); + this.m_presetBlue.setDecorator( this.m_BORDER_NONE ); + this.m_presetYellow.setDecorator( this.m_BORDER_NONE ); + }, + + /** + * Notification that the RGB value of the color mask + * has changed. + */ + _primaryColorChanged : function(){ + this._presetsNotSelected(); + this._setPreviewColor(); + this._sendMaskColorCmd(); + }, + + /** + * Notify the server that the mask color has changed. + */ + _sendMaskColorCmd : function(){ + if ( this.m_id !== null ){ + var red = this.m_spinRed.getValue(); + var green = this.m_spinGreen.getValue(); + var blue = this.m_spinBlue.getValue(); + var params = "red:"+red+",green:"+green+",blue:"+blue; + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setMaskColor"; + this.m_connector.sendCommand( cmd, params, function(){}); + } + }, + + /** + * Update the UI with mask information from the server. + * @param mask {Object} - mask information from the server. + */ + setControls : function( mask ){ + this._setRgbColors( mask.red, mask.green, mask.blue ); + this.m_transparency.setValue( mask.alpha ); + }, + + /** + * Enable/disable mask controls based on whether a mask is being + * applied. + * @param enabled {boolean} - true if a mask is being applied; false otherwise. + */ + setControlsEnabled : function( enabled ){ + this.m_presetRed.setEnabled( enabled ); + this.m_presetGreen.setEnabled( enabled ); + this.m_presetBlue.setEnabled( enabled ); + this.m_presetYellow.setEnabled( enabled ); + this.m_spinRed.setEnabled( enabled ); + this.m_spinBlue.setEnabled( enabled ); + this.m_spinGreen.setEnabled( enabled ); + this.m_transparency.setEnabled( enabled ); + }, + + /** + * Set the id of the server-side object that handles mask information. + * @param id {String} - server-side identifier of object handling mask information. + */ + setId : function( id ){ + this.m_id = id; + this.m_transparency.setId( id ); + }, + + + + /** + * Update the preview panel with the latest color information. + */ + _setPreviewColor : function(){ + var red = this.m_spinRed.getValue(); + var green = this.m_spinGreen.getValue(); + var blue = this.m_spinBlue.getValue(); + var alpha = this.m_transparency.getValue(); + var alphaNorm = alpha / 255; + var rgbArray = [red, green, blue]; + var hexStr = qx.util.ColorUtil.rgbToHexString(rgbArray ); + this.m_preview.setBackgroundColor( hexStr ); + this.m_preview.setOpacity( alphaNorm ); + }, + + /** + * Update the rgb controls with new color information. + * @param redAmount {Number} - the amount of red. + * @param greenAmount {Number} - the amount of green. + * @param blueAmount {Number} - the amount of blue. + */ + _setRgbColors : function( redAmount, greenAmount, blueAmount ){ + this.m_spinRed.removeListenerById( this.m_spinRedId ); + this.m_spinGreen.removeListenerById( this.m_spinGreenId ); + this.m_spinBlue.removeListenerById( this.m_spinBlueId ); + var rgbChanged = false; + if ( redAmount != this.m_spinRed.getValue() ){ + this.m_spinRed.setValue( redAmount ); + rgbChanged = true; + } + if ( greenAmount != this.m_spinGreen.getValue() ){ + this.m_spinGreen.setValue( greenAmount ); + rgbChanged = true; + } + if ( blueAmount != this.m_spinBlue.getValue() ){ + this.m_spinBlue.setValue( blueAmount ); + rgbChanged = true; + } + if ( rgbChanged ){ + this._primaryColorChanged(); + } + this.m_spinRedId = this.m_spinRed.addListener( "changeValue", this._primaryColorChanged, this ); + this.m_spinGreenId = this.m_spinGreen.addListener( "changeValue", this._primaryColorChanged, this ); + this.m_spinBlueId = this.m_spinBlue.addListener( "changeValue", this._primaryColorChanged, this ); + }, + + m_connector : null, + m_id : null, + m_presetRed : null, + m_presetGreen : null, + m_presetBlue : null, + m_presetYellow : null, + m_preview : null, + m_spinRed : null, + m_spinGreen : null, + m_spinBlue : null, + m_spinRedId : null, + m_spinGreenId : null, + m_spinBlueId : null, + m_transparency : null, + + m_BORDER_NONE : "no-border", + m_BORDER_LINE : "line-border" + + } +}); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js index 9655d494..67f128b6 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js @@ -15,6 +15,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { construct : function( ) { this.base(arguments, "Stack", ""); this.m_connector = mImport("connector"); + this.m_datas = []; this._init(); }, @@ -56,6 +57,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { if ( val ){ try { var controls = JSON.parse( val ); + this.m_datas = controls.data; this.m_imageList.setListItems( controls.data ); var errorMan = skel.widgets.ErrorHandler.getInstance(); @@ -72,7 +74,18 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { */ _init : function( ) { this.setPadding( 0, 0, 0, 0 ); - this._setLayout( new qx.ui.layout.VBox(1)); + this._setLayout( new qx.ui.layout.HBox(1)); + this._initList(); + this._initMaskControls(); + this._add( new qx.ui.core.Spacer(), {flex:1} ); + }, + + /** + * Initialize the list of loaded images. + */ + _initList : function(){ + var listContainer = new qx.ui.container.Composite(); + listContainer.setLayout( new qx.ui.layout.VBox(1)); //Auto select check var selectContainer = new qx.ui.container.Composite(); @@ -88,11 +101,33 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { //List this.m_imageList = new skel.widgets.Image.Stack.DragDropList( 300 ); this.m_imageList.addListener( "listReordered", this._sendReorderCmd, this ); - this.m_imageList.addListener( "listSelection", this._sendSelectionCmd, this ); + this.m_imageList.addListener( "listSelection", this._listItemSelected, this ); //Add to main container. - this._add( selectContainer ); - this._add( this.m_imageList ); + listContainer.add( selectContainer ); + listContainer.add( this.m_imageList ); + this._add( listContainer ); + }, + + /** + * Initialize the available mask controls for each list item. + */ + _initMaskControls : function(){ + this.m_maskControls = new skel.widgets.Image.Stack.MaskControls(); + this._add( this.m_maskControls ); + }, + + /** + * Update the mask controls with the selected item and notify the server + * that data was selected. + */ + _listItemSelected : function(){ + var indices = this.m_imageList.getSelectedIndices(); + if ( indices.length > 0 && this.m_datas.length > 0 ){ + var firstIndex = indices[0]; + this.m_maskControls.setControls( this.m_datas[firstIndex].mask ); + this._sendSelectionCmd(); + } }, /** @@ -129,6 +164,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { } }, + /** * Send a command to the server to select particular images in the stack. */ @@ -164,15 +200,18 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { */ setId : function( imageId ){ this.m_id = imageId; + this.m_maskControls.setId( imageId ); this._registerControls(); this._registerControlsData(); }, m_id : null, m_connector : null, + m_datas : null, m_sharedVar : null, m_sharedVarData : null, m_autoSelectCheck : null, + m_maskControls : null, m_imageList : null } }); \ No newline at end of file From d4885fd6aa1e3671e6299f30187f975a523072a7 Mon Sep 17 00:00:00 2001 From: slovelan Date: Wed, 2 Dec 2015 13:50:18 -0700 Subject: [PATCH 31/37] Added Pan/Zoom All option. --- carta/cpp/core/Data/Animator/Animator.cpp | 26 +- .../core/Data/Image/Contour/ContourControls.h | 1 + carta/cpp/core/Data/Image/Controller.cpp | 400 ++++++++++-------- carta/cpp/core/Data/Image/Controller.h | 71 ++-- carta/cpp/core/Data/Image/ControllerData.cpp | 119 ++++-- carta/cpp/core/Data/Image/ControllerData.h | 34 +- .../core/Data/Image/DrawStackSynchronizer.cpp | 115 +++++ .../core/Data/Image/DrawStackSynchronizer.h | 101 +++++ .../core/Data/Image/LayerCompositionModes.cpp | 119 ++++++ .../core/Data/Image/LayerCompositionModes.h | 83 ++++ carta/cpp/core/Data/Util.cpp | 1 + carta/cpp/core/Data/Util.h | 1 + carta/cpp/core/Data/ViewManager.cpp | 2 + carta/cpp/core/State/ObjectManager.cpp | 8 +- carta/cpp/core/State/ObjectManager.h | 2 +- carta/cpp/core/core.pro | 4 + carta/cpp/desktop/DesktopConnector.cpp | 5 +- .../class/skel/widgets/CustomUI/SelectBox.js | 18 +- .../skel/widgets/Image/Stack/MaskControls.js | 83 ++-- .../widgets/Image/Stack/MaskControlsColor.js | 156 ++++--- .../skel/widgets/Image/Stack/StackControls.js | 29 +- .../skel/source/class/skel/widgets/Path.js | 2 + 22 files changed, 980 insertions(+), 400 deletions(-) create mode 100755 carta/cpp/core/Data/Image/DrawStackSynchronizer.cpp create mode 100755 carta/cpp/core/Data/Image/DrawStackSynchronizer.h create mode 100644 carta/cpp/core/Data/Image/LayerCompositionModes.cpp create mode 100644 carta/cpp/core/Data/Image/LayerCompositionModes.h diff --git a/carta/cpp/core/Data/Animator/Animator.cpp b/carta/cpp/core/Data/Animator/Animator.cpp index b9b29aff..18dd3ec0 100644 --- a/carta/cpp/core/Data/Animator/Animator.cpp +++ b/carta/cpp/core/Data/Animator/Animator.cpp @@ -59,7 +59,6 @@ QString Animator::addLink( CartaObject* cartaObject ){ } if ( linkAdded ){ - //_resetAnimationParameters( -1); _adjustStateController( controller ); } return result; @@ -285,14 +284,7 @@ QString Animator::getStateString( const QString& /*sessionId*/, SnapshotType typ QString result; if ( type == SNAPSHOT_PREFERENCES ){ //User preferences should include animators visible (m_state) - //and individual animator preferences. - StateInterface prefState(""); - prefState.setState( /*getPath(),*/ m_state.toString()); - QMap::const_iterator animIter; - for (animIter = m_animators.begin(); animIter != m_animators.end(); ++animIter){ - prefState.insertObject( animIter.key(), animIter.value()->getStatePreferences()); - } - result = prefState.toString(); + result = m_state.toString(); } else if ( type == SNAPSHOT_LAYOUT ){ result = m_linkImpl->getStateString(getIndex(), getSnapType( type ) ); @@ -485,15 +477,12 @@ void Animator::_resetStateAnimator( const Carta::State::StateInterface& state, c } void Animator::resetState( const QString& state ){ - Carta::State::StateInterface prefState(""); - prefState.setState( state ); - _resetStateAnimator( prefState, Selection::CHANNEL); - _resetStateAnimator( prefState, Selection::IMAGE ); - _adjustStateAnimatorTypes(); + m_state.setState( state ); } void Animator::resetStateData( const QString& state ){ Carta::State::StateInterface dataState(""); + m_animators.clear(); dataState.setState( state ); int animationCount = dataState.getArraySize( AnimatorType::ANIMATIONS ); for (int i = 0; i < animationCount; i++ ){ @@ -502,12 +491,11 @@ void Animator::resetStateData( const QString& state ){ QString animName = dataState.getValue( keyName ); QString keyValue = UtilState::getLookup(lookup, "value"); QString stateValue = dataState.getValue( keyValue ); - if ( m_animators.contains( animName )){ - m_animators[animName]->resetStateData( stateValue ); - } - else { - qDebug() << "Unrecognized animator state: "<resetStateData( stateValue ); } } diff --git a/carta/cpp/core/Data/Image/Contour/ContourControls.h b/carta/cpp/core/Data/Image/Contour/ContourControls.h index 23a14e29..c34d03fd 100644 --- a/carta/cpp/core/Data/Image/Contour/ContourControls.h +++ b/carta/cpp/core/Data/Image/Contour/ContourControls.h @@ -213,6 +213,7 @@ class ContourControls : public QObject, public Carta::State::CartaObject{ void _updateContourSetState(); static bool m_registered; + //Used IPercentIntensityMap* m_percentIntensityMap; diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 015d22cb..8e22cd0f 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -1,6 +1,7 @@ #include "State/ObjectManager.h" #include "State/UtilState.h" #include "Data/Image/Controller.h" +#include "Data/Image/DrawStackSynchronizer.h" #include "Data/Image/Grid/AxisMapper.h" #include "Data/Image/Grid/DataGrid.h" #include "Data/Image/Grid/GridControls.h" @@ -53,6 +54,7 @@ const QString Controller::DATA_PATH = "dataPath"; const QString Controller::CURSOR = "formattedCursorCoordinates"; const QString Controller::CENTER = "center"; const QString Controller::IMAGE = "image"; +const QString Controller::PAN_ZOOM_ALL = "panZoomAll"; const QString Controller::POINTER_MOVE = "pointer-move"; const QString Controller::ZOOM = "zoom"; const QString Controller::REGIONS = "regions"; @@ -72,20 +74,19 @@ using Carta::Lib::AxisInfo; Controller::Controller( const QString& path, const QString& id ) : CartaObject( CLASS_NAME, path, id), m_selectImage(nullptr), - m_view(nullptr), + m_stackDraw(nullptr), m_stateData( UtilState::getLookup(path, StateInterface::STATE_DATA )), m_stateMouse(UtilState::getLookup(path, VIEW)){ QString viewName = Carta::State::UtilState::getLookup( path, VIEW); - m_view = makeRemoteView( viewName ); + m_stackDraw.reset( new DrawStackSynchronizer(makeRemoteView( viewName))); m_reloadFrameQueued = false; - m_repaintFrameQueued = false; _initializeSelections(); _initializeState(); - connect( m_view.get(), SIGNAL(sizeChanged()), this, SLOT(_viewResize()) ); + connect( m_stackDraw.get(), SIGNAL(viewResize()), this, SLOT(_viewResize())); Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); GridControls* gridObj = objMan->createObject(); @@ -134,11 +135,11 @@ bool Controller::addData(const QString& fileName) { //Contour controls is in charge of setting the UI for the contours. m_contourControls->_setDrawContours( contourPtr ); targetIndex = m_datas.size(); - connect( targetSource, SIGNAL(renderingDone()), this, SLOT(_scheduleFrameRepaint())); connect( targetSource, & ControllerData::saveImageResult, this, & Controller::saveImageResultCB ); m_datas.append(std::shared_ptr(targetSource)); + m_stackDraw->setLayers( m_datas ); - targetSource->_viewResize( m_view->getClientSize() ); + targetSource->_viewResize( m_stackDraw->getClientSize() ); //Colormap targetSource->_setColorMapGlobal( m_stateColor ); @@ -210,12 +211,10 @@ QString Controller::applyClips( double minIntensityPercentile, double maxIntensi if( clipsChangedValue ){ m_state.flushState(); - if ( m_view ){ - _scheduleFrameReload( true ); - double minPercent = m_state.getValue(CLIP_VALUE_MIN); - double maxPercent = m_state.getValue(CLIP_VALUE_MAX); - emit clipsChanged( minPercent, maxPercent ); - } + _scheduleFrameReload( true ); + double minPercent = m_state.getValue(CLIP_VALUE_MIN); + double maxPercent = m_state.getValue(CLIP_VALUE_MAX); + emit clipsChanged( minPercent, maxPercent ); } } else { @@ -257,6 +256,7 @@ QString Controller::closeImage( const QString& name ){ } if ( targetIndex >= 0 ){ _removeData( targetIndex ); + m_stackDraw->setLayers( m_datas ); emit dataChanged( this ); } else { @@ -266,27 +266,31 @@ QString Controller::closeImage( const QString& name ){ } void Controller::_colorMapChanged(){ - _render(); + _render( true ); } void Controller::centerOnPixel( double centerX, double centerY ){ - int dataCount = m_datas.size(); - for ( int i = 0; i < dataCount; i++ ){ - m_datas[i]->_setPan( centerX, centerY ); - _render(); + bool panZoomAll = m_state.getValue( PAN_ZOOM_ALL ); + if ( panZoomAll ){ + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + m_datas[i]->_setPan( centerX, centerY ); + } } + else { + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + m_datas[dataIndex]->_setPan( centerX, centerY ); + } + } + _render( panZoomAll ); } void Controller::_contoursChanged(){ - int dataIndex = _getIndexCurrent(); - if ( dataIndex >= 0 ){ - std::vector frames = _getFrameIndices( dataIndex ); - const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); - m_datas[dataIndex]->_render( frames, cs ); - } + _render( false ); } void Controller::_displayAxesChanged(std::vector displayAxisTypes, @@ -295,7 +299,7 @@ void Controller::_displayAxesChanged(std::vector displayAxi int dataIndex = _getIndexCurrent(); if (dataIndex >= 0 ) { if (m_datas[dataIndex] != nullptr) { - std::vector frames = _getFrameIndices( dataIndex ); + std::vector frames = _getFrameIndices(); m_datas[dataIndex]->_displayAxesChanged( displayAxisTypes, frames ); } } @@ -304,13 +308,13 @@ void Controller::_displayAxesChanged(std::vector displayAxi int dataCount = m_datas.size(); for ( int i = 0; i < dataCount; i++ ){ if ( m_datas[i] != nullptr ){ - std::vector frames = _getFrameIndices( i ); + std::vector frames = _getFrameIndices(); m_datas[i]->_displayAxesChanged( displayAxisTypes, frames ); } } } emit axesChanged(); - this->_loadView(); + _scheduleFrameReload( false ); _updateCursorText( true ); } @@ -372,7 +376,7 @@ QStringList Controller::getCoordinates( double x, double y, Carta::Lib::KnownSky QStringList result; int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ - QStringList coordList = m_datas[dataIndex]->_getCoordinates( x, y, system, _getFrameIndices(dataIndex) ); + QStringList coordList = m_datas[dataIndex]->_getCoordinates( x, y, system, _getFrameIndices() ); for ( int i = 0; i <= 1; i++ ){ result.append( coordList[i] ); } @@ -419,16 +423,12 @@ int Controller::getFrame( AxisInfo::KnownType axisType ) const { return frame; } -std::vector Controller::_getFrameIndices( int imageIndex ) const { +std::vector Controller::_getFrameIndices( ) const { int selectCount = m_selects.size(); std::vector frames( selectCount ); - if ( imageIndex >= 0 && imageIndex < m_datas.size() ){ - if ( m_datas[imageIndex] != nullptr ){ - //Determine the index of the frame to load. - for ( int i = 0; i < selectCount; i++ ){ - frames[i] = m_selects[i]->getIndex(); - } - } + //Determine the index of the frame to load. + for ( int i = 0; i < selectCount; i++ ){ + frames[i] = m_selects[i]->getIndex(); } return frames; } @@ -572,7 +572,7 @@ QString Controller::getPixelValue( double x, double y ) const { QString result(""); int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ - std::vector frames = _getFrameIndices( dataIndex ); + std::vector frames = _getFrameIndices(); result = m_datas[dataIndex]->_getPixelValue( x, y, frames ); } return result; @@ -688,7 +688,6 @@ double Controller::getZoomLevel( ){ int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ zoom = m_datas[dataIndex]->_getZoom( ); - _render(); } return zoom; } @@ -696,21 +695,18 @@ double Controller::getZoomLevel( ){ void Controller::_gridChanged( const StateInterface& state, bool applyAll ){ int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ - std::vector frames = _getFrameIndices( dataIndex ); if ( !applyAll ){ - m_datas[dataIndex]->_gridChanged( state, true, frames ); + m_datas[dataIndex]->_gridChanged( state ); + _render( false ); } else { int dataCount = m_datas.size(); for ( int i = 0; i < dataCount; i++ ){ - bool renderedImage = false; - if ( i == dataIndex ){ - renderedImage = true; - } if ( m_datas[i] != nullptr ){ - m_datas[i]->_gridChanged( state, renderedImage, frames ); + m_datas[i]->_gridChanged( state ); } } + _render( true ); } _updateCursorText( true ); } @@ -807,6 +803,24 @@ void Controller::_initializeCallbacks(){ return result; }); + addCommandCallback( "setPanZoomAll", [=] (const QString & /*cmd*/, + const QString & params, const QString & /*sessionId*/) -> QString { + std::set keys = {"panZoomAll"}; + std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); + QString panZoomKey = *keys.begin(); + bool validBool = false; + bool panZoomAll = Util::toBool( dataValues[panZoomKey], &validBool ); + QString result; + if ( validBool ){ + setPanZoomAll( panZoomAll ); + } + else { + result = "Pan/Zoom All must be true/false: "+params; + } + Util::commandPostProcess( result ); + return result; + }); + QString pointerPath= UtilState::getLookup( getPath(), UtilState::getLookup( VIEW, POINTER_MOVE)); addStateCallback( pointerPath, [=] ( const QString& /*path*/, const QString& value ) { QStringList mouseList = value.split( " "); @@ -955,7 +969,6 @@ void Controller::_initializeCallbacks(){ const QString & params, const QString & /*sessionId*/) -> QString { std::set keys = {Util::RED, Util::GREEN, Util::BLUE}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString result; QString redStr = dataValues[Util::RED]; bool validRed = false; @@ -978,7 +991,7 @@ void Controller::_initializeCallbacks(){ return result; }); - addCommandCallback( "setMaskOpacity", [=] (const QString & /*cmd*/, + addCommandCallback( "setMaskAlpha", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { std::set keys = { Util::ALPHA }; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); @@ -987,7 +1000,7 @@ void Controller::_initializeCallbacks(){ bool validAlpha = false; int alphaAmount = alphaStr.toInt( &validAlpha ); if ( validAlpha ){ - result = setMaskOpacity( alphaAmount ); + result = setMaskAlpha( alphaAmount ); } else { result = "Invalid mask opacity: "+params; @@ -1014,20 +1027,12 @@ void Controller::_initializeCallbacks(){ return result; }); - addCommandCallback( "setUseMask", [=] (const QString & /*cmd*/, + addCommandCallback( "setCompositionMode", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { - std::set keys = {ControllerData::APPLY}; + std::set keys = {ControllerData::COMPOSITION_MODE}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); - QString result; - QString applyStr = dataValues[ControllerData::APPLY]; - bool validBool = false; - bool apply = Util::toBool( applyStr, &validBool ); - if ( validBool ){ - result = setUseMask( apply ); - } - else { - result = "Use mask must be true/false: "+params; - } + QString compMode = dataValues[ControllerData::COMPOSITION_MODE]; + QString result = setCompositionMode( compMode ); Util::commandPostProcess( result ); return result; }); @@ -1049,10 +1054,9 @@ void Controller::_initializeSelections(){ void Controller::_initializeState(){ - - //Set whether or not to auto clip //First the preference state. m_state.insertValue( AUTO_CLIP, true ); + m_state.insertValue(PAN_ZOOM_ALL, true ); m_state.insertValue( STACK_SELECT_AUTO, true ); m_state.insertValue( CLIP_VALUE_MIN, 0.025 ); m_state.insertValue( CLIP_VALUE_MAX, 0.975 ); @@ -1084,11 +1088,15 @@ void Controller::_loadView( bool newClips ) { m_reloadFrameQueued = false; //Determine the index of the data to load. int dataIndex = _getIndexCurrent(); + _loadView( newClips, dataIndex ); +} + +void Controller::_loadView( bool newClips, int dataIndex ){ if ( dataIndex >= 0 ) { if (m_datas[dataIndex] != nullptr) { //Determine the indices of the frames to load. - vector frames = _getFrameIndices( dataIndex ); + vector frames = _getFrameIndices(); //Load the image. bool autoClip = m_state.getValue(AUTO_CLIP); @@ -1097,8 +1105,8 @@ void Controller::_loadView( bool newClips ) { } double clipValueMin = m_state.getValue(CLIP_VALUE_MIN); double clipValueMax = m_state.getValue(CLIP_VALUE_MAX); - const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); - m_datas[dataIndex]->_load(frames, autoClip, clipValueMin, clipValueMax, cs); + m_datas[dataIndex]->_load(frames, autoClip, clipValueMin, clipValueMax); + _render( false ); } else { qDebug() << "Uninitialized image: "<setUpperBound( frameCount ); } - this->_loadView(); + _scheduleFrameReload( false ); //Clear the statistics window if there are no images. if ( visibleImageCount == 0 ){ @@ -1167,19 +1175,22 @@ void Controller::_removeData( int index ){ } -void Controller::_render(){ - int dataIndex = _getIndexCurrent(); - if ( dataIndex >= 0 ){ - std::vector frames = _getFrameIndices( dataIndex ); - const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); - m_datas[dataIndex]->_render( frames, cs ); +void Controller::_render( bool allImages ){ + std::vector frames =_getFrameIndices(); + const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); + QList > datas; + if ( allImages ){ + datas = m_datas; + } + else { + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + datas.append( m_datas[dataIndex]); + } } + m_stackDraw->_render( datas, frames, cs ); } -void Controller::_repaintFrameNow(){ - m_view->scheduleRepaint(); - m_repaintFrameQueued = false; -} void Controller::resetState( const QString& state ){ StateInterface restoredState( ""); @@ -1200,23 +1211,27 @@ void Controller::resetStateData( const QString& state ){ Carta::State::StateInterface dataState( ""); dataState.setState( state ); - //Reset the image select state. - QString dataStateStr = dataState.getValue( Selection::IMAGE ); - m_selectImage ->resetState( dataStateStr ); - + //Reset the loaded images int dataCount = dataState.getArraySize(DATA); for ( int i = 0; i < dataCount; i++ ){ QString dataLookup = Carta::State::UtilState::getLookup( DATA, i ); QString fileLookup = Carta::State::UtilState::getLookup( dataLookup, DataSource::DATA_PATH); QString fileName = dataState.getValue( fileLookup ); addData( fileName ); + _loadView( true, i ); QString gridLookup = Carta::State::UtilState::getLookup( dataLookup, DataGrid::GRID); QString gridStr = dataState.toString( gridLookup ); StateInterface gridState( "" ); gridState.setState( gridStr ); - std::vector frames = _getFrameIndices( i ); - m_datas[i]->_gridChanged( gridState, false, frames ); + std::vector frames = _getFrameIndices(); + m_datas[i]->_gridChanged( gridState ); } + m_stackDraw->setLayers( m_datas ); + + //Reset the image select state. + QString dataStateStr = dataState.getValue( Selection::IMAGE ); + m_selectImage ->resetState( dataStateStr ); + m_stackDraw->setSelectIndex( m_selectImage->getIndex()); //Now we need to restore the axis states. int selectCount = m_selects.size(); @@ -1244,23 +1259,41 @@ void Controller::resetStateData( const QString& state ){ } void Controller::resetPan(){ - int dataCount = m_datas.size(); - if ( dataCount > 0 ){ - for ( int i = 0; i < dataCount; i++ ){ - m_datas[i]->_resetPan(); + bool panZoomAll = m_state.getValue( PAN_ZOOM_ALL ); + if ( panZoomAll ){ + int dataCount = m_datas.size(); + if ( dataCount > 0 ){ + for ( int i = 0; i < dataCount; i++ ){ + m_datas[i]->_resetPan(); + } } - _render(); } + else { + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + m_datas[dataIndex]->_resetPan(); + } + } + _render( panZoomAll ); } void Controller::resetZoom(){ - int dataCount = m_datas.size(); - if ( dataCount > 0 ){ - for ( int i = 0; i < dataCount; i++ ){ - m_datas[i]->_resetZoom(); + bool panZoomAll = m_state.getValue( PAN_ZOOM_ALL ); + if ( panZoomAll ){ + int dataCount = m_datas.size(); + if ( dataCount > 0 ){ + for ( int i = 0; i < dataCount; i++ ){ + m_datas[i]->_resetZoom(); + } + } + } + else { + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + m_datas[dataIndex]->_resetZoom(); } - _render(); } + _render( panZoomAll ); } @@ -1307,7 +1340,7 @@ QString Controller::saveImage( const QString& fileName, double scale ){ result = "Please make sure the save path is valid: "+fileName; } else { - std::vector frames = _getFrameIndices( dataIndex ); + std::vector frames = _getFrameIndices(); result = m_datas[dataIndex]->_saveImage( fileName, scale, frames ); } } @@ -1346,46 +1379,6 @@ void Controller::_saveRegions(){ } } -void Controller::_scheduleFrameRepaint(){ - int dataCount = m_datas.size(); - // if reload is already pending, do nothing - if ( m_repaintFrameQueued ) { - return; - } - m_view->resetLayers(); - - //We want the selected index to be the last one in the stack. - int selectIndex = m_selectImage->getIndex(); - int layerIndex = 0; - for ( int i = 0; i < dataCount; i++ ){ - int dIndex = ( selectIndex + i + 1 ) % dataCount; - if ( m_datas[dIndex]->_isVisible() ){ - QImage image = m_datas[dIndex]->_getQImage(); - Carta::Lib::VectorGraphics::VGList graphicsList = m_datas[dIndex]->_getVectorGraphics(); - m_view->setRasterLayer( layerIndex, image ); - bool masked = m_datas[dIndex]->_isMasked(); - if ( masked ){ - std::shared_ptr pmc = - std::make_shared < Carta::Lib::PixelMaskCombiner > (); - float alphaVal = m_datas[dIndex]->_getMaskAlpha(); - pmc-> setAlpha( alphaVal ); - qint32 maskColor = m_datas[dIndex]->_getMaskColor(); - pmc-> setMask( maskColor ); - m_view->setRasterLayerCombiner( layerIndex, pmc ); - } - else { - std::shared_ptr dc = - std::make_shared < Carta::Lib::DefaultCombiner > (); - m_view->setRasterLayerCombiner( layerIndex, dc ); - } - m_view->setVGLayer( layerIndex, graphicsList ); - layerIndex++; - } - } - m_repaintFrameQueued = true; - QMetaObject::invokeMethod( this, "_repaintFrameNow", Qt::QueuedConnection ); -} - void Controller::_scheduleFrameReload( bool newClips ){ if ( m_datas.size() > 0 ){ @@ -1393,6 +1386,7 @@ void Controller::_scheduleFrameReload( bool newClips ){ if ( m_reloadFrameQueued ) { return; } + m_stackDraw->setSelectIndex( m_selectImage->getIndex()); m_reloadFrameQueued = true; QMetaObject::invokeMethod( this, "_loadView", Qt::QueuedConnection, Q_ARG(bool, newClips) ); } @@ -1446,6 +1440,9 @@ void Controller::_setFrameAxis(int value, AxisInfo::KnownType axisType ) { } void Controller::setFrameImage( int val) { + if ( val < 0 ){ + return; + } if (m_selectImage != nullptr) { int oldIndex = m_selectImage->getIndex(); if ( oldIndex != val ){ @@ -1528,7 +1525,7 @@ QString Controller::setImageOrder( const std::vector& indices ){ } if ( imageReordered ){ m_datas = reorderedList; - _render(); + _render( true ); } return result; } @@ -1548,7 +1545,7 @@ QString Controller::setImageVisibility( int dataIndex, bool visible ){ emit dataChanged( this ); //Render the image if it is the one currently being viewed. if ( selectedImageIndex == dataIndex ){ - _loadView(); + _scheduleFrameReload( false ); } if ( visibleCount == 0 ){ _clearStatistics(); @@ -1651,7 +1648,7 @@ QStringList Controller::setMaskColor( int redAmount, int greenAmount, int blueAm return result; } -QString Controller::setMaskOpacity( int alphaAmount ){ +QString Controller::setMaskAlpha( int alphaAmount ){ QString result; int dataCount = m_datas.size(); bool dataSelected = false; @@ -1659,7 +1656,7 @@ QString Controller::setMaskOpacity( int alphaAmount ){ for ( int i = 0; i < dataCount; i++ ){ if ( m_datas[i]->_isSelected() ){ dataSelected = true; - bool changed = m_datas[i]->_setMaskOpacity( alphaAmount, result ); + bool changed = m_datas[i]->_setMaskAlpha( alphaAmount, result ); if ( changed ){ dataChanged = true; } @@ -1675,6 +1672,15 @@ QString Controller::setMaskOpacity( int alphaAmount ){ return result; } +void Controller::setPanZoomAll( bool panZoomAll ){ + bool oldPanZoomAll = m_state.getValue(PAN_ZOOM_ALL); + if ( panZoomAll != oldPanZoomAll ){ + m_state.setValue( PAN_ZOOM_ALL, panZoomAll ); + m_state.flushState(); + } +} + + void Controller::setStackSelectAuto( bool automatic ){ bool oldStackSelectAuto = m_state.getValue(STACK_SELECT_AUTO ); if ( oldStackSelectAuto != automatic ){ @@ -1683,7 +1689,7 @@ void Controller::setStackSelectAuto( bool automatic ){ } } -QString Controller::setUseMask( bool useMask ){ +QString Controller::setCompositionMode( const QString& compMode ){ QString result; int dataCount = m_datas.size(); bool dataSelected = false; @@ -1691,14 +1697,14 @@ QString Controller::setUseMask( bool useMask ){ for( int i = 0; i < dataCount; i++ ){ if ( m_datas[i]->_isSelected() ){ dataSelected = true; - bool stateChanged = m_datas[i]->_setUseMask( useMask ); + bool stateChanged = m_datas[i]->_setCompositionMode( compMode, result ); if ( stateChanged ){ dataChanged = true; } } } if ( !dataSelected ){ - result = "Data must be selected to apply a mask."; + result = "Data must be selected to apply a layer composition mode."; } if ( dataChanged ){ saveState(); @@ -1712,8 +1718,9 @@ void Controller::setZoomLevel( double zoomFactor ){ for ( int i = 0; i < dataCount; i++ ){ //Set the zoom m_datas[i]->_setZoom( zoomFactor ); - _render(); + } + _render( true ); } @@ -1734,12 +1741,11 @@ void Controller::_updateCursor( int mouseX, int mouseY ){ void Controller::_updateCursorText(bool notifyClients ){ QString formattedCursor; - int imageIndex = m_selectImage->getIndex(); int dataIndex = _getIndexCurrent(); if ( 0 <= dataIndex ){ int mouseX = m_stateMouse.getValue(ImageView::MOUSE_X ); int mouseY = m_stateMouse.getValue(ImageView::MOUSE_Y ); - QString cursorText = m_datas[dataIndex]->_getCursorText( mouseX, mouseY, _getFrameIndices( imageIndex)); + QString cursorText = m_datas[dataIndex]->_getCursorText( mouseX, mouseY, _getFrameIndices()); if ( cursorText != m_stateMouse.getValue(CURSOR)){ m_stateMouse.setValue( CURSOR, cursorText ); if ( notifyClients ){ @@ -1768,68 +1774,86 @@ void Controller::_updateDisplayAxes( int targetIndex ){ void Controller::updateZoom( double centerX, double centerY, double zoomFactor ){ - int dataIndex = _getIndexCurrent(); - if ( dataIndex >= 0 ){ - //Remember where the user clicked - QPointF clickPtScreen( centerX, centerY); - bool validImage = false; - QPointF clickPtImageOld = m_datas[dataIndex]->_getImagePt( clickPtScreen, &validImage ); - if ( validImage ){ - //Set the zoom - double newZoom = 1; - double oldZoom = m_datas[dataIndex]->_getZoom(); - if ( zoomFactor < 0 ) { - newZoom = oldZoom / 0.9; - } - else { - newZoom = oldZoom * 0.9; - } - for (std::shared_ptr data : m_datas ){ - data->_setZoom( newZoom ); - } + bool zoomPanAll = m_state.getValue(PAN_ZOOM_ALL); + if ( zoomPanAll ){ + for (std::shared_ptr data : m_datas ){ + _updateZoom( centerX, centerY, zoomFactor, data ); + } + } + else { + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + _updateZoom( centerX, centerY, zoomFactor, m_datas[dataIndex] ); + } + } + _render( zoomPanAll ); +} - // what is the new image pixel under the mouse cursor? - QPointF clickPtImageNew = m_datas[dataIndex]->_getImagePt( clickPtScreen, &validImage ); +void Controller::_updateZoom( double centerX, double centerY, double zoomFactor, + std::shared_ptr data ){ + //Remember where the user clicked + QPointF clickPtScreen( centerX, centerY); + bool validImage = false; + QPointF clickPtImageOld = data->_getImagePt( clickPtScreen, &validImage ); + if ( validImage ){ + //Set the zoom + double newZoom = 1; + double oldZoom = data->_getZoom(); + if ( zoomFactor < 0 ) { + newZoom = oldZoom / 0.9; + } + else { + newZoom = oldZoom * 0.9; + } + data->_setZoom( newZoom ); - // calculate the difference - QPointF delta = clickPtImageOld - clickPtImageNew; + // what is the new image pixel under the mouse cursor? + QPointF clickPtImageNew = data ->_getImagePt( clickPtScreen, &validImage ); - // add the delta to the current center - QPointF currCenter = m_datas[dataIndex]->_getCenter(); - QPointF newCenter = currCenter + delta; - for ( std::shared_ptr data : m_datas ){ - data->_setPan( newCenter.x(), newCenter.y() ); - } - _render(); - } + // calculate the difference + QPointF delta = clickPtImageOld - clickPtImageNew; + + // add the delta to the current center + QPointF currCenter = data ->_getCenter(); + QPointF newCenter = currCenter + delta; + data->_setPan( newCenter.x(), newCenter.y() ); } } void Controller::updatePan( double centerX , double centerY){ - int dataIndex = _getIndexCurrent(); - if ( dataIndex >= 0 ){ - bool validImage = false; - QPointF oldImageCenter = m_datas[dataIndex]-> _getImagePt( { centerX, centerY }, &validImage ); - if ( validImage ){ - - double imageX = oldImageCenter.x(); - double imageY = oldImageCenter.y(); - for ( std::shared_ptr data : m_datas ){ - data->_setPan( imageX, imageY ); - } - _render(); - _updateCursorText( true ); + bool zoomPanAll = m_state.getValue(PAN_ZOOM_ALL); + if ( zoomPanAll ){ + for ( std::shared_ptr data : m_datas ){ + _updatePan( centerX, centerY, data ); } } + else { + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + _updatePan( centerX, centerY, m_datas[dataIndex] ); + } + } + _render( zoomPanAll ); + _updateCursorText( true ); } +void Controller::_updatePan( double centerX , double centerY, + std::shared_ptr data){ + bool validImage = false; + QPointF imagePt = data -> _getImagePt( { centerX, centerY }, &validImage ); + if ( validImage ){ + double imageX = imagePt.x(); + double imageY = imagePt.y(); + data->_setPan( imageX, imageY ); + } +} void Controller::_viewResize( ){ - QSize clientSize = m_view->getClientSize(); + QSize clientSize = m_stackDraw->getClientSize(); for ( int i = 0; i < m_datas.size(); i++ ){ m_datas[i]->_viewResize( clientSize ); } - _render(); + _render( true ); } Controller::~Controller(){ diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index 1672e8df..b1143044 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -40,6 +40,7 @@ namespace Data { class ColorState; class ControllerData; class DisplayControls; +class DrawStackSynchronizer; class GridControls; class ContourControls; class Settings; @@ -350,6 +351,14 @@ class Controller: public QObject, public Carta::State::CartaObject, */ QString setClipValue( double clipValue ); + /** + * Set whether or not to apply a composition mode to the image. + * @param compMode - the type of composition mode to apply. + * @return an error message if there was a problem recognizing the composition mode. + */ + QString setCompositionMode( const QString& compMode ); + + /** * Specify a new image order. * @param imageIndices - a list specifying a new order for the images in @@ -380,17 +389,27 @@ class Controller: public QObject, public Carta::State::CartaObject, * @return - a list containing any errors that may have occurred in setting * the mask color. */ - //Note: Mask color will not take affect unless use mask is also true. + //Note: Mask color will not take affect unless a composition mode that supports + //a color filter has been set. QStringList setMaskColor( int redAmount, int greenAmount, int blueAmount ); /** - * Set the opacity of the mask. + * Set the transparency of the layer. * @param alphaAmount - the transparency level in [0,255] with 255 being opaque. - * @return - an error message if there was a problem setting the mask opacity or + * @return - an error message if there was a problem setting the layer opacity or * an empty string otherwise. */ - //Note: Mask opacity will not take affect unless use mask is also true. - QString setMaskOpacity( int alphaAmount ); + //Note: Layer transparency will not take affect unless a composition mode which supports + //transparency has been set. + QString setMaskAlpha( int alphaAmount ); + + /** + * Set whether or not a pan/zoom operation should affect all layers in the stack + * or just the top layer. + * @param panZoomAll - true if all layers should be pan/zoomed; false if just the + * top layer should be pan/zoomed. + */ + void setPanZoomAll( bool panZoomAll ); /** * Set whether or not selection of layers in the stack should be based on the @@ -399,13 +418,6 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void setStackSelectAuto( bool automatic ); - /** - * Set whether or not to apply a color mask to the image. - * @param useMask - true if a color mask should be applied; false otherwise. - * @return an error message if there was a problem setting whether or not to - * use a mask; an empty error message otherwise. - */ - QString setUseMask( bool useMask ); /** * Change the pan of the current image. @@ -481,7 +493,6 @@ private slots: void _contoursChanged(); - QString _getPreferencesId() const; void _gridChanged( const Carta::State::StateInterface& state, bool applyAll ); @@ -490,11 +501,6 @@ private slots: //The parameter newClips is set if the clip values have changed and need to be recomputed. void _loadView( bool newClips = false ); - /** - * Notification that a stack layer has changed its image or vector graphics. - */ - void _scheduleFrameRepaint(); - /** * The view has been resized. */ @@ -506,10 +512,6 @@ private slots: */ void _scheduleFrameReload( bool newClips = false ); - /** - * Repaint the image. - */ - void _repaintFrameNow(); // Asynchronous result from saveFullImage(). void saveImageResultCB( bool result ); @@ -530,7 +532,7 @@ private slots: //Clear image statistics. void _clearStatistics(); - std::vector _getFrameIndices( int imageIndex ) const; + std::vector _getFrameIndices( ) const; set _getAxesHidden() const; std::vector _getAxisZTypes() const; //Get the actual data index of the selection with the given index. This method @@ -541,17 +543,23 @@ private slots: //Get the actual index of the passed in data. int _getIndexData( ControllerData* cd ) const; + + QString _getPreferencesId() const; + //Provide default values for state. void _initializeState(); void _initializeCallbacks(); void _initializeSelections(); - + void _loadView( bool newClips, int dataIndex ); QString _makeRegion( const QString& regionType ); void _removeData( int index ); - void _render(); - + /** + * Refresh the view of the images. + * @param allImages - true if all the images in the stack need a refresh; false otherwise. + */ + void _render( bool allImages ); void _saveRegions(); /** @@ -580,6 +588,10 @@ private slots: void _updateCursor( int mouseX, int mouseY ); void _updateCursorText(bool notifyClients ); void _updateDisplayAxes( int targetIndex ); + void _updatePan( double centerX , double centerY, + std::shared_ptr data); + void _updateZoom( double centerX, double centerY, double zoomFactor, + std::shared_ptr data ); static bool m_registered; @@ -590,6 +602,7 @@ private slots: static const QString DATA; static const QString DATA_PATH; static const QString IMAGE; + static const QString PAN_ZOOM_ALL; static const QString REGIONS; static const QString CENTER; static const QString POINTER_MOVE; @@ -601,12 +614,9 @@ private slots: Selection* m_selectImage; std::vector m_selects; - //Data View - std::shared_ptr m_view; - std::shared_ptr m_gridControls; std::shared_ptr m_contourControls; - + std::unique_ptr m_stackDraw; std::unique_ptr m_settings; @@ -627,7 +637,6 @@ private slots: Carta::State::StateInterface m_stateMouse; bool m_reloadFrameQueued; - bool m_repaintFrameQueued; Controller(const Controller& other); Controller& operator=(const Controller& other); diff --git a/carta/cpp/core/Data/Image/ControllerData.cpp b/carta/cpp/core/Data/Image/ControllerData.cpp index 5756bc0c..3c697f7b 100755 --- a/carta/cpp/core/Data/Image/ControllerData.cpp +++ b/carta/cpp/core/Data/Image/ControllerData.cpp @@ -9,6 +9,7 @@ #include "Data/Colormap/ColorState.h" #include "Data/Image/Grid/AxisMapper.h" #include "Data/Image/Grid/LabelFormats.h" +#include "Data/Image/LayerCompositionModes.h" #include "State/UtilState.h" #include "CartaLib/PixelPipeline/CustomizablePixelPipeline.h" @@ -29,11 +30,16 @@ namespace Carta { namespace Data { const QString ControllerData::CLASS_NAME = "ControllerData"; -const QString ControllerData::APPLY = "apply"; +const QString ControllerData::COMPOSITION_MODE="mode"; const QString ControllerData::LAYER = "layer"; const QString ControllerData::MASK = "mask"; +const QString ControllerData::LAYER_COLOR="colorSupport"; +const QString ControllerData::LAYER_ALPHA="alphaSupport"; const QString ControllerData::SELECTED = "selected"; + +LayerCompositionModes* ControllerData::m_compositionModes = nullptr; + class ControllerData::Factory : public Carta::State::CartaObjectFactory { public: @@ -58,6 +64,7 @@ ControllerData::ControllerData(const QString& path, const QString& id) : DataGrid* gridObj = objMan->createObject(); m_dataGrid.reset( gridObj ); m_dataGrid->_initializeGridRenderer(); + _initializeSingletons(); _initializeState(); } @@ -132,6 +139,15 @@ std::shared_ptr ControllerData::_getColorState(){ return m_stateColor; } +QString ControllerData::_getCompositionMode() const { + QString maskApplyKey = Carta::State::UtilState::getLookup( MASK, COMPOSITION_MODE ); + return m_state.getValue( maskApplyKey ); +} + +std::shared_ptr ControllerData::_getContours() { + return m_dataContours; +} + QStringList ControllerData::_getCoordinates( double x, double y, Carta::Lib::KnownSkyCS system, const std::vector& frames ) const{ QStringList coordStr; @@ -336,39 +352,44 @@ double ControllerData::_getZoom() const { return zoom; } -void ControllerData::_gridChanged( const Carta::State::StateInterface& state, bool renderImage, - const std::vector& frames ){ +void ControllerData::_gridChanged( const Carta::State::StateInterface& state ){ m_dataGrid->_resetState( state ); - //m_state.setObject(DataGrid::GRID, m_dataGrid->_getState().toString()); - if ( renderImage ){ - const Carta::Lib::KnownSkyCS& cs = _getCoordinateSystem(); - _render( frames, cs ); +} + +void ControllerData::_initializeSingletons( ){ + //Load the available color maps. + if ( m_compositionModes == nullptr ){ + m_compositionModes = Util::findSingletonObject(); } } + void ControllerData::_initializeState(){ m_state.insertValue(DataSource::DATA_PATH, ""); m_state.insertValue(Util::VISIBLE, true ); m_state.insertValue(SELECTED, false ); + //Color mix m_state.insertObject( MASK ); - QString applyKey = Carta::State::UtilState::getLookup( MASK, APPLY ); - m_state.insertValue( applyKey, false ); + QString compModeKey = Carta::State::UtilState::getLookup( MASK, COMPOSITION_MODE ); + QString defaultCompMode = m_compositionModes->getDefault(); + m_state.insertValue( compModeKey, defaultCompMode ); QString redKey = Carta::State::UtilState::getLookup( MASK, Util::RED ); m_state.insertValue( redKey, 255 ); QString greenKey = Carta::State::UtilState::getLookup( MASK, Util::GREEN ); - m_state.insertValue( greenKey, 0 ); + m_state.insertValue( greenKey, 255 ); QString blueKey = Carta::State::UtilState::getLookup( MASK, Util::BLUE ); - m_state.insertValue( blueKey, 0 ); + m_state.insertValue( blueKey, 255 ); QString alphaKey = Carta::State::UtilState::getLookup( MASK, Util::ALPHA ); - m_state.insertValue( alphaKey, 100 ); -} - -bool ControllerData::_isMasked() const { - QString maskApplyKey = Carta::State::UtilState::getLookup( MASK, APPLY ); - return m_state.getValue( maskApplyKey ); + m_state.insertValue( alphaKey, 255 ); + QString layerColorKey = Carta::State::UtilState::getLookup( MASK, LAYER_COLOR ); + bool colorSupport = m_compositionModes->isColorSupport( defaultCompMode ); + m_state.insertValue( layerColorKey, colorSupport ); + QString layerAlphaKey = Carta::State::UtilState::getLookup( MASK, LAYER_ALPHA ); + bool alphaSupport = m_compositionModes->isAlphaSupport( defaultCompMode ); + m_state.insertValue( layerAlphaKey, alphaSupport ); } bool ControllerData::_isSelected() const { @@ -435,7 +456,7 @@ void ControllerData::_renderingDone( void ControllerData::_load(vector frames, bool recomputeClipsOnNewFrame, - double minClipPercentile, double maxClipPercentile, const Carta::Lib::KnownSkyCS& cs ){ + double minClipPercentile, double maxClipPercentile ){ if ( m_dataSource ){ m_dataSource->_load( frames, recomputeClipsOnNewFrame, minClipPercentile, maxClipPercentile ); @@ -445,14 +466,12 @@ void ControllerData::_load(vector frames, bool recomputeClipsOnNewFrame, gridService->setInputImage( m_dataSource->_getImage() ); } } - - _render( frames, cs ); } } - -void ControllerData::_render( const std::vector& frames, const Carta::Lib::KnownSkyCS& cs ){ +void ControllerData::_render( const std::vector& frames, + const Carta::Lib::KnownSkyCS& cs, bool topOfStack ){ // erase current grid std::shared_ptr gridService = m_dataGrid->_getRenderer(); std::shared_ptr imageService = m_dataSource->_getRenderer(); @@ -512,25 +531,28 @@ void ControllerData::_render( const std::vector& frames, const Carta::Lib:: gridService->setAxisLabelInfo( 1, vertAxisInfo ); bool contourDraw = m_dataContours->isContourDraw(); - bool gridDraw = m_dataGrid->_isGridVisible(); + bool gridDraw = false; + if ( topOfStack ){ + gridDraw = m_dataGrid->_isGridVisible(); + } m_drawSync-> start( contourDraw, gridDraw ); } - - void ControllerData::_resetZoom(){ if ( m_dataSource ){ m_dataSource->_resetZoom(); } } + void ControllerData::_resetPan(){ if ( m_dataSource ){ m_dataSource->_resetPan(); } } + QString ControllerData::_saveImage( const QString& saveName, double scale, const std::vector& frames ){ QString result; @@ -574,11 +596,13 @@ QString ControllerData::_saveImage( const QString& saveName, double scale, return result; } + void ControllerData::_saveImageResultCB( bool result ){ emit saveImageResult( result ); m_saveService->deleteLater(); } + void ControllerData::_setContours( std::shared_ptr contours ){ m_dataContours = contours; @@ -593,6 +617,7 @@ void ControllerData::_setContours( std::shared_ptr contours ){ this, & ControllerData::_renderingDone ); } + bool ControllerData::_setFileName( const QString& fileName ){ bool successfulLoad = m_dataSource->_setFileName( fileName ); if ( successfulLoad ){ @@ -671,6 +696,32 @@ void ControllerData::_colorChanged(){ } } +bool ControllerData::_setCompositionMode( const QString& compositionMode, + QString& errorMsg ){ + bool stateChanged = false; + QString actualCompMode; + bool recognizedMode = m_compositionModes->isCompositionMode( compositionMode, actualCompMode ); + if ( recognizedMode ){ + QString key = Carta::State::UtilState::getLookup( MASK, COMPOSITION_MODE ); + QString oldMode = m_state.getValue( key ); + if ( oldMode != actualCompMode ){ + m_state.setValue( key, actualCompMode ); + bool colorSupport = m_compositionModes->isColorSupport( actualCompMode ); + QString colorSupportKey = Carta::State::UtilState::getLookup( MASK, LAYER_COLOR ); + m_state.setValue( colorSupportKey, colorSupport ); + bool alphaSupport = m_compositionModes->isAlphaSupport( actualCompMode ); + QString alphaSupportKey = Carta::State::UtilState::getLookup( MASK, LAYER_ALPHA ); + m_state.setValue( alphaSupportKey, alphaSupport ); + stateChanged = true; + } + } + else { + errorMsg = "Unrecognized layer composition mode: "+compositionMode; + } + return stateChanged; +} + + bool ControllerData::_setMaskColor( int redAmount, int greenAmount, int blueAmount, QStringList& result ){ bool changed = false; @@ -701,7 +752,8 @@ bool ControllerData::_setMaskColor( int redAmount, return changed; } -bool ControllerData::_setMaskOpacity( int alphaAmount, QString& result ){ + +bool ControllerData::_setMaskAlpha( int alphaAmount, QString& result ){ bool changed = false; if ( 0 > alphaAmount || alphaAmount > 255 ){ result = "Invalid mask opacity [0,255]:"+QString::number( alphaAmount ); @@ -717,6 +769,7 @@ bool ControllerData::_setMaskOpacity( int alphaAmount, QString& result ){ return changed; } + bool ControllerData::_setSelected( bool selected ){ bool stateChanged = false; bool oldSelected = m_state.getValue(SELECTED ); @@ -727,6 +780,7 @@ bool ControllerData::_setSelected( bool selected ){ return stateChanged; } + void ControllerData::_setVisible( bool visible ){ bool oldVisible = m_state.getValue(Util::VISIBLE); if ( visible != oldVisible ){ @@ -734,23 +788,13 @@ void ControllerData::_setVisible( bool visible ){ } } + void ControllerData::_setPan( double imgX, double imgY ){ if ( m_dataSource ){ m_dataSource-> _setPan( imgX, imgY ); } } -bool ControllerData::_setUseMask( bool useMask ){ - bool stateChanged = false; - - QString key = Carta::State::UtilState::getLookup( MASK, APPLY); - bool oldUseMask = m_state.getValue( key ); - if ( oldUseMask != useMask ){ - m_state.setValue( key, useMask ); - stateChanged = true; - } - return stateChanged; -} void ControllerData::_setZoom( double zoomAmount){ if ( m_dataSource ){ @@ -759,7 +803,6 @@ void ControllerData::_setZoom( double zoomAmount){ } - void ControllerData::_updateClips( std::shared_ptr& view, double minClipPercentile, double maxClipPercentile, const std::vector& frames ){ if ( m_dataSource ){ diff --git a/carta/cpp/core/Data/Image/ControllerData.h b/carta/cpp/core/Data/Image/ControllerData.h index 9debb26c..487ba768 100755 --- a/carta/cpp/core/Data/Image/ControllerData.h +++ b/carta/cpp/core/Data/Image/ControllerData.h @@ -47,11 +47,12 @@ class DrawSynchronizer; class DataContours; class DataGrid; class DataSource; - +class LayerCompositionModes; class ControllerData : public QObject, public Carta::State::CartaObject { friend class Controller; +friend class DrawStackSynchronizer; Q_OBJECT @@ -118,6 +119,8 @@ private slots: */ std::shared_ptr _getColorState(); + QString _getCompositionMode() const; + /** * Return the coordinates at pixel (x, y) in the given coordinate system. * @param x the x-coordinate of the desired pixel. @@ -136,6 +139,8 @@ private slots: */ Carta::Lib::KnownSkyCS _getCoordinateSystem() const; + std::shared_ptr _getContours(); + /** * Returns information about the image at the current location of the cursor. * @param mouseX the mouse x-position in screen coordinates. @@ -280,8 +285,7 @@ private slots: double _getZoom() const; - void _gridChanged( const Carta::State::StateInterface& state, bool renderImage, - const std::vector& frames ); + void _gridChanged( const Carta::State::StateInterface& state); /** * Respond to a change in display axes. @@ -294,8 +298,6 @@ private slots: void _initializeSingletons( ); - bool _isMasked() const; - /** * Returns true if the name identifies this layer; false otherwise. * @return true if the name identifies this layer; false otherwise. @@ -328,17 +330,16 @@ private slots: * @param autoClip true if clips should be automatically generated; false otherwise. * @param clipMinPercentile the minimum clip value. * @param clipMaxPercentile the maximum clip value. - * @param cs - an enumerated coordinate system type. */ void _load( vector frames, bool autoClip, double clipMinPercentile, - double clipMaxPercentile, const Carta::Lib::KnownSkyCS& cs ); + double clipMaxPercentile ); /** * Generate a new QImage. * @param frames - list of image frames. * @param cs - an enumerated coordinate system type. */ - void _render( const std::vector& frames, const Carta::Lib::KnownSkyCS& cs ); + void _render( const std::vector& frames, const Carta::Lib::KnownSkyCS& cs, bool topOfStack ); /** * Center the image. @@ -365,6 +366,9 @@ private slots: */ void _setColorMapGlobal( std::shared_ptr colorState ); + bool _setCompositionMode( const QString& compositionMode, + QString& errorMsg ); + /** * Set contour set to be rendered. * @param contours - the rendered contour set. @@ -399,7 +403,7 @@ private slots: * an empty string otherwise. * @return - true if the mask opacity was changed; false otherwise. */ - bool _setMaskOpacity( int alphaAmount, QString& result ); + bool _setMaskAlpha( int alphaAmount, QString& result ); /** * Set the center for this image's display. @@ -415,12 +419,6 @@ private slots: */ bool _setSelected( bool selected ); - /** - * Set whether or not to apply a color mask to the image. - * @param useMask - true if a color mask should be applied; false otherwise. - * @return - true if the mask status was changed; false otherwise. - */ - bool _setUseMask( bool useMask ); /** * Show/hide this layer. @@ -450,11 +448,15 @@ private slots: class Factory; static bool m_registered; - static const QString APPLY; + static const QString COMPOSITION_MODE; static const QString LAYER; + static const QString LAYER_COLOR; + static const QString LAYER_ALPHA; static const QString MASK; static const QString SELECTED; + static LayerCompositionModes* m_compositionModes; + std::shared_ptr m_stateColor; std::unique_ptr m_dataGrid; diff --git a/carta/cpp/core/Data/Image/DrawStackSynchronizer.cpp b/carta/cpp/core/Data/Image/DrawStackSynchronizer.cpp new file mode 100755 index 00000000..a363f716 --- /dev/null +++ b/carta/cpp/core/Data/Image/DrawStackSynchronizer.cpp @@ -0,0 +1,115 @@ +#include "DrawStackSynchronizer.h" +#include "Data/Image/ControllerData.h" +#include "Data/Image/LayerCompositionModes.h" +#include "CartaLib/IRemoteVGView.h" + + +#include + +namespace Carta { + +namespace Data { + + +DrawStackSynchronizer::DrawStackSynchronizer( Carta::Lib::LayeredRemoteVGView* view ){ + m_repaintFrameQueued = false; + m_selectIndex = 0; + m_view.reset( view ); + connect( m_view.get(), SIGNAL(sizeChanged()), this, SIGNAL( viewResize() ) ); +} + +QSize DrawStackSynchronizer::getClientSize() const { + return m_view->getClientSize(); +} + + +void DrawStackSynchronizer::_repaintFrameNow(){ + m_view->scheduleRepaint(); + m_repaintFrameQueued = false; +} + + +void DrawStackSynchronizer::_render( QList >& datas, + std::vector frames, const Carta::Lib::KnownSkyCS& cs ){ + int dataCount = datas.size(); + m_renderCount = 0; + m_redrawCount = dataCount; + int stackIndex = 0; + for ( int i = 0; i < dataCount; i++ ){ + if ( datas[i]->_isVisible() ){ + connect( datas[i].get(), SIGNAL(renderingDone()), + this, SLOT(_scheduleFrameRepaint()), Qt::UniqueConnection); + bool topOfStack = false; + if ( stackIndex == m_selectIndex ){ + topOfStack = true; + } + datas[i]->_render( frames, cs, topOfStack ); + stackIndex++; + } + } +} + +void DrawStackSynchronizer::_scheduleFrameRepaint(){ + + // if reload is already pending, do nothing + m_renderCount++; + if ( m_repaintFrameQueued || (m_renderCount != m_redrawCount ) ) { + return; + } + + m_view->resetLayers(); + + //We want the selected index to be the last one in the stack. + int dataCount = m_layers.size(); + int stackIndex = 0; + for ( int i = 0; i < dataCount; i++ ){ + int dIndex = ( m_selectIndex + i + 1) % dataCount; + m_layers[dIndex]->disconnect( this ); + if ( m_layers[dIndex]->_isVisible() ){ + + QImage image = m_layers[dIndex]->_getQImage(); + Carta::Lib::VectorGraphics::VGList graphicsList = m_layers[dIndex]->_getVectorGraphics(); + m_view->setRasterLayer( stackIndex, image ); + + QString combineMode = m_layers[dIndex]->_getCompositionMode(); + if ( combineMode == LayerCompositionModes::PLUS ){ + std::shared_ptr pmc = + std::make_shared < Carta::Lib::PixelMaskCombiner > (); + + float alphaVal = m_layers[dIndex]->_getMaskAlpha(); + pmc-> setAlpha( alphaVal ); + qint32 maskColor = m_layers[dIndex]->_getMaskColor(); + pmc-> setMask( maskColor ); + m_view->setRasterLayerCombiner( stackIndex, pmc ); + } + else if ( combineMode == LayerCompositionModes::ALPHA ){ + std::shared_ptr alphaCombine = + std::make_shared(); + float alphaVal = m_layers[dIndex]->_getMaskAlpha(); + alphaCombine-> setAlpha( alphaVal ); + m_view->setRasterLayerCombiner( stackIndex, alphaCombine ); + } + m_view->setVGLayer( stackIndex, graphicsList ); + stackIndex++; + } + } + m_repaintFrameQueued = true; + QMetaObject::invokeMethod( this, "_repaintFrameNow", Qt::QueuedConnection ); +} + +void DrawStackSynchronizer::setLayers( QList< std::shared_ptr > layers){ + m_layers = layers; +} + +void DrawStackSynchronizer::setSelectIndex( int selectIndex ){ + m_selectIndex = selectIndex; +} + + + +DrawStackSynchronizer::~DrawStackSynchronizer(){ + +} + +} +} diff --git a/carta/cpp/core/Data/Image/DrawStackSynchronizer.h b/carta/cpp/core/Data/Image/DrawStackSynchronizer.h new file mode 100755 index 00000000..062fda8b --- /dev/null +++ b/carta/cpp/core/Data/Image/DrawStackSynchronizer.h @@ -0,0 +1,101 @@ +/*** + * Draws the layers in a stack. + */ + +#pragma once + +#include "CartaLib/CartaLib.h" + +#include +#include +#include +#include + +#include + +namespace Carta { + namespace Lib { + class LayeredRemoteVGView; + } +} + +namespace Carta { +namespace Data { + +class ControllerData; + + +class DrawStackSynchronizer: public QObject { + + friend class Controller; + + Q_OBJECT + +public: + + /** + * Constructor. + * @param view- the stack view. + */ + DrawStackSynchronizer( Carta::Lib::LayeredRemoteVGView* view); + + /** + * Return the client view size. + * @return - the size of the stack view on the client. + */ + QSize getClientSize() const; + + /** + * Set the potential data that are layers in the stack if they are visible. + * @param layers- a list of potential stack layers. + */ + void setLayers( QList< std::shared_ptr > layers); + + /** + * Set the top index in the stack. + * @param index - the top stack index. + */ + void setSelectIndex( int index ); + + + virtual ~DrawStackSynchronizer(); + +signals: + /** + * The view has been resized. + */ + void viewResize(); + +private slots: + + /** + * Repaint the image. + */ + void _repaintFrameNow(); + + /** + * Notification that a stack layer has changed its image or vector graphics. + */ + void _scheduleFrameRepaint(); + +private: + + void _render( QList >& datas, + std::vector frames, const Carta::Lib::KnownSkyCS& cs ); + + //Data View + std::shared_ptr m_view; + QList< std::shared_ptr > m_layers; + bool m_repaintFrameQueued; + + int m_renderCount; + int m_redrawCount; + int m_selectIndex; + + DrawStackSynchronizer(const DrawStackSynchronizer& other); + DrawStackSynchronizer& operator=(const DrawStackSynchronizer& other); + +}; + +} +} diff --git a/carta/cpp/core/Data/Image/LayerCompositionModes.cpp b/carta/cpp/core/Data/Image/LayerCompositionModes.cpp new file mode 100644 index 00000000..e00d03d7 --- /dev/null +++ b/carta/cpp/core/Data/Image/LayerCompositionModes.cpp @@ -0,0 +1,119 @@ +#include "LayerCompositionModes.h" + +#include "Data/Util.h" +#include "State/UtilState.h" + + +#include +#include + +namespace Carta { + +namespace Data { + +const QString LayerCompositionModes::CLASS_NAME = "LayerCompositionModes"; +const QString LayerCompositionModes::MODES = "modes"; +const QString LayerCompositionModes::NONE = "None"; +const QString LayerCompositionModes::PLUS = "Plus"; +const QString LayerCompositionModes::ALPHA = "Alpha"; + + +class LayerCompositionModes::Factory : public Carta::State::CartaObjectFactory { + + public: + + Factory(): + CartaObjectFactory( CLASS_NAME ){}; + + Carta::State::CartaObject * create (const QString & path, const QString & id) + { + return new LayerCompositionModes (path, id); + } + }; + + +bool LayerCompositionModes::m_registered = + Carta::State::ObjectManager::objectManager()->registerClass ( CLASS_NAME, new LayerCompositionModes::Factory()); + + +LayerCompositionModes::LayerCompositionModes( const QString& path, const QString& id): + CartaObject( CLASS_NAME, path, id ){ + + _initializeDefaultState(); + //_initializeCallbacks(); +} + + +QStringList LayerCompositionModes::getLayerCompositionModes() const { + QStringList buff; + int modeCount = m_modes.size(); + for ( int i = 0; i < modeCount; i++ ){ + buff << m_modes[i]; + } + return buff; +} + +QString LayerCompositionModes::getDefault() const { + return NONE; +} + +void LayerCompositionModes::_initializeDefaultState(){ + m_modes.push_back(NONE); + m_modes.push_back(ALPHA); + m_modes.push_back(PLUS); + + + int modeCount = m_modes.size(); + m_state.insertArray( MODES, modeCount ); + for ( int i = 0; i < modeCount; i++ ){ + QString arrayIndexStr = Carta::State::UtilState::getLookup(MODES, QString::number(i)); + m_state.setValue(arrayIndexStr, m_modes[i]); + } + m_state.flushState(); +} + + +bool LayerCompositionModes::isAlphaSupport( const QString& modeName ) const { + bool alphaSupport = false; + QString actualModeName; + bool actualMode = isCompositionMode( modeName, actualModeName ); + if ( actualMode ){ + if ( actualModeName == PLUS || actualModeName == ALPHA ){ + alphaSupport = true; + } + } + return alphaSupport; +} + +bool LayerCompositionModes::isColorSupport( const QString& modeName ) const { + bool colorSupport = false; + QString actualModeName; + bool actualMode = isCompositionMode( modeName, actualModeName ); + if ( actualMode ){ + if ( actualModeName == PLUS ){ + colorSupport = true; + } + } + return colorSupport; +} + +bool LayerCompositionModes::isCompositionMode( const QString& name, QString& actualName ) const { + int modeCount = m_modes.size(); + bool validMode = false; + for ( int i = 0; i < modeCount; i++ ){ + int result = QString::compare( name, m_modes[i], Qt::CaseInsensitive ); + if ( result == 0 ){ + actualName = m_modes[i]; + validMode = true; + break; + } + } + return validMode; +} + + +LayerCompositionModes::~LayerCompositionModes(){ + +} +} +} diff --git a/carta/cpp/core/Data/Image/LayerCompositionModes.h b/carta/cpp/core/Data/Image/LayerCompositionModes.h new file mode 100644 index 00000000..453f90c3 --- /dev/null +++ b/carta/cpp/core/Data/Image/LayerCompositionModes.h @@ -0,0 +1,83 @@ +/*** + * List of possible layer composition modes that can be applied to the image. + * + */ + +#pragma once + +#include "State/ObjectManager.h" +#include "State/StateInterface.h" +#include + + + +namespace Carta { + +namespace Data { + +class LayerCompositionModes : public Carta::State::CartaObject { + +public: + + /** + * Return the default stack layer composition mode. + * @return - the default stack layer composition mode. + */ + QString getDefault() const; + + /** + * Returns true if the indicated layer composition mode supports transparency. + * @param modeName - an identifier for a layer composition mode. + * @return - true if the layer composition mode supports transparency; false + * otherwise. + */ + bool isAlphaSupport( const QString& modeName ) const; + + /** + * Returns true if the indicated layer composition mode supports a color filter. + * @param modeName - an identifier for a layer composition mode. + * @return - true if the layer composition mode supports a color filter; false + * otherwise. + */ + bool isColorSupport( const QString& modeName ) const; + + /** + * Returns true if the name represents a valid data transform; false, otherwise. + * @param name a QString identifying a data transform. + * @param actualName the actual name of the transform in case of a case mismatch + * @return true if the name represents a valid data tranform; false, otherwise. + */ + bool isCompositionMode( const QString& name, QString& actualName ) const; + + /** + * Returns a list of available layer composition modes. + * @return a QStringList containing the names of available layer composition modes. + */ + QStringList getLayerCompositionModes() const; + + virtual ~LayerCompositionModes(); + + const static QString CLASS_NAME; + const static QString NONE; + const static QString ALPHA; + const static QString PLUS; + +private: + void _initializeDefaultState(); + + std::vector < QString > m_modes; + + static bool m_registered; + const static QString MODES; + + LayerCompositionModes( const QString& path, const QString& id ); + + class Factory; + + + LayerCompositionModes( const LayerCompositionModes& other); + LayerCompositionModes& operator=( const LayerCompositionModes & other ); +}; + +} +} diff --git a/carta/cpp/core/Data/Util.cpp b/carta/cpp/core/Data/Util.cpp index 19e6920b..9c14394c 100644 --- a/carta/cpp/core/Data/Util.cpp +++ b/carta/cpp/core/Data/Util.cpp @@ -8,6 +8,7 @@ namespace Carta { namespace Data { const QString Util::ALPHA = "alpha"; +const QString Util::APPLY = "apply"; const QString Util::TRUE = "true"; const QString Util::FALSE = "false"; const QString Util::PREFERENCES = "preferences"; diff --git a/carta/cpp/core/Data/Util.h b/carta/cpp/core/Data/Util.h index 585972b2..79ef5f90 100644 --- a/carta/cpp/core/Data/Util.h +++ b/carta/cpp/core/Data/Util.h @@ -94,6 +94,7 @@ class Util { static const QString PREFERENCES; static const QString ALPHA; + static const QString APPLY; static const QString RED; static const QString GREEN; static const QString BLUE; diff --git a/carta/cpp/core/Data/ViewManager.cpp b/carta/cpp/core/Data/ViewManager.cpp index bbadb2ae..73cf1342 100644 --- a/carta/cpp/core/Data/ViewManager.cpp +++ b/carta/cpp/core/Data/ViewManager.cpp @@ -10,6 +10,7 @@ #include "Data/Image/Contour/ContourGenerateModes.h" #include "Data/Image/Contour/ContourSpacingModes.h" #include "Data/Image/Contour/ContourStyles.h" +#include "Data/Image/LayerCompositionModes.h" #include "Data/Histogram/ChannelUnits.h" #include "Data/DataLoader.h" #include "Data/Colormap/TransformsData.h" @@ -79,6 +80,7 @@ ViewManager::ViewManager( const QString& path, const QString& id) Util::findSingletonObject(); Util::findSingletonObject(); Util::findSingletonObject(); + Util::findSingletonObject(); _initCallbacks(); _initializeDefaultState(); _makeDataLoader(); diff --git a/carta/cpp/core/State/ObjectManager.cpp b/carta/cpp/core/State/ObjectManager.cpp index 7b342a8c..7e686513 100644 --- a/carta/cpp/core/State/ObjectManager.cpp +++ b/carta/cpp/core/State/ObjectManager.cpp @@ -21,7 +21,8 @@ namespace State { QList CartaObjectFactory::globalIds = {"ChannelUnits", "Clips", "Colormaps","ContourGenerateModes","ContourSpacingModes","ContourStyles", - "CoordinateSystems","DataLoader","Fonts","LabelFormats","TransformsImage","TransformsData", + "CoordinateSystems","DataLoader","Fonts","LabelFormats","LayerCompositionModes", + "TransformsImage","TransformsData", "ErrorManager","Layout","Preferences", "PreferencesSave", "Themes","ViewManager"}; QString CartaObject::addIdToCommand (const QString & command) const { @@ -139,8 +140,9 @@ void CartaObject::unregisterView() conn()-> unregisterView( m_path +"/view" ); } -std::shared_ptr CartaObject::makeRemoteView( const QString& path ){ - return Carta::Lib::LayeredRemoteVGView::create( conn(), path ); +Carta::Lib::LayeredRemoteVGView* CartaObject::makeRemoteView( const QString& path ){ + //return Carta::Lib::LayeredRemoteVGView::create( conn(), path ); + return new Carta::Lib::LayeredRemoteVGView( conn(), path, NULL ); } QString CartaObject::getStateLocation( const QString& name ) const diff --git a/carta/cpp/core/State/ObjectManager.h b/carta/cpp/core/State/ObjectManager.h index ab5dc303..4990133b 100644 --- a/carta/cpp/core/State/ObjectManager.h +++ b/carta/cpp/core/State/ObjectManager.h @@ -115,7 +115,7 @@ class CartaObject { * Construct a layered view and return it. * @param path - a unique identifier for the remote view. */ - std::shared_ptr makeRemoteView( const QString& path ); + Carta::Lib::LayeredRemoteVGView* makeRemoteView( const QString& path ); //Return the full location for the state with the given name. QString getStateLocation( const QString& name ) const; diff --git a/carta/cpp/core/core.pro b/carta/cpp/core/core.pro index d324fb4a..6ab770aa 100755 --- a/carta/cpp/core/core.pro +++ b/carta/cpp/core/core.pro @@ -52,6 +52,7 @@ HEADERS += \ Data/Image/CoordinateSystems.h \ Data/Image/DataSource.h \ Data/Image/DrawSynchronizer.h \ + Data/Image/DrawStackSynchronizer.h \ Data/Image/Grid/AxisMapper.h \ Data/Image/Grid/DataGrid.h \ Data/Image/Grid/Fonts.h \ @@ -59,6 +60,7 @@ HEADERS += \ Data/Image/Grid/Themes.h \ Data/Image/Grid/LabelFormats.h \ Data/Image/IPercentIntensityMap.h \ + Data/Image/LayerCompositionModes.h \ Data/Selection.h \ Data/Layout/Layout.h \ Data/Layout/LayoutNode.h \ @@ -148,6 +150,8 @@ SOURCES += \ Data/Image/Grid/LabelFormats.cpp \ Data/Image/Grid/Themes.cpp \ Data/Image/DrawSynchronizer.cpp \ + Data/Image/DrawStackSynchronizer.cpp \ + Data/Image/LayerCompositionModes.cpp \ Data/DataLoader.cpp \ Data/Error/ErrorReport.cpp \ Data/Error/ErrorManager.cpp \ diff --git a/carta/cpp/desktop/DesktopConnector.cpp b/carta/cpp/desktop/DesktopConnector.cpp index 0c60e51f..62163514 100644 --- a/carta/cpp/desktop/DesktopConnector.cpp +++ b/carta/cpp/desktop/DesktopConnector.cpp @@ -264,7 +264,8 @@ void DesktopConnector::refreshViewNow(IView *view) const QImage & origImage = view-> getBuffer(); QSize clientImageSize = viewInfo->clientSize; - if( origImage.size() != clientImageSize && clientImageSize.height() > 0 && clientImageSize.width() > 0 ) { + if( origImage.size() != clientImageSize && clientImageSize.height() > 0 && + clientImageSize.width() > 0 && origImage.height() > 0 ) { qDebug() << "Having to re-scale the image, this is slow" << origImage.size() << viewInfo->clientSize; // scale the image to fit the client size, in case it wasn't scaled alerady QImage destImage = origImage.scaled( @@ -316,7 +317,7 @@ void DesktopConnector::jsUpdateViewSlot(const QString & viewName, int width, int void DesktopConnector::jsViewRefreshedSlot(const QString & viewName, qint64 id) { - qDebug() << "jsViewRefreshedSlot()" << viewName << id; + //qDebug() << "jsViewRefreshedSlot()" << viewName << id; ViewInfo * viewInfo = findViewInfo( viewName); if( ! viewInfo) { qCritical() << "Received refresh view signal for unknown view" << viewName; diff --git a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/SelectBox.js b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/SelectBox.js index 04179308..28ca293a 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/CustomUI/SelectBox.js +++ b/carta/html5/common/skel/source/class/skel/widgets/CustomUI/SelectBox.js @@ -49,14 +49,18 @@ qx.Class.define("skel.widgets.CustomUI.SelectBox", { _sendCmd : function(){ var errorMan = skel.widgets.ErrorHandler.getInstance(); errorMan.clearErrors(); - if ( this.m_id !== null ){ - var path = skel.widgets.Path.getInstance(); - var cmd = this.m_id + path.SEP_COMMAND + this.m_cmd; - var selectValue = this.getValue(); - var params = this.m_paramId + ":"+selectValue; - this.m_connector.sendCommand( cmd, params, function(){}); + var selectValue = this.getValue(); + if ( selectValue !== null && selectValue.length > 0 ){ + if ( this.m_id !== null ){ + + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + this.m_cmd; + + var params = this.m_paramId + ":"+selectValue; + this.m_connector.sendCommand( cmd, params, function(){}); + } + this.fireDataEvent( "selectChanged", null ); } - this.fireDataEvent( "selectChanged", null ); }, /** diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js index e287caaa..baac535c 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js @@ -22,31 +22,60 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControls", { members : { + /** + * Callback for a change in the available composition modes on the server. + */ + _compModesChangedCB : function(){ + if ( this.m_sharedVarCompModes ){ + var val = this.m_sharedVarCompModes.get(); + if ( val ){ + try { + var oldName = this.m_compModeCombo.getValue(); + var modeNames = JSON.parse( val ); + this.m_compModeCombo.setSelectItems( modeNames.modes ); + + //Try to reset the old selection + if ( oldName !== null ){ + this.m_compModeCombo.setSelectValue( oldName ); + } + } + catch( err ){ + console.log( "Could not layer composition modes: "+val ); + console.log( "Err="+err ); + } + } + } + }, + /* * Initializes the UI. */ _init : function( ) { this._setLayout( new qx.ui.layout.VBox(1) ); - this.m_content = new qx.ui.groupbox.GroupBox( /*"Mask"*/); + this.m_content = new qx.ui.groupbox.GroupBox( ""); this.m_content.setLayout( new qx.ui.layout.VBox(1) ); this._add( this.m_content ); - this._initCheck(); + this._initCompositeModes(); this._initMask(); }, /** * Initialize UI as to whether or not to apply a mask. */ - _initCheck : function(){ - var checkContainer = new qx.ui.container.Composite(); - checkContainer.setLayout( new qx.ui.layout.HBox(1) ); - this.m_applyCheck = new qx.ui.form.CheckBox( "Apply Mask" ); - this.m_applyCheck.setToolTipText( "Apply a color/transparency mask to the selected layer(s)." ); - this.m_applyId = this.m_applyCheck.addListener( "changeValue", this._sendApplyCmd, this ); - checkContainer.add( new qx.ui.core.Spacer(1), {flex:1} ); - checkContainer.add( this.m_applyCheck ); - checkContainer.add( new qx.ui.core.Spacer(1), {flex:1} ); - this.m_content.add( checkContainer ); + _initCompositeModes : function(){ + var hContainer = new qx.ui.container.Composite(); + hContainer.setLayout( new qx.ui.layout.HBox(1) ); + var label = new qx.ui.basic.Label( "Composer:" ); + this.m_compModeCombo = new skel.widgets.CustomUI.SelectBox( + "setCompositionMode", "mode"); + this.m_compModeCombo.setToolTipText( "Select a layer composition mode."); + + //this.m_applyId = this.m_applyCheck.addListener( "changeValue", this._sendApplyCmd, this ); + hContainer.add( new qx.ui.core.Spacer(1), {flex:1} ); + hContainer.add( label ); + hContainer.add( this.m_compModeCombo ); + hContainer.add( new qx.ui.core.Spacer(1), {flex:1} ); + this.m_content.add( hContainer ); }, @@ -58,31 +87,14 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControls", { this.m_content.add( this.m_maskColor ); }, - /** - * Notify the server as to whether a mask should be applied to the selected - * layers. - */ - _sendApplyCmd : function(){ - if ( this.m_id !== null ){ - var apply = this.m_applyCheck.getValue(); - var params = "apply:"+apply; - var path = skel.widgets.Path.getInstance(); - var cmd = this.m_id + path.SEP_COMMAND + "setUseMask"; - this.m_connector.sendCommand( cmd, params, function(){}); - } - }, /** * Update the UI with server information. * @param mask {Object} - information from server about the mask. */ setControls : function(mask){ - this.m_applyCheck.removeListenerById( this.m_applyId ); - this.m_applyCheck.setValue( mask.apply ); - this.m_applyId = this.m_applyCheck.addListener( "changeValue", this._sendApplyCmd, this ); - + this.m_compModeCombo.setSelectValue( mask.mode ); this.m_maskColor.setControls( mask ); - this.m_maskColor.setControlsEnabled( mask.apply ); }, /** @@ -93,15 +105,18 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControls", { setId : function( id ){ this.m_id = id; this.m_maskColor.setId( id ); + this.m_compModeCombo.setId( id ); + var path = skel.widgets.Path.getInstance(); + this.m_sharedVarCompModes = this.m_connector.getSharedVar(path.LAYER_COMPOSITION_MODES); + this.m_sharedVarCompModes.addCB( this._compModesChangedCB.bind( this)); + this._compModesChangedCB(); }, - m_applyCheck : null, - m_applyId : null, + m_compModeCombo : null, m_connector : null, m_content : null, m_id : null, - + m_sharedVarCompModes : null, m_maskColor : null - } }); \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js index 33bc2d1c..5c35a6e4 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js @@ -30,6 +30,7 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { */ _addColorRgb : function( parent, colorRgb, labelText, rowIndex ){ var label = new qx.ui.basic.Label( labelText ); + label.setTextAlign( "right"); parent.add( label, {row:rowIndex, column:0} ); parent.add( colorRgb, {row:rowIndex, column:1} ); }, @@ -38,11 +39,11 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { * Initializes the UI. */ _init : function( ) { - this._setLayout( new qx.ui.layout.VBox(1) ); + this._setLayout( new qx.ui.layout.VBox(2) ); + this._initOpacity(); this._initPresets(); this._add( new qx.ui.core.Spacer(1), {flex:1} ); this._initRgbs(); - this._initOpacity(); this._initPreview(); }, @@ -52,11 +53,11 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { _initOpacity : function(){ var transContainer = new qx.ui.container.Composite(); transContainer.setLayout( new qx.ui.layout.HBox(1)); - this.m_transparency = new skel.widgets.CustomUI.TextSlider("setMaskOpacity", "alpha", + this.m_transparency = new skel.widgets.CustomUI.TextSlider("setMaskAlpha", "alpha", 0, skel.widgets.Path.MAX_RGB, 0, - "Opacity", false, - "Set the mask opacity.", "Slide to set the grid opacity.", - "maskOpacityTextField", "maskOpacitySlider", false); + "Transparency", true, + "Set the transparency.", "Slide to set the transparency.", + "maskAlphaTextField", "maskAlphaSlider", false); this.m_transparency.setNotify( true ); this.m_transparency.addListener( "textSliderChanged", this._setPreviewColor, this ); transContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); @@ -72,34 +73,51 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { var presetContainer = new qx.ui.container.Composite(); presetContainer.setLayout( new qx.ui.layout.HBox(1)); this.m_presetRed = this._makePreset( "#ff0000" ); - this.m_presetRed.setToolTipText( "Apply a red color mask to the selected layer(s)."); + this.m_presetRed.setToolTipText( "Apply a red filter to the selected layer(s)."); this.m_presetGreen = this._makePreset( "#00ff00"); - this.m_presetGreen.setToolTipText( "Apply a green color mask to the selected layer(s)."); + this.m_presetGreen.setToolTipText( "Apply a green filter to the selected layer(s)."); this.m_presetBlue = this._makePreset( "#0000ff"); - this.m_presetBlue.setToolTipText( "Apply a blue color mask to the selected layer(s)."); - this.m_presetYellow = this._makePreset( "#ffff00"); - this.m_presetYellow.setToolTipText( "Apply a yellow color mask to the selected layer(s)."); + this.m_presetBlue.setToolTipText( "Apply a blue filter to the selected layer(s)."); + this.m_presetNone = this._makePreset( "#ffffff"); + this.m_presetNone.setToolTipText( "No color filter should be applied to the selected layer(s)."); this.m_presetRed.addListener( "mousedown", this._presetRedSelected, this ); this.m_presetGreen.addListener( "mousedown", this._presetGreenSelected, this ); this.m_presetBlue.addListener( "mousedown", this._presetBlueSelected, this ); - this.m_presetYellow.addListener( "mousedown", this._presetYellowSelected, this ); - presetContainer.add( this.m_presetRed ); + this.m_presetNone.addListener( "mousedown", this._presetNoneSelected, this ); presetContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); + presetContainer.add( this.m_presetRed ); + presetContainer.add( new qx.ui.core.Spacer(2), {flex:0.5} ); presetContainer.add( this.m_presetGreen ); - presetContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); + presetContainer.add( new qx.ui.core.Spacer(2), {flex:0.5} ); presetContainer.add( this.m_presetBlue ); + presetContainer.add( new qx.ui.core.Spacer(2), {flex:0.5} ); + presetContainer.add( this.m_presetNone ); presetContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); - presetContainer.add( this.m_presetYellow ); this._add( presetContainer ); }, + /** + * Initialize the color preview. + */ + _initPreview : function(){ + this.m_preview = new qx.ui.core.Widget(); + this.m_preview.setDecorator( this.m_BORDER_LINE ); + this.m_preview.setBackgroundColor( "#000000"); + this.m_preview.setWidth( 100 ); + this.m_preview.setHeight( 20 ); + this._add( this.m_preview ); + }, + /** * Initialize controls for setting RGB. */ _initRgbs : function(){ + var hContainer = new qx.ui.container.Composite(); + hContainer.setLayout( new qx.ui.layout.HBox(1)); + hContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); var rgbContainer = new qx.ui.container.Composite(); - rgbContainer.setLayout( new qx.ui.layout.Grid()); + rgbContainer.setLayout( new qx.ui.layout.Grid(2,2)); this.m_spinRed = new qx.ui.form.Spinner( 0, 0, 255 ); this.m_spinRed.setToolTipText( "Set the red mask color amount in the selected layer(s)."); this.m_spinRedId = this.m_spinRed.addListener("changeValue", this._primaryColorChanged, this ); @@ -112,20 +130,11 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { this._addColorRgb( rgbContainer, this.m_spinRed, "Red:", 0); this._addColorRgb( rgbContainer, this.m_spinGreen, "Green:", 1); this._addColorRgb( rgbContainer, this.m_spinBlue, "Blue:", 2); - this._add( rgbContainer ); + hContainer.add( rgbContainer ); + hContainer.add( new qx.ui.core.Spacer(2), {flex:1} ); + this._add( hContainer ); }, - /** - * Initialize the color preview. - */ - _initPreview : function(){ - this.m_preview = new qx.ui.core.Widget(); - this.m_preview.setDecorator( this.m_BORDER_LINE ); - this.m_preview.setBackgroundColor( "#000000"); - this.m_preview.setWidth( 100 ); - this.m_preview.setHeight( 20 ); - this._add( this.m_preview ); - }, /** * Construct a square for a preset color. @@ -147,7 +156,9 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { this.m_presetRed.setDecorator( this.m_BORDER_LINE ); this.m_presetGreen.setDecorator( this.m_BORDER_NONE ); this.m_presetBlue.setDecorator( this.m_BORDER_NONE ); - this.m_presetYellow.setDecorator( this.m_BORDER_NONE ); + this.m_presetNone.setDecorator( this.m_BORDER_NONE ); + this._setPreviewColor(); + this._sendMaskColorCmd(); }, /** @@ -158,7 +169,9 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { this.m_presetRed.setDecorator( this.m_BORDER_NONE ); this.m_presetGreen.setDecorator( this.m_BORDER_LINE ); this.m_presetBlue.setDecorator( this.m_BORDER_NONE ); - this.m_presetYellow.setDecorator( this.m_BORDER_NONE ); + this.m_presetNone.setDecorator( this.m_BORDER_NONE ); + this._setPreviewColor(); + this._sendMaskColorCmd(); }, /** @@ -169,18 +182,22 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { this.m_presetRed.setDecorator( this.m_BORDER_NONE ); this.m_presetGreen.setDecorator( this.m_BORDER_NONE ); this.m_presetBlue.setDecorator( this.m_BORDER_LINE ); - this.m_presetYellow.setDecorator( this.m_BORDER_NONE ); + this.m_presetNone.setDecorator( this.m_BORDER_NONE ); + this._setPreviewColor(); + this._sendMaskColorCmd(); }, /** - * Update the UI based on a preset color of yellow. + * Update the UI based on no color filter. */ - _presetYellowSelected : function(){ - this._setRgbColors( 255, 255, 0 ); + _presetNoneSelected : function(){ + this._setRgbColors( 255, 255, 255 ); this.m_presetRed.setDecorator( this.m_BORDER_NONE ); this.m_presetGreen.setDecorator( this.m_BORDER_NONE ); this.m_presetBlue.setDecorator( this.m_BORDER_NONE ); - this.m_presetYellow.setDecorator( this.m_BORDER_LINE ); + this.m_presetNone.setDecorator( this.m_BORDER_LINE ); + this._setPreviewColor(); + this._sendMaskColorCmd(); }, /** @@ -190,7 +207,33 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { this.m_presetRed.setDecorator( this.m_BORDER_NONE ); this.m_presetGreen.setDecorator( this.m_BORDER_NONE ); this.m_presetBlue.setDecorator( this.m_BORDER_NONE ); - this.m_presetYellow.setDecorator( this.m_BORDER_NONE ); + this.m_presetNone.setDecorator( this.m_BORDER_NONE ); + }, + + /** + * Update the selected states of the presets based on the + * RGB color values. + */ + _updatePresets : function(){ + var red = this.m_spinRed.getValue(); + var green = this.m_spinGreen.getValue(); + var blue = this.m_spinBlue.getValue(); + if ( red == 255 && green == 0 && blue == 0 ){ + this._presetRedSelected(); + } + else if ( red == 0 && green == 255 && blue == 0 ){ + this._presetGreenSelected(); + } + else if ( red == 0 && green == 0 && blue == 255 ){ + this._presetBlueSelected(); + } + else if ( red == 255 && green == 255 && blue == 255 ){ + this._presetNoneSelected(); + } + else { + this._presetsNotSelected(); + } + this._setPreviewColor(); }, /** @@ -198,8 +241,7 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { * has changed. */ _primaryColorChanged : function(){ - this._presetsNotSelected(); - this._setPreviewColor(); + this._updatePresets(); this._sendMaskColorCmd(); }, @@ -224,23 +266,27 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { */ setControls : function( mask ){ this._setRgbColors( mask.red, mask.green, mask.blue ); + this._updatePresets(); this.m_transparency.setValue( mask.alpha ); + this._setControlsEnabled( mask.alphaSupport, mask.colorSupport ); }, /** - * Enable/disable mask controls based on whether a mask is being - * applied. - * @param enabled {boolean} - true if a mask is being applied; false otherwise. + * Enable/disable mask controls based on the type of filter being applied. + * @param enabledTransparency {boolean} - true if a filter supporting transparency + * is being applied; false, otherwise. + * @param enabledColor {boolean} - true if a filter supporting color is being + * applied; false, otherwise. */ - setControlsEnabled : function( enabled ){ - this.m_presetRed.setEnabled( enabled ); - this.m_presetGreen.setEnabled( enabled ); - this.m_presetBlue.setEnabled( enabled ); - this.m_presetYellow.setEnabled( enabled ); - this.m_spinRed.setEnabled( enabled ); - this.m_spinBlue.setEnabled( enabled ); - this.m_spinGreen.setEnabled( enabled ); - this.m_transparency.setEnabled( enabled ); + _setControlsEnabled : function( enabledTransparency, enabledColor ){ + this.m_presetRed.setEnabled( enabledColor ); + this.m_presetGreen.setEnabled( enabledColor ); + this.m_presetBlue.setEnabled( enabledColor ); + this.m_presetNone.setEnabled( enabledColor ); + this.m_spinRed.setEnabled( enabledColor ); + this.m_spinBlue.setEnabled( enabledColor ); + this.m_spinGreen.setEnabled( enabledColor ); + this.m_transparency.setEnabled( enabledTransparency ); }, /** @@ -251,7 +297,6 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { this.m_id = id; this.m_transparency.setId( id ); }, - /** @@ -279,21 +324,14 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { this.m_spinRed.removeListenerById( this.m_spinRedId ); this.m_spinGreen.removeListenerById( this.m_spinGreenId ); this.m_spinBlue.removeListenerById( this.m_spinBlueId ); - var rgbChanged = false; if ( redAmount != this.m_spinRed.getValue() ){ this.m_spinRed.setValue( redAmount ); - rgbChanged = true; } if ( greenAmount != this.m_spinGreen.getValue() ){ this.m_spinGreen.setValue( greenAmount ); - rgbChanged = true; } if ( blueAmount != this.m_spinBlue.getValue() ){ this.m_spinBlue.setValue( blueAmount ); - rgbChanged = true; - } - if ( rgbChanged ){ - this._primaryColorChanged(); } this.m_spinRedId = this.m_spinRed.addListener( "changeValue", this._primaryColorChanged, this ); this.m_spinGreenId = this.m_spinGreen.addListener( "changeValue", this._primaryColorChanged, this ); @@ -305,7 +343,7 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { m_presetRed : null, m_presetGreen : null, m_presetBlue : null, - m_presetYellow : null, + m_presetNone : null, m_preview : null, m_spinRed : null, m_spinGreen : null, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js index 67f128b6..2a749418 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/StackControls.js @@ -39,6 +39,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { try { var controls = JSON.parse( val ); this.m_autoSelectCheck.setValue( controls.stackAutoSelect ); + this.m_panZoomAllCheck.setValue( controls.panZoomAll ); var errorMan = skel.widgets.ErrorHandler.getInstance(); errorMan.clearErrors(); @@ -87,10 +88,12 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { var listContainer = new qx.ui.container.Composite(); listContainer.setLayout( new qx.ui.layout.VBox(1)); - //Auto select check + //Check container var selectContainer = new qx.ui.container.Composite(); selectContainer.setLayout( new qx.ui.layout.HBox(1) ); selectContainer.add( new qx.ui.core.Spacer(), {flex:1}); + + //Auto select check this.m_autoSelectCheck = new qx.ui.form.CheckBox( "Auto Select"); skel.widgets.TestID.addTestId( this.m_autoSelectCheck, "autoSelectImages" ); this.m_autoSelectCheck.setToolTipText( "Auto selection based on animator or manual selection of layer(s)."); @@ -98,8 +101,15 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { selectContainer.add( this.m_autoSelectCheck ); selectContainer.add( new qx.ui.core.Spacer(), {flex:1}); + //Pan/zoom all check + this.m_panZoomAllCheck = new qx.ui.form.CheckBox( "Pan/Zoom All"); + this.m_panZoomAllCheck.setToolTipText( "Pan/Zoom all images in the stack instead of just the top image."); + this.m_panZoomAllCheck.addListener( "changeValue", this._sendPanZoomAllCmd, this ); + selectContainer.add( this.m_panZoomAllCheck ); + selectContainer.add( new qx.ui.core.Spacer(), {flex:1}); + //List - this.m_imageList = new skel.widgets.Image.Stack.DragDropList( 300 ); + this.m_imageList = new skel.widgets.Image.Stack.DragDropList( 250 ); this.m_imageList.addListener( "listReordered", this._sendReorderCmd, this ); this.m_imageList.addListener( "listSelection", this._listItemSelected, this ); @@ -130,6 +140,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { } }, + /** * Register to get updates on stack settings from the server. */ @@ -150,6 +161,18 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { this._controlsDataChangedCB(); }, + /** + * Send a command to the server indicating whether to pan/zoom all images + * in the stack or only the top one. + */ + _sendPanZoomAllCmd : function(){ + var panZoomAll = this.m_panZoomAllCheck.getValue(); + var params = "panZoomAll:"+panZoomAll; + var path = skel.widgets.Path.getInstance(); + var cmd = this.m_id + path.SEP_COMMAND + "setPanZoomAll"; + this.m_connector.sendCommand( cmd, params, function(){}); + }, + /** * Send a command to the server to reorder the images in the stack. * @param msg {Array} - a list specifying the new image order. @@ -182,6 +205,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { } }, + /** * Send a command to the server to reorder the images in the stack. * @param msg {Array} - a list specifying the new image order. @@ -208,6 +232,7 @@ qx.Class.define("skel.widgets.Image.Stack.StackControls", { m_id : null, m_connector : null, m_datas : null, + m_panZoomAllCheck : null, m_sharedVar : null, m_sharedVarData : null, m_autoSelectCheck : null, diff --git a/carta/html5/common/skel/source/class/skel/widgets/Path.js b/carta/html5/common/skel/source/class/skel/widgets/Path.js index 0c38ed58..73b49deb 100644 --- a/carta/html5/common/skel/source/class/skel/widgets/Path.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Path.js @@ -20,6 +20,7 @@ qx.Class.define("skel.widgets.Path", { this.ERROR_HANDLER = this.BASE_PATH + "ErrorManager"; this.FONTS = this.BASE_PATH + "Fonts"; this.LABEL_FORMATS = this.BASE_PATH + "LabelFormats"; + this.LAYER_COMPOSITION_MODES = this.BASE_PATH + "LayerCompositionModes"; this.LAYOUT = this.BASE_PATH + "Layout"; this.LAYOUT_PLUGIN = this.LAYOUT + this.SEP + "plugins"; this.MOUSE_X = this.BASE_PATH + this.MOUSE + this.SEP + "x" + this.SEP; @@ -68,6 +69,7 @@ qx.Class.define("skel.widgets.Path", { HIDE_IMAGE : "hideImage", HISTOGRAM_PLUGIN : "Histogram", LABEL_FORMATS : "", + LAYER_COMPOSITION_MODES : "", LAYOUT : "", LAYOUT_MANAGER : "Layout", LAYOUT_PLUGIN : "", From 65eb54692ca19b2c23caac987d84805ad61c3f3d Mon Sep 17 00:00:00 2001 From: slovelan Date: Mon, 7 Dec 2015 11:38:56 -0700 Subject: [PATCH 32/37] Selenium tests run, fixed, added & bug fixes. --- carta/cpp/core/Data/Animator/Animator.cpp | 101 ++++++-- carta/cpp/core/Data/Animator/Animator.h | 7 +- carta/cpp/core/Data/Histogram/Histogram.cpp | 1 + .../Data/Image/Contour/ContourControls.cpp | 126 +++------ .../core/Data/Image/Contour/ContourControls.h | 14 - .../core/Data/Image/Contour/DataContours.h | 1 + .../Data/Image/Contour/GeneratorState.cpp | 1 + carta/cpp/core/Data/Image/Controller.cpp | 215 ++++++++++------ carta/cpp/core/Data/Image/Controller.h | 32 ++- carta/cpp/core/Data/Image/ControllerData.cpp | 241 ++++++++++++++---- carta/cpp/core/Data/Image/ControllerData.h | 86 ++++++- .../core/Data/Image/DrawStackSynchronizer.cpp | 7 +- .../core/Data/Image/DrawStackSynchronizer.h | 4 +- .../cpp/core/Data/Image/DrawSynchronizer.cpp | 20 +- carta/cpp/core/Data/Image/DrawSynchronizer.h | 8 +- .../core/Data/Image/IPercentIntensityMap.h | 14 + .../class/skel/simulation/tSnapshotData.py | 92 ++++++- .../source/class/skel/simulation/tStack.py | 140 +++++++++- .../widgets/Image/Contour/ContourWidget.js | 9 +- .../skel/widgets/Image/Stack/MaskControls.js | 1 + .../widgets/Image/Stack/MaskControlsColor.js | 15 +- 21 files changed, 836 insertions(+), 299 deletions(-) diff --git a/carta/cpp/core/Data/Animator/Animator.cpp b/carta/cpp/core/Data/Animator/Animator.cpp index 18dd3ec0..dc2e6711 100644 --- a/carta/cpp/core/Data/Animator/Animator.cpp +++ b/carta/cpp/core/Data/Animator/Animator.cpp @@ -29,6 +29,8 @@ class Animator::Factory : public Carta::State::CartaObjectFactory { const QString Animator::CLASS_NAME = "Animator"; const QString Animator::TYPE = "type"; +const QString Animator::NAME = "name"; +const QString Animator::VALUE = "value"; bool Animator::m_registered = Carta::State::ObjectManager::objectManager()->registerClass (CLASS_NAME, new Animator::Factory()); @@ -95,6 +97,7 @@ bool Animator::_addAnimatorType( const QString& type, QString& animatorTypeId ){ bool animatorAdded = false; animatorTypeId = _initAnimator( type, &animatorAdded ); if ( animatorAdded ){ + //Restore the stored preferences. connect( m_animators[type], SIGNAL(indexChanged( int, const QString&)), this, SLOT(_frameChanged(int, const QString&))); } @@ -128,6 +131,8 @@ QString Animator::addAnimator( const QString& type, QString& animatorTypeId ){ _adjustStateAnimatorTypes(); animatorTypeId= m_animators[type]->getPath(); } + //Reset the preference state. + _resetStateAnimator( /*m_animPrefs[type],*/ type); return result; } @@ -280,11 +285,34 @@ int Animator::getMaxImageCount() const { return maxImages; } -QString Animator::getStateString( const QString& /*sessionId*/, SnapshotType type ) const{ +QString Animator::getStateString( const QString& /*sessionId*/, SnapshotType type ) const { QString result; if ( type == SNAPSHOT_PREFERENCES ){ - //User preferences should include animators visible (m_state) - result = m_state.toString(); + StateInterface prefState(""); + prefState.setState(m_state.toString() ); + + //Update the preference state strings using existing animators + QMap::const_iterator animIter; + for (animIter = m_animators.begin(); animIter != m_animators.end(); ++animIter){ + QString key = animIter.key(); + QString prefState = animIter.value()->getStatePreferences(); + m_animPrefs[key] = prefState; + } + + //Now save the preference states to the state. + int animCount = m_animPrefs.size(); + prefState.insertArray(AnimatorType::CLASS_NAME, animCount); + int i = 0; + QMap::const_iterator prefIter; + for (prefIter = m_animPrefs.begin(); prefIter != m_animPrefs.end(); ++prefIter){ + QString animKey = Carta::State::UtilState::getLookup( AnimatorType::CLASS_NAME, i ); + QString animKeyName = UtilState::getLookup(animKey, NAME ); + QString animKeyValue = UtilState::getLookup(animKey, VALUE ); + prefState.insertValue( animKeyName, prefIter.key() ); + prefState.insertValue( animKeyValue, prefIter.value() ); + i++; + } + result = prefState.toString(); } else if ( type == SNAPSHOT_LAYOUT ){ result = m_linkImpl->getStateString(getIndex(), getSnapType( type ) ); @@ -302,8 +330,8 @@ QString Animator::getStateString( const QString& /*sessionId*/, SnapshotType typ for (animIter = m_animators.begin(); animIter != m_animators.end(); ++animIter){ QString animKey = Carta::State::UtilState::getLookup( AnimatorType::ANIMATIONS, i ); QString animState = animIter.value()->getStateData(); - QString animKeyName = UtilState::getLookup(animKey, "name"); - QString animKeyValue = UtilState::getLookup(animKey, "value"); + QString animKeyName = UtilState::getLookup(animKey, NAME ); + QString animKeyValue = UtilState::getLookup(animKey, VALUE ); dataState.insertValue( animKeyName, animIter.key() ); dataState.insertValue( animKeyValue, animState ); @@ -330,6 +358,9 @@ QString Animator::_initAnimator( const QString& type, bool* newAnimator ){ AnimatorType* animObj = objMan->createObject(); animObj->_setType( type ); m_animators.insert(type, animObj ); + if ( m_animPrefs.contains(type) ){ + m_animators[type]->resetState( m_animPrefs[type], SnapshotType::SNAPSHOT_PREFERENCES); + } _adjustStateAnimatorTypes(); *newAnimator = true; } @@ -380,6 +411,7 @@ void Animator::_initializeCallbacks(){ void Animator::_initializeState(){ m_state.insertArray( AnimatorType::ANIMATIONS, 0); + m_state.flushState(); } bool Animator::isLinked( const QString& linkId ) const { @@ -404,6 +436,8 @@ void Animator::refreshState(){ QString Animator::removeAnimator( const QString& type ){ QString result; if ( m_animators.contains( type )){ + //Store the prefs + m_animPrefs[type]= m_animators[type]->getStatePreferences(); m_animators[type]->setVisible( false ); _adjustStateAnimatorTypes(); } @@ -447,7 +481,7 @@ void Animator::_resetAnimationParameters( int selectedImage ){ } else { int index = m_animators[Selection::IMAGE]->getFrame(); - if ( index > maxImages ){ + if ( index > maxImages ){ m_animators[Selection::IMAGE]->setIndex( 0 ); } } @@ -456,47 +490,62 @@ void Animator::_resetAnimationParameters( int selectedImage ){ } -void Animator::_resetStateAnimator( const Carta::State::StateInterface& state, const QString& key ){ - try { - QString animPrefs = state.toString( key ); - if ( animPrefs.length() > 0 ){ - if ( ! m_animators.contains( key )){ - QString animId; - addAnimator( key , animId ); - } - m_animators[key]->resetState( animPrefs, SNAPSHOT_PREFERENCES); +void Animator::_resetStateAnimator( const QString& key ){ + if ( m_animPrefs.contains( key ) ){ + if ( m_animators.contains( key )){ + m_animators[key]->resetState( m_animPrefs[key], SNAPSHOT_PREFERENCES); } - else { - removeAnimator( key ); - } - } - catch( std::invalid_argument& ex ){ - //State did not contain this animator so we remove it. - removeAnimator( key ); } } void Animator::resetState( const QString& state ){ - m_state.setState( state ); + StateInterface stateInterface( ""); + stateInterface.setState( state ); + + //Now store the preference setting for each animator. + int prefCount = stateInterface.getArraySize( AnimatorType::CLASS_NAME ); + for (int i = 0; i < prefCount; i++ ){ + QString lookup = UtilState::getLookup( AnimatorType::CLASS_NAME, i ); + QString keyName = UtilState::getLookup(lookup, NAME ); + QString animName = stateInterface.getValue( keyName ); + QString keyValue = UtilState::getLookup(lookup, VALUE ); + + QString prefValue = stateInterface.getValue( keyValue ); + m_animPrefs[animName] = prefValue; + if ( m_animators.contains( animName ) ){ + this->_resetStateAnimator( animName ); + } + } } void Animator::resetStateData( const QString& state ){ Carta::State::StateInterface dataState(""); - m_animators.clear(); dataState.setState( state ); + + //Add the necessary animators based on the data available. int animationCount = dataState.getArraySize( AnimatorType::ANIMATIONS ); + QStringList supportedAnim; for (int i = 0; i < animationCount; i++ ){ QString lookup = UtilState::getLookup( AnimatorType::ANIMATIONS, i ); - QString keyName = UtilState::getLookup(lookup, "name"); + QString keyName = UtilState::getLookup(lookup, NAME ); QString animName = dataState.getValue( keyName ); - QString keyValue = UtilState::getLookup(lookup, "value"); + QString keyValue = UtilState::getLookup(lookup, VALUE ); QString stateValue = dataState.getValue( keyValue ); if ( !m_animators.contains( animName) ){ QString animId; addAnimator( animName, animId ); } + supportedAnim.append( animName ); m_animators[animName]->resetStateData( stateValue ); } + + //Remove any animators not supported. + QList keys = m_animators.keys(); + for ( QString key : keys ){ + if ( !supportedAnim.contains(key) ){ + removeAnimator( key ); + } + } } bool Animator::_setAnimatorAvailability( const QString& key, bool available ){ diff --git a/carta/cpp/core/Data/Animator/Animator.h b/carta/cpp/core/Data/Animator/Animator.h index 2bc37b54..7702d45a 100644 --- a/carta/cpp/core/Data/Animator/Animator.h +++ b/carta/cpp/core/Data/Animator/Animator.h @@ -166,8 +166,8 @@ private slots: void _resetAnimationParameters( int selectedImage ); - //Reset the state of an individual animator. - void _resetStateAnimator( const Carta::State::StateInterface& state, const QString& key ); + //Reset the preferences state of an individual animator. + void _resetStateAnimator( const QString& key ); bool _setAnimatorAvailability( const QString& key, bool available ); @@ -180,8 +180,11 @@ private slots: /// Individual animation types. QMap m_animators; + mutable QMap m_animPrefs; static bool m_registered; + const static QString NAME; + const static QString VALUE; Animator( const Animator& other); Animator& operator=( const Animator& other ); diff --git a/carta/cpp/core/Data/Histogram/Histogram.cpp b/carta/cpp/core/Data/Histogram/Histogram.cpp index 3b4f091d..89613c47 100755 --- a/carta/cpp/core/Data/Histogram/Histogram.cpp +++ b/carta/cpp/core/Data/Histogram/Histogram.cpp @@ -265,6 +265,7 @@ void Histogram::_endSelectionColor(const QString& params ){ } void Histogram::_finishClips (){ + m_stateData.flushState(); //When we zoom there should be no clipping selector. m_histogram->clearSelectionColor(); _generateHistogram( true ); diff --git a/carta/cpp/core/Data/Image/Contour/ContourControls.cpp b/carta/cpp/core/Data/Image/Contour/ContourControls.cpp index 4bd57f82..01863632 100644 --- a/carta/cpp/core/Data/Image/Contour/ContourControls.cpp +++ b/carta/cpp/core/Data/Image/Contour/ContourControls.cpp @@ -50,11 +50,12 @@ ContourControls::ContourControls( const QString& path, const QString& id): m_percentIntensityMap( nullptr ), m_generatorState( new GeneratorState() ), m_stateData( UtilState::getLookup(path, StateInterface::STATE_DATA)){ - _initializeDefaultState(); _initializeCallbacks(); } + + void ContourControls::_addContourSet( const std::vector& levels, const QString& contourSetName ){ int count = levels.size(); @@ -70,22 +71,17 @@ void ContourControls::_addContourSet( const std::vector& levels, } //See if there is an existing contour with that name. - std::shared_ptr dataContours( nullptr ); - for ( std::set >::iterator it = m_dataContours.begin(); - it != m_dataContours.end(); it++ ){ - QString contourName = (*it)->getName(); - if ( contourName == contourSetName ){ - dataContours = (*it); - break; - } - } + DataContours* dataContours = _getContour( contourSetName ); //Create a new contour set if there is not an existing one. if ( !dataContours ){ ObjectManager* objMan = ObjectManager::objectManager(); - dataContours.reset(objMan->createObject()); + dataContours = objMan->createObject(); dataContours->setName( contourSetName ); - m_dataContours.insert( dataContours ); + std::shared_ptr dContours(dataContours ); + m_dataContours.insert( dContours ); + m_percentIntensityMap->addContourSet( dContours ); + } //Reset the contours @@ -96,30 +92,32 @@ void ContourControls::_addContourSet( const std::vector& levels, } } -void ContourControls::_clearContours(){ - ObjectManager* objMan = ObjectManager::objectManager(); - for ( std::set >::iterator it = m_dataContours.begin(); - it != m_dataContours.end(); it++ ){ - QString contourId = (*it)->getId(); - objMan->removeObject( contourId ); - } -} QString ContourControls::deleteContourSet( const QString& contourSetName ){ QString result; bool foundSet = false; std::set >::iterator it = m_dataContours.begin(); while ( it != m_dataContours.end() ){ - if ( (*it)->getName() == contourSetName ){ - foundSet = true; - ObjectManager* objMan = ObjectManager::objectManager(); - QString id = (*it)->getId(); - m_dataContours.erase(it); - objMan->removeObject( id ); - _updateContourSetState(); - break; + if ( (*it)->getName() == contourSetName ){ + foundSet = true; + //Reset the draw contour if it is the one we are removing. + if ( m_drawContours->getName() == contourSetName ){ + if ( m_dataContours.size() > 1 ){ + m_drawContours = (*m_dataContours.begin()); + } + else { + m_drawContours.reset(); + } } - it++; + m_percentIntensityMap->removeContourSet( (*it) ); + ObjectManager* objMan = ObjectManager::objectManager(); + QString id = (*it)->getId(); + m_dataContours.erase(it); + objMan->removeObject( id ); + _updateContourSetState(); + break; + } + it++; } if ( !foundSet ){ result = "Unrecognized contour set to delete: "+contourSetName; @@ -275,20 +273,6 @@ DataContours* ContourControls::_getContour( const QString& setName ) { return target; } -QString ContourControls::getStateString( const QString& /*sessionId*/, SnapshotType type ) const{ - QString result(""); - if ( type == SNAPSHOT_PREFERENCES ){ - result = m_state.toString(); - } - else if ( type == SNAPSHOT_DATA ){ - StateInterface dataCopy( m_stateData ); - dataCopy.setValue( StateInterface::OBJECT_TYPE, CLASS_NAME+StateInterface::STATE_DATA); - dataCopy.setValue( StateInterface::INDEX, getIndex()); - result = dataCopy.toString(); - } - return result; -} - void ContourControls::_initializeDefaultState(){ QString generateState = m_generatorState->getStateString(); @@ -410,7 +394,7 @@ void ContourControls::_initializeCallbacks(){ return result; }); - //Set the method for generating contour levels. + //Set the selected contour set. addCommandCallback( "selectContourSet", [=] (const QString & /*cmd*/, const QString & params, const QString & /*sessionId*/) -> QString { std::set keys = {DataContours::SET_NAME}; @@ -636,33 +620,9 @@ bool ContourControls::_isDuplicate( const QString& contourSetName ) const { } - -void ContourControls::resetStateData( const QString& state ){ - StateInterface dataState( ""); - dataState.setState( state ); - int contourCount = dataState.getArraySize( CONTOUR_SETS ); - Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); - for ( int i = 0; i < contourCount; i++ ){ - QString lookup = UtilState::getLookup( CONTOUR_SETS, i ); - QString contourSetState = dataState.toString( lookup ); - std::shared_ptr dataContours(objMan->createObject()); - dataContours->resetState( contourSetState ); - m_dataContours.insert( dataContours ); - } - _updateContourSetState(); -} - void ContourControls::selectContourSet( const QString& name ){ - std::shared_ptr dataContours( nullptr ); - for ( std::set >::iterator it = m_dataContours.begin(); - it != m_dataContours.end(); it++ ){ - QString contourName = (*it)->getName(); - if ( contourName == name ){ - dataContours = (*it); - break; - } - } - if ( dataContours ){ + DataContours* dataContours = _getContour( name ); + if ( dataContours != nullptr){ //Existing contour set - use it's method of generating contour levels m_generatorState = dataContours->_getGenerator(); } @@ -729,14 +689,10 @@ void ContourControls::setDashedNegative( bool useDash ){ void ContourControls::_setDrawContours( std::shared_ptr contours ){ m_drawContours = contours; - std::set drawContours; - for ( std::set >::iterator it = m_dataContours.begin(); - it != m_dataContours.end(); it++ ){ - std::set setContours = (*it)->_getContours(); - drawContours.insert( setContours.begin(), setContours.end()); - } - if ( m_drawContours ){ - m_drawContours->setContours( drawContours ); + DataContours* existingContour = _getContour( contours->getName()); + if ( !existingContour ){ + m_dataContours.insert( contours ); + _updateContourSetState(); } } @@ -881,26 +837,14 @@ void ContourControls::_updateContourSetState(){ Carta::State::StateInterface dataState = (*it)->_getState(); QString contourState = dataState.toString(); m_stateData.setObject( lookup, contourState); - std::set setContours = (*it)->_getContours(); - for ( std::set::iterator contourIt = setContours.begin(); - contourIt != setContours.end(); contourIt++ ){ - if ( (*contourIt).isVisible() ){ - drawContours.insert( (*contourIt) ); - } - } i++; } - m_stateData.flushState(); - if ( m_drawContours ){ - m_drawContours->setContours( drawContours ); - emit drawContoursChanged(); - } + emit drawContoursChanged(); } ContourControls::~ContourControls(){ - _clearContours(); } } } diff --git a/carta/cpp/core/Data/Image/Contour/ContourControls.h b/carta/cpp/core/Data/Image/Contour/ContourControls.h index c34d03fd..4a4e32e0 100644 --- a/carta/cpp/core/Data/Image/Contour/ContourControls.h +++ b/carta/cpp/core/Data/Image/Contour/ContourControls.h @@ -42,19 +42,6 @@ class ContourControls : public QObject, public Carta::State::CartaObject{ */ QString generateContourSet( const QString& contourSetName ); - /** - * Return the contour state of a specific type. - * @param sessionId - an identifier for a user session. - * @param type - an identifier for the type of state to save. - */ - QString getStateString( const QString& sessionId, SnapshotType type ) const; - - /** - * Reset the contour sets. - * @param state - a string representation of the contour sets. - */ - virtual void resetStateData( const QString& state ) Q_DECL_OVERRIDE; - /** * Select a specific contour set. * @param name - a name for a contour set. @@ -193,7 +180,6 @@ class ContourControls : public QObject, public Carta::State::CartaObject{ const static QString LEVEL_SEPARATOR; void _addContourSet( const std::vector& levels, const QString& contourSetName ); - void _clearContours(); QString _generateRange( const QString& contourSetName); QString _generateMinimum( const QString& contourSetName ); diff --git a/carta/cpp/core/Data/Image/Contour/DataContours.h b/carta/cpp/core/Data/Image/Contour/DataContours.h index 68ecc577..79579122 100644 --- a/carta/cpp/core/Data/Image/Contour/DataContours.h +++ b/carta/cpp/core/Data/Image/Contour/DataContours.h @@ -25,6 +25,7 @@ class GeneratorState; class DataContours : public QObject, public Carta::State::CartaObject { friend class ContourControls; +friend class ControllerData; Q_OBJECT diff --git a/carta/cpp/core/Data/Image/Contour/GeneratorState.cpp b/carta/cpp/core/Data/Image/Contour/GeneratorState.cpp index 5bf4c5f0..9c7cfc82 100644 --- a/carta/cpp/core/Data/Image/Contour/GeneratorState.cpp +++ b/carta/cpp/core/Data/Image/Contour/GeneratorState.cpp @@ -33,6 +33,7 @@ using Carta::State::StateInterface; GeneratorState::GeneratorState(): m_state( "", ""){ + m_state.insertValue( Carta::State::StateInterface::FLUSH_STATE, false ); _initializeSingletons(); _initializeDefaultState(); } diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 8e22cd0f..a4c494de 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -99,7 +99,8 @@ Controller::Controller( const QString& path, const QString& id ) : ContourControls* contourObj = objMan->createObject(); m_contourControls.reset( contourObj ); m_contourControls->setPercentIntensityMap( this ); - connect( m_contourControls.get(), SIGNAL(drawContoursChanged()), this, SLOT( _contoursChanged())); + connect( m_contourControls.get(), SIGNAL(drawContoursChanged()), + this, SLOT( _contoursChanged())); Settings* settingsObj = objMan->createObject(); m_settings.reset( settingsObj ); @@ -110,32 +111,31 @@ Controller::Controller( const QString& path, const QString& id ) : _initializeCallbacks(); } - +void Controller::addContourSet( std::shared_ptr contourSet){ + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_isSelected() ){ + m_datas[i]->_addContourSet( contourSet ); + } + } +} bool Controller::addData(const QString& fileName) { //Find the location of the data, if it already exists. - int targetIndex = -1; - for (int i = 0; i < m_datas.size(); i++) { - if (m_datas[i]->_isMatch(fileName)) { - targetIndex = i; - break; - } - } + int targetIndex = _getIndex( fileName ); //Add the data if it is not already there. if (targetIndex == -1) { Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); ControllerData* targetSource = objMan->createObject(); - DataContours* contourObj = objMan->createObject(); - std::shared_ptr contourPtr( contourObj ); - //Controller Data is in charge of drawing the contours. - targetSource->_setContours( contourPtr ); - //Contour controls is in charge of setting the UI for the contours. - m_contourControls->_setDrawContours( contourPtr ); targetIndex = m_datas.size(); connect( targetSource, & ControllerData::saveImageResult, this, & Controller::saveImageResultCB ); + connect( targetSource, SIGNAL(contourSetAdded(ControllerData*,const QString&)), + this, SLOT(_contourSetAdded(ControllerData*, const QString&))); + connect( targetSource, SIGNAL(contourSetRemoved(const QString&)), + this, SLOT(_contourSetRemoved(const QString&))); m_datas.append(std::shared_ptr(targetSource)); m_stackDraw->setLayers( m_datas ); @@ -266,7 +266,7 @@ QString Controller::closeImage( const QString& name ){ } void Controller::_colorMapChanged(){ - _render( true ); + _renderAll(); } @@ -278,19 +278,44 @@ void Controller::centerOnPixel( double centerX, double centerY ){ for ( int i = 0; i < dataCount; i++ ){ m_datas[i]->_setPan( centerX, centerY ); } + _renderAll(); } else { int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ m_datas[dataIndex]->_setPan( centerX, centerY ); } + _renderSingle( dataIndex ); } - _render( panZoomAll ); } +void Controller::_contourSetAdded( ControllerData* cData, const QString& setName ){ + if ( cData != nullptr ){ + std::shared_ptr addedSet = cData->_getContour( setName ); + if ( addedSet ){ + m_contourControls->_setDrawContours( addedSet ); + } + } +} + +void Controller::_contourSetRemoved( const QString setName ){ + //Remove the contour set from the controls only if nothing in the stack + //is using it. + bool stillExists = true; + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + if ( m_datas[i]->_getContour( setName ) ){ + stillExists = true; + break; + } + } + if ( !stillExists ){ + m_contourControls->deleteContourSet( setName ); + } +} void Controller::_contoursChanged(){ - _render( false ); + _renderAll(); } void Controller::_displayAxesChanged(std::vector displayAxisTypes, @@ -462,18 +487,24 @@ QString Controller::getImageName(int index) const{ return name; } +int Controller::_getIndex( const QString& fileName) const{ + int dataCount = m_datas.size(); + int targetIndex = -1; + for ( int i = 0; i < dataCount; i++ ){ + QString dataName = m_datas[i]->_getFileName(); + if ( fileName == dataName ){ + targetIndex = i; + break; + } + } + return targetIndex; +} + int Controller::_getIndexData( ControllerData* target ) const { int targetIndex = -1; if ( target != nullptr ){ - int dataCount = m_datas.size(); QString targetName = target->_getFileName(); - for ( int i = 0; i < dataCount; i++ ){ - QString dataName = m_datas[i]->_getFileName(); - if ( targetName == dataName ){ - targetIndex = i; - break; - } - } + targetIndex = _getIndex( targetName); } return targetIndex; } @@ -526,8 +557,6 @@ QStringList Controller::getOutputSize( ){ } - - double Controller::getPercentile( double intensity ) const { int currentFrame = getFrame( AxisInfo::KnownType::SPECTRAL ); return getPercentile( currentFrame, currentFrame, intensity ); @@ -667,7 +696,6 @@ QString Controller::getStateString( const QString& sessionId, SnapshotType type dataState.insertValue( axisName, m_selects[i]->getStateString()); } dataState.insertValue( Selection::IMAGE, m_selectImage->getStateString()); - dataState.insertValue( ContourControls::CLASS_NAME, m_contourControls->getStateString( sessionId, type)); result = dataState.toString(); } return result; @@ -697,7 +725,7 @@ void Controller::_gridChanged( const StateInterface& state, bool applyAll ){ if ( dataIndex >= 0 ){ if ( !applyAll ){ m_datas[dataIndex]->_gridChanged( state ); - _render( false ); + _renderSingle( dataIndex ); } else { int dataCount = m_datas.size(); @@ -706,7 +734,7 @@ void Controller::_gridChanged( const StateInterface& state, bool applyAll ){ m_datas[i]->_gridChanged( state ); } } - _render( true ); + _renderAll(); } _updateCursorText( true ); } @@ -1106,7 +1134,7 @@ void Controller::_loadView( bool newClips, int dataIndex ){ double clipValueMin = m_state.getValue(CLIP_VALUE_MIN); double clipValueMax = m_state.getValue(CLIP_VALUE_MAX); m_datas[dataIndex]->_load(frames, autoClip, clipValueMin, clipValueMax); - _render( false ); + _renderSingle( dataIndex ); } else { qDebug() << "Uninitialized image: "< contourSet ){ + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + m_datas[i]->_removeContourSet( contourSet ); + } +} + void Controller::_removeData( int index ){ disconnect( m_datas[index].get()); @@ -1174,21 +1209,28 @@ void Controller::_removeData( int index ){ saveState(); } +void Controller::_renderAll(){ + int gridIndex = _getIndexCurrent(); + _render( m_datas, gridIndex ); +} -void Controller::_render( bool allImages ){ - std::vector frames =_getFrameIndices(); - const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); - QList > datas; - if ( allImages ){ - datas = m_datas; - } - else { - int dataIndex = _getIndexCurrent(); - if ( dataIndex >= 0 ){ - datas.append( m_datas[dataIndex]); +void Controller::_renderSingle( int dIndex ){ + if ( dIndex >= 0 && dIndex >= m_datas.size() ){ + QList > datas; + datas.append( m_datas[dIndex] ); + int topIndex = _getIndexCurrent(); + int gridIndex = -1; + if ( dIndex == topIndex ){ + gridIndex = 0; } + _render( datas, gridIndex ); } - m_stackDraw->_render( datas, frames, cs ); +} + +void Controller::_render( QList > datas, int gridIndex ){ + std::vector frames =_getFrameIndices(); + const Carta::Lib::KnownSkyCS& cs = getCoordinateSystem(); + m_stackDraw->_render( datas, frames, cs, gridIndex ); } @@ -1205,26 +1247,35 @@ void Controller::resetState( const QString& state ){ } void Controller::resetStateData( const QString& state ){ - //First we reset the data this controller is displaying - _clearData(); - m_datas.clear(); + Carta::State::StateInterface dataState( ""); dataState.setState( state ); //Reset the loaded images + std::vector frames = _getFrameIndices(); int dataCount = dataState.getArraySize(DATA); + QStringList loadedFiles; for ( int i = 0; i < dataCount; i++ ){ QString dataLookup = Carta::State::UtilState::getLookup( DATA, i ); QString fileLookup = Carta::State::UtilState::getLookup( dataLookup, DataSource::DATA_PATH); QString fileName = dataState.getValue( fileLookup ); - addData( fileName ); - _loadView( true, i ); - QString gridLookup = Carta::State::UtilState::getLookup( dataLookup, DataGrid::GRID); - QString gridStr = dataState.toString( gridLookup ); - StateInterface gridState( "" ); - gridState.setState( gridStr ); - std::vector frames = _getFrameIndices(); - m_datas[i]->_gridChanged( gridState ); + int dataIndex = _getIndex( fileName ); + if ( dataIndex == -1 ){ + addData( fileName ); + dataIndex = m_datas.size() - 1; + } + loadedFiles.append( fileName ); + m_datas[dataIndex]->_resetState(dataState.toString(dataLookup)); + _loadView( true, dataIndex ); + } + + //Remove any data that should not be there + int stackCount = m_datas.size(); + for ( int i = stackCount-1; i>= 0; i--){ + QString fileName = m_datas[i]->_getFileName(); + if ( !loadedFiles.contains( fileName)){ + _removeData( i ); + } } m_stackDraw->setLayers( m_datas ); @@ -1232,6 +1283,7 @@ void Controller::resetStateData( const QString& state ){ QString dataStateStr = dataState.getValue( Selection::IMAGE ); m_selectImage ->resetState( dataStateStr ); m_stackDraw->setSelectIndex( m_selectImage->getIndex()); + _renderAll(); //Now we need to restore the axis states. int selectCount = m_selects.size(); @@ -1252,10 +1304,6 @@ void Controller::resetStateData( const QString& state ){ StateInterface controlState = m_datas[dataIndex]->_getGridState(); this->m_gridControls->_resetState( controlState ); } - - //Restore contours - QString contourData = dataState.getValue( ContourControls::CLASS_NAME); - m_contourControls->resetStateData( contourData ); } void Controller::resetPan(){ @@ -1266,15 +1314,16 @@ void Controller::resetPan(){ for ( int i = 0; i < dataCount; i++ ){ m_datas[i]->_resetPan(); } + _renderAll(); } } else { int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ m_datas[dataIndex]->_resetPan(); + _renderSingle( dataIndex ); } } - _render( panZoomAll ); } void Controller::resetZoom(){ @@ -1285,15 +1334,16 @@ void Controller::resetZoom(){ for ( int i = 0; i < dataCount; i++ ){ m_datas[i]->_resetZoom(); } + _renderAll(); } } else { int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ m_datas[dataIndex]->_resetZoom(); + _renderSingle( dataIndex ); } } - _render( panZoomAll ); } @@ -1386,7 +1436,9 @@ void Controller::_scheduleFrameReload( bool newClips ){ if ( m_reloadFrameQueued ) { return; } - m_stackDraw->setSelectIndex( m_selectImage->getIndex()); + int selectIndex=m_selectImage->getIndex(); + m_stackDraw->setSelectIndex( selectIndex); + _renderAll(); m_reloadFrameQueued = true; QMetaObject::invokeMethod( this, "_loadView", Qt::QueuedConnection, Q_ARG(bool, newClips) ); } @@ -1504,6 +1556,8 @@ QString Controller::setImageOrder( const std::vector& indices ){ int dataCount = m_datas.size(); int indexCount = indices.size(); QList > reorderedList; + int selectedIndex = m_selectImage->getIndex(); + int newSelectedIndex = selectedIndex; if ( indexCount != dataCount ){ result = "Reorder image size must match the stack count: "+QString::number(dataCount); } @@ -1516,8 +1570,12 @@ QString Controller::setImageOrder( const std::vector& indices ){ } //Insert the image at the target index at position i. else { + reorderedList.append( m_datas[targetIndex] ); if ( targetIndex != i ){ + if ( selectedIndex == targetIndex ){ + newSelectedIndex = i; + } imageReordered = true; } } @@ -1525,7 +1583,10 @@ QString Controller::setImageOrder( const std::vector& indices ){ } if ( imageReordered ){ m_datas = reorderedList; - _render( true ); + if ( selectedIndex != newSelectedIndex ){ + this->setFrameImage( newSelectedIndex ); + } + _renderAll(); } return result; } @@ -1714,13 +1775,21 @@ QString Controller::setCompositionMode( const QString& compMode ){ } void Controller::setZoomLevel( double zoomFactor ){ - int dataCount = m_datas.size(); - for ( int i = 0; i < dataCount; i++ ){ - //Set the zoom - m_datas[i]->_setZoom( zoomFactor ); - + bool zoomPanAll = m_state.getValue(PAN_ZOOM_ALL); + if ( zoomPanAll ){ + int dataCount = m_datas.size(); + for ( int i = 0; i < dataCount; i++ ){ + m_datas[i]->_setZoom( zoomFactor ); + } + _renderAll(); + } + else { + int dataIndex = _getIndexCurrent(); + if ( dataIndex >= 0 ){ + m_datas[dataIndex]->_setZoom( zoomFactor ); + _renderSingle( dataIndex ); + } } - _render( true ); } @@ -1779,14 +1848,15 @@ void Controller::updateZoom( double centerX, double centerY, double zoomFactor ) for (std::shared_ptr data : m_datas ){ _updateZoom( centerX, centerY, zoomFactor, data ); } + _renderAll(); } else { int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ _updateZoom( centerX, centerY, zoomFactor, m_datas[dataIndex] ); + _renderSingle( dataIndex ); } } - _render( zoomPanAll ); } void Controller::_updateZoom( double centerX, double centerY, double zoomFactor, @@ -1826,14 +1896,15 @@ void Controller::updatePan( double centerX , double centerY){ for ( std::shared_ptr data : m_datas ){ _updatePan( centerX, centerY, data ); } + _renderAll(); } else { int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ _updatePan( centerX, centerY, m_datas[dataIndex] ); + _renderSingle( dataIndex ); } } - _render( zoomPanAll ); _updateCursorText( true ); } @@ -1853,7 +1924,7 @@ void Controller::_viewResize( ){ for ( int i = 0; i < m_datas.size(); i++ ){ m_datas[i]->_viewResize( clientSize ); } - _render( true ); + _renderAll(); } Controller::~Controller(){ diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index b1143044..af587ab3 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -63,6 +63,12 @@ class Controller: public QObject, public Carta::State::CartaObject, */ void clear(); + /** + * Add a contour set to the selected images. + * @param contourSet - the contour set to add. + */ + virtual void addContourSet( std::shared_ptr contourSet) Q_DECL_OVERRIDE; + /** * Add data to this controller. * @param fileName the location of the data; @@ -281,6 +287,12 @@ class Controller: public QObject, public Carta::State::CartaObject, */ bool isStackSelectAuto() const; + /** + * Remove a contour set from the images. + * @param contourSet - the contour set to remove. + */ + virtual void removeContourSet( std::shared_ptr contourSet ) Q_DECL_OVERRIDE; + /** * Center the image. */ @@ -491,6 +503,8 @@ private slots: void _colorMapChanged(); + void _contourSetAdded( ControllerData* cData, const QString& setName ); + void _contourSetRemoved( const QString setName ); void _contoursChanged(); @@ -535,6 +549,10 @@ private slots: std::vector _getFrameIndices( ) const; set _getAxesHidden() const; std::vector _getAxisZTypes() const; + + //Get the data index + int _getIndex( const QString& fileName) const; + //Get the actual data index of the selection with the given index. This method //takes into account that some images may be hidden, i.e., temporarily not seen //on the stack. @@ -555,11 +573,15 @@ private slots: QString _makeRegion( const QString& regionType ); void _removeData( int index ); - /** - * Refresh the view of the images. - * @param allImages - true if all the images in the stack need a refresh; false otherwise. - */ - void _render( bool allImages ); + + //Render all images in the stack + void _renderAll(); + //Render a single image in the stack at the given index + void _renderSingle( int dIndex ); + //Helper function to render a subset of layers in the stack. The grid index is + //the index of the layer in the subset that should draw the grid or -1 if there + //is no such index. + void _render( QList > datas, int gridIndex ); void _saveRegions(); /** diff --git a/carta/cpp/core/Data/Image/ControllerData.cpp b/carta/cpp/core/Data/Image/ControllerData.cpp index 3c697f7b..33bad39d 100755 --- a/carta/cpp/core/Data/Image/ControllerData.cpp +++ b/carta/cpp/core/Data/Image/ControllerData.cpp @@ -66,6 +66,26 @@ ControllerData::ControllerData(const QString& path, const QString& id) : m_dataGrid->_initializeGridRenderer(); _initializeSingletons(); _initializeState(); + + std::shared_ptr gridService = m_dataGrid->_getRenderer(); + std::shared_ptr imageService = m_dataSource->_getRenderer(); + + // create the synchronizer + m_drawSync.reset( new DrawSynchronizer( imageService, gridService, this ) ); + + // connect its done() slot to our renderingSlot() + connect( m_drawSync.get(), & DrawSynchronizer::done, + this, & ControllerData::_renderingDone ); +} + +void ControllerData::_addContourSet( std::shared_ptr contour ){ + if ( contour ){ + QString targetName = contour->getName(); + std::shared_ptr contourSet = _getContour( targetName ); + if ( !contourSet ){ + m_dataContours.insert( contour ); + } + } } void ControllerData::_clearColorMap(){ @@ -81,11 +101,47 @@ void ControllerData::_clearData(){ if ( m_dataGrid != nullptr){ objMan->removeObject(m_dataGrid->getId()); } - if ( m_dataContours != nullptr){ - objMan->removeObject(m_dataContours->getId()); + for ( std::set< std::shared_ptr >::iterator it = m_dataContours.begin(); + it != m_dataContours.end(); it++ ){ + if ( (*it) ){ + objMan->removeObject( (*it)->getId() ); + } } } +void ControllerData::_colorChanged(){ + if ( m_dataSource ){ + QString mapName = m_stateColor->_getColorMap(); + m_dataSource->_setColorMap( mapName ); + m_dataSource->_setTransformData( m_stateColor->_getDataTransform() ); + m_dataSource->_setGamma( m_stateColor->_getGamma() ); + m_dataSource->_setColorReversed( m_stateColor->_isReversed() ); + m_dataSource->_setColorInverted( m_stateColor->_isInverted() ); + double redAmount = m_stateColor->_getMixRed(); + double greenAmount = m_stateColor->_getMixGreen(); + double blueAmount = m_stateColor->_getMixBlue(); + m_dataSource->_setColorAmounts( redAmount, greenAmount, blueAmount ); + bool defaultNan = m_stateColor->_isNanDefault(); + m_dataSource->_setNanDefault( defaultNan ); + + //If we aren't using a default nan color, tell the dataSource to use + //the custom color. + if ( !defaultNan ){ + int redAmount = m_stateColor->_getNanRed(); + int greenAmount = m_stateColor->_getNanGreen(); + int blueAmount = m_stateColor->_getNanBlue(); + m_dataSource->_setColorNan( redAmount, greenAmount, blueAmount ); + } + //We are using the default nan color. Update the state to the default color. + else { + QColor nanColor = m_dataSource->_getNanColor(); + m_stateColor->_setNanColor( nanColor.red(), nanColor.green(), nanColor.blue() ); + } + emit colorStateChanged(); + } +} + + void ControllerData::_displayAxesChanged(std::vector displayAxisTypes, const std::vector& frames ){ @@ -144,7 +200,19 @@ QString ControllerData::_getCompositionMode() const { return m_state.getValue( maskApplyKey ); } -std::shared_ptr ControllerData::_getContours() { +std::shared_ptr ControllerData::_getContour( const QString& name ){ + std::shared_ptr contourSet; + for ( std::set >::iterator it= m_dataContours.begin(); + it != m_dataContours.end(); it++ ){ + if ( name == (*it)->getName() ){ + contourSet = (*it); + break; + } + } + return contourSet; +} + +std::set> ControllerData::_getContours() { return m_dataContours; } @@ -336,6 +404,15 @@ QPointF ControllerData::_getScreenPt( QPointF imagePt, bool* valid ) const { QString ControllerData::_getStateString() const{ Carta::State::StateInterface copyState( m_state ); copyState.insertObject( DataGrid::GRID, m_dataGrid->_getState().toString() ); + int contourCount = m_dataContours.size(); + copyState.insertArray( DataContours::CONTOURS, contourCount ); + int i = 0; + for ( std::set< std::shared_ptr >::iterator iter = m_dataContours.begin(); + iter != m_dataContours.end(); iter++ ){ + QString lookup = Carta::State::UtilState::getLookup( DataContours::CONTOURS, i ); + copyState.setObject( lookup, (*iter)->_getState().toString() ); + i++; + } QString stateStr = copyState.toString(); return stateStr; } @@ -392,6 +469,18 @@ void ControllerData::_initializeState(){ m_state.insertValue( layerAlphaKey, alphaSupport ); } +bool ControllerData::_isContourDraw() const { + bool contourDraw = false; + for ( std::set< std::shared_ptr >::iterator it = m_dataContours.begin(); + it != m_dataContours.end(); it++ ){ + if ( (*it)->isContourDraw() ){ + contourDraw = true; + break; + } + } + return contourDraw; +} + bool ControllerData::_isSelected() const { return m_state.getValue( SELECTED ); } @@ -419,7 +508,7 @@ void ControllerData::_renderingDone( m_qimage = image; Carta::Lib::VectorGraphics::VGComposer comp( gridVG ); - if ( m_dataContours->isContourDraw()){ + if ( _isContourDraw()){ // where does 0.5, 0.5 map to? if ( m_dataSource ){ @@ -470,6 +559,20 @@ void ControllerData::_load(vector frames, bool recomputeClipsOnNewFrame, } +void ControllerData::_removeContourSet( std::shared_ptr contourSet ){ + if ( contourSet ){ + QString targetName = contourSet->getName(); + for ( std::set< std::shared_ptr >::iterator it = m_dataContours.begin(); + it != m_dataContours.end(); it++ ){ + if ( targetName == (*it)->getName() ){ + m_dataContours.erase(*it); + break; + } + } + } +} + + void ControllerData::_render( const std::vector& frames, const Carta::Lib::KnownSkyCS& cs, bool topOfStack ){ // erase current grid @@ -530,7 +633,7 @@ void ControllerData::_render( const std::vector& frames, Carta::Lib::AxisLabelInfo vertAxisInfo = m_dataGrid->_getAxisLabelInfo( 1, vertAxisType, cs ); gridService->setAxisLabelInfo( 1, vertAxisInfo ); - bool contourDraw = m_dataContours->isContourDraw(); + bool contourDraw = _isContourDraw(); bool gridDraw = false; if ( topOfStack ){ gridDraw = m_dataGrid->_isGridVisible(); @@ -538,6 +641,80 @@ void ControllerData::_render( const std::vector& frames, m_drawSync-> start( contourDraw, gridDraw ); } +void ControllerData::_resetState( const QString& stateStr ){ + Carta::State::StateInterface restoreState(""); + restoreState.setState( stateStr ); + + //Restore the grid + QString gridStr = restoreState.toString( DataGrid::GRID ); + Carta::State::StateInterface gridState( "" ); + gridState.setState( gridStr ); + _gridChanged( gridState ); + + _resetStateContours( restoreState ); + _resetState( restoreState ); +} + +void ControllerData::_resetStateContours(const Carta::State::StateInterface& restoreState ){ + int contourCount = restoreState.getArraySize( DataContours::CONTOURS ); + Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); + QStringList supportedContours; + + //Add any contours not there + for ( int i = 0; i < contourCount; i++ ){ + QString lookup = Carta::State::UtilState::getLookup( DataContours::CONTOURS, i ); + QString nameLookup = Carta::State::UtilState::getLookup( lookup, DataContours::SET_NAME ); + QString contourStr = restoreState.toString( lookup); + QString contourName = restoreState.getValue( nameLookup ); + supportedContours.append( contourName ); + bool newContourSet = false; + std::shared_ptr contourSet = _getContour( contourName ); + if ( !contourSet ){ + newContourSet = true; + contourSet = std::shared_ptr(objMan->createObject()); + m_dataContours.insert( contourSet ); + } + + QString contourSetState = restoreState.toString( lookup ); + contourSet->resetState( contourSetState ); + if ( newContourSet ){ + emit contourSetAdded( this, contourName ); + } + } + + //Remove any contours no longer there + for ( std::set >::iterator it = m_dataContours.begin(); + it != m_dataContours.end(); it++ ){ + QString contourSetName = (*it)->getName(); + if ( !supportedContours.contains( contourSetName )){ + _removeContourSet( (*it) ); + emit contourSetRemoved( contourSetName ); + } + } +} + +void ControllerData::_resetState( const Carta::State::StateInterface& restoreState ){ + //Restore the other state variables + m_state.setValue(Util::VISIBLE, restoreState.getValue(Util::VISIBLE) ); + m_state.setValue(SELECTED, restoreState.getValue(SELECTED) ); + + //Color mix + QString compModeKey = Carta::State::UtilState::getLookup( MASK, COMPOSITION_MODE ); + m_state.setValue( compModeKey, restoreState.getValue(compModeKey) ); + QString redKey = Carta::State::UtilState::getLookup( MASK, Util::RED ); + m_state.setValue( redKey, restoreState.getValue(redKey) ); + QString greenKey = Carta::State::UtilState::getLookup( MASK, Util::GREEN ); + m_state.setValue( greenKey, restoreState.getValue(greenKey) ); + QString blueKey = Carta::State::UtilState::getLookup( MASK, Util::BLUE ); + m_state.setValue( blueKey, restoreState.getValue(blueKey) ); + QString alphaKey = Carta::State::UtilState::getLookup( MASK, Util::ALPHA ); + m_state.setValue( alphaKey, restoreState.getValue(alphaKey) ); + QString layerColorKey = Carta::State::UtilState::getLookup( MASK, LAYER_COLOR ); + m_state.setValue( layerColorKey, restoreState.getValue(layerColorKey) ); + QString layerAlphaKey = Carta::State::UtilState::getLookup( MASK, LAYER_ALPHA ); + m_state.setValue( layerAlphaKey, restoreState.getValue(layerAlphaKey) ); +} + void ControllerData::_resetZoom(){ if ( m_dataSource ){ @@ -603,21 +780,6 @@ void ControllerData::_saveImageResultCB( bool result ){ } -void ControllerData::_setContours( std::shared_ptr contours ){ - m_dataContours = contours; - - std::shared_ptr gridService = m_dataGrid->_getRenderer(); - std::shared_ptr imageService = m_dataSource->_getRenderer(); - - // create the synchronizer - m_drawSync.reset( new DrawSynchronizer( imageService, gridService, this ) ); - - // connect its done() slot to our renderingSlot() - connect( m_drawSync.get(), & DrawSynchronizer::done, - this, & ControllerData::_renderingDone ); -} - - bool ControllerData::_setFileName( const QString& fileName ){ bool successfulLoad = m_dataSource->_setFileName( fileName ); if ( successfulLoad ){ @@ -664,37 +826,6 @@ void ControllerData::_setColorMapGlobal( std::shared_ptr colorState } -void ControllerData::_colorChanged(){ - if ( m_dataSource ){ - QString mapName = m_stateColor->_getColorMap(); - m_dataSource->_setColorMap( mapName ); - m_dataSource->_setTransformData( m_stateColor->_getDataTransform() ); - m_dataSource->_setGamma( m_stateColor->_getGamma() ); - m_dataSource->_setColorReversed( m_stateColor->_isReversed() ); - m_dataSource->_setColorInverted( m_stateColor->_isInverted() ); - double redAmount = m_stateColor->_getMixRed(); - double greenAmount = m_stateColor->_getMixGreen(); - double blueAmount = m_stateColor->_getMixBlue(); - m_dataSource->_setColorAmounts( redAmount, greenAmount, blueAmount ); - bool defaultNan = m_stateColor->_isNanDefault(); - m_dataSource->_setNanDefault( defaultNan ); - - //If we aren't using a default nan color, tell the dataSource to use - //the custom color. - if ( !defaultNan ){ - int redAmount = m_stateColor->_getNanRed(); - int greenAmount = m_stateColor->_getNanGreen(); - int blueAmount = m_stateColor->_getNanBlue(); - m_dataSource->_setColorNan( redAmount, greenAmount, blueAmount ); - } - //We are using the default nan color. Update the state to the default color. - else { - QColor nanColor = m_dataSource->_getNanColor(); - m_stateColor->_setNanColor( nanColor.red(), nanColor.green(), nanColor.blue() ); - } - emit colorStateChanged(); - } -} bool ControllerData::_setCompositionMode( const QString& compositionMode, QString& errorMsg ){ @@ -709,9 +840,17 @@ bool ControllerData::_setCompositionMode( const QString& compositionMode, bool colorSupport = m_compositionModes->isColorSupport( actualCompMode ); QString colorSupportKey = Carta::State::UtilState::getLookup( MASK, LAYER_COLOR ); m_state.setValue( colorSupportKey, colorSupport ); + if ( !colorSupport ){ + QStringList result; + _setMaskColor( 255, 255, 255, result ); + } bool alphaSupport = m_compositionModes->isAlphaSupport( actualCompMode ); QString alphaSupportKey = Carta::State::UtilState::getLookup( MASK, LAYER_ALPHA ); m_state.setValue( alphaSupportKey, alphaSupport ); + if ( !alphaSupport ){ + QString result; + _setMaskAlpha( 255, result ); + } stateChanged = true; } } diff --git a/carta/cpp/core/Data/Image/ControllerData.h b/carta/cpp/core/Data/Image/ControllerData.h index 487ba768..a446109f 100755 --- a/carta/cpp/core/Data/Image/ControllerData.h +++ b/carta/cpp/core/Data/Image/ControllerData.h @@ -12,8 +12,7 @@ #include "CartaLib/VectorGraphics/VGList.h" #include #include - - +#include class CoordinateFormatterInterface; @@ -66,6 +65,10 @@ Q_OBJECT signals: + void contourSetRemoved( const QString& name ); + void contourSetAdded(ControllerData* data, const QString& name ); + + //Notification that a new image has been produced. void renderingDone(); @@ -91,6 +94,13 @@ private slots: void _colorChanged(); private: + + /** + * Add a contour set. + * @param contour - the contour set to add. + */ + void _addContourSet( std::shared_ptr contour ); + void _clearColorMap(); void _clearData(); @@ -119,6 +129,10 @@ private slots: */ std::shared_ptr _getColorState(); + /** + * Return the mode used to composed the layer. + * @return - a string identifier for the composition mode. + */ QString _getCompositionMode() const; /** @@ -139,7 +153,18 @@ private slots: */ Carta::Lib::KnownSkyCS _getCoordinateSystem() const; - std::shared_ptr _getContours(); + /** + * Return the contour set with the indicated name. + * @return - the corresponding contour set with the designated name or a nullptr + * if no such set exists. + */ + std::shared_ptr _getContour( const QString& name ); + + /** + * Return all contour sets for this particular layer. + * @return - all contour sets in the layer. + */ + std::set< std::shared_ptr > _getContours(); /** * Returns information about the image at the current location of the cursor. @@ -151,7 +176,6 @@ private slots: QString _getCursorText( int mouseX, int mouseY, const std::vector& frames ); - /** * Return the image size for the given coordinate index. * @param coordIndex an index of a coordinate of the image. @@ -214,7 +238,17 @@ private slots: * @return - a string representation of layer specific information. */ QString _getLayerString() const; + + /** + * Get the transparency for the layer. + * @return - a transparency amount for the layer. + */ float _getMaskAlpha() const; + + /** + * Return the color filter for the layer. + * @return - a color filter for the layer. + */ quint32 _getMaskColor() const; /** @@ -274,8 +308,16 @@ private slots: */ QPointF _getScreenPt( QPointF imagePt, bool* valid ) const; + /** + * Return the state of this layer. + * @return - a string representation of the layer state. + */ QString _getStateString() const; + /** + * Return the layer vector graphics. + * @return - the layer vector graphics, which can include both the grid and contours. + */ Carta::Lib::VectorGraphics::VGList _getVectorGraphics(); /** @@ -297,6 +339,11 @@ private slots: void _initializeState(); void _initializeSingletons( ); + /** + * Returns true if at least one contour set should be drawn; false otherwise. + * @return - true if there is at least one contour set to draw; false otherwise. + */ + bool _isContourDraw() const; /** * Returns true if the name identifies this layer; false otherwise. @@ -346,6 +393,18 @@ private slots: */ void _resetPan(); + /** + * Reset the prefereence state of this layer. + * @param restoreState - the new layer state. + */ + void _resetState( const Carta::State::StateInterface& restoreState ); + + /** + * Reset the layer contours. + * @param restoreeState - the new layer state. + */ + void _resetStateContours(const Carta::State::StateInterface& restoreState ); + /** * Reset the zoom to the original value. */ @@ -366,16 +425,25 @@ private slots: */ void _setColorMapGlobal( std::shared_ptr colorState ); + /** + * Set the mode used to compose this layer. + * @param compositionMode - the mode used to compose this layer. + * @param errorMsg - a error message if the composition mode was not successfully set. + */ bool _setCompositionMode( const QString& compositionMode, QString& errorMsg ); /** - * Set contour set to be rendered. - * @param contours - the rendered contour set. + * Remove the contour set from this layer. + * @param contourSet - the contour set to remove from the layer. */ - //Note: The rendered contour set is an accumulation of all the contour sets. - void _setContours( std::shared_ptr contours ); + void _removeContourSet( std::shared_ptr contourSet ); + /** + * Restore the state of this layer. + * @param stateStr - the new layer state. + */ + void _resetState( const QString& stateStr ); /** * Returns whether or not the data was successfully loaded. @@ -461,7 +529,7 @@ private slots: std::unique_ptr m_dataGrid; - std::shared_ptr m_dataContours; + std::set< std::shared_ptr > m_dataContours; //Pointer to image interface. std::unique_ptr m_dataSource; diff --git a/carta/cpp/core/Data/Image/DrawStackSynchronizer.cpp b/carta/cpp/core/Data/Image/DrawStackSynchronizer.cpp index a363f716..7ed911b9 100755 --- a/carta/cpp/core/Data/Image/DrawStackSynchronizer.cpp +++ b/carta/cpp/core/Data/Image/DrawStackSynchronizer.cpp @@ -13,7 +13,7 @@ namespace Data { DrawStackSynchronizer::DrawStackSynchronizer( Carta::Lib::LayeredRemoteVGView* view ){ m_repaintFrameQueued = false; - m_selectIndex = 0; + m_selectIndex = -1; m_view.reset( view ); connect( m_view.get(), SIGNAL(sizeChanged()), this, SIGNAL( viewResize() ) ); } @@ -29,8 +29,9 @@ void DrawStackSynchronizer::_repaintFrameNow(){ } + void DrawStackSynchronizer::_render( QList >& datas, - std::vector frames, const Carta::Lib::KnownSkyCS& cs ){ + std::vector frames, const Carta::Lib::KnownSkyCS& cs, int topIndex ){ int dataCount = datas.size(); m_renderCount = 0; m_redrawCount = dataCount; @@ -40,7 +41,7 @@ void DrawStackSynchronizer::_render( QList >& da connect( datas[i].get(), SIGNAL(renderingDone()), this, SLOT(_scheduleFrameRepaint()), Qt::UniqueConnection); bool topOfStack = false; - if ( stackIndex == m_selectIndex ){ + if ( i == topIndex ){ topOfStack = true; } datas[i]->_render( frames, cs, topOfStack ); diff --git a/carta/cpp/core/Data/Image/DrawStackSynchronizer.h b/carta/cpp/core/Data/Image/DrawStackSynchronizer.h index 062fda8b..e6fa6fe5 100755 --- a/carta/cpp/core/Data/Image/DrawStackSynchronizer.h +++ b/carta/cpp/core/Data/Image/DrawStackSynchronizer.h @@ -32,7 +32,7 @@ class DrawStackSynchronizer: public QObject { Q_OBJECT public: - + /** * Constructor. * @param view- the stack view. @@ -81,7 +81,7 @@ private slots: private: void _render( QList >& datas, - std::vector frames, const Carta::Lib::KnownSkyCS& cs ); + std::vector frames, const Carta::Lib::KnownSkyCS& cs, int gridIndex ); //Data View std::shared_ptr m_view; diff --git a/carta/cpp/core/Data/Image/DrawSynchronizer.cpp b/carta/cpp/core/Data/Image/DrawSynchronizer.cpp index a60ae6e1..1b45093d 100644 --- a/carta/cpp/core/Data/Image/DrawSynchronizer.cpp +++ b/carta/cpp/core/Data/Image/DrawSynchronizer.cpp @@ -88,9 +88,23 @@ void DrawSynchronizer::setInput( std::shared_ptr & contours ){ - m_pens = contours->getPens(); - m_cec->setLevels( contours->getLevels() ); +void DrawSynchronizer::setContours( const std::set > & contours ){ + std::vector levels; + bool drawing = false; + m_pens.clear(); + for( std::set< std::shared_ptr >::iterator it = contours.begin(); + it != contours.end(); it++ ){ + if ( (*it)->isContourDraw() ){ + drawing = true; + std::vector setPens = (*it)->getPens(); + m_pens.insert( m_pens.end(), setPens.begin(), setPens.end()); + std::vector setLevels = (*it)->getLevels(); + levels.insert( levels.end(), setLevels.begin(), setLevels.end()); + } + } + if ( drawing ){ + m_cec->setLevels( levels ); + } } int64_t DrawSynchronizer::start( bool contourDraw, bool gridDraw, int64_t jobId ){ diff --git a/carta/cpp/core/Data/Image/DrawSynchronizer.h b/carta/cpp/core/Data/Image/DrawSynchronizer.h index 68405598..4c987d80 100644 --- a/carta/cpp/core/Data/Image/DrawSynchronizer.h +++ b/carta/cpp/core/Data/Image/DrawSynchronizer.h @@ -4,7 +4,7 @@ #pragma once #include - +#include namespace Carta { @@ -46,10 +46,10 @@ class DrawSynchronizer : public QObject { void setInput( std::shared_ptr rawView ); /** - * Sets the contour set to be drawn. - * @param contours - the contour set to draw. + * Sets the contour set(s) to be drawn. + * @param contours - a set of contours to be drawn. */ - void setContours( const std::shared_ptr & contours ); + void setContours( const std::set > & contours ); /** * Start a synchronized rendering. diff --git a/carta/cpp/core/Data/Image/IPercentIntensityMap.h b/carta/cpp/core/Data/Image/IPercentIntensityMap.h index 0aec1167..74712efe 100755 --- a/carta/cpp/core/Data/Image/IPercentIntensityMap.h +++ b/carta/cpp/core/Data/Image/IPercentIntensityMap.h @@ -13,6 +13,8 @@ namespace Carta { namespace Data { +class DataContours; + class IPercentIntensityMap { public: @@ -31,6 +33,18 @@ class IPercentIntensityMap { * @return the percentile corresponding to the intensity. */ virtual double getPercentile( int frameLow, int frameHigh, double intensity ) const = 0; + + /** + * Add a contour set. + * @param contourSet - the contour set to add. + */ + virtual void addContourSet( std::shared_ptr contourSet) = 0; + + /** + * Remove a contour set. + * @param contourSet - the contour set to remove. + */ + virtual void removeContourSet( std::shared_ptr contourSet) = 0; }; } } diff --git a/carta/html5/common/skel/source/class/skel/simulation/tSnapshotData.py b/carta/html5/common/skel/source/class/skel/simulation/tSnapshotData.py index d12166b3..8d42dcee 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tSnapshotData.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tSnapshotData.py @@ -20,10 +20,18 @@ def setUp(self): def _verifyImage(self, driver, count ): #Get the upper bound of images from the image animator - imageUpperSpin = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='ImageUpperBoundSpin']/input"))) - imageCount = imageUpperSpin.get_attribute( "value") - print "Expected count=",count," actualCount=",imageCount - self.assertEqual( int(imageCount), count, "Incorrect image count") + if ( count > 1 ): + imageUpperSpin = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='ImageUpperBoundSpin']/input"))) + imageCount = imageUpperSpin.get_attribute( "value") + print "Expected count=",count," actualCount=",imageCount + self.assertEqual( int(imageCount), count, "Incorrect image count") + #In exact, but we verify the image animator is not present which means 0 or 1. + else: + try: + imageUpperSpin = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='ImageUpperBoundSpin']/input"))) + self.assertTrue( False, "Image animator should not be present") + except Exception: + print "0 or 1 images present" # Set the channel animator to the last channel. Save a data snapshot. # Set the channel animator back to 0, the first channel. Restore a data snapshot. @@ -160,5 +168,81 @@ def test_image_load(self): # Verify that only the original two images are loaded self._verifyImage( driver, 1 ) + # Load an image. Save a data snapshot. Remove the image. Restore + # the snapshot. Check that the image is loaded. + def test_image_remove(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + # Wait for the image window to be present (ensures browser is fully loaded) + imageWindow = WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowImage']"))) + + # There must be an image loaded with more than one channel to see the channel animator. + Util.load_image(self,driver, "TWHydra_CO2_1line.image.fits" ) + + #Click on the animation window so that its actions will be enabled + animationWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowAnimation']"))) + + #Make sure the animation window is clicked by clicking an element within the window + channelText = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ChannelIndexText"))) + ActionChains(driver).click( channelText).perform() + + # Check that the channel upper spin is visible to verify the image is loaded. + channelUpperSpin = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='ChannelUpperBoundSpin']/input"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", channelUpperSpin ) + + # Find the session button on the menu bar and click it. + menuBar = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Menu.MenuBar']"))) + self._clickSessionButton( driver ) + + # Find the save session button in the submenu and click it. + self._clickSessionSaveButton( driver ) + + # The save popup should be visible. Make sure data is checked and + # layout and preferences are not checked + self._setSaveOptions( driver, False, False, True ) + + # Type in tSnapshotData for the save name. + self._setSaveName( driver, "tSnapshotData") + + # Hit the save button + self._saveSnapshot( driver ) + + # Close the dialog + self._closeSave( driver ) + + # Remove the image + ActionChains(driver).double_click( imageWindow ).perform() + dataButton = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//div[text()='Data']/.."))) + ActionChains(driver).click( dataButton ).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN).send_keys( + Keys.ARROW_RIGHT).send_keys(Keys.ENTER).perform() + + # Verify there are no images loaded. + try: + channelUpperSpin = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='ChannelUpperBoundSpin']/input"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", channelUpperSpin ) + assertTrue(False,"Upper spin was present") + except Exception: + print "Test passed - all images are gone" + + # Click the restore sessions button + self._clickSessionButton( driver ) + self._clickSessionRestoreButton( driver ) + + # Select tSnapshotData in the restore combo box + self._selectRestoreSnapshot( driver, "tSnapshotData") + + # Hit the restore button + self._restoreSnapshot( driver ) + + # Close the restore dialog + self._closeRestore( driver ) + time.sleep( timeout ) + + # Verify the original image is present - i.e., no image animator but a channel animator. + self._verifyImage( driver, 0 ) + channelUpperSpin = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='ChannelUpperBoundSpin']/input"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", channelUpperSpin ) + if __name__ == "__main__": unittest.main() \ No newline at end of file diff --git a/carta/html5/common/skel/source/class/skel/simulation/tStack.py b/carta/html5/common/skel/source/class/skel/simulation/tStack.py index 69f71c4d..b1876d02 100644 --- a/carta/html5/common/skel/source/class/skel/simulation/tStack.py +++ b/carta/html5/common/skel/source/class/skel/simulation/tStack.py @@ -15,8 +15,29 @@ def setUp(self): browser = selectBrowser._getBrowser() Util.setUp(self, browser) + def verifyCompositionMode(self, driver, mode): + print "verifying mode=", mode + combineCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "layerCompositionMode"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", combineCombo ) + combineText = combineCombo.find_element_by_xpath( ".//div/div") + combMode = combineText.text + print "Combine mode=",combMode + self.assertTrue( mode==combMode, "Combine modes not as expected") - + def verifyColor(self, driver, redExpected, greenExpected, blueExpected ): + spinRed = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='filterRGBSpinRed']/input"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", spinRed ) + redAmount = spinRed.get_attribute( "value"); + print "Red amount=",redAmount + self.assertEqual( int(redAmount), redExpected, "Red amount is not correct") + spinGreen = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='filterRGBSpinGreen']/input"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", spinGreen ) + greenAmount = spinGreen.get_attribute( "value"); + self.assertEqual( int(greenAmount), greenExpected, "Green amount is not correct") + spinBlue = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@id='filterRGBSpinBlue']/input"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", spinBlue ) + blueAmount = spinBlue.get_attribute( "value"); + self.assertEqual( int(blueAmount), blueExpected, "Blue amount is not correct") # Load 3 images # Hide the second image; check the count goes down to 2 @@ -57,7 +78,122 @@ def test_hideShow(self): #Verify the animator sees three images Util.verifyAnimatorUpperBound( self, driver, 2, "Image") - + + # Test that flipping between combine types resets non-supported settings + # to default values. + def test_layerDefaultSettings(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + #Open image settings and stack. + Util.load_image( self, driver, "Default") + Util.openSettings( self, driver, "Image" ) + Util.clickTab( driver, "Stack" ) + + #Load the plus layer + combineCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "layerCompositionMode"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", combineCombo ) + ActionChains(driver).click( combineCombo ).perform() + ActionChains(driver).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() + + #Change the transparency to 100 + transparencyText = driver.find_element_by_xpath( "//input[starts-with(@id,'maskAlphaTextField')]" ) + driver.execute_script( "arguments[0].scrollIntoView(true);", transparencyText) + Util._changeElementText(self, driver, transparencyText, 100) + + #Click the red color box. + filterBoxRed = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "filterRedBox"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", filterBoxRed ) + ActionChains(driver).click( filterBoxRed ).perform() + + #Verify the RGC spins are correct. + self.verifyColor( driver, 255, 0, 0 ) + + #Load the alpha layer. + combineCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "layerCompositionMode"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", combineCombo ) + ActionChains(driver).click( combineCombo ).perform() + ActionChains(driver).send_keys(Keys.ARROW_UP).send_keys(Keys.ENTER).perform() + + #Verify the color has been set back to white. + self.verifyColor( driver, 255,255, 255) + + #Load the none layer. + combineCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "layerCompositionMode"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", combineCombo ) + ActionChains(driver).click( combineCombo ).perform() + ActionChains(driver).send_keys(Keys.ARROW_UP).send_keys(Keys.ENTER).perform() + + #Verify the layer is now opaque. + transparencyText = driver.find_element_by_xpath( "//input[starts-with(@id,'maskAlphaTextField')]" ) + driver.execute_script( "arguments[0].scrollIntoView(true);", transparencyText) + alphaAmount = transparencyText.get_attribute( "value" ) + self.assertEqual( int(alphaAmount), 255, "Layer did not go opaque") + + #Test that layer settings work when multiple layers are selected. + def test_selectMultipleLayers(self): + driver = self.driver + timeout = selectBrowser._getSleep() + + #Load images + Util.load_image( self, driver, "Orion.methanol.cbc.contsub.image.fits") + Util.load_image( self, driver, "aJ.fits") + Util.load_image( self, driver, "aH.fits") + + #Open the image settings + #Open the stack tab + Util.openSettings( self, driver, "Image" ) + Util.clickTab( driver, "Stack" ) + + #Turn off auto select + autoSelectCheck = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "autoSelectImages"))) + ActionChains(driver).click( autoSelectCheck ).perform() + + #Select the last two images + secondItem = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.form.ListItem']/div[text()='aJ.fits']/.."))) + ActionChains(driver).key_down( Keys.CONTROL).click(secondItem).perform() + #ActionChains(driver).click( firstItem ).perform() + #secondItem = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='qx.ui.form.ListItem']/div[text()='aH.fits']/.."))) + #ActionChains(driver).key_down(Keys.CONTROL).click(secondItem).perform() + + #Change these images to plus layer combining + combineCombo = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "layerCompositionMode"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", combineCombo ) + ActionChains(driver).click( combineCombo ).perform() + ActionChains(driver).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform() + + #Change the filter color to green + filterBoxGreen = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "filterGreenBox"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", filterBoxGreen ) + ActionChains(driver).click( filterBoxGreen ).perform() + + #Turn on auto select + autoSelectCheck = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "autoSelectImages"))) + ActionChains(driver).click( autoSelectCheck ).perform() + + #Scroll through images and check the first two are plus with green and the + #third one is not. + # Click on Animator window so its actions will be enabled + animWindow = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@qxclass='skel.widgets.Window.DisplayWindowAnimation']"))) + ActionChains(driver).click( animWindow ).perform() + # Find the next button + forwardAnimateButton = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "ImageTapeDeckIncrement"))) + driver.execute_script( "arguments[0].scrollIntoView(true);", forwardAnimateButton) + + #First layer + ActionChains(driver).click( forwardAnimateButton ).perform() + self.verifyCompositionMode( driver, "None"); + self.verifyColor( driver, 255, 255, 255 ) + + #Second & third layer + for i in range(0,2): + print "Layer, ",i + ActionChains(driver).click( forwardAnimateButton ).perform() + time.sleep( timeout ) + self.verifyCompositionMode( driver,"Plus"); + self.verifyColor( driver, 0, 255, 0 ) + + def tearDown(self): #Close the browser diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Contour/ContourWidget.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Contour/ContourWidget.js index 36c38777..3b5c44f2 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Contour/ContourWidget.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Contour/ContourWidget.js @@ -197,7 +197,6 @@ qx.Class.define("skel.widgets.Image.Contour.ContourWidget", { setContour : function( contour ){ this._setVisible( contour.visible ); this._setWidth( contour.width ); - this._setColor( contour.red, contour.green, contour.blue); this._setTransparency( contour.alpha ); @@ -221,11 +220,9 @@ qx.Class.define("skel.widgets.Image.Contour.ContourWidget", { this.m_colorSelector.setBlue( blue ); this.m_colorId = this.m_colorSelector.addListener( "changeValue", this._sendColorCmd, this ); } - else { - this.m_red = red; - this.m_green = green; - this.m_blue = blue; - } + this.m_red = red; + this.m_green = green; + this.m_blue = blue; }, /** diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js index baac535c..010468a3 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControls.js @@ -68,6 +68,7 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControls", { var label = new qx.ui.basic.Label( "Composer:" ); this.m_compModeCombo = new skel.widgets.CustomUI.SelectBox( "setCompositionMode", "mode"); + skel.widgets.TestID.addTestId( this.m_compModeCombo, "layerCompositionMode"); this.m_compModeCombo.setToolTipText( "Select a layer composition mode."); //this.m_applyId = this.m_applyCheck.addListener( "changeValue", this._sendApplyCmd, this ); diff --git a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js index 5c35a6e4..b31b9a56 100755 --- a/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js +++ b/carta/html5/common/skel/source/class/skel/widgets/Image/Stack/MaskControlsColor.js @@ -72,13 +72,13 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { _initPresets : function(){ var presetContainer = new qx.ui.container.Composite(); presetContainer.setLayout( new qx.ui.layout.HBox(1)); - this.m_presetRed = this._makePreset( "#ff0000" ); + this.m_presetRed = this._makePreset( "#ff0000", "filterRedBox" ); this.m_presetRed.setToolTipText( "Apply a red filter to the selected layer(s)."); - this.m_presetGreen = this._makePreset( "#00ff00"); + this.m_presetGreen = this._makePreset( "#00ff00", "filterGreenBox"); this.m_presetGreen.setToolTipText( "Apply a green filter to the selected layer(s)."); - this.m_presetBlue = this._makePreset( "#0000ff"); + this.m_presetBlue = this._makePreset( "#0000ff", "filterBlueBox"); this.m_presetBlue.setToolTipText( "Apply a blue filter to the selected layer(s)."); - this.m_presetNone = this._makePreset( "#ffffff"); + this.m_presetNone = this._makePreset( "#ffffff", "filterNoneBox"); this.m_presetNone.setToolTipText( "No color filter should be applied to the selected layer(s)."); this.m_presetRed.addListener( "mousedown", this._presetRedSelected, this ); this.m_presetGreen.addListener( "mousedown", this._presetGreenSelected, this ); @@ -121,12 +121,15 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { this.m_spinRed = new qx.ui.form.Spinner( 0, 0, 255 ); this.m_spinRed.setToolTipText( "Set the red mask color amount in the selected layer(s)."); this.m_spinRedId = this.m_spinRed.addListener("changeValue", this._primaryColorChanged, this ); + skel.widgets.TestID.addTestId( this.m_spinRed, "filterRGBSpinRed"); this.m_spinBlue = new qx.ui.form.Spinner( 0, 0, 255 ); this.m_spinBlue.setToolTipText( "Set the blue mask color amount in the selected layer(s)."); this.m_spinBlueId = this.m_spinBlue.addListener( "changeValue", this._primaryColorChanged, this ); + skel.widgets.TestID.addTestId( this.m_spinBlue, "filterRGBSpinBlue"); this.m_spinGreen = new qx.ui.form.Spinner( 0, 0, 255 ); this.m_spinGreen.setToolTipText( "Set the green mask color amount in the selected layer(s)."); this.m_spinGreenId = this.m_spinGreen.addListener( "changeValue", this._primaryColorChanged, this ); + skel.widgets.TestID.addTestId( this.m_spinGreen, "filterRGBSpinGreen"); this._addColorRgb( rgbContainer, this.m_spinRed, "Red:", 0); this._addColorRgb( rgbContainer, this.m_spinGreen, "Green:", 1); this._addColorRgb( rgbContainer, this.m_spinBlue, "Blue:", 2); @@ -139,12 +142,14 @@ qx.Class.define("skel.widgets.Image.Stack.MaskControlsColor", { /** * Construct a square for a preset color. * @param colorParam {String} - the preset color. + * @param testId {String} - unique locator for testing */ - _makePreset : function( colorParam ){ + _makePreset : function( colorParam, testId ){ var preset = new qx.ui.core.Widget(); preset.setBackgroundColor( colorParam ); preset.setWidth( 15 ); preset.setHeight( 15 ); + skel.widgets.TestID.addTestId( preset, testId ); return preset; }, From 8e6c6e5a62612ac18eee110364d06402df3b14ea Mon Sep 17 00:00:00 2001 From: slovelan Date: Wed, 9 Dec 2015 08:46:37 -0700 Subject: [PATCH 33/37] Histogram Bug Fixing: Channel range & channel selection not working. --- carta/cpp/core/Data/Animator/Animator.cpp | 3 +- carta/cpp/core/Data/Image/Controller.cpp | 46 ++++++--------- carta/cpp/core/Data/Image/Controller.h | 11 ++-- .../core/Data/Image/DrawStackSynchronizer.h | 2 +- carta/cpp/plugins/Histogram/Histogram1.cpp | 59 +++++++------------ carta/cpp/plugins/Histogram/Histogram1.h | 10 ++-- .../cpp/plugins/Histogram/ImageHistogram.cpp | 45 ++++++-------- carta/cpp/plugins/Histogram/ImageHistogram.h | 12 ++-- 8 files changed, 73 insertions(+), 115 deletions(-) diff --git a/carta/cpp/core/Data/Animator/Animator.cpp b/carta/cpp/core/Data/Animator/Animator.cpp index dc2e6711..3c2cf10a 100644 --- a/carta/cpp/core/Data/Animator/Animator.cpp +++ b/carta/cpp/core/Data/Animator/Animator.cpp @@ -652,12 +652,11 @@ void Animator::_updateSupportedZAxes( Controller* controller ){ } Animator::~Animator(){ - Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); int animationCount = m_animators.size(); QList keys = m_animators.keys(); for ( int i = 0; i < animationCount; i++ ){ if ( m_animators[keys[i]] != nullptr ){ - objMan->destroyObject( m_animators[keys[i]]->getId() ); + delete m_animators[keys[i]]; m_animators[keys[i]] = nullptr; } } diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index e045d9d2..83c12237 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -367,15 +367,14 @@ std::set Controller::_getAxesHidden() const { return axes; } -QStringList Controller::getCenterPixel() { + +QPointF Controller::getCenterPixel() { int dataIndex = _getIndexCurrent(); - QStringList returnValue = QStringList( "null" ); + QPointF center = QPointF( nan(""), nan("") ); if ( dataIndex >= 0 ) { - QPointF center = m_datas[dataIndex]->_getCenter(); - returnValue = QStringList( QString::number( center.x() ) ); - returnValue.append( QString::number( center.y() ) ); + center = m_datas[dataIndex]->_getCenter(); } - return returnValue; + return center; } double Controller::getClipPercentileMax() const { @@ -467,18 +466,18 @@ std::shared_ptr Controller::getGridControls() { return m_gridControls; } -QStringList Controller::getImageDimensions( ){ - QStringList result; +std::vector Controller::getImageDimensions( ){ + std::vector result; int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ int dimensions = m_datas[dataIndex]->_getDimensions(); for ( int i = 0; i < dimensions; i++ ) { int d = m_datas[dataIndex]->_getDimension( i ); - result.append( QString::number( d ) ); + result.push_back( d ); } } else { - result = QStringList(""); + result.push_back(0); } return result; } @@ -546,16 +545,11 @@ bool Controller::getIntensity( int frameLow, int frameHigh, double percentile, d return validIntensity; } -QStringList Controller::getOutputSize( ){ - QStringList result; +QSize Controller::getOutputSize( ){ + QSize result(-1,-1); int dataIndex = _getIndexCurrent(); if ( dataIndex >= 0 ){ - QSize outputSize = m_datas[dataIndex]->_getOutputSize(); - result.append( QString::number( outputSize.width() ) ); - result.append( QString::number( outputSize.height() ) ); - } - else { - result = QStringList(""); + result = m_datas[dataIndex]->_getOutputSize(); } return result; } @@ -1269,8 +1263,10 @@ void Controller::resetStateData( const QString& state ){ dataIndex = m_datas.size() - 1; } loadedFiles.append( fileName ); - m_datas[dataIndex]->_resetState(dataState.toString(dataLookup)); - _loadView( true, dataIndex ); + if ( dataIndex >= 0 ){ + m_datas[dataIndex]->_resetState(dataState.toString(dataLookup)); + _loadView( true, dataIndex ); + } } //Remove any data that should not be there @@ -1901,15 +1897,6 @@ void Controller::updatePan( double centerX , double centerY){ _updatePan( centerX, centerY, data ); } _renderAll(); -QPointF Controller::getCenterPixel() const { - int imageIndex = m_selectImage->getIndex(); - QPointF center = QPointF( nan(""), nan("") ); - if ( imageIndex >= 0 && imageIndex < m_datas.size() ) { - center = m_datas[imageIndex]->_getCenter(); - } - return center; -} - } else { int dataIndex = _getIndexCurrent(); @@ -1941,6 +1928,7 @@ void Controller::_viewResize( ){ } Controller::~Controller(){ + //unregisterView(); clear(); Carta::State::ObjectManager* objMan = Carta::State::ObjectManager::objectManager(); int selectCount = m_selects.size(); diff --git a/carta/cpp/core/Data/Image/Controller.h b/carta/cpp/core/Data/Image/Controller.h index 300c83f0..b9c26e3c 100755 --- a/carta/cpp/core/Data/Image/Controller.h +++ b/carta/cpp/core/Data/Image/Controller.h @@ -102,10 +102,11 @@ class Controller: public QObject, public Carta::State::CartaObject, /** * Get the image pixel that is currently centered. - * @return a list of the x- and y-coordinates of the center pixel, - * or error information if the center pixel could not be obtained. + * @return a QPointF value consisting of the x- and y-coordinates of + * the center pixel, or a special value of (-0.0, -0.0) if the + * center pixel could not be obtained. */ - QStringList getCenterPixel(); + QPointF getCenterPixel(); /** * Return the coordinate system in use. @@ -162,7 +163,7 @@ class Controller: public QObject, public Carta::State::CartaObject, /** * Get the image dimensions. */ - QStringList getImageDimensions( ); + std::vector getImageDimensions( ); /** * Returns an identifier for the data source at the given index. @@ -192,7 +193,7 @@ class Controller: public QObject, public Carta::State::CartaObject, /** * Get the dimensions of the image viewer (window size). */ - QStringList getOutputSize( ); + QSize getOutputSize( ); /** * Return the percentile corresponding to the given intensity in the current frame. diff --git a/carta/cpp/core/Data/Image/DrawStackSynchronizer.h b/carta/cpp/core/Data/Image/DrawStackSynchronizer.h index e6fa6fe5..1f2ade85 100755 --- a/carta/cpp/core/Data/Image/DrawStackSynchronizer.h +++ b/carta/cpp/core/Data/Image/DrawStackSynchronizer.h @@ -84,7 +84,7 @@ private slots: std::vector frames, const Carta::Lib::KnownSkyCS& cs, int gridIndex ); //Data View - std::shared_ptr m_view; + std::unique_ptr m_view; QList< std::shared_ptr > m_layers; bool m_repaintFrameQueued; diff --git a/carta/cpp/plugins/Histogram/Histogram1.cpp b/carta/cpp/plugins/Histogram/Histogram1.cpp index 03a9c689..f25e694f 100755 --- a/carta/cpp/plugins/Histogram/Histogram1.cpp +++ b/carta/cpp/plugins/Histogram/Histogram1.cpp @@ -33,15 +33,9 @@ Histogram1::_computeHistogram() } // _computeHistogram std::pair < int, int > -Histogram1::_getChannelBounds( double freqMin, double freqMax, const QString & unitStr ) const -{ +Histogram1::_getChannelBounds( casa::ImageInterface* casaImage, + double freqMin, double freqMax, const QString & unitStr ) const{ std::pair < int, int > bounds( - 1, - 1 ); - if ( ! m_cartaImage ) { - //qWarning() << "No image available for channel bounds."; - return bounds; - } - - casa::ImageInterface < casa::Float > * casaImage = cartaII2casaII_float( m_cartaImage ); if ( ! casaImage ) { qWarning() << "Could not get casacore image ."; return bounds; @@ -113,15 +107,9 @@ Histogram1::_getChannelBounds( double freqMin, double freqMax, const QString & u } // _getChannelBounds std::pair < double, double > -Histogram1::_getFrequencyBounds( int channelMin, int channelMax, const QString & unitStr ) const -{ +Histogram1::_getFrequencyBounds( casa::ImageInterface* casaImage, + int channelMin, int channelMax, const QString & unitStr ) const{ std::pair < double, double > bounds( - 1, - 1 ); - if( ! m_cartaImage) { - //qWarning() << "No image available for channel bounds."; - return bounds; - } - - casa::ImageInterface < casa::Float > * casaImage = cartaII2casaII_float( m_cartaImage ); if ( ! casaImage ) { qWarning() << "Could not get casacore image ."; return bounds; @@ -177,14 +165,15 @@ Histogram1::handleHook( BaseHook & hookData ) return false; } - auto casaImage = cartaII2casaII_float( images.front()); + auto casaImage = cartaII2casaII_float( images.front() ); if( ! casaImage) { qWarning() << "Histogram plugin: not an image created by casaimageloader..."; return false; } - - m_histogram.reset( new ImageHistogram < casa::Float > ); - m_histogram-> setImage( casaImage ); + if ( !m_histogram ){ + m_histogram.reset(new ImageHistogram < casa::Float >()); + } + m_histogram-> setImage( casaImage->cloneII() ); m_histogram-> setBinCount( hook.paramsPtr->binCount ); double frequencyMin = hook.paramsPtr->minFrequency; @@ -193,28 +182,20 @@ Histogram1::handleHook( BaseHook & hookData ) int minChannel = - 1; int maxChannel = - 1; if ( frequencyMin < 0 || frequencyMax < 0 ) { - if ( casaImage) { - casa::CoordinateSystem cSys = casaImage->coordinates(); - casa::Int specAx = cSys.findCoordinate( casa::Coordinate::SPECTRAL ); - if ( specAx >= 0 ) { - minChannel = hook.paramsPtr->minChannel; - maxChannel = hook.paramsPtr->maxChannel; - m_histogram->setChannelRange( minChannel, maxChannel ); - } - } - else { - m_histogram->setChannelRange( - 1, - 1 ); + casa::CoordinateSystem cSys = casaImage->coordinates(); + casa::Int specAx = cSys.findCoordinate( casa::Coordinate::SPECTRAL ); + if ( specAx >= 0 ) { + minChannel = hook.paramsPtr->minChannel; + maxChannel = hook.paramsPtr->maxChannel; + m_histogram->setChannelRange( minChannel, maxChannel ); } - auto bounds = _getFrequencyBounds( minChannel, - maxChannel, - rangeUnits ); + + auto bounds = _getFrequencyBounds( casaImage, minChannel, maxChannel, rangeUnits ); frequencyMin = bounds.first; frequencyMax = bounds.second; } else { - auto bounds = _getChannelBounds( frequencyMin, - frequencyMax, - rangeUnits ); + auto bounds = _getChannelBounds( casaImage, frequencyMin, frequencyMax, rangeUnits ); m_histogram-> setChannelRange( bounds.first, bounds.second ); } m_histogram->setIntensityRange( @@ -239,4 +220,6 @@ Histogram1::getInitialHookList() }; } -Histogram1::~Histogram1() { } +Histogram1::~Histogram1() { + +} diff --git a/carta/cpp/plugins/Histogram/Histogram1.h b/carta/cpp/plugins/Histogram/Histogram1.h index 249ab8da..b9f689aa 100755 --- a/carta/cpp/plugins/Histogram/Histogram1.h +++ b/carta/cpp/plugins/Histogram/Histogram1.h @@ -40,18 +40,18 @@ class Histogram1 : public QObject, public IPlugin * Returns channel range for the given frequency bounds. */ std::pair - _getChannelBounds( double freq1, double freq2, const QString& unitStr ) const; + _getChannelBounds( casa::ImageInterface* casaImage, + double freq1, double freq2, + const QString& unitStr ) const; /** * Returns frequency bounds corresponding to the given channel range. */ std::pair - _getFrequencyBounds( int channelMin, int channelMax, const QString& unitStr ) const; + _getFrequencyBounds( casa::ImageInterface* casaImage, + int channelMin, int channelMax, const QString& unitStr ) const; /// Histogram implementation. std::unique_ptr> m_histogram = nullptr; - /// Current histogram image - std::shared_ptr m_cartaImage = nullptr; - }; diff --git a/carta/cpp/plugins/Histogram/ImageHistogram.cpp b/carta/cpp/plugins/Histogram/ImageHistogram.cpp index e87a472f..ceb4f6d7 100755 --- a/carta/cpp/plugins/Histogram/ImageHistogram.cpp +++ b/carta/cpp/plugins/Histogram/ImageHistogram.cpp @@ -8,6 +8,7 @@ #include #ifdef CASACORE_VERSION #include + #else #include #endif @@ -115,8 +116,7 @@ bool ImageHistogram::compute( ){ } template -casa::LatticeHistograms* ImageHistogram::_filterByChannels( const casa::ImageInterface * image ){ - casa::LatticeHistograms* imageHistogram = NULL; +void ImageHistogram::_filterByChannels( const casa::ImageInterface* image ){ if ( m_channelMin != ALL_CHANNELS && m_channelMax != ALL_CHANNELS ){ //Create a slicer from the image casa::CoordinateSystem cSys = image->coordinates(); @@ -143,63 +143,56 @@ casa::LatticeHistograms* ImageHistogram::_filterByChannels( const casa::Im endPos[spectralIndex] = endIndex; casa::Slicer channelSlicer( startPos, endPos, stride, casa::Slicer::endIsLast ); - casa::ImageInterface* img = new casa::SubImage(*image, channelSlicer ); - imageHistogram = new casa::LatticeHistograms( *img ); + casa::ImageInterface* img = new casa::SubImage( *(image), channelSlicer ); + m_histogramMaker->setNewLattice( *(img) ); } } } else { - casa::ImageInterface* img = new casa::SubImage(*image ); - imageHistogram = new casa::LatticeHistograms( *img ); + m_histogramMaker->setNewLattice( *(image) ); } - return imageHistogram; } template -void ImageHistogram::setImage( casa::ImageInterface * val ){ +void ImageHistogram::setImage( const casa::ImageInterface* val ){ if ( val != nullptr ){ - m_image = val; - _reset(); + if ( m_image == nullptr || m_image->name(true) != val->name(true) ){ + m_image = val; + _reset(); + } } } template void ImageHistogram::setRegion( casa::ImageRegion* region ){ - this->m_region = region; + m_region = region; } template bool ImageHistogram::_reset(){ bool success = true; if ( m_image != nullptr ){ - if ( m_histogramMaker != NULL ){ - //delete m_histogramMaker; - m_histogramMaker = NULL; - } + if ( !m_histogramMaker ){ + m_histogramMaker = new casa::LatticeHistograms( *(m_image) ); + } try { if ( m_region == NULL ){ //Make the histogram based on the image - m_histogramMaker = _filterByChannels( m_image ); + _filterByChannels( m_image ); } else { //Make the histogram based on the region casa::SubImage* subImage = new casa::SubImage( *m_image, *m_region ); - m_histogramMaker = _filterByChannels( subImage ); + _filterByChannels( subImage ); //Filter will make a new image based on this one so we can delete this sub //image once filter is done. delete subImage; } - if ( success ){ - success = compute(); - } + success = compute(); } catch( casa::AipsError& error ){ success = false; - /*if ( heightSource != NULL ){ - QString msg( "Could not make a histogram of the region: "); - msg.append( error.getMesg().c_str() ); - heightSource->postMessage( msg ); - }*/ + qDebug() << "Error making histogram: "<::toAscii( QTextStream& out ) const { template ImageHistogram::~ImageHistogram() { - delete m_histogramMaker; - //delete m_subImage; } template class ImageHistogram; diff --git a/carta/cpp/plugins/Histogram/ImageHistogram.h b/carta/cpp/plugins/Histogram/ImageHistogram.h index 8f6d2e85..ad95180c 100755 --- a/carta/cpp/plugins/Histogram/ImageHistogram.h +++ b/carta/cpp/plugins/Histogram/ImageHistogram.h @@ -2,7 +2,6 @@ #ifndef IMAGE_HISTOGRAM_H_ #define IMAGE_HISTOGRAM_H_ -#include #include #include "IImageHistogram.h" #include @@ -15,7 +14,6 @@ namespace casa { template class LatticeHistograms; template class SubImage; class ImageRegion; - class String; } /** @@ -46,7 +44,7 @@ class ImageHistogram : public IImageHistogram { std::pair getDataRange() const; void toAscii( QTextStream& out ) const; - virtual ~ImageHistogram(); + //common to all histograms void setBinCount( int count ) Q_DECL_OVERRIDE; @@ -56,18 +54,16 @@ class ImageHistogram : public IImageHistogram { void setIntensityRange( double minimumIntensity, double maximumIntensity ) Q_DECL_OVERRIDE; - void setImage(casa::ImageInterface * val); + void setImage(const casa::ImageInterface* val); static double computeYValue( double value, bool useLog ); - -signals: - void postStatus( const QString& msg ); + virtual ~ImageHistogram(); private: ImageHistogram( const ImageHistogram& other ); ImageHistogram operator=( const ImageHistogram& other ); //Completely reset the histogram if the image, region, or channels change bool _reset(); - casa::LatticeHistograms* _filterByChannels( const casa::ImageInterface* image ); + void _filterByChannels( const casa::ImageInterface* image ); vector m_xValues; vector m_yValues; From 0e4aeff28cba888c7a1fbd4e88ce9493f3358c68 Mon Sep 17 00:00:00 2001 From: slovelan Date: Sun, 20 Dec 2015 11:00:52 -0700 Subject: [PATCH 34/37] Fix of scripted client close image bug. --- carta/cpp/core/Data/Image/Controller.cpp | 9 +++++---- carta/cpp/core/Data/ViewManager.cpp | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/carta/cpp/core/Data/Image/Controller.cpp b/carta/cpp/core/Data/Image/Controller.cpp index 83c12237..c56101ea 100755 --- a/carta/cpp/core/Data/Image/Controller.cpp +++ b/carta/cpp/core/Data/Image/Controller.cpp @@ -245,11 +245,10 @@ void Controller::_clearStatistics(){ QString Controller::closeImage( const QString& name ){ int targetIndex = -1; QString result; - DataLoader* dataLoader = Util::findSingletonObject(); - QString longName = dataLoader->getLongName( name, ""); + int dataCount = m_datas.size(); for ( int i = 0; i < dataCount; i++ ){ - if ( m_datas[i]->_isMatch( longName )){ + if ( m_datas[i]->_isMatch( name )){ targetIndex = i; break; } @@ -867,7 +866,9 @@ void Controller::_initializeCallbacks(){ std::set keys = {"image"}; std::map dataValues = Carta::State::UtilState::parseParamMap( params, keys ); QString imageName = dataValues[*keys.begin()]; - QString result = closeImage( imageName ); + DataLoader* dataLoader = Util::findSingletonObject(); + QString longName = dataLoader->getLongName( imageName, ""); + QString result = closeImage( longName ); return result; }); diff --git a/carta/cpp/core/Data/ViewManager.cpp b/carta/cpp/core/Data/ViewManager.cpp index 73cf1342..4bcf1f38 100644 --- a/carta/cpp/core/Data/ViewManager.cpp +++ b/carta/cpp/core/Data/ViewManager.cpp @@ -90,7 +90,7 @@ ViewManager::ViewManager( const QString& path, const QString& id) void ViewManager::_adjustSize( int count, const QString& name, const QVector & insertionIndices ){ int existingCount = 0; - if ( name == NodeFactory::HIDDEN ){ + if ( name == NodeFactory::HIDDEN || name == NodeFactory::EMPTY ){ return; } else { @@ -101,7 +101,7 @@ void ViewManager::_adjustSize( int count, const QString& name, const QVectorgetObjectId( name, insertionIndices[index], true ); + getObjectId( name, insertionIndices[index], true ); index++; } } @@ -804,6 +804,7 @@ int ViewManager::_removeViews( const QString& name, int startIndex, int endIndex } _clearAnimators(startIndex, upperBound); } + else if ( name == Controller::PLUGIN_NAME ){ existingCount = m_controllers.size(); if ( endIndex < 0 ){ From c8f2bda355f89eb73b221fda317ac35b5c6de170 Mon Sep 17 00:00:00 2001 From: slovelan Date: Mon, 21 Dec 2015 09:46:13 -0700 Subject: [PATCH 35/37] CARTA not working for images with linear axes. --- carta/cpp/CartaLib/AxisInfo.h | 1 + carta/cpp/core/Data/Image/Grid/AxisMapper.cpp | 3 ++- carta/cpp/core/Data/Image/Grid/AxisMapper.h | 1 + .../CasaImageLoader/CCCoordinateFormatter.cpp | 21 ++++++++++++------- carta/cpp/plugins/Histogram/Histogram1.cpp | 4 ++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/carta/cpp/CartaLib/AxisInfo.h b/carta/cpp/CartaLib/AxisInfo.h index 10c3ec5d..db999b6d 100644 --- a/carta/cpp/CartaLib/AxisInfo.h +++ b/carta/cpp/CartaLib/AxisInfo.h @@ -19,6 +19,7 @@ class AxisInfo STOKES, /// < stokes axis TABULAR, /// < not sure but casacore uses it, maybe it's important? QUALITY, /// < not sure but casacore uses it, maybe it's important? + LINEAR, OTHER }; diff --git a/carta/cpp/core/Data/Image/Grid/AxisMapper.cpp b/carta/cpp/core/Data/Image/Grid/AxisMapper.cpp index a332279f..8a9f114a 100644 --- a/carta/cpp/core/Data/Image/Grid/AxisMapper.cpp +++ b/carta/cpp/core/Data/Image/Grid/AxisMapper.cpp @@ -18,12 +18,13 @@ const QString AxisMapper::LONGITUDE = "Longitude"; const QString AxisMapper::DECLINATION = "Declination"; const QString AxisMapper::LATITUDE = "Latitude"; const QString AxisMapper::SPECTRAL = "Channel"; +const QString AxisMapper::LINEAR = "Linear"; const QString AxisMapper::STOKES = "Stokes"; const QString AxisMapper::TABULAR = "Tabular"; const QString AxisMapper::QUALITY = "Quality"; const QList AxisMapper::m_purposes( {RIGHT_ASCENSION, DECLINATION, SPECTRAL, - STOKES, TABULAR, QUALITY}); + STOKES, TABULAR, QUALITY, LINEAR}); AxisMapper::AxisMapper(){ diff --git a/carta/cpp/core/Data/Image/Grid/AxisMapper.h b/carta/cpp/core/Data/Image/Grid/AxisMapper.h index d3b14e42..e152caa5 100644 --- a/carta/cpp/core/Data/Image/Grid/AxisMapper.h +++ b/carta/cpp/core/Data/Image/Grid/AxisMapper.h @@ -81,6 +81,7 @@ class AxisMapper { const static QString STOKES; const static QString TABULAR; const static QString QUALITY; + const static QString LINEAR; private: static QString _getAxisRAPurpose( const Carta::Lib::KnownSkyCS& cs ); diff --git a/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.cpp b/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.cpp index 034be71f..b19c934c 100644 --- a/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.cpp +++ b/carta/cpp/plugins/CasaImageLoader/CCCoordinateFormatter.cpp @@ -269,7 +269,7 @@ CCCoordinateFormatter::skyCS() CCCoordinateFormatter::Me & CCCoordinateFormatter::setSkyCS( const KnownSkyCS & scs ) { - qDebug() << "setSkyCS" << static_cast < int > ( scs ); + //qDebug() << "setSkyCS" << static_cast < int > ( scs ); // don't even try to set this to unknown if ( scs == KnownSkyCS::Unknown ) { @@ -354,13 +354,13 @@ CCCoordinateFormatter::setSkyFormatting( SkyFormatting format ) void CCCoordinateFormatter::parseCasaCS() { - qDebug() << "CCC nAxes=" << nAxes(); + /*qDebug() << "CCC nAxes=" << nAxes(); for ( auto & u : m_casaCS->worldAxisUnits() ) { qDebug() << "all units:" << u.c_str(); } for ( auto & u : m_casaCS->worldAxisNames() ) { qDebug() << "all names:" << u.c_str(); - } + }*/ // default precision is 3 m_precisions.resize( nAxes(), 3 ); @@ -369,12 +369,12 @@ CCCoordinateFormatter::parseCasaCS() for ( int i = 0 ; i < nAxes() ; i++ ) { parseCasaCSi( i ); } - qDebug() << "Parsed axis infos:"; + /*qDebug() << "Parsed axis infos:"; for ( auto & ai : m_axisInfos ) { qDebug() << " lp:" << ai.longLabel().plain() << "lh:" << ai.longLabel().html() << "sp:" << ai.shortLabel().html() << "sh:" << ai.shortLabel().html() << "u:" << ai.unit(); - } + }*/ // set formatting to default setSkyFormatting( SkyFormatting::Default ); @@ -396,9 +396,9 @@ CCCoordinateFormatter::parseCasaCSi( int pixelAxis ) m_casaCS->findPixelAxis( coord, coord2, pixelAxis ); - qDebug() << pixelAxis << "-->" << coord << "," << coord2; - qDebug() << " " - << casa::Coordinate::typeToString( m_casaCS->coordinate( coord ).type() ).c_str(); + //qDebug() << pixelAxis << "-->" << coord << "," << coord2; + //qDebug() << " " + // << casa::Coordinate::typeToString( m_casaCS->coordinate( coord ).type() ).c_str(); AxisInfo & aInfo = m_axisInfos[pixelAxis]; @@ -485,6 +485,11 @@ CCCoordinateFormatter::parseCasaCSi( int pixelAxis ) // aInfo.setKnownType( aInfo.KnownType::QUALITY); // } } + else if ( cc.type() == casa::Coordinate::LINEAR ){ + aInfo.setKnownType( AxisInfo::KnownType::LINEAR ) + .setLongLabel( HtmlString::fromPlain( "Linear")) + .setShortLabel( HtmlString::fromPlain( "Linear")); + } else { // other types... we copy whatever casacore dishes out aInfo.setKnownType( AxisInfo::KnownType::OTHER ); diff --git a/carta/cpp/plugins/Histogram/Histogram1.cpp b/carta/cpp/plugins/Histogram/Histogram1.cpp index f25e694f..b99c24b8 100755 --- a/carta/cpp/plugins/Histogram/Histogram1.cpp +++ b/carta/cpp/plugins/Histogram/Histogram1.cpp @@ -44,7 +44,7 @@ Histogram1::_getChannelBounds( casa::ImageInterface* casaImage, casa::CoordinateSystem cSys = casaImage->coordinates(); casa::Int specAx = cSys.findCoordinate( casa::Coordinate::SPECTRAL ); if ( specAx < 0 ) { - qWarning() << "Image did not have a spectral coordinate"; + //qWarning() << "Image did not have a spectral coordinate"; /// \todo Does this mean we can only compute histograms on spectral coordinates!?!?!?! return bounds; } @@ -118,7 +118,7 @@ Histogram1::_getFrequencyBounds( casa::ImageInterface* casaImage, casa::CoordinateSystem cSys = casaImage->coordinates(); casa::Int specAx = cSys.findCoordinate( casa::Coordinate::SPECTRAL ); if ( specAx < 0 ) { - qWarning() << "Image did not have a spectral coordinate"; + //qWarning() << "Image did not have a spectral coordinate"; /// \todo Does this mean we can only compute histograms on spectral coordinates!?!?!?! return bounds; } From dbc027e5fc7f2c9bfe38ea3ce88571d6b3f979c5 Mon Sep 17 00:00:00 2001 From: Alex Strilets Date: Fri, 8 Jan 2016 13:24:27 -0700 Subject: [PATCH 36/37] Modification for Mac OSX --- carta/cpp/core/MainConfig.cpp | 1 + carta/cpp/plugins/CyberSKA/CyberSKA.pro | 2 +- carta/cpp/plugins/Histogram/Histogram.pro | 11 +++++++---- carta/cpp/plugins/hpcImgRender/hpcImgRender.pro | 2 ++ carta/cpp/plugins/qimage/qimage.pro | 1 + 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/carta/cpp/core/MainConfig.cpp b/carta/cpp/core/MainConfig.cpp index c01477be..a1706770 100644 --- a/carta/cpp/core/MainConfig.cpp +++ b/carta/cpp/core/MainConfig.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace MainConfig { diff --git a/carta/cpp/plugins/CyberSKA/CyberSKA.pro b/carta/cpp/plugins/CyberSKA/CyberSKA.pro index dae93200..35347401 100644 --- a/carta/cpp/plugins/CyberSKA/CyberSKA.pro +++ b/carta/cpp/plugins/CyberSKA/CyberSKA.pro @@ -5,7 +5,7 @@ INCLUDEPATH += $$PROJECT_ROOT DEPENDPATH += $$PROJECT_ROOT -QT += core gui +QT += core gui network TARGET = plugin TEMPLATE = lib diff --git a/carta/cpp/plugins/Histogram/Histogram.pro b/carta/cpp/plugins/Histogram/Histogram.pro index 204b8218..7936254b 100755 --- a/carta/cpp/plugins/Histogram/Histogram.pro +++ b/carta/cpp/plugins/Histogram/Histogram.pro @@ -11,13 +11,13 @@ CONFIG += plugin SOURCES += \ IImageHistogram.cpp \ ImageHistogram.cpp \ - Histogram1.cpp + Histogram1.cpp HEADERS += \ IImageHistogram.h \ ImageHistogram.h \ - Histogram1.h + Histogram1.h casacoreLIBS += -L$${CASACOREDIR}/lib @@ -25,14 +25,15 @@ casacoreLIBS += -lcasa_lattices -lcasa_tables -lcasa_scimath -lcasa_scimath_f -l casacoreLIBS += -lcasa_casa -llapack -lblas -ldl casacoreLIBS += -lcasa_images -lcasa_coordinates -lcasa_fits -lcasa_measures -LIBS += $${casacoreLIBS} +LIBS += $${casacoreLIBS} LIBS += -L$$OUT_PWD/../../core/ -lcore LIBS += -L$$OUT_PWD/../../CartaLib/ -lCartaLib LIBS += -L$${WCSLIBDIR}/lib -lwcs +LIBS += -L$$OUT_PWD/../CasaImageLoader -lplugin INCLUDEPATH += $${CASACOREDIR}/include -INCLUDEPATH += $${WCSLIBDIR}/include +INCLUDEPATH += $${WCSLIBDIR}/include OTHER_FILES += \ plugin.json @@ -49,8 +50,10 @@ QMAKE_EXTRA_COMPILERS += copy_files unix:macx { PRE_TARGETDEPS += $$OUT_PWD/../../core/libcore.dylib + PRE_TARGETDEPS += $$OUT_PWD/../CasaImageLoader/libplugin.dylib } else{ PRE_TARGETDEPS += $$OUT_PWD/../../core/libcore.so + PRE_TARGETDEPS += $$OUT_PWD/../CasaImageLoader/libplugin.so } diff --git a/carta/cpp/plugins/hpcImgRender/hpcImgRender.pro b/carta/cpp/plugins/hpcImgRender/hpcImgRender.pro index a5914fc5..e7027dd6 100644 --- a/carta/cpp/plugins/hpcImgRender/hpcImgRender.pro +++ b/carta/cpp/plugins/hpcImgRender/hpcImgRender.pro @@ -21,6 +21,8 @@ HEADERS += \ OTHER_FILES += \ plugin.json +LIBS += -L$$OUT_PWD/../../CartaLib/ -lCartaLib + # copy json to build directory #MYFILES = $$files($${PWD}/files/*.*) MYFILES = plugin.json diff --git a/carta/cpp/plugins/qimage/qimage.pro b/carta/cpp/plugins/qimage/qimage.pro index ed4db8cf..4713fb8f 100644 --- a/carta/cpp/plugins/qimage/qimage.pro +++ b/carta/cpp/plugins/qimage/qimage.pro @@ -6,6 +6,7 @@ INCLUDEPATH += $$PROJECT_ROOT DEPENDPATH += $$PROJECT_ROOT QT += core gui +LIBS += -L$$OUT_PWD/../../CartaLib/ -lCartaLib TARGET = plugin TEMPLATE = lib From 72e6fb3cfb2ee72312ceb1fca9939e6b08e75432 Mon Sep 17 00:00:00 2001 From: Alex Strilets Date: Thu, 14 Jan 2016 13:12:49 -0700 Subject: [PATCH 37/37] Fixed empty Histogram problem --- carta/cpp/plugins/Histogram/Histogram.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/carta/cpp/plugins/Histogram/Histogram.pro b/carta/cpp/plugins/Histogram/Histogram.pro index 7936254b..0b47e4da 100755 --- a/carta/cpp/plugins/Histogram/Histogram.pro +++ b/carta/cpp/plugins/Histogram/Histogram.pro @@ -29,7 +29,6 @@ LIBS += $${casacoreLIBS} LIBS += -L$$OUT_PWD/../../core/ -lcore LIBS += -L$$OUT_PWD/../../CartaLib/ -lCartaLib LIBS += -L$${WCSLIBDIR}/lib -lwcs -LIBS += -L$$OUT_PWD/../CasaImageLoader -lplugin INCLUDEPATH += $${CASACOREDIR}/include @@ -51,6 +50,7 @@ QMAKE_EXTRA_COMPILERS += copy_files unix:macx { PRE_TARGETDEPS += $$OUT_PWD/../../core/libcore.dylib PRE_TARGETDEPS += $$OUT_PWD/../CasaImageLoader/libplugin.dylib + LIBS += -L$$OUT_PWD/../CasaImageLoader -lplugin } else{ PRE_TARGETDEPS += $$OUT_PWD/../../core/libcore.so